fans

这是一个app(android/iOS)项目,但页面视图全部都用的是html5页,没有使用app的原生页面。 前端h5是基于mui + vue2 + vue-router2 + es6 + webpack2 + vuex + signalR 的前端webApp单页项目框架,项目可以直接在PC上运行html5页面。 app打包技术是用HBuilder IDE工具一键打包成APP。

Stars
416
Committers
1

app(android/iOS)html5appPChtml5web apiJSON WEB TOKEN (JSON Web TokenJWT))android Apk,

h5mui + vue2 + vue-router2 + es6 + webpack2 + vuex + signalRwebApp

appHBuilder IDEAPPStorageapphtml5,APPdcloud

**** signalRsignalRWebJSWebSocketsHtml5signalRWebSocketssignalR

  1. UImui

  2. appHBuilder IDE

  3. App

  4. vue-router2

  5. .vue

  6. vuexwebApp

  7. signalR

  8. webpack2

  9. apph5header

**** webpack2webpack.config.jsloaderwebapck2 APIVUEvue-clivue-cliwebpack1webpack2JSTM10

  • HBuilder IDEHBuilderdcloud eclipseappphpjsprubypythonnodejsweblesscoffee

  • node.jswebnode.js6.9.2 npm3.10.9

  • appmanifest.jsonappappid(app)sdkdcloud

npm

package.json
{
	"name": "Fans",
	"version": "1.0.0",
	"description": "",
	"main": "js/entrance.js",
	"keywords": "",
	"homepage": "",
	"bugs": {
		"url": "https://github.com/yujinjin/fans/issues",
		"email": "[email protected]"
	},
	"author": {
		"name": "jinyu",
		"email": "[email protected]",
		"url": "https://github.com/yujinjin"
	},
	"license": "MIT",
	"repository": {
		"type": "git",
		"url": "https://github.com/yujinjin/fans.git"
	},
	"scripts": {
		"R_DEV": "set NODE_RUN=1&&webpack-dev-server --progress --watch --inline --host=0.0.0.0  --port 8083",
		"B_DEV":"set NODE_ENV=dev&set NODE_RUN=0&webpack --progress --hide-modules",
		"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs"
	},
	"dependencies": {
		"vue": "^2.1.8",
		"vue-resource": "^1.0.3",
		"vue-router": "^2.0.1",
		"vue-html-loader": "1.2.3",
    	"vue-loader": "10.0.0",
    	"vue-style-loader": "^1.0.0",
    	"vue-template-compiler": "^2.1.0"
	},
	"devDependencies": {
		"vuex": "^2.0.0",
		"autoprefixer": "^6.4.0",
	    "babel-core": "^6.0.0",
	    "babel-eslint": "^7.0.0",
	    "babel-loader": "^6.0.0",
	    "babel-plugin-transform-runtime": "^6.0.0",
	    "babel-preset-es2015": "^6.0.0",
	    "babel-preset-stage-2": "^6.0.0",
	    "babel-register": "^6.0.0",
	    "babel-polyfill": "^6.22.0",
		"cross-env": "^1.0.6",
		"css-loader": "^0.25.0",
		"less": "^2.7.1",
		"less-loader": "^2.2.3",
		"file-loader": "^0.9.0",
		"html-loader": "^0.4.4",
		"html-webpack-plugin": "^2.24.1",
		"jshint": "^2.9.4",
		"jshint-loader": "^0.8.3",
		"style-loader": "^0.13.1",
		"url-loader": "^0.5.7",
		"extract-text-webpack-plugin": "^2.0.0-beta.4",
		"webpack": "^2.1.0-beta.25",
		"webpack-dev-server": "^2.1.0-beta.10",
		"webpack-require-http": "^0.4.0"
	},
	
	"engines": {
		"node": ">=5.0.0",
		"npm": ">=3.3.6"
	}
}

vue                            //
vue-resource                   //vue http ajax
vue-router                     //vue 
vue-html-loader                //vue html
vue-loader                     //vue
vue-style-loader               //vue
vue-template-compiler          //vue
vuex                           //
autoprefixer                   //css  
babel-core                     //ES6  
babel-eslint                   //ES6
babel-loader                   //ES6  webpack
babel-plugin-transform-runtime //polyfill
babel-preset-es2015            //ES6  ES5
babel-preset-stage-2           //ES6  ES7
babel-register                 //require,,require.js.jsx.es.es6,Babel
babel-polyfill                 //BabelJavaScriptsyntaxAPIbabel-polyfillPromise
cross-env                      //NODE_ENV
css-loader                     //css  
less                           //css  less
less-loader                    //css  lesswebpack
file-loader                    //webpack  
html-loader                    //webpackhtmlhtml
html-webpack-plugin            //html  
jshint                         //Js
jshint-loader                  //webpackjshintJs
style-loader                   //webpackstylecss  style
url-loader                     //webpackurl
extract-text-webpack-plugin    // ()
webpack                        //
webpack-dev-server             //
webpack-require-http           //webapckrequrirehttp

