feat: 路由动态权限控制、图片广场优化

This commit is contained in:
Gsh
2026-01-03 17:03:42 +08:00
parent 3892ff1937
commit 42edd4c230
11 changed files with 660 additions and 145 deletions

View File

@@ -12,10 +12,17 @@ import {
ZoomIn,
} from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { getSelectableTokenInfo } from '@/api';
import { generateImage, getImageModels, getTaskStatus } from '@/api/aiImage';
const props = defineProps({
isActive: {
type: Boolean,
default: true,
},
});
const emit = defineEmits(['task-created']);
// State
@@ -41,6 +48,19 @@ const canGenerate = computed(() => {
return selectedModelId.value && prompt.value && !generating.value;
});
// Watch isActive to manage polling
watch(() => props.isActive, (active) => {
if (active) {
// Resume polling if we have a processing task
if (currentTaskId.value && currentTask.value?.taskStatus === 'Processing') {
startPolling(currentTaskId.value);
}
}
else {
stopPolling();
}
});
// Methods
async function fetchTokens() {
tokenLoading.value = true;
@@ -197,6 +217,12 @@ function startPolling(taskId: string) {
}
async function pollStatus(taskId: string) {
// Double check active status before polling (though timer should be cleared)
if (!props.isActive) {
stopPolling();
return;
}
try {
const res = await getTaskStatus(taskId);
// Handle response structure if needed
@@ -257,7 +283,8 @@ async function downloadImage() {
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} catch (e) {
}
catch (e) {
console.error('Download failed', e);
// Fallback
window.open(currentTask.value.storeUrl, '_blank');
@@ -324,14 +351,15 @@ onUnmounted(() => {
配置
</h2>
<!-- Token & Model -->
<div class="bg-gray-50 p-4 rounded-lg space-y-4">
<el-form-item label="API密钥" class="mb-0">
<el-form label-position="top" class="space-y-2" label-width="auto">
<!-- Token -->
<el-form-item label="API密钥 (可选)">
<el-select
v-model="selectedTokenId"
placeholder="请选择API密钥"
class="w-full"
:loading="tokenLoading"
clearable
>
<el-option
v-for="token in tokenOptions"
@@ -343,7 +371,8 @@ onUnmounted(() => {
</el-select>
</el-form-item>
<el-form-item label="模型" class="mb-0">
<!-- Model -->
<el-form-item label="模型" required>
<el-select
v-model="selectedModelId"
placeholder="请选择模型"
@@ -363,57 +392,59 @@ onUnmounted(() => {
</el-option>
</el-select>
</el-form-item>
</div>
<!-- Prompt -->
<div class="space-y-2">
<div class="flex justify-between items-center">
<label class="text-sm font-medium text-gray-700">提示词</label>
<el-button link type="primary" size="small" @click="clearPrompt">
<el-icon class="mr-1">
<Delete />
</el-icon>清空
</el-button>
</div>
<el-input
v-model="prompt"
type="textarea"
:autosize="{ minRows: 8, maxRows: 15 }"
placeholder="描述你想要生成的画面,例如:一只在太空中飞行的赛博朋克风格的猫..."
maxlength="2000"
show-word-limit
class="custom-textarea"
/>
</div>
<!-- Reference Image -->
<div class="space-y-2">
<label class="text-sm font-medium text-gray-700">参考图 (可选)</label>
<div class="bg-gray-50 p-4 rounded-lg border border-dashed border-gray-300 hover:border-blue-400 transition-colors">
<el-upload
v-model:file-list="fileList"
action="#"
list-type="picture-card"
:auto-upload="false"
:limit="2"
:on-change="handleFileChange"
:on-remove="handleRemove"
accept=".jpg,.jpeg,.png,.bmp,.webp"
:class="{ 'hide-upload-btn': fileList.length >= 2 }"
>
<div class="flex flex-col items-center justify-center text-gray-400">
<el-icon class="text-2xl mb-2">
<Plus />
</el-icon>
<span class="text-xs">点击上传</span>
<!-- Prompt -->
<el-form-item label="提示词" required class="prompt-form-item ">
<template #label>
<div class="flex justify-between items-center w-full ">
<span>提示词</span>
<el-button link type="primary" size="small" @click="clearPrompt">
<el-icon class="mr-1">
<Delete />
</el-icon>清空
</el-button>
</div>
</template>
<el-input
v-model="prompt"
type="textarea"
:autosize="{ minRows: 8, maxRows: 8 }"
placeholder="描述你想要生成的画面,例如:一只在太空中飞行的赛博朋克风格的猫..."
maxlength="2000"
show-word-limit
class="custom-textarea"
resize="vertical"
/>
</el-form-item>
<!-- Reference Image -->
<el-form-item label="参考图 (可选)">
<div class="w-full bg-gray-50 p-4 rounded-lg border border-dashed border-gray-300 hover:border-blue-400 transition-colors">
<el-upload
v-model:file-list="fileList"
action="#"
list-type="picture-card"
:auto-upload="false"
:limit="2"
:on-change="handleFileChange"
:on-remove="handleRemove"
accept=".jpg,.jpeg,.png,.bmp,.webp"
:class="{ 'hide-upload-btn': fileList.length >= 2 }"
>
<div class="flex flex-col items-center justify-center text-gray-400">
<el-icon class="text-2xl mb-2">
<Plus />
</el-icon>
<span class="text-xs">点击上传</span>
</div>
</el-upload>
<div class="text-xs text-gray-400 mt-2 flex justify-between items-center flex-wrap gap-2">
<span>最多2张< 5MB (支持 JPG/PNG/WEBP)</span>
<el-checkbox v-model="compressImage" label="压缩图片" size="small" />
</div>
</el-upload>
<div class="text-xs text-gray-400 mt-2 flex justify-between items-center flex-wrap gap-2">
<span>最多2张< 5MB (支持 JPG/PNG/WEBP)</span>
<el-checkbox v-model="compressImage" label="压缩图片" size="small" />
</div>
</div>
</div>
</el-form-item>
</el-form>
</div>
<div class="mt-auto pt-4">
@@ -450,7 +481,7 @@ onUnmounted(() => {
<div class="absolute bottom-4 right-4 opacity-0 group-hover:opacity-100 transition-opacity flex gap-2 z-10">
<el-button circle type="primary" :icon="ZoomIn" @click="showViewer = true" />
<el-button circle type="primary" :icon="Download" @click="downloadImage" />
<el-button circle type="info" :icon="Refresh" title="新任务" @click="currentTask = null" />
<el-button circle type="success" :icon="Refresh" title="重新生成" @click="handleGenerate" />
</div>
<el-image-viewer
@@ -563,4 +594,8 @@ onUnmounted(() => {
:deep(.hide-upload-btn .el-upload--picture-card) {
display: none;
}
/* 隐藏默认的标签 */
:deep(.prompt-form-item .el-form-item__label){
display: flex;
}
</style>