grcz_grtfAdd.ss.jsp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. <%@ page import="java.util.Map" %>
  2. <%@ page import="java.util.TreeMap" %>
  3. <html>
  4. <head>
  5. <style>
  6. .table-container>tr>th{
  7. width:130px !important;
  8. }
  9. /* 把content-box的高度限制 从公共css 抽到具体有需要的页面 by xu 20251215 */
  10. .form-container .content-box {
  11. height: calc(100% - 80px) !important;
  12. }
  13. </style>
  14. </head>
  15. <body class="env-input-body">
  16. <form method="post" id="app" class="form-container">
  17. <div class="content-box fit-height-content">
  18. <div class="content-div" ssFith="true">
  19. <table class='form'>
  20. <tr>
  21. <th style="width: 120px">退费类别</th>
  22. <td >
  23. <%-- 功能说明:个人退费页退费类别切换时联动重跑金额校验 by xu 20260323 --%>
  24. <onoff.ss name="grczlbm" mode="edit" rad="true" null="false" val="" onChange="handleGrczlbmChange" />
  25. <%
  26. Map<Integer,String > grczlbMap = (Map)(request.getAttribute("grczlbMap"));
  27. for (Integer key : grczlbMap.keySet()) {
  28. pageContext.setAttribute("k",key);
  29. pageContext.setAttribute("v",grczlbMap.get(key));
  30. %>
  31. <input name="grczlbm" value="${v}" ssVal="${k}" />
  32. <%
  33. }
  34. %>
  35. </td>
  36. </tr>
  37. <tr>
  38. <th>班级/亲属</th>
  39. <td style="display: flex;align-items: center;border: none;
  40. border-right: 1px solid #e2e4ec;">
  41. <objp.ss name="bjid" cb="bj" inp="true" width="200px"/>
  42. <onoff.ss name="rylbm" mode="edit" rad="false" null="false" val="1100" />
  43. <input name="rylbm" value="职工亲属" ssVal="1000" />
  44. </td>
  45. </tr>
  46. <%-- 先去掉,接入读卡器时再加。Lin
  47. <tr>
  48. <th>卡号</th>
  49. <td >
  50. <@input name="kah"/>
  51. </td>
  52. </tr>
  53. --%>
  54. <tr>
  55. <th>人员</th>
  56. <td>
  57. <input name="czryid" type="hidden" value='${sessionScope.ssUser.ryid}'/> <%-- 操作人员ID。Lin --%>
  58. <objp.ss name="ryid" cb="ryByBjOrRylb" inp="true" onChange="selBaseInfoByRyid" filterField="bjid,rylbm" />
  59. </td>
  60. </tr>
  61. <tr>
  62. <th>部门/班级</th>
  63. <td id='bmbj'></td>
  64. </tr>
  65. <tr>
  66. <th>姓名</th>
  67. <td id='xm'></td>
  68. </tr>
  69. <tr>
  70. <th>人员号</th>
  71. <td id='ryh'></td>
  72. </tr>
  73. <tr>
  74. <th>金额</th>
  75. <td>
  76. <script>
  77. // 功能说明:个人退费页金额字段改为 SsInp 组件,并声明 Vue 表单模型 by xu 20260323
  78. ss.dom.formElemConfig.je={val:'',type:window.ss.dom.TYPE.INPUT};
  79. </script>
  80. <ss-inp v-model="je" name="je" placeholder="请输入金额"></ss-inp>
  81. </td>
  82. </tr>
  83. <tr>
  84. <th>消费余额</th>
  85. <td ><input name="xfye"/></td>
  86. </tr>
  87. <tr>
  88. <th>描述</th>
  89. <td>
  90. <script>
  91. // 功能说明:个人退费页描述字段改为 SsInp 组件,并声明 Vue 表单模型 by xu 20260323
  92. ss.dom.formElemConfig.ms={val:'',type:window.ss.dom.TYPE.INPUT};
  93. </script>
  94. <ss-inp v-model="ms" name="ms" placeholder="请输入描述"></ss-inp>
  95. </td>
  96. </tr>
  97. </table>
  98. </div>
  99. <div class='bottom-div'>
  100. <ss-bottom-button
  101. id="saveAndCommit"
  102. text="保存并提交"
  103. onclick='submitGrtfForm();'<%-- 功能说明:个人退费页提交前先校验金额必须为负数 by xu 20260323 --%>
  104. icon-class="bottom-div-save"
  105. ></ss-bottom-button>
  106. <ss-bottom-button
  107. text="关闭"
  108. onclick='ss.display.closeDialog();'
  109. icon-class="bottom-div-close"
  110. ></ss-bottom-button>
  111. </div>
  112. </div>
  113. </form>
  114. </body>
  115. </html>
  116. <script>
  117. function getFormAppVm(){
  118. var appEl = document.getElementById("app");
  119. if (!appEl || !appEl.__vue_app__ || !appEl.__vue_app__._instance) {
  120. return null;
  121. }
  122. return appEl.__vue_app__._instance.proxy || null;
  123. }
  124. function normalizeRylbmValues(value){
  125. if (Array.isArray(value)) {
  126. return value.map(function(item){
  127. return item == null ? "" : item.toString();
  128. }).filter(Boolean);
  129. }
  130. if (value == null || value === "") {
  131. return [];
  132. }
  133. var cleanValue = value.toString().replace(/^,+/, "");
  134. if (!cleanValue) {
  135. return [];
  136. }
  137. return cleanValue.split(/[,|]/).filter(Boolean);
  138. }
  139. function hasRelativeRylbmValue(value){
  140. return normalizeRylbmValues(value).indexOf("1000") !== -1;
  141. }
  142. function clearRyDisplay(){
  143. var bmbjEl = document.getElementById('bmbj');
  144. var xmEl = document.getElementById('xm');
  145. var ryhEl = document.getElementById('ryh');
  146. if (bmbjEl) bmbjEl.innerHTML = "";
  147. if (xmEl) xmEl.innerHTML = "";
  148. if (ryhEl) ryhEl.innerHTML = "";
  149. }
  150. function clearRySelection(vm){
  151. if (!vm) {
  152. clearRyDisplay();
  153. return;
  154. }
  155. vm.ryid = "";
  156. clearRyDisplay();
  157. }
  158. function handleRylbmChange(groupValue){
  159. if (!hasRelativeRylbmValue(groupValue)) {
  160. return;
  161. }
  162. var vm = getFormAppVm();
  163. if (!vm) {
  164. clearRyDisplay();
  165. return;
  166. }
  167. vm.bjid = "";
  168. clearRySelection(vm);
  169. }
  170. function handleBjChange(value){
  171. if (value == null || value === "") {
  172. return;
  173. }
  174. var vm = getFormAppVm();
  175. if (!vm) {
  176. clearRyDisplay();
  177. return;
  178. }
  179. if (hasRelativeRylbmValue(vm.rylbm)) {
  180. vm.rylbm = "1100";
  181. }
  182. clearRySelection(vm);
  183. }
  184. function selBaseInfoByRyid(value){
  185. $.ajax({
  186. url:"<serv.ss name='ry_selBaseInfoByRyid'/>",
  187. type:"post",
  188. data:{
  189. ryid:value
  190. },
  191. dataType:"json",
  192. success:function(data){
  193. if (data.ssCode != 0) {
  194. alert(data.ssMsg);
  195. return;
  196. }
  197. var d = data.ssData;
  198. document.getElementById('xm').innerHTML = d.xm;
  199. document.getElementById('ryh').innerHTML = d.ryh;
  200. if (d.rylbm != 1100) { // 不是学员。Lin
  201. if (d.bmmc)
  202. document.getElementById('bmbj').innerHTML = d.bmmc;
  203. else
  204. document.getElementById('bmbj').innerHTML = "(无)";
  205. } else {
  206. if (d.bjmc)
  207. document.getElementById('bmbj').innerHTML = d.bjmc;
  208. else
  209. document.getElementById('bmbj').innerHTML = "(无)";
  210. }
  211. }
  212. });
  213. }
  214. </script>
  215. <script type="text/javascript" src="/js/validate/validator-rules.js"></script><%-- 功能说明:个人退费页接入桌面端校验规则,支持 SsInp 红线提示 by xu 20260323 --%>
  216. <script type="text/javascript" src="/js/validate/validation-manager.js"></script><%-- 功能说明:个人退费页接入桌面端校验管理器,支持 SsInp 红线提示 by xu 20260323 --%>
  217. <script>
  218. // 功能说明:根据当前退费类别渲染 code-label 映射,供金额负数校验复用 by xu 20260323
  219. var GRTF_LABEL_MAP = {
  220. <%
  221. for (Integer key : grczlbMap.keySet()) {
  222. %>
  223. "<%=key%>": "<%=grczlbMap.get(key)%>",
  224. <%
  225. }
  226. %>
  227. };
  228. // 功能说明:记录当前退费类别的最新 value/label,避免 onoff 隐藏值切换时机导致金额校验判断失真 by xu 20260323
  229. var CURRENT_GRTF_CATEGORY_STATE = {
  230. value: "",
  231. label: ""
  232. };
  233. // 功能说明:退费类别变化后延后一拍重跑金额校验,确保隐藏字段值更新后再清理红线状态 by xu 20260323
  234. function handleGrczlbmChange(groupValue, value, label){
  235. CURRENT_GRTF_CATEGORY_STATE.value = groupValue == null ? "" : groupValue.toString();
  236. CURRENT_GRTF_CATEGORY_STATE.label = label == null ? "" : label.toString();
  237. setTimeout(function(){
  238. if (window.ssVm && typeof window.ssVm.validateField === "function") {
  239. window.ssVm.validateField("je");
  240. return;
  241. }
  242. validateNegativeAmountByCategory(false);
  243. }, 0);
  244. }
  245. // 功能说明:初始化个人退费页金额负数校验规则,整页金额都必须输入负数 by xu 20260323
  246. function initNegativeAmountValidation(){
  247. if (!window.ssVm || typeof window.ssVm.add !== "function" || window.ss.dom._grczGrtfNegativeAmountValidationInited) {
  248. return;
  249. }
  250. window.ss.dom._grczGrtfNegativeAmountValidationInited = true;
  251. window.ssVm.add("ss.commonValidator.custom", ["je"], {
  252. msgPrfx: "金额",
  253. relField: "grczlbm",
  254. validate: function(value, categoryValue){
  255. var currentLabel = getCurrentGrczlbmLabel(categoryValue);
  256. if (value == null || value.toString().trim() === "") {
  257. return true;
  258. }
  259. if (isValidNegativeAmountText(value)) {
  260. return true;
  261. }
  262. return {
  263. valid: false,
  264. message: (currentLabel || "退费") + "金额只能输入负数"
  265. };
  266. }
  267. }, {
  268. je: window.ss.dom.formElemConfig.je ? window.ss.dom.formElemConfig.je.val : ""
  269. });
  270. }
  271. // 功能说明:根据当前退费类别值读取显示文案,供联动校验和错误提示复用 by xu 20260323
  272. function getCurrentGrczlbmLabel(categoryValue){
  273. var currentValue = categoryValue == null ? getCurrentGrczlbmValue() : categoryValue.toString();
  274. if (CURRENT_GRTF_CATEGORY_STATE.label && (!currentValue || CURRENT_GRTF_CATEGORY_STATE.value === currentValue)) {
  275. return CURRENT_GRTF_CATEGORY_STATE.label;
  276. }
  277. return GRTF_LABEL_MAP[currentValue] || "";
  278. }
  279. // 功能说明:统一判断金额文本是否为合法负数,供提交校验和 ssVm 规则复用 by xu 20260323
  280. function isValidNegativeAmountText(value){
  281. var amountText = value == null ? "" : value.toString().trim();
  282. if (!amountText) {
  283. return false;
  284. }
  285. var amountNumber = Number(amountText);
  286. return !isNaN(amountNumber) && amountNumber < 0;
  287. }
  288. // 功能说明:根据当前退费类别值读取隐藏字段值,供金额校验复用 by xu 20260323
  289. function getCurrentGrczlbmValue(){
  290. var vm = getFormAppVm();
  291. if (vm && vm.grczlbm != null && vm.grczlbm !== "") {
  292. return vm.grczlbm.toString();
  293. }
  294. var categoryElem = document.querySelector('[name="grczlbm"]');
  295. return categoryElem && categoryElem.value != null ? categoryElem.value.toString() : "";
  296. }
  297. // 功能说明:统一校验个人退费金额必须为负数,不再区分具体退费类别 by xu 20260323
  298. function validateNegativeAmountByCategory(showMsg){
  299. var currentLabel = getCurrentGrczlbmLabel();
  300. var jeElem = document.querySelector('[name="je"]');
  301. if (!jeElem) {
  302. return true;
  303. }
  304. if (isValidNegativeAmountText(jeElem.value)) {
  305. return true;
  306. }
  307. if (showMsg !== false) {
  308. alert((currentLabel || "退费") + "金额只能输入负数");
  309. jeElem.focus();
  310. }
  311. return false;
  312. }
  313. // 功能说明:个人退费页提交前先走 ssVm 全量校验,确保显示 SsInp 左侧红线与底部提示 by xu 20260323
  314. function submitGrtfForm(){
  315. if (window.ssVm && window.ssVm.validations && window.ssVm.validations.size > 0) {
  316. var validateResult = window.ssVm.validateAll();
  317. if (!validateResult.valid) {
  318. return false;
  319. }
  320. } else if (!validateNegativeAmountByCategory(true)) {
  321. return false;
  322. }
  323. var formElem = document.querySelector("form");
  324. if (!formElem) {
  325. alert("表单不存在");
  326. return false;
  327. }
  328. formElem.action = "<ss:serv name='grcz_lr_tj' dest='addSure' parm='{thisViewObject:\"grcz\",dataType:\"update\"}'/>";
  329. ss.display.resizeComponent(881,361,515,515);
  330. formElem.submit();
  331. return true;
  332. }
  333. if (window.SS && typeof SS.ready === "function") {
  334. SS.ready(function(){
  335. // 功能说明:页面初始化后同步当前退费类别状态,避免首次校验读取不到最新类别文案 by xu 20260323
  336. CURRENT_GRTF_CATEGORY_STATE.value = getCurrentGrczlbmValue();
  337. CURRENT_GRTF_CATEGORY_STATE.label = GRTF_LABEL_MAP[CURRENT_GRTF_CATEGORY_STATE.value] || "";
  338. // 功能说明:页面初始化后注册个人退费金额负数校验规则,保证 SsInp 输入时直接出现红线提示 by xu 20260323
  339. initNegativeAmountValidation();
  340. });
  341. }
  342. </script>