Переглянути джерело

reactor:对接自贡智能质检;

zhangchong 3 місяців тому
батько
коміт
21501a8b76

+ 1 - 1
.env.development

@@ -5,7 +5,7 @@ VITE_STORAGE_NAME=dev
 # 业务系统基础请求地址
 VITE_API_URL=http://110.188.24.28:50300
 # 业务系统socket请求地址
-VITE_API_SOCKET_URL=http://110.188.24.28:50100/hubs/hotline
+VITE_API_SOCKET_URL=http://110.188.24.28:50300/hubs/hotline
 # 业务系统文件上传上传请求地址
 VITE_API_UPLOAD_URL=http://110.188.24.28:50120
 # 数据共享平台请求地址

+ 1 - 1
.eslintignore

@@ -7,4 +7,4 @@ node_modules
 *.ttf
 .vscode
 .idea
-dist
+dist

+ 22 - 0
src/api/quality/index.ts

@@ -54,4 +54,26 @@ export const qualityDetail = (id: string) => {
         url: `/api/v1/Quality/${id}`,
         method: 'get'
     });
+};
+/**
+ * @description 通话录音转写 兴唐
+ * @param data
+ */
+export const qualityRecordTransfer = (data: object) => {
+    return request({
+        url: `/api/v1/Quality/aitransfer_xt`,
+        method: 'post',
+        data
+    });
+};
+/**
+ * @description 导出质检单
+ * @param data
+ */
+export const qualityExport = (data: object) => {
+    return request({
+        url: `/api/v1/ExportWord/quality_certificate`,
+        method: 'post',
+        data
+    });
 };

+ 45 - 0
src/utils/tools.ts

@@ -5,6 +5,7 @@ import { formatDate } from '@/utils/formatTime';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { VxeUI } from 'vxe-pc-ui';
 import { exportJbOrder } from '@/api/business/order';
+import { qualityExport } from '@/api/quality';
 /**
  * @description 防抖
  * @param func    功能函数(即要防抖的函数)
@@ -532,3 +533,47 @@ export function exportAssignment(ids: string[]) {
 			.catch(() => {});
 	});
 }
+/**
+ * @description  导出质检单 --- 公共方法
+ * @returns {*}
+ * @param ids
+ * */
+export function exportQuality(ids: string[]) {
+	return new Promise((resolve, reject) => {
+		ElMessageBox.confirm(`您确定导出选中的工单交办单,是否继续?`, '提示', {
+			confirmButtonText: '确认',
+			cancelButtonText: '取消',
+			type: 'warning',
+			draggable: true,
+			cancelButtonClass: 'default-button',
+			autofocus: false,
+		})
+			.then(() => {
+				VxeUI.modal.message({
+					content: `导出中,请稍等`,
+					status: 'loading',
+					id: 'exportJBD',
+					duration: -1,
+				});
+				qualityExport(ids)
+					.then((res: any) => {
+						downloadZip(res);
+						VxeUI.modal.close('exportJBD');
+						VxeUI.modal.message({
+							content: `导出成功`,
+							status: 'success',
+						});
+						resolve(true);
+					})
+					.catch((err: any) => {
+						VxeUI.modal.close('exportJBD');
+						VxeUI.modal.message({
+							content: `导出失败`,
+							status: 'error',
+						});
+						reject(err);
+					});
+			})
+			.catch(() => {});
+	});
+}

+ 246 - 0
src/views/quality/done/YBQualityDone.vue

@@ -0,0 +1,246 @@
+<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="{
+					queryMethod: handleQuery,
+				}"
+      >
+      </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 }"
+        >
+          <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>
+
+<script lang="tsx" setup name="qualityDone">
+import { 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';
+
+// 引入组件
+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 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: [], // 表格数据
+});
+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);
+  }
+};
+// 手动查询,将页码设置为1
+const handleQuery = () => {
+  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;
+      });
+};
+// 重置表单
+const resetQuery = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  formEl.resetFields();
+  state.queryParams.CreationTimeStart = null;
+  state.queryParams.CreationTimeEnd = null;
+  queryList();
+};
+// 质检查看
+const qualityInspectionRef = ref<RefType>();
+const onQualityView = (row: any) => {
+  qualityInspectionRef.value.openDialog(row, state.queryParams.Source, true);
+};
+// 质检编辑
+const onQualityEdit = (row: any) => {
+  qualityInspectionRef.value.openDialog(row, state.queryParams.Source);
+};
+// 页面加载时
+const toolbarRef = ref<RefType>();
+const tableRef = ref<RefType>();
+onMounted(() => {
+  queryList();
+  if (tableRef.value && toolbarRef.value) {
+    tableRef.value.connect(toolbarRef.value);
+  }
+  getBaseData();
+});
+</script>

+ 246 - 0
src/views/quality/done/ZGQualityDone.vue

@@ -0,0 +1,246 @@
+<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="{
+					queryMethod: handleQuery,
+				}"
+      >
+      </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 }"
+        >
+          <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>
+
+<script lang="tsx" setup name="qualityDone">
+import { 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';
+
+// 引入组件
+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 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: [], // 表格数据
+});
+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);
+  }
+};
+// 手动查询,将页码设置为1
+const handleQuery = () => {
+  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;
+      });
+};
+// 重置表单
+const resetQuery = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  formEl.resetFields();
+  state.queryParams.CreationTimeStart = null;
+  state.queryParams.CreationTimeEnd = null;
+  queryList();
+};
+// 质检查看
+const qualityInspectionRef = ref<RefType>();
+const onQualityView = (row: any) => {
+  qualityInspectionRef.value.openDialog(row, state.queryParams.Source, true);
+};
+// 质检编辑
+const onQualityEdit = (row: any) => {
+  qualityInspectionRef.value.openDialog(row, state.queryParams.Source);
+};
+// 页面加载时
+const toolbarRef = ref<RefType>();
+const tableRef = ref<RefType>();
+onMounted(() => {
+  queryList();
+  if (tableRef.value && toolbarRef.value) {
+    tableRef.value.connect(toolbarRef.value);
+  }
+  getBaseData();
+});
+</script>

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

@@ -10,7 +10,7 @@
 		@close="close"
 		@opened="opened"
 	>
-		<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="110px">
+		<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="110px" v-loading="state.loading">
 			<el-row :gutter="10">
 				<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
 					<el-form-item label="工单编码:"> {{ state.orderDetail.no }} </el-form-item>
