feat: support multi-canvas & adaptive canvas

This commit is contained in:
ShawnPhang 2024-04-16 23:13:00 +08:00
parent 22ecc9900d
commit b739528c21
35 changed files with 1062 additions and 382 deletions

View File

@ -3,7 +3,7 @@
* @Date: 2022-01-17 16:06:30
* @Description: Design Palxp
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-09-19 18:36:13
* @LastEditTime: 2024-04-13 18:16:44
-->
<!DOCTYPE html>
<html lang="zh-CN">
@ -12,6 +12,7 @@
<link rel="icon" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>迅排设计 - 轻松创意,迅捷排版,感受云上设计带来的乐趣!</title>
<script> var _hmt = _hmt || [] </script>
</head>
<body>

View File

@ -2,14 +2,18 @@
* @Author: ShawnPhang
* @Date: 2021-08-19 18:43:22
* @Description:
* @LastEditors: ShawnPhang <site: book.palxp.com>
* @LastEditTime: 2023-07-24 13:01:10
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-16 15:37:54
*/
import fetch from '@/utils/axios'
import _config from '@/config'
function serialize(obj: any) {
return Object.keys(obj).map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`).join('&');
}
// const screenshot_url = window.location.protocol + '//' + window.location.host + '/draw'
export const download = (params: Type.Object = {}) => `${_config.SCREEN_URL}/api/screenshots?id=${params.id}&width=${params.width}&height=${params.height}`
export const download = (params: Type.Object = {}) => `${_config.SCREEN_URL}/api/screenshots?${serialize(params)}`
type IGetTempListParam = {
search: string

View File

@ -1,15 +1,14 @@
@import './color.less';
// design index page
@color4: #50555b;
@color5: #808080;
@width0: 1180px;
@width1: 120px;
@width2: 100%;
@height2: 54px;
@canvasBG: #f8f8f8;
.page-design-bg-color {
// background-color: #4b678c;
// background-color: #4682b4;
@ -22,7 +21,7 @@
min-width: @width0;
position: absolute;
width: @width2;
background-color: @canvasBG;
.top-nav {
height: @height2;
min-width: @width0;

View File

@ -3,7 +3,7 @@
* @Date: 2024-04-09 11:24:57
* @Description: 创建/编辑画布尺寸
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-10 17:29:15
* @LastEditTime: 2024-04-16 17:11:59
-->
<template>
<div>
@ -32,9 +32,10 @@ import { ElCheckbox } from 'element-plus'
import { useRouter } from 'vue-router'
import sizeEditor from './sizeEditor.vue'
import sizes from '@/assets/data/PageSizeData'
import { useWidgetStore } from '@/store';
import { useWidgetStore, useControlStore } from '@/store';
const router = useRouter()
const controlStore = useControlStore()
const widgetStore = useWidgetStore()
const props = withDefaults(
defineProps<{
@ -54,6 +55,7 @@ const applySize = ({ width, height }: any) => {
}
const open = () => {
controlStore.setShowMoveable(false) //
if (props.params) {
page.value.width = props.params.width
page.value.height = props.params.height

View File

@ -1,73 +1,67 @@
<!--
* @Author: ShawnPhang
* @Date: 2024-04-10 23:02:46
* @Description: 主画布
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-16 11:34:08
-->
<template>
<div id="main">
<div id="page-design" ref="page_design" :style="{ paddingTop: dPaddingTop + 'px', minWidth: (dPage.width * dZoom) / 100 + 120 + 'px' }" >
<div id="page-design" ref="page_design" :style="{ paddingTop: dPaddingTop + 'px', minWidth: (dPage.width * dZoom) / 100 + dPresetPadding * 2 + 'px' }">
<div
id="out-page"
class="out-page"
:style="{
width: (dPage.width * dZoom) / 100 + 120 + 'px',
height: (dPage.height * dZoom) / 100 + 120 + 'px',
padding: dPresetPadding + 'px',
width: (dPage.width * dZoom) / 100 + dPresetPadding * 2 + 'px',
height: (dPage.height * dZoom) / 100 + dPresetPadding * 2 + 'px',
opacity: 1 - (dZoom < 100 ? dPage.tag : 0),
}"
>
<slot />
<resize-page :width="(dPage.width * dZoom) / 100" :height="(dPage.height * dZoom) / 100" />
<watermark :customStyle="{ height: (dPage.height * dZoom) / 100 + 'px'}">
<div
:id="pageDesignCanvasId"
class="design-canvas"
:data-type="dPage.type"
:data-uuid="dPage.uuid"
:style="{
width: dPage.width + 'px',
height: dPage.height + 'px',
transform: 'scale(' + dZoom / 100 + ')',
transformOrigin: (dZoom >= 100 ? 'center' : 'left') + ' top',
backgroundColor: dPage.backgroundGradient ? undefined : dPage.backgroundColor,
backgroundImage: dPage.backgroundImage ? `url(${dPage?.backgroundImage})` : dPage.backgroundGradient || undefined,
backgroundSize: dPage.backgroundTransform?.x ? 'auto' : 'cover',
backgroundPositionX: (dPage.backgroundTransform?.x || 0) + 'px',
backgroundPositionY: (dPage.backgroundTransform?.y || 0) + 'px',
opacity: dPage.opacity + (dZoom < 100 ? dPage.tag : 0),
}"
@mousemove="dropOver($event)"
@drop="drop($event)"
@mouseup="drop($event)"
>
<!-- <grid-size /> -->
<component
:is="layer.type"
v-for="layer in getlayers()"
:id="layer.uuid" :key="layer.uuid"
:class="['layer', { 'layer-hover': layer.uuid === dHoverUuid || dActiveElement?.parent === layer.uuid, 'layer-no-hover': dActiveElement?.uuid === layer.uuid }]"
:data-title="layer.type" :params="layer"
:parent="dPage" :data-type="layer.type"
:data-uuid="layer.uuid"
<slot />
<resize-page :width="(dPage.width * dZoom) / 100" :height="(dPage.height * dZoom) / 100" />
<watermark :customStyle="{ height: (dPage.height * dZoom) / 100 + 'px' }">
<div
:id="pageDesignCanvasId"
class="design-canvas"
:data-type="dPage.type"
:data-uuid="dPage.uuid"
:style="{
width: dPage.width + 'px',
height: dPage.height + 'px',
transform: 'scale(' + dZoom / 100 + ')',
transformOrigin: (dZoom >= 100 ? 'center' : 'left') + ' top',
backgroundColor: dPage.backgroundGradient ? undefined : dPage.backgroundColor,
backgroundImage: dPage.backgroundImage ? `url(${dPage?.backgroundImage})` : dPage.backgroundGradient || undefined,
backgroundSize: dPage.backgroundTransform?.x ? 'auto' : 'cover',
backgroundPositionX: (dPage.backgroundTransform?.x || 0) + 'px',
backgroundPositionY: (dPage.backgroundTransform?.y || 0) + 'px',
opacity: dPage.opacity + (dZoom < 100 ? dPage.tag : 0),
}"
@mousemove="dropOver($event)"
@drop="drop($event)"
@mouseup="drop($event)"
>
<template v-if="layer.isContainer">
<!-- :class="{
<!-- <grid-size /> -->
<component :is="layer.type" v-for="layer in getlayers()" :id="layer.uuid" :key="layer.uuid" :class="['layer', { 'layer-hover': layer.uuid === dHoverUuid || dActiveElement?.parent === layer.uuid, 'layer-no-hover': dActiveElement?.uuid === layer.uuid }]" :data-title="layer.type" :params="layer" :parent="dPage" :data-type="layer.type" :data-uuid="layer.uuid">
<template v-if="layer.isContainer">
<!-- :class="{
layer: true,
'layer-active': getIsActive(widget.uuid),
'layer-no-hover': dActiveElement.uuid !== widget.parent && dActiveElement.parent !== widget.parent,
'layer-hover': widget.uuid === dHoverUuid,
}" -->
<component
:is="widget.type"
v-for="widget in getChilds(layer.uuid)"
:key="widget.uuid" child :class="['layer', { 'layer-no-hover':dActiveElement?.uuid !== widget.parent && dActiveElement?.parent !== widget.parent }]"
:data-title="widget.type" :params="widget"
:parent="layer" :data-type="widget.type"
:data-uuid="widget.uuid"
/>
</template>
</component>
<component :is="widget.type" v-for="widget in getChilds(layer.uuid)" :key="widget.uuid" child :class="['layer', { 'layer-no-hover': dActiveElement?.uuid !== widget.parent && dActiveElement?.parent !== widget.parent }]" :data-title="widget.type" :params="widget" :parent="layer" :data-type="widget.type" :data-uuid="widget.uuid" />
</template>
</component>
<!-- <ref-line v-if="dSelectWidgets.length === 0" /> -->
<!-- <size-control v-if="dSelectWidgets.length === 0" /> -->
</div>
</watermark>
<!-- <ref-line v-if="dSelectWidgets.length === 0" /> -->
<!-- <size-control v-if="dSelectWidgets.length === 0" /> -->
</div>
</watermark>
</div>
</div>
<slot name="bottom" />
</div>
</template>
@ -84,6 +78,7 @@ import { storeToRefs } from 'pinia'
import { TPageState } from '@/store/design/canvas/d'
import resizePage from './comps/resize.vue'
import watermark from './comps/pageWatermark.vue'
//
type TProps = {
pageDesignCanvasId: string
@ -103,11 +98,10 @@ const canvasStore = useCanvasStore()
const { pageDesignCanvasId } = defineProps<TProps>()
const { dPage } = storeToRefs(useCanvasStore())
const { dZoom, dPaddingTop, dScreen } = storeToRefs(canvasStore)
const { dZoom, dPresetPadding, dPaddingTop, dScreen } = storeToRefs(canvasStore)
const { dDraging, showRotatable, dAltDown, dSpaceDown } = storeToRefs(controlStore)
const { dWidgets, dActiveElement, dSelectWidgets, dHoverUuid } = storeToRefs(widgetStore)
let _dropIn: string | null = ''
let _srcCache: string | null = ''
@ -145,14 +139,14 @@ onMounted(() => {
}
})
// components: {lineGuides},
// mixins: [moveInit],
// ...mapActions(['updateScreen', 'selectWidget', 'deleteWidget', 'addWidget', 'addGroup']),
// components: {lineGuides},
// mixins: [moveInit],
// ...mapActions(['updateScreen', 'selectWidget', 'deleteWidget', 'addWidget', 'addGroup']),
// getBackground(data) {
// if (data.startsWith('http')) return `url(${data})`
// if (data.startsWith('linear-gradient')) return data
// },
// getBackground(data) {
// if (data.startsWith('http')) return `url(${data})`
// if (data.startsWith('linear-gradient')) return data
// },
async function dropOver(e: MouseEvent) {
if (!dActiveElement.value) return
@ -171,7 +165,7 @@ async function dropOver(e: MouseEvent) {
if (!target) return
const uuid = target.getAttribute('data-uuid')
widgetStore.setDropOver(uuid ?? "-1")
widgetStore.setDropOver(uuid ?? '-1')
// store.dispatch('setDropOver', uuid)
const imgEl = target?.firstElementChild?.firstElementChild as HTMLImageElement
@ -200,7 +194,7 @@ async function drop(e: MouseEvent) {
const dropIn = _dropIn
_dropIn = ''
widgetStore.setDropOver("-1")
widgetStore.setDropOver('-1')
// store.dispatch('setDropOver', '-1')
// store.commit('setShowMoveable', false) //
@ -238,7 +232,7 @@ async function drop(e: MouseEvent) {
})
const half = {
x: parent.width ? (parent.width * dZoom.value) / 100 / 2 : 0,
y: parent.height ? (parent.height * dZoom.value) / 100 / 2 : 0
y: parent.height ? (parent.height * dZoom.value) / 100 / 2 : 0,
}
componentItem.forEach((element) => {
element.left += (lost ? lostX - half.x : e.layerX - half.x) * (100 / dZoom.value)
@ -249,9 +243,9 @@ async function drop(e: MouseEvent) {
// addGroup(item)
}
//
const half = {
const half = {
x: setting.width ? (setting.width * dZoom.value) / 100 / 2 : 0,
y: setting.height ? (setting.height * dZoom.value) / 100 / 2 : 0
y: setting.height ? (setting.height * dZoom.value) / 100 / 2 : 0,
}
// const half = { x: (this.dDragInitData.offsetX * this.dZoom) / 100, y: (this.dDragInitData.offsetY * this.dZoom) / 100 }
setting.left = (lost ? lostX - half.x : e.layerX - half.x) * (100 / dZoom.value)
@ -267,7 +261,7 @@ async function drop(e: MouseEvent) {
// store.commit('setShowMoveable', true) //
controlStore.setShowMoveable(true) //
const widget = dWidgets.value.find((item: {uuid: string}) => item.uuid === uuid)
const widget = dWidgets.value.find((item: { uuid: string }) => item.uuid === uuid)
if (!widget) return
widget.imgUrl = item.value.url
// if (e.target.className.baseVal) {
@ -276,14 +270,13 @@ async function drop(e: MouseEvent) {
// }
} else {
if (dropIn) {
const widget = dWidgets.value.find((item: {uuid: string}) => item.uuid == dropIn)
const widget = dWidgets.value.find((item: { uuid: string }) => item.uuid == dropIn)
if (!widget) return
widget.imgUrl = item.value.url
console.log('加入+', widget)
// store.commit('setShowMoveable', true) //
controlStore.setShowMoveable(true) //
} else {
widgetStore.addWidget(setting as Required<TPageState>)
// store.dispatch('addWidget', setting) //
@ -342,7 +335,7 @@ async function handleSelection(e: MouseEvent) {
if (type) {
let uuid = target.getAttribute('data-uuid')
if (uuid !== '-1' && !dAltDown.value) {
let widget = dWidgets.value.find((item: {uuid: string}) => item.uuid === uuid)
let widget = dWidgets.value.find((item: { uuid: string }) => item.uuid === uuid)
if (!widget || !dActiveElement.value) return
if (widget.parent !== '-1' && widget.parent !== dActiveElement.value.uuid && widget.parent !== dActiveElement.value.parent) {
uuid = widget.parent || null
@ -353,7 +346,7 @@ async function handleSelection(e: MouseEvent) {
// this.$store.commit('setMoveable', false)
if (showRotatable.value !== false) {
widgetStore.selectWidget({
uuid: uuid ?? " -1",
uuid: uuid ?? ' -1',
})
// store.dispatch('selectWidget', {
// uuid: uuid,
@ -366,7 +359,7 @@ async function handleSelection(e: MouseEvent) {
} else {
//
widgetStore.selectWidget({
uuid: "-1"
uuid: '-1',
})
// store.dispatch('selectWidget', {
// uuid: '-1',
@ -381,34 +374,33 @@ function getlayers() {
function getChilds(uuid: string) {
return dWidgets.value.filter((item) => item.parent === uuid)
}
// getIsActive(uuid) {
// if (this.dSelectWidgets.length > 0) {
// let widget = this.dSelectWidgets.find((item) => item.uuid === uuid)
// if (widget) {
// return true
// }
// return false
// } else {
// return uuid === this.dActiveElement.uuid
// }
// },
// getIsActive(uuid) {
// if (this.dSelectWidgets.length > 0) {
// let widget = this.dSelectWidgets.find((item) => item.uuid === uuid)
// if (widget) {
// return true
// }
// return false
// } else {
// return uuid === this.dActiveElement.uuid
// }
// },
</script>
<style lang="less" scoped>
#main {
overflow: auto; position: relative;
overflow: auto;
position: relative;
}
#page-design {
scrollbar-width: none;
min-height: 100%;
// display: flex;
// align-items: center;
overflow: auto;
position: relative;
// width: 100%;
.out-page {
margin: 0 auto;
padding: 60px;
// padding: 60px;
position: relative;
.design-canvas {
// transition: all 0.3s;

View File

@ -1,10 +1,8 @@
<template>
<div id="page-style">
<div v-if="state.showBgLib" style="width: 256px;height: 100%;">
<span class="header-back" @click="state.showBgLib = false">
<i class="iconfont icon-right"></i> 选择背景
</span>
<bg-img-list-wrap style="padding-top: 2rem;" model="stylePanel" />
<div v-if="state.showBgLib" style="width: 256px; height: 100%">
<span class="header-back" @click="state.showBgLib = false"> <i class="iconfont icon-right"></i> 选择背景 </span>
<bg-img-list-wrap style="padding-top: 2rem" model="stylePanel" />
</div>
<el-collapse v-else v-model="state.activeNames">
<el-collapse-item title="画布尺寸" name="1">
@ -13,20 +11,20 @@
</sizeEditor>
</el-collapse-item>
<el-collapse-item title="背景设置" name="2">
<el-button style="width: 100%; margin: 0 0 1rem 0;" type="primary" link @click="state.showBgLib = true">在背景库中选择</el-button>
<el-button style="width: 100%; margin: 0 0 1rem 0" type="primary" link @click="state.showBgLib = true">在背景库中选择</el-button>
<Tabs :value="state.mode" @update:value="onChangeMode">
<TabPanel v-for="label in state.modes" :key="label" :label="label"></TabPanel>
</Tabs>
<color-select v-show="state.mode === '颜色'" v-model="state.innerElement.backgroundColor" :modes="['纯色','渐变']" @change="colorChange" />
<color-select v-show="state.mode === '颜色'" v-model="state.innerElement.backgroundColor" :modes="['纯色', '渐变']" @change="colorChange" />
<div v-if="state.mode === '图片' && state.innerElement.backgroundImage" style="margin-top: 1.2rem">
<div class="backgroud-wrap">
<el-image style="height: 100%" :src="state.innerElement.backgroundImage" fit="contain"></el-image>
<div class="bg-control">
<div class="btns">
<uploader style="width: 47%;" @done="uploadImgDone">
<el-button style="width: 100%;" plain>上传图片</el-button>
<uploader style="width: 47%" @done="uploadImgDone">
<el-button style="width: 100%" plain>上传图片</el-button>
</uploader>
<el-button style="width: 47%;" @click="state.showBgLib = true" plain>背景库</el-button>
<el-button style="width: 47%" @click="state.showBgLib = true" plain>背景库</el-button>
</div>
</div>
<div class="bg-options">
@ -91,7 +89,7 @@ const state = reactive<TState>({
downP: 0,
mode: '颜色',
modes: ['颜色', '图片'],
showBgLib: false
showBgLib: false,
})
const sizeEditRef: Ref<typeof createDesign | null> = ref(null)
// const { dActiveElement } = useSetupMapGetters(['dActiveElement'])
@ -103,7 +101,7 @@ watch(
() => {
change()
},
{ deep: true }
{ deep: true },
)
watch(
@ -111,7 +109,7 @@ watch(
() => {
changeValue()
},
{ deep: true }
{ deep: true },
)
onMounted(() => {
@ -144,12 +142,9 @@ function changeValue() {
return
}
for (let key in state.innerElement) {
if (
key !== 'setting' && key !== 'record' &&
state.innerElement[key] !== (dActiveElement.value as Record<string, any>)[key]
) {
if (state.innerElement[key] !== (dActiveElement.value as Record<string, any>)[key]) {
if (state.ingoreKeys.indexOf(key) !== -1) {
(dActiveElement.value as Record<string, any>)[key] = state.innerElement[key]
;(dActiveElement.value as Record<string, any>)[key] = state.innerElement[key]
} else {
pageStore.updatePageData({
key: key as keyof TPageState,
@ -197,7 +192,7 @@ async function shiftOut() {
setting.width = state.innerElement.width
setting.height = state.innerElement.height
setting.imgUrl = state.innerElement.backgroundImage
setting.uuid = `bg-${(new Date()).getTime()}`
setting.uuid = `bg-${new Date().getTime()}`
widgetStore.dWidgets.unshift(setting)
widgetStore.selectWidget({
uuid: widgetStore.dWidgets[0].uuid,
@ -229,25 +224,26 @@ function openSizeEdit() {
margin-bottom: 10px;
}
.btn-wrap {
width: 100%; margin-top: 1.2rem;
width: 100%;
margin-top: 1.2rem;
}
.header {
&-back {
cursor: pointer;
display: flex;
align-items: center;
color: #333;
font-size: 14px;
font-weight: 600;
height: 2.9rem;
position: absolute;
z-index: 2;
background: #ffffff;
width: 259px;
.icon-right {
transform: rotate(180deg);
}
cursor: pointer;
display: flex;
align-items: center;
color: #333;
font-size: 14px;
font-weight: 600;
height: 2.9rem;
position: absolute;
z-index: 2;
background: #ffffff;
width: 259px;
.icon-right {
transform: rotate(180deg);
}
}
}
.backgroud-wrap {

View File

@ -3,7 +3,7 @@
* @Date: 2022-04-08 10:31:34
* @Description: 标尺
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-03-11 01:42:25
* @LastEditTime: 2024-04-10 23:07:44
-->
<template>
<div></div>
@ -13,29 +13,28 @@
import { watch } from 'vue'
import Guides, { GuideOptions } from '@scena/guides'
import { useCanvasStore } from '@/store';
import { useCanvasStore } from '@/store'
type TProps = {
show: boolean
}
type TSameParams = {
backgroundColor: string,
backgroundColor: string
lineColor: string
textColor: string
// direction: 'start',
// height: 30,
displayDragPos: boolean,
dragPosFormat: (v: string | number) => string,
displayDragPos: boolean
dragPosFormat: (v: string | number) => string
}
type TGuidesData = Guides & GuideOptions
const props = withDefaults(defineProps<TProps>(), {
show: false
show: false,
})
const canvasStore = useCanvasStore()
const container = 'page-design' // page-design out-page
let guidesTop: TGuidesData | null = null
@ -55,20 +54,6 @@ watch(
},
)
// onMounted(() => {
// // let scrollX = 0
// // let scrollY = 0
// // window.addEventListener('resize', () => {
// // guides.resize()
// // })
// // window.addEventListener('wheel', (e) => {
// // scrollX += e.deltaX
// // scrollY += e.deltaY
// // guides.scrollGuides(scrollY)
// // guides.scroll(scrollX)
// // })
// })
function destroy() {
guidesTop?.destroy()
guidesLeft?.destroy()

View File

@ -0,0 +1,2 @@
import index from './multipleBoards.vue'
export default index

View File

@ -0,0 +1,313 @@
<!--
* @Author: ShawnPhang
* @Date: 2024-04-11 17:27:58
* @Description: 多画板操作界面
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-16 22:58:33
-->
<template>
<div :style="{ position, bottom: -1 * st + 'px', left: sl + 'px' }" :class="['artboards', isFold ? 'fold' : 'unfold']">
<div class="wrap">
<div v-if="isFold" v-show="dLayouts.length > 0" class="btn" @click="isFold = !isFold">画板 {{ index + 1 }}/{{ dLayouts.length }} <i class="icon sd-zhankai" /></div>
<div class="list" v-else>
<span @click="isFold = !isFold" class="icon-btn"><i class="icon sd-zhankai" /></span>
<div v-for="(l, li) in dLayouts" :key="'l' + li" :style="{width: getPW(l.global)+'px'}" @click="selectPoster(li)" :class="['item-box', index == li ? 'item-select' : 'item-default']">
<div
class="mini-poster"
:style="{
transform: getTransform(l.global),
width: l.global.width + 'px',
height: l.global.height + 'px',
backgroundColor: l.global.backgroundGradient ? undefined : l.global.backgroundColor,
backgroundImage: l.global.backgroundImage ? `url(${l.global?.backgroundImage})` : l.global.backgroundGradient || undefined,
backgroundSize: l.global.backgroundTransform?.x ? 'auto' : 'cover',
backgroundPositionX: (l.global.backgroundTransform?.x || 0) + 'px',
backgroundPositionY: (l.global.backgroundTransform?.y || 0) + 'px',
}"
>
<component :is="layer.type + '-static'" v-for="layer in getlayers(l.layers)" :key="layer.uuid" :params="layer" :parent="l.global">
<template v-if="layer.isContainer">
<component :is="widget.type + '-static'" v-for="widget in getChilds(l.layers, layer.uuid)" :key="widget.uuid" :params="widget" :parent="layer" />
</template>
</component>
</div>
<div class="item-idx">{{ li + 1 }}</div>
<i @click.stop="removePoster(li)" class="icon sd-quxiao" />
</div>
<div v-show="dLayouts.length < 9" @click="addLayer" class="item-add"><i class="iconfont icon-add" /></div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, Ref, onMounted, nextTick, watch, computed } from 'vue'
import { storeToRefs } from 'pinia'
import { useCanvasStore, useWidgetStore, useForceStore, useControlStore } from '@/store'
import { ElMessage } from 'element-plus'
const forceStore = useForceStore()
const canvasStore = useCanvasStore()
const widgetStore = useWidgetStore()
const controlStore = useControlStore()
const position: Ref = ref('absolute') // sticky
const isFold = ref(true)
const st = ref(0)
const sl = ref(0)
const index = computed(() => canvasStore.dCurrentPage)
const { dZoom, dPage } = storeToRefs(canvasStore)
const { dWidgets, dLayouts } = storeToRefs(widgetStore)
watch(
() => dZoom.value,
(val) => {
// bottom
mainEl.scrollTop = 0
},
)
watch(
() => isFold.value,
(isFold) => {
canvasStore.setBottomHeight(isFold ? 0 : 90)
setTimeout(() => {
forceStore.setZoomScreenChange()
}, 300)
},
)
let mainEl: any = null
onMounted(async () => {
await nextTick()
mainEl = document.getElementById('main')
mainEl.addEventListener('scroll', function (e) {
st.value = mainEl.scrollTop
sl.value = mainEl.scrollLeft
})
})
/** 计算变换量 */
function getTransform(global: any) {
const { width, height } = global
const isVertical = height > width
const edge = isVertical ? Math.max(width, height) : Math.min(width, height)
const s = 72 / edge
const left = isVertical ? ((72 - width * s) / 2 - 1) / s : 0
return `scale(${s}) translateX(${left}px)`
}
/** 计算实际宽度 */
function getPW(global: any) {
const { width, height } = global
const isVertical = height > width
const s = 72 / Math.min(width, height)
return isVertical ? 72 : width * s
}
function getlayers(widgets: any) {
return widgets.filter((item: any) => item.parent === dPage.value.uuid)
}
function getChilds(widgets: any, uuid: string) {
return widgets.filter((item: any) => item.parent === uuid)
}
function getInitPage() {
const clonePage = JSON.parse(JSON.stringify(dPage.value))
clonePage.backgroundColor = '#ffffffff'
clonePage.backgroundGradient = ''
clonePage.backgroundImage = ''
return clonePage
}
function addLayer() {
controlStore.setShowMoveable(false) //
widgetStore.dLayouts.push({ global: getInitPage(), layers: [] })
canvasStore.dCurrentPage = dLayouts.value.length - 1
widgetStore.setDWidgets(widgetStore.getWidgets)
canvasStore.setDPage(getInitPage())
canvasStore.updateDPage()
widgetStore.selectWidget({ uuid: '-1' })
}
function selectPoster(i: number) {
controlStore.setShowMoveable(false) //
canvasStore.dCurrentPage = i
widgetStore.setDWidgets(widgetStore.getWidgets)
canvasStore.setDPage(dLayouts.value[i].global)
widgetStore.selectWidget({ uuid: '-1' })
}
function removePoster(removeIndex: number) {
if (index.value === removeIndex) {
//
widgetStore.dLayouts[removeIndex].layers.length = 0
ElMessage('画布已清空')
widgetStore.setDWidgets([]) //
// widgetStore.updateDWidgets()
// widgetStore.dLayouts[removeIndex].global = getInitPage()
canvasStore.setDPage(getInitPage()) //
// canvasStore.updateDPage()
// widgetStore.setDWidgets([])
} else widgetStore.dLayouts.splice(removeIndex, 1)
}
</script>
<style lang="less" scoped>
.artboards {
left: 0;
z-index: 99;
padding: 0 12px;
font-size: 14px;
color: #666666;
font-weight: 600;
transition: all 0.5s;
.icon {
transition: all 0.3s;
color: #babec2;
}
.list {
display: flex;
align-items: center;
}
.item-box,
.item-add {
position: relative;
width: 72px;
height: 72px;
border-radius: 5px;
margin: 5px 0 0 14px;
background: #f1f2f4;
overflow: hidden;
border: 1px solid rgba(0, 0, 0, 0.07);
}
.item-box:hover {
.sd-quxiao {
opacity: 1;
}
}
.sd-quxiao,
.item-idx {
position: absolute;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
border-radius: 2px;
}
.item-idx {
font-size: 12px;
bottom: 2px;
left: 2px;
width: 17px;
height: 17px;
color: #ffffff;
background: rgba(0, 0, 0, 0.6);
}
.sd-quxiao {
opacity: 0;
font-size: 12px;
padding: 1px 2px;
border: 1px solid rgba(0, 0, 0, 0.1);
cursor: pointer;
background-color: #ffffff;
color: #666666;
right: 1px;
top: 1px;
}
.sd-quxiao:hover {
transform: scale(1.2);
}
.item-add {
cursor: pointer;
width: 70px;
height: 70px;
display: flex;
align-items: center;
justify-content: center;
.icon-add {
font-size: 24px;
}
}
.item-add:hover {
filter: brightness(95%);
}
.item-default:hover {
box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.4), 0px 4px 12px 0px rgba(0, 0, 0, 0.07);
}
.item-select {
box-shadow: 0px 0px 2px 3px @main-color;
}
.item-box:first-of-type {
margin-left: 0;
}
.item-box:first-child {
margin-left: 0;
}
}
.unfold {
width: calc(100% - 155px);
height: 90px;
.wrap {
padding: 10px 10px 10px 0;
height: 100%;
background-color: #ffffff;
display: flex;
align-items: center;
justify-content: flex-start;
overflow-x: scroll;
overflow-y: hidden;
border-radius: 12px;
box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, 0.08), 0px 4px 12px 0px rgba(0, 0, 0, 0.04);
.icon-btn {
display: inline-block;
cursor: pointer;
width: 47px;
height: 70px;
display: flex;
align-items: center;
justify-content: center;
}
.sd-zhankai {
font-size: 18px;
}
.sd-zhankai:hover {
transform: scale(1.4);
}
}
}
.fold {
cursor: pointer;
width: 150px;
text-align: center;
height: 38px;
margin-bottom: 10px;
.wrap {
display: flex;
align-items: center;
height: 100%;
background-color: #ffffff;
border-radius: 5px;
box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, 0.08), 0px 4px 12px 0px rgba(0, 0, 0, 0.04);
}
.icon {
margin-left: 4px;
}
.btn {
padding: 0 20px;
display: flex;
width: 100%;
align-items: center;
justify-content: center;
height: 100%;
}
.btn:hover > .sd-zhankai {
transform: rotate(180deg);
}
}
.mini-poster {
overflow: hidden;
position: absolute;
transform-origin: 0 0;
}
</style>

View File

@ -51,7 +51,7 @@ const canvasStore = useCanvasStore()
const { dPage } = storeToRefs(useCanvasStore())
const { zoomScreenChange } = storeToRefs(useForceStore())
const { dZoom, dScreen } = storeToRefs(canvasStore)
const presetPadding = canvasStore.dPresetPadding
watch(
activezoomIndex,
@ -142,10 +142,6 @@ function changeScreen() {
width: screen.offsetWidth,
height: screen.offsetHeight,
})
// store.dispatch('updateScreen', {
// width: screen.offsetWidth,
// height: screen.offsetHeight,
// })
}, 300)
}
@ -239,27 +235,29 @@ function nearZoom(add?: boolean) {
}
function calcZoom() {
let widthZoom = ((dScreen.value.width - 142) * 100) / dPage.value.width
let heightZoom = ((dScreen.value.height - 122) * 100) / dPage.value.height
// let widthZoom = ((dScreen.value.width - 142) * 100) / dPage.value.width
// let heightZoom = ((dScreen.value.height - 122) * 100) / dPage.value.height
const diffHeight = presetPadding * 2 + 2 + canvasStore.dBottomHeight
const diffWidth = presetPadding * 2 + 22
let widthZoom = ((dScreen.value.width - diffWidth) * 100) / dPage.value.width
let heightZoom = ((dScreen.value.height - diffHeight) * 100) / dPage.value.height
bestZoom.value = Math.min(widthZoom, heightZoom)
return bestZoom.value
}
async function autoFixTop() {
await nextTick()
const presetPadding = 60
const el = document.getElementById('out-page')
if (!el) return
const clientHeight = window.innerHeight - 54
const headerBarHeight = 54
const clientHeight = window.innerHeight - headerBarHeight - canvasStore.dBottomHeight
// const parentHeight = (el.offsetParent as HTMLElement).offsetHeight - 54
let padding = (clientHeight - el.offsetHeight) / 2
if (typeof curAction.value === 'undefined') {
padding += presetPadding / 2
}
curAction.value === 'add' && (padding -= presetPadding)
canvasStore.updatePaddingTop(padding > 0 ? padding : 0)
// store.commit('updatePaddingTop', padding > 0 ? padding : 0)
}
defineExpose({
@ -280,26 +278,28 @@ defineExpose({
@z-border-color: #e6e6e6;
#zoom-control {
bottom: 20px;
bottom: 10px;
position: absolute;
right: 302px;
right: 292px;
z-index: 1000;
.zoom-control-wrap {
display: flex;
flex-direction: row;
font-size: 14px;
height: 40px;
height: 38px;
.radius-left {
border-bottom-left-radius: 50%;
border-top-left-radius: 50%;
border-bottom-left-radius: 6px;
border-top-left-radius: 6px;
border-block-end: 1px solid @z-border-color;
border-block-start: 1px solid @z-border-color;
border-left: 1px solid @z-border-color;
}
.radius-right {
border-bottom-right-radius: 50%;
border-top-right-radius: 50%;
border-bottom-right-radius: 6px;
border-top-right-radius: 6px;
border-block-end: 1px solid @z-border-color;
border-block-start: 1px solid @z-border-color;
border-right: 1px solid @z-border-color;
}
.zoom-icon {
align-items: center;
@ -378,10 +378,4 @@ defineExpose({
}
}
}
// #zoom-control-active {
// background-color: @color1;
// background-color: @color5;
// color: @color-select;
// color: @color-select;
// }
</style>

View File

@ -150,7 +150,7 @@ defineExpose({
left: 394px;
pointer-events: none;
z-index: 99;
width: 20px;
width: 15px;
height: 100%;
display: -webkit-box;
display: -webkit-flex;

View File

@ -3,7 +3,7 @@
* @Date: 2021-08-02 09:41:41
* @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-10-16 00:30:03
* @LastEditTime: 2024-04-16 17:19:15
-->
<template>
<div
@ -59,26 +59,6 @@ const widget = ref<HTMLElement | null>(null)
const ratio = ref(0)
const temp = ref<Record<string, any>>({})
const compWidgetsRecord = ref<Record<string, any>>({})
// const setting = {
// name: '',
// type: NAME,
// uuid: -1,
// width: 0,
// height: 0,
// left: 0,
// top: 0,
// transform: '',
// opacity: 1,
// parent: '-1',
// isContainer: true,
// record: {
// width: 0,
// height: 0,
// minWidth: 0,
// minHeight: 0,
// dir: 'none',
// },
// }
const timer = ref<number | null>(null)
// const { dActiveElement, dWidgets } = useSetupMapGetters(['dActiveElement', 'dWidgets'])
@ -196,12 +176,12 @@ function touchend() {
keyChange(key, 'width', temp.value[key].width)
keyChange(key, 'height', temp.value[key].height)
// DOM
keySetValue(key, 'left', compWidgetsRecord.value[key].left * ratio.value)
keySetValue(key, 'top', compWidgetsRecord.value[key].top * ratio.value)
keySetValue(key, 'left', compWidgetsRecord.value[key]?.left * ratio.value)
keySetValue(key, 'top', compWidgetsRecord.value[key]?.top * ratio.value)
// this.keySetValue(key, 'left', Number(document.getElementById(key).style.left.replace('px', '')) * this.ratio)
// this.keySetValue(key, 'top', Number(document.getElementById(key).style.top.replace('px', '')) * this.ratio)
if (temp.value[key].raw.type === 'w-text') {
keyChange(key, 'fontSize', compWidgetsRecord.value[key].fontSize * ratio.value)
keyChange(key, 'fontSize', compWidgetsRecord.value[key]?.fontSize * ratio.value)
// this.keyChange(key, 'fontSize', this.temp[key].raw.fontSize * this.ratio)
// this.keyChange(key, 'letterSpacing', this.temp[key].raw.letterSpacing * this.ratio)
}

View File

@ -0,0 +1,48 @@
<!--
* @Author: ShawnPhang
* @Date: 2021-08-02 09:41:41
* @Description: 静态组件
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-16 16:13:56
-->
<template>
<div
:style="{
position: 'absolute',
left: (props.params.left || 0) - (props.parent?.left || 0) + 'px',
top: (props.params.top || 0) - (props.parent.top || 0) + 'px',
width: params.width + 'px',
height: params.height + 'px',
opacity: params.opacity,
}"
>
<slot></slot>
</div>
</template>
<script lang="ts" setup>
//
export type TParamsData = {
left: number
top: number
width: number
height: number
opacity: number
rotate: number
uuid: string
lock: boolean
fontSize: number
}
type TProps = {
params?: Partial<TParamsData>
parent?: Partial<Pick<TParamsData, "top" | "left">>
}
const props = withDefaults(defineProps<TProps>(), {
params: () => ({}),
parent: () => ({})
})
</script>

View File

@ -0,0 +1,92 @@
<!--
* @Author: ShawnPhang
* @Date: 2024-04-12 17:47:19
* @Description: 静态组件
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-16 16:25:35
-->
<template>
<div
ref="widgetRef"
:style="{
position: state.position,
left: params.left - parent.left + 'px',
top: params.top - parent.top + 'px',
width: params.width + 'px',
height: params.height + 'px',
opacity: params.opacity,
}"
>
<div :style="{ transform: params.flip ? `rotate${params.flip}(180deg)` : undefined, borderRadius: params.radius + 'px', '-webkit-mask-image': `${params.mask ? `url('${params.mask}')` : 'initial'}` }" :class="['img__box', { mask: params.mask }]">
<div v-if="params.isNinePatch" ref="targetRef" class="target" :style="{ border: `${(params.height * params.sliceData.ratio) / 2}px solid transparent`, borderImage: `url('${params.imgUrl}') ${params.sliceData.left} round` }"></div>
<img v-else ref="targetRef" class="target" style="transform-origin: center" :src="params.imgUrl" />
</div>
</div>
</template>
<script lang="ts" setup>
import { CSSProperties, reactive, ref } from 'vue'
import setting from "./wImageSetting"
type TProps = {
params: typeof setting
parent: {
left: number
top: number
}
}
type TState = {
position: 'absolute' | 'relative', // 'absolute'relative
editBoxStyle: CSSProperties,
cropWidgetXY: {
x: number
y: number
}
holdPosition: {
left: number
top: number
}
}
const props = defineProps<TProps>()
const state = reactive<TState>({
position: 'absolute', // 'absolute'relative
editBoxStyle: {
transformOrigin: 'center',
transform: '',
},
cropWidgetXY: {
x: 0,
y: 0,
},
holdPosition: {
left: 0,
top: 0,
}
})
const widgetRef = ref<HTMLElement | null>(null)
const targetRef = ref<HTMLImageElement | null>(null)
</script>
<style lang="less" scoped>
.mask {
-webkit-mask-size: 100% 100%;
-webkit-mask-position: center;
}
.img__box {
width: 100%;
height: 100%;
overflow: hidden;
img {
display: block;
}
}
.target {
display: block;
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,135 @@
<!--
TODO: 重构
-->
<template>
<div
ref="widgetRef"
:style="{
position: state.position,
left: params.left - parent.left + 'px',
top: params.top - parent.top + 'px',
width: params.width + 'px',
height: params.height + 'px',
opacity: params.opacity,
}"
></div>
</template>
<script lang="ts" setup>
import { TWSvgSetting } from './wSvgSetting'
import { CSSProperties, computed, nextTick, onBeforeMount, onMounted, onUpdated, reactive, ref, watch } from 'vue';
type TProps = {
params: TWSvgSetting
parent: {
left: number
top: number
}
}
type TState = {
position: CSSProperties['position'], // 'absolute'relative
}
const props = defineProps<TProps>()
const state = reactive<TState>({
position: 'absolute', // 'absolute'relative
})
const widgetRef = ref<HTMLElement | null>(null)
let svgElements: Record<string, any>[] | null = null
onMounted(async () => {
await nextTick()
await loadSvg()
})
function loadSvg() {
// console.log(this.params)
const Snap = (window as any).Snap
return new Promise<void>((resolve) => {
Snap.load(
props.params.svgUrl,
function (svg: Record<string, any>) {
let svg2 = Snap(svg.node)
// let item = svg2.select('circle')
// item.attr({
// fill: 'rgb(255, 0, 0)',
// })
// console.log(item.attr('fill'))
let items = svg2.node.childNodes
svg2.node.removeAttribute('width')
svg2.node.removeAttribute('height')
svg2.node.setAttribute('style', 'height: inherit;width: inherit;')
// svg2.node.setAttribute('height', 'inherit')
svgElements = []
const colorsObj = color2obj()
deepElement(items)
function deepElement(els: Record<string, any>) {
// NodeList
if (els.item) {
els.forEach((element: Record<string, any>) => {
elementFactory(element)
if (element.childNodes.length > 0) {
element.childNodes.forEach((element: Record<string, any>) => {
deepElement(element)
})
}
})
} else {
elementFactory(els)
}
}
// :
function elementFactory(element: Record<string, any>) {
const attrsColor: Record<string, any> = {}
try {
element.attributes.forEach((attr: Record<string, any>) => {
if (colorsObj[attr.value]) {
// console.log(attr.name, colorsObj[attr.value])
attr.value = colorsObj[attr.value]
attrsColor[attr.name] = props.params.colors.findIndex((x) => x == attr.value)
}
})
} catch (e) {}
if (JSON.stringify(attrsColor) !== '{}' && svgElements) {
svgElements.push({
item: element,
attrsColor,
})
}
// console.log(element.attributes, element.getAttribute('fill'), _this.params.colors)
}
// _this.viewBox = svg2.node.viewBox.baseVal
// _this.svgImg = img
// img.attr({
// width: '100%',
// height: '100%',
// transform: '',
// 'xlink:href': _this.params.imgUrl || '',
// })
if (widgetRef.value) {
// svg.node.classList.add('svg__box')
widgetRef.value.appendChild(svg.node)
}
resolve()
},
document.getElementById(props.params.uuid),
)
})
}
function color2obj() {
const obj: Record<string, any> = {}
for (let i = 0; i < props.params.colors.length; i++) {
obj[`{{colors[${i}]}}`] = props.params.colors[i]
}
return obj
}
</script>

View File

@ -0,0 +1,100 @@
<template>
<div
ref="widget"
:style="{
position: 'absolute',
left: params.left - parent.left + 'px',
top: params.top - parent.top + 'px',
width: params.width + 'px',
minWidth: params.fontSize + 'px',
minHeight: params.fontSize * params.lineHeight + 'px',
height: params.height + 'px',
lineHeight: params.fontSize * params.lineHeight + 'px',
letterSpacing: (params.fontSize * params.letterSpacing) / 100 + 'px',
fontSize: params.fontSize + 'px',
color: params.color,
textAlign: params.textAlign,
fontWeight: params.fontWeight,
fontStyle: params.fontStyle,
textDecoration: params.textDecoration,
opacity: params.opacity,
backgroundColor: params.backgroundColor,
writingMode: params.writingMode,
fontFamily: `'${params.fontClass.value}'`,
}"
>
<template v-if="params.textEffects">
<div
v-for="(ef, efi) in params.textEffects"
:key="efi + 'effect'"
:style="{
fontFamily: `'${params.fontClass.value}'`,
color: ef.filling && ef.filling.enable && ef.filling.type === 0 ? ef.filling.color : 'transparent',
WebkitTextStroke: ef.stroke && ef.stroke.enable ? `${ef.stroke.width}px ${ef.stroke.color}` : undefined,
textShadow: ef.shadow && ef.shadow.enable ? `${ef.shadow.offsetX}px ${ef.shadow.offsetY}px ${ef.shadow.blur}px ${ef.shadow.color}` : undefined,
backgroundImage: ef.filling && ef.filling.enable ? (ef.filling.type === 0 ? undefined : getGradientOrImg(ef)) : undefined,
WebkitBackgroundClip: ef.filling && ef.filling.enable ? (ef.filling.type === 0 ? undefined : 'text') : undefined,
transform: ef.offset && ef.offset.enable ? `translate(${ef.offset.x}px, ${ef.offset.y}px)` : undefined,
}"
class="edit-text effect-text"
spellcheck="false"
v-html="params.text"
></div>
</template>
<div
:style="{ fontFamily: `'${params.fontClass.value}'` }"
class="edit-text" spellcheck="false"
v-html="params.text"></div>
</div>
</template>
<script lang="ts" setup>
import { reactive, onMounted, ref } from 'vue'
import getGradientOrImg from './getGradientOrImg'
import { wTextSetting } from './wTextSetting'
export type TwTextParams = {
rotate?: number
lock?: boolean
width?: number
height?: number
} & typeof wTextSetting
type TProps = {
params: TwTextParams
parent: {
left: number
top: number
}
}
const props = defineProps<TProps>()
const widget = ref<HTMLElement | null>(null)
onMounted(() => {
if (!widget.value) return
props.params.transform && (widget.value.style.transform = props.params.transform)
props.params.rotate && (widget.value.style.transform += `translate(0px, 0px) rotate(${props.params.rotate}) scale(1, 1)`)
})
defineExpose({
getGradientOrImg,
widget,
})
</script>
<style lang="less" scoped>
.edit-text {
outline: none;
word-break: break-word;
white-space: pre-wrap;
margin: 0;
}
.effect-text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>

View File

@ -3,7 +3,7 @@
* @Date: 2024-04-05 07:31:45
* @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-10 00:34:21
* @LastEditTime: 2024-04-12 01:00:40
*/
// const prefix = import.meta.env
const prefix = process.env
@ -23,7 +23,7 @@ export default {
IMG_URL: 'https://store.palxp.cn/', // 七牛云资源地址
// ICONFONT_URL: '//at.alicdn.com/t/font_3223711_74mlzj4jdue.css',
ICONFONT_URL: '//at.alicdn.com/t/font_2717063_ypy8vprc3b.css?display=swap',
ICONFONT_EXTRA: '//at.alicdn.com/t/c/font_3228074_ljv2tbkwgqp.css',
ICONFONT_EXTRA: '//at.alicdn.com/t/c/font_3228074_xojoer6zhp.css',
QINIUYUN_PLUGIN: 'https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/qiniu-js/2.5.5/qiniu.min.js',
supportSubFont: true, // 是否开启服务端字体压缩
}

View File

@ -3,7 +3,7 @@
* @Date: 2022-03-09 14:20:09
* @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-04 00:33:01
* @LastEditTime: 2024-04-16 19:19:36
*/
import { useControlStore, useWidgetStore } from '@/store'
import { TdWidgetData } from '@/store/design/widget'
@ -50,7 +50,7 @@ export default function keyCodeOptions(e: any, params: any) {
break
}
if (e.key === ' ') {
if (e.key === ' ' && widgetStore.dActiveElement?.uuid == '-1') {
dealWithSpace(e)
}
}

View File

@ -2,8 +2,8 @@
* @Author: ShawnPhang
* @Date: 2021-08-19 18:43:22
* @Description:
* @LastEditors: ShawnPhang <site: book.palxp.com>
* @LastEditTime: 2023-07-31 09:31:52
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-16 19:10:18
*/
import { useControlStore, useWidgetStore } from '@/store'
// import store from '@/store'
@ -22,12 +22,6 @@ const move = {
originX: target.left,
originY: target.top,
})
// store.dispatch('initDMove', {
// startX: e.pageX,
// startY: e.pageY,
// originX: target.left,
// originY: target.top,
// })
// 绑定鼠标移动事件
document.addEventListener('mousemove', this.handlemousemove, true)
@ -45,10 +39,6 @@ const move = {
x: e.pageX,
y: e.pageY,
})
// store.dispatch('dMove', {
// x: e.pageX,
// y: e.pageY,
// })
},
handlemouseup() {
@ -56,7 +46,6 @@ const move = {
document.removeEventListener('mousemove', this.handlemousemove, true)
document.removeEventListener('mouseup', this.handlemouseup, true)
controlStore.stopDMove()
// store.dispatch('stopDMove')
},
},
}
@ -70,7 +59,6 @@ const moveInit = {
// 设置mouseevent给moveable初始
// 在组合操作时排除
widgetStore.setMouseEvent(e)
// store.commit('setMouseEvent', e)
}
const target = widgetStore.dActiveElement
@ -81,18 +69,11 @@ const moveInit = {
originX: target.left,
originY: target.top,
})
// store.dispatch('initDMove', {
// startX: e.pageX,
// startY: e.pageY,
// originX: target.left,
// originY: target.top,
// })
const handlemouseup = () => {
const widgetStore = useWidgetStore()
// 销毁选中即刻移
widgetStore.setMouseEvent(null)
// store.commit('setMouseEvent', null)
document.removeEventListener('mouseup', handlemouseup, true)
}

View File

@ -3,7 +3,7 @@
* @Date: 2024-04-04 00:36:13
* @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-05 05:56:27
* @LastEditTime: 2024-04-11 15:05:41
*/
const ctrlKey = isMacOS() ? `` : `Ctrl`
function isMacOS() {
@ -35,14 +35,6 @@ export default [
feat: `重做`,
info: `${ctrlKey} + Shift + Z`,
},
{
feat: `元素移动`,
info: `← ↑ → ↓`,
},
{
feat: `快速移动`,
info: `Shift + ← ↑ → ↓`,
},
{
feat: `复制`,
info: `${ctrlKey} + C`,
@ -55,6 +47,14 @@ export default [
feat: `删除`,
info: `Delete / Backspace`,
},
{
feat: `元素移动`,
info: `← ↑ → ↓`,
},
{
feat: `快速移动`,
info: `Shift + ← ↑ → ↓`,
},
{
feat: `多选`,
info: `${ctrlKey} / Shift + 点选`,

View File

@ -3,7 +3,7 @@
* @Date: 2021-08-01 14:12:08
* @Description: mixin形式放入views/index.vue中
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-04 00:36:19
* @LastEditTime: 2024-04-16 19:17:51
*/
import keyCodeOptions from './methods/keyCodeOptions'
import dealWithCtrl from './methods/dealWithCtrl'
@ -71,7 +71,7 @@ const shortcuts = {
if (e.key === 'Alt' || e.key === 'Shift' || e.key === 'Control' || e.key === 'Meta') {
store.updateAltDown(false)
}
if (e.key === ' ') {
if (e.key === ' ' && controlStore.dSpaceDown) {
appContainer.classList.remove('move-case');
controlStore.setSpaceDown(false)
widgetStore.lockWidgets()

View File

@ -3,7 +3,7 @@
* @Date: 2024-04-05 06:23:23
* @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-05 14:53:01
* @LastEditTime: 2024-04-16 12:13:54
*/
export type TScreeData = {
/** 记录编辑界面的宽度 */
@ -20,6 +20,10 @@ export type TGuidelinesData = {
export type TCanvasState = {
/** 画布缩放百分比 */
dZoom: number
/** 画布默认预留边距 */
dPresetPadding: number,
/** 画布底部工具栏高度 */
dBottomHeight: number,
/** 画布垂直居中修正值 */
dPaddingTop: number
/** 编辑界面 */
@ -28,6 +32,8 @@ export type TCanvasState = {
guidelines: TGuidelinesData
/** 页面数据 */
dPage: TPageState
/** 当前页面下标 */
dCurrentPage: number
}
export type TStoreAction = {
@ -47,8 +53,13 @@ export type TStoreAction = {
value: TPageState[T]
pushHistory?: boolean
}): void
getDPage(data: TPageState): void
/** 设置dPage */
setDPage(data: TPageState): void
/** 更新 Page从layouts获取*/
updateDPage(): void
/** 设置底部工具栏高度 */
setBottomHeight(h: number): void
}
export type TPageState = {
@ -75,11 +86,5 @@ export type TPageState = {
opacity: number
/** 强制刷新用 */
tag: number
setting:{
label: string
parentKey: string
value: boolean
}[]
record: Record<string, any>
}

View File

@ -4,17 +4,21 @@
* @Date: 2024-03-18 21:00:00
* @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-08 21:23:38
* @LastEditTime: 2024-04-16 12:16:25
*/
import { Store, defineStore } from 'pinia'
import { TCanvasState, TScreeData, TGuidelinesData, TStoreAction, TPageState } from './d'
import { useWidgetStore } from "@/store";
import pageDefault from './page-default';
/** 画布全局设置 */
const CanvasStore = defineStore<"canvasStore", TCanvasState, {}, TStoreAction>("canvasStore", {
state: () => ({
dZoom: 0, // 画布缩放百分比
dPaddingTop: 0, // 画布垂直居中修正值
dPresetPadding: 25, // 画布默认预留边距
dBottomHeight: 0, // 画布底部工具栏高度
dPaddingTop: 0, // 用于画布垂直居中的修正值
dScreen: {
width: 0, // 记录编辑界面的宽度
height: 0, // 记录编辑界面的高度
@ -24,30 +28,15 @@ const CanvasStore = defineStore<"canvasStore", TCanvasState, {}, TStoreAction>("
verticalGuidelines: [],
horizontalGuidelines: [],
},
dPage: {
name: '背景页面',
type: 'page',
uuid: '-1',
left: 0,
top: 0,
width: 1920, // 画布宽度
height: 1080, // 画布高度
backgroundColor: '#ffffff', // 画布背景颜色
backgroundGradient: '', // 用于兼容渐变颜色
backgroundImage: '', // 画布背景图片
backgroundTransform: {},
opacity: 1, // 透明度
tag: 0, // 强制刷新用
setting: [
{
label: '背景颜色',
parentKey: 'backgroundColor',
value: false,
},
],
record: {},
}
dCurrentPage: 0,
dPage: pageDefault
}),
getters: {
getDPage() {
const widgetStore = useWidgetStore()
return widgetStore.dLayouts[this.dCurrentPage].global
},
},
actions: {
/** 更新画布缩放百分比 */
updateZoom(zoom: number) {
@ -80,11 +69,27 @@ const CanvasStore = defineStore<"canvasStore", TCanvasState, {}, TStoreAction>("
},
/** 设置 Page */
setDPage(data: TPageState) {
const cur = this.dPage
const keys = Object.keys(data) as (keyof TPageState)[];
keys.forEach(val => {
cur[val] = data[val]
})
this.dPage = data
this.updateDPage()
// const cur = this.dPage
// const keys = Object.keys(data) as (keyof TPageState)[];
// keys.forEach(val => {
// cur[val] = data[val]
// })
},
/** 更新 Pagelayouts*/
updateDPage() {
const widgetStore = useWidgetStore()
widgetStore.dLayouts[this.dCurrentPage].global = this.dPage
// const cur = this.dPage
// const keys = Object.keys(data) as (keyof TPageState)[];
// keys.forEach(val => {
// cur[val] = data[val]
// })
},
/** 设置底部工具栏高度 */
setBottomHeight(h: number) {
this.dBottomHeight = h
}
}
})

View File

@ -0,0 +1,22 @@
/*
* @Author: ShawnPhang
* @Date: 2024-04-16 10:06:23
* @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-16 10:06:32
*/
export default {
name: '新画板',
type: 'page',
uuid: '-1',
left: 0,
top: 0,
width: 1920, // 画布宽度
height: 1080, // 画布高度
backgroundColor: '#ffffffff', // 画布背景颜色
backgroundGradient: '', // 用于兼容渐变颜色
backgroundImage: '', // 画布背景图片
backgroundTransform: {},
opacity: 1, // 透明度
tag: 0, // 强制刷新用
}

View File

@ -2,14 +2,14 @@
* @Author: Jeremy Yu
* @Date: 2024-03-18 21:00:00
* @Description:
* @LastEditors: Jeremy Yu <https://github.com/JeremyYu-cn>
* @LastEditTime: 2024-03-27 21:00:00
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-15 16:53:51
*/
import { Store, defineStore } from "pinia"
import {pushHistory, pushColorToHistory} from "./actions/pushHistory"
import handleHistory from "./actions/handleHistory"
import { useCanvasStore, useWidgetStore } from "@/store"
import { Store, defineStore } from 'pinia'
import { pushHistory, pushColorToHistory } from './actions/pushHistory'
import handleHistory from './actions/handleHistory'
import { useCanvasStore, useWidgetStore } from '@/store'
export type THistoryParamData = {
index: number
@ -20,11 +20,9 @@ export type THistoryParamData = {
type THistoryState = {
/** 记录历史操作保存整个画布的json数据 */
dHistory: string[]
/** 记录历史操作对应的激活的组件的uuid */
dActiveUuidHistory: string[]
/** 记录历史操作对应的page */
dPageHistory: string[]
dHistoryParams: THistoryParamData,
dHistoryParams: THistoryParamData
/** 记录历史选择的颜色 */
dColorHistory: string[]
}
@ -41,12 +39,12 @@ type THistoryAction = {
* action为undo表示撤销
* action为redo表示重做
*/
handleHistory: (action: "undo" | "redo") => void
handleHistory: (action: 'undo' | 'redo') => void
pushColorToHistory: (color: string) => void
}
/** 历史记录Store */
const HistoryStore = defineStore<"historyStore", THistoryState, {}, THistoryAction>("historyStore", {
const HistoryStore = defineStore<'historyStore', THistoryState, {}, THistoryAction>('historyStore', {
state: () => ({
dHistory: [],
dHistoryParams: {
@ -54,9 +52,8 @@ const HistoryStore = defineStore<"historyStore", THistoryState, {}, THistoryActi
length: 0,
maxLength: 20,
},
dActiveUuidHistory: [],
dColorHistory: [],
dPageHistory: []
dPageHistory: [],
}),
actions: {
@ -72,10 +69,10 @@ const HistoryStore = defineStore<"historyStore", THistoryState, {}, THistoryActi
},
pushColorToHistory(color) {
pushColorToHistory(this, color)
}
}
},
},
})
export type THistoryStore = Store<"historyStore", THistoryState, {}, THistoryAction>
export type THistoryStore = Store<'historyStore', THistoryState, {}, THistoryAction>
export default HistoryStore

View File

@ -3,7 +3,7 @@
* @Date: 2024-03-18 21:00:00
* @Description: Store方法export
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-10 18:01:14
* @LastEditTime: 2024-04-16 17:25:42
*/
import { useCanvasStore, useControlStore } from '@/store'
@ -136,8 +136,10 @@ export function autoResizeAll(store: TWidgetStore, lastPageSize: TSize) {
let diff = 0
if (widget.type === 'w-text') {
widget.fontSize && (widget.fontSize *= ratio)
} else if (widget.type !== 'w-group') {
widget.width *= ratio
widget.height *= ratio
} else widget.height *= ratio
widget.width *= ratio
diff = (originWidth - widget.width) / 2
widget.left = widget.left + diff + pageDiff
widget.top *= degree[1]

View File

@ -2,29 +2,27 @@
* @Author: Jeremy Yu
* @Date: 2024-03-28 21:00:00
* @Description:
* @LastEditors: Jeremy Yu <https://github.com/JeremyYu-cn>
* @LastEditTime: 2024-03-28 14:00:00
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-16 01:03:05
*/
import { customAlphabet } from 'nanoid/non-secure'
import { TWidgetStore, TdWidgetData } from '..'
import { useCanvasStore, useHistoryStore } from '@/store'
import { useCanvasStore, useWidgetStore } from '@/store'
const nanoid = customAlphabet('1234567890abcdef', 12)
// TODO: 选择模板
export function setTemplate(store: TWidgetStore, allWidgets: TdWidgetData[]) {
const historyStore = useHistoryStore()
// const historyStore = useHistoryStore()
const canvasStore = useCanvasStore()
const widgetStore = useWidgetStore()
allWidgets.forEach((item) => {
Number(item.uuid) < 0 && (item.uuid = nanoid()) // 重设id
item.text && (item.text = decodeURIComponent(item.text))
store.dWidgets.push(item)
})
historyStore.pushHistory("setTemplate")
// store.dispatch('pushHistory', 'setTemplate')
widgetStore.updateDWidgets()
// historyStore.pushHistory("setTemplate")
canvasStore.reChangeCanvas()
// store.dispatch('reChangeCanvas')
}

View File

@ -2,12 +2,12 @@
* @Author: Jeremy Yu
* @Date: 2024-03-28 21:00:00
* @Description:
* @LastEditors: Jeremy Yu <https://github.com/JeremyYu-cn>
* @LastEditTime: 2024-03-28 14:00:00
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-16 11:12:40
*/
import { useCanvasStore, useHistoryStore } from "@/store"
import { TWidgetStore, TdWidgetData } from ".."
import { useCanvasStore, useHistoryStore } from '@/store'
import { TWidgetStore, TdWidgetData } from '..'
import { customAlphabet } from 'nanoid/non-secure'
const nanoid = customAlphabet('1234567890abcdef', 12)
@ -57,11 +57,11 @@ export function updateWidgetData(store: TWidgetStore, { uuid, key, value, pushHi
}
break
}
(widget[key] as TUpdateWidgetPayload['value']) = value
;(widget[key] as TUpdateWidgetPayload['value']) = value
if (pushHistory) {
const historyStore = useHistoryStore()
setTimeout(() => {
historyStore.pushHistory("updateWidgetData")
historyStore.pushHistory('updateWidgetData')
// pushHistory && store.dispatch('pushHistory', 'updateWidgetData')
}, 100)
}
@ -107,12 +107,12 @@ export function updateWidgetMultiple(store: TWidgetStore, { uuid, data, pushHist
}
break
}
(widget[key] as number | string) = value
;(widget[key] as number | string) = value
}
}
setTimeout(() => {
const historyStore = useHistoryStore()
historyStore.pushHistory("updateWidgetMultiple")
historyStore.pushHistory('updateWidgetMultiple')
// store.dispatch('pushHistory', 'updateWidgetMultiple')
}, 100)
}
@ -133,7 +133,7 @@ export function addWidget(store: TWidgetStore, setting: TdWidgetData) {
// uuid: store.dWidgets[len - 1].uuid,
// })
historyStore.pushHistory("addWidget")
historyStore.pushHistory('addWidget')
// store.dispatch('pushHistory', 'addWidget')
canvasStore.reChangeCanvas()
// store.dispatch('reChangeCanvas')
@ -154,12 +154,15 @@ export function deleteWidget(store: TWidgetStore) {
const uuid = selectWidgets[i].uuid
const index = widgets.findIndex((item) => item.uuid === uuid)
widgets.splice(index, 1)
try {
// 清除掉可能存在的选中框
document.getElementById(uuid)?.classList.remove('widget-selected')
} catch (e) {}
// try {
// // 清除掉可能存在的中框
// document.getElementById(uuid)?.classList.remove('widget-selected')
// } catch (e) {}
}
store.dSelectWidgets = []
store.selectWidget({
uuid: '-1',
})
} else {
if (activeElement.type === 'page') {
return
@ -213,7 +216,7 @@ export function deleteWidget(store: TWidgetStore) {
// store.dispatch('updateGroupSize', store.dActiveElement.uuid)
}
historyStore.pushHistory("deleteWidget")
historyStore.pushHistory('deleteWidget')
// store.dispatch('pushHistory', 'deleteWidget')
canvasStore.reChangeCanvas()
// store.dispatch('reChangeCanvas')
@ -229,11 +232,19 @@ export type TsetWidgetStyleData = {
export function setWidgetStyle(state: TWidgetStore, { uuid, key, value, pushHistory }: TsetWidgetStyleData) {
const widget = state.dWidgets.find((item) => item.uuid === uuid)
if (!widget) return
(widget[key] as Record<string, any>) = value
;(widget[key] as Record<string, any>) = value
}
export function setDWidgets(state: TWidgetStore, e: TdWidgetData[]) {
state.dWidgets = e
updateDWidgets(state)
}
export function updateDWidgets(state: TWidgetStore) {
const pageStore = useCanvasStore()
const { dCurrentPage } = pageStore
state.dLayouts[dCurrentPage].layers = state.dWidgets
state.dWidgets = state.getWidgets
}
// 锁定所有图层 / 再次调用时还原图层
@ -253,4 +264,4 @@ export function lockWidgets(state: TWidgetStore) {
widget.lock = true
})
}
}
}

View File

@ -2,13 +2,13 @@
* @Author: Jeremy Yu
* @Date: 2024-03-28 14:00:00
* @Description:
* @LastEditors: Jeremy Yu <https://github.com/JeremyYu-cn>
* @LastEditTime: 2024-03-28 14:00:00
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-15 16:54:45
*/
import { useCanvasStore } from "@/store"
import { TWidgetState, TdWidgetData } from ".."
import { TPageState } from "@/store/design/canvas/d"
import { useCanvasStore } from '@/store'
import { TWidgetState, TdWidgetData } from '..'
import { TPageState } from '@/store/design/canvas/d'
export type TWidgetJsonData = TPageState & {
widgets: TdWidgetData
@ -17,9 +17,7 @@ export type TWidgetJsonData = TPageState & {
/** 返回组件Json数据 */
export function widgetJsonData(state: TWidgetState) {
const pageStore = useCanvasStore()
const page: TWidgetJsonData = JSON.parse(JSON.stringify(pageStore.dPage))
const widgets = JSON.parse(JSON.stringify(state.dWidgets))
page.widgets = widgets
return page
const { dCurrentPage } = pageStore
// const page: TWidgetJsonData = JSON.parse(JSON.stringify(pageStore.dPage))
return state.dLayouts[dCurrentPage].layers
}

View File

@ -3,14 +3,14 @@
* @Date: 2024-03-18 21:00:00
* @Description: Store方法export
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-10 08:17:16
* @LastEditTime: 2024-04-16 10:07:13
*/
import { Store, defineStore } from "pinia";
import { TInidDMovePayload, TMovePayload, dMove, initDMove, setDropOver, setMouseEvent, setdActiveElement, updateGroupSize, updateHoverUuid } from "./actions";
import { TPageState } from "@/store/design/canvas/d";
import { TInitResize, TSize, TdResizePayload, dResize, initDResize, resize, autoResizeAll } from "./actions/resize";
import { TUpdateWidgetMultiplePayload, TUpdateWidgetPayload, TsetWidgetStyleData, addWidget, deleteWidget, setDWidgets, setWidgetStyle, updateWidgetData, updateWidgetMultiple, lockWidgets } from "./actions/widget";
import { TUpdateWidgetMultiplePayload, TUpdateWidgetPayload, TsetWidgetStyleData, addWidget, deleteWidget, setDWidgets, updateDWidgets, setWidgetStyle, updateWidgetData, updateWidgetMultiple, lockWidgets } from "./actions/widget";
import { addGroup } from "./actions/group";
import { setTemplate } from "./actions/template";
import { copyWidget, pasteWidget } from "./actions/clone";
@ -18,6 +18,7 @@ import { TSelectWidgetData, TselectItem, selectItem, selectWidget, selectWidgets
import { TUpdateAlignData, updateAlign } from "./actions/align";
import { TWidgetJsonData, widgetJsonData } from "./getter";
import { TupdateLayerIndexData, ungroup, updateLayerIndex } from "./actions/layer";
import pageDefault from "../canvas/page-default";
export type TdWidgetData = TPageState & Partial<TCommonItemData> & {
parent?: string
@ -35,6 +36,11 @@ export type TdWidgetData = TPageState & Partial<TCommonItemData> & {
writingMode?: string
}
export type TdLayout = {
global: TPageState
layers: TdWidgetData[]
}
export type TWidgetState = {
dActiveWidgetXY: {
/** 选中组件的横向初始值 */
@ -61,6 +67,8 @@ export type TWidgetState = {
dDropOverUuid: string
/** 已使用的组件 */
dWidgets: TdWidgetData[]
/** 所有图层数据与页面数据 */
dLayouts: TdLayout[]
/** 记录多选的组件 */
dSelectWidgets: TdWidgetData[]
/** 复制的组件(可能是单个也可能是数组) */
@ -72,7 +80,7 @@ export type TWidgetState = {
}
type TGetter = {
getWidgetJsonData(state: TWidgetState): TWidgetJsonData
getWidgets(state: TWidgetState): TWidgetJsonData
}
type TAction = {
@ -116,6 +124,7 @@ type TAction = {
resize: (data: TSize) => void
setWidgetStyle: (data: TsetWidgetStyleData) => void
setDWidgets: (data: TdWidgetData[]) => void
updateDWidgets: () => void
lockWidgets: () => void
setMouseEvent: (e: MouseEvent | null) => void
setdActiveElement: (data: TdWidgetData) => void
@ -141,6 +150,10 @@ const WidgetStore = defineStore<"widgetStore", TWidgetState, TGetter, TAction>("
dHoverUuid: '-1', // 鼠标在这个图层上
dDropOverUuid: '', // 拖动时放在哪个图层上
dWidgets: [], // 已使用的组件
dLayouts: [{
global: pageDefault,
layers: []
}], // 页面与图层数据
dSelectWidgets: [], // 记录多选的组件
selectItem: { data: null }, // 记录当前选择的元素, data
activeMouseEvent: null, // 正在活动的鼠标事件
@ -148,7 +161,7 @@ const WidgetStore = defineStore<"widgetStore", TWidgetState, TGetter, TAction>("
}),
getters: {
getWidgetJsonData(store) {
getWidgets(store) {
return widgetJsonData(store)
}
},
@ -178,6 +191,7 @@ const WidgetStore = defineStore<"widgetStore", TWidgetState, TGetter, TAction>("
resize(data) { resize(this, data) },
setWidgetStyle(data) { setWidgetStyle(this, data) },
setDWidgets(data) { setDWidgets(this, data) },
updateDWidgets() { updateDWidgets(this) },
lockWidgets() { lockWidgets(this) },
setMouseEvent(event) { setMouseEvent(this, event) },
setdActiveElement(data) { setdActiveElement(this, data) },

View File

@ -2,16 +2,15 @@
* @Author: ShawnPhang
* @Date: 2021-07-13 02:48:38
* @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn>, Jeremy Yu <https://github.com/JeremyYu-cn>
* @LastEditTime: 2022-03-07 20:25:54
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-13 18:19:28
*/
// import store from '../store'
import * as services from '../api/index'
import * as utils from './utils'
import _config from '@/config'
import modules from './plugins/modules'
import cssLoader from './plugins/cssLoader'
import type {App} from 'vue'
import type { App } from 'vue'
/**
*
@ -28,9 +27,12 @@ export default {
myVue.config.globalProperties.$utils = utils
// myVue.config.globalProperties.$bus =
// myVue.prototype.$Ilist = List;
// myVue.prototype.$Imap = mmap;
// baidu statistics
;(function () {
const hm = document.createElement('script')
hm.src = 'https://hm.baidu.com/hm.js?21238d2872af8b12083429237026b84c'
const s: any = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(hm, s)
})()
},
}

View File

@ -17,7 +17,7 @@ import { fontWithDraw, font2style } from '@/utils/widgets/loadFontRule'
import designBoard from '@/components/modules/layout/designBoard/index.vue'
import zoomControl from '@/components/modules/layout/zoomControl/index.vue'
import { useRoute } from 'vue-router'
import { wGroupSetting } from '@/components/modules/widgets/wGroup/groupSetting'
// import { wGroupSetting } from '@/components/modules/widgets/wGroup/groupSetting'
import { storeToRefs } from 'pinia'
import { useGroupStore, useCanvasStore, useWidgetStore } from '@/store'
@ -32,12 +32,12 @@ const state = reactive<TState>({
},
})
const pageStore = useCanvasStore()
const groupStore = useGroupStore()
// const groupStore = useGroupStore()
const widgetStore = useWidgetStore()
const { dPage } = storeToRefs(pageStore)
onMounted(() => {
groupStore.initGroupJson(JSON.stringify(wGroupSetting))
// groupStore.initGroupJson(JSON.stringify(wGroupSetting))
// store.dispatch('initGroupJson', JSON.stringify(wGroupSetting))
// initGroupJson(JSON.stringify(wGroup.setting))
nextTick(() => {
@ -48,37 +48,37 @@ onMounted(() => {
async function load() {
let backgroundImage = ''
let loadFlag = false
const { id, tempid, tempType: type = 0 } = route.query
const { id, tempid, tempType: type = 0, index = 0 }: any = route.query
if (id || tempid) {
const postData = {
id: Number(id || tempid),
type: Number(type)
}
const { data, width, height } = await api.home[id ? 'getWorks' : 'getTempDetail'](postData)
const content = JSON.parse(data)
const widgets = Number(type) == 1 ? content : content.widgets
let content = JSON.parse(data)
const isGroupTemplate = Number(type) == 1
if (Number(type) == 1) {
if (Array.isArray(content)) {
const { global, layers } = content[index]
content = {page: global, widgets: layers}
}
const widgets = isGroupTemplate ? content : content.widgets
if (isGroupTemplate) {
dPage.value.width = width
dPage.value.height = height
dPage.value.backgroundColor = '#ffffff00'
widgetStore.addGroup(content)
// store.dispatch('addGroup', content)
// addGroup(content)
} else {
pageStore.setDPage(content.page)
// store.commit('setDPage', content.page)
//
backgroundImage = content.page?.backgroundImage
backgroundImage && delete content.page.backgroundImage
pageStore.setDPage(content.page)
// store.commit('setDPage', content.page)
if (id) {
widgetStore.setDWidgets(widgets)
// store.commit('setDWidgets', widgets)
} else {
widgetStore.setTemplate(widgets)
// store.dispatch('setTemplate', widgets)
}
}

View File

@ -4,7 +4,7 @@
* @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastUpdateContent: Support typescript
* @LastEditTime: 2024-04-10 07:16:48
* @LastEditTime: 2024-04-16 11:35:11
-->
<template>
<div id="page-design-index" ref="pageDesignIndex" class="page-design-bg-color">
@ -37,6 +37,8 @@
<div class="shelter" :style="{ width: Math.floor((dPage.width * dZoom) / 100) + 'px', height: Math.floor((dPage.height * dZoom) / 100) + 'px' }"></div>
<!-- 提供一个背景图层 -->
<div class="shelter-bg transparent-bg" :style="{ width: Math.floor((dPage.width * dZoom) / 100) + 'px', height: Math.floor((dPage.height * dZoom) / 100) + 'px' }"></div>
<!-- 多画板操作组件 -->
<template #bottom> <multipleBoards /> </template>
</design-board>
<style-panel ref="ref3"></style-panel>
</div>
@ -86,6 +88,7 @@ import { useCanvasStore, useControlStore, useHistoryStore, useWidgetStore, useGr
import type { ButtonInstance } from 'element-plus'
import Tour from './components/Tour.vue'
import createDesign from '@/components/business/create-design'
import multipleBoards from '@/components/modules/layout/multipleBoards'
const ref1 = ref<ButtonInstance>()
const ref2 = ref<ButtonInstance>()

View File

@ -3,7 +3,7 @@
* @Date: 2022-01-10 14:57:53
* @Description: Psd文件解析
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-03 20:58:43
* @LastEditTime: 2024-04-11 15:37:38
-->
<template>
<div id="page-design-index" ref="pageDesignIndex">
@ -156,12 +156,7 @@ async function loadPSD(file: File) {
async function clear() {
widgetStore.setDWidgets([])
// store.commit('setDWidgets', [])
pageStore.setDPage(Object.assign(pageStore.dPage, { width: 1920, height: 1080, backgroundColor: '#ffffff', backgroundImage: '' }))
// store.commit('setDPage', Object.assign(store.getters.dPage, { width: 1920, height: 1080, backgroundColor: '#ffffff', backgroundImage: '' }))
// store.commit('setShowMoveable', false)
controlStore.setShowMoveable(false)
await nextTick()

View File

@ -3,7 +3,7 @@
* @Date: 2022-01-12 11:26:53
* @Description: 顶部操作按钮组
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-04-09 23:39:24
* @LastEditTime: 2024-04-16 15:38:39
-->
<template>
<div class="top-title"><el-input v-model="state.title" placeholder="未命名的设计" class="input-wrap" /></div>
@ -94,14 +94,13 @@ async function save(hasCover: boolean = false) {
if (dHistory.value.length <= 0) {
return
}
controlStore.setShowMoveable(false) //
// console.log(proxy?.dPage, proxy?.dWidgets)
const { id, tempid } = route.query
const cover = hasCover ? await draw() : undefined
const widgets = dWidgets.value // reviseData()
const { id: newId, stat, msg } = await api.home.saveWorks({ cover, id: (id as string), title: state.title || '未命名设计', data: JSON.stringify({ page: dPage.value, widgets }), temp_id: (tempid as string), width: dPage.value.width, height: dPage.value.height })
// const widgets = dWidgets.value // reviseData()
const data = widgetStore.dLayouts
const { id: newId, stat, msg } = await api.home.saveWorks({ cover, id: (id as string), title: state.title || '未命名设计', data: JSON.stringify(data), temp_id: (tempid as string), width: dPage.value.width, height: dPage.value.height })
stat !== 0 ? useNotification('保存成功', '可在"我的作品"中查看') : useNotification('保存失败', msg, { type: 'error' })
!id && router.push({ path: '/home', query: { id: newId }, replace: true })
controlStore.setShowMoveable(true)
@ -168,7 +167,7 @@ async function saveTemp() {
clearInterval(animation)
}
}, 800)
await _dl.downloadImg(api.home.download({ id, width, height }) + '&r=' + Math.random(), (progress: number, xhr: any) => {
await _dl.downloadImg(api.home.download({ id, width, height, index: pageStore.dCurrentPage }) + '&r=' + Math.random(), (progress: number, xhr: any) => {
if (props.modelValue) {
clearInterval(animation)
progress >= timerCount && emit('change', { downloadPercent: Number(progress.toFixed(0)), downloadText: '图片生成中' })
@ -198,7 +197,8 @@ async function load(cb: () => void) {
await useFontStore.init() //
}
const apiName = tempId && !id ? 'getTempDetail' : 'getWorks'
if (w_h) {
if (w_h && !id && !tempId) {
//
const wh: any = w_h.toString().split('*')
wh[0] && (dPage.value.width = wh[0])
wh[1] && (dPage.value.height = wh[1])
@ -208,25 +208,29 @@ async function load(cb: () => void) {
return
}
const { data: content, title, state: _state, width, height } = await api.home[apiName]({ id: id || tempId, type })
if (content) {
const data = JSON.parse(content)
state.stateBollean = !!_state
state.title = title
controlStore.setShowMoveable(false) //
// this.$store.commit('setDWidgets', [])
if (type == 1) {
//
dPage.value.width = width
dPage.value.height = height
widgetStore.addGroup(data)
if (!content) return
const data = JSON.parse(content)
state.stateBollean = !!_state
state.title = title
controlStore.setShowMoveable(false) //
if (type == 1) {
//
dPage.value.width = width
dPage.value.height = height
widgetStore.addGroup(data)
} else {
if (Array.isArray(data)) {
widgetStore.dLayouts = data
widgetStore.setDWidgets(widgetStore.getWidgets)
} else {
pageStore.setDPage(data.page)
id ? widgetStore.setDWidgets(data.widgets) : widgetStore.setTemplate(data.widgets)
widgetStore.dLayouts = [{global: data.page, layers: data.widgets}]
id ? widgetStore.setDWidgets(widgetStore.getWidgets) : widgetStore.setTemplate(widgetStore.getWidgets)
}
cb()
historyStore.pushHistory('请求加载load')
pageStore.setDPage(pageStore.getDPage)
// id ? widgetStore.setDWidgets(data.widgets) : widgetStore.setTemplate(data.widgets)
}
cb()
historyStore.pushHistory('请求加载load')
}
function draw() {