|-- build                               // webapck
|-- logo                                // app
|-- src                                 // 
|   |-- components                      // 
|       |-- member-qrcode.vue           // 
|       |-- ...                         // 
|   |-- css                             // css
|       |-- app.css                     // app 
|       |-- icons-extra.css             // icons 
|       |-- mui.css                     // muicss
|       |-- ...                         // css
|   |-- fonts                           // fonts
|       |-- ...                         // fonts
|   |-- imgs                            // 
|       |-- test                        // 
|           |-- ...                     // 
|       |-- ...                         // 
|   |-- js                              // js
|       |-- components                  // js
|           |-- app-routers.js          // 
|           |-- signalR.js              // signalR
|           |-- ...                     // JS
|       |-- config                      // 
|           |-- DEV.js                  // DEV
|           |-- GQC.js                  // GQC
|           |-- PRD.js                  // PRD
|           |-- ...                     // 
|       |-- lib                         // JS lib
|           |-- mui.js                  // mui
|           |-- ...                     // JS
|       |-- services                    // app
|           |-- global-service.js       // APP 
|       |-- store                       // vuexwebApp
|           |-- index.js                // app
|           |-- app-data.js             // app
|           |-- app-event.js            // app
|           |-- router-status.js        // app
|       |-- utils                       // app
|           |-- directives.js           // vue 
|           |-- log.js                  // app log
|           |-- update.js               // app
|           |-- utils.js                // app
|           |-- ....                    // JS
|   		|-- app.js                      // app
|   		|-- entrance.js                 // app
|   		|-- routers.js                  // vue
|   |-- json                      			// json
|   |-- less                      			// less
|   		|-- app.less                    // appless
|   		|-- ...                         // less
|   |-- views                      			// 
|   		|-- error                       // 
|   				|-- 404.vue                 // 404
|   		|-- users                      	// 
|   				|-- login.vue               // 
|   				|-- user-center.vue         // 
|   				|-- welcome.vue         		// 
|   				|-- ...         		// 
|   		|-- ...                     // 
|   		|-- app.vue                     // app
|   		|-- barcode.vue                 // barcode
|   		|-- home.vue                    // app
|   |-- index.html                      // apphtml
|-- unpackage                           // app
|-- .babelrc                            // ES6
|-- .editorconfig                       // 
|-- .gitignore                          // git
|-- index.html                          // webapp
|-- manifest.json                       // app
|-- package.json                        //  npm init 
|-- webpack.config.js                   // webpack

1. app-
2.
3.
3. 1.
3. 2.
3. 3.
4. ()
4. 1.
5.
5. 1.
5. 2.
5. 3.
5. 4.
5. 5.

git clone

git clone https://github.com/yujinjin/fans.git

npm(Node.js)

npm install

DEV(http://localhost:8083)

npm run R_DEV (window)
npm run MR_DEV (MAC)

DEV

npm run B_DEV (window)
npm run MB_DEV (MAC)

iOSclick300mstapclick

webpack.config.js

const path = require('path'),
	webpack = require('webpack'),
	NODE_ENV = process.env.NODE_ENV || "DEV", //
	NODE_RUN = process.env.NODE_RUN || "0", //
	ROOT_PATH = path.resolve(__dirname) + "\\",
	OUT_PATH = path.resolve(ROOT_PATH, 'build') + "\\",
	SERVER_PATH = process.env.SERVER || "./build/",// 
	ExtractTextPlugin = require("extract-text-webpack-plugin"),
	HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
	entry: {
		page: "./src/js/entrance.js", //[ROOT_PATH + "\\js\\entrance.js"],
		// 
	    commons: ['vue', 'vue-router']
	},
	output: {
		path: NODE_RUN === "0" ? './build' : "/",//"./build",//"./build",//path.resolve(__dirname, './build'), //path.resolve(__dirname, './build'), //
		//publicPath/util/vue/build , publicPath: "/util/vue/build/",css
		publicPath: NODE_RUN === "0" ? "./build/": "/build/",//"build/",//SERVER_PATH, //process.env.CUSTOM ? "/git/WebApp/n-build/" : "/n-build/",
		filename: NODE_RUN === "0" ? "build.[hash].js" : "build.js",
		/*
	    	import(), chunk, chunkFilenamechunk.
	    	[id]: chunkid.
	    	[chunkhash]: chunkhash, . .
	    */
	    //chunkFilename: '[id].js?[chunkhash]',
	},
	externals:[require('webpack-require-http')],
	module: {
		rules: [{
          	test: /\.html$/,
          	use: [{
          		loader: 'html-loader',
          		options: {
            		//root: resolve(__dirname, 'src'),
            		attrs: ['img:src', 'link:href']
          		}
           }]
		}, {
            test: /\.js(x)*$/,
            exclude: /^node_modules$/,
            //loader: 'babel-loader'
            use: ['babel-loader']
       	}, {
			test: /\.vue$/,
			use: ['vue-loader']
			//loader: 'vue-loader'
//			options: {
//				loaders: {
//		            css: ExtractTextPlugin.extract({
//		              	loader: 'css-loader',
//		              	fallbackLoader: 'vue-style-loader' // <- this is a dep of vue-loader, so no need to explicitly install if using npm3
//		            })
//		        }
//			}
		}, 
//		{
//			test: /\.html$/,
//			loader: 'html-loader',
//			options: {
//            	/*
//	              	html-loaderattrs, webpackloader.
//	              	<img>src, webpack<img>, src.
//	              	loader, module.rules.
//	              	html-loaderattrs, img:src, <img>.
//	              	<link>href, index.htmlfavicon.png.
//            	*/
//            	attrs: ['img:src', 'link:href']
//          }
//		},
		{
			test: /\.css$/,
			exclude: /^node_modules$/,
//			use:[{
//          	loader: 'style-loader'
//          },{
//          	loader: 'css-loader'
//          }]
			//use: ['style-loader', 'css-loader']
//			loader: ['style-loader', 'css-loader']
//			loader: `vue-style-loader!css-loader!autoprefixer-loader?{ browsers: ['last 100 versions'] }!`
			loader: ExtractTextPlugin.extract({
                fallbackLoader: "style-loader",
                loader: "css-loader",
                publicPath: "./"
            })
		}, {
            test: /\.less/,
            exclude: /^node_modules$/,
//          use:[{
//          	loader: 'style-loader'
//          },{
//          	loader: 'css-loader'
//          },{
//          	loader: 'less-loader'
//          }]
//                loader: ['style-loader', 'css-loader', 'less-loader']
//                loader: `vue-style-loader!css-loader!less-loader!autoprefixer-loader?{ browsers: ['last 100 versions'] }!less-loader`
            loader: ExtractTextPlugin.extract({
	          	fallbackLoader: 'style-loader',
	          	loader: "css-loader!less-loader",
                publicPath: "./"
	        })
        },{
	        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
	        use: [{
	        	loader: "url-loader",
	        	query: {
		          	limit: 10000,
		          	name: 'imgs/[name].[hash:7].[ext]'
	        	}
	        }]
	        //loader: 'url-loader',
//	        use: ['url-loader'],
//	        query: {
//	          	limit: 5000,
//	          	name: 'imgs/[name].[hash:7].[ext]'
//	        }
      	} ,{
	        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
	        use: [{
	        	loader: "url-loader",
	        	query: {
		          	limit: 5000,
		          	name: 'fonts/[name].[hash:7].[ext]'
	        	}
	        }]
	        //loader: 'url-loader',
//	        query: {
//	          	limit: 5000,
//	          	name: 'fonts/[name].[hash:7].[ext]'
//      	}
      	}
//      ,{
//			//!cssnext-loader 
//			test: /\.(png|jpg)$/,
//			exclude: /^node_modules$/,
//			//limitbase64
//			loader: 'url-loader?limit=8000&name=[name].[ext]'
//		}, {
//          test: /\.(eot|woff|svg|ttf|woff2|gif|appcache)(\?|$)/,
//          exclude: /^node_modules$/,
//          loader: 'file-loader?name=[name].[ext]'
//      }
		]
	},
	plugins:[
//		new ExtractTextPlugin({
//			fileName: NODE_RUN === "0" ? "style.[hash].css" : "style.css",
//			disable: false,
//			allChunks: true
//		}), //
		new ExtractTextPlugin(NODE_RUN === "0" ? "style.[hash].css" : "style.css"),
		new HtmlWebpackPlugin({
			filename: "../index.html", //html path
			template: './src/index.html', //html
			favicon: "./src/imgs/goldfish.ico",
			inject: true, //headbody
			minify: { //HTML
				removeComments: true, //HTML
				collapseWhitespace: false, //
				//removeAttributeQuotes: true
			}
		}),
		/*
		      CommonsChunkPlugin
		      vendor.jsindex.jsspa-history, , spa-history,
      		CommonsChunkPluginvendor.js
      	*/
		new webpack.optimize.CommonsChunkPlugin({
			name: "commons",
			filename: NODE_RUN === "0" ? "common.[hash].js" : "common.js",
			minChunks: function (module, count) {
		        // any required modules inside node_modules are extracted to vendor
		        return (module.resource && /\.js$/.test(module.resource) && module.resource.indexOf(path.join(__dirname, '../node_modules')) === 0);
		    }
		}),
//		new webpack.optimize.CommonsChunkPlugin({
//    		name: 'manifest',
//    		chunks: ['commons']
//  	}),
		//
		new webpack.ProvidePlugin({
			//JS
			config: ROOT_PATH + "\\src\\js\\config\\" + NODE_ENV
		})
	],
	resolve: {
        extensions: ['.js', '.vue', '.jsx', '.less', '.scss', '.css'], //
    	//fallback: [path.join(__dirname, '../node_modules')], webpack2 
//  	alias: {
//          hubs : 'http://www.dev.zmscrm.cn/signalr/hubs',// require('AppStore') 
//      }
	},
	devServer: {
		historyApiFallback: true,//true, , index.html
		noInfo: true
	},
	performance: {
    	hints: false
  	},
//	vue: {
//		loaders: {
//			css:"vue-style-loader!css-loader?sourceMap",
//			less:"vue-style-loader!css-loader?sourceMap!less-loader?sourceMap"
//		},
//      postcss: [
//          require('autoprefixer')({
//              browsers: ['last 100 versions']
//          })
//      ]
//	},
	devtool: '#eval-source-map'
}
var fileSystem = require('fs');
//
if(NODE_RUN === "0") {
	module.exports.devtool = false;
	module.exports.plugins = (module.exports.plugins || []).concat([
//		new webpack.LoaderOptionsPlugin({
//		      	minimize: true
//		}), //url('data:image/svg+xml;charset=utf-8,<svg....
		new webpack.DefinePlugin({
			'process.env': {
				NODE_ENV: '"production"'
			}
		}),
		new webpack.optimize.UglifyJsPlugin({
			compress: {
		        warnings: false
		    },
		    output: {
		        comments: false
		    },
			sourceMap: false
		})
	]);
	// output 
	var dirArray = [];
	//
	var clearOutPutDir = function(path) {
		if(fileSystem.existsSync(path)) {
			var dirList = fileSystem.readdirSync(path);
			dirList.forEach(function(fileName) {
				if(fileSystem.statSync(path + fileName).isDirectory()) {
					console.info(":" + path + fileName);
					// 
					dirArray.push(path + fileName);
					clearOutPutDir(path + fileName + "\\");
				} else {
					console.info(":" + path + fileName);
					fileSystem.unlinkSync(path + fileName);
				}
			});
		};
	}
	clearOutPutDir(OUT_PATH);
	for(var i = dirArray.length - 1, j = 0; i >= j; i--) {
		console.info(dirArray[i])
		fileSystem.rmdirSync(dirArray[i]);
	}
} else {
	console.info("run........................................");
	//index.htmlhrefsrc
	fileSystem.readFile("index.html", 'utf-8', function(err, data) {
	if(err) {
		console.log("error");
	} else {
		//index.htmlhash
		var devhtml = data.replace(/((?:href|src)="[^"]+\.)(\w{20}\.)(js|css)/g, '$1$3');
		fileSystem.writeFileSync('index.html', devhtml);
	}
});
}