@@ -226,6 +226,8 @@ const viewMode = ref<boolean>(false); // 是否查看
 // 打开弹窗
 const openDialog = async (row: any, source: string | number | undefined, isView?: boolean) => {
 	score.value = 100;
+  state.loading = true;
+  state.dialogVisible = true;
 	try {
 		const { result } = await qualityDetail(row.id);
 		state.ruleForm = result ?? {};
@@ -237,6 +239,7 @@ const openDialog = async (row: any, source: string | number | undefined, isView?
 		viewMode.value = isView ?? false;
 		const response = await templateList({ IsEnable: 1, Grouping: source, PageIndex: 1, PageSize: 1000 });
 		projectArray.value = response.result?.items[0]?.templateDetails ?? [];
+    state.loading = false;
 		switch (source) {
 			case 1:
 				dialogTitle.value = viewMode.value ? '查看受理质检' : '编辑受理质检';
@@ -248,7 +251,6 @@ const openDialog = async (row: any, source: string | number | undefined, isView?
 				dialogTitle.value = viewMode.value ? '查看回访质检' : '编辑回访质检';
 				break;
 		}
-		state.dialogVisible = true;
 	} catch (e) {
 		console.log(e);
 	}

+ 18 - 237
src/views/quality/done/index.vue

@@ -1,246 +1,27 @@
 <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="{
-					queryMethod: handleQuery,
-				}"
-			>
-			</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 }"
-				>
-					<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>
+	<component :is="currentCity" />
 </template>
 
 <script lang="tsx" setup name="qualityDone">
-import { 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 { defineAsyncComponent, computed } from 'vue';
+import { useThemeConfig } from '@/stores/themeConfig';
+import { storeToRefs } from 'pinia';
 
 // 引入组件
-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 YiBin = defineAsyncComponent(() => import('@/views/quality/done/YBQualityDone.vue')); // 宜宾工单质检
+const ZiGong = defineAsyncComponent(() => import('@/views/quality/done/ZGQualityDone.vue')); // 自贡工单质检
+const LuZhou = defineAsyncComponent(() => import('@/views/quality/done/YBQualityDone.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: [], // 表格数据
-});
-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);
-	}
-};
-// 手动查询,将页码设置为1
-const handleQuery = () => {
-	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;
-		});
-};
-// 重置表单
-const resetQuery = (formEl: FormInstance | undefined) => {
-	if (!formEl) return;
-	formEl.resetFields();
-	state.queryParams.CreationTimeStart = null;
-	state.queryParams.CreationTimeEnd = null;
-	queryList();
-};
-// 质检查看
-const qualityInspectionRef = ref<RefType>();
-const onQualityView = (row: any) => {
-	qualityInspectionRef.value.openDialog(row, state.queryParams.Source, true);
-};
-// 质检编辑
-const onQualityEdit = (row: any) => {
-	qualityInspectionRef.value.openDialog(row, state.queryParams.Source);
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+
+const COMPONENT_LIST = {
+	YiBin,
+	ZiGong,
+	LuZhou,
 };
-// 页面加载时
-const toolbarRef = ref<RefType>();
-const tableRef = ref<RefType>();
-onMounted(() => {
-	queryList();
-	if (tableRef.value && toolbarRef.value) {
-		tableRef.value.connect(toolbarRef.value);
-	}
-	getBaseData();
+// 当前地州市
+const currentCity = computed(() => {
+	return COMPONENT_LIST[themeConfig.value.appScope];
 });
-</script>
+</script>

+ 228 - 0
src/views/quality/index/YBQuality.vue

@@ -0,0 +1,228 @@
+<template>
+  <div class="quality-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="item.value"
+            :name="item.key"
+            v-for="item in qualitySourceOptions"
+            :key="item.key"
+            :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="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,
+				}"
+      >
+      </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="qualityIndex"
+            :custom-config="{ storage: true }"
+        >
+          <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 === 2">
+            <vxe-column field="order.centerToOrgTime" title="交办时间" width="160">
+              <template #default="{ row }">
+                {{ formatDate(row.order.centerToOrgTime, 'YYYY-mm-dd HH:MM:SS') }}
+              </template>
+            </vxe-column>
+          </template>
+          <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>
+          <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 title="操作" fixed="right" width="90" align="center">
+            <template #default="{ row }">
+              <el-button link type="primary" @click="onQualityInspection(row)" v-auth="'quality:inspection'" 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>
+
+<script lang="tsx" setup name="qualityIndex">
+import { 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';
+
+// 引入组件
+const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
+const QualityInspection = defineAsyncComponent(() => import('@/views/quality/index/components/Quality-inspection.vue')); // 质检
+const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
+
+// 定义变量内容
+const state = reactive<any>({
+  loading: false, // 加载状态
+  queryParams: {
+    // 查询参数
+    PageIndex: 1,
+    PageSize: 20,
+    Source: 1, // 受理待质检
+    crTime: [], // 创建时间
+    CreationTimeStart: null, // 创建时间 开始
+    CreationTimeEnd: null, // 创建时间 结束
+    State: 0, // 待质检
+    Keyword: null,
+  },
+  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);
+  }
+};
+// 手动查询,将页码设置为1
+const handleQuery = () => {
+  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'); // 删除无用的参数
+  qualityList(requestParams.value)
+      .then((res) => {
+        state.loading = false;
+        state.tableData = res.result.items ?? [];
+        state.total = res.result.total ?? 0;
+      })
+      .finally(() => {
+        state.loading = false;
+      });
+};
+// 重置表单
+const resetQuery = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  formEl.resetFields();
+  state.queryParams.CreationTimeStart = null;
+  state.queryParams.CreationTimeEnd = null;
+  queryList();
+};
+// 质检
+const qualityInspectionRef = ref<RefType>();
+const onQualityInspection = (row: any) => {
+  qualityInspectionRef.value.openDialog(row, state.queryParams.Source);
+};
+// 页面加载时
+const toolbarRef = ref<RefType>();
+const tableRef = ref<RefType>();
+onMounted(() => {
+  queryList();
+  if (tableRef.value && toolbarRef.value) {
+    tableRef.value.connect(toolbarRef.value);
+  }
+  getBaseData();
+});
+</script>

+ 306 - 0
src/views/quality/index/ZGQuality.vue

@@ -0,0 +1,306 @@
+<template>
+	<div class="quality-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="item.value"
+					:name="item.key"
+					v-for="item in qualitySourceOptions"
+					:key="item.key"
+					: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="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="onTransfer"
+						:loading="state.loading"
+						v-auth="'quality:index:transfer'"
+						:disabled="isChecked"
+						v-if="state.queryParams.Source === 1"
+					>
+						录音转写<span v-if="checkTable.length">({{ checkTable.length }})</span>
+					</el-button>
+					<el-button type="primary" @click="onExport" :loading="state.loading" v-auth="'quality:index: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="qualityIndex"
+					: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="transferStateText" title="转写状态" width="100">
+						<template #default="{ row }">
+							{{ row.transferStateText }}
+						</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 === 2">
+						<vxe-column field="order.centerToOrgTime" title="交办时间" width="160">
+							<template #default="{ row }">
+								{{ formatDate(row.order.centerToOrgTime, 'YYYY-mm-dd HH:MM:SS') }}
+							</template>
+						</vxe-column>
+					</template>
+					<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>
+					<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 title="操作" fixed="right" width="90" align="center">
+						<template #default="{ row }">
+							<el-button link type="primary" @click="onQualityInspection(row)" v-auth="'quality:inspection'" 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="qualityInspectionRef" @updateList="queryList" />
+	</div>
+</template>
+
+<script lang="tsx" setup name="qualityIndex">
+import { computed, defineAsyncComponent, onMounted, reactive, ref } from 'vue';
+import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
+import { formatDate } from '@/utils/formatTime';
+import { qualityBaseData, qualityList, qualityRecordTransfer } from '@/api/quality';
+import { defaultTimeStartEnd, shortcuts } from '@/utils/constants';
+import Other from '@/utils/other';
+import { exportQuality } from '@/utils/tools';
+import { businessTagDelete } from '@/api/auxiliary/businessTag';
+
+// 引入组件
+const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
+const ZGQuality = defineAsyncComponent(() => import('@/views/quality/index/components/ZG-quality.vue')); // 质检
+const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
+
+// 定义变量内容
+const state = reactive<any>({
+	loading: false, // 加载状态
+	queryParams: {
+		// 查询参数
+		PageIndex: 1,
+		PageSize: 20,
+		Source: 1, // 受理待质检
+		crTime: [], // 创建时间
+		CreationTimeStart: null, // 创建时间 开始
+		CreationTimeEnd: null, // 创建时间 结束
+		State: 0, // 待质检
+		Keyword: null,
+	},
+	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);
+	}
+};
+// 手动查询,将页码设置为1
+const handleQuery = () => {
+	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'); // 删除无用的参数
+	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();
+};
+// 质检
+const qualityInspectionRef = ref<RefType>();
+const onQualityInspection = (row: any) => {
+	qualityInspectionRef.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);
+	}
+};
+
+const selectChangeEvent = ({ checked }) => {
+	if (tableRef.value) {
+		const records = tableRef.value.getCheckboxRecords();
+		checkTable.value = records;
+		console.log(checked ? '勾选事件' : '取消事件', records);
+	}
+};
+const isChecked = computed(() => {
+	return !checkTable.value.length;
+});
+const tableRef = ref<RefType>();
+// 录音转写
+const onTransfer = () => {
+	const ids = checkTable.value.map((item) => {
+		return { id: item.id };
+	});
+	ElMessageBox.confirm(`您确定要选中的质检转写录音,是否继续?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+		draggable: true,
+		cancelButtonClass: 'default-button',
+		autofocus: false,
+	})
+		.then(() => {
+			qualityRecordTransfer(ids)
+				.then(() => {
+					ElMessage.success('转写任务创建成功');
+					handleQuery();
+				})
+				.catch((err) => {});
+		})
+		.catch(() => {});
+};
+// 质检单导出
+const onExport = () => {
+	const ids = checkTable.value.map((item) => item.id);
+	exportQuality(ids);
+};
+onMounted(() => {
+	queryList();
+	if (tableRef.value && toolbarRef.value) {
+		tableRef.value.connect(toolbarRef.value);
+	}
+	getBaseData();
+});
+</script>

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

@@ -10,7 +10,7 @@
 		@close="close"
 		@opened="opened"
 	>
-		<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="110px">
+		<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="110px" v-loading="state.loading">
 			<el-row :gutter="10">
 				<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
 					<el-form-item label="工单编码:"> {{ state.orderDetail.no }} </el-form-item>
@@ -226,6 +226,8 @@ const qualityId = ref<string>(''); // 质检ID
 const openDialog = async (row: any, source?: string | number | undefined) => {
 	qualityId.value = row.id;
 	score.value = 100;
+  state.loading = true;
+  state.dialogVisible = true;
 	if (source) {
 		state.source = source;
 		const [temRes, qualityRes] = await Promise.all([
@@ -237,6 +239,7 @@ const openDialog = async (row: any, source?: string | number | undefined) => {
 		multipleSelection.value = tableData.value.filter((v: any) => v.check === true);
 		state.orderDetail = qualityRes.result?.order ?? {};
 		state.visit = qualityRes.result?.visit ?? {};
+    state.loading = false;
 		switch (source) {
 			case 1:
 				dialogTitle.value = '受理质检';
@@ -249,7 +252,6 @@ const openDialog = async (row: any, source?: string | number | undefined) => {
 				break;
 		}
 	}
-	state.dialogVisible = true;
 };
 const opened = () => {
 	// 回显选中表格

+ 491 - 0
src/views/quality/index/components/ZG-quality.vue

@@ -0,0 +1,491 @@
+<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(80vh - 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>
+          <div v-if="!state.qualityDetail.transferState">
+            <el-empty description="无录音转写内容" :image-size="100" style="padding:0">
+              <el-button type="primary">录音转写</el-button>
+            </el-empty>
+            <el-empty description="录音转写中,请稍后" :image-size="100">
+            </el-empty>
+          </div>
+        </template>
+			</pane>
+			<pane style="max-height: calc(80vh - 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">新增扣分项</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" />
+							<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">
+								<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="8" :xl="8">
+							<el-form-item label="总分:"> 100 分 </el-form-item>
+						</el-col>
+						<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
+							<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 }"
+								></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 { defineAsyncComponent, reactive, ref, watch } from 'vue';
+import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
+import { formatDate, formatDuration } from '@/utils/formatTime';
+import { templateList } from '@/api/quality/template';
+import Other from '@/utils/other';
+import { qualityUpdate, qualityDetail } from '@/api/quality';
+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: {}, // 工单详情
+	visit: {}, // 回访详情
+	dialogVisibleItem: false,
+	ruleItemForm: {
+		second: 0,
+		name: '', // 扣分时间点
+		grade: 1, // 分值
+		content: '', // 扣分内容
+	},
+	source: 1, // 来源
+  qualityDetail:{},
+});
+const dialogTitle = ref<string>('受理质检');
+const dialogTitleItem = ref<string>('新增扣分项');
+const projectArray = ref<EmptyArrayType>([]); // 质检项数组
+const tableData = ref<EmptyArrayType>([]); // 表格数据
+const qualityId = ref<string>(''); // 质检ID
+// 打开弹窗
+const openDialog = async (row: any, source?: string | number | undefined) => {
+	qualityId.value = row.id;
+	score.value = 100;
+	state.loading = true;
+	state.dialogVisible = true;
+	if (source) {
+		state.source = source;
+		const [temRes, qualityRes] = await Promise.all([
+			templateList({ IsEnable: 1, Grouping: source, PageIndex: 1, PageSize: 999 }),
+			qualityDetail(row.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 ?? {};
+		state.loading = false;
+		switch (source) {
+			case 1:
+				dialogTitle.value = '受理质检';
+				break;
+			case 2:
+				dialogTitle.value = '派单质检';
+				break;
+			case 3:
+				dialogTitle.value = '回访质检';
+				break;
+		}
+	}
+};
+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 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 formatSecond = (val: string) => {
+	if (!val) return 0;
+	const minute = Number(val.split(':')[0]);
+	const second = Number(val.split(':')[1]);
+	return (minute * 60 + second) * 1000;
+};
+// 选择开始扣分时间点
+const selectSecond = (val: any) => {
+	state.ruleItemForm.second = formatSecond(val);
+};
+// 选择结束扣分时间点
+const selectSecondEnd = (val: any) => {
+	state.ruleItemForm.endSecond = formatSecond(val);
+};
+// 扣分项保存
+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);
+			setTimeout(() => {
+				getEnableAcceptType();
+			}, 100);
+		})
+		.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 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>

+ 17 - 218
src/views/quality/index/index.vue

@@ -1,228 +1,27 @@
 <template>
-	<div class="quality-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="item.value"
-					:name="item.key"
-					v-for="item in qualitySourceOptions"
-					:key="item.key"
-					: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="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,
-				}"
-			>
-			</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="qualityIndex"
-					:custom-config="{ storage: true }"
-				>
-					<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 === 2">
-						<vxe-column field="order.centerToOrgTime" title="交办时间" width="160">
-							<template #default="{ row }">
-								{{ formatDate(row.order.centerToOrgTime, 'YYYY-mm-dd HH:MM:SS') }}
-							</template>
-						</vxe-column>
-					</template>
-					<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>
-					<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 title="操作" fixed="right" width="90" align="center">
-						<template #default="{ row }">
-							<el-button link type="primary" @click="onQualityInspection(row)" v-auth="'quality:inspection'" 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>
+  <component :is="currentCity" />
 </template>
 
 <script lang="tsx" setup name="qualityIndex">
-import { 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 { defineAsyncComponent, computed } from 'vue';
+import { useThemeConfig } from '@/stores/themeConfig';
+import { storeToRefs } from 'pinia';
 
 // 引入组件
-const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
-const QualityInspection = defineAsyncComponent(() => import('@/views/quality/index/components/Quality-inspection.vue')); // 质检
-const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
+const YiBin = defineAsyncComponent(() => import('@/views/quality/index/YBQuality.vue')); // 宜宾工单质检
+const ZiGong = defineAsyncComponent(() => import('@/views/quality/index/ZGQuality.vue')); // 自贡工单质检
+const LuZhou = defineAsyncComponent(() => import('@/views/quality/index/YBQuality.vue')); // 泸州工单质检
 
-// 定义变量内容
-const state = reactive<any>({
-	loading: false, // 加载状态
-	queryParams: {
-		// 查询参数
-		PageIndex: 1,
-		PageSize: 20,
-		Source: 1, // 受理待质检
-		crTime: [], // 创建时间
-		CreationTimeStart: null, // 创建时间 开始
-		CreationTimeEnd: null, // 创建时间 结束
-		State: 0, // 待质检
-		Keyword: null,
-	},
-	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);
-	}
-};
-// 手动查询,将页码设置为1
-const handleQuery = () => {
-	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'); // 删除无用的参数
-	qualityList(requestParams.value)
-		.then((res) => {
-			state.loading = false;
-			state.tableData = res.result.items ?? [];
-			state.total = res.result.total ?? 0;
-		})
-		.finally(() => {
-			state.loading = false;
-		});
-};
-// 重置表单
-const resetQuery = (formEl: FormInstance | undefined) => {
-	if (!formEl) return;
-	formEl.resetFields();
-	state.queryParams.CreationTimeStart = null;
-	state.queryParams.CreationTimeEnd = null;
-	queryList();
-};
-// 质检
-const qualityInspectionRef = ref<RefType>();
-const onQualityInspection = (row: any) => {
-	qualityInspectionRef.value.openDialog(row, state.queryParams.Source);
+const storesThemeConfig = useThemeConfig();
+const { themeConfig } = storeToRefs(storesThemeConfig);
+
+const COMPONENT_LIST = {
+  YiBin,
+  ZiGong,
+  LuZhou
 };
-// 页面加载时
-const toolbarRef = ref<RefType>();
-const tableRef = ref<RefType>();
-onMounted(() => {
-	queryList();
-	if (tableRef.value && toolbarRef.value) {
-		tableRef.value.connect(toolbarRef.value);
-	}
-	getBaseData();
+// 当前地州市
+const currentCity = computed(() => {
+  return COMPONENT_LIST[themeConfig.value.appScope];
 });
 </script>