Unverified Commit d8558a0c authored by LiangLiu's avatar LiangLiu Committed by GitHub
Browse files

update froentend (#470)


Co-authored-by: default avatarqinxinyi <qxy118045534@163.com>
parent 2a31ba43
......@@ -28,10 +28,10 @@
color: white;
/* Apple 极简黑白风格 - 品牌色 */
--brand-primary: #a68ee7;
--brand-primary-light: #cdbbfe;
--brand-primary-rgb: 179, 151, 255;
--brand-primary-light-rgb: 210, 193, 255;
--brand-primary: #90dce1;
--brand-primary-light: #90dce1;
--brand-primary-rgb: 89, 194, 233;
--brand-primary-light-rgb: 142, 220, 255;
/* Apple 极简黑白风格 - 基础颜色 */
--text-primary: #1d1d1f; /* 浅色模式主要文字 */
......@@ -841,12 +841,6 @@ body {
padding: 0.5rem 0.75rem !important;
}
/* 创建视频页面移动端适配 */
.max-w-4xl.mx-auto {
max-width: 100% !important;
padding: 0 1rem !important;
}
/* 上传区域在移动端调整 */
.upload-section {
grid-template-columns: 1fr !important;
......
......@@ -55,7 +55,7 @@ async function switchLang() {
// };
const languageOptions = ref([
{ code: 'zh', name: '中文', flag: 'ZH' },
{ code: 'zh', name: '中文', flag: '' },
{ code: 'en', name: 'English', flag: 'EN' }
]);
......
......@@ -10,7 +10,9 @@ export const locale = i18n.global.locale
const loginLoading = ref(false);
const initLoading = ref(false);
const downloadLoading = ref(false);
const downloadLoadingMessage = ref('');
const isLoading = ref(false); // 页面加载loading状态
const isPageLoading = ref(false); // 分页加载loading状态
// 录音相关状态
const isRecording = ref(false);
......@@ -45,7 +47,8 @@ export const locale = i18n.global.locale
confirm: () => { }
});
const submitting = ref(false);
const templateLoading = ref(false); // 模板加载状态
const templateLoading = ref(false); // 模板/任务复用加载状态
const templateLoadingMessage = ref('');
const taskSearchQuery = ref('');
const sidebarCollapsed = ref(false);
const showExpandHint = ref(false);
......@@ -135,6 +138,7 @@ export const locale = i18n.global.locale
const templatePaginationKey = ref(0);
const imageHistory = ref([]);
const audioHistory = ref([]);
const ttsHistory = ref([]);
// 模板文件缓存,避免重复下载
const currentUser = ref({});
......@@ -1194,7 +1198,7 @@ export const locale = i18n.global.locale
};
const triggerAudioUpload = () => {
const audioInput = document.querySelector('input[type="file"][accept="audio/*"]');
const audioInput = document.querySelector('input[type="file"][data-role="audio-input"]');
if (audioInput) {
audioInput.click();
} else {
......@@ -1223,7 +1227,7 @@ export const locale = i18n.global.locale
updateUploadedContentStatus();
console.log('音频已移除');
// 重置音频文件输入框,确保可以重新选择相同文件
const audioInput = document.querySelector('input[type="file"][accept="audio/*"]');
const audioInput = document.querySelector('input[type="file"][data-role="audio-input"]');
if (audioInput) {
audioInput.value = '';
}
......@@ -1239,7 +1243,14 @@ export const locale = i18n.global.locale
const handleAudioUpload = (event) => {
const file = event.target.files[0];
if (file) {
if (file && (file.type?.startsWith('audio/') || file.type?.startsWith('video/'))) {
const allowedVideoTypes = ['video/mp4', 'video/x-m4v', 'video/mpeg'];
if (file.type.startsWith('video/') && !allowedVideoTypes.includes(file.type)) {
showAlert(t('unsupportedVideoFormat'), 'warning');
setCurrentAudioPreview(null);
updateUploadedContentStatus();
return;
}
s2vForm.value.audioFile = file;
const reader = new FileReader();
reader.onload = (e) => {
......@@ -1251,6 +1262,9 @@ export const locale = i18n.global.locale
} else {
setCurrentAudioPreview(null);
updateUploadedContentStatus();
if (file) {
showAlert(t('unsupportedAudioOrVideo'), 'warning');
}
}
};
......@@ -2134,15 +2148,15 @@ export const locale = i18n.global.locale
// 分页相关函数
const goToPage = async (page) => {
isLoading.value = true;
isPageLoading.value = true;
if (page < 1 || page > pagination.value?.total_pages || page === currentTaskPage.value) {
isLoading.value = false;
isPageLoading.value = false;
return;
}
currentTaskPage.value = page;
taskPageInput.value = page; // 同步更新输入框
await refreshTasks();
isLoading.value = false;
isPageLoading.value = false;
};
const jumpToPage = async () => {
......@@ -2157,15 +2171,15 @@ export const locale = i18n.global.locale
// Template分页相关函数
const goToTemplatePage = async (page) => {
isLoading.value=true;
isPageLoading.value=true;
if (page < 1 || page > templatePagination.value?.total_pages || page === templateCurrentPage.value) {
isLoading.value = false;
isPageLoading.value = false;
return;
}
templateCurrentPage.value = page;
templatePageInput.value = page; // 同步更新输入框
await loadImageAudioTemplates();
isLoading.value = false;
isPageLoading.value = false;
};
const jumpToTemplatePage = async () => {
......@@ -2270,15 +2284,15 @@ export const locale = i18n.global.locale
// 灵感广场分页相关函数
const goToInspirationPage = async (page) => {
isLoading.value = true;
isPageLoading.value = true;
if (page < 1 || page > inspirationPagination.value?.total_pages || page === inspirationCurrentPage.value) {
isLoading.value = false;
isPageLoading.value = false;
return;
}
inspirationCurrentPage.value = page;
inspirationPageInput.value = page; // 同步更新输入框
await loadInspirationData();
isLoading.value = false;
isPageLoading.value = false;
};
const jumpToInspirationPage = async () => {
......@@ -2623,16 +2637,20 @@ export const locale = i18n.global.locale
if (!confirmed) {
return;
}
// 显示删除中的提示
showAlert(t('deletingTaskAlert'), 'info');
const response = await apiRequest(`/api/v1/task/delete?task_id=${taskId}`, {
method: 'DELETE'
});
if (response && response.ok) {
showAlert(t('taskDeletedSuccessAlert'), 'success');
const deletedTaskIndex = tasks.value.findIndex(task => task.task_id === taskId);
if (deletedTaskIndex !== -1) {
const wasCurrent = currentTask.value?.task_id === taskId;
tasks.value.splice(deletedTaskIndex, 1);
if (wasCurrent) {
currentTask.value = tasks.value[deletedTaskIndex] || tasks.value[deletedTaskIndex - 1] || null;
}
}
refreshTasks(true); // 强制刷新
// 如果是从任务详情页删除,删除成功后关闭详情弹窗
......@@ -2727,9 +2745,16 @@ export const locale = i18n.global.locale
};
const reuseTask = async (task) => {
if (!task) {
showAlert(t('loadTaskDataFailedAlert'), 'danger');
return;
}
try {
templateLoading.value = true;
templateLoadingMessage.value = t('prefillLoadingTask');
// 跳转到任务创建界面
isCreationAreaExpanded.value=true
isCreationAreaExpanded.value = true;
if (showTaskDetailModal.value) {
closeTaskDetailModal();
}
......@@ -2741,6 +2766,9 @@ export const locale = i18n.global.locale
// 获取当前表单
const currentForm = getCurrentForm();
// 立即切换到创建视图,后续资产异步加载
switchToCreateView();
// 设置模型
if (task.params && task.params.model_cls) {
currentForm.model_cls = task.params.model_cls;
......@@ -2779,6 +2807,9 @@ export const locale = i18n.global.locale
// 加载音频文件
if (audioUrl) {
try {
currentForm.audioUrl = audioUrl;
setCurrentAudioPreview(audioUrl);
const audioResponse = await fetch(audioUrl);
if (audioResponse && audioResponse.ok) {
const blob = await audioResponse.blob();
......@@ -2810,13 +2841,6 @@ export const locale = i18n.global.locale
size: file.size,
originalBlobType: blob.type
});
// 使用FileReader生成data URL,与正常上传保持一致
const reader = new FileReader();
reader.onload = (e) => {
setCurrentAudioPreview(e.target.result);
console.log('复用任务 - 音频预览已设置:', e.target.result.substring(0, 50) + '...');
};
reader.readAsDataURL(file);
}
} catch (error) {
console.warn('Failed to load audio file:', error);
......@@ -2828,161 +2852,169 @@ export const locale = i18n.global.locale
showAlert(t('taskMaterialReuseSuccessAlert'), 'success');
// 检查当前路由,如果已经在 generate 页面,则滚动到生成区域
const currentRoute = router.currentRoute.value;
if (currentRoute.path === '/generate') {
// 关闭任务详情弹窗
if (showTaskDetailModal.value) {
closeTaskDetailModal();
}
// 等待 DOM 更新后滚动到生成区域
await nextTick();
// 如果之前有展开过创作区域,保持展开状态
const creationArea = document.querySelector('.creation-area');
if (isCreationAreaExpanded.value) {
// 延迟一点时间确保DOM更新完成
setTimeout(() => {
if (creationArea) {
creationArea.classList.add('show');
}
}, 50);
}
// 滚动到顶部
const mainScrollable = document.querySelector('.main-scrollbar');
if (mainScrollable) {
mainScrollable.scrollTo({
top: 0,
behavior: 'smooth'
});
}
} else {
// 不在 generate 页面,跳转过去
switchToCreateView();
}
} catch (error) {
console.error('Failed to reuse task:', error);
showAlert(t('loadTaskDataFailedAlert'), 'danger');
} finally {
templateLoading.value = false;
templateLoadingMessage.value = '';
}
};
const downloadFile = (fileInfo) => {
const downloadFile = async (fileInfo) => {
if (!fileInfo || !fileInfo.blob) {
showAlert(t('fileUnavailableAlert'), 'danger');
return;
return false;
}
const blob = fileInfo.blob;
const fileName = fileInfo.name || 'download';
const mimeType = blob.type || fileInfo.mimeType || 'application/octet-stream';
const isMobile = typeof navigator !== 'undefined' && /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (isMobile && typeof navigator?.canShare === 'function' && typeof navigator?.share === 'function') {
try {
const shareFile = new File([blob], fileName, { type: mimeType });
if (navigator.canShare({ files: [shareFile] })) {
await navigator.share({
files: [shareFile],
title: fileName
});
showAlert(t('downloadSuccessAlert'), 'success');
return true;
}
} catch (error) {
if (error?.name === 'AbortError') {
console.info('User cancelled share dialog');
showAlert(t('downloadCancelledAlert'), 'info');
return false;
}
console.warn('Native share failed, falling back to download link:', error);
}
}
try {
const url = URL.createObjectURL(fileInfo.blob);
const objectUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileInfo.name || 'download';
a.href = objectUrl;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
URL.revokeObjectURL(objectUrl);
showAlert(t('downloadSuccessAlert'), 'success');
return true;
} catch (error) {
console.error('Download failed:', error);
showAlert(t('downloadFailedAlert'), 'danger');
return false;
}
};
// 处理文件下载
const handleDownloadFile = async (taskId, fileKey, fileName) => {
if (downloadLoading.value) {
showAlert(t('downloadInProgressNotice'), 'info');
return;
}
downloadLoading.value = true;
downloadLoadingMessage.value = t('downloadPreparing');
try {
console.log('开始下载文件:', { taskId, fileKey, fileName })
console.log('开始下载文件:', { taskId, fileKey, fileName });
// 处理文件名,确保有正确的后缀名
let finalFileName = fileName
let finalFileName = fileName;
if (fileName && typeof fileName === 'string') {
// 检查是否已有后缀名
const hasExtension = /\.[a-zA-Z0-9]+$/.test(fileName)
const hasExtension = /\.[a-zA-Z0-9]+$/.test(fileName);
if (!hasExtension) {
// 没有后缀名,根据文件类型添加
const extension = getFileExtension(fileKey)
finalFileName = `${fileName}.${extension}`
console.log('添加后缀名:', finalFileName)
const extension = getFileExtension(fileKey);
finalFileName = `${fileName}.${extension}`;
console.log('添加后缀名:', finalFileName);
}
} else {
// 没有文件名,使用默认名称
finalFileName = `${fileKey}.${getFileExtension(fileKey)}`
finalFileName = `${fileKey}.${getFileExtension(fileKey)}`;
}
// 先尝试从缓存获取
let fileData = getTaskFileFromCache(taskId, fileKey)
console.log('缓存中的文件数据:', fileData)
if (fileData && fileData.blob) {
// 缓存中有blob数据,直接使用
console.log('使用缓存中的文件数据')
downloadFile({ ...fileData, name: finalFileName })
return
}
downloadLoadingMessage.value = t('downloadFetching');
if (fileData && fileData.url) {
// 缓存中有URL,使用URL下载
console.log('使用缓存中的URL下载:', fileData.url)
try {
const response = await fetch(fileData.url)
console.log('文件响应状态:', response.status, response.ok)
let downloadUrl = null;
if (response.ok) {
const blob = await response.blob()
console.log('文件blob大小:', blob.size)
const downloadData = {
blob: blob,
name: finalFileName
}
console.log('构造的文件数据:', downloadData)
downloadFile(downloadData)
return
} else {
console.error('文件响应失败:', response.status, response.statusText)
}
} catch (error) {
console.error('使用缓存URL下载失败:', error)
}
const cachedData = getTaskFileFromCache(taskId, fileKey);
if (cachedData?.url) {
downloadUrl = cachedData.url;
}
if (!fileData) {
console.log('缓存中没有文件,尝试异步获取...')
// 缓存中没有,尝试异步获取
const url = await getTaskFileUrl(taskId, fileKey)
console.log('获取到的文件URL:', url)
if (!downloadUrl) {
downloadUrl = await getTaskFileUrl(taskId, fileKey);
}
if (url) {
const response = await fetch(url)
console.log('文件响应状态:', response.status, response.ok)
if (!downloadUrl) {
throw new Error('无法获取文件URL');
}
if (response.ok) {
const blob = await response.blob()
console.log('文件blob大小:', blob.size)
const response = await fetch(downloadUrl);
if (!response.ok) {
throw new Error(`文件响应失败: ${response.status}`);
}
fileData = {
blob: blob,
name: finalFileName
}
console.log('构造的文件数据:', fileData)
} else {
console.error('文件响应失败:', response.status, response.statusText)
}
const blob = await response.blob();
const isMobileBrowser = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if (isMobileBrowser) {
downloadLoadingMessage.value = '';
downloadLoading.value = false;
showAlert(t('mobileSaveToAlbumTip'), 'info');
const blobUrl = URL.createObjectURL(blob);
const previewWindow = window.open('', '_blank', 'noopener,noreferrer');
if (previewWindow) {
previewWindow.document.write(`<!DOCTYPE html>
<html lang="${locale.value || 'en'}">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>${t('mobileSavePreviewTitle')}</title>
<style>
body { margin: 0; background: #000; color: #fff; font-family: system-ui, sans-serif; }
.wrapper { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 16px; gap: 16px; }
video { width: 100%; height: auto; border-radius: 16px; max-height: calc(100vh - 160px); }
p { text-align: center; line-height: 1.5; font-size: 15px; color: rgba(255,255,255,0.85); }
</style>
</head>
<body>
<div class="wrapper">
<video controls playsinline webkit-playsinline preload="auto" src="${blobUrl}"></video>
<p>${t('mobileSaveInstruction')}</p>
</div>
<script>
window.addEventListener('pagehide', () => URL.revokeObjectURL('${blobUrl}'));
window.addEventListener('beforeunload', () => URL.revokeObjectURL('${blobUrl}'));
</script>
</body>
</html>`);
previewWindow.document.close();
} else {
console.error('无法获取文件URL')
URL.revokeObjectURL(blobUrl);
window.location.href = downloadUrl;
}
return;
}
if (fileData && fileData.blob) {
console.log('开始下载文件:', fileData.name)
downloadFile(fileData)
} else {
console.error('文件数据无效:', fileData)
showAlert(t('fileUnavailableAlert'), 'danger')
}
downloadLoadingMessage.value = t('downloadSaving');
await downloadFile({
blob,
name: finalFileName,
mimeType: blob.type
});
} catch (error) {
console.error('下载失败:', error)
showAlert(t('downloadFailedAlert'), 'danger')
console.error('下载失败:', error);
showAlert(t('downloadFailedAlert'), 'danger');
} finally {
downloadLoading.value = false;
downloadLoadingMessage.value = '';
}
}
......@@ -4750,10 +4782,10 @@ export const locale = i18n.global.locale
// 选择分类
const selectInspirationCategory = async (category) => {
isLoading.value = true;
isPageLoading.value = true;
// 如果点击的是当前分类,不重复请求
if (selectedInspirationCategory.value === category) {
isLoading.value = false;
isPageLoading.value = false;
return;
}
......@@ -4770,7 +4802,7 @@ export const locale = i18n.global.locale
// 重新加载数据
await loadInspirationData(); // 强制刷新,不使用缓存
isLoading.value = false;
isPageLoading.value = false;
};
// 搜索防抖定时器
......@@ -4796,7 +4828,7 @@ export const locale = i18n.global.locale
// 重新加载数据
await loadInspirationData(); // 强制刷新,不使用缓存
isLoading.value = false;
isPageLoading.value = false;
}, 500); // 500ms 防抖延迟
};
......@@ -5665,7 +5697,7 @@ export const locale = i18n.global.locale
try {
// 开始模板加载
templateLoading.value = true;
showAlert('模板加载中...', 'info');
templateLoadingMessage.value = t('prefillLoadingTemplate');
// 先设置任务类型
selectedTaskId.value = item.task_type;
......@@ -5680,6 +5712,12 @@ export const locale = i18n.global.locale
currentForm.model_cls = item.model_cls || '';
currentForm.stage = item.stage || 'single_stage';
// 立即关闭模板详情并切换到创建视图,后续资源异步加载
showTemplateDetailModal.value = false;
selectedTemplate.value = null;
isCreationAreaExpanded.value = true;
switchToCreateView();
// 创建加载Promise数组
const loadingPromises = [];
......@@ -5783,14 +5821,6 @@ export const locale = i18n.global.locale
await Promise.all(loadingPromises);
}
// 关闭模板详情弹窗(不跳转路由)
showTemplateDetailModal.value = false;
selectedTemplate.value = null;
// 切换到创建视图
isCreationAreaExpanded.value=true;
switchToCreateView();
showAlert(`模板加载完成`, 'success');
} catch (error) {
console.error('应用模板失败:', error);
......@@ -5798,6 +5828,7 @@ export const locale = i18n.global.locale
} finally {
// 结束模板加载
templateLoading.value = false;
templateLoadingMessage.value = '';
}
};
......@@ -6212,6 +6243,75 @@ export const locale = i18n.global.locale
featuredTemplatesLoading.value = false;
}
};
const removeTtsHistoryEntry = (entryId) => {
if (!entryId) return;
const currentHistory = loadTtsHistory().filter(entry => entry.id !== entryId);
saveTtsHistory(currentHistory);
};
const loadTtsHistory = () => {
try {
const stored = localStorage.getItem('ttsHistory');
if (!stored) return [];
const parsed = JSON.parse(stored);
ttsHistory.value = Array.isArray(parsed) ? parsed : [];
return ttsHistory.value;
} catch (error) {
console.error('加载TTS历史失败:', error);
ttsHistory.value = [];
return [];
}
};
const saveTtsHistory = (historyList) => {
try {
localStorage.setItem('ttsHistory', JSON.stringify(historyList));
ttsHistory.value = historyList;
} catch (error) {
console.error('保存TTS历史失败:', error);
}
};
const addTtsHistoryEntry = (text = '', instruction = '') => {
const trimmedText = (text || '').trim();
const trimmedInstruction = (instruction || '').trim();
if (!trimmedText && !trimmedInstruction) {
return;
}
const currentHistory = loadTtsHistory();
const existingIndex = currentHistory.findIndex(entry =>
entry.text === trimmedText && entry.instruction === trimmedInstruction
);
const timestamp = new Date().toISOString();
if (existingIndex !== -1) {
const existingEntry = currentHistory.splice(existingIndex, 1)[0];
existingEntry.timestamp = timestamp;
currentHistory.unshift(existingEntry);
} else {
currentHistory.unshift({
id: Date.now(),
text: trimmedText,
instruction: trimmedInstruction,
timestamp
});
}
if (currentHistory.length > 20) {
currentHistory.length = 20;
}
saveTtsHistory(currentHistory);
};
const clearTtsHistory = () => {
ttsHistory.value = [];
localStorage.removeItem('ttsHistory');
};
export {
// 任务类型下拉菜单
......@@ -6222,7 +6322,9 @@ export {
loginLoading,
initLoading,
downloadLoading,
downloadLoadingMessage,
isLoading,
isPageLoading,
// 录音相关
isRecording,
......@@ -6245,6 +6347,7 @@ export {
toggleSmsLogin,
submitting,
templateLoading,
templateLoadingMessage,
taskSearchQuery,
currentUser,
models,
......@@ -6531,4 +6634,10 @@ export {
initTheme,
toggleTheme,
getThemeIcon,
loadTtsHistory,
removeTtsHistoryEntry,
ttsHistory,
addTtsHistoryEntry,
saveTtsHistory,
clearTtsHistory,
};
......@@ -9,6 +9,7 @@ import PromptTemplate from '../components/PromptTemplate.vue'
import Voice_tts from '../components/Voice_tts.vue'
import MediaTemplate from '../components/MediaTemplate.vue'
import Loading from '../components/Loading.vue'
import SiteFooter from '../components/SiteFooter.vue'
import { useI18n } from 'vue-i18n'
import { isLoading, showVoiceTTSModal, handleAudioUpload, showAlert } from '../utils/other'
......@@ -59,6 +60,8 @@ const handleTTSComplete = (audioBlob) => {
<router-view></router-view>
</div>
</div>
<SiteFooter />
</div>
<!-- 全局组件 -->
......
......@@ -90,6 +90,7 @@ onMounted(async () => {
<span class="text-sm font-medium text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">LightX2V</span>
<i class="fas fa-external-link-alt text-xs text-[#86868b] dark:text-[#98989d] transition-all duration-200 group-hover:translate-x-0.5 group-hover:-translate-y-0.5"></i>
</a>
</div>
<Alert />
......@@ -99,6 +100,7 @@ onMounted(async () => {
<Loading />
</div>
</div>
</template>
<style scoped>
......
......@@ -276,35 +276,33 @@ onMounted(async () => {
<template>
<!-- Apple 极简风格分享页面 -->
<div class="bg-[#f5f5f7] dark:bg-[#000000] transition-colors duration-300 w-full h-full">
<!-- 主内容区域 -->
<div class="flex flex-col w-full h-full">
<!-- TopBar -->
<topMenu />
<!-- 滚动内容区域 - 带滚动条 -->
<div class="flex-1 overflow-y-auto main-scrollbar">
<!-- 错误状态 - Apple 风格 - 响应式 -->
<div v-if="error" class="flex items-center justify-center min-h-[60vh] px-4 sm:px-6">
<div class="min-h-screen w-full bg-[#f5f5f7] dark:bg-[#000000]">
<!-- TopBar -->
<topMenu />
<!-- 主要内容区域 -->
<div class="w-full min-h-[calc(100vh-80px)] overflow-y-auto main-scrollbar">
<!-- 错误状态 - Apple 风格 -->
<div v-if="error" class="flex items-center justify-center min-h-[60vh] px-6">
<div class="text-center max-w-md">
<div class="inline-flex items-center justify-center w-16 h-16 sm:w-20 sm:h-20 bg-red-500/10 dark:bg-red-400/10 rounded-2xl sm:rounded-3xl mb-4 sm:mb-6">
<i class="fas fa-exclamation-triangle text-2xl sm:text-3xl text-red-500 dark:text-red-400"></i>
<div class="inline-flex items-center justify-center w-20 h-20 bg-red-500/10 dark:bg-red-400/10 rounded-3xl mb-6">
<i class="fas fa-exclamation-triangle text-3xl text-red-500 dark:text-red-400"></i>
</div>
<h2 class="text-xl sm:text-2xl font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] mb-3 sm:mb-4 tracking-tight">{{ t('shareNotFound') }}</h2>
<p class="text-sm sm:text-base text-[#86868b] dark:text-[#98989d] mb-6 sm:mb-8 tracking-tight">{{ error }}</p>
<h2 class="text-2xl font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] mb-4 tracking-tight">{{ t('shareNotFound') }}</h2>
<p class="text-base text-[#86868b] dark:text-[#98989d] mb-8 tracking-tight">{{ error }}</p>
<button @click="router.push('/')"
class="inline-flex items-center justify-center gap-2 px-6 sm:px-8 py-2.5 sm:py-3 bg-[color:var(--brand-primary)] dark:bg-[color:var(--brand-primary-light)] text-white rounded-full text-sm sm:text-[15px] font-semibold tracking-tight transition-all duration-200 hover:scale-[1.02] hover:shadow-[0_8px_24px_rgba(var(--brand-primary-rgb),0.35)] dark:hover:shadow-[0_8px_24px_rgba(var(--brand-primary-light-rgb),0.4)] active:scale-100">
<i class="fas fa-home text-xs sm:text-sm"></i>
class="inline-flex items-center justify-center gap-2 px-8 py-3 bg-[color:var(--brand-primary)] dark:bg-[color:var(--brand-primary-light)] text-white rounded-full text-[15px] font-semibold tracking-tight transition-all duration-200 hover:scale-[1.02] hover:shadow-[0_8px_24px_rgba(var(--brand-primary-rgb),0.35)] dark:hover:shadow-[0_8px_24px_rgba(var(--brand-primary-light-rgb),0.4)] active:scale-100">
<i class="fas fa-home text-sm"></i>
<span>{{ t('backToHome') }}</span>
</button>
</div>
</div>
<!-- 分享内容 - Apple 风格 - 响应式布局 -->
<div v-else-if="shareData" class="flex flex-col lg:grid lg:grid-cols-2 gap-8 lg:gap-16 w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-12 py-8 sm:py-12 lg:py-16 items-center">
<!-- 左侧视频区域 - 响应式尺寸 -->
<div class="flex justify-center items-center w-full order-1">
<div class="w-full max-w-[300px] sm:max-w-[350px] lg:max-w-[400px] aspect-[9/16] bg-black dark:bg-[#000000] rounded-2xl overflow-hidden shadow-[0_8px_24px_rgba(0,0,0,0.15)] dark:shadow-[0_8px_24px_rgba(0,0,0,0.5)] relative">
<!-- 分享内容 - Apple 风格 -->
<div v-else-if="shareData" class="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-16 w-full max-w-7xl mx-auto px-6 sm:px-8 lg:px-12 py-12 lg:py-16 items-center">
<!-- 左侧视频区域 -->
<div class="flex justify-center items-center">
<div class="w-full max-w-[400px] aspect-[9/16] bg-black dark:bg-[#000000] rounded-2xl overflow-hidden shadow-[0_8px_24px_rgba(0,0,0,0.15)] dark:shadow-[0_8px_24px_rgba(0,0,0,0.5)] relative">
<!-- 视频加载占位符 - Apple 风格 -->
<div v-if="!videoUrl" class="w-full h-full flex flex-col items-center justify-center bg-[#f5f5f7] dark:bg-[#1c1c1e]">
<div class="relative w-12 h-12 mb-6">
......@@ -339,94 +337,94 @@ onMounted(async () => {
</div>
</div>
<!-- 右侧信息区域 - Apple 风格 - 响应式 -->
<div class="flex items-center justify-center w-full order-2">
<div class="w-full max-w-[300px] sm:max-w-[500px]">
<!-- 标题 - Apple 风格 - 响应式字体 -->
<h1 class="text-2xl sm:text-3xl lg:text-4xl xl:text-5xl font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] mb-3 sm:mb-4 tracking-tight leading-tight text-center lg:text-left">
<!-- 右侧信息区域 - Apple 风格 -->
<div class="flex items-center justify-center">
<div class="w-full max-w-[500px]">
<!-- 标题 - Apple 风格 -->
<h1 class="text-4xl sm:text-5xl font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] mb-4 tracking-tight leading-tight">
{{ getShareTitle() }}
</h1>
<!-- 描述 - Apple 风格 - 响应式字体 -->
<p class="text-base sm:text-lg text-[#86868b] dark:text-[#98989d] mb-6 sm:mb-8 leading-relaxed tracking-tight text-center lg:text-left">
<!-- 描述 - Apple 风格 -->
<p class="text-lg text-[#86868b] dark:text-[#98989d] mb-8 leading-relaxed tracking-tight">
{{ getShareDescription() }}
</p>
<!-- 特性列表 - Apple 风格 - 响应式 -->
<div class="grid grid-cols-1 gap-2 sm:gap-3 mb-6 sm:mb-8">
<div class="flex items-center gap-2.5 sm:gap-3 p-2.5 sm:p-3 bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] border border-black/8 dark:border-white/8 rounded-xl transition-all duration-200 hover:bg-white dark:hover:bg-[#3a3a3c] hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_4px_12px_rgba(0,0,0,0.08)] dark:hover:shadow-[0_4px_12px_rgba(0,0,0,0.2)]">
<div class="w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center bg-[color:var(--brand-primary)]/10 dark:bg-[color:var(--brand-primary-light)]/15 rounded-lg flex-shrink-0">
<i class="fas fa-rocket text-sm sm:text-base text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
<!-- 特性列表 - Apple 风格 -->
<div class="grid grid-cols-1 gap-3 mb-8">
<div class="flex items-center gap-3 p-3 bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] border border-black/8 dark:border-white/8 rounded-xl transition-all duration-200 hover:bg-white dark:hover:bg-[#3a3a3c] hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_4px_12px_rgba(0,0,0,0.08)] dark:hover:shadow-[0_4px_12px_rgba(0,0,0,0.2)]">
<div class="w-10 h-10 flex items-center justify-center bg-[color:var(--brand-primary)]/10 dark:bg-[color:var(--brand-primary-light)]/15 rounded-lg flex-shrink-0">
<i class="fas fa-rocket text-base text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
</div>
<span class="text-xs sm:text-sm font-medium text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('latestAIModel') }}</span>
<span class="text-sm font-medium text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('latestAIModel') }}</span>
</div>
<div class="flex items-center gap-2.5 sm:gap-3 p-2.5 sm:p-3 bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] border border-black/8 dark:border-white/8 rounded-xl transition-all duration-200 hover:bg-white dark:hover:bg-[#3a3a3c] hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_4px_12px_rgba(0,0,0,0.08)] dark:hover:shadow-[0_4px_12px_rgba(0,0,0,0.2)]">
<div class="w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center bg-[color:var(--brand-primary)]/10 dark:bg-[color:var(--brand-primary-light)]/15 rounded-lg flex-shrink-0">
<i class="fas fa-bolt text-sm sm:text-base text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
<div class="flex items-center gap-3 p-3 bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] border border-black/8 dark:border-white/8 rounded-xl transition-all duration-200 hover:bg-white dark:hover:bg-[#3a3a3c] hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_4px_12px_rgba(0,0,0,0.08)] dark:hover:shadow-[0_4px_12px_rgba(0,0,0,0.2)]">
<div class="w-10 h-10 flex items-center justify-center bg-[color:var(--brand-primary)]/10 dark:bg-[color:var(--brand-primary-light)]/15 rounded-lg flex-shrink-0">
<i class="fas fa-bolt text-base text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
</div>
<span class="text-xs sm:text-sm font-medium text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('oneClickReplication') }}</span>
<span class="text-sm font-medium text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('oneClickReplication') }}</span>
</div>
<div class="flex items-center gap-2.5 sm:gap-3 p-2.5 sm:p-3 bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] border border-black/8 dark:border-white/8 rounded-xl transition-all duration-200 hover:bg-white dark:hover:bg-[#3a3a3c] hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_4px_12px_rgba(0,0,0,0.08)] dark:hover:shadow-[0_4px_12px_rgba(0,0,0,0.2)]">
<div class="w-9 h-9 sm:w-10 sm:h-10 flex items-center justify-center bg-[color:var(--brand-primary)]/10 dark:bg-[color:var(--brand-primary-light)]/15 rounded-lg flex-shrink-0">
<i class="fas fa-user-cog text-sm sm:text-base text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
<div class="flex items-center gap-3 p-3 bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] border border-black/8 dark:border-white/8 rounded-xl transition-all duration-200 hover:bg-white dark:hover:bg-[#3a3a3c] hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_4px_12px_rgba(0,0,0,0.08)] dark:hover:shadow-[0_4px_12px_rgba(0,0,0,0.2)]">
<div class="w-10 h-10 flex items-center justify-center bg-[color:var(--brand-primary)]/10 dark:bg-[color:var(--brand-primary-light)]/15 rounded-lg flex-shrink-0">
<i class="fas fa-user-cog text-base text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
</div>
<span class="text-xs sm:text-sm font-medium text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('customizableCharacter') }}</span>
<span class="text-sm font-medium text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('customizableCharacter') }}</span>
</div>
</div>
<!-- 操作按钮 - Apple 风格 - 响应式 -->
<div class="space-y-2.5 sm:space-y-3 mb-6 sm:mb-8">
<!-- 操作按钮 - Apple 风格 -->
<div class="space-y-3 mb-8">
<button @click="createSimilar"
class="w-full rounded-full bg-[color:var(--brand-primary)] dark:bg-[color:var(--brand-primary-light)] border-0 px-6 sm:px-8 py-3 sm:py-3.5 text-sm sm:text-[15px] font-semibold text-white hover:scale-[1.02] hover:shadow-[0_8px_24px_rgba(var(--brand-primary-rgb),0.35)] dark:hover:shadow-[0_8px_24px_rgba(var(--brand-primary-light-rgb),0.4)] active:scale-100 transition-all duration-200 ease-out tracking-tight flex items-center justify-center gap-2">
class="w-full rounded-full bg-[color:var(--brand-primary)] dark:bg-[color:var(--brand-primary-light)] border-0 px-8 py-3.5 text-[15px] font-semibold text-white hover:scale-[1.02] hover:shadow-[0_8px_24px_rgba(var(--brand-primary-rgb),0.35)] dark:hover:shadow-[0_8px_24px_rgba(var(--brand-primary-light-rgb),0.4)] active:scale-100 transition-all duration-200 ease-out tracking-tight flex items-center justify-center gap-2">
<i class="fas fa-magic text-sm"></i>
<span>{{ getShareButtonText() }}</span>
</button>
<!-- 详细信息按钮 -->
<button @click="showDetails = !showDetails"
class="w-full rounded-full bg-white dark:bg-[#3a3a3c] border border-black/8 dark:border-white/8 px-6 sm:px-8 py-2.5 sm:py-3 text-sm sm:text-[15px] font-medium text-[#1d1d1f] dark:text-[#f5f5f7] hover:bg-white/80 dark:hover:bg-[#3a3a3c]/80 hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_4px_12px_rgba(0,0,0,0.1)] dark:hover:shadow-[0_4px_12px_rgba(0,0,0,0.3)] active:scale-[0.98] transition-all duration-200 tracking-tight flex items-center justify-center gap-2">
class="w-full rounded-full bg-white dark:bg-[#3a3a3c] border border-black/8 dark:border-white/8 px-8 py-3 text-[15px] font-medium text-[#1d1d1f] dark:text-[#f5f5f7] hover:bg-white/80 dark:hover:bg-[#3a3a3c]/80 hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_4px_12px_rgba(0,0,0,0.1)] dark:hover:shadow-[0_4px_12px_rgba(0,0,0,0.3)] active:scale-[0.98] transition-all duration-200 tracking-tight flex items-center justify-center gap-2">
<i :class="showDetails ? 'fas fa-chevron-up' : 'fas fa-info-circle'" class="text-sm"></i>
<span>{{ showDetails ? t('hideDetails') : t('showDetails') }}</span>
</button>
</div>
<!-- 技术信息 - Apple 风格 - 响应式 -->
<div class="text-center lg:text-left pt-4 sm:pt-6 border-t border-black/8 dark:border-white/8">
<div class="text-center pt-4 sm:pt-6 border-t border-black/8 dark:border-white/8">
<a href="https://github.com/ModelTC/LightX2V"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center gap-2 text-xs sm:text-sm text-[#86868b] dark:text-[#98989d] hover:text-[color:var(--brand-primary)] dark:hover:text-[color:var(--brand-primary-light)] transition-colors tracking-tight">
<i class="fab fa-github text-sm sm:text-base"></i>
class="inline-flex items-center gap-2 text-sm text-[#86868b] dark:text-[#98989d] hover:text-[color:var(--brand-primary)] dark:hover:text-[color:var(--brand-primary-light)] transition-colors tracking-tight">
<i class="fab fa-github text-base"></i>
<span>{{ t('poweredByLightX2V') }}</span>
<i class="fas fa-external-link-alt text-[10px] sm:text-xs"></i>
<i class="fas fa-external-link-alt text-xs"></i>
</a>
</div>
</div>
</div>
</div>
<!-- 详细信息面板 - Apple 风格 - 响应式 -->
<div v-if="showDetails && shareData" class="w-full bg-white dark:bg-[#1c1c1e] border-t border-black/8 dark:border-white/8 py-8 sm:py-12 lg:py-16">
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-12">
<!-- 输入素材标题 - Apple 风格 - 响应式 -->
<h2 class="text-xl sm:text-2xl lg:text-3xl font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] flex flex-col sm:flex-row items-center justify-center gap-2 sm:gap-3 mb-6 sm:mb-8 lg:mb-10 tracking-tight">
<!-- 详细信息面板 - Apple 风格 -->
<div v-if="showDetails && shareData" class="w-full bg-white dark:bg-[#1c1c1e] border-t border-black/8 dark:border-white/8 py-16">
<div class="max-w-6xl mx-auto px-6 sm:px-8 lg:px-12">
<!-- 输入素材标题 - Apple 风格 -->
<h2 class="text-2xl sm:text-3xl font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] flex items-center justify-center gap-3 mb-10 tracking-tight">
<i class="fas fa-upload text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
<span>{{ t('inputMaterials') }}</span>
</h2>
<!-- 三个卡片 - Apple 风格 - 响应式竖向排列 -->
<div class="flex flex-col md:grid md:grid-cols-3 gap-4 sm:gap-6">
<!-- 三个并列的分块卡片 - Apple 风格 -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- 图片卡片 - Apple 风格 -->
<div class="bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] border border-black/8 dark:border-white/8 rounded-2xl overflow-hidden transition-all duration-200 hover:bg-white dark:hover:bg-[#3a3a3c] hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_8px_24px_rgba(0,0,0,0.1)] dark:hover:shadow-[0_8px_24px_rgba(0,0,0,0.3)]">
<!-- 卡片头部 - 响应式 -->
<div class="flex items-center justify-between px-4 sm:px-5 py-3 sm:py-4 bg-[color:var(--brand-primary)]/5 dark:bg-[color:var(--brand-primary-light)]/10 border-b border-black/8 dark:border-white/8">
<div class="flex items-center gap-2 sm:gap-3">
<i class="fas fa-image text-base sm:text-lg text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
<h3 class="text-sm sm:text-base font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('image') }}</h3>
<!-- 卡片头部 -->
<div class="flex items-center justify-between px-5 py-4 bg-[color:var(--brand-primary)]/5 dark:bg-[color:var(--brand-primary-light)]/10 border-b border-black/8 dark:border-white/8">
<div class="flex items-center gap-3">
<i class="fas fa-image text-lg text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
<h3 class="text-base font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('image') }}</h3>
</div>
</div>
<!-- 卡片内容 - 响应式 - 带滚动条 -->
<div class="p-4 sm:p-6 min-h-[150px] sm:min-h-[200px] max-h-[300px] overflow-y-auto main-scrollbar">
<!-- 卡片内容 -->
<div class="p-6 min-h-[200px]">
<div v-if="getImageMaterials().length > 0">
<div v-for="[inputName, url] in getImageMaterials()" :key="inputName" class="rounded-xl overflow-hidden border border-black/8 dark:border-white/8">
<img :src="url" :alt="inputName"
......@@ -442,17 +440,17 @@ onMounted(async () => {
</div>
</div>
<!-- 音频卡片 - Apple 风格 - 响应式 -->
<!-- 音频卡片 - Apple 风格 -->
<div class="bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] border border-black/8 dark:border-white/8 rounded-2xl overflow-hidden transition-all duration-200 hover:bg-white dark:hover:bg-[#3a3a3c] hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_8px_24px_rgba(0,0,0,0.1)] dark:hover:shadow-[0_8px_24px_rgba(0,0,0,0.3)]">
<!-- 卡片头部 - 响应式 -->
<div class="flex items-center justify-between px-4 sm:px-5 py-3 sm:py-4 bg-[color:var(--brand-primary)]/5 dark:bg-[color:var(--brand-primary-light)]/10 border-b border-black/8 dark:border-white/8">
<div class="flex items-center gap-2 sm:gap-3">
<i class="fas fa-music text-base sm:text-lg text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
<h3 class="text-sm sm:text-base font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('audio') }}</h3>
<!-- 卡片头部 -->
<div class="flex items-center justify-between px-5 py-4 bg-[color:var(--brand-primary)]/5 dark:bg-[color:var(--brand-primary-light)]/10 border-b border-black/8 dark:border-white/8">
<div class="flex items-center gap-3">
<i class="fas fa-music text-lg text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
<h3 class="text-base font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('audio') }}</h3>
</div>
</div>
<!-- 卡片内容 - 响应式 - 带滚动条 -->
<div class="p-4 sm:p-6 min-h-[150px] sm:min-h-[200px] max-h-[300px] overflow-y-auto main-scrollbar">
<!-- 卡片内容 -->
<div class="p-6 min-h-[200px]">
<div v-if="getAudioMaterials().length > 0" class="space-y-4">
<div v-for="[inputName, url] in getAudioMaterials()" :key="inputName">
<audio :src="url" controls class="w-full rounded-xl"></audio>
......@@ -465,23 +463,23 @@ onMounted(async () => {
</div>
</div>
<!-- 提示词卡片 - Apple 风格 - 响应式 -->
<!-- 提示词卡片 - Apple 风格 -->
<div class="bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] border border-black/8 dark:border-white/8 rounded-2xl overflow-hidden transition-all duration-200 hover:bg-white dark:hover:bg-[#3a3a3c] hover:border-black/12 dark:hover:border-white/12 hover:shadow-[0_8px_24px_rgba(0,0,0,0.1)] dark:hover:shadow-[0_8px_24px_rgba(0,0,0,0.3)]">
<!-- 卡片头部 - 响应式 -->
<div class="flex items-center justify-between px-4 sm:px-5 py-3 sm:py-4 bg-[color:var(--brand-primary)]/5 dark:bg-[color:var(--brand-primary-light)]/10 border-b border-black/8 dark:border-white/8">
<div class="flex items-center gap-2 sm:gap-3">
<i class="fas fa-file-alt text-base sm:text-lg text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
<h3 class="text-sm sm:text-base font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('prompt') }}</h3>
<!-- 卡片头部 -->
<div class="flex items-center justify-between px-5 py-4 bg-[color:var(--brand-primary)]/5 dark:bg-[color:var(--brand-primary-light)]/10 border-b border-black/8 dark:border-white/8">
<div class="flex items-center gap-3">
<i class="fas fa-file-alt text-lg text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
<h3 class="text-base font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">{{ t('prompt') }}</h3>
</div>
<button v-if="shareData.prompt"
@click="copyPrompt(shareData.prompt)"
class="w-7 h-7 sm:w-8 sm:h-8 flex items-center justify-center bg-[color:var(--brand-primary)]/10 dark:bg-[color:var(--brand-primary-light)]/15 border border-[color:var(--brand-primary)]/20 dark:border-[color:var(--brand-primary-light)]/20 text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)] rounded-lg transition-all duration-200 hover:scale-110 active:scale-100"
class="w-8 h-8 flex items-center justify-center bg-[color:var(--brand-primary)]/10 dark:bg-[color:var(--brand-primary-light)]/15 border border-[color:var(--brand-primary)]/20 dark:border-[color:var(--brand-primary-light)]/20 text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)] rounded-lg transition-all duration-200 hover:scale-110 active:scale-100"
:title="t('copy')">
<i class="fas fa-copy text-[10px] sm:text-xs"></i>
<i class="fas fa-copy text-xs"></i>
</button>
</div>
<!-- 卡片内容 - 响应式 -->
<div class="p-4 sm:p-6 min-h-[150px] sm:min-h-[200px]">
<!-- 卡片内容 -->
<div class="p-6 min-h-[200px]">
<div v-if="shareData.prompt" class="bg-white/50 dark:bg-[#1e1e1e]/50 backdrop-blur-[10px] border border-black/6 dark:border-white/6 rounded-xl p-4">
<p class="text-sm text-[#1d1d1f] dark:text-[#f5f5f7] leading-relaxed tracking-tight break-words">{{ shareData.prompt }}</p>
</div>
......@@ -494,13 +492,12 @@ onMounted(async () => {
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 全局路由跳转Loading覆盖层 - Apple 风格 -->
<div v-show="isLoading" class="fixed inset-0 bg-[#f5f5f7] dark:bg-[#000000] flex items-center justify-center z-[9999]">
<Loading />
</div>
<!-- 全局路由跳转Loading覆盖层 - Apple 风格 -->
<div v-show="isLoading" class="fixed inset-0 bg-[#f5f5f7] dark:bg-[#000000] flex items-center justify-center z-[9999]">
<Loading />
</div>
</template>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment