@liujitcn/kratos-admin-system 0.0.53 → 0.0.55
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.
|
@@ -35,10 +35,6 @@ export interface BaseFile {
|
|
|
35
35
|
id: number;
|
|
36
36
|
/** 租户ID */
|
|
37
37
|
tenant_id: number;
|
|
38
|
-
/** 存储供应商 */
|
|
39
|
-
provider: number;
|
|
40
|
-
/** 存储桶名称 */
|
|
41
|
-
bucket_name: string;
|
|
42
38
|
/** 文件目录 */
|
|
43
39
|
file_directory: string;
|
|
44
40
|
/** 文件唯一标识 */
|
|
@@ -61,6 +57,8 @@ export interface BaseFile {
|
|
|
61
57
|
created_by: number;
|
|
62
58
|
/** 创建时间 */
|
|
63
59
|
created_at: string;
|
|
60
|
+
/** 文件访问方式:1公开,2需要访问令牌 */
|
|
61
|
+
access_mode: number;
|
|
64
62
|
}
|
|
65
63
|
/** Admin文件资产管理服务。 */
|
|
66
64
|
export interface BaseFileService {
|
|
@@ -49,10 +49,6 @@ export interface BaseFile {
|
|
|
49
49
|
id: number;
|
|
50
50
|
/** 租户ID */
|
|
51
51
|
tenant_id: number;
|
|
52
|
-
/** 存储供应商 */
|
|
53
|
-
provider: number;
|
|
54
|
-
/** 存储桶名称 */
|
|
55
|
-
bucket_name: string;
|
|
56
52
|
/** 文件目录 */
|
|
57
53
|
file_directory: string;
|
|
58
54
|
/** 文件唯一标识 */
|
|
@@ -75,6 +71,8 @@ export interface BaseFile {
|
|
|
75
71
|
created_by: number;
|
|
76
72
|
/** 创建时间 */
|
|
77
73
|
created_at: string;
|
|
74
|
+
/** 文件访问方式:1公开,2需要访问令牌 */
|
|
75
|
+
access_mode: number;
|
|
78
76
|
}
|
|
79
77
|
|
|
80
78
|
/** Admin文件资产管理服务。 */
|
|
@@ -44,7 +44,11 @@
|
|
|
44
44
|
/>
|
|
45
45
|
</template>
|
|
46
46
|
<template #imageValue>
|
|
47
|
-
<UploadImg
|
|
47
|
+
<UploadImg
|
|
48
|
+
v-model:image-url="formData.value"
|
|
49
|
+
upload-type="config"
|
|
50
|
+
:access-mode="configImageAccessMode"
|
|
51
|
+
/>
|
|
48
52
|
</template>
|
|
49
53
|
<template #richTextValue>
|
|
50
54
|
<WangEditor v-model:value="formData.value" upload-type="config" />
|
|
@@ -135,6 +139,7 @@ import type {
|
|
|
135
139
|
} from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_config";
|
|
136
140
|
import type { BaseI18n } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_i18n";
|
|
137
141
|
import { BaseConfigSite } from "@liujitcn/kratos-admin-system/rpc/base/v1/config";
|
|
142
|
+
import { BaseFileAccessMode } from "@liujitcn/kratos-admin-core/rpc/base/v1/file";
|
|
138
143
|
import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
|
|
139
144
|
import { BaseConfigType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_config";
|
|
140
145
|
import { I18nTargetType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_i18n";
|
|
@@ -364,6 +369,14 @@ const BASE_CONFIG_DICT_CODE_MAP: Record<string, string> = {
|
|
|
364
369
|
/** 当前字典类配置对应的字典编码,未配置映射时允许退回手动输入。 */
|
|
365
370
|
const dictValueCode = computed(() => BASE_CONFIG_DICT_CODE_MAP[formData.key] ?? "");
|
|
366
371
|
|
|
372
|
+
/** 需要匿名访问的系统图片配置键,其他图片配置继续按默认授权模式上传。 */
|
|
373
|
+
const PUBLIC_IMAGE_CONFIG_KEYS = new Set(["adminLogo", "appLogo", "background"]);
|
|
374
|
+
const configImageAccessMode = computed(() =>
|
|
375
|
+
PUBLIC_IMAGE_CONFIG_KEYS.has(formData.key)
|
|
376
|
+
? BaseFileAccessMode.BASE_FILE_ACCESS_MODE_PUBLIC
|
|
377
|
+
: BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED
|
|
378
|
+
);
|
|
379
|
+
|
|
367
380
|
/** 系统配置表单字段配置。 */
|
|
368
381
|
const formFields = computed<ProFormField[]>(() => [
|
|
369
382
|
{
|
|
@@ -8,28 +8,42 @@
|
|
|
8
8
|
:key="metric.key"
|
|
9
9
|
:class="['metric-card', `metric-card--${metric.tone}`, 'admin-page-card']"
|
|
10
10
|
>
|
|
11
|
-
<div class="metric-
|
|
12
|
-
<span class="metric-label">{{ metric.label }}</span
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<el-icon><component :is="metric.icon" /></el-icon>
|
|
11
|
+
<div class="metric-top">
|
|
12
|
+
<span class="metric-label">{{ metric.label }}</span
|
|
13
|
+
><span class="metric-icon"
|
|
14
|
+
><el-icon><component :is="metric.icon" /></el-icon
|
|
15
|
+
></span>
|
|
17
16
|
</div>
|
|
17
|
+
<strong class="metric-value">{{ metric.value }}</strong>
|
|
18
18
|
</el-card>
|
|
19
19
|
</section>
|
|
20
|
-
|
|
21
20
|
<section class="chart-grid">
|
|
22
21
|
<el-card class="chart-card chart-card-wide admin-page-card">
|
|
23
|
-
<template #header
|
|
24
|
-
|
|
22
|
+
<template #header
|
|
23
|
+
><div class="chart-heading">
|
|
24
|
+
<span>{{ t("system.dashboard.login_trend") }}</span
|
|
25
|
+
><small>近 7 天</small>
|
|
26
|
+
</div></template
|
|
27
|
+
>
|
|
28
|
+
<div class="chart-wrap"><ECharts :option="loginTrendOption" height="285" /></div>
|
|
25
29
|
</el-card>
|
|
26
30
|
<el-card class="chart-card admin-page-card">
|
|
27
|
-
<template #header
|
|
28
|
-
|
|
31
|
+
<template #header
|
|
32
|
+
><div class="chart-heading">
|
|
33
|
+
<span>{{ t("system.dashboard.login_distribution") }}</span
|
|
34
|
+
><small>总计 {{ loginDistributionTotal }}</small>
|
|
35
|
+
</div></template
|
|
36
|
+
>
|
|
37
|
+
<div class="chart-wrap"><ECharts :option="loginDistributionOption" height="265" /></div>
|
|
29
38
|
</el-card>
|
|
30
39
|
<el-card class="chart-card admin-page-card">
|
|
31
|
-
<template #header
|
|
32
|
-
|
|
40
|
+
<template #header
|
|
41
|
+
><div class="chart-heading">
|
|
42
|
+
<span>{{ t("system.dashboard.operation_distribution") }}</span
|
|
43
|
+
><small>总计 {{ operationDistributionTotal }}</small>
|
|
44
|
+
</div></template
|
|
45
|
+
>
|
|
46
|
+
<div class="chart-wrap"><ECharts :option="operationDistributionOption" height="265" /></div>
|
|
33
47
|
</el-card>
|
|
34
48
|
</section>
|
|
35
49
|
</template>
|
|
@@ -37,7 +51,7 @@
|
|
|
37
51
|
</template>
|
|
38
52
|
|
|
39
53
|
<script setup lang="ts">
|
|
40
|
-
import { computed, onMounted, ref } from "vue";
|
|
54
|
+
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
|
41
55
|
import ECharts from "@liujitcn/kratos-admin-core/components/ECharts/index.vue";
|
|
42
56
|
import { defBaseDashboardService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_dashboard";
|
|
43
57
|
import type {
|
|
@@ -52,25 +66,84 @@ const overview = ref<BaseDashboardOverview>();
|
|
|
52
66
|
const trend = ref<BaseDashboardTrendPoint[]>([]);
|
|
53
67
|
const loginDistribution = ref<BaseDashboardDistributionItem[]>([]);
|
|
54
68
|
const operationDistribution = ref<BaseDashboardDistributionItem[]>([]);
|
|
55
|
-
|
|
69
|
+
const chartTheme = ref({
|
|
70
|
+
primary: "#409eff",
|
|
71
|
+
success: "#67c23a",
|
|
72
|
+
warning: "#e6a23c",
|
|
73
|
+
info: "#909399",
|
|
74
|
+
danger: "#f56c6c",
|
|
75
|
+
text: "#606266",
|
|
76
|
+
divider: "#e4e7ed",
|
|
77
|
+
background: "#ffffff"
|
|
78
|
+
});
|
|
56
79
|
const metrics = computed(() => [
|
|
57
80
|
{ key: "users", label: t("system.dashboard.users"), value: overview.value?.user_count ?? 0, icon: User, tone: "primary" },
|
|
58
81
|
{ key: "roles", label: t("system.dashboard.roles"), value: overview.value?.role_count ?? 0, icon: UserFilled, tone: "success" },
|
|
59
|
-
{
|
|
60
|
-
|
|
82
|
+
{
|
|
83
|
+
key: "logins",
|
|
84
|
+
label: t("system.dashboard.today_logins"),
|
|
85
|
+
value: overview.value?.today_login_count ?? 0,
|
|
86
|
+
icon: Timer,
|
|
87
|
+
tone: "warning"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
key: "operations",
|
|
91
|
+
label: t("system.dashboard.today_operations"),
|
|
92
|
+
value: overview.value?.today_operation_count ?? 0,
|
|
93
|
+
icon: Operation,
|
|
94
|
+
tone: "danger"
|
|
95
|
+
}
|
|
61
96
|
]);
|
|
97
|
+
const loginDistributionTotal = computed(() => loginDistribution.value.reduce((total, item) => total + item.count, 0));
|
|
98
|
+
const operationDistributionTotal = computed(() => operationDistribution.value.reduce((total, item) => total + item.count, 0));
|
|
62
99
|
|
|
100
|
+
function readThemeColor(name: string, fallback: string) {
|
|
101
|
+
return getComputedStyle(document.documentElement).getPropertyValue(name).trim() || fallback;
|
|
102
|
+
}
|
|
103
|
+
function refreshChartTheme() {
|
|
104
|
+
chartTheme.value = {
|
|
105
|
+
primary: readThemeColor("--el-color-primary", "#409eff"),
|
|
106
|
+
success: readThemeColor("--el-color-success", "#67c23a"),
|
|
107
|
+
warning: readThemeColor("--el-color-warning", "#e6a23c"),
|
|
108
|
+
info: readThemeColor("--el-color-info", "#909399"),
|
|
109
|
+
danger: readThemeColor("--el-color-danger", "#f56c6c"),
|
|
110
|
+
text: readThemeColor("--el-text-color-secondary", "#606266"),
|
|
111
|
+
divider: readThemeColor("--el-border-color-lighter", "#e4e7ed"),
|
|
112
|
+
background: readThemeColor("--el-bg-color", "#ffffff")
|
|
113
|
+
};
|
|
114
|
+
}
|
|
63
115
|
const loginTrendOption = computed(() => ({
|
|
116
|
+
color: [chartTheme.value.primary],
|
|
64
117
|
tooltip: { trigger: "axis" as const },
|
|
65
|
-
grid: { left:
|
|
66
|
-
xAxis: {
|
|
67
|
-
|
|
68
|
-
|
|
118
|
+
grid: { left: 42, right: 20, top: 20, bottom: 28 },
|
|
119
|
+
xAxis: {
|
|
120
|
+
type: "category" as const,
|
|
121
|
+
boundaryGap: false,
|
|
122
|
+
data: trend.value.map(item => item.date),
|
|
123
|
+
axisLine: { lineStyle: { color: chartTheme.value.divider } },
|
|
124
|
+
axisLabel: { color: chartTheme.value.text }
|
|
125
|
+
},
|
|
126
|
+
yAxis: {
|
|
127
|
+
type: "value" as const,
|
|
128
|
+
minInterval: 1,
|
|
129
|
+
splitLine: { lineStyle: { color: chartTheme.value.divider } },
|
|
130
|
+
axisLabel: { color: chartTheme.value.text }
|
|
131
|
+
},
|
|
132
|
+
series: [
|
|
133
|
+
{
|
|
134
|
+
type: "line" as const,
|
|
135
|
+
smooth: true,
|
|
136
|
+
data: trend.value.map(item => item.count),
|
|
137
|
+
symbol: "circle",
|
|
138
|
+
symbolSize: 7,
|
|
139
|
+
lineStyle: { width: 3, color: chartTheme.value.primary },
|
|
140
|
+
itemStyle: { color: chartTheme.value.primary, borderWidth: 2, borderColor: chartTheme.value.background },
|
|
141
|
+
areaStyle: { color: chartTheme.value.primary, opacity: 0.12 }
|
|
142
|
+
}
|
|
143
|
+
]
|
|
69
144
|
}));
|
|
70
|
-
|
|
71
145
|
const loginDistributionOption = computed(() => distributionOption(loginDistribution.value));
|
|
72
146
|
const operationDistributionOption = computed(() => distributionOption(operationDistribution.value));
|
|
73
|
-
|
|
74
147
|
async function loadDashboard() {
|
|
75
148
|
loading.value = true;
|
|
76
149
|
try {
|
|
@@ -85,12 +158,11 @@ async function loadDashboard() {
|
|
|
85
158
|
loginDistribution.value = loginResponse.items ?? [];
|
|
86
159
|
operationDistribution.value = operationResponse.items ?? [];
|
|
87
160
|
} catch {
|
|
88
|
-
|
|
161
|
+
/* 请求层已展示具体错误。 */
|
|
89
162
|
} finally {
|
|
90
163
|
loading.value = false;
|
|
91
164
|
}
|
|
92
165
|
}
|
|
93
|
-
|
|
94
166
|
/** 将审计技术枚举名转换为当前语言的展示文案。 */
|
|
95
167
|
function distributionItemLabel(label: string) {
|
|
96
168
|
const mappings = [
|
|
@@ -102,67 +174,109 @@ function distributionItemLabel(label: string) {
|
|
|
102
174
|
const value = label.slice(mapping[0].length).toLowerCase();
|
|
103
175
|
return value === "unspecified" ? t("common.message.unknown") : t(`${mapping[1]}${value}`);
|
|
104
176
|
}
|
|
105
|
-
|
|
106
177
|
/** 创建使用本地化图例的分布图配置。 */
|
|
107
178
|
function distributionOption(items: BaseDashboardDistributionItem[]) {
|
|
108
179
|
return {
|
|
180
|
+
color: [
|
|
181
|
+
chartTheme.value.primary,
|
|
182
|
+
chartTheme.value.success,
|
|
183
|
+
chartTheme.value.info,
|
|
184
|
+
chartTheme.value.warning,
|
|
185
|
+
chartTheme.value.danger
|
|
186
|
+
],
|
|
109
187
|
tooltip: { trigger: "item" as const },
|
|
110
|
-
legend: { bottom: 0, type: "scroll" as const },
|
|
111
|
-
series: [
|
|
188
|
+
legend: { bottom: 0, type: "scroll" as const, textStyle: { color: chartTheme.value.text } },
|
|
189
|
+
series: [
|
|
190
|
+
{
|
|
191
|
+
type: "pie" as const,
|
|
192
|
+
radius: ["55%", "76%"] as [string, string],
|
|
193
|
+
center: ["50%", "43%"] as [string, string],
|
|
194
|
+
label: { color: chartTheme.value.text },
|
|
195
|
+
itemStyle: { borderColor: chartTheme.value.background, borderWidth: 3 },
|
|
196
|
+
data: items.map(item => ({ name: distributionItemLabel(item.label), value: item.count }))
|
|
197
|
+
}
|
|
198
|
+
]
|
|
112
199
|
};
|
|
113
200
|
}
|
|
114
|
-
|
|
115
|
-
|
|
201
|
+
onMounted(() => {
|
|
202
|
+
refreshChartTheme();
|
|
203
|
+
loadDashboard();
|
|
204
|
+
window.addEventListener("theme-change", refreshChartTheme);
|
|
205
|
+
});
|
|
206
|
+
onBeforeUnmount(() => window.removeEventListener("theme-change", refreshChartTheme));
|
|
116
207
|
</script>
|
|
117
208
|
|
|
118
209
|
<style scoped>
|
|
119
210
|
.dashboard-page {
|
|
120
211
|
min-width: 0;
|
|
121
212
|
}
|
|
213
|
+
.dashboard-heading {
|
|
214
|
+
display: flex;
|
|
215
|
+
align-items: flex-end;
|
|
216
|
+
justify-content: space-between;
|
|
217
|
+
margin-bottom: 22px;
|
|
218
|
+
}
|
|
219
|
+
.dashboard-eyebrow {
|
|
220
|
+
display: block;
|
|
221
|
+
color: var(--admin-page-text-placeholder);
|
|
222
|
+
font-size: 11px;
|
|
223
|
+
letter-spacing: 0.12em;
|
|
224
|
+
}
|
|
225
|
+
.dashboard-heading h1 {
|
|
226
|
+
margin: 6px 0 0;
|
|
227
|
+
color: var(--admin-page-text-primary);
|
|
228
|
+
font-size: 26px;
|
|
229
|
+
line-height: 1.2;
|
|
230
|
+
}
|
|
231
|
+
.dashboard-updated {
|
|
232
|
+
color: var(--admin-page-text-placeholder);
|
|
233
|
+
font-size: 12px;
|
|
234
|
+
}
|
|
122
235
|
.metric-grid,
|
|
123
236
|
.chart-grid {
|
|
124
237
|
display: grid;
|
|
125
|
-
gap:
|
|
238
|
+
gap: 16px;
|
|
126
239
|
}
|
|
127
240
|
.metric-grid {
|
|
128
241
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
129
|
-
margin-bottom:
|
|
242
|
+
margin-bottom: 16px;
|
|
243
|
+
}
|
|
244
|
+
.metric-card {
|
|
245
|
+
position: relative;
|
|
246
|
+
overflow: hidden;
|
|
130
247
|
}
|
|
131
248
|
.metric-card :deep(.el-card__body) {
|
|
249
|
+
min-height: 124px;
|
|
250
|
+
padding: 18px 20px 16px;
|
|
251
|
+
}
|
|
252
|
+
.metric-top {
|
|
132
253
|
display: flex;
|
|
133
|
-
gap: 16px;
|
|
134
254
|
align-items: center;
|
|
135
255
|
justify-content: space-between;
|
|
136
|
-
min-height: 92px;
|
|
137
|
-
}
|
|
138
|
-
.metric-content {
|
|
139
|
-
display: flex;
|
|
140
|
-
flex-direction: column;
|
|
141
|
-
gap: 10px;
|
|
142
|
-
min-width: 0;
|
|
143
256
|
}
|
|
144
257
|
.metric-label {
|
|
258
|
+
color: var(--admin-page-text-secondary);
|
|
145
259
|
font-size: 13px;
|
|
146
|
-
color: var(--el-text-color-secondary);
|
|
147
260
|
}
|
|
148
261
|
.metric-value {
|
|
149
|
-
|
|
262
|
+
display: block;
|
|
263
|
+
margin-top: 18px;
|
|
264
|
+
color: var(--admin-page-text-primary);
|
|
265
|
+
font-size: 32px;
|
|
150
266
|
line-height: 1;
|
|
151
|
-
|
|
267
|
+
font-variant-numeric: tabular-nums;
|
|
152
268
|
}
|
|
153
269
|
.metric-icon {
|
|
154
|
-
display:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
height: 48px;
|
|
270
|
+
display: grid;
|
|
271
|
+
width: 34px;
|
|
272
|
+
height: 34px;
|
|
273
|
+
place-items: center;
|
|
274
|
+
border-radius: 10px;
|
|
160
275
|
color: var(--metric-color);
|
|
161
276
|
background: var(--metric-bg);
|
|
162
|
-
border-radius: var(--admin-page-radius);
|
|
163
277
|
}
|
|
164
278
|
.metric-icon .el-icon {
|
|
165
|
-
font-size:
|
|
279
|
+
font-size: 17px;
|
|
166
280
|
}
|
|
167
281
|
.metric-card--primary {
|
|
168
282
|
--metric-color: var(--el-color-primary);
|
|
@@ -186,11 +300,45 @@ onMounted(loadDashboard);
|
|
|
186
300
|
.chart-card-wide {
|
|
187
301
|
grid-column: 1 / -1;
|
|
188
302
|
}
|
|
303
|
+
.chart-card :deep(.el-card__header) {
|
|
304
|
+
padding: 17px 22px 14px;
|
|
305
|
+
border-bottom-color: var(--admin-page-divider);
|
|
306
|
+
}
|
|
307
|
+
.chart-card :deep(.el-card__body) {
|
|
308
|
+
padding: 0 20px 16px;
|
|
309
|
+
}
|
|
310
|
+
.chart-heading {
|
|
311
|
+
display: flex;
|
|
312
|
+
align-items: center;
|
|
313
|
+
justify-content: space-between;
|
|
314
|
+
}
|
|
315
|
+
.chart-heading span {
|
|
316
|
+
color: var(--admin-page-text-primary);
|
|
317
|
+
font-size: 16px;
|
|
318
|
+
font-weight: 600;
|
|
319
|
+
}
|
|
320
|
+
.chart-heading small {
|
|
321
|
+
color: var(--admin-page-text-placeholder);
|
|
322
|
+
font-size: 12px;
|
|
323
|
+
font-weight: 400;
|
|
324
|
+
}
|
|
189
325
|
.chart-wrap {
|
|
190
|
-
height:
|
|
326
|
+
height: 285px;
|
|
327
|
+
}
|
|
328
|
+
.chart-card:not(.chart-card-wide) .chart-wrap {
|
|
329
|
+
height: 265px;
|
|
191
330
|
}
|
|
192
|
-
|
|
193
331
|
@media (width <= 960px) {
|
|
332
|
+
.metric-grid {
|
|
333
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
@media (width <= 640px) {
|
|
337
|
+
.dashboard-heading {
|
|
338
|
+
align-items: flex-start;
|
|
339
|
+
flex-direction: column;
|
|
340
|
+
gap: 8px;
|
|
341
|
+
}
|
|
194
342
|
.metric-grid,
|
|
195
343
|
.chart-grid {
|
|
196
344
|
grid-template-columns: 1fr;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liujitcn/kratos-admin-system",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.55",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "kratos-admin system management module",
|
|
7
7
|
"license": "MIT",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"sortablejs": "^1.15.2",
|
|
17
17
|
"swagger-ui-dist": "^5.32.15",
|
|
18
18
|
"vue-element-plus-x": "^2.0.3",
|
|
19
|
-
"@liujitcn/kratos-admin-core": "0.0.
|
|
19
|
+
"@liujitcn/kratos-admin-core": "0.0.55"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"@element-plus/icons-vue": "^2.3.1",
|