diff --git a/src/api/crm/permission/index.ts b/src/api/crm/permission/index.ts new file mode 100644 index 00000000..f71e2bfc --- /dev/null +++ b/src/api/crm/permission/index.ts @@ -0,0 +1,32 @@ +import request from '@/config/axios' + +export interface PermissionVO { + id: number // 数据权限编号 + userId: number // 用户编号 + bizType: number // Crm 类型 + bizId: number // Crm 类型数据编号 + level: number // 权限级别 + deptName: string // 部门名称 + nickname: string // 用户昵称 + postNames: string // 岗位名称数组 +} + +// 查询团队成员列表 +export const getPermissionList = async (params) => { + return await request.get({ url: `/crm/permission/list`, params }) +} + +// 新增团队成员 +export const createPermission = async (data: PermissionVO) => { + return await request.post({ url: `/crm/permission/add`, data }) +} + +// 修改团队成员 +export const updatePermission = async (data: PermissionVO) => { + return await request.put({ url: `/crm/permission/update`, data }) +} + +// 删除团队成员 +export const deletePermission = async (params) => { + return await request.delete({ url: '/crm/permission/delete', params }) +} diff --git a/src/views/crm/components/CrmPermissionForm.vue b/src/views/crm/components/CrmPermissionForm.vue new file mode 100644 index 00000000..1d217323 --- /dev/null +++ b/src/views/crm/components/CrmPermissionForm.vue @@ -0,0 +1,106 @@ +<template> + <Dialog v-model="dialogVisible" :title="dialogTitle"> + <el-form + ref="formRef" + v-loading="formLoading" + :model="formData" + :rules="formRules" + label-width="100px" + > + <el-form-item v-if="formType === 'create'" label="选择人员" prop="userId"> + <el-select v-model="formData.userId"> + <el-option + v-for="item in userOptions" + :key="parseInt(item.id)" + :label="item.nickname" + :value="parseInt(item.id)" + /> + </el-select> + </el-form-item> + <el-form-item label="权限级别" prop="level"> + <el-radio-group v-model="formData.level"> + <el-radio label="2">只读</el-radio> + <el-radio label="3">读写</el-radio> + </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' + +defineOptions({ name: 'CrmPermissionForm' }) +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 userOptions = ref<UserApi.UserVO[]>([]) // 用户列表 +const formData = ref({ + userId: undefined, // 用户编号 + bizType: undefined, // Crm 类型 + bizId: undefined, // Crm 类型数据编号 + level: undefined // 权限级别 +}) +const formRules = reactive({ + userId: [{ required: true, message: '人员不能为空', trigger: 'blur' }], + level: [{ required: true, message: '权限级别不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: 'create' | 'update', bizType: number, bizId: number, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + '团队成员' + formType.value = type + resetForm(bizType, bizId) + // 修改时,设置数据 + if (id) { + formData.value.id = id + } + // 获得用户列表 + userOptions.value = await UserApi.getSimpleUserList() +} +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 + if (formType.value === 'create') { + message.success(t('common.createSuccess')) + } else { + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = (bizType: number, bizId: number) => { + formData.value = { + userId: undefined, // 用户编号 + bizType, // Crm 类型 + bizId, // Crm 类型数据编号 + level: undefined // 权限级别 + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/crm/components/CrmTeamList.vue b/src/views/crm/components/CrmTeamList.vue new file mode 100644 index 00000000..824409b8 --- /dev/null +++ b/src/views/crm/components/CrmTeamList.vue @@ -0,0 +1,116 @@ +<template> + <!-- 操作栏 --> + <el-row justify="end"> + <el-button type="primary" @click="handleAdd"> + <Icon class="mr-5px" icon="ep:plus" /> + 新增 + </el-button> + <el-button @click="handleEdit"> + <Icon class="mr-5px" icon="ep:edit" /> + 编辑 + </el-button> + <el-button @click="handleRemove"> + <Icon class="mr-5px" icon="ep:delete" /> + 移除 + </el-button> + <el-button type="danger" @click="handleQuit"> 退出团队</el-button> + </el-row> + <!-- 团队成员展示 --> + <el-table + 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="mobile" /> + <el-table-column align="center" label="部门" prop="detailAddress" /> + <el-table-column align="center" label="岗位" prop="detailAddress" /> + <el-table-column align="center" label="权限级别" prop="creatorName" /> + <el-table-column :formatter="dateFormatter" align="center" label="加入时间" prop="createTime" /> + </el-table> + <CrmPermissionForm ref="crmPermissionFormRef" /> +</template> +<script lang="ts" setup> +import { dateFormatter } from '@/utils/formatTime' +import { ElTable } from 'element-plus' +import * as PermissionApi from '@/api/crm/permission' +import { useUserStoreWithOut } from '@/store/modules/user' +import CrmPermissionForm from './CrmPermissionForm.vue' + +defineOptions({ name: 'CrmTeam' }) +const props = defineProps<{ + bizType: number + bizId: number +}>() +const loading = ref(true) // 列表的加载中 +const list = ref<PermissionApi.PermissionVO[]>([]) // 列表的数据 +const getList = async () => { + loading.value = true + try { + const res = await PermissionApi.getPermissionList({ + bizType: props.bizType, + bizId: props.bizId + }) + list.value = res + } finally { + loading.value = false + } +} + +const multipleSelection = ref<PermissionApi.PermissionVO[]>([]) +const handleSelectionChange = (val: PermissionApi.PermissionVO[]) => { + multipleSelection.value = val +} +const message = useMessage() +const crmPermissionFormRef = ref<InstanceType<typeof CrmPermissionForm | null>>(null) +const handleEdit = () => { + if (multipleSelection.value?.length === 0) { + message.warning('请先选择团队成员后操作!') + return + } + const ids = multipleSelection.value?.map((item) => item.id) + crmPermissionFormRef.value?.open('update', props.bizType, props.bizId, ids[0]) +} +const handleRemove = async () => { + if (multipleSelection.value?.length === 0) { + message.warning('请先选择团队成员后操作!') + return + } + await message.delConfirm() + const ids = multipleSelection.value?.map((item) => item.id) + ids?.forEach((id) => { + // TODO 还不确定要不要搞个批量删除,还是一次只能删除一个,先用循环弄一下 + PermissionApi.deletePermission({ + bizType: props.bizType, + bizId: props.bizId, + id + }) + }) +} +const handleAdd = () => { + crmPermissionFormRef.value?.open('create', props.bizType, props.bizId) +} + +const userStore = useUserStoreWithOut() +const handleQuit = () => { + const permission = list.value.find( + (item) => item.userId === userStore.getUser.id && item.level === 1 + ) + if (permission) { + message.warning('负责人不能退出团队!') + return + } +} + +watch( + () => props.bizId, + () => { + getList() + }, + { immediate: true, deep: true } +) +</script> +<style lang="scss" scoped></style> diff --git a/src/views/crm/components/index.ts b/src/views/crm/components/index.ts new file mode 100644 index 00000000..b0bf3e42 --- /dev/null +++ b/src/views/crm/components/index.ts @@ -0,0 +1,11 @@ +import CrmTeam from './CrmTeamList.vue' + +enum CrmBizTypeEnum { + CRM_LEADS = 1, // 线索 + CRM_CUSTOMER = 2, // 客户 + CRM_CONTACTS = 3, // 联系人 + CRM_BUSINESS = 5, // 商机 + CRM_CONTRACT = 6 // 合同 +} + +export { CrmTeam, CrmBizTypeEnum } diff --git a/src/views/crm/customer/index.vue b/src/views/crm/customer/index.vue index 073bf8cb..b214d7c1 100644 --- a/src/views/crm/customer/index.vue +++ b/src/views/crm/customer/index.vue @@ -2,36 +2,36 @@ <ContentWrap> <!-- 搜索工作栏 --> <el-form - class="-mb-15px" - :model="queryParams" ref="queryFormRef" :inline="true" + :model="queryParams" + class="-mb-15px" label-width="68px" > <el-form-item label="客户名称" prop="name"> <el-input v-model="queryParams.name" - placeholder="请输入客户名称" - clearable - @keyup.enter="handleQuery" class="!w-240px" + clearable + placeholder="请输入客户名称" + @keyup.enter="handleQuery" /> </el-form-item> <el-form-item label="手机" prop="mobile"> <el-input v-model="queryParams.mobile" - placeholder="请输入手机" - clearable - @keyup.enter="handleQuery" class="!w-240px" + clearable + placeholder="请输入手机" + @keyup.enter="handleQuery" /> </el-form-item> <el-form-item label="所属行业" prop="industryId"> <el-select v-model="queryParams.industryId" - placeholder="请选择所属行业" - clearable class="!w-240px" + clearable + placeholder="请选择所属行业" > <el-option v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)" @@ -44,9 +44,9 @@ <el-form-item label="客户等级" prop="level"> <el-select v-model="queryParams.level" - placeholder="请选择客户等级" - clearable class="!w-240px" + clearable + placeholder="请选择客户等级" > <el-option v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)" @@ -59,9 +59,9 @@ <el-form-item label="客户来源" prop="source"> <el-select v-model="queryParams.source" - placeholder="请选择客户来源" - clearable class="!w-240px" + clearable + placeholder="请选择客户来源" > <el-option v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)" @@ -72,19 +72,27 @@ </el-select> </el-form-item> <el-form-item> - <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> - <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> - <el-button type="primary" @click="openForm('create')" v-hasPermi="['crm:customer:create']"> - <Icon icon="ep:plus" class="mr-5px" /> 新增 + <el-button @click="handleQuery"> + <Icon class="mr-5px" icon="ep:search" /> + 搜索 + </el-button> + <el-button @click="resetQuery"> + <Icon class="mr-5px" icon="ep:refresh" /> + 重置 + </el-button> + <el-button v-hasPermi="['crm:customer:create']" type="primary" @click="openForm('create')"> + <Icon class="mr-5px" icon="ep:plus" /> + 新增 </el-button> <el-button - type="success" - plain - @click="handleExport" - :loading="exportLoading" v-hasPermi="['crm:customer:export']" + :loading="exportLoading" + plain + type="success" + @click="handleExport" > - <Icon icon="ep:download" class="mr-5px" /> 导出 + <Icon class="mr-5px" icon="ep:download" /> + 导出 </el-button> </el-form-item> </el-form> @@ -92,77 +100,77 @@ <!-- 列表 --> <ContentWrap> - <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="name" width="160" /> - <el-table-column label="所属行业" align="center" prop="industryId" width="120"> + <el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true"> + <el-table-column align="center" label="编号" prop="id" /> + <el-table-column align="center" label="客户名称" prop="name" width="160" /> + <el-table-column align="center" label="所属行业" prop="industryId" width="120"> <template #default="scope"> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" /> </template> </el-table-column> - <el-table-column label="客户来源" align="center" prop="source" width="100"> + <el-table-column align="center" label="客户来源" prop="source" width="100"> <template #default="scope"> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" /> </template> </el-table-column> - <el-table-column label="客户等级" align="center" prop="level" width="120"> + <el-table-column align="center" label="客户等级" prop="level" width="120"> <template #default="scope"> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" /> </template> </el-table-column> - <el-table-column label="手机" align="center" prop="mobile" width="120" /> - <el-table-column label="详细地址" align="center" prop="detailAddress" width="200" /> - <el-table-column label="负责人" align="center" prop="ownerUserName" /> - <el-table-column label="所属部门" align="center" prop="ownerUserDept" /> - <el-table-column label="创建人" align="center" prop="creatorName" /> + <el-table-column align="center" label="手机" prop="mobile" width="120" /> + <el-table-column align="center" label="详细地址" prop="detailAddress" width="200" /> + <el-table-column align="center" label="负责人" prop="ownerUserName" /> + <el-table-column align="center" label="所属部门" prop="ownerUserDept" /> + <el-table-column align="center" label="创建人" prop="creatorName" /> <el-table-column - label="创建时间" - align="center" - prop="createTime" :formatter="dateFormatter" + align="center" + label="创建时间" + prop="createTime" width="180px" /> - <el-table-column label="成交状态" align="center" prop="dealStatus"> + <el-table-column align="center" label="成交状态" prop="dealStatus"> <template #default="scope"> <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" /> </template> </el-table-column> <el-table-column - label="下次联系时间" - align="center" - prop="contactNextTime" :formatter="dateFormatter" + align="center" + label="下次联系时间" + prop="contactNextTime" width="180px" /> <el-table-column - label="最后跟进时间" - align="center" - prop="contactLastTime" :formatter="dateFormatter" + align="center" + label="最后跟进时间" + prop="contactLastTime" width="180px" /> - <el-table-column label="锁定状态" align="center" prop="lockStatus"> + <el-table-column align="center" label="锁定状态" prop="lockStatus"> <template #default="scope"> <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.lockStatus" /> </template> </el-table-column> <!-- TODO @wanwan 距进入公海天数 --> - <el-table-column label="操作" align="center" min-width="150" fixed="right"> + <el-table-column align="center" fixed="right" label="操作" min-width="150"> <template #default="scope"> <el-button link type="primary" @click="openDetail(scope.row.id)">详情</el-button> <el-button + v-hasPermi="['crm:customer:update']" link type="primary" @click="openForm('update', scope.row.id)" - v-hasPermi="['crm:customer:update']" > 编辑 </el-button> <el-button + v-hasPermi="['crm:customer:delete']" link type="danger" @click="handleDelete(scope.row.id)" - v-hasPermi="['crm:customer:delete']" > 删除 </el-button> @@ -171,23 +179,26 @@ </el-table> <!-- 分页 --> <Pagination - :total="total" - v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" + v-model:page="queryParams.pageNo" + :total="total" @pagination="getList" /> </ContentWrap> + <!-- TODO 方便查看效果 --> + <CrmTeam :biz-id="1" :biz-type="CrmBizTypeEnum.CRM_CUSTOMER" /> <!-- 表单弹窗:添加/修改 --> <CustomerForm ref="formRef" @success="getList" /> </template> -<script setup lang="ts"> -import { DICT_TYPE, getBoolDictOptions, getIntDictOptions } from '@/utils/dict' +<script lang="ts" setup> +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { dateFormatter } from '@/utils/formatTime' import download from '@/utils/download' import * as CustomerApi from '@/api/crm/customer' import CustomerForm from './CustomerForm.vue' +import { CrmBizTypeEnum, CrmTeam } from '@/views/crm/components' defineOptions({ name: 'CrmCustomer' })