webApp

1. entrance.js

webpackWebappapp SUIVUEwebappJS

import babelPolyfill from 'babel-polyfill'
import mui from "./lib/mui"
import app from './app'
import globalService from './services/global-service'
import log from './utils/log'
import utils from './utils/utils'
import directives from "./utils/directives"
import Vue from 'vue'
import Vuex from 'vuex'
import VueRouter from 'vue-router'
import routers from "./routers"
import vueApp from "../views/app"
import store from "./store/"
import jQuery from "./lib/jquery-1.12.4"

Object.assign(app.Config, config);
window.app = Object.assign({}, app, {log, utils, mui, globalService});
//signalRjqueryjQuerysignalRTM...
window.jQuery = window.$ = jQuery;
const initVue = function(){
	Vue.use(Vuex);
	Vue.use(VueRouter);
	Object.keys(directives).forEach((key) => {
	    Vue.directive(key, directives[key]);
	});
	const [router, VueApp] = [routers.createRouter(VueRouter, store), Vue.extend(vueApp)];
	window.app.vueApp = new VueApp({ router, name: "app", store }).$mount('#app');
}
mui.init({
	swipeBack:false, //false
	keyEventBind: {
		backbutton: true  //backtrue
	},
	statusBarBackground: "#1981D8" //,iOS
});
if(mui.os.plus) {
	app.Config.isApp = true;
	mui.plusReady(function(){
		Object.assign(app.Config.device, {
			isAndroid : plus.os.name === "Android", //
			isIOS : plus.os.name === "iOS", //IOS
			model: plus.device.model, //
			imsi: plus.device.imsi, // ,//Android - 2.2+ (): SIM|iOS - 4.5+ (): iOSSIM
			vendor: plus.device.vendor, // 
			uuid: plus.device.uuid, //
//			resolutionHeight: plus.screen.resolutionHeight * plus.screen.scale, //
//			resolutionWidth: plus.screen.resolutionWidtht * plus.screen.scale, //
//			scale: plus.screen.scale, //
			version: plus.os.version, //
			osName: plus.os.name //
		});
		app.Config.version = plus.runtime.version;
		app.Config.clientVersion = plus.runtime.innerVersion;
		initVue();
	});
} else {
	mui.ready(function() {
		initVue();
	});
}
2. appapp.js
/**
 * [email protected]
 * 2016-03-03
 * app 
 */
