befly-admin-ui 1.48.0 → 1.50.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.
package/layouts/1.vue
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly-admin-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.50.0",
|
|
4
4
|
"gitHead": "071d17be177355cdd61f30659c6e4c5873f87d39",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Befly - 管理后台功能组件",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"dependencies": {},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"tdesign-icons-vue-next": ">=0.4.0",
|
|
53
|
-
"tdesign-vue-next": ">=1.
|
|
54
|
-
"vue": ">=3.
|
|
53
|
+
"tdesign-vue-next": ">=1.20.3",
|
|
54
|
+
"vue": ">=3.5.39",
|
|
55
55
|
"vue-router": ">=5.0.0"
|
|
56
56
|
}
|
|
57
57
|
}
|
|
@@ -43,20 +43,9 @@
|
|
|
43
43
|
<t-col :span="2" class="dashboard-card-col">
|
|
44
44
|
<div class="dashboard-card">
|
|
45
45
|
<div class="dashboard-card-content">
|
|
46
|
-
<div class="dashboard-card-label"
|
|
46
|
+
<div class="dashboard-card-label">今日在线</div>
|
|
47
47
|
<div class="dashboard-card-value-row">
|
|
48
|
-
<div class="dashboard-card-main dashboard-card-main--metric dashboard-card-main--success">{{ $Computed.
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
</t-col>
|
|
53
|
-
|
|
54
|
-
<t-col :span="2" class="dashboard-card-col">
|
|
55
|
-
<div class="dashboard-card">
|
|
56
|
-
<div class="dashboard-card-content">
|
|
57
|
-
<div class="dashboard-card-label">当前在线</div>
|
|
58
|
-
<div class="dashboard-card-value-row">
|
|
59
|
-
<div class="dashboard-card-main dashboard-card-main--metric dashboard-card-main--primary">{{ $Computed.onlineCount }}</div>
|
|
48
|
+
<div class="dashboard-card-main dashboard-card-main--metric dashboard-card-main--success">{{ $Computed.dailyTodayCount }}</div>
|
|
60
49
|
</div>
|
|
61
50
|
</div>
|
|
62
51
|
</div>
|
|
@@ -88,14 +77,11 @@ const $Data = reactive({
|
|
|
88
77
|
apiCount: 0,
|
|
89
78
|
roleCount: 0
|
|
90
79
|
},
|
|
91
|
-
|
|
80
|
+
dailyStats: {
|
|
92
81
|
today: {
|
|
93
82
|
count: 0
|
|
94
83
|
}
|
|
95
84
|
},
|
|
96
|
-
onlineStats: {
|
|
97
|
-
onlineCount: 0
|
|
98
|
-
},
|
|
99
85
|
errorStats: {
|
|
100
86
|
today: {
|
|
101
87
|
count: 0
|
|
@@ -106,11 +92,10 @@ const $Data = reactive({
|
|
|
106
92
|
const $Method = {
|
|
107
93
|
fetchData: async function () {
|
|
108
94
|
try {
|
|
109
|
-
const [overviewRes,
|
|
95
|
+
const [overviewRes, dailyStatsRes, errorStatsRes] = await Promise.all([$Http("/core/dashboard/systemOverview", {}, [""]), $Http("/core/tongJi/dailyStats", {}, [""]), $Http("/core/tongJi/errorStats", {}, [""])]);
|
|
110
96
|
|
|
111
97
|
Object.assign($Data.permissionStats, overviewRes.data);
|
|
112
|
-
$Data.
|
|
113
|
-
$Data.onlineStats.onlineCount = Number(onlineStatsRes.data?.onlineCount || 0);
|
|
98
|
+
$Data.dailyStats.today.count = Number(dailyStatsRes.data?.today?.count || 0);
|
|
114
99
|
$Data.errorStats.today.count = Number(errorStatsRes.data?.today?.count || 0);
|
|
115
100
|
} catch (_error) {
|
|
116
101
|
// 静默失败:不阻断页面展示
|
|
@@ -119,11 +104,8 @@ const $Method = {
|
|
|
119
104
|
};
|
|
120
105
|
|
|
121
106
|
const $Computed = reactive({
|
|
122
|
-
|
|
123
|
-
return $Data.
|
|
124
|
-
}),
|
|
125
|
-
onlineCount: computed(() => {
|
|
126
|
-
return $Data.onlineStats.onlineCount;
|
|
107
|
+
dailyTodayCount: computed(() => {
|
|
108
|
+
return $Data.dailyStats.today.count;
|
|
127
109
|
}),
|
|
128
110
|
errorTodayCount: computed(() => {
|
|
129
111
|
return $Data.errorStats.today.count;
|
|
@@ -1,60 +1,54 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="page-project-stats">
|
|
3
3
|
<div class="section-block">
|
|
4
|
-
<div class="section-header">
|
|
5
|
-
<
|
|
6
|
-
|
|
4
|
+
<div class="section-header page-project-stats__header">
|
|
5
|
+
<div class="page-project-stats__header-title">
|
|
6
|
+
<ChartIcon />
|
|
7
|
+
<h2>项目统计</h2>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div class="page-project-stats__header-actions">
|
|
11
|
+
<TRadioGroup v-model="$Data.layout" theme="button" variant="default-filled" size="small">
|
|
12
|
+
<TRadioButton value="list">列表</TRadioButton>
|
|
13
|
+
<TRadioButton value="card">卡片</TRadioButton>
|
|
14
|
+
</TRadioGroup>
|
|
15
|
+
</div>
|
|
7
16
|
</div>
|
|
8
17
|
|
|
9
18
|
<div class="section-content">
|
|
10
|
-
<div class="project-row-list">
|
|
19
|
+
<div v-if="$Data.layout === 'list'" class="project-row-list">
|
|
11
20
|
<div v-for="item in $Computed.productRows" :key="item.key" class="product-row">
|
|
12
21
|
<div class="product-title">{{ $Method.formatProductTitle(item) }}</div>
|
|
13
22
|
|
|
14
23
|
<t-row :gutter="[16, 16]">
|
|
15
|
-
<t-col :xs="12" :sm="6" :md="4" :lg="2" class="metric-col">
|
|
16
|
-
<div class="metric-card">
|
|
17
|
-
<span class="metric-label">在线人数</span>
|
|
18
|
-
<strong class="metric-value-online">{{ item.onlineCount }}</strong>
|
|
19
|
-
</div>
|
|
20
|
-
</t-col>
|
|
21
|
-
|
|
22
|
-
<t-col :xs="12" :sm="6" :md="4" :lg="2" class="metric-col">
|
|
24
|
+
<t-col v-for="metric in $Const.metricList" :key="metric.key" :xs="12" :sm="6" :md="4" :lg="2" class="metric-col">
|
|
23
25
|
<div class="metric-card">
|
|
24
|
-
<span class="metric-label"
|
|
25
|
-
<strong class="metric-value-
|
|
26
|
-
</div>
|
|
27
|
-
</t-col>
|
|
28
|
-
|
|
29
|
-
<t-col :xs="12" :sm="6" :md="4" :lg="2" class="metric-col">
|
|
30
|
-
<div class="metric-card">
|
|
31
|
-
<span class="metric-label">昨日访客</span>
|
|
32
|
-
<strong class="metric-value-visitor">{{ item.yesterday }}</strong>
|
|
26
|
+
<span class="metric-label">{{ metric.label }}</span>
|
|
27
|
+
<strong class="metric-value-daily">{{ item[metric.key] }}</strong>
|
|
33
28
|
</div>
|
|
34
29
|
</t-col>
|
|
30
|
+
</t-row>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
<div v-else class="project-card-grid">
|
|
35
|
+
<t-row :gutter="[16, 16]">
|
|
36
|
+
<t-col v-for="item in $Computed.productRows" :key="item.key" :xs="12" :sm="6" :md="4" :lg="2" class="product-card-col">
|
|
37
|
+
<div class="product-card">
|
|
38
|
+
<div class="product-card__title">
|
|
39
|
+
<span>{{ item.productName }}</span>
|
|
40
|
+
<span>{{ item.productCode }}</span>
|
|
40
41
|
</div>
|
|
41
|
-
</t-col>
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
<div class="product-card__metrics">
|
|
44
|
+
<div v-for="metric in $Const.metricList" :key="metric.key" class="product-card__metric">
|
|
45
|
+
<span class="product-card__metric-label">{{ metric.label }}</span>
|
|
46
|
+
<strong class="product-card__metric-value">{{ item[metric.key] }}</strong>
|
|
47
|
+
</div>
|
|
47
48
|
</div>
|
|
48
|
-
</
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<div class="metric-card">
|
|
52
|
-
<span class="metric-label">本月访客</span>
|
|
53
|
-
<strong class="metric-value-visitor">{{ item.month }}</strong>
|
|
54
|
-
</div>
|
|
55
|
-
</t-col>
|
|
56
|
-
</t-row>
|
|
57
|
-
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</t-col>
|
|
51
|
+
</t-row>
|
|
58
52
|
</div>
|
|
59
53
|
</div>
|
|
60
54
|
</div>
|
|
@@ -63,30 +57,41 @@
|
|
|
63
57
|
|
|
64
58
|
<script setup>
|
|
65
59
|
import { ChartIcon } from "tdesign-icons-vue-next";
|
|
66
|
-
import { Col as TCol, Row as TRow } from "tdesign-vue-next";
|
|
60
|
+
import { Col as TCol, RadioButton as TRadioButton, RadioGroup as TRadioGroup, Row as TRow } from "tdesign-vue-next";
|
|
67
61
|
import { computed, reactive } from "vue";
|
|
68
62
|
|
|
69
63
|
import { $Http } from "@/plugins/http.js";
|
|
70
64
|
|
|
65
|
+
const $Const = {
|
|
66
|
+
metricList: [
|
|
67
|
+
{ key: "today", label: "今日在线" },
|
|
68
|
+
{ key: "yesterday", label: "昨日在线" },
|
|
69
|
+
{ key: "dayBeforeYesterday", label: "前天在线" },
|
|
70
|
+
{ key: "recent7", label: "近7天在线" },
|
|
71
|
+
{ key: "recent30", label: "近30天在线" },
|
|
72
|
+
{ key: "month", label: "本月在线" }
|
|
73
|
+
]
|
|
74
|
+
};
|
|
75
|
+
|
|
71
76
|
const $Data = reactive({
|
|
77
|
+
layout: "list",
|
|
72
78
|
productState: {
|
|
73
79
|
options: []
|
|
74
80
|
}
|
|
75
81
|
});
|
|
76
82
|
|
|
77
83
|
const $Method = {
|
|
78
|
-
buildProjectRow: function (
|
|
84
|
+
buildProjectRow: function (item) {
|
|
79
85
|
return {
|
|
80
|
-
key: String(
|
|
81
|
-
productName: String(
|
|
82
|
-
productCode: String(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
month: Number(visitItem?.month || 0)
|
|
86
|
+
key: String(item?.key || item?.productCode || ""),
|
|
87
|
+
productName: String(item?.productName || "-"),
|
|
88
|
+
productCode: String(item?.productCode || ""),
|
|
89
|
+
today: Number(item?.today || 0),
|
|
90
|
+
yesterday: Number(item?.yesterday || 0),
|
|
91
|
+
dayBeforeYesterday: Number(item?.dayBeforeYesterday || 0),
|
|
92
|
+
recent7: Number(item?.recent7 || 0),
|
|
93
|
+
recent30: Number(item?.recent30 || 0),
|
|
94
|
+
month: Number(item?.month || 0)
|
|
90
95
|
};
|
|
91
96
|
},
|
|
92
97
|
formatProductTitle: function (row) {
|
|
@@ -99,33 +104,11 @@ const $Method = {
|
|
|
99
104
|
|
|
100
105
|
return productName || productCode || "-";
|
|
101
106
|
},
|
|
102
|
-
mapProjectRows: function (infoStatsData, onlineStatsData) {
|
|
103
|
-
const onlineMap = new Map();
|
|
104
|
-
|
|
105
|
-
for (const item of onlineStatsData?.products || []) {
|
|
106
|
-
onlineMap.set(String(item?.productCode || item?.key || ""), item);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const rows = [];
|
|
110
|
-
|
|
111
|
-
for (const item of infoStatsData?.products || []) {
|
|
112
|
-
const productCode = String(item?.productCode || item?.key || "");
|
|
113
|
-
rows.push($Method.buildProjectRow(item, onlineMap.get(productCode)));
|
|
114
|
-
onlineMap.delete(productCode);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
for (const item of onlineMap.values()) {
|
|
118
|
-
rows.push($Method.buildProjectRow(null, item));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return rows.filter((item) => {
|
|
122
|
-
return Boolean(item.key);
|
|
123
|
-
});
|
|
124
|
-
},
|
|
125
107
|
fetchData: async function () {
|
|
126
108
|
try {
|
|
127
|
-
const
|
|
128
|
-
|
|
109
|
+
const dailyStatsRes = await $Http("/core/tongJi/dailyStats", {}, [""]);
|
|
110
|
+
|
|
111
|
+
$Data.productState.options = (dailyStatsRes.data?.products || []).map((item) => $Method.buildProjectRow(item)).filter((item) => Boolean(item.key));
|
|
129
112
|
} catch (_error) {
|
|
130
113
|
// 静默失败:不阻断页面展示
|
|
131
114
|
}
|
|
@@ -150,12 +133,30 @@ $Method.fetchData();
|
|
|
150
133
|
overflow-y: auto;
|
|
151
134
|
overflow-x: hidden;
|
|
152
135
|
padding: 4px 0 12px;
|
|
136
|
+
|
|
137
|
+
// 头部区域
|
|
138
|
+
&__header {
|
|
139
|
+
justify-content: space-between;
|
|
140
|
+
gap: 12px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
&__header-title {
|
|
144
|
+
display: flex;
|
|
145
|
+
align-items: center;
|
|
146
|
+
gap: 8px;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
&__header-actions {
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
.section-content {
|
|
156
156
|
min-width: 0;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
// 列表布局
|
|
159
160
|
.project-row-list {
|
|
160
161
|
display: flex;
|
|
161
162
|
flex-direction: column;
|
|
@@ -183,10 +184,12 @@ $Method.fetchData();
|
|
|
183
184
|
@include dashboardCard.dashboardCardLabel();
|
|
184
185
|
}
|
|
185
186
|
|
|
186
|
-
.metric-value-
|
|
187
|
-
@include dashboardCard.dashboardCardNumericValue(var(--
|
|
187
|
+
.metric-value-daily {
|
|
188
|
+
@include dashboardCard.dashboardCardNumericValue(var(--success-color));
|
|
188
189
|
|
|
189
|
-
color: var(--
|
|
190
|
+
color: var(--success-color);
|
|
191
|
+
text-align: left;
|
|
192
|
+
white-space: nowrap;
|
|
190
193
|
}
|
|
191
194
|
|
|
192
195
|
.metric-card {
|
|
@@ -202,11 +205,63 @@ $Method.fetchData();
|
|
|
202
205
|
padding: 12px;
|
|
203
206
|
}
|
|
204
207
|
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
// 卡片布局
|
|
209
|
+
.project-card-grid {
|
|
210
|
+
min-width: 0;
|
|
211
|
+
}
|
|
207
212
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
213
|
+
.product-card-col {
|
|
214
|
+
min-width: 0;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.product-card {
|
|
218
|
+
display: flex;
|
|
219
|
+
flex-direction: column;
|
|
220
|
+
|
|
221
|
+
@include dashboardCard.dashboardCardShell();
|
|
222
|
+
|
|
223
|
+
justify-content: flex-start;
|
|
224
|
+
padding: 12px;
|
|
225
|
+
|
|
226
|
+
&__title {
|
|
227
|
+
display: flex;
|
|
228
|
+
align-items: center;
|
|
229
|
+
justify-content: space-between;
|
|
230
|
+
gap: 8px;
|
|
231
|
+
font-size: 14px;
|
|
232
|
+
font-weight: 700;
|
|
233
|
+
color: var(--text-primary);
|
|
234
|
+
line-height: 1.2;
|
|
235
|
+
margin-bottom: 12px;
|
|
236
|
+
|
|
237
|
+
span:last-child {
|
|
238
|
+
font-weight: 500;
|
|
239
|
+
color: var(--text-secondary);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
&__metrics {
|
|
244
|
+
display: flex;
|
|
245
|
+
flex-direction: column;
|
|
246
|
+
gap: 8px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
&__metric {
|
|
250
|
+
display: flex;
|
|
251
|
+
align-items: center;
|
|
252
|
+
justify-content: space-between;
|
|
253
|
+
gap: 8px;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
&__metric-label {
|
|
257
|
+
@include dashboardCard.dashboardCardLabel();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
&__metric-value {
|
|
261
|
+
@include dashboardCard.dashboardCardNumericValue(var(--success-color));
|
|
262
|
+
|
|
263
|
+
font-size: 16px;
|
|
264
|
+
white-space: nowrap;
|
|
265
|
+
}
|
|
211
266
|
}
|
|
212
267
|
</style>
|