1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183 |
- <!DOCTYPE html>
- <html lang="zh" xmlns:th="http://www.thymeleaf.org">
- <head>
- <th:block th:include="include :: header('编辑表单模板')" />
- <th:block th:include="include :: bootstrap-select-css" />
- <th:block th:include="include :: bootstrap-select-js" />
- <th:block th:include="include :: bootstrap-fileinput-css" />
- <th:block th:include="include :: bootstrap-fileinput-js" />
- <th:block th:include="include :: datetimepicker-css" />
- <th:block th:include="include :: datetimepicker-js" />
- <th:block th:include="include :: summernote-css" />
- <th:block th:include="include :: summernote-js"/>
- <style>
- .form-designer-container {
- display: flex;
- min-height: 600px;
- }
- .components-panel {
- width: 250px;
- border-right: 1px solid #ddd;
- padding: 10px;
- background-color: #f8f8f8;
- }
- .form-canvas {
- flex: 1;
- padding: 20px;
- background-color: #ffffff;
- overflow: auto;
- }
- .property-panel {
- width: 300px;
- border-left: 1px solid #ddd;
- padding: 10px;
- background-color: #f8f8f8;
- }
- .component-item {
- border: 1px solid #ddd;
- margin-bottom: 10px;
- padding: 8px;
- background-color: #fff;
- cursor: move;
- border-radius: 4px;
- }
- .component-item:hover {
- background-color: #f0f0f0;
- }
- .section-item {
- border: 1px dashed #ccc;
- margin-bottom: 15px;
- padding: 15px;
- background-color: #fafafa;
- position: relative;
- }
- .section-header {
- font-weight: bold;
- margin-bottom: 10px;
- padding-bottom: 5px;
- border-bottom: 1px solid #eee;
- display: flex;
- justify-content: space-between;
- }
- .field-item {
- border: 1px solid #eee;
- margin-bottom: 10px;
- padding: 10px;
- background-color: #fff;
- position: relative;
- }
- .field-actions {
- position: absolute;
- right: 5px;
- top: 5px;
- }
- .section-actions {
- display: flex;
- gap: 5px;
- }
- .subtable-item {
- border: 1px solid #ddd;
- margin-top: 15px;
- padding: 10px;
- background-color: #f5f5f5;
- }
- .subtable-header {
- font-weight: bold;
- margin-bottom: 10px;
- padding-bottom: 5px;
- border-bottom: 1px solid #eee;
- display: flex;
- justify-content: space-between;
- }
- .subtable-columns {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- padding: 10px;
- background-color: #ffffff;
- }
- .property-group {
- margin-bottom: 15px;
- border: 1px solid #eee;
- border-radius: 4px;
- overflow: hidden;
- }
- .property-group-title {
- background-color: #f0f0f0;
- padding: 8px;
- font-weight: bold;
- }
- .property-group-content {
- padding: 10px;
- }
- .property-row {
- margin-bottom: 10px;
- }
- .handle {
- cursor: move;
- color: #999;
- margin-right: 5px;
- }
- .form-canvas {
- min-height: 400px;
- }
- .placeholder {
- border: 2px dashed #ccc;
- height: 50px;
- margin: 10px 0;
- background-color: #f9f9f9;
- display: flex;
- justify-content: center;
- align-items: center;
- color: #999;
- }
- .ui-sortable-helper {
- z-index: 9999;
- }
- .section-item.selected, .field-item.selected, .subtable-item.selected {
- border: 2px solid #1890ff;
- box-shadow: 0 0 5px rgba(24, 144, 255, 0.3);
- }
- .field-options-container {
- margin-top: 10px;
- padding: 8px;
- background-color: #f9f9f9;
- border: 1px solid #eee;
- }
- </style>
- </head>
- <body class="white-bg">
- <div class="wrapper wrapper-content animated fadeInRight ibox-content">
- <form class="form-horizontal m" id="form-template-edit">
- <input type="hidden" id="templateId" name="id" th:value="${id}">
- <div class="form-group">
- <label class="col-sm-2 control-label">模板名称:</label>
- <div class="col-sm-4">
- <input class="form-control" type="text" name="template_name" th:value="${template_name}">
- </div>
- <label class="col-sm-2 control-label">模板版本:</label>
- <div class="col-sm-4">
- <input class="form-control" type="text" name="version" th:value="${version}">
- </div>
- </div>
- <div class="form-group">
- <label class="col-sm-2 control-label">模板编码:</label>
- <div class="col-sm-4">
- <input class="form-control" type="text" name="template_code" th:value="${template_code}">
- </div>
- <label class="col-sm-2 control-label">模板类型:</label>
- <div class="col-sm-4">
- <select name="template_type" class="form-control selectpicker">
- <option value="常规随访" th:selected="${template_type == '常规随访'}">常规随访</option>
- <option value="脱落召回" th:selected="${template_type == '脱落召回'}">脱落召回</option>
- <option value="其他" th:selected="${template_type == '其他'}">其他</option>
- </select>
- </div>
- </div>
- <div class="form-group">
- <label class="col-sm-2 control-label">模板描述:</label>
- <div class="col-sm-10">
- <textarea name="description" class="form-control" rows="2" th:text="${description}"></textarea>
- </div>
- </div>
- <div class="form-designer-container">
- <!-- 组件面板 -->
- <div class="components-panel">
- <h4>组件</h4>
- <div class="panel-group" id="componentAccordion">
- <div class="panel panel-default">
- <div class="panel-heading">
- <h4 class="panel-title">
- <a data-toggle="collapse" data-parent="#componentAccordion" href="#layoutComponents">
- 布局组件
- </a>
- </h4>
- </div>
- <div id="layoutComponents" class="panel-collapse collapse in">
- <div class="panel-body">
- <div class="component-item" data-type="section">
- <i class="fa fa-columns"></i> 分组
- </div>
- <div class="component-item" data-type="subtable">
- <i class="fa fa-table"></i> 子表
- </div>
- </div>
- </div>
- </div>
- <div class="panel panel-default">
- <div class="panel-heading">
- <h4 class="panel-title">
- <a data-toggle="collapse" data-parent="#componentAccordion" href="#basicComponents">
- 基础组件
- </a>
- </h4>
- </div>
- <div id="basicComponents" class="panel-collapse collapse in">
- <div class="panel-body">
- <div class="component-item" data-type="text">
- <i class="fa fa-font"></i> 单行文本
- </div>
- <div class="component-item" data-type="textarea">
- <i class="fa fa-align-left"></i> 多行文本
- </div>
- <div class="component-item" data-type="number">
- <i class="fa fa-calculator"></i> 数字
- </div>
- <div class="component-item" data-type="select">
- <i class="fa fa-list"></i> 下拉选择
- </div>
- <div class="component-item" data-type="radio">
- <i class="fa fa-dot-circle-o"></i> 单选按钮
- </div>
- <div class="component-item" data-type="checkbox">
- <i class="fa fa-check-square-o"></i> 复选框
- </div>
- <div class="component-item" data-type="date">
- <i class="fa fa-calendar"></i> 日期
- </div>
- <div class="component-item" data-type="datetime">
- <i class="fa fa-clock-o"></i> 日期时间
- </div>
- <div class="component-item" data-type="file">
- <i class="fa fa-file"></i> 文件上传
- </div>
- <div class="component-item" data-type="image">
- <i class="fa fa-picture-o"></i> 图片上传
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <!-- 表单设计画布 -->
- <div class="form-canvas" id="formCanvas">
- <div id="formSections">
- <!-- 表单分组和字段将在这里动态加载 -->
- </div>
- </div>
- <!-- 属性面板 -->
- <div class="property-panel" id="propertyPanel">
- <h4>属性</h4>
- <div class="alert alert-warning">
- 选择表单元素来编辑其属性
- </div>
- <!-- 属性编辑表单将在这里动态创建 -->
- </div>
- </div>
- <div class="row">
- <div class="col-sm-offset-5 col-sm-10 mt20">
- <button type="button" class="btn btn-sm btn-primary" onclick="submitForm()"><i class="fa fa-check"></i>保 存</button>
- <button type="button" class="btn btn-sm btn-danger" onclick="$.modal.closeTab()"><i class="fa fa-close"></i>关 闭</button>
- </div>
- </div>
- </form>
- </div>
- <th:block th:include="include :: footer" />
- <script th:inline="javascript">
- var prefix = ctx + "form/template";
- /*<![CDATA[*/
- var formData = /*[[${formData}]]*/ '';
- /*]]>*/
- debugger;
- console.log(formData);
- var formEditor = {
- currentSelectedElement: null,
- sectionCounter: 0,
- fieldCounter: 0,
- subtableCounter: 0,
- init: function() {
- // 初始化拖拽功能
- this.initDragAndDrop();
- // 加载已有表单数据
- this.loadExistingForm();
- // 绑定事件
- this.bindEvents();
- },
- initDragAndDrop: function() {
- // 使组件可拖拽
- $(".component-item").draggable({
- helper: "clone",
- connectToSortable: "#formSections"
- });
- // 使画布可接收拖拽
- $("#formSections").sortable({
- placeholder: "placeholder",
- handle: ".section-drag-handle",
- update: function(event, ui) {
- if (ui.item.hasClass('component-item')) {
- var type = ui.item.data('type');
- if (type === 'section') {
- var sectionHtml = formEditor.createSection();
- ui.item.replaceWith(sectionHtml);
- } else {
- ui.item.remove();
- }
- }
- }
- });
- },
- loadExistingForm: function() {
- // 清空画布
- $("#formSections").empty();
- // 如果有表单数据,加载显示
- if (formData && formData.sections) {
- this.sectionCounter = 0;
- this.fieldCounter = 0;
- this.subtableCounter = 0;
- // 加载分组
- $.each(formData.sections, function(i, section) {
- formEditor.sectionCounter++;
- var sectionId = "section_" + formEditor.sectionCounter;
- var sectionHtml = '<div class="section-item" data-id="' + sectionId + '" data-type="section" ' +
- 'data-section-code="' + section.section_code + '" ' +
- 'data-collapsible="' + (section.is_collapsible === 1 ? "1" : "0") + '" ' +
- 'data-required="' + (section.is_required === 1 ? "1" : "0") + '">' +
- '<div class="section-header">' +
- '<span><i class="fa fa-bars section-drag-handle handle"></i> ' + section.section_name + '</span>' +
- '<div class="section-actions">' +
- '<button type="button" class="btn btn-primary btn-xs btn-add-field"><i class="fa fa-plus"></i> 添加字段</button> ' +
- '<button type="button" class="btn btn-info btn-xs btn-add-subtable"><i class="fa fa-plus"></i> 添加子表</button> ' +
- '<button type="button" class="btn btn-danger btn-xs btn-delete-section"><i class="fa fa-trash"></i></button>' +
- '</div>' +
- '</div>' +
- '<div class="section-fields" data-section-id="' + sectionId + '"></div>' +
- '<div class="section-subtables" data-section-id="' + sectionId + '"></div>' +
- '</div>';
- $("#formSections").append(sectionHtml);
- // 加载字段
- if (section.fields && section.fields.length > 0) {
- $.each(section.fields, function(j, field) {
- formEditor.fieldCounter++;
- var fieldId = "field_" + formEditor.fieldCounter;
- var fieldType = field.field_type || 'text';
- var fieldTypeIcon = '';
- // 根据字段类型选择图标
- switch(fieldType) {
- case 'text': fieldTypeIcon = 'fa-font'; break;
- case 'textarea': fieldTypeIcon = 'fa-align-left'; break;
- case 'number': fieldTypeIcon = 'fa-calculator'; break;
- case 'select': fieldTypeIcon = 'fa-list'; break;
- case 'radio': fieldTypeIcon = 'fa-dot-circle-o'; break;
- case 'checkbox': fieldTypeIcon = 'fa-check-square-o'; break;
- case 'date': fieldTypeIcon = 'fa-calendar'; break;
- case 'datetime': fieldTypeIcon = 'fa-clock-o'; break;
- case 'file': fieldTypeIcon = 'fa-file'; break;
- case 'image': fieldTypeIcon = 'fa-picture-o'; break;
- default: fieldTypeIcon = 'fa-font';
- }
- var fieldHtml = '<div class="field-item" data-id="' + fieldId + '" data-section-id="' + sectionId + '" data-type="field" ' +
- 'data-field-id="' + field.id + '" ' +
- 'data-field-code="' + field.field_code + '" ' +
- 'data-field-name="' + field.field_name + '" ' +
- 'data-field-type="' + fieldType + '" ' +
- 'data-required="' + (field.is_required === 1 ? "1" : "0") + '" ' +
- 'data-placeholder="' + (field.placeholder || "") + '" ' +
- 'data-default="' + (field.default_value || "") + '" ' +
- 'data-help="' + (field.help_text || "") + '" ' +
- 'data-width="' + (field.field_width || "100%") + '" ' +
- 'data-validation="' + (field.validation_rule || "") + '" ' +
- 'data-validation-message="' + (field.validation_message || "") + '" ' +
- 'data-dict="' + (field.dict_type || "") + '">' +
- '<i class="fa ' + fieldTypeIcon + ' field-drag-handle handle"></i> ' +
- '<span class="field-label">' + field.field_label + '</span>' +
- '<div class="field-actions">' +
- '<button type="button" class="btn btn-danger btn-xs btn-delete-field"><i class="fa fa-trash"></i></button>' +
- '</div>';
- // 如果有选项,保存选项数据
- if (field.options && field.options.length > 0) {
- var optionsData = [];
- $.each(field.options, function(k, option) {
- optionsData.push({
- id: option.id || "",
- optionCode: option.option_code || "",
- optionLabel: option.option_label || "",
- optionValue: option.option_value || "",
- isDefault: option.is_default === 1
- });
- });
- fieldHtml += '<input type="hidden" class="field-options-data" value=\'' + JSON.stringify(optionsData) + '\'>';
- }
- fieldHtml += '</div>';
- $(".section-fields[data-section-id='" + sectionId + "']").append(fieldHtml);
- });
- }
- // 加载子表
- if (section.subtables && section.subtables.length > 0) {
- $.each(section.subtables, function(j, subtable) {
- formEditor.subtableCounter++;
- var subtableId = "subtable_" + formEditor.subtableCounter;
- var subtableHtml = '<div class="subtable-item" data-id="' + subtableId + '" data-section-id="' + sectionId + '" data-type="subtable" ' +
- 'data-subtable-id="' + subtable.id + '" ' +
- 'data-subtable-code="' + subtable.subtable_code + '" ' +
- 'data-required="' + (subtable.is_required === 1 ? "1" : "0") + '" ' +
- 'data-add-button-text="' + (subtable.add_button_text || "新增") + '" ' +
- 'data-min-rows="' + (subtable.min_rows || "0") + '" ' +
- 'data-max-rows="' + (subtable.max_rows || "") + '">' +
- '<div class="subtable-header">' +
- '<span>' + subtable.subtable_name + '</span>' +
- '<div class="subtable-actions">' +
- '<button type="button" class="btn btn-primary btn-xs btn-add-subtable-field"><i class="fa fa-plus"></i> 添加列</button> ' +
- '<button type="button" class="btn btn-danger btn-xs btn-delete-subtable"><i class="fa fa-trash"></i></button>' +
- '</div>' +
- '</div>' +
- '<div class="subtable-columns">';
- // 添加固定列
- subtableHtml += '<div class="subtable-column">序号</div>';
- // 添加字段列
- if (subtable.fields && subtable.fields.length > 0) {
- var fieldsData = [];
- $.each(subtable.fields, function(k, field) {
- fieldsData.push({
- id: field.id || "",
- fieldCode: field.field_code || "",
- fieldName: field.field_name || "",
- fieldType: field.field_type || "text",
- fieldLabel: field.field_label || "",
- isRequired: field.is_required === 1,
- displayOrder: field.display_order || 0
- });
- subtableHtml += '<div class="subtable-column" data-field-id="' + field.id + '">' + field.field_label + '</div>';
- });
- // 保存字段数据
- subtableHtml += '<input type="hidden" class="subtable-fields-data" value=\'' + JSON.stringify(fieldsData) + '\'>';
- }
- // 添加操作列
- subtableHtml += '<div class="subtable-column">操作</div>';
- subtableHtml += '</div></div>';
- $(".section-subtables[data-section-id='" + sectionId + "']").append(subtableHtml);
- });
- }
- });
- } else {
- // 如果没有数据,显示提示
- $("#formSections").html('<div class="alert alert-info">拖拽左侧组件到这里来设计表单</div>');
- }
- // 初始化字段排序
- this.initSortable();
- },
- initSortable: function() {
- // 使分组内的字段可排序
- $(".section-fields").sortable({
- handle: ".field-drag-handle",
- placeholder: "placeholder",
- connectWith: ".section-fields"
- });
- // 使子表内的列可排序
- $(".subtable-columns").sortable({
- items: ".subtable-column:not(:first-child):not(:last-child)",
- placeholder: "placeholder"
- });
- },
- bindEvents: function() {
- // 绑定组件点击事件
- $(document).on('click', '.section-item, .field-item, .subtable-item', function(e) {
- e.stopPropagation();
- // 先移除所有已选中的元素
- $('.section-item, .field-item, .subtable-item').removeClass('selected');
- $(this).addClass('selected');
- // 更新当前选中的元素
- formEditor.currentSelectedElement = $(this);
- // 显示属性面板
- formEditor.showProperties($(this));
- });
- // 绑定添加字段按钮事件
- $(document).on('click', '.btn-add-field', function() {
- var sectionId = $(this).closest('.section-item').data('id');
- var fieldHtml = formEditor.createField(sectionId);
- $(this).closest('.section-item').find('.section-fields').append(fieldHtml);
- });
- // 绑定添加子表按钮事件
- $(document).on('click', '.btn-add-subtable', function() {
- var sectionId = $(this).closest('.section-item').data('id');
- var subtableHtml = formEditor.createSubtable(sectionId);
- $(this).closest('.section-item').find('.section-subtables').append(subtableHtml);
- });
- // 绑定删除分组按钮事件
- $(document).on('click', '.btn-delete-section', function() {
- $(this).closest('.section-item').remove();
- $('#propertyPanel').html('<h4>属性</h4><div class="alert alert-warning">选择表单元素来编辑其属性</div>');
- });
- // 绑定删除字段按钮事件
- $(document).on('click', '.btn-delete-field', function() {
- $(this).closest('.field-item').remove();
- $('#propertyPanel').html('<h4>属性</h4><div class="alert alert-warning">选择表单元素来编辑其属性</div>');
- });
- // 绑定删除子表按钮事件
- $(document).on('click', '.btn-delete-subtable', function() {
- $(this).closest('.subtable-item').remove();
- $('#propertyPanel').html('<h4>属性</h4><div class="alert alert-warning">选择表单元素来编辑其属性</div>');
- });
- // 绑定添加子表字段按钮事件
- $(document).on('click', '.btn-add-subtable-field', function() {
- formEditor.addSubtableField($(this).closest('.subtable-item'));
- });
- // 绑定画布点击事件,取消选中
- $(document).on('click', '#formCanvas', function(e) {
- if ($(e.target).is('#formCanvas') || $(e.target).is('#formSections')) {
- $('.section-item, .field-item, .subtable-item').removeClass('selected');
- formEditor.currentSelectedElement = null;
- $('#propertyPanel').html('<h4>属性</h4><div class="alert alert-warning">选择表单元素来编辑其属性</div>');
- }
- });
- },
- createSection: function() {
- this.sectionCounter++;
- var sectionId = "section_" + this.sectionCounter;
- var html = '<div class="section-item" data-id="' + sectionId + '" data-type="section">' +
- '<div class="section-header">' +
- '<span><i class="fa fa-bars section-drag-handle handle"></i> 分组 ' + this.sectionCounter + '</span>' +
- '<div class="section-actions">' +
- '<button type="button" class="btn btn-primary btn-xs btn-add-field"><i class="fa fa-plus"></i> 添加字段</button> ' +
- '<button type="button" class="btn btn-info btn-xs btn-add-subtable"><i class="fa fa-plus"></i> 添加子表</button> ' +
- '<button type="button" class="btn btn-danger btn-xs btn-delete-section"><i class="fa fa-trash"></i></button>' +
- '</div>' +
- '</div>' +
- '<div class="section-fields" data-section-id="' + sectionId + '"></div>' +
- '<div class="section-subtables" data-section-id="' + sectionId + '"></div>' +
- '</div>';
- return html;
- },
- createField: function(sectionId) {
- this.fieldCounter++;
- var fieldId = "field_" + this.fieldCounter;
- var html = '<div class="field-item" data-id="' + fieldId + '" data-section-id="' + sectionId + '" data-type="field" data-field-type="text">' +
- '<i class="fa fa-font field-drag-handle handle"></i> <span class="field-label">字段 ' + this.fieldCounter + '</span>' +
- '<div class="field-actions">' +
- '<button type="button" class="btn btn-danger btn-xs btn-delete-field"><i class="fa fa-trash"></i></button>' +
- '</div>' +
- '</div>';
- return html;
- },
- createSubtable: function(sectionId) {
- this.subtableCounter++;
- var subtableId = "subtable_" + this.subtableCounter;
- var html = '<div class="subtable-item" data-id="' + subtableId + '" data-section-id="' + sectionId + '" data-type="subtable">' +
- '<div class="subtable-header">' +
- '<span>子表 ' + this.subtableCounter + '</span>' +
- '<div class="subtable-actions">' +
- '<button type="button" class="btn btn-primary btn-xs btn-add-subtable-field"><i class="fa fa-plus"></i> 添加列</button> ' +
- '<button type="button" class="btn btn-danger btn-xs btn-delete-subtable"><i class="fa fa-trash"></i></button>' +
- '</div>' +
- '</div>' +
- '<div class="subtable-columns">' +
- '<div class="subtable-column">序号</div>' +
- '<div class="subtable-column">操作</div>' +
- '</div>' +
- '</div>';
- return html;
- },
- addSubtableField: function(subtable) {
- // 打开字段添加对话框
- var subtableId = subtable.data('id');
- // 这里可以使用modal弹窗来添加字段,或者直接添加一个默认字段
- // 简单实现:直接添加一个默认列
- var columnCount = subtable.find('.subtable-column').length - 2; // 减去序号和操作列
- var newColumn = $('<div class="subtable-column">新列 ' + (columnCount + 1) + '</div>');
- // 插入到操作列之前
- subtable.find('.subtable-columns').children().last().before(newColumn);
- // 选中这个字段以便编辑属性
- newColumn.trigger('click');
- },
- showProperties: function(element) {
- var type = element.data('type');
- var propertyHtml = '';
- if (type === 'section') {
- propertyHtml = this.getSectionProperties(element);
- } else if (type === 'field') {
- propertyHtml = this.getFieldProperties(element);
- } else if (type === 'subtable') {
- propertyHtml = this.getSubtableProperties(element);
- }
- $('#propertyPanel').html('<h4>属性</h4>' + propertyHtml);
- // 初始化下拉框等UI组件
- $('.selectpicker').selectpicker();
- },
- getSectionProperties: function(section) {
- var sectionName = section.find('.section-header span').text().trim().replace(/^.*?\s/, '');
- var isCollapsible = section.data('collapsible') === '1';
- var isRequired = section.data('required') === '1';
- var sectionCode = section.data('section-code') || '';
- var html = '<div class="property-group">' +
- '<div class="property-group-title">基本属性</div>' +
- '<div class="property-group-content">' +
- '<div class="property-row">' +
- '<label>分组名称</label>' +
- '<input type="text" class="form-control" id="section_name" value="' + sectionName + '" ' +
- 'onchange="formEditor.updateSectionProperty(\'name\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>分组编码</label>' +
- '<input type="text" class="form-control" id="section_code" value="' + sectionCode + '" ' +
- 'onchange="formEditor.updateSectionProperty(\'code\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>是否可折叠</label>' +
- '<select class="form-control selectpicker" id="section_collapsible" ' +
- 'onchange="formEditor.updateSectionProperty(\'collapsible\', this.value)">' +
- '<option value="0" ' + (!isCollapsible ? 'selected' : '') + '>否</option>' +
- '<option value="1" ' + (isCollapsible ? 'selected' : '') + '>是</option>' +
- '</select>' +
- '</div>' +
- '<div class="property-row">' +
- '<label>是否必填分组</label>' +
- '<select class="form-control selectpicker" id="section_required" ' +
- 'onchange="formEditor.updateSectionProperty(\'required\', this.value)">' +
- '<option value="0" ' + (!isRequired ? 'selected' : '') + '>否</option>' +
- '<option value="1" ' + (isRequired ? 'selected' : '') + '>是</option>' +
- '</select>' +
- '</div>' +
- '</div>' +
- '</div>';
- return html;
- },
- getFieldProperties: function(field) {
- var fieldLabel = field.find('.field-label').text().trim();
- var fieldType = field.data('field-type') || 'text';
- var isRequired = field.data('required') === '1';
- var placeholder = field.data('placeholder') || '';
- var defaultValue = field.data('default') || '';
- var helpText = field.data('help') || '';
- var fieldWidth = field.data('width') || '100%';
- var fieldCode = field.data('field-code') || '';
- var fieldName = field.data('field-name') || '';
- var validationRule = field.data('validation') || '';
- var validationMessage = field.data('validation-message') || '';
- var dictType = field.data('dict') || '';
- var html = '<div class="property-group">' +
- '<div class="property-group-title">基本属性</div>' +
- '<div class="property-group-content">' +
- '<div class="property-row">' +
- '<label>字段标签</label>' +
- '<input type="text" class="form-control" id="field_label" value="' + fieldLabel + '" ' +
- 'onchange="formEditor.updateFieldProperty(\'label\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>字段名称</label>' +
- '<input type="text" class="form-control" id="field_name" value="' + fieldName + '" ' +
- 'onchange="formEditor.updateFieldProperty(\'name\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>字段编码</label>' +
- '<input type="text" class="form-control" id="field_code" value="' + fieldCode + '" ' +
- 'onchange="formEditor.updateFieldProperty(\'code\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>字段类型</label>' +
- '<select class="form-control selectpicker" id="field_type" ' +
- 'onchange="formEditor.updateFieldProperty(\'type\', this.value)">' +
- '<option value="text" ' + (fieldType === 'text' ? 'selected' : '') + '>单行文本</option>' +
- '<option value="textarea" ' + (fieldType === 'textarea' ? 'selected' : '') + '>多行文本</option>' +
- '<option value="number" ' + (fieldType === 'number' ? 'selected' : '') + '>数字</option>' +
- '<option value="select" ' + (fieldType === 'select' ? 'selected' : '') + '>下拉选择</option>' +
- '<option value="radio" ' + (fieldType === 'radio' ? 'selected' : '') + '>单选按钮</option>' +
- '<option value="checkbox" ' + (fieldType === 'checkbox' ? 'selected' : '') + '>复选框</option>' +
- '<option value="date" ' + (fieldType === 'date' ? 'selected' : '') + '>日期</option>' +
- '<option value="datetime" ' + (fieldType === 'datetime' ? 'selected' : '') + '>日期时间</option>' +
- '<option value="file" ' + (fieldType === 'file' ? 'selected' : '') + '>文件上传</option>' +
- '<option value="image" ' + (fieldType === 'image' ? 'selected' : '') + '>图片上传</option>' +
- '</select>' +
- '</div>' +
- '<div class="property-row">' +
- '<label>是否必填</label>' +
- '<select class="form-control selectpicker" id="field_required" ' +
- 'onchange="formEditor.updateFieldProperty(\'required\', this.value)">' +
- '<option value="0" ' + (!isRequired ? 'selected' : '') + '>否</option>' +
- '<option value="1" ' + (isRequired ? 'selected' : '') + '>是</option>' +
- '</select>' +
- '</div>' +
- '<div class="property-row">' +
- '<label>默认值</label>' +
- '<input type="text" class="form-control" id="field_default" value="' + defaultValue + '" ' +
- 'onchange="formEditor.updateFieldProperty(\'default\', this.value)">' +
- '</div>' +
- '</div>' +
- '</div>';
- // 对于选择类型字段,添加选项编辑区域
- if (fieldType === 'select' || fieldType === 'radio' || fieldType === 'checkbox') {
- // 获取字段选项数据
- var optionsData = [];
- if (field.find('.field-options-data').length > 0) {
- try {
- optionsData = JSON.parse(field.find('.field-options-data').val());
- } catch (e) {
- console.error('解析选项数据错误', e);
- }
- }
- // 转换为文本
- var optionsText = '';
- if (optionsData.length > 0) {
- $.each(optionsData, function(i, option) {
- optionsText += option.optionValue + '=' + option.optionLabel + '\n';
- });
- }
- html += '<div class="property-group">' +
- '<div class="property-group-title">选项设置</div>' +
- '<div class="property-group-content">' +
- '<div class="property-row">' +
- '<label>选项列表</label>' +
- '<textarea class="form-control" id="field_options" rows="5" ' +
- 'placeholder="每行一个选项,格式:值=标签" ' +
- 'onchange="formEditor.updateFieldProperty(\'options\', this.value)">' + optionsText + '</textarea>' +
- '</div>' +
- '</div>' +
- '</div>';
- }
- html += '<div class="property-group">' +
- '<div class="property-group-title">高级设置</div>' +
- '<div class="property-group-content">' +
- '<div class="property-row">' +
- '<label>字段宽度</label>' +
- '<select class="form-control selectpicker" id="field_width" ' +
- 'onchange="formEditor.updateFieldProperty(\'width\', this.value)">' +
- '<option value="100%" ' + (fieldWidth === '100%' ? 'selected' : '') + '>100%</option>' +
- '<option value="75%" ' + (fieldWidth === '75%' ? 'selected' : '') + '>75%</option>' +
- '<option value="50%" ' + (fieldWidth === '50%' ? 'selected' : '') + '>50%</option>' +
- '<option value="25%" ' + (fieldWidth === '25%' ? 'selected' : '') + '>25%</option>' +
- '</select>' +
- '</div>' +
- '<div class="property-row">' +
- '<label>占位文本</label>' +
- '<input type="text" class="form-control" id="field_placeholder" value="' + placeholder + '" ' +
- 'onchange="formEditor.updateFieldProperty(\'placeholder\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>帮助文本</label>' +
- '<input type="text" class="form-control" id="field_help" value="' + helpText + '" ' +
- 'onchange="formEditor.updateFieldProperty(\'help\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>验证规则</label>' +
- '<input type="text" class="form-control" id="field_validation" value="' + validationRule + '" ' +
- 'onchange="formEditor.updateFieldProperty(\'validation\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>验证提示</label>' +
- '<input type="text" class="form-control" id="field_validation_message" value="' + validationMessage + '" ' +
- 'onchange="formEditor.updateFieldProperty(\'validationMessage\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>数据字典</label>' +
- '<input type="text" class="form-control" id="field_dict" value="' + dictType + '" ' +
- 'onchange="formEditor.updateFieldProperty(\'dict\', this.value)">' +
- '</div>' +
- '</div>' +
- '</div>';
- return html;
- },
- getSubtableProperties: function(subtable) {
- var subtableName = subtable.find('.subtable-header span').text().trim();
- var isRequired = subtable.data('required') === '1';
- var addButtonText = subtable.data('add-button-text') || '新增';
- var minRows = subtable.data('min-rows') || '0';
- var maxRows = subtable.data('max-rows') || '';
- var subtableCode = subtable.data('subtable-code') || '';
- var html = '<div class="property-group">' +
- '<div class="property-group-title">基本属性</div>' +
- '<div class="property-group-content">' +
- '<div class="property-row">' +
- '<label>子表名称</label>' +
- '<input type="text" class="form-control" id="subtable_name" value="' + subtableName + '" ' +
- 'onchange="formEditor.updateSubtableProperty(\'name\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>子表编码</label>' +
- '<input type="text" class="form-control" id="subtable_code" value="' + subtableCode + '" ' +
- 'onchange="formEditor.updateSubtableProperty(\'code\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>添加按钮文本</label>' +
- '<input type="text" class="form-control" id="subtable_add_text" value="' + addButtonText + '" ' +
- 'onchange="formEditor.updateSubtableProperty(\'addButtonText\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>是否必填</label>' +
- '<select class="form-control selectpicker" id="subtable_required" ' +
- 'onchange="formEditor.updateSubtableProperty(\'required\', this.value)">' +
- '<option value="0" ' + (!isRequired ? 'selected' : '') + '>否</option>' +
- '<option value="1" ' + (isRequired ? 'selected' : '') + '>是</option>' +
- '</select>' +
- '</div>' +
- '<div class="property-row">' +
- '<label>最少行数</label>' +
- '<input type="number" class="form-control" id="subtable_min_rows" value="' + minRows + '" min="0" ' +
- 'onchange="formEditor.updateSubtableProperty(\'minRows\', this.value)">' +
- '</div>' +
- '<div class="property-row">' +
- '<label>最多行数</label>' +
- '<input type="number" class="form-control" id="subtable_max_rows" value="' + maxRows + '" min="0" ' +
- 'onchange="formEditor.updateSubtableProperty(\'maxRows\', this.value)">' +
- '</div>' +
- '</div>' +
- '</div>';
- // 显示子表字段列表
- var fieldsData = [];
- if (subtable.find('.subtable-fields-data').length > 0) {
- try {
- fieldsData = JSON.parse(subtable.find('.subtable-fields-data').val());
- } catch (e) {
- console.error('解析子表字段数据错误', e);
- }
- }
- if (fieldsData.length > 0) {
- html += '<div class="property-group">' +
- '<div class="property-group-title">字段列表</div>' +
- '<div class="property-group-content">' +
- '<div class="table-responsive">' +
- '<table class="table table-bordered">' +
- '<thead><tr><th>标签</th><th>名称</th><th>类型</th><th>必填</th></tr></thead>' +
- '<tbody>';
- $.each(fieldsData, function(i, field) {
- html += '<tr>' +
- '<td>' + field.fieldLabel + '</td>' +
- '<td>' + field.fieldName + '</td>' +
- '<td>' + field.fieldType + '</td>' +
- '<td>' + (field.isRequired ? '是' : '否') + '</td>' +
- '</tr>';
- });
- html += '</tbody></table></div></div></div>';
- }
- return html;
- },
- updateSectionProperty: function(property, value) {
- if (!this.currentSelectedElement) return;
- var section = this.currentSelectedElement;
- if (property === 'name') {
- section.find('.section-header span').text(value);
- } else if (property === 'code') {
- section.data('section-code', value);
- } else if (property === 'collapsible') {
- section.data('collapsible', value);
- } else if (property === 'required') {
- section.data('required', value);
- }
- },
- updateFieldProperty: function(property, value) {
- if (!this.currentSelectedElement) return;
- var field = this.currentSelectedElement;
- if (property === 'label') {
- field.find('.field-label').text(value);
- } else if (property === 'name') {
- field.data('field-name', value);
- } else if (property === 'code') {
- field.data('field-code', value);
- } else if (property === 'type') {
- field.data('field-type', value);
- // 更新字段图标
- var icon = 'fa-font';
- switch(value) {
- case 'textarea': icon = 'fa-align-left'; break;
- case 'number': icon = 'fa-calculator'; break;
- case 'select': icon = 'fa-list'; break;
- case 'radio': icon = 'fa-dot-circle-o'; break;
- case 'checkbox': icon = 'fa-check-square-o'; break;
- case 'date': icon = 'fa-calendar'; break;
- case 'datetime': icon = 'fa-clock-o'; break;
- case 'file': icon = 'fa-file'; break;
- case 'image': icon = 'fa-picture-o'; break;
- }
- field.find('.field-drag-handle').removeClass().addClass('fa ' + icon + ' field-drag-handle handle');
- // 刷新属性面板以显示类型相关的设置
- this.showProperties(field);
- } else if (property === 'required') {
- field.data('required', value);
- } else if (property === 'default') {
- field.data('default', value);
- } else if (property === 'options') {
- // 解析选项文本
- var optionsLines = value.split('\n');
- var options = [];
- $.each(optionsLines, function(i, line) {
- if (line.trim()) {
- var parts = line.split('=');
- var optionValue = parts[0].trim();
- var optionLabel = parts.length > 1 ? parts[1].trim() : optionValue;
- options.push({
- optionCode: 'option_' + (Math.floor(Math.random() * 10000)),
- optionLabel: optionLabel,
- optionValue: optionValue,
- isDefault: false
- });
- }
- });
- // 更新字段的选项数据
- field.find('.field-options-data').remove();
- field.append('<input type="hidden" class="field-options-data" value=\'' + JSON.stringify(options) + '\'>');
- } else if (property === 'width') {
- field.data('width', value);
- } else if (property === 'placeholder') {
- field.data('placeholder', value);
- } else if (property === 'help') {
- field.data('help', value);
- } else if (property === 'validation') {
- field.data('validation', value);
- } else if (property === 'validationMessage') {
- field.data('validation-message', value);
- } else if (property === 'dict') {
- field.data('dict', value);
- }
- },
- updateSubtableProperty: function(property, value) {
- if (!this.currentSelectedElement) return;
- var subtable = this.currentSelectedElement;
- if (property === 'name') {
- subtable.find('.subtable-header span').text(value);
- } else if (property === 'code') {
- subtable.data('subtable-code', value);
- } else if (property === 'addButtonText') {
- subtable.data('add-button-text', value);
- } else if (property === 'required') {
- subtable.data('required', value);
- } else if (property === 'minRows') {
- subtable.data('min-rows', value);
- } else if (property === 'maxRows') {
- subtable.data('max-rows', value);
- }
- },
- getFormData: function() {
- var formData = {
- sections: []
- };
- // 遍历所有分组
- $('.section-item').each(function(sectionIndex) {
- var section = $(this);
- var sectionData = {
- id: section.data('section-id') || '',
- section_code: section.data('section-code') || ('section_' + (sectionIndex + 1)),
- section_name: section.find('.section-header span').text().trim(),
- section_type: 'normal',
- display_order: sectionIndex,
- is_collapsible: section.data('collapsible') === '1' ? 1 : 0,
- is_required: section.data('required') === '1' ? 1 : 0,
- fields: [],
- subtables: []
- };
- // 遍历分组内的字段
- section.find('.field-item').each(function(fieldIndex) {
- var field = $(this);
- var fieldType = field.data('field-type') || 'text';
- var fieldData = {
- id: field.data('field-id') || '',
- field_code: field.data('field-code') || ('field_' + (sectionIndex + 1) + '_' + (fieldIndex + 1)),
- field_name: field.data('field-name') || ('field_' + (sectionIndex + 1) + '_' + (fieldIndex + 1)),
- field_type: fieldType,
- field_label: field.find('.field-label').text().trim(),
- placeholder: field.data('placeholder') || '',
- default_value: field.data('default') || '',
- is_required: field.data('required') === '1' ? 1 : 0,
- validation_rule: field.data('validation') || '',
- validation_message: field.data('validation-message') || '',
- dict_type: field.data('dict') || '',
- display_order: fieldIndex,
- field_width: field.data('width') || '100%',
- help_text: field.data('help') || '',
- is_editable: 1
- };
- // 处理选项
- if (fieldType === 'select' || fieldType === 'radio' || fieldType === 'checkbox') {
- fieldData.options = [];
- if (field.find('.field-options-data').length > 0) {
- try {
- var optionsData = JSON.parse(field.find('.field-options-data').val());
- $.each(optionsData, function(i, option) {
- fieldData.options.push({
- id: option.id || '',
- option_code: option.optionCode || ('option_' + (fieldIndex + 1) + '_' + (i + 1)),
- option_label: option.optionLabel,
- option_value: option.optionValue,
- display_order: i,
- is_default: option.isDefault ? 1 : 0
- });
- });
- } catch (e) {
- console.error('处理字段选项时出错', e);
- }
- }
- }
- sectionData.fields.push(fieldData);
- });
- // 遍历分组内的子表
- section.find('.subtable-item').each(function(subtableIndex) {
- var subtable = $(this);
- var subtableData = {
- id: subtable.data('subtable-id') || '',
- subtable_code: subtable.data('subtable-code') || ('subtable_' + (sectionIndex + 1) + '_' + (subtableIndex + 1)),
- subtable_name: subtable.find('.subtable-header span').text().trim(),
- display_order: subtableIndex,
- add_button_text: subtable.data('add-button-text') || '新增',
- is_required: subtable.data('required') === '1' ? 1 : 0,
- min_rows: parseInt(subtable.data('min-rows') || '0'),
- max_rows: subtable.data('max-rows') ? parseInt(subtable.data('max-rows')) : null,
- fields: []
- };
- // 处理子表字段
- if (subtable.find('.subtable-fields-data').length > 0) {
- try {
- var fieldsData = JSON.parse(subtable.find('.subtable-fields-data').val());
- $.each(fieldsData, function(i, field) {
- subtableData.fields.push({
- id: field.id || '',
- field_code: field.fieldCode || ('subtable_field_' + (subtableIndex + 1) + '_' + (i + 1)),
- field_name: field.fieldName || ('subtable_field_' + (subtableIndex + 1) + '_' + (i + 1)),
- field_type: field.fieldType || 'text',
- field_label: field.fieldLabel,
- is_required: field.isRequired ? 1 : 0,
- display_order: field.displayOrder || i,
- is_editable: 1
- });
- });
- } catch (e) {
- console.error('处理子表字段数据时出错', e);
- }
- }
- sectionData.subtables.push(subtableData);
- });
- formData.sections.push(sectionData);
- });
- return formData;
- }
- };
- // 初始化表单编辑器
- $(function() {
- formEditor.init();
- });
- function submitForm() {
- // 获取表单基本数据
- var templateId = $("#templateId").val();
- var templateName = $("input[name='template_name']").val();
- var templateCode = $("input[name='template_code']").val();
- var templateType = $("select[name='template_type']").val();
- var version = $("input[name='version']").val();
- var description = $("textarea[name='description']").val();
- // 获取表单设计数据
- var formDesignData = formEditor.getFormData();
- // 构建完整表单数据
- var data = {
- "id": templateId,
- "template_name": templateName,
- "template_code": templateCode,
- "template_type": templateType,
- "version": version,
- "description": description,
- "formData": formDesignData
- };
- // 提交表单
- $.operate.save(prefix + "/edit", data);
- }
- </script>
- </body>
- </html>
|