@veltra/desktop 1.2.6 → 1.2.8

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 (29) hide show
  1. package/dist/components/action/action-group.vue.d.ts +3 -3
  2. package/dist/components/action/action.vue.d.ts +2 -2
  3. package/dist/components/batch-edit/batch-edit-form.js +2 -6
  4. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  5. package/dist/components/batch-edit/use-edit.js +17 -1
  6. package/dist/components/batch-edit/use-edit.js.map +1 -1
  7. package/dist/components/checkbox/checkbox-button.vue.d.ts +1 -1
  8. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  9. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  10. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  11. package/dist/components/dual-nav/dual-nav.js +2 -2
  12. package/dist/components/dual-nav/dual-nav.js.map +1 -1
  13. package/dist/components/dual-nav/helper.js +6 -1
  14. package/dist/components/dual-nav/helper.js.map +1 -1
  15. package/dist/components/dual-nav/style2.css +0 -2
  16. package/dist/components/empty/empty.vue.d.ts +1 -1
  17. package/dist/components/number/number.vue.d.ts +1 -1
  18. package/dist/components/steps/steps.vue.d.ts +2 -2
  19. package/dist/components/tabs/tabs-horizontal.vue.d.ts +2 -2
  20. package/dist/components/tabs/tabs-vertical.vue.d.ts +2 -2
  21. package/dist/components/tabs/tabs.vue.d.ts +2 -2
  22. package/dist/types/batch-edit.d.ts +1 -1
  23. package/package.json +6 -6
  24. package/src/components/batch-edit/batch-edit-form.vue +2 -9
  25. package/src/components/batch-edit/use-edit.ts +25 -3
  26. package/src/components/dual-nav/dual-nav.vue +2 -4
  27. package/src/components/dual-nav/helper.ts +7 -1
  28. package/src/components/dual-nav/style.scss +0 -2
  29. package/src/types/batch-edit.ts +1 -1
@@ -6,11 +6,11 @@ type __VLS_Slots = {
6
6
  default?: () => VNode[];
7
7
  };
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
- text: boolean;
10
- size: "small" | "default" | "large";
11
9
  type: import("@veltra/utils").ColorType;
12
- circle: boolean;
13
10
  max: number;
11
+ size: "small" | "default" | "large";
12
+ text: boolean;
13
+ circle: 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>;
@@ -12,9 +12,9 @@ declare const __VLS_base: import("vue").DefineComponent<ActionProps, {}, {}, {},
12
12
  }, string, import("vue").PublicProps, Readonly<ActionProps> & Readonly<{
13
13
  onRun?: (() => any) | undefined;
14
14
  }>, {
15
- text: boolean;
16
- size: import("@veltra/utils").ComponentSize;
17
15
  type: ButtonType;
16
+ size: import("@veltra/utils").ComponentSize;
17
+ text: boolean;
18
18
  circle: boolean;
19
19
  loading: boolean;
20
20
  inDropdown: boolean;
@@ -31,10 +31,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
31
31
  const isQuick = computed(() => props.mode === "quick");
32
32
  const isNormal = computed(() => props.mode !== "quick");
33
33
  const isQuickUpdate = computed(() => isQuick.value && state.type === "update");
34
- const formModel = computed(() => {
35
- if (isQuick.value && state.type === "update" && state.row) return state.row.data;
36
- return props.model;
37
- });
38
34
  const headerInfo = computed(() => {
39
35
  if (props.readonly) return {
40
36
  icon: View,
@@ -65,7 +61,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
65
61
  return `${state.type}-${state.row?.uid ?? "create"}-${state.parentRow?.uid ?? "root"}`;
66
62
  });
67
63
  return (_ctx, _cache) => {
68
- return unref(state).visible && formModel.value ? (openBlock(), createElementBlock("aside", {
64
+ return unref(state).visible && unref(props).model ? (openBlock(), createElementBlock("aside", {
69
65
  key: 0,
70
66
  class: normalizeClass(unref(cls).e("form"))
71
67
  }, [
@@ -105,7 +101,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
105
101
  default: withCtx(() => [createVNode(unref(form_default), {
106
102
  ref_key: "formComponentRef",
107
103
  ref: formComponentRef,
108
- model: formModel.value,
104
+ model: unref(props).model,
109
105
  readonly: unref(props).readonly,
110
106
  "label-width": unref(props).labelWidth
111
107
  }, {
@@ -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.visible && formModel\">\n <header :class=\"cls.e('form-header')\">\n <span :class=\"[cls.e('form-icon'), bem.is(headerInfo.tone)]\">\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 <span v-if=\"headerInfo.chip\" :class=\"cls.e('form-chip')\">\n {{ headerInfo.chip }}\n </span>\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=\"formModel\"\n :readonly=\"props.readonly\"\n :label-width=\"props.labelWidth\"\n >\n <slot\n v-bind=\"{\n row: state.row,\n depth: state.depth,\n indexes: insertIndexes,\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')\" v-if=\"props.readonly\">只读模式</span>\n <span :class=\"cls.e('form-hint')\" v-else-if=\"isQuickUpdate\">\n 实时编辑\n <span :class=\"{ [bem.is('concealed')]: !focused }\"> · <u-kbd>Esc</u-kbd> 关闭</span>\n </span>\n <span :class=\"cls.e('form-hint')\" v-else>\n <span :class=\"{ [bem.is('concealed')]: !focused }\">\n <u-kbd>Ctrl + S</u-kbd> 保存 · <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\n v-if=\"\n !props.readonly && ((isNormal && (creatable || updatable)) || (isQuick && creatable))\n \"\n :type=\"state.type === 'create' ? 'success' : 'primary'\"\n :loading=\"state.loading\"\n @click=\"handleSave\"\n >\n 保存\n </u-button>\n </div>\n </footer>\n </aside>\n</template>\n\n<script lang=\"ts\" setup>\nimport { AddChild, Close, EditPen, Plus, View } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, shallowRef, toRef, type Component } 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'\n\ndefineOptions({ name: 'BatchEditForm' })\n\nconst batchEditCtx = inject(BatchEditDIKey)!\n\nconst {\n cls,\n props,\n state,\n insertIndexes,\n handleSave,\n handleClose,\n staticFeatures,\n dynamicFeatures\n} = 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\nconst creatable = computed(() => {\n return (\n state.type === 'create' &&\n (staticFeatures.value.has('create') || dynamicFeatures.value.create?.(state.row))\n )\n})\n\nconst updatable = computed(() => {\n return (\n state.type === 'update' &&\n (staticFeatures.value.has('update') || dynamicFeatures.value.update?.(state.row))\n )\n})\n\nconst isQuick = computed(() => props.mode === 'quick')\nconst isNormal = computed(() => props.mode !== 'quick')\nconst isQuickUpdate = computed(() => isQuick.value && state.type === 'update')\n\nconst formModel = computed(() => {\n if (isQuick.value && state.type === 'update' && state.row) {\n return state.row.data\n }\n return props.model\n})\n\ninterface HeaderInfo {\n icon: Component\n title: string\n chip?: string\n tone: 'create' | 'update' | 'view' | 'child'\n}\n\nconst headerInfo = computed<HeaderInfo>(() => {\n if (props.readonly) {\n return {\n icon: View,\n title: '查看详情',\n chip: state.row ? `第 ${state.row.index + 1} 行` : undefined,\n tone: 'view'\n }\n }\n if (state.parentRow) {\n return {\n icon: AddChild,\n title: '新增子级',\n chip: `归属于第 ${state.parentRow.index + 1} 行`,\n tone: 'child'\n }\n }\n if (state.type === 'create') {\n return { icon: Plus, title: '新增', tone: 'create' }\n }\n return {\n icon: EditPen,\n title: '编辑',\n chip: state.row ? `第 ${state.row.index + 1} 行` : undefined,\n tone: 'update'\n }\n})\n\n/** 通过 row 切换 / 类型切换驱动表单内容淡出淡入 */\nconst bodyKey = computed(() => {\n return `${state.type}-${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.visible && props.model\">\n <header :class=\"cls.e('form-header')\">\n <span :class=\"[cls.e('form-icon'), bem.is(headerInfo.tone)]\">\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 <span v-if=\"headerInfo.chip\" :class=\"cls.e('form-chip')\">\n {{ headerInfo.chip }}\n </span>\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 >\n <slot\n v-bind=\"{\n row: state.row,\n depth: state.depth,\n indexes: insertIndexes,\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')\" v-if=\"props.readonly\">只读模式</span>\n <span :class=\"cls.e('form-hint')\" v-else-if=\"isQuickUpdate\">\n 实时编辑\n <span :class=\"{ [bem.is('concealed')]: !focused }\"> · <u-kbd>Esc</u-kbd> 关闭</span>\n </span>\n <span :class=\"cls.e('form-hint')\" v-else>\n <span :class=\"{ [bem.is('concealed')]: !focused }\">\n <u-kbd>Ctrl + S</u-kbd> 保存 · <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\n v-if=\"\n !props.readonly && ((isNormal && (creatable || updatable)) || (isQuick && creatable))\n \"\n :type=\"state.type === 'create' ? 'success' : 'primary'\"\n :loading=\"state.loading\"\n @click=\"handleSave\"\n >\n 保存\n </u-button>\n </div>\n </footer>\n </aside>\n</template>\n\n<script lang=\"ts\" setup>\nimport { AddChild, Close, EditPen, Plus, View } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, shallowRef, toRef, type Component } 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'\n\ndefineOptions({ name: 'BatchEditForm' })\n\nconst batchEditCtx = inject(BatchEditDIKey)!\n\nconst {\n cls,\n props,\n state,\n insertIndexes,\n handleSave,\n handleClose,\n staticFeatures,\n dynamicFeatures\n} = 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\nconst creatable = computed(() => {\n return (\n state.type === 'create' &&\n (staticFeatures.value.has('create') || dynamicFeatures.value.create?.(state.row))\n )\n})\n\nconst updatable = computed(() => {\n return (\n state.type === 'update' &&\n (staticFeatures.value.has('update') || dynamicFeatures.value.update?.(state.row))\n )\n})\n\nconst isQuick = computed(() => props.mode === 'quick')\nconst isNormal = computed(() => props.mode !== 'quick')\nconst isQuickUpdate = computed(() => isQuick.value && state.type === 'update')\n\ninterface HeaderInfo {\n icon: Component\n title: string\n chip?: string\n tone: 'create' | 'update' | 'view' | 'child'\n}\n\nconst headerInfo = computed<HeaderInfo>(() => {\n if (props.readonly) {\n return {\n icon: View,\n title: '查看详情',\n chip: state.row ? `第 ${state.row.index + 1} 行` : undefined,\n tone: 'view'\n }\n }\n if (state.parentRow) {\n return {\n icon: AddChild,\n title: '新增子级',\n chip: `归属于第 ${state.parentRow.index + 1} 行`,\n tone: 'child'\n }\n }\n if (state.type === 'create') {\n return { icon: Plus, title: '新增', tone: 'create' }\n }\n return {\n icon: EditPen,\n title: '编辑',\n chip: state.row ? `第 ${state.row.index + 1} 行` : undefined,\n tone: 'update'\n }\n})\n\n/** 通过 row 切换 / 类型切换驱动表单内容淡出淡入 */\nconst bodyKey = computed(() => {\n return `${state.type}-${state.row?.uid ?? 'create'}-${state.parentRow?.uid ?? 'root'}`\n})\n</script>\n"],"mappings":"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"}
@@ -50,9 +50,25 @@ function useEdit(options) {
50
50
  if (row) {
51
51
  state.type = "update";
52
52
  state.visible = true;
53
- if (!isQuickMode()) setFormData(row.data);
53
+ setFormData(row.data);
54
54
  } else state.visible = false;
55
55
  }, { flush: "sync" });
56
+ /**
57
+ * quick + update:model 实时写回 row.data,让表格显示同步更新。
58
+ * normal 模式不写回,需等待保存。
59
+ *
60
+ * 监听源仅限 props.model(deep 遍历仅作用于 model,不触及 row 树);
61
+ * mode/type/row 在回调中读取但不作为依赖,避免行切换等场景误触发。
62
+ */
63
+ watch(() => props.model, (model) => {
64
+ const { row, type } = state;
65
+ if (props.mode !== "quick" || type !== "update" || !row || !model) return;
66
+ const data = row.data;
67
+ Object.keys(model).forEach((key) => {
68
+ const next = o(model).get(key);
69
+ if (o(data).get(key) !== next) o(data).set(key, next);
70
+ });
71
+ }, { deep: true });
56
72
  const childrenKey = computed(() => {
57
73
  return typeof props.tree === "string" ? props.tree : "children";
58
74
  });
@@ -1 +1 @@
1
- {"version":3,"file":"use-edit.js","names":[],"sources":["../../../src/components/batch-edit/use-edit.ts"],"sourcesContent":["import { last, o, safeRun } from '@cat-kit/core'\nimport { computed, nextTick, shallowReactive, shallowRef, watch, type ShallowRef } from 'vue'\n\nimport type { TableRow, BatchEditEmits, BatchEditProps } from '../../types'\nimport type { FormExposed } from '../../types/form'\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 formRef: ShallowRef<FormExposed | undefined>\n}\n\nexport interface BatchEditStates {\n /** 层级 */\n depth?: number\n visible: boolean\n type: 'create' | 'update'\n loading: boolean\n row?: TableRow\n /** 父级,只在添加子级时会有值 */\n parentRow?: TableRow\n}\n\nexport interface EditReturned {\n state: BatchEditStates\n insertIndexes: ShallowRef<number[]>\n handleSave: () => Promise<void>\n handleClose: () => void\n handleCreate: () => Promise<void>\n handleEdit: (row: TableRow) => void\n handleCopy: (row: TableRow) => Promise<void>\n handleDelete: (row: TableRow) => Promise<void>\n handleInsertToNext: (row: TableRow) => Promise<void>\n handleInsertToPrev: (row: TableRow) => Promise<void>\n handleInsertChild: (row: TableRow) => Promise<void>\n}\n\nfunction snapshotData(data?: Record<string, any>) {\n return safeRun(() => JSON.parse(JSON.stringify(data ?? {})), {})\n}\n\nfunction replaceModelData(model: Record<string, any>, data: Record<string, any>) {\n for (const key of Object.keys(model)) {\n delete model[key]\n }\n Object.assign(model, data)\n}\n\nexport function useEdit(options: Options): EditReturned {\n const { props, emit, formRef } = options\n\n /** 组件状态 */\n const state = shallowReactive<BatchEditStates>({\n depth: undefined,\n visible: false,\n type: 'create',\n loading: false\n })\n\n const insertIndexes = shallowRef<number[]>([])\n\n function snapshotModelData() {\n return snapshotData(props.model)\n }\n\n function setFormData(formData?: Record<string, any>) {\n if (!props.model) return\n replaceModelData(props.model, snapshotData(formData))\n }\n\n const isQuickMode = () => props.mode === 'quick'\n\n async function runBeforeCreate(data: Record<string, any>) {\n await props.beforeCreate?.(data, state.parentRow?.data)\n }\n\n watch(\n () => state.row,\n (row) => {\n if (row) {\n state.type = 'update'\n state.visible = true\n if (!isQuickMode()) {\n setFormData(row.data)\n }\n } else {\n state.visible = false\n }\n },\n { flush: 'sync' }\n )\n\n const childrenKey = computed(() => {\n return typeof props.tree === 'string' ? props.tree : 'children'\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(\n data ?? [],\n insertIndexes.value.slice(0, -1),\n childrenKey.value\n )\n\n if (parent) {\n const children = parent[childrenKey.value]\n if (!children) {\n parent[childrenKey.value] = [item]\n } else {\n children.splice(last(insertIndexes.value), 0, item)\n }\n } else {\n data.splice(last(insertIndexes.value), 0, item)\n }\n\n emit('update:data', data)\n\n return item\n }\n\n function getInsertData(): Record<string, any> {\n return snapshotModelData()\n }\n\n async function runCreate(cb: () => void, initialData?: Record<string, any>) {\n state.parentRow = undefined\n if (state.row) {\n state.row.isCurrent = false\n }\n state.row = undefined\n state.type = 'create'\n cb()\n\n await nextTick()\n\n setFormData(initialData)\n\n if (isQuickMode() && props.model) {\n await runBeforeCreate(props.model)\n }\n\n state.visible = true\n }\n\n /**\n * 点击新增按钮\n */\n async function handleCreate() {\n const { data } = props\n await runCreate(() => {\n state.depth = 1\n insertIndexes.value = [data?.length ?? 0]\n })\n }\n\n async function handleInsertToPrev(row: TableRow) {\n await runCreate(() => {\n state.depth = row.depth\n insertIndexes.value = [...row.indexes]\n })\n }\n\n async function handleInsertToNext(row: TableRow) {\n await runCreate(() => {\n state.depth = row.depth\n insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1]\n })\n }\n\n async function handleInsertChild(row: TableRow) {\n await runCreate(() => {\n state.parentRow = row\n state.depth = row.depth + 1\n row.expanded = true\n\n insertIndexes.value = [...row.indexes, row.children?.length ?? 0]\n })\n }\n\n /** 编辑指定行(等同于点击行) */\n function handleEdit(row: TableRow) {\n state.type = 'update'\n state.row = row\n state.depth = row.depth\n }\n\n /** 复制指定行:在其下方以 create 模式打开表单,并预填副本数据 */\n async function handleCopy(row: TableRow) {\n await runCreate(() => {\n state.depth = row.depth\n insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1]\n }, row.data)\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\n if (!model) return\n\n if (isQuickMode() && state.type === 'update') return\n\n formRef.value?.clearValidate()\n const valid = await formRef.value?.validate()\n\n if (!valid) return\n\n // quick + create:仅校验 + insert\n if (isQuickMode() && state.type === 'create') {\n insert(getInsertData())\n return\n }\n\n let item = getInsertData()\n\n // normal + create:beforeCreate → saveMethod → insert\n if (state.type === 'create') {\n await runBeforeCreate(item)\n if (saveMethod) {\n const result = await saveMethod(item, state.type, state.parentRow?.data)\n if (result) {\n item = result\n }\n }\n insert(item)\n return\n }\n\n // normal + update\n if (saveMethod) {\n const result = await saveMethod(item, state.type, 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 state.row = undefined\n state.depth = undefined\n }\n\n emit('update:data', data)\n })\n\n function handleClose() {\n state.visible = false\n if (state.row) {\n state.row.isCurrent = false\n }\n state.row = undefined\n state.parentRow = undefined\n state.depth = undefined\n insertIndexes.value = []\n }\n\n return {\n state,\n insertIndexes,\n handleDelete,\n handleInsertChild,\n handleInsertToNext,\n handleInsertToPrev,\n handleCreate,\n handleEdit,\n handleCopy,\n handleClose,\n handleSave\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-edit.js","names":[],"sources":["../../../src/components/batch-edit/use-edit.ts"],"sourcesContent":["import { last, o, safeRun } from '@cat-kit/core'\nimport { computed, nextTick, shallowReactive, shallowRef, watch, type ShallowRef } from 'vue'\n\nimport type { TableRow, BatchEditEmits, BatchEditProps } from '../../types'\nimport type { FormExposed } from '../../types/form'\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 formRef: ShallowRef<FormExposed | undefined>\n}\n\nexport interface BatchEditStates {\n /** 层级 */\n depth?: number\n visible: boolean\n type: 'create' | 'update'\n loading: boolean\n row?: TableRow\n /** 父级,只在添加子级时会有值 */\n parentRow?: TableRow\n}\n\nexport interface EditReturned {\n state: BatchEditStates\n insertIndexes: ShallowRef<number[]>\n handleSave: () => Promise<void>\n handleClose: () => void\n handleCreate: () => Promise<void>\n handleEdit: (row: TableRow) => void\n handleCopy: (row: TableRow) => Promise<void>\n handleDelete: (row: TableRow) => Promise<void>\n handleInsertToNext: (row: TableRow) => Promise<void>\n handleInsertToPrev: (row: TableRow) => Promise<void>\n handleInsertChild: (row: TableRow) => Promise<void>\n}\n\nfunction snapshotData(data?: Record<string, any>) {\n return safeRun(() => JSON.parse(JSON.stringify(data ?? {})), {})\n}\n\nfunction replaceModelData(model: Record<string, any>, data: Record<string, any>) {\n for (const key of Object.keys(model)) {\n delete model[key]\n }\n Object.assign(model, data)\n}\n\nexport function useEdit(options: Options): EditReturned {\n const { props, emit, formRef } = options\n\n /** 组件状态 */\n const state = shallowReactive<BatchEditStates>({\n depth: undefined,\n visible: false,\n type: 'create',\n loading: false\n })\n\n const insertIndexes = shallowRef<number[]>([])\n\n function snapshotModelData() {\n return snapshotData(props.model)\n }\n\n function setFormData(formData?: Record<string, any>) {\n if (!props.model) return\n replaceModelData(props.model, snapshotData(formData))\n }\n\n const isQuickMode = () => props.mode === 'quick'\n\n async function runBeforeCreate(data: Record<string, any>) {\n await props.beforeCreate?.(data, state.parentRow?.data)\n }\n\n watch(\n () => state.row,\n (row) => {\n if (row) {\n state.type = 'update'\n state.visible = true\n // 所有模式下都回填 model,保证表单内条件字段(依赖 model)能响应当前编辑行\n setFormData(row.data)\n } else {\n state.visible = false\n }\n },\n { flush: 'sync' }\n )\n\n /**\n * quick + update:model 实时写回 row.data,让表格显示同步更新。\n * normal 模式不写回,需等待保存。\n *\n * 监听源仅限 props.model(deep 遍历仅作用于 model,不触及 row 树);\n * mode/type/row 在回调中读取但不作为依赖,避免行切换等场景误触发。\n */\n watch(\n () => props.model,\n (model) => {\n const { row, type } = state\n if (props.mode !== 'quick' || type !== 'update' || !row || !model) return\n const data = row.data\n Object.keys(model).forEach((key) => {\n const next = o(model).get(key)\n if (o(data).get(key) !== next) {\n o(data).set(key, next)\n }\n })\n },\n { deep: true }\n )\n\n const childrenKey = computed(() => {\n return typeof props.tree === 'string' ? props.tree : 'children'\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(\n data ?? [],\n insertIndexes.value.slice(0, -1),\n childrenKey.value\n )\n\n if (parent) {\n const children = parent[childrenKey.value]\n if (!children) {\n parent[childrenKey.value] = [item]\n } else {\n children.splice(last(insertIndexes.value), 0, item)\n }\n } else {\n data.splice(last(insertIndexes.value), 0, item)\n }\n\n emit('update:data', data)\n\n return item\n }\n\n function getInsertData(): Record<string, any> {\n return snapshotModelData()\n }\n\n async function runCreate(cb: () => void, initialData?: Record<string, any>) {\n state.parentRow = undefined\n if (state.row) {\n state.row.isCurrent = false\n }\n state.row = undefined\n state.type = 'create'\n cb()\n\n await nextTick()\n\n setFormData(initialData)\n\n if (isQuickMode() && props.model) {\n await runBeforeCreate(props.model)\n }\n\n state.visible = true\n }\n\n /**\n * 点击新增按钮\n */\n async function handleCreate() {\n const { data } = props\n await runCreate(() => {\n state.depth = 1\n insertIndexes.value = [data?.length ?? 0]\n })\n }\n\n async function handleInsertToPrev(row: TableRow) {\n await runCreate(() => {\n state.depth = row.depth\n insertIndexes.value = [...row.indexes]\n })\n }\n\n async function handleInsertToNext(row: TableRow) {\n await runCreate(() => {\n state.depth = row.depth\n insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1]\n })\n }\n\n async function handleInsertChild(row: TableRow) {\n await runCreate(() => {\n state.parentRow = row\n state.depth = row.depth + 1\n row.expanded = true\n\n insertIndexes.value = [...row.indexes, row.children?.length ?? 0]\n })\n }\n\n /** 编辑指定行(等同于点击行) */\n function handleEdit(row: TableRow) {\n state.type = 'update'\n state.row = row\n state.depth = row.depth\n }\n\n /** 复制指定行:在其下方以 create 模式打开表单,并预填副本数据 */\n async function handleCopy(row: TableRow) {\n await runCreate(() => {\n state.depth = row.depth\n insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1]\n }, row.data)\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\n if (!model) return\n\n if (isQuickMode() && state.type === 'update') return\n\n formRef.value?.clearValidate()\n const valid = await formRef.value?.validate()\n\n if (!valid) return\n\n // quick + create:仅校验 + insert\n if (isQuickMode() && state.type === 'create') {\n insert(getInsertData())\n return\n }\n\n let item = getInsertData()\n\n // normal + create:beforeCreate → saveMethod → insert\n if (state.type === 'create') {\n await runBeforeCreate(item)\n if (saveMethod) {\n const result = await saveMethod(item, state.type, state.parentRow?.data)\n if (result) {\n item = result\n }\n }\n insert(item)\n return\n }\n\n // normal + update\n if (saveMethod) {\n const result = await saveMethod(item, state.type, 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 state.row = undefined\n state.depth = undefined\n }\n\n emit('update:data', data)\n })\n\n function handleClose() {\n state.visible = false\n if (state.row) {\n state.row.isCurrent = false\n }\n state.row = undefined\n state.parentRow = undefined\n state.depth = undefined\n insertIndexes.value = []\n }\n\n return {\n state,\n insertIndexes,\n handleDelete,\n handleInsertChild,\n handleInsertToNext,\n handleInsertToPrev,\n handleCreate,\n handleEdit,\n handleCopy,\n handleClose,\n handleSave\n }\n}\n"],"mappings":"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"}
@@ -17,9 +17,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
17
17
  "onUpdate:modelValue"?: ((checked: boolean) => any) | undefined;
18
18
  onChange?: ((checked: boolean) => any) | undefined;
19
19
  }>, {
20
+ type: import("@veltra/utils").ColorType;
20
21
  disabled: boolean;
21
22
  readonly: boolean;
22
- type: import("@veltra/utils").ColorType;
23
23
  round: boolean;
24
24
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
25
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -7,8 +7,8 @@ declare const __VLS_export: import("vue").DefineComponent<DatePanelProps, {}, {}
7
7
  "onSelect:range-date"?: ((rangeDate?: [import("@cat-kit/core").Dater, import("@cat-kit/core").Dater] | undefined) => any) | undefined;
8
8
  "onSelect:date"?: ((date: import("@cat-kit/core").Dater) => any) | undefined;
9
9
  }>, {
10
- size: import("@veltra/utils").ComponentSize;
11
10
  type: "date" | "month" | "year";
11
+ size: import("@veltra/utils").ComponentSize;
12
12
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  //#endregion
@@ -5,11 +5,11 @@ declare const __VLS_export: import("vue").DefineComponent<DatePickerProps, {}, {
5
5
  }, string, import("vue").PublicProps, Readonly<DatePickerProps> & Readonly<{
6
6
  "onUpdate:modelValue"?: ((value?: string | number | Date | undefined) => any) | undefined;
7
7
  }>, {
8
+ type: "date" | "month" | "year";
8
9
  placeholder: string;
9
10
  clearable: boolean;
10
11
  disabled: boolean;
11
12
  readonly: boolean;
12
- type: "date" | "month" | "year";
13
13
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
14
  declare const _default: typeof __VLS_export;
15
15
  //#endregion
@@ -5,11 +5,11 @@ declare const __VLS_export: import("vue").DefineComponent<DateRangePickerProps,
5
5
  }, string, import("vue").PublicProps, Readonly<DateRangePickerProps> & Readonly<{
6
6
  "onUpdate:modelValue"?: ((value?: [string, string] | undefined) => any) | undefined;
7
7
  }>, {
8
+ type: "date" | "month" | "year";
8
9
  placeholder: [string, string];
9
10
  clearable: boolean;
10
11
  disabled: boolean;
11
12
  readonly: boolean;
12
- type: "date" | "month" | "year";
13
13
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
14
  declare const _default: typeof __VLS_export;
15
15
  //#endregion
@@ -3,7 +3,7 @@ import _sfc_main$2 from "../tip/tip.js";
3
3
  import { collectNavBranchPaths } from "../nav/helper.js";
4
4
  import _sfc_main$3 from "../nav/nav.js";
5
5
  import _sfc_main$4 from "./dual-nav-app.js";
6
- import { findRootApp } from "./helper.js";
6
+ import { findFirstLeaf, findRootApp } from "./helper.js";
7
7
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, renderList, toDisplayString, unref, useTemplateRef, watch, withCtx } from "vue";
8
8
  import { bem } from "@veltra/utils";
9
9
  import { Folder, FolderOpened } from "@veltra/icons/normal";
@@ -64,7 +64,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
64
64
  function handleAppClick(app) {
65
65
  if (app.disabled) return;
66
66
  panelAppPath.value = app.path;
67
- if (!app.children?.length) emit("item-click", app);
67
+ emit("item-click", findFirstLeaf(app));
68
68
  }
69
69
  function handleToggleExpandAll() {
70
70
  if (allExpanded.value) {
@@ -1 +1 @@
1
- {"version":3,"file":"dual-nav.js","names":[],"sources":["../../../src/components/dual-nav/dual-nav.vue"],"sourcesContent":["<template>\n <div :class=\"cls.b\">\n <div :class=\"cls.e('rail')\">\n <DualNavApp\n v-for=\"app in menus\"\n :key=\"app.path\"\n :app=\"app\"\n :active=\"isAppActive(app)\"\n :selected=\"isAppSelected(app)\"\n @click=\"handleAppClick(app)\"\n />\n </div>\n\n <!-- <div :class=\"cls.e('divider')\" aria-hidden=\"true\" /> -->\n\n <div :class=\"cls.e('panel')\">\n <template v-if=\"panelApp\">\n <div :class=\"cls.e('panel-header')\">\n <div :class=\"cls.e('panel-header-main')\">\n <h3 :class=\"cls.e('panel-title')\">{{ panelApp.title }}</h3>\n <p v-if=\"panelApp.description\" :class=\"cls.e('description')\">\n {{ panelApp.description }}\n </p>\n </div>\n\n <u-tip\n v-if=\"hasExpandableMenus\"\n :content=\"allExpanded ? '折叠全部' : '展开全部'\"\n hide-arrow\n >\n <button\n type=\"button\"\n :class=\"cls.e('toggle-expand')\"\n :aria-label=\"allExpanded ? '折叠全部' : '展开全部'\"\n @click=\"handleToggleExpandAll\"\n >\n <u-icon>\n <FolderOpened v-if=\"!allExpanded\" />\n <Folder v-else />\n </u-icon>\n </button>\n </u-tip>\n </div>\n\n <div :class=\"cls.e('panel-body')\">\n <UNav\n ref=\"navRef\"\n :menus=\"panelMenus\"\n :current-path=\"currentPath\"\n :collapsed=\"false\"\n @item-click=\"emit('item-click', $event)\"\n />\n </div>\n </template>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Folder, FolderOpened } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, ref, useTemplateRef, watch } from 'vue'\n\nimport type { DualNavEmits, DualNavProps, DualNavRootItem } from '../../types/dual-nav'\nimport type { NavExposed, NavItem } from '../../types/nav'\nimport { UIcon } from '../icon'\nimport { UNav } from '../nav'\nimport { collectNavBranchPaths } from '../nav/helper'\nimport { UTip } from '../tip'\nimport DualNavApp from './dual-nav-app.vue'\nimport { findRootApp } from './helper'\n\ndefineOptions({ name: 'DualNav' })\n\nconst props = defineProps<DualNavProps>()\nconst emit = defineEmits<DualNavEmits>()\n\nconst cls = bem('dual-nav')\nconst navRef = useTemplateRef<NavExposed>('navRef')\n\n/** 右栏当前展示的应用(点击左轨或 currentPath 同步) */\nconst panelAppPath = ref<string>()\nconst allExpanded = ref(false)\n\nfunction syncPanelAppFromCurrentPath(currentPath?: string) {\n const rootApp = findRootApp(props.menus, currentPath)\n if (rootApp) {\n panelAppPath.value = rootApp.path\n return\n }\n\n if (!panelAppPath.value && props.menus?.length) {\n panelAppPath.value = props.menus[0]!.path\n }\n}\n\nwatch(\n () => [props.currentPath, props.menus] as const,\n ([currentPath]) => {\n syncPanelAppFromCurrentPath(currentPath)\n },\n { immediate: true }\n)\n\nconst panelApp = computed<DualNavRootItem | undefined>(() => {\n if (!panelAppPath.value || !props.menus?.length) return undefined\n return props.menus.find((app) => app.path === panelAppPath.value)\n})\n\n/** 右栏菜单:有子级时展示子级,单层时展示根项自身 */\nconst panelMenus = computed<NavItem[] | undefined>(() => {\n const app = panelApp.value\n if (!app) return undefined\n if (app.children?.length) return app.children\n return [app]\n})\n\nconst hasExpandableMenus = computed(() => {\n return collectNavBranchPaths(panelMenus.value).length > 0\n})\n\nwatch(panelAppPath, () => {\n allExpanded.value = false\n})\n\n/** 左轨高亮:currentPath 落在该应用下时为激活态 */\nfunction isAppActive(app: NavItem): boolean {\n return findRootApp(props.menus, props.currentPath)?.path === app.path\n}\n\n/** 左轨选中:已切换右栏但尚未导航到该应用下任一菜单时,使用 hover 风格 */\nfunction isAppSelected(app: NavItem): boolean {\n return panelAppPath.value === app.path && !isAppActive(app)\n}\n\nfunction handleAppClick(app: NavItem) {\n if (app.disabled) return\n\n panelAppPath.value = app.path\n\n if (!app.children?.length) {\n emit('item-click', app)\n }\n}\n\nfunction handleToggleExpandAll() {\n if (allExpanded.value) {\n navRef.value?.collapseAll()\n allExpanded.value = false\n return\n }\n\n navRef.value?.expandAll()\n allExpanded.value = true\n}\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"dual-nav.js","names":[],"sources":["../../../src/components/dual-nav/dual-nav.vue"],"sourcesContent":["<template>\n <div :class=\"cls.b\">\n <div :class=\"cls.e('rail')\">\n <DualNavApp\n v-for=\"app in menus\"\n :key=\"app.path\"\n :app=\"app\"\n :active=\"isAppActive(app)\"\n :selected=\"isAppSelected(app)\"\n @click=\"handleAppClick(app)\"\n />\n </div>\n\n <!-- <div :class=\"cls.e('divider')\" aria-hidden=\"true\" /> -->\n\n <div :class=\"cls.e('panel')\">\n <template v-if=\"panelApp\">\n <div :class=\"cls.e('panel-header')\">\n <div :class=\"cls.e('panel-header-main')\">\n <h3 :class=\"cls.e('panel-title')\">{{ panelApp.title }}</h3>\n <p v-if=\"panelApp.description\" :class=\"cls.e('description')\">\n {{ panelApp.description }}\n </p>\n </div>\n\n <u-tip\n v-if=\"hasExpandableMenus\"\n :content=\"allExpanded ? '折叠全部' : '展开全部'\"\n hide-arrow\n >\n <button\n type=\"button\"\n :class=\"cls.e('toggle-expand')\"\n :aria-label=\"allExpanded ? '折叠全部' : '展开全部'\"\n @click=\"handleToggleExpandAll\"\n >\n <u-icon>\n <FolderOpened v-if=\"!allExpanded\" />\n <Folder v-else />\n </u-icon>\n </button>\n </u-tip>\n </div>\n\n <div :class=\"cls.e('panel-body')\">\n <UNav\n ref=\"navRef\"\n :menus=\"panelMenus\"\n :current-path=\"currentPath\"\n :collapsed=\"false\"\n @item-click=\"emit('item-click', $event)\"\n />\n </div>\n </template>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Folder, FolderOpened } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, ref, useTemplateRef, watch } from 'vue'\n\nimport type { DualNavEmits, DualNavProps, DualNavRootItem } from '../../types/dual-nav'\nimport type { NavExposed, NavItem } from '../../types/nav'\nimport { UIcon } from '../icon'\nimport { UNav } from '../nav'\nimport { collectNavBranchPaths } from '../nav/helper'\nimport { UTip } from '../tip'\nimport DualNavApp from './dual-nav-app.vue'\nimport { findFirstLeaf, findRootApp } from './helper'\n\ndefineOptions({ name: 'DualNav' })\n\nconst props = defineProps<DualNavProps>()\nconst emit = defineEmits<DualNavEmits>()\n\nconst cls = bem('dual-nav')\nconst navRef = useTemplateRef<NavExposed>('navRef')\n\n/** 右栏当前展示的应用(点击左轨或 currentPath 同步) */\nconst panelAppPath = ref<string>()\nconst allExpanded = ref(false)\n\nfunction syncPanelAppFromCurrentPath(currentPath?: string) {\n const rootApp = findRootApp(props.menus, currentPath)\n if (rootApp) {\n panelAppPath.value = rootApp.path\n return\n }\n\n if (!panelAppPath.value && props.menus?.length) {\n panelAppPath.value = props.menus[0]!.path\n }\n}\n\nwatch(\n () => [props.currentPath, props.menus] as const,\n ([currentPath]) => {\n syncPanelAppFromCurrentPath(currentPath)\n },\n { immediate: true }\n)\n\nconst panelApp = computed<DualNavRootItem | undefined>(() => {\n if (!panelAppPath.value || !props.menus?.length) return undefined\n return props.menus.find((app) => app.path === panelAppPath.value)\n})\n\n/** 右栏菜单:有子级时展示子级,单层时展示根项自身 */\nconst panelMenus = computed<NavItem[] | undefined>(() => {\n const app = panelApp.value\n if (!app) return undefined\n if (app.children?.length) return app.children\n return [app]\n})\n\nconst hasExpandableMenus = computed(() => {\n return collectNavBranchPaths(panelMenus.value).length > 0\n})\n\nwatch(panelAppPath, () => {\n allExpanded.value = false\n})\n\n/** 左轨高亮:currentPath 落在该应用下时为激活态 */\nfunction isAppActive(app: NavItem): boolean {\n return findRootApp(props.menus, props.currentPath)?.path === app.path\n}\n\n/** 左轨选中:已切换右栏但尚未导航到该应用下任一菜单时,使用 hover 风格 */\nfunction isAppSelected(app: NavItem): boolean {\n return panelAppPath.value === app.path && !isAppActive(app)\n}\n\nfunction handleAppClick(app: NavItem) {\n if (app.disabled) return\n\n panelAppPath.value = app.path\n\n emit('item-click', findFirstLeaf(app))\n}\n\nfunction handleToggleExpandAll() {\n if (allExpanded.value) {\n navRef.value?.collapseAll()\n allExpanded.value = false\n return\n }\n\n navRef.value?.expandAll()\n allExpanded.value = true\n}\n</script>\n"],"mappings":"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"}
@@ -15,7 +15,12 @@ function findRootApp(menus, currentPath) {
15
15
  if (matched) return app;
16
16
  }
17
17
  }
18
+ /** 深度优先查找首个叶子节点(无子级或子级为空) */
19
+ function findFirstLeaf(node) {
20
+ if (!node.children?.length) return node;
21
+ return findFirstLeaf(node.children[0]);
22
+ }
18
23
  //#endregion
19
- export { findRootApp };
24
+ export { findFirstLeaf, findRootApp };
20
25
 
21
26
  //# sourceMappingURL=helper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helper.js","names":[],"sources":["../../../src/components/dual-nav/helper.ts"],"sourcesContent":["import type { DualNavRootItem } from '../../types'\nimport { walkNavWithPath } from '../nav/walk-nav-path'\n\n/** 根据当前路径反查所属顶层应用 */\nexport function findRootApp(\n menus: DualNavRootItem[] | undefined,\n currentPath?: string\n): DualNavRootItem | undefined {\n if (!currentPath || !menus?.length) return undefined\n\n for (const app of menus) {\n if (app.path === currentPath) return app\n\n let matched = false\n walkNavWithPath(app, (node) => {\n if (node.path === currentPath) {\n matched = true\n return false\n }\n })\n if (matched) return app\n }\n\n return undefined\n}\n"],"mappings":";;;AAIA,SAAgB,YACd,OACA,aAC6B;CAC7B,IAAI,CAAC,eAAe,CAAC,OAAO,QAAQ,OAAO,KAAA;CAE3C,KAAK,MAAM,OAAO,OAAO;EACvB,IAAI,IAAI,SAAS,aAAa,OAAO;EAErC,IAAI,UAAU;EACd,gBAAgB,MAAM,SAAS;GAC7B,IAAI,KAAK,SAAS,aAAa;IAC7B,UAAU;IACV,OAAO;GACT;EACF,CAAC;EACD,IAAI,SAAS,OAAO;CACtB;AAGF"}
1
+ {"version":3,"file":"helper.js","names":[],"sources":["../../../src/components/dual-nav/helper.ts"],"sourcesContent":["import type { DualNavRootItem, NavItem } from '../../types'\nimport { walkNavWithPath } from '../nav/walk-nav-path'\n\n/** 根据当前路径反查所属顶层应用 */\nexport function findRootApp(\n menus: DualNavRootItem[] | undefined,\n currentPath?: string\n): DualNavRootItem | undefined {\n if (!currentPath || !menus?.length) return undefined\n\n for (const app of menus) {\n if (app.path === currentPath) return app\n\n let matched = false\n walkNavWithPath(app, (node) => {\n if (node.path === currentPath) {\n matched = true\n return false\n }\n })\n if (matched) return app\n }\n\n return undefined\n}\n\n/** 深度优先查找首个叶子节点(无子级或子级为空) */\nexport function findFirstLeaf(node: NavItem): NavItem {\n if (!node.children?.length) return node\n return findFirstLeaf(node.children[0]!)\n}\n"],"mappings":";;;AAIA,SAAgB,YACd,OACA,aAC6B;CAC7B,IAAI,CAAC,eAAe,CAAC,OAAO,QAAQ,OAAO,KAAA;CAE3C,KAAK,MAAM,OAAO,OAAO;EACvB,IAAI,IAAI,SAAS,aAAa,OAAO;EAErC,IAAI,UAAU;EACd,gBAAgB,MAAM,SAAS;GAC7B,IAAI,KAAK,SAAS,aAAa;IAC7B,UAAU;IACV,OAAO;GACT;EACF,CAAC;EACD,IAAI,SAAS,OAAO;CACtB;AAGF;;AAGA,SAAgB,cAAc,MAAwB;CACpD,IAAI,CAAC,KAAK,UAAU,QAAQ,OAAO;CACnC,OAAO,cAAc,KAAK,SAAS,EAAG;AACxC"}
@@ -20,7 +20,6 @@
20
20
  height: 100%;
21
21
  min-height: 0;
22
22
  background-color: var(--u-nav-bg-color);
23
- border: 1px solid var(--u-border-color);
24
23
  border-radius: calc(var(--u-radius-large) + 6px);
25
24
  overflow: hidden;
26
25
  }
@@ -53,7 +52,6 @@
53
52
  justify-content: space-between;
54
53
  gap: 8px;
55
54
  padding: 12px 12px 10px;
56
- border-bottom: 1px solid var(--u-border-color);
57
55
  }
58
56
  .u-dual-nav__panel-header-main {
59
57
  flex: 1;
@@ -1,8 +1,8 @@
1
1
  import { EmptyProps } from "../../types/empty.js";
2
2
  //#region src/components/empty/empty.vue.d.ts
3
3
  declare const __VLS_export: import("vue").DefineComponent<EmptyProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<EmptyProps> & Readonly<{}>, {
4
- text: string;
5
4
  size: number;
5
+ text: string;
6
6
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
7
  declare const _default: typeof __VLS_export;
8
8
  //#endregion
@@ -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
- duration: number;
5
4
  format: "currency" | "percent" | "decimal";
5
+ duration: number;
6
6
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
7
  declare const _default: typeof __VLS_export;
8
8
  //#endregion
@@ -6,11 +6,11 @@ type __VLS_Slots = {
6
6
  tip?: (scope: StepsSlotScope) => any;
7
7
  };
8
8
  declare const __VLS_base: import("vue").DefineComponent<StepsProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
9
- "item-click": (item: Record<string, any>, index: number) => any;
10
9
  "update:current": (value?: string | number | undefined) => any;
10
+ "item-click": (item: Record<string, any>, index: number) => any;
11
11
  }, string, import("vue").PublicProps, Readonly<StepsProps> & Readonly<{
12
- "onItem-click"?: ((item: Record<string, any>, index: number) => any) | undefined;
13
12
  "onUpdate:current"?: ((value?: string | number | undefined) => any) | undefined;
13
+ "onItem-click"?: ((item: Record<string, any>, index: number) => any) | undefined;
14
14
  }>, {
15
15
  labelKey: string;
16
16
  direction: "horizontal" | "vertical";
@@ -15,12 +15,12 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
15
15
  "update:modelValue": (value: string | undefined) => any;
16
16
  } & {
17
17
  "update:modelValue": (value: string) => any;
18
- click: (item: TabItem, index: number) => any;
19
18
  close: (item: TabItem, index: number) => any;
19
+ click: (item: TabItem, index: number) => any;
20
20
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
21
21
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
22
- onClick?: ((item: TabItem, index: number) => any) | undefined;
23
22
  onClose?: ((item: TabItem, index: number) => any) | undefined;
23
+ onClick?: ((item: TabItem, index: number) => any) | undefined;
24
24
  }>, {
25
25
  closable: boolean;
26
26
  position: "top" | "bottom";
@@ -15,12 +15,12 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
15
15
  "update:modelValue": (value: string | undefined) => any;
16
16
  } & {
17
17
  "update:modelValue": (value: string) => any;
18
- click: (item: TabItem, index: number) => any;
19
18
  close: (item: TabItem, index: number) => any;
19
+ click: (item: TabItem, index: number) => any;
20
20
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
21
21
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
22
- onClick?: ((item: TabItem, index: number) => any) | undefined;
23
22
  onClose?: ((item: TabItem, index: number) => any) | undefined;
23
+ onClick?: ((item: TabItem, index: number) => any) | undefined;
24
24
  }>, {
25
25
  closable: boolean;
26
26
  position: "left" | "right";
@@ -10,12 +10,12 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
10
10
  "update:modelValue": (value: string | undefined) => any;
11
11
  } & {
12
12
  "update:modelValue": (value: string) => any;
13
- click: (item: TabItem, index: number) => any;
14
13
  close: (item: TabItem, index: number) => any;
14
+ click: (item: TabItem, index: number) => any;
15
15
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
16
16
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
17
- onClick?: ((item: TabItem, index: number) => any) | undefined;
18
17
  onClose?: ((item: TabItem, index: number) => any) | undefined;
18
+ onClick?: ((item: TabItem, index: number) => any) | undefined;
19
19
  }>, {
20
20
  closable: boolean;
21
21
  position: "left" | "right" | "top" | "bottom";
@@ -26,7 +26,7 @@ interface BatchEditProps extends TableProps {
26
26
  /**
27
27
  * 编辑模式
28
28
  * @default 'normal'
29
- * @description `quick` 模式下编辑行时表单直接绑定 `row.data` 实时更新,且不调用 `saveMethod`
29
+ * @description `quick` 模式下编辑行时表单实时写回 `row.data`(经 `model` 中转),且不调用 `saveMethod`
30
30
  */
31
31
  mode?: BatchEditMode;
32
32
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltra/desktop",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -68,11 +68,11 @@
68
68
  "@cat-kit/core": ">=1.1.6",
69
69
  "@cat-kit/excel": ">=1.0.0",
70
70
  "@cat-kit/fe": "^1.1.6",
71
- "@veltra/compositions": "1.2.6",
72
- "@veltra/directives": "1.2.6",
73
- "@veltra/icons": "1.2.6",
74
- "@veltra/styles": "1.2.6",
75
- "@veltra/utils": "1.2.6",
71
+ "@veltra/compositions": "1.2.8",
72
+ "@veltra/directives": "1.2.8",
73
+ "@veltra/icons": "1.2.8",
74
+ "@veltra/styles": "1.2.8",
75
+ "@veltra/utils": "1.2.8",
76
76
  "vue": ">=3.5.0"
77
77
  }
78
78
  }
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <aside :class="cls.e('form')" v-if="state.visible && formModel">
2
+ <aside :class="cls.e('form')" v-if="state.visible && props.model">
3
3
  <header :class="cls.e('form-header')">
4
4
  <span :class="[cls.e('form-icon'), bem.is(headerInfo.tone)]">
5
5
  <u-icon>
@@ -27,7 +27,7 @@
27
27
  <transition name="fade" appear mode="out-in">
28
28
  <u-form
29
29
  ref="formComponentRef"
30
- :model="formModel"
30
+ :model="props.model"
31
31
  :readonly="props.readonly"
32
32
  :label-width="props.labelWidth"
33
33
  >
@@ -129,13 +129,6 @@ const isQuick = computed(() => props.mode === 'quick')
129
129
  const isNormal = computed(() => props.mode !== 'quick')
130
130
  const isQuickUpdate = computed(() => isQuick.value && state.type === 'update')
131
131
 
132
- const formModel = computed(() => {
133
- if (isQuick.value && state.type === 'update' && state.row) {
134
- return state.row.data
135
- }
136
- return props.model
137
- })
138
-
139
132
  interface HeaderInfo {
140
133
  icon: Component
141
134
  title: string
@@ -102,9 +102,8 @@ export function useEdit(options: Options): EditReturned {
102
102
  if (row) {
103
103
  state.type = 'update'
104
104
  state.visible = true
105
- if (!isQuickMode()) {
106
- setFormData(row.data)
107
- }
105
+ // 所有模式下都回填 model,保证表单内条件字段(依赖 model)能响应当前编辑行
106
+ setFormData(row.data)
108
107
  } else {
109
108
  state.visible = false
110
109
  }
@@ -112,6 +111,29 @@ export function useEdit(options: Options): EditReturned {
112
111
  { flush: 'sync' }
113
112
  )
114
113
 
114
+ /**
115
+ * quick + update:model 实时写回 row.data,让表格显示同步更新。
116
+ * normal 模式不写回,需等待保存。
117
+ *
118
+ * 监听源仅限 props.model(deep 遍历仅作用于 model,不触及 row 树);
119
+ * mode/type/row 在回调中读取但不作为依赖,避免行切换等场景误触发。
120
+ */
121
+ watch(
122
+ () => props.model,
123
+ (model) => {
124
+ const { row, type } = state
125
+ if (props.mode !== 'quick' || type !== 'update' || !row || !model) return
126
+ const data = row.data
127
+ Object.keys(model).forEach((key) => {
128
+ const next = o(model).get(key)
129
+ if (o(data).get(key) !== next) {
130
+ o(data).set(key, next)
131
+ }
132
+ })
133
+ },
134
+ { deep: true }
135
+ )
136
+
115
137
  const childrenKey = computed(() => {
116
138
  return typeof props.tree === 'string' ? props.tree : 'children'
117
139
  })
@@ -68,7 +68,7 @@ import { UNav } from '../nav'
68
68
  import { collectNavBranchPaths } from '../nav/helper'
69
69
  import { UTip } from '../tip'
70
70
  import DualNavApp from './dual-nav-app.vue'
71
- import { findRootApp } from './helper'
71
+ import { findFirstLeaf, findRootApp } from './helper'
72
72
 
73
73
  defineOptions({ name: 'DualNav' })
74
74
 
@@ -138,9 +138,7 @@ function handleAppClick(app: NavItem) {
138
138
 
139
139
  panelAppPath.value = app.path
140
140
 
141
- if (!app.children?.length) {
142
- emit('item-click', app)
143
- }
141
+ emit('item-click', findFirstLeaf(app))
144
142
  }
145
143
 
146
144
  function handleToggleExpandAll() {
@@ -1,4 +1,4 @@
1
- import type { DualNavRootItem } from '../../types'
1
+ import type { DualNavRootItem, NavItem } from '../../types'
2
2
  import { walkNavWithPath } from '../nav/walk-nav-path'
3
3
 
4
4
  /** 根据当前路径反查所属顶层应用 */
@@ -23,3 +23,9 @@ export function findRootApp(
23
23
 
24
24
  return undefined
25
25
  }
26
+
27
+ /** 深度优先查找首个叶子节点(无子级或子级为空) */
28
+ export function findFirstLeaf(node: NavItem): NavItem {
29
+ if (!node.children?.length) return node
30
+ return findFirstLeaf(node.children[0]!)
31
+ }
@@ -7,7 +7,6 @@
7
7
  height: 100%;
8
8
  min-height: 0;
9
9
  background-color: var(--u-nav-bg-color);
10
- border: 1px solid fn.use-var(border, color);
11
10
  border-radius: calc(fn.use-var(radius, large) + 6px);
12
11
  overflow: hidden;
13
12
 
@@ -43,7 +42,6 @@
43
42
  justify-content: space-between;
44
43
  gap: 8px;
45
44
  padding: 12px 12px 10px;
46
- border-bottom: 1px solid fn.use-var(border, color);
47
45
  }
48
46
 
49
47
  @include m.e(panel-header-main) {
@@ -39,7 +39,7 @@ export interface BatchEditProps extends TableProps {
39
39
  /**
40
40
  * 编辑模式
41
41
  * @default 'normal'
42
- * @description `quick` 模式下编辑行时表单直接绑定 `row.data` 实时更新,且不调用 `saveMethod`
42
+ * @description `quick` 模式下编辑行时表单实时写回 `row.data`(经 `model` 中转),且不调用 `saveMethod`
43
43
  */
44
44
  mode?: BatchEditMode
45
45
  /**