|
@@ -14,7 +14,8 @@
|
|
|
ref,
|
|
|
watch,
|
|
|
computed,
|
|
|
- onMounted
|
|
|
+ onMounted,
|
|
|
+ onUnmounted
|
|
|
} from 'vue'
|
|
|
import {
|
|
|
useUserStore
|
|
@@ -114,6 +115,100 @@
|
|
|
}
|
|
|
const selectVisible = ref(false);
|
|
|
const selectData = ref({});
|
|
|
+ // 打字机效果状态
|
|
|
+ const phrasesInput = ref('企业官网,电商独立站,个人独立站,生活服务独立站');
|
|
|
+ const typeSpeed = ref(120);
|
|
|
+ const isRunning = ref(true);
|
|
|
+ const isPaused = ref(false);
|
|
|
+ const placeholder = ref('');
|
|
|
+
|
|
|
+ // 动画控制变量
|
|
|
+ const currentPhraseIndex = ref(0);
|
|
|
+ const currentCharIndex = ref(0);
|
|
|
+ const isDeleting = ref(false);
|
|
|
+ let typeInterval = null;
|
|
|
+
|
|
|
+ // 计算属性 - 获取短语数组
|
|
|
+ const phrases = computed(() => {
|
|
|
+ return phrasesInput.value.split(',')
|
|
|
+ .map(phrase => phrase.trim())
|
|
|
+ .filter(phrase => phrase.length > 0);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 打字机动画核心逻辑
|
|
|
+ const type = () => {
|
|
|
+ if (isPaused.value) return;
|
|
|
+ const currentPhrase = phrases.value[currentPhraseIndex.value];
|
|
|
+ if (!currentPhrase) return;
|
|
|
+
|
|
|
+ if (isDeleting.value) {
|
|
|
+ // 删除字符
|
|
|
+ placeholder.value = currentPhrase.substring(0, currentCharIndex.value - 1);
|
|
|
+ currentCharIndex.value--;
|
|
|
+ if (currentCharIndex.value === 0) {
|
|
|
+ isDeleting.value = false;
|
|
|
+ currentPhraseIndex.value = (currentPhraseIndex.value + 1) % phrases.value.length;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 添加字符
|
|
|
+ placeholder.value = currentPhrase.substring(0, currentCharIndex.value + 1);
|
|
|
+ currentCharIndex.value++;
|
|
|
+ if (currentCharIndex.value === currentPhrase.length) {
|
|
|
+ // 完成输入,暂停一下然后开始删除
|
|
|
+ setTimeout(() => {
|
|
|
+ isDeleting.value = true;
|
|
|
+ }, 1000);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 开始/停止动画
|
|
|
+ const toggleAnimation = () => {
|
|
|
+ isRunning.value = !isRunning.value;
|
|
|
+ if (isRunning.value) {
|
|
|
+ startTypeAnimation();
|
|
|
+ } else {
|
|
|
+ clearInterval(typeInterval);
|
|
|
+ typeInterval = null;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 暂停/继续动画
|
|
|
+ const togglePause = () => {
|
|
|
+ if (!isRunning.value) return;
|
|
|
+ isPaused.value = !isPaused.value;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 启动打字动画
|
|
|
+ const startTypeAnimation = () => {
|
|
|
+ if (typeInterval) {
|
|
|
+ clearInterval(typeInterval);
|
|
|
+ typeInterval = null;
|
|
|
+ }
|
|
|
+ if (phrases.value.length === 0) return;
|
|
|
+ // 重置状态
|
|
|
+ currentPhraseIndex.value = 0;
|
|
|
+ currentCharIndex.value = 0;
|
|
|
+ isDeleting.value = false;
|
|
|
+ placeholder.value = '';
|
|
|
+ typeInterval = setInterval(type, typeSpeed.value);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 当短语变化时重置动画
|
|
|
+ watch(phrases, (newVal) => {
|
|
|
+ if (newVal.length === 0) return;
|
|
|
+ if (isRunning.value) {
|
|
|
+ startTypeAnimation();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 当速度变化时重新设置定时器
|
|
|
+ watch(typeSpeed, (newVal) => {
|
|
|
+ if (isRunning.value && typeInterval) {
|
|
|
+ clearInterval(typeInterval);
|
|
|
+ typeInterval = setInterval(type, newVal);
|
|
|
+ }
|
|
|
+ });
|
|
|
const sendMessage = async () => {
|
|
|
if (!userStore.token) {
|
|
|
ElMessage({
|
|
@@ -123,10 +218,19 @@
|
|
|
loginVisible.value = true;
|
|
|
return;
|
|
|
}
|
|
|
- loading.value = true;
|
|
|
- let createChatData = await createFLowChat(7, {
|
|
|
+ let postData = {
|
|
|
query: message.value
|
|
|
- })
|
|
|
+ }
|
|
|
+ if (websiteURL.value) {
|
|
|
+ if (!postData['inputs']) postData['inputs'] = {};
|
|
|
+ postData['inputs']['websiteURL'] = websiteURL.value;
|
|
|
+ }
|
|
|
+ if (imageList.value.length > 0) {
|
|
|
+ if (!postData['inputs']) postData['inputs'] = {};
|
|
|
+ postData['inputs']['files'] = imageList.value.join(',');
|
|
|
+ }
|
|
|
+ loading.value = true;
|
|
|
+ let createChatData = await createFLowChat(7, postData)
|
|
|
loading.value = false;
|
|
|
if (createChatData.state) router.push(`/aichat/${createChatData.data}`);
|
|
|
}
|
|
@@ -150,6 +254,13 @@
|
|
|
}
|
|
|
onMounted(() => {
|
|
|
init();
|
|
|
+ startTypeAnimation();
|
|
|
+ })
|
|
|
+ // 组件卸载时清除定时器(重要!)
|
|
|
+ onUnmounted(() => {
|
|
|
+ if (typeInterval) {
|
|
|
+ clearInterval(typeInterval);
|
|
|
+ }
|
|
|
})
|
|
|
</script>
|
|
|
|
|
@@ -184,13 +295,20 @@
|
|
|
<p class="small-title">通过与AI聊天创建应用程序和网站</p>
|
|
|
</div>
|
|
|
<div class="form-box">
|
|
|
- <el-input type="textarea" v-model="message" placeholder="生成一个信息科技企业官网" resize="none" :rows="5"
|
|
|
+ <el-input type="textarea" v-model="message" :placeholder="`生成一个${placeholder}`" resize="none" :rows="5"
|
|
|
:autosize="{ minRows: 3, maxRows: 7 }">
|
|
|
</el-input>
|
|
|
<HideUpload ref="hideUploadRef" v-show="false" @uploadImage="uploadImage"></HideUpload>
|
|
|
<div class="form-submit">
|
|
|
<div class="form-operation">
|
|
|
- <el-dropdown @command="commandFunction">
|
|
|
+ <el-dropdown @command="commandFunction" :popper-options="{
|
|
|
+ modifiers: [
|
|
|
+ {
|
|
|
+ name: 'computeStyles',
|
|
|
+ options: { gpuAcceleration: false, adaptive: false },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ }">
|
|
|
<el-button size="default" :icon="Paperclip" circle></el-button>
|
|
|
<template #dropdown>
|
|
|
<el-dropdown-menu>
|
|
@@ -199,9 +317,9 @@
|
|
|
</el-dropdown-menu>
|
|
|
</template>
|
|
|
</el-dropdown>
|
|
|
- <el-button style="margin-left: 10px;" size="default" :icon="Plus" circle
|
|
|
+ <!-- <el-button style="margin-left: 10px;" size="default" :icon="Plus" circle
|
|
|
@click="selectVisible = true">
|
|
|
- </el-button>
|
|
|
+ </el-button> -->
|
|
|
</div>
|
|
|
<el-button type="primary" size="large" :icon="Top" circle :loading="loading"
|
|
|
style="font-size: 18px;" @click="sendMessage" :disabled="!message.trim()">
|
|
@@ -210,7 +328,7 @@
|
|
|
<div class="operation-file" v-if="websiteURL || imageList.length > 0">
|
|
|
<div class="operation-url" v-if="websiteURL">
|
|
|
<el-tag type="info" closable effect="plain" round @close="websiteURL = ''">
|
|
|
- {{websiteURL}}
|
|
|
+ 参考网站:{{websiteURL}}
|
|
|
</el-tag>
|
|
|
</div>
|
|
|
<div class="operation-image" v-if="imageList.length > 0">
|
|
@@ -392,6 +510,10 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ .el-button:focus-visible {
|
|
|
+ outline: none;
|
|
|
+ }
|
|
|
+
|
|
|
.form-submit {
|
|
|
margin-top: 10px;
|
|
|
display: flex;
|
|
@@ -420,10 +542,6 @@
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
|
|
-
|
|
|
- .el-select__wrapper {
|
|
|
- border-radius: 24px;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
.operation-file {
|
|
@@ -432,6 +550,7 @@
|
|
|
|
|
|
.operation-image {
|
|
|
display: flex;
|
|
|
+ margin-top: 10px;
|
|
|
}
|
|
|
|
|
|
.image-item {
|