index.vue 19 KB

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