Ver código fonte

reactor:194 市州通用-优化首页布局;30 多人并行 新增统计表 前三个完成;

zhangchong 4 meses atrás
pai
commit
402d58d25b

+ 78 - 0
src/api/statistics/call.ts

@@ -310,4 +310,82 @@ export const callDateSimpleDetailBase = (params?: object) => {
 		method: 'get',
 		params,
 	});
+}
+/**
+ * @description 话务日期统计明细列表
+ * @param {object} params
+ */
+export const callDateDetail = (params: object) => {
+	return request({
+		url: `/api/v1/BiCall/query-calldate-statistics`,
+		method: 'get',
+		params,
+	});
+}
+/**
+ * @description 话务日期统计明细列表导出
+ * @param {object} data
+ */
+export const callDateDetailExport = (data: object) => {
+	return request({
+		url: `/api/v1/BiCall/query-calldate-statistics/export`,
+		method: 'post',
+		data,
+		responseType: 'blob',
+	}, {
+		reduce_data_format: false
+	}
+	)
+}
+/**
+ * @description 个人服务话务明细
+ * @param {object} params
+ */
+export const callPersonal = (params: object) => {
+	return request({
+		url: `/api/v1/BiCall/query-person-calldate-statistics`,
+		method: 'get',
+		params,
+	});
+}
+/**
+ * @description 个人服务话务明细导出
+ * @param {object} data
+ */
+export const callPersonalExport = (data: object) => {
+	return request({
+		url: `/api/v1/BiCall/query-person-calldate-statistics/export`,
+		method: 'post',
+		data,
+		responseType: 'blob',
+	}, {
+		reduce_data_format: false
+	}
+	)
+}
+/**
+ * @description 企业服务话务明细
+ * @param {object} params
+ */
+export const callCompany = (params: object) => {
+	return request({
+		url: `/api/v1/BiCall/query-enterprise-calldate-statistics`,
+		method: 'get',
+		params,
+	});
+}
+/**
+ * @description 企业服务话务明细导出
+ * @param {object} data
+ */
+ export const callCompanyExport = (data: object) => {
+	return request({
+		url: `/api/v1/BiCall/query-enterprise-calldate-statistics/export`,
+		method: 'post',
+		data,
+		responseType: 'blob',
+	}, {
+		reduce_data_format: false
+	}
+	)
 }

+ 0 - 1
src/components/LogicFlow/PropertySetting/end.vue

@@ -351,7 +351,6 @@ defineExpose({
 });
 </script>
 <style lang="scss">
-@import '@wsfe/vue-tree/style.css';
 .vtree-tree-drop__wrapper {
 	width: 100%;
 }

+ 0 - 1
src/components/LogicFlow/PropertySetting/start.vue

@@ -350,7 +350,6 @@ defineExpose({
 });
 </script>
 <style lang="scss">
-@import '@wsfe/vue-tree/style.css';
 .vtree-tree-drop__wrapper {
 	width: 100%;
 }

+ 0 - 1
src/components/LogicFlow/PropertySetting/task.vue

@@ -383,7 +383,6 @@ defineExpose({
 });
 </script>
 <style lang="scss">
-@import '@wsfe/vue-tree/style.css';
 .vtree-tree-drop__wrapper {
 	width: 100%;
 }

+ 3 - 0
src/main.ts

@@ -18,6 +18,9 @@ import { VxeTable, VxeColumn, VxeToolbar, VxeGrid, VxeColgroup } from 'vxe-table
 // 导入主题变量,也可以重写主题变量
 import 'vxe-table/styles/cssvar.scss';
 import 'vxe-pc-ui/styles/cssvar.scss';
+// 虚拟数的css
+import '@wsfe/vue-tree/style.css';
+
 // 导入默认的语言
 import zhCN from 'vxe-pc-ui/lib/language/zh-CN';
 

+ 1 - 0
src/theme/app.scss

