@befly-addon/admin 1.1.34 → 1.2.0

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 (46) hide show
  1. package/apis/admin/cacheRefresh.ts +2 -2
  2. package/apis/menu/all.ts +8 -9
  3. package/apis/menu/list.ts +1 -2
  4. package/apis/role/apiSave.ts +3 -3
  5. package/apis/role/apis.ts +3 -3
  6. package/apis/role/menuSave.ts +2 -2
  7. package/apis/role/menus.ts +3 -3
  8. package/package.json +21 -21
  9. package/plugins/email.ts +3 -8
  10. package/tables/api.json +1 -20
  11. package/tables/menu.json +6 -11
  12. package/tables/role.json +4 -4
  13. package/views/403_1/index.vue +0 -75
  14. package/views/config/dict/components/edit.vue +0 -109
  15. package/views/config/dict/index.vue +0 -266
  16. package/views/config/dictType/components/edit.vue +0 -100
  17. package/views/config/dictType/index.vue +0 -244
  18. package/views/config/index.vue +0 -12
  19. package/views/config/system/components/edit.vue +0 -171
  20. package/views/config/system/index.vue +0 -286
  21. package/views/index/components/addonList.vue +0 -132
  22. package/views/index/components/environmentInfo.vue +0 -100
  23. package/views/index/components/operationLogs.vue +0 -112
  24. package/views/index/components/performanceMetrics.vue +0 -145
  25. package/views/index/components/quickActions.vue +0 -30
  26. package/views/index/components/serviceStatus.vue +0 -192
  27. package/views/index/components/systemNotifications.vue +0 -137
  28. package/views/index/components/systemOverview.vue +0 -190
  29. package/views/index/components/systemResources.vue +0 -111
  30. package/views/index/components/userInfo.vue +0 -204
  31. package/views/index/index.vue +0 -74
  32. package/views/log/email/index.vue +0 -292
  33. package/views/log/index.vue +0 -12
  34. package/views/log/login/index.vue +0 -187
  35. package/views/log/operate/index.vue +0 -249
  36. package/views/login_1/index.vue +0 -415
  37. package/views/people/admin/components/edit.vue +0 -168
  38. package/views/people/admin/index.vue +0 -240
  39. package/views/people/index.vue +0 -12
  40. package/views/permission/api/index.vue +0 -149
  41. package/views/permission/index.vue +0 -12
  42. package/views/permission/menu/index.vue +0 -128
  43. package/views/permission/role/components/api.vue +0 -261
  44. package/views/permission/role/components/edit.vue +0 -142
  45. package/views/permission/role/components/menu.vue +0 -116
  46. package/views/permission/role/index.vue +0 -263
@@ -1,171 +0,0 @@
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">
3
- <TForm :model="$Data.formData" label-width="120px" label-position="left" :rules="$Data2.formRules" :ref="(el) => ($Form.form = el)">
4
- <TFormItem label="配置名称" prop="name">
5
- <TInput v-model="$Data.formData.name" placeholder="请输入配置名称" :disabled="$Data.isSystem" />
6
- </TFormItem>
7
- <TFormItem label="配置代码" prop="code">
8
- <TInput v-model="$Data.formData.code" placeholder="请输入配置代码,如:site_name" :disabled="$Prop.actionType === 'upd'" />
9
- </TFormItem>
10
- <TFormItem label="配置值" prop="value">
11
- <TTextarea v-if="$Data.formData.valueType === 'json' || $Data.formData.valueType === 'text'" v-model="$Data.formData.value" placeholder="请输入配置值" :autosize="{ minRows: 3, maxRows: 8 }" />
12
- <TInput v-else v-model="$Data.formData.value" placeholder="请输入配置值" />
13
- </TFormItem>
14
- <TFormItem label="值类型" prop="valueType">
15
- <TSelect v-model="$Data.formData.valueType" :disabled="$Data.isSystem">
16
- <TOption label="字符串" value="string" />
17
- <TOption label="数字" value="number" />
18
- <TOption label="布尔" value="boolean" />
19
- <TOption label="JSON" value="json" />
20
- </TSelect>
21
- </TFormItem>
22
- <TFormItem label="配置分组" prop="group">
23
- <TSelect v-model="$Data.formData.group" placeholder="请选择分组" clearable :disabled="$Data.isSystem">
24
- <TOption v-for="item in $Data2.groupOptions" :key="item" :label="item" :value="item" />
25
- </TSelect>
26
- </TFormItem>
27
- <TFormItem label="排序" prop="sort">
28
- <TInputNumber v-model="$Data.formData.sort" :min="0" :max="9999" :disabled="$Data.isSystem" />
29
- </TFormItem>
30
- <TFormItem label="描述说明" prop="description">
31
- <TTextarea v-model="$Data.formData.description" placeholder="请输入描述说明" :autosize="{ minRows: 2, maxRows: 4 }" :disabled="$Data.isSystem" />
32
- </TFormItem>
33
- <TFormItem v-if="$Prop.actionType === 'upd' && !$Data.isSystem" label="状态" prop="state">
34
- <TRadioGroup v-model="$Data.formData.state">
35
- <TRadio :value="1">正常</TRadio>
36
- <TRadio :value="2">禁用</TRadio>
37
- </TRadioGroup>
38
- </TFormItem>
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>
45
- </template>
46
-
47
- <script setup>
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";
49
- import { $Http } from "@/plugins/http";
50
-
51
- const $Prop = defineProps({
52
- modelValue: {
53
- type: Boolean,
54
- default: false
55
- },
56
- actionType: {
57
- type: String,
58
- default: "add"
59
- },
60
- rowData: {
61
- type: Object,
62
- default: () => ({})
63
- }
64
- });
65
-
66
- const $Emit = defineEmits(["update:modelValue", "success"]);
67
-
68
- // 表单引用
69
- const $Form = $shallowRef({
70
- form: null
71
- });
72
-
73
- const $Data = $ref({
74
- visible: false,
75
- submitting: false,
76
- isSystem: false,
77
- formData: {
78
- id: 0,
79
- name: "",
80
- code: "",
81
- value: "",
82
- valueType: "string",
83
- group: "",
84
- sort: 0,
85
- description: "",
86
- state: 1
87
- }
88
- });
89
-
90
- const $Data2 = $shallowRef({
91
- formRules: {
92
- name: [{ required: true, message: "请输入配置名称", trigger: "blur" }],
93
- code: [
94
- { required: true, message: "请输入配置代码", trigger: "blur" },
95
- { pattern: /^[a-zA-Z0-9_]+$/, message: "配置代码只能包含字母、数字和下划线", trigger: "blur" }
96
- ],
97
- value: [{ required: true, message: "请输入配置值", trigger: "blur" }],
98
- valueType: [{ required: true, message: "请选择值类型", trigger: "change" }]
99
- },
100
- groupOptions: ["基础配置", "邮件配置", "存储配置", "安全配置", "其他"]
101
- });
102
-
103
- // 方法集合
104
- const $Method = {
105
- async initData() {
106
- $Method.onShow();
107
- },
108
-
109
- onShow() {
110
- $Data.visible = true;
111
- if ($Prop.actionType === "upd" && $Prop.rowData) {
112
- $Data.formData.id = $Prop.rowData.id || 0;
113
- $Data.formData.name = $Prop.rowData.name || "";
114
- $Data.formData.code = $Prop.rowData.code || "";
115
- $Data.formData.value = $Prop.rowData.value || "";
116
- $Data.formData.valueType = $Prop.rowData.valueType || "string";
117
- $Data.formData.group = $Prop.rowData.group || "";
118
- $Data.formData.sort = $Prop.rowData.sort || 0;
119
- $Data.formData.description = $Prop.rowData.description || "";
120
- $Data.formData.state = $Prop.rowData.state || 1;
121
- $Data.isSystem = $Prop.rowData.isSystem === 1;
122
- } else {
123
- $Data.formData = {
124
- id: 0,
125
- name: "",
126
- code: "",
127
- value: "",
128
- valueType: "string",
129
- group: "",
130
- sort: 0,
131
- description: "",
132
- state: 1
133
- };
134
- $Data.isSystem = false;
135
- }
136
- },
137
-
138
- onClose() {
139
- $Data.visible = false;
140
- $Emit("update:modelValue", false);
141
- },
142
-
143
- async onSubmit() {
144
- const valid = await $Form.form?.validate();
145
- if (valid !== true) return;
146
-
147
- $Data.submitting = true;
148
- try {
149
- const api = $Prop.actionType === "upd" ? "/addon/admin/sysConfig/upd" : "/addon/admin/sysConfig/ins";
150
- const res = await $Http(api, $Data.formData);
151
-
152
- if (res.code === 0) {
153
- MessagePlugin.success($Prop.actionType === "upd" ? "编辑成功" : "添加成功");
154
- $Emit("success");
155
- $Method.onClose();
156
- } else {
157
- MessagePlugin.error(res.msg || "操作失败");
158
- }
159
- } catch (error) {
160
- MessagePlugin.error("操作失败");
161
- } finally {
162
- $Data.submitting = false;
163
- }
164
- }
165
- };
166
-
167
- // 该组件由父组件 v-if 控制挂载/卸载,因此无需 watch:创建时初始化一次即可
168
- $Method.initData();
169
- </script>
170
-
171
- <style scoped lang="scss"></style>
@@ -1,286 +0,0 @@
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
- 新增配置
10
- </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>
82
- </template>
83
-
84
- <script setup>
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";
86
- import ILucidePlus from "~icons/lucide/plus";
87
- import ILucideRotateCw from "~icons/lucide/rotate-cw";
88
- import ILucidePencil from "~icons/lucide/pencil";
89
- import ILucideTrash2 from "~icons/lucide/trash-2";
90
- import ILucideChevronDown from "~icons/lucide/chevron-down";
91
- import EditDialog from "./components/edit.vue";
92
- import DetailPanel from "@/components/DetailPanel.vue";
93
- import { $Http } from "@/plugins/http";
94
- import { withDefaultColumns } from "befly-vite/utils/withDefaultColumns";
95
-
96
- definePage({
97
- meta: {
98
- title: "系统配置",
99
- order: 2
100
- }
101
- });
102
-
103
- // 响应式数据
104
- const $Data = $ref({
105
- tableData: [],
106
- loading: false,
107
- columns: withDefaultColumns([
108
- { colKey: "name", title: "配置名称", fixed: "left", width: 150 },
109
- { colKey: "id", title: "序号", width: 80 },
110
- { colKey: "code", title: "配置代码", ellipsis: true },
111
- { colKey: "value", title: "配置值", ellipsis: true, width: 200 },
112
- { colKey: "valueType", title: "值类型", width: 100 },
113
- { colKey: "group", title: "分组", width: 100 },
114
- { colKey: "sort", title: "排序", width: 80 },
115
- { colKey: "isSystem", title: "类型", width: 80 },
116
- { colKey: "state", title: "状态", width: 80 },
117
- { colKey: "operation", title: "操作", width: 100 }
118
- ]),
119
- detailFields: [
120
- { colKey: "name", title: "配置名称" },
121
- { colKey: "code", title: "配置代码" },
122
- { colKey: "value", title: "配置值" },
123
- { colKey: "valueType", title: "值类型" },
124
- { colKey: "group", title: "配置分组" },
125
- { colKey: "sort", title: "排序" },
126
- { colKey: "isSystem", title: "配置类型" },
127
- { colKey: "description", title: "描述说明" }
128
- ],
129
- pagerConfig: {
130
- currentPage: 1,
131
- limit: 30,
132
- total: 0
133
- },
134
- currentRow: null,
135
- activeRowKeys: [],
136
- editVisible: false,
137
- actionType: "add",
138
- rowData: {},
139
- filter: {
140
- group: ""
141
- },
142
- groupOptions: ["基础配置", "邮件配置", "存储配置", "安全配置", "其他"]
143
- });
144
-
145
- // 方法
146
- const $Method = {
147
- async initData() {
148
- await $Method.apiConfigList();
149
- },
150
-
151
- // 加载配置列表
152
- async apiConfigList() {
153
- $Data.loading = true;
154
- try {
155
- const res = await $Http("/addon/admin/sysConfig/list", {
156
- page: $Data.pagerConfig.currentPage,
157
- limit: $Data.pagerConfig.limit
158
- });
159
- $Data.tableData = res.data.lists || [];
160
- $Data.pagerConfig.total = res.data.total || 0;
161
-
162
- if ($Data.tableData.length > 0) {
163
- $Data.currentRow = $Data.tableData[0];
164
- $Data.activeRowKeys = [$Data.tableData[0].id];
165
- } else {
166
- $Data.currentRow = null;
167
- $Data.activeRowKeys = [];
168
- }
169
- } catch (error) {
170
- MessagePlugin.error("加载数据失败");
171
- } finally {
172
- $Data.loading = false;
173
- }
174
- },
175
-
176
- // 删除配置
177
- async apiConfigDel(row) {
178
- if (row.isSystem === 1) {
179
- MessagePlugin.warning("系统配置不允许删除");
180
- return;
181
- }
182
-
183
- let dialog = null;
184
- let destroyed = false;
185
-
186
- const destroy = () => {
187
- if (destroyed) return;
188
- destroyed = true;
189
- if (dialog && typeof dialog.destroy === "function") {
190
- dialog.destroy();
191
- }
192
- };
193
-
194
- dialog = DialogPlugin.confirm({
195
- header: "确认删除",
196
- body: `确认删除配置“${row.name}”吗?`,
197
- status: "warning",
198
- confirmBtn: "删除",
199
- cancelBtn: "取消",
200
- onConfirm: async () => {
201
- if (dialog && typeof dialog.setConfirmLoading === "function") {
202
- dialog.setConfirmLoading(true);
203
- }
204
-
205
- try {
206
- await $Http("/addon/admin/sysConfig/del", { id: row.id });
207
- MessagePlugin.success("删除成功");
208
- destroy();
209
- await $Method.apiConfigList();
210
- } catch (error) {
211
- MessagePlugin.error("删除失败");
212
- } finally {
213
- if (dialog && typeof dialog.setConfirmLoading === "function") {
214
- dialog.setConfirmLoading(false);
215
- }
216
- }
217
- },
218
- onClose: () => {
219
- destroy();
220
- }
221
- });
222
- },
223
-
224
- // 筛选
225
- handleFilter() {
226
- $Data.pagerConfig.currentPage = 1;
227
- $Method.apiConfigList();
228
- },
229
-
230
- // 刷新
231
- handleRefresh() {
232
- $Method.apiConfigList();
233
- },
234
-
235
- // 分页改变
236
- onPageChange(currentPage) {
237
- $Data.pagerConfig.currentPage = currentPage;
238
- $Method.apiConfigList();
239
- },
240
-
241
- // 每页条数改变
242
- handleSizeChange(pageSize) {
243
- $Data.pagerConfig.limit = pageSize;
244
- $Data.pagerConfig.currentPage = 1;
245
- $Method.apiConfigList();
246
- },
247
-
248
- // 高亮行变化
249
- onActiveChange(value, context) {
250
- if (value.length === 0 && $Data.activeRowKeys.length > 0) {
251
- return;
252
- }
253
- $Data.activeRowKeys = value;
254
- if (context.activeRowList && context.activeRowList.length > 0) {
255
- $Data.currentRow = context.activeRowList[0].row;
256
- }
257
- },
258
-
259
- // 操作菜单点击
260
- onAction(command, rowData) {
261
- $Data.actionType = command;
262
- $Data.rowData = rowData;
263
- if (command === "add" || command === "upd") {
264
- $Data.editVisible = true;
265
- } else if (command === "del") {
266
- $Method.apiConfigDel(rowData);
267
- }
268
- }
269
- };
270
-
271
- $Method.initData();
272
- </script>
273
-
274
- <style scoped lang="scss">
275
- .config-value {
276
- margin: 0;
277
- padding: 8px;
278
- background: var(--td-bg-color-container);
279
- border-radius: 4px;
280
- font-size: 12px;
281
- max-height: 150px;
282
- overflow: auto;
283
- white-space: pre-wrap;
284
- word-break: break-all;
285
- }
286
- </style>
@@ -1,132 +0,0 @@
1
- <template>
2
- <div class="section-block">
3
- <div class="section-header flex items-center gap-2">
4
- <i-lucide:package />
5
- <h2>已安装插件</h2>
6
- </div>
7
- <div class="section-content">
8
- <div class="addon-list">
9
- <div v-for="addon in addonList" :key="addon.name" class="addon-item">
10
- <div class="addon-icon">
11
- <i-lucide:box />
12
- </div>
13
- <div class="addon-info">
14
- <div class="addon-title">
15
- <span class="addon-name">{{ addon.title }}</span>
16
- <t-tag type="success" size="small">{{ addon.version }}</t-tag>
17
- </div>
18
- <div class="addon-desc">{{ addon.description }}</div>
19
- </div>
20
- </div>
21
- </div>
22
- </div>
23
- </div>
24
- </template>
25
-
26
- <script setup>
27
- import { $Http } from "@/plugins/http";
28
-
29
- // 组件内部数据
30
- const addonList = $ref([]);
31
-
32
- // 获取数据
33
- const fetchData = async () => {
34
- try {
35
- const { data } = await $Http("/addon/admin/dashboard/addonList");
36
- addonList.splice(0, addonList.length, ...data);
37
- } catch (error) {
38
- // 静默失败:不阻断页面展示
39
- }
40
- };
41
-
42
- fetchData();
43
- </script>
44
-
45
- <style scoped lang="scss">
46
- .addon-list {
47
- display: flex;
48
- flex-direction: column;
49
- gap: 8px;
50
-
51
- .addon-item {
52
- position: relative;
53
- background: var(--bg-color-container);
54
- border: 1px solid var(--border-color);
55
- border-left: 3px solid var(--primary-color);
56
- border-radius: var(--border-radius-small);
57
- padding: 10px 12px;
58
- display: flex;
59
- align-items: center;
60
- gap: 10px;
61
- transition: all 0.3s;
62
-
63
- &:hover {
64
- border-left-color: var(--success-color);
65
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
66
- transform: translateY(-2px);
67
- }
68
-
69
- .addon-status-badge {
70
- position: absolute;
71
- top: 8px;
72
- right: 8px;
73
- width: 8px;
74
- height: 8px;
75
- border-radius: 50%;
76
- background: var(--text-disabled);
77
- transition: all 0.3s;
78
-
79
- &::after {
80
- content: '';
81
- position: absolute;
82
- top: -2px;
83
- right: -2px;
84
- width: 8px;
85
- height: 8px;
86
- border-radius: 50%;
87
- background: var(--success-color);
88
- box-shadow: 0 0 0 2px rgba(var(--success-color-rgb), 0.2);
89
- }
90
-
91
- .addon-icon {
92
- width: 32px;
93
- height: 32px;
94
- background: linear-gradient(135deg, var(--primary-color), #764ba2);
95
- border-radius: var(--border-radius-small);
96
- display: flex;
97
- align-items: center;
98
- justify-content: center;
99
- color: white;
100
- flex-shrink: 0;
101
- }
102
-
103
- .addon-info {
104
- flex: 1;
105
- min-width: 0;
106
- padding-right: 16px;
107
-
108
- .addon-title {
109
- display: flex;
110
- align-items: center;
111
- gap: 6px;
112
- margin-bottom: 2px;
113
-
114
- .addon-name {
115
- font-size: 14px;
116
- font-weight: 600;
117
- color: var(--text-primary);
118
- }
119
- }
120
-
121
- .addon-desc {
122
- font-size: 14px;
123
- color: var(--text-secondary);
124
- line-height: 1.3;
125
- overflow: hidden;
126
- text-overflow: ellipsis;
127
- white-space: nowrap;
128
- }
129
- }
130
- }
131
- }
132
- </style>
@@ -1,100 +0,0 @@
1
- <template>
2
- <div class="section-block">
3
- <div class="section-header flex items-center gap-2">
4
- <ILucideServer />
5
- <h2>运行环境</h2>
6
- </div>
7
- <div class="section-content">
8
- <div class="env-grid-compact">
9
- <div class="env-compact-item">
10
- <span class="env-label">操作系统</span>
11
- <span class="env-value">{{ environmentInfo.os }}</span>
12
- </div>
13
- <div class="env-compact-item">
14
- <span class="env-label">服务器</span>
15
- <span class="env-value">{{ environmentInfo.server }}</span>
16
- </div>
17
- <div class="env-compact-item">
18
- <span class="env-label">Node版本</span>
19
- <span class="env-value">{{ environmentInfo.nodeVersion }}</span>
20
- </div>
21
- <div class="env-compact-item">
22
- <span class="env-label">数据库</span>
23
- <span class="env-value">{{ environmentInfo.database }}</span>
24
- </div>
25
- <div class="env-compact-item">
26
- <span class="env-label">缓存</span>
27
- <span class="env-value">{{ environmentInfo.cache }}</span>
28
- </div>
29
- <div class="env-compact-item">
30
- <span class="env-label">时区</span>
31
- <span class="env-value">{{ environmentInfo.timezone }}</span>
32
- </div>
33
- </div>
34
- </div>
35
- </div>
36
- </template>
37
-
38
- <script setup>
39
- import ILucideServer from "~icons/lucide/server";
40
- import { $Http } from "@/plugins/http";
41
-
42
- // 组件内部数据
43
- const environmentInfo = $ref({
44
- os: "",
45
- server: "",
46
- nodeVersion: "",
47
- database: "",
48
- cache: "",
49
- timezone: ""
50
- });
51
-
52
- // 获取数据
53
- const fetchData = async () => {
54
- try {
55
- const { data } = await $Http("/addon/admin/dashboard/environmentInfo");
56
- Object.assign(environmentInfo, data);
57
- } catch (error) {
58
- // 静默失败:不阻断页面展示
59
- }
60
- };
61
-
62
- fetchData();
63
- </script>
64
-
65
- <style scoped lang="scss">
66
- .env-grid-compact {
67
- display: grid;
68
- grid-template-columns: repeat(4, 1fr);
69
- gap: 10px;
70
-
71
- .env-compact-item {
72
- display: flex;
73
- justify-content: space-between;
74
- align-items: center;
75
- padding: 10px 12px;
76
- background: var(--bg-color-container);
77
- border-radius: 6px;
78
- border: 1px solid var(--border-color);
79
- transition: all 0.2s ease;
80
-
81
- &:hover {
82
- background: rgba(var(--primary-color-rgb), 0.03);
83
- border-color: rgba(var(--primary-color-rgb), 0.2);
84
- }
85
-
86
- .env-label {
87
- font-size: 14px;
88
- color: var(--text-secondary);
89
- font-weight: 500;
90
- }
91
-
92
- .env-value {
93
- font-size: 14px;
94
- color: var(--text-primary);
95
- font-weight: 600;
96
- text-align: right;
97
- }
98
- }
99
- }
100
- </style>