index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. <template>
  2. <div class="tenant">
  3. <!-- <Banner /> -->
  4. <div class="content">
  5. <byTable
  6. :source="sourceList.data"
  7. :pagination="sourceList.pagination"
  8. :config="config"
  9. :loading="loading"
  10. highlight-current-row
  11. :selectConfig="selectConfig"
  12. :table-events="{
  13. //element talbe事件都能传
  14. select: select,
  15. }"
  16. :action-list="[
  17. {
  18. text: '产品组合',
  19. action: () => openModal('add'),
  20. },
  21. ]"
  22. @get-list="getList"
  23. >
  24. <template #slotName="{ item }">
  25. {{ item.createTime }}
  26. </template>
  27. </byTable>
  28. </div>
  29. <el-dialog
  30. :title="modalType == 'add' ? '产品组合' : '组合详情'"
  31. v-model="dialogVisible"
  32. width="800"
  33. v-loading="loading"
  34. >
  35. <byForm
  36. :formConfig="formConfig"
  37. :formOption="formOption"
  38. v-model="formData.data"
  39. :rules="rules"
  40. ref="byform"
  41. >
  42. <template #products>
  43. <div style="width: 100%">
  44. <el-button
  45. type="primary"
  46. @click="openProduct = true"
  47. style="margin-bottom: 10px"
  48. v-if="modalType == 'add'"
  49. >
  50. 选择产品
  51. </el-button>
  52. <el-table
  53. :data="formData.data.groupRecordDetailsList"
  54. show-summary
  55. :summary-method="getSummaries"
  56. >
  57. <el-table-column prop="productName" label="产品名称" />
  58. <el-table-column
  59. prop="canSum"
  60. label="可组合数量"
  61. v-if="modalType == 'add'"
  62. />
  63. <el-table-column prop="groupNum" label="本次组合" min-width="150">
  64. <template #default="{ row, $index }">
  65. <el-form-item
  66. :prop="'groupRecordDetailsList.' + $index + '.groupNum'"
  67. :rules="rules.groupNum"
  68. :inline-message="true"
  69. >
  70. <el-input-number
  71. v-model="row.groupNum"
  72. :precision="0"
  73. :controls="false"
  74. :min="0"
  75. @change="() => changeCanSum(row, $index)"
  76. onmousewheel="return false;"
  77. />
  78. </el-form-item>
  79. </template>
  80. </el-table-column>
  81. <el-table-column
  82. prop="zip"
  83. label="操作"
  84. width="100"
  85. v-if="modalType == 'add'"
  86. >
  87. <template #default="{ $index }">
  88. <el-button type="primary" link @click="handleRemove($index)"
  89. >删除</el-button
  90. >
  91. </template>
  92. </el-table-column>
  93. </el-table>
  94. </div>
  95. </template>
  96. </byForm>
  97. <template #footer v-if="modalType == 'add'">
  98. <el-button @click="dialogVisible = false" size="large">取 消</el-button>
  99. <el-button
  100. type="primary"
  101. @click="submitForm('byform')"
  102. size="large"
  103. :loading="submitLoading"
  104. >
  105. 确 定
  106. </el-button>
  107. </template>
  108. </el-dialog>
  109. <el-dialog
  110. v-model="openProduct"
  111. title="选择产品"
  112. width="70%"
  113. append-to-body
  114. >
  115. <SelectProduct
  116. :isCombination="true"
  117. @handleSelect="handleSelect"
  118. key="a"
  119. ></SelectProduct>
  120. <template #footer>
  121. <span class="dialog-footer">
  122. <el-button @click="openProduct = false">取消</el-button>
  123. </span>
  124. </template>
  125. </el-dialog>
  126. </div>
  127. </template>
  128. <script setup>
  129. /* eslint-disable vue/no-unused-components */
  130. import { ElMessage, ElMessageBox } from "element-plus";
  131. import byTable from "@/components/byTable/index";
  132. import byForm from "@/components/byForm/index";
  133. import { computed, defineComponent, ref, watch } from "vue";
  134. import useUserStore from "@/store/modules/user";
  135. import SelectProduct from "@/components/WDLY/product/SelectProduct";
  136. const loading = ref(false);
  137. const submitLoading = ref(false);
  138. const sourceList = ref({
  139. data: [],
  140. pagination: {
  141. total: 3,
  142. pageNum: 1,
  143. pageSize: 10,
  144. type: "1",
  145. },
  146. });
  147. let dialogVisible = ref(false);
  148. let openProduct = ref(false);
  149. let roomDialogVisible = ref(false);
  150. let modalType = ref("add");
  151. let rules = ref({
  152. productWarehouseId: [
  153. { required: true, message: "请选择半成品所在仓库", trigger: "change" },
  154. ],
  155. groupWarehouseId: [
  156. { required: true, message: "请选择组合后放置仓库", trigger: "change" },
  157. ],
  158. groupNum: [{ required: true, message: "请输入分拆数量", trigger: "blur" }],
  159. });
  160. const { proxy } = getCurrentInstance();
  161. const selectConfig = reactive([]);
  162. const config = computed(() => {
  163. return [
  164. {
  165. attrs: {
  166. label: "组合编码",
  167. prop: "productCode",
  168. },
  169. },
  170. {
  171. attrs: {
  172. label: "组合名称",
  173. prop: "productName",
  174. },
  175. },
  176. {
  177. attrs: {
  178. label: "规格",
  179. prop: "productSpec",
  180. },
  181. },
  182. {
  183. attrs: {
  184. label: "半成品所在仓库",
  185. prop: "productWarehouseName",
  186. },
  187. },
  188. {
  189. attrs: {
  190. label: "组合后放置仓库",
  191. prop: "groupWarehouseName",
  192. },
  193. },
  194. {
  195. attrs: {
  196. label: "组合数量",
  197. prop: "groupNum",
  198. },
  199. },
  200. {
  201. attrs: {
  202. label: "操作人",
  203. prop: "createUserName",
  204. },
  205. },
  206. {
  207. attrs: {
  208. label: "操作时间",
  209. prop: "createTime",
  210. },
  211. },
  212. {
  213. attrs: {
  214. label: "操作",
  215. width: "100",
  216. align: "right",
  217. },
  218. // 渲染 el-button,一般用在最后一列。
  219. renderHTML(row) {
  220. return [
  221. {
  222. attrs: {
  223. label: "查看",
  224. type: "primary",
  225. text: true,
  226. },
  227. el: "button",
  228. click() {
  229. getDtl(row);
  230. },
  231. },
  232. ];
  233. },
  234. },
  235. ];
  236. });
  237. let formData = reactive({
  238. data: {},
  239. treeData: [],
  240. });
  241. const formOption = reactive({
  242. inline: true,
  243. labelWidth: 100,
  244. itemWidth: 100,
  245. rules: [],
  246. });
  247. const byform = ref(null);
  248. const treeData = ref([]);
  249. const formConfig = reactive([
  250. {
  251. type: "title",
  252. title: "仓库信息",
  253. },
  254. {
  255. type: "select",
  256. prop: "productWarehouseId",
  257. label: "半成品所在仓库",
  258. itemWidth: 33,
  259. data: [],
  260. },
  261. {
  262. type: "select",
  263. prop: "groupWarehouseId",
  264. label: "组合后放置仓库",
  265. itemWidth: 33,
  266. data: [],
  267. },
  268. {
  269. type: "title",
  270. title: "组合明细",
  271. },
  272. {
  273. type: "slot",
  274. slotName: "products",
  275. },
  276. ]);
  277. const getList = async (req) => {
  278. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  279. loading.value = true;
  280. proxy
  281. .post("/groupRecordDetails/page", sourceList.value.pagination)
  282. .then((message) => {
  283. console.log(message);
  284. sourceList.value.data = message.rows;
  285. sourceList.value.pagination.total = message.total;
  286. setTimeout(() => {
  287. loading.value = false;
  288. }, 200);
  289. });
  290. };
  291. const openModal = () => {
  292. dialogVisible.value = true;
  293. modalType.value = "add";
  294. formConfig[1].disabled = false;
  295. formConfig[2].disabled = false;
  296. formData.data = {
  297. type: "1",
  298. groupRecordDetailsList: [],
  299. };
  300. };
  301. const submitForm = () => {
  302. byform.value.handleSubmit((valid) => {
  303. const list = formData.data.groupRecordDetailsList;
  304. for (let i = 0; i < list.length; i++) {
  305. const e = list[i];
  306. if (e.groupNum == 0) {
  307. return ElMessage({
  308. message: "本次组合不能为0!",
  309. type: "info",
  310. });
  311. }
  312. if (e.groupNum > e.canSum) {
  313. return ElMessage({
  314. message: "本次组合不可大于可组合数量!",
  315. type: "info",
  316. });
  317. }
  318. }
  319. submitLoading.value = true;
  320. const data = {
  321. type: formData.data.type,
  322. productWarehouseId: formData.data.productWarehouseId,
  323. groupWarehouseId: formData.data.groupWarehouseId,
  324. groupRecordDetailsList: list.map((x) => ({
  325. productId: x.productId,
  326. groupNum: x.groupNum,
  327. })),
  328. };
  329. proxy.post("/groupRecordDetails/" + modalType.value, data).then(
  330. (res) => {
  331. ElMessage({
  332. message: modalType.value == "add" ? "添加成功" : "编辑成功",
  333. type: "success",
  334. });
  335. dialogVisible.value = false;
  336. submitLoading.value = false;
  337. getList();
  338. },
  339. (err) => (submitLoading.value = false)
  340. );
  341. });
  342. };
  343. const getDtl = (row) => {
  344. modalType.value = "edit";
  345. proxy
  346. .post("/groupRecordDetails/detail", { id: row.groupRecordId })
  347. .then((res) => {
  348. formConfig[1].disabled = true;
  349. formConfig[2].disabled = true;
  350. formData.data.productWarehouseId = row.productWarehouseId;
  351. formData.data.groupWarehouseId = row.groupWarehouseId;
  352. formData.data.groupRecordDetailsList = res;
  353. dialogVisible.value = true;
  354. });
  355. };
  356. const warehouseList = ref([]);
  357. const warehouseListData = () => {
  358. // // 币种数据
  359. proxy
  360. .post("/warehouse/page", {
  361. pageNum: 1,
  362. pageSize: 9999,
  363. })
  364. .then((message) => {
  365. warehouseList.value = message.rows;
  366. formConfig[1].data = message.rows.map((x) => ({
  367. label: x.name,
  368. value: x.id,
  369. }));
  370. formConfig[2].data = message.rows.map((x) => ({
  371. label: x.name,
  372. value: x.id,
  373. }));
  374. });
  375. };
  376. getList();
  377. warehouseListData();
  378. const handleSelect = (row) => {
  379. const flag = formData.data.groupRecordDetailsList.some(
  380. (x) => x.productId === row.id
  381. );
  382. if (flag)
  383. return ElMessage({
  384. message: "该物品已选择",
  385. type: "info",
  386. });
  387. formData.data.groupRecordDetailsList.push({
  388. productName: row.name,
  389. productCode: row.code,
  390. productSpec: row.spec,
  391. productId: row.id,
  392. groupNum: 0,
  393. });
  394. return ElMessage({
  395. message: "选择成功",
  396. type: "success",
  397. });
  398. };
  399. const handleRemove = (index) => {
  400. formData.data.groupRecordDetailsList.splice(index, 1);
  401. return ElMessage({
  402. message: "删除成功",
  403. type: "success",
  404. });
  405. };
  406. const stockMapData = reactive({
  407. data: {},
  408. });
  409. watchEffect(() => {
  410. if (
  411. formData.data.productWarehouseId &&
  412. formData.data.groupRecordDetailsList.length > 0
  413. ) {
  414. proxy
  415. .post("/groupRecordDetails/getCombinableQuantity", {
  416. warehouseId: formData.data.productWarehouseId,
  417. productIdList: formData.data.groupRecordDetailsList.map(
  418. (x) => x.productId
  419. ),
  420. })
  421. .then((res) => {
  422. stockMapData.data = res.stockMap;
  423. const stockMap = { ...stockMapData.data };
  424. const list = formData.data.groupRecordDetailsList;
  425. for (let i = 0; i < list.length; i++) {
  426. const e = list[i];
  427. for (const key in res.combinableQuantity) {
  428. if (e.productId === key) {
  429. e.needProductIdMap = res.combinableQuantity[key]; //赋值需要的半成品id
  430. }
  431. }
  432. }
  433. for (let i = 0; i < list.length; i++) {
  434. const e = list[i];
  435. const canSumMap = {};
  436. for (const needKey in e.needProductIdMap) {
  437. if (stockMap.hasOwnProperty(needKey)) {
  438. canSumMap[needKey] =
  439. stockMap[needKey] / e.needProductIdMap[needKey];
  440. } else {
  441. canSumMap[needKey] = 0;
  442. }
  443. }
  444. e.canSumArr = Object.values(canSumMap);
  445. e.canSum = Math.floor(Math.min(...e.canSumArr)); //取出最小值且向下取整
  446. }
  447. });
  448. }
  449. });
  450. const changeCanSum = (row, rowIndex) => {
  451. const list = formData.data.groupRecordDetailsList;
  452. const stockMap = { ...stockMapData.data };
  453. if (rowIndex < list.length - 1) {
  454. for (let i = 0; i < list.length; i++) {
  455. const e = list[i];
  456. const canSumMap = {};
  457. for (const key in e.needProductIdMap) {
  458. if (stockMap.hasOwnProperty(key)) {
  459. if (e.groupNum && e.groupNum > 0) {
  460. stockMap[key] -= e.needProductIdMap[key] * e.groupNum;
  461. }
  462. } else {
  463. canSumMap[key] = 0;
  464. }
  465. // canSumMap[key] = stockMap[key] / e.needProductIdMap[key];
  466. if (row.productId != e.productId) {
  467. // console.log("aaa");
  468. canSumMap[key] = stockMap[key] / e.needProductIdMap[key];
  469. }
  470. }
  471. e.canSumArr =
  472. Object.values(canSumMap).length > 0
  473. ? Object.values(canSumMap)
  474. : [...e.canSumArr];
  475. e.canSum = Math.floor(Math.min(...e.canSumArr)); //取出最小值且向下取整
  476. }
  477. } else {
  478. for (let i = list.length - 1; i >= 0; i--) {
  479. const e = list[i];
  480. const canSumMap = {};
  481. for (const key in e.needProductIdMap) {
  482. if (stockMap.hasOwnProperty(key)) {
  483. if (e.groupNum && e.groupNum > 0) {
  484. stockMap[key] -= e.needProductIdMap[key] * e.groupNum;
  485. }
  486. } else {
  487. canSumMap[key] = 0;
  488. }
  489. // canSumMap[key] = stockMap[key] / e.needProductIdMap[key];
  490. if (row.productId != e.productId) {
  491. // console.log("aaa");
  492. canSumMap[key] = stockMap[key] / e.needProductIdMap[key];
  493. }
  494. }
  495. e.canSumArr =
  496. Object.values(canSumMap).length > 0
  497. ? Object.values(canSumMap)
  498. : [...e.canSumArr];
  499. e.canSum = Math.floor(Math.min(...e.canSumArr)); //取出最小值且向下取整
  500. }
  501. }
  502. // for (let i = 0; i < list.length; i++) {
  503. // const e = list[i];
  504. // let canSumMap = {};
  505. // for (const needKey in e.needProductIdMap) {
  506. // if (stockMap.hasOwnProperty(needKey)) {
  507. // if (e.groupNum && e.groupNum > 0) {
  508. // stockMap[needKey] =
  509. // stockMap[needKey] - e.needProductIdMap[needKey] * e.groupNum;
  510. // } else {
  511. // stockMap[needKey] =
  512. // stockMap[needKey] - e.needProductIdMap[needKey] * -1;
  513. // }
  514. // if (row.productId != e.productId) {
  515. // canSumMap[needKey] = stockMap[needKey] / e.needProductIdMap[needKey];
  516. // }
  517. // } else {
  518. // canSumMap[needKey] = 0;
  519. // }
  520. // }
  521. // e.canSumArr = Object.values(canSumMap).length
  522. // ? Object.values(canSumMap)
  523. // : [...e.canSumArr];
  524. // e.canSum = Math.floor(Math.min(...e.canSumArr)); //取出最小值且向下取整
  525. // // e.canSum = e.canSum - 1;
  526. // }
  527. };
  528. const getSummaries = (param) => {
  529. const { columns, data } = param; //columns是每列的信息,data是每行的信息
  530. const sums = [];
  531. columns.forEach((column, index) => {
  532. if (index === 0) {
  533. sums[index] = "合计"; //此处是在index=0的这一列显示为“合计”
  534. return;
  535. }
  536. const values = data.map((item) => Number(item[column.property]));
  537. if (column.property === "canSum" || column.property === "groupNum") {
  538. sums[index] = values.reduce((prev, curr) => {
  539. const value = Number(curr);
  540. if (!isNaN(value)) {
  541. return prev + curr;
  542. } else {
  543. return prev;
  544. }
  545. }, 0);
  546. sums[index];
  547. }
  548. });
  549. return sums;
  550. };
  551. </script>
  552. <style lang="scss" scoped>
  553. .tenant {
  554. padding: 20px;
  555. }
  556. </style>