feat: 首页调整
This commit is contained in:
112
src/views/home/components/conversation-detail/index.vue
Normal file
112
src/views/home/components/conversation-detail/index.vue
Normal 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>
|
||||
9
src/views/home/components/conversation-detail/style.scss
Normal file
9
src/views/home/components/conversation-detail/style.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
85
src/views/home/components/created/index.vue
Normal file
85
src/views/home/components/created/index.vue
Normal 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>
|
||||
32
src/views/home/components/created/style.scss
Normal file
32
src/views/home/components/created/style.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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} />
|
||||
|
||||
107
src/views/home/components/sender-input/index.vue
Normal file
107
src/views/home/components/sender-input/index.vue
Normal 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>
|
||||
Reference in New Issue
Block a user