befly-admin 3.12.5 → 3.12.7
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 +9 -10
- package/src/App.vue +2 -4
- package/src/components/{DetailPanel.vue → detailPanel.vue} +71 -36
- package/src/components/pageDialog.vue +223 -0
- package/src/components/{PagedTableDetailPage.vue → pagedTableDetail.vue} +147 -61
- package/src/layouts/default.vue +151 -166
- package/src/main.ts +1 -1
- package/src/plugins/config.ts +6 -6
- package/src/plugins/http.ts +3 -4
- package/src/plugins/storage.ts +1 -1
- package/src/styles/global.scss +0 -11
- package/src/types/auto-imports.d.ts +14 -2
- package/src/types/components.d.ts +7 -3
- package/src/types/typed-router.d.ts +18 -13
- package/src/utils/tdesignPlugins.ts +57 -0
- package/src/views/index2.vue +1 -8
- package/src/types/unplugin-vue-router-client.d.ts +0 -3
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
<div class="page-table-detail page-table">
|
|
3
3
|
<div class="main-tool">
|
|
4
4
|
<div class="left">
|
|
5
|
-
<slot name="toolLeft" :reload="reload" :current-row="currentRow"></slot>
|
|
5
|
+
<slot name="toolLeft" :reload="reload" :current-row="$Data.currentRow"></slot>
|
|
6
6
|
</div>
|
|
7
7
|
<div class="right">
|
|
8
|
-
<slot name="toolRight" :reload="reload" :current-row="currentRow"></slot>
|
|
8
|
+
<slot name="toolRight" :reload="reload" :current-row="$Data.currentRow"></slot>
|
|
9
9
|
</div>
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
12
|
<div class="main-content">
|
|
13
13
|
<div class="main-table">
|
|
14
|
-
<TTable :data="rows" :columns="
|
|
14
|
+
<TTable :data="$Data.rows" :columns="tableColumns" :loading="$Data.loading" :active-row-keys="$Data.activeRowKeys" :row-key="rowKey" :height="tableHeight" active-row-type="single" @active-change="onActiveChange">
|
|
15
15
|
<template #operation="scope">
|
|
16
16
|
<slot name="operation" v-bind="buildOperationSlotProps(scope)"></slot>
|
|
17
17
|
</template>
|
|
@@ -23,26 +23,32 @@
|
|
|
23
23
|
</div>
|
|
24
24
|
|
|
25
25
|
<div class="main-detail">
|
|
26
|
-
<slot name="detail" :row="currentRow" :reload="reload">
|
|
27
|
-
<DetailPanel :data="currentRow" :fields="detailFields" />
|
|
26
|
+
<slot name="detail" :row="$Data.currentRow" :reload="reload">
|
|
27
|
+
<DetailPanel :data="$Data.currentRow" :fields="detailFields" />
|
|
28
28
|
</slot>
|
|
29
29
|
</div>
|
|
30
30
|
</div>
|
|
31
31
|
|
|
32
32
|
<div class="main-page">
|
|
33
|
-
<TPagination :current-page="pager.currentPage" :page-size="pager.limit" :total="pager.total"
|
|
33
|
+
<TPagination :current-page="$Data.pager.currentPage" :page-size="$Data.pager.limit" :total="$Data.pager.total" align="right" :layout="paginationLayout" @current-change="onPageChange" @page-size-change="onPageSizeChange" />
|
|
34
34
|
</div>
|
|
35
35
|
|
|
36
|
-
<slot name="dialogs" :reload="reload" :current-row="currentRow"></slot>
|
|
36
|
+
<slot name="dialogs" :reload="reload" :current-row="$Data.currentRow"></slot>
|
|
37
37
|
</div>
|
|
38
38
|
</template>
|
|
39
39
|
|
|
40
40
|
<script setup lang="ts">
|
|
41
|
-
|
|
42
|
-
import { DialogPlugin, MessagePlugin, Pagination as TPagination, Table as TTable, type PrimaryTableCol, type TableRowData } from "tdesign-vue-next";
|
|
41
|
+
defineOptions({ name: "PagedTableDetail" });
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
type PrimaryTableCol = import("tdesign-vue-next").PrimaryTableCol;
|
|
44
|
+
type TableRowData = import("tdesign-vue-next").TableRowData;
|
|
45
|
+
|
|
46
|
+
type PagedTableCol = PrimaryTableCol & {
|
|
47
|
+
// 页面自定义字段:是否为“详情专属列”(默认 false)
|
|
48
|
+
// - detail: false => 表格展示(同时也会出现在详情里,且顺序靠前)
|
|
49
|
+
// - detail: true => 仅在详情展示(顺序靠后)
|
|
50
|
+
detail?: boolean;
|
|
51
|
+
};
|
|
46
52
|
|
|
47
53
|
type PagerState = {
|
|
48
54
|
currentPage: number;
|
|
@@ -93,24 +99,20 @@ type LoadListOptions = {
|
|
|
93
99
|
|
|
94
100
|
const props = withDefaults(
|
|
95
101
|
defineProps<{
|
|
96
|
-
columns:
|
|
97
|
-
detailFields?: PrimaryTableCol[];
|
|
102
|
+
columns: PagedTableCol[];
|
|
98
103
|
rowKey?: string;
|
|
99
104
|
tableHeight?: string;
|
|
100
105
|
pageSize?: number;
|
|
101
106
|
endpoints?: Endpoints<TableRowData>;
|
|
102
|
-
paginationAlign?: "left" | "center" | "right";
|
|
103
107
|
paginationLayout?: string;
|
|
104
108
|
autoLoad?: boolean;
|
|
105
109
|
tableSlotNames?: string[];
|
|
106
110
|
}>(),
|
|
107
111
|
{
|
|
108
|
-
detailFields: undefined,
|
|
109
112
|
rowKey: "id",
|
|
110
113
|
tableHeight: "calc(100vh - var(--search-height) - var(--pagination-height) - var(--layout-gap) * 4)",
|
|
111
114
|
pageSize: 30,
|
|
112
115
|
endpoints: undefined,
|
|
113
|
-
paginationAlign: "right",
|
|
114
116
|
paginationLayout: "total, prev, pager, next, jumper",
|
|
115
117
|
autoLoad: true,
|
|
116
118
|
tableSlotNames: undefined
|
|
@@ -125,24 +127,108 @@ const emit = defineEmits<{
|
|
|
125
127
|
|
|
126
128
|
const slots = useSlots();
|
|
127
129
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
const $Data = $ref({
|
|
131
|
+
rows: [] as TableRowData[],
|
|
132
|
+
loading: false,
|
|
133
|
+
pager: {
|
|
134
|
+
currentPage: 1,
|
|
135
|
+
limit: props.pageSize,
|
|
136
|
+
total: 0
|
|
137
|
+
} as PagerState,
|
|
138
|
+
currentRow: null as TableRowData | null,
|
|
139
|
+
activeRowKeys: [] as Array<string | number>
|
|
134
140
|
});
|
|
135
141
|
|
|
136
|
-
const currentRow = ref<TableRowData | null>(null);
|
|
137
|
-
const activeRowKeys = ref<Array<string | number>>([]);
|
|
138
|
-
|
|
139
142
|
let requestSeq = 0;
|
|
140
143
|
|
|
144
|
+
function getColKey(col: PrimaryTableCol): string {
|
|
145
|
+
const record = col as unknown as Record<string, unknown>;
|
|
146
|
+
const rawColKey = record["colKey"];
|
|
147
|
+
if (typeof rawColKey === "string") return rawColKey;
|
|
148
|
+
if (typeof rawColKey === "number") return String(rawColKey);
|
|
149
|
+
return "";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function filterValidColumns(input: unknown): PrimaryTableCol[] {
|
|
153
|
+
if (!Array.isArray(input)) {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const out: PrimaryTableCol[] = [];
|
|
158
|
+
|
|
159
|
+
for (const col of input as Array<unknown>) {
|
|
160
|
+
if (!col || typeof col !== "object") {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const key = getColKey(col as PrimaryTableCol);
|
|
164
|
+
if (key.length === 0) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
out.push(col as PrimaryTableCol);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function mergeDetailColumns(mainColumns: PrimaryTableCol[], extraColumns: PrimaryTableCol[]): PrimaryTableCol[] {
|
|
174
|
+
const out: PrimaryTableCol[] = [];
|
|
175
|
+
const set = new Set<string>();
|
|
176
|
+
|
|
177
|
+
for (const col of mainColumns) {
|
|
178
|
+
out.push(col);
|
|
179
|
+
const key = getColKey(col);
|
|
180
|
+
if (key) {
|
|
181
|
+
set.add(key);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
for (const col of extraColumns) {
|
|
186
|
+
const key = getColKey(col);
|
|
187
|
+
if (key && set.has(key)) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
out.push(col);
|
|
191
|
+
if (key) {
|
|
192
|
+
set.add(key);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function isDetail(col: PagedTableCol): boolean {
|
|
200
|
+
const record = col as unknown as Record<string, unknown>;
|
|
201
|
+
if (record["detail"] === true) return true;
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const tableColumns = computed(() => {
|
|
206
|
+
const out: PagedTableCol[] = [];
|
|
207
|
+
const cols = filterValidColumns(props.columns) as PagedTableCol[];
|
|
208
|
+
for (const col of cols) {
|
|
209
|
+
if (isDetail(col)) continue;
|
|
210
|
+
out.push(col);
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
});
|
|
214
|
+
|
|
141
215
|
const detailFields = computed(() => {
|
|
142
|
-
|
|
143
|
-
|
|
216
|
+
// 只维护一个 columns:
|
|
217
|
+
// - detail: false(默认)=> 表格展示(同时也会出现在详情里,且顺序靠前)
|
|
218
|
+
// - detail: true => 仅在详情展示(顺序靠后)
|
|
219
|
+
const extras: PagedTableCol[] = [];
|
|
220
|
+
const cols = filterValidColumns(props.columns) as PagedTableCol[];
|
|
221
|
+
for (const col of cols) {
|
|
222
|
+
if (!isDetail(col)) continue;
|
|
223
|
+
extras.push(col);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (extras.length > 0) {
|
|
227
|
+
return mergeDetailColumns(tableColumns.value, extras);
|
|
144
228
|
}
|
|
145
|
-
|
|
229
|
+
|
|
230
|
+
// 默认复用表格列
|
|
231
|
+
return tableColumns.value;
|
|
146
232
|
});
|
|
147
233
|
|
|
148
234
|
const forwardedTableSlotNames = computed(() => {
|
|
@@ -172,7 +258,7 @@ const forwardedTableSlotNames = computed(() => {
|
|
|
172
258
|
});
|
|
173
259
|
|
|
174
260
|
function setCurrentRow(row: TableRowData | null): void {
|
|
175
|
-
currentRow
|
|
261
|
+
$Data.currentRow = row;
|
|
176
262
|
emit("row-change", { row: row });
|
|
177
263
|
}
|
|
178
264
|
|
|
@@ -190,7 +276,7 @@ function getRowKeyValue(row: TableRowData): string | number | null {
|
|
|
190
276
|
function applyAutoSelection(list: TableRowData[], preferKey: string | number | null): void {
|
|
191
277
|
if (!Array.isArray(list) || list.length === 0) {
|
|
192
278
|
setCurrentRow(null);
|
|
193
|
-
activeRowKeys
|
|
279
|
+
$Data.activeRowKeys = [];
|
|
194
280
|
return;
|
|
195
281
|
}
|
|
196
282
|
|
|
@@ -199,7 +285,7 @@ function applyAutoSelection(list: TableRowData[], preferKey: string | number | n
|
|
|
199
285
|
const k = getRowKeyValue(row);
|
|
200
286
|
if (k === preferKey) {
|
|
201
287
|
setCurrentRow(row);
|
|
202
|
-
activeRowKeys
|
|
288
|
+
$Data.activeRowKeys = [k];
|
|
203
289
|
return;
|
|
204
290
|
}
|
|
205
291
|
}
|
|
@@ -210,11 +296,11 @@ function applyAutoSelection(list: TableRowData[], preferKey: string | number | n
|
|
|
210
296
|
setCurrentRow(first);
|
|
211
297
|
|
|
212
298
|
if (firstKey === null) {
|
|
213
|
-
activeRowKeys
|
|
299
|
+
$Data.activeRowKeys = [];
|
|
214
300
|
return;
|
|
215
301
|
}
|
|
216
302
|
|
|
217
|
-
activeRowKeys
|
|
303
|
+
$Data.activeRowKeys = [firstKey];
|
|
218
304
|
}
|
|
219
305
|
|
|
220
306
|
function getLastPage(total: number, limit: number): number {
|
|
@@ -231,22 +317,22 @@ async function loadList(options?: LoadListOptions): Promise<void> {
|
|
|
231
317
|
return;
|
|
232
318
|
}
|
|
233
319
|
|
|
234
|
-
const preferKey = options?.keepSelection ? (currentRow
|
|
320
|
+
const preferKey = options?.keepSelection ? ($Data.currentRow ? getRowKeyValue($Data.currentRow) : null) : null;
|
|
235
321
|
|
|
236
322
|
requestSeq += 1;
|
|
237
323
|
const seq = requestSeq;
|
|
238
324
|
|
|
239
|
-
loading
|
|
325
|
+
$Data.loading = true;
|
|
240
326
|
try {
|
|
241
327
|
const pageKey = listEndpoint.pageKey || "page";
|
|
242
328
|
const limitKey = listEndpoint.limitKey || "limit";
|
|
243
329
|
|
|
244
330
|
const data: Record<string, unknown> = {};
|
|
245
|
-
data[pageKey] = pager.
|
|
246
|
-
data[limitKey] = pager.
|
|
331
|
+
data[pageKey] = $Data.pager.currentPage;
|
|
332
|
+
data[limitKey] = $Data.pager.limit;
|
|
247
333
|
|
|
248
334
|
if (listEndpoint.buildData) {
|
|
249
|
-
const extra = listEndpoint.buildData({ currentPage: pager.
|
|
335
|
+
const extra = listEndpoint.buildData({ currentPage: $Data.pager.currentPage, limit: $Data.pager.limit });
|
|
250
336
|
if (extra && typeof extra === "object") {
|
|
251
337
|
for (const k of Object.keys(extra)) {
|
|
252
338
|
data[k] = extra[k];
|
|
@@ -270,16 +356,16 @@ async function loadList(options?: LoadListOptions): Promise<void> {
|
|
|
270
356
|
// B:页码越界修正(删除/过滤等导致 total 变小后,当前页可能已不存在)
|
|
271
357
|
const allowPageFallback = options?.allowPageFallback !== false;
|
|
272
358
|
if (allowPageFallback && lists.length === 0 && total > 0) {
|
|
273
|
-
const lastPage = getLastPage(total, pager.
|
|
274
|
-
if (pager.
|
|
275
|
-
pager.
|
|
359
|
+
const lastPage = getLastPage(total, $Data.pager.limit);
|
|
360
|
+
if ($Data.pager.currentPage > lastPage) {
|
|
361
|
+
$Data.pager.currentPage = lastPage;
|
|
276
362
|
await loadList({ keepSelection: false, allowPageFallback: false });
|
|
277
363
|
return;
|
|
278
364
|
}
|
|
279
365
|
}
|
|
280
366
|
|
|
281
|
-
rows
|
|
282
|
-
pager.
|
|
367
|
+
$Data.rows = lists;
|
|
368
|
+
$Data.pager.total = total;
|
|
283
369
|
|
|
284
370
|
applyAutoSelection(lists, preferKey);
|
|
285
371
|
emit("loaded", { rows: lists, total: total });
|
|
@@ -287,45 +373,45 @@ async function loadList(options?: LoadListOptions): Promise<void> {
|
|
|
287
373
|
if (seq !== requestSeq) {
|
|
288
374
|
return;
|
|
289
375
|
}
|
|
290
|
-
|
|
376
|
+
void showMessageError("加载数据失败");
|
|
291
377
|
} finally {
|
|
292
378
|
if (seq === requestSeq) {
|
|
293
|
-
loading
|
|
379
|
+
$Data.loading = false;
|
|
294
380
|
}
|
|
295
381
|
}
|
|
296
382
|
}
|
|
297
383
|
|
|
298
384
|
async function reload(options?: { keepSelection?: boolean; resetPage?: boolean }): Promise<void> {
|
|
299
385
|
if (options?.resetPage) {
|
|
300
|
-
pager.
|
|
386
|
+
$Data.pager.currentPage = 1;
|
|
301
387
|
}
|
|
302
388
|
await loadList({ keepSelection: options?.keepSelection, allowPageFallback: true });
|
|
303
389
|
}
|
|
304
390
|
|
|
305
391
|
function onPageChange(info: { currentPage: number }): void {
|
|
306
|
-
pager.
|
|
392
|
+
$Data.pager.currentPage = info.currentPage;
|
|
307
393
|
void reload({ keepSelection: true });
|
|
308
394
|
}
|
|
309
395
|
|
|
310
396
|
function onPageSizeChange(info: { pageSize: number }): void {
|
|
311
|
-
pager.
|
|
312
|
-
pager.
|
|
397
|
+
$Data.pager.limit = info.pageSize;
|
|
398
|
+
$Data.pager.currentPage = 1;
|
|
313
399
|
void reload({ keepSelection: false });
|
|
314
400
|
}
|
|
315
401
|
|
|
316
402
|
function onActiveChange(value: Array<string | number>, context: { activeRowList?: Array<{ row: TableRowData }> }): void {
|
|
317
403
|
// 禁止取消高亮:如果新值为空,保持当前选中
|
|
318
404
|
if (value.length === 0) {
|
|
319
|
-
if (activeRowKeys.
|
|
405
|
+
if ($Data.activeRowKeys.length > 0) {
|
|
320
406
|
return;
|
|
321
407
|
}
|
|
322
408
|
|
|
323
|
-
activeRowKeys
|
|
409
|
+
$Data.activeRowKeys = [];
|
|
324
410
|
setCurrentRow(null);
|
|
325
411
|
return;
|
|
326
412
|
}
|
|
327
413
|
|
|
328
|
-
activeRowKeys
|
|
414
|
+
$Data.activeRowKeys = value;
|
|
329
415
|
|
|
330
416
|
const list = context.activeRowList;
|
|
331
417
|
if (list && Array.isArray(list) && list.length > 0) {
|
|
@@ -336,7 +422,7 @@ function onActiveChange(value: Array<string | number>, context: { activeRowList?
|
|
|
336
422
|
|
|
337
423
|
// C1:兜底回查(activeRowList 缺失时按 key 在 rows 中回查)
|
|
338
424
|
const activeKey = value[0];
|
|
339
|
-
for (const row of rows
|
|
425
|
+
for (const row of $Data.rows) {
|
|
340
426
|
const key = getRowKeyValue(row);
|
|
341
427
|
if (key === activeKey) {
|
|
342
428
|
setCurrentRow(row);
|
|
@@ -365,7 +451,7 @@ function getDeleteConfirmContent(ep: DeleteEndpoint<TableRowData>, row: TableRow
|
|
|
365
451
|
async function deleteRow(row: TableRowData): Promise<void> {
|
|
366
452
|
const ep = props.endpoints?.delete;
|
|
367
453
|
if (!ep) {
|
|
368
|
-
|
|
454
|
+
await showMessageError("未配置删除接口");
|
|
369
455
|
return;
|
|
370
456
|
}
|
|
371
457
|
|
|
@@ -374,7 +460,7 @@ async function deleteRow(row: TableRowData): Promise<void> {
|
|
|
374
460
|
const rawId = record[idKey];
|
|
375
461
|
|
|
376
462
|
if (rawId === null || rawId === undefined || rawId === "") {
|
|
377
|
-
|
|
463
|
+
await showMessageError("删除失败:缺少主键");
|
|
378
464
|
return;
|
|
379
465
|
}
|
|
380
466
|
|
|
@@ -391,7 +477,7 @@ async function deleteRow(row: TableRowData): Promise<void> {
|
|
|
391
477
|
}
|
|
392
478
|
};
|
|
393
479
|
|
|
394
|
-
dialog =
|
|
480
|
+
dialog = await openConfirmDialog({
|
|
395
481
|
header: confirmContent.header,
|
|
396
482
|
body: confirmContent.body,
|
|
397
483
|
status: confirmContent.status || "warning",
|
|
@@ -420,12 +506,12 @@ async function deleteRow(row: TableRowData): Promise<void> {
|
|
|
420
506
|
dropKeyValue: ep.dropKeyValue
|
|
421
507
|
});
|
|
422
508
|
|
|
423
|
-
|
|
509
|
+
await showMessageSuccess("删除成功");
|
|
424
510
|
destroy();
|
|
425
511
|
emit("deleted", { row: row });
|
|
426
512
|
await reload({ keepSelection: true });
|
|
427
513
|
} catch (error) {
|
|
428
|
-
|
|
514
|
+
await showMessageError("删除失败");
|
|
429
515
|
} finally {
|
|
430
516
|
if (dialog && typeof dialog.setConfirmLoading === "function") {
|
|
431
517
|
dialog.setConfirmLoading(false);
|
|
@@ -454,9 +540,9 @@ function buildOperationSlotProps(scope: Record<string, unknown>): Record<string,
|
|
|
454
540
|
defineExpose({
|
|
455
541
|
reload: reload,
|
|
456
542
|
deleteRow: deleteRow,
|
|
457
|
-
rows: rows,
|
|
458
|
-
pager: pager,
|
|
459
|
-
currentRow: currentRow
|
|
543
|
+
rows: $Data.rows,
|
|
544
|
+
pager: $Data.pager,
|
|
545
|
+
currentRow: $Data.currentRow
|
|
460
546
|
});
|
|
461
547
|
|
|
462
548
|
onMounted(() => {
|