index.vue 15 KB

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