feat: 优化文本组件
parent
177a060323
commit
14d68f09fb
|
|
@ -1,16 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<a-config-provider>
|
<!-- <a-config-provider>
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<page-403 v-if="Math.random() > 0.999"></page-403>
|
<page-403 v-if="Math.random() > 0.999"></page-403>
|
||||||
<component v-else :is="Component"></component>
|
<component v-else :is="Component"></component>
|
||||||
</router-view>
|
</router-view>
|
||||||
</a-config-provider>
|
</a-config-provider> -->
|
||||||
<!-- <div>
|
<div>
|
||||||
<my-editor></my-editor>
|
<my-editor></my-editor>
|
||||||
</div> -->
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import MyEditor from './components/editor/index.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import Option from "./option.vue";
|
||||||
|
import Render from "./render.vue";
|
||||||
|
import { Font } from "./interface";
|
||||||
|
export * from "./interface";
|
||||||
|
|
||||||
|
export const FontRender = Render;
|
||||||
|
|
||||||
|
export const FontOption = Option;
|
||||||
|
|
||||||
|
export const font: Font = {
|
||||||
|
content: "请输入文字",
|
||||||
|
family: "microsoft yahei",
|
||||||
|
size: 14,
|
||||||
|
color: "#000000",
|
||||||
|
bold: false,
|
||||||
|
italic: false,
|
||||||
|
underline: false,
|
||||||
|
align: 3,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
import { CSSProperties, StyleValue } from "vue";
|
||||||
|
|
||||||
|
export interface Font {
|
||||||
|
/**
|
||||||
|
* 文字内容
|
||||||
|
*/
|
||||||
|
content: string;
|
||||||
|
/**
|
||||||
|
* 字体
|
||||||
|
*/
|
||||||
|
family: string;
|
||||||
|
/**
|
||||||
|
* 字号(px)
|
||||||
|
*/
|
||||||
|
size: number;
|
||||||
|
/**
|
||||||
|
* 颜色(16进制)
|
||||||
|
*/
|
||||||
|
color: string;
|
||||||
|
/**
|
||||||
|
* 是否加粗
|
||||||
|
*/
|
||||||
|
bold: boolean;
|
||||||
|
/**
|
||||||
|
* 是否斜体
|
||||||
|
*/
|
||||||
|
italic: boolean;
|
||||||
|
/**
|
||||||
|
* 是否下划线
|
||||||
|
*/
|
||||||
|
underline: boolean;
|
||||||
|
/**
|
||||||
|
* 对齐方式
|
||||||
|
*/
|
||||||
|
align: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AlignOptions = [
|
||||||
|
{
|
||||||
|
label: "居上",
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "居下",
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "居中",
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "居左",
|
||||||
|
value: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "居右",
|
||||||
|
value: 5,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const FontFamilyOptions = [
|
||||||
|
{
|
||||||
|
label: "微软雅黑",
|
||||||
|
value: "microsoft yahei",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "黑体",
|
||||||
|
value: "gothic",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "宋体",
|
||||||
|
value: "simsun",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Arial",
|
||||||
|
value: "arial",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getFontStyle = (font: Font) => {
|
||||||
|
const { size, family: fontFamily, color, bold, italic, underline, align } = font;
|
||||||
|
const fontSize = `${size}px`;
|
||||||
|
const fontWeight = bold ? "bold" : "normal";
|
||||||
|
const fontStyle = italic ? "italic" : "normal";
|
||||||
|
const textDecoration = underline ? "underline" : "none";
|
||||||
|
let textAlign = "left";
|
||||||
|
let verticalAlign = "middle";
|
||||||
|
|
||||||
|
switch (align) {
|
||||||
|
case 1:
|
||||||
|
textAlign = "center";
|
||||||
|
verticalAlign = "top";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
textAlign = "center";
|
||||||
|
verticalAlign = "bottom";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
textAlign = "center";
|
||||||
|
verticalAlign = "middle";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
textAlign = "left";
|
||||||
|
verticalAlign = "middle";
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
textAlign = "right";
|
||||||
|
verticalAlign = "middle";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fontFamily,
|
||||||
|
fontSize,
|
||||||
|
fontWeight,
|
||||||
|
fontStyle,
|
||||||
|
textDecoration,
|
||||||
|
color,
|
||||||
|
textAlign,
|
||||||
|
verticalAlign,
|
||||||
|
} as CSSProperties;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-form-item label="内容">
|
||||||
|
<a-textarea v-model="data.content" placeholder="输入内容..."></a-textarea>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="颜色">
|
||||||
|
<input-color v-model="data.color"></input-color>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<div class="flex gap-4">
|
||||||
|
<a-form-item label="字体">
|
||||||
|
<a-select v-model="data.family" :options="FontFamilyOptions" class="w-full overflow-hidden"> </a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="大小">
|
||||||
|
<a-input-number v-model="data.size" :min="12"> </a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<a-form-item label="样式">
|
||||||
|
<div class="h-8 flex items-center justify-between">
|
||||||
|
<a-tag
|
||||||
|
class="cursor-pointer !h-7"
|
||||||
|
:color="data.bold ? 'blue' : ''"
|
||||||
|
:bordered="data.bold ? true : false"
|
||||||
|
@click="data.bold = !data.bold"
|
||||||
|
>
|
||||||
|
<i class="icon-park-outline-text-bold"></i>
|
||||||
|
</a-tag>
|
||||||
|
<a-tag
|
||||||
|
class="!h-7 cursor-pointer"
|
||||||
|
:color="data.italic ? 'blue' : ''"
|
||||||
|
:bordered="data.italic ? true : false"
|
||||||
|
@click="data.italic = !data.italic"
|
||||||
|
>
|
||||||
|
<i class="icon-park-outline-text-italic"></i>
|
||||||
|
</a-tag>
|
||||||
|
<a-tag
|
||||||
|
class="!h-7 cursor-pointer"
|
||||||
|
:color="data.underline ? 'blue' : ''"
|
||||||
|
:bordered="data.underline ? true : false"
|
||||||
|
@click="data.underline = !data.underline"
|
||||||
|
>
|
||||||
|
<i class="icon-park-outline-text-underline"></i>
|
||||||
|
</a-tag>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="方向">
|
||||||
|
<a-select v-model="data.align" :options="AlignOptions"></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PropType } from "vue";
|
||||||
|
import InputColor from "../../components/InputColor.vue";
|
||||||
|
import { Font, AlignOptions, FontFamilyOptions } from "./interface";
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object as PropType<Font>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
./types
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full h-full table overflow-hidden" :style="style">
|
||||||
|
<div class="table-cell break-all" :style="{ verticalAlign: style.verticalAlign }">
|
||||||
|
<slot>
|
||||||
|
{{ data.content }}
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PropType, computed } from "vue";
|
||||||
|
import { Font, getFontStyle } from "./interface";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object as PropType<Font>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const style = computed(() => {
|
||||||
|
return getFontStyle(props.data);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
./types
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import Text from "./text";
|
import Text from "./text";
|
||||||
|
|
||||||
export const BlockerMap = {
|
export const BlockerMap = {
|
||||||
[Text.initial.type]: Text,
|
[Text.type]: Text,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBlockerRender = (type: string) => {
|
export const getBlockerRender = (type: string) => {
|
||||||
return BlockerMap[type].render;
|
return BlockerMap[type].render;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default BlockerMap;
|
export default BlockerMap;
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,44 @@
|
||||||
import { defineBlocker } from '../../config'
|
import { defineBlocker } from "../../config";
|
||||||
import Option from './option.vue'
|
import Render from "./render.vue";
|
||||||
import Render from './render.vue'
|
import Option from "./option.vue";
|
||||||
export * from './interface'
|
import { TextData } from "./interface";
|
||||||
|
|
||||||
export default defineBlocker({
|
export default defineBlocker<TextData>({
|
||||||
|
type: "text",
|
||||||
|
icon: "icon-park-outline-text",
|
||||||
|
title: "文本组件",
|
||||||
|
description: "文字",
|
||||||
|
render: Render,
|
||||||
|
option: Option,
|
||||||
initial: {
|
initial: {
|
||||||
id: "",
|
id: "",
|
||||||
type: "text",
|
type: "text",
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
w: 50,
|
w: 300,
|
||||||
h: 50,
|
h: 100,
|
||||||
xFixed: false,
|
xFixed: false,
|
||||||
yFixed: false,
|
yFixed: false,
|
||||||
bgImage: "",
|
bgImage: "",
|
||||||
bgColor: "",
|
bgColor: "",
|
||||||
data: {},
|
|
||||||
meta: {},
|
meta: {},
|
||||||
actived: false,
|
actived: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
|
data: {
|
||||||
|
marquee: false,
|
||||||
|
marqueeSpeed: 1,
|
||||||
|
marqueeDirection: "left",
|
||||||
|
fontCh: {
|
||||||
|
content: "请输入文字",
|
||||||
|
family: "微软雅黑",
|
||||||
|
size: 14,
|
||||||
|
color: "#000000",
|
||||||
|
bold: false,
|
||||||
|
italic: false,
|
||||||
|
underline: false,
|
||||||
|
align: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
render: Render,
|
});
|
||||||
option: Option,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,43 @@
|
||||||
export interface Text {
|
import { Font } from "../font";
|
||||||
|
|
||||||
|
export interface TextData {
|
||||||
/**
|
/**
|
||||||
* 文字内容
|
* 是否滚动
|
||||||
*/
|
*/
|
||||||
text: string;
|
marquee?: boolean;
|
||||||
/**
|
/**
|
||||||
* 字体
|
* 滚动速度
|
||||||
*/
|
*/
|
||||||
family: string;
|
marqueeSpeed?: number;
|
||||||
/**
|
/**
|
||||||
* 字号(px)
|
* 滚动方向
|
||||||
*/
|
*/
|
||||||
size: number;
|
marqueeDirection?: "left" | "right" | "up" | "down";
|
||||||
/**
|
/**
|
||||||
* 颜色(16进制)
|
* 内容(中文)
|
||||||
*/
|
*/
|
||||||
color: string;
|
fontCh: Font;
|
||||||
/**
|
|
||||||
* 是否加粗
|
|
||||||
*/
|
|
||||||
bold: boolean;
|
|
||||||
/**
|
|
||||||
* 是否斜体
|
|
||||||
*/
|
|
||||||
italic: boolean;
|
|
||||||
/**
|
|
||||||
* 是否下划线
|
|
||||||
*/
|
|
||||||
underline: boolean;
|
|
||||||
/**
|
|
||||||
* 对齐方式
|
|
||||||
*/
|
|
||||||
align: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DefaultText: Text = {
|
export const DirectionOptions = [
|
||||||
text: "双击编辑文字",
|
|
||||||
family: "microsoft yahei",
|
|
||||||
size: 14,
|
|
||||||
color: "#000000",
|
|
||||||
bold: false,
|
|
||||||
italic: false,
|
|
||||||
underline: false,
|
|
||||||
align: 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TextAlignOptions = [
|
|
||||||
{
|
{
|
||||||
label: "居上",
|
icon: "icon-park-outline-arrow-left",
|
||||||
value: 1,
|
tip: "向左滚动",
|
||||||
|
value: "left",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "居下",
|
icon: "icon-park-outline-arrow-up",
|
||||||
value: 2,
|
tip: "向上滚动",
|
||||||
|
value: "up",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "居中",
|
icon: "icon-park-outline-arrow-down",
|
||||||
value: 3,
|
tip: "向下滚动",
|
||||||
|
value: "down",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "居左",
|
icon: "icon-park-outline-arrow-right",
|
||||||
value: 4,
|
tip: "向右滚动",
|
||||||
},
|
value: "right",
|
||||||
{
|
|
||||||
label: "居右",
|
|
||||||
value: 5,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const TextFamilyOptions = [
|
|
||||||
{
|
|
||||||
label: "微软雅黑111111111",
|
|
||||||
value: "microsoft yahei",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "黑体",
|
|
||||||
value: "gothic",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "宋体",
|
|
||||||
value: "simsun",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Arial",
|
|
||||||
value: "arial",
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,224 @@
|
||||||
|
<template>
|
||||||
|
<div ref="box" class="marquee" :style="marqueeStyle">
|
||||||
|
<p ref="text" class="marquee-text" :style="marqueeTextStyle" v-bind="$attrs" @animationend="reset">
|
||||||
|
<slot></slot>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "marquee",
|
||||||
|
props: {
|
||||||
|
speed: {
|
||||||
|
type: Number,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
type: String as PropType<"up" | "right" | "down" | "left">,
|
||||||
|
default: "left",
|
||||||
|
},
|
||||||
|
delayTime: {
|
||||||
|
type: Number,
|
||||||
|
default: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
textAnimation: "",
|
||||||
|
timer: null as any,
|
||||||
|
boxClientHeight: 0,
|
||||||
|
boxClientWidth: 0,
|
||||||
|
textClientHeight: 0,
|
||||||
|
textClientWidth: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* 容器样式
|
||||||
|
*/
|
||||||
|
marqueeStyle() {
|
||||||
|
const display = this.isVertical(this.direction) ? "block" : "flex";
|
||||||
|
const alignItems = this.isVertical(this.direction) ? "initial" : "center";
|
||||||
|
return { display, alignItems };
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本样式
|
||||||
|
*/
|
||||||
|
marqueeTextStyle() {
|
||||||
|
const transform = this.textTransform;
|
||||||
|
const animation = this.textAnimation;
|
||||||
|
const whiteSpace = this.isHorizontal(this.direction) ? "nowrap" : "normal";
|
||||||
|
const direction = this.direction === "right" ? "rtl" : "ltr";
|
||||||
|
const unicodeBidi = this.direction === "right" ? "bidi-override" : undefined;
|
||||||
|
return {
|
||||||
|
transform,
|
||||||
|
animation,
|
||||||
|
whiteSpace,
|
||||||
|
direction,
|
||||||
|
unicodeBidi,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据容器宽高和文本宽高,动态计算文本的滚动时间
|
||||||
|
*/
|
||||||
|
textDuration() {
|
||||||
|
if (["up", "down"].includes(this.direction)) {
|
||||||
|
return (this.boxClientHeight + this.textClientHeight) / this.speed;
|
||||||
|
}
|
||||||
|
return (this.boxClientWidth + this.textClientWidth) / this.speed;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据容器宽高和文本宽高,动态计算文本的起始位置
|
||||||
|
*/
|
||||||
|
textTransform() {
|
||||||
|
let transform = "";
|
||||||
|
switch (this.direction) {
|
||||||
|
case "up": {
|
||||||
|
transform = `translateY(${this.boxClientHeight}px)`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "right": {
|
||||||
|
transform = `translateX(-100%)`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "down": {
|
||||||
|
transform = `translateY(-100%)`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "left": {
|
||||||
|
transform = `translateX(${this.boxClientWidth}px)`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return transform;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
speed: "marquee",
|
||||||
|
direction: "marquee",
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.marquee();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 主函数,编辑版式时,使用NextTick也获取不到DOM数据,暂用SetTimeout实现
|
||||||
|
*/
|
||||||
|
marquee() {
|
||||||
|
if (this.timer) {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.initClient();
|
||||||
|
this.initStyle();
|
||||||
|
this.startMarquee();
|
||||||
|
}, this.delayTime);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取容器和文本的宽高
|
||||||
|
*/
|
||||||
|
initClient() {
|
||||||
|
const box = this.$refs.box as HTMLElement;
|
||||||
|
this.boxClientHeight = box.clientHeight;
|
||||||
|
this.boxClientWidth = box.clientWidth;
|
||||||
|
const text = this.$refs.text as HTMLElement;
|
||||||
|
this.textClientHeight = text.clientHeight;
|
||||||
|
this.textClientWidth = text.clientWidth;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态设置CSS样式
|
||||||
|
*/
|
||||||
|
initStyle() {
|
||||||
|
const head = document.querySelector("head")!;
|
||||||
|
let style = document.querySelector("#marqueeanimation")!;
|
||||||
|
if (style) {
|
||||||
|
style.parentNode?.removeChild(style);
|
||||||
|
}
|
||||||
|
style = document.createElement("style");
|
||||||
|
style.id = "marqueeanimation";
|
||||||
|
|
||||||
|
let transform = "";
|
||||||
|
switch (this.direction) {
|
||||||
|
case "down":
|
||||||
|
transform = `translateY(${this.boxClientHeight}px)`;
|
||||||
|
break;
|
||||||
|
case "up":
|
||||||
|
transform = `translateY(-100%)`;
|
||||||
|
break;
|
||||||
|
case "right":
|
||||||
|
transform = `translateX(${this.boxClientWidth}px)`;
|
||||||
|
break;
|
||||||
|
case "left":
|
||||||
|
transform = `translateX(-100%)`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
style.innerHTML = `
|
||||||
|
@keyframes marquee-to-${this.direction} {
|
||||||
|
100% {
|
||||||
|
transform: ${transform}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
head.appendChild(style);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始滚动文本(设置css的animation属性会自动滚动)
|
||||||
|
*/
|
||||||
|
startMarquee() {
|
||||||
|
this.textAnimation = `marquee-to-${this.direction} ${this.textDuration}s linear`;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置并开始下一轮
|
||||||
|
*/
|
||||||
|
reset() {
|
||||||
|
this.$emit("animationend");
|
||||||
|
this.textAnimation = "";
|
||||||
|
this.timer = setTimeout(this.startMarquee, this.delayTime);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否水平方向滚动
|
||||||
|
*/
|
||||||
|
isVertical(direction: string) {
|
||||||
|
return ["up", "down"].includes(direction);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否垂直方向滚动
|
||||||
|
*/
|
||||||
|
isHorizontal(direction: string) {
|
||||||
|
return ["left", "right"].includes(direction);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeDestory() {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.marquee {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.marquee-text {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,65 +1,57 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="flex gap-4">
|
<base-option :data="data"></base-option>
|
||||||
<a-form-item label="左侧">
|
</div>
|
||||||
<a-input-number v-model="data.x" :min="0" :max="100">
|
<div>
|
||||||
<template #prefix>
|
<a-divider></a-divider>
|
||||||
<a-tooltip content="固定水平方向">
|
<div class="muti-form-item grid grid-cols-2 gap-4">
|
||||||
<i
|
<a-form-item label="是否滚动">
|
||||||
class="cursor-pointer text-gray-400 hover:text-gray-700"
|
<a-radio-group type="button" v-model="data.data.marquee" class="!w-full">
|
||||||
:class="data.xFixed ? 'icon-park-outline-lock text-gray-900' : 'icon-park-outline-unlock text-gray-400'"
|
<a-radio :value="false">否</a-radio>
|
||||||
@click="data.xFixed = !data.xFixed"
|
<a-radio :value="true">是</a-radio>
|
||||||
></i>
|
</a-radio-group>
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input-number>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="顶部">
|
<a-form-item v-show="data.data.marquee" label="滚动速度">
|
||||||
<a-input-number v-model="data.y" :min="0" :max="100">
|
<a-input-number v-model="data.data.marqueeSpeed" :min="10" :step="10"></a-input-number>
|
||||||
<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>
|
</a-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
<a-form-item v-show="data.data.marquee" label="滚动方向">
|
||||||
<div class="flex gap-4">
|
<a-radio-group type="button" v-model="data.data.marqueeDirection" class="!w-full">
|
||||||
<a-form-item label="宽度">
|
<a-radio v-for="item in DirectionOptions" :key="item.value" :value="item.value" class="dir-radio">
|
||||||
<a-input-number v-model="data.w" :min="0" :max="100"> </a-input-number>
|
<i :class="item.icon"></i>
|
||||||
</a-form-item>
|
</a-radio>
|
||||||
<a-form-item label="高度">
|
</a-radio-group>
|
||||||
<a-input-number v-model="data.h" :min="0" :max="100"> </a-input-number>
|
</a-form-item>
|
||||||
</a-form-item>
|
</div>
|
||||||
|
<div>
|
||||||
|
<a-divider></a-divider>
|
||||||
|
<div class="mb-4 leading-0">
|
||||||
|
<i class="icon-park-outline-text-style"></i>
|
||||||
|
内容(中文)
|
||||||
</div>
|
</div>
|
||||||
|
<font-option :data="data.data.fontCh"></font-option>
|
||||||
<a-form-item label="背景图片">
|
|
||||||
<input-image v-model="data.bgImage"></input-image>
|
|
||||||
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PropType } from "vue";
|
import { PropType } from "vue";
|
||||||
import InputImage from "../../components/InputImage.vue";
|
import { FontOption } from "../font";
|
||||||
|
import { Block } from "../../config";
|
||||||
|
import { TextData, DirectionOptions } from "./interface";
|
||||||
|
import BaseOption from "../../components/BaseOption.vue";
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object as PropType<any>,
|
type: Object as PropType<Block<TextData>>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dir-radio {
|
||||||
|
.arco-radio-button-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full h-full table overflow-hidden" :style="style">
|
<ani-marquee
|
||||||
<div class="table-cell break-all" :style="{ verticalAlign: style.verticalAlign }">
|
v-if="data.data.marquee"
|
||||||
<slot>
|
:style="style"
|
||||||
{{ data.text }}
|
:speed="data.data.marqueeSpeed"
|
||||||
</slot>
|
:direction="data.data.marqueeDirection"
|
||||||
</div>
|
>
|
||||||
</div>
|
{{ data.data.fontCh.content }}
|
||||||
|
</ani-marquee>
|
||||||
|
<font-render v-else :data="data.data.fontCh"></font-render>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PropType } from "vue";
|
import { PropType } from "vue";
|
||||||
import { Text } from "./interface";
|
import { FontRender, getFontStyle } from "../font";
|
||||||
|
import { Block } from "../../config";
|
||||||
|
import { TextData } from "./interface";
|
||||||
|
import AniMarquee from "./marquee.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object as PropType<Text>,
|
type: Object as PropType<Block<TextData>>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const style = computed(() => {
|
const style = computed(() => {
|
||||||
const { size, family, color, bold, italic, underline, align } = props.data;
|
return getFontStyle(props.data.data.fontCh);
|
||||||
|
|
||||||
let textAlign = "left";
|
|
||||||
let verticalAlign = "middle";
|
|
||||||
switch (align) {
|
|
||||||
case 1:
|
|
||||||
textAlign = "center";
|
|
||||||
verticalAlign = "top";
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
textAlign = "center";
|
|
||||||
verticalAlign = "bottom";
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
textAlign = "center";
|
|
||||||
verticalAlign = "middle";
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
textAlign = "left";
|
|
||||||
verticalAlign = "middle";
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
textAlign = "right";
|
|
||||||
verticalAlign = "middle";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
fontFamily: family,
|
|
||||||
fontSize: size + "px",
|
|
||||||
fontWeight: bold ? "bold" : "normal",
|
|
||||||
fontStyle: italic ? "italic" : "normal",
|
|
||||||
textDecoration: underline ? "underline" : "none",
|
|
||||||
color,
|
|
||||||
textAlign,
|
|
||||||
verticalAlign,
|
|
||||||
} as any;
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
<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="背景图片">
|
||||||
|
<input-image v-model="data.bgImage"></input-image>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="背景颜色">
|
||||||
|
<input-color v-model="data.bgColor"></input-color>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PropType } from "vue";
|
||||||
|
import { Block } from "../config";
|
||||||
|
import InputColor from "./InputColor.vue";
|
||||||
|
import InputImage from "./InputImage.vue";
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object as PropType<Block>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="innerVisible"
|
v-model:visible="innerVisible"
|
||||||
title="选择图片"
|
title="选择素材"
|
||||||
title-align="start"
|
title-align="start"
|
||||||
:width="1080"
|
:width="1080"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
:mask-closable="false"
|
:mask-closable="false"
|
||||||
|
:draggable="true"
|
||||||
:ok-button-props="{ disabled: !seleted.length }"
|
:ok-button-props="{ disabled: !seleted.length }"
|
||||||
>
|
>
|
||||||
<div class="w-full flex items-center justify-between gap-4">
|
<div class="w-full flex items-center justify-between gap-4">
|
||||||
|
|
@ -30,10 +31,8 @@
|
||||||
<div class="h-[450px] grid grid-cols-5 grid-rows-2 items-start justify-between gap-4 mt-2">
|
<div class="h-[450px] grid grid-cols-5 grid-rows-2 items-start justify-between gap-4 mt-2">
|
||||||
<div
|
<div
|
||||||
v-for="item in images"
|
v-for="item in images"
|
||||||
:key="item.title"
|
:key="item.id"
|
||||||
:class="{
|
:class="{ selected: selectedKeys.includes(item.id) }"
|
||||||
selected: selectedKeys.includes(item.id),
|
|
||||||
}"
|
|
||||||
class="p-2 border border-transparent rounded"
|
class="p-2 border border-transparent rounded"
|
||||||
@click="onSelectedImage(item)"
|
@click="onSelectedImage(item)"
|
||||||
>
|
>
|
||||||
|
|
@ -63,8 +62,8 @@
|
||||||
<div class="flex items-center justify-between gap-4">
|
<div class="flex items-center justify-between gap-4">
|
||||||
<div>已选: {{ seleted.length }} 项</div>
|
<div>已选: {{ seleted.length }} 项</div>
|
||||||
<div>
|
<div>
|
||||||
<a-button class="mr-2" @click="onClose">取消</a-button>
|
<a-button class="mr-2" @click="onClose"> 取消 </a-button>
|
||||||
<a-button type="primary" @click="onBeforeOk" :disabled="!seleted.length">确定</a-button>
|
<a-button type="primary" @click="onBeforeOk" :disabled="!seleted.length"> 确定 </a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -93,6 +92,13 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:modelValue", "update:visible"]);
|
||||||
|
|
||||||
|
const innerVisible = computed({
|
||||||
|
get: () => props.visible,
|
||||||
|
set: (value) => emit("update:visible", value),
|
||||||
|
});
|
||||||
|
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
const { page, size } = pagination.value;
|
const { page, size } = pagination.value;
|
||||||
const params = { ...search.value, page, size };
|
const params = { ...search.value, page, size };
|
||||||
|
|
@ -141,19 +147,12 @@ const onSelectedImage = (image: any) => {
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
async (value) => {
|
(value) => {
|
||||||
if (!value) return;
|
if (value) {
|
||||||
|
loadData();
|
||||||
loadData();
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue", "update:visible"]);
|
|
||||||
|
|
||||||
const innerVisible = computed({
|
|
||||||
get: () => props.visible,
|
|
||||||
set: (value) => emit("update:visible", value),
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,45 @@
|
||||||
import { Component } from "vue";
|
import { Component } from "vue";
|
||||||
import { Block } from "./block";
|
import { Block } from "./block";
|
||||||
|
|
||||||
interface Blocker {
|
interface Blocker<T = any> {
|
||||||
|
/**
|
||||||
|
* 组件名称
|
||||||
|
*/
|
||||||
|
title: string;
|
||||||
|
/**
|
||||||
|
* 组件描述
|
||||||
|
*/
|
||||||
|
description: string;
|
||||||
|
/**
|
||||||
|
* 组件类型
|
||||||
|
*/
|
||||||
|
type: string;
|
||||||
|
/**
|
||||||
|
* 组件图标
|
||||||
|
*/
|
||||||
|
icon: string;
|
||||||
/**
|
/**
|
||||||
* 组件默认值
|
* 组件默认值
|
||||||
*/
|
*/
|
||||||
initial: Block;
|
initial: Block<T>;
|
||||||
/**
|
/**
|
||||||
* 渲染组件
|
* 编辑时的渲染组件
|
||||||
*/
|
*/
|
||||||
render: Component;
|
render: Component;
|
||||||
/**
|
/**
|
||||||
* 配置组件
|
* 配置时的渲染组件
|
||||||
*/
|
*/
|
||||||
option: Component;
|
option: Component;
|
||||||
|
/**
|
||||||
|
* 预览时的渲染组件
|
||||||
|
*/
|
||||||
|
viewer?: Component;
|
||||||
|
/**
|
||||||
|
* 初始化钩子
|
||||||
|
* @param block 组件
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
onInit?: (block: Block) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -21,6 +47,6 @@ interface Blocker {
|
||||||
* @param blocker 参数
|
* @param blocker 参数
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const defineBlocker = (blocker: Blocker) => {
|
export const defineBlocker = <T>(blocker: Blocker<T>) => {
|
||||||
return blocker;
|
return blocker;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@ export interface Context {
|
||||||
}>;
|
}>;
|
||||||
blocks: Ref<Block[]>;
|
blocks: Ref<Block[]>;
|
||||||
container: Ref<Container>;
|
container: Ref<Container>;
|
||||||
setCurrentBlock: (block: Block) => void;
|
setCurrentBlock: (block: Block | null) => void;
|
||||||
setContainerOrigin: () => void;
|
setContainerOrigin: () => void;
|
||||||
|
saveData: () => void;
|
||||||
|
loadData: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ContextKey = Symbol('ContextKey') as InjectionKey<Context>;
|
export const ContextKey = Symbol('ContextKey') as InjectionKey<Context>;
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,21 @@ import PanelLeft from "./panel-left/index.vue";
|
||||||
import PanelMain from "./panel-main/index.vue";
|
import PanelMain from "./panel-main/index.vue";
|
||||||
import PanelRight from "./panel-right/index.vue";
|
import PanelRight from "./panel-right/index.vue";
|
||||||
|
|
||||||
const blocks = ref<Block[]>([]);
|
/**
|
||||||
|
* 运行时上下文
|
||||||
|
*/
|
||||||
const current = ref({
|
const current = ref({
|
||||||
block: null as Block | null,
|
block: null as Block | null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件列表
|
||||||
|
*/
|
||||||
|
const blocks = ref<Block[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 画布容器
|
||||||
|
*/
|
||||||
const container = ref<Container>({
|
const container = ref<Container>({
|
||||||
id: 11,
|
id: 11,
|
||||||
title: "国庆节喜庆版式设计",
|
title: "国庆节喜庆版式设计",
|
||||||
|
|
@ -45,6 +54,38 @@ const container = ref<Container>({
|
||||||
bgColor: "#ffffff",
|
bgColor: "#ffffff",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存数据
|
||||||
|
*/
|
||||||
|
const saveData = () => {
|
||||||
|
const data = {
|
||||||
|
container: container.value,
|
||||||
|
children: blocks.value,
|
||||||
|
};
|
||||||
|
const str = JSON.stringify(data);
|
||||||
|
localStorage.setItem("ANI_EDITOR_DATA", str);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载数据
|
||||||
|
*/
|
||||||
|
const loadData = async () => {
|
||||||
|
const str = localStorage.getItem("ANI_EDITOR_DATA");
|
||||||
|
if (!str) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = JSON.parse(str);
|
||||||
|
container.value = data.container;
|
||||||
|
blocks.value = data.children;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前选中的组件
|
||||||
|
*/
|
||||||
const setCurrentBlock = (block: Block | null) => {
|
const setCurrentBlock = (block: Block | null) => {
|
||||||
for (const block of blocks.value) {
|
for (const block of blocks.value) {
|
||||||
block.actived = false;
|
block.actived = false;
|
||||||
|
|
@ -57,19 +98,26 @@ const setCurrentBlock = (block: Block | null) => {
|
||||||
current.value.block = block;
|
current.value.block = block;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 恢复画布到原始比例和远点
|
/**
|
||||||
|
* 恢复画布到原始比例和远点
|
||||||
|
*/
|
||||||
const setContainerOrigin = () => {
|
const setContainerOrigin = () => {
|
||||||
container.value.x = 0;
|
container.value.x = 0;
|
||||||
container.value.y = 0;
|
container.value.y = 0;
|
||||||
container.value.zoom = 0.7;
|
container.value.zoom = 0.7;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供上下文注入
|
||||||
|
*/
|
||||||
provide(ContextKey, {
|
provide(ContextKey, {
|
||||||
current,
|
current,
|
||||||
container,
|
container,
|
||||||
blocks,
|
blocks,
|
||||||
setCurrentBlock,
|
setCurrentBlock,
|
||||||
setContainerOrigin,
|
setContainerOrigin,
|
||||||
|
loadData,
|
||||||
|
saveData,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
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>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DefaultBlock: Block = {
|
|
||||||
id: "",
|
|
||||||
type: "",
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
w: 50,
|
|
||||||
h: 50,
|
|
||||||
xFixed: false,
|
|
||||||
yFixed: false,
|
|
||||||
bgImage: "",
|
|
||||||
bgColor: "",
|
|
||||||
data: {},
|
|
||||||
meta: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Container {
|
|
||||||
id: string;
|
|
||||||
type: string;
|
|
||||||
title: string;
|
|
||||||
description?: string;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
bgImage?: string;
|
|
||||||
bgColor?: string;
|
|
||||||
children: Block[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Current {
|
|
||||||
container: Container;
|
|
||||||
block: Block;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TextAlign = "left" | "center" | "right";
|
|
||||||
|
|
||||||
export interface TextStyle {
|
|
||||||
text: string;
|
|
||||||
family: string;
|
|
||||||
size: number;
|
|
||||||
color: string;
|
|
||||||
bold: boolean;
|
|
||||||
italic: boolean;
|
|
||||||
underline: boolean;
|
|
||||||
align: TextAlign;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TextAlignOptions = [
|
|
||||||
{
|
|
||||||
label: "居上",
|
|
||||||
value: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "居下",
|
|
||||||
value: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "居中",
|
|
||||||
value: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "居左",
|
|
||||||
value: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "居右",
|
|
||||||
value: 5,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const TextFamilyOptions = [
|
|
||||||
{
|
|
||||||
label: "微软雅黑111111111",
|
|
||||||
value: "microsoft yahei",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "黑体",
|
|
||||||
value: "gothic",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "宋体",
|
|
||||||
value: "simsun",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Arial",
|
|
||||||
value: "arial",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full flex items-center justify-between px-4">
|
<div class="h-full flex items-center justify-between px-4">
|
||||||
<div class="text-base group">
|
<div class="text-base group">
|
||||||
<a-tag color="green" bordered class="mr-1.5"> 新增 </a-tag>前端编辑器
|
<a-tag :color="container.id ? 'blue' : 'green'" bordered class="mr-2">
|
||||||
<i class="icon-park-outline-edit text-gray-400 hover:text-gray-700 ml-1"></i>
|
{{ container.id ? "修改" : "新增" }}
|
||||||
|
</a-tag>
|
||||||
|
<ani-texter v-model="container.title"></ani-texter>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<a-button> 导出 </a-button>
|
<a-button> 导出 </a-button>
|
||||||
<a-button> 设置 </a-button>
|
<a-button> 设置 </a-button>
|
||||||
<a-dropdown-button type="primary">
|
<a-dropdown-button type="primary" @click="onSaveData">
|
||||||
保存
|
保存
|
||||||
<template #content>
|
<template #content>
|
||||||
<a-doption>保存为JSON</a-doption>
|
<a-doption>保存为JSON</a-doption>
|
||||||
|
|
@ -19,6 +21,17 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import { Message } from "@arco-design/web-vue";
|
||||||
|
import { ContextKey } from "../config";
|
||||||
|
import AniTexter from "../panel-main/components/texter.vue";
|
||||||
|
|
||||||
|
const { saveData, container } = inject(ContextKey)!;
|
||||||
|
|
||||||
|
const onSaveData = () => {
|
||||||
|
saveData();
|
||||||
|
Message.success("保存成功");
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
当前组件
|
当前组件
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
</a-menu>
|
</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-tooltip content="帮助" position="right">
|
||||||
<a-button type="text">
|
<a-button type="text">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
|
@ -37,16 +37,20 @@
|
||||||
</div>
|
</div>
|
||||||
<div v-show="!collapsed">
|
<div v-show="!collapsed">
|
||||||
<ul class="list-none px-2 grid gap-2" @dragstart="onDragStart" @dragover="onDragOver">
|
<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"
|
<li
|
||||||
|
v-for="item in blocks"
|
||||||
|
:key="item.type"
|
||||||
:draggable="true"
|
:draggable="true"
|
||||||
:data-type="'text'"
|
:data-type="'text'"
|
||||||
|
class="flex items-center justify-between gap-2 bg-gray-100 text-gray-500 px-2 py-1 rounded cursor-move"
|
||||||
>
|
>
|
||||||
<div class="">
|
<div class="">
|
||||||
<i class="icon-park-outline-pic text-base text-gray-500"></i>
|
<i class="text-base text-gray-500" :class="item.icon"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 leading-0">
|
<div class="flex-1 leading-0">
|
||||||
<div class="">图片</div>
|
<div class="">
|
||||||
<!-- <div class="text-xs text-gray-400 mt-1">image</div> -->
|
{{ item.title }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -55,17 +59,20 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { BlockerMap } from "../blocks";
|
||||||
|
|
||||||
|
const blocks = Object.values(BlockerMap);
|
||||||
const collapsed = ref(false);
|
const collapsed = ref(false);
|
||||||
|
|
||||||
const onDragStart = (e: DragEvent) => {
|
const onDragStart = (e: DragEvent) => {
|
||||||
console.log('start');
|
console.log("start");
|
||||||
e.dataTransfer?.setData("type", (e.target as HTMLElement).dataset.type!);
|
e.dataTransfer?.setData("type", (e.target as HTMLElement).dataset.type!);
|
||||||
}
|
};
|
||||||
|
|
||||||
const onDragOver = (e: Event) => {
|
const onDragOver = (e: Event) => {
|
||||||
console.log('over');
|
console.log("over");
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
@resizing="onItemDragOrResize"
|
@resizing="onItemDragOrResize"
|
||||||
@activated="setCurrentBlock(data)"
|
@activated="setCurrentBlock(data)"
|
||||||
>
|
>
|
||||||
<component :is="BlockerMap[data.type].render" :data="{}" />
|
<component :is="BlockerMap[data.type].render" :data="data" />
|
||||||
</drag-resizer>
|
</drag-resizer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<span class="text-gray-700">{{ Math.floor(container.x) }} , {{ Math.floor(container.y) }} </span>
|
<span class="text-gray-700">{{ Math.floor(container.x) }} , {{ Math.floor(container.y) }} </span>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-gray-400 text-xs mr-3">
|
<span class="text-gray-400 text-xs mr-3">
|
||||||
尺寸:
|
画布:
|
||||||
<span class="text-gray-700">{{ container.width }} * {{ container.height }} </span>
|
<span class="text-gray-700">{{ container.width }} * {{ container.height }} </span>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-gray-400 text-xs mr-2">
|
<span class="text-gray-400 text-xs mr-2">
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
></i>
|
></i>
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="inline-flex items-center">
|
<span v-else class="inline-flex items-center">
|
||||||
<a-input size="small" v-model="descContent" class="!w-96"></a-input>
|
<a-input size="small" v-model="descContent" class="!w-96" v-bind="inputProps"></a-input>
|
||||||
<a-button type="text" size="small" @click="onDescEdited" class="ml-2">
|
<a-button type="text" size="small" @click="onDescEdited" class="ml-2">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="icon-park-outline-check"></i>
|
<i class="icon-park-outline-check"></i>
|
||||||
|
|
@ -22,11 +22,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { PropType } from "vue";
|
||||||
|
import { Input } from "@arco-design/web-vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
|
inputProps: {
|
||||||
|
type: Object as PropType<Partial<InstanceType<typeof Input>["$props"]>>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue"]);
|
const emit = defineEmits(["update:modelValue"]);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
<div
|
<div
|
||||||
class="relative"
|
class="relative"
|
||||||
:style="containerStyle"
|
:style="containerStyle"
|
||||||
|
@click="onClick"
|
||||||
@drop="onDragDrop"
|
@drop="onDragDrop"
|
||||||
@dragover.prevent
|
@dragover.prevent
|
||||||
@wheel="onMouseWheel"
|
@wheel="onMouseWheel"
|
||||||
|
|
@ -22,11 +23,19 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import BlockerMap from "../blocks";
|
||||||
import { ContextKey } from "../config";
|
import { ContextKey } from "../config";
|
||||||
import AniBlock from "./components/block.vue";
|
import AniBlock from "./components/block.vue";
|
||||||
import AniHeader from "./components/header.vue";
|
import AniHeader from "./components/header.vue";
|
||||||
|
import { cloneDeep } from "lodash-es";
|
||||||
|
|
||||||
const { blocks, container } = inject(ContextKey)!;
|
const { blocks, container, setCurrentBlock } = inject(ContextKey)!;
|
||||||
|
|
||||||
|
const onClick = (e: Event) => {
|
||||||
|
if(e.target === e.currentTarget) {
|
||||||
|
setCurrentBlock(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const isStart = ref(false);
|
const isStart = ref(false);
|
||||||
const position = ref({
|
const position = ref({
|
||||||
|
|
@ -88,22 +97,16 @@ const onDragDrop = (e: DragEvent) => {
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const blocker = BlockerMap[type];
|
||||||
|
const currentIds = blocks.value.map((item) => item.id);
|
||||||
|
const maxId = Math.max(...currentIds.map((item) => parseInt(item)));
|
||||||
|
const id = (maxId + 1).toString();
|
||||||
|
|
||||||
blocks.value.push({
|
blocks.value.push({
|
||||||
id: "0",
|
...cloneDeep(blocker.initial),
|
||||||
w: 200,
|
id,
|
||||||
h: 100,
|
|
||||||
bgColor: "#0099ff",
|
|
||||||
xFixed: false,
|
|
||||||
yFixed: false,
|
|
||||||
resizable: true,
|
|
||||||
draggable: true,
|
|
||||||
type,
|
|
||||||
x: e.offsetX,
|
x: e.offsetX,
|
||||||
y: e.offsetY,
|
y: e.offsetY,
|
||||||
data: {},
|
|
||||||
meta: {},
|
|
||||||
actived: false,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="flex gap-4">
|
|
||||||
<a-form-item label="左侧">
|
|
||||||
<a-input-number v-model="block.x" :min="0" :max="100">
|
|
||||||
<template #prefix>
|
|
||||||
<a-tooltip content="固定水平方向">
|
|
||||||
<i
|
|
||||||
class="cursor-pointer text-gray-400 hover:text-gray-700"
|
|
||||||
:class="
|
|
||||||
block.xFixed ? 'icon-park-outline-lock text-gray-900' : 'icon-park-outline-unlock text-gray-400'
|
|
||||||
"
|
|
||||||
@click="block.xFixed = !block.xFixed"
|
|
||||||
></i>
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="顶部">
|
|
||||||
<a-input-number v-model="block.y" :min="0" :max="100">
|
|
||||||
<template #prefix>
|
|
||||||
<a-tooltip content="固定垂直方向">
|
|
||||||
<i
|
|
||||||
class="cursor-pointer text-gray-400 hover:text-gray-700"
|
|
||||||
:class="
|
|
||||||
block.yFixed ? 'icon-park-outline-lock text-gray-900' : 'icon-park-outline-unlock text-gray-400'
|
|
||||||
"
|
|
||||||
@click="block.yFixed = !block.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="block.w" :min="0" :max="100"> </a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="高度">
|
|
||||||
<a-input-number v-model="block.h" :min="0" :max="100"> </a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a-form-item label="背景图片">
|
|
||||||
<a-input v-model="block.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="block.bgColor" allow-clear placeholder="无">
|
|
||||||
<template #prefix>
|
|
||||||
<color-picker v-model="block.bgColor"></color-picker>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { PropType } from "vue";
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
block: {
|
|
||||||
type: Object as PropType<any>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -2,16 +2,13 @@
|
||||||
<div class="w-[248px] overflow-hidden p-3">
|
<div class="w-[248px] overflow-hidden p-3">
|
||||||
<a-radio-group type="button" class="w-full mb-2">
|
<a-radio-group type="button" class="w-full mb-2">
|
||||||
<a-radio value="1">基本</a-radio>
|
<a-radio value="1">基本</a-radio>
|
||||||
<a-radio value="2">字体</a-radio>
|
<a-radio value="2">文本</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
<a-form :model="block" layout="vertical">
|
<a-form :model="block" layout="vertical">
|
||||||
<div v-if="current.block" class="muti-form-item mt-2">
|
<div v-if="current.block" class="muti-form-item mt-2">
|
||||||
<component :is="BlockerMap[current.block.type].option" :data="current.block" />
|
<component :is="BlockerMap[current.block.type].option" :data="current.block" />
|
||||||
</div>
|
</div>
|
||||||
<div class="muti-form-item">
|
<a-empty v-else :description="'选择组件后显示'" class="mt-8"></a-empty>
|
||||||
<a-divider orientation="left">中文设置</a-divider>
|
|
||||||
<text-attr :block="block.textStyle"></text-attr>
|
|
||||||
</div>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<a-form-item label="内容">
|
|
||||||
<a-textarea v-model="block.text" placeholder="输入内容..."></a-textarea>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="颜色">
|
|
||||||
<a-input v-model="block.color">
|
|
||||||
<template #prefix>
|
|
||||||
<color-picker v-model="block.color"></color-picker>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<div class="flex gap-4">
|
|
||||||
<a-form-item label="字体">
|
|
||||||
<a-select v-model="block.family" :options="TextFamilyOptions" class="w-full overflow-hidden"> </a-select>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="大小">
|
|
||||||
<a-input-number v-model="block.size" :min="12"> </a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-4">
|
|
||||||
<a-form-item label="样式">
|
|
||||||
<div class="h-8 flex items-center justify-between">
|
|
||||||
<a-tag
|
|
||||||
class="cursor-pointer !h-7"
|
|
||||||
:color="block.bold ? 'blue' : ''"
|
|
||||||
:bordered="block.bold ? true : false"
|
|
||||||
@click="block.bold = !block.bold"
|
|
||||||
>
|
|
||||||
<i class="icon-park-outline-text-bold"></i>
|
|
||||||
</a-tag>
|
|
||||||
<a-tag
|
|
||||||
class="!h-7 cursor-pointer"
|
|
||||||
:color="block.italic ? 'blue' : ''"
|
|
||||||
:bordered="block.italic ? true : false"
|
|
||||||
@click="block.italic = !block.italic"
|
|
||||||
>
|
|
||||||
<i class="icon-park-outline-text-italic"></i>
|
|
||||||
</a-tag>
|
|
||||||
<a-tag
|
|
||||||
class="!h-7 cursor-pointer"
|
|
||||||
:color="block.underline ? 'blue' : ''"
|
|
||||||
:bordered="block.underline ? true : false"
|
|
||||||
@click="block.underline = !block.underline"
|
|
||||||
>
|
|
||||||
<i class="icon-park-outline-text-underline"></i>
|
|
||||||
</a-tag>
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="方向">
|
|
||||||
<a-select v-model="block.align" :options="TextAlignOptions"></a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
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: {
|
|
||||||
type: Object as PropType<any>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -7,43 +7,34 @@ export {}
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
declare module '@vue/runtime-core' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
AAvatar: typeof import('@arco-design/web-vue')['Avatar']
|
|
||||||
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
|
|
||||||
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
|
|
||||||
AButton: typeof import('@arco-design/web-vue')['Button']
|
AButton: typeof import('@arco-design/web-vue')['Button']
|
||||||
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
|
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
|
||||||
ACheckboxGroup: typeof import('@arco-design/web-vue')['CheckboxGroup']
|
|
||||||
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
|
|
||||||
ADivider: typeof import('@arco-design/web-vue')['Divider']
|
ADivider: typeof import('@arco-design/web-vue')['Divider']
|
||||||
ADoption: typeof import('@arco-design/web-vue')['Doption']
|
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']
|
ADropdownButton: typeof import('@arco-design/web-vue')['DropdownButton']
|
||||||
|
AEmpty: typeof import('@arco-design/web-vue')['Empty']
|
||||||
AForm: typeof import('@arco-design/web-vue')['Form']
|
AForm: typeof import('@arco-design/web-vue')['Form']
|
||||||
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
|
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
|
||||||
AInput: typeof import('@arco-design/web-vue')['Input']
|
AInput: typeof import('@arco-design/web-vue')['Input']
|
||||||
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
|
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
|
||||||
|
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
||||||
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
|
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']
|
|
||||||
ALink: typeof import('@arco-design/web-vue')['Link']
|
ALink: typeof import('@arco-design/web-vue')['Link']
|
||||||
AMenu: typeof import('@arco-design/web-vue')['Menu']
|
AMenu: typeof import('@arco-design/web-vue')['Menu']
|
||||||
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
||||||
AMenuItemGroup: typeof import('@arco-design/web-vue')['MenuItemGroup']
|
|
||||||
AModal: typeof import('@arco-design/web-vue')['Modal']
|
AModal: typeof import('@arco-design/web-vue')['Modal']
|
||||||
APagination: typeof import('@arco-design/web-vue')['Pagination']
|
APagination: typeof import('@arco-design/web-vue')['Pagination']
|
||||||
APopover: typeof import('@arco-design/web-vue')['Popover']
|
APopover: typeof import('@arco-design/web-vue')['Popover']
|
||||||
ARadio: typeof import('@arco-design/web-vue')['Radio']
|
ARadio: typeof import('@arco-design/web-vue')['Radio']
|
||||||
|
ARadioButton: typeof import('@arco-design/web-vue')['RadioButton']
|
||||||
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup']
|
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup']
|
||||||
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
|
|
||||||
ASelect: typeof import('@arco-design/web-vue')['Select']
|
ASelect: typeof import('@arco-design/web-vue')['Select']
|
||||||
|
ASpace: typeof import('@arco-design/web-vue')['Space']
|
||||||
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
||||||
ATag: typeof import('@arco-design/web-vue')['Tag']
|
ATag: typeof import('@arco-design/web-vue')['Tag']
|
||||||
ATextarea: typeof import('@arco-design/web-vue')['Textarea']
|
ATextarea: typeof import('@arco-design/web-vue')['Textarea']
|
||||||
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
|
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
|
||||||
AUpload: typeof import('@arco-design/web-vue')['Upload']
|
BaseOption: typeof import('./../components/editor/components/BaseOption.vue')['default']
|
||||||
Block: typeof import('./../components/editor/panel-main/components/block.vue')['default']
|
Block: typeof import('./../components/editor/panel-main/components/block.vue')['default']
|
||||||
BlockAttr: typeof import('./../components/editor/panel-right/block-attr.vue')['default']
|
BlockAttr: typeof import('./../components/editor/panel-right/block-attr.vue')['default']
|
||||||
BreadCrumb: typeof import('./../components/breadcrumb/bread-crumb.vue')['default']
|
BreadCrumb: typeof import('./../components/breadcrumb/bread-crumb.vue')['default']
|
||||||
|
|
@ -55,14 +46,15 @@ declare module '@vue/runtime-core' {
|
||||||
ImagePicker: typeof import('./../components/editor/components/ImagePicker.vue')['default']
|
ImagePicker: typeof import('./../components/editor/components/ImagePicker.vue')['default']
|
||||||
InputColor: typeof import('./../components/editor/components/InputColor.vue')['default']
|
InputColor: typeof import('./../components/editor/components/InputColor.vue')['default']
|
||||||
InputImage: typeof import('./../components/editor/components/InputImage.vue')['default']
|
InputImage: typeof import('./../components/editor/components/InputImage.vue')['default']
|
||||||
Option: typeof import('./../components/editor/blocks/text/option.vue')['default']
|
Marquee: typeof import('./../components/editor/blocks/text/marquee.vue')['default']
|
||||||
|
Option: typeof import('./../components/editor/blocks/font/option.vue')['default']
|
||||||
Page403: typeof import('./../components/error/page-403.vue')['default']
|
Page403: typeof import('./../components/error/page-403.vue')['default']
|
||||||
PanelHeader: typeof import('./../components/editor/panel-header/index.vue')['default']
|
PanelHeader: typeof import('./../components/editor/panel-header/index.vue')['default']
|
||||||
PanelLeft: typeof import('./../components/editor/panel-left/index.vue')['default']
|
PanelLeft: typeof import('./../components/editor/panel-left/index.vue')['default']
|
||||||
PanelMain: typeof import('./../components/editor/panel-main/index.vue')['default']
|
PanelMain: typeof import('./../components/editor/panel-main/index.vue')['default']
|
||||||
PanelRight: typeof import('./../components/editor/panel-right/index.vue')['default']
|
PanelRight: typeof import('./../components/editor/panel-right/index.vue')['default']
|
||||||
Preview: typeof import('./../components/editor/preview/index.vue')['default']
|
Preview: typeof import('./../components/editor/preview/index.vue')['default']
|
||||||
Render: typeof import('./../components/editor/blocks/text/render.vue')['default']
|
Render: typeof import('./../components/editor/blocks/font/render.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
TextAttr: typeof import('./../components/editor/panel-right/text-attr.vue')['default']
|
TextAttr: typeof import('./../components/editor/panel-right/text-attr.vue')['default']
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue