@veltra/desktop 1.2.26 → 1.2.28

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.
Files changed (34) hide show
  1. package/dist/components/action/action-group.vue.d.ts +1 -1
  2. package/dist/components/action/action.vue.d.ts +1 -1
  3. package/dist/components/batch-edit/batch-edit-form.js +10 -5
  4. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  5. package/dist/components/batch-edit/use-edit-state.js +1 -0
  6. package/dist/components/batch-edit/use-edit-state.js.map +1 -1
  7. package/dist/components/batch-edit/use-handlers.js +3 -0
  8. package/dist/components/batch-edit/use-handlers.js.map +1 -1
  9. package/dist/components/batch-edit/use-shortcut-key.js +2 -0
  10. package/dist/components/batch-edit/use-shortcut-key.js.map +1 -1
  11. package/dist/components/cascade/cascade.vue.d.ts +2 -2
  12. package/dist/components/grid-input/grid-input.js +18 -12
  13. package/dist/components/grid-input/grid-input.js.map +1 -1
  14. package/dist/components/grid-input/grid-input.vue.d.ts +1 -1
  15. package/dist/components/input/input.vue.d.ts +2 -2
  16. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +2 -2
  17. package/dist/components/number/number.vue.d.ts +1 -1
  18. package/dist/components/number-input/number-input.vue.d.ts +2 -2
  19. package/dist/components/number-range-input/number-range-input.js +23 -16
  20. package/dist/components/number-range-input/number-range-input.js.map +1 -1
  21. package/dist/components/textarea/textarea.vue.d.ts +2 -2
  22. package/dist/components/tip/tip.vue.d.ts +1 -1
  23. package/dist/components/tree-select/tree-select.vue.d.ts +2 -2
  24. package/dist/components/watermark/watermark.vue.d.ts +1 -1
  25. package/dist/types/grid-input.d.ts +12 -5
  26. package/package.json +6 -6
  27. package/src/components/batch-edit/batch-edit-form.vue +11 -5
  28. package/src/components/batch-edit/use-edit-state.ts +3 -0
  29. package/src/components/batch-edit/use-handlers.ts +5 -0
  30. package/src/components/batch-edit/use-shortcut-key.ts +2 -0
  31. package/src/components/grid-input/grid-input.vue +21 -17
  32. package/src/components/number-range-input/__test__/number-range-input.test.ts +100 -0
  33. package/src/components/number-range-input/number-range-input.vue +28 -16
  34. package/src/types/grid-input.ts +12 -5
@@ -8,9 +8,9 @@ type __VLS_Slots = {
8
8
  declare const __VLS_base: import("vue").DefineComponent<ActionGroupProps, _ActionGroupExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ActionGroupProps> & Readonly<{}>, {
9
9
  type: import("@veltra/utils").ColorType;
10
10
  size: "small" | "default" | "large";
11
- text: boolean;
12
11
  circle: boolean;
13
12
  max: number;
13
+ text: boolean;
14
14
  loading: boolean;
15
15
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
16
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -14,8 +14,8 @@ declare const __VLS_base: import("vue").DefineComponent<ActionProps, {}, {}, {},
14
14
  }>, {
15
15
  type: ButtonType;
16
16
  size: import("@veltra/utils").ComponentSize;
17
- text: boolean;
18
17
  circle: boolean;
18
+ text: boolean;
19
19
  loading: boolean;
20
20
  inDropdown: boolean;
21
21
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -7,7 +7,7 @@ import { BatchEditDIKey } from "./di.js";
7
7
  import { FORM_ACTION_HEADER_MAP } from "./form-action-header.js";
8
8
  import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, guardReactiveProps, inject, normalizeClass, normalizeProps, openBlock, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, toRef, unref, withCtx } from "vue";
9
9
  import { bem } from "@veltra/utils";
10
- import { Close } from "@veltra/icons/normal";
10
+ import { Close, Save } from "@veltra/icons/normal";
11
11
  import { o } from "@cat-kit/core";
12
12
  //#region src/components/batch-edit/batch-edit-form.vue
13
13
  const _sfc_main = /*@__PURE__*/ defineComponent({
@@ -119,6 +119,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
119
119
  }),
120
120
  _cache[3] || (_cache[3] = createTextVNode(" 关闭 ", -1))
121
121
  ], 2)], 2), createElementVNode("div", { class: normalizeClass(unref(cls).e("form-actions")) }, [createVNode(unref(_sfc_main$3), {
122
+ size: "small",
122
123
  text: "",
123
124
  loading: unref(state).loading,
124
125
  onClick: unref(handleClose)
@@ -128,12 +129,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
128
129
  }, 8, ["loading", "onClick"]), showSaveBtn.value ? (openBlock(), createBlock(unref(_sfc_main$3), {
129
130
  key: 0,
130
131
  type: "primary",
132
+ size: "small",
133
+ title: "保存",
134
+ icon: unref(Save),
131
135
  loading: unref(state).loading,
132
136
  onClick: unref(handleSave)
133
- }, {
134
- default: withCtx(() => [..._cache[5] || (_cache[5] = [createTextVNode(" 保存 ", -1)])]),
135
- _: 1
136
- }, 8, ["loading", "onClick"])) : createCommentVNode("v-if", true)], 2)], 2)
137
+ }, null, 8, [
138
+ "icon",
139
+ "loading",
140
+ "onClick"
141
+ ])) : createCommentVNode("v-if", true)], 2)], 2)
137
142
  ], 2)) : createCommentVNode("v-if", true);
138
143
  };
139
144
  }
@@ -1 +1 @@
1
- {"version":3,"file":"batch-edit-form.js","names":[],"sources":["../../../src/components/batch-edit/batch-edit-form.vue"],"sourcesContent":["<template>\n <aside :class=\"cls.e('form')\" v-if=\"state.formVisible && !!props.model\">\n <header :class=\"cls.e('form-header')\">\n <span :class=\"[cls.e('form-icon'), bem.is(state.formActionType)]\">\n <u-icon>\n <component :is=\"headerInfo.icon\" />\n </u-icon>\n </span>\n <div :class=\"cls.e('form-title')\">\n <h3 :class=\"cls.e('form-title-text')\">{{ headerInfo.title }}</h3>\n </div>\n <u-button\n :class=\"cls.e('form-close')\"\n :icon=\"Close\"\n text\n circle\n size=\"small\"\n title=\"关闭\"\n @click=\"handleClose\"\n />\n </header>\n\n <u-scroll always :class=\"cls.e('form-body')\" :key=\"bodyKey\">\n <transition name=\"fade\" appear mode=\"out-in\">\n <u-form\n ref=\"formComponentRef\"\n :model=\"props.model\"\n :readonly=\"props.readonly\"\n :label-width=\"props.labelWidth\"\n @field:change=\"handleFieldChange\"\n >\n <slot\n v-bind=\"{\n row: state.row,\n depth: state.depth,\n indexes: state.indexPath,\n index: state.row?.index\n }\"\n />\n </u-form>\n </transition>\n </u-scroll>\n\n <footer :class=\"cls.e('form-footer')\">\n <span :class=\"cls.e('form-hint')\">\n <template v-if=\"props.readonly\"> 只读模式 </template>\n\n <span :class=\"{ [bem.is('concealed')]: !focused }\">\n <template v-if=\"!(state.formActionType === 'update' && props.quickEdit)\">\n <u-kbd>Ctrl + S</u-kbd> 保存 ·\n </template>\n <u-kbd>Esc</u-kbd> 关闭\n </span>\n </span>\n\n <div :class=\"cls.e('form-actions')\">\n <u-button text :loading=\"state.loading\" @click=\"handleClose\"> 取消 </u-button>\n <u-button v-if=\"showSaveBtn\" type=\"primary\" :loading=\"state.loading\" @click=\"handleSave\">\n 保存\n </u-button>\n </div>\n </footer>\n </aside>\n</template>\n\n<script lang=\"ts\" setup>\nimport { o } from '@cat-kit/core'\nimport { Close } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, shallowRef, toRef } from 'vue'\n\nimport type { _FormExposed, FormExposed } from '../../types/form'\nimport { UButton } from '../button'\nimport { UForm } from '../form'\nimport { UIcon } from '../icon'\nimport { UKbd } from '../kbd'\nimport { UScroll } from '../scroll'\nimport { BatchEditDIKey } from './di'\nimport { FORM_ACTION_HEADER_MAP } from './form-action-header'\n\ndefineOptions({ name: 'UBatchEditForm' })\n\nconst batchEditCtx = inject(BatchEditDIKey)!\n\nconst { cls, props, state, handleClose, handleSave, staticFeatures, dynamicFeatures } = batchEditCtx\n\nconst focused = toRef(batchEditCtx, 'focused')\n\nconst formComponentRef = shallowRef<FormExposed>()\n\ndefineExpose<_FormExposed>({\n el: toRef(() => formComponentRef.value?.el),\n validate: () => formComponentRef.value?.validate() ?? Promise.resolve(true),\n clearValidate: () => formComponentRef.value?.clearValidate(),\n reset: () => formComponentRef.value?.reset()\n})\n\nfunction handleFieldChange(field: string, value: any) {\n if (state.row && props.quickEdit) {\n o(state.row.data).set(field, value)\n }\n}\n\nconst creatable = computed(() => {\n if (state.formActionType !== 'create') return false\n return staticFeatures.value.has('create') || dynamicFeatures.value.create?.(state.row)\n})\n\nconst creatableChild = computed(() => {\n if (state.formActionType !== 'createChild') return false\n return staticFeatures.value.has('createChild') || dynamicFeatures.value.createChild?.(state.row)\n})\n\nconst updatable = computed(() => {\n if (state.formActionType !== 'update') return false\n return staticFeatures.value.has('update') || dynamicFeatures.value.update?.(state.row)\n})\n\nconst showSaveBtn = computed(() => {\n if (props.readonly) return false\n if (props.quickEdit && state.formActionType === 'update') return false\n return creatable.value || updatable.value || creatableChild.value\n})\n\nconst headerInfo = computed(() => FORM_ACTION_HEADER_MAP[state.formActionType])\n\n/** 通过 row 切换 / 类型切换驱动表单内容淡出淡入 */\nconst bodyKey = computed(() => {\n return `${state.formActionType}-${state.row?.uid ?? 'create'}-${state.parentRow?.uid ?? 'root'}`\n})\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"batch-edit-form.js","names":[],"sources":["../../../src/components/batch-edit/batch-edit-form.vue"],"sourcesContent":["<template>\n <aside :class=\"cls.e('form')\" v-if=\"state.formVisible && !!props.model\">\n <header :class=\"cls.e('form-header')\">\n <span :class=\"[cls.e('form-icon'), bem.is(state.formActionType)]\">\n <u-icon>\n <component :is=\"headerInfo.icon\" />\n </u-icon>\n </span>\n <div :class=\"cls.e('form-title')\">\n <h3 :class=\"cls.e('form-title-text')\">{{ headerInfo.title }}</h3>\n </div>\n <u-button\n :class=\"cls.e('form-close')\"\n :icon=\"Close\"\n text\n circle\n size=\"small\"\n title=\"关闭\"\n @click=\"handleClose\"\n />\n </header>\n\n <u-scroll always :class=\"cls.e('form-body')\" :key=\"bodyKey\">\n <transition name=\"fade\" appear mode=\"out-in\">\n <u-form\n ref=\"formComponentRef\"\n :model=\"props.model\"\n :readonly=\"props.readonly\"\n :label-width=\"props.labelWidth\"\n @field:change=\"handleFieldChange\"\n >\n <slot\n v-bind=\"{\n row: state.row,\n depth: state.depth,\n indexes: state.indexPath,\n index: state.row?.index\n }\"\n />\n </u-form>\n </transition>\n </u-scroll>\n\n <footer :class=\"cls.e('form-footer')\">\n <span :class=\"cls.e('form-hint')\">\n <template v-if=\"props.readonly\"> 只读模式 </template>\n\n <span :class=\"{ [bem.is('concealed')]: !focused }\">\n <template v-if=\"!(state.formActionType === 'update' && props.quickEdit)\">\n <u-kbd>Ctrl + S</u-kbd> 保存 ·\n </template>\n <u-kbd>Esc</u-kbd> 关闭\n </span>\n </span>\n\n <div :class=\"cls.e('form-actions')\">\n <u-button size=\"small\" text :loading=\"state.loading\" @click=\"handleClose\"> 取消 </u-button>\n <u-button\n v-if=\"showSaveBtn\"\n type=\"primary\"\n size=\"small\"\n title=\"保存\"\n :icon=\"Save\"\n :loading=\"state.loading\"\n @click=\"handleSave\"\n />\n </div>\n </footer>\n </aside>\n</template>\n\n<script lang=\"ts\" setup>\nimport { o } from '@cat-kit/core'\nimport { Close, Save } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, shallowRef, toRef } from 'vue'\n\nimport type { _FormExposed, FormExposed } from '../../types/form'\nimport { UButton } from '../button'\nimport { UForm } from '../form'\nimport { UIcon } from '../icon'\nimport { UKbd } from '../kbd'\nimport { UScroll } from '../scroll'\nimport { BatchEditDIKey } from './di'\nimport { FORM_ACTION_HEADER_MAP } from './form-action-header'\n\ndefineOptions({ name: 'UBatchEditForm' })\n\nconst batchEditCtx = inject(BatchEditDIKey)!\n\nconst { cls, props, state, handleClose, handleSave, staticFeatures, dynamicFeatures } = batchEditCtx\n\nconst focused = toRef(batchEditCtx, 'focused')\n\nconst formComponentRef = shallowRef<FormExposed>()\n\ndefineExpose<_FormExposed>({\n el: toRef(() => formComponentRef.value?.el),\n validate: () => formComponentRef.value?.validate() ?? Promise.resolve(true),\n clearValidate: () => formComponentRef.value?.clearValidate(),\n reset: () => formComponentRef.value?.reset()\n})\n\nfunction handleFieldChange(field: string, value: any) {\n if (state.row && props.quickEdit) {\n o(state.row.data).set(field, value)\n }\n}\n\nconst creatable = computed(() => {\n if (state.formActionType !== 'create') return false\n return staticFeatures.value.has('create') || dynamicFeatures.value.create?.(state.row)\n})\n\nconst creatableChild = computed(() => {\n if (state.formActionType !== 'createChild') return false\n return staticFeatures.value.has('createChild') || dynamicFeatures.value.createChild?.(state.row)\n})\n\nconst updatable = computed(() => {\n if (state.formActionType !== 'update') return false\n return staticFeatures.value.has('update') || dynamicFeatures.value.update?.(state.row)\n})\n\nconst showSaveBtn = computed(() => {\n if (props.readonly) return false\n if (props.quickEdit && state.formActionType === 'update') return false\n return creatable.value || updatable.value || creatableChild.value\n})\n\nconst headerInfo = computed(() => FORM_ACTION_HEADER_MAP[state.formActionType])\n\n/** 通过 row 切换 / 类型切换驱动表单内容淡出淡入 */\nconst bodyKey = computed(() => {\n return `${state.formActionType}-${state.row?.uid ?? 'create'}-${state.parentRow?.uid ?? 'root'}`\n})\n</script>\n"],"mappings":"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"}
@@ -12,6 +12,7 @@ function useEditState(options) {
12
12
  };
13
13
  const state = shallowReactive({ ...defaultState });
14
14
  function resetState() {
15
+ formRef.value?.reset();
15
16
  if (state.row) state.row.isCurrent = false;
16
17
  Object.keys(state).forEach((key) => {
17
18
  delete state[key];
@@ -1 +1 @@
1
- {"version":3,"file":"use-edit-state.js","names":[],"sources":["../../../src/components/batch-edit/use-edit-state.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { nextTick, shallowReactive, watch, type ShallowRef } from 'vue'\n\nimport type { BatchEditProps, BatchEditStates, FormExposed } from '../../types'\n\ninterface Options {\n props: BatchEditProps\n formRef: ShallowRef<FormExposed | null>\n}\n\nexport function useEditState(options: Options) {\n const { props, formRef } = options\n\n const defaultState: BatchEditStates = {\n depth: -1,\n formVisible: false,\n formActionType: 'create',\n loading: false,\n indexPath: []\n }\n\n const state = shallowReactive({ ...defaultState })\n\n function resetState() {\n if (state.row) {\n state.row.isCurrent = false\n }\n\n Object.keys(state).forEach((key) => {\n delete state[key]\n })\n\n Object.assign(state, defaultState)\n }\n\n watch(\n () => state.row,\n (row) => {\n formRef.value?.reset()\n\n if (row) {\n /** 延迟设置 model,防止表单初始状态丢失 */\n nextTick(() => {\n if (props.model) {\n o(props.model).deepExtend(row.data)\n }\n })\n state.formActionType = props.readonly ? 'view' : 'update'\n state.formVisible = true\n state.depth = row.depth\n state.indexPath = [...row.indexes]\n\n return\n }\n\n resetState()\n }\n )\n\n return { state, resetState }\n}\n"],"mappings":";;;AAUA,SAAgB,aAAa,SAAkB;CAC7C,MAAM,EAAE,OAAO,YAAY;CAE3B,MAAM,eAAgC;EACpC,OAAO;EACP,aAAa;EACb,gBAAgB;EAChB,SAAS;EACT,WAAW,CAAC;CACd;CAEA,MAAM,QAAQ,gBAAgB,EAAE,GAAG,aAAa,CAAC;CAEjD,SAAS,aAAa;EACpB,IAAI,MAAM,KACR,MAAM,IAAI,YAAY;EAGxB,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,QAAQ;GAClC,OAAO,MAAM;EACf,CAAC;EAED,OAAO,OAAO,OAAO,YAAY;CACnC;CAEA,YACQ,MAAM,MACX,QAAQ;EACP,QAAQ,OAAO,MAAM;EAErB,IAAI,KAAK;;GAEP,eAAe;IACb,IAAI,MAAM,OACR,EAAE,MAAM,KAAK,CAAC,CAAC,WAAW,IAAI,IAAI;GAEtC,CAAC;GACD,MAAM,iBAAiB,MAAM,WAAW,SAAS;GACjD,MAAM,cAAc;GACpB,MAAM,QAAQ,IAAI;GAClB,MAAM,YAAY,CAAC,GAAG,IAAI,OAAO;GAEjC;EACF;EAEA,WAAW;CACb,CACF;CAEA,OAAO;EAAE;EAAO;CAAW;AAC7B"}
1
+ {"version":3,"file":"use-edit-state.js","names":[],"sources":["../../../src/components/batch-edit/use-edit-state.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { nextTick, shallowReactive, watch, type ShallowRef } from 'vue'\n\nimport type { BatchEditProps, BatchEditStates, FormExposed } from '../../types'\n\ninterface Options {\n props: BatchEditProps\n formRef: ShallowRef<FormExposed | null>\n}\n\nexport function useEditState(options: Options) {\n const { props, formRef } = options\n\n const defaultState: BatchEditStates = {\n depth: -1,\n formVisible: false,\n formActionType: 'create',\n loading: false,\n indexPath: []\n }\n\n const state = shallowReactive({ ...defaultState })\n\n function resetState() {\n // 销毁表单前先重置,避免下次 v-if 重挂载时 Form 拍到脏 model\n formRef.value?.reset()\n\n if (state.row) {\n state.row.isCurrent = false\n }\n\n Object.keys(state).forEach((key) => {\n delete state[key]\n })\n\n Object.assign(state, defaultState)\n }\n\n watch(\n () => state.row,\n (row) => {\n formRef.value?.reset()\n\n if (row) {\n /** 延迟设置 model,防止表单初始状态丢失 */\n nextTick(() => {\n if (props.model) {\n o(props.model).deepExtend(row.data)\n }\n })\n state.formActionType = props.readonly ? 'view' : 'update'\n state.formVisible = true\n state.depth = row.depth\n state.indexPath = [...row.indexes]\n\n return\n }\n\n resetState()\n }\n )\n\n return { state, resetState }\n}\n"],"mappings":";;;AAUA,SAAgB,aAAa,SAAkB;CAC7C,MAAM,EAAE,OAAO,YAAY;CAE3B,MAAM,eAAgC;EACpC,OAAO;EACP,aAAa;EACb,gBAAgB;EAChB,SAAS;EACT,WAAW,CAAC;CACd;CAEA,MAAM,QAAQ,gBAAgB,EAAE,GAAG,aAAa,CAAC;CAEjD,SAAS,aAAa;EAEpB,QAAQ,OAAO,MAAM;EAErB,IAAI,MAAM,KACR,MAAM,IAAI,YAAY;EAGxB,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,QAAQ;GAClC,OAAO,MAAM;EACf,CAAC;EAED,OAAO,OAAO,OAAO,YAAY;CACnC;CAEA,YACQ,MAAM,MACX,QAAQ;EACP,QAAQ,OAAO,MAAM;EAErB,IAAI,KAAK;;GAEP,eAAe;IACb,IAAI,MAAM,OACR,EAAE,MAAM,KAAK,CAAC,CAAC,WAAW,IAAI,IAAI;GAEtC,CAAC;GACD,MAAM,iBAAiB,MAAM,WAAW,SAAS;GACjD,MAAM,cAAc;GACpB,MAAM,QAAQ,IAAI;GAClB,MAAM,YAAY,CAAC,GAAG,IAAI,OAAO;GAEjC;EACF;EAEA,WAAW;CACb,CACF;CAEA,OAAO;EAAE;EAAO;CAAW;AAC7B"}
@@ -103,6 +103,9 @@ function useHandlers(options) {
103
103
  if (result) item = result;
104
104
  }
105
105
  insert(item);
106
+ const next = last(state.indexPath) + 1;
107
+ state.indexPath = [...state.indexPath.slice(0, -1), next];
108
+ formRef.value?.reset();
106
109
  return;
107
110
  }
108
111
  if (saveMethod) {
@@ -1 +1 @@
1
- {"version":3,"file":"use-handlers.js","names":[],"sources":["../../../src/components/batch-edit/use-handlers.ts"],"sourcesContent":["import { last, o } from '@cat-kit/core'\nimport { computed, nextTick, type ShallowRef } from 'vue'\n\nimport type {\n TableRow,\n BatchEditEmits,\n BatchEditProps,\n BatchEditStates,\n FormExposed\n} from '../../types'\n\n/** 按索引路径在原始数据树中定位节点(等价于历史 Forest.visit 路径访问) */\nfunction visitDataByIndexPath<T extends Record<string, unknown>>(\n roots: T[],\n indexPath: number[],\n childrenKey: string\n): T | undefined {\n if (!indexPath.length) return undefined\n let currentList: T[] | undefined = roots\n let node: T | undefined\n for (let i = 0; i < indexPath.length; i++) {\n const idx = indexPath[i]!\n if (!currentList) return undefined\n node = currentList[idx]\n if (node === undefined) return undefined\n if (i < indexPath.length - 1) {\n const next = node[childrenKey]\n currentList = Array.isArray(next) ? (next as T[]) : undefined\n }\n }\n return node\n}\n\ninterface Options {\n props: BatchEditProps\n emit: BatchEditEmits\n state: BatchEditStates\n formRef: ShallowRef<FormExposed | null>\n resetState: () => void\n}\n\nexport interface EditReturned {\n handleSave: () => Promise<void>\n handleClose: () => void\n handleCreate: () => void\n handleDelete: (row: TableRow) => Promise<void>\n handleInsertToNext: (row: TableRow) => void\n handleInsertToPrev: (row: TableRow) => void\n handleInsertChild: (row: TableRow) => void\n}\n\nexport function useHandlers(options: Options): EditReturned {\n const { props, emit, state, resetState, formRef } = options\n\n const childrenKey = computed(() => {\n return typeof props.tree === 'string' ? props.tree : 'children'\n })\n\n function getInsertData(): Record<string, any> {\n return o(props.model ?? {}).copy()\n }\n\n /**\n * 插入数据\n * @param item 数据\n */\n function insert(item: Record<string, any>) {\n const data = [...(props.data ?? [])]\n\n const parent = visitDataByIndexPath(data ?? [], state.indexPath.slice(0, -1), childrenKey.value)\n\n if (parent) {\n const children = parent[childrenKey.value]\n if (!children) {\n parent[childrenKey.value] = [item]\n } else {\n children.splice(last(state.indexPath), 0, item)\n }\n } else {\n data.splice(last(state.indexPath), 0, item)\n }\n\n emit('update:data', data)\n\n return item\n }\n\n /**\n * 点击新增按钮\n */\n function handleCreate() {\n const { data } = props\n\n resetState()\n\n nextTick(() => {\n state.formActionType = 'create'\n state.formVisible = true\n state.depth = 1\n state.indexPath = [data?.length ?? 0]\n })\n }\n\n function handleInsertToPrev(row: TableRow) {\n resetState()\n state.formActionType = 'create'\n state.formVisible = true\n state.depth = row.depth\n state.indexPath = [...row.indexes]\n }\n\n function handleInsertToNext(row: TableRow) {\n resetState()\n state.formActionType = 'create'\n state.formVisible = true\n state.depth = row.depth\n state.indexPath = [...row.indexes.slice(0, -1), row.index + 1]\n }\n\n function handleInsertChild(row: TableRow) {\n resetState()\n state.formActionType = 'createChild'\n state.formVisible = true\n state.parentRow = row\n state.depth = row.depth + 1\n row.expanded = true\n state.indexPath = [...row.indexes, row.children?.length ?? 0]\n }\n\n function runWithLoading<Arg extends any[]>(fn: (...args: Arg) => Promise<void> | void) {\n return async (...args: Arg) => {\n state.loading = true\n if (state.row) {\n state.row.operating = true\n }\n try {\n await fn.apply(null, args)\n } catch (error) {\n console.error(error)\n }\n state.loading = false\n if (state.row) {\n state.row.operating = false\n }\n }\n }\n\n /** 保存 */\n const handleSave = runWithLoading(async () => {\n const { model, saveMethod } = props\n const valid = await formRef.value?.validate()\n if (!model || !valid) return\n\n let item = getInsertData()\n\n // 新增\n if (state.formActionType === 'create' || state.formActionType === 'createChild') {\n await props.beforeCreate?.(item, state.parentRow?.data)\n if (saveMethod) {\n const result = await saveMethod(item, state.formActionType, state.parentRow?.data)\n if (result) {\n item = result\n }\n }\n insert(item)\n return\n }\n\n // 普通模式下的编辑\n if (saveMethod) {\n const result = await saveMethod(item, state.formActionType, state.parentRow?.data)\n if (result) {\n item = result\n }\n }\n\n const { row } = state\n row &&\n Object.keys(item).forEach((key) => {\n o(row.data).set(key, o(item).get(key))\n })\n })\n\n const handleDelete = runWithLoading(async (row: TableRow) => {\n const { deleteMethod } = props\n\n if (deleteMethod) {\n await deleteMethod([row.data])\n }\n\n const data = [...(props.data ?? [])]\n\n const parent = visitDataByIndexPath(data, row.indexes.slice(0, -1), childrenKey.value)\n\n if (parent) {\n const children = parent[childrenKey.value]\n if (children) {\n children.splice(row.index, 1)\n } else {\n console.error('行路径不正确')\n }\n } else {\n data.splice(row.index, 1)\n }\n\n if (state.row === row) {\n resetState()\n }\n\n emit('update:data', data)\n })\n\n function handleClose() {\n resetState()\n }\n\n return {\n handleDelete,\n handleInsertChild,\n handleInsertToNext,\n handleInsertToPrev,\n handleCreate,\n handleClose,\n handleSave\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-handlers.js","names":[],"sources":["../../../src/components/batch-edit/use-handlers.ts"],"sourcesContent":["import { last, o } from '@cat-kit/core'\nimport { computed, nextTick, type ShallowRef } from 'vue'\n\nimport type {\n TableRow,\n BatchEditEmits,\n BatchEditProps,\n BatchEditStates,\n FormExposed\n} from '../../types'\n\n/** 按索引路径在原始数据树中定位节点(等价于历史 Forest.visit 路径访问) */\nfunction visitDataByIndexPath<T extends Record<string, unknown>>(\n roots: T[],\n indexPath: number[],\n childrenKey: string\n): T | undefined {\n if (!indexPath.length) return undefined\n let currentList: T[] | undefined = roots\n let node: T | undefined\n for (let i = 0; i < indexPath.length; i++) {\n const idx = indexPath[i]!\n if (!currentList) return undefined\n node = currentList[idx]\n if (node === undefined) return undefined\n if (i < indexPath.length - 1) {\n const next = node[childrenKey]\n currentList = Array.isArray(next) ? (next as T[]) : undefined\n }\n }\n return node\n}\n\ninterface Options {\n props: BatchEditProps\n emit: BatchEditEmits\n state: BatchEditStates\n formRef: ShallowRef<FormExposed | null>\n resetState: () => void\n}\n\nexport interface EditReturned {\n handleSave: () => Promise<void>\n handleClose: () => void\n handleCreate: () => void\n handleDelete: (row: TableRow) => Promise<void>\n handleInsertToNext: (row: TableRow) => void\n handleInsertToPrev: (row: TableRow) => void\n handleInsertChild: (row: TableRow) => void\n}\n\nexport function useHandlers(options: Options): EditReturned {\n const { props, emit, state, resetState, formRef } = options\n\n const childrenKey = computed(() => {\n return typeof props.tree === 'string' ? props.tree : 'children'\n })\n\n function getInsertData(): Record<string, any> {\n return o(props.model ?? {}).copy()\n }\n\n /**\n * 插入数据\n * @param item 数据\n */\n function insert(item: Record<string, any>) {\n const data = [...(props.data ?? [])]\n\n const parent = visitDataByIndexPath(data ?? [], state.indexPath.slice(0, -1), childrenKey.value)\n\n if (parent) {\n const children = parent[childrenKey.value]\n if (!children) {\n parent[childrenKey.value] = [item]\n } else {\n children.splice(last(state.indexPath), 0, item)\n }\n } else {\n data.splice(last(state.indexPath), 0, item)\n }\n\n emit('update:data', data)\n\n return item\n }\n\n /**\n * 点击新增按钮\n */\n function handleCreate() {\n const { data } = props\n\n resetState()\n\n nextTick(() => {\n state.formActionType = 'create'\n state.formVisible = true\n state.depth = 1\n state.indexPath = [data?.length ?? 0]\n })\n }\n\n function handleInsertToPrev(row: TableRow) {\n resetState()\n state.formActionType = 'create'\n state.formVisible = true\n state.depth = row.depth\n state.indexPath = [...row.indexes]\n }\n\n function handleInsertToNext(row: TableRow) {\n resetState()\n state.formActionType = 'create'\n state.formVisible = true\n state.depth = row.depth\n state.indexPath = [...row.indexes.slice(0, -1), row.index + 1]\n }\n\n function handleInsertChild(row: TableRow) {\n resetState()\n state.formActionType = 'createChild'\n state.formVisible = true\n state.parentRow = row\n state.depth = row.depth + 1\n row.expanded = true\n state.indexPath = [...row.indexes, row.children?.length ?? 0]\n }\n\n function runWithLoading<Arg extends any[]>(fn: (...args: Arg) => Promise<void> | void) {\n return async (...args: Arg) => {\n state.loading = true\n if (state.row) {\n state.row.operating = true\n }\n try {\n await fn.apply(null, args)\n } catch (error) {\n console.error(error)\n }\n state.loading = false\n if (state.row) {\n state.row.operating = false\n }\n }\n }\n\n /** 保存 */\n const handleSave = runWithLoading(async () => {\n const { model, saveMethod } = props\n const valid = await formRef.value?.validate()\n if (!model || !valid) return\n\n let item = getInsertData()\n\n // 新增\n if (state.formActionType === 'create' || state.formActionType === 'createChild') {\n await props.beforeCreate?.(item, state.parentRow?.data)\n if (saveMethod) {\n const result = await saveMethod(item, state.formActionType, state.parentRow?.data)\n if (result) {\n item = result\n }\n }\n insert(item)\n\n // 推进插入点,便于连续新增;并重置表单到初始默认值\n const next = last(state.indexPath) + 1\n state.indexPath = [...state.indexPath.slice(0, -1), next]\n formRef.value?.reset()\n return\n }\n\n // 普通模式下的编辑\n if (saveMethod) {\n const result = await saveMethod(item, state.formActionType, state.parentRow?.data)\n if (result) {\n item = result\n }\n }\n\n const { row } = state\n row &&\n Object.keys(item).forEach((key) => {\n o(row.data).set(key, o(item).get(key))\n })\n })\n\n const handleDelete = runWithLoading(async (row: TableRow) => {\n const { deleteMethod } = props\n\n if (deleteMethod) {\n await deleteMethod([row.data])\n }\n\n const data = [...(props.data ?? [])]\n\n const parent = visitDataByIndexPath(data, row.indexes.slice(0, -1), childrenKey.value)\n\n if (parent) {\n const children = parent[childrenKey.value]\n if (children) {\n children.splice(row.index, 1)\n } else {\n console.error('行路径不正确')\n }\n } else {\n data.splice(row.index, 1)\n }\n\n if (state.row === row) {\n resetState()\n }\n\n emit('update:data', data)\n })\n\n function handleClose() {\n resetState()\n }\n\n return {\n handleDelete,\n handleInsertChild,\n handleInsertToNext,\n handleInsertToPrev,\n handleCreate,\n handleClose,\n handleSave\n }\n}\n"],"mappings":"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"}
@@ -10,7 +10,9 @@ function useShortcutKey(options) {
10
10
  if (!e.currentTarget.contains(e.relatedTarget)) focused.value = false;
11
11
  }
12
12
  function runEscape(e) {
13
+ if (!state.formVisible) return;
13
14
  e.preventDefault();
15
+ e.stopPropagation();
14
16
  onClose();
15
17
  }
16
18
  function runSave(e) {
@@ -1 +1 @@
1
- {"version":3,"file":"use-shortcut-key.js","names":[],"sources":["../../../src/components/batch-edit/use-shortcut-key.ts"],"sourcesContent":["import { shallowRef } from 'vue'\n\nimport type { BatchEditProps, BatchEditStates } from '../../types'\n\ninterface Options {\n props: BatchEditProps\n onSave: () => void\n onClose: () => void\n state: BatchEditStates\n}\nexport function useShortcutKey(options: Options) {\n const { props, onClose, onSave, state } = options\n const focused = shallowRef(false)\n\n function handleFocusIn() {\n focused.value = true\n }\n\n function handleFocusOut(e: FocusEvent) {\n const root = e.currentTarget as HTMLElement\n if (!root.contains(e.relatedTarget as Node | null)) {\n focused.value = false\n }\n }\n\n function runEscape(e: KeyboardEvent) {\n e.preventDefault()\n onClose()\n }\n\n function runSave(e: KeyboardEvent) {\n if (!state.formVisible) return\n if (props.quickEdit && state.formActionType === 'update') return\n e.preventDefault()\n onSave()\n }\n\n /**\n * 键盘快捷键(仅组件获焦时生效):\n * - Esc 关闭表单\n * - ⌘/Ctrl + S 保存(快速编辑模式下编辑行时除外)\n */\n function handleKeydown(e: KeyboardEvent) {\n if (!focused.value) return\n if (props.readonly && e.key !== 'Escape') return\n\n if (e.key === 'Escape') return runEscape(e)\n\n const meta = e.metaKey || e.ctrlKey\n if (!meta) return\n\n const key = e.key.toLowerCase()\n\n if (key === 's') return runSave(e)\n }\n\n return { focused, handleFocusIn, handleFocusOut, handleKeydown }\n}\n"],"mappings":";;AAUA,SAAgB,eAAe,SAAkB;CAC/C,MAAM,EAAE,OAAO,SAAS,QAAQ,UAAU;CAC1C,MAAM,UAAU,WAAW,KAAK;CAEhC,SAAS,gBAAgB;EACvB,QAAQ,QAAQ;CAClB;CAEA,SAAS,eAAe,GAAe;EAErC,IAAI,CADS,EAAE,cACL,SAAS,EAAE,aAA4B,GAC/C,QAAQ,QAAQ;CAEpB;CAEA,SAAS,UAAU,GAAkB;EACnC,EAAE,eAAe;EACjB,QAAQ;CACV;CAEA,SAAS,QAAQ,GAAkB;EACjC,IAAI,CAAC,MAAM,aAAa;EACxB,IAAI,MAAM,aAAa,MAAM,mBAAmB,UAAU;EAC1D,EAAE,eAAe;EACjB,OAAO;CACT;;;;;;CAOA,SAAS,cAAc,GAAkB;EACvC,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI,MAAM,YAAY,EAAE,QAAQ,UAAU;EAE1C,IAAI,EAAE,QAAQ,UAAU,OAAO,UAAU,CAAC;EAG1C,IAAI,EADS,EAAE,WAAW,EAAE,UACjB;EAIX,IAFY,EAAE,IAAI,YAEZ,MAAM,KAAK,OAAO,QAAQ,CAAC;CACnC;CAEA,OAAO;EAAE;EAAS;EAAe;EAAgB;CAAc;AACjE"}
1
+ {"version":3,"file":"use-shortcut-key.js","names":[],"sources":["../../../src/components/batch-edit/use-shortcut-key.ts"],"sourcesContent":["import { shallowRef } from 'vue'\n\nimport type { BatchEditProps, BatchEditStates } from '../../types'\n\ninterface Options {\n props: BatchEditProps\n onSave: () => void\n onClose: () => void\n state: BatchEditStates\n}\nexport function useShortcutKey(options: Options) {\n const { props, onClose, onSave, state } = options\n const focused = shallowRef(false)\n\n function handleFocusIn() {\n focused.value = true\n }\n\n function handleFocusOut(e: FocusEvent) {\n const root = e.currentTarget as HTMLElement\n if (!root.contains(e.relatedTarget as Node | null)) {\n focused.value = false\n }\n }\n\n function runEscape(e: KeyboardEvent) {\n if (!state.formVisible) return\n e.preventDefault()\n e.stopPropagation()\n onClose()\n }\n\n function runSave(e: KeyboardEvent) {\n if (!state.formVisible) return\n if (props.quickEdit && state.formActionType === 'update') return\n e.preventDefault()\n onSave()\n }\n\n /**\n * 键盘快捷键(仅组件获焦时生效):\n * - Esc 关闭表单\n * - ⌘/Ctrl + S 保存(快速编辑模式下编辑行时除外)\n */\n function handleKeydown(e: KeyboardEvent) {\n if (!focused.value) return\n if (props.readonly && e.key !== 'Escape') return\n\n if (e.key === 'Escape') return runEscape(e)\n\n const meta = e.metaKey || e.ctrlKey\n if (!meta) return\n\n const key = e.key.toLowerCase()\n\n if (key === 's') return runSave(e)\n }\n\n return { focused, handleFocusIn, handleFocusOut, handleKeydown }\n}\n"],"mappings":";;AAUA,SAAgB,eAAe,SAAkB;CAC/C,MAAM,EAAE,OAAO,SAAS,QAAQ,UAAU;CAC1C,MAAM,UAAU,WAAW,KAAK;CAEhC,SAAS,gBAAgB;EACvB,QAAQ,QAAQ;CAClB;CAEA,SAAS,eAAe,GAAe;EAErC,IAAI,CADS,EAAE,cACL,SAAS,EAAE,aAA4B,GAC/C,QAAQ,QAAQ;CAEpB;CAEA,SAAS,UAAU,GAAkB;EACnC,IAAI,CAAC,MAAM,aAAa;EACxB,EAAE,eAAe;EACjB,EAAE,gBAAgB;EAClB,QAAQ;CACV;CAEA,SAAS,QAAQ,GAAkB;EACjC,IAAI,CAAC,MAAM,aAAa;EACxB,IAAI,MAAM,aAAa,MAAM,mBAAmB,UAAU;EAC1D,EAAE,eAAe;EACjB,OAAO;CACT;;;;;;CAOA,SAAS,cAAc,GAAkB;EACvC,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI,MAAM,YAAY,EAAE,QAAQ,UAAU;EAE1C,IAAI,EAAE,QAAQ,UAAU,OAAO,UAAU,CAAC;EAG1C,IAAI,EADS,EAAE,WAAW,EAAE,UACjB;EAIX,IAFY,EAAE,IAAI,YAEZ,MAAM,KAAK,OAAO,QAAQ,CAAC;CACnC;CAEA,OAAO;EAAE;EAAS;EAAe;EAAgB;CAAc;AACjE"}
@@ -2,13 +2,13 @@ import { CascadeProps } from "../../types/cascade.js";
2
2
  //#region src/components/cascade/cascade.vue.d.ts
3
3
  declare const __VLS_export: import("vue").DefineComponent<CascadeProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
4
4
  "update:modelValue": (value?: string | string[] | undefined) => any;
5
- clear: () => any;
6
5
  change: (...args: [item?: Record<string, any> | undefined] | [items: Record<string, any>[]]) => any;
6
+ clear: () => any;
7
7
  "update:label": (label?: string | string[] | undefined) => any;
8
8
  }, string, import("vue").PublicProps, Readonly<CascadeProps> & Readonly<{
9
9
  "onUpdate:modelValue"?: ((value?: string | string[] | undefined) => any) | undefined;
10
- onClear?: (() => any) | undefined;
11
10
  onChange?: ((...args: [item?: Record<string, any> | undefined] | [items: Record<string, any>[]]) => any) | undefined;
11
+ onClear?: (() => any) | undefined;
12
12
  "onUpdate:label"?: ((label?: string | string[] | undefined) => any) | undefined;
13
13
  }>, {
14
14
  labelKey: string;
@@ -25,11 +25,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
25
25
  const props = __props;
26
26
  const { size } = useFallbackProps([props], { size: "default" });
27
27
  const cls = bem("grid-input");
28
- const _numberReg = computed(() => props.zero ? /^[0-9]$/ : /^[1-9]$/);
28
+ const numberReg = computed(() => props.zero ? /^[0-9]$/ : /^[1-9]$/);
29
29
  const valueArray = ref([]);
30
30
  const position = ref(-1);
31
31
  const blur = ref(false);
32
32
  const instance = getCurrentInstance();
33
+ const emitValue = () => {
34
+ const value = valueArray.value.join(props.separator);
35
+ emit("input", value);
36
+ emit("update:modelValue", value);
37
+ };
33
38
  const focus = async (index) => {
34
39
  if (index === void 0) {
35
40
  focus(position.value);
@@ -42,38 +47,39 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
42
47
  };
43
48
  const changeValue = (value, index) => {
44
49
  if (position.value === props.length) return;
45
- if (valueArray.value[index]) valueArray.value.splice(index, 1, value);
50
+ if (index < valueArray.value.length) valueArray.value.splice(index, 1, value);
46
51
  else if (index === valueArray.value.length) valueArray.value.push(value);
47
52
  else return;
48
53
  position.value++;
49
54
  focus();
50
- emit("input", valueArray.value.filter((v) => v).join(props.separator));
55
+ emitValue();
51
56
  };
52
57
  const baseOperation = {
53
- Backspace: (value, index) => {
54
- if (value) valueArray.value.splice(index, 1);
58
+ Backspace: (_value, index) => {
59
+ if (index < valueArray.value.length) valueArray.value.splice(index, 1);
55
60
  else if (index - 1 >= 0) valueArray.value.splice(index - 1, 1);
56
61
  if (index !== 0) focus(index - 1);
57
- emit("input", valueArray.value.filter((v) => v).join(props.separator));
62
+ emitValue();
58
63
  },
59
- ArrowLeft: (value, index) => {
64
+ ArrowLeft: (_value, index) => {
60
65
  if (index === 0) return;
61
66
  focus(index - 1);
62
67
  },
63
- ArrowRight: (value, index) => {
68
+ ArrowRight: (_value, index) => {
64
69
  if (index === valueArray.value.length) return;
65
70
  focus(index + 1);
66
71
  }
67
72
  };
68
73
  const onKeydown = (e, value, index) => {
69
- if (_numberReg.value.test(e.key)) {
74
+ if (numberReg.value.test(e.key)) {
70
75
  changeValue(e.key, index);
71
76
  return;
72
77
  }
73
- baseOperation[e.key](value, index);
78
+ baseOperation[e.key]?.(value, index);
74
79
  };
75
80
  const clear = () => {
76
- while (valueArray.value.length) valueArray.value.pop();
81
+ valueArray.value = [];
82
+ position.value = -1;
77
83
  };
78
84
  __expose({ clear });
79
85
  return (_ctx, _cache) => {
@@ -87,7 +93,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
87
93
  ref_for: true,
88
94
  ref: "items",
89
95
  class: normalizeClass([unref(cls).e("item"), unref(bem).is("focus", index === position.value && !blur.value)])
90
- }, [valueArray.value[index] ? (openBlock(), createElementBlock("span", {
96
+ }, [index < valueArray.value.length ? (openBlock(), createElementBlock("span", {
91
97
  key: 0,
92
98
  class: normalizeClass(unref(cls).em("item", "text"))
93
99
  }, toDisplayString(valueArray.value[index]), 3)) : index === position.value ? (openBlock(), createElementBlock("span", {
@@ -1 +1 @@
1
- {"version":3,"file":"grid-input.js","names":[],"sources":["../../../src/components/grid-input/grid-input.vue"],"sourcesContent":["<template>\n <ul :class=\"[cls.b, cls.m(size)]\">\n <li\n v-for=\"(_, index) in length\"\n :key=\"index\"\n tabindex=\"0\"\n @keydown=\"onKeydown($event, valueArray[index]!, index)\"\n @focus=\"focus(index)\"\n @blur=\"index === position && (blur = true)\"\n ref=\"items\"\n :class=\"[cls.e('item'), bem.is('focus', index === position && !blur)]\"\n >\n <span :class=\"cls.em('item', 'text')\" v-if=\"valueArray[index]\">\n {{ valueArray[index] }}\n </span>\n\n <span :class=\"cls.em('item', 'cursor')\" v-else-if=\"index === position\"></span>\n </li>\n </ul>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps } from '@veltra/compositions'\nimport { bem } from '@veltra/utils'\nimport { computed, ref, getCurrentInstance } from 'vue'\n\nimport type { GridInputProps, GridInputEmits, GridInputExposed } from '../../types'\n\ndefineOptions({ name: 'UGridInput' })\n\nconst emit = defineEmits<GridInputEmits>()\n\nconst props = withDefaults(defineProps<GridInputProps>(), {\n length: 6,\n zero: false,\n separator: '-'\n})\n\nconst { size } = useFallbackProps([props], { size: 'default' })\n\nconst cls = bem('grid-input')\n\nconst _numberReg = computed(() => (props.zero ? /^[0-9]$/ : /^[1-9]$/))\n\nconst valueArray = ref<string[]>([])\n\nconst position = ref<number>(-1)\n\nconst blur = ref<boolean>(false)\n\nconst instance = getCurrentInstance()\n\nconst focus = async (index?: number) => {\n if (index === undefined) {\n focus(position.value)\n return\n }\n if (index !== 0 && index > valueArray.value.length) {\n index = valueArray.value.length\n }\n position.value = index\n const items = (instance?.refs as { items?: unknown })?.items as any\n let dom = items?.[index]\n dom?.focus()\n blur.value = false\n}\n\nconst changeValue = (value: string, index: number) => {\n if (position.value === props.length) return\n const currentValue = valueArray.value[index]\n\n if (currentValue) {\n valueArray.value.splice(index, 1, value)\n } else if (index === valueArray.value.length) {\n valueArray.value.push(value)\n } else {\n return\n }\n position.value++\n focus()\n\n emit('input', valueArray.value.filter((v) => v).join(props.separator))\n}\n\nconst baseOperation = {\n Backspace: (value: string, index: number) => {\n if (value) {\n valueArray.value.splice(index, 1)\n } else if (index - 1 >= 0) {\n valueArray.value.splice(index - 1, 1)\n }\n if (index !== 0) {\n focus(index - 1)\n }\n emit('input', valueArray.value.filter((v) => v).join(props.separator))\n },\n ArrowLeft: (value: string, index: number) => {\n if (index === 0) return\n focus(index - 1)\n },\n ArrowRight: (value: string, index: number) => {\n if (index === valueArray.value.length) return\n focus(index + 1)\n }\n}\n\nconst onKeydown = (e: any, value: string, index: number) => {\n if (_numberReg.value.test(e.key)) {\n changeValue(e.key, index)\n return\n }\n baseOperation[e.key](value, index)\n}\n\nconst clear = () => {\n while (valueArray.value.length) {\n valueArray.value.pop()\n }\n}\n\ndefineExpose<GridInputExposed>({ clear })\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"grid-input.js","names":[],"sources":["../../../src/components/grid-input/grid-input.vue"],"sourcesContent":["<template>\n <ul :class=\"[cls.b, cls.m(size)]\">\n <li\n v-for=\"(_, index) in length\"\n :key=\"index\"\n tabindex=\"0\"\n @keydown=\"onKeydown($event, valueArray[index]!, index)\"\n @focus=\"focus(index)\"\n @blur=\"index === position && (blur = true)\"\n ref=\"items\"\n :class=\"[cls.e('item'), bem.is('focus', index === position && !blur)]\"\n >\n <span :class=\"cls.em('item', 'text')\" v-if=\"index < valueArray.length\">\n {{ valueArray[index] }}\n </span>\n\n <span :class=\"cls.em('item', 'cursor')\" v-else-if=\"index === position\"></span>\n </li>\n </ul>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps } from '@veltra/compositions'\nimport { bem } from '@veltra/utils'\nimport { computed, ref, getCurrentInstance } from 'vue'\n\nimport type { GridInputProps, GridInputEmits, GridInputExposed } from '../../types'\n\ndefineOptions({ name: 'UGridInput' })\n\nconst emit = defineEmits<GridInputEmits>()\n\nconst props = withDefaults(defineProps<GridInputProps>(), {\n length: 6,\n zero: false,\n separator: '-'\n})\n\nconst { size } = useFallbackProps([props], { size: 'default' })\n\nconst cls = bem('grid-input')\n\nconst numberReg = computed(() => (props.zero ? /^[0-9]$/ : /^[1-9]$/))\n\nconst valueArray = ref<string[]>([])\n\nconst position = ref<number>(-1)\n\nconst blur = ref<boolean>(false)\n\nconst instance = getCurrentInstance()\n\nconst emitValue = () => {\n const value = valueArray.value.join(props.separator)\n emit('input', value)\n emit('update:modelValue', value)\n}\n\nconst focus = async (index?: number) => {\n if (index === undefined) {\n focus(position.value)\n return\n }\n if (index !== 0 && index > valueArray.value.length) {\n index = valueArray.value.length\n }\n position.value = index\n const items = (instance?.refs as { items?: unknown })?.items as any\n let dom = items?.[index]\n dom?.focus()\n blur.value = false\n}\n\nconst changeValue = (value: string, index: number) => {\n if (position.value === props.length) return\n\n if (index < valueArray.value.length) {\n valueArray.value.splice(index, 1, value)\n } else if (index === valueArray.value.length) {\n valueArray.value.push(value)\n } else {\n return\n }\n position.value++\n focus()\n\n emitValue()\n}\n\nconst baseOperation: Record<string, (value: string, index: number) => void> = {\n Backspace: (_value: string, index: number) => {\n if (index < valueArray.value.length) {\n valueArray.value.splice(index, 1)\n } else if (index - 1 >= 0) {\n valueArray.value.splice(index - 1, 1)\n }\n if (index !== 0) {\n focus(index - 1)\n }\n emitValue()\n },\n ArrowLeft: (_value: string, index: number) => {\n if (index === 0) return\n focus(index - 1)\n },\n ArrowRight: (_value: string, index: number) => {\n if (index === valueArray.value.length) return\n focus(index + 1)\n }\n}\n\nconst onKeydown = (e: KeyboardEvent, value: string, index: number) => {\n if (numberReg.value.test(e.key)) {\n changeValue(e.key, index)\n return\n }\n baseOperation[e.key]?.(value, index)\n}\n\nconst clear = () => {\n valueArray.value = []\n position.value = -1\n}\n\ndefineExpose<GridInputExposed>({ clear })\n</script>\n"],"mappings":"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"}
@@ -9,7 +9,7 @@ declare const __VLS_export: import("vue").DefineComponent<GridInputProps, import
9
9
  }>, {
10
10
  length: number;
11
11
  separator: string;
12
- zero: false;
12
+ zero: boolean;
13
13
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
14
  declare const _default: typeof __VLS_export;
15
15
  //#endregion
@@ -15,20 +15,20 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, _Inpu
15
15
  "update:modelValue": (value: string | undefined) => any;
16
16
  } & {
17
17
  "update:modelValue": (value: string) => any;
18
- clear: () => any;
19
18
  change: (value: string) => any;
20
19
  "suffix:click": (value?: string | undefined) => any;
21
20
  "prefix:click": (value?: string | undefined) => any;
22
21
  focus: (value?: string | undefined) => any;
22
+ clear: () => any;
23
23
  blur: (value?: string | undefined) => any;
24
24
  "native:input": (ev: Event) => any;
25
25
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
26
26
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
27
- onClear?: (() => any) | undefined;
28
27
  onChange?: ((value: string) => any) | undefined;
29
28
  "onSuffix:click"?: ((value?: string | undefined) => any) | undefined;
30
29
  "onPrefix:click"?: ((value?: string | undefined) => any) | undefined;
31
30
  onFocus?: ((value?: string | undefined) => any) | undefined;
31
+ onClear?: (() => any) | undefined;
32
32
  onBlur?: ((value?: string | undefined) => any) | undefined;
33
33
  "onNative:input"?: ((ev: Event) => any) | undefined;
34
34
  }>, {
@@ -14,12 +14,12 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
14
14
  "update:modelValue": (value: (string | number)[]) => any;
15
15
  } & {
16
16
  "update:modelValue": (value: any[]) => any;
17
- clear: () => any;
18
17
  change: (checked: Record<string, any>[]) => any;
18
+ clear: () => any;
19
19
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
20
20
  "onUpdate:modelValue"?: ((value: any[]) => any) | undefined;
21
- onClear?: (() => any) | undefined;
22
21
  onChange?: ((checked: Record<string, any>[]) => any) | undefined;
22
+ onClear?: (() => any) | undefined;
23
23
  }>, {
24
24
  expandAll: boolean;
25
25
  labelKey: string;
@@ -1,8 +1,8 @@
1
1
  import { NumberProps } from "../../types/number.js";
2
2
  //#region src/components/number/number.vue.d.ts
3
3
  declare const __VLS_export: import("vue").DefineComponent<NumberProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NumberProps> & Readonly<{}>, {
4
- format: "currency" | "percent" | "decimal";
5
4
  duration: number;
5
+ format: "currency" | "percent" | "decimal";
6
6
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
7
  declare const _default: typeof __VLS_export;
8
8
  //#endregion
@@ -13,12 +13,12 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
13
13
  "update:modelValue": (value: number | undefined) => any;
14
14
  } & {
15
15
  "update:modelValue": (value?: number | undefined) => any;
16
- clear: () => any;
17
16
  change: (value?: number | undefined) => any;
17
+ clear: () => any;
18
18
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
19
19
  "onUpdate:modelValue"?: ((value?: number | undefined) => any) | undefined;
20
- onClear?: (() => any) | undefined;
21
20
  onChange?: ((value?: number | undefined) => any) | undefined;
21
+ onClear?: (() => any) | undefined;
22
22
  }>, {
23
23
  disabled: boolean;
24
24
  readonly: boolean;
@@ -87,10 +87,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
87
87
  const endRef = useModel(__props, "end");
88
88
  let syncGuard = false;
89
89
  function normalizeFromSplit(s, e) {
90
- let nextS = s;
91
- let nextE = e;
92
- if (nextS !== void 0 && nextE !== void 0 && nextS > nextE) nextE = nextS;
93
- return [nextS, nextE];
90
+ if (s !== void 0 && e !== void 0 && s > e) return [s, s];
91
+ return [s, e];
94
92
  }
95
93
  watch(model, (t) => {
96
94
  if (syncGuard) return;
@@ -145,10 +143,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
145
143
  return model.value?.[0];
146
144
  },
147
145
  set(v) {
148
- const end = (model.value ?? [void 0, void 0])[1];
149
- let nextEnd = end;
150
- if (v !== void 0 && end !== void 0 && v > end) nextEnd = v;
151
- model.value = [v, nextEnd];
146
+ model.value = [v, (model.value ?? [void 0, void 0])[1]];
152
147
  }
153
148
  });
154
149
  const endModel = computed({
@@ -156,10 +151,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
156
151
  return model.value?.[1];
157
152
  },
158
153
  set(v) {
159
- const start = (model.value ?? [void 0, void 0])[0];
160
- let nextStart = start;
161
- if (v !== void 0 && start !== void 0 && v < start) nextStart = v;
162
- model.value = [nextStart, v];
154
+ model.value = [(model.value ?? [void 0, void 0])[0], v];
163
155
  }
164
156
  });
165
157
  function formatNumberPart(num) {
@@ -182,8 +174,23 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
182
174
  const sep = props.separator ?? "~";
183
175
  return `${p}${s === void 0 ? "—" : formatNumberPart(s)} ${sep} ${e === void 0 ? "—" : formatNumberPart(e)}${sfx}`;
184
176
  });
185
- function emitChange() {
186
- emit("change", model.value ?? [void 0, void 0]);
177
+ function handleStartChange() {
178
+ const [start, end] = model.value ?? [void 0, void 0];
179
+ let next = [start, end];
180
+ if (start !== void 0 && end !== void 0 && start > end) {
181
+ next = [end, end];
182
+ model.value = next;
183
+ }
184
+ emit("change", next);
185
+ }
186
+ function handleEndChange() {
187
+ const [start, end] = model.value ?? [void 0, void 0];
188
+ let next = [start, end];
189
+ if (start !== void 0 && end !== void 0 && end < start) {
190
+ next = [start, start];
191
+ model.value = next;
192
+ }
193
+ emit("change", next);
187
194
  }
188
195
  return (_ctx, _cache) => {
189
196
  return !unref(readonly) ? (openBlock(), createElementBlock("div", mergeProps({ key: 0 }, _ctx.$attrs, { class: className.value }), [
@@ -194,7 +201,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
194
201
  disabled: unref(disabled),
195
202
  readonly: unref(readonly),
196
203
  size: unref(size)
197
- }, numberInputBind.value, { onChange: emitChange }), null, 16, [
204
+ }, numberInputBind.value, { onChange: handleStartChange }), null, 16, [
198
205
  "modelValue",
199
206
  "placeholder",
200
207
  "disabled",
@@ -209,7 +216,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
209
216
  disabled: unref(disabled),
210
217
  readonly: unref(readonly),
211
218
  size: unref(size)
212
- }, numberInputBind.value, { onChange: emitChange }), null, 16, [
219
+ }, numberInputBind.value, { onChange: handleEndChange }), null, 16, [
213
220
  "modelValue",
214
221
  "placeholder",
215
222
  "disabled",
@@ -1 +1 @@
1
- {"version":3,"file":"number-range-input.js","names":["$attrs"],"sources":["../../../src/components/number-range-input/number-range-input.vue"],"sourcesContent":["<template>\n <div v-if=\"!readonly\" v-bind=\"$attrs\" :class=\"className\">\n <u-number-input\n v-model=\"startModel\"\n :placeholder=\"startPlaceholder\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :size=\"size\"\n v-bind=\"numberInputBind\"\n @change=\"emitChange\"\n />\n <span :class=\"cls.e('separator')\">{{ separator }}</span>\n <u-number-input\n v-model=\"endModel\"\n :placeholder=\"endPlaceholder\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :size=\"size\"\n v-bind=\"numberInputBind\"\n @change=\"emitChange\"\n />\n </div>\n\n <template v-else>\n {{ readonlyText || FORM_EMPTY_CONTENT }}\n </template>\n</template>\n\n<script lang=\"ts\" setup>\nimport { n, $n, o } from '@cat-kit/core'\nimport { useFormFallbackProps } from '@veltra/compositions'\nimport { bem, FORM_EMPTY_CONTENT } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { computed, getCurrentInstance, nextTick, onMounted, watch } from 'vue'\n\nimport type { NumberRangeInputEmits, NumberRangeInputProps, NumberRangeTuple } from '../../types'\nimport { UNumberInput } from '../number-input'\n\ndefineOptions({ name: 'UNumberRangeInput', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<NumberRangeInputProps>(), {\n startPlaceholder: '请输入',\n endPlaceholder: '请输入',\n separator: '~',\n clearable: true,\n disabled: undefined,\n readonly: undefined\n})\n\nconst emit = defineEmits<NumberRangeInputEmits>()\n\nconst inst = getCurrentInstance()\n\nfunction splitBound(): boolean {\n const p = inst?.vnode.props\n if (!p) return false\n return 'start' in p || 'onUpdate:start' in p || 'end' in p || 'onUpdate:end' in p\n}\n\nconst { formProps } = injectFormContext()\n\nconst { size, disabled, readonly } = useFormFallbackProps([formProps ?? {}, props], {\n size: 'default',\n disabled: false,\n readonly: false\n})\n\nconst cls = bem('number-range-input')\n\nconst className = computed(() => {\n return [cls.b, cls.m(size.value)]\n})\n\nconst model = defineModel<NumberRangeTuple>({ default: () => [undefined, undefined] })\n\nconst startRef = defineModel<number | undefined>('start')\nconst endRef = defineModel<number | undefined>('end')\n\nlet syncGuard = false\n\nfunction normalizeFromSplit(s: number | undefined, e: number | undefined): NumberRangeTuple {\n let nextS = s\n let nextE = e\n if (nextS !== undefined && nextE !== undefined && nextS > nextE) nextE = nextS\n return [nextS, nextE]\n}\n\nwatch(\n model,\n (t) => {\n if (syncGuard) return\n syncGuard = true\n const tuple = t ?? [undefined, undefined]\n if (startRef.value !== tuple[0]) startRef.value = tuple[0]\n if (endRef.value !== tuple[1]) endRef.value = tuple[1]\n nextTick(() => {\n syncGuard = false\n })\n },\n { deep: true }\n)\n\nwatch(\n [startRef, endRef],\n () => {\n if (!splitBound() || syncGuard) return\n syncGuard = true\n const normalized = normalizeFromSplit(startRef.value, endRef.value)\n const cur = model.value ?? [undefined, undefined]\n if (cur[0] !== normalized[0] || cur[1] !== normalized[1]) {\n model.value = normalized\n }\n nextTick(() => {\n syncGuard = false\n })\n },\n { deep: true }\n)\n\nonMounted(() => {\n if (!splitBound()) return\n const cur = model.value ?? [undefined, undefined]\n const hasModel = cur[0] !== undefined || cur[1] !== undefined\n if (hasModel) return\n const normalized = normalizeFromSplit(startRef.value, endRef.value)\n if (cur[0] !== normalized[0] || cur[1] !== normalized[1]) {\n syncGuard = true\n model.value = normalized\n nextTick(() => {\n syncGuard = false\n })\n }\n})\n\nconst numberInputBind = computed(() => {\n return o(props).pick([\n 'clearable',\n 'prefix',\n 'suffix',\n 'step',\n 'min',\n 'max',\n 'currency',\n 'precision',\n 'minPrecision',\n 'maxPrecision',\n 'multiple'\n ])\n})\n\nconst startModel = computed({\n get(): number | undefined {\n return model.value?.[0]\n },\n set(v: number | undefined) {\n const cur = model.value ?? [undefined, undefined]\n const end = cur[1]\n let nextEnd = end\n if (v !== undefined && end !== undefined && v > end) nextEnd = v\n model.value = [v, nextEnd]\n }\n})\n\nconst endModel = computed({\n get(): number | undefined {\n return model.value?.[1]\n },\n set(v: number | undefined) {\n const cur = model.value ?? [undefined, undefined]\n const start = cur[0]\n let nextStart = start\n if (v !== undefined && start !== undefined && v < start) nextStart = v\n model.value = [nextStart, v]\n }\n})\n\nfunction formatNumberPart(num: number): string {\n const { currency, precision, minPrecision, maxPrecision, multiple } = props\n\n const displayValue = multiple ? $n.div(num, multiple) : num\n\n return currency\n ? n(displayValue).currency('CNY', { precision, minPrecision, maxPrecision })\n : n(displayValue).fixed(precision ?? { minPrecision, maxPrecision })\n}\n\nconst readonlyText = computed(() => {\n const [s, e] = model.value ?? [undefined, undefined]\n if (s === undefined && e === undefined) return ''\n const p = props.prefix ?? ''\n const sfx = props.suffix ?? ''\n const sep = props.separator ?? '~'\n const a = s === undefined ? '—' : formatNumberPart(s)\n const b = e === undefined ? '—' : formatNumberPart(e)\n return `${p}${a} ${sep} ${b}${sfx}`\n})\n\nfunction emitChange(): void {\n emit('change', model.value ?? [undefined, undefined])\n}\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"number-range-input.js","names":["$attrs"],"sources":["../../../src/components/number-range-input/number-range-input.vue"],"sourcesContent":["<template>\n <div v-if=\"!readonly\" v-bind=\"$attrs\" :class=\"className\">\n <u-number-input\n v-model=\"startModel\"\n :placeholder=\"startPlaceholder\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :size=\"size\"\n v-bind=\"numberInputBind\"\n @change=\"handleStartChange\"\n />\n <span :class=\"cls.e('separator')\">{{ separator }}</span>\n <u-number-input\n v-model=\"endModel\"\n :placeholder=\"endPlaceholder\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :size=\"size\"\n v-bind=\"numberInputBind\"\n @change=\"handleEndChange\"\n />\n </div>\n\n <template v-else>\n {{ readonlyText || FORM_EMPTY_CONTENT }}\n </template>\n</template>\n\n<script lang=\"ts\" setup>\nimport { n, $n, o } from '@cat-kit/core'\nimport { useFormFallbackProps } from '@veltra/compositions'\nimport { bem, FORM_EMPTY_CONTENT } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { computed, getCurrentInstance, nextTick, onMounted, watch } from 'vue'\n\nimport type { NumberRangeInputEmits, NumberRangeInputProps, NumberRangeTuple } from '../../types'\nimport { UNumberInput } from '../number-input'\n\ndefineOptions({ name: 'UNumberRangeInput', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<NumberRangeInputProps>(), {\n startPlaceholder: '请输入',\n endPlaceholder: '请输入',\n separator: '~',\n clearable: true,\n disabled: undefined,\n readonly: undefined\n})\n\nconst emit = defineEmits<NumberRangeInputEmits>()\n\nconst inst = getCurrentInstance()\n\nfunction splitBound(): boolean {\n const p = inst?.vnode.props\n if (!p) return false\n return 'start' in p || 'onUpdate:start' in p || 'end' in p || 'onUpdate:end' in p\n}\n\nconst { formProps } = injectFormContext()\n\nconst { size, disabled, readonly } = useFormFallbackProps([formProps ?? {}, props], {\n size: 'default',\n disabled: false,\n readonly: false\n})\n\nconst cls = bem('number-range-input')\n\nconst className = computed(() => {\n return [cls.b, cls.m(size.value)]\n})\n\nconst model = defineModel<NumberRangeTuple>({ default: () => [undefined, undefined] })\n\nconst startRef = defineModel<number | undefined>('start')\nconst endRef = defineModel<number | undefined>('end')\n\nlet syncGuard = false\n\nfunction normalizeFromSplit(s: number | undefined, e: number | undefined): NumberRangeTuple {\n // 外部 split 写入无法区分哪一侧后改,统一将越界的 end 抬到 start\n if (s !== undefined && e !== undefined && s > e) return [s, s]\n return [s, e]\n}\n\nwatch(\n model,\n (t) => {\n if (syncGuard) return\n syncGuard = true\n const tuple = t ?? [undefined, undefined]\n if (startRef.value !== tuple[0]) startRef.value = tuple[0]\n if (endRef.value !== tuple[1]) endRef.value = tuple[1]\n nextTick(() => {\n syncGuard = false\n })\n },\n { deep: true }\n)\n\nwatch(\n [startRef, endRef],\n () => {\n if (!splitBound() || syncGuard) return\n syncGuard = true\n const normalized = normalizeFromSplit(startRef.value, endRef.value)\n const cur = model.value ?? [undefined, undefined]\n if (cur[0] !== normalized[0] || cur[1] !== normalized[1]) {\n model.value = normalized\n }\n nextTick(() => {\n syncGuard = false\n })\n },\n { deep: true }\n)\n\nonMounted(() => {\n if (!splitBound()) return\n const cur = model.value ?? [undefined, undefined]\n const hasModel = cur[0] !== undefined || cur[1] !== undefined\n if (hasModel) return\n const normalized = normalizeFromSplit(startRef.value, endRef.value)\n if (cur[0] !== normalized[0] || cur[1] !== normalized[1]) {\n syncGuard = true\n model.value = normalized\n nextTick(() => {\n syncGuard = false\n })\n }\n})\n\nconst numberInputBind = computed(() => {\n return o(props).pick([\n 'clearable',\n 'prefix',\n 'suffix',\n 'step',\n 'min',\n 'max',\n 'currency',\n 'precision',\n 'minPrecision',\n 'maxPrecision',\n 'multiple'\n ])\n})\n\nconst startModel = computed({\n get(): number | undefined {\n return model.value?.[0]\n },\n set(v: number | undefined) {\n // 输入过程只更新单侧,交叉约束放到 change(失焦)时处理\n const cur = model.value ?? [undefined, undefined]\n model.value = [v, cur[1]]\n }\n})\n\nconst endModel = computed({\n get(): number | undefined {\n return model.value?.[1]\n },\n set(v: number | undefined) {\n const cur = model.value ?? [undefined, undefined]\n model.value = [cur[0], v]\n }\n})\n\nfunction formatNumberPart(num: number): string {\n const { currency, precision, minPrecision, maxPrecision, multiple } = props\n\n const displayValue = multiple ? $n.div(num, multiple) : num\n\n return currency\n ? n(displayValue).currency('CNY', { precision, minPrecision, maxPrecision })\n : n(displayValue).fixed(precision ?? { minPrecision, maxPrecision })\n}\n\nconst readonlyText = computed(() => {\n const [s, e] = model.value ?? [undefined, undefined]\n if (s === undefined && e === undefined) return ''\n const p = props.prefix ?? ''\n const sfx = props.suffix ?? ''\n const sep = props.separator ?? '~'\n const a = s === undefined ? '—' : formatNumberPart(s)\n const b = e === undefined ? '—' : formatNumberPart(e)\n return `${p}${a} ${sep} ${b}${sfx}`\n})\n\nfunction handleStartChange(): void {\n const [start, end] = model.value ?? [undefined, undefined]\n // 失焦时只校正刚改的一侧:start > end 时把 start 压回 end\n let next: NumberRangeTuple = [start, end]\n if (start !== undefined && end !== undefined && start > end) {\n next = [end, end]\n model.value = next\n }\n emit('change', next)\n}\n\nfunction handleEndChange(): void {\n const [start, end] = model.value ?? [undefined, undefined]\n // 失焦时只校正刚改的一侧:end < start 时把 end 抬回 start\n let next: NumberRangeTuple = [start, end]\n if (start !== undefined && end !== undefined && end < start) {\n next = [start, start]\n model.value = next\n }\n emit('change', next)\n}\n</script>\n"],"mappings":"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"}
@@ -9,15 +9,15 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
9
9
  "update:modelValue": (value: string | undefined) => any;
10
10
  } & {
11
11
  "update:modelValue": (value: string) => any;
12
- clear: () => any;
13
12
  change: (value: string) => any;
14
13
  focus: () => any;
14
+ clear: () => any;
15
15
  blur: () => any;
16
16
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
17
17
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
18
- onClear?: (() => any) | undefined;
19
18
  onChange?: ((value: string) => any) | undefined;
20
19
  onFocus?: (() => any) | undefined;
20
+ onClear?: (() => any) | undefined;
21
21
  onBlur?: (() => any) | undefined;
22
22
  }>, {
23
23
  disabled: boolean;
@@ -13,8 +13,8 @@ declare const __VLS_base: import("vue").DefineComponent<TipProps, {}, {}, {}, {}
13
13
  contentTag: string;
14
14
  visible: boolean;
15
15
  direction: TipDirection;
16
- content: string;
17
16
  alignment: TipAlign;
17
+ content: string;
18
18
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
19
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
20
20
  declare const _default: typeof __VLS_export;
@@ -15,13 +15,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
15
15
  "update:modelValue": (value: string | number | undefined) => any;
16
16
  } & {
17
17
  "update:modelValue": (value?: string | number | undefined) => any;
18
- clear: () => any;
19
18
  change: (selectedData?: Record<string, any> | undefined) => any;
19
+ clear: () => any;
20
20
  "update:text": (text?: string | undefined) => any;
21
21
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
22
22
  "onUpdate:modelValue"?: ((value?: string | number | undefined) => any) | undefined;
23
- onClear?: (() => any) | undefined;
24
23
  onChange?: ((selectedData?: Record<string, any> | undefined) => any) | undefined;
24
+ onClear?: (() => any) | undefined;
25
25
  "onUpdate:text"?: ((text?: string | undefined) => any) | undefined;
26
26
  }>, {
27
27
  expandAll: boolean;
@@ -5,8 +5,8 @@ type __VLS_Slots = {} & {
5
5
  default?: (props: typeof __VLS_7) => any;
6
6
  };
7
7
  declare const __VLS_base: import("vue").DefineComponent<WatermarkProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<WatermarkProps> & Readonly<{}>, {
8
- route: number;
9
8
  fontSize: number;
9
+ route: number;
10
10
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
11
11
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
12
12
  declare const _default: typeof __VLS_export;
@@ -1,23 +1,30 @@
1
1
  import { DeconstructValue } from "@veltra/utils";
2
2
 
3
3
  //#region src/types/grid-input.d.ts
4
- /** 密码输入框组件组件属性 */
4
+ /** 网格输入框组件属性 */
5
5
  interface GridInputProps {
6
6
  modelValue?: string;
7
+ /** 格子数量 */
7
8
  length?: number;
8
- zero?: false;
9
+ /**
10
+ * 是否允许输入 0
11
+ * @description 验证码场景通常开启;组织编码结构等场景关闭(如 3-3-2)
12
+ * @default false
13
+ */
14
+ zero?: boolean;
15
+ /** 格子之间的分隔符 */
9
16
  separator?: string;
10
17
  }
11
- /** 密码输入框组件组件定义的事件 */
18
+ /** 网格输入框组件定义的事件 */
12
19
  interface GridInputEmits {
13
20
  (e: 'update:modelValue', value: string): void;
14
21
  (e: 'input', value: string): void;
15
22
  }
16
- /** 密码输入框组件组件暴露的属性和方法(组件内部使用) */
23
+ /** 网格输入框组件暴露的属性和方法(组件内部使用) */
17
24
  interface _GridInputExposed {
18
25
  clear: () => void;
19
26
  }
20
- /** 密码输入框组件组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
27
+ /** 网格输入框组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
21
28
  type GridInputExposed = DeconstructValue<_GridInputExposed>;
22
29
  //#endregion
23
30
  export { GridInputEmits, GridInputExposed, GridInputProps, _GridInputExposed };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltra/desktop",
3
- "version": "1.2.26",
3
+ "version": "1.2.28",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -57,11 +57,11 @@
57
57
  "@cat-kit/core": ">=1.1.8",
58
58
  "@cat-kit/excel": ">=1.0.0",
59
59
  "@cat-kit/fe": "^1.1.8",
60
- "@veltra/compositions": "1.2.26",
61
- "@veltra/directives": "1.2.26",
62
- "@veltra/icons": "1.2.26",
63
- "@veltra/styles": "1.2.26",
64
- "@veltra/utils": "1.2.26",
60
+ "@veltra/compositions": "1.2.28",
61
+ "@veltra/directives": "1.2.28",
62
+ "@veltra/icons": "1.2.28",
63
+ "@veltra/styles": "1.2.28",
64
+ "@veltra/utils": "1.2.28",
65
65
  "vue": ">=3.5.39"
66
66
  },
67
67
  "devDependencies": {
@@ -54,10 +54,16 @@
54
54
  </span>
55
55
 
56
56
  <div :class="cls.e('form-actions')">
57
- <u-button text :loading="state.loading" @click="handleClose"> 取消 </u-button>
58
- <u-button v-if="showSaveBtn" type="primary" :loading="state.loading" @click="handleSave">
59
- 保存
60
- </u-button>
57
+ <u-button size="small" text :loading="state.loading" @click="handleClose"> 取消 </u-button>
58
+ <u-button
59
+ v-if="showSaveBtn"
60
+ type="primary"
61
+ size="small"
62
+ title="保存"
63
+ :icon="Save"
64
+ :loading="state.loading"
65
+ @click="handleSave"
66
+ />
61
67
  </div>
62
68
  </footer>
63
69
  </aside>
@@ -65,7 +71,7 @@
65
71
 
66
72
  <script lang="ts" setup>
67
73
  import { o } from '@cat-kit/core'
68
- import { Close } from '@veltra/icons/normal'
74
+ import { Close, Save } from '@veltra/icons/normal'
69
75
  import { bem } from '@veltra/utils'
70
76
  import { computed, inject, shallowRef, toRef } from 'vue'
71
77
 
@@ -22,6 +22,9 @@ export function useEditState(options: Options) {
22
22
  const state = shallowReactive({ ...defaultState })
23
23
 
24
24
  function resetState() {
25
+ // 销毁表单前先重置,避免下次 v-if 重挂载时 Form 拍到脏 model
26
+ formRef.value?.reset()
27
+
25
28
  if (state.row) {
26
29
  state.row.isCurrent = false
27
30
  }
@@ -163,6 +163,11 @@ export function useHandlers(options: Options): EditReturned {
163
163
  }
164
164
  }
165
165
  insert(item)
166
+
167
+ // 推进插入点,便于连续新增;并重置表单到初始默认值
168
+ const next = last(state.indexPath) + 1
169
+ state.indexPath = [...state.indexPath.slice(0, -1), next]
170
+ formRef.value?.reset()
166
171
  return
167
172
  }
168
173
 
@@ -24,7 +24,9 @@ export function useShortcutKey(options: Options) {
24
24
  }
25
25
 
26
26
  function runEscape(e: KeyboardEvent) {
27
+ if (!state.formVisible) return
27
28
  e.preventDefault()
29
+ e.stopPropagation()
28
30
  onClose()
29
31
  }
30
32
 
@@ -10,7 +10,7 @@
10
10
  ref="items"
11
11
  :class="[cls.e('item'), bem.is('focus', index === position && !blur)]"
12
12
  >
13
- <span :class="cls.em('item', 'text')" v-if="valueArray[index]">
13
+ <span :class="cls.em('item', 'text')" v-if="index < valueArray.length">
14
14
  {{ valueArray[index] }}
15
15
  </span>
16
16
 
@@ -40,7 +40,7 @@ const { size } = useFallbackProps([props], { size: 'default' })
40
40
 
41
41
  const cls = bem('grid-input')
42
42
 
43
- const _numberReg = computed(() => (props.zero ? /^[0-9]$/ : /^[1-9]$/))
43
+ const numberReg = computed(() => (props.zero ? /^[0-9]$/ : /^[1-9]$/))
44
44
 
45
45
  const valueArray = ref<string[]>([])
46
46
 
@@ -50,6 +50,12 @@ const blur = ref<boolean>(false)
50
50
 
51
51
  const instance = getCurrentInstance()
52
52
 
53
+ const emitValue = () => {
54
+ const value = valueArray.value.join(props.separator)
55
+ emit('input', value)
56
+ emit('update:modelValue', value)
57
+ }
58
+
53
59
  const focus = async (index?: number) => {
54
60
  if (index === undefined) {
55
61
  focus(position.value)
@@ -67,9 +73,8 @@ const focus = async (index?: number) => {
67
73
 
68
74
  const changeValue = (value: string, index: number) => {
69
75
  if (position.value === props.length) return
70
- const currentValue = valueArray.value[index]
71
76
 
72
- if (currentValue) {
77
+ if (index < valueArray.value.length) {
73
78
  valueArray.value.splice(index, 1, value)
74
79
  } else if (index === valueArray.value.length) {
75
80
  valueArray.value.push(value)
@@ -79,12 +84,12 @@ const changeValue = (value: string, index: number) => {
79
84
  position.value++
80
85
  focus()
81
86
 
82
- emit('input', valueArray.value.filter((v) => v).join(props.separator))
87
+ emitValue()
83
88
  }
84
89
 
85
- const baseOperation = {
86
- Backspace: (value: string, index: number) => {
87
- if (value) {
90
+ const baseOperation: Record<string, (value: string, index: number) => void> = {
91
+ Backspace: (_value: string, index: number) => {
92
+ if (index < valueArray.value.length) {
88
93
  valueArray.value.splice(index, 1)
89
94
  } else if (index - 1 >= 0) {
90
95
  valueArray.value.splice(index - 1, 1)
@@ -92,30 +97,29 @@ const baseOperation = {
92
97
  if (index !== 0) {
93
98
  focus(index - 1)
94
99
  }
95
- emit('input', valueArray.value.filter((v) => v).join(props.separator))
100
+ emitValue()
96
101
  },
97
- ArrowLeft: (value: string, index: number) => {
102
+ ArrowLeft: (_value: string, index: number) => {
98
103
  if (index === 0) return
99
104
  focus(index - 1)
100
105
  },
101
- ArrowRight: (value: string, index: number) => {
106
+ ArrowRight: (_value: string, index: number) => {
102
107
  if (index === valueArray.value.length) return
103
108
  focus(index + 1)
104
109
  }
105
110
  }
106
111
 
107
- const onKeydown = (e: any, value: string, index: number) => {
108
- if (_numberReg.value.test(e.key)) {
112
+ const onKeydown = (e: KeyboardEvent, value: string, index: number) => {
113
+ if (numberReg.value.test(e.key)) {
109
114
  changeValue(e.key, index)
110
115
  return
111
116
  }
112
- baseOperation[e.key](value, index)
117
+ baseOperation[e.key]?.(value, index)
113
118
  }
114
119
 
115
120
  const clear = () => {
116
- while (valueArray.value.length) {
117
- valueArray.value.pop()
118
- }
121
+ valueArray.value = []
122
+ position.value = -1
119
123
  }
120
124
 
121
125
  defineExpose<GridInputExposed>({ clear })
@@ -0,0 +1,100 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { createApp, h, nextTick, ref } from 'vue'
3
+
4
+ import type { NumberRangeTuple } from '../../../types'
5
+ import UNumberRangeInput from '../number-range-input.vue'
6
+
7
+ function mountNumberRangeInput(initial: NumberRangeTuple) {
8
+ const host = document.createElement('div')
9
+ document.body.appendChild(host)
10
+
11
+ const model = ref<NumberRangeTuple>([...initial])
12
+ const changes: NumberRangeTuple[] = []
13
+
14
+ const app = createApp({
15
+ render() {
16
+ return h(UNumberRangeInput, {
17
+ modelValue: model.value,
18
+ 'onUpdate:modelValue': (value: NumberRangeTuple) => {
19
+ model.value = value
20
+ },
21
+ onChange: (value: NumberRangeTuple) => {
22
+ changes.push(value)
23
+ }
24
+ })
25
+ }
26
+ })
27
+
28
+ app.mount(host)
29
+
30
+ const inputs = () => host.querySelectorAll<HTMLInputElement>('input')
31
+
32
+ return {
33
+ host,
34
+ model,
35
+ changes,
36
+ inputs,
37
+ async setInputValue(index: 0 | 1, value: string) {
38
+ const input = inputs()[index]
39
+ if (!input) throw new Error(`input[${index}] not found`)
40
+ input.value = value
41
+ input.dispatchEvent(new Event('input', { bubbles: true }))
42
+ await nextTick()
43
+ },
44
+ async blurInput(index: 0 | 1) {
45
+ const input = inputs()[index]
46
+ if (!input) throw new Error(`input[${index}] not found`)
47
+ input.dispatchEvent(new Event('change', { bubbles: true }))
48
+ input.dispatchEvent(new FocusEvent('blur', { bubbles: true }))
49
+ await nextTick()
50
+ },
51
+ unmount() {
52
+ app.unmount()
53
+ host.remove()
54
+ }
55
+ }
56
+ }
57
+
58
+ describe('NumberRangeInput', () => {
59
+ it('does not rewrite the other side while typing an intermediate value', async () => {
60
+ const { model, setInputValue, unmount } = mountNumberRangeInput([50, 80])
61
+
62
+ try {
63
+ // 结束框键入 2(意图输入 20),输入过程中 start 不应被改写
64
+ await setInputValue(1, '2')
65
+ expect(model.value).toEqual([50, 2])
66
+ } finally {
67
+ unmount()
68
+ }
69
+ })
70
+
71
+ it('clamps the edited end back to start on change when end < start', async () => {
72
+ const { model, changes, setInputValue, blurInput, unmount } = mountNumberRangeInput([80, 100])
73
+
74
+ try {
75
+ await setInputValue(1, '10')
76
+ expect(model.value).toEqual([80, 10])
77
+
78
+ await blurInput(1)
79
+ expect(model.value).toEqual([80, 80])
80
+ expect(changes.at(-1)).toEqual([80, 80])
81
+ } finally {
82
+ unmount()
83
+ }
84
+ })
85
+
86
+ it('clamps the edited start back to end on change when start > end', async () => {
87
+ const { model, changes, setInputValue, blurInput, unmount } = mountNumberRangeInput([50, 80])
88
+
89
+ try {
90
+ await setInputValue(0, '90')
91
+ expect(model.value).toEqual([90, 80])
92
+
93
+ await blurInput(0)
94
+ expect(model.value).toEqual([80, 80])
95
+ expect(changes.at(-1)).toEqual([80, 80])
96
+ } finally {
97
+ unmount()
98
+ }
99
+ })
100
+ })
@@ -7,7 +7,7 @@
7
7
  :readonly="readonly"
8
8
  :size="size"
9
9
  v-bind="numberInputBind"
10
- @change="emitChange"
10
+ @change="handleStartChange"
11
11
  />
12
12
  <span :class="cls.e('separator')">{{ separator }}</span>
13
13
  <u-number-input
@@ -17,7 +17,7 @@
17
17
  :readonly="readonly"
18
18
  :size="size"
19
19
  v-bind="numberInputBind"
20
- @change="emitChange"
20
+ @change="handleEndChange"
21
21
  />
22
22
  </div>
23
23
 
@@ -79,10 +79,9 @@ const endRef = defineModel<number | undefined>('end')
79
79
  let syncGuard = false
80
80
 
81
81
  function normalizeFromSplit(s: number | undefined, e: number | undefined): NumberRangeTuple {
82
- let nextS = s
83
- let nextE = e
84
- if (nextS !== undefined && nextE !== undefined && nextS > nextE) nextE = nextS
85
- return [nextS, nextE]
82
+ // 外部 split 写入无法区分哪一侧后改,统一将越界的 end 抬到 start
83
+ if (s !== undefined && e !== undefined && s > e) return [s, s]
84
+ return [s, e]
86
85
  }
87
86
 
88
87
  watch(
@@ -153,11 +152,9 @@ const startModel = computed({
153
152
  return model.value?.[0]
154
153
  },
155
154
  set(v: number | undefined) {
155
+ // 输入过程只更新单侧,交叉约束放到 change(失焦)时处理
156
156
  const cur = model.value ?? [undefined, undefined]
157
- const end = cur[1]
158
- let nextEnd = end
159
- if (v !== undefined && end !== undefined && v > end) nextEnd = v
160
- model.value = [v, nextEnd]
157
+ model.value = [v, cur[1]]
161
158
  }
162
159
  })
163
160
 
@@ -167,10 +164,7 @@ const endModel = computed({
167
164
  },
168
165
  set(v: number | undefined) {
169
166
  const cur = model.value ?? [undefined, undefined]
170
- const start = cur[0]
171
- let nextStart = start
172
- if (v !== undefined && start !== undefined && v < start) nextStart = v
173
- model.value = [nextStart, v]
167
+ model.value = [cur[0], v]
174
168
  }
175
169
  })
176
170
 
@@ -195,7 +189,25 @@ const readonlyText = computed(() => {
195
189
  return `${p}${a} ${sep} ${b}${sfx}`
196
190
  })
197
191
 
198
- function emitChange(): void {
199
- emit('change', model.value ?? [undefined, undefined])
192
+ function handleStartChange(): void {
193
+ const [start, end] = model.value ?? [undefined, undefined]
194
+ // 失焦时只校正刚改的一侧:start > end 时把 start 压回 end
195
+ let next: NumberRangeTuple = [start, end]
196
+ if (start !== undefined && end !== undefined && start > end) {
197
+ next = [end, end]
198
+ model.value = next
199
+ }
200
+ emit('change', next)
201
+ }
202
+
203
+ function handleEndChange(): void {
204
+ const [start, end] = model.value ?? [undefined, undefined]
205
+ // 失焦时只校正刚改的一侧:end < start 时把 end 抬回 start
206
+ let next: NumberRangeTuple = [start, end]
207
+ if (start !== undefined && end !== undefined && end < start) {
208
+ next = [start, start]
209
+ model.value = next
210
+ }
211
+ emit('change', next)
200
212
  }
201
213
  </script>
@@ -1,23 +1,30 @@
1
1
  import type { DeconstructValue } from '@veltra/utils'
2
2
 
3
- /** 密码输入框组件组件属性 */
3
+ /** 网格输入框组件属性 */
4
4
  export interface GridInputProps {
5
5
  modelValue?: string
6
+ /** 格子数量 */
6
7
  length?: number
7
- zero?: false
8
+ /**
9
+ * 是否允许输入 0
10
+ * @description 验证码场景通常开启;组织编码结构等场景关闭(如 3-3-2)
11
+ * @default false
12
+ */
13
+ zero?: boolean
14
+ /** 格子之间的分隔符 */
8
15
  separator?: string
9
16
  }
10
17
 
11
- /** 密码输入框组件组件定义的事件 */
18
+ /** 网格输入框组件定义的事件 */
12
19
  export interface GridInputEmits {
13
20
  (e: 'update:modelValue', value: string): void
14
21
  (e: 'input', value: string): void
15
22
  }
16
23
 
17
- /** 密码输入框组件组件暴露的属性和方法(组件内部使用) */
24
+ /** 网格输入框组件暴露的属性和方法(组件内部使用) */
18
25
  export interface _GridInputExposed {
19
26
  clear: () => void
20
27
  }
21
28
 
22
- /** 密码输入框组件组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
29
+ /** 网格输入框组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
23
30
  export type GridInputExposed = DeconstructValue<_GridInputExposed>