@befly-addon/admin 1.8.2 → 1.8.5

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.
@@ -1,102 +1,93 @@
1
1
  <template>
2
- <div class="page-sys-config page-table">
3
- <div class="main-tool">
4
- <div class="left">
5
- <TButton theme="primary" @click="$Method.onAction('add', {})">
6
- <template #icon>
7
- <ILucidePlus />
8
- </template>
9
- 新增配置
2
+ <PagedTableDetail class="page-sys-config page-table" :columns="$Data.columns" :endpoints="$Data.endpoints" :table-slot-names="['isSystem', 'valueType', 'state']">
3
+ <template #toolLeft="scope">
4
+ <TButton theme="primary" @click="onAdd">
5
+ <template #icon>
6
+ <ILucidePlus />
7
+ </template>
8
+ 新增配置
9
+ </TButton>
10
+ <TSelect v-model="$Data.filter.group" placeholder="配置分组" clearable style="width: 150px" @change="handleFilter(scope.reload)">
11
+ <TOption v-for="item in $Data.groupOptions" :key="item" :label="item" :value="item" />
12
+ </TSelect>
13
+ </template>
14
+
15
+ <template #toolRight="scope">
16
+ <TButton shape="circle" @click="onReload(scope.reload)">
17
+ <template #icon>
18
+ <ILucideRotateCw />
19
+ </template>
20
+ </TButton>
21
+ </template>
22
+
23
+ <template #isSystem="{ row }">
24
+ <TTag v-if="row.isSystem === 1" shape="round" theme="warning" variant="light-outline">系统</TTag>
25
+ <TTag v-else shape="round" variant="light-outline">自定义</TTag>
26
+ </template>
27
+
28
+ <template #valueType="{ row }">
29
+ <TTag shape="round" variant="light-outline">{{ row.valueType }}</TTag>
30
+ </template>
31
+
32
+ <template #state="{ row }">
33
+ <TTag v-if="row.state === 1" shape="round" theme="success" variant="light-outline">正常</TTag>
34
+ <TTag v-else-if="row.state === 2" shape="round" theme="warning" variant="light-outline">禁用</TTag>
35
+ </template>
36
+
37
+ <template #operation="{ row, deleteRow }">
38
+ <TDropdown trigger="click" placement="bottom-right" @click="onDropdownAction($event, row, deleteRow)">
39
+ <TButton theme="primary" size="small">
40
+ 操作
41
+ <template #suffix><ILucideChevronDown /></template>
10
42
  </TButton>
