@veltra/sheet 2.0.0 → 2.1.1
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/index.d.ts +4 -25
- package/dist/index.js +3 -22
- package/dist/tools/builtin.js +279 -8
- package/dist/tools/context.d.ts +54 -11
- package/dist/tools/context.js +28 -5
- package/dist/tools/download.js +68 -0
- package/dist/tools/export.worker.d.ts +16 -0
- package/dist/tools/export.worker.js +29 -0
- package/dist/tools/registry.d.ts +7 -4
- package/dist/tools/registry.js +1 -3
- package/dist/types/index.d.ts +1 -0
- package/dist/types/sheet.d.ts +6 -5
- package/dist/vue/formula-bar.js +393 -0
- package/dist/vue/formula-suggest-list.js +66 -0
- package/dist/vue/import-file.js +140 -0
- package/dist/vue/insert-count-menu-item.js +68 -0
- package/dist/vue/insert-image.js +79 -0
- package/dist/vue/popup-helpers.js +73 -0
- package/dist/vue/popups/border-popup.js +75 -0
- package/dist/vue/popups/border-preset-glyph.js +106 -0
- package/dist/vue/popups/export-popup.js +51 -0
- package/dist/vue/popups/fill-color-popup.js +37 -0
- package/dist/vue/popups/find-popup.js +113 -0
- package/dist/vue/popups/font-color-popup.js +37 -0
- package/dist/vue/popups/font-size-popup.js +63 -0
- package/dist/vue/popups/import.worker.d.ts +21 -0
- package/dist/vue/popups/import.worker.js +41 -0
- package/dist/vue/popups/insert-image-popup.js +38 -0
- package/dist/vue/sheet-context-menu.js +200 -0
- package/dist/vue/sheet-tabs.js +162 -0
- package/dist/vue/sheet-toolbar.js +106 -0
- package/dist/vue/sheet.js +144 -138
- package/dist/vue/style.d.ts +11 -1
- package/dist/vue/style.js +11 -0
- package/dist/vue/style2.css +564 -3
- package/dist/vue/use-find-replace.js +161 -0
- package/dist/vue/use-formula-ref-select.js +37 -0
- package/dist/vue/use-formula-suggest.js +126 -0
- package/dist/vue/use-sheet-grid.js +180 -0
- package/dist/vue/use-sheet-state.js +122 -0
- package/dist/vue/use-sheet-tabs-bar.js +90 -0
- package/dist/vue/use-tool-groups.js +42 -0
- package/dist/vue/use-tool-popup.js +123 -0
- package/dist/vue/use-toolbar-scroll.js +72 -0
- package/package.json +12 -6
- package/src/env.d.ts +14 -0
- package/src/index.ts +203 -13
- package/src/tools/__test__/builtin.test.ts +208 -66
- package/src/tools/__test__/context.test.ts +62 -2
- package/src/tools/builtin.ts +350 -6
- package/src/tools/context.ts +89 -11
- package/src/tools/download.ts +82 -0
- package/src/tools/export.worker.ts +61 -0
- package/src/tools/registry.ts +16 -1
- package/src/types/index.ts +6 -0
- package/src/types/sheet.ts +5 -3
- package/src/vue/__test__/formula-bar.test.ts +455 -0
- package/src/vue/__test__/import-file.test.ts +183 -0
- package/src/vue/__test__/insert-count-menu-item.test.ts +123 -0
- package/src/vue/__test__/insert-image-popup.test.ts +89 -0
- package/src/vue/__test__/insert-image.test.ts +100 -0
- package/src/vue/__test__/sheet-component.test.ts +716 -27
- package/src/vue/__test__/sheet-context-menu.test.ts +206 -0
- package/src/vue/__test__/use-formula-ref-select.test.ts +35 -0
- package/src/vue/__test__/use-formula-suggest.test.ts +83 -0
- package/src/vue/formula-bar.vue +473 -0
- package/src/vue/formula-suggest-list.vue +64 -0
- package/src/vue/import-file.ts +173 -0
- package/src/vue/insert-count-menu-item.vue +69 -0
- package/src/vue/insert-image.ts +90 -0
- package/src/vue/parsing.ts +11 -0
- package/src/vue/popup-helpers.ts +50 -0
- package/src/vue/popups/border-popup.vue +93 -0
- package/src/vue/popups/border-preset-glyph.vue +111 -0
- package/src/vue/popups/export-popup.vue +52 -0
- package/src/vue/popups/fill-color-popup.vue +39 -0
- package/src/vue/popups/find-popup.vue +111 -0
- package/src/vue/popups/font-color-popup.vue +39 -0
- package/src/vue/popups/font-size-popup.vue +52 -0
- package/src/vue/popups/import.worker.ts +66 -0
- package/src/vue/popups/insert-image-popup.vue +39 -0
- package/src/vue/sheet-context-menu.ts +229 -0
- package/src/vue/sheet-tabs.vue +162 -0
- package/src/vue/sheet-toolbar.vue +91 -0
- package/src/vue/sheet.vue +163 -148
- package/src/vue/style.scss +646 -3
- package/src/vue/style.ts +12 -0
- package/src/vue/use-find-replace.ts +200 -0
- package/src/vue/use-formula-ref-select.ts +34 -0
- package/src/vue/use-formula-suggest.ts +150 -0
- package/src/vue/use-sheet-grid.ts +243 -0
- package/src/vue/use-sheet-state.ts +161 -0
- package/src/vue/use-sheet-tabs-bar.ts +115 -0
- package/src/vue/use-tool-groups.ts +61 -0
- package/src/vue/use-tool-popup.ts +150 -0
- package/src/vue/use-toolbar-scroll.ts +95 -0
- package/dist/core/address.d.ts +0 -60
- package/dist/core/address.js +0 -142
- package/dist/core/address.js.map +0 -1
- package/dist/core/cell-store.d.ts +0 -61
- package/dist/core/cell-store.js +0 -135
- package/dist/core/cell-store.js.map +0 -1
- package/dist/core/command/default-registry.d.ts +0 -10
- package/dist/core/command/default-registry.js +0 -18
- package/dist/core/command/default-registry.js.map +0 -1
- package/dist/core/command/history.d.ts +0 -64
- package/dist/core/command/history.js +0 -127
- package/dist/core/command/history.js.map +0 -1
- package/dist/core/command/merge-cells.d.ts +0 -21
- package/dist/core/command/merge-cells.js +0 -74
- package/dist/core/command/merge-cells.js.map +0 -1
- package/dist/core/command/registry.d.ts +0 -17
- package/dist/core/command/registry.js +0 -26
- package/dist/core/command/registry.js.map +0 -1
- package/dist/core/command/set-cell-formula.d.ts +0 -17
- package/dist/core/command/set-cell-formula.js +0 -29
- package/dist/core/command/set-cell-formula.js.map +0 -1
- package/dist/core/command/set-cell-value.d.ts +0 -22
- package/dist/core/command/set-cell-value.js +0 -35
- package/dist/core/command/set-cell-value.js.map +0 -1
- package/dist/core/command/types.d.ts +0 -68
- package/dist/core/events.js +0 -29
- package/dist/core/events.js.map +0 -1
- package/dist/core/formula/ast.d.ts +0 -58
- package/dist/core/formula/ast.js +0 -38
- package/dist/core/formula/ast.js.map +0 -1
- package/dist/core/formula/dependency-graph.d.ts +0 -70
- package/dist/core/formula/dependency-graph.js +0 -269
- package/dist/core/formula/dependency-graph.js.map +0 -1
- package/dist/core/formula/errors.d.ts +0 -25
- package/dist/core/formula/errors.js +0 -36
- package/dist/core/formula/errors.js.map +0 -1
- package/dist/core/formula/evaluator.d.ts +0 -41
- package/dist/core/formula/evaluator.js +0 -154
- package/dist/core/formula/evaluator.js.map +0 -1
- package/dist/core/formula/functions.d.ts +0 -30
- package/dist/core/formula/functions.js +0 -209
- package/dist/core/formula/functions.js.map +0 -1
- package/dist/core/formula/parser.d.ts +0 -7
- package/dist/core/formula/parser.js +0 -208
- package/dist/core/formula/parser.js.map +0 -1
- package/dist/core/formula/tokenizer.d.ts +0 -37
- package/dist/core/formula/tokenizer.js +0 -146
- package/dist/core/formula/tokenizer.js.map +0 -1
- package/dist/core/merge-manager.d.ts +0 -64
- package/dist/core/merge-manager.js +0 -111
- package/dist/core/merge-manager.js.map +0 -1
- package/dist/core/selection.d.ts +0 -31
- package/dist/core/selection.js +0 -62
- package/dist/core/selection.js.map +0 -1
- package/dist/core/sheet.d.ts +0 -105
- package/dist/core/sheet.js +0 -202
- package/dist/core/sheet.js.map +0 -1
- package/dist/core/workbook.d.ts +0 -43
- package/dist/core/workbook.js +0 -77
- package/dist/core/workbook.js.map +0 -1
- package/dist/grid/sheet-grid.d.ts +0 -59
- package/dist/grid/sheet-grid.js +0 -230
- package/dist/grid/sheet-grid.js.map +0 -1
- package/dist/tools/builtin.js.map +0 -1
- package/dist/tools/context.js.map +0 -1
- package/dist/tools/registry.js.map +0 -1
- package/dist/vue/index.d.ts +0 -2
- package/dist/vue/sheet.js.map +0 -1
- package/dist/vue/sheet.vue.d.ts +0 -24
- package/src/core/__test__/address.test.ts +0 -113
- package/src/core/__test__/cell-store.test.ts +0 -97
- package/src/core/__test__/command.test.ts +0 -377
- package/src/core/__test__/formula-adversarial.test.ts +0 -265
- package/src/core/__test__/formula-engine.test.ts +0 -470
- package/src/core/__test__/formula-functions.test.ts +0 -178
- package/src/core/__test__/formula-parser.test.ts +0 -160
- package/src/core/__test__/history.test.ts +0 -225
- package/src/core/__test__/merge-manager.test.ts +0 -106
- package/src/core/__test__/sheet.test.ts +0 -142
- package/src/core/address.ts +0 -166
- package/src/core/cell-store.ts +0 -173
- package/src/core/command/default-registry.ts +0 -15
- package/src/core/command/history.ts +0 -167
- package/src/core/command/index.ts +0 -7
- package/src/core/command/merge-cells.ts +0 -74
- package/src/core/command/registry.ts +0 -30
- package/src/core/command/set-cell-formula.ts +0 -29
- package/src/core/command/set-cell-value.ts +0 -38
- package/src/core/command/types.ts +0 -73
- package/src/core/events.ts +0 -37
- package/src/core/formula/ast.ts +0 -66
- package/src/core/formula/dependency-graph.ts +0 -329
- package/src/core/formula/errors.ts +0 -41
- package/src/core/formula/evaluator.ts +0 -219
- package/src/core/formula/functions.ts +0 -266
- package/src/core/formula/index.ts +0 -7
- package/src/core/formula/parser.ts +0 -198
- package/src/core/formula/tokenizer.ts +0 -160
- package/src/core/merge-manager.ts +0 -158
- package/src/core/selection.ts +0 -66
- package/src/core/sheet.ts +0 -257
- package/src/core/workbook.ts +0 -99
- package/src/grid/__test__/canvas-mock.ts +0 -49
- package/src/grid/__test__/setup.ts +0 -3
- package/src/grid/__test__/sheet-grid-formula.test.ts +0 -140
- package/src/grid/__test__/sheet-grid.test.ts +0 -177
- package/src/grid/sheet-grid.ts +0 -283
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useFindReplace } from "../use-find-replace.js";
|
|
2
|
+
import { Fragment, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, isRef, normalizeClass, openBlock, toDisplayString, unref, vModelCheckbox, vModelSelect, withDirectives } from "vue";
|
|
3
|
+
import { UInput } from "@veltra/desktop";
|
|
4
|
+
import { bem } from "@veltra/utils";
|
|
5
|
+
//#region src/vue/popups/find-popup.vue
|
|
6
|
+
const _hoisted_1 = ["disabled"];
|
|
7
|
+
const _hoisted_2 = ["disabled"];
|
|
8
|
+
const _hoisted_3 = ["disabled"];
|
|
9
|
+
const _hoisted_4 = ["disabled"];
|
|
10
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
11
|
+
name: "USheetFindPopup",
|
|
12
|
+
__name: "find-popup",
|
|
13
|
+
props: {
|
|
14
|
+
sheet: {},
|
|
15
|
+
context: {}
|
|
16
|
+
},
|
|
17
|
+
emits: ["close"],
|
|
18
|
+
setup(__props, { emit: __emit }) {
|
|
19
|
+
/**
|
|
20
|
+
* 查找条(关键词 / 上一个 / 下一个 / 命中计数 / 关闭 + 替换 / 全部替换)。
|
|
21
|
+
* 不参与面板事务:每次替换独立为一个 undo 单元;全部替换 = 一次批量 = 单 undo 单元。
|
|
22
|
+
* 弹层随 v-if 挂载 / 销毁,每次打开都是全新状态(关键词清空)。
|
|
23
|
+
*/
|
|
24
|
+
const props = __props;
|
|
25
|
+
const emit = __emit;
|
|
26
|
+
const cls = bem("sheet");
|
|
27
|
+
const { findQuery, findReplace, caseSensitive, wholeCell, searchIn, canFind, canReplace, findCountText, findForward, findPrevious, handleFindKeydown, replaceCurrent, replaceAll } = useFindReplace({
|
|
28
|
+
getSheet: () => props.sheet,
|
|
29
|
+
context: props.context
|
|
30
|
+
});
|
|
31
|
+
return (_ctx, _cache) => {
|
|
32
|
+
return openBlock(), createElementBlock(Fragment, null, [
|
|
33
|
+
createElementVNode("div", { class: normalizeClass(unref(cls).e("find-row")) }, [
|
|
34
|
+
createVNode(unref(UInput), {
|
|
35
|
+
modelValue: unref(findQuery),
|
|
36
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRef(findQuery) ? findQuery.value = $event : null),
|
|
37
|
+
placeholder: "查找内容",
|
|
38
|
+
size: "small",
|
|
39
|
+
class: normalizeClass(unref(cls).e("find-input")),
|
|
40
|
+
onKeydown: unref(handleFindKeydown)
|
|
41
|
+
}, null, 8, [
|
|
42
|
+
"modelValue",
|
|
43
|
+
"class",
|
|
44
|
+
"onKeydown"
|
|
45
|
+
]),
|
|
46
|
+
createElementVNode("span", { class: normalizeClass(unref(cls).e("find-count")) }, toDisplayString(unref(findCountText)), 3),
|
|
47
|
+
createElementVNode("button", {
|
|
48
|
+
type: "button",
|
|
49
|
+
class: normalizeClass(unref(cls).e("find-nav")),
|
|
50
|
+
disabled: !unref(canFind),
|
|
51
|
+
title: "上一个(Shift+Enter)",
|
|
52
|
+
onClick: _cache[1] || (_cache[1] = (...args) => unref(findPrevious) && unref(findPrevious)(...args))
|
|
53
|
+
}, " ↑ ", 10, _hoisted_1),
|
|
54
|
+
createElementVNode("button", {
|
|
55
|
+
type: "button",
|
|
56
|
+
class: normalizeClass(unref(cls).e("find-nav")),
|
|
57
|
+
disabled: !unref(canFind),
|
|
58
|
+
title: "下一个(Enter)",
|
|
59
|
+
onClick: _cache[2] || (_cache[2] = (...args) => unref(findForward) && unref(findForward)(...args))
|
|
60
|
+
}, " ↓ ", 10, _hoisted_2),
|
|
61
|
+
createElementVNode("button", {
|
|
62
|
+
type: "button",
|
|
63
|
+
class: normalizeClass(unref(cls).e("find-close")),
|
|
64
|
+
title: "关闭",
|
|
65
|
+
onClick: _cache[3] || (_cache[3] = ($event) => emit("close"))
|
|
66
|
+
}, " ✕ ", 2)
|
|
67
|
+
], 2),
|
|
68
|
+
createElementVNode("div", { class: normalizeClass(unref(cls).e("find-row")) }, [
|
|
69
|
+
createVNode(unref(UInput), {
|
|
70
|
+
modelValue: unref(findReplace),
|
|
71
|
+
"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => isRef(findReplace) ? findReplace.value = $event : null),
|
|
72
|
+
placeholder: "替换为",
|
|
73
|
+
size: "small",
|
|
74
|
+
class: normalizeClass(unref(cls).e("find-input")),
|
|
75
|
+
onKeydown: unref(handleFindKeydown)
|
|
76
|
+
}, null, 8, [
|
|
77
|
+
"modelValue",
|
|
78
|
+
"class",
|
|
79
|
+
"onKeydown"
|
|
80
|
+
]),
|
|
81
|
+
createElementVNode("button", {
|
|
82
|
+
type: "button",
|
|
83
|
+
class: normalizeClass(unref(cls).e("find-btn")),
|
|
84
|
+
disabled: !unref(canReplace),
|
|
85
|
+
onClick: _cache[5] || (_cache[5] = (...args) => unref(replaceCurrent) && unref(replaceCurrent)(...args))
|
|
86
|
+
}, " 替换 ", 10, _hoisted_3),
|
|
87
|
+
createElementVNode("button", {
|
|
88
|
+
type: "button",
|
|
89
|
+
class: normalizeClass(unref(cls).e("find-btn")),
|
|
90
|
+
disabled: !unref(canReplace),
|
|
91
|
+
onClick: _cache[6] || (_cache[6] = (...args) => unref(replaceAll) && unref(replaceAll)(...args))
|
|
92
|
+
}, " 全部替换 ", 10, _hoisted_4)
|
|
93
|
+
], 2),
|
|
94
|
+
createElementVNode("div", { class: normalizeClass(unref(cls).e("find-row")) }, [
|
|
95
|
+
createElementVNode("label", { class: normalizeClass(unref(cls).e("find-option")) }, [withDirectives(createElementVNode("input", {
|
|
96
|
+
"onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => isRef(caseSensitive) ? caseSensitive.value = $event : null),
|
|
97
|
+
type: "checkbox"
|
|
98
|
+
}, null, 512), [[vModelCheckbox, unref(caseSensitive)]]), _cache[10] || (_cache[10] = createTextVNode("区分大小写 ", -1))], 2),
|
|
99
|
+
createElementVNode("label", { class: normalizeClass(unref(cls).e("find-option")) }, [withDirectives(createElementVNode("input", {
|
|
100
|
+
"onUpdate:modelValue": _cache[8] || (_cache[8] = ($event) => isRef(wholeCell) ? wholeCell.value = $event : null),
|
|
101
|
+
type: "checkbox"
|
|
102
|
+
}, null, 512), [[vModelCheckbox, unref(wholeCell)]]), _cache[11] || (_cache[11] = createTextVNode("整格匹配 ", -1))], 2),
|
|
103
|
+
createElementVNode("label", { class: normalizeClass(unref(cls).e("find-option")) }, [_cache[13] || (_cache[13] = createTextVNode(" 查找 ", -1)), withDirectives(createElementVNode("select", {
|
|
104
|
+
"onUpdate:modelValue": _cache[9] || (_cache[9] = ($event) => isRef(searchIn) ? searchIn.value = $event : null),
|
|
105
|
+
class: normalizeClass(unref(cls).e("find-select"))
|
|
106
|
+
}, [..._cache[12] || (_cache[12] = [createElementVNode("option", { value: "value" }, "按显示值", -1), createElementVNode("option", { value: "formula" }, "按公式", -1)])], 2), [[vModelSelect, unref(searchIn)]])], 2)
|
|
107
|
+
], 2)
|
|
108
|
+
], 64);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
//#endregion
|
|
113
|
+
export { _sfc_main as default };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { currentRange } from "../popup-helpers.js";
|
|
2
|
+
import { createBlock, defineComponent, onMounted, openBlock, ref, unref } from "vue";
|
|
3
|
+
import { UPalette } from "@veltra/desktop";
|
|
4
|
+
//#region src/vue/popups/font-color-popup.vue
|
|
5
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
6
|
+
name: "USheetFontColorPopup",
|
|
7
|
+
__name: "font-color-popup",
|
|
8
|
+
props: { context: {} },
|
|
9
|
+
setup(__props) {
|
|
10
|
+
/**
|
|
11
|
+
* 字体颜色面板(UPalette;空值 = 清除 color 保留其它字体字段)。
|
|
12
|
+
* 面板打开期间的写入由 use-tool-popup 事务包裹为一个 undo 单元。
|
|
13
|
+
*/
|
|
14
|
+
const props = __props;
|
|
15
|
+
/** 字体颜色('' = 自动/默认) */
|
|
16
|
+
const fontColor = ref("");
|
|
17
|
+
onMounted(() => {
|
|
18
|
+
const active = props.context.getSelection().activeCell;
|
|
19
|
+
const style = active ? props.context.getCellStyle(active) : void 0;
|
|
20
|
+
fontColor.value = style?.font?.color ?? "";
|
|
21
|
+
});
|
|
22
|
+
/** 字体颜色变化('' = 清除 color) */
|
|
23
|
+
function applyFontColor(color) {
|
|
24
|
+
const range = currentRange(props.context);
|
|
25
|
+
if (!range) return;
|
|
26
|
+
props.context.setCellStyle(range, color ? { font: { color } } : { font: { color: null } });
|
|
27
|
+
}
|
|
28
|
+
return (_ctx, _cache) => {
|
|
29
|
+
return openBlock(), createBlock(unref(UPalette), {
|
|
30
|
+
"model-value": fontColor.value,
|
|
31
|
+
"onUpdate:modelValue": applyFontColor
|
|
32
|
+
}, null, 8, ["model-value"]);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
export { _sfc_main as default };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { currentRange } from "../popup-helpers.js";
|
|
2
|
+
import { Fragment, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, onMounted, openBlock, ref, renderList, toDisplayString, unref, withCtx } from "vue";
|
|
3
|
+
import { UScroll } from "@veltra/desktop";
|
|
4
|
+
import { bem } from "@veltra/utils";
|
|
5
|
+
//#region src/vue/popups/font-size-popup.vue
|
|
6
|
+
const _hoisted_1 = ["onClick"];
|
|
7
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
8
|
+
name: "USheetFontSizePopup",
|
|
9
|
+
__name: "font-size-popup",
|
|
10
|
+
props: { context: {} },
|
|
11
|
+
setup(__props) {
|
|
12
|
+
/** 预设字号列表(pt,与 Excel 常用档对齐) */
|
|
13
|
+
const FONT_SIZES = [
|
|
14
|
+
9,
|
|
15
|
+
10,
|
|
16
|
+
11,
|
|
17
|
+
12,
|
|
18
|
+
14,
|
|
19
|
+
16,
|
|
20
|
+
18,
|
|
21
|
+
20,
|
|
22
|
+
24,
|
|
23
|
+
28,
|
|
24
|
+
32
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* 字号面板(预设列表;点击写入 font.size)。
|
|
28
|
+
* 面板打开期间的写入由 use-tool-popup 事务包裹为一个 undo 单元。
|
|
29
|
+
*/
|
|
30
|
+
const props = __props;
|
|
31
|
+
const cls = bem("sheet");
|
|
32
|
+
const currentSize = ref(void 0);
|
|
33
|
+
onMounted(() => {
|
|
34
|
+
const active = props.context.getSelection().activeCell;
|
|
35
|
+
const style = active ? props.context.getCellStyle(active) : void 0;
|
|
36
|
+
currentSize.value = style?.font?.size;
|
|
37
|
+
});
|
|
38
|
+
function applyFontSize(size) {
|
|
39
|
+
const range = currentRange(props.context);
|
|
40
|
+
if (!range) return;
|
|
41
|
+
currentSize.value = size;
|
|
42
|
+
props.context.setCellStyle(range, { font: { size } });
|
|
43
|
+
}
|
|
44
|
+
return (_ctx, _cache) => {
|
|
45
|
+
return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" 下拉排列:每行一个字号;滚动用 desktop UScroll(自定义滚动条) "), createVNode(unref(UScroll), {
|
|
46
|
+
height: 280,
|
|
47
|
+
class: normalizeClass(unref(cls).e("popup-size-scroll"))
|
|
48
|
+
}, {
|
|
49
|
+
default: withCtx(() => [createElementVNode("div", { class: normalizeClass(unref(cls).e("popup-size-list")) }, [(openBlock(), createElementBlock(Fragment, null, renderList(FONT_SIZES, (size) => {
|
|
50
|
+
return createElementVNode("button", {
|
|
51
|
+
key: size,
|
|
52
|
+
type: "button",
|
|
53
|
+
class: normalizeClass([unref(cls).e("popup-size-item"), unref(bem).is("active", currentSize.value === size)]),
|
|
54
|
+
onClick: ($event) => applyFontSize(size)
|
|
55
|
+
}, toDisplayString(size), 11, _hoisted_1);
|
|
56
|
+
}), 64))], 2)]),
|
|
57
|
+
_: 1
|
|
58
|
+
}, 8, ["class"])], 2112);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
//#endregion
|
|
63
|
+
export { _sfc_main as default };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SheetSnapshot } from "@veltra/sheet-core/core/sheet";
|
|
2
|
+
//#region src/vue/popups/import.worker.d.ts
|
|
3
|
+
type ImportWorkerResponse = {
|
|
4
|
+
type: 'progress';
|
|
5
|
+
done: number;
|
|
6
|
+
total: number;
|
|
7
|
+
} | {
|
|
8
|
+
type: 'done';
|
|
9
|
+
ok: true;
|
|
10
|
+
sheets: {
|
|
11
|
+
name: string;
|
|
12
|
+
snapshot: SheetSnapshot;
|
|
13
|
+
}[];
|
|
14
|
+
activeIndex: number;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'done';
|
|
17
|
+
ok: false;
|
|
18
|
+
error: string;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { ImportWorkerResponse };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/vue/popups/import.worker.ts
|
|
2
|
+
self.onmessage = (e) => {
|
|
3
|
+
const { buffer } = e.data;
|
|
4
|
+
(async () => {
|
|
5
|
+
try {
|
|
6
|
+
const [{ readXlsx }, { buildWorkbookFromHucre }] = await Promise.all([import("hucre/xlsx"), import("@veltra/sheet-core/core/io/import")]);
|
|
7
|
+
const hucreWb = await readXlsx(new Uint8Array(buffer), { readStyles: true });
|
|
8
|
+
let lastPercent = -1;
|
|
9
|
+
const wb = buildWorkbookFromHucre(hucreWb, (done, total) => {
|
|
10
|
+
const percent = total > 0 ? Math.floor(done / total * 10) : 10;
|
|
11
|
+
if (percent !== lastPercent || done === total) {
|
|
12
|
+
lastPercent = percent;
|
|
13
|
+
const progress = {
|
|
14
|
+
type: "progress",
|
|
15
|
+
done,
|
|
16
|
+
total
|
|
17
|
+
};
|
|
18
|
+
self.postMessage(progress);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const response = {
|
|
22
|
+
type: "done",
|
|
23
|
+
ok: true,
|
|
24
|
+
sheets: wb.getSheets().map((s) => ({
|
|
25
|
+
name: s.name,
|
|
26
|
+
snapshot: s.snapshot()
|
|
27
|
+
})),
|
|
28
|
+
activeIndex: wb.activeSheetIndex
|
|
29
|
+
};
|
|
30
|
+
self.postMessage(response);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
const response = {
|
|
33
|
+
type: "done",
|
|
34
|
+
ok: false,
|
|
35
|
+
error: err instanceof Error ? err.message : String(err)
|
|
36
|
+
};
|
|
37
|
+
self.postMessage(response);
|
|
38
|
+
}
|
|
39
|
+
})();
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { INSERT_IMAGE_ACCEPT, insertImageFromFile } from "../insert-image.js";
|
|
2
|
+
import { createBlock, createElementVNode, createTextVNode, defineComponent, normalizeClass, openBlock, unref, withCtx } from "vue";
|
|
3
|
+
import { UFilePicker } from "@veltra/desktop";
|
|
4
|
+
import { bem } from "@veltra/utils";
|
|
5
|
+
//#region src/vue/popups/insert-image-popup.vue
|
|
6
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
7
|
+
name: "USheetInsertImagePopup",
|
|
8
|
+
__name: "insert-image-popup",
|
|
9
|
+
props: { context: {} },
|
|
10
|
+
emits: ["close"],
|
|
11
|
+
setup(__props, { emit: __emit }) {
|
|
12
|
+
/**
|
|
13
|
+
* 插入图片面板(UFilePicker):选文件后走共享 insertImageFromFile,锚定当前选区活动格。
|
|
14
|
+
* 不参与面板事务(单次插入 = 一个 undo 单元)。
|
|
15
|
+
*/
|
|
16
|
+
const props = __props;
|
|
17
|
+
const emit = __emit;
|
|
18
|
+
const cls = bem("sheet");
|
|
19
|
+
function handlePick(files) {
|
|
20
|
+
const file = files[0];
|
|
21
|
+
if (!file) return;
|
|
22
|
+
emit("close");
|
|
23
|
+
insertImageFromFile(props.context, file);
|
|
24
|
+
}
|
|
25
|
+
return (_ctx, _cache) => {
|
|
26
|
+
return openBlock(), createBlock(unref(UFilePicker), {
|
|
27
|
+
accept: unref(INSERT_IMAGE_ACCEPT),
|
|
28
|
+
class: normalizeClass(unref(cls).e("insert-image-picker")),
|
|
29
|
+
onPick: handlePick
|
|
30
|
+
}, {
|
|
31
|
+
default: withCtx(() => [createElementVNode("div", { class: normalizeClass(unref(cls).e("insert-image-hint")) }, [_cache[0] || (_cache[0] = createTextVNode(" 选择图片文件 ", -1)), createElementVNode("div", { class: normalizeClass(unref(cls).e("insert-image-sub")) }, "支持 png / jpeg / gif / svg / webp", 2)], 2)]),
|
|
32
|
+
_: 1
|
|
33
|
+
}, 8, ["accept", "class"]);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
//#endregion
|
|
38
|
+
export { _sfc_main as default };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { defaultToolRegistry } from "../tools/registry.js";
|
|
2
|
+
import { pickAndInsertImage } from "./insert-image.js";
|
|
3
|
+
import _sfc_main from "./insert-count-menu-item.js";
|
|
4
|
+
import { createRange } from "@veltra/sheet-core/core/address";
|
|
5
|
+
import { defineComponent, h } from "vue";
|
|
6
|
+
const INSERT_COUNT_MAX = 1e3;
|
|
7
|
+
/** 选区覆盖行/列数 → 插入默认 N(min 1 / max 1000) */
|
|
8
|
+
function defaultInsertCount(range, axis) {
|
|
9
|
+
if (!range) return 1;
|
|
10
|
+
const n = axis === "row" ? range.end.row - range.start.row + 1 : range.end.col - range.start.col + 1;
|
|
11
|
+
return Math.min(INSERT_COUNT_MAX, Math.max(1, n));
|
|
12
|
+
}
|
|
13
|
+
/** 点击行是否落在任一选区内(按行号覆盖) */
|
|
14
|
+
function isRowInSelection(ranges, row) {
|
|
15
|
+
return ranges.some((r) => r.start.row <= row && row <= r.end.row);
|
|
16
|
+
}
|
|
17
|
+
/** 点击列是否落在任一选区内(按列号覆盖) */
|
|
18
|
+
function isColInSelection(ranges, col) {
|
|
19
|
+
return ranges.some((r) => r.start.col <= col && col <= r.end.col);
|
|
20
|
+
}
|
|
21
|
+
/** 整行选区(列跨渲染宽度) */
|
|
22
|
+
function wholeRowRange(row, colCount) {
|
|
23
|
+
return createRange({
|
|
24
|
+
row,
|
|
25
|
+
col: 0
|
|
26
|
+
}, {
|
|
27
|
+
row,
|
|
28
|
+
col: Math.max(colCount - 1, 0)
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** 整列选区(行跨渲染高度) */
|
|
32
|
+
function wholeColRange(col, rowCount) {
|
|
33
|
+
return createRange({
|
|
34
|
+
row: 0,
|
|
35
|
+
col
|
|
36
|
+
}, {
|
|
37
|
+
row: Math.max(rowCount - 1, 0),
|
|
38
|
+
col
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/** 渲染尺寸 = max(props, sheet.rows/cols),与 SheetGrid 一致 */
|
|
42
|
+
function resolveRenderSize(propsRows, propsCols, sheetRows, sheetCols) {
|
|
43
|
+
return {
|
|
44
|
+
rows: Math.max(propsRows ?? 100, sheetRows),
|
|
45
|
+
cols: Math.max(propsCols ?? 26, sheetCols)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function insertCountRender(options) {
|
|
49
|
+
return defineComponent({
|
|
50
|
+
name: "SheetInsertCountMenuRender",
|
|
51
|
+
setup() {
|
|
52
|
+
return () => h(_sfc_main, {
|
|
53
|
+
prefix: options.prefix,
|
|
54
|
+
suffix: options.suffix,
|
|
55
|
+
defaultValue: options.defaultValue,
|
|
56
|
+
min: 1,
|
|
57
|
+
max: INSERT_COUNT_MAX,
|
|
58
|
+
onConfirm: options.onConfirm
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function primaryRange(ctx) {
|
|
64
|
+
return ctx.getSelection().ranges[0] ?? null;
|
|
65
|
+
}
|
|
66
|
+
/** 行号右键菜单:插入×2 / 删除 / divider / 冻结 / 取消冻结 */
|
|
67
|
+
function buildRowHeaderMenus(ctx) {
|
|
68
|
+
const range = primaryRange(ctx);
|
|
69
|
+
const startRow = range?.start.row ?? 0;
|
|
70
|
+
const endRow = range?.end.row ?? startRow;
|
|
71
|
+
const rowCount = defaultInsertCount(range, "row");
|
|
72
|
+
const deleteCount = range ? endRow - startRow + 1 : 1;
|
|
73
|
+
const frozen = ctx.frozen;
|
|
74
|
+
const freezeActive = frozen.rows === endRow + 1;
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
label: "在上方插入行",
|
|
78
|
+
keepOpen: true,
|
|
79
|
+
render: insertCountRender({
|
|
80
|
+
prefix: "在上方插入",
|
|
81
|
+
suffix: "行",
|
|
82
|
+
defaultValue: rowCount,
|
|
83
|
+
onConfirm: (n) => ctx.insertRows(startRow, n)
|
|
84
|
+
})
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
label: "在下方插入行",
|
|
88
|
+
keepOpen: true,
|
|
89
|
+
render: insertCountRender({
|
|
90
|
+
prefix: "在下方插入",
|
|
91
|
+
suffix: "行",
|
|
92
|
+
defaultValue: rowCount,
|
|
93
|
+
onConfirm: (n) => ctx.insertRows(endRow + 1, n)
|
|
94
|
+
})
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
label: "删除行",
|
|
98
|
+
callback: () => ctx.deleteRows(startRow, deleteCount)
|
|
99
|
+
},
|
|
100
|
+
{ divider: true },
|
|
101
|
+
{
|
|
102
|
+
label: freezeActive ? "✓ 冻结到当前行" : "冻结到当前行",
|
|
103
|
+
callback: () => ctx.setFrozen(endRow + 1, frozen.cols)
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
label: "取消冻结",
|
|
107
|
+
disabled: frozen.rows === 0 && frozen.cols === 0,
|
|
108
|
+
callback: () => ctx.setFrozen(0, 0)
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
/** 列头右键菜单:插入×2 / 删除 / divider / 冻结 / 取消冻结 */
|
|
113
|
+
function buildColHeaderMenus(ctx) {
|
|
114
|
+
const range = primaryRange(ctx);
|
|
115
|
+
const startCol = range?.start.col ?? 0;
|
|
116
|
+
const endCol = range?.end.col ?? startCol;
|
|
117
|
+
const colCount = defaultInsertCount(range, "col");
|
|
118
|
+
const deleteCount = range ? endCol - startCol + 1 : 1;
|
|
119
|
+
const frozen = ctx.frozen;
|
|
120
|
+
const freezeActive = frozen.cols === endCol + 1;
|
|
121
|
+
return [
|
|
122
|
+
{
|
|
123
|
+
label: "在左侧插入列",
|
|
124
|
+
keepOpen: true,
|
|
125
|
+
render: insertCountRender({
|
|
126
|
+
prefix: "在左侧插入",
|
|
127
|
+
suffix: "列",
|
|
128
|
+
defaultValue: colCount,
|
|
129
|
+
onConfirm: (n) => ctx.insertCols(startCol, n)
|
|
130
|
+
})
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
label: "在右侧插入列",
|
|
134
|
+
keepOpen: true,
|
|
135
|
+
render: insertCountRender({
|
|
136
|
+
prefix: "在右侧插入",
|
|
137
|
+
suffix: "列",
|
|
138
|
+
defaultValue: colCount,
|
|
139
|
+
onConfirm: (n) => ctx.insertCols(endCol + 1, n)
|
|
140
|
+
})
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
label: "删除列",
|
|
144
|
+
callback: () => ctx.deleteCols(startCol, deleteCount)
|
|
145
|
+
},
|
|
146
|
+
{ divider: true },
|
|
147
|
+
{
|
|
148
|
+
label: freezeActive ? "✓ 冻结到当前列" : "冻结到当前列",
|
|
149
|
+
callback: () => ctx.setFrozen(frozen.rows, endCol + 1)
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
label: "取消冻结",
|
|
153
|
+
disabled: frozen.rows === 0 && frozen.cols === 0,
|
|
154
|
+
callback: () => ctx.setFrozen(0, 0)
|
|
155
|
+
}
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
/** body 格右键菜单:合并/取消合并 + 插入图片(行列插入/删除仅行号/列头菜单) */
|
|
159
|
+
function buildBodyMenus(ctx) {
|
|
160
|
+
const mergeTool = defaultToolRegistry.get("merge");
|
|
161
|
+
const unmergeTool = defaultToolRegistry.get("unmerge");
|
|
162
|
+
const active = ctx.getSelection().activeCell;
|
|
163
|
+
return [
|
|
164
|
+
{
|
|
165
|
+
label: "合并单元格",
|
|
166
|
+
disabled: mergeTool?.disabled?.(ctx) ?? true,
|
|
167
|
+
callback: () => mergeTool?.onClick(ctx)
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
label: "取消合并单元格",
|
|
171
|
+
disabled: unmergeTool?.disabled?.(ctx) ?? true,
|
|
172
|
+
callback: () => unmergeTool?.onClick(ctx)
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
label: "插入图片",
|
|
176
|
+
disabled: !active,
|
|
177
|
+
callback: () => pickAndInsertImage(ctx)
|
|
178
|
+
}
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 右键落点预处理:body 格落在选区外 → 选中该格;
|
|
183
|
+
* 行号/列头落在选区外 → 选中整行/整列(渲染尺寸)。
|
|
184
|
+
*/
|
|
185
|
+
function ensureContextMenuSelection(ctx, info, renderSize) {
|
|
186
|
+
const ranges = ctx.getSelection().ranges;
|
|
187
|
+
if (info.kind === "body" && info.addr) {
|
|
188
|
+
if (!ranges.some((range) => range.start.row <= info.addr.row && info.addr.row <= range.end.row && range.start.col <= info.addr.col && info.addr.col <= range.end.col)) ctx.selectCell(info.addr);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (info.kind === "row-header" && info.row != null) {
|
|
192
|
+
if (!isRowInSelection(ranges, info.row)) ctx.selectRange(wholeRowRange(info.row, renderSize.cols));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (info.kind === "col-header" && info.col != null) {
|
|
196
|
+
if (!isColInSelection(ranges, info.col)) ctx.selectRange(wholeColRange(info.col, renderSize.rows));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
export { INSERT_COUNT_MAX, buildBodyMenus, buildColHeaderMenus, buildRowHeaderMenus, defaultInsertCount, ensureContextMenuSelection, isColInSelection, isRowInSelection, resolveRenderSize, wholeColRange, wholeRowRange };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { useSheetTabsBar } from "./use-sheet-tabs-bar.js";
|
|
2
|
+
import { ArrowLeft, ArrowRight } from "@veltra/icons/normal";
|
|
3
|
+
import { Fragment, createElementBlock, createElementVNode, createVNode, defineComponent, nextTick, normalizeClass, openBlock, ref, renderList, toDisplayString, toRef, unref, useTemplateRef, vModelText, vShow, withCtx, withDirectives, withKeys, withModifiers } from "vue";
|
|
4
|
+
import { UIcon, contextmenu, message, messageConfirm } from "@veltra/desktop";
|
|
5
|
+
import { bem } from "@veltra/utils";
|
|
6
|
+
//#region src/vue/sheet-tabs.vue
|
|
7
|
+
const _hoisted_1 = ["disabled"];
|
|
8
|
+
const _hoisted_2 = ["onClick", "onContextmenu"];
|
|
9
|
+
const _hoisted_3 = ["onKeydown"];
|
|
10
|
+
const _hoisted_4 = { key: 1 };
|
|
11
|
+
const _hoisted_5 = ["disabled"];
|
|
12
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
13
|
+
name: "USheetTabs",
|
|
14
|
+
__name: "sheet-tabs",
|
|
15
|
+
props: {
|
|
16
|
+
workbook: {},
|
|
17
|
+
sheetList: {},
|
|
18
|
+
activeIndex: {}
|
|
19
|
+
},
|
|
20
|
+
setup(__props) {
|
|
21
|
+
/**
|
|
22
|
+
* 底部 sheet tabs(可滚动视口 + 左右导航;「+」钉在视口外):
|
|
23
|
+
* - tab 点击切换;末尾「+」addSheet + 自动激活新表(激活事件驱动 grid 重建)
|
|
24
|
+
* - tab 右键菜单「重命名 / 删除」:重命名进入行内输入(Enter 提交、Esc 取消、失焦提交;
|
|
25
|
+
* 冲突提示且不写入),删除二次确认(最后一个 sheet 的删除项禁用)
|
|
26
|
+
* - sheet 增删改名是工作簿级结构操作(不走 undo),直接调用 Workbook——
|
|
27
|
+
* 不经 SheetContext(见 AGENTS.md 门面边界);UI 刷新由 workbook 事件驱动
|
|
28
|
+
*/
|
|
29
|
+
const props = __props;
|
|
30
|
+
const cls = bem("sheet");
|
|
31
|
+
const tabsViewportRef = useTemplateRef("tabsViewportRef");
|
|
32
|
+
const tabsListRef = useTemplateRef("tabsListRef");
|
|
33
|
+
const { showNav, canPrev, canNext, scrollByStep } = useSheetTabsBar({
|
|
34
|
+
viewportRef: tabsViewportRef,
|
|
35
|
+
listRef: tabsListRef,
|
|
36
|
+
sheetList: toRef(props, "sheetList"),
|
|
37
|
+
activeIndex: toRef(props, "activeIndex")
|
|
38
|
+
});
|
|
39
|
+
function handleTabClick(index) {
|
|
40
|
+
if (index === props.activeIndex) return;
|
|
41
|
+
const sheet = props.sheetList[index];
|
|
42
|
+
if (sheet) props.workbook.activateSheet(sheet.name);
|
|
43
|
+
}
|
|
44
|
+
/** 「+」添加 sheet:自动激活新表(激活事件驱动 grid 重建) */
|
|
45
|
+
function handleAddSheet() {
|
|
46
|
+
const sheet = props.workbook.addSheet();
|
|
47
|
+
props.workbook.activateSheet(sheet.name);
|
|
48
|
+
}
|
|
49
|
+
/** 正在行内重命名的 tab 下标(-1 = 无) */
|
|
50
|
+
const renamingIndex = ref(-1);
|
|
51
|
+
const renameDraft = ref("");
|
|
52
|
+
const renameInputRef = useTemplateRef("renameInputRef");
|
|
53
|
+
function handleTabContextMenu(event, sheet, index) {
|
|
54
|
+
contextmenu.pop({
|
|
55
|
+
mousePosition: {
|
|
56
|
+
x: event.clientX,
|
|
57
|
+
y: event.clientY
|
|
58
|
+
},
|
|
59
|
+
width: 160,
|
|
60
|
+
menus: [{
|
|
61
|
+
label: "重命名",
|
|
62
|
+
callback: () => startRename(index)
|
|
63
|
+
}, {
|
|
64
|
+
label: "删除",
|
|
65
|
+
disabled: props.sheetList.length <= 1,
|
|
66
|
+
callback: () => confirmRemoveSheet(sheet)
|
|
67
|
+
}]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** 进入行内重命名(预填当前名并全选) */
|
|
71
|
+
function startRename(index) {
|
|
72
|
+
renamingIndex.value = index;
|
|
73
|
+
renameDraft.value = props.sheetList[index]?.name ?? "";
|
|
74
|
+
nextTick(() => renameInputRef.value?.[0]?.select());
|
|
75
|
+
}
|
|
76
|
+
/** 提交重命名:空名 / 重名(含大小写变体)由 Workbook 拒绝,提示且不写入 */
|
|
77
|
+
function commitRename() {
|
|
78
|
+
const index = renamingIndex.value;
|
|
79
|
+
if (index < 0) return;
|
|
80
|
+
renamingIndex.value = -1;
|
|
81
|
+
const sheet = props.sheetList[index];
|
|
82
|
+
if (!sheet) return;
|
|
83
|
+
const next = renameDraft.value.trim();
|
|
84
|
+
if (next === "" || next === sheet.name) return;
|
|
85
|
+
if (!props.workbook.renameSheet(sheet.name, next)) message.warn(`无法重命名:名称“${next}”无效或已被占用`);
|
|
86
|
+
}
|
|
87
|
+
function cancelRename() {
|
|
88
|
+
renamingIndex.value = -1;
|
|
89
|
+
}
|
|
90
|
+
/** 删除确认(message-confirm);最后一个 sheet 在菜单层已禁用,这里再兜底 */
|
|
91
|
+
function confirmRemoveSheet(sheet) {
|
|
92
|
+
if (props.workbook.sheetCount <= 1) return;
|
|
93
|
+
messageConfirm.danger(`确定删除工作表“${sheet.name}”吗?删除后不可恢复。`, {
|
|
94
|
+
confirmButtonText: "删除",
|
|
95
|
+
onClosed: (action) => {
|
|
96
|
+
if (action !== "confirm") return;
|
|
97
|
+
props.workbook.removeSheet(sheet.name);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return (_ctx, _cache) => {
|
|
102
|
+
return openBlock(), createElementBlock("div", { class: normalizeClass(unref(cls).e("tabs")) }, [
|
|
103
|
+
withDirectives(createElementVNode("button", {
|
|
104
|
+
type: "button",
|
|
105
|
+
class: normalizeClass(unref(cls).e("tabs-nav")),
|
|
106
|
+
disabled: !unref(canPrev),
|
|
107
|
+
title: "向左滚动",
|
|
108
|
+
onClick: _cache[0] || (_cache[0] = ($event) => unref(scrollByStep)(-1))
|
|
109
|
+
}, [createVNode(unref(UIcon), null, {
|
|
110
|
+
default: withCtx(() => [createVNode(unref(ArrowLeft))]),
|
|
111
|
+
_: 1
|
|
112
|
+
})], 10, _hoisted_1), [[vShow, unref(showNav)]]),
|
|
113
|
+
createElementVNode("div", {
|
|
114
|
+
ref_key: "tabsViewportRef",
|
|
115
|
+
ref: tabsViewportRef,
|
|
116
|
+
class: normalizeClass(unref(cls).e("tabs-viewport"))
|
|
117
|
+
}, [createElementVNode("div", {
|
|
118
|
+
ref_key: "tabsListRef",
|
|
119
|
+
ref: tabsListRef,
|
|
120
|
+
class: normalizeClass(unref(cls).e("tabs-list"))
|
|
121
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.sheetList, (sheet, index) => {
|
|
122
|
+
return openBlock(), createElementBlock("div", {
|
|
123
|
+
key: sheet,
|
|
124
|
+
class: normalizeClass([unref(cls).e("tab"), unref(bem).is("active", index === __props.activeIndex)]),
|
|
125
|
+
title: "右键重命名 / 删除",
|
|
126
|
+
onClick: ($event) => handleTabClick(index),
|
|
127
|
+
onContextmenu: withModifiers(($event) => handleTabContextMenu($event, sheet, index), ["prevent"])
|
|
128
|
+
}, [renamingIndex.value === index ? withDirectives((openBlock(), createElementBlock("input", {
|
|
129
|
+
key: 0,
|
|
130
|
+
ref_for: true,
|
|
131
|
+
ref_key: "renameInputRef",
|
|
132
|
+
ref: renameInputRef,
|
|
133
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => renameDraft.value = $event),
|
|
134
|
+
class: normalizeClass(unref(cls).e("tab-rename-input")),
|
|
135
|
+
maxlength: 31,
|
|
136
|
+
onClick: _cache[2] || (_cache[2] = withModifiers(() => {}, ["stop"])),
|
|
137
|
+
onKeydown: [withKeys(withModifiers(commitRename, ["prevent"]), ["enter"]), withKeys(withModifiers(cancelRename, ["prevent"]), ["esc"])],
|
|
138
|
+
onBlur: commitRename
|
|
139
|
+
}, null, 42, _hoisted_3)), [[vModelText, renameDraft.value]]) : (openBlock(), createElementBlock("span", _hoisted_4, toDisplayString(sheet.name), 1))], 42, _hoisted_2);
|
|
140
|
+
}), 128))], 2)], 2),
|
|
141
|
+
withDirectives(createElementVNode("button", {
|
|
142
|
+
type: "button",
|
|
143
|
+
class: normalizeClass(unref(cls).e("tabs-nav")),
|
|
144
|
+
disabled: !unref(canNext),
|
|
145
|
+
title: "向右滚动",
|
|
146
|
+
onClick: _cache[3] || (_cache[3] = ($event) => unref(scrollByStep)(1))
|
|
147
|
+
}, [createVNode(unref(UIcon), null, {
|
|
148
|
+
default: withCtx(() => [createVNode(unref(ArrowRight))]),
|
|
149
|
+
_: 1
|
|
150
|
+
})], 10, _hoisted_5), [[vShow, unref(showNav)]]),
|
|
151
|
+
createElementVNode("button", {
|
|
152
|
+
type: "button",
|
|
153
|
+
class: normalizeClass(unref(cls).e("tab-add")),
|
|
154
|
+
title: "添加工作表",
|
|
155
|
+
onClick: handleAddSheet
|
|
156
|
+
}, " + ", 2)
|
|
157
|
+
], 2);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
//#endregion
|
|
162
|
+
export { _sfc_main as default };
|