Files
admin-ui/src/views/ads/summary/monitor/index.vue
2910410219 4c91dd6fd5 feat(文档管理): 添加文档状态更新和向量生成功能
refactor(话术管理): 重构话术列表和编辑界面,调整API路径

fix(请求拦截器): 移除调试日志并优化错误处理

style(分页组件): 格式化代码并添加初始化标记

feat(知识库): 实现文档状态切换和向量生成功能
2026-04-11 16:11:00 +08:00

1346 lines
37 KiB
Vue

<template>
<div class="ads-summary-monitor">
<el-card shadow="hover">
<template #header>
<div class="card-header">
<span>监控总表</span>
<el-button type="text" @click="openSettingDialog" class="setting-btn">
<el-icon><Setting /></el-icon>
<span>设置</span>
</el-button>
</div>
</template>
<el-tabs v-model="activeTab" class="tabs-container">
<el-tab-pane label="数据趋势" name="trend">
<div class="chart-container">
<el-row :gutter="20">
<el-col :span="24">
<el-card>
<template #header>
<div class="card-header">预算执行趋势</div>
</template>
<div ref="trendChartRef" class="chart" style="width: 100%; height: 600px"></div>
</el-card>
</el-col>
</el-row>
</div>
<div class="table-container">
<el-card>
<template #header>
<div class="card-header">
<span>部门执行明细</span>
<el-form :model="searchParams" :inline="true" class="search-form">
<el-form-item label="时间范围">
<el-date-picker
v-model="searchParams.dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
:shortcuts="dateShortcuts"
/>
</el-form-item>
<el-form-item label="部门">
<el-select v-model="searchParams.department" placeholder="选择部门">
<el-option v-for="option in departmentOptions" :key="option.value || 'all'" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<el-table :data="trendTableData" style="width: 100%">
<el-table-column prop="department" label="部门" />
<el-table-column prop="data" label="数据" />
</el-table>
</el-card>
</div>
</el-tab-pane>
<el-tab-pane label="部门分析" name="department">
<div class="chart-container">
<el-row :gutter="20">
<el-col :span="24">
<el-card>
<template #header>
<div class="card-header">部门达成率分析</div>
</template>
<div ref="departmentChartRef" class="chart" style="width: 100%; height: 600px"></div>
</el-card>
</el-col>
</el-row>
</div>
<div class="table-container">
<el-card>
<template #header>
<div class="card-header">
<span>部门预算执行明细</span>
<el-form :model="searchParams" :inline="true" class="search-form">
<el-form-item label="时间范围">
<el-date-picker
v-model="searchParams.dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
:shortcuts="dateShortcuts"
/>
</el-form-item>
<el-form-item label="部门">
<el-select v-model="searchParams.department" placeholder="选择部门">
<el-option v-for="option in departmentOptions" :key="option.value || 'all'" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<el-table :data="departmentTableData" style="width: 100%">
<el-table-column prop="department" label="部门" />
<el-table-column prop="monthlyBudget" label="月度任务" />
<el-table-column prop="executed" label="已达成" />
<el-table-column prop="achievementRate" label="达成率">
<template #default="scope">{{ scope.row.achievementRate }}%</template>
</el-table-column>
<el-table-column prop="surplusBudget" label="剩余任务" />
<el-table-column prop="dailyAverage" label="剩余日均" />
<el-table-column prop="yesterday" label="昨日" />
<el-table-column prop="estimatedAchievement" label="预计达成" />
<el-table-column prop="dailyDifference" label="日均差" />
<el-table-column prop="previousDay" label="前日" />
<el-table-column prop="sequentialRatio" label="环比" />
<el-table-column prop="timeProgress" label="对比时间进度" />
<el-table-column prop="januaryAchievement" label="1月达成" />
<el-table-column prop="januarySequentialRatio" label="环比" />
<el-table-column prop="differenceFromTarget" label="目标差" />
</el-table>
</el-card>
</div>
</el-tab-pane>
<el-tab-pane label="任务进度" name="progress">
<div class="chart-container">
<el-row :gutter="20">
<el-col :span="24">
<el-card>
<template #header>
<div class="card-header">任务进度分析</div>
</template>
<div ref="progressChartRef" class="chart" style="width: 100%; height: 600px"></div>
</el-card>
</el-col>
</el-row>
</div>
<div class="table-container">
<el-card>
<template #header>
<div class="card-header">
<span>任务进度明细</span>
<el-form :model="searchParams" :inline="true" class="search-form">
<el-form-item label="时间范围">
<el-date-picker
v-model="searchParams.dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
:shortcuts="dateShortcuts"
/>
</el-form-item>
<el-form-item label="部门">
<el-select v-model="searchParams.department" placeholder="选择部门">
<el-option v-for="option in departmentOptions" :key="option.value || 'all'" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<el-table :data="progressTableData" style="width: 100%">
<el-table-column prop="date" label="日期" />
<el-table-column prop="quarterFirstDay" label="季度第一天" />
<el-table-column prop="quarterLastDay" label="季度最后一天" />
<el-table-column prop="textAudit" label="文本审核" />
<el-table-column prop="homeCount" label="家居次数" />
<el-table-column prop="completedCount" label="已完成次数" />
<el-table-column prop="remainingDays" label="剩余天数" />
<el-table-column prop="quarterSecondMonth" label="季度二月" />
<el-table-column prop="textAudit2" label="文本审核2" />
</el-table>
</el-card>
</div>
</el-tab-pane>
<el-tab-pane label="客户监控" name="customer">
<div class="chart-container">
<el-row :gutter="20">
<el-col :span="24">
<el-card>
<template #header>
<div class="card-header">客户数据趋势</div>
</template>
<div ref="customerChartRef" class="chart" style="width: 100%; height: 600px"></div>
</el-card>
</el-col>
</el-row>
</div>
<div class="table-container">
<el-card>
<template #header>
<div class="card-header">
<span>监控总表</span>
<el-form :model="customerSearchParams" :inline="true" class="search-form">
<el-form-item label="客户">
<el-input v-model="customerSearchParams.customer" placeholder="搜索客户" style="width: 180px" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="customerSearchParams.status" placeholder="选择状态" style="width: 120px">
<el-option label="全部" value="" />
<el-option label="正常" value="normal" />
<el-option label="异常" value="abnormal" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleCustomerSearch">查询</el-button>
<el-button @click="handleCustomerReset">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<el-table :data="customerTableData" style="width: 100%" border>
<el-table-column prop="customer" label="客户" width="150" />
<el-table-column prop="date1" label="2026/2/7" width="120">
<template #default="scope">
<span :class="scope.row.date1 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date1 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date2" label="2026/2/8" width="120">
<template #default="scope">
<span :class="scope.row.date2 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date2 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date3" label="2026/2/9" width="120">
<template #default="scope">
<span :class="scope.row.date3 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date3 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date4" label="2026/2/10" width="120">
<template #default="scope">
<span :class="scope.row.date4 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date4 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date5" label="2026/2/11" width="120">
<template #default="scope">
<span :class="scope.row.date5 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date5 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date6" label="2026/2/12" width="120">
<template #default="scope">
<span :class="scope.row.date6 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date6 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date7" label="2026/2/15" width="120">
<template #default="scope">
<span :class="scope.row.date7 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date7 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date8" label="2026/2/16" width="120">
<template #default="scope">
<span :class="scope.row.date8 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date8 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date9" label="2026/2/17" width="120">
<template #default="scope">
<span :class="scope.row.date9 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date9 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date10" label="2026/2/18" width="120">
<template #default="scope">
<span :class="scope.row.date10 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date10 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date11" label="2026/2/19" width="120">
<template #default="scope">
<span :class="scope.row.date11 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date11 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date12" label="2026/2/20" width="120">
<template #default="scope">
<span :class="scope.row.date12 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date12 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date13" label="2026/2/21" width="120">
<template #default="scope">
<span :class="scope.row.date13 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date13 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date14" label="2026/2/22" width="120">
<template #default="scope">
<span :class="scope.row.date14 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date14 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date15" label="2026/2/23" width="120">
<template #default="scope">
<span :class="scope.row.date15 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date15 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date16" label="2026/2/24" width="120">
<template #default="scope">
<span :class="scope.row.date16 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date16 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date17" label="2026/2/25" width="120">
<template #default="scope">
<span :class="scope.row.date17 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date17 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date18" label="2026/2/26" width="120">
<template #default="scope">
<span :class="scope.row.date18 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date18 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date19" label="2026/2/27" width="120">
<template #default="scope">
<span :class="scope.row.date19 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date19 }}
</span>
</template>
</el-table-column>
<el-table-column prop="date20" label="2026/2/28" width="120">
<template #default="scope">
<span :class="scope.row.date20 >= 0 ? 'text-green' : 'text-red'">
{{ scope.row.date20 }}
</span>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</el-tab-pane>
</el-tabs>
<el-dialog v-model="dialogVisible" title="任务设置" width="400px">
<el-form :model="taskSettings" label-width="120px">
<el-form-item label="任务类型">
<el-radio-group v-model="taskSettings.taskType">
<el-radio label="monthly">月度任务</el-radio>
<el-radio label="quarterly">季度任务</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="总任务金额">
<el-input-number v-model="taskSettings.totalBudget" :min="0" :step="10000" />
</el-form-item>
<el-form-item label="部门任务分配">
<el-button type="primary" @click="openDepartmentTaskDialog">设置部门任务</el-button>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSaveSettings">保存设置</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="departmentTaskDialogVisible" title="部门任务分配" width="600px">
<el-table :data="departmentTasks" style="width: 100%">
<el-table-column prop="department" label="部门" />
<el-table-column prop="budget" label="任务金额">
<template #default="scope">
<el-input-number v-model="scope.row.budget" :min="0" :step="10000" />
</template>
</el-table-column>
</el-table>
<template #footer>
<span class="dialog-footer">
<el-button @click="departmentTaskDialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveDepartmentTasks">保存分配</el-button>
</span>
</template>
</el-dialog>
</el-card>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, watch, nextTick } from 'vue';
import * as echarts from 'echarts';
import { Setting } from '@element-plus/icons-vue';
interface DepartmentData {
department: string;
monthlyBudget: string;
executed: string;
achievementRate: string;
surplusBudget: string;
surplusDays: string;
dailyAverage: string;
yesterday: string;
estimatedAchievement: string;
dailyDifference: string;
previousDay: string;
sequentialRatio: string;
timeProgress: string;
januaryAchievement: string;
januarySequentialRatio: string;
differenceFromTarget: string;
}
interface TrendData {
department: string;
data: string;
}
interface ProgressData {
date: string;
quarterFirstDay: string;
quarterLastDay: string;
textAudit: string;
homeCount: string;
completedCount: string;
remainingDays: string;
quarterSecondMonth: string;
textAudit2: string;
}
interface DepartmentTask {
department: string;
budget: number;
}
interface CustomerData {
customer: string;
date1: number;
date2: number;
date3: number;
date4: number;
date5: number;
date6: number;
date7: number;
date8: number;
date9: number;
date10: number;
date11: number;
date12: number;
date13: number;
date14: number;
date15: number;
date16: number;
date17: number;
date18: number;
date19: number;
date20: number;
}
const departmentOptions = [
{ label: '全部', value: 'all' },
{ label: '一区一部', value: '一区一部' },
{ label: '一区二部', value: '一区二部' },
{ label: '一区三部', value: '一区三部' },
{ label: '一区四部', value: '一区四部' },
{ label: '代理运营总', value: '代理运营总' },
{ label: '渠道部', value: '渠道部' },
{ label: '电商一部', value: '电商一部' },
{ label: '电商二部', value: '电商二部' },
{ label: '张哥自营', value: '张哥自营' },
{ label: '电商合计', value: '电商合计' },
{ label: '金牛总任务', value: '金牛总任务' },
];
const searchParams = reactive({
dateRange: [],
department: '',
});
const customerSearchParams = reactive({
customer: '',
status: '',
});
const dateShortcuts = [
{
text: '最近7天',
value: () => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
return [start, end];
},
},
{
text: '最近30天',
value: () => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
return [start, end];
},
},
{
text: '最近90天',
value: () => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
return [start, end];
},
},
];
const activeTab = ref('trend');
const dialogVisible = ref(false);
const departmentTaskDialogVisible = ref(false);
const taskSettings = reactive({
taskType: 'monthly',
totalBudget: 52000000,
});
const departmentTasks = ref<DepartmentTask[]>([
{ department: '一区一部', budget: 1300000 },
{ department: '一区二部', budget: 3500000 },
{ department: '一区三部', budget: 2100000 },
{ department: '一区四部', budget: 1600000 },
{ department: '代理运营总', budget: 8500000 },
{ department: '渠道部', budget: 3000000 },
{ department: '电商一部', budget: 4700000 },
{ department: '电商二部', budget: 2800000 },
{ department: '张哥自营', budget: 6000000 },
{ department: '电商合计', budget: 13500000 },
{ department: '金牛总任务', budget: 52000000 },
]);
const trendChartRef = ref();
const departmentChartRef = ref();
const progressChartRef = ref();
const customerChartRef = ref();
let trendChart: echarts.ECharts | null = null;
let departmentChart: echarts.ECharts | null = null;
let progressChart: echarts.ECharts | null = null;
let customerChart: echarts.ECharts | null = null;
const totalStats = reactive({
totalBudget: '0',
executed: '0',
overallRate: '0',
dailyAverage: '0',
});
const data = ref<DepartmentData[]>([]);
const trendTableData = ref<TrendData[]>([]);
const departmentTableData = ref<DepartmentData[]>([]);
const progressTableData = ref<ProgressData[]>([]);
const customerTableData = ref<CustomerData[]>([]);
const getMockData = (): DepartmentData[] => {
// 根据图片中的数据生成模拟数据
const departments = [
{
name: '一区一部',
monthlyBudget: '1300000',
executed: '937947.12',
achievementRate: '72.15',
surplusBudget: '362052.88',
surplusDays: '21',
dailyAverage: '17240.61',
yesterday: '36129.91',
estimatedAchievement: '130.51%',
dailyDifference: '18889.30',
previousDay: '47003.57',
sequentialRatio: '-10873.66',
timeProgress: '42%',
januaryAchievement: '1547156.813',
januarySequentialRatio: '-16%',
differenceFromTarget: '-7%',
},
{
name: '一区二部',
monthlyBudget: '3500000',
executed: '2338291.84',
achievementRate: '66.81',
surplusBudget: '1161708.16',
surplusDays: '21',
dailyAverage: '55319.44',
yesterday: '106494.91',
estimatedAchievement: '130.71%',
dailyDifference: '51175.47',
previousDay: '105791.93',
sequentialRatio: '702.97',
timeProgress: '37%',
januaryAchievement: '3789374.035',
januarySequentialRatio: '-8%',
differenceFromTarget: '2%',
},
{
name: '一区三部',
monthlyBudget: '2100000',
executed: '1896655.41',
achievementRate: '90.32',
surplusBudget: '203344.59',
surplusDays: '21',
dailyAverage: '9683.07',
yesterday: '55613.03',
estimatedAchievement: '145.93%',
dailyDifference: '45928.43',
previousDay: '53994.08',
sequentialRatio: '1618.95',
timeProgress: '60%',
januaryAchievement: '2861643.583',
januarySequentialRatio: '-27%',
differenceFromTarget: '-19%',
},
{
name: '一区四部',
monthlyBudget: '1600000',
executed: '1960555.94',
achievementRate: '122.53',
surplusBudget: '-360555.94',
surplusDays: '21',
dailyAverage: '-17169.33',
yesterday: '66458.76',
estimatedAchievement: '209.76%',
dailyDifference: '83628.09',
previousDay: '79558.31',
sequentialRatio: '-1309.55',
timeProgress: '93%',
januaryAchievement: '2216583.797',
januarySequentialRatio: '-28%',
differenceFromTarget: '-20%',
},
{
name: '代理运营总',
monthlyBudget: '8500000',
executed: '7134418.26',
achievementRate: '83.93',
surplusBudget: '1365581.74',
surplusDays: '21',
dailyAverage: '65027.70',
yesterday: '264596.76',
estimatedAchievement: '149.32%',
dailyDifference: '199682.28',
previousDay: '286347.89',
sequentialRatio: '-21651.29',
timeProgress: '54%',
januaryAchievement: '10414758.23',
januarySequentialRatio: '-18%',
differenceFromTarget: '-10%',
},
{
name: '渠道部',
monthlyBudget: '3000000',
executed: '29134092.68',
achievementRate: '97.11',
surplusBudget: '865907.32',
surplusDays: '21',
dailyAverage: '41233.68',
yesterday: '806116.365',
estimatedAchievement: '153.54%',
dailyDifference: '764882.68',
previousDay: '844507.14',
sequentialRatio: '-38390.78',
timeProgress: '67%',
januaryAchievement: '39078360.25',
januarySequentialRatio: '-23%',
differenceFromTarget: '-15%',
},
{
name: '电商一部',
monthlyBudget: '4700000',
executed: '3993326.756',
achievementRate: '83.88',
surplusBudget: '706673.24',
surplusDays: '21',
dailyAverage: '33651.11',
yesterday: '125785.982',
estimatedAchievement: '140.02%',
dailyDifference: '89563.45',
previousDay: '132741.39',
sequentialRatio: '-6955.41',
timeProgress: '54%',
januaryAchievement: '4522665.91',
januarySequentialRatio: '4%',
differenceFromTarget: '15%',
},
{
name: '电商二部',
monthlyBudget: '2800000',
executed: '1302679.056',
achievementRate: '46.52',
surplusBudget: '1497320.94',
surplusDays: '21',
dailyAverage: '7130.10',
yesterday: '26049.373',
estimatedAchievement: '66.06%',
dailyDifference: '-45251.62',
previousDay: '27892.62',
sequentialRatio: '-1843.24',
timeProgress: '17%',
januaryAchievement: '2776056.70',
januarySequentialRatio: '1%',
differenceFromTarget: '12%',
},
{
name: '张哥自营',
monthlyBudget: '6000000',
executed: '9770673.61',
achievementRate: '162.84',
surplusBudget: '-3770673.61',
surplusDays: '21',
dailyAverage: '-179555.89',
yesterday: '392448.463',
estimatedAchievement: '300.20%',
dailyDifference: '572004.35',
previousDay: '331376.53',
sequentialRatio: '61071.93',
timeProgress: '133%',
januaryAchievement: '8310458.83',
januarySequentialRatio: '-28%',
differenceFromTarget: '-20%',
},
{
name: '电商合计',
monthlyBudget: '13500000',
executed: '15012801.94',
achievementRate: '111.21',
surplusBudget: '-1512801.94',
surplusDays: '21',
dailyAverage: '-72038.19',
yesterday: '544283.818',
estimatedAchievement: '195.87%',
dailyDifference: '616316.17',
previousDay: '492286.58',
sequentialRatio: '5207.28',
timeProgress: '81%',
januaryAchievement: '15609181.44',
januarySequentialRatio: '-14%',
differenceFromTarget: '-4%',
},
{
name: '金牛总任务',
monthlyBudget: '52000000',
executed: '51280190.4',
achievementRate: '98.62',
surplusBudget: '719809.60',
surplusDays: '21',
dailyAverage: '34276.65',
yesterday: '1615096.78',
estimatedAchievement: '163.84%',
dailyDifference: '1622865.58',
previousDay: '1622865.58',
sequentialRatio: '-7768.79',
timeProgress: '69%',
januaryAchievement: '65108327.29',
januarySequentialRatio: '-20%',
differenceFromTarget: '-12%',
},
];
const mockData: DepartmentData[] = departments.map((dept) => ({
department: dept.name,
monthlyBudget: dept.monthlyBudget,
executed: dept.executed,
achievementRate: dept.achievementRate,
surplusBudget: dept.surplusBudget,
surplusDays: dept.surplusDays,
dailyAverage: dept.dailyAverage,
yesterday: dept.yesterday,
estimatedAchievement: dept.estimatedAchievement,
dailyDifference: dept.dailyDifference,
previousDay: dept.previousDay,
sequentialRatio: dept.sequentialRatio,
timeProgress: dept.timeProgress,
januaryAchievement: dept.januaryAchievement,
januarySequentialRatio: dept.januarySequentialRatio,
differenceFromTarget: dept.differenceFromTarget,
}));
return mockData;
};
const getTrendTableData = (): TrendData[] => {
const departments = [
{ name: '一区一部', data: '1487551.85' },
{ name: '一区二部', data: '3526692.48' },
{ name: '一区三部', data: '1666811.56' },
{ name: '一区四部', data: '1551268.30' },
{ name: '代理运营总', data: '8232324.19' },
{ name: '渠道部', data: '5976545.88' },
{ name: '电商一部', data: '2159880.32' },
{ name: '电商二部', data: '1365144.30' },
{ name: '张哥自营', data: '5569665.85' },
{ name: '电商合计', data: '9094690.47' },
{ name: '金牛总任务', data: '23303560.56' },
];
return departments.map((dept) => ({
department: dept.name,
data: dept.data,
}));
};
const getProgressTableData = (): ProgressData[] => {
return [
{
date: '2026/4/10',
quarterFirstDay: '2026/4/1',
quarterLastDay: '2026/6/30',
textAudit: '2026/4/1',
homeCount: '91',
completedCount: '9',
remainingDays: '82',
quarterSecondMonth: '2026/5/1',
textAudit2: '2026/3/31',
},
{
date: '2026/4/10',
quarterFirstDay: '2026/4/1',
quarterLastDay: '2026/6/30',
textAudit: '2026/4/1',
homeCount: '30',
completedCount: '9',
remainingDays: '21',
quarterSecondMonth: '2026/5/1',
textAudit2: '2026/3/31',
},
];
};
const getCustomerTableData = (): CustomerData[] => {
return [
{
customer: '客户1',
date1: 18168312,
date2: 18628392,
date3: 3985,
date4: 20786,
date5: 191584,
date6: 359278,
date7: 3870546,
date8: 3873374,
date9: 4457089,
date10: 4295086,
date11: 2672587,
date12: 1569225,
date13: 114875,
date14: 279896,
date15: 396185,
date16: 291112,
date17: 215208,
date18: 0,
date19: 0,
date20: 0,
},
{
customer: '客户2',
date1: 12345678,
date2: 87654321,
date3: 12345,
date4: 54321,
date5: 987654,
date6: 456789,
date7: 2345678,
date8: 8765432,
date9: 3456789,
date10: 9876543,
date11: 4567890,
date12: 1234567,
date13: 234567,
date14: 345678,
date15: 456789,
date16: 567890,
date17: 678901,
date18: 789012,
date19: 890123,
date20: 901234,
},
{
customer: '客户3',
date1: 98765432,
date2: 23456789,
date3: 98765,
date4: 56789,
date5: 1234567,
date6: 7654321,
date7: 3456789,
date8: 9876543,
date9: 4567890,
date10: 12345678,
date11: 87654321,
date12: 23456789,
date13: 9876543,
date14: 4567890,
date15: 1234567,
date16: 7654321,
date17: 3456789,
date18: 9876543,
date19: 4567890,
date20: 1234567,
},
];
};
const calculateTotalStats = (_data: DepartmentData[]) => {
totalStats.totalBudget = '52000000';
totalStats.executed = '51280190.4';
totalStats.overallRate = '98.62';
totalStats.dailyAverage = '1654199.70';
};
const initTrendChart = () => {
nextTick(() => {
if (!trendChartRef.value) return;
if (trendChart) trendChart.dispose();
trendChart = echarts.init(trendChartRef.value);
// 生成动态日期数据
const dates: string[] = [];
const today = new Date();
for (let i = 15; i >= 0; i--) {
const date = new Date(today);
date.setDate(today.getDate() - i);
dates.push(`${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`);
}
// 使用图片中的数据模式,生成动态数据
const executedData = [
2149518.05, 1871119.64, 2088655.19, 1702786.8, 1770269.31, 1714822.74, 1649665.0, 1622865.58, 1615096.78, 1485551.17, 1407212.21, 1364826.0,
1233123.83, 1089680.69, 1086898.47, 816321.92,
];
const targetData = dates.map(() => 1733333.33); // 52000000 / 30
trendChart.setOption({
title: {
text: '预算执行趋势',
left: 'center',
},
tooltip: {
trigger: 'axis',
formatter: '{b}<br/>{a}: ¥{c}',
},
legend: {
data: ['实际执行', '目标'],
bottom: 0,
},
xAxis: {
type: 'category',
data: dates,
axisLabel: {
rotate: 45,
},
},
yAxis: {
type: 'value',
name: '金额',
axisLabel: {
formatter: '¥{value}',
},
},
series: [
{
name: '实际执行',
type: 'line',
data: executedData,
areaStyle: {},
},
{
name: '目标',
type: 'line',
data: targetData,
lineStyle: {
type: 'dashed',
},
},
{
name: '执行柱状图',
type: 'bar',
data: executedData,
itemStyle: {
color: 'rgba(64, 158, 255, 0.5)',
},
},
],
});
});
};
const initDepartmentChart = (data: DepartmentData[]) => {
nextTick(() => {
if (!departmentChartRef.value) return;
if (departmentChart) departmentChart.dispose();
departmentChart = echarts.init(departmentChartRef.value);
// 提取部门和预算数据用于饼图
const pieData = data.map((item) => ({
name: item.department,
value: parseFloat(item.monthlyBudget.replace(/,/g, '')),
}));
departmentChart.setOption({
title: {
text: '部门预算分布',
left: 'center',
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: ¥{c} ({d}%)',
},
legend: {
orient: 'vertical',
right: '5%',
top: 'center',
data: data.map((item) => item.department),
textStyle: {
fontSize: 10,
},
},
series: [
{
name: '预算',
type: 'pie',
radius: ['30%', '60%'],
center: ['40%', '50%'],
data: pieData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
},
],
});
});
};
const initProgressChart = () => {
nextTick(() => {
if (!progressChartRef.value) return;
if (progressChart) progressChart.dispose();
progressChart = echarts.init(progressChartRef.value);
// 任务进度雷达图数据
const indicator = [
{ name: '预算执行', max: 100 },
{ name: '时间进度', max: 100 },
{ name: '部门达成率', max: 100 },
{ name: '日均完成', max: 100 },
{ name: '目标达成', max: 100 },
];
progressChart.setOption({
title: {
text: '任务进度分析',
left: 'center',
},
tooltip: {},
legend: {
data: ['实际进度', '目标进度'],
bottom: 0,
},
radar: {
indicator: indicator,
radius: '60%',
center: ['50%', '50%'],
},
series: [
{
name: '任务进度',
type: 'radar',
data: [
{
value: [98.62, 69, 85.3, 95.2, 98.62],
name: '实际进度',
areaStyle: {
color: 'rgba(64, 158, 255, 0.3)',
},
},
{
value: [100, 100, 100, 100, 100],
name: '目标进度',
areaStyle: {
color: 'rgba(255, 159, 64, 0.3)',
},
},
],
},
],
});
});
};
const initCustomerChart = () => {
nextTick(() => {
if (!customerChartRef.value) return;
if (customerChart) customerChart.dispose();
customerChart = echarts.init(customerChartRef.value);
// 生成日期数据
const dates = [
'2026/2/7',
'2026/2/8',
'2026/2/9',
'2026/2/10',
'2026/2/11',
'2026/2/12',
'2026/2/15',
'2026/2/16',
'2026/2/17',
'2026/2/18',
'2026/2/19',
'2026/2/20',
'2026/2/21',
'2026/2/22',
'2026/2/23',
'2026/2/24',
'2026/2/25',
'2026/2/26',
'2026/2/27',
'2026/2/28',
];
// 模拟客户数据趋势
const customerData = [
18168312, 18628392, 3985, 20786, 191584, 359278, 3870546, 3873374, 4457089, 4295086, 2672587, 1569225, 114875, 279896, 396185, 291112, 215208,
0, 0, 0,
];
customerChart.setOption({
title: {
text: '客户数据趋势',
left: 'center',
},
tooltip: {
trigger: 'axis',
formatter: '{b}<br/>{a}: {c}',
},
legend: {
data: ['客户数据'],
bottom: 0,
},
xAxis: {
type: 'category',
data: dates,
axisLabel: {
rotate: 45,
},
},
yAxis: {
type: 'value',
name: '数值',
},
series: [
{
name: '客户数据',
type: 'line',
data: customerData,
areaStyle: {},
itemStyle: {
color: '#409eff',
},
},
{
name: '数据柱状图',
type: 'bar',
data: customerData,
itemStyle: {
color: 'rgba(64, 158, 255, 0.5)',
},
},
],
});
});
};
const openSettingDialog = () => {
dialogVisible.value = true;
};
const openDepartmentTaskDialog = () => {
departmentTaskDialogVisible.value = true;
};
const handleSaveSettings = () => {
dialogVisible.value = false;
};
const saveDepartmentTasks = () => {
departmentTaskDialogVisible.value = false;
};
const handleSearch = () => {
data.value = getMockData();
departmentTableData.value = data.value;
trendTableData.value = getTrendTableData();
progressTableData.value = getProgressTableData();
customerTableData.value = getCustomerTableData();
calculateTotalStats(data.value);
initTrendChart();
initDepartmentChart(data.value);
initProgressChart();
initCustomerChart();
};
const handleReset = () => {
searchParams.dateRange = [];
searchParams.department = '';
handleSearch();
};
const handleCustomerSearch = () => {
// 这里可以根据搜索参数过滤数据
customerTableData.value = getCustomerTableData();
initCustomerChart();
};
const handleCustomerReset = () => {
customerSearchParams.customer = '';
customerSearchParams.status = '';
handleCustomerSearch();
};
onMounted(() => {
handleSearch();
window.addEventListener('resize', () => {
trendChart?.resize();
departmentChart?.resize();
progressChart?.resize();
customerChart?.resize();
});
});
watch(activeTab, (newTab) => {
nextTick(() => {
if (newTab === 'trend') {
initTrendChart();
} else if (newTab === 'department') {
initDepartmentChart(data.value);
} else if (newTab === 'progress') {
initProgressChart();
} else if (newTab === 'customer') {
initCustomerChart();
}
});
});
</script>
<style scoped>
.ads-summary-monitor {
padding: 20px;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 16px;
font-weight: 600;
}
.setting-btn {
color: #409eff;
}
.search-container {
margin-bottom: 20px;
}
.search-form {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.search-form :deep(.el-form-item) {
margin-right: 12px;
margin-bottom: 12px;
}
.ads-summary-monitor :deep(.el-select) {
width: 180px;
}
.ads-summary-monitor :deep(.el-select__wrapper),
.ads-summary-monitor :deep(.el-select__selected-item),
.ads-summary-monitor :deep(.el-select__placeholder) {
color: #303133;
}
.tabs-container {
margin-top: 20px;
}
.tabs-container :deep(.el-tabs__header) {
margin-bottom: 20px;
}
.chart-container {
margin-bottom: 20px;
}
.chart {
width: 100%;
height: 600px;
min-height: 600px;
}
.table-container {
margin-top: 20px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
}
</style>