@@ -1,5 +1,68 @@
< template >
< template >
< div class = "design-panel" >
< div class = "design-panel" >
<!-- === == 背面设计面板 === == -- >
< template v-if = "isBack" >
< section >
< h4 > 背面背景色 < / h4 >
< div class = "row" >
< input type = "color" :value = "backDesign.background_color" @input ="setBackBgColor($event.target.value)" / >
< input type = "text" :value = "backDesign.background_color" @change ="setBackBgColor($event.target.value)" / >
< / div >
< / section >
< section >
< h4 > 背面边框 < / h4 >
< div class = "row" >
< label class = "mini-label" > 颜色 < / label >
< input type = "color" :value = "backDesign.border_color" @input ="setBackDesign('border_color', $event.target.value)" / >
< label class = "mini-label" > 粗细 < / label >
< input type = "number" min = "0" max = "40" :value = "backDesign.border_width"
@change ="setBackDesign('border_width', parseInt($event.target.value) || 0)" / >
< / div >
< / section >
< section >
< h4 > 图案色调 < / h4 >
< p class = "hint" > 为背面图案叠加一层半透明色调 < / p >
< div class = "row" >
< input type = "color" : value = "backDesign.pattern_color || '#C0A050'"
@input ="setBackDesign('pattern_color', $event.target.value)" / >
< button v-if = "backDesign.pattern_color" @click="setBackDesign('pattern_color', null)" class="mini ghost" > 清除色调 < / button >
< / div >
< / section >
< section >
< h4 > 背面图片 < / h4 >
< div class = "row" >
< button @click ="uploadBackImage" class = "mini" > 上传背面图 < / button >
< / div >
< input ref = "backImageInput" type = "file" accept = "image/*" @change ="onBackImageFile" hidden / >
< div v-if = "backDesign.image" class="row" >
< img :src = "backDesign.image" class = "thumb" / >
< button @click ="setBackDesign('image', null)" class = "mini ghost" > 清除 < / button >
< / div >
< / section >
< section v-if = "backDesign.image" >
< h4 > 图片位置微调 < / h4 >
< p class = "hint" > 拖动滑块微调素材图片的位置与缩放 ( 0 = 默认 ) < / p >
< div class = "pip-row" >
< label class = "mini-label" > dx < / label >
< input type = "range" min = "-0.05" max = "0.05" step = "0.005"
:value = "backImageOffsetVal('image_dx')" @input ="setBackImageOffset('image_dx', $event.target.value)" / >
< label class = "mini-label" > dy < / label >
< input type = "range" min = "-0.05" max = "0.05" step = "0.005"
:value = "backImageOffsetVal('image_dy')" @input ="setBackImageOffset('image_dy', $event.target.value)" / >
< label class = "mini-label" > 缩放 < / label >
< input type = "range" min = "0.6" max = "1.4" step = "0.05"
:value = "backImageOffsetVal('image_scale')" @input ="setBackImageOffset('image_scale', $event.target.value)" / >
< / div >
< button @click ="resetBackImageOffset" class = "mini ghost" > 重置图片位置 < / button >
< / section >
< / template >
<!-- === == 正面设计面板 === == -- >
< template v-else >
< ! - - 背景色 - - >
< ! - - 背景色 - - >
< section >
< section >
< h4 > 背景色 < / h4 >
< h4 > 背景色 < / h4 >
@@ -91,7 +154,7 @@
< / div >
< / div >
< / section >
< / section >
<!-- 图片位置微调 ( 大小王 / 背面 ) -- >
<!-- 图片位置微调 ( 大小王 ) -- >
< section v-if = "showImageOffset" >
< section v-if = "showImageOffset" >
< h4 > 图片位置微调 < / h4 >
< h4 > 图片位置微调 < / h4 >
< p class = "hint" > 拖动滑块微调素材图片的位置与缩放 ( 0 = 默认 ) < / p >
< p class = "hint" > 拖动滑块微调素材图片的位置与缩放 ( 0 = 默认 ) < / p >
@@ -132,6 +195,7 @@
< / div >
< / div >
< button @click ="resetLayout" class = "mini ghost" > 重置本点数布局 < / button >
< button @click ="resetLayout" class = "mini ghost" > 重置本点数布局 < / button >
< / section >
< / section >
< / template >
< / div >
< / div >
< / template >
< / template >
@@ -142,6 +206,9 @@ import { SUITS, SUIT_TEXT, LAYOUT_POSITIONS, isJoker } from '@/utils/cardLayout'
const store = useProjectStore ( )
const store = useProjectStore ( )
const design = computed ( ( ) => store . effectiveDesign )
const design = computed ( ( ) => store . effectiveDesign )
const backDesign = computed ( ( ) => store . effectiveBackDesign )
const isBack = computed ( ( ) => store . currentCard === 'back' )
const override = computed ( ( ) => {
const override = computed ( ( ) => {
if ( ! store . project || ! store . currentCard ) return null
if ( ! store . project || ! store . currentCard ) return null
return ( store . project . card _overrides || { } ) [ store . currentCard ] || null
return ( store . project . card _overrides || { } ) [ store . currentCard ] || null
@@ -152,6 +219,7 @@ const suits = SUITS
const suitSymbol = ( s ) => SUIT _TEXT [ s ]
const suitSymbol = ( s ) => SUIT _TEXT [ s ]
const suitLabel = ( s ) => ( { spade : '黑桃' , heart : '红桃' , club : '梅花' , diamond : '方块' } ) [ s ]
const suitLabel = ( s ) => ( { spade : '黑桃' , heart : '红桃' , club : '梅花' , diamond : '方块' } ) [ s ]
// ===== 正面相关 =====
const bgFileInput = ref ( null )
const bgFileInput = ref ( null )
function uploadBg ( ) { bgFileInput . value ? . click ( ) }
function uploadBg ( ) { bgFileInput . value ? . click ( ) }
function onBgFile ( e ) {
function onBgFile ( e ) {
@@ -159,7 +227,6 @@ function onBgFile(e) {
if ( ! f ) return
if ( ! f ) return
const reader = new FileReader ( )
const reader = new FileReader ( )
reader . onload = ( ev ) => {
reader . onload = ( ev ) => {
// 这里只把 dataURL 临时存到 design; 正式应上传到后端
store . patchDesign ( 'background_image' , ev . target . result )
store . patchDesign ( 'background_image' , ev . target . result )
}
}
reader . readAsDataURL ( f )
reader . readAsDataURL ( f )
@@ -190,7 +257,7 @@ const isNumberCard = computed(() => {
} )
} )
const showImageOffset = computed ( ( ) => {
const showImageOffset = computed ( ( ) => {
return isJoker ( store . currentCard ) || store . currentCard === 'back'
return isJoker ( store . currentCard )
} )
} )
const isFaceCard = computed ( ( ) => {
const isFaceCard = computed ( ( ) => {
@@ -206,8 +273,6 @@ const showSymmetryMode = computed(() => {
const symmetryMode = computed ( ( ) => design . value . symmetry _mode || 'flip' )
const symmetryMode = computed ( ( ) => design . value . symmetry _mode || 'flip' )
function setSymmetryMode ( v ) {
function setSymmetryMode ( v ) {
// JQK 和大小王都存到 design 里(因为它们是单牌设置)
// 但 design 是合并后的,所以用 patchCardOverride 写到当前牌更干净
store . patchCardOverride ( store . currentCard , 'symmetry_mode' , v )
store . patchCardOverride ( store . currentCard , 'symmetry_mode' , v )
}
}
@@ -251,6 +316,36 @@ function setOffset(i, key, val) {
function resetLayout ( ) {
function resetLayout ( ) {
store . resetNumberLayout ( String ( selectedRank . value ) )
store . resetNumberLayout ( String ( selectedRank . value ) )
}
}
// ===== 背面相关 =====
const backImageInput = ref ( null )
function uploadBackImage ( ) { backImageInput . value ? . click ( ) }
function onBackImageFile ( e ) {
const f = e . target . files [ 0 ]
if ( ! f ) return
const reader = new FileReader ( )
reader . onload = ( ev ) => {
store . patchBackDesign ( 'image' , ev . target . result )
}
reader . readAsDataURL ( f )
}
function setBackBgColor ( v ) { store . patchBackDesign ( 'background_color' , v ) }
function setBackDesign ( path , v ) { store . patchBackDesign ( path , v ) }
function backImageOffsetVal ( key ) {
const v = Number ( backDesign . value [ key ] )
if ( Number . isNaN ( v ) ) return key === 'image_scale' ? 1 : 0
return v
}
function setBackImageOffset ( key , val ) {
store . patchBackDesign ( key , parseFloat ( val ) )
}
function resetBackImageOffset ( ) {
store . patchBackDesign ( 'image_dx' , 0 )
store . patchBackDesign ( 'image_dy' , 0 )
store . patchBackDesign ( 'image_scale' , 1 )
}
< / script >
< / script >
< style scoped >
< style scoped >