index.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. <template>
  2. <div class="supervision-doc-number-manage">
  3. <div class="supervision-doc-content-container">
  4. <!-- 左侧分类树 -->
  5. <div class="category-tree">
  6. <el-input
  7. v-model="filterText"
  8. style="width: 80%"
  9. placeholder="输入关键字进行过滤"
  10. clearable
  11. prefix-icon="el-icon-search"
  12. ></el-input>
  13. <el-tree
  14. ref="typeTree"
  15. :data="categoryData"
  16. node-key="id"
  17. default-expand-all
  18. :props="{
  19. label: 'documentName',
  20. value: 'id',
  21. }"
  22. :expand-on-click-node="false"
  23. :filter-node-method="filterNode"
  24. @node-click="handleNodeClick"
  25. ></el-tree>
  26. </div>
  27. <!-- 右侧内容区域 -->
  28. <div class="right-content">
  29. <!-- 搜索区域 -->
  30. <div class="search-plane">
  31. <el-form :inline="true" size="small">
  32. <!-- 文号名称输入框 -->
  33. <el-form-item label="文号名称:">
  34. <el-input
  35. v-model="searchForm.whName"
  36. placeholder="请输入"
  37. clearable
  38. ></el-input>
  39. </el-form-item>
  40. <!-- 搜索和重置按钮 -->
  41. <el-form-item>
  42. <el-button
  43. icon="el-icon-search"
  44. type="primary"
  45. @click="handleSearch"
  46. >
  47. 搜索
  48. </el-button>
  49. <el-button
  50. plain
  51. type="primary"
  52. icon="el-icon-refresh"
  53. @click="handleReset"
  54. >
  55. 重置
  56. </el-button>
  57. </el-form-item>
  58. </el-form>
  59. </div>
  60. <!-- 操作按钮区域 -->
  61. <div class="operation-bar">
  62. <el-button
  63. plain
  64. type="success"
  65. icon="el-icon-circle-plus"
  66. @click="handleAdd"
  67. >
  68. 添加
  69. </el-button>
  70. <el-button
  71. plain
  72. type="danger"
  73. icon="el-icon-delete"
  74. :disabled="selectedRows.length === 0"
  75. @click="handleDelete"
  76. >
  77. 删除
  78. </el-button>
  79. </div>
  80. <!-- 数据表格 -->
  81. <CostAuditTable
  82. ref="costAuditTable"
  83. :table-data="tableData"
  84. :columns="columns"
  85. :show-selection="true"
  86. :show-index="true"
  87. :show-pagination="true"
  88. :show-action-column="true"
  89. :pagination="{
  90. currentPage: currentPage,
  91. pageSize: pageSize,
  92. total: total,
  93. }"
  94. @selection-change="handleSelectionChange"
  95. @pagination-change="handlePaginationChange"
  96. >
  97. <template #updateTime="{ row }">
  98. <div>
  99. {{ row.updateTime ? row.updateTime.split(' ')[0] : '' }}
  100. </div>
  101. <div>
  102. {{ row.updateTime ? row.updateTime.split(' ')[1] : '' }}
  103. </div>
  104. </template>
  105. </CostAuditTable>
  106. <!-- 添加/修改弹窗 -->
  107. <el-dialog
  108. :title="dialogTitle"
  109. :visible.sync="dialogVisible"
  110. width="65%"
  111. :close-on-click-modal="false"
  112. >
  113. <!-- 弹窗内容保持不变 -->
  114. <div class="dialog-content">
  115. <el-form
  116. ref="docForm"
  117. :model="formData"
  118. :rules="formRules"
  119. label-width="120px"
  120. size="small"
  121. :disabled="dialogVisible == '详情'"
  122. >
  123. <el-row :gutter="20">
  124. <el-col :span="12">
  125. <!-- 适用区域 -->
  126. <el-form-item label="适用区域:" prop="areaCode" required>
  127. <el-cascader
  128. v-model="formData.areaCode"
  129. style="width: 100%"
  130. :options="districtTree"
  131. :props="districtTreeCascaderProps"
  132. :show-all-levels="false"
  133. clearable
  134. ></el-cascader>
  135. </el-form-item>
  136. </el-col>
  137. <el-col :span="12">
  138. <el-form-item label="文号分类:" prop="whType" required>
  139. <el-select v-model="formData.whType" style="width: 100%">
  140. <el-option
  141. v-for="item in whTypeOptions"
  142. :key="item.id"
  143. :label="item.documentName"
  144. :value="item.id"
  145. ></el-option>
  146. </el-select>
  147. </el-form-item>
  148. </el-col>
  149. </el-row>
  150. <el-row :gutter="20">
  151. <el-col :span="12">
  152. <!-- 文号名称 -->
  153. <el-form-item label="文号名称:" prop="whName" required>
  154. <el-input
  155. v-model="formData.whName"
  156. v-pinyin-abbreviation="{
  157. target: 'formData.alias',
  158. }"
  159. placeholder="请输入"
  160. maxlength="50"
  161. show-word-limit
  162. ></el-input>
  163. </el-form-item>
  164. </el-col>
  165. <el-col :span="12">
  166. <!-- 文号别名 -->
  167. <el-form-item label="文号别名:" prop="alias" required>
  168. <el-input
  169. v-model="formData.alias"
  170. placeholder="请输入"
  171. ></el-input>
  172. </el-form-item>
  173. </el-col>
  174. </el-row>
  175. <el-row :gutter="20">
  176. <el-col :span="12">
  177. <el-form-item label="文号前缀:" prop="prefixText" required>
  178. <el-input
  179. v-model="formData.prefixText"
  180. placeholder="请输入文号前缀,例如:晋"
  181. ></el-input>
  182. </el-form-item>
  183. </el-col>
  184. <el-col :span="12">
  185. <el-form-item label="年号:" prop="year" required>
  186. <el-input
  187. v-model="formData.year"
  188. placeholder="请输入年号,例如:2025"
  189. ></el-input>
  190. </el-form-item>
  191. </el-col>
  192. </el-row>
  193. <el-row :gutter="20">
  194. <el-col :span="12">
  195. <el-form-item
  196. label="当前流水号值:"
  197. prop="currentValue"
  198. required
  199. >
  200. <el-input
  201. v-model="formData.currentValue"
  202. placeholder="请输入当前流水号值"
  203. ></el-input>
  204. </el-form-item>
  205. </el-col>
  206. <el-col :span="12">
  207. <!-- 文号规则 -->
  208. <el-form-item label="文号规则:" prop="rulePattern" required>
  209. <el-input
  210. v-model="formData.rulePattern"
  211. placeholder="请输入"
  212. show-word-limit
  213. ></el-input>
  214. 例如: {文号前缀}成审 {年份} {当前流水号值}号
  215. </el-form-item>
  216. </el-col>
  217. </el-row>
  218. <el-row :gutter="20">
  219. <el-col :span="12">
  220. <!-- : incremental-递增, daily-每天生成, monthly-每月生成, yearly-每年生成-->
  221. <el-form-item label="生成类型:" prop="generateType" required>
  222. <el-radio-group v-model="formData.generateType">
  223. <el-radio
  224. v-for="item in dictData['whGenerateType']"
  225. :key="item.id"
  226. :label="item.key"
  227. >
  228. {{ item.name }}
  229. </el-radio>
  230. </el-radio-group>
  231. </el-form-item>
  232. </el-col>
  233. <el-col :span="12">
  234. <!-- 文号长度 -->
  235. <el-form-item label="文号长度:" prop="serialLength">
  236. <el-input
  237. v-model.number="formData.serialLength"
  238. placeholder="请输入"
  239. type="number"
  240. min="1"
  241. max="20"
  242. ></el-input>
  243. </el-form-item>
  244. </el-col>
  245. </el-row>
  246. <el-row :gutter="20">
  247. <el-col :span="12">
  248. <!-- 初始值 -->
  249. <el-form-item label="初始值:" prop="initialValue">
  250. <el-input
  251. v-model.number="formData.initialValue"
  252. placeholder="请输入"
  253. type="number"
  254. min="0"
  255. show-word-limit
  256. ></el-input>
  257. </el-form-item>
  258. </el-col>
  259. </el-row>
  260. <!-- 步长 -->
  261. <!-- <el-row :gutter="20">
  262. <el-col :span="12">
  263. <el-form-item label="步长:" prop="step">
  264. <el-input
  265. v-model.number="formData.step"
  266. placeholder="请输入"
  267. type="number"
  268. min="1"
  269. ></el-input>
  270. </el-form-item>
  271. </el-col>
  272. <el-col :span="12">
  273. <div class="step-desc">
  274. <p>
  275. 这个增加值表示文号部分(NO)的增加值,如2015102700001,步长为1,则文号部分的增加值为00001。
  276. </p>
  277. </div>
  278. </el-col>
  279. </el-row> -->
  280. </el-form>
  281. </div>
  282. <div slot="footer" class="dialog-footer">
  283. <el-button type="primary" @click="handleSave">确认</el-button>
  284. <el-button @click="dialogVisible = false">取消</el-button>
  285. </div>
  286. </el-dialog>
  287. </div>
  288. </div>
  289. </div>
  290. </template>
  291. <script>
  292. import {
  293. getData,
  294. addEntity,
  295. updateEntity,
  296. deleteById,
  297. batchDelete,
  298. getDetail,
  299. } from '@/api/auditDocNoManage'
  300. import { getWhCateList } from '@/api/auditReviewDocManage.js'
  301. import { dictMixin, regionMixin } from '@/mixins/useDict'
  302. import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
  303. export default {
  304. name: 'SupervisionDocNumberManage',
  305. components: {
  306. CostAuditTable,
  307. },
  308. mixins: [dictMixin, regionMixin],
  309. data() {
  310. return {
  311. dictData: {
  312. whGenerateType: [],
  313. },
  314. filterText: '',
  315. isExpand: true,
  316. // 搜索表单数据
  317. searchForm: {
  318. whName: '',
  319. whType: '',
  320. },
  321. // 分类树数据 - 增加更多分类 使用字典
  322. categoryData: [
  323. {
  324. documentName: '文号分类',
  325. children: [],
  326. },
  327. ],
  328. // 表格数据
  329. tableData: [],
  330. // 当前页码
  331. currentPage: 1,
  332. // 每页显示条数
  333. pageSize: 50,
  334. // 总记录数
  335. total: 0,
  336. // 选中的行数据
  337. selectedRows: [],
  338. // 选中的分类
  339. selectedCategories: [],
  340. // 弹窗相关
  341. dialogVisible: false,
  342. dialogTitle: '',
  343. isEdit: false,
  344. // 详情弹窗相关
  345. detailVisible: false,
  346. detailData: {},
  347. // 表单数据 - 完整结构
  348. formData: {
  349. whName: '',
  350. alias: '',
  351. currentValue: '',
  352. rulePattern: '{prefixText}成审 {year} {currentValue}号',
  353. generateType: '',
  354. serialLength: 5,
  355. initialValue: 1,
  356. // step: 1,
  357. areaCode: [],
  358. whType: '',
  359. },
  360. // 表单验证规则
  361. formRules: {
  362. whName: [
  363. { required: true, message: '请输入文号名称', trigger: 'blur' },
  364. { max: 50, message: '文号名称不能超过50个字符', trigger: 'blur' },
  365. ],
  366. alias: [
  367. { required: true, message: '请输入文号别名', trigger: 'blur' },
  368. ],
  369. currentValue: [
  370. { required: true, message: '请输入当前流水号值', trigger: 'blur' },
  371. ],
  372. rulePattern: [
  373. { required: true, message: '请输入文号规则', trigger: 'blur' },
  374. ],
  375. generateType: [
  376. { required: true, message: '请选择生成类型', trigger: 'change' },
  377. ],
  378. serialLength: [
  379. { required: true, message: '请输入文号长度', trigger: 'blur' },
  380. ],
  381. initialValue: [
  382. { required: true, message: '请输入初始值', trigger: 'blur' },
  383. ],
  384. // step: [{ required: true, message: '请输入步长', trigger: 'blur' }],
  385. areaCode: [
  386. { required: true, message: '请选择适用区域', trigger: 'change' },
  387. ],
  388. },
  389. whTypeOptions: [],
  390. // 表格列配置
  391. columns: [
  392. {
  393. prop: 'whType',
  394. label: '文号分类',
  395. showOverflowTooltip: true,
  396. align: 'center',
  397. formatter: (row) => {
  398. let documentName =
  399. this.whTypeOptions.find((item) => item.id == row.whType)
  400. ?.documentName || '-'
  401. return documentName
  402. },
  403. },
  404. {
  405. prop: 'whName',
  406. label: '文号名称',
  407. showOverflowTooltip: true,
  408. align: 'center',
  409. },
  410. {
  411. prop: 'areaCode',
  412. label: '适用区域',
  413. showOverflowTooltip: true,
  414. align: 'center',
  415. formatter: (row) => this.regionNameMap[row.areaCode] || '-',
  416. },
  417. {
  418. prop: 'generateType',
  419. label: '生成类型',
  420. showOverflowTooltip: true,
  421. align: 'center',
  422. width: 120,
  423. formatter: (row) =>
  424. this.getDictName('whGenerateType', row.generateType),
  425. },
  426. {
  427. prop: 'createBy',
  428. label: '创建人',
  429. showOverflowTooltip: true,
  430. align: 'center',
  431. width: 120,
  432. },
  433. {
  434. prop: 'updateTime',
  435. label: '最后更新时间',
  436. showOverflowTooltip: true,
  437. align: 'center',
  438. width: 120,
  439. slotName: 'updateTime',
  440. },
  441. {
  442. prop: 'action',
  443. label: '操作',
  444. align: 'center',
  445. width: 200,
  446. actions: [
  447. {
  448. name: 'detail',
  449. label: '详情',
  450. type: 'text',
  451. size: 'mini',
  452. onClick: this.handleDetail,
  453. },
  454. {
  455. name: 'edit',
  456. label: '修改',
  457. type: 'text',
  458. size: 'mini',
  459. onClick: this.handleEdit,
  460. },
  461. {
  462. name: 'delete',
  463. label: '删除',
  464. type: 'text',
  465. size: 'mini',
  466. className: 'delete-btn',
  467. onClick: this.handleDeleteRow,
  468. },
  469. ],
  470. },
  471. ],
  472. }
  473. },
  474. watch: {
  475. // 添加对filterText的监听,当它变化时触发树的过滤
  476. filterText(val) {
  477. if (this.$refs.typeTree) {
  478. this.$refs.typeTree.filter(val)
  479. }
  480. },
  481. },
  482. mounted() {
  483. // 加载分类数据
  484. this.getCategoryData()
  485. // 页面加载时获取数据
  486. this.getTableData()
  487. },
  488. methods: {
  489. async getCategoryData() {
  490. try {
  491. const res = await getWhCateList()
  492. this.categoryData[0].children = res.value || []
  493. this.whTypeOptions = res.value
  494. } catch (error) {
  495. console.error('获取分类数据失败:', error)
  496. }
  497. },
  498. // 节点点击事件处理
  499. handleNodeClick(data) {
  500. // 如果点击的是子节点,直接使用其dictValue
  501. // 如果点击的是父节点,不进行筛选
  502. if (!data.children) {
  503. this.selectedCategories = [data.id]
  504. this.searchForm.whType = data.id
  505. // 重置分页并重新获取数据
  506. this.currentPage = 1
  507. this.getTableData()
  508. }
  509. },
  510. // 过滤树节点
  511. filterNode(value, data) {
  512. if (!value) return true
  513. const searchValue = value.toLowerCase()
  514. const dictLabelMatch =
  515. data.documentName &&
  516. data.documentName.toLowerCase().includes(searchValue)
  517. return dictLabelMatch
  518. },
  519. // 获取表格数据
  520. async getTableData() {
  521. try {
  522. const params = {
  523. pageSize: this.pageSize,
  524. page: this.currentPage,
  525. ...this.searchForm,
  526. }
  527. const response = await getData(params)
  528. this.tableData = response.value.records || []
  529. this.total = response.value.total || 0
  530. this.fetchRegionNames(this.tableData, 'areaCode')
  531. } catch (error) {
  532. console.error('获取数据失败:', error)
  533. }
  534. },
  535. // 搜索按钮点击事件
  536. handleSearch() {
  537. this.currentPage = 1
  538. this.getTableData()
  539. },
  540. // 重置按钮点击事件
  541. handleReset() {
  542. this.searchForm = {
  543. whName: '',
  544. }
  545. this.selectedCategories = []
  546. this.currentPage = 1
  547. this.getTableData()
  548. },
  549. // 添加按钮点击事件
  550. handleAdd() {
  551. this.isEdit = false
  552. this.dialogTitle = '添加文号'
  553. this.formData = {
  554. whName: '',
  555. alias: '',
  556. currentValue: '',
  557. rulePattern: '{prefixText}成审 {year} {currentValue}号',
  558. generateType: '',
  559. serialLength: 5,
  560. initialValue: 1,
  561. // step: 1,
  562. areaCode: [],
  563. whType: this.searchForm.whType,
  564. }
  565. // 清空表单验证
  566. this.$nextTick(() => {
  567. this.$refs.docForm.clearValidate()
  568. })
  569. this.dialogVisible = true
  570. },
  571. // 修改按钮点击事件
  572. handleEdit(row) {
  573. this.isEdit = true
  574. this.dialogTitle = '修改文号'
  575. getDetail({ id: row.id }).then((res) => {
  576. this.formData = {
  577. ...res.value,
  578. areaCode: [
  579. res.value.province,
  580. ...(row.areaLevel >= 1 ? [res.value.city] : []),
  581. ...(row.areaLevel === 2 ? [res.value.areaCode] : []),
  582. ].filter(Boolean),
  583. whType: Number(res.value.whType),
  584. rulePattern: '{prefixText}成审 {year} {currentValue}号',
  585. }
  586. // 清空表单验证
  587. this.$nextTick(() => {
  588. this.$refs.docForm.clearValidate()
  589. })
  590. this.dialogVisible = true
  591. })
  592. },
  593. // 保存按钮点击事件 - 添加错误提示
  594. async handleSave() {
  595. this.$refs.docForm.validate(async (valid) => {
  596. if (valid) {
  597. let data = {
  598. ...this.formData,
  599. areaCode:
  600. this.formData.areaCode[this.formData.areaCode.length - 1],
  601. areaLevel: this.formData.areaCode.length - 1,
  602. }
  603. try {
  604. if (this.isEdit) {
  605. // 处理修改逻辑
  606. await updateEntity(data)
  607. this.$message.success('修改成功')
  608. } else {
  609. // 处理添加逻辑
  610. await addEntity(data)
  611. this.$message.success('添加成功')
  612. }
  613. this.dialogVisible = false
  614. // 重新获取数据
  615. this.getTableData()
  616. } catch (error) {
  617. console.error('保存失败:', error)
  618. // this.$message.error('保存失败,请重试')
  619. }
  620. } else {
  621. this.$message.error('请填写表单数据')
  622. }
  623. })
  624. },
  625. // 删除按钮点击事件
  626. async handleDelete() {
  627. if (this.selectedRows.length === 0) {
  628. this.$message.warning('请先选择要删除的记录')
  629. return
  630. }
  631. this.$confirm('确定要删除选中的记录吗?', '提示', {
  632. confirmButtonText: '确定',
  633. cancelButtonText: '取消',
  634. type: 'warning',
  635. })
  636. .then(async () => {
  637. try {
  638. // 获取选中行的ID列表
  639. const ids = this.selectedRows.map((row) => row.id)
  640. // 调用批量删除API
  641. const response = await batchDelete(ids)
  642. this.$message.success('删除成功')
  643. // 重新获取数据
  644. this.getTableData()
  645. this.selectedRows = []
  646. } catch (error) {
  647. console.error('删除失败:', error)
  648. }
  649. })
  650. .catch(() => {
  651. this.$message.info('已取消删除')
  652. })
  653. },
  654. // 选中行变化事件
  655. handleSelectionChange(val) {
  656. this.selectedRows = val
  657. },
  658. // 分类树选中变化事件 - 用于筛选表格数据
  659. handleCategoryChange(checkedKeys, halfCheckedKeys) {
  660. this.selectedCategories = checkedKeys
  661. // 重置分页
  662. this.currentPage = 1
  663. // 重新获取数据
  664. this.getTableData()
  665. },
  666. // 详情按钮点击事件 - 打开详情弹窗
  667. handleDetail(row) {
  668. getDetail({ id: row.id }).then((res) => {
  669. this.formData = {
  670. ...res.value,
  671. areaCode: [
  672. res.value.province,
  673. ...(row.areaLevel >= 1 ? [res.value.city] : []),
  674. ...(row.areaLevel === 2 ? [res.value.areaCode] : []),
  675. ].filter(Boolean),
  676. whType: Number(res.value.whType),
  677. }
  678. // 清空表单验证
  679. this.$nextTick(() => {
  680. this.$refs.docForm.clearValidate()
  681. })
  682. this.dialogVisible = true
  683. this.dialogVisible = true
  684. this.dialogTitle = '详情'
  685. })
  686. },
  687. // 单个删除按钮点击事件
  688. async handleDeleteRow(row) {
  689. this.$confirm('确定要删除该记录吗?', '提示', {
  690. confirmButtonText: '确定',
  691. cancelButtonText: '取消',
  692. type: 'warning',
  693. })
  694. .then(async () => {
  695. try {
  696. // 调用单个删除API
  697. const response = await deleteById({ id: row.id })
  698. this.$message.success('删除成功')
  699. // 重新获取数据
  700. this.getTableData()
  701. } catch (error) {
  702. console.error('删除失败:', error)
  703. }
  704. })
  705. .catch(() => {
  706. this.$message.info('已取消删除')
  707. })
  708. },
  709. // 分页变化事件处理
  710. handlePaginationChange({ currentPage, pageSize }) {
  711. this.currentPage = currentPage
  712. this.pageSize = pageSize
  713. this.getTableData()
  714. },
  715. },
  716. }
  717. </script>
  718. <style scoped lang="scss">
  719. @import '@/styles/costAudit.scss';
  720. /* 内容容器 - 包含左侧分类树和右侧内容 */
  721. .supervision-doc-number-manage {
  722. width: 100%;
  723. padding: 20px;
  724. }
  725. .supervision-doc-content-container {
  726. display: flex;
  727. }
  728. /* 左侧分类树样式 */
  729. .category-tree {
  730. width: 240px;
  731. flex-shrink: 0;
  732. border: 1px solid #dbd8d8;
  733. padding: 10px;
  734. height: calc(100vh - 260px);
  735. overflow-y: auto;
  736. }
  737. /* 右侧内容区域 */
  738. .right-content {
  739. width: 80%;
  740. margin-left: 20px;
  741. /* 确保在小屏幕上有足够空间 */
  742. min-width: 0;
  743. }
  744. /* 弹窗内容区域样式 */
  745. .dialog-content {
  746. max-height: 500px;
  747. overflow-y: auto;
  748. }
  749. /* 详情弹窗描述项样式 */
  750. .el-descriptions__label {
  751. font-weight: bold;
  752. }
  753. </style>