befly-admin-ui 1.10.1 → 1.10.2
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/components/detailPanel.vue +52 -54
- package/components/pageDialog.vue +131 -117
- package/components/pageTableDetail.vue +381 -402
- package/layouts/default.vue +212 -215
- package/package.json +2 -2
- package/views/config/dict/components/edit.vue +83 -60
- package/views/config/dict/index.vue +51 -55
- package/views/config/dictType/components/edit.vue +81 -57
- package/views/config/dictType/index.vue +37 -39
- package/views/config/system/components/edit.vue +97 -86
- package/views/config/system/index.vue +35 -37
- package/views/index/components/addonList.vue +14 -11
- package/views/index/components/environmentInfo.vue +25 -22
- package/views/index/components/operationLogs.vue +20 -16
- package/views/index/components/performanceMetrics.vue +24 -21
- package/views/index/components/serviceStatus.vue +26 -22
- package/views/index/components/systemNotifications.vue +24 -19
- package/views/index/components/systemOverview.vue +368 -385
- package/views/index/components/systemResources.vue +22 -19
- package/views/index/components/userInfo.vue +56 -56
- package/views/log/email/index.vue +96 -85
- package/views/log/error/index.vue +111 -117
- package/views/log/login/index.vue +15 -13
- package/views/log/operate/index.vue +31 -31
- package/views/login_1/index.vue +50 -32
- package/views/people/admin/components/edit.vue +1 -1
- package/views/people/admin/index.vue +27 -28
- package/views/permission/api/index.vue +44 -45
- package/views/permission/menu/index.vue +35 -35
- package/views/permission/role/components/api.vue +143 -144
- package/views/permission/role/components/edit.vue +74 -52
- package/views/permission/role/components/menu.vue +119 -121
- package/views/permission/role/index.vue +47 -49
- package/views/resource/gallery/index.vue +77 -78
|
@@ -9,35 +9,35 @@
|
|
|
9
9
|
<div class="summary-grid">
|
|
10
10
|
<div class="summary-card overview-card-small stat-primary">
|
|
11
11
|
<div class="summary-content">
|
|
12
|
-
<div class="summary-value">{{ permissionStats.menuCount }}</div>
|
|
12
|
+
<div class="summary-value">{{ $Data.permissionStats.menuCount }}</div>
|
|
13
13
|
<div class="summary-label">菜单总数</div>
|
|
14
14
|
</div>
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
17
|
<div class="summary-card overview-card-small stat-success">
|
|
18
18
|
<div class="summary-content">
|
|
19
|
-
<div class="summary-value">{{ permissionStats.apiCount }}</div>
|
|
19
|
+
<div class="summary-value">{{ $Data.permissionStats.apiCount }}</div>
|
|
20
20
|
<div class="summary-label">接口总数</div>
|
|
21
21
|
</div>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
24
|
<div class="summary-card overview-card-small stat-warning">
|
|
25
25
|
<div class="summary-content">
|
|
26
|
-
<div class="summary-value">{{ permissionStats.roleCount }}</div>
|
|
26
|
+
<div class="summary-value">{{ $Data.permissionStats.roleCount }}</div>
|
|
27
27
|
<div class="summary-label">角色总数</div>
|
|
28
28
|
</div>
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
31
|
<div class="summary-card overview-card-small stat-danger">
|
|
32
32
|
<div class="summary-content">
|
|
33
|
-
<div class="summary-value">{{ realtimeStats.onlineCount }}</div>
|
|
33
|
+
<div class="summary-value">{{ $Data.realtimeStats.onlineCount }}</div>
|
|
34
34
|
<div class="summary-label">当前在线</div>
|
|
35
35
|
</div>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
38
|
<div class="summary-card overview-card-small stat-critical">
|
|
39
39
|
<div class="summary-content">
|
|
40
|
-
<div class="summary-value">{{ errorTodayCount }}</div>
|
|
40
|
+
<div class="summary-value">{{ $Computed.errorTodayCount }}</div>
|
|
41
41
|
<div class="summary-label">今日错误</div>
|
|
42
42
|
</div>
|
|
43
43
|
</div>
|
|
@@ -50,14 +50,14 @@
|
|
|
50
50
|
</div>
|
|
51
51
|
|
|
52
52
|
<div class="product-select-wrap">
|
|
53
|
-
<TSelect :model-value="productState.selectedKey" class="product-select" placeholder="请选择产品" @change="selectProduct">
|
|
54
|
-
<TOption v-for="item in productOptions" :key="item.key" :value="item.key" :label="item.productName" />
|
|
53
|
+
<TSelect :model-value="$Data.productState.selectedKey" class="product-select" placeholder="请选择产品" @change="$Method.selectProduct">
|
|
54
|
+
<TOption v-for="item in $Computed.productOptions" :key="item.key" :value="item.key" :label="item.productName" />
|
|
55
55
|
</TSelect>
|
|
56
56
|
</div>
|
|
57
57
|
</div>
|
|
58
58
|
|
|
59
59
|
<div class="product-metrics-grid">
|
|
60
|
-
<div v-for="item in productPeriodCards" :key="item.key" class="metric-box metric-box-period">
|
|
60
|
+
<div v-for="item in $Computed.productPeriodCards" :key="item.key" class="metric-box metric-box-period">
|
|
61
61
|
<div class="metric-period-label">{{ item.label }}</div>
|
|
62
62
|
<div class="metric-pair-row">
|
|
63
63
|
<span class="metric-pair-name">PV</span>
|
|
@@ -84,20 +84,20 @@
|
|
|
84
84
|
</div>
|
|
85
85
|
</div>
|
|
86
86
|
|
|
87
|
-
<div v-if="selectedDaysHasData" class="trend-content">
|
|
87
|
+
<div v-if="$Computed.selectedDaysHasData" class="trend-content">
|
|
88
88
|
<div class="trend-bars">
|
|
89
|
-
<div v-for="item in selectedDaysBars" :key="item.reportDate" class="trend-bar-group">
|
|
89
|
+
<div v-for="item in $Computed.selectedDaysBars" :key="item.reportDate" class="trend-bar-group">
|
|
90
90
|
<div class="trend-bar-stack">
|
|
91
|
-
<div class="trend-bar trend-bar-pv" :style="{ height: item.pvHeight }" :title="`${formatReportDate(item.reportDate)} PV ${item.pv}`"></div>
|
|
92
|
-
<div class="trend-bar trend-bar-uv" :style="{ height: item.uvHeight }" :title="`${formatReportDate(item.reportDate)} UV ${item.uv}`"></div>
|
|
91
|
+
<div class="trend-bar trend-bar-pv" :style="{ height: item.pvHeight }" :title="`${$Method.formatReportDate(item.reportDate)} PV ${item.pv}`"></div>
|
|
92
|
+
<div class="trend-bar trend-bar-uv" :style="{ height: item.uvHeight }" :title="`${$Method.formatReportDate(item.reportDate)} UV ${item.uv}`"></div>
|
|
93
93
|
</div>
|
|
94
|
-
<div class="trend-bar-label">{{ formatReportDate(item.reportDate) }}</div>
|
|
94
|
+
<div class="trend-bar-label">{{ $Method.formatReportDate(item.reportDate) }}</div>
|
|
95
95
|
</div>
|
|
96
96
|
</div>
|
|
97
97
|
|
|
98
98
|
<div class="trend-briefs trend-briefs-wide">
|
|
99
|
-
<div v-for="item in selectedDaysTrendPreview" :key="item.reportDate" class="trend-brief-item">
|
|
100
|
-
<div class="trend-date">{{ formatReportDate(item.reportDate) }}</div>
|
|
99
|
+
<div v-for="item in $Computed.selectedDaysTrendPreview" :key="item.reportDate" class="trend-brief-item">
|
|
100
|
+
<div class="trend-date">{{ $Method.formatReportDate(item.reportDate) }}</div>
|
|
101
101
|
<div class="trend-values">
|
|
102
102
|
<span class="trend-value pv">{{ item.pv }}</span>
|
|
103
103
|
<span class="trend-value uv">{{ item.uv }}</span>
|
|
@@ -113,9 +113,9 @@
|
|
|
113
113
|
<div class="ua-grid">
|
|
114
114
|
<div class="ua-card overview-card-small">
|
|
115
115
|
<div class="ua-card-title">设备类型</div>
|
|
116
|
-
<div v-if="uaStats.deviceTypes.length > 0" class="ua-list">
|
|
117
|
-
<div v-for="item in uaStats.deviceTypes.slice(0, 5)" :key="`device-type-${item.name}`" class="ua-list-item">
|
|
118
|
-
<span class="ua-list-name">{{ formatDeviceType(item.name) }}</span>
|
|
116
|
+
<div v-if="$Data.uaStats.deviceTypes.length > 0" class="ua-list">
|
|
117
|
+
<div v-for="item in $Data.uaStats.deviceTypes.slice(0, 5)" :key="`device-type-${item.name}`" class="ua-list-item">
|
|
118
|
+
<span class="ua-list-name">{{ $Method.formatDeviceType(item.name) }}</span>
|
|
119
119
|
<span class="ua-list-count">{{ item.count }}</span>
|
|
120
120
|
</div>
|
|
121
121
|
</div>
|
|
@@ -124,8 +124,8 @@
|
|
|
124
124
|
|
|
125
125
|
<div class="ua-card overview-card-small">
|
|
126
126
|
<div class="ua-card-title">浏览器分布</div>
|
|
127
|
-
<div v-if="uaStats.browsers.length > 0" class="ua-list">
|
|
128
|
-
<div v-for="item in uaStats.browsers.slice(0, 5)" :key="`browser-${item.name}`" class="ua-list-item">
|
|
127
|
+
<div v-if="$Data.uaStats.browsers.length > 0" class="ua-list">
|
|
128
|
+
<div v-for="item in $Data.uaStats.browsers.slice(0, 5)" :key="`browser-${item.name}`" class="ua-list-item">
|
|
129
129
|
<span class="ua-list-name">{{ item.name }}</span>
|
|
130
130
|
<span class="ua-list-count">{{ item.count }}</span>
|
|
131
131
|
</div>
|
|
@@ -135,8 +135,8 @@
|
|
|
135
135
|
|
|
136
136
|
<div class="ua-card overview-card-small">
|
|
137
137
|
<div class="ua-card-title">操作系统分布</div>
|
|
138
|
-
<div v-if="uaStats.osList.length > 0" class="ua-list">
|
|
139
|
-
<div v-for="item in uaStats.osList.slice(0, 5)" :key="`os-${item.name}`" class="ua-list-item">
|
|
138
|
+
<div v-if="$Data.uaStats.osList.length > 0" class="ua-list">
|
|
139
|
+
<div v-for="item in $Data.uaStats.osList.slice(0, 5)" :key="`os-${item.name}`" class="ua-list-item">
|
|
140
140
|
<span class="ua-list-name">{{ item.name }}</span>
|
|
141
141
|
<span class="ua-list-count">{{ item.count }}</span>
|
|
142
142
|
</div>
|
|
@@ -146,8 +146,8 @@
|
|
|
146
146
|
|
|
147
147
|
<div class="ua-card overview-card-small">
|
|
148
148
|
<div class="ua-card-title">设备厂商</div>
|
|
149
|
-
<div v-if="uaStats.deviceVendors.length > 0" class="ua-list">
|
|
150
|
-
<div v-for="item in uaStats.deviceVendors.slice(0, 5)" :key="`vendor-${item.name}`" class="ua-list-item">
|
|
149
|
+
<div v-if="$Data.uaStats.deviceVendors.length > 0" class="ua-list">
|
|
150
|
+
<div v-for="item in $Data.uaStats.deviceVendors.slice(0, 5)" :key="`vendor-${item.name}`" class="ua-list-item">
|
|
151
151
|
<span class="ua-list-name">{{ item.name }}</span>
|
|
152
152
|
<span class="ua-list-count">{{ item.count }}</span>
|
|
153
153
|
</div>
|
|
@@ -161,24 +161,24 @@
|
|
|
161
161
|
<div class="ua-meta-group">
|
|
162
162
|
<div class="ua-meta-block">
|
|
163
163
|
<div class="ua-meta-title">设备型号</div>
|
|
164
|
-
<div v-if="uaStats.deviceModels.length > 0" class="ua-chip-list">
|
|
165
|
-
<span v-for="item in uaStats.deviceModels.slice(0, 6)" :key="`model-${item.name}`" class="ua-chip">{{ item.name }} · {{ item.count }}</span>
|
|
164
|
+
<div v-if="$Data.uaStats.deviceModels.length > 0" class="ua-chip-list">
|
|
165
|
+
<span v-for="item in $Data.uaStats.deviceModels.slice(0, 6)" :key="`model-${item.name}`" class="ua-chip">{{ item.name }} · {{ item.count }}</span>
|
|
166
166
|
</div>
|
|
167
167
|
<div v-else class="ua-empty ua-empty-inline">暂无设备型号数据</div>
|
|
168
168
|
</div>
|
|
169
169
|
|
|
170
170
|
<div class="ua-meta-block">
|
|
171
171
|
<div class="ua-meta-title">渲染引擎</div>
|
|
172
|
-
<div v-if="uaStats.engines.length > 0" class="ua-chip-list">
|
|
173
|
-
<span v-for="item in uaStats.engines.slice(0, 6)" :key="`engine-${item.name}`" class="ua-chip">{{ item.name }} · {{ item.count }}</span>
|
|
172
|
+
<div v-if="$Data.uaStats.engines.length > 0" class="ua-chip-list">
|
|
173
|
+
<span v-for="item in $Data.uaStats.engines.slice(0, 6)" :key="`engine-${item.name}`" class="ua-chip">{{ item.name }} · {{ item.count }}</span>
|
|
174
174
|
</div>
|
|
175
175
|
<div v-else class="ua-empty ua-empty-inline">暂无渲染引擎数据</div>
|
|
176
176
|
</div>
|
|
177
177
|
|
|
178
178
|
<div class="ua-meta-block">
|
|
179
179
|
<div class="ua-meta-title">CPU 架构</div>
|
|
180
|
-
<div v-if="uaStats.cpuArchitectures.length > 0" class="ua-chip-list">
|
|
181
|
-
<span v-for="item in uaStats.cpuArchitectures.slice(0, 6)" :key="`cpu-${item.name}`" class="ua-chip">{{ item.name }} · {{ item.count }}</span>
|
|
180
|
+
<div v-if="$Data.uaStats.cpuArchitectures.length > 0" class="ua-chip-list">
|
|
181
|
+
<span v-for="item in $Data.uaStats.cpuArchitectures.slice(0, 6)" :key="`cpu-${item.name}`" class="ua-chip">{{ item.name }} · {{ item.count }}</span>
|
|
182
182
|
</div>
|
|
183
183
|
<div v-else class="ua-empty ua-empty-inline">暂无 CPU 架构数据</div>
|
|
184
184
|
</div>
|
|
@@ -194,409 +194,392 @@ import { computed, reactive } from "vue";
|
|
|
194
194
|
import { Option as TOption, Select as TSelect } from "tdesign-vue-next";
|
|
195
195
|
import { InfoCircleIcon, TrendingUpIcon } from "tdesign-icons-vue-next";
|
|
196
196
|
|
|
197
|
-
import { $Config } from "@/plugins/config";
|
|
198
|
-
import { $Http } from "@/plugins/http";
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
deviceVendors: [],
|
|
235
|
-
deviceModels: [],
|
|
236
|
-
engines: [],
|
|
237
|
-
cpuArchitectures: []
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
const errorTodayTrend = computed(() => sortTrendList(errorStats.trend, "bucketTime"));
|
|
241
|
-
|
|
242
|
-
const errorTodayCount = computed(() => {
|
|
243
|
-
let total = 0;
|
|
244
|
-
|
|
245
|
-
for (const item of errorTodayTrend.value) {
|
|
246
|
-
total += Number(item.count || 0);
|
|
197
|
+
import { $Config } from "@/plugins/config.js";
|
|
198
|
+
import { $Http } from "@/plugins/http.js";
|
|
199
|
+
|
|
200
|
+
const $Data = reactive({
|
|
201
|
+
permissionStats: {
|
|
202
|
+
menuCount: 0,
|
|
203
|
+
apiCount: 0,
|
|
204
|
+
roleCount: 0
|
|
205
|
+
},
|
|
206
|
+
realtimeStats: {
|
|
207
|
+
onlineCount: 0,
|
|
208
|
+
todayPv: 0,
|
|
209
|
+
todayUv: 0,
|
|
210
|
+
days: []
|
|
211
|
+
},
|
|
212
|
+
errorStats: {
|
|
213
|
+
trend: []
|
|
214
|
+
},
|
|
215
|
+
productInfo: {
|
|
216
|
+
productName: String($Config.productName || "-"),
|
|
217
|
+
productCode: String($Config.productCode || "-"),
|
|
218
|
+
productVersion: String($Config.productVersion || "-")
|
|
219
|
+
},
|
|
220
|
+
productState: {
|
|
221
|
+
selectedKey: "",
|
|
222
|
+
options: []
|
|
223
|
+
},
|
|
224
|
+
uaStats: {
|
|
225
|
+
deviceTypes: [],
|
|
226
|
+
browsers: [],
|
|
227
|
+
browserVersions: [],
|
|
228
|
+
osList: [],
|
|
229
|
+
osVersions: [],
|
|
230
|
+
deviceVendors: [],
|
|
231
|
+
deviceModels: [],
|
|
232
|
+
engines: [],
|
|
233
|
+
cpuArchitectures: []
|
|
247
234
|
}
|
|
248
|
-
|
|
249
|
-
return total;
|
|
250
235
|
});
|
|
251
236
|
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
237
|
+
const $Method = {
|
|
238
|
+
buildFallbackProduct() {
|
|
239
|
+
const days = $Method.sortTrendList($Data.realtimeStats.days, "reportDate");
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
key: "all-products",
|
|
243
|
+
productName: "全部产品",
|
|
244
|
+
productCode: $Data.productInfo.productCode,
|
|
245
|
+
productVersion: $Data.productInfo.productVersion,
|
|
246
|
+
today: {
|
|
247
|
+
pv: Number($Data.realtimeStats.todayPv || 0),
|
|
248
|
+
uv: Number($Data.realtimeStats.todayUv || 0)
|
|
249
|
+
},
|
|
250
|
+
days: days,
|
|
251
|
+
totalPv: $Method.sumTrendField(days, "pv"),
|
|
252
|
+
totalUv: $Method.sumTrendField(days, "uv")
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
getReportDateNumber(timestamp) {
|
|
256
|
+
return Number(
|
|
257
|
+
new Intl.DateTimeFormat("en-CA", {
|
|
258
|
+
year: "numeric",
|
|
259
|
+
month: "2-digit",
|
|
260
|
+
day: "2-digit"
|
|
261
|
+
})
|
|
262
|
+
.format(timestamp)
|
|
263
|
+
.replace(/[^0-9]/g, "")
|
|
264
|
+
);
|
|
265
|
+
},
|
|
266
|
+
buildRecentReportDateList(limit) {
|
|
267
|
+
const list = [];
|
|
259
268
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return -1;
|
|
269
|
+
for (let i = limit - 1; i >= 0; i--) {
|
|
270
|
+
list.push($Method.getReportDateNumber(Date.now() - i * 24 * 60 * 60 * 1000));
|
|
263
271
|
}
|
|
264
272
|
|
|
265
|
-
|
|
266
|
-
|
|
273
|
+
return list;
|
|
274
|
+
},
|
|
275
|
+
buildFilledTrendDays(list, limit) {
|
|
276
|
+
const dateList = $Method.buildRecentReportDateList(limit);
|
|
277
|
+
const dataMap = new Map();
|
|
278
|
+
|
|
279
|
+
for (const item of list) {
|
|
280
|
+
dataMap.set(Number(item?.reportDate || 0), {
|
|
281
|
+
reportDate: Number(item?.reportDate || 0),
|
|
282
|
+
pv: Number(item?.pv || 0),
|
|
283
|
+
uv: Number(item?.uv || 0)
|
|
284
|
+
});
|
|
267
285
|
}
|
|
268
286
|
|
|
269
|
-
|
|
270
|
-
return
|
|
287
|
+
return dateList.map((reportDate) => {
|
|
288
|
+
return (
|
|
289
|
+
dataMap.get(reportDate) || {
|
|
290
|
+
reportDate: reportDate,
|
|
291
|
+
pv: 0,
|
|
292
|
+
uv: 0
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
},
|
|
297
|
+
sumTrendRange(list) {
|
|
298
|
+
let pv = 0;
|
|
299
|
+
let uv = 0;
|
|
300
|
+
|
|
301
|
+
for (const item of list) {
|
|
302
|
+
pv += Number(item?.pv || 0);
|
|
303
|
+
uv += Number(item?.uv || 0);
|
|
271
304
|
}
|
|
272
305
|
|
|
273
|
-
return
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const matched = list.find((item) => item.key === productState.selectedKey);
|
|
287
|
-
return matched || list[0];
|
|
288
|
-
});
|
|
306
|
+
return {
|
|
307
|
+
pv: pv,
|
|
308
|
+
uv: uv
|
|
309
|
+
};
|
|
310
|
+
},
|
|
311
|
+
ensureSelectedProduct() {
|
|
312
|
+
const list = $Computed.productOptions;
|
|
313
|
+
|
|
314
|
+
if (list.length === 0) {
|
|
315
|
+
$Data.productState.selectedKey = "";
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
289
318
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const selectedDaysTrendPreview = computed(() => buildTrendPreview(selectedDaysFilled.value, 10));
|
|
294
|
-
const selectedDaysBars = computed(() => buildTrendBars(selectedDaysFilled.value, getTrendMax(selectedDaysFilled.value)));
|
|
295
|
-
const productPeriodCards = computed(() => {
|
|
296
|
-
const list = selectedDaysFilled.value;
|
|
297
|
-
const today = list[list.length - 1] || { pv: 0, uv: 0 };
|
|
298
|
-
const yesterday = list[list.length - 2] || { pv: 0, uv: 0 };
|
|
299
|
-
const dayBeforeYesterday = list[list.length - 3] || { pv: 0, uv: 0 };
|
|
300
|
-
const recent7 = sumTrendRange(list.slice(-7));
|
|
301
|
-
const recent30 = sumTrendRange(list);
|
|
302
|
-
|
|
303
|
-
return [
|
|
304
|
-
{ key: "today", label: "今日", pv: today.pv, uv: today.uv },
|
|
305
|
-
{ key: "yesterday", label: "昨日", pv: yesterday.pv, uv: yesterday.uv },
|
|
306
|
-
{ key: "dayBeforeYesterday", label: "前日", pv: dayBeforeYesterday.pv, uv: dayBeforeYesterday.uv },
|
|
307
|
-
{ key: "recent7", label: "最近7天", pv: recent7.pv, uv: recent7.uv },
|
|
308
|
-
{ key: "recent30", label: "最近30天", pv: recent30.pv, uv: recent30.uv }
|
|
309
|
-
];
|
|
310
|
-
});
|
|
319
|
+
if (list.some((item) => item.key === $Data.productState.selectedKey)) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
311
322
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
days: days,
|
|
325
|
-
totalPv: sumTrendField(days, "pv"),
|
|
326
|
-
totalUv: sumTrendField(days, "uv")
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function getReportDateNumber(timestamp) {
|
|
331
|
-
return Number(
|
|
332
|
-
new Intl.DateTimeFormat("en-CA", {
|
|
333
|
-
year: "numeric",
|
|
334
|
-
month: "2-digit",
|
|
335
|
-
day: "2-digit"
|
|
336
|
-
})
|
|
337
|
-
.format(timestamp)
|
|
338
|
-
.replace(/[^0-9]/g, "")
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function buildRecentReportDateList(limit) {
|
|
343
|
-
const list = [];
|
|
344
|
-
|
|
345
|
-
for (let i = limit - 1; i >= 0; i--) {
|
|
346
|
-
list.push(getReportDateNumber(Date.now() - i * 24 * 60 * 60 * 1000));
|
|
347
|
-
}
|
|
323
|
+
const currentProduct = list.find((item) => item.productName === $Data.productInfo.productName);
|
|
324
|
+
$Data.productState.selectedKey = currentProduct?.key || "all-products";
|
|
325
|
+
},
|
|
326
|
+
selectProduct(productKey) {
|
|
327
|
+
$Data.productState.selectedKey = String(productKey || "");
|
|
328
|
+
},
|
|
329
|
+
sumTrendField(list, field) {
|
|
330
|
+
let total = 0;
|
|
331
|
+
|
|
332
|
+
for (const item of list) {
|
|
333
|
+
total += Number(item?.[field] || 0);
|
|
334
|
+
}
|
|
348
335
|
|
|
349
|
-
|
|
350
|
-
}
|
|
336
|
+
return total;
|
|
337
|
+
},
|
|
338
|
+
sortTrendList(list, keyField) {
|
|
339
|
+
const result = Array.isArray(list) ? list.slice() : [];
|
|
340
|
+
|
|
341
|
+
result.sort((a, b) => Number(a?.[keyField] || 0) - Number(b?.[keyField] || 0));
|
|
342
|
+
return result;
|
|
343
|
+
},
|
|
344
|
+
buildTrendPreview(list, maxCount) {
|
|
345
|
+
if (list.length <= maxCount) {
|
|
346
|
+
return list;
|
|
347
|
+
}
|
|
351
348
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const dataMap = new Map();
|
|
349
|
+
const preview = [];
|
|
350
|
+
const usedIndex = new Set();
|
|
355
351
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
reportDate: Number(item?.reportDate || 0),
|
|
359
|
-
pv: Number(item?.pv || 0),
|
|
360
|
-
uv: Number(item?.uv || 0)
|
|
361
|
-
});
|
|
362
|
-
}
|
|
352
|
+
for (let i = 0; i < maxCount; i++) {
|
|
353
|
+
const index = Math.round((i * (list.length - 1)) / (maxCount - 1));
|
|
363
354
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
dataMap.get(reportDate) || {
|
|
367
|
-
reportDate: reportDate,
|
|
368
|
-
pv: 0,
|
|
369
|
-
uv: 0
|
|
355
|
+
if (usedIndex.has(index)) {
|
|
356
|
+
continue;
|
|
370
357
|
}
|
|
371
|
-
);
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function sumTrendRange(list) {
|
|
376
|
-
let pv = 0;
|
|
377
|
-
let uv = 0;
|
|
378
|
-
|
|
379
|
-
for (const item of list) {
|
|
380
|
-
pv += Number(item?.pv || 0);
|
|
381
|
-
uv += Number(item?.uv || 0);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return {
|
|
385
|
-
pv: pv,
|
|
386
|
-
uv: uv
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
358
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
if (list.length === 0) {
|
|
394
|
-
productState.selectedKey = "";
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (list.some((item) => item.key === productState.selectedKey)) {
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const currentProduct = list.find((item) => item.productName === productInfo.productName);
|
|
403
|
-
productState.selectedKey = currentProduct?.key || "all-products";
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
function selectProduct(productKey) {
|
|
407
|
-
productState.selectedKey = String(productKey || "");
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
function sumTrendField(list, field) {
|
|
411
|
-
let total = 0;
|
|
412
|
-
|
|
413
|
-
for (const item of list) {
|
|
414
|
-
total += Number(item?.[field] || 0);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return total;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
function sortTrendList(list, keyField) {
|
|
421
|
-
const result = Array.isArray(list) ? list.slice() : [];
|
|
359
|
+
usedIndex.add(index);
|
|
360
|
+
preview.push(list[index]);
|
|
361
|
+
}
|
|
422
362
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
363
|
+
return preview;
|
|
364
|
+
},
|
|
365
|
+
buildTrendBars(list, maxValue) {
|
|
366
|
+
const result = [];
|
|
367
|
+
|
|
368
|
+
for (const item of list) {
|
|
369
|
+
const pv = Number(item?.pv || 0);
|
|
370
|
+
const uv = Number(item?.uv || 0);
|
|
371
|
+
|
|
372
|
+
result.push({
|
|
373
|
+
reportDate: Number(item?.reportDate || 0),
|
|
374
|
+
pv: pv,
|
|
375
|
+
uv: uv,
|
|
376
|
+
pvHeight: pv > 0 ? `${Math.max((pv / maxValue) * 100, 6)}%` : "0%",
|
|
377
|
+
uvHeight: uv > 0 ? `${Math.max((uv / maxValue) * 100, 6)}%` : "0%"
|
|
378
|
+
});
|
|
379
|
+
}
|
|
426
380
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
381
|
+
return result;
|
|
382
|
+
},
|
|
383
|
+
getTrendMax(list) {
|
|
384
|
+
let max = 0;
|
|
431
385
|
|
|
432
|
-
|
|
433
|
-
|
|
386
|
+
for (const item of list) {
|
|
387
|
+
const pv = Number(item?.pv || 0);
|
|
388
|
+
const uv = Number(item?.uv || 0);
|
|
434
389
|
|
|
435
|
-
|
|
436
|
-
|
|
390
|
+
if (pv > max) {
|
|
391
|
+
max = pv;
|
|
392
|
+
}
|
|
437
393
|
|
|
438
|
-
|
|
439
|
-
|
|
394
|
+
if (uv > max) {
|
|
395
|
+
max = uv;
|
|
396
|
+
}
|
|
440
397
|
}
|
|
441
398
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
}
|
|
399
|
+
return max > 0 ? max : 1;
|
|
400
|
+
},
|
|
401
|
+
formatDeviceType(deviceType) {
|
|
402
|
+
if (deviceType === "desktop") {
|
|
403
|
+
return "桌面端";
|
|
404
|
+
}
|
|
448
405
|
|
|
449
|
-
|
|
450
|
-
|
|
406
|
+
if (deviceType === "mobile") {
|
|
407
|
+
return "移动端";
|
|
408
|
+
}
|
|
451
409
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
410
|
+
if (deviceType === "tablet") {
|
|
411
|
+
return "平板";
|
|
412
|
+
}
|
|
455
413
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
uv: uv,
|
|
460
|
-
pvHeight: pv > 0 ? `${Math.max((pv / maxValue) * 100, 6)}%` : "0%",
|
|
461
|
-
uvHeight: uv > 0 ? `${Math.max((uv / maxValue) * 100, 6)}%` : "0%"
|
|
462
|
-
});
|
|
463
|
-
}
|
|
414
|
+
if (deviceType === "smarttv") {
|
|
415
|
+
return "电视";
|
|
416
|
+
}
|
|
464
417
|
|
|
465
|
-
|
|
466
|
-
|
|
418
|
+
if (deviceType === "wearable") {
|
|
419
|
+
return "穿戴设备";
|
|
420
|
+
}
|
|
467
421
|
|
|
468
|
-
|
|
469
|
-
|
|
422
|
+
if (deviceType === "embedded") {
|
|
423
|
+
return "嵌入式设备";
|
|
424
|
+
}
|
|
470
425
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
426
|
+
return String(deviceType || "Unknown");
|
|
427
|
+
},
|
|
428
|
+
formatReportDate(reportDate) {
|
|
429
|
+
const text = String(reportDate || "");
|
|
474
430
|
|
|
475
|
-
if (
|
|
476
|
-
|
|
431
|
+
if (text.length !== 8) {
|
|
432
|
+
return text;
|
|
477
433
|
}
|
|
478
434
|
|
|
479
|
-
|
|
480
|
-
|
|
435
|
+
return `${text.slice(4, 6)}-${text.slice(6, 8)}`;
|
|
436
|
+
},
|
|
437
|
+
async fetchData() {
|
|
438
|
+
try {
|
|
439
|
+
const [overviewRes, onlineStatsRes, infoStatsRes, errorStatsRes] = await Promise.all([$Http("/core/dashboard/systemOverview", {}, [""]), $Http("/core/tongJi/onlineStats", {}, [""]), $Http("/core/tongJi/infoStats", {}, [""]), $Http("/core/tongJi/errorStats", {}, [""])]);
|
|
440
|
+
|
|
441
|
+
Object.assign($Data.permissionStats, overviewRes.data);
|
|
442
|
+
$Data.realtimeStats.onlineCount = Number(onlineStatsRes.data?.onlineCount || 0);
|
|
443
|
+
$Data.realtimeStats.todayPv = Number(onlineStatsRes.data?.today?.pv || 0);
|
|
444
|
+
$Data.realtimeStats.todayUv = Number(onlineStatsRes.data?.today?.uv || 0);
|
|
445
|
+
$Data.realtimeStats.days = Array.isArray(onlineStatsRes.data?.days) ? onlineStatsRes.data.days : [];
|
|
446
|
+
$Data.productInfo.productName = String($Config.productName || "-");
|
|
447
|
+
$Data.productInfo.productCode = String($Config.productCode || "-");
|
|
448
|
+
$Data.productInfo.productVersion = String($Config.productVersion || "-");
|
|
449
|
+
$Data.productState.options = Array.isArray(onlineStatsRes.data?.products)
|
|
450
|
+
? onlineStatsRes.data.products.map((item) => {
|
|
451
|
+
return {
|
|
452
|
+
key: String(item?.key || item?.productName || ""),
|
|
453
|
+
productName: String(item?.productName || "-"),
|
|
454
|
+
today: {
|
|
455
|
+
pv: Number(item?.today?.pv || 0),
|
|
456
|
+
uv: Number(item?.today?.uv || 0)
|
|
457
|
+
},
|
|
458
|
+
week: {
|
|
459
|
+
pv: Number(item?.week?.pv || 0),
|
|
460
|
+
uv: Number(item?.week?.uv || 0)
|
|
461
|
+
},
|
|
462
|
+
month: {
|
|
463
|
+
pv: Number(item?.month?.pv || 0),
|
|
464
|
+
uv: Number(item?.month?.uv || 0)
|
|
465
|
+
},
|
|
466
|
+
days: Array.isArray(item?.days) ? item.days : [],
|
|
467
|
+
totalPv: Number(item?.totalPv || 0),
|
|
468
|
+
totalUv: Number(item?.totalUv || 0)
|
|
469
|
+
};
|
|
470
|
+
})
|
|
471
|
+
: [];
|
|
472
|
+
if (Array.isArray(infoStatsRes.data?.month?.productNames) && infoStatsRes.data.month.productNames.length === 1 && $Data.productInfo.productName !== "-") {
|
|
473
|
+
$Data.productState.options = [
|
|
474
|
+
{
|
|
475
|
+
key: $Data.productInfo.productName,
|
|
476
|
+
productName: $Data.productInfo.productName,
|
|
477
|
+
today: {
|
|
478
|
+
pv: $Data.realtimeStats.todayPv,
|
|
479
|
+
uv: $Data.realtimeStats.todayUv
|
|
480
|
+
},
|
|
481
|
+
week: {
|
|
482
|
+
pv: Number(onlineStatsRes.data?.week?.pv || 0),
|
|
483
|
+
uv: Number(onlineStatsRes.data?.week?.uv || 0)
|
|
484
|
+
},
|
|
485
|
+
month: {
|
|
486
|
+
pv: Number(onlineStatsRes.data?.month?.pv || 0),
|
|
487
|
+
uv: Number(onlineStatsRes.data?.month?.uv || 0)
|
|
488
|
+
},
|
|
489
|
+
days: $Data.realtimeStats.days,
|
|
490
|
+
totalPv: $Method.sumTrendField($Data.realtimeStats.days, "pv"),
|
|
491
|
+
totalUv: $Method.sumTrendField($Data.realtimeStats.days, "uv")
|
|
492
|
+
}
|
|
493
|
+
];
|
|
494
|
+
}
|
|
495
|
+
$Data.uaStats.deviceTypes = Array.isArray(infoStatsRes.data?.today?.deviceTypes) ? infoStatsRes.data.today.deviceTypes : [];
|
|
496
|
+
$Data.uaStats.browsers = Array.isArray(infoStatsRes.data?.today?.browsers) ? infoStatsRes.data.today.browsers : [];
|
|
497
|
+
$Data.uaStats.browserVersions = Array.isArray(infoStatsRes.data?.today?.browserVersions) ? infoStatsRes.data.today.browserVersions : [];
|
|
498
|
+
$Data.uaStats.osList = Array.isArray(infoStatsRes.data?.today?.osList) ? infoStatsRes.data.today.osList : [];
|
|
499
|
+
$Data.uaStats.osVersions = Array.isArray(infoStatsRes.data?.today?.osVersions) ? infoStatsRes.data.today.osVersions : [];
|
|
500
|
+
$Data.uaStats.deviceVendors = Array.isArray(infoStatsRes.data?.today?.deviceVendors) ? infoStatsRes.data.today.deviceVendors : [];
|
|
501
|
+
$Data.uaStats.deviceModels = Array.isArray(infoStatsRes.data?.today?.deviceModels) ? infoStatsRes.data.today.deviceModels : [];
|
|
502
|
+
$Data.uaStats.engines = Array.isArray(infoStatsRes.data?.today?.engines) ? infoStatsRes.data.today.engines : [];
|
|
503
|
+
$Data.uaStats.cpuArchitectures = Array.isArray(infoStatsRes.data?.today?.cpuArchitectures) ? infoStatsRes.data.today.cpuArchitectures : [];
|
|
504
|
+
$Data.errorStats.trend = Array.isArray(errorStatsRes.data?.trend) ? errorStatsRes.data.trend : [];
|
|
505
|
+
$Method.ensureSelectedProduct();
|
|
506
|
+
} catch (_error) {
|
|
507
|
+
// 静默失败:不阻断页面展示
|
|
481
508
|
}
|
|
482
509
|
}
|
|
510
|
+
};
|
|
483
511
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
if (deviceType === "desktop") {
|
|
489
|
-
return "桌面端";
|
|
490
|
-
}
|
|
512
|
+
const $Computed = reactive({
|
|
513
|
+
errorTodayTrend: computed(() => $Method.sortTrendList($Data.errorStats.trend, "bucketTime")),
|
|
514
|
+
errorTodayCount: computed(() => {
|
|
515
|
+
let total = 0;
|
|
491
516
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
517
|
+
for (const item of $Computed.errorTodayTrend) {
|
|
518
|
+
total += Number(item.count || 0);
|
|
519
|
+
}
|
|
495
520
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
521
|
+
return total;
|
|
522
|
+
}),
|
|
523
|
+
productOptions: computed(() => {
|
|
524
|
+
const list = Array.isArray($Data.productState.options) ? $Data.productState.options.slice() : [];
|
|
525
|
+
const fallbackProduct = $Method.buildFallbackProduct();
|
|
499
526
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
527
|
+
if (list.length === 0) {
|
|
528
|
+
return [fallbackProduct];
|
|
529
|
+
}
|
|
503
530
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
531
|
+
list.sort((a, b) => {
|
|
532
|
+
if (a.productName === $Data.productInfo.productName && b.productName !== $Data.productInfo.productName) {
|
|
533
|
+
return -1;
|
|
534
|
+
}
|
|
507
535
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
536
|
+
if (b.productName === $Data.productInfo.productName && a.productName !== $Data.productInfo.productName) {
|
|
537
|
+
return 1;
|
|
538
|
+
}
|
|
511
539
|
|
|
512
|
-
|
|
513
|
-
|
|
540
|
+
if (Number(b.totalPv || 0) !== Number(a.totalPv || 0)) {
|
|
541
|
+
return Number(b.totalPv || 0) - Number(a.totalPv || 0);
|
|
542
|
+
}
|
|
514
543
|
|
|
515
|
-
|
|
516
|
-
|
|
544
|
+
return String(a.productName || "").localeCompare(String(b.productName || ""), "zh-CN");
|
|
545
|
+
});
|
|
517
546
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
547
|
+
return [fallbackProduct, ...list];
|
|
548
|
+
}),
|
|
549
|
+
selectedProduct: computed(() => {
|
|
550
|
+
const list = $Computed.productOptions;
|
|
521
551
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const fetchData = async () => {
|
|
526
|
-
try {
|
|
527
|
-
const [overviewRes, onlineStatsRes, infoStatsRes, errorStatsRes] = await Promise.all([$Http("/core/dashboard/systemOverview", {}, [""]), $Http("/core/tongJi/onlineStats", {}, [""]), $Http("/core/tongJi/infoStats", {}, [""]), $Http("/core/tongJi/errorStats", {}, [""])]);
|
|
528
|
-
|
|
529
|
-
Object.assign(permissionStats, overviewRes.data);
|
|
530
|
-
realtimeStats.onlineCount = Number(onlineStatsRes.data?.onlineCount || 0);
|
|
531
|
-
realtimeStats.todayPv = Number(onlineStatsRes.data?.today?.pv || 0);
|
|
532
|
-
realtimeStats.todayUv = Number(onlineStatsRes.data?.today?.uv || 0);
|
|
533
|
-
realtimeStats.days = Array.isArray(onlineStatsRes.data?.days) ? onlineStatsRes.data.days : [];
|
|
534
|
-
productInfo.productName = String($Config.productName || "-");
|
|
535
|
-
productInfo.productCode = String($Config.productCode || "-");
|
|
536
|
-
productInfo.productVersion = String($Config.productVersion || "-");
|
|
537
|
-
productState.options = Array.isArray(onlineStatsRes.data?.products)
|
|
538
|
-
? onlineStatsRes.data.products.map((item) => {
|
|
539
|
-
return {
|
|
540
|
-
key: String(item?.key || item?.productName || ""),
|
|
541
|
-
productName: String(item?.productName || "-"),
|
|
542
|
-
today: {
|
|
543
|
-
pv: Number(item?.today?.pv || 0),
|
|
544
|
-
uv: Number(item?.today?.uv || 0)
|
|
545
|
-
},
|
|
546
|
-
week: {
|
|
547
|
-
pv: Number(item?.week?.pv || 0),
|
|
548
|
-
uv: Number(item?.week?.uv || 0)
|
|
549
|
-
},
|
|
550
|
-
month: {
|
|
551
|
-
pv: Number(item?.month?.pv || 0),
|
|
552
|
-
uv: Number(item?.month?.uv || 0)
|
|
553
|
-
},
|
|
554
|
-
days: Array.isArray(item?.days) ? item.days : [],
|
|
555
|
-
totalPv: Number(item?.totalPv || 0),
|
|
556
|
-
totalUv: Number(item?.totalUv || 0)
|
|
557
|
-
};
|
|
558
|
-
})
|
|
559
|
-
: [];
|
|
560
|
-
if (Array.isArray(infoStatsRes.data?.month?.productNames) && infoStatsRes.data.month.productNames.length === 1 && productInfo.productName !== "-") {
|
|
561
|
-
productState.options = [
|
|
562
|
-
{
|
|
563
|
-
key: productInfo.productName,
|
|
564
|
-
productName: productInfo.productName,
|
|
565
|
-
today: {
|
|
566
|
-
pv: realtimeStats.todayPv,
|
|
567
|
-
uv: realtimeStats.todayUv
|
|
568
|
-
},
|
|
569
|
-
week: {
|
|
570
|
-
pv: Number(onlineStatsRes.data?.week?.pv || 0),
|
|
571
|
-
uv: Number(onlineStatsRes.data?.week?.uv || 0)
|
|
572
|
-
},
|
|
573
|
-
month: {
|
|
574
|
-
pv: Number(onlineStatsRes.data?.month?.pv || 0),
|
|
575
|
-
uv: Number(onlineStatsRes.data?.month?.uv || 0)
|
|
576
|
-
},
|
|
577
|
-
days: realtimeStats.days,
|
|
578
|
-
totalPv: sumTrendField(realtimeStats.days, "pv"),
|
|
579
|
-
totalUv: sumTrendField(realtimeStats.days, "uv")
|
|
580
|
-
}
|
|
581
|
-
];
|
|
552
|
+
if (list.length === 0) {
|
|
553
|
+
return null;
|
|
582
554
|
}
|
|
583
|
-
uaStats.deviceTypes = Array.isArray(infoStatsRes.data?.today?.deviceTypes) ? infoStatsRes.data.today.deviceTypes : [];
|
|
584
|
-
uaStats.browsers = Array.isArray(infoStatsRes.data?.today?.browsers) ? infoStatsRes.data.today.browsers : [];
|
|
585
|
-
uaStats.browserVersions = Array.isArray(infoStatsRes.data?.today?.browserVersions) ? infoStatsRes.data.today.browserVersions : [];
|
|
586
|
-
uaStats.osList = Array.isArray(infoStatsRes.data?.today?.osList) ? infoStatsRes.data.today.osList : [];
|
|
587
|
-
uaStats.osVersions = Array.isArray(infoStatsRes.data?.today?.osVersions) ? infoStatsRes.data.today.osVersions : [];
|
|
588
|
-
uaStats.deviceVendors = Array.isArray(infoStatsRes.data?.today?.deviceVendors) ? infoStatsRes.data.today.deviceVendors : [];
|
|
589
|
-
uaStats.deviceModels = Array.isArray(infoStatsRes.data?.today?.deviceModels) ? infoStatsRes.data.today.deviceModels : [];
|
|
590
|
-
uaStats.engines = Array.isArray(infoStatsRes.data?.today?.engines) ? infoStatsRes.data.today.engines : [];
|
|
591
|
-
uaStats.cpuArchitectures = Array.isArray(infoStatsRes.data?.today?.cpuArchitectures) ? infoStatsRes.data.today.cpuArchitectures : [];
|
|
592
|
-
errorStats.trend = Array.isArray(errorStatsRes.data?.trend) ? errorStatsRes.data.trend : [];
|
|
593
|
-
ensureSelectedProduct();
|
|
594
|
-
} catch (_error) {
|
|
595
|
-
// 静默失败:不阻断页面展示
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
555
|
|
|
599
|
-
|
|
556
|
+
const matched = list.find((item) => item.key === $Data.productState.selectedKey);
|
|
557
|
+
return matched || list[0];
|
|
558
|
+
}),
|
|
559
|
+
selectedDaysTrend: computed(() => $Method.sortTrendList($Computed.selectedProduct?.days || [], "reportDate")),
|
|
560
|
+
selectedDaysFilled: computed(() => $Method.buildFilledTrendDays($Computed.selectedProduct?.days || [], 30)),
|
|
561
|
+
selectedDaysHasData: computed(() => $Computed.selectedDaysTrend.length > 0),
|
|
562
|
+
selectedDaysTrendPreview: computed(() => $Method.buildTrendPreview($Computed.selectedDaysFilled, 10)),
|
|
563
|
+
selectedDaysBars: computed(() => $Method.buildTrendBars($Computed.selectedDaysFilled, $Method.getTrendMax($Computed.selectedDaysFilled))),
|
|
564
|
+
productPeriodCards: computed(() => {
|
|
565
|
+
const list = $Computed.selectedDaysFilled;
|
|
566
|
+
const today = list[list.length - 1] || { pv: 0, uv: 0 };
|
|
567
|
+
const yesterday = list[list.length - 2] || { pv: 0, uv: 0 };
|
|
568
|
+
const dayBeforeYesterday = list[list.length - 3] || { pv: 0, uv: 0 };
|
|
569
|
+
const recent7 = $Method.sumTrendRange(list.slice(-7));
|
|
570
|
+
const recent30 = $Method.sumTrendRange(list);
|
|
571
|
+
|
|
572
|
+
return [
|
|
573
|
+
{ key: "today", label: "今日", pv: today.pv, uv: today.uv },
|
|
574
|
+
{ key: "yesterday", label: "昨日", pv: yesterday.pv, uv: yesterday.uv },
|
|
575
|
+
{ key: "dayBeforeYesterday", label: "前日", pv: dayBeforeYesterday.pv, uv: dayBeforeYesterday.uv },
|
|
576
|
+
{ key: "recent7", label: "最近7天", pv: recent7.pv, uv: recent7.uv },
|
|
577
|
+
{ key: "recent30", label: "最近30天", pv: recent30.pv, uv: recent30.uv }
|
|
578
|
+
];
|
|
579
|
+
})
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
$Method.fetchData();
|
|
600
583
|
</script>
|
|
601
584
|
|
|
602
585
|
<style scoped lang="scss">
|