feat(home): surface ops shortcuts and realtime stats

This commit is contained in:
lzh
2026-03-15 16:41:25 +08:00
parent 6178e21e18
commit 28537774ed
2 changed files with 69 additions and 30 deletions

View File

@@ -7,7 +7,7 @@
<text class="text-28rpx text-[#1F2937] font-bold">常用应用</text>
</view>
<view class="ai-card p-32rpx">
<view class="grid grid-cols-3 gap-y-40rpx">
<view class="grid grid-cols-4 gap-y-40rpx">
<view
v-for="app in quickApps"
:key="app.key"
@@ -93,12 +93,10 @@ const expandedGroups = ref<string[]>([])
/** 快速应用 — 重新设计图标与配色 */
const quickApps = [
{ key: 'workOrder', name: '工单', icon: 'i-carbon-task-complete', color: '#F97316', bgLight: '#FFF7ED', url: '/pages/scan/work-order/index' },
{ key: 'staff', name: '员工管理', icon: 'i-carbon-events', color: '#3B82F6', bgLight: '#EFF6FF', url: '/pages/scan/staff/index' },
{ key: 'inspection', name: '扫码巡检', icon: 'i-carbon-qr-code', color: '#8B5CF6', bgLight: '#F5F3FF', url: '/pages/scan/index' },
{ key: 'dashboard', name: '数据看板', icon: 'i-carbon-analytics', color: '#10B981', bgLight: '#ECFDF5' },
{ key: 'attendance', name: '考勤打卡', icon: 'i-carbon-location-person', color: '#06B6D4', bgLight: '#ECFEFF' },
{ key: 'repair', name: '报修管理', icon: 'i-carbon-settings-adjust', color: '#EC4899', bgLight: '#FDF2F8' },
{ key: 'workOrder', name: '工单中心', icon: 'i-carbon-task-complete', color: '#F97316', bgLight: '#FFF7ED', url: '/pages/scan/work-order/index' },
{ key: 'inspection', name: '巡检记录', icon: 'i-carbon-list-checked', color: '#8B5CF6', bgLight: '#F5F3FF', url: '/pages/scan/inspection/list' },
{ key: 'workOrderStats', name: '工单统计', icon: 'i-carbon-chart-bar', color: '#3B82F6', bgLight: '#EFF6FF', url: '/pages/scan/work-order/stats' },
{ key: 'trafficStats', name: '客流统计', icon: 'i-carbon-pedestrian', color: '#10B981', bgLight: '#ECFDF5', url: '/pages/scan/traffic/index' },
]
function handleQuickApp(app: any) {

View File

@@ -50,11 +50,11 @@
<view class="mt-32rpx flex px-8rpx">
<view class="flex flex-1 flex-col">
<text class="stat-label">实时客流统计</text>
<text class="stat-value">2,450</text>
<text class="stat-value">{{ trafficCount }}</text>
</view>
<view class="stat-divider flex flex-1 flex-col pl-32rpx">
<text class="stat-label">实时工单统计</text>
<text class="stat-value">128</text>
<text class="stat-value">{{ orderCount }}</text>
</view>
</view>
@@ -102,6 +102,8 @@
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { getWorkspaceStats } from '@/api/ops/order-center'
import { getTrafficRealtime } from '@/api/ops/traffic'
import { useUserStore } from '@/store'
import { menuButtonInfo, statusBarHeight } from '@/utils/systemInfo'
@@ -149,29 +151,36 @@ const avatarStyle = computed(() => {
return { width: '80rpx', height: '80rpx' }
})
// ---- 统计数据 ----
const trafficTotal = ref<number | undefined>()
const orderTotal = ref<number | undefined>()
/** 格式化数字:大于 1000 使用逗号分隔 */
function formatNumber(num: number): string {
return num.toLocaleString()
}
const trafficCount = computed(() =>
trafficTotal.value !== undefined ? formatNumber(trafficTotal.value) : '--',
)
const orderCount = computed(() =>
orderTotal.value !== undefined ? formatNumber(orderTotal.value) : '--',
)
// ---- 柱状图数据 ----
const rawData = [
{ label: '08:00', value: 180 },
{ label: '09:00', value: 360 },
{ label: '10:00', value: 520 },
{ label: '11:00', value: 720 },
{ label: '12:00', value: 490 },
{ label: '13:00', value: 410 },
{ label: '14:00', value: 670 },
{ label: '15:00', value: 810 },
{ label: '16:00', value: 580 },
{ label: '17:00', value: 450 },
{ label: '18:00', value: 320 },
{ label: '19:00', value: 240 },
]
const rawData = ref<{ label: string, value: number }[]>([])
const maxValue = Math.max(...rawData.map(d => d.value))
const chartData = rawData.map((d, i) => ({
...d,
percent: (d.value / maxValue) * 100,
showLabel: i % 2 === 0, // 隔一个显示
}))
const chartData = computed(() => {
const data = rawData.value
if (!data.length)
return []
const max = Math.max(...data.map(d => d.value), 1)
return data.map((d, i) => ({
...d,
percent: (d.value / max) * 100,
showLabel: i % 2 === 0,
}))
})
/** 当前选中的柱子,-1 未选中 */
const activeBar = ref(-1)
@@ -180,6 +189,38 @@ function handleBarTap(index: number) {
activeBar.value = activeBar.value === index ? -1 : index
}
/** 获取当天日期 yyyy-MM-dd */
function getTodayStr() {
const d = new Date()
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
/** 加载统计数据(并行请求) */
async function loadStats() {
const [trafficResult, workspaceResult] = await Promise.allSettled([
getTrafficRealtime(getTodayStr()),
getWorkspaceStats(),
])
if (trafficResult.status === 'fulfilled') {
trafficTotal.value = trafficResult.value.totalIn
if (trafficResult.value.hourlyTrend) {
const { hours, inData } = trafficResult.value.hourlyTrend
rawData.value = hours
.map((h, i) => ({ label: h, value: inData[i] || 0 }))
.filter(d => d.label >= '06:00' && d.label <= '23:00')
}
}
if (workspaceResult.status === 'fulfilled') {
orderTotal.value = workspaceResult.value.todayOrderCount
}
}
onShow(() => {
loadStats()
})
/** 根据时间获取问候语 */
const greeting = computed(() => {
const hour = new Date().getHours()