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
@@ -3,3 +3,4 @@
3
3
  <RouterView />
4
4
  </div>
5
5
  </template>
6
+ <script setup></script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly-admin-ui",
3
- "version": "1.48.0",
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.0.0",
54
- "vue": ">=3.0.0",
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">今日访客</div>
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.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>
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
- infoStats: {
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, infoStatsRes, onlineStatsRes, errorStatsRes] = await Promise.all([$Http("/core/dashboard/systemOverview", {}, [""]), $Http("/core/tongJi/infoStats", {}, [""]), $Http("/core/tongJi/onlineStats", {}, [""]), $Http("/core/tongJi/errorStats", {}, [""])]);
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.infoStats.today.count = Number(infoStatsRes.data?.today?.count || 0);
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
- visitTodayCount: computed(() => {
123
- return $Data.infoStats.today.count;
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
- <ChartIcon />
6
- <h2>项目统计</h2>
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">今日访客</span>
25
- <strong class="metric-value-visitor">{{ item.today }}</strong>
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
- <t-col :xs="12" :sm="6" :md="4" :lg="2" class="metric-col">
37
- <div class="metric-card">
38
- <span class="metric-label">前天访客</span>
39
- <strong class="metric-value-visitor">{{ item.dayBeforeYesterday }}</strong>
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
- <t-col :xs="12" :sm="6" :md="4" :lg="2" class="metric-col">
44
- <div class="metric-card">
45
- <span class="metric-label">近30天访客</span>
46
- <strong class="metric-value-visitor">{{ item.recent30 }}</strong>
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
- </t-col>
49
-
50
- <t-col :xs="12" :sm="6" :md="4" :lg="2" class="metric-col">
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 (visitItem, onlineItem) {
84
+ buildProjectRow: function (item) {
79
85
  return {
80
- key: String(visitItem?.key || onlineItem?.key || visitItem?.productCode || onlineItem?.productCode || ""),
81
- productName: String(visitItem?.productName || onlineItem?.productName || "-"),
82
- productCode: String(visitItem?.productCode || onlineItem?.productCode || ""),
83
-
84
- onlineCount: Number(onlineItem?.onlineCount || 0),
85
- today: Number(visitItem?.today || 0),
86
- yesterday: Number(visitItem?.yesterday || 0),
87
- dayBeforeYesterday: Number(visitItem?.dayBeforeYesterday || 0),
88
- recent30: Number(visitItem?.recent30 || 0),
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 [infoStatsRes, onlineStatsRes] = await Promise.all([$Http("/core/tongJi/infoStats", {}, [""]), $Http("/core/tongJi/onlineStats", {}, [""])]);
128
- $Data.productState.options = $Method.mapProjectRows(infoStatsRes.data, onlineStatsRes.data);
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-online {
187
- @include dashboardCard.dashboardCardNumericValue(var(--error-color));
187
+ .metric-value-daily {
188
+ @include dashboardCard.dashboardCardNumericValue(var(--success-color));
188
189
 
189
- color: var(--error-color);
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
- .metric-value-visitor {
206
- @include dashboardCard.dashboardCardNumericValue(var(--success-color));
208
+ // 卡片布局
209
+ .project-card-grid {
210
+ min-width: 0;
211
+ }
207
212
 
208
- color: var(--success-color);
209
- text-align: left;
210
- white-space: nowrap;
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>