// 工具方法
const findParentTd = (element) => {
let parent = element.parentElement;
while (parent && parent.tagName !== 'TD') {
parent = parent.parentElement;
}
return parent;
};
const isSameTd = (element1, element2) => {
const td1 = findParentTd(element1);
const td2 = findParentTd(element2);
return td1 && td1 === td2;
};
class ValidationManager {
constructor() {
this.validations = new Map();
this.dependRules = new Map(); // 存储依赖关系
}
// 添加验证规则
add(ruleName, fields, options = {}, priority = 1) {
if (!Array.isArray(fields)) {
fields = [fields];
}
const rule = this._parseRule(ruleName);
if (!rule) return;
fields.forEach(field => {
if (!this.validations.has(field)) {
this.validations.set(field, []);
}
this.validations.get(field).push({
rule: rule,
options: {
msgPrfx: options.msgPrfx || field,
...options
},
priority: priority
});
});
if (options.relField) {
const relElement = document.querySelector(`[name="${options.relField}"]`);
if (relElement) {
relElement.addEventListener('change', () => {
this.validateField(field);
});
}
}
this.initRequiredMarks();
// ssVm.bindForm(".form-container");
}
// 验证单个字段
validateField(field) {
const element = document.querySelector(`[name="${field}"]`);
if (!element) return { valid: true };
const td = findParentTd(element);
if (!td) return { valid: true };
// 获取同一个 td 内的所有需要验证的字段
const tdFields = Array.from(td.querySelectorAll('[name]'))
.map(el => el.getAttribute('name'))
.filter(name => this.validations.has(name));
// 验证所有字段并合并错误信息
let errors = [];
let hasEmptyRequired = false; // 是否有空的必填字段
for (const f of tdFields) {
const element = document.querySelector(`[name="${f}"]`);
const value = element.value;
// 检查是否为空
if (!value || value.trim() === '') {
hasEmptyRequired = true;
}
const result = this._doValidate(f);
if (!result.valid && errors.indexOf(result.message) === -1) {
errors.push(result.message);
}
}
// 检查是否有依赖规则
const dependRule = this.dependRules.get(field);
if (dependRule) {
const { dependField, rules } = dependRule;
const dependElement = document.querySelector(`[name="${dependField}"]`);
if (dependElement) {
const dependValue = dependElement.value;
const validatorName = rules[dependValue];
if (validatorName) {
// 更新验证规则
this.validations.set(field, [{
rule: window.ValidatorRules[validatorName],
options: {
msgPrfx: field,
required: true
}
}]);
}
}
}
// 管理必填标记
let requiredMark = td.querySelector('.required');
if ((hasEmptyRequired || errors.length > 0) && !requiredMark) {
requiredMark = document.createElement('div');
requiredMark.className = 'required';
td.appendChild(requiredMark);
} else if (!hasEmptyRequired && errors.length === 0 && requiredMark) {
requiredMark.remove();
}
// 动态管理错误提示
let errTip = td.querySelector('.err-tip');
if (!errTip && errors.length > 0) {
errTip = document.createElement('div');
errTip.className = 'err-tip';
errTip.style.position = 'absolute';
errTip.style.zIndex = '1';
errTip.innerHTML = `
${errors.join(';')}
${errors.join(';')}
`;
td.appendChild(errTip);
} else if (errTip && errors.length === 0) {
errTip.remove();
} else if (errTip && errors.length > 0) {
errTip.querySelector('.tip').textContent = errors.join(';');
errTip.querySelector('.tip-more').textContent = errors.join(';');
}
return {
valid: errors.length === 0,
message: errors.join(';')
};
}
// 验证所有字段
validateAll() {
const errors = [];
for (const field of this.validations.keys()) {
const result = this.validateField(field);
if (!result.valid) {
errors.push({
field: field,
message: result.message
});
}
}
console.log("errors",errors);
console.log("this.validations",this.validations);
return {
valid: errors.length === 0,
errors: errors
};
}
// 解析规则名称并获取对应的验证规则
_parseRule(ruleName) {
const parts = ruleName.split('.');
const actualRuleName = parts[parts.length - 1];
return ValidatorRules[actualRuleName];
}
//执行所有表单校验,并显示校验结果
validateAllAndShowMsg(){
const result = this.validateAll();
console.log("validateAll",result);
if (!result.valid) {
e.preventDefault();
result.errors.forEach(error => {
const element = document.querySelector(`[name="${error.field}"]`);
if (element) {
const validateComponent = element.closest('.input-container')?.querySelector('.err-tip');
if (validateComponent) {
validateComponent.querySelector('.tip').textContent = error.message;
validateComponent.querySelector('.tip-more').textContent = error.message;
}
}
});
return false;
}
return true;
}
// 绑定到表单提交(本方法已暂停使用)
bindForm(formClass) {
// 创建一个观察器实例
const observer = new MutationObserver((mutations) => {
const form = document.querySelector(formClass);
if (form && !form._bound) {
console.log("Found form, binding submit event");
$(form).on('submit', (e) => {
return this.validateAllAndShowMsg();
});
form._bound = true; // 标记已绑定
observer.disconnect(); // 停止观察
}
});
// 开始观察
observer.observe(document.body, {
childList: true,
subtree: true
});
}
remove(fields) {
// 如果传入单个字段,转换为数组
if (!Array.isArray(fields)) {
fields = [fields];
}
// 遍历字段并移除验证规则
fields.forEach(field => {
if (this.validations.has(field)) {
this.validations.delete(field);
}
});
}
// 私有验证方法
_doValidate(field) {
const rules = this.validations.get(field);
if (!rules) return { valid: true };
const element = document.querySelector(`[name="${field}"]`);
if (!element) return { valid: true };
const value = element.value;
for (const {rule, options} of rules) {
// 如果设置了 required 且值为空,进行必填验证
if (options.required && (!value || value.trim() === '')) {
return {
valid: false,
message: `${options.msgPrfx}不能为空`
};
}
// 执行规则验证,传入完整的 options 以支持关联字段验证
const isValid = rule.validate(value, options);
if (!isValid) {
let message = rule.message;
message = message.replace('{field}', options.msgPrfx);
Object.keys(options).forEach(key => {
message = message.replace(`{${key}}`, options[key]);
});
return {
valid: false,
message: message
};
}
}
return { valid: true };
}
// 初始化必填标记
initRequiredMarks() {
// 先收集所有需要处理的 td
const processedTds = new Set();
// 遍历所有带验证规则的字段
for (const [field, rules] of this.validations.entries()) {
const element = document.querySelector(`[name="${field}"]`);
if (!element) continue;
const td = findParentTd(element);
if (!td || processedTds.has(td)) continue; // 跳过已处理的 td
processedTds.add(td); // 标记该 td 已处理
// 获取同一个 td 内的所有需要验证的字段
const tdFields = Array.from(td.querySelectorAll('[name]'))
.map(el => el.getAttribute('name'))
.filter(name => this.validations.has(name));
// 只要有验证规则就添加必填标记
if (tdFields.length > 0) {
let requiredMark = td.querySelector('.required');
if (!requiredMark) {
requiredMark = document.createElement('div');
requiredMark.className = 'required';
td.appendChild(requiredMark);
}
}
}
}
// 验证字段但不显示错误信息
validateFieldSilent(field) {
const rules = this.validations.get(field);
if (!rules) return { valid: true };
const element = document.querySelector(`[name="${field}"]`);
if (!element) return { valid: true };
const value = element.value;
for (const {rule, options} of rules) {
if (options.required && (!value || value.trim() === '')) {
return { valid: false };
}
if (!value && !options.required) {
return { valid: true };
}
const isValid = rule.validate(value, options);
if (!isValid) {
return { valid: false };
}
}
return { valid: true };
}
}
// 创建全局实例
window.ssVm = window.ssVm || new ValidationManager();
// 在 DOM 加载完成后初始化必填标记