@@ -536,4 +536,5 @@ li {
 // 树形选择字体颜色
 .vtree-tree-node__title{
 	color: var(--el-text-color-regular);
+	font-size: var(--el-font-size-base);
 }

+ 4 - 4
src/views/home/index.vue

@@ -5,11 +5,11 @@
 				<el-col class="mb10">
 					<home-date />
 				</el-col>
-				<el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16">
-					<TodoCenter v-if="userInfos.isCenter"/>
-					<TodoDepartment v-else/>
+				<el-col :xs="24" :sm="24" :md="24" :lg="17" :xl="17">
+					<TodoCenter v-if="userInfos.isCenter" />
+					<TodoDepartment v-else />
 				</el-col>
-				<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
+				<el-col :xs="24" :sm="24" :md="24" :lg="7" :xl="7">
 					<!-- 常用入口 -->
 					<entrance />
 					<!-- 通知公告 -->

+ 0 - 1
src/views/knowledge/index/edit.vue

@@ -676,7 +676,6 @@ onMounted(async () => {
 });
 </script>
 <style lang="scss">
-@import '@wsfe/vue-tree/style.css';
 .vtree-tree-drop__wrapper {
 	width: 100%;
 }

+ 167 - 0
src/views/statistics/call/businessSeatsDetail.vue

@@ -0,0 +1,167 @@
+<template>
+	<div class="statistics-call-business-seats-detail-container layout-padding">
+		<div class="layout-padding-auto layout-padding-view pd20">
+			<vxe-grid v-bind="gridOptions">
+				<template #form>
+					<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline :disabled="gridOptions.loading">
+						<el-form-item prop="crTime">
+							<statistical-time v-model="state.queryParams.crTime"	@change="handleQuery" ref="statisticalTimeRef" :disabled="state.loading"/>
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
+							<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
+								<SvgIcon name="ele-Refresh" class="mr5" />重置
+							</el-button>
+						</el-form-item>
+					</el-form>
+				</template>
+			</vxe-grid>
+		</div>
+	</div>
+</template>
+<script setup lang="tsx" name="statisticsCallBusinessSeatsDetail">
+import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
+import { FormInstance } from 'element-plus';
+import { defaultDate } from '@/utils/constants';
+import Other from '@/utils/other';
+import { callCompany, callCompanyExport } from '@/api/statistics/call';
+import XEUtils from 'xe-utils';
+
+const StatisticalTime = defineAsyncComponent(() => import('@/components/StatisticalTime/index.vue')); // 日期类型选择组件
+// 定义变量内容
+const ruleFormRef = ref<RefType>(); // 表单ref
+const state = reactive<any>({
+	queryParams: {
+		// 查询条件
+		crTime: defaultDate,
+	},
+	tableData: [], //表单
+	loading: false, // 加载
+	total: 0, // 总数
+	callForwardingSource: [],
+	totalCount: {},
+});
+
+const requestParams = ref<EmptyObjectType>({});
+const gridOptions = reactive<any>({
+	loading: false,
+	border: true,
+	showOverflow: true,
+	columnConfig: {
+		resizable: true,
+	},
+	scrollY: {
+		enabled: true,
+		gt: 100,
+	},
+	toolbarConfig: {
+		zoom: true,
+		custom: true,
+		refresh: {
+			queryMethod: () => {
+				handleQuery();
+			},
+		},
+		tools: [{ toolRender: { name: 'exportAll' } }],
+	},
+	customConfig: {
+		storage: true,
+	},
+	id: 'statisticsCallBusinessSeatsDetail',
+	rowConfig: { isHover: true, height: 30, isCurrent: true, useKey: true },
+	height: 'auto',
+	align: 'center',
+	columns: [
+		{
+			field: 'date',
+			title: '日期',
+		},
+		{
+			field: 'enterpriseCallInCount',
+			title: '企业服务呼入总量',
+		},
+		{
+			field: 'enterpriseCallInPutthroughCount',
+			title: '企业服务接通量',
+		},
+		{
+			field: 'enterpriseRingOffCount',
+			title: '企业服务挂断总量',
+		},
+		{
+			title: '挂断类型',
+			children:[
+				{
+					field: 'enterpriseQueueOffCount',
+					title: '企业服务队列挂断',
+				},
+				{
+					field: 'enterpriseWaitOffCount',
+					title: '企业服务等待挂断',
+				}
+			]
+		},
+		{
+			field: 'enterpriseCallPutthorughRateText',
+			title: '企业服务接通率',
+		},
+	],
+	data: [],
+	params: {
+		exportMethod: callCompanyExport,
+		exportParams: requestParams,
+	},
+	sortConfig: {
+		remote: true,
+	},
+	showFooter:true,
+	footerMethod:  ({ columns, data }) => {
+		return [
+			columns.map((column: any, columnIndex: number) => {
+				if (columnIndex === 0) {
+					return '合计';
+				}
+				// 后端返回了数据集合 state.totalCount 所以不需要计算 直接进行赋值
+				return XEUtils.get(state.totalCount, column.property);
+			}),
+		];
+	}
+});
+/** 搜索按钮操作 */
+const handleQuery = () => {
+	// state.queryParams.PageIndex = 1;
+	queryList();
+};
+/** 获取列表 */
+const queryList = async () => {
+	state.loading = true;
+	gridOptions.loading = true;
+	try {
+		requestParams.value = Other.deepClone(state.queryParams);
+		requestParams.value.StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
+		requestParams.value.EndTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
+		Reflect.deleteProperty(requestParams.value, 'crTime');
+		const { result } = await callCompany(requestParams.value);
+		state.tableData = result.list ?? [];
+		state.totalCount = result.total;
+		gridOptions.data = state.tableData;
+		state.loading = false;
+		gridOptions.loading = false;
+	} catch (e) {
+		state.loading = false;
+		gridOptions.loading = false;
+		console.log(e);
+	}
+};
+/** 重置按钮操作 */
+const statisticalTimeRef = ref<RefType>();
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	statisticalTimeRef.value.reset();
+	queryList();
+};
+onMounted(() => {
+	queryList();
+});
+</script>

+ 213 - 0
src/views/statistics/call/callOutDetail.vue

@@ -0,0 +1,213 @@
+<template>
+	<div class="statistics-call-call-out-detail-container layout-padding">
+		<div class="layout-padding-auto layout-padding-view pd20">
+			<vxe-grid v-bind="gridOptions">
+				<template #form>
+					<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline :disabled="gridOptions.loading">
+						<el-form-item prop="crTime">
+							<statistical-time v-model="state.queryParams.crTime"	@change="handleQuery" ref="statisticalTimeRef" :disabled="state.loading"/>
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
+							<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
+								<SvgIcon name="ele-Refresh" class="mr5" />重置
+							</el-button>
+						</el-form-item>
+					</el-form>
+				</template>
+			</vxe-grid>
+		</div>
+	</div>
+</template>
+<script setup lang="tsx" name="statisticsCallCallOutDetail">
+import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
+import { FormInstance } from 'element-plus';
+import { defaultDate } from '@/utils/constants';
+import Other from '@/utils/other';
+import { callDateSimple, callDateSimpleExport } from '@/api/statistics/call';
+import XEUtils from 'xe-utils';
+
+const StatisticalTime = defineAsyncComponent(() => import('@/components/StatisticalTime/index.vue')); // 日期类型选择组件
+// 定义变量内容
+const ruleFormRef = ref<RefType>(); // 表单ref
+const state = reactive<any>({
+	queryParams: {
+		// 查询条件
+		crTime: defaultDate,
+	},
+	tableData: [], //表单
+	loading: false, // 加载
+	total: 0, // 总数
+	callForwardingSource: [],
+	totalCount: {},
+});
+
+const requestParams = ref<EmptyObjectType>({});
+const gridOptions = reactive<any>({
+	loading: false,
+	border: true,
+	showOverflow: true,
+	columnConfig: {
+		resizable: true,
+	},
+	scrollY: {
+		enabled: true,
+		gt: 100,
+	},
+	toolbarConfig: {
+		zoom: true,
+		custom: true,
+		refresh: {
+			queryMethod: () => {
+				handleQuery();
+			},
+		},
+		tools: [{ toolRender: { name: 'exportAll' } }],
+	},
+	customConfig: {
+		storage: true,
+	},
+	id: 'statisticsCallCallOutDetail',
+	rowConfig: { isHover: true, height: 30, isCurrent: true, useKey: true },
+	height: 'auto',
+	align: 'center',
+	columns: [
+		{
+			field: 'date',
+			title: '日期',
+		},
+		{
+			field: 'callInTotal',
+			title: '呼出总量',
+		},
+		{
+			title:'呼入类型',
+			children:[
+				{
+					title: '个人服务呼出总量',
+					field: 'personCallInCount',
+				},
+				{
+					field: 'enterpriseCallInCount',
+					title: '企业服务呼出总量'
+				},
+				{
+					field: 'aiCallInCount',
+					title: '智能回访呼出量'
+				},
+				{
+					field: 'aiCallInCount',
+					title: '智能外呼呼出量'
+				},
+			]
+		},
+		{
+			field: 'putthroughCount',
+			title: '呼出接通总量',
+		},
+		{
+			title:'呼出接通类型',
+			children:[
+				{
+					title: '个人服务呼出接通量',
+					field: 'personCallInPutthroughCount',
+				},
+				{
+					title: '企业服务呼出接通量',
+					field: 'enterpriseCallInPutthroughCount',
+				},
+				{
+					title: '智能回访呼出接通量',
+					field: 'aiCallInPutthroughCount',
+				},
+				{
+					title: '智能外呼呼出接通量',
+					field: 'aiCallInPutthroughCount',
+				}
+			]
+		},
+		{
+			title: '呼出接通率',
+			field: 'ringOffCount',
+		},
+		{
+			title: '各类呼出接通率',
+			children:[
+				{
+					title: '个人服务呼出接通率',
+					field: 'personRingOffCount',
+				},
+				{
+					title: '企业服务呼出接通率',
+					field: 'enterpriseRingOffCount',
+				},
+				{
+					title: '智能回访呼出接通率',
+					field: 'ivrRingOffCount',
+				},
+				{
+					title: '智能外呼呼出接通率',
+					field: 'ivrRingOffCount',
+				}
+			]
+		}
+	],
+	data: [],
+	params: {
+		exportMethod: callDateSimpleExport,
+		exportParams: requestParams,
+	},
+	sortConfig: {
+		remote: true,
+	},
+	showFooter:true,
+	footerMethod:  ({ columns, data }) => {
+		return [
+			columns.map((column: any, columnIndex: number) => {
+				if (columnIndex === 0) {
+					return '合计';
+				}
+				// 后端返回了数据集合 state.totalCount 所以不需要计算 直接进行赋值
+				return XEUtils.get(state.totalCount, column.property);
+			}),
+		];
+	}
+});
+/** 搜索按钮操作 */
+const handleQuery = () => {
+	// state.queryParams.PageIndex = 1;
+	queryList();
+};
+/** 获取列表 */
+const queryList = async () => {
+	state.loading = true;
+	gridOptions.loading = true;
+	try {
+		requestParams.value = Other.deepClone(state.queryParams);
+		requestParams.value.StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
+		requestParams.value.EndTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
+		Reflect.deleteProperty(requestParams.value, 'crTime');
+	/*	const { result } = await callDateSimple(requestParams.value);
+		state.tableData = result.list ?? [];
+		state.totalCount = result.total;
+		gridOptions.data = state.tableData;*/
+		state.loading = false;
+		gridOptions.loading = false;
+	} catch (e) {
+		state.loading = false;
+		gridOptions.loading = false;
+		console.log(e);
+	}
+};
+/** 重置按钮操作 */
+const statisticalTimeRef = ref<RefType>();
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	statisticalTimeRef.value.reset();
+	queryList();
+};
+onMounted(() => {
+	queryList();
+});
+</script>

+ 168 - 0
src/views/statistics/call/personalSeatsDetail.vue

@@ -0,0 +1,168 @@
+<template>
+	<div class="statistics-call-personal-seats-date-detail-container layout-padding">
+		<div class="layout-padding-auto layout-padding-view pd20">
+			<vxe-grid v-bind="gridOptions">
+				<template #form>
+					<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline :disabled="gridOptions.loading">
+						<el-form-item prop="crTime">
+							<statistical-time v-model="state.queryParams.crTime"	@change="handleQuery" ref="statisticalTimeRef" :disabled="state.loading"/>
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
+							<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
+								<SvgIcon name="ele-Refresh" class="mr5" />重置
+							</el-button>
+
+						</el-form-item>
+					</el-form>
+				</template>
+			</vxe-grid>
+		</div>
+	</div>
+</template>
+<script setup lang="tsx" name="statisticsCallPersonalSeatDetail">
+import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
+import { FormInstance } from 'element-plus';
+import { defaultDate } from '@/utils/constants';
+import Other from '@/utils/other';
+import {  callPersonal, callPersonalExport } from '@/api/statistics/call';
+import XEUtils from 'xe-utils';
+
+const StatisticalTime = defineAsyncComponent(() => import('@/components/StatisticalTime/index.vue')); // 日期类型选择组件
+// 定义变量内容
+const ruleFormRef = ref<RefType>(); // 表单ref
+const state = reactive<any>({
+	queryParams: {
+		// 查询条件
+		crTime: defaultDate,
+	},
+	tableData: [], //表单
+	loading: false, // 加载
+	total: 0, // 总数
+	callForwardingSource: [],
+	totalCount: {},
+});
+
+const requestParams = ref<EmptyObjectType>({});
+const gridOptions = reactive<any>({
+	loading: false,
+	border: true,
+	showOverflow: true,
+	columnConfig: {
+		resizable: true,
+	},
+	scrollY: {
+		enabled: true,
+		gt: 100,
+	},
+	toolbarConfig: {
+		zoom: true,
+		custom: true,
+		refresh: {
+			queryMethod: () => {
+				handleQuery();
+			},
+		},
+		tools: [{ toolRender: { name: 'exportAll' } }],
+	},
+	customConfig: {
+		storage: true,
+	},
+	id: 'statisticsCallPersonalSeatDetail',
+	rowConfig: { isHover: true, height: 30, isCurrent: true, useKey: true },
+	height: 'auto',
+	align: 'center',
+	columns: [
+		{
+			field: 'date',
+			title: '日期',
+		},
+		{
+			field: 'personCallInCount',
+			title: '个人服务呼入总量',
+		},
+		{
+			field: 'personCallInPutthroughCount',
+			title: '个人服务接通量',
+		},
+		{
+			field: 'personRingOffCount',
+			title: '个人服务挂断总量',
+		},
+		{
+			title: '挂断类型',
+			children:[
+				{
+					field: 'personQueueOffCount',
+					title: '个人服务队列挂断',
+				},
+				{
+					field: 'personWaitOffCount',
+					title: '个人服务等待挂断',
+				}
+			]
+		},
+		{
+			field: 'personCallPutthorughRateText',
+			title: '个人服务接通率',
+		},
+	],
+	data: [],
+	params: {
+		exportMethod: callPersonalExport,
+		exportParams: requestParams,
+	},
+	sortConfig: {
+		remote: true,
+	},
+	showFooter:true,
+	footerMethod:  ({ columns, data }) => {
+		return [
+			columns.map((column: any, columnIndex: number) => {
+				if (columnIndex === 0) {
+					return '合计';
+				}
+				// 后端返回了数据集合 state.totalCount 所以不需要计算 直接进行赋值
+				return XEUtils.get(state.totalCount, column.property);
+			}),
+		];
+	}
+});
+/** 搜索按钮操作 */
+const handleQuery = () => {
+	// state.queryParams.PageIndex = 1;
+	queryList();
+};
+/** 获取列表 */
+const queryList = async () => {
+	state.loading = true;
+	gridOptions.loading = true;
+	try {
+		requestParams.value = Other.deepClone(state.queryParams);
+		requestParams.value.StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
+		requestParams.value.EndTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
+		Reflect.deleteProperty(requestParams.value, 'crTime');
+		const { result } = await callPersonal(requestParams.value);
+		state.tableData = result.list ?? [];
+		state.totalCount = result.total;
+		gridOptions.data = state.tableData;
+		state.loading = false;
+		gridOptions.loading = false;
+	} catch (e) {
+		state.loading = false;
+		gridOptions.loading = false;
+		console.log(e);
+	}
+};
+/** 重置按钮操作 */
+const statisticalTimeRef = ref<RefType>();
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	statisticalTimeRef.value.reset();
+	queryList();
+};
+onMounted(() => {
+	queryList();
+});
+</script>

+ 247 - 0
src/views/statistics/call/seatDateDetail.vue

