Compare commits
6 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
abb8d279c4 | |
|
|
8959b775a4 | |
|
|
aed52b0437 | |
|
|
809b47215c | |
|
|
5a0edc320a | |
|
|
cb1fbdb3af |
|
|
@ -0,0 +1,27 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
|
unpackage
|
||||||
|
/dist/
|
||||||
|
/manager/dist/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
/test/unit/coverage/
|
||||||
|
/test/e2e/reports/
|
||||||
|
selenium-debug.log
|
||||||
|
nodejs/public/uploads
|
||||||
|
*.zip
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
package-lock.json
|
||||||
|
.history
|
||||||
|
/unpackage/resources/
|
||||||
|
/unpackage/dist/dev
|
||||||
|
/unpackage/dist/build/.automator
|
||||||
|
/unpackage/dist/build/app-plus
|
||||||
|
*.lock
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/python" />
|
||||||
|
<excludePattern pattern="*.zip" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ VUE_APP_TITLE = 管理系统
|
||||||
ENV = 'production'
|
ENV = 'production'
|
||||||
|
|
||||||
# 若依管理系统/生产环境
|
# 若依管理系统/生产环境
|
||||||
VUE_APP_BASE_API = 'https://admin-api.undressing.name'
|
VUE_APP_BASE_API = 'http://47.86.170.114:8011'
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ yarn-error.log*
|
||||||
tests/**/coverage/
|
tests/**/coverage/
|
||||||
tests/e2e/reports
|
tests/e2e/reports
|
||||||
selenium-debug.log
|
selenium-debug.log
|
||||||
|
*.zip
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.idea
|
.idea
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,5 @@ VITE_AMAP_KEY = 6888d721d30981b479ecdabd85f286fe
|
||||||
VITE_AMAP_SAFE_KEY = 23d3fdbc6b4eaf65d4a82a16510938c9
|
VITE_AMAP_SAFE_KEY = 23d3fdbc6b4eaf65d4a82a16510938c9
|
||||||
VITE_REGISTER_SOURCE = ALL
|
VITE_REGISTER_SOURCE = ALL
|
||||||
#VITE_REGISTER_SOURCE = XM001
|
#VITE_REGISTER_SOURCE = XM001
|
||||||
VITE_SHOW_PAY = SUCCESS
|
VITE_SHOW_PAY = SUCCESS
|
||||||
|
VITE_API_URL = http://47.86.170.114:8011/api
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
# 端口
|
# 端口
|
||||||
VITE_PORT = 8887
|
VITE_PORT = 8887
|
||||||
VITE_API_URL = https://api.undressing.name/api
|
VITE_API_URL = http://47.86.170.114:8011/api
|
||||||
|
|
@ -10,7 +10,7 @@ yarn-error.log*
|
||||||
/test/e2e/reports/
|
/test/e2e/reports/
|
||||||
selenium-debug.log
|
selenium-debug.log
|
||||||
nodejs/public/uploads
|
nodejs/public/uploads
|
||||||
|
*.zip
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -615,4 +615,4 @@
|
||||||
right: 15px
|
right: 15px
|
||||||
}
|
}
|
||||||
|
|
||||||
/*# sourceMappingURL=quill.core.css.map*/
|
/* Quill core styles - source map removed to fix build error */
|
||||||
|
|
@ -1286,4 +1286,4 @@
|
||||||
border: 1px solid #ccc
|
border: 1px solid #ccc
|
||||||
}
|
}
|
||||||
|
|
||||||
/*# sourceMappingURL=quill.snow.css.map*/
|
/* Quill snow styles - source map removed to fix build error */
|
||||||
|
|
@ -8,10 +8,6 @@
|
||||||
fullscreen
|
fullscreen
|
||||||
:class="`${prefixCls}-wrapper`"
|
:class="`${prefixCls}-wrapper`"
|
||||||
:modal-class="`${prefixCls}-dialog`">
|
:modal-class="`${prefixCls}-dialog`">
|
||||||
<mf-video
|
|
||||||
autoplay
|
|
||||||
:controls="false"
|
|
||||||
modelValue="https://images.iqyjsnwv.com/tmp/hello-BCSGJ8fP.mp4" />
|
|
||||||
<div :class="`${prefixCls}-shadow`">
|
<div :class="`${prefixCls}-shadow`">
|
||||||
<div :class="`${prefixCls}-title`">
|
<div :class="`${prefixCls}-title`">
|
||||||
{{ $t('common.fbTitle') }}
|
{{ $t('common.fbTitle') }}
|
||||||
|
|
|
||||||
|
|
@ -462,12 +462,11 @@ export default {
|
||||||
// 获取国家名称(根据当前语言)
|
// 获取国家名称(根据当前语言)
|
||||||
getCountryName(country) {
|
getCountryName(country) {
|
||||||
if (!country) return ''
|
if (!country) return ''
|
||||||
// 如果是英文,显示英文名称
|
// 繁体中文显示中文名称,其他语言显示英文名称
|
||||||
if (this.lang === 'en_US') {
|
if (this.lang === 'zh_HK') {
|
||||||
return country.nameEn || country.name
|
return country.name || country.nameEn || ''
|
||||||
}
|
}
|
||||||
// 默认显示中文名称
|
return country.nameEn || country.name || ''
|
||||||
return country.name
|
|
||||||
},
|
},
|
||||||
// 国家下拉框搜索过滤
|
// 国家下拉框搜索过滤
|
||||||
filterCountry(inputValue, option) {
|
filterCountry(inputValue, option) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,412 @@
|
||||||
|
<template>
|
||||||
|
<div class="rich-editor-root">
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="tool-btn" type="button" @click="execCommand('bold')"><b>B</b></button>
|
||||||
|
<button class="tool-btn" type="button" @click="execCommand('italic')"><i>I</i></button>
|
||||||
|
<button class="tool-btn" type="button" @click="openImagePicker">{{ $t('common.insertImage') || '插入图片' }}</button>
|
||||||
|
<button class="tool-btn" type="button" @click="clear">清空</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 隐藏的文件输入 -->
|
||||||
|
<input
|
||||||
|
ref="fileInputRef"
|
||||||
|
class="hidden-input"
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
multiple
|
||||||
|
@change="handleSelectFiles"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 编辑器区域 -->
|
||||||
|
<div
|
||||||
|
ref="editorRef"
|
||||||
|
class="user-input rich-editor"
|
||||||
|
contenteditable="true"
|
||||||
|
:data-placeholder="placeholder || '请输入文本...'"
|
||||||
|
@input="handleInput"
|
||||||
|
@keyup="handleKeyup"
|
||||||
|
@click="handleEditorClick"
|
||||||
|
@keydown="handleKeydown"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<!-- @ 图片选择面板 -->
|
||||||
|
<div v-if="mentionVisible" class="mention-panel">
|
||||||
|
<div
|
||||||
|
v-for="(item, idx) in mentionImageList"
|
||||||
|
:key="item.url"
|
||||||
|
class="mention-item"
|
||||||
|
@mousedown.prevent="insertMentionImage(item)"
|
||||||
|
>
|
||||||
|
<img :src="item.url" class="mention-thumb" />
|
||||||
|
<span class="mention-label">@图{{ idx + 1 }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="mentionImageList.length === 0" class="mention-empty">
|
||||||
|
{{ $t('common.noImageToMention') || '暂无可引用图片' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { nextTick, onMounted, ref, watch } from 'vue'
|
||||||
|
import { uploadFile } from '@/utils/file'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RichTextEditor',
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请输入文本生成视频...'
|
||||||
|
},
|
||||||
|
uploadedImages: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue', 'text-change', 'image-upload'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const editorRef = ref(null)
|
||||||
|
const fileInputRef = ref(null)
|
||||||
|
const savedSelectionRange = ref(null)
|
||||||
|
const mentionVisible = ref(false)
|
||||||
|
const mentionImageList = ref([])
|
||||||
|
const MAX_IMAGE_COUNT = 4
|
||||||
|
|
||||||
|
// 执行编辑器命令
|
||||||
|
const execCommand = (command) => {
|
||||||
|
if (!editorRef.value) return
|
||||||
|
editorRef.value.focus()
|
||||||
|
document.execCommand(command, false, null)
|
||||||
|
handleInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开图片选择器
|
||||||
|
const openImagePicker = () => {
|
||||||
|
if (fileInputRef.value) {
|
||||||
|
fileInputRef.value.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理文件选择 - 上传到服务器
|
||||||
|
const handleSelectFiles = async (event) => {
|
||||||
|
const files = event.target.files
|
||||||
|
if (!files.length) return
|
||||||
|
|
||||||
|
for (let file of files) {
|
||||||
|
if (file.type.startsWith('image/')) {
|
||||||
|
try {
|
||||||
|
// 显示上传中提示
|
||||||
|
const loading = window.$message ? window.$message.loading('上传中...') : null
|
||||||
|
|
||||||
|
// 上传到后端
|
||||||
|
const res = await uploadFile({
|
||||||
|
url: '/api/cos/upload', // 使用腾讯云COS上传接口
|
||||||
|
file: file,
|
||||||
|
name: 'file'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res && res.code === 200 && res.data) {
|
||||||
|
const imageUrl = typeof res.data === 'string' ? res.data : (res.data.url || res.data)
|
||||||
|
insertImage(imageUrl, file.name)
|
||||||
|
|
||||||
|
// 通知父组件有新图片上传成功(用于@功能)
|
||||||
|
emit('image-upload', { url: imageUrl, name: file.name })
|
||||||
|
} else {
|
||||||
|
console.error('上传失败:', res)
|
||||||
|
alert('图片上传失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('上传图片出错:', error)
|
||||||
|
alert('图片上传失败: ' + (error.message || '未知错误'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空input
|
||||||
|
event.target.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插入图片
|
||||||
|
const insertImage = (url, name = 'image') => {
|
||||||
|
if (!editorRef.value) return
|
||||||
|
|
||||||
|
editorRef.value.focus()
|
||||||
|
const selection = window.getSelection()
|
||||||
|
if (!selection) return
|
||||||
|
|
||||||
|
const img = document.createElement('img')
|
||||||
|
img.src = url
|
||||||
|
img.alt = name
|
||||||
|
img.style.maxWidth = '100%'
|
||||||
|
img.style.height = 'auto'
|
||||||
|
img.style.margin = '4px 0'
|
||||||
|
img.setAttribute('data-image-name', name)
|
||||||
|
|
||||||
|
const range = selection.getRangeAt(0)
|
||||||
|
range.deleteContents()
|
||||||
|
range.insertNode(img)
|
||||||
|
|
||||||
|
// 添加空格
|
||||||
|
const space = document.createTextNode(' ')
|
||||||
|
range.setStartAfter(img)
|
||||||
|
range.setEndAfter(img)
|
||||||
|
range.insertNode(space)
|
||||||
|
|
||||||
|
handleInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理输入
|
||||||
|
const handleInput = () => {
|
||||||
|
if (!editorRef.value) return
|
||||||
|
const content = editorRef.value.innerHTML
|
||||||
|
emit('update:modelValue', content)
|
||||||
|
emit('text-change', content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理按键
|
||||||
|
const handleKeyup = (e) => {
|
||||||
|
if (e.key === '@') {
|
||||||
|
showMentionPanel()
|
||||||
|
} else if (e.key === 'Escape' && mentionVisible.value) {
|
||||||
|
mentionVisible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKeydown = (e) => {
|
||||||
|
if (e.key === '@') {
|
||||||
|
// 保存当前选择位置
|
||||||
|
const selection = window.getSelection()
|
||||||
|
if (selection && selection.rangeCount > 0) {
|
||||||
|
savedSelectionRange.value = selection.getRangeAt(0).cloneRange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示 @ 面板
|
||||||
|
const showMentionPanel = () => {
|
||||||
|
if (props.uploadedImages && props.uploadedImages.length > 0) {
|
||||||
|
mentionImageList.value = props.uploadedImages.slice(0, MAX_IMAGE_COUNT)
|
||||||
|
mentionVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插入 @ 图片
|
||||||
|
const insertMentionImage = (imageItem) => {
|
||||||
|
if (!editorRef.value || !savedSelectionRange.value) {
|
||||||
|
mentionVisible.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
editorRef.value.focus()
|
||||||
|
|
||||||
|
const selection = window.getSelection()
|
||||||
|
if (selection) {
|
||||||
|
selection.removeAllRanges()
|
||||||
|
selection.addRange(savedSelectionRange.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const img = document.createElement('img')
|
||||||
|
img.src = imageItem.url
|
||||||
|
img.alt = '@图片'
|
||||||
|
img.style.maxWidth = '60px'
|
||||||
|
img.style.height = 'auto'
|
||||||
|
img.style.verticalAlign = 'middle'
|
||||||
|
img.setAttribute('data-mention', 'true')
|
||||||
|
|
||||||
|
const range = savedSelectionRange.value
|
||||||
|
range.deleteContents()
|
||||||
|
range.insertNode(img)
|
||||||
|
|
||||||
|
// 添加空格
|
||||||
|
const space = document.createTextNode(' ')
|
||||||
|
range.setStartAfter(img)
|
||||||
|
range.setEndAfter(img)
|
||||||
|
range.insertNode(space)
|
||||||
|
|
||||||
|
mentionVisible.value = false
|
||||||
|
handleInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空编辑器
|
||||||
|
const clear = () => {
|
||||||
|
if (!editorRef.value) return
|
||||||
|
editorRef.value.innerHTML = ''
|
||||||
|
handleInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理编辑器点击
|
||||||
|
const handleEditorClick = () => {
|
||||||
|
if (mentionVisible.value) {
|
||||||
|
mentionVisible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听 props 变化
|
||||||
|
watch(() => props.modelValue, (newValue) => {
|
||||||
|
if (editorRef.value && editorRef.value.innerHTML !== newValue) {
|
||||||
|
editorRef.value.innerHTML = newValue || ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听上传的图片列表更新
|
||||||
|
watch(() => props.uploadedImages, (newImages) => {
|
||||||
|
mentionImageList.value = newImages || []
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (editorRef.value) {
|
||||||
|
editorRef.value.innerHTML = props.modelValue || ''
|
||||||
|
|
||||||
|
// 添加粘贴事件处理
|
||||||
|
editorRef.value.addEventListener('paste', (e) => {
|
||||||
|
const items = e.clipboardData.items
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
if (items[i].type.indexOf('image') !== -1) {
|
||||||
|
const blob = items[i].getAsFile()
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = (event) => {
|
||||||
|
insertImage(event.target.result, 'pasted-image')
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(blob)
|
||||||
|
e.preventDefault()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
editorRef,
|
||||||
|
fileInputRef,
|
||||||
|
mentionVisible,
|
||||||
|
mentionImageList,
|
||||||
|
execCommand,
|
||||||
|
openImagePicker,
|
||||||
|
handleSelectFiles,
|
||||||
|
handleInput,
|
||||||
|
handleKeyup,
|
||||||
|
handleKeydown,
|
||||||
|
handleEditorClick,
|
||||||
|
insertMentionImage,
|
||||||
|
clear
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.rich-editor-root {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding: 8px;
|
||||||
|
background: #1a1b20;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
|
||||||
|
.tool-btn {
|
||||||
|
padding: 6px 12px;
|
||||||
|
background: rgba(255,255,255,0.1);
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-editor {
|
||||||
|
min-height: 120px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #1a1b20;
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&:empty:before {
|
||||||
|
content: attr(data-placeholder);
|
||||||
|
color: #666;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin: 4px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention-panel {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
background: #1f2128;
|
||||||
|
border: 1px solid #3a3d47;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
z-index: 1000;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 280px;
|
||||||
|
|
||||||
|
.mention-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #2a2d38;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention-thumb {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention-label {
|
||||||
|
color: #a1a4b3;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention-empty {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -237,11 +237,18 @@ export default {
|
||||||
{content}
|
{content}
|
||||||
</div></div>
|
</div></div>
|
||||||
}
|
}
|
||||||
{/* 图片预览区域 */}
|
{/* 图片预览区域 - 点击框体可替换 */}
|
||||||
{!this.$datas.isEmpty(mValue) && (
|
{!this.$datas.isEmpty(mValue) && (
|
||||||
<div class="mf-image-upload-draggable-preview">
|
<div class="mf-image-upload-draggable-preview">
|
||||||
{mValue.map(item => {
|
{mValue.map(item => {
|
||||||
return <div class="mf-image-upload-draggable-preview-item">
|
return <div
|
||||||
|
class="mf-image-upload-draggable-preview-item mf-image-upload-draggable-replace"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
if (!readonly && !this.disabled) this.triggerUpload()
|
||||||
|
}}
|
||||||
|
>
|
||||||
<a-image
|
<a-image
|
||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
|
|
@ -250,6 +257,16 @@ export default {
|
||||||
preview={true}
|
preview={true}
|
||||||
/>
|
/>
|
||||||
<div class="mf-image-upload-draggable-preview-mask">
|
<div class="mf-image-upload-draggable-preview-mask">
|
||||||
|
{!readonly && (
|
||||||
|
<mf-icon
|
||||||
|
value="icon-edit"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
this.triggerUpload()
|
||||||
|
}}
|
||||||
|
title={this.$t('common.replaceImage') || '替换'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{item.type !== '.pdf' && (
|
{item.type !== '.pdf' && (
|
||||||
<mf-icon
|
<mf-icon
|
||||||
value="icon-eye"
|
value="icon-eye"
|
||||||
|
|
@ -297,6 +314,52 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 触发文件选择(用于替换已上传图片)
|
||||||
|
triggerUpload() {
|
||||||
|
if (this.readonly || this.disabled) return
|
||||||
|
const mValue = this.getValue()
|
||||||
|
// draggable 且已有图片时,用专用替换 input,避免 a-upload 在 limit 满时不渲染 input
|
||||||
|
if (this.listType === 'draggable' && mValue.length > 0 && this.$refs.replaceInput) {
|
||||||
|
this.$refs.replaceInput.value = ''
|
||||||
|
this.$refs.replaceInput.click()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const el = this.$refs.mfUpload?.$el
|
||||||
|
const input = el?.querySelector?.('input[type="file"]')
|
||||||
|
if (input) {
|
||||||
|
input.value = ''
|
||||||
|
input.click()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 替换时选择文件后手动上传
|
||||||
|
handleReplaceFileChange(e) {
|
||||||
|
const file = e.target.files[0]
|
||||||
|
if (!file) return
|
||||||
|
if (file.size / 1024 / 1024 > 10) {
|
||||||
|
this.$message.error('文件大小不能超过10M')
|
||||||
|
e.target.value = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$emit('beforeUpload', file)
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
const action = import.meta.env.MODE === 'production' ? 'api/file/upload' : 'dev-api/api/file/upload'
|
||||||
|
const headers = { ...this.headers }
|
||||||
|
delete headers['Content-Type']
|
||||||
|
fetch(action, { method: 'POST', body: formData, headers })
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.code === 200 && data.url) {
|
||||||
|
const url = this.getImageUrl(data.url)
|
||||||
|
this.handleChange({ url, name: file.name })
|
||||||
|
this.$emit('success', data.url, file)
|
||||||
|
} else {
|
||||||
|
this.$message.error(data.msg || data.url || '上传失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => this.$message.error('上传失败'))
|
||||||
|
.finally(() => { e.target.value = '' })
|
||||||
|
},
|
||||||
handlePreview() {
|
handlePreview() {
|
||||||
let images = this.getValue().map((i) => i.url)
|
let images = this.getValue().map((i) => i.url)
|
||||||
this.$viewerApi({
|
this.$viewerApi({
|
||||||
|
|
@ -486,14 +549,26 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a-upload
|
<div class={`${prefixCls}-wrap`}>
|
||||||
ref="mfUpload"
|
{listType === 'draggable' && (
|
||||||
class={wrapCls}
|
<input
|
||||||
action={action}
|
ref="replaceInput"
|
||||||
name="file"
|
type="file"
|
||||||
headers={headers}
|
accept={accept}
|
||||||
{...imageUploadProps}
|
style={{ position: 'absolute', opacity: 0, width: 0, height: 0, pointerEvents: 'none' }}
|
||||||
v-slots={slots}></a-upload>
|
onChange={this.handleReplaceFileChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<a-upload
|
||||||
|
ref="mfUpload"
|
||||||
|
class={wrapCls}
|
||||||
|
action={action}
|
||||||
|
name="file"
|
||||||
|
headers={headers}
|
||||||
|
{...imageUploadProps}
|
||||||
|
v-slots={slots}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,9 @@
|
||||||
&-item {
|
&-item {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
&.mf-image-upload-draggable-replace {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&-mask {
|
&-mask {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
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: 'قد يتأخر الشحن؛ انتظر 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: 'إلغاء'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import route from './route'
|
||||||
|
import common from './common.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
route,
|
||||||
|
common
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
export default {
|
||||||
|
index: 'الرئيسية',
|
||||||
|
imageToImage: 'خلع بضغطة واحدة',
|
||||||
|
imageToImage2: 'صورة إلى صورة 2',
|
||||||
|
changeFace: 'تبديل الوجه',
|
||||||
|
changeFaceVideo: 'تبديل الوجه في الفيديو',
|
||||||
|
fastImage: 'إنشاء صورة',
|
||||||
|
fastVideo: 'إنشاء فيديو',
|
||||||
|
recharge: 'شحن سريع',
|
||||||
|
help: 'مركز المساعدة',
|
||||||
|
moneyInvite: 'دعوة مكافآت'
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ export default {
|
||||||
uploadTemplate: 'Click to upload custom template',
|
uploadTemplate: 'Click to upload custom template',
|
||||||
textPlaceholder: 'Describe the image you want to generate',
|
textPlaceholder: 'Describe the image you want to generate',
|
||||||
uploadImageError: 'Please upload an image',
|
uploadImageError: 'Please upload an image',
|
||||||
|
replaceImage: 'Replace image',
|
||||||
textError: 'Please enter a prompt',
|
textError: 'Please enter a prompt',
|
||||||
textVideoPlaceholder: "Describe the video you want to generate",
|
textVideoPlaceholder: "Describe the video you want to generate",
|
||||||
uploadFirstPlaceholder: 'Click to upload first frame',
|
uploadFirstPlaceholder: 'Click to upload first frame',
|
||||||
|
|
@ -142,6 +143,14 @@ export default {
|
||||||
cardNoRequired: 'Please enter card number',
|
cardNoRequired: 'Please enter card number',
|
||||||
cardNameRequired: 'Please enter cardholder name',
|
cardNameRequired: 'Please enter cardholder name',
|
||||||
rechargeFailed: 'Recharge failed',
|
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支付相关
|
// VM支付相关
|
||||||
vmCardInfo: 'Credit Card Information',
|
vmCardInfo: 'Credit Card Information',
|
||||||
cardNumber: 'Card Number',
|
cardNumber: 'Card Number',
|
||||||
|
|
@ -174,5 +183,9 @@ export default {
|
||||||
country: 'Country',
|
country: 'Country',
|
||||||
countryPlaceholder: 'Select country',
|
countryPlaceholder: 'Select country',
|
||||||
countryRequired: 'Please select country',
|
countryRequired: 'Please select country',
|
||||||
submit: 'Submit'
|
videoGen: 'Video Generation',
|
||||||
|
uploadFirstImage: 'Upload Reference Image',
|
||||||
|
insertImage: 'Insert Image',
|
||||||
|
submit: 'Submit',
|
||||||
|
cancel: 'Cancel'
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
export default {
|
export default {
|
||||||
index: 'Home',
|
index: 'Home',
|
||||||
imageToImage: 'One-click undressing',
|
imageToImage: 'Image-to-Image 1',
|
||||||
imageToImage2: 'Image-to-Image2',
|
imageToImage2: 'Image-to-Image2',
|
||||||
changeFace: 'Swap Face',
|
changeFace: 'Swap Face',
|
||||||
changeFaceVideo: 'Swap Video Face',
|
changeFaceVideo: 'Swap Video Face',
|
||||||
fastImage: 'Gen Image',
|
fastImage: 'Gen Image',
|
||||||
fastVideo: 'Gen Video',
|
fastVideo: 'Gen Video',
|
||||||
|
videoGen: 'Video Generation',
|
||||||
recharge: 'Quick Recharge',
|
recharge: 'Quick Recharge',
|
||||||
help: 'Help Center',
|
help: 'Help Center',
|
||||||
moneyInvite: 'Reward Invitation'
|
moneyInvite: 'Reward Invitation'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import route from './route'
|
||||||
|
import common from './common.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
route,
|
||||||
|
common
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
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 3–5 min avant d’actualiser.',
|
||||||
|
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 l’identifiant',
|
||||||
|
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 l’identifiant',
|
||||||
|
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 qu’il 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 l’ancien mot de passe',
|
||||||
|
newpasswordPlaceholder: 'Saisissez le nouveau mot de passe',
|
||||||
|
switchPageTip: 'Une vidéo est en cours de génération ; consultez l’historique 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é n’a pas de limite de durée.',
|
||||||
|
rechargeNotice4: 'Vérifiez l’offre avant d’acheter ; 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 n’est pas inscrit',
|
||||||
|
gearNotExists: 'L’offre choisie n’existe 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 l’année (ex. 2027)',
|
||||||
|
expYearRequired: 'Saisissez l’année d\'expiration',
|
||||||
|
expYearInvalid: 'L’année doit faire 4 chiffres',
|
||||||
|
expMonth: 'Mois d\'expiration',
|
||||||
|
expMonthPlaceholder: 'Choisissez le mois',
|
||||||
|
expMonthRequired: 'Choisissez le mois',
|
||||||
|
email: 'Email',
|
||||||
|
emailPlaceholder: 'Saisissez l’email',
|
||||||
|
emailRequired: 'Saisissez l’email',
|
||||||
|
emailInvalid: 'Format d’email invalide',
|
||||||
|
emailMaxLength: 'L’email 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'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import route from './route'
|
||||||
|
import common from './common.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
route,
|
||||||
|
common
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
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: 'रद्द करें'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import route from './route'
|
||||||
|
import common from './common.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
route,
|
||||||
|
common
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
export default {
|
||||||
|
index: 'होम',
|
||||||
|
imageToImage: 'एक क्लिक में कपड़े उतारें',
|
||||||
|
imageToImage2: 'इमेज टू इमेज 2',
|
||||||
|
changeFace: 'चेहरा बदलें',
|
||||||
|
changeFaceVideo: 'वीडियो में चेहरा बदलें',
|
||||||
|
fastImage: 'छवि जनरेट करें',
|
||||||
|
fastVideo: 'वीडियो जनरेट करें',
|
||||||
|
recharge: 'त्वरित रिचार्ज',
|
||||||
|
help: 'सहायता केंद्र',
|
||||||
|
moneyInvite: 'इनाम निमंत्रण'
|
||||||
|
}
|
||||||
|
|
@ -2,16 +2,40 @@ import { createI18n } from 'vue-i18n'
|
||||||
import Cookies from 'js-cookie'
|
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 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 = Cookies.get('language') || 'en_US'
|
let locale = Cookies.get('language') || 'en_US'
|
||||||
|
|
||||||
|
/** 各语言在界面上的显示名称 */
|
||||||
|
export const LOCALE_NAMES = {
|
||||||
|
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
|
en_US,
|
||||||
}
|
es_ES,
|
||||||
|
pt_BR,
|
||||||
|
hi_IN,
|
||||||
|
ru_RU,
|
||||||
|
ar_SA,
|
||||||
|
fr_FR
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default i18n
|
export default i18n
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import route from './route'
|
||||||
|
import common from './common.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
route,
|
||||||
|
common
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
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: 'Зачисление может задерживаться; подождите 3–5 минут и обновите.',
|
||||||
|
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: 'Номер карты должен быть из 13–19 цифр',
|
||||||
|
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: 'Отмена'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import route from './route'
|
||||||
|
import common from './common.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
route,
|
||||||
|
common
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
export default {
|
||||||
|
index: 'Главная',
|
||||||
|
imageToImage: 'Раздеть в один клик',
|
||||||
|
imageToImage2: 'Изображение в изображение 2',
|
||||||
|
changeFace: 'Заменить лицо',
|
||||||
|
changeFaceVideo: 'Заменить лицо в видео',
|
||||||
|
fastImage: 'Создать изображение',
|
||||||
|
fastVideo: 'Создать видео',
|
||||||
|
recharge: 'Быстрая пополнение',
|
||||||
|
help: 'Центр помощи',
|
||||||
|
moneyInvite: 'Приглашение с наградой'
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,7 @@ export default {
|
||||||
uploadTemplate: '點擊上傳自定義模板',
|
uploadTemplate: '點擊上傳自定義模板',
|
||||||
textPlaceholder: '請描述你想生成的圖片',
|
textPlaceholder: '請描述你想生成的圖片',
|
||||||
uploadImageError: '請上傳圖片',
|
uploadImageError: '請上傳圖片',
|
||||||
|
replaceImage: '替換圖片',
|
||||||
uploadFaceImageError: '請上傳人臉圖片',
|
uploadFaceImageError: '請上傳人臉圖片',
|
||||||
uploadTemplateError: '請上傳自定義模板',
|
uploadTemplateError: '請上傳自定義模板',
|
||||||
textError: '請輸入提示詞',
|
textError: '請輸入提示詞',
|
||||||
|
|
@ -144,6 +145,14 @@ export default {
|
||||||
cardNoRequired: '請輸入銀行卡號',
|
cardNoRequired: '請輸入銀行卡號',
|
||||||
cardNameRequired: '請輸入銀行卡姓名',
|
cardNameRequired: '請輸入銀行卡姓名',
|
||||||
rechargeFailed: '充值失敗',
|
rechargeFailed: '充值失敗',
|
||||||
|
// 模型选择
|
||||||
|
selectModel: '選擇模型',
|
||||||
|
seedance20: 'Seedance 2.0',
|
||||||
|
seedance20Fast: 'Seedance 2.0 Fast',
|
||||||
|
// 富文本编辑器
|
||||||
|
insertImage: '插入圖片',
|
||||||
|
mentionImage: '引用圖片',
|
||||||
|
noImageToMention: '暫無可引用的圖片',
|
||||||
// VM支付相關
|
// VM支付相關
|
||||||
vmCardInfo: '信用卡信息',
|
vmCardInfo: '信用卡信息',
|
||||||
cardNumber: '信用卡卡號',
|
cardNumber: '信用卡卡號',
|
||||||
|
|
@ -177,6 +186,9 @@ export default {
|
||||||
country: '國家',
|
country: '國家',
|
||||||
countryPlaceholder: '請選擇國家',
|
countryPlaceholder: '請選擇國家',
|
||||||
countryRequired: '請選擇國家',
|
countryRequired: '請選擇國家',
|
||||||
|
videoGen: '視頻生成',
|
||||||
|
uploadFirstImage: '上傳首圖',
|
||||||
|
insertImage: '插入圖片',
|
||||||
submit: '提交',
|
submit: '提交',
|
||||||
cancel: '取消'
|
cancel: '取消'
|
||||||
}
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
changeFaceVideo: '視頻換臉',
|
changeFaceVideo: '視頻換臉',
|
||||||
fastImage: '快捷生圖',
|
fastImage: '快捷生圖',
|
||||||
fastVideo: '快捷生視頻',
|
fastVideo: '快捷生視頻',
|
||||||
|
videoGen: '視頻生成',
|
||||||
recharge: '快速充值',
|
recharge: '快速充值',
|
||||||
help: '幫助中心',
|
help: '幫助中心',
|
||||||
moneyInvite: '有獎邀請'
|
moneyInvite: '有獎邀請'
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,12 @@
|
||||||
:title="$t('common.moneyInvite')">
|
:title="$t('common.moneyInvite')">
|
||||||
<Money />
|
<Money />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane
|
<!-- 隐藏快速充值入口 -->
|
||||||
|
<!-- <a-tab-pane
|
||||||
key="recharge"
|
key="recharge"
|
||||||
:title="$t('common.rechargeRecord')">
|
:title="$t('common.rechargeRecord')">
|
||||||
<RechargeRecord />
|
<RechargeRecord />
|
||||||
</a-tab-pane>
|
</a-tab-pane> -->
|
||||||
<a-tab-pane
|
<a-tab-pane
|
||||||
key="resume"
|
key="resume"
|
||||||
:title="$t('common.myProduct')">
|
:title="$t('common.myProduct')">
|
||||||
|
|
@ -67,7 +68,6 @@ import { mapGetters } from 'vuex'
|
||||||
import i18n from '@/lang/i18n'
|
import i18n from '@/lang/i18n'
|
||||||
import Money from './Money.vue'
|
import Money from './Money.vue'
|
||||||
import UserAccount from './UserAccount.vue'
|
import UserAccount from './UserAccount.vue'
|
||||||
import RechargeRecord from './RechargeRecord.vue'
|
|
||||||
import ResumeRecord from './ResumeRecord.vue'
|
import ResumeRecord from './ResumeRecord.vue'
|
||||||
import RewardRecord from './RewardRecord.vue'
|
import RewardRecord from './RewardRecord.vue'
|
||||||
|
|
||||||
|
|
@ -93,7 +93,6 @@ export default {
|
||||||
Register,
|
Register,
|
||||||
Money,
|
Money,
|
||||||
UserAccount,
|
UserAccount,
|
||||||
RechargeRecord,
|
|
||||||
ResumeRecord,
|
ResumeRecord,
|
||||||
RewardRecord
|
RewardRecord
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,16 @@
|
||||||
<div class="right-menu-item language">
|
<div class="right-menu-item language">
|
||||||
<a-dropdown @select="handleSelect">
|
<a-dropdown @select="handleSelect">
|
||||||
<mf-button type="text">
|
<mf-button type="text">
|
||||||
{{ lang == 'zh_HK' ? '繁体中文' : 'English' }}
|
{{ localeName }}
|
||||||
<icon-down />
|
<icon-down />
|
||||||
</mf-button>
|
</mf-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<a-doption value="zh_HK">繁体中文</a-doption>
|
<a-doption
|
||||||
<a-doption value="en_US">English</a-doption>
|
v-for="(name, code) in localeNames"
|
||||||
|
:key="code"
|
||||||
|
:value="code">
|
||||||
|
{{ name }}
|
||||||
|
</a-doption>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -93,7 +97,7 @@ import { mapGetters, mapState } from 'vuex'
|
||||||
import cloneDeep from 'lodash-es/cloneDeep'
|
import cloneDeep from 'lodash-es/cloneDeep'
|
||||||
import { constantRoutes } from '@/router/index.js'
|
import { constantRoutes } from '@/router/index.js'
|
||||||
import Login from './Login.vue'
|
import Login from './Login.vue'
|
||||||
import i18n from '@/lang/i18n'
|
import i18n, { LOCALE_NAMES } from '@/lang/i18n'
|
||||||
import User from './User.vue'
|
import User from './User.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -122,6 +126,12 @@ export default {
|
||||||
demoEnv() {
|
demoEnv() {
|
||||||
return import.meta.env.MODE === 'demo'
|
return import.meta.env.MODE === 'demo'
|
||||||
},
|
},
|
||||||
|
localeNames() {
|
||||||
|
return LOCALE_NAMES
|
||||||
|
},
|
||||||
|
localeName() {
|
||||||
|
return LOCALE_NAMES[this.lang] || 'English'
|
||||||
|
},
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
'theme',
|
'theme',
|
||||||
'permission_routes',
|
'permission_routes',
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@ import router from './router'
|
||||||
|
|
||||||
import '@/assets/styles/base.less'
|
import '@/assets/styles/base.less'
|
||||||
|
|
||||||
import '@/assets/styles/quill.bubble.css'
|
// Quill 样式 - 临时注释避免 source map 文件缺失导致的构建错误
|
||||||
import '@/assets/styles/quill.core.css'
|
// 如果后续需要使用 Quill 富文本编辑器,可取消注释
|
||||||
import '@/assets/styles/quill.snow.css'
|
// import '@/assets/styles/quill.bubble.css'
|
||||||
|
// import '@/assets/styles/quill.core.css'
|
||||||
|
// import '@/assets/styles/quill.snow.css'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
|
|
|
||||||
|
|
@ -69,49 +69,47 @@ export const constantRoutes = [{
|
||||||
permission: "pass",
|
permission: "pass",
|
||||||
icon: 'btn_tst'
|
icon: 'btn_tst'
|
||||||
}
|
}
|
||||||
},
|
}, {
|
||||||
// {
|
path: 'image-to-image?type=2',
|
||||||
// path: 'image-to-image?type=2',
|
name: 'image-to-image2',
|
||||||
// name: 'image-to-image2',
|
component: () => import('@/views/Image.vue'),
|
||||||
// component: () => import('@/views/Image.vue'),
|
meta: {
|
||||||
// meta: {
|
title: 'imageToImage2',
|
||||||
// title: 'imageToImage2',
|
menuItem: true,
|
||||||
// menuItem: true,
|
permission: "pass",
|
||||||
// permission: "pass",
|
icon: 'btn_tst'
|
||||||
// icon: 'btn_tst'
|
}
|
||||||
// }
|
}, {
|
||||||
// }, {
|
path: 'change-face',
|
||||||
// path: 'change-face',
|
name: 'change-face',
|
||||||
// name: 'change-face',
|
component: () => import('@/views/ChangeFace.vue'),
|
||||||
// component: () => import('@/views/ChangeFace.vue'),
|
meta: {
|
||||||
// meta: {
|
title: 'changeFace',
|
||||||
// title: 'changeFace',
|
menuItem: true,
|
||||||
// menuItem: true,
|
permission: "pass",
|
||||||
// permission: "pass",
|
icon: 'btn_yjhl'
|
||||||
// icon: 'btn_yjhl'
|
}
|
||||||
// }
|
}, {
|
||||||
// }, {
|
path: 'change-face-video',
|
||||||
// path: 'change-face-video',
|
name: 'change-face-video',
|
||||||
// name: 'change-face-video',
|
component: () => import('@/views/ChangeFace.vue'),
|
||||||
// component: () => import('@/views/ChangeFace.vue'),
|
meta: {
|
||||||
// meta: {
|
title: 'changeFaceVideo',
|
||||||
// title: 'changeFaceVideo',
|
menuItem: true,
|
||||||
// menuItem: true,
|
permission: "pass",
|
||||||
// permission: "pass",
|
icon: 'btn_yjhl'
|
||||||
// icon: 'btn_yjhl'
|
}
|
||||||
// }
|
}, {
|
||||||
// }, {
|
path: 'fast-image',
|
||||||
// path: 'fast-image',
|
name: 'fast-image',
|
||||||
// name: 'fast-image',
|
component: () => import('@/views/FastImage.vue'),
|
||||||
// component: () => import('@/views/FastImage.vue'),
|
meta: {
|
||||||
// meta: {
|
title: 'fastImage',
|
||||||
// title: 'fastImage',
|
menuItem: true,
|
||||||
// menuItem: true,
|
permission: "pass",
|
||||||
// permission: "pass",
|
icon: 'btn_kjst'
|
||||||
// icon: 'btn_kjst'
|
}
|
||||||
// }
|
}, {
|
||||||
// },
|
|
||||||
{
|
|
||||||
path: 'fast-video',
|
path: 'fast-video',
|
||||||
name: 'fast-video',
|
name: 'fast-video',
|
||||||
component: () => import('@/views/FastVideo.vue'),
|
component: () => import('@/views/FastVideo.vue'),
|
||||||
|
|
@ -121,6 +119,16 @@ export const constantRoutes = [{
|
||||||
permission: "pass",
|
permission: "pass",
|
||||||
icon: 'btn_kjsp'
|
icon: 'btn_kjsp'
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
path: 'video-gen',
|
||||||
|
name: 'video-gen',
|
||||||
|
component: () => import('@/views/VideoGen.vue'),
|
||||||
|
meta: {
|
||||||
|
title: 'videoGen',
|
||||||
|
menuItem: true,
|
||||||
|
permission: "pass",
|
||||||
|
icon: 'btn_video'
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
path: 'recharge',
|
path: 'recharge',
|
||||||
name: 'recharge',
|
name: 'recharge',
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ const messages = i18n.global.messages[lang];
|
||||||
* @desc 创建axios实例
|
* @desc 创建axios实例
|
||||||
*/
|
*/
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
// baseURL: process.env.VUE_APP_BASE_API,
|
baseURL: process.env.VUE_APP_BASE_API,
|
||||||
withCredentials: import.meta.env.MODE === 'development',
|
withCredentials: import.meta.env.MODE === 'development',
|
||||||
timeout: 600 * 1000
|
timeout: 600 * 1000
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,28 @@
|
||||||
</a-radio>
|
</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="text">
|
<!-- 模型选择 -->
|
||||||
<a-textarea
|
<div class="model-select">
|
||||||
v-model="text"
|
<div class="model-title">{{ $t('common.selectModel') || '选择模型' }}</div>
|
||||||
:placeholder="$t('common.textVideoPlaceholder')" />
|
<a-select v-model="selectedModel" style="width: 100%;">
|
||||||
</div> -->
|
<a-option
|
||||||
|
v-for="option in modelOptions"
|
||||||
|
:key="option.value"
|
||||||
|
:value="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 富文本编辑器 -->
|
||||||
|
<RichTextEditor
|
||||||
|
v-model="editorContent"
|
||||||
|
:placeholder="$t('common.textVideoPlaceholder') || '请输入文本生成视频...'"
|
||||||
|
:uploaded-images="uploadedImages"
|
||||||
|
@text-change="handleTextChange"
|
||||||
|
@image-upload="handleImageUpload"
|
||||||
|
/>
|
||||||
|
|
||||||
<mf-button
|
<mf-button
|
||||||
class="submit"
|
class="submit"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|
@ -175,6 +192,8 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import RichTextEditor from '@/components/RichTextEditor.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -182,6 +201,7 @@ export default {
|
||||||
firstUrl: '',
|
firstUrl: '',
|
||||||
lastUrl: '',
|
lastUrl: '',
|
||||||
text: '',
|
text: '',
|
||||||
|
editorContent: '',
|
||||||
interval: null,
|
interval: null,
|
||||||
videoUrl: null,
|
videoUrl: null,
|
||||||
videoLoading: false,
|
videoLoading: false,
|
||||||
|
|
@ -202,9 +222,20 @@ export default {
|
||||||
templateLoading: false,
|
templateLoading: false,
|
||||||
selectedTemplate: null,
|
selectedTemplate: null,
|
||||||
selectedTemplatePreview: '', // 选中的模版预览图URL
|
selectedTemplatePreview: '', // 选中的模版预览图URL
|
||||||
maxPollAttempts: 40 // 最大轮询次数(3秒 * 40 = 120秒超时)
|
maxPollAttempts: 40, // 最大轮询次数(3秒 * 40 = 120秒超时)
|
||||||
|
// 模型选择
|
||||||
|
modelOptions: [
|
||||||
|
{ label: 'Seedance 2.0', value: 'ep-20260326165811-dlkth' },
|
||||||
|
{ label: 'Seedance 2.0 Fast', value: 'ep-20260326170056-dkj9m' }
|
||||||
|
],
|
||||||
|
selectedModel: 'ep-20260326165811-dlkth',
|
||||||
|
// 已上传的图片列表(用于@功能)
|
||||||
|
uploadedImages: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
RichTextEditor
|
||||||
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
this.destroyInterval()
|
this.destroyInterval()
|
||||||
},
|
},
|
||||||
|
|
@ -313,16 +344,28 @@ export default {
|
||||||
// 获取模板名称(根据当前语言)
|
// 获取模板名称(根据当前语言)
|
||||||
getTemplateName(template) {
|
getTemplateName(template) {
|
||||||
if (!template) return ''
|
if (!template) return ''
|
||||||
// 如果是中文繁体,显示 chineseContent
|
// 繁体中文显示 chineseContent,其他语言显示 englishContent
|
||||||
if (this.lang === 'zh_HK') {
|
if (this.lang === 'zh_HK') {
|
||||||
return template.chineseContent || template.name || ''
|
return template.chineseContent || template.name || ''
|
||||||
|
}else{
|
||||||
|
return template.englishContent || template.name || ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 处理富文本内容变化
|
||||||
|
handleTextChange(content) {
|
||||||
|
this.editorContent = content
|
||||||
|
// 同步到旧的 text 字段以保持兼容性
|
||||||
|
this.text = content
|
||||||
|
},
|
||||||
|
// 处理上传的图片(用于@功能)
|
||||||
|
handleImageUpload(imageInfo) {
|
||||||
|
if (imageInfo && imageInfo.url) {
|
||||||
|
this.uploadedImages.push({
|
||||||
|
url: imageInfo.url,
|
||||||
|
name: imageInfo.name || 'image'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
// 如果是英文,显示 englishContent
|
|
||||||
else if (this.lang === 'en_US') {
|
|
||||||
return template.englishContent || template.name || ''
|
|
||||||
}
|
|
||||||
// 默认返回 name
|
|
||||||
return template.name || ''
|
|
||||||
},
|
},
|
||||||
// 确认选择模板
|
// 确认选择模板
|
||||||
handleConfirmTemplate() {
|
handleConfirmTemplate() {
|
||||||
|
|
@ -482,9 +525,10 @@ export default {
|
||||||
|
|
||||||
this.generateLoading = true
|
this.generateLoading = true
|
||||||
let params = {
|
let params = {
|
||||||
text: this.text,
|
text: this.editorContent || this.text,
|
||||||
firstUrl: firstImageUrl,
|
firstUrl: firstImageUrl,
|
||||||
functionType: '21'
|
functionType: '21',
|
||||||
|
model: this.selectedModel // 新增模型参数
|
||||||
}
|
}
|
||||||
if (this.lastUrl && this.lastUrl.url) {
|
if (this.lastUrl && this.lastUrl.url) {
|
||||||
params.lastUrl = this.lastUrl.url
|
params.lastUrl = this.lastUrl.url
|
||||||
|
|
@ -606,8 +650,76 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload {
|
.upload {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.model-select {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.model-title {
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-select) {
|
||||||
|
background-color: #1a1b20;
|
||||||
|
border-color: rgba(255,255,255,0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 富文本编辑器样式 */
|
||||||
|
.rich-editor-root {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 8px;
|
||||||
|
background-color: #1a1b20;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
|
||||||
|
.tool-btn {
|
||||||
|
padding: 6px 12px;
|
||||||
|
background-color: rgba(255,255,255,0.05);
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255,255,255,0.1);
|
||||||
|
border-color: rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-editor {
|
||||||
|
min-height: 160px;
|
||||||
|
padding: 16px;
|
||||||
|
background-color: #1a1b20;
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
outline: none;
|
||||||
|
resize: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: rgb(var(--primary-6));
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-placeholder]:empty::before {
|
||||||
|
content: attr(data-placeholder);
|
||||||
|
color: #5c5d68;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
&-title {
|
&-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -61,13 +61,28 @@
|
||||||
</a-radio>
|
</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div
|
<!-- 模型选择 -->
|
||||||
class="text"
|
<div class="model-select">
|
||||||
v-if="current == 2">
|
<div class="model-title">{{ $t('common.selectModel') || '选择模型' }}</div>
|
||||||
<a-textarea
|
<a-select v-model="selectedModel" style="width: 100%;">
|
||||||
v-model="text"
|
<a-option
|
||||||
:placeholder="$t('common.textPlaceholder')" />
|
v-for="option in modelOptions"
|
||||||
</div> -->
|
:key="option.value"
|
||||||
|
:value="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 富文本编辑器 -->
|
||||||
|
<RichTextEditor
|
||||||
|
v-model="editorContent"
|
||||||
|
:placeholder="$t('common.textPlaceholder') || '请输入文本生成图片...'"
|
||||||
|
:uploaded-images="uploadedImages"
|
||||||
|
@text-change="handleTextChange"
|
||||||
|
@image-upload="handleImageUpload"
|
||||||
|
/>
|
||||||
|
|
||||||
<mf-button
|
<mf-button
|
||||||
class="submit"
|
class="submit"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|
@ -157,6 +172,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import RichTextEditor from '@/components/RichTextEditor.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -179,9 +195,20 @@ export default {
|
||||||
templateList: [],
|
templateList: [],
|
||||||
templateLoading: false,
|
templateLoading: false,
|
||||||
selectedTemplate: null,
|
selectedTemplate: null,
|
||||||
selectedTemplatePreview: '' // 选中的模版预览图URL
|
selectedTemplatePreview: '', // 选中的模版预览图URL
|
||||||
|
// 模型选择 - 支持 Seedance 2.0
|
||||||
|
modelOptions: [
|
||||||
|
{ label: 'Seedance 2.0', value: 'ep-20260326165811-dlkth' },
|
||||||
|
{ label: 'Seedance 2.0 Fast', value: 'ep-20260326170056-dkj9m' }
|
||||||
|
],
|
||||||
|
selectedModel: 'ep-20260326165811-dlkth',
|
||||||
|
// 已上传的图片列表(用于@功能)
|
||||||
|
uploadedImages: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
RichTextEditor
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['lang']),
|
...mapGetters(['lang']),
|
||||||
},
|
},
|
||||||
|
|
@ -311,16 +338,11 @@ export default {
|
||||||
// 获取模板名称(根据当前语言)
|
// 获取模板名称(根据当前语言)
|
||||||
getTemplateName(template) {
|
getTemplateName(template) {
|
||||||
if (!template) return ''
|
if (!template) return ''
|
||||||
// 如果是中文繁体,显示 chineseContent
|
// 繁体中文显示 chineseContent,其他语言显示 englishContent
|
||||||
if (this.lang === 'zh_HK') {
|
if (this.lang === 'zh_HK') {
|
||||||
return template.chineseContent || template.name || ''
|
return template.chineseContent || template.name || ''
|
||||||
}
|
}
|
||||||
// 如果是英文,显示 englishContent
|
return template.englishContent || template.name || ''
|
||||||
else if (this.lang === 'en_US') {
|
|
||||||
return template.englishContent || template.name || ''
|
|
||||||
}
|
|
||||||
// 默认返回 name
|
|
||||||
return template.name || ''
|
|
||||||
},
|
},
|
||||||
// 确认选择模板
|
// 确认选择模板
|
||||||
handleConfirmTemplate() {
|
handleConfirmTemplate() {
|
||||||
|
|
@ -338,6 +360,21 @@ export default {
|
||||||
this.templateDialogVisible = false
|
this.templateDialogVisible = false
|
||||||
// 不重置selectedTemplate,保持用户的选择状态,方便二次选择
|
// 不重置selectedTemplate,保持用户的选择状态,方便二次选择
|
||||||
},
|
},
|
||||||
|
// 处理富文本内容变化
|
||||||
|
handleTextChange(content) {
|
||||||
|
this.editorContent = content
|
||||||
|
// 同步到旧的 text 字段以保持兼容性
|
||||||
|
this.text = content
|
||||||
|
},
|
||||||
|
// 处理上传的图片(用于@功能)
|
||||||
|
handleImageUpload(imageInfo) {
|
||||||
|
if (imageInfo && imageInfo.url) {
|
||||||
|
this.uploadedImages.push({
|
||||||
|
url: imageInfo.url,
|
||||||
|
name: imageInfo.name || 'image'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
close() {
|
close() {
|
||||||
this.showResult = false
|
this.showResult = false
|
||||||
},
|
},
|
||||||
|
|
@ -401,10 +438,11 @@ export default {
|
||||||
url: 'api/ai/imgToImg',
|
url: 'api/ai/imgToImg',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
text: this.text,
|
text: this.editorContent || this.text,
|
||||||
firstUrl: this.firstUrl.url,
|
firstUrl: this.firstUrl.url,
|
||||||
functionType: this.current == 1 ? '11' : '12',
|
functionType: this.current == 1 ? '11' : '12',
|
||||||
tags: tags.join(',')
|
tags: tags.join(','),
|
||||||
|
model: this.selectedModel // 新增模型参数
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
|
@ -498,9 +536,77 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload {
|
.upload {
|
||||||
// margin-top: 20px;
|
// margin-top: 20px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.model-select {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.model-title {
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-select) {
|
||||||
|
background-color: #1a1b20;
|
||||||
|
border-color: rgba(255,255,255,0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 富文本编辑器样式 */
|
||||||
|
.rich-editor-root {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 8px;
|
||||||
|
background-color: #1a1b20;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
|
||||||
|
.tool-btn {
|
||||||
|
padding: 6px 12px;
|
||||||
|
background-color: rgba(255,255,255,0.05);
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(255,255,255,0.1);
|
||||||
|
border-color: rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-editor {
|
||||||
|
min-height: 160px;
|
||||||
|
padding: 16px;
|
||||||
|
background-color: #1a1b20;
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
outline: none;
|
||||||
|
resize: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: rgb(var(--primary-6));
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-placeholder]:empty::before {
|
||||||
|
content: attr(data-placeholder);
|
||||||
|
color: #5c5d68;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
&-title {
|
&-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,436 @@
|
||||||
|
<template>
|
||||||
|
<div :class="prefixCls">
|
||||||
|
<div class="left">
|
||||||
|
<!-- 模式切换 -->
|
||||||
|
<div class="mode-tabs">
|
||||||
|
<button
|
||||||
|
:class="['mode-tab', { active: mode === 'text-to-video' }]"
|
||||||
|
@click="switchMode('text-to-video')">
|
||||||
|
文生视频
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:class="['mode-tab', { active: mode === 'image-to-video' }]"
|
||||||
|
@click="switchMode('image-to-video')">
|
||||||
|
图生视频
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 上传区域 - 根据模式显示 -->
|
||||||
|
<div class="upload" v-if="mode === 'image-to-video'">
|
||||||
|
<div class="upload-title">
|
||||||
|
<div class="upload-title-left">
|
||||||
|
{{ $t('common.uploadFirstImage') || '上传参考图' }}
|
||||||
|
</div>
|
||||||
|
<div class="upload-title-tip">
|
||||||
|
{{ $t('common.uploadImageTip') || '支持PNG/JPG,最大10MB' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mf-image-upload
|
||||||
|
listType="picture-card"
|
||||||
|
uploadHeight="180px"
|
||||||
|
:show-file-list="false"
|
||||||
|
:content="$t('common.uploadFirstPlaceholder') || '点击上传参考图片'"
|
||||||
|
v-model="firstUrl" />
|
||||||
|
|
||||||
|
<!-- 可选:上传尾帧 -->
|
||||||
|
<div class="last-frame" v-if="showLastFrame">
|
||||||
|
<div class="upload-title-left">
|
||||||
|
{{ $t('common.uploadLastPlaceholder') || '上传尾帧(可选)' }}
|
||||||
|
</div>
|
||||||
|
<mf-image-upload
|
||||||
|
listType="picture-card"
|
||||||
|
uploadHeight="120px"
|
||||||
|
:show-file-list="false"
|
||||||
|
:content="$t('common.uploadLastPlaceholder') || '点击上传尾帧'"
|
||||||
|
v-model="lastUrl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模型选择 -->
|
||||||
|
<div class="model-select">
|
||||||
|
<div class="model-title">{{ $t('common.selectModel') || '选择模型' }}</div>
|
||||||
|
<a-select v-model="selectedModel" style="width: 100%;">
|
||||||
|
<a-option
|
||||||
|
v-for="option in modelOptions"
|
||||||
|
:key="option.value"
|
||||||
|
:value="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 富文本编辑器 - 加大输入框 -->
|
||||||
|
<div class="rich-editor-container">
|
||||||
|
<RichTextEditor
|
||||||
|
v-model="editorContent"
|
||||||
|
:placeholder="$t('common.textVideoPlaceholder') || '描述视频内容,例如:一个女孩在海边跳舞...'"
|
||||||
|
:uploaded-images="uploadedImages"
|
||||||
|
@text-change="handleTextChange"
|
||||||
|
@image-upload="handleImageUpload"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mf-button
|
||||||
|
class="submit"
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
:loading="generateLoading"
|
||||||
|
@click="generateVideo">
|
||||||
|
{{
|
||||||
|
price
|
||||||
|
? `${$t('common.createVideo', { price: price })}`
|
||||||
|
: $t('common.generateVideo')
|
||||||
|
}}
|
||||||
|
</mf-button>
|
||||||
|
<div class="submit-tip">
|
||||||
|
{{ $t('common.generateTip') || '生成视频需要消耗余额' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧结果区 -->
|
||||||
|
<div class="right" :class="{ 'has-result': showResult }">
|
||||||
|
<div v-if="!showResult" class="empty-state">
|
||||||
|
<img src="/images/empty-video.png" alt="视频生成" />
|
||||||
|
<p>生成的视频将在这里显示</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="result-container">
|
||||||
|
<div class="result-video-wrapper">
|
||||||
|
<video
|
||||||
|
v-if="videoUrl"
|
||||||
|
:src="videoUrl"
|
||||||
|
controls
|
||||||
|
autoplay
|
||||||
|
class="result-video"
|
||||||
|
:poster="firstUrl?.url || ''">
|
||||||
|
您的浏览器不支持视频播放。
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result-actions">
|
||||||
|
<mf-button v-if="canCancel" @click="cancelTask" type="danger">
|
||||||
|
取消任务
|
||||||
|
</mf-button>
|
||||||
|
<mf-button @click="saveVideo" :disabled="!videoUrl">
|
||||||
|
<a-image src="/images/btn_save.png" /> 保存视频
|
||||||
|
</mf-button>
|
||||||
|
<mf-button @click="closeResult">
|
||||||
|
<a-image src="/images/btn_close.png" /> 关闭
|
||||||
|
</mf-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
import RichTextEditor from '@/components/RichTextEditor.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'VideoGen',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
prefixCls: 'video-gen',
|
||||||
|
mode: 'text-to-video', // 'text-to-video' 或 'image-to-video'
|
||||||
|
firstUrl: '',
|
||||||
|
lastUrl: '',
|
||||||
|
editorContent: '',
|
||||||
|
interval: null,
|
||||||
|
videoUrl: null,
|
||||||
|
videoLoading: false,
|
||||||
|
generateLoading: false,
|
||||||
|
videoId: null,
|
||||||
|
showResult: false,
|
||||||
|
price: null,
|
||||||
|
id: null,
|
||||||
|
modelOptions: [
|
||||||
|
{ label: 'Seedance 2.0', value: 'ep-20260326165811-dlkth' },
|
||||||
|
{ label: 'Seedance 2.0 Fast', value: 'ep-20260326170056-dkj9m' }
|
||||||
|
],
|
||||||
|
selectedModel: 'ep-20260326165811-dlkth',
|
||||||
|
uploadedImages: [],
|
||||||
|
showLastFrame: false,
|
||||||
|
maxPollAttempts: 40
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
RichTextEditor
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['lang'])
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadPriceInfo()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadPriceInfo() {
|
||||||
|
this.$axios({
|
||||||
|
url: 'api/manager/selectInfo',
|
||||||
|
method: 'GET',
|
||||||
|
data: { aiType: '21' }
|
||||||
|
}).then((res) => {
|
||||||
|
this.price = res.data?.price
|
||||||
|
this.id = res.data?.id
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTextChange(content) {
|
||||||
|
this.editorContent = content
|
||||||
|
},
|
||||||
|
|
||||||
|
handleImageUpload(imageInfo) {
|
||||||
|
if (imageInfo && imageInfo.url) {
|
||||||
|
this.uploadedImages.push({
|
||||||
|
url: imageInfo.url,
|
||||||
|
name: imageInfo.name || 'image'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
generateVideo() {
|
||||||
|
if (this.mode === 'image-to-video') {
|
||||||
|
const firstImageUrl = typeof this.firstUrl === 'object' ? this.firstUrl.url : this.firstUrl
|
||||||
|
if (!firstImageUrl) {
|
||||||
|
this.$message.error(this.$t('common.uploadFirstImageError') || '请上传参考图')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if (!this.editorContent.trim()) {
|
||||||
|
this.$message.error('请输入视频描述文本')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.generateLoading = true
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
text: this.editorContent || '一个优雅的女孩在阳光下跳舞',
|
||||||
|
functionType: '21',
|
||||||
|
model: this.selectedModel
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图生视频模式需要上传图片
|
||||||
|
if (this.mode === 'image-to-video') {
|
||||||
|
const firstImageUrl = typeof this.firstUrl === 'object' ? this.firstUrl.url : this.firstUrl
|
||||||
|
params.firstUrl = firstImageUrl
|
||||||
|
|
||||||
|
if (this.lastUrl) {
|
||||||
|
params.lastUrl = typeof this.lastUrl === 'object' ? this.lastUrl.url : this.lastUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$axios({
|
||||||
|
url: 'api/ai/imgToVideo',
|
||||||
|
method: 'POST',
|
||||||
|
data: params
|
||||||
|
}).then((res) => {
|
||||||
|
this.generateLoading = false
|
||||||
|
if (res.code == 200) {
|
||||||
|
this.videoId = res.data.id
|
||||||
|
this.showResult = true
|
||||||
|
this.getVideo(res.data.id)
|
||||||
|
} else if (res.code == -1) {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('common.notice'),
|
||||||
|
content: this.$t('common.balenceLow'),
|
||||||
|
onOk: () => this.$router.push('/recharge')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
this.generateLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getVideo(videoId) {
|
||||||
|
let attempts = 0
|
||||||
|
this.interval = setInterval(() => {
|
||||||
|
attempts++
|
||||||
|
if (attempts > this.maxPollAttempts) {
|
||||||
|
this.$message.warning('视频生成超时')
|
||||||
|
this.destroyInterval()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$axios({
|
||||||
|
url: `api/ai/${videoId}`,
|
||||||
|
method: 'GET'
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.code == 200 && res.data.status === 'succeeded') {
|
||||||
|
this.videoUrl = res.data.content?.video_url || res.data.video_url
|
||||||
|
this.destroyInterval()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 3000)
|
||||||
|
},
|
||||||
|
|
||||||
|
destroyInterval() {
|
||||||
|
if (this.interval) {
|
||||||
|
clearInterval(this.interval)
|
||||||
|
this.interval = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
saveVideo() {
|
||||||
|
if (!this.videoUrl) return
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = this.videoUrl
|
||||||
|
link.download = `video_${Date.now()}.mp4`
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
},
|
||||||
|
|
||||||
|
closeResult() {
|
||||||
|
this.showResult = false
|
||||||
|
this.videoUrl = null
|
||||||
|
this.videoId = null
|
||||||
|
this.taskStatus = null
|
||||||
|
},
|
||||||
|
|
||||||
|
// 取消任务
|
||||||
|
async cancelTask() {
|
||||||
|
if (!this.videoId) return
|
||||||
|
|
||||||
|
this.$confirm({
|
||||||
|
title: '取消任务',
|
||||||
|
content: '确定要取消当前视频生成任务吗?取消后余额将退回。',
|
||||||
|
okText: '确定取消',
|
||||||
|
cancelText: '关闭',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
const res = await this.$axios({
|
||||||
|
url: `api/ai/${this.videoId}/cancel`,
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.$message.success('任务已取消,余额已退回')
|
||||||
|
this.destroyInterval()
|
||||||
|
this.showResult = false
|
||||||
|
this.videoUrl = null
|
||||||
|
this.videoId = null
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '取消失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('取消请求失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 计算属性:是否可以取消
|
||||||
|
canCancel() {
|
||||||
|
return this.videoId && !this.videoUrl
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.destroyInterval()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.video-gen {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #0f1014;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
width: 480px;
|
||||||
|
background: #1a1b20;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 22px;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-left: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-select {
|
||||||
|
margin: 16px 0;
|
||||||
|
|
||||||
|
.model-title {
|
||||||
|
color: #ddd;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-editor-container {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 320px;
|
||||||
|
margin: 16px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
:deep(.rich-editor-root) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.rich-editor) {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 280px !important;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.75;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
flex: 1;
|
||||||
|
background: #000;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.has-result {
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 120px;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-video-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 720px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-video {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue