@veltra/sheet 2.1.1 → 2.2.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.
Files changed (202) hide show
  1. package/dist/components/report/binding-badge.js +76 -0
  2. package/dist/components/report/designer/cell-coords.js +46 -0
  3. package/dist/components/report/designer/col-widths.js +15 -0
  4. package/dist/components/report/designer/conditional-rules/helpers.js +139 -0
  5. package/dist/components/report/designer/drop-highlight-overlay.js +76 -0
  6. package/dist/components/report/designer/float-panel.js +385 -0
  7. package/dist/components/report/designer/role.js +17 -0
  8. package/dist/components/report/designer/rule-preview.js +33 -0
  9. package/dist/components/report/designer/rule-row.js +269 -0
  10. package/dist/components/report/designer/rules-dialog.js +135 -0
  11. package/dist/components/report/designer/topology-overlay.js +122 -0
  12. package/dist/components/report/designer/topology.js +64 -0
  13. package/dist/components/report/designer/use-grid-overlay.js +75 -0
  14. package/dist/components/report/designer-hub.js +177 -0
  15. package/dist/components/report/field-panel-helpers.js +33 -0
  16. package/dist/components/report/field-panel.js +132 -0
  17. package/dist/components/report/filter-bar.js +74 -0
  18. package/dist/components/report/hub-connection-form.js +145 -0
  19. package/dist/components/report/hub-dataset-editor.js +394 -0
  20. package/dist/components/report/report-designer.js +343 -0
  21. package/dist/components/report/report-viewer.js +111 -0
  22. package/dist/components/report/style.d.ts +17 -0
  23. package/dist/components/report/style.js +18 -0
  24. package/dist/components/report/style2.css +869 -0
  25. package/dist/components/report/use-report-designer.js +360 -0
  26. package/dist/components/report/use-report-viewer.js +50 -0
  27. package/dist/{vue → components/sheet}/formula-bar.js +1 -1
  28. package/dist/{vue → components/sheet}/formula-suggest-list.js +1 -1
  29. package/dist/{vue → components/sheet}/import-file.js +1 -1
  30. package/dist/{vue → components/sheet}/insert-count-menu-item.js +1 -1
  31. package/dist/{vue → components/sheet}/insert-image.js +1 -1
  32. package/dist/{vue → components/sheet}/popup-helpers.js +1 -1
  33. package/dist/{vue → components/sheet}/popups/border-popup.js +1 -1
  34. package/dist/{vue → components/sheet}/popups/border-preset-glyph.js +1 -1
  35. package/dist/{vue → components/sheet}/popups/export-popup.js +2 -2
  36. package/dist/{vue → components/sheet}/popups/fill-color-popup.js +1 -1
  37. package/dist/{vue → components/sheet}/popups/find-popup.js +1 -1
  38. package/dist/{vue → components/sheet}/popups/font-color-popup.js +1 -1
  39. package/dist/{vue → components/sheet}/popups/font-size-popup.js +1 -1
  40. package/dist/{vue → components/sheet}/popups/import.worker.d.ts +1 -1
  41. package/dist/{vue → components/sheet}/popups/import.worker.js +1 -1
  42. package/dist/{vue → components/sheet}/popups/insert-image-popup.js +1 -1
  43. package/dist/{vue → components/sheet}/sheet-context-menu.js +1 -1
  44. package/dist/{vue → components/sheet}/sheet-tabs.js +1 -1
  45. package/dist/{vue → components/sheet}/sheet-toolbar.js +1 -1
  46. package/dist/{vue → components/sheet}/sheet.js +8 -1
  47. package/dist/{vue → components/sheet}/use-find-replace.js +1 -1
  48. package/dist/{vue → components/sheet}/use-formula-ref-select.js +1 -1
  49. package/dist/{vue → components/sheet}/use-sheet-grid.js +10 -2
  50. package/dist/{vue → components/sheet}/use-sheet-state.js +3 -3
  51. package/dist/{vue → components/sheet}/use-sheet-tabs-bar.js +1 -1
  52. package/dist/{vue → components/sheet}/use-tool-groups.js +2 -2
  53. package/dist/{vue → components/sheet}/use-tool-popup.js +2 -2
  54. package/dist/{vue → components/sheet}/use-toolbar-scroll.js +1 -1
  55. package/dist/index.d.ts +17 -3
  56. package/dist/index.js +13 -3
  57. package/dist/report/binding.d.ts +45 -0
  58. package/dist/report/binding.js +175 -0
  59. package/dist/report/connector.d.ts +66 -0
  60. package/dist/report/connector.js +159 -0
  61. package/dist/report/export-xlsx.d.ts +19 -0
  62. package/dist/report/export-xlsx.js +95 -0
  63. package/dist/report/filter-bar.d.ts +10 -0
  64. package/dist/report/filter-bar.js +31 -0
  65. package/dist/report/index.d.ts +12 -0
  66. package/dist/report/params.d.ts +13 -0
  67. package/dist/report/params.js +61 -0
  68. package/dist/report/render/aggregate.js +157 -0
  69. package/dist/report/render/builder.d.ts +12 -0
  70. package/dist/report/render/builder.js +139 -0
  71. package/dist/report/render/coordinate.d.ts +25 -0
  72. package/dist/report/render/coordinate.js +733 -0
  73. package/dist/report/render/index.d.ts +14 -0
  74. package/dist/report/render/index.js +21 -0
  75. package/dist/report/render/style-resolver.js +50 -0
  76. package/dist/report/render/template-index.d.ts +24 -0
  77. package/dist/report/render/template-index.js +137 -0
  78. package/dist/report/rules/evaluate.d.ts +25 -0
  79. package/dist/report/rules/evaluate.js +82 -0
  80. package/dist/report/rules/index.d.ts +2 -0
  81. package/dist/report/template.d.ts +59 -0
  82. package/dist/report/template.js +80 -0
  83. package/dist/report/types.d.ts +85 -0
  84. package/dist/tools/context.d.ts +1 -1
  85. package/dist/types/index.d.ts +3 -2
  86. package/dist/types/report-designer.d.ts +36 -0
  87. package/dist/types/report-viewer.d.ts +38 -0
  88. package/dist/types/sheet.d.ts +17 -1
  89. package/package.json +3 -5
  90. package/src/components/report/__test__/conditional-rules-helpers.test.ts +87 -0
  91. package/src/components/report/__test__/field-panel-helpers.test.ts +65 -0
  92. package/src/components/report/__test__/float-panel-position.test.ts +45 -0
  93. package/src/components/report/__test__/report-designer.test.ts +388 -0
  94. package/src/components/report/__test__/report-viewer.test.ts +334 -0
  95. package/src/components/report/__test__/topology.test.ts +143 -0
  96. package/src/components/report/__test__/use-report-designer.test.ts +565 -0
  97. package/src/components/report/binding-badge.ts +73 -0
  98. package/src/components/report/designer/cell-coords.ts +94 -0
  99. package/src/components/report/designer/col-widths.ts +33 -0
  100. package/src/components/report/designer/conditional-rules/helpers.ts +186 -0
  101. package/src/components/report/designer/drop-highlight-overlay.vue +74 -0
  102. package/src/components/report/designer/float-panel.vue +387 -0
  103. package/src/components/report/designer/role.ts +32 -0
  104. package/src/components/report/designer/rule-preview.vue +40 -0
  105. package/src/components/report/designer/rule-row.vue +252 -0
  106. package/src/components/report/designer/rules-dialog.vue +126 -0
  107. package/src/components/report/designer/topology-overlay.vue +127 -0
  108. package/src/components/report/designer/topology.ts +124 -0
  109. package/src/components/report/designer/use-grid-overlay.ts +113 -0
  110. package/src/components/report/designer-hub.vue +198 -0
  111. package/src/components/report/field-panel-helpers.ts +44 -0
  112. package/src/components/report/field-panel.vue +136 -0
  113. package/src/components/report/filter-bar.vue +77 -0
  114. package/src/components/report/hub-connection-form.vue +124 -0
  115. package/src/components/report/hub-dataset-editor.vue +369 -0
  116. package/src/components/report/index.ts +2 -0
  117. package/src/components/report/report-designer.vue +356 -0
  118. package/src/components/report/report-viewer.vue +128 -0
  119. package/src/components/report/style-designer.scss +583 -0
  120. package/src/components/report/style-hub.scss +292 -0
  121. package/src/components/report/style-viewer.scss +104 -0
  122. package/src/components/report/style.scss +3 -0
  123. package/src/components/report/style.ts +20 -0
  124. package/src/components/report/use-report-designer.ts +547 -0
  125. package/src/components/report/use-report-viewer.ts +79 -0
  126. package/src/{vue → components/sheet}/__test__/formula-bar.test.ts +37 -18
  127. package/src/{vue → components/sheet}/__test__/insert-image-popup.test.ts +1 -1
  128. package/src/{vue → components/sheet}/__test__/insert-image.test.ts +1 -1
  129. package/src/{vue → components/sheet}/__test__/sheet-component.test.ts +40 -3
  130. package/src/{vue → components/sheet}/__test__/sheet-context-menu.test.ts +1 -1
  131. package/src/{vue → components/sheet}/formula-bar.vue +1 -1
  132. package/src/{vue → components/sheet}/insert-image.ts +1 -1
  133. package/src/{vue → components/sheet}/popup-helpers.ts +1 -1
  134. package/src/{vue → components/sheet}/popups/border-popup.vue +1 -1
  135. package/src/{vue → components/sheet}/popups/export-popup.vue +2 -2
  136. package/src/{vue → components/sheet}/popups/fill-color-popup.vue +1 -1
  137. package/src/{vue → components/sheet}/popups/find-popup.vue +1 -1
  138. package/src/{vue → components/sheet}/popups/font-color-popup.vue +1 -1
  139. package/src/{vue → components/sheet}/popups/font-size-popup.vue +1 -1
  140. package/src/{vue → components/sheet}/popups/insert-image-popup.vue +1 -1
  141. package/src/{vue → components/sheet}/sheet-context-menu.ts +2 -2
  142. package/src/{vue → components/sheet}/sheet-toolbar.vue +2 -2
  143. package/src/{vue → components/sheet}/sheet.vue +4 -3
  144. package/src/{vue → components/sheet}/use-find-replace.ts +1 -1
  145. package/src/{vue → components/sheet}/use-sheet-grid.ts +7 -3
  146. package/src/{vue → components/sheet}/use-sheet-state.ts +3 -3
  147. package/src/{vue → components/sheet}/use-tool-groups.ts +2 -2
  148. package/src/{vue → components/sheet}/use-tool-popup.ts +2 -2
  149. package/src/index.ts +24 -188
  150. package/src/report/__test__/binding.test.ts +218 -0
  151. package/src/report/__test__/connector.test.ts +218 -0
  152. package/src/report/__test__/export-xlsx.test.ts +118 -0
  153. package/src/report/__test__/filter-bar.test.ts +58 -0
  154. package/src/report/__test__/params.test.ts +156 -0
  155. package/src/report/__test__/render.test.ts +728 -0
  156. package/src/report/__test__/rules.test.ts +131 -0
  157. package/src/report/__test__/template.test.ts +355 -0
  158. package/src/report/binding.ts +243 -0
  159. package/src/report/connector.ts +205 -0
  160. package/src/report/export-xlsx.ts +118 -0
  161. package/src/report/filter-bar.ts +36 -0
  162. package/src/report/index.ts +13 -0
  163. package/src/report/params.ts +96 -0
  164. package/src/report/render/__test__/aggregate.test.ts +16 -0
  165. package/src/report/render/__test__/coordinate.test.ts +405 -0
  166. package/src/report/render/aggregate.ts +240 -0
  167. package/src/report/render/builder.ts +211 -0
  168. package/src/report/render/coordinate.ts +1239 -0
  169. package/src/report/render/index.ts +30 -0
  170. package/src/report/render/style-resolver.ts +68 -0
  171. package/src/report/render/template-index.ts +200 -0
  172. package/src/report/rules/evaluate.ts +113 -0
  173. package/src/report/rules/index.ts +7 -0
  174. package/src/report/template.ts +141 -0
  175. package/src/report/types.ts +96 -0
  176. package/src/tools/builtin.ts +2 -2
  177. package/src/types/index.ts +2 -6
  178. package/src/types/report-designer.ts +36 -0
  179. package/src/types/report-viewer.ts +37 -0
  180. package/src/types/sheet.ts +22 -1
  181. /package/dist/{vue → components/sheet}/style.d.ts +0 -0
  182. /package/dist/{vue → components/sheet}/style.js +0 -0
  183. /package/dist/{vue → components/sheet}/style2.css +0 -0
  184. /package/dist/{vue → components/sheet}/use-formula-suggest.js +0 -0
  185. /package/src/{vue → components/sheet}/__test__/import-file.test.ts +0 -0
  186. /package/src/{vue → components/sheet}/__test__/insert-count-menu-item.test.ts +0 -0
  187. /package/src/{vue → components/sheet}/__test__/use-formula-ref-select.test.ts +0 -0
  188. /package/src/{vue → components/sheet}/__test__/use-formula-suggest.test.ts +0 -0
  189. /package/src/{vue → components/sheet}/formula-suggest-list.vue +0 -0
  190. /package/src/{vue → components/sheet}/import-file.ts +0 -0
  191. /package/src/{vue → components/sheet}/index.ts +0 -0
  192. /package/src/{vue → components/sheet}/insert-count-menu-item.vue +0 -0
  193. /package/src/{vue → components/sheet}/parsing.ts +0 -0
  194. /package/src/{vue → components/sheet}/popups/border-preset-glyph.vue +0 -0
  195. /package/src/{vue → components/sheet}/popups/import.worker.ts +0 -0
  196. /package/src/{vue → components/sheet}/sheet-tabs.vue +0 -0
  197. /package/src/{vue → components/sheet}/style.scss +0 -0
  198. /package/src/{vue → components/sheet}/style.ts +0 -0
  199. /package/src/{vue → components/sheet}/use-formula-ref-select.ts +0 -0
  200. /package/src/{vue → components/sheet}/use-formula-suggest.ts +0 -0
  201. /package/src/{vue → components/sheet}/use-sheet-tabs-bar.ts +0 -0
  202. /package/src/{vue → components/sheet}/use-toolbar-scroll.ts +0 -0
@@ -0,0 +1,177 @@
1
+ import { DEFAULT_CONNECTION_PORTS } from "../../report/connector.js";
2
+ import _sfc_main$1 from "./hub-connection-form.js";
3
+ import _sfc_main$2 from "./hub-dataset-editor.js";
4
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, renderList, toDisplayString, unref, watch, withCtx } from "vue";
5
+ import { UButton, UTag } from "@veltra/desktop";
6
+ import { bem } from "@veltra/utils";
7
+ //#region src/components/report/designer-hub.vue
8
+ const _hoisted_1 = ["onClick"];
9
+ const _hoisted_2 = ["onClick"];
10
+ const _sfc_main = /*@__PURE__*/ defineComponent({
11
+ name: "UReportDatasetHub",
12
+ __name: "designer-hub",
13
+ props: { hub: {} },
14
+ emits: ["close"],
15
+ setup(__props, { emit: __emit }) {
16
+ const props = __props;
17
+ const emit = __emit;
18
+ const cls = bem("report-hub");
19
+ const connections = computed(() => props.hub.connections.value);
20
+ const datasets = computed(() => props.hub.datasets.value);
21
+ function datasetsByConnection(connectionId) {
22
+ return datasets.value.filter((item) => item.connectionId === connectionId);
23
+ }
24
+ const activeConnectionId = ref();
25
+ const activeDatasetId = ref();
26
+ const creatingConnection = ref(false);
27
+ const connectionDraft = ref();
28
+ watch(connections, () => {
29
+ if (!activeConnectionId.value && connections.value[0]) {
30
+ activeConnectionId.value = connections.value[0].id;
31
+ syncConnectionDraft();
32
+ }
33
+ }, { immediate: true });
34
+ function syncConnectionDraft() {
35
+ if (activeDatasetId.value) {
36
+ connectionDraft.value = void 0;
37
+ return;
38
+ }
39
+ if (creatingConnection.value) return;
40
+ const id = activeConnectionId.value;
41
+ const conn = id ? connections.value.find((item) => item.id === id) : void 0;
42
+ connectionDraft.value = conn ? { ...conn } : void 0;
43
+ }
44
+ function selectConnection(connectionId) {
45
+ activeConnectionId.value = connectionId;
46
+ activeDatasetId.value = void 0;
47
+ creatingConnection.value = false;
48
+ syncConnectionDraft();
49
+ }
50
+ function selectDataset(datasetId) {
51
+ activeDatasetId.value = datasetId;
52
+ const dataset = datasets.value.find((item) => item.id === datasetId);
53
+ if (dataset) activeConnectionId.value = dataset.connectionId;
54
+ connectionDraft.value = void 0;
55
+ creatingConnection.value = false;
56
+ }
57
+ let connectionSeq = 0;
58
+ function startNewConnection() {
59
+ creatingConnection.value = true;
60
+ activeDatasetId.value = void 0;
61
+ connectionDraft.value = {
62
+ id: `conn-${Date.now()}-${connectionSeq++}`,
63
+ label: "新连接",
64
+ type: "mysql",
65
+ host: "127.0.0.1",
66
+ port: DEFAULT_CONNECTION_PORTS.mysql,
67
+ database: "",
68
+ username: "",
69
+ password: ""
70
+ };
71
+ }
72
+ function saveConnection(conn) {
73
+ if (creatingConnection.value) {
74
+ props.hub.addConnection(conn);
75
+ creatingConnection.value = false;
76
+ } else props.hub.updateConnection(conn);
77
+ activeConnectionId.value = conn.id;
78
+ syncConnectionDraft();
79
+ }
80
+ function removeActiveConnection() {
81
+ const id = activeConnectionId.value;
82
+ if (!id) return;
83
+ props.hub.removeConnection(id);
84
+ activeDatasetId.value = void 0;
85
+ activeConnectionId.value = connections.value[0]?.id;
86
+ creatingConnection.value = false;
87
+ syncConnectionDraft();
88
+ }
89
+ function addDataset(connectionId) {
90
+ const dataset = props.hub.addDataset(connectionId);
91
+ activeConnectionId.value = connectionId;
92
+ activeDatasetId.value = dataset.id;
93
+ creatingConnection.value = false;
94
+ connectionDraft.value = void 0;
95
+ }
96
+ function removeActiveDataset() {
97
+ const id = activeDatasetId.value;
98
+ if (!id) return;
99
+ props.hub.removeDataset(id);
100
+ activeDatasetId.value = datasets.value.find((item) => item.connectionId === activeConnectionId.value)?.id;
101
+ syncConnectionDraft();
102
+ }
103
+ return (_ctx, _cache) => {
104
+ return openBlock(), createElementBlock("div", { class: normalizeClass(unref(cls).b) }, [createElementVNode("header", { class: normalizeClass(unref(cls).e("head")) }, [createElementVNode("h3", { class: normalizeClass(unref(cls).e("title")) }, "数据中枢", 2), createVNode(unref(UButton), {
105
+ size: "small",
106
+ type: "primary",
107
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("close"))
108
+ }, {
109
+ default: withCtx(() => [..._cache[1] || (_cache[1] = [createTextVNode("完成", -1)])]),
110
+ _: 1
111
+ })], 2), createElementVNode("div", { class: normalizeClass(unref(cls).e("body")) }, [createElementVNode("aside", { class: normalizeClass(unref(cls).e("sidebar")) }, [createElementVNode("div", { class: normalizeClass(unref(cls).e("sidebar-head")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("sidebar-title")) }, "连接与数据集", 2), createVNode(unref(UButton), {
112
+ size: "small",
113
+ plain: "",
114
+ onClick: startNewConnection
115
+ }, {
116
+ default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode("新建连接", -1)])]),
117
+ _: 1
118
+ })], 2), createElementVNode("div", { class: normalizeClass(unref(cls).e("tree")) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(connections.value, (conn) => {
119
+ return openBlock(), createElementBlock("section", {
120
+ key: conn.id,
121
+ class: normalizeClass(unref(cls).e("conn-group"))
122
+ }, [
123
+ createElementVNode("div", {
124
+ class: normalizeClass([unref(cls).e("conn"), unref(bem).is("active", activeConnectionId.value === conn.id && !activeDatasetId.value)]),
125
+ onClick: ($event) => selectConnection(conn.id)
126
+ }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("conn-label")) }, toDisplayString(conn.label), 3), createVNode(unref(UTag), {
127
+ size: "small",
128
+ type: "info"
129
+ }, {
130
+ default: withCtx(() => [createTextVNode(toDisplayString(conn.type), 1)]),
131
+ _: 2
132
+ }, 1024)], 10, _hoisted_1),
133
+ createElementVNode("ul", { class: normalizeClass(unref(cls).e("dataset-list")) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(datasetsByConnection(conn.id), (item) => {
134
+ return openBlock(), createElementBlock("li", {
135
+ key: item.id,
136
+ class: normalizeClass([unref(cls).e("dataset-item"), unref(bem).is("active", activeDatasetId.value === item.id)]),
137
+ onClick: ($event) => selectDataset(item.id)
138
+ }, [createElementVNode("span", null, toDisplayString(item.label), 1), createElementVNode("span", { class: normalizeClass(unref(cls).e("dataset-id")) }, toDisplayString(item.id), 3)], 10, _hoisted_2);
139
+ }), 128))], 2),
140
+ createVNode(unref(UButton), {
141
+ size: "small",
142
+ text: "",
143
+ class: normalizeClass(unref(cls).e("add-dataset")),
144
+ onClick: ($event) => addDataset(conn.id)
145
+ }, {
146
+ default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode(" + 新建数据集 ", -1)])]),
147
+ _: 1
148
+ }, 8, ["class", "onClick"])
149
+ ], 2);
150
+ }), 128)), connections.value.length === 0 ? (openBlock(), createElementBlock("p", {
151
+ key: 0,
152
+ class: normalizeClass(unref(cls).e("sidebar-empty"))
153
+ }, " 尚无连接。点「新建连接」配置 MySQL / PostgreSQL 数据连接。 ", 2)) : createCommentVNode("v-if", true)], 2)], 2), createElementVNode("div", { class: normalizeClass(unref(cls).e("main")) }, [activeDatasetId.value ? (openBlock(), createBlock(_sfc_main$2, {
154
+ key: 0,
155
+ hub: __props.hub,
156
+ "dataset-id": activeDatasetId.value,
157
+ onRemove: removeActiveDataset
158
+ }, null, 8, ["hub", "dataset-id"])) : connectionDraft.value ? (openBlock(), createBlock(_sfc_main$1, {
159
+ key: 1,
160
+ connection: connectionDraft.value,
161
+ "is-new": creatingConnection.value,
162
+ test: __props.hub.testConnection,
163
+ onSave: saveConnection,
164
+ onRemove: removeActiveConnection
165
+ }, null, 8, [
166
+ "connection",
167
+ "is-new",
168
+ "test"
169
+ ])) : (openBlock(), createElementBlock("div", {
170
+ key: 2,
171
+ class: normalizeClass(unref(cls).e("empty"))
172
+ }, [..._cache[4] || (_cache[4] = [createElementVNode("p", null, "选择左侧连接或数据集开始配置;数据集字段解析后即可在字段面板拖拽绑定。", -1)])], 2))], 2)], 2)], 2);
173
+ };
174
+ }
175
+ });
176
+ //#endregion
177
+ export { _sfc_main as default };
@@ -0,0 +1,33 @@
1
+ //#region src/components/report/field-panel-helpers.ts
2
+ /** 字段拖拽的 dataTransfer MIME(字段面板 dragstart / 网格 drop 共用) */
3
+ const FIELD_DRAG_MIME = "application/x-sheet-report-field";
4
+ /** 字段拖拽负载:`${datasetId}:${fieldName}` */
5
+ function formatFieldDragPayload(datasetId, fieldName) {
6
+ return `${datasetId}:${fieldName}`;
7
+ }
8
+ /** 解析字段拖拽负载;非法输入返回 null */
9
+ function parseFieldDragPayload(raw) {
10
+ const sep = raw.indexOf(":");
11
+ if (sep <= 0) return null;
12
+ const fieldName = raw.slice(sep + 1);
13
+ if (!fieldName) return null;
14
+ return {
15
+ datasetId: raw.slice(0, sep),
16
+ fieldName
17
+ };
18
+ }
19
+ function fieldTypeGlyph(type) {
20
+ if (type === "number") return "#";
21
+ if (type === "date") return "日";
22
+ return "文";
23
+ }
24
+ function filterDatasetsByQuery(datasets, query) {
25
+ const keyword = query.trim().toLowerCase();
26
+ if (!keyword) return datasets;
27
+ return datasets.map((dataset) => ({
28
+ ...dataset,
29
+ fields: dataset.fields.filter((field) => field.label.toLowerCase().includes(keyword) || field.name.toLowerCase().includes(keyword))
30
+ })).filter((dataset) => dataset.fields.length > 0);
31
+ }
32
+ //#endregion
33
+ export { FIELD_DRAG_MIME, fieldTypeGlyph, filterDatasetsByQuery, formatFieldDragPayload, parseFieldDragPayload };
@@ -0,0 +1,132 @@
1
+ import { FIELD_DRAG_MIME, fieldTypeGlyph, filterDatasetsByQuery, formatFieldDragPayload } from "./field-panel-helpers.js";
2
+ import { Fragment, computed, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, renderList, toDisplayString, unref, vShow, withCtx, withDirectives, withKeys, withModifiers } from "vue";
3
+ import { UButton, UInput, UTag } from "@veltra/desktop";
4
+ import { bem } from "@veltra/utils";
5
+ //#region src/components/report/field-panel.vue
6
+ const _hoisted_1 = [
7
+ "title",
8
+ "onClick",
9
+ "onKeydown",
10
+ "onDragstart"
11
+ ];
12
+ const _hoisted_2 = ["data-type"];
13
+ const _sfc_main = /*@__PURE__*/ defineComponent({
14
+ name: "UReportFieldPanel",
15
+ __name: "field-panel",
16
+ props: {
17
+ datasets: {},
18
+ selectionLabel: {},
19
+ boundKeys: {}
20
+ },
21
+ emits: ["bind"],
22
+ setup(__props, { emit: __emit }) {
23
+ const props = __props;
24
+ const emit = __emit;
25
+ const cls = bem("report-field-panel");
26
+ const searchQuery = ref("");
27
+ const collapsedDatasetIds = ref(/* @__PURE__ */ new Set());
28
+ const visibleDatasets = computed(() => filterDatasetsByQuery(props.datasets, searchQuery.value));
29
+ /** 折叠集合;搜索时视为全部展开(返回空集) */
30
+ const effectiveCollapsed = computed(() => {
31
+ if (!searchQuery.value.trim()) return collapsedDatasetIds.value;
32
+ return /* @__PURE__ */ new Set();
33
+ });
34
+ function isDatasetExpanded(datasetId) {
35
+ return !effectiveCollapsed.value.has(datasetId);
36
+ }
37
+ function isBound(datasetId, fieldName) {
38
+ return props.boundKeys.has(`${datasetId}:${fieldName}`);
39
+ }
40
+ function toggleDataset(datasetId) {
41
+ const next = new Set(collapsedDatasetIds.value);
42
+ if (next.has(datasetId)) next.delete(datasetId);
43
+ else next.add(datasetId);
44
+ collapsedDatasetIds.value = next;
45
+ }
46
+ function onDragStart(event, datasetId, fieldName) {
47
+ event.dataTransfer?.setData(FIELD_DRAG_MIME, formatFieldDragPayload(datasetId, fieldName));
48
+ if (event.dataTransfer) event.dataTransfer.effectAllowed = "copy";
49
+ const target = event.currentTarget;
50
+ if (event.dataTransfer && target instanceof HTMLElement) event.dataTransfer.setDragImage(target, Math.round(target.offsetWidth / 2), Math.round(target.offsetHeight / 2));
51
+ }
52
+ return (_ctx, _cache) => {
53
+ return openBlock(), createElementBlock("aside", { class: normalizeClass(unref(cls).b) }, [
54
+ createElementVNode("h3", { class: normalizeClass(unref(cls).e("title")) }, "数据集字段", 2),
55
+ createElementVNode("p", { class: normalizeClass(unref(cls).e("meta")) }, [_cache[1] || (_cache[1] = createTextVNode(" 当前选区: ", -1)), createVNode(unref(UTag), {
56
+ size: "small",
57
+ type: "primary"
58
+ }, {
59
+ default: withCtx(() => [createTextVNode(toDisplayString(__props.selectionLabel), 1)]),
60
+ _: 1
61
+ })], 2),
62
+ createVNode(unref(UInput), {
63
+ modelValue: searchQuery.value,
64
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchQuery.value = $event),
65
+ class: normalizeClass(unref(cls).e("search")),
66
+ size: "small",
67
+ clearable: "",
68
+ placeholder: "搜索字段…"
69
+ }, null, 8, ["modelValue", "class"]),
70
+ createElementVNode("p", { class: normalizeClass(unref(cls).e("hint")) }, "点击或拖拽字段到网格;中文名为主,英文键为绑定标识。", 2),
71
+ (openBlock(true), createElementBlock(Fragment, null, renderList(visibleDatasets.value, (dataset) => {
72
+ return openBlock(), createElementBlock("section", {
73
+ key: dataset.id,
74
+ class: normalizeClass(unref(cls).e("dataset"))
75
+ }, [createVNode(unref(UButton), {
76
+ text: "",
77
+ size: "small",
78
+ class: normalizeClass(unref(cls).e("dataset-toggle")),
79
+ "aria-expanded": isDatasetExpanded(dataset.id),
80
+ onClick: ($event) => toggleDataset(dataset.id)
81
+ }, {
82
+ default: withCtx(() => [
83
+ createElementVNode("span", {
84
+ class: normalizeClass([unref(cls).e("dataset-chevron"), unref(bem).is("expanded", isDatasetExpanded(dataset.id))]),
85
+ "aria-hidden": "true"
86
+ }, " ▸ ", 2),
87
+ createElementVNode("span", { class: normalizeClass(unref(cls).e("dataset-name")) }, toDisplayString(dataset.label), 3),
88
+ createElementVNode("span", { class: normalizeClass(unref(cls).e("dataset-count")) }, toDisplayString(dataset.fields.length), 3)
89
+ ]),
90
+ _: 2
91
+ }, 1032, [
92
+ "class",
93
+ "aria-expanded",
94
+ "onClick"
95
+ ]), withDirectives(createElementVNode("div", { class: normalizeClass(unref(cls).e("fields")) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(dataset.fields, (field) => {
96
+ return openBlock(), createElementBlock("div", {
97
+ key: field.name,
98
+ role: "button",
99
+ tabindex: "0",
100
+ class: normalizeClass([unref(cls).e("pill"), unref(bem).is("bound", isBound(dataset.id, field.name))]),
101
+ draggable: "true",
102
+ title: `${field.label}(${field.name})`,
103
+ onClick: ($event) => emit("bind", dataset.id, field.name),
104
+ onKeydown: [withKeys(withModifiers(($event) => emit("bind", dataset.id, field.name), ["prevent"]), ["enter"]), withKeys(withModifiers(($event) => emit("bind", dataset.id, field.name), ["prevent"]), ["space"])],
105
+ onDragstart: ($event) => onDragStart($event, dataset.id, field.name)
106
+ }, [
107
+ createElementVNode("span", {
108
+ class: normalizeClass(unref(cls).e("pill-icon")),
109
+ "data-type": field.type
110
+ }, toDisplayString(unref(fieldTypeGlyph)(field.type)), 11, _hoisted_2),
111
+ createElementVNode("span", { class: normalizeClass(unref(cls).e("pill-label")) }, toDisplayString(field.label), 3),
112
+ isBound(dataset.id, field.name) ? (openBlock(), createElementBlock("span", {
113
+ key: 0,
114
+ class: normalizeClass(unref(cls).e("pill-bound")),
115
+ title: "已绑定"
116
+ }, "✓", 2)) : createCommentVNode("v-if", true)
117
+ ], 42, _hoisted_1);
118
+ }), 128))], 2), [[vShow, isDatasetExpanded(dataset.id)]])], 2);
119
+ }), 128)),
120
+ __props.datasets.length === 0 ? (openBlock(), createElementBlock("p", {
121
+ key: 0,
122
+ class: normalizeClass(unref(cls).e("empty"))
123
+ }, " 尚无可用数据集,请点「数据中枢」创建连接与 SQL 数据集。 ", 2)) : visibleDatasets.value.length === 0 ? (openBlock(), createElementBlock("p", {
124
+ key: 1,
125
+ class: normalizeClass(unref(cls).e("empty"))
126
+ }, "没有匹配的字段。", 2)) : createCommentVNode("v-if", true)
127
+ ], 2);
128
+ };
129
+ }
130
+ });
131
+ //#endregion
132
+ export { _sfc_main as default };
@@ -0,0 +1,74 @@
1
+ import { parseDateRangeValue, patchParamValues, resolveNumberParamValue } from "../../report/filter-bar.js";
2
+ import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, openBlock, renderList, toDisplayString, unref } from "vue";
3
+ import { UDatePicker, UDateRangePicker, UInput, UNumberInput, USelect } from "@veltra/desktop";
4
+ import { bem } from "@veltra/utils";
5
+ //#region src/components/report/filter-bar.vue
6
+ const _sfc_main = /*@__PURE__*/ defineComponent({
7
+ name: "UReportFilterBar",
8
+ __name: "filter-bar",
9
+ props: {
10
+ queryParams: {},
11
+ values: {}
12
+ },
13
+ emits: ["update:values"],
14
+ setup(__props, { emit: __emit }) {
15
+ const props = __props;
16
+ const emit = __emit;
17
+ const cls = bem("report-filter-bar");
18
+ function dateRangeModel(paramId) {
19
+ return parseDateRangeValue(props.values[paramId]);
20
+ }
21
+ function numberModel(paramId) {
22
+ return resolveNumberParamValue(props.values[paramId]);
23
+ }
24
+ function emitChange(id, value) {
25
+ emit("update:values", patchParamValues(props.values, id, value));
26
+ }
27
+ function emitDateRangeChange(id, value) {
28
+ emit("update:values", patchParamValues(props.values, id, value ?? ["", ""]));
29
+ }
30
+ return (_ctx, _cache) => {
31
+ return __props.queryParams.length ? (openBlock(), createElementBlock("section", {
32
+ key: 0,
33
+ class: normalizeClass(unref(cls).b)
34
+ }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("label")) }, "筛选参数", 2), createElementVNode("div", { class: normalizeClass(unref(cls).e("fields")) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.queryParams, (param) => {
35
+ return openBlock(), createElementBlock("div", {
36
+ key: param.id,
37
+ class: normalizeClass(unref(cls).e("field"))
38
+ }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("field-label")) }, toDisplayString(param.label), 3), param.type === "date" ? (openBlock(), createBlock(unref(UDatePicker), {
39
+ key: 0,
40
+ "model-value": String(__props.values[param.id] ?? ""),
41
+ size: "small",
42
+ "onUpdate:modelValue": (v) => emitChange(param.id, v)
43
+ }, null, 8, ["model-value", "onUpdate:modelValue"])) : param.type === "date-range" ? (openBlock(), createBlock(unref(UDateRangePicker), {
44
+ key: 1,
45
+ "model-value": dateRangeModel(param.id),
46
+ size: "small",
47
+ "onUpdate:modelValue": (v) => emitDateRangeChange(param.id, v)
48
+ }, null, 8, ["model-value", "onUpdate:modelValue"])) : param.type === "select" ? (openBlock(), createBlock(unref(USelect), {
49
+ key: 2,
50
+ "model-value": String(__props.values[param.id] ?? ""),
51
+ size: "small",
52
+ options: param.options ?? [],
53
+ "onUpdate:modelValue": (v) => emitChange(param.id, v)
54
+ }, null, 8, [
55
+ "model-value",
56
+ "options",
57
+ "onUpdate:modelValue"
58
+ ])) : param.type === "number" ? (openBlock(), createBlock(unref(UNumberInput), {
59
+ key: 3,
60
+ "model-value": numberModel(param.id),
61
+ size: "small",
62
+ "onUpdate:modelValue": (v) => emitChange(param.id, v)
63
+ }, null, 8, ["model-value", "onUpdate:modelValue"])) : (openBlock(), createBlock(unref(UInput), {
64
+ key: 4,
65
+ "model-value": String(__props.values[param.id] ?? ""),
66
+ size: "small",
67
+ "onUpdate:modelValue": (v) => emitChange(param.id, v)
68
+ }, null, 8, ["model-value", "onUpdate:modelValue"]))], 2);
69
+ }), 128))], 2)], 2)) : createCommentVNode("v-if", true);
70
+ };
71
+ }
72
+ });
73
+ //#endregion
74
+ export { _sfc_main as default };
@@ -0,0 +1,145 @@
1
+ import { DEFAULT_CONNECTION_PORTS, resolvePortOnTypeChange } from "../../report/connector.js";
2
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, reactive, ref, toDisplayString, unref, watch, withCtx } from "vue";
3
+ import { UButton, UInput, USelect } from "@veltra/desktop";
4
+ import { bem } from "@veltra/utils";
5
+ //#region src/components/report/hub-connection-form.vue
6
+ const _sfc_main = /*@__PURE__*/ defineComponent({
7
+ name: "UReportHubConnectionForm",
8
+ __name: "hub-connection-form",
9
+ props: {
10
+ connection: {},
11
+ isNew: { type: Boolean },
12
+ test: { type: Function }
13
+ },
14
+ emits: ["save", "remove"],
15
+ setup(__props, { emit: __emit }) {
16
+ const props = __props;
17
+ const emit = __emit;
18
+ const cls = bem("report-hub-connection-form");
19
+ /** 连接类型收敛为 MySQL / PostgreSQL(ADR-0003 决策 3) */
20
+ const typeOptions = [{
21
+ label: "MySQL",
22
+ value: "mysql"
23
+ }, {
24
+ label: "PostgreSQL",
25
+ value: "postgresql"
26
+ }];
27
+ const draft = reactive({ ...props.connection });
28
+ const defaultPort = computed(() => DEFAULT_CONNECTION_PORTS[draft.type]);
29
+ const portText = computed({
30
+ get: () => String(draft.port),
31
+ set: (value) => {
32
+ const n = Number(value);
33
+ draft.port = Number.isFinite(n) ? n : 0;
34
+ }
35
+ });
36
+ const testing = ref(false);
37
+ const testMessage = ref("");
38
+ const testOk = ref(false);
39
+ watch(() => props.connection, (conn) => {
40
+ testMessage.value = "";
41
+ testOk.value = false;
42
+ Object.assign(draft, conn);
43
+ }, { immediate: true });
44
+ watch(() => draft.type, (type, prevType) => {
45
+ if (!prevType) return;
46
+ draft.port = resolvePortOnTypeChange(prevType, type, draft.port);
47
+ });
48
+ async function onTest() {
49
+ testing.value = true;
50
+ testMessage.value = "";
51
+ const result = await props.test({ ...draft });
52
+ testing.value = false;
53
+ testOk.value = result.ok;
54
+ testMessage.value = result.ok ? `连接成功:${draft.label}(${draft.host}:${draft.port}/${draft.database})` : result.error.message;
55
+ }
56
+ function onSave() {
57
+ emit("save", { ...draft });
58
+ }
59
+ return (_ctx, _cache) => {
60
+ return openBlock(), createElementBlock("section", { class: normalizeClass(unref(cls).b) }, [
61
+ createElementVNode("h4", { class: normalizeClass(unref(cls).e("title")) }, toDisplayString(__props.isNew ? "新建连接" : "编辑连接"), 3),
62
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("grid")) }, [
63
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("field")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("label")) }, "显示名", 2), createVNode(unref(UInput), {
64
+ modelValue: draft.label,
65
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => draft.label = $event),
66
+ size: "small",
67
+ placeholder: "连接名称"
68
+ }, null, 8, ["modelValue"])], 2),
69
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("field")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("label")) }, "类型", 2), createVNode(unref(USelect), {
70
+ modelValue: draft.type,
71
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => draft.type = $event),
72
+ size: "small",
73
+ options: typeOptions
74
+ }, null, 8, ["modelValue"])], 2),
75
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("field")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("label")) }, "主机", 2), createVNode(unref(UInput), {
76
+ modelValue: draft.host,
77
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => draft.host = $event),
78
+ size: "small",
79
+ placeholder: "127.0.0.1"
80
+ }, null, 8, ["modelValue"])], 2),
81
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("field")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("label")) }, "端口", 2), createVNode(unref(UInput), {
82
+ modelValue: portText.value,
83
+ "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => portText.value = $event),
84
+ size: "small",
85
+ type: "number",
86
+ placeholder: String(defaultPort.value)
87
+ }, null, 8, ["modelValue", "placeholder"])], 2),
88
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("field")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("label")) }, "数据库", 2), createVNode(unref(UInput), {
89
+ modelValue: draft.database,
90
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => draft.database = $event),
91
+ size: "small",
92
+ placeholder: "数据库名"
93
+ }, null, 8, ["modelValue"])], 2),
94
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("field")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("label")) }, "用户名", 2), createVNode(unref(UInput), {
95
+ modelValue: draft.username,
96
+ "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => draft.username = $event),
97
+ size: "small"
98
+ }, null, 8, ["modelValue"])], 2),
99
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("field")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("label")) }, "密码", 2), createVNode(unref(UInput), {
100
+ modelValue: draft.password,
101
+ "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => draft.password = $event),
102
+ size: "small",
103
+ type: "password",
104
+ "show-password": ""
105
+ }, null, 8, ["modelValue"])], 2)
106
+ ], 2),
107
+ testMessage.value ? (openBlock(), createElementBlock("div", {
108
+ key: 0,
109
+ class: normalizeClass([unref(cls).e("test"), unref(bem).is("ok", testOk.value)]),
110
+ role: "status"
111
+ }, toDisplayString(testMessage.value), 3)) : createCommentVNode("v-if", true),
112
+ createElementVNode("div", { class: normalizeClass(unref(cls).e("actions")) }, [
113
+ createVNode(unref(UButton), {
114
+ size: "small",
115
+ loading: testing.value,
116
+ onClick: onTest
117
+ }, {
118
+ default: withCtx(() => [..._cache[8] || (_cache[8] = [createTextVNode("测试连接", -1)])]),
119
+ _: 1
120
+ }, 8, ["loading"]),
121
+ !__props.isNew ? (openBlock(), createBlock(unref(UButton), {
122
+ key: 0,
123
+ size: "small",
124
+ type: "danger",
125
+ plain: "",
126
+ onClick: _cache[7] || (_cache[7] = ($event) => emit("remove"))
127
+ }, {
128
+ default: withCtx(() => [..._cache[9] || (_cache[9] = [createTextVNode(" 删除连接 ", -1)])]),
129
+ _: 1
130
+ })) : createCommentVNode("v-if", true),
131
+ createVNode(unref(UButton), {
132
+ size: "small",
133
+ type: "primary",
134
+ onClick: onSave
135
+ }, {
136
+ default: withCtx(() => [..._cache[10] || (_cache[10] = [createTextVNode("保存", -1)])]),
137
+ _: 1
138
+ })
139
+ ], 2)
140
+ ], 2);
141
+ };
142
+ }
143
+ });
144
+ //#endregion
145
+ export { _sfc_main as default };