11
- <TSelect v-model="$Data.filter.group" placeholder="配置分组" clearable style="width: 150px" @change="$Method.handleFilter">
12
- <TOption v-for="item in $Data.groupOptions" :key="item" :label="item" :value="item" />
13
- </TSelect>
14
- </div>
15
- <div class="right">
16
- <TButton shape="circle" @click="$Method.handleRefresh">
17
- <template #icon>
18
- <ILucideRotateCw />
19
- </template>
20
- </TButton>
21
- </div>
22
- </div>
23
-
24
- <div class="main-content">
25
- <div class="main-table">
26
- <TTable :data="$Data.tableData" :columns="$Data.columns" :loading="$Data.loading" :active-row-keys="$Data.activeRowKeys" row-key="id" height="100%" active-row-type="single" @active-change="$Method.onActiveChange">
27
- <template #isSystem="{ row }">
28
- <TTag v-if="row.isSystem === 1" shape="round" theme="warning" variant="light-outline">系统</TTag>
29
- <TTag v-else shape="round" variant="light-outline">自定义</TTag>
30
- </template>
31
- <template #valueType="{ row }">
32
- <TTag shape="round" variant="light-outline">{{ row.valueType }}</TTag>
33
- </template>
34
- <template #state="{ row }">
35
- <TTag v-if="row.state === 1" shape="round" theme="success" variant="light-outline">正常</TTag>
36
- <TTag v-else-if="row.state === 2" shape="round" theme="warning" variant="light-outline">禁用</TTag>
37
- </template>
38
- <template #operation="{ row }">
39
- <TDropdown trigger="click" placement="bottom-right" @click="(data) => $Method.onAction(data.value, row)">
40
- <TButton theme="primary" size="small">
41
- 操作
42
- <template #suffix><ILucideChevronDown /></template>
43
- </TButton>
44
- <TDropdownMenu slot="dropdown">
45
- <TDropdownItem value="upd">
46
- <ILucidePencil />
47
- 编辑
48
- </TDropdownItem>
49
- <TDropdownItem v-if="row.isSystem !== 1" value="del" :divider="true">
50
- <ILucideTrash2 style="width: 14px; height: 14px; margin-right: 6px" />
51
- 删除
52
- </TDropdownItem>
53
- </TDropdownMenu>
54
- </TDropdown>
55
- </template>
56
- </TTable>
57
- </div>
58
-
59
- <div class="main-detail">
60
- <DetailPanel :data="$Data.currentRow" :fields="$Data.detailFields">
61
- <template #isSystem="{ value }">
62
- <TTag v-if="value === 1" shape="round" theme="warning" variant="light-outline">系统配置</TTag>
63
- <TTag v-else shape="round" variant="light-outline">自定义配置</TTag>
64
- </template>
65
- <template #valueType="{ value }">
66
- <TTag shape="round" variant="light-outline">{{ value }}</TTag>
67
- </template>
68
- <template #value="{ value }">
69
- <pre class="config-value">{{ value }}</pre>
70
- </template>
71
- </DetailPanel>
72
- </div>
73
- </div>
74
-
75
- <div class="main-page">
76
- <TPagination :current-page="$Data.pagerConfig.currentPage" :page-size="$Data.pagerConfig.limit" :total="$Data.pagerConfig.total" @current-change="$Method.onPageChange" @page-size-change="$Method.handleSizeChange" />
77
- </div>
78
-
79
- <!-- 编辑对话框 -->
80
- <EditDialog v-if="$Data.editVisible" v-model="$Data.editVisible" :action-type="$Data.actionType" :row-data="$Data.rowData" @success="$Method.apiConfigList" />
81
- </div>
43
+ <TDropdownMenu slot="dropdown">
44
+ <TDropdownItem value="upd">
45
+ <ILucidePencil />
46
+ 编辑
47
+ </TDropdownItem>
48
+ <TDropdownItem v-if="row.isSystem !== 1" value="del" :divider="true">
49
+ <ILucideTrash2 style="width: 14px; height: 14px; margin-right: 6px" />
50
+ 删除
51
+ </TDropdownItem>
52
+ </TDropdownMenu>
53
+ </TDropdown>
54
+ </template>
55
+
56
+ <template #detail="scope">
57
+ <DetailPanel :data="scope.row" :fields="$Data.columns">
58
+ <template #isSystem="slotScope">
59
+ <TTag v-if="slotScope.value === 1" shape="round" theme="warning" variant="light-outline">系统配置</TTag>
60
+ <TTag v-else shape="round" variant="light-outline">自定义配置</TTag>
61
+ </template>
62
+ <template #valueType="slotScope">
63
+ <TTag shape="round" variant="light-outline">{{ slotScope.value }}</TTag>
64
+ </template>
65
+ <template #value="slotScope">
66
+ <pre class="config-value">{{ slotScope.value }}</pre>
67
+ </template>
68
+ </DetailPanel>
69
+ </template>
70
+
71
+ <template #dialogs="scope">
72
+ <EditDialog v-if="$Data.editVisible" v-model="$Data.editVisible" :action-type="$Data.actionType" :row-data="$Data.rowData" @success="onDialogSuccess(scope.reload)" />
73
+ </template>
74
+ </PagedTableDetail>
82
75
  </template>
83
76
 
84
77
  <script setup lang="ts">
