update:报告编辑组件改版

This commit is contained in:
2026-02-28 17:39:35 +08:00
parent 2fb5b8cc8f
commit 9fe892c750
7 changed files with 285 additions and 0 deletions

View File

@@ -0,0 +1,285 @@
<template>
<Draggable :list="documentData" :animation="250" item-key="id" handle=".drag-btn">
<template #item="{ element, index }">
<div class="comp-edit-item">
<div class="toper">
<div class="title">
<div v-if="mode === 'edit'" class="drag-btn">
<el-icon :size="20"><DCaret /></el-icon>
</div>
<div class="type-name">
{{ titleMap[element.type] }}{{ element.key ? `-${element.key}` : '' }}
</div>
<div class="danger-tip">
<template v-if="element.type === 'img'">
<el-link
v-if="!element.key"
class="tip-item"
type="danger"
@click="editFun(element)"
>
<el-icon :size="14"><Warning /></el-icon>
未设置图片key
</el-link>
</template>
<template v-if="element.type === 'table'">
<el-link
v-if="!element.key"
class="tip-item"
type="danger"
@click="editFun(element)"
>
<el-icon :size="14"><Warning /></el-icon>
未设置表格key
</el-link>
<el-link
v-if="element.head?.length === 0"
class="tip-item"
type="danger"
@click="editFun(element)"
>
<el-icon :size="14"><Warning /></el-icon>
未设置表头
</el-link>
</template>
</div>
</div>
<div v-if="mode === 'edit'" class="options">
<el-dropdown
v-if="paragraphType.includes(element.type)"
:teleported="false"
trigger="click"
>
<el-link v-if="paragraphType.includes(element.type)" class="item" type="primary">
<el-icon :size="14"><CirclePlus /></el-icon>新增
</el-link>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="addFun('title', index)">标题</el-dropdown-item>
<el-dropdown-item @click="addFun('text', index)">文本</el-dropdown-item>
<el-dropdown-item @click="addFun('form', index)">表单</el-dropdown-item>
<el-dropdown-item @click="addFun('img', index)">图片</el-dropdown-item>
<el-dropdown-item @click="addFun('table', index)">表格</el-dropdown-item>
<el-dropdown-item @click="addFun('section', index)">小节</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown :teleported="false" trigger="click">
<el-link class="item" type="primary">
<el-icon :size="14"><Setting /></el-icon>编辑
</el-link>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="editType.includes(element.type)">
<el-link type="primary" @click="editFun(element)">设置</el-link>
</el-dropdown-item>
<el-dropdown-item>
<el-link type="danger" @click="delFun(index)">删除</el-link>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<div v-if="paragraphType.includes(element.type)" class="content">
<EditItem
v-if="element.children?.length > 0"
v-model:data="element.children"
:mode="mode"
:preview="preview"
/>
</div>
<div v-else class="content">
<CompTitle
v-if="element.type === 'title'"
v-model:value="element.value"
:mode="mode"
:disabled="preview"
/>
<CompText
v-if="element.type === 'text'"
v-model:keyValue="element.key"
v-model:value="element.value"
:mode="mode"
:disabled="preview"
/>
<CompForm
v-if="element.type === 'form'"
v-model:children="element.children"
:mode="mode"
:disabled="preview"
/>
<CompImg
v-if="element.type === 'img'"
:ref="(el) => (CompRefs[element.id] = el)"
v-model:keyValue="element.key"
v-model:value="element.value"
v-model:params="element.params"
:mode="mode"
:disabled="preview"
/>
<CompTable
v-if="element.type === 'table'"
:ref="(el) => (CompRefs[element.id] = el)"
v-model:keyValue="element.key"
v-model:value="element.value"
v-model:params="element.params"
v-model:head="element.head"
:mode="mode"
:disabled="preview"
/>
</div>
</div>
</template>
</Draggable>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import Draggable from 'vuedraggable';
import { CirclePlus, Setting, Warning, DCaret } from '@element-plus/icons-vue';
import CompTitle from './components/title.vue';
import CompText from './components/text.vue';
import CompForm from './components/form.vue';
import CompImg from './components/img.vue';
import CompTable from './components/table.vue';
defineOptions({
name: 'EditItem',
});
interface Props {
data: any;
mode: any;
preview?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
data: [],
mode: '',
preview: false,
});
const CompRefs = ref<any>({});
const documentData = ref<any>(props.data);
const paragraphType = ref(['paragraph', 'section', 'empty']);
const editType = ref(['img', 'table']);
const titleMap = ref<any>({
paragraph: '章节',
section: '小节',
empty: '小节',
title: '标题',
text: '文本',
form: '表单',
img: '图片',
table: '表格',
});
watch(
() => props.data,
(val: any, oldVal) => {
if (JSON.stringify(val) !== JSON.stringify(oldVal)) {
documentData.value = val;
}
},
{ deep: true }
);
const addFun = (type: any, index: number) => {
const data: any = {
id: new Date().getTime(),
type, // 类型
key: '', // 报告生成对应key
value: '', // 内容
params: {}, // 其他参数
children: [], // 子节点
};
documentData.value[index]?.children?.push(data);
};
const editFun = (data: any) => {
CompRefs.value[data.id]?.openFun();
};
const delFun = (index: number) => {
documentData.value.splice(index, 1);
};
</script>
<style lang="scss" scoped>
.comp-edit-item {
position: relative;
width: 100%;
padding: 25px 10px 10px;
margin-bottom: 10px;
border: solid 1px var(--el-border-color);
border-radius: 4px;
&:hover {
> .toper {
> .options {
opacity: 1;
}
}
}
&:last-child {
margin-bottom: 0;
}
.toper {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 20px;
.drag-btn {
display: flex;
align-items: center;
background-color: var(--el-border-color-light);
color: var(--el-text-color-placeholder);
cursor: -webkit-grab;
}
.title {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
height: 20px;
font-size: 12px;
.type-name {
display: flex;
align-items: center;
height: 20px;
color: var(--el-text-color-secondary);
background-color: var(--el-border-color-light);
padding: 0 10px;
border-bottom-right-radius: 4px;
margin-right: 5px;
}
.danger-tip {
display: flex;
align-items: center;
color: var(--el-color-error);
.tip-item {
padding-right: 5px;
font-size: 12px;
}
}
}
.options {
position: absolute;
top: 0;
right: 0;
display: flex;
align-items: center;
height: 20px;
font-size: 12px;
color: var(--el-text-color-secondary);
padding: 0 10px;
opacity: 0;
.item {
font-size: 12px;
margin-left: 10px;
}
}
}
}
</style>