@@ -0,0 +1,247 @@
+<template>
+	<div class="statistics-call-seats-date-detail-container layout-padding">
+		<div class="layout-padding-auto layout-padding-view pd20">
+			<vxe-grid v-bind="gridOptions">
+				<template #form>
+					<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline :disabled="gridOptions.loading">
+						<el-form-item prop="crTime">
+							<statistical-time v-model="state.queryParams.crTime" @change="handleQuery" ref="statisticalTimeRef" :disabled="state.loading" />
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
+							<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
+								<SvgIcon name="ele-Refresh" class="mr5" />重置
+							</el-button>
+						</el-form-item>
+					</el-form>
+				</template>
+				<template #toolbar_buttons>
+					<el-popover :width="500" trigger="click">
+						<template #reference>
+							<el-button type="primary" title="口径说明"><SvgIcon name="ele-QuestionFilled" class="mr5" />口径说明</el-button>
+						</template>
+						<el-descriptions title="" :column="1" border style="max-height: 400px; overflow: auto">
+							<el-descriptions-item label="呼入总量">个人服务呼入总量+企业服务呼入总量+智能应答呼入总量+IVR呼入总量</el-descriptions-item>
+							<el-descriptions-item label="接通总量"> 个人服务接通总量+企业服务接通总量+智能应答呼入总量(智能应答视为全部接通) </el-descriptions-item>
+							<el-descriptions-item label="挂断总量">个人服务挂断总量+企业服务挂断总量+IVR挂断</el-descriptions-item>
+							<el-descriptions-item label="总体接通率">=接通总量/呼入总量</el-descriptions-item>
+							<el-descriptions-item label="服务接通率"> (个人服务接通总量+企业服务接通总量+智能应答呼入总量)/(个人服务呼入总量+企业服务呼入总量+智能应答呼入总量) </el-descriptions-item>
+							<el-descriptions-item label="话务接通率"> (个人服务接通总量+企业服务接通总量)/(个人服务呼入总量+企业服务呼入总量) </el-descriptions-item>
+							<el-descriptions-item label="IVR呼入总量"> 进入IVR以后,群众未按键直接挂断电话的(该数据与IVR挂断数据一致) </el-descriptions-item>
+<!--
+
+							个人服务呼入总量:进入IVR以后,已按了1键的所有电话量
+							个人服务接通量:通过个人服务通道后,话务人员接通的所有电话量
+							个人服务挂断总量:已按了1键,但是在话务人员接通前挂断的电话量
+							个人服务队列挂断:已进入个人服务通道,在队列中挂断的电话量
+							个人服务等待挂断:已进入个人服务话机,等待话务人员接听过程中挂断的电话量
+							个人服务挂断总量=个人服务队列挂断+个人服务等待挂断
+							个人服务接通率=个人服务接通量/个人服务呼入总量-->
+
+						</el-descriptions>
+					</el-popover>
+				</template>
+			</vxe-grid>
+		</div>
+	</div>
+</template>
+<script setup lang="tsx" name="statisticsCallSeatDateDetail">
+import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
+import { FormInstance } from 'element-plus';
+import { defaultDate } from '@/utils/constants';
+import Other from '@/utils/other';
+import { callDateDetail, callDateDetailExport } from '@/api/statistics/call';
+import XEUtils from 'xe-utils';
+
+const StatisticalTime = defineAsyncComponent(() => import('@/components/StatisticalTime/index.vue')); // 日期类型选择组件
+// 定义变量内容
+const ruleFormRef = ref<RefType>(); // 表单ref
+const state = reactive<any>({
+	queryParams: {
+		// 查询条件
+		crTime: defaultDate,
+	},
+	tableData: [], //表单
+	loading: false, // 加载
+	total: 0, // 总数
+	callForwardingSource: [],
+	totalCount: {},
+});
+
+const requestParams = ref<EmptyObjectType>({});
+const gridOptions = reactive<any>({
+	loading: false,
+	border: true,
+	showOverflow: true,
+	columnConfig: {
+		resizable: true,
+	},
+	scrollY: {
+		enabled: true,
+		gt: 100,
+	},
+	toolbarConfig: {
+		zoom: true,
+		custom: true,
+		refresh: {
+			queryMethod: () => {
+				handleQuery();
+			},
+		},
+		tools: [{ toolRender: { name: 'exportAll' } }],
+	/*	slots: {
+			buttons: 'toolbar_buttons',
+		},*/
+	},
+	customConfig: {
+		storage: true,
+	},
+	// showHeaderOverflow: true,
+	id: 'statisticsCallSeatDateDetail',
+	rowConfig: { isHover: true, height: 30, isCurrent: true, useKey: true },
+	height: 'auto',
+	align: 'center',
+	columns: [
+		{
+			field: 'date',
+			title: '日期',
+		},
+		{
+			field: 'callInTotal',
+			title: '呼入总量',
+		},
+		{
+			title:'呼入类型',
+			children:[
+				{
+						field: 'ivrCallInTotal',
+						title:'IVR呼入总量'
+				},
+				{
+					title: '个人服务呼入总量',
+					field: 'personCallInCount',
+				},
+				{
+					field: 'enterpriseCallInCount',
+					title: '企业服务呼入总量'
+				},
+				{
+					field: 'aiCallInCount',
+					title: '智能应答呼入总量'
+				},
+			]
+		},
+		{
+			field: 'putthroughCount',
+			title: '接通总量',
+		},
+		{
+			title:'呼入接通类型',
+			children:[
+				{
+					title: '个人服务接通量',
+					field: 'personCallInPutthroughCount',
+				},
+				{
+					title: '企业服务接通量',
+					field: 'enterpriseCallInPutthroughCount',
+				},
+				{
+					title: '智能应答接通量',
+					field: 'aiCallInPutthroughCount',
+				}
+			]
+		},
+		{
+			title: '挂断总量',
+			field: 'ringOffCount',
+		},
+		{
+			title: '挂断类型',
+			children:[
+				{
+					title: '个人服务挂断总量',
+					field: 'personRingOffCount',
+				},
+				{
+					title: '企业服务挂断总量',
+					field: 'enterpriseRingOffCount',
+				},
+				{
+					title: 'IVR挂断',
+					field: 'ivrRingOffCount',
+				}
+			]
+		},
+		{
+			field: 'totalPutthroughRateText',
+			title: '总体接通率',
+		},
+		{
+			field: 'servicePutthorughRateText',
+			title: '服务接通率',
+		},
+		{
+			field: 'callPutthorughRateText',
+			title: '话务接通率',
+		},
+	],
+	data: [],
+	params: {
+		exportMethod: callDateDetailExport,
+		exportParams: requestParams,
+	},
+	sortConfig: {
+		remote: true,
+	},
+	showFooter: true,
+	footerMethod: ({ columns, data }) => {
+		return [
+			columns.map((column: any, columnIndex: number) => {
+				if (columnIndex === 0) {
+					return '合计';
+				}
+				// 后端返回了数据集合 state.totalCount 所以不需要计算 直接进行赋值
+				return XEUtils.get(state.totalCount, column.property);
+					// return XEUtils.sum(data, column.property);
+			}),
+		];
+	},
+});
+/** 搜索按钮操作 */
+const handleQuery = () => {
+	// state.queryParams.PageIndex = 1;
+	queryList();
+};
+/** 获取列表 */
+const queryList = async () => {
+	state.loading = true;
+	gridOptions.loading = true;
+	try {
+		requestParams.value = Other.deepClone(state.queryParams);
+		requestParams.value.StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
+		requestParams.value.EndTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
+		Reflect.deleteProperty(requestParams.value, 'crTime');
+		const { result } = await callDateDetail(requestParams.value);
+		gridOptions.data =  result.list ?? [];
+		state.totalCount = result.total;
+		state.loading = false;
+		gridOptions.loading = false;
+	} catch (e) {
+		state.loading = false;
+		gridOptions.loading = false;
+		console.log(e);
+	}
+};
+/** 重置按钮操作 */
+const statisticalTimeRef = ref<RefType>();
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	statisticalTimeRef.value.reset();
+	queryList();
+};
+onMounted(() => {
+	queryList();
+});
+</script>

