@veltra/desktop 1.7.13 → 1.7.16

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 (89) hide show
  1. package/dist/components/batch-edit/batch-edit-form.js +9 -3
  2. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  3. package/dist/components/batch-edit/batch-edit-list.js +2 -1
  4. package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
  5. package/dist/components/batch-edit/batch-edit.js +57 -30
  6. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  7. package/dist/components/batch-edit/style.d.ts +1 -0
  8. package/dist/components/batch-edit/style.js +1 -0
  9. package/dist/components/batch-edit/style2.css +14 -0
  10. package/dist/components/batch-edit/use-dialog-effect.js +20 -0
  11. package/dist/components/batch-edit/use-dialog-effect.js.map +1 -0
  12. package/dist/components/batch-edit/use-edit-state.js +22 -3
  13. package/dist/components/batch-edit/use-edit-state.js.map +1 -1
  14. package/dist/components/batch-edit/use-handlers.js +5 -0
  15. package/dist/components/batch-edit/use-handlers.js.map +1 -1
  16. package/dist/components/batch-edit/use-shortcut-key.js +5 -2
  17. package/dist/components/batch-edit/use-shortcut-key.js.map +1 -1
  18. package/dist/components/button/button.js +2 -2
  19. package/dist/components/button/button.js.map +1 -1
  20. package/dist/components/file-viewer/file-viewer.js +21 -14
  21. package/dist/components/file-viewer/file-viewer.js.map +1 -1
  22. package/dist/components/file-viewer/helper.js +2 -0
  23. package/dist/components/file-viewer/helper.js.map +1 -1
  24. package/dist/components/file-viewer/previewers/ofd-previewer.js +196 -0
  25. package/dist/components/file-viewer/previewers/ofd-previewer.js.map +1 -0
  26. package/dist/components/file-viewer/style2.css +27 -0
  27. package/dist/components/form-item/style2.css +17 -2
  28. package/dist/components/table-editor/style2.css +48 -5
  29. package/dist/components/table-editor/table-editor.js +228 -63
  30. package/dist/components/table-editor/table-editor.js.map +1 -1
  31. package/dist/components/tip/style2.css +63 -0
  32. package/dist/components/tip/tip.js +6 -1
  33. package/dist/components/tip/tip.js.map +1 -1
  34. package/dist/index.d.ts +3 -3
  35. package/dist/packages/ofd-core/dist/color.js +69 -0
  36. package/dist/packages/ofd-core/dist/color.js.map +1 -0
  37. package/dist/packages/ofd-core/dist/ctm.js +55 -0
  38. package/dist/packages/ofd-core/dist/ctm.js.map +1 -0
  39. package/dist/packages/ofd-core/dist/error.js +14 -0
  40. package/dist/packages/ofd-core/dist/error.js.map +1 -0
  41. package/dist/packages/ofd-core/dist/font.js +384 -0
  42. package/dist/packages/ofd-core/dist/font.js.map +1 -0
  43. package/dist/packages/ofd-core/dist/index.js +6 -0
  44. package/dist/packages/ofd-core/dist/number.js +13 -0
  45. package/dist/packages/ofd-core/dist/number.js.map +1 -0
  46. package/dist/packages/ofd-core/dist/page-objects.js +181 -0
  47. package/dist/packages/ofd-core/dist/page-objects.js.map +1 -0
  48. package/dist/packages/ofd-core/dist/parse.js +65 -0
  49. package/dist/packages/ofd-core/dist/parse.js.map +1 -0
  50. package/dist/packages/ofd-core/dist/render.js +361 -0
  51. package/dist/packages/ofd-core/dist/render.js.map +1 -0
  52. package/dist/packages/ofd-core/dist/xml.js +199 -0
  53. package/dist/packages/ofd-core/dist/xml.js.map +1 -0
  54. package/dist/packages/ofd-core/dist/zip.js +93 -0
  55. package/dist/packages/ofd-core/dist/zip.js.map +1 -0
  56. package/dist/style.d.ts +1 -1
  57. package/dist/style.js +1 -1
  58. package/dist/types/batch-edit.d.ts +6 -0
  59. package/dist/types/file-viewer.d.ts +1 -1
  60. package/dist/types/index.d.ts +3 -3
  61. package/dist/types/table-editor.d.ts +28 -7
  62. package/dist/types/tip.d.ts +5 -1
  63. package/package.json +8 -7
  64. package/src/components/batch-edit/__test__/batch-edit.test.ts +191 -4
  65. package/src/components/batch-edit/batch-edit-form.vue +17 -3
  66. package/src/components/batch-edit/batch-edit-list.vue +2 -1
  67. package/src/components/batch-edit/batch-edit.vue +22 -4
  68. package/src/components/batch-edit/style.scss +20 -0
  69. package/src/components/batch-edit/style.ts +1 -0
  70. package/src/components/batch-edit/use-dialog-effect.ts +9 -3
  71. package/src/components/batch-edit/use-edit-state.ts +32 -4
  72. package/src/components/batch-edit/use-handlers.ts +10 -0
  73. package/src/components/batch-edit/use-shortcut-key.ts +6 -2
  74. package/src/components/button/button.vue +3 -2
  75. package/src/components/file-viewer/__test__/helper.test.ts +13 -0
  76. package/src/components/file-viewer/file-viewer.vue +23 -16
  77. package/src/components/file-viewer/helper.ts +2 -0
  78. package/src/components/file-viewer/previewers/ofd-previewer.vue +216 -0
  79. package/src/components/file-viewer/style.scss +33 -0
  80. package/src/components/form-item/style.scss +11 -1
  81. package/src/components/table-editor/__test__/table-editor.test.ts +479 -0
  82. package/src/components/table-editor/style.scss +57 -5
  83. package/src/components/table-editor/table-editor.vue +286 -38
  84. package/src/components/tip/style.scss +15 -0
  85. package/src/components/tip/tip.vue +1 -1
  86. package/src/types/batch-edit.ts +6 -0
  87. package/src/types/file-viewer.ts +1 -1
  88. package/src/types/table-editor.ts +29 -7
  89. package/src/types/tip.ts +6 -1
@@ -16,11 +16,54 @@
16
16
  */
17
17
  /** 暗色:`UITheme.render()` 按主题系列写入 html[data-theme] */
18
18
  .u-table-editor .u-table__body .u-table__cell {
19
- padding: 2px;
19
+ padding: 4px;
20
20
  }
21
- .u-table-editor .u-table__body .u-table__cell [class^=u-input] {
22
- box-shadow: none;
21
+ .u-table-editor .u-table__body .u-table__cell [class^=u-] {
22
+ vertical-align: top;
23
23
  }
24
- .u-table-editor .u-table__body .u-table__cell > [class^=u-] {
25
- box-shadow: none;
24
+ .u-table-editor .is-error {
25
+ color: var(--u-color-danger);
26
+ }
27
+ .u-table-editor .is-error .u-input, .u-table-editor .is-error .u-input:hover, .u-table-editor .is-error .u-input.is-focus,
28
+ .u-table-editor .is-error .u-multi-select,
29
+ .u-table-editor .is-error .u-multi-select:hover,
30
+ .u-table-editor .is-error .u-multi-select.is-focus,
31
+ .u-table-editor .is-error .u-multi-tree-select,
32
+ .u-table-editor .is-error .u-multi-tree-select:hover,
33
+ .u-table-editor .is-error .u-multi-tree-select.is-focus,
34
+ .u-table-editor .is-error .u-auto-complete,
35
+ .u-table-editor .is-error .u-auto-complete:hover,
36
+ .u-table-editor .is-error .u-auto-complete.is-focus,
37
+ .u-table-editor .is-error .u-textarea--more,
38
+ .u-table-editor .is-error .u-textarea--more:hover,
39
+ .u-table-editor .is-error .u-textarea--more.is-focus {
40
+ border-color: var(--u-color-danger);
41
+ }
42
+ .u-table-editor .is-error .u-input.is-focus,
43
+ .u-table-editor .is-error .u-multi-select.is-focus,
44
+ .u-table-editor .is-error .u-multi-tree-select.is-focus,
45
+ .u-table-editor .is-error .u-auto-complete.is-focus,
46
+ .u-table-editor .is-error .u-textarea--more.is-focus {
47
+ box-shadow: var(--u-focus-ring-danger);
48
+ }
49
+ .u-table-editor .u-table-editor__operations {
50
+ display: flex;
51
+ justify-content: center;
52
+ gap: 4px;
53
+ }
54
+ .u-table-editor .u-table-editor__operations .u-button {
55
+ padding: 0 0.4em;
56
+ }
57
+ .u-table-editor__required-mark {
58
+ padding-right: 2px;
59
+ color: var(--u-color-danger);
60
+ }
61
+ .u-table-editor__header-text.is-error {
62
+ color: var(--u-color-danger);
63
+ }
64
+ .u-table-editor__header-icon {
65
+ color: var(--u-color-danger);
66
+ margin-left: 2px;
67
+ vertical-align: -2px;
68
+ cursor: help;
26
69
  }
@@ -1,9 +1,12 @@
1
- import _sfc_main$1 from "../button/button.js";
2
- import _sfc_main$2 from "../table/table.js";
3
- import { computed, createBlock, createCommentVNode, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, unref, withCtx } from "vue";
4
- import { useComponentProps } from "@veltra/compositions";
1
+ import _sfc_main$1 from "../icon/icon.js";
2
+ import _sfc_main$2 from "../tip/tip.js";
3
+ import { validateField } from "../form-item/validate.js";
4
+ import _sfc_main$3 from "../button/button.js";
5
+ import _sfc_main$4 from "../table/table.js";
6
+ import { computed, createBlock, createCommentVNode, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, guardReactiveProps, h, mergeProps, normalizeClass, normalizeProps, openBlock, renderList, renderSlot, shallowRef, toRaw, triggerRef, unref, useSlots, watch, withCtx } from "vue";
5
7
  import { bem } from "@veltra/utils";
6
- import { Copy, Minus, Plus } from "@veltra/icons/normal";
8
+ import { Copy, Minus, Plus, Warning } from "@veltra/icons/normal";
9
+ import { o } from "@cat-kit/core";
7
10
  //#region src/components/table-editor/table-editor.vue
8
11
  const _hoisted_1 = { style: {
9
12
  "text-align": "center",
@@ -13,9 +16,10 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
13
16
  name: "UTableEditor",
14
17
  __name: "table-editor",
15
18
  props: {
16
- modelValue: { default: () => [] },
19
+ modelValue: {},
20
+ columns: {},
21
+ readonly: { type: Boolean },
17
22
  size: {},
18
- columns: { default: () => [] },
19
23
  checked: {},
20
24
  selected: {},
21
25
  checkable: { type: Boolean },
@@ -35,82 +39,239 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
35
39
  textEllipsis: { type: Boolean }
36
40
  },
37
41
  emits: ["update:modelValue"],
38
- setup(__props, { emit: __emit }) {
42
+ setup(__props, { expose: __expose, emit: __emit }) {
43
+ const props = __props;
39
44
  const emit = __emit;
40
- const cls = bem("table-editor");
41
- const ButtonCommonProps = useComponentProps({
42
- size: "small",
43
- circle: true,
44
- text: true,
45
- type: "primary"
45
+ const columns = computed(() => props.columns ?? []);
46
+ const modelValue = computed(() => props.modelValue ?? []);
47
+ /** 转发给 u-table 的表格属性;columns / modelValue / readonly 为编辑器自用,stripe / showIndex 被覆盖 */
48
+ const tableProps = computed(() => {
49
+ const rest = { ...props };
50
+ delete rest.columns;
51
+ delete rest.modelValue;
52
+ delete rest.readonly;
53
+ return rest;
46
54
  });
55
+ const cls = bem("table-editor");
56
+ const slots = useSlots();
47
57
  const actionColumn = {
48
58
  key: "__operation",
49
59
  name: "操作",
50
- width: 120,
60
+ width: 104,
51
61
  align: "center",
52
62
  fixed: "right",
53
63
  resizable: false
54
64
  };
65
+ /**
66
+ * 内部列定义:单元格渲染挂在列的 render 上(用户列自带的 render 优先),
67
+ * 配置了 rules 的列包装 nameRender 追加校验标识,非只读时末尾追加操作列。
68
+ * 列 key 带上只读态后缀:u-table 行内单元格在 props 值相等时可能跳过子组件补丁,
69
+ * 只读切换改用挂载键直接重挂单元格,确保控件在编辑/只读形态间可靠切换。
70
+ */
55
71
  const internalColumns = computed(() => {
56
- return [...__props.columns, actionColumn];
72
+ const list = columns.value.map((column) => ({
73
+ ...column,
74
+ key: `${column.key}${props.readonly ? ":ro" : ""}`,
75
+ nameRender: column.rules ? headerCell(column) : column.nameRender,
76
+ render: column.render ?? ((ctx) => editCell(column, ctx))
77
+ }));
78
+ return props.readonly ? list : [...list, actionColumn];
79
+ });
80
+ /** 透传给 u-table 的插槽名:column:* 经 editCell 注入 model、empty 由组件接管,其余透传 */
81
+ const passSlotNames = Object.keys(slots).filter((name) => !name.startsWith("column:") && name !== "empty");
82
+ /**
83
+ * 带校验的列表头包装:required 时列名前渲染红星;该列存在未通过项时文字标红并追加
84
+ * 感叹号图标,悬停经 UTip 气泡展示各行错误明细;`#header:key` 插槽 / nameRender 内容替代列名。
85
+ * 渲染函数内读取 columnErrors 建立响应依赖,错误出现/消失即时更新表头。
86
+ */
87
+ function headerCell(column) {
88
+ return (ctx) => {
89
+ const messages = columnErrors.value.get(column.key);
90
+ const nodes = [h("span", { class: [cls.e("header-text"), bem.is("error", !!messages?.length)] }, slots[`header:${column.key}`]?.(ctx) ?? column.nameRender?.(ctx) ?? column.name)];
91
+ if (column.rules?.required) nodes.unshift(h("span", {
92
+ class: cls.e("required-mark"),
93
+ "aria-hidden": "true"
94
+ }, "*"));
95
+ if (messages?.length) nodes.push(h(_sfc_main$2, { key: "header-error-tip" }, {
96
+ default: () => h(_sfc_main$1, {
97
+ size: 14,
98
+ class: cls.e("header-icon")
99
+ }, { default: () => h(Warning) }),
100
+ content: () => messages.map((message) => h("div", { key: message }, message))
101
+ }));
102
+ return nodes;
103
+ };
104
+ }
105
+ /** 行数据(以原始对象为键)→ 列 key → 错误信息 */
106
+ const cellErrors = shallowRef(/* @__PURE__ */ new Map());
107
+ /** 行节点 data 可能是响应式代理,统一取原始对象作为键 */
108
+ const errorRowKey = (rowData) => toRaw(rowData);
109
+ function setCellError(rowData, key, message) {
110
+ const row = errorRowKey(rowData);
111
+ const rowErrors = cellErrors.value.get(row);
112
+ if (!message) {
113
+ if (!rowErrors?.delete(key)) return;
114
+ if (!rowErrors.size) cellErrors.value.delete(row);
115
+ } else {
116
+ if (rowErrors?.get(key) === message) return;
117
+ if (rowErrors) rowErrors.set(key, message);
118
+ else cellErrors.value.set(row, /* @__PURE__ */ new Map([[key, message]]));
119
+ }
120
+ triggerRef(cellErrors);
121
+ }
122
+ /** 丢弃同一单元格过期的异步校验结果 */
123
+ const validateSeqs = /* @__PURE__ */ new WeakMap();
124
+ /** 单元格级校验:以行数据为 formData、列 key 为 field,结果同步到错误状态 */
125
+ async function validateCell(rowData, key, rules) {
126
+ if (!rules) return;
127
+ const row = errorRowKey(rowData);
128
+ let seqs = validateSeqs.get(row);
129
+ if (!seqs) {
130
+ seqs = /* @__PURE__ */ new Map();
131
+ validateSeqs.set(row, seqs);
132
+ }
133
+ const seq = (seqs.get(key) ?? 0) + 1;
134
+ seqs.set(key, seq);
135
+ const message = await validateField(rowData, key, rules);
136
+ if (seqs.get(key) !== seq) return;
137
+ if (!modelValue.value.some((item) => errorRowKey(item) === row)) return;
138
+ setCellError(row, key, message);
139
+ }
140
+ /**
141
+ * 整表校验:并行校验所有行的规则列(不懒校验),未通过的单元格全部标出。
142
+ * 全部通过 resolve true,否则 resolve false。
143
+ */
144
+ async function validate() {
145
+ const ruleColumns = columns.value.filter((column) => column.rules);
146
+ await Promise.all(modelValue.value.map((row) => Promise.all(ruleColumns.map((column) => validateCell(row, column.key, column.rules)))));
147
+ return cellErrors.value.size === 0;
148
+ }
149
+ watch(modelValue, (rows) => {
150
+ if (!cellErrors.value.size) return;
151
+ const aliveRows = new Set(rows.map(errorRowKey));
152
+ const next = /* @__PURE__ */ new Map();
153
+ let changed = false;
154
+ for (const [row, errors] of cellErrors.value) if (aliveRows.has(row)) next.set(row, errors);
155
+ else changed = true;
156
+ if (changed) cellErrors.value = next;
157
+ });
158
+ /** 列 key → 该列全部未通过信息(带行序号),驱动表头错误标识 */
159
+ const columnErrors = computed(() => {
160
+ const map = /* @__PURE__ */ new Map();
161
+ if (!cellErrors.value.size) return map;
162
+ for (const [row, errors] of cellErrors.value) {
163
+ const index = modelValue.value.findIndex((item) => errorRowKey(item) === row);
164
+ const label = index >= 0 ? `第 ${index + 1} 行:` : "";
165
+ for (const [key, message] of errors) {
166
+ const list = map.get(key) ?? [];
167
+ list.push(`${label}${message}`);
168
+ map.set(key, list);
169
+ }
170
+ }
171
+ return map;
57
172
  });
173
+ /**
174
+ * 声明了 `#column:key` 插槽的列调用编辑插槽(注入 model),未声明的渲染字段原始值。
175
+ * 配置了 rules 的列在渲染期间读取 cellErrors 建立依赖,错误出现/消失即时更新:
176
+ * 插槽内容常驻包一层容器、仅切换 is-error 类(与表单项错误态同类,不增删 DOM,
177
+ * 控件不因错误态变化重挂丢焦点);纯文本列仅在出错时包一层标红原始值。
178
+ */
179
+ function editCell(column, ctx) {
180
+ const editSlot = slots[`column:${column.key}`];
181
+ const val = o(ctx.rowData).get(column.key);
182
+ if (!column.rules) {
183
+ if (!editSlot) return val;
184
+ const scope = {
185
+ ...ctx,
186
+ val
187
+ };
188
+ return editSlot({
189
+ ...scope,
190
+ model: createCellModel(column, scope)
191
+ });
192
+ }
193
+ const hasError = !!cellErrors.value.get(errorRowKey(ctx.rowData))?.has(column.key);
194
+ if (!editSlot) return hasError ? h("span", { class: bem.is("error") }, val) : val;
195
+ const scope = {
196
+ ...ctx,
197
+ val
198
+ };
199
+ return h("span", { class: bem.is("error", hasError) }, editSlot({
200
+ ...scope,
201
+ model: createCellModel(column, scope)
202
+ }));
203
+ }
204
+ /**
205
+ * 单元格编辑 model:值先原地写回行数据(行节点与 DOM 得以复用,输入不丢焦点),
206
+ * 再以浅拷贝数组经 update:modelValue 通知宿主,维持 v-model 契约。
207
+ * 只读下注入 readonly: true 且不提供写回通道,值不可修改;
208
+ * 列配置了 rules 时,控件的 change 事件(如失焦提交)触发单元格级校验,输入过程不校验。
209
+ */
210
+ function createCellModel(column, ctx) {
211
+ const { rowData, val } = ctx;
212
+ const key = column.key;
213
+ const rules = column.rules;
214
+ return {
215
+ modelValue: val,
216
+ ...props.readonly ? { readonly: true } : { "onUpdate:modelValue": (value) => {
217
+ rowData[key] = value;
218
+ emit("update:modelValue", [...modelValue.value]);
219
+ } },
220
+ ...rules ? { onChange: () => void validateCell(rowData, key, rules) } : {}
221
+ };
222
+ }
58
223
  function handleCreate(index) {
59
- if (index !== void 0) emit("update:modelValue", [
60
- ...__props.modelValue.slice(0, index + 1),
224
+ const at = index !== void 0 ? index + 1 : modelValue.value.length;
225
+ emit("update:modelValue", [
226
+ ...modelValue.value.slice(0, at),
61
227
  {},
62
- ...__props.modelValue.slice(index + 1)
228
+ ...modelValue.value.slice(at)
63
229
  ]);
64
- else emit("update:modelValue", [...__props.modelValue, {}]);
65
230
  }
66
231
  function handleCopy(row) {
67
232
  emit("update:modelValue", [
68
- ...__props.modelValue.slice(0, row.index + 1),
233
+ ...modelValue.value.slice(0, row.index + 1),
69
234
  JSON.parse(JSON.stringify(row.data)),
70
- ...__props.modelValue.slice(row.index + 1)
235
+ ...modelValue.value.slice(row.index + 1)
71
236
  ]);
72
237
  }
73
238
  function handleDelete(index) {
74
- emit("update:modelValue", __props.modelValue.filter((_, i) => i !== index));
239
+ emit("update:modelValue", modelValue.value.filter((_, i) => i !== index));
75
240
  }
241
+ __expose({ validate });
76
242
  return (_ctx, _cache) => {
77
- return openBlock(), createBlock(unref(_sfc_main$2), {
243
+ return openBlock(), createBlock(unref(_sfc_main$4), mergeProps(tableProps.value, {
78
244
  columns: internalColumns.value,
79
- data: __props.modelValue || [],
80
- class: normalizeClass(unref(cls).b),
245
+ data: modelValue.value,
246
+ class: unref(cls).b,
81
247
  stripe: false,
82
- "show-index": "",
83
- slots: _ctx.$slots
84
- }, {
85
- "column:__operation": withCtx(({ row }) => [createVNode(unref(ButtonCommonProps), {
86
- tag: "div",
87
- style: {
88
- "display": "flex",
89
- "justify-content": "center",
90
- "gap": "8px"
91
- }
92
- }, {
93
- default: withCtx(() => [
94
- createVNode(unref(_sfc_main$1), {
95
- type: "danger",
96
- onClick: ($event) => handleDelete(row.index),
97
- icon: unref(Minus),
98
- title: "移除"
99
- }, null, 8, ["onClick", "icon"]),
100
- createVNode(unref(_sfc_main$1), {
101
- icon: unref(Plus),
102
- onClick: ($event) => handleCreate(row.index),
103
- title: "新增到下一行"
104
- }, null, 8, ["icon", "onClick"]),
105
- createVNode(unref(_sfc_main$1), {
106
- icon: unref(Copy),
107
- onClick: ($event) => handleCopy(row),
108
- title: "复制到下一行"
109
- }, null, 8, ["icon", "onClick"])
110
- ]),
111
- _: 2
112
- }, 1024)]),
113
- empty: withCtx(() => [createElementVNode("div", _hoisted_1, [!__props.modelValue.length ? (openBlock(), createBlock(unref(_sfc_main$1), {
248
+ "show-index": ""
249
+ }), createSlots({
250
+ "column:__operation": withCtx(({ row }) => [createElementVNode("div", { class: normalizeClass(unref(cls).e("operations")) }, [
251
+ createVNode(unref(_sfc_main$3), {
252
+ text: "",
253
+ size: "small",
254
+ type: "danger",
255
+ icon: unref(Minus),
256
+ title: "移除",
257
+ onClick: ($event) => handleDelete(row.index)
258
+ }, null, 8, ["icon", "onClick"]),
259
+ createVNode(unref(_sfc_main$3), {
260
+ text: "",
261
+ size: "small",
262
+ icon: unref(Plus),
263
+ title: "新增到下一行",
264
+ onClick: ($event) => handleCreate(row.index)
265
+ }, null, 8, ["icon", "onClick"]),
266
+ createVNode(unref(_sfc_main$3), {
267
+ text: "",
268
+ size: "small",
269
+ icon: unref(Copy),
270
+ title: "复制到下一行",
271
+ onClick: ($event) => handleCopy(row)
272
+ }, null, 8, ["icon", "onClick"])
273
+ ], 2)]),
274
+ empty: withCtx(() => [createElementVNode("div", _hoisted_1, [!props.readonly ? (openBlock(), createBlock(unref(_sfc_main$3), {
114
275
  key: 0,
115
276
  text: "",
116
277
  size: "small",
@@ -118,15 +279,19 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
118
279
  icon: unref(Plus),
119
280
  onClick: _cache[0] || (_cache[0] = ($event) => handleCreate())
120
281
  }, {
121
- default: withCtx(() => [..._cache[1] || (_cache[1] = [createTextVNode("添加", -1)])]),
282
+ default: withCtx(() => [..._cache[1] || (_cache[1] = [createTextVNode(" 添加 ", -1)])]),
122
283
  _: 1
123
284
  }, 8, ["icon"])) : createCommentVNode("v-if", true)])]),
124
- _: 1
125
- }, 8, [
285
+ _: 2
286
+ }, [renderList(unref(passSlotNames), (name) => {
287
+ return {
288
+ name,
289
+ fn: withCtx((scope) => [renderSlot(_ctx.$slots, name, normalizeProps(guardReactiveProps(scope)))])
290
+ };
291
+ })]), 1040, [
126
292
  "columns",
127
293
  "data",
128
- "class",
129
- "slots"
294
+ "class"
130
295
  ]);
131
296
  };
132
297
  }
@@ -1 +1 @@
1
- {"version":3,"file":"table-editor.js","names":["$slots"],"sources":["../../../src/components/table-editor/table-editor.vue"],"sourcesContent":["<template>\n <u-table\n :columns=\"internalColumns\"\n :data=\"modelValue || []\"\n :class=\"cls.b\"\n :stripe=\"false\"\n show-index\n :slots=\"$slots\"\n >\n <template #column:__operation=\"{ row }\">\n <ButtonCommonProps tag=\"div\" style=\"display: flex; justify-content: center; gap: 8px\">\n <u-button type=\"danger\" @click=\"handleDelete(row.index)\" :icon=\"Minus\" title=\"移除\" />\n <u-button :icon=\"Plus\" @click=\"handleCreate(row.index)\" title=\"新增到下一行\" />\n <u-button :icon=\"Copy\" @click=\"handleCopy(row)\" title=\"复制到下一行\" />\n </ButtonCommonProps>\n </template>\n\n <!-- <template #header:__operation> </template> -->\n\n <template #empty>\n <div style=\"text-align: center; padding: 4px 0\">\n <u-button\n text\n size=\"small\"\n type=\"primary\"\n v-if=\"!modelValue.length\"\n :icon=\"Plus\"\n @click=\"handleCreate()\"\n >添加</u-button\n >\n </div>\n </template>\n </u-table>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useComponentProps } from '@veltra/compositions'\nimport { Copy, Minus, Plus } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed } from 'vue'\n\nimport type { TableEditorProps, TableEditorEmits, TableColumn } from '../../types'\nimport { UButton } from '../button'\nimport { UTable } from '../table'\nimport type { TableRowNode } from '../table/node/row'\n\ndefineOptions({ name: 'UTableEditor' })\n\nconst { columns = [], modelValue = [] } = defineProps<TableEditorProps>()\nconst emit = defineEmits<TableEditorEmits>()\n\nconst cls = bem('table-editor')\n\nconst ButtonCommonProps = useComponentProps({\n size: 'small',\n circle: true,\n text: true,\n type: 'primary'\n})\n\nconst actionColumn: TableColumn = {\n key: '__operation',\n name: '操作', // 使用name而不是title\n width: 120,\n align: 'center',\n fixed: 'right',\n resizable: false\n}\n\n// 内部列定义,添加编辑和删除操作列\nconst internalColumns = computed(() => {\n return [...columns, actionColumn]\n})\n\nfunction handleCreate(index?: number) {\n if (index !== undefined) {\n emit('update:modelValue', [\n ...modelValue.slice(0, index + 1),\n {},\n ...modelValue.slice(index + 1)\n ])\n } else {\n emit('update:modelValue', [...modelValue, {}])\n }\n}\n\nfunction handleCopy(row: TableRowNode) {\n emit('update:modelValue', [\n ...modelValue.slice(0, row.index + 1),\n JSON.parse(JSON.stringify(row.data)),\n ...modelValue.slice(row.index + 1)\n ])\n}\n\nfunction handleDelete(index: number) {\n emit(\n 'update:modelValue',\n modelValue.filter((_, i) => i !== index)\n )\n}\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiDA,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,cAAc;EAE9B,MAAM,oBAAoB,kBAAkB;GAC1C,MAAM;GACN,QAAQ;GACR,MAAM;GACN,MAAM;EACR,CAAC;EAED,MAAM,eAA4B;GAChC,KAAK;GACL,MAAM;GACN,OAAO;GACP,OAAO;GACP,OAAO;GACP,WAAW;EACb;EAGA,MAAM,kBAAkB,eAAe;GACrC,OAAO,CAAC,GAAG,QAAA,SAAS,YAAY;EAClC,CAAC;EAED,SAAS,aAAa,OAAgB;GACpC,IAAI,UAAU,KAAA,GACZ,KAAK,qBAAqB;IACxB,GAAG,QAAA,WAAW,MAAM,GAAG,QAAQ,CAAC;IAChC,CAAC;IACD,GAAG,QAAA,WAAW,MAAM,QAAQ,CAAC;GAC/B,CAAC;QAED,KAAK,qBAAqB,CAAC,GAAG,QAAA,YAAY,CAAC,CAAC,CAAC;EAEjD;EAEA,SAAS,WAAW,KAAmB;GACrC,KAAK,qBAAqB;IACxB,GAAG,QAAA,WAAW,MAAM,GAAG,IAAI,QAAQ,CAAC;IACpC,KAAK,MAAM,KAAK,UAAU,IAAI,IAAI,CAAC;IACnC,GAAG,QAAA,WAAW,MAAM,IAAI,QAAQ,CAAC;GACnC,CAAC;EACH;EAEA,SAAS,aAAa,OAAe;GACnC,KACE,qBACA,QAAA,WAAW,QAAQ,GAAG,MAAM,MAAM,KAAK,CACzC;EACF;;GAlGE,OAAA,UAAA,GAAA,YA+BU,MAAA,WAAA,GAAA;IA9BP,SAAS,gBAAA;IACT,MAAM,QAAA,cAAU,CAAA;IAChB,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,CAAC;IACZ,QAAQ;IACT,cAAA;IACC,OAAOA,KAAAA;;IAEG,sBAAkB,SAKP,EALW,UAAG,CAClC,YAIoB,MAAA,iBAAA,GAAA;KAJD,KAAI;KAAM,OAAA;MAAA,WAAA;MAAA,mBAAA;MAAA,OAAA;KAAA;;KAC3B,SAAA,cAAoF;MAApF,YAAoF,MAAA,WAAA,GAAA;OAA1E,MAAK;OAAU,UAAK,WAAE,aAAa,IAAI,KAAK;OAAI,MAAM,MAAA,KAAA;OAAO,OAAM;;MAC7E,YAAyE,MAAA,WAAA,GAAA;OAA9D,MAAM,MAAA,IAAA;OAAO,UAAK,WAAE,aAAa,IAAI,KAAK;OAAG,OAAM;;MAC9D,YAAiE,MAAA,WAAA,GAAA;OAAtD,MAAM,MAAA,IAAA;OAAO,UAAK,WAAE,WAAW,GAAG;OAAG,OAAM;;;;;IAM/C,OAAK,cAWR,CAVN,mBAUM,OAVN,YAUM,CALK,CAAA,QAAA,WAAW,UAJpB,UAAA,GAAA,YAQC,MAAA,WAAA,GAAA;;KAPC,MAAA;KACA,MAAK;KACL,MAAK;KAEJ,MAAM,MAAA,IAAA;KACN,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,aAAY;;KACnB,SAAA,cAAE,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAF,gBAAA,MAAE,EAAA,CAAA,EAAA,CAAA"}
1
+ {"version":3,"file":"table-editor.js","names":[],"sources":["../../../src/components/table-editor/table-editor.vue"],"sourcesContent":["<template>\n <u-table\n v-bind=\"tableProps\"\n :columns=\"internalColumns\"\n :data=\"modelValue\"\n :class=\"cls.b\"\n :stripe=\"false\"\n show-index\n >\n <!-- 其余插槽(header:* / row:expand / foot / append 等)透传给 u-table;column:* 与 empty 由组件接管 -->\n <template v-for=\"name in passSlotNames\" :key=\"name\" #[name]=\"scope\">\n <slot :name=\"name\" v-bind=\"scope\" />\n </template>\n\n <template #column:__operation=\"{ row }\">\n <div :class=\"cls.e('operations')\">\n <u-button\n text\n size=\"small\"\n type=\"danger\"\n :icon=\"Minus\"\n title=\"移除\"\n @click=\"handleDelete(row.index)\"\n />\n <u-button\n text\n size=\"small\"\n :icon=\"Plus\"\n title=\"新增到下一行\"\n @click=\"handleCreate(row.index)\"\n />\n <u-button text size=\"small\" :icon=\"Copy\" title=\"复制到下一行\" @click=\"handleCopy(row)\" />\n </div>\n </template>\n\n <template #empty>\n <div style=\"text-align: center; padding: 4px 0\">\n <u-button\n v-if=\"!props.readonly\"\n text\n size=\"small\"\n type=\"primary\"\n :icon=\"Plus\"\n @click=\"handleCreate()\"\n >\n 添加\n </u-button>\n </div>\n </template>\n </u-table>\n</template>\n\n<script lang=\"ts\" setup>\nimport { o } from '@cat-kit/core'\nimport { Copy, Minus, Plus, Warning } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, h, shallowRef, toRaw, triggerRef, useSlots, watch } from 'vue'\n\nimport type {\n TableEditorProps,\n TableEditorEmits,\n TableEditorColumn,\n _TableEditorExposed,\n TableColumn,\n TableColumnNode,\n TableColumnRenderContext,\n TableColumnSlotsScope,\n RenderReturn\n} from '../../types'\nimport { UButton } from '../button'\nimport { validateField } from '../form-item/validate'\nimport { UIcon } from '../icon'\nimport { UTable } from '../table'\nimport type { TableRowNode } from '../table/node/row'\nimport { UTip } from '../tip'\n\ndefineOptions({ name: 'UTableEditor' })\n\ndefineSlots<{\n [key: `column:${string}`]: (props: TableColumnSlotsScope) => any\n [key: `header:${string}`]: (props: { column: TableColumnNode }) => any\n}>()\n\nconst props = defineProps<TableEditorProps>()\nconst emit = defineEmits<TableEditorEmits>()\n\nconst columns = computed(() => props.columns ?? [])\nconst modelValue = computed(() => props.modelValue ?? [])\n\n/** 转发给 u-table 的表格属性;columns / modelValue / readonly 为编辑器自用,stripe / showIndex 被覆盖 */\nconst tableProps = computed(() => {\n const rest: Record<string, any> = { ...props }\n delete rest.columns\n delete rest.modelValue\n delete rest.readonly\n return rest\n})\n\nconst cls = bem('table-editor')\n\nconst slots = useSlots()\n\nconst actionColumn: TableColumn = {\n key: '__operation',\n name: '操作',\n width: 104,\n align: 'center',\n fixed: 'right',\n resizable: false\n}\n\n/**\n * 内部列定义:单元格渲染挂在列的 render 上(用户列自带的 render 优先),\n * 配置了 rules 的列包装 nameRender 追加校验标识,非只读时末尾追加操作列。\n * 列 key 带上只读态后缀:u-table 行内单元格在 props 值相等时可能跳过子组件补丁,\n * 只读切换改用挂载键直接重挂单元格,确保控件在编辑/只读形态间可靠切换。\n */\nconst internalColumns = computed<TableColumn[]>(() => {\n const list = columns.value.map((column) => ({\n ...column,\n key: `${column.key}${props.readonly ? ':ro' : ''}`,\n nameRender: column.rules ? headerCell(column) : column.nameRender,\n render: column.render ?? ((ctx: TableColumnRenderContext) => editCell(column, ctx))\n }))\n return props.readonly ? list : [...list, actionColumn]\n})\n\n/** 透传给 u-table 的插槽名:column:* 经 editCell 注入 model、empty 由组件接管,其余透传 */\nconst passSlotNames = Object.keys(slots).filter(\n (name) => !name.startsWith('column:') && name !== 'empty'\n)\n\n/**\n * 带校验的列表头包装:required 时列名前渲染红星;该列存在未通过项时文字标红并追加\n * 感叹号图标,悬停经 UTip 气泡展示各行错误明细;`#header:key` 插槽 / nameRender 内容替代列名。\n * 渲染函数内读取 columnErrors 建立响应依赖,错误出现/消失即时更新表头。\n */\nfunction headerCell(column: TableEditorColumn) {\n return (ctx: { column: TableColumnNode }): RenderReturn => {\n const messages = columnErrors.value.get(column.key)\n const nodes = [\n h(\n 'span',\n { class: [cls.e('header-text'), bem.is('error', !!messages?.length)] },\n slots[`header:${column.key}`]?.(ctx) ?? column.nameRender?.(ctx) ?? column.name\n )\n ]\n\n if (column.rules?.required) {\n nodes.unshift(h('span', { class: cls.e('required-mark'), 'aria-hidden': 'true' }, '*'))\n }\n\n if (messages?.length) {\n nodes.push(\n h(\n UTip,\n { key: 'header-error-tip' },\n {\n default: () =>\n h(UIcon, { size: 14, class: cls.e('header-icon') }, { default: () => h(Warning) }),\n content: () => messages.map((message) => h('div', { key: message }, message))\n }\n )\n )\n }\n\n return nodes\n }\n}\n\n// --- 按列校验 ---\n\n/** 行数据(以原始对象为键)→ 列 key → 错误信息 */\nconst cellErrors = shallowRef(new Map<Record<string, any>, Map<string, string>>())\n\n/** 行节点 data 可能是响应式代理,统一取原始对象作为键 */\nconst errorRowKey = (rowData: Record<string, any>) => toRaw(rowData) as Record<string, any>\n\nfunction setCellError(rowData: Record<string, any>, key: string, message: string | undefined) {\n const row = errorRowKey(rowData)\n const rowErrors = cellErrors.value.get(row)\n\n if (!message) {\n if (!rowErrors?.delete(key)) return\n // 行错误清空后移除条目,保证「size > 0 即存在错误」可作整体判定\n if (!rowErrors.size) cellErrors.value.delete(row)\n } else {\n if (rowErrors?.get(key) === message) return\n if (rowErrors) rowErrors.set(key, message)\n else cellErrors.value.set(row, new Map([[key, message]]))\n }\n\n triggerRef(cellErrors)\n}\n\n/** 丢弃同一单元格过期的异步校验结果 */\nconst validateSeqs = new WeakMap<Record<string, any>, Map<string, number>>()\n\n/** 单元格级校验:以行数据为 formData、列 key 为 field,结果同步到错误状态 */\nasync function validateCell(\n rowData: Record<string, any>,\n key: string,\n rules: TableEditorColumn['rules']\n) {\n if (!rules) return\n\n const row = errorRowKey(rowData)\n let seqs = validateSeqs.get(row)\n if (!seqs) {\n seqs = new Map()\n validateSeqs.set(row, seqs)\n }\n const seq = (seqs.get(key) ?? 0) + 1\n seqs.set(key, seq)\n\n const message = await validateField(rowData, key, rules)\n if (seqs.get(key) !== seq) return\n // 校验是异步的,期间行可能已被移出模型(整体替换/删除);\n // 把结果写回「死行」会绕过清理 watch 永久残留,直接丢弃\n if (!modelValue.value.some((item) => errorRowKey(item) === row)) return\n\n setCellError(row, key, message)\n}\n\n/**\n * 整表校验:并行校验所有行的规则列(不懒校验),未通过的单元格全部标出。\n * 全部通过 resolve true,否则 resolve false。\n */\nasync function validate() {\n const ruleColumns = columns.value.filter((column) => column.rules)\n\n await Promise.all(\n modelValue.value.map((row) =>\n Promise.all(ruleColumns.map((column) => validateCell(row, column.key, column.rules)))\n )\n )\n\n return cellErrors.value.size === 0\n}\n\n// 行删除(或整体替换)后,同步清理已不存在的行数据上的错误\nwatch(modelValue, (rows) => {\n if (!cellErrors.value.size) return\n\n const aliveRows = new Set(rows.map(errorRowKey))\n const next = new Map<Record<string, any>, Map<string, string>>()\n let changed = false\n\n for (const [row, errors] of cellErrors.value) {\n if (aliveRows.has(row)) next.set(row, errors)\n else changed = true\n }\n\n if (changed) cellErrors.value = next\n})\n\n/** 列 key → 该列全部未通过信息(带行序号),驱动表头错误标识 */\nconst columnErrors = computed(() => {\n const map = new Map<string, string[]>()\n if (!cellErrors.value.size) return map\n\n for (const [row, errors] of cellErrors.value) {\n const index = modelValue.value.findIndex((item) => errorRowKey(item) === row)\n const label = index >= 0 ? `第 ${index + 1} 行:` : ''\n for (const [key, message] of errors) {\n const list = map.get(key) ?? []\n list.push(`${label}${message}`)\n map.set(key, list)\n }\n }\n return map\n})\n\n// --- 单元格渲染 ---\n\n/**\n * 声明了 `#column:key` 插槽的列调用编辑插槽(注入 model),未声明的渲染字段原始值。\n * 配置了 rules 的列在渲染期间读取 cellErrors 建立依赖,错误出现/消失即时更新:\n * 插槽内容常驻包一层容器、仅切换 is-error 类(与表单项错误态同类,不增删 DOM,\n * 控件不因错误态变化重挂丢焦点);纯文本列仅在出错时包一层标红原始值。\n */\nfunction editCell(column: TableEditorColumn, ctx: TableColumnRenderContext): RenderReturn {\n const editSlot = slots[`column:${column.key}`]\n // 内部列 key 带只读态后缀,u-table 按 key 取到的 val 失效,这里按原始列 key 重取\n const val = o(ctx.rowData).get(column.key)\n\n if (!column.rules) {\n if (!editSlot) return val\n const scope = { ...ctx, val }\n return editSlot({ ...scope, model: createCellModel(column, scope) })\n }\n\n const hasError = !!cellErrors.value.get(errorRowKey(ctx.rowData))?.has(column.key)\n if (!editSlot) return hasError ? h('span', { class: bem.is('error') }, val) : val\n\n const scope = { ...ctx, val }\n return h(\n 'span',\n { class: bem.is('error', hasError) },\n editSlot({ ...scope, model: createCellModel(column, scope) })\n )\n}\n\n/**\n * 单元格编辑 model:值先原地写回行数据(行节点与 DOM 得以复用,输入不丢焦点),\n * 再以浅拷贝数组经 update:modelValue 通知宿主,维持 v-model 契约。\n * 只读下注入 readonly: true 且不提供写回通道,值不可修改;\n * 列配置了 rules 时,控件的 change 事件(如失焦提交)触发单元格级校验,输入过程不校验。\n */\nfunction createCellModel(column: TableEditorColumn, ctx: TableColumnRenderContext) {\n const { rowData, val } = ctx\n const key = column.key\n const rules = column.rules\n return {\n modelValue: val,\n ...(props.readonly\n ? { readonly: true }\n : {\n 'onUpdate:modelValue': (value: any) => {\n rowData[key] = value\n emit('update:modelValue', [...modelValue.value])\n }\n }),\n ...(rules ? { onChange: () => void validateCell(rowData, key, rules) } : {})\n }\n}\n\nfunction handleCreate(index?: number) {\n const at = index !== undefined ? index + 1 : modelValue.value.length\n emit('update:modelValue', [...modelValue.value.slice(0, at), {}, ...modelValue.value.slice(at)])\n}\n\nfunction handleCopy(row: TableRowNode) {\n emit('update:modelValue', [\n ...modelValue.value.slice(0, row.index + 1),\n JSON.parse(JSON.stringify(row.data)),\n ...modelValue.value.slice(row.index + 1)\n ])\n}\n\nfunction handleDelete(index: number) {\n emit(\n 'update:modelValue',\n modelValue.value.filter((_, i) => i !== index)\n )\n}\n\ndefineExpose<_TableEditorExposed>({ validate })\n</script>\n"],"mappings":"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"}
@@ -56,6 +56,69 @@
56
56
  font-size: var(--u-font-size-main-large);
57
57
  padding: var(--u-gap-large);
58
58
  }
59
+ .u-tip--color-primary {
60
+ color: var(--u-text-color-white);
61
+ }
62
+ .u-tip--color-primary::after {
63
+ background-color: var(--u-color-primary);
64
+ }
65
+ .u-tip--color-primary .u-tip__arrow {
66
+ background-color: var(--u-color-primary);
67
+ }
68
+ .u-tip--color-success {
69
+ color: var(--u-text-color-white);
70
+ }
71
+ .u-tip--color-success::after {
72
+ background-color: var(--u-color-success);
73
+ }
74
+ .u-tip--color-success .u-tip__arrow {
75
+ background-color: var(--u-color-success);
76
+ }
77
+ .u-tip--color-warning {
78
+ color: var(--u-text-color-white);
79
+ }
80
+ .u-tip--color-warning::after {
81
+ background-color: var(--u-color-warning);
82
+ }
83
+ .u-tip--color-warning .u-tip__arrow {
84
+ background-color: var(--u-color-warning);
85
+ }
86
+ .u-tip--color-danger {
87
+ color: var(--u-text-color-white);
88
+ }
89
+ .u-tip--color-danger::after {
90
+ background-color: var(--u-color-danger);
91
+ }
92
+ .u-tip--color-danger .u-tip__arrow {
93
+ background-color: var(--u-color-danger);
94
+ }
95
+ .u-tip--color-info {
96
+ color: var(--u-text-color-white);
97
+ }
98
+ .u-tip--color-info::after {
99
+ background-color: var(--u-color-info);
100
+ }
101
+ .u-tip--color-info .u-tip__arrow {
102
+ background-color: var(--u-color-info);
103
+ }
104
+ .u-tip--color-disabled {
105
+ color: var(--u-text-color-white);
106
+ }
107
+ .u-tip--color-disabled::after {
108
+ background-color: var(--u-color-disabled);
109
+ }
110
+ .u-tip--color-disabled .u-tip__arrow {
111
+ background-color: var(--u-color-disabled);
112
+ }
113
+ .u-tip--color-default {
114
+ color: var(--u-text-color-white);
115
+ }
116
+ .u-tip--color-default::after {
117
+ background-color: var(--u-color-default);
118
+ }
119
+ .u-tip--color-default .u-tip__arrow {
120
+ background-color: var(--u-color-default);
121
+ }
59
122
 
60
123
  .tip-enter-active,
61
124
  .tip-leave-active {
@@ -13,6 +13,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
13
13
  type: Boolean,
14
14
  default: void 0
15
15
  },
16
+ type: {},
16
17
  content: { default: "" },
17
18
  style: {},
18
19
  class: {},
@@ -33,7 +34,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
33
34
  const slots = useSlots();
34
35
  const { size } = useFallbackProps([props], { size: "default" });
35
36
  const contentClass = computed(() => {
36
- const fixed = [cls.e("content"), cls.m(size.value)];
37
+ const fixed = [
38
+ cls.e("content"),
39
+ cls.m(size.value),
40
+ props.type && cls.m("color-" + props.type)
41
+ ];
37
42
  const className = props.class;
38
43
  if (!Array.isArray(className)) return [...fixed, className];
39
44
  return [...fixed, ...className];
@@ -1 +1 @@
1
- {"version":3,"file":"tip.js","names":[],"sources":["../../../src/components/tip/tip.vue"],"sourcesContent":["<template>\n <!-- 触发 -->\n <UNodeRender v-bind=\"eventsHandlers\" :content=\"renderDefaultSlotTrigger()\" ref=\"triggerRef\" />\n\n <!-- 弹出内容 -->\n <teleport :to=\"`#${popperContainerId}`\">\n <transition name=\"tip\">\n <component\n v-if=\"tipVisible\"\n :is=\"contentTag\"\n :class=\"contentClass\"\n :style=\"[style, { zIndex: zIndex() }]\"\n ref=\"contentRef\"\n @mouseenter=\"eventsHandlers.onMouseenter\"\n @mouseleave=\"eventsHandlers.onMouseleave\"\n @click.stop\n v-click-outside=\"handleClickOutside\"\n >\n <UNodeRender v-if=\"externalNode\" :content=\"externalNode\" />\n\n <slot name=\"content\">{{ content }}</slot>\n\n <div :class=\"cls.e('arrow')\" v-if=\"!hideArrow\" ref=\"arrowRef\"></div>\n </component>\n </transition>\n </teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps, usePop } from '@veltra/compositions'\nimport { vClickOutside } from '@veltra/directives'\nimport { bem, extractNormalVNodes, zIndex } from '@veltra/utils'\nimport { shallowRef, computed, useSlots, onBeforeUnmount, toRef, watch, nextTick } from 'vue'\n\nimport type { TipProps, ComponentSize, TipEmits } from '../../types'\nimport { UNodeRender } from '../node-render'\nimport { useNest } from './use-nest'\n\ndefineOptions({ name: 'UTip' })\n\nconst props = withDefaults(defineProps<TipProps>(), {\n content: '',\n trigger: 'hover',\n direction: 'top',\n alignment: 'center',\n contentTag: 'div',\n visible: undefined,\n showDelay: 0\n})\n\nconst emit = defineEmits<TipEmits>()\n\nconst cls = bem('tip')\nconst slots = useSlots()\nconst { size } = useFallbackProps([props], { size: 'default' as ComponentSize })\n\nconst contentClass = computed(() => {\n const fixed = [cls.e('content'), cls.m(size.value)]\n const className = props.class\n if (!Array.isArray(className)) {\n return [...fixed, className]\n }\n return [...fixed, ...className]\n})\n\nconst triggerRef = shallowRef<InstanceType<typeof UNodeRender>>()\nconst contentRef = shallowRef<HTMLElement>()\nconst arrowRef = shallowRef<HTMLElement>()\n\nconst _visible = shallowRef(false)\n\nconst visible = computed(() => {\n return props.visible !== undefined ? props.visible : _visible.value\n})\n\nfunction updateVisible(v: boolean) {\n if (props.visible === undefined) {\n _visible.value = v\n } else {\n emit('update:visible', v)\n }\n}\n\nconst tipVisible = useNest(visible)\n\nfunction renderDefaultSlotTrigger() {\n const nodes = slots.default?.()\n if (!nodes?.length) return null\n return extractNormalVNodes(nodes)[0]\n}\n\nconst eventsHandlers = computed(() => {\n const { trigger, disabled } = props\n\n const handlers: Record<string, Function> = {}\n\n if (disabled) return handlers\n\n if (trigger === 'click') {\n handlers.onClick = open\n } else if (trigger === 'hover') {\n handlers.onMouseenter = open\n handlers.onMouseleave = close\n }\n\n return handlers\n})\n\nconst handleClickOutside = (e: MouseEvent) => {\n if (props.trigger === 'hover') return\n\n // 点击的元素在触发元素中则啥也\n if (props.trigger === 'click' && triggerDom.value.contains(e.target as Node)) {\n return\n }\n close()\n}\n\nlet openTimer: ReturnType<typeof setTimeout> | undefined\nlet closeTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction clearOpenTimer() {\n if (openTimer !== undefined) {\n clearTimeout(openTimer)\n openTimer = undefined\n }\n}\n\nfunction clearCloseTimer() {\n if (closeTimer !== undefined) {\n clearTimeout(closeTimer)\n closeTimer = undefined\n }\n}\n\n/** 弹出 */\nconst open = (e?: PointerEvent) => {\n e?.stopPropagation()\n clearCloseTimer()\n\n const delay = props.trigger === 'hover' ? props.showDelay : 0\n if (delay <= 0) {\n clearOpenTimer()\n updateVisible(true)\n return\n }\n\n clearOpenTimer()\n openTimer = setTimeout(() => {\n openTimer = undefined\n updateVisible(true)\n }, delay)\n}\n/** 关闭 */\nconst close = () => {\n clearOpenTimer()\n\n if (props.trigger === 'hover') {\n closeTimer = setTimeout(() => {\n closeTimer = undefined\n updateVisible(false)\n }, 250)\n } else {\n updateVisible(false)\n }\n}\n\nconst triggerDom = computed(() => {\n return props.triggerDom || triggerRef.value?.$el\n})\n\nconst { update, popperContainerId } = usePop({\n triggerRef: triggerDom,\n contentRef,\n arrowRef,\n direction: toRef(() => props.direction),\n alignment: toRef(() => props.alignment),\n onTriggerPositionChange() {\n close()\n }\n})\n\nwatch(triggerDom, (dom) => {\n // TODO: 看起来不是很优雅\n if (dom?.nodeType === Node.ELEMENT_NODE && visible.value) {\n nextTick(() => {\n void update()\n })\n }\n})\n\n/** 外部节点 */\nconst externalNode = shallowRef<any>()\n\nonBeforeUnmount(() => {\n clearOpenTimer()\n clearCloseTimer()\n})\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"tip.js","names":[],"sources":["../../../src/components/tip/tip.vue"],"sourcesContent":["<template>\n <!-- 触发 -->\n <UNodeRender v-bind=\"eventsHandlers\" :content=\"renderDefaultSlotTrigger()\" ref=\"triggerRef\" />\n\n <!-- 弹出内容 -->\n <teleport :to=\"`#${popperContainerId}`\">\n <transition name=\"tip\">\n <component\n v-if=\"tipVisible\"\n :is=\"contentTag\"\n :class=\"contentClass\"\n :style=\"[style, { zIndex: zIndex() }]\"\n ref=\"contentRef\"\n @mouseenter=\"eventsHandlers.onMouseenter\"\n @mouseleave=\"eventsHandlers.onMouseleave\"\n @click.stop\n v-click-outside=\"handleClickOutside\"\n >\n <UNodeRender v-if=\"externalNode\" :content=\"externalNode\" />\n\n <slot name=\"content\">{{ content }}</slot>\n\n <div :class=\"cls.e('arrow')\" v-if=\"!hideArrow\" ref=\"arrowRef\"></div>\n </component>\n </transition>\n </teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps, usePop } from '@veltra/compositions'\nimport { vClickOutside } from '@veltra/directives'\nimport { bem, extractNormalVNodes, zIndex } from '@veltra/utils'\nimport { shallowRef, computed, useSlots, onBeforeUnmount, toRef, watch, nextTick } from 'vue'\n\nimport type { TipProps, ComponentSize, TipEmits } from '../../types'\nimport { UNodeRender } from '../node-render'\nimport { useNest } from './use-nest'\n\ndefineOptions({ name: 'UTip' })\n\nconst props = withDefaults(defineProps<TipProps>(), {\n content: '',\n trigger: 'hover',\n direction: 'top',\n alignment: 'center',\n contentTag: 'div',\n visible: undefined,\n showDelay: 0\n})\n\nconst emit = defineEmits<TipEmits>()\n\nconst cls = bem('tip')\nconst slots = useSlots()\nconst { size } = useFallbackProps([props], { size: 'default' as ComponentSize })\n\nconst contentClass = computed(() => {\n const fixed = [cls.e('content'), cls.m(size.value), props.type && cls.m('color-' + props.type)]\n const className = props.class\n if (!Array.isArray(className)) {\n return [...fixed, className]\n }\n return [...fixed, ...className]\n})\n\nconst triggerRef = shallowRef<InstanceType<typeof UNodeRender>>()\nconst contentRef = shallowRef<HTMLElement>()\nconst arrowRef = shallowRef<HTMLElement>()\n\nconst _visible = shallowRef(false)\n\nconst visible = computed(() => {\n return props.visible !== undefined ? props.visible : _visible.value\n})\n\nfunction updateVisible(v: boolean) {\n if (props.visible === undefined) {\n _visible.value = v\n } else {\n emit('update:visible', v)\n }\n}\n\nconst tipVisible = useNest(visible)\n\nfunction renderDefaultSlotTrigger() {\n const nodes = slots.default?.()\n if (!nodes?.length) return null\n return extractNormalVNodes(nodes)[0]\n}\n\nconst eventsHandlers = computed(() => {\n const { trigger, disabled } = props\n\n const handlers: Record<string, Function> = {}\n\n if (disabled) return handlers\n\n if (trigger === 'click') {\n handlers.onClick = open\n } else if (trigger === 'hover') {\n handlers.onMouseenter = open\n handlers.onMouseleave = close\n }\n\n return handlers\n})\n\nconst handleClickOutside = (e: MouseEvent) => {\n if (props.trigger === 'hover') return\n\n // 点击的元素在触发元素中则啥也\n if (props.trigger === 'click' && triggerDom.value.contains(e.target as Node)) {\n return\n }\n close()\n}\n\nlet openTimer: ReturnType<typeof setTimeout> | undefined\nlet closeTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction clearOpenTimer() {\n if (openTimer !== undefined) {\n clearTimeout(openTimer)\n openTimer = undefined\n }\n}\n\nfunction clearCloseTimer() {\n if (closeTimer !== undefined) {\n clearTimeout(closeTimer)\n closeTimer = undefined\n }\n}\n\n/** 弹出 */\nconst open = (e?: PointerEvent) => {\n e?.stopPropagation()\n clearCloseTimer()\n\n const delay = props.trigger === 'hover' ? props.showDelay : 0\n if (delay <= 0) {\n clearOpenTimer()\n updateVisible(true)\n return\n }\n\n clearOpenTimer()\n openTimer = setTimeout(() => {\n openTimer = undefined\n updateVisible(true)\n }, delay)\n}\n/** 关闭 */\nconst close = () => {\n clearOpenTimer()\n\n if (props.trigger === 'hover') {\n closeTimer = setTimeout(() => {\n closeTimer = undefined\n updateVisible(false)\n }, 250)\n } else {\n updateVisible(false)\n }\n}\n\nconst triggerDom = computed(() => {\n return props.triggerDom || triggerRef.value?.$el\n})\n\nconst { update, popperContainerId } = usePop({\n triggerRef: triggerDom,\n contentRef,\n arrowRef,\n direction: toRef(() => props.direction),\n alignment: toRef(() => props.alignment),\n onTriggerPositionChange() {\n close()\n }\n})\n\nwatch(triggerDom, (dom) => {\n // TODO: 看起来不是很优雅\n if (dom?.nodeType === Node.ELEMENT_NODE && visible.value) {\n nextTick(() => {\n void update()\n })\n }\n})\n\n/** 外部节点 */\nconst externalNode = shallowRef<any>()\n\nonBeforeUnmount(() => {\n clearOpenTimer()\n clearCloseTimer()\n})\n</script>\n"],"mappings":"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"}