前言
angularJS搭建的系统,是一年前用的技术栈,有些地方比较过时,这里只是介绍实现思路
前端架构
工程目录
项目浅析
项目依赖包配置package.json
{
"name": "crm-gulp",
"version": "1.0.0",
"description": "crm-gulp",
"main": "index.js",
"dependencies": {
"gulp": "^3.9.0",
"gulp-autoprefixer": "^3.1.0",
"gulp-concat-json": "^1.0.0",
"gulp-modify": "^0.1.1",
"gulp-replace": "^0.5.4"
},
"devDependencies": {
"gulp-compass": "^2.1.0",
"gulp-concat": "^2.6.0",
"gulp-connect": "^2.3.1",
"gulp-merge-json": "^0.6.0",
"gulp-minify-css": "^1.2.1",
"gulp-ng-annotate": "^2.0.0",
"gulp-uglify": "^1.4.2"
},
"author": "tom.h",
"license": "MIT"
}
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>