@xmszm/core 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +187 -0
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +1431 -1170
- package/dist/plugin/vite/initRouteMeta.cjs +1 -0
- package/dist/plugin/vite/initRouteMeta.mjs +13 -0
- package/dist/style.css +1 -1
- package/docs/.vitepress/config.mjs +91 -0
- package/docs/components/config-options.md +125 -0
- package/docs/components/dataform.md +176 -23
- package/docs/components/datatable.md +58 -39
- package/docs/components/dialog.md +158 -19
- package/docs/components/options.md +44 -15
- package/docs/components/query.md +68 -14
- package/docs/components/utils.md +124 -16
- package/docs/guide/changelog.md +81 -0
- package/docs/guide/config.md +415 -0
- package/docs/guide/demo.md +2 -2
- package/docs/guide/local-development.md +109 -0
- package/docs/guide/quickstart.md +40 -11
- package/docs/index.md +3 -3
- package/docs/usage.md +30 -6
- package/examples/README.md +46 -0
- package/examples/index.html +14 -0
- package/examples/package.json +25 -0
- package/examples/pnpm-lock.yaml +1569 -0
- package/examples/pnpm-workspace.yaml +3 -0
- package/examples/src/AdminSystem.vue +870 -0
- package/examples/src/App.vue +330 -0
- package/examples/src/Introduction.vue +307 -0
- package/examples/src/main.js +22 -0
- package/examples/src/utils/permission.js +16 -0
- package/examples/src/utils/request.js +10 -0
- package/examples/vite.config.js +41 -0
- package/package.json +13 -4
- package/src/dialog/commonDialog.tsx +285 -0
- package/src/dialog/useCommonDialog.ts +41 -0
- package/src/dialog/utils/{dialog.js → dialog.ts} +2 -0
- package/src/directives/auto-register.ts +57 -0
- package/src/directives/permission.ts +67 -0
- package/src/enum/sort.tsx +45 -0
- package/src/form/DataForm.vue +34 -52
- package/src/index.ts +58 -0
- package/src/list/{useList.jsx → useList.tsx} +49 -14
- package/src/options/{Options.jsx → Options.tsx} +86 -72
- package/src/options/defaultOptions.tsx +656 -0
- package/src/plugin/index.ts +20 -0
- package/src/query/CommonQuery.vue +65 -90
- package/src/table/DataTable.vue +82 -95
- package/src/table/opr/{DataColumnCollet.jsx → DataColumnCollet.tsx} +18 -8
- package/src/table/opr/useDataColumn.tsx +226 -0
- package/src/table/opr/{useDataColumnButton.jsx → useDataColumnButton.tsx} +13 -6
- package/src/table/opr/{useDataColumnPop.jsx → useDataColumnPop.tsx} +13 -5
- package/src/table/opr/useQRCode.ts +40 -0
- package/src/utils/{array.js → array.ts} +4 -6
- package/src/utils/config.ts +192 -0
- package/src/utils/dialog.ts +110 -0
- package/src/utils/{object.js → object.ts} +1 -0
- package/src/utils/upload.ts +53 -0
- package/types/auto-imports.d.ts +78 -0
- package/types/components.d.ts +402 -0
- package/types/index.d.ts +145 -7
- package/types/plugin/vite/initRouteMeta.d.ts +23 -0
- package/types/src.d.ts +55 -0
- package/types/vue-shim.d.ts +9 -0
- package/examples/demo.vue +0 -224
- package/src/dialog/commonDialog.jsx +0 -230
- package/src/enum/sort.jsx +0 -31
- package/src/index.js +0 -46
- package/src/options/defaultOptions.jsx +0 -580
- package/src/table/opr/useDataColumn.jsx +0 -196
- package/src/utils/upload.js +0 -46
- /package/src/enum/{options.js → options.ts} +0 -0
- /package/src/plugin/vite/{initRouteMeta.js → initRouteMeta.ts} +0 -0
- /package/src/store/utils/{index.js → index.ts} +0 -0
- /package/src/table/utils/{ellipsis.js → ellipsis.ts} +0 -0
- /package/src/utils/{auth.js → auth.ts} +0 -0
- /package/src/utils/{time.js → time.ts} +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import vueJsx from '@vitejs/plugin-vue-jsx'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
|
|
6
|
+
// 插件:重定向 @xmszm/core 中的 @/ 路径到项目 src
|
|
7
|
+
const redirectExternalImports = () => {
|
|
8
|
+
return {
|
|
9
|
+
name: 'redirect-external-imports',
|
|
10
|
+
resolveId(id, importer) {
|
|
11
|
+
// 如果是从 @xmszm/core 导入的 @/ 路径,重定向到项目 src
|
|
12
|
+
if (importer && importer.includes('@xmszm/core') && id.startsWith('@/')) {
|
|
13
|
+
const resolvedPath = path.resolve(__dirname, 'src', id.replace('@/', ''))
|
|
14
|
+
return resolvedPath
|
|
15
|
+
}
|
|
16
|
+
return null
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default defineConfig({
|
|
22
|
+
base: '/core/examples/',
|
|
23
|
+
plugins: [vue(), vueJsx(), redirectExternalImports()],
|
|
24
|
+
resolve: {
|
|
25
|
+
alias: {
|
|
26
|
+
'@': path.resolve(__dirname, 'src'),
|
|
27
|
+
'@xmszm/core': path.resolve(__dirname, '..'),
|
|
28
|
+
'@xmszm/core/dist/style.css': path.resolve(__dirname, '../dist/style.css'),
|
|
29
|
+
},
|
|
30
|
+
dedupe: ['vue'],
|
|
31
|
+
},
|
|
32
|
+
build: {
|
|
33
|
+
outDir: 'dist',
|
|
34
|
+
assetsDir: 'assets',
|
|
35
|
+
},
|
|
36
|
+
server: {
|
|
37
|
+
port: 3000,
|
|
38
|
+
open: true
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmszm/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "naiveui core 组件与工具库",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -18,8 +18,15 @@
|
|
|
18
18
|
".": {
|
|
19
19
|
"import": "./dist/index.mjs",
|
|
20
20
|
"require": "./dist/index.cjs",
|
|
21
|
+
"style": "./dist/style.css",
|
|
21
22
|
"types": "./types/index.d.ts"
|
|
22
23
|
},
|
|
24
|
+
"./plugin/vite/initRouteMeta": {
|
|
25
|
+
"import": "./dist/plugin/vite/initRouteMeta.mjs",
|
|
26
|
+
"require": "./dist/plugin/vite/initRouteMeta.cjs",
|
|
27
|
+
"types": "./types/plugin/vite/initRouteMeta.d.ts"
|
|
28
|
+
},
|
|
29
|
+
"./dist/style.css": "./dist/style.css",
|
|
23
30
|
"./package.json": "./package.json"
|
|
24
31
|
},
|
|
25
32
|
"files": [
|
|
@@ -45,6 +52,8 @@
|
|
|
45
52
|
"vue-router": "^4.2.0"
|
|
46
53
|
},
|
|
47
54
|
"devDependencies": {
|
|
55
|
+
"@types/node": "^20.0.0",
|
|
56
|
+
"@unocss/vite": "^66.5.10",
|
|
48
57
|
"@vicons/ionicons5": "^0.13.0",
|
|
49
58
|
"@vitejs/plugin-vue": "^5.1.2",
|
|
50
59
|
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
|
@@ -53,12 +62,12 @@
|
|
|
53
62
|
"lodash-es": "^4.17.21",
|
|
54
63
|
"naive-ui": "^2.38.0",
|
|
55
64
|
"rimraf": "^6.0.1",
|
|
65
|
+
"typescript": "^5.0.0",
|
|
66
|
+
"unocss": "^66.5.10",
|
|
67
|
+
"unplugin-auto-import": "^20.3.0",
|
|
56
68
|
"vite": "^5.4.0",
|
|
57
69
|
"vitepress": "^1.2.3",
|
|
58
70
|
"vue": "^3.3.0",
|
|
59
71
|
"vue-router": "^4.2.0"
|
|
60
|
-
},
|
|
61
|
-
"dependencies": {
|
|
62
|
-
"npm": "^11.7.0"
|
|
63
72
|
}
|
|
64
73
|
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { NButton, NSpace, useDialog, createDiscreteApi } from 'naive-ui'
|
|
2
|
+
import type { DialogApi } from 'naive-ui'
|
|
3
|
+
import { reactive, ref, unref, defineComponent } from 'vue'
|
|
4
|
+
import type { Ref, VNode } from 'vue'
|
|
5
|
+
import type { CommonDialogOptions, DialogAction } from '../../types/components'
|
|
6
|
+
import type { CommonDialogResult } from '../../types'
|
|
7
|
+
import DataForm from '../form/DataForm.vue'
|
|
8
|
+
import { dialogDefaultOption } from './utils/dialog'
|
|
9
|
+
import './style/commonDialog.less'
|
|
10
|
+
|
|
11
|
+
// 声明全局 $dialog 变量(外部可能注入)
|
|
12
|
+
declare global {
|
|
13
|
+
// eslint-disable-next-line no-var
|
|
14
|
+
var $dialog: DialogApi | undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 全局缓存 dialog 实例,避免重复创建离散 API
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
let globalDialogInstance: any = null
|
|
20
|
+
let discreteDialogFactory: { dialog: any } | null = null
|
|
21
|
+
|
|
22
|
+
function getDialogInstanceOnce(): any {
|
|
23
|
+
if (globalDialogInstance)
|
|
24
|
+
return globalDialogInstance
|
|
25
|
+
|
|
26
|
+
// 1. 检查全局 $dialog
|
|
27
|
+
if (typeof $dialog !== 'undefined' && $dialog) {
|
|
28
|
+
globalDialogInstance = $dialog
|
|
29
|
+
return globalDialogInstance
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 2. 组件上下文中尝试 useDialog(若无上下文会抛错)
|
|
33
|
+
try {
|
|
34
|
+
const dialog = useDialog()
|
|
35
|
+
if (dialog) {
|
|
36
|
+
globalDialogInstance = dialog
|
|
37
|
+
return globalDialogInstance
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
// 忽略,继续尝试离散 API
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 3. 使用离散 API,且只创建一次
|
|
45
|
+
if (!discreteDialogFactory) {
|
|
46
|
+
const { dialog } = createDiscreteApi(['dialog'])
|
|
47
|
+
discreteDialogFactory = { dialog }
|
|
48
|
+
}
|
|
49
|
+
globalDialogInstance = discreteDialogFactory.dialog
|
|
50
|
+
return globalDialogInstance
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 集成填写表单功能的弹窗
|
|
55
|
+
* @param param - 弹窗配置选项
|
|
56
|
+
* @param dialogProps - 传递给 NDialog 的属性
|
|
57
|
+
* @returns 弹窗控制对象 {cancel, setValue, model, modeEnum}
|
|
58
|
+
*/
|
|
59
|
+
export function commonDialogMethod(
|
|
60
|
+
{
|
|
61
|
+
title = '',
|
|
62
|
+
noTitle = false,
|
|
63
|
+
titleFull = null,
|
|
64
|
+
options = [],
|
|
65
|
+
mode = 'add',
|
|
66
|
+
modeEnum = {},
|
|
67
|
+
labelField = 'label',
|
|
68
|
+
isNo = true,
|
|
69
|
+
formProps = {},
|
|
70
|
+
interfaceFn = null,
|
|
71
|
+
interfaceFnCancel = null,
|
|
72
|
+
valueData,
|
|
73
|
+
read,
|
|
74
|
+
isRead,
|
|
75
|
+
action = null,
|
|
76
|
+
contentStyle = {},
|
|
77
|
+
actionProps = {},
|
|
78
|
+
}: CommonDialogOptions = {
|
|
79
|
+
title: '自定义弹窗',
|
|
80
|
+
noTitle: false,
|
|
81
|
+
action: [],
|
|
82
|
+
options: [],
|
|
83
|
+
read: false,
|
|
84
|
+
isRead: false,
|
|
85
|
+
valueData: {},
|
|
86
|
+
},
|
|
87
|
+
dialogProps: Record<string, any> | null = null,
|
|
88
|
+
): CommonDialogResult {
|
|
89
|
+
// 优先使用外部注册的 $dialog;再尝试组件上下文 useDialog;再退回离散 API(仅全局创建一次)
|
|
90
|
+
const dialogInstance = getDialogInstanceOnce()
|
|
91
|
+
|
|
92
|
+
const defaultModeEnum: Record<string, { sub?: string; read?: boolean }> = {
|
|
93
|
+
none: { sub: '', read: false },
|
|
94
|
+
create: { sub: '创建', read: false },
|
|
95
|
+
add: { sub: '添加', read: false },
|
|
96
|
+
edit: { sub: '编辑', read: false },
|
|
97
|
+
view: { sub: '查看', read: true },
|
|
98
|
+
export: { sub: '导出', read: false },
|
|
99
|
+
import: { sub: '导入', read: false },
|
|
100
|
+
delete: { sub: '删除', read: false },
|
|
101
|
+
copy: { sub: '复制', read: false },
|
|
102
|
+
...modeEnum,
|
|
103
|
+
}
|
|
104
|
+
const formRef: Ref<any> = ref()
|
|
105
|
+
const actionLoading = reactive<boolean[]>([])
|
|
106
|
+
const model = ref({ ...valueData })
|
|
107
|
+
const defaultActionProps = {
|
|
108
|
+
justify: 'end' as 'end',
|
|
109
|
+
wrapItem: false,
|
|
110
|
+
style: {
|
|
111
|
+
width: '100%',
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const defaultButtonStyle = {
|
|
116
|
+
width: '120px',
|
|
117
|
+
}
|
|
118
|
+
const defaultAction: DialogAction[] = [
|
|
119
|
+
{
|
|
120
|
+
label: '取消',
|
|
121
|
+
props: {
|
|
122
|
+
type: 'primary',
|
|
123
|
+
ghost: true,
|
|
124
|
+
},
|
|
125
|
+
onClick: ({ cancel }) => {
|
|
126
|
+
if (interfaceFnCancel) {
|
|
127
|
+
interfaceFnCancel(unref(model), {
|
|
128
|
+
close: cancel,
|
|
129
|
+
})
|
|
130
|
+
} else {
|
|
131
|
+
cancel()
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
label: '确定',
|
|
137
|
+
valid: true,
|
|
138
|
+
onClick: async ({ cancel, hideLoading }) =>
|
|
139
|
+
interfaceFn
|
|
140
|
+
? await interfaceFn(unref(model), {
|
|
141
|
+
close: cancel,
|
|
142
|
+
hideLoading,
|
|
143
|
+
})
|
|
144
|
+
: cancel(),
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
(Array.isArray(action) ? action : defaultAction).forEach((v, i) => {
|
|
149
|
+
actionLoading[i] = false
|
|
150
|
+
})
|
|
151
|
+
const titleRender = typeof titleFull === 'function'
|
|
152
|
+
? () => titleFull(defaultModeEnum[mode]?.sub || '')
|
|
153
|
+
: titleFull
|
|
154
|
+
|
|
155
|
+
// 使用 ref 存储 dialog 实例,以便在函数中访问
|
|
156
|
+
const dialogRef: Ref<any> = ref()
|
|
157
|
+
|
|
158
|
+
function cancel(): void {
|
|
159
|
+
console.log('取消', model.value)
|
|
160
|
+
dialogRef.value?.destroy()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function validate(arr: string[] = []): Promise<void> {
|
|
164
|
+
console.log('开启验证')
|
|
165
|
+
await formRef.value?.valid()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function comfirm(fn?: () => any): Promise<any> {
|
|
169
|
+
return fn && fn()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const renderAction = !(read ?? isRead)
|
|
173
|
+
? typeof action === 'function'
|
|
174
|
+
? () => action({ formRef, data: unref(model), d: dialogRef.value, close: cancel })
|
|
175
|
+
: () => (
|
|
176
|
+
<NSpace
|
|
177
|
+
{...defaultActionProps}
|
|
178
|
+
{...actionProps}
|
|
179
|
+
style={{
|
|
180
|
+
...defaultActionProps?.style,
|
|
181
|
+
...(actionProps?.style || {}),
|
|
182
|
+
}}
|
|
183
|
+
>
|
|
184
|
+
{(Array.isArray(action) ? action : defaultAction).map((v, i) =>
|
|
185
|
+
v?.render
|
|
186
|
+
? (
|
|
187
|
+
v?.render?.()
|
|
188
|
+
)
|
|
189
|
+
: (
|
|
190
|
+
<NButton
|
|
191
|
+
type="primary"
|
|
192
|
+
ghost={v.mode === 'cancel'}
|
|
193
|
+
{...(v?.props || {})}
|
|
194
|
+
{...(actionProps?.buttonProps || {})}
|
|
195
|
+
style={{ ...defaultButtonStyle, ...(v?.style || {}) }}
|
|
196
|
+
loading={actionLoading[i]}
|
|
197
|
+
onClick={async () => {
|
|
198
|
+
if (v.mode === 'cancel') {
|
|
199
|
+
dialogRef.value?.destroy()
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const showLoading = () => (actionLoading[i] = true)
|
|
203
|
+
const hideLoading = () => (actionLoading[i] = false)
|
|
204
|
+
if (v?.loading)
|
|
205
|
+
showLoading()
|
|
206
|
+
try {
|
|
207
|
+
console.log('model', unref(model))
|
|
208
|
+
|
|
209
|
+
if (v?.valid)
|
|
210
|
+
await validate()
|
|
211
|
+
console.log(v?.valid)
|
|
212
|
+
if (v?.valid || v?.loading)
|
|
213
|
+
showLoading()
|
|
214
|
+
await v?.onClick?.({
|
|
215
|
+
model: unref(model),
|
|
216
|
+
comfirm,
|
|
217
|
+
cancel,
|
|
218
|
+
validate,
|
|
219
|
+
showLoading,
|
|
220
|
+
hideLoading,
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
catch (e) {
|
|
224
|
+
console.log(e)
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
if (v?.valid || v?.loading)
|
|
228
|
+
hideLoading()
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}}
|
|
232
|
+
>
|
|
233
|
+
{v.label || ''}
|
|
234
|
+
</NButton>
|
|
235
|
+
),
|
|
236
|
+
)}
|
|
237
|
+
</NSpace>
|
|
238
|
+
)
|
|
239
|
+
: null
|
|
240
|
+
|
|
241
|
+
const d = dialogInstance.create({
|
|
242
|
+
type: 'info',
|
|
243
|
+
...dialogDefaultOption,
|
|
244
|
+
...(!noTitle
|
|
245
|
+
? { title: titleRender || (defaultModeEnum[mode]?.sub ?? '') + title }
|
|
246
|
+
: {}),
|
|
247
|
+
style: {
|
|
248
|
+
width: '500px',
|
|
249
|
+
},
|
|
250
|
+
content: () => (
|
|
251
|
+
<DataForm
|
|
252
|
+
ref={(v: any) => {
|
|
253
|
+
formRef.value = v
|
|
254
|
+
}}
|
|
255
|
+
options={options}
|
|
256
|
+
v-model:value={model}
|
|
257
|
+
isNo={isNo}
|
|
258
|
+
read={read ?? isRead}
|
|
259
|
+
labelField={labelField}
|
|
260
|
+
formProps={formProps}
|
|
261
|
+
contentStyle={contentStyle}
|
|
262
|
+
dialog
|
|
263
|
+
/>
|
|
264
|
+
),
|
|
265
|
+
action: renderAction,
|
|
266
|
+
// ...readButton.value,
|
|
267
|
+
...dialogProps,
|
|
268
|
+
class: `core-dialog ${(read ?? isRead) ? 'core-dialog-read' : ''} ${dialogProps?.class || ''}`,
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
// 将 dialog 实例存储到 ref 中
|
|
272
|
+
dialogRef.value = d
|
|
273
|
+
|
|
274
|
+
// onBeforeUnmount(() => {
|
|
275
|
+
// console.log('commomDialog end')
|
|
276
|
+
// cancel()
|
|
277
|
+
// })
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
cancel,
|
|
281
|
+
setValue: (v: any, str?: string) => str ? (model.value[str] = v) : (model.value = { ...model.value, ...v }),
|
|
282
|
+
model: unref(model),
|
|
283
|
+
modeEnum: defaultModeEnum
|
|
284
|
+
};
|
|
285
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useCommonDialog Hook
|
|
3
|
+
* 在组件中使用 commonDialogMethod 的便捷方式
|
|
4
|
+
*/
|
|
5
|
+
import { useDialog } from 'naive-ui'
|
|
6
|
+
import { commonDialogMethod } from './commonDialog'
|
|
7
|
+
import { registerDialogInstance } from '../utils/config'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 使用 commonDialog 的 Hook
|
|
11
|
+
* @returns {Function} openDialog 函数
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* import { useCommonDialog } from '@xmszm/core'
|
|
15
|
+
*
|
|
16
|
+
* export default defineComponent({
|
|
17
|
+
* setup() {
|
|
18
|
+
* const openDialog = useCommonDialog()
|
|
19
|
+
*
|
|
20
|
+
* const handleEdit = () => {
|
|
21
|
+
* openDialog({
|
|
22
|
+
* title: '编辑',
|
|
23
|
+
* options: [...],
|
|
24
|
+
* })
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* return { handleEdit }
|
|
28
|
+
* }
|
|
29
|
+
* })
|
|
30
|
+
*/
|
|
31
|
+
export function useCommonDialog() {
|
|
32
|
+
const dialog = useDialog()
|
|
33
|
+
|
|
34
|
+
// 注册 dialog 实例到全局配置,这样 commonDialogMethod 可以直接使用
|
|
35
|
+
registerDialogInstance(dialog)
|
|
36
|
+
|
|
37
|
+
return (options, dialogProps) => {
|
|
38
|
+
return commonDialogMethod(options, dialogProps)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动注册指令
|
|
3
|
+
* 在导入库时自动注册所有指令,无需手动配置
|
|
4
|
+
*/
|
|
5
|
+
import { permissionDirective } from './permission'
|
|
6
|
+
|
|
7
|
+
// 全局应用实例缓存
|
|
8
|
+
let globalApp = null
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 注册所有指令到应用实例
|
|
12
|
+
* @param {Object} app - Vue 应用实例
|
|
13
|
+
*/
|
|
14
|
+
export function registerDirectives(app) {
|
|
15
|
+
if (!app) return
|
|
16
|
+
|
|
17
|
+
// 缓存应用实例
|
|
18
|
+
globalApp = app
|
|
19
|
+
|
|
20
|
+
// 只注册内部的 corePermission 指令
|
|
21
|
+
// 外部如果需要 v-permission,可以自己注册,与内部无关
|
|
22
|
+
if (!app._context.directives?.corePermission) {
|
|
23
|
+
app.directive('corePermission', permissionDirective)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 尝试自动注册指令
|
|
29
|
+
* 通过 getCurrentInstance 获取应用实例并注册
|
|
30
|
+
*/
|
|
31
|
+
export function autoRegisterDirectives() {
|
|
32
|
+
try {
|
|
33
|
+
// 动态导入 vue,避免在构建时出错
|
|
34
|
+
const vue = typeof require !== 'undefined' ? require('vue') : null
|
|
35
|
+
if (!vue) return false
|
|
36
|
+
|
|
37
|
+
const { getCurrentInstance } = vue
|
|
38
|
+
const instance = getCurrentInstance?.()
|
|
39
|
+
|
|
40
|
+
if (instance?.appContext?.app) {
|
|
41
|
+
registerDirectives(instance.appContext.app)
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
// 忽略错误,可能不在组件上下文中
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 获取全局应用实例
|
|
53
|
+
*/
|
|
54
|
+
export function getGlobalApp() {
|
|
55
|
+
return globalApp
|
|
56
|
+
}
|
|
57
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { checkPermission } from '../utils/config'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 权限指令
|
|
5
|
+
* 根据权限标识控制元素的显示/隐藏
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* <div v-corePermission="'user:edit'">编辑按钮</div>
|
|
9
|
+
* <div v-corePermission="['user:edit', 'user:view']">需要编辑或查看权限</div>
|
|
10
|
+
*/
|
|
11
|
+
export const permissionDirective = {
|
|
12
|
+
mounted(el, binding) {
|
|
13
|
+
const permission = binding.value
|
|
14
|
+
|
|
15
|
+
if (!permission) {
|
|
16
|
+
// 如果没有权限标识,默认显示
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 支持字符串或数组
|
|
21
|
+
const permissions = Array.isArray(permission) ? permission : [permission]
|
|
22
|
+
|
|
23
|
+
// 检查是否有任一权限
|
|
24
|
+
const hasPermission = permissions.some(p => checkPermission(p))
|
|
25
|
+
|
|
26
|
+
if (!hasPermission) {
|
|
27
|
+
// 没有权限时隐藏元素
|
|
28
|
+
el.style.display = 'none'
|
|
29
|
+
// 保存原始 display 值,以便后续恢复
|
|
30
|
+
el._originalDisplay = el.style.display || ''
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
updated(el, binding) {
|
|
35
|
+
const permission = binding.value
|
|
36
|
+
|
|
37
|
+
if (!permission) {
|
|
38
|
+
// 恢复显示
|
|
39
|
+
if (el._originalDisplay !== undefined) {
|
|
40
|
+
el.style.display = el._originalDisplay || ''
|
|
41
|
+
} else {
|
|
42
|
+
el.style.display = ''
|
|
43
|
+
}
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const permissions = Array.isArray(permission) ? permission : [permission]
|
|
48
|
+
const hasPermission = permissions.some(p => checkPermission(p))
|
|
49
|
+
|
|
50
|
+
if (!hasPermission) {
|
|
51
|
+
el.style.display = 'none'
|
|
52
|
+
} else {
|
|
53
|
+
// 恢复显示
|
|
54
|
+
if (el._originalDisplay !== undefined) {
|
|
55
|
+
el.style.display = el._originalDisplay || ''
|
|
56
|
+
} else {
|
|
57
|
+
el.style.display = ''
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
unmounted(el) {
|
|
63
|
+
// 清理
|
|
64
|
+
delete el._originalDisplay
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { h } from 'vue'
|
|
2
|
+
import { ChevronDown, ChevronUp, Code } from '@vicons/ionicons5'
|
|
3
|
+
|
|
4
|
+
interface OrderEnumItem {
|
|
5
|
+
title: string
|
|
6
|
+
value: boolean | null
|
|
7
|
+
Icon: any
|
|
8
|
+
fn: (listQuery: any, key?: string) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface OrderEnum {
|
|
12
|
+
ascend: OrderEnumItem
|
|
13
|
+
descend: OrderEnumItem
|
|
14
|
+
false: OrderEnumItem
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const orderEnum: OrderEnum = {
|
|
18
|
+
ascend: {
|
|
19
|
+
title: '升序',
|
|
20
|
+
value: true,
|
|
21
|
+
Icon: h(ChevronUp),
|
|
22
|
+
fn: (listQuery: any, key: string) => {
|
|
23
|
+
listQuery.desc = true
|
|
24
|
+
listQuery.sortFieldName = key
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
descend: {
|
|
28
|
+
title: '降序',
|
|
29
|
+
value: false,
|
|
30
|
+
Icon: h(ChevronDown),
|
|
31
|
+
fn: (listQuery: any) => {
|
|
32
|
+
listQuery.sortFieldName = ''
|
|
33
|
+
listQuery.desc = true
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
false: {
|
|
37
|
+
title: '默认',
|
|
38
|
+
value: null,
|
|
39
|
+
Icon: h(Code, { style: 'transform:rotateZ(90deg)' }),
|
|
40
|
+
fn: (listQuery: any) => {
|
|
41
|
+
listQuery.desc = false
|
|
42
|
+
listQuery.sortFieldName = ''
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}
|
package/src/form/DataForm.vue
CHANGED
|
@@ -1,47 +1,29 @@
|
|
|
1
|
-
<script setup>
|
|
1
|
+
<script setup lang="ts">
|
|
2
2
|
import { NForm } from 'naive-ui'
|
|
3
|
-
import { ref, unref } from 'vue'
|
|
3
|
+
import { ref, unref, computed } from 'vue'
|
|
4
|
+
import type { DataFormProps, DataFormExpose } from '../../types/components'
|
|
4
5
|
|
|
5
|
-
import Options from '../options/Options
|
|
6
|
-
import {initRules} from '../dialog/utils/dialog
|
|
7
|
-
import { toArray } from '
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
type: Array,
|
|
27
|
-
default: () => []
|
|
28
|
-
},
|
|
29
|
-
rules: {
|
|
30
|
-
type: Object,
|
|
31
|
-
default: () => ({})
|
|
32
|
-
},
|
|
33
|
-
formProps: {
|
|
34
|
-
type: Object,
|
|
35
|
-
default: () => ({})
|
|
36
|
-
},
|
|
37
|
-
formItemProps: {
|
|
38
|
-
type: Object,
|
|
39
|
-
default: () => ({})
|
|
40
|
-
},
|
|
41
|
-
dialog: {
|
|
42
|
-
type: Boolean,
|
|
43
|
-
default: () => false
|
|
44
|
-
}
|
|
6
|
+
import Options from '../options/Options'
|
|
7
|
+
import { initRules } from '../dialog/utils/dialog'
|
|
8
|
+
import { toArray } from '../utils/array'
|
|
9
|
+
import { registerDirectives } from '../directives/auto-register'
|
|
10
|
+
import { getCurrentInstance } from 'vue'
|
|
11
|
+
|
|
12
|
+
// 自动注册指令
|
|
13
|
+
const instance = getCurrentInstance()
|
|
14
|
+
if (instance?.appContext?.app) {
|
|
15
|
+
registerDirectives(instance.appContext.app)
|
|
16
|
+
}
|
|
17
|
+
const props = withDefaults(defineProps<DataFormProps>(), {
|
|
18
|
+
isNo: true,
|
|
19
|
+
read: false,
|
|
20
|
+
labelField: 'label',
|
|
21
|
+
contentStyle: () => ({}),
|
|
22
|
+
options: () => [],
|
|
23
|
+
rules: () => ({}),
|
|
24
|
+
formProps: () => ({}),
|
|
25
|
+
formItemProps: () => ({}),
|
|
26
|
+
dialog: false,
|
|
45
27
|
})
|
|
46
28
|
const _options = computed(() => props.options)
|
|
47
29
|
// ====== 复制 initRules 逻辑 ======
|
|
@@ -68,27 +50,27 @@ const autoRules = computed(() => {
|
|
|
68
50
|
})
|
|
69
51
|
console.log(autoRules.value);
|
|
70
52
|
|
|
71
|
-
defineExpose({
|
|
53
|
+
defineExpose<DataFormExpose>({
|
|
72
54
|
formRef,
|
|
73
55
|
getRule: () => autoRules.value,
|
|
74
|
-
valid: (keyCode = []) =>{
|
|
75
|
-
console.log('?? valid',keyCode)
|
|
76
|
-
return new Promise((resolve, reject) => {
|
|
56
|
+
valid: (keyCode: string[] = []) => {
|
|
57
|
+
console.log('?? valid', keyCode)
|
|
58
|
+
return new Promise<void>((resolve, reject) => {
|
|
77
59
|
const arrCode = toArray(keyCode)
|
|
78
|
-
formRef.value?.validate((v) => {
|
|
60
|
+
formRef.value?.validate((v: any) => {
|
|
79
61
|
if (v) return reject(v)
|
|
80
62
|
resolve()
|
|
81
|
-
},(v)=> arrCode.length ?
|
|
63
|
+
}, (v: any) => arrCode.length ? arrCode.includes(v?.key) : true)
|
|
82
64
|
})
|
|
83
65
|
},
|
|
84
|
-
confirm: (fn) =>
|
|
85
|
-
new Promise((resolve, reject) => {
|
|
86
|
-
formRef.value?.validate((v) => {
|
|
66
|
+
confirm: (fn?: (model: any) => void) =>
|
|
67
|
+
new Promise<any>((resolve, reject) => {
|
|
68
|
+
formRef.value?.validate((v: any) => {
|
|
87
69
|
if (v) return reject(v)
|
|
88
70
|
fn && fn(unref(_model))
|
|
89
71
|
resolve(unref(_model))
|
|
90
72
|
})
|
|
91
|
-
})
|
|
73
|
+
}),
|
|
92
74
|
})
|
|
93
75
|
</script>
|
|
94
76
|
|