西藏巴青项目

index.vue 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  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: 120px">
  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 prop="statMonth">
  12. <template slot="label">{{ dmT("queryStatMonth") }}</template>
  13. <el-select v-model="queryParams.statMonth" :placeholder="dmCommon('pleaseSelect')" clearable style="width: 120px">
  14. <el-option v-for="m in monthOptions" :key="'q-' + m" :label="formatStatMonth(m)" :value="m" />
  15. </el-select>
  16. </el-form-item>
  17. <el-form-item prop="townDeptId">
  18. <template slot="label">{{ dmT("queryTown") }}</template>
  19. <el-select v-model="queryParams.townDeptId" :placeholder="dmCommon('pleaseSelect')" clearable filterable style="width: 180px">
  20. <el-option v-for="item in townOptions" :key="item.deptId" :label="item.deptName" :value="item.deptId" />
  21. </el-select>
  22. </el-form-item>
  23. <el-form-item>
  24. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ dmCommon("search") }}</el-button>
  25. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ dmCommon("reset") }}</el-button>
  26. <el-button
  27. v-hasPermi="['dataModel:yakOutboundReport:add']"
  28. type="primary"
  29. plain
  30. icon="el-icon-plus"
  31. size="mini"
  32. @click="handleAdd"
  33. >{{ dmCommon("add") }}</el-button>
  34. <el-button
  35. v-hasPermi="['dataModel:yakOutboundReport:import']"
  36. type="info"
  37. plain
  38. icon="el-icon-upload2"
  39. size="mini"
  40. @click="handleImportOpen"
  41. >{{ dmT("btnImport") }}</el-button>
  42. <el-button
  43. v-hasPermi="['dataModel:yakOutboundReport:export']"
  44. type="warning"
  45. plain
  46. icon="el-icon-download"
  47. size="mini"
  48. @click="handleExportOpen"
  49. >{{ dmCommon("export") }}</el-button>
  50. </el-form-item>
  51. </el-form>
  52. </el-card>
  53. <br />
  54. <el-card shadow="never">
  55. <el-table v-loading="loading" :data="tableList" border>
  56. <template slot="empty">
  57. <span>{{ dmT("emptyList") }}</span>
  58. </template>
  59. <el-table-column :label="dmT('colStatYear')" prop="statYear" align="center" width="100" />
  60. <el-table-column :label="dmT('colStatMonth')" align="center" width="90">
  61. <template slot-scope="scope">{{ formatStatMonth(scope.row.statMonth) }}</template>
  62. </el-table-column>
  63. <el-table-column :label="dmT('colTownName')" prop="townName" align="center" min-width="120" :show-overflow-tooltip="true" />
  64. <el-table-column :label="dmT('colFarmerHouseholdCount')" align="center" width="100">
  65. <template slot-scope="scope">{{ formatCount(scope.row.farmerHouseholdCount) }}</template>
  66. </el-table-column>
  67. <el-table-column :label="dmT('colFarmerPopulationCount')" align="center" width="100">
  68. <template slot-scope="scope">{{ formatCount(scope.row.farmerPopulationCount) }}</template>
  69. </el-table-column>
  70. <el-table-column :label="dmT('colYakOutboundCount')" align="center" width="110">
  71. <template slot-scope="scope">{{ formatCount(scope.row.yakOutboundCount) }}</template>
  72. </el-table-column>
  73. <el-table-column :label="dmT('colSelfConsumptionCattle')" align="center" width="120">
  74. <template slot-scope="scope">{{ formatCount(scope.row.selfConsumptionCattleCount) }}</template>
  75. </el-table-column>
  76. <el-table-column :label="dmCommon('colOp')" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
  77. <template slot-scope="scope">
  78. <el-button v-hasPermi="['dataModel:yakOutboundReport:query']" type="text" size="mini" @click="handleView(scope.row)">{{ dmCommon("view") }}</el-button>
  79. <el-button v-hasPermi="['dataModel:yakOutboundReport:edit']" type="text" size="mini" @click="handleEdit(scope.row)">{{ dmCommon("edit") }}</el-button>
  80. <el-button v-hasPermi="['dataModel:yakOutboundReport:remove']" type="text" size="mini" @click="handleDelete(scope.row)">{{ dmCommon("delete") }}</el-button>
  81. </template>
  82. </el-table-column>
  83. </el-table>
  84. <pagination
  85. v-show="total > 0"
  86. :total="total"
  87. :page.sync="queryParams.pageNum"
  88. :limit.sync="queryParams.pageSize"
  89. @pagination="getList"
  90. />
  91. </el-card>
  92. <el-dialog :title="dialogTitle" :visible.sync="open" width="920px" append-to-body @close="cancel">
  93. <el-form :key="formKey" ref="form" :model="form" label-width="168px" size="small">
  94. <div class="yor-section-title">{{ dmT("sectionBasic") }}</div>
  95. <el-row :gutter="16">
  96. <el-col :span="8">
  97. <el-form-item prop="statYear" :rules="formRules.statYear">
  98. <template slot="label">{{ dmT("formStatYear") }}</template>
  99. <el-select v-model="form.statYear" :placeholder="dmCommon('pleaseSelect')" style="width: 100%">
  100. <el-option v-for="y in yearOptions" :key="'f-' + y" :label="String(y)" :value="y" />
  101. </el-select>
  102. </el-form-item>
  103. </el-col>
  104. <el-col :span="8">
  105. <el-form-item prop="statMonth" :rules="formRules.statMonth">
  106. <template slot="label">{{ dmT("formStatMonth") }}</template>
  107. <el-select v-model="form.statMonth" :placeholder="dmCommon('pleaseSelect')" style="width: 100%">
  108. <el-option v-for="m in monthOptions" :key="'fm-' + m" :label="formatStatMonth(m)" :value="m" />
  109. </el-select>
  110. </el-form-item>
  111. </el-col>
  112. <el-col :span="8">
  113. <el-form-item prop="townDeptId" :rules="formRules.townDeptId">
  114. <template slot="label">{{ dmT("formTown") }}</template>
  115. <el-select v-model="form.townDeptId" :placeholder="dmCommon('pleaseSelect')" filterable style="width: 100%">
  116. <el-option v-for="item in townOptions" :key="'ft-' + item.deptId" :label="item.deptName" :value="item.deptId" />
  117. </el-select>
  118. </el-form-item>
  119. </el-col>
  120. </el-row>
  121. <div class="yor-section-title">{{ dmT("sectionFarmer") }}</div>
  122. <el-row :gutter="16">
  123. <el-col v-for="field in farmerCountFields" :key="field.prop" :span="12">
  124. <el-form-item :prop="field.prop" :rules="formRules.count">
  125. <template slot="label">{{ dmT(field.labelKey) }}</template>
  126. <div class="dm-input-with-unit">
  127. <el-input-number
  128. v-model="form[field.prop]"
  129. :min="0"
  130. :precision="0"
  131. :controls="true"
  132. controls-position="right"
  133. class="dm-input-with-unit__input"
  134. />
  135. <span class="dm-input-with-unit__suffix">{{ dmT(field.unitKey) }}</span>
  136. </div>
  137. </el-form-item>
  138. </el-col>
  139. </el-row>
  140. <div class="yor-section-title">{{ dmT("sectionOutbound") }}</div>
  141. <el-row :gutter="16">
  142. <el-col v-for="field in outboundCountFields" :key="field.prop" :span="12">
  143. <el-form-item :prop="field.prop" :rules="formRules.count">
  144. <template slot="label">{{ dmT(field.labelKey) }}</template>
  145. <div class="dm-input-with-unit">
  146. <el-input-number
  147. v-model="form[field.prop]"
  148. :min="0"
  149. :precision="0"
  150. :controls="true"
  151. controls-position="right"
  152. class="dm-input-with-unit__input"
  153. />
  154. <span class="dm-input-with-unit__suffix">{{ dmT(field.unitKey) }}</span>
  155. </div>
  156. </el-form-item>
  157. </el-col>
  158. </el-row>
  159. <div class="yor-section-title">{{ dmT("sectionSelfConsumption") }}</div>
  160. <el-row :gutter="16">
  161. <el-col v-for="field in selfConsumptionCountFields" :key="field.prop" :span="12">
  162. <el-form-item :prop="field.prop" :rules="formRules.count">
  163. <template slot="label">{{ dmT(field.labelKey) }}</template>
  164. <div class="dm-input-with-unit">
  165. <el-input-number
  166. v-model="form[field.prop]"
  167. :min="0"
  168. :precision="0"
  169. :controls="true"
  170. controls-position="right"
  171. class="dm-input-with-unit__input"
  172. />
  173. <span class="dm-input-with-unit__suffix">{{ dmT(field.unitKey) }}</span>
  174. </div>
  175. </el-form-item>
  176. </el-col>
  177. </el-row>
  178. <el-form-item prop="remark" :rules="formRules.remark">
  179. <template slot="label">{{ dmT("formRemark") }}</template>
  180. <el-input v-model="form.remark" type="textarea" :rows="2" :placeholder="dmT('phRemark')" maxlength="500" show-word-limit />
  181. </el-form-item>
  182. </el-form>
  183. <div slot="footer" class="dialog-footer">
  184. <el-button type="primary" @click="submitForm">{{ dmCommon("ok") }}</el-button>
  185. <el-button @click="cancel">{{ dmCommon("cancel") }}</el-button>
  186. </div>
  187. </el-dialog>
  188. <el-dialog :title="dmCommon('viewTitle')" :visible.sync="viewOpen" width="720px" append-to-body>
  189. <el-form label-width="168px" size="small">
  190. <el-row :gutter="16">
  191. <el-col :span="12">
  192. <el-form-item :label="dmT('formStatYear')">
  193. <span>{{ viewRow.statYear || dash }}</span>
  194. </el-form-item>
  195. </el-col>
  196. <el-col :span="12">
  197. <el-form-item :label="dmT('formStatMonth')">
  198. <span>{{ formatStatMonth(viewRow.statMonth) }}</span>
  199. </el-form-item>
  200. </el-col>
  201. <el-col :span="12">
  202. <el-form-item :label="dmT('formTown')">
  203. <span>{{ viewRow.townName || dash }}</span>
  204. </el-form-item>
  205. </el-col>
  206. <el-col v-for="field in viewFields" :key="'v-' + field.prop" :span="12">
  207. <el-form-item :label="dmT(field.labelKey)">
  208. <span>{{ formatCount(viewRow[field.prop]) }}</span>
  209. </el-form-item>
  210. </el-col>
  211. </el-row>
  212. </el-form>
  213. <div slot="footer" class="dialog-footer">
  214. <el-button
  215. v-hasPermi="['dataModel:yakOutboundReport:edit']"
  216. type="primary"
  217. @click="handleEditFromView"
  218. >{{ dmCommon("edit") }}</el-button>
  219. <el-button @click="viewOpen = false">{{ dmCommon("close") }}</el-button>
  220. </div>
  221. </el-dialog>
  222. <el-dialog :title="dmT('importTitle')" :visible.sync="importOpen" width="480px" append-to-body @close="resetImport">
  223. <el-form ref="importFormRef" :model="importForm" label-width="100px" size="small">
  224. <el-form-item prop="statYear" :rules="importRules.statYear">
  225. <template slot="label">{{ dmT("importStatYear") }}</template>
  226. <el-select v-model="importForm.statYear" :placeholder="dmT('importStatYearPh')" style="width: 100%">
  227. <el-option v-for="y in yearOptions" :key="'i-' + y" :label="String(y)" :value="y" />
  228. </el-select>
  229. </el-form-item>
  230. <el-form-item prop="statMonth" :rules="importRules.statMonth">
  231. <template slot="label">{{ dmT("importStatMonth") }}</template>
  232. <el-select v-model="importForm.statMonth" :placeholder="dmT('importStatMonthPh')" style="width: 100%">
  233. <el-option v-for="m in monthOptions" :key="'im-' + m" :label="formatStatMonth(m)" :value="m" />
  234. </el-select>
  235. </el-form-item>
  236. <p class="yor-import-tip">{{ dmT("importTip") }}</p>
  237. <el-upload
  238. ref="importUpload"
  239. drag
  240. :limit="1"
  241. accept=".xlsx,.xls"
  242. :auto-upload="false"
  243. action="#"
  244. >
  245. <i class="el-icon-upload" />
  246. <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
  247. <div slot="tip" class="el-upload__tip">
  248. <el-link type="primary" :underline="false" @click="handleDownloadTemplate">{{ dmT("btnDownloadTemplate") }}</el-link>
  249. </div>
  250. </el-upload>
  251. </el-form>
  252. <div slot="footer" class="dialog-footer">
  253. <el-button type="primary" :loading="importing" @click="handleImportSubmit">{{ dmCommon("ok") }}</el-button>
  254. <el-button @click="importOpen = false">{{ dmCommon("cancel") }}</el-button>
  255. </div>
  256. </el-dialog>
  257. <el-dialog :title="dmT('exportTitle')" :visible.sync="exportOpen" width="480px" append-to-body @close="resetExport">
  258. <el-form ref="exportFormRef" :model="exportForm" label-width="100px" size="small">
  259. <el-form-item prop="statYear" :rules="exportRules.statYear">
  260. <template slot="label">{{ dmT("exportStatYear") }}</template>
  261. <el-select v-model="exportForm.statYear" :placeholder="dmT('exportStatYearPh')" style="width: 100%">
  262. <el-option v-for="y in yearOptions" :key="'e-' + y" :label="String(y)" :value="y" />
  263. </el-select>
  264. </el-form-item>
  265. <el-form-item prop="statMonth" :rules="exportRules.statMonth">
  266. <template slot="label">{{ dmT("exportStatMonth") }}</template>
  267. <el-select v-model="exportForm.statMonth" :placeholder="dmT('exportStatMonthPh')" style="width: 100%">
  268. <el-option v-for="m in monthOptions" :key="'em-' + m" :label="formatStatMonth(m)" :value="m" />
  269. </el-select>
  270. </el-form-item>
  271. </el-form>
  272. <div slot="footer" class="dialog-footer">
  273. <el-button type="primary" :loading="exporting" @click="handleExportSubmit">{{ dmCommon("ok") }}</el-button>
  274. <el-button @click="exportOpen = false">{{ dmCommon("cancel") }}</el-button>
  275. </div>
  276. </el-dialog>
  277. </div>
  278. </template>
  279. <script>
  280. import dataModelLocaleMixin from "@/mixins/dataModelLocaleMixin"
  281. import {
  282. listYakOutboundReport,
  283. getYakOutboundReport,
  284. addYakOutboundReport,
  285. updateYakOutboundReport,
  286. delYakOutboundReport,
  287. listYakOutboundReportYearOptions,
  288. listYakOutboundReportMonthOptions,
  289. listYakOutboundReportTownOptions,
  290. importYakOutboundReport,
  291. downloadYakOutboundReportTemplate,
  292. exportYakOutboundReport
  293. } from "@/api/dataModel/yakOutboundReport"
  294. import { blobValidate } from "@/utils/ruoyi"
  295. import { saveAs } from "file-saver"
  296. const COUNT_FIELDS = [
  297. { prop: "farmerHouseholdCount", labelKey: "formFarmerHouseholdCount", unitKey: "unitHousehold", section: "farmer" },
  298. { prop: "farmerPopulationCount", labelKey: "formFarmerPopulationCount", unitKey: "unitPerson", section: "farmer" },
  299. { prop: "yakOutboundCount", labelKey: "formYakOutboundCount", unitKey: "unitHead", section: "outbound" },
  300. { prop: "cattleOutboundCount", labelKey: "formCattleOutboundCount", unitKey: "unitHead", section: "outbound" },
  301. { prop: "sheepOutboundCount", labelKey: "formSheepOutboundCount", unitKey: "unitSheep", section: "outbound" },
  302. { prop: "goatOutboundCount", labelKey: "formGoatOutboundCount", unitKey: "unitSheep", section: "outbound" },
  303. { prop: "horseMuleOutboundCount", labelKey: "formHorseMuleOutboundCount", unitKey: "unitHorse", section: "outbound" },
  304. { prop: "donkeyOutboundCount", labelKey: "formDonkeyOutboundCount", unitKey: "unitHead", section: "outbound" },
  305. { prop: "selfConsumptionCattleCount", labelKey: "formSelfConsumptionCattleCount", unitKey: "unitHead", section: "selfConsumption" },
  306. { prop: "selfConsumptionSheepCount", labelKey: "formSelfConsumptionSheepCount", unitKey: "unitSheep", section: "selfConsumption" },
  307. { prop: "selfConsumptionGoatCount", labelKey: "formSelfConsumptionGoatCount", unitKey: "unitSheep", section: "selfConsumption" }
  308. ]
  309. const VIEW_FIELDS = [
  310. { prop: "farmerHouseholdCount", labelKey: "formFarmerHouseholdCount" },
  311. { prop: "farmerPopulationCount", labelKey: "formFarmerPopulationCount" },
  312. { prop: "yakOutboundCount", labelKey: "formYakOutboundCount" },
  313. { prop: "selfConsumptionCattleCount", labelKey: "formSelfConsumptionCattleCount" }
  314. ]
  315. function getCurrentYearMonth() {
  316. const now = new Date()
  317. return {
  318. statYear: now.getFullYear(),
  319. statMonth: now.getMonth() + 1
  320. }
  321. }
  322. function createEmptyForm() {
  323. const form = {
  324. id: undefined,
  325. statYear: undefined,
  326. statMonth: undefined,
  327. townDeptId: undefined,
  328. remark: undefined
  329. }
  330. COUNT_FIELDS.forEach((field) => {
  331. form[field.prop] = 0
  332. })
  333. return form
  334. }
  335. function normalizeCountFields(data) {
  336. const row = Object.assign(createEmptyForm(), data || {})
  337. COUNT_FIELDS.forEach((field) => {
  338. const val = row[field.prop]
  339. row[field.prop] = val == null || val === "" ? 0 : val
  340. })
  341. return row
  342. }
  343. export default {
  344. name: "YakOutboundReport",
  345. mixins: [dataModelLocaleMixin],
  346. data() {
  347. return {
  348. dmNs: "yakOutboundReport",
  349. loading: false,
  350. importing: false,
  351. exporting: false,
  352. open: false,
  353. viewOpen: false,
  354. importOpen: false,
  355. exportOpen: false,
  356. dialogEdit: false,
  357. formKey: 0,
  358. total: 0,
  359. tableList: [],
  360. yearOptions: [],
  361. monthOptions: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
  362. townOptions: [],
  363. viewRow: {},
  364. importForm: {
  365. statYear: undefined,
  366. statMonth: undefined
  367. },
  368. exportForm: {
  369. statYear: undefined,
  370. statMonth: undefined
  371. },
  372. queryParams: {
  373. pageNum: 1,
  374. pageSize: 20,
  375. statYear: undefined,
  376. statMonth: undefined,
  377. townDeptId: undefined
  378. },
  379. form: createEmptyForm()
  380. }
  381. },
  382. computed: {
  383. dash() {
  384. return this.$t("dataModel.status.dash")
  385. },
  386. farmerCountFields() {
  387. return COUNT_FIELDS.filter((field) => field.section === "farmer")
  388. },
  389. outboundCountFields() {
  390. return COUNT_FIELDS.filter((field) => field.section === "outbound")
  391. },
  392. selfConsumptionCountFields() {
  393. return COUNT_FIELDS.filter((field) => field.section === "selfConsumption")
  394. },
  395. viewFields() {
  396. return VIEW_FIELDS
  397. },
  398. dialogTitle() {
  399. return this.dialogEdit ? this.dmT("dialogEdit") : this.dmT("dialogAdd")
  400. },
  401. formRules() {
  402. return {
  403. statYear: [{ required: true, message: this.dmT("ruleStatYearRequired"), trigger: "change" }],
  404. statMonth: [
  405. { required: true, message: this.dmT("ruleStatMonthRequired"), trigger: "change" },
  406. { validator: this.validateFormStatMonth, trigger: "change" }
  407. ],
  408. townDeptId: [{ required: true, message: this.dmT("ruleTownRequired"), trigger: "change" }],
  409. count: [{ validator: this.validateOptionalCount, trigger: "change" }],
  410. remark: [{ max: 500, message: this.dmCommon("ruleLen500"), trigger: "blur" }]
  411. }
  412. },
  413. importRules() {
  414. return {
  415. statYear: [{ required: true, message: this.dmT("ruleStatYearRequired"), trigger: "change" }],
  416. statMonth: [
  417. { required: true, message: this.dmT("ruleStatMonthRequired"), trigger: "change" },
  418. { validator: this.validateImportStatMonth, trigger: "change" }
  419. ]
  420. }
  421. },
  422. exportRules() {
  423. return {
  424. statYear: [{ required: true, message: this.dmT("exportStatYearRequired"), trigger: "change" }],
  425. statMonth: [
  426. { required: true, message: this.dmT("exportStatMonthRequired"), trigger: "change" },
  427. { validator: this.validateExportStatMonth, trigger: "change" }
  428. ]
  429. }
  430. }
  431. },
  432. created() {
  433. this.loadOptions()
  434. this.getList()
  435. },
  436. methods: {
  437. loadOptions() {
  438. listYakOutboundReportYearOptions().then((res) => {
  439. this.yearOptions = Array.isArray(res.data) ? res.data : []
  440. })
  441. listYakOutboundReportMonthOptions().then((res) => {
  442. if (Array.isArray(res.data) && res.data.length) {
  443. this.monthOptions = res.data
  444. }
  445. })
  446. listYakOutboundReportTownOptions().then((res) => {
  447. this.townOptions = Array.isArray(res.data) ? res.data : []
  448. })
  449. },
  450. formatStatMonth(month) {
  451. if (month == null || month === "") {
  452. return this.dash
  453. }
  454. return this.dmT("statMonthLabel", { month })
  455. },
  456. isFutureStatPeriod(statYear, statMonth) {
  457. if (statYear == null || statMonth == null) {
  458. return false
  459. }
  460. const now = new Date()
  461. const currentYear = now.getFullYear()
  462. const currentMonth = now.getMonth() + 1
  463. if (statYear > currentYear) {
  464. return true
  465. }
  466. return statYear === currentYear && statMonth > currentMonth
  467. },
  468. validateFormStatMonth(rule, value, callback) {
  469. if (this.isFutureStatPeriod(this.form.statYear, value)) {
  470. callback(new Error(this.dmT("ruleStatMonthFuture")))
  471. return
  472. }
  473. callback()
  474. },
  475. validateImportStatMonth(rule, value, callback) {
  476. if (this.isFutureStatPeriod(this.importForm.statYear, value)) {
  477. callback(new Error(this.dmT("ruleStatMonthFuture")))
  478. return
  479. }
  480. callback()
  481. },
  482. validateExportStatMonth(rule, value, callback) {
  483. if (this.isFutureStatPeriod(this.exportForm.statYear, value)) {
  484. callback(new Error(this.dmT("ruleStatMonthFuture")))
  485. return
  486. }
  487. callback()
  488. },
  489. formatCount(val) {
  490. if (val == null || val === "") {
  491. return this.dash
  492. }
  493. return val
  494. },
  495. validateOptionalCount(rule, value, callback) {
  496. if (value == null || value === "") {
  497. callback()
  498. return
  499. }
  500. if (!Number.isInteger(value) || value < 0) {
  501. callback(new Error(this.dmT("ruleCountNonNegative")))
  502. return
  503. }
  504. callback()
  505. },
  506. buildListQuery() {
  507. const q = {
  508. pageNum: this.queryParams.pageNum,
  509. pageSize: this.queryParams.pageSize
  510. }
  511. if (this.queryParams.statYear != null) {
  512. q.statYear = this.queryParams.statYear
  513. }
  514. if (this.queryParams.statMonth != null) {
  515. q.statMonth = this.queryParams.statMonth
  516. }
  517. if (this.queryParams.townDeptId != null) {
  518. q.townDeptId = this.queryParams.townDeptId
  519. }
  520. return q
  521. },
  522. buildPayload() {
  523. const remarkText = this.form.remark != null ? String(this.form.remark).trim() : ""
  524. const payload = {
  525. statYear: this.form.statYear,
  526. statMonth: this.form.statMonth,
  527. townDeptId: this.form.townDeptId,
  528. remark: remarkText !== "" ? remarkText : undefined
  529. }
  530. COUNT_FIELDS.forEach((field) => {
  531. payload[field.prop] = this.form[field.prop]
  532. })
  533. return payload
  534. },
  535. getList() {
  536. this.loading = true
  537. listYakOutboundReport(this.buildListQuery())
  538. .then((res) => {
  539. this.tableList = res.rows || []
  540. this.total = res.total != null ? res.total : 0
  541. })
  542. .finally(() => {
  543. this.loading = false
  544. })
  545. },
  546. handleQuery() {
  547. this.queryParams.pageNum = 1
  548. this.getList()
  549. },
  550. resetQuery() {
  551. this.queryParams.statYear = undefined
  552. this.queryParams.statMonth = undefined
  553. this.queryParams.townDeptId = undefined
  554. this.resetForm("queryForm")
  555. this.handleQuery()
  556. },
  557. handleAdd() {
  558. this.formKey += 1
  559. const current = getCurrentYearMonth()
  560. this.form = createEmptyForm()
  561. this.form.statYear = current.statYear
  562. this.form.statMonth = current.statMonth
  563. this.dialogEdit = false
  564. this.open = true
  565. this.$nextTick(() => {
  566. if (this.$refs.form) {
  567. this.$refs.form.clearValidate()
  568. }
  569. })
  570. },
  571. handleEdit(row) {
  572. getYakOutboundReport(row.id).then((res) => {
  573. const data = res.data || {}
  574. this.formKey += 1
  575. this.dialogEdit = true
  576. this.form = normalizeCountFields(data)
  577. this.open = true
  578. this.viewOpen = false
  579. this.$nextTick(() => {
  580. if (this.$refs.form) {
  581. this.$refs.form.clearValidate()
  582. }
  583. })
  584. })
  585. },
  586. handleEditFromView() {
  587. this.handleEdit(this.viewRow)
  588. },
  589. handleView(row) {
  590. getYakOutboundReport(row.id).then((res) => {
  591. this.viewRow = res.data || {}
  592. this.viewOpen = true
  593. })
  594. },
  595. handleDelete(row) {
  596. this.$modal
  597. .confirm(this.dmT("confirmDelete"))
  598. .then(() => delYakOutboundReport(row.id))
  599. .then(() => {
  600. this.$modal.msgSuccess(this.dmCommon("msgDelOk"))
  601. this.viewOpen = false
  602. this.getList()
  603. })
  604. .catch(() => {})
  605. },
  606. submitForm() {
  607. this.$refs.form.validate((valid) => {
  608. if (!valid) {
  609. return
  610. }
  611. const payload = this.buildPayload()
  612. if (this.dialogEdit) {
  613. payload.id = this.form.id
  614. updateYakOutboundReport(payload).then(() => {
  615. this.$modal.msgSuccess(this.dmCommon("msgModOk"))
  616. this.open = false
  617. this.getList()
  618. })
  619. } else {
  620. addYakOutboundReport(payload).then(() => {
  621. this.$modal.msgSuccess(this.dmCommon("msgAddOk"))
  622. this.open = false
  623. this.getList()
  624. })
  625. }
  626. })
  627. },
  628. cancel() {
  629. this.open = false
  630. this.form = createEmptyForm()
  631. },
  632. handleImportOpen() {
  633. const current = getCurrentYearMonth()
  634. this.importForm.statYear = current.statYear
  635. this.importForm.statMonth = current.statMonth
  636. this.importOpen = true
  637. this.$nextTick(() => {
  638. this.resetImportFiles()
  639. })
  640. },
  641. resetImportFiles() {
  642. if (this.$refs.importUpload) {
  643. this.$refs.importUpload.clearFiles()
  644. }
  645. },
  646. resetImport() {
  647. this.importing = false
  648. this.importForm.statYear = undefined
  649. this.importForm.statMonth = undefined
  650. this.resetImportFiles()
  651. },
  652. handleDownloadTemplate() {
  653. const filename = `yak_outbound_report_template_${new Date().getTime()}.xlsx`
  654. this.$modal.loading("正在下载模板,请稍候")
  655. downloadYakOutboundReportTemplate()
  656. .then(async (data) => {
  657. if (blobValidate(data)) {
  658. saveAs(new Blob([data], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }), filename)
  659. } else {
  660. const resText = await data.text()
  661. const rspObj = JSON.parse(resText)
  662. this.$modal.msgError(rspObj.msg || "下载模板失败")
  663. }
  664. })
  665. .catch(() => {
  666. this.$modal.msgError("下载模板失败")
  667. })
  668. .finally(() => {
  669. this.$modal.closeLoading()
  670. })
  671. },
  672. handleExportOpen() {
  673. const current = getCurrentYearMonth()
  674. this.exportForm.statYear = current.statYear
  675. this.exportForm.statMonth = current.statMonth
  676. this.exportOpen = true
  677. this.$nextTick(() => {
  678. if (this.$refs.exportFormRef) {
  679. this.$refs.exportFormRef.clearValidate()
  680. }
  681. })
  682. },
  683. resetExport() {
  684. this.exporting = false
  685. this.exportForm.statYear = undefined
  686. this.exportForm.statMonth = undefined
  687. },
  688. handleExportSubmit() {
  689. this.$refs.exportFormRef.validate((valid) => {
  690. if (!valid) {
  691. return
  692. }
  693. const statYear = this.exportForm.statYear
  694. const statMonth = this.exportForm.statMonth
  695. if (!this.yearOptions.includes(statYear) || !this.monthOptions.includes(statMonth)) {
  696. this.$modal.msgError(this.dmT("exportStatYearInvalid"))
  697. return
  698. }
  699. const filename = `${statYear}年${statMonth}月牦牛出栏数据.xlsx`
  700. this.exporting = true
  701. this.$modal.loading(this.dmT("exportLoading"))
  702. exportYakOutboundReport(statYear, statMonth)
  703. .then(async (data) => {
  704. if (blobValidate(data)) {
  705. saveAs(new Blob([data], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }), filename)
  706. this.exportOpen = false
  707. } else {
  708. const resText = await data.text()
  709. const rspObj = JSON.parse(resText)
  710. this.$modal.msgError(rspObj.msg || this.dmT("exportFail"))
  711. }
  712. })
  713. .catch(() => {
  714. this.$modal.msgError(this.dmT("exportFail"))
  715. })
  716. .finally(() => {
  717. this.exporting = false
  718. this.$modal.closeLoading()
  719. })
  720. })
  721. },
  722. handleImportSubmit() {
  723. this.$refs.importFormRef.validate((valid) => {
  724. if (!valid) {
  725. return
  726. }
  727. const upload = this.$refs.importUpload
  728. const files = upload && upload.uploadFiles
  729. if (!files || !files.length) {
  730. this.$modal.msgError("请选择要上传的文件")
  731. return
  732. }
  733. const raw = files[0].raw
  734. const name = (raw.name || "").toLowerCase()
  735. if (!name.endsWith(".xlsx") && !name.endsWith(".xls")) {
  736. this.$modal.msgError('请选择后缀为 "xls" 或 "xlsx" 的文件')
  737. return
  738. }
  739. this.importing = true
  740. this.$modal.loading(this.dmT("importLoading"))
  741. importYakOutboundReport(raw, this.importForm.statYear, this.importForm.statMonth)
  742. .then((res) => {
  743. this.importOpen = false
  744. this.showImportResult(res.data || {})
  745. this.getList()
  746. })
  747. .finally(() => {
  748. this.importing = false
  749. this.$modal.closeLoading()
  750. this.resetImportFiles()
  751. })
  752. })
  753. },
  754. showImportResult(data) {
  755. const insert = data.insertCount != null ? data.insertCount : 0
  756. const update = data.updateCount != null ? data.updateCount : 0
  757. const fail = data.failCount != null ? data.failCount : 0
  758. let html = this.dmT("importSummary", { insert, update, fail })
  759. if (data.warnMessages && data.warnMessages.length) {
  760. html += `<p><strong>${this.dmT("importWarnTitle")}</strong></p><p>${data.warnMessages.join("<br/>")}</p>`
  761. }
  762. if (data.failMessages && data.failMessages.length) {
  763. html += `<p><strong>${this.dmT("importFailTitle")}</strong></p><p>${data.failMessages.join("<br/>")}</p>`
  764. }
  765. this.$alert(
  766. `<div style='overflow:auto;overflow-x:hidden;max-height:70vh;padding:10px 20px 0;'>${html}</div>`,
  767. this.dmT("importTitle"),
  768. { dangerouslyUseHTMLString: true }
  769. )
  770. }
  771. }
  772. }
  773. </script>
  774. <style scoped>
  775. .yor-section-title {
  776. margin: 4px 0 12px;
  777. padding-left: 8px;
  778. font-size: 14px;
  779. font-weight: 600;
  780. color: #303133;
  781. border-left: 3px solid #409eff;
  782. }
  783. .yor-import-tip {
  784. margin: 0 0 12px;
  785. font-size: 12px;
  786. color: #909399;
  787. line-height: 1.6;
  788. }
  789. .dm-input-with-unit {
  790. display: flex;
  791. align-items: center;
  792. width: 100%;
  793. }
  794. .dm-input-with-unit__input {
  795. flex: 1;
  796. width: 0;
  797. }
  798. .dm-input-with-unit__input ::v-deep.el-input-number {
  799. width: 100%;
  800. }
  801. .dm-input-with-unit__suffix {
  802. flex-shrink: 0;
  803. margin-left: 8px;
  804. color: #606266;
  805. font-size: 14px;
  806. white-space: nowrap;
  807. }
  808. </style>