13 changed files with 2340 additions and 52 deletions
File diff suppressed because it is too large
@ -0,0 +1,257 @@ |
|||||
|
<!-- |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-02-24 13:22:18 |
||||
|
@ 备注: 控件库 |
||||
|
--> |
||||
|
<script lang='ts' setup> |
||||
|
import controlListData from "@/components/DesignForm/assembly"; |
||||
|
import { jsonParseStringify } from "@/utils/DesignForm"; |
||||
|
import Draggable from "vuedraggable-es"; |
||||
|
import "@/assets/iconfont/iconfont.css"; |
||||
|
|
||||
|
import SvgIcon from "@/components/SvgIcon/index.vue"; |
||||
|
import FormVersion from "@/components/DesignForm/formVersion.vue"; |
||||
|
const props = defineProps({ |
||||
|
tableKey: { |
||||
|
type: String, |
||||
|
default: "" |
||||
|
}, |
||||
|
signCode: { |
||||
|
type: String, |
||||
|
default: "" |
||||
|
} |
||||
|
}) |
||||
|
// const props = withDefaults( |
||||
|
// defineProps<{ |
||||
|
// tableKey?: number | string; |
||||
|
// signCode?: number | string; |
||||
|
// }>(), |
||||
|
// {} |
||||
|
// ); |
||||
|
const emits = defineEmits<{ |
||||
|
(e: "versionUpdateForm", value: string): void; |
||||
|
(e: "versionPreviewPage", value: string): void; |
||||
|
}>(); |
||||
|
|
||||
|
const designType = inject("formDesignType") as string; //注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。 |
||||
|
const versionActiveTab = ref(false); |
||||
|
const tableVersion = ref(); //版本显示器 |
||||
|
const isSearch = computed(() => { |
||||
|
return designType === "search"; |
||||
|
}); |
||||
|
// 默认搜索允许显示的字段 |
||||
|
const searchField = [ |
||||
|
"input", |
||||
|
"radio", |
||||
|
"checkbox", |
||||
|
"select", |
||||
|
"datePicker", |
||||
|
"timePicker", |
||||
|
"inputNumber", |
||||
|
"cascader", |
||||
|
"component", |
||||
|
"button", |
||||
|
]; |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-05-09 11:36:59 |
||||
|
@ 功能: 启用和禁用版本 |
||||
|
*/ |
||||
|
const enableOrDisable = (val?: any) => { |
||||
|
// console.log("启用和禁用版本",val) |
||||
|
emits("versionUpdateForm", val); |
||||
|
}; |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-05-21 09:05:20 |
||||
|
@ 功能: 预览版本 |
||||
|
*/ |
||||
|
const previewPage = (val?: any) => { |
||||
|
emits("versionPreviewPage", val); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-02-24 13:45:38 |
||||
|
@ 功能: 表单组件数据 |
||||
|
*/ |
||||
|
const controlList = computed(() => { |
||||
|
if (designType === "search") { |
||||
|
// 只返回基础字段 |
||||
|
const temp: any = []; |
||||
|
controlListData.forEach((item: any) => { |
||||
|
if (item.children) { |
||||
|
const filter = item.children.filter((ch: any) => { |
||||
|
return searchField.includes(ch.type); |
||||
|
}); |
||||
|
if (filter && filter.length) { |
||||
|
temp.push({ title: item.title, children: filter }); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
return temp; |
||||
|
} else { |
||||
|
return controlListData; |
||||
|
} |
||||
|
}); |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-05-09 11:40:15 |
||||
|
@ 功能: 打开版本选择页面 |
||||
|
*/ |
||||
|
const useVersionClick = () => { |
||||
|
tableVersion.value.open(); |
||||
|
}; |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-05-15 10:15:14 |
||||
|
@ 功能: 克隆选中的组件数据结构 |
||||
|
*/ |
||||
|
const clone = (origin: any) => { |
||||
|
// console.log("克隆选中的组件数据结构",origin) |
||||
|
return jsonParseStringify(origin); |
||||
|
}; |
||||
|
</script> |
||||
|
<template> |
||||
|
<el-card shadow="always"> |
||||
|
<template #header> |
||||
|
<div class="cardHeader"> |
||||
|
<div class="card-header">控件库</div> |
||||
|
<div v-if="!isSearch" class="card-headerRight" @click="useVersionClick">版本管理</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<el-scrollbar ref="scrollbarRef" class="scroBox "> |
||||
|
<div v-for="(list, index) in controlList" :key="index" v-memo="[index]" class="unitBox"> |
||||
|
<el-text class="category-title">{{ list.title }}</el-text> |
||||
|
<el-divider style="margin: 5px 0 10px 0;" /> |
||||
|
<div class="unitBody"> |
||||
|
<Draggable |
||||
|
v-model="list.children" |
||||
|
tag="ul" |
||||
|
:group="{ name: 'form', pull: 'clone', put: false }" |
||||
|
ghost-class="ghost" |
||||
|
:sort="false" |
||||
|
:clone="clone" |
||||
|
itemKey="unitListKey" |
||||
|
> |
||||
|
<template #item="{ element }"> |
||||
|
<div class="unitItem"> |
||||
|
<SvgIcon v-if="element.iconFont == ''" icon-class="caogaoxiang" class="control-icon" /> |
||||
|
<i v-if="element.iconFont != ''" :class="`fa ${element.iconFont} `"></i> |
||||
|
<el-text class="control-name">{{element.label}}</el-text> |
||||
|
</div> |
||||
|
</template> |
||||
|
</Draggable> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</el-scrollbar> |
||||
|
<FormVersion |
||||
|
ref="tableVersion" |
||||
|
:table-key="props.tableKey" |
||||
|
:sign-code="props.signCode" |
||||
|
@enable-or-disable="enableOrDisable" |
||||
|
@preview-page="previewPage" |
||||
|
/> |
||||
|
</el-card> |
||||
|
</template> |
||||
|
<style lang='scss' scoped> |
||||
|
.form-content { |
||||
|
width: 100%; |
||||
|
display: grid; |
||||
|
grid-template-columns: 300px 1fr 300px; |
||||
|
grid-template-rows: auto; |
||||
|
gap: 10px; |
||||
|
padding: 10px 10px 0 10px; |
||||
|
:deep .el-tabs--border-card{ |
||||
|
border: 0; |
||||
|
} |
||||
|
:deep .el-card__header{ |
||||
|
padding: 10px 15px; |
||||
|
background-color: #f5f7fa; |
||||
|
} |
||||
|
:deep .el-card__body{ |
||||
|
padding: 0; |
||||
|
|
||||
|
} |
||||
|
:deep .el-tabs__header{ |
||||
|
justify-content: space-between; |
||||
|
width: 100%; |
||||
|
} |
||||
|
:deep .is-active{ |
||||
|
border-bottom: 1px solid var(--el-color-primary); |
||||
|
} |
||||
|
} |
||||
|
.cardHeader{ |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.card-header{ |
||||
|
font-size: 1.4rem; |
||||
|
font-weight: 700; |
||||
|
color: #0020C2; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 10px; |
||||
|
} |
||||
|
.category-title { |
||||
|
font-size: 14px; |
||||
|
font-weight: 600; |
||||
|
color: #165DFF; |
||||
|
margin-bottom: 10px; |
||||
|
padding-bottom: 8px; |
||||
|
border-bottom: 1px solid #ebeef5; |
||||
|
} |
||||
|
|
||||
|
.scroBox{ |
||||
|
height: calc(100vh - 110px); |
||||
|
overflow: auto; |
||||
|
} |
||||
|
|
||||
|
// 布局区域 |
||||
|
|
||||
|
.unitBox{ |
||||
|
padding: 10px 15px; |
||||
|
} |
||||
|
.unitBody ul{ |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
grid-gap: 10px; |
||||
|
} |
||||
|
.unitItem{ |
||||
|
background: #f5f7fa; |
||||
|
border: 1px solid #dcdfe6; |
||||
|
border-radius: 4px; |
||||
|
padding: 10px 5px; /* 减小内边距以适应3列布局 */ |
||||
|
text-align: center; |
||||
|
cursor: grab; |
||||
|
transition: all 0.2s; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
&:hover{ |
||||
|
background: #ecf5ff; |
||||
|
transform: translateY(-2px); |
||||
|
box-shadow: 0 2px 12px 0 rgba(22, 93, 255, 0.1); |
||||
|
border-color: #b3d8ff; |
||||
|
color: #165DFF; |
||||
|
} |
||||
|
i{ |
||||
|
font-size: 20px; /* 减小图标大小以适应3列布局 */ |
||||
|
color: #165DFF; |
||||
|
margin: 5px 0; /* 减小间距以适应3列布局 */ |
||||
|
} |
||||
|
} |
||||
|
.control-icon { |
||||
|
font-size: 25px; /* 减小图标大小以适应3列布局 */ |
||||
|
color: #165DFF; |
||||
|
margin-bottom: 3px; /* 减小间距以适应3列布局 */ |
||||
|
} |
||||
|
|
||||
|
.control-name { |
||||
|
font-size: 12px; /* 减小字体大小以适应3列布局 */ |
||||
|
color: #606266; |
||||
|
line-height: 1.2; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,241 @@ |
|||||
|
<!-- |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-02-24 13:23:08 |
||||
|
@ 备注: 设计区域 |
||||
|
--> |
||||
|
<script lang='ts' setup> |
||||
|
import "@/assets/scss/element-var.scss"; |
||||
|
import "@/assets/scss/index.scss"; |
||||
|
import { FormData, FormList, FormDataStyle } from "@/api/DesignForm/types"; |
||||
|
import { |
||||
|
constGetControlByName, |
||||
|
constSetFormOptions, |
||||
|
constFormBtnEvent, |
||||
|
constControlChange, |
||||
|
constFormProps, |
||||
|
appendOrRemoveStyle, |
||||
|
constAiEffect, |
||||
|
} from "@/api/DesignForm/utils"; |
||||
|
|
||||
|
import SvgIcon from "@/components/SvgIcon/index.vue"; |
||||
|
import { useRoute } from "vue-router"; |
||||
|
|
||||
|
|
||||
|
const props = withDefaults( |
||||
|
defineProps<{ |
||||
|
formData: FormData; |
||||
|
type?: number; // 1新增;2修改;3查看(表单模式) ;4查看; 5设计 |
||||
|
numrun?: number; //1:获取;非1:不变 |
||||
|
disabled?: boolean; // 禁用表单提交 |
||||
|
requestUrl?: string; // 编辑数据请求url |
||||
|
beforeRequest?: Function; // 请求编辑数据前参数处理方法,可对请求参数处理 |
||||
|
afterResponse?: Function | string; // 请求数据加载完成后数据处理方法,可对返回数据处理 |
||||
|
addUrl?: string; // 表单数据新增提交保存url |
||||
|
editUrl?: string; // 表单数据修改保存提交url |
||||
|
beforeSubmit?: Function | string; // 表单提交前数据处理,可对提交数据处理,新增和保存都会触发 |
||||
|
afterSubmit?: Function; // 表单提交后,默认提示提交结果,可return false阻止提示 |
||||
|
closeAppSubmit?: Function; //关闭拉窗 |
||||
|
changeKeyVal?: Function; //监听表单值该表 |
||||
|
anewSubmit?: Function; //重新提交表单 |
||||
|
saveDraftPage?: Function; //保存草稿 |
||||
|
saveEditFormInfo?: Function; //保存草稿 只改表单不操作流程 |
||||
|
sendDraftSubmit?: Function; //草稿提交审批 |
||||
|
submitEdit?: Function; //提交修改数据申请 |
||||
|
value?: { [key: string]: any }; // 表单初始值,同setValue |
||||
|
options?: { [key: string]: any }; // 表单组件选项,同setOptions |
||||
|
dict?: object; // 固定匹配的字典 |
||||
|
isSearch?: boolean; // 列表里作为筛选使用 |
||||
|
isWorkFlow?: number; // |
||||
|
flowkey?: string; // |
||||
|
groupid?: string; // |
||||
|
signCode?: string; // |
||||
|
versionId?: string; // |
||||
|
mastesformjson?: string; // |
||||
|
isWeb: boolean; // |
||||
|
nodeKey?: string; |
||||
|
purview?: any[]; |
||||
|
}>(), |
||||
|
{ |
||||
|
type: 1, // 1新增;2修改;3查看(表单模式) ;4查看; 5设计 |
||||
|
numrun: 2, |
||||
|
formData: () => { |
||||
|
return { |
||||
|
list: [], |
||||
|
form: {}, |
||||
|
config: { |
||||
|
groupKey: '', |
||||
|
style: "", |
||||
|
}, |
||||
|
styles: { |
||||
|
divStyle: {}, |
||||
|
labelStyle: {}, |
||||
|
inputStyle: {}, |
||||
|
}, |
||||
|
aiConfig: [], |
||||
|
}; |
||||
|
}, |
||||
|
dict: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
isSearch: false, |
||||
|
issave: { |
||||
|
type: Boolean, |
||||
|
default: true, |
||||
|
}, |
||||
|
key: 1, |
||||
|
isWorkFlow: 2, |
||||
|
flowkey: "", |
||||
|
groupid: "", |
||||
|
signCode: "", |
||||
|
mastesformjson: "", |
||||
|
} |
||||
|
); |
||||
|
const emits = defineEmits<{ |
||||
|
(e: "btnClick", type: string): void; |
||||
|
(e: "change", val: any): void; // 表单组件值发生变化时 |
||||
|
(e: "update:issave", type: boolean): void; |
||||
|
(e: "refresh"): void; |
||||
|
(e: "optionsValue3Get3", val: any, fieldName: string): void; |
||||
|
(e: 'click', value: string): void; |
||||
|
}>(); |
||||
|
const route = useRoute(); |
||||
|
const rangedUserTrees1= ref([]); |
||||
|
// 处理表单值开始 |
||||
|
const model = ref<any>({}); |
||||
|
// 设置全局事件结束 |
||||
|
const resultDict = ref({}); |
||||
|
|
||||
|
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); |
||||
|
|
||||
|
const pickPageType = ref("PC"); |
||||
|
|
||||
|
|
||||
|
function optionsValue3Get2(data: any, fieldName: string) { |
||||
|
//console.log("form.vue","optionsValue3Get2") |
||||
|
emits("optionsValue3Get3", data, fieldName); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2026-02-25 10:51:00 |
||||
|
@ 功能: 执行哪个按钮的操作3579.76 2509.81 |
||||
|
2162.87 2783.09 |
||||
|
*/ |
||||
|
const btnClick = (type: string) => { |
||||
|
if(type === 'monitor'){ |
||||
|
pickPageType.value = 'PC' |
||||
|
}else if(type === 'iphone'){ |
||||
|
pickPageType.value = 'WEB' |
||||
|
} |
||||
|
emits('click', type) |
||||
|
} |
||||
|
</script> |
||||
|
<template> |
||||
|
<el-card shadow="always"> |
||||
|
<template #header> |
||||
|
<div class="card-header">设计区域</div> |
||||
|
</template> |
||||
|
<div class="design-toolbar"> |
||||
|
<el-button-group> |
||||
|
<el-button class="fa fa-television" :type="pickPageType === 'PC' ? 'primary' : ''" size="small" @click="btnClick('monitor')">电脑端</el-button> |
||||
|
<el-button class="fa fa-mobile" :type="pickPageType === 'WEB' ? 'primary' : ''" size="small" @click="btnClick('iphone')">移动端</el-button> |
||||
|
</el-button-group> |
||||
|
<el-button-group> |
||||
|
<el-button class="fa fa-trash-o" size="small" @click="btnClick('del')">清空</el-button> |
||||
|
<el-button class="fa fa-eye" size="small" @click="btnClick('eye')">预览</el-button> |
||||
|
<el-button class="fa fa-file-code-o" size="small" @click="btnClick('json')">脚本预览</el-button> |
||||
|
<el-button class="fa fa-save" type="success" size="small" @click="btnClick('save')">保存</el-button> |
||||
|
<el-button class="fa fa-code-fork" type="warning" size="small" @click="btnClick('branch')">另存未新版本</el-button> |
||||
|
</el-button-group> |
||||
|
</div> |
||||
|
<div class="design-area" id="designArea"> |
||||
|
<div v-if="formData.list.length === 0" class="placeholder" id="emptyPlaceholder"> |
||||
|
<i class="fa fa-magic"></i> |
||||
|
<p>从左侧拖拽控件到此处开始设计表单</p> |
||||
|
</div> |
||||
|
<el-form |
||||
|
v-bind="formData.form" |
||||
|
ref="ruleForm" |
||||
|
|
||||
|
:disabled="disabled || type === 3" |
||||
|
class="add-form" |
||||
|
:class="{ |
||||
|
'design-form': type === 5, |
||||
|
'detail-form': type === 3 || type === 4 || type === 1, |
||||
|
}" |
||||
|
> |
||||
|
|
||||
|
<FormGroup |
||||
|
:tableinfo="formData.form" |
||||
|
:numrun="numrun" |
||||
|
:data="formData.list" |
||||
|
:alldata="formData" |
||||
|
:node-key="nodeKey" |
||||
|
:purview="purview" |
||||
|
:org-and-man-tree="rangedUserTrees1" |
||||
|
@options-value3-get2="optionsValue3Get2" |
||||
|
/> |
||||
|
|
||||
|
<slot></slot> |
||||
|
</el-form> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</template> |
||||
|
<style lang='scss' scoped> |
||||
|
.design-toolbar { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
padding: 10px 10px; |
||||
|
border-bottom: 1px solid #ebeef5; |
||||
|
background: #f5f7fa; |
||||
|
:deep .el-button>span{ |
||||
|
margin-left: 5px; |
||||
|
} |
||||
|
} |
||||
|
.toolbar-right { |
||||
|
display: flex; |
||||
|
gap: 0px; |
||||
|
} |
||||
|
|
||||
|
.design-area { |
||||
|
flex: 1; |
||||
|
background: #fafafa; |
||||
|
border: 2px dashed #c0c4cc; |
||||
|
border-radius: 4px; |
||||
|
padding: 20px; |
||||
|
margin: 10px; |
||||
|
height: calc(100vh - 170px); |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
.placeholder { |
||||
|
text-align: center; |
||||
|
color: #909399; |
||||
|
padding: 40px 20px; |
||||
|
} |
||||
|
|
||||
|
.placeholder i { |
||||
|
font-size: 48px; |
||||
|
margin-bottom: 15px; |
||||
|
opacity: 0.7; |
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue