优化
This commit is contained in:
@@ -47,18 +47,6 @@
|
||||
<span class="loading-text">加载中...</span>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-else-if="!loading && allPrompts.length === 0" class="prompt-empty-state">
|
||||
<div class="prompt-empty-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<circle cx="8.5" cy="8.5" r="1.5"></circle>
|
||||
<polyline points="21 15 16 10 5 21"></polyline>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="prompt-empty-text">没有找到文案风格</p>
|
||||
</div>
|
||||
|
||||
<!-- 更多提示词弹窗 -->
|
||||
<div v-if="showAllPromptsModal" class="prompt-modal-mask" @click.self="showAllPromptsModal = false">
|
||||
<div class="prompt-modal">
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
import { CommonService } from '@/api/common'
|
||||
import { UserPromptApi } from '@/api/userPrompt'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import PromptSelector from '@/components/PromptSelector.vue'
|
||||
import { getVoiceText } from '@gold/hooks/web/useVoiceText'
|
||||
import TikhubService, { InterfaceType, MethodType, ParamType } from '@/api/tikhub'
|
||||
import { UserPromptApi } from '@/api/userPrompt'
|
||||
import PromptSelector from '@/components/PromptSelector.vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { getVoiceText } from '@gold/hooks/web/useVoiceText'
|
||||
|
||||
defineOptions({ name: 'ForecastView' })
|
||||
|
||||
@@ -45,31 +46,40 @@ const loadingPrompts = ref(false)
|
||||
const promptSearchKeyword = ref('')
|
||||
|
||||
// 工具函数
|
||||
const formatNumber = (num) => {
|
||||
function formatNumber(num) {
|
||||
if (!num) return '0'
|
||||
return num >= 10000 ? `${(num / 10000).toFixed(1)}w` : num.toString()
|
||||
return num >= 10000 ? `${(num / 10000).toFixed(1)}w` : String(num)
|
||||
}
|
||||
|
||||
const truncateTitle = (title, maxLength = 28) => {
|
||||
function truncateTitle(title, maxLength = 28) {
|
||||
if (!title) return ''
|
||||
return title.length <= maxLength ? title : `${title.substring(0, maxLength)}...`
|
||||
}
|
||||
|
||||
const handleImageError = (event) => {
|
||||
function handleImageError(event) {
|
||||
event.target.style.display = 'none'
|
||||
}
|
||||
|
||||
const openVideo = (topic, event) => {
|
||||
function openVideo(topic, event) {
|
||||
event.stopPropagation()
|
||||
if (topic.videoUrl) window.open(topic.videoUrl, '_blank')
|
||||
}
|
||||
|
||||
const handleSearchKeypress = (event) => {
|
||||
function handleSearchKeypress(event) {
|
||||
if (event.key === 'Enter' && !isLoading.value) {
|
||||
handleSearch()
|
||||
}
|
||||
}
|
||||
|
||||
async function copyContent() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(generatedContent.value)
|
||||
message.success('已复制')
|
||||
} catch {
|
||||
message.error('复制失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 提示词管理
|
||||
async function loadUserPrompts() {
|
||||
if (!userStore.userId) {
|
||||
@@ -177,22 +187,12 @@ async function handleGenerate() {
|
||||
|
||||
const ctrl = new AbortController()
|
||||
let fullText = ''
|
||||
let errorOccurred = false
|
||||
let isResolved = false
|
||||
let completed = false
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
let timeout = null
|
||||
|
||||
const cleanup = () => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
timeout = null
|
||||
}
|
||||
}
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
if (!isResolved) {
|
||||
cleanup()
|
||||
const timeout = setTimeout(() => {
|
||||
if (!completed) {
|
||||
completed = true
|
||||
ctrl.abort()
|
||||
reject(new Error('请求超时'))
|
||||
}
|
||||
@@ -202,25 +202,21 @@ async function handleGenerate() {
|
||||
data: requestData,
|
||||
ctrl,
|
||||
onMessage: (event) => {
|
||||
if (errorOccurred || !event?.data) return
|
||||
if (completed || !event?.data) return
|
||||
|
||||
const dataStr = event.data.trim()
|
||||
|
||||
if (dataStr === '[DONE]') {
|
||||
cleanup()
|
||||
if (!isResolved) {
|
||||
isResolved = true
|
||||
resolve()
|
||||
}
|
||||
completed = true
|
||||
clearTimeout(timeout)
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
if (dataStr.startsWith('[TIMEOUT]')) {
|
||||
cleanup()
|
||||
if (!isResolved) {
|
||||
errorOccurred = true
|
||||
isResolved = true
|
||||
reject(new Error(dataStr.replace('[TIMEOUT]', '').trim() || '请求超时'))
|
||||
}
|
||||
completed = true
|
||||
clearTimeout(timeout)
|
||||
reject(new Error(dataStr.replace('[TIMEOUT]', '').trim() || '请求超时'))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -231,7 +227,7 @@ async function handleGenerate() {
|
||||
fullText += piece
|
||||
generatedContent.value = fullText
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
if (event.data && !event.data.startsWith('[')) {
|
||||
fullText += event.data
|
||||
generatedContent.value = fullText
|
||||
@@ -239,20 +235,18 @@ async function handleGenerate() {
|
||||
}
|
||||
},
|
||||
onError: (err) => {
|
||||
cleanup()
|
||||
if (!isResolved) {
|
||||
errorOccurred = true
|
||||
isResolved = true
|
||||
if (!completed) {
|
||||
completed = true
|
||||
clearTimeout(timeout)
|
||||
ctrl.abort()
|
||||
const errorMsg = err?.message || '网络请求失败'
|
||||
message.error(errorMsg)
|
||||
reject(new Error(errorMsg))
|
||||
message.error(err?.message || '网络请求失败')
|
||||
reject(new Error(err?.message || '网络请求失败'))
|
||||
}
|
||||
},
|
||||
onClose: () => {
|
||||
cleanup()
|
||||
if (!isResolved) {
|
||||
isResolved = true
|
||||
if (!completed) {
|
||||
completed = true
|
||||
clearTimeout(timeout)
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
@@ -273,11 +267,11 @@ async function handleGenerate() {
|
||||
function extractAudioUrl(video) {
|
||||
const urlList = video?.play_addr?.url_list
|
||||
if (Array.isArray(urlList) && urlList.length > 0) {
|
||||
const lastUrl = urlList[urlList.length - 1]
|
||||
const firstUrl = urlList[0]
|
||||
return (lastUrl && lastUrl.trim()) || (firstUrl && firstUrl.trim()) || ''
|
||||
const lastUrl = urlList[urlList.length - 1]?.trim()
|
||||
const firstUrl = urlList[0]?.trim()
|
||||
return lastUrl || firstUrl || ''
|
||||
}
|
||||
return (video?.play_addr?.url && video.play_addr.url.trim()) || ''
|
||||
return video?.play_addr?.url?.trim() || ''
|
||||
}
|
||||
|
||||
function extractCover(video) {
|
||||
@@ -369,12 +363,12 @@ async function handleSearch() {
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
onMounted(() => {
|
||||
if (userStore.userId) {
|
||||
await loadUserPrompts()
|
||||
loadUserPrompts()
|
||||
} else if (userStore.isLoggedIn) {
|
||||
setTimeout(async () => {
|
||||
if (userStore.userId) await loadUserPrompts()
|
||||
setTimeout(() => {
|
||||
if (userStore.userId) loadUserPrompts()
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
@@ -608,7 +602,7 @@ onMounted(async () => {
|
||||
<span class="result-label">生成结果</span>
|
||||
<button
|
||||
class="copy-btn"
|
||||
@click="navigator.clipboard.writeText(generatedContent); message.success('已复制')"
|
||||
@click="copyContent"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2"/>
|
||||
|
||||
Reference in New Issue
Block a user