index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. <template>
  2. <div class="user">
  3. <div class="tree">
  4. <treeList title="物料分类" submitType="2" :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" :pagination="sourceList.pagination" :config="config" :loading="loading" highlight-current-row
  10. :selectConfig="selectConfig" :action-list="[
  11. {
  12. text: '添加物料',
  13. action: () => openModal('add'),
  14. disabled: false,
  15. },
  16. ]" @get-list="getList">
  17. <template #pic="{ item }">
  18. <div v-if="item.fileList.length > 0">
  19. <img :src="item.fileList[0].fileUrl" class="pic" @click="handleClickFile(item.fileList[0])" />
  20. </div>
  21. <div v-else></div>
  22. </template>
  23. <template #name="{ item }">
  24. <div>
  25. <span class="el-click">{{ item.name }}</span>
  26. </div>
  27. </template>
  28. <template #size="{ item }">
  29. <div v-if="item['length'] && item.width && item.height">
  30. <span>{{ item['length'] }}cm</span>*
  31. <span>{{ item.width }}cm</span>*
  32. <span>{{ item.height }}cm</span>
  33. </div>
  34. <div v-else></div>
  35. </template>
  36. <template #price="{ item }">
  37. <div v-if="item.price">
  38. <span>{{ item.currency }} {{ moneyFormat(item.price ,2)}}</span>
  39. </div>
  40. <div v-else></div>
  41. </template>
  42. <template #costPrice="{ item }">
  43. <div v-if="item.costPrice">
  44. <span>{{ item.currency }} {{ moneyFormat(item.costPrice ,2)}}</span>
  45. </div>
  46. <div v-else></div>
  47. </template>
  48. </byTable>
  49. </div>
  50. <el-dialog :title="modalType == 'add' ? '添加物料' : '编辑物料'" v-model="dialogVisible" width="80%" destroy-on-close>
  51. <div class="public_height_dialog">
  52. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="formDom" v-loading="submitLoading">
  53. <template #size>
  54. <div style="width: 100%">
  55. <el-row>
  56. <el-col :span="8">
  57. <el-form-item prop="length" label-width="0px" class="margin-b-0 wid100">
  58. <el-input-number v-model="formData.data.length" placeholder="请输入" style="width: 100%" :precision="2" :controls="false" :min="0"
  59. onmousewheel="return false;" />
  60. </el-form-item>
  61. </el-col>
  62. <el-col :span="8">
  63. <el-form-item prop="width" label-width="0px" class="margin-b-0 wid100">
  64. <el-input-number v-model="formData.data.width" placeholder="请输入" style="width: 100%" :precision="2" :controls="false" :min="0"
  65. onmousewheel="return false;" />
  66. </el-form-item>
  67. </el-col>
  68. <el-col :span="8">
  69. <el-form-item prop="height" label-width="0px" class="margin-b-0 wid100">
  70. <el-input-number v-model="formData.data.height" placeholder="请输入" style="width: 100%" :precision="2" :controls="false" :min="0"
  71. onmousewheel="return false;" />
  72. </el-form-item>
  73. </el-col>
  74. </el-row>
  75. </div>
  76. </template>
  77. </byForm>
  78. </div>
  79. <template #footer>
  80. <el-button @click="dialogVisible = false" size="defualt">取 消</el-button>
  81. <el-button type="primary" @click="submitForm()" size="defualt" :loading="submitLoading">
  82. 确 定
  83. </el-button>
  84. </template>
  85. </el-dialog>
  86. </div>
  87. </template>
  88. <script setup>
  89. import byTable from "@/components/byTable/index";
  90. import byForm from "@/components/byForm/index";
  91. import treeList from "@/components/product/treeList";
  92. const { proxy } = getCurrentInstance();
  93. const currencyData = computed(
  94. () => proxy.useUserStore().allDict["account_currency"]
  95. );
  96. const materialUnitData = computed(
  97. () => proxy.useUserStore().allDict["material_unit"]
  98. );
  99. const colorLayerData = computed(
  100. () => proxy.useUserStore().allDict["color_layer"]
  101. );
  102. const backLinesData = computed(
  103. () => proxy.useUserStore().allDict["back_lines"]
  104. );
  105. const frontLinesData = computed(
  106. () => proxy.useUserStore().allDict["front_lines"]
  107. );
  108. const loading = ref(false);
  109. const submitLoading = ref(false);
  110. const sourceList = ref({
  111. data: [],
  112. pagination: {
  113. total: 3,
  114. pageNum: 1,
  115. pageSize: 10,
  116. type: "",
  117. productClassifyId: "",
  118. keyword: "",
  119. definition: "2",
  120. },
  121. });
  122. const dialogVisible = ref(false);
  123. const modalType = ref("add");
  124. const treeData = ref([]);
  125. const rules = ref({
  126. productClassifyId: [
  127. { required: true, message: "请选择物料分类", trigger: "change" },
  128. ],
  129. name: [{ required: true, message: "请输入物料名称", trigger: "blur" }],
  130. customCode: [{ required: true, message: "请输入物料编码", trigger: "blur" }],
  131. });
  132. const props = defineProps({
  133. selectStatus: Boolean,
  134. });
  135. const selectConfig = computed(() => []);
  136. const config = computed(() => {
  137. return [
  138. {
  139. attrs: {
  140. label: "图片",
  141. slot: "pic",
  142. align: "center",
  143. width: 80,
  144. },
  145. },
  146. {
  147. attrs: {
  148. label: "物料分类",
  149. prop: "classifyName",
  150. "min-width": 150,
  151. },
  152. },
  153. {
  154. attrs: {
  155. label: "物料编码",
  156. prop: "customCode",
  157. width: 120,
  158. },
  159. },
  160. {
  161. attrs: {
  162. label: " 物料名称",
  163. slot: "name",
  164. "min-width": 150,
  165. },
  166. },
  167. {
  168. attrs: {
  169. label: "物料材质",
  170. prop: "material",
  171. width: 120,
  172. },
  173. },
  174. {
  175. attrs: {
  176. label: "物料型号",
  177. prop: "spec",
  178. width: 120,
  179. },
  180. },
  181. {
  182. attrs: {
  183. label: "单位",
  184. prop: "unit",
  185. width: 80,
  186. },
  187. render(val) {
  188. return proxy.dictKeyValue(val, materialUnitData.value);
  189. },
  190. },
  191. {
  192. attrs: {
  193. label: "尺寸",
  194. slot: "size",
  195. width: 150,
  196. },
  197. },
  198. {
  199. attrs: {
  200. label: "净重",
  201. prop: "netWeight",
  202. width: 100,
  203. },
  204. render(val) {
  205. if (val) {
  206. return val + " kg";
  207. }
  208. },
  209. },
  210. {
  211. attrs: {
  212. label: "成本价",
  213. slot: "costPrice",
  214. width: 100,
  215. },
  216. },
  217. {
  218. attrs: {
  219. label: "销售价",
  220. slot: "price",
  221. width: 100,
  222. },
  223. },
  224. {
  225. attrs: {
  226. label: "操作",
  227. width: "160",
  228. align: "center",
  229. fixed: "right",
  230. },
  231. renderHTML(row) {
  232. return [
  233. // props.selectStatus
  234. // ? {}
  235. // : {
  236. // attrs: {
  237. // label: "复制",
  238. // type: "primary",
  239. // text: true,
  240. // },
  241. // el: "button",
  242. // click() {
  243. // getDtlOne(row);
  244. // },
  245. // },
  246. props.selectStatus
  247. ? {
  248. attrs: {
  249. label: "选择",
  250. type: "primary",
  251. text: true,
  252. },
  253. el: "button",
  254. click() {
  255. clickSelect(row);
  256. },
  257. }
  258. : {
  259. attrs: {
  260. label: "修改",
  261. type: "primary",
  262. text: true,
  263. },
  264. el: "button",
  265. click() {
  266. getDtl(row);
  267. },
  268. },
  269. props.selectStatus
  270. ? {}
  271. : {
  272. attrs: {
  273. label: "删除",
  274. type: "danger",
  275. text: true,
  276. },
  277. el: "button",
  278. click() {
  279. proxy
  280. .msgConfirm()
  281. .then((res) => {
  282. proxy
  283. .post("/productInfo/delete", {
  284. id: row.id,
  285. })
  286. .then((res) => {
  287. proxy.msgTip("删除成功", 1);
  288. getList();
  289. });
  290. })
  291. .catch((err) => {});
  292. },
  293. },
  294. ];
  295. },
  296. },
  297. ];
  298. });
  299. const formData = reactive({
  300. data: {},
  301. });
  302. const formOption = reactive({
  303. inline: true,
  304. labelWidth: 100,
  305. itemWidth: 100,
  306. });
  307. const formDom = ref(null);
  308. const treeListData = ref([]);
  309. const formConfig = computed(() => {
  310. return [
  311. {
  312. type: "title1",
  313. title: "基本信息",
  314. },
  315. {
  316. type: "treeSelect",
  317. prop: "productClassifyId",
  318. label: "物料分类",
  319. data: treeData.value,
  320. itemWidth: 100,
  321. disabled: false,
  322. },
  323. {
  324. type: "input",
  325. prop: "name",
  326. label: "物料名称",
  327. itemWidth: 50,
  328. disabled: false,
  329. },
  330. {
  331. type: "input",
  332. prop: "customCode",
  333. label: "物料编码",
  334. itemWidth: 50,
  335. disabled: false,
  336. },
  337. {
  338. type: "uploadImg",
  339. // limit: 1,
  340. // listType: "picture-card",
  341. // accept: ".gif, .jpeg, .jpg, .png",
  342. imgProp: "imageUrl",
  343. prop: "fileList",
  344. label: "物料缩略图",
  345. },
  346. {
  347. type: "title1",
  348. title: "材质特征",
  349. },
  350. {
  351. type: "input",
  352. prop: "material",
  353. label: "材质",
  354. itemWidth: 50,
  355. },
  356. {
  357. type: "input",
  358. prop: "spec",
  359. label: "型号",
  360. itemWidth: 50,
  361. },
  362. {
  363. type: "select",
  364. prop: "frontalTexture",
  365. label: "正面纹路",
  366. data: frontLinesData.value,
  367. itemWidth: 50,
  368. },
  369. {
  370. type: "select",
  371. prop: "reverseTexture",
  372. label: "背面纹路",
  373. data: backLinesData.value,
  374. itemWidth: 50,
  375. },
  376. {
  377. type: "select",
  378. prop: "unit",
  379. label: "单位",
  380. data: materialUnitData.value,
  381. itemWidth: 50,
  382. },
  383. {
  384. type: "select",
  385. prop: "colorLayer",
  386. label: "色层",
  387. data: colorLayerData.value,
  388. itemWidth: 50,
  389. },
  390. // {
  391. // type: "input",
  392. // prop: "remark",
  393. // label: "备注",
  394. // itemType: "textarea",
  395. // },
  396. {
  397. type: "title1",
  398. title: "规格",
  399. },
  400. {
  401. type: "input",
  402. prop: "color",
  403. label: "颜色",
  404. itemWidth: 50,
  405. },
  406. {
  407. type: "number",
  408. prop: "stockThreshold",
  409. label: "安全库存",
  410. precision: 2,
  411. min: 0,
  412. controls: false,
  413. itemWidth: 50,
  414. },
  415. {
  416. type: "selectInput",
  417. prop: "costPrice",
  418. selectProp: "costCurrency",
  419. label: "成本价",
  420. itemWidth: 50,
  421. disabledSelect: true,
  422. data: currencyData.value,
  423. },
  424. {
  425. type: "selectInput",
  426. prop: "price",
  427. selectProp: "currency",
  428. label: "销售价",
  429. itemWidth: 50,
  430. disabledSelect: true,
  431. data: currencyData.value,
  432. },
  433. {
  434. type: "slot",
  435. slotName: "size",
  436. prop: "size",
  437. label: "尺寸",
  438. itemWidth: 50,
  439. disabled: false,
  440. },
  441. {
  442. type: "number",
  443. prop: "netWeight",
  444. label: "净重(kg)",
  445. precision: 2,
  446. min: 0,
  447. controls: false,
  448. itemWidth: 50,
  449. },
  450. ];
  451. });
  452. const getList = async (req) => {
  453. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  454. loading.value = true;
  455. proxy.post("/productInfo/page", sourceList.value.pagination).then((res) => {
  456. sourceList.value.data = res.rows.map((x) => ({ ...x, fileList: [] }));
  457. sourceList.value.pagination.total = res.total;
  458. setTimeout(() => {
  459. loading.value = false;
  460. }, 200);
  461. const productIdList = res.rows.map((x) => x.id);
  462. // 请求文件数据并回显
  463. if (productIdList.length > 0) {
  464. proxy
  465. .post("/fileInfo/getList", { businessIdList: productIdList })
  466. .then((fileObj) => {
  467. for (let i = 0; i < sourceList.value.data.length; i++) {
  468. const e = sourceList.value.data[i];
  469. for (const key in fileObj) {
  470. if (e.id === key) {
  471. e.fileList = fileObj[key];
  472. }
  473. }
  474. }
  475. });
  476. }
  477. });
  478. };
  479. const treeChange = (e) => {
  480. if (e.id != undefined) {
  481. sourceList.value.pagination.productClassifyId = e.id;
  482. getList({ productClassifyId: e.id });
  483. }
  484. };
  485. const openModal = () => {
  486. dialogVisible.value = true;
  487. modalType.value = "add";
  488. formData.data = {
  489. definition: "2",
  490. fileList: [],
  491. };
  492. if (currencyData.value && currencyData.value.length > 0) {
  493. formData.data.currency = currencyData.value[0].dictKey;
  494. formData.data.costCurrency = currencyData.value[0].dictKey;
  495. }
  496. };
  497. const submitForm = () => {
  498. formDom.value.handleSubmit((valid) => {
  499. if (!formData.data.fileList.length > 0) {
  500. return proxy.msgTip("请上传图片", 2);
  501. }
  502. submitLoading.value = true;
  503. proxy.post("/productInfo/" + modalType.value, formData.data).then(
  504. (res) => {
  505. proxy.msgTip("操作成功", 1);
  506. dialogVisible.value = false;
  507. submitLoading.value = false;
  508. getList();
  509. },
  510. (err) => {
  511. submitLoading.value = false;
  512. }
  513. );
  514. });
  515. };
  516. const getTreeList = () => {
  517. proxy
  518. .post("/productClassify/tree", { parentId: "", name: "", definition: "2" })
  519. .then((message) => {
  520. treeListData.value = [
  521. {
  522. id: "",
  523. label: "全部",
  524. parentId: "",
  525. children: message,
  526. },
  527. ];
  528. treeData.value = message;
  529. });
  530. };
  531. const getDtl = (row) => {
  532. modalType.value = "edit";
  533. proxy.post("/productInfo/detail", { id: row.id }).then((res) => {
  534. formData.data = res;
  535. formData.data.fileList = row.fileList.map((x) => ({
  536. ...x,
  537. url: x.fileUrl,
  538. name: x.fileName,
  539. }));
  540. if (formData.data.fileList.length > 0) {
  541. formData.data.imageUrl = formData.data.fileList[0].fileUrl;
  542. }
  543. dialogVisible.value = true;
  544. });
  545. };
  546. // const getDtlOne = (row) => {
  547. // modalType.value = "add";
  548. // proxy.post("/productInfo/detail", { id: row.id }).then((res) => {
  549. // fileList.value = [];
  550. // fileListCopy.value = [];
  551. // res.type = res.type + "";
  552. // res.definition = "2";
  553. // delete res.id;
  554. // formData.data = res;
  555. // dialogVisible.value = true;
  556. // });
  557. // };
  558. const handleClickFile = (file) => {
  559. window.open(file.fileUrl, "_blank");
  560. };
  561. getTreeList();
  562. getList();
  563. const clickSelect = (item) => {
  564. proxy.$emit("selectMaterial", item);
  565. };
  566. </script>
  567. <style lang="scss" scoped>
  568. .user {
  569. padding: 20px;
  570. display: flex;
  571. justify-content: space-between;
  572. .tree {
  573. width: 300px;
  574. }
  575. .content {
  576. width: calc(100% - 320px);
  577. }
  578. }
  579. .pic {
  580. object-fit: contain;
  581. width: 50px;
  582. height: 50px;
  583. cursor: pointer;
  584. vertical-align: middle;
  585. }
  586. </style>