fix: 收藏功能

This commit is contained in:
old burden 2026-04-09 17:01:41 +08:00
parent 8afd28ca10
commit 6a7abddfa4
31 changed files with 1047 additions and 1450 deletions

1
.gitignore vendored
View File

@ -26,3 +26,4 @@ package-lock.json
/unpackage/dist/build/app-plus /unpackage/dist/build/app-plus
*.lock *.lock
/.logs/* /.logs/*
/portal-ui/vite.config.js.timestamp-1775717209686-71db82cf15ae7.mjs

View File

@ -1,179 +0,0 @@
export default {
image1: 'خلع بضغطة واحدة',
image2: 'صورة إلى صورة 2',
uploadImage: 'رفع صورة',
uploadImageTip: 'PNG/JPG، بحد أقصى 10 ميجا',
uploadPlaceholder: 'انقر لرفع صورة',
selectImageSource: 'اختر مصدر الصورة',
selectTemplate: 'اختر القالب',
reselectTemplate: 'إعادة اختيار القالب',
noTemplates: 'لا توجد قوالب',
tag1: 'نوع الوسم 1',
tag2: 'نوع الوسم 2',
tag3: 'نوع الوسم 3',
generateImage: 'إنشاء الآن (يخصم {score} من الرصيد)',
generateImageNow: 'إنشاء الآن',
generateTip: 'بعد الإرسال يمكنك المشاهدة في "أعمالي"',
generateVideo: 'إنشاء فيديو',
imageFace: 'تبديل الوجه في صورة',
videoFace: 'تبديل الوجه في فيديو',
uploadImageFace: 'انقر لرفع صورة الوجه',
uploadTemplate: 'انقر لرفع قالب مخصص',
textPlaceholder: 'صف الصورة التي تريد إنشاءها',
uploadImageError: 'يرجى رفع صورة',
replaceImage: 'استبدال الصورة',
uploadFaceImageError: 'يرجى رفع صورة الوجه',
uploadTemplateError: 'يرجى رفع قالب مخصص',
textError: 'يرجى إدخال النص التوجيهي',
textVideoPlaceholder: 'صف الفيديو الذي تريد إنشاءه',
uploadFirstPlaceholder: 'انقر لرفع الإطار الأول',
uploadLastPlaceholder: 'انقر لرفع الإطار الأخير',
uploadFirstImageError: 'يرجى رفع صورة الإطار الأول',
uploadWaitImageError: 'يرجى انتظار انتهاء الرفع',
saveVideo: 'تحميل الفيديو',
videoLoadingText: 'جاري إنشاء الفيديو...',
viewVideo: 'عرض الفيديو',
changeFacePrompt: 'أخذ الوجه من الصورة الثانية واستبداله بالوجه في الأولى',
rechartTip1: 'عند الشحن via المحفظة تأكد من شبكة المحفظة.',
rechartTip2: 'قد يتأخر الشحن؛ انتظر 35 دقائق ثم حدّث.',
walletAddr: 'عنوان المحفظة:',
fbTitle: 'تحذير! هذا الموقع للبالغين فقط!',
fbContent: 'بالدخول أؤكد أن عمري 18 سنة أو أكثر',
fbCancel: 'أقل من 18',
fbOK: 'عمري 18+',
sorry: 'عذراً!',
useLess: 'لا يمكنك استخدام هذا الموقع...',
loginAccount: 'تسجيل الدخول',
logout: 'تسجيل الخروج',
userEmailPlaceholder: 'أدخل البريد أو اسم المستخدم',
passwordPlaceholder: 'أدخل كلمة المرور',
forgetPassword: 'نسيت كلمة المرور؟',
register: 'التسجيل',
login: 'دخول',
forgotPassword: 'نسيت كلمة المرور',
registerAccount: 'إنشاء حساب',
usernamePlaceholder: 'أدخل اسم المستخدم',
codePlaceholder: 'أدخل رمز التحقق',
confirmPasswordPlaceholder: 'تأكيد كلمة المرور',
backToLogin: 'العودة لتسجيل الدخول',
send: 'إرسال',
emailValidPlaceholder: 'أدخل بريداً إلكترونياً صحيحاً',
confirmPwdValidMsg: 'كلمات المرور غير متطابقة',
editPassword: 'تغيير كلمة المرور',
recharge: 'شحن',
myAccount: 'حسابي',
moneyInvite: 'دعوة مكافآت',
rechargeRecord: 'سجل الشحن',
resumeRecord: 'سجل الاستهلاك',
inviteRecord: 'سجل الدعوات',
username: 'اسم المستخدم',
userId: 'معرف المستخدم',
accountAmount: 'الرصيد',
editUserInfo: 'تعديل البيانات',
contact: 'تواصل معنا',
backToUser: 'رجوع',
moneyTips: 'عند دعوة صديق للتسجيل تحصل على {rate} من مبلغ شحنه في كل مرة.',
inviteCode: 'رمز الدعوة',
inviteLink: 'رابط الدعوة',
saveImage: 'حفظ الصورة',
totalAmount: 'إجمالي الشحن',
amount: 'المبلغ',
sendAmount: 'مبلغ الهدية',
rechargeType: 'طريقة الشحن',
rechargeTime: 'وقت الشحن',
emptyText: 'لا توجد بيانات',
product: 'عمل',
resumeAmount: 'المبلغ المستهلك',
productType: 'نوع العمل',
productTime: 'وقت الإنشاء',
totalReward: 'رصيد المكافآت',
rewardAmount: 'مبلغ المكافأة',
rewardTime: 'وقت المكافأة',
reSend: 'إعادة الإرسال',
registerSuccessfully: 'تم التسجيل بنجاح',
loginSuccessfully: 'تم تسجيل الدخول',
passwordResetSuccessfully: 'تم إعادة تعيين كلمة المرور',
rechargeSuccessfully: 'تم الشحن بنجاح',
avatar: 'الصورة الرمزية',
input: 'أدخل',
save: 'حفظ',
editEmail: 'تغيير البريد',
editEmailSuccessfully: 'تم تحديث البريد',
updateAvatarSuccessfully: 'تم تحديث الصورة الرمزية',
balenceLow: 'رصيدك غير كافٍ، يرجى الشحن',
confirm: 'تأكيد',
createFailed: 'فشل الإنشاء، تمت إعادة الرصيد',
notice: 'تنبيه',
oldPasswordPlaceholder: 'أدخل كلمة المرور القديمة',
newpasswordPlaceholder: 'أدخل كلمة المرور الجديدة',
switchPageTip: 'الفيديو قيد الإنشاء؛ اخرج وتحقق من سجل الاستهلاك.',
loginless: 'انتهت الجلسة! يرجى تسجيل الدخول مجدداً.',
createVideo: 'إنشاء فيديو (يخصم {price} من الرصيد)',
ok: 'تأكيد',
rechartNotice: 'تعليمات الشحن',
rechargeNotice1: '1. يُضاف الشحن فوراً. إن لم يتغير الرصيد حدّث الصفحة.',
rechargeNotice2: '2. الباقات المحدودة عروض؛ يمكنك الشحن أكثر من مرة.',
rechargeNotice3: '3. الرصيد المشحون لا ينتهي صلاحيته.',
rechargeNotice4: 'راجع الباقة قبل الشراء؛ لا استرداد.',
rechargeLeft: 'الرصيد',
dollor: 'USD',
isDevelop: 'قيد التطوير.',
copySuccessfully: 'تم النسخ',
copyLink: 'نسخ الرابط',
goPay: 'الذهاب للدفع',
filePreview: 'معاينة',
doSame: 'عمل نسخة مشابهة',
saveQRCode: 'حفظ رمز QR',
invitationCodePlaceholder: 'أدخل رمز الدعوة',
testRecharge: 'شحن تجريبي',
orderNo: 'رقم الطلب',
orderNoP: 'رقم الطلب مطلوب',
emailNotExists: 'هذا البريد غير مسجّل',
gearNotExists: 'خطة الشحن المختارة غير موجودة؛ اختر غيرها.',
myProduct: 'أعمالي',
permissionError: 'إجمالي شحنك {total}، لا يكفي للمعاينة.',
createTagFailed: 'فشل الإنشاء؛ حدّث وجرب مرة أخرى.',
loadingText: 'جاري التحميل...',
hasMore: 'اسحب لأعلى للمزيد',
noMore: 'لا مزيد',
giftAmount: 'المبلغ المُستَوفى',
cardNo: 'رقم البطاقة',
cardName: 'اسم صاحب البطاقة',
cardNoRequired: 'أدخل رقم البطاقة',
cardNameRequired: 'أدخل اسم صاحب البطاقة',
rechargeFailed: 'فشل الشحن',
vmCardInfo: 'بيانات البطاقة',
cardNumber: 'رقم البطاقة',
cardNumberPlaceholder: 'أدخل رقم البطاقة',
cardNumberRequired: 'أدخل رقم البطاقة',
cardNumberInvalid: 'رقم البطاقة يجب أن يكون 1319 رقماً',
cvc: 'CVC',
cvcPlaceholder: 'أدخل CVC',
cvcRequired: 'أدخل CVC',
cvcInvalid: 'CVC يجب أن يكون 3 أرقام',
expYear: 'سنة انتهاء الصلاحية',
expYearPlaceholder: 'أدخل السنة (مثل 2027)',
expYearRequired: 'أدخل سنة انتهاء الصلاحية',
expYearInvalid: 'السنة يجب أن تكون 4 أرقام',
expMonth: 'شهر انتهاء الصلاحية',
expMonthPlaceholder: 'اختر الشهر',
expMonthRequired: 'اختر الشهر',
email: 'البريد',
emailPlaceholder: 'أدخل البريد',
emailRequired: 'أدخل البريد',
emailInvalid: 'صيغة البريد غير صحيحة',
emailMaxLength: 'البريد لا يتجاوز 30 حرفاً',
firstName: 'الاسم الأول',
firstNamePlaceholder: 'أدخل الاسم الأول',
firstNameRequired: 'أدخل الاسم الأول',
firstNameMaxLength: 'الاسم لا يتجاوز 30 حرفاً',
lastName: 'اسم العائلة',
lastNamePlaceholder: 'أدخل اسم العائلة',
lastNameRequired: 'أدخل اسم العائلة',
lastNameMaxLength: 'اسم العائلة لا يتجاوز 30 حرفاً',
country: 'الدولة',
countryPlaceholder: 'اختر الدولة',
countryRequired: 'اختر الدولة',
submit: 'إرسال',
cancel: 'إلغاء'
}

View File

@ -1,7 +0,0 @@
import route from './route'
import common from './common.js'
export default {
route,
common
}

View File

@ -1,15 +0,0 @@
export default {
index: 'الرئيسية',
imageToImage: 'خلع بضغطة واحدة',
imageToImage2: 'صورة إلى صورة 2',
changeFace: 'تبديل الوجه',
changeFaceVideo: 'تبديل الوجه في الفيديو',
fastImage: 'إنشاء صورة',
fastVideo: 'إنشاء فيديو',
recharge: 'شحن سريع',
help: 'مركز المساعدة',
moneyInvite: 'دعوة مكافآت',
assetGroupManage: 'إدارة مجموعات الأصول',
assetManage: 'إدارة الأصول',
thirdPartyAsset: 'أصول الطرف الثالث'
}

View File

@ -1,191 +0,0 @@
export default {
image1: "One-click undressing",
image2: 'Image-to-Image 2',
uploadImage: 'Upload Image',
uploadImageTip: 'Supports PNG/JPG up to 10MB',
uploadPlaceholder: 'Click to upload image',
selectImageSource: 'Select Image Source',
selectTemplate: 'Select Template',
reselectTemplate: 'Reselect Template',
noTemplates: 'No Templates Available',
tag1: 'Tag Type 1',
tag2: 'Tag Type 2',
tag3: 'Tag Type 3',
generateImage: 'Generate Now (costs {score} balance)',
generateImageNow: 'Generate Now',
generateTip: 'Tip: After submission, you can view it in "My Works"',
generateVideo: 'Generate Video',
imageFace: 'Image Face Swap',
videoFace: 'Video Face Swap',
uploadImageFace: 'Click to upload face image',
uploadFaceImageError: 'Please upload a face image',
uploadTemplateError: 'Please upload a custom template',
uploadTemplate: 'Click to upload custom template',
textPlaceholder: 'Describe the image you want to generate',
uploadImageError: 'Please upload an image',
replaceImage: 'Replace image',
textError: 'Please enter a prompt',
textVideoPlaceholder: "Describe the video you want to generate",
uploadFirstPlaceholder: 'Click to upload first frame',
uploadLastPlaceholder: 'Click to upload last frame',
uploadFirstImageError: 'Please upload the first frame image',
uploadWaitImageError: 'Please wait for the image upload to finish',
saveVideo: 'Down Video',
videoLoadingText: "Video is creating...",
viewVideo: 'View Video',
changeFacePrompt: 'Extract the face from the second image and replace the face in the first image',
rechartTip1: 'When recharging via wallet transfer, please make sure the wallet is on the correct blockchain network!!!',
rechartTip2: 'Recharge may be delayed. Please wait 35 minutes before refreshing to check.',
walletAddr: 'Wallet Address',
fbTitle: 'Warning! This website is for adults only!',
fbContent: 'By entering, you confirm you are 18+.',
fbCancel: 'Under 18',
fbOK: 'Im 18+',
sorry: 'Sorry!',
useLess: 'You cannot use this website...',
loginAccount: 'Login Account',
logout: 'Logout',
userEmailPlaceholder: 'Enter email or username',
passwordPlaceholder: 'Enter password',
forgetPassword: 'Forgot password?',
register: "Register",
login: "Login",
forgotPassword: 'Forgot password',
registerAccount: 'Register Account',
usernamePlaceholder: 'Enter username',
codePlaceholder: 'Enter verification code',
confirmPasswordPlaceholder: 'Confirm password',
backToLogin: 'Back to Login',
send: 'Send',
emailValidPlaceholder: 'Enter a valid email address',
confirmPwdValidMsg: 'Passwords do not match',
editPassword: 'Edit Password',
recharge: 'Recharge',
myAccount: 'My Account',
moneyInvite: 'Reward Invite',
rechargeRecord: 'Recharge Record',
resumeRecord: 'Consume Record',
inviteRecord: 'Invite Record',
username: 'Username',
userId: 'User ID',
accountAmount: 'Account Balance',
contact: 'Contact Us',
backToUser: 'Back',
moneyTips: 'When you invite a friend to register, you will earn {rate} of their recharge amount each time they recharge.',
inviteCode: 'Invite Code',
inviteLink: 'Invite Link',
saveImage: 'Save Image',
totalAmount: 'Total Recharge Amount',
amount: 'Amount',
sendAmount: 'Gift Amount',
rechargeType: 'Recharge Method',
rechargeTime: 'Recharge Time',
emptyText: 'No Data Available',
product: 'Product',
resumeAmount: 'Amount',
productType: 'Product Type',
productTime: 'Creation Time',
totalReward: 'Total Reward Balance',
rewardAmount: 'Reward Amount',
rewardTime: 'Reward Time',
reSend: 'Resend',
registerSuccessfully: 'Registration Successful',
loginSuccessfully: 'Login Successful',
passwordResetSuccessfully: 'Password Reset Successful',
rechargeSuccessfully: 'Recharge Successful',
avatar: 'Avatar',
email: 'Email',
input: 'Please enter',
save: 'Save',
editEmail: 'Edit Email',
editEmailSuccessfully: 'Email successfully updated',
updateAvatarSuccessfully: 'Avatar updated successfully',
balenceLow: 'Your balance is low, please recharge',
confirm: 'Confirm',
cancel: 'Cancel',
createFailed: 'Creation failed, balance has been refunded',
notice: 'Notice',
oldPasswordPlaceholder: 'Enter your old password',
newpasswordPlaceholder: 'Enter your new password',
switchPageTip: 'A video is currently being generated. After exiting, please check your balance consumption record.',
loginless: 'Login information has expired! Please log in again.',
createVideo: 'Generate Video (Consumes {price} balance)',
ok: 'Confirm',
rechartNotice: 'Recharge Instructions',
rechargeNotice1: '1. Recharge will be credited immediately. If the hash rate doesnt change, you can refresh and re-enter.',
rechargeNotice2: '2. Limited-time packages are irregular promotions. You can stock up on hash rate during the event and recharge multiple times.',
rechargeNotice3: '3. The purchased hash rate has no time limit.',
rechargeNotice4: 'Before purchasing a package, please carefully review the details. No returns are supported.',
rechargeLeft: 'Balance',
dollor: 'USD',
isDevelop: 'In development, stay tuned!',
copySuccessfully: 'Copy successful',
copyLink: 'Copy link',
goPay: 'Go to Pay',
filePreview: 'Preview',
doSame: 'Create Similar',
saveQRCode: 'Save QR Code',
invitationCodePlaceholder: 'Please enter the invitation code',
testRecharge: 'Test Recharge',
orderNo: "Order Number",
orderNoP: "Order number cannot be empty",
emailNotExists: 'Email address is not registered',
gearNotExists: 'The selected recharge plan does not exist. Please choose another plan.',
myProduct: 'My Works',
permissionError: "Your total accumulated recharge is {total}, which does not meet the preview requirement.",
createTagFailed: "Generation failed. Please refresh and try again.",
loadingText: 'Loading...',
hasMore: 'Pull up to load more',
noMore: 'No more data',
giftAmount: 'Credited Amount',
cardNo: 'Card Number',
cardName: 'Cardholder Name',
cardNoRequired: 'Please enter card number',
cardNameRequired: 'Please enter cardholder name',
rechargeFailed: 'Recharge failed',
// Model selection
selectModel: 'Select Model',
seedance20: 'Seedance 2.0',
seedance20Fast: 'Seedance 2.0 Fast',
// Rich text editor
insertImage: 'Insert Image',
mentionImage: 'Mention Image',
noImageToMention: 'No images to mention',
// VM支付相关
vmCardInfo: 'Credit Card Information',
cardNumber: 'Card Number',
cardNumberPlaceholder: 'Enter card number',
cardNumberRequired: 'Please enter card number',
cardNumberInvalid: 'Card number must be 13-19 digits',
cvc: 'CVC',
cvcPlaceholder: 'Enter CVC',
cvcRequired: 'Please enter CVC',
cvcInvalid: 'CVC must be 3 digits',
expYear: 'Expiration Year',
expYearPlaceholder: 'Enter expiration year (e.g., 2027)',
expYearRequired: 'Please enter expiration year',
expYearInvalid: 'Year must be 4 digits',
expMonth: 'Expiration Month',
expMonthPlaceholder: 'Select expiration month',
expMonthRequired: 'Please select expiration month',
emailPlaceholder: 'Enter email address',
emailRequired: 'Please enter email',
emailInvalid: 'Invalid email format',
emailMaxLength: 'Email cannot exceed 30 characters',
firstName: 'First Name',
firstNamePlaceholder: 'Enter first name',
firstNameRequired: 'Please enter first name',
firstNameMaxLength: 'First name cannot exceed 30 characters',
lastName: 'Last Name',
lastNamePlaceholder: 'Enter last name',
lastNameRequired: 'Please enter last name',
lastNameMaxLength: 'Last name cannot exceed 30 characters',
country: 'Country',
countryPlaceholder: 'Select country',
countryRequired: 'Please select country',
videoGen: 'Video Generation',
uploadFirstImage: 'Upload Reference Image',
insertImage: 'Insert Image',
submit: 'Submit',
cancel: 'Cancel'
}

View File

@ -1,7 +0,0 @@
import route from './route'
import common from './common.js'
export default {
route,
common
}

View File

@ -1,16 +0,0 @@
export default {
index: 'Home',
imageToImage: 'Image-to-Image 1',
imageToImage2: 'Image-to-Image2',
changeFace: 'Swap Face',
changeFaceVideo: 'Swap Video Face',
fastImage: 'Gen Image',
fastVideo: 'Gen Video',
videoGen: 'Video Generation',
recharge: 'Quick Recharge',
help: 'Help Center',
moneyInvite: 'Reward Invitation',
assetGroupManage: 'Asset Group Manage',
assetManage: 'Asset Manage',
thirdPartyAsset: 'Third-party assets'
}

View File

@ -1,179 +0,0 @@
export default {
image1: 'Desvestir en un clic',
image2: 'Imagen a Imagen 2',
uploadImage: 'Subir imagen',
uploadImageTip: 'Soporta PNG/JPG, máx. 10MB',
uploadPlaceholder: 'Clic para subir imagen',
selectImageSource: 'Seleccionar fuente de imagen',
selectTemplate: 'Seleccionar plantilla',
reselectTemplate: 'Reseleccionar plantilla',
noTemplates: 'Sin plantillas',
tag1: 'Tipo de etiqueta 1',
tag2: 'Tipo de etiqueta 2',
tag3: 'Tipo de etiqueta 3',
generateImage: 'Generar ahora (consume {score} de saldo)',
generateImageNow: 'Generar ahora',
generateTip: 'Después de enviar, puede ver en "Mis obras"',
generateVideo: 'Generar video',
imageFace: 'Cambiar rostro en imagen',
videoFace: 'Cambiar rostro en video',
uploadImageFace: 'Clic para subir imagen de rostro',
uploadTemplate: 'Clic para subir plantilla personalizada',
textPlaceholder: 'Describa la imagen que desea generar',
uploadImageError: 'Por favor suba una imagen',
replaceImage: 'Reemplazar imagen',
uploadFaceImageError: 'Por favor suba una imagen de rostro',
uploadTemplateError: 'Por favor suba una plantilla personalizada',
textError: 'Por favor escriba el texto de indicación',
textVideoPlaceholder: 'Describa el video que desea generar',
uploadFirstPlaceholder: 'Clic para subir imagen del primer frame',
uploadLastPlaceholder: 'Clic para subir imagen del último frame',
uploadFirstImageError: 'Por favor suba la imagen del primer frame',
uploadWaitImageError: 'Espere a que termine la subida',
saveVideo: 'Descargar video',
videoLoadingText: 'Generando video...',
viewVideo: 'Ver video',
changeFacePrompt: 'Extraer el rostro de la segunda imagen y reemplazar en la primera',
rechartTip1: 'Al recargar con transferencia de billetera, confirme la red del monedero.',
rechartTip2: 'La recarga puede tardar; espere 3-5 minutos antes de actualizar.',
walletAddr: 'Dirección del monedero',
fbTitle: 'Advertencia: este sitio es solo para adultos.',
fbContent: 'Al entrar confirmo que tengo 18 años o más',
fbCancel: 'Menor de 18',
fbOK: 'Tengo 18 o más',
sorry: '¡Lo sentimos!',
useLess: 'No puede usar este sitio...',
loginAccount: 'Iniciar sesión',
logout: 'Cerrar sesión',
userEmailPlaceholder: 'Introduzca correo o usuario',
passwordPlaceholder: 'Introduzca contraseña',
forgetPassword: '¿Olvidó la contraseña?',
register: 'Registrarse',
login: 'Iniciar sesión',
forgotPassword: 'Olvidé la contraseña',
registerAccount: 'Crear cuenta',
usernamePlaceholder: 'Introduzca usuario',
codePlaceholder: 'Introduzca código de verificación',
confirmPasswordPlaceholder: 'Confirme la contraseña',
backToLogin: 'Volver al inicio de sesión',
send: 'Enviar',
emailValidPlaceholder: 'Introduzca un correo válido',
confirmPwdValidMsg: 'Las contraseñas no coinciden',
editPassword: 'Cambiar contraseña',
recharge: 'Recargar',
myAccount: 'Mi cuenta',
moneyInvite: 'Invitación con recompensa',
rechargeRecord: 'Historial de recargas',
resumeRecord: 'Historial de consumo',
inviteRecord: 'Historial de invitaciones',
username: 'Usuario',
userId: 'ID de usuario',
accountAmount: 'Saldo',
editUserInfo: 'Editar datos',
contact: 'Contacto',
backToUser: 'Volver',
moneyTips: 'Al invitar a un amigo a registrarse, obtiene {rate} de su recarga cada vez.',
inviteCode: 'Código de invitación',
inviteLink: 'Enlace de invitación',
saveImage: 'Guardar imagen',
totalAmount: 'Total recargado',
amount: 'Monto',
sendAmount: 'Monto de regalo',
rechargeType: 'Método de recarga',
rechargeTime: 'Fecha de recarga',
emptyText: 'Sin datos',
product: 'Obra',
resumeAmount: 'Monto consumido',
productType: 'Tipo de obra',
productTime: 'Fecha de creación',
totalReward: 'Saldo de recompensas',
rewardAmount: 'Monto de recompensa',
rewardTime: 'Fecha de recompensa',
reSend: 'Reenviar',
registerSuccessfully: 'Registro exitoso',
loginSuccessfully: 'Sesión iniciada',
passwordResetSuccessfully: 'Contraseña restablecida',
rechargeSuccessfully: 'Recarga exitosa',
avatar: 'Avatar',
input: 'Introduzca',
save: 'Guardar',
editEmail: 'Cambiar correo',
editEmailSuccessfully: 'Correo actualizado',
updateAvatarSuccessfully: 'Avatar actualizado',
balenceLow: 'Saldo insuficiente, recargue',
confirm: 'Aceptar',
createFailed: 'Error al generar, saldo devuelto',
notice: 'Aviso',
oldPasswordPlaceholder: 'Introduzca la contraseña anterior',
newpasswordPlaceholder: 'Introduzca la nueva contraseña',
switchPageTip: 'El video se está generando; salga y revise el historial de consumo.',
loginless: 'Sesión expirada; inicie sesión de nuevo.',
createVideo: 'Generar video (consume {price} de saldo)',
ok: 'Aceptar',
rechartNotice: 'Instrucciones de recarga',
rechargeNotice1: '1. La recarga se acredita al instante. Si no cambia, actualice la página.',
rechargeNotice2: '2. Los paquetes por tiempo limitado son promociones; puede recargar varias veces.',
rechargeNotice3: '3. El saldo recargado no tiene límite de tiempo.',
rechargeNotice4: 'Revise el paquete antes de comprar; no hay devoluciones.',
rechargeLeft: 'Saldo',
dollor: 'USD',
isDevelop: 'En desarrollo.',
copySuccessfully: 'Copiado',
copyLink: 'Copiar enlace',
goPay: 'Ir a pagar',
filePreview: 'Vista previa',
doSame: 'Hacer similar',
saveQRCode: 'Guardar código QR',
invitationCodePlaceholder: 'Introduzca código de invitación',
testRecharge: 'Recarga de prueba',
orderNo: 'Número de pedido',
orderNoP: 'El número de pedido es obligatorio',
emailNotExists: 'El correo no está registrado',
gearNotExists: 'El plan de recarga no existe; elija otro.',
myProduct: 'Mis obras',
permissionError: 'Su recarga acumulada es {total}, no cumple para previsualizar.',
createTagFailed: 'Error al generar; actualice e intente de nuevo.',
loadingText: 'Cargando...',
hasMore: 'Deslizar para más',
noMore: 'No hay más',
giftAmount: 'Monto acreditado',
cardNo: 'Número de tarjeta',
cardName: 'Nombre en tarjeta',
cardNoRequired: 'Introduzca número de tarjeta',
cardNameRequired: 'Introduzca nombre en tarjeta',
rechargeFailed: 'Recarga fallida',
vmCardInfo: 'Datos de tarjeta',
cardNumber: 'Número de tarjeta',
cardNumberPlaceholder: 'Introduzca número de tarjeta',
cardNumberRequired: 'Introduzca número de tarjeta',
cardNumberInvalid: 'El número debe tener 13-19 dígitos',
cvc: 'CVC',
cvcPlaceholder: 'Introduzca CVC',
cvcRequired: 'Introduzca CVC',
cvcInvalid: 'CVC debe ser 3 dígitos',
expYear: 'Año de vencimiento',
expYearPlaceholder: 'Introduzca año (ej.: 2027)',
expYearRequired: 'Introduzca año de vencimiento',
expYearInvalid: 'El año debe ser 4 dígitos',
expMonth: 'Mes de vencimiento',
expMonthPlaceholder: 'Seleccione mes',
expMonthRequired: 'Seleccione mes',
email: 'Correo',
emailPlaceholder: 'Introduzca correo',
emailRequired: 'Introduzca correo',
emailInvalid: 'Formato de correo no válido',
emailMaxLength: 'Máx. 30 caracteres',
firstName: 'Nombre',
firstNamePlaceholder: 'Introduzca nombre',
firstNameRequired: 'Introduzca nombre',
firstNameMaxLength: 'Máx. 30 caracteres',
lastName: 'Apellido',
lastNamePlaceholder: 'Introduzca apellido',
lastNameRequired: 'Introduzca apellido',
lastNameMaxLength: 'Máx. 30 caracteres',
country: 'País',
countryPlaceholder: 'Seleccione país',
countryRequired: 'Seleccione país',
submit: 'Enviar',
cancel: 'Cancelar'
}

View File

@ -1,7 +0,0 @@
import route from './route'
import common from './common.js'
export default {
route,
common
}

View File

@ -1,15 +0,0 @@
export default {
index: 'Inicio',
imageToImage: 'Desvestir en un clic',
imageToImage2: 'Imagen a Imagen 2',
changeFace: 'Cambiar rostro',
changeFaceVideo: 'Cambiar rostro en video',
fastImage: 'Generar imagen',
fastVideo: 'Generar video',
recharge: 'Recarga rápida',
help: 'Centro de ayuda',
moneyInvite: 'Invitación con recompensa',
assetGroupManage: 'Gestión de grupos de activos',
assetManage: 'Gestión de activos',
thirdPartyAsset: 'Activos de terceros'
}

View File

@ -1,179 +0,0 @@
export default {
image1: 'Déshabiller en un clic',
image2: 'Image vers Image 2',
uploadImage: 'Télécharger une image',
uploadImageTip: 'PNG/JPG, max. 10 Mo',
uploadPlaceholder: 'Cliquez pour télécharger',
selectImageSource: 'Choisir la source de l\'image',
selectTemplate: 'Choisir un modèle',
reselectTemplate: 'Resélectionner un modèle',
noTemplates: 'Aucun modèle',
tag1: 'Type d\'étiquette 1',
tag2: 'Type d\'étiquette 2',
tag3: 'Type d\'étiquette 3',
generateImage: 'Générer maintenant (coûte {score} de solde)',
generateImageNow: 'Générer maintenant',
generateTip: 'Après envoi, voir dans « Mes œuvres »',
generateVideo: 'Générer une vidéo',
imageFace: 'Changer le visage sur image',
videoFace: 'Changer le visage en vidéo',
uploadImageFace: 'Cliquez pour télécharger une photo de visage',
uploadTemplate: 'Cliquez pour télécharger un modèle personnalisé',
textPlaceholder: 'Décrivez l\'image que vous voulez générer',
uploadImageError: 'Veuillez télécharger une image',
replaceImage: 'Remplacer l\'image',
uploadFaceImageError: 'Veuillez télécharger une photo de visage',
uploadTemplateError: 'Veuillez télécharger un modèle personnalisé',
textError: 'Veuillez saisir le texte d\'invite',
textVideoPlaceholder: 'Décrivez la vidéo que vous voulez générer',
uploadFirstPlaceholder: 'Cliquez pour télécharger la première image',
uploadLastPlaceholder: 'Cliquez pour télécharger la dernière image',
uploadFirstImageError: 'Veuillez télécharger l\'image du premier plan',
uploadWaitImageError: 'Veuillez attendre la fin du téléchargement',
saveVideo: 'Télécharger la vidéo',
videoLoadingText: 'Génération de la vidéo...',
viewVideo: 'Voir la vidéo',
changeFacePrompt: 'Extraire le visage de la 2e image et remplacer celui de la 1re',
rechartTip1: 'Lors d\'une recharge par portefeuille, vérifiez le réseau du portefeuille.',
rechartTip2: 'La recharge peut être retardée ; attendez 35 min avant dactualiser.',
walletAddr: 'Adresse du portefeuille : ',
fbTitle: 'Attention ! Ce site est réservé aux adultes.',
fbContent: 'En entrant je confirme avoir 18 ans ou plus',
fbCancel: 'Moins de 18 ans',
fbOK: 'J\'ai 18 ans ou plus',
sorry: 'Désolé !',
useLess: 'Vous ne pouvez pas utiliser ce site...',
loginAccount: 'Connexion',
logout: 'Déconnexion',
userEmailPlaceholder: 'Saisissez l\'email ou lidentifiant',
passwordPlaceholder: 'Saisissez le mot de passe',
forgetPassword: 'Mot de passe oublié ?',
register: 'S\'inscrire',
login: 'Connexion',
forgotPassword: 'Mot de passe oublié',
registerAccount: 'Créer un compte',
usernamePlaceholder: 'Saisissez lidentifiant',
codePlaceholder: 'Saisissez le code de vérification',
confirmPasswordPlaceholder: 'Confirmez le mot de passe',
backToLogin: 'Retour à la connexion',
send: 'Envoyer',
emailValidPlaceholder: 'Saisissez un email valide',
confirmPwdValidMsg: 'Les mots de passe ne correspondent pas',
editPassword: 'Modifier le mot de passe',
recharge: 'Recharger',
myAccount: 'Mon compte',
moneyInvite: 'Invitation avec récompense',
rechargeRecord: 'Historique des recharges',
resumeRecord: 'Historique des consommations',
inviteRecord: 'Historique des invitations',
username: 'Identifiant',
userId: 'ID utilisateur',
accountAmount: 'Solde',
editUserInfo: 'Modifier les informations',
contact: 'Nous contacter',
backToUser: 'Retour',
moneyTips: 'Quand vous parrainez un ami, vous gagnez {rate} de chaque recharge quil fait.',
inviteCode: 'Code de parrainage',
inviteLink: 'Lien d\'invitation',
saveImage: 'Enregistrer l\'image',
totalAmount: 'Total rechargé',
amount: 'Montant',
sendAmount: 'Montant offert',
rechargeType: 'Méthode de recharge',
rechargeTime: 'Date de recharge',
emptyText: 'Aucune donnée',
product: 'Œuvre',
resumeAmount: 'Montant consommé',
productType: 'Type d\'œuvre',
productTime: 'Date de création',
totalReward: 'Solde de récompenses',
rewardAmount: 'Montant de la récompense',
rewardTime: 'Date de la récompense',
reSend: 'Renvoyer',
registerSuccessfully: 'Inscription réussie',
loginSuccessfully: 'Connexion réussie',
passwordResetSuccessfully: 'Mot de passe réinitialisé',
rechargeSuccessfully: 'Recharge réussie',
avatar: 'Avatar',
input: 'Saisir',
save: 'Enregistrer',
editEmail: 'Modifier l\'email',
editEmailSuccessfully: 'Email mis à jour',
updateAvatarSuccessfully: 'Avatar mis à jour',
balenceLow: 'Solde insuffisant, veuillez recharger',
confirm: 'Confirmer',
createFailed: 'Échec de génération, solde remboursé',
notice: 'Avis',
oldPasswordPlaceholder: 'Saisissez lancien mot de passe',
newpasswordPlaceholder: 'Saisissez le nouveau mot de passe',
switchPageTip: 'Une vidéo est en cours de génération ; consultez lhistorique des consommations.',
loginless: 'Session expirée ! Veuillez vous reconnecter.',
createVideo: 'Générer une vidéo (coûte {price} de solde)',
ok: 'Confirmer',
rechartNotice: 'Instructions de recharge',
rechargeNotice1: '1. La recharge est créditée tout de suite. Si rien ne change, actualisez la page.',
rechargeNotice2: '2. Les offres limitées sont des promos ; vous pouvez recharger plusieurs fois.',
rechargeNotice3: '3. Le solde rechargé na pas de limite de durée.',
rechargeNotice4: 'Vérifiez loffre avant dacheter ; pas de remboursement.',
rechargeLeft: 'Solde',
dollor: 'USD',
isDevelop: 'En cours de développement.',
copySuccessfully: 'Copié',
copyLink: 'Copier le lien',
goPay: 'Aller au paiement',
filePreview: 'Aperçu',
doSame: 'Créer un équivalent',
saveQRCode: 'Enregistrer le QR code',
invitationCodePlaceholder: 'Saisissez le code d\'invitation',
testRecharge: 'Recharge test',
orderNo: 'Numéro de commande',
orderNoP: 'Le numéro de commande est obligatoire',
emailNotExists: 'Cet email nest pas inscrit',
gearNotExists: 'Loffre choisie nexiste pas ; choisissez une autre.',
myProduct: 'Mes œuvres',
permissionError: 'Votre total de recharges est {total}, insuffisant pour la prévisualisation.',
createTagFailed: 'Échec de génération ; actualisez et réessayez.',
loadingText: 'Chargement...',
hasMore: 'Tirez pour charger plus',
noMore: 'Plus rien',
giftAmount: 'Montant crédité',
cardNo: 'Numéro de carte',
cardName: 'Nom sur la carte',
cardNoRequired: 'Saisissez le numéro de carte',
cardNameRequired: 'Saisissez le nom sur la carte',
rechargeFailed: 'Recharge échouée',
vmCardInfo: 'Informations de carte',
cardNumber: 'Numéro de carte',
cardNumberPlaceholder: 'Saisissez le numéro de carte',
cardNumberRequired: 'Saisissez le numéro de carte',
cardNumberInvalid: 'Le numéro doit contenir 13 à 19 chiffres',
cvc: 'CVC',
cvcPlaceholder: 'Saisissez le CVC',
cvcRequired: 'Saisissez le CVC',
cvcInvalid: 'Le CVC doit faire 3 chiffres',
expYear: 'Année d\'expiration',
expYearPlaceholder: 'Saisissez lannée (ex. 2027)',
expYearRequired: 'Saisissez lannée d\'expiration',
expYearInvalid: 'Lannée doit faire 4 chiffres',
expMonth: 'Mois d\'expiration',
expMonthPlaceholder: 'Choisissez le mois',
expMonthRequired: 'Choisissez le mois',
email: 'Email',
emailPlaceholder: 'Saisissez lemail',
emailRequired: 'Saisissez lemail',
emailInvalid: 'Format demail invalide',
emailMaxLength: 'Lemail ne doit pas dépasser 30 caractères',
firstName: 'Prénom',
firstNamePlaceholder: 'Saisissez le prénom',
firstNameRequired: 'Saisissez le prénom',
firstNameMaxLength: 'Le prénom ne doit pas dépasser 30 caractères',
lastName: 'Nom de famille',
lastNamePlaceholder: 'Saisissez le nom de famille',
lastNameRequired: 'Saisissez le nom de famille',
lastNameMaxLength: 'Le nom ne doit pas dépasser 30 caractères',
country: 'Pays',
countryPlaceholder: 'Choisissez le pays',
countryRequired: 'Choisissez le pays',
submit: 'Envoyer',
cancel: 'Annuler'
}

View File

@ -1,7 +0,0 @@
import route from './route'
import common from './common.js'
export default {
route,
common
}

View File

@ -1,15 +0,0 @@
export default {
index: 'Accueil',
imageToImage: 'Déshabiller en un clic',
imageToImage2: 'Image vers Image 2',
changeFace: 'Changer le visage',
changeFaceVideo: 'Changer le visage en vidéo',
fastImage: 'Générer une image',
fastVideo: 'Générer une vidéo',
recharge: 'Recharge rapide',
help: 'Centre d\'aide',
moneyInvite: 'Invitation avec récompense',
assetGroupManage: 'Gestion des groupes d\'actifs',
assetManage: 'Gestion des actifs',
thirdPartyAsset: 'Actifs tiers'
}

View File

@ -1,179 +0,0 @@
export default {
image1: 'एक क्लिक में कपड़े उतारें',
image2: 'इमेज टू इमेज 2',
uploadImage: 'छवि अपलोड करें',
uploadImageTip: 'PNG/JPG, अधिकतम 10MB',
uploadPlaceholder: 'अपलोड करने के लिए क्लिक करें',
selectImageSource: 'छवि स्रोत चुनें',
selectTemplate: 'टेम्पलेट चुनें',
reselectTemplate: 'टेम्पलेट दोबारा चुनें',
noTemplates: 'कोई टेम्पलेट नहीं',
tag1: 'टैग प्रकार 1',
tag2: 'टैग प्रकार 2',
tag3: 'टैग प्रकार 3',
generateImage: 'अभी जनरेट करें ({score} बैलेंस खर्च)',
generateImageNow: 'अभी जनरेट करें',
generateTip: 'सबमिट के बाद "मेरी रचनाएं" में देखें',
generateVideo: 'वीडियो जनरेट करें',
imageFace: 'छवि में चेहरा बदलें',
videoFace: 'वीडियो में चेहरा बदलें',
uploadImageFace: 'चेहरे की छवि अपलोड करने के लिए क्लिक करें',
uploadTemplate: 'कस्टम टेम्पलेट अपलोड करने के लिए क्लिक करें',
textPlaceholder: 'वह छवि बताएं जो आप बनाना चाहते हैं',
uploadImageError: 'कृपया एक छवि अपलोड करें',
replaceImage: 'छवि बदलें',
uploadFaceImageError: 'कृपया चेहरे की छवि अपलोड करें',
uploadTemplateError: 'कृपया कस्टम टेम्पलेट अपलोड करें',
textError: 'कृपया प्रॉम्प्ट दर्ज करें',
textVideoPlaceholder: 'वह वीडियो बताएं जो आप बनाना चाहते हैं',
uploadFirstPlaceholder: 'पहला फ्रेम अपलोड करने के लिए क्लिक करें',
uploadLastPlaceholder: 'आखिरी फ्रेम अपलोड करने के लिए क्लिक करें',
uploadFirstImageError: 'कृपया पहले फ्रेम की छवि अपलोड करें',
uploadWaitImageError: 'अपलोड पूरा होने तक प्रतीक्षा करें',
saveVideo: 'वीडियो डाउनलोड करें',
videoLoadingText: 'वीडियो बन रहा है...',
viewVideo: 'वीडियो देखें',
changeFacePrompt: 'दूसरी छवि का चेहरा लेकर पहली छवि के चेहरे से बदलें',
rechartTip1: 'वॉलेट ट्रांसफर से रिचार्ज करते समय वॉलेट का नेटवर्क जांचें।',
rechartTip2: 'रिचार्ज में देरी हो सकती है; 3-5 मिनट बाद रिफ्रेश करें।',
walletAddr: 'वॉलेट पता:',
fbTitle: 'चेतावनी! यह साइट केवल वयस्कों के लिए है।',
fbContent: 'प्रवेश करके मैं पुष्टि करता/करती हूं कि मैं 18 वर्ष या उससे अधिक का हूं',
fbCancel: '18 से कम',
fbOK: 'मैं 18+ हूं',
sorry: 'क्षमा करें!',
useLess: 'आप इस साइट का उपयोग नहीं कर सकते...',
loginAccount: 'लॉगिन',
logout: 'लॉगआउट',
userEmailPlaceholder: 'ईमेल या यूजरनेम दर्ज करें',
passwordPlaceholder: 'पासवर्ड दर्ज करें',
forgetPassword: 'पासवर्ड भूल गए?',
register: 'रजिस्टर',
login: 'लॉगिन',
forgotPassword: 'पासवर्ड भूल गए',
registerAccount: 'अकाउंट बनाएं',
usernamePlaceholder: 'यूजरनेम दर्ज करें',
codePlaceholder: 'वेरिफिकेशन कोड दर्ज करें',
confirmPasswordPlaceholder: 'पासवर्ड की पुष्टि करें',
backToLogin: 'लॉगिन पर वापस',
send: 'भेजें',
emailValidPlaceholder: 'वैध ईमेल दर्ज करें',
confirmPwdValidMsg: 'पासवर्ड मेल नहीं खाते',
editPassword: 'पासवर्ड बदलें',
recharge: 'रिचार्ज',
myAccount: 'मेरा अकाउंट',
moneyInvite: 'इनाम निमंत्रण',
rechargeRecord: 'रिचार्ज रिकॉर्ड',
resumeRecord: 'खर्च रिकॉर्ड',
inviteRecord: 'निमंत्रण रिकॉर्ड',
username: 'यूजरनेम',
userId: 'यूजर आईडी',
accountAmount: 'बैलेंस',
editUserInfo: 'जानकारी संपादित करें',
contact: 'संपर्क करें',
backToUser: 'वापस',
moneyTips: 'जब आप दोस्त को रजिस्टर करवाते हैं, उसके रिचार्ज का {rate} हर बार मिलता है।',
inviteCode: 'इनवाइट कोड',
inviteLink: 'इनवाइट लिंक',
saveImage: 'छवि सहेजें',
totalAmount: 'कुल रिचार्ज',
amount: 'राशि',
sendAmount: 'उपहार राशि',
rechargeType: 'रिचार्ज तरीका',
rechargeTime: 'रिचार्ज समय',
emptyText: 'कोई डेटा नहीं',
product: 'रचना',
resumeAmount: 'खर्च राशि',
productType: 'रचना प्रकार',
productTime: 'बनाने का समय',
totalReward: 'कुल इनाम बैलेंस',
rewardAmount: 'इनाम राशि',
rewardTime: 'इनाम समय',
reSend: 'दोबारा भेजें',
registerSuccessfully: 'रजिस्ट्रेशन सफल',
loginSuccessfully: 'लॉगिन सफल',
passwordResetSuccessfully: 'पासवर्ड रीसेट सफल',
rechargeSuccessfully: 'रिचार्ज सफल',
avatar: 'अवतार',
input: 'दर्ज करें',
save: 'सहेजें',
editEmail: 'ईमेल बदलें',
editEmailSuccessfully: 'ईमेल अपडेट हो गया',
updateAvatarSuccessfully: 'अवतार अपडेट हो गया',
balenceLow: 'बैलेंस कम है, रिचार्ज करें',
confirm: 'पुष्टि करें',
createFailed: 'जनरेशन विफल, बैलेंस वापस',
notice: 'सूचना',
oldPasswordPlaceholder: 'पुराना पासवर्ड दर्ज करें',
newpasswordPlaceholder: 'नया पासवर्ड दर्ज करें',
switchPageTip: 'वीडियो बन रहा है; निकलकर खर्च रिकॉर्ड देखें।',
loginless: 'लॉगिन समाप्त! दोबारा लॉगिन करें।',
createVideo: 'वीडियो जनरेट करें ({price} बैलेंस खर्च)',
ok: 'पुष्टि करें',
rechartNotice: 'रिचार्ज निर्देश',
rechargeNotice1: '1. रिचार्ज तुरंत जमा होता है। बदलाव न दिखे तो रिफ्रेश करें।',
rechargeNotice2: '2. लिमिटेड पैक प्रचार हैं; आप कई बार रिचार्ज कर सकते हैं।',
rechargeNotice3: '3. रिचार्ज की गई राशि की कोई समय सीमा नहीं।',
rechargeNotice4: 'खरीदने से पहले पैक देखें; वापसी नहीं होती।',
rechargeLeft: 'बैलेंस',
dollor: 'USD',
isDevelop: 'विकास में है।',
copySuccessfully: 'कॉपी हुआ',
copyLink: 'लिंक कॉपी करें',
goPay: 'भुगतान पर जाएं',
filePreview: 'पूर्वावलोकन',
doSame: 'इसी तरह बनाएं',
saveQRCode: 'QR कोड सहेजें',
invitationCodePlaceholder: 'इनवाइट कोड दर्ज करें',
testRecharge: 'टेस्ट रिचार्ज',
orderNo: 'ऑर्डर नंबर',
orderNoP: 'ऑर्डर नंबर खाली नहीं हो सकता',
emailNotExists: 'यह ईमेल रजिस्टर नहीं है',
gearNotExists: 'चुना गया रिचार्ज प्लान मौजूद नहीं; दूसरा चुनें।',
myProduct: 'मेरी रचनाएं',
permissionError: 'आपका कुल रिचार्ज {total} है, प्रीव्यू के लिए पर्याप्त नहीं।',
createTagFailed: 'जनरेशन विफल; रिफ्रेश करके दोबारा कोशिश करें।',
loadingText: 'लोड हो रहा है...',
hasMore: 'ऊपर खींचें और अधिक लोड करें',
noMore: 'और नहीं',
giftAmount: 'जमा राशि',
cardNo: 'कार्ड नंबर',
cardName: 'कार्डधारक का नाम',
cardNoRequired: 'कार्ड नंबर दर्ज करें',
cardNameRequired: 'कार्डधारक का नाम दर्ज करें',
rechargeFailed: 'रिचार्ज विफल',
vmCardInfo: 'कार्ड जानकारी',
cardNumber: 'कार्ड नंबर',
cardNumberPlaceholder: 'कार्ड नंबर दर्ज करें',
cardNumberRequired: 'कार्ड नंबर दर्ज करें',
cardNumberInvalid: 'कार्ड नंबर 13-19 अंकों का होना चाहिए',
cvc: 'CVC',
cvcPlaceholder: 'CVC दर्ज करें',
cvcRequired: 'CVC दर्ज करें',
cvcInvalid: 'CVC 3 अंकों का होना चाहिए',
expYear: 'समाप्ति वर्ष',
expYearPlaceholder: 'वर्ष दर्ज करें (जैसे 2027)',
expYearRequired: 'समाप्ति वर्ष दर्ज करें',
expYearInvalid: 'वर्ष 4 अंकों का होना चाहिए',
expMonth: 'समाप्ति माह',
expMonthPlaceholder: 'माह चुनें',
expMonthRequired: 'माह चुनें',
email: 'ईमेल',
emailPlaceholder: 'ईमेल दर्ज करें',
emailRequired: 'ईमेल दर्ज करें',
emailInvalid: 'ईमेल फॉर्मेट गलत',
emailMaxLength: 'ईमेल 30 अक्षर से अधिक नहीं',
firstName: 'पहला नाम',
firstNamePlaceholder: 'पहला नाम दर्ज करें',
firstNameRequired: 'पहला नाम दर्ज करें',
firstNameMaxLength: 'पहला नाम 30 अक्षर से अधिक नहीं',
lastName: 'उपनाम',
lastNamePlaceholder: 'उपनाम दर्ज करें',
lastNameRequired: 'उपनाम दर्ज करें',
lastNameMaxLength: 'उपनाम 30 अक्षर से अधिक नहीं',
country: 'देश',
countryPlaceholder: 'देश चुनें',
countryRequired: 'देश चुनें',
submit: 'भेजें',
cancel: 'रद्द करें'
}

View File

@ -1,7 +0,0 @@
import route from './route'
import common from './common.js'
export default {
route,
common
}

View File

@ -1,15 +0,0 @@
export default {
index: 'होम',
imageToImage: 'एक क्लिक में कपड़े उतारें',
imageToImage2: 'इमेज टू इमेज 2',
changeFace: 'चेहरा बदलें',
changeFaceVideo: 'वीडियो में चेहरा बदलें',
fastImage: 'छवि जनरेट करें',
fastVideo: 'वीडियो जनरेट करें',
recharge: 'त्वरित रिचार्ज',
help: 'सहायता केंद्र',
moneyInvite: 'इनाम निमंत्रण',
assetGroupManage: 'एसेट समूह प्रबंधन',
assetManage: 'एसेट प्रबंधन',
thirdPartyAsset: 'तीसरे पक्ष की सामग्री'
}

View File

@ -1,41 +1,19 @@
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
import Cookies from 'js-cookie'
import zh_HK from '@/lang/zh_HK/index.js' import zh_HK from '@/lang/zh_HK/index.js'
import en_US from '@/lang/en_US/index.js'
import es_ES from '@/lang/es_ES/index.js'
import pt_BR from '@/lang/pt_BR/index.js'
import hi_IN from '@/lang/hi_IN/index.js'
import ru_RU from '@/lang/ru_RU/index.js'
import ar_SA from '@/lang/ar_SA/index.js'
import fr_FR from '@/lang/fr_FR/index.js'
// 多语言切换已禁用:全站固定繁体中文 // 多语言切换已禁用:全站固定繁体中文
let locale = 'zh_HK' let locale = 'zh_HK'
/** 各语言在界面上的显示名称 */ /** 各语言在界面上的显示名称 */
export const LOCALE_NAMES = { export const LOCALE_NAMES = {
zh_HK: '繁体中文', zh_HK: '繁体中文'
en_US: 'English',
es_ES: 'Español',
pt_BR: 'Português',
hi_IN: 'हिन्दी',
ru_RU: 'Русский',
ar_SA: 'العربية',
fr_FR: 'Français'
} }
const i18n = createI18n({ const i18n = createI18n({
globalInjection: true, globalInjection: true,
locale, locale,
messages: { messages: {
zh_HK, zh_HK
en_US,
es_ES,
pt_BR,
hi_IN,
ru_RU,
ar_SA,
fr_FR
} }
}) })

