Browse Source

reactor:对接自贡质检;

zhangchong 3 tháng trước cách đây
mục cha
commit
8a606e2e14

+ 1 - 1
src/api/quality/index.ts

@@ -176,7 +176,7 @@ export const qualityScoreGrade = (params: object) => {
  * @description 坐席质检分值分析基础数据
  * @param {object}  params
  */
-export const qualityScoreBase = (params: object) => {
+export const qualityScoreBase = (params?: object) => {
 	return request({
 		url: `/api/v1/BiQuality/quality_work/base`,
 		method: 'get',

+ 3 - 1
src/components/NumberRange/index.vue

@@ -48,7 +48,7 @@ const props = defineProps({
 		default: 100,
 	},
 });
-const emit = defineEmits(['update:modelValue']);
+const emit = defineEmits(['update:modelValue','change']);
 const userInputForm = ref<any>(null);
 const userInputTo = ref<any>(null);
 watch(
@@ -61,9 +61,11 @@ watch(
 );
 const handleInputChangeFrom = () => {
 	emit('update:modelValue', [userInputForm.value, userInputTo.value]);
+  emit('change', [userInputForm.value, userInputTo.value])
 };
 const handleInputChangeTo = () => {
 	emit('update:modelValue', [userInputForm.value, userInputTo.value]);
+  emit('change', [userInputForm.value, userInputTo.value])
 };
 </script>
 <style lang="scss" scoped>

+ 4 - 2
src/utils/echarts.ts

@@ -16,7 +16,8 @@ import {
   GeoComponent,
   MarkPointComponent,
   GraphicComponent,
-  DataZoomComponent
+  DataZoomComponent,
+  ToolboxComponent
 } from 'echarts/components'
 
 use([
@@ -31,7 +32,8 @@ use([
   GeoComponent,
   MarkPointComponent,
   GraphicComponent,
-  DataZoomComponent
+  DataZoomComponent,
+  ToolboxComponent
 ])
 
 export const registerEcharts = (app: any) => {

+ 226 - 222
src/views/quality/done/ZGQualityDone.vue

@@ -1,280 +1,284 @@
 <template>
-  <div class="quality-done-container layout-padding">
-    <div class="layout-padding-auto layout-padding-view pd20">
-      <el-tabs v-model="state.queryParams.Source" @tab-change="handleQuery">
-        <el-tab-pane label="受理已质检" :name="1" :disabled="state.loading"></el-tab-pane>
-        <el-tab-pane label="派单已质检" :name="2" :disabled="state.loading"></el-tab-pane>
-        <el-tab-pane label="回访已质检" :name="3" :disabled="state.loading"></el-tab-pane>
-      </el-tabs>
-      <el-form :model="state.queryParams" ref="ruleFormRef" inline @submit.native.prevent>
-        <el-form-item label="关键词" prop="Keyword">
-          <el-input v-model="state.queryParams.Keyword" placeholder="关键词" clearable @keyup.enter="handleQuery" class="keyword-input"/>
-        </el-form-item>
-        <el-form-item label="分数区间" prop="value">
-          <input-number-range v-model="state.queryParams.value" />
-        </el-form-item>
-        <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="handleQuery"
-              value-format="YYYY-MM-DD[T]HH:mm:ss"
-              :default-time="defaultTimeStartEnd"
-          />
-        </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)" v-waves class="default-button">
-            <SvgIcon name="ele-Refresh" class="mr5" />重置
-          </el-button>
-        </el-form-item>
-      </el-form>
-      <vxe-toolbar
-          ref="toolbarRef"
-          :loading="state.loading"
-          custom
-          :refresh="{
+	<div class="quality-done-container layout-padding">
+		<div class="layout-padding-auto layout-padding-view pd20">
+			<el-tabs v-model="state.queryParams.Source" @tab-change="handleQuery">
+				<el-tab-pane label="受理已质检" :name="1" :disabled="state.loading"></el-tab-pane>
+				<el-tab-pane label="派单已质检" :name="2" :disabled="state.loading"></el-tab-pane>
+				<el-tab-pane label="回访已质检" :name="3" :disabled="state.loading"></el-tab-pane>
+			</el-tabs>
+			<el-form :model="state.queryParams" ref="ruleFormRef" inline @submit.native.prevent>
+				<el-form-item label="关键词" prop="Keyword">
+					<el-input v-model="state.queryParams.Keyword" placeholder="关键词" clearable @keyup.enter="handleQuery" class="keyword-input" />
+				</el-form-item>
+				<el-form-item label="分数区间" prop="value">
+					<input-number-range v-model="state.queryParams.value" />
+				</el-form-item>
+				<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="handleQuery"
+						value-format="YYYY-MM-DD[T]HH:mm:ss"
+						:default-time="defaultTimeStartEnd"
+					/>
+				</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)" v-waves class="default-button"> <SvgIcon name="ele-Refresh" class="mr5" />重置 </el-button>
+				</el-form-item>
+			</el-form>
+			<vxe-toolbar
+				ref="toolbarRef"
+				:loading="state.loading"
+				custom
+				:refresh="{
 					queryMethod: handleQuery,
 				}"
-      >
-        <template #buttons>
-          <el-button type="primary" @click="onExport" :loading="state.loading" v-auth="'quality:done:export'" :disabled="isChecked">
-            导出质检结果<span v-if="checkTable.length">({{ checkTable.length }})</span>
-          </el-button>
-        </template>
-      </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, useKey: true }"
-            ref="tableRef"
-            height="auto"
-            auto-resize
-            show-overflow
-            :scrollY="{ enabled: true, gt: 100 }"
-            id="qualityDone"
-            :custom-config="{ storage: true }"
-            @checkbox-all="selectAllChangeEvent"
-            @checkbox-change="selectChangeEvent"
-        >
-          <vxe-column type="checkbox" width="50" align="center"></vxe-column>
-          <vxe-column field="order.no" title="工单编码" width="140"></vxe-column>
-          <vxe-column field="order.title" title="工单标题" min-width="200">
-            <template #default="{ row }">
-              <order-detail :order="row.order" @updateList="queryList">{{ row.order?.title }}</order-detail>
-            </template>
-          </vxe-column>
-          <vxe-column field="aiQuality" title="质检方式" width="100">
-            <template #default="{ row }">
-              {{ row.aiQuality ? '智能质检' : '人工质检' }}
-            </template>
-          </vxe-column>
-          <vxe-column field="order.sourceChannel" title="来源渠道" width="110"></vxe-column>
-          <vxe-column field="order.startTime" title="受理时间" width="160">
-            <template #default="{ row }">
-              {{ formatDate(row.order?.startTime, 'YYYY-mm-dd HH:MM:SS') }}
-            </template>
-          </vxe-column>
-          <template v-if="state.queryParams.Source === 3">
-            <vxe-column field="visit.visitStateText" title="回访状态" width="110"></vxe-column>
-            <vxe-column field="visit.visitTypeText" title="回访方式" width="110"></vxe-column>
-            <vxe-column field="order.filedTime" title="办结时间" width="160">
-              <template #default="{ row }">
-                {{ formatDate(row.order?.filedTime, 'YYYY-mm-dd HH:MM:SS') }}
-              </template>
-            </vxe-column>
-            <vxe-column field="visit.publishTime" title="发布时间" width="160">
-              <template #default="{ row }">
-                {{ formatDate(row.visit?.publishTime, 'YYYY-mm-dd HH:MM:SS') }}
-              </template>
-            </vxe-column>
-            <vxe-column field="visit.creationTime" title="回访任务创建时间" width="160">
-              <template #default="{ row }">
-                {{ formatDate(row.visit?.creationTime, 'YYYY-mm-dd HH:MM:SS') }}
-              </template>
-            </vxe-column>
-            <vxe-column field="visit.employeeName" title="回访人" width="120"></vxe-column>
-            <vxe-column field="visit.visitTime" title="回访时间" width="160">
-              <template #default="{ row }">
-                {{ formatDate(row.visit?.visitTime, 'YYYY-mm-dd HH:MM:SS') }}
-              </template>
-            </vxe-column>
-            <vxe-column field="order.counterSignTypeText" title="是否会签" width="100"></vxe-column>
-          </template>
-          <template v-if="state.queryParams.Source === 2">
-            <vxe-column field="creationTime" title="交办时间" width="160">
-              <template #default="{ row }">
-                {{ formatDate(row.creationTime, 'YYYY-mm-dd HH:MM:SS') }}
-              </template>
-            </vxe-column>
-          </template>
-          <vxe-column field="order.statusText" title="工单状态" width="100">
-            <template #default="{ row }">
-              <el-text type="danger" tag="b" v-if="[1, 2, 3, 9, 101, 102, 103, 104, 105, 200].includes(row.order?.status)">{{ row.order?.statusText }}</el-text>
-              <span v-else>{{ row.order?.statusText }}</span>
-            </template>
-          </vxe-column>
-          <vxe-column field="order.acceptType" title="受理类型" width="100"></vxe-column>
-          <vxe-column field="order.hotspotName" title="热点分类" width="150"></vxe-column>
-          <vxe-column field="order.acceptorName" title="受理人" width="120"></vxe-column>
-          <vxe-column field="order.fromPhone" title="来电电话" width="140"></vxe-column>
-          <vxe-column field="userName" title="质检人" width="120"></vxe-column>
-          <vxe-column field="qualityTime" title="质检时间" width="160">
-            <template #default="{ row }">
-              {{ formatDate(row.qualityTime, 'YYYY-mm-dd HH:MM:SS') }}
-            </template>
-          </vxe-column>
-          <vxe-column field="grade" title="质检得分" width="100"></vxe-column>
-          <vxe-column title="操作" fixed="right" width="120" align="center" :show-overflow="false">
-            <template #default="{ row }">
-              <el-button link type="primary" @click="onQualityView(row)" v-auth="'quality:done:view'" title="查看质检详情"> 查看 </el-button>
-              <el-button link type="primary" @click="onQualityEdit(row)" v-auth="'quality:done:edit'" title="编辑质检"> 编辑 </el-button>
-            </template>
-          </vxe-column>
-        </vxe-table>
-      </div>
-      <pagination
-          @pagination="queryList"
-          :total="state.total"
-          v-model:current-page="state.queryParams.PageIndex"
-          v-model:page-size="state.queryParams.PageSize"
-          :disabled="state.loading"
-      />
-    </div>
-    <!-- 质检 -->
-    <quality-inspection ref="qualityInspectionRef" @updateList="queryList" />
-  </div>
+			>
+				<template #buttons>
+					<el-button type="primary" @click="onExport" :loading="state.loading" v-auth="'quality:done:export'" :disabled="isChecked">
+						导出质检结果<span v-if="checkTable.length">({{ checkTable.length }})</span>
+					</el-button>
+				</template>
+			</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, useKey: true }"
+					ref="tableRef"
+					height="auto"
+					auto-resize
+					show-overflow
+					:scrollY="{ enabled: true, gt: 100 }"
+					id="qualityDone"
+					:custom-config="{ storage: true }"
+					@checkbox-all="selectAllChangeEvent"
+					@checkbox-change="selectChangeEvent"
+				>
+					<vxe-column type="checkbox" width="50" align="center"></vxe-column>
+					<vxe-column field="order.no" title="工单编码" width="140"></vxe-column>
+					<vxe-column field="order.title" title="工单标题" min-width="200">
+						<template #default="{ row }">
+							<order-detail :order="row.order" @updateList="queryList">{{ row.order?.title }}</order-detail>
+						</template>
+					</vxe-column>
+					<vxe-column field="aiQuality" title="质检方式" width="100">
+						<template #default="{ row }">
+							{{ row.aiQuality ? '智能质检' : '人工质检' }}
+						</template>
+					</vxe-column>
+					<vxe-column field="order.sourceChannel" title="来源渠道" width="110"></vxe-column>
+					<vxe-column field="order.startTime" title="受理时间" width="160">
+						<template #default="{ row }">
+							{{ formatDate(row.order?.startTime, 'YYYY-mm-dd HH:MM:SS') }}
+						</template>
+					</vxe-column>
+					<template v-if="state.queryParams.Source === 3">
+						<vxe-column field="visit.visitStateText" title="回访状态" width="110"></vxe-column>
+						<vxe-column field="visit.visitTypeText" title="回访方式" width="110"></vxe-column>
+						<vxe-column field="order.filedTime" title="办结时间" width="160">
+							<template #default="{ row }">
+								{{ formatDate(row.order?.filedTime, 'YYYY-mm-dd HH:MM:SS') }}
+							</template>
+						</vxe-column>
+						<vxe-column field="visit.publishTime" title="发布时间" width="160">
+							<template #default="{ row }">
+								{{ formatDate(row.visit?.publishTime, 'YYYY-mm-dd HH:MM:SS') }}
+							</template>
+						</vxe-column>
+						<vxe-column field="visit.creationTime" title="回访任务创建时间" width="160">
+							<template #default="{ row }">
+								{{ formatDate(row.visit?.creationTime, 'YYYY-mm-dd HH:MM:SS') }}
+							</template>
+						</vxe-column>
+						<vxe-column field="visit.employeeName" title="回访人" width="120"></vxe-column>
+						<vxe-column field="visit.visitTime" title="回访时间" width="160">
+							<template #default="{ row }">
+								{{ formatDate(row.visit?.visitTime, 'YYYY-mm-dd HH:MM:SS') }}
+							</template>
+						</vxe-column>
+						<vxe-column field="order.counterSignTypeText" title="是否会签" width="100"></vxe-column>
+					</template>
+					<template v-if="state.queryParams.Source === 2">
+						<vxe-column field="creationTime" title="交办时间" width="160">
+							<template #default="{ row }">
+								{{ formatDate(row.creationTime, 'YYYY-mm-dd HH:MM:SS') }}
+							</template>
+						</vxe-column>
+					</template>
+					<vxe-column field="order.statusText" title="工单状态" width="100">
+						<template #default="{ row }">
+							<el-text type="danger" tag="b" v-if="[1, 2, 3, 9, 101, 102, 103, 104, 105, 200].includes(row.order?.status)">{{
+								row.order?.statusText
+							}}</el-text>
+							<span v-else>{{ row.order?.statusText }}</span>
+						</template>
+					</vxe-column>
+					<vxe-column field="order.acceptType" title="受理类型" width="100"></vxe-column>
+					<vxe-column field="order.hotspotName" title="热点分类" width="150"></vxe-column>
+					<vxe-column field="order.acceptorName" title="受理人" width="120"></vxe-column>
+					<vxe-column field="order.fromPhone" title="来电电话" width="140"></vxe-column>
+					<vxe-column field="userName" title="质检人" width="120"></vxe-column>
+					<vxe-column field="qualityTime" title="质检时间" width="160">
+						<template #default="{ row }">
+							{{ formatDate(row.qualityTime, 'YYYY-mm-dd HH:MM:SS') }}
+						</template>
+					</vxe-column>
+					<vxe-column field="grade" title="质检得分" width="100"></vxe-column>
+					<vxe-column title="操作" fixed="right" width="120" align="center" :show-overflow="false">
+						<template #default="{ row }">
+							<el-button link type="primary" @click="onQualityView(row)" v-auth="'quality:done:view'" title="查看质检详情"> 查看 </el-button>
+							<el-button link type="primary" @click="onQualityEdit(row)" v-auth="'quality:done:edit'" title="编辑质检"> 编辑 </el-button>
+						</template>
+					</vxe-column>
+				</vxe-table>
+			</div>
+			<pagination
+				@pagination="queryList"
+				:total="state.total"
+				v-model:current-page="state.queryParams.PageIndex"
+				v-model:page-size="state.queryParams.PageSize"
+				:disabled="state.loading"
+			/>
+		</div>
+		<!-- 质检 -->
+		<z-g-quality ref="ZGQualityRef" @updateList="queryList" />
+	</div>
 </template>
 
 <script lang="tsx" setup name="qualityDone">
-import {computed, defineAsyncComponent, onMounted, reactive, ref} from 'vue';
+import { computed, defineAsyncComponent, onMounted, reactive, ref } from 'vue';
 import { FormInstance } from 'element-plus';
 import { formatDate } from '@/utils/formatTime';
 import { qualityBaseData, qualityList } from '@/api/quality';
 import { defaultTimeStartEnd, shortcuts } from '@/utils/constants';
 import Other from '@/utils/other';
-import {exportQuality} from "@/utils/tools";
+import { exportQuality } from '@/utils/tools';
 
 // 引入组件
-const QualityInspection = defineAsyncComponent(() => import('@/views/quality/done/components/Quality-inspection.vue')); // 质检
 const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
 const InputNumberRange = defineAsyncComponent(() => import('@/components/NumberRange/index.vue')); // 数字区间
 const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
+const ZGQuality = defineAsyncComponent(() => import('@/views/quality/done/components/ZG-quality.vue')); // 质检
 
 // 定义变量内容
 const state = reactive<any>({
-  loading: false, // 加载状态
-  queryParams: {
-    // 查询参数
-    PageIndex: 1,
-    PageSize: 20,
-    Source: 1,
-    CreationTimeStart: null, // 创建时间 开始
-    CreationTimeEnd: null, // 创建时间 结束
-    crTime: [], // 办理期限
-    State: 1, // 待质检
-    Keyword: null,
-    value: [0, 100],
-  },
-  total: 0, // 总条数
-  tableData: [], // 表格数据
+	loading: false, // 加载状态
+	queryParams: {
+		// 查询参数
+		PageIndex: 1,
+		PageSize: 20,
+		Source: 1,
+		CreationTimeStart: null, // 创建时间 开始
+		CreationTimeEnd: null, // 创建时间 结束
+		crTime: [], // 办理期限
+		State: 1, // 待质检
+		Keyword: null,
+		value: [0, 100],
+	},
+	total: 0, // 总条数
+	tableData: [], // 表格数据
 });
 const ruleFormRef = ref<RefType>(null); // 表单ref
 const qualitySourceOptions = ref<EmptyArrayType>([]); // 违禁词分类
 const qualityStateOptions = ref<EmptyArrayType>([]); // 违禁词属性
 const getBaseData = async () => {
-  try {
-    const res = await qualityBaseData();
-    qualitySourceOptions.value = res.result?.qualitySource ?? [];
-    qualityStateOptions.value = res.result?.qualityState ?? [];
-  } catch (error) {
-    console.log(error);
-  }
+	try {
+		const res = await qualityBaseData();
+		qualitySourceOptions.value = res.result?.qualitySource ?? [];
+		qualityStateOptions.value = res.result?.qualityState ?? [];
+	} catch (error) {
+		console.log(error);
+	}
 };
 // 手动查询,将页码设置为1
 const handleQuery = () => {
-  state.queryParams.PageIndex = 1;
-  queryList();
+	state.queryParams.PageIndex = 1;
+	queryList();
 };
 // 查询已质检列表
 const requestParams = ref<EmptyObjectType>({});
 const queryList = () => {
-  state.loading = true;
-  requestParams.value = Other.deepClone(state.queryParams);
-  requestParams.value.CreationTimeStart = state.queryParams.crTime === null ? null : state.queryParams.crTime[0]; // 生成时间
-  requestParams.value.CreationTimeEnd = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
-  Reflect.deleteProperty(requestParams.value, 'crTime'); // 删除无用的参数
-  requestParams.value.MinGrade = state.queryParams.value === null ? null : state.queryParams.value[0]; // 分数区间
-  requestParams.value.MaxGrade = state.queryParams.value === null ? null : state.queryParams.value[1];
-  Reflect.deleteProperty(requestParams.value, 'value'); // 删除无用的参数
-  qualityList(requestParams.value)
-      .then((res) => {
-        state.loading = false;
-        state.tableData = res.result.items ?? [];
-        state.total = res.result.total ?? 0;
-      })
-      .finally(() => {
-        state.loading = false;
-      });
+	state.loading = true;
+	requestParams.value = Other.deepClone(state.queryParams);
+	requestParams.value.CreationTimeStart = state.queryParams.crTime === null ? null : state.queryParams.crTime[0]; // 生成时间
+	requestParams.value.CreationTimeEnd = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
+	Reflect.deleteProperty(requestParams.value, 'crTime'); // 删除无用的参数
+	requestParams.value.MinGrade = state.queryParams.value === null ? null : state.queryParams.value[0]; // 分数区间
+	requestParams.value.MaxGrade = state.queryParams.value === null ? null : state.queryParams.value[1];
+	Reflect.deleteProperty(requestParams.value, 'value'); // 删除无用的参数
+	qualityList(requestParams.value)
+		.then((res) => {
+			state.loading = false;
+			state.tableData = res.result.items ?? [];
+			state.total = res.result.total ?? 0;
+			tableRef.value?.clearCheckboxRow();
+			checkTable.value = [];
+		})
+		.finally(() => {
+			state.loading = false;
+			tableRef.value?.clearCheckboxRow();
+			checkTable.value = [];
+		});
 };
 // 重置表单
 const resetQuery = (formEl: FormInstance | undefined) => {
-  if (!formEl) return;
-  formEl.resetFields();
-  state.queryParams.CreationTimeStart = null;
-  state.queryParams.CreationTimeEnd = null;
-  queryList();
+	if (!formEl) return;
+	formEl.resetFields();
+	state.queryParams.CreationTimeStart = null;
+	state.queryParams.CreationTimeEnd = null;
+	queryList();
 };
 // 质检查看
-const qualityInspectionRef = ref<RefType>();
+const ZGQualityRef = ref<RefType>();
 const onQualityView = (row: any) => {
-  qualityInspectionRef.value.openDialog(row, state.queryParams.Source, true);
+	ZGQualityRef.value.openDialog(row, state.queryParams.Source, true);
 };
 // 质检编辑
 const onQualityEdit = (row: any) => {
-  qualityInspectionRef.value.openDialog(row, state.queryParams.Source);
+	ZGQualityRef.value.openDialog(row, state.queryParams.Source);
 };
 // 页面加载时
 const toolbarRef = ref<RefType>();
 const checkTable = ref<EmptyArrayType>([]);
 const selectAllChangeEvent = ({ checked }) => {
-  if (tableRef.value) {
-    const records = tableRef.value.getCheckboxRecords();
-    checkTable.value = records;
-    console.log(checked ? '所有勾选事件' : '所有取消事件', records);
-  }
+	if (tableRef.value) {
+		const records = tableRef.value.getCheckboxRecords();
+		checkTable.value = records;
+		console.log(checked ? '所有勾选事件' : '所有取消事件', records);
+	}
 };
 
 const selectChangeEvent = ({ checked }) => {
-  if (tableRef.value) {
-    const records = tableRef.value.getCheckboxRecords();
-    checkTable.value = records;
-    console.log(checked ? '勾选事件' : '取消事件', records);
-  }
+	if (tableRef.value) {
+		const records = tableRef.value.getCheckboxRecords();
+		checkTable.value = records;
+		console.log(checked ? '勾选事件' : '取消事件', records);
+	}
 };
 const isChecked = computed(() => {
-  return !checkTable.value.length;
+	return !checkTable.value.length;
 });
 // 质检单导出
 const onExport = () => {
-  const ids = checkTable.value.map((item) => item.id);
-  exportQuality(ids);
+	const ids = checkTable.value.map((item) => item.id);
+	exportQuality(ids);
 };
 // 页面加载时
 const tableRef = ref<RefType>();
 onMounted(() => {
-  queryList();
-  if (tableRef.value && toolbarRef.value) {
-    tableRef.value.connect(toolbarRef.value);
-  }
-  getBaseData();
+	queryList();
+	if (tableRef.value && toolbarRef.value) {
+		tableRef.value.connect(toolbarRef.value);
+	}
+	getBaseData();
 });
-</script>
+</script>

+ 1 - 1
src/views/quality/done/components/Quality-inspection.vue

@@ -454,7 +454,7 @@ const onSubmit = (formEl: FormInstance | undefined) => {
 		};
 		qualityUpdate(request)
 			.then(() => {
-				ElMessage.success('操作成功');
+				ElMessage.success('质检成功');
 				emit('updateList');
 				state.loading = false;
 				state.dialogVisible = false;

+ 527 - 0
src/views/quality/done/components/ZG-quality.vue

@@ -0,0 +1,527 @@
+<template>
+	<el-dialog
+		v-model="state.dialogVisible"
+		:title="dialogTitle"
+		draggable
+		ref="dialogRef"
+		width="90%"
+		append-to-body
+		destroy-on-close
+		@close="close"
+		@opened="opened"
+	>
+		<splitpanes Vertical>
+			<pane min-size="60" max-size="60" size="60" style="max-height: calc(84vh - 111px); overflow-y: auto">
+				<p class="border-title mb10">工单详情</p>
+				<el-form :model="state.orderDetail" label-width="90px" v-loading="state.loading" class="show-info-form">
+					<el-row :gutter="10">
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="工单编码">
+								{{ state.orderDetail.no }}
+								<span v-if="state.orderDetail.password && userInfos.isCenter" class="ml5">({{ state.orderDetail.password }})</span>
+							</el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8" v-if="state.orderDetail.receiveProvinceNo">
+							<el-form-item label="省编码"> {{ state.orderDetail.receiveProvinceNo }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="来源方式"> {{ state.orderDetail.sourceChannel }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="转接来源"> {{ state.orderDetail.transferPhone }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="来电人"> {{ state.orderDetail.fromName }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="性别"> {{ state.orderDetail.fromGenderText }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="来电号码">
+								{{ state.orderDetail.fromPhone }}
+								<el-button
+									plain
+									title="工单录音文件"
+									v-if="
+										['ZiGong'].includes(themeConfig.appScope)
+											? state.orderDetail?.recordingAbsolutePath && userInfos.isCenter
+											: state.orderDetail?.recordingAbsolutePath
+									"
+									size="small"
+									type="primary"
+									class="ml8"
+									@click="recordFile(state.orderDetail, 'order')"
+									>工单录音</el-button
+								>
+								<el-button
+									plain
+									title="回访录音文件"
+									v-if="state.visit.recordingAbsolutePath"
+									size="small"
+									type="primary"
+									class="ml8"
+									@click="recordFile(state.visit, 'visit')"
+									>回访录音</el-button
+								></el-form-item
+							>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="联系电话"> {{ state.orderDetail.contact }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="受理类型"> {{ state.orderDetail.acceptType }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="办理时限"> {{ state.orderDetail.timeLimit }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="热点分类"> {{ state.orderDetail.hotspotSpliceName }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="受理人"> {{ state.orderDetail.acceptorName }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="受理时间"> {{ formatDate(state.orderDetail.startTime, 'YYYY-mm-dd HH:MM:SS') }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="来电人主体"> {{ state.orderDetail.identityTypeText }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<el-form-item label="推送分类"> {{ state.orderDetail.pushType }} </el-form-item>
+						</el-col>
+						<el-col>
+							<el-form-item label="事发地址"> {{ state.orderDetail.fullAddress }} </el-form-item>
+						</el-col>
+						<el-col>
+							<el-form-item label="工单标题" class="formatted-text mb5"> {{ state.orderDetail.title }} </el-form-item>
+						</el-col>
+						<el-col>
+							<el-form-item label="受理内容" class="formatted-text mb5"> {{ state.orderDetail.content }} </el-form-item>
+						</el-col>
+						<el-col>
+							<el-form-item label="承办意见" class="formatted-text mb5"> {{ state.orderDetail.fileOpinion }} </el-form-item>
+						</el-col>
+					</el-row>
+				</el-form>
+				<!-- 受理质检才有质检录音撰写 -->
+				<template v-if="state.source === 1">
+					<p class="border-title mb10 mt20">录音转写</p>
+					<!-- 未转写 -->
+					<template v-if="!state.qualityDetail.transferState">
+						<el-empty description="无录音转写内容" :image-size="100" style="padding: 0"> </el-empty>
+					</template>
+					<!-- 转写中 -->
+					<el-empty description="录音转写中,请稍后" :image-size="100" style="padding: 0" v-if="state.qualityDetail.transferState === 1"> </el-empty>
+					<!-- 转写完成 -->
+					<template v-if="state.qualityDetail.transferState === 2">
+						<template v-if="transformContent && transformContent.length">
+							<el-scrollbar class="transfer-content">
+								<div v-for="(item, index) in transformContent" :key="index" class="transfer-content-item">
+									<div>
+										<span>{{ item.type === 0 ? '坐席:' : '市民:' }}</span>
+										<div v-html="showKeyWord(item.reviseContent, item.prohibited)" class="formatted-text"></div>
+									</div>
+								</div>
+							</el-scrollbar>
+						</template>
+						<el-empty description="无录音转写内容" :image-size="100" style="padding: 0" v-else> </el-empty>
+					</template>
+					<!-- 转写失败 -->
+					<template v-if="state.qualityDetail.transferState === 3">
+						<el-empty description="录音转写失败" :image-size="100" style="padding: 0"> </el-empty>
+					</template>
+				</template>
+			</pane>
+			<pane style="max-height: calc(84vh - 111px); overflow-y: auto">
+				<p class="border-title">质检</p>
+				<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="110px" v-loading="state.loading">
+					<div class="mt20 mb20">
+						<el-button type="primary" class="mb10" @click="onAddItem" v-if="!viewMode">新增质检项</el-button>
+						<!-- 表格 -->
+						<el-table :data="tableData" border row-key="name" ref="multipleTableRef" @selection-change="handleSelectionChange">
+							<el-table-column type="selection" width="55" align="center" :reserve-selection="true" :selectable="selectable" />
+							<el-table-column prop="name" label="质检项" show-overflow-tooltip align="center"></el-table-column>
+							<el-table-column label="质检类型" show-overflow-tooltip align="center">
+								<template #default="{ row }">
+									<span> {{ row.intelligent ? '智能质检' : '人工质检' }}</span>
+								</template>
+							</el-table-column>
+							<el-table-column prop="content" label="质检内容" show-overflow-tooltip align="center"></el-table-column>
+							<el-table-column prop="grade" label="分值(分)" show-overflow-tooltip align="center"></el-table-column>
+							<el-table-column label="操作" width="120" fixed="right" align="center" v-if="!viewMode">
+								<template #default="{ row }">
+									<!-- 智能质检不能编辑就和删除 -->
+									<el-button link type="primary" @click="onEditItem(row)" v-if="!row.intelligent"> 编辑 </el-button>
+									<el-button link type="primary" @click="onDeleteItem(row)" v-if="!row.intelligent"> 删除 </el-button>
+								</template>
+							</el-table-column>
+						</el-table>
+					</div>
+					<el-row :gutter="10">
+						<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+							<el-form-item label="总分:"> 100 分 </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+							<el-form-item label="当前质检得分:"> {{ score }} </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+							<el-form-item label="质检评价:" prop="content" :rules="[{ required: false, message: '请填写质检评价', trigger: 'blur' }]">
+								<el-input
+									v-model="state.ruleForm.content"
+									placeholder="请填写质检评价"
+									clearable
+									type="textarea"
+									:autosize="{ minRows: 4, maxRows: 8 }"
+									:disabled="viewMode"
+								></el-input>
+							</el-form-item>
+						</el-col>
+					</el-row>
+				</el-form>
+				<div class="flex-end">
+					<el-button @click="closeDialog" class="default-button">取 消</el-button>
+					<el-button type="primary" @click="onSubmit(ruleFormRef)" v-waves="'light'" :loading="state.loading">确 定 </el-button>
+				</div>
+			</pane>
+		</splitpanes>
+	</el-dialog>
+	<el-dialog
+		v-model="state.dialogVisibleItem"
+		:title="dialogTitleItem"
+		draggable
+		ref="dialogItemRef"
+		width="600px"
+		append-to-body
+		destroy-on-close
+		@close="closeItem"
+	>
+		<el-form :model="state.ruleItemForm" ref="ruleItemFormRef" label-width="100px">
+			<el-form-item label="质检项" prop="name" :rules="[{ required: true, message: '请选择质检项', trigger: 'change' }]">
+				<el-select v-model="state.ruleItemForm.name" placeholder="请选择质检项" class="w100" @change="changeProject">
+					<el-option v-for="item in projectArray" :key="item.id" :label="item.name" :value="item.name" :disabled="item.disabled" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="分值" prop="grade" :rules="[{ required: true, message: '请填写分值', trigger: 'blur' }]">
+				<el-input-number v-model="state.ruleItemForm.grade" :precision="0" :min="1" :max="100" placeholder="请填写分值" />
+				<span class="ml10">分</span>
+			</el-form-item>
+			<el-form-item label="质检内容" prop="content" :rules="[{ required: false, message: '请填写质检内容', trigger: 'blur' }]">
+				<el-input v-model="state.ruleItemForm.content" placeholder="请填写质检内容" type="textarea" :autosize="{ minRows: 4, maxRows: 8 }"></el-input>
+			</el-form-item>
+		</el-form>
+		<template #footer>
+			<span class="dialog-footer">
+				<el-button @click="state.dialogVisibleItem = false" class="default-button">取 消</el-button>
+				<el-button type="primary" @click="onSubmitItem(ruleItemFormRef)" v-waves="'light'" :loading="state.loading">确 定 </el-button>
+			</span>
+		</template>
+	</el-dialog>
+	<!-- 播放录音 -->
+	<play-record ref="playRecordRef" />
+</template>
+<script setup lang="ts" name="qualityInspection">
+import { reactive, ref, watch, defineAsyncComponent } from 'vue';
+import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
+import { formatDate, formatDuration } from '@/utils/formatTime';
+import Other from '@/utils/other';
+import { qualityUpdate, qualityDetail, qualityRecordTransfer, qualityContentUpdate } from '@/api/quality';
+import { templateList } from '@/api/quality/template';
+import { useThemeConfig } from '@/stores/themeConfig';
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '@/stores/userInfo';
+import { Splitpanes, Pane } from 'splitpanes';
+import 'splitpanes/dist/splitpanes.css';
+
+// 引入组件
+const PlayRecord = defineAsyncComponent(() => import('@/components/PlayRecord/index.vue')); // 播放录音
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['updateList']);
+const state = reactive<any>({
+	dialogVisible: false, // 弹窗显示隐藏
+	ruleForm: {
+		name: '', // 违禁词
+		classify: '', // 违禁词分类
+		type: '', // 违禁词属性
+	},
+	loading: false, // 确定按钮loading
+	orderDetail: {},
+	dialogVisibleItem: false,
+	ruleItemForm: {
+		second: 0,
+		name: '', // 质检时间点
+		grade: 1, // 分值
+		content: '', // 质检内容
+	},
+	source: 1, // 来源
+	qualityDetail: {},
+	visit: {},
+});
+const dialogTitle = ref<string>('查看受理质检');
+const dialogTitleItem = ref<string>('新增质检项');
+const projectArray = ref<EmptyArrayType>([]); // 质检项数组
+const tableData = ref<EmptyArrayType>([]); // 表格数据
+const qualityId = ref<string>(''); // 质检ID
+const viewMode = ref<boolean>(false); // 是否查看
+// 打开弹窗
+const openDialog = (row: any, source: string | number | undefined, isView?: boolean) => {
+	score.value = 100;
+	state.loading = true;
+	qualityId.value = row.id;
+	state.dialogVisible = true;
+	getQualityDetail(row.id, source, isView);
+};
+// 转写内容
+const transformContent = ref<EmptyArrayType>([]);
+// 编辑模式
+const editMode = ref<boolean>(false);
+// 获取质检详情
+const getQualityDetail = async (id: string, source?: string | number | undefined, isView?: boolean) => {
+	try {
+		if (source) {
+			state.source = source;
+			const [temRes, qualityRes] = await Promise.all([
+				templateList({ IsEnable: 1, Grouping: source, PageIndex: 1, PageSize: 999 }),
+				qualityDetail(id),
+			]);
+			projectArray.value = temRes.result?.items[0]?.templateDetails ?? [];
+			state.qualityDetail = qualityRes.result;
+			tableData.value = qualityRes.result?.qualityDetails ?? [];
+			multipleSelection.value = tableData.value.filter((v: any) => v.check === true);
+			state.orderDetail = qualityRes.result?.order ?? {};
+			state.visit = qualityRes.result?.visit ?? {};
+			transformContent.value = Other.deepClone(qualityRes.result.transfer);
+			viewMode.value = isView ?? false;
+			switch (source) {
+				case 1:
+					dialogTitle.value = viewMode.value ? '查看受理质检' : '编辑受理质检';
+					break;
+				case 2:
+					dialogTitle.value = viewMode.value ? '查看派单质检' : '编辑派单质检';
+					break;
+				case 3:
+					dialogTitle.value = viewMode.value ? '查看回访质检' : '编辑回访质检';
+					break;
+			}
+			state.loading = false;
+		}
+	} catch (e) {
+		console.log(e);
+	}
+};
+const opened = () => {
+	// 回显选中表格
+	if (state.dialogVisible) {
+		multipleSelection.value.forEach((v: any) => {
+			multipleTableRef.value?.toggleRowSelection(v, true);
+		});
+	}
+};
+// 表格多选
+const multipleTableRef = ref<RefType>();
+const multipleSelection = ref<EmptyArrayType>([]);
+const handleSelectionChange = (val: any[]) => {
+	multipleSelection.value = val;
+	multipleSelection.value.forEach((v: any) => {
+		v.check = true;
+	});
+};
+// 是否可选
+const selectable = () => {
+	return !viewMode.value;
+};
+// 计算当前得分
+const score = ref(100);
+watch(
+	() => multipleSelection.value,
+	(newVal) => {
+		let total = 100;
+		for (let i of newVal) {
+			total -= i.grade;
+		}
+		score.value = total;
+	}
+);
+watch(
+	() => score.value,
+	(newVal) => {
+		if (newVal <= 0) {
+			ElMessage.warning('质检得分不能小于0分');
+		}
+	}
+);
+// 关闭弹窗
+const closeDialog = () => {
+	state.dialogVisible = false;
+};
+const ruleFormRef = ref<FormInstance>();
+const close = () => {
+	ruleFormRef.value?.clearValidate();
+	ruleFormRef.value?.resetFields();
+};
+const closeItem = () => {
+	ruleItemFormRef.value?.clearValidate();
+	ruleItemFormRef.value?.resetFields();
+};
+// 查看录音文件
+const playRecordRef = ref<RefType>();
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+const storesUserInfo = useUserInfo();
+const { userInfos } = storeToRefs(storesUserInfo); // 用户信息
+const recordFile = (obj: any, type: string) => {
+	switch (type) {
+		case 'order':
+			playRecordRef.value.playRecord(obj.callId);
+			break;
+		case 'visit':
+			playRecordRef.value.playRecord(themeConfig.value.recordPrefix + obj.recordingAbsolutePath);
+			break;
+	}
+};
+// 新增质检项内容
+const onAddItem = () => {
+	dialogTitleItem.value = '新增质检项';
+	state.ruleItemForm = {
+		second: 0,
+		name: '', // 质检时间点
+		grade: 1, // 分值
+		content: '', // 质检内容
+	};
+	state.dialogVisibleItem = true;
+	getEnableAcceptType();
+};
+// 选择质检项
+const changeProject = (val: any) => {
+	const item = projectArray.value.filter((v: any) => v.name === val)[0];
+	state.ruleItemForm.grade = item.grade;
+	state.ruleItemForm.content = item.content;
+	getEnableAcceptType();
+};
+// 编辑质检内容
+const onEditItem = (row: any) => {
+	dialogTitleItem.value = '编辑质检项';
+	state.ruleItemForm = Other.deepClone(row);
+	state.ruleItemForm.value = formatDuration(state.ruleItemForm.second, false, true);
+	state.ruleItemForm.endValue = formatDuration(state.ruleItemForm.endSecond, false, true);
+	state.dialogVisibleItem = true;
+	getEnableAcceptType();
+};
+// 质检项保存
+const ruleItemFormRef = ref<RefType>();
+const onSubmitItem = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.validate((valid: boolean) => {
+		if (!valid) return;
+		state.loading = true;
+		if (dialogTitleItem.value === '新增质检项') {
+			const data = Other.deepClone(state.ruleItemForm);
+			tableData.value.push(data);
+			state.loading = false;
+			state.dialogVisibleItem = false;
+		} else {
+			const newArray = <EmptyArrayType>[];
+			for (let i of tableData.value) {
+				if (i.name === state.ruleItemForm.name) {
+					newArray.push({
+						...i,
+						...state.ruleItemForm,
+					});
+				} else {
+					newArray.push(i);
+				}
+			}
+			tableData.value = newArray;
+			state.loading = false;
+			state.dialogVisibleItem = false;
+		}
+	});
+};
+// 删除质检项内容
+const onDeleteItem = (row: any) => {
+	ElMessageBox.confirm(`您确定要删除:【${row.name}】质检项,是否继续?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+		draggable: true,
+		cancelButtonClass: 'default-button',
+		autofocus: false,
+	})
+		.then(() => {
+			tableData.value = tableData.value.filter((v: any) => v.name !== row.name);
+			getEnableAcceptType();
+		})
+		.catch(() => {});
+};
+// 获取可用的数组
+const getEnableAcceptType = () => {
+	if (tableData.value.length) {
+		const array = tableData.value.map((v: any) => v.name);
+		projectArray.value.forEach((v: any) => {
+			v.disabled = array.includes(v.name);
+		});
+	} else {
+		projectArray.value.forEach((v: any) => {
+			v.disabled = false;
+		});
+	}
+};
+// 高亮关键词
+const showKeyWord = (val: string[], keywordString: string) => {
+	if (!keywordString) return val;
+	const keywordArr = keywordString.split(',');
+	let str: any = val;
+	if (keywordArr && keywordArr.length) {
+		// 2.定制关键词对应正则
+		keywordArr.forEach((e) => {
+			let regStr = '' + `(${e})`;
+			let reg = new RegExp(regStr, 'g');
+			// 3.正则替换,关键词飘红
+			str = str.replace(reg, '<span class="color-danger font-bold"">' + e + '</span>');
+		});
+	}
+	return str;
+};
+// 保存
+const onSubmit = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.validate((valid: boolean) => {
+		if (!valid) return;
+		if (score.value <= 0) {
+			ElMessage.warning('质检得分不能小于0分');
+			return;
+		}
+		const qualityDetails = tableData.value.map((v: any) => {
+			return {
+				...v,
+				check: multipleSelection.value.includes(v),
+			};
+		});
+		state.loading = true;
+		const request = {
+			orderId: state.orderDetail.id, // 工单id
+			mode: '人工质检', // 质检模式
+			grade: score.value, // 质检得分
+			content: state.ruleForm.content, // 质检评价
+			source: state.source, // 来源
+			qualityDetails: qualityDetails, // 质检详情
+			id: qualityId.value, // 质检id
+		};
+		qualityUpdate(request)
+			.then(() => {
+				ElMessage.success('质检成功');
+				emit('updateList');
+				state.loading = false;
+				state.dialogVisible = false;
+			})
+			.catch(() => {
+				state.loading = false;
+				state.dialogVisible = false;
+				emit('updateList');
+			});
+	});
+};
+defineExpose({
+	openDialog,
+	closeDialog,
+});
+</script>

+ 5 - 5
src/views/quality/index/ZGQuality.vue

@@ -81,12 +81,12 @@
 							<order-detail :order="row.order" @updateList="queryList">{{ row.order.title }}</order-detail>
 						</template>
 					</vxe-column>
-					<vxe-column field="transferStateText" title="转写状态" width="100">
+					<vxe-column field="transferStateText" title="转写状态" width="100" v-if="state.queryParams.Source === 1">
 						<template #default="{ row }">
-              <el-text v-if="row.transferState === 0" type="info">{{ row.transferStateText }}</el-text>
-              <el-text v-if="row.transferState === 1" type="primary">{{ row.transferStateText }}</el-text>
-              <el-text v-if="row.transferState === 2" type="success">{{ row.transferStateText }}</el-text>
-              <el-text v-if="row.transferState === 3" type="danger">{{ row.transferStateText }}</el-text>
+							<el-text v-if="row.transferState === 0" type="info">{{ row.transferStateText }}</el-text>
+							<el-text v-if="row.transferState === 1" type="primary">{{ row.transferStateText }}</el-text>
+							<el-text v-if="row.transferState === 2" type="success">{{ row.transferStateText }}</el-text>
+							<el-text v-if="row.transferState === 3" type="danger">{{ row.transferStateText }}</el-text>
 						</template>
 					</vxe-column>
 					<vxe-column field="aiQuality" title="质检方式" width="100">

+ 1 - 1
src/views/quality/index/components/Quality-inspection.vue

@@ -450,7 +450,7 @@ const onSubmit = (formEl: FormInstance | undefined) => {
 		};
 		qualityUpdate(request)
 			.then(() => {
-				ElMessage.success('操作成功');
+				ElMessage.success('质检成功');
 				emit('updateList');
 				state.loading = false;
 				state.dialogVisible = false;

+ 27 - 27
src/views/quality/index/components/ZG-quality.vue

@@ -149,7 +149,7 @@
 					</template>
 				</template>
 			</pane>
-			<pane style="max-height: calc(80vh - 111px); overflow-y: auto">
+			<pane style="max-height: calc(84vh - 111px); overflow-y: auto">
 				<p class="border-title">质检</p>
 				<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="110px" v-loading="state.loading">
 					<div class="mt20 mb20">
@@ -286,11 +286,11 @@ const openDialog = (row: any, source?: string | number | undefined) => {
 	state.dialogVisible = true;
 	getQualityDetail(row.id, source);
 };
-// 获取质检详情
 // 转写内容
 const transformContent = ref<EmptyArrayType>([]);
 // 编辑模式
 const editMode = ref<boolean>(false);
+// 获取质检详情
 const getQualityDetail = async (id: string, source?: string | number | undefined) => {
 	try {
 		if (source) {
@@ -417,30 +417,6 @@ const onEditItem = (row: any) => {
 	state.dialogVisibleItem = true;
 	getEnableAcceptType();
 };
-// 录音转写
-const onTransfer = () => {
-	ElMessageBox.confirm(`您确定要创建录音转写任务,是否继续?`, '提示', {
-		confirmButtonText: '确认',
-		cancelButtonText: '取消',
-		type: 'warning',
-		draggable: true,
-		cancelButtonClass: 'default-button',
-		autofocus: false,
-	})
-		.then(() => {
-			state.loading = true;
-			qualityRecordTransfer([{ id: qualityId.value }])
-				.then(() => {
-					ElMessage.success('转写任务创建成功');
-					getQualityDetail(qualityId.value, state.source);
-					emit('updateList');
-				})
-				.catch((err) => {
-					console.log(err);
-				});
-		})
-		.catch(() => {});
-};
 // 质检项保存
 const ruleItemFormRef = ref<RefType>();
 const onSubmitItem = (formEl: FormInstance | undefined) => {
@@ -502,6 +478,30 @@ const getEnableAcceptType = () => {
 		});
 	}
 };
+// 录音转写
+const onTransfer = () => {
+  ElMessageBox.confirm(`您确定要创建录音转写任务,是否继续?`, '提示', {
+    confirmButtonText: '确认',
+    cancelButtonText: '取消',
+    type: 'warning',
+    draggable: true,
+    cancelButtonClass: 'default-button',
+    autofocus: false,
+  })
+      .then(() => {
+        state.loading = true;
+        qualityRecordTransfer([{ id: qualityId.value }])
+            .then(() => {
+              ElMessage.success('转写任务创建成功');
+              getQualityDetail(qualityId.value, state.source);
+              emit('updateList');
+            })
+            .catch((err) => {
+              console.log(err);
+            });
+      })
+      .catch(() => {});
+};
 // 高亮关键词
 const showKeyWord = (val: string[], keywordString: string) => {
 	if (!keywordString) return val;
@@ -568,7 +568,7 @@ const onSubmit = (formEl: FormInstance | undefined) => {
 		};
 		qualityUpdate(request)
 			.then(() => {
-				ElMessage.success('操作成功');
+				ElMessage.success('质检成功');
 				emit('updateList');
 				state.loading = false;
 				state.dialogVisible = false;

+ 115 - 106
src/views/quality/seats.vue

@@ -1,135 +1,144 @@
 <template>
-  <div class="quality-seats-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>
-            <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>
+	<div class="quality-seats-container layout-padding">
+		<div class="layout-padding-auto layout-padding-view pd20">
+			<vxe-grid v-bind="gridOptions" v-on="gridEvents" ref="gridRef">
+				<template #form>
+					<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="gridOptions.loading" />
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleQuery" :loading="gridOptions.loading">
+								<SvgIcon name="ele-Search" class="mr5" />查询
+							</el-button>
+							<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="gridOptions.loading">
+								<SvgIcon name="ele-Refresh" class="mr5" />重置
+							</el-button>
+						</el-form-item>
+					</el-form>
+				</template>
+				<template #no="{ row }">
+					<order-detail :order="row.order" @updateList="handleQuery">{{ row.no }}</order-detail>
+				</template>
+				<template #pager>
+					<pagination
+						@pagination="queryList"
+						:total="state.total"
+						v-model:current-page="state.queryParams.PageIndex"
+						v-model:page-size="state.queryParams.PageSize"
+						:disabled="state.loading"
+					/>
+				</template>
+			</vxe-grid>
+		</div>
+	</div>
 </template>
 <script setup lang="tsx" name="qualitySeats">
 import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
 import { FormInstance } from 'element-plus';
 import { defaultDate } from '@/utils/constants';
 import Other from '@/utils/other';
-import XEUtils from 'xe-utils';
-import {qualityAnalysis, qualityAnalysisExport} from "@/api/quality";
+import { qualityAnalysis, qualityAnalysisExport } from '@/api/quality';
 
 const StatisticalTime = defineAsyncComponent(() => import('@/components/StatisticalTime/index.vue')); // 日期类型选择组件
+const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
+const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
 // 定义变量内容
 const ruleFormRef = ref<RefType>(); // 表单ref
 const state = reactive<any>({
-  queryParams: {
-    // 查询条件
-    EmpId: null, // 坐席ID
-    crTime: defaultDate,
-    SortField: null,
-    SortRule: null,
-  },
-  tableData: [], //表单
-  loading: false, // 加载
-  total: 0, // 总数
-  roleOptions: [],
-  totalCount: {},
+	queryParams: {
+		PageIndex: 1, // 当前页
+		PageSize: 20, // 每页条数
+		// 查询条件
+		crTime: defaultDate,
+		SortField: null,
+		SortRule: null,
+	},
+	loading: false, // 加载
+	total: 0, // 总数
 });
 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: 'exportCurrent' } }, { toolRender: { name: 'exportAll' } }],
-  },
-  customConfig: {
-    storage: true,
-  },
-  id: 'qualitySeats',
-  rowConfig: { isHover: true, height: 30, isCurrent: true, useKey: true },
-  height: 'auto',
-  columns: [
-    { field: 'no', title: '受理人' },
-    { field: 'no', title: '工单编码' },
-    { field: 'no', title: '质检项' },
-    { field: 'no', title: '质检得分' },
-  ],
-  data: [],
-  params: {
-    exportMethod: qualityAnalysisExport,
-    exportParams: requestParams,
-  },
-  sortConfig: {
-    remote: true,
-  },
+	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: 'exportCurrent' } }, { toolRender: { name: 'exportAll' } }],
+	},
+	customConfig: {
+		storage: true,
+	},
+	id: 'qualitySeats',
+	rowConfig: { isHover: true, height: 30, isCurrent: true, useKey: true },
+	height: 'auto',
+	columns: [
+		{ field: 'acceptorName', title: '受理人' },
+		{ field: 'no', title: '工单编码', slots: { default: 'no' } },
+		{ field: 'qualityDetail', title: '质检项' },
+		{ field: 'grade', title: '质检得分', sortable: true },
+	],
+	data: [],
+	params: {
+		exportMethod: qualityAnalysisExport,
+		exportParams: requestParams,
+	},
+	sortConfig: {
+		remote: true,
+	},
 });
 /** 搜索按钮操作 */
 const handleQuery = () => {
-  // state.queryParams.PageIndex = 1;
-  queryList();
+	state.queryParams.PageIndex = 1;
+	queryList();
+};
+const gridEvents = {
+	sortChange(val: any) {
+		state.queryParams.SortField = val.order ? val.field : null;
+		// 0 升序 1 降序
+		state.queryParams.SortRule = val.order ? (val.order == 'desc' ? 1 : 0) : null;
+		handleQuery();
+	},
 };
 /** 获取列表 */
 const queryList = () => {
-  state.loading = true;
-  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');
-  qualityAnalysis(requestParams.value)
-      .then((res: any) => {
-        state.tableData = res.result.list;
-        state.totalCount = res.result.total;
-        state.loading = false;
-      })
-      .catch(() => {
-        state.loading = false;
-      });
+	gridOptions.loading = true;
+	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');
+	qualityAnalysis(requestParams.value)
+		.then((res: any) => {
+			gridOptions.data = res.result.items ?? [];
+			state.total = res.result.total;
+			gridOptions.loading = false;
+		})
+		.catch(() => {
+			gridOptions.loading = false;
+		});
 };
 /** 重置按钮操作 */
 const statisticalTimeRef = ref<RefType>();
 const resetQuery = (formEl: FormInstance | undefined) => {
-  if (!formEl) return;
-  formEl.resetFields();
-  statisticalTimeRef.value.reset();
-  queryList();
-};
-// 计算合计
-const footerMethod = ({ columns, data }) => {
-  return [
-    columns.map((column: any, columnIndex: number) => {
-      if (columnIndex === 0) {
-        return '合计';
-      }
-      // 后端返回了数据集合 state.totalCount 所以不需要计算 直接进行赋值
-      return XEUtils.get(state.totalCount, column.property);
-    }),
-  ];
+	if (!formEl) return;
+	formEl.resetFields();
+	statisticalTimeRef.value.reset();
+	queryList();
 };
 onMounted(() => {
-  queryList();
+	queryList();
 });
 </script>

+ 417 - 213
src/views/quality/work.vue

@@ -1,241 +1,445 @@
 <template>
-  <div class="quality-work-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 label="坐席" prop="EmpId">
-          <el-select-v2
-              v-model="state.queryParams.EmpId"
-              filterable
-              :options="seatUser"
-              :props="{
-							label: 'name',
-							value: 'id',
-						}"
-              placeholder="请选择坐席"
-              clearable
-              @change="handleQuery"
-          />
-        </el-form-item>
-        <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>
-      <vxe-toolbar
-          ref="toolbarRef"
-          :loading="state.loading"
-          custom
-          :refresh="{
-					queryMethod: handleQuery,
-				}"
-          :tools="[{ toolRender: { name: 'exportAll' } }]"
-      >
-        <template #buttons>
-          <el-button type="primary" @click="onLink">通话时段明细</el-button>
-          <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="呼入总量">呼入接通量+呼入未接通总量</el-descriptions-item>
-              <el-descriptions-item label="呼入接通总量">坐席已接听的电话量</el-descriptions-item>
-              <el-descriptions-item label="有效接通量">通话时长大于15秒的电话量</el-descriptions-item>
-              <el-descriptions-item label="接通秒挂量">通话时长小于15秒的电话量</el-descriptions-item>
-              <el-descriptions-item label="超时接通量">铃时长大于15秒且接通的电话量</el-descriptions-item>
-              <el-descriptions-item label="按时接通量">振铃时长小于15秒且接通的电话量</el-descriptions-item>
-              <el-descriptions-item label="呼入未接通总量">已分配给该坐席但该坐席未接的电话量(即振铃未接电话量)</el-descriptions-item>
-              <el-descriptions-item label="未接通秒挂量">振铃时长小于15秒且未接通的电话量</el-descriptions-item>
-              <el-descriptions-item label="超时未接通量">振铃时长大于15秒且未接通的电话量</el-descriptions-item>
-              <el-descriptions-item label="接通率">呼入接通总量/呼入总量</el-descriptions-item>
-            </el-descriptions>
-          </el-popover>
-        </template>
-      </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, useKey: true }"
-            ref="tableRef"
-            height="auto"
-            auto-resize
-            :scrollY="{ enabled: true, gt: 100 }"
-            show-overflow
-            id="statisticsCallSeatsMoth"
-            :custom-config="{ storage: true }"
-            show-footer
-            :footer-method="footerMethod"
-            :params="{ exportMethod: callAgentMonthExport, exportParams: requestParams }"
-            :sort-config="{ remote: true }"
-            @sort-change="sortChange"
-        >
-          <vxe-column field="name" title="坐席"></vxe-column>
-          <vxe-column title="呼入总量" field="inTotal" sortable> </vxe-column>
-          <vxe-column title="呼入接通总量" field="inAnswered" sortable>
-            <template #default="{ row }">
-              <el-button link type="primary" @click="linkDetail(row, '1')">{{ row.inAnswered }}</el-button>
-            </template>
-          </vxe-column>
-          <vxe-column title="有效接通量" field="inAvailableAnswer" sortable>
-            <template #default="{ row }">
-              <el-button link type="primary" @click="linkDetail(row, '2')">{{ row.inAvailableAnswer }}</el-button>
-            </template>
-          </vxe-column>
-          <vxe-column title="接通秒挂量" field="inHangupImmediateWhenAnswered" sortable>
-            <template #default="{ row }">
-              <el-button link type="primary" @click="linkDetail(row, '3')">{{ row.inHangupImmediateWhenAnswered }}</el-button>
-            </template>
-          </vxe-column>
-          <vxe-column title="超时接通量" field="overTimeImmediate" sortable>
-            <template #default="{ row }">
-              <el-button link type="primary" @click="linkDetail(row, '4')">{{ row.overTimeImmediate }}</el-button>
-            </template>
-          </vxe-column>
-          <vxe-column title="按时接通量" field="inTimeImmediate" sortable>
-            <template #default="{ row }">
-              <el-button link type="primary" @click="linkDetail(row, '5')">{{ row.inTimeImmediate }}</el-button>
-            </template>
-          </vxe-column>
-          <vxe-column title="呼入未接通总量" field="inHanguped" sortable>
-            <template #default="{ row }">
-              <el-button link type="primary" @click="linkDetail(row, '6')">{{ row.inHanguped }}</el-button>
-            </template>
-          </vxe-column>
-          <vxe-column title="未接通秒挂量" field="inHangupImmediate" sortable>
-            <template #default="{ row }">
-              <el-button link type="primary" @click="linkDetail(row, '7')">{{ row.inHangupImmediate }}</el-button>
-            </template>
-          </vxe-column>
-          <vxe-column title="超时未接通量" field="overTimeInHanguped" sortable>
-            <template #default="{ row }">
-              <el-button link type="primary" @click="linkDetail(row, '8')">{{ row.overTimeInHanguped }}</el-button>
-            </template>
-          </vxe-column>
-          <vxe-column title="接通率" field="inAnsweredRateString" sortable></vxe-column>
-        </vxe-table>
-      </div>
-    </div>
-  </div>
+	<div class="quality-seats-container layout-pd">
+		<el-row :gutter="10">
+			<el-col class="mb10">
+				<el-card shadow="never">
+					<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="gridOptions.loading" />
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleQuery" :loading="gridOptions.loading">
+								<SvgIcon name="ele-Search" class="mr5" />查询
+							</el-button>
+							<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="gridOptions.loading">
+								<SvgIcon name="ele-Refresh" class="mr5" />重置
+							</el-button>
+						</el-form-item>
+					</el-form>
+				</el-card>
+			</el-col>
+			<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" class="mb10">
+				<el-card shadow="never" style="height: 400px">
+					<vxe-grid v-bind="gridOptions" v-on="gridEvents" ref="gridRef"> </vxe-grid>
+				</el-card>
+			</el-col>
+			<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" class="mb10">
+				<el-card shadow="never" style="height: 400px">
+					<div class="flex-center-between">
+						<h3></h3>
+						<div>
+							<el-select-v2
+								v-model="AcceptorId"
+								filterable
+								:options="seatUser"
+								:props="{
+									label: 'value',
+									value: 'key',
+								}"
+								placeholder="请选择坐席"
+								clearable
+								@change="getSeatsData"
+								style="width: 240px"
+							/>
+						</div>
+					</div>
+					<v-chart :option="option" :loading="seatsLoading" autoresize style="height: 320px" />
+				</el-card>
+			</el-col>
+			<el-col>
+				<el-card shadow="never" style="height: 400px">
+					<div class="flex-center-between">
+						<h3></h3>
+						<div class="flex">
+							<input-number-range v-model="state.queryParams.value" @change="getYearData" />
+							<el-select-v2
+								v-model="AcceptorId1"
+								filterable
+								:options="seatUser"
+								:props="{
+									label: 'value',
+									value: 'key',
+								}"
+								placeholder="请选择坐席"
+								clearable
+								@change="getYearData"
+								style="width: 240px"
+							/>
+						</div>
+					</div>
+					<v-chart :option="option1" :loading="yearLoading" autoresize style="height: 320px" />
+				</el-card>
+			</el-col>
+		</el-row>
+	</div>
 </template>
 <script setup lang="tsx" name="qualityWork">
 import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
 import { FormInstance } from 'element-plus';
 import { defaultDate } from '@/utils/constants';
 import Other from '@/utils/other';
-import XEUtils from 'xe-utils';
-import { useRouter } from 'vue-router';
-import { callAgentMonth, callAgentMonthBase, callAgentMonthExport } from '@/api/statistics/call';
+import { qualityOverview, qualityOverviewExport, qualityScore, qualityScoreBase, qualityScoreGrade } from '@/api/quality';
 
 const StatisticalTime = defineAsyncComponent(() => import('@/components/StatisticalTime/index.vue')); // 日期类型选择组件
+const InputNumberRange = defineAsyncComponent(() => import('@/components/NumberRange/index.vue')); // 数字区间
+
 // 定义变量内容
-const ruleFormRef = ref<RefType>(); // 表单ref
 const state = reactive<any>({
-  queryParams: {
-    // 查询条件
-    EmpId: null, // 坐席ID
-    crTime: defaultDate,
-    SortField: null,
-    SortRule: null,
-  },
-  tableData: [], //表单
-  loading: false, // 加载
-  total: 0, // 总数
-  roleOptions: [],
-  totalCount: {},
+	queryParams: {
+		// 查询条件
+		crTime: defaultDate,
+		SortField: null,
+		SortRule: null,
+		value: [0, 100],
+		AcceptorId: null,
+	},
+	loading: false, // 加载
+	total: 0, // 总数
+});
+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: 'exportCurrent' } }, { toolRender: { name: 'exportAll' } }],
+	},
+	customConfig: {
+		storage: true,
+	},
+	height: 360,
+	id: 'qualityWork',
+	rowConfig: { isHover: true, height: 30, isCurrent: true, useKey: true },
+	columns: [
+		{ field: 'qualityItem', title: '质检项' },
+		{ field: 'orderNum', title: '质检工单数', sortable: true },
+		{ field: 'rate', title: '占比' },
+	],
+	data: [],
+	params: {
+		exportMethod: qualityOverviewExport,
+		exportParams: requestParams,
+	},
+	sortConfig: {
+		remote: true,
+	},
 });
 /** 搜索按钮操作 */
 const handleQuery = () => {
-  // state.queryParams.PageIndex = 1;
-  queryList();
+	queryList();
 };
 /** 获取列表 */
-const requestParams = ref<EmptyObjectType>({});
-const queryList = () => {
-  state.loading = true;
-  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');
-  callAgentMonth(requestParams.value)
-      .then((res: any) => {
-        state.tableData = res.result.list;
-        state.totalCount = res.result.total;
-        state.loading = false;
-      })
-      .catch(() => {
-        state.loading = false;
-      });
+const queryList = async () => {
+	gridOptions.loading = true;
+	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');
+	requestParams.value.MinGrade = state.queryParams.value[0];
+	requestParams.value.MaxGrade = state.queryParams.value[1];
+	Reflect.deleteProperty(requestParams.value, 'value');
+	try {
+		const { result } = await qualityOverview(requestParams.value);
+		gridOptions.data = result ?? [];
+		gridOptions.loading = false;
+	} catch (e) {
+		gridOptions.loading = false;
+		console.log(e);
+	}
 };
-// 排序
-const sortChange = (val: any) => {
-  state.queryParams.SortField = val.order ? val.field : null;
-  // 0 升序 1 降序
-  state.queryParams.SortRule = val.order ? (val.order == 'desc' ? 1 : 0) : null;
-  queryList();
+const gridEvents = {
+	sortChange(val: any) {
+		state.queryParams.SortField = val.order ? val.field : null;
+		// 0 升序 1 降序
+		state.queryParams.SortRule = val.order ? (val.order == 'desc' ? 1 : 0) : null;
+		handleQuery();
+	},
 };
-/** 重置按钮操作 */
-const statisticalTimeRef = ref<RefType>();
-const resetQuery = (formEl: FormInstance | undefined) => {
-  if (!formEl) return;
-  formEl.resetFields();
-  statisticalTimeRef.value.reset();
-  queryList();
+// 获取坐席质检分值分析
+const seatsLoading = ref(false);
+const AcceptorId = ref<string | number | null>(null);
+const getSeatsData = async () => {
+	seatsLoading.value = true;
+	try {
+		const { result } = await qualityScore({ ...requestParams.value, AcceptorId: AcceptorId.value });
+		const legendData = ['90-100分', '80-90分', '70-80分', '60-70分', '60分以下'];
+		const data = [
+			{
+				value: result.ninetyGrade,
+				name: '90-100分',
+			},
+			{
+				value: result.eightyGrade,
+				name: '80-90分',
+			},
+			{
+				value: result.seventyGrade,
+				name: '70-80分',
+			},
+			{
+				value: result.sixtyGrade,
+				name: '60-70分',
+			},
+			{
+				value: result.fiftyGrade,
+				name: '60分以下',
+			},
+		];
+		setOption(legendData, data);
+		seatsLoading.value = false;
+	} catch (e) {
+		console.log(e);
+		seatsLoading.value = false;
+	}
+};
+const option = ref<any>({});
+// 坐席质检分值分析
+const setOption = (legendData: string[], data: any) => {
+	option.value = {
+		title: {
+			text: '坐席质检分值分析',
+			left: '0',
+		},
+		tooltip: {
+			formatter: '{b0}: {c0} ({d}%)',
+		},
+		toolbox: {
+			show: true,
+			orient: 'horizontal',
+			showTitle: true,
+			feature: {
+				saveAsImage: {
+					type: 'png',
+					show: true,
+					title: '保存为图片',
+				},
+			},
+		},
+		legend: [
+			{
+				top: '20%',
+				right: '20',
+				orient: 'vertical',
+				data: legendData,
+			},
+		],
+		series: [
+			{
+				type: 'pie',
+				radius: ['0%', '80%'],
+				roseType: 'area',
+				label: {
+					show: true,
+					overflow: 'none',
+					formatter: function (params: any) {
+						if (params.name !== '') {
+							return `${params.name}:${params.data.value}(${params.percent}%)`;
+						}
+					},
+				},
+				data: data,
+			},
+		],
+	};
 };
-// 计算合计
-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 AcceptorId1 = ref<string | number | null>(null);
+const yearLoading = ref(false);
+// 获取近一年质检分值分析
+const getYearData = async () => {
+	yearLoading.value = true;
+	try {
+		const { result } = await qualityScoreGrade({ ...requestParams.value, AcceptorId: AcceptorId1.value });
+		console.log(result, 'result');
+		const xData = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
+		const data = result.map((item: any) => item.num);
+		// const data = [1,2,45,213,45,65,7,8,1,23]
+		setOption1(data, xData);
+		yearLoading.value = false;
+	} catch (e) {
+		console.log(e);
+		yearLoading.value = false;
+	}
 };
-const router = useRouter();
-// 跳转明细
-const onLink = () => {
-  router.push({
-    path: '/statistics/call/detailSeatsMoth',
-  });
+const option1 = ref<any>({});
+// 近一年质检分值分析
+const setOption1 = (data: any, xData: any) => {
+	option1.value = {
+		title: {
+			text: '近一年质检分值分析',
+			left: '0',
+		},
+		legend: {
+			show: false,
+		},
+		tooltip: {
+			show: true,
+			trigger: 'item',
+		},
+		grid: {
+			top: '20%',
+			left: '5%',
+			right: '1%',
+			bottom: '12%',
+		},
+		toolbox: {
+			show: true,
+			orient: 'horizontal',
+			showTitle: true,
+			feature: {
+				saveAsImage: {
+					type: 'png',
+					show: true,
+					title: '保存为图片',
+				},
+			},
+		},
+		xAxis: [
+			{
+				type: 'category',
+				data: xData,
+				axisTick: {
+					show: false, // 是否显示坐标轴轴线
+				},
+				axisLabel: {
+					color: '#000',
+					fontSize: 14,
+				},
+				splitLine: {
+					show: false,
+				},
+				boundaryGap: true,
+				axisLine: {
+					//坐标轴轴线相关设置。
+					show: true,
+					inside: false,
+				},
+			},
+		],
+		yAxis: [
+			{
+				type: 'value',
+				name: '单位:件',
+				nameGap: 15,
+				nameTextStyle: {
+					fontSize: 14,
+					color: '#aaa',
+					align: 'center',
+					padding: [10, 0, 0, -5],
+				},
+				axisLabel: {
+					//坐标轴刻度标签的相关设置。
+					show: true,
+				},
+				axisLine: {
+					show: true,
+				},
+				axisTick: {
+					show: false,
+				},
+				splitLine: {
+					show: true,
+				},
+				show: true,
+			},
+		],
+		series: [
+			{
+				name: '',
+				type: 'bar',
+				barMaxWidth: 30,
+				data: data,
+				itemStyle: {
+					color: {
+						type: 'linear',
+						x: 0,
+						y: 0,
+						x2: 0,
+						y2: 1,
+						colorStops: [
+							{
+								offset: 0,
+								color: 'rgba(31, 129, 255, 1)',
+							},
+							{
+								offset: 1,
+								color: 'rgba(31, 134, 255, .5)',
+							},
+						],
+					},
+				},
+				label: {
+          show: true,
+          position: 'top',
+          fontSize: 14,
+          color: '#333',
+				},
+			},
+			{
+				name: '',
+				type: 'line',
+				barMaxWidth: 30,
+				data: data,
+				itemStyle: {
+					color: {
+						type: 'linear',
+						x: 0,
+						y: 0,
+						x2: 0,
+						y2: 1,
+						colorStops: [
+							{
+								offset: 0,
+								color: 'rgba(31, 129, 255, 1)',
+							},
+							{
+								offset: 1,
+								color: 'rgba(31, 134, 255, .5)',
+							},
+						],
+					},
+				},
+			},
+		],
+	};
 };
-// 点击数字查询详情
-const linkDetail = (row: any, type: string) => {
-  router.push({
-    path: '/statistics/call/detailSeatsMoth',
-    query: {
-      EmpId: row.userId,
-      StartTime: state.queryParams.crTime[0],
-      EndTime: state.queryParams.crTime[1],
-      QueryType: type,
-    },
-  });
+const ruleFormRef = ref<RefType>();
+/** 重置按钮操作 */
+const statisticalTimeRef = ref<RefType>();
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	statisticalTimeRef.value.reset();
+	queryList();
 };
-// 基础数据
+// 获取基础信息
 const seatUser = ref<EmptyArrayType>([]);
 const getBaseData = async () => {
-  try {
-    const { result } = await callAgentMonthBase();
-    seatUser.value = result.seatUser ?? [];
-  } catch (e) {
-    console.log(e);
-  }
+	try {
+		const { result } = await qualityScoreBase();
+		seatUser.value = result.seats ?? [];
+	} catch (e) {
+		console.log(e);
+	}
 };
-const toolbarRef = ref<RefType>();
-const tableRef = ref<RefType>();
 onMounted(() => {
-  queryList();
-  if (tableRef.value && toolbarRef.value) {
-    tableRef.value.connect(toolbarRef.value);
-  }
-  getBaseData();
+	queryList();
+	getSeatsData();
+	getYearData();
+	getBaseData();
 });
 </script>

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

@@ -129,7 +129,7 @@
 	</div>
 </template>
 <script setup lang="tsx" name="todoSeats">
-import { computed, defineAsyncComponent, onActivated, onBeforeUnmount, onDeactivated, onMounted, reactive, ref } from 'vue';
+import { computed, defineAsyncComponent, onActivated, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
 import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
 import { useRouter } from 'vue-router';
 import { seatsListTodo, orderSign, orderListTodoCount } from '@/api/todo/order';