소스 검색

!316 feat: CRM 客户公海配置 + CRM 客户限制 + review 修改
Merge pull request !316 from wanwan/dev

芋道源码 1 년 전
부모
커밋
c06223decd

+ 35 - 0
src/api/crm/customerLimitConfig/index.ts

@@ -0,0 +1,35 @@
+import request from '@/config/axios'
+
+export interface CustomerLimitConfigVO {
+  id?: number
+  type?: number
+  userIds?: string
+  deptIds?: string
+  maxCount?: number
+  dealCountEnabled?: boolean
+}
+
+// 查询客户限制配置列表
+export const getCustomerLimitConfigPage = async (params) => {
+  return await request.get({ url: `/crm/customer-limit-config/page`, params })
+}
+
+// 查询客户限制配置详情
+export const getCustomerLimitConfig = async (id: number) => {
+  return await request.get({ url: `/crm/customer-limit-config/get?id=` + id })
+}
+
+// 新增客户限制配置
+export const createCustomerLimitConfig = async (data: CustomerLimitConfigVO) => {
+  return await request.post({ url: `/crm/customer-limit-config/create`, data })
+}
+
+// 修改客户限制配置
+export const updateCustomerLimitConfig = async (data: CustomerLimitConfigVO) => {
+  return await request.put({ url: `/crm/customer-limit-config/update`, data })
+}
+
+// 删除客户限制配置
+export const deleteCustomerLimitConfig = async (id: number) => {
+  return await request.delete({ url: `/crm/customer-limit-config/delete?id=` + id })
+}

+ 19 - 0
src/api/crm/customerPoolConf/index.ts

@@ -0,0 +1,19 @@
+import request from '@/config/axios'
+
+export interface CustomerPoolConfigVO {
+  enabled?: boolean
+  contactExpireDays?: number
+  dealExpireDays?: number
+  notifyEnabled?: boolean
+  notifyDays: number
+}
+
+// 获取客户公海规则设置
+export const getCustomerPoolConfig = async () => {
+  return await request.get({ url: `/crm/customer-pool-config/get` })
+}
+
+// 更新客户公海规则设置
+export const updateCustomerPoolConfig = async (data: ConfigVO) => {
+  return await request.put({ url: `/crm/customer-pool-config/update`, data })
+}

+ 5 - 0
src/api/system/user/index.ts

@@ -22,6 +22,11 @@ export const getUserPage = (params: PageParam) => {
   return request.get({ url: '/system/user/page', params })
 }
 