const site = {
	Config: {
		resourecePath: "", //
		serverPath: "", //
		version: "", //app
		releaseTime: "", //
		isDebug: true, //
		innerVersion : "999.999.999", // 
		startVersion: "0.1.1", //app
		isInsideApp : false, // APP
		isWeiXin : false, //
		isApp: false, //app
		device: {
			isAndroid : false, //
			isIOS : false, //IOS
			model: null, //
			imsi: null, // ,//Android - 2.2+ (): SIM|iOS - 4.5+ (): iOSSIM
			vendor: null, // 
			uuid: null, //
			resolutionHeight: null, //
			resolutionWidth: null, //
			scale: null, //
			version: null, //
			osName: null, //
		}//
	},

	initApp() {
		//
		if(window.navigator && window.navigator.userAgent) {
			var ua = window.navigator.userAgent.toLocaleLowerCase();
			site.Config.isWeiXin = (ua.match(/MicroMessenger/i) == 'micromessenger');
		}
	},

	//ajax
	ajax: function(options) {
		let _url = null;
		if(app.Config.isApp && plus.networkinfo.getCurrentType() === plus.networkinfo.CONNECTION_NONE){
			app.mui.toast('<a href="javascript:void(0);" style="text-decoration: underline;color: #FFF;" onclick="window.location.reload();">~</a>', {duration:'8000', type:'div'});
			return;
		}
		if(typeof(options) === "string"){
			_url = options;
		} else if(typeof(options) !== "object" || !options.url){
			app.mui.alert("Ajax ");
			return;
		} else {
			_url = options.url;
			delete options.url;
		}
		var _default = {
			type: "POST",
            dataType: "json",
            headers:{'Content-Type':'application/json'},
            processData: true, //data
            async: true,
            timeout: 20000,
            auth: false, //API
            authFailCallbackFun: null, // 
            successFunData: true, //
            showLoading: false //
		};
        var _options = app.mui.extend(true, {}, _default, options);
       	if(!_options.data) {
       		_options.data = {};
       	}
       	if(_options.type.toUpperCase() === 'GET'){
        	_url += '&rnd='+ (+new Date()+'');
        	_options.data = _options.data || {};
        } else if(!_url.match(/^(?:http|ftp|https):\/\//)){
        	//url http://
            _url = app.Config.webapiDomain + _url;
        }
        if(_default.processData && typeof(_options.data) === "object" && _options.type.toUpperCase() === 'POST'){
        	_options.data = JSON.stringify(_options.data);
        }
        if(_options.auth === true && !app.globalService.isLogin()) {
        	if(typeof(_options.authFailCallbackFun) === "function"){
        		_options.authFailCallbackFun();
        	}
        	return;
        }
        _options.success = function(data, textStatus){
        	if(_options.showLoading === true){
            	//router.app.$emit('vHideLoad')
            }
        	var _data = data;
        	if(_options.successFunData === true){
        		if(data.success){
        			_data = data.result;
    			} else if(data.error && data.error.message) {
					app.mui.alert(data.error.message, "");
					return;
				} else {
					app.mui.toast("");
					return;
				}
        	}
        	if(typeof(options.success) === "function"){
        		options.success(_data);
        	}
        	if(typeof(options.complete) === "function"){
        		options.complete(data, textStatus);
        	}
        }
        _options.error = function(xhr, error){
        	if(_options.showLoading === true){
        		_options.showLoading = false;
            	//router.app.$emit('vHideLoad');
            }
            app.log.debug(xhr, error);
        	if(typeof(options.error) === "function"){
        		options.error(xhr, error);
        	} else if (xhr.response) {
        		var responseJSON = null;
        		try{
        			responseJSON = JSON.parse(xhr.response);
        		}catch(e){}
        		if(responseJSON && responseJSON.__abp && responseJSON.unAuthorizedRequest){
        			//app.mui.toast('<a href="javascript:void(0);" style="text-decoration: underline;color: #FFF;" onclick="window.location.reload();">~</a>', {duration: 8000, type:'div'});
        			app.mui.confirm("~", null, ['',''], function(e){
        				if(e.index === 1){
        					app.vueApp.$router.push({ name: 'login' });
        				}
        			});
        		} else if(responseJSON && responseJSON.__abp && responseJSON.error && responseJSON.error.message){
        			app.mui.toast(responseJSON.error.message);
        		}
            } else {
                app.mui.toast('<a href="javascript:void(0);" style="text-decoration: underline;color: #FFF;" onclick="window.location.reload();">~</a>', {duration: 8000, type:'div'});
            }
        	if(typeof(options.complete) === "function"){
        		options.complete(xhr, error);
        	}
        }
        _options.beforeSend = function(xhr){
        	//xhr.setRequestHeader("ClientVersion", app.Config.innerVersion);
        	var _token = app.globalService.getLoginUserInfo().token;
        	if(_token){
                xhr.setRequestHeader("Authorization", _token);
            }
            if(_options.showLoading === true){
            	//router.app.$emit('vShowLoad');
            }
            if(typeof(options.beforeSend) === "function"){
        		options.beforeSend(xhr);
        	}
        }
        app.mui.ajax(_url, _options);
	},
	
	// http://
    getResourceUrl: function(url){
    	if(url && url.match(/http:\/\//)){
            return url;
    	}
        //
        if(window.abp){
            return app.Config.imageDomain + url;
        }
        return "http://img.yaomaiche.com" + url;
    },

	//
	getSiteLocalStorage: function(){
		var _site_local_storage = app.utils.localStorage("siteLocalStorage");
		if(_site_local_storage) {
			try {
				_site_local_storage = JSON.parse(_site_local_storage);
			}catch(e){
				app.log.error(e);
			}
		}
		if(_site_local_storage == null || typeof(_site_local_storage) != "object"){
			_site_local_storage = {};
		}
		return _site_local_storage;
	},
};
site.initApp();
module.exports = site;
3. routers.js
import globalService from './services/global-service'
import appRouters from "./components/app-routers"

export default {
	routes: [{
		path: '/', //
		name: "home",
        meta: { title: "" },
		component: require('../views/home.vue') //resolve => require(['../views/home.vue'], resolve)
	},{
		path: '/users/user-center', //
		name: "userCenter",
		meta: { title: "" },
		component: require('../views/users/user-center.vue') 
	},{
		path: '/users/my-message-list', //
		name: "myMessageList",
		meta: { title: "" },
		component: require('../views/users/my-message-list.vue') 
	},{
		path: '/users/message-details', //
		name: "messageDetails",
		meta: { title: "" },
		component: require('../views/users/message-details.vue') 
	},{
		path: '/users/user-info', //
		name: "userInfo",
		meta: { title: "" },
		component: require('../views/users/user-info.vue') 
	},{
		path: '/users/reset-password', //
		name: "resetPassword",
		meta: { title: "" },
		component: require('../views/users/reset-password.vue') 
	},{
		path: '/users/register', //
		name: "register",
		meta: {auth: false, title: "" },
		component: require('../views/users/register.vue') 
	},{
		path: '/users/login', //
		name: "login",
		meta: {auth: false, title: "" },
		component: require('../views/users/login.vue') 
	},{
		path: '/customerGather/my-customer-gathers',
		name: "myCustomerGathers",
		meta: {title: "" },
		component: require('../views/customerGather/my-customer-gathers.vue') 
	},{
		path: '/barcode', //
		name: "barcode",
		meta: {title: "" },
		component: require('../views/barcode.vue') 
	},{
		path: '/users/welcome', //
		name: "welcome",
		meta: {auth: false, title: "" },
		component: require('../views/users/welcome.vue') 
	},{
		path: '*', //
		name: "notFound",
		meta: {auth: false, title: "" },
		component: require('../views/error/404.vue') 
	}],
	
	// 
	scrollBehavior(to, from, savedPosition) {
		if (savedPosition) {
	    	return savedPosition;
	  	} else {
	    	return { x: 0, y: 0 };
	  	}
	  	if (to.hash) {
    		return { selector: to.hash};
  		}
	},
	
	//
	createRouter(VueRouter, store){
		var _this = this;
		var router = new VueRouter({
			//
			routes: _this.routes,
			// 
			scrollBehavior: _this.scrollBehavior,
			//hash:  URL hash  HTML5 History Api 
			//history:  HTML5 History API  HTML5 History .
			//abstract:  JavaScript  Node.js  API
			//mode: 'history',
			// /app/  base  "/app/"
			base: "/",
			// <router-link>  class  router-link.
			linkActiveClass: "router-link-active"
		});
		//const [_push, _go, _replace] = [router.push, router.go, router.replace];
		const {push, go, replace} = router;
		router.push = function(location) {
			console.info("........push");
			if(!store.state.routerStatus.direction){
				store.dispatch("updateDirection", "going");
			}
			push.call(this, location);
		}
		router.go = function(n) {
			console.info("........go");
			if(store.state.routerStatus.direction != "backing"){
				store.dispatch("updateDirection", "backing");
			}
			go.call(this, location);
		}
		router.replace = function(location) {
			console.info("........replace");
			if(store.state.routerStatus.direction != "replace"){
				store.dispatch("updateDirection", "replace");
			}
			replace.call(this, location);
		}
		router.beforeEach((to, from, next)=>_this.beforeEach(to, from, next, store));
		router.afterEach((router)=> _this.afterEach(router, store));
		return router;
	},

	//
	beforeEach(to, from, next, store){
		console.info(to.name + "...................beforeEach");
		if(JSON.stringify(store.state.routerStatus.backConfig) !== "{}") {
			store.dispatch("resetBackConfig");
		}
		if(to.meta.auth !== false && !globalService.isLogin()){
			next({name: 'login', query: Object.assign({toName: to.name}, to.query)});
			return;
		}
		switch(to.name) {
			case 'home':
				store.dispatch("updateNavbarStatus",{isShowHead: false, isShowBack: false});
				appRouters.clear();
				break;
			case 'userCenter':
				store.dispatch("updateNavbarStatus",{isShowHead: false, isShowBack: false});
				appRouters.clear();
				break;
			case 'myCustomerGathers':
				store.dispatch("updateNavbarStatus",{isShowHead: false, isShowBack: false});
				appRouters.clear();
				break;
			case 'login':
				store.dispatch("updateNavbarStatus",{isShowBack: false, isShowHead: true, isShowFoot: false});
				appRouters.clear();
				break;
			case 'welcome':
				store.dispatch("updateNavbarStatus",{isShowBack: false, isShowHead: false, isShowFoot: false});
				appRouters.clear();
				break;
			case 'barcode':
				store.dispatch("updateTransition", null);
				store.dispatch("updateNavbarStatus",{isShowBack: false, isShowHead: false, isShowFoot: false});
				appRouters.clear();
				break;
			default:
				store.dispatch("updateNavbarStatus",{isShowFoot: false});
				break;
		}
//		if(site.globalService.isLogin() && "_login _reg _smslogin".indexOf(transition.to.name) != -1) {
//			next({path: '/login', query: { redirect: to.fullPath }});
//			return;
//		}
		//  confirmed 
		// next(false):  URL  URL  from 
		// next('/')  next({ path: '/' }): 
		next();
		if(store.state.routerStatus.direction) {
			appRouters.push((store.state.routerStatus.direction == "going" || store.state.routerStatus.direction == "backing" || store.state.routerStatus.direction == "replace"), {
				name: to.name,
				query: to.query,
				url: window.location.href
			});
		} else {
			store.dispatch("updateDirection", appRouters.push(false, {name: to.name, query: to.query, url: window.location.href}) ? "going" : "backing");
		}
		console.info("...................next");
		store.dispatch("updateDirection", null);
	},
	
	//
	afterEach(router, store){
		console.info("...................afterEach");
		if(router.meta.title && router.meta.title != store.state.appData.navbarTitle){
			store.dispatch("updateNavbarTitle", router.meta.title);
		}
	}
}
4.

app3

5. JS

JSappsignalR

6.

DEVGQCPRDPREUATDEV.js

/**
 * [email protected]
 * 2016-03-07
 * dev 
 */
module.exports = {
	//M
	webapiDomain:'http://storeapi.dev.XXX.cn',
	//M
	msiteDomain:'http://msite.dev.platform.XXX.com',
	//
	payDomain:'http://pay.shdev.XXX.com',
	//
	imageDomain:'http://img.shdev.ymc.com',
	//
	resourceUploadUrl:'http://resource.shdev.XXX.com/Uploader',
	//H5
	h5ServiceHost:'http://h5.private.XXX.app:7070',
	//
	commonDomain: "http://common.dev.ymc.com:8080"
	//UBT
}
7. app

appglobal-service.js,

/**
 * [email protected]
 * 2015-08-04
 * APP 
 */
export default{
	//
	isLogin() {
        return true;
    },
    
    //
    getStartFlag(){
    	//apptrue
    	if(!app.Config.isApp) {
    		return true;
    	}
    	const _site_local_storage = app.getSiteLocalStorage();
    	if(_site_local_storage.startInfo) {
    		return (_site_local_storage.startInfo.flag === true && _site_local_storage.startInfo.version === app.Config.startVersion);
    	}
    	return false;
    },
    
    //
    setStartFlag(flag){
    	const _site_local_storage = app.getSiteLocalStorage();
		if(_site_local_storage.startInfo == null || typeof(_site_local_storage.startInfo) != "object"){
			_site_local_storage.startInfo = {};
		}
		Object.assign(_site_local_storage.startInfo, {flag: flag, version: app.Config.startVersion});
		app.utils.localStorage("siteLocalStorage", JSON.stringify(_site_local_storage));
    },
    
    //Token
    getLoginUserInfo(){
    	const [_currentTime, _userInfo] = [(new Date()).getTime(), app.getSiteLocalStorage().userInfo || {}];
    	if(_userInfo.expireTime && (_userInfo.expireTime - _currentTime) > 0) {
    		return _userInfo;
    	} else {
    		app.globalService.setUserInfo({});
    		return {};
    	}
    },
    
    //
    logOut(){
    	app.globalService.setUserInfo({});
    },
    
    //
    setUserInfo({tenancyName, token, usernameOrEmailAddress, expireTime = -1}) {
    	if(expireTime > 0) {
    		const _site_local_storage = app.getSiteLocalStorage();
			if(_site_local_storage.userInfo == null || typeof(_site_local_storage.userInfo) != "object"){
				_site_local_storage.userInfo = {};
			}
			expireTime = (new Date()).getTime() + (expireTime - 60) * 1000;
			Object.assign(_site_local_storage.userInfo, {tenancyName, token, usernameOrEmailAddress, expireTime, version: app.Config.innerVersion});
    		app.utils.localStorage("siteLocalStorage", JSON.stringify(_site_local_storage));
    	} else {
    		app.utils.localStorage("siteLocalStorage", "{}");
    	}
    },
    
    //app TODO:  by yujinjin
    updateApp(){
    	//mui.os.plus && !mui.os.stream && mui.plusReady(update);
    	app.ajax({
    		url: "", //URL
    		data: {
    			"appid": plus.runtime.appid,
				"version": plus.runtime.version,
				"imei": plus.device.imei
    		},
    		success: function(data){
    			if (data.status) {
					plus.nativeUI.confirm(data.note, function(event) {
						if (0 == event.index) {
							plus.runtime.openURL(data.url);
						}
					}, data.title, ["", ""]);
				}
    		}
    	});
    }
}
8. vuexwebApp

webAppappindex.js

9. webapp

app

10. webapp

appJS

  • appHBuilder IDEmanifest.jsonandroidiOS,dcloud

  • dcloud

  • appdcloud

      1. (apk/ipa)
      1. AppApp
      1. App
        JS
// src/js/utils/update.js

/**
 * [email protected]
 * 2017-01-19
 * app
 */
module.exports = {
	//
	updateWgt(){
		plus.downloader.createDownload("http://demo.dcloud.net.cn/test/update/H5EF3C469.wgt", {filename:"_doc/update/"}, function(d,status){
			plus.nativeUI.showWaiting("wgt...");
	        if ( status == 200 ) { 
	            app.log.debug("wgt"+ d.filename);
	            plus.nativeUI.showWaiting("wgt...");
			    plus.runtime.install(d.filename, {} ,function(){
			        plus.nativeUI.closeWaiting();
			        app.log.debug("wgt");
			        plus.nativeUI.alert("",function(){
			            plus.runtime.restart();
			        });
			    },function(e){
			        plus.nativeUI.closeWaiting();
			        app.log.debug("wgt["+e.code+"]"+e.message);
			        plus.nativeUI.alert("wgt["+e.code+"]"+e.message);
			    });
	        } else {
	            app.log.debug("wgt");
	            plus.nativeUI.alert("wgt");
	        }
	        plus.nativeUI.closeWaiting();
	    }).start();
	},
	//
	updateApk(){
		if(app.Config.device.isAndroid){
			plus.downloader.createDownload("", {filename:"_doc/update/"}, function(d,status){
				plus.nativeUI.showWaiting("app...");
		        if ( status == 200 ) { 
		            app.log.debug("app"+ d.filename);
		            plus.nativeUI.showWaiting("app...");
				    plus.runtime.install(d.filename, {} ,function(){
				        plus.nativeUI.closeWaiting();
				        app.log.debug("app");
				        plus.nativeUI.alert("",function(){
				            plus.runtime.restart();
				        });
				    },function(e){
				        plus.nativeUI.closeWaiting();
				        app.log.debug("app["+e.code+"]"+e.message);
				        plus.nativeUI.alert("app["+e.code+"]"+e.message);
				    });
		        } else {
		            app.log.debug("wgt");
		            plus.nativeUI.alert("wgt");
		        }
		        plus.nativeUI.closeWaiting();
		    }).start();
		} else if(app.Config.device.isIOS){
			//iOSipaappstoreappstoreappstore
			var url='itms-apps://itunes.apple.com/cn/app/hello-h5+/id682211190?l=zh&mt=8';// HelloH5appstore
			plus.runtime.openURL(url);
		}
	}

}

  • star!!!!!

  • lssues