index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. <template>
  2. <div class="plan-index-container layout-padding">
  3. <div class="layout-padding-auto layout-padding-view pd20">
  4. <splitpanes class="h100" :horizontal="horizontal">
  5. <pane min-size="16" max-size="25" size="16">
  6. <el-scrollbar ref="scrollBarRef" noresiz>
  7. <el-skeleton :loading="state.typeLoading" animated :rows="10">
  8. <template #default>
  9. <v-tree-search
  10. :data="state.typeOptions"
  11. titleField="name"
  12. keyField="id"
  13. selectable
  14. searchPlaceholder="预案分类名称"
  15. @select="selectType"
  16. @unselect="unSelectType"
  17. ref="treeSearchRef"
  18. >
  19. <template #node="{ node }">
  20. <span>{{ node.name }}</span>
  21. </template>
  22. </v-tree-search>
  23. </template>
  24. </el-skeleton>
  25. </el-scrollbar>
  26. </pane>
  27. <pane class="h100" style="display: flex; flex: 1; flex-direction: column">
  28. <el-tabs v-model="state.queryParams.Status" @tab-change="handleClick" v-loading="state.loading">
  29. <el-tab-pane :label="v.value" :name="v.key" v-for="(v, i) in state.statusOptions" :key="i" :disabled="state.tableLoading"></el-tab-pane>
  30. </el-tabs>
  31. <el-form :model="state.queryParams" ref="ruleFormRef" inline @submit.native.prevent>
  32. <el-form-item label="标题" prop="Title">
  33. <el-input v-model="state.queryParams.Title" placeholder="标题" clearable @keyup.enter="handleQuery" class="keyword-input" />
  34. </el-form-item>
  35. <el-form-item label="创建时间" prop="cjTime">
  36. <el-date-picker
  37. v-model="state.queryParams.cjTime"
  38. type="datetimerange"
  39. unlink-panels
  40. range-separator="至"
  41. start-placeholder="开始时间"
  42. end-placeholder="结束时间"
  43. :shortcuts="shortcuts"
  44. @change="handleQuery"
  45. value-format="YYYY-MM-DD[T]HH:mm:ss"
  46. :default-time="defaultTimeStartEnd"
  47. />
  48. </el-form-item>
  49. <el-form-item>
  50. <el-button type="primary" @click="handleQuery" :loading="state.tableLoading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
  51. <el-button @click="drawer = true" class="default-button"> <SvgIcon name="ele-Search" class="mr5" />更多查询</el-button>
  52. </el-form-item>
  53. </el-form>
  54. <vxe-toolbar
  55. ref="toolbarRef"
  56. :loading="state.tableLoading"
  57. custom
  58. :refresh="{
  59. queryMethod: handleQuery,
  60. }"
  61. :tools="[{ toolRender: { name: 'exportCurrent' } }, { toolRender: { name: 'exportAll' } }]"
  62. >
  63. <template #buttons>
  64. <el-button type="primary" @click="onAdd" v-auth="'plan:index:add'" :loading="state.tableLoading">
  65. <SvgIcon name="ele-Plus" class="mr5" />创建预案
  66. </el-button>
  67. <el-dropdown v-auth="'plan:index:export:detail'" @command="onExportDetail" class="ml12 mr12" :disabled="isChecked">
  68. <el-button type="primary" :disabled="isChecked" :loading="state.tableLoading">
  69. 导出预案详情<SvgIcon name="ele-ArrowDown" class="ml3" />
  70. </el-button>
  71. <template #dropdown>
  72. <el-dropdown-menu>
  73. <el-dropdown-item :command="item.key" v-for="item in exportType" :key="item.key">{{ item.value }}</el-dropdown-item>
  74. </el-dropdown-menu>
  75. </template>
  76. </el-dropdown>
  77. </template>
  78. </vxe-toolbar>
  79. <div style="overflow: hidden; width: 100%; height: 100%; flex: 1">
  80. <vxe-table
  81. border
  82. :loading="state.tableLoading"
  83. :data="state.tableData"
  84. :column-config="{ resizable: true }"
  85. :row-config="{ isCurrent: true, isHover: true, height: 30, useKey: true }"
  86. ref="tableRef"
  87. height="auto"
  88. auto-resize
  89. show-overflow
  90. :print-config="{}"
  91. :scrollY="{ enabled: true, gt: 100 }"
  92. id="planManage"
  93. :custom-config="{ storage: true }"
  94. showHeaderOverflow
  95. :params="{ exportMethod: getPlanExport, exportParams: requestParams }"
  96. @checkbox-all="selectAllChangeEvent"
  97. @checkbox-change="selectChangeEvent"
  98. >
  99. <vxe-column type="checkbox" width="50" align="center"></vxe-column>
  100. <vxe-column field="title" title="标题" min-width="200">
  101. <template #default="{ row }">
  102. <el-button link type="primary" @click="onPreview(row)">{{ row.title }}</el-button>
  103. </template>
  104. </vxe-column>
  105. <vxe-column field="planTypeText" title="预案分类" width="150"></vxe-column>
  106. <vxe-column field="statusName" title="预案状态" width="110"></vxe-column>
  107. <!-- <vxe-column field="isPublic" title="是否公开" width="100">
  108. <template #default="{ row }">
  109. {{ row.isPublic ? '是' : '否' }}
  110. </template>
  111. </vxe-column>-->
  112. <vxe-column field="pageView" title="阅读次数" width="100"></vxe-column>
  113. <vxe-column field="onShelfTime" title="上架时间" width="160">
  114. <template #default="{ row }">
  115. {{ formatDate(row.onShelfTime, 'YYYY-mm-dd HH:MM:SS') }}
  116. </template>
  117. </vxe-column>
  118. <vxe-column field="offShelfTime" title="下架时间" width="160">
  119. <template #default="{ row }">
  120. {{ formatDate(row.offShelfTime, 'YYYY-mm-dd HH:MM:SS') }}
  121. </template>
  122. </vxe-column>
  123. <vxe-column field="expiredTime" title="到期时间" width="160">
  124. <template #default="{ row }">
  125. {{ formatDate(row.expiredTime, 'YYYY-mm-dd HH:MM:SS') }}
  126. </template>
  127. </vxe-column>
  128. <vxe-column field="creatorName" title="创建人" width="120"></vxe-column>
  129. <vxe-column field="creatorOrgName" title="创建部门" width="150"></vxe-column>
  130. <vxe-column field="creationTime" title="创建时间" width="160">
  131. <template #default="{ row }">
  132. {{ formatDate(row.creationTime, 'YYYY-mm-dd HH:MM:SS') }}
  133. </template>
  134. </vxe-column>
  135. <vxe-column field="examinMan.name" title="审批人" width="120"></vxe-column>
  136. <vxe-column field="examinTime" title="审批时间" width="160">
  137. <template #default="{ row }">
  138. {{ formatDate(row.examinTime, 'YYYY-mm-dd HH:MM:SS') }}
  139. </template>
  140. </vxe-column>
  141. <vxe-column field="examinOpinion" title="审批意见" width="200"></vxe-column>
  142. <vxe-column title="操作" fixed="right" width="150" align="center" :show-overflow="false">
  143. <template #default="{ row }">
  144. <el-button link type="primary" @click="onEdit(row)" title="编辑" v-auth="'plan:index:edit'" v-if="[4, 7, 5].includes(row.status)">
  145. 编辑
  146. </el-button>
  147. <el-button
  148. link
  149. type="danger"
  150. @click="onRowDel(row)"
  151. title="删除"
  152. v-auth="'plan:index:delete'"
  153. v-if="[4, 7, 5].includes(row.status)"
  154. >
  155. 删除
  156. </el-button>
  157. <el-button
  158. link
  159. type="primary"
  160. @click="offShelfFn(row)"
  161. title="下架"
  162. v-auth="'plan:index:undercarriage'"
  163. v-if="[3].includes(row.status)"
  164. >
  165. 下架
  166. </el-button>
  167. <el-button
  168. link
  169. type="primary"
  170. @click="groundingFn(row)"
  171. title="上架"
  172. v-auth="'plan:index:grounding'"
  173. v-if="[4].includes(row.status)"
  174. >
  175. 上架
  176. </el-button>
  177. <el-button link type="primary" @click="onAudit(row)" title="审批" v-if="[1].includes(row.status)" v-auth="'plan:index:audit'">
  178. 审批
  179. </el-button>
  180. </template>
  181. </vxe-column>
  182. </vxe-table>
  183. </div>
  184. <pagination
  185. @pagination="queryList"
  186. :total="state.total"
  187. v-model:current-page="state.queryParams.PageIndex"
  188. v-model:page-size="state.queryParams.PageSize"
  189. :disabled="state.tableLoading"
  190. />
  191. </pane>
  192. </splitpanes>
  193. </div>
  194. <!-- 更多查询 -->
  195. <el-drawer v-model="drawer" title="更多查询" size="500px">
  196. <el-form :model="state.queryParams" ref="drawerRuleFormRef" @submit.native.prevent label-width="100px">
  197. <el-form-item label="上架时间" prop="sjTime">
  198. <el-date-picker
  199. v-model="state.queryParams.sjTime"
  200. type="datetimerange"
  201. unlink-panels
  202. range-separator="至"
  203. start-placeholder="开始时间"
  204. end-placeholder="结束时间"
  205. :shortcuts="shortcuts"
  206. @change="handleQuery"
  207. value-format="YYYY-MM-DD[T]HH:mm:ss"
  208. :default-time="defaultTimeStartEnd"
  209. />
  210. </el-form-item>
  211. <el-form-item label="下架时间" prop="xjTime">
  212. <el-date-picker
  213. v-model="state.queryParams.xjTime"
  214. type="datetimerange"
  215. unlink-panels
  216. range-separator="至"
  217. start-placeholder="开始时间"
  218. end-placeholder="结束时间"
  219. :shortcuts="shortcuts"
  220. @change="handleQuery"
  221. value-format="YYYY-MM-DD[T]HH:mm:ss"
  222. :default-time="defaultTimeStartEnd"
  223. />
  224. </el-form-item>
  225. <el-form-item label="更新时间" prop="gxTime">
  226. <el-date-picker
  227. v-model="state.queryParams.gxTime"
  228. type="datetimerange"
  229. unlink-panels
  230. range-separator="至"
  231. start-placeholder="开始时间"
  232. end-placeholder="结束时间"
  233. :shortcuts="shortcuts"
  234. @change="handleQuery"
  235. value-format="YYYY-MM-DD[T]HH:mm:ss"
  236. :default-time="defaultTimeStartEnd"
  237. />
  238. </el-form-item>
  239. </el-form>
  240. <template #footer>
  241. <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
  242. <el-button @click="resetQuery(drawerRuleFormRef)" class="default-button"> <SvgIcon name="ele-Refresh" class="mr5" />重置 </el-button>
  243. </template>
  244. </el-drawer>
  245. <!-- 流程审批 -->
  246. <process-audit ref="processAuditRef" @orderProcessSuccess="queryList" />
  247. <!-- 审核记录 -->
  248. <audit-progress ref="auditProgressRef" />
  249. <!-- 批量审批 -->
  250. <to-end ref="toEndRef" @updateList="queryList" />
  251. <!-- 删除或者更新提交审核 -->
  252. <plan-audit ref="planAuditRef" @updateList="handleQuery" />
  253. <!-- 删除或者更新提交审核 -->
  254. <edit-submit-audit ref="editSubmitAuditRef" @updateList="handleQuery" />
  255. </div>
  256. </template>
  257. <script lang="tsx" setup name="planManage">
  258. import { ref, reactive, onMounted, defineAsyncComponent, computed } from 'vue';
  259. import { ElMessageBox, ElMessage } from 'element-plus';
  260. import { useRouter } from 'vue-router';
  261. import type { FormInstance } from 'element-plus';
  262. import { formatDate } from '@/utils/formatTime';
  263. import { Splitpanes, Pane } from 'splitpanes';
  264. import 'splitpanes/dist/splitpanes.css';
  265. import Other from '@/utils/other';
  266. import { downloadFileByStream } from '@/utils/tools';
  267. import { VxeUI } from 'vxe-pc-ui';
  268. import { defaultTimeStartEnd, shortcuts } from '@/utils/constants';
  269. import { getPlanBaseData, getPlanExport, getPlanList, planDetailExport, planOffShelf, planOnShelf } from '@/api/plan';
  270. import { VTreeSearch } from '@wsfe/vue-tree';
  271. import { planTreeList } from '@/api/plan/type';
  272. // 引入组件
  273. const ProcessAudit = defineAsyncComponent(() => import('@/components/ProcessAudit/index.vue')); // 流程审批
  274. const AuditProgress = defineAsyncComponent(() => import('@/views/knowledge/components/Audit-progress.vue')); // 编写规范
  275. const ToEnd = defineAsyncComponent(() => import('@/views/knowledge/index/components/To-end.vue')); // 批量审批
  276. const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
  277. const PlanAudit = defineAsyncComponent(() => import('@/views/plan/index/components/Plan-audit.vue')); // 审批新增 修改 删除
  278. const EditSubmitAudit = defineAsyncComponent(() => import('@/views/plan/index/components/Edit-submit-audit.vue')); // 删除或者更新 提交审核
  279. const router = useRouter(); //路由
  280. const horizontal = ref(false);
  281. // 定义变量内容
  282. const state = reactive<any>({
  283. queryParams: {
  284. PageIndex: 1, //页码
  285. PageSize: 20, //每页条数
  286. Keyword: null, //关键字
  287. Status: 3, //状态 默认已上架
  288. Title: null, //标题
  289. Attribution: '中心预案库',
  290. sjTime: [], // 上架时间
  291. StartOnShelfTime: null,
  292. EndOnShelfTime: null,
  293. xjTime: [], // 下架时间
  294. StartOffShelfTime: null,
  295. EndOffShelfTime: null,
  296. cjTime: [], // 创建时间
  297. CreationStartTime: null,
  298. CreationEndTime: null,
  299. gxTime: [], // 更新时间
  300. StartUpdateTime: null,
  301. EndUpdateTime: null,
  302. },
  303. activeName: '1', //tab切换 默认预案分类
  304. tableData: [], //表格数据
  305. total: 0, //总条数
  306. loading: false, //表格loading
  307. tableLoading: false, //表格loading
  308. typeOptions: [], // 类型数据
  309. statusOptions: [], //状态数据
  310. typeLoading: false, // 类型loading
  311. });
  312. // 切换tab 查询列表
  313. const handleClick = () => {
  314. state.queryParams.PageIndex = 1;
  315. queryList();
  316. };
  317. /** 搜索按钮操作 节流操作 */
  318. const handleQuery = () => {
  319. state.queryParams.PageIndex = 1;
  320. queryList();
  321. };
  322. // 获取案例分类
  323. const getPlanType = async () => {
  324. state.typeLoading = true;
  325. try {
  326. const { result } = await planTreeList({ IsEnable: true });
  327. state.typeOptions = result ?? [];
  328. state.typeLoading = false;
  329. } catch (error) {
  330. state.typeLoading = false;
  331. }
  332. };
  333. const exportType = ref<EmptyArrayType>([]);
  334. const getBaseDataFn = async () => {
  335. try {
  336. const { result } = await getPlanBaseData();
  337. state.statusOptions = result?.tabStatusName ?? []; // 列表状态
  338. exportType.value = result?.fileType ?? []; // 导出文件类型
  339. state.tabNewDraftsNames = result?.tabNewDraftsNames.map((item: any) => {
  340. return {
  341. label: item.value,
  342. value: item.key,
  343. };
  344. });
  345. state.tabAuditingNames = result?.tabAuditingNames.map((item: any) => {
  346. return {
  347. label: item.value,
  348. value: item.key,
  349. };
  350. });
  351. state.loading = false;
  352. } catch (error) {
  353. state.loading = false;
  354. }
  355. };
  356. /** 获取列表 */
  357. const requestParams = ref<EmptyObjectType>({});
  358. const queryList = () => {
  359. state.tableLoading = true;
  360. requestParams.value = Other.deepClone(state.queryParams);
  361. requestParams.value.StartOnShelfTime = state.queryParams.sjTime === null ? null : state.queryParams.sjTime[0]; // 上架时间
  362. requestParams.value.EndOnShelfTime = state.queryParams.sjTime === null ? null : state.queryParams.sjTime[1];
  363. Reflect.deleteProperty(requestParams.value, 'sjTime'); // 删除无用的参数
  364. requestParams.value.StartOffShelfTime = state.queryParams.xjTime === null ? null : state.queryParams.xjTime[0]; // 受理时间
  365. requestParams.value.EndOffShelfTime = state.queryParams.xjTime === null ? null : state.queryParams.xjTime[1]; //下架时间
  366. Reflect.deleteProperty(requestParams.value, 'xjTime'); // 删除无用的参数
  367. requestParams.value.CreationStartTime = state.queryParams.cjTime === null ? null : state.queryParams.cjTime[0]; // 创建时间
  368. requestParams.value.CreationEndTime = state.queryParams.cjTime === null ? null : state.queryParams.cjTime[1];
  369. Reflect.deleteProperty(requestParams.value, 'cjTime'); // 删除无用的参数
  370. requestParams.value.StartUpdateTime = state.queryParams.gxTime === null ? null : state.queryParams.gxTime[0]; // 更新时间
  371. requestParams.value.EndUpdateTime = state.queryParams.gxTime === null ? null : state.queryParams.gxTime[1];
  372. Reflect.deleteProperty(requestParams.value, 'gxTime'); // 删除无用的参数
  373. getPlanList(requestParams.value)
  374. .then((response: any) => {
  375. state.tableData = response?.result.items ?? [];
  376. state.total = response?.result.total;
  377. state.tableLoading = false;
  378. })
  379. .catch(() => {
  380. state.tableLoading = false;
  381. });
  382. };
  383. // 选择分类
  384. const selectType = (data: any) => {
  385. state.queryParams.PlanTypeID = data.id;
  386. handleQuery();
  387. };
  388. // 取消选择
  389. const unSelectType = () => {
  390. state.queryParams.PlanTypeID = null;
  391. handleQuery();
  392. };
  393. /** 重置按钮操作 */
  394. const drawerRuleFormRef = ref();
  395. const ruleFormRef = ref<RefType>(); // 表单ref
  396. const drawer = ref(false);
  397. const treeSearchRef = ref<RefType>();
  398. const resetQuery = (formEl: FormInstance | undefined) => {
  399. if (!formEl) return;
  400. formEl.resetFields();
  401. ruleFormRef.value?.resetFields();
  402. treeSearchRef.value.setSelected(state.queryParams.OrgCode, false); // 清空选择
  403. treeSearchRef.value.clearKeyword(); // 清空搜索关键词
  404. treeSearchRef.value.search(); // 搜索
  405. resetNode();
  406. setTimeout(() => {
  407. treeSearchRef.value.setExpandAll(false); // 默认全部收起
  408. }, 300);
  409. };
  410. // 重置选中的节点
  411. const resetNode = () => {
  412. state.queryParams.PlanTypeID = null;
  413. handleQuery();
  414. };
  415. // 新增预案
  416. const onAdd = () => {
  417. router.push({
  418. name: 'planEdit',
  419. params: {
  420. tagsViewName: '新增预案',
  421. },
  422. });
  423. };
  424. // 修改
  425. const onEdit = (row: any) => {
  426. router.push({
  427. name: 'planEdit',
  428. params: {
  429. id: row.id,
  430. tagsViewName: '编辑预案',
  431. },
  432. });
  433. };
  434. // 预览
  435. const onPreview = (row: any) => {
  436. router.push({
  437. name: 'planPreview',
  438. params: {
  439. id: row.id,
  440. tagsViewName: row.title,
  441. },
  442. });
  443. };
  444. // 审批
  445. const planAuditRef = ref<RefType>();
  446. const onAudit = (row: any) => {
  447. planAuditRef.value.openDialog(row);
  448. };
  449. // 下架
  450. const offShelfFn = (row: any) => {
  451. ElMessageBox.confirm(`是否确定要下架【${row.title}】?,预案下架后,将不会被检索到!`, '提示', {
  452. confirmButtonText: '确认',
  453. cancelButtonText: '取消',
  454. type: 'warning',
  455. draggable: true,
  456. cancelButtonClass: 'default-button',
  457. })
  458. .then(() => {
  459. planOffShelf(row.id).then(() => {
  460. ElMessage.success('下架成功');
  461. handleQuery();
  462. });
  463. })
  464. .catch(() => {});
  465. };
  466. // 上架
  467. const groundingFn = (row: any) => {
  468. ElMessageBox.confirm(`是否确定要上架【${row.title}】?`, '提示', {
  469. confirmButtonText: '确认',
  470. cancelButtonText: '取消',
  471. type: 'warning',
  472. draggable: true,
  473. cancelButtonClass: 'default-button',
  474. })
  475. .then(() => {
  476. planOnShelf(row.id).then(() => {
  477. ElMessage.success('上架成功');
  478. handleQuery();
  479. });
  480. })
  481. .catch(() => {});
  482. };
  483. // 删除预案
  484. const editSubmitAuditRef = ref<RefType>();
  485. const onRowDel = (row: any) => {
  486. editSubmitAuditRef.value.openDialog(row, 'delete');
  487. };
  488. // 导出详情
  489. const onExportDetail = (command: string | number | object) => {
  490. state.loading = true;
  491. const ids = checkTable.value.map((item: any) => item.id);
  492. VxeUI.modal.message({
  493. content: `导出中,请稍等`,
  494. status: 'loading',
  495. id: 'exportDetail',
  496. duration: -1,
  497. });
  498. planDetailExport({ ids, fileType: command })
  499. .then((res) => {
  500. downloadFileByStream(res);
  501. state.loading = false;
  502. VxeUI.modal.close('exportDetail');
  503. VxeUI.modal.message({
  504. content: `导出成功`,
  505. status: 'success',
  506. });
  507. })
  508. .catch((e) => {
  509. console.log(e,'导出错误')
  510. state.loading = false;
  511. VxeUI.modal.close('exportDetail');
  512. VxeUI.modal.message({
  513. content: `导出失败`,
  514. status: 'error',
  515. });
  516. });
  517. };
  518. const tableRef = ref<RefType>();
  519. const checkTable = ref<EmptyArrayType>([]);
  520. const selectAllChangeEvent = ({ checked }) => {
  521. if (tableRef.value) {
  522. const records = tableRef.value.getCheckboxRecords();
  523. checkTable.value = records;
  524. console.log(checked ? '所有勾选事件' : '所有取消事件', records);
  525. }
  526. };
  527. const selectChangeEvent = ({ checked }) => {
  528. if (tableRef.value) {
  529. const records = tableRef.value.getCheckboxRecords();
  530. checkTable.value = records;
  531. console.log(checked ? '勾选事件' : '取消事件', records);
  532. }
  533. };
  534. const isChecked = computed(() => {
  535. return !Boolean(checkTable.value.length);
  536. });
  537. const toolbarRef = ref<RefType>();
  538. onMounted(() => {
  539. queryList();
  540. if (tableRef.value && toolbarRef.value) {
  541. tableRef.value.connect(toolbarRef.value);
  542. }
  543. getBaseDataFn();
  544. getPlanType();
  545. });
  546. </script>