Pre Merge pull request !159 from xingyu/dev

pull/159/MERGE
xingyu 2025-06-27 14:14:26 +00:00 committed by Gitee
commit 9e8cd2020e
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
11 changed files with 160 additions and 110 deletions

View File

@ -305,7 +305,7 @@ onMounted(async () => {
<template>
<Layout.Sider
width="280px"
class="!bg-primary-foreground conversation-container relative flex h-full flex-col justify-between overflow-hidden p-4"
class="conversation-container relative flex h-full flex-col justify-between overflow-hidden p-4"
>
<Drawer />
<!-- 左顶部对话 -->
@ -358,7 +358,9 @@ onMounted(async () => {
<div
class="conversation flex cursor-pointer flex-row items-center justify-between rounded-lg px-2 leading-10"
:class="[
conversation.id === activeConversationId ? 'bg-gray-100' : '',
conversation.id === activeConversationId
? 'bg-primary-200'
: '',
]"
>
<div class="title-wrapper flex items-center">
@ -418,7 +420,7 @@ onMounted(async () => {
<!-- 左底部工具栏 -->
<div
class="absolute bottom-1 left-0 right-0 mb-4 flex items-center justify-between bg-gray-50 px-5 leading-9 text-gray-400 shadow-sm"
class="bg-card absolute bottom-1 left-0 right-0 mb-4 flex items-center justify-between px-5 leading-9 text-gray-400 shadow-sm"
>
<div
class="flex cursor-pointer items-center text-gray-400"

View File

@ -79,7 +79,7 @@ async function handleTabsScroll() {
}"
>
<!-- 更多操作 -->
<div v-if="showMore" class="absolute right-3 top-0">
<div v-if="showMore" class="absolute right-2 top-0">
<Dropdown>
<Button type="link">
<IconifyIcon icon="lucide:ellipsis-vertical" />
@ -89,7 +89,7 @@ async function handleTabsScroll() {
<Menu.Item @click="handleMoreClick(['edit', role])">
<div class="flex items-center">
<IconifyIcon icon="lucide:edit" color="#787878" />
<span>编辑</span>
<span class="text-primary">编辑</span>
</div>
</Menu.Item>
<Menu.Item @click="handleMoreClick(['delete', role])">
@ -108,12 +108,12 @@ async function handleTabsScroll() {
<Avatar :src="role.avatar" class="h-10 w-10 overflow-hidden" />
</div>
<div class="ml-2 w-full">
<div class="ml-2 w-4/5">
<div class="h-20">
<div class="max-w-36 text-lg font-bold text-gray-600">
<div class="max-w-32 text-lg font-bold">
{{ role.name }}
</div>
<div class="mt-2 text-sm text-gray-400">
<div class="mt-2 text-sm">
{{ role.description }}
</div>
</div>

View File

@ -174,7 +174,7 @@ onMounted(async () => {
<template>
<Drawer>
<Layout
class="absolute inset-0 flex h-full w-full flex-col overflow-hidden bg-white"
class="bg-card absolute inset-0 flex h-full w-full flex-col overflow-hidden"
>
<FormModal @success="handlerAddRoleSuccess" />

View File

@ -495,6 +495,7 @@ onMounted(async () => {
<Layout class="absolute left-0 top-0 m-4 h-full w-full flex-1">
<!-- 左侧对话列表 -->
<ConversationList
class="!bg-card"
:active-id="activeConversationId as any"
ref="conversationListRef"
@on-conversation-create="handleConversationCreateSuccess"
@ -504,9 +505,9 @@ onMounted(async () => {
/>
<!-- 右侧详情部分 -->
<Layout class="mx-4 bg-white">
<Layout class="bg-card mx-4">
<Layout.Header
class="flex items-center justify-between !bg-gray-50 shadow-none"
class="!bg-card border-border flex items-center justify-between border-b"
>
<div class="text-lg font-bold">
{{ activeConversation?.title ? activeConversation?.title : '对话' }}
@ -565,9 +566,9 @@ onMounted(async () => {
</div>
</Layout.Content>
<Layout.Footer class="m-0 flex flex-col !bg-white p-0">
<Layout.Footer class="!bg-card m-0 flex flex-col p-0">
<form
class="my-5 mb-5 mt-2 flex flex-col rounded-xl border border-gray-200 px-2 py-2.5"
class="border-border my-5 mb-5 mt-2 flex flex-col rounded-xl border px-2 py-2.5"
>
<textarea
class="box-border h-24 resize-none overflow-auto border-none px-0 py-1 focus:outline-none"

View File

@ -90,9 +90,9 @@ onMounted(async () => {
<template>
<Page auto-content-height>
<div class="bg-card absolute inset-0 flex h-full w-full flex-row">
<div class="left-0 flex w-96 flex-col p-4">
<div class="segmented flex justify-center">
<div class="absolute inset-0 m-4 flex h-full w-full flex-row">
<div class="bg-card left-0 mr-4 flex w-96 flex-col rounded-lg p-4">
<div class="flex justify-center">
<Segmented
v-model:value="selectPlatform"
:options="platformOptions"
@ -125,7 +125,7 @@ onMounted(async () => {
/>
</div>
</div>
<div class="bg-card ml-4 flex-1">
<div class="bg-card flex-1">
<ImageList ref="imageListRef" @on-regeneration="handleRegeneration" />
</div>
</div>

View File

@ -123,8 +123,10 @@ function submit() {
<template>
<DefineTab v-slot="{ active, text, itemClick }">
<span
:class="active ? 'text-black shadow-md' : 'hover:bg-gray-200'"
class="relative z-10 inline-block w-1/2 cursor-pointer rounded-full text-center leading-7 text-gray-400 hover:text-black"
:class="
active ? 'bg-primary-600 text-white shadow-md' : 'hover:bg-primary-200'
"
class="relative z-10 inline-block w-1/2 cursor-pointer rounded-full text-center leading-7 hover:text-black"
@click="itemClick"
>
{{ text }}
@ -136,7 +138,7 @@ function submit() {
<span>{{ label }}</span>
<span
v-if="hint"
class="flex cursor-pointer select-none items-center text-xs text-purple-500"
class="text-primary-500 flex cursor-pointer select-none items-center text-xs"
@click="hintClick"
>
<IconifyIcon icon="lucide:circle-help" />
@ -145,14 +147,14 @@ function submit() {
</h3>
</DefineLabel>
<div class="flex flex-col" v-bind="$attrs">
<div class="flex w-full justify-center bg-gray-50 pt-2">
<div class="z-10 w-72 rounded-full bg-gray-200 p-1">
<div class="bg-card flex w-full justify-center pt-2">
<div class="bg-card z-10 w-72 rounded-full p-1">
<div
:class="
selectedTab === AiWriteTypeEnum.REPLY &&
'after:translate-x-[100%] after:transform'
"
class="relative flex items-center after:absolute after:left-0 after:top-0 after:block after:h-7 after:w-1/2 after:rounded-full after:bg-white after:transition-transform after:content-['']"
class="after:bg-card relative flex items-center after:absolute after:left-0 after:top-0 after:block after:h-7 after:w-1/2 after:rounded-full after:transition-transform after:content-['']"
>
<ReuseTab
v-for="tab in tabs"
@ -166,7 +168,7 @@ function submit() {
</div>
</div>
<div
class="box-border h-full w-96 flex-grow overflow-y-auto bg-gray-50 px-7 pb-2 lg:block"
class="bg-card box-border h-full w-96 flex-grow overflow-y-auto px-7 pb-2 lg:block"
>
<div>
<template v-if="selectedTab === 1">
@ -233,11 +235,7 @@ function submit() {
<Button :disabled="isWriting" class="mr-2" @click="reset">
重置
</Button>
<Button
:loading="isWriting"
class="bg-purple-500 text-white"
@click="submit"
>
<Button type="primary" :loading="isWriting" @click="submit">
生成
</Button>
</div>

View File

@ -54,22 +54,18 @@ watch(copied, (val) => {
});
</script>
<template>
<Card class="my-card flex h-full flex-col">
<Card class="flex h-full flex-col">
<template #title>
<h3 class="m-0 flex shrink-0 items-center justify-between px-7">
<span>预览</span>
<!-- 展示在右上角 -->
<Button
class="flex bg-purple-500 text-white"
type="primary"
v-show="showCopy"
@click="copyContent"
size="small"
>
<template #icon>
<div class="flex items-center justify-center">
<IconifyIcon icon="lucide:copy" />
</div>
</template>
复制
</Button>
</h3>
@ -79,7 +75,7 @@ watch(copied, (val) => {
class="hide-scroll-bar box-border h-full overflow-y-auto"
>
<div
class="relative box-border min-h-full w-full flex-grow bg-white p-3 sm:p-7"
class="bg-card relative box-border min-h-full w-full flex-grow p-3 sm:p-7"
>
<!-- 终止生成内容的按钮 -->
<Button

View File

@ -21,8 +21,10 @@ const emits = defineEmits<{
<span
v-for="tag in props.tags"
:key="tag.value"
class="mb-2 cursor-pointer rounded border-2 border-solid border-gray-200 bg-gray-200 px-1 text-xs leading-6"
:class="modelValue === tag.value && '!border-purple-500 !text-purple-500'"
class="bg-card border-card-100 mb-2 cursor-pointer rounded border-2 border-solid px-1 text-xs leading-6"
:class="
modelValue === tag.value && '!border-primary-500 !text-primary-500'
"
@click="emits('update:modelValue', tag.value)"
>
{{ tag.label }}

View File

@ -66,10 +66,10 @@ function reset() {
<template>
<Page auto-content-height>
<div class="absolute bottom-0 left-0 right-0 top-0 flex">
<div class="absolute bottom-0 left-0 right-0 top-0 m-4 flex">
<Left
:is-writing="isWriting"
class="h-full"
class="mr-4 h-full rounded-lg"
@submit="submit"
@reset="reset"
@example="handleExampleClick"

View File

@ -8,13 +8,20 @@ import { useRoute, useRouter } from 'vue-router';
import { confirm, Page, useVbenModal } from '@vben/common-ui';
import { useTabs } from '@vben/hooks';
import { Button, Card, message, Tabs } from 'ant-design-vue';
import { Card, message, Tabs } from 'ant-design-vue';
import { getCustomer, updateCustomerDealStatus } from '#/api/crm/customer';
import {
getCustomer,
lockCustomer,
putCustomerPool,
receiveCustomer,
updateCustomerDealStatus,
} from '#/api/crm/customer';
import { getOperateLogPage } from '#/api/crm/operateLog';
import { BizTypeEnum } from '#/api/crm/permission';
import { useDescription } from '#/components/description';
import { AsyncOperateLog } from '#/components/operate-log';
import { ACTION_ICON, TableAction } from '#/components/table-action';
import { BusinessDetailsList } from '#/views/crm/business';
import { ContactDetailsList } from '#/views/crm/contact';
import { ContractDetailsList } from '#/views/crm/contract';
@ -99,18 +106,45 @@ function handleTransfer() {
}
/** 锁定客户 */
function handleLock() {
transferModalApi.setData({ id: customerId.value }).open();
}
/** 解锁客户 */
function handleUnlock() {
transferModalApi.setData({ id: customerId.value }).open();
function handleLock(lockStatus: boolean): Promise<boolean | undefined> {
return new Promise((resolve, reject) => {
confirm({
content: `确定锁定客户【${customer.value.name}】吗?`,
})
.then(async () => {
const res = await lockCustomer(customerId.value, lockStatus);
if (res) {
message.success(lockStatus ? '锁定客户成功' : '解锁客户成功');
resolve(true);
} else {
reject(new Error(lockStatus ? '锁定客户失败' : '解锁客户失败'));
}
})
.catch(() => {
reject(new Error('取消操作'));
});
});
}
/** 领取客户 */
function handleReceive() {
transferModalApi.setData({ id: customerId.value }).open();
function handleReceive(): Promise<boolean | undefined> {
return new Promise((resolve, reject) => {
confirm({
content: `确定领取客户【${customer.value.name}】吗?`,
})
.then(async () => {
const res = await receiveCustomer([customerId.value]);
if (res) {
message.success('领取客户成功');
resolve(true);
} else {
reject(new Error('领取客户失败'));
}
})
.catch(() => {
reject(new Error('取消操作'));
});
});
}
/** 分配客户 */
@ -119,8 +153,24 @@ function handleDistributeForm() {
}
/** 客户放入公海 */
function handlePutPool() {
transferModalApi.setData({ id: customerId.value }).open();
function handlePutPool(): Promise<boolean | undefined> {
return new Promise((resolve, reject) => {
confirm({
content: `确定将客户【${customer.value.name}】放入公海吗?`,
})
.then(async () => {
const res = await putCustomerPool(customerId.value);
if (res) {
message.success('放入公海成功');
resolve(true);
} else {
reject(new Error('放入公海失败'));
}
})
.catch(() => {
reject(new Error('取消操作'));
});
});
}
/** 更新成交状态操作 */
@ -161,61 +211,62 @@ onMounted(() => {
<TransferModal @success="loadCustomerDetail" />
<DistributeModal @success="loadCustomerDetail" />
<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"
type="primary"
@click="handleReceive"
>
领取
</Button>
<Button
v-if="!customer.ownerUserId"
type="primary"
@click="handleDistributeForm"
>
分配
</Button>
<Button
v-if="customer.ownerUserId && permissionListRef?.validateOwnerUser"
@click="handlePutPool"
>
放入公海
</Button>
</div>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.edit'),
type: 'primary',
icon: ACTION_ICON.EDIT,
auth: ['crm:customer:update'],
ifShow: permissionListRef?.validateWrite,
onClick: handleEdit,
},
{
label: '转移',
type: 'primary',
ifShow: permissionListRef?.validateOwnerUser,
onClick: handleTransfer,
},
{
label: '更改成交状态',
type: 'default',
ifShow: permissionListRef?.validateWrite,
onClick: handleUpdateDealStatus,
},
{
label: '锁定',
type: 'default',
ifShow:
!customer.lockStatus && permissionListRef?.validateOwnerUser,
onClick: handleLock.bind(null, true),
},
{
label: '解锁',
type: 'default',
ifShow: customer.lockStatus && permissionListRef?.validateOwnerUser,
onClick: handleLock.bind(null, false),
},
{
label: '领取',
type: 'primary',
ifShow: !customer.ownerUserId,
onClick: handleReceive,
},
{
label: '分配',
type: 'default',
ifShow: !customer.ownerUserId,
onClick: handleDistributeForm,
},
{
label: '放入公海',
type: 'default',
ifShow:
!!customer.ownerUserId && permissionListRef?.validateOwnerUser,
onClick: handlePutPool,
},
]"
/>
</template>
<Card class="min-h-[10%]">
<Description :data="customer" />

View File

@ -78,7 +78,7 @@ export function useFormSchema(): VbenFormSchema[] {
options: contracts.map((item) => ({
label: item.name,
value: item.id,
disabled: item.auditStatus === 20,
disabled: item.auditStatus !== 20,
})),
placeholder: '请选择合同',
} as any;