Jelajahi Sumber

crm-客户:完善转移和按钮权限

(cherry picked from commit 9cbb3aa84a6adee46bde55208d2ee42f3babdc90)
puhui999 1 tahun lalu
induk
melakukan
d5be5f35a8

+ 11 - 0
src/api/crm/customer/index.ts

@@ -75,6 +75,17 @@ export const getOperateLogPage = async (id: number) => {
 
 // ======================= 业务操作 =======================
 
+export interface TransferReqVO {
+  id: number | undefined // 客户编号
+  newOwnerUserId: number | undefined // 新负责人的用户编号
+  oldOwnerPermissionLevel: number | undefined // 老负责人加入团队后的权限级别
+}
+
+// 客户转移
+export const transfer = async (data: TransferReqVO) => {
+  return await request.put({ url: '/crm/customer/transfer', data })
+}
+
 // 锁定/解锁客户
 export const lockCustomer = async (id: number, lockStatus: boolean) => {
   return await request.put({ url: `/crm/customer/lock`, data: { id, lockStatus } })

+ 42 - 11
src/views/crm/customer/detail/index.vue

@@ -1,19 +1,38 @@
 <template>
   <CustomerDetailsHeader :customer="customer" :loading="loading">
-    <!-- TODO puhui999: 按钮数据权限收尾统一完善,需要按权限分级和客户状态来动态显示匹配的按钮 -->
-    <el-button v-hasPermi="['crm:customer:update']" type="primary" @click="openForm">
+    <el-button
+      v-if="permissionListRef?.validateWrite"
+      v-hasPermi="['crm:customer:update']"
+      type="primary"
+      @click="openForm"
+    >
       编辑
     </el-button>
-    <!-- TODO @puhui999:转移的操作接入 -->
-    <el-button type="primary" @click="transfer">转移</el-button>
-    <!-- TODO @puhui999:修改成交状态的接入 -->
-    <el-button>更改成交状态</el-button>
-    <el-button v-if="customer.lockStatus" @click="handleUnlock">解锁</el-button>
-    <el-button v-if="!customer.lockStatus" @click="handleLock">锁定</el-button>
+    <el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
+      转移
+    </el-button>
+    <el-button v-if="permissionListRef?.validateWrite">更改成交状态</el-button>
+    <el-button
+      v-if="customer.lockStatus && permissionListRef?.validateOwnerUser"
+      @click="handleUnlock"
+    >
+      解锁
+    </el-button>
+    <el-button
+      v-if="!customer.lockStatus && permissionListRef?.validateOwnerUser"
+      @click="handleLock"
+    >
+      锁定
+    </el-button>
     <el-button v-if="!customer.ownerUserId" type="primary" @click="handleReceive">
       领取客户
     </el-button>
-    <el-button v-if="customer.ownerUserId" @click="handlePutPool">客户放入公海</el-button>
+    <el-button
+      v-if="customer.ownerUserId && permissionListRef?.validateOwnerUser"
+      @click="handlePutPool"
+    >
+      客户放入公海
+    </el-button>
   </CustomerDetailsHeader>
   <el-col>
     <el-tabs>
@@ -30,7 +49,12 @@
         <ContactList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
       </el-tab-pane>
       <el-tab-pane label="团队成员">
-        <PermissionList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
+        <PermissionList
+          ref="permissionListRef"
+          :biz-id="customer.id!"
+          :biz-type="BizTypeEnum.CRM_CUSTOMER"
+          :show-action="!permissionListRef?.isPool || false"
+        />
       </el-tab-pane>
       <el-tab-pane label="商机" lazy>
         <BusinessList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
@@ -48,6 +72,7 @@
 
   <!-- 表单弹窗:添加/修改 -->
   <CustomerForm ref="formRef" @success="getCustomer" />
+  <CrmTransferForm ref="crmTransferFormRef" @success="close" />
 </template>
 <script lang="ts" setup>
 import { useTagsViewStore } from '@/store/modules/tagsView'
@@ -61,6 +86,7 @@ import BusinessList from '@/views/crm/business/components/BusinessList.vue' // 
 import ReceivableList from '@/views/crm/receivable/components/ReceivableList.vue' // 回款列表
 import ReceivablePlanList from '@/views/crm/receivable/plan/components/ReceivablePlanList.vue' // 回款计划列表
 import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
+import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
 import FollowUpList from '@/views/crm/followup/index.vue'
 import { BizTypeEnum } from '@/api/crm/permission'
 import type { OperateLogV2VO } from '@/api/system/operatelog'
@@ -73,6 +99,8 @@ const message = useMessage() // 消息弹窗
 const { delView } = useTagsViewStore() // 视图操作
 const { currentRoute } = useRouter() // 路由
 
+const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
+
 /** 获取详情 */
 const customer = ref<CustomerApi.CustomerVO>({} as CustomerApi.CustomerVO) // 客户详情
 const getCustomer = async () => {
@@ -92,7 +120,10 @@ const openForm = () => {
 }
 
 /** 客户转移 */
-const transfer = () => {}
+const crmTransferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 客户转移表单 ref
+const transfer = () => {
+  crmTransferFormRef.value?.open('客户转移', customerId.value, CustomerApi.transfer)
+}
 
 /** 锁定客户 */
 const handleLock = async () => {

+ 110 - 62
src/views/crm/permission/components/PermissionList.vue

@@ -1,103 +1,112 @@
 <template>
   <!-- 操作栏 -->
-  <el-row justify="end">
-    <el-button @click="openForm">
-      <Icon class="mr-5px" icon="fluent:people-team-add-20-filled" />
-      添加团队成员
+  <el-row v-if="showAction" justify="end">
+    <el-button v-if="validateOwnerUser" type="primary" @click="openForm">
+      <Icon class="mr-5px" icon="ep:plus" />
+      新增
     </el-button>
-    <el-button @click="handleUpdate">
+    <el-button v-if="validateOwnerUser" @click="handleUpdate">
       <Icon class="mr-5px" icon="ep:edit" />
       编辑
     </el-button>
-    <el-button @click="handleDelete">
+    <el-button v-if="validateOwnerUser" @click="handleDelete">
       <Icon class="mr-5px" icon="ep:delete" />
       移除
     </el-button>
-    <el-button type="danger" @click="handleQuit"> 退出团队</el-button>
+    <el-button v-if="!validateOwnerUser && list.length > 0" type="danger" @click="handleQuit">
+      退出团队
+    </el-button>
   </el-row>
-
-  <!-- 列表 -->
-  <ContentWrap class="mt-10px">
-    <el-table
-      v-loading="loading"
-      :data="list"
-      :show-overflow-tooltip="true"
-      :stripe="true"
-      @selection-change="handleSelectionChange"
-    >
-      <!-- TODO @puhui999:负责人不允许选中 -->
-      <el-table-column type="selection" width="55" />
-      <el-table-column align="center" label="姓名" prop="nickname" />
-      <el-table-column align="center" label="部门" prop="deptName" />
-      <el-table-column align="center" label="岗位" prop="postNames" />
-      <el-table-column align="center" label="权限" prop="level">
-        <template #default="{ row }">
-          <dict-tag :type="DICT_TYPE.CRM_PERMISSION_LEVEL" :value="row.level" />
-        </template>
-      </el-table-column>
-      <el-table-column
-        :formatter="dateFormatter"
-        align="center"
-        label="加入时间"
-        prop="createTime"
-      />
-    </el-table>
-  </ContentWrap>
+  <!-- 团队成员展示 -->
+  <el-table
+    ref="elTableRef"
+    v-loading="loading"
+    :data="list"
+    :show-overflow-tooltip="true"
+    :stripe="true"
+    class="mt-20px"
+    @selection-change="handleSelectionChange"
+  >
+    <el-table-column type="selection" width="55" />
+    <el-table-column align="center" label="姓名" prop="nickname" />
+    <el-table-column align="center" label="部门" prop="deptName" />
+    <el-table-column align="center" label="岗位" prop="postNames" />
+    <el-table-column align="center" label="权限级别" prop="level">
+      <template #default="{ row }">
+        <dict-tag :type="DICT_TYPE.CRM_PERMISSION_LEVEL" :value="row.level" />
+      </template>
+    </el-table-column>
+    <el-table-column :formatter="dateFormatter" align="center" label="加入时间" prop="createTime" />
+  </el-table>
 
   <!-- 表单弹窗:添加/修改 -->
   <CrmPermissionForm ref="formRef" @success="getList" />
 </template>
 <script lang="ts" setup>
 import { dateFormatter } from '@/utils/formatTime'
-import { DICT_TYPE } from '@/utils/dict'
+import { ElTable } from 'element-plus'
 import * as PermissionApi from '@/api/crm/permission'
-import { PermissionLevelEnum } from '@/api/crm/permission'
 import { useUserStoreWithOut } from '@/store/modules/user'
 import CrmPermissionForm from './PermissionForm.vue'
+import { DICT_TYPE } from '@/utils/dict'
 
 defineOptions({ name: 'CrmPermissionList' })
-const props = defineProps<{
-  bizType: number // 业务类型
-  bizId: number // 业务编号
-}>()
 
 const message = useMessage() // 消息
+
+const props = defineProps<{
+  bizType: number // 模块类型
+  bizId: number | undefined // 模块数据编号
+  showAction: boolean //是否展示操作按钮
+}>()
 const loading = ref(true) // 列表的加载中
 const list = ref<PermissionApi.PermissionVO[]>([]) // 列表的数据
+const formData = ref({
+  ownerUserId: 0
+})
+const userStore = useUserStoreWithOut() // 用户信息缓存
 
 /** 查询列表 */
 const getList = async () => {
   loading.value = true
   try {
-    list.value = await PermissionApi.getPermissionList({
+    const data = await PermissionApi.getPermissionList({
       bizType: props.bizType,
       bizId: props.bizId
     })
+    list.value = data
+    const permission = list.value.find(
+      (item) =>
+        item.userId === userStore.getUser.id &&
+        item.level === PermissionApi.PermissionLevelEnum.OWNER
+    )
+    if (permission) {
+      formData.value.ownerUserId = userStore.getUser.id
+    }
   } finally {
     loading.value = false
   }
 }
-
-/** 选中团队成员 */
 const multipleSelection = ref<PermissionApi.PermissionVO[]>([]) // 选择的团队成员
+const elTableRef = ref<InstanceType<typeof ElTable>>()
 const handleSelectionChange = (val: PermissionApi.PermissionVO[]) => {
+  if (val.findIndex((item) => item.level === PermissionApi.PermissionLevelEnum.OWNER) !== -1) {
+    message.warning('不能选择负责人!')
+    elTableRef.value?.clearSelection()
+    return
+  }
   multipleSelection.value = val
 }
 
-/** 添加团队成员 */
-const formRef = ref<InstanceType<typeof CrmPermissionForm>>() // 权限表单 Ref
-const openForm = () => {
-  formRef.value?.open('create', props.bizType, props.bizId)
-}
-
 /** 编辑团队成员 */
+const formRef = ref<InstanceType<typeof CrmPermissionForm>>() // 权限表单 Ref
 const handleUpdate = () => {
   if (multipleSelection.value?.length === 0) {
     message.warning('请先选择团队成员后操作!')
     return
   }
-  const ids = multipleSelection.value?.map((item) => item.id) as number[]
-  formRef.value?.open('update', props.bizType, props.bizId, ids)
+  const ids = multipleSelection.value?.map((item) => item.id) as unknown as number[]
+  formRef.value?.open('update', props.bizType, props.bizId!, ids)
 }
 
 /** 移除团队成员 */
@@ -106,36 +115,75 @@ const handleDelete = async () => {
     message.warning('请先选择团队成员后操作!')
     return
   }
-  await message.delConfirm('是否删除选择的团队成员?')
+  await message.delConfirm()
   const ids = multipleSelection.value?.map((item) => item.id)
   await PermissionApi.deletePermissionBatch({
     bizType: props.bizType,
     bizId: props.bizId,
     ids
   })
-  message.success('删除成功')
 }
 
+/** 添加团队成员 */
+const openForm = () => {
+  formRef.value?.open('create', props.bizType, props.bizId!)
+}
+
+// 校验负责人权限和编辑权限
+const validateOwnerUser = ref(false)
+const validateWrite = ref(false)
+const isPool = ref(false)
+watch(
+  list,
+  (newArr) => {
+    isPool.value = false
+    if (newArr?.length > 0) {
+      isPool.value = !list.value.some(
+        (item) => item.level === PermissionApi.PermissionLevelEnum.OWNER
+      )
+      validateOwnerUser.value = false
+      validateWrite.value = false
+      const userId = userStore.getUser?.id
+      list.value
+        .filter((item) => item.userId === userId)
+        .forEach((item) => {
+          if (item.level === PermissionApi.PermissionLevelEnum.OWNER) {
+            validateOwnerUser.value = true
+            validateWrite.value = true
+          } else if (item.level === PermissionApi.PermissionLevelEnum.WRITE) {
+            validateWrite.value = true
+          }
+        })
+    } else {
+      isPool.value = true
+    }
+  },
+  {
+    immediate: true
+  }
+)
+
+defineExpose({ openForm, validateOwnerUser, validateWrite, isPool })
 /** 退出团队 */
-const userStore = useUserStoreWithOut() // 用户信息缓存
 const handleQuit = async () => {
   const permission = list.value.find(
-    (item) => item.userId === userStore.getUser.id && item.level === PermissionLevelEnum.OWNER
+    (item) =>
+      item.userId === userStore.getUser.id && item.level === PermissionApi.PermissionLevelEnum.OWNER
   )
   if (permission) {
     message.warning('负责人不能退出团队!')
     return
   }
-  await message.confirm('确认退出团队吗?')
   const userPermission = list.value.find((item) => item.userId === userStore.getUser.id)
-  await PermissionApi.deleteSelfPermission(userPermission?.id)
+  if (userPermission) {
+    await PermissionApi.deleteSelfPermission(userPermission.id!)
+  }
 }
 
-/** 监听打开的 bizId + bizType,从而加载最新的列表 */
 watch(
-  () => [props.bizId, props.bizType],
-  (val) => {
-    if (!val[0]) {
+  () => props.bizId,
+  (bizId) => {
+    if (!bizId) {
       return
     }
     getList()

+ 121 - 0
src/views/crm/permission/components/TransferForm.vue

@@ -0,0 +1,121 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="30%">
+    <el-form
+      ref="formRef"
+      v-loading="formLoading"
+      :model="formData"
+      :rules="formRules"
+      label-width="150px"
+    >
+      <el-form-item label="选择新负责人" prop="newOwnerUserId">
+        <el-select v-model="formData.newOwnerUserId">
+          <el-option
+            v-for="item in userOptions"
+            :key="item.id"
+            :label="item.nickname"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="老负责人">
+        <el-radio-group
+          v-model="oldOwnerHandler"
+          @change="formData.oldOwnerPermissionLevel = undefined"
+        >
+          <el-radio :label="false" size="large">移除</el-radio>
+          <el-radio :label="true" size="large">加入团队</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item v-if="oldOwnerHandler" label="老负责人权限级别" prop="oldOwnerPermissionLevel">
+        <el-radio-group v-model="formData.oldOwnerPermissionLevel">
+          <template
+            v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PERMISSION_LEVEL)"
+            :key="dict.value"
+          >
+            <el-radio v-if="dict.value != PermissionLevelEnum.OWNER" :label="dict.value">
+              {{ dict.label }}
+            </el-radio>
+          </template>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+import * as UserApi from '@/api/system/user'
+import type { TransferReqVO } from '@/api/crm/customer'
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { PermissionLevelEnum } from '@/api/crm/permission'
+
+defineOptions({ name: 'CrmTransferForm' })
+
+const message = useMessage() // 消息弹窗
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
+const oldOwnerHandler = ref(false) // 老负责人的处理方式
+const formData = ref<TransferReqVO>({
+  id: undefined, // 客户编号
+  newOwnerUserId: undefined, // 新负责人的用户编号
+  oldOwnerPermissionLevel: undefined // 老负责人加入团队后的权限级别
+})
+const formRules = reactive({
+  newOwnerUserId: [{ required: true, message: '新负责人不能为空', trigger: 'blur' }],
+  oldOwnerPermissionLevel: [
+    { required: true, message: '老负责人加入团队后的权限级别不能为空', trigger: 'blur' }
+  ]
+})
+const formRef = ref() // 表单 Ref
+const transferFuncRef = ref<Function>(() => {}) // 转移所需回调
+
+/** 打开弹窗 */
+const open = async (title: string, bizId: number, transferFunc: Function) => {
+  dialogVisible.value = true
+  dialogTitle.value = title
+  transferFuncRef.value = transferFunc
+  resetForm()
+  formData.value.id = bizId
+}
+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
+    await transferFuncRef.value(unref(data))
+    message.success(dialogTitle.value + '成功')
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formRef.value?.resetFields()
+  formData.value = {
+    id: undefined, // 客户编号
+    newOwnerUserId: undefined, // 新负责人的用户编号
+    oldOwnerPermissionLevel: undefined // 老负责人加入团队后的权限级别
+  }
+}
+onMounted(async () => {
+  // 获得用户列表
+  // TODO 芋艿:用户列表的选择组件
+  userOptions.value = await UserApi.getSimpleUserList()
+})
+</script>