85
- import { Button as TButton, Table as TTable, Tag as TTag, Select as TSelect, Option as TOption, Dropdown as TDropdown, DropdownMenu as TDropdownMenu, DropdownItem as TDropdownItem, Pagination as TPagination, MessagePlugin, DialogPlugin } from "tdesign-vue-next";
78
+ import { Button as TButton, Dropdown as TDropdown, DropdownItem as TDropdownItem, DropdownMenu as TDropdownMenu, Option as TOption, Select as TSelect, Tag as TTag } from "tdesign-vue-next";
86
79
  import ILucidePlus from "~icons/lucide/plus";
87
80
  import ILucideRotateCw from "~icons/lucide/rotate-cw";
88
81
  import ILucidePencil from "~icons/lucide/pencil";
89
82
  import ILucideTrash2 from "~icons/lucide/trash-2";
90
83
  import ILucideChevronDown from "~icons/lucide/chevron-down";
91
84
  import EditDialog from "./components/edit.vue";
92
- import DetailPanel from "@/components/DetailPanel.vue";
93
- import { $Http } from "@/plugins/http";
85
+ import DetailPanel from "@/components/detailPanel.vue";
86
+ import PagedTableDetail from "@/components/pagedTableDetail.vue";
94
87
  import { withDefaultColumns } from "befly-shared/utils/withDefaultColumns";
95
88
 
96
89
  // 响应式数据
97
90
  const $Data = $ref({
98
- tableData: [],
99
- loading: false,
100
91
  columns: withDefaultColumns([
101
92
  { colKey: "name", title: "配置名称", fixed: "left", width: 150 },
102
93
  { colKey: "code", title: "配置代码", ellipsis: true },
@@ -106,25 +97,35 @@ const $Data = $ref({
106
97
  { colKey: "sort", title: "排序", width: 80 },
107
98
  { colKey: "isSystem", title: "类型", width: 80 },
108
99
  { colKey: "state", title: "状态", width: 80 },
109
- { colKey: "operation", title: "操作", width: 100 }
100
+ { colKey: "operation", title: "操作", width: 100 },
101
+ { colKey: "description", title: "描述说明", detail: true }
110
102
  ]),
111
- detailFields: [
112
- { colKey: "name", title: "配置名称" },
113
- { colKey: "code", title: "配置代码" },
114
- { colKey: "value", title: "配置值" },
115
- { colKey: "valueType", title: "值类型" },
116
- { colKey: "group", title: "配置分组" },
117
- { colKey: "sort", title: "排序" },
118
- { colKey: "isSystem", title: "配置类型" },
119
- { colKey: "description", title: "描述说明" }
120
- ],
121
- pagerConfig: {
122
- currentPage: 1,
123
- limit: 30,
124
- total: 0
103
+ endpoints: {
104
+ list: {
105
+ path: "/addon/admin/sysConfig/list",
106
+ dropValues: [0, ""],
107
+ dropKeyValue: {
108
+ group: [""]
109
+ },
110
+ buildData: () => {
111
+ return {
112
+ group: $Data.filter.group
113
+ };
114
+ }
115
+ },
116
+ delete: {
117
+ path: "/addon/admin/sysConfig/del",
118
+ idKey: "id",
119
+ confirm: (row) => {
120
+ return {
121
+ header: "确认删除",
122
+ body: `确认删除配置“${row.name}”吗?`,
123
+ confirmBtn: "删除",
124
+ status: "warning"
125
+ };
126
+ }
127
+ }
125
128
  },
126
- currentRow: null,
127
- activeRowKeys: [],
128
129
  editVisible: false,
129
130
  actionType: "add",
130
131
  rowData: {},
@@ -134,147 +135,45 @@ const $Data = $ref({
134
135
  groupOptions: ["基础配置", "邮件配置", "存储配置", "安全配置", "其他"]
135
136
  });
136
137
 
137
- // 方法
138
- const $Method = {
139
- async initData() {
140
- await $Method.apiConfigList();
141
- },
142
-
143
- // 加载配置列表
144
- async apiConfigList() {
145
- $Data.loading = true;
146
- try {
147
- const res = await $Http.post(
148
- "/addon/admin/sysConfig/list",
149
- {
150
- page: $Data.pagerConfig.currentPage,
151
- limit: $Data.pagerConfig.limit,
152
- group: $Data.filter.group
153
- },
154
- {
155
- dropValues: [0, ""],
156
- dropKeyValue: {
157
- page: [],
158
- limit: [],
159
- group: [""]
160
- }
161
- }
162
- );
163
- $Data.tableData = res.data.lists || [];
164
- $Data.pagerConfig.total = res.data.total || 0;
165
-
166
- if ($Data.tableData.length > 0) {
167
- $Data.currentRow = $Data.tableData[0];
168
- $Data.activeRowKeys = [$Data.tableData[0].id];
169
- } else {
170
- $Data.currentRow = null;
171
- $Data.activeRowKeys = [];
172
- }
173
- } catch (error) {
174
- MessagePlugin.error("加载数据失败");
175
- } finally {
176
- $Data.loading = false;
177
- }
178
- },
179
-
180
- // 删除配置
181
- async apiConfigDel(row) {
182
- if (row.isSystem === 1) {
183
- MessagePlugin.warning("系统配置不允许删除");
184
- return;
185
- }
186
-
187
- let dialog = null;
188
- let destroyed = false;
189
-
190
- const destroy = () => {
191
- if (destroyed) return;
192
- destroyed = true;
193
- if (dialog && typeof dialog.destroy === "function") {
194
- dialog.destroy();
195
- }
196
- };
197
-
198
- dialog = DialogPlugin.confirm({
199
- header: "确认删除",
200
- body: `确认删除配置“${row.name}”吗?`,
201
- status: "warning",
202
- confirmBtn: "删除",
203
- cancelBtn: "取消",
204
- onConfirm: async () => {
205
- if (dialog && typeof dialog.setConfirmLoading === "function") {
206
- dialog.setConfirmLoading(true);
207
- }
208
-
209
- try {
210
- await $Http.post("/addon/admin/sysConfig/del", {
211
- id: row.id
212
- });
213
- MessagePlugin.success("删除成功");
214
- destroy();
215
- await $Method.apiConfigList();
216
- } catch (error) {
217
- MessagePlugin.error("删除失败");
218
- } finally {
219
- if (dialog && typeof dialog.setConfirmLoading === "function") {
220
- dialog.setConfirmLoading(false);
221
- }
222
- }
223
- },
224
- onClose: () => {
225
- destroy();
226
- }
227
- });
228
- },
229
-
230
- // 筛选
231
- handleFilter() {
232
- $Data.pagerConfig.currentPage = 1;
233
- $Method.apiConfigList();
234
- },
138
+ function onAdd(): void {
139
+ onAction("add", {});
140
+ }
235
141
 
236
- // 刷新
237
- handleRefresh() {
238
- $Method.apiConfigList();
239
- },
142
+ function handleFilter(reload: (options: { keepSelection?: boolean; resetPage?: boolean }) => void): void {
143
+ reload({ keepSelection: false, resetPage: true });
144
+ }
240
145
 
241
- // 分页改变
242
- onPageChange(currentPage) {
243
- $Data.pagerConfig.currentPage = currentPage;
244
- $Method.apiConfigList();
245
- },
146
+ function onReload(reload: (options: { keepSelection?: boolean }) => void): void {
147
+ reload({ keepSelection: true });
148
+ }
246
149
 
247
- // 每页条数改变
248
- handleSizeChange(pageSize) {
249
- $Data.pagerConfig.limit = pageSize;
250
- $Data.pagerConfig.currentPage = 1;
251
- $Method.apiConfigList();
252
- },
150
+ function onDialogSuccess(reload: (options: { keepSelection?: boolean }) => void): void {
151
+ reload({ keepSelection: true });
152
+ }
253
153
 
254
- // 高亮行变化
255
- onActiveChange(value, context) {
256
- if (value.length === 0 && $Data.activeRowKeys.length > 0) {
257
- return;
258
- }
259
- $Data.activeRowKeys = value;
260
- if (context.activeRowList && context.activeRowList.length > 0) {
261
- $Data.currentRow = context.activeRowList[0].row;
262
- }
263
- },
154
+ function onAction(command: string, rowData: Record<string, unknown>): void {
155
+ $Data.actionType = command;
156
+ if (command === "add") {
157
+ $Data.rowData = {};
158
+ } else {
159
+ $Data.rowData = Object.assign({}, rowData);
160
+ }
264
161
 
265
- // 操作菜单点击
266
- onAction(command, rowData) {
267
- $Data.actionType = command;
268
- $Data.rowData = rowData;
269
- if (command === "add" || command === "upd") {
270
- $Data.editVisible = true;
271
- } else if (command === "del") {
272
- $Method.apiConfigDel(rowData);
273
- }
162
+ if (command === "add" || command === "upd") {
163
+ $Data.editVisible = true;
274
164
  }
275
- };
165
+ }
276
166
 
277
- $Method.initData();
167
+ function onDropdownAction(data: unknown, rowData: Record<string, unknown>, deleteRow: (row: Record<string, unknown>) => void): void {
168
+ const record = data as Record<string, unknown>;
169
+ const rawValue = record && record["value"] ? record["value"] : "";
170
+ const cmd = rawValue ? String(rawValue) : "";
171
+ if (cmd === "del") {
172
+ deleteRow(rowData);
173
+ return;
174
+ }
175
+ onAction(cmd, rowData);
176
+ }
278
177
  </script>
279
178
 
280
179
  <style scoped lang="scss">
@@ -22,13 +22,13 @@
22
22
  </div>
23
23
  <div v-if="$Data.userInfo.lastLoginTime" class="detail-item">
24
24
  <ILucideClock />
25
- <span>{{ $Method.formatTime($Data.userInfo.lastLoginTime) }}</span>
25
+ <span>{{ formatTime($Data.userInfo.lastLoginTime) }}</span>
26
26
  </div>
27
27
  </div>
28
28
 
29
29
  <!-- 仅 dev 角色显示刷新缓存按钮 -->
30
30
  <div v-if="$Data.userInfo.roleCode === 'dev'" class="user-actions">
31
- <TButton theme="primary" size="mini" :loading="$Data.refreshing" @click="$Method.handleRefreshCache">
31
+ <TButton theme="primary" size="mini" :loading="$Data.refreshing" @click="handleRefreshCache">
32
32
  <template #icon>
33
33
  <ILucideRotateCw />
34
34
  </template>
@@ -53,85 +53,75 @@ const $Data = $ref({
53
53
  refreshing: false
54
54
  });
55
55
 
56
- // 方法集合
57
- const $Method = {
58
- // 获取数据
59
- async fetchData() {
60
- try {
61
- const { data } = await $Http.post(
62
- "/addon/admin/admin/detail",
63
- {},
64
- {
65
- dropValues: [""]
66
- }
67
- );
68
- Object.assign($Data.userInfo, data);
69
- } catch (error) {
70
- MessagePlugin.error("获取用户信息失败");
71
- }
72
- },
73
-
74
- // 刷新缓存
75
- async handleRefreshCache() {
76
- try {
77
- $Data.refreshing = true;
78
- const result = await $Http.post("/addon/admin/admin/cacheRefresh");
79
-
80
- if (result.code === 0) {
81
- const { apis, menus, roles } = result.data;
82
- const messages = [];
83
-
84
- if (apis.success) {
85
- messages.push(`接口缓存: ${apis.count} 个`);
86
- }
87
- if (menus.success) {
88
- messages.push(`菜单缓存: ${menus.count} 个`);
89
- }
90
- if (roles.success) {
91
- messages.push(`角色缓存: ${roles.count} 个`);
92
- }
93
-
94
- MessagePlugin.success(`缓存刷新成功!${messages.join(",")}`);
95
- } else {
96
- MessagePlugin.warning(result.msg || "部分缓存刷新失败");
56
+ async function fetchData(): Promise<void> {
57
+ try {
58
+ const { data } = await $Http.post(
59
+ "/addon/admin/admin/detail",
60
+ {},
61
+ {
62
+ dropValues: [""]
97
63
  }
98
- } catch (error) {
99
- MessagePlugin.error("刷新缓存失败,请稍后重试");
100
- } finally {
101
- $Data.refreshing = false;
102
- }
103
- },
104
-
105
- // 格式化时间
106
- formatTime(timestamp) {
107
- if (!timestamp) return "";
108
- const date = new Date(Number(timestamp));
109
- const now = new Date();
110
- const diff = now - date;
111
-
112
- // 小于1分钟
113
- if (diff < 60000) {
114
- return "刚刚";
115
- }
116
- // 小于1小时
117
- if (diff < 3600000) {
118
- return `${Math.floor(diff / 60000)}分钟前`;
119
- }
120
- // 小于24小时
121
- if (diff < 86400000) {
122
- return `${Math.floor(diff / 3600000)}小时前`;
123
- }
124
- // 小于7天
125
- if (diff < 604800000) {
126
- return `${Math.floor(diff / 86400000)}天前`;
64
+ );
65
+ Object.assign($Data.userInfo, data);
66
+ } catch (error) {
67
+ MessagePlugin.error("获取用户信息失败");
68
+ }
69
+ }
70
+
71
+ async function handleRefreshCache(): Promise<void> {
72
+ try {
73
+ $Data.refreshing = true;
74
+ const result = await $Http.post("/addon/admin/admin/cacheRefresh");
75
+
76
+ if (result.code === 0) {
77
+ const apis = result.data.apis;
78
+ const menus = result.data.menus;
79
+ const roles = result.data.roles;
80
+ const messages: string[] = [];
81
+
82
+ if (apis && apis.success) {
83
+ messages.push(`接口缓存: ${apis.count} 个`);
84
+ }
85
+ if (menus && menus.success) {
86
+ messages.push(`菜单缓存: ${menus.count} 个`);
87
+ }
88
+ if (roles && roles.success) {
89
+ messages.push(`角色缓存: ${roles.count} 个`);
90
+ }
91
+
92
+ MessagePlugin.success(`缓存刷新成功!${messages.join(",")}`);
93
+ } else {
94
+ MessagePlugin.warning(result.msg || "部分缓存刷新失败");
127
95
  }
128
- // 超过7天显示具体日期
129
- return `${date.getMonth() + 1}月${date.getDate()}日`;
96
+ } catch (error) {
97
+ MessagePlugin.error("刷新缓存失败,请稍后重试");
98
+ } finally {
99
+ $Data.refreshing = false;
130
100
  }
131
- };
101
+ }
102
+
103
+ function formatTime(timestamp: unknown): string {
104
+ if (!timestamp) return "";
105
+ const date = new Date(Number(timestamp));
106
+ const now = new Date();
107
+ const diff = now.getTime() - date.getTime();
108
+
109
+ if (diff < 60000) {
110
+ return "刚刚";
111
+ }
112
+ if (diff < 3600000) {
113
+ return `${Math.floor(diff / 60000)}分钟前`;
114
+ }
115
+ if (diff < 86400000) {
116
+ return `${Math.floor(diff / 3600000)}小时前`;
117
+ }
118
+ if (diff < 604800000) {
119
+ return `${Math.floor(diff / 86400000)}天前`;
120
+ }
121
+ return `${date.getMonth() + 1}月${date.getDate()}日`;
122
+ }
132
123
 
133
- // 初始化
134
- $Method.fetchData();
124
+ fetchData();
135
125
  </script>
136
126
 
137
127
  <style scoped lang="scss">