index.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. <template>
  2. <div class="box">
  3. <div class="tree">
  4. <treeList title="产品分类" submitType="1" :data="treeListData" v-model="sourceList.pagination.productClassifyId" @change="treeChange"
  5. @changeTreeList="getTreeList">
  6. </treeList>
  7. </div>
  8. <div class="content">
  9. <byTable :source="sourceList.data" :tableHeight="tableHeight" :pagination="sourceList.pagination" :config="config" :loading="loading"
  10. highlight-current-row :selectConfig="selectConfig" :action-list="[
  11. {
  12. text: '导出Excel',
  13. action: () => exportExcel(),
  14. disabled: false,
  15. },
  16. {
  17. text: '添加',
  18. action: () => openModal('add'),
  19. disabled: false,
  20. },]" @get-list="getList">
  21. <template #name="{ item }">
  22. <div>
  23. <span class="el-click">{{ item.name }}</span>
  24. </div>
  25. </template>
  26. <template #pic="{ item }">
  27. <div v-if="item.fileList.length > 0">
  28. <img :src="item.fileList[0].fileUrl" class="pic" @click="handleClickFile(item.fileList[0])" />
  29. </div>
  30. <div v-else></div>
  31. </template>
  32. <template #size="{ item }">
  33. <div v-if="item['length'] && item.width && item.height">
  34. <span>{{ item['length'] }}cm</span>*
  35. <span>{{ item.width }}cm</span>*
  36. <span>{{ item.height }}cm</span>
  37. </div>
  38. <div v-else></div>
  39. </template>
  40. <template #price="{ item }">
  41. <div v-if="item.price">
  42. <span>{{ item.currency }} {{ moneyFormat(item.price ,2)}}</span>
  43. </div>
  44. <div v-else></div>
  45. </template>
  46. </byTable>
  47. </div>
  48. <el-dialog :title="modalType == 'add' ? '添加产品' : '编辑产品'" v-model="dialogVisible" width="80%" destroy-on-close>
  49. <div class="public_height_dialog">
  50. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="formDom" v-loading="submitLoading">
  51. <template #nameEnglish>
  52. <div style="width: 100%">
  53. <el-input v-model="formData.data.nameEnglish" placeholder="请输入" onkeyup="value=value.replace(/[^\x00-\xff]/g, '')"></el-input>
  54. </div>
  55. </template>
  56. <template #productionFile>
  57. <div style="width: 100%">
  58. <span style="color:#409eff;cursor:pointer" @click="handleClickUpload('prodFilePath',true)"
  59. v-if="!formData.data.prodFilePath">点击上传</span>
  60. <span style="color:#409eff;cursor:pointer" @click="handleClickUpload('prodFilePath',false)" v-else>点击查看</span>
  61. </div>
  62. </template>
  63. <template #productionFileOne>
  64. <div style="width: 100%">
  65. <span style="color:#409eff;cursor:pointer" @click="handleClickUpload('prodImgPath',true)" v-if="!formData.data.prodImgPath">点击上传</span>
  66. <span style="color:#409eff;cursor:pointer" @click="handleClickUpload('prodImgPath',false)" v-else>点击查看</span>
  67. </div>
  68. </template>
  69. <template #size>
  70. <div style="width: 100%">
  71. <el-form-item label="尺寸" class="margin-b-0 wid100" required>
  72. <el-row>
  73. <el-col :span="8">
  74. <el-form-item prop="length" label-width="0px" class="margin-b-0 wid100">
  75. <el-input-number v-model="formData.data['length']" placeholder="长 (cm)" style="width: 100%" :precision="2" :controls="false"
  76. :min="0" onmousewheel="return false;" />
  77. </el-form-item>
  78. </el-col>
  79. <el-col :span="8">
  80. <el-form-item prop="width" label-width="0px" class="margin-b-0 wid100">
  81. <el-input-number v-model="formData.data.width" placeholder="宽 (cm)" style="width: 100%" :precision="2" :controls="false"
  82. :min="0" onmousewheel="return false;" />
  83. </el-form-item>
  84. </el-col>
  85. <el-col :span="8">
  86. <el-form-item prop="height" label-width="0px" class="margin-b-0 wid100">
  87. <el-input-number v-model="formData.data.height" placeholder="高 (cm)" style="width: 100%" :precision="2" :controls="false"
  88. :min="0" onmousewheel="return false;" />
  89. </el-form-item>
  90. </el-col>
  91. </el-row>
  92. </el-form-item>
  93. </div>
  94. </template>
  95. </byForm>
  96. </div>
  97. <template #footer>
  98. <el-button @click="dialogVisible = false" size="defualt" v-debounce>取 消</el-button>
  99. <el-button type="primary" @click="submitForm()" size="defualt" v-debounce>确 定</el-button>
  100. </template>
  101. </el-dialog>
  102. <el-dialog :title="'BOM 配置'" v-model="bomDialog" width="60%" destroy-on-close>
  103. <!-- <div class="public_height_dialog"> -->
  104. <byForm :formConfig="bomFormConfig" :formOption="bomFormOption" v-model="formData.bomData" :rules="bomRules" ref="bomFormDom"
  105. v-loading="submitLoading">
  106. <template #accessories>
  107. <div style="width: 100%">
  108. <el-button type="primary" @click="openSelectMaterial = true" plain>选择</el-button>
  109. <el-table :data="formData.bomData.productBomDetailList" style="width: 100%; margin-top: 16px">
  110. <el-table-column prop="materialName" label="物料名称" min-width="130" />
  111. <el-table-column prop="materialCode" label="物料编码" width="150" />
  112. <el-table-column label="数量" width="150">
  113. <template #default="{ row, $index }">
  114. <div style="width: 100%">
  115. <el-form-item :prop="'productBomDetailList.' + $index + '.quantity'" :rules="bomRules.quantity" :inline-message="true"
  116. class="margin-b-0 wid100">
  117. <el-input-number onmousewheel="return false;" v-model="row.quantity" placeholder="请输入" style="width: 100%" :precision="0"
  118. :controls="false" :min="1" />
  119. </el-form-item>
  120. </div>
  121. </template>
  122. </el-table-column>
  123. <el-table-column label="操作" width="60" align="center" fixed="right">
  124. <template #default="{ $index }">
  125. <el-button type="primary" link @click="handleRemove($index)">删除</el-button>
  126. </template>
  127. </el-table-column>
  128. </el-table>
  129. </div>
  130. </template>
  131. </byForm>
  132. <!-- </div> -->
  133. <template #footer>
  134. <el-button @click="bomDialog = false" size="defualt" v-debounce>取 消</el-button>
  135. <el-button type="primary" @click="submitBomForm()" size="defualt" v-debounce>确 定</el-button>
  136. </template>
  137. </el-dialog>
  138. <el-dialog :title="'物料选择'" v-model="openSelectMaterial" width="90%" destroy-on-close>
  139. <SelectMaterial :isNeRawMaterial="'1'" @selectMaterial="selectMaterial"></SelectMaterial>
  140. <template #footer>
  141. <el-button @click="bomDialog = false" size="defualt" v-debounce>取 消</el-button>
  142. </template>
  143. </el-dialog>
  144. <el-dialog title="导入产品" v-model="openExcelDialog" width="400">
  145. <div v-loading="excelLoading">
  146. <el-upload :action="actionUrl + '/productInfo/excelImportByEhsd'" :headers="headers" :on-success="handleSuccess" :on-progress="handleProgress"
  147. :show-file-list="false" :on-error="handleError" accept=".xlsx">
  148. <el-button type="primary">点击导入</el-button>
  149. </el-upload>
  150. </div>
  151. <template #footer>
  152. <el-button @click="openExcelDialog = false" size="default">取 消</el-button>
  153. </template>
  154. </el-dialog>
  155. </div>
  156. </template>
  157. <script setup>
  158. import byTable from "@/components/byTable/index";
  159. import byForm from "@/components/byForm/index";
  160. import treeList from "@/components/product/treeList";
  161. import SelectMaterial from "@/components/product/SelectMaterial.vue";
  162. import { getToken } from "@/utils/auth";
  163. const { proxy } = getCurrentInstance();
  164. const actionUrl = import.meta.env.VITE_APP_BASE_API;
  165. const loading = ref(false);
  166. const submitLoading = ref(false);
  167. const treeData = ref([]);
  168. const treeDataOne = ref([]);
  169. const treeListData = ref([]);
  170. const technologyData = ref([]);
  171. const companyData = ref([]);
  172. const currencyData = computed(
  173. () => proxy.useUserStore().allDict["account_currency"]
  174. );
  175. const headers = ref({ Authorization: "Bearer " + getToken() });
  176. const tableHeight = ref(0);
  177. const getTableHeight = () => {
  178. tableHeight.value = window.innerHeight - 245;
  179. };
  180. getTableHeight();
  181. window.addEventListener("resize", () => {
  182. getTableHeight();
  183. });
  184. const sourceList = ref({
  185. data: [],
  186. pagination: {
  187. total: 3,
  188. pageNum: 1,
  189. pageSize: 10,
  190. type: "",
  191. productClassifyId: "",
  192. keyword: "",
  193. definition: "1",
  194. companyId: "",
  195. },
  196. });
  197. const dialogVisible = ref(false);
  198. const openExcelDialog = ref(false);
  199. const excelLoading = ref(false);
  200. const modalType = ref("add");
  201. const rules = ref({
  202. companyId: [{ required: true, message: "请选择归属公司", trigger: "change" }],
  203. productClassifyId: [
  204. { required: true, message: "请选择产品分类", trigger: "change" },
  205. ],
  206. name: [{ required: true, message: "请输入产品名称", trigger: "blur" }],
  207. customCode: [{ required: true, message: "请输入产品编号", trigger: "blur" }],
  208. length: [{ required: true, message: "请输入长 (cm)", trigger: "blur" }],
  209. width: [{ required: true, message: "请输入宽 (cm)", trigger: "blur" }],
  210. height: [{ required: true, message: "请输入高 (cm)", trigger: "blur" }],
  211. });
  212. const props = defineProps({
  213. selectStatus: Boolean,
  214. });
  215. const selectConfig = computed(() => [
  216. {
  217. label: "归属公司",
  218. prop: "companyId",
  219. data: companyData.value,
  220. },
  221. ]);
  222. const config = computed(() => {
  223. return [
  224. {
  225. attrs: {
  226. label: "图片",
  227. slot: "pic",
  228. align: "center",
  229. width: 80,
  230. },
  231. },
  232. {
  233. attrs: {
  234. label: "归属公司",
  235. prop: "companyName",
  236. width: 150,
  237. },
  238. },
  239. {
  240. attrs: {
  241. label: "产品分类",
  242. prop: "classifyName",
  243. "min-width": 200,
  244. },
  245. },
  246. {
  247. attrs: {
  248. label: "产品编码",
  249. prop: "customCode",
  250. width: 180,
  251. },
  252. },
  253. {
  254. attrs: {
  255. label: "产品名称",
  256. slot: "name",
  257. "min-width": 200,
  258. },
  259. },
  260. // {
  261. // attrs: {
  262. // label: "产品英文名",
  263. // prop: "nameEnglish",
  264. // "min-width": 120,
  265. // },
  266. // },
  267. // {
  268. // attrs: {
  269. // label: "产品规格",
  270. // prop: "spec",
  271. // width: 120,
  272. // },
  273. // },
  274. {
  275. attrs: {
  276. label: "尺寸",
  277. slot: "size",
  278. width: 180,
  279. },
  280. },
  281. {
  282. attrs: {
  283. label: "净重",
  284. prop: "netWeight",
  285. width: 100,
  286. },
  287. render(val) {
  288. if (val) {
  289. return val + " kg";
  290. }
  291. },
  292. },
  293. {
  294. attrs: {
  295. label: "销售价",
  296. slot: "price",
  297. width: 100,
  298. },
  299. },
  300. // {
  301. // attrs: {
  302. // label: "海关编码",
  303. // prop: "hsCode",
  304. // width: 100,
  305. // },
  306. // },
  307. {
  308. attrs: {
  309. label: "原材料编码",
  310. prop: "rawMaterialCode",
  311. width: 120,
  312. },
  313. },
  314. {
  315. attrs: {
  316. label: "原材料",
  317. prop: "rawMaterialName",
  318. "min-width": 300,
  319. },
  320. },
  321. {
  322. attrs: {
  323. label: "操作",
  324. width: "160",
  325. align: "center",
  326. fixed: "right",
  327. },
  328. renderHTML(row) {
  329. return [
  330. // {
  331. // attrs: {
  332. // label: "选择",
  333. // type: "primary",
  334. // text: true,
  335. // },
  336. // el: "button",
  337. // click() {
  338. // clickSelect(row);
  339. // },
  340. // }
  341. {
  342. attrs: {
  343. label: "BOM",
  344. type: "primary",
  345. text: true,
  346. },
  347. el: "button",
  348. click() {
  349. bomSetting(row);
  350. },
  351. },
  352. {
  353. attrs: {
  354. label: "修改",
  355. type: "primary",
  356. text: true,
  357. },
  358. el: "button",
  359. click() {
  360. getDtl(row);
  361. },
  362. },
  363. {
  364. attrs: {
  365. label: "删除",
  366. type: "danger",
  367. text: true,
  368. },
  369. el: "button",
  370. click() {
  371. proxy
  372. .msgConfirm()
  373. .then((res) => {
  374. proxy
  375. .post("/productInfo/delete", {
  376. id: row.id,
  377. })
  378. .then((res) => {
  379. proxy.msgTip("删除成功", 1);
  380. getList();
  381. });
  382. })
  383. .catch((err) => {});
  384. },
  385. },
  386. ];
  387. },
  388. },
  389. ];
  390. });
  391. const formData = reactive({
  392. data: {},
  393. bomData: {},
  394. });
  395. const formOption = reactive({
  396. disabled: false,
  397. inline: true,
  398. labelWidth: 100,
  399. itemWidth: 100,
  400. });
  401. const formDom = ref(null);
  402. const formConfig = computed(() => {
  403. return [
  404. {
  405. type: "title1",
  406. title: "基本信息",
  407. },
  408. {
  409. type: "treeSelect",
  410. prop: "companyId",
  411. label: "归属公司",
  412. data: treeDataOne.value,
  413. propsTreeLabel: "deptName",
  414. propsTreeValue: "deptId",
  415. itemWidth: 50,
  416. },
  417. {
  418. type: "treeSelect",
  419. prop: "productClassifyId",
  420. label: "产品分类",
  421. data: treeData.value,
  422. itemWidth: 50,
  423. disabled: false,
  424. },
  425. {
  426. type: "input",
  427. prop: "name",
  428. label: "产品名称",
  429. itemWidth: 50,
  430. disabled: false,
  431. },
  432. {
  433. type: "slot",
  434. slotName: "nameEnglish",
  435. label: "英文名",
  436. itemWidth: 50,
  437. },
  438. {
  439. type: "input",
  440. prop: "customCode",
  441. label: "产品编号",
  442. itemWidth: 50,
  443. disabled: false,
  444. },
  445. {
  446. type: "uploadImg",
  447. // listType: "picture-card",
  448. // limit: 1,
  449. // accept: ".gif, .jpeg, .jpg, .png",
  450. prop: "fileList",
  451. imgProp: "imageUrl",
  452. label: "产品缩略图",
  453. },
  454. {
  455. type: "slot",
  456. slotName: "productionFileOne",
  457. label: "产品原图",
  458. itemWidth: 100,
  459. },
  460. {
  461. type: "upload",
  462. listType: "text",
  463. accept: "",
  464. limit: 1,
  465. prop: "prodFileList",
  466. label: "生产文件",
  467. },
  468. // {
  469. // type: "slot",
  470. // slotName: "productionFile",
  471. // label: "生产文件",
  472. // itemWidth: 100,
  473. // },
  474. {
  475. type: "title1",
  476. title: "属性信息",
  477. },
  478. {
  479. type: "selectInput",
  480. prop: "price",
  481. selectProp: "currency",
  482. label: "销售价",
  483. itemWidth: 50,
  484. disabledSelect: true,
  485. data: currencyData.value,
  486. },
  487. {
  488. type: "select",
  489. prop: "technologyId",
  490. label: "生产工艺",
  491. itemWidth: 50,
  492. data: technologyData.value,
  493. filterable: true,
  494. disabled: false,
  495. },
  496. {
  497. type: "input",
  498. prop: "spec",
  499. label: "规格型号",
  500. itemWidth: 50,
  501. disabled: false,
  502. },
  503. {
  504. type: "slot",
  505. slotName: "size",
  506. prop: "",
  507. label: "",
  508. itemWidth: 50,
  509. disabled: false,
  510. },
  511. // {
  512. // type: "select",
  513. // prop: "innerPackMethod",
  514. // label: "内包装方式",
  515. // required: true,
  516. // itemWidth: 50,
  517. // multiple: true,
  518. // data: innerMethon.value,
  519. // filterable: true,
  520. // placeholder: "内包装方式",
  521. // style: {
  522. // width: "100%",
  523. // },
  524. // disabled: false,
  525. // },
  526. // {
  527. // type: "select",
  528. // prop: "outerPackMethod",
  529. // label: "外包装方式",
  530. // required: true,
  531. // itemWidth: 50,
  532. // multiple: true,
  533. // data: outsideMethon.value,
  534. // filterable: true,
  535. // placeholder: "外包装方式",
  536. // style: {
  537. // width: "100%",
  538. // },
  539. // disabled: false,
  540. // },
  541. {
  542. type: "number",
  543. prop: "netWeight",
  544. label: "净重(kg)",
  545. precision: 2,
  546. min: 0,
  547. controls: false,
  548. itemWidth: 50,
  549. },
  550. {
  551. type: "input",
  552. prop: "hsCode",
  553. label: "海关编码",
  554. itemWidth: 50,
  555. disabled: false,
  556. },
  557. {
  558. type: "input",
  559. itemType: "textarea",
  560. prop: "remark",
  561. label: "备注",
  562. itemWidth: 100,
  563. },
  564. ];
  565. });
  566. const getList = (req) => {
  567. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  568. loading.value = true;
  569. proxy.post("/productInfo/page", sourceList.value.pagination).then(
  570. (res) => {
  571. sourceList.value.data = res.rows.map((x) => ({
  572. ...x,
  573. fileList: [],
  574. }));
  575. sourceList.value.pagination.total = res.total;
  576. setTimeout(() => {
  577. loading.value = false;
  578. }, 200);
  579. let productIdList = res.rows.map((x) => x.id);
  580. // 请求文件数据并回显
  581. if (productIdList.length > 0) {
  582. // proxy.getFile(productIdList, sourceList.value.data, "id");
  583. proxy
  584. .post("/fileInfo/getList", { businessIdList: productIdList })
  585. .then((fileObj) => {
  586. for (let i = 0; i < sourceList.value.data.length; i++) {
  587. const ele = sourceList.value.data[i];
  588. for (const key in fileObj) {
  589. if (
  590. ele.id == key &&
  591. fileObj[ele.id] &&
  592. fileObj[ele.id].length > 0
  593. ) {
  594. ele.fileList = fileObj[ele.id].filter(
  595. (x) => x.businessType == "0"
  596. );
  597. }
  598. }
  599. }
  600. });
  601. }
  602. },
  603. (err) => {
  604. loading.value = false;
  605. }
  606. );
  607. };
  608. const getDict = () => {
  609. proxy
  610. .get("/tenantDept/list", {
  611. pageNum: 1,
  612. pageSize: 9999,
  613. keyword: "",
  614. tenantId: proxy.useUserStore().user.tenantId,
  615. type: 0,
  616. })
  617. .then((res) => {
  618. companyData.value = res.data.map((x) => ({
  619. ...x,
  620. label: x.deptName,
  621. value: x.deptId,
  622. }));
  623. treeDataOne.value = proxy.handleTree(res.data, "deptId");
  624. });
  625. };
  626. getDict();
  627. const treeChange = (e) => {
  628. if (e.id != undefined) {
  629. sourceList.value.pagination.productClassifyId = e.id;
  630. getList({ productClassifyId: e.id });
  631. }
  632. };
  633. const openModal = () => {
  634. dialogVisible.value = true;
  635. modalType.value = "add";
  636. formData.data = {
  637. definition: "1",
  638. fileList: [],
  639. currency: "",
  640. prodFileList: [],
  641. };
  642. if (currencyData.value && currencyData.value.length > 0) {
  643. formData.data.currency = currencyData.value[0].dictKey;
  644. }
  645. };
  646. const openExcel = () => {
  647. openExcelDialog.value = true;
  648. };
  649. const submitForm = () => {
  650. formDom.value.handleSubmit((valid) => {
  651. if (!formData.data.fileList.length > 0) {
  652. return proxy.msgTip("请上传图片", 2);
  653. }
  654. // formData.data.fileList = formData.data.fileList.map((x) => ({
  655. // id: x.id,
  656. // fileName: x.fileName,
  657. // fileUrl: x.fileUrl,
  658. // }));
  659. submitLoading.value = true;
  660. proxy.post("/productInfo/" + modalType.value, formData.data).then(
  661. (res) => {
  662. proxy.msgTip("操作成功", 1);
  663. dialogVisible.value = false;
  664. submitLoading.value = false;
  665. getList();
  666. },
  667. (err) => {
  668. submitLoading.value = false;
  669. }
  670. );
  671. });
  672. };
  673. const getTreeList = () => {
  674. proxy
  675. .post("/productClassify/tree", { parentId: "", name: "", definition: "1" })
  676. .then((message) => {
  677. treeListData.value = [
  678. {
  679. id: "",
  680. label: "全部",
  681. parentId: "",
  682. children: message,
  683. },
  684. ];
  685. treeData.value = message;
  686. });
  687. };
  688. const getTechnologyData = () => {
  689. proxy.post("/technology/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  690. technologyData.value = res.rows;
  691. });
  692. };
  693. const getDtl = (row) => {
  694. modalType.value = "edit";
  695. proxy.post("/productInfo/detail", { id: row.id }).then((res) => {
  696. formData.data = res;
  697. formData.data.fileList = row.fileList.map((x) => ({
  698. ...x,
  699. url: x.fileUrl,
  700. name: x.fileName,
  701. }));
  702. if (formData.data.fileList.length > 0) {
  703. formData.data.imageUrl = formData.data.fileList[0].fileUrl;
  704. }
  705. proxy
  706. .post("/fileInfo/getList", { businessIdList: [row.id] })
  707. .then((fileObj) => {
  708. if (fileObj[row.id] && fileObj[row.id].length > 0) {
  709. formData.data.prodFileList = fileObj[row.id]
  710. .filter((x) => x.businessType == "2")
  711. .map((item) => {
  712. return {
  713. ...item,
  714. name: item.fileName,
  715. url: item.fileUrl,
  716. };
  717. });
  718. } else {
  719. formData.data.prodFileList = [];
  720. }
  721. });
  722. dialogVisible.value = true;
  723. });
  724. };
  725. const rawMaterialData = ref([]);
  726. const getRawMaterialData = () => {
  727. proxy.post("/productInfo/page", { productClassifyId: 100 }).then((res) => {
  728. rawMaterialData.value = res.rows;
  729. });
  730. };
  731. getRawMaterialData();
  732. const bomDialog = ref(false);
  733. const bomFormDom = ref(null);
  734. const bomFormOption = reactive({
  735. disabled: false,
  736. inline: true,
  737. labelWidth: 100,
  738. itemWidth: 100,
  739. });
  740. const bomFormConfig = computed(() => {
  741. return [
  742. {
  743. type: "select",
  744. prop: "rawMaterialId",
  745. label: "原材料",
  746. itemWidth: 100,
  747. data: rawMaterialData.value,
  748. filterable: true,
  749. disabled: false,
  750. },
  751. {
  752. type: "slot",
  753. slotName: "accessories",
  754. label: "包材辅料",
  755. itemWidth: 100,
  756. },
  757. ];
  758. });
  759. const bomRules = ref({
  760. rawMaterialId: [
  761. { required: true, message: "请选择原材料", trigger: "change" },
  762. ],
  763. quantity: [{ required: true, message: "请输入数量", trigger: "blur" }],
  764. });
  765. const bomSetting = (row) => {
  766. bomDialog.value = true;
  767. proxy.post("/productBomInfo/detail", { id: row.id }).then((res) => {
  768. formData.bomData = {
  769. id: res.id,
  770. rawMaterialId: res.rawMaterialId,
  771. productBomDetailList: [],
  772. };
  773. if (res.productBomDetailList && res.productBomDetailList.length > 0) {
  774. formData.bomData.productBomDetailList = res.productBomDetailList.filter(
  775. (x) => x.type == 2
  776. );
  777. } else {
  778. formData.bomData.productBomDetailList = [];
  779. }
  780. });
  781. };
  782. const submitBomForm = () => {
  783. bomFormDom.value.handleSubmit((valid) => {
  784. if (!formData.bomData.productBomDetailList.length > 0) {
  785. return proxy.msgTip("请选择包材辅料", 2);
  786. }
  787. formData.bomData.productBomDetailList.push({
  788. materialId: formData.bomData.rawMaterialId,
  789. type: 1,
  790. quantity: 1,
  791. });
  792. submitLoading.value = true;
  793. proxy.post("/productBomInfo/edit", formData.bomData).then(
  794. (res) => {
  795. proxy.msgTip("操作成功", 1);
  796. getList();
  797. bomDialog.value = false;
  798. submitLoading.value = false;
  799. },
  800. (err) => {
  801. submitLoading.value = false;
  802. }
  803. );
  804. });
  805. };
  806. const openSelectMaterial = ref(false);
  807. const selectMaterial = (row) => {
  808. let flag = formData.bomData.productBomDetailList.some(
  809. (x) => x.materialId == row.id
  810. );
  811. if (!flag) {
  812. formData.bomData.productBomDetailList.push({
  813. type: 2,
  814. materialName: row.name,
  815. materialCode: row.customCode,
  816. materialId: row.id,
  817. quantity: null,
  818. });
  819. proxy.msgTip("选择成功");
  820. } else {
  821. proxy.msgTip("该物料已选择", 2);
  822. }
  823. };
  824. const handleRemove = (index) => {
  825. formData.bomData.productBomDetailList.splice(index, 1);
  826. };
  827. const handleClickFile = (file) => {
  828. window.open(file.fileUrl, "_blank");
  829. };
  830. const handleProgress = () => {
  831. excelLoading.value = true;
  832. };
  833. const handleError = (err) => {
  834. proxy.msgTip(`${err},请重试`, 2);
  835. openExcelDialog.value = false;
  836. excelLoading.value = false;
  837. };
  838. const handleSuccess = (res) => {
  839. if (res.code != 200) {
  840. return proxy.msgTip(`${err},请重试`, 2);
  841. } else {
  842. proxy.msgTip(`导入成功`, 1);
  843. openExcelDialog.value = false;
  844. excelLoading.value = false;
  845. getList();
  846. }
  847. };
  848. const clickSelect = (item) => {
  849. proxy.$emit("selectProduct", item);
  850. };
  851. const handleClickUpload = async (att, flag) => {
  852. let res = null;
  853. let path = "";
  854. if (flag) {
  855. proxy.msgTip("请稍后", 2);
  856. res = await proxy.post("/fileService/createTempFolder");
  857. if (res && res.path) {
  858. formData.data[att] = res.path;
  859. path = res.path;
  860. }
  861. } else {
  862. path = formData.data[att];
  863. }
  864. let a = document.createElement("a");
  865. a.href = "printer://" + "ftp://121.37.194.75/" + path + "/";
  866. a.style.display = "none";
  867. document.body.appendChild(a);
  868. a.click();
  869. document.body.removeChild(a);
  870. };
  871. getTechnologyData();
  872. getTreeList();
  873. getList();
  874. const exportExcel = () => {
  875. proxy.msgTip("请稍后", 2);
  876. proxy
  877. .postTwo("/productInfo/exportExcel", sourceList.value.pagination)
  878. .then((res) => {
  879. proxy.downloadFile(res, "产品数据.xlsx");
  880. });
  881. };
  882. </script>
  883. <style lang="scss" scoped>
  884. .box {
  885. padding: 10px;
  886. display: flex;
  887. justify-content: space-between;
  888. .tree {
  889. width: 300px;
  890. }
  891. .content {
  892. width: calc(100% - 310px);
  893. }
  894. }
  895. .pic {
  896. object-fit: contain;
  897. width: 50px;
  898. height: 50px;
  899. cursor: pointer;
  900. vertical-align: middle;
  901. }
  902. </style>