stocktakeManage.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. <template>
  2. <div>
  3. <div class="stocktakeManage">
  4. <el-row type="flex">
  5. <el-col class="button">
  6. <div class="title">试剂耗材相关信息</div>
  7. <div v-if="isRead" />
  8. <div v-else>
  9. <el-button
  10. type="primary"
  11. size="mini"
  12. icon="ibps-icon-import"
  13. @click="handleImport"
  14. >导入</el-button
  15. >
  16. <el-button
  17. type="warning"
  18. size="mini"
  19. icon="ibps-icon-sign-out"
  20. @click="handleDownload"
  21. >
  22. 导出</el-button
  23. >
  24. <el-button
  25. type="danger"
  26. size="mini"
  27. icon="ibps-icon-remove"
  28. @click="handleDelete"
  29. >
  30. 删除</el-button
  31. >
  32. </div>
  33. </el-col>
  34. </el-row>
  35. <el-row type="flex">
  36. <el-col>
  37. <el-table
  38. ref="external"
  39. :data="reagentBatchDataFilter"
  40. @selection-change="handleSelectionChange"
  41. >
  42. <el-table-column type="selection" width="55" />
  43. <el-table-column
  44. label="试剂耗材名称"
  45. width="120"
  46. prop="shiJiMingCheng"
  47. />
  48. <el-table-column label="编码" prop="bianMa" />
  49. <el-table-column label="批号" prop="piHao" />
  50. <el-table-column label="类别" prop="leiBie" />
  51. <el-table-column label="规格" prop="guiGe" />
  52. <el-table-column label="单位" prop="danWei" />
  53. <el-table-column label="有效期" prop="youXiaoQi" />
  54. <el-table-column
  55. label="生产商/供应商"
  56. width="120"
  57. prop="shengChanShang"
  58. />
  59. <el-table-column label="库存量" prop="kuCunLiang" />
  60. <el-table-column label="上月结存" prop="shangYueJieCun" />
  61. <el-table-column label="本月入库" prop="benYueRuKu" />
  62. <el-table-column label="本月出库" prop="benYueChuKu" />
  63. <el-table-column label="本月结存" prop="benYueJieCun" />
  64. <el-table-column
  65. fixed="right"
  66. width="110"
  67. label="盘存数量"
  68. prop="panCunShuLiang"
  69. >
  70. <template slot-scope="{ row }">
  71. <el-input
  72. v-if="!isRead"
  73. v-model="row.panCunShuLiang"
  74. size="mini"
  75. type="number"
  76. placeholder="请输入"
  77. />
  78. <span v-else>{{ row.panCunShuLiang }}</span>
  79. </template>
  80. </el-table-column>
  81. <el-table-column
  82. fixed="right"
  83. width="120"
  84. label="存储位置"
  85. prop="cunChuWeiZhi"
  86. >
  87. <template slot-scope="scope">
  88. <ibps-custom-dialog
  89. v-model="scope.row.cunChuWeiZhi"
  90. size="mini"
  91. template-key="ckglpzlbdhkk"
  92. multiple
  93. disabled
  94. type="dialog"
  95. class="custom-dialog"
  96. placeholder="请选择"
  97. icon="el-icon-search"
  98. style="width: 100%"
  99. />
  100. </template>
  101. </el-table-column>
  102. <el-table-column
  103. fixed="right"
  104. width="120"
  105. label="备注"
  106. prop="beiZhu"
  107. >
  108. <template slot-scope="{ row }">
  109. <el-input
  110. v-if="!isRead"
  111. v-model="row.beiZhu"
  112. size="mini"
  113. placeholder="请输入"
  114. />
  115. <span v-else>{{ row.beiZhu }}</span>
  116. </template>
  117. </el-table-column>
  118. </el-table>
  119. <el-pagination
  120. layout="total,sizes,prev, pager, next,jumper"
  121. :current-page="requestPage.pageNo"
  122. :page-size="requestPage.limit"
  123. :page-sizes="[10, 15, 20, 30, 50, 100]"
  124. :total="reagentBatchData.length"
  125. @size-change="handleSizeChange"
  126. @current-change="handleCurrentChange"
  127. />
  128. </el-col>
  129. </el-row>
  130. </div>
  131. <import-table
  132. :visible="importTableDialogVisible"
  133. title="导入"
  134. @close="(visible) => (importTableDialogVisible = visible)"
  135. @action-event="handleImportTableActionEvent"
  136. />
  137. </div>
  138. </template>
  139. <script>
  140. import { cloneDeep } from 'lodash'
  141. import request from '@/utils/request'
  142. import { FORM_URL } from '@/api/baseUrl'
  143. import IbpsExport from '@/plugins/export'
  144. import IbpsImport from '@/plugins/import'
  145. import importTable from '@/business/platform/form/formrender/dynamic-form/components/import-table'
  146. import ActionUtils from '@/utils/action'
  147. export default {
  148. components: {
  149. IbpsCustomDialog: () =>
  150. import('@/business/platform/data/templaterender/custom-dialog'),
  151. importTable
  152. },
  153. props: {
  154. formData: {
  155. type: Object,
  156. default: () => ({})
  157. },
  158. readonly: {
  159. type: Boolean,
  160. default: false
  161. },
  162. params: {
  163. type: Object,
  164. default: () => ({})
  165. }
  166. },
  167. data() {
  168. return {
  169. reagentBatchData: [],
  170. requestPage: {
  171. limit: 10,
  172. pageNo: 1
  173. },
  174. multipleSelection: [],
  175. isRead: false,
  176. storeHouseList: [],
  177. importTableDialogVisible: false
  178. }
  179. },
  180. computed: {
  181. reagentBatchDataFilter() {
  182. return this.reagentBatchData.slice(
  183. (this.requestPage.pageNo - 1) * this.requestPage.limit,
  184. (this.requestPage.pageNo - 1) * this.requestPage.limit +
  185. this.requestPage.limit
  186. )
  187. }
  188. },
  189. watch: {
  190. 'formData.sjhcpdzb': {
  191. handler(value, old) {
  192. if (value && value.length) {
  193. this.reagentBatchData = value
  194. }
  195. },
  196. immediate: true
  197. },
  198. reagentBatchData: {
  199. handler(value, old) {
  200. this.$emit('change-data', 'sjhcpdzb', value)
  201. },
  202. deep: true
  203. }
  204. },
  205. mounted() {
  206. this.getStoreHouse()
  207. this.isRead = this.readonly
  208. setTimeout(() => {
  209. this.$watch(
  210. 'formData.cangKu',
  211. function (val, oldVal) {
  212. if (val) {
  213. this.initData(this.formData.yueFen?.slice(0, 7), val)
  214. } else {
  215. this.initData(this.formData.yueFen?.slice(0, 7), '')
  216. }
  217. },
  218. {
  219. immediate: !this.formData?.id && !this.readonly
  220. }
  221. )
  222. }, 1000)
  223. // this.$watch('formData.yueFen', function (val, oldVal) {
  224. // if (val) {
  225. // this.initData(val.slice(0, 7), this.formData?.cangKu)
  226. // } else {
  227. // this.initData('', this.formData?.cangKu)
  228. // }
  229. // }, {
  230. // immediate: !this.formData?.id
  231. // })
  232. },
  233. methods: {
  234. getStoreHouse() {
  235. const { first, second } = this.$store.getters.level || {}
  236. // const sql = `select cang_ku_ming_chen mingChen, id_ from t_ckglpzb where di_dian_ = '${second || first}'`
  237. this.$common
  238. .request('query', {
  239. key: 'getStockConfig1',
  240. params: [second || first]
  241. })
  242. .then((res) => {
  243. this.storeHouseList = res?.variables?.data || []
  244. })
  245. },
  246. getDataValue() {
  247. const columns = [
  248. {
  249. field_name: 'shiJiMingCheng',
  250. label: '试剂耗材名称',
  251. name: 'shiJiMingCheng'
  252. },
  253. {
  254. field_name: 'bianMa',
  255. label: '编码',
  256. name: 'bianMa'
  257. },
  258. {
  259. field_name: 'piHao',
  260. label: '批号',
  261. name: 'piHao'
  262. },
  263. {
  264. field_name: 'leiBie',
  265. label: '类别',
  266. name: 'leiBie'
  267. },
  268. {
  269. field_name: 'guiGe',
  270. label: '规格',
  271. name: 'guiGe'
  272. },
  273. {
  274. field_name: 'danWei',
  275. label: '单位',
  276. name: 'danWei'
  277. },
  278. {
  279. field_name: 'youXiaoQi',
  280. label: '有效期',
  281. name: 'youXiaoQi'
  282. },
  283. {
  284. field_name: 'shengChanShang',
  285. label: '生产商/供应商',
  286. name: 'shengChanShang'
  287. },
  288. {
  289. field_name: 'kuCunLiang',
  290. label: '库存量',
  291. name: 'kuCunLiang'
  292. },
  293. {
  294. field_name: 'shangYueJieCun',
  295. label: '上月结存',
  296. name: 'shangYueJieCun'
  297. },
  298. {
  299. field_name: 'benYueRuKu',
  300. label: '本月入库',
  301. name: 'benYueRuKu'
  302. },
  303. {
  304. field_name: 'benYueChuKu',
  305. label: '本月出库',
  306. name: 'benYueChuKu'
  307. },
  308. {
  309. field_name: 'benYueJieCun',
  310. label: '本月结存',
  311. name: 'benYueJieCun'
  312. },
  313. {
  314. field_name: 'panCunShuLiang',
  315. label: '盘存数量',
  316. name: 'panCunShuLiang'
  317. },
  318. {
  319. field_name: 'cunChuWeiZhi',
  320. label: '存储位置',
  321. name: 'cunChuWeiZhi'
  322. },
  323. {
  324. field_name: 'beiZhu',
  325. label: '备注',
  326. name: 'beiZhu'
  327. }
  328. ]
  329. return columns
  330. },
  331. initData(date, ckId) {
  332. request({
  333. url:
  334. FORM_URL() +
  335. `/reagent/reagentConsumablesInventory/takeStock?date=${date}&ckId=${ckId}`,
  336. method: 'post'
  337. })
  338. .then((res) => {
  339. const { data = [] } = res
  340. const arry = []
  341. const oldData =
  342. this.reagentBatchData?.length > 0
  343. ? JSON.parse(JSON.stringify(this.reagentBatchData))
  344. : []
  345. data.forEach((item) => {
  346. const temp = oldData.find((b) => b.kuCunId == item.id_)
  347. arry.push(
  348. temp || {
  349. kuCunId: item.id_,
  350. shiJiMingCheng: item.ming_cheng_,
  351. bianMa: item.bian_ma,
  352. piHao: item.batch_num,
  353. leiBie: item.lei_bie_,
  354. guiGe: item.gui_ge_,
  355. danWei: item.dan_wei_,
  356. youXiaoQi: item.exp_date,
  357. shengChanShang: item.chang_jia_ + '/' + item.gong_ying_shang_,
  358. kuCunLiang: item.quantity || 0,
  359. panCunShuLiang: '',
  360. beiZhu: '',
  361. cunChuWeiZhi: item.position,
  362. shangYueJieCun: item.lastMonth || 0,
  363. benYueRuKu: item.currIn || 0,
  364. benYueChuKu: item.currOut || 0,
  365. benYueJieCun: item.currBalance || 0
  366. }
  367. )
  368. })
  369. this.reagentBatchData = arry
  370. })
  371. .catch(() => {
  372. this.reagentBatchData = []
  373. })
  374. },
  375. handleDownload() {
  376. const arry = cloneDeep(this.reagentBatchData) || []
  377. arry.forEach((item) => {
  378. const result = this.storeHouseList.find(
  379. (el) => el.id_ == item.cunChuWeiZhi
  380. )
  381. item.cunChuWeiZhi = result?.mingChen || ''
  382. })
  383. IbpsExport.excel({
  384. columns: this.getDataValue(),
  385. data: arry,
  386. nameKey: 'name',
  387. title: '盘点试剂耗材'
  388. }).then(() => {
  389. ActionUtils.success('导出成功')
  390. })
  391. },
  392. handleSelectionChange(val) {
  393. this.multipleSelection = val
  394. },
  395. // 导入
  396. handleImport() {
  397. this.importTableDialogVisible = true
  398. },
  399. getKeys(data) {
  400. return Array.isArray(data)
  401. ? data.reduce((acc, item) => ({ ...acc, [item.label]: item.name }), {})
  402. : {}
  403. },
  404. handleImportTableActionEvent(file, options) {
  405. IbpsImport.xlsx(file, options).then(({ header, results }) => {
  406. const list = []
  407. const keys = this.getKeys(this.getDataValue())
  408. results.forEach((item) => {
  409. const obj = {}
  410. Object.keys(item).forEach((key) => {
  411. if (keys[key]) {
  412. obj[keys[key]] = item[key]
  413. }
  414. })
  415. list.push(obj)
  416. })
  417. list.forEach((item) => {
  418. const result = this.storeHouseList.filter(
  419. (el) => el.mingChen == item.cunChuWeiZhi
  420. )
  421. item.cunChuWeiZhi = result?.length > 0 ? result[0].id_ : ''
  422. })
  423. setTimeout(() => {
  424. this.$nextTick(() => {
  425. this.reagentBatchData = this.mergeArraysWithFirstMatch(
  426. cloneDeep(this.reagentBatchData),
  427. list
  428. )
  429. this.$refs.external && this.$refs.external.$forceUpdate()
  430. })
  431. })
  432. this.importTableDialogVisible = false
  433. })
  434. },
  435. mergeArraysWithFirstMatch(A, B) {
  436. // 1. 构建 B 的映射表(仅保留第一条匹配记录)
  437. const bMap = B.reduce((map, item) => {
  438. const key = `${item.bianMa}_${item.piHao}_${item.cunChuWeiZhi}`
  439. // 仅当键不存在时才添加,确保保留第一条
  440. if (!map.has(key)) {
  441. map.set(key, item)
  442. }
  443. return map
  444. }, new Map())
  445. // 2. 覆盖逻辑(与之前相同)
  446. return A.map((aItem) => {
  447. const key = `${aItem.bianMa}_${aItem.piHao}_${aItem.cunChuWeiZhi}`
  448. if (bMap.has(key)) {
  449. // 合并 B 的记录,但保留 A 的 id
  450. return {
  451. ...bMap.get(key), // 先展开 B 的记录(覆盖其他字段)
  452. kuCunId: aItem.kuCunId // 显式保留 A 的 id
  453. }
  454. }
  455. return aItem // 无匹配则返回原记录
  456. })
  457. },
  458. // 删除
  459. handleDelete() {
  460. this.$confirm('确定删除当前选中数据?', '提示', {
  461. confirmButtonText: '确定',
  462. cancelButtonText: '取消',
  463. type: 'warning'
  464. }).then(() => {
  465. if (this.multipleSelection.length > 0) {
  466. this.reagentBatchData = this.reagentBatchData.filter(
  467. (row) => !this.multipleSelection.includes(row)
  468. )
  469. } else {
  470. this.$message.warning('请选择数据')
  471. }
  472. })
  473. },
  474. // 当前页码改变
  475. handleCurrentChange(val) {
  476. this.requestPage.pageNo = val
  477. },
  478. // 页码选择器改变
  479. handleSizeChange(val) {
  480. this.requestPage.limit = val
  481. this.requestPage.pageNo = 1
  482. }
  483. }
  484. }
  485. </script>
  486. <style lang="scss" scoped>
  487. .stocktakeManage {
  488. margin-bottom: 20px;
  489. .button {
  490. display: flex;
  491. justify-content: space-between;
  492. padding: 0px 0px 0px 15px;
  493. background: #f0ffff;
  494. .title {
  495. color: #999;
  496. font-size: 12px;
  497. font-weight: bold;
  498. margin-bottom: 0;
  499. }
  500. .el-button {
  501. margin: 0;
  502. }
  503. }
  504. }
  505. </style>