View File

@ -1,7 +1,5 @@
import en_USLocale from './en/index'
import zh_HKLocale from './zh_HK/index' import zh_HKLocale from './zh_HK/index'
export default { export default {
en_US: en_USLocale,
zh_HK: zh_HKLocale zh_HK: zh_HKLocale
} }

View File

@ -1,179 +0,0 @@
export default {
image1: 'Despir em um clique',
image2: 'Imagem para Imagem 2',
uploadImage: 'Enviar imagem',
uploadImageTip: 'PNG/JPG, máx. 10MB',
uploadPlaceholder: 'Clique para enviar imagem',
selectImageSource: 'Selecionar origem da imagem',
selectTemplate: 'Selecionar modelo',
reselectTemplate: 'Selecionar outro modelo',
noTemplates: 'Sem modelos',
tag1: 'Tipo de tag 1',
tag2: 'Tipo de tag 2',
tag3: 'Tipo de tag 3',
generateImage: 'Gerar agora (usa {score} de saldo)',
generateImageNow: 'Gerar agora',
generateTip: 'Após enviar, veja em "Minhas obras"',
generateVideo: 'Gerar vídeo',
imageFace: 'Trocar rosto em imagem',
videoFace: 'Trocar rosto em vídeo',
uploadImageFace: 'Clique para enviar imagem do rosto',
uploadTemplate: 'Clique para enviar modelo personalizado',
textPlaceholder: 'Descreva a imagem que deseja gerar',
uploadImageError: 'Envie uma imagem',
replaceImage: 'Substituir imagem',
uploadFaceImageError: 'Envie uma imagem do rosto',
uploadTemplateError: 'Envie um modelo personalizado',
textError: 'Digite o texto de indicação',
textVideoPlaceholder: 'Descreva o vídeo que deseja gerar',
uploadFirstPlaceholder: 'Clique para enviar primeiro frame',
uploadLastPlaceholder: 'Clique para enviar último frame',
uploadFirstImageError: 'Envie a imagem do primeiro frame',
uploadWaitImageError: 'Aguarde o envio terminar',
saveVideo: 'Baixar vídeo',
videoLoadingText: 'Gerando vídeo...',
viewVideo: 'Ver vídeo',
changeFacePrompt: 'Extrair o rosto da segunda imagem e substituir na primeira',
rechartTip1: 'Ao recarregar por carteira, confirme a rede do carteira.',
rechartTip2: 'A recarga pode atrasar; aguarde 3-5 minutos antes de atualizar.',
walletAddr: 'Endereço da carteira',
fbTitle: 'Aviso: este site é apenas para maiores de 18 anos.',
fbContent: 'Ao entrar confirmo que tenho 18 anos ou mais',
fbCancel: 'Menor de 18',
fbOK: 'Tenho 18 ou mais',
sorry: 'Desculpe!',
useLess: 'Você não pode usar este site...',
loginAccount: 'Entrar',
logout: 'Sair',
userEmailPlaceholder: 'Digite e-mail ou usuário',
passwordPlaceholder: 'Digite a senha',
forgetPassword: 'Esqueceu a senha?',
register: 'Cadastrar',
login: 'Entrar',
forgotPassword: 'Esqueci a senha',
registerAccount: 'Criar conta',
usernamePlaceholder: 'Digite o usuário',
codePlaceholder: 'Digite o código de verificação',
confirmPasswordPlaceholder: 'Confirme a senha',
backToLogin: 'Voltar ao login',
send: 'Enviar',
emailValidPlaceholder: 'Digite um e-mail válido',
confirmPwdValidMsg: 'As senhas não coincidem',
editPassword: 'Alterar senha',
recharge: 'Recarregar',
myAccount: 'Minha conta',
moneyInvite: 'Convite com recompensa',
rechargeRecord: 'Histórico de recargas',
resumeRecord: 'Histórico de consumo',
inviteRecord: 'Histórico de convites',
username: 'Usuário',
userId: 'ID do usuário',
accountAmount: 'Saldo',
editUserInfo: 'Editar dados',
contact: 'Contato',
backToUser: 'Voltar',
moneyTips: 'Ao convidar um amigo, você ganha {rate} do valor que ele recarregar.',
inviteCode: 'Código de convite',
inviteLink: 'Link de convite',
saveImage: 'Salvar imagem',
totalAmount: 'Total recarregado',
amount: 'Valor',
sendAmount: 'Valor de brinde',
rechargeType: 'Forma de recarga',
rechargeTime: 'Data da recarga',
emptyText: 'Sem dados',
product: 'Obra',
resumeAmount: 'Valor consumido',
productType: 'Tipo de obra',
productTime: 'Data de criação',
totalReward: 'Saldo de recompensas',
rewardAmount: 'Valor da recompensa',
rewardTime: 'Data da recompensa',
reSend: 'Reenviar',
registerSuccessfully: 'Cadastro concluído',
loginSuccessfully: 'Login realizado',
passwordResetSuccessfully: 'Senha redefinida',
rechargeSuccessfully: 'Recarga concluída',
avatar: 'Avatar',
input: 'Digite',
save: 'Salvar',
editEmail: 'Alterar e-mail',
editEmailSuccessfully: 'E-mail atualizado',
updateAvatarSuccessfully: 'Avatar atualizado',
balenceLow: 'Saldo insuficiente, recarregue',
confirm: 'Confirmar',
createFailed: 'Falha na geração, saldo devolvido',
notice: 'Aviso',
oldPasswordPlaceholder: 'Digite a senha antiga',
newpasswordPlaceholder: 'Digite a nova senha',
switchPageTip: 'O vídeo está sendo gerado; saia e veja o histórico de consumo.',
loginless: 'Sessão expirada; faça login novamente.',
createVideo: 'Gerar vídeo (usa {price} de saldo)',
ok: 'Confirmar',
rechartNotice: 'Instruções de recarga',
rechargeNotice1: '1. A recarga é creditada na hora. Se não mudar, atualize a página.',
rechargeNotice2: '2. Pacotes por tempo limitado são promoções; você pode recarregar várias vezes.',
rechargeNotice3: '3. O saldo recarregado não tem prazo.',
rechargeNotice4: 'Confira o pacote antes de comprar; não há reembolso.',
rechargeLeft: 'Saldo',
dollor: 'USD',
isDevelop: 'Em desenvolvimento.',
copySuccessfully: 'Copiado',
copyLink: 'Copiar link',
goPay: 'Ir para pagamento',
filePreview: 'Visualizar',
doSame: 'Fazer igual',
saveQRCode: 'Salvar código QR',
invitationCodePlaceholder: 'Digite o código de convite',
testRecharge: 'Recarga de teste',
orderNo: 'Número do pedido',
orderNoP: 'O número do pedido é obrigatório',
emailNotExists: 'O e-mail não está cadastrado',
gearNotExists: 'O plano de recarga não existe; escolha outro.',
myProduct: 'Minhas obras',
permissionError: 'Sua recarga acumulada é {total}, não atende para visualizar.',
createTagFailed: 'Falha na geração; atualize e tente de novo.',
loadingText: 'Carregando...',
hasMore: 'Deslize para mais',
noMore: 'Não há mais',
giftAmount: 'Valor creditado',
cardNo: 'Número do cartão',
cardName: 'Nome no cartão',
cardNoRequired: 'Digite o número do cartão',
cardNameRequired: 'Digite o nome no cartão',
rechargeFailed: 'Recarga falhou',
vmCardInfo: 'Dados do cartão',
cardNumber: 'Número do cartão',
cardNumberPlaceholder: 'Digite o número do cartão',
cardNumberRequired: 'Digite o número do cartão',
cardNumberInvalid: 'O número deve ter 13-19 dígitos',
cvc: 'CVC',
cvcPlaceholder: 'Digite o CVC',
cvcRequired: 'Digite o CVC',
cvcInvalid: 'CVC deve ter 3 dígitos',
expYear: 'Ano de validade',
expYearPlaceholder: 'Digite o ano (ex.: 2027)',
expYearRequired: 'Digite o ano de validade',
expYearInvalid: 'O ano deve ter 4 dígitos',
expMonth: 'Mês de validade',
expMonthPlaceholder: 'Selecione o mês',
expMonthRequired: 'Selecione o mês',
email: 'E-mail',
emailPlaceholder: 'Digite o e-mail',
emailRequired: 'Digite o e-mail',
emailInvalid: 'Formato de e-mail inválido',
emailMaxLength: 'Máx. 30 caracteres',
firstName: 'Nome',
firstNamePlaceholder: 'Digite o nome',
firstNameRequired: 'Digite o nome',
firstNameMaxLength: 'Máx. 30 caracteres',
lastName: 'Sobrenome',
lastNamePlaceholder: 'Digite o sobrenome',
lastNameRequired: 'Digite o sobrenome',
lastNameMaxLength: 'Máx. 30 caracteres',
country: 'País',
countryPlaceholder: 'Selecione o país',
countryRequired: 'Selecione o país',
submit: 'Enviar',
cancel: 'Cancelar'
}

