feat: 完善部分详情

pull/125/head^2
xingyu4j 2025-06-03 20:49:59 +08:00
parent 6edd0826a6
commit 4f219881c2
6 changed files with 247 additions and 20 deletions

View File

@ -1,5 +1,3 @@
import type { PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace CrmPermissionApi {
@ -57,7 +55,7 @@ export enum PermissionLevelEnum {
/** 获得数据权限列表(查询团队成员列表) */
export function getPermissionList(params: CrmPermissionApi.PermissionListReq) {
return requestClient.get<PageResult<CrmPermissionApi.Permission>>(
return requestClient.get<CrmPermissionApi.Permission[]>(
'/crm/permission/list',
{ params },
);

View File

@ -5,16 +5,18 @@ import { defineAsyncComponent, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui';
import { useTabs } from '@vben/hooks';
import { ArrowLeft } from '@vben/icons';
import { Button, Card, Modal, Tabs } from 'ant-design-vue';
import { getClue, transformClue } from '#/api/crm/clue';
import { BizTypeEnum } from '#/api/crm/permission';
import { useDescription } from '#/components/description';
import { PermissionList, TransferForm } from '#/views/crm/permission';
import { useDetailSchema } from '../data';
import ClueForm from './form.vue';
import TransferForm from './transfer.vue';
const ClueDetailsInfo = defineAsyncComponent(() => import('./detail-info.vue'));
@ -22,6 +24,7 @@ const loading = ref(false);
const route = useRoute();
const router = useRouter();
const tabs = useTabs();
const clueId = ref(0);
@ -58,6 +61,7 @@ async function loadClueDetail() {
/** 返回列表页 */
function handleBack() {
tabs.closeCurrentTab();
router.push('/crm/clue');
}
@ -68,7 +72,7 @@ function handleEdit() {
/** 转移线索 */
function handleTransfer() {
transferModalApi.setData({ id: clueId }).open();
transferModalApi.setData({ bizType: BizTypeEnum.CRM_CLUE }).open();
}
/** 转化为客户 */
@ -141,7 +145,13 @@ onMounted(async () => {
<ClueDetailsInfo :clue="clue" />
</Tabs.TabPane>
<Tabs.TabPane tab="团队成员" key="3">
<div>团队成员</div>
<PermissionList
ref="permissionListRef"
:biz-id="clue.id!"
:biz-type="BizTypeEnum.CRM_CLUE"
:show-action="true"
@quit-team="handleBack"
/>
</Tabs.TabPane>
<Tabs.TabPane tab="操作日志" key="4">
<div>操作日志</div>

View File

@ -0,0 +1,5 @@
<script lang="ts" setup></script>
<template>
<div>detail-info</div>
</template>

View File

@ -1,7 +1,209 @@
<script lang="ts" setup></script>
<script setup lang="ts">
import type { CrmCustomerApi } from '#/api/crm/customer';
import type { SystemOperateLogApi } from '#/api/system/operate-log';
import { defineAsyncComponent, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Page } from '@vben/common-ui';
import { Button, Card, Modal, Tabs } from 'ant-design-vue';
import { getCustomer, updateCustomerDealStatus } from '#/api/crm/customer';
import { getOperateLogPage } from '#/api/crm/operateLog';
import { BizTypeEnum } from '#/api/crm/permission';
import { useDescription } from '#/components/description';
import { OperateLog } from '#/components/operate-log';
import { useDetailSchema } from '../data';
const CustomerDetailsInfo = defineAsyncComponent(
() => import('./detail-info.vue'),
);
const loading = ref(false);
const route = useRoute();
const router = useRouter();
const customerId = ref(0);
const customer = ref<CrmCustomerApi.Customer>({} as CrmCustomerApi.Customer);
const permissionListRef = ref(); // Ref
const [Description] = useDescription({
componentProps: {
bordered: false,
column: 4,
class: 'mx-4',
},
schema: useDetailSchema(),
});
/** 加载详情 */
async function loadCustomerDetail() {
loading.value = true;
customerId.value = Number(route.params.id);
const data = await getCustomer(customerId.value);
await getOperateLog();
customer.value = data;
loading.value = false;
}
/** 编辑 */
function handleEdit() {
// formModalApi.setData({ id: clueId }).open();
}
/** 转移线索 */
function handleTransfer() {
// transferModalApi.setData({ id: clueId }).open();
}
/** 锁定客户 */
function handleLock() {
// transferModalApi.setData({ id: clueId }).open();
}
/** 解锁客户 */
function handleUnlock() {
// transferModalApi.setData({ id: clueId }).open();
}
/** 领取客户 */
function handleReceive() {
// transferModalApi.setData({ id: clueId }).open();
}
/** 分配客户 */
function handleDistributeForm() {
// transferModalApi.setData({ id: clueId }).open();
}
/** 客户放入公海 */
function handlePutPool() {
// transferModalApi.setData({ id: clueId }).open();
}
/** 更新成交状态操作 */
async function handleUpdateDealStatus() {
const dealStatus = !customer.value.dealStatus;
try {
await Modal.confirm({
title: '提示',
content: `确定更新成交状态为【${dealStatus ? '已成交' : '未成交'}】吗?`,
});
await updateCustomerDealStatus(customerId.value, dealStatus);
Modal.success({
title: '成功',
content: '更新成交状态成功',
});
await loadCustomerDetail();
} catch {
//
}
}
/** 获取操作日志 */
const logList = ref<SystemOperateLogApi.OperateLog[]>([]); //
async function getOperateLog() {
if (!customerId.value) {
return;
}
const data = await getOperateLogPage({
bizType: BizTypeEnum.CRM_CUSTOMER,
bizId: customerId.value,
});
logList.value = data.list;
}
//
onMounted(async () => {
await loadCustomerDetail();
});
</script>
<template>
<div>
<p>待完成</p>
</div>
<Page auto-content-height :title="customer?.name" :loading="loading">
<template #extra>
<div class="flex items-center gap-2">
<Button
v-if="permissionListRef?.validateWrite"
type="primary"
@click="handleEdit"
v-access:code="['crm:customer:update']"
>
{{ $t('ui.actionTitle.edit') }}
</Button>
<Button
v-if="permissionListRef?.validateOwnerUser"
type="primary"
@click="handleTransfer"
>
转移
</Button>
<Button
v-if="permissionListRef?.validateWrite"
@click="handleUpdateDealStatus"
>
更改成交状态
</Button>
<Button
v-if="customer.lockStatus && permissionListRef?.validateOwnerUser"
@click="handleUnlock"
>
解锁
</Button>
<Button
v-if="!customer.lockStatus && permissionListRef?.validateOwnerUser"
@click="handleLock"
>
锁定
</Button>
<Button v-if="!customer.ownerUserId" @click="handleReceive">
领取
</Button>
<Button v-if="!customer.ownerUserId" @click="handleDistributeForm">
分配
</Button>
<Button
v-if="customer.ownerUserId && permissionListRef?.validateOwnerUser"
@click="handlePutPool"
>
放入公海
</Button>
</div>
</template>
<Card>
<Description :data="customer" />
</Card>
<Card class="mt-4">
<Tabs>
<Tabs.TabPane tab="跟进记录" key="1">
<div>跟进记录</div>
</Tabs.TabPane>
<Tabs.TabPane tab="基本信息" key="2">
<CustomerDetailsInfo />
</Tabs.TabPane>
<Tabs.TabPane tab="联系人" key="3">
<div>联系人</div>
</Tabs.TabPane>
<Tabs.TabPane tab="团队成员" key="4">
<div>团队成员</div>
</Tabs.TabPane>
<Tabs.TabPane tab="商机" key="5">
<div>商机</div>
</Tabs.TabPane>
<Tabs.TabPane tab="合同" key="6">
<div>合同</div>
</Tabs.TabPane>
<Tabs.TabPane tab="回款" key="7">
<div>回款</div>
</Tabs.TabPane>
<Tabs.TabPane tab="操作日志" key="8">
<OperateLog :log-list="logList" />
</Tabs.TabPane>
</Tabs>
</Card>
</Page>
</template>

View File

@ -114,7 +114,8 @@ const [Modal, modalApi] = useVbenModal({
}
modalApi.lock();
//
const data = (await formApi.getValues()) as CrmPermissionApi.Permission;
let data = (await formApi.getValues()) as CrmPermissionApi.Permission;
data = Object.assign(data, formData.value);
try {
await (formData.value?.ids
? updatePermission(data)

View File

@ -33,6 +33,8 @@ const emits = defineEmits<{
(e: 'quitTeam'): void;
}>();
const gridData = ref<CrmPermissionApi.Permission[]>([]);
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
destroyOnClose: true,
@ -67,6 +69,14 @@ function handleCreate() {
}
function handleEdit() {
if (checkedIds.value.length === 0) {
message.error('请先选择团队成员后操作!');
return;
}
if (checkedIds.value.length > 1) {
message.error('只能选择一个团队成员进行编辑!');
return;
}
formModalApi
.setData({
bizType: props.bizType,
@ -112,7 +122,7 @@ async function handleQuit() {
.getData()
.find(
(item) =>
item.id === userStore.userInfo?.userId &&
item.id === userStore.userInfo?.id &&
item.level === PermissionLevelEnum.OWNER,
);
if (permission) {
@ -122,7 +132,7 @@ async function handleQuit() {
const userPermission = gridApi.grid
.getData()
.find((item) => item.id === userStore.userInfo?.userId);
.find((item) => item.id === userStore.userInfo?.id);
if (!userPermission) {
message.warning('你不是团队成员!');
return;
@ -176,6 +186,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
bizId: props.bizId,
bizType: props.bizType,
});
gridData.value = res;
return res;
},
},
@ -201,19 +212,19 @@ defineExpose({
validateWrite,
isPool,
});
watch(
() => gridApi.grid.getData(),
() => gridData.value,
(data) => {
isPool.value = false;
if (data.length > 0) {
isPool.value = gridApi.grid
.getData()
.some((item) => item.level === PermissionLevelEnum.OWNER);
isPool.value = data.some(
(item) => item.level === PermissionLevelEnum.OWNER,
);
validateOwnerUser.value = false;
validateWrite.value = false;
const userId = userStore.userInfo?.userId;
gridApi.grid
.getData()
const userId = userStore.userInfo?.id;
gridData.value
.filter((item) => item.userId === userId)
.forEach((item) => {
if (item.level === PermissionLevelEnum.OWNER) {