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