人员岗位补负责人/线路维护,并同步后台人员、机器人回复与看板页改动。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
linmianxun
2026-09-15 11:12:13 +08:00
co-authored by Cursor
parent a7a8487eb3
commit 58b3c2f3e6
7 changed files with 425 additions and 155 deletions
@@ -21,16 +21,13 @@ const COST_PLOT_TOP = 36;
const COST_PLOT_BOTTOM = 186;
const now = new Date();
const isoDay = now.toISOString().slice(0, 10);
const dateDisplay = ref(`${isoDay}——${isoDay}`);
const dateFrom = ref(isoDay);
/** 用本地日期,避免 toISOString 因时区偏到前一天 */
const pad2 = (n: number) => String(n).padStart(2, "0");
const isoDay = `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-${pad2(now.getDate())}`;
/** 默认:当月 1 号 → 今天(不是当天到当天) */
const isoMonthStart = `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-01`;
const dateFrom = ref(isoMonthStart);
const dateTo = ref(isoDay);
const dateFromMonth = ref(isoDay.slice(0, 7));
const dateToMonth = ref(isoDay.slice(0, 7));
const dateFromYear = ref(isoDay.slice(0, 4));
const dateToYear = ref(isoDay.slice(0, 4));
const pickerOpen = ref(false);
const pickerMode = ref<"day" | "month" | "year">("day");
const updatedTime = ref("00:00");
const routeFilterOpen = ref(false);
const selectedRoutes = ref<string[]>([]);
@@ -60,18 +57,34 @@ const INQUIRY_PLOT_TOP = 36;
const INQUIRY_PLOT_BOTTOM = 188;
function resolveRange(): string {
if (pickerMode.value === "day") {
const from = new Date(dateFrom.value);
const to = new Date(dateTo.value);
const days = Math.max(0, Math.round((to.getTime() - from.getTime()) / 86400000));
if (days <= 0) return "today";
if (days <= 6) return "7d";
return "30d";
}
if (pickerMode.value === "month") return "30d";
const from = new Date(dateFrom.value);
const to = new Date(dateTo.value);
const days = Math.max(0, Math.round((to.getTime() - from.getTime()) / 86400000));
if (days <= 0) return "today";
if (days <= 6) return "7d";
return "30d";
}
/**
* 顶栏日期:与工单列表同一套 a-range-picker(双月历点选起止)。
* 不再并排放「按日/月/年」切换,避免顶栏挤换行显得乱。
*/
const dateRange = computed({
get(): [string, string] | undefined {
if (!dateFrom.value && !dateTo.value) return undefined;
return [dateFrom.value || "", dateTo.value || ""];
},
set(v: [string, string] | null | undefined) {
dateFrom.value = v?.[0] ? String(v[0]) : isoMonthStart;
dateTo.value = v?.[1] ? String(v[1]) : isoDay;
},
});
/** 选完起止日后立刻刷新看板 */
function onDateRangeChange() {
loadMetrics();
}
async function loadMetrics() {
loading.value = true;
try {
@@ -240,14 +253,6 @@ const costXs = computed(() => {
return costDates.value.map((_, i) => (n === 1 ? 380 : 36 + (654 / (n - 1)) * i));
});
function applyDateRange() {
if (pickerMode.value === "day") dateDisplay.value = `${dateFrom.value}——${dateTo.value}`;
else if (pickerMode.value === "month") dateDisplay.value = `${dateFromMonth.value}——${dateToMonth.value}`;
else dateDisplay.value = `${dateFromYear.value}——${dateToYear.value}`;
pickerOpen.value = false;
loadMetrics();
}
function refreshData() {
loadMetrics().then(() => message.success("数据已更新"));
}
@@ -334,7 +339,6 @@ function exportRanking() {
function onDocClick(e: MouseEvent) {
const t = e.target as HTMLElement;
if (!t.closest(".month-range-picker")) pickerOpen.value = false;
if (!t.closest(".ref-route-filter")) routeFilterOpen.value = false;
}
@@ -353,33 +357,21 @@ onUnmounted(() => document.removeEventListener("click", onDocClick));
<!-- defer:等布局挂好 #page-topbar-actions 再传送,避免顶栏工具丢失 -->
<Teleport defer to="#page-topbar-actions">
<div class="dash-topbar-tools">
<div class="month-range-picker" @click.stop>
<div class="month-range-input" @click="pickerOpen = !pickerOpen">
<span>{{ dateDisplay }}</span>
<ProtoIcon name="calendar" :size="14" style="color: #94a3b8" />
</div>
<div v-show="pickerOpen" class="month-range-dropdown dash-picker-dropdown">
<div class="month-range-tabs">
<button type="button" class="month-range-tab" :class="{ 'is-active': pickerMode === 'day' }" @click="pickerMode = 'day'">按日</button>
<button type="button" class="month-range-tab" :class="{ 'is-active': pickerMode === 'month' }" @click="pickerMode = 'month'">按月</button>
<button type="button" class="month-range-tab" :class="{ 'is-active': pickerMode === 'year' }" @click="pickerMode = 'year'">按年</button>
</div>
<div class="dash-picker-body">
<template v-if="pickerMode === 'day'">
<input v-model="dateFrom" type="date" class="dash-picker-input" />
<input v-model="dateTo" type="date" class="dash-picker-input" />
</template>
<template v-else-if="pickerMode === 'month'">
<input v-model="dateFromMonth" type="month" class="dash-picker-input" />
<input v-model="dateToMonth" type="month" class="dash-picker-input" />
</template>
<template v-else>
<input v-model="dateFromYear" type="number" min="2020" max="2035" class="dash-picker-input" placeholder="起始年" />
<input v-model="dateToYear" type="number" min="2020" max="2035" class="dash-picker-input" placeholder="结束年" />
</template>
<button type="button" class="btn btn-primary" style="width: 100%; min-height: 32px; height: 32px" @click="applyDateRange">确定</button>
</div>
</div>
<div class="toolbar-date-range dash-toolbar-date" title="选择统计起止日期">
<a-range-picker
v-model:value="dateRange"
class="toolbar-range-picker dash-range-picker"
value-format="YYYY-MM-DD"
separator="至"
:placeholder="['开始日期', '结束日期']"
:allow-clear="false"
:input-read-only="true"
@change="onDateRangeChange"
>
<template #suffixIcon>
<ProtoIcon name="calendar" :size="14" />
</template>
</a-range-picker>
</div>
<div class="dashboard-data-note" aria-live="polite">
<span class="dashboard-data-note__icon"><ProtoIcon name="clock" :size="13" /></span>
@@ -634,38 +626,30 @@ onUnmounted(() => document.removeEventListener("click", onDocClick));
<style scoped>
.dash-topbar-tools {
display: flex;
flex-wrap: wrap;
flex-wrap: nowrap;
align-items: center;
gap: 8px;
}
.dash-picker-dropdown {
position: absolute;
right: 0;
top: calc(100% + 8px);
width: 300px;
z-index: 80;
.dash-toolbar-date {
height: 34px;
flex-shrink: 0;
}
.dash-picker-body {
display: grid;
gap: 8px;
padding-top: 4px;
}
.dash-picker-input {
width: 100%;
height: 32px;
padding: 0 10px;
border: 1px solid #dfe5ee;
border-radius: 7px;
font-size: 11px;
font-weight: 700;
.dash-toolbar-date :deep(.ant-picker) {
height: 34px !important;
min-height: 34px !important;
width: 260px !important;
}
.dash-refresh-btn {
height: 34px;
min-height: 34px;
flex-shrink: 0;
}
.dashboard-data-note {
flex-shrink: 0;
}
.dash-chart-title {