阶段10开发完成
This commit is contained in:
1 parent
cffe02c1ec
commit
c239171cc4
21 files changed
+997
-36
No files matched your search
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="image-card">
|
||||
<div class="imgrow">
|
||||
<button class="img-thumb" title="选择本地图片" @click="$emit('pick', resource.id)">
|
||||
<img v-if="resource.previewUrl" :src="resource.previewUrl" :alt="resource.textureName" />
|
||||
<button class="img-thumb" :title="supportsSequence && resource.imageMode === 'sequence' ? '选择序列帧图片' : '选择本地图片'" @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">
|
||||
@@ -15,6 +15,25 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="thumb-note">{{ displayFileName }}</div>
|
||||
<label v-if="supportsSequence" class="row"><span>图片模式</span>
|
||||
<select v-model="resource.imageMode" class="inp">
|
||||
<option value="fixed">固定</option>
|
||||
<option value="sequence">序列帧</option>
|
||||
</select>
|
||||
</label>
|
||||
<template v-if="supportsSequence && resource.imageMode === 'sequence'">
|
||||
<label class="row"><span>播放模式</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>
|
||||
</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>
|
||||
<div class="sequence-note">{{ sequenceOrderText }}</div>
|
||||
</template>
|
||||
<div class="resource-weight-row">
|
||||
<NumSlider
|
||||
class="resource-weight-control"
|
||||
@@ -48,6 +67,10 @@ import NumSlider from './NumSlider.vue'
|
||||
type CommonImageResource = {
|
||||
id: number
|
||||
previewUrl: string
|
||||
imageMode?: 'fixed' | 'sequence'
|
||||
sequencePlayback?: 'once-hide' | 'once-hold' | 'loop-forward' | 'loop-reverse'
|
||||
sequenceFps?: number
|
||||
sequenceFrames?: Array<{ name: string; previewUrl: string }>
|
||||
textureName: string
|
||||
textureFolder?: string
|
||||
weight: number
|
||||
@@ -60,13 +83,16 @@ const props = withDefaults(defineProps<{
|
||||
defaultFile: string
|
||||
namePlaceholder?: string
|
||||
showFolder?: boolean
|
||||
supportsSequence?: boolean
|
||||
}>(), {
|
||||
namePlaceholder: 'particle',
|
||||
showFolder: true,
|
||||
supportsSequence: false,
|
||||
})
|
||||
|
||||
defineEmits<{
|
||||
const emit = defineEmits<{
|
||||
pick: [id: number]
|
||||
pickSequence: [id: number]
|
||||
reset: [id: number]
|
||||
remove: [id: number]
|
||||
updateWeight: [id: number, value: number]
|
||||
@@ -74,9 +100,33 @@ defineEmits<{
|
||||
}>()
|
||||
|
||||
const displayFileName = computed(() => {
|
||||
if (props.resource.imageMode === 'sequence') {
|
||||
const frames = props.resource.sequenceFrames || []
|
||||
if (!frames.length) return '未选择序列帧'
|
||||
const first = frames[0]?.name || '第 1 帧'
|
||||
const last = frames[frames.length - 1]?.name || first
|
||||
return frames.length === 1 ? `1 帧 · ${first}` : `${frames.length} 帧 · ${first} → ${last}`
|
||||
}
|
||||
const name = props.resource.textureName || props.defaultFile.replace(/\.[^.]+$/, '')
|
||||
return name.includes('.') ? name : `${name}.png`
|
||||
})
|
||||
|
||||
const displayPreviewUrl = computed(() => props.resource.imageMode === 'sequence'
|
||||
? props.resource.sequenceFrames?.[0]?.previewUrl || ''
|
||||
: props.resource.previewUrl)
|
||||
|
||||
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} 顺序播放`
|
||||
})
|
||||
|
||||
function pickImage() {
|
||||
if (props.supportsSequence && props.resource.imageMode === 'sequence') emit('pickSequence', props.resource.id)
|
||||
else emit('pick', props.resource.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -94,6 +144,9 @@ const displayFileName = computed(() => {
|
||||
.mini-btn2:hover { background: #2b3a5c; }
|
||||
.mini-btn2:disabled { opacity: 0.35; cursor: not-allowed; }
|
||||
.thumb-note { margin: 4px 0 2px; color: #667; font-size: 11px; }
|
||||
.sequence-pick { width: 100%; margin: 2px 0 4px; padding: 6px 8px; border: 1px solid #4a4a76; border-radius: 5px; background: #27264b; color: #d8d7ff; font-size: 12px; cursor: pointer; }
|
||||
.sequence-pick:hover { border-color: #7774ff; background: #35336a; }
|
||||
.sequence-note { margin: 0 0 6px; color: #727b9b; font-size: 11px; line-height: 1.4; }
|
||||
.resource-weight-row { display: grid; grid-template-columns: minmax(0, 1fr) 28px; align-items: center; gap: 7px; }
|
||||
.resource-weight-control { width: 100%; min-width: 0; }
|
||||
.resource-weight-control :deep(.ns-range) { min-width: 0; }
|
||||
|
||||
Reference in new issue
Block a user