diff --git a/projects/Infrastructure/Extensions/ControllerExtensions.cs b/projects/Infrastructure/Extensions/ControllerExtensions.cs index d8bd179c..ef64e63f 100644 --- a/projects/Infrastructure/Extensions/ControllerExtensions.cs +++ b/projects/Infrastructure/Extensions/ControllerExtensions.cs @@ -1,5 +1,4 @@ -using Infrastructure.Resources; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.Rendering; @@ -56,7 +55,7 @@ namespace Infrastructure.Extensions return new SelectList(values, "Id", "Name", selectedValue); } - public static object CreateJson(this ControllerBase controller, DefaultModelMetadata metadata) + public static object CreateJson(this ControllerBase controller, DefaultModelMetadata metadata, DefaultModelMetadata parent = null) { if (metadata is null) { @@ -69,6 +68,7 @@ namespace Infrastructure.Extensions dynamic json = new ExpandoObject(); var dictionary = (IDictionary)json; //type + if (metadata.IsComplexType && metadata.IsCollectionType) { json.type = "array"; @@ -112,13 +112,6 @@ namespace Infrastructure.Extensions { json.title = metadata.DisplayName; } - //if (metadata.IsRequired) - //{ - // if (metadata.ModelType != typeof(bool)) - // { - // json.required = true; - // } - //} foreach (var attribute in metadata.Attributes.Attributes) { if (attribute is DescriptionAttribute descriptionAttribute) @@ -127,15 +120,9 @@ namespace Infrastructure.Extensions } else if (attribute is RequiredAttribute requiredAttribute) { - var localizedString = localizer.GetString(nameof(RequiredAttribute)); - var message = requiredAttribute.ErrorMessage; - if (!localizedString.ResourceNotFound) - { - message = string.Format(localizedString.Value, metadata.DisplayName); - } json.required = new { - message = localizedString.ResourceNotFound ? requiredAttribute.FormatErrorMessage(metadata.DisplayName) : string.Format(localizedString.Value, metadata.DisplayName) + message = requiredAttribute.GetErrorMessage(localizer, metadata.DisplayName) }; } else if (attribute is RegularExpressionAttribute regularExpressionAttribute) @@ -143,7 +130,7 @@ namespace Infrastructure.Extensions json.pattern = new { regex = regularExpressionAttribute.Pattern, - message = regularExpressionAttribute.ErrorMessage + message = regularExpressionAttribute.GetErrorMessage(localizer, metadata.DisplayName) }; } else if (attribute is DataTypeAttribute dataTypeAttribute) @@ -179,37 +166,50 @@ namespace Infrastructure.Extensions } else if (attribute is MinLengthAttribute minLengthAttribute) {//minLength - json.minLength = minLengthAttribute.Length; + json.minLength = new + { + min = minLengthAttribute.Length, + message = minLengthAttribute.GetErrorMessage(localizer, metadata.DisplayName) + }; } else if (attribute is MaxLengthAttribute maxLengthAttribute) {//maxLength - json.maxLength = maxLengthAttribute.Length; + json.maxLength = new + { + max = maxLengthAttribute.Length, + message = maxLengthAttribute.GetErrorMessage(localizer, metadata.DisplayName) + }; } else if (attribute is StringLengthAttribute stringLengthAttribute) {//minLength,maxLength - json.minLength = stringLengthAttribute.MinimumLength; - json.maxLength = stringLengthAttribute.MaximumLength; + json.length = new + { + min = stringLengthAttribute.MinimumLength, + max = stringLengthAttribute.MaximumLength, + message = stringLengthAttribute.GetErrorMessage(localizer, metadata.DisplayName, + stringLengthAttribute.MinimumLength.ToString(), + stringLengthAttribute.MaximumLength.ToString()) + }; } else if (attribute is RangeAttribute rangeAttribute) {//minimum,maximum - json.minimum = rangeAttribute.Minimum; - json.maximum = rangeAttribute.Maximum; - } - else if (attribute is ScaffoldColumnAttribute scaffoldColumnAttribute) - {//scaffold 自定义 - json.scaffold = scaffoldColumnAttribute.Scaffold; - } - else if (attribute is HiddenInputAttribute hiddenInputAttribute) - {//hidden 自定义 - json.hidden = hiddenInputAttribute.DisplayValue; - } - else if (attribute is UIHintAttribute uIHintAttribute) - {//ui 自定义 - json.ui = uIHintAttribute.UIHint.ToLower(); + json.range = new + { + minimum = rangeAttribute.Minimum, + maximum = rangeAttribute.Maximum, + message = rangeAttribute.GetErrorMessage(localizer, metadata.DisplayName, + rangeAttribute.Minimum.ToString(), + rangeAttribute.Minimum.ToString()) + }; } else if (attribute is CompareAttribute compareAttribute) {//compare 自定义 - json.compare = compareAttribute.OtherProperty; + var compareName = parent.Properties.FirstOrDefault(o => o.PropertyName == compareAttribute.OtherProperty)?.DisplayName; + json.compare = new + { + other = compareAttribute.OtherProperty, + message = compareAttribute.GetErrorMessage(localizer, metadata.DisplayName, compareName) + }; } else if (attribute is RemoteAttribute remoteAttribute) {//remote 自定义 @@ -218,36 +218,67 @@ namespace Infrastructure.Extensions { url = controller.Url.Action(routeData["action"].ToString(), routeData["controller"].ToString(), new { Area = routeData["area"].ToString() }), remoteAttribute.AdditionalFields, - //remoteAttribute. + message = remoteAttribute.GetErrorMessage(localizer, metadata.DisplayName) }; } + else if (attribute is ScaffoldColumnAttribute scaffoldColumnAttribute) + {//scaffold 自定义 + json.scaffold = scaffoldColumnAttribute.Scaffold; + } + else if (attribute is HiddenInputAttribute hiddenInputAttribute) + {//hidden 自定义 + json.hidden = hiddenInputAttribute.DisplayValue; + } + else if (attribute is UIHintAttribute uIHintAttribute) + {//ui 自定义 + json.ui = uIHintAttribute.UIHint.ToLower(); + } } dynamic properties = new ExpandoObject(); var propertiesDictionary = (IDictionary)properties; - //var requiredList = new List(); if (metadata.IsComplexType && !metadata.IsCollectionType && metadata.Properties.Any()) { foreach (var item in metadata.Properties) { var modelMetadataItem = item as DefaultModelMetadata; - propertiesDictionary[item.PropertyName] = CreateJson(controller, modelMetadataItem); - //if (modelMetadataItem.IsRequired) - //{ - // requiredList.Add(item.PropertyName); - //} + propertiesDictionary[item.PropertyName] = CreateJson(controller, modelMetadataItem, metadata); } json.properties = properties; - // if (requiredList.Any()) - // { - // json.required = requiredList; - // } } return json; } catch (Exception ex) { + ex.PrintStack(); return null; } } + + public static string GetErrorMessage(this ValidationAttribute attribute, IStringLocalizer localizer, params string[] args) + { + var localizedString = localizer.GetString(attribute.GetType().Name); + //return localizedString.ResourceNotFound ? attribute.FormatErrorMessage(args[0]) : string.Format(localizedString.Value, args); + var format = attribute.ErrorMessage; + if (localizedString.ResourceNotFound) + { + if (string.IsNullOrEmpty(format)) + { + format = attribute.GetType().GetProperty("ErrorMessageString", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(attribute) as string; + } + } + else + { + format = localizedString.Value; + } + try + { + return string.Format(format, args); + } + catch (Exception ex) + { + ex.PrintStack(); + } + return format; + } } } \ No newline at end of file diff --git a/projects/IoT.Shared/Application/Models/EditNodeModel.cs b/projects/IoT.Shared/Application/Models/EditNodeModel.cs index 31daedaf..44951990 100644 --- a/projects/IoT.Shared/Application/Models/EditNodeModel.cs +++ b/projects/IoT.Shared/Application/Models/EditNodeModel.cs @@ -1,8 +1,7 @@ using Infrastructure.Application; -using Infrastructure.Resources; +using Microsoft.AspNetCore.Mvc; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Security.AccessControl; namespace Application.Models { @@ -31,6 +30,9 @@ namespace Application.Models [Display(Name = "节点名称")] [MaxLength(64, ErrorMessage = "{0}最大长度为{1}")] [Required(ErrorMessage = nameof(RequiredAttribute))] + //for temp test start + //[Remote("Valid", "Ajax", "Admin", AdditionalFields = "CategoryNumber", ErrorMessage = "test ajax valid")] + //for temp test end public string Name { get; set; } [Display(Name = "节点编号")] diff --git a/projects/WebMVC/wwwroot/router/shared/edit.html b/projects/WebMVC/wwwroot/router/shared/edit.html index 1f11a1b4..9b54798a 100644 --- a/projects/WebMVC/wwwroot/router/shared/edit.html +++ b/projects/WebMVC/wwwroot/router/shared/edit.html @@ -80,22 +80,6 @@ mounted: function () { this.load(); }, - //watch: { - // 'data.model': { - // handler: function (newModel, oldModel) { - // if (oldModel === null) { - // return; - // } - - // _.forIn(this.data.schema.properties, function (value, key) { - // //console.log(newModel[key] + ':' + oldModel[key]); - // if (newModel[key] !== oldModel[key]) { - // } - // }); - // }, - // deep: true - // } - //}, methods: { load: function () { var vm = this; @@ -104,39 +88,88 @@ vm.data = response.data; }); }, - change: function (e) { - var name = e.target.name; - var value = e.target.value; + change: function (name) { + var vm = this; + var value = this.data.model[name]; var property = this.data.schema.properties[name]; - if (property.required) { - var error = Enumerable.from(this.data.errors).firstOrDefault(o => o.key.toLowerCase() === name.toLowerCase()); - if (value) { - if (error) { - var index = _.findIndex(this.data.errors, o => o.key.toLowerCase() === name.toLowerCase()); - this.data.errors.splice(index, 1); + this.validInternal(property.required, name, o => value); + this.validInternal(property.pattern, name, o => new RegExp(property.pattern.regex).test(value)); + this.validInternal(property.minLength, name, o => value.length >= property.minLength.min); + this.validInternal(property.maxLength, name, o => value.length <= property.maxLength.max); + this.validInternal(property.length, name, o => value.length >= property.length.min && value.length <= property.length.max); + this.validInternal(property.range, name, o => parseFloat(value) >= property.range.minimum && parseFloat(value) <= property.range.maximum); + this.validInternal(property.compare, name, o => o === vm.data.model[vm.camelCased(name)]); + this.validInternal(property.remote, name, o => { + var url = property.remote.url + '?' + name + '=' + value; + if (property.remote.additionalFields) { + var keys = property.remote.additionalFields.split(','); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + url = url + '&' + key + '=' + vm.data.model[vm.camelCased(key)]; } } - else { - var message = property.required.message; - if (!error) { - error = { - key: name, - value: { - errors: [{ errorMessage: message }] - } - }; - this.data.errors.push(error); + axios.get(url).then(function (response) { + if (response.data === true) { + vm.removeError(name); } else { - if (!Enumable.from(error.value.errors).any(o => o === message)) { - error.value.errors.push(property.required); - } + var message = response.data === false ? property.remote.message : response.data; + vm.updateError(name, message); + } + }).catch(function (error) { + vm.removeError(name); + vm.updateError(name, 'network error'); + }); + return false; + }, 'loading'); + console.log(this.data.errors); + }, + validInternal: function (validator, name, valid, message) { + if (!validator) { + return; + } + var property = this.data.schema.properties[name]; + if (valid()) { + this.removeError(name); + } + else { + message = message || validator.message; + this.updateError(name, message); + } + }, + camelCased: function (name) { + return name[0].toLowerCase() + name.substr(1); + }, + removeError: function (name, message) { + var error = Enumerable.from(this.data.errors).firstOrDefault(o => o.key.toLowerCase() === name.toLowerCase()); + if (error) { + var index = _.findIndex(this.data.errors, o => o.key.toLowerCase() === name.toLowerCase()); + this.data.errors.splice(index, 1); + } + }, + updateError: function (name, message) { + var error = Enumerable.from(this.data.errors).firstOrDefault(o => o.key.toLowerCase() === name.toLowerCase()); + if (!error) { + error = { + key: name, + value: { + errors: [{ errorMessage: message }] } + }; + this.data.errors.push(error); + } + else { + if (!Enumerable.from(error.value.errors).any(o => o === message)) { + error.value.errors.push({ errorMessage: message }); } } }, valid: function () { - return true; + var vm = this + _.forIn(this.data.schema.properties, function (value, key) { + vm.change(key); + }); + return this.data.errors.length === 0; }, getValidationSummary: function (excludePropertyErrors) { var query = Enumerable.from(this.data.errors); diff --git a/projects/WebMVC/wwwroot/router/shared/edit/string.html b/projects/WebMVC/wwwroot/router/shared/edit/string.html index 58c68ef2..85155e15 100644 --- a/projects/WebMVC/wwwroot/router/shared/edit/string.html +++ b/projects/WebMVC/wwwroot/router/shared/edit/string.html @@ -1,6 +1,6 @@