befly-admin-ui 1.22.0 → 1.23.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly-admin-ui",
3
- "version": "1.22.0",
3
+ "version": "1.23.0",
4
4
  "gitHead": "071d17be177355cdd61f30659c6e4c5873f87d39",
5
5
  "private": false,
6
6
  "description": "Befly - 管理后台功能组件",
@@ -8,87 +8,39 @@
8
8
  <div class="section-content">
9
9
  <div class="project-row-list">
10
10
  <div v-for="item in $Computed.productRows" :key="item.key" class="product-row">
11
- <div class="product-row-main">
12
- <div class="product-row-name">
13
- <span>{{ item.productName }}</span>
14
- <span v-if="item.productCode" class="product-row-code">({{ item.productCode }})</span>
15
- </div>
16
- <div class="product-row-online">
17
- <span>在线人数</span>
18
- <strong>{{ item.onlineCount }}</strong>
19
- </div>
11
+ <div class="metric-card">
12
+ <span class="metric-label">产品名称</span>
13
+ <strong class="metric-value-name">{{ item.productCode || item.productName }}</strong>
20
14
  </div>
21
15
 
22
- <div class="project-metrics">
23
- <div class="metric-card">
24
- <div class="metric-label">今日</div>
25
- <div class="metric-values">
26
- <div class="metric-value">
27
- <span>PV</span>
28
- <strong class="metric-value-pv">{{ item.today.pv }}</strong>
29
- </div>
30
- <div class="metric-value">
31
- <span>UV</span>
32
- <strong class="metric-value-uv">{{ item.today.uv }}</strong>
33
- </div>
34
- </div>
35
- </div>
36
-
37
- <div class="metric-card">
38
- <div class="metric-label">昨日</div>
39
- <div class="metric-values">
40
- <div class="metric-value">
41
- <span>PV</span>
42
- <strong class="metric-value-pv">{{ item.yesterday.pv }}</strong>
43
- </div>
44
- <div class="metric-value">
45
- <span>UV</span>
46
- <strong class="metric-value-uv">{{ item.yesterday.uv }}</strong>
47
- </div>
48
- </div>
49
- </div>
50
-
51
- <div class="metric-card">
52
- <div class="metric-label">前天</div>
53
- <div class="metric-values">
54
- <div class="metric-value">
55
- <span>PV</span>
56
- <strong class="metric-value-pv">{{ item.dayBeforeYesterday.pv }}</strong>
57
- </div>
58
- <div class="metric-value">
59
- <span>UV</span>
60
- <strong class="metric-value-uv">{{ item.dayBeforeYesterday.uv }}</strong>
61
- </div>
62
- </div>
63
- </div>
64
-
65
- <div class="metric-card">
66
- <div class="metric-label">近30天</div>
67
- <div class="metric-values">
68
- <div class="metric-value">
69
- <span>PV</span>
70
- <strong class="metric-value-pv">{{ item.recent30.pv }}</strong>
71
- </div>
72
- <div class="metric-value">
73
- <span>UV</span>
74
- <strong class="metric-value-uv">{{ item.recent30.uv }}</strong>
75
- </div>
76
- </div>
77
- </div>
78
-
79
- <div class="metric-card">
80
- <div class="metric-label">本月</div>
81
- <div class="metric-values">
82
- <div class="metric-value">
83
- <span>PV</span>
84
- <strong class="metric-value-pv">{{ item.month.pv }}</strong>
85
- </div>
86
- <div class="metric-value">
87
- <span>UV</span>
88
- <strong class="metric-value-uv">{{ item.month.uv }}</strong>
89
- </div>
90
- </div>
91
- </div>
16
+ <div class="metric-card">
17
+ <span class="metric-label">在线人数</span>
18
+ <strong class="metric-value-online">{{ item.onlineCount }}</strong>
19
+ </div>
20
+
21
+ <div class="metric-card">
22
+ <span class="metric-label">今日访客</span>
23
+ <strong class="metric-value-visitor">{{ item.today }}</strong>
24
+ </div>
25
+
26
+ <div class="metric-card">
27
+ <span class="metric-label">昨日访客</span>
28
+ <strong class="metric-value-visitor">{{ item.yesterday }}</strong>
29
+ </div>
30
+
31
+ <div class="metric-card">
32
+ <span class="metric-label">前天访客</span>
33
+ <strong class="metric-value-visitor">{{ item.dayBeforeYesterday }}</strong>
34
+ </div>
35
+
36
+ <div class="metric-card">
37
+ <span class="metric-label">近30天访客</span>
38
+ <strong class="metric-value-visitor">{{ item.recent30 }}</strong>
39
+ </div>
40
+
41
+ <div class="metric-card">
42
+ <span class="metric-label">本月访客</span>
43
+ <strong class="metric-value-visitor">{{ item.month }}</strong>
92
44
  </div>
