Browse Source

feat: CRM 客户限制

Wanwan 1 year ago
parent
commit
6369b334e3

+ 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 })
+}

+ 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 })
   return request.get({ url: '/system/user/page', params })
 }
 }
 
 
+// 查询所有用户列表
+export const getAllUser = () => {
+  return request.get({ url: '/system/user/all' })
+}
+
 // 查询用户详情
 // 查询用户详情
 export const getUser = (id: number) => {
 export const getUser = (id: number) => {
   return request.get({ url: '/system/user/get?id=' + id })
   return request.get({ url: '/system/user/get?id=' + id })

+ 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>

+ 130 - 0
src/views/crm/customerLimitConfig/CustomerQuantityLimit.vue

@@ -0,0 +1,130 @@
+<template>
+  <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">
+    <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'
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+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 { confType } = defineProps<{ confType: LimitConfType }>()
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>
+<style scoped lang="scss"></style>

+ 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" style="height: 200px">
+      <el-tab-pane label="拥有客户数限制">
+        <CustomerQuantityLimit :confType="LimitConfType.CUSTOMER_QUANTITY_LIMIT" />
+      </el-tab-pane>
+      <el-tab-pane label="锁定客户数限制">
+        <CustomerQuantityLimit :confType="LimitConfType.CUSTOMER_LOCK_LIMIT" />
+      </el-tab-pane>
+    </el-tabs>
+  </ContentWrap>
+</template>
+
+<script setup lang="ts">
+import CustomerQuantityLimit from '@/views/crm/customerLimitConfig/CustomerQuantityLimit.vue'
+import { LimitConfType } from '@/views/crm/customerLimitConfig/customerLimitConf'
+
+defineOptions({ name: 'CrmCustomerLimitConfig' })
+</script>

+ 2 - 2
src/views/crm/customerPoolConf/index.vue

@@ -87,7 +87,6 @@ const getConfig = async () => {
       return
       return
     }
     }
     formData.value = data
     formData.value = data
-    console.log(formData.value)
   } finally {
   } finally {
     formLoading.value = false
     formLoading.value = false
   }
   }
@@ -107,7 +106,8 @@ const onSubmit = async () => {
     const data = formData.value as unknown as CustomerPoolConfApi.CustomerPoolConfigVO
     const data = formData.value as unknown as CustomerPoolConfApi.CustomerPoolConfigVO
     await CustomerPoolConfApi.updateCustomerPoolConfig(data)
     await CustomerPoolConfApi.updateCustomerPoolConfig(data)
     message.success(t('common.updateSuccess'))
     message.success(t('common.updateSuccess'))
-    dialogVisible.value = false
+    await getConfig()
+    formLoading.value = false
   } finally {
   } finally {
     formLoading.value = false
     formLoading.value = false
   }
   }