View File

@ -1,7 +0,0 @@
import route from './route'
import common from './common.js'
export default {
route,
common
}

View File

@ -1,15 +0,0 @@
export default {
index: 'Início',
imageToImage: 'Despir em um clique',
imageToImage2: 'Imagem para Imagem 2',
changeFace: 'Trocar rosto',
changeFaceVideo: 'Trocar rosto em vídeo',
fastImage: 'Gerar imagem',
fastVideo: 'Gerar vídeo',
recharge: 'Recarga rápida',
help: 'Central de ajuda',
moneyInvite: 'Convite com recompensa',
assetGroupManage: 'Gerenciamento de grupos de ativos',
assetManage: 'Gerenciamento de ativos',
thirdPartyAsset: 'Ativos de terceiros'
}

View File

@ -1,179 +0,0 @@
export default {
image1: 'Раздеть в один клик',
image2: 'Изображение в изображение 2',
uploadImage: 'Загрузить изображение',
uploadImageTip: 'PNG/JPG, макс. 10 МБ',
uploadPlaceholder: 'Нажмите, чтобы загрузить',
selectImageSource: 'Выберите источник изображения',
selectTemplate: 'Выберите шаблон',
reselectTemplate: 'Выбрать шаблон заново',
noTemplates: 'Нет шаблонов',
tag1: 'Тип метки 1',
tag2: 'Тип метки 2',
tag3: 'Тип метки 3',
generateImage: 'Создать сейчас (списание {score} с баланса)',
generateImageNow: 'Создать сейчас',
generateTip: 'После отправки смотрите в «Мои работы»',
generateVideo: 'Создать видео',
imageFace: 'Заменить лицо на изображении',
videoFace: 'Заменить лицо в видео',
uploadImageFace: 'Нажмите, чтобы загрузить фото лица',
uploadTemplate: 'Нажмите, чтобы загрузить свой шаблон',
textPlaceholder: 'Опишите изображение, которое хотите получить',
uploadImageError: 'Загрузите изображение',
replaceImage: 'Заменить изображение',
uploadFaceImageError: 'Загрузите фото лица',
uploadTemplateError: 'Загрузите свой шаблон',
textError: 'Введите текст подсказки',
textVideoPlaceholder: 'Опишите видео, которое хотите получить',
uploadFirstPlaceholder: 'Нажмите, чтобы загрузить первый кадр',
uploadLastPlaceholder: 'Нажмите, чтобы загрузить последний кадр',
uploadFirstImageError: 'Загрузите изображение первого кадра',
uploadWaitImageError: 'Дождитесь окончания загрузки',
saveVideo: 'Скачать видео',
videoLoadingText: 'Создание видео...',
viewVideo: 'Смотреть видео',
changeFacePrompt: 'Взять лицо со второй картинки и заменить им лицо на первой',
rechartTip1: 'При пополнении через кошелёк проверьте сеть кошелька.',
rechartTip2: 'Зачисление может задерживаться; подождите 35 минут и обновите.',
walletAddr: 'Адрес кошелька:',
fbTitle: 'Внимание! Сайт только для взрослых!',
fbContent: 'Входя, я подтверждаю, что мне 18 лет или больше',
fbCancel: 'Мне нет 18',
fbOK: 'Мне есть 18',
sorry: 'Извините!',
useLess: 'Вам запрещено пользоваться этим сайтом...',
loginAccount: 'Вход',
logout: 'Выйти',
userEmailPlaceholder: 'Введите email или логин',
passwordPlaceholder: 'Введите пароль',
forgetPassword: 'Забыли пароль?',
register: 'Регистрация',
login: 'Войти',
forgotPassword: 'Забыл пароль',
registerAccount: 'Создать аккаунт',
usernamePlaceholder: 'Введите имя пользователя',
codePlaceholder: 'Введите код подтверждения',
confirmPasswordPlaceholder: 'Подтвердите пароль',
backToLogin: 'Вернуться к входу',
send: 'Отправить',
emailValidPlaceholder: 'Введите корректный email',
confirmPwdValidMsg: 'Пароли не совпадают',
editPassword: 'Изменить пароль',
recharge: 'Пополнить',
myAccount: 'Мой аккаунт',
moneyInvite: 'Приглашение с наградой',
rechargeRecord: 'История пополнений',
resumeRecord: 'История списаний',
inviteRecord: 'История приглашений',
username: 'Имя пользователя',
userId: 'ID пользователя',
accountAmount: 'Баланс',
editUserInfo: 'Редактировать данные',
contact: 'Связаться с нами',
backToUser: 'Назад',
moneyTips: 'При регистрации по вашей ссылке вы получаете {rate} от каждой его пополнения.',
inviteCode: 'Код приглашения',
inviteLink: 'Ссылка приглашения',
saveImage: 'Сохранить изображение',
totalAmount: 'Всего пополнено',
amount: 'Сумма',
sendAmount: 'Сумма бонуса',
rechargeType: 'Способ пополнения',
rechargeTime: 'Время пополнения',
emptyText: 'Нет данных',
product: 'Работа',
resumeAmount: 'Списано',
productType: 'Тип работы',
productTime: 'Дата создания',
totalReward: 'Баланс наград',
rewardAmount: 'Сумма награды',
rewardTime: 'Время награды',
reSend: 'Отправить снова',
registerSuccessfully: 'Регистрация выполнена',
loginSuccessfully: 'Вход выполнен',
passwordResetSuccessfully: 'Пароль сброшен',
rechargeSuccessfully: 'Пополнение выполнено',
avatar: 'Аватар',
input: 'Введите',
save: 'Сохранить',
editEmail: 'Изменить email',
editEmailSuccessfully: 'Email обновлён',
updateAvatarSuccessfully: 'Аватар обновлён',
balenceLow: 'Недостаточно средств, пополните баланс',
confirm: 'Подтвердить',
createFailed: 'Ошибка создания, средства возвращены',
notice: 'Уведомление',
oldPasswordPlaceholder: 'Введите старый пароль',
newpasswordPlaceholder: 'Введите новый пароль',
switchPageTip: 'Видео создаётся; выйдите и проверьте историю списаний.',
loginless: 'Сессия истекла. Войдите снова.',
createVideo: 'Создать видео (списание {price} с баланса)',
ok: 'Подтвердить',
rechartNotice: 'Правила пополнения',
rechargeNotice1: '1. Средства зачисляются сразу. Если не изменилось — обновите страницу.',
rechargeNotice2: '2. Акционные пакеты проводятся периодически; можно пополнять несколько раз.',
rechargeNotice3: '3. Пополненный баланс не имеет срока действия.',
rechargeNotice4: 'Перед покупкой ознакомьтесь с пакетом; возврат не предусмотрен.',
rechargeLeft: 'Баланс',
dollor: 'USD',
isDevelop: 'В разработке.',
copySuccessfully: 'Скопировано',
copyLink: 'Копировать ссылку',
goPay: 'Перейти к оплате',
filePreview: 'Предпросмотр',
doSame: 'Сделать похожее',
saveQRCode: 'Сохранить QR-код',
invitationCodePlaceholder: 'Введите код приглашения',
testRecharge: 'Тестовое пополнение',
orderNo: 'Номер заказа',
orderNoP: 'Номер заказа обязателен',
emailNotExists: 'Этот email не зарегистрирован',
gearNotExists: 'Выбранный тариф отсутствует; выберите другой.',
myProduct: 'Мои работы',
permissionError: 'Ваше суммарное пополнение {total}, недостаточно для просмотра.',
createTagFailed: 'Ошибка создания; обновите страницу и повторите.',
loadingText: 'Загрузка...',
hasMore: 'Потяните вверх для подгрузки',
noMore: 'Больше нет',
giftAmount: 'Зачисленная сумма',
cardNo: 'Номер карты',
cardName: 'Имя владельца',
cardNoRequired: 'Введите номер карты',
cardNameRequired: 'Введите имя владельца',
rechargeFailed: 'Ошибка пополнения',
vmCardInfo: 'Данные карты',
cardNumber: 'Номер карты',
cardNumberPlaceholder: 'Введите номер карты',
cardNumberRequired: 'Введите номер карты',
cardNumberInvalid: 'Номер карты должен быть из 1319 цифр',
cvc: 'CVC',
cvcPlaceholder: 'Введите CVC',
cvcRequired: 'Введите CVC',
cvcInvalid: 'CVC должен быть из 3 цифр',
expYear: 'Год срока действия',
expYearPlaceholder: 'Введите год (например 2027)',
expYearRequired: 'Введите год',
expYearInvalid: 'Год должен быть из 4 цифр',
expMonth: 'Месяц срока действия',
expMonthPlaceholder: 'Выберите месяц',
expMonthRequired: 'Выберите месяц',
email: 'Email',
emailPlaceholder: 'Введите email',
emailRequired: 'Введите email',
emailInvalid: 'Некорректный email',
emailMaxLength: 'Email не более 30 символов',
firstName: 'Имя',
firstNamePlaceholder: 'Введите имя',
firstNameRequired: 'Введите имя',
firstNameMaxLength: 'Имя не более 30 символов',
lastName: 'Фамилия',
lastNamePlaceholder: 'Введите фамилию',
lastNameRequired: 'Введите фамилию',
lastNameMaxLength: 'Фамилия не более 30 символов',
country: 'Страна',
countryPlaceholder: 'Выберите страну',
countryRequired: 'Выберите страну',
submit: 'Отправить',
cancel: 'Отмена'
}