93
45
  </div>
94
46
  </div>
@@ -108,128 +60,47 @@ const $Data = reactive({
108
60
  });
109
61
 
110
62
  const $Method = {
111
- getReportDateNumber: function (timestamp) {
112
- return Number(
113
- new Intl.DateTimeFormat("en-CA", {
114
- year: "numeric",
115
- month: "2-digit",
116
- day: "2-digit"
117
- })
118
- .format(timestamp)
119
- .replace(/[^0-9]/g, "")
120
- );
63
+ buildProjectRow: function (visitItem, onlineItem) {
64
+ return {
65
+ key: String(visitItem?.key || onlineItem?.key || visitItem?.productCode || onlineItem?.productCode || ""),
66
+ productName: String(visitItem?.productName || onlineItem?.productName || "-"),
67
+ productCode: String(visitItem?.productCode || onlineItem?.productCode || ""),
68
+ productVersion: String(visitItem?.productVersion || onlineItem?.productVersion || ""),
69
+ onlineCount: Number(onlineItem?.onlineCount || 0),
70
+ today: Number(visitItem?.today || 0),
71
+ yesterday: Number(visitItem?.yesterday || 0),
72
+ dayBeforeYesterday: Number(visitItem?.dayBeforeYesterday || 0),
73
+ recent30: Number(visitItem?.recent30 || 0),
74
+ month: Number(visitItem?.month || 0)
75
+ };
121
76
  },
122
- buildRecentReportDateList: function (limit) {
123
- const list = [];
77
+ mapProjectRows: function (infoStatsData, onlineStatsData) {
78
+ const onlineMap = new Map();
124
79
 
125
- for (let index = limit - 1; index >= 0; index -= 1) {
126
- list.push($Method.getReportDateNumber(Date.now() - index * 24 * 60 * 60 * 1000));
80
+ for (const item of onlineStatsData?.products || []) {
81
+ onlineMap.set(String(item?.productCode || item?.key || ""), item);
127
82
  }
128
83
 
129
- return list;
130
- },
131
- buildFilledTrendDays: function (list, limit) {
132
- const dateList = $Method.buildRecentReportDateList(limit);
133
- const dataMap = new Map();
134
-
135
- for (const item of list) {
136
- dataMap.set(Number(item?.reportDate || 0), {
137
- reportDate: Number(item?.reportDate || 0),
138
- pv: Number(item?.pv || 0),
139
- uv: Number(item?.uv || 0)
140
- });
141
- }
84
+ const rows = [];
142
85
 
143
- return dateList.map((reportDate) => {
144
- return (
145
- dataMap.get(reportDate) || {
146
- reportDate: reportDate,
147
- pv: 0,
148
- uv: 0
149
- }
150
- );
151
- });
152
- },
153
- sumTrendRange: function (list) {
154
- let pv = 0;
155
- let uv = 0;
156
-
157
- for (const item of list) {
158
- pv += Number(item?.pv || 0);
159
- uv += Number(item?.uv || 0);
86
+ for (const item of infoStatsData?.products || []) {
87
+ const productCode = String(item?.productCode || item?.key || "");
88
+ rows.push($Method.buildProjectRow(item, onlineMap.get(productCode)));
89
+ onlineMap.delete(productCode);
160
90
  }
161
91
 
162
- return {
163
- pv: pv,
164
- uv: uv
165
- };
166
- },
167
- buildPeriodStats: function (days) {
168
- const list = $Method.buildFilledTrendDays(days, 30);
169
- const yesterday = list[list.length - 2] || { pv: 0, uv: 0 };
170
- const dayBeforeYesterday = list[list.length - 3] || { pv: 0, uv: 0 };
171
-
172
- return {
173
- yesterday: {
174
- pv: Number(yesterday.pv || 0),
175
- uv: Number(yesterday.uv || 0)
176
- },
177
- dayBeforeYesterday: {
178
- pv: Number(dayBeforeYesterday.pv || 0),
179
- uv: Number(dayBeforeYesterday.uv || 0)
180
- },
181
- recent30: $Method.sumTrendRange(list)
182
- };
183
- },
184
- buildProjectRow: function (rawItem, statsItem) {
185
- const periodStats = $Method.buildPeriodStats(Array.isArray(statsItem?.days) ? statsItem.days : []);
186
-
187
- return {
188
- key: String(rawItem?.key || ""),
189
- productName: String(rawItem?.productName || "-"),
190
- productCode: String(rawItem?.productCode || ""),
191
- productVersion: String(rawItem?.productVersion || ""),
192
- onlineCount: Number(statsItem?.onlineCount || 0),
193
- today: {
194
- pv: Number(statsItem?.today?.pv || 0),
195
- uv: Number(statsItem?.today?.uv || 0)
196
- },
197
- yesterday: periodStats.yesterday,
198
- dayBeforeYesterday: periodStats.dayBeforeYesterday,
199
- recent30: periodStats.recent30,
200
- month: {
201
- pv: Number(statsItem?.month?.pv || 0),
202
- uv: Number(statsItem?.month?.uv || 0)
203
- }
204
- };
205
- },
206
- mapProjectRows: function (onlineStatsData) {
207
- if (!Array.isArray(onlineStatsData?.products)) {
208
- return [];
92
+ for (const item of onlineMap.values()) {
93
+ rows.push($Method.buildProjectRow(null, item));
209
94
  }
210
95
 
211
- return onlineStatsData.products
212
- .map((item) => {
213
- const productName = String(item?.productName || "-");
214
-
215
- return $Method.buildProjectRow(
216
- {
217
- key: String(item?.key || productName),
218
- productName: productName,
219
- productCode: String(item?.productCode || ""),
220
- productVersion: String(item?.productVersion || "")
221
- },
222
- item
223
- );
224
- })
225
- .filter((item) => {
226
- return Boolean(item.key);
227
- });
96
+ return rows.filter((item) => {
97
+ return Boolean(item.key);
98
+ });
228
99
  },
229
100
  fetchData: async function () {
230
101
  try {
231
- const onlineStatsRes = await $Http("/core/tongJi/onlineStats", {}, [""]);
232
- $Data.productState.options = $Method.mapProjectRows(onlineStatsRes.data);
102
+ const [infoStatsRes, onlineStatsRes] = await Promise.all([$Http("/core/tongJi/infoStats", {}, [""]), $Http("/core/tongJi/onlineStats", {}, [""])]);
103
+ $Data.productState.options = $Method.mapProjectRows(infoStatsRes.data, onlineStatsRes.data);
233
104
  } catch (_error) {
234
105
  // 静默失败:不阻断页面展示
235
106
  }
@@ -256,75 +127,24 @@ $Method.fetchData();
256
127
 
257
128
  .product-row {
258
129
  display: grid;
259
- grid-template-columns: minmax(180px, 0.75fr) minmax(0, 2.25fr);
130
+ grid-template-columns: repeat(7, minmax(0, 1fr));
260
131
  gap: 16px;
261
132
  align-items: stretch;
262
133
  }
263
134
 
264
- .product-row-main {
265
- display: flex;
266
- flex-direction: column;
267
- align-items: flex-start;
268
- gap: 10px;
269
- min-width: 0;
270
- text-align: left;
271
-
272
- @include dashboardCard.dashboardCardShell();
273
-
274
- justify-content: flex-start;
275
- }
276
-
277
- .product-row-name {
278
- display: flex;
279
- align-items: baseline;
280
- flex-wrap: wrap;
281
- gap: 4px;
282
- font-size: 14px;
283
- font-weight: 700;
284
- line-height: 1.2;
285
- color: var(--text-primary);
286
- }
287
-
288
- .product-row-code {
289
- font-size: 12px;
290
- font-weight: 600;
291
- color: var(--text-secondary);
292
- }
293
-
294
135
  .metric-label {
295
- font-size: 14px;
296
- font-weight: 700;
297
- line-height: 1.2;
298
- color: var(--text-primary);
136
+ @include dashboardCard.dashboardCardLabel();
299
137
  }
300
138
 
301
- .metric-value span,
302
- .product-row-online span {
303
- font-size: 12px;
304
- font-weight: 500;
305
- line-height: 1.2;
306
- color: var(--text-secondary);
307
- }
308
-
309
- .product-row-online {
310
- display: flex;
311
- flex-direction: column;
312
- align-items: flex-start;
313
- gap: 6px;
314
- white-space: nowrap;
315
- text-align: left;
316
- }
317
-
318
- .product-row-online strong {
319
- @include dashboardCard.dashboardCardNumericValue(#d54941);
139
+ .metric-value-online {
140
+ @include dashboardCard.dashboardCardNumericValue(var(--error-color));
320
141
 
321
142
  color: var(--error-color);
322
143
  }
323
144
 
324
- .project-metrics {
325
- display: grid;
326
- grid-template-columns: repeat(5, minmax(0, 1fr));
327
- gap: 16px;
145
+ .metric-value-name {
146
+ @include dashboardCard.dashboardCardMainValue();
147
+
328
148
  text-align: left;
329
149
  }
330
150
 
@@ -332,60 +152,32 @@ $Method.fetchData();
332
152
  display: flex;
333
153
  flex-direction: column;
334
154
  align-items: flex-start;
335
- gap: 8px;
336
155
  text-align: left;
337
156
 
338
157
  @include dashboardCard.dashboardCardShell();
339
158
 
159
+ gap: 8px;
340
160
  justify-content: flex-start;
341
161
  min-height: 100%;
342
162
  padding: 12px;
343
163
  }
344
164
 
345
- .metric-values {
346
- display: grid;
347
- grid-template-columns: repeat(2, minmax(0, 1fr));
348
- gap: 10px;
349
- align-items: end;
350
- width: 100%;
351
- }
352
-
353
- .metric-value {
354
- display: flex;
355
- flex-direction: column;
356
- align-items: flex-start;
357
- justify-content: flex-end;
358
- gap: 6px;
359
- min-width: 0;
360
- }
361
-
362
- .metric-value strong {
363
- @include dashboardCard.dashboardCardNumericValue();
165
+ .metric-value-visitor {
166
+ @include dashboardCard.dashboardCardNumericValue(var(--success-color));
364
167
 
168
+ color: var(--success-color);
365
169
  text-align: left;
366
170
  white-space: nowrap;
367
171
  }
368
172
 
369
- .metric-value-pv {
370
- color: #0052d9;
371
- }
372
-
373
- .metric-value-uv {
374
- color: #2ba471;
375
- }
376
-
377
173
  @media (max-width: 1280px) {
378
- .project-metrics {
379
- grid-template-columns: repeat(2, minmax(0, 1fr));
380
- }
381
-
382
174
  .product-row {
383
- grid-template-columns: 1fr;
175
+ grid-template-columns: repeat(2, minmax(0, 1fr));
384
176
  }
385
177
  }
386
178
 
387
179
  @media (max-width: 720px) {
388
- .project-metrics {
180
+ .product-row {
389
181
  grid-template-columns: 1fr;
390
182
  }
391
183
  }
@@ -40,6 +40,28 @@
40
40
  </div>
41
41
  </t-col>
42
42
 
43
+ <t-col :span="2" class="dashboard-card-col">
44
+ <div class="dashboard-card">
45
+ <div class="dashboard-card-content">
46
+ <div class="dashboard-card-label">今日访客</div>
47
+ <div class="dashboard-card-value-row">
48
+ <div class="dashboard-card-main dashboard-card-main--metric dashboard-card-main--success">{{ $Computed.visitTodayCount }}</div>
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>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </t-col>
64
+
43
65
  <t-col :span="2" class="dashboard-card-col">
44
66
  <div class="dashboard-card">
45
67
  <div class="dashboard-card-content">
@@ -66,18 +88,30 @@ const $Data = reactive({
66
88
  apiCount: 0,
67
89
  roleCount: 0
68
90
  },
91
+ infoStats: {
92
+ today: {
93
+ count: 0
94
+ }
95
+ },
96
+ onlineStats: {
97
+ onlineCount: 0
98
+ },
69
99
  errorStats: {
70
- trend: []
100
+ today: {
101
+ count: 0
102
+ }
71
103
  }
72
104
  });
73
105
 
74
106
  const $Method = {
75
107
  fetchData: async function () {
76
108
  try {
77
- const [overviewRes, errorStatsRes] = await Promise.all([$Http("/core/dashboard/systemOverview", {}, [""]), $Http("/core/tongJi/errorStats", {}, [""])]);
109
+ const [overviewRes, infoStatsRes, onlineStatsRes, errorStatsRes] = await Promise.all([$Http("/core/dashboard/systemOverview", {}, [""]), $Http("/core/tongJi/infoStats", {}, [""]), $Http("/core/tongJi/onlineStats", {}, [""]), $Http("/core/tongJi/errorStats", {}, [""])]);
78
110
 
79
111
  Object.assign($Data.permissionStats, overviewRes.data);
80
- $Data.errorStats.trend = Array.isArray(errorStatsRes.data?.trend) ? errorStatsRes.data.trend : [];
112
+ $Data.infoStats.today.count = Number(infoStatsRes.data?.today?.count || 0);
113
+ $Data.onlineStats.onlineCount = Number(onlineStatsRes.data?.onlineCount || 0);
114
+ $Data.errorStats.today.count = Number(errorStatsRes.data?.today?.count || 0);
81
115
  } catch (_error) {
82
116
  // 静默失败:不阻断页面展示
83
117
  }
@@ -85,14 +119,14 @@ const $Method = {
85
119
  };
86
120
 
87
121
  const $Computed = reactive({
122
+ visitTodayCount: computed(() => {
123
+ return $Data.infoStats.today.count;
124
+ }),
125
+ onlineCount: computed(() => {
126
+ return $Data.onlineStats.onlineCount;
127
+ }),
88
128
  errorTodayCount: computed(() => {
89
- let total = 0;
90
-
91
- for (const item of $Data.errorStats.trend) {
92
- total += Number(item.count || 0);
93
- }
94
-
95
- return total;
129
+ return $Data.errorStats.today.count;
96
130
  })
97
131
  });
98
132