validation-manager.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // 工具方法
  2. const findParentTd = (element) => {
  3. let parent = element.parentElement;
  4. while (parent && parent.tagName !== 'TD') {
  5. parent = parent.parentElement;
  6. }
  7. return parent;
  8. };
  9. const isSameTd = (element1, element2) => {
  10. const td1 = findParentTd(element1);
  11. const td2 = findParentTd(element2);
  12. return td1 && td1 === td2;
  13. };
  14. class ValidationManager {
  15. constructor() {
  16. this.validations = new Map();
  17. this.dependRules = new Map(); // 存储依赖关系
  18. }
  19. // 添加验证规则
  20. add(ruleName, fields, options = {}, fieldValMap, priority = 1) {//增加fieldValMap参数,传入表单元素在页面打开时的默认值 Ben(20260107)
  21. if (!Array.isArray(fields)) {
  22. fields = [fields];
  23. }
  24. const rule = this._parseRule(ruleName);
  25. if (!rule) return;
  26. fields.forEach(field => {
  27. if (!this.validations.has(field)) {
  28. this.validations.set(field, []);
  29. }
  30. let val = fieldValMap[field];//add by Ben(20260107)
  31. this.validations.get(field).push({
  32. rule: rule,
  33. options: {
  34. msgPrfx: options.msgPrfx || field,
  35. ...options
  36. },
  37. priority: priority,
  38. 'val':val, //传入的val值,用于页面刚打开时初始化非空红线时判断用 by Ben(20260107)
  39. 'ruleName':ruleName //传入ruleName规则名称,用于页面刚打开时初始化非空红线时判断用 by Ben(20260107)
  40. });
  41. });
  42. if (options.relField) {
  43. const relElement = document.querySelector(`[name="${options.relField}"]`);
  44. if (relElement) {
  45. relElement.addEventListener('change', () => {
  46. this.validateField(field);
  47. });
  48. }
  49. }
  50. this.initRequiredMarks();
  51. // ssVm.bindForm(".form-container");
  52. }
  53. // 验证单个字段
  54. validateField(field) {
  55. const element = document.querySelector(`[name="${field}"]`);
  56. if (!element) return { valid: true };
  57. const td = findParentTd(element);
  58. if (!td) return { valid: true };
  59. // 获取同一个 td 内的所有需要验证的字段
  60. const tdFields = Array.from(td.querySelectorAll('[name]'))
  61. .map(el => el.getAttribute('name'))
  62. .filter(name => this.validations.has(name));
  63. // 验证所有字段并合并错误信息
  64. let errors = [];
  65. let hasEmptyRequired = false; // 是否有空的必填字段
  66. for (const f of tdFields) {
  67. const element = document.querySelector(`[name="${f}"]`);
  68. const value = element.value;
  69. // 检查是否为空
  70. if (!value || value.trim() === '') {
  71. hasEmptyRequired = true;
  72. }
  73. const result = this._doValidate(f);
  74. if (!result.valid && errors.indexOf(result.message) === -1) {
  75. errors.push(result.message);
  76. }
  77. }
  78. // 检查是否有依赖规则
  79. const dependRule = this.dependRules.get(field);
  80. if (dependRule) {
  81. const { dependField, rules } = dependRule;
  82. const dependElement = document.querySelector(`[name="${dependField}"]`);
  83. if (dependElement) {
  84. const dependValue = dependElement.value;
  85. const validatorName = rules[dependValue];
  86. if (validatorName) {
  87. // 更新验证规则
  88. this.validations.set(field, [{
  89. rule: window.ValidatorRules[validatorName],
  90. options: {
  91. msgPrfx: field,
  92. required: true
  93. }
  94. }]);
  95. }
  96. }
  97. }
  98. // 管理必填标记
  99. let requiredMark = td.querySelector('.required');
  100. if ((hasEmptyRequired || errors.length > 0) && !requiredMark) {
  101. requiredMark = document.createElement('div');
  102. requiredMark.className = 'required';
  103. td.appendChild(requiredMark);
  104. } else if (!hasEmptyRequired && errors.length === 0 && requiredMark) {
  105. requiredMark.remove();
  106. }
  107. // 动态管理错误提示
  108. let errTip = td.querySelector('.err-tip');
  109. if (!errTip && errors.length > 0) {
  110. errTip = document.createElement('div');
  111. errTip.className = 'err-tip';
  112. errTip.style.position = 'absolute';
  113. errTip.style.zIndex = '1';
  114. errTip.innerHTML = `
  115. <div class="tip">${errors.join(';')}</div>
  116. <div class="tip-more">${errors.join(';')}</div>
  117. `;
  118. td.appendChild(errTip);
  119. } else if (errTip && errors.length === 0) {
  120. errTip.remove();
  121. } else if (errTip && errors.length > 0) {
  122. errTip.querySelector('.tip').textContent = errors.join(';');
  123. errTip.querySelector('.tip-more').textContent = errors.join(';');
  124. }
  125. return {
  126. valid: errors.length === 0,
  127. message: errors.join(';')
  128. };
  129. }
  130. // 验证所有字段
  131. validateAll() {
  132. const errors = [];
  133. for (const field of this.validations.keys()) {
  134. const result = this.validateField(field);
  135. if (!result.valid) {
  136. errors.push({
  137. field: field,
  138. message: result.message
  139. });
  140. }
  141. }
  142. console.log("errors",errors);
  143. console.log("this.validations",this.validations);
  144. return {
  145. valid: errors.length === 0,
  146. errors: errors
  147. };
  148. }
  149. // 解析规则名称并获取对应的验证规则
  150. _parseRule(ruleName) {
  151. const parts = ruleName.split('.');
  152. const actualRuleName = parts[parts.length - 1];
  153. return ValidatorRules[actualRuleName];
  154. }
  155. //执行所有表单校验,并显示校验结果
  156. validateAllAndShowMsg(){
  157. const result = this.validateAll();
  158. console.log("validateAll",result);
  159. if (!result.valid) {
  160. e.preventDefault();
  161. result.errors.forEach(error => {
  162. const element = document.querySelector(`[name="${error.field}"]`);
  163. if (element) {
  164. const validateComponent = element.closest('.input-container')?.querySelector('.err-tip');
  165. if (validateComponent) {
  166. validateComponent.querySelector('.tip').textContent = error.message;
  167. validateComponent.querySelector('.tip-more').textContent = error.message;
  168. }
  169. }
  170. });
  171. return false;
  172. }
  173. return true;
  174. }
  175. // 绑定到表单提交(本方法已暂停使用)
  176. bindForm(formClass) {
  177. // 创建一个观察器实例
  178. const observer = new MutationObserver((mutations) => {
  179. const form = document.querySelector(formClass);
  180. if (form && !form._bound) {
  181. console.log("Found form, binding submit event");
  182. $(form).on('submit', (e) => {
  183. return this.validateAllAndShowMsg();
  184. });
  185. form._bound = true; // 标记已绑定
  186. observer.disconnect(); // 停止观察
  187. }
  188. });
  189. // 开始观察
  190. observer.observe(document.body, {
  191. childList: true,
  192. subtree: true
  193. });
  194. }
  195. remove(fields) {
  196. // 如果传入单个字段,转换为数组
  197. if (!Array.isArray(fields)) {
  198. fields = [fields];
  199. }
  200. // 遍历字段并移除验证规则
  201. fields.forEach(field => {
  202. if (this.validations.has(field)) {
  203. this.validations.delete(field);
  204. }
  205. });
  206. }
  207. // 私有验证方法
  208. _doValidate(field) {
  209. const rules = this.validations.get(field);
  210. if (!rules) return { valid: true };
  211. const element = document.querySelector(`[name="${field}"]`);
  212. if (!element) return { valid: true };
  213. const value = element.value;
  214. for (const {rule, options} of rules) {
  215. // 如果设置了 required 且值为空,进行必填验证
  216. if (options.required && (!value || value.trim() === '')) {
  217. return {
  218. valid: false,
  219. message: `${options.msgPrfx}不能为空`
  220. };
  221. }
  222. // 执行规则验证,传入完整的 options 以支持关联字段验证
  223. const isValid = rule.validate(value, options);
  224. if (!isValid) {
  225. let message = rule.message;
  226. message = message.replace('{field}', options.msgPrfx);
  227. Object.keys(options).forEach(key => {
  228. message = message.replace(`{${key}}`, options[key]);
  229. });
  230. return {
  231. valid: false,
  232. message: message
  233. };
  234. }
  235. }
  236. return { valid: true };
  237. }
  238. // 初始化必填标记
  239. initRequiredMarks() {
  240. // 先收集所有需要处理的 td
  241. const processedTds = new Set();
  242. // 遍历所有带验证规则的字段
  243. for (const [field, rules] of this.validations.entries()) {
  244. const element = document.querySelector(`[name="${field}"]`);
  245. if (!element) continue;
  246. const td = findParentTd(element);
  247. if (!td || processedTds.has(td)) continue; // 跳过已处理的 td
  248. processedTds.add(td); // 标记该 td 已处理
  249. // 获取同一个 td 内的所有需要验证的字段
  250. const tdFields = Array.from(td.querySelectorAll('[name]'))
  251. .map(el => el.getAttribute('name'))
  252. .filter(name => {//条件改为:如果字段存在非空校验规则,且当前值为空,filter才通过返回tdField Ben(20260107)
  253. let vArr = this.validations.get(name);
  254. for(let i=0;i<vArr.length;i++){
  255. let v = vArr[i];
  256. console.log('@@v:'+JSON.stringify(v));
  257. if(v&&(v.ruleName==='ss.commonValidator.notNull')&&(v.val==='0'||v.val===''||v.val===undefined||v.val===null))
  258. return true;
  259. else
  260. return false;
  261. }
  262. });
  263. // 只要有验证规则就添加必填标记
  264. if (tdFields.length > 0) {
  265. let requiredMark = td.querySelector('.required');
  266. if (!requiredMark) {
  267. requiredMark = document.createElement('div');
  268. requiredMark.className = 'required';
  269. td.appendChild(requiredMark);
  270. }
  271. }
  272. }
  273. }
  274. // 验证字段但不显示错误信息
  275. validateFieldSilent(field) {
  276. const rules = this.validations.get(field);
  277. if (!rules) return { valid: true };
  278. const element = document.querySelector(`[name="${field}"]`);
  279. if (!element) return { valid: true };
  280. const value = element.value;
  281. for (const {rule, options} of rules) {
  282. if (options.required && (!value || value.trim() === '')) {
  283. return { valid: false };
  284. }
  285. if (!value && !options.required) {
  286. return { valid: true };
  287. }
  288. const isValid = rule.validate(value, options);
  289. if (!isValid) {
  290. return { valid: false };
  291. }
  292. }
  293. return { valid: true };
  294. }
  295. }
  296. // 创建全局实例
  297. window.ssVm = window.ssVm || new ValidationManager();
  298. // 在 DOM 加载完成后初始化必填标记