22 changed files with 2748 additions and 726 deletions
@ -0,0 +1,499 @@ |
|||
<!-- |
|||
@ 作者: 秦东 |
|||
@ 时间: 2023-07-12 15:08:07 |
|||
@ 备注: 表单画布 |
|||
--> |
|||
<script lang='ts' setup> |
|||
import FormGroup from './formGroup.vue' |
|||
import { FormData,FormList,FormDataStyle } from '@/api/DesignForm/types' |
|||
import { getRequest } from '@/api/DesignForm' |
|||
import { useRoute, useRouter } from 'vue-router' |
|||
import { |
|||
constGetControlByName, |
|||
constSetFormOptions, |
|||
constFormBtnEvent, |
|||
constControlChange, |
|||
constFormProps, |
|||
appendOrRemoveStyle |
|||
} from '@/api/DesignForm/utils' |
|||
import formatResult from '@/utils/DesignForm/formatResult' |
|||
import formChangeValue from '@/utils/DesignForm/formChangeValue' |
|||
import { jsonParseStringify } from '@/utils/DesignForm' |
|||
|
|||
|
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
formData: FormData |
|||
type?: number // 1新增;2修改;3查看(表单模式) ;4查看; 5设计 |
|||
disabled?: boolean // 禁用表单提交 |
|||
requestUrl?: string // 编辑数据请求url |
|||
beforeRequest?: Function // 请求编辑数据前参数处理方法,可对请求参数处理 |
|||
afterResponse?: Function | string // 请求数据加载完成后数据处理方法,可对返回数据处理 |
|||
addUrl?: string // 表单数据新增提交保存url |
|||
editUrl?: string // 表单数据修改保存提交url |
|||
beforeSubmit?: Function | string // 表单提交前数据处理,可对提交数据处理,新增和保存都会触发 |
|||
afterSubmit?: Function // 表单提交后,默认提示提交结果,可return false阻止提示 |
|||
value?: { [key: string]: any } // 表单初始值,同setValue |
|||
options?: { [key: string]: any } // 表单组件选项,同setOptions |
|||
dict?: object // 固定匹配的字典 |
|||
isSearch?: boolean // 列表里作为筛选使用 |
|||
}>(), |
|||
{ |
|||
type: 1, // 1新增;2修改;3查看(表单模式) ;4查看; 5设计 |
|||
formData: () => { |
|||
return { |
|||
list: [], |
|||
form: {}, |
|||
config: { |
|||
style:'' |
|||
} |
|||
} |
|||
}, |
|||
dict: () => { |
|||
return {} |
|||
}, |
|||
isSearch: false, |
|||
issave:{ |
|||
type:Boolean, |
|||
default:true |
|||
}, |
|||
} |
|||
) |
|||
const emits = defineEmits<{ |
|||
(e: 'btnClick', type: string): void |
|||
(e: 'change', val: any): void // 表单组件值发生变化时 |
|||
(e: 'update:issave', type: boolean): void |
|||
}>() |
|||
const route = useRoute() |
|||
const router = useRouter() |
|||
|
|||
const loading = ref(false) |
|||
|
|||
let timer = 0 |
|||
let eventName = '' |
|||
let getValueEvent = '' |
|||
// 注册window事件 |
|||
const setWindowEvent = (bool?: boolean) => { |
|||
if (props.formData.list.length > 0) { |
|||
const formName = props.formData.form?.name |
|||
if (!formName) { |
|||
// 导出.vue时,name可以没有 |
|||
return |
|||
} |
|||
eventName = `get${formName}ControlByName` |
|||
getValueEvent = `get${formName}ValueByName` |
|||
if (formName && (!window[eventName as any] || !bool)) { |
|||
// 根据name获取当前数据项 |
|||
// @ts-ignore |
|||
window[eventName] = (name: string) => { |
|||
return getNameForEach(props.formData.list, name) |
|||
} |
|||
// 根据name获取当前项的值 |
|||
// @ts-ignore |
|||
window[getValueEvent] = (name: string) => { |
|||
return model.value[name] |
|||
} |
|||
} |
|||
} |
|||
} |
|||
watch( |
|||
() => props.formData, |
|||
() => { |
|||
console.log("监听数据----->1",props.formData) |
|||
emits('update:issave', false) |
|||
}, |
|||
{ deep: true } |
|||
) |
|||
watch( |
|||
() => props.formData.config, |
|||
() => { |
|||
if (timer < 2) { |
|||
setWindowEvent() // 简单判断下,这里不是每次都更新 |
|||
} |
|||
timer++ |
|||
appendRemoveStyle(true) // 更新样式 |
|||
}, |
|||
{ deep: true } |
|||
) |
|||
setWindowEvent() |
|||
// 设置全局事件结束 |
|||
const resultDict = ref({}) |
|||
// 处理表单值开始 |
|||
const model = ref<any>({}) |
|||
// 获取表单初始model值 |
|||
const getInitModel = () => { |
|||
const obj = {} |
|||
forEachGetFormModel(props.formData.list, obj) |
|||
model.value = obj |
|||
} |
|||
watch( |
|||
() => props.formData.list, |
|||
() => { |
|||
// formData从接口获取时 |
|||
getInitModel() |
|||
} |
|||
) |
|||
// 从表单数据里提取表单所需的model |
|||
const forEachGetFormModel = (list: FormList[], obj: any) => { |
|||
list.forEach((item: any) => { |
|||
if (['table', 'flex'].includes(item.type)) { |
|||
obj[item.name] = jsonParseStringify(item.tableData) |
|||
} else if (['grid', 'tabs'].includes(item.type)) { |
|||
item.columns.forEach((col: any) => { |
|||
forEachGetFormModel(col.list, obj) |
|||
}) |
|||
} else if (['card', 'div'].includes(item.type)) { |
|||
forEachGetFormModel(item.list, obj) |
|||
} else { |
|||
const excludeType = ['title', 'divider', 'txt', 'button'] |
|||
if (excludeType.indexOf(item.type) === -1) { |
|||
obj[item.name] = jsonParseStringify(item.control.modelValue) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
|
|||
// 表单组件值改变事件 tProp为子表格相关 |
|||
provide(constControlChange, ({ key, value, data, tProp }: any) => { |
|||
if (key) { |
|||
if (!tProp) { |
|||
// 表格和弹性布局不是这里更新,只触change |
|||
model.value[key] = value |
|||
} |
|||
// 支持在线方式数据处理,如A组件值改变时,可自动修改B组件的值,可参考请假流程自动时长计算 |
|||
if (props.formData.events?.change) { |
|||
model.value = props.formData.events.change(key, model.value) |
|||
} |
|||
const onFormChange = props.formData.events?.change |
|||
if (onFormChange) { |
|||
if (typeof onFormChange === 'function') { |
|||
model.value = onFormChange(key, model.value) |
|||
} else { |
|||
model.value = formChangeValue(key, model.value, onFormChange) |
|||
} |
|||
} |
|||
// 当表格和弹性内的字段和外面字段冲突时,可通过tProps区分 |
|||
emits('change', { key, value, model: model.value, data, tProp }) |
|||
} |
|||
}) |
|||
const dictForm = computed(() => { |
|||
const storage = window.localStorage.getItem('akFormDict') |
|||
let storageDict = {} |
|||
if (storage) { |
|||
storageDict = JSON.parse(storage) |
|||
} |
|||
// 全局的、当前表单配置的以及接口返回的 |
|||
return Object.assign({}, storageDict, props.dict, resultDict.value) |
|||
}) |
|||
// 表单参数 |
|||
const formProps = computed(() => { |
|||
return { |
|||
model: model.value, |
|||
type: props.type, |
|||
hideField: props.formData.config?.hideField as [], |
|||
showColon: props.formData.form.showColon, |
|||
dict: dictForm.value |
|||
} |
|||
}) |
|||
provide(constFormProps, formProps) |
|||
|
|||
// 提供一个方法,用于根据name从formData.list里查找数据 |
|||
const getNameForEach = (data: any, name: string) => { |
|||
let temp = {} |
|||
for (const key in data) { |
|||
const dataKey = data[key] |
|||
if (dataKey.name === name) { |
|||
return dataKey |
|||
} |
|||
if (['grid', 'tabs'].includes(dataKey.type)) { |
|||
dataKey.columns.forEach((co: any) => { |
|||
temp = getNameForEach(co.list, name) |
|||
}) |
|||
} |
|||
if (['card', 'div'].includes(dataKey.type)) { |
|||
temp = getNameForEach(dataKey.list, name) |
|||
} |
|||
} |
|||
return temp |
|||
} |
|||
const getControlByName = (name: string) => { |
|||
return getNameForEach(props.formData.list, name) |
|||
} |
|||
provide(constGetControlByName, getControlByName) |
|||
// 表单检验方法 |
|||
const ruleForm = ref() |
|||
const validate = (callback: any) => { |
|||
ruleForm.value.validate((valid: boolean, fields: any) => { |
|||
let fieldValue = fields |
|||
if (valid) { |
|||
// 校验通过,返回当前表单的值 |
|||
fieldValue = getValue() |
|||
} |
|||
callback(valid, fieldValue) |
|||
}) |
|||
} |
|||
// 提供一个取值的方法 |
|||
const getValue = (filter?: boolean) => { |
|||
if (filter) { |
|||
const obj: any = {} |
|||
for (const key in model.value) { |
|||
// eslint-disable-next-line no-prototype-builtins |
|||
if (model.value.hasOwnProperty(key)) { |
|||
const val = (model.value as any)[key] |
|||
if (!/^\s*$/.test(val)) { |
|||
obj[key] = val |
|||
} |
|||
} |
|||
} |
|||
return obj |
|||
} else { |
|||
return model.value |
|||
} |
|||
} |
|||
// 对表单设置初始值 |
|||
const setValue = (obj: { [key: string]: any }, filter?: boolean) => { |
|||
// 分两种,false时将obj所有值合并到model,当obj有某些值不存于表单中,也会合并到model,当提交表单也会提交此值 |
|||
// true则过滤没用的值,即存在当前表单的才合并 |
|||
if (filter) { |
|||
for (const key in obj) { |
|||
if (model.value[key] !== undefined) { |
|||
model.value[key] = obj[key] |
|||
} |
|||
} |
|||
} else { |
|||
model.value = Object.assign({}, model.value, jsonParseStringify(obj)) // 防止列表直接使用set方法对弹窗表单编辑,当重置表单时当前行数据被清空 |
|||
} |
|||
} |
|||
// 对表单选择项快速设置 |
|||
const setFormOptions = ref({}) |
|||
provide(constSetFormOptions, setFormOptions) |
|||
const setOptions = (obj: { [key: string]: string[] }) => { |
|||
setFormOptions.value = obj |
|||
} |
|||
// 追加移除style样式 |
|||
const appendRemoveStyle = (type?: boolean) => { |
|||
try { |
|||
const { |
|||
config:{ style } |
|||
} = props.formData |
|||
appendOrRemoveStyle('formStyle', style || '', type) |
|||
} catch (e) { |
|||
/* empty */ |
|||
} |
|||
} |
|||
|
|||
// 按钮组件事件 |
|||
provide(constFormBtnEvent, (obj: any) => { |
|||
emits('btnClick', obj.key) |
|||
if ([3, 4, 5].includes(props.type)) { |
|||
return ElMessage.error('当前模式不能提交表单') |
|||
} |
|||
switch (obj.key) { |
|||
case 'submit': |
|||
submit() // 提交 |
|||
break |
|||
case 'reset': |
|||
resetFields() // 重置 |
|||
break |
|||
case 'cancel': // 取消返回, |
|||
router.go(-1) //这个刷新后可能会失败 |
|||
break |
|||
} |
|||
}) |
|||
// 获取表单数据,编辑时,外部调用 |
|||
const getData = (params = {}) => { |
|||
const requestUrl = props.formData.config?.requestUrl || props.requestUrl |
|||
if (props.type === 5 || !requestUrl || props.isSearch) { |
|||
console.error('执行了获取数据方法,但配置有误!') |
|||
return |
|||
} |
|||
loading.value = true |
|||
const newParams: any = params |
|||
// 同时可使用props或是events里的事件,根据使用使用其中一种即可 |
|||
let newParams2 |
|||
const beforeRequest = props.formData.events?.beforeRequest |
|||
if (typeof beforeRequest === 'function') { |
|||
newParams2 = beforeRequest(newParams, route) |
|||
} |
|||
if (typeof props.beforeRequest === 'function') { |
|||
newParams2 = props.beforeRequest(newParams, route) |
|||
} |
|||
if (newParams2 === false) { |
|||
// 停止数据请求 |
|||
return |
|||
} |
|||
getRequest(requestUrl, newParams2 ?? newParams) |
|||
.then(res => { |
|||
// console.log(res) |
|||
loading.value = false |
|||
const result = res.data |
|||
if (result) { |
|||
let formatRes: any = result |
|||
// 比较适用通用表单,保存在服务端 |
|||
const afterResponse = props.formData.events?.afterResponse |
|||
if (typeof afterResponse === 'string' && afterResponse) { |
|||
formatRes = formatResult(result, afterResponse) |
|||
} else if (typeof afterResponse === 'function') { |
|||
formatRes = afterResponse(result) ?? result |
|||
} |
|||
// 比较适用于导出vue文件 |
|||
if (typeof props.afterResponse === 'string' && props.afterResponse) { |
|||
formatRes = formatResult(result, props.afterResponse) |
|||
} else if (typeof props.afterResponse === 'function') { |
|||
formatRes = props.afterResponse(result) ?? result |
|||
} |
|||
if (formatRes === false) { |
|||
return |
|||
} |
|||
setValue(formatRes.result || formatRes) |
|||
nextTick(() => { |
|||
// 将dict保存,可用于从接口中设置表单组件options。 |
|||
if (formatRes.dict && Object.keys(formatRes.dict).length) { |
|||
resultDict.value = formatRes.dict |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
.catch((res: any) => { |
|||
loading.value = false |
|||
return ElMessage.error(res.message) |
|||
}) |
|||
} |
|||
const submit = (params = {}) => { |
|||
const addUrl = props.formData.config?.addUrl || props.addUrl |
|||
const editUrl = props.formData.config?.editUrl || props.editUrl |
|||
const apiUrl = props.type === 1 ? addUrl : editUrl |
|||
if (props.isSearch || !apiUrl || loading.value) { |
|||
if (!props.isSearch && !apiUrl) { |
|||
console.error( |
|||
new Error('请在表单设计处配置接口事件url或选择数据源或设置props') |
|||
) |
|||
} |
|||
// 列表里作为筛选时,不提交表单 |
|||
return |
|||
} |
|||
validate((valid: boolean, fields: any) => { |
|||
if (valid) { |
|||
const formatParams = Object.assign({}, fields, params) |
|||
let submitParams |
|||
const beforeSubmit = props.formData.events?.beforeSubmit |
|||
if (beforeSubmit) { |
|||
if (typeof beforeSubmit === 'function') { |
|||
submitParams = beforeSubmit(formatParams, route) |
|||
} else { |
|||
submitParams = formatResult(formatParams, beforeSubmit) |
|||
} |
|||
} |
|||
if (props.beforeSubmit && typeof props.beforeSubmit === 'string') { |
|||
submitParams = formatResult(formatParams, props.beforeSubmit) |
|||
} else if (typeof props.beforeSubmit === 'function') { |
|||
submitParams = props.beforeSubmit(formatParams, route) |
|||
} |
|||
if (submitParams === false) { |
|||
return |
|||
} |
|||
loading.value = true |
|||
// 提交保存表单 |
|||
getRequest(apiUrl, submitParams ?? formatParams) |
|||
.then((res: any) => { |
|||
afterSubmit('success', res) |
|||
}) |
|||
.catch(res => { |
|||
afterSubmit('fail', res) |
|||
}) |
|||
} else { |
|||
// 没通过校验 |
|||
afterSubmit('validate', fields) |
|||
} |
|||
}) |
|||
} |
|||
// 不管成功失败,有事件时都需要执行回调 |
|||
const afterSubmit = (type: string, res: any) => { |
|||
const afterSubmit = props.formData.events?.afterSubmit |
|||
let notReturn |
|||
if (typeof afterSubmit === 'function') { |
|||
notReturn = afterSubmit(type, res) |
|||
} else if (typeof props.afterSubmit === 'function') { |
|||
notReturn = props.afterSubmit(type, res) |
|||
} |
|||
loading.value = false |
|||
// 不管结果,重置表单,防再次打开时保留上一次的值 |
|||
// resetFields() |
|||
if (notReturn === false) { |
|||
// 有返回false时则不提示 |
|||
return |
|||
} |
|||
if (type === 'success') { |
|||
ElMessage.success(res.message || '保存成功!') |
|||
} else if (type === 'fail') { |
|||
ElMessage.error(res.message || '保存失败!') |
|||
} |
|||
} |
|||
// 表单初始值 |
|||
watch( |
|||
() => props.value, |
|||
(v: any) => { |
|||
v && setValue(v) |
|||
}, |
|||
{ |
|||
immediate: true |
|||
} |
|||
) |
|||
// 表单options |
|||
watch( |
|||
() => props.options, |
|||
(v: any) => { |
|||
v && setOptions(v) |
|||
} |
|||
) |
|||
// ------------------------数据处理结束------------------------ |
|||
// 重置表单方法 |
|||
const resetFields = () => { |
|||
ruleForm.value.resetFields() |
|||
// setValue(Object.assign(model.value, obj || {})) // 这才能清空组件显示的值 |
|||
} |
|||
onMounted(() => { |
|||
getInitModel() |
|||
nextTick(() => { |
|||
appendRemoveStyle(true) |
|||
}) |
|||
}) |
|||
onUnmounted(() => { |
|||
if (eventName) { |
|||
// @ts-ignore |
|||
window[eventName] = '' |
|||
} |
|||
appendRemoveStyle() |
|||
}) |
|||
defineExpose({ |
|||
setOptions, |
|||
setValue, |
|||
getValue, |
|||
validate, |
|||
resetFields, |
|||
getData, |
|||
submit |
|||
}) |
|||
</script> |
|||
<template> |
|||
<div v-loading="loading"> |
|||
<el-form |
|||
v-bind="formData.form" |
|||
ref="ruleForm" |
|||
:model="model as any" |
|||
:disabled="disabled || type === 3" |
|||
class="add-form" |
|||
:class="{ |
|||
'design-form': type === 5, |
|||
'detail-form': type === 3 || type === 4 |
|||
}" |
|||
> |
|||
<FormGroup :data="formData.list" /> |
|||
<slot></slot> |
|||
</el-form> |
|||
</div> |
|||
</template> |
|||
<style lang='scss' scoped> |
|||
|
|||
</style> |
|||
@ -0,0 +1,437 @@ |
|||
<!-- |
|||
@ 作者: 秦东 |
|||
@ 时间: 2023-07-14 08:46:51 |
|||
@ 备注: 表单组 |
|||
--> |
|||
<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 } from '@/api/DesignForm/types' |
|||
import { |
|||
constFormBtnEvent, |
|||
constFormProps |
|||
} from '@/api/DesignForm/utils' |
|||
import { Md5 } from 'ts-md5'; |
|||
import { jsonParseStringify } from '@/utils/DesignForm' |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
data: FormList[] |
|||
}>(), |
|||
{ |
|||
data: () => { |
|||
return [] |
|||
} |
|||
} |
|||
) |
|||
const store = useDesignFormStore() as any |
|||
const formProps = inject(constFormProps, {}) as any |
|||
const type = computed(() => { |
|||
console.log("11111",formProps.value.type) |
|||
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 |
|||
} |
|||
) |
|||
const activeKey = computed(() => { |
|||
return store.activeKey |
|||
}) |
|||
|
|||
// 不能嵌套 |
|||
const notNested = (type: string) => { |
|||
const controlType = ['grid', 'table', 'tabs', 'div', 'flex', 'card'] |
|||
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') // 不能嵌套 |
|||
if (isNested === 'not-nested' && notNested(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' |
|||
] |
|||
if (!notNeedItem.includes(obj.type)) { |
|||
objectItem = { |
|||
item: { |
|||
label: label |
|||
} |
|||
} |
|||
} |
|||
// 不需要name的组件 |
|||
let nameObj = {} |
|||
const notNeedName = [ |
|||
'txt', |
|||
'title', |
|||
'button', |
|||
'grid', |
|||
'tabs', |
|||
'divider', |
|||
'div', |
|||
'card' |
|||
] |
|||
if (!notNeedName.includes(obj.type) && !obj.name) { |
|||
nameObj = { |
|||
name: obj.type + key |
|||
} |
|||
} |
|||
Object.assign(obj, nameObj, objectItem) |
|||
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 |
|||
} |
|||
} |
|||
// 点击激活当前 |
|||
const groupClick = (item: any, ele?: string) => { |
|||
// 设计模式下才执行 |
|||
if (type.value !== 5) { |
|||
return |
|||
} |
|||
if (ele) { |
|||
item.type = ele |
|||
} |
|||
store.setActiveKey(getGroupName(item)) |
|||
store.setControlAttr(item) |
|||
// grid时显示添加列按钮 |
|||
state.gridAdd = item.type === 'grid' |
|||
state.clone = !notNested(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 + '%' } |
|||
} |
|||
} |
|||
// 根据关联条件显示隐藏当前项 |
|||
const linksShow = (el: FormList, index: number) => { |
|||
// 当前项设置了关联条件,当关联主体的值等于当前组件设定的值时 |
|||
if (!el.config) { |
|||
return true |
|||
} |
|||
const key = el.config.linkKey |
|||
const value = el.config.linkValue |
|||
const linkResult = el.config.linkResult |
|||
if (key && value && type.value !== 5) { |
|||
const Fn = new Function('$', `return (${value})`) |
|||
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) => { |
|||
// 0: '提交表单', |
|||
// 1: '重置表单', |
|||
// 2: '取消返回', |
|||
// 3: '无动作(自定义)' |
|||
if (type.value !== 5) { |
|||
// 非设计模式才触发事件 |
|||
injectBtnEvent && injectBtnEvent(control) |
|||
} |
|||
} |
|||
onUnmounted(() => { |
|||
// console.log('formGroup onUnmounted') |
|||
dataList.value = {} |
|||
store.setActiveKey('') |
|||
store.setControlAttr({}) |
|||
}) |
|||
</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)" |
|||
> |
|||
<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" |
|||
> |
|||
<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" /> |
|||
</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> |
|||
|
|||
<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" |
|||
/> |
|||
<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)" |
|||
>{{ element.control?.label }}</el-button |
|||
> |
|||
</div> |
|||
</template> |
|||
|
|||
<template v-else-if="element.type === 'inputSlot' && type !== 5"> |
|||
<!-- 除设计外其他无需处理--> |
|||
</template> |
|||
|
|||
<FormItem v-else :data="element" /> |
|||
|
|||
<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: none;">{{ element.name }}</div> |
|||
</template> |
|||
|
|||
|
|||
</div> |
|||
</template> |
|||
</draggable> |
|||
</template> |
|||
<style lang='scss' scoped> |
|||
|
|||
</style> |
|||
@ -0,0 +1,203 @@ |
|||
<!-- |
|||
@ 作者: 秦东 |
|||
@ 时间: 2023-07-14 16:38:32 |
|||
@ 备注: |
|||
--> |
|||
<script lang='ts' setup> |
|||
import { onMounted, watch, ref, computed, onUnmounted } from 'vue' |
|||
import { ElMessage } from 'element-plus' |
|||
import { getRequest } from '@/api/DesignForm' |
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
modelValue: string |
|||
placeholder?: string |
|||
width?: string |
|||
height?: string |
|||
blobUrl?: string |
|||
imgUrl?: string |
|||
config?: any |
|||
}>(), |
|||
{ |
|||
modelValue: '', |
|||
placeholder: '请输入内容', |
|||
width: '100%', |
|||
height: '300px', |
|||
blobUrl: '', |
|||
imgUrl: '', |
|||
config: () => { |
|||
return {} |
|||
} |
|||
} |
|||
) |
|||
const emits = defineEmits<{ |
|||
(e: 'update:modelValue', value: string): void |
|||
}>() |
|||
const contentValue = ref(props.modelValue) |
|||
// 参数自定义初始化 |
|||
// eslint-disable-next-line max-len |
|||
const buttonPlugins ='preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media code codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount autosave ' |
|||
// 导入工具栏 |
|||
// eslint-disable-next-line max-len |
|||
const toolbar ='fullscreen undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link anchor table image | alignleft aligncenter alignright alignjustify outdent indent | styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | media charmap emoticons pagebreak insertdatetime print preview | code selectall searchreplace visualblocks | indent2em lineheight formatpainter axupimgs' |
|||
const toolbarSimple ='undo cut copy paste pastetext |forecolor backcolor bold italic underline strikethrough|alignleft aligncenter alignright alignjustify|' |
|||
const commInit = { |
|||
selector: '#myTextarea', |
|||
cleanup: true, |
|||
language: 'zh_CN', // 语言类型 |
|||
fontsize_formats: '12px 14px 16px 18px 20px 22px 24px 28px 32px 36px', // 字体大小 |
|||
lineheight_formats: '0.5 0.8 1 1.2 1.5 1.75 2 2.5 3 4 5', // 行高配置,也可配置成"12px 14px 16px 20px"这种形式 |
|||
branding: false, // tiny技术支持信息是否显示 |
|||
resize: false, // 编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号 |
|||
// statusbar: false, //最下方的元素路径和字数统计那一栏是否显示 |
|||
elementpath: false, // 元素路径是否显示 |
|||
height: props.height, // 允许外界传进来高度和placeholder |
|||
width: props.width, |
|||
placeholder: props.placeholder, |
|||
init_instance_callback: (editor: any) => { |
|||
// editor.setContent('') |
|||
editor.on('NodeChange Change KeyUp SetContent', () => { |
|||
contentValue.value = editor.getContent() |
|||
emits('update:modelValue', editor.getContent()) |
|||
}) |
|||
} |
|||
} |
|||
// 图片上传 |
|||
const imgUploadFn = (blobInfo: any, progress: number) => |
|||
new Promise((resolve, reject) => { |
|||
// https://www.tiny.cloud/docs/tinymce/6/file-image-upload/#images_upload_handler |
|||
console.log("图片上传",progress) |
|||
const params = new FormData() |
|||
params.append('file', blobInfo.blob()) |
|||
let options = {} |
|||
if (props.imgUrl) { |
|||
options = { |
|||
url: props.imgUrl |
|||
} |
|||
} |
|||
getRequest('upload', params, options) |
|||
.then(res => { |
|||
// console.log(res) |
|||
// console.log(res.data.path) |
|||
if (res.data.code === 1) { |
|||
resolve(res.data.path) // 上传成功,在成功函数里填入图片路径 |
|||
// console.log('[文件上传]', res.data) |
|||
} else { |
|||
reject('上传失败') |
|||
} |
|||
}) |
|||
.catch(() => { |
|||
reject('上传出错,示例暂不提供上传接口') |
|||
}) |
|||
}) |
|||
const fileUpload = (callback: any, value: string, meta: any) => { |
|||
const filetype ='.pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4' |
|||
//后端接收上传文件的地址 |
|||
const input = document.createElement('input') //创建文件选择 |
|||
input.setAttribute('type', 'file') |
|||
input.setAttribute('accept', filetype) |
|||
input.click() |
|||
input.onchange = () => { |
|||
const file = input?.files && input.files[0] //获取文件信息 |
|||
// console.log(file) |
|||
// meta对应于file_picker_types的三种类型 |
|||
let attr = {} |
|||
if (meta.filetype === 'file') { |
|||
attr = { text: file?.name } |
|||
} |
|||
if (meta.filetype === 'image') { |
|||
attr = { alt: file?.name } |
|||
} |
|||
if (meta.filetype === 'media') { |
|||
// attr={source2: 'alt.ogg', poster: 'image.jpg'} |
|||
} |
|||
/*if(file.type.slice(0,5)=='image'&&file.size/1024/1024>2){ |
|||
alert("上传失败,图片大小请控制在2M以内") |
|||
}else if(file.type.slice(0,5)=='video'&&file.size/1024/1024>500){ |
|||
alert("上传失败,视频大小请控制在 500M 以内") |
|||
}else if(file.size/1024/1024>10){ |
|||
alert("上传失败,文件大小请控制在 10M 以内") |
|||
}*/ |
|||
const params = new FormData() |
|||
params.append('file', file as any) |
|||
let options = {} |
|||
if (props.blobUrl) { |
|||
options = { |
|||
url: props.blobUrl |
|||
} |
|||
} |
|||
getRequest('upload', params, options) |
|||
.then(res => { |
|||
if (res.data.code === 1) { |
|||
callback(res.data.path, attr) // 上传成功,在成功函数里填入图片路径 |
|||
} else { |
|||
ElMessage.error(res.data?.message) |
|||
} |
|||
}) |
|||
.catch(res => { |
|||
ElMessage.error(res.data?.message) |
|||
}) |
|||
} |
|||
} |
|||
const defaultInit = { |
|||
plugins: buttonPlugins, // 插件配置 |
|||
toolbar: toolbar, // 工具栏配置,设为false则隐藏 |
|||
menubar: true, // 菜单栏配置,设为false则隐藏,不配置则默认显示全部菜单,也可自定义配置--查看 http://tinymce.ax-z.cn/configure/editor-appearance.php --搜索“自定义菜单” |
|||
//emoticons_database_url: './tinymce/emoticons/js/emojis.js', |
|||
// eslint-disable-next-line max-len |
|||
font_formats:'微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;', // 字体样式 微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif, 宋体=simsun,serif,仿宋体=FangSong,黑体=SimHei,Arial=arial, |
|||
// content_style: 'p {margin-block-start: 0; margin-block-end: 0; color: #606D81; font-size: 14px;}; table { border: 1px}', // 直接自定义可编辑区域的css样式 |
|||
content_css: false, // 以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入 |
|||
paste_data_images: true, // 图片是否可粘贴 |
|||
// 允许外界传进来高度和placeholder |
|||
// 粘贴图片 自动处理 base64 |
|||
urlconverter_callback: (url: string, node: string) => { |
|||
if (node === 'img' && url.startsWith('blob:')) { |
|||
// @ts-ignore |
|||
tinymce.activeEditor && tinymce.activeEditor.uploadImages() |
|||
} |
|||
return url |
|||
}, |
|||
// 图片上传 |
|||
images_upload_handler: imgUploadFn, |
|||
file_picker_types: 'file image media', //分别对应三个类型文件的上传:link插件,image和axupimgs插件,media插件。想屏蔽某个插件的上传就去掉对应的参数 |
|||
file_picker_callback: fileUpload |
|||
} |
|||
const simpleInit = { |
|||
plugins: '', // 插件配置 |
|||
toolbar: toolbarSimple, // 工具栏配置,设为false则隐藏 |
|||
menubar: false, // 菜单栏配置,设为false则隐藏,不配置则默认显示全部菜单,也可自定义配置--查看 |
|||
font_formats: '', |
|||
paste_data_images: false // 图片是否可粘贴 |
|||
} |
|||
const myInit = computed(() => { |
|||
const styleType = |
|||
props.config?.style === 'simple' ? simpleInit : defaultInit |
|||
return Object.assign(commInit, styleType) |
|||
}) |
|||
onMounted(() => { |
|||
// @ts-ignore |
|||
tinymce.init(myInit.value) |
|||
}) |
|||
onUnmounted(() => { |
|||
// @ts-ignore |
|||
tinymce.remove() |
|||
}) |
|||
// 侦听默认值 外界第一次传进来一个 v-model 就赋值给 contentValue |
|||
watch( |
|||
() => props.modelValue, |
|||
(n: any) => { |
|||
if (n && n !== contentValue.value) { |
|||
contentValue.value = n |
|||
} |
|||
} |
|||
) |
|||
</script> |
|||
<template> |
|||
<textarea id="myTextarea" v-model="contentValue"></textarea> |
|||
</template> |
|||
<style lang='scss' scoped> |
|||
.tox-tinymce-aux { |
|||
z-index: 99999 !important; /*el-dialog层为2014,默认时在el弹出层显示不了编辑器里的弹窗*/ |
|||
z-index: 99999; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,84 @@ |
|||
import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios'; |
|||
import { useUserStoreHook } from '@/store/modules/user'; |
|||
|
|||
// 创建 axios 实例
|
|||
const service = axios.create({ |
|||
baseURL: import.meta.env.VITE_APP_BASE_API, |
|||
timeout: 50000, |
|||
}); |
|||
|
|||
// 请求拦截器
|
|||
service.interceptors.request.use( |
|||
(config: InternalAxiosRequestConfig) => { |
|||
const userStore = useUserStoreHook(); |
|||
if (userStore.tokenIng) { |
|||
config.headers.Authorization = userStore.tokenIng; |
|||
} |
|||
if (userStore.userKey) { |
|||
config.headers["user-key"] = userStore.userKey; |
|||
} |
|||
if (userStore.userToken) { |
|||
config.headers["user-token"] = userStore.userToken; |
|||
} |
|||
return config; |
|||
}, |
|||
(error: any) => { |
|||
return Promise.reject(error); |
|||
} |
|||
); |
|||
|
|||
// 响应拦截器
|
|||
service.interceptors.response.use( |
|||
(response: AxiosResponse) => { |
|||
const { code, msg } = response.data; |
|||
if (code === 0) { |
|||
return response.data; |
|||
} |
|||
if (code === 7 || code === 300 || code === 301 || code === 302){ |
|||
ElMessageBox.confirm("身份令牌已失效!请重新登录!", "提示", { |
|||
confirmButtonText: "确定", |
|||
type: "warning", |
|||
}).then(() => { |
|||
localStorage.clear(); |
|||
window.location.href = "/"; |
|||
}); |
|||
return response.data; |
|||
} |
|||
// 响应数据为二进制流处理(Excel导出)
|
|||
if (response.data instanceof ArrayBuffer) { |
|||
return response; |
|||
} |
|||
|
|||
ElMessage.error(msg || '系统出错'); |
|||
return Promise.reject(new Error(msg || 'Error')); |
|||
}, |
|||
(error: any) => { |
|||
if (error.response.data) { |
|||
const { code, msg } = error.response.data; |
|||
// token 过期,重新登录
|
|||
if (code === 'A0230') { |
|||
ElMessageBox.confirm('当前页面已失效,请重新登录', '提示', { |
|||
confirmButtonText: '确定', |
|||
type: 'warning' |
|||
}).then(() => { |
|||
localStorage.clear(); |
|||
window.location.href = '/'; |
|||
}); |
|||
}else if(code === 7 || code === 300 || code === 301 || code === 302){ |
|||
ElMessageBox.confirm("身份令牌已失效!请重新登录!", "提示", { |
|||
confirmButtonText: "确定", |
|||
type: "warning", |
|||
}).then(() => { |
|||
localStorage.clear(); |
|||
window.location.href = "/"; |
|||
}); |
|||
} else { |
|||
ElMessage.error(msg || '系统出错'); |
|||
} |
|||
} |
|||
return Promise.reject(error.message); |
|||
} |
|||
); |
|||
|
|||
// 导出 axios 实例
|
|||
export default service; |
|||
@ -0,0 +1,230 @@ |
|||
<!-- |
|||
@ 作者: 秦东 |
|||
@ 时间: 2023-09-13 10:43:12 |
|||
@ 备注: 数据结构表单 |
|||
--> |
|||
<script lang='ts' setup> |
|||
import { publicFormTableStruct,formTabelStruct } from "@/api/DesignForm/type"; |
|||
import { callBackFormTableVersion,haveFormTabelcont,optimizeOrRepairFormTable,haveFormTablelist } from '@/api/DesignForm/requestapi' |
|||
//引入页面 |
|||
import SetupField from '@/views/sysworkflow/codepage/setupfield.vue' |
|||
|
|||
|
|||
import { |
|||
Ticket, |
|||
Finished |
|||
} from '@element-plus/icons-vue' |
|||
|
|||
const props = defineProps({ |
|||
isshow:{ |
|||
type:Boolean, |
|||
default:true |
|||
}, |
|||
formcont:{ |
|||
type:Object, |
|||
default(){ |
|||
return {} |
|||
} |
|||
}, |
|||
}) |
|||
const emits = defineEmits(["update:isshow"]); |
|||
const isShow = computed({ |
|||
get: () => props.isshow, |
|||
set: (val) => { |
|||
emits("update:isshow", val); |
|||
}, |
|||
}); |
|||
//字段列表 |
|||
const formTableFieldList = ref<formTabelStruct[]>() |
|||
const versionIndex = ref("") //当前版本 |
|||
const formTableIndex = ref("") //当前表单 |
|||
const versionAry = ref<publicFormTableStruct[]>([]) //版本列表 |
|||
const formTableAry = ref<publicFormTableStruct[]>([]) //表单列表 |
|||
const isEditForm = ref(false) |
|||
const formTableCont = ref<publicFormTableStruct>() //表单内容 |
|||
const isEditFormField = ref(false) |
|||
const tableLoading = ref(false) |
|||
//选择版本 |
|||
const clickVersion = (val:any) =>{ |
|||
// console.log("切换版本",val,versionIndex.value) |
|||
formTableAry.value.slice(0,formTableAry.value.length) |
|||
// console.log("舒适化",formTableAry.value) |
|||
haveFormTablelist({id:val.toString()}) |
|||
.then(({data})=>{ |
|||
// console.log("选择版本",data,formTableAry.value) |
|||
formTableAry.value = data |
|||
if(data.length > 0){ |
|||
data.forEach( item =>{ |
|||
if(item.ismain){ |
|||
formTableIndex.value = item.name |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
.finally(()=>{ |
|||
getTableFieldList(versionIndex.value,formTableIndex.value) |
|||
}) |
|||
} |
|||
//选择表单 |
|||
const clickFormTable = (val:any) =>{ |
|||
// console.log("切换表单",val,formTableIndex.value) |
|||
getTableFieldList(versionIndex.value,val) |
|||
} |
|||
//获取版本及表列表 |
|||
const getVersionFormTable = () =>{ |
|||
callBackFormTableVersion({id:props.formcont.id}) |
|||
.then(({ data }) =>{ |
|||
// console.log("获取版本及表列表--->",data) |
|||
versionAry.value = data.version |
|||
formTableAry.value = data.tablelist |
|||
if(data.version){ |
|||
if(data.version.length > 0){ |
|||
data.version.forEach( item =>{ |
|||
if(item.ismain){ |
|||
versionIndex.value = item.versionid |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
if(data.tablelist){ |
|||
if(data.tablelist.length > 0){ |
|||
data.tablelist.forEach( item =>{ |
|||
if(item.ismain){ |
|||
formTableIndex.value = item.name |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
}) |
|||
.finally(() =>{ |
|||
// console.log("初始化数据结构-->",formTableIndex.value,versionIndex.value) |
|||
getTableFieldList(versionIndex.value,formTableIndex.value) |
|||
}); |
|||
} |
|||
function getTableFieldList(id:string,name:string){ |
|||
tableLoading.value = true; |
|||
haveFormTabelcont({"id":id, "name":name}) |
|||
.then(({data}) =>{ |
|||
// console.log("初始化数据结构-->",data) |
|||
formTableFieldList.value = data.filedlist |
|||
isEditForm.value = data.isedit |
|||
}) |
|||
.finally(()=>{ |
|||
tableLoading.value = false; |
|||
}) |
|||
|
|||
} |
|||
//监听 |
|||
watch(()=>props.isshow,()=>{ |
|||
if(props.isshow){ |
|||
getVersionFormTable() |
|||
} |
|||
}) |
|||
//编辑字段 |
|||
const editCustomerFormField = (val:any) =>{ |
|||
// optimizeOrRepairFormTable() |
|||
formTableCont.value = val |
|||
isEditFormField.value = true |
|||
} |
|||
//优化修复表单 |
|||
const optimizeOrRepairTable = (val:number) =>{ |
|||
optimizeOrRepairFormTable({name:formTableIndex.value,optimizerender:val}) |
|||
.then((data:any)=>{ |
|||
// console.log("表单处理",data,formTableIndex.value,val) |
|||
ElMessage.success(data.msg || '加载异常') |
|||
}) |
|||
} |
|||
//刷新表单 |
|||
const refreshTable = () =>{ |
|||
getTableFieldList(versionIndex.value,formTableIndex.value) |
|||
} |
|||
</script> |
|||
<template> |
|||
<el-dialog v-model="isShow" :title="'<'+props.formcont.name+'>数据结构'" width="70%" draggable> |
|||
<div class="common-layout"> |
|||
<el-container> |
|||
<el-main style="padding: 0;"> |
|||
<!-- 表结构 --> |
|||
<el-tabs v-model="formTableIndex" class="form_version" style="height: 40px;" @tab-change="clickFormTable"> |
|||
<el-tab-pane v-for="item in formTableAry" :key="item.name" :name="item.name" > |
|||
<template #label> |
|||
<el-icon v-if="item.ismain"><Management /></el-icon> |
|||
<el-icon v-else><Memo /></el-icon> |
|||
{{ item.name }} |
|||
</template> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
<el-table v-loading="tableLoading" :data="formTableFieldList" border height="460" style="width: 100%;"> |
|||
<el-table-column fixed prop="field" label="字段" width="120" /> |
|||
<el-table-column prop="type" label="类型" width="120" /> |
|||
<el-table-column prop="collation" label="排序规则" width="180" /> |
|||
<el-table-column prop="attribute" label="属性" width="100" /> |
|||
<el-table-column prop="null" label="空" align="center" /> |
|||
<el-table-column prop="default" label="默认值" align="center" /> |
|||
<el-table-column prop="extra" label="补充信息" width="200" /> |
|||
<el-table-column fixed="right" prop="comment" label="备注" width="200" /> |
|||
<el-table-column fixed="right" v-if="isEditForm" label="操作" align="center"> |
|||
<template #default="scope"> |
|||
<el-button |
|||
type="primary" |
|||
link |
|||
size="small" |
|||
@click.stop="editCustomerFormField(scope.row)" |
|||
> |
|||
<i-ep-edit />编辑 |
|||
</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<el-row style="margin-top: 10px;margin-bottom: 10px;"> |
|||
<el-col :span="24"> |
|||
<el-button :icon="Finished" type="primary" @click.stop="optimizeOrRepairTable(1)">优化表单</el-button> |
|||
<el-button :icon="Ticket" type="warning" @click.stop="optimizeOrRepairTable(2)">修复表单</el-button> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
</el-main> |
|||
<el-aside width="100px"> |
|||
<!-- 版本 --> |
|||
<el-tabs v-model="versionIndex" tab-position="right" style="height: 500px;" @tab-change="clickVersion"> |
|||
<el-tab-pane v-for="item in versionAry" :key="item.versionid" :label="item.name" :name="item.versionid"> |
|||
<template #label> |
|||
<div class="version_field" title="ConfigConfigConfig"> |
|||
{{ item.name }} |
|||
</div> |
|||
</template> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</el-aside> |
|||
</el-container> |
|||
</div> |
|||
<SetupField v-model:isEditFormField="isEditFormField" :formtablecont="formTableCont" :formname="formTableIndex" @refreshtable="refreshTable" /> |
|||
</el-dialog> |
|||
</template> |
|||
<style lang='scss'> |
|||
.form_version{ |
|||
height: 500px; |
|||
|
|||
} |
|||
.form_table{ |
|||
height: 500px; |
|||
width: 100%; |
|||
overflow-y: auto; |
|||
} |
|||
.form_version > .el-tabs__content{ |
|||
padding: 0; |
|||
} |
|||
.version_field{ |
|||
width:60px; |
|||
|
|||
// white-space: normal; |
|||
// word-break: break-word; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
.but_clic{ |
|||
margin-top: 10px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,237 @@ |
|||
<!-- |
|||
@ 作者: 秦东 |
|||
@ 时间: 2023-09-14 09:47:27 |
|||
@ 备注: 编辑表单字段 |
|||
--> |
|||
<script lang='ts' setup> |
|||
import { gogoBackFormTabelStruct } from "@/api/DesignForm/type"; |
|||
import { formFieldHandle,editFormField } from '@/api/DesignForm/requestapi' |
|||
const fieldType = ["tinyint","smallint","mediumint","int","integer","bigint","decimal", "float", "double","char","varchar","binary","varbinary","enum","set","text","tinytext","mediumtext","longtext","blob","year","date","time","datetime","timestamp"]; |
|||
const props = defineProps({ |
|||
isEditFormField:{ |
|||
type:Boolean, |
|||
default:true |
|||
}, |
|||
formname:{ |
|||
type:String, |
|||
default:"" |
|||
}, |
|||
formtablecont:{ |
|||
type:Object, |
|||
default(){ |
|||
return {} |
|||
} |
|||
}, |
|||
}) |
|||
|
|||
const unsignedIsShow = ref(false); //符号 |
|||
const decimalIsShow = ref(false); //小数点 |
|||
const fieldLenghtIsShow = ref(false); //字段长度 |
|||
const isNull = ref(false); //是否显示空置 |
|||
const butLoad = ref(false); |
|||
|
|||
const formTableField = reactive<gogoBackFormTabelStruct>({ |
|||
formname:props.formname, |
|||
field:"", |
|||
type:"", |
|||
attribute:"", |
|||
collation:"", |
|||
null:"", |
|||
key:"", |
|||
default:"", |
|||
extra:"", |
|||
privileges:"", |
|||
comment:"", |
|||
integer:"", |
|||
decimal:"", |
|||
}) |
|||
const dialogTitle = ref<string>() |
|||
const isEditOk = ref(false) |
|||
|
|||
const emits = defineEmits(["update:isEditFormField","refreshtable","xxxxx"]); |
|||
const isShow = computed({ |
|||
get: () => props.isEditFormField, |
|||
set: (val) => { |
|||
emits("update:isEditFormField", val); |
|||
emits("refreshtable"); |
|||
}, |
|||
}); |
|||
|
|||
watch(()=>props.isEditFormField,()=>{ |
|||
// console.log("监听状态----->",props.isEditFormField,props.formtablecont) |
|||
if(props.isEditFormField){ |
|||
if(props.formtablecont.comment != null && props.formtablecont.comment != ""){ |
|||
dialogTitle.value = "编辑<"+props.formtablecont.field+">字段(PS:"+props.formtablecont.comment+")" |
|||
}else{ |
|||
dialogTitle.value = "编辑<"+props.formtablecont.field+">字段" |
|||
} |
|||
formFieldHandle(props.formtablecont) |
|||
.then(({data})=>{ |
|||
// console.log("监听状态---1-->",data) |
|||
formTableField.formname=props.formname |
|||
formTableField.field = data.field; |
|||
formTableField.type = data.type; |
|||
formTableField.attribute = data.attribute; |
|||
formTableField.collation = data.collation; |
|||
formTableField.null = data.null; |
|||
formTableField.key = data.key; |
|||
formTableField.default = data.default; |
|||
formTableField.extra = data.extra; |
|||
formTableField.privileges = data.privileges; |
|||
formTableField.comment = data.comment; |
|||
formTableField.integer = data.integer; |
|||
formTableField.decimal = data.decimal; |
|||
if(["tinyint","smallint","mediumint","int","integer","bigint","decimal", "float", "double"].indexOf(data.type) != -1){ |
|||
unsignedIsShow.value = true |
|||
}else{ |
|||
unsignedIsShow.value = false |
|||
} |
|||
if(["decimal", "float", "double"].indexOf(data.type) != -1){ |
|||
decimalIsShow.value = true |
|||
}else{ |
|||
decimalIsShow.value = false |
|||
} |
|||
if(["enum","set","text","tinytext","mediumtext","longtext","blob","year","date","time","datetime","timestamp"].indexOf(data.type) != -1){ |
|||
fieldLenghtIsShow.value = false |
|||
}else{ |
|||
fieldLenghtIsShow.value = true |
|||
} |
|||
}) |
|||
// formTableField.value = props.formtablecont |
|||
}else{ |
|||
if(isEditOk.value){ |
|||
emits("refreshtable") |
|||
} |
|||
initData() |
|||
// formTableField.value=new Value(props.formtablecont) |
|||
} |
|||
}) |
|||
//初始化数据 |
|||
function initData(){ |
|||
formTableField.formname=""; |
|||
formTableField.field = ""; //field; |
|||
formTableField.type = ""; //type; |
|||
formTableField.attribute = ""; //attribute; |
|||
formTableField.collation = ""; //collation; |
|||
formTableField.null = ""; //null; |
|||
formTableField.key = ""; //key; |
|||
formTableField.default = ""; //default; |
|||
formTableField.extra = ""; //extra; |
|||
formTableField.privileges = ""; //privileges; |
|||
formTableField.comment = ""; //comment; |
|||
formTableField.integer = ""; //integer; |
|||
formTableField.decimal = ""; //decimal; |
|||
unsignedIsShow.value = false; |
|||
decimalIsShow.value = false; |
|||
fieldLenghtIsShow.value = false; |
|||
} |
|||
/** |
|||
* 关闭弹窗 |
|||
*/ |
|||
function closeDialog() { |
|||
isShow.value = false; |
|||
emits("update:isEditFormField", false); |
|||
initData(); |
|||
} |
|||
//提交表单 |
|||
function sendFormData(){ |
|||
butLoad.value=true; |
|||
// console.log("提交数据--->",props.formname,formTableField) |
|||
editFormField(formTableField) |
|||
.then((data)=>{ |
|||
// console.log("提交数据--1->",data) |
|||
ElMessage.success('成功') |
|||
isEditOk.value=true; |
|||
}) |
|||
.finally(()=>{ |
|||
butLoad.value=false; |
|||
closeDialog(); |
|||
}) |
|||
} |
|||
//判断数据类型 |
|||
const judgeFieldType = (typeStr:string) => { |
|||
if(["tinyint","smallint","mediumint","int","integer","bigint","decimal", "float", "double"].indexOf(typeStr) != -1){ |
|||
unsignedIsShow.value = true |
|||
formTableField.attribute = "unsigned" |
|||
formTableField.integer = "3" |
|||
formTableField.decimal = "" |
|||
}else{ |
|||
unsignedIsShow.value = false |
|||
formTableField.attribute = "" |
|||
} |
|||
if(["decimal", "float", "double"].indexOf(typeStr) != -1){ |
|||
decimalIsShow.value = true |
|||
}else{ |
|||
decimalIsShow.value = false |
|||
} |
|||
if(["enum","set","text","tinytext","mediumtext","longtext","blob","year","date","time","datetime","timestamp"].indexOf(typeStr) != -1){ |
|||
fieldLenghtIsShow.value = false |
|||
formTableField.integer = "" |
|||
formTableField.decimal = "" |
|||
}else{ |
|||
fieldLenghtIsShow.value = true |
|||
} |
|||
if(["char","varchar","binary","varbinary"].indexOf(typeStr) != -1){ |
|||
formTableField.integer = "255" |
|||
} |
|||
} |
|||
</script> |
|||
<template> |
|||
<el-dialog |
|||
v-model="isShow" |
|||
width="550" |
|||
:title="dialogTitle" |
|||
append-to-body |
|||
draggable |
|||
@close="closeDialog" |
|||
> |
|||
<el-form :model="formTableField" label-width="80px" border> |
|||
<el-form-item label="字段"> |
|||
{{ formTableField.field }} |
|||
</el-form-item> |
|||
<el-form-item label="类型"> |
|||
<el-select v-model="formTableField.type" class="m-2" placeholder="请选择数据类型" @change="judgeFieldType(formTableField.type)"> |
|||
<el-option |
|||
v-for="item in fieldType" |
|||
:key="item" |
|||
:label="item" |
|||
:value="item" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item v-if="unsignedIsShow" label="无符号"> |
|||
<el-radio-group v-model="formTableField.attribute" class="ml-4"> |
|||
<el-radio label="unsigned" size="large">正数</el-radio> |
|||
<el-radio label="" size="large">区分正负</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="不是Null"> |
|||
<el-radio-group v-model="formTableField.null" class="ml-4"> |
|||
<el-radio label="YES" size="large">YES</el-radio> |
|||
<el-radio label="NO" size="large">NO</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item v-if="fieldLenghtIsShow" label="长度"> |
|||
<el-input v-model="formTableField.integer" /> |
|||
</el-form-item> |
|||
<el-form-item v-if="decimalIsShow" label="小数点"> |
|||
<el-input v-model="formTableField.decimal" /> |
|||
</el-form-item> |
|||
<el-form-item label="默认值"> |
|||
<el-input v-model="formTableField.default" /> |
|||
</el-form-item> |
|||
<el-form-item label="备注"> |
|||
<el-input v-model="formTableField.comment" type="textarea" /> |
|||
</el-form-item> |
|||
</el-form> |
|||
<template #footer> |
|||
<span class="dialog-footer"> |
|||
<el-button @click="closeDialog">取消</el-button> |
|||
<el-button type="primary" @click="sendFormData">提交</el-button> |
|||
</span> |
|||
</template> |
|||
</el-dialog> |
|||
</template> |
|||
<style lang='scss' scoped> |
|||
|
|||
</style> |
|||
@ -0,0 +1,14 @@ |
|||
<!-- |
|||
@ 作者: 秦东 |
|||
@ 时间: 2023-09-21 13:41:04 |
|||
@ 备注: 任务 |
|||
--> |
|||
<script lang='ts' setup> |
|||
|
|||
</script> |
|||
<template> |
|||
<div>任务列表</div> |
|||
</template> |
|||
<style lang='scss' scoped> |
|||
|
|||
</style> |
|||
Loading…
Reference in new issue