{"id":14976326,"url":"https://github.com/yujinjin/fans","last_synced_at":"2025-04-05T18:12:14.509Z","repository":{"id":52617468,"uuid":"82165760","full_name":"yujinjin/fans","owner":"yujinjin","description":"这是一个app(android/iOS)项目，但页面视图全部都用的是html5页，没有使用app的原生页面。 前端h5是基于mui + vue2 + vue-router2 + es6 + webpack2 + vuex + signalR 的前端webApp单页项目框架，项目可以直接在PC上运行html5页面。  app打包技术是用HBuilder IDE工具一键打包成APP。","archived":false,"fork":false,"pushed_at":"2021-04-23T07:50:36.000Z","size":42621,"stargazers_count":419,"open_issues_count":18,"forks_count":150,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-03-29T17:13:16.049Z","etag":null,"topics":["es6","mui","signalr","vue-router","vue2","vue2-demo","vuex2","webapp","webpack2"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yujinjin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-16T09:54:59.000Z","updated_at":"2025-02-13T14:59:52.000Z","dependencies_parsed_at":"2022-08-25T21:10:13.115Z","dependency_job_id":null,"html_url":"https://github.com/yujinjin/fans","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yujinjin%2Ffans","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yujinjin%2Ffans/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yujinjin%2Ffans/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yujinjin%2Ffans/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yujinjin","download_url":"https://codeload.github.com/yujinjin/fans/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247378152,"owners_count":20929297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["es6","mui","signalr","vue-router","vue2","vue2-demo","vuex2","webapp","webpack2"],"created_at":"2024-09-24T13:53:43.414Z","updated_at":"2025-04-05T18:12:14.488Z","avatar_url":"https://github.com/yujinjin.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"## 前言\r\n这是一个app(android/iOS)项目，但页面视图全部都用的是html5页，没有使用app的原生页面，项目可以直接在PC上运行html5页面。与服务端的交互全部都是走web api接口方式。客户端的登录是JSON WEB TOKEN 认证([JSON Web Token（JWT)是什么鬼](https://github.com/bigmeow/JWT))。项目里有android Apk打包文件,可以直接下载安装[点此链接下载](https://github.com/yujinjin/fans/tree/master/unpackage/release/fans.apk)。 \r\n\r\n前端h5是基于[mui](http://dev.dcloud.net.cn/mui/) + [vue2](http://cn.vuejs.org/v2/api/) + [vue-router2](http://router.vuejs.org/zh-cn/) + [es6](http://es6.ruanyifeng.com/) + [webpack2](http://webpack.github.io/) + [vuex](http://vuex.vuejs.org/zh-cn/) + [signalR](http://signalr.net/)的前端webApp单页项目框架。\r\n\r\napp打包技术是用[HBuilder IDE](http://www.dcloud.io/index.html)工具一键打包成APP，本项目使用了原生设备的的Storage和管理条码扫描。对于app的升级是html5资源在线升级更新,而不是整个APP更新。这些都是[dcloud](http://www.dcloud.io/index.html)提供一整套技术解决方案。\r\n\r\n本项目只是一个技术框架，对于项目中具体的业务的东西只会大概的说明一下。\r\n\r\n\r\n**说明：** 可能有些朋友不知道[signalR](http://signalr.net/)是什么东西，其实[signalR](http://signalr.net/)就是让客户端（Web页面）和服务器端可以互相通知消息及调用方法的前端JS，当WebSockets可用时（即浏览器支持Html5）[signalR](http://signalr.net/)使用WebSockets，当不支持时[signalR](http://signalr.net/)将使用其它技术来保证达到相同效果。\r\n\r\n\r\n\u003e 1. 前端UI的部分使用mui框架\r\n\u003e \r\n\u003e 2. app打包技术使用HBuilder IDE工具\r\n\u003e \r\n\u003e 3. 原生App对设备的调用\r\n\u003e \r\n\u003e 4. 使用vue-router2实现单页路由\r\n\u003e \r\n\u003e 5. 使用.vue文件进行页面功能组件化的开发\r\n\u003e \r\n\u003e 6. 使用vuex管理webApp的数据状态\r\n\u003e \r\n\u003e 7. 使用signalR实现客户端与服务端长时间通信\r\n\u003e \r\n\u003e 8. 使用webpack2实现对模块打包、压缩、混淆，预处理，热加载。\r\n\u003e \r\n\u003e 9. 自己实现了一套路由机制，但它只适用于app中使用h5的header。\r\n\r\n**吐槽：** 我想吐槽一下webpack2的webpack.config.js中各个插件配置，当时配置了好几天，这个loader配置好了，另外一个又出问题了，而且网上关于webapck2 API太少了，都是靠摸索着前进，真的是好难配。那有人肯定问了为啥不用VUE官方提供的vue-cli创建项目，我想说兄弟呀那个vue-cli是针对webpack1的，我想用webpack2（不要问我为什么，我就是固执的想用），而且官方配置的JS太TM的不直接了，10个配置文件我想改一些配置得看半天。\r\n\r\n\r\n## 安装\r\n- 下载[HBuilder IDE](http://www.dcloud.io/index.html)开发工具，其实HBuilder是dcloud 把eclipse的改造成一个专门应用于app打包、多种语言支持：php、jsp、ruby、python、nodejs等web语言，less、coffee等编译型语言均支持的开发工具\r\n\r\n- 下载[node.js](https://nodejs.org/en/)，作为前端web的运行环境。我当前的node.js版本是6.9.2 npm版本是3.10.9\r\n\r\n\r\n- app打包完全是基于manifest.json配置文件，它主要是用来配置app的基本信息（版本号、appid等）、图标(app的应用图标)、sdk配置、模块权限配置、页面引用关系、代码视图，具体参看dcloud提供的[文档](http://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/94)。\r\n\r\n\r\n## npm初始化\r\n\r\n##### package.json内容如下\r\n\r\n```\r\n{\r\n\t\"name\": \"Fans\",\r\n\t\"version\": \"1.0.0\",\r\n\t\"description\": \"粉丝煲\",\r\n\t\"main\": \"js/entrance.js\",\r\n\t\"keywords\": \"粉丝煲\",\r\n\t\"homepage\": \"\",\r\n\t\"bugs\": {\r\n\t\t\"url\": \"https://github.com/yujinjin/fans/issues\",\r\n\t\t\"email\": \"yujinjin9@126.com\"\r\n\t},\r\n\t\"author\": {\r\n\t\t\"name\": \"jinyu\",\r\n\t\t\"email\": \"yujinjin9@126.com\",\r\n\t\t\"url\": \"https://github.com/yujinjin\"\r\n\t},\r\n\t\"license\": \"MIT\",\r\n\t\"repository\": {\r\n\t\t\"type\": \"git\",\r\n\t\t\"url\": \"https://github.com/yujinjin/fans.git\"\r\n\t},\r\n\t\"scripts\": {\r\n\t\t\"R_DEV\": \"set NODE_RUN=1\u0026\u0026webpack-dev-server --progress --watch --inline --host=0.0.0.0  --port 8083\",\r\n\t\t\"B_DEV\":\"set NODE_ENV=dev\u0026set NODE_RUN=0\u0026webpack --progress --hide-modules\",\r\n\t\t\"lint\": \"eslint --ext .js,.vue src test/unit/specs test/e2e/specs\"\r\n\t},\r\n\t\"dependencies\": {\r\n\t\t\"vue\": \"^2.1.8\",\r\n\t\t\"vue-resource\": \"^1.0.3\",\r\n\t\t\"vue-router\": \"^2.0.1\",\r\n\t\t\"vue-html-loader\": \"1.2.3\",\r\n    \t\"vue-loader\": \"10.0.0\",\r\n    \t\"vue-style-loader\": \"^1.0.0\",\r\n    \t\"vue-template-compiler\": \"^2.1.0\"\r\n\t},\r\n\t\"devDependencies\": {\r\n\t\t\"vuex\": \"^2.0.0\",\r\n\t\t\"autoprefixer\": \"^6.4.0\",\r\n\t    \"babel-core\": \"^6.0.0\",\r\n\t    \"babel-eslint\": \"^7.0.0\",\r\n\t    \"babel-loader\": \"^6.0.0\",\r\n\t    \"babel-plugin-transform-runtime\": \"^6.0.0\",\r\n\t    \"babel-preset-es2015\": \"^6.0.0\",\r\n\t    \"babel-preset-stage-2\": \"^6.0.0\",\r\n\t    \"babel-register\": \"^6.0.0\",\r\n\t    \"babel-polyfill\": \"^6.22.0\",\r\n\t\t\"cross-env\": \"^1.0.6\",\r\n\t\t\"css-loader\": \"^0.25.0\",\r\n\t\t\"less\": \"^2.7.1\",\r\n\t\t\"less-loader\": \"^2.2.3\",\r\n\t\t\"file-loader\": \"^0.9.0\",\r\n\t\t\"html-loader\": \"^0.4.4\",\r\n\t\t\"html-webpack-plugin\": \"^2.24.1\",\r\n\t\t\"jshint\": \"^2.9.4\",\r\n\t\t\"jshint-loader\": \"^0.8.3\",\r\n\t\t\"style-loader\": \"^0.13.1\",\r\n\t\t\"url-loader\": \"^0.5.7\",\r\n\t\t\"extract-text-webpack-plugin\": \"^2.0.0-beta.4\",\r\n\t\t\"webpack\": \"^2.1.0-beta.25\",\r\n\t\t\"webpack-dev-server\": \"^2.1.0-beta.10\",\r\n\t\t\"webpack-require-http\": \"^0.4.0\"\r\n\t},\r\n\t\r\n\t\"engines\": {\r\n\t\t\"node\": \"\u003e=5.0.0\",\r\n\t\t\"npm\": \"\u003e=3.3.6\"\r\n\t}\r\n}\r\n\r\n```\r\n\r\n##### 开发环境依赖模块说明\r\n\r\n\r\n```\r\nvue                            //构建用户界面的\r\nvue-resource                   //vue 的http ajax请求插件（本项目没有用它，暂时保留）\r\nvue-router                     //vue 路由插件\r\nvue-html-loader                //vue html加载器\r\nvue-loader                     //vue加载器\r\nvue-style-loader               //vue的样式加载器\r\nvue-template-compiler          //vue的模板编译器\r\nvuex                           //组件状态管理\r\nautoprefixer                   //css  浏览器兼容性问题处理\r\nbabel-core                     //ES6  代码转换器\r\nbabel-eslint                   //ES6的代码检查\r\nbabel-loader                   //ES6  代码转换器，webpack插件\r\nbabel-plugin-transform-runtime //和polyfill类似，替换助手函数\r\nbabel-preset-es2015            //ES6  代码编译成现在浏览器支持的ES5\r\nbabel-preset-stage-2           //ES6  ES7要使用的语法阶段\r\nbabel-register                 //用于改写require命令,为它加上一个钩子。此后,每当使用require加载.js、.jsx、.es和.es6后缀名的文件,就会先用Babel进行转码。\r\nbabel-polyfill                 //Babel默认只转换新的JavaScript句法（syntax），而不转换新的API，babel-polyfill就是为当前环境提供一个垫片。解决一些浏览器不能识别的语法，比如：Promise\r\ncross-env                      //解决跨平台设置NODE_ENV的问题\r\ncss-loader                     //css  生成\r\nless                           //css  预处理器less\r\nless-loader                    //css  预处理器less的webpack插件\r\nfile-loader                    //webpack的文件加载器，主要用于字体  将字体文件打包\r\nhtml-loader                    //webpack的html加载器，主要用于html文件的加载\r\nhtml-webpack-plugin            //html  文件编译\r\njshint                         //Js代码检查工具\r\njshint-loader                  //webpack的jshint加载器，主要用于Js代码检查工具\r\nstyle-loader                   //webpack的style加载器，主要用于css  插入到style标签\r\nurl-loader                     //webpack的url加载器，主要用于图片加载及限制\r\nextract-text-webpack-plugin    //把额外的数据内容加到编译好的文件中 (独立打包样式文件)\r\nwebpack                        //用来构建打包程序\r\nwebpack-dev-server             //开发环境下，设置代理服务器\r\nwebpack-require-http           //webapck打包环境下的requrire加载http文件的插件\r\n```\r\n\r\n\r\n\r\n## 项目目录说明\r\n\r\n\r\n```\r\n|-- build                               // webapck打包后的文件目录\r\n|-- logo                                // 存放app的图表地址目录\r\n|-- src                                 // 源码目录\r\n|   |-- components                      // 存放公共组件的目录\r\n|       |-- member-qrcode.vue           // 会员二维码公共组件\r\n|       |-- ...                         // 其他公共组件\r\n|   |-- css                             // 存放各种css文件目录\r\n|       |-- app.css                     // app的公用样式文件 \r\n|       |-- icons-extra.css             // icons的扩展字体样式 \r\n|       |-- mui.css                     // mui框架css\r\n|       |-- ...                         // 其他css\r\n|   |-- fonts                           // 存放各种fonts文件目录\r\n|       |-- ...                         // 其他fonts文件\r\n|   |-- imgs                            // 存放各种图片文件目录\r\n|       |-- test                        // 存放开发测试的图片文件目录\r\n|           |-- ...                     // 其他测试图片文件\r\n|       |-- ...                         // 其他图片文件\r\n|   |-- js                              // 存放各种js文件目录\r\n|       |-- components                  // 存放各种js组件的目录\r\n|           |-- app-routers.js          // 站点路由插件（只做路由的操作，不涉及实际的业务处理）\r\n|           |-- signalR.js              // signalR组件\r\n|           |-- ...                     // 其他JS组件\r\n|       |-- config                      // 存放打包各种环境的目录\r\n|           |-- DEV.js                  // DEV环境配置文件\r\n|           |-- GQC.js                  // GQC环境配置文件\r\n|           |-- PRD.js                  // PRD环境配置文件\r\n|           |-- ...                     // 其他环境配置文件\r\n|       |-- lib                         // 第三方JS lib目录\r\n|           |-- mui.js                  // mui插件\r\n|           |-- ...                     // 其他第三方JS插件\r\n|       |-- services                    // app自己的业务目录\r\n|           |-- global-service.js       // APP 全局业务逻辑方法，主要处理登录、登出的业务逻辑\r\n|       |-- store                       // vuex管理webApp的数据状态目录\r\n|           |-- index.js                // app数据管理入口文件\r\n|           |-- app-data.js             // app临时数据管理\r\n|           |-- app-event.js            // app事件管理\r\n|           |-- router-status.js        // app路由状态管理\r\n|       |-- utils                       // app的存放工具\r\n|           |-- directives.js           // vue 自定义指令文件\r\n|           |-- log.js                  // app log日志\r\n|           |-- update.js               // app在线更新\r\n|           |-- utils.js                // app站点页面表单验证框架工具类\r\n|           |-- ....                    // 其他工具JS文件\r\n|   \t\t|-- app.js                      // app配置以及其他方法\r\n|   \t\t|-- entrance.js                 // app程序入口文件，加载各种公共组件\r\n|   \t\t|-- routers.js                  // vue的路由配置文件\r\n|   |-- json                      \t\t\t// 测试的json数据存放目录\r\n|   |-- less                      \t\t\t// 存放各种less文件的目录\r\n|   \t\t|-- app.less                    // app基础样式，包含其他less文件的入口\r\n|   \t\t|-- ...                         // 其他less样式文件\r\n|   |-- views                      \t\t\t// 存放各种页面视图组件目录\r\n|   \t\t|-- error                       // 存放错误视图组件目录\r\n|   \t\t\t\t|-- 404.vue                 // 404页面视图\r\n|   \t\t|-- users                      \t// 存放用户的视图组件目录\r\n|   \t\t\t\t|-- login.vue               // 登录页面\r\n|   \t\t\t\t|-- user-center.vue         // 用户中心页面\r\n|   \t\t\t\t|-- welcome.vue         \t\t// 欢迎页面\r\n|   \t\t\t\t|-- ...         \t\t// 其他视图页面\r\n|   \t\t|-- ...                     // 其他功能模块目录\r\n|   \t\t|-- app.vue                     // app页面入口文件\r\n|   \t\t|-- barcode.vue                 // barcode页面入口文件\r\n|   \t\t|-- home.vue                    // app首页面\r\n|   |-- index.html                      // app的html模板页面\r\n|-- unpackage                           // app编译包目录\r\n|-- .babelrc                            // ES6语法编译配置\r\n|-- .editorconfig                       // 编辑器编码规范配置\r\n|-- .gitignore                          // git忽略文件\r\n|-- index.html                          // webapp的首页加载文件\r\n|-- manifest.json                       // 打包app的配置文件\r\n|-- package.json                        // 配置项目相关信息，通过执行 npm init 命令创建\r\n|-- webpack.config.js                   // webpack配置文件\r\n```\r\n\r\n## 上图\r\n##### 1. app首次启动-欢迎页面\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/969A81C308FE49EB9F04FC527DC3CEB1/1098)\r\n\r\n##### 2. 登录页\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/4450268334F74135AD89CD829DACBB3A/1251)\r\n\r\n##### 3. 首页\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/DF54BD8D51AA4B35A4F54A02EBA0743A/1086)\r\n\r\n###### 3. 1. 扫码核销\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/049F878BA97F4B8BBB581094DC9533CF/1091)\r\n\r\n\r\n###### 3. 2. 会员识别\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/0FACF7F6C6894C4E8557AD08658E0A89/1088)\r\n\r\n###### 3. 3. 素材列表\r\n\r\n\r\n##### 4. 我的集客(随便凑的一个页面)\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/D4B0EB839E53489F9A64F6F3FF5DB45B/1249)\r\n\r\n###### 4. 1. 会员列表\r\n\r\n\r\n##### 5. 个人中心\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/227254DE56E2459AB053B31352688643/965)\r\n\r\n###### 5. 1. 个人资料\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/0E885C4086324932B18B816FB2F31B57/1074)\r\n\r\n###### 5. 2. 我的收益\r\n\r\n\r\n###### 5. 3. 密码修改\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/BA781772D4964035B55AA432CA0A9C21/972)\r\n\r\n###### 5. 4. 消息列表\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/C08C00F62A0E46AE94D8031AFD9E3864/974)\r\n\r\n###### 5. 5. 消息内容\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/DC592CD8FA92455E8C445120FC6D0242/975)\r\n\r\n## 运行程序\r\n\r\n项目地址：（`git clone`）\r\n```shell\r\ngit clone https://github.com/yujinjin/fans.git\r\n```\r\n通过`npm`安装本地服务第三方依赖模块(需要已安装[Node.js](https://nodejs.org/))\r\n\r\n```\r\nnpm install\r\n```\r\n启动DEV服务(http://localhost:8083)\r\n\r\n```\r\nnpm run R_DEV (window)\r\nnpm run MR_DEV (MAC)\r\n```\r\n打包发布DEV代码\r\n\r\n```\r\nnpm run B_DEV (window)\r\nnpm run MB_DEV (MAC)\r\n```\r\n\r\n说明一下：由于要解决移动端iOS操作系统click事件延迟300ms问题，特意使用了tap事件来替代click事件。所以运行时最好是在浏览器中的手机模拟器中操作。\r\n\r\n## 实现的功能\r\n- 用户登录\r\n- 首页\r\n    - 扫码核销\r\n    - 会员识别\r\n    - 消息中心\r\n    - 我的收益\r\n    - 营销（素材列表、分享集客）\r\n    - 集客排行榜\r\n- 我的集客\r\n    - 会员管理\r\n    - 集团集客排行榜\r\n    - 本院集客排行榜\r\n- 个人中心\r\n    - 员工信息\r\n    - 我的收益\r\n    - 密码修改\r\n    - 消息中心（收益变化 等）\r\n    - 用户注销\r\n\r\n\r\n## webpack.config.js 配置说明\r\n\r\n\r\n```\r\nconst path = require('path'),\r\n\twebpack = require('webpack'),\r\n\tNODE_ENV = process.env.NODE_ENV || \"DEV\", //环境类型\r\n\tNODE_RUN = process.env.NODE_RUN || \"0\", //是否是运行\r\n\tROOT_PATH = path.resolve(__dirname) + \"\\\\\",\r\n\tOUT_PATH = path.resolve(ROOT_PATH, 'build') + \"\\\\\",\r\n\tSERVER_PATH = process.env.SERVER || \"./build/\",// 服务路径\r\n\tExtractTextPlugin = require(\"extract-text-webpack-plugin\"),\r\n\tHtmlWebpackPlugin = require(\"html-webpack-plugin\");\r\nmodule.exports = {\r\n\tentry: {\r\n\t\tpage: \"./src/js/entrance.js\", //[ROOT_PATH + \"\\\\js\\\\entrance.js\"],\r\n\t\t// 打包第三方库作为公共包\r\n\t    commons: ['vue', 'vue-router']\r\n\t},\r\n\toutput: {\r\n\t\tpath: NODE_RUN === \"0\" ? './build' : \"/\",//\"./build\",//\"./build\",//path.resolve(__dirname, './build'), //path.resolve(__dirname, './build'), //\r\n\t\t//publicPath路径就是你发布之后的路径，比如你想发布到你站点的/util/vue/build 目录下, 那么设置publicPath: \"/util/vue/build/\",此字段配置如果不正确，发布后资源定位不对，比如：css里面的精灵图路径错误\r\n\t\tpublicPath: NODE_RUN === \"0\" ? \"./build/\": \"/build/\",//\"build/\",//SERVER_PATH, //process.env.CUSTOM ? \"/git/WebApp/n-build/\" : \"/n-build/\",\r\n\t\tfilename: NODE_RUN === \"0\" ? \"build.[hash].js\" : \"build.js\",\r\n\t\t/*\r\n\t    \timport()加载的文件会被分开打包, 我们称这个包为chunk, chunkFilename用来配置这个chunk输出的文件名.\r\n\t    \t[id]: 编译时每个chunk会有一个id.\r\n\t    \t[chunkhash]: 这个chunk的hash值, 文件发生变化时该值也会变. 文件名加上该值可以防止浏览器读取旧的缓存文件.\r\n\t    */\r\n\t    //chunkFilename: '[id].js?[chunkhash]',\r\n\t},\r\n\texternals:[require('webpack-require-http')],\r\n\tmodule: {\r\n\t\trules: [{\r\n          \ttest: /\\.html$/,\r\n          \tuse: [{\r\n          \t\tloader: 'html-loader',\r\n          \t\toptions: {\r\n            \t\t//root: resolve(__dirname, 'src'),\r\n            \t\tattrs: ['img:src', 'link:href']\r\n          \t\t}\r\n           }]\r\n\t\t}, {\r\n            test: /\\.js(x)*$/,\r\n            exclude: /^node_modules$/,\r\n            //loader: 'babel-loader'\r\n            use: ['babel-loader']\r\n       \t}, {\r\n\t\t\ttest: /\\.vue$/,\r\n\t\t\tuse: ['vue-loader']\r\n\t\t\t//loader: 'vue-loader'\r\n//\t\t\toptions: {\r\n//\t\t\t\tloaders: {\r\n//\t\t            css: ExtractTextPlugin.extract({\r\n//\t\t              \tloader: 'css-loader',\r\n//\t\t              \tfallbackLoader: 'vue-style-loader' // \u003c- this is a dep of vue-loader, so no need to explicitly install if using npm3\r\n//\t\t            })\r\n//\t\t        }\r\n//\t\t\t}\r\n\t\t}, \r\n//\t\t{\r\n//\t\t\ttest: /\\.html$/,\r\n//\t\t\tloader: 'html-loader',\r\n//\t\t\toptions: {\r\n//            \t/*\r\n//\t              \thtml-loader接受attrs参数, 表示什么标签的什么属性需要调用webpack的loader进行打包.\r\n//\t              \t比如\u003cimg\u003e标签的src属性, webpack会把\u003cimg\u003e引用的图片打包, 然后src的属性值替换为打包后的路径.\r\n//\t              \t使用什么loader代码, 同样是在module.rules定义中使用匹配的规则.\r\n//\t              \t如果html-loader不指定attrs参数, 默认值是img:src, 意味着会默认打包\u003cimg\u003e标签的图片.\r\n//\t              \t这里我们加上\u003clink\u003e标签的href属性, 用来打包入口index.html引入的favicon.png文件.\r\n//            \t*/\r\n//            \tattrs: ['img:src', 'link:href']\r\n//          }\r\n//\t\t},\r\n\t\t{\r\n\t\t\ttest: /\\.css$/,\r\n\t\t\texclude: /^node_modules$/,\r\n//\t\t\tuse:[{\r\n//          \tloader: 'style-loader'\r\n//          },{\r\n//          \tloader: 'css-loader'\r\n//          }]\r\n\t\t\t//use: ['style-loader', 'css-loader']\r\n//\t\t\tloader: ['style-loader', 'css-loader']\r\n//\t\t\tloader: `vue-style-loader!css-loader!autoprefixer-loader?{ browsers: ['last 100 versions'] }!`\r\n\t\t\tloader: ExtractTextPlugin.extract({\r\n                fallbackLoader: \"style-loader\",\r\n                loader: \"css-loader\",\r\n                publicPath: \"./\"\r\n            })\r\n\t\t}, {\r\n            test: /\\.less/,\r\n            exclude: /^node_modules$/,\r\n//          use:[{\r\n//          \tloader: 'style-loader'\r\n//          },{\r\n//          \tloader: 'css-loader'\r\n//          },{\r\n//          \tloader: 'less-loader'\r\n//          }]\r\n//                loader: ['style-loader', 'css-loader', 'less-loader']\r\n//                loader: `vue-style-loader!css-loader!less-loader!autoprefixer-loader?{ browsers: ['last 100 versions'] }!less-loader`\r\n            loader: ExtractTextPlugin.extract({\r\n\t          \tfallbackLoader: 'style-loader',\r\n\t          \tloader: \"css-loader!less-loader\",\r\n                publicPath: \"./\"\r\n\t        })\r\n        },{\r\n\t        test: /\\.(png|jpe?g|gif|svg)(\\?.*)?$/,\r\n\t        use: [{\r\n\t        \tloader: \"url-loader\",\r\n\t        \tquery: {\r\n\t\t          \tlimit: 10000,\r\n\t\t          \tname: 'imgs/[name].[hash:7].[ext]'\r\n\t        \t}\r\n\t        }]\r\n\t        //loader: 'url-loader',\r\n//\t        use: ['url-loader'],\r\n//\t        query: {\r\n//\t          \tlimit: 5000,\r\n//\t          \tname: 'imgs/[name].[hash:7].[ext]'\r\n//\t        }\r\n      \t} ,{\r\n\t        test: /\\.(woff2?|eot|ttf|otf)(\\?.*)?$/,\r\n\t        use: [{\r\n\t        \tloader: \"url-loader\",\r\n\t        \tquery: {\r\n\t\t          \tlimit: 5000,\r\n\t\t          \tname: 'fonts/[name].[hash:7].[ext]'\r\n\t        \t}\r\n\t        }]\r\n\t        //loader: 'url-loader',\r\n//\t        query: {\r\n//\t          \tlimit: 5000,\r\n//\t          \tname: 'fonts/[name].[hash:7].[ext]'\r\n//      \t}\r\n      \t}\r\n//      ,{\r\n//\t\t\t//!cssnext-loader 会导致压缩的时候动画命名被覆盖\r\n//\t\t\ttest: /\\.(png|jpg)$/,\r\n//\t\t\texclude: /^node_modules$/,\r\n//\t\t\t//注意后面那个limit的参数，当你图片大小小于这个限制的时候，会自动启用base64编码图片\r\n//\t\t\tloader: 'url-loader?limit=8000\u0026name=[name].[ext]'\r\n//\t\t}, {\r\n//          test: /\\.(eot|woff|svg|ttf|woff2|gif|appcache)(\\?|$)/,\r\n//          exclude: /^node_modules$/,\r\n//          loader: 'file-loader?name=[name].[ext]'\r\n//      }\r\n\t\t]\r\n\t},\r\n\tplugins:[\r\n//\t\tnew ExtractTextPlugin({\r\n//\t\t\tfileName: NODE_RUN === \"0\" ? \"style.[hash].css\" : \"style.css\",\r\n//\t\t\tdisable: false,\r\n//\t\t\tallChunks: true\r\n//\t\t}), //加上这个参数老是编译不通过不知道为什么\r\n\t\tnew ExtractTextPlugin(NODE_RUN === \"0\" ? \"style.[hash].css\" : \"style.css\"),\r\n\t\tnew HtmlWebpackPlugin({\r\n\t\t\tfilename: \"../index.html\", //生成的html存放路径，相对于 path\r\n\t\t\ttemplate: './src/index.html', //html模板路径\r\n\t\t\tfavicon: \"./src/imgs/goldfish.ico\",\r\n\t\t\tinject: true, //允许插件修改哪些内容，包括head与body\r\n\t\t\tminify: { //压缩HTML文件\r\n\t\t\t\tremoveComments: true, //移除HTML中的注释\r\n\t\t\t\tcollapseWhitespace: false, //删除空白符与换行符\r\n\t\t\t\t//removeAttributeQuotes: true\r\n\t\t\t}\r\n\t\t}),\r\n\t\t/*\r\n\t\t      使用CommonsChunkPlugin插件来处理重复代码\r\n\t\t      因为vendor.js和index.js都引用了spa-history, 如果不处理的话, 两个文件里都会有spa-history包的代码,\r\n      \t\t我们用CommonsChunkPlugin插件来使共同引用的文件只打包进vendor.js\r\n      \t*/\r\n\t\tnew webpack.optimize.CommonsChunkPlugin({\r\n\t\t\tname: \"commons\",\r\n\t\t\tfilename: NODE_RUN === \"0\" ? \"common.[hash].js\" : \"common.js\",\r\n\t\t\tminChunks: function (module, count) {\r\n\t\t        // any required modules inside node_modules are extracted to vendor\r\n\t\t        return (module.resource \u0026\u0026 /\\.js$/.test(module.resource) \u0026\u0026 module.resource.indexOf(path.join(__dirname, '../node_modules')) === 0);\r\n\t\t    }\r\n\t\t}),\r\n//\t\tnew webpack.optimize.CommonsChunkPlugin({\r\n//    \t\tname: 'manifest',\r\n//    \t\tchunks: ['commons']\r\n//  \t}),\r\n\t\t//自动分析重用的模块并且打包成单独的文件\r\n\t\tnew webpack.ProvidePlugin({\r\n\t\t\t//根据环境加载JS\r\n\t\t\tconfig: ROOT_PATH + \"\\\\src\\\\js\\\\config\\\\\" + NODE_ENV\r\n\t\t})\r\n\t],\r\n\tresolve: {\r\n        extensions: ['.js', '.vue', '.jsx', '.less', '.scss', '.css'], //后缀名自动补全\r\n    \t//fallback: [path.join(__dirname, '../node_modules')], webpack2 不能有这个\r\n//  \talias: {\r\n//          hubs : 'http://www.dev.zmscrm.cn/signalr/hubs',//后续直接 require('AppStore') 即可\r\n//      }\r\n\t},\r\n\tdevServer: {\r\n\t\thistoryApiFallback: true,//配置为true, 当访问的文件不存在时, 返回根目录下的index.html文件\r\n\t\tnoInfo: true\r\n\t},\r\n\tperformance: {\r\n    \thints: false\r\n  \t},\r\n//\tvue: {\r\n//\t\tloaders: {\r\n//\t\t\tcss:\"vue-style-loader!css-loader?sourceMap\",\r\n//\t\t\tless:\"vue-style-loader!css-loader?sourceMap!less-loader?sourceMap\"\r\n//\t\t},\r\n//      postcss: [\r\n//          require('autoprefixer')({\r\n//              browsers: ['last 100 versions']\r\n//          })\r\n//      ]\r\n//\t},\r\n\tdevtool: '#eval-source-map'\r\n}\r\nvar fileSystem = require('fs');\r\n//打包状态\r\nif(NODE_RUN === \"0\") {\r\n\tmodule.exports.devtool = false;\r\n\tmodule.exports.plugins = (module.exports.plugins || []).concat([\r\n//\t\tnew webpack.LoaderOptionsPlugin({\r\n//\t\t      \tminimize: true\r\n//\t\t}), //加上这个编辑“url('data:image/svg+xml;charset=utf-8,\u003csvg....”会报错\r\n\t\tnew webpack.DefinePlugin({\r\n\t\t\t'process.env': {\r\n\t\t\t\tNODE_ENV: '\"production\"'\r\n\t\t\t}\r\n\t\t}),\r\n\t\tnew webpack.optimize.UglifyJsPlugin({\r\n\t\t\tcompress: {\r\n\t\t        warnings: false\r\n\t\t    },\r\n\t\t    output: {\r\n\t\t        comments: false\r\n\t\t    },\r\n\t\t\tsourceMap: false\r\n\t\t})\r\n\t]);\r\n\t//非开发环境下要清空 output 文件夹下的文件\r\n\tvar dirArray = [];\r\n\t//递归删文件\r\n\tvar clearOutPutDir = function(path) {\r\n\t\tif(fileSystem.existsSync(path)) {\r\n\t\t\tvar dirList = fileSystem.readdirSync(path);\r\n\t\t\tdirList.forEach(function(fileName) {\r\n\t\t\t\tif(fileSystem.statSync(path + fileName).isDirectory()) {\r\n\t\t\t\t\tconsole.info(\"目录:\" + path + fileName);\r\n\t\t\t\t\t// 目录\r\n\t\t\t\t\tdirArray.push(path + fileName);\r\n\t\t\t\t\tclearOutPutDir(path + fileName + \"\\\\\");\r\n\t\t\t\t} else {\r\n\t\t\t\t\tconsole.info(\"文件:\" + path + fileName);\r\n\t\t\t\t\tfileSystem.unlinkSync(path + fileName);\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t};\r\n\t}\r\n\tclearOutPutDir(OUT_PATH);\r\n\tfor(var i = dirArray.length - 1, j = 0; i \u003e= j; i--) {\r\n\t\tconsole.info(dirArray[i])\r\n\t\tfileSystem.rmdirSync(dirArray[i]);\r\n\t}\r\n} else {\r\n\tconsole.info(\"run........................................\");\r\n\t//本地运行状态把index.html中的href、src连接修改掉\r\n\tfileSystem.readFile(\"index.html\", 'utf-8', function(err, data) {\r\n\tif(err) {\r\n\t\tconsole.log(\"error\");\r\n\t} else {\r\n\t\t//将index.html里面的hash值清除掉\r\n\t\tvar devhtml = data.replace(/((?:href|src)=\"[^\"]+\\.)(\\w{20}\\.)(js|css)/g, '$1$3');\r\n\t\tfileSystem.writeFileSync('index.html', devhtml);\r\n\t}\r\n});\r\n}\r\n\r\n```\r\n\r\n## webApp技术框架说明\r\n##### 1. 入口（entrance.js）\r\nwebpack的入口加载文件，也是Webapp的初始化。主要做app SUI的初始化、VUE的初始化、webapp的常用JS加载。\r\n\r\n```\r\nimport babelPolyfill from 'babel-polyfill'\r\nimport mui from \"./lib/mui\"\r\nimport app from './app'\r\nimport globalService from './services/global-service'\r\nimport log from './utils/log'\r\nimport utils from './utils/utils'\r\nimport directives from \"./utils/directives\"\r\nimport Vue from 'vue'\r\nimport Vuex from 'vuex'\r\nimport VueRouter from 'vue-router'\r\nimport routers from \"./routers\"\r\nimport vueApp from \"../views/app\"\r\nimport store from \"./store/\"\r\nimport jQuery from \"./lib/jquery-1.12.4\"\r\n\r\nObject.assign(app.Config, config);\r\nwindow.app = Object.assign({}, app, {log, utils, mui, globalService});\r\n//signalR是基于jquery的，所以必须要把jQuery引进来，仅仅是用于signalR。太恶心了，其实我TM的真的不想这样...\r\nwindow.jQuery = window.$ = jQuery;\r\nconst initVue = function(){\r\n\tVue.use(Vuex);\r\n\tVue.use(VueRouter);\r\n\tObject.keys(directives).forEach((key) =\u003e {\r\n\t    Vue.directive(key, directives[key]);\r\n\t});\r\n\tconst [router, VueApp] = [routers.createRouter(VueRouter, store), Vue.extend(vueApp)];\r\n\twindow.app.vueApp = new VueApp({ router, name: \"app\", store }).$mount('#app');\r\n}\r\nmui.init({\r\n\tswipeBack:false, //关闭右滑关闭功能（默认就是false）\r\n\tkeyEventBind: {\r\n\t\tbackbutton: true  //开启back按键监听（默认就是true）\r\n\t},\r\n\tstatusBarBackground: \"#1981D8\" //设置状态栏颜色,仅iOS可用\r\n});\r\nif(mui.os.plus) {\r\n\tapp.Config.isApp = true;\r\n\tmui.plusReady(function(){\r\n\t\tObject.assign(app.Config.device, {\r\n\t\t\tisAndroid : plus.os.name === \"Android\", //是否在安卓环境内\r\n\t\t\tisIOS : plus.os.name === \"iOS\", //是否在IOS环境内\r\n\t\t\tmodel: plus.device.model, //设备的型号\r\n\t\t\timsi: plus.device.imsi, //设备的国际移动用户识别码 ,//Android - 2.2+ (支持): 如果设备没有插入SIM卡，则返回空数组。|iOS - 4.5+ (不支持): iOS设备不支持获取SIM卡信息，返回空数组。\r\n\t\t\tvendor: plus.device.vendor, // 设备的生产厂商\r\n\t\t\tuuid: plus.device.uuid, //设备的唯一标识\r\n//\t\t\tresolutionHeight: plus.screen.resolutionHeight * plus.screen.scale, //设备屏幕高度分辨率\r\n//\t\t\tresolutionWidth: plus.screen.resolutionWidtht * plus.screen.scale, //设备屏幕宽度分辨率，目前好像是空的\r\n//\t\t\tscale: plus.screen.scale, //逻辑分辨率与实际分辨率的比例\r\n\t\t\tversion: plus.os.version, //系统版本信息\r\n\t\t\tosName: plus.os.name //系统的名称\r\n\t\t});\r\n\t\tapp.Config.version = plus.runtime.version;\r\n\t\tapp.Config.clientVersion = plus.runtime.innerVersion;\r\n\t\tinitVue();\r\n\t});\r\n} else {\r\n\tmui.ready(function() {\r\n\t\tinitVue();\r\n\t});\r\n}\r\n```\r\n\r\n##### 2. app配置以及其他方法（app.js）\r\n\r\n\r\n\r\n```\r\n/**\r\n * 作者：yujinjin9@126.com\r\n * 时间：2016-03-03\r\n * 描述：app 核心框架\r\n */\r\nconst site = {\r\n\tConfig: {\r\n\t\tresourecePath: \"\", //资源服务路径\r\n\t\tserverPath: \"\", //服务路径\r\n\t\tversion: \"\", //app版本\r\n\t\treleaseTime: \"\", //发布时间\r\n\t\tisDebug: true, //是否是前端调试状态\r\n\t\tinnerVersion : \"999.999.999\", // 获得当前终端的版本号\r\n\t\tstartVersion: \"0.1.1\", //app启动动画版本号\r\n\t\tisInsideApp : false, // 是否在APP应用环境内\r\n\t\tisWeiXin : false, //是否在微信环境内\r\n\t\tisApp: false, //是否是在app内\r\n\t\tdevice: {\r\n\t\t\tisAndroid : false, //是否在安卓环境内\r\n\t\t\tisIOS : false, //是否在IOS环境内\r\n\t\t\tmodel: null, //设备的型号\r\n\t\t\timsi: null, //设备的国际移动用户识别码 ,//Android - 2.2+ (支持): 如果设备没有插入SIM卡，则返回空数组。|iOS - 4.5+ (不支持): iOS设备不支持获取SIM卡信息，返回空数组。\r\n\t\t\tvendor: null, // 设备的生产厂商\r\n\t\t\tuuid: null, //设备的唯一标识\r\n\t\t\tresolutionHeight: null, //设备屏幕高度分辨率\r\n\t\t\tresolutionWidth: null, //设备屏幕宽度分辨率\r\n\t\t\tscale: null, //逻辑分辨率与实际分辨率的比例\r\n\t\t\tversion: null, //系统版本信息\r\n\t\t\tosName: null, //系统的名称\r\n\t\t}//设备信息\r\n\t},\r\n\r\n\tinitApp() {\r\n\t\t//获取当前环境\r\n\t\tif(window.navigator \u0026\u0026 window.navigator.userAgent) {\r\n\t\t\tvar ua = window.navigator.userAgent.toLocaleLowerCase();\r\n\t\t\tsite.Config.isWeiXin = (ua.match(/MicroMessenger/i) == 'micromessenger');\r\n\t\t}\r\n\t},\r\n\r\n\t//重写ajax定义的方法，主要用于自己业务逻辑上的处理\r\n\tajax: function(options) {\r\n\t\tlet _url = null;\r\n\t\tif(app.Config.isApp \u0026\u0026 plus.networkinfo.getCurrentType() === plus.networkinfo.CONNECTION_NONE){\r\n\t\t\tapp.mui.toast('\u003ca href=\"javascript:void(0);\" style=\"text-decoration: underline;color: #FFF;\" onclick=\"window.location.reload();\"\u003e亲~网络连接不上，请检测网络。点此刷新重试\u003c/a\u003e', {duration:'8000', type:'div'});\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif(typeof(options) === \"string\"){\r\n\t\t\t_url = options;\r\n\t\t} else if(typeof(options) !== \"object\" || !options.url){\r\n\t\t\tapp.mui.alert(\"Ajax 参数错误！\");\r\n\t\t\treturn;\r\n\t\t} else {\r\n\t\t\t_url = options.url;\r\n\t\t\tdelete options.url;\r\n\t\t}\r\n\t\tvar _default = {\r\n\t\t\ttype: \"POST\",\r\n            dataType: \"json\",\r\n            headers:{'Content-Type':'application/json'},\r\n            processData: true, //是否自动处理data数据\r\n            async: true,\r\n            timeout: 20000,\r\n            auth: false, //是否验证当前API接口的登录权限\r\n            authFailCallbackFun: null, // 验证失败的回调函数\r\n            successFunData: true, //是否验证成功回调函数的数据\r\n            showLoading: false //是否显示加载\r\n\t\t};\r\n        var _options = app.mui.extend(true, {}, _default, options);\r\n       \tif(!_options.data) {\r\n       \t\t_options.data = {};\r\n       \t}\r\n       \tif(_options.type.toUpperCase() === 'GET'){\r\n        \t_url += '\u0026rnd='+ (+new Date()+'');\r\n        \t_options.data = _options.data || {};\r\n        } else if(!_url.match(/^(?:http|ftp|https):\\/\\//)){\r\n        \t//如果传的url含有 http://说明是个绝对路径，就不用拼了\r\n            _url = app.Config.webapiDomain + _url;\r\n        }\r\n        if(_default.processData \u0026\u0026 typeof(_options.data) === \"object\" \u0026\u0026 _options.type.toUpperCase() === 'POST'){\r\n        \t_options.data = JSON.stringify(_options.data);\r\n        }\r\n        if(_options.auth === true \u0026\u0026 !app.globalService.isLogin()) {\r\n        \tif(typeof(_options.authFailCallbackFun) === \"function\"){\r\n        \t\t_options.authFailCallbackFun();\r\n        \t}\r\n        \treturn;\r\n        }\r\n        _options.success = function(data, textStatus){\r\n        \tif(_options.showLoading === true){\r\n            \t//router.app.$emit('vHideLoad')\r\n            }\r\n        \tvar _data = data;\r\n        \tif(_options.successFunData === true){\r\n        \t\tif(data.success){\r\n        \t\t\t_data = data.result;\r\n    \t\t\t} else if(data.error \u0026\u0026 data.error.message) {\r\n\t\t\t\t\tapp.mui.alert(data.error.message, \"错误提示\");\r\n\t\t\t\t\treturn;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tapp.mui.toast(\"出错了！\");\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n        \t}\r\n        \tif(typeof(options.success) === \"function\"){\r\n        \t\toptions.success(_data);\r\n        \t}\r\n        \tif(typeof(options.complete) === \"function\"){\r\n        \t\toptions.complete(data, textStatus);\r\n        \t}\r\n        }\r\n        _options.error = function(xhr, error){\r\n        \tif(_options.showLoading === true){\r\n        \t\t_options.showLoading = false;\r\n            \t//router.app.$emit('vHideLoad');\r\n            }\r\n            app.log.debug(xhr, error);\r\n        \tif(typeof(options.error) === \"function\"){\r\n        \t\toptions.error(xhr, error);\r\n        \t} else if (xhr.response) {\r\n        \t\tvar responseJSON = null;\r\n        \t\ttry{\r\n        \t\t\tresponseJSON = JSON.parse(xhr.response);\r\n        \t\t}catch(e){}\r\n        \t\tif(responseJSON \u0026\u0026 responseJSON.__abp \u0026\u0026 responseJSON.unAuthorizedRequest){\r\n        \t\t\t//app.mui.toast('\u003ca href=\"javascript:void(0);\" style=\"text-decoration: underline;color: #FFF;\" onclick=\"window.location.reload();\"\u003e亲~登录过期了。点此重新登录\u003c/a\u003e', {duration: 8000, type:'div'});\r\n        \t\t\tapp.mui.confirm(\"亲~登录过期了！\", null, ['想再看看','去登录'], function(e){\r\n        \t\t\t\tif(e.index === 1){\r\n        \t\t\t\t\tapp.vueApp.$router.push({ name: 'login' });\r\n        \t\t\t\t}\r\n        \t\t\t});\r\n        \t\t} else if(responseJSON \u0026\u0026 responseJSON.__abp \u0026\u0026 responseJSON.error \u0026\u0026 responseJSON.error.message){\r\n        \t\t\tapp.mui.toast(responseJSON.error.message);\r\n        \t\t}\r\n            } else {\r\n                app.mui.toast('\u003ca href=\"javascript:void(0);\" style=\"text-decoration: underline;color: #FFF;\" onclick=\"window.location.reload();\"\u003e亲~服务出错了。点此刷新重试\u003c/a\u003e', {duration: 8000, type:'div'});\r\n            }\r\n        \tif(typeof(options.complete) === \"function\"){\r\n        \t\toptions.complete(xhr, error);\r\n        \t}\r\n        }\r\n        _options.beforeSend = function(xhr){\r\n        \t//xhr.setRequestHeader(\"ClientVersion\", app.Config.innerVersion);\r\n        \tvar _token = app.globalService.getLoginUserInfo().token;\r\n        \tif(_token){\r\n                xhr.setRequestHeader(\"Authorization\", _token);\r\n            }\r\n            if(_options.showLoading === true){\r\n            \t//router.app.$emit('vShowLoad');\r\n            }\r\n            if(typeof(options.beforeSend) === \"function\"){\r\n        \t\toptions.beforeSend(xhr);\r\n        \t}\r\n        }\r\n        app.mui.ajax(_url, _options);\r\n\t},\r\n\t\r\n\t//获取图片地址，如果地址带有 http://那么就认为是绝对地址，然后直接返回\r\n    getResourceUrl: function(url){\r\n    \tif(url \u0026\u0026 url.match(/http:\\/\\//)){\r\n            return url;\r\n    \t}\r\n        //全站统一配置\r\n        if(window.abp){\r\n            return app.Config.imageDomain + url;\r\n        }\r\n        return \"http://img.yaomaiche.com\" + url;\r\n    },\r\n\r\n\t//获取站点本地存储信息\r\n\tgetSiteLocalStorage: function(){\r\n\t\tvar _site_local_storage = app.utils.localStorage(\"siteLocalStorage\");\r\n\t\tif(_site_local_storage) {\r\n\t\t\ttry {\r\n\t\t\t\t_site_local_storage = JSON.parse(_site_local_storage);\r\n\t\t\t}catch(e){\r\n\t\t\t\tapp.log.error(e);\r\n\t\t\t}\r\n\t\t}\r\n\t\tif(_site_local_storage == null || typeof(_site_local_storage) != \"object\"){\r\n\t\t\t_site_local_storage = {};\r\n\t\t}\r\n\t\treturn _site_local_storage;\r\n\t},\r\n};\r\nsite.initApp();\r\nmodule.exports = site;\r\n```\r\n\r\n##### 3. 单页路由配置（routers.js）\r\n\r\n```\r\nimport globalService from './services/global-service'\r\nimport appRouters from \"./components/app-routers\"\r\n\r\nexport default {\r\n\troutes: [{\r\n\t\tpath: '/', //首页\r\n\t\tname: \"home\",\r\n        meta: { title: \"首页\" },\r\n\t\tcomponent: require('../views/home.vue') //resolve =\u003e require(['../views/home.vue'], resolve)\r\n\t},{\r\n\t\tpath: '/users/user-center', //用户中心\r\n\t\tname: \"userCenter\",\r\n\t\tmeta: { title: \"个人中心\" },\r\n\t\tcomponent: require('../views/users/user-center.vue') \r\n\t},{\r\n\t\tpath: '/users/my-message-list', //消息中心\r\n\t\tname: \"myMessageList\",\r\n\t\tmeta: { title: \"消息列表\" },\r\n\t\tcomponent: require('../views/users/my-message-list.vue') \r\n\t},{\r\n\t\tpath: '/users/message-details', //消息中心\r\n\t\tname: \"messageDetails\",\r\n\t\tmeta: { title: \"消息详情\" },\r\n\t\tcomponent: require('../views/users/message-details.vue') \r\n\t},{\r\n\t\tpath: '/users/user-info', //个人资料\r\n\t\tname: \"userInfo\",\r\n\t\tmeta: { title: \"个人资料\" },\r\n\t\tcomponent: require('../views/users/user-info.vue') \r\n\t},{\r\n\t\tpath: '/users/reset-password', //个人资料\r\n\t\tname: \"resetPassword\",\r\n\t\tmeta: { title: \"重设密码\" },\r\n\t\tcomponent: require('../views/users/reset-password.vue') \r\n\t},{\r\n\t\tpath: '/users/register', //注册\r\n\t\tname: \"register\",\r\n\t\tmeta: {auth: false, title: \"注册\" },\r\n\t\tcomponent: require('../views/users/register.vue') \r\n\t},{\r\n\t\tpath: '/users/login', //登录\r\n\t\tname: \"login\",\r\n\t\tmeta: {auth: false, title: \"登录\" },\r\n\t\tcomponent: require('../views/users/login.vue') \r\n\t},{\r\n\t\tpath: '/customerGather/my-customer-gathers',\r\n\t\tname: \"myCustomerGathers\",\r\n\t\tmeta: {title: \"我的集客\" },\r\n\t\tcomponent: require('../views/customerGather/my-customer-gathers.vue') \r\n\t},{\r\n\t\tpath: '/barcode', //二维码扫描\r\n\t\tname: \"barcode\",\r\n\t\tmeta: {title: \"二维码扫描\" },\r\n\t\tcomponent: require('../views/barcode.vue') \r\n\t},{\r\n\t\tpath: '/users/welcome', //登录\r\n\t\tname: \"welcome\",\r\n\t\tmeta: {auth: false, title: \"启动欢迎\" },\r\n\t\tcomponent: require('../views/users/welcome.vue') \r\n\t},{\r\n\t\tpath: '*', //未发现该页面\r\n\t\tname: \"notFound\",\r\n\t\tmeta: {auth: false, title: \"未发现该页面\" },\r\n\t\tcomponent: require('../views/error/404.vue') \r\n\t}],\r\n\t\r\n\t//使用前端路由，当切换到新路由时，想要页面滚到顶部，或者是保持原先的滚动位置，就像重新加载页面那样。 \r\n\tscrollBehavior(to, from, savedPosition) {\r\n\t\tif (savedPosition) {\r\n\t    \treturn savedPosition;\r\n\t  \t} else {\r\n\t    \treturn { x: 0, y: 0 };\r\n\t  \t}\r\n\t  \tif (to.hash) {\r\n    \t\treturn { selector: to.hash};\r\n  \t\t}\r\n\t},\r\n\t\r\n\t//创建路由\r\n\tcreateRouter(VueRouter, store){\r\n\t\tvar _this = this;\r\n\t\tvar router = new VueRouter({\r\n\t\t\t//路由列表\r\n\t\t\troutes: _this.routes,\r\n\t\t\t//使用前端路由，当切换到新路由时，想要页面滚到顶部，或者是保持原先的滚动位置，就像重新加载页面那样。 \r\n\t\t\tscrollBehavior: _this.scrollBehavior,\r\n\t\t\t//hash: 使用 URL hash 值来作路由。支持所有浏览器，包括不支持 HTML5 History Api 的浏览器。\r\n\t\t\t//history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式.\r\n\t\t\t//abstract: 支持所有 JavaScript 运行环境，如 Node.js 服务器端。如果发现没有浏览器的 API，路由会自动强制进入这个模式。\r\n\t\t\t//mode: 'history',\r\n\t\t\t//应用的基路径。例如，如果整个单页应用服务在 /app/ 下，然后 base 就应该设为 \"/app/\"。\r\n\t\t\tbase: \"/\",\r\n\t\t\t//全局配置 \u003crouter-link\u003e 的默认『激活 class 类名』。参考 router-link.\r\n\t\t\tlinkActiveClass: \"router-link-active\"\r\n\t\t});\r\n\t\t//const [_push, _go, _replace] = [router.push, router.go, router.replace];\r\n\t\tconst {push, go, replace} = router;\r\n\t\trouter.push = function(location) {\r\n\t\t\tconsole.info(\"........push\");\r\n\t\t\tif(!store.state.routerStatus.direction){\r\n\t\t\t\tstore.dispatch(\"updateDirection\", \"going\");\r\n\t\t\t}\r\n\t\t\tpush.call(this, location);\r\n\t\t}\r\n\t\trouter.go = function(n) {\r\n\t\t\tconsole.info(\"........go\");\r\n\t\t\tif(store.state.routerStatus.direction != \"backing\"){\r\n\t\t\t\tstore.dispatch(\"updateDirection\", \"backing\");\r\n\t\t\t}\r\n\t\t\tgo.call(this, location);\r\n\t\t}\r\n\t\trouter.replace = function(location) {\r\n\t\t\tconsole.info(\"........replace\");\r\n\t\t\tif(store.state.routerStatus.direction != \"replace\"){\r\n\t\t\t\tstore.dispatch(\"updateDirection\", \"replace\");\r\n\t\t\t}\r\n\t\t\treplace.call(this, location);\r\n\t\t}\r\n\t\trouter.beforeEach((to, from, next)=\u003e_this.beforeEach(to, from, next, store));\r\n\t\trouter.afterEach((router)=\u003e _this.afterEach(router, store));\r\n\t\treturn router;\r\n\t},\r\n\r\n\t//访问之前的函数\r\n\tbeforeEach(to, from, next, store){\r\n\t\tconsole.info(to.name + \"...................beforeEach\");\r\n\t\tif(JSON.stringify(store.state.routerStatus.backConfig) !== \"{}\") {\r\n\t\t\tstore.dispatch(\"resetBackConfig\");\r\n\t\t}\r\n\t\tif(to.meta.auth !== false \u0026\u0026 !globalService.isLogin()){\r\n\t\t\tnext({name: 'login', query: Object.assign({toName: to.name}, to.query)});\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tswitch(to.name) {\r\n\t\t\tcase 'home':\r\n\t\t\t\tstore.dispatch(\"updateNavbarStatus\",{isShowHead: false, isShowBack: false});\r\n\t\t\t\tappRouters.clear();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'userCenter':\r\n\t\t\t\tstore.dispatch(\"updateNavbarStatus\",{isShowHead: false, isShowBack: false});\r\n\t\t\t\tappRouters.clear();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'myCustomerGathers':\r\n\t\t\t\tstore.dispatch(\"updateNavbarStatus\",{isShowHead: false, isShowBack: false});\r\n\t\t\t\tappRouters.clear();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'login':\r\n\t\t\t\tstore.dispatch(\"updateNavbarStatus\",{isShowBack: false, isShowHead: true, isShowFoot: false});\r\n\t\t\t\tappRouters.clear();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'welcome':\r\n\t\t\t\tstore.dispatch(\"updateNavbarStatus\",{isShowBack: false, isShowHead: false, isShowFoot: false});\r\n\t\t\t\tappRouters.clear();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'barcode':\r\n\t\t\t\tstore.dispatch(\"updateTransition\", null);\r\n\t\t\t\tstore.dispatch(\"updateNavbarStatus\",{isShowBack: false, isShowHead: false, isShowFoot: false});\r\n\t\t\t\tappRouters.clear();\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tstore.dispatch(\"updateNavbarStatus\",{isShowFoot: false});\r\n\t\t\t\tbreak;\r\n\t\t}\r\n//\t\tif(site.globalService.isLogin() \u0026\u0026 \"_login _reg _smslogin\".indexOf(transition.to.name) != -1) {\r\n//\t\t\tnext({path: '/login', query: { redirect: to.fullPath }});\r\n//\t\t\treturn;\r\n//\t\t}\r\n\t\t// 进行管道中的下一个钩子。如果全部钩子执行完了，则导航的状态就是 confirmed （确认的）。\r\n\t\t// next(false): 中断当前的导航。如果浏览器的 URL 改变了（可能是用户手动或者浏览器后退按钮），那么 URL 地址会重置到 from 路由对应的地址。\r\n\t\t// next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断，然后进行一个新的导航。\r\n\t\tnext();\r\n\t\tif(store.state.routerStatus.direction) {\r\n\t\t\tappRouters.push((store.state.routerStatus.direction == \"going\" || store.state.routerStatus.direction == \"backing\" || store.state.routerStatus.direction == \"replace\"), {\r\n\t\t\t\tname: to.name,\r\n\t\t\t\tquery: to.query,\r\n\t\t\t\turl: window.location.href\r\n\t\t\t});\r\n\t\t} else {\r\n\t\t\tstore.dispatch(\"updateDirection\", appRouters.push(false, {name: to.name, query: to.query, url: window.location.href}) ? \"going\" : \"backing\");\r\n\t\t}\r\n\t\tconsole.info(\"...................next\");\r\n\t\tstore.dispatch(\"updateDirection\", null);\r\n\t},\r\n\t\r\n\t//可以记录访问路径\r\n\tafterEach(router, store){\r\n\t\tconsole.info(\"...................afterEach\");\r\n\t\tif(router.meta.title \u0026\u0026 router.meta.title != store.state.appData.navbarTitle){\r\n\t\t\tstore.dispatch(\"updateNavbarTitle\", router.meta.title);\r\n\t\t}\r\n\t}\r\n}\r\n```\r\n\r\n\r\n\r\n##### 4. 视图组件\r\n存放app的视图组件的目录，目前有加载动画、二维码、弹窗3个组件。\r\n\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/9E24002FF01640D885F43821D0477FE0/1040)\r\n\r\n\r\n##### 5. JS组件\r\n下面是目录JS组件文件的截图，主要是存放app的路由、弹窗、signalR组件\r\n\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/3485025E71B247A98F3577B05B305F93/1007)\r\n\r\n##### 6. 环境打包配置\r\nDEV、GQC、PRD、PRE、UAT环境的打包配置文件，比如DEV.js的配置文件如下：\r\n\r\n```\r\n/**\r\n * 作者：yujinjin9@126.com\r\n * 时间：2016-03-07\r\n * 描述：dev 外部接口配置文件\r\n */\r\nmodule.exports = {\r\n\t//M站点的接口地址\r\n\twebapiDomain:'http://storeapi.dev.XXX.cn',\r\n\t//M站点的地址\r\n\tmsiteDomain:'http://msite.dev.platform.XXX.com',\r\n\t//支付服务器地址\r\n\tpayDomain:'http://pay.shdev.XXX.com',\r\n\t//获取资源服务器地址\r\n\timageDomain:'http://img.shdev.ymc.com',\r\n\t//上传资源服务器地址\r\n\tresourceUploadUrl:'http://resource.shdev.XXX.com/Uploader',\r\n\t//无线H5服务器地址\r\n\th5ServiceHost:'http://h5.private.XXX.app:7070',\r\n\t//公共服务站点地址\r\n\tcommonDomain: \"http://common.dev.ymc.com:8080\"\r\n\t//UBT服务器地址\r\n}\r\n```\r\n\r\n##### 7. app自己的业务\r\n目前app自己所特有的业务逻辑是global-service.js,是全局业务逻辑用于判断当前用户登录、站点本地存储等一些业务处理。具体代码如下：\r\n\r\n```\r\n/**\r\n * 作者：yujinjin9@126.com\r\n * 时间：2015-08-04\r\n * 描述：APP 全局业务逻辑\r\n */\r\nexport default{\r\n\t//判断当前用户信息是否登录\r\n\tisLogin() {\r\n        return true;\r\n    },\r\n    \r\n    //获取启动项标志\r\n    getStartFlag(){\r\n    \t//如果不是app始终返回true\r\n    \tif(!app.Config.isApp) {\r\n    \t\treturn true;\r\n    \t}\r\n    \tconst _site_local_storage = app.getSiteLocalStorage();\r\n    \tif(_site_local_storage.startInfo) {\r\n    \t\treturn (_site_local_storage.startInfo.flag === true \u0026\u0026 _site_local_storage.startInfo.version === app.Config.startVersion);\r\n    \t}\r\n    \treturn false;\r\n    },\r\n    \r\n    //设置启动项标志\r\n    setStartFlag(flag){\r\n    \tconst _site_local_storage = app.getSiteLocalStorage();\r\n\t\tif(_site_local_storage.startInfo == null || typeof(_site_local_storage.startInfo) != \"object\"){\r\n\t\t\t_site_local_storage.startInfo = {};\r\n\t\t}\r\n\t\tObject.assign(_site_local_storage.startInfo, {flag: flag, version: app.Config.startVersion});\r\n\t\tapp.utils.localStorage(\"siteLocalStorage\", JSON.stringify(_site_local_storage));\r\n    },\r\n    \r\n    //获取用户登录的Token信息\r\n    getLoginUserInfo(){\r\n    \tconst [_currentTime, _userInfo] = [(new Date()).getTime(), app.getSiteLocalStorage().userInfo || {}];\r\n    \tif(_userInfo.expireTime \u0026\u0026 (_userInfo.expireTime - _currentTime) \u003e 0) {\r\n    \t\treturn _userInfo;\r\n    \t} else {\r\n    \t\tapp.globalService.setUserInfo({});\r\n    \t\treturn {};\r\n    \t}\r\n    },\r\n    \r\n    //退出登录\r\n    logOut(){\r\n    \tapp.globalService.setUserInfo({});\r\n    },\r\n    \r\n    //设置用户信息\r\n    setUserInfo({tenancyName, token, usernameOrEmailAddress, expireTime = -1}) {\r\n    \tif(expireTime \u003e 0) {\r\n    \t\tconst _site_local_storage = app.getSiteLocalStorage();\r\n\t\t\tif(_site_local_storage.userInfo == null || typeof(_site_local_storage.userInfo) != \"object\"){\r\n\t\t\t\t_site_local_storage.userInfo = {};\r\n\t\t\t}\r\n\t\t\texpireTime = (new Date()).getTime() + (expireTime - 60) * 1000;\r\n\t\t\tObject.assign(_site_local_storage.userInfo, {tenancyName, token, usernameOrEmailAddress, expireTime, version: app.Config.innerVersion});\r\n    \t\tapp.utils.localStorage(\"siteLocalStorage\", JSON.stringify(_site_local_storage));\r\n    \t} else {\r\n    \t\tapp.utils.localStorage(\"siteLocalStorage\", \"{}\");\r\n    \t}\r\n    },\r\n    \r\n    //app更新升级 TODO: 需要根据实际的业务数据调整 by yujinjin\r\n    updateApp(){\r\n    \t//mui.os.plus \u0026\u0026 !mui.os.stream \u0026\u0026 mui.plusReady(update);\r\n    \tapp.ajax({\r\n    \t\turl: \"\", //更新URL\r\n    \t\tdata: {\r\n    \t\t\t\"appid\": plus.runtime.appid,\r\n\t\t\t\t\"version\": plus.runtime.version,\r\n\t\t\t\t\"imei\": plus.device.imei\r\n    \t\t},\r\n    \t\tsuccess: function(data){\r\n    \t\t\tif (data.status) {\r\n\t\t\t\t\tplus.nativeUI.confirm(data.note, function(event) {\r\n\t\t\t\t\t\tif (0 == event.index) {\r\n\t\t\t\t\t\t\tplus.runtime.openURL(data.url);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}, data.title, [\"立即更新\", \"取　　消\"]);\r\n\t\t\t\t}\r\n    \t\t}\r\n    \t});\r\n    }\r\n}\r\n```\r\n\r\n\r\n##### 8. vuex管理webApp的数据状态\r\n存放webApp的临时数据，目前有app业务数据、定义事件、路由状态信息，其index.js是数据管理的入口文件。\r\n\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/FF28D56C4EF04F4DBDA0DD9740DD1BAB/1027)\r\n\r\n##### 9. webapp的工具包\r\n存放app的工具包，目前有自定义指令、日志、更新、常用方法。\r\n\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/48323BE4CE9144988A25D2A3F9AB0D97/1026)\r\n\r\n\r\n##### 10. webapp的页面视图\r\n\r\n下面是目录视图文件的截图，主要是首页、登录、个人中心等页面视图。\r\n\r\n![image](http://note.youdao.com/yws/public/resource/1f0d14f63c838cc80fcfc5870b5ec8dc/xmlnote/4E375B79F2C940D1B33A20DAFF17CF6C/999)\r\n\r\n\r\n\r\n\r\n\r\n\r\n## app打包及原生JS接口调用\r\n\r\n- app打包技术是用HBuilder IDE工具通过manifest.json配置一键打包成android和iOS,具体教程参看dcloud提供的[文档](http://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/94)。\r\n\r\n- 原生设备的接口教程参看dcloud提供的[文档](http://www.html5plus.org/doc/h5p.html)。\r\n\r\n- app的更新，dcloud提供三种解决方案。目前采用第二种解决方案，第一种方案作为备用。\r\n    - 1. 整包(apk/ipa)升级 \r\n    - 2. App资源在线升级更新（生成移动App资源升级包直接下载更新）\r\n    - 3. App资源在线差量升级更新（差量升级包是针对某个历史版本到新版本的差量，所以对于升级服务器来讲需要保留所有历史版本，并且分别生成每个历史版本到新版本的差量升级包。）\r\n其更新的JS的代码如下\r\n\r\n```\r\n// src/js/utils/update.js\r\n\r\n/**\r\n * 作者：yujinjin9@126.com\r\n * 时间：2017-01-19\r\n * 描述：app在线更新\r\n */\r\nmodule.exports = {\r\n\t//资源在线升级更新\r\n\tupdateWgt(){\r\n\t\tplus.downloader.createDownload(\"http://demo.dcloud.net.cn/test/update/H5EF3C469.wgt\", {filename:\"_doc/update/\"}, function(d,status){\r\n\t\t\tplus.nativeUI.showWaiting(\"下载wgt文件...\");\r\n\t        if ( status == 200 ) { \r\n\t            app.log.debug(\"下载wgt成功：\"+ d.filename);\r\n\t            plus.nativeUI.showWaiting(\"安装wgt文件...\");\r\n\t\t\t    plus.runtime.install(d.filename, {} ,function(){\r\n\t\t\t        plus.nativeUI.closeWaiting();\r\n\t\t\t        app.log.debug(\"安装wgt文件成功！\");\r\n\t\t\t        plus.nativeUI.alert(\"应用资源更新完成！\",function(){\r\n\t\t\t            plus.runtime.restart();\r\n\t\t\t        });\r\n\t\t\t    },function(e){\r\n\t\t\t        plus.nativeUI.closeWaiting();\r\n\t\t\t        app.log.debug(\"安装wgt文件失败[\"+e.code+\"]：\"+e.message);\r\n\t\t\t        plus.nativeUI.alert(\"安装wgt文件失败[\"+e.code+\"]：\"+e.message);\r\n\t\t\t    });\r\n\t        } else {\r\n\t            app.log.debug(\"下载wgt失败！\");\r\n\t            plus.nativeUI.alert(\"下载wgt失败！\");\r\n\t        }\r\n\t        plus.nativeUI.closeWaiting();\r\n\t    }).start();\r\n\t},\r\n\t//整包更新\r\n\tupdateApk(){\r\n\t\tif(app.Config.device.isAndroid){\r\n\t\t\tplus.downloader.createDownload(\"\", {filename:\"_doc/update/\"}, function(d,status){\r\n\t\t\t\tplus.nativeUI.showWaiting(\"下载app文件...\");\r\n\t\t        if ( status == 200 ) { \r\n\t\t            app.log.debug(\"下载app成功：\"+ d.filename);\r\n\t\t            plus.nativeUI.showWaiting(\"安装app文件...\");\r\n\t\t\t\t    plus.runtime.install(d.filename, {} ,function(){\r\n\t\t\t\t        plus.nativeUI.closeWaiting();\r\n\t\t\t\t        app.log.debug(\"安装app文件成功！\");\r\n\t\t\t\t        plus.nativeUI.alert(\"应用资源更新完成！\",function(){\r\n\t\t\t\t            plus.runtime.restart();\r\n\t\t\t\t        });\r\n\t\t\t\t    },function(e){\r\n\t\t\t\t        plus.nativeUI.closeWaiting();\r\n\t\t\t\t        app.log.debug(\"安装app文件失败[\"+e.code+\"]：\"+e.message);\r\n\t\t\t\t        plus.nativeUI.alert(\"安装app文件失败[\"+e.code+\"]：\"+e.message);\r\n\t\t\t\t    });\r\n\t\t        } else {\r\n\t\t            app.log.debug(\"下载wgt失败！\");\r\n\t\t            plus.nativeUI.alert(\"下载wgt失败！\");\r\n\t\t        }\r\n\t\t        plus.nativeUI.closeWaiting();\r\n\t\t    }).start();\r\n\t\t} else if(app.Config.device.isIOS){\r\n\t\t\t//iOS平台的ipa无法安装，此时需要跳转到appstore，提示用户自动点击升级更新，跳转到appstore的方法为打开应用的appstore地址\r\n\t\t\tvar url='itms-apps://itunes.apple.com/cn/app/hello-h5+/id682211190?l=zh\u0026mt=8';// HelloH5应用在appstore的地址\r\n\t\t\tplus.runtime.openURL(url);\r\n\t\t}\r\n\t}\r\n\r\n}\r\n```\r\n\r\n\r\n## 最后\r\n- 如果喜欢一定要 star哈!!!（谢谢!!）\r\n\r\n- 如果有意见和问题 请在 lssues提出，我会在线解答。","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyujinjin%2Ffans","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyujinjin%2Ffans","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyujinjin%2Ffans/lists"}