Browse Source

客户档案-公海-私海

lxf 2 years ago
parent
commit
310584b22e

+ 583 - 0
src/views/customer/file/index.vue

@@ -0,0 +1,583 @@
+<template>
+  <div class="tenant">
+    <div class="content">
+      <byTable
+        :source="sourceList.data"
+        :pagination="sourceList.pagination"
+        :config="config"
+        :loading="loading"
+        :selectConfig="selectConfig"
+        highlight-current-row
+        :action-list="[
+          {
+            text: '添加用户',
+            action: () => openModal('add'),
+          },
+        ]"
+        @get-list="getList">
+        <template #fileSlot="{ item }">
+          <span>{{ item.countryName }}</span>
+          <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
+          <span v-if="item.cityName"> ,{{ item.cityName }}</span>
+        </template>
+      </byTable>
+    </div>
+
+    <el-dialog :title="modalType == 'add' ? '新增' : '编辑'" v-model="dialogVisible" width="800" v-loading="loadingOperation">
+      <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
+        <template #address>
+          <el-row :gutter="10" style="width: 100%; margin-left: -15px">
+            <el-col :span="8">
+              <el-form-item prop="countryId">
+                <el-select v-model="formData.data.countryId" placeholder="国家" @change="(val) => getCityData(val, '20', true)">
+                  <el-option v-for="item in countryData" :label="item.chineseName" :value="item.id"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item prop="provinceId">
+                <el-select v-model="formData.data.provinceId" placeholder="省/洲" @change="(val) => getCityData(val, '30', true)">
+                  <el-option v-for="item in provinceData" :label="item.name" :value="item.id"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item prop="cityId">
+                <el-select v-model="formData.data.cityId" placeholder="城市">
+                  <el-option v-for="item in cityData" :label="item.name" :value="item.id"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row style="margin-top: 20px; width: 100%; margin-left: -10px">
+            <el-col :span="24">
+              <el-form-item prop="address">
+                <el-input v-model="formData.data.address" type="textarea"> </el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <template #person>
+          <div>
+            <el-button type="primary" @click="clickAddPerson"> 添加 </el-button>
+            <byTable :source="formData.data.customerUserList" :config="configPerson" hideSearch hidePagination> </byTable>
+          </div>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button type="primary" @click="submitForm('submit')" size="large" :loading="submitLoading"> 确 定 </el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog title="添加联系人" v-model="openPerson" width="400" v-loading="openPerson">
+      <byForm :formConfig="formConfigPerson" :formOption="formOption" v-model="formPerson.data" :rules="rulesPerson" ref="person"> </byForm>
+      <template #footer>
+        <el-button @click="openPerson = false" size="large">取 消</el-button>
+        <el-button type="primary" @click="submitPerson('person')" size="large"> 确 定 </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ElMessage, ElMessageBox } from "element-plus";
+import byTable from "@/components/byTable/index";
+import byForm from "@/components/byForm/index";
+import { computed, ref } from "vue";
+import useUserStore from "@/store/modules/user";
+
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+const loadingOperation = ref(false);
+const submitLoading = ref(false);
+const openPerson = ref(false);
+const customerSource = ref([]);
+const customerStatus = ref([]);
+const userList = ref([]);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    status: "",
+    source: "",
+    type: "",
+  },
+});
+const selectConfig = computed(() => {
+  return [
+    {
+      label: "客户状态",
+      prop: "type",
+      data: [
+        {
+          label: "公海",
+          value: "0",
+        },
+        {
+          label: "私海",
+          value: "1",
+        },
+      ],
+    },
+    {
+      label: "客户来源",
+      prop: "source",
+      data: customerSource.value,
+    },
+    {
+      label: "客户类型",
+      prop: "status",
+      data: customerStatus.value,
+    },
+  ];
+});
+const config = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "客户名称",
+        prop: "name",
+      },
+    },
+
+    {
+      attrs: {
+        label: "所在城市",
+        slot: "address",
+        width: 200,
+      },
+    },
+    {
+      attrs: {
+        label: "客户代码",
+        prop: "customerCode",
+        width: 140,
+      },
+    },
+    {
+      attrs: {
+        label: "客户来源",
+        prop: "source",
+        width: 140,
+      },
+      render(type) {
+        let data = customerSource.value.filter((item) => item.value == type);
+        if (data && data.length > 0) {
+          return data[0].label;
+        } else {
+          return "";
+        }
+      },
+    },
+    {
+      attrs: {
+        label: "客户类型",
+        prop: "status",
+        width: 140,
+      },
+      render(type) {
+        let data = customerStatus.value.filter((item) => item.value == type);
+        if (data && data.length > 0) {
+          return data[0].label;
+        } else {
+          return "";
+        }
+      },
+    },
+    {
+      attrs: {
+        label: "业务员",
+        prop: "userId",
+        width: 140,
+      },
+      render(type) {
+        let data = userList.value.filter((item) => item.value == type);
+        if (data && data.length > 0) {
+          return data[0].label;
+        } else {
+          return "";
+        }
+      },
+    },
+    {
+      attrs: {
+        label: "操作",
+        width: "160",
+        align: "center",
+      },
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "修改",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              update(row);
+            },
+          },
+          {
+            attrs: {
+              label: "删除",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              ElMessageBox.confirm("此操作将永久删除该数据, 是否继续?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+                type: "warning",
+              }).then(() => {
+                proxy
+                  .post("/customer/delete", {
+                    id: row.id,
+                  })
+                  .then(() => {
+                    ElMessage({
+                      message: "删除成功",
+                      type: "success",
+                    });
+                    getList();
+                  });
+              });
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+let modalType = ref("add");
+let dialogVisible = ref(false);
+let formData = reactive({
+  data: {
+    countryId: "China",
+  },
+});
+let formPerson = reactive({
+  data: {},
+});
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+  rules: [],
+});
+const formConfig = computed(() => {
+  return [
+    {
+      type: "input",
+      prop: "name",
+      label: "客户名称",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+    {
+      type: "slot",
+      slotName: "address",
+      label: "详细地址",
+    },
+    {
+      type: "input",
+      prop: "customerCode",
+      label: "客户代码",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+    {
+      type: "select",
+      label: "客户来源",
+      prop: "source",
+      itemWidth: 50,
+      isLoad: {
+        url: "/dictTenantData/page",
+        labelKey: "dictValue",
+        labelVal: "dictKey",
+        method: "post",
+        req: {
+          pageNum: 1,
+          pageSize: 999,
+          dictCode: "customer_source",
+          tenantId: useUserStore().user.tenantId,
+        },
+        resUrl: "rows",
+      },
+    },
+    {
+      type: "select",
+      label: "客户类型",
+      prop: "status",
+      itemWidth: 50,
+      isLoad: {
+        url: "/dictTenantData/page",
+        labelKey: "dictValue",
+        labelVal: "dictKey",
+        method: "post",
+        req: {
+          pageNum: 1,
+          pageSize: 999,
+          dictCode: "customer_status",
+          tenantId: useUserStore().user.tenantId,
+        },
+        resUrl: "rows",
+      },
+    },
+    {
+      type: "select",
+      label: "业务员",
+      prop: "userId",
+      itemWidth: 100,
+      isLoad: {
+        url: "/tenantUser/list?pageNum=1&pageSize=10000",
+        labelKey: "userName",
+        labelVal: "userId",
+        method: "get",
+        resUrl: "rows",
+      },
+    },
+    {
+      type: "slot",
+      slotName: "person",
+      label: "客户联系人",
+    },
+  ];
+});
+let rules = ref({
+  name: [{ required: true, message: "请输入客户名称", trigger: "blur" }],
+  countryId: [{ required: true, message: "请选择国家", trigger: "change" }],
+  provinceId: [{ required: true, message: "请选择省/州", trigger: "change" }],
+  // cityId: [{ required: true, message: "请选择城市", trigger: "change" }],
+  source: [{ required: true, message: "请选择客户来源", trigger: "change" }],
+  status: [{ required: true, message: "请选择类型", trigger: "change" }],
+});
+const formConfigPerson = computed(() => {
+  return [
+    {
+      type: "input",
+      prop: "name",
+      label: "联系人名称",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+    {
+      type: "input",
+      prop: "phone",
+      label: "联系人电话",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+  ];
+});
+const configPerson = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "联系人名称",
+        prop: "name",
+        width: 150,
+      },
+    },
+    {
+      attrs: {
+        label: "联系人电话",
+        prop: "phone",
+        width: 440,
+      },
+    },
+    {
+      attrs: {
+        label: "操作",
+        width: "100",
+        align: "center",
+      },
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "删除",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              formData.data.customerUserList.splice(row.$index, 1);
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+
+let rulesPerson = ref({
+  name: [{ required: true, message: "请输入联系人名称", trigger: "blur" }],
+  phone: [{ required: true, message: "请输入联系人电话", trigger: "blur" }],
+});
+const submit = ref(null);
+const person = ref(null);
+const getList = async (req) => {
+  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  loading.value = true;
+  proxy.post("/customer/page", sourceList.value.pagination).then((res) => {
+    sourceList.value.data = res.rows;
+    sourceList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loading.value = false;
+    }, 200);
+  });
+};
+const openModal = () => {
+  modalType.value = "add";
+  formData.data = {
+    countryId: "China",
+  };
+  getCityData(formData.data.countryId, "20");
+  loadingOperation.value = false;
+  dialogVisible.value = true;
+};
+const update = (row) => {
+  modalType.value = "edit";
+  loadingOperation.value = true;
+  proxy.post("/customer/detail", { id: row.id }).then((res) => {
+    res.customerUserList = res.customerUserList.map((item) => {
+      return {
+        name: item.name,
+        phone: item.phone,
+        customerId: item.customerId,
+      };
+    });
+    formData.data = res;
+    getCityData(formData.data.countryId, "20");
+    getCityData(formData.data.provinceId, "30");
+    loadingOperation.value = false;
+  });
+  dialogVisible.value = true;
+};
+const countryData = ref([]);
+const provinceData = ref([]);
+const cityData = ref([]);
+const getCityData = (id, type, isChange) => {
+  proxy.post("/areaInfo/list", { parentId: id }).then((res) => {
+    if (type === "20") {
+      provinceData.value = res;
+      if (isChange) {
+        formData.data.provinceId = "";
+        formData.data.cityId = "";
+      }
+    } else if (type === "30") {
+      cityData.value = res;
+      if (isChange) {
+        formData.data.cityId = "";
+      }
+    } else {
+      countryData.value = res;
+    }
+  });
+};
+getCityData("0");
+const clickAddPerson = () => {
+  formPerson.data = {};
+  openPerson.value = true;
+};
+const submitPerson = () => {
+  person.value.handleSubmit(() => {
+    if (formData.data.customerUserList && formData.data.customerUserList.length > 0) {
+      formData.data.customerUserList.push({
+        name: formPerson.data.name,
+        phone: formPerson.data.phone,
+      });
+    } else {
+      formData.data.customerUserList = [
+        {
+          name: formPerson.data.name,
+          phone: formPerson.data.phone,
+        },
+      ];
+    }
+    openPerson.value = false;
+  });
+};
+const submitForm = () => {
+  submit.value.handleSubmit(() => {
+    if (formData.data.customerUserList && formData.data.customerUserList.length > 0) {
+      submitLoading.value = true;
+      proxy.post("/customer/" + modalType.value, formData.data).then(
+        () => {
+          ElMessage({
+            message: modalType.value == "add" ? "添加成功" : "编辑成功",
+            type: "success",
+          });
+          dialogVisible.value = false;
+          submitLoading.value = false;
+          getList();
+        },
+        (err) => {
+          console.log(err);
+          submitLoading.value = false;
+        }
+      );
+    } else {
+      ElMessage("请添加客户联系人");
+    }
+  });
+};
+const getDict = () => {
+  proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      dictCode: "customer_source",
+      tenantId: useUserStore().user.tenantId,
+    })
+    .then((res) => {
+      customerSource.value = res.rows.map((item) => {
+        return {
+          label: item.dictValue,
+          value: item.dictKey,
+        };
+      });
+    });
+  proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      dictCode: "customer_status",
+      tenantId: useUserStore().user.tenantId,
+    })
+    .then((res) => {
+      customerStatus.value = res.rows.map((item) => {
+        return {
+          label: item.dictValue,
+          value: item.dictKey,
+        };
+      });
+    });
+  proxy.get("/tenantUser/list?pageNum=1&pageSize=10000", {}).then((res) => {
+    userList.value = res.rows.map((item) => {
+      return {
+        label: item.userName,
+        value: item.userId,
+      };
+    });
+  });
+};
+getDict();
+getList();
+</script>
+
+<style lang="scss" scoped>
+.tenant {
+  padding: 20px;
+}
+</style>

