数通智联化工云平台
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

2480 lines
105 KiB

<!--
@ 作者: 秦东
@ 时间: 2023-08-28 08:31:58
@ 备注:
-->
<script lang='ts' setup>
import { reactive, computed, toRefs, ref, watch, inject } from 'vue'
import { getRequest } from '@/api/DesignForm'
import { useDesignFormStore } from '@/store/DesignForm/designForm'
import validate from './validate'
import { ElMessage } from 'element-plus'
import { formatNumber } from '@/api/DesignForm/utils'
import { PublicAtrr } from '@/api/DesignForm/types'
import { chineseToPinyin,customerFormGroupList } from '@/api/DesignForm/requestapi'
//数学计算公式编辑器
import MathFormula from '@/components/DesignForm/math/mathFormula.vue'
import { uploadUrl } from '@/api/DesignForm'
import { UploadFilled } from '@element-plus/icons-vue'
import { UploadFile, UploadFiles } from 'element-plus/es/components/upload/src/upload';
import type Node from 'element-plus/es/components/tree/src/model/node'
//样式布局
import LayoutPage from '@/components/DesignForm/layoutPage/index.vue'
import { Plus } from '@element-plus/icons-vue'
const props = withDefaults( //接收父级参数
defineProps<{
formData: any
formList: any
formConfig: any
formOtherData: any
customerformid: number | string,
formInfo: any
}>(),
{
formConfig: () => {
return {}
},
formOtherData: () => {
// 其他不需要保存在formData里的数据
return {}
}
}
)
const mathBoxShow = ref(false)
//注册自定义事件
const emits = defineEmits<{
(e: 'openDialog', data: any): void
(e: 'update:formOtherData', data: any): void
(e: 'videoMsgChange', data: VideoMsg): void
//(e: 'update:formData', data: any): void
//(e: 'update:formConfig', data: any): void
}>()
const { formConfig, formData } = toRefs(props)
const store = useDesignFormStore() as any
const controlData = computed(() => {
console.log("注册自定义事件----------------->",props.formInfo)
// console.log("注册自定义事件--------1--------->",store.controlAttr)
return store.controlAttr
})
//获取组件样式Demo
const controlDataStyls = computed(() => {
return store.controlAttr.styles
})
const dataSourceOption = ref([])
const designType = inject('formDesignType')
const state = reactive({
dataSourceList: [],
customRulesList: [
...validate,
{
type: 'rules',
label: '自定义正则'
},
{
type: 'methods',
label: '自定义方法'
}
], // 自定义校验规则
isSearch: designType === 'search',
tooltip: {
css: '当前表单应用页的样式,类似于.vue文件中的style scoped中的样式',
dict: '数据字典,用于匹配多选组、下拉选择等,提供动态获取Options接口字典数据,一般不设置,从接口dict获取。json格式:"sex":{"0":"男","1":"女"}',
rules:
"可参考UI组件表单校验,<a href='https://element-plus.gitee.io/zh-CN/component/form.html#%E8%A1%A8%E5%8D%95%E6%A0%A1%E9%AA%8C' target='_blank' style='color:red'>详情点击</a>",
props: '可添加当前组件所有prop属性及事件方法'
},
tabsName: 'second',
numberRulesList:[
{
type: 'time',
label: '时间',
msg:'请选择时间格式'
},
{
type: 'text',
label: '自定义字符',
msg:'请输入自定义字符'
},
{
type: 'serialnumber',
label: '流水序号',
msg:'流水号起始值'
},
{
type: 'randomnumber',
label: '随机数',
msg:'请输入随机数位数'
}
]
})
const formAttr = computed(() => {
const isSearch = state.isSearch
return [
{
label: '表单名称',
placeholder: '用于保存的表单名称',
value: props.formOtherData.formName,
key: 'formName',
hide: isSearch
},
{
label: '数据源',
placeholder: '请选择数据源',
value: formatNumber(props.formOtherData.source),
type: 'select',
options: dataSourceOption.value,
key: 'source',
hide: isSearch || !dataSourceOption.value?.length,
clearable: true
},
{
label: '表单标识',
value: formData.value.name,
placeholder: '表单唯一标识,可为空',
key: 'name',
hide: isSearch
}
// {
// label: '表单标签宽度',
// value: formData.value.labelWidth,
// placeholder: '表单label宽,如180px',
// key: 'labelWidth'
// },
// {
// label: '表单样式名称',
// value: formData.value.class,
// placeholder: '额外添加的表单class类名',
// key: 'class',
// type: 'select',
// options: [
// { label: '无样式', value: '',name:'',id:'' },
// { label: '每行两列', value: 'form-row-2',name:'',id:'' },
// { label: '每行三列', value: 'form-row-3',name:'',id:'' },
// { label: '每行四列', value: 'form-row-4',name:'',id:'' }
// ],
// hide: isSearch,
// clearable: true
// },
// {
// label: '字段名后添加冒号',
// value: formData.value.showColon,
// key: 'showColon',
// type: 'switch'
// },
// {
// label: '组件尺寸',
// value: formData.value.size,
// type: 'select',
// key: 'size',
// options: [
// { label: 'large', value: 'large',name:'',id:'' },
// { label: 'default', value: 'default',name:'',id:'' },
// { label: 'small', value: 'small',name:'',id:'' }
// ]
// }
/*{
label: '筛选条件展开/收起',
value: formConfig.value.expand,
type: 'switch',
path: 'config',
key: 'expand',
hide: !isSearch
}*/
]
})
const attrList = computed(() => {
// console.log("controlData.value-1-->",controlData.value)
if (Object.keys(controlData.value).length) {
const {
control = {},
type,
name,
config = {},
item = {},
attr = {},
selectvalue = "default",
mathFormula = {}
}: { control: any;type:any;name:any;item:any;attr:any; config: any,selectvalue:any,mathFormula:any } = controlData.value
// console.log("controlData.value--->",controlData.value)
// console.log("controlData.value-1-->",control,selectvalue)
let columnIndex = false // 是否显示序号列
if (type === 'table') {
// 表格时处理
const list = controlData.value && controlData.value.list
if (list && list.length > 0) {
columnIndex = list[0].type === 'index'
}
}
const temp =reactive<PublicAtrr[]>([
{
label: '自定义Class',
value: config.className,
placeholder: '样式类名',
path: 'config.className'
},
{
label: '字段标识',
value: name,
type:
Object.keys(state.dataSourceList).length > 0 ? 'select' : 'text',
placeholder: '字段唯一标识,对应数据库',
dict: state.dataSourceList,
path: 'name',
vHide: [
'grid',
'tabs',
'card',
'title',
'gridChild',
'tableColumn',
'divider',
'txt',
'div',
'button'
],
eventName: 'filedNameKey'
},
{
label: '占位内容',
value: control.placeholder,
placeholder: 'placeholder',
path: 'control.placeholder',
vShow: [
'password',
'input',
'textarea',
'select',
'date',
'number',
'datePicker',
'tinymce',
'timePicker',
'treeSelect'
]
},
{
label: '按钮类型',
value: control.type,
path: 'control.type',
type: 'select',
dict: {
primary: 'primary',
success: 'success',
info: 'info',
warning: 'warning',
danger: 'danger'
},
vShow: ['button'],
clearable: true
},
{
label: '按钮名称',
value: control.label,
path: 'control.label',
vShow: ['button']
},
{
label: '按钮事件',
value: control.key,
path: 'control.key',
type: 'select',
dict: {
submit: '提交表单',
reset: '重置表单',
cancel: '取消返回',
none: '无动作(自定义)'
},
vShow: ['button']
},
{
label: '对齐方式',
value: config.textAlign,
path: 'config.textAlign',
type: 'select',
dict: {
left: 'left',
right: 'right',
center: 'center'
},
vShow: ['button', 'div']
},
{
label: 'label值',
value: item.label,
path: 'item.label',
vHide: [
'table',
'grid',
'tabs',
'title',
'gridChild',
'div',
'inputSlot',
'flex',
'button',
'txt'
],
eventName: 'labelNameVal'
},
{
label: '隐藏label',
value: item.showLabel,
path: 'item.showLabel',
type: 'switch',
vHide: [
'table',
'grid',
'tabs',
'title',
'gridChild',
'divider',
'card',
'div',
'inputSlot',
'flex',
'button',
'txt'
]
},
{
label: '帮助信息',
value: config.help,
path: 'config.help',
vHide: [
'table',
'grid',
'tabs',
'gridChild',
'divider',
'div',
'inputSlot',
'flex',
'button',
'txt'
]
},
{
label: '表单栅格',
value: config.span,
placeholder: '表单区域栅格宽,0为自动宽',
path: 'config.span',
vHide: ['gridChild'],
isNum: true
},
/*{
label: '行类样式',
value: config.inline,
type: 'switch',
path: 'config.inline',
vShow: ['div']
},*/
{
label: '文本值',
value: control.modelValue,
placeholder: '支持html',
path: 'control.modelValue',
vShow: ['txt'],
inputStyle: 'textarea'
},
{
label: '设为密码',
value: type,
type: 'select',
dict: { input: '文本', password: '密码' },
path: 'type',
vShow: ['input', 'password'],
vIf: state.isSearch // 搜索模式下隐藏 为true
},
{
label: '文本域高度',
value: control.rows,
placeholder: '输入框行数',
path: 'control.rows',
vShow: ['textarea'],
isNum: true
},
{
label: '前缀',
value: config.prepend,
placeholder: '文本前缀',
path: 'config.prepend',
vShow: ['input', 'password']
},
{
label: '后缀',
value: config.append,
placeholder: '文本后缀',
path: 'config.append',
vShow: ['input', 'password']
},
{
label: '状态打开时的值',
value: control.activeValue,
placeholder: '状态打开时的值',
path: 'control.activeValue',
vShow: ['switch'],
isNum: true
},
{
label: '状态关闭时的值',
value: control.inactiveValue,
placeholder: '状态关闭时的值',
path: 'control.inactiveValue',
vShow: ['switch'],
isNum: true
},
// {
// label: '转换格式化值',
// value: config.transform,
// path: 'config.transform',
// type: 'switch',
// vShow: [
// 'checkbox',
// 'select',
// 'switch',
// 'cascader',
// 'slider',
// 'table',
// 'flex',
// 'treeSelect'
// ]
// },
{
label: '增加按钮文案',
value: config.addBtnText,
path: 'config.addBtnText',
type: 'text',
vShow: ['flex', 'table']
},
{
label: '删除按钮文案',
value: config.delBtnText,
path: 'config.delBtnText',
type: 'text',
vShow: ['flex', 'table']
},
{
label: '是否多选',
value: control.multiple,
path: 'control.multiple',
type: 'switch',
vShow: ['select', 'treeSelect'],
eventName: 'selectMultiple'
},
{
label: '可清空',
value: control.clearable,
path: 'control.clearable',
type: 'switch',
vShow: ['select']
},
{
label: '是否禁用',
value: control.disabled,
path: 'control.disabled',
type: 'switch',
vShow: [
'input',
'password',
'textarea',
'radio',
'checkbox',
'select',
'date',
'switch',
'number',
'cascader',
'upload',
'rate',
'tinymce',
'treeSelect'
],
vIf: state.isSearch
},
{
label: '是否禁用编辑',
value: config.editDisabled,
path: 'config.editDisabled',
type: 'switch',
vShow: [
'input',
'password',
'textarea',
'radio',
'checkbox',
'select',
'date',
'switch',
'number',
'cascader',
'upload',
'treeSelect',
'table',
'flex'
],
vIf: state.isSearch
},
{
label: '添加页隐藏',
value: config.disabledAdd,
path: 'config.disabledAdd',
type: 'switch',
vIf: state.isSearch,
vHide: ['inputSlot']
},
{
label: '编辑页隐藏',
value: config.disabledEdit,
path: 'config.disabledEdit',
type: 'switch',
vIf: state.isSearch,
vHide: ['inputSlot']
},
{
label: '详情页隐藏',
value: config.disabledDetail,
path: 'config.disabledDetail',
type: 'switch',
vIf: state.isSearch,
vHide: ['inputSlot']
},
{
label: '设为Input输入框的前/后缀',
value: type === 'inputSlot',
path: '',
type: 'switch',
vShow: ['select', 'inputSlot'],
eventName: 'setInputSlot'
},
{
label: '标题',
value: control.modelValue,
path: 'control.modelValue',
vShow: ['title']
},
{
label: '占据的列数span',
value: attr.span,
path: 'attr.span',
vShow: ['gridChild'],
eventName: 'formatNumber',
isNum: true
},
{
label: '左侧的间隔格数offset',
value: attr.offset,
path: 'attr.offset',
vShow: ['gridChild'],
eventName: 'formatNumber',
isNum: true
},
{
label: '向右移动格数push',
value: attr.push,
path: 'attr.push',
vShow: ['gridChild'],
eventName: 'formatNumber',
isNum: true
},
{
label: '向左移动格数pull',
value: attr.pull,
path: 'attr.pull',
vShow: ['gridChild'],
eventName: 'formatNumber',
isNum: true
},
{
label: '序号列',
value: columnIndex,
type: 'switch',
vShow: ['table'],
eventName: 'tableColumn1'
},
/*{
label: '操作列',
value: columnOperate,
type: 'switch',
vShow: ['table'],
eventName: 'tableColumn2'
},*/
{
label: '组件名',
value: config.componentName,
placeholder: '全局注册的组件名称',
path: 'config.componentName',
vShow: ['component']
},
{
label: '轮播图设置',
value: config.carousel,
path: 'config.carousel',
type: 'carousel',
vIf: state.isSearch,
vShow: ['lowcodeCarsusel']
},
{
label: '上传视频',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_url',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
},
/* {
label: '上传视频封面',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_poster',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
}, */
{
label: '视频宽度(像素)',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_width',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
},
{
label: '视频高度(像素)',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_height',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
},
{
label: '视频自动播放',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_autoPlay',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
},
{
label: '视频循环播放',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_loopPlay',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
},
{
label: '上传地址',
value: control.action,
placeholder: '图片/文件上传地址,可不填有默认值',
path: 'control.action',
vShow: ['upload']
},
{
label: '文件字段名',
value: control.name,
placeholder: '上传的文件字段名,默认file',
path: 'control.name',
vShow: ['upload']
},
{
label: '列表类型',
value: control.listType,
type: 'select',
dict: {
text: 'text',
picture: 'picture',
'picture-card': 'picture-card'
},
path: 'control.listType',
vShow: ['upload']
},
{
label: '提示文字',
value: config.tip,
placeholder: '提示说明文字',
path: 'config.tip',
vShow: ['upload']
},
{
label: '按钮文本',
value: config.btnText,
placeholder: '上传按钮文本',
path: 'config.btnText',
vShow: ['upload']
},
{
label: 'direction',
type: 'select',
dict: { horizontal: 'horizontal', vertical: 'vertical' },
placeholder: '分割线方向,默认horizontal',
value: control.direction,
path: 'control.direction',
vShow: ['divider']
},
{
label: 'border-style',
placeholder: '分隔符样式,默认solid',
value: control.borderStyle,
path: 'control.borderStyle',
vShow: ['divider']
},
{
label: 'content-position',
type: 'select',
dict: { left: 'left', right: 'right', center: 'center' },
value: control.contentPosition,
path: 'control.contentPosition',
vShow: ['divider']
},
{
label: '最小值',
value: control.min,
path: 'control.min',
vShow: ['slider'],
eventName: 'formatNumber',
isNum: true
},
{
label: '最大值',
value: control.max,
path: 'control.max',
vShow: ['rate', 'slider'],
eventName: 'formatNumber',
isNum: true
},
{
label: '步长',
value: control.step,
path: 'control.step',
vShow: ['slider'],
eventName: 'formatNumber',
isNum: true
},
{
label: 'type',
value: control.type,
path: 'control.type',
vShow: ['datePicker'],
type: 'select',
placeholder: '显示类型',
dict: {
year: 'year',
month: 'month',
date: 'date',
datetime: 'datetime',
week: 'week',
datetimerange: 'datetimerange',
daterange: 'daterange',
monthrange: 'monthrange'
}
},
{
label: 'format',
value: control.format,
path: 'control.format',
vShow: ['datePicker', 'timePicker'],
placeholder: '显示在输入框中的格式'
},
{
label: 'color-format',
value: control.colorFormat,
path: 'control.colorFormat',
type: 'select',
placeholder: '写入 v-model 的颜色的格式',
dict: { hsl: 'hsl', hsv: 'hsv', hex: 'hex', rgb: 'rgb' },
vShow: ['colorPicker']
},
{
label: '文本高度',
value: control.height,
path: 'control.height',
placeholder: '文本高度(预览查看效果)',
vShow: ['tinymce']
},
{
label: '文本宽度',
value: control.width,
path: 'control.width',
placeholder: '文本宽度(预览查看效果)',
vShow: ['tinymce']
},
{
label: '图片上传地址',
value: control.imgUrl,
path: 'control.imgUrl',
placeholder: '图片上传地址',
vShow: ['tinymce']
},
{
label: '附件上传地址',
value: control.blobUrl,
path: 'control.blobUrl',
placeholder: '附件上传地址',
vShow: ['tinymce']
},
{
label: '显示模式',
value: config.style,
path: 'config.style',
placeholder: '显示风格(预览查看效果)',
type: 'select',
dict: { default: 'default', simple: 'simple' },
vShow: ['tinymce']
},
{
label: '默认值',
value: control.mathsValue,
selectvalue: selectvalue,
path: 'control.mathsValue',
placeholder: '显示风格(预览查看效果)',
type: 'digitpage',
dict: { default: '自定义', simple: '编辑公式' },
vShow: ['digitpage'],
control:control
}
])
// 过滤显示对应的值
return temp.filter((item: any) => {
let hasFilter = true
if (item.vShow) {
hasFilter = item.vShow.includes(type)
}
if (item.vHide) {
hasFilter = !item.vHide.includes(type)
}
if (item.vIf) {
// 不显示vif=true的
hasFilter = false
}
return hasFilter
})
} else {
return []
}
})
watch(
() => store.activeKey,
(val: string) => {
if (val) {
// 有值时自动跳到第一项
state.tabsName = 'first'
cssIsShouw(attrList)
}
}
)
const fileSignAry = reactive<any>([])
const controlChange = (obj: any, val: any) => {
// console.log("字段编辑--1--》",obj)
// console.log("字段编辑--2--》",obj.eventName)
// console.log("字段编辑--3--》",val)
// console.log("字段编辑--4--》",controlData.value)
// console.log("字段编辑--5--》",obj.path)
// select多选属性,
switch (obj.eventName) {
case 'selectMultiple':
if (val) {
// 多选,将值改为数组
controlData.value.control.modelValue = []
} else {
// 单选
controlData.value.control.modelValue = ''
}
break
case 'tableColumn1':
tableColumnAdd(val)
break
case 'formatNumber':
val = parseInt(val) // 将值转数值
break
case 'filedNameKey':
// 选择字段标识时,同时修改显示标题
// 根据value找key
if (obj.type === 'select') {
state.dataSourceList.forEach((item: any) => {
if (item.name === val) {
if (controlData.value.item) {
controlData.value.item.label = item.label
}
controlData.value.name = item.label
}
})
}
break
case 'setInputSlot':
if (val) {
// 将类型改为inputSlot
controlData.value.type = 'inputSlot'
ElMessage.success(
`请在对应的Input输入框属性前后缀设置key:${controlData.value.name}`
)
} else {
controlData.value.type = 'select'
}
// 这里会报错Cannot set properties of null (setting 'checked')
// 因value:type===inputSlot,这里使用了v-model,影响不大暂不处理
break
case 'labelNameVal':
if(val != ""){
chineseToPinyin({title:val,types:8,connector:"",formJson:JSON.stringify(props.formInfo)})
.then((data:any)=>{
if(data.code == 0){
if(data.data != ""){
if(!fileSignAry.includes(data.data)){
controlData.value.name = data.data
fileSignAry.push(data.data)
}else{
let isEnd = true;
do{
let titleVal = data.data + Rand(10000000,99999999)
if(!fileSignAry.includes(titleVal)){
controlData.value.name = titleVal;
fileSignAry.push(titleVal);
isEnd=false;
}
}while(isEnd);
}
}else{
if(!fileSignAry.includes(val)){
controlData.value.name = val
fileSignAry.push(val)
}else{
let titleVal = val + Rand(10000000,99999999)
if(!fileSignAry.includes(titleVal)){
controlData.value.name = titleVal;
fileSignAry.push(titleVal);
}
}
}
}
})
}
// console.log("字段编辑--fileSignAry--》",fileSignAry)
break
}
if (obj.path) {
const newVal = obj.isNum ? formatNumber(val) : val // 类型为数字时转整数
obj.path && getPropByPath(controlData.value, obj.path, newVal)
}
}
/**
* 获取非负整数随机数
* @param Min 最小整数
* @param Max 最大整数
* @returns 随机数
*/
const Rand = (Min: number, Max: number) => {
switch (Min) {
case 0: return Math.round(Math.random() * Max);
case 1: return Math.ceil(Math.random() * Max);
default: return Math.round(Math.random() * (Max - Min) + Min);
}
}
// 修改指定路径下的值
const getPropByPath = (obj: any, path: string, val: any) => {
let tempObj = obj
const keyArr = path.split('.')
let i = 0
for (i; i < keyArr.length - 1; i++) {
const key = keyArr[i]
if (key in tempObj) {
tempObj = tempObj[key]
} else {
throw new Error(`${key} is undefined`)
// break
}
}
const key = keyArr[i]
const value = tempObj[keyArr[i]]
// 检查最后一级是否存在
/*if (!(key in tempObj)) {
throw new Error(`${key} is undefined`)
}*/
if (val !== undefined) {
tempObj[key] = val
}
return {
obj: tempObj,
key: key,
value: value
}
}
// 属性设置相关结束
// 多选固定选项删除
const delSelectOption = (index: number, type?: string) => {
if (type === 'tabs') {
controlData.value.columns.splice(index, 1)
} else {
controlData.value.options.splice(index, 1)
}
}
// 多选固定选项增加
const addSelectOption = (type: any) => {
if (controlData.value.type === 'cascader') {
// 级联时打开弹窗口
openAttrDialog('cascader')
} else if (controlData.value.type === 'treeSelect') {
openAttrDialog('treeSelect', '编辑组件下拉选项数据')
} else {
if (type === 'tabs') {
controlData.value.columns.push({
label: '标签名称',
list: []
})
} else {
controlData.value.options.push({
label: '',
value: ''
})
}
}
}
// 更多属性弹窗
const openAttrDialog = (type?: string, tooltip?: string) => {
let editData = controlData.value.control
if (controlData.value.type === 'button') {
// 按钮组件编辑属性
editData = controlData.value.config
type = 'button'
}
switch (type) {
case 'treeSelect':
editData = controlData.value.control.data
break
case 'cascader':
editData = controlData.value.options
break
case 'optionsParams': // 选项请求附加参数
editData = controlData.value.config.beforeRequest
// params.codeType = 'json'
break
case 'optionsResult':
editData = controlData.value.config.afterResponse
break
}
const emitsParams = {
content: editData,
title: tooltip,
type: type,
direction: 'ltr',
callback: (result: any) => {
switch (type) {
case 'treeSelect':
controlData.value.control.data = result
break
case 'cascader':
controlData.value.options = result
break
case 'optionsParams':
controlData.value.config.beforeRequest = result
break
case 'optionsResult':
controlData.value.config.afterResponse = result
break
case 'button':
controlData.value.config = result
break
default:
controlData.value.control = {}
Object.assign(controlData.value.control, result)
}
}
}
emits('openDialog', emitsParams)
}
// 必填校验
const requiredChange = (val: any) => {
if (!controlData.value.item?.rules) {
controlData.value.item.rules = []
}
if (val) {
controlData.value.item.rules.push({
required: true,
message: '必填项',
trigger: 'change'
})
} else {
controlData.value.item.rules.splice(0, 1)
}
}
// 添加校验规则
const addRules = (tooltip: string) => {
if (!controlData.value.item?.rules) {
controlData.value.item.rules = []
}
const params = {
content: controlData.value.item?.rules,
title: tooltip,
direction: 'ltr',
callback: (result: any) => {
Object.assign(controlData.value.item.rules, result)
}
}
emits('openDialog', params)
}
// 根据不同类型判断是否显示当前属性
const showHide = (type: string[], show?: boolean) => {
// show=true 条件成立显示,false符合条件隐藏
if (
(type && type.length === 0) ||
Object.keys(controlData.value).length === 0
) {
return false
}
const index = type.indexOf(controlData.value.type)
return show ? index !== -1 : index === -1
}
// 子表时添加序号和操作列
const tableColumnAdd = (val: string) => {
const item = {
name: 'index',
type: 'index',
item: {
label: '序号'
},
control: {},
config: {}
}
if (val) {
controlData.value.list.unshift(item)
} else {
controlData.value.list.splice(0, 1)
}
}
// 校验规则必填勾选设置,存在校验规则时勾选
const checkboxRequired = computed(() => {
const val = controlData.value && controlData.value.item?.rules
return val && val.length > 0
})
// 快速添加一条校验规则
const addRulesFast = () => {
if (!controlData.value.customRules) {
controlData.value.customRules = []
}
controlData.value.customRules.push({
type: 'required',
message: '必填项',
trigger: 'blur'
})
}
// 删除一条校验规则
const delAddRules = (index: number) => {
controlData.value.customRules &&
controlData.value.customRules.splice(index, 1)
}
//添加一条编码规则
const addNumRules = () => {
if (!controlData.value.config.customRules) {
controlData.value.config.customRules = []
}
controlData.value.config.customRules.push({
type:'time',
rule:"YYYYMMDD"
})
}
//删除一条编码规则
const delNumRules = (index: number) => {
controlData.value.config.customRules &&
controlData.value.config.customRules.splice(index, 1)
}
// 编辑表单样式
const editFormStyle = (tooltip: string) => {
emits('openDialog', {
codeType: 'css',
direction: 'ltr',
type: 'css',
title: tooltip
})
}
const editFormDict = (tooltip: string) => {
emits('openDialog', {
type: 'dict',
direction: 'ltr',
codeType: 'json',
title: tooltip
})
}
// 根据选定数据源获取表单字段
const getFormFieldBySource = (id?: string) => {
if (state.isSearch) {
// 搜索设计这里不需要数据源
return
}
const source = id
if (source) {
getRequest('sourceById', { id: source })
.then(res => {
// console.log(res)
const tableData = res.data.result?.tableData
if (tableData && tableData.length) {
state.dataSourceList = tableData.filter(
(item: any) => item.enterable
)
}
})
.catch(res => {
// console.log(res)
})
}
}
const getDataSource = () => {
// 获取数据源,表单设计才加载,搜索设置不需要
if (!state.isSearch) {
// getRequest('sourceList').then((res: any) => {
// dataSourceOption.value = res.data.list
// //dataSourceOption.value.unshift({ name: '无', id: '' })
// })
}
}
// 表单属性修改
const formAttrChange = (obj: any, val?: any) => {
// console.log("表单属性修改--->",obj,val,{ [obj.key]: obj.value })
if (obj.key === 'source') {
getFormFieldBySource(obj.value) // 改变了数据源了,重新请求数据
// 清空设计区已选择的组件,再一次选择时字段标识才会变
store.setActiveKey('')
store.setControlAttr({})
}
if (['formName', 'source'].includes(obj.key)) {
emits(
'update:formOtherData',
Object.assign(props.formOtherData, { [obj.key]: obj.value })
)
formData.value[obj.key] = obj.value
return
}
if (obj.path === 'config') {
formConfig.value[obj.key] = obj.value || val
} else {
formData.value[obj.key] = obj.value
}
}
// 返回选项配置提示
const getOptionPlaceholder = (type: number) => {
switch (type) {
case 1:
return '数据源接口URL,可带参数'
case 2:
return '字典key,默认为字段标识'
}
return ''
}
// 快速添加校验规则改变时,填写默认的校验提示信息
const rulesSelectChange = (item: any, val: string) => {
const filter = validate.filter(item => item.type === val)
if (filter && filter.length) {
item.message = filter[0].message
}
}
const rulesSelectNumChange = (item: any, val: string) => {
// console.log("快速添加校验规则改变时",item,val)
const filter = state.numberRulesList.filter(item => item.type === val)
if (filter && filter.length) {
item.message = filter[0].msg
if(val == "time") {
item.rule = "YYYYMMDD"
}else{
item.rule = ""
}
}
}
// options动态选项数据源请求时
const optionsEvent = (type: string, tooltip?: string) => {
openAttrDialog(type, tooltip)
}
const eventClick = (type: string, tooltip?: string) => {
emits('openDialog', { type: type, title: tooltip, direction: 'ltr' })
}
getDataSource()
defineExpose({ getFormFieldBySource })
//判断输入框是否可写
const isNotWrite = (val:any) =>{
// console.log("判断输入框是否可写--->",val)
if(val.key === "name" && props.customerformid != ""){
return true
}
return false
}
const unitInfo = ref<any>()
const formListmap = ref<any>()
const subUnit = ref<any>()
//默认值处理
const digitPageSub = (val:any,envt:any) => {
// console.log("默认值处理--->",val.value,envt,props.formData)
// console.log("默认值处理-1-->",props.formList)
// console.log("默认值处理-2-->",props.formConfig)
// console.log("默认值处理--3->",props.formOtherData)
// console.log("默认值处理--4->",props.customerformid)
// console.log("默认值处理--5->",controlData.value)
// console.log("默认值处理-6-->",val)
subUnit.value = val
controlData.value.selectvalue = envt
formListmap.value = props.formList
if(envt == "simple"){
unitInfo.value = attrList.value
mathBoxShow.value = true
}
}
const openMathDialog = (val:any) => {
subUnit.value = val
formListmap.value = props.formList
unitInfo.value = attrList.value
mathBoxShow.value = true
}
//返回值处理
const updataDigit = (key:sring,val:any) => {
// console.log("默认值处理-1-->",props.formList)
// console.log("默认值处理-2-->",props.formConfig)
// console.log("默认值处理--3->",props.formOtherData)
console.log("默认值处理--4->",val)
formConfig.value[key] = {
mathsFormula:val.mathsFormula,
takingMethod:val.takingMethod,
digit:val.digit
}
console.log("返回值处理--->",formConfig.value)
attrList.value.forEach((item:any)=>{
// console.log("返回值处理--1->",item)
if(item.type == "digitpage"){
item.control.mathFormula = {
formulaHtml:val.formulaHtml,
mathsString:val.mathsString,
mathsFormula:val.mathsFormula,
takingMethod:val.takingMethod,
digit:val.digit
}
}
})
// console.log("默认值处理-5-->",props.formConfig)
}
//liwenxuan 20240108 vidioupload start
import { VideoMsg } from '@/api/DesignForm/types'
import { genFileId } from 'element-plus'
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus'
//更改已上传的视频:el-upload中设置 limit 和 on-exceed 可以在选中时自动替换上一个文件。
const upload = ref<UploadInstance>()
const handleExceed: UploadProps['onExceed'] = (files) => {
upload.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
upload.value!.handleStart(file)
}
//每个视频上传控件对应一个videoMsg[videoIndex]
const videoIndex = ref(0);
//上传成功钩子
function videoUploadOk(response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) {
const { control = {} } : { control: any } = controlData.value
control.videoMsg[videoIndex.value].url = response.data.url;
control.videoMsg[videoIndex.value].videoReady = true;
control.videoMsg[videoIndex.value].CreatedAt = response.data.CreatedAt;
control.videoMsg[videoIndex.value].UpdatedAt = response.data.UpdatedAt;
control.videoMsg[videoIndex.value].fileSize = response.data.fileSize;
control.videoMsg[videoIndex.value].id = response.data.id;
control.videoMsg[videoIndex.value].key = response.data.key;
control.videoMsg[videoIndex.value].name = response.data.name;
control.videoMsg[videoIndex.value].physicspath = response.data.physicspath;
control.videoMsg[videoIndex.value].size = response.data.size;
control.videoMsg[videoIndex.value].tag = response.data.tag;
control.videoMsg[videoIndex.value].type = response.data.type;
}
//删除钩子 禁止删除
function beforeRemove(){
return false;
}
//上传失败钩子
function videoUploadErr(error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles){
alert("上传失败,请重试")
console.log("上传失败"+error);
}
//liwenxuan 20240108 vidioupload end
//liwenxuan 20240111 carousel start
import { Delete,Plus } from '@element-plus/icons-vue'
import { CarsuselConfig } from '@/api/DesignForm/types'
import errimg from '@/assets/404_images/untilUploadImg.png'
import { v4 as uuidv4 } from "uuid";
//轮播图设置弹窗显示控制
const dialogTableVisible = ref(false)
//图片展示显示控制
const showImagePreview = ref(false)
//最近点击的上传按钮对应的imgId
let currentUploadImgid = "";
//刚拖拽视频组件时,初始化imgid监听designForm.activeKey,变化时,如果controlData.value.type=='lowcodeCarsusel',就遍历轮播图数组把imgid=''的都生成uuid
watch(()=>store.activeKey, () => {
if(controlData.value.type==='lowcodeCarsusel'){
const carsuselConfigData:CarsuselConfig[] = controlData.value.control.carsuselConfigArr
carsuselConfigData.forEach(element => {
//console.log(element)
if(element.imgId==''){
//console.log("初始化imgid")
let onlyNumber = uuidv4().replaceAll('-','').toString(); //获取唯一编码
element.imgId = onlyNumber;
}
});
}
})
//轮播图图片上传成功钩子
function carouselImgUploadSuccess(response: any, uploadFile: UploadFile, uploadFiles: UploadFiles){
const carsuselConfigData:CarsuselConfig[] = controlData.value.control.carsuselConfigArr
carsuselConfigData.forEach(element => {
if(element.imgId==currentUploadImgid){
element.imgUrl=response.data.url
element.uploadFlag=true
}
});
}
//轮播图所有已上传的图片的url,点击略缩图展示大图用
const carouselImgUrlList = computed(() => {
const list:string[] = [];
const carsuselConfigData:CarsuselConfig[] = store.controlAttr.control.carsuselConfigArr
carsuselConfigData.forEach(element => {
if(element.uploadFlag===true){
list.push(element.imgUrl)
}
});
return list;
})
//轮播图设置table
function createRow(clickedRow:any){
const newRow:CarsuselConfig = {
uploadFlag:false,
imgId:'',
imgSort:1,
imgUrl: '',
link: ''
}
let onlyNumber = uuidv4().replaceAll('-','').toString(); //获取唯一编码
newRow.imgId = onlyNumber;
const nextSort:number = clickedRow.sort+1;
newRow.imgSort = nextSort;
controlData.value.control.carsuselConfigArr.push(newRow)
}
function deleteRow(clickedRow:any){
//alert('删除一行')
const carsuselConfigData:CarsuselConfig[] = controlData.value.control.carsuselConfigArr
for (var i = 0; i < carsuselConfigData.length; i++) {
if(carsuselConfigData[i].imgId===clickedRow.imgId){
carsuselConfigData.splice(i, 1);
i--; // 如果不减,将漏掉一个元素
}
}
}
//关闭大图展示
const closePreview = () => {
showImagePreview.value = false
}
//开启大图展示
const handlePreview = (index: number, row: any) => {
showImagePreview.value = true
}
const changeCurrentUploadImgid = (clickedRow:any) => {
currentUploadImgid = clickedRow.imgId
}
const optionsCss = [
{ label: '无样式', value: '' },
{ label: '每行两列', value: 'form-row-2' },
{ label: '每行三列', value: 'form-row-3' },
{ label: '每行四列', value: 'form-row-4' }
]
const layouytStyle = reactive<any>(controlData.value.styles)
watch(()=>layouytStyle,(val : any)=>{
console.log("监听样式处理",val)
controlData.value.styles = val
// Object.assign(store.controlAttr.styles.divStyle, val)
},
{
deep: true
});
const setStylefield = ref<string>("divStyle")
//liwenxuan 20240111 carousel end
const activeFormName = ref("0")
/**
@ 作者: 秦东
@ 时间: 2024-03-01 08:16:45
@ 功能: 判断是否是组件
*/
const cssIsShouw = (val:any) => {
if(val.length && val.length > 0){
return true;
}
return false;
}
/**
@ 作者: 秦东
@ 时间: 2024-03-18 11:42:52
@ 功能: 上传图标相关
*/
const imgUploadApiUrl = import.meta.env.VITE_APP_BASE_API+"/api/upordown" //上传地址
const handleAvatarSuccess: UploadProps['onSuccess'] = (
response,
uploadFile
) => {
// imageUrl.value = URL.createObjectURL(uploadFile.raw!)
formConfig.value.imageUrl = response.data.url
}
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
console.log("beforeAvatarUpload",rawFile.type)
if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/jpg' && rawFile.type !== 'image/png' && rawFile.type !== 'image/gif' && rawFile.type !== 'image/icon') {
ElMessage.error('请上传以下格式的图片(jpg、jpeg、png、gif、icon)!'+rawFile.type)
return false
} else if (rawFile.size / 1024 / 1024 > 2) {
ElMessage.error('图片大小不要大于 2MB!')
return false
}
return true
}
const page = ref<number>(1)
const pageSize = ref<number>(200000)
const loadingmore = ref(false)
const loadingnomore = ref(false)
const formGroup = ref<any[]>([])
/**
@ 作者: 秦东
@ 时间: 2024-03-19 08:38:27
@ 功能: 获取表单分组
*/
const gainFormGroupList = () =>{
loadingmore.value = true;
let sendInfo = {
page:page.value,
pagesize:pageSize.value,
state:1
}
customerFormGroupList(sendInfo)
.then((data)=>{
console.log("获取表单分组",data)
formGroup.value = data.data
})
.finally(()=>{
loadingmore.value = false;
})
}
onMounted(() => {
gainFormGroupList()
})
/**
@ 作者: 秦东
@ 时间: 2024-03-19 09:17:51
@ 功能: 控制下拉列表触底加载
*/
const handleVisibleChange = (visible: any) => {
if (visible) {
// 如果下拉框显示
let selectDropdown: any = document.querySelector('.el-select-dropdown__wrap')
// 监听下拉框滚动事件
selectDropdown.addEventListener('scroll', loadNextPage)
} else {
// 如果下拉框隐藏
let selectDropdown: any = document.querySelector('.el-select-dropdown__wrap')
// 移除下拉框滚动事件监听
selectDropdown.removeEventListener('scroll', loadNextPage)
}
}
const loadNextPage = () => {
let selectDropdown: any = document.querySelector('.el-select-dropdown__wrap');
// 判断下拉框是否触底
if (selectDropdown.scrollTop + selectDropdown.clientHeight >= selectDropdown.scrollHeight - 2) {
loadingnomore.value = false
loadingmore.value = true
if (selectoptions.value.length <= 20) {
setTimeout(() => {
selectoptions.value.push({
label: String(selectoptions.value.length),
value: String(selectoptions.value.length),
}
)
loadingmore.value = false
}, 2000)
}else{
loadingmore.value = false
selectDropdown.removeEventListener('scroll', loadNextPage)
loadingnomore.value = true
}
} else {
loadingnomore.value = true
}
}
</script>
<template>
<div class="sidebar-tools">
<el-tabs v-model="state.tabsName">
<el-tab-pane label="字段配置" name="first">
<el-form size="small" class="form">
<!-- <div class=""><h3>通用属性</h3></div> -->
<el-divider content-position="left">通用属性</el-divider>
<template v-for="(item, index) in attrList" :key="index">
<el-form-item :label="item.label" class="form_cont">
<el-select
v-if="item.type === 'select'"
:placeholder="item.placeholder"
v-model="item.value"
:filterable="item.path === 'name'"
:allow-create="item.path === 'name'"
:clearable="item.clearable"
@change="controlChange(item, $event)"
>
<el-option
v-for="(opt, key) in item.dict"
:key="key"
:value="item.path === 'name' ? opt.name : key"
:label="
item.path === 'name' ? `${opt.label}(${opt.name})` : opt
"
/>
</el-select>
<el-switch
v-else-if="item.type === 'switch'"
v-model="item.value"
@change="controlChange(item, $event)"
/>
<template v-else-if="item.type === 'digitpage'">
<el-row>
<el-col :span="12">
<el-select
v-model="item.selectvalue"
:placeholder="item.placeholder"
:filterable="item.path === 'name'"
:allow-create="item.path === 'name'"
:clearable="item.clearable"
@change="digitPageSub(item, $event)"
>
<el-option
v-for="(opt, key) in item.dict"
:key="key"
:value="item.path === 'name' ? opt.name : key"
:label="
item.path === 'name' ? `${opt.label}(${opt.name})` : opt
"
/>
</el-select>
</el-col>
<el-col :span="12">
<el-input-number
v-if="item.selectvalue == 'default'"
v-model="item.value"
class="mx-4"
/>
</el-col>
</el-row>
<div v-if="item.selectvalue == 'simple'" style="width:100%;cursor: pointer; margin-top:10px;" @click="openMathDialog(item)" v-html="item.control.mathFormula.formulaHtml"></div>
<MathFormula v-model:show="mathBoxShow" :sub-unit="subUnit" :unit-info="attrList" :form-listmap="formListmap" @updata-digit="updataDigit" />
</template>
<el-row v-else-if="item.type === 'uploadvideo_url'">
<!-- {{ controlData.control }} -->
<el-upload
:action="uploadUrl" :before-remove="beforeRemove"
:on-success="videoUploadOk" :show-file-list="true"
:on-error="videoUploadErr"
:limit="1"
:on-exceed="handleExceed"
accept=".mp4,.MOV,.WMV,.FLV,.AVI,.AVCHD,.WebM,.MKV,.rmvb">
<el-button v-if="!controlData.control.videoMsg[videoIndex].videoReady" type="primary">点此上传</el-button>
<el-button v-if="controlData.control.videoMsg[videoIndex].videoReady" type="primary">已上传,点击修改</el-button>
</el-upload>
<!-- <el-button v-if="controlData.control.videoMsg[videoIndex].videoReady" type="primary">已上传,点击修改</el-button>
v-if="!controlData.control.videoMsg[videoIndex].videoReady"-->
</el-row>
<!-- <el-row v-else-if="item.type === 'uploadvideo_poster'">
<el-upload
v-if="controlData.control.videoMsg[videoIndex].videoReady&&controlData.control.videoMsg[videoIndex].poster===''"
:action="uploadUrl" :before-remove="beforeRemove"
:on-success="videoUploadOk" :show-file-list="true"
:on-error="videoUploadErr"
accept=".mp4,.MOV,.WMV,.FLV,.AVI,.AVCHD,.WebM,.MKV,.rmvb">
<el-tooltip
class="box-item"
content="不上传则视频封面默认为视频第一帧"
placement="top-end"
>
<el-button type="primary">点此上传</el-button>
</el-tooltip>
</el-upload>
<el-button v-if="!controlData.control[videoIndex].videoReady" type="warning">请先上传视频</el-button>
<el-button v-if="controlData.control.videoMsg[videoIndex].videoReady&&controlData.control.videoMsg[videoIndex].poster!=''" type="primary">已上传,点击修改</el-button>
</el-row> -->
<el-row v-else-if="item.type === 'uploadvideo_autoPlay'">
<el-switch v-model="controlData.control.videoMsg[videoIndex].videoAutoPlay" />
</el-row>
<el-row v-else-if="item.type === 'uploadvideo_loopPlay'">
<el-switch v-model="controlData.control.videoMsg[videoIndex].loop" />
</el-row>
<el-row v-else-if="item.type === 'uploadvideo_width'">
<el-input-number v-model="controlData.control.videoMsg[videoIndex].videoWidth" :step="50" :max="4096"/>
</el-row>
<el-row v-else-if="item.type === 'uploadvideo_height'">
<el-input-number v-model="controlData.control.videoMsg[videoIndex].videoHeight" :step="50" :max="2160"/>
</el-row>
<el-row v-else-if="item.type === 'carousel'">
<el-button type="primary" append-to-body="true" modal="true" @click="dialogTableVisible = true" >轮播图设置</el-button>
</el-row>
<el-input
v-else
:type="item.inputStyle"
v-model="item.value"
:placeholder="item.placeholder"
@input="controlChange(item, $event)"
/>
</el-form-item>
</template>
<template v-if="controlData.config">
<el-form-item label="联动条件" class="form_cont">
<el-switch v-model="controlData.config.linkKey" />
</el-form-item>
<template v-if="controlData.config.linkKey">
<el-form-item class="form_cont">
<el-input
v-model="controlData.config.linkValue"
type="textarea"
placeholder="表达式如: $.input>1 $表示为当前表单数据,input为字段标识"
/>
</el-form-item>
<el-form-item
class="form_cont"
v-if="
showHide(
[
'input',
'textarea',
'radio',
'checkbox',
'select',
'date',
'switch',
'number',
'cascader',
'slider',
'datePicker',
'timePicker',
'colorPicker',
'inputNumber',
'rate',
'treeSelect'
],
true
)
"
label="联动结果"
>
<el-radio-group
v-model="controlData.config.linkResult"
class="option-radio"
>
<el-radio label="hidden">隐藏(默认)</el-radio>
<el-radio label="disabled">禁用</el-radio>
</el-radio-group>
</el-form-item>
</template>
</template>
<template v-if="showHide(['tabs'], true)">
<!-- <div class="h3"><h3>标签配置项</h3></div> -->
<el-divider content-position="left">标签配置项</el-divider>
<el-form-item
class="form_cont"
v-for="(item, index) in controlData.columns"
:key="index"
>
<el-col :span="12">
<el-input v-model="item.label" placeholder="标签配置项" />
</el-col>
<el-col :span="2" :offset="1">
<i
class="icon-del"
@click="delSelectOption(index as number, 'tabs')"
></i>
</el-col>
</el-form-item>
<el-form-item class="form_cont">
<el-button @click="addSelectOption('tabs')">增加标签</el-button>
</el-form-item>
</template>
<!--编号规则设置--->
<div
v-if="showHide(['serialNumber'],true)"
>
<!-- <div class="h3"><h3>编码规则配置</h3></div> -->
<el-divider content-position="left">编码规则配置</el-divider>
<el-form-item label="编码方式" class="form_cont">
<el-switch v-model="controlData.config.automatic" inline-prompt active-text="自动编码" inactive-text="手动编码" style="--el-switch-on-color: #13ce66; --el-switch-off-color: #E6A23C" />
</el-form-item>
<div v-if="controlData.config.automatic">
<el-form-item v-for="(item, index) in controlData.config.customRules" :key="index" class="form_cont">
<el-input v-model="item.rule" :placeholder="item.message?item.message:'请输入自定义字符'">
<template #prepend>
<el-select
v-model="item.type"
style="width: 100px"
@change="rulesSelectNumChange(item, $event)"
>
<el-option
v-for="list in state.numberRulesList"
:key="list.type"
:label="list.label"
:value="list.type"
/>
</el-select>
</template>
<template #append>
<i
class="icon-del"
@click="delNumRules(index as number)"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item class="form_cont">
<el-button @click="addNumRules">添加规则</el-button>
</el-form-item>
</div>
</div>
<div
v-if="
showHide(
[
'radio',
'select',
'checkbox',
'cascader',
'inputSlot',
'treeSelect'
],
true
)
"
>
<!-- <div class="h3"><h3>选项配置</h3></div> -->
<el-divider content-position="left">选项配置</el-divider>
<el-form-item v-if="showHide(['select'], true)" label="添加全部项" class="form_cont">
<el-input
v-model="controlData.config.addAll"
placeholder="请输入全部项文案"
/>
</el-form-item>
<el-form-item label="选项数据源" class="form_cont">
<el-select
v-model="controlData.config.optionsType"
@change="controlData.config.optionsFun = ''"
>
<el-option :value="0" label="固定选项" />
<el-option :value="1" label="数据源" />
<el-option :value="2" label="接口字典" />
</el-select>
</el-form-item>
<template v-if="controlData.config.optionsType === 0">
<div v-if="controlData.type !== 'cascader'">
<el-form-item
v-for="(item, index) in controlData.options"
:key="index"
class="form_cont"
>
<el-col :span="10">
<el-input v-model="item.label" placeholder="选项标签" />
</el-col>
<el-col :span="10" :offset="1">
<el-input v-model="item.value" placeholder="选项值" />
</el-col>
<el-col :span="2" :offset="1">
<i
class="icon-del"
@click="delSelectOption(index as number)"
></i>
</el-col>
</el-form-item>
</div>
<el-form-item class="form_cont">
<el-button @click="addSelectOption">{{ controlData.type === 'cascader' ? '编辑' : '新增' }}</el-button>
</el-form-item>
</template>
<template v-else>
<el-form-item class="form_cont">
<el-input
v-model="controlData.config.optionsFun"
:placeholder="
getOptionPlaceholder(controlData.config.optionsType)
"
>
<template
v-if="controlData.config.optionsType === 1"
#prepend
>
<el-select
v-model="controlData.config.method"
style="width: 80px"
>
<el-option label="get" value="get" />
<el-option label="post" value="post" />
</el-select>
</template>
</el-input>
</el-form-item>
<template v-if="controlData.config.optionsType === 1">
<el-form-item label="指定label属性值" class="form_cont">
<el-input
v-model="controlData.config.label"
placeholder="返回数据中没有label时可设置"
/>
</el-form-item>
<el-form-item label="指定value属性值" class="form_cont">
<el-input
v-model="controlData.config.value"
placeholder="返回数据中没有value时可设置"
/>
</el-form-item>
</template>
<el-form-item v-if="controlData.config.optionsType === 1" class="form_cont">
<el-button
@click="
optionsEvent(
'optionsParams',
'请求前处理事件,参数(data,route,form) data请求参数,route页面路由,form表单值'
)
"
>beforeRequest</el-button>
<el-button
@click="
optionsEvent(
'optionsResult',
'请求返回结束处理;,也可为字符串,如opt=formatTest'
)
"
>afterResponse</el-button>
</el-form-item>
</template>
<el-form-item label="尝试转换value值为" class="form_cont">
<el-select
v-model="controlData.config.transformData"
placeholder="默认为number"
>
<el-option value="none">不转换</el-option>
<el-option value="number">number</el-option>
<el-option value="string">string</el-option>
</el-select>
</el-form-item>
</div>
<template
v-if="
!state.isSearch &&
showHide([
'txt',
'title',
'table',
'grid',
'tabs',
'card',
'switch',
'gridChild',
'tableColumn',
'divider',
'div',
'button'
])
"
>
<!-- <div class="h3"><h3>校验设置</h3></div> -->
<el-divider content-position="left">校验设置</el-divider>
<div v-if="showHide(['input', 'password', 'component'], true)">
<el-form-item
v-for="(item, index) in controlData.customRules"
:key="item.type"
class="form_cont"
>
<el-input v-model="item.message" placeholder="校验提示信息">
<template #prepend>
<el-select
v-model="item.type"
style="width: 80px"
@change="rulesSelectChange(item, $event)"
>
<el-option
v-for="list in state.customRulesList"
:key="list.type"
:label="list.label"
:value="list.type"
/>
</el-select>
</template>
<template #append>
<i
class="icon-del"
@click="delAddRules(index as number)"
></i>
</template>
</el-input>
<el-input
v-if="item.type === 'rules'"
v-model="item.rules"
placeholder="正则表达式"
/>
<el-input
v-if="item.type === 'methods'"
v-model="item.methods"
placeholder="方法名称,此方法仅适用于导出vue文件"
/>
</el-form-item>
<el-form-item class="form_cont">
<el-button @click="addRulesFast">快速添加</el-button>
<el-button @click="addRules(state.tooltip.rules)"
>编写校验规则
<el-tooltip
:content="state.tooltip.rules"
placement="top"
raw-content
>
<el-icon>
<QuestionFilled />
</el-icon>
</el-tooltip>
</el-button>
</el-form-item>
</div>
<el-form-item class="form_cont" v-else>
<el-checkbox
:model-value="checkboxRequired"
@change="requiredChange"
>必填
</el-checkbox>
<el-input
v-if="controlData.item?.rules && controlData.item?.rules[0]"
v-model="controlData.item.rules[0].message"
placeholder="自定义必填错误提示"
/>
</el-form-item>
</template>
<!-- <div class="h3"><h3>其他属性</h3></div> -->
<el-divider content-position="left">其他属性</el-divider>
<div v-if="showHide(['grid', 'card', 'gridChild', 'divider', 'div'])" class="form_cont">
<el-button
size="small"
@click="openAttrDialog('', state.tooltip.props)"
>编辑属性
<el-tooltip :content="state.tooltip.props" placement="top">
<el-icon>
<QuestionFilled />
</el-icon>
</el-tooltip>
</el-button>
</div>
<el-divider content-position="left">高级属性</el-divider>
<LayoutPage v-if="cssIsShouw(attrList)" v-model:styles-val="controlDataStyls" :place="controlData.type" />
</el-form>
</el-tab-pane>
<el-tab-pane label="表单配置" name="second">
<el-form size="small" class="form">
<el-divider content-position="left">基础配置</el-divider>
<el-form-item class="form_cont">
<template #label>
归属分组
</template>
<el-select id="groupForm" v-model="formConfig.groupKey" placeholder="Select" ref="groupForm">
<el-option v-for="item in formGroup" :key="item.idStr" :label="item.title" :value="item.idStr" />
<p v-if="loadingmore">加载中</p>
<p v-if="loadingnomore">无数据</p>
</el-select>
</el-form-item>
<el-form-item
v-for="(item, index) in formAttr.filter(item => !item.hide)"
:label="item.label"
:key="index"
class="form_cont"
>
<el-select
v-if="item.type === 'select'"
v-model="item.value"
:filterable="item.key === 'class'"
:allow-create="item.key === 'class'"
:placeholder="item.placeholder"
:clearable="item.clearable"
@change="formAttrChange(item)"
>
<el-option
v-for="opt in item.options"
:key="opt.label || opt.name"
:label="opt.label || opt.name"
:value="formatNumber(opt.value ?? opt.id)"
/>
</el-select>
<el-switch
v-else-if="item.type === 'switch'"
v-model="item.value"
@input="formAttrChange(item)"
/>
<el-input
v-else
v-model="item.value"
:placeholder="item.placeholder"
:disabled="isNotWrite(item)"
@input="formAttrChange(item)"
/>
</el-form-item>
<el-form-item class="form_cont">
<template #label>
表单图标
</template>
<el-upload
class="avatar-uploader"
:action="imgUploadApiUrl"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="formConfig.imageUrl" :src="formConfig.imageUrl" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
<template v-if="!state.isSearch">
<el-divider content-position="left">接口数据事件</el-divider>
<el-form-item v-if="!state.isSearch" class="form_cont">
<template #label
>
添加时获取请求
<el-tooltip
content="新增表单数据时,从接口获取新增初始数据"
placement="top"
>
<el-icon>
<QuestionFilled />
</el-icon>
</el-tooltip>
</template>
<el-switch
v-model="formConfig.addLoad"
@change="
formAttrChange({ key: 'addLoad', path: 'config' }, $event)
"
/>
</el-form-item>
<el-form-item label="新增数据保存url" class="form_cont">
<el-input
v-model="formConfig.addUrl"
placeholder="表单提交的url,非特殊不需要设置"
/>
</el-form-item>
<el-form-item label="修改数据保存url" class="form_cont">
<el-input
v-model="formConfig.editUrl"
placeholder="修改提交的url,非特殊不需要设置"
/>
</el-form-item>
<el-form-item label="获取表单数据url" class="form_cont">
<el-input
v-model="formConfig.requestUrl"
placeholder="获取表单数据url,非特殊不需要设置"
/>
</el-form-item>
<el-form-item class="event-btn form_cont">
<el-button
@click="
eventClick(
'beforeRequest',
'获取表单初始数据前事件,可修改请求参数'
)
"
>beforeRequest
</el-button>
<el-button
@click="
eventClick(
'afterResponse',
'获取表单初始数据后事件,可对请求返回数据进行处理;也可为字符串,如opt=formatTest'
)
"
>afterResponse
</el-button>
<el-button
@click="
eventClick(
'beforeSubmit',
'表单数据提交前事件,可对提交数据进行处理;也可为字符串,如opt=formatTest'
)
"
>beforeSubmit
</el-button>
<el-button
@click="eventClick('afterSubmit', '表单数据提交成功事件')"
>afterSubmit
</el-button>
<el-button
@click="
eventClick(
'change',
'表单组件值改变事件。当表单某值改变时,可修改其他组件的值;也可为字符串,如opt=formChange,字符串即为/utils/formChangeValue(name,model,key)中的key值'
)
"
>表单组件改变事件change
</el-button>
</el-form-item>
</template>
<el-divider content-position="left">表单整体布局</el-divider>
<el-form-item label="组件尺寸" class="form_cont">
<el-radio-group v-model="formData.size">
<el-radio-button label="large">大</el-radio-button>
<el-radio-button label="default">适中</el-radio-button>
<el-radio-button label="small">小</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="表单样式名称" class="form_cont">
<el-select
v-model="formData.class"
class="m-2"
placeholder="额外添加的表单class类名"
>
<el-option
v-for="item in optionsCss"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="行内表单模式" class="form_cont">
<el-switch v-model="formData.inline" active-text="开启" inactive-text="关闭" />
</el-form-item>
<el-form-item label="标签宽度" class="form_cont">
<el-input v-model="formData.labelWidth" clearable placeholder="表单标签宽度">
<template #append >PX</template>
</el-input>
</el-form-item>
<el-form-item label="标签后缀" class="form_cont">
<el-input v-model="formData.labelSuffix" placeholder="表单标签后缀" />
</el-form-item>
<el-form-item label="表单标签对齐方式" class="form_cont">
<el-radio-group v-model="formData.labelPosition">
<el-radio-button label="left">左</el-radio-button>
<el-radio-button label="right">右</el-radio-button>
<el-radio-button label="top">上</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="必填项星号位置" class="form_cont">
<el-radio-group v-model="formData.requireAsteriskPosition">
<el-radio-button label="left">左</el-radio-button>
<el-radio-button label="right">右</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="是否隐藏星号" class="form_cont">
<el-switch v-model="formData.hideRequiredAsterisk" active-text="隐藏" inactive-text="显示" style="--el-switch-on-color: #ff4949; --el-switch-off-color: #13ce66" />
</el-form-item>
<el-form-item label="校验错误信息" class="form_cont">
<el-switch v-model="formData.showMessage" active-text="显示" inactive-text="隐藏" />
</el-form-item>
<el-form-item label="以行内形式展示校验信息" class="form_cont">
<el-switch v-model="formData.inlineMessage" active-text="显示" inactive-text="隐藏" />
</el-form-item>
<el-form-item label="是否显示校验结果反馈图标" class="form_cont">
<el-switch v-model="formData.statusIcon" active-text="显示" inactive-text="隐藏" />
</el-form-item>
<el-divider content-position="left">表单功能补充</el-divider>
<el-form-item class="form_cont">
<el-button @click="editFormStyle(state.tooltip.css)"
>
编辑表单样式
<el-tooltip :content="state.tooltip.css" placement="top">
<el-icon>
<QuestionFilled />
</el-icon>
</el-tooltip>
</el-button>
<el-button @click="editFormDict(state.tooltip.dict)"
>
设置数据字典
<el-tooltip :content="state.tooltip.dict" placement="top">
<el-icon>
<QuestionFilled />
</el-icon>
</el-tooltip>
</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</div>
<!-- 轮播图设置弹窗 20240122 start -->
<el-dialog v-model="dialogTableVisible" title="轮播图设置" top="20px" style="margin-top:70px,margin-left:270px">
<div v-if="controlData.type=='lowcodeCarsusel'" style="margin-bottom: 30px;">
<span style="margin-right: 10px;">轮播图宽度(像素):</span><el-input-number v-model="controlData.control.config.carsuselWidth" :step="64" :max="4096" :min="178"/>
<span style="margin-left:15px;margin-right: 10px;">轮播图高度(像素):</span><el-input-number v-model="controlData.control.config.carsuselHeight" :step="36" :max="4096" :min="100"/>
<span style="margin-left:15px;margin-right: 10px;">切图间隔(毫秒):</span><el-input-number v-model="controlData.control.config.interval" :step="200"/>
</div>
<el-table v-if="controlData.type=='lowcodeCarsusel'" :data="controlData.control.carsuselConfigArr" border style="width: 100%" row-key="imgId">
<el-table-column label="设置图片" width="180" >
<template #default="propFlag">
<el-upload
:action="uploadUrl" :before-remove="beforeRemove"
:on-success="carouselImgUploadSuccess"
:on-error="videoUploadErr"
:limit="1"
accept=".jpg,.jpeg,.png,.tif,.tga,.bmp,.dds,.svg,.eps,.pdf,.hdr,.raw,.exr,.psd,.afphoto,.afdesign">
<el-button v-if="!propFlag.row.uploadFlag" type="primary" @click="changeCurrentUploadImgid(propFlag.row)">点此上传</el-button>
<el-button v-if="propFlag.row.uploadFlag" type="primary" @click="changeCurrentUploadImgid(propFlag.row)">已上传,点击修改</el-button>
</el-upload>
</template>
</el-table-column>
<el-table-column prop="imgUrl" label="查看图片" width="180">
<template #default="imgScope">
<!-- {{ imgScope.row.imgUrl }} -->
<el-image style="width: 100px; height: 100px;cursor:pointer;" :src="imgScope.row.imgUrl" fit="contain" alt="暂未上传" @click="handlePreview(imgScope.$index, imgScope.row)" >
<template #error>
<div class="image-slot">
<el-image style="width: 100px; height: 100px;" :src="errimg" fit="contain" ></el-image>
</div>
</template>
</el-image>
</template>
</el-table-column>
<el-table-column prop="link" label="链接地址">
<template #default="linkScope">
<el-input v-model="linkScope.row.link" placeholder="输入图片的链接地址" />
</template>
</el-table-column>
<el-table-column fixed="right" label="操作(增/删)" width="120" >
<template #default="scope">
<el-button v-if="controlData.control.carsuselConfigArr.length>1" type="danger" :icon="Delete" circle @click="deleteRow(scope.row)"/>
<el-button v-if="controlData.control.carsuselConfigArr[controlData.control.carsuselConfigArr.length-1].imgId == scope.row.imgId" type="success" :icon="Plus" circle @click="createRow(scope.row)" />
</template>
</el-table-column>
</el-table>
</el-dialog>
<!-- 图片预览 -->
<el-image-viewer
v-if="showImagePreview"
:zoom-rate="1.2"
:url-list="carouselImgUrlList"
@close="closePreview"
/>
<!-- 轮播图设置弹窗 liwenxuan 20240122 end 1-->
</template>
<style lang='scss' scoped>
.form_cont{
padding: 0 10px;
}
.avatar-uploader{
width: 100px;
height: 100px;
.avatar{
width: 100px;
height: 100px;
}
.avatar-uploader-icon{
width: 100px;
height: 100px;
}
}
</style>
<style >
.sidebar-tools .el-tabs__content{
padding: 0;
}
.el-collapse-item__header {
padding-left: 10px;
background-color: rgb(229, 229, 229);
}
.el-collapse-item__header{
padding-left: 10px;
background-color: rgb(229, 229, 229);
}
</style>