@xmszm/core 0.0.1 → 0.0.2

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/docs/usage.md CHANGED
@@ -2,18 +2,29 @@
2
2
 
3
3
  ## 安装
4
4
  ```bash
5
- npm install core
5
+ npm install @xmszm/core
6
+ # 或
7
+ pnpm add @xmszm/core
8
+ # 或
9
+ yarn add @xmszm/core
6
10
  ```
7
11
 
8
- 需要同时安装的 peer 依赖(版本可按项目统一):`vue`、`naive-ui`、`vue-router`、`dayjs`、`lodash-es`、`@vicons/ionicons5`。
12
+ 需要同时安装的 peer 依赖(版本可按项目统一):
13
+ - `vue` >= 3.3.0
14
+ - `naive-ui` >= 2.38.0
15
+ - `vue-router` >= 4.2.0
16
+ - `dayjs` >= 1.11.0
17
+ - `lodash-es` >= 4.17.21
18
+ - `@vicons/ionicons5` >= 0.13.0
19
+
9
20
  如项目使用 `@` 别名,请确保存在对应的打包/运行时配置。
10
21
 
11
22
  ## 快速开始
12
23
  ```vue
13
24
  <script setup>
14
25
  import { ref } from 'vue'
15
- import { DataForm, DataTable, commonDialogMethod } from 'core'
16
- import 'core/dist/style.css' // 如需默认样式
26
+ import { DataForm, DataTable, commonDialogMethod } from '@xmszm/core'
27
+ import '@xmszm/core/dist/style.css' // 如需默认样式
17
28
 
18
29
  const formValue = ref({})
19
30
  const formOptions = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmszm/core",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "naiveui core 组件与工具库",
5
5
  "license": "UNLICENSED",
6
6
  "main": "./dist/index.cjs",
@@ -18,8 +18,10 @@
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
+ "./dist/style.css": "./dist/style.css",
23
25
  "./package.json": "./package.json"
24
26
  },
25
27
  "files": [
@@ -53,6 +55,7 @@
53
55
  "lodash-es": "^4.17.21",
54
56
  "naive-ui": "^2.38.0",
55
57
  "rimraf": "^6.0.1",
58
+ "unplugin-auto-import": "^20.3.0",
56
59
  "vite": "^5.4.0",
57
60
  "vitepress": "^1.2.3",
58
61
  "vue": "^3.3.0",
@@ -1,9 +1,38 @@
1
- import { NButton, NSpace } from 'naive-ui'
1
+ import { NButton, NSpace, useDialog, createDiscreteApi } from 'naive-ui'
2
2
  import { computed, reactive, ref, unref, watch } from 'vue'
3
3
  import DataForm from '../form/DataForm.vue'
4
4
  import { dialogDefaultOption } from './utils/dialog'
5
5
  import './style/commonDialog.less'
6
6
 
7
+ // 全局缓存 dialog 实例,避免重复创建离散 API
8
+ let globalDialogInstance = typeof $dialog !== 'undefined' ? $dialog : null
9
+ let discreteDialogFactory = null
10
+
11
+ function getDialogInstanceOnce() {
12
+ if (globalDialogInstance)
13
+ return globalDialogInstance
14
+
15
+ // 1. 组件上下文中尝试 useDialog(若无上下文会抛错)
16
+ try {
17
+ const dialog = useDialog()
18
+ if (dialog) {
19
+ globalDialogInstance = dialog
20
+ return globalDialogInstance
21
+ }
22
+ }
23
+ catch (e) {
24
+ // 忽略,继续尝试离散 API
25
+ }
26
+
27
+ // 2. 使用离散 API,且只创建一次
28
+ if (!discreteDialogFactory) {
29
+ const { dialog } = createDiscreteApi(['dialog'])
30
+ discreteDialogFactory = { dialog }
31
+ }
32
+ globalDialogInstance = discreteDialogFactory.dialog
33
+ return globalDialogInstance
34
+ }
35
+
7
36
  /**
8
37
  *
9
38
  * @param {*} param
@@ -41,6 +70,9 @@ export function commonDialogMethod(
41
70
  },
42
71
  dialogProps = null,
43
72
  ) {
73
+ // 优先使用外部注册的 $dialog;再尝试组件上下文 useDialog;再退回离散 API(仅全局创建一次)
74
+ const dialogInstance = getDialogInstanceOnce()
75
+
44
76
  const defaultModeEnum = {
45
77
  none: { sub: '', read: false },
46
78
  create: { sub: '创建', read: false },
@@ -106,7 +138,7 @@ export function commonDialogMethod(
106
138
  ? () => titleFull(defaultModeEnum[mode]?.sub)
107
139
  : titleFull
108
140
 
109
- const d = $dialog.create({
141
+ const d = dialogInstance.create({
110
142
  type: 'info',
111
143
  ...dialogDefaultOption,
112
144
  ...(!noTitle
@@ -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
+
@@ -5,6 +5,14 @@ import { ref, unref } from 'vue'
5
5
  import Options from '../options/Options.jsx'
6
6
  import {initRules} from '../dialog/utils/dialog.js'
7
7
  import { toArray } from 'core'
8
+ import { registerDirectives } from '../directives/auto-register'
9
+ import { getCurrentInstance } from 'vue'
10
+
11
+ // 自动注册指令
12
+ const instance = getCurrentInstance()
13
+ if (instance?.appContext?.app) {
14
+ registerDirectives(instance.appContext.app)
15
+ }
8
16
  const props = defineProps({
9
17
  isNo: {
10
18
  type: Boolean,
package/src/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { commonDialogMethod } from './dialog/commonDialog'
2
+ export { useCommonDialog } from './dialog/useCommonDialog'
2
3
 
3
4
  export { default as DataForm } from './form/DataForm.vue'
4
5
  export {
@@ -9,7 +10,8 @@ export {
9
10
  export { default as Options } from './options/Options.jsx'
10
11
  export { default as CommonQuery } from './query/CommonQuery.vue'
11
12
 
12
- export { createActionColumnJsx } from './table/opr/useDataColumn'
13
+ export { createActionColumnJsx, createQRCode } from './table/opr/useDataColumn'
14
+ export { useQRCode } from './table/opr/useQRCode'
13
15
 
14
16
  export { default as OprButton } from './table/opr/useDataColumnButton.jsx'
15
17
 
@@ -29,18 +31,27 @@ export { ArrayToObject } from './utils/array'
29
31
 
30
32
 
31
33
 
32
- export { customUpload, registryUpload } from './utils/upload'
34
+ export { customUpload, registryUpload, getFileUrl } from './utils/upload'
35
+ export { setupConfig, getConfig, getBaseURL, getHasPermission, getUploadMethod, checkPermission, getDialogConfig, registerDialogInstance, getDialogInstance } from './utils/config'
36
+ export { createDialog, createDialogMethods, createDialogOptions } from './utils/dialog'
33
37
 
34
38
  export { default as DataTable } from './table/DataTable.vue'
35
39
 
36
40
 
37
- export { labelField as globalLabelField,valueField as globalValueField } from './enum/options'
41
+ export { labelField as globalLabelField, valueField as globalValueField } from './enum/options'
38
42
 
39
43
 
40
- export {initRules} from './dialog/utils/dialog.js'
44
+ export { initRules } from './dialog/utils/dialog.js'
41
45
 
42
46
  export { ellipsis } from './table/utils/ellipsis.js'
43
47
 
44
48
  export { initRouteMeta } from './plugin/vite/initRouteMeta'
45
49
 
46
- export { orderEnum } from './enum/sort'
50
+ export { orderEnum } from './enum/sort'
51
+
52
+ // 导出插件和指令
53
+ import CorePluginDefault from './plugin/index'
54
+ export { CorePluginDefault as CorePlugin }
55
+ export { install } from './plugin/index'
56
+ export { permissionDirective } from './directives/permission'
57
+ export { registerDirectives, autoRegisterDirectives, getGlobalApp } from './directives/auto-register'
@@ -16,9 +16,23 @@ import {
16
16
  } from '../enum/options'
17
17
  import { getOptions } from './defaultOptions'
18
18
  import SvgIcon from '../image/SvgIcon.vue'
19
+ import { registerDirectives, getGlobalApp } from '../directives/auto-register'
20
+ import { getCurrentInstance } from 'vue'
19
21
 
20
22
  export default defineComponent(
21
23
  (props, { emit }) => {
24
+ // 自动注册 corePermission 指令(如果尚未注册)
25
+ // 必须在组件渲染前注册,确保指令可用
26
+ const instance = getCurrentInstance()
27
+ if (instance?.appContext?.app) {
28
+ registerDirectives(instance.appContext.app)
29
+ } else {
30
+ // 如果无法从实例获取,尝试使用全局应用实例
31
+ const globalApp = getGlobalApp()
32
+ if (globalApp) {
33
+ registerDirectives(globalApp)
34
+ }
35
+ }
22
36
  const _value = ref(props.value)
23
37
  const _isRead = computed(() => props.read || false)
24
38
 
@@ -62,9 +76,9 @@ export default defineComponent(
62
76
  (v) => {
63
77
  emit('update:value', v)
64
78
  }
65
- ,{
66
- immediate: true,
67
- })
79
+ , {
80
+ immediate: true,
81
+ })
68
82
 
69
83
  function initProps(cProp) {
70
84
  let obj = {}
@@ -130,7 +144,7 @@ export default defineComponent(
130
144
  style={{
131
145
  width: '100%',
132
146
  }}
133
- {...(item?.contentProps||{})}
147
+ {...(item?.contentProps || {})}
134
148
  >
135
149
  {initMain(item?.prefix)}
136
150
  {initMain(item?.default || item)}
@@ -205,7 +219,7 @@ export default defineComponent(
205
219
 
206
220
  return (
207
221
  <NFormItem
208
- v-permission={item?.permission}
222
+ v-corePermission={item?.permission}
209
223
  key={index}
210
224
  showLabel={!item?.noLabel}
211
225
  {...item?.formItemProps}
@@ -231,31 +245,30 @@ export default defineComponent(
231
245
  >
232
246
  {item?.labelSuffix
233
247
  ? (
234
- typeof item?.labelSuffix === 'string'
235
- ? (
236
- <NIcon
237
- {...{
238
- class: 'wh-20px c-inherit',
239
- ...item?.labelSuffixProps,
240
- }}
241
- >
242
- <SvgIcon name={item?.labelSuffix} />
243
- </NIcon>
244
- )
245
- : (
246
- item?.labelSuffix?.()
247
- )
248
- )
248
+ typeof item?.labelSuffix === 'string'
249
+ ? (
250
+ <NIcon
251
+ {...{
252
+ class: 'wh-20px c-inherit',
253
+ ...item?.labelSuffixProps,
254
+ }}
255
+ >
256
+ <SvgIcon name={item?.labelSuffix} />
257
+ </NIcon>
258
+ )
259
+ : (
260
+ item?.labelSuffix?.()
261
+ )
262
+ )
249
263
  : (
250
- <></>
251
- )}
252
- {`${
253
- unref(
254
- typeof item?.[props.labelField] === 'function'
255
- ? item?.[props.labelField]?.()
256
- : item?.[props.labelField],
257
- ) || ''
258
- } ${_isRead.value || item.read ? ' ' : ' '}
264
+ <></>
265
+ )}
266
+ {`${unref(
267
+ typeof item?.[props.labelField] === 'function'
268
+ ? item?.[props.labelField]?.()
269
+ : item?.[props.labelField],
270
+ ) || ''
271
+ } ${_isRead.value || item.read ? ' ' : ' '}
259
272
  `}
260
273
  </div>
261
274
  ),
@@ -282,17 +295,17 @@ export default defineComponent(
282
295
  : unref(isRender)
283
296
  )
284
297
  ? (
285
- item?.render
286
- ? (
287
- item?.render(unref(_value), index, {
288
- setValue,
289
- value: unref(_value),
290
- })
291
- )
292
- : (
293
- <CreateFormItem item={item} index={index} />
294
- )
295
- )
298
+ item?.render
299
+ ? (
300
+ item?.render(unref(_value), index, {
301
+ setValue,
302
+ value: unref(_value),
303
+ })
304
+ )
305
+ : (
306
+ <CreateFormItem item={item} index={index} />
307
+ )
308
+ )
296
309
  : null
297
310
  })}
298
311
  </NSpace>
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 库插件
3
+ * 用于注册指令等全局功能
4
+ */
5
+ import { registerDirectives } from '../directives/auto-register'
6
+
7
+ /**
8
+ * 安装插件
9
+ * @param {Object} app - Vue 应用实例
10
+ * @param {Object} options - 插件选项
11
+ */
12
+ export function install(app, options = {}) {
13
+ // 注册所有指令(使用 corePermission 名称)
14
+ registerDirectives(app)
15
+ }
16
+
17
+ export default {
18
+ install,
19
+ }
20
+
@@ -3,7 +3,14 @@ import { RefreshOutline, SearchOutline } from '@vicons/ionicons5'
3
3
  import { DataForm } from 'core'
4
4
  import { getOptions } from 'core/options/defaultOptions.jsx'
5
5
  import { ObjectToArray } from 'core/utils/object.js'
6
- import { computed, onMounted, onUnmounted, ref } from 'vue'
6
+ import { computed, onMounted, onUnmounted, ref, getCurrentInstance } from 'vue'
7
+ import { registerDirectives } from '../directives/auto-register'
8
+
9
+ // 自动注册指令
10
+ const instance = getCurrentInstance()
11
+ if (instance?.appContext?.app) {
12
+ registerDirectives(instance.appContext.app)
13
+ }
7
14
 
8
15
  // 防抖函数
9
16
  function debounce(func, delay) {
@@ -5,10 +5,17 @@ import { ellipsis } from 'core/table/utils/ellipsis'
5
5
  import dayjs from 'dayjs'
6
6
  import { uniqueId } from 'lodash-es'
7
7
  import { NButton, NTooltip } from 'naive-ui'
8
- import { computed, onMounted, ref, unref, watch ,isProxy} from 'vue'
8
+ import { computed, onMounted, ref, unref, watch ,isProxy, getCurrentInstance} from 'vue'
9
9
  import { useRoute } from 'vue-router'
10
10
  import FilterDialog from './FilterDialog.vue'
11
11
  import { orderEnum } from 'core'
12
+ import { registerDirectives } from '../directives/auto-register'
13
+
14
+ // 自动注册指令
15
+ const instance = getCurrentInstance()
16
+ if (instance?.appContext?.app) {
17
+ registerDirectives(instance.appContext.app)
18
+ }
12
19
 
13
20
  const props = defineProps({
14
21
  data: {
@@ -71,6 +78,16 @@ const props = defineProps({
71
78
  const route = useRoute()
72
79
  const FilterKey = 'filter_key'
73
80
  const emit = defineEmits(['sorted'])
81
+
82
+ // 安全获取路由路径,如果没有路由上下文则使用默认值
83
+ const getRoutePath = () => {
84
+ try {
85
+ return route?.fullPath || route?.path || ''
86
+ } catch {
87
+ return ''
88
+ }
89
+ }
90
+
74
91
  const _data = computed(() => {
75
92
  console.log('table -data', props.data)
76
93
 
@@ -125,8 +142,9 @@ watch(
125
142
 
126
143
  function init() {
127
144
  const columns = unref(props.columns)
145
+ const routePath = getRoutePath()
128
146
  headDefault.value
129
- = getFilterAll.value?.[route.fullPath]
147
+ = (routePath && getFilterAll.value?.[routePath])
130
148
  || columns?.map((v, i) => v?.key || dayjs().valueOf() + i)
131
149
 
132
150
  const arr = props.isFilter
@@ -223,8 +241,11 @@ function filterHandle() {
223
241
  selectItem={headDefault.value}
224
242
  defaultItem={props.defaultColumns}
225
243
  onSubmit={(v) => {
226
- getFilterAll.value[route.fullPath] = v
244
+ const routePath = getRoutePath()
245
+ if (routePath) {
246
+ getFilterAll.value[routePath] = v
227
247
  setHeadFilter(getFilterAll.value)
248
+ }
228
249
  init()
229
250
  cancel()
230
251
  }}