index.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. import { ref, reactive, nextTick, toRefs } from "vue";
  2. import { Table } from "@/hooks/interface";
  3. import { ColumnProps } from "@/components/LjVxeTable/interface";
  4. import { ALLOW_EDIT_STATE } from "@/config/index";
  5. import {
  6. SaveConfigureType,
  7. SaveConfigureCode,
  8. SaveConfigureCodeMx,
  9. SaveConfigureBomList,
  10. DeleteConfigureType,
  11. DeleteConfigureCode,
  12. DeleteConfigureCodeMx,
  13. CopyConfigureCodeMxList,
  14. DeleteConfigureBomList
  15. } from "@/api/modules/basicinfo";
  16. import { ElMessage, ElMessageBox } from "element-plus";
  17. import { useClipboardStore } from "@/stores/modules/copy";
  18. import ErpMtrlPriceSelect from "@/views/system/selector/erpMtrlPrice/select.vue";
  19. interface defaultState {
  20. /**
  21. * @description 单据当前状态
  22. */
  23. orderStatus: string;
  24. /**
  25. * @description 列表Ref
  26. */
  27. VxeTableLeftRef: any;
  28. /**
  29. * @description 列表Ref
  30. */
  31. VxeTableMidRef: any;
  32. /**
  33. * @description 列表Ref
  34. */
  35. VxeTableRightRef: any;
  36. /**
  37. * @description 列表Ref
  38. */
  39. VxeTableBottomRef: any;
  40. /**
  41. * @description 详情页Ref
  42. */
  43. LjDetailLeftRef: any;
  44. /**
  45. * @description 详情页Ref
  46. */
  47. LjDetailMidRef: any;
  48. /**
  49. * @description 详情页Ref
  50. */
  51. LjDetailRightRef: any;
  52. /**
  53. * @description 详情页Ref
  54. */
  55. LjDetailBottomRef: any;
  56. /**
  57. * @description 详情页明细表格Ref
  58. */
  59. VxeTableMxRef: any;
  60. MtrldefDialogRef: any;
  61. MtrldefDialogProps: any;
  62. }
  63. const state = reactive<defaultState>({
  64. orderStatus: "",
  65. VxeTableLeftRef: null,
  66. VxeTableMidRef: null,
  67. VxeTableRightRef: null,
  68. VxeTableBottomRef: null,
  69. LjDetailLeftRef: null,
  70. LjDetailMidRef: null,
  71. LjDetailRightRef: null,
  72. LjDetailBottomRef: null,
  73. VxeTableMxRef: null,
  74. MtrldefDialogRef: null,
  75. MtrldefDialogProps: {}
  76. });
  77. const ClipboardStore = useClipboardStore();
  78. const COPY_KEY = "configure_codemx";
  79. /**
  80. * @description 表格多选数据操作
  81. * @param {String} rowKey 当表格可以多选时,所指定的 id
  82. * */
  83. export const useHooks = (t?: any) => {
  84. // 表格配置项
  85. const columns_left: ColumnProps<any>[] = [
  86. { type: "checkbox", width: 40, fixed: "left" },
  87. {
  88. field: "contfigtypename",
  89. title: "类型名称",
  90. basicinfo: {
  91. el: "input",
  92. span: 4,
  93. editable: ALLOW_EDIT_STATE
  94. }
  95. },
  96. {
  97. field: "flag",
  98. title: "审核",
  99. basicinfo: {
  100. visible: false
  101. }
  102. },
  103. {
  104. field: "auditemp",
  105. title: "审核人",
  106. basicinfo: {
  107. visible: false
  108. }
  109. },
  110. {
  111. field: "auditdate",
  112. title: "审核时间",
  113. basicinfo: {
  114. visible: false
  115. }
  116. }
  117. ];
  118. const columns_mid: ColumnProps<any>[] = [
  119. { type: "checkbox", width: 40, fixed: "left" },
  120. {
  121. field: "pzcode",
  122. title: "配置项编号",
  123. width: 100,
  124. basicinfo: {
  125. el: "input",
  126. span: 4,
  127. editable: ALLOW_EDIT_STATE
  128. }
  129. },
  130. {
  131. field: "name",
  132. title: "配置项名称",
  133. basicinfo: {
  134. el: "input",
  135. span: 4,
  136. editable: ALLOW_EDIT_STATE
  137. }
  138. },
  139. {
  140. field: "inputtype",
  141. title: "录入类型",
  142. width: 100,
  143. basicinfo: {
  144. el: "select",
  145. span: 3,
  146. editable: ALLOW_EDIT_STATE
  147. }
  148. },
  149. {
  150. field: "ifnum",
  151. title: "数值",
  152. basicinfo: {
  153. editable: ALLOW_EDIT_STATE,
  154. span: 1,
  155. render: (scope: any) => {
  156. const { column, searchParam } = scope;
  157. const _disabled = Number(searchParam.inputtype) === 0;
  158. return (
  159. <>
  160. <el-checkbox v-model={searchParam[column.field]} true-value={1} false-value={0} disabled={_disabled} />
  161. </>
  162. );
  163. }
  164. }
  165. },
  166. {
  167. field: "ifcross",
  168. title: "混搭",
  169. basicinfo: {
  170. editable: ALLOW_EDIT_STATE,
  171. span: 1,
  172. render: (scope: any) => {
  173. const { column, searchParam } = scope;
  174. const _disabled = Number(searchParam.inputtype) === 0;
  175. return (
  176. <>
  177. <el-checkbox v-model={searchParam[column.field]} true-value={1} false-value={0} disabled={_disabled} />
  178. </>
  179. );
  180. }
  181. }
  182. },
  183. {
  184. field: "ifcheck",
  185. title: "必填",
  186. basicinfo: {
  187. editable: ALLOW_EDIT_STATE,
  188. span: 1,
  189. render: (scope: any) => {
  190. const { column, searchParam } = scope;
  191. const _disabled = Number(searchParam.inputtype) === 0;
  192. return (
  193. <>
  194. <el-checkbox v-model={searchParam[column.field]} true-value={1} false-value={0} disabled={_disabled} />
  195. </>
  196. );
  197. }
  198. }
  199. },
  200. {
  201. field: "ifuse",
  202. title: "有效",
  203. basicinfo: {
  204. editable: ALLOW_EDIT_STATE,
  205. span: 1,
  206. render: (scope: any) => {
  207. const { column, searchParam } = scope;
  208. return (
  209. <>
  210. <div class="flx-align-center">
  211. <el-checkbox v-model={searchParam[column.field]} true-value={1} false-value={0} />
  212. </div>
  213. </>
  214. );
  215. }
  216. }
  217. },
  218. {
  219. field: "maxnum",
  220. title: "最大数",
  221. basicinfo: {
  222. el: "input",
  223. span: 4,
  224. editable: (scope: any) => {
  225. return Number(scope.searchParam.inputtype) > 0;
  226. }
  227. }
  228. },
  229. {
  230. field: "minnum",
  231. title: "最小数",
  232. basicinfo: {
  233. el: "input",
  234. span: 4,
  235. editable: (scope: any) => {
  236. return Number(scope.searchParam.inputtype) > 0;
  237. }
  238. }
  239. },
  240. {
  241. field: "pricestr",
  242. title: "差价公式",
  243. basicinfo: {
  244. el: "input",
  245. span: 4,
  246. editable: (scope: any) => {
  247. return Number(scope.searchParam.inputtype) > 0;
  248. }
  249. }
  250. },
  251. {
  252. field: "priceratestr",
  253. title: "差价比例公式",
  254. basicinfo: {
  255. el: "input",
  256. span: 4,
  257. editable: (scope: any) => {
  258. return Number(scope.searchParam.inputtype) > 0;
  259. }
  260. }
  261. }
  262. ];
  263. const columns_right: ColumnProps<any>[] = [
  264. { type: "checkbox", width: 40, fixed: "left" },
  265. {
  266. field: "pzcodemx",
  267. title: "明细编号",
  268. width: 100,
  269. basicinfo: {
  270. el: "input",
  271. span: 4,
  272. editable: ALLOW_EDIT_STATE
  273. }
  274. },
  275. {
  276. field: "namemx",
  277. title: "明细名称",
  278. basicinfo: {
  279. el: "input",
  280. span: 4,
  281. editable: ALLOW_EDIT_STATE
  282. }
  283. },
  284. {
  285. field: "ifuse",
  286. title: "有效",
  287. basicinfo: {
  288. editable: ALLOW_EDIT_STATE,
  289. span: 1,
  290. render: (scope: any) => {
  291. const { column, searchParam } = scope;
  292. return (
  293. <>
  294. <div class="flx-align-center">
  295. <el-checkbox v-model={searchParam[column.field]} true-value={1} false-value={0} />
  296. </div>
  297. </>
  298. );
  299. }
  300. }
  301. }
  302. ];
  303. const columns_bottom: ColumnProps<any>[] = [
  304. {
  305. field: "mtrlname",
  306. title: "物料名称",
  307. basicinfo: {
  308. el: "select",
  309. span: 3,
  310. row: 1,
  311. order: 1,
  312. editable: ALLOW_EDIT_STATE,
  313. render: (scope: any) => {
  314. const { column, searchParam: row, status } = scope;
  315. let params = {};
  316. return (
  317. <>
  318. <ErpMtrlPriceSelect
  319. value={row.mtrlid}
  320. {...params}
  321. clearable
  322. placeholder={row.mtrlname}
  323. onOpenModal={() => fModelChoseMtrl(row, params)}
  324. onSelect={(val: any) => rModelSetMtrl(row, val)}
  325. onClear={() => rModelClearMtrl(scope.searchParam)}
  326. >
  327. {{
  328. label: () => row.mtrlname
  329. }}
  330. </ErpMtrlPriceSelect>
  331. </>
  332. );
  333. }
  334. }
  335. },
  336. {
  337. field: "unit",
  338. title: "单位",
  339. basicinfo: {
  340. span: 1,
  341. row: 1,
  342. order: 2
  343. }
  344. },
  345. {
  346. field: "default_length",
  347. title: "默认长",
  348. basicinfo: {
  349. el: "input",
  350. span: 2,
  351. row: 1,
  352. order: 5,
  353. editable: ALLOW_EDIT_STATE
  354. }
  355. },
  356. {
  357. field: "default_width",
  358. title: "默认宽",
  359. basicinfo: {
  360. el: "input",
  361. span: 2,
  362. row: 1,
  363. order: 6,
  364. editable: ALLOW_EDIT_STATE
  365. }
  366. },
  367. {
  368. field: "default_qty",
  369. title: "默认数量",
  370. basicinfo: {
  371. el: "input",
  372. span: 2,
  373. row: 1,
  374. order: 7,
  375. editable: ALLOW_EDIT_STATE
  376. }
  377. },
  378. {
  379. field: "sonscale",
  380. title: "用料量",
  381. basicinfo: {
  382. el: "input",
  383. span: 2,
  384. row: 1,
  385. order: 8,
  386. editable: ALLOW_EDIT_STATE
  387. }
  388. },
  389. {
  390. field: "mng_cost_rate",
  391. title: "管理费用率",
  392. basicinfo: {
  393. el: "input",
  394. span: 2,
  395. row: 1,
  396. order: 9,
  397. editable: ALLOW_EDIT_STATE
  398. }
  399. },
  400. {
  401. field: "profit_rate",
  402. title: "利润率",
  403. basicinfo: {
  404. el: "input",
  405. span: 2,
  406. row: 1,
  407. order: 10,
  408. editable: ALLOW_EDIT_STATE
  409. }
  410. },
  411. {
  412. field: "sonloss",
  413. title: "损耗率",
  414. basicinfo: {
  415. el: "input",
  416. span: 2,
  417. row: 1,
  418. order: 11,
  419. editable: ALLOW_EDIT_STATE
  420. }
  421. },
  422. {
  423. field: "sondecloss",
  424. title: "损附加",
  425. basicinfo: {
  426. el: "input",
  427. span: 2,
  428. row: 1,
  429. order: 12,
  430. editable: ALLOW_EDIT_STATE
  431. }
  432. },
  433. {
  434. field: "sonscale_formula",
  435. title: "用料量公式",
  436. basicinfo: {
  437. el: "input",
  438. span: 4,
  439. row: 1,
  440. order: 13,
  441. editable: ALLOW_EDIT_STATE,
  442. props: { type: "textarea", rows: 3 }
  443. }
  444. },
  445. {
  446. field: "sonloss_formula",
  447. title: "损耗率公式",
  448. basicinfo: {
  449. el: "input",
  450. span: 4,
  451. row: 1,
  452. order: 14,
  453. editable: ALLOW_EDIT_STATE,
  454. props: { type: "textarea", rows: 3 }
  455. }
  456. },
  457. {
  458. field: "sondecloss_formula",
  459. title: "损附加公式",
  460. basicinfo: {
  461. el: "input",
  462. span: 4,
  463. row: 1,
  464. order: 15,
  465. editable: ALLOW_EDIT_STATE,
  466. props: { type: "textarea", rows: 3 }
  467. }
  468. },
  469. {
  470. field: "cost",
  471. title: "成本价",
  472. basicinfo: {
  473. span: 1,
  474. row: 1,
  475. order: 16,
  476. visible: false
  477. }
  478. },
  479. {
  480. field: "cost_emp",
  481. title: "成本价统计人",
  482. basicinfo: {
  483. span: 1,
  484. row: 1,
  485. order: 17,
  486. visible: false
  487. }
  488. },
  489. {
  490. field: "cost_date",
  491. title: "成本价统计时间",
  492. basicinfo: {
  493. span: 1,
  494. row: 1,
  495. order: 18,
  496. visible: false
  497. }
  498. },
  499. {
  500. field: "realqty",
  501. title: "物料清单总用量",
  502. basicinfo: {
  503. span: 1,
  504. row: 1,
  505. order: 19,
  506. visible: false
  507. }
  508. },
  509. {
  510. field: "deptid_scll",
  511. title: "默认领用部门",
  512. basicinfo: {
  513. span: 1,
  514. row: 1,
  515. order: 20,
  516. visible: false
  517. }
  518. }
  519. ];
  520. const getSaveFun = (type: Number) => {
  521. switch (type) {
  522. case 0:
  523. return SaveConfigureType;
  524. case 1:
  525. return SaveConfigureCode;
  526. case 2:
  527. return SaveConfigureCodeMx;
  528. case 3:
  529. return SaveConfigureBomList;
  530. }
  531. };
  532. const getDelFun = (type: Number) => {
  533. switch (type) {
  534. case 0:
  535. return DeleteConfigureType;
  536. case 1:
  537. return DeleteConfigureCode;
  538. case 2:
  539. return DeleteConfigureCodeMx;
  540. case 3:
  541. return DeleteConfigureBomList;
  542. }
  543. };
  544. const getTableRef = (type: Number) => {
  545. switch (type) {
  546. case 0:
  547. return state.VxeTableLeftRef;
  548. case 1:
  549. return state.VxeTableMidRef;
  550. case 2:
  551. return state.VxeTableRightRef;
  552. case 3:
  553. return state.VxeTableBottomRef;
  554. }
  555. };
  556. // 保存
  557. const fSave = (type, param: any) => {
  558. return new Promise((resolve, reject) => {
  559. ElMessageBox.confirm("是否确定要保存吗?", "询问", {
  560. confirmButtonText: "是",
  561. cancelButtonText: "否",
  562. type: "warning"
  563. })
  564. .then(() => {
  565. getSaveFun(type)(param).then(() => {
  566. ElMessage.success("保存成功!");
  567. getTableRef(type)?.refresh();
  568. resolve({});
  569. });
  570. })
  571. .catch(() => {
  572. ElMessage({
  573. type: "info",
  574. message: "操作取消"
  575. });
  576. });
  577. });
  578. };
  579. // 删除
  580. const fDelete = (type: Number) => {
  581. const checkDate = getTableRef(type)?.element.getCheckboxRecords();
  582. if (checkDate.length === 0) {
  583. ElMessage.error("请选择要删除的数据!");
  584. return;
  585. }
  586. const delArr = checkDate.map((item: any) => {
  587. if (type === 0) return { contfigtypeid: parseInt(item.contfigtypeid) };
  588. if (type === 1) return { typeid: parseInt(item.typeid), pzid: parseInt(item.pzid) };
  589. return { pzid: parseInt(item.pzid), printid: parseInt(item.printid) };
  590. });
  591. ElMessageBox.confirm("是否确定要删除吗?", "询问", {
  592. confirmButtonText: "是",
  593. cancelButtonText: "否",
  594. type: "warning"
  595. })
  596. .then(() => {
  597. getDelFun(type)({ list: delArr }).then(() => {
  598. ElMessage.success("删除成功!");
  599. getTableRef(type)?.refresh();
  600. });
  601. })
  602. .catch(() => {
  603. ElMessage({
  604. type: "info",
  605. message: "操作取消"
  606. });
  607. });
  608. };
  609. const fCopy = () => {
  610. const checkDate = getTableRef(2)?.element.getCheckboxRecords();
  611. if (checkDate.length === 0) {
  612. ElMessage.error("请勾选要复制的数据!");
  613. return;
  614. }
  615. ClipboardStore.copy(
  616. checkDate.map((item: any) => ({
  617. pzcodemx: item.pzcodemx,
  618. namemx: item.namemx
  619. })),
  620. { key: COPY_KEY }
  621. );
  622. ElMessage.success("复制成功!");
  623. };
  624. const fPaste = async (type: number) => {
  625. const checkDate = getTableRef(1)?.element.getCurrentRecord();
  626. const copyData = ClipboardStore.getLatest<Array<{ pzcodemx: string; namemx: string }>>(COPY_KEY);
  627. if (!copyData?.length) {
  628. ElMessage.error("没有可粘贴的内容,请先执行复制功能");
  629. return;
  630. }
  631. try {
  632. if (type === 1) {
  633. await handleOverwritePaste(checkDate, copyData, type);
  634. } else {
  635. await handleAppendPaste(checkDate, copyData, type);
  636. }
  637. ElMessage.success("粘贴成功!");
  638. getTableRef(2)?.refresh();
  639. } catch (error) {
  640. if (error !== "cancel") {
  641. console.error("粘贴操作出错:", error);
  642. }
  643. }
  644. };
  645. const handleOverwritePaste = async (subject: any, mxList: any[], type: number) => {
  646. try {
  647. await ElMessageBox.confirm("粘贴功能将清空原有的数据,是否继续?", "询问", {
  648. confirmButtonText: "是",
  649. cancelButtonText: "否",
  650. type: "warning"
  651. });
  652. await CopyConfigureCodeMxList({ subject, mxList, type });
  653. } catch {
  654. ElMessage({ type: "info", message: "操作取消" });
  655. throw "cancel"; // 特殊标记取消操作
  656. }
  657. };
  658. const handleAppendPaste = async (subject: any, mxList: any[], type: number) => {
  659. const tableData = getTableRef(2)?.element.getTableData().fullData;
  660. const duplicateSet = new Set();
  661. const duplicates = tableData.filter(a => {
  662. const key = `${a.pzcodemx}|${a.namemx}`;
  663. const isDuplicate = mxList.some(b => b.pzcodemx === a.pzcodemx && b.namemx === a.namemx);
  664. if (isDuplicate && !duplicateSet.has(key)) {
  665. duplicateSet.add(key);
  666. return true;
  667. }
  668. return false;
  669. });
  670. if (duplicates.length > 0) {
  671. try {
  672. const duplicateList = duplicates.map(d => `${d.pzcodemx} ${d.namemx}`).join("、");
  673. await ElMessageBox.confirm(`当前部件选配项值已有(${duplicateList}),补充黏贴后会出现重复数据,是否继续?`, "询问", {
  674. confirmButtonText: "是",
  675. cancelButtonText: "否",
  676. type: "warning"
  677. });
  678. await CopyConfigureCodeMxList({ subject, mxList, type });
  679. } catch {
  680. ElMessage({ type: "info", message: "操作取消" });
  681. throw "cancel"; // 特殊标记取消操作
  682. }
  683. } else {
  684. await CopyConfigureCodeMxList({ subject, mxList, type });
  685. }
  686. };
  687. /**
  688. * @description 弹窗模块:客户选择
  689. * @param data 当前数据
  690. * @param status 当前订单状态
  691. * @returns Promise
  692. */
  693. const fModelChoseMtrl = (data: any, params: any) => {
  694. return new Promise((resolve, reject) => {
  695. // if (!ALLOW_EDIT_STATE.includes(status)) return;
  696. let _params = {
  697. keyword: ""
  698. };
  699. state.MtrldefDialogProps = {
  700. onSubmit: (res: any) => {
  701. // submit
  702. console.log("openCustDialog res", res);
  703. nextTick(() => {
  704. rModelSetMtrl(data, res.value[0]);
  705. resolve(1);
  706. });
  707. },
  708. onCancel: (error: any) => {
  709. // cancel 回调
  710. console.log("openCustDialog error", error);
  711. }
  712. };
  713. state.MtrldefDialogRef.show(_params);
  714. });
  715. };
  716. /**
  717. * @description 数据赋值: 联系人
  718. * @param data 当前数据
  719. * @param item 当前选择的数据
  720. */
  721. const rModelSetMtrl = (data: any, item: any) => {
  722. data.mtrlid = Number(item.mtrlid);
  723. data.mtrlname = item.mtrlname;
  724. data.mtrlcode = item.mtrlcode;
  725. data.mtrlmode = item.mtrlmode;
  726. data.unit = item.unit;
  727. data.price = item.price;
  728. };
  729. const rModelClearMtrl = (data: any) => {
  730. data.mtrlid = 0;
  731. data.mtrlname = "";
  732. data.mtrlcode = "";
  733. data.mtrlmode = "";
  734. data.unit = "";
  735. data.price = 0;
  736. };
  737. return {
  738. ...toRefs(state),
  739. columns_left,
  740. columns_mid,
  741. columns_right,
  742. columns_bottom,
  743. fSave,
  744. fDelete,
  745. fCopy,
  746. fPaste
  747. };
  748. };