refactor(@vben/common-ui): 优化三个工作视图的样式和功能

- 为用户和 AI 气泡添加白色背景样式
-修复文件下载时的文件名问题
- 移除或注释掉不必要的代码
- 添加对项目名称和内容的验证
-优化通知消息的显示
This commit is contained in:
Kven 2025-05-18 15:58:47 +08:00
parent 65fa961a7f
commit 4c9cc17366
4 changed files with 143 additions and 178 deletions

View File

@ -74,6 +74,11 @@ const roles: BubbleListProps['roles'] = {
user: { user: {
placement: 'end', placement: 'end',
typing: false, typing: false,
styles: {
content: {
background: '#ffffff',
},
},
avatar: { icon: h(UserOutlined), style: { background: '#87d068' } }, avatar: { icon: h(UserOutlined), style: { background: '#87d068' } },
}, },
ai: { ai: {
@ -84,6 +89,9 @@ const roles: BubbleListProps['roles'] = {
marginInlineEnd: 44, marginInlineEnd: 44,
}, },
styles: { styles: {
content: {
background: '#ffffff',
},
footer: { footer: {
width: '100%', width: '100%',
}, },
@ -232,7 +240,7 @@ const startFetching = async () => {
// <a> // <a>
const link = document.createElement('a'); const link = document.createElement('a');
link.href = result; // link.href = result; //
link.download = filename; // link.download = filename.value; //
document.body.append(link); // <a> document.body.append(link); // <a>
link.click(); // link.click(); //
link.remove(); // <a> link.remove(); // <a>
@ -307,7 +315,7 @@ watch(
// <a> // <a>
const link = document.createElement('a'); const link = document.createElement('a');
link.href = msg.content; // link.href = msg.content; //
link.download = filename; // link.download = filename.value; //
document.body.append(link); // <a> document.body.append(link); // <a>
link.click(); // link.click(); //
link.remove(); // <a> link.remove(); // <a>

View File

@ -1,40 +1,40 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ConversationsProps } from 'ant-design-x-vue'; // import type { ConversationsProps } from 'ant-design-x-vue';
//
// import type { SpiderItem } from './typing';
import type { SpiderItem } from './typing'; import { ref } from 'vue';
import { computed, ref } from 'vue';
import { Menu } from 'ant-design-vue'; import { Menu } from 'ant-design-vue';
import { Conversations } from 'ant-design-x-vue'; // import { Conversations } from 'ant-design-x-vue';
interface Props { // interface Props {
items?: SpiderItem[]; // items?: SpiderItem[];
spiderList?: SpiderItem[]; // spiderList?: SpiderItem[];
title: string; // title: string;
} // }
defineOptions({ defineOptions({
name: 'SpiderListView', name: 'SpiderListView',
}); });
const props = withDefaults(defineProps<Props>(), { // const props = withDefaults(defineProps<Props>(), {
items: () => [], // items: () => [],
spiderList: () => [], // spiderList: () => [],
}); // });
const emit = defineEmits(['click', 'clickMode']); const emit = defineEmits(['click', 'clickMode']);
const selectedKeys = ref([]); const selectedKeys = ref([]);
const openKeys = ref([]); const openKeys = ref([]);
const defaultConversationsItems = computed(() => { // const defaultConversationsItems = computed(() => {
return props.items.map((item) => { // return props.items.map((item) => {
return { // return {
key: item.id, // key: item.id,
label: item.id, // label: item.id,
}; // };
}); // });
}); // });
const conversationsItems = ref(defaultConversationsItems); // const conversationsItems = ref(defaultConversationsItems);
const activeKey = ref(defaultConversationsItems.value[0]?.key); // const activeKey = ref(defaultConversationsItems.value[0]?.key);
const itemsData = ref([ const itemsData = ref([
{ {
key: '77c068fd-d5b6-4c33-97d8-db5511a09b26', key: '77c068fd-d5b6-4c33-97d8-db5511a09b26',
@ -63,16 +63,16 @@ const handleMenuClick = (item: { key: string }) => {
} }
}; };
const onConversationClick: ConversationsProps['onActiveChange'] = (key) => { // const onConversationClick: ConversationsProps['onActiveChange'] = (key) => {
activeKey.value = key; // activeKey.value = key;
//
const matchedItem = props.items.find((item) => item.id === key); // const matchedItem = props.items.find((item) => item.id === key);
if (matchedItem) { // if (matchedItem) {
emit('click', matchedItem); // emit('click', matchedItem);
} else { // } else {
emit('click', null); // // emit('click', null); //
} // }
}; // };
</script> </script>
<template> <template>
@ -88,16 +88,16 @@ const onConversationClick: ConversationsProps['onActiveChange'] = (key) => {
/> />
<!-- 🌟 添加会话 --> <!-- 🌟 添加会话 -->
<!-- <Button type="link" class="addBtn">会话</Button>--> <!-- <Button type="link" class="addBtn">会话</Button>-->
<div class="addBtn">获取记录</div> <!-- <div class="addBtn">获取记录</div>-->
<!-- 🌟 添加会话 --> <!-- 🌟 添加会话 -->
<!-- 🌟 会话管理 --> <!-- 🌟 会话管理 -->
<Conversations <!-- <Conversations-->
:items="conversationsItems" <!-- :items="conversationsItems"-->
class="conversations" <!-- class="conversations"-->
:active-key="activeKey" <!-- :active-key="activeKey"-->
@active-change="onConversationClick" <!-- @active-change="onConversationClick"-->
/> <!-- />-->
</div> </div>
</template> </template>

View File

@ -344,82 +344,6 @@ const startFetching = async () => {
} }
isFetching.value = false; isFetching.value = false;
} }
// try {
// notification.info({
// message: '...',
// duration: 3,
// });
// const res = await props.runSpider(
// {
// publish_start_time,
// publish_end_time,
// llm_api_key: '77c068fd-d5b6-4c33-97d8-db5511a09b26',
// },
// );
// if (res.data.outputs.result) {
// //
// fetchResult.value = res.data.outputs.result;
// notification.success({
// message: '',
// duration: 3,
// });
// const { result } = res.data.outputs;
//
// const filename = ref('');
//
// if (result && isDocxURL(result)) {
// filename.value = extractDocxFilename(result);
// }
//
// // url http://47.112.173.8:6802/static/66f3cfd95e364a239d8036390db658ae.docx
// fetchResult.value = `/static/${filename.value}`;
// resultItems.value.push({
// key: resultItems.value.length + 1,
// role: 'ai',
// content: '',
// footer: h(Flex, null, [
// h(
// Button,
// {
// size: 'nomarl',
// type: 'primary',
// onClick: () => {
// openPreviewDrawer('right', filename);
// },
// },
// '',
// ),
// h(
// Button,
// {
// size: 'normal',
// type: 'primary',
// style: {
// marginLeft: '10px',
// },
// onClick: () => {
// // <a>
// const link = document.createElement('a');
// link.href = msg.content; //
// link.download = filename; //
// document.body.append(link); // <a>
// link.click(); //
// link.remove(); // <a>
// },
// },
// '',
// ),
// ]),
// });
// fetchStatus.value = 'completed';
// } else {
// fetchResult.value = '';
// message.error('');
// }
// } catch (error) {
// message.error(`${error}`);
// }
}; };
// //
@ -427,6 +351,11 @@ const roles: BubbleListProps['roles'] = {
user: { user: {
placement: 'end', placement: 'end',
typing: false, typing: false,
styles: {
content: {
background: '#ffffff',
},
},
avatar: { icon: h(UserOutlined), style: { background: '#87d068' } }, avatar: { icon: h(UserOutlined), style: { background: '#87d068' } },
}, },
ai: { ai: {
@ -437,6 +366,9 @@ const roles: BubbleListProps['roles'] = {
marginInlineEnd: 44, marginInlineEnd: 44,
}, },
styles: { styles: {
content: {
background: '#ffffff',
},
footer: { footer: {
width: '100%', width: '100%',
}, },
@ -462,6 +394,14 @@ const isFetching = ref(false);
const fetchResult = ref(''); const fetchResult = ref('');
const fetchStatus = ref(''); const fetchStatus = ref('');
// title resultItems
watch(
() => props.title,
() => {
resultItems.value = [];
},
);
// itemMessage resultItems // itemMessage resultItems
watch( watch(
() => props.itemMessage, () => props.itemMessage,

View File

@ -14,8 +14,9 @@ import { computed, h, ref, watch } from 'vue';
import { useVbenDrawer } from '@vben-core/popup-ui'; import { useVbenDrawer } from '@vben-core/popup-ui';
import { import {
CloudUploadOutlined, EditOutlined,
PaperClipOutlined, // CloudUploadOutlined,
// PaperClipOutlined,
UserOutlined, UserOutlined,
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
// import type { VNode } from 'vue'; // import type { VNode } from 'vue';
@ -27,6 +28,7 @@ import {
FormItem, FormItem,
Input, Input,
Modal, Modal,
notification,
Space, Space,
Textarea, Textarea,
Typography, Typography,
@ -147,17 +149,27 @@ const senderPromptsItems = computed(() => {
const roles: BubbleListProps['roles'] = { const roles: BubbleListProps['roles'] = {
user: { user: {
placement: 'end', placement: 'end',
variant: 'shadow',
typing: false, typing: false,
styles: {
content: {
background: '#ffffff',
},
},
avatar: { icon: h(UserOutlined), style: { background: '#87d068' } }, avatar: { icon: h(UserOutlined), style: { background: '#87d068' } },
}, },
ai: { ai: {
placement: 'start', placement: 'start',
variant: 'shadow',
typing: false, typing: false,
style: { style: {
maxWidth: 600, maxWidth: 600,
marginInlineEnd: 44, marginInlineEnd: 44,
}, },
styles: { styles: {
content: {
background: '#ffffff',
},
footer: { footer: {
width: '100%', width: '100%',
}, },
@ -260,10 +272,19 @@ function extractDocxFilename(url: string): null | string {
} }
return null; return null;
} }
const showModal = () => {
open.value = true;
};
const startFetching = async () => { const startFetching = async () => {
if (projectInfo.value.projectName === '') {
open.value = true;
return;
}
if (content.value === '') {
notification.warn({
message: '请输入项目内容',
description: '请在输入框中输入项目内容',
});
open.value = false;
return;
}
open.value = false; open.value = false;
agentRequestLoading.value = true; agentRequestLoading.value = true;
fetchStatus.value = 'fetching'; fetchStatus.value = 'fetching';
@ -328,7 +349,7 @@ const startFetching = async () => {
// <a> // <a>
const link = document.createElement('a'); const link = document.createElement('a');
link.href = answer; // link.href = answer; //
link.download = filename; // link.download = filename.value; //
document.body.append(link); // <a> document.body.append(link); // <a>
link.click(); // link.click(); //
link.remove(); // <a> link.remove(); // <a>
@ -360,8 +381,8 @@ const onPromptsItemClick: PromptsProps['onItemClick'] = (info) => {
content.value = info.data.description; content.value = info.data.description;
}; };
const handleFileChange: AttachmentsProps['onChange'] = (info) => // const handleFileChange: AttachmentsProps['onChange'] = (info) =>
(attachedFiles.value = info.fileList); // (attachedFiles.value = info.fileList);
// itemMessage resultItems // itemMessage resultItems
watch( watch(
@ -410,7 +431,7 @@ watch(
// <a> // <a>
const link = document.createElement('a'); const link = document.createElement('a');
link.href = msg.content; // link.href = msg.content; //
link.download = filename; // link.download = filename.value; //
document.body.append(link); // <a> document.body.append(link); // <a>
link.click(); // link.click(); //
link.remove(); // <a> link.remove(); // <a>
@ -446,23 +467,19 @@ watch(
placeholder="请输入项目名称" placeholder="请输入项目名称"
/> />
</FormItem> </FormItem>
<FormItem label="项目背景" name="projectContext" required> <FormItem label="项目背景" name="projectContext">
<Textarea <Textarea
v-model:value="projectInfo.projectContext" v-model:value="projectInfo.projectContext"
placeholder="请输入项目背景" placeholder="请输入项目背景"
/> />
</FormItem> </FormItem>
<FormItem <FormItem label="规避关键词" name="projectKeyAvoidTechOrKeyword">
label="规避关键词"
name="projectKeyAvoidTechOrKeyword"
required
>
<Textarea <Textarea
v-model:value="projectInfo.projectKeyAvoidTechOrKeyword" v-model:value="projectInfo.projectKeyAvoidTechOrKeyword"
placeholder="规避关键词" placeholder="规避关键词"
/> />
</FormItem> </FormItem>
<FormItem label="创新点" name="userInitialInnovationPoint" required> <FormItem label="创新点" name="userInitialInnovationPoint">
<Textarea <Textarea
v-model:value="projectInfo.userInitialInnovationPoint" v-model:value="projectInfo.userInitialInnovationPoint"
placeholder="请输入创新点" placeholder="请输入创新点"
@ -510,59 +527,59 @@ watch(
:value="content" :value="content"
class="sender" class="sender"
:loading="agentRequestLoading" :loading="agentRequestLoading"
@submit="showModal" @submit="startFetching"
@change="(value) => (content = value)" @change="(value) => (content = value)"
> >
<template #prefix> <template #prefix>
<Badge :dot="attachedFiles.length > 0 && !headerOpen"> <Badge :dot="attachedFiles.length > 0 && !headerOpen">
<Button type="text" @click="() => (headerOpen = !headerOpen)"> <Button type="text" @click="() => (open = !open)">
<template #icon> <template #icon>
<PaperClipOutlined /> <EditOutlined />
</template> </template>
</Button> </Button>
</Badge> </Badge>
</template> </template>
<template #header> <!-- <template #header>-->
<Sender.Header <!-- <Sender.Header-->
title="Attachments" <!-- title="Attachments"-->
:open="headerOpen" <!-- :open="headerOpen"-->
:styles="{ content: { padding: 0 } }" <!-- :styles="{ content: { padding: 0 } }"-->
@open-change="(open) => (headerOpen = open)" <!-- @open-change="(open) => (headerOpen = open)"-->
> <!-- >-->
<Attachments <!-- <Attachments-->
:before-upload="() => false" <!-- :before-upload="() => false"-->
:items="attachedFiles" <!-- :items="attachedFiles"-->
@change="handleFileChange" <!-- @change="handleFileChange"-->
> <!-- >-->
<template #placeholder="type"> <!-- <template #placeholder="type">-->
<Flex <!-- <Flex-->
v-if="type && type.type === 'inline'" <!-- v-if="type && type.type === 'inline'"-->
align="center" <!-- align="center"-->
justify="center" <!-- justify="center"-->
vertical <!-- vertical-->
gap="2" <!-- gap="2"-->
> <!-- >-->
<Typography.Text style="font-size: 30px; line-height: 1"> <!-- <Typography.Text style="font-size: 30px; line-height: 1">-->
<CloudUploadOutlined /> <!-- <CloudUploadOutlined />-->
</Typography.Text> <!-- </Typography.Text>-->
<Typography.Title <!-- <Typography.Title-->
:level="5" <!-- :level="5"-->
style="margin: 0; font-size: 14px; line-height: 1.5" <!-- style="margin: 0; font-size: 14px; line-height: 1.5"-->
> <!-- >-->
Upload files <!-- Upload files-->
</Typography.Title> <!-- </Typography.Title>-->
<Typography.Text type="secondary"> <!-- <Typography.Text type="secondary">-->
Click or drag files to this area to upload <!-- Click or drag files to this area to upload-->
</Typography.Text> <!-- </Typography.Text>-->
</Flex> <!-- </Flex>-->
<Typography.Text v-if="type && type.type === 'drop'"> <!-- <Typography.Text v-if="type && type.type === 'drop'">-->
Drop file here <!-- Drop file here-->
</Typography.Text> <!-- </Typography.Text>-->
</template> <!-- </template>-->
</Attachments> <!-- </Attachments>-->
</Sender.Header> <!-- </Sender.Header>-->
</template> <!-- </template>-->
</Sender> </Sender>
</div> </div>
</div> </div>