189 8069 5689

Angular如何使用输入框实现自定义验证功能

这篇文章将为大家详细讲解有关Angular如何使用输入框实现自定义验证功能,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

创新互联建站主要为客户提供服务项目涵盖了网页视觉设计、VI标志设计、成都全网营销、网站程序开发、HTML5响应式成都网站建设手机网站制作设计、微商城、网站托管及成都网站维护、WEB系统开发、域名注册、国内外服务器租用、视频、平面设计、SEO优化排名。设计、前端、后端三个建站步骤的完善服务体系。一人跟踪测试的建站服务标准。已经为柴油发电机行业客户提供了网站营销推广服务。

此插件使用angular.js、JQuery实现。(jQuery的引入需在angular 之前)

用户可以 在输入框输入数据后验证 必填项、整数型、浮点型验证。

如果在form 里面的输入框验证,可以点击 提交按钮后,实现 必填项验证。

效果图如下:

Angular如何使用输入框实现自定义验证功能

(1)验证未通过时,背景标红等样式为

input.ng-invalid, select.ng-invalid {
   background-color: #ee82ee !important;
   border: 1px solid #CCC;
  }
  .qtip {
   position: absolute;
   max-width: 260px;
   display: none;
   min-width: 50px;
   font-size: 10.5px;
   line-height: 12px;
   direction: ltr;
  }
  .qtip-content {
   position: relative;
   padding: 5px 9px;
   overflow: hidden;
   text-align: left;
   word-wrap: break-word;
  }
  .qtip-rounded, .qtip-tipsy {
   -moz-border-radius: 5px;
   -webkit-border-radius: 5px;
   border-radius: 5px;
  }
  .qtipmodal-ie6fix {
   position: absolute !important;
  }
  .box-shadow-tips {
   background-color: #F63;
   border-color: #F5A88F;
   color: white;
   -moz-box-shadow: 2px 2px 2px #969696;
   -webkit-box-shaow: 2px 2px 2px #969696;
   box-shadow: 2px 2px 2px #969696;
  }

因为angular.js 内置验证未通过时,会自动为 标签 增加 .ng-invalid 样式,因为这里重写此样式

 input.ng-invalid, select.ng-invalid {
   background-color: #ee82ee !important;
   border: 1px solid #CCC;
  } 

 (2)HTML 代码如下



 
  
  
    

(3)此插件使用 directive myValid 实现

app.directive('myValid', ['$parse', 'uiTipsFactory', 'uiValidFactory', function ($parse, tips, valid) {
  var uiValidAttrIdName = 'ui-valid-id';
  return {
   restrict: 'A',
   require: 'ngModel',
   link: function (scope, el, attrs, ctrl) {
    var validId = el.attr(uiValidAttrIdName);
    if (!validId) {
     validId = Math.guid();
     el.attr(uiValidAttrIdName, validId);
    }
    var getRules = function () {
     return attrs.myValid;
    };
    var lastOldRules;
    var validFn = function (value, oldRules) {
     var sp = '_';
     var rules = getRules();
     var r = valid.check(value, rules, scope, attrs.uiValidTips);
     if (lastOldRules && !oldRules) {
      oldRules = lastOldRules;
     }
     if (r.flag && oldRules) {
      rules = rules ? rules + ' ' + oldRules : oldRules;
     }
     if (rules) {
      var arrInner = rules.split(' ');
      var i = 0;
      for (; i < arrInner.length; i++) {
       var oneRule = arrInner[i];
       if (!oneRule.trim()) {
        continue;
       }
       ctrl.$setValidity(attrs.ngModel + sp + oneRule, r.flag ? true : oneRule != r.rule);
      }
     }
     if (!r.flag) {
      tips.on(el, r.msg);
     } else {
      tips.off(el);
     }
     return r.flag;
    };
    var init = function () {
     var rules = getRules();
     if (!rules) {
      return;
     }
     var parsers = ctrl.$parsers;
     if (parsers && parsers.length > 0) {
      parsers.clean();
     }
     parsers.unshift(function (value) {
      return validFn(value) ? value : undefined;
     });
    };
    scope.$watch(attrs.ngModel, function (newVal, oldVal) {
     if (newVal === oldVal) {
      return;
     }
     if (ctrl.$modelValue != undefined && (ctrl.$invalid || el.hasClass('ng-invalid'))) {
      validFn(ctrl.$modelValue);
     }
    });
    scope.$watch(getRules, function (newRules, oldRules) {
     init();
     lastOldRules = oldRules;
     if (ctrl.$modelValue === undefined || ctrl.$modelValue === null) {
      var needValid = false;
      el.hasClass('ng-invalid');
      var isValNaN = ctrl.$viewValue !== ctrl.$viewValue;
      if (ctrl.$invalid || (ctrl.$viewValue !== undefined && !isValNaN)) {
       needValid = true;
      }
      if (needValid) {
       ctrl.$setViewValue(ctrl.$viewValue);
      }
     } else {
      if (!ctrl.$dirty && attrs.dirtyCheck) {
       console.log('----');
      } else {
       validFn(ctrl.$modelValue, oldRules);
      }
     }
    });
   }
  }
 }]);

通过 监听 attrs.ngModel,验证规则 rules ,ctrl.$parser 来实现 输入框内容改变的响应。

一旦使用此directive,则动态为当前输入框添加ID,以便在 验证通过后,改变输入框的验证背景信息。 

(4)验证逻辑处理 uiValidFactory

app.factory('uiValidFactory', ['$parse', 'uiTipsFactory', function ($parse, tips) {
  return {
   check: function (val, rules, $scope, defaultTips, extendParam) {
    if (!rules) {
     return {
      flag: true
     };
    }
    var rulesArr = rules.split(' '),
      isBlank = val === null || val === undefined || val === '' || ('' + val === '');
    //如果不是必填项 且没有输入值 则清除提示框
    if ($.inArray('r', rulesArr) === -1 && isBlank) {
     return {
      flag: true
     }
    }
    var i = 0, len = rulesArr.length;
    for (; i < len; i++) {
     var rule = rulesArr[i];
     if (!rule) {
      continue;
     }
     var flag = true;
     if ('r' === rule) {
      //如果是必填项,有值 返回true
      flag = !isBlank;
     } else if (rule.contains(':')) {
      //如果校验规则是 fn:ctrl.certCheck
      flag = this.checkRule(val, rule.split(/:/), $scope, extendParam);
     } else {
      //校验 规则是 int 用正则匹配 数字 邮箱 长度
      var pat = this.pats[rule];
      if (pat instanceof RegExp) {
       if (angular.isString(val)) {
        flag = this.mat(val, pat);
       }
      } else if (angular.isFunction(pat)) {
       flag = pat(val);
      } else {
       flag = false;
      }
     }
     //这是干什么的呢
     if (angular.isString(flag)) {
      return {
       flag: false,
       msg: flag,
       rule: rule
      }
     }
     if (flag === false) {
      var msg = this.getMsg(rule, defaultTips) || this.getMsg('tips.valid');
      console.log(msg);
      return {
       flag: false,
       msg: msg,
       rule: rule
      }
     }
    }
    return {
     flag: true
    }
   },
   checkRule: function (val, ruleArr, $scope, extendParam) {
    //ruleArr fn:certCheck
    var rule = ruleArr[0];
    if (rule === 'fn') {
     fnName = ruleArr[1];//指定被调函数的名字 certCheck
     var fn = $parse(fnName)($scope);
     if (!fn) {
      return true;
     }
     return fn.call($scope, val, extendParam);
    } else {
     return true;
    }
   },
   checkValidForm: function (formName) {
    //只检查必填项
    //使用属性筛选器 获得里面所有的元素
    var formContext = $('form[name="{0}"],[ng-form="{0}"],[data-ng-form="{0}"]'.format(formName)),
      validList = formContext.find('[my-valid]');//validList 不是数组,是伪数组
    if (!validList.length) {
     return;
    }
    var that = this,
      validFlags = [];
    validList.each(function () {
       var ele = $(this),
         val = ele.val(),
         ruleStr = ele.attr('my-valid');
       if (!ruleStr) {
        return true;
       }
       if (angular.isString(val)) {
        val = val.trim();
       }
       var validRules = ruleStr.split(' ');
       if ($.inArray('r', validRules) != -1 && !val) {
        var modelValue = ele.attr('ng-model') || ele.attr('data-ng-model');
        validFlags.push(modelValue);
        tips.on(ele, that.getMsg('r'));
       }
      }
    );
    return validFlags;
   },
   mat: function (val, pat) {
    if (!pat) {
     return;
    }
    return pat.test(val);
   }
   ,
   getMsg: function (rule, tips) {
    tips = tips || '';
    //可以在界面上直接写 tips
    if (tips && tips.contains(':')) {
     return tips;
    }
    var msg = this.msgs[rule];
    if (msg) {
     var params0 = tips.contains(':') ? tips.split(/:/)[0] : '';
     var params1 = '';
     if (rule.startsWith('min') || rule.startsWith('max')) {
      var ruleArr = rule.split(/:/);
      params1 = ruleArr[ruleArr.length - 1];
     }
     return msg.format(params0, params1);
    } else {
    }
   }
   ,
   regPat: function (code, pat, msg) {
    if (this.pat[code]) {
     return;
    }
    this.pats[code] = pat;
    this.msgs[code] = msg;
   }
   ,
   msgs: {
    'r': '必填',
    'int': '{0}必须为整数'
   }
   ,
   pats: {
    'int': /^[\-\+]?([0-9]+)$/
   }
  }
 }
 ])
 ;

       通过获取输入框 ele.myValid  验证规则,