+ 517 - 0
src/views/customer/highseas/index.vue

@@ -0,0 +1,517 @@
+<template>
+  <div class="tenant">
+    <div class="content">
+      <byTable
+        :source="sourceList.data"
+        :pagination="sourceList.pagination"
+        :config="config"
+        :loading="loading"
+        :selectConfig="selectConfig"
+        highlight-current-row
+        :action-list="[
+          {
+            text: '添加用户',
+            action: () => openModal('add'),
+          },
+        ]"
+        @get-list="getList">
+        <template #fileSlot="{ item }">
+          <span>{{ item.countryName }}</span>
+          <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
+          <span v-if="item.cityName"> ,{{ item.cityName }}</span>
+        </template>
+      </byTable>
+    </div>
+
+    <el-dialog :title="modalType == 'add' ? '新增' : '编辑'" v-model="dialogVisible" width="800" v-loading="loadingOperation">
+      <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
+        <template #address>
+          <el-row :gutter="10" style="width: 100%; margin-left: -15px">
+            <el-col :span="8">
+              <el-form-item prop="countryId">
+                <el-select v-model="formData.data.countryId" placeholder="国家" @change="(val) => getCityData(val, '20', true)">
+                  <el-option v-for="item in countryData" :label="item.chineseName" :value="item.id"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item prop="provinceId">
+                <el-select v-model="formData.data.provinceId" placeholder="省/洲" @change="(val) => getCityData(val, '30', true)">
+                  <el-option v-for="item in provinceData" :label="item.name" :value="item.id"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item prop="cityId">
+                <el-select v-model="formData.data.cityId" placeholder="城市">
+                  <el-option v-for="item in cityData" :label="item.name" :value="item.id"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row style="margin-top: 20px; width: 100%; margin-left: -10px">
+            <el-col :span="24">
+              <el-form-item prop="address">
+                <el-input v-model="formData.data.address" type="textarea"> </el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <template #person>
+          <div>
+            <el-button type="primary" @click="clickAddPerson"> 添加 </el-button>
+            <byTable :source="formData.data.customerUserList" :config="configPerson" hideSearch hidePagination> </byTable>
+          </div>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button type="primary" @click="submitForm('submit')" size="large" :loading="submitLoading"> 确 定 </el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog title="添加联系人" v-model="openPerson" width="400" v-loading="openPerson">
+      <byForm :formConfig="formConfigPerson" :formOption="formOption" v-model="formPerson.data" :rules="rulesPerson" ref="person"> </byForm>
+      <template #footer>
+        <el-button @click="openPerson = false" size="large">取 消</el-button>
+        <el-button type="primary" @click="submitPerson('person')" size="large"> 确 定 </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ElMessage, ElMessageBox } from "element-plus";
+import byTable from "@/components/byTable/index";
+import byForm from "@/components/byForm/index";
+import { computed, ref } from "vue";
+import useUserStore from "@/store/modules/user";
+
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+const loadingOperation = ref(false);
+const submitLoading = ref(false);
+const openPerson = ref(false);
+const customerSource = ref([]);
+const customerStatus = ref([]);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    status: "",
+    source: "",
+    type: "0",
+  },
+});
+const selectConfig = computed(() => {
+  return [
+    {
+      label: "客户来源",
+      prop: "source",
+      data: customerSource.value,
+    },
+    {
+      label: "客户类型",
+      prop: "status",
+      data: customerStatus.value,
+    },
+  ];
+});
+const config = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "客户名称",
+        prop: "name",
+      },
+    },
+
+    {
+      attrs: {
+        label: "所在城市",
+        slot: "address",
+        width: 200,
+      },
+    },
+    {
+      attrs: {
+        label: "客户代码",
+        prop: "customerCode",
+        width: 140,
+      },
+    },
+    {
+      attrs: {
+        label: "客户来源",
+        prop: "source",
+        width: 140,
+      },
+      render(type) {
+        let data = customerSource.value.filter((item) => item.value == type);
+        if (data && data.length > 0) {
+          return data[0].label;
+        } else {
+          return "";
+        }
+      },
+    },
+    {
+      attrs: {
+        label: "客户类型",
+        prop: "status",
+        width: 140,
+      },
+      render(type) {
+        let data = customerStatus.value.filter((item) => item.value == type);
+        if (data && data.length > 0) {
+          return data[0].label;
+        } else {
+          return "";
+        }
+      },
+    },
+    {
+      attrs: {
+        label: "操作",
+        width: "100",
+        align: "center",
+      },
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "认领客户",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              ElMessageBox.confirm("是否确定认领该客户?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+                type: "warning",
+              }).then(() => {
+                proxy
+                  .post("/customer/CustomerAllocation", {
+                    id: row.id,
+                    userId: useUserStore().user.userId,
+                  })
+                  .then(() => {
+                    ElMessage({
+                      message: "认领成功",
+                      type: "success",
+                    });
+                    getList();
+                  });
+              });
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+let modalType = ref("add");
+let dialogVisible = ref(false);
+let formData = reactive({
+  data: {
+    countryId: "China",
+  },
+});
+let formPerson = reactive({
+  data: {},
+});
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+  rules: [],
+});
+const formConfig = computed(() => {
+  return [
+    {
+      type: "input",
+      prop: "name",
+      label: "客户名称",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+    {
+      type: "slot",
+      slotName: "address",
+      label: "详细地址",
+    },
+    {
+      type: "input",
+      prop: "customerCode",
+      label: "客户代码",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+    {
+      type: "select",
+      label: "客户来源",
+      prop: "source",
+      itemWidth: 50,
+      isLoad: {
+        url: "/dictTenantData/page",
+        labelKey: "dictValue",
+        labelVal: "dictKey",
+        method: "post",
+        req: {
+          pageNum: 1,
+          pageSize: 999,
+          dictCode: "customer_source",
+          tenantId: useUserStore().user.tenantId,
+        },
+        resUrl: "rows",
+      },
+    },
+    {
+      type: "select",
+      label: "客户类型",
+      prop: "status",
+      itemWidth: 50,
+      isLoad: {
+        url: "/dictTenantData/page",
+        labelKey: "dictValue",
+        labelVal: "dictKey",
+        method: "post",
+        req: {
+          pageNum: 1,
+          pageSize: 999,
+          dictCode: "customer_status",
+          tenantId: useUserStore().user.tenantId,
+        },
+        resUrl: "rows",
+      },
+    },
+    {
+      type: "select",
+      label: "业务员",
+      prop: "userId",
+      itemWidth: 100,
+      isLoad: {
+        url: "/tenantUser/list?pageNum=1&pageSize=10000",
+        labelKey: "userName",
+        labelVal: "userId",
+        method: "get",
+        resUrl: "rows",
+      },
+    },
+    {
+      type: "slot",
+      slotName: "person",
+      label: "客户联系人",
+    },
+  ];
+});
+let rules = ref({
+  name: [{ required: true, message: "请输入客户名称", trigger: "blur" }],
+  countryId: [{ required: true, message: "请选择国家", trigger: "change" }],
+  provinceId: [{ required: true, message: "请选择省/州", trigger: "change" }],
+  // cityId: [{ required: true, message: "请选择城市", trigger: "change" }],
+  source: [{ required: true, message: "请选择客户来源", trigger: "change" }],
+  status: [{ required: true, message: "请选择类型", trigger: "change" }],
+});
+const formConfigPerson = computed(() => {
+  return [
+    {
+      type: "input",
+      prop: "name",
+      label: "联系人名称",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+    {
+      type: "input",
+      prop: "phone",
+      label: "联系人电话",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+  ];
+});
+const configPerson = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "联系人名称",
+        prop: "name",
+        width: 150,
+      },
+    },
+    {
+      attrs: {
+        label: "联系人电话",
+        prop: "phone",
+        width: 440,
+      },
+    },
+    {
+      attrs: {
+        label: "操作",
+        width: "100",
+        align: "center",
+      },
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "删除",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              formData.data.customerUserList.splice(row.$index, 1);
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+
+let rulesPerson = ref({
+  name: [{ required: true, message: "请输入联系人名称", trigger: "blur" }],
+  phone: [{ required: true, message: "请输入联系人电话", trigger: "blur" }],
+});
+const submit = ref(null);
+const person = ref(null);
+const getList = async (req) => {
+  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  loading.value = true;
+  proxy.post("/customer/page", sourceList.value.pagination).then((res) => {
+    sourceList.value.data = res.rows;
+    sourceList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loading.value = false;
+    }, 200);
+  });
+};
+const openModal = () => {
+  modalType.value = "add";
+  formData.data = {
+    countryId: "China",
+  };
+  getCityData(formData.data.countryId, "20");
+  loadingOperation.value = false;
+  dialogVisible.value = true;
+};
+const countryData = ref([]);
+const provinceData = ref([]);
+const cityData = ref([]);
+const getCityData = (id, type, isChange) => {
+  proxy.post("/areaInfo/list", { parentId: id }).then((res) => {
+    if (type === "20") {
+      provinceData.value = res;
+      if (isChange) {
+        formData.data.provinceId = "";
+        formData.data.cityId = "";
+      }
+    } else if (type === "30") {
+      cityData.value = res;
+      if (isChange) {
+        formData.data.cityId = "";
+      }
+    } else {
+      countryData.value = res;
+    }
+  });
+};
+getCityData("0");
+const clickAddPerson = () => {
+  formPerson.data = {};
+  openPerson.value = true;
+};
+const submitPerson = () => {
+  person.value.handleSubmit(() => {
+    if (formData.data.customerUserList && formData.data.customerUserList.length > 0) {
+      formData.data.customerUserList.push({
+        name: formPerson.data.name,
+        phone: formPerson.data.phone,
+      });
+    } else {
+      formData.data.customerUserList = [
+        {
+          name: formPerson.data.name,
+          phone: formPerson.data.phone,
+        },
+      ];
+    }
+    openPerson.value = false;
+  });
+};
+const submitForm = () => {
+  submit.value.handleSubmit(() => {
+    if (formData.data.customerUserList && formData.data.customerUserList.length > 0) {
+      submitLoading.value = true;
+      proxy.post("/customer/" + modalType.value, formData.data).then(
+        () => {
+          ElMessage({
+            message: modalType.value == "add" ? "添加成功" : "编辑成功",
+            type: "success",
+          });
+          dialogVisible.value = false;
+          submitLoading.value = false;
+          getList();
+        },
+        (err) => {
+          console.log(err);
+          submitLoading.value = false;
+        }
+      );
+    } else {
+      ElMessage("请添加客户联系人");
+    }
+  });
+};
+const getDict = () => {
+  proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      dictCode: "customer_source",
+      tenantId: useUserStore().user.tenantId,
+    })
+    .then((res) => {
+      customerSource.value = res.rows.map((item) => {
+        return {
+          label: item.dictValue,
+          value: item.dictKey,
+        };
+      });
+    });
+  proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      dictCode: "customer_status",
+      tenantId: useUserStore().user.tenantId,
+    })
+    .then((res) => {
+      customerStatus.value = res.rows.map((item) => {
+        return {
+          label: item.dictValue,
+          value: item.dictKey,
+        };
+      });
+    });
+};
+getDict();
+getList();
+</script>
+
+<style lang="scss" scoped>
+.tenant {
+  padding: 20px;
+}
+</style>

