浏览代码

reactor:报表调整;

zhangchong 1 年之前
父节点
当前提交
0fee399877

+ 2 - 0
package.json

@@ -24,6 +24,7 @@
 		"@wangeditor/editor-for-vue": "^5.1.12",
 		"axios": "^1.4.0",
 		"dayjs": "^1.11.9",
+		"echarts": "^5.5.0",
 		"element-plus": "~2.4.4",
 		"file-saver": "^2.0.5",
 		"html-docx-js-typescript": "^0.1.5",
@@ -40,6 +41,7 @@
 		"sortablejs": "^1.15.0",
 		"splitpanes": "^3.1.5",
 		"vue": "^3.2.45",
+		"vue-echarts": "^6.6.9",
 		"vue-router": "^4.1.6",
 		"vue3-puzzle-vcode": "^1.1.7",
 		"vue3-seamless-scroll": "^2.0.1",

+ 4 - 0
src/main.ts

@@ -14,6 +14,9 @@ import Empty from '@/components/Empty/index.vue';
 import ProTable from '@/components/ProTable/index.vue';
 import { MotionPlugin } from '@vueuse/motion';
 
+// 注册echarts
+import { registerEcharts } from '@/utils/echarts';
+
 const app = createApp(App);
 
 // 自定义指令和svg组件
@@ -22,4 +25,5 @@ other.elSvg(app);
 // 全局组件挂载
 app.component('Empty', Empty);
 app.component('ProTable', ProTable);
+registerEcharts(app);
 app.use(pinia).use(router).use(ElementPlus).use(MotionPlugin).mount('#app');

+ 2 - 0
src/theme/splitpanes.scss

@@ -6,6 +6,8 @@
 	background-color: var(--el-border-color);
 	position: relative;
 	margin:0 10px;
