rules.html 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. <!DOCTYPE html>
  2. <html lang="zh" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <th:block th:include="include :: header('规则配置')" />
  5. <style>
  6. .rule-form-container {
  7. background-color: #f5f5f5;
  8. padding: 15px;
  9. margin-bottom: 20px;
  10. border-radius: 5px;
  11. }
  12. .rule-item {
  13. background-color: white;
  14. padding: 15px;
  15. margin-bottom: 10px;
  16. border: 1px solid #ddd;
  17. border-radius: 5px;
  18. }
  19. .rule-header {
  20. display: flex;
  21. justify-content: space-between;
  22. align-items: center;
  23. margin-bottom: 10px;
  24. }
  25. .rule-title {
  26. font-weight: bold;
  27. color: #333;
  28. }
  29. .rule-status {
  30. margin-left: 10px;
  31. }
  32. .rule-content {
  33. color: #666;
  34. font-size: 14px;
  35. }
  36. .rule-actions {
  37. margin-top: 10px;
  38. }
  39. .condition-group {
  40. background-color: #f9f9f9;
  41. padding: 10px;
  42. margin: 10px 0;
  43. border-left: 3px solid #1ab394;
  44. }
  45. .action-group {
  46. background-color: #f0f8ff;
  47. padding: 10px;
  48. margin: 10px 0;
  49. border-left: 3px solid #5bc0de;
  50. }
  51. </style>
  52. </head>
  53. <body class="gray-bg">
  54. <div class="container-fluid">
  55. <div class="row">
  56. <div class="col-sm-12">
  57. <div class="ibox">
  58. <div class="ibox-title">
  59. <h5>字段显示规则配置 - <span th:text="${template.templateName}"></span></h5>
  60. <div class="ibox-tools">
  61. <button class="btn btn-primary btn-sm" onclick="addRule()">
  62. <i class="fa fa-plus"></i> 新增规则
  63. </button>
  64. <button class="btn btn-default btn-sm" onclick="backToList()">
  65. <i class="fa fa-arrow-left"></i> 返回
  66. </button>
  67. </div>
  68. </div>
  69. <div class="ibox-content">
  70. <!-- 规则列表 -->
  71. <div id="ruleList">
  72. <!-- 规则项将通过JavaScript动态生成 -->
  73. </div>
  74. <!-- 暂无规则提示 -->
  75. <div id="emptyTip" class="text-center" style="display: none; padding: 50px;">
  76. <i class="fa fa-info-circle fa-3x text-muted"></i>
  77. <p class="text-muted" style="margin-top: 10px;">暂无配置规则,点击"新增规则"按钮添加</p>
  78. </div>
  79. </div>
  80. </div>
  81. </div>
  82. </div>
  83. </div>
  84. <!-- 新增/编辑规则弹窗 -->
  85. <div class="modal fade" id="ruleModal" tabindex="-1" role="dialog">
  86. <div class="modal-dialog modal-lg" role="document">
  87. <div class="modal-content">
  88. <div class="modal-header">
  89. <button type="button" class="close" data-dismiss="modal">&times;</button>
  90. <h4 class="modal-title" id="ruleModalTitle">新增规则</h4>
  91. </div>
  92. <div class="modal-body">
  93. <form id="ruleForm" class="form-horizontal">
  94. <input type="hidden" name="id" id="ruleId">
  95. <input type="hidden" name="templateId" th:value="${templateId}">
  96. <div class="form-group">
  97. <label class="col-sm-3 control-label is-required">规则名称:</label>
  98. <div class="col-sm-8">
  99. <input name="ruleName" class="form-control" type="text" placeholder="请输入规则名称" required>
  100. </div>
  101. </div>
  102. <div class="form-group">
  103. <label class="col-sm-3 control-label is-required">触发条件:</label>
  104. <div class="col-sm-8">
  105. <div class="condition-group">
  106. <div class="row">
  107. <div class="col-sm-5">
  108. <select name="triggerFieldCode" class="form-control" required onchange="loadFieldValues(this.value)">
  109. <option value="">选择字段</option>
  110. </select>
  111. </div>
  112. <div class="col-sm-2 text-center">
  113. <span style="line-height: 34px;">等于</span>
  114. </div>
  115. <div class="col-sm-5">
  116. <select name="triggerFieldValue" class="form-control" required>
  117. <option value="">选择值</option>
  118. </select>
  119. </div>
  120. </div>
  121. <div class="help-block">当选择的字段值满足条件时,触发相应动作</div>
  122. </div>
  123. </div>
  124. </div>
  125. <div class="form-group">
  126. <label class="col-sm-3 control-label is-required">触发动作:</label>
  127. <div class="col-sm-8">
  128. <div class="action-group">
  129. <div class="row">
  130. <div class="col-sm-4">
  131. <select name="actionType" class="form-control" required onchange="updateActionFields()">
  132. <option value="">选择动作</option>
  133. <option value="show">显示字段</option>
  134. <option value="hide">隐藏字段</option>
  135. <option value="required">设为必填</option>
  136. <option value="optional">设为选填</option>
  137. <option value="enable">启用字段</option>
  138. <option value="disable">禁用字段</option>
  139. </select>
  140. </div>
  141. <div class="col-sm-8">
  142. <select name="targetFieldCodes" class="form-control select2" multiple="multiple" required>
  143. <option value="">选择目标字段</option>
  144. </select>
  145. </div>
  146. </div>
  147. <div class="help-block">选择要执行的动作和目标字段</div>
  148. </div>
  149. </div>
  150. </div>
  151. <div class="form-group">
  152. <label class="col-sm-3 control-label">规则逻辑:</label>
  153. <div class="col-sm-8">
  154. <textarea name="ruleLogic" class="form-control" rows="3" placeholder="可选:输入自定义规则逻辑(JavaScript表达式)"></textarea>
  155. <div class="help-block">高级功能:自定义JavaScript逻辑,留空使用默认逻辑</div>
  156. </div>
  157. </div>
  158. <div class="form-group">
  159. <label class="col-sm-3 control-label">状态:</label>
  160. <div class="col-sm-8">
  161. <div class="radio-box">
  162. <input type="radio" id="isEnabled1" name="isEnabled" value="1" checked>
  163. <label for="isEnabled1">启用</label>
  164. </div>
  165. <div class="radio-box">
  166. <input type="radio" id="isEnabled0" name="isEnabled" value="0">
  167. <label for="isEnabled0">禁用</label>
  168. </div>
  169. </div>
  170. </div>
  171. </form>
  172. </div>
  173. <div class="modal-footer">
  174. <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
  175. <button type="button" class="btn btn-primary" onclick="saveRule()">保存</button>
  176. </div>
  177. </div>
  178. </div>
  179. </div>
  180. <!-- 规则测试弹窗 -->
  181. <div class="modal fade" id="testRuleModal" tabindex="-1" role="dialog">
  182. <div class="modal-dialog" role="document">
  183. <div class="modal-content">
  184. <div class="modal-header">
  185. <button type="button" class="close" data-dismiss="modal">&times;</button>
  186. <h4 class="modal-title">规则测试</h4>
  187. </div>
  188. <div class="modal-body">
  189. <div id="testRuleContent">
  190. <!-- 测试内容将动态生成 -->
  191. </div>
  192. </div>
  193. <div class="modal-footer">
  194. <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
  195. </div>
  196. </div>
  197. </div>
  198. </div>
  199. <th:block th:include="include :: footer" />
  200. <th:block th:include="include :: select2-js" />
  201. <script th:inline="javascript">
  202. var templateId = [[${templateId}]];
  203. var prefix = ctx + "gxhpz/followTemplateConfig";
  204. var rules = [];
  205. var fields = [];
  206. var currentRule = null;
  207. $(function() {
  208. // 初始化select2
  209. $('.select2').select2({
  210. placeholder: "请选择目标字段",
  211. allowClear: true,
  212. language: 'zh-CN'
  213. });
  214. loadRules();
  215. loadFields();
  216. });
  217. // 加载规则列表
  218. function loadRules() {
  219. $.ajax({
  220. url: prefix + "/ruleList",
  221. type: "POST",
  222. data: { templateId: templateId },
  223. success: function(result) {
  224. if (result.code == 0) {
  225. rules = result.rows;
  226. renderRules();
  227. }
  228. }
  229. });
  230. }
  231. // 加载字段列表
  232. function loadFields() {
  233. $.ajax({
  234. url: prefix + "/getTemplateFields",
  235. type: "POST",
  236. data: { templateId: templateId },
  237. success: function(result) {
  238. if (result.code == 0) {
  239. fields = result.data;
  240. updateFieldSelects();
  241. }
  242. }
  243. });
  244. }
  245. // 渲染规则列表
  246. function renderRules() {
  247. var html = '';
  248. if (rules.length === 0) {
  249. $('#ruleList').hide();
  250. $('#emptyTip').show();
  251. return;
  252. }
  253. $('#emptyTip').hide();
  254. $('#ruleList').show();
  255. rules.forEach(function(rule) {
  256. var statusHtml = rule.isEnabled == 1
  257. ? '<span class="label label-success">启用</span>'
  258. : '<span class="label label-default">禁用</span>';
  259. var actionText = getActionText(rule.actionType);
  260. var targetFields = rule.targetFieldCodes ? rule.targetFieldCodes.split(',') : [];
  261. var targetFieldNames = targetFields.map(function(code) {
  262. var field = fields.find(f => f.fieldCode === code);
  263. return field ? field.fieldName : code;
  264. }).join(', ');
  265. html += '<div class="rule-item">';
  266. html += ' <div class="rule-header">';
  267. html += ' <div>';
  268. html += ' <span class="rule-title">' + rule.ruleName + '</span>';
  269. html += ' <span class="rule-status">' + statusHtml + '</span>';
  270. html += ' </div>';
  271. html += ' <div class="rule-actions">';
  272. html += ' <button class="btn btn-xs btn-primary" onclick="editRule(' + rule.id + ')"><i class="fa fa-edit"></i> 编辑</button>';
  273. html += ' <button class="btn btn-xs btn-info" onclick="testRule(' + rule.id + ')"><i class="fa fa-flask"></i> 测试</button>';
  274. if (rule.isEnabled == 1) {
  275. html += ' <button class="btn btn-xs btn-warning" onclick="toggleRuleStatus(' + rule.id + ', 0)"><i class="fa fa-ban"></i> 禁用</button>';
  276. } else {
  277. html += ' <button class="btn btn-xs btn-success" onclick="toggleRuleStatus(' + rule.id + ', 1)"><i class="fa fa-check"></i> 启用</button>';
  278. }
  279. html += ' <button class="btn btn-xs btn-danger" onclick="deleteRule(' + rule.id + ')"><i class="fa fa-trash"></i> 删除</button>';
  280. html += ' </div>';
  281. html += ' </div>';
  282. html += ' <div class="rule-content">';
  283. html += ' <p><strong>触发条件:</strong>当 <span class="text-primary">' + getFieldName(rule.triggerFieldCode) + '</span> = <span class="text-info">' + rule.triggerFieldValue + '</span> 时</p>';
  284. html += ' <p><strong>执行动作:</strong>' + actionText + ' <span class="text-success">' + targetFieldNames + '</span></p>';
  285. if (rule.ruleLogic) {
  286. html += ' <p><strong>自定义逻辑:</strong><code>' + rule.ruleLogic + '</code></p>';
  287. }
  288. html += ' </div>';
  289. html += '</div>';
  290. });
  291. $('#ruleList').html(html);
  292. }
  293. // 获取字段名称
  294. function getFieldName(fieldCode) {
  295. var field = fields.find(f => f.fieldCode === fieldCode);
  296. return field ? field.fieldName : fieldCode;
  297. }
  298. // 获取动作文本
  299. function getActionText(actionType) {
  300. var actionMap = {
  301. 'show': '显示字段',
  302. 'hide': '隐藏字段',
  303. 'required': '设为必填',
  304. 'optional': '设为选填',
  305. 'enable': '启用字段',
  306. 'disable': '禁用字段'
  307. };
  308. return actionMap[actionType] || actionType;
  309. }
  310. // 更新字段选择框
  311. function updateFieldSelects() {
  312. // 更新触发字段选择框
  313. var triggerOptions = '<option value="">选择字段</option>';
  314. fields.forEach(function(field) {
  315. triggerOptions += '<option value="' + field.fieldCode + '">' + field.fieldName + '</option>';
  316. });
  317. $('select[name="triggerFieldCode"]').html(triggerOptions);
  318. // 更新目标字段选择框
  319. var targetOptions = '';
  320. fields.forEach(function(field) {
  321. targetOptions += '<option value="' + field.fieldCode + '">' + field.fieldName + '</option>';
  322. });
  323. $('select[name="targetFieldCodes"]').html(targetOptions);
  324. $('select[name="targetFieldCodes"]').select2('destroy').select2({
  325. placeholder: "请选择目标字段",
  326. allowClear: true,
  327. language: 'zh-CN'
  328. });
  329. }
  330. // 加载字段值选项
  331. function loadFieldValues(fieldCode) {
  332. if (!fieldCode) {
  333. $('select[name="triggerFieldValue"]').html('<option value="">选择值</option>');
  334. return;
  335. }
  336. var field = fields.find(f => f.fieldCode === fieldCode);
  337. if (!field) return;
  338. var valueOptions = '<option value="">选择值</option>';
  339. // 根据字段类型设置选项
  340. if (field.fieldType === 'radio' || field.fieldType === 'select') {
  341. // 这里应该从字段配置中获取选项,暂时使用默认值
  342. if (fieldCode === 'iscoordinate' || fieldCode === 'medicationStatus') {
  343. valueOptions += '<option value="是">是</option>';
  344. valueOptions += '<option value="否">否</option>';
  345. } else if (fieldCode === 'returnMethod') {
  346. valueOptions += '<option value="电话">电话</option>';
  347. valueOptions += '<option value="微信">微信</option>';
  348. valueOptions += '<option value="面访">面访</option>';
  349. } else {
  350. // 默认选项
  351. valueOptions += '<option value="选项1">选项1</option>';
  352. valueOptions += '<option value="选项2">选项2</option>';
  353. valueOptions += '<option value="选项3">选项3</option>';
  354. }
  355. } else if (field.fieldType === 'checkbox') {
  356. valueOptions += '<option value="包含">包含特定选项</option>';
  357. valueOptions += '<option value="不包含">不包含特定选项</option>';
  358. } else {
  359. valueOptions += '<option value="非空">非空</option>';
  360. valueOptions += '<option value="为空">为空</option>';
  361. }
  362. $('select[name="triggerFieldValue"]').html(valueOptions);
  363. }
  364. // 更新动作字段
  365. function updateActionFields() {
  366. var actionType = $('select[name="actionType"]').val();
  367. // 可以根据不同的动作类型过滤可选字段
  368. }
  369. // 新增规则
  370. function addRule() {
  371. currentRule = null;
  372. $('#ruleModalTitle').text('新增规则');
  373. $('#ruleForm')[0].reset();
  374. $('#ruleId').val('');
  375. $('select[name="targetFieldCodes"]').val(null).trigger('change');
  376. $('#ruleModal').modal('show');
  377. }
  378. // 编辑规则
  379. function editRule(id) {
  380. currentRule = rules.find(r => r.id === id);
  381. if (!currentRule) return;
  382. $('#ruleModalTitle').text('编辑规则');
  383. $('#ruleId').val(currentRule.id);
  384. $('input[name="ruleName"]').val(currentRule.ruleName);
  385. $('select[name="triggerFieldCode"]').val(currentRule.triggerFieldCode);
  386. // 加载字段值选项后设置值
  387. loadFieldValues(currentRule.triggerFieldCode);
  388. setTimeout(function() {
  389. $('select[name="triggerFieldValue"]').val(currentRule.triggerFieldValue);
  390. }, 100);
  391. $('select[name="actionType"]').val(currentRule.actionType);
  392. // 设置目标字段
  393. var targetFields = currentRule.targetFieldCodes ? currentRule.targetFieldCodes.split(',') : [];
  394. $('select[name="targetFieldCodes"]').val(targetFields).trigger('change');
  395. $('textarea[name="ruleLogic"]').val(currentRule.ruleLogic || '');
  396. $('input[name="isEnabled"][value="' + currentRule.isEnabled + '"]').prop('checked', true);
  397. $('#ruleModal').modal('show');
  398. }
  399. // 保存规则
  400. function saveRule() {
  401. if (!$('#ruleForm').valid()) {
  402. return;
  403. }
  404. var formData = $('#ruleForm').serializeArray();
  405. var data = {};
  406. formData.forEach(function(item) {
  407. if (item.name === 'targetFieldCodes') {
  408. if (!data[item.name]) {
  409. data[item.name] = [];
  410. }
  411. data[item.name].push(item.value);
  412. } else {
  413. data[item.name] = item.value;
  414. }
  415. });
  416. // 将目标字段数组转换为逗号分隔的字符串
  417. if (data.targetFieldCodes && Array.isArray(data.targetFieldCodes)) {
  418. data.targetFieldCodes = data.targetFieldCodes.join(',');
  419. }
  420. var url = currentRule ? prefix + "/updateRule" : prefix + "/saveRule";
  421. $.ajax({
  422. url: url,
  423. type: "POST",
  424. data: data,
  425. success: function(result) {
  426. if (result.code == 0) {
  427. $.modal.alertSuccess("保存成功");
  428. $('#ruleModal').modal('hide');
  429. loadRules();
  430. } else {
  431. $.modal.alertError(result.msg);
  432. }
  433. }
  434. });
  435. }
  436. // 测试规则
  437. function testRule(id) {
  438. var rule = rules.find(r => r.id === id);
  439. if (!rule) return;
  440. var html = '<div class="form-horizontal">';
  441. html += '<div class="form-group">';
  442. html += ' <label class="col-sm-4 control-label">规则名称:</label>';
  443. html += ' <div class="col-sm-8"><p class="form-control-static">' + rule.ruleName + '</p></div>';
  444. html += '</div>';
  445. html += '<div class="form-group">';
  446. html += ' <label class="col-sm-4 control-label">触发条件:</label>';
  447. html += ' <div class="col-sm-8">';
  448. html += ' <select class="form-control" id="testTriggerValue">';
  449. html += ' <option value="">请选择' + getFieldName(rule.triggerFieldCode) + '的值</option>';
  450. // 添加测试选项
  451. $('select[name="triggerFieldValue"] option').each(function() {
  452. if ($(this).val()) {
  453. html += '<option value="' + $(this).val() + '">' + $(this).text() + '</option>';
  454. }
  455. });
  456. html += ' </select>';
  457. html += ' </div>';
  458. html += '</div>';
  459. html += '<div class="form-group">';
  460. html += ' <label class="col-sm-4 control-label">测试结果:</label>';
  461. html += ' <div class="col-sm-8">';
  462. html += ' <div id="testResult" class="alert alert-info" style="display: none;"></div>';
  463. html += ' </div>';
  464. html += '</div>';
  465. html += '</div>';
  466. $('#testRuleContent').html(html);
  467. // 绑定测试事件
  468. $('#testTriggerValue').change(function() {
  469. var value = $(this).val();
  470. var resultHtml = '';
  471. if (value === rule.triggerFieldValue) {
  472. resultHtml = '<i class="fa fa-check-circle text-success"></i> 规则触发!<br>';
  473. resultHtml += '执行动作:' + getActionText(rule.actionType) + ' ';
  474. var targetFields = rule.targetFieldCodes ? rule.targetFieldCodes.split(',') : [];
  475. var targetFieldNames = targetFields.map(function(code) {
  476. return getFieldName(code);
  477. }).join(', ');
  478. resultHtml += '<strong>' + targetFieldNames + '</strong>';
  479. $('#testResult').removeClass('alert-warning').addClass('alert-success');
  480. } else {
  481. resultHtml = '<i class="fa fa-times-circle text-warning"></i> 规则未触发';
  482. $('#testResult').removeClass('alert-success').addClass('alert-warning');
  483. }
  484. $('#testResult').html(resultHtml).show();
  485. });
  486. $('#testRuleModal').modal('show');
  487. }
  488. // 切换规则状态
  489. function toggleRuleStatus(id, status) {
  490. var statusText = status == 1 ? "启用" : "禁用";
  491. $.modal.confirm("确认要" + statusText + "该规则吗?", function() {
  492. $.ajax({
  493. url: prefix + "/updateRuleStatus",
  494. type: "POST",
  495. data: {
  496. id: id,
  497. isEnabled: status
  498. },
  499. success: function(result) {
  500. if (result.code == 0) {
  501. $.modal.alertSuccess(statusText + "成功");
  502. loadRules();
  503. } else {
  504. $.modal.alertError(result.msg);
  505. }
  506. }
  507. });
  508. });
  509. }
  510. // 删除规则
  511. function deleteRule(id) {
  512. $.modal.confirm("确认要删除该规则吗?", function() {
  513. $.ajax({
  514. url: prefix + "/deleteRule",
  515. type: "POST",
  516. data: { id: id },
  517. success: function(result) {
  518. if (result.code == 0) {
  519. $.modal.alertSuccess("删除成功");
  520. loadRules();
  521. } else {
  522. $.modal.alertError(result.msg);
  523. }
  524. }
  525. });
  526. });
  527. }
  528. // 返回列表
  529. function backToList() {
  530. var url = ctx + 'gxhpz/followTemplateConfig';
  531. $.modal.openTab("随访模版配置", url);
  532. }
  533. // 表单验证
  534. $("#ruleForm").validate({
  535. rules: {
  536. ruleName: {
  537. required: true,
  538. maxlength: 100
  539. },
  540. triggerFieldCode: {
  541. required: true
  542. },
  543. triggerFieldValue: {
  544. required: true
  545. },
  546. actionType: {
  547. required: true
  548. },
  549. targetFieldCodes: {
  550. required: true
  551. }
  552. },
  553. messages: {
  554. ruleName: {
  555. required: "请输入规则名称",
  556. maxlength: "规则名称不能超过100个字符"
  557. },
  558. triggerFieldCode: {
  559. required: "请选择触发字段"
  560. },
  561. triggerFieldValue: {
  562. required: "请选择触发值"
  563. },
  564. actionType: {
  565. required: "请选择动作类型"
  566. },
  567. targetFieldCodes: {
  568. required: "请选择目标字段"
  569. }
  570. }
  571. });
  572. </script>
  573. </body>
  574. </html>