feat(@vben/web-antd): 新增新建会话功能并优化界面展示

This commit is contained in:
Kven 2025-06-12 21:44:12 +08:00
parent 8d17c6eb9f
commit e4349221b2
13 changed files with 132 additions and 35 deletions

View File

@ -16,7 +16,7 @@ const props = withDefaults(defineProps<Props>(), {
items: () => [],
temp: () => [],
});
const emit = defineEmits(['click', 'clickMode']);
const emit = defineEmits(['click', 'clickMode', 'newConversation']);
const defaultConversationsItems = computed(() => {
return props.items.map((item) => {
return {
@ -51,6 +51,10 @@ const handleMenuClick = (item: { key: string }) => {
}
};
const handleNewConversation = () => {
emit('newConversation');
};
const onConversationClick: ConversationsProps['onActiveChange'] = (key) => {
activeKey.value = key;
@ -73,6 +77,17 @@ watch(
},
{ immediate: true },
);
watch(
() => itemsData.value,
(newVal) => {
if (newVal && newVal.length > 0) {
selectedKeys.value = [newVal[0].key]; //
handleMenuClick(newVal[0]);
}
},
{ immediate: true },
);
</script>
<template>
@ -88,6 +103,10 @@ watch(
/>
<div class="addBtn">会话</div>
<Button type="submit" class="newBtn" @click="handleNewConversation">
新建会话
</Button>
<!-- 🌟 会话管理 -->
<Conversations
:items="conversationsItems"
@ -182,4 +201,13 @@ watch(
width: calc(100% - 24px);
margin: 12px 12px 24px 12px;
}
.newBtn {
float: left;
width: calc(100% - 24px);
margin: 12px 12px 12px 12px;
padding: 5px;
border-radius: 5px;
background-color: rgba(19, 118, 163, 0.1);
}
</style>

View File

@ -110,6 +110,7 @@ const handleFileChange: AttachmentsProps['onChange'] = (info) =>
// }
const startFetching = async () => {
resultItems.value = [];
agentRequestLoading.value = true;
fetchStatus.value = 'fetching';
resultItems.value.push({
@ -192,6 +193,7 @@ watch(
() => props.itemMessage,
(newVal) => {
resultItems.value = [];
content.value = '';
if (newVal && newVal.length > 0) {
newVal.forEach((msg) => {
if (msg.role === 'user') {
@ -272,7 +274,7 @@ watch(
:typing="true"
:items="resultItems"
:roles="roles"
style="flex: 1"
style="flex: 1; padding: 10px 10px 0"
/>
<!-- 🌟 输入框 -->

View File

@ -60,6 +60,11 @@ async function handleClick(item: PPTTempItem) {
}
}
function handleNewConversation() {
//
itemMessage.value = [];
}
const currentTemp = ref<null | PPTTempItem>(null);
async function handleClickMode(item: PPTTempItem) {
@ -87,6 +92,7 @@ onMounted(() => {
:loading="loading"
@click-mode="handleClickMode"
@click="handleClick"
@new-conversation="handleNewConversation"
/>
</div>
<PptWorkView

View File

@ -40,6 +40,7 @@ const props = withDefaults(defineProps<Props>(), {
id: '',
name: '',
url: '',
description: '',
}),
runSpider: () => async () => ({
msg: '',
@ -372,7 +373,7 @@ watch(
<CardTitle class="text-lg">{{ title }}</CardTitle>
</CardHeader>
<CardContent class="flex flex-wrap pt-0">
{{ item ? item.url : '请选择左侧列表' }}
{{ props.item ? props.item.description : '请选择左侧列表' }}
</CardContent>
<CardFooter class="flex justify-end">
<RangePicker

View File

@ -30,6 +30,7 @@ const transformItems = computed(() => {
key: item.id,
label: item.name,
title: item.name,
description: item.description,
}));
});
@ -43,6 +44,7 @@ const handleMenuClick = (item: { key: string }) => {
name: selectedItem.title,
id: selectedItem.key,
url: selectedItem.url,
description: selectedItem.description,
};
emit('clickMode', transformedItem); //
}
@ -66,6 +68,16 @@ watch(
},
{ immediate: true },
);
watch(
() => itemsData.value,
(newVal) => {
if (newVal && newVal.length > 0) {
selectedKeys.value = [newVal[0].key]; //
openKeys.value = [newVal[0].key]; //
}
},
{ immediate: true },
);
</script>
<template>

View File

@ -33,6 +33,7 @@ const props = withDefaults(defineProps<Props>(), {
id: '',
name: '',
url: '',
description: '',
}),
runSpiderGz: () => async () => ({
outputs: {
@ -292,7 +293,7 @@ watch(
<CardTitle class="text-lg">{{ title }}</CardTitle>
</CardHeader>
<CardContent class="flex flex-wrap pt-0">
{{ item ? item.url : '请选择左侧列表' }}
{{ props.item ? props.item.description : '请选择左侧列表' }}
</CardContent>
<CardFooter class="flex justify-end">
<Button :disabled="!item" type="primary" @click="startFetching">

View File

@ -36,7 +36,11 @@ const getTemp = async () => {
const res = await getAppListByType(3);
if (Array.isArray(res) && res.length > 0) {
// id name temp
temp.value = res.map(({ id, name }) => ({ id, name }));
temp.value = res.map(({ id, name, description }) => ({
id,
name,
description,
}));
}
};
@ -58,6 +62,7 @@ async function handleClick(item: WordFlowItem) {
const currentTemp = ref<TempItem>({
id: '77c068fd-d5b6-4c33-97d8-db5511a09b26',
name: '全国公共资源交易平台_信息爬取',
description: 'https://ygp.gdzwfw.gov.cn/',
});
async function handleClickMode(item: TempItem) {

View File

@ -2,6 +2,7 @@ interface TempItem {
id: string;
name: string;
url?: string;
description?: string;
}
interface ResultItem {

View File

@ -18,7 +18,7 @@ const props = withDefaults(defineProps<PropsHistory>(), {
items: () => [],
temp: () => [],
});
const emit = defineEmits(['click', 'clickMode', 'delete']);
const emit = defineEmits(['click', 'clickMode', 'delete', 'newConversation']);
const defaultConversationsItems = computed(() => {
return props.items.map((item) => {
return {
@ -58,6 +58,10 @@ const onConversationClick: ConversationsProps['onActiveChange'] = (key) => {
emit('click', key);
};
const handleNewConversation = () => {
emit('newConversation');
};
const menuConfig: ConversationsProps['menu'] = (conversation) => ({
items: [
{
@ -83,6 +87,17 @@ watch(
},
{ immediate: true },
);
watch(
() => itemsData.value,
(newVal) => {
if (newVal && newVal.length > 0) {
selectedKeys.value = [newVal[0].key]; //
handleMenuClick(newVal[0]);
}
},
{ immediate: true },
);
</script>
<template>
@ -99,8 +114,10 @@ watch(
<!-- 🌟 添加会话 -->
<!-- <Button type="link" class="addBtn">会话</Button>-->
<div class="addBtn">会话</div>
<!-- 🌟 添加会话 -->
<!-- 🌟 新增新建会话按钮 -->
<Button type="submit" class="newBtn" @click="handleNewConversation">
新建会话
</Button>
<!-- 🌟 会话管理 -->
<Conversations
:items="conversationsItems"
@ -199,4 +216,12 @@ watch(
width: calc(100% - 24px);
margin: 12px 12px 24px 12px;
}
.newBtn {
float: left;
width: calc(100% - 24px);
margin: 12px 12px 12px 12px;
padding: 5px;
border-radius: 5px;
background-color: rgba(19, 118, 163, 0.1);
}
</style>

View File

@ -58,6 +58,12 @@ const props = withDefaults(defineProps<Props>(), {
},
},
}),
projectInfo: () => ({
projectName: '',
projectContext: '',
projectKeyAvoidTechOrKeyword: '',
userInitialInnovationPoint: '',
}),
});
// markdown
@ -148,12 +154,7 @@ const content = ref('');
// const conversationsItems = ref(defaultConversationsItems);
const attachedFiles = ref<AttachmentsProps['items']>([]);
const agentRequestLoading = ref(false);
const projectInfo = ref({
projectName: '',
projectContext: '',
projectKeyAvoidTechOrKeyword: '',
userInitialInnovationPoint: '',
});
const projectInfo = ref({ ...props.projectInfo });
const open = ref(false);
// const inputStatus = ref<string>('');
const fetchStatus = ref('');
@ -178,20 +179,6 @@ function openPreviewDrawer(
}
// ==================== Event ====================
// .docx URL
// function isDocxURL(str: string): boolean {
// return str.endsWith('.docx');
// }
// 使 /static/
// function extractDocxFilename(url: string): null | string {
// const match = url.match(/\/static\/([a-f0-9-]+\.docx)$/i);
// if (match) {
// return match[1]; // 9e1c421e-991c-411f-8176-6350a97e70f3.docx
// }
// return null;
// }
function extractDocxInfo(
markdownLink: string,
): null | { filename: string; url: string } {
@ -310,6 +297,7 @@ watch(
() => props.itemMessage,
(newVal) => {
resultItems.value = [];
content.value = '';
if (newVal && newVal.length > 0) {
newVal.forEach((msg) => {
if (msg.role === 'user') {
@ -372,6 +360,14 @@ watch(
},
{ deep: true },
);
watch(
() => props.projectInfo,
(newVal) => {
projectInfo.value = { ...newVal };
},
{ deep: true, immediate: true },
);
</script>
<template>
@ -429,7 +425,7 @@ watch(
v-else
:items="resultItems"
:roles="roles"
style="flex: 1"
style="flex: 1; padding: 10px"
:message-render="renderMarkdown"
/>
@ -445,12 +441,13 @@ watch(
:value="content"
class="sender"
:loading="agentRequestLoading"
:disabled="agentRequestLoading"
@submit="startFetching"
@change="(value) => (content = value)"
>
<template #prefix>
<Badge :dot="attachedFiles.length > 0 && !headerOpen">
<Button type="text" @click="() => (open = !open)">
<Button @click="() => (open = !open)">
<template #icon>
<EditOutlined />
</template>

View File

@ -23,6 +23,7 @@ const temp = ref([]);
const hitsory = ref([]);
const loading = ref(true);
const params = ref({});
const projectInfo = ref({});
const getLogs = async (appid: string) => {
loading.value = true;
@ -68,17 +69,30 @@ function handleClickMode(item: WordTempItem) {
getParameters(item.id);
}
function handleNewConversation() {
//
projectInfo.value = {
projectName: '',
projectContext: '',
projectKeyAvoidTechOrKeyword: '',
userInitialInnovationPoint: '',
};
//
itemMessage.value = [];
}
const itemMessage = ref<ResultItem[]>([]);
async function deleteLog(item: any) {
const res = await deleteChatflow({
appId: temp.value.id,
appId: currentTemp.value.id,
conversationId: item,
userId: '1562',
});
if (res.code === 0) {
if (res.message === '删除成功') {
message.success('删除成功');
getLogs(temp.value.id);
getLogs(currentTemp.value.id);
} else {
message.error('删除失败');
}
@ -96,6 +110,7 @@ async function handleClick(item: string) {
conversationId: item,
limit: '',
});
projectInfo.value = res.data[0].inputs;
itemMessage.value = [];
if (res.data.length > 0) {
res.data.forEach((msg) => {
@ -120,6 +135,7 @@ async function handleClick(item: string) {
onMounted(() => {
getTemp();
handleNewConversation();
});
</script>
@ -134,6 +150,7 @@ onMounted(() => {
@click-mode="handleClickMode"
@click="handleClick"
@delete="deleteLog"
@new-conversation="handleNewConversation"
/>
</div>
<WordWorkView
@ -141,6 +158,7 @@ onMounted(() => {
:params-data="params"
:run-chatflow="sendWord"
:item-message="itemMessage"
:project-info="projectInfo"
/>
</div>
</template>

View File

@ -48,6 +48,7 @@ interface Props {
appId: any,
context: WorkflowContext,
) => Promise<WorkflowResult>;
projectInfo: object;
}
interface PropsHistory {

View File

@ -11,8 +11,8 @@ export default defineConfig(async () => {
rewrite: (path) => path.replace(/^\/api/, ''),
// mock代理目标地址
// target: 'http://172.16.35.190:9090/api',
// target: 'http://43.139.10.64:8082/api',
target: 'http://localhost:8081/api',
target: 'http://43.139.10.64:8082/api',
// target: 'http://localhost:8081/api',
// target: 'http://192.168.3.238:8081/api',
ws: true,
},