@befly-addon/admin 1.8.4 → 1.8.6

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,5 +1,5 @@
1
1
  <template>
2
- <TDialog v-model:visible="$Data.visible" :title="$Prop.actionType === 'upd' ? '编辑配置' : '添加配置'" width="600px" :append-to-body="true" :show-footer="true" :esc-closable="false" top="10vh" @close="$Method.onClose">
2
+ <PageDialog v-model="dialogVisible" :title="$Prop.actionType === 'upd' ? '编辑配置' : '添加配置'" :confirm-loading="$Data.submitting" @confirm="onSubmit">
3
3
  <TForm :model="$Data.formData" label-width="120px" label-position="left" :rules="$Data2.formRules" :ref="(el) => ($From.form = el)">
4
4
  <TFormItem label="配置名称" prop="name">
5
5
  <TInput v-model="$Data.formData.name" placeholder="请输入配置名称" :disabled="$Data.isSystem" />
@@ -37,15 +37,14 @@
37
37
  </TRadioGroup>
38
38
  </TFormItem>
39
39
  </TForm>
40
- <template #footer>
41
- <TButton @click="$Method.onClose">取消</TButton>
42
- <TButton theme="primary" :loading="$Data.submitting" @click="$Method.onSubmit">确定</TButton>
43
- </template>
44
- </TDialog>
40
+ </PageDialog>
45
41
  </template>
46
42
 
47
43
  <script setup lang="ts">
48
- import { Dialog as TDialog, Form as TForm, FormItem as TFormItem, Input as TInput, Textarea as TTextarea, InputNumber as TInputNumber, Select as TSelect, Option as TOption, RadioGroup as TRadioGroup, Radio as TRadio, Button as TButton, MessagePlugin } from "tdesign-vue-next";
44
+ import { computed } from "vue";
45
+
46
+ import { Form as TForm, FormItem as TFormItem, Input as TInput, Textarea as TTextarea, InputNumber as TInputNumber, Select as TSelect, Option as TOption, RadioGroup as TRadioGroup, Radio as TRadio, MessagePlugin } from "tdesign-vue-next";
47
+ import PageDialog from "@/components/pageDialog.vue";
49
48
  import { $Http } from "@/plugins/http";
50
49
 
