feat: 添加主界面
parent
79e016fbd7
commit
2bc4ac1d34
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<a-modal title="选择图片" v-model:visible="visible" title-align="start" :closable="false">
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<div v-for="item in images" :key="item" class="w-24 h-24 bg-gray-200 flex items-center justify-center">
|
||||
<img :src="item" class="w-full h-full object-cover" />
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const visible = ref(false);
|
||||
const images = ref([]);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
export interface Block<T = any> {
|
||||
id: string;
|
||||
type: string;
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
xFixed: boolean;
|
||||
yFixed: boolean;
|
||||
bgImage?: string;
|
||||
bgColor?: string;
|
||||
data: T;
|
||||
meta: Record<string, any>;
|
||||
actived: false,
|
||||
resizable: boolean,
|
||||
draggable: boolean,
|
||||
}
|
||||
|
||||
export const DefaultBlock: Block = {
|
||||
id: "",
|
||||
type: "",
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 50,
|
||||
h: 50,
|
||||
xFixed: false,
|
||||
yFixed: false,
|
||||
bgImage: "",
|
||||
bgColor: "",
|
||||
data: {},
|
||||
meta: {},
|
||||
actived: false,
|
||||
resizable: true,
|
||||
draggable: true,
|
||||
};
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { Component } from "vue";
|
||||
import { Block } from "./block";
|
||||
|
||||
interface Blocker {
|
||||
/**
|
||||
* 组件默认值
|
||||
*/
|
||||
initial: Block;
|
||||
/**
|
||||
* 渲染组件
|
||||
*/
|
||||
render: Component;
|
||||
/**
|
||||
* 配置组件
|
||||
*/
|
||||
option: Component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义组件
|
||||
* @param blocker 参数
|
||||
* @returns
|
||||
*/
|
||||
export const defineBlocker = (blocker: Blocker) => {
|
||||
return blocker;
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
export interface Container {
|
||||
id: number | string;
|
||||
title: string;
|
||||
description?: string;
|
||||
width: number;
|
||||
height: number;
|
||||
bgImage?: string;
|
||||
bgColor?: string;
|
||||
zoom: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { InjectionKey, Ref } from "vue";
|
||||
import { Block } from "./block";
|
||||
import { Container } from "./container";
|
||||
|
||||
export interface Context {
|
||||
current: {
|
||||
block: Block;
|
||||
};
|
||||
blocks: Ref<Block[]>;
|
||||
container: Ref<Container>;
|
||||
setCurrentBlock: (block: Block) => void;
|
||||
}
|
||||
|
||||
export const ContextKey = Symbol('ContextKey') as InjectionKey<Context>;
|
||||
|
||||
|
|
@ -1 +1,4 @@
|
|||
export const ContextKey = Symbol('ContextKey');
|
||||
export * from './block';
|
||||
export * from './blocker';
|
||||
export * from './container';
|
||||
export * from './context';
|
||||
|
|
@ -20,20 +20,47 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ContextKey } from './config';
|
||||
import { ContextKey } from "./config";
|
||||
import PanelHeader from "./panel-header/index.vue";
|
||||
import PanelLeft from "./panel-left/index.vue";
|
||||
import PanelMain from "./panel-main/index.vue";
|
||||
import PanelRight from "./panel-right/index.vue";
|
||||
|
||||
const context = reactive({
|
||||
current: {
|
||||
const blocks = ref<Block>([]);
|
||||
|
||||
const current = ref({
|
||||
block: null as Block | null,
|
||||
});
|
||||
|
||||
const container = ref<Container>({
|
||||
id: 11,
|
||||
title: "国庆节喜庆版式设计",
|
||||
description: "适用于国庆节1日-7日间上午9:00-10:00播出的版式设计",
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
bgImage: "",
|
||||
bgColor: "#ffffff",
|
||||
zoom: 1,
|
||||
});
|
||||
|
||||
const setCurrentBlock = (block: Block | null) => {
|
||||
for (const block of blocks.value) {
|
||||
block.active = false;
|
||||
}
|
||||
})
|
||||
|
||||
provide(ContextKey, {})
|
||||
if (!block) {
|
||||
current.value.block = null;
|
||||
return;
|
||||
}
|
||||
block.active = true;
|
||||
current.value.block = block;
|
||||
};
|
||||
|
||||
provide(ContextKey, {
|
||||
current,
|
||||
container,
|
||||
blocks,
|
||||
setCurrentBlock,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import Text from "./text";
|
||||
|
||||
export const BlockerMap = {
|
||||
[Text.initial.type]: Text,
|
||||
};
|
||||
|
||||
export const getBlockerRender = (type: string) => {
|
||||
return BlockerMap[type].render;
|
||||
}
|
||||
|
||||
export default BlockerMap;
|
||||
|
|
@ -1,6 +1,27 @@
|
|||
import { defineBlocker } from '../../config'
|
||||
import Option from './option.vue'
|
||||
import Render from './render.vue'
|
||||
export * from './interface'
|
||||
|
||||
export { Option, Render }
|
||||
export default defineBlocker({
|
||||
initial: {
|
||||
id: "",
|
||||
type: "text",
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 50,
|
||||
h: 50,
|
||||
xFixed: false,
|
||||
yFixed: false,
|
||||
bgImage: "",
|
||||
bgColor: "",
|
||||
data: {},
|
||||
meta: {},
|
||||
actived: false,
|
||||
resizable: true,
|
||||
draggable: true,
|
||||
},
|
||||
render: Render,
|
||||
option: Option,
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="flex gap-4">
|
||||
<a-form-item label="左侧">
|
||||
<a-input-number v-model="data.x" :min="0" :max="100">
|
||||
<template #prefix>
|
||||
<a-tooltip content="固定水平方向">
|
||||
<i
|
||||
class="cursor-pointer text-gray-400 hover:text-gray-700"
|
||||
:class="
|
||||
data.xFixed ? 'icon-park-outline-lock text-gray-900' : 'icon-park-outline-unlock text-gray-400'
|
||||
"
|
||||
@click="data.xFixed = !data.xFixed"
|
||||
></i>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="顶部">
|
||||
<a-input-number v-model="data.y" :min="0" :max="100">
|
||||
<template #prefix>
|
||||
<a-tooltip content="固定垂直方向">
|
||||
<i
|
||||
class="cursor-pointer text-gray-400 hover:text-gray-700"
|
||||
:class="
|
||||
data.yFixed ? 'icon-park-outline-lock text-gray-900' : 'icon-park-outline-unlock text-gray-400'
|
||||
"
|
||||
@click="data.yFixed = !data.yFixed"
|
||||
></i>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<a-form-item label="宽度">
|
||||
<a-input-number v-model="data.w" :min="0" :max="100"> </a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="高度">
|
||||
<a-input-number v-model="data.h" :min="0" :max="100"> </a-input-number>
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
<a-form-item label="背景图片">
|
||||
<a-input v-model="data.bgImage" class="group w-full" allow-clear placeholder="暂无">
|
||||
<template #prefix>
|
||||
<a-link class="!text-xs">选择</a-link>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="背景颜色">
|
||||
<a-input v-model="data.bgColor" allow-clear placeholder="无">
|
||||
<template #prefix>
|
||||
<color-picker v-model="data.bgColor"></color-picker>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from "vue";
|
||||
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object as PropType<any>,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -10,24 +10,12 @@
|
|||
</a-menu-item>
|
||||
<a-menu-item key="0_1">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-all-application"></i>
|
||||
<i class="icon-park-outline-rss"></i>
|
||||
</template>
|
||||
Menu 2
|
||||
</a-menu-item>
|
||||
<a-menu-item key="0_2">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-all-application"></i>
|
||||
</template>
|
||||
Menu 3
|
||||
</a-menu-item>
|
||||
<a-menu-item key="0_3">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-all-application"></i>
|
||||
</template>
|
||||
Menu 4
|
||||
当前组件
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<div class="w-full justify-center gap-1 grid text-center pb-4">
|
||||
<div class="w-full justify-center gap-1 grid text-center pb-4">
|
||||
<a-tooltip content="帮助" position="right">
|
||||
<a-button type="text">
|
||||
<template #icon>
|
||||
|
|
@ -47,12 +35,37 @@
|
|||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
<div v-show="!collapsed">
|
||||
<ul class="list-none px-2 grid gap-2" @dragstart="onDragStart" @dragover="onDragOver">
|
||||
<li v-for="i in 10" class="flex items-center justify-between gap-2 border border-slate-200 text-gray-500 px-2 py-1 rounded cursor-move"
|
||||
:draggable="true"
|
||||
:data-type="'text'"
|
||||
>
|
||||
<div class="">
|
||||
<i class="icon-park-outline-pic text-base text-gray-500"></i>
|
||||
</div>
|
||||
<div class="flex-1 leading-0">
|
||||
<div class="">图片</div>
|
||||
<!-- <div class="text-xs text-gray-400 mt-1">image</div> -->
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const collapsed = ref(false);
|
||||
|
||||
const onDragStart = (e: Event) => {
|
||||
console.log('start');
|
||||
e.dataTransfer?.setData("type", (e.target as HTMLElement).dataset.type!);
|
||||
}
|
||||
|
||||
const onDragOver = (e: Event) => {
|
||||
console.log('over');
|
||||
e.preventDefault();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<drag-resizer
|
||||
:x="data.x"
|
||||
:y="data.y"
|
||||
:w="data.w"
|
||||
:h="data.h"
|
||||
:parentW="container.width"
|
||||
:parentH="container.height"
|
||||
:parentScaleX="container.zoom / 100"
|
||||
:parentScaleY="container.zoom / 100"
|
||||
:parentLimitation="true"
|
||||
:preventActiveBehavior="!data.draggable"
|
||||
:isActive="data.active"
|
||||
:isResizable="data.resizable"
|
||||
:style="blockStyle"
|
||||
@dragging="onItemDragOrResize"
|
||||
@resizing="onItemDragOrResize"
|
||||
@activated="setCurrentBlock(data)"
|
||||
>
|
||||
<component :is="BlockerMap[data.type].render" :data="{}" />
|
||||
</drag-resizer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from "vue";
|
||||
import { Block, Container, ContextKey } from "../../config";
|
||||
import { BlockerMap } from "../../items";
|
||||
import DragResizer from "../../components/DragResizer.vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object as PropType<Block>,
|
||||
required: true,
|
||||
},
|
||||
container: {
|
||||
type: Object as PropType<Container>,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const { setCurrentBlock } = inject(ContextKey);
|
||||
|
||||
const blockStyle = computed(() => {
|
||||
const { bgColor, bgImage } = props.data;
|
||||
return {
|
||||
backgroundColor: bgColor,
|
||||
backgroundImage: bgImage ? `url(${bgImage})` : undefined,
|
||||
backgroundSize: "100% 100%",
|
||||
};
|
||||
});
|
||||
|
||||
const onItemDragOrResize = (rect: any) => {
|
||||
props.data.x = rect.left;
|
||||
props.data.y = rect.top;
|
||||
props.data.w = rect.width;
|
||||
props.data.h = rect.height;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
<template>
|
||||
<div class="h-full flex items-center justify-between gap-4 px-4 bg-white">
|
||||
<div class="flex-1">
|
||||
<div class="group">
|
||||
<span class="text-gray-400">描述: </span>
|
||||
<span v-if="!descEditing">
|
||||
{{ container.description }}
|
||||
<i
|
||||
class="!hidden !group-hover:inline-block icon-park-outline-edit text-gray-400 hover:text-gray-700 ml-1 cursor-pointer"
|
||||
@click="onDescEdit"
|
||||
></i>
|
||||
</span>
|
||||
<span v-else class="inline-flex items-center">
|
||||
<a-input size="small" v-model="descContent" class="!w-96"></a-input>
|
||||
<a-button type="text" size="small" @click="onDescEdited" class="ml-2">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-check"></i>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" size="small" class="!text-gray-500" @click="descEditing = false">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-close"></i>
|
||||
</template>
|
||||
</a-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="text-gray-400 text-xs mr-2">
|
||||
尺寸:
|
||||
<span class="text-gray-700"> {{ container.width }} * {{ container.height }} </span>
|
||||
</span>
|
||||
<span class="text-gray-400 text-xs mr-2">
|
||||
比例:
|
||||
<span class="text-gray-700">
|
||||
{{ parseInt(container.zoom * 100) }}%
|
||||
</span>
|
||||
</span>
|
||||
<span class="text-gray-400 text-xs mr-2">
|
||||
组件:
|
||||
<span class="text-gray-700">{{ 2 }} 个</span>
|
||||
</span>
|
||||
<a-tooltip content="预览" position="bottom">
|
||||
<a-button type="text">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-play text-base !text-gray-600"></i>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-popover position="br" trigger="click">
|
||||
<a-button type="text">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-config text-base !text-gray-600"></i>
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<span>
|
||||
背景图片:
|
||||
<a-link>选择</a-link>
|
||||
</span>
|
||||
<span class="inline-flex items-center">
|
||||
背景颜色:
|
||||
<color-picker></color-picker>
|
||||
</span>
|
||||
</template>
|
||||
</a-popover>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
container: {
|
||||
type: Object as PropType<any>,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const descEditing = ref(false);
|
||||
const descContent = ref("");
|
||||
|
||||
const onDescEdited = () => {
|
||||
props.container.description = descContent.value;
|
||||
descEditing.value = false;
|
||||
};
|
||||
|
||||
const onDescEdit = () => {
|
||||
descContent.value = props.container.description;
|
||||
descEditing.value = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,68 +1,21 @@
|
|||
<template>
|
||||
<div class="h-full grid grid-rows-[auto_1fr]">
|
||||
<div class="h-10 flex items-center justify-between gap-4 px-4 bg-white">
|
||||
<div>
|
||||
<span class="group">
|
||||
<span class="text-gray-400">描述: </span>
|
||||
国庆节上午9:00-10:00版式设计
|
||||
<i
|
||||
class="!hidden !group-hover:inline-block icon-park-outline-edit text-gray-400 hover:text-gray-700 ml-1"
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="text-gray-400 text-xs mr-2">
|
||||
尺寸:
|
||||
<span class="text-gray-700">1920 * 1080</span>
|
||||
</span>
|
||||
<span class="text-gray-400 text-xs mr-2">
|
||||
比例:
|
||||
<span class="text-gray-700">100%</span>
|
||||
</span>
|
||||
<span class="text-gray-400 text-xs mr-2">
|
||||
组件:
|
||||
<span class="text-gray-700">3 个</span>
|
||||
</span>
|
||||
<a-tooltip content="预览" position="bottom">
|
||||
<a-button type="text">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-play text-base !text-gray-600"></i>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-popover position="br" trigger="click">
|
||||
<a-button type="text">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-config text-base !text-gray-600"></i>
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<span>
|
||||
背景图片:
|
||||
<a-link>选择</a-link>
|
||||
</span>
|
||||
<span class="inline-flex items-center">
|
||||
背景颜色:
|
||||
<color-picker></color-picker>
|
||||
</span>
|
||||
</template>
|
||||
</a-popover>
|
||||
</div>
|
||||
<div class="h-10">
|
||||
<ani-header :container="container"></ani-header>
|
||||
</div>
|
||||
<div class="h-full p-5 px-6">
|
||||
<div class="bg-white h-full relative">
|
||||
<drag-resizer
|
||||
:style="{ backgroundColor: item.bgColor }"
|
||||
:x="item.x"
|
||||
:y="item.y"
|
||||
:w="item.w"
|
||||
:h="item.h"
|
||||
:parentLimitation="true"
|
||||
@dragging="onItemDragOrResize"
|
||||
@resizing="onItemDragOrResize"
|
||||
<div class="h-full w-full overflow-hidden p-4">
|
||||
<div class="dd1 w-full h-full flex items-center justify-center overflow-hidden relative bg-slate-50">
|
||||
<div
|
||||
class="relative"
|
||||
:style="containerStyle"
|
||||
@drop="onDragDrop"
|
||||
@dragover.prevent
|
||||
@wheel="onMouseWheel"
|
||||
@mousedown="onMouseDown"
|
||||
@mousemove="onMouseMove"
|
||||
>
|
||||
<text-render :data="textCh"> </text-render>
|
||||
</drag-resizer>
|
||||
<ani-block v-for="block in blocks" :key="block.id" :data="block" :container="container"></ani-block>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -70,38 +23,121 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import ColorPicker from "../components/ColorPicker.vue";
|
||||
import DragResizer from "../components/DragResizer.vue";
|
||||
import TextRender from "../items/text/render.vue";
|
||||
import AniBlock from "./components/block.vue";
|
||||
import AniHeader from "./components/header.vue";
|
||||
import { Block, ContextKey, Container } from "../config";
|
||||
|
||||
const item = ref({
|
||||
const isStart = ref(false);
|
||||
const position = ref({
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 200,
|
||||
h: 100,
|
||||
bgColor: "#0099ff",
|
||||
xFixed: false,
|
||||
yFixed: false,
|
||||
resizable: true,
|
||||
draggable: true,
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
mouseX: 0,
|
||||
mouseY: 0,
|
||||
});
|
||||
|
||||
const onItemDragOrResize = (rect: any) => {
|
||||
item.value.x = rect.left;
|
||||
item.value.y = rect.top;
|
||||
item.value.w = rect.width;
|
||||
item.value.h = rect.height;
|
||||
const onMouseDown = (e: Event) => {
|
||||
isStart.value = true;
|
||||
position.value.startX = e.offsetX;
|
||||
position.value.startY = e.offsetY;
|
||||
};
|
||||
|
||||
const textCh = {
|
||||
text: "进站前请准备好车票或乘车二维码。进站前请主动配合车站工作人员,接受安检,准备好自己的车票或乘车二维码。车站检票闸机会扫描并验证车票或乘车二维码,验证通过后方可进站。",
|
||||
family: "",
|
||||
bold: false,
|
||||
italic: false,
|
||||
underline: false,
|
||||
size: 14,
|
||||
color: "#ffffff",
|
||||
align: 3,
|
||||
const onMouseMove = (e: Event) => {
|
||||
if (!isStart.value) {
|
||||
return;
|
||||
}
|
||||
const scale = container.value.zoom;
|
||||
position.value.x += (e.offsetX - position.value.startX) * scale;
|
||||
position.value.y += (e.offsetY - position.value.startY) * scale;
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
isStart.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener("mouseup", onMouseUp);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("mouseup", onMouseUp);
|
||||
});
|
||||
|
||||
const { blocks, container } = inject(ContextKey);
|
||||
|
||||
const containerStyle = computed(() => {
|
||||
const { width, height, bgColor, bgImage, zoom } = container.value;
|
||||
const { x, y } = position.value;
|
||||
return {
|
||||
position: "absolute",
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
backgroundColor: bgColor,
|
||||
backgroundImage: bgImage ? `url(${bgImage})` : undefined,
|
||||
backgroundSize: "100% 100%",
|
||||
// transform: `translate3d(${x}px, ${y}px, 0) scale(${zoom})`,
|
||||
transform: `matrix(${zoom}, 0, 0, ${zoom}, ${x}, ${y})`,
|
||||
// transformOrigin: "0 0",
|
||||
};
|
||||
});
|
||||
|
||||
const onDragDrop = (e: Event) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const type = e.dataTransfer?.getData("type");
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
|
||||
blocks.value.push({
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 200,
|
||||
h: 100,
|
||||
bgColor: "#0099ff",
|
||||
xFixed: false,
|
||||
yFixed: false,
|
||||
resizable: true,
|
||||
draggable: true,
|
||||
type,
|
||||
x: e.offsetX,
|
||||
y: e.offsetY,
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseWheel = (e: WheelEvent) => {
|
||||
e.preventDefault();
|
||||
const prezoom = container.value.zoom;
|
||||
let zoom = prezoom;
|
||||
if (e.wheelDelta > 0) {
|
||||
console.log("放大");
|
||||
zoom += 0.1;
|
||||
if (zoom > 10) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
console.log("缩小");
|
||||
zoom -= 0.1;
|
||||
if (zoom < 0.1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
zoom = parseFloat(zoom.toFixed(1));
|
||||
// const { x, y } = position.value
|
||||
// const x1 = x + e.offsetX;
|
||||
// const y1 = y + e.offsetY;
|
||||
// position.value.x = x1 - (x1 - x) * (zoom / prezoom);
|
||||
// position.value.y = y1 - (y1 - y) * (zoom / prezoom);
|
||||
// position.value.x = e.clientX;
|
||||
// position.value.y = e.clientY;
|
||||
container.value.zoom = zoom;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.dd1 {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@
|
|||
<a-radio value="2">字体</a-radio>
|
||||
</a-radio-group>
|
||||
<a-form :model="block" layout="vertical">
|
||||
<div class="muti-form-item mt-2">
|
||||
<a-divider orientation="left">基本设置</a-divider>
|
||||
<block-attr :block="item"></block-attr>
|
||||
<div v-if="current.block" class="muti-form-item mt-2">
|
||||
<component :is="BlockerMap[current.block.type].option" :data="current.block" />
|
||||
</div>
|
||||
<div class="muti-form-item">
|
||||
<a-divider orientation="left">中文设置</a-divider>
|
||||
|
|
@ -20,6 +19,10 @@
|
|||
<script setup lang="ts">
|
||||
import BlockAttr from "./block-attr.vue";
|
||||
import TextAttr from "./text-attr.vue";
|
||||
import { ContextKey } from "../config";
|
||||
import BlockerMap from "../items";
|
||||
|
||||
const { current } = inject(ContextKey);
|
||||
|
||||
const item = ref<any>({
|
||||
x: 0,
|
||||
|
|
|
|||
|
|
@ -54,8 +54,6 @@
|
|||
<a-select v-model="block.align" :options="TextAlignOptions"></a-select>
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -63,6 +61,10 @@
|
|||
import { PropType } from "vue";
|
||||
import ColorPicker from "../components/ColorPicker.vue";
|
||||
import { TextAlignOptions, TextFamilyOptions } from "../interface";
|
||||
import { ContextKey } from '../config';
|
||||
|
||||
const context = inject(ContextKey);
|
||||
console.log('ctx', context);
|
||||
|
||||
defineProps({
|
||||
block: {
|
||||
|
|
|
|||
|
|
@ -7,42 +7,37 @@ export {}
|
|||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
AAvatar: typeof import('@arco-design/web-vue')['Avatar']
|
||||
AButton: typeof import('@arco-design/web-vue')['Button']
|
||||
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
|
||||
ADivider: typeof import('@arco-design/web-vue')['Divider']
|
||||
ADoption: typeof import('@arco-design/web-vue')['Doption']
|
||||
ADrawer: typeof import('@arco-design/web-vue')['Drawer']
|
||||
ADropdown: typeof import('@arco-design/web-vue')['Dropdown']
|
||||
ADropdownButton: typeof import('@arco-design/web-vue')['DropdownButton']
|
||||
AForm: typeof import('@arco-design/web-vue')['Form']
|
||||
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
|
||||
AInput: typeof import('@arco-design/web-vue')['Input']
|
||||
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
|
||||
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
|
||||
ALayout: typeof import('@arco-design/web-vue')['Layout']
|
||||
ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent']
|
||||
ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
|
||||
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
|
||||
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
||||
ALink: typeof import('@arco-design/web-vue')['Link']
|
||||
AMenu: typeof import('@arco-design/web-vue')['Menu']
|
||||
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
||||
AMenuItemGroup: typeof import('@arco-design/web-vue')['MenuItemGroup']
|
||||
AModal: typeof import('@arco-design/web-vue')['Modal']
|
||||
APopover: typeof import('@arco-design/web-vue')['Popover']
|
||||
ARadio: typeof import('@arco-design/web-vue')['Radio']
|
||||
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup']
|
||||
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
|
||||
ASelect: typeof import('@arco-design/web-vue')['Select']
|
||||
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
||||
ASpace: typeof import('@arco-design/web-vue')['Space']
|
||||
ATag: typeof import('@arco-design/web-vue')['Tag']
|
||||
ATextarea: typeof import('@arco-design/web-vue')['Textarea']
|
||||
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
|
||||
Block: typeof import('./../components/editor/panel-main/components/block.vue')['default']
|
||||
BlockAttr: typeof import('./../components/editor/panel-right/block-attr.vue')['default']
|
||||
BreadCrumb: typeof import('./../components/breadcrumb/bread-crumb.vue')['default']
|
||||
BreadPage: typeof import('./../components/breadcrumb/bread-page.vue')['default']
|
||||
ColorPicker: typeof import('./../components/editor/components/ColorPicker.vue')['default']
|
||||
DragResizer: typeof import('./../components/editor/components/DragResizer.vue')['default']
|
||||
Editor: typeof import('./../components/editor/index.vue')['default']
|
||||
Header: typeof import('./../components/editor/panel-main/components/header.vue')['default']
|
||||
ImagePicker: typeof import('./../components/editor/components/ImagePicker.vue')['default']
|
||||
Option: typeof import('./../components/editor/items/text/option.vue')['default']
|
||||
Page403: typeof import('./../components/error/page-403.vue')['default']
|
||||
PanelHeader: typeof import('./../components/editor/panel-header/index.vue')['default']
|
||||
|
|
|
|||
Loading…
Reference in New Issue