中英双语
This commit is contained in:
1 parent
ea26af26ee
commit
b98c0333e6
50 files changed
+4082
-2049
No files matched your search
@@ -1,43 +1,43 @@
|
||||
<template>
|
||||
<div class="image-card">
|
||||
<div class="imgrow">
|
||||
<button class="img-thumb" :title="supportsSequence && resource.imageMode === 'sequence' ? '选择序列帧图片' : '选择本地图片'" @click="pickImage">
|
||||
<button class="img-thumb" :title="supportsSequence && resource.imageMode === 'sequence' ? $t('image.chooseSequence') : $t('image.chooseLocal')" @click="pickImage">
|
||||
<img v-if="displayPreviewUrl" :src="displayPreviewUrl" :alt="resource.textureName" />
|
||||
<svg v-else viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="#8a93bb" stroke-width="1.6"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.6"/><path d="M21 15l-5-5-9 9"/></svg>
|
||||
</button>
|
||||
<div class="img-fields">
|
||||
<label v-if="showFolder" class="row"><span>子文件夹</span><input v-model="resource.textureFolder" type="text" class="inp" placeholder="subfolder/" /></label>
|
||||
<label class="row"><span>名称</span><input v-model="resource.textureName" type="text" class="inp" :placeholder="namePlaceholder" /></label>
|
||||
<label v-if="showFolder" class="row"><span>{{ $t('image.subfolder') }}</span><input v-model="resource.textureFolder" type="text" class="inp" placeholder="subfolder/" /></label>
|
||||
<label class="row"><span>{{ $t('image.name') }}</span><input v-model="resource.textureName" type="text" class="inp" :placeholder="namePlaceholder" /></label>
|
||||
</div>
|
||||
<div class="img-actions">
|
||||
<button class="mini-btn2" :title="`恢复 ${defaultFile}`" @click="$emit('reset', resource.id)">⟳</button>
|
||||
<button class="mini-btn2" title="删除图片资源" :disabled="resourceCount === 1" @click="$emit('remove', resource.id)">🗑</button>
|
||||
<button class="mini-btn2" :title="$t('image.restore', { name: defaultFile })" @click="$emit('reset', resource.id)">⟳</button>
|
||||
<button class="mini-btn2" :title="$t('image.deleteResource')" :disabled="resourceCount === 1" @click="$emit('remove', resource.id)">🗑</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="thumb-note">{{ displayFileName }}</div>
|
||||
<label v-if="supportsSequence" class="row"><span>图片模式</span>
|
||||
<label v-if="supportsSequence" class="row"><span>{{ $t('image.imageMode') }}</span>
|
||||
<select v-model="resource.imageMode" class="inp">
|
||||
<option value="fixed">固定</option>
|
||||
<option value="sequence">序列帧</option>
|
||||
<option value="fixed">{{ $t('image.fixed') }}</option>
|
||||
<option value="sequence">{{ $t('image.sequence') }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<template v-if="supportsSequence && resource.imageMode === 'sequence'">
|
||||
<label class="row"><span>播放模式</span>
|
||||
<label class="row"><span>{{ $t('image.playbackMode') }}</span>
|
||||
<select v-model="resource.sequencePlayback" class="inp">
|
||||
<option value="once-hide">单次-尾帧消失</option>
|
||||
<option value="once-hold">单次-尾帧固定</option>
|
||||
<option value="loop-forward">正向循环</option>
|
||||
<option value="loop-reverse">反向循环</option>
|
||||
<option value="once-hide">{{ $t('image.onceHide') }}</option>
|
||||
<option value="once-hold">{{ $t('image.onceHold') }}</option>
|
||||
<option value="loop-forward">{{ $t('image.loopForward') }}</option>
|
||||
<option value="loop-reverse">{{ $t('image.loopReverse') }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="row"><span>播放帧率</span><input v-model.number="resource.sequenceFps" type="number" min="1" max="120" step="1" class="inp" /></label>
|
||||
<button class="sequence-pick" @click="$emit('pickSequence', resource.id)">选择序列帧(多选)</button>
|
||||
<label class="row"><span>{{ $t('image.playbackFps') }}</span><input v-model.number="resource.sequenceFps" type="number" min="1" max="120" step="1" class="inp" /></label>
|
||||
<button class="sequence-pick" @click="$emit('pickSequence', resource.id)">{{ $t('image.chooseSequenceMulti') }}</button>
|
||||
<div class="sequence-note">{{ sequenceOrderText }}</div>
|
||||
</template>
|
||||
<div class="resource-weight-row">
|
||||
<NumSlider
|
||||
class="resource-weight-control"
|
||||
label="占比%"
|
||||
:label="$t('image.weight')"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="1"
|
||||
@@ -49,7 +49,7 @@
|
||||
class="weight-lock"
|
||||
:class="{ on: resource.locked }"
|
||||
:disabled="resourceCount === 1"
|
||||
:title="resource.locked ? '解除占比锁定' : '锁定当前占比'"
|
||||
:title="resource.locked ? $t('image.unlockWeight') : $t('image.lockWeight')"
|
||||
@click="$emit('toggleLock', resource.id)"
|
||||
>
|
||||
<svg v-if="resource.locked" viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg>
|
||||
@@ -60,10 +60,10 @@
|
||||
<div v-if="supportsPivot" class="pivot-toggle-row">
|
||||
<div class="pivot-toggle-icon">◎</div>
|
||||
<div class="pivot-toggle-text">
|
||||
<div class="pivot-toggle-title">贴图轴点</div>
|
||||
<div class="pivot-toggle-title">{{ $t('image.pivot') }}</div>
|
||||
<div class="pivot-toggle-sub" :title="pivotSummary">{{ pivotSummary }}</div>
|
||||
</div>
|
||||
<label class="pivot-switch" :title="pivotOpen ? '收起轴点调整' : '展开轴点调整'">
|
||||
<label class="pivot-switch" :title="pivotOpen ? $t('image.collapsePivot') : $t('image.expandPivot')">
|
||||
<input v-model="pivotOpen" type="checkbox" @change="onPivotToggle" />
|
||||
<span class="pivot-switch-ui"></span>
|
||||
</label>
|
||||
@@ -81,19 +81,19 @@
|
||||
|
||||
<Teleport to="body">
|
||||
<div v-if="pivotFullscreen" class="pivot-modal-backdrop" @pointerdown.self="closePivotFullscreen(false)">
|
||||
<div class="pivot-modal" role="dialog" aria-modal="true" aria-label="贴图旋转轴点大图精修">
|
||||
<div class="pivot-modal" role="dialog" aria-modal="true" :aria-label="$t('image.pivotModal')">
|
||||
<div class="pivot-modal-head">
|
||||
<div class="pivot-modal-icon">◎</div>
|
||||
<div class="pivot-modal-info">
|
||||
<div class="pivot-modal-title">
|
||||
贴图旋转轴点大图精修
|
||||
{{ $t('image.pivotModal') }}
|
||||
<span class="pivot-modal-badge">{{ resource.textureName || defaultFile }}</span>
|
||||
</div>
|
||||
<div class="pivot-modal-desc">在下方贴图上任意点击或拖拽,精确设置粒子的旋转/摆动中心</div>
|
||||
<div class="pivot-modal-desc">{{ $t('image.pivotModalDesc') }}</div>
|
||||
</div>
|
||||
<button type="button" class="pivot-modal-close" title="关闭" @click="closePivotFullscreen(false)">✕</button>
|
||||
<button type="button" class="pivot-modal-close" :title="$t('common.close')" @click="closePivotFullscreen(false)">✕</button>
|
||||
</div>
|
||||
<div class="pivot-modal-hint">支持 100% ~ 400% 放大,精确对准尖端或根部:</div>
|
||||
<div class="pivot-modal-hint">{{ $t('image.pivotZoomHint') }}</div>
|
||||
<PivotEditor
|
||||
large
|
||||
:preview-url="displayPreviewUrl"
|
||||
@@ -103,8 +103,8 @@
|
||||
@update:anchorY="draftY = $event"
|
||||
/>
|
||||
<div class="pivot-modal-actions">
|
||||
<button type="button" class="pivot-modal-cancel" @click="closePivotFullscreen(false)">取消</button>
|
||||
<button type="button" class="pivot-modal-confirm" @click="closePivotFullscreen(true)">确定并保存轴点</button>
|
||||
<button type="button" class="pivot-modal-cancel" @click="closePivotFullscreen(false)">{{ $t('common.cancel') }}</button>
|
||||
<button type="button" class="pivot-modal-confirm" @click="closePivotFullscreen(true)">{{ $t('image.confirmPivot') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -113,6 +113,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import NumSlider from './NumSlider.vue'
|
||||
import PivotEditor from './PivotEditor.vue'
|
||||
|
||||
@@ -154,14 +155,17 @@ const emit = defineEmits<{
|
||||
updateWeight: [id: number, value: number]
|
||||
toggleLock: [id: number]
|
||||
}>()
|
||||
const { t } = useI18n()
|
||||
|
||||
const displayFileName = computed(() => {
|
||||
if (props.resource.imageMode === 'sequence') {
|
||||
const frames = props.resource.sequenceFrames || []
|
||||
if (!frames.length) return '未选择序列帧'
|
||||
const first = frames[0]?.name || '第 1 帧'
|
||||
if (!frames.length) return t('image.noSequence')
|
||||
const first = frames[0]?.name || t('image.firstFrame')
|
||||
const last = frames[frames.length - 1]?.name || first
|
||||
return frames.length === 1 ? `1 帧 · ${first}` : `${frames.length} 帧 · ${first} → ${last}`
|
||||
return frames.length === 1
|
||||
? t('image.frameCountOne', { name: first })
|
||||
: t('image.frameCountMany', { count: frames.length, first, last })
|
||||
}
|
||||
const name = props.resource.textureName || props.defaultFile.replace(/\.[^.]+$/, '')
|
||||
return name.includes('.') ? name : `${name}.png`
|
||||
@@ -207,9 +211,9 @@ const draftY = ref(0.5)
|
||||
const pivotSummary = computed(() => {
|
||||
const x = ((props.resource.anchorX ?? 0.5) * 100).toFixed(1)
|
||||
const y = ((props.resource.anchorY ?? 0.5) * 100).toFixed(1)
|
||||
if (pivotOpen.value) return `已开启:X ${x}% | Y ${y}%`
|
||||
if (pivotCentered.value) return '未开启,默认围绕图片正中心 (50%, 50%)'
|
||||
return `轴点已自定义:X ${x}% | Y ${y}%`
|
||||
if (pivotOpen.value) return t('image.pivotOn', { x, y })
|
||||
if (pivotCentered.value) return t('image.pivotOff')
|
||||
return t('image.pivotCustom', { x, y })
|
||||
})
|
||||
|
||||
function openPivotFullscreen() {
|
||||
@@ -243,10 +247,10 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKeydown))
|
||||
|
||||
const sequenceOrderText = computed(() => {
|
||||
const frames = props.resource.sequenceFrames || []
|
||||
if (!frames.length) return '未选择序列帧'
|
||||
const first = frames[0]?.name || '第一张图片'
|
||||
const last = frames[frames.length - 1]?.name || '最后一张图片'
|
||||
return `已选择 ${frames.length} 帧,按 ${first}-${last} 顺序播放`
|
||||
if (!frames.length) return t('image.noSequence')
|
||||
const first = frames[0]?.name || t('image.firstImage')
|
||||
const last = frames[frames.length - 1]?.name || t('image.lastImage')
|
||||
return t('image.sequenceSelected', { count: frames.length, first, last })
|
||||
})
|
||||
|
||||
function pickImage() {
|
||||
|
||||
Reference in new issue
Block a user