发表于: 2017-02-15 16:17:21

13 1721


今天完成的事情:

整理一下萝卜多后台的一些相关内容和期间遇到的一些问题:

后台部分:

基本结构

登录

登录验证和登录保持,作为公共接口,萝卜多接口文档里没有写这个接口,在这里提一提(登陆:/a/login ; 注销登陆:/a/logout),页面结构如图:

我将管理页和登录页分设了两个路由,在进入后台时,默认进入登录页,登录成功后保存该账号权限可用模块和权限信息,并跳转至管理页面;勾选保持登录,则写入用户信息cookie(依赖ngcookie),进入页面时若检测到cookie信息,则进入管理页面;进入页面后点击注销,则将本地所有登录信息删除,并跳转至登录页面,;

===========================华丽的分割线=================================

信息管理:

公司列表:

基本功能包括:

通过公司、产品名称搜索出相应的公司, 后台已经提供相对应的接口,将string作为请求参数发送即可;

公司行业、融资规模、认证/取消、冻结/解冻:下拉框这部分在后面还用到,所以用directive封装了一下,留几个参数:标签名、数据对象、输出值,使用时只需提供名字和数据即可输出对应值,这里数据结构为{type:xx,name:yy},默认首选项为{type:null,name:不限},在指令初始化的时候进行赋值,防止空白


地区三级联动:省-市-区县,这里封装为directive,考虑到可能会只用到两个参数,所以留了可选的三个参数输出:city,county和pro(省);

//初始化
scope.PROVINCE = LINKAGE.PROVINCE; //传入基础数据
scope.pro = scope.city = scope.county = null;
//根据选择过滤
scope.$watch('pro', function (n, o, scope) { //监听选中省份数据,变化后过滤出市
   scope.CITY = $filter('FilterCity')(scope.pro);
   n ? scope.pro = +n : ''; //当前选中值无效,则选项不变化(不限)
});

scope.$watch('city', function (n, o, scope) {//监听选中市数据,变化后过滤出区县数据    

scope.COUNTY = $filter('FilterCounty')(scope.city);

   n ? scope.city = +n : ''      //当前选中值无效,则不变化

});

scope.$watch('county', function (n, o, scope) {

   n ? scope.county = +n : '' //当前选中值无效,则不变化

})

其中过滤器如,返回匹配的数组:

.filter('FilterCity', function (LINKAGE) {
   return function (proID) {

       var city = [{"CityID": null, "CityName": "不限"}];//初始数据,上一级未选择时选项

       if (proID){angular.forEach(LINKAGE.CITY, function (data) {//上一级选择有效值后执行匹配
           if (data.ProID == proID) {
               city.push(data)
           }
       })};
       return city;
   }
})


搜索、清空:清空这里写了个简单的服务,将当前参数设置空,应为所有下拉菜单默认值设置为null,所以,不能直接赋值{},否则下拉菜单将出现空白首选项:

.factory('clean', clean);
function clean() {
   return function (obj) {
       angular.forEach(obj,function (data,key) {
           obj[key]=null
       })
       return obj
   }
}


列表:这里我做个了处理,后面的列表都为同一样式、模态框(bootstrap),所有将表格都用directive模板,传入对应数据和对应控制方法;由于前期没考虑到权限控制,所以将所有操作都写进了模板;


职位:点击将当前对象的id作为参数跳转至职位列表页,在职位列表页用这个参数请求出详细数据;


认证、冻结用同一个接口,传入不同字段,这里需要注意的是在状态栏中显示的状态和操作栏中的状态相反,所以一个比较直观的方法是写两个相反的过滤器,或者用一个过滤器,将数据做相反的赋值,如:获取数据Freezed:0,因为只有两种值0和1,这里直接用三目:

Freezed=Freezed?0:1;//Freezed=1

结果Freezed=1,反之亦然;


 删除:这个只需传入对象id,method为DELETE即可;


编辑:这里东西比较多,表单验证、图片上传、公司标签、行业类型(行业可多选)

表单验证:除了公司标签、公司产品、招聘公司相关信息,其他都为必填字段,任务做过,所以问题不大;图片上传 :做task9的内容,不过任务9的时候用原生的FormData对象,图片上传前预览和上传后url,没有做进度提示;这里尝试使用了插件

angular-file-upload,里面集成了很多方法可以直接用,由于有重复使用的地方,所以二次封装将样式添加后写入directive(这种做法low-_-!),点击日报链接

.directive('upLoader', function (FileUploader) {
   return {
       restrict: 'E',
       templateUrl: 'Pages/template/upload.html',
       scope: {
           logoUrl: '=ngModel',//图片上传后地址
        tabName: '@'//标签名
       },
       replace: 'true',
       link: function (scope, ele, attrs) {
           scope.class = attrs.class;
           scope.labelClass = attrs.labelClass;
           scope.uploader = new FileUploader({//实例化
               url: '/lbd-admin/a/u/img/test',//图片上传地址
               queueLimit: 1
           });
           scope.clearItem = function () {//清空队列
               scope.uploader.clearQueue()
           };
           scope.getUrl = function (files) {// 图片预览(选择后、上传前)
               scope.fileList = files;
               scope.imgURL = window.URL.createObjectURL(scope.fileList[0]);//考虑性能用后清除
           };
           scope.uploader.onSuccessItem = function (item, response) {//上传成功返回地址
               scope.logoUrl = response.data.url
           }
       }
   }
})

做上传前的预览,这里感觉踩了很多坑,单个页面多次使用插件,不封闭作用域后面覆盖前面或者同步、实例化未完成时加载报错等等。。

html:

<div class="col-xs-10 logo-min file-upload" ng-if="uploader">//在实例化前不渲染
   <button class="file-upload-class btn btn-primary">
       <label for="{{id}}">点击上传图片</label>
       <input id="{{id}}" type="file" class="form-control" nv-file-select="" uploader="uploader" multiple
              ng-click="clearItem()" onchange="angular.element(this).scope().$parent.getUrl(this.files)"
              required>
   </button>
</div>

这里id可忽略,之前做尝试的时候留下的nv-file-select="" uploader="uploader"为关键属性,onchange这里是为了做上传前的预览,获取选中的图片,要注意作用域问题,要注意作用域问题,要注意作用域问题!之前这个getUrl我始终找不到,一直以为在当前作用域,甚至直接用原生方法调用函数(未限制作用域),结果每个页面只有最后一个插件预览有效,之后查看了作用域关系,在父作用域中找到了这个方法,设置后问题解决。

图片展示:

<div class="col-xs-10 col-xs-offset-2 mar-top-15 logo-min" ng-show="uploader.queue.length||logoUrl">//上传队列中有文件或者从服务端获取到Url时
   <a class="thumbnail rest-mr">
       <img ng-src="{{imgURL||logoUrl}}">//本地图片地址或服务器图片地址
   </a>
</div>

图片信息与操作:

<tr class="text-center" ng-repeat="item in uploader.queue"> //文件队列
   <td>{{item.file.name}}</td>//文件名
   <td>{{ item.file.size/1024/1024|number:2 }} MB</td> //文件大小
   <td>
       <div class="progress rest-mr"> //上传进度显示
           <div class="progress-bar progress-bar-success" role="progressbar"
                aria-valuenow="{{item.progress + '%'}}"
                aria-valuemin="0" aria-valuemax="100" ng-style="{ 'width': item.progress + '%' }">
               <span class="sr-only"></span>
           </div>
       </div>
   </td>
   <td><span class="glyphicon"
             ng-class="{'glyphicon-ok':item.isSuccess,'glyphicon-remove':item.isError}"></span></td>//上传状态显示
   <td class="btn-group-xs" ng-if="item">
       <button class="btn btn-success" ng-click="item.upload()" ng-disabled="item.isReady || item.isUploading || item.isSuccess">
           <span class="glyphicon glyphicon-upload" style="vertical-align: text-top"></span>
           上传
     </button>//执行上传 队列空 正在传 完毕时禁用按钮
       <button class="btn btn-danger" ng-click="item.remove();"><span class="glyphicon glyphicon-trash" style="vertical-align: text-top"></span>删除
     </button>// 清空上传队列
   </td>
</tr>


公司行业:由于公司行业可以多选,但又是下拉的方式,所自己写了个指令,这里用了下菜单的指令,添加了多选,因为只有公司行业用到,所以判断条件简单设置了一个,复用地方多的话可以设置一个属性,通过属性值判断执行简单下拉框or多选标签;

代码:

数据输出格式:

scope.removeItem = remove;//删除标签
function remove(idx) {
   scope.selectedList.splice(idx, 1)
}

