@befly-addon/admin 1.2.1 → 1.2.3

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.
Files changed (36) hide show
  1. package/adminViews/403_1/index.vue +75 -0
  2. package/adminViews/config/dict/components/edit.vue +109 -0
  3. package/adminViews/config/dict/index.vue +266 -0
  4. package/adminViews/config/dictType/components/edit.vue +100 -0
  5. package/adminViews/config/dictType/index.vue +244 -0
  6. package/adminViews/config/index.vue +12 -0
  7. package/adminViews/config/system/components/edit.vue +171 -0
  8. package/adminViews/config/system/index.vue +286 -0
  9. package/adminViews/index/components/addonList.vue +132 -0
  10. package/adminViews/index/components/environmentInfo.vue +100 -0
  11. package/adminViews/index/components/operationLogs.vue +112 -0
  12. package/adminViews/index/components/performanceMetrics.vue +145 -0
  13. package/adminViews/index/components/quickActions.vue +30 -0
  14. package/adminViews/index/components/serviceStatus.vue +192 -0
  15. package/adminViews/index/components/systemNotifications.vue +137 -0
  16. package/adminViews/index/components/systemOverview.vue +190 -0
  17. package/adminViews/index/components/systemResources.vue +111 -0
  18. package/adminViews/index/components/userInfo.vue +204 -0
  19. package/adminViews/index/index.vue +74 -0
  20. package/adminViews/log/email/index.vue +292 -0
  21. package/adminViews/log/index.vue +12 -0
  22. package/adminViews/log/login/index.vue +187 -0
  23. package/adminViews/log/operate/index.vue +249 -0
  24. package/adminViews/login_1/index.vue +415 -0
  25. package/adminViews/people/admin/components/edit.vue +168 -0
  26. package/adminViews/people/admin/index.vue +240 -0
  27. package/adminViews/people/index.vue +12 -0
  28. package/adminViews/permission/api/index.vue +149 -0
  29. package/adminViews/permission/index.vue +12 -0
  30. package/adminViews/permission/menu/index.vue +130 -0
  31. package/adminViews/permission/role/components/api.vue +361 -0
  32. package/adminViews/permission/role/components/edit.vue +142 -0
  33. package/adminViews/permission/role/components/menu.vue +118 -0
  34. package/adminViews/permission/role/index.vue +263 -0
  35. package/package.json +12 -10
  36. package/tsconfig.json +15 -0