+// 查询所有用户列表
+export const getAllUser = () => {
+  return request.get({ url: '/system/user/all' })
+}
+
 // 查询用户详情
 export const getUser = (id: number) => {
   return request.get({ url: '/system/user/get?id=' + id })

+ 82 - 79
src/views/crm/customer/detail/CustomerDetails.vue

@@ -1,83 +1,86 @@
 <template>
-  <el-collapse v-model="activeNames">
-    <el-collapse-item name="basicInfo">
-      <template #title>
-        <span class="text-base font-bold">基本信息</span>
-      </template>
-      <el-descriptions :column="4">
-        <el-descriptions-item label="客户名称">
-          {{ customer.name }}
-        </el-descriptions-item>
-        <el-descriptions-item label="所属行业">
-          <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="customer.industryId" />
-        </el-descriptions-item>
-        <el-descriptions-item label="客户来源">
-          <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="customer.source" />
-        </el-descriptions-item>
-        <el-descriptions-item label="客户等级">
-          <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="customer.level" />
-        </el-descriptions-item>
-        <el-descriptions-item label="手机">
-          {{ customer.mobile }}
-        </el-descriptions-item>
-        <el-descriptions-item label="电话">
-          {{ customer.telephone }}
-        </el-descriptions-item>
-        <el-descriptions-item label="邮箱">
-          {{ customer.email }}
-        </el-descriptions-item>
-        <el-descriptions-item label="QQ">
-          {{ customer.qq }}
-        </el-descriptions-item>
-        <el-descriptions-item label="微信">
-          {{ customer.wechat }}
-        </el-descriptions-item>
-        <el-descriptions-item label="网址">
-          {{ customer.website }}
-        </el-descriptions-item>
-        <el-descriptions-item label="所在地">
-          {{ customer.areaName }}
-        </el-descriptions-item>
-        <el-descriptions-item label="详细地址">
-          {{ customer.detailAddress }}
-        </el-descriptions-item>
-        <el-descriptions-item label="下次联系时间">
-          {{ customer.contactNextTime ? formatDate(customer.contactNextTime, 'YYYY-MM-DD') : '空' }}
-        </el-descriptions-item>
-      </el-descriptions>
-      <el-descriptions :column="1">
-        <el-descriptions-item label="客户描述">
-          {{ customer.description }}
-        </el-descriptions-item>
-        <el-descriptions-item label="备注">
-          {{ customer.remark }}
-        </el-descriptions-item>
-      </el-descriptions>
-    </el-collapse-item>
-    <el-collapse-item name="systemInfo">
-      <template #title>
-        <span class="text-base font-bold">系统信息</span>
-      </template>
-      <el-descriptions :column="2">
-        <el-descriptions-item label="负责人">
-          {{ customer.ownerUserName }}
-        </el-descriptions-item>
-        <el-descriptions-item label="创建人">
-          {{ customer.creatorName }}
-        </el-descriptions-item>
-        <el-descriptions-item label="创建时间">
-          {{ customer.createTime ? formatDate(customer.createTime) : '空' }}
-        </el-descriptions-item>
-        <el-descriptions-item label="更新时间">
-          {{ customer.updateTime ? formatDate(customer.updateTime) : '空' }}
-        </el-descriptions-item>
-        <!-- TODO wanwan:要不把“最后跟进时间”放到“下次联系时间”后面 -->
-        <el-descriptions-item label="最后跟进时间">
-          {{ customer.contactLastTime ? formatDate(customer.contactLastTime) : '空' }}
-        </el-descriptions-item>
-      </el-descriptions>
-    </el-collapse-item>
-  </el-collapse>
+  <ContentWrap>
+    <el-collapse class="" v-model="activeNames">
+      <el-collapse-item name="basicInfo">
+        <template #title>
+          <span class="text-base font-bold">基本信息</span>
+        </template>
+        <el-descriptions :column="4">
+          <el-descriptions-item label="客户名称">
+            {{ customer.name }}
+          </el-descriptions-item>
+          <el-descriptions-item label="所属行业">
+            <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="customer.industryId" />
+          </el-descriptions-item>
+          <el-descriptions-item label="客户来源">
+            <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="customer.source" />
+          </el-descriptions-item>
+          <el-descriptions-item label="客户等级">
+            <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="customer.level" />
+          </el-descriptions-item>
+          <el-descriptions-item label="手机">
+            {{ customer.mobile }}
+          </el-descriptions-item>
+          <el-descriptions-item label="电话">
+            {{ customer.telephone }}
+          </el-descriptions-item>
+          <el-descriptions-item label="邮箱">
+            {{ customer.email }}
+          </el-descriptions-item>
+          <el-descriptions-item label="QQ">
+            {{ customer.qq }}
+          </el-descriptions-item>
+          <el-descriptions-item label="微信">
+            {{ customer.wechat }}
+          </el-descriptions-item>
+          <el-descriptions-item label="网址">
+            {{ customer.website }}
+          </el-descriptions-item>
+          <el-descriptions-item label="所在地">
+            {{ customer.areaName }}
+          </el-descriptions-item>
+          <el-descriptions-item label="详细地址">
+            {{ customer.detailAddress }}
+          </el-descriptions-item>
+          <el-descriptions-item label="下次联系时间">
+            {{
+              customer.contactNextTime ? formatDate(customer.contactNextTime, 'YYYY-MM-DD') : '空'
+            }}
+          </el-descriptions-item>
+          <el-descriptions-item label="最后跟进时间">
+            {{ customer.contactLastTime ? formatDate(customer.contactLastTime) : '空' }}
+          </el-descriptions-item>
+        </el-descriptions>
+        <el-descriptions :column="1">
+          <el-descriptions-item label="客户描述">
+            {{ customer.description }}
+          </el-descriptions-item>
+          <el-descriptions-item label="备注">
+            {{ customer.remark }}
+          </el-descriptions-item>
+        </el-descriptions>
+      </el-collapse-item>
+      <el-collapse-item name="systemInfo">
+        <template #title>
+          <span class="text-base font-bold">系统信息</span>
+        </template>
+        <el-descriptions :column="2">
+          <el-descriptions-item label="负责人">
+            {{ customer.ownerUserName }}
+          </el-descriptions-item>
+          <el-descriptions-item label="创建人">
+            {{ customer.creatorName }}
+          </el-descriptions-item>
+          <el-descriptions-item label="创建时间">
+            {{ customer.createTime ? formatDate(customer.createTime) : '空' }}
+          </el-descriptions-item>
+          <el-descriptions-item label="更新时间">
+            {{ customer.updateTime ? formatDate(customer.updateTime) : '空' }}
+          </el-descriptions-item>
+        </el-descriptions>
+      </el-collapse-item>
+    </el-collapse>
+  </ContentWrap>
 </template>
 <script setup lang="ts">
 import * as CustomerApi from '@/api/crm/customer'

+ 2 - 4
src/views/crm/customer/detail/index.vue

@@ -63,12 +63,10 @@
       </el-descriptions-item>
     </el-descriptions>
   </ContentWrap>
-  <!-- TODO wanwan:这个 tab 拉满哈,可以更好看; -->
-  <el-col :span="18">
+  <el-col>
     <el-tabs>
       <el-tab-pane label="详细资料">
-        <!-- TODO wanwan:这个 ml-2 是不是可以优化下,不要整个左移,而是里面的内容有个几 px 的偏移,不顶在框里 -->
-        <CustomerDetails class="ml-2" :customer="customer" />
+        <CustomerDetails :customer="customer" />
       </el-tab-pane>
       <el-tab-pane label="活动" lazy> 活动</el-tab-pane>
       <el-tab-pane label="邮件" lazy> 邮件</el-tab-pane>

+ 147 - 0
src/views/crm/customerLimitConfig/CustomerLimitConfDetails.vue

@@ -0,0 +1,147 @@
+<template>
+  <el-button type="primary" plain @click="handleQuery">
+    <Icon icon="ep:refresh" class="mr-5px" /> 刷新
+  </el-button>
+  <el-button
+    type="primary"
+    plain
+    @click="openForm('create')"
+    v-hasPermi="['crm:customer-limit-config:create']"
+  >
+    <Icon icon="ep:plus" class="mr-5px" /> 新增
+  </el-button>
+  <el-table
+    v-loading="loading"
+    :data="list"
+    :stripe="true"
+    :show-overflow-tooltip="true"
+    class="mt-4"
+  >
+    <el-table-column label="编号" align="center" prop="id" />
+    <el-table-column label="规则类型" align="center" prop="type" />
+    <el-table-column label="规则适用人群" align="center" prop="userNames" />
+    <el-table-column label="规则适用部门" align="center" prop="deptNames" />
+    <el-table-column
+      :label="
+        confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT ? '拥有客户数上限' : '锁定客户数上限'
+      "
+      align="center"
+      prop="maxCount"
+    />
+    <el-table-column
+      v-if="confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT"
+      label="成交客户是否占用拥有客户数"
+      align="center"
+      prop="dealCountEnabled"
+    >
+      <template #default="scope">
+        <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealCountEnabled" />
+      </template>
+    </el-table-column>
+    <el-table-column
+      label="创建时间"
+      align="center"
+      prop="createTime"
+      :formatter="dateFormatter"
+      width="180px"
+    />
+    <el-table-column label="操作" align="center" min-width="110" fixed="right">
+      <template #default="scope">
+        <el-button
+          link
+          type="primary"
+          @click="openForm('update', scope.row.id)"
+          v-hasPermi="['crm:customer-limit-config:update']"
+        >
+          编辑
+        </el-button>
+        <el-button
+          link
+          type="danger"
+          @click="handleDelete(scope.row.id)"
+          v-hasPermi="['crm:customer-limit-config:delete']"
+        >
+          删除
+        </el-button>
+      </template>
+    </el-table-column>
+  </el-table>
+  <!-- 分页 -->
+  <Pagination
+    :total="total"
+    v-model:page="queryParams.pageNo"
+    v-model:limit="queryParams.pageSize"
+    @pagination="getList"
+  />
+
+  <!-- 表单弹窗:添加/修改 -->
+  <CustomerLimitConfigForm ref="formRef" @success="getList" />
+</template>
+<script setup lang="ts">
+import { dateFormatter } from '@/utils/formatTime'
+import * as CustomerLimitConfigApi from '@/api/crm/customerLimitConfig'
+import CustomerLimitConfigForm from '@/views/crm/customerLimitConfig/CustomerLimitConfigForm.vue'
+import { LimitConfType } from '@/views/crm/customerLimitConfig/customerLimitConf'
+import { DICT_TYPE } from '@/utils/dict'
+
+defineOptions({ name: 'CustomerLimitConfDetails' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const { confType } = defineProps<{ confType: LimitConfType }>()
+
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  type: confType
+})
+const queryFormRef = ref() // 搜索的表单
+const exportLoading = ref(false) // 导出的加载中
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await CustomerLimitConfigApi.getCustomerLimitConfigPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id, confType)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await CustomerLimitConfigApi.deleteCustomerLimitConfig(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>
+<style scoped lang="scss"></style>

+ 222 - 0
src/views/crm/customerLimitConfig/CustomerLimitConfigForm.vue

@@ -0,0 +1,222 @@
+<template>
+  <Dialog :title="dialogTitle" v-model="dialogVisible">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="200px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="规则适用人群" prop="userIds">
+        <el-tree-select
+          v-model="formData.userIds"
+          :data="userTree"
+          :props="defaultProps"
+          check-on-click-node
+          multiple
+          filterable
+          node-key="id"
+          placeholder="请选择规则适用人群"
+        />
+      </el-form-item>
+      <el-form-item label="规则适用部门" prop="deptIds">
+        <el-tree-select
+          v-model="formData.deptIds"
+          :data="deptTree"
+          :props="defaultProps"
+          multiple
+          check-strictly
+          filterable
+          node-key="id"
+          placeholder="请选择规则适用部门"
+        />
+      </el-form-item>
+      <el-form-item
+        :label="
+          formData.type === LimitConfType.CUSTOMER_QUANTITY_LIMIT
+            ? '拥有客户数上限'
+            : '锁定客户数上限'
+        "
+        prop="maxCount"
+      >
+        <el-input-number v-model="formData.maxCount" placeholder="请输入数量上限" />
+      </el-form-item>
+      <el-form-item
+        label="成交客户是否占用拥有客户数"
+        v-if="formData.type === LimitConfType.CUSTOMER_QUANTITY_LIMIT"
+        prop="dealCountEnabled"
+      >
+        <el-switch v-model="formData.dealCountEnabled" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import * as CustomerLimitConfigApi from '@/api/crm/customerLimitConfig'
+import { LimitConfType } from '@/views/crm/customerLimitConfig/customerLimitConf'
+import * as DeptApi from '@/api/system/dept'
+import { defaultProps, handleTree } from '@/utils/tree'
+import * as UserApi from '@/api/system/user'
+import { cloneDeep } from 'lodash-es'
+
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+  id: undefined,
+  type: undefined,
+  userIds: undefined,
+  deptIds: undefined,
+  maxCount: undefined,
+  dealCountEnabled: false
+})
+const formRules = reactive({
+  type: [{ required: true, message: '规则类型不能为空', trigger: 'change' }],
+  maxCount: [{ required: true, message: '数量上限不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+const deptTree = ref() // 部门树形结构
+const userTree = ref() // 用户树形结构
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number, limitConfType?: LimitConfType) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 获得部门树
+  await getDeptTree()
+  // 获得用户
+  await getUserTree()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await CustomerLimitConfigApi.getCustomerLimitConfig(id)
+    } finally {
+      formLoading.value = false
+    }
+  } else {
+    formData.value.type = limitConfType
+  }
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as CustomerLimitConfigApi.CustomerLimitConfigVO
+    if (formType.value === 'create') {
+      await CustomerLimitConfigApi.createCustomerLimitConfig(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await CustomerLimitConfigApi.updateCustomerLimitConfig(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    type: undefined,
+    userIds: undefined,
+    deptIds: undefined,
+    maxCount: undefined,
+    dealCountEnabled: false
+  }
+  formRef.value?.resetFields()
+}
+
+/**
+ * 获取部门树
+ */
+const getDeptTree = async () => {
+  const res = await DeptApi.getSimpleDeptList()
+  deptTree.value = []
+  deptTree.value.push(...handleTree(res))
+}
+
+/**
+ * 获取用户树
+ */
+const getUserTree = async () => {
+  const res = await UserApi.getAllUser()
+  userTree.value = []
+  userTree.value = cloneDeep(unref(deptTree))
+
+  const deptUserMap = {}
+  res.forEach((user) => {
+    if (user.dept) {
+      if (!deptUserMap[user.deptId]) {
+        deptUserMap[user.deptId] = []
+      }
+      deptUserMap[user.deptId].push(user)
+    }
+  })
+
+  handleUserData(userTree.value, deptUserMap)
+}
+
+/**
+ * 处理用户树
+ *
+ * @param deptTree
+ * @param deptUserMap
+ */
+const handleUserData = (deptTree, deptUserMap) => {
+  for (let i = 0; i < deptTree.length; i++) {
+    // 如果是用户,就不用继续找部门下的用户
+    if (deptTree[i].isUser) {
+      continue
+    }
+    const users = deptUserMap[deptTree[i].id]
+    if (users) {
+      if (!deptTree[i].children) {
+        deptTree[i].children = []
+      }
+      deptTree[i].children.push(
+        ...users.map((user) => {
+          return {
+            id: user.id,
+            name: user.username + '-' + user.nickname,
+            isUser: true,
+            // 用户状态为关闭
+            disabled: user.status === 1
+          }
+        })
+      )
+    }
+
+    if (deptTree[i].children && deptTree[i].children.length !== 0) {
+      handleUserData(deptTree[i].children, deptUserMap)
+    }
+
+    // 非人员选项禁用
+    deptTree[i].disabled = true
+    // 将非人员的 id 置为空
+    deptTree[i].id = 'null'
+  }
+}
+</script>

+ 13 - 0
src/views/crm/customerLimitConfig/customerLimitConf.ts

@@ -0,0 +1,13 @@
+/**
+ * 客户限制配置类型
+ */
+export enum LimitConfType {
+  /**
+   * 拥有客户数限制
+   */
+  CUSTOMER_QUANTITY_LIMIT = 1,
+  /**
+   * 锁定客户数限制
+   */
+  CUSTOMER_LOCK_LIMIT = 2
+}

+ 20 - 0
src/views/crm/customerLimitConfig/index.vue

@@ -0,0 +1,20 @@
+<template>
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-tabs tab-position="left">
+      <el-tab-pane label="拥有客户数限制">
+        <CustomerLimitConfDetails :confType="LimitConfType.CUSTOMER_QUANTITY_LIMIT" />
+      </el-tab-pane>
+      <el-tab-pane label="锁定客户数限制">
+        <CustomerLimitConfDetails :confType="LimitConfType.CUSTOMER_LOCK_LIMIT" />
+      </el-tab-pane>
+    </el-tabs>
+  </ContentWrap>
+</template>
+
+<script setup lang="ts">
+import CustomerLimitConfDetails from '@/views/crm/customerLimitConfig/CustomerLimitConfDetails.vue'
+import { LimitConfType } from '@/views/crm/customerLimitConfig/customerLimitConf'
+
+defineOptions({ name: 'CrmCustomerLimitConfig' })
+</script>

+ 139 - 0
src/views/crm/customerPoolConf/index.vue

@@ -0,0 +1,139 @@
+<template>
+  <ContentWrap>
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="160px"
+      v-loading="formLoading"
+    >
+      <el-card shadow="never">
+        <template #header>
+          <div class="flex items-center justify-between">
+            <CardTitle title="客户公海规则设置" />
+            <el-button
+              type="primary"
+              @click="onSubmit"
+              v-hasPermi="['crm:customer-pool-config:update']"
+              >保存</el-button
+            >
+          </div>
+        </template>
+
+        <el-form-item label="客户公海规则设置" prop="enabled">
+          <el-radio-group v-model="formData.enabled" class="ml-4">
+            <el-radio :label="false" size="large">不启用</el-radio>
+            <el-radio :label="true" size="large">启用</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <div v-if="formData.enabled">
+          <el-form-item>
+            <el-input-number class="mr-2" v-model="formData.contactExpireDays" />
+            天不跟进或
+            <el-input-number class="mx-2" v-model="formData.dealExpireDays" />
+            天未成交
+          </el-form-item>
+          <el-form-item label="提前提醒设置" prop="notifyEnabled">
+            <el-radio-group v-model="formData.notifyEnabled" class="ml-4">
+              <el-radio :label="false" size="large">不提醒</el-radio>
+              <el-radio :label="true" size="large">提醒</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <div v-if="formData.notifyEnabled">
+            <el-form-item>
+              提前
+              <el-input-number class="mx-2" v-model="formData.notifyDays" />
+              天提醒
+            </el-form-item>
+          </div>
+        </div>
+      </el-card>
+    </el-form>
+  </ContentWrap>
+</template>
+
+<script setup lang="ts">
+import * as CustomerPoolConfApi from '@/api/crm/customerPoolConf'
+import { CardTitle } from '@/components/Card'
+import { CustomerPoolConfigVO } from '@/api/crm/customerPoolConf'
+
+defineOptions({ name: 'CustomerPoolConf' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const formLoading = ref(false)
+const formData = ref({
+  enabled: false,
+  contactExpireDays: 0,
+  dealExpireDays: 0,
+  notifyEnabled: false,
+  notifyDays: 0
+})
+
+const formRules = reactive({
+  enabled: [{ required: true, message: '是否启用客户公海不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+
+/**
+ * 获取配置
+ */
+const getConfig = async () => {
+  try {
+    formLoading.value = true
+    const data = await CustomerPoolConfApi.getCustomerPoolConfig()
+    if (data === null) {
+      return
+    }
+    formData.value = data
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/**
+ * 提交配置
+ */
+const onSubmit = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as CustomerPoolConfApi.CustomerPoolConfigVO
+    await CustomerPoolConfApi.updateCustomerPoolConfig(data)
+    message.success(t('common.updateSuccess'))
+    await getConfig()
+    formLoading.value = false
+  } finally {
+    formLoading.value = false
+  }
+}
+
+watch(
+  () => formData.value.enabled,
+  (val: boolean) => {
+    if (!val) {
+      formData.value.contactExpireDays = undefined
+      formData.value.dealExpireDays = undefined
+      formData.value.notifyEnabled = false
+      formData.value.notifyDays = undefined
+    }
+  }
+)
+watch(
+  () => formData.value.notifyEnabled,
+  (val: boolean) => {
+    if (!val) {
+      formData.value.notifyDays = undefined
+    }
+  }
+)
+
+onMounted(() => {
+  getConfig()
+})
+</script>