index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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. @get-list="getList"
  18. >
  19. <template #slotName="{ item }">
  20. {{ item.createTime }}
  21. </template>
  22. </byTable>
  23. </div>
  24. <el-dialog
  25. :title="'调整BOM'"
  26. v-model="dialogVisible"
  27. width="60%"
  28. v-loading="submitLoading"
  29. destroy-on-close
  30. >
  31. <byForm
  32. :formConfig="formConfig"
  33. :formOption="formOption"
  34. v-model="formData.data"
  35. :rules="rules"
  36. ref="byform"
  37. >
  38. <template #slot>
  39. <div style="width: 100%">
  40. <el-button type="primary" plain @click="openMaterial = true"
  41. >添加物料/半成品</el-button
  42. >
  43. <el-button type="primary" plain> Excel导入</el-button>
  44. <el-form
  45. ref="tableForm"
  46. :model="formData.data"
  47. :rules="rules"
  48. label-width="0px"
  49. style="margin-top: 15px"
  50. >
  51. <el-table :data="formData.data.workOrderBomList">
  52. <el-table-column prop="productCode" label="物料编码" />
  53. <el-table-column prop="productName" label="物料名称" />
  54. <el-table-column
  55. prop="productUnit"
  56. label="单位"
  57. :formatter="
  58. (row) => dictValueLabel(row.productUnit, materialUnit)
  59. "
  60. />
  61. <el-table-column prop="quantity" label="数量" width="150">
  62. <template #default="{ row, $index }">
  63. <el-form-item
  64. :prop="'workOrderBomList.' + $index + '.quantity'"
  65. :rules="rules.quantity"
  66. :inline-message="true"
  67. >
  68. <el-input-number
  69. v-model="row.quantity"
  70. :precision="2"
  71. :controls="false"
  72. :min="1"
  73. />
  74. </el-form-item>
  75. </template>
  76. </el-table-column>
  77. <el-table-column prop="zip" label="操作" width="100">
  78. <template #default="{ $index }">
  79. <el-button type="primary" link @click="handleRemove($index)"
  80. >删除</el-button
  81. >
  82. </template>
  83. </el-table-column>
  84. </el-table>
  85. </el-form>
  86. </div>
  87. </template>
  88. </byForm>
  89. <template #footer>
  90. <el-button @click="dialogVisible = false" size="large">取 消</el-button>
  91. <el-button
  92. type="primary"
  93. @click="submitForm(0)"
  94. size="large"
  95. :loading="submitLoading"
  96. >
  97. 暂 存
  98. </el-button>
  99. <el-button
  100. type="primary"
  101. @click="submitForm(1)"
  102. size="large"
  103. :loading="submitLoading"
  104. >
  105. 提 交
  106. </el-button>
  107. </template>
  108. </el-dialog>
  109. <el-dialog
  110. title="调整工艺"
  111. v-model="dialogVisibleOne"
  112. width="60%"
  113. v-loading="loadingOne"
  114. destroy-on-close
  115. >
  116. <byForm
  117. :formConfig="formConfigOne"
  118. :formOption="formOption"
  119. v-model="formData.dataOne"
  120. :rules="rulesOne"
  121. ref="byformOne"
  122. >
  123. <template #slot>
  124. <div style="width: 100%" class="tableDrop">
  125. <el-button type="primary" plain @click="clickAdd"
  126. >添加工序</el-button
  127. >
  128. <el-table
  129. :data="formData.dataOne.workOrderProductionProcessesList"
  130. style="width: 100%; margin-top: 16px"
  131. row-key="id"
  132. >
  133. <el-table-column label="工序名称" width="150">
  134. <template #default="{ row, $index }">
  135. <div style="width: 100%">
  136. <el-form-item
  137. :prop="
  138. 'workOrderProductionProcessesList.' + $index + '.name'
  139. "
  140. :rules="rulesOne.name"
  141. :inline-message="true"
  142. >
  143. <el-input v-model="row.name" placeholder="请输入" />
  144. </el-form-item>
  145. </div>
  146. </template>
  147. </el-table-column>
  148. <el-table-column label="工艺说明" width="300">
  149. <template #default="{ row, $index }">
  150. <div style="width: 100%">
  151. <el-form-item
  152. :prop="
  153. 'workOrderProductionProcessesList.' +
  154. $index +
  155. '.remarks'
  156. "
  157. :rules="rulesOne.remarks"
  158. :inline-message="true"
  159. >
  160. <el-input
  161. v-model="row.remarks"
  162. type="textarea"
  163. placeholder="请输入"
  164. />
  165. </el-form-item>
  166. </div>
  167. </template>
  168. </el-table-column>
  169. <el-table-column label="图纸">
  170. <template #default="{ row, $index }">
  171. <div style="width: 100%">
  172. <el-form-item
  173. :prop="
  174. 'workOrderProductionProcessesList.' + $index + '.name'
  175. "
  176. :rules="rulesOne.name"
  177. :inline-message="true"
  178. >
  179. <el-upload
  180. v-model:fileList="row.fileList"
  181. :show-file-list="false"
  182. class="upload-demo"
  183. action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
  184. :data="uploadData"
  185. :before-upload="
  186. (file) => handleBeforeUpload(file, $index)
  187. "
  188. accept=".pdf"
  189. >
  190. <el-icon
  191. :size="17"
  192. style="margin-top: 12px; cursor: pointer"
  193. >
  194. <Edit />
  195. </el-icon>
  196. </el-upload>
  197. <div>
  198. <div>
  199. <el-tag
  200. style="margin-left: 10px"
  201. class="ml-2"
  202. type="info"
  203. v-for="(item, index) in row.fileListCopy"
  204. :key="index"
  205. >{{ item.fileName }}</el-tag
  206. >
  207. </div>
  208. </div>
  209. </el-form-item>
  210. </div>
  211. </template>
  212. </el-table-column>
  213. <el-table-column
  214. align="center"
  215. label="操作"
  216. width="60"
  217. fixed="right"
  218. >
  219. <template #default="{ $index }">
  220. <el-button type="primary" link @click="clickDelete($index)"
  221. >删除</el-button
  222. >
  223. </template>
  224. </el-table-column>
  225. </el-table>
  226. </div>
  227. </template>
  228. </byForm>
  229. <template #footer>
  230. <el-button @click="dialogVisibleOne = false" size="large"
  231. >取 消</el-button
  232. >
  233. <el-button
  234. type="primary"
  235. @click="submitFormOne(0)"
  236. size="large"
  237. :loading="loadingOne"
  238. >
  239. 暂 存
  240. </el-button>
  241. <el-button
  242. type="primary"
  243. @click="submitFormOne(1)"
  244. size="large"
  245. :loading="loadingOne"
  246. >
  247. 提 交
  248. </el-button>
  249. </template>
  250. </el-dialog>
  251. <el-dialog
  252. v-model="openMaterial"
  253. title="选择产品"
  254. width="70%"
  255. append-to-body
  256. >
  257. <SelectMaterial @handleSelect="handleSelect"></SelectMaterial>
  258. <template #footer>
  259. <span class="dialog-footer">
  260. <el-button @click="openMaterial = false">取消</el-button>
  261. </span>
  262. </template>
  263. </el-dialog>
  264. </div>
  265. </template>
  266. <script setup>
  267. /* eslint-disable vue/no-unused-components */
  268. import { ElMessage, ElMessageBox } from "element-plus";
  269. import byTable from "@/components/byTable/index";
  270. import byForm from "@/components/byForm/index";
  271. import { computed, defineComponent, nextTick, ref } from "vue";
  272. import useUserStore from "@/store/modules/user";
  273. import SelectMaterial from "@/components/product/SelectMaterial";
  274. import Sortable from "sortablejs";
  275. const uploadData = ref({});
  276. let fileList = ref([]);
  277. let fileListCopy = ref([]);
  278. const loading = ref(false);
  279. const submitLoading = ref(false);
  280. const loadingOne = ref(false);
  281. const sourceList = ref({
  282. data: [],
  283. pagination: {
  284. total: 3,
  285. pageNum: 1,
  286. pageSize: 10,
  287. },
  288. });
  289. let dialogVisible = ref(false);
  290. let dialogVisibleOne = ref(false);
  291. let openMaterial = ref(false);
  292. let roomDialogVisible = ref(false);
  293. let modalType = ref("add");
  294. let rules = ref({
  295. quantity: [{ required: true, message: "请输入数量", trigger: "blur" }],
  296. });
  297. let rulesOne = ref({
  298. name: [{ required: true, message: "请输入工序名称", trigger: "blur" }],
  299. remarks: [{ required: true, message: "请输入工艺说明", trigger: "blur" }],
  300. });
  301. const { proxy } = getCurrentInstance();
  302. const materialUnit = ref([]);
  303. const workOrderSource = ref([]);
  304. const selectConfig = computed(() => [
  305. {
  306. label: "工单来源",
  307. prop: "source",
  308. data: workOrderSource.value,
  309. },
  310. ]);
  311. const config = computed(() => {
  312. return [
  313. {
  314. attrs: {
  315. label: "工单来源",
  316. prop: "source",
  317. },
  318. render(source) {
  319. return proxy.dictValueLabel(source, workOrderSource.value);
  320. },
  321. },
  322. {
  323. attrs: {
  324. label: "销售单号",
  325. prop: "contractCode",
  326. },
  327. },
  328. {
  329. attrs: {
  330. label: "合同创建时间",
  331. prop: "contractCreateTime",
  332. },
  333. },
  334. {
  335. attrs: {
  336. label: "工单单号",
  337. prop: "code",
  338. },
  339. },
  340. {
  341. attrs: {
  342. label: "产品名称",
  343. prop: "productName",
  344. },
  345. },
  346. {
  347. attrs: {
  348. label: "是否定制",
  349. prop: "isCustomized",
  350. },
  351. render(isCustomized) {
  352. return isCustomized == 1 ? "是" : "否";
  353. },
  354. },
  355. {
  356. attrs: {
  357. label: "工单数量",
  358. prop: "quantity",
  359. },
  360. },
  361. {
  362. attrs: {
  363. label: "已计划数量",
  364. prop: "arrangedQuantity",
  365. },
  366. },
  367. {
  368. attrs: {
  369. label: "完成率",
  370. prop: "completionRate",
  371. },
  372. render(completionRate) {
  373. if (completionRate !== undefined && completionRate !== "") {
  374. return completionRate + "%";
  375. }
  376. },
  377. },
  378. {
  379. attrs: {
  380. label: "操作",
  381. width: "200",
  382. align: "right",
  383. },
  384. // 渲染 el-button,一般用在最后一列。
  385. renderHTML(row) {
  386. return [
  387. row.isCustomized == 1
  388. ? {
  389. attrs: {
  390. label: "调整BOM",
  391. type: "primary",
  392. text: true,
  393. },
  394. el: "button",
  395. click() {
  396. getDtl(row);
  397. },
  398. }
  399. : {},
  400. row.isCustomized == 1
  401. ? {
  402. attrs: {
  403. label: "调整工艺",
  404. type: "primary",
  405. text: true,
  406. },
  407. el: "button",
  408. click() {
  409. getDtlOne(row);
  410. },
  411. }
  412. : {},
  413. ];
  414. },
  415. },
  416. ];
  417. });
  418. let formData = reactive({
  419. data: {},
  420. dataOne: {},
  421. treeData: [],
  422. });
  423. const formOption = reactive({
  424. inline: true,
  425. labelWidth: 100,
  426. itemWidth: 100,
  427. rules: [],
  428. });
  429. const byform = ref(null);
  430. const byformOne = ref(null);
  431. const treeData = ref([]);
  432. const formConfig = reactive([
  433. {
  434. type: "slot",
  435. slotName: "slot",
  436. },
  437. ]);
  438. const formConfigOne = reactive([
  439. {
  440. type: "slot",
  441. slotName: "slot",
  442. label: "",
  443. },
  444. ]);
  445. const getList = async (req) => {
  446. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  447. loading.value = true;
  448. proxy
  449. .post("/workOrder/pageByJxst", sourceList.value.pagination)
  450. .then((message) => {
  451. sourceList.value.data = message.rows;
  452. sourceList.value.pagination.total = message.total;
  453. setTimeout(() => {
  454. loading.value = false;
  455. }, 200);
  456. });
  457. };
  458. const openModal = () => {
  459. dialogVisible.value = true;
  460. modalType.value = "add";
  461. formData.data = {};
  462. };
  463. const submitForm = (type) => {
  464. byform.value.handleSubmit((valid) => {
  465. formData.data.bomStatus = type;
  466. submitLoading.value = true;
  467. proxy.post("/workOrderBom/edit", formData.data).then(
  468. (res) => {
  469. ElMessage({
  470. message: "操作成功",
  471. type: "success",
  472. });
  473. dialogVisible.value = false;
  474. submitLoading.value = false;
  475. getList();
  476. },
  477. (err) => (submitLoading.value = false)
  478. );
  479. });
  480. };
  481. const submitFormOne = (type) => {
  482. byformOne.value.handleSubmit((valid) => {
  483. for (
  484. let i = 0;
  485. i < formData.dataOne.workOrderProductionProcessesList.length;
  486. i++
  487. ) {
  488. const e = formData.dataOne.workOrderProductionProcessesList[i];
  489. if (!e.fileListCopy.length > 0) {
  490. return ElMessage({
  491. message: "请上传图纸",
  492. type: "info",
  493. });
  494. }
  495. }
  496. for (
  497. let i = 0;
  498. i < formData.dataOne.workOrderProductionProcessesList.length;
  499. i++
  500. ) {
  501. const e = formData.dataOne.workOrderProductionProcessesList[i];
  502. e.fileList = e.fileListCopy;
  503. }
  504. formData.dataOne.technologyStatus = type;
  505. loadingOne.value = true;
  506. proxy.post("/workOrderProductionProcesses/edit", formData.dataOne).then(
  507. (res) => {
  508. ElMessage({
  509. message: "操作成功",
  510. type: "success",
  511. });
  512. dialogVisibleOne.value = false;
  513. loadingOne.value = false;
  514. getList();
  515. },
  516. (err) => (loadingOne.value = false)
  517. );
  518. });
  519. };
  520. const getDtl = (row) => {
  521. modalType.value = "edit";
  522. proxy.post("/workOrderBom/list", { workOrderId: row.id }).then((res) => {
  523. formData.data = {
  524. workOrderBomList: res,
  525. workOrderId: row.id,
  526. };
  527. dialogVisible.value = true;
  528. });
  529. };
  530. const handleRemove = (index) => {
  531. formData.data.workOrderBomList.splice(index, 1);
  532. };
  533. // 对el-table进行拖拽排序
  534. const initSort = () => {
  535. const tbody = document.querySelector(
  536. ".tableDrop .el-table__body-wrapper tbody"
  537. );
  538. Sortable.create(tbody, {
  539. onEnd({ newIndex, oldIndex }) {
  540. if (newIndex == oldIndex) return;
  541. formData.dataOne.workOrderProductionProcessesList.splice(
  542. newIndex,
  543. 0,
  544. formData.dataOne.workOrderProductionProcessesList.splice(oldIndex, 1)[0]
  545. );
  546. var newArray = formData.dataOne.workOrderProductionProcessesList.slice(0);
  547. formData.dataOne.workOrderProductionProcessesList = [];
  548. nextTick(() => {
  549. formData.dataOne.workOrderProductionProcessesList = newArray;
  550. });
  551. },
  552. });
  553. };
  554. const getDtlOne = (row) => {
  555. modalType.value = "edit";
  556. proxy
  557. .post("/workOrderProductionProcesses/list", { workOrderId: row.id })
  558. .then((res) => {
  559. dialogVisibleOne.value = true;
  560. res = res.map((x) => ({
  561. ...x,
  562. fileList: [
  563. {
  564. fileName: x.fileName,
  565. id: x.oldId,
  566. },
  567. ],
  568. fileListCopy: [
  569. {
  570. fileName: x.fileName,
  571. id: x.oldId,
  572. },
  573. ],
  574. }));
  575. formData.dataOne = {
  576. workOrderId: row.id,
  577. workOrderProductionProcessesList: res,
  578. };
  579. nextTick(() => {
  580. initSort();
  581. });
  582. });
  583. };
  584. const getDict = () => {
  585. proxy.getDictOne(["material_unit", "work_order_source"]).then((res) => {
  586. materialUnit.value = res["material_unit"].map((x) => ({
  587. label: x.dictValue,
  588. value: x.dictKey,
  589. }));
  590. workOrderSource.value = res["work_order_source"].map((x) => ({
  591. label: x.dictValue,
  592. value: x.dictKey,
  593. }));
  594. });
  595. };
  596. getList();
  597. getDict();
  598. const handleSelect = (row) => {
  599. const flag = formData.data.workOrderBomList.some(
  600. (x) => x.productId === row.id
  601. );
  602. if (flag)
  603. return ElMessage({
  604. message: "该物料已选择",
  605. type: "info",
  606. });
  607. formData.data.workOrderBomList.push({
  608. productId: row.id,
  609. productCode: row.code,
  610. productName: row.name,
  611. productUnit: row.unit,
  612. quantity: null,
  613. });
  614. return ElMessage({
  615. message: "选择成功",
  616. type: "success",
  617. });
  618. };
  619. const clickAdd = () => {
  620. formData.dataOne.workOrderProductionProcessesList.push({
  621. fileList: [],
  622. fileListCopy: [],
  623. name: "",
  624. remarks: "",
  625. });
  626. };
  627. const clickDelete = (index) => {
  628. formData.dataOne.workOrderProductionProcessesList.splice(index, 1);
  629. };
  630. const handleBeforeUpload = async (file, index) => {
  631. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  632. uploadData.value = res.uploadBody;
  633. formData.dataOne.workOrderProductionProcessesList[index].fileListCopy = [
  634. {
  635. id: res.id,
  636. fileName: res.fileName,
  637. path: res.fileUrl,
  638. url: res.fileUrl,
  639. uid: file.uid,
  640. },
  641. ];
  642. };
  643. </script>
  644. <style lang="scss" scoped>
  645. .tenant {
  646. padding: 20px;
  647. }
  648. </style>