feat: 功能优化
This commit is contained in:
39
frontend/app/web-gold/src/api/benchmarkTask.js
Normal file
39
frontend/app/web-gold/src/api/benchmarkTask.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import http from '@/api/http'
|
||||
import { API_BASE } from '@gold/config/api'
|
||||
|
||||
const SERVER_BASE = API_BASE.APP_TIK
|
||||
|
||||
/**
|
||||
* 对标分析任务 API
|
||||
*/
|
||||
export const BenchmarkTaskApi = {
|
||||
/**
|
||||
* 创建对标分析任务
|
||||
*/
|
||||
createTask: async (data) => {
|
||||
return await http.post(`${SERVER_BASE}/benchmark-task/create`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取任务详情
|
||||
*/
|
||||
getTask: async (id) => {
|
||||
return await http.get(`${SERVER_BASE}/benchmark-task/get`, { params: { id } })
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取任务分页列表
|
||||
*/
|
||||
getTaskPage: async (params) => {
|
||||
return await http.get(`${SERVER_BASE}/benchmark-task/page`, { params })
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除任务
|
||||
*/
|
||||
deleteTask: async (id) => {
|
||||
return await http.delete(`${SERVER_BASE}/benchmark-task/delete`, { params: { id } })
|
||||
},
|
||||
}
|
||||
|
||||
export default BenchmarkTaskApi
|
||||
@@ -9,6 +9,14 @@ const SERVER_BASE_AI = API_BASE.APP_AI
|
||||
* 用户提示词 API
|
||||
*/
|
||||
export const UserPromptApi = {
|
||||
/**
|
||||
* 获取用户可用提示词列表(自建 + 收藏的智能体)
|
||||
* @returns {Promise} 响应数据
|
||||
*/
|
||||
getMyPromptList: async () => {
|
||||
return await http.get(`${SERVER_BASE_AI}/user-prompt/my-list`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建用户提示词
|
||||
* @param {Object} data - 提示词数据
|
||||
|
||||
@@ -128,7 +128,7 @@ const props = defineProps({
|
||||
// 展示模式:tags(标签)或 select(下拉选择)
|
||||
displayMode: {
|
||||
type: String,
|
||||
default: 'select' // 默认为select模式,因为用户反馈标签模式不好看
|
||||
default: 'select'
|
||||
},
|
||||
// 展示数量(仅标签模式有效)
|
||||
displayCount: {
|
||||
@@ -140,7 +140,7 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 本地存储键名前缀,用于保存和恢复选择
|
||||
// 本地存储键名前缀
|
||||
storageKey: {
|
||||
type: String,
|
||||
default: 'prompt_selector'
|
||||
@@ -150,7 +150,7 @@ const props = defineProps({
|
||||
// Emits
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
|
||||
// Stores
|
||||
// Stores - 单一数据源
|
||||
const userStore = useUserStore()
|
||||
const promptStore = usePromptStore()
|
||||
|
||||
@@ -159,7 +159,7 @@ const showAllPromptsModal = ref(false)
|
||||
const promptSearchKeyword = ref('')
|
||||
const selectedPromptId = ref(props.modelValue)
|
||||
|
||||
// 使用 store 中的数据
|
||||
// ===== 单一数据源:从 Store 获取 =====
|
||||
const allPrompts = computed(() => promptStore.promptList)
|
||||
const loading = computed(() => promptStore.promptListLoading)
|
||||
|
||||
@@ -197,28 +197,31 @@ watch(() => props.modelValue, (newValue) => {
|
||||
selectedPromptId.value = newValue
|
||||
})
|
||||
|
||||
// 加载用户提示词
|
||||
// 加载用户提示词(通过 Store)
|
||||
async function loadUserPrompts() {
|
||||
// 检查用户是否登录
|
||||
if (!userStore.userId) {
|
||||
console.warn('用户未登录,无法加载提示词')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用 store 加载数据
|
||||
const prompts = await promptStore.loadPromptList(userStore.userId)
|
||||
// 使用 store 加载(自建 + 收藏的智能体)
|
||||
await promptStore.loadPromptList()
|
||||
|
||||
// 如果有选中ID,但当前选中的提示词不在列表中,清空选择
|
||||
if (selectedPromptId.value && !prompts.find(p => p.id === selectedPromptId.value)) {
|
||||
selectedPromptId.value = null
|
||||
}
|
||||
// 如果没有选中ID且有提示词,默认选中第一个
|
||||
else if (!selectedPromptId.value && prompts.length > 0) {
|
||||
selectedPromptId.value = prompts[0].id
|
||||
// 如果有选中ID,验证是否在列表中
|
||||
if (selectedPromptId.value) {
|
||||
const exists = allPrompts.value.find(p => p.id === selectedPromptId.value)
|
||||
if (!exists) {
|
||||
selectedPromptId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试从本地存储恢复选中状态
|
||||
// 如果没有选中且有提示词,默认选中第一个
|
||||
if (!selectedPromptId.value && allPrompts.value.length > 0) {
|
||||
selectedPromptId.value = allPrompts.value[0].id
|
||||
}
|
||||
|
||||
// 恢复本地存储的选择
|
||||
await restoreSelectedPromptId()
|
||||
} catch (error) {
|
||||
console.error('加载提示词失败:', error)
|
||||
@@ -272,10 +275,10 @@ function handleSelectChange(value) {
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新用户提示词
|
||||
// 刷新用户提示词(强制重新加载)
|
||||
async function refreshUserPrompts() {
|
||||
try {
|
||||
await promptStore.refreshPromptList(userStore.userId)
|
||||
await promptStore.refreshPromptList()
|
||||
await restoreSelectedPromptId()
|
||||
} catch (error) {
|
||||
console.error('刷新提示词失败:', error)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ref } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import localforage from 'localforage'
|
||||
import { UserPromptApi } from '@/api/userPrompt'
|
||||
@@ -10,11 +10,14 @@ export const usePromptStore = defineStore('prompt', () => {
|
||||
// 存储提示词相关的视频信息
|
||||
const currentVideoInfo = ref(null)
|
||||
|
||||
// 存储提示词列表
|
||||
// 存储提示词列表(自建 + 收藏)
|
||||
const promptList = ref([])
|
||||
const promptListLoading = ref(false)
|
||||
const promptListError = ref(null)
|
||||
|
||||
// 缓存标记:记录是否已加载过
|
||||
const hasLoaded = ref(false)
|
||||
|
||||
// 设置提示词
|
||||
function setPrompt(prompt, videoInfo = null) {
|
||||
currentPrompt.value = prompt
|
||||
@@ -27,15 +30,29 @@ export const usePromptStore = defineStore('prompt', () => {
|
||||
currentVideoInfo.value = null
|
||||
}
|
||||
|
||||
// 加载提示词列表
|
||||
async function loadPromptList(userId) {
|
||||
if (!userId) {
|
||||
console.warn('用户未登录,无法加载提示词')
|
||||
return
|
||||
/**
|
||||
* 加载用户可用提示词列表(自建 + 收藏的智能体)
|
||||
* @param {Object} options
|
||||
* @param {boolean} options.force - 是否强制刷新
|
||||
*/
|
||||
async function loadPromptList(options = {}) {
|
||||
const { force = false } = options
|
||||
|
||||
// 如果已有数据且不强制刷新,直接返回缓存
|
||||
if (hasLoaded.value && !force && promptList.value.length > 0) {
|
||||
return promptList.value
|
||||
}
|
||||
|
||||
// 如果已有数据且不在加载中,直接返回缓存数据
|
||||
if (promptList.value.length > 0 && !promptListLoading.value) {
|
||||
// 防止重复请求
|
||||
if (promptListLoading.value) {
|
||||
await new Promise(resolve => {
|
||||
const unwatch = watch(promptListLoading, (loading) => {
|
||||
if (!loading) {
|
||||
unwatch()
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
return promptList.value
|
||||
}
|
||||
|
||||
@@ -43,17 +60,16 @@ export const usePromptStore = defineStore('prompt', () => {
|
||||
promptListError.value = null
|
||||
|
||||
try {
|
||||
const response = await UserPromptApi.getUserPromptPage({
|
||||
pageNo: 1,
|
||||
pageSize: 100,
|
||||
status: undefined
|
||||
})
|
||||
// 调用新接口:获取自建 + 收藏的提示词
|
||||
const response = await UserPromptApi.getMyPromptList()
|
||||
|
||||
if (response?.data?.list) {
|
||||
promptList.value = response.data.list
|
||||
if (response?.data) {
|
||||
promptList.value = response.data
|
||||
} else {
|
||||
promptList.value = []
|
||||
}
|
||||
|
||||
hasLoaded.value = true
|
||||
return promptList.value
|
||||
} catch (error) {
|
||||
console.error('加载提示词列表失败:', error)
|
||||
@@ -68,10 +84,8 @@ export const usePromptStore = defineStore('prompt', () => {
|
||||
function addPromptToList(prompt) {
|
||||
const existingIndex = promptList.value.findIndex(p => p.id === prompt.id)
|
||||
if (existingIndex >= 0) {
|
||||
// 更新已存在的提示词
|
||||
promptList.value[existingIndex] = prompt
|
||||
} else {
|
||||
// 添加新提示词
|
||||
promptList.value.unshift(prompt)
|
||||
}
|
||||
}
|
||||
@@ -92,25 +106,35 @@ export const usePromptStore = defineStore('prompt', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新提示词列表
|
||||
async function refreshPromptList(userId) {
|
||||
promptList.value = [] // 清空缓存,强制重新加载
|
||||
return await loadPromptList(userId)
|
||||
// 刷新提示词列表(强制重新加载)
|
||||
async function refreshPromptList() {
|
||||
hasLoaded.value = false
|
||||
return await loadPromptList({ force: true })
|
||||
}
|
||||
|
||||
// 根据ID获取提示词
|
||||
function getPromptById(id) {
|
||||
return promptList.value.find(p => p.id === id)
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
currentPrompt,
|
||||
currentVideoInfo,
|
||||
promptList,
|
||||
promptListLoading,
|
||||
promptListError,
|
||||
hasLoaded,
|
||||
|
||||
// Actions
|
||||
setPrompt,
|
||||
clearPrompt,
|
||||
loadPromptList,
|
||||
addPromptToList,
|
||||
removePromptFromList,
|
||||
updatePromptInList,
|
||||
refreshPromptList
|
||||
refreshPromptList,
|
||||
getPromptById
|
||||
}
|
||||
}, {
|
||||
persist: {
|
||||
|
||||
@@ -15,6 +15,7 @@ import BenchmarkForm from './components/BenchmarkForm.vue'
|
||||
import BenchmarkTable from './components/BenchmarkTable.vue'
|
||||
import BatchAnalyzeModal from './components/BatchAnalyzeModal.vue'
|
||||
import SavePromptModal from '@/components/SavePromptModal.vue'
|
||||
import { BenchmarkTaskApi } from '@/api/benchmarkTask'
|
||||
|
||||
const router = useRouter()
|
||||
const promptStore = usePromptStore()
|
||||
@@ -221,11 +222,9 @@ async function handleLoadMore() {
|
||||
}
|
||||
|
||||
function validatePrompt(prompt, warningMsg = '没有提示词') {
|
||||
if (!prompt?.trim()) {
|
||||
message.warning(warningMsg)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
const isValid = prompt?.trim()
|
||||
if (!isValid) message.warning(warningMsg)
|
||||
return !!isValid
|
||||
}
|
||||
|
||||
async function handleCopyBatchPrompt(prompt) {
|
||||
@@ -250,6 +249,39 @@ function handleOpenSavePromptModal(batchPrompt = null) {
|
||||
savePromptModalVisible.value = true
|
||||
}
|
||||
|
||||
// 创建异步任务
|
||||
async function handleCreateAsyncTask() {
|
||||
if (!selectedRowKeys.value.length) {
|
||||
message.warning('请先选择要分析的视频')
|
||||
return
|
||||
}
|
||||
|
||||
// 获取选中的视频 URL
|
||||
const selectedRows = data.value.filter(item => selectedRowKeys.value.includes(item.id))
|
||||
const videoUrls = selectedRows.map(row => row.audio_url || row.share_url).filter(Boolean)
|
||||
|
||||
if (!videoUrls.length) {
|
||||
message.warning('选中的视频没有有效的URL')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await BenchmarkTaskApi.createTask({
|
||||
taskName: `批量分析 ${videoUrls.length} 个视频`,
|
||||
videoUrls: videoUrls,
|
||||
})
|
||||
|
||||
if (response?.code === 0 || response?.data) {
|
||||
message.success('任务创建成功!请到任务中心查看进度')
|
||||
} else {
|
||||
message.error(response?.message || '创建任务失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建任务失败:', error)
|
||||
message.error('创建任务失败')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadTableDataFromSession()
|
||||
})
|
||||
@@ -279,6 +311,7 @@ defineOptions({ name: 'ContentStyleBenchmark' })
|
||||
@export="handleExportToExcel"
|
||||
@batch-analyze="handleBatchAnalyze"
|
||||
@load-more="handleLoadMore"
|
||||
@create-async-task="handleCreateAsyncTask"
|
||||
/>
|
||||
|
||||
<section v-if="!data.length" class="card results-card empty-state">
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onActivated, computed, watch } from 'vue'
|
||||
import { usePromptStore } from '@/stores/prompt'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { CommonService } from '@/api/common'
|
||||
import useVoiceText from '@/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 PromptSelector from '@/components/PromptSelector.vue'
|
||||
import { setJSON, getJSON } from '@/utils/storage'
|
||||
import { copyToClipboard } from '@/utils/clipboard'
|
||||
import BasicLayout from '@/layouts/components/BasicLayout.vue'
|
||||
|
||||
@@ -18,11 +16,11 @@ const promptStore = usePromptStore()
|
||||
const userStore = useUserStore()
|
||||
const md = new MarkdownIt()
|
||||
|
||||
// 表单数据(合并为单一输入)
|
||||
// 表单数据
|
||||
const form = ref({
|
||||
prompt: '',
|
||||
userInput: '', // 用户输入的文本或视频链接
|
||||
amplitude: 50 // 幅度,默认50%
|
||||
userInput: '',
|
||||
amplitude: 50
|
||||
})
|
||||
|
||||
// 生成的文案内容
|
||||
@@ -37,52 +35,8 @@ const originalContent = ref('')
|
||||
const isLoading = ref(false)
|
||||
const { getVoiceText } = useVoiceText()
|
||||
|
||||
// 提示词相关状态
|
||||
const allPrompts = ref([])
|
||||
const loadingPrompts = ref(false)
|
||||
// ===== 使用 Store 作为单一数据源 =====
|
||||
const selectedPromptId = ref(null)
|
||||
const promptSearchKeyword = ref('')
|
||||
const DISPLAY_COUNT = 6 // 展示的提示词数量
|
||||
|
||||
/**
|
||||
* 加载用户提示词列表
|
||||
* 从服务器获取当前用户的提示词,并按创建时间倒序排列
|
||||
*/
|
||||
async function loadUserPrompts() {
|
||||
const userId = Number(userStore.userId)
|
||||
if (!userId) {
|
||||
console.warn('无法获取用户ID,跳过加载提示词')
|
||||
return
|
||||
}
|
||||
|
||||
loadingPrompts.value = true
|
||||
try {
|
||||
const response = await UserPromptApi.getUserPromptPage({
|
||||
userId: userId,
|
||||
status: 1, // 只加载启用的提示词
|
||||
pageNo: 1,
|
||||
pageSize: 100, // 加载前100个
|
||||
})
|
||||
|
||||
if (response && (response.code === 0 || response.code === 200)) {
|
||||
const list = response.data?.list || []
|
||||
// 按创建时间倒序排列(最新的在前)
|
||||
allPrompts.value = list.sort((a, b) => {
|
||||
const timeA = a.createTime ? new Date(a.createTime).getTime() : 0
|
||||
const timeB = b.createTime ? new Date(b.createTime).getTime() : 0
|
||||
return timeB - timeA
|
||||
})
|
||||
} else {
|
||||
throw new Error(response?.msg || response?.message || '加载失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载提示词列表失败:', error)
|
||||
// 不显示错误提示,避免影响用户体验
|
||||
} finally {
|
||||
loadingPrompts.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 处理提示词选择
|
||||
function handlePromptChange(prompt) {
|
||||
@@ -96,59 +50,23 @@ function handlePromptChange(prompt) {
|
||||
promptStore.setPrompt(prompt.content, prompt)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 等待用户信息初始化完成
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function waitForUserInfo() {
|
||||
// 等待 store 从本地存储恢复完成(最多等待 500ms)
|
||||
let waitCount = 0
|
||||
const maxWait = 50 // 50 * 10ms = 500ms
|
||||
while (!userStore.isHydrated && waitCount < maxWait) {
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
waitCount++
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保用户信息已加载
|
||||
* 如果已登录但 userId 为空,则从服务器获取用户信息
|
||||
*/
|
||||
async function ensureUserInfoLoaded() {
|
||||
const isLoggedIn = userStore.isLoggedIn
|
||||
const hasNoUserId = !userStore.userId
|
||||
|
||||
if (isLoggedIn && hasNoUserId) {
|
||||
try {
|
||||
await userStore.fetchUserInfo()
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化页面数据
|
||||
* 1. 恢复之前保存的提示词(如果有)
|
||||
* 2. 等待用户信息初始化
|
||||
* 3. 确保用户信息已加载
|
||||
* 4. 加载提示词列表
|
||||
* 通过 Store 加载提示词(Store 会自动缓存)
|
||||
*/
|
||||
async function initializePage() {
|
||||
// 1. 恢复之前保存的提示词
|
||||
// 恢复之前保存的提示词
|
||||
if (promptStore.currentPrompt) {
|
||||
form.value.prompt = promptStore.currentPrompt
|
||||
}
|
||||
|
||||
// 2. 等待用户信息初始化完成
|
||||
await waitForUserInfo()
|
||||
// 加载提示词列表(自建 + 收藏,Store 会自动缓存)
|
||||
await promptStore.loadPromptList()
|
||||
|
||||
// 3. 确保用户信息已加载
|
||||
await ensureUserInfoLoaded()
|
||||
|
||||
// 4. 加载提示词列表
|
||||
await loadUserPrompts()
|
||||
// 如果有选中的提示词,同步 ID
|
||||
if (promptStore.currentVideoInfo?.id) {
|
||||
selectedPromptId.value = promptStore.currentVideoInfo.id
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
@@ -156,16 +74,6 @@ onMounted(() => {
|
||||
initializePage()
|
||||
})
|
||||
|
||||
// 监听 userId 变化:如果之前没有 userId,现在有了,则自动加载提示词
|
||||
watch(() => userStore.userId, async (newUserId, oldUserId) => {
|
||||
const userIdChanged = newUserId && !oldUserId
|
||||
const hasNoPrompts = allPrompts.value.length === 0
|
||||
|
||||
if (userIdChanged && hasNoPrompts) {
|
||||
await loadUserPrompts()
|
||||
}
|
||||
})
|
||||
|
||||
// 生成文案(流式)
|
||||
async function generateCopywriting() {
|
||||
const inputContent = form.value.userInput || ''
|
||||
@@ -358,17 +266,11 @@ defineOptions({ name: 'ContentStyleCopywriting' })
|
||||
<a-card class="form-card" :bordered="false" title="创作设置">
|
||||
<a-form :model="form" layout="vertical" class="form-container">
|
||||
<a-form-item class="form-item">
|
||||
<!-- 使用 PromptSelector 组件 -->
|
||||
<!-- 使用 PromptSelector 组件(数据来自 Store) -->
|
||||
<PromptSelector
|
||||
v-model="selectedPromptId"
|
||||
:prompts="allPrompts"
|
||||
:loading="loadingPrompts"
|
||||
:search-keyword="promptSearchKeyword"
|
||||
@change="handlePromptChange"
|
||||
@update:searchKeyword="promptSearchKeyword = $event"
|
||||
/>
|
||||
|
||||
|
||||
</a-form-item>
|
||||
|
||||
<!-- 统一输入:文本或视频链接 -->
|
||||
|
||||
@@ -11,7 +11,7 @@ defineProps({
|
||||
hasMore: { type: Boolean, default: false },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:selectedRowKeys', 'export', 'batchAnalyze', 'loadMore'])
|
||||
const emit = defineEmits(['update:selectedRowKeys', 'export', 'batchAnalyze', 'loadMore', 'createAsyncTask'])
|
||||
|
||||
const defaultColumns = [
|
||||
{ title: '封面', key: 'cover', dataIndex: 'cover', width: 100 },
|
||||
@@ -59,6 +59,13 @@ function formatNumber(value) {
|
||||
@click="$emit('batchAnalyze')"
|
||||
:disabled="data.length === 0 || selectedRowKeys.length === 0 || selectedRowKeys.length > 20"
|
||||
/>
|
||||
<GradientButton
|
||||
text="异步任务"
|
||||
size="small"
|
||||
@click="$emit('createAsyncTask')"
|
||||
:disabled="data.length === 0 || selectedRowKeys.length === 0"
|
||||
icon="clock-circle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<a-table
|
||||
@@ -90,17 +97,8 @@ function formatNumber(value) {
|
||||
<template v-else-if="column.key === 'play_count'">
|
||||
{{ record.play_count ? (record.play_count / 10000).toFixed(1) + 'w' : '0' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'digg_count'">
|
||||
{{ formatNumber(record.digg_count) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'comment_count'">
|
||||
{{ formatNumber(record.comment_count) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'share_count'">
|
||||
{{ formatNumber(record.share_count) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'collect_count'">
|
||||
{{ formatNumber(record.collect_count) }}
|
||||
<template v-else-if="['digg_count', 'comment_count', 'share_count', 'collect_count'].includes(column.key)">
|
||||
{{ formatNumber(record[column.key]) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'create_time'">
|
||||
{{ formatTime(record.create_time) }}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<BasicLayout title="对标分析任务">
|
||||
<div class="task-list-container">
|
||||
<!-- 筛选 -->
|
||||
<div class="filter-section">
|
||||
<a-select
|
||||
v-model:value="filterStatus"
|
||||
placeholder="全部状态"
|
||||
style="width: 150px"
|
||||
allowClear
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<a-select-option :value="0">待处理</a-select-option>
|
||||
<a-select-option :value="1">处理中</a-select-option>
|
||||
<a-select-option :value="2">成功</a-select-option>
|
||||
<a-select-option :value="3">失败</a-select-option>
|
||||
</a-select>
|
||||
<a-button type="primary" @click="handleRefresh" :loading="loading">刷新</a-button>
|
||||
</div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="taskList"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag :color="getStatusColor(record.status)">{{ getStatusText(record.status) }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'progress'">
|
||||
<a-progress :percent="record.progress" :status="getProgressStatus(record.status)" size="small" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button v-if="record.status === 2 && record.generatedPrompt" type="link" size="small" @click="handleViewPrompt(record)">查看</a-button>
|
||||
<a-button v-if="record.status === 2 && record.generatedPrompt" type="link" size="small" @click="handleCopyPrompt(record)">复制</a-button>
|
||||
<a-popconfirm v-if="record.status !== 1" title="确定删除?" @confirm="handleDelete(record)">
|
||||
<a-button type="link" size="small" danger>删除</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- 提示词弹窗 -->
|
||||
<a-modal v-model:open="promptModalVisible" title="生成的提示词" :footer="null" width="700px">
|
||||
<div class="prompt-content">{{ currentPrompt }}</div>
|
||||
<div class="prompt-actions">
|
||||
<a-button type="primary" @click="handleCopyCurrentPrompt">复制到剪贴板</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</BasicLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { BenchmarkTaskApi } from '@/api/benchmarkTask'
|
||||
import { copyToClipboard } from '@/utils/clipboard'
|
||||
import BasicLayout from '@/layouts/components/BasicLayout.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const taskList = ref([])
|
||||
const filterStatus = ref(undefined)
|
||||
const promptModalVisible = ref(false)
|
||||
const currentPrompt = ref('')
|
||||
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{ title: '任务名称', dataIndex: 'taskName', key: 'taskName', ellipsis: true },
|
||||
{ title: '视频数量', dataIndex: 'videoCount', key: 'videoCount', width: 100 },
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', width: 100 },
|
||||
{ title: '进度', dataIndex: 'progress', key: 'progress', width: 150 },
|
||||
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 180 },
|
||||
{ title: '操作', key: 'action', width: 180 },
|
||||
]
|
||||
|
||||
const STATUS_MAP = {
|
||||
color: { 0: 'default', 1: 'processing', 2: 'success', 3: 'error' },
|
||||
text: { 0: '待处理', 1: '处理中', 2: '成功', 3: '失败' }
|
||||
}
|
||||
|
||||
let refreshTimer = null
|
||||
|
||||
async function loadTaskList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await BenchmarkTaskApi.getTaskPage({
|
||||
pageNo: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
status: filterStatus.value,
|
||||
})
|
||||
if (response?.data) {
|
||||
taskList.value = response.data.list || []
|
||||
pagination.total = response.data.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载失败:', error)
|
||||
message.error('加载任务列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleRefresh() { loadTaskList() }
|
||||
function handleFilterChange() { pagination.current = 1; loadTaskList() }
|
||||
function handleTableChange(page) { pagination.current = page.current; pagination.pageSize = page.pageSize; loadTaskList() }
|
||||
|
||||
function getStatusColor(status) {
|
||||
return STATUS_MAP.color[status] || 'default'
|
||||
}
|
||||
|
||||
function getStatusText(status) {
|
||||
return STATUS_MAP.text[status] || '未知'
|
||||
}
|
||||
|
||||
function getProgressStatus(status) {
|
||||
if (status === 3) return 'exception'
|
||||
if (status === 2) return 'success'
|
||||
return 'active'
|
||||
}
|
||||
|
||||
function handleViewPrompt(record) {
|
||||
currentPrompt.value = record.generatedPrompt
|
||||
promptModalVisible.value = true
|
||||
}
|
||||
|
||||
async function copyPromptText(text) {
|
||||
const success = await copyToClipboard(text)
|
||||
message[success ? 'success' : 'error'](success ? '已复制' : '复制失败')
|
||||
}
|
||||
|
||||
function handleCopyPrompt(record) {
|
||||
copyPromptText(record.generatedPrompt)
|
||||
}
|
||||
|
||||
function handleCopyCurrentPrompt() {
|
||||
copyPromptText(currentPrompt.value)
|
||||
}
|
||||
|
||||
async function handleDelete(record) {
|
||||
try {
|
||||
await BenchmarkTaskApi.deleteTask(record.id)
|
||||
message.success('删除成功')
|
||||
loadTaskList()
|
||||
} catch {
|
||||
message.error('删除失败')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadTaskList()
|
||||
refreshTimer = setInterval(() => {
|
||||
const hasRunning = taskList.value.some(t => t.status === 0 || t.status === 1)
|
||||
if (hasRunning) loadTaskList()
|
||||
}, 5000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (refreshTimer) clearInterval(refreshTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.task-list-container { padding: 24px; background: var(--bg-primary); border-radius: 8px; }
|
||||
.filter-section { display: flex; gap: 16px; margin-bottom: 16px; }
|
||||
.prompt-content { padding: 16px; background: var(--bg-secondary); border-radius: 6px; white-space: pre-wrap; max-height: 400px; overflow-y: auto; }
|
||||
.prompt-actions { margin-top: 16px; text-align: right; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user