Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite

This commit is contained in:
李志鹏
2026-01-13 14:41:53 +08:00
34 changed files with 2059 additions and 586 deletions

View File

@@ -50,7 +50,7 @@
</div>
</div>
<div class="item detailLeft" :class="{isEditPattern:isEditPattern.value}">
<detailLeft v-if="currentDetailType" ref="detailLeft"></detailLeft>
<detailLeft v-if="currentDetailType" ref="detailLeft" :detailLeftColorKey="detailLeftColorKey"></detailLeft>
<!-- <detailLeft v-if="selectDetail && selectDetail.id && currentDetailType"></detailLeft> -->
<div class="btn" style="margin: 0;" v-show="currentDetailType == 'color'">
<div class="gallery_btn" @click="previwe">{{$t('DesignPrintOperation.Preview')}}</div>
@@ -61,7 +61,6 @@
<model
ref="model"
:key="positionKey"
@addDetail="addDetail"
@canvasReload="canvasReload"
@detailEdit="detailEdit"
@addSketch="()=>isEditPattern.value = ''"
@@ -103,15 +102,14 @@
<div class="gallery_btn" @click="previwe">{{$t('DesignPrintOperation.Preview')}}</div>
</div>
</div>
<div class="contentRight" v-if="selectDetail && selectDetail.id && currentDetailType && isEditPattern.value">
<canvasBox ref="canvasBox" :key="canvasKey || isEditPattern.value" :isEditPattern="isEditPattern.value"></canvasBox>
<div class="contentRight canvas" v-if="selectDetail && selectDetail.id && currentDetailType" :class="{'active': isEditPattern.value}">
<canvasBox ref="canvasBox" :key="canvasKey" @setSloganData="setSloganData" :isEditPattern="isEditPattern.value" :updateOtherLayers="updateOtherLayers"></canvasBox>
</div>
<!-- 画布 -->
<!-- <div class="content" v-else-if="selectDetail && selectDetail.id">
</div> -->
</div>
</div>
<addDetails ref="addDetails" @setSloganData="setSloganData"></addDetails>
</a-modal>
<div class="mark_loading" v-show="loadingShow">
<a-spin size="large" />
@@ -130,14 +128,13 @@ import canvasBox from './canvas/index.vue'
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { Https } from "@/tool/https";
import { Modal,message } from 'ant-design-vue';
import {getUploadUrl,isMoible,setGradual} from '@/tool/util'
import {getUploadUrl,segmentImage,setGradual,rgbToHsv,rgbaToHex} from '@/tool/util'
import { useStore } from "vuex";
import { openGuide,driverObj__ } from "@/tool/guide";
import { useI18n } from 'vue-i18n'
import addDetails from '@/component/Detail/addDetails.vue'
export default defineComponent({
components:{
detailLeft,model,detailRight,canvasBox,addDetails
detailLeft,model,detailRight,canvasBox
},
emits:['destroy'],
setup(props,{emit}) {
@@ -148,7 +145,6 @@ export default defineComponent({
canvasBox,
detailRight,
detailLeft:null as any,
addDetails:null as any,
})
const userDetail = computed(()=>{
return store.state.UserHabit.userDetail
@@ -157,10 +153,12 @@ export default defineComponent({
selectObject:computed(()=>store.state.Workspace.probjects) as any,//选择的项目
designDetail:computed(()=>store.state.DesignDetail.designDetail),
currentDetailType:computed(()=>store.state.DesignDetail.currentDetailType),
frontBack:computed(()=>store.state.DesignDetail.frontBack),
selectDetail:computed(()=>store.state.DesignDetail.selectDetail),
designDetailShow:false,
loadingShow:false,
oppositeRevocationShow:-1,
imgDomIndex:-1,
revocationShow:-1,
isEditPattern:{
value:'' as any,
@@ -174,8 +172,13 @@ export default defineComponent({
},
positionKey:0,
isUndividedLayerWithSinglePrint:false,
detailLeftColorKey:0,
})
watch(()=>detailData.selectDetail,(newValue,oldValue)=>{
detailData.imgDomIndex = detailData.frontBack.front.findIndex((item:any)=>item.id == newValue.id)
if(newValue?.undividedLayer)newValue.undividedLayer_ = newValue.undividedLayer
// privewDetail(oldValue)
},{immediate: true})
provide('getCanvasIfEdit',detailData.getCanvasIfEdit)
provide('singleOveral',detailData.singleOveral)
@@ -320,13 +323,15 @@ export default defineComponent({
const setCurrentDetail = (str:string)=>{
store.commit('DesignDetail/setCurrentDetailType',str)
}
const setClothes = async (list:any)=>{
const setClothes = async (list:any,str:string)=>{
let clothesList:any = []
await nextTick()
if(detailData.isEditPattern.value == 'editSketch')await detailDom.canvasBox.submitBase64Data().then((rv)=>{
detailData.selectDetail.sketchString = rv
})
for(let i = 0;i<list.length;i++){
detailData.selectDetail
let {scale,offset,priority,maskUrl,maskMinioUrl} = await (detailDom.model as any).getSubmitData(list[i],detailData.isUndividedLayerWithSinglePrint)
if(detailDom.canvasBox?.privewDetail)await (detailDom.canvasBox as any).privewDetail()
let {scale,offset,priority,transpose,rotate,maskUrl,maskMinioUrl} = await (detailDom.model as any).getSubmitData(list[i],detailData.isUndividedLayerWithSinglePrint)
if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail()
let gradient = null
let newData = list[i]?.newDetail?.[detailData.currentDetailType]
@@ -362,7 +367,9 @@ export default defineComponent({
// 406.90964
// ],
offset,
partialDesign:list[i].partialDesign || {},
transpose,
rotate,
partialDesign:(detailData.currentDetailType == 'sketch' || detailData.isEditPattern.value == 'editSketch')?{}:list[i].partialDesign,
// partialDesign:detailData.isEditPattern.value?list[i].partialDesign:{},
path:(newData && detailData.currentDetailType == 'sketch' && isCurrent && !detailData.isEditPattern.value)?newData.minIOPath:list[i].minIOPath,
printObject:(newData && detailData.currentDetailType == 'print' && isCurrent && !detailData.isEditPattern.value)?{prints:newData}:list[i].printObject?list[i].printObject:{prints:[]},
@@ -392,9 +399,9 @@ export default defineComponent({
if(!detailData?.selectDetail?.path && !detailData?.selectDetail?.newDetail?.sketch?.minIOPath)return
let clothes:any
if(detailData.currentDetailType == 'models' || detailData.isUndividedLayerWithSinglePrint){
clothes = await setClothes(detailData.designDetail.clothes)
clothes = await setClothes(detailData.designDetail.clothes,str)
}else{
clothes = await setClothes([detailData.selectDetail])
clothes = await setClothes([detailData.selectDetail],str)
}
let data = {
designItemId:detailData.designDetail.designItemId,
@@ -417,7 +424,8 @@ export default defineComponent({
fun:setRevocation
}
if(detailData?.designDetail?.newModel)detailData.designDetail.oldModel = JSON.parse(JSON.stringify(detailData.designDetail.newModel))
delete detailData.designDetail.newModel
// delete detailData.designDetail.newModel
detailData.selectDetail.sketchString = null
store.commit('DesignDetail/setPraeview',value)
detailData.loadingShow = false
detailData.isUndividedLayerWithSinglePrint = false
@@ -429,7 +437,7 @@ export default defineComponent({
}
const submit = async ()=>{
let workspace = store.state.Workspace.probjects
let clothes:any = await setClothes(detailData.designDetail.clothes)
let clothes:any = await setClothes(detailData.designDetail.clothes,'sub')
let data = {
designItemId:detailData.designDetail.designItemId,
designSingleItemDTOList:clothes,
@@ -466,9 +474,41 @@ export default defineComponent({
detailData.loadingShow = false
});
}
const previwe = ()=>{
let data = getSubmitData('preview')
store.dispatch('DesignDetail/setSubmit',data)
const previwe = async ()=>{
if((detailData.currentDetailType == 'sketch' && !detailData.isEditPattern.value) || detailData.isEditPattern.value == 'editSketch'){
let data = getSubmitData('preview')
store.dispatch('DesignDetail/setSubmit',data)
}else{
//走画布合成图片并且直接分割
if(detailData.isEditPattern.value !== 'canvasEditor'){
if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail()
let otherData = await updateOtherLayers('single')
await detailDom.canvasBox.updateOtherLayers(otherData)
}
await detailDom.canvasBox.privewDetail()
let img = new Image()
img.onload = ()=>{
let partialDesign = detailData.selectDetail.partialDesign.partialDesignBase64 || detailData.selectDetail.partialDesign.partialDesignPath
let size = {
width:img.width,
height:img.height,
}
segmentImage(detailData.selectDetail.maskUrl,partialDesign,size).then(async (rv)=>{
let front = detailData.frontBack.front[detailData.imgDomIndex]
let back = detailData.frontBack.back[detailData.imgDomIndex]
if(!front?.oldImageUrl)front.oldImageUrl = front.imageUrl
if(!front?.oldMaskUrl)front.oldMaskUrl = front.maskUrl
if(!back?.oldImageUrl)back.oldImageUrl = back.imageUrl
if(!front?.oldMaskUrl)store.commit('DesignDetail/updataDetailItem',{maskUrl:front.oldMaskUrl})
front.imageUrl = rv.targetFrontUrl
back.imageUrl = rv.targetBackUrl
detailData.selectDetail.undividedLayerWithSinglePrint = partialDesign
store.commit('DesignDetail/canvasPreviewUpdata',{type:detailData.isEditPattern.value?'all':detailData.currentDetailType,callBack:setRevocation})
})
}
img.src = detailData.selectDetail.path
}
}
const modelOnLoad = ()=>{
if(!detailData.isUndividedLayerWithSinglePrint)return
@@ -480,31 +520,132 @@ export default defineComponent({
const detailEdit = async (str:any)=>{
if(str){
if(detailData.isEditPattern.value && detailData.isEditPattern.value == str){
await detailDom.canvasBox.saveCanvas()
// await detailDom.canvasBox.saveCanvas()
await (detailDom.canvasBox as any).privewDetail()
if(detailData.isEditPattern.value == 'canvasEditor')await uploadElement()
detailData.isEditPattern.value = ''
}else{
if(detailData.isEditPattern.value){
detailDom.canvasBox.editFront(str)
}
// if(detailData.isEditPattern.value && (str == 'canvasEditor' || str == 'redGreenExample')){
// detailDom.canvasBox.editFront(str)
// }
detailDom.canvasBox.editFront(str)
let otherData = await updateOtherLayers('single')
await detailDom.canvasBox.updateOtherLayers(otherData)
detailData.isEditPattern.value = str
}
}else{
detailData.isEditPattern.value = ''
}
}
const getColorName = (color:any)=>{
let rgb:any = [color.r, color.g, color.b];
let hsv = rgbToHsv(rgb);
let data = [{
h: hsv[0],
s: hsv[1],
v: hsv[2],
}]
return new Promise((resolve, reject) => {
Https.axiosPost(Https.httpUrls.getRgbByHsvBatch, data)
.then((rv) => {
if (rv) {
resolve(rv[0])
}
})
.catch((res) => {
resolve({name:'--',tcx:'--'})
});
})
}
const updateOtherLayers = async (str:any='all')=>{//更新到画布图层
let otherData:any = {}
if(detailDom.detailRight?.privewDetail)await (detailDom.detailRight as any).privewDetail()
if(str == 'all'){
otherData = {
color: detailData.selectDetail.newDetail?.color.r?detailData.selectDetail.newDetail?.color:detailData.selectDetail.color,
printObject: detailData.selectDetail.newDetail?.print?.length>0?{prints:detailData.selectDetail.newDetail?.print}:detailData.selectDetail.printObject || null,
trims: detailData.selectDetail.newDetail?.element?.length>0?detailData.selectDetail.newDetail?.element:detailData.selectDetail.trims || null,
}
}else if(str == 'single'){
otherData = {
color: detailData.selectDetail.color,
printObject: detailData.selectDetail.printObject || null,
trims: detailData.selectDetail.trims || null,
}
if(detailData.currentDetailType == 'color'){
let color = detailData.selectDetail.newDetail?.color
console.log(color)
// let colorData:any = await getColorName(color?.rgba)
if(detailData.selectDetail.newDetail?.color?.rgba?.r){
color.rgba = {r:color.r,g:color.g,b:color.b,a:color.a}
otherData.color = color
}
}
if(detailData.currentDetailType == 'print'){
otherData.printObject = detailData.selectDetail.newDetail?.print?.length>0?{prints:detailData.selectDetail.newDetail?.print}:detailData.selectDetail.printObject || null
}
if(detailData.currentDetailType == 'element'){
otherData.trims = detailData.selectDetail.newDetail?.element?.length>0?detailData.selectDetail.newDetail?.element:detailData.selectDetail.trims || null
}
}
console.log(otherData,'=======',detailData.selectDetail)
return otherData
}
const uploadElement = async ()=>{//取出画布数据更新到detail
if(detailData.isEditPattern.value == 'canvasEditor'){
// await detailDom.canvasBox.saveCanvas()
const allInfo = await (detailDom.canvasBox as any).getCanvasElement()
console.log(allInfo,'allInfo')
if(allInfo.trims?.length > 0){
// detailData.selectDetail.trims.prints = allInfo.trims
let value = {
data:allInfo.trims,
str:'element'
}
store.commit('DesignDetail/setNewDetail',value)
}
if(allInfo.prints?.length > 0){
// detailData.selectDetail.printObject.prints = allInfo.prints
let value = {
data:allInfo.prints,
str:'print'
}
store.commit('DesignDetail/setNewDetail',value)
}
if(allInfo.color?.color?.rgba){
let canvasColor = allInfo.color.color
let colorData:any = await getColorName(allInfo.color.color?.rgba)
let value:any = {
data:{
hsv:{
h:colorData.h,
s:colorData.s,
v:colorData.v,
},
name:colorData.name,
tcx:colorData.tcx,
rgba:canvasColor.rgba,
hex:rgbaToHex([canvasColor.rgba.r,canvasColor.rgba.g,canvasColor.rgba.b]),
},
str:'color'
}
if(canvasColor.gradient){
value.data.gradient = canvasColor.gradient
}
store.commit('DesignDetail/setNewDetail',value)
if(allInfo.color.color.gradient)detailData.selectDetail.color.gradient = allInfo.color.color.gradient
if(detailData.currentDetailType == 'color'){
detailData.detailLeftColorKey++
}
}
}
}
const canvasReload = async ()=>{
if(detailData.isEditPattern.value){
await detailDom.canvasBox.saveCanvas()
}
detailData.canvasKey += 1
}
let time = null as any
const handleResize = ()=>{
clearTimeout(time)
time = setTimeout(()=>{
store.commit('DesignDetail/setDesignDetail',detailData.designDetail)
},1000)
}
const sketchSysToLibrary = ()=>{//系统sketch添加到library更新library
coverRevocation()
detailDom.detailLeft.sketchSysToLibrary()
@@ -516,11 +657,6 @@ export default defineComponent({
sessionStorage.setItem('revocation', JSON.stringify(revocation));
sessionStorage.setItem('oppositeRevocation',JSON.stringify([]));
}
const addDetail = () =>{
let addDetails:any = detailDom.addDetails
addDetails.init(detailData.selectDetail,'')
}
const setSloganData = (data:any)=>{
detailData.selectDetail.sketchString = data
if(detailData.currentDetailType == 'sketch' && detailData.selectDetail?.newDetail?.sketch){
@@ -528,14 +664,12 @@ export default defineComponent({
}
}
onMounted(()=>{
window.addEventListener('resize', handleResize);
})
onBeforeUnmount(()=>{
sessionStorage.removeItem('oppositeRevocation')
sessionStorage.removeItem('revocation')
store.commit('DesignDetail/clearDesignDetail')
window.removeEventListener('resize', handleResize);
})
return{
@@ -553,8 +687,8 @@ export default defineComponent({
canvasReload,
modelOnLoad,
sketchSysToLibrary,
addDetail,
setSloganData,
updateOtherLayers,//更新到画布图层 再canvasInit中执行
}
},
@@ -680,6 +814,24 @@ export default defineComponent({
flex: 1;
flex-direction: column;
overflow: hidden;
&.canvas{
opacity: 0;
position: absolute;
flex: auto;
width: 35vw;
height: 80vh;
pointer-events: none;
transform: translate(100vw,100vh);
}
&.active{
position: relative;
opacity: 1;
flex: 1;
width: auto;
height: auto;
pointer-events: auto;
transform: translate(0,0);
}
}
}
.btn{

View File

@@ -1,98 +0,0 @@
<template>
<div class="addDetailsModal generalModel" ref="addDetailsModal"></div>
<a-modal
class="addDetails_modal generalModel fullScreen"
v-model:visible="addDetails"
:footer="null"
:get-container="() => $refs.addDetailsModal"
width="100%"
height="100%"
:maskClosable="false"
:centered="true"
:closable="false"
:destroyOnClose="true"
wrapClassName="#app"
:keyboard="false"
:mask="false"
>
<div class="generalModel_btn">
<div class="generalModel_closeIcon" @click.stop="cancelDsign()">
<svg width="100%" height="100%" viewBox="0 0 46 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="23" cy="23" r="23" fill="white" fill-opacity="0.3"/>
<rect x="32.5063" y="12" width="3" height="29" rx="1.5" transform="rotate(45 32.5063 12)" fill="#000"/>
<rect x="34.6274" y="32.5059" width="3" height="29" rx="1.5" transform="rotate(135 34.6274 32.5059)" fill="#000"/>
</svg>
</div>
</div>
<div class="addDetails_center">
<generalMiniCanvas :imgUrl="imgUrl" @submitBase64Data="submitBase64Data"></generalMiniCanvas>
</div>
<div></div>
</a-modal>
</template>
<script>
import { defineComponent, ref, reactive, watch, onMounted, nextTick, toRefs } from "vue";
import generalMiniCanvas from "../modules/generalMiniCanvas.vue";
export default defineComponent({
components: {
generalMiniCanvas,
},
emits: ['setSloganData'],
setup(props,{emit}) {
let addDetail = reactive({
imgUrl:''
});
let addDetails = ref(false);
let init = (data,index)=>{
addDetails.value = true
addDetail.imgUrl = data.sketchString || data.path
}
let submitBase64Data = (data)=>{
emit('setSloganData',data)
cancelDsign()
}
let cancelDsign = ()=>{
addDetails.value = false
}
return {
...toRefs(addDetail),
addDetails,
init,
submitBase64Data,
cancelDsign,
};
},
data() {
return {
};
},
mounted() {},
methods: {
},
});
</script>
<style lang="less" scoped>
.addDetailsModal{
width: 0;
height: 0;
}
.addDetails_modal {
.closeIcon {
z-index: 2;
}
.addDetails_center{
position: relative;
height: 100%;
display: flex;
flex-direction: column;
margin: 0 auto;
}
.exportCanvasBox_submit{
// margin-top: 2.4rem;
// text-align: center;
}
}
</style>

View File

@@ -6,16 +6,15 @@
<div class="content-bottom" ref="canvasContent">
<div class="contet">
<!-- :clothingImageUrl="selectDetail?.undividedLayerWithSinglePrint || selectDetail.undividedLayer || selectDetail.path" -->
<div class="canvas" v-if="currentView === 'canvasEditor'" @click.stop>
<div class="canvas" :class="{'active': currentView === 'canvasEditor'}"@click.stop>
<editCanvas v-if="canvasLoad" :config="canvasConfig"
@canvasInit="canvasInit"
@changeCanvas="changeCanvas"
is-edit
:clothingImageUrl="selectDetail.path"
:clothingImageUrl2="selectDetail.undividedLayer"
:clothingImageUrl2="selectDetail.undividedLayer_"
showFixedLayer
:canvasJSON="canvasJSON"
:otherData="otherData"
@canvasLoadJsonSuccess="canvasLoadJsonSuccess"
:clothing-image-opts="{
imageMode:'contains',
}"
@@ -25,12 +24,6 @@
<!-- <canvasContent ref="canvasContent"></canvasContent> -->
</div>
<div class="editFrontBack" v-if="currentView === 'redGreenExample'" @click.stop>
<!-- <editFrontBack
:patchData="frontBack"
:imgDomIndex="imgDomIndex"
ref="editFrontBack">
</editFrontBack> -->
<editCanvas v-if="canvasLoad" :config="canvasConfig"
@canvasInit="canvasInit"
:enabledRedGreenMode="true"
@@ -45,6 +38,9 @@
ref="editCanvasBackFront">
</editCanvas>
</div>
<div class="editSketch" v-if="currentView === 'editSketch'" @click.stop>
<generalMiniCanvas ref="generalMiniCanvas" :btnShow="false" :imgUrl="selectDetail.sketchString || selectDetail.path"></generalMiniCanvas>
</div>
</div>
<!-- <div class="Finish">
@@ -69,15 +65,20 @@ import { useI18n } from 'vue-i18n'
import editCanvas from "@/component/Canvas/CanvasEditor/index.vue";
import { formatTime,segmentImage,getMinioUrl } from "@/tool/util";
import { useRouter, useRoute } from 'vue-router'
import generalMiniCanvas from "../../modules/generalMiniCanvas.vue";
export default defineComponent({
components:{
editCanvas
editCanvas,generalMiniCanvas
},
props:{
isEditPattern:{
type:String,
default:''
},
updateOtherLayers:{
type:Function,
default:()=>{}
}
},
setup(props,{emit}) {
@@ -90,6 +91,7 @@ export default defineComponent({
editCanvas:null as any,
editCanvasBackFront:null as any,
canvasContent:null as any,
generalMiniCanvas:null as any,
})
const userDetail = computed(()=>{
return store.state.UserHabit.userDetail
@@ -130,14 +132,14 @@ export default defineComponent({
if(detailData.currentView === 'canvasEditor'){
sessionStorage.setItem('sketchEdit',detailDom.editCanvas.getJSON())
canvasJSON = sessionStorage.getItem('frontBackEdit');
}else{
}else if(detailData.currentView === 'redGreenExample'){
sessionStorage.setItem('frontBackEdit',detailDom.editCanvasBackFront.getJSON())
canvasJSON = sessionStorage.getItem('sketchEdit');
}
detailData.canvasLoad = false
// detailData.canvasLoad = false
detailData.currentView = str
if(canvasJSON){
detailData.canvasLoad = true
// detailData.canvasLoad = true
nextTick(()=>{
if(detailData.currentView === 'redGreenExample'){
detailDom.editCanvas.loadJSON(canvasJSON)
@@ -149,28 +151,38 @@ export default defineComponent({
if(detailData.currentView === 'redGreenExample'){
nextTick(()=>{
setCanvas(detailData.selectDetail?.undividedLayerWithSinglePrint || detailData.selectDetail.undividedLayer || detailData.selectDetail.path).then(()=>{
detailData.canvasLoad = true
// detailData.canvasLoad = true
})
})
}else{
nextTick(()=>{
setCanvas(detailData.frontBack.front[detailData.imgDomIndex].maskUrl).then(()=>{
detailData.canvasLoad = true
// detailData.canvasLoad = true
})
})
}
}
}
const privewDetail = async (oldSelectDetail = detailData.selectDetail)=>{
const updateOtherLayers = (obj:any)=>{
if(!detailDom.editCanvas)return
return new Promise(async (res,reject)=>{
console.log(obj,'====')
await detailDom?.editCanvas.updateOtherLayers(obj)
res('')
})
}
const privewDetail = async (oldSelectDetail = detailData.selectDetail)=>{
// if(!detailDom.editCanvas)return
return new Promise((res,reject)=>{
detailDom.editCanvas.exportImage({isContainBg:false,isContainFixed:false}).then((rv)=>{
detailDom.editCanvas.exportImage({isContainFixedOther:true,isContainFixed:true}).then((rv)=>{
if(oldSelectDetail?.partialDesign)oldSelectDetail.partialDesign.partialDesignBase64 = rv
res('')
})
})
}
const getCanvasElement = ()=>{
if(!detailDom?.editCanvas)return ''
return detailDom?.editCanvas.exportExtraInfo();
}
const setFrontBackColor = (data:any)=>{
detailDom.editFrontBack.setBackground(data)
@@ -258,7 +270,8 @@ export default defineComponent({
const canvasInit = (value:any)=>{
detailData.canvasInstance = value
detailData.getCanvasIfEdit.fun = getCanvasLength
detailData.isShowMark = false
detailData.isShowMark = false
console.log('初始化完成')
}
const getCanvasLength = ()=>{
return detailData.canvasInstance?.commandManager?.undoStack?.length
@@ -297,14 +310,21 @@ export default defineComponent({
});
})
}
let time = null as any
const changeCanvas = ()=>{
clearTimeout(time)
time = setTimeout(()=>{
saveCanvas('auto')
},3000)
// let time = null as any
// const changeCanvas = ()=>{
// clearTimeout(time)
// time = setTimeout(()=>{
// saveCanvas('auto')
// },3000)
// }
const canvasLoadJsonSuccess = async ()=>{
let otherData = await props.updateOtherLayers()
updateOtherLayers(otherData)
}
const submitBase64Data = ()=>{
return detailDom.generalMiniCanvas.submitBase64Data()
}
onBeforeUnmount(()=>{
let front = detailData.frontBack.front[detailData.imgDomIndex]
let back = detailData.frontBack.back[detailData.imgDomIndex]
@@ -353,7 +373,10 @@ export default defineComponent({
frontBackChange,
canvasInit,
saveCanvas,
changeCanvas,
getCanvasElement,
updateOtherLayers,
canvasLoadJsonSuccess,
submitBase64Data,
}
},
provide() {
@@ -434,7 +457,15 @@ export default defineComponent({
flex: 1;
overflow: hidden;
position: relative;
.canvas,.editFrontBack{
.canvas{
opacity: 0;
position: absolute;
&.active{
opacity: 1;
position: relative;
}
}
.canvas,.editFrontBack,.editSketch{
width: 100%;
height: 100%;
}

View File

@@ -61,6 +61,7 @@ import SelectImages from '@/component/common/SelectImages.vue'
import upload from './upload.vue'
import pallet from './pallet.vue'
import { select } from 'three/tsl';
export default defineComponent({
components:{
upload,pallet,SelectImages
@@ -99,13 +100,18 @@ export default defineComponent({
tcxToColor:'',
})
watch(()=>colorData.selectColor,async (newVal,oldVal)=>{
console.log(newVal)
if(newVal.rgba && newVal.rgba?.r){
let data:any = await getColorName(newVal.rgba)
newVal.name = data.name
newVal.tcx = data.tcx
colorData.colorList.list[colorData.selectDetail.id][colorData.colorList.index] = newVal
data.rgba = newVal.rgba
if(newVal.gradient){
data.gradient = newVal.gradient
}
let value = {
data:newVal,
data:data,
str:'color'
}
store.commit('DesignDetail/setNewDetail',value)
@@ -141,7 +147,6 @@ export default defineComponent({
JSON.stringify(colorData.selectDetail.color.gradient) == JSON.stringify(color?.gradient)
&& colorData.selectDetail.color.rgba?.r
){
console.log('---',index)
isNoSelect = true
colorData.selectColor = item
colorData.colorList.index = index
@@ -190,52 +195,11 @@ export default defineComponent({
colorData.colorList.index = num
colorData.colorList.list[newVal][num] = item
}
// for (let index = 0; index < 9; index++) {
// colorData.allBoardData.colorBoards
// let item:any = {}
// item = colorData.allBoardData.colorBoards[index]
// if(
// colorData.allBoardData.colorBoards?.[index] &&
// colorData.selectDetail.color.rgba?.r == colorData.allBoardData.colorBoards?.[index]?.rgba?.r &&
// colorData.selectDetail.color.rgba?.g == colorData.allBoardData.colorBoards?.[index]?.rgba?.g &&
// colorData.selectDetail.color.rgba?.b == colorData.allBoardData.colorBoards?.[index]?.rgba?.b &&
// JSON.stringify(colorData.selectDetail.color.gradient) == JSON.stringify(colorData.allBoardData.colorBoards?.[index]?.gradient)
// && colorData.selectDetail.color.rgba?.r
// ){
// console.log(13212)
// if(!isOneChecked){
// isOneChecked = true
// if(colorData.allBoardData.colorBoards?.[index].gradient){
// item.gradient = colorData.allBoardData.colorBoards?.[index].gradient
// }
// colorData.selectColor = item
// colorData.colorList.index = index
// }
// }else if(colorData.selectDetail.color.rgba?.r){
// if(!isNoSelect){
// item = {
// hex:rgbaToHex([colorData.selectDetail.color.rgba.r,colorData.selectDetail.color.rgba.g,colorData.selectDetail.color.rgba.b]),
// id:colorData.selectDetail.color.id,
// rgba:{
// r:colorData.selectDetail.color.rgba.r,
// g:colorData.selectDetail.color.rgba.g,
// b:colorData.selectDetail.color.rgba.b,
// },
// tcx:colorData.selectDetail.color.tcx,
// name:colorData.selectDetail.color.name,
// }
// if(colorData.selectDetail.color.gradient){
// item.gradient = colorData.selectDetail.color.gradient
// }
// colorData.colorList.index = index
// colorData.selectColor = item
// isNoSelect = true
// }else{
// item = {}
// }
// }
// colorData.colorList.list[newVal].push(item)
// let newData = colorData.selectDetail?.newDetail?.color
// console.log(newData,colorData.selectColor)
// if(newData){
// newData.hex = rgbaToHex([newData.rgba.r,newData.rgba.g,newData.rgba.b])
// colorData.selectColor = newData
// }
},{immediate: true})
const selectImgItem = ()=>{
@@ -281,7 +245,6 @@ export default defineComponent({
})
}
const handleShowListChange=(val:boolean)=>{
console.log('val',val)
showLibrary.value = !val
}

View File

@@ -2,7 +2,7 @@
<div class="detailLeft">
<sketch v-show="currentDetailType == 'sketch'" ref="sketch" @addDetail="addDetail"></sketch>
<print v-show="currentDetailType == 'print'"></print>
<color v-if="currentDetailType == 'color'"></color>
<color v-if="currentDetailType == 'color'" :key="detailLeftColorKey"></color>
<element v-show="currentDetailType == 'element'"></element>
<accessory v-show="currentDetailType == 'accessory'"></accessory>
<models v-show="currentDetailType == 'models'"></models>
@@ -26,6 +26,12 @@ export default defineComponent({
components:{
sketch,print,color,element,models,accessory
},
props:{
detailLeftColorKey:{
type:Number,
default:0,
},
},
emit:['addDetail'],
setup(props,{emit}) {
const store = useStore();

View File

@@ -69,7 +69,6 @@ export default defineComponent({
file.designType = file?.resData?.designType
}
}
// store.commit('DesignDetail/setNewDetail',file.resData)
emit('selectImgItem',file)
}

View File

@@ -98,7 +98,6 @@ export default defineComponent({
})
const selectImgItem = (file:any)=>{
// let data = JSON.parse(JSON.stringify(file))
// store.commit('DesignDetail/setNewDetail',file)
emit('selectImgItem',file)
}
const upFileUploadChange = (data:any)=>{

View File

@@ -88,7 +88,6 @@ export default defineComponent({
})
const selectImgItem = (file:any)=>{
// let data = JSON.parse(JSON.stringify(file))
// store.commit('DesignDetail/setNewDetail',file)
emit('selectImgItem',file)
}
const setUploadImgList = (list:any)=>{

View File

@@ -51,6 +51,25 @@
:tip-formatter="formatter"
>
</a-slider>
<a-popover
trigger="click"
destroyTooltipOnHide
:title="t('Canvas.repeatSetting')"
>
<template #content>
<repeat-setting
:object="overallDetail"
@inputFillAngle="inputFillAngle"
@inputFillOffset="inputFillOffset"
@inputFillScale="inputFillScale"
@inputFill_Gap="
(x, y) => inputFill_Gap(x, y)"
/>
</template>
<div class="btn">
<i class="iconfont icon-gengduo"></i>
</div>
</a-popover>
</div>
<div class="editPrintElementBox">
<div class="designOpenrtion_centent" id="designOpenrtionCentent">
@@ -70,9 +89,10 @@
</div>
</div>
<!-- <img :src="selectDetail.path" alt="" class="designOpenrtion_sketch" ref="sketchImg"> -->
<img :src="selectDetail?.undividedLayer?selectDetail.undividedLayer:selectDetail.path" alt="" class="designOpenrtion_sketch" ref="sketchImg" @load="()=>isSketchLoad = true">
<div class="designOpenrtion_btn">
<ul v-if="stateOverallSingle == 'single'" v-for="item,index in printStyleList[type][stateOverallSingle]" :key="item" :class="{active:item?.pattern.designOpenrtionBtn?item?.pattern.designOpenrtionBtn:false}" class="designOpenrtion_Mousingle" :style="item?.pattern.style" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))">
<img :src="selectDetail?.undividedLayerWithSinglePrint?selectDetail.undividedLayerWithSinglePrint:selectDetail.path" alt="" class="designOpenrtion_sketch" ref="sketchImg" @load="()=>isSketchLoad = true">
<img :src="selectDetail.sketchMask" alt="" class="designOpenrtion_sketchMask" ref="sketchMask">
<div class="designOpenrtion_btn" v-if="stateOverallSingle == 'single'" >
<ul v-for="item,index in printStyleList[type][stateOverallSingle]" :key="item" :class="{active:item?.pattern.designOpenrtionBtn?item?.pattern.designOpenrtionBtn:false}" class="designOpenrtion_Mousingle" :style="item?.pattern.style" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))">
<li class="designOpenrtion_btn_top" @mousedown.stop="itemSizeMousedown('top',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('top',getMousePosition($event,true))"></li>
<li class="designOpenrtion_btn_bottom" @mousedown.stop="itemSizeMousedown('bottom',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('bottom',getMousePosition($event,true))"></li>
<li class="designOpenrtion_btn_left" @mousedown.stop="itemSizeMousedown('left',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('left',getMousePosition($event,true))"></li>
@@ -82,12 +102,15 @@
<img src="../../../assets/images/homePage/cuowu.svg" alt="">
</li>
</ul>
<div v-show="stateOverallSingle != 'single'"></div>
<!-- <div v-show="stateOverallSingle != 'single'"></div>
<ul v-if="stateOverallSingle != 'single' && printStyleList[type][stateOverallSingle][0]" class="designOpenrtion_Mouoverall active" :style="'left:'+printStyleList[type][stateOverallSingle][0]?.pattern?.style?.left+';top:'+printStyleList[type][stateOverallSingle][0]?.pattern?.style?.top+';'" @mousedown.stop="itemMoveMousedown(0,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(0,getMousePosition($event,true))">
<i class="fi fi-rr-arrows animtion1"></i>
<i class="fi fi-rr-arrows animtion2"></i>
<li class="designOpenrtion_rotote" v-rotote.stop="[0,printStyleList[type][stateOverallSingle][0]?.pattern?.transform,type]"></li>
</ul>
</ul> -->
</div>
<div class="designOpenrtion_pingpu" v-else>
<pingpu v-if="overallDetail.url" v-bind="overallDetail"></pingpu>
</div>
</div>
</div>
@@ -106,8 +129,13 @@ import { defineComponent,computed,ref,onMounted,nextTick,watch,toRefs, reactive,
// import setDesignItem from '@/component/Detail/setDesignItem2.vue'
import { useStore } from "vuex";
import { getMousePosition } from "@/tool/mdEvent";
import { sketchToMask } from "@/tool/util";
import pingpu from '@/component/Canvas/OverallCanvas/index.vue'
import RepeatSetting from "./overallSetting/RepeatSetting.vue";
import { useI18n } from 'vue-i18n'
export default defineComponent({
components:{
pingpu,RepeatSetting
},
props: {
type: {
@@ -116,6 +144,7 @@ export default defineComponent({
}
},
setup(props,{emit}) {
const { t } = useI18n()
const store = useStore();
const editPrintElementDom = reactive({
imgDom:null as any,
@@ -126,6 +155,15 @@ export default defineComponent({
currentDetailType:computed(()=>store.state.DesignDetail.currentDetailType),
currentPrintElement:computed(()=>store.state.DesignDetail.currentPrintElement),
systemDesignerPercentage:0,
overallDetail:{
url: '',
offsetX: 0, // px
offsetY: 0, // px
angle: 0, // 角度
scale: 100, // %
gapX: 0, // px
gapY: 0, // px
},
printStyleList:{
print:{
single:[],
@@ -215,6 +253,7 @@ export default defineComponent({
path:data.url,
priority:editPrintElementData.printZIndex,
scale,
globalCompositeOperation:'',
}
getItemPosition(item)
setItemPosition()
@@ -227,6 +266,7 @@ export default defineComponent({
let scale,location
let style = item.pattern.style
let sketchWH = editPrintElementData.sketchWH.scale
let overallDetail = {}
if(item.ifSingle){
scale = [style.width.replace(/px/g,'')/(editPrintElementData.sketchWH.width),(style.height.replace(/px/g,'')/(editPrintElementData.sketchWH.height))]
location = [style.left.replace(/px/g,'')*sketchWH[0],style.top.replace(/px/g,'')*sketchWH[1]]
@@ -237,6 +277,14 @@ export default defineComponent({
scale =[ editPrintElementData.systemDesignerPercentage/100, editPrintElementData.systemDesignerPercentage/100]
// scale = [item.pattern.style.width/item.pattern.style.height,item.pattern.style.height/item.pattern.style.width]
// location = [item.pattern.style.left,item.pattern.style.top]
overallDetail = {
offsetX: editPrintElementData.overallDetail.offsetX, // px
offsetY: editPrintElementData.overallDetail.offsetY, // px
angle: editPrintElementData.overallDetail.angle, // 角度
scale: editPrintElementData.overallDetail.scale, // %
gapX: editPrintElementData.overallDetail.gapX, // px
gapY: editPrintElementData.overallDetail.gapY, // px
}
}
let value ={
angle : item.pattern.transform.rotateZ,
@@ -249,6 +297,9 @@ export default defineComponent({
path:item.path,
minIOPath:item.minIOPath,
ifSingle:!!item.ifSingle,
globalCompositeOperation:'',
object:null,
// ...overallDetail,
}
return value
}
@@ -316,6 +367,15 @@ export default defineComponent({
editPrintElementData.printStyleList[props.type].single.push(item)
}else{
editPrintElementData.printStyleList[props.type].overall = []
editPrintElementData.overallDetail = {
url: item.path,
offsetX: 0, // px
offsetY: 0, // px
angle: 0, // 角度
scale: 100, // %
gapX: 0, // px
gapY: 0, // px
}
editPrintElementData.printStyleList[props.type].overall.push(item)
}
@@ -337,9 +397,9 @@ export default defineComponent({
if(!editPrintElementData.selectDetail.printObject.prints)return
let state = true
// editPrintElementData.stateOverallSingle = 'single'
let arr:any = editPrintElementData.selectDetail.printObject.prints
let arr:any = editPrintElementData.selectDetail.newDetail?.print || editPrintElementData.selectDetail.printObject.prints
if(props.type == 'element'){
arr = editPrintElementData.selectDetail.trims.prints
arr = editPrintElementData.selectDetail.newDetail?.element || editPrintElementData.selectDetail.trims.prints
}
if(editPrintElementData.selectDetail.newDetail?.[editPrintElementData.currentDetailType]){
arr = editPrintElementData.selectDetail.newDetail[editPrintElementData.currentDetailType]
@@ -384,8 +444,28 @@ export default defineComponent({
single:[],
overall:[],
}
if(!editPrintElementData.selectDetail.sketchMask){
sketchToMask(newVal).then((res:any)=>{
editPrintElementData.selectDetail.sketchMask = res
})
}
setPosition()
},{immediate: true,})
watch(()=>editPrintElementData.stateOverallSingle,(newVal)=>{
if(newVal == 'overall'){
let overallPrint = editPrintElementData.printStyleList[props.type][editPrintElementData.stateOverallSingle]?.[0]
if(!overallPrint?.path)return
editPrintElementData.overallDetail = {
url: overallPrint.path,
offsetX: overallPrint.offsetX || 0, // px
offsetY: overallPrint.offsetY || 0, // px
angle: overallPrint.angle || 0, // 角度
scale: overallPrint.scale || 100, // %
gapX: overallPrint.gapX || 0, // px
gapY: overallPrint.gapY || 0, // px
}
}
})
//设置移动
const itemMoveMousedown = (index:number,event:any)=>{
if (event.target.tagName === 'IMG' || event.target.nodeName === 'IMG')return
@@ -738,6 +818,20 @@ export default defineComponent({
collItemSize.prentWidth = (collItemSize.padding + remValue) * elArr.length + 'px'
moveItem()
}
const inputFillAngle = (angle:any)=>{
editPrintElementData.overallDetail.angle = angle
}
const inputFillOffset = (offset:any)=>{
editPrintElementData.overallDetail.offsetX = offset.left
editPrintElementData.overallDetail.offsetY = offset.top
}
const inputFillScale = (scale:any)=>{
editPrintElementData.overallDetail.scale = scale
}
const inputFill_Gap = (x:any,y:any)=>{
editPrintElementData.overallDetail.gapX = x
editPrintElementData.overallDetail.gapY = y
}
onMounted(()=>{
if(props.type == 'element'){
editPrintElementData.stateOverallSingle = 'single'
@@ -747,6 +841,7 @@ export default defineComponent({
previewDetailPrintData()
})
return{
t,
getMousePosition,
...toRefs(editPrintElementDom),
...toRefs(editPrintElementData),
@@ -760,6 +855,10 @@ export default defineComponent({
clearOverall,
designMousedown,
navDelete,
inputFillAngle,
inputFillOffset,
inputFillScale,
inputFill_Gap,
}
},
directives:{
@@ -983,7 +1082,13 @@ export default defineComponent({
// height: 100%;
max-width: 100%;
// width: 100%;
&.designOpenrtion_sketchMask{
position: absolute;
z-index: 3;
top: 0;
left: 0;
pointer-events: none;
}
}
.designOpenrtion_sketch_mask{
z-index: 2;
@@ -1013,8 +1118,13 @@ export default defineComponent({
}
}
}
.designOpenrtion_pingpu{
width: 100%;
height: 100%;
z-index: 2;
}
.designOpenrtion_btn{
z-index: 3;
z-index: 99;
>div{
width: 100%;
height: 100%;

View File

@@ -0,0 +1,124 @@
<template>
<div class="repeat-setting">
<div class="repeat-setting-item">
<span class="label">{{ t("Canvas.angle") }}</span>
<angle-tool
:angle="angle"
@input="(e) => emit('inputFillAngle', e)"
/>
</div>
<p></p>
<div class="repeat-setting-item">
<span class="label">{{ t("Canvas.scale") }}</span>
<slider
:min="1"
:max="1000"
:step="1"
is-input
:tipFormatter="(v) => `${scale}%`"
:value="scale"
@input="inputFillScale"
/>
</div>
<p></p>
<div class="repeat-setting-item">
<span class="label">Gap X</span>
<slider
:min="0"
:max="1000"
:step="1"
is-input
:tipFormatter="(v) => `${v}px`"
:value="gapX"
@input="(e) => emit('inputFill_Gap', e, gapY)"
@change="(e) => emit('changeFill_Gap', e, gapY)"
/>
</div>
<p></p>
<div class="repeat-setting-item">
<span class="label">Gap Y</span>
<slider
:min="0"
:max="1000"
:step="1"
is-input
:tipFormatter="(v) => `${v}px`"
:value="gapY"
@input="(e) => emit('inputFill_Gap', gapX, e)"
@change="(e) => emit('changeFill_Gap', gapX, e)"
/>
</div>
<p></p>
<div class="repeat-setting-item">
<span class="label">{{ t("Canvas.offset") }}</span>
<offset-tool
:top="(props.object.fill?.offsetY / props.object.height) * 100"
:left="(props.object.fill?.offsetX / props.object.width) * 100"
@input="(e) => emit('inputFillOffset', e)"
@change="(e) => emit('changeFillOffset', e)"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits, computed } from "vue";
import AngleTool from "./tools/AngleTool.vue";
import OffsetTool from "./tools/OffsetTool.vue";
import Slider from "./tools/Slider.vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const props = defineProps({
object: {
required: true,
type: Object,
},
});
const angle = computed(() => props.object?.angle || 0);
const scale = computed(() => {
// let scaleValue = props.object?.scale/10;
// return props.object?.scale/10;
return props.object?.scale
});
const gapX = computed(() => props.object?.gapX || 0);
const gapY = computed(() => props.object?.gapY || 0);
const emit = defineEmits([
"inputFillAngle",
"changeFillAngle",
"inputFillOffset",
"changeFillOffset",
"inputFillScale",
"changeFillScale",
"inputFill_Gap",
"changeFill_Gap",
]);
const inputFillScale = (e) => {
// const scale = e * 10;
emit("inputFillScale", e);
};
</script>
<style scoped lang="less">
.repeat-setting {
user-select: none;
> .repeat-setting-item {
display: flex;
align-items: center;
//虚线
> .label {
min-width: 50px;
font-size: 14px;
}
> .angle-tool {
width: 120px;
}
}
> p {
margin: 10px 0;
width: 100%;
height: 0;
border-bottom: 1px dashed #e5e5e5;
}
}
</style>

View File

@@ -0,0 +1,122 @@
<template>
<div class="angle-tool">
<div
ref="dishRef"
class="dish"
@mousedown.stop="mousedown"
@touchmove.stop="mousedown"
>
<div class="pointer" :style="{ transform: `rotate(${angle}deg)` }">
<span></span>
</div>
</div>
<div class="input">
<input type="number" v-model="angle" @input="onInput" @change="onChange" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits, watch } from "vue";
import { calculateAngle } from "@/component/Canvas/CanvasEditor/utils/helper";
// Props
const props = defineProps({
angle: {
type: Number,
default: 0,
},
});
const emit = defineEmits(["change", "input"]);
const angle = ref(props.angle);
watch(() => props.angle, (value) => {
angle.value = value;
});
const dishRef = ref<HTMLDivElement>();
const mousedown = (e: MouseEvent | TouchEvent) => {
const mousemove = (e: MouseEvent | TouchEvent) => {
if (!dishRef.value) return;
const { left, top, width, height } =
dishRef.value.getBoundingClientRect();
const centerX = left + width / 2;
const centerY = top + height / 2;
const { clientX, clientY } = e?.touches?.[0] || e;
angle.value = calculateAngle(centerX, centerY, clientX, clientY, true);
console.log(angle.value)
onInput();
};
mousemove(e);
const mouseup = () => {
onChange();
document.removeEventListener("mousemove", mousemove);
document.removeEventListener("touchmove", mousemove);
document.removeEventListener("mouseup", mouseup);
document.removeEventListener("touchend", mouseup);
};
document.addEventListener("mousemove", mousemove);
document.addEventListener("touchmove", mousemove);
document.addEventListener("mouseup", mouseup);
document.addEventListener("touchend", mouseup);
};
const onInput = () => emit("input", angle.value);
var changeTime: any = null;
const onChange = () => {
clearTimeout(changeTime);
changeTime = setTimeout(() => emit("change", angle.value), 500);
};
// var angleTime = null;
// watch(angle, (value) => {
// emit("input", value);
// clearTimeout(angleTime);
// angleTime = setTimeout(() => emit("change", value), 50);
// });
// defineExpose({
// open,
// close,
// });
</script>
<style scoped lang="less">
.angle-tool {
display: flex;
align-items: center;
width: 100%;
> .dish {
width: 24px;
height: 24px;
border: 1px solid #000;
border-radius: 50%;
cursor: pointer;
> .pointer {
pointer-events: none;
user-select: none;
position: relative;
width: 100%;
height: 100%;
> span {
position: absolute;
top: 10%;
left: 50%;
transform: translate(-50%, 0);
width: 35%;
height: 35%;
background-color: #000;
border-radius: 50%;
}
}
}
> .input {
margin-left: 5px;
font-size: 14px;
color: #000;
flex: 1;
// min-width: 45px;
// max-width: 80px;
// width: 50px;
> input {
width: 100%;
border-radius: 3px;
outline: none;
}
}
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<a-select
class="my-select"
:size="size"
@change="change"
:defaultValue="defaultValue"
@dropdownVisibleChange="dropdownVisibleChange"
>
<a-select-option
v-for="v in list"
:key="v.value"
:value="v.value"
:title="v.tip"
@mouseover.stop.prevent="mouseover(v)"
@mouseleave="mouseleave(v)"
>{{ v.label }}</a-select-option
>
</a-select>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits, watch } from "vue";
const props = defineProps({
defaultValue: {
default: "",
},
list: {
type: Array,
default: () => [],
},
size: {
type: String,
default: "small",
},
});
const emit = defineEmits(["change", "active"]);
const isChange = ref(false);
const initValue = ref(props.defaultValue);
const activeValue = ref(props.defaultValue);
const timeout = ref(null);
const mouseover = (v) => {
clearTimeout(timeout.value);
if (v.value === activeValue.value) return;
emit("active", v.value, activeValue.value);
activeValue.value = v.value;
};
const mouseleave = () => {
clearTimeout(timeout.value);
timeout.value = setTimeout(() => {
dropdownVisibleChange(false);
}, 100);
};
const change = (v) => {
isChange.value = true;
emit("change", v, initValue.value);
};
const dropdownVisibleChange = (v) => {
if (v) {
isChange.value = false;
initValue.value = props.defaultValue;
} else if (!isChange.value) {
emit("active", initValue.value, activeValue.value);
activeValue.value = initValue.value;
}
};
</script>

View File

@@ -0,0 +1,190 @@
<template>
<div class="offset-tool">
<div
class="dish"
@mousedown="mousedown"
@touchstart="mousedown"
ref="dishRef"
>
<span
:style="{ top: data.top + '%', left: data.left + '%' }"
></span>
</div>
<input
class="top"
type="range"
:min="0"
:max="100"
:step="0.1"
v-model="data.top"
@input="onInput"
@change="onChange"
/>
<input
class="left"
type="range"
:min="0"
:max="100"
:step="0.1"
v-model="data.left"
@input="onInput"
@change="onChange"
/>
<span class="tip"
>x:{{ tofix(data.left) }}% y:{{ tofix(data.top) }}%</span
>
</div>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits, watch } from "vue";
const props = defineProps({
top: {
type: Number,
default: 50,
},
left: {
type: Number,
default: 50,
},
});
const tofix = (v: number | string) => Number(Number(v).toFixed(1));
const emit = defineEmits(["change", "input"]);
const data = reactive({
top: tofix(props.top),
left: tofix(props.left),
});
watch(
() => props.top,
(v) => (data.top = tofix(v))
);
watch(
() => props.left,
(v) => (data.left = tofix(v))
);
const dishRef = ref<HTMLDivElement>();
const mousedown = (e: MouseEvent | TouchEvent) => {
if (!dishRef.value) return;
const mousemove = (e: MouseEvent | TouchEvent) => {
if (!dishRef.value) return;
const { left, top, width, height } =
dishRef.value.getBoundingClientRect();
const X = e.clientX || (e as TouchEvent).touches[0].clientX;
const Y = e.clientY || (e as TouchEvent).touches[0].clientY;
var x = ((X - left) / width) * 100;
var y = ((Y - top) / height) * 100;
if (x < 0) x = 0;
if (x > 100) x = 100;
if (y < 0) y = 0;
if (y > 100) y = 100;
data.left = tofix(x);
data.top = tofix(y);
onInput();
};
mousemove(e);
const mouseup = () => {
onChange();
document.removeEventListener("mousemove", mousemove);
document.removeEventListener("touchmove", mousemove);
document.removeEventListener("mouseup", mouseup);
document.removeEventListener("touchend", mouseup);
};
document.addEventListener("mousemove", mousemove);
document.addEventListener("touchmove", mousemove);
document.addEventListener("mouseup", mouseup);
document.addEventListener("touchend", mouseup);
};
const onInput = () => emit("input", { ...data });
var changeTime: any = null;
const onChange = () => {
clearTimeout(changeTime);
changeTime = setTimeout(() => emit("change", { ...data }), 500);
};
// var offsetTime = null;
// watch(data, (v) => {
// const obj = { ...v };
// emit("input", obj);
// clearTimeout(offsetTime);
// offsetTime = setTimeout(() => emit("change", obj), 50);
// });
// defineExpose({
// open,
// close,
// });
</script>
<style scoped lang="less">
.offset-tool {
width: 125px;
height: 125px;
display: flex;
position: relative;
overflow: hidden;
--gap: 15px;
> .dish {
margin: var(--gap) 0 0 var(--gap);
flex: 1;
border: 1px solid #000;
border-radius: 5px;
cursor: pointer;
position: relative;
background-color: #fff;
> span {
pointer-events: none;
user-select: none;
position: absolute;
top: 0%;
left: 0%;
transform: translate(-50%, -50%);
width: 8px;
height: 8px;
background-color: #000;
border-radius: 50%;
}
}
> .tip {
position: absolute;
right: 4px;
bottom: 0;
font-size: 10px;
pointer-events: none;
user-select: none;
color: #666;
}
> input.left {
right: 0;
}
> input.top {
bottom: 0;
left: 0;
transform-origin: left bottom;
transform: rotate(90deg) translateX(-100%);
}
> input {
position: absolute;
width: calc(100% - var(--gap));
-webkit-appearance: none;
appearance: none;
height: 8px;
border-radius: 8px;
background: rgba(0, 0, 0, 0.1); /* 更柔和的颜色 */
// outline: none;
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 8px;
height: 8px;
border-radius: 50%;
background: #4285f4; /* 蓝色滑块 */
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
&::-webkit-slider-thumb:hover {
background: #3b77db;
transform: scale(1.1);
}
}
}
</style>

View File

@@ -0,0 +1,160 @@
<template>
<div class="slider">
<div class="input-range">
<span
class="tip"
:style="{
'--progress': (value - props.min) / (props.max - props.min),
}"
>{{ props.tipFormatter(value) }}</span
>
<input
type="range"
v-model="value"
:min="props.min"
:max="props.max"
:step="props.step"
@input="onInput"
@change="onChange"
/>
</div>
<div class="input" v-show="isInput">
<input
type="number"
v-model="value"
:min="props.min"
:max="props.max"
:step="props.step"
@input="onInput"
@change="onChange"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits, watch } from "vue";
const props = defineProps({
value: {
type: Number,
default: 0,
},
min: {
type: Number,
default: 0,
},
max: {
type: Number,
default: 100,
},
step: {
type: Number,
default: 1,
},
tipFormatter: {
type: Function,
default: (v) => v,
},
isInput: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["change", "input"]);
const value = ref(props.value);
watch(
() => props.value,
(v) => (value.value = v)
);
const onInput = () => emit("input", Number(value.value));
var changeTime: any = null;
const onChange = () => {
clearTimeout(changeTime);
changeTime = setTimeout(() => emit("change", Number(value.value)), 500);
};
</script>
<style scoped lang="less">
.slider {
position: relative;
display: flex;
align-items: center;
--input-thumb-size: 12px;
width: 150px;
// &:focus-within,
&:hover {
> .input-range > .tip {
display: block;
}
}
> .input-range {
position: relative;
flex: 2;
> input {
width: 100%;
-webkit-appearance: none;
appearance: none;
height: 5px;
border-radius: 5px;
background: rgba(0, 0, 0, 0.1); /* 更柔和的颜色 */
outline: none;
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: var(--input-thumb-size);
height: var(--input-thumb-size);
border-radius: 50%;
background: #4285f4; /* 蓝色滑块 */
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
&::-webkit-slider-thumb:hover {
background: #3b77db;
transform: scale(1.1);
}
}
> .tip {
position: absolute;
font-size: 10px;
pointer-events: none;
user-select: none;
color: #666;
top: 0;
left: calc(
(100% - var(--input-thumb-size)) * var(--progress) +
var(--input-thumb-size) / 2
);
transform: translate(-50%, -100%);
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 0.4rem 0.8rem;
border-radius: 0.4rem;
font-size: 1.2rem;
white-space: nowrap;
pointer-events: none;
display: none;
&::after {
content: "";
position: absolute;
top: 97%;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid rgba(0, 0, 0, 0.8);
}
}
}
> .input {
flex: 1;
margin-left: 10px;
> input {
border-radius: 3px;
width: 100%;
}
}
}
</style>

View File

@@ -4,9 +4,9 @@
<div class="back" v-show="isEditPattern.value">
<i class="fi fi-br-angle-left" @click="setBack"></i>
</div>
<modelNav @canvasReload="()=>$emit('canvasReload')" @addSketch="()=>$emit('addSketch')" @sketchSysToLibrary="()=>$emit('sketchSysToLibrary')" @deleteItem="deleteItem"></modelNav>
<modelNav ref="modelNav" @canvasReload="()=>$emit('canvasReload')" @addSketch="()=>$emit('addSketch')" @sketchSysToLibrary="()=>$emit('sketchSysToLibrary')" @deleteItem="deleteItem"></modelNav>
</div>
<div class="modelindex_right">
<div class="modelindex_right" ref="modelindexRight">
<div class="detail_btn">
<!-- 全屏 -->
<!-- <i class="fi fi-bs-expand-arrows-alt" @click="showDesignImgDetail('2')"></i> -->
@@ -14,7 +14,7 @@
<i class="icon iconfont icon-chehui" @click="revocation"></i>
<i class="icon iconfont icon-fanchehui" @click="oppositeRevocation"></i>
<!-- 编辑 -->
<i class="fi fi-rs-pencil-paintbrush" :title="$t('DesignDetail.editSketchTitle')" :class="{'pointerEventsNone':!selectDetail?.id}" @click="()=>$emit('addDetail')"></i>
<i class="fi fi-rs-pencil-paintbrush" :title="$t('DesignDetail.editSketchTitle')" :class="{'pointerEventsNone':!selectDetail?.id,active:isEditPattern.value == 'editSketch'}" @click="showDesignImgDetail('editSketch')"></i>
<i class="fi fi-rr-edit" :title="$t('DesignDetail.editTitle')" :class="{active:isEditPattern.value == 'canvasEditor','pointerEventsNone':!selectDetail?.id}" @click="showDesignImgDetail('canvasEditor')"></i>
<i class="icon iconfont icon-clothes" :title="$t('Canvas.editFrontBack')" style="font-size: 3.2rem;" @click="showDesignImgDetail('redGreenExample')" :class="{active:isEditPattern.value == 'redGreenExample','pointerEventsNone':!selectDetail?.id}"></i>
@@ -51,7 +51,7 @@ export default defineComponent({
components:{
position,modelNav
},
emits:['detailEdit','canvasReload','addSketch','revocation','oppositeRevocation','modelOnLoad','sketchSysToLibrary','addDetail'],
emits:['detailEdit','canvasReload','addSketch','revocation','oppositeRevocation','modelOnLoad','sketchSysToLibrary'],
setup(props,{emit}) {
const {t} = useI18n()
const store = useStore();
@@ -68,36 +68,40 @@ export default defineComponent({
const getDetailListDom = reactive({
libraryList:null as any,
position:null as any,
modelindexRight:null as any,
modelNav:null as any,
})
const getSubmitData = (value:any,boolean)=>{
return getDetailListDom.position.getSubmitData(value,boolean)
}
const showDesignImgDetail = (str:any)=>{
new Promise((resolve, reject) => {
if(
getDetailListData.isEditPattern.value&&
detailData?.getCanvasIfEdit?.fun&&detailData?.getCanvasIfEdit?.fun() > 0
){
Modal.confirm({
title: t('collectionModal.jsContent2'),
icon: createVNode(ExclamationCircleOutlined),
okText: 'Yes',
cancelText: 'No',
mask:false,
centered:true,
onOk() {
resolve(true)
emit('detailEdit',str)
},
onCancel(){
resolve(false)
}
});
}else{
resolve(true)
emit('detailEdit',str)
}
})
emit('detailEdit',str)
// new Promise((resolve, reject) => {
// if(
// getDetailListData.isEditPattern.value&&
// detailData?.getCanvasIfEdit?.fun&&detailData?.getCanvasIfEdit?.fun() > 0
// ){
// Modal.confirm({
// title: t('collectionModal.jsContent2'),
// icon: createVNode(ExclamationCircleOutlined),
// okText: 'Yes',
// cancelText: 'No',
// mask:false,
// centered:true,
// onOk() {
// resolve(true)
// emit('detailEdit',str)
// },
// onCancel(){
// resolve(false)
// }
// });
// }else{
// resolve(true)
// emit('detailEdit',str)
// }
// })
}
const deleteItem = ()=>{
setRevocation()
@@ -117,22 +121,29 @@ export default defineComponent({
}
let time = null as any
const handleResize = ()=>{
const handleResize = ()=>{
clearTimeout(time)
time = setTimeout(()=>{
store.commit('DesignDetail/setDesignDetail',getDetailListData.designDetail)
getDetailListDom.position.updataPosition()
},1000)
getDetailListDom.position?.updataPosition?.()
getDetailListDom.modelNav?.setItemPosition?.()
getDetailListDom.position?.updateRect?.()
},500)
}
const setBack = ()=>{
emit('detailEdit')
showDesignImgDetail(getDetailListData.isEditPattern.value)
}
let observers = null as any
onMounted(()=>{
window.addEventListener('resize', handleResize);
observers = new ResizeObserver(entries => {
for (let entry of entries) {
handleResize()
}
});
observers.observe(getDetailListDom.modelindexRight);
})
onBeforeUnmount(()=>{
window.removeEventListener('resize', handleResize);
observers.disconnect();
})
return{
...toRefs(detailData),

View File

@@ -268,17 +268,8 @@ export default defineComponent({
let allPostition = store.state.Workspace.workspaceAllPosition
return allPostition.find(item => item.value === type)?.name
}
let observers = null as any
onMounted(()=>{
observers = new ResizeObserver(entries => {
for (let entry of entries) {
setItemPosition()
}
});
observers.observe(detailData.modelNavBox);
})
onBeforeUnmount(()=>{
observers.disconnect();
})
return{
...toRefs(detailData),

View File

@@ -1,21 +1,17 @@
<template>
<div class="molepositon" :class="{active:!imgDesignImg}">
<div class="designOpenrtion_imgMask" v-if="frontBack?.body?.path" :style="frontBack?.body?.style">
<div class="designOpenrtion_print" v-for="item,index in frontBack.back" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))" @click="setpitch(item,index)" :style="frontBack.front[index].style">
<div class="designOpenrtion_print" v-for="item,index in frontBack.back" :style="frontBack.front[index].style">
<img :style="item.imageUrl?'':'display:none;'" :src="item.imageUrl" alt="">
</div>
<img class="perview_img" @load="setPrintSize()" ref="detailBody" :key="designDetail.designItemId" :src="frontBack?.body?.path" :style="'width:'+ frontBack?.body?.layersObject?.[0].imageSize?.[0] +';height:' + frontBack?.body?.layersObject?.[0].imageSize?.[0] +';'">
<div class="detail_modal_item_front" v-for="item,index in frontBack.front" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))" @click="setpitch(item,index)" :style="item.style">
<!-- <div class="detail_modal_item_front" ref="target" v-for="item,index in frontBack.front" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))" @click="setpitch(item,index)" :style="item.style">
<img :src="item.imageUrl" alt="">
</div> -->
<div class="detail_modal_item_front" :ref="el => { setElementRef(el, index) }" v-for="item,index in frontBack.front" :class="{'active':item.id == selectDetail?.id}" :style="item.style">
<img :src="item.imageUrl" alt="">
</div>
<div class="designOpenrtion_btnBox">
<ul v-for="item,index in frontBack.front" :key="item" :class="{active:item.designOpenrtionBtn}" class="designOpenrtion_btn" :style="item.style" @mousedown.stop="itemMoveMousedown(index,getMousePosition($event,false))" @touchstart.passive="itemMoveMousedown(index,getMousePosition($event,true))">
<li class="designOpenrtion_btn_top" @mousedown.stop="itemSizeMousedown('top',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('top',getMousePosition($event,true))"></li>
<li class="designOpenrtion_btn_bottom" @mousedown.stop="itemSizeMousedown('bottom',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('bottom',getMousePosition($event,true))"></li>
<li class="designOpenrtion_btn_left" @mousedown.stop="itemSizeMousedown('left',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('left',getMousePosition($event,true))"></li>
<li class="designOpenrtion_btn_right" @mousedown.stop="itemSizeMousedown('right',getMousePosition($event,false))" @touchstart.passive="itemSizeMousedown('right',getMousePosition($event,true))"></li>
</ul>
</div>
<div ref="moveableContainer" class="moveableContainer"></div>
</div>
<div class="designOpenrtion_imgMask" v-if="!frontBack?.body?.path">
<img :src="selectDetail?.undividedLayerWithSinglePrint || selectDetail?.undividedLayer || selectDetail?.path" style="object-fit: cover;" alt="">
@@ -40,8 +36,10 @@ import { Https } from "@/tool/https";
import { useStore } from "vuex";
import { useI18n } from 'vue-i18n'
import { getMousePosition } from "@/tool/mdEvent";
import { Modal,message } from 'ant-design-vue';
import newFollowVue from '@/component/Account/message/newFollow.vue';
import Vue3Moveable from 'vue3-moveable';
import Moveable from 'moveable';
import { parse } from 'vue/compiler-sfc';
import { scale } from 'echarts/types/src/scale/helper.js';
export default defineComponent({
components:{
},
@@ -75,10 +73,7 @@ export default defineComponent({
imgDom:null as any,
direction:'',
})
watch(()=>selectItem.selectDetail,(newValue,oldValue)=>{
if(!newValue && newValue?.id == oldValue?.id)return
selectItem.imgDomIndex = detailData.frontBack.front.findIndex((item:any)=>item.id == newValue.id)
},{immediate: true,})
watch(()=>detailData.frontBack?.body?.path,(newVal)=>{
setPrintSize()
})
@@ -99,7 +94,6 @@ export default defineComponent({
if(entries[0].contentRect.width == 0)return
detailData.observerWH.width = Math.floor(entries[0].contentRect.width)
detailData.observerWH.height = Math.floor(entries[0].contentRect.height)
console.log(detailData.observerWH)
})
detailData.observer.observe(dom)
@@ -131,6 +125,7 @@ export default defineComponent({
}
for (const key in detailData.frontBack.back[index].style) {
if(key == 'zIndex')return
if(key == 'transform')return
let value = detailData.frontBack.back[index].style[key]
if(typeof value !== 'number'){
value = value.replace('px','')
@@ -143,6 +138,7 @@ export default defineComponent({
});
setTimeout(() => {
emit('modelOnLoad')
initMoveableForSelected()
},500);
};
img.src = detailData.frontBack?.body?.path;
@@ -150,203 +146,280 @@ export default defineComponent({
}
const getDetailListDom = reactive({
libraryList:null as any,
moveableContainer:null as any,//控件层
})
//设置尺寸
const itemSizeMousedown = (direction:any,event:any)=>{
selectItem.direction = direction
selectItem.imgDom = document.getElementsByClassName('molepositon')[0].getElementsByClassName("detail_modal_item_front")[selectItem.imgDomIndex]
detailData.frontBack.front[selectItem.imgDomIndex].designOpenrtionBtn = true
let imgDomWH = selectItem.imgDom.getBoundingClientRect()
let li = (document.getElementsByClassName('molepositon')[0].getElementsByClassName("designOpenrtion_btn_top")[0] as any).offsetWidth/2
if(selectItem.direction == 'right' || selectItem.direction == 'bottom'){
detailData.frontBack.front[selectItem.imgDomIndex].centers.left = imgDomWH.x+event.offsetX-li
detailData.frontBack.front[selectItem.imgDomIndex].centers.top = imgDomWH.y+event.offsetY-li
}else{
detailData.frontBack.front[selectItem.imgDomIndex].centers.left = imgDomWH.x+event.offsetX+imgDomWH.width-li
detailData.frontBack.front[selectItem.imgDomIndex].centers.top = imgDomWH.y+event.offsetY+imgDomWH.height-li
const elementRefs = ref([]) as any;
const moveableInstance = ref(null) as any;
const setElementRef = (el, index) => {
elementRefs.value[index] = el;
};
const updateRect = ()=>{
setTimeout(() => {
moveableInstance.value.updateRect()
}, 500);
}
const initMoveableForSelected = () => {
// 销毁旧的实例
if(selectItem.imgDomIndex == -1)return
if (moveableInstance.value) {
moveableInstance.value.destroy();
}
document.addEventListener('mousemove', sizeMouseMove);
document.addEventListener('touchmove', sizeTouchmove);
document.addEventListener('mouseup', sizeMouseup);
document.addEventListener('touchend', sizeMouseup);
}
const sizeMouseMove = (event:any)=>{
let e = getMousePosition(event,false)
sizeMouseMoveOperation(e)
}
const sizeTouchmove = (event:any)=>{
let e = getMousePosition(event,true)
sizeMouseMoveOperation(e)
}
const sizeMouseup = (e:any)=>{
detailData.frontBack.front[selectItem.imgDomIndex].style={
right:'auto',
left:selectItem.imgDom.offsetLeft+'px',
bottom:'auto',
top:selectItem.imgDom.offsetTop+'px',
height:selectItem.imgDom.offsetHeight+'px',
width:selectItem.imgDom.offsetWidth+'px',
zIndex:selectItem.imgDom.style.zIndex,
// zIndex:selectItem.printZIndex
const selectedEl = elementRefs.value[selectItem.imgDomIndex];
if (!selectedEl) return;
if(!selectedEl.style.left)return
moveableInstance.value = new Moveable(getDetailListDom.moveableContainer, {
target: selectedEl,
draggable: true,
scalable: true,
rotatable: true,
keepRatio: false, // 等比缩放
snappable: true,
snapThreshold: 5,
edge: false,
});
let startPosition = {//记录初始位置
left: 0,
top: 0,
}
// detailData.frontBack.back[selectItem.imgDomIndex].style.zIndex = selectItem.printZIndex
document.removeEventListener('mousemove',sizeMouseMove)
document.removeEventListener('touchmove',sizeTouchmove)
document.removeEventListener('mouseup',sizeMouseup)
document.removeEventListener('touchend',sizeMouseup)
//鼠标抬起
setRevocation()
}
let isMove = false//表示是否移动,是否需要在鼠标抬起的时候保存数据
moveableInstance.value.on('scaleStart', ({ target, direction }) => {
const frontStyle = detailData.frontBack.front[selectItem.imgDomIndex];
if (!frontStyle.mirror){
let scaleX = frontStyle.style.transform.match(/scaleX\(([-\d.]+)\)/);
let scaleY = frontStyle.style.transform.match(/scaleY\(([-\d.]+)\)/);
frontStyle.mirror = { x: scaleX[1] == '-1' ? true : false, y: scaleY[1] == '-1' ? true : false };
}
});
moveableInstance.value.on('rotateStart', ({ target, direction }) => {
const frontStyle = detailData.frontBack.front[selectItem.imgDomIndex];
if (!frontStyle.mirror){
let scaleX = frontStyle.style.transform.match(/scaleX\(([-\d.]+)\)/);
let scaleY = frontStyle.style.transform.match(/scaleY\(([-\d.]+)\)/);
frontStyle.mirror = { x: scaleX[1] == '-1' ? true : false, y: scaleY[1] == '-1' ? true : false };
}
});
moveableInstance.value.on('dragStart', ({ target, clientX, clientY }) => {
startPosition = {
left:parseFloat(selectedEl.style.left.replace('px','')) || 0,
top:parseFloat(selectedEl.style.top.replace('px','')) || 0,
}
});
// 拖拽
moveableInstance.value.on('drag', ({ target, beforeTranslate }) => {
let x = startPosition.left + beforeTranslate[0]
let y = startPosition.top + beforeTranslate[1]
detailData.frontBack.front[selectItem.imgDomIndex].style.left = x + 'px'
detailData.frontBack.front[selectItem.imgDomIndex].style.top = y + 'px'
});
const updateElementTransform = (element, rotateDeg = null) => {
const currentTransform = element.style?.transform || '';
// 1. 提取当前的所有rotate
const currentRotates = (currentTransform.match(/rotate[XYZ]?\([^)]+\)/g) || []);
// 2. 获取除镜像和rotate外的其他所有变换
let otherTransforms = currentTransform
.replace(/scaleX\(-1\)|scaleY\(-1\)/g, '') // 移除镜像
.replace(/rotate[XYZ]?\([^)]+\)/g, '') // 移除rotate
.replace(/\s+/g, ' ')
.trim();
// 3. 构建新transform
const transforms = [];
// 镜像部分
if (element.mirror.x) transforms.push('scaleX(-1)');
if (element.mirror.y) transforms.push('scaleY(-1)');
if (otherTransforms) transforms.push(otherTransforms);
if (rotateDeg !== null) {
transforms.push(`rotate(${rotateDeg}deg)`);
} else if (currentRotates.length > 0) {
transforms.push(...currentRotates);
}
element.style.transform = transforms.join(' ').trim();
};
moveableInstance.value.on('scale', ({ target, delta, direction }) => {
const frontStyle = detailData.frontBack.front[selectItem.imgDomIndex];
if (!frontStyle.mirror) frontStyle.mirror = { x: false, y: false };
const width = parseFloat(frontStyle.style.width);
const height = parseFloat(frontStyle.style.height);
let left = parseFloat(frontStyle.style.left) || 0;
let top = parseFloat(frontStyle.style.top) || 0;
let rotation = 0;
// 获取原始比例
const originalRatio = width / height;
if (frontStyle.style.transform) {
const transform = frontStyle.style.transform;
const match = transform.match(/rotate\(([-\d.]+)deg\)/);
if (match) {
rotation = parseFloat(match[1]);
}
}
// 根据旋转角度重新计算控制点的方向
function getAdjustedCorner(originalCorner, rotationAngle) {
const angleRad = rotationAngle * (Math.PI / 180);
const cosA = Math.cos(angleRad);
const sinA = Math.sin(angleRad);
const newX = originalCorner.x * cosA - originalCorner.y * sinA;
const newY = originalCorner.x * sinA + originalCorner.y * cosA;
const threshold = 0.5;
return {
x: Math.abs(newX) > threshold ? (newX > 0 ? 1 : -1) : 0,
y: Math.abs(newY) > threshold ? (newY > 0 ? 1 : -1) : 0
};
}
if (rotation !== 0) {
direction = getAdjustedCorner({x: direction[0], y: direction[1]}, rotation);
direction = [direction.x, direction.y];
}
// 判断是否是对角线方向(需要等比缩放)
const isDiagonal = Math.abs(direction[0]) === 1 && Math.abs(direction[1]) === 1;
// 处理轴缩放,包含镜像翻转逻辑
const processAxis = (axis, val, deltaVal, dir, originalPosition, originalSize, keepRatio = false, otherAxisResult = null) => {
let newVal = val * deltaVal;
const mirrorKey = axis === 'width' ? 'x' : 'y';
const isWidth = axis === 'width';
// 检查是否需要镜像翻转当值小于等于0时
if (newVal <= 0) {
frontStyle.mirror[mirrorKey] = !frontStyle.mirror[mirrorKey];
newVal = Math.abs(newVal);
updateElementTransform(frontStyle);
// 镜像翻转后,位置需要根据原始锚点调整
if (dir === -1) {
// 从左上/右上缩放时,位置保持不变
return {
newVal,
adjustPos: originalPosition,
shouldFlip: true
};
} else {
// 从左下/右下缩放时,位置需要调整
const newPosition = originalPosition + (originalSize - newVal) * (frontStyle.mirror[mirrorKey] ? -1 : 1);
return {
newVal,
adjustPos: newPosition,
shouldFlip: true
};
}
}
const shouldMove = (!frontStyle.mirror[mirrorKey] && dir === -1) ||
(frontStyle.mirror[mirrorKey] && dir === 1);
if (keepRatio && otherAxisResult) {
newVal = isWidth ?
otherAxisResult.newVal * originalRatio :
otherAxisResult.newVal / originalRatio;
}
let adjustPos;
if (shouldMove) {
adjustPos = originalPosition - (newVal - originalSize);
} else {
adjustPos = originalPosition;
}
return {
newVal,
adjustPos,
shouldFlip: false
};
};
// 自由缩放
const widthResult = processAxis('width', width, delta[0], direction[0], left, width);
const heightResult = processAxis('height', height, delta[1], direction[1], top, height);
frontStyle.style.left = widthResult.adjustPos + 'px';
frontStyle.style.top = heightResult.adjustPos + 'px';
frontStyle.style.width = widthResult.newVal + 'px';
frontStyle.style.height = heightResult.newVal + 'px';
// }
});
// 旋转
moveableInstance.value.on('rotate', ({ target, beforeRotate }) => {
let frontStyle = detailData.frontBack.front[selectItem.imgDomIndex];
// 确保镜像状态存在
if (!frontStyle.mirror) frontStyle.mirror = { x: false, y: false };
const { x: isMirroredX, y: isMirroredY } = frontStyle.mirror;
// 计算实际旋转角度
let actualRotate = beforeRotate;
// 关键逻辑当镜像状态不同时一个true一个false旋转方向反转
if (isMirroredX !== isMirroredY) {
actualRotate = -beforeRotate;
}
// 确保角度在 0-360 度范围内
actualRotate = actualRotate % 360;
// 如果角度为负数,转换为正数
if (actualRotate < 0) {
actualRotate += 360;
}
// 确保角度在 [0, 360) 范围内
actualRotate = ((actualRotate % 360) + 360) % 360;
updateElementTransform(frontStyle, actualRotate.toFixed(2));
});
// 调整大小
moveableInstance.value.on('resize', ({ target, width, height }) => {
// console.log(width, height)
// detailData.frontBack.front[selectItem.imgDomIndex].style.width = width
// detailData.frontBack.front[selectItem.imgDomIndex].style.height = height
});
moveableInstance.value.on('dragEnd', ({ target, clientX, clientY }) => {
startPosition = {
left:0,
top:0,
}
upDataDetail()
});
moveableInstance.value.on('scaleEnd', () => {
upDataDetail()
});
moveableInstance.value.on('rotateEnd', () => {
upDataDetail()
});
};
watch(()=>selectItem.selectDetail,(newValue,oldValue)=>{
if(!newValue && newValue?.id == oldValue?.id)return
selectItem.imgDomIndex = detailData.frontBack.front.findIndex((item:any)=>item.id == newValue.id)
initMoveableForSelected()
},{immediate: true,})
const setRevocation = ()=>{
if(!isMove)return
isMove = false
let frontBack = JSON.parse(JSON.stringify(detailData.frontBack))
console.log(frontBack)
let revocation:any = JSON.parse((sessionStorage.getItem("revocation") as any))
revocation.push({designData:null,position:frontBack})
sessionStorage.setItem('revocation', JSON.stringify(revocation));
}
const sizeMouseMoveOperation = (e:any)=> {
isMove = true
let imgDomWH = selectItem.imgDom.getBoundingClientRect()
let parentNode =selectItem.imgDom.parentNode
let width = imgDomWH.width
let height = imgDomWH.height
let w,h
let num = height/width
//判断移动四个边
if(selectItem.direction == 'right'){
w = (e.clientX - detailData.frontBack.front[selectItem.imgDomIndex].centers.left)
h = (e.clientX - detailData.frontBack.front[selectItem.imgDomIndex].centers.left)*num
width = w+'px'
// height = w*num+'px'
}else if(selectItem.direction == 'top'){
num = width/height
detailData.frontBack.front[selectItem.imgDomIndex].style.top = 'auto'
// this.printStyleList[selectItem.imgDomIndex].style.left = 'auto'
detailData.frontBack.front[selectItem.imgDomIndex].style.bottom = parentNode.offsetHeight -imgDomWH.height - selectItem.imgDom.offsetTop+'px'
w = (e.clientX - detailData.frontBack.front[selectItem.imgDomIndex].centers.left)*num
h = (detailData.frontBack.front[selectItem.imgDomIndex].centers.top - e.clientY)
height = h+'px'
// width = h*num+'px'
}else if(selectItem.direction == 'bottom'){
num = width/height
h = (e.clientY - detailData.frontBack.front[selectItem.imgDomIndex].centers.top)
height = h+'px'
// width = h*num+'px'
}else if(selectItem.direction == 'left'){
detailData.frontBack.front[selectItem.imgDomIndex].style.left = 'auto'
detailData.frontBack.front[selectItem.imgDomIndex].style.right = parentNode.offsetWidth -imgDomWH.width - selectItem.imgDom.offsetLeft+'px'
w = (detailData.frontBack.front[selectItem.imgDomIndex].centers.left - e.clientX)
width = w+'px'
// height = w*num+'px'
}
//判断尺寸是否到边
detailData.frontBack.front[selectItem.imgDomIndex].style.width = width
detailData.frontBack.front[selectItem.imgDomIndex].style.height = height
}
// 设置移动
const mouseMove = (event:any)=>{
let e = getMousePosition(event,false)
mouseMoveOperation(e)
}
const touchmove=(event:any)=>{
let e = getMousePosition(event,true)
mouseMoveOperation(e)
}
const mouseup = (e:any)=> {
document.removeEventListener('mousemove',mouseMove)
document.removeEventListener('touchmove',touchmove)
document.removeEventListener('mouseup',mouseup)
document.removeEventListener('touchend',mouseup)
///鼠标抬起
const upDataDetail = async ()=>{
//同步到selectDetail数据中
// getDetailListData.designDetail
let {scale,offset,priority,transpose,rotate,position,imageSize} = await getSubmitData(selectItem.selectDetail,false)
selectItem.selectDetail.layersObject[0].scale = scale
selectItem.selectDetail.layersObject[1].scale = scale
selectItem.selectDetail.layersObject[0].offset = offset
selectItem.selectDetail.layersObject[1].offset = offset
selectItem.selectDetail.layersObject[0].priority = priority
selectItem.selectDetail.layersObject[1].priority = priority
selectItem.selectDetail.layersObject[0].rotate = rotate
selectItem.selectDetail.layersObject[1].rotate = rotate
selectItem.selectDetail.layersObject[0].transpose = transpose
selectItem.selectDetail.layersObject[1].transpose = transpose
selectItem.selectDetail.layersObject[0].position = position
selectItem.selectDetail.layersObject[1].position = position
selectItem.selectDetail.layersObject[0].imageSize = imageSize
selectItem.selectDetail.layersObject[1].imageSize = imageSize
setRevocation()
}
const mouseMoveOperation = (e:any)=>{
isMove = true
let imgDomWH = selectItem.imgDom.getBoundingClientRect()
let parentNode = document.getElementsByClassName('molepositon')[0].getElementsByClassName("designOpenrtion_imgMask")[0].getBoundingClientRect()
let x = (e.clientX - detailData.frontBack.front[selectItem.imgDomIndex].centers.left)+'px'
let y = ( e.clientY - detailData.frontBack.front[selectItem.imgDomIndex].centers.top)+'px'
detailData.frontBack.front[selectItem.imgDomIndex].style.left = x
detailData.frontBack.front[selectItem.imgDomIndex].style.top = y
}
const clothesOpenActive = (index:number)=>{
// this.designItemDetail.clothes.forEach((item)=>{
// item.clothesOpenItem = false
// })
// if(index != -1){
// this.designItemDetail.clothes[index].clothesOpenItem = true
// }
}
const itemMoveMousedown = async (index:any,e:any)=>{
if(detailData.selectDetail.id != detailData.frontBack.front[index].id)return
let isModal = false
await new Promise((resolve, reject) => {
// if(
// detailData.isEditPattern.value &&
// selectItem.selectDetail?.id &&
// (detailData.frontBack.front[index].id != selectItem.selectDetail.id)
// ){
// isModal = true
// Modal.confirm({
// title: t('collectionModal.jsContent2'),
// icon: createVNode(ExclamationCircleOutlined),
// okText: 'Yes',
// cancelText: 'No',
// mask:false,
// centered:true,
// onOk() {
// resolve(true)
// isOpen = true
// },
// onCancel(){
// resolve(false)
// isOpen = false
// }
// });
// }else{
// if(detailData.frontBack.front[index].id != selectItem.selectDetail.id){
// isOpen = true
// }
// resolve(true)
// isModal = false
// }
resolve(true)
}).then((rv)=>{
})
// emit('canvasReload')
// store.commit('DesignDetail/setDesignColthes',detailData.frontBack.front[index].id)
if(isModal)return
store.commit('DesignDetail/setDesignColthes',detailData.frontBack.front[index].id)
selectItem.imgDomIndex = index
detailData.frontBack.front.forEach((v:any)=>{
v.designOpenrtionBtn = false
})
clothesOpenActive(index)
let event = e||window.event
selectItem.imgDom = document.getElementsByClassName('molepositon')[0].getElementsByClassName("detail_modal_item_front")[selectItem.imgDomIndex]
detailData.frontBack.front[index].designOpenrtionBtn = true
// detailData.frontBack.front[index].style.zIndex = selectItem.printZIndex++
// detailData.frontBack.back[index].style.zIndex = selectItem.printZIndex
let imgDomWH = selectItem.imgDom.getBoundingClientRect()
let left = Number(detailData.frontBack.front[index].style.left.replace(/px/g,''))
let top = Number(detailData.frontBack.front[index].style.top.replace(/px/g,''))
detailData.frontBack.front[index].centers.left = imgDomWH.x+event.offsetX-left
detailData.frontBack.front[index].centers.top = imgDomWH.y+event.offsetY-top
document.addEventListener('mousemove', mouseMove);
document.addEventListener('touchmove', touchmove);
document.addEventListener('mouseup', mouseup);
document.addEventListener('touchend', mouseup);
}
const sort = (arr:any)=>{
arr.sort((a:any, b:any) => {
var a_num = a.style.zIndex;
@@ -355,7 +428,7 @@ export default defineComponent({
});
return arr
}
const getSubmitData = (value:any,isNoComputed)=>{
const getSubmitData = async (value:any,isNoComputed)=>{
let parentNode = document.getElementsByClassName('molepositon')[0].getElementsByClassName("designOpenrtion_imgMask")[0].getBoundingClientRect()
if(!detailData.frontBack?.body?.layersObject?.[0]?.imageSize || isNoComputed){
return{
@@ -365,6 +438,17 @@ export default defineComponent({
}
}
let ratio = detailData.frontBack.body.layersObject[0].imageSize[0]/parentNode.width
let scale = 0
let dom:any = document.querySelector('.molepositon .perview_img')
const img = new Image();
img.src = detailData.frontBack?.body?.path;
await new Promise<void>((resolve, reject) => {
img.onload = () => {
scale = dom.parentNode.offsetWidth / img.width
resolve()
};
})
// let arr:any = sort(detailData.frontBack.front)
let arr:any = sort(JSON.parse(JSON.stringify(detailData.frontBack.front)))
let num = 10
@@ -377,6 +461,8 @@ export default defineComponent({
priority:'',
maskUrl:'',
maskMinioUrl:'',
position:null,
imageSize:null,
}
let state = false
for (let index = 0; index < arr.length; index++) {
@@ -384,13 +470,27 @@ export default defineComponent({
state = true
let y = ((arr[index]?.style?.top.replace(/px/g,'')*ratio).toFixed(0) as any - arr[index]?.position[0])
let x = ((arr[index]?.style?.left.replace(/px/g,'')*ratio).toFixed(0) as any - arr[index]?.position[1])
let positionX = Number((arr[index]?.style?.left.replace(/px/g,'')/scale)).toFixed(2)
let positionY = Number((arr[index]?.style?.top.replace(/px/g,'')/scale)).toFixed(2)
let imageSizeW = Number((arr[index]?.style?.width.replace(/px/g,'')/scale)).toFixed(2)
let imageSizeH = Number((arr[index]?.style?.height.replace(/px/g,'')/scale)).toFixed(2)
let scaleWidth = arr[index]?.imageSize?Number(((arr[index]?.style?.width.replace(/px/g,'')*ratio)/(arr[index]?.imageSize[0]/arr[index].scale[0])).toFixed(2)):1
let scaleHeight = arr[index]?.imageSize?Number(((arr[index]?.style?.height.replace(/px/g,'')*ratio)/(arr[index]?.imageSize[1]/arr[index].scale[1])).toFixed(2)):1
// let widthScale = (arr[index].style.width.replace(/px/g,'')/arr[index].style.height.replace(/px/g,'')).toFixed(2)
let transformStr = arr[index]?.style?.transform
let scaleX = transformStr.match(/scaleX\(([-\d.]+)\)/)
let scaleY = transformStr.match(/scaleY\(([-\d.]+)\)/)
let rotate = transformStr.match(/rotate\(([-\d.]+)deg\)/);
data.transpose = [parseFloat(scaleX?.[1] || 1),parseFloat(scaleY?.[1] || 1)]
data.rotate = parseFloat(rotate?.[1] || 0)
data.scale = [scaleWidth,scaleHeight]
let top = y == 0 ? value.layersObject[0].offset[1]:y+value.layersObject[0].offset[1]
let left = x == 0 ? value.layersObject[0].offset[0]:x+value.layersObject[0].offset[0]
data.offset = [left?left:0,top?top:0]
data.position = [positionY,positionX]
data.imageSize = [imageSizeW,imageSizeH]
// data.offset = [left?left:0,top?top:0]
data.maskUrl = arr[index].maskUrl
data.maskMinioUrl = arr[index].maskMinioUrl
@@ -410,15 +510,6 @@ export default defineComponent({
}
const deleteNav = ()=>{
}
const setpitch = (item:any,index:any)=>{
detailData.frontBack.front.forEach((v:any)=>{
v.designOpenrtionBtn = false
})
detailData.frontBack.front[index].designOpenrtionBtn = true
// detailData.frontBack.front[index].style.zIndex = selectItem.printZIndex++
// detailData.frontBack.back[index].style.zIndex = selectItem.printZIndex
clothesOpenActive(index)
}
const updataPosition = ()=>{
let url = detailData.frontBack?.body?.path
@@ -431,10 +522,12 @@ export default defineComponent({
detailData.frontBack.front.forEach((item:any,index:number) => {
for (const key in item.style) {
if(key == 'zIndex')return
if(key == 'transform')return
item.style[key] = item.style[key]*sacle+'px'
}
for (const key in detailData.frontBack.back[index].style) {
if(key == 'zIndex')return
if(key == 'transform')return
detailData.frontBack.back[index].style[key] = detailData.frontBack.back[index].style[key]*sacle+'px'
}
});
@@ -445,20 +538,22 @@ export default defineComponent({
if (detailData.observer) {
detailData.observer.disconnect()
}
if (moveableInstance.value) {
moveableInstance.value.destroy();
}
})
return{
...toRefs(detailData),
...toRefs(selectItem),
...toRefs(getDetailListDom),
setElementRef,
setPrintSize,
itemSizeMousedown,
itemMoveMousedown,
deleteNav,
setpitch,
getSubmitData,
getMousePosition,
updataPosition,
updateRect,
}
},
@@ -541,6 +636,11 @@ export default defineComponent({
}
}
}
.moveableContainer{
:deep(.moveable-origin){
opacity: 0;
}
}
> .designOpenrtion_imgMask{
width: auto;
height: auto;
@@ -561,10 +661,15 @@ export default defineComponent({
position: absolute;
top: 0;
}
.detail_modal_item_front,.designOpenrtion_print{
z-index: 2;
height: 100%;
width: 100%;
pointer-events: none;
&.active{
pointer-events: auto;
}
img{
width: 100%;
// height: ;
@@ -583,94 +688,6 @@ export default defineComponent({
.designOpenrtion_print{
z-index: 1 !important;
}
> .designOpenrtion_btnBox{
z-index: 99;
width: 100%;
height: 100%;
left: 0;
ul{
list-style: none;
// width: 100%;
// height: 100%;
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
border: 2px solid rgb(20, 188, 255);
padding: 0;
-webkit-user-drag: none;
user-select:none;
opacity: 0;
margin: 0;
li{
cursor: pointer;
// border-radius: 50%;
width: calc(2rem*1.2);
height: calc(2rem*1.2);
background-color: rgb(20, 188, 255);
position: absolute;
pointer-events: none;
}
&.active{
opacity: 1;
z-index: 999 !important;
li{
pointer-events: auto;
}
}
.designOpenrtion_btn_top,.designOpenrtion_btn_bottom{
left: 50%;
transform: translate(-50%,-50%) ;
cursor: n-resize;
}
.designOpenrtion_btn_top{
top: 0;
}
.designOpenrtion_btn_bottom{
top: 100%;
}
.designOpenrtion_btn_left,.designOpenrtion_btn_right{
top: 50%;
transform: translate(-50%,-50%) ;
cursor: e-resize;
}
.designOpenrtion_btn_left{
left: 0;
}
.designOpenrtion_btn_right{
left: 100%;
}
.designOpenrtion_rotote{
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
width: 0;
height: 0;
}
.designOpenrtion_rotote::after{
position: absolute;
content: "";
background-color: #14bcff;
width: 2px;
height: 30px;
left: 50%;
bottom: 0;
transform: translateX(-50%);
}
.designOpenrtion_rotote::before{
position: absolute;
content: "";
background-color: #14bcff;
top: calc(50% - 30px);
left: 50%;
transform: translate(-50%,-50%) ;
width: calc(1.5rem*1.2);
height: calc(1.5rem*1.2);
border-radius: 50%;
}
}
}
}
}
</style>

View File

@@ -132,7 +132,25 @@ export default defineComponent({
url:rv.url,
designType:props.item.resData.designType || props.item.designType,
}
props.list.unshift(rv)
rv.type_ = {
type1: "material",
type2: props.level1Type
}
if(props.level1Type == 'Printboard'){
let list = props.list.filter((v:any)=> v.type_?.type1 == "material")
list.unshift(rv)
store.commit("setPrintboardMaterialFiles", list);
}else if(props.level1Type == 'Sketchboard'){
let list = props.list.filter((v:any)=> v.type_?.type1 == "material")
list.unshift(rv)
store.commit("setSketchboardMaterialFiles", list);
}else if(props.level1Type == 'Moodleboard'){
let list = props.list.filter((v:any)=> v.type_?.type1 == "material")
list.unshift(rv)
store.commit("setMoodleboardMaterialFiles", list);
}else{
props.list.unshift(rv)
}
}
}
).catch(res=>{

View File

@@ -9,7 +9,7 @@
:isBackgroundChangeable="false"
ref="editCanvas"></editCanvas>
</div>
<div class="btn">
<div class="btn" v-if="btnShow">
<div class="gallery_btn" @click="canvasSave" style="width: min-content;margin-top: auto;">{{ $t('exportModel.Save') }}</div>
<div class="gallery_btn" @click="exportElement">{{ $t('exportModel.Export') }}</div>
</div>
@@ -46,7 +46,11 @@ export default defineComponent({
isSubmitCanvasJSON:{
type:Boolean,
default:false
}
},
btnShow:{
type:Boolean,
default:true
},
},
emits:['submitBase64Data','canvasChangeGetJSON'],
setup(props,{emit}) {
@@ -99,6 +103,9 @@ export default defineComponent({
}
}
const submitBase64Data = (base64Data)=>{
return dataDom.editCanvas.exportImage({isContainBg:true,isContainFixed:true,isCropByBg:true})
}
const exportElement = ()=>{
dataDom.editCanvas.exportImage({isContainBg:true,isContainFixed:false,isCropByBg:true}).then((rv)=>{
downloadBase64Image(rv,'canvas')
@@ -174,6 +181,7 @@ export default defineComponent({
canvasInit,
exportElement,
changeCanvas,
submitBase64Data,
};
},
data(prop) {