数据建设页面开发

This commit is contained in:
2026-03-10 17:10:04 +08:00
parent 825e35c4fc
commit 4ead65751d

View File

@@ -1,12 +1,207 @@
<template> <template>
<div class="stats-view"> <div class="stats-view">
<el-card> <el-card class="box-card" shadow="never">
<template #header> <template #header>
<h2>数据统计</h2> <div class="card-header">
<span>员工锁定站点统计</span>
<div class="header-actions">
<el-text size="small" type="info" style="margin-right: 12px">只看本月</el-text>
<el-switch v-model="showOnlyThisMonth" />
</div>
</div>
</template> </template>
<p>数据统计页面正在建设中...</p> <div v-loading="loading">
<el-empty v-if="!staffWithLockedTickets.length && !loading" description="暂无已锁定的站点数据" />
<el-collapse v-else v-model="activeCollapse" accordion>
<el-collapse-item v-for="staff in staffWithLockedTickets" :key="staff.id" :name="staff.id">
<template #title>
<div class="collapse-title" @mousedown.prevent>
<div class="staff-info">
<el-avatar :size="28">{{ staff.name.slice(0, 1) }}</el-avatar>
<span class="staff-name">{{ staff.name }}</span>
</div>
<el-tag type="success" effect="dark" round>
已锁定 {{ staff.lockedTickets.length }} 个站点
</el-tag>
</div>
</template>
<el-table :data="staff.lockedTickets" stripe border size="small" class="inner-table">
<el-table-column prop="id" label="工单ID" width="80" align="center" />
<el-table-column prop="website" label="站点名称" min-width="200" show-overflow-tooltip />
<el-table-column prop="operator" label="运营" width="120" align="center" />
<el-table-column prop="datanum" label="数据量" width="100" align="center">
<template #default="{ row }">
<span>{{
row.datanum && row.datanum !== "0" ? row.datanum : "-"
}}</span>
</template>
</el-table-column>
<el-table-column label="锁定日期" width="150" align="center">
<template #default="{ row }">
{{ row.lock_time ? row.lock_time.slice(0, 10) : "-" }}
</template>
</el-table-column>
<el-table-column prop="is_complete" label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag :type="row.is_complete === '已完成' ? 'success' : 'info'" size="small">{{ row.is_complete
}}</el-tag>
</template>
</el-table-column>
</el-table>
</el-collapse-item>
</el-collapse>
</div>
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup></script> <script setup>
import { ref, onMounted, computed } from "vue";
import { ticketApi } from "@/api/ticket";
import { ElMessage } from "element-plus";
const loading = ref(true);
const staffList = ref([]);
const allTickets = ref([]);
const activeCollapse = ref("");
const showOnlyThisMonth = ref(false);
onMounted(async () => {
loading.value = true;
try {
const [staffRes, ticketsRes] = await Promise.all([
ticketApi.getStaffs(),
ticketApi.getTickets({ page: 1, limit: 9999 }), // 获取所有工单
]);
if (staffRes.code) {
staffList.value = staffRes.data;
} else {
ElMessage.error("获取员工列表失败");
}
if (ticketsRes.code) {
allTickets.value = ticketsRes.data.data;
} else {
ElMessage.error("获取工单列表失败");
}
} catch (error) {
console.error("加载数据失败:", error);
ElMessage.error("加载数据失败");
} finally {
loading.value = false;
}
});
const staffWithLockedTickets = computed(() => {
if (!staffList.value.length || !allTickets.value.length) return [];
// 筛选出所有已锁定(已完成/已完结)且具有有效锁定时间的工单
let lockedTickets = allTickets.value.filter((ticket) => {
const isLockedStatus = ["已完成", "已完结"].includes(ticket.is_complete);
// lock_time 是 "YYYY-MM-DD HH:mm" 格式的字符串,只需判断其是否存在
return isLockedStatus && ticket.staff_id && ticket.lock_time;
});
// 如果开启了“只看本月”,则进一步筛选
if (showOnlyThisMonth.value) {
const now = new Date();
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59);
lockedTickets = lockedTickets.filter((ticket) => {
// 直接用日期字符串创建 Date 对象进行比较
const lockDate = new Date(ticket.lock_time);
return (
!isNaN(lockDate.getTime()) && lockDate >= startOfMonth && lockDate <= endOfMonth
);
});
}
// 创建一个员工的Map,方便快速查找
const staffMap = new Map(
staffList.value.map((staff) => [String(staff.id), { ...staff, lockedTickets: [] }])
);
// 将锁定的工单分配到对应的员工下
lockedTickets.forEach((ticket) => {
const staffId = String(ticket.staff_id);
if (staffMap.has(staffId)) {
staffMap.get(staffId).lockedTickets.push(ticket);
}
});
// 对每个员工的工单按锁定时间倒序排序
staffMap.forEach((staff) => {
staff.lockedTickets.sort((a, b) => {
// 使用 new Date().getTime() 来比较日期字符串
return new Date(b.lock_time).getTime() - new Date(a.lock_time).getTime();
});
});
// 返回一个数组,只包含有锁定工单的员工,并按工单数量排序
return Array.from(staffMap.values())
.filter((staff) => staff.lockedTickets.length > 0)
.sort((a, b) => b.lockedTickets.length - a.lockedTickets.length);
});
</script>
<style scoped>
.stats-view {
padding: 20px;
}
.box-card {
border-radius: 12px;
border: 1px solid #e4e7ed;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-actions {
display: flex;
align-items: center;
}
.card-header>span {
font-weight: bold;
font-size: 16px;
}
.collapse-title {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding-right: 16px;
}
.staff-info {
display: flex;
align-items: center;
gap: 12px;
}
.staff-name {
font-size: 15px;
font-weight: 500;
}
.inner-table {
width: calc(100% - 32px);
margin: 0 16px 16px 16px;
}
:deep(.el-collapse-item__header) {
padding: 0 16px;
height: 60px;
}
:deep(.el-collapse-item__content) {
padding-bottom: 0;
}
</style>