todo.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. <template>
  2. <div class="business-publish-todo-container layout-padding">
  3. <div class="layout-padding-auto layout-padding-view pd20">
  4. <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
  5. <el-form-item label="数据范围" prop="QuerySelf" v-auth="'business:publish:todo:querySelf'">
  6. <el-segmented
  7. :options="[
  8. { label: '我的', value: 'true' },
  9. { label: '全部', value: 'false' },
  10. ]"
  11. v-model="state.queryParams.QuerySelf"
  12. @change="handleQuery"
  13. :disabled="state.loading"
  14. />
  15. </el-form-item>
  16. <el-form-item label="省市工单" prop="fastSearch">
  17. <el-segmented
  18. :options="[
  19. {
  20. value: 'all',
  21. label: '全部',
  22. },
  23. {
  24. value: 'city',
  25. label: '市工单',
  26. },
  27. {
  28. value: 'province',
  29. label: '省工单',
  30. },
  31. ]"
  32. v-model="fastSearch"
  33. @change="fastSearchChange"
  34. :disabled="state.loading"
  35. />
  36. </el-form-item>
  37. <el-form-item label="工单标题" prop="Title">
  38. <el-input v-model.trim="state.queryParams.Title" placeholder="工单标题" clearable @keyup.enter="handleQuery" class="keyword-input" />
  39. </el-form-item>
  40. <el-form-item label="工单编码" prop="No">
  41. <el-input v-model.trim="state.queryParams.No" placeholder="工单编码" clearable @keyup.enter="handleQuery" class="keyword-input" />
  42. </el-form-item>
  43. <el-form-item>
  44. <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
  45. <el-button @click="drawer = true" class="default-button"> <SvgIcon name="ele-Search" class="mr5" />更多查询</el-button>
  46. </el-form-item>
  47. </el-form>
  48. <vxe-toolbar
  49. ref="toolbarRef"
  50. :loading="state.loading"
  51. custom
  52. :refresh="{
  53. queryMethod: handleQuery,
  54. }"
  55. >
  56. <template #buttons>
  57. <el-button type="primary" @click="onJbExport" :loading="state.loading" :disabled="isChecked"
  58. ><SvgIcon name="iconfont icon-daochu" class="mr5" />交办单导出
  59. </el-button>
  60. <el-button type="primary" @click="publishMultiple" v-auth="'business:publish:todo:multiple'" :disabled="isChecked">
  61. <SvgIcon name="iconfont icon-tianjiawenjian" class="mr5" />批量发布
  62. </el-button>
  63. <el-button type="primary" @click="onMigration" v-auth="'business:publish:todo:migration'" :disabled="isChecked">
  64. <SvgIcon name="ele-Rank" class="mr5" />平级移动
  65. </el-button>
  66. </template>
  67. </vxe-toolbar>
  68. <div style="overflow: hidden; width: 100%; height: 100%; flex: 1">
  69. <vxe-table
  70. border
  71. :loading="state.loading"
  72. :data="state.tableData"
  73. :column-config="{ resizable: true }"
  74. :row-config="{ isCurrent: true, isHover: true, height: 30 }"
  75. ref="tableRef"
  76. @checkbox-all="selectAllChangeEvent"
  77. @checkbox-change="selectChangeEvent"
  78. height="auto"
  79. auto-resize
  80. show-overflow
  81. :scrollY="{ enabled: true, gt: 0 }"
  82. id="businessPublishTodo"
  83. :custom-config="{ storage: true }"
  84. >
  85. <vxe-column type="checkbox" width="60" align="center"></vxe-column>
  86. <vxe-column field="expiredStatusText" title="超期状态" width="90" align="center">
  87. <template #default="{ row }">
  88. <span :class="'overdue-status-' + row.expiredStatus" :title="row.expiredStatusText"></span>
  89. </template>
  90. </vxe-column>
  91. <vxe-column field="no" title="工单编码" width="140"></vxe-column>
  92. <vxe-column field="isProvinceText" title="省/市工单" width="100"></vxe-column>
  93. <vxe-column field="statusText" title="工单状态" width="100"></vxe-column>
  94. <vxe-column field="title" title="工单标题" width="200">
  95. <template #default="{ row }">
  96. <order-detail :order="row" @updateList="queryList(true)">{{ row.title }}</order-detail>
  97. </template>
  98. </vxe-column>
  99. <vxe-column field="sourceChannel" title="来源渠道" width="100"></vxe-column>
  100. <vxe-column field="acceptType" title="受理类型" width="100"></vxe-column>
  101. <vxe-column field="counterSignTypeText" title="是否会签" width="90"></vxe-column>
  102. <vxe-column field="actualHandleOrgName" title="接办部门" width="140"></vxe-column>
  103. <vxe-column field="hotspotName" title="热点分类" width="140"></vxe-column>
  104. <vxe-column field="acceptorName" title="受理人" width="120"></vxe-column>
  105. <vxe-column field="actualHandleTime" title="接办时间" width="160">
  106. <template #default="{ row }">
  107. {{ formatDate(row.actualHandleTime, 'YYYY-mm-dd HH:MM:SS') }}
  108. </template>
  109. </vxe-column>
  110. <vxe-column field="startTime" title="受理时间" width="160">
  111. <template #default="{ row }">
  112. {{ formatDate(row.startTime, 'YYYY-mm-dd HH:MM:SS') }}
  113. </template>
  114. </vxe-column>
  115. <vxe-column field="filedTime" title="办结时间" width="160">
  116. <template #default="{ row }">
  117. {{ formatDate(row.filedTime, 'YYYY-mm-dd HH:MM:SS') }}
  118. </template>
  119. </vxe-column>
  120. <vxe-column title="操作" fixed="right" width="90" align="center">
  121. <template #default="{ row }">
  122. <el-button link type="primary" @click="publish(row)" title="发布工单" v-auth="'business:publish:todo:publish'"> 发布 </el-button>
  123. </template>
  124. </vxe-column>
  125. </vxe-table>
  126. </div>
  127. <div class="flex-end" style="align-items: center">
  128. <span v-loading="totalLoading" class="mr10">共 {{ totalCount }} 条</span>
  129. <!-- sizes-->
  130. <el-pagination
  131. layout="prev, pager, next"
  132. :total="state.total"
  133. @current-change="handleCurrentChange"
  134. :page-size="state.queryParams.PageSize"
  135. :current-page="state.queryParams.PageIndex"
  136. @size-change="handleSizeChange"
  137. :disabled="state.loading"
  138. />
  139. </div>
  140. </div>
  141. <!-- 工单发布详情 -->
  142. <order-publish ref="orderPublishRef" @updateList="queryList(true)" />
  143. <!-- 工单平移 -->
  144. <order-migration ref="orderMigrationRef" @updateList="queryList(true)" />
  145. <!-- 更多查询 -->
  146. <el-drawer v-model="drawer" title="更多查询" size="500px">
  147. <el-form :model="state.queryParams" ref="drawerRuleFormRef" @submit.native.prevent label-width="100px">
  148. <el-form-item label="归档类型" prop="FiledType">
  149. <el-select v-model="state.queryParams.FiledType" placeholder="请选择归档类型" @change="handleQuery" clearable>
  150. <el-option label="中心归档" value="10" />
  151. <el-option label="部门归档" value="20" />
  152. </el-select>
  153. </el-form-item>
  154. <el-form-item label="是否会签" prop="IsCountersign">
  155. <el-select v-model="state.queryParams.IsCountersign" placeholder="请选择是否会签" @change="handleQuery" clearable>
  156. <el-option label="是" value="true" />
  157. <el-option label="否" value="false" />
  158. </el-select>
  159. </el-form-item>
  160. <el-form-item label="受理时间" prop="slTime">
  161. <el-date-picker
  162. v-model="state.queryParams.slTime"
  163. type="datetimerange"
  164. unlink-panels
  165. range-separator="至"
  166. start-placeholder="开始时间"
  167. end-placeholder="结束时间"
  168. :shortcuts="shortcuts"
  169. @change="handleQuery"
  170. value-format="YYYY-MM-DD[T]HH:mm:ss"
  171. :default-time="defaultTimeStartEnd"
  172. />
  173. </el-form-item>
  174. <el-form-item label="来电号码" prop="FromPhone">
  175. <el-input v-model.trim="state.queryParams.FromPhone" placeholder="来电号码" clearable @keyup.enter="handleQuery" />
  176. </el-form-item>
  177. <el-form-item label="接办部门" prop="ActualHandleOrgName">
  178. <el-input v-model="state.queryParams.ActualHandleOrgName" placeholder="请填写接办部门名称" clearable @keyup.enter="handleQuery" />
  179. </el-form-item>
  180. <el-form-item label="受理类型" prop="AcceptType">
  181. <el-select v-model="state.queryParams.AcceptType" placeholder="请选择受理类型" clearable @change="handleQuery">
  182. <el-option v-for="item in acceptTypeOptions" :value="item.dicDataValue" :key="item.dicDataValue" :label="item.dicDataName" />
  183. </el-select>
  184. </el-form-item>
  185. <el-form-item label="热点分类" prop="Hotspot">
  186. <el-input v-model.trim="state.queryParams.Hotspot" placeholder="热点分类名称" clearable @keyup.enter="handleQuery" />
  187. </el-form-item>
  188. <el-form-item label="受理人" prop="NameOrNo">
  189. <el-input v-model="state.queryParams.NameOrNo" placeholder="受理人/坐席工号" clearable @keyup.enter="handleQuery" />
  190. </el-form-item>
  191. <el-form-item label="派单人" prop="CenterToOrgHandlerName">
  192. <el-input v-model="state.queryParams.CenterToOrgHandlerName" placeholder="派单人" clearable @keyup.enter="handleQuery" />
  193. </el-form-item>
  194. <el-form-item label="来源渠道" prop="Channel">
  195. <el-select v-model="state.queryParams.Channel" placeholder="请选择来源渠道" clearable @change="handleQuery">
  196. <el-option v-for="items in channelOptions" :key="items.dicDataValue" :label="items.dicDataName" :value="items.dicDataValue" />
  197. </el-select>
  198. </el-form-item>
  199. <el-form-item label="办结时间" prop="bjTime">
  200. <el-date-picker
  201. v-model="state.queryParams.bjTime"
  202. type="datetimerange"
  203. unlink-panels
  204. range-separator="至"
  205. start-placeholder="开始时间"
  206. end-placeholder="结束时间"
  207. :shortcuts="shortcuts"
  208. @change="handleQuery"
  209. value-format="YYYY-MM-DD[T]HH:mm:ss"
  210. :default-time="defaultTimeStartEnd"
  211. />
  212. </el-form-item>
  213. <template v-if="['ZiGong'].includes(themeConfig.appScope)">
  214. <!-- <el-form-item label="行业" prop="Channel">
  215. <el-select v-model="state.queryParams.Channel" placeholder="请选择行业" clearable @change="handleQuery">
  216. <el-option v-for="items in channelOptions" :key="items.dicDataValue" :label="items.dicDataName" :value="items.dicDataValue" />
  217. </el-select>
  218. </el-form-item>-->
  219. <el-form-item label="工单标签" prop="OrderTag">
  220. <el-select v-model="state.queryParams.OrderTag" placeholder="请选择工单标签" clearable @change="handleQuery">
  221. <el-option v-for="items in orderTags" :key="items.dicDataValue" :label="items.dicDataName" :value="items.dicDataValue" />
  222. </el-select>
  223. </el-form-item>
  224. </template>
  225. <el-form-item label="是否超期" prop="IsOverTime">
  226. <el-select v-model="state.queryParams.IsOverTime" placeholder="请选择是否超期" class="w100" clearable @change="handleQuery">
  227. <el-option label="是" :value="true" />
  228. <el-option label="否" :value="false" />
  229. </el-select>
  230. </el-form-item>
  231. </el-form>
  232. <template #footer>
  233. <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
  234. <el-button @click="resetQuery(drawerRuleFormRef)" class="default-button"> <SvgIcon name="ele-Refresh" class="mr5" />重置 </el-button>
  235. </template>
  236. </el-drawer>
  237. </div>
  238. </template>
  239. <script setup lang="tsx" name="businessPublishTodo">
  240. import { computed, defineAsyncComponent, onMounted, reactive, ref } from 'vue';
  241. import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
  242. import { formatDate } from '@/utils/formatTime';
  243. import { batchPublishOrder, publishBaseInfo, publishListFixed, publishListFixedCount } from '@/api/todo/publish';
  244. import { defaultTimeStartEnd, shortcuts } from '@/utils/constants';
  245. import { useThemeConfig } from '@/stores/themeConfig';
  246. import { storeToRefs } from 'pinia';
  247. import Other from '@/utils/other';
  248. import { exportAssignment, getNeedArr } from '@/utils/tools';
  249. // 引入组件
  250. const OrderPublish = defineAsyncComponent(() => import('@/views/business/publish/components/Order-publish.vue')); // 发布
  251. const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
  252. const OrderMigration = defineAsyncComponent(() => import('@/views/todo/center/Order-migration.vue')); // 工单平移
  253. const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
  254. // 定义变量内容
  255. const state = reactive<any>({
  256. queryParams: {
  257. // 查询条件
  258. PageIndex: 1,
  259. PageSize: 20,
  260. OrderTitle: null, // 关键词
  261. No: null,
  262. FiledType: null, // 归档类型
  263. IsCountersign: null, // 是否会签
  264. Keyword: null, // 标题
  265. QuerySelf: 'true', // 是否只查询自己的待发布工单
  266. IsProvinceOrder: null, // 是否省工单
  267. Channel: null,
  268. slTime: [],
  269. CreationTimeStart: null,
  270. CreationTimeEnd: null,
  271. bjTime: [],
  272. FiledTimeStart: null,
  273. FiledTimeEnd: null,
  274. },
  275. tableData: [], //表单
  276. loading: false, // 加载
  277. total: 0, // 总数
  278. });
  279. const fastSearch = ref('all'); // tab位置
  280. const storesThemeConfig = useThemeConfig();
  281. const { themeConfig } = storeToRefs(storesThemeConfig);
  282. const fastSearchChange = (val: string) => {
  283. fastSearch.value = val;
  284. switch (val) {
  285. case 'all':
  286. state.queryParams.IsProvinceOrder = null;
  287. break;
  288. case 'city':
  289. state.queryParams.IsProvinceOrder = false;
  290. break;
  291. case 'province':
  292. state.queryParams.IsProvinceOrder = true;
  293. break;
  294. }
  295. handleQuery();
  296. };
  297. // 手动查询,将页码设置为1
  298. const handleQuery = () => {
  299. state.queryParams.PageIndex = 1;
  300. queryIndex.value = 0;
  301. queryList(true);
  302. };
  303. // 改变页码
  304. const queryIndex = ref(0); // 数据批次
  305. const totalTable = ref([]); // 数据总数
  306. const handleCurrentChange = (val: number) => {
  307. state.queryParams.PageIndex = val;
  308. // 判断当前页是否是数据的最后一页
  309. if (val === Math.ceil(state.total / state.queryParams.PageSize)) {
  310. queryList();
  311. } else {
  312. state.tableData = getNeedArr(totalTable.value, state.queryParams.PageSize)[state.queryParams.PageIndex - 1]; //当前页的表格数据
  313. }
  314. };
  315. // 改变每页条数
  316. const handleSizeChange = (val: any) => {
  317. state.queryParams.PageSize = val;
  318. // 判断当前页是否是数据的最后一页
  319. if (val === Math.ceil(state.total / state.queryParams.PageSize)) {
  320. queryList();
  321. } else {
  322. state.tableData = getNeedArr(totalTable.value, state.queryParams.PageSize)[state.queryParams.PageIndex - 1]; //当前页的表格数据
  323. }
  324. };
  325. /** 获取列表 */
  326. const requestParams = ref<EmptyObjectType>({});
  327. const totalCount = ref(0);
  328. const totalLoading = ref(false);
  329. const queryList = (isQuery: boolean = false) => {
  330. return new Promise((resolve, reject) => {
  331. state.loading = true;
  332. requestParams.value = Other.deepClone(state.queryParams);
  333. requestParams.value.CreationTimeStart = state.queryParams.slTime === null ? null : state.queryParams.slTime[0]; // 受理时间
  334. requestParams.value.CreationTimeEnd = state.queryParams.slTime === null ? null : state.queryParams.slTime[1];
  335. Reflect.deleteProperty(requestParams.value, 'slTime'); // 删除无用的参数
  336. requestParams.value.FiledTimeStart = state.queryParams.bjTime === null ? null : state.queryParams.bjTime[0]; // 期满时间
  337. requestParams.value.FiledTimeEnd = state.queryParams.bjTime === null ? null : state.queryParams.bjTime[1];
  338. Reflect.deleteProperty(requestParams.value, 'bjTime'); // 删除无用的参数
  339. requestParams.value.QueryIndex = queryIndex.value; // 数据批次
  340. if(isQuery) requestParams.value.QueryIndex = 0;
  341. publishListFixed(requestParams.value)
  342. .then((res: any) => {
  343. if (isQuery) {
  344. // 如果是查询
  345. totalTable.value = []; // 先清空
  346. totalTable.value = res?.result;
  347. state.total = totalTable.value.length;
  348. state.tableData = getNeedArr(totalTable.value, state.queryParams.PageSize)[state.queryParams.PageIndex - 1]; //当前页的表格数据
  349. queryIndex.value = 1; // 请求完成之后,页码加一避免重复
  350. } else {
  351. if (res?.result.length) queryIndex.value++;
  352. totalTable.value = totalTable.value.concat(res?.result);
  353. state.total = totalTable.value.length;
  354. state.tableData = getNeedArr(totalTable.value, state.queryParams.PageSize)[state.queryParams.PageIndex - 1]; //当前页的表格数据
  355. }
  356. state.loading = false;
  357. resolve(res);
  358. })
  359. .then(() => {
  360. // 查询总数
  361. publishListFixedCount(requestParams.value)
  362. .then((count) => {
  363. totalCount.value = count.result;
  364. totalLoading.value = false;
  365. })
  366. .catch(() => {
  367. totalLoading.value = false;
  368. });
  369. })
  370. .catch(() => {
  371. state.loading = false;
  372. totalLoading.value = false;
  373. reject();
  374. });
  375. });
  376. };
  377. /** 重置按钮操作 */
  378. const drawerRuleFormRef = ref();
  379. const ruleFormRef = ref<RefType>(); // 表单ref
  380. const drawer = ref(false);
  381. const resetQuery = (formEl: FormInstance | undefined) => {
  382. if (!formEl) return;
  383. formEl.resetFields();
  384. state.queryParams.QuerySelf = 'true';
  385. ruleFormRef.value?.resetFields();
  386. state.queryParams.IsProvinceOrder = null;
  387. fastSearch.value = 'all';
  388. queryList();
  389. };
  390. // 批量发布
  391. const publishMultiple = () => {
  392. const ids = checkTable.value.map((item: any) => item.id);
  393. ElMessageBox.confirm(`您确定要发布选中的工单吗,是否继续?`, '提示', {
  394. confirmButtonText: '确认',
  395. cancelButtonText: '取消',
  396. type: 'warning',
  397. draggable: true,
  398. cancelButtonClass: 'default-button',
  399. autofocus: false,
  400. })
  401. .then(() => {
  402. batchPublishOrder({ ids }).then(() => {
  403. ElMessage.success('操作成功');
  404. /*ElNotification({
  405. dangerouslyUseHTMLString: true,
  406. message: `<p style="word-break: break-all;max-height: 500px;overflow: auto">${res.result}</p>`,
  407. type: 'info',
  408. })*/
  409. handleQuery();
  410. });
  411. })
  412. .catch(() => {});
  413. };
  414. // 发布
  415. const orderPublishRef = ref<RefType>(); // 工单发布详情ref
  416. const publish = (row: any) => {
  417. orderPublishRef.value.openDialog(row);
  418. };
  419. // 平移功能
  420. const orderMigrationRef = ref<RefType>();
  421. const onMigration = () => {
  422. const ids = checkTable.value.map((item: any) => item.id);
  423. orderMigrationRef.value.openDialog('publishTodo', ids);
  424. };
  425. // 获取基础信息
  426. const channelOptions = ref<EmptyArrayType>([]); // 来源渠道
  427. const acceptTypeOptions = ref<EmptyArrayType>([]); // 受理类型
  428. const orderTags = ref<EmptyArrayType>([]); // 工单标签
  429. const getBaseInfo = async () => {
  430. // 获取基础信息
  431. try {
  432. const { result } = await publishBaseInfo();
  433. channelOptions.value = result.channelOptions;
  434. acceptTypeOptions.value = result.acceptTypeOptions;
  435. orderTags.value = result.orderTags;
  436. } catch (e) {
  437. console.log(e);
  438. }
  439. };
  440. // 交办单导出
  441. const onJbExport = () => {
  442. const ids = checkTable.value.map((item: any) => item.id);
  443. exportAssignment(ids);
  444. };
  445. const tableRef = ref<RefType>();
  446. const checkTable = ref<EmptyArrayType>([]);
  447. const selectAllChangeEvent = ({ checked }) => {
  448. if (tableRef.value) {
  449. const records = tableRef.value.getCheckboxRecords();
  450. checkTable.value = records;
  451. console.log(checked ? '所有勾选事件' : '所有取消事件', records);
  452. }
  453. };
  454. const selectChangeEvent = ({ checked }) => {
  455. if (tableRef.value) {
  456. const records = tableRef.value.getCheckboxRecords();
  457. checkTable.value = records;
  458. console.log(checked ? '勾选事件' : '取消事件', records);
  459. }
  460. };
  461. const isChecked = computed(() => {
  462. return !Boolean(checkTable.value.length);
  463. });
  464. const toolbarRef = ref<RefType>();
  465. onMounted(() => {
  466. queryList().then(() => {
  467. getBaseInfo();
  468. });
  469. if (tableRef.value && toolbarRef.value) {
  470. tableRef.value.connect(toolbarRef.value);
  471. }
  472. });
  473. </script>