feat: 功能优化

This commit is contained in:
2025-11-14 02:15:14 +08:00
parent c652d0ddf3
commit 6624627802
10 changed files with 1224 additions and 223 deletions

View File

@@ -1,5 +1,5 @@
<script setup>
import { ref, onMounted, computed, watch } from 'vue'
import { ref, onMounted, onActivated, computed, watch } from 'vue'
import { usePromptStore } from '@/stores/prompt'
import MarkdownIt from 'markdown-it'
import { message } from 'ant-design-vue'
@@ -8,6 +8,8 @@ import useVoiceText from '@gold/hooks/web/useVoiceText'
import GmIcon from '@/components/icons/Icon.vue'
import { UserPromptApi } from '@/api/userPrompt'
import { useUserStore } from '@/stores/user'
import GradientButton from '@/components/GradientButton.vue'
import { setJSON, getJSON } from '@/utils/storage'
const promptStore = usePromptStore()
const userStore = useUserStore()
@@ -86,8 +88,8 @@ async function loadUserPrompts() {
return timeB - timeA
})
// 如果用户没有选择提示词,默认选中第一个
autoSelectFirstPromptIfNeeded()
// 如果用户没有选择提示词,尝试恢复本地存储的选中项或默认选中第一个
await restoreOrSelectPrompt()
} else {
throw new Error(response?.msg || response?.message || '加载失败')
}
@@ -100,24 +102,86 @@ async function loadUserPrompts() {
}
/**
* 自动选中第一个提示词(如果用户还没有选择)
* 保存选中的提示词ID到本地存储
*/
function autoSelectFirstPromptIfNeeded() {
const hasPrompts = allPrompts.value.length > 0
const hasNoSelection = !selectedPromptId.value && !form.value.prompt
if (hasPrompts && hasNoSelection) {
const firstPrompt = allPrompts.value[0]
if (firstPrompt?.content) {
selectedPromptId.value = firstPrompt.id
form.value.prompt = firstPrompt.content
promptStore.setPrompt(firstPrompt.content, firstPrompt)
}
async function saveSelectedPromptId(promptId) {
if (promptId) {
await setJSON('copywriting_selected_prompt_id', promptId)
}
}
/**
* 从本地存储恢复选中的提示词ID
*/
async function loadSelectedPromptId() {
try {
const savedId = await getJSON('copywriting_selected_prompt_id', null)
return savedId
} catch (error) {
console.error('加载保存的提示词ID失败:', error)
return null
}
}
/**
* 根据ID选中提示词
*/
async function selectPromptById(promptId) {
if (!promptId) return false
const prompt = allPrompts.value.find(p => p.id === promptId)
if (prompt && prompt.content) {
selectedPromptId.value = prompt.id
form.value.prompt = prompt.content
promptStore.setPrompt(prompt.content, prompt)
await saveSelectedPromptId(promptId)
return true
}
return false
}
/**
* 恢复或选中提示词
* 优先级:本地存储的选中项 > 第一个提示词
*/
async function restoreOrSelectPrompt() {
if (allPrompts.value.length === 0) {
return false
}
// 如果已经有选中项且内容存在,不需要重新选择
if (selectedPromptId.value && form.value.prompt) {
// 验证选中的提示词是否还在列表中
const currentPrompt = allPrompts.value.find(p => p.id === selectedPromptId.value)
if (currentPrompt && currentPrompt.content === form.value.prompt) {
return true // 已经正确选中,无需操作
}
}
// 尝试恢复本地存储的选中项
const savedPromptId = await loadSelectedPromptId()
if (savedPromptId) {
const restored = await selectPromptById(savedPromptId)
if (restored) {
return true // 成功恢复保存的选中项
}
}
// 如果没有保存的选中项或恢复失败,则选中第一个
const firstPrompt = allPrompts.value[0]
if (firstPrompt?.content) {
selectedPromptId.value = firstPrompt.id
form.value.prompt = firstPrompt.content
promptStore.setPrompt(firstPrompt.content, firstPrompt)
await saveSelectedPromptId(firstPrompt.id)
return true
}
return false
}
// 选择提示词
function selectPrompt(prompt) {
async function selectPrompt(prompt) {
if (!prompt || !prompt.content) {
message.warning('提示词内容为空')
return
@@ -126,6 +190,7 @@ function selectPrompt(prompt) {
selectedPromptId.value = prompt.id
form.value.prompt = prompt.content
promptStore.setPrompt(prompt.content, prompt)
await saveSelectedPromptId(prompt.id)
showAllPromptsModal.value = false
}
@@ -189,6 +254,29 @@ onMounted(() => {
initializePage()
})
/**
* keep-alive 激活时的处理
* 1. 如果有提示词列表,尝试恢复或选中提示词
* 2. 如果没有提示词列表但用户已登录,加载提示词列表
*/
onActivated(async () => {
// 如果已经有提示词列表,尝试恢复或选中提示词
if (allPrompts.value.length > 0) {
await restoreOrSelectPrompt()
}
// 如果提示词列表为空,但用户已登录,则尝试加载
else if (userStore.userId) {
await loadUserPrompts()
}
// 如果用户未登录,等待用户信息加载
else if (!userStore.userId && userStore.isLoggedIn) {
await ensureUserInfoLoaded()
if (userStore.userId) {
await loadUserPrompts()
}
}
})
// 监听 userId 变化:如果之前没有 userId现在有了则自动加载提示词
watch(() => userStore.userId, async (newUserId, oldUserId) => {
const userIdChanged = newUserId && !oldUserId
@@ -500,23 +588,16 @@ defineOptions({ name: 'ContentStyleCopywriting' })
</a-form-item>
<a-form-item class="form-item">
<a-button
type="primary"
@click="generateCopywriting"
<GradientButton
text="生成文案"
icon="icon-sparkle"
:disabled="!getCurrentInputValue() || !selectedPromptId || isLoading"
:loading="isLoading"
block
loading-text="生成中..."
size="large"
class="generate-btn"
>
<template v-if="!isLoading">
<span class="btn-icon"><GmIcon name="icon-sparkle" :size="16" /></span>
生成文案
</template>
<template v-else>
生成中...
</template>
</a-button>
:block="true"
@click="generateCopywriting"
/>
</a-form-item>
</a-form>
</a-card>
@@ -814,47 +895,7 @@ defineOptions({ name: 'ContentStyleCopywriting' })
display: none;
}
/* 生成按钮样式 */
.generate-btn {
margin-top: 16px;
height: 40px;
border-radius: 6px;
background: var(--color-primary);
border: none;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
}
.generate-btn:hover {
background: var(--color-primary);
box-shadow: var(--glow-primary);
}
.generate-btn:active {
background: var(--color-primary);
}
.btn-icon {
display: inline-flex;
align-items: center;
font-size: 16px;
}
/* 按钮禁用态保持主色,不换颜色(仅本页) */
:deep(.ant-btn-primary[disabled]),
:deep(.ant-btn-primary[disabled]:hover),
:deep(.ant-btn-primary[disabled]:active),
:deep(.ant-btn-primary.ant-btn-disabled),
:deep(.ant-btn-primary.ant-btn-disabled:hover),
:deep(.ant-btn-primary.ant-btn-disabled:active) {
background: var(--color-primary) !important;
border-color: var(--color-primary) !important;
color: #fff !important;
opacity: 0.6; /* 仅降低不透明度,不改变颜色 */
cursor: not-allowed;
box-shadow: none !important;
}
/* 生成按钮样式 - 已替换为 GradientButton 组件 */
/* 操作按钮样式 */
.action-btn {
@@ -1248,11 +1289,6 @@ defineOptions({ name: 'ContentStyleCopywriting' })
margin-right: 10px;
}
.generate-btn {
height: 36px;
font-size: 13px;
}
.generated-content {
padding: 14px;
font-size: 15px;

View File

@@ -1,5 +1,6 @@
<script setup>
import { ref } from 'vue'
import GradientButton from '@/components/GradientButton.vue'
const props = defineProps({
modelValue: {
@@ -52,9 +53,13 @@ function handleReset() {
<div class="form-hint">数量越大越全面但分析时间更长建议 2030</div>
</a-form-item>
<a-space>
<a-button type="primary" :loading="loading" @click="handleAnalyze">
{{ loading ? '分析中' : '开始分析' }}
</a-button>
<GradientButton
text="开始分析"
:loading="loading"
loading-text="分析中"
size="middle"
@click="handleAnalyze"
/>
<a-button @click="handleReset">清空</a-button>
</a-space>
</a-form>

View File

@@ -3,8 +3,9 @@ import { reactive, h } from 'vue'
import { DownloadOutlined } from '@ant-design/icons-vue'
import { formatTime } from '../utils/benchmarkUtils'
import ExpandedRowContent from './ExpandedRowContent.vue'
import GradientButton from '@/components/GradientButton.vue'
const props = defineProps({
defineProps({
data: {
type: Array,
required: true,
@@ -81,9 +82,12 @@ function onExpandedRowKeysChange(keys) {
</template>
导出Excel ({{ selectedRowKeys.length }}/10)
</a-button>
<a-button size="small" type="primary" class="batch-btn" @click="$emit('batchAnalyze')">
批量分析 ({{ selectedRowKeys.length }})
</a-button>
<GradientButton
:text="`批量分析 (${selectedRowKeys.length})`"
size="small"
@click="$emit('batchAnalyze')"
:disabled="selectedRowKeys.length === 0"
/>
</a-space>
</div>
<a-table
@@ -145,9 +149,14 @@ function onExpandedRowKeysChange(keys) {
</template>
<template v-else-if="column.key === 'action'">
<a-space>
<a-button size="small" type="primary" :loading="record._analyzing" :disabled="record._analyzing" @click="$emit('analyze', record)">
{{ record._analyzing ? '分析中…' : '分析' }}
</a-button>
<GradientButton
text="分析"
size="small"
:loading="record._analyzing"
loading-text="分析中"
:disabled="record._analyzing"
@click="$emit('analyze', record)"
/>
</a-space>
</template>
</template>