ka_nfcAdd.ss.jsp 12 KB

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