index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. <template>
  2. <div class="tenant">
  3. <byTable
  4. :source="sourceList.data"
  5. :pagination="sourceList.pagination"
  6. :config="config"
  7. :loading="loading"
  8. :selectConfig="selectConfig"
  9. highlight-current-row
  10. :action-list="[
  11. {
  12. text: '新建样品单',
  13. action: () => newSample(),
  14. },
  15. ]"
  16. @get-list="getList"
  17. >
  18. <template #code="{ item }">
  19. <div style="width: 100%">
  20. <a
  21. style="color: #409eff; cursor: pointer; word-break: break-all"
  22. @click="openDetails(item)"
  23. >{{ item.code }}</a
  24. >
  25. </div>
  26. </template>
  27. <template #amount="{ item }">
  28. <div style="width: 100%">
  29. <span style="padding-right: 4px">{{ item.currency }}</span>
  30. <span>{{ moneyFormat(item.amount, 2) }}</span>
  31. </div>
  32. </template>
  33. <template #buyCorporationName="{ item }">
  34. <div style="width: 100%">
  35. <a
  36. style="color: #409eff; cursor: pointer"
  37. @click="clickCorporationName(item)"
  38. >{{ item.buyCorporationName }}</a
  39. >
  40. </div>
  41. </template>
  42. <template #tags="{ item }">
  43. <div style="width: 100%">
  44. <el-tag
  45. style="margin-right: 8px"
  46. type="success"
  47. v-for="(tag, index) in item.tags"
  48. closable
  49. :key="index"
  50. @close="tagClose(tag, item)"
  51. >
  52. {{ dictValueLabel(tag, customerTag) }}
  53. </el-tag>
  54. <template v-if="item.tags.length !== customerTag.length">
  55. <el-select
  56. v-if="item.addTagShow"
  57. v-model="addTag"
  58. style="width: 100%"
  59. @change="
  60. (val) => {
  61. return changeTag(val, item);
  62. }
  63. "
  64. >
  65. <el-option
  66. v-for="tag in customerTag"
  67. :key="tag.value"
  68. :label="tag.label"
  69. :value="tag.value"
  70. :disabled="judgeTagSelect(item.tags, tag.value)"
  71. />
  72. </el-select>
  73. <el-tag
  74. style="cursor: pointer"
  75. type="success"
  76. @click="showSelect(item)"
  77. v-else
  78. >
  79. +
  80. </el-tag>
  81. </template>
  82. </div>
  83. </template>
  84. <template #advanceRatio="{ item }">
  85. <div style="width: 100%">
  86. <span>{{ item.advanceRatio }}%</span>
  87. </div>
  88. </template>
  89. <template #scale="{ item }">
  90. <div>
  91. {{ computeScale(item) }}
  92. </div>
  93. </template>
  94. </byTable>
  95. <el-dialog
  96. title="交接单"
  97. v-if="openHandoverSlip"
  98. v-model="openHandoverSlip"
  99. width="600"
  100. >
  101. <byForm
  102. :formConfig="formConfig"
  103. :formOption="formOption"
  104. v-model="handoverSlipForm"
  105. >
  106. <template #file>
  107. <div style="width: 100%">
  108. <el-upload
  109. v-model:fileList="handoverSlipForm.fileList"
  110. action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
  111. :data="uploadData"
  112. multiple
  113. :before-upload="uploadFile"
  114. :on-success="handleSuccess"
  115. :on-preview="onPreviewFile"
  116. >
  117. <el-button type="primary" plain>选择</el-button>
  118. </el-upload>
  119. </div>
  120. </template>
  121. <template #indication>
  122. <div style="width: 100%">
  123. <el-upload
  124. v-model:fileList="handoverSlipForm.packageFileList"
  125. action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
  126. :data="indicationUploadData"
  127. multiple
  128. :before-upload="indicationUploadFile"
  129. :on-success="handleSuccess"
  130. :on-preview="onPreviewFile"
  131. >
  132. <el-button type="primary" plain>选择</el-button>
  133. </el-upload>
  134. </div>
  135. </template>
  136. </byForm>
  137. <template #footer>
  138. <el-button @click="openHandoverSlip = false" size="large"
  139. >关 闭</el-button
  140. >
  141. <el-button type="primary" @click="submitHandoverSlip()" size="large"
  142. >确 定</el-button
  143. >
  144. </template>
  145. </el-dialog>
  146. <el-dialog
  147. title="合同详情"
  148. v-if="openDetailsDialog"
  149. v-model="openDetailsDialog"
  150. width="1100"
  151. >
  152. <ContractDetailsOne :contractId="currentContractId"></ContractDetailsOne>
  153. </el-dialog>
  154. <el-dialog title="打印" v-if="openPrint" v-model="openPrint" width="920">
  155. <SamplePDF :rowData="rowData"></SamplePDF>
  156. <template #footer>
  157. <el-button @click="openPrint = false" size="large">取消</el-button>
  158. <el-button type="primary" @click="clickDownload()" size="large"
  159. >下载PDF</el-button
  160. >
  161. </template>
  162. </el-dialog>
  163. </div>
  164. </template>
  165. <script setup>
  166. import { computed, ref } from "vue";
  167. import byTable from "@/components/byTable/index";
  168. import byForm from "@/components/byForm/index";
  169. import useUserStore from "@/store/modules/user";
  170. import { ElMessage, ElMessageBox } from "element-plus";
  171. import ContractDetailsOne from "@/components/contractCom/contractDetailsOne.vue";
  172. import SamplePDF from "@/components/PDF/samplePDF.vue";
  173. const { proxy } = getCurrentInstance();
  174. const accountCurrency = ref([]);
  175. const tradeMethods = ref([]);
  176. const corporationList = ref([]);
  177. const customerList = ref([]);
  178. const userList = ref([]);
  179. const shippingMethod = ref([]);
  180. const customerTag = ref([]);
  181. const openDetailsDialog = ref(false);
  182. const status = ref([
  183. {
  184. label: "草稿",
  185. value: 0,
  186. },
  187. {
  188. label: "审批中",
  189. value: 10,
  190. },
  191. {
  192. label: "驳回",
  193. value: 20,
  194. },
  195. {
  196. label: "审批通过",
  197. value: 30,
  198. },
  199. {
  200. label: "终止",
  201. value: 99,
  202. },
  203. ]);
  204. const sourceList = ref({
  205. data: [],
  206. pagination: {
  207. total: 0,
  208. pageNum: 1,
  209. pageSize: 10,
  210. keyword: "",
  211. status: "",
  212. sellCorporationId: "",
  213. },
  214. });
  215. const loading = ref(false);
  216. const selectConfig = computed(() => {
  217. return [
  218. {
  219. label: "审批状态",
  220. prop: "status",
  221. data: status.value,
  222. },
  223. {
  224. label: "归属公司",
  225. prop: "sellCorporationId",
  226. data: corporationList.value,
  227. },
  228. ];
  229. });
  230. const config = computed(() => {
  231. return [
  232. {
  233. attrs: {
  234. label: "样品单号",
  235. slot: "code",
  236. width: 180,
  237. },
  238. },
  239. {
  240. attrs: {
  241. label: "归属公司",
  242. prop: "sellCorporationId",
  243. "min-width": 220,
  244. },
  245. render(type) {
  246. return proxy.dictValueLabel(type, corporationList.value);
  247. },
  248. },
  249. {
  250. attrs: {
  251. label: "业务员",
  252. prop: "createUser",
  253. width: 140,
  254. },
  255. render(type) {
  256. return proxy.dictValueLabel(type, userList.value);
  257. },
  258. },
  259. {
  260. attrs: {
  261. label: "下单时间",
  262. prop: "createTime",
  263. width: 160,
  264. },
  265. },
  266. {
  267. attrs: {
  268. label: "客户名称",
  269. slot: "buyCorporationName",
  270. "min-width": 220,
  271. },
  272. },
  273. // {
  274. // attrs: {
  275. // label: "客户标签",
  276. // slot: "tags",
  277. // width: 180,
  278. // },
  279. // },
  280. {
  281. attrs: {
  282. label: "预付比例",
  283. slot: "advanceRatio",
  284. width: 140,
  285. align: "right",
  286. },
  287. },
  288. {
  289. attrs: {
  290. label: "样品单金额",
  291. slot: "amount",
  292. width: 140,
  293. align: "right",
  294. },
  295. },
  296. {
  297. attrs: {
  298. label: "汇率",
  299. prop: "rate",
  300. width: 80,
  301. align: "right",
  302. },
  303. render(rate) {
  304. return proxy.moneyFormat(rate, 4);
  305. },
  306. },
  307. {
  308. attrs: {
  309. label: "样品单金额(CNY)",
  310. prop: "amountCNY",
  311. width: 160,
  312. align: "right",
  313. },
  314. render(amountCNY) {
  315. return proxy.moneyFormat(amountCNY, 2);
  316. },
  317. },
  318. {
  319. attrs: {
  320. label: "到账金额(CNY)",
  321. prop: "sumClaimMoney",
  322. width: 160,
  323. align: "right",
  324. },
  325. render(sumClaimMoney) {
  326. return proxy.moneyFormat(sumClaimMoney, 2);
  327. },
  328. },
  329. {
  330. attrs: {
  331. label: "到账比例",
  332. slot: "scale",
  333. width: 100,
  334. align: "right",
  335. },
  336. },
  337. {
  338. attrs: {
  339. label: "审批状态",
  340. prop: "status",
  341. width: 140,
  342. },
  343. render(type) {
  344. return proxy.dictValueLabel(type, status.value);
  345. },
  346. },
  347. {
  348. attrs: {
  349. label: "操作",
  350. width: 160,
  351. align: "center",
  352. fixed: "right",
  353. },
  354. renderHTML(row) {
  355. return [
  356. {
  357. attrs: {
  358. label: "交接单",
  359. type: "primary",
  360. text: true,
  361. },
  362. el: "button",
  363. click() {
  364. clickHandoverSlip(row);
  365. },
  366. },
  367. {
  368. attrs: {
  369. label: "打印",
  370. type: "primary",
  371. text: true,
  372. },
  373. el: "button",
  374. click() {
  375. clickPrint(row);
  376. },
  377. },
  378. {
  379. attrs: {
  380. label: "作废",
  381. type: "primary",
  382. text: true,
  383. },
  384. el: "button",
  385. click() {
  386. ElMessageBox.confirm(
  387. "此操作将永久删除该数据, 是否继续?",
  388. "提示",
  389. {
  390. confirmButtonText: "确定",
  391. cancelButtonText: "取消",
  392. type: "warning",
  393. }
  394. ).then(() => {
  395. proxy
  396. .post("/sample/edit", {
  397. id: row.id,
  398. status: 88,
  399. })
  400. .then(() => {
  401. ElMessage({
  402. message: "作废成功",
  403. type: "success",
  404. });
  405. getList();
  406. });
  407. });
  408. },
  409. },
  410. ];
  411. },
  412. },
  413. ];
  414. });
  415. const getDict = () => {
  416. proxy
  417. .getDictOne([
  418. "customer_tag",
  419. "trade_mode",
  420. "account_currency",
  421. "shipping_method",
  422. ])
  423. .then((res) => {
  424. customerTag.value = res["customer_tag"].map((x) => ({
  425. label: x.dictValue,
  426. value: x.dictKey,
  427. }));
  428. tradeMethods.value = res["trade_mode"].map((x) => ({
  429. label: x.dictValue,
  430. value: x.dictKey,
  431. }));
  432. accountCurrency.value = res["account_currency"].map((x) => ({
  433. label: x.dictValue,
  434. value: x.dictKey,
  435. }));
  436. shippingMethod.value = res["shipping_method"].map((x) => ({
  437. label: x.dictValue,
  438. value: x.dictKey,
  439. }));
  440. });
  441. proxy.post("/corporation/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  442. corporationList.value = res.rows.map((item) => {
  443. return {
  444. ...item,
  445. label: item.name,
  446. value: item.id,
  447. };
  448. });
  449. });
  450. proxy.post("/customer/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  451. customerList.value = res.rows.map((item) => {
  452. return {
  453. ...item,
  454. label: item.name,
  455. value: item.id,
  456. };
  457. });
  458. });
  459. proxy
  460. .get("/tenantUser/list", {
  461. pageNum: 1,
  462. pageSize: 10000,
  463. tenantId: useUserStore().user.tenantId,
  464. })
  465. .then((res) => {
  466. userList.value = res.rows.map((item) => {
  467. return {
  468. label: item.nickName,
  469. value: item.userId,
  470. };
  471. });
  472. });
  473. };
  474. const getList = async (req) => {
  475. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  476. loading.value = true;
  477. proxy.post("/sample/page", sourceList.value.pagination).then((res) => {
  478. res.rows.forEach((x) => {
  479. x.addTagShow = false;
  480. if (x.tag) {
  481. x.tags = x.tag.split(",");
  482. } else {
  483. x.tags = [];
  484. }
  485. });
  486. sourceList.value.data = res.rows;
  487. sourceList.value.pagination.total = res.total;
  488. setTimeout(() => {
  489. loading.value = false;
  490. }, 200);
  491. });
  492. };
  493. getDict();
  494. getList();
  495. const newSample = () => {
  496. proxy.$router.replace({
  497. path: "/platform_manage/process/processApproval",
  498. query: {
  499. flowKey: "sample_flow",
  500. flowName: "样品单审批流程",
  501. random: proxy.random(),
  502. tenantType: "EHSD",
  503. },
  504. });
  505. };
  506. const openHandoverSlip = ref(false);
  507. const handoverSlipForm = ref({
  508. id: "",
  509. code: "",
  510. buyCorporationName: "",
  511. fileList: [],
  512. packageFileList: [],
  513. });
  514. const formOption = reactive({
  515. inline: true,
  516. labelWidth: 100,
  517. itemWidth: 100,
  518. rules: [],
  519. });
  520. const formConfig = computed(() => {
  521. return [
  522. {
  523. type: "input",
  524. prop: "code",
  525. label: "样品单编号",
  526. itemType: "text",
  527. disabled: true,
  528. },
  529. {
  530. type: "input",
  531. prop: "buyCorporationName",
  532. label: "客户名称",
  533. itemType: "text",
  534. disabled: true,
  535. },
  536. {
  537. type: "slot",
  538. slotName: "file",
  539. label: "交接单",
  540. },
  541. {
  542. type: "slot",
  543. slotName: "indication",
  544. label: "包装指示",
  545. },
  546. ];
  547. });
  548. const uploadData = ref({});
  549. const indicationUploadData = ref({});
  550. const clickHandoverSlip = (item) => {
  551. handoverSlipForm.value.id = item.id;
  552. handoverSlipForm.value.code = item.code;
  553. handoverSlipForm.value.buyCorporationName = item.buyCorporationName;
  554. if (item.fileInfoVos && item.fileInfoVos.length > 0) {
  555. handoverSlipForm.value.fileList = item.fileInfoVos.map((item) => {
  556. return {
  557. raw: item,
  558. name: item.fileName,
  559. url: item.fileUrl,
  560. };
  561. });
  562. } else {
  563. handoverSlipForm.value.fileList = [];
  564. }
  565. if (item.packageFileInfoVOList && item.packageFileInfoVOList.length > 0) {
  566. handoverSlipForm.value.packageFileList = item.packageFileInfoVOList.map(
  567. (item) => {
  568. return {
  569. raw: item,
  570. name: item.fileName,
  571. url: item.fileUrl,
  572. };
  573. }
  574. );
  575. } else {
  576. handoverSlipForm.value.packageFileList = [];
  577. }
  578. openHandoverSlip.value = true;
  579. };
  580. const uploadFile = async (file) => {
  581. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  582. uploadData.value = res.uploadBody;
  583. file.id = res.id;
  584. file.fileName = res.fileName;
  585. file.fileUrl = res.fileUrl;
  586. file.uploadState = true;
  587. return true;
  588. };
  589. const indicationUploadFile = async (file) => {
  590. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  591. indicationUploadData.value = res.uploadBody;
  592. file.id = res.id;
  593. file.fileName = res.fileName;
  594. file.fileUrl = res.fileUrl;
  595. file.uploadState = true;
  596. return true;
  597. };
  598. const handleSuccess = (any, UploadFile) => {
  599. UploadFile.raw.uploadState = false;
  600. };
  601. const onPreviewFile = (file) => {
  602. window.open(file.raw.fileUrl, "_blank");
  603. };
  604. const submitHandoverSlip = () => {
  605. if (
  606. handoverSlipForm.value.fileList &&
  607. handoverSlipForm.value.fileList.length > 0
  608. ) {
  609. for (let i = 0; i < handoverSlipForm.value.fileList.length; i++) {
  610. if (handoverSlipForm.value.fileList[i].raw.uploadState) {
  611. return ElMessage("文件上传中,请稍后提交");
  612. }
  613. }
  614. }
  615. if (
  616. handoverSlipForm.value.packageFileList &&
  617. handoverSlipForm.value.packageFileList.length > 0
  618. ) {
  619. for (let i = 0; i < handoverSlipForm.value.packageFileList.length; i++) {
  620. if (handoverSlipForm.value.packageFileList[i].raw.uploadState) {
  621. return ElMessage("文件上传中,请稍后提交");
  622. }
  623. }
  624. }
  625. let data = proxy.deepClone(handoverSlipForm.value);
  626. if (data.fileList && data.fileList.length > 0) {
  627. data.fileList = data.fileList.map((item) => {
  628. return {
  629. id: item.raw.id,
  630. fileName: item.raw.fileName,
  631. fileUrl: item.raw.fileUrl,
  632. };
  633. });
  634. } else {
  635. data.fileList = [];
  636. }
  637. if (data.packageFileList && data.packageFileList.length > 0) {
  638. data.packageFileList = data.packageFileList.map((item) => {
  639. return {
  640. id: item.raw.id,
  641. fileName: item.raw.fileName,
  642. fileUrl: item.raw.fileUrl,
  643. };
  644. });
  645. } else {
  646. data.packageFileList = [];
  647. }
  648. proxy.post("/sample/sampleHandover", data).then(() => {
  649. ElMessage({
  650. message: "操作成功!",
  651. type: "success",
  652. });
  653. openHandoverSlip.value = false;
  654. getList();
  655. });
  656. };
  657. const addTag = ref("");
  658. const judgeTagSelect = (data, val) => {
  659. if (data && data.length > 0) {
  660. if (data.includes(val)) {
  661. return true;
  662. }
  663. }
  664. return false;
  665. };
  666. const changeTag = (val, item) => {
  667. let data = {
  668. id: item.buyCorporationId,
  669. tag: proxy.deepClone(item.tags),
  670. };
  671. data.tag.push(val);
  672. data.tag = data.tag.join(",");
  673. proxy.post("/customer/editTag", data).then(() => {
  674. ElMessage({
  675. message: "添加成功",
  676. type: "success",
  677. });
  678. item.addTagShow = false;
  679. addTag.value = "";
  680. getList();
  681. });
  682. };
  683. const tagClose = (val, item) => {
  684. let data = {
  685. id: item.buyCorporationId,
  686. tag: proxy.deepClone(item.tags),
  687. };
  688. data.tag = data.tag.filter((row) => row !== val);
  689. if (data.tag && data.tag.length > 0) {
  690. data.tag = data.tag.join(",");
  691. } else {
  692. data.tag = "";
  693. }
  694. proxy.post("/customer/editTag", data).then(() => {
  695. ElMessage({
  696. message: "添加成功",
  697. type: "success",
  698. });
  699. item.addTagShow = false;
  700. addTag.value = "";
  701. getList();
  702. });
  703. };
  704. const currentContractId = ref("");
  705. const openDetails = (row) => {
  706. currentContractId.value = row.id;
  707. openDetailsDialog.value = true;
  708. };
  709. const showSelect = (item) => {
  710. item.addTagShow = true;
  711. };
  712. const computeScale = (item) => {
  713. let text = 0;
  714. if (
  715. item.sumClaimMoney &&
  716. Number(item.sumClaimMoney) > 0 &&
  717. item.amountCNY &&
  718. Number(item.amountCNY) > 0
  719. ) {
  720. text = parseFloat(
  721. (Number(item.sumClaimMoney) / Number(item.amountCNY)) * 100
  722. ).toFixed(2);
  723. }
  724. return text + "%";
  725. };
  726. const clickCorporationName = (row) => {
  727. proxy.$router.push({
  728. name: "Portrait",
  729. query: {
  730. id: row.buyCorporationId,
  731. },
  732. });
  733. };
  734. const openPrint = ref(false);
  735. const rowData = ref({});
  736. const clickPrint = (row) => {
  737. rowData.value = {
  738. id: row.id,
  739. };
  740. openPrint.value = true;
  741. };
  742. </script>
  743. <style lang="scss" scoped>
  744. .tenant {
  745. padding: 20px;
  746. }
  747. ::v-deep(.el-input-number .el-input__inner) {
  748. text-align: left;
  749. }
  750. .baseRow {
  751. min-height: 24px;
  752. border-top: 1px solid black;
  753. border-left: 1px solid black;
  754. }
  755. .contentRow {
  756. border-right: 1px solid black;
  757. line-height: 24px;
  758. padding-left: 4px;
  759. }
  760. </style>