+	width: 1px;
+	cursor: col-resize;
 }
 .splitpanes__splitter:before {
 	content: '';

+ 24 - 8
src/utils/constants.ts

@@ -46,12 +46,19 @@ export const shortcuts = [
 		text: '上季度',
 		value: () => {
 			let oDate = new Date();
-			let thisYear = oDate.getFullYear();
-			let thisMonth = oDate.getMonth() + 1;
-			let n = Math.ceil(thisMonth / 3); // 季度
-			let Month = n * 3 - 1;
-			let start = new Date(thisYear, Month - 2, 1);
-			let end = new Date();
+			let prevMonth = oDate.getMonth() - 3;
+			const end = new Date();
+			const start = new Date();
+			start.setMonth(prevMonth);
+			start.setDate(1);
+			start.setHours(0);
+			start.setMinutes(0);
+			start.setSeconds(0);
+			end.setMonth(prevMonth + 3);
+			end.setDate(0);
+			end.setHours(23);
+			end.setMinutes(59);
+			end.setSeconds(59);
 			return [start, end];
 		},
 	},
@@ -60,7 +67,16 @@ export const shortcuts = [
 		value: () => {
 			const end = new Date();
 			const start = new Date();
-			start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+			start.setMonth(Math.floor(new Date().getMonth() / 3) * 3);
+			start.setDate(1);
+			start.setHours(0);
+			start.setMinutes(0);
+			start.setSeconds(0);
+			end.setMonth(Math.floor(new Date().getMonth() / 3) * 3 + 3);
+			end.setDate(0);
+			end.setHours(23);
+			end.setMinutes(59);
+			end.setSeconds(59);
 			return [start, end];
 		},
 	},
@@ -127,4 +143,4 @@ export const commonEnum = {
 // 限制日期选择最大时间
 export const disabledDate = (time: Date) => {
 	return time.getTime() < new Date().getTime();
-};
+};

+ 37 - 0
src/utils/echarts.ts

@@ -0,0 +1,37 @@
+import ECharts from 'vue-echarts'
+import {use} from "echarts/core"
+import {
+  CanvasRenderer
+} from 'echarts/renderers'
+import {
+  BarChart, PieChart, MapChart, EffectScatterChart, LineChart, PictorialBarChart, GraphChart, GaugeChart, ScatterChart
+} from 'echarts/charts'
+import {
+  GridComponent,
+  TitleComponent,
+  TooltipComponent,
+  LegendComponent,
+  DatasetComponent,
+  VisualMapComponent,
+  GeoComponent,
+  MarkPointComponent,
+  GraphicComponent,
+} from 'echarts/components'
+
+use([
+  CanvasRenderer,
+  BarChart, PieChart, MapChart, EffectScatterChart, LineChart, PictorialBarChart, GraphChart, GaugeChart, ScatterChart,
+  GridComponent,
+  LegendComponent,
+  TooltipComponent,
+  TitleComponent,
+  DatasetComponent,
+  VisualMapComponent,
+  GeoComponent,
+  MarkPointComponent,
+  GraphicComponent,
+])
+
+export const registerEcharts = (app: any) => {
+  app.component('v-chart', ECharts)
+}

+ 51 - 17
src/utils/formatTime.ts

@@ -10,8 +10,8 @@
  * @returns {string} 返回拼接后的时间字符串
  */
 export function formatDate(date: any, format: string): string {
-	if(!date) return '';
-	date = new Date(date)
+	if (!date) return '';
+	date = new Date(date);
 	let we = date.getDay(); // 星期
 	let z = getWeek(date); // 周
 	let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // 季度
@@ -19,7 +19,7 @@ export function formatDate(date: any, format: string): string {
 		'Y+': date.getFullYear().toString(), // 年
 		'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
 		'd+': date.getDate().toString(), // 日
-		'H+': date.getHours().toString(), // 时	
+		'H+': date.getHours().toString(), // 时
 		'M+': date.getMinutes().toString(), // 分
 		'S+': date.getSeconds().toString(), // 秒
 		'q+': qut, // 季度
@@ -98,32 +98,66 @@ export function formatAxis(param: Date): string {
  * @description param 调用 `formatDuration(秒) 转换成xx:xx:xx的格式
  * @returns {string} 输出 即xx:xx:xx的格式;
  */
-export function formatDuration(time:any,showHour:boolean = true,isMillisecond=false){
-	if(!time) return showHour ? '00:00:00' : '00:00';
-	if(isMillisecond){
+export function formatDuration(time: any, showHour: boolean = true, isMillisecond = false) {
+	if (!time) return showHour ? '00:00:00' : '00:00';
+	if (isMillisecond) {
 		time = Math.floor(time / 1000);
 	}
-	if(time > -1){
+	if (time > -1) {
 		const hour = Math.floor(time / 3600);
 		const min = Math.floor(time / 60) % 60;
 		const sec = time % 60;
-		if(showHour){ // 是否要展示小时
-			if(hour < 10) {
-				time = '0'+ hour + ":";
+		if (showHour) {
+			// 是否要展示小时
+			if (hour < 10) {
+				time = '0' + hour + ':';
 			} else {
-				time = hour + ":";
+				time = hour + ':';
 			}
-		}else{
+		} else {
 			time = '';
 		}
-		if(min < 10){
-			time += "0";
+		if (min < 10) {
+			time += '0';
 		}
-		time += min + ":";
-		if(sec < 10){
-			time += "0";
+		time += min + ':';
+		if (sec < 10) {
+			time += '0';
 		}
 		time += sec;
 	}
 	return time;
 }
+/**
+ * @description 秒转换成xx天:xx小时:xx分钟xx秒格式
+ * @param time 传入秒
+ * @param isMillisecond 是否是毫秒
+ * @returns {string} 输出 即xx天:xx小时:xx分钟xx秒格式
+ */
+export function formatDurationDay(time: any, isMillisecond = false) {
+	if (!time) return '0秒';
+	if (isMillisecond) {
+		time = Math.floor(time / 1000);
+	}
+	if (time > -1) {
+		const day = Math.floor(time / 86400);
+		const hour = Math.floor((time % 86400) / 3600);
+		const min = Math.floor((time % 3600) / 60);
+		const sec = ( time % 60).toFixed(0);
+		let str = '';
+		if (day) {
+			str += day + '天';
+		}
+		if (hour) {
+			str += hour + '小时';
+		}
+		if (min) {
+			str += min + '分钟';
+		}
+		if (sec) {
+			str += sec + '秒';
+		}
+		return str;
+	}
+	return '0秒';
+}

+ 180 - 19
src/views/statistics/call/index.vue

@@ -28,17 +28,43 @@
 			</el-form>
 		</el-card>
 		<el-card shadow="never">
-			<ProTable
-				ref="proTableRef"
-				:columns="columns"
-				:data="state.tableData"
-				@updateTable="queryList"
-				:loading="state.loading"
-				:pagination="false"
-				show-summary
-				border
-			>
-			</ProTable>
+			<el-row :gutter="20">
+				<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
+					<v-chart class="chart" :option="option" :loading="state.loading" autoresize />
+				</el-col>
+				<el-col :xs="24" :sm="12" :md="12" :lg="4" :xl="4">
+					<ProTable
+						ref="proTableRef1"
+						:columns="columns1"
+						:data="state.tableData1"
+						@updateTable="queryList"
+						:loading="state.loading"
+						:pagination="false"
+						show-summary
+						border
+						:tool-button="false"
+					>
+					</ProTable>
+				</el-col>
+				<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+					<v-chart class="chart1" :option="option1" :loading="state.loading" autoresize />
+				</el-col>
+				<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
+					<ProTable
+						ref="proTableRef"
+						:columns="columns"
+						:data="state.tableData"
+						@updateTable="queryList"
+						:loading="state.loading"
+						:pagination="false"
+						show-summary
+						border
+						:tool-button="false"
+            max-height="60vh"
+					>
+					</ProTable>
+				</el-col>
+			</el-row>
 		</el-card>
 	</div>
 </template>
@@ -47,16 +73,22 @@ import { onMounted, reactive, ref } from 'vue';
 import { ElButton, FormInstance } from 'element-plus';
 import { throttle } from '@/utils/tools';
 import { callList } from '@/api/statistics/call';
-import { shortcuts } from '@/utils/constants';
+import { loadingOptions, shortcuts } from "@/utils/constants";
 import dayjs from 'dayjs';
 
 const proTableRef = ref<RefType>(); // 表格ref
+const proTableRef1 = ref<RefType>(); // 表格ref
 // 表格配置项
 const columns = ref<any[]>([
-	{ prop: 'hourRange', label: '时段', align: 'center' },
-	{ prop: 'total', label: '呼入数量', align: 'center', sortable: true },
-	{ prop: 'answered', label: '接通数量', align: 'center', sortable: true },
-	{ prop: 'hanguped', label: '挂断数量', align: 'center', sortable: true },
+	{ prop: 'hourRange', label: '时段', align: 'center', minWidth: 120 },
+	{ prop: 'total', label: '呼入数量', align: 'center' },
+	{ prop: 'answered', label: '接通数量', align: 'center' },
+	{ prop: 'hanguped', label: '挂断数量', align: 'center' },
+]);
+// 表格配置项
+const columns1 = ref<any[]>([
+	{ prop: 'name', label: '', align: 'center' },
+	{ prop: 'value', label: '数量', align: 'center' },
 ]);
 // 定义变量内容
 const ruleFormRef = ref<RefType>(); // 表单ref
@@ -67,6 +99,7 @@ const state = reactive({
 		crTime: [dayjs().startOf('day').format('YYYY-MM-DD'), dayjs().endOf('day').format('YYYY-MM-DD')], //
 	},
 	tableData: [], //表单
+	tableData1: [],
 	loading: false, // 加载
 	total: 0, // 总数
 });
@@ -83,14 +116,47 @@ const queryList = throttle(() => {
 	const request = {
 		StartTime,
 		EndTime,
-		DelayState: state.queryParams.DelayState,
-		PageIndex: state.queryParams.PageIndex,
-		PageSize: state.queryParams.PageSize,
 		Keyword: state.queryParams.Keyword,
 	};
 	callList(request)
 		.then((res: any) => {
 			state.tableData = res.result;
+
+			const xData = state.tableData.map((item: any) => item.hourRange);
+			const inData = state.tableData.map((item: any) => item.total);
+			const connectData = state.tableData.map((item: any) => item.answered);
+			const hangupData = state.tableData.map((item: any) => item.hanguped);
+			setOption1({
+				xData,
+				inData,
+				connectData,
+				hangupData,
+			});
+
+			const allConnectNum = state.tableData.reduce((pre: any, cur: any) => {
+				pre += cur.answered;
+				return pre;
+			}, 0);
+			const allHangupNum = state.tableData.reduce((pre: any, cur: any) => {
+				pre += cur.hanguped;
+				return pre;
+			}, 0);
+			state.tableData1 = state.tableData.length
+				? [
+						{ value: allConnectNum, name: '接通' },
+						{ value: allHangupNum, name: '挂断' },
+				  ]
+				: [];
+			const dataTable = state.tableData.length
+				? [
+						{ value: allConnectNum, name: '接通' },
+						{ value: allHangupNum, name: '挂断' },
+				  ]
+				: [
+						{ value: null, name: '接通' },
+						{ value: null, name: '挂断' },
+				  ];
+			setOption(dataTable);
 			state.loading = false;
 		})
 		.catch(() => {
@@ -103,7 +169,102 @@ const resetQuery = throttle((formEl: FormInstance | undefined) => {
 	formEl.resetFields();
 	queryList();
 }, 300);
+const option = ref<any>({});
+const option1 = ref<any>({});
+// 呼入总体分析图
+const setOption = (data: any) => {
+	option.value = {
+		title: {
+			text: '呼入总体分析图',
+			left: 'center',
+		},
+		backgroundColor: '#fff',
+		tooltip: {
+			formatter: '{b}:{d}%',
+		},
+		legend: [
+			{
+				left: 'left',
+				top: '40',
+				orient: 'vertical',
+				data: ['接通', '挂断'],
+			},
+		],
+		grid: {
+			containLabel: true,
+		},
+		series: [
+			{
+				type: 'pie',
+				radius: ['0%', '40%'],
+				color: ['#86DF6C', '#249EFF'],
+				label: {
+					show: true,
+					formatter: function (params) {
+						if (params.name !== '') {
+							return `${params.name}(${params.data.value}) ${params.percent}%`;
+						}
+					},
+				},
+				data: data,
+			},
+		],
+	};
+};
+// 呼入数量分析图
+const setOption1 = (data: any) => {
+	option1.value = {
+		title: {
+			text: '呼入数量分析图',
+			left: 'center',
+		},
+		tooltip: {
+			trigger: 'axis',
+		},
+		legend: {
+			data: ['呼入数量', '接通数量', '挂断数量'],
+			top: 30,
+		},
+		grid: {
+			left: '13%',
+			right: '4%',
+			top: '20%',
+		},
+		xAxis: {
+			type: 'category',
+			boundaryGap: false,
+			data: data.xData,
+		},
+		yAxis: {
+			type: 'value',
+		},
+		series: [
+			{
+				name: '呼入数量',
+				type: 'line',
+				data: data.inData,
+			},
+			{
+				name: '接通数量',
+				type: 'line',
+				data: data.connectData,
+			},
+			{
+				name: '挂断数量',
+				type: 'line',
+				data: data.hangupData,
+			},
+		],
+	};
+};
 onMounted(() => {
 	queryList();
 });
 </script>
+<style lang="scss" scoped>
+.chart,
+.chart1 {
+	height: 60vh;
+	margin-top: 10px;
+}
+</style>

+ 12 - 14
src/views/statistics/call/telephonist.vue

@@ -54,7 +54,7 @@ import { throttle } from '@/utils/tools';
 import { callAgent } from '@/api/statistics/call';
 import { shortcuts } from '@/utils/constants';
 import dayjs from 'dayjs';
-import { formatDuration } from '@/utils/formatTime';
+import { formatDuration, formatDurationDay } from '@/utils/formatTime';
 
 const proTableRef = ref<RefType>(); // 表格ref
 // 表格配置项
@@ -100,7 +100,6 @@ const columns = ref<any[]>([
 		prop: 'outAnsweredRate',
 		label: '呼出接通率',
 		align: 'center',
-		sortable: 'custom',
 		render: (scope) => <span>{scope.row.outAnsweredRate}%</span>,
 		minWidth: 120,
 	},
@@ -108,27 +107,22 @@ const columns = ref<any[]>([
 		prop: 'outDurationAvg',
 		label: '呼出平均时长',
 		align: 'center',
-		sortable: 'custom',
 		minWidth: 140,
-		render: (scope) => <span>{formatDuration(scope.row.outDurationAvg)}</span>,
 	},
 	{
 		prop: 'loginDuration',
 		label: '登录时长',
 		align: 'center',
-		sortable: 'custom',
-		minWidth: 120,
-		render: (scope) => <span>{formatDuration(scope.row.loginDuration)}</span>,
+		minWidth: 200,
+		render: (scope) => <span>{formatDurationDay(scope.row.loginDuration)}</span>,
 	},
 	{
 		prop: 'restDuration',
-		label: '小休+摘机时长',
+		label: '小休时长',
 		align: 'center',
-		sortable: 'custom',
-		minWidth: 150,
-		render: (scope) => <span>{formatDuration(scope.row.restDuration)}</span>,
+		minWidth: 200,
+		render: (scope) => <span>{formatDurationDay(scope.row.restDuration)}</span>,
 	},
-	{ prop: 'outDurationAvg', label: '摘机次数', align: 'center', sortable: 'custom', minWidth: 120 },
 	{ prop: 'workRate', label: '工作效率', align: 'center', minWidth: 120, render: (scope) => <span>{scope.row.workRate}%</span> },
 ]);
 // 定义变量内容
@@ -192,7 +186,11 @@ const getSummaries = (param: any) => {
 			return;
 		}
 		const values = data.map((item: { [x: string]: any }) => Number(item[column.property]));
-		if (['inAnsweredRate', 'outAnsweredRate', 'inAvailableAnswer'].includes(column.property)) {
+		if (
+			['inAnsweredRate', 'outAnsweredRate', 'inAvailableAnswer', 'outDurationAvg', 'loginDuration', 'restDuration', 'workRate'].includes(
+				column.property
+			)
+		) {
 			//百分比不能计算
 			sums[index] = '';
 			return '';
@@ -210,7 +208,7 @@ const getSummaries = (param: any) => {
 		}
 	});
 	return sums;
-  /*const { columns } = param;
+	/*const { columns } = param;
   const sums: string[] = [];
   columns.forEach((column: { property: string }, index: number) => {
     switch (column.property) {

+ 284 - 284
src/views/statistics/department/orgSatisfied.vue

@@ -1,7 +1,7 @@
 <template>
-  <div class="statistics-department-satisfied-container layout-pd">
-    <!-- 搜索  -->
-<!--    <el-card shadow="never">
+	<div class="statistics-department-satisfied-container layout-pd">
+		<!-- 搜索  -->
+		<!--    <el-card shadow="never">
       <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
         <el-form-item label="部门名称" prop="OrgName">
           <el-input v-model="state.queryParams.OrgName" placeholder="部门名称" clearable @keyup.enter="queryList" class="keyword-input" />
@@ -36,316 +36,316 @@
         </el-form-item>
       </el-form>
     </el-card>-->
-    <el-card shadow="never">
-      <ProTable
-        ref="proTableRef"
-        :columns="columns"
-        :data="state.tableData"
-        @updateTable="queryList"
-        :loading="state.loading"
-        show-summary
-        border
-        :pagination="false"
-        :summary-method="getSummaries"
-      >
-        <template #orgName="{ row }">
-          <span>{{ row.orgName }}</span>
-        </template>
-        <template #verySatisfiedCount="{ row }">
-          <el-button
-            link
-            type="primary"
-            @click="onDetail(row.verySatisfiedKey, row, '非常满意')"
-            v-if="!['市直合计', '区县合计'].includes(row.orgName)"
-          >{{ row.verySatisfiedCount }}</el-button
-          >
-          <span v-else>{{ row.verySatisfiedCount }}</span>
-        </template>
-        <template #satisfiedCount="{ row }">
-          <el-button link type="primary" @click="onDetail(row.satisfiedKey, row, '满意')" v-if="!['市直合计', '区县合计'].includes(row.orgName)">{{
-              row.satisfiedCount
-            }}</el-button>
-          <span v-else>{{ row.satisfiedCount }}</span>
-        </template>
-        <template #regardedAsSatisfiedCount="{ row }">
-          <el-button
-            link
-            type="primary"
-            @click="onDetail(row.regardedAsSatisfiedKey, row, '视为满意')"
-            v-if="!['市直合计', '区县合计'].includes(row.orgName)"
-          >{{ row.regardedAsSatisfiedCount }}</el-button
-          >
-          <span v-else>{{ row.regardedAsSatisfiedCount }}</span>
-        </template>
-        <template #defaultSatisfiedCount="{ row }">
-          <el-button
-            link
-            type="primary"
-            @click="onDetail(row.defaultSatisfiedKey, row, '默认满意')"
-            v-if="!['市直合计', '区县合计'].includes(row.orgName)"
-          >{{ row.defaultSatisfiedCount }}</el-button
-          >
-          <span v-else>{{ row.defaultSatisfiedCount }}</span>
-        </template>
-        <template #noSatisfiedCount="{ row }">
-          <el-button
-            link
-            type="primary"
-            @click="onDetail(row.noSatisfiedKey, row, '不满意')"
-            v-if="!['市直合计', '区县合计'].includes(row.orgName)"
-          >{{ row.noSatisfiedCount }}</el-button
-          >
-          <span v-else>{{ row.noSatisfiedCount }}</span>
-        </template>
-        <template #noEvaluateCount="{ row }">
-          <el-button
-            link
-            type="primary"
-            @click="onDetail(row.noEvaluateKey, row, '未作评价')"
-            v-if="!['市直合计', '区县合计'].includes(row.orgName)"
-          >{{ row.noEvaluateCount }}</el-button
-          >
-          <span v-else>{{ row.noEvaluateCount }}</span>
-        </template>
-        <template #noPutThroughCount="{ row }">
-          <el-button
-            link
-            type="primary"
-            @click="onDetail(row.noPutThroughKey, row, '未接通')"
-            v-if="!['市直合计', '区县合计'].includes(row.orgName)"
-          >{{ row.noPutThroughCount }}</el-button
-          >
-          <span v-else>{{ row.noPutThroughCount }}</span>
-        </template>
-      </ProTable>
-    </el-card>
-  </div>
+		<el-card shadow="never">
+			<ProTable
+				ref="proTableRef"
+				:columns="columns"
+				:data="state.tableData"
+				@updateTable="queryList"
+				:loading="state.loading"
+				show-summary
+				border
+				:pagination="false"
+				:summary-method="getSummaries"
+			>
+				<template #orgName="{ row }">
+					<span>{{ row.orgName }}</span>
+				</template>
+				<template #verySatisfiedCount="{ row }">
+					<el-button
+						link
+						type="primary"
+						@click="onDetail(row.verySatisfiedKey, row, '非常满意')"
+						v-if="!['市直合计', '区县合计'].includes(row.orgName)"
+						>{{ row.verySatisfiedCount }}</el-button
+					>
+					<span v-else>{{ row.verySatisfiedCount }}</span>
+				</template>
+				<template #satisfiedCount="{ row }">
+					<el-button link type="primary" @click="onDetail(row.satisfiedKey, row, '满意')" v-if="!['市直合计', '区县合计'].includes(row.orgName)">{{
+						row.satisfiedCount
+					}}</el-button>
+					<span v-else>{{ row.satisfiedCount }}</span>
+				</template>
+				<template #regardedAsSatisfiedCount="{ row }">
+					<el-button
+						link
+						type="primary"
+						@click="onDetail(row.regardedAsSatisfiedKey, row, '视为满意')"
+						v-if="!['市直合计', '区县合计'].includes(row.orgName)"
+						>{{ row.regardedAsSatisfiedCount }}</el-button
+					>
+					<span v-else>{{ row.regardedAsSatisfiedCount }}</span>
+				</template>
+				<template #defaultSatisfiedCount="{ row }">
+					<el-button
+						link
+						type="primary"
+						@click="onDetail(row.defaultSatisfiedKey, row, '默认满意')"
+						v-if="!['市直合计', '区县合计'].includes(row.orgName)"
+						>{{ row.defaultSatisfiedCount }}</el-button
+					>
+					<span v-else>{{ row.defaultSatisfiedCount }}</span>
+				</template>
+				<template #noSatisfiedCount="{ row }">
+					<el-button
+						link
+						type="primary"
+						@click="onDetail(row.noSatisfiedKey, row, '不满意')"
+						v-if="!['市直合计', '区县合计'].includes(row.orgName)"
+						>{{ row.noSatisfiedCount }}</el-button
+					>
+					<span v-else>{{ row.noSatisfiedCount }}</span>
+				</template>
+				<template #noEvaluateCount="{ row }">
+					<el-button
+						link
+						type="primary"
+						@click="onDetail(row.noEvaluateKey, row, '未作评价')"
+						v-if="!['市直合计', '区县合计'].includes(row.orgName)"
+						>{{ row.noEvaluateCount }}</el-button
+					>
+					<span v-else>{{ row.noEvaluateCount }}</span>
+				</template>
+				<template #noPutThroughCount="{ row }">
+					<el-button
+						link
+						type="primary"
+						@click="onDetail(row.noPutThroughKey, row, '未接通')"
+						v-if="!['市直合计', '区县合计'].includes(row.orgName)"
+						>{{ row.noPutThroughCount }}</el-button
+					>
+					<span v-else>{{ row.noPutThroughCount }}</span>
+				</template>
+			</ProTable>
+		</el-card>
+	</div>
 </template>
 <script setup lang="tsx" name="statisticsDepartmentSatisfied">
 import { onMounted, reactive, ref } from 'vue';
 import { ElButton, ElMessage, FormInstance } from 'element-plus';
 import { throttle } from '@/utils/tools';
-import { departmentSatisfaction } from '@/api/statistics/department';
+import { departmentSatisfactionOrg } from '@/api/statistics/department';
 import dayjs from 'dayjs';
 import { useRouter } from 'vue-router';
 
 // 表格配置项
 const columns = ref<any[]>([
-  { type: 'index', fixed: 'left', width: 55, label: '序号', align: 'center' },
-  { prop: 'orgName', label: '部门名称', minWidth: 200 },
-  { prop: 'orgTypeText', label: '部门类别', minWidth: 120 },
-  { prop: 'totalSumCount', label: '小计' },
-  {
-    prop: 'totalSumRate',
-    label: '总满意率',
-    minWidth: 120,
-    render: ({ row }) => {
-      return `${row.totalSumRate}%`;
-    },
-  },
-  { prop: 'verySatisfiedCount', label: '非常满意', minWidth: 120 },
-  {
-    prop: 'verySatisfiedRate',
-    label: '非常满意率',
-    minWidth: 120,
-    render: ({ row }) => {
-      return `${row.verySatisfiedRate}%`;
-    },
-  },
-  { prop: 'satisfiedCount', label: '满意', minWidth: 120 },
-  {
-    prop: 'satisfiedRate',
-    label: '满意率',
-    minWidth: 120,
-    render: ({ row }) => {
-      return `${row.satisfiedRate}%`;
-    },
-  },
-  { prop: 'regardedAsSatisfiedCount', label: '视为满意', minWidth: 120 },
-  {
-    prop: 'regardedAsSatisfiedRate',
-    label: '视为满意率',
-    minWidth: 120,
-    render: ({ row }) => {
-      return `${row.regardedAsSatisfiedRate}%`;
-    },
-  },
-  { prop: 'defaultSatisfiedCount', label: '默认满意', minWidth: 120 },
-  {
-    prop: 'defaultSatisfiedRate',
-    label: '默认满意率',
-    minWidth: 120,
-    render: ({ row }) => {
-      return `${row.defaultSatisfiedRate}%`;
-    },
-  },
-  { prop: 'noSatisfiedCount', label: '不满意', minWidth: 120 },
-  {
-    prop: 'noSatisfiedRate',
-    label: '不满意率',
-    minWidth: 120,
-    render: ({ row }) => {
-      return `${row.noSatisfiedRate}%`;
-    },
-  },
-  { prop: 'noEvaluateCount', label: '未作评价', minWidth: 120 },
-  {
-    prop: 'noEvaluateRate',
-    label: '未作评价率',
-    minWidth: 120,
-    render: ({ row }) => {
-      return `${row.noEvaluateRate}%`;
-    },
-  },
-  { prop: 'noPutThroughCount', label: '未接通', minWidth: 120 },
-  {
-    prop: 'noPutThroughRate',
-    label: '未接通率',
-    minWidth: 120,
-    render: ({ row }) => {
-      return `${row.noPutThroughRate}%`;
-    },
-  },
+	{ type: 'index', fixed: 'left', width: 55, label: '序号', align: 'center' },
+	{ prop: 'orgName', label: '部门名称', minWidth: 200 },
+	{ prop: 'orgTypeText', label: '部门类别', minWidth: 120 },
+	{ prop: 'totalSumCount', label: '小计' },
+	{
+		prop: 'totalSumRate',
+		label: '总满意率',
+		minWidth: 120,
+		render: ({ row }) => {
+			return `${row.totalSumRate}%`;
+		},
+	},
+	{ prop: 'verySatisfiedCount', label: '非常满意', minWidth: 120 },
+	{
+		prop: 'verySatisfiedRate',
+		label: '非常满意率',
+		minWidth: 120,
+		render: ({ row }) => {
+			return `${row.verySatisfiedRate}%`;
+		},
+	},
+	{ prop: 'satisfiedCount', label: '满意', minWidth: 120 },
+	{
+		prop: 'satisfiedRate',
+		label: '满意率',
+		minWidth: 120,
+		render: ({ row }) => {
+			return `${row.satisfiedRate}%`;
+		},
+	},
+	{ prop: 'regardedAsSatisfiedCount', label: '视为满意', minWidth: 120 },
+	{
+		prop: 'regardedAsSatisfiedRate',
+		label: '视为满意率',
+		minWidth: 120,
+		render: ({ row }) => {
+			return `${row.regardedAsSatisfiedRate}%`;
+		},
+	},
+	{ prop: 'defaultSatisfiedCount', label: '默认满意', minWidth: 120 },
+	{
+		prop: 'defaultSatisfiedRate',
+		label: '默认满意率',
+		minWidth: 120,
+		render: ({ row }) => {
+			return `${row.defaultSatisfiedRate}%`;
+		},
+	},
+	{ prop: 'noSatisfiedCount', label: '不满意', minWidth: 120 },
+	{
+		prop: 'noSatisfiedRate',
+		label: '不满意率',
+		minWidth: 120,
+		render: ({ row }) => {
+			return `${row.noSatisfiedRate}%`;
+		},
+	},
+	{ prop: 'noEvaluateCount', label: '未作评价', minWidth: 120 },
+	{
+		prop: 'noEvaluateRate',
+		label: '未作评价率',
+		minWidth: 120,
+		render: ({ row }) => {
+			return `${row.noEvaluateRate}%`;
+		},
+	},
+	{ prop: 'noPutThroughCount', label: '未接通', minWidth: 120 },
+	{
+		prop: 'noPutThroughRate',
+		label: '未接通率',
+		minWidth: 120,
+		render: ({ row }) => {
+			return `${row.noPutThroughRate}%`;
+		},
+	},
 ]);
 // 定义变量内容
 const ruleFormRef = ref<RefType>(); // 表单ref
 const state = reactive({
-  queryParams: {
-    // 查询条件
-    PageIndex: 1,
-    PageSize: 10,
-    TypeId: '1', //
-    LineNum: null,
-    crTime: [dayjs().startOf('day').format('YYYY-MM-DD'), dayjs().endOf('day').format('YYYY-MM-DD')], // 时间默认今天开始到今天结束
-  },
-  tableData: [], //表单
-  loading: false, // 加载
-  total: 0, // 总数
-  totalCount: {},
+	queryParams: {
+		// 查询条件
+		PageIndex: 1,
+		PageSize: 10,
+		TypeId: '1', //
+		LineNum: null,
+		crTime: [dayjs().startOf('day').format('YYYY-MM-DD'), dayjs().endOf('day').format('YYYY-MM-DD')], // 时间默认今天开始到今天结束
+	},
+	tableData: [], //表单
+	loading: false, // 加载
+	total: 0, // 总数
+	totalCount: {},
 });
 /** 获取列表 */
 const historyParams = history.state;
 const queryList = throttle(() => {
-  state.loading = true;
-  const request = {
-    StartDate: historyParams.StartDate,
-    EndDate: historyParams.EndDate,
-    TypeId: historyParams.TypeId,
-    LineNum: historyParams.LineNum,
-    OrgCode: historyParams.OrgCode,
-    DateValue: historyParams.DateValue,
-  };
-  departmentSatisfaction(request)
-    .then((res: any) => {
-      state.tableData = res.result?.dataList ?? [];
-      if (res.result.dataList.length) {
-        state.tableData.push(res.result.citySumModel);
-        state.tableData.push(res.result.countySumModel);
-      }
-      state.totalCount = res.result.sumModel;
-      state.loading = false;
-    })
-    .catch((err: any) => {
-      state.loading = false;
-    });
+	state.loading = true;
+	const request = {
+		StartDate: historyParams.StartDate,
+		EndDate: historyParams.EndDate,
+		TypeId: historyParams.TypeId,
+		LineNum: historyParams.LineNum,
+		OrgCode: historyParams.OrgCode,
+		DateValue: historyParams.DateValue,
+	};
+	departmentSatisfactionOrg(request)
+		.then((res: any) => {
+			state.tableData = res.result?.dataList ?? [];
+			if (res.result.dataList.length) {
+				state.tableData.push(res.result.citySumModel);
+				state.tableData.push(res.result.countySumModel);
+			}
+			state.totalCount = res.result.sumModel;
+			state.loading = false;
+		})
+		.catch((err: any) => {
+			state.loading = false;
+		});
 }, 300);
 /** 重置按钮操作 */
 const resetQuery = throttle((formEl: FormInstance | undefined) => {
-  if (!formEl) return;
-  formEl.resetFields();
-  queryList();
+	if (!formEl) return;
+	formEl.resetFields();
+	queryList();
 }, 300);
 // 合计
 const getSummaries = (param: any) => {
-  const { columns } = param;
-  const sums: string[] = [];
-  columns.forEach((column: { property: string }, index: number) => {
-    if (index === 0) {
-      sums[index] = '合计';
-      return;
-    }
-    switch (column.property) {
-      case 'totalSumCount':
-        sums[index] = state.totalCount?.totalSumCount;
-        break;
-      case 'totalSumRate':
-        sums[index] = `${state.totalCount?.totalSumRate}%`;
-        break;
-      case 'verySatisfiedCount':
-        sums[index] = state.totalCount?.verySatisfiedCount;
-        break;
-      case 'verySatisfiedRate':
-        sums[index] = `${state.totalCount?.verySatisfiedRate}%`;
-        break;
-      case 'satisfiedCount':
-        sums[index] = state.totalCount?.satisfiedCount;
-        break;
-      case 'satisfiedRate':
-        sums[index] = `${state.totalCount?.satisfiedRate}%`;
-        break;
-      case 'regardedAsSatisfiedCount':
-        sums[index] = state.totalCount?.regardedAsSatisfiedCount;
-        break;
-      case 'regardedAsSatisfiedRate':
-        sums[index] = `${state.totalCount?.regardedAsSatisfiedRate}%`;
-        break;
-      case 'defaultSatisfiedCount':
-        sums[index] = state.totalCount?.defaultSatisfiedCount;
-        break;
-      case 'defaultSatisfiedRate':
-        sums[index] = `${state.totalCount?.defaultSatisfiedRate}%`;
-        break;
-      case 'noSatisfiedCount':
-        sums[index] = state.totalCount?.noSatisfiedCount;
-        break;
-      case 'noSatisfiedRate':
-        sums[index] = `${state.totalCount?.noSatisfiedRate}%`;
-        break;
-      case 'noEvaluateCount':
-        sums[index] = state.totalCount?.noEvaluateCount;
-        break;
-      case 'noEvaluateRate':
-        sums[index] = `${state.totalCount?.noEvaluateRate}%`;
-        break;
-      case 'noPutThroughCount':
-        sums[index] = state.totalCount?.noPutThroughCount;
-        break;
-      case 'noPutThroughRate':
-        sums[index] = `${state.totalCount?.noPutThroughRate}%`;
-        break;
-      default:
-        sums[index] = '';
-        break;
-    }
-  });
-  return sums;
+	const { columns } = param;
+	const sums: string[] = [];
+	columns.forEach((column: { property: string }, index: number) => {
+		if (index === 0) {
+			sums[index] = '合计';
+			return;
+		}
+		switch (column.property) {
+			case 'totalSumCount':
+				sums[index] = state.totalCount?.totalSumCount;
+				break;
+			case 'totalSumRate':
+				sums[index] = `${state.totalCount?.totalSumRate}%`;
+				break;
+			case 'verySatisfiedCount':
+				sums[index] = state.totalCount?.verySatisfiedCount;
+				break;
+			case 'verySatisfiedRate':
+				sums[index] = `${state.totalCount?.verySatisfiedRate}%`;
+				break;
+			case 'satisfiedCount':
+				sums[index] = state.totalCount?.satisfiedCount;
+				break;
+			case 'satisfiedRate':
+				sums[index] = `${state.totalCount?.satisfiedRate}%`;
+				break;
+			case 'regardedAsSatisfiedCount':
+				sums[index] = state.totalCount?.regardedAsSatisfiedCount;
+				break;
+			case 'regardedAsSatisfiedRate':
+				sums[index] = `${state.totalCount?.regardedAsSatisfiedRate}%`;
+				break;
+			case 'defaultSatisfiedCount':
+				sums[index] = state.totalCount?.defaultSatisfiedCount;
+				break;
+			case 'defaultSatisfiedRate':
+				sums[index] = `${state.totalCount?.defaultSatisfiedRate}%`;
+				break;
+			case 'noSatisfiedCount':
+				sums[index] = state.totalCount?.noSatisfiedCount;
+				break;
+			case 'noSatisfiedRate':
+				sums[index] = `${state.totalCount?.noSatisfiedRate}%`;
+				break;
+			case 'noEvaluateCount':
+				sums[index] = state.totalCount?.noEvaluateCount;
+				break;
+			case 'noEvaluateRate':
+				sums[index] = `${state.totalCount?.noEvaluateRate}%`;
+				break;
+			case 'noPutThroughCount':
+				sums[index] = state.totalCount?.noPutThroughCount;
+				break;
+			case 'noPutThroughRate':
+				sums[index] = `${state.totalCount?.noPutThroughRate}%`;
+				break;
+			default:
+				sums[index] = '';
+				break;
+		}
+	});
+	return sums;
 };
 const router = useRouter();
 // 点击数字
 const onDetail = (key: string, row, type: string) => {
-  let StartDate = null;
-  let EndDate = null;
-  if (state.queryParams?.crTime) {
-    StartDate = state.queryParams?.crTime[0];
-    EndDate = state.queryParams?.crTime[1];
-  }
-  router.push({
-    name: 'statisticsDepartmentSatisfiedDetail',
-    params: {
-      key: key,
-      tagsViewName: '部门满意度统计明细',
-    },
-    state: {
-      StartDate,
-      EndDate,
-      OrgCode: row.orgCode,
-      TypeId: state.queryParams.TypeId,
-      LineNum: state.queryParams.LineNum,
-      DateValue: key,
-    },
-  });
+	let StartDate = null;
+	let EndDate = null;
+	if (state.queryParams?.crTime) {
+		StartDate = state.queryParams?.crTime[0];
+		EndDate = state.queryParams?.crTime[1];
+	}
+	router.push({
+		name: 'statisticsDepartmentSatisfiedDetail',
+		params: {
+			key: key,
+			tagsViewName: '部门满意度统计明细',
+		},
+		state: {
+			StartDate,
+			EndDate,
+			OrgCode: row.orgCode,
+			TypeId: state.queryParams.TypeId,
+			LineNum: state.queryParams.LineNum,
+			DateValue: key,
+		},
+	});
 };
 onMounted(() => {
-  queryList();
+	queryList();
 });
 </script>

+ 1 - 1
src/views/system/user/index.vue

@@ -113,7 +113,7 @@
 </template>
 
 <script lang="tsx" setup name="systemUser">
-import { defineAsyncComponent, nextTick, onActivated, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
+import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
 import type { FormInstance } from 'element-plus';
 import { ElButton, ElMessage, ElMessageBox } from 'element-plus';
 import { formatDate } from '@/utils/formatTime';

+ 38 - 0
yarn.lock

@@ -2275,6 +2275,14 @@ dotenv@^16.0.3, dotenv@^16.3.2:
   resolved "https://registry.npmmirror.com/dotenv/-/dotenv-16.4.5.tgz"
   integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
 
+echarts@^5.5.0:
+  version "5.5.0"
+  resolved "https://registry.npmmirror.com/echarts/-/echarts-5.5.0.tgz#c13945a7f3acdd67c134d8a9ac67e917830113ac"
+  integrity sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==
+  dependencies:
+    tslib "2.3.0"
+    zrender "5.5.0"
+
 electron-to-chromium@^1.4.601, electron-to-chromium@^1.4.668:
   version "1.4.701"
   resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.701.tgz#7335e5761331774b4dea54cd24a1b84861d45cdf"
@@ -4545,6 +4553,11 @@ requires-port@^1.0.0:
   resolved "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz"
   integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
 
+resize-detector@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz#fe495112e184695500a8f51e0389f15774cb1cfc"
+  integrity sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==
+
 resolve-cwd@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmmirror.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz"
@@ -5065,6 +5078,11 @@ tr46@~0.0.3:
   resolved "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz"
   integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
 
+tslib@2.3.0:
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
+  integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
+
 tslib@2.4.0:
   version "2.4.0"
   resolved "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz"
@@ -5287,6 +5305,19 @@ vue-demi@^0.12.1:
   resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.12.5.tgz"
   integrity sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==
 
+vue-demi@^0.13.11:
+  version "0.13.11"
+  resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99"
+  integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==
+
+vue-echarts@^6.6.9:
+  version "6.6.9"
+  resolved "https://registry.npmmirror.com/vue-echarts/-/vue-echarts-6.6.9.tgz#151372ecd086db985dafeeebd3ea83c8d4d2846b"
+  integrity sha512-mojIq3ZvsjabeVmDthhAUDV8Kgf2Rr/X4lV4da7gEFd1fP05gcSJ0j7wa7HQkW5LlFmF2gdCJ8p4Chas6NNIQQ==
+  dependencies:
+    resize-detector "^0.3.0"
+    vue-demi "^0.13.11"
+
 vue-eslint-parser@^9.1.0, vue-eslint-parser@^9.4.2:
   version "9.4.2"
   resolved "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz#02ffcce82042b082292f2d1672514615f0d95b6d"
@@ -5543,3 +5574,10 @@ yocto-queue@^0.1.0:
   version "0.1.0"
   resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz"
   integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+
+zrender@5.5.0:
+  version "5.5.0"
+  resolved "https://registry.npmmirror.com/zrender/-/zrender-5.5.0.tgz#54d0d6c4eda81a96d9f60a9cd74dc48ea026bc1e"
+  integrity sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==
+  dependencies:
+    tslib "2.3.0"