index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. <template>
  2. <div class="p-4">
  3. <div class="mb-6 p-4 bg-white rounded-lg shadow-sm flex flex-wrap items-center gap-4">
  4. <el-form :inline="true" :model="searchForm" class="flex-grow flex flex-wrap">
  5. <el-form-item>
  6. <span slot="label" class="font-size-4.5">企业名称:</span>
  7. <el-input
  8. v-model="searchForm.enterpriseName"
  9. style="width: 240px"
  10. size="default"
  11. placeholder="请输入企业名称"
  12. @clear="handleSearch"
  13. />
  14. </el-form-item>
  15. <el-form-item>
  16. <span slot="label" class="font-size-4.5">信用代码:</span>
  17. <el-input
  18. v-model="searchForm.unifiedSocialCreditCode"
  19. style="width: 240px"
  20. size="default"
  21. placeholder="输入统一社会信用代码"
  22. clearable
  23. />
  24. </el-form-item>
  25. <el-form-item>
  26. <span slot="label" class="font-size-4.5">户号信息:</span>
  27. <el-input
  28. v-model="searchForm.accountNumber"
  29. style="width: 240px"
  30. size="default"
  31. placeholder="输入户号信息"
  32. clearable
  33. />
  34. </el-form-item>
  35. <el-form-item>
  36. <span slot="label" class="font-size-4.5">年度:</span>
  37. <el-select
  38. v-model="searchForm.year"
  39. style="width: 240px"
  40. size="default"
  41. placeholder="请选择年度"
  42. >
  43. <el-option
  44. v-for="item in annualOptions"
  45. :key="item.value"
  46. :label="item.label"
  47. :value="item.value"
  48. />
  49. </el-select>
  50. </el-form-item>
  51. <el-form-item>
  52. <el-button type="primary" :icon="Search" @click="handleSearch" size="search"
  53. >查询
  54. </el-button>
  55. <el-button :icon="Refresh" @click="handleResetSearch" size="search">重置</el-button>
  56. </el-form-item>
  57. </el-form>
  58. <div class="flex gap-1" style="justify-content: flex-end">
  59. <el-button type="primary" :icon="Plus" @click="handleAdd" size="search">新增</el-button>
  60. <el-button
  61. type="danger"
  62. :icon="Delete"
  63. @click="handleBatchDelete"
  64. :disabled="selectedIds.length === 0"
  65. size="search"
  66. >批量删除
  67. </el-button>
  68. <el-button type="success" :icon="Download" @click="exportExcel" size="search"
  69. >导出</el-button
  70. >
  71. <el-upload
  72. class="inline-block ml-2"
  73. :action="base_url + '/eenterpriseGasAnnualStatistics/importData'"
  74. :show-file-list="false"
  75. :on-success="handleUploadSuccess"
  76. :on-error="handleUploadError"
  77. :before-upload="handleBeforeUpload"
  78. :http-request="(options) => importExcel(options.file)"
  79. >
  80. <el-button type="info" :icon="Upload" size="search">导入</el-button>
  81. </el-upload>
  82. </div>
  83. </div>
  84. <!-- Table -->
  85. <div class="bg-white rounded-lg shadow-sm p-4 overflow-x-auto">
  86. <el-table
  87. :data="tableData"
  88. style="width: 100%"
  89. border
  90. @selection-change="handleSelectionChange"
  91. :cell-style="{ fontSize: '14px' }"
  92. :header-cell-style="{ fontSize: '14px' }"
  93. :row-style="{ fontSize: '14px' }"
  94. >
  95. <el-table-column type="selection" width="55" fixed="left" />
  96. <el-table-column prop="enterpriseName" label="企业名称" align="center" />
  97. <el-table-column prop="unifiedSocialCreditCode" label="统一社会信用代码" align="center" />
  98. <el-table-column prop="accountNumber" label="户号信息" align="center" />
  99. <el-table-column prop="year" label="年度" align="center" />
  100. <el-table-column prop="openingDate" label="开户日期" align="center" />
  101. <el-table-column label="第一季度" align="center">
  102. <el-table-column prop="q1CubicMeter" label="用气量(m³)" align="center" />
  103. <el-table-column prop="q1Cost" label="费用" align="center" />
  104. </el-table-column>
  105. <el-table-column label="第二季度" align="center">
  106. <el-table-column prop="q2CubicMeter" label="用气量(m³)" align="center" />
  107. <el-table-column prop="q2CubicMeter" label="费用" align="center" />
  108. </el-table-column>
  109. <el-table-column label="第三季度" align="center">
  110. <el-table-column prop="q3CubicMeter" label="用气量(m³)" align="center" />
  111. <el-table-column prop="q3CubicMeter" label="费用" align="center" />
  112. </el-table-column>
  113. <el-table-column label="第四季度" align="center">
  114. <el-table-column prop="q4CubicMeter" label="用气量(m³)" align="center" />
  115. <el-table-column prop="q4CubicMeter" label="费用" align="center" />
  116. </el-table-column>
  117. <el-table-column label="年度总量" align="center">
  118. <el-table-column prop="annualTotalCubicMeter" label="用气量(m³)" align="center" />
  119. <el-table-column prop="annualTotalCost" label="费用" align="center" />
  120. </el-table-column>
  121. <el-table-column label="操作" fixed="right" align="center">
  122. <template #default="{ row }">
  123. <el-button :icon="Edit" size="small" @click="handleEdit(row)">编辑</el-button>
  124. <el-button type="danger" :icon="Delete" size="small" @click="handleDelete(row.id)"
  125. >删除
  126. </el-button>
  127. </template>
  128. </el-table-column>
  129. </el-table>
  130. <!-- Pagination -->
  131. <div class="mt-4 flex justify-end">
  132. <el-pagination
  133. @size-change="handleSizeChange"
  134. @current-change="handleCurrentChange"
  135. :current-page="pageNum"
  136. :page-sizes="[10, 20, 50, 100]"
  137. :page-size="pageSize"
  138. layout="total, sizes, prev, pager, next, jumper"
  139. :total="total"
  140. />
  141. </div>
  142. </div>
  143. <!-- Add/Edit Dialog -->
  144. <el-dialog
  145. v-model="dialogVisible"
  146. :title="dialogTitle"
  147. width="800px"
  148. :close-on-click-modal="false"
  149. :close-on-press-escape="false"
  150. >
  151. <el-form
  152. :model="formData"
  153. ref="formRef"
  154. label-width="120px"
  155. class="grid grid-cols-1 md:grid-cols-2 gap-4"
  156. >
  157. <el-form-item
  158. label="企业名称"
  159. prop="enterpriseName"
  160. :rules="[{ required: true, message: '请输入企业名称', trigger: 'blur' }]"
  161. >
  162. <el-input v-model="formData.enterpriseName" placeholder="请输入企业名称" />
  163. </el-form-item>
  164. <el-form-item
  165. label="信用代码"
  166. prop="unifiedSocialCreditCode"
  167. :rules="[{ required: true, message: '请输入统一社会信用代码', trigger: 'blur' }]"
  168. >
  169. <el-input
  170. v-model="formData.unifiedSocialCreditCode"
  171. placeholder="请输入统一社会信用代码"
  172. />
  173. </el-form-item>
  174. <el-form-item
  175. label="户号信息"
  176. prop="accountNumber"
  177. :rules="[{ required: true, message: '请输入户号信息', trigger: 'blur' }]"
  178. >
  179. <el-input v-model="formData.accountNumber" placeholder="请输入户号信息" />
  180. </el-form-item>
  181. <el-form-item
  182. label="开户日期"
  183. prop="openingDate"
  184. :rules="[{ required: true, message: '请选择开户日期', trigger: 'change' }]"
  185. >
  186. <el-date-picker
  187. v-model="formData.openingDate"
  188. type="date"
  189. format="YYYY/MM/DD"
  190. value-format="YYYY-MM-DD"
  191. />
  192. </el-form-item>
  193. <el-form-item
  194. label="年度"
  195. prop="year"
  196. :rules="[{ required: true, message: '请选择年度', trigger: 'change' }]"
  197. 部门
  198. >
  199. <el-select v-model="formData.year" placeholder="请选择年度" style="width: 100%">
  200. <el-option
  201. v-for="item in annualOptions"
  202. :key="item.value"
  203. :label="item.label"
  204. :value="item.value"
  205. />
  206. </el-select>
  207. </el-form-item>
  208. <el-form-item
  209. label="第一季度费用"
  210. prop="q1Cost"
  211. :rules="[
  212. { required: true, message: '请输入第一季度费用', trigger: 'blur' },
  213. { type: 'number', message: '请输入数字', trigger: 'blur' },
  214. ]"
  215. >
  216. <el-input v-model.number="formData.q1Cost" placeholder="请输入第一季度费用" />
  217. </el-form-item>
  218. <el-form-item
  219. label="第一季度用气数"
  220. prop="q1CubicMeter"
  221. :rules="[
  222. { required: true, message: '请输入第一季度用气数', trigger: 'blur' },
  223. { type: 'number', message: '请输入数字', trigger: 'blur' },
  224. ]"
  225. >
  226. <el-input v-model.number="formData.q1CubicMeter" placeholder="请输入第一季度用气数" />
  227. </el-form-item>
  228. <el-form-item
  229. label="第二季度费用"
  230. prop="q2Cost"
  231. :rules="[
  232. { required: true, message: '请输入第二季度费用', trigger: 'blur' },
  233. { type: 'number', message: '请输入数字', trigger: 'blur' },
  234. ]"
  235. >
  236. <el-input v-model.number="formData.q2Cost" placeholder="请输入第二季度费用" />
  237. </el-form-item>
  238. <el-form-item
  239. label="第二季度用气数"
  240. prop="q2CubicMeter"
  241. :rules="[
  242. { required: true, message: '请输入第二季度用气数', trigger: 'blur' },
  243. { type: 'number', message: '请输入数字', trigger: 'blur' },
  244. ]"
  245. >
  246. <el-input v-model.number="formData.q2CubicMeter" placeholder="请输入第二季度用气数" />
  247. </el-form-item>
  248. <el-form-item
  249. label="第三季度费用"
  250. prop="q3Cost"
  251. :rules="[
  252. { required: true, message: '请输入第三季度费用', trigger: 'blur' },
  253. { type: 'number', message: '请输入数字', trigger: 'blur' },
  254. ]"
  255. >
  256. <el-input v-model.number="formData.q3Cost" placeholder="请输入第三季度费用" />
  257. </el-form-item>
  258. <el-form-item
  259. label="第三季度用气数"
  260. prop="q3CubicMeter"
  261. :rules="[
  262. { required: true, message: '请输入第三季度用气数', trigger: 'blur' },
  263. { type: 'number', message: '请输入数字', trigger: 'blur' },
  264. ]"
  265. >
  266. <el-input v-model.number="formData.q3CubicMeter" placeholder="请输入第三季度用气数" />
  267. </el-form-item>
  268. <el-form-item
  269. label="第四季度费用"
  270. prop="q4Cost"
  271. :rules="[
  272. { required: true, message: '请输入第四季度费用', trigger: 'blur' },
  273. { type: 'number', message: '请输入数字', trigger: 'blur' },
  274. ]"
  275. >
  276. <el-input v-model.number="formData.q4Cost" placeholder="请输入第四季度费用" />
  277. </el-form-item>
  278. <el-form-item
  279. label="第四季度用气数"
  280. prop="q4CubicMeter"
  281. :rules="[
  282. { required: true, message: '请输入第四季度用气数', trigger: 'blur' },
  283. { type: 'number', message: '请输入数字', trigger: 'blur' },
  284. ]"
  285. >
  286. <el-input v-model.number="formData.q4CubicMeter" placeholder="请输入第四季度用气数" />
  287. </el-form-item>
  288. <el-form-item
  289. label="年度总费用"
  290. prop="annualTotalCost"
  291. :rules="[
  292. { required: true, message: '请输入年度总费用', trigger: 'blur' },
  293. { type: 'number', message: '请输入数字', trigger: 'blur' },
  294. ]"
  295. >
  296. <el-input v-model.number="formData.annualTotalCost" placeholder="请输入年度总费用" />
  297. </el-form-item>
  298. <el-form-item
  299. label="年度总度数"
  300. prop="annualTotalCubicMeter"
  301. :rules="[
  302. { required: true, message: '请输入年度总度数', trigger: 'blur' },
  303. { type: 'number', message: '请输入数字', trigger: 'blur' },
  304. ]"
  305. >
  306. <el-input
  307. v-model.number="formData.annualTotalCubicMeter"
  308. placeholder="请输入年度总度数"
  309. />
  310. </el-form-item>
  311. </el-form>
  312. <template #footer>
  313. <span class="dialog-footer">
  314. <el-button @click="dialogVisible = false">取消</el-button>
  315. <el-button type="primary" @click="handleConfirm">确定</el-button>
  316. </span>
  317. </template>
  318. </el-dialog>
  319. </div>
  320. </template>
  321. <script setup lang="ts">
  322. import { onMounted, reactive, ref } from 'vue'
  323. import { clientDownloadExcel, clientGet, clientPost } from '@/utils/request.ts'
  324. import type { FormInstance, UploadProps } from 'element-plus'
  325. import { ElForm, ElLoading, ElMessage, ElMessageBox } from 'element-plus'
  326. import { Delete, Download, Edit, Plus, Refresh, Search, Upload } from '@element-plus/icons-vue'
  327. // 假设 BaseResponse 和 PageType 在全局或 request.ts 中已定义
  328. interface BaseResponse {
  329. code: number
  330. msg: string
  331. }
  332. interface PageType<T> {
  333. records: T[]
  334. total: number
  335. size: number
  336. current: number
  337. pages: number
  338. }
  339. interface Property {
  340. id: string // 主键ID
  341. enterpriseName: string // 单位名称
  342. unifiedSocialCreditCode: string // 统一社会信用代码
  343. year?: string // 年度(可选)
  344. accountNumber?: string // 户号信息
  345. q1Cost?: string // 第一季度费用
  346. q1CubicMeter?: string // 第一季度用气数
  347. q2Cost?: string // 第二季度费用
  348. q2CubicMeter?: string // 第二季度用气数
  349. q3Cost?: string // 第三季度费用
  350. q3CubicMeter?: string // 第三季度用气数
  351. q4Cost?: string // 第四季度费用
  352. q4CubicMeter?: string // 第四季度用气数
  353. annualTotalCost?: string // 年度总费用
  354. annualTotalCubicMeter?: string // 年度总度数
  355. openingDate?: string // 开户日期
  356. updateTime?: string // 更新时间
  357. createTime?: string // 创建时间
  358. createBy?: string // 创建人
  359. }
  360. interface PropertyOneResponse extends BaseResponse {
  361. data: Property
  362. }
  363. interface PropertyListResponse extends BaseResponse {
  364. data: PageType<Property>
  365. }
  366. interface AddProperty {
  367. enterpriseName: string // 单位名称
  368. unifiedSocialCreditCode: string // 统一社会信用代码
  369. year?: string // 年度(可选)
  370. accountNumber?: string // 户号信息
  371. q1Cost?: string // 第一季度费用
  372. q1CubicMeter?: string // 第一季度用气数
  373. q2Cost?: string // 第二季度费用
  374. q2CubicMeter?: string // 第二季度用气数
  375. q3Cost?: string // 第三季度费用
  376. q3CubicMeter?: string // 第三季度用气数
  377. q4Cost?: string // 第四季度费用
  378. q4CubicMeter?: string // 第四季度用气数
  379. annualTotalCost?: string // 年度总费用
  380. annualTotalCubicMeter?: string // 年度总度数
  381. openingDate?: string // 开户日期
  382. }
  383. interface UpdateProperty {
  384. id?: string
  385. enterpriseName: string // 单位名称
  386. unifiedSocialCreditCode: string // 统一社会信用代码
  387. year?: string // 年度(可选)
  388. accountNumber?: string // 户号信息
  389. q1Cost?: string // 第一季度费用
  390. q1CubicMeter?: string // 第一季度用气数
  391. q2Cost?: string // 第二季度费用
  392. q2CubicMeter?: string // 第二季度用气数
  393. q3Cost?: string // 第三季度费用
  394. q3CubicMeter?: string // 第三季度用气数
  395. q4Cost?: string // 第四季度费用
  396. q4CubicMeter?: string // 第四季度用气数
  397. annualTotalCost?: string // 年度总费用
  398. annualTotalCubicMeter?: string // 年度总度数
  399. openingDate?: string // 开户日期
  400. }
  401. // 响应式状态变量
  402. const tableData = ref<Property[]>([])
  403. const total = ref(0)
  404. const pageSize = ref(10)
  405. const pageNum = ref(1)
  406. const searchForm = reactive({
  407. accountNumber: '',
  408. enterpriseName: '',
  409. unifiedSocialCreditCode: '',
  410. year: '',
  411. })
  412. const dialogVisible = ref(false)
  413. const dialogTitle = ref('')
  414. const isEdit = ref(false)
  415. const formData = reactive<Property>({})
  416. const formRef = ref<FormInstance>()
  417. const selectedIds = ref<string[]>([])
  418. const base_url = import.meta.env.VITE_APP_BASE_API
  419. // 欠缴状态选项
  420. const arrearsOptions = [
  421. { label: '是', value: '是' },
  422. { label: '否', value: '否' },
  423. ]
  424. // 年度选项(动态生成最近5年)
  425. const currentYear = new Date().getFullYear()
  426. const annualOptions = Array.from({ length: 10 }, (_, i) => ({
  427. label: `${currentYear - i}年`,
  428. value: `${currentYear - i}`,
  429. }))
  430. // 新增
  431. const add = async (data: AddProperty) => {
  432. const res = await clientPost<AddProperty, BaseResponse>('/eenterpriseGasAnnualStatistics/save', {
  433. ...data,
  434. })
  435. if (res.code !== 200) {
  436. ElMessage.error(res.msg)
  437. return false
  438. }
  439. ElMessage.success(res.msg)
  440. return true
  441. }
  442. // 根据id获取数据
  443. const getById = async (id: string) => {
  444. const res = await clientGet<null, PropertyOneResponse>(
  445. '/eenterpriseGasAnnualStatistics/getById/' + id,
  446. )
  447. if (res.code !== 200) {
  448. ElMessage.error(res.msg)
  449. return null
  450. }
  451. return res.data
  452. }
  453. // 分页获取数据
  454. const getList = async () => {
  455. // 构建参数对象,只包含有值的搜索条件
  456. const params = {
  457. pageNum: pageNum.value,
  458. pageSize: pageSize.value,
  459. ...(searchForm.accountNumber ? { accountNumber: searchForm.accountNumber } : {}),
  460. ...(searchForm.unifiedSocialCreditCode
  461. ? { unifiedSocialCreditCode: searchForm.unifiedSocialCreditCode }
  462. : {}),
  463. ...(searchForm.enterpriseName ? { enterpriseName: searchForm.enterpriseName } : {}),
  464. ...(searchForm.year ? { year: searchForm.year } : {}),
  465. }
  466. const res = await clientGet<
  467. {
  468. pageNum: number
  469. pageSize: number
  470. accountNumber?: string
  471. unifiedSocialCreditCode?: string
  472. enterpriseName?: string
  473. year?: string
  474. },
  475. PropertyListResponse
  476. >('/eenterpriseGasAnnualStatistics/findByPage', {
  477. params,
  478. })
  479. if (res.code !== 200) {
  480. ElMessage.error(res.msg)
  481. return
  482. }
  483. tableData.value = res.data.records
  484. total.value = res.data.total
  485. }
  486. // 批量删除
  487. const delBatch = async (ids: string[]) => {
  488. const res = await clientPost<string[], BaseResponse>(
  489. '/eenterpriseGasAnnualStatistics/deleteBatch',
  490. ids,
  491. )
  492. if (res.code !== 200) {
  493. ElMessage.error(res.msg)
  494. return false
  495. }
  496. ElMessage.success(res.msg)
  497. return true
  498. }
  499. // 修改
  500. const update = async (data: UpdateProperty) => {
  501. const res = await clientPost<UpdateProperty, BaseResponse>(
  502. '/eenterpriseGasAnnualStatistics/update',
  503. {
  504. ...data,
  505. },
  506. )
  507. if (res.code !== 200) {
  508. ElMessage.error(res.msg)
  509. return false
  510. }
  511. ElMessage.success(res.msg)
  512. return true
  513. }
  514. // 导出为excel
  515. const exportExcel = async () => {
  516. const loading = ElLoading.service({
  517. lock: true,
  518. text: '导出中,请稍候...',
  519. background: 'rgba(0, 0, 0, 0.1)',
  520. fullscreen: true,
  521. })
  522. try {
  523. // 构建参数对象,只包含有值的搜索条件
  524. const params = {
  525. ...(searchForm.accountNumber ? { accountNumber: searchForm.accountNumber } : {}),
  526. ...(searchForm.unifiedSocialCreditCode
  527. ? { unifiedSocialCreditCode: searchForm.unifiedSocialCreditCode }
  528. : {}),
  529. ...(searchForm.enterpriseName ? { enterpriseName: searchForm.enterpriseName } : {}),
  530. ...(searchForm.year ? { year: searchForm.year } : {}),
  531. }
  532. await clientDownloadExcel('/eenterpriseGasAnnualStatistics/exportData', {
  533. params,
  534. })
  535. ElMessage.success('导出成功')
  536. } catch (error) {
  537. console.error('导出失败:', error)
  538. ElMessage.error('导出失败')
  539. } finally {
  540. loading.close()
  541. }
  542. }
  543. // excel导入
  544. const importExcel = async (file: File) => {
  545. const formData = new FormData()
  546. formData.append('file', file)
  547. const res = await clientPost<FormData, BaseResponse>(
  548. '/eenterpriseGasAnnualStatistics/importData',
  549. formData,
  550. {
  551. headers: {
  552. 'Content-Type': 'multipart/form-data',
  553. },
  554. },
  555. )
  556. if (res.code !== 200) {
  557. ElMessage.error(res.msg)
  558. return false
  559. }
  560. ElMessage.success(res.msg)
  561. getList()
  562. return true
  563. }
  564. // --- CRUD 操作和处理函数 ---
  565. const handleAdd = () => {
  566. isEdit.value = false
  567. dialogTitle.value = '新增用水信息'
  568. // 重置表单数据
  569. Object.keys(formData).forEach((key) => delete formData[key as keyof Property])
  570. // 设置默认值为当前年度
  571. formData.openingDate = `${currentYear}`
  572. dialogVisible.value = true
  573. }
  574. const handleEdit = async (row: Property) => {
  575. isEdit.value = true
  576. dialogTitle.value = '编辑用水信息'
  577. const data = await getById(row.id)
  578. if (data) {
  579. Object.assign(formData, data)
  580. dialogVisible.value = true
  581. }
  582. }
  583. const handleDelete = async (id: string) => {
  584. ElMessageBox.confirm('确定删除此条记录吗?', '提示', {
  585. confirmButtonText: '确定',
  586. cancelButtonText: '取消',
  587. type: 'warning',
  588. })
  589. .then(async () => {
  590. const success = await delBatch([id])
  591. if (success) {
  592. getList()
  593. }
  594. })
  595. .catch(() => {
  596. ElMessage.info('已取消删除')
  597. })
  598. }
  599. const handleBatchDelete = () => {
  600. if (selectedIds.value.length === 0) {
  601. ElMessage.warning('请选择要删除的记录')
  602. return
  603. }
  604. ElMessageBox.confirm(`确定删除选中的 ${selectedIds.value.length} 条记录吗?`, '提示', {
  605. confirmButtonText: '确定',
  606. cancelButtonText: '取消',
  607. type: 'warning',
  608. })
  609. .then(async () => {
  610. const success = await delBatch(selectedIds.value)
  611. if (success) {
  612. getList()
  613. }
  614. })
  615. .catch(() => {
  616. ElMessage.info('已取消批量删除')
  617. })
  618. }
  619. const handleSearch = () => {
  620. pageNum.value = 1 // 搜索时重置到第一页
  621. getList()
  622. }
  623. const handleResetSearch = () => {
  624. searchForm.accountNumber = ''
  625. searchForm.unifiedSocialCreditCode = ''
  626. searchForm.enterpriseName = ''
  627. searchForm.year = ''
  628. pageNum.value = 1
  629. getList()
  630. }
  631. const handleConfirm = async () => {
  632. if (!formRef.value) return
  633. await formRef.value.validate(async (valid) => {
  634. if (valid) {
  635. let success = false
  636. const dataToSend = { ...formData }
  637. if (isEdit.value) {
  638. success = await update(dataToSend)
  639. } else {
  640. success = await add(dataToSend)
  641. }
  642. if (success) {
  643. dialogVisible.value = false
  644. getList()
  645. }
  646. } else {
  647. ElMessage.error('请检查表单填写')
  648. }
  649. })
  650. }
  651. const handleCurrentChange = (val: number) => {
  652. pageNum.value = val
  653. getList()
  654. }
  655. const handleSizeChange = (val: number) => {
  656. pageSize.value = val
  657. pageNum.value = 1 // 页大小改变时重置到第一页
  658. getList()
  659. }
  660. const handleSelectionChange = (selection: Property[]) => {
  661. selectedIds.value = selection.map((item) => item.id)
  662. }
  663. const handleUploadSuccess: UploadProps['onSuccess'] = async (response, uploadFile) => {
  664. if (response && response.code === 200) {
  665. ElMessage.success('文件导入成功')
  666. getList()
  667. } else {
  668. ElMessage.error(`文件导入失败: ${response?.msg || '未知错误'}`)
  669. }
  670. }
  671. const handleUploadError: UploadProps['onError'] = (error) => {
  672. ElMessage.error(`文件导入失败: ${error.message}`)
  673. }
  674. const handleBeforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
  675. const isExcel =
  676. rawFile.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
  677. rawFile.type === 'application/vnd.ms-excel'
  678. if (!isExcel) {
  679. ElMessage.error('导入文件只能是 Excel 格式!')
  680. return false
  681. }
  682. return true
  683. }
  684. const init = () => {
  685. getList()
  686. }
  687. onMounted(() => {
  688. init()
  689. })
  690. </script>
  691. <style scoped>
  692. /* Add any specific styles here if needed, otherwise UnoCSS handles most */
  693. </style>