index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. <template>
  2. <div class="todo-center-container layout-padding">
  3. <div class="layout-padding-auto layout-padding-view pd15">
  4. <vxe-grid v-bind="gridOptions" v-on="gridEvents" ref="gridRef" @checkbox-all="selectAllChangeEvent" @checkbox-change="selectChangeEvent">
  5. <template #form>
  6. <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent class="mb10" inline :disabled="state.loading">
  7. <el-form-item label="工单标题" prop="Title">
  8. <el-input v-model.trim="state.queryParams.Title" placeholder="工单标题" clearable @keyup.enter="handleQuery" class="keyword-input" />
  9. </el-form-item>
  10. <el-form-item label="工单编码" prop="No">
  11. <el-input v-model.trim="state.queryParams.No" placeholder="工单编码" clearable @keyup.enter="handleQuery" class="keyword-input" />
  12. </el-form-item>
  13. <el-form-item>
  14. <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
  15. <el-button @click="drawer = true" class="default-button"> <SvgIcon name="ele-Search" class="mr5" />更多查询</el-button>
  16. </el-form-item>
  17. </el-form>
  18. </template>
  19. <template #toolbar_buttons>
  20. <el-button type="primary" @click="onJbExport" :loading="state.loading" :disabled="isChecked" v-auth="'todo:center:jbdExport'"
  21. ><SvgIcon name="iconfont icon-daochu" class="mr5" />交办单导出<span v-if="checkTable.length">({{ checkTable.length }})</span>
  22. </el-button>
  23. <el-button type="primary" @click="onAssignOrders" :loading="state.loading" v-auth="'todo:center:assignOrders'"
  24. ><SvgIcon name="ele-List" class="mr5" />分配工单
  25. </el-button>
  26. </template>
  27. <template #statusText="{ row }">
  28. <el-text type="danger" tag="b" v-if="[1, 2, 3, 9, 101, 102, 103, 104, 105, 200].includes(row.status)">{{ row.statusText }}</el-text>
  29. <span v-else>{{ row.statusText }}</span>
  30. </template>
  31. <template #order_detail="{ row }">
  32. <order-detail :order="row" @updateList="refreshList">{{ row.title }}</order-detail>
  33. </template>
  34. <template #action="{ row }">
  35. <el-button link type="primary" @click="onSign(row)" title="签收工单" v-show="row.canSign" v-auth="'todo:center:sign'"> 签收 </el-button>
  36. <el-button link type="success" @click="onOrderEdit(row)" title="编辑工单" v-show="row.canEdit" v-auth="'todo:center:edit'"> 修改 </el-button>
  37. <el-button link type="primary" @click="onMigration(row)" title="平级移动" v-auth="'todo:center:migration'"> 平级移动 </el-button>
  38. </template>
  39. <template #pager>
  40. <pagination
  41. @pagination="queryList"
  42. :total="state.total"
  43. v-model:current-page="state.queryParams.PageIndex"
  44. v-model:page-size="state.queryParams.PageSize"
  45. :disabled="state.loading"
  46. />
  47. </template>
  48. </vxe-grid>
  49. </div>
  50. <!-- 工单平移 -->
  51. <order-migration ref="orderMigrationRef" @updateList="refreshList" />
  52. <!-- 更多查询 -->
  53. <el-drawer v-model="drawer" title="更多查询" size="500px">
  54. <el-form :model="state.queryParams" ref="drawerRuleFormRef" @submit.native.prevent label-width="100px" :disabled="gridOptions.loading">
  55. <el-form-item label="受理时间" prop="scTime">
  56. <el-date-picker
  57. v-model="state.queryParams.scTime"
  58. type="datetimerange"
  59. unlink-panels
  60. range-separator="至"
  61. start-placeholder="开始时间"
  62. end-placeholder="结束时间"
  63. :shortcuts="shortcuts"
  64. @change="handleQuery"
  65. value-format="YYYY-MM-DD[T]HH:mm:ss"
  66. :default-time="defaultTimeStartEnd"
  67. />
  68. </el-form-item>
  69. <el-form-item label="当前节点" prop="StepName">
  70. <el-select v-model="state.queryParams.StepName" placeholder="请选择当前节点" clearable class="w100" @change="handleQuery">
  71. <el-option v-for="item in state.stepNamesOptions" :value="item" :key="item" :label="item" />
  72. </el-select>
  73. </el-form-item>
  74. <el-form-item label="派单员" prop="CenterToOrgHandlerName">
  75. <el-input v-model="state.queryParams.CenterToOrgHandlerName" placeholder="派单员" clearable @keyup.enter="handleQuery" />
  76. </el-form-item>
  77. <el-form-item label="是否紧急" prop="IsUrgent">
  78. <el-select v-model="state.queryParams.IsUrgent" placeholder="请选择是否紧急" clearable class="w100" @change="handleQuery">
  79. <el-option :value="true" label="紧急" />
  80. <el-option :value="false" label="不紧急" />
  81. </el-select>
  82. </el-form-item>
  83. <el-form-item label="接办部门" prop="ActualHandleOrgName">
  84. <el-input v-model="state.queryParams.ActualHandleOrgName" placeholder="接办部门名称" clearable @keyup.enter="handleQuery" />
  85. </el-form-item>
  86. <el-form-item label="工单状态" prop="Status">
  87. <el-select v-model="state.queryParams.Status" placeholder="请选择工单状态" clearable class="w100" @change="handleQuery">
  88. <el-option v-for="item in state.orderStatusOptions" :value="item.key" :key="item.key" :label="item.value" />
  89. </el-select>
  90. </el-form-item>
  91. <el-form-item label="受理人" prop="AcceptorName">
  92. <el-input v-model="state.queryParams.AcceptorName" placeholder="受理人" clearable @keyup.enter="handleQuery" />
  93. </el-form-item>
  94. <el-form-item label="超期状态" prop="ExpiredStatus">
  95. <el-select v-model="state.queryParams.ExpiredStatus" placeholder="请选择超期状态" clearable @change="handleQuery">
  96. <el-option v-for="item in state.expiredStatusOptions" :value="item.key" :key="item.key" :label="item.value" />
  97. </el-select>
  98. </el-form-item>
  99. <el-form-item label="来源渠道" prop="SourceChannelCode">
  100. <el-select v-model="state.queryParams.SourceChannelCode" placeholder="请选择来源渠道" clearable @change="handleQuery">
  101. <el-option v-for="item in state.channelOptions" :value="item.dicDataValue" :key="item.dicDataValue" :label="item.dicDataName" />
  102. </el-select>
  103. </el-form-item>
  104. <!-- <el-form-item label="工单标签" prop="OrderTagCode" v-if="['ZiGong', 'LuZhou'].includes(themeConfig.appScope)">
  105. <el-cascader
  106. :options="state.orderTagOptions"
  107. filterable
  108. :props="{ value: 'dicDataValue', label: 'dicDataName', emitPath: false, checkStrictly: true }"
  109. placeholder="请选择工单标签"
  110. class="w100"
  111. v-model="state.queryParams.OrderTagCode"
  112. @change="handleQuery"
  113. clearable
  114. >
  115. </el-cascader>
  116. </el-form-item>-->
  117. </el-form>
  118. <template #footer>
  119. <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
  120. <el-button @click="resetQuery(drawerRuleFormRef)" class="default-button"> <SvgIcon name="ele-Refresh" class="mr5" />重置 </el-button>
  121. </template>
  122. </el-drawer>
  123. </div>
  124. </template>
  125. <script setup lang="tsx" name="todoCenter">
  126. import { computed, defineAsyncComponent, onActivated, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
  127. import type { FormInstance } from 'element-plus';
  128. import { ElMessage, ElMessageBox } from 'element-plus';
  129. import { defaultTimeStartEnd, shortcuts } from '@/utils/constants';
  130. import other from '@/utils/other';
  131. import { useRouter } from 'vue-router';
  132. import { centerTodo, centerTodoBase } from '@/api/todo/center';
  133. import { orderAverage, orderSign } from '@/api/todo/order';
  134. import { debounce, exportAssignment } from '@/utils/tools';
  135. import mittBus from '@/utils/mitt';
  136. import { useThemeConfig } from '@/stores/themeConfig';
  137. import { storeToRefs } from 'pinia';
  138. // 引入组件
  139. const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
  140. const OrderMigration = defineAsyncComponent(() => import('@/views/todo/center/Order-migration.vue')); // 工单平移
  141. const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
  142. const storesThemeConfig = useThemeConfig();
  143. const { themeConfig } = storeToRefs(storesThemeConfig);
  144. // 定义变量内容
  145. const state = reactive<any>({
  146. queryParams: {
  147. PageIndex: 1, // 当前页
  148. PageSize: 20, // 每页条数
  149. Title: null, // 标题
  150. No: null, // 工单编号
  151. scTime: [], // 受理时间
  152. StCreationTime: null, // 受理开始时间
  153. EnCreationTime: null, // 受理结束时间
  154. AcceptorName: null, // 受理人
  155. ActualHandleOrgName: null, // 接办部门
  156. Status: null, // 工单状态
  157. ExpiredStatus: null, // 超期状态
  158. StepName: null, // 当前节点名称
  159. IsUrgent: null,
  160. CenterToOrgHandlerName: null, // 派单员
  161. SortField: null,
  162. SortRule: null,
  163. OrderTagCode: null,
  164. SourceChannelCode: null, // 来源渠道
  165. },
  166. tableData: [], //表单
  167. loading: false, // 加载
  168. total: 0, // 总数
  169. expiredStatusOptions: [], //超期状态
  170. orderStatusOptions: [], //工单状态
  171. stepNamesOptions: [], //步骤名称
  172. orderTagOptions: [], // 工单标签
  173. });
  174. const gridOptions = reactive<any>({
  175. loading: false,
  176. border: true,
  177. showOverflow: true,
  178. columnConfig: {
  179. resizable: true,
  180. },
  181. scrollY: {
  182. enabled: true,
  183. gt: 100,
  184. },
  185. toolbarConfig: {
  186. zoom: true,
  187. custom: true,
  188. refresh: {
  189. queryMethod: () => {
  190. handleQuery();
  191. },
  192. },
  193. slots: {
  194. buttons: 'toolbar_buttons',
  195. },
  196. },
  197. customConfig: {
  198. storage: true,
  199. },
  200. id: 'todoCenter',
  201. rowConfig: { isHover: true, height: 30, isCurrent: true, useKey: true },
  202. height: 'auto',
  203. columns: [
  204. { type: 'checkbox', width: 50, align: 'center' },
  205. {
  206. field: 'expiredStatusText',
  207. title: '状态',
  208. width: 60,
  209. align: 'center',
  210. slots: {
  211. default: ({ row }) => {
  212. return <span class={'overdue-status-' + row.expiredStatus} title={row.expiredStatusText}></span>;
  213. },
  214. },
  215. },
  216. { field: 'acceptorName', title: '受理人', width: 120 },
  217. { field: 'no', title: '工单编码', width: 140 },
  218. { field: 'isProvinceText', title: '省/市工单', width: 90 },
  219. {
  220. field: 'isUrgentText',
  221. title: '是否紧急',
  222. width: 90,
  223. slots: {
  224. default: ({ row }) => {
  225. return <span class="color-danger font-bold">{row.isUrgentText}</span>;
  226. },
  227. },
  228. },
  229. { field: 'currentStepName', title: '当前节点', width: 120 },
  230. {
  231. field: 'statusText',
  232. title: '工单状态',
  233. width: 110,
  234. slots: {
  235. default: 'statusText',
  236. },
  237. },
  238. {
  239. field: 'title',
  240. title: '工单标题',
  241. minWidth: 200,
  242. slots: { default: 'order_detail' },
  243. },
  244. { field: 'centerToOrgHandlerName', title: '派单员', width: 120 },
  245. {
  246. field: 'creationTime',
  247. title: '受理时间',
  248. width: 160,
  249. sortable: true,
  250. formatter: 'formatDate',
  251. },
  252. {
  253. field: 'expiredTime',
  254. title: '期满时间',
  255. width: 160,
  256. sortable: true,
  257. formatter: 'formatDate',
  258. },
  259. {
  260. field: 'filedTime',
  261. title: '办结时间',
  262. width: 160,
  263. sortable: true,
  264. formatter: 'formatDate',
  265. },
  266. { field: 'orgLevelOneName', title: '一级部门', width: 140 },
  267. { field: 'actualHandleOrgName', title: '接办部门', width: 140 },
  268. { field: 'acceptType', title: '受理类型', width: 110 },
  269. { field: 'counterSignTypeText', title: '是否会签', width: 110 },
  270. { field: 'sourceChannel', title: '来源渠道', width: 110 },
  271. { field: 'hotspotName', title: '热点分类', width: 150 },
  272. { field: 'sensitive', title: '敏感词', width: 150 },
  273. { field: 'reTransactNum', title: '重办次数', width: 130 },
  274. { title: '操作', width: 150, fixed: 'right', align: 'center', slots: { default: 'action' } },
  275. ],
  276. data: [],
  277. sortConfig: {
  278. remote: true,
  279. },
  280. });
  281. const gridEvents = {
  282. sortChange(val: any) {
  283. state.queryParams.SortField = val.order ? val.field : null;
  284. // 0 升序 1 降序
  285. state.queryParams.SortRule = val.order ? (val.order == 'desc' ? 1 : 0) : null;
  286. handleQuery();
  287. },
  288. };
  289. const router = useRouter(); // 路由
  290. // 获取查询条件基础信息
  291. const getBaseData = async () => {
  292. try {
  293. const res: any = await centerTodoBase();
  294. const mappings: any = {
  295. expiredStatusOptions: 'expiredStatus',
  296. orderStatusOptions: 'orderStatus',
  297. stepNamesOptions: 'stepNames',
  298. orderTagOptions: 'orderTag',
  299. channelOptions: 'channelOptions',
  300. };
  301. for (const key in mappings) {
  302. state[key] = res.result?.[mappings[key]] ?? [];
  303. }
  304. } catch (error) {
  305. console.log(error);
  306. }
  307. };
  308. // 手动查询,将页码设置为1
  309. const handleQuery = () => {
  310. state.queryParams.PageIndex = 1;
  311. queryList();
  312. };
  313. // 刷新列表 停留当前页 重新查询数据和总数
  314. const refreshList = () => {
  315. queryList();
  316. };
  317. /** 获取列表 */
  318. const requestParams = ref<EmptyObjectType>({});
  319. const queryList = () => {
  320. return new Promise((resolve, reject) => {
  321. requestParams.value = other.deepClone(state.queryParams);
  322. requestParams.value.StCreationTime = state.queryParams.scTime === null ? null : state.queryParams.scTime[0]; // 受理时间
  323. requestParams.value.EndCreationTime = state.queryParams.scTime === null ? null : state.queryParams.scTime[1];
  324. Reflect.deleteProperty(requestParams.value, 'scTime');
  325. state.loading = true;
  326. gridOptions.loading = true;
  327. centerTodo(requestParams.value)
  328. .then((response: any) => {
  329. state.tableData = response?.result.items ?? [];
  330. state.total = response?.result.total;
  331. gridRef.value.clearCheckboxRow();
  332. checkTable.value = [];
  333. gridOptions.data = state.tableData;
  334. state.loading = false;
  335. gridOptions.loading = false;
  336. resolve(response);
  337. })
  338. .catch(() => {
  339. state.loading = false;
  340. gridOptions.loading = false;
  341. gridRef.value.clearCheckboxRow();
  342. checkTable.value = [];
  343. reject();
  344. });
  345. });
  346. };
  347. /** 重置按钮操作 */
  348. const drawerRuleFormRef = ref();
  349. const ruleFormRef = ref<RefType>(); // 表单ref
  350. const drawer = ref(false);
  351. const resetQuery = (formEl: FormInstance | undefined) => {
  352. if (!formEl) return;
  353. formEl.resetFields();
  354. ruleFormRef.value?.resetFields();
  355. queryList();
  356. };
  357. // 签收工单
  358. const onSign = (row: any) => {
  359. ElMessageBox.confirm(`您确定要要签收【${row.title}】,是否继续?`, '提示', {
  360. confirmButtonText: '确认',
  361. cancelButtonText: '取消',
  362. type: 'warning',
  363. draggable: true,
  364. cancelButtonClass: 'default-button',
  365. autofocus: false,
  366. })
  367. .then(() => {
  368. orderSign(row.id).then(() => {
  369. ElMessage.success('签收成功');
  370. queryList();
  371. });
  372. })
  373. .catch(() => {});
  374. };
  375. // 编辑工单
  376. const onOrderEdit = (row: any) => {
  377. router.push({
  378. name: 'orderAccept',
  379. query: {
  380. id: row.id,
  381. tagsViewName: `编辑工单-${row.title}`,
  382. },
  383. });
  384. };
  385. // 交办单导出
  386. const onJbExport = () => {
  387. const ids = checkTable.value.map((item: any) => item.id);
  388. exportAssignment(ids);
  389. };
  390. // 平移功能
  391. const orderMigrationRef = ref<RefType>();
  392. const onMigration = (row: any) => {
  393. orderMigrationRef.value.openDialog('centerTodo', row);
  394. };
  395. // 分配工单
  396. const onAssignOrders = () => {
  397. ElMessageBox.confirm(`您确定分配工单,是否继续?`, '提示', {
  398. confirmButtonText: '确认',
  399. cancelButtonText: '取消',
  400. type: 'warning',
  401. draggable: true,
  402. cancelButtonClass: 'default-button',
  403. autofocus: false,
  404. })
  405. .then(() => {
  406. state.loading = true;
  407. orderAverage()
  408. .then(() => {
  409. state.loading = false;
  410. ElMessage.success('分配成功');
  411. handleQuery();
  412. })
  413. .catch(() => {
  414. state.loading = false;
  415. });
  416. })
  417. .catch(() => {});
  418. };
  419. const tableRef = ref<RefType>();
  420. const checkTable = ref<EmptyArrayType>([]);
  421. const gridRef = ref<RefType>();
  422. const selectAllChangeEvent = ({ checked }) => {
  423. if (gridRef.value) {
  424. const records = gridRef.value.getCheckboxRecords();
  425. checkTable.value = records;
  426. console.log(checked ? '所有勾选事件' : '所有取消事件', records);
  427. }
  428. };
  429. const selectChangeEvent = ({ checked }) => {
  430. if (gridRef.value) {
  431. const records = gridRef.value.getCheckboxRecords();
  432. checkTable.value = records;
  433. console.log(checked ? '勾选事件' : '取消事件', records);
  434. }
  435. };
  436. const isChecked = computed(() => {
  437. return !Boolean(checkTable.value.length);
  438. });
  439. onMounted(() => {
  440. queryList().then(() => {
  441. getBaseData();
  442. });
  443. });
  444. const handleEvent = debounce(() => {
  445. handleQuery();
  446. }, 1000);
  447. onActivated(() => {
  448. mittBus.on('clearCachePage', () => {
  449. //清除缓存
  450. handleEvent();
  451. });
  452. });
  453. onBeforeUnmount(() => {
  454. mittBus.off('clearCachePage');
  455. });
  456. </script>