基于angularJS搭建的管理系统
前言
angularJS搭建的系统,是一年前用的技术栈,有些地方比较过时,这里只是介绍实现思路
前端架构
工程目录
项目浅析
项目依赖包配置package.json
1 { 2 "name": "crm-gulp", 3 "version": "1.0.0", 4 "description": "crm-gulp", 5 "main": "index.js", 6 "dependencies": { 7 "gulp": "^3.9.0", 8 "gulp-autoprefixer": "^3.1.0", 9 "gulp-concat-json": "^1.0.0", 10 "gulp-modify": "^0.1.1", 11 "gulp-replace": "^0.5.4" 12 }, 13 "devDependencies": { 14 "gulp-compass": "^2.1.0", 15 "gulp-concat": "^2.6.0", 16 "gulp-connect": "^2.3.1", 17 "gulp-merge-json": "^0.6.0", 18 "gulp-minify-css": "^1.2.1", 19 "gulp-ng-annotate": "^2.0.0", 20 "gulp-uglify": "^1.4.2" 21 }, 22 "author": "tom.h", 23 "license": "MIT" 24 }
Sass配置 config.rb
require 'compass/import-once/activate' # Require any additional compass plugins here. # Set this to the root of your project when deployed: http_path = "/" css_dir = "css" sass_dir = "sass" images_dir = "images" javascripts_dir = "js" # You can select your preferred output style here (can be overridden via the command line): # output_style = :expanded or :nested or :compact or :compressed output_style = :compact # To enable relative paths to assets via compass helper functions. Uncomment: relative_assets = true # To disable debugging comments that display the original location of your selectors. Uncomment: # line_comments = false # If you prefer the indented syntax, you might want to regenerate this # project again passing --syntax sass, or you can uncomment this: # preferred_syntax = :sass # and then run: # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
页面打包配置 gulp
var isDebug = false; var gulp = require('gulp'); var compass = require('gulp-compass'); var concat = require('gulp-concat'); var uglifyjs = require('gulp-uglify'); var minifycss = require('gulp-minify-css'); var replace = require('gulp-replace'); var autoprefixer = require('gulp-autoprefixer'); var connect = require('gulp-connect'); var ngAnnotate = require('gulp-ng-annotate'); var concatJson = require("gulp-concat-json"); var modify = require('gulp-modify'); var baseCss = [ './libs/bootstrap-3.3.6/css/bootstrap.css', './libs/font-awesome-3.0.2/css/font-awesome.css', './libs/bootstrap-datetimepicker/bootstrap-datetimepicker.css', './libs/angular-select-0.16.1/select.css', './libs/angular-ui-tree-2.13.0/angular-ui-tree.min.css', './libs/jquery-color/css/colpick.css', './libs/components/angular-datetimepicker/v1.1/dist/css/angular-datetimepicker.css', './libs/components/angular-grid/v1.2/dist/css/angular-grid.css', './libs/components/angular-confirm/dist/css/angular-confirm.css', './libs/components/angular-message/dist/css/angular-message.css', './libs/components/angular-pagination/v1.1/dist/css/angular-pagination.css', './libs/components/angular-checkbox/dist/css/angular-checkbox.css', './libs/components/angular-itempicker/dist/css/angular-itempicker.css', './libs/components/angular-autocomplete/dist/css/angular-autocomplete.css', './libs/components/angular-colorpick/dist/css/angular-colorpick.css', './css/layout/ui.css', './css/wrapper/wrapper.css' ]; var libsJs = [ './libs/jquery-cookie-1.4.1/jquery-cookie.js', './libs/requireJS-2.1.22/require.js', './libs/require-template/require-text.js', './libs/require-template/require-html.js', './libs/requireCss-0.1.8/css.js', './libs/angular-require-1.3.0/angular-require.js', './libs/angular-ui-tree-2.13.0/angular-ui-tree.js', './libs/angular-1.4.8/angular-messages.js', './libs/angular-file-upload/FileAPI.js', './libs/angular-file-upload/ng-file-upload-shim.js', './libs/angular-file-upload/ng-file-upload.js', './libs/underscore-1.8.3/underscore.js', './libs/bootstrap-datetimepicker/bootstrap-datetimepicker.js', './libs/bootstrap-datetimepicker/bootstrap-datetimepicker.zh-CN.js', './libs/angular-sanitize-1.4.6/angular-sanitize.js', './libs/angular-select-0.16.1/select.js', './libs/angular-bootstrap-1.3.2/ui-bootstrap-tpls-1.3.2.js', './libs/ui-router-0.2.18/angular-ui-router.js', './libs/jQuery-Autocomplete-1.2.24/jquery.autocomplete.js', './libs/jquery-color/js/colpick.js', './libs/components/angular-datetime/dist/js/angular-datetime.js', './libs/components/angular-permission/dist/js/angular-permission.js', './libs/components/angular-datetimepicker/v1.1/dist/js/angular-datetimepicker.js', './libs/components/angular-datetimepickerGroup/dist/js/angular-datetimepickerGroup.js', './libs/components/angular-grid/v1.2/dist/js/angular-grid.js', './libs/components/angular-confirm/dist/js/angular-confirm.js', './libs/components/angular-message/dist/js/angular-message.js', './libs/components/angular-pagination/v1.1/dist/js/angular-pagination.js', './libs/components/angular-checkbox/dist/js/angular-checkbox.js', './libs/components/angular-itempicker/dist/js/angular-itempicker.js', './libs/components/angular-autocomplete/dist/js/angular-autocomplete.js', './libs/components/angular-colorpick/dist/js/angular-colorpick.js', './libs/components/angular-breadcrumb/angular-breadcrumb.js', './libs/angular-shims-placeholder/angular-shims-placeholder.js', './libs/jquery-md5/jquery.md5.js' ]; gulp.task('compass', function() { return gulp.src('./sass/**/*.scss') .pipe(compass({ config_file: './config.rb', css: './css', sass: './sass', image: './images' })) .pipe(autoprefixer({ browsers: ['Firefox >= 1', 'Chrome >= 1', 'ie >= 7'], cascade: true })) .pipe(minifycss()) .pipe(gulp.dest('./css')); }); gulp.task('customDateTimepicker', function() { return gulp.src('./libs/bootstrap-datetimepicker/bootstrap-datetimepicker.js') .pipe(replace("'icon-arrow-left'", "'icon-caret-left'")) .pipe(replace("'icon-arrow-right'", "'icon-caret-right'")) .pipe(gulp.dest('./libs/bootstrap-datetimepicker/')); }); gulp.task('customSelect2', function() { return gulp.src('./libs/angular-select/select.js') .pipe(replace("glyphicon glyphicon-remove", "'icon-remove'")) .pipe(gulp.dest('./libs/angular-select/')); }); gulp.task('concatBaseCss', function() { return gulp.src(baseCss) .pipe(minifycss()) .pipe(concat('base.css')) .pipe(gulp.dest('./css/')); }); gulp.task('concatLibsJs', function() { var _pipe = gulp.src(libsJs) .pipe(concat('libs.js')); if (!isDebug) { _pipe.pipe(uglifyjs()); } _pipe.pipe(gulp.dest('./libs/')); return _pipe; }); gulp.task('concatBaseJS', ['combineRouter'], function() { var _pipe = gulp.src(['./js/base/app/app.js', './js/base/router/routerData.js', './js/base/*/**.js']) .pipe(concat('base.js')); if (!isDebug) { _pipe.pipe(uglifyjs()); } _pipe.pipe(gulp.dest('./js/base/')); return _pipe; }); gulp.task('replace',function(){ gulp.src('./template/index.jsp') .pipe(replace('{{version}}', (new Date()).getTime())) //替换地址 .pipe(gulp.dest('../WEB-INF/views/')) }); gulp.task('connect', function() { connect.server({ root: '../', port: 1212, host: 'localhost' }); }); /*合并各个模块的路由*/ gulp.task("combineRouter", function() { var tempNames = [], tempUrls = []; return gulp.src('./modules/**/*/router/router.json') .pipe(concatJson("routerData.js")) .pipe(modify({ fileModifier: function(file, contents) { var name = contents.match(/\"name\"\:(\W?)\"(\w+)/)[2]; var url = contents.match(/\"url\"\:(\W?)\"(\/\w+)/)[2]; var tip; if (tempNames.indexOf(name) >= 0) { tip = 'file:' + file.path + ' routeName ' + tempNames + ' is conflict', "color:red"; console.log(tip.warn); } else { tempNames.push(name); } if (tempUrls.indexOf(url) >= 0) { tip = 'file:' + file.path + ' routeUrl ' + url + ' is conflict', "color:red" console.warn(tip.warn); } else { tempUrls.push(url); } return contents; } })) .pipe(modify({ fileModifier: function(file, contents) { return "var ROUTER_DATA = " + contents; } })) .pipe(gulp.dest('./js/base/router/')); }); gulp.task('start', function() { gulp.watch('./sass/**/*.scss', ['compass']); gulp.watch(['./libs/**/*.css', './css/layout/*.css'], ['concatBaseCss']); gulp.watch(['./js/base/*/**.js', './modules/*/**/router/**.json'], ['concatBaseJS']); gulp.watch('./template/*.jsp', ['replace']); gulp.watch(libsJs, ['concatLibsJs']); gulp.start('concatBaseCss'); gulp.start('concatLibsJs'); gulp.start('concatBaseJS'); gulp.start('replace'); gulp.start('connect'); }); gulp.task('default', ['start']);
路由配置 rotuer.js
app.config(['$stateProvider', '$locationProvider', '$urlRouterProvider', '$requireProvider', function($stateProvider, $locationProvider, $urlRouterProvider, $requireProvider) { var routerModules = ROUTER_DATA; var version = BaseInfo.VERSION || "1.0"; var setRouterItem = function(rootRouter, item) { var path = item.path || rootRouter.path; var tempDepsArray = item.deps || []; var tempJsArray = item.js || []; var tempCssArray = item.css || []; var jsResultArray = []; var cssResultArray = []; var tplResultArray = []; for (var j = 0; j < tempJsArray.length; j++) { jsResultArray.push(path + tempJsArray[j] + "?v=" + version); } for (var i = 0; i < tempCssArray.length; i++) { cssResultArray.push("css!" + path + tempCssArray[i] + "?v=" + version); } for (var i = 0; i < tempDepsArray.length; i++) { if (/.*\.js$/.test(tempDepsArray[i])) { jsResultArray.push(tempDepsArray[i] + "?v=" + version); } if (/.*\.css/.test(tempDepsArray[i])) { cssResultArray.push("css!" + tempDepsArray[i] + "?v=" + version); } if (/.*\.tpl/.test(tempDepsArray[i])) { tplResultArray.push("html!" + tempDepsArray[i] + "?v=" + version); } } var stateOptions = { "url": item.url, "templateUrl": path + item.templateUrl + "?v=" + version, "customParams": item.customParams, "resolve": { deps: $requireProvider.require(tplResultArray), js: $requireProvider.requireJS(jsResultArray), css: $requireProvider.requireCSS(cssResultArray) } }; $stateProvider.state(item.name, stateOptions); }; var constructChildPage = function(rootRouter, childPages) { for (var i = 0; i < childPages.length; i++) { setRouterItem(rootRouter, childPages[i]); if (childPages[i].childPages) { constructChildPage(rootRouter, childPages[i].childPages); } } }; var setRouter = function() { for (var i = 0; i < routerModules.length; i++) { setRouterItem(routerModules[i], routerModules[i]); if (routerModules[i].childPages) { constructChildPage(routerModules[i], routerModules[i].childPages); } } }; setRouter(); $urlRouterProvider.otherwise("/activityCalendar/weekView"); }]);
文件依赖
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta charset="UTF-8"> <meta name="renderer" content="webkit"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0,user-scalable=no"> <title>客户关系管理平台</title> <link rel="stylesheet" href="/assets/css/base.css" /> <link rel="shortcut icon" href="/assets/favicon.ico"> <script src="/assets/libs/jquery-1.11.1/jquery.min.js"></script> <script src="/assets/libs/echarts/echarts.min.js"></script> <script src="/assets/libs/angular-1.4.8/angular.min.js"></script> <script src="/assets/libs/libs.js"></script> <script> var BaseInfo = { //左侧菜单 MENU_LIST: [{"name":"活动日历","code":"act_calendar","children":[{"name":"日历视图","code":"act_calendar_view","children":[]},{"name":"活动列表视图","code":"act_calendar_actlist_view","children":[]}]},{"name":"营销中心","code":"market_center","children":[{"name":"创建活动","code":"market_center_create_act","children":[]},{"name":"常用人群模板","code":"market_center_crowd_tpl","children":[]},{"name":"策略标签管理","code":"market_center_strategy_tag","children":[]},{"name":"营销控制项","code":"market_center_sale_manage","children":[]},{"name":"渠道审批列表","code":"market_center_channel_approval","children":[]}]},{"name":"会员中心","code":"vip_center","children":[{"name":"营销人群管理","code":"vip_center_sale_crowd_manage","children":[]},{"name":"会员透视分析","code":"vip_center_perspective_analyse","children":[]},{"name":"人群组管理","code":"vip_center_crowd_group_manage","children":[]}]},{"name":"内容库","code":"content_center","children":[{"name":"内容管理","code":"content_center_content_manage","children":[]},{"name":"短句管理","code":"content_center_phrase_manage","children":[]}]},{"name":"报表中心","code":"report_center","children":[{"name":"策略视图","code":"report_center_strategy_view","children":[]},{"name":"营销活动分析","code":"report_center_sale_act_analyse","children":[]},{"name":"活动发送管理","code":"report_center_act_send_manage","children":[]},{"name":"消息进程管理","code":"report_center_msg_process_manage","children":[]},{"name":"优惠券发送管理","code":"report_center_coupon_send_manage","children":[]}]},{"name":"系统管理","code":"sys_manage","children":[{"name":"用户管理","code":"sys_manage_user_manage","children":[]},{"name":"用户组管理","code":"sys_manage_user_group_manage","children":[]},{"name":"角色管理","code":"sys_manage_role_manage","children":[]},{"name":"渠道管理","code":"sys_manage_channel_manage","children":[]},{"name":"超级管理","code":"sys_manage_super_manage","children":[]}]},{"name":"配置项管理","code":"sys_config","children":[{"name":"业务场景","code":"sys_config_biz_scene","children":[]},{"name":"频次控制","code":"sys_config_frequency_capping","children":[]},{"name":"同步人群属性","code":"sys_config_user_property","children":[]},{"name":"默认透视图","code":"sys_config_perspective_manage","children":[]},{"name":"变量参数管理","code":"sys_config_parameters_manage","children":[]},{"name":"push落地页","code":"sys_config_push_land_page","children":[]},{"name":"字数限制","code":"sys_config_words_limit","children":[]},{"name":"高峰时间设置","code":"sys_config_top_hot_time","children":[]},{"name":"push铃声设置","code":"sys_config_push_sound","children":[]},{"name":"浮层运营位","code":"sys_config_floating_layer","children":[]},{"name":"标签管理","code":"sys_config_tag_manage","children":[]},{"name":"监控预警","code":"sys_config_monitor_warn","children":[]}]}], //菜单下数据及操作权限 RESOURCE_PERMISSON: ["approval1", "approval2"], //用户基本信息 USER_INFO: { "loginName": "OA登录名", "realName" : "真实名", "roleName": "角色名称", "groupName": "CRM组" }, //版本号 VERSION: "5.0" } </script> </head> <body ng-app="app" ng-controller="appCtrl"> <!--[if lte IE 8]> <div class="browser-tips" id="j-browser-tips"> <div class="mask"></div> <div class="tips-box"> <div class="title"><span class="icon-warning-sign icon"></span><span class="text">非常抱歉!由于您的IE浏览器版本过低,该系统不兼容此版本的浏览器!</span></div> <div class="desc">我们强烈建议您使用IE9(或以上)、Chrome、Firefox等主流浏览器。</div> <div class="btn-group"> <a class="btn btn-warning btn-continue" href="http://browsehappy.com/" target="_blank"><span class="icon-ok"></span>升级浏览器</a> <div class="btn btn-success btn-close" onclick="self.close()"><span class="icon-remove"></span>关闭</div> </div> </div> </div> <![endif]--> <div id="j-navbar" class="sidebar"> <a class="logo-wrap" href="/#/index"></a> <div ui-tree menu class="menu"> <ul ui-tree-nodes ng-model="menuList"> <li class="menu-parent item-parent" data-nodrag data-collapsed="true" ng-class="{'show':!collapsed}" ng-repeat="node in menuList" ui-tree-node ng-include="'nodes_renderer.html'"></li> </ul> </div> <script type="text/ng-template" id="nodes_renderer.html"> <div ng-if="::node.children.length > 0" class="item" data-nodrag ng-click="toggle();customToggleEvent(this,node);"> <span class="icon-angle-left icon-collapsed-status" ng-if="::node.children.length > 0" ng-class="{'icon-angle-left': collapsed,'icon-angle-down': !collapsed}"></span> <span class="item-icon" ng-class="{'icon-folder-close': collapsed,'icon-folder-open': !collapsed}"></span> <span class="item-name" ui-sref-active="active" ng-bind="::node.name" title="{{::node.name}}"></span> </div> <a ng-if="::node.children.length===0 && node.code != 'report_center_sale_act_analyse'" menu-id="{{::node.code}}" href="{{node.url}}" data-nodrag ui-sref-active="active" class="item" ng-click="toggle();customToggleEvent(this,node);"> <span class="icon-angle-left icon-collapsed-status" ng-if="::node.children.length > 0" ng-class="{'icon-angle-left': collapsed,'icon-angle-down': !collapsed}"></span> <span class="item-icon" ng-class="{true:'{{::node.icon}}', false: 'icon-folder-close'}[!!node.icon]"></span> <span class="item-name" ng-bind="::node.name" title="{{::node.name}}"></span> <span class="icon-active icon-caret-left"></span> </a> <a ng-if="::node.children.length===0 && node.code == 'report_center_sale_act_analyse'" target="_blank" menu-id="{{::node.code}}" href="{{node.url}}" data-nodrag ui-sref-active="active" class="item" ng-click="toggle();customToggleEvent(this,node);"> <span class="icon-angle-left icon-collapsed-status" ng-if="::node.children.length > 0" ng-class="{'icon-angle-left': collapsed,'icon-angle-down': !collapsed}"></span> <span class="item-icon" ng-class="{true:'{{::node.icon}}', false: 'icon-folder-close'}[!!node.icon]"></span> <span class="item-name" ng-bind="::node.name" title="{{::node.name}}"></span> <span class="icon-active icon-caret-left"></span> </a> <ul class=" item-child" ui-tree-nodes ng-model="::node.children" ng-class="{'collapsed': collapsed}"> <li ng-repeat="node in node.children" ui-tree-node ng-include="'nodes_renderer.html'"></li> </ul> </script> </div> <div id="j-main-view" class="main-view"> <div class="topbar"> <button slider-btn type="button" class="btn btn-success btn-collapse"><span class="icon-list"></span></button> <breadcrumbs></breadcrumbs> <span class="user-info"> <span class="username"> <span class="icon-user"></span> <span ng-bind="::userInfo.loginName"></span>(<span class="green" ng-bind="::userInfo.groupName"></span>) </span> <a ng-click="logout()" confirm="您确定退出登录吗?" confirm-ok="确定" confirm-cancel="取消" confirm-ok-color="success" confirm-title="退出登录" class="logout" href="javascript:void(0);"><span class="icon-signout"></span>退出</a> </span> </div> <div class="pages"> <div class="inner" ui-view> </div> </div> </div> </body> <script src="/assets/js/base/base.js"></script> </html>
模块解析