bl-common-vue3 3.8.132 → 3.8.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1342,7 +1342,7 @@
|
|
|
1342
1342
|
:url="requestInfo?.serviceUrl"
|
|
1343
1343
|
:service="requestInfo?.service"
|
|
1344
1344
|
:method="requestInfo?.serviceType"
|
|
1345
|
-
:params="
|
|
1345
|
+
:params="exportParams"
|
|
1346
1346
|
:limit="exportLimit"
|
|
1347
1347
|
:columnstitle="exportColumnsTitle"
|
|
1348
1348
|
:merge_options="exportMergeOptions"
|
|
@@ -1892,6 +1892,7 @@ export default defineComponent({
|
|
|
1892
1892
|
dictMenuAlias: "", // 后端返回的字典标识
|
|
1893
1893
|
isExport: false,
|
|
1894
1894
|
listParams: {},
|
|
1895
|
+
exportParams: {},
|
|
1895
1896
|
exportResultData: [], // 导出结果处理
|
|
1896
1897
|
importVisible: false,
|
|
1897
1898
|
tableScrollHeight: 0,
|
|
@@ -2440,7 +2441,8 @@ export default defineComponent({
|
|
|
2440
2441
|
let columnIndexObj = {};
|
|
2441
2442
|
let haveSlot = false;
|
|
2442
2443
|
cloneDeep(realColumns.value).forEach((columnItem, index) => {
|
|
2443
|
-
//
|
|
2444
|
+
// 不导出的展示列可能没有 dataIndex,也可能返回 VNode,不能参与数据转换。
|
|
2445
|
+
if (columnItem.exportHidden) return;
|
|
2444
2446
|
let virtualDataIndex = replaceObjDataIndex(columnItem.dataIndex);
|
|
2445
2447
|
if (columnItem.columnType) {
|
|
2446
2448
|
columnIndexObj[virtualDataIndex] = {
|
|
@@ -2488,20 +2490,21 @@ export default defineComponent({
|
|
|
2488
2490
|
localRowSelection.value?.selectedRowKeys
|
|
2489
2491
|
);
|
|
2490
2492
|
}
|
|
2493
|
+
// 导出组件会更新分页并追加选中条件,使用快照避免污染列表的后续查询。
|
|
2494
|
+
state.exportParams = cloneDeep(state.listParams);
|
|
2495
|
+
state.exportParams.filters = (state.exportParams.filters || []).filter(
|
|
2496
|
+
(item) => item.field !== "ids"
|
|
2497
|
+
);
|
|
2491
2498
|
if (localRowSelection.value?.selectedRowKeys.length > 0) {
|
|
2492
|
-
state.
|
|
2499
|
+
state.exportParams.filters.push({
|
|
2493
2500
|
field: "ids",
|
|
2494
2501
|
type: "custom",
|
|
2495
|
-
value: localRowSelection.value
|
|
2502
|
+
value: [...localRowSelection.value.selectedRowKeys],
|
|
2496
2503
|
});
|
|
2497
|
-
} else {
|
|
2498
|
-
state.listParams.filters = state.listParams.filters.filter(
|
|
2499
|
-
(item) => item.field != "ids"
|
|
2500
|
-
);
|
|
2501
2504
|
}
|
|
2502
2505
|
if (props.customExport) {
|
|
2503
2506
|
/* 自定义导出 */
|
|
2504
|
-
props.customExport(state.
|
|
2507
|
+
props.customExport(state.exportParams);
|
|
2505
2508
|
} else {
|
|
2506
2509
|
/* 默认导出 */
|
|
2507
2510
|
state.isExport = true;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const assert = require('node:assert/strict');
|
|
2
|
+
const {test, before, after} = require('node:test');
|
|
3
|
+
const {readFileSync} = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const Module = require('node:module');
|
|
6
|
+
const Vue = require('vue');
|
|
7
|
+
const {parse, compileTemplate} = require('@vue/compiler-sfc');
|
|
8
|
+
const {transformSync} = require('@babel/core');
|
|
9
|
+
const originalFrame = global.requestAnimationFrame;
|
|
10
|
+
before(() => {global.requestAnimationFrame = () => 0;});
|
|
11
|
+
after(() => {global.requestAnimationFrame = originalFrame;});
|
|
12
|
+
const filename = path.resolve(__dirname, '../src/components/CustomTable/index.vue');
|
|
13
|
+
const {descriptor} = parse(readFileSync(filename, 'utf8'));
|
|
14
|
+
assert.deepEqual(compileTemplate({id: 'table-export-test', filename, source: descriptor.template.content}).errors, []);
|
|
15
|
+
const loaded = new Module(filename, module);
|
|
16
|
+
loaded.require = (name) => {
|
|
17
|
+
if (name === 'vue') return {...Vue, onMounted() {}, onBeforeUnmount() {}};
|
|
18
|
+
if (name === 'lodash' || name.startsWith('dayjs')) return require(name);
|
|
19
|
+
if (name.includes('locale')) return {t: key => key, loadLanguageAsync() {}};
|
|
20
|
+
if (name.endsWith('/util')) return {getCommonLocal() {}, getLang: () => 'zh-CN', isEnv: () => false,
|
|
21
|
+
i18nMapShow: (item, keys) => item[keys[0]]};
|
|
22
|
+
return {};
|
|
23
|
+
};
|
|
24
|
+
loaded._compile(transformSync(descriptor.script.content, {filename, configFile: false, babelrc: false,
|
|
25
|
+
plugins: ['@babel/plugin-transform-modules-commonjs']}).code, filename);
|
|
26
|
+
const component = loaded.exports.default;
|
|
27
|
+
const defaults = () => Object.fromEntries(Object.entries(component.props).map(([key, value]) => [key,
|
|
28
|
+
typeof value.default === 'function' && value.type !== Function ? value.default() : value.default]));
|
|
29
|
+
const flush = async () => {for (let i = 0; i < 20; i++) await Promise.resolve();};
|
|
30
|
+
|
|
31
|
+
async function table(columns, overrides = {}) {
|
|
32
|
+
const events = [];
|
|
33
|
+
const previousWindow = global.window;
|
|
34
|
+
global.window = {addEventListener() {}};
|
|
35
|
+
try {
|
|
36
|
+
const state = component.setup({...defaults(), loadDataAtInit: false, ...overrides}, {
|
|
37
|
+
slots: {}, expose() {}, attrs: {columns}, emit: (...args) => events.push(args),
|
|
38
|
+
});
|
|
39
|
+
await flush();
|
|
40
|
+
return {state, events};
|
|
41
|
+
} finally {global.window = previousWindow;}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
test('export skips hidden presentation fields without a dataIndex', async () => {
|
|
45
|
+
const {state} = await table([
|
|
46
|
+
{key: 'company', dataIndex: 'company', title: 'Company'},
|
|
47
|
+
{key: 'extra_1', exportHidden: true, title: 'Attachment', customRender: () => {throw new Error('must not render');}},
|
|
48
|
+
]);
|
|
49
|
+
assert.deepEqual(state.exportColumnsTitle.value, {company: 'Company'});
|
|
50
|
+
state.handleExportData([{company: 'Company A'}]);
|
|
51
|
+
assert.deepEqual(state.exportResultData.value, [{company: 'Company A'}]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('built-in export snapshots selection and pagination without changing subsequent list queries', async () => {
|
|
55
|
+
const {state} = await table([]);
|
|
56
|
+
const original = {page: 3, pageSize: 20, filters: [{field: 'status', value: 0}]};
|
|
57
|
+
state.listParams.value = structuredClone(original);
|
|
58
|
+
state.selectedRowKeys.value = [1, 3];
|
|
59
|
+
state.handleExport();
|
|
60
|
+
assert.equal(state.isExport.value, true);
|
|
61
|
+
assert.deepEqual(state.exportParams.value.filters, [...original.filters, {field: 'ids', type: 'custom', value: [1, 3]}]);
|
|
62
|
+
state.exportParams.value.page = 1;
|
|
63
|
+
state.exportParams.value.pageSize = 500;
|
|
64
|
+
state.selectedRowKeys.value.push(9);
|
|
65
|
+
assert.deepEqual(state.exportParams.value.filters.at(-1).value, [1, 3]);
|
|
66
|
+
assert.deepEqual(state.listParams.value, original);
|
|
67
|
+
state.selectedRowKeys.value = [9];
|
|
68
|
+
state.handleExport();
|
|
69
|
+
assert.deepEqual(state.exportParams.value.filters.at(-1).value, [9]);
|
|
70
|
+
assert.equal(state.exportParams.value.filters.length, 2);
|
|
71
|
+
state.selectedRowKeys.value = [];
|
|
72
|
+
state.handleExport();
|
|
73
|
+
assert.deepEqual(state.exportParams.value, original);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('custom export receives the same isolated selection snapshot', async () => {
|
|
77
|
+
let exported;
|
|
78
|
+
const {state} = await table([], {customExport: params => {exported = params;}});
|
|
79
|
+
state.listParams.value = {page: 2, filters: []};
|
|
80
|
+
state.selectedRowKeys.value = [5];
|
|
81
|
+
state.handleExport();
|
|
82
|
+
assert.deepEqual(exported.filters, [{field: 'ids', type: 'custom', value: [5]}]);
|
|
83
|
+
exported.page = 1;
|
|
84
|
+
assert.equal(state.listParams.value.page, 2);
|
|
85
|
+
assert.equal(state.isExport.value, false);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('built-in complete and partial imports refresh the table and display partial errors', async () => {
|
|
89
|
+
const {state, events} = await table([], {requestInfo: {service: '/org', serviceUrl: '/company/orgBranch', serviceType: 'get'}});
|
|
90
|
+
state.importVisible.value = true;
|
|
91
|
+
await state.handleImportSuccess();
|
|
92
|
+
await flush();
|
|
93
|
+
assert.equal(state.importVisible.value, false);
|
|
94
|
+
assert.equal(events.filter(([type, event]) => type === 'request' && event.params.url === '/company/orgBranch').length, 1);
|
|
95
|
+
state.importVisible.value = true;
|
|
96
|
+
const result = {errorFileKey: 'partial', success: 2, error: 1};
|
|
97
|
+
state.handleImportrefresh(result);
|
|
98
|
+
await flush();
|
|
99
|
+
assert.equal(state.importVisible.value, false);
|
|
100
|
+
assert.equal(state.ImportResultes.value, true);
|
|
101
|
+
assert.deepEqual(state.result.value, result);
|
|
102
|
+
assert.equal(events.filter(([type, event]) => type === 'request' && event.params.url === '/company/orgBranch').length, 2);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('existing action slots still trigger the business export transformation callback', async () => {
|
|
106
|
+
const {state, events} = await table([
|
|
107
|
+
{key: 'company', dataIndex: 'company', title: 'Company'},
|
|
108
|
+
{key: 'action', dataIndex: 'id', title: 'Action'},
|
|
109
|
+
]);
|
|
110
|
+
state.columnSlots.value = ['action'];
|
|
111
|
+
state.handleExportData([{id: 1, company: 'Company A'}]);
|
|
112
|
+
assert.deepEqual(state.exportColumnsTitle.value, {company: 'Company'});
|
|
113
|
+
assert.deepEqual(events.find(([name]) => name === 'changeExportData')[1], [{id: 1, company: 'Company A'}]);
|
|
114
|
+
});
|