1、如果是必填,则返回 标红此输入框,鼠标移上,则显示 验证信息 “必填””。

2、如果是整数、浮点型等验证,则通过 正则表达式进行验证。

      3、如果是最大(max)、最小(min),则自定义逻辑。

4、如果是 fn 验证,则根据 对应controller中函数进行验证。

5、用户点击提交按钮,则 判断是否必填项,验证不通过,对应元素背景标红。

(5) 验证不通过,提示Factory---uiTipsFactory

app.factory('uiTipsFactory', function () {
  return {
   filterClass: function (ele, invalid) {
    if (invalid) {
     //如果验证不通过
     ele.removeClass('ng-valid').removeClass('ng-pristine').addClass('ng-invalid').addClass('ng-dirty');
    } else {
     ele.removeClass('ng-invalid').addClass('ng-valid');
    }
   },
   on: function (ele, msg) {
    var lastTip = ele.data('last-tip');
    if (lastTip && lastTip === msg) {
     return;
    }
    ele.data('last-tip', msg);
    this.filterClass(ele, true);
    var offset = ele.offset();
    if (!offset.top && !offset.left && ele.is('hidden')) {
     offset = ele.show().offset();
    }
    var id = ele.attr('ui-valid-id');
    if (!id) {
     id = Math.guid();
     ele.attr('ui-valid-id', id);
    }
    if (id.contains('.')) {
     id = id.replace(/\./g, '_');
    }
    var top = offset.top,
      left = offset.left;
    var getTips = function () {
     var _tip = $('#vtip_' + id);
     if (_tip.length) {
      _tip.html(msg).css({
       'display': 'none',
       'top': top + 'px',
       'left': left + ele.width() + 10 + 'px'
      });
     } else {
      var html = '' +
        '' + msg + '
';       $(html).css({        'display': 'none',        'position': 'absolute',        'top': top + 'px',        'left': left + ele.width() + 10 + 'px'       }).appendTo($('body'));      }     };     var bindTipsShow = function () {      getTips();      ele.unbind('mouseenter mouseleave').bind('mouseenter', function () {       var _tip = $('#vtip_' + id);       if (_tip.is(':hidden')) {        _tip.show();       }      }).bind('mouseleave', function () {       $('#vtip_' + id).hide();      });     };     bindTipsShow();    },    off: function (ele) {     ele.data('last-tip', '');     this.filterClass(ele);     var id = ele.attr('ui-valid-id');     if (!id) {      return;     }     if (id.contains('.')) {      id = id.replace(/\./g, '_');     }     $('#vtip_' + id).remove();     ele.unbind('mouseenter mouseleave');    }   }  });

1、验证不通过,增加背景色,元素css处理如下

 ele.removeClass('ng-valid').removeClass('ng-pristine').addClass('ng-invalid').addClass('ng-dirty');

验证通过,CSS处理如下

 ele.removeClass('ng-invalid').addClass('ng-valid');

2、背景提示语,则是在body上增加一个div层。

(6)其他相关代码

var app = angular.module('myApp', []);
 app.controller('testCtrl', ['$scope', 'uiValidFactory', function ($scope, uiValidFactory) {
    $scope.certCheck = function (val) {
     if (val > 32) {
      return "数字太大了";
     }
     return true;
    };
    $scope.submit = function () {
     if (!uiValidFactory.checkValidForm($scope.baseInfoForm.$name)) {
     }
    };
   }]
 );
 Math.guid = function () {
  var a = "", b = 1;
  for (; b <= 32; b++) {
   var c = Math.floor(Math.random() * 16).toString(16);
   a += c;
   if (b === 8 || b === 12 || b === 16 || b === 20) {
    a += '-';
   }
  }
  return a;
 };
 String.prototype.contains = String.prototype.contains || function (a) {
  return this.indexOf(a) != -1;
 };
 String.prototype.format = String.prototype.format || function () {
  var a = Array.prototype.slice.call(arguments);
  return this.replace(/\{(\d+)}/g, function (c, b) {
   return a[b];
  })
 };

整个代码如下:




 
 
 
 
 
  input.ng-invalid, select.ng-invalid {
   background-color: #ee82ee !important;
   border: 1px solid #CCC;
  }
  .qtip {
   position: absolute;
   max-width: 260px;
   display: none;
   min-width: 50px;
   font-size: 10.5px;
   line-height: 12px;
   direction: ltr;
  }
  .qtip-content {
   position: relative;
   padding: 5px 9px;
   overflow: hidden;
   text-align: left;
   word-wrap: break-word;
  }
  .qtip-rounded, .qtip-tipsy {
   -moz-border-radius: 5px;
   -webkit-border-radius: 5px;
   border-radius: 5px;
  }
  .qtipmodal-ie6fix {
   position: absolute !important;
  }
  .box-shadow-tips {
   background-color: #F63;
   border-color: #F5A88F;
   color: white;
   -moz-box-shadow: 2px 2px 2px #969696;
   -webkit-box-shaow: 2px 2px 2px #969696;
   box-shadow: 2px 2px 2px #969696;
  }
 



 
  
  
    
 var app = angular.module('myApp', []);  app.controller('testCtrl', ['$scope', 'uiValidFactory', function ($scope, uiValidFactory) {     $scope.certCheck = function (val) {      if (val > 32) {       return "数字太大了";      }      return true;     };     $scope.submit = function () {      if (!uiValidFactory.checkValidForm($scope.baseInfoForm.$name)) {      }     };    }]  );  Math.guid = function () {   var a = "", b = 1;   for (; b <= 32; b++) {    var c = Math.floor(Math.random() * 16).toString(16);    a += c;    if (b === 8 || b === 12 || b === 16 || b === 20) {     a += '-';    }   }   return a;  };  String.prototype.contains = String.prototype.contains || function (a) {   return this.indexOf(a) != -1;  };  String.prototype.format = String.prototype.format || function () {   var a = Array.prototype.slice.call(arguments);   return this.replace(/\{(\d+)}/g, function (c, b) {    return a[b];   })  };  app.factory('uiTipsFactory', function () {   return {    filterClass: function (ele, invalid) {     if (invalid) {      //如果验证不通过      ele.removeClass('ng-valid').removeClass('ng-pristine').addClass('ng-invalid').addClass('ng-dirty');     } else {      ele.removeClass('ng-invalid').addClass('ng-valid');     }    },    on: function (ele, msg) {     var lastTip = ele.data('last-tip');     if (lastTip && lastTip === msg) {      return;     }     ele.data('last-tip', msg);     this.filterClass(ele, true);     var offset = ele.offset();     if (!offset.top && !offset.left && ele.is('hidden')) {      offset = ele.show().offset();     }     var id = ele.attr('ui-valid-id');     if (!id) {      id = Math.guid();      ele.attr('ui-valid-id', id);     }     if (id.contains('.')) {      id = id.replace(/\./g, '_');     }     var top = offset.top,       left = offset.left;     var getTips = function () {      var _tip = $('#vtip_' + id);      if (_tip.length) {       _tip.html(msg).css({        'display': 'none',        'top': top + 'px',        'left': left + ele.width() + 10 + 'px'       });      } else {       var html = '' +         '' + msg + '';       $(html).css({        'display': 'none',        'position': 'absolute',        'top': top + 'px',        'left': left + ele.width() + 10 + 'px'       }).appendTo($('body'));      }     };     var bindTipsShow = function () {      getTips();      ele.unbind('mouseenter mouseleave').bind('mouseenter', function () {       var _tip = $('#vtip_' + id);       if (_tip.is(':hidden')) {        _tip.show();       }      }).bind('mouseleave', function () {       $('#vtip_' + id).hide();      });     };     bindTipsShow();    },    off: function (ele) {     ele.data('last-tip', '');     this.filterClass(ele);     var id = ele.attr('ui-valid-id');     if (!id) {      return;     }     if (id.contains('.')) {      id = id.replace(/\./g, '_');     }     $('#vtip_' + id).remove();     ele.unbind('mouseenter mouseleave');    }   }  });  app.factory('uiValidFactory', ['$parse', 'uiTipsFactory', function ($parse, tips) {   return {    check: function (val, rules, $scope, defaultTips, extendParam) {     if (!rules) {      return {       flag: true      };     }     var rulesArr = rules.split(' '),       isBlank = val === null || val === undefined || val === '' || ('' + val === '');     //如果不是必填项 且没有输入值 则清除提示框     if ($.inArray('r', rulesArr) === -1 && isBlank) {      return {       flag: true      }     }     var i = 0, len = rulesArr.length;     for (; i < len; i++) {      var rule = rulesArr[i];      if (!rule) {       continue;      }      var flag = true;      if ('r' === rule) {       //如果是必填项,有值 返回true       flag = !isBlank;      } else if (rule.contains(':')) {       //如果校验规则是 fn:ctrl.certCheck       flag = this.checkRule(val, rule.split(/:/), $scope, extendParam);      } else {       //校验 规则是 int 用正则匹配 数字 邮箱 长度       var pat = this.pats[rule];       if (pat instanceof RegExp) {        if (angular.isString(val)) {         flag = this.mat(val, pat);        }       } else if (angular.isFunction(pat)) {        flag = pat(val);       } else {        flag = false;       }      }      //这是干什么的呢      if (angular.isString(flag)) {       return {        flag: false,        msg: flag,        rule: rule       }      }      if (flag === false) {       var msg = this.getMsg(rule, defaultTips) || this.getMsg('tips.valid');       console.log(msg);       return {        flag: false,        msg: msg,        rule: rule       }      }     }     return {      flag: true     }    },    checkRule: function (val, ruleArr, $scope, extendParam) {     //ruleArr fn:certCheck     var rule = ruleArr[0];     if (rule === 'fn') {      fnName = ruleArr[1];//指定被调函数的名字 certCheck      var fn = $parse(fnName)($scope);      if (!fn) {       return true;      }      return fn.call($scope, val, extendParam);     } else {      return true;     }    },    checkValidForm: function (formName) {     //只检查必填项     //使用属性筛选器 获得里面所有的元素     var formContext = $('form[name="{0}"],[ng-form="{0}"],[data-ng-form="{0}"]'.format(formName)),       validList = formContext.find('[my-valid]');//validList 不是数组,是伪数组     if (!validList.length) {      return;     }     var that = this,       validFlags = [];     validList.each(function () {        var ele = $(this),          val = ele.val(),          ruleStr = ele.attr('my-valid');        if (!ruleStr) {         return true;        }        if (angular.isString(val)) {         val = val.trim();        }        var validRules = ruleStr.split(' ');        if ($.inArray('r', validRules) != -1 && !val) {         var modelValue = ele.attr('ng-model') || ele.attr('data-ng-model');         validFlags.push(modelValue);         tips.on(ele, that.getMsg('r'));        }       }     );     return validFlags;    },    mat: function (val, pat) {     if (!pat) {      return;     }     return pat.test(val);    }    ,    getMsg: function (rule, tips) {     tips = tips || '';     //可以在界面上直接写 tips     if (tips && tips.contains(':')) {      return tips;     }     var msg = this.msgs[rule];     if (msg) {      var params0 = tips.contains(':') ? tips.split(/:/)[0] : '';      var params1 = '';      if (rule.startsWith('min') || rule.startsWith('max')) {       var ruleArr = rule.split(/:/);       params1 = ruleArr[ruleArr.length - 1];      }      return msg.format(params0, params1);     } else {     }    }    ,    regPat: function (code, pat, msg) {     if (this.pat[code]) {      return;     }     this.pats[code] = pat;     this.msgs[code] = msg;    }    ,    msgs: {     'r': '必填',     'int': '{0}必须为整数'    }    ,    pats: {     'int': /^[\-\+]?([0-9]+)$/    }   }  }  ])  ;  app.directive('myValid', ['$parse', 'uiTipsFactory', 'uiValidFactory', function ($parse, tips, valid) {   var uiValidAttrIdName = 'ui-valid-id';   return {    restrict: 'A',    require: 'ngModel',    link: function (scope, el, attrs, ctrl) {     var validId = el.attr(uiValidAttrIdName);     if (!validId) {      validId = Math.guid();      el.attr(uiValidAttrIdName, validId);     }     var getRules = function () {      return attrs.myValid;     };     var lastOldRules;     var validFn = function (value, oldRules) {      var sp = '_';      var rules = getRules();      var r = valid.check(value, rules, scope, attrs.uiValidTips);      if (lastOldRules && !oldRules) {       oldRules = lastOldRules;      }      if (r.flag && oldRules) {       rules = rules ? rules + ' ' + oldRules : oldRules;      }      if (rules) {       var arrInner = rules.split(' ');       var i = 0;       for (; i < arrInner.length; i++) {        var oneRule = arrInner[i];        if (!oneRule.trim()) {         continue;        }        ctrl.$setValidity(attrs.ngModel + sp + oneRule, r.flag ? true : oneRule != r.rule);       }      }      if (!r.flag) {       tips.on(el, r.msg);      } else {       tips.off(el);      }      return r.flag;     };     var init = function () {      var rules = getRules();      if (!rules) {       return;      }      var parsers = ctrl.$parsers;      if (parsers && parsers.length > 0) {       parsers.clean();      }      parsers.unshift(function (value) {       return validFn(value) ? value : undefined;      });     };     scope.$watch(attrs.ngModel, function (newVal, oldVal) {      if (newVal === oldVal) {       return;      }      if (ctrl.$modelValue != undefined && (ctrl.$invalid || el.hasClass('ng-invalid'))) {       validFn(ctrl.$modelValue);      }     });     scope.$watch(getRules, function (newRules, oldRules) {      init();      lastOldRules = oldRules;      if (ctrl.$modelValue === undefined || ctrl.$modelValue === null) {       var needValid = false;       el.hasClass('ng-invalid');       var isValNaN = ctrl.$viewValue !== ctrl.$viewValue;       if (ctrl.$invalid || (ctrl.$viewValue !== undefined && !isValNaN)) {        needValid = true;       }       if (needValid) {        ctrl.$setViewValue(ctrl.$viewValue);       }      } else {       if (!ctrl.$dirty && attrs.dirtyCheck) {        console.log('----');       } else {        validFn(ctrl.$modelValue, oldRules);       }      }     });    }   }  }]);

关于“Angular如何使用输入框实现自定义验证功能”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。


文章题目:Angular如何使用输入框实现自定义验证功能
网页链接:http://cdxtjz.cn/article/igchec.html

联系我们

您好HELLO!
感谢您来到成都网站建设公司,若您有合作意向,请您为我们留言或使用以下方式联系我们, 我们将尽快给你回复,并为您提供真诚的设计服务,谢谢。
  • 电话:028- 86922220 18980695689
  • 商务合作邮箱:631063699@qq.com
  • 合作QQ: 532337155
  • 成都网站设计地址:成都市青羊区锣锅巷31号五金站写字楼6楼

小谭建站工作室

成都小谭网站建设公司拥有多年以上互联网从业经验的团队,始终保持务实的风格,以"帮助客户成功"为已任,专注于提供对客户有价值的服务。 我们已为众企业及上市公司提供专业的网站建设服务。我们不只是一家网站建设的网络公司;我们对营销、技术、管理都有自己独特见解,小谭建站采取“创意+综合+营销”一体化的方式为您提供更专业的服务!

小谭观点

相对传统的成都网站建设公司而言,小谭是互联网中的网站品牌策划,我们精于企业品牌与互联网相结合的整体战略服务。
我们始终认为,网站必须注入企业基因,真正使网站成为企业vi的一部分,让整个网站品牌策划体系变的深入而持久。