feat: 首页调整

This commit is contained in:
rd
2025-08-20 18:17:23 +08:00
parent 0c941b0c66
commit 6e3158cdb4
19 changed files with 446 additions and 131 deletions

View File

@ -0,0 +1,112 @@
<script lang="tsx">
import { message } from 'ant-design-vue';
import { BubbleList } from 'ant-design-x-vue';
import type { BubbleListProps } from 'ant-design-x-vue';
import { useRoute } from 'vue-router';
import SenderInput from '../sender-input/index.vue';
import markdownit from 'markdown-it';
import { Typography } from 'ant-design-vue';
const QUESTION_ROLE = 'question';
const ANSWER_ROLE = 'text';
export default {
setup(props, { emit, expose }) {
const route = useRoute();
const senderRef = ref(null);
const searchValue = ref('');
const generateLoading = ref(false);
const conversationList = ref([]);
const md = markdownit({ html: true, breaks: true });
const conversationId = computed(() => {
return route.params.conversationId;
});
const handleSubmit = () => {
generateLoading.value = true;
conversationList.value.push({
role: QUESTION_ROLE,
content: searchValue.value,
});
const tempId = Date.now();
const tempIndex = conversationList.value.length;
conversationList.value.push({
id: tempId,
loading: true,
role: ANSWER_ROLE
});
setTimeout(() => {
const content = `> Render as markdown content to show rich text!
Link: [Ant Design X](https://x.ant.design)
`;
searchValue.value = '';
conversationList.value.splice(tempIndex, 1, {
id: tempId,
loading: false,
role: ANSWER_ROLE,
content,
messageRender: (content) => (
<Typography>
<div v-html={md.render(content)}></div>
</Typography>
),
});
}, 1000);
};
const handleCancel = () => {
generateLoading.value = false;
message.info('取消生成');
};
const roles: BubbleListProps['roles'] = {
[ANSWER_ROLE]: {
placement: 'start',
variant: 'borderless',
typing: { step: 2, interval: 100 },
onTypingComplete: () => {
generateLoading.value = false;
},
},
[QUESTION_ROLE]: {
placement: 'end',
shape: 'round',
styles: {
content: {
padding: '6px 12px',
},
},
},
};
return () => (
<div class="conversation-detail-wrap w-full h-full flex">
<div class="flex-1 flex justify-center">
<section class="main w-600px h-full relative flex flex-col pt-20px">
<BubbleList class="flex-1" roles={roles} items={conversationList.value} />
<div class="w-full flex flex-col justify-center items-center">
<SenderInput
class="w-full"
ref={senderRef}
placeholder="继续追问..."
v-model={searchValue.value}
loading={generateLoading.value}
onSubmit={handleSubmit}
onCancel={handleCancel}
/>
<p class="cts !color-#939499 text-12px !lh-20px my-4px">内容由AI生成仅供参考</p>
</div>
</section>
</div>
</div>
);
},
};
</script>
<style lang="scss" scoped>
@import './style.scss';
</style>

View File

@ -0,0 +1,9 @@
.conversation-detail-wrap {
.cts {
color: var(--Text-1, #737478);
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
}

View File

@ -0,0 +1,85 @@
<script lang="tsx">
import { message } from 'ant-design-vue';
import ExpandableTags from '@/components/expandable-tags/index.vue';
import SenderInput from '../sender-input/index.vue';
import { useSharedDataStore } from '@/stores/modules/share-data';
import { useRouter } from 'vue-router';
export default {
setup(props, { emit, expose }) {
const router = useRouter();
const senderRef = ref(null);
const searchValue = ref('');
const sharedDataStore = useSharedDataStore();
const handleSubmit = () => {
handleSearch();
};
const handleSearch = () => {
console.log('searchValue.value', searchValue.value);
// const conversationId = searchValue.value;
// router.push({
// name: 'Home',
// params: {
// conversationId,
// },
// });
};
const tagList = [
'人工智能',
'人工智能与应用',
'行业分析与市场数据',
'标签标签标签标签标签标签标签',
'标签标签标签标签标签标签标签',
'标签标签标签标签标签标签标签',
'标签标签标签标签标签标签标签',
'标签A',
'啊啊啊',
'宝宝贝贝',
'微信',
'吧啊啊',
'哦哦哦哦哦哦哦哦',
'人工智能',
'人工智能与应用',
];
const handleTagClick = (tag: string) => {
searchValue.value = tag;
senderRef.value?.focus();
};
onMounted(() => {
const params = sharedDataStore.routeParams;
if (params) {
searchValue.value = params.keyWord;
sharedDataStore.clearRouteParams();
handleSearch();
}
});
return () => (
<div class="create-conversation-wrap w-full h-full flex justify-center">
<div class="main w-600px pt-120px">
<p class="title mb-16px">
<span>营小智7x24小时</span>
<span class="s1">AI营销</span>
<span>团队</span>
</p>
<p class="cts text-center mb-104px">AI 辅助账号托管账号 自动生成爆款内容 定时任务发布</p>
<SenderInput ref={senderRef} v-model={searchValue.value} onSubmit={handleSubmit} class="mb-24px" />
<p class="cts mb-6px">可以试试这样下发任务</p>
<ExpandableTags tags={tagList} clickable onTagClick={handleTagClick} />
</div>
</div>
);
},
};
</script>
<style lang="scss" scoped>
@import './style.scss';
</style>

View File

@ -0,0 +1,32 @@
.create-conversation-wrap {
.cts {
color: var(--Text-1, #737478);
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
.main {
.title {
color: var(--Text-1, #211f24);
text-align: center;
font-family: $font-family-medium;
font-size: 32px;
font-style: normal;
font-weight: 700;
line-height: 48px;
.s1 {
background: linear-gradient(90deg, #6d4cfe 38.12%, #b93bf0 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
:deep(.expandable-tags-container) {
.tag-list {
row-gap: 6px;
column-gap: 12px;
}
}
}
}

View File

@ -5,9 +5,11 @@ import Conversations from '@/components/xt-chat/conversations';
import SvgIcon from '@/components/svg-icon';
import { Button, Flex, Input } from 'ant-design-vue';
import DeleteChatModal from './delete-chat-modal.vue';
import { useRouter } from 'vue-router';
export default {
setup(props, { emit, expose }) {
const router = useRouter();
const open = ref(false);
const dataSource = ref([]);
const activeKey = ref('');
@ -19,6 +21,7 @@ export default {
};
const getData = () => {
dataSource.value = Array.from({ length: 4 }).map((conversation, index) => ({
id: `${index + 1}`,
key: `item${index + 1}`,
label: `Conversation Item ${index + 1}Conversation Item 1`,
}));
@ -43,6 +46,15 @@ export default {
const handleRename = (item) => {
console.log('handleRename', item);
};
const handleActiveChange = (item) => {
const { id } = item;
router.push({
name: 'Home',
params: {
conversationId: id,
},
});
};
expose({
showDrawer,
@ -60,6 +72,7 @@ export default {
dataSource={dataSource.value}
onMenuClick={handleMenuClick}
onRename={handleRename}
onActiveChange={handleActiveChange}
/>
</section>
<DeleteChatModal ref={deleteChatModalRef} />

View File

@ -0,0 +1,107 @@
<script lang="tsx">
import { ref } from 'vue';
import { Sender } from 'ant-design-x-vue';
import { Tooltip } from 'ant-design-vue';
interface SenderInputProps {
modelValue?: string;
loading?: boolean;
placeholder?: string;
}
export default {
name: 'SenderInput',
props: {
modelValue: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '随时告诉我你想做什么,比如查数据、发任务、写内容,我会立刻帮你完成。',
},
loading: {
type: Boolean,
default: false,
},
},
emits: ['update:modelValue', 'submit', 'cancel'],
setup(props: SenderInputProps, { emit, expose }) {
const senderRef = ref(null);
const localSearchValue = ref(props.modelValue);
// 监听外部value变化
watch(
() => props.modelValue,
(newValue) => {
localSearchValue.value = newValue || '';
},
);
const handleSubmit = () => {
if (!props.loading) {
emit('submit', localSearchValue.value);
}
};
const handleCancel = () => {
emit('cancel');
};
const focus = () => {
senderRef.value?.focus?.();
};
const renderActions = () => {
if (props.loading) {
return (
<Tooltip title="停止生成" onClick={handleCancel}>
<div class="w-32px h-32px p-6px flex justify-center items-center rounded-50% bg-#6D4CFE cursor-pointer">
<div class="w-12px h-12px rounded-2px bg-#FFF"></div>
</div>
</Tooltip>
);
}
return (
<div
onClick={handleSubmit}
class={`submit-btn w-32px h-32px p-6px flex justify-center items-center rounded-50% cursor-pointer ${
!localSearchValue.value ? 'opacity-50' : ''
}`}
>
<icon-arrow-right size={20} class="color-#FFFFFF" />
</div>
);
};
expose({
focus,
});
return () => (
<div class="sender-input-wrap full h-120px">
<Sender
v-model:value={localSearchValue.value}
ref={senderRef}
onChange={(value: string) => emit('update:modelValue', value)}
onSubmit={handleSubmit}
class="h-full w-full mb-24px"
placeholder={props.placeholder}
actions={() => renderActions()}
/>
</div>
);
},
};
</script>
<style lang="scss" scoped>
.sender-input-wrap {
:deep(.ant-sender) {
.submit-btn {
background: linear-gradient(125deg, #6d4cfe 32.25%, #3ba1f0 72.31%),
linear-gradient(113deg, #6d4cfe 0%, #b93bf0 100%);
}
}
}
</style>

View File

@ -1,93 +1,24 @@
<script lang="tsx">
import HistoryConversationDrawer from './components/history-conversation-drawer';
import { Sender } from 'ant-design-x-vue';
import { message } from 'ant-design-vue';
import ExpandableTags from '@/components/expandable-tags';
import { useRoute } from 'vue-router';
import { ref, computed } from 'vue';
import { useSharedDataStore } from '@/stores/modules/share-data';
import HistoryConversationDrawer from './components/history-conversation-drawer/index.vue';
import ConversationDetail from './components/conversation-detail/index.vue';
import ConversationCreate from './components/created/index.vue';
export default {
setup(props, { emit, expose }) {
const route = useRoute();
const historyConversationDrawerRef = ref(null);
const senderRef = ref(null);
const searchValue = ref('');
const sharedDataStore = useSharedDataStore();
const handleSubmit = () => {
handleSearch();
searchValue.value = '';
};
const handleSearch = () => {
message.info('handleSearch----' + searchValue.value);
};
const tagList = [
'人工智能',
'人工智能与应用',
'行业分析与市场数据',
'标签标签标签标签标签标签标签',
'标签标签标签标签标签标签标签',
'标签标签标签标签标签标签标签',
'标签标签标签标签标签标签标签',
'标签A',
'啊啊啊',
'宝宝贝贝',
'微信',
'吧啊啊',
'哦哦哦哦哦哦哦哦',
'人工智能',
'人工智能与应用',
];
const handleTagClick = (tag: string) => {
searchValue.value = tag;
handleSearch();
senderRef.value?.focus();
};
onMounted(() => {
const params = sharedDataStore.routeParams;
if (params) {
searchValue.value = params.keyWord;
sharedDataStore.clearRouteParams();
handleSearch();
senderRef.value?.focus();
}
const conversationId = computed(() => {
return route.params.conversationId;
});
return () => (
<div class="home-wrap rounded-12px w-full h-full">
<div class="w-full h-full flex justify-center ">
<div class="main-chat-wrap w-600px pt-120px">
<p class="title mb-16px">
<span>营小智7x24小时</span>
<span class="s1">AI营销</span>
<span>团队</span>
</p>
<p class="cts text-center mb-104px">AI 辅助账号托管账号 自动生成爆款内容 定时任务发布</p>
<Sender
v-model:value={searchValue.value}
ref={senderRef}
onSubmit={handleSubmit}
class="h-120px w-full mb-24px"
placeholder="随时告诉我你想做什么,比如查数据、发任务、写内容,我会立刻帮你完成。"
actions={() => (
<div
onClick={handleSubmit}
class={`submit-btn w-32px h-32px p-6px flex justify-center items-center rounded-50% cursor-pointer ${
!searchValue.value ? 'opacity-50' : ''
}`}
>
<icon-arrow-right size={20} class="color-#FFFFFF" />
</div>
)}
/>
<p class="cts mb-6px">可以试试这样下发任务</p>
<ExpandableTags tags={tagList} clickable onTagClick={handleTagClick} />
</div>
</div>
<div class="chat-wrap rounded-12px w-full h-full">
{conversationId.value ? <ConversationDetail /> : <ConversationCreate />}
{/**历史对话入口 */}
<div

View File

@ -1,43 +1,7 @@
.home-wrap {
.chat-wrap {
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
background: rgba(255, 255, 255, 0.9);
.cts {
color: var(--Text-1, #737478);
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
.main-chat-wrap {
.title {
color: var(--Text-1, #211f24);
text-align: center;
font-family: $font-family-medium;
font-size: 32px;
font-style: normal;
font-weight: 700;
line-height: 48px;
.s1 {
background: linear-gradient(90deg, #6d4cfe 38.12%, #b93bf0 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
:deep(.ant-sender) {
.submit-btn {
background: linear-gradient(125deg, #6d4cfe 32.25%, #3ba1f0 72.31%),
linear-gradient(113deg, #6d4cfe 0%, #b93bf0 100%);
}
}
:deep(.expandable-tags-container) {
.tag-list {
row-gap: 6px;
column-gap: 12px;
}
}
}
.history-conversation-btn {
border-radius: 8px 0 0 8px;
border-top: 1px solid #eabaff;