ka_nfcAdd.jsp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. <%@ page language="java" pageEncoding="UTF-8" isELIgnored="false" %>
  2. <%@ taglib uri="/ssTag" prefix="ss"%>
  3. <% pageContext.setAttribute(ss.page.PageC.PAGE_objName,"ka");%>
  4. <%pageContext.setAttribute("wdpageinformation","{'hastab':'0'}");%>
  5. <!DOCTYPE html>
  6. <html>
  7. <head>
  8. <%@ include file="/page/clip/header.jsp" %>
  9. <style>
  10. .table-container>tr>th{
  11. width:130px !important;
  12. }
  13. /* 把content-box的高度限制 从公共css 抽到具体有需要的页面 by xu 20251215 */
  14. .form-container .content-box {
  15. height: calc(100% - 80px) !important;
  16. padding-top: 40px !important;
  17. }
  18. td{
  19. display: flex;
  20. align-items: center;
  21. justify-content: flex-start;
  22. }
  23. </style>
  24. </head>
  25. <body class="env-input-body">
  26. <script src="/js/reader/reader-client.js"></script>
  27. <script type="text/javascript" src="/js/reader/ka-nfc-page.js"></script>
  28. <div style="position:fixed;top:0px;left:0px;z-index:9999;padding:8px 24px;display:flex;align-items:center;gap:12px;">
  29. <button id="btnConnect" class="primary" type="button" style="display:none;">连接读卡器</button>
  30. <span id="readerStatusTag" style="font-size:14px;color:#64748b;">读卡器连接中...</span>
  31. </div>
  32. <input id="wsUrlText" type="hidden" value="ws://127.0.0.1:6689"/> <%-- WebSocket。Lin --%>
  33. <input id="pollingInterval" type="hidden" value="1000" min="100" step="100" /> <%-- 轮询间隔 (毫秒)。Lin --%>
  34. <input id="dedupeMode" type="hidden" value="session"/> <%-- 去重模式:本次会话去重。Lin --%>
  35. <%-- ISO14443A 参数。Lin --%>
  36. <input id="keyTypeInput" type="hidden" value="0"/> <%-- 密钥类型:Key A。1 = Key B。Lin --%>
  37. <input id="keyInput" type="hidden" value="FFFFFFFFFFFF" maxlength="12"/> <%-- 密钥。Lin --%>
  38. <input id="blockAddressInput" type="hidden" value="1" min="0" /> <%-- 块地址。Lin --%>
  39. <input id="numberOfBlocksReadInput" type="hidden" value="1" min="1"/> <%-- 读取块数。Lin --%>
  40. <%-- --%>
  41. <%-- ISO15693 参数。Lin --%>
  42. <input id="readDataTypeInput" type="hidden" value="0"/> <%-- 读取数据类型:block。1 = afi,2 = eas,3 = block and afi,4 = block and eas,5 = afi and eas,6 = block and afi and eas。Lin --%>
  43. <input id="readSecurityStatusInput" type="hidden" value="0"/> <%-- 读取安全位:不读取。1 = 读取。Lin --%>
  44. <input id="iso15693BlockAddressInput" type="hidden" value="0" min="0"/> <%-- 块地址。Lin --%>
  45. <input id="iso15693NumberOfBlocksInput" type="hidden" value="7" min="1"/> <%-- 读取块数。Lin --%>
  46. <%-- --%>
  47. <form method="post" id="app" class="form-container">
  48. <div class="content-box fit-height-content">
  49. <div class="content-div" ssFith="true">
  50. <table class='form'>
  51. <tr>
  52. <th>部门/亲属</th>
  53. <td style="display: flex;align-items: center;">
  54. <script>
  55. ss.dom.formElemConfig.bmid={val:null,type:window.ss.dom.TYPE.OBJP};
  56. </script>
  57. <ss-objp
  58. :opt="bmidOption"
  59. :inp="true"
  60. url="<ss:serv name='loadObjpOpt' parm='{"objectpickerdropdown":"1"}' />"
  61. cb="bm"
  62. v-model="bmid"
  63. name="bmid"
  64. :readonly="false"
  65. width="200px"
  66. ></ss-objp>
  67. <script>
  68. ss.dom.formElemConfig.rylbm={val:'1100',type:window.ss.dom.TYPE.ONOFFBTN};
  69. </script>
  70. <ss-onoff
  71. v-model="rylbm"
  72. name="rylbm"
  73. label="职工亲属"
  74. value="1000"
  75. :multiple="true"
  76. :null="false"
  77. placeholder="职工亲属"
  78. v-model="rylbm"
  79. :readonly="false"
  80. ></ss-onoff>
  81. </td>
  82. </tr>
  83. <%-- 先去掉,接入读卡器时再加。Lin
  84. <tr>
  85. <th>卡号</th>
  86. <td >
  87. <@input name="kah"/>
  88. </td>
  89. </tr>
  90. --%>
  91. <tr>
  92. <th>人员</th>
  93. <td>
  94. <input name="czryid" type="hidden" value='${sessionScope.ssUser.ryid}'/> <%-- 操作人员ID。Lin --%>
  95. <script>
  96. ss.dom.formElemConfig.cyryid={val:null,type:window.ss.dom.TYPE.OBJP};
  97. </script>
  98. <ss-objp
  99. :opt="cyryidOption"
  100. :inp="true"
  101. url="<ss:serv name='loadObjpOpt' parm='{"objectpickerdropdown":"1","objectpickerfilterField":"bmid,rylbm"}' />"
  102. cb="ryByBmOrRylb"
  103. v-model="cyryid"
  104. name="cyryid"
  105. :readonly="false"
  106. filterField="bmid,rylbm"
  107. onChange="selBaseInfoByRyid"
  108. ></ss-objp>
  109. </td>
  110. <th>部门</th>
  111. <td id='bm'></td>
  112. </tr>
  113. <tr>
  114. <th>姓名</th>
  115. <td id='xm'></td>
  116. <th>人员号</th>
  117. <td id='ryh'></td>
  118. </tr>
  119. <tr>
  120. <th>NFC</th>
  121. <td>
  122. <%--自动生成:ka.kah--%>
  123. <script>
  124. ss.dom.formElemConfig.kah={val:'<ss:txt val='${ka.kah}'/>',type:window.ss.dom.TYPE.INPUT};
  125. </script>
  126. <ss-inp
  127. placeholder="请输入卡号"
  128. v-model="kah"
  129. name="kah"
  130. :readonly="false"
  131. >
  132. </ss-inp>
  133. </td>
  134. </tr>
  135. </table>
  136. </div>
  137. </div>
  138. <div class='bottom-div'>
  139. <ss-bottom-button
  140. id="saveAndCommit"
  141. text="保存并提交"
  142. onclick='submitGrtfForm();'<%-- 功能说明:个人退费页提交前先校验金额必须为负数 by xu 20260323 --%>
  143. icon-class="bottom-div-save"
  144. ></ss-bottom-button>
  145. <ss-bottom-button
  146. text="关闭"
  147. onclick='ss.display.closeDialog();'
  148. icon-class="bottom-div-close"
  149. ></ss-bottom-button>
  150. </div>
  151. <input name='wdComponentID' type='hidden' value='ka_nfcAdd'/></form>
  152. <ss:ichk name='ka_nfcAdd'/>
  153. <script type="text/javascript">var wdRecordValue='${wdRecordValue}';</script>
  154. <script type="text/javascript" src="/ss/js/wdRecord.js"></script>
  155. <script type="text/javascript">(function(){wdRecord("ka_nfcAdd");})();</script>
  156. <script type="text/javascript" src="/ss/js/wdFitHeight.js"></script>
  157. <script type="text/javascript">initWdFitHeight(0)</script>
  158. <script type="text/javascript">initWdFitHeightFunction=function(){initWdFitHeight(0);};</script>
  159. <ss:equal val="${empty resizeComponent}" val2="false">
  160. <script>{var iframe=wd.display.getFrameOfWindow();
  161. if(iframe&&iframe.contentWindow==window)
  162. wd.display.resizeComponent(${resizeComponent.width}, ${resizeComponent.height}, ${empty resizeComponent.minHeight?'null':resizeComponent.minHeight}, ${empty resizeComponent.maxHeight?'null':resizeComponent.maxHeight});}</script>
  163. </ss:equal>
  164. <ss:help/>
  165. </body>
  166. <script type="text/javascript">
  167. try{wd.display.showMsgPopup('${msg}');
  168. }catch(err){console.error(err);}
  169. </script>
  170. <ss:equal val="${empty wdclosewindowparam}" val2="false">
  171. <script type="text/javascript">
  172. try{wd.display.setCloseWindowParam('${wdclosewindowparam}');
  173. }catch(err){console.error(err);}
  174. </script>
  175. </ss:equal>
  176. </html>
  177. <%@ include file="/page/clip/footer.jsp" %>
  178. <script>
  179. function getFormAppVm(){
  180. var appEl = document.getElementById("app");
  181. if (!appEl || !appEl.__vue_app__ || !appEl.__vue_app__._instance) {
  182. return null;
  183. }
  184. return appEl.__vue_app__._instance.proxy || null;
  185. }
  186. function normalizeRylbmValues(value){
  187. if (Array.isArray(value)) {
  188. return value.map(function(item){
  189. return item == null ? "" : item.toString();
  190. }).filter(Boolean);
  191. }
  192. if (value == null || value === "") {
  193. return [];
  194. }
  195. var cleanValue = value.toString().replace(/^,+/, "");
  196. if (!cleanValue) {
  197. return [];
  198. }
  199. return cleanValue.split(/[,|]/).filter(Boolean);
  200. }
  201. function hasRelativeRylbmValue(value){
  202. return normalizeRylbmValues(value).indexOf("1000") !== -1;
  203. }
  204. function clearRyDisplay(){
  205. var bmbjEl = document.getElementById('bmbj');
  206. var xmEl = document.getElementById('xm');
  207. var ryhEl = document.getElementById('ryh');
  208. if (bmbjEl) bmbjEl.innerHTML = "";
  209. if (xmEl) xmEl.innerHTML = "";
  210. if (ryhEl) ryhEl.innerHTML = "";
  211. }
  212. function clearRySelection(vm){
  213. if (!vm) {
  214. clearRyDisplay();
  215. return;
  216. }
  217. vm.ryid = "";
  218. clearRyDisplay();
  219. }
  220. function handleRylbmChange(groupValue){
  221. if (!hasRelativeRylbmValue(groupValue)) {
  222. return;
  223. }
  224. var vm = getFormAppVm();
  225. if (!vm) {
  226. clearRyDisplay();
  227. return;
  228. }
  229. vm.bmid = "";
  230. clearRySelection(vm);
  231. }
  232. function handleBjChange(value){
  233. if (value == null || value === "") {
  234. return;
  235. }
  236. var vm = getFormAppVm();
  237. if (!vm) {
  238. clearRyDisplay();
  239. return;
  240. }
  241. if (hasRelativeRylbmValue(vm.rylbm)) {
  242. vm.rylbm = "1100";
  243. }
  244. clearRySelection(vm);
  245. }
  246. function selBaseInfoByRyid(value){
  247. $.ajax({
  248. url:"<ss:serv name='ry_selBaseInfoByRyid'/>",
  249. type:"post",
  250. data:{
  251. ryid:value
  252. },
  253. dataType:"json",
  254. success:function(data){
  255. if (data.ssCode != 0) {
  256. alert(data.ssMsg);
  257. return;
  258. }
  259. var d = data.ssData;
  260. document.getElementById('xm').innerHTML = d.xm;
  261. document.getElementById('ryh').innerHTML = d.ryh;
  262. if (d.rylbm != 1100) { // 不是学员。Lin
  263. if (d.bmmc)
  264. document.getElementById('bmbj').innerHTML = d.bmmc;
  265. else
  266. document.getElementById('bmbj').innerHTML = "(无)";
  267. } else {
  268. if (d.bjmc)
  269. document.getElementById('bmbj').innerHTML = d.bjmc;
  270. else
  271. document.getElementById('bmbj').innerHTML = "(无)";
  272. }
  273. <%-- 再去掉,不显示。Lin
  274. document.getElementById('xfye').innerHTML = d.xfye;
  275. --%>
  276. }
  277. });
  278. }
  279. function selBaseInfoByKah(value){
  280. $.ajax({
  281. url:"<ss:serv name='ry_selBaseInfoByKah'/>",
  282. type:"post",
  283. data:{
  284. kah:value
  285. },
  286. dataType:"json",
  287. success:function(data){
  288. if (data.ssCode != 0) {
  289. alert(data.ssMsg);
  290. return;
  291. }
  292. var d = data.ssData;
  293. document.getElementById('xm').innerHTML = d.xm;
  294. document.getElementById('ryh').innerHTML = d.ryh;
  295. if (d.rylbm != 1100) { // 不是学员。Lin
  296. if (d.bmmc)
  297. document.getElementById('bmbj').innerHTML = d.bmmc;
  298. else
  299. document.getElementById('bmbj').innerHTML = "(无)";
  300. } else {
  301. if (d.bjmc)
  302. document.getElementById('bmbj').innerHTML = d.bjmc;
  303. else
  304. document.getElementById('bmbj').innerHTML = "(无)";
  305. }
  306. <%-- 再去掉,不显示。Lin
  307. document.getElementById('xfye').innerHTML = d.xfye;
  308. --%>
  309. }
  310. });
  311. }
  312. </script>
  313. <script type="text/javascript" src="/js/validate/validator-rules.js"></script><%-- 功能说明:个人退费页接入桌面端校验规则,支持 SsInp 红线提示 by xu 20260323 --%>
  314. <script type="text/javascript" src="/js/validate/validation-manager.js"></script><%-- 功能说明:个人退费页接入桌面端校验管理器,支持 SsInp 红线提示 by xu 20260323 --%>
  315. <script>
  316. var GRTF_LABEL_MAP = window.GRTF_LABEL_MAP || {};
  317. // 功能说明:记录当前退费类别的最新 value/label,避免 onoff 隐藏值切换时机导致金额校验判断失真 by xu 20260323
  318. var CURRENT_GRTF_CATEGORY_STATE = {
  319. value: "",
  320. label: ""
  321. };
  322. // 功能说明:退费类别变化后延后一拍重跑金额校验,确保隐藏字段值更新后再清理红线状态 by xu 20260323
  323. function handleGrczlbmChange(groupValue, value, label){
  324. CURRENT_GRTF_CATEGORY_STATE.value = groupValue == null ? "" : groupValue.toString();
  325. CURRENT_GRTF_CATEGORY_STATE.label = label == null ? "" : label.toString();
  326. setTimeout(function(){
  327. if (window.ssVm && typeof window.ssVm.validateField === "function") {
  328. window.ssVm.validateField("je");
  329. return;
  330. }
  331. validateNegativeAmountByCategory(false);
  332. }, 0);
  333. }
  334. // 功能说明:初始化个人退费页金额负数校验规则,整页金额都必须输入负数 by xu 20260323
  335. function initNegativeAmountValidation(){
  336. if (!document.querySelector('[name="je"]') || !document.querySelector('[name="grczlbm"]')) {
  337. return;
  338. }
  339. if (!window.ssVm || typeof window.ssVm.add !== "function" || window.ss.dom._grczGrtfNegativeAmountValidationInited) {
  340. return;
  341. }
  342. window.ss.dom._grczGrtfNegativeAmountValidationInited = true;
  343. window.ssVm.add("ss.commonValidator.custom", ["je"], {
  344. msgPrfx: "金额",
  345. relField: "grczlbm",
  346. validate: function(value, categoryValue){
  347. var currentLabel = getCurrentGrczlbmLabel(categoryValue);
  348. if (value == null || value.toString().trim() === "") {
  349. return true;
  350. }
  351. if (isValidNegativeAmountText(value)) {
  352. return true;
  353. }
  354. return {
  355. valid: false,
  356. message: (currentLabel || "退费") + "金额只能输入负数"
  357. };
  358. }
  359. }, {
  360. je: window.ss.dom.formElemConfig.je ? window.ss.dom.formElemConfig.je.val : ""
  361. });
  362. }
  363. // 功能说明:根据当前退费类别值读取显示文案,供联动校验和错误提示复用 by xu 20260323
  364. function getCurrentGrczlbmLabel(categoryValue){
  365. var currentValue = categoryValue == null ? getCurrentGrczlbmValue() : categoryValue.toString();
  366. if (CURRENT_GRTF_CATEGORY_STATE.label && (!currentValue || CURRENT_GRTF_CATEGORY_STATE.value === currentValue)) {
  367. return CURRENT_GRTF_CATEGORY_STATE.label;
  368. }
  369. return GRTF_LABEL_MAP[currentValue] || "";
  370. }
  371. // 功能说明:统一判断金额文本是否为合法负数,供提交校验和 ssVm 规则复用 by xu 20260323
  372. function isValidNegativeAmountText(value){
  373. var amountText = value == null ? "" : value.toString().trim();
  374. if (!amountText) {
  375. return false;
  376. }
  377. var amountNumber = Number(amountText);
  378. return !isNaN(amountNumber) && amountNumber < 0;
  379. }
  380. // 功能说明:根据当前退费类别值读取隐藏字段值,供金额校验复用 by xu 20260323
  381. function getCurrentGrczlbmValue(){
  382. var vm = getFormAppVm();
  383. if (vm && vm.grczlbm != null && vm.grczlbm !== "") {
  384. return vm.grczlbm.toString();
  385. }
  386. var categoryElem = document.querySelector('[name="grczlbm"]');
  387. return categoryElem && categoryElem.value != null ? categoryElem.value.toString() : "";
  388. }
  389. // 功能说明:统一校验个人退费金额必须为负数,不再区分具体退费类别 by xu 20260323
  390. function validateNegativeAmountByCategory(showMsg){
  391. var currentLabel = getCurrentGrczlbmLabel();
  392. var jeElem = document.querySelector('[name="je"]');
  393. if (!jeElem) {
  394. return true;
  395. }
  396. if (isValidNegativeAmountText(jeElem.value)) {
  397. return true;
  398. }
  399. if (showMsg !== false) {
  400. alert((currentLabel || "退费") + "金额只能输入负数");
  401. jeElem.focus();
  402. }
  403. return false;
  404. }
  405. // 功能说明:个人退费页提交前先走 ssVm 全量校验,确保显示 SsInp 左侧红线与底部提示 by xu 20260323
  406. function submitGrtfForm(){
  407. /* 去掉,不是 个人退费页 了。Lin
  408. if (window.ssVm && window.ssVm.validations && window.ssVm.validations.size > 0) {
  409. var validateResult = window.ssVm.validateAll();
  410. if (!validateResult.valid) {
  411. return false;
  412. }
  413. } else if (!validateNegativeAmountByCategory(true)) {
  414. return false;
  415. }
  416. */
  417. var formElem = document.querySelector("form");
  418. if (!formElem) {
  419. alert("表单不存在");
  420. return false;
  421. }
  422. formElem.action = "<ss:serv name='ka_lr_tj' dest='addSure' parm='{thisViewObject:\"ka\",dataType:\"update\"}'/>";
  423. ss.display.resizeComponent(881,361,515,515);
  424. formElem.submit();
  425. return true;
  426. }
  427. if (window.SS && typeof SS.ready === "function") {
  428. SS.ready(function(){
  429. // 功能说明:页面初始化后同步当前退费类别状态,避免首次校验读取不到最新类别文案 by xu 20260323
  430. CURRENT_GRTF_CATEGORY_STATE.value = getCurrentGrczlbmValue();
  431. CURRENT_GRTF_CATEGORY_STATE.label = GRTF_LABEL_MAP[CURRENT_GRTF_CATEGORY_STATE.value] || "";
  432. // 功能说明:页面初始化后注册个人退费金额负数校验规则,保证 SsInp 输入时直接出现红线提示 by xu 20260323
  433. initNegativeAmountValidation();
  434. });
  435. }
  436. </script>