feat: enhance prompt selector with category tags and improve benchmark task processing
Add visual indicators for prompt categories and source types in the prompt selector component, and refactor benchmark task execution to use Dify streaming analysis with proper error handling and text extraction from Alibaba Cloud transcription results.
This commit is contained in:
@@ -15,7 +15,13 @@
|
||||
:value="prompt.id"
|
||||
>
|
||||
<div class="prompt-option">
|
||||
<span class="prompt-option-name">{{ prompt.name }}</span>
|
||||
<div class="prompt-option-left">
|
||||
<span class="prompt-option-name">{{ prompt.name }}</span>
|
||||
<span v-if="prompt.category" class="prompt-option-tag category">{{ prompt.category }}</span>
|
||||
</div>
|
||||
<span class="prompt-option-tag" :class="prompt.source === 'created' ? 'created' : 'favorite'">
|
||||
{{ prompt.source === 'created' ? '自建' : '收藏' }}
|
||||
</span>
|
||||
</div>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
@@ -313,6 +319,37 @@ onMounted(() => {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.prompt-option-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.prompt-option-tag {
|
||||
font-size: 12px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.prompt-option-tag.category {
|
||||
background: rgba(24, 144, 255, 0.1);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.prompt-option-tag.created {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.prompt-option-tag.favorite {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
/* 标签模式 */
|
||||
.prompt-tags-container {
|
||||
margin-bottom: 12px;
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { resolveId } from '@/utils/url'
|
||||
import { exportBenchmarkDataToExcel } from '@/utils/excel'
|
||||
import { usePromptStore } from '@/stores/prompt'
|
||||
import GlobalLoading from '@/components/GlobalLoading.vue'
|
||||
import TikhubService, { InterfaceType, MethodType } from '@/api/tikhub/index.js'
|
||||
import { useBenchmarkData } from './composables/useBenchmarkData'
|
||||
import { useBenchmarkAnalysis } from './composables/useBenchmarkAnalysis'
|
||||
import { formatTime } from './utils/benchmarkUtils'
|
||||
import { copyToClipboard } from '@/utils/clipboard'
|
||||
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()
|
||||
import CreateStyleTaskModal from './components/CreateStyleTaskModal.vue'
|
||||
|
||||
const {
|
||||
data,
|
||||
@@ -33,10 +25,8 @@ const {
|
||||
|
||||
const {
|
||||
loading,
|
||||
batchAnalyzeLoading,
|
||||
globalLoading,
|
||||
globalLoadingText,
|
||||
batchAnalyze,
|
||||
getVoiceText,
|
||||
} = useBenchmarkAnalysis(data, expandedRowKeys, saveTableDataToSession)
|
||||
|
||||
@@ -47,12 +37,8 @@ const form = ref({
|
||||
sort_type: 0,
|
||||
})
|
||||
|
||||
const modalVisible = ref(false)
|
||||
const batchPromptMergedText = ref('')
|
||||
const batchPromptTextCount = ref(0)
|
||||
|
||||
const savePromptModalVisible = ref(false)
|
||||
const savePromptContent = ref('')
|
||||
// 创建风格任务弹窗
|
||||
const createTaskModalVisible = ref(false)
|
||||
|
||||
const maxCursor = ref(0)
|
||||
const hasMore = ref(false)
|
||||
@@ -150,18 +136,6 @@ async function handleExportToExcel() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleBatchAnalyze() {
|
||||
try {
|
||||
await batchAnalyze(selectedRowKeys, async (mergedText, textCount) => {
|
||||
batchPromptMergedText.value = mergedText
|
||||
batchPromptTextCount.value = textCount
|
||||
modalVisible.value = true
|
||||
})
|
||||
} finally {
|
||||
selectedRowKeys.value = []
|
||||
}
|
||||
}
|
||||
|
||||
async function handleResetForm() {
|
||||
form.value = { platform: '抖音', url: '', count: 20, sort_type: 0 }
|
||||
maxCursor.value = 0
|
||||
@@ -221,65 +195,34 @@ async function handleLoadMore() {
|
||||
}
|
||||
}
|
||||
|
||||
function validatePrompt(prompt, warningMsg = '没有提示词') {
|
||||
const isValid = prompt?.trim()
|
||||
if (!isValid) message.warning(warningMsg)
|
||||
return !!isValid
|
||||
// 获取选中视频的 URL 列表
|
||||
function getSelectedVideoUrls() {
|
||||
return data.value
|
||||
.filter(item => selectedRowKeys.value.includes(item.id))
|
||||
.map(row => row.audio_url || row.share_url)
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
async function handleCopyBatchPrompt(prompt) {
|
||||
if (!validatePrompt(prompt, '没有提示词可复制')) return
|
||||
|
||||
const success = await copyToClipboard(prompt)
|
||||
message[success ? 'success' : 'error'](success ? '提示词已复制到剪贴板' : '复制失败')
|
||||
}
|
||||
|
||||
function handleUseBatchPrompt(prompt) {
|
||||
if (!validatePrompt(prompt, '暂无批量生成的提示词')) return
|
||||
|
||||
promptStore.setPrompt(prompt, { batch: true })
|
||||
router.push('/content-style/copywriting')
|
||||
}
|
||||
|
||||
function handleOpenSavePromptModal(batchPrompt = null) {
|
||||
const promptToSave = batchPrompt || batchPromptMergedText.value
|
||||
if (!validatePrompt(promptToSave, '没有提示词可保存')) return
|
||||
|
||||
savePromptContent.value = promptToSave
|
||||
savePromptModalVisible.value = true
|
||||
}
|
||||
|
||||
// 创建异步任务
|
||||
async function handleCreateAsyncTask() {
|
||||
// 打开创建风格任务弹窗
|
||||
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) {
|
||||
if (selectedRowKeys.value.length > 20) {
|
||||
message.warning('最多只能选择20个视频')
|
||||
return
|
||||
}
|
||||
if (!getSelectedVideoUrls().length) {
|
||||
message.warning('选中的视频没有有效的URL')
|
||||
return
|
||||
}
|
||||
createTaskModalVisible.value = true
|
||||
}
|
||||
|
||||
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('创建任务失败')
|
||||
}
|
||||
// 任务创建成功后清空选择
|
||||
function handleTaskCreated() {
|
||||
selectedRowKeys.value = []
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -291,63 +234,51 @@ defineOptions({ name: 'ContentStyleBenchmark' })
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<a-spin :spinning="batchAnalyzeLoading" tip="批量分析中,请稍候..." wrapperClassName="batch-analyze-spin-wrapper">
|
||||
<div class="stack">
|
||||
<!-- 表单区域 -->
|
||||
<BenchmarkForm
|
||||
v-model="form"
|
||||
:loading="loading"
|
||||
@analyze="handleAnalyzeUser"
|
||||
@reset="handleResetForm"
|
||||
/>
|
||||
<div class="stack">
|
||||
<!-- 表单区域 -->
|
||||
<BenchmarkForm
|
||||
v-model="form"
|
||||
:loading="loading"
|
||||
@analyze="handleAnalyzeUser"
|
||||
@reset="handleResetForm"
|
||||
/>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<BenchmarkTable
|
||||
:data="data"
|
||||
v-model:selectedRowKeys="selectedRowKeys"
|
||||
:loading="loading"
|
||||
:loading-more="loadingMore"
|
||||
:has-more="hasMore"
|
||||
@export="handleExportToExcel"
|
||||
@batch-analyze="handleBatchAnalyze"
|
||||
@load-more="handleLoadMore"
|
||||
@create-async-task="handleCreateAsyncTask"
|
||||
/>
|
||||
<!-- 表格区域 -->
|
||||
<BenchmarkTable
|
||||
:data="data"
|
||||
v-model:selectedRowKeys="selectedRowKeys"
|
||||
:loading="loading"
|
||||
:loading-more="loadingMore"
|
||||
:has-more="hasMore"
|
||||
@export="handleExportToExcel"
|
||||
@load-more="handleLoadMore"
|
||||
@create-async-task="handleCreateAsyncTask"
|
||||
/>
|
||||
|
||||
<section v-if="!data.length" class="card results-card empty-state">
|
||||
<a-empty description="暂无数据,请点击开始分析">
|
||||
<template #image>
|
||||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="20" y="30" width="80" height="60" rx="4" stroke="currentColor" stroke-width="2" fill="none" opacity="0.3"/>
|
||||
<circle cx="40" cy="50" r="8" fill="currentColor" opacity="0.4"/>
|
||||
<rect x="54" y="47" width="40" height="6" rx="3" fill="currentColor" opacity="0.4"/>
|
||||
<rect x="54" y="60" width="32" height="6" rx="3" fill="currentColor" opacity="0.4"/>
|
||||
<line x1="32" y1="75" x2="88" y2="75" stroke="currentColor" stroke-width="2" opacity="0.3"/>
|
||||
<line x1="32" y1="82" x2="88" y2="82" stroke="currentColor" stroke-width="2" opacity="0.3"/>
|
||||
<line x1="32" y1="89" x2="72" y2="89" stroke="currentColor" stroke-width="2" opacity="0.3"/>
|
||||
</svg>
|
||||
</template>
|
||||
</a-empty>
|
||||
</section>
|
||||
</div>
|
||||
</a-spin>
|
||||
<section v-if="!data.length" class="card results-card empty-state">
|
||||
<a-empty description="暂无数据,请点击开始分析">
|
||||
<template #image>
|
||||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="20" y="30" width="80" height="60" rx="4" stroke="currentColor" stroke-width="2" fill="none" opacity="0.3"/>
|
||||
<circle cx="40" cy="50" r="8" fill="currentColor" opacity="0.4"/>
|
||||
<rect x="54" y="47" width="40" height="6" rx="3" fill="currentColor" opacity="0.4"/>
|
||||
<rect x="54" y="60" width="32" height="6" rx="3" fill="currentColor" opacity="0.4"/>
|
||||
<line x1="32" y1="75" x2="88" y2="75" stroke="currentColor" stroke-width="2" opacity="0.3"/>
|
||||
<line x1="32" y1="82" x2="88" y2="82" stroke="currentColor" stroke-width="2" opacity="0.3"/>
|
||||
<line x1="32" y1="89" x2="72" y2="89" stroke="currentColor" stroke-width="2" opacity="0.3"/>
|
||||
</svg>
|
||||
</template>
|
||||
</a-empty>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- 批量分析结果弹窗 -->
|
||||
<BatchAnalyzeModal
|
||||
v-model:visible="modalVisible"
|
||||
:merged-text="batchPromptMergedText"
|
||||
:text-count="batchPromptTextCount"
|
||||
@copy="handleCopyBatchPrompt"
|
||||
@save="(prompt) => handleOpenSavePromptModal(prompt)"
|
||||
@use="handleUseBatchPrompt"
|
||||
<!-- 创建风格任务弹窗 -->
|
||||
<CreateStyleTaskModal
|
||||
v-model:visible="createTaskModalVisible"
|
||||
:video-urls="getSelectedVideoUrls()"
|
||||
@success="handleTaskCreated"
|
||||
/>
|
||||
|
||||
<!-- 保存提示词弹窗 -->
|
||||
<SavePromptModal
|
||||
v-model:visible="savePromptModalVisible"
|
||||
:prompt-content="savePromptContent"
|
||||
/>
|
||||
|
||||
|
||||
<!-- 全局 Loading 遮罩 -->
|
||||
<GlobalLoading :visible="globalLoading" :text="globalLoadingText" />
|
||||
</div>
|
||||
@@ -399,15 +330,4 @@ defineOptions({ name: 'ContentStyleBenchmark' })
|
||||
color: var(--color-gray-600);
|
||||
font-size: var(--font-size-base);
|
||||
}
|
||||
|
||||
/* 全屏 Spin 遮罩样式 */
|
||||
:deep(.batch-analyze-spin-wrapper) {
|
||||
width: 100%;
|
||||
min-height: calc(100vh - 120px);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
:deep(.batch-analyze-spin-wrapper .ant-spin-spinning) {
|
||||
max-height: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,7 +11,7 @@ defineProps({
|
||||
hasMore: { type: Boolean, default: false },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:selectedRowKeys', 'export', 'batchAnalyze', 'loadMore', 'createAsyncTask'])
|
||||
const emit = defineEmits(['update:selectedRowKeys', 'export', 'loadMore', 'createAsyncTask'])
|
||||
|
||||
const defaultColumns = [
|
||||
{ title: '封面', key: 'cover', dataIndex: 'cover', width: 100 },
|
||||
@@ -56,14 +56,8 @@ function formatNumber(value) {
|
||||
<GradientButton
|
||||
:text="`批量分析 (${selectedRowKeys.length}/20)`"
|
||||
size="small"
|
||||
@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"
|
||||
:disabled="data.length === 0 || selectedRowKeys.length === 0 || selectedRowKeys.length > 20"
|
||||
icon="clock-circle"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
<script setup>
|
||||
import { ref, watchEffect } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { BenchmarkTaskApi } from '@/api/benchmarkTask'
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
videoUrls: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:visible', 'success'])
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const form = ref({
|
||||
taskName: '',
|
||||
promptName: '',
|
||||
category: '',
|
||||
})
|
||||
|
||||
// 监听 visible 变化,重置表单
|
||||
watchEffect(() => {
|
||||
if (props.visible) {
|
||||
const videoCount = props.videoUrls?.length || 0
|
||||
form.value = {
|
||||
taskName: `风格分析 ${videoCount} 个视频`,
|
||||
promptName: '',
|
||||
category: '',
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!form.value.taskName.trim()) {
|
||||
message.warning('请输入任务名称')
|
||||
return
|
||||
}
|
||||
|
||||
if (!props.videoUrls?.length) {
|
||||
message.warning('没有选择视频')
|
||||
return
|
||||
}
|
||||
|
||||
if (!form.value.promptName.trim()) {
|
||||
message.warning('请输入提示词名称')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const payload = {
|
||||
taskName: form.value.taskName.trim(),
|
||||
videoUrls: props.videoUrls,
|
||||
saveAsPrompt: true,
|
||||
promptName: form.value.promptName.trim(),
|
||||
}
|
||||
|
||||
const response = await BenchmarkTaskApi.createTask(payload)
|
||||
|
||||
if (response?.code === 0 || response?.data) {
|
||||
message.success('任务创建成功!请到任务中心查看进度')
|
||||
emit('update:visible', false)
|
||||
emit('success', response.data)
|
||||
} else {
|
||||
throw new Error(response?.msg || response?.message || '创建任务失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建任务失败:', error)
|
||||
message.error(error?.message || '创建任务失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
emit('update:visible', false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
:open="visible"
|
||||
title="创建风格分析任务"
|
||||
:width="500"
|
||||
:maskClosable="false"
|
||||
:confirmLoading="loading"
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form :model="form" layout="vertical">
|
||||
<a-form-item label="任务名称" required>
|
||||
<a-input
|
||||
v-model:value="form.taskName"
|
||||
placeholder="请输入任务名称"
|
||||
:maxlength="100"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="提示词名称" required>
|
||||
<a-input
|
||||
v-model:value="form.promptName"
|
||||
placeholder="任务完成后将保存为提示词"
|
||||
:maxlength="50"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="分类/标签">
|
||||
<a-input
|
||||
v-model:value="form.category"
|
||||
placeholder="可选:输入分类或标签"
|
||||
:maxlength="20"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="选中视频">
|
||||
<a-tag color="blue">{{ videoUrls.length }} 个视频</a-tag>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" :loading="loading" @click="handleSubmit">
|
||||
创建任务
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, defineAsyncComponent, markRaw, onMounted, watch } from 'vue'
|
||||
import { VideoCameraOutlined, UserOutlined } from '@ant-design/icons-vue'
|
||||
import { VideoCameraOutlined, UserOutlined, FormOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const STORAGE_KEY = 'task-management-active-tab'
|
||||
|
||||
@@ -50,6 +50,12 @@ const NAV_ITEMS = [
|
||||
label: '数字人视频任务',
|
||||
icon: UserOutlined,
|
||||
component: markRaw(defineAsyncComponent(() => import('../digital-human-task/index.vue')))
|
||||
},
|
||||
{
|
||||
type: 'style-task',
|
||||
label: '风格任务',
|
||||
icon: FormOutlined,
|
||||
component: markRaw(defineAsyncComponent(() => import('../../../task-center/BenchmarkTaskList.vue')))
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -1,68 +1,69 @@
|
||||
<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"
|
||||
<div class="task-list-container">
|
||||
<!-- 筛选 -->
|
||||
<div class="filter-section">
|
||||
<a-select
|
||||
v-model:value="filterStatus"
|
||||
placeholder="全部状态"
|
||||
style="width: 150px"
|
||||
allowClear
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<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>
|
||||
<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>
|
||||
</BasicLayout>
|
||||
|
||||
<!-- 列表 -->
|
||||
<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 === 'createTime'">
|
||||
{{ formatTime(record.createTime) }}
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { BenchmarkTaskApi } from '@/api/benchmarkTask'
|
||||
import { copyToClipboard } from '@/utils/clipboard'
|
||||
import BasicLayout from '@/layouts/components/BasicLayout.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const taskList = ref([])
|
||||
@@ -92,7 +93,10 @@ const STATUS_MAP = {
|
||||
text: { 0: '待处理', 1: '处理中', 2: '成功', 3: '失败' }
|
||||
}
|
||||
|
||||
let refreshTimer = null
|
||||
function formatTime(time) {
|
||||
if (!time) return '-'
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
async function loadTaskList() {
|
||||
loading.value = true
|
||||
@@ -162,20 +166,33 @@ async function handleDelete(record) {
|
||||
|
||||
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 scoped lang="less">
|
||||
.task-list-container {
|
||||
padding: 24px;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.prompt-content {
|
||||
padding: 16px;
|
||||
background: var(--color-bg-secondary, #f5f5f5);
|
||||
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