index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. <template>
  2. <div class="pageIndexClass">
  3. <div class="content">
  4. <byTable :source="sourceList.data" :pagination="sourceList.pagination" :config="config" :loading="loading" :selectConfig="selectConfig"
  5. highlight-current-row @get-list="getList">
  6. <template #expand="{item}">
  7. <div style="width:100%;padding:0 50px">
  8. <el-table :data="item.stockWaitDetailsList" class="bom-table" border>
  9. <el-table-column label="物品图片" width="100" align="center">
  10. <template #default="{ row }">
  11. <div v-if="row.fileUrl">
  12. <img :src="row.fileUrl" class="pic" @click="openImg(row.fileUrl)" />
  13. </div>
  14. </template>
  15. </el-table-column>
  16. <el-table-column prop="productCode" label="物品编码" width="200" />
  17. <el-table-column prop="productName" label="物品名称" min-width="130" />
  18. <el-table-column label="规格尺寸 (cm)" width="140">
  19. <template #default="{ row, $index }">
  20. <div style="width: 100%">
  21. {{row.productLength}} * {{row.productWidth}} * {{row.productHeight}}
  22. </div>
  23. </template>
  24. </el-table-column>
  25. <el-table-column label="需入库数量" prop="quantity" width="100" />
  26. <el-table-column label="待入库数量" prop="waitQuantity" width="100" />
  27. <el-table-column label="已入库数量" prop="receiptQuantity" width="100" />
  28. </el-table>
  29. </div>
  30. </template>
  31. </byTable>
  32. </div>
  33. <el-dialog title="入库" v-if="dialogVisible" v-model="dialogVisible" width="90%">
  34. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit" v-loading="loadingDialog">
  35. <template #detail>
  36. <div style="width:100%">
  37. <div style="width:100%;padding:0 15px">
  38. <el-table :data="formData.data.stockWaitDetailsList">
  39. <el-table-column label="物品图片" width="80">
  40. <template #default="{ row }">
  41. <div v-if="row.fileUrl">
  42. <img :src="row.fileUrl" class="pic" @click="openImg(row.fileUrl)" />
  43. </div>
  44. </template>
  45. </el-table-column>
  46. <el-table-column prop="productCode" label="物品编码" min-width="160" />
  47. <el-table-column prop="productName" label="物品名称" width="150" />
  48. <el-table-column label="规格尺寸 (cm)" width="140">
  49. <template #default="{ row, $index }">
  50. <div style="width: 100%">
  51. {{row.productLength}} * {{row.productWidth}} * {{row.productHeight}}
  52. </div>
  53. </template>
  54. </el-table-column>
  55. <el-table-column prop="productColor" label="颜色" width="130" />
  56. <el-table-column prop="productNetWeight" label="净重" width="100" />
  57. <el-table-column prop="productFrontalTexture" label="纹路" width="130"
  58. :formatter="(row) => dictKeyValue(row.productFrontalTexture, frontLinesData)" />
  59. <el-table-column prop="productUnit" label="单位" width="100" />
  60. <el-table-column label="需入库数量" prop="needQuantity" width="100" fixed="right" />
  61. <el-table-column label="待入库数量" prop="waitQuantity" width="100" fixed="right" />
  62. <el-table-column label="已入库数量" prop="receiptQuantity" width="100" fixed="right" />
  63. <el-table-column prop="quantity" label="入库数量" width="130" fixed="right">
  64. <template #default="{ row, $index }">
  65. <el-form-item :prop="'stockWaitDetailsList.' + $index + '.quantity'" :rules="rules.quantity" :inline-message="true"
  66. class="margin-b-0">
  67. <el-input-number onmousewheel="return false;" v-model="row.quantity" placeholder="请输入" style="width: 100%" :precision="0"
  68. :controls="false" :min="0" />
  69. </el-form-item>
  70. </template>
  71. </el-table-column>
  72. </el-table>
  73. </div>
  74. </div>
  75. </template>
  76. </byForm>
  77. <template #footer>
  78. <el-button @click="dialogVisible = false" size="default">取 消</el-button>
  79. <el-button type="primary" @click="submitForm()" size="default">确 定</el-button>
  80. </template>
  81. </el-dialog>
  82. </div>
  83. </template>
  84. <script setup>
  85. import { computed, ref } from "vue";
  86. import byTable from "@/components/byTable/index";
  87. import byForm from "@/components/byForm/index";
  88. import { ElMessage } from "element-plus";
  89. import moment from "moment";
  90. const { proxy } = getCurrentInstance();
  91. const warehouseList = ref([]);
  92. const userList = ref([]);
  93. const deptData = ref([]);
  94. const materialUnitData = computed(
  95. () => proxy.useUserStore().allDict["material_unit"]
  96. );
  97. const frontLinesData = computed(
  98. () => proxy.useUserStore().allDict["front_lines"]
  99. );
  100. const status = ref([
  101. {
  102. label: "待入库",
  103. value: 0,
  104. },
  105. {
  106. label: "部分入库",
  107. value: 1,
  108. },
  109. // {
  110. // label: "入库完成",
  111. // value: 2,
  112. // },
  113. ]);
  114. const businessType = ref([
  115. // {
  116. // label: "线边回仓",
  117. // value: 1,
  118. // },
  119. // {
  120. // label: "完工入库",
  121. // value: 2,
  122. // },
  123. // {
  124. // label: "采购到货",
  125. // value: 3,
  126. // },
  127. // {
  128. // label: "退货出库",
  129. // value: 4,
  130. // },
  131. // {
  132. // label: "京东订单",
  133. // value: 5,
  134. // },
  135. // {
  136. // label: "销售订单出库",
  137. // value: 6,
  138. // },
  139. {
  140. label: "借用归还",
  141. value: "100",
  142. },
  143. {
  144. label: "退料入库",
  145. value: "101",
  146. },
  147. {
  148. label: "废料入库",
  149. value: "102",
  150. },
  151. {
  152. label: "丢件寻回",
  153. value: "103",
  154. },
  155. {
  156. label: "采购入库",
  157. value: "104",
  158. },
  159. {
  160. label: "调拨入库",
  161. value: "105",
  162. },
  163. {
  164. label: "盘盈入库",
  165. value: "106",
  166. },
  167. {
  168. label: "完工入库",
  169. value: "107",
  170. },
  171. ]);
  172. const sourceList = ref({
  173. data: [],
  174. pagination: {
  175. total: 0,
  176. pageNum: 1,
  177. pageSize: 10,
  178. keyword: "",
  179. status: "",
  180. type: 1,
  181. },
  182. });
  183. const loading = ref(false);
  184. const selectConfig = computed(() => {
  185. return [
  186. {
  187. label: "业务公司",
  188. prop: "companyId",
  189. data: proxy.useUserStore().allDict["list_company_data"],
  190. },
  191. {
  192. label: "入库类型",
  193. prop: "status",
  194. data: status.value,
  195. },
  196. {
  197. label: "数据来源",
  198. prop: "businessType",
  199. data: businessType.value,
  200. },
  201. ];
  202. });
  203. const config = computed(() => {
  204. return [
  205. {
  206. type: "expand",
  207. attrs: {
  208. slot: "expand",
  209. width: 50,
  210. align: "center",
  211. },
  212. },
  213. {
  214. attrs: {
  215. label: "业务公司",
  216. prop: "companyName",
  217. width: 110,
  218. },
  219. },
  220. {
  221. attrs: {
  222. label: "数据来源",
  223. prop: "businessType",
  224. // width: 120,
  225. },
  226. render(type) {
  227. return proxy.dictValueLabel(type, businessType.value);
  228. },
  229. },
  230. {
  231. attrs: {
  232. label: "单号",
  233. prop: "businessCode",
  234. // width: 160,
  235. },
  236. },
  237. // {
  238. // attrs: {
  239. // label: "图片",
  240. // slot: "pic",
  241. // align: "center",
  242. // width: 80,
  243. // },
  244. // },
  245. // {
  246. // attrs: {
  247. // label: "物品编码",
  248. // prop: "productCustomCode",
  249. // width: 140,
  250. // },
  251. // },
  252. // {
  253. // attrs: {
  254. // label: "物品名称",
  255. // prop: "productName",
  256. // "min-width": 200,
  257. // },
  258. // },
  259. // {
  260. // attrs: {
  261. // label: "规格型号",
  262. // prop: "productSpec",
  263. // width: 140,
  264. // },
  265. // },
  266. // {
  267. // attrs: {
  268. // label: "单位",
  269. // prop: "productUnit",
  270. // width: 120,
  271. // },
  272. // render(unit) {
  273. // return proxy.dictKeyValue(unit, materialUnitData.value);
  274. // },
  275. // },
  276. // {
  277. // attrs: {
  278. // label: "待入库数量",
  279. // prop: "quantity",
  280. // width: 120,
  281. // },
  282. // },
  283. {
  284. attrs: {
  285. label: "入库状态",
  286. prop: "status",
  287. // width: 140,
  288. },
  289. render(type) {
  290. return proxy.dictValueLabel(type, status.value);
  291. },
  292. },
  293. {
  294. attrs: {
  295. label: "创建时间",
  296. prop: "createTime",
  297. // width: 160,
  298. },
  299. },
  300. {
  301. attrs: {
  302. label: "操作",
  303. width: "80",
  304. align: "center",
  305. },
  306. renderHTML(row) {
  307. return [
  308. row.status !== 2
  309. ? {
  310. attrs: {
  311. label: "入库",
  312. type: "primary",
  313. text: true,
  314. },
  315. el: "button",
  316. click() {
  317. clickOperation(row);
  318. },
  319. }
  320. : {},
  321. ];
  322. },
  323. },
  324. ];
  325. });
  326. const getList = async (req) => {
  327. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  328. loading.value = true;
  329. proxy.post("/stockWait/page", sourceList.value.pagination).then((res) => {
  330. sourceList.value.data = res.rows;
  331. let productIds = [];
  332. for (let i = 0; i < res.rows.length; i++) {
  333. for (let j = 0; j < res.rows[i].stockWaitDetailsList.length; j++) {
  334. let ele = res.rows[i].stockWaitDetailsList[j];
  335. ele.waitQuantity = Number(ele.quantity) - Number(ele.receiptQuantity);
  336. productIds.push(ele.productId);
  337. }
  338. }
  339. if (productIds && productIds.length > 0) {
  340. proxy
  341. .getFileData({
  342. businessIdList: Array.from(new Set(productIds)),
  343. getAll: true,
  344. })
  345. .then((res) => {
  346. for (let i = 0; i < sourceList.value.data.length; i++) {
  347. for (
  348. let j = 0;
  349. j < sourceList.value.data[i].stockWaitDetailsList.length;
  350. j++
  351. ) {
  352. let ele = sourceList.value.data[i].stockWaitDetailsList[j];
  353. for (const key in res) {
  354. if (ele.productId == key && res[key]) {
  355. let list = res[key].filter((x) => x.businessType == "0");
  356. ele.fileUrl = list && list.length > 0 ? list[0].fileUrl : "";
  357. break;
  358. }
  359. }
  360. }
  361. }
  362. });
  363. }
  364. sourceList.value.pagination.total = res.total;
  365. setTimeout(() => {
  366. loading.value = false;
  367. }, 200);
  368. });
  369. };
  370. getList();
  371. const dialogVisible = ref(false);
  372. const loadingDialog = ref(false);
  373. const submit = ref(null);
  374. const formOption = reactive({
  375. inline: true,
  376. labelWidth: 100,
  377. itemWidth: 100,
  378. rules: [],
  379. });
  380. const formData = reactive({
  381. data: {},
  382. });
  383. const formConfig = computed(() => {
  384. return [
  385. {
  386. type: "title1",
  387. title: "待入库信息",
  388. },
  389. {
  390. type: "select",
  391. prop: "businessType",
  392. label: "数据来源",
  393. disabled: true,
  394. data: businessType.value,
  395. itemWidth: 25,
  396. },
  397. {
  398. type: "input",
  399. prop: "businessCode",
  400. label: "单号",
  401. itemType: "text",
  402. disabled: true,
  403. itemWidth: 25,
  404. },
  405. {
  406. type: "input",
  407. prop: "purchaseUserName",
  408. label: "采购员",
  409. itemType: "text",
  410. disabled: true,
  411. itemWidth: 25,
  412. isShow: rowBusinessType.value == "104",
  413. },
  414. {
  415. type: "date",
  416. prop: "inOutTime",
  417. itemType: "date",
  418. label: "入库日期",
  419. itemWidth: 25,
  420. disabled: false,
  421. },
  422. {
  423. type: "treeSelect",
  424. prop: "companyId",
  425. label: "业务公司",
  426. data: proxy.useUserStore().allDict["tree_company_data"],
  427. propsTreeLabel: "deptName",
  428. propsTreeValue: "deptId",
  429. itemWidth: 25,
  430. disabled: true,
  431. },
  432. {
  433. type: "treeSelect",
  434. prop: "deptId",
  435. label: "业务部门",
  436. data: deptData.value,
  437. propsTreeLabel: "deptName",
  438. propsTreeValue: "deptId",
  439. itemWidth: 25,
  440. },
  441. {
  442. type: "select",
  443. prop: "inOutUserId",
  444. label: "入库员",
  445. required: true,
  446. filterable: true,
  447. data: userList.value,
  448. itemWidth: 25,
  449. },
  450. {
  451. type: "select",
  452. prop: "warehouseId",
  453. label: "仓库名称",
  454. required: true,
  455. data: warehouseList.value,
  456. itemWidth: 25,
  457. },
  458. {
  459. type: "upload",
  460. listType: "text",
  461. accept: "",
  462. prop: "fileList",
  463. label: "上传凭证",
  464. itemWidth: 100,
  465. required: true,
  466. },
  467. {
  468. type: "title1",
  469. title: "入库明细",
  470. },
  471. {
  472. type: "slot",
  473. slotName: "detail",
  474. label: "",
  475. },
  476. ];
  477. });
  478. const rules = ref({
  479. companyId: [{ required: true, message: "请选择业务公司", trigger: "change" }],
  480. warehouseId: [{ required: true, message: "请选择仓库", trigger: "change" }],
  481. quantity: [{ required: true, message: "请输入入库数量", trigger: "blur" }],
  482. fileList: [
  483. { required: true, message: "请上传凭证", trigger: ["blur", "change"] },
  484. ],
  485. inOutTime: [{ required: true, message: "请选择入库日期", trigger: "change" }],
  486. deptId: [{ required: true, message: "请选择业务部门", trigger: "change" }],
  487. inOutUserId: [{ required: true, message: "请选择入库员", trigger: "change" }],
  488. });
  489. const submitForm = () => {
  490. submit.value.handleSubmit(() => {
  491. if (!(formData.data.fileList && formData.data.fileList.length > 0)) {
  492. return proxy.msgTip("请上传附件", 2);
  493. }
  494. for (let i = 0; i < formData.data.stockWaitDetailsList.length; i++) {
  495. const ele = formData.data.stockWaitDetailsList[i];
  496. if (Number(ele.quantity) > Number(ele.waitQuantity)) {
  497. return proxy.msgTip("入库数量不可大于待入库数量", 2);
  498. }
  499. }
  500. loadingDialog.value = true;
  501. proxy.post("/stockWait/add", formData.data).then(
  502. () => {
  503. proxy.msgTip("提交成功", 1);
  504. dialogVisible.value = false;
  505. getList();
  506. },
  507. (err) => {
  508. loadingDialog.value = false;
  509. }
  510. );
  511. });
  512. };
  513. const getDeptData = (val) => {
  514. proxy
  515. .get("/tenantUser/list", {
  516. pageNum: 1,
  517. pageSize: 10000,
  518. tenantId: proxy.useUserStore().user.tenantId,
  519. companyId: val,
  520. })
  521. .then((res) => {
  522. userList.value = res.rows.map((item) => {
  523. return {
  524. label: item.nickName,
  525. value: item.userId,
  526. };
  527. });
  528. });
  529. proxy
  530. .get("/tenantDept/list", {
  531. pageNum: 1,
  532. pageSize: 9999,
  533. keyword: "",
  534. ancestors: val,
  535. tenantId: proxy.useUserStore().user.tenantId,
  536. // type: 2,
  537. })
  538. .then((res) => {
  539. deptData.value = proxy.handleTree(res.data, "deptId");
  540. });
  541. proxy
  542. .post("/warehouse/page", {
  543. pageNum: 1,
  544. pageSize: 999,
  545. companyId: val,
  546. })
  547. .then((res) => {
  548. if (res.rows && res.rows.length > 0) {
  549. warehouseList.value = res.rows.map((item) => {
  550. return {
  551. label: item.name,
  552. value: item.id,
  553. };
  554. });
  555. }
  556. });
  557. };
  558. const rowBusinessType = ref("");
  559. const clickOperation = (row) => {
  560. rowBusinessType.value = row.businessType;
  561. if (row.companyId) {
  562. getDeptData(row.companyId);
  563. }
  564. formData.data = {
  565. id: row.id,
  566. companyId: row.companyId,
  567. businessType: row.businessType + "",
  568. businessCode: row.businessCode,
  569. purchaseUserName: row.purchaseUserName,
  570. warehouseId: row.warehouseId || "",
  571. stockWaitDetailsList: row.stockWaitDetailsList.map((x) => ({
  572. ...x,
  573. needQuantity: x.quantity,
  574. quantity: null,
  575. })),
  576. fileList: [],
  577. inOutTime: moment().format("YYYY-MM-DD"),
  578. inOutUserId: row.inOutUserId || proxy.useUserStore().user.userId,
  579. };
  580. if (
  581. proxy.useUserStore().currentCompany == proxy.useUserStore().user.ofCompanyId
  582. ) {
  583. formData.data.deptId = proxy.useUserStore().user.deptId;
  584. }
  585. loadingDialog.value = false;
  586. dialogVisible.value = true;
  587. };
  588. </script>
  589. <style lang="scss" scoped>
  590. .tenant {
  591. padding: 20px;
  592. }
  593. ::v-deep(.el-input-number .el-input__inner) {
  594. text-align: left;
  595. }
  596. :deep(.bom-table .el-table__body-wrapper .el-table__body .el-table__row) {
  597. background: #f4f4f5 !important;
  598. }
  599. </style>