@@ -0,0 +1,187 @@
1
+ <template>
2
+ <div class="page-login-log page-table">
3
+ <div class="main-tool">
4
+ <div class="left"></div>
5
+ <div class="right">
6
+ <TButton shape="circle" @click="$Method.handleRefresh">
7
+ <template #icon>
8
+ <ILucideRotateCw />
9
+ </template>
10
+ </TButton>
11
+ </div>
12
+ </div>
13
+
14
+ <div class="main-content">
15
+ <div class="main-table">
16
+ <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">
17
+ <template #loginResult="{ row }">
18
+ <TTag v-if="row.loginResult === 1" shape="round" theme="success" variant="light-outline">成功</TTag>
19
+ <TTag v-else shape="round" theme="danger" variant="light-outline">失败</TTag>
20
+ </template>
21
+ <template #loginTime="{ row }">
22
+ {{ $Method.formatTime(row.loginTime) }}
23
+ </template>
24
+ <template #deviceType="{ row }">
25
+ <TTag shape="round" variant="light-outline">{{ row.deviceType || "desktop" }}</TTag>
26
+ </template>
27
+ </TTable>
28
+ </div>
29
+
30
+ <div class="main-detail">
31
+ <DetailPanel :data="$Data.currentRow" :fields="$Data.detailFields">
32
+ <template #loginResult="{ value }">
33
+ <TTag v-if="value === 1" shape="round" theme="success" variant="light-outline">成功</TTag>
34
+ <TTag v-else shape="round" theme="danger" variant="light-outline">失败</TTag>
35
+ </template>
36
+ <template #loginTime="{ value }">
37
+ {{ $Method.formatTime(value) }}
38
+ </template>
39
+ <template #deviceType="{ value }">
40
+ <TTag shape="round" variant="light-outline">{{ value || "desktop" }}</TTag>
41
+ </template>
42
+ </DetailPanel>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="main-page">
47
+ <TPagination :current-page="$Data.pagerConfig.currentPage" :page-size="$Data.pagerConfig.limit" :total="$Data.pagerConfig.total" @current-change="$Method.onPageChange" @page-size-change="$Method.handleSizeChange" />
48
+ </div>
49
+ </div>
50
+ </template>
51
+
52
+ <script setup>
53
+ import { Button as TButton, Table as TTable, Tag as TTag, Pagination as TPagination, MessagePlugin } from "tdesign-vue-next";
54
+ import ILucideRotateCw from "~icons/lucide/rotate-cw";
55
+ import DetailPanel from "@/components/DetailPanel.vue";
56
+ import { $Http } from "@/plugins/http";
57
+ import { withDefaultColumns } from "befly-vite/utils/withDefaultColumns";
58
+
59
+ definePage({
60
+ meta: {
61
+ title: "登录日志",
62
+ order: 1
63
+ }
64
+ });
65
+
66
+ // 响应式数据
67
+ const $Data = $ref({
68
+ tableData: [],
69
+ loading: false,
70
+ columns: withDefaultColumns([
71
+ { colKey: "username", title: "用户名", fixed: "left" },
72
+ { colKey: "id", title: "序号" },
73
+ { colKey: "ip", title: "登录IP" },
74
+ { colKey: "browserName", title: "浏览器" },
75
+ { colKey: "osName", title: "操作系统" },
76
+ { colKey: "deviceType", title: "设备类型" },
77
+ { colKey: "loginTime", title: "登录时间" },
78
+ { colKey: "loginResult", title: "登录结果" }
79
+ ]),
80
+ // 详情面板显示更多字段
81
+ detailFields: [
82
+ { colKey: "username", title: "用户名" },
83
+ { colKey: "nickname", title: "昵称" },
84
+ { colKey: "ip", title: "登录IP" },
85
+ { colKey: "browserName", title: "浏览器" },
86
+ { colKey: "browserVersion", title: "浏览器版本" },
87
+ { colKey: "osName", title: "操作系统" },
88
+ { colKey: "osVersion", title: "系统版本" },
89
+ { colKey: "deviceType", title: "设备类型" },
90
+ { colKey: "deviceVendor", title: "设备厂商" },
91
+ { colKey: "deviceModel", title: "设备型号" },
92
+ { colKey: "engineName", title: "渲染引擎" },
93
+ { colKey: "cpuArchitecture", title: "CPU架构" },
94
+ { colKey: "loginTime", title: "登录时间" },
95
+ { colKey: "loginResult", title: "登录结果" },
96
+ { colKey: "failReason", title: "失败原因" }
97
+ ],
98
+ pagerConfig: {
99
+ currentPage: 1,
100
+ limit: 30,
101
+ total: 0
102
+ },
103
+ currentRow: null,
104
+ activeRowKeys: []
105
+ });
106
+
107
+ // 方法
108
+ const $Method = {
109
+ async initData() {
110
+ await $Method.apiLoginLogList();
111
+ },
112
+
113
+ // 加载登录日志列表
114
+ async apiLoginLogList() {
115
+ $Data.loading = true;
116
+ try {
117
+ const res = await $Http("/addon/admin/loginLog/list", {
118
+ page: $Data.pagerConfig.currentPage,
119
+ limit: $Data.pagerConfig.limit
120
+ });
121
+ $Data.tableData = res.data.lists || [];
122
+ $Data.pagerConfig.total = res.data.total || 0;
123
+
124
+ // 自动高亮第一行
125
+ if ($Data.tableData.length > 0) {
126
+ $Data.currentRow = $Data.tableData[0];
127
+ $Data.activeRowKeys = [$Data.tableData[0].id];
128
+ } else {
129
+ $Data.currentRow = null;
130
+ $Data.activeRowKeys = [];
131
+ }
132
+ } catch (error) {
133
+ MessagePlugin.error("加载数据失败");
134
+ } finally {
135
+ $Data.loading = false;
136
+ }
137
+ },
138
+
139
+ // 刷新
140
+ handleRefresh() {
141
+ $Method.apiLoginLogList();
142
+ },
143
+
144
+ // 分页改变
145
+ onPageChange(currentPage) {
146
+ $Data.pagerConfig.currentPage = currentPage;
147
+ $Method.apiLoginLogList();
148
+ },
149
+
150
+ // 每页条数改变
151
+ handleSizeChange(pageSize) {
152
+ $Data.pagerConfig.limit = pageSize;
153
+ $Data.pagerConfig.currentPage = 1;
154
+ $Method.apiLoginLogList();
155
+ },
156
+
157
+ // 高亮行变化
158
+ onActiveChange(value, context) {
159
+ if (value.length === 0 && $Data.activeRowKeys.length > 0) {
160
+ return;
161
+ }
162
+ $Data.activeRowKeys = value;
163
+ if (context.activeRowList && context.activeRowList.length > 0) {
164
+ $Data.currentRow = context.activeRowList[0].row;
165
+ }
166
+ },
167
+
168
+ // 格式化时间
169
+ formatTime(timestamp) {
170
+ if (!timestamp) return "-";
171
+ const date = new Date(timestamp);
172
+ const year = date.getFullYear();
173
+ const month = String(date.getMonth() + 1).padStart(2, "0");
174
+ const day = String(date.getDate()).padStart(2, "0");
175
+ const hours = String(date.getHours()).padStart(2, "0");
176
+ const minutes = String(date.getMinutes()).padStart(2, "0");
177
+ const seconds = String(date.getSeconds()).padStart(2, "0");
178
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
179
+ }
180
+ };
181
+
182
+ $Method.initData();
183
+ </script>
184
+
185
+ <style scoped lang="scss">
186
+ // 样式继承自全局 page-table
187
+ </style>
@@ -0,0 +1,249 @@
1
+ <template>
2
+ <div class="page-operate-log page-table">
3
+ <div class="main-tool">
4
+ <div class="left">
5
+ <TSelect v-model="$Data.filter.module" placeholder="操作模块" clearable style="width: 150px" @change="$Method.handleFilter">
6
+ <TOption v-for="item in $Data.moduleOptions" :key="item.value" :label="item.label" :value="item.value" />
7
+ </TSelect>
8
+ <TSelect v-model="$Data.filter.action" placeholder="操作类型" clearable style="width: 150px" @change="$Method.handleFilter">
9
+ <TOption v-for="item in $Data.actionOptions" :key="item.value" :label="item.label" :value="item.value" />
10
+ </TSelect>
11
+ <TSelect v-model="$Data.filter.result" placeholder="操作结果" clearable style="width: 120px" @change="$Method.handleFilter">
12
+ <TOption label="成功" :value="1" />
13
+ <TOption label="失败" :value="0" />
14
+ </TSelect>
15
+ </div>
16
+ <div class="right">
17
+ <TButton shape="circle" @click="$Method.handleRefresh">
18
+ <template #icon>
19
+ <ILucideRotateCw />
20
+ </template>
21
+ </TButton>
22
+ </div>
23
+ </div>
24
+
25
+ <div class="main-content">
26
+ <div class="main-table">
27
+ <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">
28
+ <template #result="{ row }">
29
+ <TTag v-if="row.result === 1" shape="round" theme="success" variant="light-outline">成功</TTag>
30
+ <TTag v-else shape="round" theme="danger" variant="light-outline">失败</TTag>
31
+ </template>
32
+ <template #operateTime="{ row }">
33
+ {{ $Method.formatTime(row.operateTime) }}
34
+ </template>
35
+ <template #duration="{ row }">
36
+ <TTag shape="round" :theme="row.duration > 1000 ? 'warning' : 'default'" variant="light-outline">{{ row.duration }}ms</TTag>
37
+ </template>
38
+ <template #action="{ row }">
39
+ <TTag shape="round" variant="light-outline">{{ row.action }}</TTag>
40
+ </template>
41
+ </TTable>
42
+ </div>
43
+
44
+ <div class="main-detail">
45
+ <DetailPanel :data="$Data.currentRow" :fields="$Data.detailFields">
46
+ <template #result="{ value }">
47
+ <TTag v-if="value === 1" shape="round" theme="success" variant="light-outline">成功</TTag>
48
+ <TTag v-else shape="round" theme="danger" variant="light-outline">失败</TTag>
49
+ </template>
50
+ <template #operateTime="{ value }">
51
+ {{ $Method.formatTime(value) }}
52
+ </template>
53
+ <template #duration="{ value }">
54
+ <TTag shape="round" :theme="value > 1000 ? 'warning' : 'default'" variant="light-outline">{{ value }}ms</TTag>
55
+ </template>
56
+ <template #params="{ value }">
57
+ <pre class="json-content">{{ $Method.formatJson(value) }}</pre>
58
+ </template>
59
+ <template #response="{ value }">
60
+ <pre class="json-content">{{ $Method.formatJson(value) }}</pre>
61
+ </template>
62
+ </DetailPanel>
63
+ </div>
64
+ </div>
65
+
66
+ <div class="main-page">
67
+ <TPagination :current-page="$Data.pagerConfig.currentPage" :page-size="$Data.pagerConfig.limit" :total="$Data.pagerConfig.total" @current-change="$Method.onPageChange" @page-size-change="$Method.handleSizeChange" />
68
+ </div>
69
+ </div>
70
+ </template>
71
+
72
+ <script setup>
73
+ import { Button as TButton, Table as TTable, Tag as TTag, Pagination as TPagination, Select as TSelect, Option as TOption, MessagePlugin } from "tdesign-vue-next";
74
+ import ILucideRotateCw from "~icons/lucide/rotate-cw";
75
+ import DetailPanel from "@/components/DetailPanel.vue";
76
+ import { $Http } from "@/plugins/http";
77
+ import { withDefaultColumns } from "befly-vite/utils/withDefaultColumns";
78
+
79
+ definePage({
80
+ meta: {
81
+ title: "操作日志",
82
+ order: 3
83
+ }
84
+ });
85
+
86
+ // 响应式数据
87
+ const $Data = $ref({
88
+ tableData: [],
89
+ loading: false,
90
+ columns: withDefaultColumns([
91
+ { colKey: "username", title: "操作人", fixed: "left", width: 100 },
92
+ { colKey: "id", title: "序号", width: 80 },
93
+ { colKey: "module", title: "模块", width: 100 },
94
+ { colKey: "action", title: "操作", width: 80 },
95
+ { colKey: "path", title: "请求路径", ellipsis: true },
96
+ { colKey: "ip", title: "IP地址", width: 130 },
97
+ { colKey: "duration", title: "耗时", width: 100 },
98
+ { colKey: "operateTime", title: "操作时间", width: 170 },
99
+ { colKey: "result", title: "结果", width: 80 }
100
+ ]),
101
+ detailFields: [
102
+ { colKey: "username", title: "操作人账号" },
103
+ { colKey: "nickname", title: "操作人昵称" },
104
+ { colKey: "module", title: "操作模块" },
105
+ { colKey: "action", title: "操作类型" },
106
+ { colKey: "method", title: "请求方法" },
107
+ { colKey: "path", title: "请求路径" },
108
+ { colKey: "ip", title: "IP地址" },
109
+ { colKey: "params", title: "请求参数" },
110
+ { colKey: "response", title: "响应内容" },
111
+ { colKey: "duration", title: "耗时" },
112
+ { colKey: "operateTime", title: "操作时间" },
113
+ { colKey: "result", title: "操作结果" },
114
+ { colKey: "remark", title: "备注" }
115
+ ],
116
+ pagerConfig: {
117
+ currentPage: 1,
118
+ limit: 30,
119
+ total: 0
120
+ },
121
+ currentRow: null,
122
+ activeRowKeys: [],
123
+ filter: {
124
+ module: "",
125
+ action: "",
126
+ result: null
127
+ },
128
+ moduleOptions: [
129
+ { label: "管理员", value: "管理员" },
130
+ { label: "角色", value: "角色" },
131
+ { label: "菜单", value: "菜单" },
132
+ { label: "接口", value: "接口" },
133
+ { label: "字典", value: "字典" }
134
+ ],
135
+ actionOptions: [
136
+ { label: "新增", value: "新增" },
137
+ { label: "编辑", value: "编辑" },
138
+ { label: "删除", value: "删除" },
139
+ { label: "查询", value: "查询" }
140
+ ]
141
+ });
142
+
143
+ // 方法
144
+ const $Method = {
145
+ async initData() {
146
+ await $Method.apiOperateLogList();
147
+ },
148
+
149
+ // 加载操作日志列表
150
+ async apiOperateLogList() {
151
+ $Data.loading = true;
152
+ try {
153
+ const res = await $Http("/addon/admin/operateLog/list", {
154
+ page: $Data.pagerConfig.currentPage,
155
+ limit: $Data.pagerConfig.limit
156
+ });
157
+ $Data.tableData = res.data.lists || [];
158
+ $Data.pagerConfig.total = res.data.total || 0;
159
+
160
+ if ($Data.tableData.length > 0) {
161
+ $Data.currentRow = $Data.tableData[0];
162
+ $Data.activeRowKeys = [$Data.tableData[0].id];
163
+ } else {
164
+ $Data.currentRow = null;
165
+ $Data.activeRowKeys = [];
166
+ }
167
+ } catch (error) {
168
+ MessagePlugin.error("加载数据失败");
169
+ } finally {
170
+ $Data.loading = false;
171
+ }
172
+ },
173
+
174
+ // 筛选
175
+ handleFilter() {
176
+ $Data.pagerConfig.currentPage = 1;
177
+ $Method.apiOperateLogList();
178
+ },
179
+
180
+ // 刷新
181
+ handleRefresh() {
182
+ $Method.apiOperateLogList();
183
+ },
184
+
185
+ // 分页改变
186
+ onPageChange(currentPage) {
187
+ $Data.pagerConfig.currentPage = currentPage;
188
+ $Method.apiOperateLogList();
189
+ },
190
+
191
+ // 每页条数改变
192
+ handleSizeChange(pageSize) {
193
+ $Data.pagerConfig.limit = pageSize;
194
+ $Data.pagerConfig.currentPage = 1;
195
+ $Method.apiOperateLogList();
196
+ },
197
+
198
+ // 高亮行变化
199
+ onActiveChange(value, context) {
200
+ if (value.length === 0 && $Data.activeRowKeys.length > 0) {
201
+ return;
202
+ }
203
+ $Data.activeRowKeys = value;
204
+ if (context.activeRowList && context.activeRowList.length > 0) {
205
+ $Data.currentRow = context.activeRowList[0].row;
206
+ }
207
+ },
208
+
209
+ // 格式化时间
210
+ formatTime(timestamp) {
211
+ if (!timestamp) return "-";
212
+ const date = new Date(timestamp);
213
+ const year = date.getFullYear();
214
+ const month = String(date.getMonth() + 1).padStart(2, "0");
215
+ const day = String(date.getDate()).padStart(2, "0");
216
+ const hours = String(date.getHours()).padStart(2, "0");
217
+ const minutes = String(date.getMinutes()).padStart(2, "0");
218
+ const seconds = String(date.getSeconds()).padStart(2, "0");
219
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
220
+ },
221
+
222
+ // 格式化 JSON
223
+ formatJson(value) {
224
+ if (!value) return "-";
225
+ try {
226
+ const obj = typeof value === "string" ? JSON.parse(value) : value;
227
+ return JSON.stringify(obj, null, 2);
228
+ } catch {
229
+ return value;
230
+ }
231
+ }
232
+ };
233
+
234
+ $Method.initData();
235
+ </script>
236
+
237
+ <style scoped lang="scss">
238
+ .json-content {
239
+ margin: 0;
240
+ padding: 8px;
241
+ background: var(--td-bg-color-container);
242
+ border-radius: 4px;
243
+ font-size: 12px;
244
+ max-height: 200px;
245
+ overflow: auto;
246
+ white-space: pre-wrap;
247
+ word-break: break-all;
248
+ }
249
+ </style>