7 changed files with 675 additions and 33 deletions
@ -0,0 +1,627 @@ |
|||
<!-- |
|||
@ 作者: 秦东 |
|||
@ 时间: 2023-09-20 13:00:49 |
|||
@ 备注: 自定义表单组件渲染 |
|||
--> |
|||
<script lang='ts' setup> |
|||
import { computed, watch, inject, onUnmounted } from 'vue' |
|||
import Draggable from 'vuedraggable-es' |
|||
import FormItem from './formItem.vue' |
|||
import ChildTable from './childTable.vue' |
|||
import Tooltips from '@/components/DesignForm/tooltip.vue' |
|||
import FlexBox from './flexBox.vue' |
|||
import { useDesignFormStore } from '@/store/DesignForm/designForm' |
|||
import { FormList,formTableInfo } from '@/api/DesignForm/types' |
|||
import { |
|||
constFormBtnEvent, |
|||
constFormProps |
|||
} from '@/api/DesignForm/utils' |
|||
import { Md5 } from 'ts-md5'; |
|||
import { jsonParseStringify } from '@/utils/DesignForm' |
|||
|
|||
import { AnalysisCss } from '@/components/DesignForm/public/form/calculate/cssInfo.ts' |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
data: FormList[] |
|||
tableinfo:formTableInfo |
|||
numrun?:number |
|||
|
|||
}>(), |
|||
{ |
|||
data: () => { |
|||
return [] |
|||
} |
|||
} |
|||
) |
|||
|
|||
let emits = defineEmits(['optionsValue3Get2']); |
|||
|
|||
const store = useDesignFormStore() as any //自定义表单存储器 |
|||
const formProps = inject(constFormProps, {}) as any |
|||
const type = computed(() => { //动态计算属性 |
|||
return formProps.value.type |
|||
}) |
|||
const state = reactive({ |
|||
clone: true, // 允许clone |
|||
gridAdd: false |
|||
}) |
|||
const dataList = ref<any>(props.data) |
|||
|
|||
watch( |
|||
() => props.data, |
|||
(v: FormList[]) => { |
|||
dataList.value = v |
|||
// console.log("监听表单变化++++++++++>",v) |
|||
}, |
|||
{ |
|||
deep: true |
|||
} |
|||
) |
|||
const activeKey = computed(() => { |
|||
return store.activeKey |
|||
}) |
|||
// 禁止容器嵌套 |
|||
const notNested = (type: string) => { |
|||
// const controlType = ['grid', 'table', 'tabs', 'div', 'flex', 'card'] |
|||
const controlType = ['grid', 'tabs', 'div', 'card'] |
|||
// let kjkdh = controlType.includes(type) |
|||
// console.log("禁止容器嵌套",kjkdh,controlType) |
|||
return controlType.includes(type) |
|||
// return false |
|||
} |
|||
const notNestedTableFlex = (type: string) => { |
|||
const controlType = ['grid', 'table', 'tabs', 'div', 'flex', 'card'] |
|||
// let kjkdh = controlType.includes(type) |
|||
// console.log("禁止容器嵌套",kjkdh,controlType) |
|||
return controlType.includes(type) |
|||
// return false |
|||
} |
|||
/** |
|||
@ 作者: 秦东 |
|||
@ 时间: 2024-06-22 15:03:31 |
|||
@ 功能: 判断不能重复出现的组件 |
|||
*/ |
|||
const determineDuplicates = (type: string) => { |
|||
const controlType = ["founder","founderTime","editTime","owner","deptOrg"] |
|||
// console.log("判断不能重复出现的组件",dataList); |
|||
if(dataList.value.length > 0){ |
|||
let jibuqi = 0; |
|||
dataList.value.forEach((item:any)=>{ |
|||
// console.log("组件循环",item); |
|||
if(controlType.includes(type)){ |
|||
if(type == item.type){ |
|||
jibuqi++ |
|||
} |
|||
} |
|||
|
|||
}) |
|||
// console.log("组件循环",jibuqi); |
|||
if(jibuqi > 1){ |
|||
ElMessage({ |
|||
showClose: true, |
|||
message: '此类控件在表单中只能出现一次!不可重复添加!', |
|||
type: 'error', |
|||
}) |
|||
return true; |
|||
}else{ |
|||
return false |
|||
} |
|||
}else{ |
|||
return false |
|||
} |
|||
// return controlType.includes(type) |
|||
} |
|||
// 删除或复制 |
|||
const click = (action: string, index: number, item?: any) => { |
|||
if (type.value !== 5) { |
|||
return // 非设计模式 |
|||
} |
|||
if (action === 'clone') { |
|||
const key = item.type + new Date().getTime().toString() |
|||
const newItem = jsonParseStringify(item) |
|||
dataList.value.splice(index, 0, Object.assign(newItem, { name: key })) |
|||
} else if (action === 'del') { |
|||
dataList.value.splice(index, 1) |
|||
// 清空右侧栏信息 |
|||
store.setActiveKey('') |
|||
store.setControlAttr({}) |
|||
} else if (action === 'gridAdd') { |
|||
item.columns.push({ |
|||
list: [], |
|||
attr: { span: 12 } |
|||
}) |
|||
} else if (action === 'delGridChild') { |
|||
item.splice(index, 1) |
|||
} |
|||
} |
|||
const draggableAdd = (evt: any) => { |
|||
if (type.value !== 5) { |
|||
return // 非设计模式 |
|||
} |
|||
const newIndex = evt.newIndex |
|||
const key = new Date().getTime().toString() |
|||
const obj: any = dataList.value[newIndex] |
|||
const isNested = evt.target && evt.target.getAttribute('data-type') // 不能嵌套 |
|||
// console.log("设计拖拽事件-----1------->",newIndex) |
|||
// console.log("设计拖拽事件-----2------->",key) |
|||
// console.log("设计拖拽事件-----3------->",obj) |
|||
// console.log("设计拖拽事件-----4------->",isNested) |
|||
// console.log("设计拖拽事件-----4------->",dataList.value) |
|||
if ((isNested === 'not-nested' && notNested(obj.type)) || ((isNested === 'not-table' || isNested === 'not-flex') && notNestedTableFlex(obj.type)) || determineDuplicates(obj.type)) { |
|||
dataList.value.splice(newIndex, 1) |
|||
return |
|||
} |
|||
if (!obj) { |
|||
return |
|||
} |
|||
const label = obj.label || obj.item.label |
|||
delete obj.label |
|||
delete obj.icon |
|||
let objectItem = {} |
|||
// 不需要添加item的项 |
|||
const notNeedItem = [ |
|||
'txt', |
|||
'title', |
|||
'button', |
|||
'table', |
|||
'grid', |
|||
'tabs', |
|||
'flex', |
|||
'div' |
|||
] |
|||
// console.log("非设计模式-->",obj.type) |
|||
if (!notNeedItem.includes(obj.type)) { |
|||
objectItem = { |
|||
item: { |
|||
label: label |
|||
} |
|||
} |
|||
} |
|||
// 不需要name的组件 |
|||
let nameObj = {} |
|||
const notNeedName = [ |
|||
'txt', |
|||
'title', |
|||
'button', |
|||
'grid', |
|||
'tabs', |
|||
'divider', |
|||
'div', |
|||
'card' |
|||
] |
|||
switch(obj.type){ |
|||
case "founder": |
|||
nameObj = { |
|||
name: "creater" |
|||
} |
|||
// console.log("founder--->creater",nameObj) |
|||
break |
|||
case "founderTime": |
|||
nameObj = { |
|||
name: "creater_time" |
|||
} |
|||
// console.log("founderTime-->creater_time",nameObj) |
|||
break |
|||
case "editTime": |
|||
nameObj = { |
|||
name: "edit_time" |
|||
} |
|||
// console.log("editTime--->edit_time",nameObj) |
|||
break |
|||
case "owner": |
|||
nameObj = { |
|||
name: "owner" |
|||
} |
|||
// console.log("owner------>owner",nameObj) |
|||
break |
|||
case "deptOrg": |
|||
nameObj = { |
|||
name:"org" |
|||
} |
|||
// console.log("deptOrg---->org",nameObj) |
|||
break |
|||
default: |
|||
if (!notNeedName.includes(obj.type) && !obj.name) { |
|||
nameObj = { |
|||
name: obj.type + key |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// console.log("设计拖拽事件-----5------->",obj) |
|||
// console.log("设计拖拽事件-----6------->",nameObj) |
|||
// console.log("设计拖拽事件-----7------->",objectItem) |
|||
|
|||
Object.assign(obj, nameObj, objectItem) |
|||
// console.log("设计拖拽事件-----8------->",obj) |
|||
groupClick(obj) |
|||
} |
|||
const getGroupName = (item: any) => { |
|||
if (item.name) { |
|||
return item.name |
|||
} else { |
|||
let md5Object:any = new Md5() |
|||
md5Object.appendAsciiStr(JSON.stringify(item)) |
|||
let md5Str = md5Object.end() |
|||
return md5Str |
|||
} |
|||
} |
|||
// 点击激活当前 |
|||
/** |
|||
* styles:{ |
|||
divStyle:{}, |
|||
labelStyle:{}, |
|||
inputStyle:{} |
|||
} |
|||
*/ |
|||
const groupClick = (item: any, ele?: string) => { |
|||
// 设计模式下才执行 |
|||
if (type.value !== 5) { |
|||
return |
|||
} |
|||
if (ele) { |
|||
item.type = ele |
|||
} |
|||
if(!item.styles){ |
|||
item.styles={ |
|||
divStyle:{}, |
|||
labelStyle:{}, |
|||
inputStyle:{} |
|||
} |
|||
} |
|||
// console.log("点击激活当前--->",item) |
|||
store.setActiveKey(getGroupName(item)) |
|||
store.setControlAttr(item) |
|||
// grid时显示添加列按钮 |
|||
state.gridAdd = item.type === 'grid' |
|||
// state.clone = !notNested(item.type) |
|||
state.clone = !notNestedTableFlex(item.type) |
|||
} |
|||
// 返回栅格宽度 |
|||
const getFormItemStyle = (ele: FormList) => { |
|||
if (ele.config?.span === 0) { |
|||
return { width: 'auto', margin: '0 5px' } |
|||
} |
|||
if (ele.config && ele.config.span) { |
|||
return { width: (ele.config.span / 24) * 100 + '%' } |
|||
} |
|||
if(ele.styles?.divStyle){ |
|||
// console.log("返回栅格宽度3",AnalysisCss(ele.styles.divStyle)) |
|||
return AnalysisCss(ele.styles.divStyle) |
|||
} |
|||
} |
|||
// 根据关联条件显示隐藏当前项 |
|||
const linksShow = (el: FormList, index: number) => { |
|||
|
|||
/* |
|||
|
|||
*/ |
|||
// 当前项设置了关联条件,当关联主体的值等于当前组件设定的值时 |
|||
if (!el.config) { |
|||
return true |
|||
} |
|||
const key = el.config.linkKey |
|||
//liwenxuan 关联选项设置效果实现 |
|||
//console.log("linksShow--------"+index+"--------"+JSON.stringify(el)) |
|||
//仅第一次加载会触发,须将出现在配置条件中的表单项隐藏。 |
|||
|
|||
const value = el.config.linkValue |
|||
const linkResult = el.config.linkResult |
|||
if (key && value && type.value !== 5) { |
|||
const Fn = new Function('$', `return (${value})`) |
|||
// console.log(Fn) |
|||
const pass = Fn(formProps.value.model) |
|||
if (linkResult === 'disabled') { |
|||
// 设置为disabled后返回显示状态 |
|||
dataList.value[index].control.disabled = pass |
|||
return true |
|||
} else { |
|||
return pass |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
// 根据表单设置不显示指定字段 |
|||
const linksIf = (obj: FormList) => { |
|||
const { type } = formProps.value |
|||
const { config: { disabledAdd, disabledEdit, disabledDetail } = {} } = obj |
|||
if (type === 1) { |
|||
if (disabledAdd) { |
|||
// 编辑页 || 新增页 |
|||
return false // 不显示 |
|||
} |
|||
} else if (type === 2) { |
|||
// 编辑 |
|||
if (disabledEdit) { |
|||
return false |
|||
} |
|||
} else if (type === 4 || type === 3) { |
|||
// 查看 |
|||
if (disabledDetail) { |
|||
return false |
|||
} |
|||
} |
|||
// 如果当前字段的name值存在于表单数据的vIf中,则不显示 |
|||
const vIf: string | string[] = formProps.value.hideField |
|||
if (vIf?.length > 0 && obj.name) { |
|||
return vIf.indexOf(obj.name) === -1 // 存在时返回false隐藏 |
|||
} |
|||
return true |
|||
} |
|||
//按钮点击事件 |
|||
const injectBtnEvent:any = inject(constFormBtnEvent) |
|||
const clickBtn = (control: any) => { |
|||
// console.log(control) |
|||
// 0: '提交表单', |
|||
// 1: '重置表单', |
|||
// 2: '取消返回', |
|||
// 3: '无动作(自定义)' |
|||
if (type.value !== 5) { |
|||
// 非设计模式才触发事件 |
|||
injectBtnEvent && injectBtnEvent(control) |
|||
} |
|||
} |
|||
onUnmounted(() => { |
|||
// console.log('formGroup onUnmounted') |
|||
dataList.value = {} |
|||
store.setActiveKey('') |
|||
store.setControlAttr({}) |
|||
}) |
|||
onMounted(()=>{ |
|||
// console.log('formGroup onMounted',props.data,dataList.value,props.tableinfo) |
|||
}) |
|||
|
|||
const getFormItemLableStyle = (ele: any) => { |
|||
if(ele?.labelStyle){ |
|||
// console.log("返回栅格宽度3",AnalysisCss(ele?.labelStyle)) |
|||
return AnalysisCss(ele?.labelStyle) |
|||
} |
|||
} |
|||
|
|||
function optionsValue3Get1(data: any,fieldName: string){ |
|||
emits('optionsValue3Get2',data,fieldName) |
|||
} |
|||
|
|||
</script> |
|||
<template> |
|||
<draggable |
|||
itemKey="id" |
|||
:list="dataList" |
|||
name="fade" |
|||
class="drag" |
|||
v-bind="{ |
|||
group: 'form', |
|||
ghostClass: 'ghost', |
|||
animation: 200, |
|||
handle: '.drag-move', |
|||
disabled: type !== 5 |
|||
}" |
|||
@add="draggableAdd" |
|||
> |
|||
|
|||
<template #item="{ element, index }"> |
|||
|
|||
<div |
|||
class="group" |
|||
:class="{ |
|||
['group-' + element.type]: true, |
|||
active: activeKey === getGroupName(element) |
|||
}" |
|||
:style="getFormItemStyle(element)" |
|||
@click.stop="groupClick(element)" |
|||
v-show="linksShow(element, index)" |
|||
v-if="linksIf(element)" |
|||
> |
|||
<!-- {{element}} --> |
|||
<!--选项卡组件--> |
|||
<template v-if="element.type === 'tabs'"> |
|||
<div class="form-tabs"> |
|||
<el-tabs |
|||
v-bind="element.control" |
|||
:class="[element.config?.className]" |
|||
> |
|||
<el-tab-pane |
|||
v-for="(item, tIndex) in element.columns" |
|||
:label="item.label" |
|||
:key="tIndex" |
|||
> |
|||
<form-group :data="item.list" data-type="not-nested" /> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</div> |
|||
</template> |
|||
<!--标题组件--> |
|||
<template v-else-if="element.type === 'title'"> |
|||
<div |
|||
class="title" |
|||
:class="[element.config.className]" |
|||
v-bind="element.control" |
|||
:style="getFormItemLableStyle(element.styles)" |
|||
> |
|||
<span v-html="element.control.modelValue"></span> |
|||
<Tooltips |
|||
:content="element.config.help" |
|||
v-if="element.config.help" |
|||
/> |
|||
</div> |
|||
</template> |
|||
<!--文字组件--> |
|||
<template v-else-if="element.type === 'txt'"> |
|||
<div |
|||
v-bind="element.control" |
|||
:class="[element.config.className]" |
|||
v-html="element.control.modelValue" |
|||
> |
|||
</div> |
|||
</template> |
|||
<!--表格组件--> |
|||
<template v-else-if="element.type === 'table'"> |
|||
<div class="form-table" v-if="type === 5"> |
|||
<!-- <form-group :data="element.list" data-type="not-nested" /> --> |
|||
<form-group :data="element.list" data-type="not-table" /> |
|||
</div> |
|||
|
|||
<child-table v-else :data="element" /> |
|||
</template> |
|||
<!--格栅布局--> |
|||
<template v-else-if="element.type === 'grid'"> |
|||
<el-row class="form-row" :class="[element.className]"> |
|||
<el-col |
|||
class="form-col" |
|||
:class="{ |
|||
'active-col': activeKey === getGroupName(col), |
|||
[col.className]: col.className |
|||
}" |
|||
v-bind="col.attr" |
|||
v-for="(col, i) in element.columns" |
|||
:key="i" |
|||
@click.stop="groupClick(col, 'gridChild')" |
|||
> |
|||
<form-group :data="col.list" data-type="not-nested" /> |
|||
<div class="drag-control" v-if="type === 5"> |
|||
<div class="item-control"> |
|||
<i |
|||
class="icon-del" |
|||
@click.stop="click('delGridChild', i as number, element.columns)" |
|||
> |
|||
</i> |
|||
</div> |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
</template> |
|||
<!--卡片布局--> |
|||
<template v-else-if="element.type === 'card'"> |
|||
<el-collapse model-value="1"> |
|||
<el-collapse-item :title="element.item.label" name="1"> |
|||
<template #title v-if="element.help"> |
|||
{{ element.item.label }} |
|||
<Tooltips :content="element.help" /> |
|||
</template> |
|||
<form-group :data="element.list" data-type="not-nested" /> |
|||
</el-collapse-item> |
|||
</el-collapse> |
|||
</template> |
|||
<!--分割线--> |
|||
<template v-else-if="element.type === 'divider'"> |
|||
<el-divider v-bind="element.control">{{ element.item && element.item.label }}</el-divider> |
|||
</template> |
|||
<!--div容器--> |
|||
<template v-else-if="element.type === 'div'"> |
|||
<div |
|||
class="div-layout" |
|||
v-bind="element.control" |
|||
:class="{ |
|||
[element.className]: element.className, |
|||
inline: element.config?.inline, |
|||
[element.config?.textAlign]: element.config?.textAlign |
|||
}" |
|||
> |
|||
<form-group :data="element.list" data-type="not-nested" /> |
|||
</div> |
|||
</template> |
|||
<!--弹性布局--> |
|||
<template v-else-if="element.type === 'flex'"> |
|||
<!-- <form-group |
|||
:data="element.list" |
|||
data-type="not-nested" |
|||
v-if="type === 5" |
|||
/> --> |
|||
<form-group |
|||
:data="element.list" |
|||
data-type="not-flex" |
|||
v-if="type === 5" |
|||
/> |
|||
<flex-box :data="element" v-else /> |
|||
<el-button |
|||
style="position: relative; top: -28px; left: 10px" |
|||
v-if="element.config.addBtnText && type === 5" |
|||
size="small" |
|||
> |
|||
{{ element.config.addBtnText }} |
|||
</el-button> |
|||
</template> |
|||
<!--按钮--> |
|||
<template v-else-if="element.type === 'button'"> |
|||
<div |
|||
:class="[element.config?.className]" |
|||
:style="{ 'text-align': element.config?.textAlign }" |
|||
> |
|||
<el-button |
|||
v-bind="element.control" |
|||
@click="clickBtn(element.control)" |
|||
color="#626aef" |
|||
> |
|||
<td :style="getFormItemLableStyle(element.styles)">{{ element.control?.label }}</td> |
|||
</el-button> |
|||
</div> |
|||
|
|||
</template> |
|||
<!--创建人--> |
|||
<FounderForm v-else-if="element.type === 'founder'" :data="element" :tablekey="props.tableinfo" :numrun="props.numrun" /> |
|||
|
|||
<!--创建时间--> |
|||
<FounderTime v-else-if="element.type === 'founderTime'" :data="element" :tablekey="props.tableinfo" :numrun="props.numrun" /> |
|||
|
|||
<!--修改时间--> |
|||
<EditTime v-else-if="element.type === 'editTime'" :data="element" :tablekey="props.tableinfo" :numrun="props.numrun" /> |
|||
|
|||
<!--拥有者--> |
|||
<OwnerPage v-else-if="element.type === 'owner'" :data="element" :tablekey="props.tableinfo" :numrun="props.numrun" /> |
|||
<!--所属部门--> |
|||
<DeptOrgPage v-else-if="element.type === 'deptOrg'" :data="element" :tablekey="props.tableinfo" :numrun="props.numrun" /> |
|||
|
|||
<DigitPage v-else-if="element.type === 'digitpage'" :data="element" /> |
|||
|
|||
|
|||
|
|||
<AssociatedForms v-else-if="element.type === 'associatedForms'" :data="element" :form-props="formProps" :tablekey="props.tableinfo" /> |
|||
|
|||
<LowcodeImage v-else-if="element.type === 'lowcodeImage'" :data="element" /> |
|||
<LowcodeTransfer v-else-if="element.type === 'lowcodeTransfer'" :data="element" /> |
|||
|
|||
<VideoUpAndPlay v-else-if="element.type === 'videoUpAndPlay'" :data="element" /> |
|||
<LowcodeCarsusel v-else-if="element.type === 'lowcodeCarsusel'" :data="element" /> |
|||
<SignatureMap v-else-if="element.type === 'signaturemap'" :data="element" /> |
|||
|
|||
<OrgCentent v-else-if="element.type === 'orgCentent'" :data="element" /> |
|||
<BaiduMap v-else-if="element.type === 'baidumap'" :data="element" /> |
|||
<OrgCitys v-else-if="element.type === 'organization'" :data="element" /> |
|||
<UrlLink v-else-if="element.type === 'urllink'" :data="element" /> |
|||
<SerialNumber v-else-if="element.type === 'serialNumber'" :data="element" :tablekey="props.tableinfo.name" :numrun="props.numrun" /> |
|||
<!--其他组件--> |
|||
<FormItem v-else :data="element" @optionsValue3Get1="optionsValue3Get1"/> |
|||
|
|||
<!--组件设计外功能框架--> |
|||
<template v-if="type === 5"> |
|||
<div class="drag-control"> |
|||
<div class="item-control"> |
|||
<i |
|||
class="icon-plus" |
|||
@click.stop="click('gridAdd', index, element)" |
|||
v-if="state.gridAdd" |
|||
title="添加列" |
|||
></i> |
|||
<i |
|||
class="icon-clone" |
|||
@click.stop="click('clone', index, element)" |
|||
v-if="state.clone" |
|||
title="克隆" |
|||
></i> |
|||
<i class="icon-del" @click.stop="click('del', index)"></i> |
|||
</div> |
|||
<div class="drag-move icon-move"></div> |
|||
</div> |
|||
<div class="tooltip" style="display: black;">{{ element.name }}</div> |
|||
</template> |
|||
|
|||
</div> |
|||
</template> |
|||
</draggable> |
|||
</template> |
|||
<style lang='scss' scoped> |
|||
|
|||
</style> |
|||
|
|||
Loading…
Reference in new issue