index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. <template>
  2. <div class="tenant">
  3. <!-- <Banner /> -->
  4. <div class="content">
  5. <byTable
  6. :source="sourceList.data"
  7. :pagination="sourceList.pagination"
  8. :config="config"
  9. :loading="loading"
  10. highlight-current-row
  11. :selectConfig="selectConfig"
  12. :table-events="{
  13. //element talbe事件都能传
  14. select: select,
  15. }"
  16. :action-list="[
  17. {
  18. text: '添加供应商',
  19. action: () => openModal('add'),
  20. },
  21. ]"
  22. @get-list="getList"
  23. >
  24. <template #address="{ item }">
  25. <div>
  26. {{ item.countryName }}, {{ item.provinceName }} ,
  27. {{ item.cityName }}, {{ item.areaDetail }}
  28. </div>
  29. </template>
  30. </byTable>
  31. </div>
  32. <el-dialog
  33. :title="modalType == 'add' ? '添加供应商' : '编辑供应商'"
  34. v-model="dialogVisible"
  35. width="800"
  36. v-loading="loading"
  37. destroy-on-close
  38. >
  39. <byForm
  40. :formConfig="formConfig"
  41. :formOption="formOption"
  42. v-model="formData.data"
  43. :rules="rules"
  44. ref="byform"
  45. >
  46. <template #address>
  47. <el-row :gutter="10" style="width: 100%">
  48. <el-col :span="8">
  49. <el-form-item prop="countryId">
  50. <el-select
  51. v-model="formData.data.countryId"
  52. placeholder="国家"
  53. @change="(val) => getCityData(val, '20', true)"
  54. >
  55. <el-option
  56. v-for="item in countryData"
  57. :label="item.chineseName"
  58. :value="item.id"
  59. :key="item.id"
  60. >
  61. </el-option>
  62. </el-select>
  63. </el-form-item>
  64. </el-col>
  65. <el-col :span="8">
  66. <el-form-item prop="provinceId">
  67. <el-select
  68. v-model="formData.data.provinceId"
  69. placeholder="省/洲"
  70. @change="(val) => getCityData(val, '30', true)"
  71. >
  72. <el-option
  73. v-for="item in provinceData"
  74. :label="item.name"
  75. :value="item.id"
  76. :key="item.id"
  77. >
  78. </el-option>
  79. </el-select>
  80. </el-form-item>
  81. </el-col>
  82. <el-col :span="8">
  83. <el-form-item prop="cityId">
  84. <el-select v-model="formData.data.cityId" placeholder="城市">
  85. <el-option
  86. v-for="item in cityData"
  87. :label="item.name"
  88. :value="item.id"
  89. :key="item.id"
  90. >
  91. </el-option>
  92. </el-select>
  93. </el-form-item>
  94. </el-col>
  95. </el-row>
  96. <el-row style="margin-top: 20px; width: 100%">
  97. <el-col :span="24">
  98. <el-form-item prop="areaDetail">
  99. <el-input v-model="formData.data.areaDetail" type="textarea">
  100. </el-input>
  101. </el-form-item>
  102. </el-col>
  103. </el-row>
  104. </template>
  105. <template #contact>
  106. <el-row :gutter="10" style="width: 100%">
  107. <el-col :span="8">
  108. <el-form-item prop="contactPerson">
  109. <el-input
  110. v-model="formData.data.contactPerson"
  111. placeholder="联系人"
  112. >
  113. </el-input>
  114. </el-form-item>
  115. </el-col>
  116. <el-col :span="16">
  117. <el-form-item prop="contactNumber">
  118. <el-input
  119. v-model="formData.data.contactNumber"
  120. placeholder="联系电话"
  121. :formatter="(val) => val.replace(/[^\d\-]/g, '')"
  122. :parser="(val) => val.replace(/[^\d\-]/g, '')"
  123. >
  124. </el-input>
  125. </el-form-item>
  126. </el-col>
  127. </el-row>
  128. </template>
  129. <template #fileSlot>
  130. <div>
  131. <el-upload
  132. v-model:fileList="fileList"
  133. :show-file-list="false"
  134. class="upload-demo"
  135. action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
  136. :data="uploadData"
  137. :on-preview="handlePreview"
  138. :on-remove="handleRemove"
  139. :on-success="handleSuccess"
  140. :before-upload="handleBeforeUpload"
  141. >
  142. <el-button type="primary">选择</el-button>
  143. </el-upload>
  144. <div>
  145. <div style="margin-top: 15px">
  146. <el-tag
  147. style="margin-right: 10px"
  148. class="ml-2"
  149. type="info"
  150. v-for="(item, index) in fileListCopy"
  151. :key="index"
  152. closable
  153. @close="handleClose(index)"
  154. >{{ item.fileName }}</el-tag
  155. >
  156. </div>
  157. </div>
  158. </div>
  159. </template>
  160. </byForm>
  161. <template #footer>
  162. <el-button @click="dialogVisible = false" size="large">取 消</el-button>
  163. <el-button
  164. type="primary"
  165. @click="submitForm('byform')"
  166. size="large"
  167. :loading="submitLoading"
  168. >
  169. 确 定
  170. </el-button>
  171. </template>
  172. </el-dialog>
  173. </div>
  174. </template>
  175. <script setup>
  176. /* eslint-disable vue/no-unused-components */
  177. import { ElMessage, ElMessageBox } from "element-plus";
  178. import byTable from "@/components/byTable/index";
  179. import byForm from "@/components/byForm/index";
  180. import FileUpload from "@/components/FileUpload/index";
  181. import { computed, defineComponent, ref } from "vue";
  182. import { getToken } from "@/utils/auth";
  183. const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传文件服务器地址
  184. const headers = ref({ Authorization: "Bearer " + getToken() });
  185. const uploadData = ref({});
  186. const loading = ref(false);
  187. const submitLoading = ref(false);
  188. const sourceList = ref({
  189. data: [],
  190. pagination: {
  191. total: 3,
  192. pageNum: 1,
  193. pageSize: 10,
  194. type: "",
  195. keyword: "",
  196. },
  197. });
  198. let dialogVisible = ref(false);
  199. let modalType = ref("add");
  200. let fileList = ref([]);
  201. let fileListCopy = ref([]);
  202. const checkContactNumber = (rule, val, callback) => {
  203. if (val === "") {
  204. callback(new Error("请输入联系电话"));
  205. } else {
  206. val = "aaa";
  207. }
  208. console.log(rule, val, callback, "213");
  209. // @input="(val) => val.replace(/[^\d]/g, '')"
  210. };
  211. let rules = ref({
  212. name: [{ required: true, message: "请输入供应商名称", trigger: "blur" }],
  213. type: [{ required: true, message: "请选择供应商类型", trigger: "change" }],
  214. countryId: [{ required: true, message: "请选择国家", trigger: "change" }],
  215. provinceId: [{ required: true, message: "请选择省/洲", trigger: "change" }],
  216. cityId: [{ required: true, message: "请选择城市", trigger: "change" }],
  217. // areaDetail: [{ required: true, message: "请输入详细地址", trigger: "blur" }],
  218. contactPerson: [{ required: true, message: "请输入联系人", trigger: "blur" }],
  219. // contactNumber: [{ validator: checkContactNumber, trigger: "blur" }],
  220. contactNumber: [
  221. { required: true, message: "请输入联系电话", trigger: "blur" },
  222. ],
  223. accountPeriod: [{ required: true, message: "请输入账期", trigger: "blur" }],
  224. returnPeriod: [
  225. { required: true, message: "请输入退换货期限", trigger: "blur" },
  226. ],
  227. });
  228. const { proxy } = getCurrentInstance();
  229. const selectConfig = reactive([
  230. {
  231. label: "供应商类型",
  232. prop: "type",
  233. data: [],
  234. },
  235. {
  236. label: "账期",
  237. prop: "accountPeriodType",
  238. data: [
  239. {
  240. label: "有",
  241. value: "1",
  242. },
  243. {
  244. label: "无",
  245. value: "0",
  246. },
  247. ],
  248. },
  249. ]);
  250. const config = computed(() => {
  251. return [
  252. {
  253. attrs: {
  254. label: "供应商类型",
  255. prop: "type",
  256. },
  257. render(type) {
  258. return proxy.dictDataEcho(type, supplierType.value);
  259. },
  260. },
  261. {
  262. attrs: {
  263. label: "供应商编码",
  264. prop: "code",
  265. },
  266. },
  267. {
  268. attrs: {
  269. label: "供应商名称",
  270. prop: "name",
  271. },
  272. },
  273. {
  274. attrs: {
  275. label: "所在城市",
  276. prop: "remarks",
  277. slot: "address",
  278. },
  279. },
  280. {
  281. attrs: {
  282. label: "联系人",
  283. prop: "contactPerson",
  284. },
  285. },
  286. {
  287. attrs: {
  288. label: "联系人电话",
  289. prop: "contactNumber",
  290. },
  291. },
  292. {
  293. attrs: {
  294. label: "账期",
  295. prop: "accountPeriod",
  296. },
  297. },
  298. {
  299. attrs: {
  300. label: "退换货期限",
  301. prop: "returnPeriod",
  302. },
  303. },
  304. {
  305. attrs: {
  306. label: "备注",
  307. prop: "remark",
  308. },
  309. },
  310. {
  311. attrs: {
  312. label: "操作",
  313. width: "200",
  314. align: "right",
  315. },
  316. // 渲染 el-button,一般用在最后一列。
  317. renderHTML(row) {
  318. return [
  319. {
  320. attrs: {
  321. label: "修改",
  322. type: "primary",
  323. text: true,
  324. },
  325. el: "button",
  326. click() {
  327. getDtl(row);
  328. },
  329. },
  330. {
  331. attrs: {
  332. label: "删除",
  333. type: "danger",
  334. text: true,
  335. },
  336. el: "button",
  337. click() {
  338. // 弹窗提示是否删除
  339. ElMessageBox.confirm(
  340. "此操作将永久删除该数据, 是否继续?",
  341. "提示",
  342. {
  343. confirmButtonText: "确定",
  344. cancelButtonText: "取消",
  345. type: "warning",
  346. }
  347. ).then(() => {
  348. // 删除
  349. proxy
  350. .post("/supplierInfo/delete", {
  351. id: row.id,
  352. })
  353. .then((res) => {
  354. ElMessage({
  355. message: "删除成功",
  356. type: "success",
  357. });
  358. getList();
  359. });
  360. });
  361. },
  362. },
  363. ];
  364. },
  365. },
  366. ];
  367. });
  368. let formData = reactive({
  369. data: {
  370. type: "1",
  371. },
  372. });
  373. const formOption = reactive({
  374. inline: true,
  375. labelWidth: 100,
  376. itemWidth: 100,
  377. rules: [],
  378. });
  379. const byform = ref(null);
  380. const formConfig = computed(() => {
  381. return [
  382. {
  383. type: "radio",
  384. prop: "type",
  385. label: "供应商类型",
  386. required: true,
  387. border: true,
  388. data: supplierType.value.map((x) => ({
  389. label: x.dictValue,
  390. value: x.dictKey,
  391. })),
  392. },
  393. {
  394. type: "input",
  395. prop: "name",
  396. label: "供应商名称",
  397. required: true,
  398. },
  399. {
  400. type: "slot",
  401. slotName: "address",
  402. prop: "countryId",
  403. label: "地址",
  404. required: true,
  405. },
  406. {
  407. type: "slot",
  408. slotName: "contact",
  409. prop: "contactPerson",
  410. label: "联系信息",
  411. required: true,
  412. },
  413. {
  414. type: "slot",
  415. slotName: "fileSlot",
  416. label: "上传附件",
  417. },
  418. {
  419. type: "input",
  420. label: "备注",
  421. prop: "remark",
  422. itemType: "textarea",
  423. },
  424. {
  425. type: "json",
  426. prop: "victoriatouristJson",
  427. json: [
  428. {
  429. type: "number",
  430. prop: "accountPeriod",
  431. label: "账期",
  432. itemWidth: 33,
  433. controls: false,
  434. precision: 2,
  435. min: 0,
  436. style: {
  437. width: "100%",
  438. "margin-right": "10px",
  439. "text-align": "left",
  440. },
  441. },
  442. {
  443. type: "number",
  444. prop: "returnPeriod",
  445. label: "退换货期限",
  446. itemWidth: 33,
  447. controls: false,
  448. precision: 2,
  449. min: 0,
  450. style: {
  451. width: "100%",
  452. "text-align": "left",
  453. },
  454. },
  455. {
  456. type: "input",
  457. prop: "accountName",
  458. label: "账户名称",
  459. itemWidth: 100,
  460. },
  461. {
  462. type: "input",
  463. prop: "bankOfDeposit",
  464. label: "开户银行",
  465. itemWidth: 100,
  466. },
  467. {
  468. type: "input",
  469. prop: "bankAccount",
  470. label: "银行账号",
  471. itemWidth: 100,
  472. },
  473. {
  474. type: "input",
  475. prop: "interBankNo",
  476. label: "联行号",
  477. itemWidth: 100,
  478. },
  479. // {
  480. // type: "date",
  481. // prop: "clearancePeriod",
  482. // label: "库存清空期限",
  483. // itemWidth: 33.33,
  484. // format: "YYYY-MM-DD",
  485. // },
  486. ],
  487. },
  488. ];
  489. });
  490. const getList = async (req) => {
  491. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  492. loading.value = true;
  493. proxy
  494. .post("/supplierInfo/pageByWdly", sourceList.value.pagination)
  495. .then((message) => {
  496. console.log(message);
  497. sourceList.value.data = message.rows.map((x) => ({
  498. ...x,
  499. ...JSON.parse(x.victoriatouristJson),
  500. }));
  501. sourceList.value.pagination.total = message.total;
  502. setTimeout(() => {
  503. loading.value = false;
  504. }, 200);
  505. });
  506. };
  507. const openModal = () => {
  508. dialogVisible.value = true;
  509. modalType.value = "add";
  510. fileList.value = [];
  511. fileListCopy.value = [];
  512. formData.data = {
  513. countryId: "China",
  514. victoriatouristJson: {},
  515. };
  516. getCityData(formData.data.countryId, "20");
  517. };
  518. const submitForm = () => {
  519. byform.value.handleSubmit((valid) => {
  520. formData.data.fileList =
  521. fileListCopy.value.map((x) => ({
  522. id: x.id,
  523. fileName: x.fileName,
  524. })) || [];
  525. const obj = JSON.parse(formData.data.victoriatouristJson);
  526. console.log(obj, "ss");
  527. if (obj.accountPeriod === undefined || obj.accountPeriod === "") {
  528. formData.data.victoriatouristJson = obj;
  529. return ElMessage({
  530. message: "请输入账期!",
  531. type: "info",
  532. });
  533. }
  534. if (obj.returnPeriod === undefined || obj.returnPeriod === "") {
  535. formData.data.victoriatouristJson = obj;
  536. return ElMessage({
  537. message: "请输入退换货期限!",
  538. type: "info",
  539. });
  540. }
  541. formData.data.victoriatouristJson = JSON.stringify(obj);
  542. submitLoading.value = true;
  543. proxy.post("/supplierInfo/" + modalType.value, formData.data).then(
  544. (res) => {
  545. ElMessage({
  546. message: modalType.value == "add" ? "添加成功" : "编辑成功",
  547. type: "success",
  548. });
  549. fileList.value = [];
  550. dialogVisible.value = false;
  551. submitLoading.value = false;
  552. getList();
  553. },
  554. (err) => {
  555. submitLoading.value = false;
  556. }
  557. );
  558. });
  559. };
  560. const getDtl = (row) => {
  561. modalType.value = "edit";
  562. proxy.post("/supplierInfo/detailByWdly", { id: row.id }).then((res) => {
  563. proxy
  564. .post("/fileInfo/getList", { businessIdList: [row.id] })
  565. .then((fileObj) => {
  566. fileList.value = fileObj[row.id] || [];
  567. fileListCopy.value = [...fileList.value];
  568. getCityData(res.countryId, "20");
  569. getCityData(res.provinceId, "30");
  570. res.type = res.type + "";
  571. res.victoriatouristJson = res.victoriatouristJson
  572. ? JSON.parse(res.victoriatouristJson)
  573. : {};
  574. formData.data = res;
  575. dialogVisible.value = true;
  576. });
  577. });
  578. };
  579. const handleBeforeUpload = async (file) => {
  580. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  581. uploadData.value = res.uploadBody;
  582. fileListCopy.value.push({
  583. id: res.id,
  584. fileName: res.fileName,
  585. path: res.fileUrl,
  586. url: res.fileUrl,
  587. uid: file.uid,
  588. });
  589. };
  590. const handleClickFile = (row) => {
  591. ElMessage({
  592. message: "数据请求中,请稍后!",
  593. type: "success",
  594. });
  595. let id = row.id;
  596. proxy.post("/fileInfo/getList", { businessIdList: [id] }).then((res) => {
  597. const file = res[id][0];
  598. window.open(file.fileUrl, "_blank");
  599. });
  600. };
  601. const handleClose = (index) => {
  602. // if (fileListCopy.value.length === 1) {
  603. // return ElMessage({
  604. // message: "最后一个附件啦!",
  605. // type: "info",
  606. // });
  607. // }
  608. fileList.value.splice(index, 1);
  609. fileListCopy.value.splice(index, 1);
  610. };
  611. const countryData = ref([]);
  612. const provinceData = ref([]);
  613. const cityData = ref([]);
  614. const getCityData = (id, type, isChange) => {
  615. proxy.post("/areaInfo/list", { parentId: id }).then((res) => {
  616. if (type === "20") {
  617. provinceData.value = res;
  618. if (isChange) {
  619. formData.data.provinceId = "";
  620. formData.data.cityId = "";
  621. }
  622. } else if (type === "30") {
  623. cityData.value = res;
  624. if (isChange) {
  625. formData.data.cityId = "";
  626. }
  627. } else {
  628. countryData.value = res;
  629. }
  630. });
  631. };
  632. const supplierType = ref([]);
  633. const getDict = () => {
  634. proxy.getDict(["supplier_type"]).then((res) => {
  635. supplierType.value = res["supplier_type"];
  636. selectConfig[0].data = supplierType.value.map((x) => ({
  637. label: x.dictValue,
  638. value: x.dictKey,
  639. }));
  640. });
  641. };
  642. getDict();
  643. getCityData("0");
  644. getList();
  645. </script>
  646. <style lang="scss" scoped>
  647. .tenant {
  648. padding: 20px;
  649. }
  650. </style>