stokledger.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. <template>
  2. <ele-modal
  3. :visible.sync="visible"
  4. title="物品信息"
  5. width="80%"
  6. :close-on-click-modal="true"
  7. :close-on-press-escape="false"
  8. :maxable="true"
  9. append-to-body
  10. >
  11. <el-form :model="searchForm" label-width="100px">
  12. <el-row :gutter="20">
  13. <el-col :span="6">
  14. <el-form-item label="列表维度:" prop="dimension">
  15. <template>
  16. <el-select
  17. style="width: 100%"
  18. @change="changeDimensionHandler"
  19. v-model="dimension"
  20. placeholder="请选择"
  21. >
  22. <!-- <el-option label="物料维度" value="4"> </el-option> -->
  23. <el-option label="包装维度" value="3"> </el-option>
  24. <el-option label="批次维度" value="2"> </el-option>
  25. <el-option label="物品维度" value="1"> </el-option>
  26. </el-select>
  27. </template>
  28. </el-form-item>
  29. </el-col>
  30. <el-col :span="6">
  31. <el-form-item label="所属工厂" prop="factoryId">
  32. <el-select
  33. filterable
  34. placeholder="请选择"
  35. v-model="searchForm.factoryId"
  36. clearable
  37. class="w100"
  38. @change="handleFactoryChange"
  39. >
  40. <el-option
  41. v-for="item in factoryList"
  42. :key="item.id"
  43. :label="item.name"
  44. :value="item.id"
  45. ></el-option>
  46. </el-select>
  47. </el-form-item>
  48. </el-col>
  49. <el-col :span="6">
  50. <el-form-item label="仓库">
  51. <el-select
  52. clearable
  53. filterable
  54. style="width: 100%"
  55. v-model="searchForm.warehouseId"
  56. placeholder="请选择"
  57. >
  58. <el-option
  59. v-for="item in warehouseOptions"
  60. :label="item.name"
  61. :value="item.id"
  62. :key="item.id"
  63. >
  64. </el-option>
  65. </el-select>
  66. </el-form-item>
  67. </el-col>
  68. <el-col :span="6">
  69. <el-form-item label="物品编码:">
  70. <el-input
  71. type="text"
  72. placeholder="搜索物品编码"
  73. v-model="searchForm.categoryCode"
  74. ></el-input>
  75. </el-form-item>
  76. </el-col>
  77. <el-col :span="6">
  78. <el-form-item label="物品名称:">
  79. <el-input
  80. type="text"
  81. placeholder="搜索物品名称"
  82. v-model="searchForm.categoryName"
  83. ></el-input>
  84. </el-form-item>
  85. </el-col>
  86. <el-col :span="6">
  87. <el-form-item label="批次号:">
  88. <el-input
  89. type="text"
  90. placeholder="搜索批次号"
  91. v-model="searchForm.batchNo"
  92. ></el-input>
  93. </el-form-item>
  94. </el-col>
  95. <el-col :span="6">
  96. <el-form-item label="刻码">
  97. <el-input
  98. type="text"
  99. placeholder="搜索物品刻码"
  100. v-model="searchForm.engrave"
  101. ></el-input>
  102. </el-form-item>
  103. </el-col>
  104. <el-col :span="6">
  105. <el-form-item label="物料代号">
  106. <el-input
  107. type="text"
  108. placeholder="搜索物料代号"
  109. v-model="searchForm.materielDesignation"
  110. ></el-input>
  111. </el-form-item>
  112. </el-col>
  113. <el-col :span="6">
  114. <el-form-item label="发货码">
  115. <el-input
  116. type="text"
  117. placeholder="搜索发货码"
  118. v-model="searchForm.barcodes"
  119. ></el-input>
  120. </el-form-item>
  121. </el-col>
  122. <el-col :span="6">
  123. <el-form-item label="牌号">
  124. <el-input
  125. type="text"
  126. placeholder="搜索牌号"
  127. v-model="searchForm.brandNum"
  128. ></el-input>
  129. </el-form-item>
  130. </el-col>
  131. <el-col>
  132. <div style="float: right">
  133. <el-button type="primary" @click="doSearch">搜索</el-button>
  134. <el-button icon="el-icon-refresh-left" @click="reset"
  135. >重置</el-button
  136. >
  137. </div>
  138. </el-col>
  139. </el-row>
  140. </el-form>
  141. <el-container class="assets-dialog">
  142. <el-aside width="200px" class="wrapper-assets">
  143. <AssetTree
  144. ref="treeList"
  145. :treeIds="treeIds"
  146. @handleNodeClick="handleNodeClick"
  147. />
  148. </el-aside>
  149. <el-main>
  150. <el-table
  151. ref="multipleTable"
  152. :data="tableData"
  153. tooltip-effect="dark"
  154. height="50vh"
  155. border
  156. row-key="id"
  157. style="width: 100%"
  158. :header-cell-style="{ background: '#F0F3F3', border: 'none' }"
  159. @selection-change="handleSelectionChange"
  160. :header-cell-class-name="hideHeaderCheckbox"
  161. >
  162. <el-table-column
  163. type="selection"
  164. :reserve-selection="true"
  165. width="55"
  166. align="center"
  167. fixed="left"
  168. :selectable="
  169. (row) => (dimension != 3 ? true : checkSelectable(row))
  170. "
  171. >
  172. </el-table-column>
  173. <el-table-column label="序号" type="index" width="50" fixed="left">
  174. </el-table-column>
  175. <el-table-column
  176. v-if="dimension != 4"
  177. label="抽取数量"
  178. type="index"
  179. width="100"
  180. >
  181. <template slot-scope="{ row }">
  182. <el-input
  183. type="text"
  184. placeholder="请输入"
  185. v-model="row.outboundNum"
  186. @input="handleInput(row, $event)"
  187. :disabled="dimension == '3'"
  188. ></el-input>
  189. </template>
  190. </el-table-column>
  191. <el-table-column
  192. prop="categoryCode"
  193. label="物品编码"
  194. min-width="120"
  195. fixed="left"
  196. :show-overflow-tooltip="true"
  197. ></el-table-column>
  198. <el-table-column
  199. prop="categoryName"
  200. width="200"
  201. label="物品名称"
  202. fixed="left"
  203. :show-overflow-tooltip="true"
  204. ></el-table-column>
  205. <el-table-column
  206. prop="brandNum"
  207. label="牌号"
  208. :show-overflow-tooltip="true"
  209. ></el-table-column>
  210. <el-table-column
  211. prop="categoryModel"
  212. label="型号"
  213. :show-overflow-tooltip="true"
  214. ></el-table-column>
  215. <el-table-column
  216. prop="specification"
  217. label="规格"
  218. :show-overflow-tooltip="true"
  219. ></el-table-column>
  220. <template v-if="dimension == 1">
  221. <el-table-column
  222. v-for="(item, index) in newColumns"
  223. :label="item.label"
  224. :align="item.align"
  225. :prop="item.prop"
  226. :show-overflow-tooltip="item.showOverflowTooltip"
  227. ></el-table-column>
  228. </template>
  229. <el-table-column
  230. v-else
  231. prop="batchNo"
  232. label="批次号"
  233. key="batchNo"
  234. min-width="80"
  235. ></el-table-column>
  236. <el-table-column prop="level" label="级别"></el-table-column>
  237. <el-table-column
  238. prop="measureQuantity"
  239. label="计量数量"
  240. width="120"
  241. ></el-table-column>
  242. <el-table-column
  243. prop="measureUnit"
  244. label="计量单位"
  245. width="90"
  246. ></el-table-column>
  247. <el-table-column
  248. prop="weight"
  249. label="重量"
  250. min-width="120"
  251. ></el-table-column>
  252. <el-table-column
  253. prop="weightUnit"
  254. label="重量单位"
  255. min-width="120"
  256. ></el-table-column>
  257. <el-table-column
  258. v-if="dimension == 3"
  259. prop="packageNo"
  260. label="包装编码"
  261. min-width="120"
  262. :show-overflow-tooltip="true"
  263. ></el-table-column>
  264. <el-table-column
  265. v-if="dimension == 3"
  266. prop="packingQuantity"
  267. label="包装数量"
  268. min-width="120"
  269. :show-overflow-tooltip="true"
  270. ></el-table-column>
  271. <el-table-column
  272. v-if="dimension == 3"
  273. prop="packingUnit"
  274. label="包装单位"
  275. min-width="120"
  276. :show-overflow-tooltip="true"
  277. ></el-table-column>
  278. <el-table-column
  279. v-if="dimension == 3 || dimension == 4"
  280. label="发货条码"
  281. prop="barcodes"
  282. width="80"
  283. :show-overflow-tooltip="true"
  284. ></el-table-column>
  285. <el-table-column
  286. v-if="dimension == 4"
  287. prop="no"
  288. label="物料编码"
  289. min-width="120"
  290. :show-overflow-tooltip="true"
  291. ></el-table-column>
  292. <el-table-column
  293. v-if="dimension == 3 || dimension == 4"
  294. prop="materielDesignation"
  295. label="物料代号"
  296. min-width="100"
  297. ></el-table-column>
  298. <el-table-column
  299. v-if="dimension == 3 || dimension == 4"
  300. prop="clientCode"
  301. label="客户代号"
  302. min-width="100"
  303. ></el-table-column>
  304. <el-table-column
  305. v-if="dimension == 3 || dimension == 4"
  306. prop="engrave"
  307. label="刻码"
  308. min-width="120"
  309. ></el-table-column>
  310. <el-table-column
  311. v-if="dimension == 3 || dimension == 4"
  312. prop="warehouseName"
  313. label="仓库"
  314. min-width="200"
  315. :show-overflow-tooltip="true"
  316. ></el-table-column>
  317. <el-table-column
  318. prop="supplierName"
  319. label="供应商"
  320. min-width="200"
  321. :show-overflow-tooltip="true"
  322. ></el-table-column>
  323. <el-table-column
  324. prop="supplierCode"
  325. label="供应商代号"
  326. min-width="200"
  327. :show-overflow-tooltip="true"
  328. ></el-table-column>
  329. </el-table>
  330. <div style="text-align: right; padding: 10px">
  331. <el-pagination
  332. background
  333. layout="total, sizes, prev, pager, next, jumper"
  334. :total="total"
  335. :page-sizes="[5, 10, 20, 50]"
  336. :page-size.sync="searchForm.size"
  337. :current-page.sync="searchForm.pageNum"
  338. @current-change="handleCurrentChange"
  339. @size-change="handleSizeChange"
  340. >
  341. </el-pagination>
  342. </div>
  343. </el-main>
  344. </el-container>
  345. <div slot="footer">
  346. <el-button type="primary" @click="confirm">确定</el-button>
  347. <el-button @click="cancel">关闭</el-button>
  348. </div>
  349. </ele-modal>
  350. </template>
  351. <script>
  352. import warehouseDefinition from '@/api/warehouseManagement/warehouseDefinition';
  353. import storageApi from '@/api/warehouseManagement';
  354. import AssetTree from './assetTree.vue';
  355. export default {
  356. components: { AssetTree },
  357. props: {
  358. treeIds: { type: Array, default: () => [] }
  359. },
  360. data() {
  361. return {
  362. factoryList: [],
  363. newColumns: [], // 动态表头
  364. isShowTable: true,
  365. qualityResultOption: [
  366. {
  367. value: 0,
  368. label: '合格'
  369. },
  370. {
  371. value: 1,
  372. label: '不合格'
  373. }
  374. ],
  375. qualityStatusOption: [
  376. {
  377. value: 1,
  378. label: '已检测'
  379. },
  380. {
  381. value: 0,
  382. label: '未检测'
  383. }
  384. ],
  385. visible: false,
  386. tableData: [],
  387. total: 0,
  388. categoryLevelId: '',
  389. searchForm: {
  390. categoryCode: '',
  391. categoryName: '',
  392. batchNo: '',
  393. factoryId: '',
  394. brandNum: '',
  395. engrave: '',
  396. barcodes: '',
  397. categoryLevelId: '',
  398. warehouseId: '',
  399. pageNum: 1,
  400. size: 20
  401. },
  402. selectionList: [],
  403. materialType: '',
  404. warehouseList: [],
  405. dimension: '1',
  406. productLists: []
  407. };
  408. },
  409. created() {
  410. this.getFieldModel();
  411. this.getWarehouse();
  412. this.getFactoryList();
  413. },
  414. computed: {
  415. // 仓库级联:仓库归属工厂,选择了工厂后只显示该工厂下的仓库
  416. warehouseOptions() {
  417. if (this.searchForm.factoryId) {
  418. return this.warehouseList.filter(
  419. (item) => item.factoryId == this.searchForm.factoryId
  420. );
  421. }
  422. return this.warehouseList;
  423. }
  424. },
  425. watch: {
  426. tableData(val) {
  427. this.doLayout();
  428. }
  429. },
  430. methods: {
  431. //获取工厂列表
  432. async getFactoryList() {
  433. const res = await warehouseDefinition.getFactoryarea({
  434. pageNum: 1,
  435. size: 9999,
  436. type: 1
  437. });
  438. this.factoryList = res.list;
  439. },
  440. async getWarehouse() {
  441. // 用带工厂信息(factoryId/factoryName)的仓库列表接口,供工厂-仓库级联
  442. const res = await warehouseDefinition.list({});
  443. this.warehouseList = res || [];
  444. },
  445. // 切换工厂时清空已选仓库,保持级联关系
  446. handleFactoryChange() {
  447. this.searchForm.warehouseId = '';
  448. },
  449. doLayout() {
  450. let that = this;
  451. this.$nextTick(() => {
  452. that.$refs.multipleTable.doLayout();
  453. });
  454. },
  455. // 获取动态表头
  456. getFieldModel() {
  457. storageApi.fieldModel({ fieldModel: 't_main_category' }).then((res) => {
  458. let newRes = res.map((m) => {
  459. return {
  460. prop: 'extField.' + m.prop,
  461. label: m.label,
  462. align: 'center',
  463. showOverflowTooltip: true
  464. };
  465. });
  466. this.newColumns = [...newRes];
  467. });
  468. },
  469. getRowKeys(row) {
  470. return row.id;
  471. },
  472. // 出库数量限制
  473. handleInput(row, value) {
  474. if (row.outboundNum <= row.measureQuantity) {
  475. // return (row.outboundNum = value.replace(/^(0+)|[^\d]+/g, ''));
  476. // 过滤掉非数字和小数点的字符
  477. const filteredValue = value.replace(/[^0-9.]/g, '');
  478. // 限制只能有一个小数点
  479. const singleDotValue = filteredValue.replace(/(\..*)\./g, '$1');
  480. let finalValue = '';
  481. const dotIndex = singleDotValue.indexOf('.');
  482. if (dotIndex !== -1) {
  483. // 截取小数点后最多两位
  484. finalValue = singleDotValue.slice(0, dotIndex + 3);
  485. } else {
  486. finalValue = singleDotValue;
  487. }
  488. return (row.outboundNum = finalValue);
  489. } else {
  490. row.outboundNum = row.measureQuantity;
  491. }
  492. },
  493. // 切换维度
  494. changeDimensionHandler(e) {
  495. this.searchForm.pageNum = 1;
  496. this.selectionList = [];
  497. this.$refs.multipleTable.clearSelection();
  498. this.changeDimension(e);
  499. },
  500. async changeDimension(e) {
  501. this.dimension = e;
  502. if (this.dimension == 1) {
  503. // 物品维度
  504. const data = await storageApi.getProductList(this.searchForm);
  505. this.tableData = data.list;
  506. this.total = data.count;
  507. } else if (this.dimension == 2) {
  508. // 批次维度
  509. const data = await storageApi.getBatchList(this.searchForm);
  510. for (let i = 0; i < data.list.length; i++) {
  511. data.list[i].outboundNum = data.list[i].measureQuantity;
  512. }
  513. this.tableData = data.list;
  514. this.total = data.count;
  515. } else if (this.dimension == 4) {
  516. // 物料维度
  517. const data = await storageApi.getMaterialList(this.searchForm);
  518. this.tableData = data.list;
  519. this.total = data.count;
  520. } else {
  521. // 包装维度
  522. const data = await storageApi.getPackingList(this.searchForm);
  523. for (let i = 0; i < data.list.length; i++) {
  524. data.list[i].outboundNum = data.list[i].packingQuantity;
  525. }
  526. this.tableData = data.list;
  527. this.total = data.count;
  528. }
  529. console.log(this.tableData);
  530. this.updateProductOutboundNums(this.tableData);
  531. },
  532. updateProductOutboundNums(list) {
  533. console.log(this.productLists, 'this.productLists');
  534. if (this.dimension == 1) {
  535. //物品层
  536. for (let i = 0; i < list.length; i++) {
  537. const item = list[i];
  538. const matchedProduct = this.productLists.find((product) => {
  539. const isMatch = product.categoryId === item.categoryId;
  540. return isMatch;
  541. });
  542. if (matchedProduct) {
  543. this.$set(item, 'outboundNum', matchedProduct.measureQuantity);
  544. } else {
  545. console.warn('No match for item.id:', item.id);
  546. }
  547. }
  548. } else {
  549. for (let i = 0; i < list.length; i++) {
  550. const item = list[i];
  551. const matchedProduct = this.productLists.find((product) => {
  552. const isMatch = product.id === item.id;
  553. return isMatch;
  554. });
  555. if (matchedProduct) {
  556. this.$set(item, 'outboundNum', matchedProduct.measureQuantity);
  557. } else {
  558. console.warn('No match for item.id:', item.id);
  559. }
  560. }
  561. }
  562. },
  563. open(val) {
  564. this.productLists = val || [];
  565. this.visible = true;
  566. this.$nextTick(() => {
  567. this.$refs.treeList.getTreeData().then((data) => {
  568. this.handleNodeClick(data);
  569. });
  570. });
  571. },
  572. async confirm() {
  573. if (!this.selectionList.length) {
  574. this.$message.error('请至少选择一条数据!');
  575. return;
  576. }
  577. if (this.dimension == 1) {
  578. let boolen = this.selectionList.every((item) => item.outboundNum > 0);
  579. if (!boolen) {
  580. this.$message.error('请输入抽取数量!');
  581. return;
  582. }
  583. }
  584. this.$emit('choose', this.selectionList, this.dimension);
  585. this.cancel();
  586. },
  587. cancel() {
  588. this.selectionList = [];
  589. this.$refs.multipleTable.clearSelection();
  590. this.visible = false;
  591. },
  592. handleSelectionChange(selection) {
  593. // if (this.dimension != 3 && selection.length > 1) {
  594. // // 单选模式下,如果选了多个,只保留最后一个
  595. // const lastSelected = selection[selection.length - 1];
  596. // this.$refs.multipleTable.clearSelection(); // 清空所有选中
  597. // this.$nextTick(() => {
  598. // this.$refs.multipleTable.toggleRowSelection(lastSelected, true); // 重新选中最后一个
  599. // });
  600. // this.selectionList = [lastSelected];
  601. // } else {
  602. // this.selectionList = selection;
  603. // }
  604. this.selectionList = selection;
  605. },
  606. handleNodeClick(data) {
  607. console.log(data);
  608. this.categoryLevelId = data.id;
  609. this.searchForm.categoryLevelId = data.id;
  610. this.doSearch();
  611. },
  612. handleCurrentChange() {
  613. this.changeDimension(this.dimension);
  614. },
  615. reset() {
  616. this.searchForm = {
  617. categoryCode: '',
  618. categoryName: '',
  619. batchNo: '',
  620. brandNum: '',
  621. engrave: '',
  622. barcodes: '',
  623. warehouseId: '',
  624. factoryId: '',
  625. categoryLevelId: this.categoryLevelId,
  626. pageNum: 1,
  627. size: 20
  628. };
  629. this.doSearch();
  630. },
  631. doSearch() {
  632. this.searchForm.pageNum = 1;
  633. this.changeDimension(this.dimension);
  634. },
  635. handleSizeChange() {
  636. this.searchForm.pageNum = 1;
  637. this.changeDimension(this.dimension, 'page');
  638. },
  639. checkSelectable(row, index) {
  640. // if (this.selectionList.length < 1) return true;
  641. // let code = this.selectionList[0].categoryCode;
  642. // return row.categoryCode === code;
  643. if (this.dimension === 3 && this.selectionList.length > 0) {
  644. // 包装维度下,只能选择相同物品编码的行
  645. let code = this.selectionList[0].categoryCode;
  646. return row.categoryCode === code;
  647. }
  648. // 其他维度允许全选所有行
  649. return true;
  650. },
  651. hideHeaderCheckbox({ column }) {
  652. // if (column.type === "selection") {
  653. // return "hide-header-checkbox";
  654. // }
  655. return '';
  656. }
  657. }
  658. };
  659. </script>
  660. <style lang="scss" >
  661. .el-dialog__wrapper {
  662. ::v-deep .el-aside {
  663. background-color: #fff !important;
  664. border: 1px solid #ccc;
  665. }
  666. }
  667. .wrapper-assets {
  668. max-height: 53vh;
  669. overflow: auto;
  670. }
  671. /* 隐藏表头的全选复选框 */
  672. .hide-header-checkbox .el-checkbox {
  673. display: none !important;
  674. }
  675. </style>