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