formTemplateEdit.html 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183
  1. <!DOCTYPE html>
  2. <html lang="zh" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <th:block th:include="include :: header('编辑表单模板')" />
  5. <th:block th:include="include :: bootstrap-select-css" />
  6. <th:block th:include="include :: bootstrap-select-js" />
  7. <th:block th:include="include :: bootstrap-fileinput-css" />
  8. <th:block th:include="include :: bootstrap-fileinput-js" />
  9. <th:block th:include="include :: datetimepicker-css" />
  10. <th:block th:include="include :: datetimepicker-js" />
  11. <th:block th:include="include :: summernote-css" />
  12. <th:block th:include="include :: summernote-js"/>
  13. <style>
  14. .form-designer-container {
  15. display: flex;
  16. min-height: 600px;
  17. }
  18. .components-panel {
  19. width: 250px;
  20. border-right: 1px solid #ddd;
  21. padding: 10px;
  22. background-color: #f8f8f8;
  23. }
  24. .form-canvas {
  25. flex: 1;
  26. padding: 20px;
  27. background-color: #ffffff;
  28. overflow: auto;
  29. }
  30. .property-panel {
  31. width: 300px;
  32. border-left: 1px solid #ddd;
  33. padding: 10px;
  34. background-color: #f8f8f8;
  35. }
  36. .component-item {
  37. border: 1px solid #ddd;
  38. margin-bottom: 10px;
  39. padding: 8px;
  40. background-color: #fff;
  41. cursor: move;
  42. border-radius: 4px;
  43. }
  44. .component-item:hover {
  45. background-color: #f0f0f0;
  46. }
  47. .section-item {
  48. border: 1px dashed #ccc;
  49. margin-bottom: 15px;
  50. padding: 15px;
  51. background-color: #fafafa;
  52. position: relative;
  53. }
  54. .section-header {
  55. font-weight: bold;
  56. margin-bottom: 10px;
  57. padding-bottom: 5px;
  58. border-bottom: 1px solid #eee;
  59. display: flex;
  60. justify-content: space-between;
  61. }
  62. .field-item {
  63. border: 1px solid #eee;
  64. margin-bottom: 10px;
  65. padding: 10px;
  66. background-color: #fff;
  67. position: relative;
  68. }
  69. .field-actions {
  70. position: absolute;
  71. right: 5px;
  72. top: 5px;
  73. }
  74. .section-actions {
  75. display: flex;
  76. gap: 5px;
  77. }
  78. .subtable-item {
  79. border: 1px solid #ddd;
  80. margin-top: 15px;
  81. padding: 10px;
  82. background-color: #f5f5f5;
  83. }
  84. .subtable-header {
  85. font-weight: bold;
  86. margin-bottom: 10px;
  87. padding-bottom: 5px;
  88. border-bottom: 1px solid #eee;
  89. display: flex;
  90. justify-content: space-between;
  91. }
  92. .subtable-columns {
  93. display: flex;
  94. flex-wrap: wrap;
  95. gap: 10px;
  96. padding: 10px;
  97. background-color: #ffffff;
  98. }
  99. .property-group {
  100. margin-bottom: 15px;
  101. border: 1px solid #eee;
  102. border-radius: 4px;
  103. overflow: hidden;
  104. }
  105. .property-group-title {
  106. background-color: #f0f0f0;
  107. padding: 8px;
  108. font-weight: bold;
  109. }
  110. .property-group-content {
  111. padding: 10px;
  112. }
  113. .property-row {
  114. margin-bottom: 10px;
  115. }
  116. .handle {
  117. cursor: move;
  118. color: #999;
  119. margin-right: 5px;
  120. }
  121. .form-canvas {
  122. min-height: 400px;
  123. }
  124. .placeholder {
  125. border: 2px dashed #ccc;
  126. height: 50px;
  127. margin: 10px 0;
  128. background-color: #f9f9f9;
  129. display: flex;
  130. justify-content: center;
  131. align-items: center;
  132. color: #999;
  133. }
  134. .ui-sortable-helper {
  135. z-index: 9999;
  136. }
  137. .section-item.selected, .field-item.selected, .subtable-item.selected {
  138. border: 2px solid #1890ff;
  139. box-shadow: 0 0 5px rgba(24, 144, 255, 0.3);
  140. }
  141. .field-options-container {
  142. margin-top: 10px;
  143. padding: 8px;
  144. background-color: #f9f9f9;
  145. border: 1px solid #eee;
  146. }
  147. </style>
  148. </head>
  149. <body class="white-bg">
  150. <div class="wrapper wrapper-content animated fadeInRight ibox-content">
  151. <form class="form-horizontal m" id="form-template-edit">
  152. <input type="hidden" id="templateId" name="id" th:value="${id}">
  153. <div class="form-group">
  154. <label class="col-sm-2 control-label">模板名称:</label>
  155. <div class="col-sm-4">
  156. <input class="form-control" type="text" name="template_name" th:value="${template_name}">
  157. </div>
  158. <label class="col-sm-2 control-label">模板版本:</label>
  159. <div class="col-sm-4">
  160. <input class="form-control" type="text" name="version" th:value="${version}">
  161. </div>
  162. </div>
  163. <div class="form-group">
  164. <label class="col-sm-2 control-label">模板编码:</label>
  165. <div class="col-sm-4">
  166. <input class="form-control" type="text" name="template_code" th:value="${template_code}">
  167. </div>
  168. <label class="col-sm-2 control-label">模板类型:</label>
  169. <div class="col-sm-4">
  170. <select name="template_type" class="form-control selectpicker">
  171. <option value="常规随访" th:selected="${template_type == '常规随访'}">常规随访</option>
  172. <option value="脱落召回" th:selected="${template_type == '脱落召回'}">脱落召回</option>
  173. <option value="其他" th:selected="${template_type == '其他'}">其他</option>
  174. </select>
  175. </div>
  176. </div>
  177. <div class="form-group">
  178. <label class="col-sm-2 control-label">模板描述:</label>
  179. <div class="col-sm-10">
  180. <textarea name="description" class="form-control" rows="2" th:text="${description}"></textarea>
  181. </div>
  182. </div>
  183. <div class="form-designer-container">
  184. <!-- 组件面板 -->
  185. <div class="components-panel">
  186. <h4>组件</h4>
  187. <div class="panel-group" id="componentAccordion">
  188. <div class="panel panel-default">
  189. <div class="panel-heading">
  190. <h4 class="panel-title">
  191. <a data-toggle="collapse" data-parent="#componentAccordion" href="#layoutComponents">
  192. 布局组件
  193. </a>
  194. </h4>
  195. </div>
  196. <div id="layoutComponents" class="panel-collapse collapse in">
  197. <div class="panel-body">
  198. <div class="component-item" data-type="section">
  199. <i class="fa fa-columns"></i> 分组
  200. </div>
  201. <div class="component-item" data-type="subtable">
  202. <i class="fa fa-table"></i> 子表
  203. </div>
  204. </div>
  205. </div>
  206. </div>
  207. <div class="panel panel-default">
  208. <div class="panel-heading">
  209. <h4 class="panel-title">
  210. <a data-toggle="collapse" data-parent="#componentAccordion" href="#basicComponents">
  211. 基础组件
  212. </a>
  213. </h4>
  214. </div>
  215. <div id="basicComponents" class="panel-collapse collapse in">
  216. <div class="panel-body">
  217. <div class="component-item" data-type="text">
  218. <i class="fa fa-font"></i> 单行文本
  219. </div>
  220. <div class="component-item" data-type="textarea">
  221. <i class="fa fa-align-left"></i> 多行文本
  222. </div>
  223. <div class="component-item" data-type="number">
  224. <i class="fa fa-calculator"></i> 数字
  225. </div>
  226. <div class="component-item" data-type="select">
  227. <i class="fa fa-list"></i> 下拉选择
  228. </div>
  229. <div class="component-item" data-type="radio">
  230. <i class="fa fa-dot-circle-o"></i> 单选按钮
  231. </div>
  232. <div class="component-item" data-type="checkbox">
  233. <i class="fa fa-check-square-o"></i> 复选框
  234. </div>
  235. <div class="component-item" data-type="date">
  236. <i class="fa fa-calendar"></i> 日期
  237. </div>
  238. <div class="component-item" data-type="datetime">
  239. <i class="fa fa-clock-o"></i> 日期时间
  240. </div>
  241. <div class="component-item" data-type="file">
  242. <i class="fa fa-file"></i> 文件上传
  243. </div>
  244. <div class="component-item" data-type="image">
  245. <i class="fa fa-picture-o"></i> 图片上传
  246. </div>
  247. </div>
  248. </div>
  249. </div>
  250. </div>
  251. </div>
  252. <!-- 表单设计画布 -->
  253. <div class="form-canvas" id="formCanvas">
  254. <div id="formSections">
  255. <!-- 表单分组和字段将在这里动态加载 -->
  256. </div>
  257. </div>
  258. <!-- 属性面板 -->
  259. <div class="property-panel" id="propertyPanel">
  260. <h4>属性</h4>
  261. <div class="alert alert-warning">
  262. 选择表单元素来编辑其属性
  263. </div>
  264. <!-- 属性编辑表单将在这里动态创建 -->
  265. </div>
  266. </div>
  267. <div class="row">
  268. <div class="col-sm-offset-5 col-sm-10 mt20">
  269. <button type="button" class="btn btn-sm btn-primary" onclick="submitForm()"><i class="fa fa-check"></i>保 存</button>&nbsp;
  270. <button type="button" class="btn btn-sm btn-danger" onclick="$.modal.closeTab()"><i class="fa fa-close"></i>关 闭</button>
  271. </div>
  272. </div>
  273. </form>
  274. </div>
  275. <th:block th:include="include :: footer" />
  276. <script th:inline="javascript">
  277. var prefix = ctx + "form/template";
  278. /*<![CDATA[*/
  279. var formData = /*[[${formData}]]*/ '';
  280. /*]]>*/
  281. debugger;
  282. console.log(formData);
  283. var formEditor = {
  284. currentSelectedElement: null,
  285. sectionCounter: 0,
  286. fieldCounter: 0,
  287. subtableCounter: 0,
  288. init: function() {
  289. // 初始化拖拽功能
  290. this.initDragAndDrop();
  291. // 加载已有表单数据
  292. this.loadExistingForm();
  293. // 绑定事件
  294. this.bindEvents();
  295. },
  296. initDragAndDrop: function() {
  297. // 使组件可拖拽
  298. $(".component-item").draggable({
  299. helper: "clone",
  300. connectToSortable: "#formSections"
  301. });
  302. // 使画布可接收拖拽
  303. $("#formSections").sortable({
  304. placeholder: "placeholder",
  305. handle: ".section-drag-handle",
  306. update: function(event, ui) {
  307. if (ui.item.hasClass('component-item')) {
  308. var type = ui.item.data('type');
  309. if (type === 'section') {
  310. var sectionHtml = formEditor.createSection();
  311. ui.item.replaceWith(sectionHtml);
  312. } else {
  313. ui.item.remove();
  314. }
  315. }
  316. }
  317. });
  318. },
  319. loadExistingForm: function() {
  320. // 清空画布
  321. $("#formSections").empty();
  322. // 如果有表单数据,加载显示
  323. if (formData && formData.sections) {
  324. this.sectionCounter = 0;
  325. this.fieldCounter = 0;
  326. this.subtableCounter = 0;
  327. // 加载分组
  328. $.each(formData.sections, function(i, section) {
  329. formEditor.sectionCounter++;
  330. var sectionId = "section_" + formEditor.sectionCounter;
  331. var sectionHtml = '<div class="section-item" data-id="' + sectionId + '" data-type="section" ' +
  332. 'data-section-code="' + section.section_code + '" ' +
  333. 'data-collapsible="' + (section.is_collapsible === 1 ? "1" : "0") + '" ' +
  334. 'data-required="' + (section.is_required === 1 ? "1" : "0") + '">' +
  335. '<div class="section-header">' +
  336. '<span><i class="fa fa-bars section-drag-handle handle"></i> ' + section.section_name + '</span>' +
  337. '<div class="section-actions">' +
  338. '<button type="button" class="btn btn-primary btn-xs btn-add-field"><i class="fa fa-plus"></i> 添加字段</button> ' +
  339. '<button type="button" class="btn btn-info btn-xs btn-add-subtable"><i class="fa fa-plus"></i> 添加子表</button> ' +
  340. '<button type="button" class="btn btn-danger btn-xs btn-delete-section"><i class="fa fa-trash"></i></button>' +
  341. '</div>' +
  342. '</div>' +
  343. '<div class="section-fields" data-section-id="' + sectionId + '"></div>' +
  344. '<div class="section-subtables" data-section-id="' + sectionId + '"></div>' +
  345. '</div>';
  346. $("#formSections").append(sectionHtml);
  347. // 加载字段
  348. if (section.fields && section.fields.length > 0) {
  349. $.each(section.fields, function(j, field) {
  350. formEditor.fieldCounter++;
  351. var fieldId = "field_" + formEditor.fieldCounter;
  352. var fieldType = field.field_type || 'text';
  353. var fieldTypeIcon = '';
  354. // 根据字段类型选择图标
  355. switch(fieldType) {
  356. case 'text': fieldTypeIcon = 'fa-font'; break;
  357. case 'textarea': fieldTypeIcon = 'fa-align-left'; break;
  358. case 'number': fieldTypeIcon = 'fa-calculator'; break;
  359. case 'select': fieldTypeIcon = 'fa-list'; break;
  360. case 'radio': fieldTypeIcon = 'fa-dot-circle-o'; break;
  361. case 'checkbox': fieldTypeIcon = 'fa-check-square-o'; break;
  362. case 'date': fieldTypeIcon = 'fa-calendar'; break;
  363. case 'datetime': fieldTypeIcon = 'fa-clock-o'; break;
  364. case 'file': fieldTypeIcon = 'fa-file'; break;
  365. case 'image': fieldTypeIcon = 'fa-picture-o'; break;
  366. default: fieldTypeIcon = 'fa-font';
  367. }
  368. var fieldHtml = '<div class="field-item" data-id="' + fieldId + '" data-section-id="' + sectionId + '" data-type="field" ' +
  369. 'data-field-id="' + field.id + '" ' +
  370. 'data-field-code="' + field.field_code + '" ' +
  371. 'data-field-name="' + field.field_name + '" ' +
  372. 'data-field-type="' + fieldType + '" ' +
  373. 'data-required="' + (field.is_required === 1 ? "1" : "0") + '" ' +
  374. 'data-placeholder="' + (field.placeholder || "") + '" ' +
  375. 'data-default="' + (field.default_value || "") + '" ' +
  376. 'data-help="' + (field.help_text || "") + '" ' +
  377. 'data-width="' + (field.field_width || "100%") + '" ' +
  378. 'data-validation="' + (field.validation_rule || "") + '" ' +
  379. 'data-validation-message="' + (field.validation_message || "") + '" ' +
  380. 'data-dict="' + (field.dict_type || "") + '">' +
  381. '<i class="fa ' + fieldTypeIcon + ' field-drag-handle handle"></i> ' +
  382. '<span class="field-label">' + field.field_label + '</span>' +
  383. '<div class="field-actions">' +
  384. '<button type="button" class="btn btn-danger btn-xs btn-delete-field"><i class="fa fa-trash"></i></button>' +
  385. '</div>';
  386. // 如果有选项,保存选项数据
  387. if (field.options && field.options.length > 0) {
  388. var optionsData = [];
  389. $.each(field.options, function(k, option) {
  390. optionsData.push({
  391. id: option.id || "",
  392. optionCode: option.option_code || "",
  393. optionLabel: option.option_label || "",
  394. optionValue: option.option_value || "",
  395. isDefault: option.is_default === 1
  396. });
  397. });
  398. fieldHtml += '<input type="hidden" class="field-options-data" value=\'' + JSON.stringify(optionsData) + '\'>';
  399. }
  400. fieldHtml += '</div>';
  401. $(".section-fields[data-section-id='" + sectionId + "']").append(fieldHtml);
  402. });
  403. }
  404. // 加载子表
  405. if (section.subtables && section.subtables.length > 0) {
  406. $.each(section.subtables, function(j, subtable) {
  407. formEditor.subtableCounter++;
  408. var subtableId = "subtable_" + formEditor.subtableCounter;
  409. var subtableHtml = '<div class="subtable-item" data-id="' + subtableId + '" data-section-id="' + sectionId + '" data-type="subtable" ' +
  410. 'data-subtable-id="' + subtable.id + '" ' +
  411. 'data-subtable-code="' + subtable.subtable_code + '" ' +
  412. 'data-required="' + (subtable.is_required === 1 ? "1" : "0") + '" ' +
  413. 'data-add-button-text="' + (subtable.add_button_text || "新增") + '" ' +
  414. 'data-min-rows="' + (subtable.min_rows || "0") + '" ' +
  415. 'data-max-rows="' + (subtable.max_rows || "") + '">' +
  416. '<div class="subtable-header">' +
  417. '<span>' + subtable.subtable_name + '</span>' +
  418. '<div class="subtable-actions">' +
  419. '<button type="button" class="btn btn-primary btn-xs btn-add-subtable-field"><i class="fa fa-plus"></i> 添加列</button> ' +
  420. '<button type="button" class="btn btn-danger btn-xs btn-delete-subtable"><i class="fa fa-trash"></i></button>' +
  421. '</div>' +
  422. '</div>' +
  423. '<div class="subtable-columns">';
  424. // 添加固定列
  425. subtableHtml += '<div class="subtable-column">序号</div>';
  426. // 添加字段列
  427. if (subtable.fields && subtable.fields.length > 0) {
  428. var fieldsData = [];
  429. $.each(subtable.fields, function(k, field) {
  430. fieldsData.push({
  431. id: field.id || "",
  432. fieldCode: field.field_code || "",
  433. fieldName: field.field_name || "",
  434. fieldType: field.field_type || "text",
  435. fieldLabel: field.field_label || "",
  436. isRequired: field.is_required === 1,
  437. displayOrder: field.display_order || 0
  438. });
  439. subtableHtml += '<div class="subtable-column" data-field-id="' + field.id + '">' + field.field_label + '</div>';
  440. });
  441. // 保存字段数据
  442. subtableHtml += '<input type="hidden" class="subtable-fields-data" value=\'' + JSON.stringify(fieldsData) + '\'>';
  443. }
  444. // 添加操作列
  445. subtableHtml += '<div class="subtable-column">操作</div>';
  446. subtableHtml += '</div></div>';
  447. $(".section-subtables[data-section-id='" + sectionId + "']").append(subtableHtml);
  448. });
  449. }
  450. });
  451. } else {
  452. // 如果没有数据,显示提示
  453. $("#formSections").html('<div class="alert alert-info">拖拽左侧组件到这里来设计表单</div>');
  454. }
  455. // 初始化字段排序
  456. this.initSortable();
  457. },
  458. initSortable: function() {
  459. // 使分组内的字段可排序
  460. $(".section-fields").sortable({
  461. handle: ".field-drag-handle",
  462. placeholder: "placeholder",
  463. connectWith: ".section-fields"
  464. });
  465. // 使子表内的列可排序
  466. $(".subtable-columns").sortable({
  467. items: ".subtable-column:not(:first-child):not(:last-child)",
  468. placeholder: "placeholder"
  469. });
  470. },
  471. bindEvents: function() {
  472. // 绑定组件点击事件
  473. $(document).on('click', '.section-item, .field-item, .subtable-item', function(e) {
  474. e.stopPropagation();
  475. // 先移除所有已选中的元素
  476. $('.section-item, .field-item, .subtable-item').removeClass('selected');
  477. $(this).addClass('selected');
  478. // 更新当前选中的元素
  479. formEditor.currentSelectedElement = $(this);
  480. // 显示属性面板
  481. formEditor.showProperties($(this));
  482. });
  483. // 绑定添加字段按钮事件
  484. $(document).on('click', '.btn-add-field', function() {
  485. var sectionId = $(this).closest('.section-item').data('id');
  486. var fieldHtml = formEditor.createField(sectionId);
  487. $(this).closest('.section-item').find('.section-fields').append(fieldHtml);
  488. });
  489. // 绑定添加子表按钮事件
  490. $(document).on('click', '.btn-add-subtable', function() {
  491. var sectionId = $(this).closest('.section-item').data('id');
  492. var subtableHtml = formEditor.createSubtable(sectionId);
  493. $(this).closest('.section-item').find('.section-subtables').append(subtableHtml);
  494. });
  495. // 绑定删除分组按钮事件
  496. $(document).on('click', '.btn-delete-section', function() {
  497. $(this).closest('.section-item').remove();
  498. $('#propertyPanel').html('<h4>属性</h4><div class="alert alert-warning">选择表单元素来编辑其属性</div>');
  499. });
  500. // 绑定删除字段按钮事件
  501. $(document).on('click', '.btn-delete-field', function() {
  502. $(this).closest('.field-item').remove();
  503. $('#propertyPanel').html('<h4>属性</h4><div class="alert alert-warning">选择表单元素来编辑其属性</div>');
  504. });
  505. // 绑定删除子表按钮事件
  506. $(document).on('click', '.btn-delete-subtable', function() {
  507. $(this).closest('.subtable-item').remove();
  508. $('#propertyPanel').html('<h4>属性</h4><div class="alert alert-warning">选择表单元素来编辑其属性</div>');
  509. });
  510. // 绑定添加子表字段按钮事件
  511. $(document).on('click', '.btn-add-subtable-field', function() {
  512. formEditor.addSubtableField($(this).closest('.subtable-item'));
  513. });
  514. // 绑定画布点击事件,取消选中
  515. $(document).on('click', '#formCanvas', function(e) {
  516. if ($(e.target).is('#formCanvas') || $(e.target).is('#formSections')) {
  517. $('.section-item, .field-item, .subtable-item').removeClass('selected');
  518. formEditor.currentSelectedElement = null;
  519. $('#propertyPanel').html('<h4>属性</h4><div class="alert alert-warning">选择表单元素来编辑其属性</div>');
  520. }
  521. });
  522. },
  523. createSection: function() {
  524. this.sectionCounter++;
  525. var sectionId = "section_" + this.sectionCounter;
  526. var html = '<div class="section-item" data-id="' + sectionId + '" data-type="section">' +
  527. '<div class="section-header">' +
  528. '<span><i class="fa fa-bars section-drag-handle handle"></i> 分组 ' + this.sectionCounter + '</span>' +
  529. '<div class="section-actions">' +
  530. '<button type="button" class="btn btn-primary btn-xs btn-add-field"><i class="fa fa-plus"></i> 添加字段</button> ' +
  531. '<button type="button" class="btn btn-info btn-xs btn-add-subtable"><i class="fa fa-plus"></i> 添加子表</button> ' +
  532. '<button type="button" class="btn btn-danger btn-xs btn-delete-section"><i class="fa fa-trash"></i></button>' +
  533. '</div>' +
  534. '</div>' +
  535. '<div class="section-fields" data-section-id="' + sectionId + '"></div>' +
  536. '<div class="section-subtables" data-section-id="' + sectionId + '"></div>' +
  537. '</div>';
  538. return html;
  539. },
  540. createField: function(sectionId) {
  541. this.fieldCounter++;
  542. var fieldId = "field_" + this.fieldCounter;
  543. var html = '<div class="field-item" data-id="' + fieldId + '" data-section-id="' + sectionId + '" data-type="field" data-field-type="text">' +
  544. '<i class="fa fa-font field-drag-handle handle"></i> <span class="field-label">字段 ' + this.fieldCounter + '</span>' +
  545. '<div class="field-actions">' +
  546. '<button type="button" class="btn btn-danger btn-xs btn-delete-field"><i class="fa fa-trash"></i></button>' +
  547. '</div>' +
  548. '</div>';
  549. return html;
  550. },
  551. createSubtable: function(sectionId) {
  552. this.subtableCounter++;
  553. var subtableId = "subtable_" + this.subtableCounter;
  554. var html = '<div class="subtable-item" data-id="' + subtableId + '" data-section-id="' + sectionId + '" data-type="subtable">' +
  555. '<div class="subtable-header">' +
  556. '<span>子表 ' + this.subtableCounter + '</span>' +
  557. '<div class="subtable-actions">' +
  558. '<button type="button" class="btn btn-primary btn-xs btn-add-subtable-field"><i class="fa fa-plus"></i> 添加列</button> ' +
  559. '<button type="button" class="btn btn-danger btn-xs btn-delete-subtable"><i class="fa fa-trash"></i></button>' +
  560. '</div>' +
  561. '</div>' +
  562. '<div class="subtable-columns">' +
  563. '<div class="subtable-column">序号</div>' +
  564. '<div class="subtable-column">操作</div>' +
  565. '</div>' +
  566. '</div>';
  567. return html;
  568. },
  569. addSubtableField: function(subtable) {
  570. // 打开字段添加对话框
  571. var subtableId = subtable.data('id');
  572. // 这里可以使用modal弹窗来添加字段,或者直接添加一个默认字段
  573. // 简单实现:直接添加一个默认列
  574. var columnCount = subtable.find('.subtable-column').length - 2; // 减去序号和操作列
  575. var newColumn = $('<div class="subtable-column">新列 ' + (columnCount + 1) + '</div>');
  576. // 插入到操作列之前
  577. subtable.find('.subtable-columns').children().last().before(newColumn);
  578. // 选中这个字段以便编辑属性
  579. newColumn.trigger('click');
  580. },
  581. showProperties: function(element) {
  582. var type = element.data('type');
  583. var propertyHtml = '';
  584. if (type === 'section') {
  585. propertyHtml = this.getSectionProperties(element);
  586. } else if (type === 'field') {
  587. propertyHtml = this.getFieldProperties(element);
  588. } else if (type === 'subtable') {
  589. propertyHtml = this.getSubtableProperties(element);
  590. }
  591. $('#propertyPanel').html('<h4>属性</h4>' + propertyHtml);
  592. // 初始化下拉框等UI组件
  593. $('.selectpicker').selectpicker();
  594. },
  595. getSectionProperties: function(section) {
  596. var sectionName = section.find('.section-header span').text().trim().replace(/^.*?\s/, '');
  597. var isCollapsible = section.data('collapsible') === '1';
  598. var isRequired = section.data('required') === '1';
  599. var sectionCode = section.data('section-code') || '';
  600. var html = '<div class="property-group">' +
  601. '<div class="property-group-title">基本属性</div>' +
  602. '<div class="property-group-content">' +
  603. '<div class="property-row">' +
  604. '<label>分组名称</label>' +
  605. '<input type="text" class="form-control" id="section_name" value="' + sectionName + '" ' +
  606. 'onchange="formEditor.updateSectionProperty(\'name\', this.value)">' +
  607. '</div>' +
  608. '<div class="property-row">' +
  609. '<label>分组编码</label>' +
  610. '<input type="text" class="form-control" id="section_code" value="' + sectionCode + '" ' +
  611. 'onchange="formEditor.updateSectionProperty(\'code\', this.value)">' +
  612. '</div>' +
  613. '<div class="property-row">' +
  614. '<label>是否可折叠</label>' +
  615. '<select class="form-control selectpicker" id="section_collapsible" ' +
  616. 'onchange="formEditor.updateSectionProperty(\'collapsible\', this.value)">' +
  617. '<option value="0" ' + (!isCollapsible ? 'selected' : '') + '>否</option>' +
  618. '<option value="1" ' + (isCollapsible ? 'selected' : '') + '>是</option>' +
  619. '</select>' +
  620. '</div>' +
  621. '<div class="property-row">' +
  622. '<label>是否必填分组</label>' +
  623. '<select class="form-control selectpicker" id="section_required" ' +
  624. 'onchange="formEditor.updateSectionProperty(\'required\', this.value)">' +
  625. '<option value="0" ' + (!isRequired ? 'selected' : '') + '>否</option>' +
  626. '<option value="1" ' + (isRequired ? 'selected' : '') + '>是</option>' +
  627. '</select>' +
  628. '</div>' +
  629. '</div>' +
  630. '</div>';
  631. return html;
  632. },
  633. getFieldProperties: function(field) {
  634. var fieldLabel = field.find('.field-label').text().trim();
  635. var fieldType = field.data('field-type') || 'text';
  636. var isRequired = field.data('required') === '1';
  637. var placeholder = field.data('placeholder') || '';
  638. var defaultValue = field.data('default') || '';
  639. var helpText = field.data('help') || '';
  640. var fieldWidth = field.data('width') || '100%';
  641. var fieldCode = field.data('field-code') || '';
  642. var fieldName = field.data('field-name') || '';
  643. var validationRule = field.data('validation') || '';
  644. var validationMessage = field.data('validation-message') || '';
  645. var dictType = field.data('dict') || '';
  646. var html = '<div class="property-group">' +
  647. '<div class="property-group-title">基本属性</div>' +
  648. '<div class="property-group-content">' +
  649. '<div class="property-row">' +
  650. '<label>字段标签</label>' +
  651. '<input type="text" class="form-control" id="field_label" value="' + fieldLabel + '" ' +
  652. 'onchange="formEditor.updateFieldProperty(\'label\', this.value)">' +
  653. '</div>' +
  654. '<div class="property-row">' +
  655. '<label>字段名称</label>' +
  656. '<input type="text" class="form-control" id="field_name" value="' + fieldName + '" ' +
  657. 'onchange="formEditor.updateFieldProperty(\'name\', this.value)">' +
  658. '</div>' +
  659. '<div class="property-row">' +
  660. '<label>字段编码</label>' +
  661. '<input type="text" class="form-control" id="field_code" value="' + fieldCode + '" ' +
  662. 'onchange="formEditor.updateFieldProperty(\'code\', this.value)">' +
  663. '</div>' +
  664. '<div class="property-row">' +
  665. '<label>字段类型</label>' +
  666. '<select class="form-control selectpicker" id="field_type" ' +
  667. 'onchange="formEditor.updateFieldProperty(\'type\', this.value)">' +
  668. '<option value="text" ' + (fieldType === 'text' ? 'selected' : '') + '>单行文本</option>' +
  669. '<option value="textarea" ' + (fieldType === 'textarea' ? 'selected' : '') + '>多行文本</option>' +
  670. '<option value="number" ' + (fieldType === 'number' ? 'selected' : '') + '>数字</option>' +
  671. '<option value="select" ' + (fieldType === 'select' ? 'selected' : '') + '>下拉选择</option>' +
  672. '<option value="radio" ' + (fieldType === 'radio' ? 'selected' : '') + '>单选按钮</option>' +
  673. '<option value="checkbox" ' + (fieldType === 'checkbox' ? 'selected' : '') + '>复选框</option>' +
  674. '<option value="date" ' + (fieldType === 'date' ? 'selected' : '') + '>日期</option>' +
  675. '<option value="datetime" ' + (fieldType === 'datetime' ? 'selected' : '') + '>日期时间</option>' +
  676. '<option value="file" ' + (fieldType === 'file' ? 'selected' : '') + '>文件上传</option>' +
  677. '<option value="image" ' + (fieldType === 'image' ? 'selected' : '') + '>图片上传</option>' +
  678. '</select>' +
  679. '</div>' +
  680. '<div class="property-row">' +
  681. '<label>是否必填</label>' +
  682. '<select class="form-control selectpicker" id="field_required" ' +
  683. 'onchange="formEditor.updateFieldProperty(\'required\', this.value)">' +
  684. '<option value="0" ' + (!isRequired ? 'selected' : '') + '>否</option>' +
  685. '<option value="1" ' + (isRequired ? 'selected' : '') + '>是</option>' +
  686. '</select>' +
  687. '</div>' +
  688. '<div class="property-row">' +
  689. '<label>默认值</label>' +
  690. '<input type="text" class="form-control" id="field_default" value="' + defaultValue + '" ' +
  691. 'onchange="formEditor.updateFieldProperty(\'default\', this.value)">' +
  692. '</div>' +
  693. '</div>' +
  694. '</div>';
  695. // 对于选择类型字段,添加选项编辑区域
  696. if (fieldType === 'select' || fieldType === 'radio' || fieldType === 'checkbox') {
  697. // 获取字段选项数据
  698. var optionsData = [];
  699. if (field.find('.field-options-data').length > 0) {
  700. try {
  701. optionsData = JSON.parse(field.find('.field-options-data').val());
  702. } catch (e) {
  703. console.error('解析选项数据错误', e);
  704. }
  705. }
  706. // 转换为文本
  707. var optionsText = '';
  708. if (optionsData.length > 0) {
  709. $.each(optionsData, function(i, option) {
  710. optionsText += option.optionValue + '=' + option.optionLabel + '\n';
  711. });
  712. }
  713. html += '<div class="property-group">' +
  714. '<div class="property-group-title">选项设置</div>' +
  715. '<div class="property-group-content">' +
  716. '<div class="property-row">' +
  717. '<label>选项列表</label>' +
  718. '<textarea class="form-control" id="field_options" rows="5" ' +
  719. 'placeholder="每行一个选项,格式:值=标签" ' +
  720. 'onchange="formEditor.updateFieldProperty(\'options\', this.value)">' + optionsText + '</textarea>' +
  721. '</div>' +
  722. '</div>' +
  723. '</div>';
  724. }
  725. html += '<div class="property-group">' +
  726. '<div class="property-group-title">高级设置</div>' +
  727. '<div class="property-group-content">' +
  728. '<div class="property-row">' +
  729. '<label>字段宽度</label>' +
  730. '<select class="form-control selectpicker" id="field_width" ' +
  731. 'onchange="formEditor.updateFieldProperty(\'width\', this.value)">' +
  732. '<option value="100%" ' + (fieldWidth === '100%' ? 'selected' : '') + '>100%</option>' +
  733. '<option value="75%" ' + (fieldWidth === '75%' ? 'selected' : '') + '>75%</option>' +
  734. '<option value="50%" ' + (fieldWidth === '50%' ? 'selected' : '') + '>50%</option>' +
  735. '<option value="25%" ' + (fieldWidth === '25%' ? 'selected' : '') + '>25%</option>' +
  736. '</select>' +
  737. '</div>' +
  738. '<div class="property-row">' +
  739. '<label>占位文本</label>' +
  740. '<input type="text" class="form-control" id="field_placeholder" value="' + placeholder + '" ' +
  741. 'onchange="formEditor.updateFieldProperty(\'placeholder\', this.value)">' +
  742. '</div>' +
  743. '<div class="property-row">' +
  744. '<label>帮助文本</label>' +
  745. '<input type="text" class="form-control" id="field_help" value="' + helpText + '" ' +
  746. 'onchange="formEditor.updateFieldProperty(\'help\', this.value)">' +
  747. '</div>' +
  748. '<div class="property-row">' +
  749. '<label>验证规则</label>' +
  750. '<input type="text" class="form-control" id="field_validation" value="' + validationRule + '" ' +
  751. 'onchange="formEditor.updateFieldProperty(\'validation\', this.value)">' +
  752. '</div>' +
  753. '<div class="property-row">' +
  754. '<label>验证提示</label>' +
  755. '<input type="text" class="form-control" id="field_validation_message" value="' + validationMessage + '" ' +
  756. 'onchange="formEditor.updateFieldProperty(\'validationMessage\', this.value)">' +
  757. '</div>' +
  758. '<div class="property-row">' +
  759. '<label>数据字典</label>' +
  760. '<input type="text" class="form-control" id="field_dict" value="' + dictType + '" ' +
  761. 'onchange="formEditor.updateFieldProperty(\'dict\', this.value)">' +
  762. '</div>' +
  763. '</div>' +
  764. '</div>';
  765. return html;
  766. },
  767. getSubtableProperties: function(subtable) {
  768. var subtableName = subtable.find('.subtable-header span').text().trim();
  769. var isRequired = subtable.data('required') === '1';
  770. var addButtonText = subtable.data('add-button-text') || '新增';
  771. var minRows = subtable.data('min-rows') || '0';
  772. var maxRows = subtable.data('max-rows') || '';
  773. var subtableCode = subtable.data('subtable-code') || '';
  774. var html = '<div class="property-group">' +
  775. '<div class="property-group-title">基本属性</div>' +
  776. '<div class="property-group-content">' +
  777. '<div class="property-row">' +
  778. '<label>子表名称</label>' +
  779. '<input type="text" class="form-control" id="subtable_name" value="' + subtableName + '" ' +
  780. 'onchange="formEditor.updateSubtableProperty(\'name\', this.value)">' +
  781. '</div>' +
  782. '<div class="property-row">' +
  783. '<label>子表编码</label>' +
  784. '<input type="text" class="form-control" id="subtable_code" value="' + subtableCode + '" ' +
  785. 'onchange="formEditor.updateSubtableProperty(\'code\', this.value)">' +
  786. '</div>' +
  787. '<div class="property-row">' +
  788. '<label>添加按钮文本</label>' +
  789. '<input type="text" class="form-control" id="subtable_add_text" value="' + addButtonText + '" ' +
  790. 'onchange="formEditor.updateSubtableProperty(\'addButtonText\', this.value)">' +
  791. '</div>' +
  792. '<div class="property-row">' +
  793. '<label>是否必填</label>' +
  794. '<select class="form-control selectpicker" id="subtable_required" ' +
  795. 'onchange="formEditor.updateSubtableProperty(\'required\', this.value)">' +
  796. '<option value="0" ' + (!isRequired ? 'selected' : '') + '>否</option>' +
  797. '<option value="1" ' + (isRequired ? 'selected' : '') + '>是</option>' +
  798. '</select>' +
  799. '</div>' +
  800. '<div class="property-row">' +
  801. '<label>最少行数</label>' +
  802. '<input type="number" class="form-control" id="subtable_min_rows" value="' + minRows + '" min="0" ' +
  803. 'onchange="formEditor.updateSubtableProperty(\'minRows\', this.value)">' +
  804. '</div>' +
  805. '<div class="property-row">' +
  806. '<label>最多行数</label>' +
  807. '<input type="number" class="form-control" id="subtable_max_rows" value="' + maxRows + '" min="0" ' +
  808. 'onchange="formEditor.updateSubtableProperty(\'maxRows\', this.value)">' +
  809. '</div>' +
  810. '</div>' +
  811. '</div>';
  812. // 显示子表字段列表
  813. var fieldsData = [];
  814. if (subtable.find('.subtable-fields-data').length > 0) {
  815. try {
  816. fieldsData = JSON.parse(subtable.find('.subtable-fields-data').val());
  817. } catch (e) {
  818. console.error('解析子表字段数据错误', e);
  819. }
  820. }
  821. if (fieldsData.length > 0) {
  822. html += '<div class="property-group">' +
  823. '<div class="property-group-title">字段列表</div>' +
  824. '<div class="property-group-content">' +
  825. '<div class="table-responsive">' +
  826. '<table class="table table-bordered">' +
  827. '<thead><tr><th>标签</th><th>名称</th><th>类型</th><th>必填</th></tr></thead>' +
  828. '<tbody>';
  829. $.each(fieldsData, function(i, field) {
  830. html += '<tr>' +
  831. '<td>' + field.fieldLabel + '</td>' +
  832. '<td>' + field.fieldName + '</td>' +
  833. '<td>' + field.fieldType + '</td>' +
  834. '<td>' + (field.isRequired ? '是' : '否') + '</td>' +
  835. '</tr>';
  836. });
  837. html += '</tbody></table></div></div></div>';
  838. }
  839. return html;
  840. },
  841. updateSectionProperty: function(property, value) {
  842. if (!this.currentSelectedElement) return;
  843. var section = this.currentSelectedElement;
  844. if (property === 'name') {
  845. section.find('.section-header span').text(value);
  846. } else if (property === 'code') {
  847. section.data('section-code', value);
  848. } else if (property === 'collapsible') {
  849. section.data('collapsible', value);
  850. } else if (property === 'required') {
  851. section.data('required', value);
  852. }
  853. },
  854. updateFieldProperty: function(property, value) {
  855. if (!this.currentSelectedElement) return;
  856. var field = this.currentSelectedElement;
  857. if (property === 'label') {
  858. field.find('.field-label').text(value);
  859. } else if (property === 'name') {
  860. field.data('field-name', value);
  861. } else if (property === 'code') {
  862. field.data('field-code', value);
  863. } else if (property === 'type') {
  864. field.data('field-type', value);
  865. // 更新字段图标
  866. var icon = 'fa-font';
  867. switch(value) {
  868. case 'textarea': icon = 'fa-align-left'; break;
  869. case 'number': icon = 'fa-calculator'; break;
  870. case 'select': icon = 'fa-list'; break;
  871. case 'radio': icon = 'fa-dot-circle-o'; break;
  872. case 'checkbox': icon = 'fa-check-square-o'; break;
  873. case 'date': icon = 'fa-calendar'; break;
  874. case 'datetime': icon = 'fa-clock-o'; break;
  875. case 'file': icon = 'fa-file'; break;
  876. case 'image': icon = 'fa-picture-o'; break;
  877. }
  878. field.find('.field-drag-handle').removeClass().addClass('fa ' + icon + ' field-drag-handle handle');
  879. // 刷新属性面板以显示类型相关的设置
  880. this.showProperties(field);
  881. } else if (property === 'required') {
  882. field.data('required', value);
  883. } else if (property === 'default') {
  884. field.data('default', value);
  885. } else if (property === 'options') {
  886. // 解析选项文本
  887. var optionsLines = value.split('\n');
  888. var options = [];
  889. $.each(optionsLines, function(i, line) {
  890. if (line.trim()) {
  891. var parts = line.split('=');
  892. var optionValue = parts[0].trim();
  893. var optionLabel = parts.length > 1 ? parts[1].trim() : optionValue;
  894. options.push({
  895. optionCode: 'option_' + (Math.floor(Math.random() * 10000)),
  896. optionLabel: optionLabel,
  897. optionValue: optionValue,
  898. isDefault: false
  899. });
  900. }
  901. });
  902. // 更新字段的选项数据
  903. field.find('.field-options-data').remove();
  904. field.append('<input type="hidden" class="field-options-data" value=\'' + JSON.stringify(options) + '\'>');
  905. } else if (property === 'width') {
  906. field.data('width', value);
  907. } else if (property === 'placeholder') {
  908. field.data('placeholder', value);
  909. } else if (property === 'help') {
  910. field.data('help', value);
  911. } else if (property === 'validation') {
  912. field.data('validation', value);
  913. } else if (property === 'validationMessage') {
  914. field.data('validation-message', value);
  915. } else if (property === 'dict') {
  916. field.data('dict', value);
  917. }
  918. },
  919. updateSubtableProperty: function(property, value) {
  920. if (!this.currentSelectedElement) return;
  921. var subtable = this.currentSelectedElement;
  922. if (property === 'name') {
  923. subtable.find('.subtable-header span').text(value);
  924. } else if (property === 'code') {
  925. subtable.data('subtable-code', value);
  926. } else if (property === 'addButtonText') {
  927. subtable.data('add-button-text', value);
  928. } else if (property === 'required') {
  929. subtable.data('required', value);
  930. } else if (property === 'minRows') {
  931. subtable.data('min-rows', value);
  932. } else if (property === 'maxRows') {
  933. subtable.data('max-rows', value);
  934. }
  935. },
  936. getFormData: function() {
  937. var formData = {
  938. sections: []
  939. };
  940. // 遍历所有分组
  941. $('.section-item').each(function(sectionIndex) {
  942. var section = $(this);
  943. var sectionData = {
  944. id: section.data('section-id') || '',
  945. section_code: section.data('section-code') || ('section_' + (sectionIndex + 1)),
  946. section_name: section.find('.section-header span').text().trim(),
  947. section_type: 'normal',
  948. display_order: sectionIndex,
  949. is_collapsible: section.data('collapsible') === '1' ? 1 : 0,
  950. is_required: section.data('required') === '1' ? 1 : 0,
  951. fields: [],
  952. subtables: []
  953. };
  954. // 遍历分组内的字段
  955. section.find('.field-item').each(function(fieldIndex) {
  956. var field = $(this);
  957. var fieldType = field.data('field-type') || 'text';
  958. var fieldData = {
  959. id: field.data('field-id') || '',
  960. field_code: field.data('field-code') || ('field_' + (sectionIndex + 1) + '_' + (fieldIndex + 1)),
  961. field_name: field.data('field-name') || ('field_' + (sectionIndex + 1) + '_' + (fieldIndex + 1)),
  962. field_type: fieldType,
  963. field_label: field.find('.field-label').text().trim(),
  964. placeholder: field.data('placeholder') || '',
  965. default_value: field.data('default') || '',
  966. is_required: field.data('required') === '1' ? 1 : 0,
  967. validation_rule: field.data('validation') || '',
  968. validation_message: field.data('validation-message') || '',
  969. dict_type: field.data('dict') || '',
  970. display_order: fieldIndex,
  971. field_width: field.data('width') || '100%',
  972. help_text: field.data('help') || '',
  973. is_editable: 1
  974. };
  975. // 处理选项
  976. if (fieldType === 'select' || fieldType === 'radio' || fieldType === 'checkbox') {
  977. fieldData.options = [];
  978. if (field.find('.field-options-data').length > 0) {
  979. try {
  980. var optionsData = JSON.parse(field.find('.field-options-data').val());
  981. $.each(optionsData, function(i, option) {
  982. fieldData.options.push({
  983. id: option.id || '',
  984. option_code: option.optionCode || ('option_' + (fieldIndex + 1) + '_' + (i + 1)),
  985. option_label: option.optionLabel,
  986. option_value: option.optionValue,
  987. display_order: i,
  988. is_default: option.isDefault ? 1 : 0
  989. });
  990. });
  991. } catch (e) {
  992. console.error('处理字段选项时出错', e);
  993. }
  994. }
  995. }
  996. sectionData.fields.push(fieldData);
  997. });
  998. // 遍历分组内的子表
  999. section.find('.subtable-item').each(function(subtableIndex) {
  1000. var subtable = $(this);
  1001. var subtableData = {
  1002. id: subtable.data('subtable-id') || '',
  1003. subtable_code: subtable.data('subtable-code') || ('subtable_' + (sectionIndex + 1) + '_' + (subtableIndex + 1)),
  1004. subtable_name: subtable.find('.subtable-header span').text().trim(),
  1005. display_order: subtableIndex,
  1006. add_button_text: subtable.data('add-button-text') || '新增',
  1007. is_required: subtable.data('required') === '1' ? 1 : 0,
  1008. min_rows: parseInt(subtable.data('min-rows') || '0'),
  1009. max_rows: subtable.data('max-rows') ? parseInt(subtable.data('max-rows')) : null,
  1010. fields: []
  1011. };
  1012. // 处理子表字段
  1013. if (subtable.find('.subtable-fields-data').length > 0) {
  1014. try {
  1015. var fieldsData = JSON.parse(subtable.find('.subtable-fields-data').val());
  1016. $.each(fieldsData, function(i, field) {
  1017. subtableData.fields.push({
  1018. id: field.id || '',
  1019. field_code: field.fieldCode || ('subtable_field_' + (subtableIndex + 1) + '_' + (i + 1)),
  1020. field_name: field.fieldName || ('subtable_field_' + (subtableIndex + 1) + '_' + (i + 1)),
  1021. field_type: field.fieldType || 'text',
  1022. field_label: field.fieldLabel,
  1023. is_required: field.isRequired ? 1 : 0,
  1024. display_order: field.displayOrder || i,
  1025. is_editable: 1
  1026. });
  1027. });
  1028. } catch (e) {
  1029. console.error('处理子表字段数据时出错', e);
  1030. }
  1031. }
  1032. sectionData.subtables.push(subtableData);
  1033. });
  1034. formData.sections.push(sectionData);
  1035. });
  1036. return formData;
  1037. }
  1038. };
  1039. // 初始化表单编辑器
  1040. $(function() {
  1041. formEditor.init();
  1042. });
  1043. function submitForm() {
  1044. // 获取表单基本数据
  1045. var templateId = $("#templateId").val();
  1046. var templateName = $("input[name='template_name']").val();
  1047. var templateCode = $("input[name='template_code']").val();
  1048. var templateType = $("select[name='template_type']").val();
  1049. var version = $("input[name='version']").val();
  1050. var description = $("textarea[name='description']").val();
  1051. // 获取表单设计数据
  1052. var formDesignData = formEditor.getFormData();
  1053. // 构建完整表单数据
  1054. var data = {
  1055. "id": templateId,
  1056. "template_name": templateName,
  1057. "template_code": templateCode,
  1058. "template_type": templateType,
  1059. "version": version,
  1060. "description": description,
  1061. "formData": formDesignData
  1062. };
  1063. // 提交表单
  1064. $.operate.save(prefix + "/edit", data);
  1065. }
  1066. </script>
  1067. </body>
  1068. </html>