西藏巴青项目

index.vue 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. <template>
  2. <div class="app-container">
  3. <el-card shadow="never">
  4. <el-form ref="queryForm" :model="queryParams" size="small" :inline="true">
  5. <el-form-item prop="statYear">
  6. <template slot="label">{{ dmT("queryStatYear") }}</template>
  7. <el-select v-model="queryParams.statYear" :placeholder="dmCommon('pleaseSelect')" clearable style="width: 140px">
  8. <el-option v-for="y in yearOptions" :key="y" :label="String(y)" :value="y" />
  9. </el-select>
  10. </el-form-item>
  11. <el-form-item>
  12. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ dmCommon("search") }}</el-button>
  13. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ dmCommon("reset") }}</el-button>
  14. <el-button
  15. v-hasPermi="['dataModel:disposableIncome:add']"
  16. type="primary"
  17. plain
  18. icon="el-icon-plus"
  19. size="mini"
  20. @click="handleAdd"
  21. >{{ dmCommon("add") }}</el-button>
  22. </el-form-item>
  23. </el-form>
  24. </el-card>
  25. <br />
  26. <el-card shadow="never">
  27. <el-table v-loading="loading" :data="tableList" border>
  28. <template slot="empty">
  29. <span>{{ dmT("emptyList") }}</span>
  30. </template>
  31. <el-table-column :label="dmT('colStatYear')" prop="statYear" align="center" width="120" />
  32. <el-table-column :label="dmT('colRuralDisposableIncome')" align="center" min-width="180">
  33. <template slot-scope="scope">{{ formatMoney(scope.row.ruralDisposableIncome) }}</template>
  34. </el-table-column>
  35. <el-table-column :label="dmT('colIncomeIncrease')" align="center" min-width="120">
  36. <template slot-scope="scope">{{ formatIncrease(scope.row.incomeIncrease) }}</template>
  37. </el-table-column>
  38. <el-table-column :label="dmT('colIncomeGrowthRate')" align="center" min-width="120">
  39. <template slot-scope="scope">{{ formatGrowthRate(scope.row.incomeGrowthRate) }}</template>
  40. </el-table-column>
  41. <el-table-column :label="dmT('colPovertyNetIncome')" align="center" min-width="180">
  42. <template slot-scope="scope">{{ formatMoney(scope.row.povertyNetIncome) }}</template>
  43. </el-table-column>
  44. <el-table-column :label="dmCommon('colOp')" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
  45. <template slot-scope="scope">
  46. <el-button v-hasPermi="['dataModel:disposableIncome:query']" type="text" size="mini" @click="handleView(scope.row)">{{ dmCommon("view") }}</el-button>
  47. <el-button v-hasPermi="['dataModel:disposableIncome:edit']" type="text" size="mini" @click="handleEdit(scope.row)">{{ dmCommon("edit") }}</el-button>
  48. <el-button v-hasPermi="['dataModel:disposableIncome:remove']" type="text" size="mini" @click="handleDelete(scope.row)">{{ dmCommon("delete") }}</el-button>
  49. </template>
  50. </el-table-column>
  51. </el-table>
  52. <pagination
  53. v-show="total > 0"
  54. :total="total"
  55. :page.sync="queryParams.pageNum"
  56. :limit.sync="queryParams.pageSize"
  57. @pagination="getList"
  58. />
  59. </el-card>
  60. <el-dialog :title="dialogTitle" :visible.sync="open" width="640px" append-to-body @close="cancel">
  61. <el-form :key="formKey" ref="form" :model="form" label-width="180px" size="small">
  62. <el-form-item prop="statYear" :rules="formRules.statYear">
  63. <template slot="label">{{ dmT("formStatYear") }}</template>
  64. <el-select v-model="form.statYear" :placeholder="dmCommon('pleaseSelect')" style="width: 100%" @change="onStatYearOrIncomeChange">
  65. <el-option v-for="y in yearOptions" :key="'f-' + y" :label="String(y)" :value="y" />
  66. </el-select>
  67. </el-form-item>
  68. <el-form-item prop="ruralDisposableIncome" :rules="formRules.ruralDisposableIncome">
  69. <template slot="label">{{ dmT("formRuralDisposableIncome") }}</template>
  70. <div class="dm-input-with-unit">
  71. <el-input-number
  72. v-model="form.ruralDisposableIncome"
  73. :min="0"
  74. :precision="2"
  75. :controls="true"
  76. controls-position="right"
  77. class="dm-input-with-unit__input"
  78. @change="onStatYearOrIncomeChange"
  79. />
  80. <span class="dm-input-with-unit__suffix">{{ dmT("unitYuan") }}</span>
  81. </div>
  82. </el-form-item>
  83. <el-form-item prop="incomeIncrease" :rules="formRules.optionalNumber">
  84. <template slot="label">{{ dmT("formIncomeIncrease") }}</template>
  85. <div class="dm-input-with-unit">
  86. <el-input
  87. v-model="form.incomeIncrease"
  88. :placeholder="dmT('phIncomeIncrease')"
  89. clearable
  90. class="dm-input-with-unit__input"
  91. />
  92. <span class="dm-input-with-unit__suffix">{{ dmT("unitYuan") }}</span>
  93. </div>
  94. </el-form-item>
  95. <el-form-item prop="incomeGrowthRate" :rules="formRules.optionalNumber">
  96. <template slot="label">{{ dmT("formIncomeGrowthRate") }}</template>
  97. <el-input
  98. v-model="form.incomeGrowthRate"
  99. :placeholder="dmT('phIncomeGrowthRate')"
  100. clearable
  101. style="width: 100%"
  102. />
  103. </el-form-item>
  104. <el-form-item prop="povertyNetIncome" :rules="formRules.povertyNetIncome">
  105. <template slot="label">{{ dmT("formPovertyNetIncome") }}</template>
  106. <div class="dm-input-with-unit">
  107. <el-input-number
  108. v-model="form.povertyNetIncome"
  109. :min="0"
  110. :precision="2"
  111. :controls="true"
  112. controls-position="right"
  113. class="dm-input-with-unit__input"
  114. />
  115. <span class="dm-input-with-unit__suffix">{{ dmT("unitYuan") }}</span>
  116. </div>
  117. </el-form-item>
  118. <el-form-item prop="remark" :rules="formRules.remark">
  119. <template slot="label">{{ dmT("formRemark") }}</template>
  120. <el-input v-model="form.remark" type="textarea" :rows="2" :placeholder="dmT('phRemark')" maxlength="500" show-word-limit />
  121. </el-form-item>
  122. </el-form>
  123. <div slot="footer" class="dialog-footer">
  124. <el-button type="primary" @click="submitForm">{{ dmCommon("ok") }}</el-button>
  125. <el-button @click="cancel">{{ dmCommon("cancel") }}</el-button>
  126. </div>
  127. </el-dialog>
  128. <el-dialog :title="dmCommon('viewTitle')" :visible.sync="viewOpen" width="640px" append-to-body>
  129. <el-form label-width="180px" size="small">
  130. <el-form-item :label="dmT('formStatYear')">
  131. <span>{{ viewRow.statYear || dash }}</span>
  132. </el-form-item>
  133. <el-form-item :label="dmT('formRuralDisposableIncome')">
  134. <span>{{ formatWithUnit(formatMoney(viewRow.ruralDisposableIncome), "unitYuan") }}</span>
  135. </el-form-item>
  136. <el-form-item :label="dmT('formIncomeIncrease')">
  137. <span>{{ formatWithUnit(formatIncrease(viewRow.incomeIncrease), "unitYuan") }}</span>
  138. </el-form-item>
  139. <el-form-item :label="dmT('formIncomeGrowthRate')">
  140. <span>{{ formatGrowthRate(viewRow.incomeGrowthRate) }}</span>
  141. </el-form-item>
  142. <el-form-item :label="dmT('formPovertyNetIncome')">
  143. <span>{{ formatWithUnit(formatMoney(viewRow.povertyNetIncome), "unitYuan") }}</span>
  144. </el-form-item>
  145. <el-form-item :label="dmT('formRemark')">
  146. <span class="di-view-text">{{ viewRow.remark || dash }}</span>
  147. </el-form-item>
  148. </el-form>
  149. <div slot="footer" class="dialog-footer">
  150. <el-button
  151. v-hasPermi="['dataModel:disposableIncome:edit']"
  152. type="primary"
  153. @click="handleEditFromView"
  154. >{{ dmCommon("edit") }}</el-button>
  155. <el-button @click="viewOpen = false">{{ dmCommon("close") }}</el-button>
  156. </div>
  157. </el-dialog>
  158. </div>
  159. </template>
  160. <script>
  161. import dataModelLocaleMixin from "@/mixins/dataModelLocaleMixin"
  162. import {
  163. listDisposableIncome,
  164. getDisposableIncome,
  165. addDisposableIncome,
  166. updateDisposableIncome,
  167. delDisposableIncome,
  168. listDisposableIncomeYearOptions
  169. } from "@/api/dataModel/disposableIncome"
  170. function createEmptyForm() {
  171. return {
  172. id: undefined,
  173. statYear: undefined,
  174. ruralDisposableIncome: undefined,
  175. incomeIncrease: undefined,
  176. incomeGrowthRate: undefined,
  177. povertyNetIncome: undefined,
  178. remark: undefined
  179. }
  180. }
  181. function optionalFieldToInput(val) {
  182. if (val == null || val === "") {
  183. return undefined
  184. }
  185. return String(val)
  186. }
  187. function parseOptionalNumber(val) {
  188. if (val == null || val === "") {
  189. return undefined
  190. }
  191. const n = Number(val)
  192. return Number.isFinite(n) ? n : NaN
  193. }
  194. function normalizeForm(data) {
  195. const row = Object.assign(createEmptyForm(), data || {})
  196. if (row.ruralDisposableIncome != null && row.ruralDisposableIncome !== "") {
  197. row.ruralDisposableIncome = Number(row.ruralDisposableIncome)
  198. } else {
  199. row.ruralDisposableIncome = undefined
  200. }
  201. if (row.povertyNetIncome != null && row.povertyNetIncome !== "") {
  202. row.povertyNetIncome = Number(row.povertyNetIncome)
  203. } else {
  204. row.povertyNetIncome = undefined
  205. }
  206. row.incomeIncrease = optionalFieldToInput(row.incomeIncrease)
  207. row.incomeGrowthRate = optionalFieldToInput(row.incomeGrowthRate)
  208. return row
  209. }
  210. export default {
  211. name: "DisposableIncome",
  212. mixins: [dataModelLocaleMixin],
  213. data() {
  214. return {
  215. dmNs: "disposableIncome",
  216. loading: false,
  217. open: false,
  218. viewOpen: false,
  219. dialogEdit: false,
  220. formKey: 0,
  221. total: 0,
  222. tableList: [],
  223. yearOptions: [],
  224. viewRow: {},
  225. /** 编辑时用于判断年份/可支配收入是否变更,以便清空增幅增速 */
  226. baselineStatYear: undefined,
  227. baselineRuralDisposableIncome: undefined,
  228. queryParams: {
  229. pageNum: 1,
  230. pageSize: 20,
  231. statYear: undefined
  232. },
  233. form: createEmptyForm()
  234. }
  235. },
  236. computed: {
  237. dash() {
  238. return this.$t("dataModel.status.dash")
  239. },
  240. dialogTitle() {
  241. return this.dialogEdit ? this.dmT("dialogEdit") : this.dmT("dialogAdd")
  242. },
  243. formRules() {
  244. return {
  245. statYear: [{ required: true, message: this.dmT("ruleStatYearRequired"), trigger: "change" }],
  246. ruralDisposableIncome: [
  247. { required: true, message: this.dmT("ruleRuralIncomeRequired"), trigger: "change" },
  248. { validator: this.validateNonNegativeMoney, trigger: "change" }
  249. ],
  250. povertyNetIncome: [
  251. { required: true, message: this.dmT("rulePovertyIncomeRequired"), trigger: "change" },
  252. { validator: this.validateNonNegativeMoney, trigger: "change" }
  253. ],
  254. optionalNumber: [{ validator: this.validateOptionalNumber, trigger: "blur" }],
  255. remark: [{ max: 500, message: this.dmCommon("ruleLen500"), trigger: "blur" }]
  256. }
  257. }
  258. },
  259. created() {
  260. this.loadOptions()
  261. this.getList()
  262. },
  263. methods: {
  264. loadOptions() {
  265. listDisposableIncomeYearOptions().then((res) => {
  266. this.yearOptions = Array.isArray(res.data) ? res.data : []
  267. })
  268. },
  269. formatMoney(val) {
  270. if (val == null || val === "") {
  271. return this.dash
  272. }
  273. const n = Number(val)
  274. if (!Number.isFinite(n)) {
  275. return this.dash
  276. }
  277. return n.toFixed(2)
  278. },
  279. /** 列表/详情:库中 null 显示为 0.00 */
  280. formatIncrease(val) {
  281. if (val == null || val === "") {
  282. return "0.00"
  283. }
  284. const n = Number(val)
  285. if (!Number.isFinite(n)) {
  286. return "0.00"
  287. }
  288. return n.toFixed(2)
  289. },
  290. /** 列表/详情:库中 null 显示为 0.00%;小数转百分比 */
  291. formatGrowthRate(val) {
  292. if (val == null || val === "") {
  293. return "0.00%"
  294. }
  295. const n = Number(val)
  296. if (!Number.isFinite(n)) {
  297. return "0.00%"
  298. }
  299. return (n * 100).toFixed(2) + "%"
  300. },
  301. validateNonNegativeMoney(rule, value, callback) {
  302. if (value == null || value === "") {
  303. callback()
  304. return
  305. }
  306. if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
  307. callback(new Error(this.dmT("ruleMoneyNonNegative")))
  308. return
  309. }
  310. callback()
  311. },
  312. validateOptionalNumber(rule, value, callback) {
  313. if (value == null || value === "") {
  314. callback()
  315. return
  316. }
  317. const n = Number(value)
  318. if (!Number.isFinite(n)) {
  319. callback(new Error(this.dmT("ruleNumberInvalid")))
  320. return
  321. }
  322. callback()
  323. },
  324. clearGrowthFields() {
  325. this.form.incomeIncrease = undefined
  326. this.form.incomeGrowthRate = undefined
  327. },
  328. onStatYearOrIncomeChange() {
  329. if (!this.dialogEdit || !this.open) {
  330. return
  331. }
  332. const yearChanged = this.form.statYear !== this.baselineStatYear
  333. const incomeChanged = !this.sameDecimal(this.form.ruralDisposableIncome, this.baselineRuralDisposableIncome)
  334. if (yearChanged || incomeChanged) {
  335. this.clearGrowthFields()
  336. }
  337. },
  338. sameDecimal(a, b) {
  339. if (a == null && b == null) {
  340. return true
  341. }
  342. if (a == null || b == null) {
  343. return false
  344. }
  345. return Number(a) === Number(b)
  346. },
  347. buildListQuery() {
  348. const q = {
  349. pageNum: this.queryParams.pageNum,
  350. pageSize: this.queryParams.pageSize
  351. }
  352. if (this.queryParams.statYear != null) {
  353. q.statYear = this.queryParams.statYear
  354. }
  355. return q
  356. },
  357. buildPayload() {
  358. const remarkText = this.form.remark != null ? String(this.form.remark).trim() : ""
  359. const incomeIncrease = parseOptionalNumber(this.form.incomeIncrease)
  360. const incomeGrowthRate = parseOptionalNumber(this.form.incomeGrowthRate)
  361. return {
  362. statYear: this.form.statYear,
  363. ruralDisposableIncome: this.form.ruralDisposableIncome,
  364. povertyNetIncome: this.form.povertyNetIncome,
  365. incomeIncrease: incomeIncrease === undefined ? undefined : incomeIncrease,
  366. incomeGrowthRate: incomeGrowthRate === undefined ? undefined : incomeGrowthRate,
  367. remark: remarkText !== "" ? remarkText : undefined
  368. }
  369. },
  370. getList() {
  371. this.loading = true
  372. listDisposableIncome(this.buildListQuery())
  373. .then((res) => {
  374. this.tableList = res.rows || []
  375. this.total = res.total != null ? res.total : 0
  376. })
  377. .finally(() => {
  378. this.loading = false
  379. })
  380. },
  381. handleQuery() {
  382. this.queryParams.pageNum = 1
  383. this.getList()
  384. },
  385. resetQuery() {
  386. this.queryParams.statYear = undefined
  387. this.resetForm("queryForm")
  388. this.handleQuery()
  389. },
  390. handleAdd() {
  391. this.formKey += 1
  392. this.form = createEmptyForm()
  393. this.form.statYear = new Date().getFullYear()
  394. this.baselineStatYear = undefined
  395. this.baselineRuralDisposableIncome = undefined
  396. this.dialogEdit = false
  397. this.open = true
  398. this.$nextTick(() => {
  399. if (this.$refs.form) {
  400. this.$refs.form.clearValidate()
  401. }
  402. })
  403. },
  404. handleEdit(row) {
  405. getDisposableIncome(row.id).then((res) => {
  406. this.formKey += 1
  407. this.dialogEdit = true
  408. this.form = normalizeForm(res.data || {})
  409. this.baselineStatYear = this.form.statYear
  410. this.baselineRuralDisposableIncome = this.form.ruralDisposableIncome
  411. this.open = true
  412. this.viewOpen = false
  413. this.$nextTick(() => {
  414. if (this.$refs.form) {
  415. this.$refs.form.clearValidate()
  416. }
  417. })
  418. })
  419. },
  420. handleEditFromView() {
  421. this.handleEdit(this.viewRow)
  422. },
  423. handleView(row) {
  424. getDisposableIncome(row.id).then((res) => {
  425. this.viewRow = res.data || {}
  426. this.viewOpen = true
  427. })
  428. },
  429. handleDelete(row) {
  430. this.$modal
  431. .confirm(this.dmT("confirmDelete"))
  432. .then(() => delDisposableIncome(row.id))
  433. .then(() => {
  434. this.$modal.msgSuccess(this.dmCommon("msgDelOk"))
  435. this.viewOpen = false
  436. this.getList()
  437. })
  438. .catch(() => {})
  439. },
  440. submitForm() {
  441. this.$refs.form.validate((valid) => {
  442. if (!valid) {
  443. return
  444. }
  445. const payload = this.buildPayload()
  446. if (Number.isNaN(payload.incomeIncrease) || Number.isNaN(payload.incomeGrowthRate)) {
  447. this.$modal.msgError(this.dmT("ruleNumberInvalid"))
  448. return
  449. }
  450. if (this.dialogEdit) {
  451. payload.id = this.form.id
  452. updateDisposableIncome(payload).then(() => {
  453. this.$modal.msgSuccess(this.dmCommon("msgModOk"))
  454. this.open = false
  455. this.getList()
  456. })
  457. } else {
  458. addDisposableIncome(payload).then(() => {
  459. this.$modal.msgSuccess(this.dmCommon("msgAddOk"))
  460. this.open = false
  461. this.getList()
  462. })
  463. }
  464. })
  465. },
  466. cancel() {
  467. this.open = false
  468. this.form = createEmptyForm()
  469. }
  470. }
  471. }
  472. </script>
  473. <style scoped>
  474. .di-view-text {
  475. white-space: pre-wrap;
  476. word-break: break-all;
  477. }
  478. .dm-input-with-unit {
  479. display: flex;
  480. align-items: center;
  481. width: 100%;
  482. }
  483. .dm-input-with-unit__input {
  484. flex: 1;
  485. width: 100%;
  486. }
  487. .dm-input-with-unit__suffix {
  488. margin-left: 8px;
  489. color: #909399;
  490. white-space: nowrap;
  491. }
  492. .dm-input-with-unit >>> .el-input-number {
  493. width: 100%;
  494. }
  495. </style>