View File

@ -1,7 +0,0 @@
import route from './route'
import common from './common.js'
export default {
route,
common
}

View File

@ -1,15 +0,0 @@
export default {
index: 'Главная',
imageToImage: 'Раздеть в один клик',
imageToImage2: 'Изображение в изображение 2',
changeFace: 'Заменить лицо',
changeFaceVideo: 'Заменить лицо в видео',
fastImage: 'Создать изображение',
fastVideo: 'Создать видео',
recharge: 'Быстрая пополнение',
help: 'Центр помощи',
moneyInvite: 'Приглашение с наградой',
assetGroupManage: 'Управление группами ресурсов',
assetManage: 'Управление ресурсами',
thirdPartyAsset: 'Сторонние материалы'
}

View File

@ -12,5 +12,6 @@ export default {
moneyInvite: '有獎邀請', moneyInvite: '有獎邀請',
assetGroupManage: '資源組管理', assetGroupManage: '資源組管理',
assetManage: '素材管理', assetManage: '素材管理',
thirdPartyAsset: '三方素材管理' thirdPartyAsset: '三方素材管理',
generatedAssets: '生成庫'
} }

View File

@ -47,7 +47,7 @@ import { constantRoutes } from '@/router/index'
import { generateTitle, generateLang } from '@/utils/i18n' import { generateTitle, generateLang } from '@/utils/i18n'
/** 左侧导航仅显示这些路由name 与 router/index.js 一致) */ /** 左侧导航仅显示这些路由name 与 router/index.js 一致) */
const SIDEBAR_ONLY_ROUTE_NAMES = ['video-gen', 'third-party-asset'] const SIDEBAR_ONLY_ROUTE_NAMES = ['video-gen', 'third-party-asset', 'generated-assets']
defineProps({ defineProps({
collapsed: Boolean collapsed: Boolean
@ -152,6 +152,9 @@ const getMenuSvg = (key) => {
if (key === 'third-party-asset') { if (key === 'third-party-asset') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h7l2 2h7v12H4V6Zm2 4v8h12v-8H6Zm2 2h8v4H8v-4Z"/></svg>' return '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h7l2 2h7v12H4V6Zm2 4v8h12v-8H6Zm2 2h8v4H8v-4Z"/></svg>'
} }
if (key === 'generated-assets') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2l-5.5 9h11L12 2zm0 3.5L14.5 9h-5L12 5.5zM4 13v8h16v-8H4zm2 2h12v4H6v-4z"/><path d="M7 15h10v2H7z"/></svg>'
}
return '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/></svg>' return '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/></svg>'
} }
</script> </script>

View File

@ -139,7 +139,17 @@ export const constantRoutes = [{
permission: "pass", permission: "pass",
icon: 'btn_video' icon: 'btn_video'
} }
}, { },{
path: 'generated-assets',
name: 'generated-assets',
component: () => import('@/views/GeneratedAssets.vue'),
meta: {
title: 'generatedAssets',
menuItem: true,
permission: "pass",
icon: 'btn_video'
}
}, {
path: 'asset-group-manage', path: 'asset-group-manage',
name: 'asset-group-manage', name: 'asset-group-manage',
component: () => import('@/views/AssetGroupManage.vue'), component: () => import('@/views/AssetGroupManage.vue'),

View File

@ -0,0 +1,838 @@
<template>
<div class="generated-assets-page">
<div class="page-header">
<div class="panel-title">生成资产管理</div>
</div>
<!-- 查询区域 -->
<div class="query-section">
<div class="form-grid">
<div class="field">
<label>收藏状态</label>
<a-select v-model="filters.isTop" clearable placeholder="全部">
<a-option :value="null">全部</a-option>
<a-option value="Y">已收藏</a-option>
<a-option value="N">未收藏</a-option>
</a-select>
</div>
<div class="field">
<label>开始时间</label>
<a-date-picker v-model="filters.beginTime" placeholder="选择开始时间" style="width: 100%" />
</div>
<div class="field">
<label>结束时间</label>
<a-date-picker v-model="filters.endTime" placeholder="选择结束时间" style="width: 100%" />
</div>
<div class="field actions">
<a-button type="primary" :loading="loading" @click="search(1)">查询</a-button>
<a-button @click="resetFilters">重置</a-button>
</div>
</div>
</div>
<!-- 表格区域 -->
<a-spin :loading="loading">
<div class="total-line">总数{{ pagination.total }}</div>
<div class="table-wrap">
<table class="asset-table">
<thead>
<tr>
<th>ID</th>
<th>订单编号</th>
<th>类型</th>
<th>状态</th>
<th>模型参数</th>
<th>生成结果</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in dataList" :key="item.id">
<td class="td-id">{{ item.id }}</td>
<td>{{ item.orderNum || '-' }}</td>
<td>{{ formatType(item.type) }}</td>
<td>
<a-tag :color="getStatusColor(item.status)">
{{ formatStatus(item.status) }}
</a-tag>
</td>
<td>
<div class="params-cell">
<div v-if="item.model" class="param-tag">模型: {{ item.model }}</div>
<div v-if="item.duration" class="param-tag">时长: {{ item.duration }}s</div>
<div v-if="item.resolution" class="param-tag">分辨率: {{ item.resolution }}</div>
<div v-if="item.ratio" class="param-tag">比例: {{ item.ratio }}</div>
<div v-if="item.mode" class="param-tag">模式: {{ formatMode(item.mode) }}</div>
<div v-if="!item.model && !item.duration && !item.resolution && !item.ratio && !item.mode" class="param-empty">-</div>
</div>
</td>
<td class="result-cell">
<div v-if="isVideoResult(item.result)" class="media-preview">
<video
class="video-thumb"
:src="item.result"
controls
preload="metadata"
@click.stop="openPreview(item.result, 'video')" />
</div>
<div v-else-if="isImageResult(item.result)" class="media-preview">
<img
class="image-thumb"
:src="item.result"
@click.stop="openPreview(item.result, 'image')" />
</div>
<div v-else-if="item.result && item.result.startsWith('cgt-')" class="task-id">
{{ item.result }}
</div>
<div v-else class="result-empty">-</div>
</td>
<td>{{ item.createTime || '-' }}</td>
<td>
<a-button
size="mini"
:type="item.isTop === 'Y' ? 'primary' : 'outline'"
:status="item.isTop === 'Y' ? 'success' : 'default'"
@click="toggleFavorite(item)">
<template v-if="item.isTop === 'Y'">
<a-icon name="star-fill" /> 已收藏
</template>
<template v-else>
<a-icon name="star" /> 收藏
</template>
</a-button>
<a-button
size="mini"
type="outline"
@click="viewDetail(item)">
详情
</a-button>
</td>
</tr>
<tr v-if="!dataList.length">
<td colspan="8" class="empty-tip">暂无数据</td>
</tr>
</tbody>
</table>
</div>
<div class="pager">
<a-pagination
:total="pagination.total"
:current="pagination.pageNum"
:page-size="pagination.pageSize"
show-total
show-jumper
show-page-size
:page-size-options="pagination.pageSizes"
@change="changePage"
@page-size-change="changePageSize" />
</div>
</a-spin>
<!-- 详情弹窗 -->
<a-modal v-model:visible="detailVisible" title="订单详情" :footer="false" width="850px">
<div class="detail-content" v-if="detailData">
<div class="detail-form">
<!-- 基本信息 -->
<div class="detail-group">
<div class="group-title">基本信息</div>
<div class="detail-row">
<div class="label">订单编号</div>
<div class="value">{{ detailData.orderNum || '-' }}</div>
</div>
<div class="detail-row">
<div class="label">功能类型</div>
<div class="value">{{ formatType(detailData.type) }}</div>
</div>
<div class="detail-row">
<div class="label">扣除金额</div>
<div class="value">{{ detailData.amount ? detailData.amount + ' 金币' : '-' }}</div>
</div>
<div class="detail-row">
<div class="label">订单状态</div>
<div class="value">
<a-tag :color="getStatusColor(detailData.status)">
{{ formatStatus(detailData.status) }}
</a-tag>
</div>
</div>
<div class="detail-row">
<div class="label">执行状态</div>
<div class="value">{{ formatExtStatus(detailData.extStatus) }}</div>
</div>
<div class="detail-row">
<div class="label">收藏状态</div>
<div class="value">
<a-tag v-if="detailData.isTop === 'Y'" color="orange">已收藏</a-tag>
<span v-else class="text-muted">未收藏</span>
</div>
</div>
<div class="detail-row">
<div class="label">请求来源</div>
<div class="value">{{ detailData.source || '-' }}</div>
</div>
</div>
<!-- 生成参数 -->
<div class="detail-group">
<div class="group-title">生成参数</div>
<div class="detail-row">
<div class="label">提示词</div>
<div class="value long-text">{{ detailData.text || '-' }}</div>
</div>
<div class="detail-row">
<div class="label">生成模式</div>
<div class="value">{{ formatMode(detailData.mode) }}</div>
</div>
<div class="detail-row">
<div class="label">使用模型</div>
<div class="value">{{ detailData.model || '-' }}</div>
</div>
<div class="detail-row">
<div class="label">视频时长</div>
<div class="value">{{ detailData.duration ? detailData.duration + ' 秒' : '-' }}</div>
</div>
<div class="detail-row">
<div class="label">分辨率</div>
<div class="value">{{ detailData.resolution || '-' }}</div>
</div>
<div class="detail-row">
<div class="label">画面比例</div>
<div class="value">{{ detailData.ratio || '-' }}</div>
</div>
</div>
<!-- 生成结果 -->
<div class="detail-group">
<div class="group-title">生成结果</div>
<div class="detail-row">
<div class="label">生成内容</div>
<div class="value media-preview">
<video
v-if="isVideoResult(detailData.result)"
class="detail-video"
:src="detailData.result"
controls
preload="metadata" />
<img
v-else-if="isImageResult(detailData.result)"
class="detail-image"
:src="detailData.result"
@click="viewImageFull(detailData.result)" />
<div v-else class="result-text">{{ detailData.result || '无结果' }}</div>
</div>
</div>
<div class="detail-row" v-if="detailData.img1">
<div class="label">首帧图片</div>
<div class="value media-preview">
<img class="detail-image small" :src="detailData.img1" @click="viewImageFull(detailData.img1)" />
</div>
</div>
<div class="detail-row" v-if="detailData.img2">
<div class="label">尾帧图片</div>
<div class="value media-preview">
<img class="detail-image small" :src="detailData.img2" @click="viewImageFull(detailData.img2)" />
</div>
</div>
</div>
<!-- 完整参数 -->
<div class="detail-group" v-if="detailData.videoParams">
<div class="group-title">完整参数</div>
<div class="detail-row">
<div class="label">JSON参数</div>
<div class="value">
<pre class="json-block">{{ formatVideoParams(detailData.videoParams) }}</pre>
</div>
</div>
</div>
<!-- 时间信息 -->
<div class="detail-group">
<div class="group-title">时间信息</div>
<div class="detail-row">
<div class="label">创建时间</div>
<div class="value">{{ detailData.createTime || '-' }}</div>
</div>
<div class="detail-row">
<div class="label">更新时间</div>
<div class="value">{{ detailData.updateTime || '-' }}</div>
</div>
</div>
</div>
</div>
</a-modal>
<!-- 预览弹窗 -->
<a-modal
v-model:visible="previewVisible"
:title="previewType === 'video' ? '视频预览' : '图片预览'"
:footer="false"
width="800px"
@cancel="closePreview">
<div class="preview-content">
<video
v-if="previewType === 'video' && previewUrl"
class="preview-video"
:src="previewUrl"
controls
autoplay />
<img
v-else-if="previewType === 'image' && previewUrl"
class="preview-image"
:src="previewUrl" />
</div>
</a-modal>
</div>
</template>
<script>
export default {
name: 'GeneratedAssets',
data() {
return {
loading: false,
dataList: [],
filters: {
isTop: null,
beginTime: '',
endTime: ''
},
pagination: {
total: 0,
pageNum: 1,
pageSize: 10,
pageSizes: [10, 20, 50, 100]
},
detailVisible: false,
detailData: null,
previewVisible: false,
previewUrl: '',
previewType: 'video'
}
},
mounted() {
this.search()
},
methods: {
//
search(page = 1) {
this.pagination.pageNum = page
this.loading = true
const params = {
pageNum: this.pagination.pageNum,
pageSize: this.pagination.pageSize
}
// - 使 is_top
if (this.filters.isTop != null && String(this.filters.isTop) !== '') {
// isTopnull
if (this.filters.isTop === null || this.filters.isTop === '' || this.filters.isTop === '全部') {
delete params.is_top
} else {
params.is_top = String(this.filters.isTop)
}
console.log('传递 is_top 参数:', params.is_top)
}
if (this.filters.beginTime) {
params.beginTime = this.formatDate(this.filters.beginTime)
}
if (this.filters.endTime) {
params.endTime = this.formatDate(this.filters.endTime)
}
console.log('查询参数:', params) //
this.$axios({
url: 'api/portal/assets/list',
method: 'GET',
data: params
})
.then((res) => {
this.loading = false
if (res.code === 200) {
this.dataList = res.rows || []
this.pagination.total = res.total || 0
} else {
this.$message.error(res.msg || '查询失败')
}
})
.catch((err) => {
this.loading = false
this.$message.error(err?.message || '查询失败')
})
},
//
resetFilters() {
this.filters.isTop = null
this.filters.beginTime = ''
this.filters.endTime = ''
this.search(1)
},
//
changePage(page) {
this.pagination.pageNum = page
this.search(page)
},
//
changePageSize(pageSize) {
this.pagination.pageSize = pageSize
this.pagination.pageNum = 1
this.search(1)
},
//
formatDate(date) {
if (!date) return ''
const d = new Date(date)
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
//
formatType(type) {
const typeMap = {
'11': '图生图',
'12': '图生图2',
'13': '换脸',
'1': '快速生图',
'21': '快速生视频'
}
return typeMap[String(type)] || type || '-'
},
//
formatStatus(status) {
const statusMap = {
0: '进行中',
1: '已完成',
2: '失败'
}
return statusMap[status] || status || '-'
},
//
formatExtStatus(extStatus) {
const statusMap = {
'running': '执行中',
'queued': '队列中',
'succeeded': '已完成',
'failed': '失败',
'cancelled': '已取消',
'expired': '超时'
}
return statusMap[extStatus] || '未知'
},
//
getStatusColor(status) {
const colorMap = {
0: 'blue',
1: 'green',
2: 'red'
}
return colorMap[status] || 'default'
},
//
formatMode(mode) {
const modeMap = {
'text-to-video': '文生视频',
'image-first-frame': '图生视频·首帧',
'image-first-last-frame': '图生视频·首尾帧',
'image-reference': '图生视频·参考图'
}
return modeMap[mode] || mode || '-'
},
// videoParams
formatVideoParams(params) {
if (!params) return ''
try {
const obj = typeof params === 'string' ? JSON.parse(params) : params
return JSON.stringify(obj, null, 2)
} catch (e) {
return params
}
},
//
isVideoResult(url) {
if (!url) return false
return /\.(mp4|mov|webm|ogg|m4v|avi|mkv)(\?.*)?$/i.test(url)
},
//
isImageResult(url) {
if (!url) return false
return /\.(jpeg|jpg|png|gif|webp|bmp)(\?.*)?$/i.test(url)
},
//
openPreview(url, type) {
this.previewUrl = url
this.previewType = type
this.previewVisible = true
},
//
closePreview() {
this.previewVisible = false
this.previewUrl = ''
},
//
viewImageFull(url) {
this.$viewerApi({
options: {
initialViewIndex: 0,
toolbar: true
},
images: [url]
})
},
//
viewDetail(item) {
this.detailData = item
this.detailVisible = true
},
//
toggleFavorite(item) {
const newIsTop = item.isTop === 'Y' ? 'N' : 'Y'
this.$axios({
url: '/api/portal/assets/favorite',
method: 'POST',
data: {
id: item.id,
isTop: newIsTop
}
})
.then((res) => {
if (res.code === 200) {
item.isTop = newIsTop
this.$message.success(newIsTop === 'Y' ? '收藏成功' : '取消收藏成功')
} else {
this.$message.error(res.msg || '操作失败')
}
})
.catch((err) => {
this.$message.error(err?.message || '操作失败')
})
}
}
}
</script>
<style scoped lang="less">
.generated-assets-page {
padding: 16px;
min-height: 100%;
background: #0a0b0d;
color: rgba(255, 255, 255, 0.9);
}
.page-header {
margin-bottom: 16px;
}
.panel-title {
font-size: 16px;
font-weight: 600;
color: rgba(255, 255, 255, 0.9);
}
.query-section {
background: rgba(22, 24, 30, 0.92);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
}
.form-grid {
display: grid;
grid-template-columns: repeat(4, minmax(180px, 1fr));
gap: 12px;
@media (max-width: 1200px) {
grid-template-columns: repeat(3, minmax(180px, 1fr));
}
@media (max-width: 900px) {
grid-template-columns: repeat(2, minmax(180px, 1fr));
}
@media (max-width: 600px) {
grid-template-columns: 1fr;
}
}
.field {
display: flex;
flex-direction: column;
gap: 6px;
label {
font-size: 12px;
color: rgba(255, 255, 255, 0.65);
}
&.actions {
flex-direction: row;
align-items: flex-end;
gap: 8px;
}
}
.total-line {
margin: 10px 0;
font-size: 12px;
color: rgba(255, 255, 255, 0.65);
}
.table-wrap {
overflow: auto;
width: 100%;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 10px;
}
.asset-table {
width: 100%;
table-layout: fixed;
border-collapse: collapse;
font-size: 12px;
th, td {
padding: 12px 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
text-align: left;
vertical-align: top;
}
th {
color: rgba(255, 255, 255, 0.72);
background: rgba(255, 255, 255, 0.02);
font-weight: 500;
}
//
th:nth-child(1), td:nth-child(1) { width: 60px; }
th:nth-child(2), td:nth-child(2) { width: 120px; }
th:nth-child(3), td:nth-child(3) { width: 80px; }
th:nth-child(4), td:nth-child(4) { width: 80px; }
th:nth-child(5), td:nth-child(5) { width: 180px; }
th:nth-child(6), td:nth-child(6) { width: 200px; }
th:nth-child(7), td:nth-child(7) { width: 140px; }
th:nth-child(8), td:nth-child(8) { width: 140px; }
}
.td-id {
word-break: break-all;
font-size: 11px;
color: rgba(255, 255, 255, 0.6);
}
.params-cell {
display: flex;
flex-direction: column;
gap: 4px;
.param-tag {
font-size: 11px;
color: rgba(255, 255, 255, 0.7);
background: rgba(255, 255, 255, 0.05);
padding: 2px 6px;
border-radius: 4px;
display: inline-block;
}
.param-empty {
color: rgba(255, 255, 255, 0.35);
}
}
.result-cell {
.media-preview {
.video-thumb {
width: 160px;
height: 90px;
object-fit: cover;
border-radius: 6px;
background: #000;
cursor: pointer;
transition: transform 0.2s ease;
&:hover {
transform: scale(1.02);
}
}
.image-thumb {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 6px;
cursor: pointer;
transition: transform 0.2s ease;
&:hover {
transform: scale(1.02);
}
}
}
.task-id {
font-size: 11px;
color: rgba(255, 255, 255, 0.5);
word-break: break-all;
}
.result-empty {
color: rgba(255, 255, 255, 0.35);
}
}
.pager {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.empty-tip {
color: rgba(255, 255, 255, 0.5);
font-size: 12px;
text-align: center;
padding: 40px 0;
}
/* 详情弹窗 - label:value 形式 */
.detail-content {
padding: 20px;
font-size: 14px;
.detail-form {
display: flex;
flex-direction: column;
gap: 24px;
}
.detail-group {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
padding: 16px;
}
.group-title {
font-size: 15px;
font-weight: 600;
color: #00cae0;
margin-bottom: 14px;
padding-bottom: 8px;
border-bottom: 1px solid rgba(0, 202, 224, 0.2);
}
.detail-row {
display: flex;
align-items: flex-start;
padding: 8px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
&:last-child {
border-bottom: none;
}
}
.label {
width: 100px;
flex-shrink: 0;
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
padding-top: 2px;
}
.value {
flex: 1;
color: rgba(255, 255, 255, 0.95);
word-break: break-all;
line-height: 1.5;
&.long-text {
max-height: 110px;
overflow-y: auto;
background: rgba(0, 0, 0, 0.3);
padding: 10px;
border-radius: 6px;
font-size: 13px;
}
.text-muted {
color: rgba(255, 255, 255, 0.45);
}
}
.media-preview {
display: flex;
flex-direction: column;
gap: 12px;
}
.detail-video {
width: 100%;
max-width: 420px;
border-radius: 8px;
background: #000;
}
.detail-image {
max-width: 100%;
max-height: 260px;
border-radius: 8px;
cursor: pointer;
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.2s ease;
&:hover {
transform: scale(1.03);
box-shadow: 0 0 0 3px rgba(0, 202, 224, 0.3);
}
&.small {
max-height: 160px;
}
}
.result-text {
color: rgba(255, 255, 255, 0.5);
font-style: italic;
padding: 12px;
background: rgba(255, 255, 255, 0.05);
border-radius: 6px;
}
.json-block {
background: #1a1f2e;
color: #a5d6ff;
padding: 14px;
border-radius: 8px;
font-size: 12px;
max-height: 220px;
overflow: auto;
white-space: pre;
font-family: 'Consolas', monospace;
line-height: 1.4;
border: 1px solid rgba(165, 214, 255, 0.1);
}
}
//
.preview-content {
display: flex;
justify-content: center;
align-items: center;
.preview-video {
width: 100%;
max-height: 500px;
border-radius: 8px;
}
.preview-image {
max-width: 100%;
max-height: 500px;
border-radius: 8px;
}
}
</style>

View File

@ -68,6 +68,18 @@
@click="regenerateFromTaskRow(row)"> @click="regenerateFromTaskRow(row)">
重新生成 重新生成
</button> </button>
<button
type="button"
class="vg-link vg-chat-action-btn favorite-btn"
:class="{ 'is-favorited': row.isTop === 'Y' }"
@click="toggleFavorite(row)">
<template v-if="row.isTop === 'Y'">
已收藏
</template>
<template v-else>
收藏
</template>
</button>
</div> </div>
<div class="vg-chat-time">{{ formatCreateTime(row.createTime) }}</div> <div class="vg-chat-time">{{ formatCreateTime(row.createTime) }}</div>
</div> </div>
@ -118,6 +130,18 @@
@click="regenerateFromTaskRow(row)"> @click="regenerateFromTaskRow(row)">
重新生成 重新生成
</button> </button>
<button
type="button"
class="vg-link vg-chat-action-btn favorite-btn"
:class="{ 'is-favorited': row.isTop === 'Y' }"
@click="toggleFavorite(row)">
<template v-if="row.isTop === 'Y'">
已收藏
</template>
<template v-else>
收藏
</template>
</button>
</div> </div>
<div class="vg-chat-time">{{ formatCreateTime(row.createTime) }}</div> <div class="vg-chat-time">{{ formatCreateTime(row.createTime) }}</div>
</div> </div>
@ -1286,6 +1310,37 @@ export default {
onCancel: () => resolve() onCancel: () => resolve()
}) })
}) })
},
// / -
async toggleFavorite(row) {
if (!row || !row.id) {
this.$message.error('无效的任务记录')
return
}
const newIsTop = row.isTop === 'Y' ? 'N' : 'Y'
try {
const res = await this.$axios({
url: '/api/portal/assets/favorite',
method: 'POST',
data: {
id: row.id,
isTop: newIsTop
}
})
if (res.code === 200) {
//
row.isTop = newIsTop
this.$message.success(newIsTop === 'Y' ? '收藏成功' : '已取消收藏')
} else {
this.$message.error(res.msg || '操作失败')
}
} catch (err) {
this.$message.error(err?.message || '收藏操作失败')
}
} }
} }
} }
@ -2056,6 +2111,15 @@ export default {
cursor: not-allowed; cursor: not-allowed;
} }
.favorite-btn {
color: #ff9800 !important;
}
.favorite-btn.is-favorited {
color: #ffeb3b !important;
font-weight: 600;
}
.vg-chat-ai-top { .vg-chat-ai-top {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -0,0 +1,123 @@
package com.ruoyi.api;
import com.ruoyi.ai.domain.AiOrder;
import com.ruoyi.ai.service.IAiOrderService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 门户-生成资产管理展示当前用户的AI订单记录支持收藏功能
*/
@Api(tags = "门户-生成资产")
@RestController
@RequestMapping("/api/portal/assets")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class PortalAssetsController extends BaseController {
private final IAiOrderService aiOrderService;
/**
* 查询当前用户的生成资产列表
*/
@GetMapping("/list")
@ApiOperation("查询生成资产列表(支持收藏筛选和时间范围)")
public TableDataInfo list(
@RequestParam(required = false) String is_top,
@RequestParam(required = false) String beginTime,
@RequestParam(required = false) String endTime) {
AiOrder query = new AiOrder();
// 只查询当前用户的数据
query.setUserId(SecurityUtils.getAiUserId());
System.out.println("=== PortalAssetsController DEBUG START ===");
System.out.println("收到参数: is_top=" + is_top + ", beginTime=" + beginTime + ", endTime=" + endTime);
// 收藏状态筛选 - is_top: Y=已收藏, N=未收藏
if (is_top != null && !is_top.trim().isEmpty()) {
String trimmedIsTop = is_top.trim();
query.setIsTop(trimmedIsTop);
System.out.println("✓ 设置 isTop = [" + trimmedIsTop + "]");
System.out.println(" query.getIsTop() = " + query.getIsTop());
} else {
System.out.println("✗ is_top 参数为空或未提供,不设置筛选条件");
}
// 时间范围筛选
if (beginTime != null && !beginTime.isEmpty()) {
query.getParams().put("beginTime", beginTime);
System.out.println("✓ 设置 beginTime = " + beginTime);
}
if (endTime != null && !endTime.isEmpty()) {
query.getParams().put("endTime", endTime);
System.out.println("✓ 设置 endTime = " + endTime);
}
System.out.println("查询对象状态: isTop=" + query.getIsTop() + ", params=" + query.getParams());
System.out.println("=== PortalAssetsController DEBUG END ===");
startPage();
List<AiOrder> list = aiOrderService.selectAiOrderList(query);
System.out.println("PortalAssetsController - 查询返回 " + (list != null ? list.size() : 0) + " 条记录");
return getDataTable(list);
}
/**
* 收藏/取消收藏
*/
@PostMapping("/favorite")
@ApiOperation("收藏或取消收藏生成资产")
public AjaxResult favorite(@RequestBody FavoriteRequest request) {
Long userId = SecurityUtils.getAiUserId();
// 验证订单归属
AiOrder order = aiOrderService.selectAiOrderById(request.getId());
if (order == null) {
return AjaxResult.error("订单不存在");
}
if (!userId.equals(order.getUserId())) {
return AjaxResult.error("无权操作该订单");
}
// 更新收藏状态
AiOrder update = new AiOrder();
update.setId(request.getId());
update.setIsTop(request.getIsTop());
int result = aiOrderService.updateAiOrder(update);
return toAjax(result);
}
/**
* 收藏请求体
*/
public static class FavoriteRequest {
private Long id;
private String isTop;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getIsTop() {
return isTop;
}
public void setIsTop(String isTop) {
this.isTop = isTop;
}
}
}

View File

@ -43,11 +43,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="text != null and text != ''"> and ao.text like concat('%', #{text}, '%')</if> <if test="text != null and text != ''"> and ao.text like concat('%', #{text}, '%')</if>
<if test="userId != null "> and ao.user_id = #{userId}</if> <if test="userId != null "> and ao.user_id = #{userId}</if>
<if test="uuid != null "> and au.user_id = #{uuid}</if> <if test="uuid != null "> and au.user_id = #{uuid}</if>
<if test="isTop != null "> and ao.is_top = #{isTop}</if> <if test="isTop != null and isTop != ''"> and ao.is_top = #{isTop}</if>
<if test="type != null "> and ao.type = #{type}</if> <if test="type != null "> and ao.type = #{type}</if>
<if test="amount != null "> and ao.amount = #{amount}</if> <if test="amount != null "> and ao.amount = #{amount}</if>
<if test="result != null and result != ''"> and ao.result = #{result}</if> <if test="result != null and result != ''"> and ao.result = #{result}</if>
<if test="status != null "> and ao.status = #{status}</if> <if test="status != null "> and ao.status = #{status}</if>
<if test="extStatus != null "> and ao.ext_status = #{extStatus}</if>
<if test="source != null "> and ao.source = #{source}</if> <if test="source != null "> and ao.source = #{source}</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
AND date_format(ao.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d') AND date_format(ao.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')