@veltra/desktop 1.7.19 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/batch-edit/batch-edit-form.js +40 -26
- package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
- package/dist/components/batch-edit/use-handlers.js +3 -3
- package/dist/components/batch-edit/use-handlers.js.map +1 -1
- package/dist/components/batch-edit/use-shortcut-key.js +4 -2
- package/dist/components/batch-edit/use-shortcut-key.js.map +1 -1
- package/dist/components/card/style2.css +0 -2
- package/dist/components/dropdown/dropdown.js +5 -3
- package/dist/components/dropdown/dropdown.js.map +1 -1
- package/dist/components/expression-editor/components/path-preview.js +3 -6
- package/dist/components/expression-editor/components/path-preview.js.map +1 -1
- package/dist/components/expression-editor/components/variable-picker.js +49 -12
- package/dist/components/expression-editor/components/variable-picker.js.map +1 -1
- package/dist/components/expression-editor/core/editor.js +20 -0
- package/dist/components/expression-editor/core/editor.js.map +1 -1
- package/dist/components/expression-editor/expression-editor.js +53 -18
- package/dist/components/expression-editor/expression-editor.js.map +1 -1
- package/dist/components/expression-editor/style2.css +135 -73
- package/dist/components/form/form.js.map +1 -1
- package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js +0 -1
- package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/form/style2.css +8 -3
- package/dist/components/index.d.ts +26 -26
- package/dist/components/index.js +52 -52
- package/dist/components/multi-select/multi-select.js +16 -12
- package/dist/components/multi-select/multi-select.js.map +1 -1
- package/dist/components/multi-select/style2.css +7 -0
- package/dist/components/multi-tree-select/multi-tree-select.js +82 -56
- package/dist/components/multi-tree-select/multi-tree-select.js.map +1 -1
- package/dist/components/multi-tree-select/style2.css +7 -0
- package/dist/components/select/select.js +7 -3
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/select/style2.css +7 -0
- package/dist/components/select/use-options.js +14 -3
- package/dist/components/select/use-options.js.map +1 -1
- package/dist/components/tree-select/style2.css +7 -0
- package/dist/components/tree-select/tree-select.js +49 -17
- package/dist/components/tree-select/tree-select.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/types/dropdown.d.ts +10 -0
- package/dist/types/multi-tree-select.d.ts +7 -1
- package/dist/types/tree-select.d.ts +7 -1
- package/package.json +1 -1
- package/src/components/batch-edit/__test__/batch-edit.test.ts +112 -1
- package/src/components/batch-edit/batch-edit-form.vue +21 -2
- package/src/components/batch-edit/use-handlers.ts +6 -5
- package/src/components/batch-edit/use-shortcut-key.ts +5 -2
- package/src/components/card/style.scss +0 -2
- package/src/components/dropdown/dropdown.vue +9 -4
- package/src/components/expression-editor/components/path-preview.vue +1 -2
- package/src/components/expression-editor/components/variable-picker.vue +54 -13
- package/src/components/expression-editor/core/editor.ts +28 -0
- package/src/components/expression-editor/expression-editor.vue +47 -17
- package/src/components/expression-editor/style.scss +167 -82
- package/src/components/form/form.vue +0 -1
- package/src/components/form/style.scss +11 -4
- package/src/components/multi-select/multi-select.vue +11 -2
- package/src/components/multi-select/style.scss +6 -0
- package/src/components/multi-tree-select/multi-tree-select.vue +58 -9
- package/src/components/multi-tree-select/style.scss +6 -0
- package/src/components/select/__test__/select.test.ts +78 -0
- package/src/components/select/select.vue +13 -3
- package/src/components/select/style.scss +6 -0
- package/src/components/select/use-options.ts +25 -4
- package/src/components/tree-select/__test__/tree-select.test.ts +116 -0
- package/src/components/tree-select/style.scss +6 -0
- package/src/components/tree-select/tree-select.vue +63 -13
- package/src/types/dropdown.ts +10 -0
- package/src/types/multi-tree-select.ts +12 -1
- package/src/types/tree-select.ts +12 -1
|
@@ -51,6 +51,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
51
51
|
if (props.quickEdit && state.formActionType === "update") return false;
|
|
52
52
|
return creatable.value || updatable.value || creatableChild.value;
|
|
53
53
|
});
|
|
54
|
+
/** 弹框模式新增类操作显示「保存并继续」:保存后重置表单继续录入,不关闭弹框 */
|
|
55
|
+
const showSaveContinueBtn = computed(() => {
|
|
56
|
+
if (props.formMode !== "dialog") return false;
|
|
57
|
+
return creatable.value || creatableChild.value;
|
|
58
|
+
});
|
|
54
59
|
const headerInfo = computed(() => FORM_ACTION_HEADER_MAP[state.formActionType]);
|
|
55
60
|
/** 头部图标色调修饰类:样式中 createChild 对应的修饰类为 is-child */
|
|
56
61
|
const iconToneCls = computed(() => {
|
|
@@ -118,36 +123,45 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
118
123
|
_: 3
|
|
119
124
|
}, 8, ["class"]),
|
|
120
125
|
createElementVNode("footer", { class: normalizeClass(unref(cls).e("form-footer")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("form-hint")) }, [unref(props).readonly ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(" 只读模式 ")], 64)) : createCommentVNode("v-if", true), createElementVNode("span", { class: normalizeClass({ [unref(bem).is("concealed")]: !shortcutActive.value }) }, [
|
|
121
|
-
!(unref(state).formActionType === "update" && unref(props).quickEdit) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createVNode(unref(_sfc_main$4), null, {
|
|
122
|
-
default: withCtx(() => [..._cache[
|
|
126
|
+
unref(props).formMode === "panel" && !(unref(state).formActionType === "update" && unref(props).quickEdit) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createVNode(unref(_sfc_main$4), null, {
|
|
127
|
+
default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode("Ctrl + S", -1)])]),
|
|
123
128
|
_: 1
|
|
124
|
-
}), _cache[
|
|
129
|
+
}), _cache[3] || (_cache[3] = createTextVNode(" 保存 · ", -1))], 64)) : createCommentVNode("v-if", true),
|
|
125
130
|
createVNode(unref(_sfc_main$4), null, {
|
|
126
|
-
default: withCtx(() => [..._cache[
|
|
131
|
+
default: withCtx(() => [..._cache[4] || (_cache[4] = [createTextVNode("Esc", -1)])]),
|
|
127
132
|
_: 1
|
|
128
133
|
}),
|
|
129
|
-
_cache[
|
|
130
|
-
], 2)], 2), createElementVNode("div", { class: normalizeClass(unref(cls).e("form-actions")) }, [
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
"
|
|
150
|
-
|
|
134
|
+
_cache[5] || (_cache[5] = createTextVNode(" 关闭 ", -1))
|
|
135
|
+
], 2)], 2), createElementVNode("div", { class: normalizeClass(unref(cls).e("form-actions")) }, [
|
|
136
|
+
createVNode(unref(_sfc_main$3), {
|
|
137
|
+
size: "small",
|
|
138
|
+
text: "",
|
|
139
|
+
loading: unref(state).loading,
|
|
140
|
+
onClick: unref(handleClose)
|
|
141
|
+
}, {
|
|
142
|
+
default: withCtx(() => [..._cache[6] || (_cache[6] = [createTextVNode(" 取消 ", -1)])]),
|
|
143
|
+
_: 1
|
|
144
|
+
}, 8, ["loading", "onClick"]),
|
|
145
|
+
showSaveContinueBtn.value ? (openBlock(), createBlock(unref(_sfc_main$3), {
|
|
146
|
+
key: 0,
|
|
147
|
+
size: "small",
|
|
148
|
+
title: "保存并继续",
|
|
149
|
+
loading: unref(state).loading,
|
|
150
|
+
onClick: _cache[0] || (_cache[0] = ($event) => unref(handleSave)(true))
|
|
151
|
+
}, {
|
|
152
|
+
default: withCtx(() => [..._cache[7] || (_cache[7] = [createTextVNode(" 保存并继续 ", -1)])]),
|
|
153
|
+
_: 1
|
|
154
|
+
}, 8, ["loading"])) : createCommentVNode("v-if", true),
|
|
155
|
+
showSaveBtn.value ? (openBlock(), createBlock(unref(_sfc_main$3), {
|
|
156
|
+
key: 1,
|
|
157
|
+
type: "primary",
|
|
158
|
+
size: "small",
|
|
159
|
+
title: "保存",
|
|
160
|
+
icon: unref(Save),
|
|
161
|
+
loading: unref(state).loading,
|
|
162
|
+
onClick: _cache[1] || (_cache[1] = ($event) => unref(handleSave)())
|
|
163
|
+
}, null, 8, ["icon", "loading"])) : createCommentVNode("v-if", true)
|
|
164
|
+
], 2)], 2)
|
|
151
165
|
], 2)), [[vShow, unref(state).formVisible]]) : createCommentVNode("v-if", true)], 2112);
|
|
152
166
|
};
|
|
153
167
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch-edit-form.js","names":[],"sources":["../../../src/components/batch-edit/batch-edit-form.vue"],"sourcesContent":["<template>\n <!-- 面板模式:表单常驻挂载(快照只在挂载时对传入的 model 拍一次),仅用 v-show 控制显隐;\n 弹框模式:表单随弹框挂载 / 卸载,model 初始快照由 use-edit-state 维护 -->\n <aside\n :class=\"[cls.e('form'), bem.is('dialog', props.formMode === 'dialog')]\"\n v-if=\"!!props.model\"\n v-show=\"state.formVisible\"\n >\n <header :class=\"cls.e('form-header')\">\n <span :class=\"[cls.e('form-icon'), iconToneCls]\">\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')\">\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:update=\"handleFieldUpdate\"\n >\n <slot\n v-bind=\"{\n row: state.row,\n parentRow: state.parentRow,\n formActionType: state.formActionType,\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')]: !shortcutActive }\">\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'\nimport { useDialogEffect } from './use-dialog-effect'\n\ndefineOptions({ name: 'UBatchEditForm' })\n\nconst batchEditCtx = inject(BatchEditDIKey)!\n\nconst { cls, props, state, handleClose, handleSave, staticFeatures, dynamicFeatures, syncing } =\n batchEditCtx\n\nconst focused = toRef(batchEditCtx, 'focused')\n\n// 弹框模式下,弹框自身关闭(遮罩点击 / 右上角关闭)时同步走 handleClose,不保存\nuseDialogEffect({ closeForm: handleClose })\n\n/** 快捷键提示是否可见:面板模式随聚焦显隐;弹框模式弹框打开期间快捷键始终可用 */\nconst shortcutActive = computed(() => {\n return props.formMode === 'dialog' ? state.formVisible : focused.value\n})\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 handleFieldUpdate(field: string, value: any) {\n // 编程方式重置/回显期间不写回行数据,避免默认值污染源数据\n if (syncing.value) return\n\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 (\n staticFeatures.value.has('createChild') || dynamicFeatures.value.createChild?.(state.parentRow)\n )\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/** 头部图标色调修饰类:样式中 createChild 对应的修饰类为 is-child */\nconst iconToneCls = computed(() => {\n const type = state.formActionType\n return bem.is(type === 'createChild' ? 'child' : type)\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 <!-- 面板模式:表单常驻挂载(快照只在挂载时对传入的 model 拍一次),仅用 v-show 控制显隐;\n 弹框模式:表单随弹框挂载 / 卸载,model 初始快照由 use-edit-state 维护 -->\n <aside\n :class=\"[cls.e('form'), bem.is('dialog', props.formMode === 'dialog')]\"\n v-if=\"!!props.model\"\n v-show=\"state.formVisible\"\n >\n <header :class=\"cls.e('form-header')\">\n <span :class=\"[cls.e('form-icon'), iconToneCls]\">\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')\">\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:update=\"handleFieldUpdate\"\n >\n <slot\n v-bind=\"{\n row: state.row,\n parentRow: state.parentRow,\n formActionType: state.formActionType,\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')]: !shortcutActive }\">\n <template\n v-if=\"\n props.formMode === 'panel' && !(state.formActionType === 'update' && props.quickEdit)\n \"\n >\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=\"showSaveContinueBtn\"\n size=\"small\"\n title=\"保存并继续\"\n :loading=\"state.loading\"\n @click=\"handleSave(true)\"\n >\n 保存并继续\n </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'\nimport { useDialogEffect } from './use-dialog-effect'\n\ndefineOptions({ name: 'UBatchEditForm' })\n\nconst batchEditCtx = inject(BatchEditDIKey)!\n\nconst { cls, props, state, handleClose, handleSave, staticFeatures, dynamicFeatures, syncing } =\n batchEditCtx\n\nconst focused = toRef(batchEditCtx, 'focused')\n\n// 弹框模式下,弹框自身关闭(遮罩点击 / 右上角关闭)时同步走 handleClose,不保存\nuseDialogEffect({ closeForm: handleClose })\n\n/** 快捷键提示是否可见:面板模式随聚焦显隐;弹框模式弹框打开期间快捷键始终可用 */\nconst shortcutActive = computed(() => {\n return props.formMode === 'dialog' ? state.formVisible : focused.value\n})\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 handleFieldUpdate(field: string, value: any) {\n // 编程方式重置/回显期间不写回行数据,避免默认值污染源数据\n if (syncing.value) return\n\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 (\n staticFeatures.value.has('createChild') || dynamicFeatures.value.createChild?.(state.parentRow)\n )\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\n/** 弹框模式新增类操作显示「保存并继续」:保存后重置表单继续录入,不关闭弹框 */\nconst showSaveContinueBtn = computed(() => {\n if (props.formMode !== 'dialog') return false\n return creatable.value || creatableChild.value\n})\n\nconst headerInfo = computed(() => FORM_ACTION_HEADER_MAP[state.formActionType])\n\n/** 头部图标色调修饰类:样式中 createChild 对应的修饰类为 is-child */\nconst iconToneCls = computed(() => {\n const type = state.formActionType\n return bem.is(type === 'createChild' ? 'child' : type)\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;EA8GA,MAAM,eAAe,OAAO,cAAc;EAE1C,MAAM,EAAE,KAAK,OAAO,OAAO,aAAa,YAAY,gBAAgB,iBAAiB,YACnF;EAEF,MAAM,UAAU,MAAM,cAAc,SAAS;EAG7C,gBAAgB,EAAE,WAAW,YAAY,CAAC;;EAG1C,MAAM,iBAAiB,eAAe;GACpC,OAAO,MAAM,aAAa,WAAW,MAAM,cAAc,QAAQ;EACnE,CAAC;EAED,MAAM,mBAAmB,WAAwB;EAEjD,SAA2B;GACzB,IAAI,YAAY,iBAAiB,OAAO,EAAE;GAC1C,gBAAgB,iBAAiB,OAAO,SAAS,KAAK,QAAQ,QAAQ,IAAI;GAC1E,qBAAqB,iBAAiB,OAAO,cAAc;GAC3D,aAAa,iBAAiB,OAAO,MAAM;EAC7C,CAAC;EAED,SAAS,kBAAkB,OAAe,OAAY;GAEpD,IAAI,QAAQ,OAAO;GAEnB,IAAI,MAAM,OAAO,MAAM,WACrB,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC,IAAI,OAAO,KAAK;EAEtC;EAEA,MAAM,YAAY,eAAe;GAC/B,IAAI,MAAM,mBAAmB,UAAU,OAAO;GAC9C,OAAO,eAAe,MAAM,IAAI,QAAQ,KAAK,gBAAgB,MAAM,SAAS,MAAM,GAAG;EACvF,CAAC;EAED,MAAM,iBAAiB,eAAe;GACpC,IAAI,MAAM,mBAAmB,eAAe,OAAO;GACnD,OACE,eAAe,MAAM,IAAI,aAAa,KAAK,gBAAgB,MAAM,cAAc,MAAM,SAAS;EAElG,CAAC;EAED,MAAM,YAAY,eAAe;GAC/B,IAAI,MAAM,mBAAmB,UAAU,OAAO;GAC9C,OAAO,eAAe,MAAM,IAAI,QAAQ,KAAK,gBAAgB,MAAM,SAAS,MAAM,GAAG;EACvF,CAAC;EAED,MAAM,cAAc,eAAe;GACjC,IAAI,MAAM,UAAU,OAAO;GAC3B,IAAI,MAAM,aAAa,MAAM,mBAAmB,UAAU,OAAO;GACjE,OAAO,UAAU,SAAS,UAAU,SAAS,eAAe;EAC9D,CAAC;;EAGD,MAAM,sBAAsB,eAAe;GACzC,IAAI,MAAM,aAAa,UAAU,OAAO;GACxC,OAAO,UAAU,SAAS,eAAe;EAC3C,CAAC;EAED,MAAM,aAAa,eAAe,uBAAuB,MAAM,eAAe;;EAG9E,MAAM,cAAc,eAAe;GACjC,MAAM,OAAO,MAAM;GACnB,OAAO,IAAI,GAAG,SAAS,gBAAgB,UAAU,IAAI;EACvD,CAAC;;GAjLC,OAAA,UAAA,GAAA,mBAAA,UAAA,MAAA,CAAA,mBAAA,8GAAA,GAIU,CAAA,CAAA,MAAA,KAAA,CAAK,CAAC,QAFhB,gBAAA,UAAA,GAAA,mBAsFQ,SAAA;;IArFL,OAAK,eAAA,CAAG,MAAA,GAAA,CAAG,CAAC,EAAC,MAAA,GAAU,MAAA,GAAA,CAAG,CAAC,GAAE,UAAW,MAAA,KAAA,CAAK,CAAC,aAAQ,QAAA,CAAA,CAAA;;IAIvD,mBAkBS,UAAA,EAlBA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,aAAA,CAAA,EAAA,GAAA;KACnB,mBAIO,QAAA,EAJA,OAAK,eAAA,CAAG,MAAA,GAAA,CAAG,CAAC,EAAC,WAAA,GAAe,YAAA,KAAW,CAAA,EAAA,GAC5C,CAAA,YAES,MAAA,WAAA,GAAA,MAAA;MADP,SAAA,cAAmC,EAAnC,UAAA,GAAA,YAAmC,wBAAnB,WAAA,MAAW,IAAI,CAAA,EAAA,CAAA;;;KAGnC,mBAEM,OAAA,EAFA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,CAAA,EAAA,GAChB,CAAA,mBAAiE,MAAA,EAA5D,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,iBAAA,CAAA,EAAwB,GAAA,gBAAA,WAAA,MAAW,KAAK,GAAA,CAAA,CAAA,GAAA,CAAA;KAE3D,YAQE,MAAA,WAAA,GAAA;MAPC,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,CAAA;MACZ,MAAM,MAAA,KAAA;MACP,MAAA;MACA,QAAA;MACA,MAAK;MACL,OAAM;MACL,SAAO,MAAA,WAAA;;;;;;;IAIZ,YAqBW,MAAA,WAAA,GAAA;KArBD,QAAA;KAAQ,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,WAAA,CAAA;;KAC5B,SAAA,cAmBa,CAnBb,YAmBa,YAAA;MAnBD,MAAK;MAAO,QAAA;MAAO,MAAK;;MAClC,SAAA,cAiBS,CAjBT,YAiBS,MAAA,YAAA,GAAA;OAhBH,SAAA;OAAJ,KAAI;OACH,OAAO,MAAA,KAAA,CAAK,CAAC;OACb,UAAU,MAAA,KAAA,CAAK,CAAC;OAChB,eAAa,MAAA,KAAA,CAAK,CAAC;OACnB,kBAAc;;OAEf,SAAA,cASE,CATF,WASE,KAAA,QAAA,WAAA,eAAA,mBAAA;QAR6B,KAAA,MAAA,KAAA,CAAK,CAAC;QAA8B,WAAA,MAAA,KAAA,CAAK,CAAC;QAAyC,gBAAA,MAAA,KAAA,CAAK,CAAC;QAAqC,OAAA,MAAA,KAAA,CAAK,CAAC;QAA8B,SAAA,MAAA,KAAA,CAAK,CAAC;QAAgC,OAAA,MAAA,KAAA,CAAK,CAAC,KAAK;;;;;;;;;;;;IAaxP,mBAqCS,UAAA,EArCA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,aAAA,CAAA,EAAA,GACnB,CAAA,mBAaO,QAAA,EAbA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,WAAA,CAAA,EAAA,GACD,CAAA,MAAA,KAAA,CAAK,CAAC,YAAtB,UAAA,GAAA,mBAAiD,UAAA,EAAA,KAAA,EAAA,GAAA,CAAjB,gBAAA,QAAM,CAAA,GAAA,EAEtC,KAAA,mBAAA,QAAA,IAAA,GAAA,mBASO,QAAA,EATA,OAAK,eAAA,GAAK,MAAA,GAAA,CAAG,CAAC,GAAE,WAAA,IAAA,CAAiB,eAAA,MAAc,CAAA,EAAA,GAAA;KAE7B,MAAA,KAAA,CAAK,CAAC,aAAQ,WAAA,EAAkB,MAAA,KAAA,CAAK,CAAC,mBAAc,YAAiB,MAAA,KAAA,CAAK,CAAC,cADlG,UAAA,GAAA,mBAMW,UAAA,EAAA,KAAA,EAAA,GAAA,CADT,YAAuB,MAAA,WAAA,GAAA,MAAA;MAAhB,SAAA,cAAQ,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAR,gBAAA,YAAQ,EAAA,CAAA,EAAA,CAAA;;KAAQ,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,gBAAA,UACzB,EAAA,EAAA,GAAA,EAAA,KAAA,mBAAA,QAAA,IAAA;KACA,YAAkB,MAAA,WAAA,GAAA,MAAA;MAAX,SAAA,cAAG,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAH,gBAAA,OAAG,EAAA,CAAA,EAAA,CAAA;;;KAAQ,OAAA,OAAA,OAAA,KAAA,gBAAA,QACpB,EAAA;IAGF,GAAA,CAAA,CAAA,GAAA,CAAA,GAAA,mBAoBM,OAAA,EApBA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,cAAA,CAAA,EAAA,GAAA;KAChB,YAAyF,MAAA,WAAA,GAAA;MAA/E,MAAK;MAAQ,MAAA;MAAM,SAAS,MAAA,KAAA,CAAK,CAAC;MAAU,SAAO,MAAA,WAAA;;MAAa,SAAA,cAAI,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAJ,gBAAA,QAAI,EAAA,CAAA,EAAA,CAAA;;;KAEtE,oBAAA,SADR,UAAA,GAAA,YAQW,MAAA,WAAA,GAAA;;MANT,MAAK;MACL,OAAM;MACL,SAAS,MAAA,KAAA,CAAK,CAAC;MACf,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,UAAA,CAAU,CAAA,IAAA;;MACnB,SAAA,cAED,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAFC,gBAAA,WAED,EAAA,CAAA,EAAA,CAAA;;;KAEQ,YAAA,SADR,UAAA,GAAA,YAQE,MAAA,WAAA,GAAA;;MANA,MAAK;MACL,MAAK;MACL,OAAM;MACL,MAAM,MAAA,IAAA;MACN,SAAS,MAAA,KAAA,CAAK,CAAC;MACf,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,UAAA,CAAU,CAAA;;;GA/EhB,GAAA,CAAA,IAAA,CAAA,CAAA,OAAA,MAAA,KAAA,CAAK,CAAC,WAAW,CAAA,CAAA,IAAA,mBAAA,QAAA,IAAA,CAAA,GAAA,IAAA"}
|
|
@@ -97,8 +97,8 @@ function useHandlers(options) {
|
|
|
97
97
|
if (state.row) state.row.operating = false;
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
|
-
/**
|
|
101
|
-
const handleSave = runWithLoading(async () => {
|
|
100
|
+
/** 保存;keepOpen 为 true 时(「保存并继续」)新增保存后不关闭表单 */
|
|
101
|
+
const handleSave = runWithLoading(async (keepOpen = false) => {
|
|
102
102
|
const { model, saveMethod } = props;
|
|
103
103
|
const valid = await formRef.value?.validate();
|
|
104
104
|
if (!model || !valid) return;
|
|
@@ -110,7 +110,7 @@ function useHandlers(options) {
|
|
|
110
110
|
if (result) item = result;
|
|
111
111
|
}
|
|
112
112
|
insert(item);
|
|
113
|
-
if (props.formMode === "dialog") {
|
|
113
|
+
if (props.formMode === "dialog" && !keepOpen) {
|
|
114
114
|
resetState();
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-handlers.js","names":[],"sources":["../../../src/components/batch-edit/use-handlers.ts"],"sourcesContent":["import { copy, 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 copy(props.model ?? {})\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 emit('create')\n })\n }\n\n function handleInsertToPrev(row: TableRow) {\n resetState()\n state.formActionType = 'create'\n state.formVisible = true\n state.parentRow = row.parent\n state.depth = row.depth\n state.indexPath = [...row.indexes]\n emit('create-prev', row)\n }\n\n function handleInsertToNext(row: TableRow) {\n resetState()\n state.formActionType = 'create'\n state.formVisible = true\n state.parentRow = row.parent\n state.depth = row.depth\n state.indexPath = [...row.indexes.slice(0, -1), row.index + 1]\n emit('create-next', row)\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 emit('create-child', row)\n }\n\n function runWithLoading<Arg extends any[]>(fn: (...args: Arg) => Promise<void> | void) {\n return async (...args: Arg) => {\n // 重入保护:进行中(快捷键/按钮并发触发)直接忽略\n if (state.loading) return\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 /**
|
|
1
|
+
{"version":3,"file":"use-handlers.js","names":[],"sources":["../../../src/components/batch-edit/use-handlers.ts"],"sourcesContent":["import { copy, 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 /** @param keepOpen 新增类操作保存后不关闭表单(弹框模式「保存并继续」) */\n handleSave: (keepOpen?: boolean) => 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 copy(props.model ?? {})\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 emit('create')\n })\n }\n\n function handleInsertToPrev(row: TableRow) {\n resetState()\n state.formActionType = 'create'\n state.formVisible = true\n state.parentRow = row.parent\n state.depth = row.depth\n state.indexPath = [...row.indexes]\n emit('create-prev', row)\n }\n\n function handleInsertToNext(row: TableRow) {\n resetState()\n state.formActionType = 'create'\n state.formVisible = true\n state.parentRow = row.parent\n state.depth = row.depth\n state.indexPath = [...row.indexes.slice(0, -1), row.index + 1]\n emit('create-next', row)\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 emit('create-child', row)\n }\n\n function runWithLoading<Arg extends any[]>(fn: (...args: Arg) => Promise<void> | void) {\n return async (...args: Arg) => {\n // 重入保护:进行中(快捷键/按钮并发触发)直接忽略\n if (state.loading) return\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 /** 保存;keepOpen 为 true 时(「保存并继续」)新增保存后不关闭表单 */\n const handleSave = runWithLoading(async (keepOpen = false) => {\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 if (props.formMode === 'dialog' && !keepOpen) {\n resetState()\n return\n }\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 if (props.formMode === 'dialog') {\n resetState()\n }\n })\n\n const handleDelete = runWithLoading(async (row: TableRow) => {\n const { deleteMethod } = props\n\n if (deleteMethod) {\n const result = await deleteMethod([row.data])\n if (result === false) {\n return\n }\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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"}
|
|
@@ -24,15 +24,17 @@ function useShortcutKey(options) {
|
|
|
24
24
|
/**
|
|
25
25
|
* 键盘快捷键:
|
|
26
26
|
* - Esc 关闭表单
|
|
27
|
-
* - ⌘/Ctrl + S
|
|
27
|
+
* - ⌘/Ctrl + S 保存(仅面板模式;快速编辑模式下编辑行时除外)
|
|
28
28
|
*
|
|
29
29
|
* 面板模式仅组件获焦时生效;弹框模式表单被 teleport 到 body,
|
|
30
|
-
*
|
|
30
|
+
* 组件聚焦状态覆盖不到,改为弹框打开期间生效。
|
|
31
|
+
* 弹框模式不支持 ⌘/Ctrl + S 保存,保存只能走底部按钮
|
|
31
32
|
*/
|
|
32
33
|
function handleKeydown(e) {
|
|
33
34
|
if (!(props.formMode === "dialog" ? state.formVisible : focused.value)) return;
|
|
34
35
|
if (props.readonly && e.key !== "Escape") return;
|
|
35
36
|
if (e.key === "Escape") return runEscape(e);
|
|
37
|
+
if (props.formMode === "dialog") return;
|
|
36
38
|
if (!(e.metaKey || e.ctrlKey)) return;
|
|
37
39
|
if (e.key.toLowerCase() === "s") return runSave(e);
|
|
38
40
|
}
|
|
@@ -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 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
|
|
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 * 面板模式仅组件获焦时生效;弹框模式表单被 teleport 到 body,\n * 组件聚焦状态覆盖不到,改为弹框打开期间生效。\n * 弹框模式不支持 ⌘/Ctrl + S 保存,保存只能走底部按钮\n */\n function handleKeydown(e: KeyboardEvent) {\n const active = props.formMode === 'dialog' ? state.formVisible : focused.value\n if (!active) return\n if (props.readonly && e.key !== 'Escape') return\n\n if (e.key === 'Escape') return runEscape(e)\n\n if (props.formMode === 'dialog') return\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;;;;;;;;;;CAWA,SAAS,cAAc,GAAkB;EAEvC,IAAI,EADW,MAAM,aAAa,WAAW,MAAM,cAAc,QAAQ,QAC5D;EACb,IAAI,MAAM,YAAY,EAAE,QAAQ,UAAU;EAE1C,IAAI,EAAE,QAAQ,UAAU,OAAO,UAAU,CAAC;EAE1C,IAAI,MAAM,aAAa,UAAU;EAGjC,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"}
|
|
@@ -18,14 +18,12 @@
|
|
|
18
18
|
.u-card {
|
|
19
19
|
border-radius: var(--u-card-radius, var(--u-radius-large));
|
|
20
20
|
background-color: var(--u-bg-color-top);
|
|
21
|
-
border: var(--u-border-muted);
|
|
22
21
|
box-shadow: var(--u-shadow-sm);
|
|
23
22
|
backdrop-filter: var(--u-bg-filter);
|
|
24
23
|
-webkit-backdrop-filter: var(--u-bg-filter);
|
|
25
24
|
overflow: hidden;
|
|
26
25
|
}
|
|
27
26
|
.u-card.is-integrate {
|
|
28
|
-
border: none;
|
|
29
27
|
box-shadow: none;
|
|
30
28
|
--u-card-header-bg: transparent;
|
|
31
29
|
--u-card-action-bg: transparent;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import NodeRender from "../node-render/node-render.js";
|
|
2
2
|
import { useNest } from "../tip/use-nest.js";
|
|
3
|
-
import { Fragment, Teleport, cloneVNode, computed, createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, renderSlot, resolveDynamicComponent, shallowRef, unref, useAttrs, useSlots, useTemplateRef, withCtx, withDirectives } from "vue";
|
|
3
|
+
import { Fragment, Teleport, cloneVNode, computed, createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, renderSlot, resolveDynamicComponent, shallowRef, toRef, unref, useAttrs, useSlots, useTemplateRef, withCtx, withDirectives } from "vue";
|
|
4
4
|
import { useModel as useModel$1, usePop, useTransition } from "@veltra/compositions";
|
|
5
5
|
import { bem, extractNormalVNodes, setStyles, zIndex } from "@veltra/utils";
|
|
6
6
|
import { vClickOutside } from "@veltra/directives";
|
|
@@ -11,6 +11,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
11
11
|
__name: "dropdown",
|
|
12
12
|
props: {
|
|
13
13
|
trigger: { default: "hover" },
|
|
14
|
+
direction: { default: "bottom" },
|
|
15
|
+
alignment: { default: "start" },
|
|
14
16
|
width: {},
|
|
15
17
|
minWidth: {},
|
|
16
18
|
contentTag: { default: "div" },
|
|
@@ -86,8 +88,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
86
88
|
const { update, popperContainerId } = usePop({
|
|
87
89
|
triggerRef: triggerDom,
|
|
88
90
|
contentRef,
|
|
89
|
-
direction:
|
|
90
|
-
alignment:
|
|
91
|
+
direction: toRef(() => props.direction),
|
|
92
|
+
alignment: toRef(() => props.alignment),
|
|
91
93
|
onPop(position) {
|
|
92
94
|
transitionName.value = position.placement.includes("top") ? "slide-up" : "slide-down";
|
|
93
95
|
transition.enter();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown.js","names":[],"sources":["../../../src/components/dropdown/dropdown.vue"],"sourcesContent":["<template>\n <UNodeRender :content=\"renderTriggerNode()\" ref=\"trigger\" />\n\n <Teleport :to=\"`#${popperContainerId}`\">\n <component\n v-if=\"dropdownVisible\"\n ref=\"contentRef\"\n :is=\"contentTag\"\n :class=\"dropdownContentClass\"\n :style=\"[{ zIndex: zIndex() }, contentStyle]\"\n @mouseenter=\"eventsHandlers.onMouseenter\"\n @mouseleave=\"eventsHandlers.onMouseleave\"\n @keydown=\"emit('keydown', $event)\"\n v-click-outside=\"trigger === 'click' ? handleClickOutside : undefined\"\n >\n <slot name=\"content\" />\n </component>\n </Teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useModel, usePop, useTransition } from '@veltra/compositions'\nimport { vClickOutside } from '@veltra/directives'\nimport { bem, extractNormalVNodes, setStyles, zIndex } from '@veltra/utils'\nimport { shallowRef, computed, createVNode, cloneVNode, useTemplateRef, useAttrs } from 'vue'\n\nimport type { DropdownProps, DropdownExposed, DropdownEmits } from '../../types'\nimport { UNodeRender } from '../node-render'\nimport { useNest } from '../tip/use-nest'\n\ndefineOptions({ name: 'UDropdown', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<DropdownProps>(), {
|
|
1
|
+
{"version":3,"file":"dropdown.js","names":[],"sources":["../../../src/components/dropdown/dropdown.vue"],"sourcesContent":["<template>\n <UNodeRender :content=\"renderTriggerNode()\" ref=\"trigger\" />\n\n <Teleport :to=\"`#${popperContainerId}`\">\n <component\n v-if=\"dropdownVisible\"\n ref=\"contentRef\"\n :is=\"contentTag\"\n :class=\"dropdownContentClass\"\n :style=\"[{ zIndex: zIndex() }, contentStyle]\"\n @mouseenter=\"eventsHandlers.onMouseenter\"\n @mouseleave=\"eventsHandlers.onMouseleave\"\n @keydown=\"emit('keydown', $event)\"\n v-click-outside=\"trigger === 'click' ? handleClickOutside : undefined\"\n >\n <slot name=\"content\" />\n </component>\n </Teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useModel, usePop, useTransition } from '@veltra/compositions'\nimport { vClickOutside } from '@veltra/directives'\nimport { bem, extractNormalVNodes, setStyles, zIndex } from '@veltra/utils'\nimport { shallowRef, computed, createVNode, cloneVNode, useTemplateRef, useAttrs, toRef } from 'vue'\n\nimport type { DropdownProps, DropdownExposed, DropdownEmits } from '../../types'\nimport { UNodeRender } from '../node-render'\nimport { useNest } from '../tip/use-nest'\n\ndefineOptions({ name: 'UDropdown', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<DropdownProps>(), {\n trigger: 'hover',\n direction: 'bottom',\n alignment: 'start',\n contentTag: 'div'\n})\n\nconst emit = defineEmits<DropdownEmits>()\n\nconst slots = defineSlots<{\n /** 内容 */\n content?: () => any\n /** 触发器 */\n trigger?: () => any\n}>()\n\nconst cls = bem('dropdown')\n\nconst dropdownContentClass = computed(() => {\n let fixed = [cls.e('content')]\n const className = props.contentClass\n if (!className) return fixed\n if (!Array.isArray(className)) {\n return [...fixed, className]\n }\n return [...fixed, ...className]\n})\n\nconst triggerRef = useTemplateRef('trigger')\nconst customTriggerRef = shallowRef<HTMLElement>()\nconst contentRef = shallowRef<HTMLElement>()\n\nconst triggerDom = computed(() => {\n return customTriggerRef.value || triggerRef.value?.$el\n})\n\n/**显示隐藏 */\nconst visible = useModel({ defaultValue: false, propName: 'visible', props, emit })\n\nconst dropdownVisible = useNest(visible)\n\nlet closeTimer: number | undefined\n\nconst attrs = useAttrs()\nfunction renderTriggerNode() {\n const slotsNode = slots.trigger?.()\n if (!slotsNode) return null\n const nodes = extractNormalVNodes(slotsNode)\n const props = { class: cls.b, ...attrs, ...eventsHandlers.value }\n if (nodes.length === 1) return cloneVNode(nodes[0]!, props)\n\n return createVNode('div', props, nodes)\n}\n\nfunction stopClose() {\n closeTimer !== undefined && clearTimeout(closeTimer)\n}\n\n/** 打开下拉框 */\nfunction open(config?: { trigger?: HTMLElement }) {\n stopClose()\n const { trigger } = config || {}\n if (trigger && trigger instanceof HTMLElement) {\n customTriggerRef.value = trigger\n }\n\n visible.value = true\n}\n\nfunction close() {\n if (props.trigger === 'hover') {\n // 给200毫秒重新浮动的缓冲时间\n closeTimer = setTimeout(() => {\n transition.leave()\n }, 200)\n } else {\n transition.leave()\n }\n}\n\n/** 点击外部 */\nfunction handleClickOutside(e: MouseEvent) {\n const { trigger } = props\n // 当下拉框为点击时触发时,点击触发元素时不关闭\n // 其他任何时候点击外部都关闭下拉器\n if (trigger === 'click' && triggerDom.value.contains(e.target as Node)) {\n return\n }\n close()\n}\n\nconst transitionName = shallowRef('slide-down')\n\nconst { update, popperContainerId } = usePop({\n triggerRef: triggerDom,\n contentRef,\n direction: toRef(() => props.direction),\n alignment: toRef(() => props.alignment),\n onPop(position) {\n transitionName.value = position.placement.includes('top') ? 'slide-up' : 'slide-down'\n\n transition.enter()\n },\n\n onTriggerPositionChange() {\n close()\n },\n\n onBeforeUpdate(triggerEl, contentEl) {\n setStyles(contentEl, {\n width: props.width ?? `${triggerEl.offsetWidth}px`,\n minWidth: props.minWidth\n })\n }\n})\n\nconst transition = useTransition('css', {\n name: transitionName,\n target: contentRef,\n afterLeave() {\n visible.value = false\n },\n\n leaveCanceled() {\n visible.value = false\n }\n})\n\nfunction triggerClick() {\n if (visible.value) {\n close()\n } else {\n open()\n }\n}\n\nconst eventsHandlers = computed(() => {\n const { trigger, disabled } = props\n\n const handlers: Record<string, Function> = {}\n if (disabled || trigger === 'custom') return handlers\n\n if (trigger === 'click') {\n handlers.onClick = triggerClick\n } else if (trigger === 'hover') {\n handlers.onMouseenter = open\n handlers.onMouseleave = close\n }\n\n return handlers\n})\n\ndefineExpose<DropdownExposed>({\n open,\n close,\n /**暴露出更新dropdown内容位置方法 适用与级联选择器组件 */\n updateDropdown: update\n})\n</script>\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Fragment,
|
|
1
|
+
import { Fragment, createElementBlock, createElementVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, renderList, toDisplayString, unref } from "vue";
|
|
2
2
|
import { bem } from "@veltra/utils";
|
|
3
3
|
//#region src/components/expression-editor/components/path-preview.vue
|
|
4
4
|
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
@@ -12,11 +12,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
12
12
|
return openBlock(), createElementBlock("li", {
|
|
13
13
|
key: `${i}-${node.value || node.label}`,
|
|
14
14
|
class: normalizeClass([unref(cls).e("path-tree-item"), unref(bem).is("active", i === __props.path.length - 1)]),
|
|
15
|
-
style: normalizeStyle({
|
|
16
|
-
}, [
|
|
17
|
-
key: 0,
|
|
18
|
-
class: normalizeClass(unref(cls).e("path-tree-branch"))
|
|
19
|
-
}, "└", 2)) : createCommentVNode("v-if", true), createElementVNode("span", { class: normalizeClass(unref(cls).e("path-tree-label")) }, toDisplayString(node.label), 3)], 6);
|
|
15
|
+
style: normalizeStyle({ "--depth": i })
|
|
16
|
+
}, [createElementVNode("span", { class: normalizeClass(unref(cls).e("path-tree-label")) }, toDisplayString(node.label), 3)], 6);
|
|
20
17
|
}), 128))], 2)], 2);
|
|
21
18
|
};
|
|
22
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-preview.js","names":[],"sources":["../../../../src/components/expression-editor/components/path-preview.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('path-preview')\">\n <div :class=\"cls.e('path-preview-title')\">变量路径</div>\n <ul :class=\"cls.e('path-tree')\">\n <li\n v-for=\"(node, i) in path\"\n :key=\"`${i}-${node.value || node.label}`\"\n :class=\"[cls.e('path-tree-item'), bem.is('active', i === path.length - 1)]\"\n :style=\"{
|
|
1
|
+
{"version":3,"file":"path-preview.js","names":[],"sources":["../../../../src/components/expression-editor/components/path-preview.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('path-preview')\">\n <div :class=\"cls.e('path-preview-title')\">变量路径</div>\n <ul :class=\"cls.e('path-tree')\">\n <li\n v-for=\"(node, i) in path\"\n :key=\"`${i}-${node.value || node.label}`\"\n :class=\"[cls.e('path-tree-item'), bem.is('active', i === path.length - 1)]\"\n :style=\"{ '--depth': i }\"\n >\n <span :class=\"cls.e('path-tree-label')\">{{ node.label }}</span>\n </li>\n </ul>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\n\nimport type { VariableItem } from '../../../types'\n\ndefineOptions({ name: 'UExpressionPathPreview' })\n\ndefineProps<{ path: VariableItem[] }>()\n\nconst cls = bem('expression-editor')\n</script>\n"],"mappings":";;;;;;;;EAyBA,MAAM,MAAM,IAAI,mBAAmB;;GAxBjC,OAAA,UAAA,GAAA,mBAYM,OAAA,EAZA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,cAAA,CAAA,EAAA,GAChB,CAAA,mBAAoD,OAAA,EAA9C,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,oBAAA,CAAA,EAAwB,GAAA,QAAI,CAAA,GAC9C,mBASK,MAAA,EATA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,WAAA,CAAA,EAAA,GACf,EAAA,UAAA,IAAA,GAAA,mBAOK,UAAA,MAAA,WANiB,QAAA,OAAZ,MAAM,MAAC;IADjB,OAAA,UAAA,GAAA,mBAOK,MAAA;KALF,KAAG,GAAK,EAAC,GAAI,KAAK,SAAS,KAAK;KAChC,OAAK,eAAA,CAAG,MAAA,GAAA,CAAG,CAAC,EAAC,gBAAA,GAAoB,MAAA,GAAA,CAAG,CAAC,GAAE,UAAW,MAAM,QAAA,KAAK,SAAM,CAAA,CAAA,CAAA;KACnE,OAAK,eAAA,EAAA,WAAe,EAAC,CAAA;IAEtB,GAAA,CAAA,mBAA+D,QAAA,EAAxD,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,iBAAA,CAAA,EAAwB,GAAA,gBAAA,KAAK,KAAK,GAAA,CAAA,CAAA,GAAA,CAAA"}
|
|
@@ -3,7 +3,7 @@ import _sfc_main$2 from "../../empty/empty.js";
|
|
|
3
3
|
import _sfc_main$3 from "../../scroll/scroll.js";
|
|
4
4
|
import _sfc_main$4 from "../../tip/tip.js";
|
|
5
5
|
import _sfc_main$5 from "./path-preview.js";
|
|
6
|
-
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, nextTick, normalizeClass, openBlock, ref, renderList, toDisplayString, unref, watch, withCtx, withModifiers } from "vue";
|
|
6
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, nextTick, normalizeClass, openBlock, ref, renderList, toDisplayString, unref, useTemplateRef, watch, withCtx, withModifiers } from "vue";
|
|
7
7
|
import { bem } from "@veltra/utils";
|
|
8
8
|
import { ArrowRight } from "@veltra/icons/normal";
|
|
9
9
|
//#region src/components/expression-editor/components/variable-picker.vue
|
|
@@ -34,6 +34,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
34
34
|
const cls = bem("expression-editor");
|
|
35
35
|
const navPath = ref([]);
|
|
36
36
|
const activeIndex = ref(0);
|
|
37
|
+
const root = useTemplateRef("root");
|
|
38
|
+
const listScroll = useTemplateRef("listScroll");
|
|
37
39
|
const isFlat = computed(() => props.filter !== "");
|
|
38
40
|
function hasChildren(item) {
|
|
39
41
|
return !!item.children && item.children.length > 0;
|
|
@@ -107,12 +109,17 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
107
109
|
emit("select", item);
|
|
108
110
|
return;
|
|
109
111
|
}
|
|
110
|
-
|
|
111
|
-
activeIndex.value = 0;
|
|
112
|
+
expandInto(item);
|
|
112
113
|
return;
|
|
113
114
|
}
|
|
114
115
|
emit("select", item);
|
|
115
116
|
}
|
|
117
|
+
/** 进入 item 的下一级(点击展开图标 / leaf 模式点击分支项共用) */
|
|
118
|
+
function expandInto(item) {
|
|
119
|
+
if (!hasChildren(item)) return;
|
|
120
|
+
navPath.value = [...navPath.value, item];
|
|
121
|
+
activeIndex.value = 0;
|
|
122
|
+
}
|
|
116
123
|
function flattenSourceFor(it) {
|
|
117
124
|
const flat = it;
|
|
118
125
|
const out = {
|
|
@@ -134,10 +141,13 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
134
141
|
}
|
|
135
142
|
function scrollActiveIntoView() {
|
|
136
143
|
nextTick(() => {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
144
|
+
const item = root.value?.querySelector(`[data-idx="${activeIndex.value}"]`);
|
|
145
|
+
const scroller = listScroll.value?.containerRef;
|
|
146
|
+
if (!(item instanceof HTMLElement) || !(scroller instanceof HTMLElement)) return;
|
|
147
|
+
const top = item.getBoundingClientRect().top - scroller.getBoundingClientRect().top + scroller.scrollTop;
|
|
148
|
+
const bottom = top + item.offsetHeight;
|
|
149
|
+
if (top < scroller.scrollTop) scroller.scrollTop = top;
|
|
150
|
+
else if (bottom > scroller.scrollTop + scroller.clientHeight) scroller.scrollTop = bottom - scroller.clientHeight;
|
|
141
151
|
});
|
|
142
152
|
}
|
|
143
153
|
function handleVisibleChange(v) {
|
|
@@ -185,8 +195,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
185
195
|
if (isFlat.value) return false;
|
|
186
196
|
const item = currentList.value[activeIndex.value];
|
|
187
197
|
if (item && hasChildren(item)) {
|
|
188
|
-
|
|
189
|
-
activeIndex.value = 0;
|
|
198
|
+
expandInto(item);
|
|
190
199
|
return true;
|
|
191
200
|
}
|
|
192
201
|
return false;
|
|
@@ -209,6 +218,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
209
218
|
"onUpdate:visible": _cache[2] || (_cache[2] = ($event) => handleVisibleChange($event))
|
|
210
219
|
}, {
|
|
211
220
|
content: withCtx(() => [createElementVNode("div", {
|
|
221
|
+
ref_key: "root",
|
|
222
|
+
ref: root,
|
|
212
223
|
class: normalizeClass(unref(cls).e("picker")),
|
|
213
224
|
onMousedown: _cache[1] || (_cache[1] = withModifiers(() => {}, ["prevent"]))
|
|
214
225
|
}, [
|
|
@@ -241,6 +252,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
241
252
|
key: 0,
|
|
242
253
|
class: normalizeClass(unref(cls).e("panel"))
|
|
243
254
|
}, [createVNode(unref(_sfc_main$3), {
|
|
255
|
+
ref_key: "listScroll",
|
|
256
|
+
ref: listScroll,
|
|
244
257
|
tag: "ul",
|
|
245
258
|
class: normalizeClass(unref(cls).e("panel-list")),
|
|
246
259
|
"content-class": unref(cls).e("panel-content")
|
|
@@ -254,11 +267,13 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
254
267
|
onMouseenter: ($event) => activeIndex.value = idx
|
|
255
268
|
}, [createElementVNode("span", { class: normalizeClass(unref(cls).e("item-label")) }, toDisplayString(item.label), 3), !isFlat.value && hasChildren(item) ? (openBlock(), createBlock(unref(_sfc_main$1), {
|
|
256
269
|
key: 0,
|
|
257
|
-
class: normalizeClass(unref(cls).e("item-expand"))
|
|
270
|
+
class: normalizeClass(unref(cls).e("item-expand")),
|
|
271
|
+
title: "展开下一级",
|
|
272
|
+
onClick: withModifiers(($event) => expandInto(item), ["stop"])
|
|
258
273
|
}, {
|
|
259
274
|
default: withCtx(() => [createVNode(unref(ArrowRight))]),
|
|
260
275
|
_: 1
|
|
261
|
-
}, 8, ["class"])) : createCommentVNode("v-if", true)], 42, _hoisted_2);
|
|
276
|
+
}, 8, ["class", "onClick"])) : createCommentVNode("v-if", true)], 42, _hoisted_2);
|
|
262
277
|
}), 128))]),
|
|
263
278
|
_: 1
|
|
264
279
|
}, 8, ["class", "content-class"])], 2)) : (openBlock(), createElementBlock("div", {
|
|
@@ -267,7 +282,29 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
267
282
|
}, [createVNode(unref(_sfc_main$2), { description: "暂无可用变量" })], 2)), isFlat.value && activePath.value.length > 0 ? (openBlock(), createBlock(_sfc_main$5, {
|
|
268
283
|
key: 2,
|
|
269
284
|
path: activePath.value
|
|
270
|
-
}, null, 8, ["path"])) : createCommentVNode("v-if", true)], 2)
|
|
285
|
+
}, null, 8, ["path"])) : createCommentVNode("v-if", true)], 2),
|
|
286
|
+
currentList.value.length > 0 ? (openBlock(), createElementBlock("div", {
|
|
287
|
+
key: 1,
|
|
288
|
+
class: normalizeClass(unref(cls).e("picker-hint"))
|
|
289
|
+
}, [isFlat.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
290
|
+
_cache[3] || (_cache[3] = createElementVNode("span", null, [
|
|
291
|
+
createElementVNode("kbd", null, "↑"),
|
|
292
|
+
createElementVNode("kbd", null, "↓"),
|
|
293
|
+
createTextVNode(" 选择")
|
|
294
|
+
], -1)),
|
|
295
|
+
_cache[4] || (_cache[4] = createElementVNode("span", null, [createElementVNode("kbd", null, "Enter"), createTextVNode(" 确认")], -1)),
|
|
296
|
+
_cache[5] || (_cache[5] = createElementVNode("span", null, [createElementVNode("kbd", null, "Esc"), createTextVNode(" 关闭")], -1))
|
|
297
|
+
], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
298
|
+
_cache[6] || (_cache[6] = createElementVNode("span", null, [
|
|
299
|
+
createElementVNode("kbd", null, "↑"),
|
|
300
|
+
createElementVNode("kbd", null, "↓"),
|
|
301
|
+
createTextVNode(" 选择")
|
|
302
|
+
], -1)),
|
|
303
|
+
_cache[7] || (_cache[7] = createElementVNode("span", null, [createElementVNode("kbd", null, "→"), createTextVNode(" 展开")], -1)),
|
|
304
|
+
_cache[8] || (_cache[8] = createElementVNode("span", null, [createElementVNode("kbd", null, "←"), createTextVNode(" 返回")], -1)),
|
|
305
|
+
_cache[9] || (_cache[9] = createElementVNode("span", null, [createElementVNode("kbd", null, "Enter"), createTextVNode(" 确认")], -1)),
|
|
306
|
+
_cache[10] || (_cache[10] = createElementVNode("span", null, [createElementVNode("kbd", null, "Esc"), createTextVNode(" 关闭")], -1))
|
|
307
|
+
], 64))], 2)) : createCommentVNode("v-if", true)
|
|
271
308
|
], 34)]),
|
|
272
309
|
_: 1
|
|
273
310
|
}, 8, ["trigger-dom", "visible"]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variable-picker.js","names":[],"sources":["../../../../src/components/expression-editor/components/variable-picker.vue"],"sourcesContent":["<template>\n <u-tip\n hide-arrow\n alignment=\"start\"\n direction=\"bottom\"\n trigger=\"click\"\n :trigger-dom=\"triggerDom\"\n :visible=\"visible\"\n @update:visible=\"handleVisibleChange($event)\"\n >\n <template #content>\n <div :class=\"cls.e('picker')\" @mousedown.prevent>\n <!-- 面包屑:仅逐级模式且非根级显示 -->\n <div v-if=\"!isFlat && navPath.length > 0\" :class=\"cls.e('breadcrumbs')\">\n <span :class=\"cls.e('breadcrumb-item')\">\n <span :class=\"cls.e('breadcrumb-link')\" @click=\"navigateTo(0)\">全部变量</span>\n <u-icon :class=\"cls.e('breadcrumb-separator')\">\n <ArrowRight />\n </u-icon>\n </span>\n <span\n v-for=\"(crumb, i) in navPath\"\n :key=\"`${i}-${crumb.value}`\"\n :class=\"cls.e('breadcrumb-item')\"\n >\n <span\n :class=\"[\n cls.e('breadcrumb-link'),\n { [cls.em('breadcrumb-link', 'active')]: i === navPath.length - 1 }\n ]\"\n @click=\"navigateTo(i + 1)\"\n >{{ crumb.label }}</span\n >\n <u-icon v-if=\"i < navPath.length - 1\" :class=\"cls.e('breadcrumb-separator')\">\n <ArrowRight />\n </u-icon>\n </span>\n </div>\n\n <div :class=\"cls.e('picker-body')\">\n <div :class=\"cls.e('panel')\" v-if=\"currentList.length > 0\">\n <u-scroll tag=\"ul\" :class=\"cls.e('panel-list')\" :content-class=\"cls.e('panel-content')\">\n <li\n v-for=\"(item, idx) in currentList\"\n :key=\"`${idx}-${item.value}`\"\n :data-idx=\"idx\"\n :class=\"getItemCls(item, idx)\"\n @click=\"handleItemClick(item, idx)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <span :class=\"cls.e('item-label')\">{{ item.label }}</span>\n <u-icon v-if=\"!isFlat && hasChildren(item)\" :class=\"cls.e('item-expand')\">\n <ArrowRight />\n </u-icon>\n </li>\n </u-scroll>\n </div>\n <div v-else :class=\"cls.e('empty')\">\n <UEmpty description=\"暂无可用变量\" />\n </div>\n\n <PathPreview v-if=\"isFlat && activePath.length > 0\" :path=\"activePath\" />\n </div>\n </div>\n </template>\n </u-tip>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ArrowRight } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, nextTick, ref, watch } from 'vue'\n\nimport type { VariableItem } from '../../../types'\nimport { UEmpty } from '../../empty'\nimport { UIcon } from '../../icon'\nimport { UScroll } from '../../scroll'\nimport { UTip } from '../../tip'\nimport PathPreview from './path-preview.vue'\n\ndefineOptions({ name: 'UVariablePicker' })\n\ninterface FlatItem extends VariableItem {\n /** 仅扁平模式使用:从根到当前节点的路径 */\n __path: VariableItem[]\n}\n\nconst props = defineProps<{\n visible: boolean\n triggerDom?: HTMLElement\n /** 顶层变量树 */\n variables?: VariableItem[]\n /** 来自编辑器 mention 状态的过滤字符串。空 = 逐级模式;非空 = 扁平模式。 */\n filter: string\n /** 是否允许选中分支节点 */\n selectableLevels: 'leaf' | 'any'\n}>()\n\nconst emit = defineEmits<{\n (e: 'select', item: VariableItem): void\n (e: 'update:visible', visible: boolean): void\n /** 用户主动取消(按 Esc)。编辑器据此把 mention 状态置为 dismissed。 */\n (e: 'dismiss'): void\n}>()\n\nconst cls = bem('expression-editor')\n\nconst navPath = ref<VariableItem[]>([])\nconst activeIndex = ref(0)\n\nconst isFlat = computed(() => props.filter !== '')\n\nfunction hasChildren(item: VariableItem): boolean {\n return !!item.children && item.children.length > 0\n}\n\nfunction isLeaf(item: VariableItem): boolean {\n return !hasChildren(item)\n}\n\n/** 当前层级展示的列表(来源依模式而定) */\nconst currentList = computed<VariableItem[]>(() => {\n const variables = props.variables ?? []\n if (isFlat.value) return flatList.value\n let level: VariableItem[] = variables\n for (const node of navPath.value) {\n const found = level.find((it) => it.value === node.value)\n if (!found || !found.children) break\n level = found.children\n }\n return level\n})\n\n/** 扁平模式下:根据 selectableLevels 拍平 + 过滤 */\nconst flatList = computed<FlatItem[]>(() => {\n const variables = props.variables ?? []\n const out: FlatItem[] = []\n const includeBranches = props.selectableLevels === 'any'\n function walk(items: VariableItem[], path: VariableItem[]) {\n for (const it of items) {\n const nextPath = [...path, it]\n if (isLeaf(it)) {\n out.push({ ...it, __path: nextPath })\n } else {\n if (includeBranches) out.push({ ...it, __path: nextPath })\n walk(it.children!, nextPath)\n }\n }\n }\n walk(variables, [])\n const q = props.filter.toLowerCase()\n return out.filter((it) => it.label.toLowerCase().includes(q))\n})\n\n/** 当前 active 项;扁平模式下用于驱动路径预览 */\nconst activePath = computed<VariableItem[]>(() => {\n if (!isFlat.value) return []\n const item = flatList.value[activeIndex.value] as FlatItem | undefined\n return item ? item.__path : []\n})\n\nfunction getItemCls(_item: VariableItem, idx: number) {\n return [cls.e('item'), bem.is('active', idx === activeIndex.value)]\n}\n\nfunction navigateTo(i: number) {\n // 0 = 全部变量;i 之后表示进入到 navPath[i-1]\n navPath.value = navPath.value.slice(0, i)\n activeIndex.value = 0\n}\n\nfunction handleItemClick(item: VariableItem, idx: number) {\n activeIndex.value = idx\n selectActive()\n}\n\nfunction selectActive() {\n const item = currentList.value[activeIndex.value]\n if (!item) return\n\n if (isFlat.value) {\n // 扁平模式始终是「选中并替换」\n emit('select', flattenSourceFor(item))\n return\n }\n\n // 逐级模式\n if (hasChildren(item)) {\n if (props.selectableLevels === 'any') {\n // Enter 在分支项上:选中分支本身\n emit('select', item)\n return\n }\n // 默认 'leaf':进入下一级\n navPath.value = [...navPath.value, item]\n activeIndex.value = 0\n return\n }\n emit('select', item)\n}\n\nfunction flattenSourceFor(it: VariableItem): VariableItem {\n const flat = it as FlatItem\n // 还原为「不带 __path 的纯 VariableItem」,避免泄漏内部字段\n const out: VariableItem = { value: flat.value, label: flat.label }\n if (flat.type) out.type = flat.type\n if (flat.children) out.children = flat.children\n return out\n}\n\nfunction moveActive(delta: -1 | 1) {\n const max = currentList.value.length - 1\n if (max < 0) return\n let next = activeIndex.value + delta\n if (next < 0) next = 0\n if (next > max) next = max\n activeIndex.value = next\n scrollActiveIntoView()\n}\n\nfunction scrollActiveIntoView() {\n void nextTick(() => {\n const list = document.querySelector(`.${cls.e('panel-list')}`)\n if (!list) return\n const item = list.querySelector(`[data-idx=\"${activeIndex.value}\"]`)\n if (item && 'scrollIntoView' in item) {\n ;(item as HTMLElement).scrollIntoView({ block: 'nearest' })\n }\n })\n}\n\nfunction handleVisibleChange(v: boolean) {\n emit('update:visible', v)\n if (!v) reset()\n}\n\nfunction reset() {\n navPath.value = []\n activeIndex.value = 0\n}\n\nwatch(\n () => props.visible,\n (v) => {\n if (!v) reset()\n else activeIndex.value = 0\n }\n)\n\nwatch(\n () => props.filter,\n () => {\n // 过滤词变化:重置 active;逐级 → 扁平时清空 navPath\n activeIndex.value = 0\n if (isFlat.value) navPath.value = []\n }\n)\n\n/**\n * 由父级 forward 进来的键盘事件。\n * 返回 true 表示已处理(外部应 preventDefault)。\n */\nfunction handleKeydown(e: KeyboardEvent): boolean {\n if (!props.visible) return false\n\n switch (e.key) {\n case 'Escape':\n emit('dismiss')\n emit('update:visible', false)\n reset()\n return true\n case 'ArrowDown':\n moveActive(1)\n return true\n case 'ArrowUp':\n moveActive(-1)\n return true\n case 'ArrowLeft':\n // 仅逐级模式:返回上一级;扁平模式下不拦截,让光标正常移动并触发 mention dismiss\n if (!isFlat.value && navPath.value.length > 0) {\n navPath.value = navPath.value.slice(0, -1)\n activeIndex.value = 0\n return true\n }\n return false\n case 'ArrowRight': {\n if (isFlat.value) return false\n const item = currentList.value[activeIndex.value]\n if (item && hasChildren(item)) {\n navPath.value = [...navPath.value, item]\n activeIndex.value = 0\n return true\n }\n return false\n }\n case 'Enter':\n selectActive()\n return true\n default:\n return false\n }\n}\n\ndefineExpose({ handleKeydown })\n</script>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"variable-picker.js","names":[],"sources":["../../../../src/components/expression-editor/components/variable-picker.vue"],"sourcesContent":["<template>\n <u-tip\n hide-arrow\n alignment=\"start\"\n direction=\"bottom\"\n trigger=\"click\"\n :trigger-dom=\"triggerDom\"\n :visible=\"visible\"\n @update:visible=\"handleVisibleChange($event)\"\n >\n <template #content>\n <div ref=\"root\" :class=\"cls.e('picker')\" @mousedown.prevent>\n <!-- 面包屑:仅逐级模式且非根级显示 -->\n <div v-if=\"!isFlat && navPath.length > 0\" :class=\"cls.e('breadcrumbs')\">\n <span :class=\"cls.e('breadcrumb-item')\">\n <span :class=\"cls.e('breadcrumb-link')\" @click=\"navigateTo(0)\">全部变量</span>\n <u-icon :class=\"cls.e('breadcrumb-separator')\">\n <ArrowRight />\n </u-icon>\n </span>\n <span\n v-for=\"(crumb, i) in navPath\"\n :key=\"`${i}-${crumb.value}`\"\n :class=\"cls.e('breadcrumb-item')\"\n >\n <span\n :class=\"[\n cls.e('breadcrumb-link'),\n { [cls.em('breadcrumb-link', 'active')]: i === navPath.length - 1 }\n ]\"\n @click=\"navigateTo(i + 1)\"\n >{{ crumb.label }}</span\n >\n <u-icon v-if=\"i < navPath.length - 1\" :class=\"cls.e('breadcrumb-separator')\">\n <ArrowRight />\n </u-icon>\n </span>\n </div>\n\n <div :class=\"cls.e('picker-body')\">\n <div :class=\"cls.e('panel')\" v-if=\"currentList.length > 0\">\n <u-scroll\n ref=\"listScroll\"\n tag=\"ul\"\n :class=\"cls.e('panel-list')\"\n :content-class=\"cls.e('panel-content')\"\n >\n <li\n v-for=\"(item, idx) in currentList\"\n :key=\"`${idx}-${item.value}`\"\n :data-idx=\"idx\"\n :class=\"getItemCls(item, idx)\"\n @click=\"handleItemClick(item, idx)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <span :class=\"cls.e('item-label')\">{{ item.label }}</span>\n <u-icon\n v-if=\"!isFlat && hasChildren(item)\"\n :class=\"cls.e('item-expand')\"\n title=\"展开下一级\"\n @click.stop=\"expandInto(item)\"\n >\n <ArrowRight />\n </u-icon>\n </li>\n </u-scroll>\n </div>\n <div v-else :class=\"cls.e('empty')\">\n <UEmpty description=\"暂无可用变量\" />\n </div>\n\n <PathPreview v-if=\"isFlat && activePath.length > 0\" :path=\"activePath\" />\n </div>\n\n <div v-if=\"currentList.length > 0\" :class=\"cls.e('picker-hint')\">\n <template v-if=\"isFlat\">\n <span><kbd>↑</kbd><kbd>↓</kbd> 选择</span>\n <span><kbd>Enter</kbd> 确认</span>\n <span><kbd>Esc</kbd> 关闭</span>\n </template>\n <template v-else>\n <span><kbd>↑</kbd><kbd>↓</kbd> 选择</span>\n <span><kbd>→</kbd> 展开</span>\n <span><kbd>←</kbd> 返回</span>\n <span><kbd>Enter</kbd> 确认</span>\n <span><kbd>Esc</kbd> 关闭</span>\n </template>\n </div>\n </div>\n </template>\n </u-tip>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ArrowRight } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, nextTick, ref, useTemplateRef, watch } from 'vue'\n\nimport type { VariableItem } from '../../../types'\nimport { UEmpty } from '../../empty'\nimport { UIcon } from '../../icon'\nimport { UScroll } from '../../scroll'\nimport { UTip } from '../../tip'\nimport PathPreview from './path-preview.vue'\n\ndefineOptions({ name: 'UVariablePicker' })\n\ninterface FlatItem extends VariableItem {\n /** 仅扁平模式使用:从根到当前节点的路径 */\n __path: VariableItem[]\n}\n\nconst props = defineProps<{\n visible: boolean\n triggerDom?: HTMLElement\n /** 顶层变量树 */\n variables?: VariableItem[]\n /** 来自编辑器 mention 状态的过滤字符串。空 = 逐级模式;非空 = 扁平模式。 */\n filter: string\n /** 是否允许选中分支节点 */\n selectableLevels: 'leaf' | 'any'\n}>()\n\nconst emit = defineEmits<{\n (e: 'select', item: VariableItem): void\n (e: 'update:visible', visible: boolean): void\n /** 用户主动取消(按 Esc)。编辑器据此把 mention 状态置为 dismissed。 */\n (e: 'dismiss'): void\n}>()\n\nconst cls = bem('expression-editor')\n\nconst navPath = ref<VariableItem[]>([])\nconst activeIndex = ref(0)\nconst root = useTemplateRef('root')\nconst listScroll = useTemplateRef<InstanceType<typeof UScroll>>('listScroll')\n\nconst isFlat = computed(() => props.filter !== '')\n\nfunction hasChildren(item: VariableItem): boolean {\n return !!item.children && item.children.length > 0\n}\n\nfunction isLeaf(item: VariableItem): boolean {\n return !hasChildren(item)\n}\n\n/** 当前层级展示的列表(来源依模式而定) */\nconst currentList = computed<VariableItem[]>(() => {\n const variables = props.variables ?? []\n if (isFlat.value) return flatList.value\n let level: VariableItem[] = variables\n for (const node of navPath.value) {\n const found = level.find((it) => it.value === node.value)\n if (!found || !found.children) break\n level = found.children\n }\n return level\n})\n\n/** 扁平模式下:根据 selectableLevels 拍平 + 过滤 */\nconst flatList = computed<FlatItem[]>(() => {\n const variables = props.variables ?? []\n const out: FlatItem[] = []\n const includeBranches = props.selectableLevels === 'any'\n function walk(items: VariableItem[], path: VariableItem[]) {\n for (const it of items) {\n const nextPath = [...path, it]\n if (isLeaf(it)) {\n out.push({ ...it, __path: nextPath })\n } else {\n if (includeBranches) out.push({ ...it, __path: nextPath })\n walk(it.children!, nextPath)\n }\n }\n }\n walk(variables, [])\n const q = props.filter.toLowerCase()\n return out.filter((it) => it.label.toLowerCase().includes(q))\n})\n\n/** 当前 active 项;扁平模式下用于驱动路径预览 */\nconst activePath = computed<VariableItem[]>(() => {\n if (!isFlat.value) return []\n const item = flatList.value[activeIndex.value] as FlatItem | undefined\n return item ? item.__path : []\n})\n\nfunction getItemCls(_item: VariableItem, idx: number) {\n return [cls.e('item'), bem.is('active', idx === activeIndex.value)]\n}\n\nfunction navigateTo(i: number) {\n // 0 = 全部变量;i 之后表示进入到 navPath[i-1]\n navPath.value = navPath.value.slice(0, i)\n activeIndex.value = 0\n}\n\nfunction handleItemClick(item: VariableItem, idx: number) {\n activeIndex.value = idx\n selectActive()\n}\n\nfunction selectActive() {\n const item = currentList.value[activeIndex.value]\n if (!item) return\n\n if (isFlat.value) {\n // 扁平模式始终是「选中并替换」\n emit('select', flattenSourceFor(item))\n return\n }\n\n // 逐级模式\n if (hasChildren(item)) {\n if (props.selectableLevels === 'any') {\n // Enter 在分支项上:选中分支本身\n emit('select', item)\n return\n }\n // 默认 'leaf':进入下一级\n expandInto(item)\n return\n }\n emit('select', item)\n}\n\n/** 进入 item 的下一级(点击展开图标 / leaf 模式点击分支项共用) */\nfunction expandInto(item: VariableItem) {\n if (!hasChildren(item)) return\n navPath.value = [...navPath.value, item]\n activeIndex.value = 0\n}\n\nfunction flattenSourceFor(it: VariableItem): VariableItem {\n const flat = it as FlatItem\n // 还原为「不带 __path 的纯 VariableItem」,避免泄漏内部字段\n const out: VariableItem = { value: flat.value, label: flat.label }\n if (flat.type) out.type = flat.type\n if (flat.children) out.children = flat.children\n return out\n}\n\nfunction moveActive(delta: -1 | 1) {\n const max = currentList.value.length - 1\n if (max < 0) return\n let next = activeIndex.value + delta\n if (next < 0) next = 0\n if (next > max) next = max\n activeIndex.value = next\n scrollActiveIntoView()\n}\n\nfunction scrollActiveIntoView() {\n void nextTick(() => {\n // 在面板根节点内查找,避免同页多个编辑器时滚错列表\n const item = root.value?.querySelector(`[data-idx=\"${activeIndex.value}\"]`)\n // expose 代理运行时会解包 ref,类型上按 unknown 收窄\n const scroller = listScroll.value?.containerRef as unknown\n if (!(item instanceof HTMLElement) || !(scroller instanceof HTMLElement)) return\n // 只滚面板列表自身:原生 scrollIntoView 会沿所有滚动祖先链滚动,\n // 面板贴近视口边缘时会连带滚动页面,导致面板与触发器错位抖动\n const top =\n item.getBoundingClientRect().top - scroller.getBoundingClientRect().top + scroller.scrollTop\n const bottom = top + item.offsetHeight\n if (top < scroller.scrollTop) {\n scroller.scrollTop = top\n } else if (bottom > scroller.scrollTop + scroller.clientHeight) {\n scroller.scrollTop = bottom - scroller.clientHeight\n }\n })\n}\n\nfunction handleVisibleChange(v: boolean) {\n emit('update:visible', v)\n if (!v) reset()\n}\n\nfunction reset() {\n navPath.value = []\n activeIndex.value = 0\n}\n\nwatch(\n () => props.visible,\n (v) => {\n if (!v) reset()\n else activeIndex.value = 0\n }\n)\n\nwatch(\n () => props.filter,\n () => {\n // 过滤词变化:重置 active;逐级 → 扁平时清空 navPath\n activeIndex.value = 0\n if (isFlat.value) navPath.value = []\n }\n)\n\n/**\n * 由父级 forward 进来的键盘事件。\n * 返回 true 表示已处理(外部应 preventDefault)。\n */\nfunction handleKeydown(e: KeyboardEvent): boolean {\n if (!props.visible) return false\n\n switch (e.key) {\n case 'Escape':\n emit('dismiss')\n emit('update:visible', false)\n reset()\n return true\n case 'ArrowDown':\n moveActive(1)\n return true\n case 'ArrowUp':\n moveActive(-1)\n return true\n case 'ArrowLeft':\n // 仅逐级模式:返回上一级;扁平模式下不拦截,让光标正常移动并触发 mention dismiss\n if (!isFlat.value && navPath.value.length > 0) {\n navPath.value = navPath.value.slice(0, -1)\n activeIndex.value = 0\n return true\n }\n return false\n case 'ArrowRight': {\n if (isFlat.value) return false\n const item = currentList.value[activeIndex.value]\n if (item && hasChildren(item)) {\n expandInto(item)\n return true\n }\n return false\n }\n case 'Enter':\n selectActive()\n return true\n default:\n return false\n }\n}\n\ndefineExpose({ handleKeydown })\n</script>\n"],"mappings":"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"}
|