⚠ 带有上下 drag 的表单设计器 先看效果
功能依赖:vue2 + element-ui + draggable
1.文字类功能操作 字体风格、字号、颜色、粗体、显示位置
2.分割线操作功能 分割线颜色、分割线类型(单/双分割线)、上/下分割线宽、分割线间隔
3.中间列部分每个元素drag功能,同步左侧预览区域上下移动
<template>
<div class="report-list-container">
<div class="page-title">
面包屑
</div>
<div class="content-mode">
<!-- 左侧预览区域 -->
<div class="left-preview">
<div>
// myArray 中的每一个对象都是一个功能(标题、下划线等)
// v-html渲染内容 buildLabel方法传入当前项根据不同处理在函数内返回不同渲染内容
<div
v-for="item in myArray"
:key="item.id"
v-html="buildLabel(item)"
:style="{
fontSize: `${item.modelFontSize}px`,
textAlign: item.textAlign,
color: item.color || 'black',
marginTop: `${item.marginTop}px`,
marginBottom: `${item.marginBottom}px`,
fontFamily: item.fontFamily || 'Microsoft YaHei',
display: item.displayLeft || 'block',
backgroundColor: item.lineColor || '',
fontWeight: item.fontWeight ? item.fontWeight : 'normal',
height: item.lineWidth ? `${item.lineWidth}px` : '',
display: item.display || 'block',
justifyContent: item.justifyContent || '',
}"
:class="item.class || ''"
></div>
</div>
</div>
<!--右侧编辑区域-->
<div class="right-edit">
<div class="drag">
<p class="edit-title">红头</p>
<draggable
v-model="myArray"
chosenClass="chosen"
forceFallback="true"
group="people"
animation="1000"
>
<transition-group>
<div
class="item"
v-for="element in myArray"
:style="{ display: element.displayRight || 'block' }"
:key="element.id"
:class="{ highlight: currentHighlightId === element.id }"
>
<div @click="setCurrentHighlightId(element)">
{{ element.text }}
</div>
</div>
</transition-group>
</draggable>
<div style="text-align: center; margin-top: 20px">
<el-button type="primary" @click="addDom">添加</el-button>
</div>
</div>
<div class="operation">
<div
style="
text-align: right;
height: 50px;
line-height: 50px;
background-color: #f9f9f9;
"
>
<span style="margin-right: 10px">显示状态</span>
<el-switch
v-model="conceal"
active-color="#13ce66"
inactive-color="#ff4949"
@change="displayStatusChange()"
style="margin-right: 10px"
>
</el-switch>
</div>
<div class="operating-area">
<div v-for="item in myArray" :key="item.id">
<div
v-if="item.id === currentHighlightId && item.conceal === true"
>
<!-- 字体 -->
<div class="font" v-if="item.fontFamily">
<label class="label" for="fontSize">字体</label>
<span>{{ item.fontFamily }}</span>
<el-select
class="oneself-right"
size="small"
v-model="item.fontFamily"
placeholder="字体"
>
<el-option
v-for="element in fontFamilyOptions"
:key="element.value"
:label="element.label"
:value="element.value"
>
</el-option>
</el-select>
</div>
<!-- 调整字号 -->
<div class="font-size" v-if="item.fontSize">
<label class="label" for="fontSize">字号</label>
<span>{{ item.wordSizeValue }}</span>
<div style="display: flex" class="oneself-right">
<div class="size-mark">
<el-select
size="small"
v-model="item.fontSize"
placeholder="字号"
@change="wordSizeChange(item.fontSize, item)"
>
<el-option
v-for="element in fontSizeOptions"
:key="element.value"
:label="element.label"
:value="element.value"
>
</el-option>
</el-select>
</div>
<div class="size-pound">
<el-select
popper-class="select-pound"
size="small"
v-model="item.poundOrPixel"
placeholder="px"
@change="pondChange(item)"
@focus="poundOrPixelFocus(item)"
>
<el-option
v-for="i in poundOptions"
:key="i.value"
:label="i.label"
:value="i.value"
>
</el-option>
</el-select>
</div>
</div>
</div>
<!-- 字体颜色 -->
<div class="font-color" v-if="item.color || item.colorSelect">
<label class="label" for="fontSize">颜色</label>
<span>{{ item.color }}</span>
<div style="display: flex" class="oneself-right">
<div class="clear" @click="clearColor(item.id)">重置</div>
<div>
<el-color-picker
class="color-select"
v-model="item.color"
></el-color-picker>
</div>
</div>
</div>
<!-- 粗体 -->
<div class="font-width" v-if="item.fontWeight">
<label class="label" for="fontWidth">粗体</label>
<span>{{ item.fontWidthText }}</span>
<div style="display: flex" class="oneself-right">
<div>
<el-radio-group
@input="fontWidthRadioInput(item)"
v-model="item.fontWidthText"
size="small"
>
<el-radio-button label="正常"
><i
size="10"
class="iconfont icon-a-VectorStroke-2"
></i
></el-radio-button>
<el-radio-button label="加粗体"
><i size="10" class="iconfont icon-bold"></i
></el-radio-button>
</el-radio-group>
</div>
</div>
</div>
<!-- 显示位置 -->
<div
class="text-align align"
v-if="(item.text || item.subset) && item.nullText"
>
<label class="label" for="fontWidth">显示位置</label>
<span>{{ item.textAlignText }}</span>
<div style="display: flex" class="oneself-right">
<div>
<el-radio-group
@input="TextAlignRadioInput(item)"
v-model="item.textAlign"
size="small"
>
<el-radio-button label="left"
><i
style="font-size: 20px"
class="iconfont icon-duiqifangshi-zuoduiqi"
></i
></el-radio-button>
<el-radio-button label="center"
><i
class="iconfont icon-duiqifangshi-juzhongduiqi"
></i
></el-radio-button>
<el-radio-button label="right"
><i class="iconfont icon-duiqifangshi-youduiqi"></i
></el-radio-button>
<el-radio-button label="justify"
><i class="iconfont icon-dispersed"></i
></el-radio-button>
</el-radio-group>
</div>
</div>
</div>
<!-- 分割线颜色 -->
<div class="font-size" v-if="item.lineColorSelect">
<label class="label" for="fontSize">分割线颜色</label>
<span>{{ item.content[0].lineColor }}</span>
<div style="display: flex" class="oneself-right">
<div
class="size-mark"
style="
text-align: right;
display: flex;
align-items: center;
justify-content: right;
"
>
<div
class="clear"
@click="
() => {
(item.content[0].lineColor = '#FF0000'),
(item.content[1].lineColor = '#FF0000');
}
"
>
重置
</div>
<el-color-picker
@change="changeColor(item)"
v-model="item.content[0].lineColor"
></el-color-picker>
</div>
</div>
</div>
<!-- 分割类型 -->
<div class="font-size" v-if="item.lineType">
<label class="label" for="fontSize">分割类型</label>
<span>{{
item.lineType === "one" ? "单线分割" : "双线分割"
}}</span>
<div
style="display: flex; width: 100px"
class="oneself-right"
>
<div class="size-mark" style="text-align: right">
<el-radio-group
style="display: flex"
@input="radioLine(item)"
v-model="item.lineType"
size="small"
>
<el-radio-button label="one"
><i class="iconfont icon-caidanfengexian"></i
></el-radio-button>
<el-radio-button label="double"
><i class="iconfont icon-fengexianfenge"></i
></el-radio-button>
</el-radio-group>
</div>
</div>
</div>
<!-- 上分割线线宽度 -->
<div
class="font-size upLineWidth"
v-if="item.lineWidth || item.FixedLineWidth"
>
<label class="label" for="fontSize">上分割线宽</label>
<span
><el-slider
:max="20"
:min="1"
@input="upLineSliderInput(item)"
v-model="item.content[0].inputWidth"
></el-slider
></span>
<div style="display: flex" class="oneself-right">
<div @click="upLineReset(item)" class="clear">重置</div>
<div class="size-mark" style="width: 65px">
<input
type="number"
style="
border: 1px solid #ccc;
border-right: 0;
width: 65px;
height: 36px;
box-sizing: border-box;
padding: 5px;
"
:value="item.content[0].inputWidth"
@input="upLineWidthInput($event, item)"
/>
</div>
<div class="size-pound">
<el-select
style="height: 39px; border-radius: 0px"
popper-class="select-pound"
size="small"
v-model="item.content[0].poundOrPixel"
placeholder="px"
@change="upLineChange(item)"
>
<el-option
v-for="i in poundOptions"
:key="i.value"
:label="i.label"
:value="i.value"
>
</el-option>
</el-select>
</div>
</div>
</div>
<!-- 下分割线线宽度 -->
<div
class="font-size upLineWidth"
v-if="
(item.lineWidth || item.FixedLineWidth) &&
item.content[1].display === 'block'
"
>
<label class="label" for="fontSize">下分割线宽</label>
<span
><el-slider
:max="20"
:min="1"
@input="downLineSliderInput(item)"
v-model="item.content[1].inputWidth"
></el-slider
></span>
<div style="display: flex" class="oneself-right">
<div @click="downLineReset(item)" class="clear">重置</div>
<div class="size-mark" style="width: 65px">
<input
type="number"
style="
border: 1px solid #ccc;
border-right: 0;
width: 65px;
height: 36px;
box-sizing: border-box;
padding: 5px;
"
:value="item.content[1].inputWidth"
@input="downLineWidthInput($event, item)"
/>
</div>
<div class="size-pound">
<el-select
style="height: 39px; border-radius: 0px"
popper-class="select-pound"
size="small"
v-model="item.content[1].poundOrPixel"
placeholder="px"
@change="downLineChange(item)"
>
<el-option
v-for="i in poundOptions"
:key="i.value"
:label="i.label"
:value="i.value"
>
</el-option>
</el-select>
</div>
</div>
</div>
<!-- 分割线间隔 -->
<div
class="font-size upLineWidth"
v-if="
(item.lineWidth || item.FixedLineWidth) &&
item.content[1].display === 'block'
"
>
<label class="label" for="fontSize">分割线间隔</label>
<span
><el-slider
:max="20"
:min="1"
@input="intervalLineMarginInput(item)"
v-model="item.content[0].interValMarginBottom"
></el-slider
></span>
<div style="display: flex" class="oneself-right">
<div @click="intervalReset(item)" class="clear">重置</div>
<div class="size-mark" style="width: 65px">
<input
type="number"
style="
border: 1px solid #ccc;
border-right: 0;
width: 65px;
height: 36px;
box-sizing: border-box;
padding: 5px;
"
:value="item.content[0].interValMarginBottom"
@input="intervalMarginInput($event, item)"
/>
</div>
<div class="size-pound">
<el-select
style="height: 39px; border-radius: 0px"
popper-class="select-pound"
size="small"
v-model="item.content[0].lineInterval"
placeholder="px"
@change="intervalChange(item)"
>
<el-option
v-for="i in poundOptions"
:key="i.value"
:label="i.label"
:value="i.value"
>
</el-option>
</el-select>
</div>
</div>
</div>
<div
class="font-size upLineWidth"
v-if="item.height && !item.lineType && !item.fontSize"
>
<label class="label" for="fontSize">占位符高度</label>
<span
><el-slider
:max="100"
:min="1"
@input="placeholderSlider(item)"
v-model="item.virtualHeight"
></el-slider
></span>
<div style="display: flex" class="oneself-right">
<div @click="placeholderReset(item)" class="clear">
重置
</div>
<div class="size-mark" style="width: 65px">
<input
type="number"
style="
border: 1px solid #ccc;
border-right: 0;
width: 65px;
height: 36px;
box-sizing: border-box;
padding: 5px;
"
:value="item.virtualHeight"
@input="placeholderInput($event, item)"
/>
</div>
<div class="size-pound">
<el-select
style="height: 39px; border-radius: 0px"
popper-class="select-pound"
size="small"
v-model="item.virtualUnit"
placeholder="px"
@change="placeholderChange(item)"
>
<el-option
v-for="i in poundOptions"
:key="i.value"
:label="i.label"
:value="i.value"
>
</el-option>
</el-select>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
// 需要 npm 安装 draggable
import draggable from "vuedraggable";
export default {
components: {
draggable,
},
watch: {},
data() {
return {
conceal: true, // 显示状态中间值
median: false, // true 表示当前选择的是分散对齐
overall: "pixel", // 中间值承接 pixel 或 pound
title: "红头标题",
currentHighlightId: 0,
// 字体选项
fontFamilyOptions: [
{
value: "宋体",
label: "宋体",
},
{
value: "黑体",
label: "黑体",
},
{
value: "微软雅黑",
label: "微软雅黑",
},
{
value: "新宋体",
label: "新宋体",
},
{
value: "楷体",
label: "楷体",
},
{
value: "仿宋",
label: "仿宋",
},
{
value: "微软正黑体",
label: "微软正黑体",
},
],
wordSizeValue: "小一", // 承接字号文本
// 字号选项
fontSizeOptions: [
{
value: "56",
label: "初号",
},
{
value: "48",
label: "小初",
},
{
value: "34.7",
label: "一号",
},
{
value: "32",
label: "小一",
},
{
value: "29.3",
label: "二号",
},
{
value: "24",
label: "小二",
},
{
value: "21.3",
label: "三号",
},
{
value: "20",
label: "小三",
},
{
value: "18.7",
label: "四号",
},
{
value: "16",
label: "小四",
},
{
value: "14",
label: "五号",
},
{
value: "12",
label: "小五",
},
{
value: "10",
label: "六号",
},
{
value: "8.7",
label: "小六",
},
{
value: "7.3",
label: "七号",
},
{
value: "6.7",
label: "八号",
},
],
// 磅选项
poundOptions: [
{
value: "pixel",
label: "px",
},
{
value: "pound",
label: "磅",
},
],
myArray: [
{
colorSelect: true, // 点击清空不要删除颜色选择器 只清空 color 值 带有颜色选择器的项此值必须为 true
people: "cn",
id: 0,
name: "红头文件主标题",
label: "h1",
class: "h1",
defaultColor: "#FF0000",
text: "红头文件主标题",
fontSize: "32",
modelFontSize: "32",
textAlign: "center",
color: "#FF0000" || "black",
marginTop: 0,
marginBottom: 0,
fontFamily: "微软雅黑",
poundOrPixel: "pixel", // 磅或者px
fontWeight: "normal",
fontWidthText: "正常",
textAlignText: "居中显示",
display: "block",
justifyContent: "space-between",
nullText: true,
median: false, // 是否为分散对齐切换为非分散对齐
wordSizeValue: "小一",
conceal: true, // 隐藏元素
},
{
colorSelect: true, // 点击清空不要删除颜色选择器 只清空 color 值 带有颜色选择器的项此值必须为 true
people: "cn",
id: 1,
name: "副标题",
label: "div",
class: "h2",
defaultColor: "#FF0000",
text: "副标题",
fontSize: "24",
modelFontSize: "24",
textAlign: "center",
color: "#FF0000" || "black",
marginTop: 10,
marginBottom: 0,
fontFamily: "微软雅黑",
poundOrPixel: "pixel", // 磅或者px
fontWeight: "normal",
fontWidthText: "正常",
textAlignText: "居中显示",
display: "block",
justifyContent: "space-between",
nullText: true,
median: false, // 是否为分散对齐切换为非分散对齐
wordSizeValue: "小二",
conceal: true, // 隐藏元素
},
{
colorSelect: true, // 点击清空不要删除颜色选择器 只清空 color 值 带有颜色选择器的项此值必须为 true
people: "cn",
id: 2,
name: "期刊编号",
label: "div",
class: "periodical",
defaultColor: "#000000",
text: "期刊编号",
fontSize: "16",
modelFontSize: "16",
textAlign: "center",
color: "#FF0000" || "black",
marginTop: 10,
marginBottom: 10,
fontFamily: "微软雅黑",
poundOrPixel: "pixel", // 磅或者px
fontWeight: "normal",
fontWidthText: "正常",
textAlignText: "居中显示",
display: "block",
justifyContent: "space-between",
nullText: true,
median: false, // 是否为分散对齐切换为非分散对齐
wordSizeValue: "小四",
conceal: true, // 隐藏元素
},
{
subset: true,
colorSelect: true, // 点击清空不要删除颜色选择器 只清空 color 值 带有颜色选择器的项此值必须为 true
people: "cn",
id: 3,
uniqueIdentification: true, // 唯一标识true的解决问题(在单行元素多列情况下,选择分散显示,操作显示状态按钮,不会调用 disperseAlign 方法),单行单列元素选择分散对齐后操作显示状态按钮重新调用 disperseAlign 方法包裹 span 标签
name: "编制单位/日期",
label: "div",
class: "date-unit",
defaultColor: "#000000",
text: "编制单位/日期",
fontSize: "16",
modelFontSize: "16",
textAlign: "justify",
color: "#000000" || "black",
marginTop: 10,
marginBottom: 10,
fontFamily: "微软雅黑",
poundOrPixel: "pixel", // 磅或者px
fontWeight: "normal",
fontWidthText: "正常",
textAlignText: "居中显示",
display: "flex",
displayRight: "block",
justifyContent: "space-between",
nullText: true,
median: false, // 是否为分散对齐切换为非分散对齐
wordSizeValue: "小四",
conceal: true, // 隐藏元素
content: [
{
colorSelect: true, // 点击清空不要删除颜色选择器 只清空 color 值 带有颜色选择器的项此值必须为 true
people: "cn",
id: 31,
name: "编制单位说明",
label: "div",
class: "unit",
defaultColor: "#000000",
text: "编制单位说明",
fontSize: "16",
textAlign: "justify",
color: "#000000" || "black",
marginTop: 10,
marginBottom: 0,
marginRight: 10,
fontFamily: "微软雅黑",
poundOrPixel: "pixel", // 磅或者px
fontWeight: "normal",
fontWidthText: "正常",
textAlignText: "居中显示",
display: "block",
justifyContent: "space-between",
nullText: true,
median: false, // 是否为分散对齐切换为非分散对齐
value: "说明说明说明说明说明",
},
{
colorSelect: true, // 点击清空不要删除颜色选择器 只清空 color 值 带有颜色选择器的项此值必须为 true
people: "cn",
id: 31,
name: "编制日期",
label: "div",
class: "date",
defaultColor: "#000000",
text: "编制日期",
fontSize: "16",
textAlign: "justify",
color: "#000000" || "black",
marginTop: 10,
marginBottom: 10,
fontFamily: "微软雅黑",
poundOrPixel: "pixel", // 磅或者px
fontWeight: "normal",
fontWidthText: "正常",
textAlignText: "居中显示",
display: "block",
justifyContent: "space-between",
nullText: true,
median: false, // 是否为分散对齐切换为非分散对齐
value: "2024年08月30日",
},
],
},
{
subset: true, // 有子集元素
lineColorSelect: true,
hidden: true,
id: 4,
name: "分割线",
text: "分割线",
label: "div",
class: "hr upLine",
textAlign: "left",
marginTop: 10,
interValMarginBottom: 5,
lineType: "double", // 单双线
FixedLineWidth: true, // input为空也不删除此线
poundOrPixel: "pixel", // 磅或者px
lineInterval: "pixel", // 分割线间隔单位
conceal: true, // 隐藏元素
content: [
{
hidden: "block",
lineColorSelect: true,
// 初始化上下分割线都显示
display: "block",
people: "cn",
id: 41,
name: "分割线",
text: "",
label: "div",
class: "hr upLine",
lineColor: "#FF0000",
marginTop: 10,
marginBottom: 5, // 真实下外边距
interValMarginBottom: 5, // 绑定变量中间值
inputWidth: 4, // 绑定输入框
lineWidth: 4, // 真实宽度
lineType: "double", // 单双线
FixedLineWidth: true, // 就算input为空也不删除此线
poundOrPixel: "pixel", // 磅或者px
lineInterval: "pixel", // 分割线间隔单位
},
{
lineColorSelect: true,
hidden: "block",
display: "block",
people: "cn",
id: 42,
name: "分割线",
label: "div",
class: "hr downLine",
text: "",
lineColor: "#FF0000",
marginTop: 0,
interValMarginBottom: 0,
marginBottom: 10,
inputWidth: 2,
lineWidth: 2,
lineType: "double", // 单双线
FixedLineWidth: true, // 就算input为空也不删除此线
poundOrPixel: "pixel", // 磅或者px
lineInterval: "pixel", // 分割线间隔单位
},
],
},
{
// colorSelect: true, // 点击清空不要删除颜色选择器 只清空 color 值 带有颜色选择器的项此值必须为 true
people: "cn",
subset: true,
id: 5,
name: "占位符",
label: "div",
class: "placeholder",
sonClass: "son-placeholder",
text: "占位符",
lineType: "",
fontSize: "",
modelFontSize: "",
poundOrPixel: "pixel", // 磅或者px
virtualUnit: "pixel", // 承接单位
conceal: true, // 隐藏元素
virtualHeight: 20, // 承接高度
height: 20,
},
],
};
},
computed: {},
mounted() {},
methods: {
buildLabel(item) {
if ((item.text || item.subset) && item.conceal !== false) {
// 这里的功能是固定的,所以可以使用 name 或 id 固定值判断
if (item.name === "分割线") {
let str = "";
// 构造标签 并将标签名样式绑在新构造的元素上 当前item.content有两项,分别为上分割线和下分割线
item.content.forEach((i) => {
str += `<div class="${i.class}"
style="height:${i.lineWidth}px;width:100%;background-color:${i.lineColor};margin-bottom:${i.marginBottom}px;margin-top:${i.marginTop}px;display:${i.display}">
</div>`;
});
return `${str}`;
} else if (item.name === "编制单位/日期") {
let str = "";
item.content.forEach((i) => {
str += `<div style='font-weight:${i.fontWeight};margin-right:${i.marginRight}px'>
${i.name}:${i.value}
</div>`;
});
return `${str}`;
} else if (item.name === "占位符") {
return `<div class="${item.sonClass}" style="height: ${item.height}px"></div>`;
} else {
return `${item.name}`;
}
}
},
// 获取当前选中的 id 和 title 以及 conceal 元素隐藏状态
setCurrentHighlightId(element) {
this.currentHighlightId = element.id;
this.title = element.name;
this.conceal = element.conceal;
},
// 切换显示状态
displayStatusChange() {
this.myArray.forEach((item) => {
// 找到对应的 id 赋值
if (item.id === this.currentHighlightId) {
// 显示状态的 switch 状态 v-model 到 this.conceal
// 切换当前项显示态
item.conceal = this.conceal;
}
// 切换显示态时 找到当前id且显示状态为true时,且当前对其方式为分散对齐,且 uniqueIdentification 不全等于 true
// uniqueIdentification: true 的作用:唯一标识true的解决问题(在单行元素多列情况下,选择分散显示,操作显示状态按钮,不会调用 disperseAlign 方法),单行单列元素选择分散对齐后操作显示状态按钮重新调用 disperseAlign 方法
if (
item.id === this.currentHighlightId &&
item.conceal === true &&
item.textAlign === "justify" && item.uniqueIdentification !== true
) {
// 调用分散对齐方法
this.disperseAlign(item);
}
});
},
// 在切换单位的 select 框获取焦点时 用 overall 承接一下单位
poundOrPixelFocus(item) {
this.overall = item.poundOrPixel;
},
// "px"和"磅"的change事件
pondChange(item) {
if (item.poundOrPixel !== this.overall && item.poundOrPixel === "pixel") {
this.overall = "pixel";
// modelFontSize 字体大小的承接值
// 选择的是"px"情况下赋值当前项字体大小
item.modelFontSize = item.fontSize;
}
if (item.poundOrPixel !== this.overall && item.poundOrPixel === "pound") {
this.overall = "pound";
// 选择的为"磅"单位,字体放大1.33倍
item.modelFontSize = item.fontSize * 1.33;
}
},
// 字号大小切换事件
wordSizeChange(size, element) {
this.fontSizeOptions.forEach((item) => {
// 找到当前项的字体大小在选项值里对应的默认大小
if (item.value === size) {
// fontSizeOptions 选项里的字体大小赋值给当前传入项(element)的字体值
element.wordSizeValue = item.label;
// 如果后缀单位为"磅"则放大1.33倍
if (element.poundOrPixel === "pound") {
element.modelFontSize = item.value * 1.33;
}
// 如果后缀单位为"px"则放直接赋值
if (element.poundOrPixel === "pixel") {
element.modelFontSize = item.value;
}
}
});
},
// 清除颜色事件
clearColor(id) {
this.myArray.forEach((item) => {
if (item.id === id) {
// 还原颜色值
item.color = "#000000";
}
});
},
// 选择粗体事件
fontWidthRadioInput(item) {
if (item.fontWidthText === "加粗体") {
// 如果选择的是粗体,并且当前文本对齐方式为分散对齐
if (item.textAlign === "justify") {
// 则需要根据当前 item 里的 class 名称获取当前项元素里到所有的 span 标签依次加粗
const spanArr = document.querySelectorAll(`.${item.class} span`);
spanArr.forEach((i) => {
i.style.fontWeight = "bold";
});
}
// 如果是在"编制单位/日期"里选择的加粗 则将当前项下的 content 里的两个元素都进行加粗
if (item.name === "编制单位/日期") {
item.content.forEach((element) => {
element.fontWeight = "bold";
});
}
// 排除以上两种情况 赋值"bold"值和"加粗体"
item.fontWeight = "bold";
item.fontWidthText = "加粗体";
}
if (item.fontWidthText === "正常") {
if (item.textAlign === "justify") {
const spanArr = document.querySelectorAll(`.${item.class} span`);
spanArr.forEach((i) => {
i.style.fontWeight = "normal";
});
}
if (item.name === "编制单位/日期") {
item.content.forEach((element) => {
element.fontWeight = "normal";
});
}
item.fontWeight = "normal";
item.fontWidthText = "正常";
}
},
// 选择分散对齐的事件
disperseAlign(item) {
item.median = true;
item.textAlignText = "分散显示";
setTimeout(() => {
// 获取到对应文本 split 分割
let domTextArr = document
.querySelector(`.${item.class}`)
.innerHTML.split("");
let newDom = "";
// 然后给分割后的每个独立的文字包裹 span 标签
domTextArr.forEach((item) => {
newDom += `<span>${item}</span>`;
});
document.querySelector(`.${item.class}`).innerHTML = newDom;
// 如果当前项的字体宽度为粗体,那么包裹完 span 标签后进行依次加粗
if (item.fontWeight === "bold") {
const spanArr = document.querySelectorAll(`.${item.class} span`);
spanArr.forEach((i) => {
i.style.fontWeight = "bold";
});
}
item.display = "flex";
});
},
// 选择文本对齐方式事件
TextAlignRadioInput(item) {
// 单独处理单行多列情况
if (item.name === "编制单位/日期") {
if (item.textAlign !== "justify") {
// 除分散对齐外正常使用当前项的对齐方式
item.justifyContent = item.textAlign;
} else {
// 两端对齐
item.justifyContent = "space-between";
}
}
// median:代表是否为分散对齐切换为非分散对齐
if (item.textAlign !== "justify" && item.median === true) {
document.querySelector(`.${item.class}`).innerHTML = item.name;
item.display = "block";
item.median = false;
}
if (item.textAlign === "left") {
item.textAlignText = "居左显示";
} else if (item.textAlign === "center") {
item.textAlignText = "居中显示";
} else if (item.textAlign === "right") {
item.textAlignText = "居右显示";
} else if (
item.textAlign === "justify" &&
item.name !== "编制单位/日期"
) {
// 调用分散对齐方法
this.disperseAlign(item);
}
},
// 选择单/双分割线事件
radioLine(item) {
if (item.lineType === "one") {
// 隐藏下分割线
item.content[1].display = "none";
// 如果选择了单分割线则重置下分割线和分割线间隔样式
this.downLineReset(item);
this.intervalReset(item);
} else if (item.lineType === "double") {
// 打开下分割线
item.content[1].display = "block";
}
},
// 颜色选择器的change事件
changeColor(item) {
item.content[1].lineColor = item.content[0].lineColor;
},
// 1.上分割线
// inputWidth为v-model的值 lineWidth为最后真实渲染值
// slider
upLineSliderInput(item) {
if (item.content[0].poundOrPixel === "pound") {
item.content[0].lineWidth = item.content[0].inputWidth * 1.33;
} else if (item.content[0].poundOrPixel === "pixel") {
item.content[0].lineWidth = item.content[0].inputWidth;
}
},
// input
upLineWidthInput(e, item) {
if (!e.target.value) {
item.content[0].inputWidth = 0;
}
item.content[0].inputWidth = Number(e.target.value);
if (item.content[0].poundOrPixel === "pixel") {
item.content[0].lineWidth = Number(item.content[0].inputWidth);
} else if (item.content[0].poundOrPixel === "pound") {
item.content[0].lineWidth = Number(item.content[0].inputWidth * 1.33);
}
},
// 切换单位
upLineChange(item) {
if (item.content[0].poundOrPixel === "pixel") {
item.content[0].lineWidth = Number(item.content[0].lineWidth / 1.33);
} else if (item.content[0].poundOrPixel === "pound") {
item.content[0].lineWidth = Number(item.content[0].lineWidth * 1.33);
}
},
// 重置上分割线宽
upLineReset(item) {
item.content[0].lineWidth = 2;
item.content[0].inputWidth = 2;
item.content[0].poundOrPixel = "pixel";
},
// 2.下分割线
// slider
downLineSliderInput(item) {
if (item.content[1].poundOrPixel === "pound") {
item.content[1].lineWidth = item.content[1].inputWidth * 1.33;
} else if (item.content[1].poundOrPixel === "pixel") {
item.content[1].lineWidth = item.content[1].inputWidth;
}
},
// input
downLineWidthInput(e, item) {
if (!e.target.value) {
item.content[1].inputWidth = 0;
}
item.content[1].inputWidth = Number(e.target.value);
if (item.content[1].poundOrPixel === "pixel") {
item.content[1].lineWidth = Number(item.content[1].inputWidth);
} else if (item.content[1].poundOrPixel === "pound") {
item.content[1].lineWidth = Number(item.content[1].inputWidth * 1.33);
}
},
// 切换单位
downLineChange(item) {
if (item.content[1].poundOrPixel === "pixel") {
item.content[1].lineWidth = Number(item.content[1].lineWidth / 1.33);
} else if (item.content[1].poundOrPixel === "pound") {
item.content[1].lineWidth = Number(item.content[1].lineWidth * 1.33);
}
},
// 重置下分割线宽
downLineReset(item) {
item.content[1].lineWidth = 2;
item.content[1].inputWidth = 2;
item.content[1].poundOrPixel = "pixel";
},
// 3.分割线间隔
// slider
intervalLineMarginInput(item) {
if (item.content[0].lineInterval === "pound") {
item.content[0].marginBottom =
item.content[0].interValMarginBottom * 1.33;
} else if (item.content[0].lineInterval === "pixel") {
item.content[0].marginBottom = item.content[0].interValMarginBottom;
}
},
// input
intervalMarginInput(e, item) {
if (!e.target.value) {
item.content[0].marginBottom = 0;
item.content[0].interValMarginBottom = 0;
}
item.content[0].interValMarginBottom = Number(e.target.value);
item.poundOrPixel = item.lineInterval;
if (item.content[0].poundOrPixel === "pixel") {
item.content[0].marginBottom = Number(
item.content[0].interValMarginBottom
);
}
if (item.content[0].poundOrPixel === "pound") {
item.content[0].marginBottom = Number(
item.content[0].interValMarginBottom * 1.33
);
}
},
// 切换单位
intervalChange(item) {
if (item.content[0].lineInterval === "pixel") {
item.content[0].marginBottom = Number(
item.content[0].interValMarginBottom
);
} else if (item.content[0].lineInterval === "pound") {
item.content[0].marginBottom = Number(
item.content[0].interValMarginBottom * 1.33
);
}
},
// 重置分割线间隔
intervalReset(item) {
item.content[0].marginBottom = 5;
item.content[0].interValMarginBottom = 5;
item.content[0].poundOrPixel = "pixel";
item.content[0].lineInterval = "pixel";
},
// 4.占位符高度
// slider
placeholderSlider(item) {
if (item.virtualUnit === "pound") {
item.height = item.virtualHeight * 1.33;
} else if (item.virtualUnit === "pixel") {
item.height = item.virtualHeight;
}
},
// input
placeholderInput(e, item) {
if (!e.target.value || e.target.value < 1) {
item.height = 0;
item.virtualHeight = 0;
}
item.virtualHeight = Number(e.target.value);
item.poundOrPixel = item.virtualUnit;
if (item.virtualUnit === "pixel") {
item.height = Number(item.virtualHeight);
}
if (item.virtualUnit === "pound") {
item.height = Number(item.virtualHeight * 1.33);
}
},
// 切换单位
placeholderChange(item) {
if (item.virtualUnit === "pixel") {
item.height = Number(item.virtualHeight);
} else if (item.virtualUnit === "pound") {
item.height = Number(item.virtualHeight * 1.33);
}
},
// 重置占位符
placeholderReset(item) {
item.height = 20;
item.virtualHeight = 20;
item.virtualUnit = "pixel";
item.poundOrPixel = "pixel";
},
// 添加元素事件
addDom() {
let copyData = null; // 初始化为null或undefined,根据需要选择
this.myArray.forEach((item) => {
if (item.id === this.currentHighlightId) {
// 深拷贝数据
copyData = JSON.parse(JSON.stringify(item));
}
});
// 计算是同类别的第几个
let domNowNumber = 0;
this.myArray.forEach((item) => {
if (item.name.includes(copyData.name)) {
domNowNumber += 1;
}
});
// 赋值id
copyData.id = this.myArray.length;
// 将需要拷贝的数据的text字split一下
let textArr = copyData.text.split(""); // 假如新增了三条分割线(到分割线3了)那得到["分","割","线","3"]
let isNaN = textArr[textArr.length - 1] * 1; // 判断最后得到的该项是否为数字类型
if (isNaN) {
// 去掉最后的数字然后在后边继续追加 += 1 那么得到了"分割线4"的一个text(数据里的text字渲染在中间的drag列操作上,所以需要看到是第几个)
copyData.text = `${copyData.text
.split("")
.slice(0, -1)
.join("")}${domNowNumber}`;
// 新增项的class名称也追加 1 这里为字符串的追加 例如 "分割线1"、"分割线11"、"分割线111"
copyData.class = `${copyData.class}${domNowNumber}`;
} else {
copyData.text = `${copyData.text}${domNowNumber}`;
copyData.class = `${copyData.class}${domNowNumber}`;
}
// 新元素的显示状态为true
copyData.conceal = true;
// 推入渲染数组
this.myArray.push(copyData);
// 如果显示状态是true显示,且对齐方式为分散对齐,且除了name里含"编制单位"的,调用分散对齐
if (
copyData.conceal === true &&
copyData.textAlign === "justify" &&
!copyData.name.includes("编制单位")
) {
this.disperseAlign(copyData);
}
},
},
};
</script>
<style lang="less" scoped>
.report-list-container {
font-family: Microsoft YaHei;
font-weight: 400;
.page-title {
height: 19px;
font-size: 14px;
font-family: Microsoft YaHei;
font-weight: 400;
line-height: 19px;
color: #9ba0ad;
margin-bottom: 20px;
.self {
color: #0c254c;
}
}
.content-mode {
display: flex;
justify-content: center;
height: calc(100% - 40px);
box-sizing: border-box;
background: #ffffff;
.item {
padding: 6px;
box-sizing: border-box;
background-color: #fdfdfd;
border: solid 1px #eee;
margin-bottom: 10px;
cursor: move;
}
.chosen {
border: solid 2px #3089dc !important;
}
.left-preview {
margin-right: 15px;
overflow-y: scroll;
padding: 16px;
width: 640px;
border: 1px solid #ccc;
box-sizing: border-box;
.hr {
width: 100%;
// height: 1px;
}
h1 {
> div {
font-weight: 900;
}
}
/deep/ .son-placeholder {
border: 1px #ccc dashed;
margin-bottom: 10px;
}
}
.left-preview::-webkit-scrollbar {
width: 5px;
height: 5px;
}
.right-edit {
.clear {
color: #0079fe;
cursor: pointer;
}
display: flex;
width: 620px;
.operation::-webkit-scrollbar {
width: 5px;
height: 5px;
}
.operation {
> div > div > div {
> div {
margin-bottom: 10px;
span {
display: inline-block;
width: 90px;
}
}
}
box-sizing: border-box;
overflow: auto;
.oneself-right {
margin-left: auto;
> div {
margin-top: 0;
}
.clear {
display: flex;
margin-right: 20px;
align-items: center;
}
.el-color-picker
.el-color-picker__trigger
.el-icon-arrow-down
.el-color-picker__icon {
font-size: 0;
}
.color-select::after {
border: 1px solid red;
}
}
.upLineWidth {
.size-mark {
/deep/ input {
border-radius: 4px 0 0 4px;
height: 36px;
box-sizing: border-box;
}
}
/deep/ input {
border-radius: 0 4px 4px 0;
height: 36px;
box-sizing: border-box;
}
}
.operating-area {
width: 440px;
padding: 16px;
box-sizing: border-box;
}
.font,
.font-size,
.font-color,
.font-width,
.text-align,
.hrColor {
display: flex;
align-items: center;
span {
font-size: 13px;
}
}
/deep/ .text-align .el-radio-button--small .el-radio-button__inner {
width: 46px;
display: flex;
justify-content: center;
align-items: center;
height: 36px;
padding: 2px;
}
.text-align i::before {
font-size: 28px;
}
.font {
.el-select {
width: 155px;
}
}
.font-size {
.size-mark {
width: 95px;
}
.size-pound {
width: 60px;
.select-pound {
padding-right: 0;
}
}
}
.label {
width: 88px;
color: #aaaaaa;
font-size: 13px;
}
}
.drag {
width: 180px;
font-size: 14px;
overflow-y: scroll;
> span > div {
box-sizing: border-box;
border-width: 1px;
border-style: solid;
border-color: rgba(233, 233, 233, 1);
border-top: 0;
border-bottom: 0;
}
.highlight {
background: inherit;
background-color: rgba(236, 245, 255, 1);
}
}
.drag::-webkit-scrollbar {
width: 5px;
height: 5px;
}
.edit-title {
box-sizing: border-box;
height: 50px;
line-height: 50px;
padding-left: 20px;
background-color: rgba(249, 249, 249, 1);
}
.item {
margin: 0;
height: 50px;
line-height: 50px;
background: none;
border: none;
border-width: 1px;
border-style: solid;
border-color: rgba(233, 233, 233, 1);
border-top: 0;
padding: 2px 2px 2px 40px;
box-sizing: border-box;
}
}
}
}
</style>