xt-element-ui 1.0.9 → 1.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.
Files changed (91) hide show
  1. package/lib/css/2.3f7aa432.css +1 -0
  2. package/lib/css/3.ffcc175d.css +1 -0
  3. package/lib/css/4.9abd1f2b.css +1 -0
  4. package/lib/css/5.1a31ed8a.css +1 -0
  5. package/lib/css/6.c2d0d77e.css +1 -0
  6. package/lib/index.common.0.js +120208 -0
  7. package/lib/index.common.2.js +1053 -0
  8. package/lib/index.common.3.js +996 -0
  9. package/lib/index.common.4.js +1108 -0
  10. package/lib/index.common.5.js +1009 -0
  11. package/lib/index.common.6.js +973 -0
  12. package/lib/index.common.js +8515 -631
  13. package/lib/index.css +1 -1
  14. package/lib/index.umd.0.js +120208 -0
  15. package/lib/index.umd.2.js +1053 -0
  16. package/lib/index.umd.3.js +996 -0
  17. package/lib/index.umd.4.js +1108 -0
  18. package/lib/index.umd.5.js +1009 -0
  19. package/lib/index.umd.6.js +973 -0
  20. package/lib/index.umd.js +8515 -631
  21. package/lib/index.umd.min.0.js +34 -0
  22. package/lib/index.umd.min.2.js +1 -0
  23. package/lib/index.umd.min.3.js +1 -0
  24. package/lib/index.umd.min.4.js +1 -0
  25. package/lib/index.umd.min.5.js +1 -0
  26. package/lib/index.umd.min.6.js +1 -0
  27. package/lib/index.umd.min.js +1 -1
  28. package/package.json +6 -2
  29. package/src/components/button/index.vue +30 -24
  30. package/src/components/button/style/index.scss +741 -82
  31. package/src/components/card/style/index.scss +1 -1
  32. package/src/components/card-item/style/index.scss +1 -1
  33. package/src/components/chart/ExBar.vue +203 -0
  34. package/src/components/chart/ExLine.vue +146 -0
  35. package/src/components/chart/ExMulti.vue +257 -0
  36. package/src/components/chart/ExPie.vue +159 -0
  37. package/src/components/chart/ExTrend.vue +121 -0
  38. package/src/components/chart/index.js +2 -0
  39. package/src/components/chart/index.vue +51 -0
  40. package/src/components/chart/pieList.vue +110 -0
  41. package/src/components/chart/theme/blue.js +91 -0
  42. package/src/components/chart/theme/dark.js +91 -0
  43. package/src/components/chart/theme/orange.js +92 -0
  44. package/src/components/chart/theme/starry.js +106 -0
  45. package/src/components/chart/theme/white.js +110 -0
  46. package/src/components/chart/utils.js +273 -0
  47. package/src/components/config-provider/index.vue +155 -39
  48. package/src/components/config-provider/style/index.scss +2 -2
  49. package/src/components/date-picker/SearchDate.vue +45 -0
  50. package/src/components/date-picker/index.js +2 -0
  51. package/src/components/date-picker/index.vue +131 -0
  52. package/src/components/date-picker/quarter.vue +152 -0
  53. package/src/components/grid-box/index.js +2 -0
  54. package/src/components/grid-box/index.vue +42 -0
  55. package/src/components/index.scss +4 -1
  56. package/src/components/input/style/index.scss +1 -1
  57. package/src/components/layout/BaseCollapse.vue +48 -0
  58. package/src/components/layout/ExFieldset.vue +204 -0
  59. package/src/components/page/index.js +0 -0
  60. package/src/components/page/index.vue +109 -0
  61. package/src/components/select-tree/index.js +0 -0
  62. package/src/components/select-tree/index.vue +386 -0
  63. package/src/components/table/ExCell.vue +27 -0
  64. package/src/components/table/ExColumn.vue +36 -0
  65. package/src/components/table/index.js +2 -0
  66. package/src/components/table/index.vue +731 -0
  67. package/src/components/table/processor.js +380 -0
  68. package/src/components/text/index.js +2 -0
  69. package/src/components/text/index.vue +119 -0
  70. package/src/components/text/style/index.scss +67 -0
  71. package/src/components/upload/index.js +2 -0
  72. package/src/components/upload/index.vue +225 -0
  73. package/src/components/upload/preview.vue +333 -0
  74. package/src/index.js +17 -5
  75. package/src/styles/css-variables.scss +258 -0
  76. package/src/styles/theme/{bg.scss → background.scss} +1 -1
  77. package/src/styles/theme/colors.scss +100 -0
  78. package/src/styles/theme/{component.scss → component-variables.scss} +3 -3
  79. package/src/styles/theme/index.scss +9 -9
  80. package/src/styles/variables.scss +1 -1
  81. package/src/utils/index.js +3 -5
  82. package/src/styles/theme/color.scss +0 -11
  83. package/src/styles/vars.scss +0 -168
  84. /package/src/styles/{theme-element.scss → element-theme.scss} +0 -0
  85. /package/src/styles/theme/{radius.scss → border-radius.scss} +0 -0
  86. /package/src/styles/theme/{border.scss → borders.scss} +0 -0
  87. /package/src/styles/theme/{dark.scss → dark-variables.scss} +0 -0
  88. /package/src/styles/theme/{shadow.scss → shadows.scss} +0 -0
  89. /package/src/styles/theme/{transition.scss → transitions.scss} +0 -0
  90. /package/src/styles/theme/{text.scss → typography.scss} +0 -0
  91. /package/src/styles/{export.scss → variables-export.scss} +0 -0
@@ -0,0 +1,380 @@
1
+ // utils/tableDataProcessor.js
2
+
3
+ /**
4
+ * 主函数:为表格数据添加小计行
5
+ * @param {Array} data - 原始数据
6
+ * @param {Object} options - 配置选项
7
+ * @returns {Array} 加工后的数据
8
+ */
9
+ export function enhanceWithSubtotal(data, options = {}) {
10
+ const defaultOptions = {
11
+ groupFields: [], // 分组字段,如 ['region', 'province', 'city']
12
+ sumFields: [], // 求和字段,如 ['sales', 'quantity']
13
+ avgFields: [], // 平均字段,如 ['price']
14
+ countFields: [], // 计数字段,如 ['product']
15
+ subtotalLabel: "小计", // 小计行标签
16
+ totalLabel: "总计", // 总计行标签
17
+ showTotal: true, // 是否显示总计
18
+ sortData: true, // 是否自动排序
19
+ formatNumbers: true, // 是否格式化数字
20
+ excludeSubtotalRows: false, // 是否排除某些行不参与小计
21
+ excludeCondition: null, // 排除条件函数
22
+ customCalculations: {} // 自定义计算函数
23
+ };
24
+
25
+ const config = { ...defaultOptions, ...options };
26
+
27
+ // 数据验证
28
+ if (!Array.isArray(data) || data.length === 0) {
29
+ return data;
30
+ }
31
+
32
+ if (config.groupFields.length === 0) {
33
+ console.warn("未指定分组字段,将返回原始数据");
34
+ return data;
35
+ }
36
+
37
+ // 1. 数据预处理
38
+ let processedData = preprocessData(data, config);
39
+
40
+ // 2. 计算并插入小计行
41
+ processedData = calculateAndInsertSubtotals(processedData, config);
42
+
43
+ // 3. 后处理(格式化等)
44
+ processedData = postprocessData(processedData, config);
45
+
46
+ return processedData;
47
+ }
48
+
49
+ /**
50
+ * 数据预处理
51
+ */
52
+ function preprocessData(data, config) {
53
+ let result = [...data];
54
+
55
+ // 标记排除的行
56
+ if (config.excludeCondition) {
57
+ result = result.map(row => ({
58
+ ...row,
59
+ _excludedFromSubtotal: config.excludeCondition(row)
60
+ }));
61
+ }
62
+
63
+ // 数据排序
64
+ if (config.sortData) {
65
+ result.sort((a, b) => {
66
+ for (const field of config.groupFields) {
67
+ const aVal = String(a[field] || "");
68
+ const bVal = String(b[field] || "");
69
+ if (aVal !== bVal) {
70
+ return aVal.localeCompare(bVal);
71
+ }
72
+ }
73
+ return 0;
74
+ });
75
+ }
76
+
77
+ return result;
78
+ }
79
+
80
+ /**
81
+ * 计算并插入小计行
82
+ */
83
+ function calculateAndInsertSubtotals(data, config) {
84
+ const result = [];
85
+ const allSumFields = [...config.sumFields, ...config.avgFields, ...config.countFields];
86
+
87
+ // 存储各级分组的小计信息
88
+ const subtotalInfo = Array(config.groupFields.length).fill().map(() => ({
89
+ startIndex: 0,
90
+ currentValue: null,
91
+ subtotalRows: []
92
+ }));
93
+
94
+ for (let i = 0; i <= data.length; i++) {
95
+ const row = i < data.length ? data[i] : null;
96
+
97
+ // 检查每一级分组是否发生变化
98
+ for (let level = config.groupFields.length - 1; level >= 0; level--) {
99
+ const field = config.groupFields[level];
100
+ const currentValue = row ? row[field] : null;
101
+ const info = subtotalInfo[level];
102
+
103
+ // 如果是数据末尾或分组值变化
104
+ if (row === null || currentValue !== info.currentValue) {
105
+ // 如果存在前一个分组,计算并插入小计
106
+ if (info.currentValue !== null) {
107
+ const startPos = info.startIndex;
108
+ const endPos = i;
109
+ const groupRows = data.slice(startPos, endPos);
110
+
111
+ // 只计算未排除的行
112
+ const validRows = config.excludeSubtotalRows
113
+ ? groupRows.filter(r => !r._excludedFromSubtotal)
114
+ : groupRows;
115
+
116
+ if (validRows.length > 0) {
117
+ const subtotalRow = createSubtotalRow(
118
+ info.currentValue,
119
+ level,
120
+ validRows,
121
+ config,
122
+ allSumFields
123
+ );
124
+
125
+ // 记录小计行插入位置
126
+ info.subtotalRows.push({
127
+ row: subtotalRow,
128
+ insertAfter: endPos - 1,
129
+ level
130
+ });
131
+ }
132
+ }
133
+
134
+ // 更新分组信息
135
+ info.currentValue = currentValue;
136
+ info.startIndex = i;
137
+ }
138
+ }
139
+
140
+ // 添加原始数据行
141
+ if (row) {
142
+ result.push(row);
143
+ }
144
+ }
145
+
146
+ // 插入小计行(从最深层开始)
147
+ for (let level = config.groupFields.length - 1; level >= 0; level--) {
148
+ const info = subtotalInfo[level];
149
+ info.subtotalRows.reverse().forEach(({ row, insertAfter }) => {
150
+ // 找到插入位置(考虑已插入的小计行)
151
+ const actualPosition = findInsertPosition(result, insertAfter);
152
+ result.splice(actualPosition + 1, 0, row);
153
+ });
154
+ }
155
+
156
+ // 添加总计行
157
+ if (config.showTotal) {
158
+ const validRows = config.excludeSubtotalRows
159
+ ? data.filter(row => !row._excludedFromSubtotal)
160
+ : data;
161
+
162
+ const totalRow = createTotalRow(validRows, config, allSumFields);
163
+ result.push(totalRow);
164
+ }
165
+
166
+ return result;
167
+ }
168
+
169
+ /**
170
+ * 创建小计行
171
+ */
172
+ function createSubtotalRow(groupValue, level, groupRows, config, allSumFields) {
173
+ const subtotalRow = {
174
+ _isSubtotal: true,
175
+ _subtotalLevel: level,
176
+ _rowType: "subtotal",
177
+ _groupValue: groupValue
178
+ };
179
+
180
+ // 设置分组字段
181
+ config.groupFields.forEach((field, index) => {
182
+ if (index === level) {
183
+ subtotalRow[field] = `${config.subtotalLabel}`;
184
+ } else if (index < level) {
185
+ subtotalRow[field] = groupRows[0][field];
186
+ } else {
187
+ subtotalRow[field] = "";
188
+ }
189
+ });
190
+
191
+ // 计算数值
192
+ calculateRowValues(subtotalRow, groupRows, config, allSumFields);
193
+
194
+ return subtotalRow;
195
+ }
196
+
197
+ /**
198
+ * 创建总计行
199
+ */
200
+ function createTotalRow(allRows, config, allSumFields) {
201
+ const totalRow = {
202
+ _isSubtotal: true,
203
+ _isTotal: true,
204
+ _rowType: "total",
205
+ _subtotalLevel: -1
206
+ };
207
+
208
+ // 设置分组字段
209
+ if (config.groupFields.length > 0) {
210
+ totalRow[config.groupFields[0]] = config.totalLabel;
211
+ for (let i = 1; i < config.groupFields.length; i++) {
212
+ totalRow[config.groupFields[i]] = "";
213
+ }
214
+ }
215
+
216
+ // 计算总计数值
217
+ calculateRowValues(totalRow, allRows, config, allSumFields);
218
+
219
+ return totalRow;
220
+ }
221
+
222
+ /**
223
+ * 计算行数值(求和、平均等)
224
+ */
225
+ function calculateRowValues(targetRow, sourceRows, config, allSumFields) {
226
+ // 求和字段
227
+ config.sumFields.forEach(field => {
228
+ const sum = sourceRows.reduce((total, row) => {
229
+ const value = parseFloat(row[field]);
230
+ return total + (isNaN(value) ? 0 : value);
231
+ }, 0);
232
+
233
+ targetRow[field] = config.formatNumbers ? formatNumber(sum) : sum;
234
+ });
235
+
236
+ // 平均字段
237
+ config.avgFields.forEach(field => {
238
+ const sum = sourceRows.reduce((total, row) => {
239
+ const value = parseFloat(row[field]);
240
+ return total + (isNaN(value) ? 0 : value);
241
+ }, 0);
242
+
243
+ const avg = sourceRows.length > 0 ? sum / sourceRows.length : 0;
244
+ targetRow[field] = config.formatNumbers ? formatNumber(avg) : avg;
245
+ });
246
+
247
+ // 计数字段(去重计数)
248
+ config.countFields.forEach(field => {
249
+ const uniqueValues = new Set(sourceRows.map(row => row[field]).filter(v => v != null));
250
+ targetRow[field] = uniqueValues.size;
251
+ });
252
+
253
+ // 自定义计算
254
+ Object.entries(config.customCalculations).forEach(([field, calculator]) => {
255
+ if (typeof calculator === "function") {
256
+ targetRow[field] = calculator(sourceRows, field);
257
+ }
258
+ });
259
+ }
260
+
261
+ /**
262
+ * 格式化数字
263
+ */
264
+ function formatNumber(num) {
265
+ if (typeof num !== "number" || isNaN(num)) return num;
266
+
267
+ // 整数不加小数位
268
+ if (Number.isInteger(num)) {
269
+ return num.toLocaleString();
270
+ }
271
+
272
+ // 小数保留两位
273
+ return num.toLocaleString("zh-CN", {
274
+ minimumFractionDigits: 2,
275
+ maximumFractionDigits: 2
276
+ });
277
+ }
278
+
279
+ /**
280
+ * 查找插入位置(考虑已插入的行)
281
+ */
282
+ function findInsertPosition(data, originalIndex) {
283
+ const currentIndex = originalIndex;
284
+ let insertedRowsCount = 0;
285
+
286
+ // 统计已插入的小计行数量
287
+ for (let i = 0; i <= originalIndex; i++) {
288
+ if (data[i] && data[i]._isSubtotal && !data[i]._isTotal) {
289
+ insertedRowsCount++;
290
+ }
291
+ }
292
+
293
+ return originalIndex + insertedRowsCount;
294
+ }
295
+
296
+ /**
297
+ * 数据后处理
298
+ */
299
+ function postprocessData(data, config) {
300
+ return data.map(row => {
301
+ // 为小计行和总计行添加样式类
302
+ if (row._isSubtotal) {
303
+ return {
304
+ ...row,
305
+ _rowClass: row._isTotal ? "total-row" : `subtotal-level-${row._subtotalLevel}`
306
+ };
307
+ }
308
+ return row;
309
+ });
310
+ }
311
+
312
+ /**
313
+ * 快速使用函数:简单小计
314
+ */
315
+ export function quickSubtotal(data, groupBy, sumFields, options = {}) {
316
+ return enhanceWithSubtotal(data, {
317
+ groupFields: Array.isArray(groupBy) ? groupBy : [groupBy],
318
+ sumFields: Array.isArray(sumFields) ? sumFields : [sumFields],
319
+ ...options
320
+ });
321
+ }
322
+
323
+ /**
324
+ * 计算分组统计信息(不插入行)
325
+ */
326
+ export function calculateGroupStats(data, groupFields, calcFields) {
327
+ const stats = {};
328
+
329
+ function processLevel(rows, level = 0, path = []) {
330
+ if (level >= groupFields.length) return;
331
+
332
+ const field = groupFields[level];
333
+ const groups = {};
334
+
335
+ rows.forEach(row => {
336
+ const key = row[field] || "";
337
+ if (!groups[key]) groups[key] = [];
338
+ groups[key].push(row);
339
+ });
340
+
341
+ Object.entries(groups).forEach(([value, groupRows]) => {
342
+ const currentPath = [...path, { field, value }];
343
+ const pathKey = currentPath.map(p => `${p.field}:${p.value}`).join("|");
344
+
345
+ // 计算统计信息
346
+ const calculations = {};
347
+ calcFields.forEach(calcField => {
348
+ if (typeof calcField === "string") {
349
+ calculations[calcField] = groupRows.reduce((sum, row) =>
350
+ sum + (parseFloat(row[calcField]) || 0), 0);
351
+ } else if (typeof calcField === "object") {
352
+ // 支持复杂计算配置
353
+ const { field: calcFieldName, type = "sum" } = calcField;
354
+ if (type === "sum") {
355
+ calculations[calcFieldName] = groupRows.reduce((sum, row) =>
356
+ sum + (parseFloat(row[calcFieldName]) || 0), 0);
357
+ } else if (type === "avg") {
358
+ const sum = groupRows.reduce((s, row) => s + (parseFloat(row[calcFieldName]) || 0), 0);
359
+ calculations[calcFieldName] = groupRows.length > 0 ? sum / groupRows.length : 0;
360
+ }
361
+ }
362
+ });
363
+
364
+ stats[pathKey] = {
365
+ path: currentPath,
366
+ value,
367
+ level,
368
+ count: groupRows.length,
369
+ calculations,
370
+ rows: groupRows
371
+ };
372
+
373
+ // 递归处理下一层
374
+ processLevel(groupRows, level + 1, currentPath);
375
+ });
376
+ }
377
+
378
+ processLevel(data);
379
+ return stats;
380
+ }
@@ -0,0 +1,2 @@
1
+ import XtText from './index.vue'
2
+ export default XtText
@@ -0,0 +1,119 @@
1
+ <template>
2
+ <span
3
+ class="xt-text"
4
+ :class="[
5
+ type ? 'xt-text--' + type : '',
6
+ { 'xt-text--bold': bold },
7
+ { 'xt-text--money': money }
8
+ ]"
9
+ :style="customStyle"
10
+ >
11
+ <slot>
12
+ <template v-if="money && displayValue !== undefined">{{ displayValue }}</template>
13
+ </slot>
14
+ </span>
15
+ </template>
16
+
17
+ <script>
18
+ export default {
19
+ name: 'XtText',
20
+ model: {
21
+ prop: 'value',
22
+ event: 'input'
23
+ },
24
+ props: {
25
+ type: {
26
+ type: String,
27
+ default: '',
28
+ validator: (val) => ['', 'primary', 'success', 'warning', 'danger'].includes(val)
29
+ },
30
+ bold: {
31
+ type: Boolean,
32
+ default: false
33
+ },
34
+ letterSpacing: {
35
+ type: [String, Number],
36
+ default: ''
37
+ },
38
+ // 金额格式化相关属性
39
+ money: {
40
+ type: Boolean,
41
+ default: false
42
+ },
43
+ value: {
44
+ type: [Number, String],
45
+ default: ''
46
+ },
47
+ currency: {
48
+ type: String,
49
+ default: 'CNY',
50
+ validator: (val) => ['CNY', 'USD', 'EUR', 'JPY', 'GBP', 'AUD', 'CAD'].includes(val)
51
+ },
52
+ decimals: {
53
+ type: Number,
54
+ default: 2,
55
+ validator: (val) => val >= 0 && val <= 10
56
+ },
57
+ locale: {
58
+ type: String,
59
+ default: 'zh-CN'
60
+ },
61
+ showSign: {
62
+ type: Boolean,
63
+ default: false
64
+ },
65
+ prefix: {
66
+ type: String,
67
+ default: ''
68
+ },
69
+ suffix: {
70
+ type: String,
71
+ default: ''
72
+ }
73
+ },
74
+ computed: {
75
+ customStyle() {
76
+ const style = {}
77
+ if (this.letterSpacing) {
78
+ style.letterSpacing = typeof this.letterSpacing === 'number' ? `${this.letterSpacing}px` : this.letterSpacing
79
+ }
80
+ return style
81
+ },
82
+ displayValue() {
83
+ if (!this.money || this.value === '' || this.value === undefined || this.value === null) {
84
+ return undefined
85
+ }
86
+
87
+ const numValue = typeof this.value === 'string' ? parseFloat(this.value) : this.value
88
+
89
+ if (isNaN(numValue)) {
90
+ return this.value
91
+ }
92
+
93
+ try {
94
+ const formatter = new Intl.NumberFormat(this.locale, {
95
+ style: 'currency',
96
+ currency: this.currency,
97
+ minimumFractionDigits: this.decimals,
98
+ maximumFractionDigits: this.decimals,
99
+ signDisplay: this.showSign ? 'always' : 'auto'
100
+ })
101
+
102
+ let result = formatter.format(numValue)
103
+
104
+ // 处理前缀和后缀
105
+ if (this.prefix) {
106
+ result = this.prefix + result
107
+ }
108
+ if (this.suffix) {
109
+ result = result + this.suffix
110
+ }
111
+
112
+ return result
113
+ } catch (e) {
114
+ return this.value
115
+ }
116
+ }
117
+ }
118
+ }
119
+ </script>
@@ -0,0 +1,67 @@
1
+ @import '../../../styles/variables.scss';
2
+
3
+ // 文字组件
4
+ .xt-text {
5
+ letter-spacing: $xt-spacing-xs;
6
+ }
7
+
8
+ // 不同类型文字颜色
9
+ .xt-text--primary {
10
+ color: var(--xt-color-primary, #1890ff);
11
+ }
12
+
13
+ .xt-text--success {
14
+ color: $xt-color-success;
15
+ }
16
+
17
+ .xt-text--warning {
18
+ color: $xt-color-warning;
19
+ }
20
+
21
+ .xt-text--danger {
22
+ color: $xt-color-danger;
23
+ }
24
+
25
+ // 粗体
26
+ .xt-text--bold {
27
+ font-weight: bold;
28
+ }
29
+
30
+ // 金额样式
31
+ .xt-text--money {
32
+ font-family: 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
33
+ font-weight: 500;
34
+ letter-spacing: 0;
35
+
36
+ &.xt-text--primary {
37
+ color: var(--xt-color-primary, #1890ff);
38
+ }
39
+
40
+ &.xt-text--success {
41
+ color: $xt-color-success;
42
+ }
43
+
44
+ &.xt-text--warning {
45
+ color: $xt-color-warning;
46
+ }
47
+
48
+ &.xt-text--danger {
49
+ color: $xt-color-danger;
50
+ }
51
+ }
52
+
53
+ // 暗色主题
54
+ html[data-theme='dark'] .xt-text--primary {
55
+ }
56
+
57
+ html[data-theme='dark'] .xt-text--success {
58
+ color: $xt-dark-color-success;
59
+ }
60
+
61
+ html[data-theme='dark'] .xt-text--warning {
62
+ color: $xt-dark-color-warning;
63
+ }
64
+
65
+ html[data-theme='dark'] .xt-text--danger {
66
+ color: $xt-dark-color-danger;
67
+ }
@@ -0,0 +1,2 @@
1
+ import XtUpload from './index.vue'
2
+ export default XtUpload