mirror of
https://github.com/palxiao/poster-design.git
synced 2025-06-08 03:19:59 +08:00
feat: support multi-canvas & adaptive canvas
This commit is contained in:
parent
22ecc9900d
commit
b739528c21
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
2
src/components/modules/layout/multipleBoards/index.ts
Normal file
2
src/components/modules/layout/multipleBoards/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import index from './multipleBoards.vue'
|
||||
export default index
|
313
src/components/modules/layout/multipleBoards/multipleBoards.vue
Normal file
313
src/components/modules/layout/multipleBoards/multipleBoards.vue
Normal 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>
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
|
48
src/components/modules/widgets/wGroup/wGroupStatic.vue
Normal file
48
src/components/modules/widgets/wGroup/wGroupStatic.vue
Normal 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>
|
||||
|
92
src/components/modules/widgets/wImage/wImageStatic.vue
Normal file
92
src/components/modules/widgets/wImage/wImageStatic.vue
Normal 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>
|
135
src/components/modules/widgets/wSvg/wSvgStatic.vue
Normal file
135
src/components/modules/widgets/wSvg/wSvgStatic.vue
Normal 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>
|
100
src/components/modules/widgets/wText/wTextStatic.vue
Normal file
100
src/components/modules/widgets/wText/wTextStatic.vue
Normal 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>
|
@ -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, // 是否开启服务端字体压缩
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 + 点选`,
|
||||
|
@ -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()
|
||||
|
@ -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>
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
// })
|
||||
},
|
||||
/** 更新 Page(layouts)*/
|
||||
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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
22
src/store/design/canvas/page-default.ts
Normal file
22
src/store/design/canvas/page-default.ts
Normal 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, // 强制刷新用
|
||||
}
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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')
|
||||
}
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) },
|
||||
|
@ -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)
|
||||
})()
|
||||
},
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>()
|
||||
|
@ -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()
|
||||
|
@ -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() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user