index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  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. },
  103. });
  104. const selectConfig = computed(() => {
  105. return [
  106. {
  107. label: "客户来源",
  108. prop: "source",
  109. data: customerSource.value,
  110. },
  111. {
  112. label: "客户类型",
  113. prop: "status",
  114. data: customerStatus.value,
  115. },
  116. ];
  117. });
  118. const config = computed(() => {
  119. return [
  120. {
  121. attrs: {
  122. label: "客户名称",
  123. prop: "name",
  124. },
  125. },
  126. {
  127. attrs: {
  128. label: "所在城市",
  129. slot: "address",
  130. width: 200,
  131. },
  132. },
  133. {
  134. attrs: {
  135. label: "客户代码",
  136. prop: "customerCode",
  137. width: 140,
  138. },
  139. },
  140. {
  141. attrs: {
  142. label: "客户来源",
  143. prop: "source",
  144. width: 140,
  145. },
  146. render(type) {
  147. let data = customerSource.value.filter((item) => item.value == type);
  148. if (data && data.length > 0) {
  149. return data[0].label;
  150. } else {
  151. return "";
  152. }
  153. },
  154. },
  155. {
  156. attrs: {
  157. label: "客户类型",
  158. prop: "status",
  159. width: 140,
  160. },
  161. render(type) {
  162. let data = customerStatus.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. width: "100",
  174. align: "center",
  175. },
  176. renderHTML(row) {
  177. return [
  178. {
  179. attrs: {
  180. label: "退回公海",
  181. type: "primary",
  182. text: true,
  183. },
  184. el: "button",
  185. click() {
  186. ElMessageBox.confirm("是否确定将该客户退回公海?", "提示", {
  187. confirmButtonText: "确定",
  188. cancelButtonText: "取消",
  189. type: "warning",
  190. }).then(() => {
  191. proxy
  192. .post("/customer/CustomerAllocation", {
  193. id: row.id,
  194. userId: "",
  195. })
  196. .then(() => {
  197. ElMessage({
  198. message: "退回成功",
  199. type: "success",
  200. });
  201. getList();
  202. });
  203. });
  204. },
  205. },
  206. ];
  207. },
  208. },
  209. ];
  210. });
  211. let modalType = ref("add");
  212. let dialogVisible = ref(false);
  213. let formData = reactive({
  214. data: {
  215. countryId: "China",
  216. },
  217. });
  218. let formPerson = reactive({
  219. data: {},
  220. });
  221. const formOption = reactive({
  222. inline: true,
  223. labelWidth: 100,
  224. itemWidth: 100,
  225. rules: [],
  226. });
  227. const formConfig = computed(() => {
  228. return [
  229. {
  230. type: "input",
  231. prop: "name",
  232. label: "客户名称",
  233. required: true,
  234. itemWidth: 100,
  235. itemType: "text",
  236. },
  237. {
  238. type: "slot",
  239. slotName: "address",
  240. label: "详细地址",
  241. },
  242. {
  243. type: "input",
  244. prop: "customerCode",
  245. label: "客户代码",
  246. required: true,
  247. itemWidth: 100,
  248. itemType: "text",
  249. },
  250. {
  251. type: "select",
  252. label: "客户来源",
  253. prop: "source",
  254. itemWidth: 50,
  255. isLoad: {
  256. url: "/dictTenantData/page",
  257. labelKey: "dictValue",
  258. labelVal: "dictKey",
  259. method: "post",
  260. req: {
  261. pageNum: 1,
  262. pageSize: 999,
  263. dictCode: "customer_source",
  264. tenantId: useUserStore().user.tenantId,
  265. },
  266. resUrl: "rows",
  267. },
  268. },
  269. {
  270. type: "select",
  271. label: "客户类型",
  272. prop: "status",
  273. itemWidth: 50,
  274. isLoad: {
  275. url: "/dictTenantData/page",
  276. labelKey: "dictValue",
  277. labelVal: "dictKey",
  278. method: "post",
  279. req: {
  280. pageNum: 1,
  281. pageSize: 999,
  282. dictCode: "customer_status",
  283. tenantId: useUserStore().user.tenantId,
  284. },
  285. resUrl: "rows",
  286. },
  287. },
  288. {
  289. type: "select",
  290. label: "业务员",
  291. prop: "userId",
  292. itemWidth: 100,
  293. isLoad: {
  294. url: "/tenantUser/list",
  295. labelKey: "nickName",
  296. labelVal: "userId",
  297. method: "get",
  298. resUrl: "rows",
  299. req: {
  300. pageNum: 1,
  301. pageSize: 10000,
  302. tenantId: useUserStore().user.tenantId,
  303. },
  304. },
  305. },
  306. {
  307. type: "slot",
  308. slotName: "person",
  309. label: "客户联系人",
  310. },
  311. ];
  312. });
  313. let rules = ref({
  314. name: [{ required: true, message: "请输入客户名称", trigger: "blur" }],
  315. countryId: [{ required: true, message: "请选择国家", trigger: "change" }],
  316. provinceId: [{ required: true, message: "请选择省/州", trigger: "change" }],
  317. // cityId: [{ required: true, message: "请选择城市", trigger: "change" }],
  318. source: [{ required: true, message: "请选择客户来源", trigger: "change" }],
  319. status: [{ required: true, message: "请选择类型", trigger: "change" }],
  320. });
  321. const formConfigPerson = computed(() => {
  322. return [
  323. {
  324. type: "input",
  325. prop: "name",
  326. label: "联系人名称",
  327. required: true,
  328. itemWidth: 100,
  329. itemType: "text",
  330. },
  331. {
  332. type: "input",
  333. prop: "phone",
  334. label: "联系人电话",
  335. required: true,
  336. itemWidth: 100,
  337. itemType: "text",
  338. },
  339. ];
  340. });
  341. const configPerson = computed(() => {
  342. return [
  343. {
  344. attrs: {
  345. label: "联系人名称",
  346. prop: "name",
  347. width: 150,
  348. },
  349. },
  350. {
  351. attrs: {
  352. label: "联系人电话",
  353. prop: "phone",
  354. width: 440,
  355. },
  356. },
  357. {
  358. attrs: {
  359. label: "操作",
  360. width: "100",
  361. align: "center",
  362. },
  363. renderHTML(row) {
  364. return [
  365. {
  366. attrs: {
  367. label: "删除",
  368. type: "primary",
  369. text: true,
  370. },
  371. el: "button",
  372. click() {
  373. formData.data.customerUserList.splice(row.$index, 1);
  374. },
  375. },
  376. ];
  377. },
  378. },
  379. ];
  380. });
  381. let rulesPerson = ref({
  382. name: [{ required: true, message: "请输入联系人名称", trigger: "blur" }],
  383. phone: [{ required: true, message: "请输入联系人电话", trigger: "blur" }],
  384. });
  385. const submit = ref(null);
  386. const person = ref(null);
  387. const getList = async (req) => {
  388. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  389. loading.value = true;
  390. proxy.post("/customer/privateSeaPage", sourceList.value.pagination).then((res) => {
  391. sourceList.value.data = res.rows;
  392. sourceList.value.pagination.total = res.total;
  393. setTimeout(() => {
  394. loading.value = false;
  395. }, 200);
  396. });
  397. };
  398. const openModal = () => {
  399. modalType.value = "add";
  400. formData.data = {
  401. countryId: "China",
  402. };
  403. getCityData(formData.data.countryId, "20");
  404. loadingOperation.value = false;
  405. dialogVisible.value = true;
  406. };
  407. const update = (row) => {
  408. modalType.value = "edit";
  409. loadingOperation.value = true;
  410. proxy.post("/customer/detail", { id: row.id }).then((res) => {
  411. res.customerUserList = res.customerUserList.map((item) => {
  412. return {
  413. name: item.name,
  414. phone: item.phone,
  415. customerId: item.customerId,
  416. };
  417. });
  418. formData.data = res;
  419. getCityData(formData.data.countryId, "20");
  420. getCityData(formData.data.provinceId, "30");
  421. loadingOperation.value = false;
  422. });
  423. dialogVisible.value = true;
  424. };
  425. const countryData = ref([]);
  426. const provinceData = ref([]);
  427. const cityData = ref([]);
  428. const getCityData = (id, type, isChange) => {
  429. proxy.post("/areaInfo/list", { parentId: id }).then((res) => {
  430. if (type === "20") {
  431. provinceData.value = res;
  432. if (isChange) {
  433. formData.data.provinceId = "";
  434. formData.data.cityId = "";
  435. }
  436. } else if (type === "30") {
  437. cityData.value = res;
  438. if (isChange) {
  439. formData.data.cityId = "";
  440. }
  441. } else {
  442. countryData.value = res;
  443. }
  444. });
  445. };
  446. getCityData("0");
  447. const clickAddPerson = () => {
  448. formPerson.data = {};
  449. openPerson.value = true;
  450. };
  451. const submitPerson = () => {
  452. person.value.handleSubmit(() => {
  453. if (formData.data.customerUserList && formData.data.customerUserList.length > 0) {
  454. formData.data.customerUserList.push({
  455. name: formPerson.data.name,
  456. phone: formPerson.data.phone,
  457. });
  458. } else {
  459. formData.data.customerUserList = [
  460. {
  461. name: formPerson.data.name,
  462. phone: formPerson.data.phone,
  463. },
  464. ];
  465. }
  466. openPerson.value = false;
  467. });
  468. };
  469. const submitForm = () => {
  470. submit.value.handleSubmit(() => {
  471. if (formData.data.customerUserList && formData.data.customerUserList.length > 0) {
  472. submitLoading.value = true;
  473. proxy.post("/customer/" + modalType.value, formData.data).then(
  474. () => {
  475. ElMessage({
  476. message: modalType.value == "add" ? "添加成功" : "编辑成功",
  477. type: "success",
  478. });
  479. dialogVisible.value = false;
  480. submitLoading.value = false;
  481. getList();
  482. },
  483. (err) => {
  484. console.log(err);
  485. submitLoading.value = false;
  486. }
  487. );
  488. } else {
  489. ElMessage("请添加客户联系人");
  490. }
  491. });
  492. };
  493. const getDict = () => {
  494. proxy
  495. .post("/dictTenantData/page", {
  496. pageNum: 1,
  497. pageSize: 999,
  498. dictCode: "customer_source",
  499. tenantId: useUserStore().user.tenantId,
  500. })
  501. .then((res) => {
  502. customerSource.value = res.rows.map((item) => {
  503. return {
  504. label: item.dictValue,
  505. value: item.dictKey,
  506. };
  507. });
  508. });
  509. proxy
  510. .post("/dictTenantData/page", {
  511. pageNum: 1,
  512. pageSize: 999,
  513. dictCode: "customer_status",
  514. tenantId: useUserStore().user.tenantId,
  515. })
  516. .then((res) => {
  517. customerStatus.value = res.rows.map((item) => {
  518. return {
  519. label: item.dictValue,
  520. value: item.dictKey,
  521. };
  522. });
  523. });
  524. proxy.get("/tenantUser/list", { pageNum: 1, pageSize: 10000, tenantId: useUserStore().user.tenantId }).then((res) => {
  525. userList.value = res.rows.map((item) => {
  526. return {
  527. label: item.nickName,
  528. value: item.userId,
  529. };
  530. });
  531. });
  532. };
  533. getDict();
  534. getList();
  535. </script>
  536. <style lang="scss" scoped>
  537. .tenant {
  538. padding: 20px;
  539. }
  540. </style>