index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. <template>
  2. <div class="tenant">
  3. <div class="content">
  4. <byTable
  5. :source="sourceList.data"
  6. :pagination="sourceList.pagination"
  7. :config="config"
  8. :loading="loading"
  9. :selectConfig="selectConfig"
  10. highlight-current-row
  11. :action-list="[
  12. {
  13. text: '添加用户',
  14. action: () => openModal('add'),
  15. },
  16. ]"
  17. @get-list="getList">
  18. <template #address="{ item }">
  19. <span>{{ item.countryName }}</span>
  20. <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
  21. <span v-if="item.cityName"> ,{{ item.cityName }}</span>
  22. </template>
  23. </byTable>
  24. </div>
  25. <el-dialog :title="modalType == 'add' ? '新增' : '编辑'" v-model="dialogVisible" width="800" v-loading="loadingOperation">
  26. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
  27. <template #address>
  28. <el-row :gutter="10" style="width: 100%; margin-left: -15px">
  29. <el-col :span="8">
  30. <el-form-item prop="countryId">
  31. <el-select v-model="formData.data.countryId" placeholder="国家" @change="(val) => getCityData(val, '20', true)">
  32. <el-option v-for="item in countryData" :label="item.chineseName" :value="item.id"> </el-option>
  33. </el-select>
  34. </el-form-item>
  35. </el-col>
  36. <el-col :span="8">
  37. <el-form-item prop="provinceId">
  38. <el-select v-model="formData.data.provinceId" placeholder="省/洲" @change="(val) => getCityData(val, '30', true)">
  39. <el-option v-for="item in provinceData" :label="item.name" :value="item.id"> </el-option>
  40. </el-select>
  41. </el-form-item>
  42. </el-col>
  43. <el-col :span="8">
  44. <el-form-item prop="cityId">
  45. <el-select v-model="formData.data.cityId" placeholder="城市">
  46. <el-option v-for="item in cityData" :label="item.name" :value="item.id"> </el-option>
  47. </el-select>
  48. </el-form-item>
  49. </el-col>
  50. </el-row>
  51. <el-row style="margin-top: 20px; width: 100%; margin-left: -10px">
  52. <el-col :span="24">
  53. <el-form-item prop="address">
  54. <el-input v-model="formData.data.address" type="textarea"> </el-input>
  55. </el-form-item>
  56. </el-col>
  57. </el-row>
  58. </template>
  59. <template #person>
  60. <div>
  61. <el-button type="primary" @click="clickAddPerson"> 添加 </el-button>
  62. <byTable :source="formData.data.customerUserList" :config="configPerson" hideSearch hidePagination> </byTable>
  63. </div>
  64. </template>
  65. </byForm>
  66. <template #footer>
  67. <el-button @click="dialogVisible = false" size="large">取 消</el-button>
  68. <el-button type="primary" @click="submitForm('submit')" size="large" :loading="submitLoading"> 确 定 </el-button>
  69. </template>
  70. </el-dialog>
  71. <el-dialog title="添加联系人" v-model="openPerson" width="400" v-loading="openPerson">
  72. <byForm :formConfig="formConfigPerson" :formOption="formOption" v-model="formPerson.data" :rules="rulesPerson" ref="person"> </byForm>
  73. <template #footer>
  74. <el-button @click="openPerson = false" size="large">取 消</el-button>
  75. <el-button type="primary" @click="submitPerson('person')" size="large"> 确 定 </el-button>
  76. </template>
  77. </el-dialog>
  78. </div>
  79. </template>
  80. <script setup>
  81. import { ElMessage, ElMessageBox } from "element-plus";
  82. import byTable from "@/components/byTable/index";
  83. import byForm from "@/components/byForm/index";
  84. import { computed, ref } from "vue";
  85. import useUserStore from "@/store/modules/user";
  86. const { proxy } = getCurrentInstance();
  87. const loading = ref(false);
  88. const loadingOperation = ref(false);
  89. const submitLoading = ref(false);
  90. const openPerson = ref(false);
  91. const customerSource = ref([]);
  92. const customerStatus = ref([]);
  93. const userList = ref([]);
  94. const sourceList = ref({
  95. data: [],
  96. pagination: {
  97. total: 0,
  98. pageNum: 1,
  99. pageSize: 10,
  100. status: "",
  101. source: "",
  102. type: "",
  103. },
  104. });
  105. const selectConfig = computed(() => {
  106. return [
  107. {
  108. label: "客户状态",
  109. prop: "type",
  110. data: [
  111. {
  112. label: "公海",
  113. value: "0",
  114. },
  115. {
  116. label: "私海",
  117. value: "1",
  118. },
  119. ],
  120. },
  121. {
  122. label: "客户来源",
  123. prop: "source",
  124. data: customerSource.value,
  125. },
  126. {
  127. label: "客户类型",
  128. prop: "status",
  129. data: customerStatus.value,
  130. },
  131. ];
  132. });
  133. const config = computed(() => {
  134. return [
  135. {
  136. attrs: {
  137. label: "客户名称",
  138. prop: "name",
  139. },
  140. },
  141. {
  142. attrs: {
  143. label: "所在城市",
  144. slot: "address",
  145. width: 200,
  146. },
  147. },
  148. {
  149. attrs: {
  150. label: "客户代码",
  151. prop: "customerCode",
  152. width: 140,
  153. },
  154. },
  155. {
  156. attrs: {
  157. label: "客户来源",
  158. prop: "source",
  159. width: 140,
  160. },
  161. render(type) {
  162. let data = customerSource.value.filter((item) => item.value == type);
  163. if (data && data.length > 0) {
  164. return data[0].label;
  165. } else {
  166. return "";
  167. }
  168. },
  169. },
  170. {
  171. attrs: {
  172. label: "客户类型",
  173. prop: "status",
  174. width: 140,
  175. },
  176. render(type) {
  177. let data = customerStatus.value.filter((item) => item.value == type);
  178. if (data && data.length > 0) {
  179. return data[0].label;
  180. } else {
  181. return "";
  182. }
  183. },
  184. },
  185. {
  186. attrs: {
  187. label: "业务员",
  188. prop: "userId",
  189. width: 140,
  190. },
  191. render(type) {
  192. let data = userList.value.filter((item) => item.value == type);
  193. if (data && data.length > 0) {
  194. return data[0].label;
  195. } else {
  196. return "";
  197. }
  198. },
  199. },
  200. {
  201. attrs: {
  202. label: "操作",
  203. width: "160",
  204. align: "center",
  205. },
  206. renderHTML(row) {
  207. return [
  208. {
  209. attrs: {
  210. label: "修改",
  211. type: "primary",
  212. text: true,
  213. },
  214. el: "button",
  215. click() {
  216. update(row);
  217. },
  218. },
  219. {
  220. attrs: {
  221. label: "删除",
  222. type: "primary",
  223. text: true,
  224. },
  225. el: "button",
  226. click() {
  227. ElMessageBox.confirm("此操作将永久删除该数据, 是否继续?", "提示", {
  228. confirmButtonText: "确定",
  229. cancelButtonText: "取消",
  230. type: "warning",
  231. }).then(() => {
  232. proxy
  233. .post("/customer/delete", {
  234. id: row.id,
  235. })
  236. .then(() => {
  237. ElMessage({
  238. message: "删除成功",
  239. type: "success",
  240. });
  241. getList();
  242. });
  243. });
  244. },
  245. },
  246. ];
  247. },
  248. },
  249. ];
  250. });
  251. let modalType = ref("add");
  252. let dialogVisible = ref(false);
  253. let formData = reactive({
  254. data: {
  255. countryId: "China",
  256. },
  257. });
  258. let formPerson = reactive({
  259. data: {},
  260. });
  261. const formOption = reactive({
  262. inline: true,
  263. labelWidth: 100,
  264. itemWidth: 100,
  265. rules: [],
  266. });
  267. const formConfig = computed(() => {
  268. return [
  269. {
  270. type: "input",
  271. prop: "name",
  272. label: "客户名称",
  273. required: true,
  274. itemWidth: 100,
  275. itemType: "text",
  276. },
  277. {
  278. type: "slot",
  279. slotName: "address",
  280. label: "详细地址",
  281. },
  282. {
  283. type: "input",
  284. prop: "customerCode",
  285. label: "客户代码",
  286. required: true,
  287. itemWidth: 100,
  288. itemType: "text",
  289. },
  290. {
  291. type: "select",
  292. label: "客户来源",
  293. prop: "source",
  294. itemWidth: 50,
  295. isLoad: {
  296. url: "/dictTenantData/page",
  297. labelKey: "dictValue",
  298. labelVal: "dictKey",
  299. method: "post",
  300. req: {
  301. pageNum: 1,
  302. pageSize: 999,
  303. dictCode: "customer_source",
  304. tenantId: useUserStore().user.tenantId,
  305. },
  306. resUrl: "rows",
  307. },
  308. },
  309. {
  310. type: "select",
  311. label: "客户类型",
  312. prop: "status",
  313. itemWidth: 50,
  314. isLoad: {
  315. url: "/dictTenantData/page",
  316. labelKey: "dictValue",
  317. labelVal: "dictKey",
  318. method: "post",
  319. req: {
  320. pageNum: 1,
  321. pageSize: 999,
  322. dictCode: "customer_status",
  323. tenantId: useUserStore().user.tenantId,
  324. },
  325. resUrl: "rows",
  326. },
  327. },
  328. {
  329. type: "select",
  330. label: "业务员",
  331. prop: "userId",
  332. itemWidth: 100,
  333. isLoad: {
  334. url: "/tenantUser/list?pageNum=1&pageSize=10000",
  335. labelKey: "userName",
  336. labelVal: "userId",
  337. method: "get",
  338. resUrl: "rows",
  339. },
  340. },
  341. {
  342. type: "slot",
  343. slotName: "person",
  344. label: "客户联系人",
  345. },
  346. ];
  347. });
  348. let rules = ref({
  349. name: [{ required: true, message: "请输入客户名称", trigger: "blur" }],
  350. countryId: [{ required: true, message: "请选择国家", trigger: "change" }],
  351. provinceId: [{ required: true, message: "请选择省/州", trigger: "change" }],
  352. // cityId: [{ required: true, message: "请选择城市", trigger: "change" }],
  353. source: [{ required: true, message: "请选择客户来源", trigger: "change" }],
  354. status: [{ required: true, message: "请选择类型", trigger: "change" }],
  355. });
  356. const formConfigPerson = computed(() => {
  357. return [
  358. {
  359. type: "input",
  360. prop: "name",
  361. label: "联系人名称",
  362. required: true,
  363. itemWidth: 100,
  364. itemType: "text",
  365. },
  366. {
  367. type: "input",
  368. prop: "phone",
  369. label: "联系人电话",
  370. required: true,
  371. itemWidth: 100,
  372. itemType: "text",
  373. },
  374. ];
  375. });
  376. const configPerson = computed(() => {
  377. return [
  378. {
  379. attrs: {
  380. label: "联系人名称",
  381. prop: "name",
  382. width: 150,
  383. },
  384. },
  385. {
  386. attrs: {
  387. label: "联系人电话",
  388. prop: "phone",
  389. width: 440,
  390. },
  391. },
  392. {
  393. attrs: {
  394. label: "操作",
  395. width: "100",
  396. align: "center",
  397. },
  398. renderHTML(row) {
  399. return [
  400. {
  401. attrs: {
  402. label: "删除",
  403. type: "primary",
  404. text: true,
  405. },
  406. el: "button",
  407. click() {
  408. formData.data.customerUserList.splice(row.$index, 1);
  409. },
  410. },
  411. ];
  412. },
  413. },
  414. ];
  415. });
  416. let rulesPerson = ref({
  417. name: [{ required: true, message: "请输入联系人名称", trigger: "blur" }],
  418. phone: [{ required: true, message: "请输入联系人电话", trigger: "blur" }],
  419. });
  420. const submit = ref(null);
  421. const person = ref(null);
  422. const getList = async (req) => {
  423. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  424. loading.value = true;
  425. proxy.post("/customer/page", sourceList.value.pagination).then((res) => {
  426. sourceList.value.data = res.rows;
  427. sourceList.value.pagination.total = res.total;
  428. setTimeout(() => {
  429. loading.value = false;
  430. }, 200);
  431. });
  432. };
  433. const openModal = () => {
  434. modalType.value = "add";
  435. formData.data = {
  436. countryId: "China",
  437. };
  438. getCityData(formData.data.countryId, "20");
  439. loadingOperation.value = false;
  440. dialogVisible.value = true;
  441. };
  442. const update = (row) => {
  443. modalType.value = "edit";
  444. loadingOperation.value = true;
  445. proxy.post("/customer/detail", { id: row.id }).then((res) => {
  446. res.customerUserList = res.customerUserList.map((item) => {
  447. return {
  448. name: item.name,
  449. phone: item.phone,
  450. customerId: item.customerId,
  451. };
  452. });
  453. formData.data = res;
  454. getCityData(formData.data.countryId, "20");
  455. getCityData(formData.data.provinceId, "30");
  456. loadingOperation.value = false;
  457. });
  458. dialogVisible.value = true;
  459. };
  460. const countryData = ref([]);
  461. const provinceData = ref([]);
  462. const cityData = ref([]);
  463. const getCityData = (id, type, isChange) => {
  464. proxy.post("/areaInfo/list", { parentId: id }).then((res) => {
  465. if (type === "20") {
  466. provinceData.value = res;
  467. if (isChange) {
  468. formData.data.provinceId = "";
  469. formData.data.cityId = "";
  470. }
  471. } else if (type === "30") {
  472. cityData.value = res;
  473. if (isChange) {
  474. formData.data.cityId = "";
  475. }
  476. } else {
  477. countryData.value = res;
  478. }
  479. });
  480. };
  481. getCityData("0");
  482. const clickAddPerson = () => {
  483. formPerson.data = {};
  484. openPerson.value = true;
  485. };
  486. const submitPerson = () => {
  487. person.value.handleSubmit(() => {
  488. if (formData.data.customerUserList && formData.data.customerUserList.length > 0) {
  489. formData.data.customerUserList.push({
  490. name: formPerson.data.name,
  491. phone: formPerson.data.phone,
  492. });
  493. } else {
  494. formData.data.customerUserList = [
  495. {
  496. name: formPerson.data.name,
  497. phone: formPerson.data.phone,
  498. },
  499. ];
  500. }
  501. openPerson.value = false;
  502. });
  503. };
  504. const submitForm = () => {
  505. submit.value.handleSubmit(() => {
  506. if (formData.data.customerUserList && formData.data.customerUserList.length > 0) {
  507. submitLoading.value = true;
  508. proxy.post("/customer/" + modalType.value, formData.data).then(
  509. () => {
  510. ElMessage({
  511. message: modalType.value == "add" ? "添加成功" : "编辑成功",
  512. type: "success",
  513. });
  514. dialogVisible.value = false;
  515. submitLoading.value = false;
  516. getList();
  517. },
  518. (err) => {
  519. console.log(err);
  520. submitLoading.value = false;
  521. }
  522. );
  523. } else {
  524. ElMessage("请添加客户联系人");
  525. }
  526. });
  527. };
  528. const getDict = () => {
  529. proxy
  530. .post("/dictTenantData/page", {
  531. pageNum: 1,
  532. pageSize: 999,
  533. dictCode: "customer_source",
  534. tenantId: useUserStore().user.tenantId,
  535. })
  536. .then((res) => {
  537. customerSource.value = res.rows.map((item) => {
  538. return {
  539. label: item.dictValue,
  540. value: item.dictKey,
  541. };
  542. });
  543. });
  544. proxy
  545. .post("/dictTenantData/page", {
  546. pageNum: 1,
  547. pageSize: 999,
  548. dictCode: "customer_status",
  549. tenantId: useUserStore().user.tenantId,
  550. })
  551. .then((res) => {
  552. customerStatus.value = res.rows.map((item) => {
  553. return {
  554. label: item.dictValue,
  555. value: item.dictKey,
  556. };
  557. });
  558. });
  559. proxy.get("/tenantUser/list?pageNum=1&pageSize=10000", {}).then((res) => {
  560. userList.value = res.rows.map((item) => {
  561. return {
  562. label: item.userName,
  563. value: item.userId,
  564. };
  565. });
  566. });
  567. };
  568. getDict();
  569. getList();
  570. </script>
  571. <style lang="scss" scoped>
  572. .tenant {
  573. padding: 20px;
  574. }
  575. </style>