51
50
  const $Prop = defineProps({
@@ -68,13 +67,23 @@ const $Emit = defineEmits<{
68
67
  (e: "success"): void;
69
68
  }>();
70
69
 
70
+ type PageDialogEventContext = {
71
+ close: () => void;
72
+ };
73
+
71
74
  // 表单引用
72
75
  const $From = $shallowRef({
73
76
  form: null
74
77
  });
75
78
 
79
+ const dialogVisible = computed({
80
+ get: () => $Prop.modelValue,
81
+ set: (value) => {
82
+ $Emit("update:modelValue", value);
83
+ }
84
+ });
85
+
76
86
  const $Data = $ref({
77
- visible: false,
78
87
  submitting: false,
79
88
  isSystem: false,
80
89
  formData: {
@@ -103,72 +112,65 @@ const $Data2 = $shallowRef({
103
112
  groupOptions: ["基础配置", "邮件配置", "存储配置", "安全配置", "其他"]
104
113
  });
105
114
 
106
- // 方法集合
107
- const $Method = {
108
- async initData() {
109
- $Method.onShow();
110
- },
115
+ async function initData(): Promise<void> {
116
+ onShow();
117
+ }
111
118
 
112
- onShow() {
113
- $Data.visible = true;
114
- if ($Prop.actionType === "upd" && $Prop.rowData) {
115
- $Data.formData.id = $Prop.rowData.id || 0;
116
- $Data.formData.name = $Prop.rowData.name || "";
117
- $Data.formData.code = $Prop.rowData.code || "";
118
- $Data.formData.value = $Prop.rowData.value || "";
119
- $Data.formData.valueType = $Prop.rowData.valueType || "string";
120
- $Data.formData.group = $Prop.rowData.group || "";
121
- $Data.formData.sort = $Prop.rowData.sort || 0;
122
- $Data.formData.description = $Prop.rowData.description || "";
123
- $Data.formData.state = $Prop.rowData.state || 1;
124
- $Data.isSystem = $Prop.rowData.isSystem === 1;
125
- } else {
126
- $Data.formData = {
127
- id: 0,
128
- name: "",
129
- code: "",
130
- value: "",
131
- valueType: "string",
132
- group: "",
133
- sort: 0,
134
- description: "",
135
- state: 1
136
- };
137
- $Data.isSystem = false;
138
- }
139
- },
119
+ function onShow(): void {
120
+ if ($Prop.actionType === "upd" && $Prop.rowData) {
121
+ const row = $Prop.rowData as Record<string, unknown>;
122
+ $Data.formData.id = (row["id"] as number) || 0;
123
+ $Data.formData.name = (row["name"] as string) || "";
124
+ $Data.formData.code = (row["code"] as string) || "";
125
+ $Data.formData.value = (row["value"] as string) || "";
126
+ $Data.formData.valueType = (row["valueType"] as string) || "string";
127
+ $Data.formData.group = (row["group"] as string) || "";
128
+ $Data.formData.sort = (row["sort"] as number) || 0;
129
+ $Data.formData.description = (row["description"] as string) || "";
130
+ $Data.formData.state = (row["state"] as number) || 1;
131
+ $Data.isSystem = row["isSystem"] === 1;
132
+ } else {
133
+ $Data.formData = {
134
+ id: 0,
135
+ name: "",
136
+ code: "",
137
+ value: "",
138
+ valueType: "string",
139
+ group: "",
140
+ sort: 0,
141
+ description: "",
142
+ state: 1
143
+ };
144
+ $Data.isSystem = false;
145
+ }
146
+ }
140
147
 
141
- onClose() {
142
- $Data.visible = false;
143
- $Emit("update:modelValue", false);
144
- },
148
+ async function onSubmit(context?: PageDialogEventContext): Promise<void> {
149
+ const valid = await $From.form?.validate();
150
+ if (valid !== true) return;
145
151
 
146
- async onSubmit() {
147
- const valid = await $From.form?.validate();
148
- if (valid !== true) return;
149
-
150
- $Data.submitting = true;
151
- try {
152
- const api = $Prop.actionType === "upd" ? "/addon/admin/sysConfig/upd" : "/addon/admin/sysConfig/ins";
153
- const res = await $Http.post(api, $Data.formData);
154
-
155
- if (res.code === 0) {
156
- MessagePlugin.success($Prop.actionType === "upd" ? "编辑成功" : "添加成功");
157
- $Emit("success");
158
- $Method.onClose();
159
- } else {
160
- MessagePlugin.error(res.msg || "操作失败");
152
+ $Data.submitting = true;
153
+ try {
154
+ const api = $Prop.actionType === "upd" ? "/addon/admin/sysConfig/upd" : "/addon/admin/sysConfig/ins";
155
+ const res = await $Http.post(api, $Data.formData);
156
+
157
+ if (res.code === 0) {
158
+ MessagePlugin.success($Prop.actionType === "upd" ? "编辑成功" : "添加成功");
159
+ $Emit("success");
160
+ if (context && typeof context.close === "function") {
161
+ context.close();
161
162
  }
162
- } catch (error) {
163
- MessagePlugin.error("操作失败");
164
- } finally {
165
- $Data.submitting = false;
163
+ } else {
164
+ MessagePlugin.error(res.msg || "操作失败");
166
165
  }
166
+ } catch (error) {
167
+ MessagePlugin.error("操作失败");
168
+ } finally {
169
+ $Data.submitting = false;
167
170
  }
168
- };
171
+ }
169
172
 
170
- // 该组件由父组件 v-if 控制挂载/卸载,因此无需 watch:创建时初始化一次即可
171
- $Method.initData();
173
+ initData();
172
174
  </script>
173
175
 
174
176
  <style scoped lang="scss"></style>
@@ -1,19 +1,19 @@
1
1
  <template>
2
- <PagedTableDetailPage class="page-sys-config page-table" :columns="$Data.columns" :endpoints="$Data.endpoints" :table-slot-names="['isSystem', 'valueType', 'state']">
2
+ <PagedTableDetail class="page-sys-config page-table" :columns="$Data.columns" :endpoints="$Data.endpoints" :table-slot-names="['isSystem', 'valueType', 'state']">
3
3
  <template #toolLeft="scope">
4
- <TButton theme="primary" @click="$Method.onAdd">
4
+ <TButton theme="primary" @click="onAdd">
5
5
  <template #icon>
6
6
  <ILucidePlus />
7
7
  </template>
8
8
  新增配置
9
9
  </TButton>
10
- <TSelect v-model="$Data.filter.group" placeholder="配置分组" clearable style="width: 150px" @change="$Method.handleFilter(scope.reload)">
10
+ <TSelect v-model="$Data.filter.group" placeholder="配置分组" clearable style="width: 150px" @change="handleFilter(scope.reload)">
11
11
  <TOption v-for="item in $Data.groupOptions" :key="item" :label="item" :value="item" />
12
12
  </TSelect>
13
13
  </template>
14
14
 
15
15
  <template #toolRight="scope">
16
- <TButton shape="circle" @click="$Method.onReload(scope.reload)">
16
+ <TButton shape="circle" @click="onReload(scope.reload)">
17
17
  <template #icon>
18
18
  <ILucideRotateCw />
19
19
  </template>
@@ -35,7 +35,7 @@
35
35
  </template>
36
36
 
37
37
  <template #operation="{ row, deleteRow }">
38
- <TDropdown trigger="click" placement="bottom-right" @click="$Method.onDropdownAction($event, row, deleteRow)">
38
+ <TDropdown trigger="click" placement="bottom-right" @click="onDropdownAction($event, row, deleteRow)">
39
39
  <TButton theme="primary" size="small">
40
40
  操作
41
41
  <template #suffix><ILucideChevronDown /></template>
@@ -54,7 +54,7 @@
54
54
  </template>
55
55
 
56
56
  <template #detail="scope">
57
- <DetailPanel :data="scope.currentRow" :fields="$Data.detailFields">
57
+ <DetailPanel :data="scope.row" :fields="$Data.columns">
58
58
  <template #isSystem="slotScope">
59
59
  <TTag v-if="slotScope.value === 1" shape="round" theme="warning" variant="light-outline">系统配置</TTag>
60
60
  <TTag v-else shape="round" variant="light-outline">自定义配置</TTag>
@@ -69,9 +69,9 @@
69
69
  </template>
70
70
 
71
71
  <template #dialogs="scope">
72
- <EditDialog v-if="$Data.editVisible" v-model="$Data.editVisible" :action-type="$Data.actionType" :row-data="$Data.rowData" @success="$Method.onDialogSuccess(scope.reload)" />
72
+ <EditDialog v-if="$Data.editVisible" v-model="$Data.editVisible" :action-type="$Data.actionType" :row-data="$Data.rowData" @success="onDialogSuccess(scope.reload)" />
73
73
  </template>
74
- </PagedTableDetailPage>
74
+ </PagedTableDetail>
75
75
  </template>
76
76
 
77
77
  <script setup lang="ts">
@@ -82,8 +82,8 @@ import ILucidePencil from "~icons/lucide/pencil";
82
82
  import ILucideTrash2 from "~icons/lucide/trash-2";
83
83
  import ILucideChevronDown from "~icons/lucide/chevron-down";
84
84
  import EditDialog from "./components/edit.vue";
85
- import DetailPanel from "@/components/DetailPanel.vue";
86
- import PagedTableDetailPage from "@/components/PagedTableDetailPage.vue";
85
+ import DetailPanel from "@/components/detailPanel.vue";
86
+ import PagedTableDetail from "@/components/pagedTableDetail.vue";
87
87
  import { withDefaultColumns } from "befly-shared/utils/withDefaultColumns";
88
88
 
89
89
  // 响应式数据
@@ -97,18 +97,9 @@ const $Data = $ref({
97
97
  { colKey: "sort", title: "排序", width: 80 },
98
98
  { colKey: "isSystem", title: "类型", width: 80 },
99
99
  { colKey: "state", title: "状态", width: 80 },
100
- { colKey: "operation", title: "操作", width: 100 }
100
+ { colKey: "operation", title: "操作", width: 100 },
101
+ { colKey: "description", title: "描述说明", detail: true }
101
102
  ]),
102
- detailFields: [
103
- { colKey: "name", title: "配置名称" },
104
- { colKey: "code", title: "配置代码" },
105
- { colKey: "value", title: "配置值" },
106
- { colKey: "valueType", title: "值类型" },
107
- { colKey: "group", title: "配置分组" },
108
- { colKey: "sort", title: "排序" },
109
- { colKey: "isSystem", title: "配置类型" },
110
- { colKey: "description", title: "描述说明" }
111
- ],
112
103
  endpoints: {
113
104
  list: {
114
105
  path: "/addon/admin/sysConfig/list",
@@ -144,46 +135,45 @@ const $Data = $ref({
144
135
  groupOptions: ["基础配置", "邮件配置", "存储配置", "安全配置", "其他"]
145
136
  });
146
137
 
147
- // 方法
148
- const $Method = {
149
- onAdd() {
150
- $Method.onAction("add", {});
151
- },
138
+ function onAdd(): void {
139
+ onAction("add", {});
140
+ }
152
141
 
153
- handleFilter(reload) {
154
- reload({ keepSelection: false, resetPage: true });
155
- },
142
+ function handleFilter(reload: (options: { keepSelection?: boolean; resetPage?: boolean }) => void): void {
143
+ reload({ keepSelection: false, resetPage: true });
144
+ }
156
145
 
157
- onReload(reload) {
158
- reload({ keepSelection: true });
159
- },
146
+ function onReload(reload: (options: { keepSelection?: boolean }) => void): void {
147
+ reload({ keepSelection: true });
148
+ }
160
149
 
161
- onDialogSuccess(reload) {
162
- reload({ keepSelection: true });
163
- },
150
+ function onDialogSuccess(reload: (options: { keepSelection?: boolean }) => void): void {
151
+ reload({ keepSelection: true });
152
+ }
164
153
 
165
- onAction(command, rowData) {
166
- $Data.actionType = command;
167
- if (command === "add") {
168
- $Data.rowData = {};
169
- } else {
170
- $Data.rowData = Object.assign({}, rowData);
171
- }
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
+ }
172
161
 
173
- if (command === "add" || command === "upd") {
174
- $Data.editVisible = true;
175
- }
176
- },
162
+ if (command === "add" || command === "upd") {
163
+ $Data.editVisible = true;
164
+ }
165
+ }
177
166
 
178
- onDropdownAction(data, rowData, deleteRow) {
179
- const cmd = data && data.value ? String(data.value) : "";
180
- if (cmd === "del") {
181
- deleteRow(rowData);
182
- return;
183
- }
184
- $Method.onAction(cmd, rowData);
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;
185
174
  }
186
- };
175
+ onAction(cmd, rowData);
176
+ }
187
177
  </script>
188
178
 
189
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">