+ 176 - 112
src/views/statistics/call/seatsDate.vue

@@ -1,15 +1,22 @@
 <template>
 	<div class="statistics-call-seats-date-container layout-padding">
 		<div class="layout-padding-auto layout-padding-view pd20">
-			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
-				<el-form-item prop="crTime">
-					<statistical-time v-model="state.queryParams.crTime"	@change="handleQuery" ref="statisticalTimeRef" :disabled="state.loading"/>
-				</el-form-item>
-				<el-form-item>
-					<el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
-					<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
-						<SvgIcon name="ele-Refresh" class="mr5" />重置
-					</el-button>
+			<vxe-grid v-bind="gridOptions">
+				<template #form>
+					<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline :disabled="gridOptions.loading">
+						<el-form-item prop="crTime">
+							<statistical-time v-model="state.queryParams.crTime"	@change="handleQuery" ref="statisticalTimeRef" :disabled="state.loading"/>
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
+							<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
+								<SvgIcon name="ele-Refresh" class="mr5" />重置
+							</el-button>
+
+						</el-form-item>
+					</el-form>
+				</template>
+				<template #toolbar_buttons>
 					<el-popover :width="500" trigger="click">
 						<template #reference>
 							<el-button type="primary" title="口径说明"><SvgIcon name="ele-QuestionFilled" class="mr5"/>口径说明</el-button>
@@ -25,91 +32,8 @@
 							<el-descriptions-item label="呼出未接通">呼出未接通的总量 </el-descriptions-item>
 						</el-descriptions>
 					</el-popover>
-				</el-form-item>
-			</el-form>
-			<vxe-toolbar
-				ref="toolbarRef"
-				:loading="state.loading"
-				custom
-				:refresh="{
-					queryMethod: handleQuery
-				}"
-				:tools="[{ toolRender: { name: 'exportAll' } }]"
-			>
-			</vxe-toolbar>
-			<div style="overflow: hidden; width: 100%; height: 100%; flex: 1">
-				<vxe-table
-					border
-					:loading="state.loading"
-					:data="state.tableData"
-					:column-config="{ resizable: true }"
-					:row-config="{ isCurrent: true, isHover: true, height: 30, keyField: 'id',useKey: true }"
-					ref="tableRef"
-					height="auto"
-					auto-resize
-					show-overflow
-					:scrollY="{ enabled: true, gt: 100 }"
-					id="statisticsCallTalkTime"
-					:custom-config="{ storage: true }"
-					showHeaderOverflow
-					:params="{ exportMethod: callDateSimpleExport, exportParams: requestParams }"
-					show-footer
-					:footer-method="footerMethod"
-				>
-					<vxe-column field="date" title="日期"></vxe-column>
-					<vxe-column field="inTotal" title="呼入总量">
-						<template #default="scope">
-							<el-button type="primary" link @click="linkDetail(scope)">
-								{{scope.row.inTotal}}
-							</el-button>
-						</template>
-					</vxe-column>
-					<vxe-column field="inConnectionRate" title="接通率">
-					</vxe-column>
-					<vxe-column field="inConnectionQuantity" title="呼入接通">
-						<template #default="scope">
-							<el-button type="primary" link @click="linkDetail(scope)">
-								{{scope.row.inConnectionQuantity}}
-							</el-button>
-						</template>
-					</vxe-column>
-					<vxe-column field="inNotAnswered" title="挂机量">
-						<template #default="scope">
-							<el-button type="primary" link @click="linkDetail(scope)">
-								{{scope.row.inNotAnswered}}
-							</el-button>
-						</template>
-					</vxe-column>
-					<vxe-column field="notAcceptedHang" title="呼入队列挂断">
-						<template #default="scope">
-							<el-button type="primary" link @click="linkDetail(scope)">
-								{{scope.row.notAcceptedHang}}
-							</el-button>
-						</template>
-					</vxe-column>
-					<vxe-column field="ivrByeCount" title="呼入IVR挂断">
-						<template #default="scope">
-							<el-button type="primary" link @click="linkDetail(scope)">
-								{{scope.row.ivrByeCount}}
-							</el-button>
-						</template>
-					</vxe-column>
-					<vxe-column field="outConnectionQuantity" title="呼出接通">
-						<template #default="scope">
-							<el-button type="primary" link @click="linkDetail(scope)">
-								{{scope.row.outConnectionQuantity}}
-							</el-button>
-						</template>
-					</vxe-column>
-					<vxe-column field="outNotAnswered" title="呼出未接通">
-						<template #default="scope">
-							<el-button type="primary" link @click="linkDetail(scope)">
-								{{scope.row.outNotAnswered}}
-							</el-button>
-						</template>
-					</vxe-column>
-				</vxe-table>
-			</div>
+				</template>
+			</vxe-grid>
 		</div>
 	</div>
 </template>
@@ -137,15 +61,169 @@ const state = reactive<any>({
 	callForwardingSource: [],
 	totalCount: {},
 });
+
+const requestParams = ref<EmptyObjectType>({});
+const gridOptions = reactive<any>({
+	loading: false,
+	border: true,
+	showOverflow: true,
+	columnConfig: {
+		resizable: true,
+	},
+	scrollY: {
+		enabled: true,
+		gt: 100,
+	},
+	toolbarConfig: {
+		zoom: true,
+		custom: true,
+		refresh: {
+			queryMethod: () => {
+				handleQuery();
+			},
+		},
+		tools: [{ toolRender: { name: 'exportAll' } }],
+		slots: {
+			buttons: 'toolbar_buttons',
+		},
+	},
+	customConfig: {
+		storage: true,
+	},
+	id: 'statisticsCallSeatsDate',
+	rowConfig: { isHover: true, height: 30, isCurrent: true, useKey: true },
+	height: 'auto',
+	columns: [
+		{
+			field: 'date',
+			title: '日期',
+		},
+		{
+			field: 'inTotal',
+			title: '呼入总量',
+			slots:{
+				default (scope:any) {
+					return (
+						<el-button type="primary" onClick={() => linkDetail(scope)} link>
+							{scope.row.inTotal}
+						</el-button>
+					)
+				}
+			}
+		},
+		{
+			field: 'inConnectionRate',
+			title: '接通率',
+		},
+		{
+			field: 'inConnectionQuantity',
+			title: '呼入接通',
+			slots:{
+				default (scope:any) {
+					return (
+						<el-button type="primary" onClick={() => linkDetail(scope)} link>
+							{scope.row.inConnectionQuantity}
+						</el-button>
+					)
+				}
+			}
+		},
+		{
+			field: 'inNotAnswered',
+			title: '挂机量',
+			slots:{
+				default (scope:any) {
+					return (
+						<el-button type="primary" onClick={() => linkDetail(scope)} link>
+							{scope.row.inNotAnswered}
+						</el-button>
+					)
+				}
+			}
+		},
+		{
+			field: 'notAcceptedHang',
+			title: '呼入队列挂断',
+			slots:{
+				default (scope:any) {
+					return (
+						<el-button type="primary" onClick={() => linkDetail(scope)} link>
+							{scope.row.notAcceptedHang}
+						</el-button>
+					)
+				}
+			}
+		},
+		{
+			field: 'ivrByeCount',
+			title: '呼入IVR挂断',
+			slots:{
+				default (scope:any) {
+					return (
+						<el-button type="primary" onClick={() => linkDetail(scope)} link>
+							{scope.row.ivrByeCount}
+						</el-button>
+					)
+				}
+			}
+		},
+		{
+			field: 'outConnectionQuantity',
+			title: '呼出接通',
+			slots:{
+				default (scope:any) {
+					return (
+						<el-button type="primary" onClick={() => linkDetail(scope)} link>
+							{scope.row.outConnectionQuantity}
+						</el-button>
+					)
+				}
+			}
+		},
+		{
+			field: 'outNotAnswered',
+			title: '呼出未接通',
+			slots:{
+				default (scope:any) {
+					return (
+						<el-button type="primary" onClick={() => linkDetail(scope)} link>
+							{scope.row.outNotAnswered}
+						</el-button>
+					)
+				}
+			}
+		},
+	],
+	data: [],
+	params: {
+		exportMethod: callDateSimpleExport,
+		exportParams: requestParams,
+	},
+	sortConfig: {
+		remote: true,
+	},
+	showFooter:true,
+	footerMethod:  ({ columns, data }) => {
+		return [
+			columns.map((column: any, columnIndex: number) => {
+				if (columnIndex === 0) {
+					return '合计';
+				}
+				// 后端返回了数据集合 state.totalCount 所以不需要计算 直接进行赋值
+				return XEUtils.get(state.totalCount, column.property);
+			}),
+		];
+	}
+});
 /** 搜索按钮操作 */
 const handleQuery = () => {
 	// state.queryParams.PageIndex = 1;
 	queryList();
 };
 /** 获取列表 */
-const requestParams = ref<EmptyObjectType>({});
 const queryList = async () => {
 	state.loading = true;
+	gridOptions.loading = true;
 	try {
 		requestParams.value = Other.deepClone(state.queryParams);
 		requestParams.value.StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
@@ -154,9 +232,12 @@ const queryList = async () => {
 		const { result } = await callDateSimple(requestParams.value);
 		state.tableData = result.list ?? [];
 		state.totalCount = result.total;
+		gridOptions.data = state.tableData;
 		state.loading = false;
+		gridOptions.loading = false;
 	} catch (e) {
 		state.loading = false;
+		gridOptions.loading = false;
 		console.log(e);
 	}
 };
@@ -180,24 +261,7 @@ const linkDetail = (scope: any) => {
 		},
 	});
 };
-// 计算合计
-const footerMethod = ({ columns, data }) => {
-	return [
-		columns.map((column: any, columnIndex: number) => {
-			if (columnIndex === 0) {
-				return '合计';
-			}
-			// 后端返回了数据集合 state.totalCount 所以不需要计算 直接进行赋值
-			return XEUtils.get(state.totalCount, column.property);
-		}),
-	];
-};
-const toolbarRef = ref<RefType>();
-const tableRef = ref<RefType>();
 onMounted(() => {
 	queryList();
-	if (tableRef.value && toolbarRef.value) {
-		tableRef.value.connect(toolbarRef.value);
-	}
 });
 </script>

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