+ 543 - 0
src/views/customer/privatesea/index.vue

@@ -0,0 +1,543 @@
+<template>
+  <div class="tenant">
+    <div class="content">
+      <byTable
+        :source="sourceList.data"
+        :pagination="sourceList.pagination"
+        :config="config"
+        :loading="loading"
+        :selectConfig="selectConfig"
+        highlight-current-row
+        :action-list="[
+          {
+            text: '添加用户',
+            action: () => openModal('add'),
+          },
+        ]"
+        @get-list="getList">
+        <template #fileSlot="{ item }">
+          <span>{{ item.countryName }}</span>
+          <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
+          <span v-if="item.cityName"> ,{{ item.cityName }}</span>
+        </template>
+      </byTable>
+    </div>
+
+    <el-dialog :title="modalType == 'add' ? '新增' : '编辑'" v-model="dialogVisible" width="800" v-loading="loadingOperation">
+      <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
+        <template #address>
+          <el-row :gutter="10" style="width: 100%; margin-left: -15px">
+            <el-col :span="8">
+              <el-form-item prop="countryId">
+                <el-select v-model="formData.data.countryId" placeholder="国家" @change="(val) => getCityData(val, '20', true)">
+                  <el-option v-for="item in countryData" :label="item.chineseName" :value="item.id"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item prop="provinceId">
+                <el-select v-model="formData.data.provinceId" placeholder="省/洲" @change="(val) => getCityData(val, '30', true)">
+                  <el-option v-for="item in provinceData" :label="item.name" :value="item.id"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item prop="cityId">
+                <el-select v-model="formData.data.cityId" placeholder="城市">
+                  <el-option v-for="item in cityData" :label="item.name" :value="item.id"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row style="margin-top: 20px; width: 100%; margin-left: -10px">
+            <el-col :span="24">
+              <el-form-item prop="address">
+                <el-input v-model="formData.data.address" type="textarea"> </el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <template #person>
+          <div>
+            <el-button type="primary" @click="clickAddPerson"> 添加 </el-button>
+            <byTable :source="formData.data.customerUserList" :config="configPerson" hideSearch hidePagination> </byTable>
+          </div>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button type="primary" @click="submitForm('submit')" size="large" :loading="submitLoading"> 确 定 </el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog title="添加联系人" v-model="openPerson" width="400" v-loading="openPerson">
+      <byForm :formConfig="formConfigPerson" :formOption="formOption" v-model="formPerson.data" :rules="rulesPerson" ref="person"> </byForm>
+      <template #footer>
+        <el-button @click="openPerson = false" size="large">取 消</el-button>
+        <el-button type="primary" @click="submitPerson('person')" size="large"> 确 定 </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ElMessage, ElMessageBox } from "element-plus";
+import byTable from "@/components/byTable/index";
+import byForm from "@/components/byForm/index";
+import { computed, ref } from "vue";
+import useUserStore from "@/store/modules/user";
+
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+const loadingOperation = ref(false);
+const submitLoading = ref(false);
+const openPerson = ref(false);
+const customerSource = ref([]);
+const customerStatus = ref([]);
+const userList = ref([]);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    status: "",
+    source: "",
+  },
+});
+const selectConfig = computed(() => {
+  return [
+    {
+      label: "客户来源",
+      prop: "source",
+      data: customerSource.value,
+    },
+    {
+      label: "客户类型",
+      prop: "status",
+      data: customerStatus.value,
+    },
+  ];
+});
+const config = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "客户名称",
+        prop: "name",
+      },
+    },
+
+    {
+      attrs: {
+        label: "所在城市",
+        slot: "address",
+        width: 200,
+      },
+    },
+    {
+      attrs: {
+        label: "客户代码",
+        prop: "customerCode",
+        width: 140,
+      },
+    },
+    {
+      attrs: {
+        label: "客户来源",
+        prop: "source",
+        width: 140,
+      },
+      render(type) {
+        let data = customerSource.value.filter((item) => item.value == type);
+        if (data && data.length > 0) {
+          return data[0].label;
+        } else {
+          return "";
+        }
+      },
+    },
+    {
+      attrs: {
+        label: "客户类型",
+        prop: "status",
+        width: 140,
+      },
+      render(type) {
+        let data = customerStatus.value.filter((item) => item.value == type);
+        if (data && data.length > 0) {
+          return data[0].label;
+        } else {
+          return "";
+        }
+      },
+    },
+    {
+      attrs: {
+        label: "操作",
+        width: "100",
+        align: "center",
+      },
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "退回公海",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              ElMessageBox.confirm("是否确定将该客户退回公海?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+                type: "warning",
+              }).then(() => {
+                proxy
+                  .post("/customer/CustomerAllocation", {
+                    id: row.id,
+                    userId: '',
+                  })
+                  .then(() => {
+                    ElMessage({
+                      message: "退回成功",
+                      type: "success",
+                    });
+                    getList();
+                  });
+              });
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+let modalType = ref("add");
+let dialogVisible = ref(false);
+let formData = reactive({
+  data: {
+    countryId: "China",
+  },
+});
+let formPerson = reactive({
+  data: {},
+});
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+  rules: [],
+});
+const formConfig = computed(() => {
+  return [
+    {
+      type: "input",
+      prop: "name",
+      label: "客户名称",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+    {
+      type: "slot",
+      slotName: "address",
+      label: "详细地址",
+    },
+    {
+      type: "input",
+      prop: "customerCode",
+      label: "客户代码",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+    {
+      type: "select",
+      label: "客户来源",
+      prop: "source",
+      itemWidth: 50,
+      isLoad: {
+        url: "/dictTenantData/page",
+        labelKey: "dictValue",
+        labelVal: "dictKey",
+        method: "post",
+        req: {
+          pageNum: 1,
+          pageSize: 999,
+          dictCode: "customer_source",
+          tenantId: useUserStore().user.tenantId,
+        },
+        resUrl: "rows",
+      },
+    },
+    {
+      type: "select",
+      label: "客户类型",
+      prop: "status",
+      itemWidth: 50,
+      isLoad: {
+        url: "/dictTenantData/page",
+        labelKey: "dictValue",
+        labelVal: "dictKey",
+        method: "post",
+        req: {
+          pageNum: 1,
+          pageSize: 999,
+          dictCode: "customer_status",
+          tenantId: useUserStore().user.tenantId,
+        },
+        resUrl: "rows",
+      },
+    },
+    {
+      type: "select",
+      label: "业务员",
+      prop: "userId",
+      itemWidth: 100,
+      isLoad: {
+        url: "/tenantUser/list?pageNum=1&pageSize=10000",
+        labelKey: "userName",
+        labelVal: "userId",
+        method: "get",
+        resUrl: "rows",
+      },
+    },
+    {
+      type: "slot",
+      slotName: "person",
+      label: "客户联系人",
+    },
+  ];
+});
+let rules = ref({
+  name: [{ required: true, message: "请输入客户名称", trigger: "blur" }],
+  countryId: [{ required: true, message: "请选择国家", trigger: "change" }],
+  provinceId: [{ required: true, message: "请选择省/州", trigger: "change" }],
+  // cityId: [{ required: true, message: "请选择城市", trigger: "change" }],
+  source: [{ required: true, message: "请选择客户来源", trigger: "change" }],
+  status: [{ required: true, message: "请选择类型", trigger: "change" }],
+});
+const formConfigPerson = computed(() => {
+  return [
+    {
+      type: "input",
+      prop: "name",
+      label: "联系人名称",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+    {
+      type: "input",
+      prop: "phone",
+      label: "联系人电话",
+      required: true,
+      itemWidth: 100,
+      itemType: "text",
+    },
+  ];
+});
+const configPerson = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "联系人名称",
+        prop: "name",
+        width: 150,
+      },
+    },
+    {
+      attrs: {
+        label: "联系人电话",
+        prop: "phone",
+        width: 440,
+      },
+    },
+    {
+      attrs: {
+        label: "操作",
+        width: "100",
+        align: "center",
+      },
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "删除",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              formData.data.customerUserList.splice(row.$index, 1);
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+
+let rulesPerson = ref({
+  name: [{ required: true, message: "请输入联系人名称", trigger: "blur" }],
+  phone: [{ required: true, message: "请输入联系人电话", trigger: "blur" }],
+});
+const submit = ref(null);
+const person = ref(null);
+const getList = async (req) => {
+  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  loading.value = true;
+  proxy.post("/customer/privateSeaPage", sourceList.value.pagination).then((res) => {
+    sourceList.value.data = res.rows;
+    sourceList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loading.value = false;
+    }, 200);
+  });
+};
+const openModal = () => {
+  modalType.value = "add";
+  formData.data = {
+    countryId: "China",
+  };
+  getCityData(formData.data.countryId, "20");
+  loadingOperation.value = false;
+  dialogVisible.value = true;
+};
+const update = (row) => {
+  modalType.value = "edit";
+  loadingOperation.value = true;
+  proxy.post("/customer/detail", { id: row.id }).then((res) => {
+    res.customerUserList = res.customerUserList.map((item) => {
+      return {
+        name: item.name,
+        phone: item.phone,
+        customerId: item.customerId,
+      };
+    });
+    formData.data = res;
+    getCityData(formData.data.countryId, "20");
+    getCityData(formData.data.provinceId, "30");
+    loadingOperation.value = false;
+  });
+  dialogVisible.value = true;
+};
+const countryData = ref([]);
+const provinceData = ref([]);
+const cityData = ref([]);
+const getCityData = (id, type, isChange) => {
+  proxy.post("/areaInfo/list", { parentId: id }).then((res) => {
+    if (type === "20") {
+      provinceData.value = res;
+      if (isChange) {
+        formData.data.provinceId = "";
+        formData.data.cityId = "";
+      }
+    } else if (type === "30") {
+      cityData.value = res;
+      if (isChange) {
+        formData.data.cityId = "";
+      }
+    } else {
+      countryData.value = res;
+    }
+  });
+};
+getCityData("0");
+const clickAddPerson = () => {
+  formPerson.data = {};
+  openPerson.value = true;
+};
+const submitPerson = () => {
+  person.value.handleSubmit(() => {
+    if (formData.data.customerUserList && formData.data.customerUserList.length > 0) {
+      formData.data.customerUserList.push({
+        name: formPerson.data.name,
+        phone: formPerson.data.phone,
+      });
+    } else {
+      formData.data.customerUserList = [
+        {
+          name: formPerson.data.name,
+          phone: formPerson.data.phone,
+        },
+      ];
+    }
+    openPerson.value = false;
+  });
+};
+const submitForm = () => {
+  submit.value.handleSubmit(() => {
+    if (formData.data.customerUserList && formData.data.customerUserList.length > 0) {
+      submitLoading.value = true;
+      proxy.post("/customer/" + modalType.value, formData.data).then(
+        () => {
+          ElMessage({
+            message: modalType.value == "add" ? "添加成功" : "编辑成功",
+            type: "success",
+          });
+          dialogVisible.value = false;
+          submitLoading.value = false;
+          getList();
+        },
+        (err) => {
+          console.log(err);
+          submitLoading.value = false;
+        }
+      );
+    } else {
+      ElMessage("请添加客户联系人");
+    }
+  });
+};
+const getDict = () => {
+  proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      dictCode: "customer_source",
+      tenantId: useUserStore().user.tenantId,
+    })
+    .then((res) => {
+      customerSource.value = res.rows.map((item) => {
+        return {
+          label: item.dictValue,
+          value: item.dictKey,
+        };
+      });
+    });
+  proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      dictCode: "customer_status",
+      tenantId: useUserStore().user.tenantId,
+    })
+    .then((res) => {
+      customerStatus.value = res.rows.map((item) => {
+        return {
+          label: item.dictValue,
+          value: item.dictKey,
+        };
+      });
+    });
+  proxy.get("/tenantUser/list?pageNum=1&pageSize=10000", {}).then((res) => {
+    userList.value = res.rows.map((item) => {
+      return {
+        label: item.userName,
+        value: item.userId,
+      };
+    });
+  });
+};
+getDict();
+getList();
+</script>
+
+<style lang="scss" scoped>
+.tenant {
+  padding: 20px;
+}
+</style>