Quellcode durchsuchen

reactor:对接通话记录失联工单;知识库知识分类加载优化;

zhangchong vor 1 Jahr
Ursprung
Commit
2c7aba1027

+ 0 - 2
src/views/business/order/index.vue

@@ -356,7 +356,6 @@ const RepeatEventEdit = defineAsyncComponent(() => import('@/views/business/repe
 const HotSpotSelect = defineAsyncComponent(() => import('@/components/Hotspot/index.vue')); // 选择热点
 
 // 定义变量内容
-const paginationModel = reactive({});
 const state = reactive({
 	queryParams: {
 		PageIndex: 1, // 当前页
@@ -511,7 +510,6 @@ const getBaseData = async () => {
 /** 获取列表 */
 const queryList = () => {
 	let request = other.deepClone(state.queryParams);
-	request = { ...request, ...paginationModel };
 	Reflect.deleteProperty(request, 'crTime'); // 删除无用的参数
 	Reflect.deleteProperty(request, 'exTime'); // 删除无用的参数
 	Reflect.deleteProperty(request, 'doneTime'); // 删除无用的参数

+ 50 - 31
src/views/knowledge/index/edit.vue

@@ -1,7 +1,7 @@
 <template>
 	<div class="knowledge-edit-container layout-pd">
 		<el-card shadow="never" style="padding: 0 50px">
-			<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="120px" scroll-to-error v-loading="state.loading">
+			<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="120px" scroll-to-error>
 				<el-row :gutter="35">
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
 						<el-form-item label="温馨提示">
@@ -19,24 +19,34 @@
 							</el-select>
 						</el-form-item>
 					</el-col>
-					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8">
-						<el-form-item label="知识分类" prop="knowledgeTypeId" :rules="[{ required: true, message: '请选择知识分类', trigger: 'change' }]">
-							<el-cascader
-								:options="state.typeData"
-								filterable
-								:props="{ checkStrictly: true, value: 'id', label: 'name', emitPath: false, children: 'children',multiple: true }"
-								placeholder="请选择知识分类"
-								clearable
-								class="w100"
-								v-model="state.ruleForm.knowledgeTypeId"
-								ref="cascadeRef"
-								@change="getKnowledgeList"
-                collapse-tags
-                collapse-tags-tooltip
-                :max-collapse-tags="2"
-							>
-							</el-cascader>
-						</el-form-item>
+					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8" >
+            <el-skeleton style="width: 240px" :loading="state.loading" animated>
+              <template #template>
+                <el-form-item label="知识分类" prop="knowledgeTypeId" :rules="[{ required: true, message: '请选择知识分类', trigger: 'change' }]">
+                  <el-skeleton-item variant="h1"/>
+                </el-form-item>
+              </template>
+              <template #default>
+                <el-form-item label="知识分类" prop="knowledgeTypeId" :rules="[{ required: true, message: '请选择知识分类', trigger: 'change' }]">
+                  <el-cascader
+                    :options="state.typeData"
+                    filterable
+                    :props="{ checkStrictly: true, value: 'id', label: 'name', emitPath: false, children: 'children',multiple: true }"
+                    placeholder="请选择知识分类"
+                    clearable
+                    class="w100"
+                    v-model="state.ruleForm.knowledgeTypeId"
+                    ref="cascadeRef"
+                    @change="getKnowledgeList"
+                    collapse-tags
+                    collapse-tags-tooltip
+                    :max-collapse-tags="2"
+
+                  >
+                  </el-cascader>
+                </el-form-item>
+              </template>
+            </el-skeleton>
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8">
 						<el-form-item label="失效时间" prop="expiredTime" :rules="[{ required: false, message: '请选择失效时间', trigger: 'change' }]">
@@ -69,18 +79,27 @@
 						</el-form-item>
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8">
-						<el-form-item label="来源部门" prop="sourceOrganizeId" :rules="[{ required: true, message: '请选择来源部门', trigger: 'change' }]">
-							<el-cascader
-								:options="orgsOptions"
-								filterable
-								:props="{ value: 'id', label: 'name', emitPath: false, checkStrictly: true }"
-								placeholder="请选择来源部门"
-								class="w100"
-								v-model="state.ruleForm.sourceOrganizeId"
-								ref="orgRef"
-							>
-							</el-cascader>
-						</el-form-item>
+            <el-skeleton style="width: 240px" :loading="state.loading" animated>
+              <template #template>
+                <el-form-item label="来源部门" prop="sourceOrganizeId" :rules="[{ required: true, message: '请选择来源部门', trigger: 'change' }]">
+                  <el-skeleton-item variant="h1"/>
+                </el-form-item>
+              </template>
+              <template #default>
+                <el-form-item label="来源部门" prop="sourceOrganizeId" :rules="[{ required: true, message: '请选择来源部门', trigger: 'change' }]">
+                  <el-cascader
+                    :options="orgsOptions"
+                    filterable
+                    :props="{ value: 'id', label: 'name', emitPath: false, checkStrictly: true }"
+                    placeholder="请选择来源部门"
+                    class="w100"
+                    v-model="state.ruleForm.sourceOrganizeId"
+                    ref="orgRef"
+                  >
+                  </el-cascader>
+                </el-form-item>
+              </template>
+            </el-skeleton>
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8">
 						<el-form-item label="热点分类" prop="hotspotId" :rules="[{ required: true, message: '请选择热点分类', trigger: 'change' }]">

+ 18 - 5
src/views/knowledge/index/index.vue

@@ -14,7 +14,7 @@
 							<el-input v-model="filterHot" placeholder="请输入热点名称" class="input-with-select mb10" clearable> </el-input>
 						</el-tab-pane>
 					</el-tabs>
-					<el-scrollbar style="height: calc(100% - 100px);'" ref="scrollBarRef" v-loading="state.loading">
+					<el-scrollbar style="height: calc(100% - 100px);'" ref="scrollBarRef">
 						<el-tree-v2
 							v-show="state.activeName === '0'"
 							:data="state.orgData"
@@ -27,6 +27,7 @@
 							:item-size="40"
 							:height="treeHeight"
 							empty-text="暂无组织数据"
+              v-loading="state.loading"
 						>
 							<template #default="{ node }">
 								<span :title="node.label">{{ node.label }}</span>
@@ -42,9 +43,9 @@
 							:filter-node-method="filterNodeType"
 							node-key="id"
 							:style="state.activeName === '1' ? 'min-width: 100%; display: inline-block' : 'display:none'"
+              v-loading="state.typeLoading"
 						>
 						</el-tree>
-
 						<el-tree
 							node-key="id"
 							:load="loadNode"
@@ -290,6 +291,7 @@ const state = reactive<any>({
 	knowledgeOptions: [], //知识库类型数据
 	statusOptions: [], //状态数据
 	hotSpotData: [], //热点数据
+  typeLoading: false, // 知识类型loading
 });
 const router = useRouter(); //路由
 const ruleFormRef = ref<FormInstance>(); //表单ref
@@ -373,15 +375,25 @@ const handleQuery = throttle(() => {
 const getOrgListApi = async () => {
 	state.loading = true;
 	try {
-		const res: any = await Promise.all([getOrgList(), treeList({ IsEnable: true }), baseData()]);
+		const res: any = await Promise.all([getOrgList(), baseData()]);
 		state.orgData = res[0].result ?? []; //部门
-		state.knowledgeOptions = res[1].result ?? []; // 知识类型
 		state.statusOptions = res[2].result ?? []; // 列表状态
 		state.loading = false;
 	} catch (error) {
 		state.loading = false;
 	}
 };
+// 获取知识分类
+const getKnowledgeType = async () => {
+  state.typeLoading = true;
+  try {
+    const res: any = await treeList({ IsEnable: true });
+    state.knowledgeOptions = res.result ?? [];
+    state.typeLoading = false;
+  } catch (error) {
+    state.typeLoading = false;
+  }
+};
 /** 获取知识列表 */
 const queryList = () => {
 	state.tableLoading = true;
@@ -564,8 +576,9 @@ const onRowDel = (row: any) => {
 	}
 };
 onMounted(() => {
+  getKnowledgeType();
+  getOrgListApi();
 	queryList();
-	getOrgListApi();
 });
 const scrollBarRef = ref<RefType>(); // 滚动条ref
 const treeHeight = ref(0);

+ 49 - 75
src/views/tels/callLog/component/Connect-business.vue

@@ -1,86 +1,70 @@
 <template>
 	<div class="tels-callLog-connect-container">
 		<el-dialog v-model="state.dialogVisible" draggable title="关联业务" @close="close">
-			<el-form :model="state.ruleForm" label-width="80px" label-position="left">
-				<el-row :gutter="35">
-					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-						<el-form-item label="关联类型">
-							<el-radio-group v-model="state.ruleForm.isOrder" @change="queryList">
-								<el-radio :label="true">工单</el-radio>
-								<el-radio :label="false">回访</el-radio>
-							</el-radio-group>
-						</el-form-item>
-					</el-col>
-				</el-row>
-			</el-form>
-			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent>
-				<el-row :gutter="10">
-					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+			<div class="flex-center-align mb15">
+				关联类型:
+				<el-radio-group v-model="isOrder" @change="queryList">
+					<el-radio :label="true">工单</el-radio>
+					<el-radio :label="false">回访</el-radio>
+				</el-radio-group>
+			</div>
+			<ProTable
+				ref="proTableRef"
+				:columns="columns"
+				:data="state.tableData"
+				@updateTable="queryList"
+				:loading="loading"
+				:total="state.total"
+				v-model:page-index="state.queryParams.PageIndex"
+				v-model:page-size="state.queryParams.PageSize"
+				:key="Math.random()"
+				v-model:radio="tableRadio"
+				@current-change="handleSelectionChange"
+				max-height="400"
+			>
+				<!-- 表格 header 按钮 -->
+				<template #tableHeader="scope">
+					<el-form :model="state.queryParams" @submit.native.prevent inline>
 						<el-form-item label="" prop="Keyword">
-							<el-input v-model="state.queryParams.Keyword" placeholder="工单编码/标题" clearable @keyup.enter="queryList" />
+							<el-input v-model="state.queryParams.Keyword" placeholder="工单编码/标题" clearable @keyup.enter="queryList" class="keyword-input" />
 						</el-form-item>
-					</el-col>
-					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
-						<el-form-item label=" ">
+						<el-form-item label="" label-width="0">
 							<el-button type="primary" @click="queryList" :loading="loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
 							<el-button
 								@click="
 									state.queryParams.Keyword = null;
 									queryList();
 								"
-								v-waves
 								class="default-button"
 								:loading="loading"
 							>
 								<SvgIcon name="ele-Refresh" class="mr5" />重置
 							</el-button>
 						</el-form-item>
-					</el-col>
-				</el-row>
-			</el-form>
-			<!-- 分页 -->
-<!--			<pagination
-				:total="state.total"
-				v-model:page="state.queryParams.PageIndex"
-				v-model:limit="state.queryParams.PageSize"
-				@pagination="queryList"
-			/>-->
-
-			<ProTable
-				ref="proTableRef"
-				:columns="columns"
-				:data="state.tableData"
-				@updateTable="queryList"
-				:loading="loading"
-				:total="state.total"
-				v-model:page-index="state.queryParams.PageIndex"
-				v-model:page-size="state.queryParams.PageSize"
-				:key="Math.random()"
-				v-model:radio="tableRadio"
-				@current-change="handleSelectionChange"
-			>
+					</el-form>
+				</template>
 				<template #title="{ row }">
-					<order-detail v-if="state.ruleForm.isOrder" :order="row">{{ row.title }}</order-detail>
+					<order-detail v-if="isOrder" :order="row">{{ row.title }}</order-detail>
 					<order-detail v-else :order="row.order">{{ row.order?.title }}</order-detail>
 				</template>
 				<template #acceptorName="{ row }">
-					<span v-if="state.ruleForm.isOrder"
+					<span v-if="isOrder"
 						>{{ row.acceptorName }} <span v-if="row.acceptorStaffNo">[{{ row.acceptorStaffNo }}]</span>
 					</span>
 					<span v-else
 						>{{ row.order?.acceptorName }} <span v-if="row.order?.acceptorStaffNo">[{{ row.order?.acceptorStaffNo }}]</span>
 					</span>
 				</template>
-        <template #voice="{ row }">
-          	<span v-for="item in row.orderVisitDetails">
-								<span v-if="item.visitTarget === 10">{{ item.voiceEvaluateText }}</span>
-							</span>
-        </template>
-        <template #seat="{ row }">
-          	<span v-for="item in row.orderVisitDetails">
-								<span v-if="item.visitTarget === 10">{{ item.seatEvaluateText }}</span>
-							</span>
-        </template>
+				<template #voice="{ row }">
+					<span v-for="item in row.orderVisitDetails">
+						<span v-if="item.visitTarget === 10">{{ item.voiceEvaluateText }}</span>
+					</span>
+				</template>
+				<template #seat="{ row }">
+					<span v-for="item in row.orderVisitDetails">
+						<span v-if="item.visitTarget === 10">{{ item.seatEvaluateText }}</span>
+					</span>
+				</template>
 			</ProTable>
 			<template #footer>
 				<span class="dialog-footer">
@@ -114,7 +98,7 @@ const proTableRef = ref<RefType>(); // 表格ref
 const columns = ref<any[]>([]);
 // 工单表头
 const orderColumns = [
-	{ type: 'radio', fixed: 'left', width: 80, label: '请选择' },
+	{ type: 'radio', fixed: 'left', width: 80, label: '请选择', align: 'center' },
 	{ prop: 'no', label: '工单编码', width: 150 },
 	{ prop: 'isProvince', label: '省/市工单', width: 100, render: (scope: any) => (scope.row.isProvince ? '省工单' : '市工单') },
 	{ prop: 'actualHandleStepName', label: '办理节点', width: 150 },
@@ -132,7 +116,7 @@ const orderColumns = [
 	{ prop: 'acceptorName', label: '受理人', width: 120 },
 ];
 const visitColumns = [
-	{ type: 'radio', fixed: 'left', width: 80, label: '请选择' },
+	{ type: 'radio', fixed: 'left', width: 80, label: '请选择', align: 'center' },
 	{ prop: 'order.no', label: '工单编码', width: 150 },
 	{ prop: 'order.isProvince', label: '省/市工单', width: 100, render: (scope: any) => (scope.row.order?.isProvince ? '省工单' : '市工单') },
 	{ prop: 'order.title', label: '工单标题', width: 300 },
@@ -154,21 +138,17 @@ const visitColumns = [
 	{
 		prop: 'voice',
 		label: '语音评价',
-		width: 150
+		width: 150,
 	},
 	{
 		prop: 'seats',
 		label: '话务员满意度',
-		width: 150
+		width: 150,
 	},
 ];
 
 // 定义变量内容
 const state = reactive<any>({
-	ruleForm: {
-		isOrder: true, // 是否工单
-		specialFlag: null,
-	},
 	queryParams: {
 		// 查询条件
 		PageIndex: 1, // 当前页
@@ -181,6 +161,7 @@ const state = reactive<any>({
 	currentCall: {}, // 当前通话记录
 });
 const loading = ref<Boolean>(false);
+const isOrder = ref<Boolean>(true); // 关联类型 true工单 false回访
 // 打开弹窗
 const openDialog = (val: any) => {
 	state.currentCall = val;
@@ -189,8 +170,8 @@ const openDialog = (val: any) => {
 };
 const queryList = () => {
 	loading.value = true;
-	columns.value = state.ruleForm.isOrder ? orderColumns : visitColumns;
-	if (state.ruleForm.isOrder) {
+	columns.value = isOrder.value ? orderColumns : visitColumns;
+	if (isOrder.value) {
 		callLogOrder(state.queryParams)
 			.then((res: any) => {
 				state.tableData = res.result?.items ?? [];
@@ -221,35 +202,28 @@ const closeDialog = () => {
 	state.dialogVisible = false;
 };
 const close = () => {
-	state.ruleForm.isOrder = true;
+	isOrder.value = true;
 	state.queryParams.PageIndex = 1;
 	state.queryParams.PageSize = 10;
 	state.queryParams.Keyword = null;
 	state.tableData = [];
 	state.total = 0;
 	tableRadio.value = '';
-	selectRow.value = {};
 };
 const tableRadio = ref<String>(''); // 选择的单选框
-const selectRow = ref<EmptyObjectType>({}); // 选择的行
 // 选择重复件
 const handleSelectionChange = (row: any) => {
 	if (row) {
 		tableRadio.value = row.id;
-		selectRow.value = row;
 	}
 };
-const handleRowChange = (row: any) => {
-	tableRadio.value = row.id;
-	selectRow.value = row;
-};
 // 保存
 const onSubmit = () => {
 	loading.value = true;
 	const request = {
 		callId: state.currentCall.callAccept,
 		id: tableRadio.value,
-		isOrder: state.ruleForm.isOrder,
+		isOrder: isOrder.value,
 	};
 	callLogLink(request)
 		.then(() => {

+ 366 - 53
src/views/tels/callLog/index.vue

@@ -1,71 +1,137 @@
 <template>
 	<div class="tels-callLog-container layout-pd">
 		<el-card shadow="never">
-			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent label-width="100px">
+			<el-tabs v-model="state.queryParams.type" class="demo-tabs" @tab-change="changeTba">
+				<el-tab-pane name="0" label="全部"></el-tab-pane>
+				<el-tab-pane name="1" label="呼入"></el-tab-pane>
+				<el-tab-pane name="2" label="呼出"></el-tab-pane>
+				<el-tab-pane name="3" label="未接"></el-tab-pane>
+			</el-tabs>
+			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent label-width="20px">
 				<el-row :gutter="10">
 					<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
-						<el-form-item label="通话时间段" prop="crTime">
-							<el-date-picker
-								v-model="state.queryParams.crTime"
-								type="datetimerange"
-								unlink-panels
-								range-separator="至"
-								start-placeholder="开始时间"
-								end-placeholder="结束时间"
-								:shortcuts="shortcuts"
-								@change="queryList"
-								value-format="YYYY-MM-DD[T]HH:mm:ss"
-							/>
+						<el-form-item prop="CPN">
+							<el-input v-model="state.queryParams.CPN" placeholder="主叫号码" clearable @keyup.enter="queryList" />
 						</el-form-item>
 					</el-col>
 					<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
-						<el-form-item label="主叫" prop="CPN">
-							<el-input v-model="state.queryParams.CPN" placeholder="请输入主叫号码" clearable @keyup.enter="queryList" />
+						<el-form-item prop="CDPN">
+							<el-input v-model="state.queryParams.CDPN" placeholder="被叫号码" clearable @keyup.enter="queryList" />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="['0', '1', '3'].includes(state.queryParams.type)">
+						<el-form-item prop="TelNo">
+							<el-input v-model="state.queryParams.TelNo" placeholder="响应分机" clearable @keyup.enter="queryList" />
 						</el-form-item>
 					</el-col>
 					<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
-						<el-form-item label="被叫" prop="CDPN">
-							<el-input v-model="state.queryParams.CDPN" placeholder="请输入被叫号码" clearable @keyup.enter="queryList" />
+						<el-form-item prop="UserName">
+							<el-input v-model="state.queryParams.UserName" placeholder="话务员名称" clearable @keyup.enter="queryList" />
 						</el-form-item>
 					</el-col>
 					<transition name="el-zoom-in-top">
-						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol">
-							<el-form-item label="电话方向" prop="Direction">
-								<el-select v-model="state.queryParams.Direction" placeholder="请选择电话方向" clearable class="w100">
+						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol && ['0', '3'].includes(state.queryParams.type)">
+							<el-form-item prop="CallDirection">
+								<el-select v-model="state.queryParams.CallDirection" placeholder="电话方向" clearable class="w100">
 									<el-option v-for="item in state.callDirection" :value="item.key" :key="item.key" :label="item.value" />
 								</el-select>
 							</el-form-item>
 						</el-col>
 					</transition>
 					<transition name="el-zoom-in-top">
-						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol">
-							<el-form-item label="通话结果" prop="OnState">
-								<el-select v-model="state.queryParams.OnState" placeholder="请选择通话结果" clearable class="w100">
+						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol && ['0'].includes(state.queryParams.type)">
+							<el-form-item prop="OnState">
+								<el-select v-model="state.queryParams.OnState" placeholder="通话结果" clearable class="w100">
 									<el-option v-for="item in state.onState" :value="item.key" :key="item.key" :label="item.value" />
 								</el-select>
 							</el-form-item>
 						</el-col>
 					</transition>
+					<transition name="el-zoom-in-top">
+						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol && ['0', '1', '2'].includes(state.queryParams.type)">
+							<el-form-item prop="EndBy">
+								<el-select v-model="state.queryParams.EndBy" placeholder="挂机类型" clearable class="w100">
+									<el-option v-for="item in state.endByOptions" :value="item.key" :key="item.key" :label="item.value" />
+								</el-select>
+							</el-form-item>
+						</el-col>
+					</transition>
+					<transition name="el-zoom-in-top">
+						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol && ['0', '1', '3'].includes(state.queryParams.type)">
+							<el-form-item prop="ivrTime">
+								<el-date-picker
+									v-model="state.queryParams.ivrTime"
+									type="datetimerange"
+									unlink-panels
+									range-separator="至"
+									start-placeholder="ivr开始时间"
+									end-placeholder="ivr结束时间"
+									:shortcuts="shortcuts"
+									@change="timeStartChangeIvr"
+									value-format="YYYY-MM-DD[T]HH:mm:ss"
+								/>
+							</el-form-item>
+						</el-col>
+					</transition>
+					<transition name="el-zoom-in-top">
+						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol && ['0', '1', '3'].includes(state.queryParams.type)">
+							<el-form-item prop="callTime">
+								<el-date-picker
+									v-model="state.queryParams.callTime"
+									type="datetimerange"
+									unlink-panels
+									range-separator="至"
+									start-placeholder="队列开始时间"
+									end-placeholder="队列结束时间"
+									:shortcuts="shortcuts"
+									@change="timeStartChangeCall"
+									value-format="YYYY-MM-DD[T]HH:mm:ss"
+								/>
+							</el-form-item>
+						</el-col>
+					</transition>
 					<transition name="el-zoom-in-top">
 						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol">
-							<el-form-item label="中继号" prop="Gateway">
-								<el-input v-model="state.queryParams.Gateway" placeholder="请输入中继号" clearable @keyup.enter="queryList" />
+							<el-form-item prop="crTime">
+								<el-date-picker
+									v-model="state.queryParams.crTime"
+									type="datetimerange"
+									unlink-panels
+									range-separator="至"
+									start-placeholder="通话开始时间"
+									end-placeholder="通话结束时间"
+									:shortcuts="shortcuts"
+									@change="timeStartChange"
+									value-format="YYYY-MM-DD[T]HH:mm:ss"
+								/>
+							</el-form-item>
+						</el-col>
+					</transition>
+					<transition name="el-zoom-in-top">
+						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol && ['0', '1', '2'].includes(state.queryParams.type)">
+							<el-form-item prop="OrderNo">
+								<el-input v-model="state.queryParams.OrderNo" placeholder="工单编码" clearable @keyup.enter="queryList" />
+							</el-form-item>
+						</el-col>
+					</transition>
+					<transition name="el-zoom-in-top">
+						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol && ['0', '1', '2'].includes(state.queryParams.type)">
+							<el-form-item prop="Title">
+								<el-input v-model="state.queryParams.Title" placeholder="工单标题" clearable @keyup.enter="queryList" />
 							</el-form-item>
 						</el-col>
 					</transition>
-					<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
-						<div class="flex-end w100">
-							<el-button type="primary" @click="queryList" :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-button link type="primary" @click="closeSearch" :loading="state.loading">
-								{{ searchCol ? '展开' : '收起' }}
-								<SvgIcon :class="{ 'is-reverse': searchCol }" name="ele-ArrowUp" class="mr5 arrow" size="18px" />
-							</el-button>
-						</div>
-					</el-col>
 				</el-row>
+				<div class="w100 ml20">
+					<el-button type="primary" @click="queryList" :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-button link type="primary" @click="closeSearch" :loading="state.loading">
+						{{ searchCol ? '展开' : '收起' }}
+						<SvgIcon :class="{ 'is-reverse': searchCol }" name="ele-ArrowUp" class="mr5 arrow" size="18px" />
+					</el-button>
+				</div>
 			</el-form>
 		</el-card>
 		<el-card shadow="never">
@@ -78,12 +144,24 @@
 				:total="state.total"
 				v-model:page-index="state.queryParams.PageIndex"
 				v-model:page-size="state.queryParams.PageSize"
+				:key="Math.random()"
 			>
 				<!-- 表格操作 -->
 				<template #operation="{ row }">
-					<el-button link type="primary" @click="onConnect(row)" title="关联业务" v-auth="'tels:callLog:connect'"> 关联业务 </el-button>
-					<el-button type="primary" @click="onPlaySoundRecording(row)" title="播放录音" link v-if="row.recordingFileUrl">播放录音</el-button>
-					<el-button link type="primary" @click="onDownload(row)" title="下载录音" v-if="row.recordingFileUrl"> 下载录音 </el-button>
+					<template v-if="['0', '1'].includes(state.queryParams.type)">
+						<el-button link type="primary" @click="onCreate(row)" title="创建失联工单" v-auth="'tels:callLog:connect'" v-if="!row.externalId">
+							失联工单
+						</el-button>
+						<el-button link type="primary" @click="onConnect(row)" title="关联业务" v-auth="'tels:callLog:connect'" v-if="!row.externalId">
+							关联业务
+						</el-button>
+						<el-button type="primary" @click="onPlaySoundRecording(row)" title="播放录音" link v-if="row.recordingFileUrl">播放录音</el-button>
+						<el-button link type="primary" @click="onDownload(row)" title="下载录音" v-if="row.recordingFileUrl"> 下载录音 </el-button>
+					</template>
+					<template v-else>
+						<el-button type="primary" @click="onPlaySoundRecording(row)" title="播放录音" link v-if="row.recordingFileUrl">播放录音</el-button>
+						<el-button link type="primary" @click="onDownload(row)" title="下载录音" v-if="row.recordingFileUrl"> 下载录音 </el-button>
+					</template>
 				</template>
 			</ProTable>
 		</el-card>
@@ -102,6 +180,8 @@ import { downloadFile, throttle } from '@/utils/tools';
 import { callBaseData, callLogPaged } from '@/api/tels/callLog';
 import { formatDate } from '@/utils/formatTime';
 import { shortcuts } from '@/utils/constants';
+import other from '@/utils/other';
+import { useRouter } from 'vue-router';
 
 // 引入组件
 const PlayRecord = defineAsyncComponent(() => import('@/views/tels/callLog/component/Play-record.vue')); // 播放录音
@@ -164,16 +244,22 @@ const state = reactive({
 	queryParams: {
 		PageIndex: 1, // 当前页
 		PageSize: 10, // 每页条数
+		type: '0',
 		StaffNo: null, // 分机号
 		CPN: null, // 中继号码
 		CDPN: null, // 分机号
-		Direction: null, // 呼叫类型
+		callDirection: null, // 呼叫类型
 		OnState: null, // 结果
 		crTime: [],
+		callTime: [],
+		ivrTime: [],
 	},
 	tableData: [], // 列表数据
 	loading: false, // 加载
 	total: 0, // 总条数
+	callDirection: [],
+	onState: [],
+	endByOptions: [],
 });
 const ruleFormRef = ref<FormInstance>(); // 表单ref
 const searchCol = ref(true); // 展开/收起
@@ -181,22 +267,230 @@ const searchCol = ref(true); // 展开/收起
 const closeSearch = () => {
 	searchCol.value = !searchCol.value;
 };
+const handleTimeChange = (val: string[], startKey: string, endKey: string) => {
+	if (val) {
+		state.queryParams[startKey] = val[0];
+		state.queryParams[endKey] = val[1];
+	} else {
+		state.queryParams[startKey] = '';
+		state.queryParams[endKey] = '';
+	}
+	queryList();
+};
+// IVR时间段
+const timeStartChangeIvr = (val: string[]) => {
+	handleTimeChange(val, 'BeginIvrTime', 'EndIvrTime');
+};
+// 队列时间段
+const timeStartChangeCall = (val: string[]) => {
+	handleTimeChange(val, 'BeginQueueTime', 'EndQueueTime');
+};
+// 通话时间段
+const timeStartChange = (val: string[]) => {
+	handleTimeChange(val, 'AnsweredTime', 'OverTime');
+};
+// 全部表头
+const allColumns = [
+	{ prop: 'cpn', label: '主叫号码', width: 120 },
+	{ prop: 'cdpn', label: '被叫号码', width: 120 },
+	{ prop: 'telNo', label: '响应分机', width: 120 },
+	{ prop: 'userName', label: '话务员', width: 120 },
+	{ prop: 'duration', label: '通话时间(秒)', width: 120 },
+	{ prop: 'onStateText', label: '通话结果' },
+	{ prop: 'callDirectionText', label: '电话方向' },
+	{ prop: 'endByText', label: '挂机类型', width: 120 },
+	{
+		prop: 'beginIvrTime',
+		label: 'ivr开始时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.beginIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'endIvrTime',
+		label: 'ivr结束时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.endIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'beginQueueTime',
+		label: '队列开始时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.beginQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'endQueueTime',
+		label: '队列结束时间',
+		width: 170,
+		render: (scope) => {
+			return <span>{formatDate(scope.row.endQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
+		},
+	},
+	{
+		prop: 'createdTime',
+		label: '开始通话时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'answeredTime',
+		label: '应答时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.answeredTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{ prop: 'overTime', label: '结束通话时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
+	{ prop: 'order.no', label: '工单编码', width: 150 },
+	{ prop: 'order.title', label: '工单标题', width: 300 },
+	{ prop: 'operation', label: '操作', fixed: 'right', width: 310, align: 'center' },
+];
+// 呼入表头
+const inColumns = [
+	{ prop: 'cpn', label: '主叫号码', width: 120 },
+	{ prop: 'cdpn', label: '被叫号码', width: 120 },
+	{ prop: 'telNo', label: '响应分机', width: 120 },
+	{ prop: 'userName', label: '话务员', width: 120 },
+	{ prop: 'duration', label: '通话时间(秒)', width: 120 },
+	{ prop: 'endByText', label: '挂机类型', width: 120 },
+	{
+		prop: 'beginIvrTime',
+		label: 'ivr开始时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.beginIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'endIvrTime',
+		label: 'ivr结束时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.endIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'beginQueueTime',
+		label: '队列开始时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.beginQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'endQueueTime',
+		label: '队列结束时间',
+		width: 170,
+		render: (scope) => {
+			return <span>{formatDate(scope.row.endQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
+		},
+	},
+	{
+		prop: 'createdTime',
+		label: '开始通话时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'answeredTime',
+		label: '应答时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.answeredTime, ' YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{ prop: 'overTime', label: '结束通话时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
+	{ prop: 'order.no', label: '工单编码', width: 150 },
+	{ prop: 'order.title', label: '工单标题', width: 300 },
+	{ prop: 'operation', label: '操作', fixed: 'right', width: 310, align: 'center' },
+];
+// 呼出表头
+const outColumns = [
+	{ prop: 'cpn', label: '主叫号码', width: 120 },
+	{ prop: 'cdpn', label: '被叫号码', width: 120 },
+	{ prop: 'telNo', label: '响应分机' },
+	{ prop: 'userName', label: '话务员' },
+	{ prop: 'duration', label: '通话时间(秒)', width: 120 },
+	{ prop: 'endByText', label: '挂机类型', width: 120 },
+	{
+		prop: 'createdTime',
+		label: '开始通话时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'answeredTime',
+		label: '应答时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.answeredTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{ prop: 'overTime', label: '结束通话时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
+	{ prop: 'operation', label: '操作', fixed: 'right', width: 180, align: 'center' },
+];
+// 未接表头
+const noColumns = [
+	{ prop: 'cpn', label: '主叫号码', width: 120 },
+	{ prop: 'cdpn', label: '被叫号码', width: 120 },
+	{ prop: 'telNo', label: '响应分机' },
+	{ prop: 'userName', label: '话务员' },
+	{ prop: 'callDirectionText', label: '电话方向' },
+	{
+		prop: 'beginIvrTime',
+		label: 'ivr开始时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.beginIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'endIvrTime',
+		label: 'ivr结束时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.endIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'beginQueueTime',
+		label: '队列开始时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.beginQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{
+		prop: 'endQueueTime',
+		label: '队列结束时间',
+		width: 170,
+		render: (scope) => {
+			return <span>{formatDate(scope.row.endQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
+		},
+	},
+	{
+		prop: 'createdTime',
+		label: '开始通话时间',
+		width: 170,
+		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{ prop: 'overTime', label: '结束通话时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
+];
+const changeTba = () => {
+	ruleFormRef.value.resetFields();
+	queryList();
+};
 /** 通话记录列表 */
 const queryList = throttle(async () => {
 	state.loading = true;
 	try {
-		const request = {
-			PageIndex: state.queryParams.PageIndex,
-			PageSize: state.queryParams.PageSize,
-			CPN: state.queryParams.CPN,
-			CDPN: state.queryParams.CDPN,
-			Direction: state.queryParams.Direction,
-			OnState: state.queryParams.OnState,
-			StaffNo: state.queryParams.StaffNo,
-			StartTime: state.queryParams.crTime[0],
-			EndTime: state.queryParams.crTime[1],
-			Gateway: state.queryParams.Gateway,
-		};
+		let request = other.deepClone(state.queryParams);
+		Reflect.deleteProperty(request, 'crTime'); // 删除无用的参数
+		Reflect.deleteProperty(request, 'callTime'); // 删除无用的参数
+		Reflect.deleteProperty(request, 'ivrTime'); // 删除无用的参数
+		Reflect.deleteProperty(request, 'type'); // 删除无用的参数
+
+		switch (state.queryParams.type) {
+			case '0':
+				columns.value = allColumns;
+				break;
+			case '1':
+				columns.value = inColumns;
+				request.CallDirection = 0;
+				break;
+			case '2':
+				columns.value = outColumns;
+				request.CallDirection = 1;
+				break;
+			case '3':
+				columns.value = noColumns;
+				request.OnState = 2;
+				break;
+			default:
+				columns.value = allColumns;
+				break;
+		}
 		const response = await callLogPaged(request);
 		state.tableData = response.result?.items ?? [];
 		state.total = response.result?.total ?? 0;
@@ -238,11 +532,30 @@ const connectBusinessRef = ref<RefType>();
 const onConnect = (row: any) => {
 	connectBusinessRef.value.openDialog(row);
 };
+// 失联工单
+const router = useRouter();
+const onCreate = (row: any) => {
+	router.push({
+		name: 'orderAccept',
+		state: {
+			createBy: 'tel',
+			fromTel: row.cpn,
+			telGuid: row.callAccept,
+			transfer: row.gateway,
+			telArea: '',
+		},
+		params: {
+			callId: row.callAccept,
+			tagsViewName: '创建失联工单',
+		},
+	});
+};
 // 基础信息
 const getBaseData = async () => {
 	const response = await callBaseData();
 	state.callDirection = response.result.callDirection;
 	state.onState = response.result.onState;
+	state.endByOptions = response.result.endBy;
 };
 onMounted(() => {
 	getBaseData();

+ 1 - 0
src/views/todo/seats/accept/index.vue

@@ -876,6 +876,7 @@ const save = throttle((formEl: FormInstance | undefined) => {
 					ElMessage.success('操作成功');
 					mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 1, ...route }));
 					mittBus.emit('clearCache', 'order');
+          mittBus.emit('clearCache', 'callLog');
 					router.go(-1);
 				})
 				.catch(() => {

+ 0 - 4
src/views/todo/seats/index.vue

@@ -183,10 +183,6 @@ const onAddOrder = () => {
 		},
 	});
 };
-// 导出
-const onExport = () => {
-	console.log('导出');
-};
 // 编辑工单
 const onOrderEdit = (row: any) => {
 	if (!router.hasRoute('orderAccept')) {