@@ -363,7 +363,4 @@ onMounted(() => {
 onActivated(()=>{
 	treeSearchRef.value.scrollTo(state.queryParams.OrgCode); // 滚动
 })
-</script>
-<style lang="scss">
-@import '@wsfe/vue-tree/style.css';
-</style>
+</script>

+ 38 - 91
src/views/tels/smartRecord/index.vue

@@ -87,48 +87,22 @@
 					</el-col>
 				</el-row>
 			</el-form>
-
-			<vxe-toolbar ref="toolbarRef" custom>
-				<template #buttons>
-					<vxe-button status="primary">新增</vxe-button>
-					<vxe-button>删除</vxe-button>
-					<vxe-button>保存</vxe-button>
-					<el-button>测试</el-button>
-					<el-button>测试</el-button>
-				</template>
-				<template #tools>
-					<vxe-button icon="vxe-icon-upload"></vxe-button>
-					<vxe-button icon="vxe-icon-setting"></vxe-button>
-					<vxe-button icon="vxe-icon-ellipsis-h"></vxe-button>
-				</template>
-			</vxe-toolbar>
-			<vxe-table
-				border
-				:loading="loading"
-				:data="tableData"
-				:sort-config="sortConfig"
-				auto-resize
-				:column-config="{ resizable: true }"
-				:row-config="{ isCurrent: true, isHover: true }"
-				ref="tableRef"
-				@sort-change="sortChangeEvent"
+			<VTreeSearch
+				selectable
+				:load="remoteLoad"
+				titleField="hotSpotFullName"
+				keyField="id"
+				searchRemote
+				@search="handleSearch"
+				ref="treeRef"
+				isLeaf="hasChild"
+				:datafields="{
+						label: 'hotSpotFullName',
+						children: 'children',
+						isLeaf: 'hasChild',
+				}"
 			>
-				<vxe-column type="seq" width="70"></vxe-column>
-				<vxe-column field="name" title="Name"></vxe-column>
-				<vxe-column field="role" title="Role" sortable></vxe-column>
-				<vxe-column field="sex" title="Sex" sortable></vxe-column>
-				<vxe-column field="age" title="Age" sortable></vxe-column>
-				<vxe-column field="address" title="Address" sortable>
-					<template #default="{ row }">
-						<el-switch v-model="row.flag1" disabled size="small"></el-switch>
-					</template>
-				</vxe-column>
-				<vxe-column field="operation" title="操作" fixed="right">
-					<template #default="{ row }">
-						<el-switch v-model="row.flag1" disabled size="small"></el-switch>
-					</template>
-				</vxe-column>
-			</vxe-table>
+			</VTreeSearch>
 		</el-card>
 	</div>
 </template>
@@ -139,8 +113,8 @@ import type { FormInstance } from 'element-plus';
 import { ElMessage } from 'element-plus';
 import { formatDate } from '@/utils/formatTime';
 import { olaFn } from '@/utils/olaFn';
-import { VxeTablePropTypes, VxeTableEvents, VxeColumnPropTypes } from 'vxe-table';
-import XEUtils from 'xe-utils';
+import { VTreeSearch } from '@wsfe/vue-tree';
+import { hotSpotSearch, hotSpotType } from '@/api/business/order';
 
 // 表格配置项
 const columns = ref<any[]>([
@@ -343,55 +317,28 @@ const hangup = () => {
 	olaRef.value.hangup();
 };
 
-interface RowVO {
-	id: number;
-	name: string;
-	role: string;
-	sex: string;
-	age: number;
-	num: string;
-	num2: string;
-	address: string;
-}
-
-const loading = ref(false);
-const tableData = ref<RowVO[]>();
-
-const sortConfig = ref<VxeTablePropTypes.SortConfig<RowVO>>({
-	remote: true,
-});
+const searchKeyword = ref('');
 
-const findList = (field?: VxeColumnPropTypes.Field, order?: VxeTablePropTypes.SortOrder) => {
-	loading.value = true;
-	// 模拟接口
-	return new Promise<RowVO[]>((resolve) => {
-		setTimeout(() => {
-			loading.value = false;
-			const mockList = [
-				{ id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, num: '3.8', num2: '3.8', address: 'test abc' },
-				{ id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, num: '511', num2: '511', address: 'Guangzhou' },
-				{ id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, num: '12.8', num2: '12.8', address: 'Shanghai' },
-				{ id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 23, num: '103', num2: '103', address: 'test abc' },
-				{ id: 10005, name: 'Test5', role: 'Develop', sex: 'Women', age: 30, num: '56', num2: '56', address: 'Shanghai' },
-				{ id: 10006, name: 'Test6', role: 'Designer', sex: 'Women', age: 21, num: '49', num2: '49', address: 'test abc' },
-				{ id: 10007, name: 'Test7', role: 'Test', sex: 'Man', age: 29, num: '400.9', num2: '400.9', address: 'test abc' },
-				{ id: 10008, name: 'Test8', role: 'Develop', sex: 'Man', age: 35, num: '5000', num2: '5000', address: 'test abc' },
-			];
-			if (field && order) {
-				const rest = XEUtils.orderBy(mockList, { field, order });
-				tableData.value = rest;
-				resolve(rest);
-			} else {
-				tableData.value = mockList;
-				resolve(mockList);
-			}
-		}, 300);
-	});
+const handleSearch = (keyword: string) => {
+	searchKeyword.value = keyword;
 };
-
-const sortChangeEvent: VxeTableEvents.SortChange<RowVO> = ({ field, order }) => {
-	findList(field, order);
+//
+const treeRef = ref();
+const remoteLoad = async (node: any, resolve: any) => {
+	console.log(node, '11');
+	try {
+		if (searchKeyword.value) {
+			const { result } = await hotSpotSearch(searchKeyword.value);
+			resolve(result);
+			setTimeout(() => {
+				treeRef.value.setExpandAll(true);
+			}, 300);
+		} else {
+			const { result } = await hotSpotType({ id: node?.id ? node?.id : null });
+			resolve(result);
+		}
+	} catch (e) {
+		console.log(e);
+	}
 };
-
-findList();
 </script>

+ 0 - 9
src/views/todo/seats/accept/History.vue

@@ -130,15 +130,6 @@ const searchHistory = throttle(async () => {
 		state.loading = false;
 	}
 }, 300);
-watch(
-	() => props.ruleForm.contact,
-	(newVal: any) => {
-		if (newVal) {
-			searchHistory();
-		}
-	},
-	{ immediate: true }
-);
 /*const toolbarRef = ref<RefType>();
 const tableRef = ref<RefType>();
 onMounted(() => {