DynamicTableDialog.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. <template>
  2. <el-dialog
  3. title="调查表填报"
  4. :visible.sync="dialogVisible"
  5. width="90%"
  6. :close-on-click-modal="false"
  7. :show-close="true"
  8. append-to-body
  9. :modal="false"
  10. @close="handleClose"
  11. >
  12. <!-- 操作按钮 -->
  13. <div class="action-buttons" style="margin-bottom: 20px">
  14. <el-button type="primary" :disabled="isViewMode" @click="handleAdd">
  15. <i class="el-icon-plus"></i>
  16. 新增
  17. </el-button>
  18. <el-button
  19. type="danger"
  20. :disabled="isViewMode || selectedRows.length === 0"
  21. @click="handleBatchDelete"
  22. >
  23. <i class="el-icon-delete"></i>
  24. 删除
  25. </el-button>
  26. </div>
  27. <!-- 数据表格 -->
  28. <el-table
  29. :data="paginatedTableData"
  30. border
  31. style="width: 100%"
  32. @selection-change="handleSelectionChange"
  33. >
  34. <!-- 复选框列 -->
  35. <el-table-column type="selection" width="55" align="center" />
  36. <!-- 序号列 -->
  37. <el-table-column prop="seq" label="序号" width="80" align="center">
  38. <template slot-scope="scope">
  39. {{ getRowIndex(scope.$index) }}
  40. </template>
  41. </el-table-column>
  42. <!-- 监审期间列 -->
  43. <el-table-column
  44. prop="auditPeriod"
  45. label="监审期间"
  46. min-width="150"
  47. align="center"
  48. >
  49. <template slot-scope="scope">
  50. {{ scope.row.auditPeriod }}
  51. </template>
  52. </el-table-column>
  53. <!-- 填报时间列 -->
  54. <el-table-column
  55. prop="fillTime"
  56. label="填报时间"
  57. width="180"
  58. align="center"
  59. >
  60. <template slot-scope="scope">
  61. {{ scope.row.fillTime }}
  62. </template>
  63. </el-table-column>
  64. <!-- 最后修改时间列 -->
  65. <el-table-column
  66. prop="lastModifyTime"
  67. label="最后修改时间"
  68. width="180"
  69. align="center"
  70. >
  71. <template slot-scope="scope">
  72. {{ scope.row.lastModifyTime }}
  73. </template>
  74. </el-table-column>
  75. <!-- 操作列 -->
  76. <el-table-column label="操作" width="200" align="center" fixed="right">
  77. <template slot-scope="scope">
  78. <el-button
  79. type="text"
  80. size="small"
  81. :disabled="isViewMode"
  82. @click="handleViewDetail(scope.row)"
  83. >
  84. 详情
  85. </el-button>
  86. <el-button
  87. type="text"
  88. size="small"
  89. :disabled="isViewMode"
  90. @click="handleEdit(scope.row)"
  91. >
  92. 编辑
  93. </el-button>
  94. <el-button
  95. type="text"
  96. size="small"
  97. :disabled="isViewMode"
  98. style="color: #f56c6c"
  99. @click="handleDelete(scope.row)"
  100. >
  101. 删除
  102. </el-button>
  103. </template>
  104. </el-table-column>
  105. </el-table>
  106. <!-- 分页 -->
  107. <el-pagination
  108. background
  109. layout="total, prev, pager, next, jumper"
  110. :current-page="pagination.currentPage"
  111. :page-size="pagination.pageSize"
  112. :total="pagination.total"
  113. style="margin-top: 20px; text-align: right"
  114. @current-change="handlePageChange"
  115. @size-change="handleSizeChange"
  116. />
  117. <!-- 详情/编辑弹窗 -->
  118. <el-dialog
  119. :title="detailDialogTitle"
  120. :visible.sync="detailDialogVisible"
  121. width="90%"
  122. :close-on-click-modal="false"
  123. append-to-body
  124. :modal="false"
  125. >
  126. <div>
  127. <!-- <div style="margin-bottom: 20px; padding: 10px; background: #f5f7fa">
  128. <p><strong>监审期间:</strong>{{ currentRow.auditPeriod }}</p>
  129. <p><strong>填报时间:</strong>{{ currentRow.fillTime }}</p>
  130. <p><strong>最后修改时间:</strong>{{ currentRow.lastModifyTime }}</p>
  131. </div> -->
  132. <!-- 固定资产表格 -->
  133. <fixed-assets-table
  134. ref="fixedAssetsTable"
  135. :table-items="localTableItems"
  136. :saved-data="currentRow ? currentRow.data || {} : {}"
  137. :is-view-mode="!isEditMode"
  138. />
  139. </div>
  140. <div slot="footer" class="dialog-footer">
  141. <el-button v-if="isEditMode" type="primary" @click="handleSaveDetail">
  142. 保存
  143. </el-button>
  144. <el-button @click="detailDialogVisible = false">关闭</el-button>
  145. </div>
  146. </el-dialog>
  147. <!-- <div slot="footer" class="dialog-footer">
  148. <el-button @click="handleCancel">取消</el-button>
  149. </div> -->
  150. </el-dialog>
  151. </template>
  152. <script>
  153. import { Message, MessageBox } from 'element-ui'
  154. import FixedAssetsTable from './FixedAssetsTable.vue'
  155. export default {
  156. name: 'DynamicTableDialog',
  157. components: {
  158. FixedAssetsTable,
  159. },
  160. props: {
  161. visible: {
  162. type: Boolean,
  163. default: false,
  164. },
  165. surveyData: {
  166. type: Object,
  167. default: () => ({}),
  168. },
  169. // 表格数据
  170. tableData: {
  171. type: Array,
  172. default: () => [],
  173. },
  174. // 表格项配置(用于详情/编辑时显示表单)
  175. tableItems: {
  176. type: Array,
  177. default: () => [],
  178. },
  179. // 是否查看模式
  180. isViewMode: {
  181. type: Boolean,
  182. default: false,
  183. },
  184. },
  185. data() {
  186. return {
  187. dialogVisible: false,
  188. // 表格数据
  189. localTableData: [],
  190. // 选中的行
  191. selectedRows: [],
  192. // 分页配置
  193. pagination: {
  194. currentPage: 1,
  195. pageSize: 10,
  196. total: 0,
  197. },
  198. // 详情/编辑弹窗
  199. detailDialogVisible: false,
  200. detailDialogTitle: '详情',
  201. currentRow: null,
  202. isEditMode: false,
  203. // 表格项配置(本地)
  204. localTableItems: [],
  205. }
  206. },
  207. computed: {
  208. // 分页后的表格数据
  209. paginatedTableData() {
  210. const start =
  211. (this.pagination.currentPage - 1) * this.pagination.pageSize
  212. const end = start + this.pagination.pageSize
  213. return this.localTableData.slice(start, end)
  214. },
  215. },
  216. watch: {
  217. visible: {
  218. handler(newVal) {
  219. this.dialogVisible = newVal
  220. if (newVal) {
  221. this.initTableData()
  222. this.initTableItems()
  223. }
  224. },
  225. immediate: true,
  226. },
  227. tableItems: {
  228. handler(newVal) {
  229. this.initTableItems()
  230. },
  231. deep: true,
  232. },
  233. dialogVisible(newVal) {
  234. if (!newVal) {
  235. this.$emit('update:visible', false)
  236. }
  237. },
  238. tableData: {
  239. handler(newVal) {
  240. this.initTableData()
  241. },
  242. deep: true,
  243. },
  244. },
  245. methods: {
  246. // 初始化表格项配置
  247. initTableItems() {
  248. // if (this.tableItems && this.tableItems.length > 0) {
  249. // this.localTableItems = [...this.tableItems]
  250. // } else {
  251. // // 使用假数据
  252. // this.localTableItems = this.getMockTableItems()
  253. // }
  254. this.localTableItems = this.getMockTableItems()
  255. },
  256. // 获取假数据表格项配置(用于测试)
  257. getMockTableItems() {
  258. return [
  259. {
  260. id: 'I',
  261. itemName: '房屋建筑物',
  262. isCategory: true,
  263. categorySeq: 'I',
  264. children: [
  265. {
  266. id: 'I-1',
  267. itemName: '办公用房',
  268. unit: '',
  269. originalValue: '',
  270. entryDate: '',
  271. depreciationPeriod: '',
  272. depreciationExpense: '',
  273. fundSource: '',
  274. remark: '',
  275. },
  276. {
  277. id: 'I-2',
  278. itemName: '教保用房',
  279. unit: '',
  280. originalValue: '',
  281. entryDate: '',
  282. depreciationPeriod: '',
  283. depreciationExpense: '',
  284. fundSource: '',
  285. remark: '',
  286. },
  287. {
  288. id: 'I-3',
  289. itemName: '幼儿宿舍用房',
  290. unit: '',
  291. originalValue: '',
  292. entryDate: '',
  293. depreciationPeriod: '',
  294. depreciationExpense: '',
  295. fundSource: '',
  296. remark: '',
  297. },
  298. {
  299. id: 'I-4',
  300. itemName: '其它',
  301. unit: '',
  302. originalValue: '',
  303. entryDate: '',
  304. depreciationPeriod: '',
  305. depreciationExpense: '',
  306. fundSource: '',
  307. remark: '',
  308. },
  309. ],
  310. },
  311. {
  312. id: 'II',
  313. itemName: '交通运输工具',
  314. isCategory: true,
  315. categorySeq: 'II',
  316. children: [
  317. {
  318. id: 'II-1',
  319. itemName: '车辆',
  320. unit: '',
  321. originalValue: '',
  322. entryDate: '',
  323. depreciationPeriod: '',
  324. depreciationExpense: '',
  325. fundSource: '',
  326. remark: '',
  327. },
  328. ],
  329. },
  330. {
  331. id: 'III',
  332. itemName: '教保专用设备',
  333. isCategory: true,
  334. categorySeq: 'III',
  335. children: [
  336. {
  337. id: 'III-1',
  338. itemName: '电教',
  339. unit: '',
  340. originalValue: '',
  341. entryDate: '',
  342. depreciationPeriod: '',
  343. depreciationExpense: '',
  344. fundSource: '',
  345. remark: '',
  346. },
  347. {
  348. id: 'III-2',
  349. itemName: '文体',
  350. unit: '',
  351. originalValue: '',
  352. entryDate: '',
  353. depreciationPeriod: '',
  354. depreciationExpense: '',
  355. fundSource: '',
  356. remark: '',
  357. },
  358. ],
  359. },
  360. {
  361. id: 'IV',
  362. itemName: '办公设备',
  363. isCategory: true,
  364. categorySeq: 'IV',
  365. children: [
  366. {
  367. id: 'IV-1',
  368. itemName: '电脑',
  369. unit: '',
  370. originalValue: '',
  371. entryDate: '',
  372. depreciationPeriod: '',
  373. depreciationExpense: '',
  374. fundSource: '',
  375. remark: '',
  376. },
  377. ],
  378. },
  379. {
  380. id: 'V',
  381. itemName: '其它固定资产',
  382. isCategory: true,
  383. categorySeq: 'V',
  384. children: [
  385. {
  386. id: 'V-1',
  387. itemName: '空调',
  388. unit: '',
  389. originalValue: '',
  390. entryDate: '',
  391. depreciationPeriod: '',
  392. depreciationExpense: '',
  393. fundSource: '',
  394. remark: '',
  395. },
  396. {
  397. id: 'V-2',
  398. itemName: '家电',
  399. unit: '',
  400. originalValue: '',
  401. entryDate: '',
  402. depreciationPeriod: '',
  403. depreciationExpense: '',
  404. fundSource: '',
  405. remark: '',
  406. },
  407. {
  408. id: 'V-3',
  409. itemName: '供水系统',
  410. unit: '',
  411. originalValue: '',
  412. entryDate: '',
  413. depreciationPeriod: '',
  414. depreciationExpense: '',
  415. fundSource: '',
  416. remark: '',
  417. },
  418. {
  419. id: 'V-4',
  420. itemName: '洗涤用具',
  421. unit: '',
  422. originalValue: '',
  423. entryDate: '',
  424. depreciationPeriod: '',
  425. depreciationExpense: '',
  426. fundSource: '',
  427. remark: '',
  428. },
  429. {
  430. id: 'V-5',
  431. itemName: '家具',
  432. unit: '',
  433. originalValue: '',
  434. entryDate: '',
  435. depreciationPeriod: '',
  436. depreciationExpense: '',
  437. fundSource: '',
  438. remark: '',
  439. },
  440. {
  441. id: 'V-6',
  442. itemName: '炊事用具',
  443. unit: '',
  444. originalValue: '',
  445. entryDate: '',
  446. depreciationPeriod: '',
  447. depreciationExpense: '',
  448. fundSource: '',
  449. remark: '',
  450. },
  451. {
  452. id: 'V-7',
  453. itemName: '其它',
  454. unit: '',
  455. originalValue: '',
  456. entryDate: '',
  457. depreciationPeriod: '',
  458. depreciationExpense: '',
  459. fundSource: '',
  460. remark: '',
  461. },
  462. ],
  463. },
  464. ]
  465. },
  466. // 初始化表格数据
  467. initTableData() {
  468. if (this.tableData && this.tableData.length > 0) {
  469. this.localTableData = [...this.tableData]
  470. } else {
  471. // 使用假数据
  472. this.localTableData = this.getMockTableData()
  473. }
  474. this.pagination.total = this.localTableData.length
  475. this.pagination.currentPage = 1
  476. },
  477. // 获取假数据(用于测试)
  478. getMockTableData() {
  479. const currentDate = new Date()
  480. const formatDateTime = (date) => {
  481. const year = date.getFullYear()
  482. const month = String(date.getMonth() + 1).padStart(2, '0')
  483. const day = String(date.getDate()).padStart(2, '0')
  484. const hours = String(date.getHours()).padStart(2, '0')
  485. const minutes = String(date.getMinutes()).padStart(2, '0')
  486. const seconds = String(date.getSeconds()).padStart(2, '0')
  487. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  488. }
  489. const data = []
  490. const currentYear = currentDate.getFullYear()
  491. // 生成25条假数据
  492. for (let i = 0; i < 25; i++) {
  493. const year = currentYear - i
  494. const fillDate = new Date(currentDate)
  495. fillDate.setDate(fillDate.getDate() - i)
  496. const modifyDate = new Date(fillDate)
  497. modifyDate.setHours(modifyDate.getHours() + 1)
  498. data.push({
  499. id: `row-${i + 1}`,
  500. seq: i + 1,
  501. auditPeriod: `${year}`,
  502. fillTime: formatDateTime(fillDate),
  503. lastModifyTime: formatDateTime(modifyDate),
  504. data: {}, // 存储具体的填报数据
  505. })
  506. }
  507. return data
  508. },
  509. // 获取行索引(考虑分页)
  510. getRowIndex(index) {
  511. return (
  512. (this.pagination.currentPage - 1) * this.pagination.pageSize +
  513. index +
  514. 1
  515. )
  516. },
  517. // 选择变化
  518. handleSelectionChange(selection) {
  519. this.selectedRows = selection
  520. },
  521. // 新增
  522. handleAdd() {
  523. const currentYear = new Date().getFullYear()
  524. const currentDate = new Date()
  525. const formatDateTime = (date) => {
  526. const year = date.getFullYear()
  527. const month = String(date.getMonth() + 1).padStart(2, '0')
  528. const day = String(date.getDate()).padStart(2, '0')
  529. const hours = String(date.getHours()).padStart(2, '0')
  530. const minutes = String(date.getMinutes()).padStart(2, '0')
  531. const seconds = String(date.getSeconds()).padStart(2, '0')
  532. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  533. }
  534. const newRow = {
  535. id: `row-${Date.now()}`,
  536. seq: this.localTableData.length + 1,
  537. auditPeriod: String(currentYear),
  538. fillTime: formatDateTime(currentDate),
  539. lastModifyTime: formatDateTime(currentDate),
  540. data: {},
  541. }
  542. this.localTableData.unshift(newRow)
  543. this.pagination.total = this.localTableData.length
  544. this.pagination.currentPage = 1
  545. // 打开编辑弹窗
  546. this.currentRow = newRow
  547. this.isEditMode = true
  548. this.detailDialogTitle = '编辑'
  549. this.detailDialogVisible = true
  550. Message.success('新增成功,请填写数据')
  551. },
  552. // 批量删除
  553. handleBatchDelete() {
  554. if (this.selectedRows.length === 0) {
  555. Message.warning('请选择要删除的记录')
  556. return
  557. }
  558. MessageBox.confirm(
  559. `确定要删除选中的 ${this.selectedRows.length} 条记录吗?`,
  560. '提示',
  561. {
  562. confirmButtonText: '确定',
  563. cancelButtonText: '取消',
  564. type: 'warning',
  565. }
  566. )
  567. .then(() => {
  568. const selectedIds = this.selectedRows.map((row) => row.id)
  569. this.localTableData = this.localTableData.filter(
  570. (row) => !selectedIds.includes(row.id)
  571. )
  572. this.pagination.total = this.localTableData.length
  573. // 如果当前页没有数据了,跳转到上一页
  574. if (
  575. this.paginatedTableData.length === 0 &&
  576. this.pagination.currentPage > 1
  577. ) {
  578. this.pagination.currentPage--
  579. }
  580. this.selectedRows = []
  581. Message.success('删除成功')
  582. })
  583. .catch(() => {})
  584. },
  585. // 查看详情
  586. handleViewDetail(row) {
  587. this.currentRow = { ...row }
  588. this.isEditMode = false
  589. this.detailDialogTitle = '详情'
  590. this.detailDialogVisible = true
  591. },
  592. // 编辑
  593. handleEdit(row) {
  594. this.currentRow = { ...row }
  595. this.isEditMode = true
  596. this.detailDialogTitle = '编辑'
  597. this.detailDialogVisible = true
  598. },
  599. // 保存详情
  600. handleSaveDetail() {
  601. // 验证表格数据
  602. if (this.$refs.fixedAssetsTable) {
  603. const isValid = this.$refs.fixedAssetsTable.validate()
  604. if (!isValid) {
  605. const errors = this.$refs.fixedAssetsTable.validationErrors
  606. Message.error('数据验证失败:\n' + errors.join('\n'))
  607. return
  608. }
  609. // 获取表格数据
  610. const tableData = this.$refs.fixedAssetsTable.getTableData()
  611. if (this.currentRow) {
  612. // 更新当前行的数据
  613. this.currentRow.data = tableData
  614. this.currentRow.lastModifyTime = this.formatDateTime(new Date())
  615. // 更新本地表格数据
  616. const index = this.localTableData.findIndex(
  617. (item) => item.id === this.currentRow.id
  618. )
  619. if (index > -1) {
  620. this.$set(this.localTableData, index, { ...this.currentRow })
  621. }
  622. Message.success('保存成功')
  623. this.detailDialogVisible = false
  624. }
  625. }
  626. },
  627. // 删除单条记录
  628. handleDelete(row) {
  629. MessageBox.confirm('确定要删除这条记录吗?', '提示', {
  630. confirmButtonText: '确定',
  631. cancelButtonText: '取消',
  632. type: 'warning',
  633. })
  634. .then(() => {
  635. const index = this.localTableData.findIndex(
  636. (item) => item.id === row.id
  637. )
  638. if (index > -1) {
  639. this.localTableData.splice(index, 1)
  640. this.pagination.total = this.localTableData.length
  641. // 如果当前页没有数据了,跳转到上一页
  642. if (
  643. this.paginatedTableData.length === 0 &&
  644. this.pagination.currentPage > 1
  645. ) {
  646. this.pagination.currentPage--
  647. }
  648. Message.success('删除成功')
  649. }
  650. })
  651. .catch(() => {})
  652. },
  653. // 详情/编辑保存
  654. handleDetailSave(saveData) {
  655. if (this.currentRow) {
  656. // 更新当前行的数据
  657. this.currentRow.data = saveData
  658. this.currentRow.lastModifyTime = this.formatDateTime(new Date())
  659. // 更新本地表格数据
  660. const index = this.localTableData.findIndex(
  661. (item) => item.id === this.currentRow.id
  662. )
  663. if (index > -1) {
  664. this.$set(this.localTableData, index, { ...this.currentRow })
  665. }
  666. Message.success('保存成功')
  667. this.detailDialogVisible = false
  668. }
  669. },
  670. // 格式化日期时间
  671. formatDateTime(date) {
  672. const year = date.getFullYear()
  673. const month = String(date.getMonth() + 1).padStart(2, '0')
  674. const day = String(date.getDate()).padStart(2, '0')
  675. const hours = String(date.getHours()).padStart(2, '0')
  676. const minutes = String(date.getMinutes()).padStart(2, '0')
  677. const seconds = String(date.getSeconds()).padStart(2, '0')
  678. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  679. },
  680. // 分页变化
  681. handlePageChange(page) {
  682. this.pagination.currentPage = page
  683. },
  684. // 每页条数变化
  685. handleSizeChange(size) {
  686. this.pagination.pageSize = size
  687. this.pagination.currentPage = 1
  688. },
  689. // 关闭弹窗
  690. handleClose() {
  691. this.dialogVisible = false
  692. this.$emit('update:visible', false)
  693. },
  694. // 取消
  695. handleCancel() {
  696. this.handleClose()
  697. },
  698. },
  699. }
  700. </script>
  701. <style scoped lang="scss">
  702. .action-buttons {
  703. margin-bottom: 20px;
  704. .el-button {
  705. margin-right: 10px;
  706. }
  707. }
  708. .dialog-footer {
  709. text-align: center;
  710. margin-top: 20px;
  711. .el-button {
  712. margin: 0 10px;
  713. }
  714. }
  715. ::v-deep .el-dialog__header {
  716. padding: 20px 20px 10px;
  717. .el-dialog__title {
  718. font-size: 18px;
  719. font-weight: 600;
  720. color: #303133;
  721. }
  722. }
  723. // 操作按钮样式
  724. ::v-deep .el-table {
  725. .el-button--text {
  726. padding: 0 5px;
  727. }
  728. }
  729. </style>