befly-admin-ui 1.8.21 → 1.8.23
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/components/detailPanel.vue +4 -14
- package/components/{pagedTableDetail.vue → pageTableDetail.vue} +83 -28
- package/package.json +2 -2
- package/utils/formatFieldValue.js +106 -0
- package/views/config/dict/index.vue +1 -1
- package/views/config/dictType/index.vue +1 -1
- package/views/config/system/index.vue +1 -1
- package/views/log/email/index.vue +1 -1
- package/views/log/login/index.vue +3 -2
- package/views/log/operate/index.vue +1 -1
- package/views/login_1/index.vue +1 -1
- package/views/people/admin/index.vue +1 -1
- package/views/permission/role/index.vue +1 -1
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</template>
|
|
18
18
|
<!-- 默认显示 -->
|
|
19
19
|
<template v-else>
|
|
20
|
-
{{
|
|
20
|
+
{{ formatFieldValue(data[field.colKey], field) }}
|
|
21
21
|
</template>
|
|
22
22
|
</div>
|
|
23
23
|
</div>
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
<script setup>
|
|
34
34
|
import { computed } from "vue";
|
|
35
35
|
import { Tag as TTag } from "tdesign-vue-next";
|
|
36
|
+
import { formatFieldValue } from "../utils/formatFieldValue.js";
|
|
36
37
|
|
|
37
38
|
const props = defineProps({
|
|
38
39
|
/**
|
|
@@ -96,7 +97,7 @@ const normalizedFields = computed(() => {
|
|
|
96
97
|
colKey: item.colKey,
|
|
97
98
|
title: item.title || item.colKey,
|
|
98
99
|
default: item.default,
|
|
99
|
-
|
|
100
|
+
format: item.format
|
|
100
101
|
};
|
|
101
102
|
})
|
|
102
103
|
.filter((item) => {
|
|
@@ -109,7 +110,7 @@ const normalizedFields = computed(() => {
|
|
|
109
110
|
colKey: "id",
|
|
110
111
|
title: "ID",
|
|
111
112
|
default: "-",
|
|
112
|
-
|
|
113
|
+
format: undefined
|
|
113
114
|
});
|
|
114
115
|
}
|
|
115
116
|
|
|
@@ -122,17 +123,6 @@ const normalizedFields = computed(() => {
|
|
|
122
123
|
|
|
123
124
|
return safeFields;
|
|
124
125
|
});
|
|
125
|
-
|
|
126
|
-
function formatValue(value, field) {
|
|
127
|
-
if (value === null || value === undefined || value === "") {
|
|
128
|
-
return field.default || "-";
|
|
129
|
-
}
|
|
130
|
-
if (field.formatter) {
|
|
131
|
-
const result = field.formatter(value);
|
|
132
|
-
return result;
|
|
133
|
-
}
|
|
134
|
-
return value;
|
|
135
|
-
}
|
|
136
126
|
</script>
|
|
137
127
|
|
|
138
128
|
<style scoped lang="scss">
|
|
@@ -11,13 +11,16 @@
|
|
|
11
11
|
|
|
12
12
|
<div class="main-content">
|
|
13
13
|
<div class="main-table">
|
|
14
|
-
<TTable :data="$Data.rows" :columns="tableColumns" :loading="$Data.loading" :active-row-keys="$Data.activeRowKeys" :row-key="rowKey" :height="
|
|
14
|
+
<TTable :data="$Data.rows" :columns="tableColumns" :loading="$Data.loading" :active-row-keys="$Data.activeRowKeys" :row-key="rowKey" :height="resolvedTableHeight" 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>
|
|
18
18
|
|
|
19
|
-
<template v-for="name in
|
|
20
|
-
<slot :name="name" v-bind="scope"></slot>
|
|
19
|
+
<template v-for="name in tableRenderSlotNames" :key="name" v-slot:[name]="scope">
|
|
20
|
+
<slot v-if="$slots[name]" :name="name" v-bind="scope"></slot>
|
|
21
|
+
<template v-else>
|
|
22
|
+
{{ formatTableCell(scope.row, name) }}
|
|
23
|
+
</template>
|
|
21
24
|
</template>
|
|
22
25
|
</TTable>
|
|
23
26
|
</div>
|
|
@@ -42,6 +45,7 @@ import { computed, onMounted, reactive, useSlots } from "vue";
|
|
|
42
45
|
import { DialogPlugin, MessagePlugin, Pagination as TPagination, Table as TTable } from "tdesign-vue-next";
|
|
43
46
|
import DetailPanel from "./detailPanel.vue";
|
|
44
47
|
import { $Http } from "@/plugins/http";
|
|
48
|
+
import { formatFieldValue } from "../utils/formatFieldValue.js";
|
|
45
49
|
|
|
46
50
|
const props = defineProps({
|
|
47
51
|
columns: {
|
|
@@ -56,6 +60,10 @@ const props = defineProps({
|
|
|
56
60
|
type: String,
|
|
57
61
|
default: "calc(100vh - var(--search-height) - var(--pagination-height) - var(--layout-gap) * 4)"
|
|
58
62
|
},
|
|
63
|
+
isPagination: {
|
|
64
|
+
type: Boolean,
|
|
65
|
+
default: true
|
|
66
|
+
},
|
|
59
67
|
pageSize: {
|
|
60
68
|
type: Number,
|
|
61
69
|
default: 30
|
|
@@ -151,39 +159,56 @@ function mergeDetailColumns(mainColumns, extraColumns) {
|
|
|
151
159
|
return out;
|
|
152
160
|
}
|
|
153
161
|
|
|
154
|
-
|
|
155
|
-
const record = col;
|
|
156
|
-
if (record["detail"] === true) return true;
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const tableColumns = computed(() => {
|
|
161
|
-
const out = [];
|
|
162
|
-
const cols = filterValidColumns(props.columns);
|
|
163
|
-
for (const col of cols) {
|
|
164
|
-
if (isDetail(col)) continue;
|
|
165
|
-
out.push(col);
|
|
166
|
-
}
|
|
167
|
-
return out;
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
const detailFields = computed(() => {
|
|
162
|
+
const columnsMeta = computed(() => {
|
|
171
163
|
// 只维护一个 columns:
|
|
172
164
|
// - detail: false(默认)=> 表格展示(同时也会出现在详情里,且顺序靠前)
|
|
173
165
|
// - detail: true => 仅在详情展示(顺序靠后)
|
|
174
|
-
const
|
|
166
|
+
const tableColumns = [];
|
|
167
|
+
const extraDetailColumns = [];
|
|
168
|
+
const tableColumnMap = {};
|
|
169
|
+
const formatKeys = [];
|
|
170
|
+
|
|
175
171
|
const cols = filterValidColumns(props.columns);
|
|
176
172
|
for (const col of cols) {
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
173
|
+
if (col?.detail === true) {
|
|
174
|
+
extraDetailColumns.push(col);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
tableColumns.push(col);
|
|
179
|
+
|
|
180
|
+
const key = getColKey(col);
|
|
181
|
+
if (!key) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
tableColumnMap[key] = col;
|
|
180
186
|
|
|
181
|
-
|
|
182
|
-
|
|
187
|
+
const format = col.format;
|
|
188
|
+
if ((typeof format === "string" && format.trim().length > 0) || typeof format === "function") {
|
|
189
|
+
formatKeys.push(key);
|
|
190
|
+
}
|
|
183
191
|
}
|
|
184
192
|
|
|
185
|
-
|
|
186
|
-
|
|
193
|
+
const detailFields = extraDetailColumns.length > 0 ? mergeDetailColumns(tableColumns, extraDetailColumns) : tableColumns;
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
tableColumns: tableColumns,
|
|
197
|
+
detailFields: detailFields,
|
|
198
|
+
tableColumnMap: tableColumnMap,
|
|
199
|
+
formatKeys: formatKeys
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const tableColumns = computed(() => columnsMeta.value.tableColumns);
|
|
204
|
+
|
|
205
|
+
const detailFields = computed(() => columnsMeta.value.detailFields);
|
|
206
|
+
|
|
207
|
+
const resolvedTableHeight = computed(() => {
|
|
208
|
+
if (props.isPagination) {
|
|
209
|
+
return props.tableHeight;
|
|
210
|
+
}
|
|
211
|
+
return "calc(100vh - var(--search-height) - var(--layout-gap) * 2)";
|
|
187
212
|
});
|
|
188
213
|
|
|
189
214
|
const forwardedTableSlotNames = computed(() => {
|
|
@@ -212,6 +237,36 @@ const forwardedTableSlotNames = computed(() => {
|
|
|
212
237
|
return names;
|
|
213
238
|
});
|
|
214
239
|
|
|
240
|
+
const tableRenderSlotNames = computed(() => {
|
|
241
|
+
const out = [];
|
|
242
|
+
const set = new Set();
|
|
243
|
+
|
|
244
|
+
for (const name of forwardedTableSlotNames.value) {
|
|
245
|
+
if (!name || set.has(name)) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
set.add(name);
|
|
249
|
+
out.push(name);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
for (const key of columnsMeta.value.formatKeys) {
|
|
253
|
+
if (!key || set.has(key)) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
set.add(key);
|
|
257
|
+
out.push(key);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return out;
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
function formatTableCell(row, colKey) {
|
|
264
|
+
const record = row && typeof row === "object" ? row : {};
|
|
265
|
+
const value = record[colKey];
|
|
266
|
+
const field = columnsMeta.value.tableColumnMap[colKey] || {};
|
|
267
|
+
return formatFieldValue(value, field);
|
|
268
|
+
}
|
|
269
|
+
|
|
215
270
|
function setCurrentRow(row) {
|
|
216
271
|
$Data.currentRow = row;
|
|
217
272
|
emit("row-change", { row: row });
|
package/package.json
CHANGED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
function toDate(value) {
|
|
2
|
+
if (value instanceof Date) {
|
|
3
|
+
return Number.isFinite(value.getTime()) ? value : null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (typeof value === "number") {
|
|
7
|
+
if (!Number.isFinite(value)) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const ms = Math.abs(value) < 1000000000000 ? value * 1000 : value;
|
|
11
|
+
const date = new Date(ms);
|
|
12
|
+
return Number.isFinite(date.getTime()) ? date : null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (typeof value === "string") {
|
|
16
|
+
const text = value.trim();
|
|
17
|
+
if (text.length === 0) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (/^\d+$/.test(text)) {
|
|
22
|
+
const num = Number(text);
|
|
23
|
+
if (!Number.isFinite(num)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const ms = Math.abs(num) < 1000000000000 ? num * 1000 : num;
|
|
27
|
+
const date = new Date(ms);
|
|
28
|
+
return Number.isFinite(date.getTime()) ? date : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const date = new Date(text);
|
|
32
|
+
return Number.isFinite(date.getTime()) ? date : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getDateParts(value) {
|
|
39
|
+
const date = toDate(value);
|
|
40
|
+
if (!date) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
year: date.getFullYear(),
|
|
46
|
+
month: String(date.getMonth() + 1).padStart(2, "0"),
|
|
47
|
+
day: String(date.getDate()).padStart(2, "0"),
|
|
48
|
+
hours: String(date.getHours()).padStart(2, "0"),
|
|
49
|
+
minutes: String(date.getMinutes()).padStart(2, "0"),
|
|
50
|
+
seconds: String(date.getSeconds()).padStart(2, "0")
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function formatDateTime(value) {
|
|
55
|
+
const parts = getDateParts(value);
|
|
56
|
+
if (!parts) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return `${parts.year}-${parts.month}-${parts.day} ${parts.hours}:${parts.minutes}:${parts.seconds}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function formatDate(value) {
|
|
64
|
+
const parts = getDateParts(value);
|
|
65
|
+
if (!parts) {
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return `${parts.year}-${parts.month}-${parts.day}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function formatTime(value) {
|
|
73
|
+
const parts = getDateParts(value);
|
|
74
|
+
if (!parts) {
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return `${parts.hours}:${parts.minutes}:${parts.seconds}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function formatFieldValue(value, field = {}) {
|
|
82
|
+
if (value === null || value === undefined || value === "") {
|
|
83
|
+
return field.default || "-";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const format = field.format;
|
|
87
|
+
|
|
88
|
+
if (typeof format === "function") {
|
|
89
|
+
return format(value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (typeof format === "string") {
|
|
93
|
+
const normalized = format.trim();
|
|
94
|
+
if (normalized === "date") {
|
|
95
|
+
return formatDate(value);
|
|
96
|
+
}
|
|
97
|
+
if (normalized === "date-time") {
|
|
98
|
+
return formatDateTime(value);
|
|
99
|
+
}
|
|
100
|
+
if (normalized === "time") {
|
|
101
|
+
return formatTime(value);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
@@ -56,7 +56,7 @@ import { Button as TButton, Dropdown as TDropdown, DropdownItem as TDropdownItem
|
|
|
56
56
|
import { AddIcon, ChevronDownIcon, DeleteIcon, EditIcon, RefreshIcon, SearchIcon } from "tdesign-icons-vue-next";
|
|
57
57
|
import EditDialog from "./components/edit.vue";
|
|
58
58
|
import { $Http } from "@/plugins/http";
|
|
59
|
-
import PageTableDetail from "befly-admin-ui/components/
|
|
59
|
+
import PageTableDetail from "befly-admin-ui/components/pageTableDetail.vue";
|
|
60
60
|
import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
|
|
61
61
|
|
|
62
62
|
const $Data = reactive({
|
|
@@ -51,7 +51,7 @@ import { reactive } from "vue";
|
|
|
51
51
|
import { Button as TButton, Dropdown as TDropdown, DropdownItem as TDropdownItem, DropdownMenu as TDropdownMenu, Input as TInput } from "tdesign-vue-next";
|
|
52
52
|
import { AddIcon, ChevronDownIcon, DeleteIcon, EditIcon, RefreshIcon, SearchIcon } from "tdesign-icons-vue-next";
|
|
53
53
|
import EditDialog from "./components/edit.vue";
|
|
54
|
-
import PageTableDetail from "befly-admin-ui/components/
|
|
54
|
+
import PageTableDetail from "befly-admin-ui/components/pageTableDetail.vue";
|
|
55
55
|
import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
|
|
56
56
|
|
|
57
57
|
const $Data = reactive({
|
|
@@ -80,7 +80,7 @@ import { Button as TButton, Dropdown as TDropdown, DropdownItem as TDropdownItem
|
|
|
80
80
|
import { AddIcon, ChevronDownIcon, DeleteIcon, EditIcon, RefreshIcon } from "tdesign-icons-vue-next";
|
|
81
81
|
import EditDialog from "./components/edit.vue";
|
|
82
82
|
import DetailPanel from "befly-admin-ui/components/detailPanel.vue";
|
|
83
|
-
import PageTableDetail from "befly-admin-ui/components/
|
|
83
|
+
import PageTableDetail from "befly-admin-ui/components/pageTableDetail.vue";
|
|
84
84
|
import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
|
|
85
85
|
|
|
86
86
|
// 响应式数据
|
|
@@ -75,7 +75,7 @@ import { CheckCircleIcon, RefreshIcon, SendIcon } from "tdesign-icons-vue-next";
|
|
|
75
75
|
import PageDialog from "befly-admin-ui/components/pageDialog.vue";
|
|
76
76
|
import DetailPanel from "befly-admin-ui/components/detailPanel.vue";
|
|
77
77
|
import { $Http } from "@/plugins/http";
|
|
78
|
-
import PageTableDetail from "befly-admin-ui/components/
|
|
78
|
+
import PageTableDetail from "befly-admin-ui/components/pageTableDetail.vue";
|
|
79
79
|
import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
|
|
80
80
|
const sendFormRef = ref(null);
|
|
81
81
|
|
|
@@ -43,7 +43,7 @@ import { reactive } from "vue";
|
|
|
43
43
|
import { Button as TButton, Tag as TTag } from "tdesign-vue-next";
|
|
44
44
|
import { RefreshIcon } from "tdesign-icons-vue-next";
|
|
45
45
|
import DetailPanel from "befly-admin-ui/components/detailPanel.vue";
|
|
46
|
-
import PageTableDetail from "befly-admin-ui/components/
|
|
46
|
+
import PageTableDetail from "befly-admin-ui/components/pageTableDetail.vue";
|
|
47
47
|
import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
|
|
48
48
|
|
|
49
49
|
// 响应式数据
|
|
@@ -54,8 +54,9 @@ const $Data = reactive({
|
|
|
54
54
|
{ colKey: "browserName", title: "浏览器" },
|
|
55
55
|
{ colKey: "osName", title: "操作系统" },
|
|
56
56
|
{ colKey: "deviceType", title: "设备类型" },
|
|
57
|
-
{ colKey: "
|
|
57
|
+
{ colKey: "createdAt", title: "登录时间", format: "date-time" },
|
|
58
58
|
{ colKey: "loginResult", title: "登录结果" },
|
|
59
|
+
// 详情面板
|
|
59
60
|
{ colKey: "nickname", title: "昵称", detail: true },
|
|
60
61
|
{ colKey: "browserVersion", title: "浏览器版本", detail: true },
|
|
61
62
|
{ colKey: "osVersion", title: "系统版本", detail: true },
|
|
@@ -66,7 +66,7 @@ import { reactive } from "vue";
|
|
|
66
66
|
import { Button as TButton, Option as TOption, Select as TSelect, Tag as TTag } from "tdesign-vue-next";
|
|
67
67
|
import { RefreshIcon } from "tdesign-icons-vue-next";
|
|
68
68
|
import DetailPanel from "befly-admin-ui/components/detailPanel.vue";
|
|
69
|
-
import PageTableDetail from "befly-admin-ui/components/
|
|
69
|
+
import PageTableDetail from "befly-admin-ui/components/pageTableDetail.vue";
|
|
70
70
|
import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
|
|
71
71
|
|
|
72
72
|
// 响应式数据
|
package/views/login_1/index.vue
CHANGED
|
@@ -117,7 +117,7 @@ async function apiLogin() {
|
|
|
117
117
|
|
|
118
118
|
MessagePlugin.success(res.msg || "登录成功");
|
|
119
119
|
|
|
120
|
-
await router.push(
|
|
120
|
+
await router.push(import.meta.env.VITE_HOME_PATH);
|
|
121
121
|
} catch (error) {
|
|
122
122
|
MessagePlugin.error(error.msg || error.message || "登录失败");
|
|
123
123
|
} finally {
|
|
@@ -52,7 +52,7 @@ import { reactive } from "vue";
|
|
|
52
52
|
import { Button as TButton, Dropdown as TDropdown, DropdownItem as TDropdownItem, DropdownMenu as TDropdownMenu, Tag as TTag } from "tdesign-vue-next";
|
|
53
53
|
import { AddIcon, ChevronDownIcon, DeleteIcon, EditIcon, RefreshIcon } from "tdesign-icons-vue-next";
|
|
54
54
|
import EditDialog from "./components/edit.vue";
|
|
55
|
-
import PageTableDetail from "befly-admin-ui/components/
|
|
55
|
+
import PageTableDetail from "befly-admin-ui/components/pageTableDetail.vue";
|
|
56
56
|
import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
|
|
57
57
|
|
|
58
58
|
// 响应式数据
|
|
@@ -77,7 +77,7 @@ import { AddIcon, ChevronDownIcon, CodeIcon, DeleteIcon, EditIcon, RefreshIcon,
|
|
|
77
77
|
import EditDialog from "./components/edit.vue";
|
|
78
78
|
import MenuDialog from "./components/menu.vue";
|
|
79
79
|
import ApiDialog from "./components/api.vue";
|
|
80
|
-
import PageTableDetail from "befly-admin-ui/components/
|
|
80
|
+
import PageTableDetail from "befly-admin-ui/components/pageTableDetail.vue";
|
|
81
81
|
import { withDefaultColumns } from "befly-admin-ui/utils/withDefaultColumns";
|
|
82
82
|
|
|
83
83
|
// 响应式数据
|