if (scope.selectName == '*认证状态' || scope.selectName === '状态') {//设置编辑页认证状态默认值
   scope.selectedValue = 0
}
if (scope.selectName == '*公司行业') {
   scope.show = true;  //显示标签部分
   scope.selectedList = [];  //数组内存选中的值【1,2,3...】
   var i = 0;
   //标签添加
   scope.$watch('selectedValue', function (n) { //监听下拉菜单选择情况,有选择行为则执行
       if (n !== null) {
           var has = true;//判断重复
           angular.forEach(scope.selectedList, function (data) {//不重复添加相同个元素
               if (data.industry === scope.selectedValue) {
                   has = false
               }
           });
           if (has)
               scope.selectedList.push({'industry': n})//添加新元素
       }
   });

   scope.text = '展开';//可以和msgCollapsed绑一起 做下拉收起功能

   scope.change = function () {

       i % 2 ? scope.text = '收起' : scope.text = '展开';
       i++
   };
}

html

<form class="form-group  flex-wrap " name="{{formName}}">
   <label>{{selectName}}</label>
   <div class="{{optionClass}}" style="position: relative">
       <select class="form-control" name="name" ng-model="selectedValue"
               ng-options="item.type as item.name for item in options"

               ng-class="{'error':company.name.$invalid&&company.name.$touched}" ng-required='{{required}}'></select>

================================多选标签===============================================================

       <div ng-if="show" class="industry col-xs-11 col-xs-offset-1 btn-group-xs">
           <button class="btn btn-danger" ng-click="msgCollapsed = !msgCollapsed;change()">{{text}}</button>//
           <div uib-collapse="msgCollapsed">
               <span class="label label-primary tag" ng-repeat="item in selectedList|industry track by $index">{{item}}<span class="close-icon" ng-click="removeItem($index)">x</span></span>
           </div>
       </div>
   </div>
</form>


公司标签:这里和上面的公司行业几乎用的同一个东西,只是这里改为手动输入,如图:

指令部分:

数据格式:

return {
   restrict: 'E',
   templateUrl: 'Pages/template/tag.html',
   replace: 'true',
   scope: {
       tagList: '='
   },
   link: function (scope) {
       scope.tagList = [];
       scope.add = add;
       scope.removeItem = remove;
       function add() {
           var tag = {'tag': scope.tag};//点击添加构建当前标签对象
           var _has = false;
           angular.forEach(scope.tagList, function (data) {//检测是否已经存在标签 
               if (data.tag === scope.tag) {
                   _has = true
               }
           });
           _has || !scope.tag ? false: scope.tagList.push(tag);//不重复则添加
       }

       function remove(n) {
           scope.tagList.splice(n, 1)//删除元素
       }
   }
}


===========================华丽的分割线=================================


职位列表

和公司列表没什么区别,这里的操作只有编辑,下架 和删除。不过这里有个职位的二级联动,用的和前面地区三级联动一样的做法即可;

时间选择器用了ui-bootstrpa的插件:

这里只需要注意将输出的时间转换为时间戳即可,默认输出为时间对象,用valueOf可以直接转换为时间戳;

同时要加个简单的限制,结束时间不能在开始时间之前等;

scope.dateOptions2 = {//结束时间配置
   minDate: new Date(scope.startAt)
}
<input type="text" class="form-control" uib-datepicker-popup ng-model="start" is-open="openedStart"
      datepicker-options="dateOptions" type="datetime-local" close-text="关闭" clear-text="清除"
      current-text="今天"/>
<span class="input-group-btn">

<button type="button" class="btn btn-default" ng-click="openSt()">

<class="glyphicon glyphicon-calendar"></i>

</button>


职业编辑/添加:职业二级联动,公司标签和表单验证,进入职位编辑时公司名称添加disable;职业新增只能从公司列表的职业选项进入时才有新增按钮,这里可以给按钮设置ng-if,判断条件为公司ID;


===========================华丽的分割线=================================


article管理

编辑控制前台页面banner图和行业大图等的展示,这里没什么新的内容,编辑下线删除操作,上线状态时,图片会在前台进行展示;表单验证这里注意一条,当类型为行业大图的时候,会有多一个选项出现(行业),而且为必填;


==============================================================


萝卜多总结:前台部分链接

萝卜多总结:后台部分2

=============================================================

明天计划的事情:

完成后台总结


遇到的问题:

。。
收获:

如上


返回列表 返回列表
评论

    分享到