Bladeren bron

初始化H5版本——20211019

Rockery 3 jaren geleden
commit
6260df6f5a
100 gewijzigde bestanden met toevoegingen van 8337 en 0 verwijderingen
  1. 24 0
      h5_web/.gitignore
  2. 18 0
      h5_web/App.vue
  3. 8 0
      h5_web/README.md
  4. 56 0
      h5_web/common/apiurl.js
  5. 21 0
      h5_web/common/config.js
  6. 88 0
      h5_web/common/http.api.js
  7. 71 0
      h5_web/common/http.interceptor.js
  8. 1201 0
      h5_web/components/tki-qrcode/qrcode.js
  9. 210 0
      h5_web/components/tki-qrcode/tki-qrcode.vue
  10. 42 0
      h5_web/main.js
  11. 77 0
      h5_web/manifest.json
  12. 180 0
      h5_web/pages.json
  13. 418 0
      h5_web/pages/authentication/authentication.vue
  14. 53 0
      h5_web/pages/bannerDetails/bannerDetails.vue
  15. 112 0
      h5_web/pages/basicTraining/basicTraining.vue
  16. 88 0
      h5_web/pages/basicTraining/basicTrainingDetails/basicTrainingDetails.vue
  17. 147 0
      h5_web/pages/basicTraining/components/offline.vue
  18. 127 0
      h5_web/pages/basicTraining/components/online.vue
  19. 84 0
      h5_web/pages/basicTraining/myBasicTraining/myBasicTraining.vue
  20. 22 0
      h5_web/pages/center/center.vue
  21. 42 0
      h5_web/pages/inbuild/inbuild.vue
  22. 43 0
      h5_web/pages/index/index.scss
  23. 188 0
      h5_web/pages/index/index.vue
  24. 115 0
      h5_web/pages/login/code.vue
  25. 133 0
      h5_web/pages/login/login.vue
  26. 159 0
      h5_web/pages/mine/mine.vue
  27. 111 0
      h5_web/pages/mycode/mycode.vue
  28. 56 0
      h5_web/pages/newsDetails/newsDetails.vue
  29. 319 0
      h5_web/pages/phoneLogin/phoneLogin.vue
  30. 30 0
      h5_web/pages/policyNews/policyNews.scss
  31. 166 0
      h5_web/pages/policyNews/policyNews.vue
  32. 56 0
      h5_web/pages/policyNewsDetails/policyNewsDetails.vue
  33. 64 0
      h5_web/pages/privacyPolicy/privacyPolicy.vue
  34. 156 0
      h5_web/pages/schools/schools.vue
  35. 115 0
      h5_web/pages/skillsTraining/skillsTraining.scss
  36. 283 0
      h5_web/pages/skillsTraining/skillsTraining.vue
  37. 116 0
      h5_web/pages/upgrade/upgrade.scss
  38. 251 0
      h5_web/pages/upgrade/upgrade.vue
  39. 952 0
      h5_web/static/css/quill.bubble.scss
  40. 399 0
      h5_web/static/css/quill.core.scss
  41. 945 0
      h5_web/static/css/quill.snow.scss
  42. BIN
      h5_web/static/icons/code-gray.png
  43. BIN
      h5_web/static/icons/code.png
  44. BIN
      h5_web/static/icons/index-gray.png
  45. BIN
      h5_web/static/icons/index-selected.png
  46. BIN
      h5_web/static/icons/index.png
  47. BIN
      h5_web/static/icons/mine-gray.png
  48. BIN
      h5_web/static/icons/mine.png
  49. BIN
      h5_web/static/img/banner-myBasicTraining.png
  50. BIN
      h5_web/static/img/banner-skillsTraining.png
  51. BIN
      h5_web/static/img/banner-upgrade.png
  52. BIN
      h5_web/static/img/basicTraining-bg.png
  53. BIN
      h5_web/static/img/default-avatar.png
  54. BIN
      h5_web/static/img/empty.png
  55. BIN
      h5_web/static/img/index-service-01.png
  56. BIN
      h5_web/static/img/index-service-02.png
  57. BIN
      h5_web/static/img/index-service-03.png
  58. BIN
      h5_web/static/img/index-service-04.png
  59. BIN
      h5_web/static/img/index-service-05.png
  60. BIN
      h5_web/static/img/index-service-06.png
  61. BIN
      h5_web/static/img/index-service-07.png
  62. BIN
      h5_web/static/img/index-service-08.png
  63. BIN
      h5_web/static/img/major-党务工作.png
  64. BIN
      h5_web/static/img/major-工商企业管理.png
  65. BIN
      h5_web/static/img/major-应用化工技术.png
  66. BIN
      h5_web/static/img/major-建筑工程技术.png
  67. BIN
      h5_web/static/img/major-机电一体化技术.png
  68. BIN
      h5_web/static/img/major-计算机应用技术.png
  69. BIN
      h5_web/static/img/major01.png
  70. BIN
      h5_web/static/img/major02.png
  71. BIN
      h5_web/static/img/major03.png
  72. BIN
      h5_web/static/img/major04.png
  73. BIN
      h5_web/static/img/major05.png
  74. BIN
      h5_web/static/img/major06.png
  75. BIN
      h5_web/static/img/mycode-bg.png
  76. BIN
      h5_web/static/img/skill-01.png
  77. BIN
      h5_web/static/img/skill-02.png
  78. BIN
      h5_web/static/img/skill-03.png
  79. BIN
      h5_web/static/img/skill-04.png
  80. BIN
      h5_web/static/img/skill-05.png
  81. BIN
      h5_web/static/img/skill-06.png
  82. BIN
      h5_web/static/img/skill-农产品食品检验员.png
  83. BIN
      h5_web/static/img/skill-化学检验员.png
  84. BIN
      h5_web/static/img/skill-工业机器人.png
  85. BIN
      h5_web/static/img/skill-工业机器人系统运维员.png
  86. BIN
      h5_web/static/img/skill-机床装调维修工.png
  87. BIN
      h5_web/static/img/skill-汽车维修.png
  88. BIN
      h5_web/static/img/skill-焊接.png
  89. BIN
      h5_web/static/img/skill-电工.png
  90. BIN
      h5_web/static/img/skill-车身修理.png
  91. BIN
      h5_web/static/img/tip.png
  92. 0 0
      h5_web/static/js/jweixin-1.4.0.js
  93. 46 0
      h5_web/static/js/mock.js
  94. 27 0
      h5_web/store/$u.mixin.js
  95. 63 0
      h5_web/store/index.js
  96. 24 0
      h5_web/template.h5.html
  97. 6 0
      h5_web/uni.scss
  98. 6 0
      h5_web/uni_modules/mescroll-uni/changelog.md
  99. 19 0
      h5_web/uni_modules/mescroll-uni/components/mescroll-body/mescroll-body.css
  100. 400 0
      h5_web/uni_modules/mescroll-uni/components/mescroll-body/mescroll-body.vue

+ 24 - 0
h5_web/.gitignore

@@ -0,0 +1,24 @@
+.DS_Store
+node_modules/
+dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+**/*.log
+
+tests/**/coverage/
+tests/e2e/reports
+selenium-debug.log
+
+# Editor directories and files
+.idea
+.vscode
+.hbuilderx
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.local
+
+package-lock.json
+yarn.lock

+ 18 - 0
h5_web/App.vue

@@ -0,0 +1,18 @@
+<script>
+	export default {
+		onLaunch: function() {
+			// console.log('App Launch')
+		},
+		onShow: function() {
+			// console.log('App Show')
+		},
+		onHide: function() {
+			// console.log('App Hide')
+		}
+	}
+</script>
+
+<style lang="scss">
+	/*每个页面公共css */
+	@import "uview-ui/index.scss";
+</style>

+ 8 - 0
h5_web/README.md

@@ -0,0 +1,8 @@
+## 基于uniapp和uView组件开发的h5项目
+
+#### 需安装uView
+##### npm install uview-ui
+
+#### 集成微信支付api (不需要可以删除)
+
+#####  有比较好或者使用的h5组件可以引入

+ 56 - 0
h5_web/common/apiurl.js

@@ -0,0 +1,56 @@
+/*
+	接口统一管理
+*/
+const apiurl = {
+	// 测试
+	testApi: '/test/test',
+	// 手机登录验证码
+	phoneLoginCodeUrl:'/auth/sendSmsCode',
+	// 手机登录验证码验证
+	phoneLoginAuthUrl:'/auth/verifyCode',
+	// 获取条款须知
+	// 首页轮播
+	indexBannerListUrl:'/sysadv/list',
+	getSystermsApi: '/systerms/getByType/',
+	// 首页新闻列表
+	indexNewsList: '/sec/article/door/list',
+	// 新闻详情
+	newsDetailsUrl:'/sec/article/door/info',
+	// 政策法规详情
+	policyNewsDetailsUrl:'/policy/article/door/info',
+	// 提交申请材料 认证
+	veteEducUrl:'/veteEduc',
+	//认证状态查询
+	veteEducCheckUrl:'/veteEduc/check',
+	// 我的详细信息
+	memberinfoUrl:'/memberinfo',
+	// 政策法规列表
+	policyNewsListUrl: '/policy/article/door/list',
+	// 枚举值key查询
+	dictdataUrl: '/sys/dictdata',
+	// 适应性培训课程列表
+	adaptTrainOnlineUrl: '/adaptTrainOnline/list',
+	// 适应性培训课程详情
+	adaptTrainOnlineDetailsUrl: '/adaptTrainOnline/',
+	// 适应性培训学习进度
+	adaptTrainOnlineRateUrl: '/adaptTrainOnline/study',
+	// 上传适应性培训视频播放情况
+	upAdaptTrainOnlineInfoUrl: '/adaptTrainOnline/duration',
+	// 适应性培训线下内容
+	adaptTrainOfflineUrl: '/adaptTrainOffline/new/offline',
+	// 查询线下培训详情
+	adaptTrainIdOfflineUrl: '/adaptTrainOffline/',
+	// 页面点击统计
+	analysisUrl: '/sys/analysis',
+	// 线下培训签到
+	signOfflineUrl: '/adaptTrainOffline/sign',
+	// 根据身份证号读取信息
+	getIdInfoUrl: '/veteEduc/idcard/',
+	// 合作学校分页数据
+	getSchoolListUrl: '/school/list',
+
+}
+
+export {
+	apiurl
+}

+ 21 - 0
h5_web/common/config.js

@@ -0,0 +1,21 @@
+/**
+ * 配置通用 
+ */
+// console.log('process.env.NODE_ENV',process.env.NODE_ENV);
+let base = '';
+if(process.env.NODE_ENV=="development"){
+	base = 'https://wx.hw.hongweisoft.com/veterans/app';
+}else if(process.env.NODE_ENV=="production"){
+	base = 'https://veterwebpro.hw.hongweisoft.com/veterans-prod-api/app';
+}
+// console.log('base',base);
+const config = {
+	wxAppid: '', // 测试wxAppid
+	// baseUrl: 'https://wx.hw.hongweisoft.com/veterans/app' ,// 64服务器地址
+	baseUrl: base ,// 201服务器地址
+	
+	uploadUrl:'https://wx.hw.hongweisoft.com/veterans/file/upload/single/minio'
+}
+export {
+	config
+}

+ 88 - 0
h5_web/common/http.api.js

@@ -0,0 +1,88 @@
+import {
+	apiurl
+} from "./apiurl.js"
+
+// 此处第二个参数vm,就是我们在页面使用的this,你可以通过vm获取vuex等操作,更多内容详见uView对拦截器的介绍部分:
+const install = (Vue, vm) => {
+
+	// 测试
+	let test = (params = {}) => vm.$u.post(apiurl.testApi, params);
+	
+	let getPhoneLoginCode = (params = {}) => vm.$u.post(apiurl.phoneLoginCodeUrl, params);
+	let phoneLoginAuth = (params = {}) => vm.$u.post(apiurl.phoneLoginAuthUrl, {code:params.code},{Authorization:`Bearer ${params.accessToken}`,user_id:params.userId});
+	// 获取自定义富文本内容: 0-包月须知 1-用户服务条款 2-隐私权政策信息 3-收费标准 4-退款温馨提示
+	let getSysterms = (params = {}) => vm.$u.get(apiurl.getSystermsApi + params.termsType);
+	// 首页轮播
+	let getIndexBannerList = (params = {}) => vm.$u.get(apiurl.indexBannerListUrl,params);
+	// 首页新闻列表
+	let getIndexNewsList = (params = {}) => vm.$u.get(apiurl.indexNewsList,params);
+	// 新闻详情
+	let getNewsDetails = (params = {}) => vm.$u.get(apiurl.newsDetailsUrl,params);
+	// 政策法规详情
+	let getPolicyNewsDetails = (params = {}) => vm.$u.get(apiurl.policyNewsDetailsUrl,params);
+	// 军人认证
+	let veteEduc = (params = {}) => vm.$u.post(apiurl.veteEducUrl, params);
+	// 认证状态查询
+	let getVeteEducCheck = (params = {}) => vm.$u.get(apiurl.veteEducCheckUrl,params);
+	//我的认证申请
+	let getVeteEducData = (params = {}) => vm.$u.get(apiurl.veteEducUrl,params);
+	// 我的详细信息
+	let getmemberinfo = (params = {}) => vm.$u.get(apiurl.memberinfoUrl,params);
+	// 首页新闻列表
+	let getpolicyNewsList = (params = {}) => vm.$u.get(apiurl.policyNewsListUrl,params);
+	// 枚举值key查询 key 新闻类型 news_type, 政策法规 policy_type 人员属性 vete_attribute
+	let getDictdataUrl = (params = {}) => vm.$u.get(apiurl.dictdataUrl,params);
+	// 适应性培训课程列表
+	let getAdaptTrainOnline = (params = {}) => vm.$u.get(apiurl.adaptTrainOnlineUrl,params);
+	// 适应性培训课程详情
+	let getAdaptTrainOnlineDetails = (params = {}) => vm.$u.get(apiurl.adaptTrainOnlineDetailsUrl+params.id);
+	// 适应性培训学习进度
+	let getAdaptTrainOnlineRate = (params = {}) => vm.$u.post(apiurl.adaptTrainOnlineRateUrl, params);
+	// 上传适应性培训视频播放情况
+	let upAdaptTrainOnlineInfo = (params = {}) => vm.$u.post(apiurl.upAdaptTrainOnlineInfoUrl, params);
+	// 适应性培训线下内容
+	let getAdaptTrainOffline = (params = {}) => vm.$u.get(apiurl.adaptTrainOfflineUrl,params);
+	// 查询线下培训详情
+	let getAdaptTrainIdOffline = (params = {}) => vm.$u.get(apiurl.adaptTrainIdOfflineUrl+params.id);
+	// 页面点击统计
+	let postAnalysis = (params = {}) => vm.$u.post(apiurl.analysisUrl, params);
+	// 线下培训签到
+	let postSignOffline = (params = {}) => vm.$u.post(apiurl.signOfflineUrl, params);
+	// 根据身份证号读取信息
+	let getIdInfo = (params = {}) => vm.$u.get(apiurl.getIdInfoUrl+params.id);
+	// 合作学校分页数据
+	let getSchoolList = (params = {}) => vm.$u.get(apiurl.getSchoolListUrl, params);
+	
+	
+	// 将各个定义的接口名称,统一放进对象挂载到vm.$u.api(因为vm就是this,也即this.$u.api)下
+	vm.$u.api = {
+		test,
+		getPhoneLoginCode,
+		phoneLoginAuth,
+		getSysterms,
+		getIndexBannerList,
+		getIndexNewsList,
+		getNewsDetails,
+		veteEduc,
+		getVeteEducCheck,
+		getmemberinfo,
+		getpolicyNewsList,
+		getVeteEducData,
+		getPolicyNewsDetails,
+		getDictdataUrl,
+		getAdaptTrainOnline,
+		getAdaptTrainOnlineDetails,
+		getAdaptTrainOnlineRate,
+		upAdaptTrainOnlineInfo,
+		getAdaptTrainOffline,
+		postAnalysis,
+		postSignOffline,
+		getAdaptTrainIdOffline,
+		getIdInfo,
+		getSchoolList
+	};
+}
+
+export default {
+	install
+}

+ 71 - 0
h5_web/common/http.interceptor.js

@@ -0,0 +1,71 @@
+import { config } from '@/common/config.js';
+import store  from '../store/index.js'
+// 这里的vm,就是我们在vue文件里面的this,所以我们能在这里获取vuex的变量,比如存放在里面的token
+// 同时,我们也可以在此使用getApp().globalData,如果你把token放在getApp().globalData的话,也是可以使用的
+const install = (Vue, vm) => {
+	Vue.prototype.$u.http.setConfig({
+		baseUrl: config.baseUrl,
+		// 如果将此值设置为true,拦截回调中将会返回服务端返回的所有数据response,而不是response.data
+		// 设置为true后,就需要在this.$u.http.interceptor.response进行多一次的判断,请打印查看具体值
+		// originalData: true, 
+		// 设置自定义头部content-type
+		// header: {
+		// 	'content-type': 'xxx'
+		// }
+	});
+	// 请求拦截,配置Token等参数
+	Vue.prototype.$u.http.interceptor.request = (config) => {
+		// config.header.Token = 'xxxxxx';
+		// 方式一,存放在vuex的token,假设使用了uView封装的vuex方式,见:https://uviewui.com/components/globalVariable.html
+		// config.header.token = vm.vuex_token;
+		
+		if(vm.vuex_token){config.header.Authorization = `Bearer ${vm.vuex_token}`;}
+		
+		// 方式二,如果没有使用uView封装的vuex方法,那么需要使用$store.state获取
+		// config.header.token = vm.$store.state.token;
+		
+		// 方式三,如果token放在了globalData,通过getApp().globalData获取
+		// config.header.token = getApp().globalData.username;
+		
+		// 方式四,如果token放在了Storage本地存储中,拦截是每次请求都执行的,所以哪怕您重新登录修改了Storage,下一次的请求将会是最新值
+		// const token = uni.getStorageSync('token');
+		// config.header.token = token;
+		// url加时间戳
+		config.url = config.url + '?t=' + Date.now()
+		// 此url参数为this.$u.get(url)中的url值
+		let noTokenList = ['/wechat/h5/user','/client/auth/verifyCode'];
+		if(noTokenList.includes(config.url)) config.header.noToken = true;
+		// console.log('noTokenList.includes(config.url)',noTokenList.includes(config.url));
+		// console.log('config.url',config.url);
+		return config; 
+	}
+	// 响应拦截,判断状态码是否通过
+	Vue.prototype.$u.http.interceptor.response = (res) => {
+		// 如果把originalData设置为了true,这里得到将会是服务器返回的所有的原始数据
+		// 判断可能变成了res.statueCode,或者res.data.code之类的,请打印查看结果
+		// console.log('interceptor res',res);
+		if(res.code == 200) {
+			// 如果把originalData设置为了true,这里return回什么,this.$u.post的then回调中就会得到什么
+			return res;  
+		} else if(res.msg == "令牌不能为空" || res.code == 401){
+			const backUrl = location.href
+			const loginUrl = 'phoneLogin'
+			if (backUrl.indexOf(loginUrl) > 0) {
+				// localStorage.clear()
+			} else {
+				localStorage.setItem('backUrl', location.href)
+				// this.$u.vuex('vuex_hasLogin', false);
+				alert('还未登录,即将跳转登录');
+				uni.navigateTo({
+					url: "/pages/phoneLogin/phoneLogin"
+				});
+				return
+			}
+			
+		}else return res;
+	}
+}
+
+export default {
+	install
+}

+ 1201 - 0
h5_web/components/tki-qrcode/qrcode.js

@@ -0,0 +1,1201 @@
+let QRCode = {};
+(function () {
+    /**
+     * 获取单个字符的utf8编码
+     * unicode BMP平面约65535个字符
+     * @param {num} code
+     * return {array}
+     */
+    function unicodeFormat8(code) {
+        // 1 byte
+        var c0, c1, c2;
+        if (code < 128) {
+            return [code];
+            // 2 bytes
+        } else if (code < 2048) {
+            c0 = 192 + (code >> 6);
+            c1 = 128 + (code & 63);
+            return [c0, c1];
+            // 3 bytes
+        } else {
+            c0 = 224 + (code >> 12);
+            c1 = 128 + (code >> 6 & 63);
+            c2 = 128 + (code & 63);
+            return [c0, c1, c2];
+        }
+    }
+    /**
+     * 获取字符串的utf8编码字节串
+     * @param {string} string
+     * @return {array}
+     */
+    function getUTF8Bytes(string) {
+        var utf8codes = [];
+        for (var i = 0; i < string.length; i++) {
+            var code = string.charCodeAt(i);
+            var utf8 = unicodeFormat8(code);
+            for (var j = 0; j < utf8.length; j++) {
+                utf8codes.push(utf8[j]);
+            }
+        }
+        return utf8codes;
+    }
+    /**
+     * 二维码算法实现
+     * @param {string} data              要编码的信息字符串
+     * @param {num} errorCorrectLevel 纠错等级
+     */
+    function QRCodeAlg(data, errorCorrectLevel) {
+        this.typeNumber = -1; //版本
+        this.errorCorrectLevel = errorCorrectLevel;
+        this.modules = null; //二维矩阵,存放最终结果
+        this.moduleCount = 0; //矩阵大小
+        this.dataCache = null; //数据缓存
+        this.rsBlocks = null; //版本数据信息
+        this.totalDataCount = -1; //可使用的数据量
+        this.data = data;
+        this.utf8bytes = getUTF8Bytes(data);
+        this.make();
+    }
+    QRCodeAlg.prototype = {
+        constructor: QRCodeAlg,
+        /**
+         * 获取二维码矩阵大小
+         * @return {num} 矩阵大小
+         */
+        getModuleCount: function () {
+            return this.moduleCount;
+        },
+        /**
+         * 编码
+         */
+        make: function () {
+            this.getRightType();
+            this.dataCache = this.createData();
+            this.createQrcode();
+        },
+        /**
+         * 设置二位矩阵功能图形
+         * @param  {bool} test 表示是否在寻找最好掩膜阶段
+         * @param  {num} maskPattern 掩膜的版本
+         */
+        makeImpl: function (maskPattern) {
+            this.moduleCount = this.typeNumber * 4 + 17;
+            this.modules = new Array(this.moduleCount);
+            for (var row = 0; row < this.moduleCount; row++) {
+                this.modules[row] = new Array(this.moduleCount);
+            }
+            this.setupPositionProbePattern(0, 0);
+            this.setupPositionProbePattern(this.moduleCount - 7, 0);
+            this.setupPositionProbePattern(0, this.moduleCount - 7);
+            this.setupPositionAdjustPattern();
+            this.setupTimingPattern();
+            this.setupTypeInfo(true, maskPattern);
+            if (this.typeNumber >= 7) {
+                this.setupTypeNumber(true);
+            }
+            this.mapData(this.dataCache, maskPattern);
+        },
+        /**
+         * 设置二维码的位置探测图形
+         * @param  {num} row 探测图形的中心横坐标
+         * @param  {num} col 探测图形的中心纵坐标
+         */
+        setupPositionProbePattern: function (row, col) {
+            for (var r = -1; r <= 7; r++) {
+                if (row + r <= -1 || this.moduleCount <= row + r) continue;
+                for (var c = -1; c <= 7; c++) {
+                    if (col + c <= -1 || this.moduleCount <= col + c) continue;
+                    if ((0 <= r && r <= 6 && (c == 0 || c == 6)) || (0 <= c && c <= 6 && (r == 0 || r == 6)) || (2 <= r && r <= 4 && 2 <= c && c <= 4)) {
+                        this.modules[row + r][col + c] = true;
+                    } else {
+                        this.modules[row + r][col + c] = false;
+                    }
+                }
+            }
+        },
+        /**
+         * 创建二维码
+         * @return {[type]} [description]
+         */
+        createQrcode: function () {
+            var minLostPoint = 0;
+            var pattern = 0;
+            var bestModules = null;
+            for (var i = 0; i < 8; i++) {
+                this.makeImpl(i);
+                var lostPoint = QRUtil.getLostPoint(this);
+                if (i == 0 || minLostPoint > lostPoint) {
+                    minLostPoint = lostPoint;
+                    pattern = i;
+                    bestModules = this.modules;
+                }
+            }
+            this.modules = bestModules;
+            this.setupTypeInfo(false, pattern);
+            if (this.typeNumber >= 7) {
+                this.setupTypeNumber(false);
+            }
+        },
+        /**
+         * 设置定位图形
+         * @return {[type]} [description]
+         */
+        setupTimingPattern: function () {
+            for (var r = 8; r < this.moduleCount - 8; r++) {
+                if (this.modules[r][6] != null) {
+                    continue;
+                }
+                this.modules[r][6] = (r % 2 == 0);
+                if (this.modules[6][r] != null) {
+                    continue;
+                }
+                this.modules[6][r] = (r % 2 == 0);
+            }
+        },
+        /**
+         * 设置矫正图形
+         * @return {[type]} [description]
+         */
+        setupPositionAdjustPattern: function () {
+            var pos = QRUtil.getPatternPosition(this.typeNumber);
+            for (var i = 0; i < pos.length; i++) {
+                for (var j = 0; j < pos.length; j++) {
+                    var row = pos[i];
+                    var col = pos[j];
+                    if (this.modules[row][col] != null) {
+                        continue;
+                    }
+                    for (var r = -2; r <= 2; r++) {
+                        for (var c = -2; c <= 2; c++) {
+                            if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) {
+                                this.modules[row + r][col + c] = true;
+                            } else {
+                                this.modules[row + r][col + c] = false;
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        /**
+         * 设置版本信息(7以上版本才有)
+         * @param  {bool} test 是否处于判断最佳掩膜阶段
+         * @return {[type]}      [description]
+         */
+        setupTypeNumber: function (test) {
+            var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
+            for (var i = 0; i < 18; i++) {
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
+                this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
+            }
+        },
+        /**
+         * 设置格式信息(纠错等级和掩膜版本)
+         * @param  {bool} test
+         * @param  {num} maskPattern 掩膜版本
+         * @return {}
+         */
+        setupTypeInfo: function (test, maskPattern) {
+            var data = (QRErrorCorrectLevel[this.errorCorrectLevel] << 3) | maskPattern;
+            var bits = QRUtil.getBCHTypeInfo(data);
+            // vertical
+            for (var i = 0; i < 15; i++) {
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                if (i < 6) {
+                    this.modules[i][8] = mod;
+                } else if (i < 8) {
+                    this.modules[i + 1][8] = mod;
+                } else {
+                    this.modules[this.moduleCount - 15 + i][8] = mod;
+                }
+                // horizontal
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                if (i < 8) {
+                    this.modules[8][this.moduleCount - i - 1] = mod;
+                } else if (i < 9) {
+                    this.modules[8][15 - i - 1 + 1] = mod;
+                } else {
+                    this.modules[8][15 - i - 1] = mod;
+                }
+            }
+            // fixed module
+            this.modules[this.moduleCount - 8][8] = (!test);
+        },
+        /**
+         * 数据编码
+         * @return {[type]} [description]
+         */
+        createData: function () {
+            var buffer = new QRBitBuffer();
+            var lengthBits = this.typeNumber > 9 ? 16 : 8;
+            buffer.put(4, 4); //添加模式
+            buffer.put(this.utf8bytes.length, lengthBits);
+            for (var i = 0, l = this.utf8bytes.length; i < l; i++) {
+                buffer.put(this.utf8bytes[i], 8);
+            }
+            if (buffer.length + 4 <= this.totalDataCount * 8) {
+                buffer.put(0, 4);
+            }
+            // padding
+            while (buffer.length % 8 != 0) {
+                buffer.putBit(false);
+            }
+            // padding
+            while (true) {
+                if (buffer.length >= this.totalDataCount * 8) {
+                    break;
+                }
+                buffer.put(QRCodeAlg.PAD0, 8);
+                if (buffer.length >= this.totalDataCount * 8) {
+                    break;
+                }
+                buffer.put(QRCodeAlg.PAD1, 8);
+            }
+            return this.createBytes(buffer);
+        },
+        /**
+         * 纠错码编码
+         * @param  {buffer} buffer 数据编码
+         * @return {[type]}
+         */
+        createBytes: function (buffer) {
+            var offset = 0;
+            var maxDcCount = 0;
+            var maxEcCount = 0;
+            var length = this.rsBlock.length / 3;
+            var rsBlocks = new Array();
+            for (var i = 0; i < length; i++) {
+                var count = this.rsBlock[i * 3 + 0];
+                var totalCount = this.rsBlock[i * 3 + 1];
+                var dataCount = this.rsBlock[i * 3 + 2];
+                for (var j = 0; j < count; j++) {
+                    rsBlocks.push([dataCount, totalCount]);
+                }
+            }
+            var dcdata = new Array(rsBlocks.length);
+            var ecdata = new Array(rsBlocks.length);
+            for (var r = 0; r < rsBlocks.length; r++) {
+                var dcCount = rsBlocks[r][0];
+                var ecCount = rsBlocks[r][1] - dcCount;
+                maxDcCount = Math.max(maxDcCount, dcCount);
+                maxEcCount = Math.max(maxEcCount, ecCount);
+                dcdata[r] = new Array(dcCount);
+                for (var i = 0; i < dcdata[r].length; i++) {
+                    dcdata[r][i] = 0xff & buffer.buffer[i + offset];
+                }
+                offset += dcCount;
+                var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
+                var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
+                var modPoly = rawPoly.mod(rsPoly);
+                ecdata[r] = new Array(rsPoly.getLength() - 1);
+                for (var i = 0; i < ecdata[r].length; i++) {
+                    var modIndex = i + modPoly.getLength() - ecdata[r].length;
+                    ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0;
+                }
+            }
+            var data = new Array(this.totalDataCount);
+            var index = 0;
+            for (var i = 0; i < maxDcCount; i++) {
+                for (var r = 0; r < rsBlocks.length; r++) {
+                    if (i < dcdata[r].length) {
+                        data[index++] = dcdata[r][i];
+                    }
+                }
+            }
+            for (var i = 0; i < maxEcCount; i++) {
+                for (var r = 0; r < rsBlocks.length; r++) {
+                    if (i < ecdata[r].length) {
+                        data[index++] = ecdata[r][i];
+                    }
+                }
+            }
+            return data;
+
+        },
+        /**
+         * 布置模块,构建最终信息
+         * @param  {} data
+         * @param  {} maskPattern
+         * @return {}
+         */
+        mapData: function (data, maskPattern) {
+            var inc = -1;
+            var row = this.moduleCount - 1;
+            var bitIndex = 7;
+            var byteIndex = 0;
+            for (var col = this.moduleCount - 1; col > 0; col -= 2) {
+                if (col == 6) col--;
+                while (true) {
+                    for (var c = 0; c < 2; c++) {
+                        if (this.modules[row][col - c] == null) {
+                            var dark = false;
+                            if (byteIndex < data.length) {
+                                dark = (((data[byteIndex] >>> bitIndex) & 1) == 1);
+                            }
+                            var mask = QRUtil.getMask(maskPattern, row, col - c);
+                            if (mask) {
+                                dark = !dark;
+                            }
+                            this.modules[row][col - c] = dark;
+                            bitIndex--;
+                            if (bitIndex == -1) {
+                                byteIndex++;
+                                bitIndex = 7;
+                            }
+                        }
+                    }
+                    row += inc;
+                    if (row < 0 || this.moduleCount <= row) {
+                        row -= inc;
+                        inc = -inc;
+                        break;
+                    }
+                }
+            }
+        }
+    };
+    /**
+     * 填充字段
+     */
+    QRCodeAlg.PAD0 = 0xEC;
+    QRCodeAlg.PAD1 = 0x11;
+    //---------------------------------------------------------------------
+    // 纠错等级对应的编码
+    //---------------------------------------------------------------------
+    var QRErrorCorrectLevel = [1, 0, 3, 2];
+    //---------------------------------------------------------------------
+    // 掩膜版本
+    //---------------------------------------------------------------------
+    var QRMaskPattern = {
+        PATTERN000: 0,
+        PATTERN001: 1,
+        PATTERN010: 2,
+        PATTERN011: 3,
+        PATTERN100: 4,
+        PATTERN101: 5,
+        PATTERN110: 6,
+        PATTERN111: 7
+    };
+    //---------------------------------------------------------------------
+    // 工具类
+    //---------------------------------------------------------------------
+    var QRUtil = {
+        /*
+        每个版本矫正图形的位置
+         */
+        PATTERN_POSITION_TABLE: [
+            [],
+            [6, 18],
+            [6, 22],
+            [6, 26],
+            [6, 30],
+            [6, 34],
+            [6, 22, 38],
+            [6, 24, 42],
+            [6, 26, 46],
+            [6, 28, 50],
+            [6, 30, 54],
+            [6, 32, 58],
+            [6, 34, 62],
+            [6, 26, 46, 66],
+            [6, 26, 48, 70],
+            [6, 26, 50, 74],
+            [6, 30, 54, 78],
+            [6, 30, 56, 82],
+            [6, 30, 58, 86],
+            [6, 34, 62, 90],
+            [6, 28, 50, 72, 94],
+            [6, 26, 50, 74, 98],
+            [6, 30, 54, 78, 102],
+            [6, 28, 54, 80, 106],
+            [6, 32, 58, 84, 110],
+            [6, 30, 58, 86, 114],
+            [6, 34, 62, 90, 118],
+            [6, 26, 50, 74, 98, 122],
+            [6, 30, 54, 78, 102, 126],
+            [6, 26, 52, 78, 104, 130],
+            [6, 30, 56, 82, 108, 134],
+            [6, 34, 60, 86, 112, 138],
+            [6, 30, 58, 86, 114, 142],
+            [6, 34, 62, 90, 118, 146],
+            [6, 30, 54, 78, 102, 126, 150],
+            [6, 24, 50, 76, 102, 128, 154],
+            [6, 28, 54, 80, 106, 132, 158],
+            [6, 32, 58, 84, 110, 136, 162],
+            [6, 26, 54, 82, 110, 138, 166],
+            [6, 30, 58, 86, 114, 142, 170]
+        ],
+        G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
+        G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
+        G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
+        /*
+        BCH编码格式信息
+         */
+        getBCHTypeInfo: function (data) {
+            var d = data << 10;
+            while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
+                d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15)));
+            }
+            return ((data << 10) | d) ^ QRUtil.G15_MASK;
+        },
+        /*
+        BCH编码版本信息
+         */
+        getBCHTypeNumber: function (data) {
+            var d = data << 12;
+            while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
+                d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18)));
+            }
+            return (data << 12) | d;
+        },
+        /*
+        获取BCH位信息
+         */
+        getBCHDigit: function (data) {
+            var digit = 0;
+            while (data != 0) {
+                digit++;
+                data >>>= 1;
+            }
+            return digit;
+        },
+        /*
+        获取版本对应的矫正图形位置
+         */
+        getPatternPosition: function (typeNumber) {
+            return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
+        },
+        /*
+        掩膜算法
+         */
+        getMask: function (maskPattern, i, j) {
+            switch (maskPattern) {
+                case QRMaskPattern.PATTERN000:
+                    return (i + j) % 2 == 0;
+                case QRMaskPattern.PATTERN001:
+                    return i % 2 == 0;
+                case QRMaskPattern.PATTERN010:
+                    return j % 3 == 0;
+                case QRMaskPattern.PATTERN011:
+                    return (i + j) % 3 == 0;
+                case QRMaskPattern.PATTERN100:
+                    return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
+                case QRMaskPattern.PATTERN101:
+                    return (i * j) % 2 + (i * j) % 3 == 0;
+                case QRMaskPattern.PATTERN110:
+                    return ((i * j) % 2 + (i * j) % 3) % 2 == 0;
+                case QRMaskPattern.PATTERN111:
+                    return ((i * j) % 3 + (i + j) % 2) % 2 == 0;
+                default:
+                    throw new Error("bad maskPattern:" + maskPattern);
+            }
+        },
+        /*
+        获取RS的纠错多项式
+         */
+        getErrorCorrectPolynomial: function (errorCorrectLength) {
+            var a = new QRPolynomial([1], 0);
+            for (var i = 0; i < errorCorrectLength; i++) {
+                a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
+            }
+            return a;
+        },
+        /*
+        获取评价
+         */
+        getLostPoint: function (qrCode) {
+            var moduleCount = qrCode.getModuleCount(),
+                lostPoint = 0,
+                darkCount = 0;
+            for (var row = 0; row < moduleCount; row++) {
+                var sameCount = 0;
+                var head = qrCode.modules[row][0];
+                for (var col = 0; col < moduleCount; col++) {
+                    var current = qrCode.modules[row][col];
+                    //level 3 评价
+                    if (col < moduleCount - 6) {
+                        if (current && !qrCode.modules[row][col + 1] && qrCode.modules[row][col + 2] && qrCode.modules[row][col + 3] && qrCode.modules[row][col + 4] && !qrCode.modules[row][col + 5] && qrCode.modules[row][col + 6]) {
+                            if (col < moduleCount - 10) {
+                                if (qrCode.modules[row][col + 7] && qrCode.modules[row][col + 8] && qrCode.modules[row][col + 9] && qrCode.modules[row][col + 10]) {
+                                    lostPoint += 40;
+                                }
+                            } else if (col > 3) {
+                                if (qrCode.modules[row][col - 1] && qrCode.modules[row][col - 2] && qrCode.modules[row][col - 3] && qrCode.modules[row][col - 4]) {
+                                    lostPoint += 40;
+                                }
+                            }
+                        }
+                    }
+                    //level 2 评价
+                    if ((row < moduleCount - 1) && (col < moduleCount - 1)) {
+                        var count = 0;
+                        if (current) count++;
+                        if (qrCode.modules[row + 1][col]) count++;
+                        if (qrCode.modules[row][col + 1]) count++;
+                        if (qrCode.modules[row + 1][col + 1]) count++;
+                        if (count == 0 || count == 4) {
+                            lostPoint += 3;
+                        }
+                    }
+                    //level 1 评价
+                    if (head ^ current) {
+                        sameCount++;
+                    } else {
+                        head = current;
+                        if (sameCount >= 5) {
+                            lostPoint += (3 + sameCount - 5);
+                        }
+                        sameCount = 1;
+                    }
+                    //level 4 评价
+                    if (current) {
+                        darkCount++;
+                    }
+                }
+            }
+            for (var col = 0; col < moduleCount; col++) {
+                var sameCount = 0;
+                var head = qrCode.modules[0][col];
+                for (var row = 0; row < moduleCount; row++) {
+                    var current = qrCode.modules[row][col];
+                    //level 3 评价
+                    if (row < moduleCount - 6) {
+                        if (current && !qrCode.modules[row + 1][col] && qrCode.modules[row + 2][col] && qrCode.modules[row + 3][col] && qrCode.modules[row + 4][col] && !qrCode.modules[row + 5][col] && qrCode.modules[row + 6][col]) {
+                            if (row < moduleCount - 10) {
+                                if (qrCode.modules[row + 7][col] && qrCode.modules[row + 8][col] && qrCode.modules[row + 9][col] && qrCode.modules[row + 10][col]) {
+                                    lostPoint += 40;
+                                }
+                            } else if (row > 3) {
+                                if (qrCode.modules[row - 1][col] && qrCode.modules[row - 2][col] && qrCode.modules[row - 3][col] && qrCode.modules[row - 4][col]) {
+                                    lostPoint += 40;
+                                }
+                            }
+                        }
+                    }
+                    //level 1 评价
+                    if (head ^ current) {
+                        sameCount++;
+                    } else {
+                        head = current;
+                        if (sameCount >= 5) {
+                            lostPoint += (3 + sameCount - 5);
+                        }
+                        sameCount = 1;
+                    }
+                }
+            }
+            // LEVEL4
+            var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
+            lostPoint += ratio * 10;
+            return lostPoint;
+        }
+
+    };
+    //---------------------------------------------------------------------
+    // QRMath使用的数学工具
+    //---------------------------------------------------------------------
+    var QRMath = {
+        /*
+        将n转化为a^m
+         */
+        glog: function (n) {
+            if (n < 1) {
+                throw new Error("glog(" + n + ")");
+            }
+            return QRMath.LOG_TABLE[n];
+        },
+        /*
+        将a^m转化为n
+         */
+        gexp: function (n) {
+            while (n < 0) {
+                n += 255;
+            }
+            while (n >= 256) {
+                n -= 255;
+            }
+            return QRMath.EXP_TABLE[n];
+        },
+        EXP_TABLE: new Array(256),
+        LOG_TABLE: new Array(256)
+
+    };
+    for (var i = 0; i < 8; i++) {
+        QRMath.EXP_TABLE[i] = 1 << i;
+    }
+    for (var i = 8; i < 256; i++) {
+        QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
+    }
+    for (var i = 0; i < 255; i++) {
+        QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
+    }
+    //---------------------------------------------------------------------
+    // QRPolynomial 多项式
+    //---------------------------------------------------------------------
+    /**
+     * 多项式类
+     * @param {Array} num   系数
+     * @param {num} shift a^shift
+     */
+    function QRPolynomial(num, shift) {
+        if (num.length == undefined) {
+            throw new Error(num.length + "/" + shift);
+        }
+        var offset = 0;
+        while (offset < num.length && num[offset] == 0) {
+            offset++;
+        }
+        this.num = new Array(num.length - offset + shift);
+        for (var i = 0; i < num.length - offset; i++) {
+            this.num[i] = num[i + offset];
+        }
+    }
+    QRPolynomial.prototype = {
+        get: function (index) {
+            return this.num[index];
+        },
+        getLength: function () {
+            return this.num.length;
+        },
+        /**
+         * 多项式乘法
+         * @param  {QRPolynomial} e 被乘多项式
+         * @return {[type]}   [description]
+         */
+        multiply: function (e) {
+            var num = new Array(this.getLength() + e.getLength() - 1);
+            for (var i = 0; i < this.getLength(); i++) {
+                for (var j = 0; j < e.getLength(); j++) {
+                    num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
+                }
+            }
+            return new QRPolynomial(num, 0);
+        },
+        /**
+         * 多项式模运算
+         * @param  {QRPolynomial} e 模多项式
+         * @return {}
+         */
+        mod: function (e) {
+            var tl = this.getLength(),
+                el = e.getLength();
+            if (tl - el < 0) {
+                return this;
+            }
+            var num = new Array(tl);
+            for (var i = 0; i < tl; i++) {
+                num[i] = this.get(i);
+            }
+            while (num.length >= el) {
+                var ratio = QRMath.glog(num[0]) - QRMath.glog(e.get(0));
+
+                for (var i = 0; i < e.getLength(); i++) {
+                    num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
+                }
+                while (num[0] == 0) {
+                    num.shift();
+                }
+            }
+            return new QRPolynomial(num, 0);
+        }
+    };
+
+    //---------------------------------------------------------------------
+    // RS_BLOCK_TABLE
+    //---------------------------------------------------------------------
+    /*
+    二维码各个版本信息[块数, 每块中的数据块数, 每块中的信息块数]
+     */
+    var RS_BLOCK_TABLE = [
+        // L
+        // M
+        // Q
+        // H
+        // 1
+        [1, 26, 19],
+        [1, 26, 16],
+        [1, 26, 13],
+        [1, 26, 9],
+
+        // 2
+        [1, 44, 34],
+        [1, 44, 28],
+        [1, 44, 22],
+        [1, 44, 16],
+
+        // 3
+        [1, 70, 55],
+        [1, 70, 44],
+        [2, 35, 17],
+        [2, 35, 13],
+
+        // 4
+        [1, 100, 80],
+        [2, 50, 32],
+        [2, 50, 24],
+        [4, 25, 9],
+
+        // 5
+        [1, 134, 108],
+        [2, 67, 43],
+        [2, 33, 15, 2, 34, 16],
+        [2, 33, 11, 2, 34, 12],
+
+        // 6
+        [2, 86, 68],
+        [4, 43, 27],
+        [4, 43, 19],
+        [4, 43, 15],
+
+        // 7
+        [2, 98, 78],
+        [4, 49, 31],
+        [2, 32, 14, 4, 33, 15],
+        [4, 39, 13, 1, 40, 14],
+
+        // 8
+        [2, 121, 97],
+        [2, 60, 38, 2, 61, 39],
+        [4, 40, 18, 2, 41, 19],
+        [4, 40, 14, 2, 41, 15],
+
+        // 9
+        [2, 146, 116],
+        [3, 58, 36, 2, 59, 37],
+        [4, 36, 16, 4, 37, 17],
+        [4, 36, 12, 4, 37, 13],
+
+        // 10
+        [2, 86, 68, 2, 87, 69],
+        [4, 69, 43, 1, 70, 44],
+        [6, 43, 19, 2, 44, 20],
+        [6, 43, 15, 2, 44, 16],
+
+        // 11
+        [4, 101, 81],
+        [1, 80, 50, 4, 81, 51],
+        [4, 50, 22, 4, 51, 23],
+        [3, 36, 12, 8, 37, 13],
+
+        // 12
+        [2, 116, 92, 2, 117, 93],
+        [6, 58, 36, 2, 59, 37],
+        [4, 46, 20, 6, 47, 21],
+        [7, 42, 14, 4, 43, 15],
+
+        // 13
+        [4, 133, 107],
+        [8, 59, 37, 1, 60, 38],
+        [8, 44, 20, 4, 45, 21],
+        [12, 33, 11, 4, 34, 12],
+
+        // 14
+        [3, 145, 115, 1, 146, 116],
+        [4, 64, 40, 5, 65, 41],
+        [11, 36, 16, 5, 37, 17],
+        [11, 36, 12, 5, 37, 13],
+
+        // 15
+        [5, 109, 87, 1, 110, 88],
+        [5, 65, 41, 5, 66, 42],
+        [5, 54, 24, 7, 55, 25],
+        [11, 36, 12],
+
+        // 16
+        [5, 122, 98, 1, 123, 99],
+        [7, 73, 45, 3, 74, 46],
+        [15, 43, 19, 2, 44, 20],
+        [3, 45, 15, 13, 46, 16],
+
+        // 17
+        [1, 135, 107, 5, 136, 108],
+        [10, 74, 46, 1, 75, 47],
+        [1, 50, 22, 15, 51, 23],
+        [2, 42, 14, 17, 43, 15],
+
+        // 18
+        [5, 150, 120, 1, 151, 121],
+        [9, 69, 43, 4, 70, 44],
+        [17, 50, 22, 1, 51, 23],
+        [2, 42, 14, 19, 43, 15],
+
+        // 19
+        [3, 141, 113, 4, 142, 114],
+        [3, 70, 44, 11, 71, 45],
+        [17, 47, 21, 4, 48, 22],
+        [9, 39, 13, 16, 40, 14],
+
+        // 20
+        [3, 135, 107, 5, 136, 108],
+        [3, 67, 41, 13, 68, 42],
+        [15, 54, 24, 5, 55, 25],
+        [15, 43, 15, 10, 44, 16],
+
+        // 21
+        [4, 144, 116, 4, 145, 117],
+        [17, 68, 42],
+        [17, 50, 22, 6, 51, 23],
+        [19, 46, 16, 6, 47, 17],
+
+        // 22
+        [2, 139, 111, 7, 140, 112],
+        [17, 74, 46],
+        [7, 54, 24, 16, 55, 25],
+        [34, 37, 13],
+
+        // 23
+        [4, 151, 121, 5, 152, 122],
+        [4, 75, 47, 14, 76, 48],
+        [11, 54, 24, 14, 55, 25],
+        [16, 45, 15, 14, 46, 16],
+
+        // 24
+        [6, 147, 117, 4, 148, 118],
+        [6, 73, 45, 14, 74, 46],
+        [11, 54, 24, 16, 55, 25],
+        [30, 46, 16, 2, 47, 17],
+
+        // 25
+        [8, 132, 106, 4, 133, 107],
+        [8, 75, 47, 13, 76, 48],
+        [7, 54, 24, 22, 55, 25],
+        [22, 45, 15, 13, 46, 16],
+
+        // 26
+        [10, 142, 114, 2, 143, 115],
+        [19, 74, 46, 4, 75, 47],
+        [28, 50, 22, 6, 51, 23],
+        [33, 46, 16, 4, 47, 17],
+
+        // 27
+        [8, 152, 122, 4, 153, 123],
+        [22, 73, 45, 3, 74, 46],
+        [8, 53, 23, 26, 54, 24],
+        [12, 45, 15, 28, 46, 16],
+
+        // 28
+        [3, 147, 117, 10, 148, 118],
+        [3, 73, 45, 23, 74, 46],
+        [4, 54, 24, 31, 55, 25],
+        [11, 45, 15, 31, 46, 16],
+
+        // 29
+        [7, 146, 116, 7, 147, 117],
+        [21, 73, 45, 7, 74, 46],
+        [1, 53, 23, 37, 54, 24],
+        [19, 45, 15, 26, 46, 16],
+
+        // 30
+        [5, 145, 115, 10, 146, 116],
+        [19, 75, 47, 10, 76, 48],
+        [15, 54, 24, 25, 55, 25],
+        [23, 45, 15, 25, 46, 16],
+
+        // 31
+        [13, 145, 115, 3, 146, 116],
+        [2, 74, 46, 29, 75, 47],
+        [42, 54, 24, 1, 55, 25],
+        [23, 45, 15, 28, 46, 16],
+
+        // 32
+        [17, 145, 115],
+        [10, 74, 46, 23, 75, 47],
+        [10, 54, 24, 35, 55, 25],
+        [19, 45, 15, 35, 46, 16],
+
+        // 33
+        [17, 145, 115, 1, 146, 116],
+        [14, 74, 46, 21, 75, 47],
+        [29, 54, 24, 19, 55, 25],
+        [11, 45, 15, 46, 46, 16],
+
+        // 34
+        [13, 145, 115, 6, 146, 116],
+        [14, 74, 46, 23, 75, 47],
+        [44, 54, 24, 7, 55, 25],
+        [59, 46, 16, 1, 47, 17],
+
+        // 35
+        [12, 151, 121, 7, 152, 122],
+        [12, 75, 47, 26, 76, 48],
+        [39, 54, 24, 14, 55, 25],
+        [22, 45, 15, 41, 46, 16],
+
+        // 36
+        [6, 151, 121, 14, 152, 122],
+        [6, 75, 47, 34, 76, 48],
+        [46, 54, 24, 10, 55, 25],
+        [2, 45, 15, 64, 46, 16],
+
+        // 37
+        [17, 152, 122, 4, 153, 123],
+        [29, 74, 46, 14, 75, 47],
+        [49, 54, 24, 10, 55, 25],
+        [24, 45, 15, 46, 46, 16],
+
+        // 38
+        [4, 152, 122, 18, 153, 123],
+        [13, 74, 46, 32, 75, 47],
+        [48, 54, 24, 14, 55, 25],
+        [42, 45, 15, 32, 46, 16],
+
+        // 39
+        [20, 147, 117, 4, 148, 118],
+        [40, 75, 47, 7, 76, 48],
+        [43, 54, 24, 22, 55, 25],
+        [10, 45, 15, 67, 46, 16],
+
+        // 40
+        [19, 148, 118, 6, 149, 119],
+        [18, 75, 47, 31, 76, 48],
+        [34, 54, 24, 34, 55, 25],
+        [20, 45, 15, 61, 46, 16]
+    ];
+
+    /**
+     * 根据数据获取对应版本
+     * @return {[type]} [description]
+     */
+    QRCodeAlg.prototype.getRightType = function () {
+        for (var typeNumber = 1; typeNumber < 41; typeNumber++) {
+            var rsBlock = RS_BLOCK_TABLE[(typeNumber - 1) * 4 + this.errorCorrectLevel];
+            if (rsBlock == undefined) {
+                throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + this.errorCorrectLevel);
+            }
+            var length = rsBlock.length / 3;
+            var totalDataCount = 0;
+            for (var i = 0; i < length; i++) {
+                var count = rsBlock[i * 3 + 0];
+                var dataCount = rsBlock[i * 3 + 2];
+                totalDataCount += dataCount * count;
+            }
+            var lengthBytes = typeNumber > 9 ? 2 : 1;
+            if (this.utf8bytes.length + lengthBytes < totalDataCount || typeNumber == 40) {
+                this.typeNumber = typeNumber;
+                this.rsBlock = rsBlock;
+                this.totalDataCount = totalDataCount;
+                break;
+            }
+        }
+    };
+
+    //---------------------------------------------------------------------
+    // QRBitBuffer
+    //---------------------------------------------------------------------
+    function QRBitBuffer() {
+        this.buffer = new Array();
+        this.length = 0;
+    }
+    QRBitBuffer.prototype = {
+        get: function (index) {
+            var bufIndex = Math.floor(index / 8);
+            return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1);
+        },
+        put: function (num, length) {
+            for (var i = 0; i < length; i++) {
+                this.putBit(((num >>> (length - i - 1)) & 1));
+            }
+        },
+        putBit: function (bit) {
+            var bufIndex = Math.floor(this.length / 8);
+            if (this.buffer.length <= bufIndex) {
+                this.buffer.push(0);
+            }
+            if (bit) {
+                this.buffer[bufIndex] |= (0x80 >>> (this.length % 8));
+            }
+            this.length++;
+        }
+    };
+
+
+
+    // xzedit
+    let qrcodeAlgObjCache = [];
+    /**
+     * 二维码构造函数,主要用于绘制
+     * @param  {参数列表} opt 传递参数
+     * @return {}
+     */
+    QRCode = function (opt) {
+        //设置默认参数
+        this.options = {
+            text: '',
+            size: 256,
+            correctLevel: 3,
+            background: '#ffffff',
+            foreground: '#000000',
+            pdground: '#000000',
+            image: '',
+            imageSize: 30,
+            canvasId: opt.canvasId,
+            context: opt.context,
+            usingComponents: opt.usingComponents,
+            showLoading: opt.showLoading,
+            loadingText: opt.loadingText,
+        };
+        if (typeof opt === 'string') { // 只编码ASCII字符串
+            opt = {
+                text: opt
+            };
+        }
+        if (opt) {
+            for (var i in opt) {
+                this.options[i] = opt[i];
+            }
+        }
+        //使用QRCodeAlg创建二维码结构
+        var qrCodeAlg = null;
+        for (var i = 0, l = qrcodeAlgObjCache.length; i < l; i++) {
+            if (qrcodeAlgObjCache[i].text == this.options.text && qrcodeAlgObjCache[i].text.correctLevel == this.options.correctLevel) {
+                qrCodeAlg = qrcodeAlgObjCache[i].obj;
+                break;
+            }
+        }
+        if (i == l) {
+            qrCodeAlg = new QRCodeAlg(this.options.text, this.options.correctLevel);
+            qrcodeAlgObjCache.push({
+                text: this.options.text,
+                correctLevel: this.options.correctLevel,
+                obj: qrCodeAlg
+            });
+        }
+        /**
+         * 计算矩阵点的前景色
+         * @param {Obj} config
+         * @param {Number} config.row 点x坐标
+         * @param {Number} config.col 点y坐标
+         * @param {Number} config.count 矩阵大小
+         * @param {Number} config.options 组件的options
+         * @return {String}
+         */
+        let getForeGround = function (config) {
+            var options = config.options;
+            if (options.pdground && (
+                (config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5) ||
+                (config.row > (config.count - 6) && config.row < (config.count - 2) && config.col > 1 && config.col < 5) ||
+                (config.row > 1 && config.row < 5 && config.col > (config.count - 6) && config.col < (config.count - 2))
+            )) {
+                return options.pdground;
+            }
+            return options.foreground;
+        }
+        // 创建canvas
+        let createCanvas = function (options) {
+            if (options.showLoading) {
+                uni.showLoading({
+                    title: options.loadingText,
+                    mask: true
+                });
+            }
+            var ctx = uni.createCanvasContext(options.canvasId, options.context);
+            var count = qrCodeAlg.getModuleCount();
+            var ratioSize = options.size;
+            var ratioImgSize = options.imageSize;
+            //计算每个点的长宽
+            var tileW = (ratioSize / count).toPrecision(4);
+            var tileH = (ratioSize / count).toPrecision(4);
+            //绘制
+            for (var row = 0; row < count; row++) {
+                for (var col = 0; col < count; col++) {
+                    var w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW));
+                    var h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW));
+                    var foreground = getForeGround({
+                        row: row,
+                        col: col,
+                        count: count,
+                        options: options
+                    });
+                    ctx.setFillStyle(qrCodeAlg.modules[row][col] ? foreground : options.background);
+                    ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h);
+                }
+            }
+            if (options.image) {
+                var x = Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
+                var y = Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
+                drawRoundedRect(ctx, x, y, ratioImgSize, ratioImgSize, 2, 6, true, true)
+                ctx.drawImage(options.image, x, y, ratioImgSize, ratioImgSize);
+                // 画圆角矩形
+                function drawRoundedRect(ctxi, x, y, width, height, r, lineWidth, fill, stroke) {
+                    ctxi.setLineWidth(lineWidth);
+                    ctxi.setFillStyle(options.background);
+                    ctxi.setStrokeStyle(options.background);
+                    ctxi.beginPath(); // draw top and top right corner 
+                    ctxi.moveTo(x + r, y);
+                    ctxi.arcTo(x + width, y, x + width, y + r, r); // draw right side and bottom right corner 
+                    ctxi.arcTo(x + width, y + height, x + width - r, y + height, r); // draw bottom and bottom left corner 
+                    ctxi.arcTo(x, y + height, x, y + height - r, r); // draw left and top left corner 
+                    ctxi.arcTo(x, y, x + r, y, r);
+                    ctxi.closePath();
+                    if (fill) {
+                        ctxi.fill();
+                    }
+                    if (stroke) {
+                        ctxi.stroke();
+                    }
+                }
+            }
+            setTimeout(() => {
+                ctx.draw(true, () => {
+                    // 保存到临时区域
+                    setTimeout(() => {
+                        uni.canvasToTempFilePath({
+                            width: options.width,
+                            height: options.height,
+                            destWidth: options.width,
+                            destHeight: options.height,
+                            canvasId: options.canvasId,
+                            quality: Number(1),
+                            success: function (res) {
+                                if (options.cbResult) {
+                                    options.cbResult(res.tempFilePath)
+                                }
+                            },
+                            fail: function (res) {
+                                if (options.cbResult) {
+                                    options.cbResult(res)
+                                }
+                            },
+                            complete: function () {
+                                if (options.showLoading){
+                                    uni.hideLoading();
+                                }
+                            },
+                        }, options.context);
+                    }, options.text.length + 100);
+                });
+            }, options.usingComponents ? 0 : 150);
+        }
+        createCanvas(this.options);
+        // 空判定
+        let empty = function (v) {
+            let tp = typeof v,
+                rt = false;
+            if (tp == "number" && String(v) == "") {
+                rt = true
+            } else if (tp == "undefined") {
+                rt = true
+            } else if (tp == "object") {
+                if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
+            } else if (tp == "string") {
+                if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
+            } else if (tp == "function") {
+                rt = false
+            }
+            return rt
+        }
+    };
+    QRCode.prototype.clear = function (fn) {
+        var ctx = uni.createCanvasContext(this.options.canvasId, this.options.context)
+        ctx.clearRect(0, 0, this.options.size, this.options.size)
+        ctx.draw(false, () => {
+            if (fn) {
+                fn()
+            }
+        })
+    };
+})()
+
+export default QRCode

+ 210 - 0
h5_web/components/tki-qrcode/tki-qrcode.vue

@@ -0,0 +1,210 @@
+<template xlang="wxml" minapp="mpvue">
+	<view class="tki-qrcode">
+		<!-- #ifndef MP-ALIPAY -->
+		<canvas class="tki-qrcode-canvas" :canvas-id="cid" :style="{width:cpSize+'px',height:cpSize+'px'}" />
+		<!-- #endif -->
+		<!-- #ifdef MP-ALIPAY -->
+		<canvas :id="cid" :width="cpSize" :height="cpSize" class="tki-qrcode-canvas" />
+		<!-- #endif -->
+		<image v-show="show" :src="result" :style="{width:cpSize+'px',height:cpSize+'px'}" />
+	</view>
+</template>
+
+<script>
+import QRCode from "./qrcode.js"
+let qrcode
+export default {
+	name: "tki-qrcode",
+	props: {
+		cid: {
+			type: String,
+			default: 'tki-qrcode-canvas'
+		},
+		size: {
+			type: Number,
+			default: 200
+		},
+		unit: {
+			type: String,
+			default: 'upx'
+		},
+		show: {
+			type: Boolean,
+			default: true
+		},
+		val: {
+			type: String,
+			default: ''
+		},
+		background: {
+			type: String,
+			default: '#ffffff'
+		},
+		foreground: {
+			type: String,
+			default: '#000000'
+		},
+		pdground: {
+			type: String,
+			default: '#000000'
+		},
+		icon: {
+			type: String,
+			default: ''
+		},
+		iconSize: {
+			type: Number,
+			default: 40
+		},
+		lv: {
+			type: Number,
+			default: 3
+		},
+		onval: {
+			type: Boolean,
+			default: false
+		},
+		loadMake: {
+			type: Boolean,
+			default: false
+		},
+		usingComponents: {
+			type: Boolean,
+			default: true
+		},
+		showLoading: {
+			type: Boolean,
+			default: true
+		},
+		loadingText: {
+			type: String,
+			default: '二维码生成中'
+		},
+	},
+	data() {
+		return {
+			result: '',
+		}
+	},
+	methods: {
+		_makeCode() {
+			let that = this
+			if (!this._empty(this.val)) {
+				qrcode = new QRCode({
+					context: that, // 上下文环境
+					canvasId:that.cid, // canvas-id
+					usingComponents: that.usingComponents, // 是否是自定义组件
+					showLoading: that.showLoading, // 是否显示loading
+					loadingText: that.loadingText, // loading文字
+					text: that.val, // 生成内容
+					size: that.cpSize, // 二维码大小
+					background: that.background, // 背景色
+					foreground: that.foreground, // 前景色
+					pdground: that.pdground, // 定位角点颜色
+					correctLevel: that.lv, // 容错级别
+					image: that.icon, // 二维码图标
+					imageSize: that.iconSize,// 二维码图标大小
+					cbResult: function (res) { // 生成二维码的回调
+						that._result(res)
+					},
+				});
+			} else {
+				uni.showToast({
+					title: '二维码内容不能为空',
+					icon: 'none',
+					duration: 2000
+				});
+			}
+		},
+		_clearCode() {
+			this._result('')
+			qrcode.clear()
+		},
+		_saveCode() {
+			let that = this;
+			if (this.result != "") {
+				uni.saveImageToPhotosAlbum({
+					filePath: that.result,
+					success: function () {
+						uni.showToast({
+							title: '二维码保存成功',
+							icon: 'success',
+							duration: 2000
+						});
+					}
+				});
+			}
+		},
+		_result(res) {
+			this.result = res;
+			this.$emit('result', res)
+		},
+		_empty(v) {
+			let tp = typeof v,
+				rt = false;
+			if (tp == "number" && String(v) == "") {
+				rt = true
+			} else if (tp == "undefined") {
+				rt = true
+			} else if (tp == "object") {
+				if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
+			} else if (tp == "string") {
+				if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
+			} else if (tp == "function") {
+				rt = false
+			}
+			return rt
+		}
+	},
+	watch: {
+		size: function (n, o) {
+			if (n != o && !this._empty(n)) {
+				this.cSize = n
+				if (!this._empty(this.val)) {
+					setTimeout(() => {
+						this._makeCode()
+					}, 100);
+				}
+			}
+		},
+		val: function (n, o) {
+			if (this.onval) {
+				if (n != o && !this._empty(n)) {
+					setTimeout(() => {
+						this._makeCode()
+					}, 0);
+				}
+			}
+		}
+	},
+	computed: {
+		cpSize() {
+			if(this.unit == "upx"){
+				return uni.upx2px(this.size)
+			}else{
+				return this.size
+			}
+		}
+	},
+	mounted: function () {
+		if (this.loadMake) {
+			if (!this._empty(this.val)) {
+				setTimeout(() => {
+					this._makeCode()
+				}, 0);
+			}
+		}
+	},
+}
+</script>
+<style>
+.tki-qrcode {
+  position: relative;
+}
+.tki-qrcode-canvas {
+  position: fixed;
+  top: -99999upx;
+  left: -99999upx;
+  z-index: -99999;
+}
+</style>

+ 42 - 0
h5_web/main.js

@@ -0,0 +1,42 @@
+import Vue from 'vue'
+import App from './App'
+
+Vue.config.productionTip = false
+import { config } from './common/config'
+Vue.prototype.config = config;
+
+import './utils/filter' 
+
+App.mpType = 'app'
+
+// 引入全局uView
+import uView from 'uview-ui'
+Vue.use(uView);
+
+// 此处为演示vuex使用,非uView的功能部分
+import store from '@/store';
+
+// 引入uView提供的对vuex的简写法文件
+let vuexStore = require('@/store/$u.mixin.js');
+Vue.mixin(vuexStore);
+
+import $wxApi from "@/utils/wxapi.js";
Vue.prototype.$wxApi = $wxApi;

//微信支付封装
import $pay from "@/utils/pay.js";
Vue.prototype.$pay = $pay;
+
+// jsonp
+// import { VueJsonp } from 'vue-jsonp'
+// Vue.use(VueJsonp);
+
+const app = new Vue({
+	store,
+    ...App
+});
+
+// http拦截器,将此部分放在new Vue()和app.$mount()之间,才能App.vue中正常使用
+import httpInterceptor from '@/common/http.interceptor.js';
+Vue.use(httpInterceptor, app);
+
+// http接口API抽离,免于写url或者一些固定的参数
+import httpApi from '@/common/http.api.js';
+Vue.use(httpApi, app);
+
+app.$mount()

+ 77 - 0
h5_web/manifest.json

@@ -0,0 +1,77 @@
+{
+    "name" : "退役军人服务",
+    "appid" : "__UNI__EB85FC7",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "h5" : {
+        "router" : {
+            "mode" : "hash"
+        },
+        "template" : "template.h5.html"
+    }
+}

+ 180 - 0
h5_web/pages.json

@@ -0,0 +1,180 @@
+{
+	"easycom": {
+		"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
+	},
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationStyle":"custom",// 隐藏系统导航栏
+				"navigationBarTitleText": "首页"
+			}
+		}
+		,{
+		    "path" : "pages/mycode/mycode",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "退役军人码"
+		    }
+		    
+		}
+		,
+		{
+		    "path" : "pages/authentication/authentication",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "退役军人职业教育码申请"
+		    }
+		    
+		},
+		{
+			"path": "pages/phoneLogin/phoneLogin",
+			"style": {
+				"navigationBarTitleText": "登录"
+			}
+		},
+		{
+		    "path" : "pages/bannerDetails/bannerDetails",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "详情页"
+		    }
+		    
+		}
+		,{
+		    "path" : "pages/policyNews/policyNews",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "政策资讯"
+				
+		    }
+		    
+		}
+		,{
+		    "path" : "pages/policyNewsDetails/policyNewsDetails",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "文章详情"
+				
+		    }
+		    
+		}
+		,{
+		    "path" : "pages/newsDetails/newsDetails",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "文章详情"
+		    }
+		    
+		},
+		{
+			"path": "pages/login/code",
+			"style": {
+				"navigationBarTitleText": "验证码"
+			}
+		},
+		{
+            "path" : "pages/mine/mine",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "个人中心",
+				"navigationStyle": "custom"
+            }
+            
+        },
+		{
+            "path" : "pages/center/center",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "中心",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+		,{
+		    "path" : "pages/privacyPolicy/privacyPolicy",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "隐私政策"
+		    }
+		    
+		}
+		,{
+		    "path" : "pages/schools/schools",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "学院列表"
+		    }
+		}
+		,{
+		    "path" : "pages/skillsTraining/skillsTraining",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "技能培训"
+		    }
+		}
+		,{
+		    "path" : "pages/basicTraining/basicTraining",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "适应性培训"
+		    }
+		}
+		,{
+		    "path" : "pages/basicTraining/myBasicTraining/myBasicTraining",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "我的适应性培训"
+		    }
+		}
+		,{
+		    "path" : "pages/basicTraining/basicTrainingDetails/basicTrainingDetails",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "在线上课"
+		    }
+		}
+		,{
+		    "path" : "pages/upgrade/upgrade",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "学历提升"
+		    }
+		}
+		,{
+		    "path" : "pages/inbuild/inbuild",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "建设中"
+		    }
+		}
+    ],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F8F8F8"
+	},
+	"tabBar": {
+	    "color": "#909399",
+	    "selectedColor": "#303133",
+	    "borderStyle": "black",
+	    "backgroundColor": "#ffffff",
+	    "list": [{
+	        "pagePath": "pages/index/index",
+	        "iconPath": "static/icons/index-gray.png",
+	        "selectedIconPath": "static/icons/index.png",
+	        "text": "首页"
+	    }, {
+	        "pagePath": "pages/mycode/mycode",
+	        "iconPath": "static/icons/code-gray.png",
+	        "selectedIconPath": "static/icons/code.png",
+	        "text": "退役军人码"
+	    }, {
+	        "pagePath": "pages/mine/mine",
+	        "iconPath": "static/icons/mine-gray.png",
+	        "selectedIconPath": "static/icons/mine.png",
+	        "text": "我"
+	    }]
+	}
+}

+ 418 - 0
h5_web/pages/authentication/authentication.vue

@@ -0,0 +1,418 @@
+<template>
+	<view>
+		<u-form :model="form" ref="uForm" label-width="150">
+			<u-form-item label="身份证号" prop="veteIdCard" :required="true" style="margin-bottom: 24rpx;">
+				<u-input v-model="form.veteIdCard" @blur="getIdcardData" />
+			</u-form-item>
+			<u-form-item label="姓名" prop="veteName" :required="true">
+				<u-input v-model="form.veteName" />
+			</u-form-item>
+			<u-form-item label="民族" prop="nation" :required="true" >
+				<u-input v-model="form.nation"  />
+			</u-form-item>
+			<u-form-item label="性别" >
+				<u-input v-model="veteSex" placeholder="输入身份证自动获取" disabled />
+			</u-form-item>
+			<u-form-item label="联系电话" prop="vetePhone" :required="true">
+				<u-input v-model="form.vetePhone" maxlength="11" />
+			</u-form-item>			
+			<u-form-item label="文化程度" prop="degrEduc" :required="true" >
+				<u-select v-model="degrEducShow" :list="degrEducList" :default-value="[3]" value-name="text" @confirm="degrEducConfirm"></u-select>
+				<u-input v-model="form.degrEduc" placeholder="请选择" disabled="disabled" @click="degrEducShow = true" />
+			</u-form-item>
+			<u-form-item label="政治面貌" prop="politOutlook"  :required="true" >
+				<u-select v-model="politOutlookShow" :list="politOutlookList" value-name="text" @confirm="politOutlookConfirm"></u-select>
+				<u-input v-model="form.politOutlook" placeholder="请选择" disabled="disabled" @click="politOutlookShow = true" />
+			</u-form-item>
+			<u-form-item label="户籍类别" prop="houseType"  :required="true" >
+				<u-select v-model="houseTypeShow" :list="houseTypeList" value-name="text" @confirm="houseTypeConfirm"></u-select>
+				<u-input v-model="form.houseType" placeholder="请选择" disabled="disabled" @click="houseTypeShow = true" />
+			</u-form-item>
+			<!-- <u-form-item label="申请人" >
+				<u-radio-group v-model="form.veteStatus" @change="veteStatusChange" >
+					<u-radio shape="circle" name="1">本人申请</u-radio>
+					<u-radio shape="circle" name="2">子女代申请</u-radio>
+				</u-radio-group>
+			</u-form-item> -->
+			<!-- <u-form-item label="出生日期" @click="birthTime = true" >
+				<u-input v-model="form.veteDateBirth" placeholder="输入身份证自动获取" disabled />
+			</u-form-item> -->
+			<!-- <u-form-item label="人员属性" prop="veteAttribute" :required="true">
+				<u-checkbox-group @change="checkboxGroupChange" :wrap="false">
+					<u-checkbox v-model="item.checked" v-for="(item, index) in checkboxList" :key="index" :name="item.label">{{ item.label }}</u-checkbox>
+				</u-checkbox-group>
+			</u-form-item> -->
+			<!-- <u-form-item label="入伍时间" prop="veteInEnlist" :required="true">
+				<u-input type="select"
+				:select-open="veteInEnlistShow" v-model="form.veteInEnlist" placeholder="请选择入伍时间" @click="veteInEnlistShow = true"></u-input>
+			</u-form-item>
+			<u-form-item label="退伍时间" prop="veteOutEnlist" :required="true">
+				<u-input type="select"
+				:select-open="veteOutEnlistShow" v-model="form.veteOutEnlist" placeholder="请选择入伍时间" @click="veteOutEnlistShow = true"></u-input>
+			</u-form-item> -->
+			
+			<u-form-item label="户籍地" prop="veteHousehRegi" :required="true">
+				<u-input type="select" 
+				:select-open="regiShow" v-model="form.veteHousehRegi" disabled="disabled" placeholder="请正确选择,否则可能影响使用" @click="regiShow = true"></u-input>
+			</u-form-item>
+			<u-form-item label="居住地址" prop="veteResiAddress" :required="true">
+				<u-input v-model="form.veteResiAddress" placeholder="省市区街道/门牌号" />
+			</u-form-item>
+			<u-form-item label="qq"  >
+				<u-input v-model="form.qqNum" />
+			</u-form-item>
+			<u-form-item label="微信"  >
+				<u-input v-model="form.wxNum" />
+			</u-form-item>
+			<view class="upload-wrap u-flex u-flex-wrap u-row-left">
+				<view class="upload-wrap-title">近期一寸免冠照(必选):</view>
+				<!-- <u-upload :action="action"  prop="files1" :show-progress="false" upload-text="身份证人像面" max-count="1" @on-success="files1Success"></u-upload>
+				<u-upload :action="action" :show-progress="false" upload-text="身份证国徽面" max-count="1" @on-success="files2Success"></u-upload>
+				<u-upload :action="action" :show-progress="false" upload-text="手持身份证" max-count="1" @on-success="files3Success"></u-upload>
+				<u-upload :action="action" :show-progress="false" upload-text="户口本" max-count="1" @on-success="files4Success"></u-upload>
+				<u-upload :action="action" :show-progress="false" upload-text="退役证" max-count="1" @on-success="files5Success"></u-upload> -->
+				<u-upload :action="action" :show-progress="true" upload-text="上传图片" max-count="1" @on-success="files6Success"></u-upload>
+			</view>
+			<!-- <u-form-item label="身份证人像面" prop="pic" label-width="200" :required="true">
+				
+			</u-form-item>
+			<u-form-item label="身份证国徽面" prop="pic" label-width="200" :required="true">
+				
+			</u-form-item> -->
+		</u-form>
+		<u-button class="submit-btn" @click="submit">提交</u-button>
+		<u-picker mode="region" v-model="regiShow" :default-region='["贵州省", "贵阳市", "南明区"]' @confirm="regionConfirm"></u-picker>
+		<u-picker mode="time" v-model="veteInEnlistShow" @confirm="veteInEnlistConfirm"></u-picker>
+		<u-picker mode="time" v-model="veteOutEnlistShow" @confirm="veteOutEnlistConfirm"></u-picker>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			let that = this;
+			return {
+				action:this.config.uploadUrl,
+				birthTime:false,
+				regiShow:false,
+				veteInEnlistShow:false,
+				veteOutEnlistShow:false,
+				politOutlookShow:false,
+				houseTypeShow:false,
+				degrEducShow:false,
+				veteSex:'',
+				checkboxList:[],
+				politOutlookList:[],
+				houseTypeList:[],
+				degrEducList:[],
+				form: {
+						veteName: '',
+						veteIdCard: '',
+						veteDateBirth:'',
+						veteSex:'',
+						veteStatus:'1',
+						veteAttribute:'',
+						veteInEnlist:'',
+						veteOutEnlist:'',
+						vetePhone:'',
+						veteHousehRegi:'',
+						veteResiAddress:'',
+						nation:'',
+						qqNum:'',
+						wxNum:'',
+						degrEduc:'',
+						degrEducId:'',
+						houseType:'',
+						houseTypeId:'',
+						politOutlook:'',
+						politOutlookId:'',
+					files:[]
+				},
+				rules: {
+					veteName: [{
+						required: true,
+						message: '请输入姓名',
+						// 可以单个或者同时写两个触发验证方式 
+						trigger: ['change', 'blur'],
+					}],
+					veteIdCard: [{
+						required: true,
+						pattern: /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/,
+						// 正则检验前先将值转为字符串
+						transform(value) {
+							return String(value);
+						},
+						message: '请正确输入身份证号',
+						trigger: ['change', 'blur'],
+					}],
+					veteAttribute:[{
+						required: true,
+						message: '请输入人员属性',
+					}],
+					veteInEnlist:[{
+						required: true,
+						message: '请选择入伍时间',
+					}],
+					veteOutEnlist:[{
+						required: true,
+						message: '请选择退伍时间',
+					}],
+					vetePhone:[{
+						pattern: /^(?:(?:\+|00)86)?1[3-9]\d{9}$/,
+						message: '请正确输入手机号',
+						required: true,
+						trigger: ['change', 'blur'],
+					}],
+					veteHousehRegi:[{
+						required: true,
+						message: '请选择户籍',
+					}],
+					veteResiAddress:[{
+						required: true,
+						message: '请填写详细地址',
+					}],
+					nation:[{
+						required: true,
+						message: '请填写民族',
+					}],
+					degrEduc:[{
+						required: true,
+						message: '请选择文化程度',
+					}],
+					politOutlook:[{
+						required: true,
+						message: '请选择政治面貌',
+					}],
+					houseType:[{
+						required: true,
+						message: '请选择户籍类别',
+					}]
+				}
+			}
+		},
+		onLoad() {
+			this.getVeteAttribute();
+			this.getVeteDegrEduc();
+			this.getVetePolitOutlook();
+			this.getVeteHouseType();
+		},
+		onReady() {
+			this.$refs.uForm.setRules(this.rules);
+			this.form.vetePhone = this.vuex_user.mobile;
+		},
+		onShow() {
+			// 获取已经提交的数据
+			// this.getVeteEducData();
+		},
+		methods: {
+			// 获取已经提交的数据
+			getVeteEducData(){
+				this.$u.api.getVeteEducData().then(res=>{
+					console.log('getVeteEducData',res);
+					if(res.code == 200){
+						for(let key  in res.data.veteEduc) {
+							if(key!='files'){
+								this.form[key] = res.data.veteEduc[key];
+							}
+						};
+						this.form.files = res.data.files;
+					}
+					console.log('this.form',this.form);
+				});
+			},
+			// 获取人员属性  
+			getVeteAttribute(){
+				this.$u.api.getDictdataUrl({key:'vete_attribute'}).then(res=>{
+					console.log('getVeteAttribute',res);
+					if(res.code == 200){						
+						this.checkboxList = res.data;
+					}
+				});
+			},
+			// 获取文化程度类别
+			getVeteDegrEduc(){
+				this.$u.api.getDictdataUrl({key:'degr_educ'}).then(res=>{
+					if(res.code == 200){						
+						this.degrEducList = res.data;
+					}
+				});
+			},
+			degrEducConfirm(e){
+				console.log('degrEducConfirm',e);
+				this.form.degrEduc = e[0].label;
+				this.form.degrEducId = e[0].value;
+			},
+			// 获取政治面貌类别
+			getVetePolitOutlook(){
+				this.$u.api.getDictdataUrl({key:'polit_outlook'}).then(res=>{
+					if(res.code == 200){						
+						this.politOutlookList = res.data;
+					}
+				});
+			},
+			politOutlookConfirm(e){
+				console.log('politOutlookConfirm',e);
+				this.form.politOutlook = e[0].label;
+				this.form.politOutlookId = e[0].value;
+			},
+			// 获取户籍类别
+			getVeteHouseType(){
+				this.$u.api.getDictdataUrl({key:'house_type'}).then(res=>{
+					if(res.code == 200){						
+						this.houseTypeList = res.data;
+					}
+				});
+			},
+			houseTypeConfirm(e){
+				console.log('houseTypeConfirm',e);
+				this.form.houseType = e[0].label;
+				this.form.houseTypeId = e[0].value;
+			},
+			// 户籍地区回调
+			regionConfirm(e) {
+				// console.log('户籍地区回调',e)
+				this.form.veteHousehRegi = e.province.label + '-' + e.city.label + '-' + e.area.label;
+			},
+			files1Success(e){
+				this.form.files.push({filesType:1,filesUrl:e.data.url})
+			},
+			files2Success(e){
+				this.form.files.push({filesType:2,filesUrl:e.data.url})
+			},
+			files3Success(e){
+				this.form.files.push({filesType:3,filesUrl:e.data.url})
+			},
+			files4Success(e){
+				this.form.files.push({filesType:4,filesUrl:e.data.url})
+			},
+			files5Success(e){
+				this.form.files.push({filesType:5,filesUrl:e.data.url})
+			},
+			files6Success(e){
+				this.form.files.push({filesType:6,filesUrl:e.data.url})
+			},
+			//入伍时间 
+			veteInEnlistConfirm(e){
+				console.log('入伍时间',e)
+				this.form.veteInEnlist = `${e.year}-${e.month}-${e.day} 00:00:00`
+			},
+			//退伍时间 
+			veteOutEnlistConfirm(e){
+				console.log('退伍时间',e)
+				this.form.veteOutEnlist = `${e.year}-${e.month}-${e.day} 00:00:00`
+			},
+			getIdcardData(){
+			   if(!/^\d{6}((?:19|20)((?:\d{2}(?:0[13578]|1[02])(?:0[1-9]|[12]\d|3[01]))|(?:\d{2}(?:0[13456789]|1[012])(?:0[1-9]|[12]\d|30))|(?:\d{2}02(?:0[1-9]|1\d|2[0-8]))|(?:(?:0[48]|[2468][048]|[13579][26])0229)))\d{2}(\d)[xX\d]$/.test(this.form.veteIdCard)){
+			      // alert('身份证号非法.');
+			      return;
+			   }
+			  this.form.veteDateBirth=(RegExp.$1).substr(0,4)+'-'+(RegExp.$1).substr(4,2)+'-'+(RegExp.$1).substr(6,2)+' 00:00:00';//设置出生日期
+			  this.form.veteSex=(parseInt(this.form.veteIdCard.charAt(this.form.veteIdCard.length-2))%2==0?'1':'0');//设置性别 0 男 1 女
+			  this.veteSex=(parseInt(this.form.veteIdCard.charAt(this.form.veteIdCard.length-2))%2==0?'女':'男');//设置性别 0 男 1 女
+			  uni.showLoading({
+			  	title:'数据查询中'
+			  });
+			  this.$u.api.getIdInfo({id:this.form.veteIdCard}).then(res=>{
+				  uni.hideLoading();
+				  if(res.code==200){
+					  this.form.nation = res.data.nation;
+					  this.form.veteName = res.data.veteName;
+					  this.form.vetePhone = res.data.vetePhone;
+					  this.form.veteResiAddress = res.data.veteResiAddress;
+				  }else{
+					uni.showToast({
+						icon:'none',
+						title:res.msg
+					})
+				}
+				  console.log('getIdInfo',res)
+			  }).catch(err=>{
+				  uni.hideLoading();
+				  // uni.showToast({
+				  // 	icon:'none',
+				  // 	title:'网络出错'
+				  // })
+			  })
+			},
+			veteStatusChange(e){
+				console.log('veteStatusChange',e)
+				this.form.veteStatus = e;
+			},
+			// checkbox选择发生变化
+			checkboxGroupChange(e) {
+				// console.log('checkboxGroupChange',e)
+				this.form.veteAttribute = e.toString();
+				// console.log('this.form',this.form)
+			},
+			submit() {
+				this.$refs.uForm.validate(valid => {
+					console.log('this.form',this.form);
+					if (valid) {
+						console.log('验证通过');						
+						if(this.form.files.length<1){
+							uni.showToast({
+							    title: '请上传所需图片',
+								icon:'none',
+							    duration: 2000
+							});
+							return;
+						}else{
+							console.log('this.form',this.form);
+							
+							let params = {veteEduc:{}};
+							params.files = this.form.files
+							
+							for(let key  in this.form) {
+								if(key!='files'){
+									params.veteEduc[key] = this.form[key];
+								}
+							};
+							console.log('params',params);
+							
+							this.$u.api.veteEduc(params)
+							.then(res=>{
+								if(res.code == 200 ){
+									uni.showToast({
+									    title: '提交成功,等待审核!',
+									    duration: 2000,
+										success:function(){
+											uni.switchTab({
+												url: '../index/index'
+											})
+										}
+									});
+								}else{
+									uni.showToast({
+									    title: res.msg,
+										icon:'none',
+									    duration: 2000
+									});
+								}
+								console.log('认证',JSON.parse(JSON.stringify(res)));
+							}).catch(err=>{
+								uni.showToast({
+								    title: '请重试!',
+									icon:'none',
+								    duration: 2000
+								});
+							});
+						}
+					} else {
+						console.log('验证失败');
+						return;
+					}
+				});
+			}
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page{background-color: #e5e6e6;padding-top: 24rpx;padding-bottom: 58rpx;}
+	.u-form-item{background-color: #fff;margin: 0 24rpx;padding-left: 24rpx;padding-right: 24rpx;}
+	.upload-wrap{background-color: #fff;margin: 0 24rpx 24rpx;padding-bottom: 24rpx;}
+	.upload-wrap-title{width: 100%;margin: 24rpx;}
+	.submit-btn{margin: 24rpx;border: 0;background-color: #2b82e4;box-shadow: none;color: #fff;}
+	// @import  './authentication.scss'
+</style>

+ 53 - 0
h5_web/pages/bannerDetails/bannerDetails.vue

@@ -0,0 +1,53 @@
+<template>
+	<!-- 轮播详情 -->
+	<view>
+		<u-parse :html="dom"></u-parse>
+		<u-toast ref="uToast" />
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				dom: ''
+			}
+		},
+		onLoad(page) {
+			if (page.id) {
+				this.getDetails(page.id)
+			}
+		},
+		methods: {
+			// 获取详情
+			getDetails(id) {
+				this.$u.api.getIndexBannerList()
+				.then(res=>{
+					if (res.code === 200) {
+						const list = res.data
+						list.forEach(item => {
+							if (item.id == id) {
+								this.dom = item.content
+							}
+						})
+					} else {
+						this.$refs.uToast.show({
+							title: res.msg,
+							type: 'error'
+						})
+					}
+				})
+				.catch(err => {
+					this.$refs.uToast.show({
+						title: '操作失败!',
+						type: 'error'
+					})
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+page{padding: 24rpx;}
+</style>

+ 112 - 0
h5_web/pages/basicTraining/basicTraining.vue

@@ -0,0 +1,112 @@
+<template>
+	<view>
+		<image class="bg" src="../../static/img/basicTraining-bg.png" mode="widthFix"></image>
+		<u-tabs ref="btabs" :list="tabs" :is-scroll="false" :current="tabIndex" @change="tabChange"></u-tabs>
+		<onlineTraining v-show="tabIndex=='0'" :key="1" :onlineData="onlineData" :rate="rate"></onlineTraining>
+		<offlineTraining v-show="tabIndex=='1'" :key="2" :offlineId="offlineId"></offlineTraining>
+	</view>
+</template>
+
+<script>
+	import onlineTraining from './components/online.vue';
+	import offlineTraining from './components/offline.vue';
+	export default{
+		components:{
+			onlineTraining,
+			offlineTraining
+		},
+		data(){
+			return{
+				tabs: [{name: '培训课程'},{name:'线下培训'}],
+				tabIndex: 0 ,// tab下标
+				onlineData:[],
+				rate:{},
+				offlineId:'',
+				analysisParams:{
+					platform:'1',//平台:1-H5 2-APP 3-小程序 4-PC端
+					pages:location.href,//页面路径
+					btnName:'',//按钮名称
+					btnEvent:'1',//按钮事件: 1-点击 2-长按 3-滑动
+					ipAddress:'',//IP地址
+					typeName:'适应性培训',//类型名称 例:学校关注度 、适应性考试等
+					typeCode:'',//类型编码 例:类型名称首字母缩写(XXGZD)
+					categoryName:'',//类别名称 例:XX学校,SS考试
+				}
+			}
+		},
+		onLoad(page){
+			if(page?.type=='offline'){
+				this.tabIndex = 1;
+				if(page.id){
+					this.offlineId = page.id;
+					console.log('this.offlineId',this.offlineId)
+				}
+			};
+			
+		
+		},
+		onShow(){
+			this.getAdaptTrainOnline();
+			this.getAdaptTrainOnlineRate();			
+		},
+		mounted() {
+			this.postAnalysis();
+			
+		},
+		methods:{
+			getip(){
+				let that = this;
+				uni.request({
+				    url:'/apis/cityjson?ie=utf-8',
+					method:'GET',
+					success: (res) => {
+						// console.log('this',this);
+				        const reg = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
+						let ip = reg.exec(res.data);
+						that.analysisParams.ipAddress = ip[0];
+						console.log('analysisParams',this.analysisParams);
+					}
+				});
+			},
+			async postAnalysis(){
+				await this.getip();
+				this.$u.api.postAnalysis(this.analysisParams).then(res=>{
+					console.log('res',res);
+				});
+			},
+			// 切换菜单
+			tabChange(index) {
+				console.log('tabChange index',index)
+				this.tabIndex = index;
+				
+			},
+			getAdaptTrainOnline(){
+				this.$u.api.getAdaptTrainOnline().then(res=>{
+					if(res.code == 200){
+						this.onlineData = res.data;
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+					
+					console.log('getAdaptTrainOnline',JSON.parse(JSON.stringify(res)));
+				})
+			},
+			getAdaptTrainOnlineRate(){
+				this.$u.api.getAdaptTrainOnlineRate().then(res=>{
+					this.rate = res.data;
+					console.log('getAdaptTrainOnlineRate',JSON.parse(JSON.stringify(res)));
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page{background-color: #f9f9f9;padding-bottom: 48rpx;}
+	.bg{width: 100%;}
+	
+	// @import  './BasicTraining.scss'
+</style>

+ 88 - 0
h5_web/pages/basicTraining/basicTrainingDetails/basicTrainingDetails.vue

@@ -0,0 +1,88 @@
+<template>
+	<view>
+		<!-- <video id="vid"><source :src="details.videoUrl"></video> -->
+		<!-- <video width="320" height="240" controls="controls" preload="auto">
+		  <source src="static/img/11111.mp4" type="video/mp4">
+		您的浏览器不支持Video标签。
+		</video> -->
+		<!-- <video @timeupdate="updateTime" @canplay="getDuration" ref="video111" :src="details.videoUrl" controls="controls" preload="auto"></video> -->
+		<view id="player" style="width: 100%;"></view>
+		<u-parse :html="details.content" class="f-padding"></u-parse>
+	</view>
+</template>
+
+<script>
+	import Player from 'xgplayer';
+	
+	export default{
+		data(){
+			return{
+				id:'',
+				content:'',
+				details:{},
+				duration:'',
+			}
+		},
+		onLoad(page){
+			console.log(page)
+			this.id = page.id;
+			this.getAdaptTrainOnlineDetails(this.id);
+		},
+		onShow(){
+			
+		},
+		methods:{
+			getAdaptTrainOnlineDetails(id){
+				this.$u.api.getAdaptTrainOnlineDetails({id:id}).then(res=>{
+					this.details = res.data;
+					let player = new Player({
+					  id: 'player',
+					  autoplay: false,
+					  playsinline: true,
+					  url: this.details.videoUrl,
+					  "screenShot": true,
+					  fluid: true,
+					  videoInit: true,
+					  enableVideoDbltouch: true,
+					});
+					player.once('ended',()=>{
+						this.upAdaptTrainOnlineInfo();
+					});
+					player.once('canplay',(e)=>{
+						console.log('canplay',player.duration)
+						this.duration = player.duration;
+					});
+					player.on('timeupdate',()=>{
+						let that = this;
+						// console.log('that',that);
+						// console.log('currentTime',player.currentTime);
+						// // setInterval(that.aaa+=1,1000)
+						// console.log('that.aaa',that.aaa);
+					  //事件名称可以在上述查询
+					});
+					console.log('getAdaptTrainOnline',JSON.parse(JSON.stringify(res)));
+				})
+			},
+			upAdaptTrainOnlineInfo(){
+				let params ={
+					tabId:this.details.id,
+					adaptName:this.details.adaptName,
+					url:this.details.videoUrl,
+					type:this.details.videoType,
+					duration:this.duration,
+					playDuration:this.duration,
+				}
+				this.$u.api.upAdaptTrainOnlineInfo(params).then(res=>{
+					this.rate = res.data;
+					console.log('upAdaptTrainOnlineInfo',JSON.parse(JSON.stringify(res)));
+				})
+			},
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	#player{margin-bottom: 24rpx;}
+	// @import  './basicTrainingDetails.scss'
+</style>

+ 147 - 0
h5_web/pages/basicTraining/components/offline.vue

@@ -0,0 +1,147 @@
+<template>
+	<view>
+		<view class="status">
+			<view class="title">{{offlineInfo.adaptStatus|verifyStatusFilter}}</view>
+			<view class="tip">注:必须参加线下培训才能通过适应性培训</view>
+			<!-- <u-parse :html="offlineInfo.offlineContent"></u-parse> -->			
+		</view>
+		<view class="content">
+			<!-- <dl>
+				<dt>【培训名称】</dt>
+				<dd>{{offlineInfo.adaptName}}</dd>
+			</dl>
+			<dl>
+				<dt>【培训地址】</dt>
+				<dd>{{offlineInfo.adaptAddress}}</dd>
+			</dl>
+			<dl>
+				<dt>【培训时长】</dt>
+				<dd>{{offlineInfo.timeLength}}</dd>
+			</dl>
+			<dl>
+				<dt>【开始时间】</dt>
+				<dd>{{offlineInfo.startTime}}</dd>小时
+			</dl>
+			<dl>
+				<dt>【结束时间】</dt>
+				<dd>{{offlineInfo.endTime}}</dd>
+			</dl> -->
+			<dl>
+				<dt>【培训安排】</dt>
+				<dd>
+					<u-parse :html="offlineInfo.adaptPlan"></u-parse>
+				</dd>
+				<!-- <dd>{{offlineInfo.adaptPlan}}</dd> -->
+			</dl>
+		</view>
+		<u-bottom>
+			<view slot="content">
+				<view @click="sign" v-if="offlineInfo.flag==0&&offlineId" class="status-btn fix-bottom">
+					确认签到
+				</view>
+				<view v-else-if="offlineInfo.flag==0&&!offlineId" class="status-btn fix-bottom">
+					未签到
+				</view>
+				<view slot="content" v-else-if="offlineInfo.flag==1" class="status-btn fix-bottom">
+					已签到
+				</view>
+			</view>
+		</u-bottom>
+	</view>
+</template>
+
+<script>
+	export default{
+		name: 'offlineTraining',
+		props:{
+			offlineId: {
+				type: String,
+				default: function() {
+					return ''
+				}
+			}
+		},
+		data(){
+			return{
+				offlineInfo:'',
+				params:{}
+			}
+		},
+		onLoad(){
+			
+		},
+		mounted() {
+			if(this.offlineId){
+				this.params.id = this.offlineId;
+				this.getAdaptTrainIdOffline(this.params);
+			}else{
+				this.getAdaptTrainOffline();
+			}
+		},
+		onShow(){
+			
+		},
+		filters: {
+		  verifyStatusFilter(value) {
+			if (value == 0) {
+			  return '未开始';
+			}else if(value == 1){
+				return '进行中';
+			}else if(value == 2){
+				return '已完成';
+			}else {
+			  return '未知';
+			}
+		  },
+		  verifyFlagFilter(value){
+			  if (value == 0) {
+			    return '未参与';
+			  }else if(value == 1){
+			  	return '已参与';
+			  }else {
+			    return '未知';
+			  }
+		  }
+		},
+		methods:{
+			getAdaptTrainOffline(){
+				this.$u.api.getAdaptTrainOffline().then(res=>{
+					this.offlineInfo = res.data||[];
+					console.log('getAdaptTrainOffline',JSON.parse(JSON.stringify(res)));
+				})
+			},
+			getAdaptTrainIdOffline(params){
+				console.log('params',params)
+				this.$u.api.getAdaptTrainIdOffline(params).then(res=>{
+					this.offlineInfo = res.data||[];
+					console.log('getAdaptTrainIdOffline',JSON.parse(JSON.stringify(res)));
+				})
+			},
+			sign(){
+				this.$u.api.postSignOffline({tabId:this.offlineId}).then(res=>{
+					if(res.code == 200){
+						this.getAdaptTrainIdOffline(this.params);
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+					console.log('postSignOffline',JSON.parse(JSON.stringify(res)));
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.status{background-color: #fff;padding: 24rpx;border-radius: 8rpx;text-align: center;margin: 24rpx;}
+	.status .title{font-size: 36rpx;color: #fc7a40;margin-bottom: 10rpx;}
+	.status .tip{font-size: 18rpx;color: #bfbfbf;}
+	.status-btn{background-color: #4433d9;color: #fff;margin: 0 24rpx;padding: 24rpx;border-radius: 8rpx;}
+	.content{background-color: #fff;padding: 24rpx;margin: 24rpx;}
+	.content dl{margin-bottom: 24rpx;}
+	.content dt{font-size: 36rpx;margin-bottom: 24rpx;}
+	.content dd{color: #5f5f5f;margin: 0;}
+	// @import  './offline.scss'
+</style>

+ 127 - 0
h5_web/pages/basicTraining/components/online.vue

@@ -0,0 +1,127 @@
+<template>
+	<view class="online">
+		<view class="statistics u-flex u-row-between">
+			<view class="statistics-item">
+				<view class="up">已看课程</view>
+				<view class="down"><text>{{rate.play}}</text> 节</view>
+			</view>
+			<view class="statistics-item">
+				<view class="up">还需观看</view>
+				<view class="down"><text>{{rate.playNot}}</text> 节</view>
+			</view>
+		</view>
+		<view class="content">
+			<!-- <u-tabs ref="otabs" :list="onlineTabs" :is-scroll="false" :current="onlineTabIndex" @change="onlineTabChange"></u-tabs> -->
+			<view class="instructions" v-if="onlineTabIndex==0">
+				<view class="text">
+					培训内容包括单不限于思想政治教育,法律法规政策介绍,就业创业指导,心里引导,求职技巧指导,服务项目指引等。
+				</view>
+				<!-- <image src="../../../static/img/instructions-01.png" mode=""></image> -->
+				<!-- <image src="../../../static/img/instructions-02.png" mode=""></image> -->
+				<view class="catalog f-padding">
+					<view 
+					class="catalog-item u-flex" 
+					:class="{ 'unstudy':item.flag == 0, 'studying':item.flag == 1, 'studyed':item.flag == 2 }"
+					v-for="(item,index) in onlineData" :key="item.id+index" 
+					@click="catalogItemClick(item)">
+						<view class="number">{{index + 1}}.</view>
+						<view class="name">{{item.adaptName}}</view>
+					</view>
+				</view>
+			</view>
+			<!-- <view class="catalog f-padding" v-if="onlineTabIndex==1">
+				<view 
+				class="catalog-item u-flex" 
+				:class="{ 'unstudy':item.flag == 0, 'studying':item.flag == 1, 'studyed':item.flag == 2 }"
+				v-for="(item,index) in onlineData" :key="item.id+index" 
+				@click="catalogItemClick(item)">
+					<view class="number">{{index + 1}}.</view>
+					<view class="name">{{item.adaptName}}</view>
+				</view>
+			</view> -->
+		</view>
+		<u-bottom>
+			<view slot="content" class="status-btn fix-bottom">
+				学习状态:{{rate.status|verifyStatusFilter}}
+			</view>
+		</u-bottom>
+	</view>
+</template>
+
+<script>
+	export default{
+		name: 'onlineTraining',
+		props:{
+			onlineData: {
+				type: Array,
+				default: function() {
+					return []
+				}
+			},
+			rate:{
+				type:Object,
+				default: function() {
+					return new Object()
+				}
+			}
+		},
+		data(){
+			return{
+				onlineTabs: [{name: '课程说明'},{name:'课程视频'}],
+				onlineTabIndex:0,
+				catalogList:[]
+			}
+		},
+		onLoad(){
+			
+		},
+		onShow(){
+			
+		},
+		filters: {
+		  verifyStatusFilter(value) {
+			if (value === 0) {
+			  return '未全部完成';
+			}else if(value === 1){
+				return '已全部完成';
+			}else {
+			  return '未知';
+			}
+		  },
+		},
+		methods:{
+			onlineTabChange(index){
+				this.onlineTabIndex = index;
+			},
+			catalogItemClick(item){
+				this.$u.route({
+					url: 'pages/basicTraining/basicTrainingDetails/basicTrainingDetails',
+					params: {
+						id: item.id
+					}
+				});
+			}
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.online{margin: 24rpx;}
+	.statistics{text-align: center;background-color: #fff;border-radius: 8rpx;padding: 24rpx;margin-bottom: 24rpx;}
+	.statistics-item{width: 50%;}
+	.statistics-item .up{font-size: 30rpx;color: #444;}
+	.statistics-item .down{color: #969696;}
+	.statistics-item .down text{font-weight: bold;margin-right: 5rpx;}
+	.catalog{background-color: #fff;font-size: 36rpx;padding-bottom: 24rpx;}
+	.catalog-item{margin-bottom: 24rpx;}
+	.catalog-item.unstudy{}
+	.catalog-item.studying{color: #e87c7c;}
+	.catalog-item.studyed{color: #c5c5c5;}
+	.catalog-item .number{margin-right: 15rpx;}
+	.content{background-color: #fff;}
+	.instructions .text{padding: 24rpx;color: #969696;}
+	.instructions image{width: 100%;}
+	.status-btn{background-color: #4433d9;color: #fff;margin: 0 24rpx;padding: 24rpx;border-radius: 8rpx;}
+	// @import  './online.scss'
+</style>

+ 84 - 0
h5_web/pages/basicTraining/myBasicTraining/myBasicTraining.vue

@@ -0,0 +1,84 @@
+<template>
+	<view>
+		<image class="bg" src="../../../static/img/banner-myBasicTraining.png" mode="widthFix"></image>
+		<view class="title u-flex u-row-between">
+			<view class="left">
+				学习进度
+			</view>
+			<!-- <view class="right">
+				查看全部
+			</view> -->
+		</view>
+		<view class="course">
+			<view class="course-item u-flex u-row-between" 
+			v-for="(item,index) in onlineData" 
+			:class="{ 'unstudy':item.flag == 0, 'studying':item.flag == 1, 'studyed':item.flag == 2 }"
+			@click="courseClick(item)">
+				<view class="left">
+					<view class="course-item-title">
+						第{{index + 1}}节
+					</view>
+					<view class="course-item-name">
+						{{item.adaptName}}
+					</view>
+				</view>
+				<view class="right">
+					<u-icon v-if="item.flag == 2" name="checkbox-mark" color="#00a046" size="28" ></u-icon>
+					<u-icon v-else name="arrow-right" color="#2683e5" size="28" ></u-icon>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default{
+		data(){
+			return{
+				onlineData:[],
+			}
+		},
+		onLoad(){
+			
+		},
+		onShow(){
+			this.getAdaptTrainOnline();
+		},
+		methods:{
+			getAdaptTrainOnline(){
+				this.$u.api.getAdaptTrainOnline().then(res=>{
+					if(res.code == 200){
+						this.onlineData = res.data;
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+					
+					console.log('getAdaptTrainOnline',JSON.parse(JSON.stringify(res)));
+				})
+			},
+			courseClick(item){
+				this.$u.route({
+					url: 'pages/basicTraining/basicTrainingDetails/basicTrainingDetails',
+					params: {
+						id: item.id
+					}
+				});
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page{padding-bottom: 48rpx;background-color: #eee;}
+	.bg{width: 100%;}
+	.title {padding: 24rpx;;background-color: #fff;border-bottom: 1px solid #ddd;}
+	.title .left{font-size: 36rpx;}
+	.course{background-color: #fff;}
+	.course-item{margin-bottom: 24rpx;padding: 20rpx 24rpx;}
+	.course-item + .course-item{border-top: 1px solid #ddd;}
+	.course-item.studyed .left{color: #c5c5c5;}
+	// @import  './myBasicTraining.scss'
+</style>

+ 22 - 0
h5_web/pages/center/center.vue

@@ -0,0 +1,22 @@
+<template>
+	<view align="center">
+		中心
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			}
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 42 - 0
h5_web/pages/inbuild/inbuild.vue

@@ -0,0 +1,42 @@
+<template>
+	<view>
+		<view class="tip-wrap">
+			<image class="empty-img" src="../../static/img/empty.png" mode="widthFix"></image>
+			<!-- <view class="msg">
+				{{msg}}
+			</view> -->
+			<view class="tip">
+				{{tip}}
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default{
+		data(){
+			return{
+				tip:'此功能正在建设中',
+				msg:''
+			}
+		},
+		onLoad(page){
+			this.msg = page.msg;
+		},
+		onShow(){
+			
+		},
+		methods:{
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page{background-color: #F4F4F4;}
+	.tip-wrap{text-align: center;margin-top: 20vh;}
+	.msg{font-size: 36rpx;color: #444;margin-bottom: 24rpx;}
+	.tip{font-size: 40rpx;color: #737373;line-height: 56px;letter-spacing: 2px;font-weight: 400;}
+	.empty-img{width: 253rpx;margin-bottom: 52rpx;}
+	// @import  './inbuild.scss'
+</style>

+ 43 - 0
h5_web/pages/index/index.scss

@@ -0,0 +1,43 @@
+page{background-color: #F6F6FF;}
+.message{position: absolute;right: 20rpx;top: 30rpx;z-index: 10000;}
+.notice{background-color: #fff;margin-top: 24rpx;padding: 10rpx 0;}
+.service{
+	.service-item{
+		width: 25%;
+		margin: 24rpx 0;
+		text-align: center;
+		.service-item-img{width: 98rpx;height: 98rpx;}
+	}
+}
+.news-list{
+	.u-body-item{
+		.u-body-item-title{
+			margin-bottom: 15rpx;
+		}
+		.foot{
+			font-size: 24rpx;
+			color: #ddd;
+			.time{
+				margin-left: 24rpx;
+			}
+		}
+	}
+}
+.u-card-wrap { 
+	background-color: $u-bg-color;
+	padding: 1px;
+}
+
+.u-body-item {
+	// font-size: 32rpx;
+	color: #333;
+	padding: 20rpx 10rpx;
+}
+	
+.u-body-item image {
+	width: 120rpx;
+	flex: 0 0 120rpx;
+	height: 120rpx;
+	border-radius: 8rpx;
+	margin-left: 12rpx;
+}

+ 188 - 0
h5_web/pages/index/index.vue

@@ -0,0 +1,188 @@
+<template>
+	<view class="content">
+		<view class="message" @click="openPage('pages/inbuild/inbuild','消息中心')" >
+			<u-icon name="chat" color="#000000" size="48"></u-icon>
+			<u-badge size="mini" type="success" count="7"></u-badge>
+		</view>
+		<u-swiper
+			:list="bannerList"
+			name="bannerUrl"
+			border-radius="0"
+			mode="none"
+			height="427"
+			img-mode="scaleToFill"
+			@click="swiperClick"
+		></u-swiper>
+		<!-- <view class="notice u-flex u-p-l-35 u-p-r-40">
+			<u-image width="113rpx" height="83rpx" src="/static/img/tip.png"></u-image>
+			<view class="notice-wrap u-flex-1">
+				<u-notice-bar
+				@click="noticeClick"
+				mode="vertical" 
+				:list="noticeList" 
+				:more-icon="true" 
+				:volume-icon="false" 
+				bg-color="#fff" 
+				color="#373737"></u-notice-bar>
+			</view>
+		</view> -->
+		<u-card class="service" :border="false" :foot-border-top="false" :full="true" border-radius="0">
+			<view class="service-head" slot="head">
+				<view class="card-head-title">综合服务</view>
+			</view>
+			<view class="service-body u-flex u-flex-wrap u-row-left" slot="body">
+				<view class="service-item" @click="openPage('pages/policyNews/policyNews')">
+					<image class="service-item-img" src="../../static/img/index-service-01.png" mode="aspectFill"></image>
+					<view class="service-item-text">政策资讯</view>
+				</view>
+				<view class="service-item" @click="openPage('pages/inbuild/inbuild','招聘就业')" >
+					<image class="service-item-img" src="../../static/img/index-service-02.png" mode="aspectFill"></image>
+					<view class="service-item-text">招聘就业</view>
+				</view>
+				<view class="service-item" @click="openPage('pages/inbuild/inbuild','合作企业')" >
+					<image class="service-item-img" src="../../static/img/index-service-03.png" mode="aspectFill"></image>
+					<view class="service-item-text">合作企业</view>
+				</view>
+				<view class="service-item" @click="openPage('pages/schools/schools')" >
+					<image class="service-item-img" src="../../static/img/index-service-04.png" mode="aspectFill"></image>
+					<view class="service-item-text">合作院校</view>
+				</view>
+				<view class="service-item" @click="openPage('pages/skillsTraining/skillsTraining')" >
+					<image class="service-item-img" src="../../static/img/index-service-05.png" mode="aspectFill"></image>
+					<view class="service-item-text">技能培训</view>
+				</view>
+				<view class="service-item" @click="openPage('pages/basicTraining/basicTraining')" >
+					<image class="service-item-img" src="../../static/img/index-service-06.png" mode="aspectFill"></image>
+					<view class="service-item-text">适应性培训</view>
+				</view>
+				<view class="service-item" @click="openPage('pages/upgrade/upgrade')" >
+					<image class="service-item-img" src="../../static/img/index-service-07.png" mode="aspectFill"></image>
+					<view class="service-item-text">学历提升</view>
+				</view>
+				<view class="service-item" @click="openPage('pages/inbuild/inbuild','创业指引')" >
+					<image class="service-item-img" src="../../static/img/index-service-08.png" mode="aspectFill"></image>
+					<view class="service-item-text">创业指导</view>
+				</view>
+			</view>
+		</u-card>
+		<u-card class="news-list" 
+		:body-style="{'padding-top':0}"
+		:border="false" 
+		:foot-border-top="false" 
+		:full="true" border-radius="0">
+			<view class="news-list-head" slot="head">
+				<view class="card-head-title">新闻动态</view>
+			</view>
+			<view class="news-list-body" slot="body">
+				<u-nodata notice="暂无新闻" v-if="newsList.length==0"></u-nodata>
+				<view v-for="(item,index) in newsList" 
+				@click="$u.route('/pages/newsDetails/newsDetails',{artId:item.artId})"
+				:key="item.artId" 
+				class="u-body-item u-flex u-border-bottom u-col-between u-row-between">
+					<view class="news-text">
+						<view class="u-body-item-title u-line-2">{{item.artTitle}}</view>
+						<view class="foot u-flex">
+							<view class="">{{item.artCategoryName}}</view>
+							<view class="time">{{$u.timeFormat(item.createTime.replace(/-/g, '/'), 'mm月dd日 hh时MM分')}}</view>
+						</view>
+					</view>
+					<image :src="item.artImage" mode="aspectFill"></image>
+				</view>
+			</view>
+		</u-card>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				paginationConfig:{
+					  pageNo: 1,
+					  pageSize: 10
+				},
+				bannerList: [],
+				newsList:[],
+				noticeList:['贵州轮胎股份有限公司对您感兴趣,为您提供了职位。','您有一个面试邀请'],
+			}
+		},
+		onLoad() {
+			// console.log(this.$u.config.v);
+			
+		},
+		onShow() {
+			this.getBannerList();
+			this.getNewsList();
+		},
+		methods: {
+			/**
+				 * 打开新页面
+				 * @param {String} path 跳转路径
+				 * */
+			openPage(path,msg) {
+			  this.$u.route({
+			    url: path,
+				params:{
+					msg:msg
+				}
+			  })
+			},
+			getNewsList(){
+				let params ={
+					// artCategoryId:2,
+					pageNum:1,
+					pageSize:10
+				}
+				this.$u.api.getIndexNewsList(params)
+				.then(res=>{
+					if(res.code==200){
+						this.newsList = res.rows;
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+					
+					// console.log('newsList',JSON.parse(JSON.stringify(res.rows)));
+				})
+			},
+			getBannerList(){
+				this.$u.api.getIndexBannerList()
+				.then(res=>{
+					this.bannerList = res.data;
+					// console.log('bannerList',JSON.parse(JSON.stringify(res)));
+				})
+			},
+			// 轮播图点击
+			swiperClick (index) {
+				// console.log('swiperClick',index);
+				// console.log('bannerList',this.bannerList);
+				if(this.bannerList[index].jumpUrl){
+					// console.log(this.bannerList[index].jumpUrl);
+					let url = this.bannerList[index].jumpUrl.split('#')[1];
+					// console.log('url',url);
+					this.$u.route({
+						url: url,
+					});
+				}else{
+					this.$u.route({
+						url: 'pages/bannerDetails/bannerDetails',
+						params: {
+						  id: this.bannerList[index].id
+						}
+					})
+				}
+			},
+			noticeClick(e){
+				console.log('e',e)
+				this.openPage('pages/inbuild/inbuild','我的消息')
+			}
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+@import "./index.scss";
+</style>

+ 115 - 0
h5_web/pages/login/code.vue

@@ -0,0 +1,115 @@
+<template>
+	<view class="wrap">
+		<view class="key-input">
+			<view class="title">输入验证码</view>
+			<view class="tips">验证码已发送至 +{{tel}}</view>
+			<u-message-input :focus="true" :value="value" @change="change" @finish="finish" mode="bottomLine" :maxlength="maxlength"></u-message-input>
+			<text :class="{ error: error }">验证码错误,请重新输入</text>
+			<view class="captcha">
+				<text :class="{ noCaptcha: show }" @tap="noCaptcha">收不到验证码点这里</text>
+				<text :class="{ regain: !show }">{{ second }}秒后重新获取验证码</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			maxlength: 4,
+			value: '',
+			second: 60,
+			show: false,
+			error: false,
+			tel: ''
+		};
+	},
+	computed: {},
+	onLoad(page) {
+		console.log(page)
+		this.tel = page.tel
+		// this.getCaptcha()
+		let interval = setInterval(() => {
+			this.second--;
+			if (this.second <= 0) {
+				this.show = true;
+				if (this.value.lenth != 4) {
+					this.error = true;
+				}
+				clearInterval(interval);
+			}
+		}, 1000);
+	},
+	methods: {
+		// 收不到验证码选择时的选择
+		noCaptcha() {
+			uni.showActionSheet({
+				itemList: ['重新获取验证码', '接听语音验证码'],
+				success: function(res) {
+					
+				},
+				fail: function(res) {
+					
+				}
+			});
+		},
+		// change事件侦听
+		change(value) {
+			// console.log('change', value);
+		},
+		// 输入完验证码最后一位执行
+		finish(value) {
+			// console.log('finish', value);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.wrap {
+	padding: 80rpx;
+}
+
+.box {
+	margin: 30rpx 0;
+	font-size: 30rpx;
+	color: 555;
+}
+
+.key-input {
+	padding: 30rpx 0;
+	text {
+		display: none;
+	}
+	.error {
+		display: block;
+		color: red;
+		font-size: 30rpx;
+		margin: 20rpx 0;
+	}
+}
+
+.title {
+	font-size: 50rpx;
+	color: #333;
+}
+
+.key-input .tips {
+	font-size: 30rpx;
+	color: #333;
+	margin-top: 20rpx;
+	margin-bottom: 60rpx;
+}
+.captcha {
+	color: $u-type-warning;
+	font-size: 30rpx;
+	margin-top: 40rpx;
+	.noCaptcha {
+		display: block;
+	}
+	.regain {
+		display: block;
+	}
+}
+</style>

+ 133 - 0
h5_web/pages/login/login.vue

@@ -0,0 +1,133 @@
+<template>
+	<view class="wrap">
+		<view class="top"></view>
+		<view class="content">
+			<view class="title">欢迎登录XX</view>
+			<input class="u-border-bottom" type="number" v-model="tel" placeholder="请输入手机号" />
+			<view class="tips">未注册的手机号验证后自动创建XX账号</view>
+			<button @tap="submit" :style="[inputStyle]" class="getCaptcha">获取短信验证码</button>
+			<view class="alternative">
+				<view class="password">密码登录</view>
+				<view class="issue">遇到问题</view>
+			</view>
+		</view>
+		<view class="buttom">
+			<view class="loginType">
+				<view class="wechat item">
+					<view class="icon"><u-icon size="70" name="weixin-fill" color="rgb(83,194,64)"></u-icon></view>
+					微信
+				</view>
+				<view class="QQ item">
+					<view class="icon"><u-icon size="70" name="qq-fill" color="rgb(17,183,233)"></u-icon></view>
+					QQ
+				</view>
+			</view>
+			<view class="hint">
+				登录代表同意
+				<text class="link">XXXX用户协议、隐私政策,</text>
+				并授权使用您的XXXX账号信息(如昵称、头像、收获地址)以便您统一管理
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			tel: ''
+		}
+	},
+	computed: {
+		inputStyle() {
+			let style = {};
+			if(this.tel) {
+				style.color = "#fff";
+				style.backgroundColor = this.$u.color['warning'];
+			}
+			return style;
+		}
+	},
+	methods: {
+		submit() {
+			if(this.$u.test.mobile(this.tel)) {
+				this.$u.route({
+					url: 'pages/login/code',
+					params: {
+						tel: this.tel
+					}
+				})
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.wrap {
+	font-size: 28rpx;
+	.content {
+		width: 600rpx;
+		margin: 80rpx auto 0;
+
+		.title {
+			text-align: left;
+			font-size: 60rpx;
+			font-weight: 500;
+			margin-bottom: 100rpx;
+		}
+		input {
+			text-align: left;
+			margin-bottom: 10rpx;
+			padding-bottom: 6rpx;
+		}
+		.tips {
+			color: $u-type-info;
+			margin-bottom: 60rpx;
+			margin-top: 8rpx;
+		}
+		.getCaptcha {
+			background-color: rgb(253, 243, 208);
+			color: $u-tips-color;
+			border: none;
+			font-size: 30rpx;
+			padding: 12rpx 0;
+			
+			&::after {
+				border: none;
+			}
+		}
+		.alternative {
+			color: $u-tips-color;
+			display: flex;
+			justify-content: space-between;
+			margin-top: 30rpx;
+		}
+	}
+	.buttom {
+		.loginType {
+			display: flex;
+			padding: 350rpx 150rpx 150rpx 150rpx;
+			justify-content:space-between;
+			
+			.item {
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				color: $u-content-color;
+				font-size: 28rpx;
+			}
+		}
+		
+		.hint {
+			padding: 20rpx 40rpx;
+			font-size: 20rpx;
+			color: $u-tips-color;
+			
+			.link {
+				color: $u-type-warning;
+			}
+		}
+	}
+}
+</style>

+ 159 - 0
h5_web/pages/mine/mine.vue

@@ -0,0 +1,159 @@
+<template>
+	<view class="mine">
+		<u-navbar :is-back="false" title=" " :border-bottom="false">
+			<view class="u-flex u-row-right" style="width: 100%;">
+				<!-- <view class="camera u-flex u-row-center">
+					<u-icon name="camera-fill" color="#000000" size="48"></u-icon>
+				</view> -->
+			</view>
+		</u-navbar>
+		<view class="u-flex user-box u-p-l-30 u-p-r-20 u-p-b-30">
+			<view class="u-m-r-10">
+				<u-avatar :src="memberinfo.avatar||pic" size="140"></u-avatar>
+			</view>
+			<view class="u-flex-1">
+				<view class="u-font-18 u-p-b-20">{{memberinfo.name}}</view>
+				<view class="u-font-14 u-tips-color">
+				军人认证状态:
+				{{memberinfo.auditStatus|verifyStatusFilter}}
+				<text v-if="memberinfo.auditStatus==1||memberinfo.auditStatus==9" @click="openPage('pages/authentication/authentication')" style="margin-left: 24rpx;">去认证</text>
+			</view>
+			</view>
+			<view class="u-m-l-10 u-p-10">
+				<u-icon name="arrow-right" color="#969799" size="28" @click="openPage('pages/inbuild/inbuild','个人信息')"></u-icon>
+			</view>
+		</view>
+		<view class="mine-todo u-flex u-row-between">
+			<view class="mine-todo-item" @click="openPage('pages/inbuild/inbuild','我的投递')">
+				<view class="num">25</view>
+				<view class="text">我的投递</view>
+			</view>
+			<view class="mine-todo-item" @click="openPage('pages/inbuild/inbuild','我的面试')">
+				<view class="num">18</view>
+				<view class="text">我的面试</view>
+			</view>
+			<view class="mine-todo-item" @click="openPage('pages/inbuild/inbuild','关注企业')">
+				<view class="num">6</view>
+				<view class="text">关注企业</view>
+			</view>
+			<view class="mine-todo-item" @click="openPage('pages/inbuild/inbuild','消息通知')">
+				<view class="num">8</view>
+				<view class="text">消息通知</view>
+			</view>
+		</view>
+		
+		<view class="u-m-t-20">
+			<u-cell-group>
+				<u-cell-item icon="order" title="电子简历" @click="openPage('pages/inbuild/inbuild','电子简历')" ></u-cell-item>
+				<u-cell-item icon="heart" title="求职意向" @click="openPage('pages/inbuild/inbuild','求职意向')"></u-cell-item>
+			</u-cell-group>
+		</view>
+		
+		<view class="u-m-t-20">
+			<u-cell-group>
+				<u-cell-item icon="cut" title="我的技能培训" @click="openPage('pages/inbuild/inbuild','我的技能培训')"></u-cell-item>
+				<u-cell-item icon="photo" title="我的学历提升" @click="openPage('pages/inbuild/inbuild','我的学历提升')"></u-cell-item>
+				<u-cell-item icon="coupon" title="我的适应性培训" @click="openPage('pages/basicTraining/myBasicTraining/myBasicTraining')"></u-cell-item>
+				<u-cell-item icon="edit-pen" title="我的考试考核" @click="openPage('pages/inbuild/inbuild','我的考试考核')"></u-cell-item>
+				<u-cell-item icon="android-circle-fill" title="我的班级" @click="openPage('pages/inbuild/inbuild','我的班级')"></u-cell-item>
+				<u-cell-item icon="file-text" title="行为积分" @click="openPage('pages/inbuild/inbuild','行为积分')"></u-cell-item>
+			</u-cell-group>
+		</view>
+		
+		<view class="u-m-t-20">
+			<u-cell-group>
+				<u-cell-item icon="setting" title="设置" @click="openPage('pages/inbuild/inbuild','设置')"></u-cell-item>
+				<u-cell-item icon="level" title="注销登录" @click="loginOut"></u-cell-item>
+			</u-cell-group>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				pic:'/static/img/default-avatar.png',
+				show:true,
+				veteEducStatus:null,
+				memberinfo:[],
+			}
+		},
+		onLoad() {
+			
+			// this.veteEducCheck();
+		},
+		onShow() {
+			this.getmemberinfo();
+		},
+		filters: {
+		  verifyStatusFilter(value) {
+			if (value === 0) {
+			  return '审核中';
+			}else if(value === 1){
+				return '不通过';
+			}else if(value === 2){
+				return '通过';
+			}else if(value === 9){
+				return '未认证';
+			} else {
+			  return '未知';
+			}
+		  },
+		},
+		methods: {
+			openPage(path,msg) {
+			  this.$u.route({
+			    url: path,
+				params:{
+					msg:msg
+				}
+			  })
+			},
+			veteEducCheck(){
+				this.$u.api.getVeteEducCheck().then(res=>{
+					// console.log('res',res);
+					this.veteEducStatus = res.data;
+				})
+			},
+			getmemberinfo(){
+				this.$u.api.getmemberinfo().then(res=>{
+					this.memberinfo = res.data;
+					console.log('this.memberinfo',JSON.parse(JSON.stringify(this.memberinfo)));
+				})
+			},
+			loginOut(){
+				let that = this;
+				this.$u.vuex('vuex_hasLogin', false);
+				this.$u.vuex('vuex_token', '');
+				this.$u.vuex('vuex_user', '');
+				this.$u.route('/pages/phoneLogin/phoneLogin');
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+page{
+	background-color: #ededed;	
+}
+
+.camera{
+	width: 54px;
+	height: 44px;
+	
+	&:active{
+		background-color: #ededed;
+	}
+}
+.user-box{
+	background-color: #fff;
+}
+.mine /deep/ .u-cell_title{font-size: 30rpx;}
+.mine /deep/ .u-iconfont{font-size: 45rpx!important;}
+.mine-todo{
+	background-color: #fff;
+	text-align: center;
+	padding: 0 24rpx 24rpx;
+}
+</style>

+ 111 - 0
h5_web/pages/mycode/mycode.vue

@@ -0,0 +1,111 @@
+	<!-- 0审核中,1不通过,2通过,9未提交 -->
+<template>
+	<view>
+		<image class="bg" src="../../static/img/mycode-bg.png" mode="widthFix"></image>
+		<view class="content" style="padding-top: 20vh;">
+			<view class="qrimg">
+			    <tki-qrcode
+			    ref="qrcode"
+			    cid="cid"
+				:showLoading="false"
+			    :val="memberinfo.id"
+			    :size="300"
+			    background="#000"
+			    foreground="#fff"
+			    pdground="#fff"
+				:icon="icon"
+				:iconSize="40"
+				:usingComponents="true"
+			    :onval="true"
+			    @result="qrR" />
+			</view>
+		</view>
+		<view class="code-text" v-show="memberinfo.auditStatus==2">
+			<view class="code-id">{{memberinfo.id}}</view>
+			<view class="thanks">
+				{{memberinfo.name}}{{memberinfo.menSex|verifySexFilter}},感谢您为国家的付出
+			</view>
+			<view v-if="!memberinfo.id">还没有退役军人码</view>
+		</view>
+		<view class="content fixed" v-if="memberinfo.auditStatus==9">
+			<!-- 9未提交 -->
+			<view class="tip">
+				提交退役军人身份审核,获取退役军人码,并享受平台提供的专享培训、就业服务。
+			</view>
+		</view>
+		<view class="content fixed" v-else-if="memberinfo.auditStatus==0">
+				<!-- 0审核中 -->
+			<view class="tip">
+				您已提交退役军人身份审核,通过后生成退役军人码,并可享受平台提供的专享培训、就业服务,请耐心等待。
+			</view>
+		</view>
+		<view class="content fixed" v-else-if="memberinfo.auditStatus==1">
+			<!-- 1不通过 -->
+			<view class="tip">
+				很抱歉,您提交的退役军人身份信息未通过审核,请审查后重新提交。
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import tkiQrcode from "@/components/tki-qrcode/tki-qrcode.vue"
+	export default{
+		components: {tkiQrcode},
+		data(){
+			return{
+				memberinfo:[],
+				icon:'/static/icons/index.png'
+			}
+		},
+		onLoad(page){
+			
+		},
+		onShow(){
+			this.getmemberinfo();
+		},
+		filters:{
+			verifySexFilter(value){
+			  if (value == 0) {
+				return '先生';
+			  }else if(value == 1){
+				return '女士';
+			  }
+			 }
+		},
+		methods:{
+			getmemberinfo(){
+				this.$u.api.getmemberinfo().then(res=>{
+					if(res.code==200){
+						this.memberinfo = res.data;
+						if(this.memberinfo.auditStatus!==2){
+							this.memberinfo.id = null
+						}
+						console.log('this',this)
+						console.log('this.memberinfo',JSON.parse(JSON.stringify(this.memberinfo)));
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+				})
+			},
+			qrR(e){
+				// console.log(e)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.bg{z-index: -1;width: 100%;height: auto;position: fixed;left: 0;top: 100rpx;opacity: 0.2;}
+	.qrimg{min-height: 100rpx;}
+	.content{text-align: center;padding: 60vh 24rpx 24rpx;}
+	.content.fixed{position: absolute;left: 0;top: 60vh;right: 0;padding-top: 0;}
+	.content .tip{font-size: 36rpx;margin: 0 24rpx;text-align: left;text-indent: 2em;}
+	.code-text{margin: 24rpx auto;text-align: center;}
+	.code-id{font-size: 36rpx;font-weight: bold;}
+	.thanks{margin-top: 100rpx;}
+	// @import  './mycode.scss'
+</style>

+ 56 - 0
h5_web/pages/newsDetails/newsDetails.vue

@@ -0,0 +1,56 @@
+<template>
+	<view class="ql-editor">
+		<u-parse :html="content"></u-parse>
+		<u-toast ref="uToast" />
+	</view>
+</template>
+
+<script>
+	export default{
+		data(){
+			return{
+				content:''
+			}
+		},
+		onLoad(page){
+			if (page.artId) {
+				this.getDetails(page.artId)
+			}
+		},
+		onShow(){
+			
+		},
+		methods:{
+			// 获取详情
+			getDetails(id) {
+				this.$u.api.getNewsDetails({id:id})
+				.then(res=>{
+					console.log('getNewsDetails',JSON.parse(JSON.stringify(res)));
+					if (res.code === 200) {
+						console.log(res.data);
+						this.content = res.data.artContent;
+					} else {
+						this.$refs.uToast.show({
+							title: res.msg,
+							type: 'error'
+						})
+					}
+				})
+				.catch(err => {
+					this.$refs.uToast.show({
+						title: '操作失败!',
+						type: 'error'
+					})
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	// page{padding: 24rpx;}
+	// @import  './newsDetails.scss'
+	@import  '@/static/css/quill.bubble.scss';
+	@import  '@/static/css/quill.core.scss';
+	@import  '@/static/css/quill.snow.scss';
+</style>

+ 319 - 0
h5_web/pages/phoneLogin/phoneLogin.vue

@@ -0,0 +1,319 @@
+<template>
+	<view class="wrap">
+		<view class="content">
+			<view class="title">手机号登录</view>
+			<input class="u-border-bottom" type="number" maxlength="11" v-model="tel" placeholder="请输入手机号" />
+			<view class="u-text-center u-type-error u-m-t-20" v-if="telError">手机号输入错误</view>
+			<button @tap="submit" :style="[inputStyle]" class="getCaptcha">获取短信验证码</button>
+			<u-message-input v-if="show" :focus="true" :value="messageCode" @change="change" @finish="finish" mode="bottomLine" :maxlength="codelength"></u-message-input>
+		</view>
+		<!-- <view class="u-text-center u-type-error" v-if="phoneError">手机号输入错误</view> -->
+		<view class="captcha">
+			<!-- <text v-if="show&&this.messageDisable==false" @tap="noCaptcha">收不到验证码点这里</text> -->
+			<text v-if="messageShow">{{ second }}秒后可重新获取验证码</text>
+		</view>
+		<view class="buttom">
+			<view class="hint u-text-center">
+				登录代表同意
+				<text class="link" @tap="jumpToPage(1)">《用户服务条款》</text>和
+				<text class="link" @tap="jumpToPage(2)">《隐私政策》</text>
+				并授权使用您的账号信息(如昵称、头像、收货地址)以便您统一管理
+			</view>
+		</view>
+		<u-toast ref="uToast" />
+	</view>
+</template>
+
+<script>
+import getUrlParams from "utils/getUrlParams.js";
+export default {
+	data() {
+		return {
+			tel: '',
+			messageCode:'',
+			messageShow: false,
+			messageDisable: false,
+			codelength: 4,
+			show: false,
+			second:60,
+			toastMsg:'',
+			toastUrl:'',
+			toastType:'',
+			accessToken:'',
+			userId:'',
+			telError:false,
+			// messageError:false
+		}
+	},
+	onLoad(page) {
+		//  如果存在code 则认为是微信登录完成后跳转过来的,直接获取信息跳转首页或者指定页面
+		let locationLocaturl = window.location.search;
+		this.code = getUrlParams(locationLocaturl, "code"); // 截取code
+		if (this.code) {
+			this.handleGetWXInfo(this.code) //把code传给后台获取用户信息
+		}
+		console.log('backUrl',localStorage.getItem('backUrl'))
+	},
+	onShow() {
+		if(this.vuex_hasLogin){
+			// uni.switchTab({
+			// 	url: '../index/index'
+			// })
+		}
+	},
+	computed: {
+		inputStyle() {
+			let style = {};
+			if(this.tel.length == 11&&this.messageDisable==false&&this.$u.test.mobile(this.tel)) {
+				style.color = "#fff";
+				style.backgroundColor = '#5295F5';
+				this.telError = false;
+				// style.backgroundColor = this.$u.color['warning'];
+			}else if(this.tel.length==11&&!this.$u.test.mobile(this.tel)){
+				this.telError = true;
+			}
+			return style;
+		}
+	},
+	methods: {
+		showToast() {
+			this.$refs.uToast.show({
+				title: this.toastMsg,
+				type: this.toastType,
+				url: this.toastUrl
+			})
+		},
+		submit() {
+			if(this.$u.test.mobile(this.tel)&&this.messageDisable==false) {
+				let that = this;
+				this.$u.api.getPhoneLoginCode({mobile:this.tel})
+				.then(res =>{
+					if(res.code == 200){
+						this.messageDisable = true;
+						this.messageShow = true;
+						this.show = true;
+						let interval = setInterval(() => {
+							that.second--;
+							if (that.second <= 0) {						
+								that.messageDisable=false
+								that.messageShow = false;
+								if (that.messageCode.lenth != 4) {
+									// this.messageError = true;
+								}
+								clearInterval(interval);
+								that.second=60;
+							}
+						}, 1000);
+						this.accessToken = res.data.accessToken;
+						this.$u.vuex('vuex_token', res.data.accessToken);
+						this.userId = res.data.userId;
+					}else{
+						uni.showToast({
+						    title: res.msg,
+							icon:'none',
+						    duration: 2000
+						});
+					}
+					
+				}).catch(err=>{
+					this.toastMsg = err.code + ":" + err.msg;
+					this.showToast();
+				});
+			}
+		},
+		// 收不到验证码选择时的选择
+		// noCaptcha() {
+		// 	uni.showActionSheet({
+		// 		itemList: ['重新获取验证码', '接听语音验证码'],
+		// 		success: function(res) {
+					
+		// 		},
+		// 		fail: function(res) {
+					
+		// 		}
+		// 	});
+		// },
+		// change事件侦听
+		change(value) {
+			// console.log('change', value);
+		},
+		// 输入完验证码最后一位执行
+		finish(value) {
+			let params = {
+				accessToken:this.accessToken,
+				userId:this.userId,
+				code:value
+			};
+			this.$u.api.phoneLoginAuth(params)
+			.then(res =>{
+				if(res.code=='200'){
+					// console.log('finish res',res);
+					this.$u.vuex('vuex_user', res.data);
+					this.$u.vuex('vuex_hasLogin', true);
+					this.veteEducCheck();
+					// this.wechatLogin()
+					
+				}else{
+					this.toastMsg = res.msg;
+					this.showToast();
+				}
+			}).catch(err=>{
+				this.toastMsg = err.msg;
+				this.showToast();
+			});
+		},
+		// 微信登录
+		wechatLogin() {
+			this.jumpIndex()
+			// const openId = this.$store.state.vuex_wxinfo?.openId
+			// if (openId) {
+			// 	this.jumpIndex()
+			// } else {
+			// 	this.getCode()
+			// }
+		},
+		veteEducCheck(){
+			this.$u.api.getVeteEducCheck().then(res=>{
+				console.log('res',res);
+				if(res.data!=9){
+					this.jumpIndex();
+				}else{
+					uni.navigateTo({
+						url: '/pages/authentication/authentication'
+					})
+				}
+			})
+		},
+		// 微信已登录则跳转到首页
+		jumpIndex() {
+			let ret = localStorage.getItem('backUrl')
+			if (ret && ret.indexOf('phoneLogin') < 0) {
+				// 截取url
+				const pagesIndex = ret.indexOf('pages')
+				let switchTabList = ['/pages/index/index','pages/center/center','pages/mine/mine'];
+				if (pagesIndex > (-1)) {
+					const pageUrl = ret.slice(pagesIndex)
+					if(switchTabList.includes(pageUrl)){
+						setTimeout(() => {
+							uni.switchTab({
+								url: '/' + pageUrl
+							})
+						}, 100)
+					}else{
+						setTimeout(() => {
+							uni.navigateTo({
+								url: '/' + pageUrl
+							})
+						}, 100)
+					}
+					
+				} else {
+					uni.switchTab({
+						url: '../index/index'
+					})
+				}
+			} else {
+				uni.switchTab({
+					url: '../index/index'
+				})
+			}
+		},
+		// 获取code
+		getCode () {
+			const local = window.location.href // 获取页面url
+			let locationLocaturl = window.location.search;
+			this.code = getUrlParams(locationLocaturl, "code"); // 截取code
+			if (this.code) { // 如果没有code,则去请求
+				this.handleGetWXInfo(this.code) //把code传给后台获取用户信息
+			} else {
+				window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${this.config.wxAppid}&redirect_uri=${encodeURIComponent(local)}&response_type=code&scope=snsapi_userinfo&#wechat_redirect`
+			}
+		},
+		// 通过code获取 openId等用户信息,/api/user/wechat/login 为后台接口
+		handleGetWXInfo (code) {
+			uni.showLoading({
+			    title: '加载中'
+			})
+			this.$u.api.getWXInfo(code).then((res) => {
+				if (res.code === 200 ) {
+					this.$u.vuex('vuex_wxinfo', res.data);
+					this.jumpIndex()
+				} else {
+					this.$refs.uToast.show({
+						title: '获取用户信息失败!',
+						type: 'error',
+					});
+				}
+				uni.hideLoading()
+			}).catch((err) => {
+				this.$refs.uToast.show({
+					title: '获取用户信息失败!',
+					type: 'error',
+				});
+			})
+		},
+		/**
+		 * 跳转页面
+		 * */
+		jumpToPage(flag){
+			uni.navigateTo({
+				url: "/pages/inbuild/inbuild",
+				// url: "/pages/privacyPolicy/privacyPolicy?termsType=" + flag,
+			})
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.hide{display: none!important;}
+.wrap {
+	font-size: 28rpx;
+	.content {
+		width: 600rpx;
+		margin: 80rpx auto 0;
+
+		.title {
+			text-align: left;
+			font-size: 60rpx;
+			font-weight: 500;
+			margin-bottom: 100rpx;
+		}
+		input {
+			text-align: left;
+			margin-bottom: 10rpx;
+			padding-bottom: 20rpx;
+			border-bottom: 1px solid #ddd;
+		}
+		.getCaptcha {
+			margin: 45rpx auto 130rpx;
+			background-color: #a8c6f1;
+			color: $u-tips-color;
+			border: none;
+			font-size: 30rpx;
+			padding: 12rpx 0;
+			
+			&::after {
+				border: none;
+			}
+		}
+	}
+	.buttom {
+		.hint {
+			padding: 20rpx 40rpx;
+			font-size: 20rpx;
+			color: $u-tips-color;
+			
+			.link {
+				color: $u-type-warning;
+			}
+		}
+	}
+	.captcha {
+		color: $u-type-warning;
+		font-size: 30rpx;
+		margin-top: 40rpx;
+		text-align: center;
+	}
+}
+</style>

+ 30 - 0
h5_web/pages/policyNews/policyNews.scss

@@ -0,0 +1,30 @@
+.news-list-head{display: none;}
+.u-card-wrap { 
+	background-color: $u-bg-color;
+	padding: 1px;
+}
+
+.u-body-item {
+	// font-size: 32rpx;
+	color: #333;
+	padding: 20rpx 10rpx;
+	.u-body-item-title{
+		margin-bottom: 15rpx;
+		font-size: 30rpx;
+	}
+	.foot{
+		font-size: 24rpx;
+		color: #ddd;
+		.time{
+			margin-left: 24rpx;
+		}
+	}
+}
+	
+.u-body-item image {
+	width: 120rpx;
+	flex: 0 0 120rpx;
+	height: 120rpx;
+	border-radius: 8rpx;
+	margin-left: 12rpx;
+}

+ 166 - 0
h5_web/pages/policyNews/policyNews.vue

@@ -0,0 +1,166 @@
+<template>
+	<view>
+		<u-swiper
+			:list="bannerList"
+			name="bannerUrl"
+			border-radius="0"
+			mode="none"
+			height="427"
+			:title="true"
+			:effect3d="false"
+			img-mode="scaleToFill"
+			@click="swiperClick"
+		></u-swiper>
+		<u-tabs :list="tabs" :is-scroll="false" name="label" :current="tabIndex" @change="tabChange"></u-tabs>
+		<!-- <me-tabs v-model="tabIndex" :tabs="tabs" @change="tabChange"></me-tabs> -->
+		<!-- top="xxx"下拉布局往下偏移,防止被悬浮菜单遮住 -->
+		 <mescroll-uni ref="mescrollRef" @init="mescrollInit" top="500" @down="downCallback" :up="upOption" @up="upCallback" @emptyclick="emptyClick">
+			<!-- 数据列表 -->
+			<u-card class="news-list"
+			:body-style="{'padding-top':0}"
+			:head-style="{'padding':0}"
+			:border="false" 
+			:foot-border-top="false" 
+			:full="true" border-radius="0">
+				<view class="news-list-head" slot="head">
+					<view class="">新闻动态</view>
+				</view>
+				<view class="news-list-body" slot="body">
+					<view v-for="(item,index) in newsList" 
+					@click="$u.route('/pages/policyNewsDetails/policyNewsDetails',{artId:item.artId})"
+					:key="item.artId" 
+					class="u-body-item u-flex u-border-bottom u-col-between u-row-between">
+						<view class="news-text">
+							<view class="u-body-item-title u-line-2">{{item.artTitle}}</view>
+							<view class="foot u-flex">
+								<view class="">{{item.artCategoryName}}</view>
+								<view class="time">{{$u.timeFormat(item.createTime.replace(/-/g, '/'), 'mm月dd日 hh时MM分')}}</view>
+							</view>
+						</view>
+						<image :src="item.artImage" mode="aspectFill"></image>
+					</view>
+				</view>
+			</u-card>
+		</mescroll-uni>
+	</view>
+</template>
+
+<script>
+	import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
+	export default{
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
+		data(){
+			return{
+				bannerList: [],
+				upOption:{
+					// page: {
+					// 	size: 10 // 每页数据的数量
+					// },
+					auto:false,
+					// use:false,
+					noMoreSize: 4, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
+					empty:{
+						tip: '~ 暂无数据 ~', // 提示
+						// btnText: '去看看'
+					}
+				},
+				newsList: [], //列表数据
+				tabs: [],
+				tabIndex: 0, // tab下标
+			}
+		},
+		onLoad(){
+			this.getPolicyType();
+			this.getBannerList();
+		},
+		onShow(){
+			
+		},
+		methods:{
+			// 获取新闻类别
+			getPolicyType(){
+				this.$u.api.getDictdataUrl({key:'policy_type'}).then(res=>{
+					if(res.code == 200){
+						// console.log('getPolicyType',res)
+						this.tabs = res.data;
+						this.mescroll.resetUpScroll();
+						console.log('this.tabs',this.tabs)
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+				});
+			},
+			getBannerList(){
+				this.$u.api.getIndexBannerList()
+				.then(res=>{
+					this.bannerList = res.data;
+					// console.log('bannerList',JSON.parse(JSON.stringify(res)));
+				})
+			},
+			// 轮播图点击
+			swiperClick (index) {
+				// console.log('swiperClick',index);
+				// console.log('bannerList',this.bannerList);
+				if(this.bannerList[index].jumpUrl){
+					// console.log(this.bannerList[index].jumpUrl);
+					let url = this.bannerList[index].jumpUrl.split('#')[1];
+					// console.log('url',url);
+					this.$u.route({
+						url: url,
+					});
+				}else{
+					this.$u.route({
+						url: 'pages/bannerDetails/bannerDetails',
+						params: {
+						  id: this.bannerList[index].id
+						}
+					})
+				}
+			},
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
+			upCallback(page) {
+				// console.log('page',page)
+				//联网加载数据
+				let keyword = this.tabs[this.tabIndex]?.text;
+				let params ={
+					artCategoryId:keyword,
+					pageNum:page.num,
+					pageSize:page.size
+				} 
+				this.$u.api.getpolicyNewsList(params).then(curPageData=>{
+					// console.log('curPageData',curPageData)
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
+					this.mescroll.endSuccess(curPageData.rows.length);
+					//设置列表数据
+					if(page.num == 1) this.newsList = []; //如果是第一页需手动制空列表
+					this.newsList=this.newsList.concat(curPageData.rows); //追加新数据
+				}).catch((err)=>{
+					uni.showToast({
+						title:'链接失败'
+					});
+					console.log('err',err)
+					//联网失败, 结束加载
+					this.mescroll.endErr();
+				})
+			},
+			// 切换菜单
+			tabChange(index) {
+				this.newsList = [];// 先置空列表,显示加载进度
+				this.tabIndex = index;
+				this.mescroll.resetUpScroll(); // 再刷新列表数据
+				
+			},
+			//点击空布局按钮的回调
+			emptyClick(){
+				
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import  './policyNews.scss'
+</style>

+ 56 - 0
h5_web/pages/policyNewsDetails/policyNewsDetails.vue

@@ -0,0 +1,56 @@
+<template>
+	<view class="ql-editor">
+		<u-parse :html="content"></u-parse>
+		<u-toast ref="uToast" />
+	</view>
+</template>
+
+<script>
+	export default{
+		data(){
+			return{
+				content:''
+			}
+		},
+		onLoad(page){
+			if (page.artId) {
+				this.getDetails(page.artId)
+			}
+		},
+		onShow(){
+			
+		},
+		methods:{
+			// 获取详情
+			getDetails(id) {
+				this.$u.api.getPolicyNewsDetails({id:id})
+				.then(res=>{
+					console.log('getNewsDetails',JSON.parse(JSON.stringify(res)));
+					if (res.code === 200) {
+						console.log(res.data);
+						this.content = res.data.artContent;
+					} else {
+						this.$refs.uToast.show({
+							title: res.msg,
+							type: 'error'
+						})
+					}
+				})
+				.catch(err => {
+					this.$refs.uToast.show({
+						title: '操作失败!',
+						type: 'error'
+					})
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	// page{padding: 24rpx;}
+	// @import  './policyNewsDetails.scss'
+	@import  '@/static/css/quill.bubble.scss';
+	@import  '@/static/css/quill.core.scss';
+	@import  '@/static/css/quill.snow.scss';
+</style>

+ 64 - 0
h5_web/pages/privacyPolicy/privacyPolicy.vue

@@ -0,0 +1,64 @@
+<!-- ============================= 隐私政策/用户服务条款 ============================= -->
+<template>
+	<view class="u-content">
+		<u-parse :html="content"></u-parse>
+		<u-toast ref="uToast" />
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				content: ''
+			}
+		},
+		onLoad(query) {
+			const termsType = query?.termsType
+			if (termsType) {
+				switch (Number(termsType)) {
+					case 1:
+						uni.setNavigationBarTitle({
+							title: '用户服务条款'
+						})
+						break
+					case 2:
+						uni.setNavigationBarTitle({
+							title: '隐私政策'
+						})
+						break
+				}
+				this.getSysterms(termsType)
+			}
+		},
+		methods: {
+			getSysterms(termsType) {
+				this.$u.api.getSysterms({
+						termsType: Number(termsType)
+					})
+					.then(res => {
+						if (res.code === 200) {
+							this.content = res.data?.content
+						} else {
+							this.$refs.uToast.show({
+								title: res.msg,
+								type: 'error',
+							})
+						}
+					})
+					.catch(err => {
+						this.$refs.uToast.show({
+							title: '系统错误!',
+							type: 'error',
+						})
+					})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.u-content {
+	padding: 20rpx;
+}
+</style>

+ 156 - 0
h5_web/pages/schools/schools.vue

@@ -0,0 +1,156 @@
+<template>
+	<view>
+		<!-- <u-empty text="此功能正在建设中" margin-top="400" src="/static/img/empty.png"></u-empty> -->
+		<u-search height="80" class="top-search" placeholder="快速搜索学院" v-model="keyword" @search="search" @custom="search" @clear="search"></u-search>
+		<!-- <u-cell-group title="教育基地">
+			<u-cell-item 
+			@click="schoolClick(item)"
+			v-for="(item,index) in schoolList" 
+			:key='item.id' 
+			:label='item.number+"个专业 | "+item.schoolNature|verifySchoolNatureFilter'
+			:title="item.schoolName">
+				<image class="cell-icon" :src="item.schoolLogoUrl" slot="icon" mode=""></image>
+			</u-cell-item>
+		</u-cell-group> -->
+		<mescroll-uni ref="mescrollRef" @init="mescrollInit" top="150" @down="downCallback" :up="upOption" @up="upCallback" @emptyclick="emptyClick">
+			<view class="school-item u-flex u-row-between" @click="schoolClick(item)" v-for="(item,index) in schoolList" :key='item.id' >
+				<view class=" u-flex">
+					<image class="school-item-img" :src="item.schoolLogoUrl" mode="scaleToFill"></image>
+					<view class="school-item-info" style="display: inline-block;">
+						<view class="up">
+							{{item.schoolName}}
+						</view>
+						<view class="down">
+							<!-- {{item.count}}个专业 | -->
+							{{item.schoolNature|verifySchoolNatureFilter}}
+						</view>
+					</view>
+				</view>
+				<view class="arrow">
+					<u-icon name="arrow-right" color="#969799" size="28"></u-icon>
+				</view>
+			</view>
+		</mescroll-uni>
+	</view>
+</template>
+
+<script>
+	import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
+	// import mock from "../../static/js/mock.js";
+	export default{
+		mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
+		data(){
+			return{
+				keyword:'',
+				schoolList:[],
+				upOption:{
+					// page: {
+					// 	size: 10 // 每页数据的数量
+					// },
+					// auto:false,
+					// use:false,
+					noMoreSize: 4, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
+					empty:{
+						tip: '~ 暂无数据 ~', // 提示
+						// btnText: '去看看'
+					}
+				},
+			}
+		},
+		onLoad(){
+			
+		},
+		onShow(){
+			
+		},
+		filters:{
+			verifySchoolNatureFilter(value){
+			  if (value == 1) {
+				return '公办';
+			  }else if(value == 2){
+				return '民办';
+			  }
+			 }
+		},
+		methods:{
+			search(value){
+				console.log('value',value);
+				this.mescroll.resetUpScroll();
+			},
+			/*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
+			upCallback(page) {
+				// console.log('page',page)
+				//联网加载数据
+				let params ={
+					schoolName:this.keyword,
+					pageNum:page.num,
+					pageSize:page.size
+				} 
+				this.$u.api.getSchoolList(params).then(curPageData=>{
+					// console.log('curPageData',curPageData)
+					//联网成功的回调,隐藏下拉刷新和上拉加载的状态;
+					this.mescroll.endSuccess(curPageData.rows.length);
+					//设置列表数据
+					if(page.num == 1) this.schoolList = []; //如果是第一页需手动制空列表
+					this.schoolList=this.schoolList.concat(curPageData.rows); //追加新数据
+				}).catch((err)=>{
+					uni.showToast({
+						title:'链接失败'
+					});
+					console.log('err',err)
+					//联网失败, 结束加载
+					this.mescroll.endErr();
+				})
+			},
+			schoolClick(item){
+				console.log('schoolClick item',item);
+				let analysisParams ={
+					platform:'1',//平台:1-H5 2-APP 3-小程序 4-PC端
+					pages:location.href,//页面路径
+					btnName:'',//按钮名称
+					btnEvent:'1',//按钮事件: 1-点击 2-长按 3-滑动
+					ipAddress:'',//IP地址
+					typeName:'学校关注度',//类型名称 例:学校关注度 、适应性考试等
+					typeCode:'',//类型编码 例:类型名称首字母缩写(XXGZD)
+					categoryName:item.schoolName,//类别名称 例:XX学校,SS考试
+				};
+				uni.request({
+				    url:'/apis/cityjson?ie=utf-8',
+					method:'GET',
+					success: (res) => {
+						// console.log('this',this);
+				        const reg = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
+						let ip = reg.exec(res.data);
+						analysisParams.ipAddress = ip[0];
+						console.log('analysisParams',analysisParams);
+					}
+				});
+				this.$u.api.postAnalysis(analysisParams).then(res=>{
+					console.log('res',res);
+					if(res.code == 200){
+						
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+					location.href = item.schoolUrl
+				});
+				
+			}
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.top-search{padding: 24rpx;}
+	.cell-icon{width: 100rpx;height: 100rpx;margin-right: 24rpx;}
+	
+	.school-item{padding: 24rpx 24rpx;border-bottom: 1px solid #ddd;}
+	.school-item .up{font-size: 30rpx;margin-bottom: 10rpx;}
+	.school-item .down{color: #ababab;}
+	.school-item-img{width: 100rpx;height: 100rpx;margin-right: 24rpx;}
+	// @import  './schools.scss'
+</style>

+ 115 - 0
h5_web/pages/skillsTraining/skillsTraining.scss

@@ -0,0 +1,115 @@
+.banner{
+	width: 100%;
+	margin-bottom: 24rpx;
+}
+.iconNav{
+	.iconNav-item{
+		width: 25%;
+		margin: 24rpx 0;
+		text-align: center;
+		.iconNav-item-img{width: 98rpx;height: 98rpx;}
+	}
+	&.no-head{
+		.card-head{display: none;}
+		/deep/ .u-card__head{padding: 0!important;}
+		/deep/ .u-card__body{padding-top: 0!important;}
+	}
+}
+.skill-list{
+	.u-body-item{
+		width: 48%;
+		height: 320rpx;
+		border-radius: 8rpx;
+		overflow: hidden;
+		position: relative;
+		margin-bottom: 24rpx;
+		image{width: 100%;height: 100%;}
+		.text{
+			position: absolute;
+			left: 24rpx;
+			right: 24rpx;
+			bottom: 24rpx;
+			z-index: 6;
+			color: #d8d8d8;
+			.name{font-size: 30rpx;}
+		}
+		&::after{
+			content: '';
+			width: 100%;
+			height: 100rpx;
+			position: absolute;
+			left: 0;
+			bottom: 0;
+			background: linear-gradient(0deg,#000,rgba(255,255,255,0));
+			z-index: 5;
+		}
+	}
+}
+.school-list{
+	.school-logo-wrap{
+		margin-bottom: 48rpx;
+		.school-logo{
+			width: 100rpx;
+			height: 100rpx;
+			border-radius: 50%;
+			opacity: 0.5;
+			&.active{
+				opacity: 1;
+			}
+		}
+	}
+	.name{
+		margin-bottom: 24rpx;
+		.text{
+			font-weight: bold;
+		}
+	}
+}
+.support{
+	.big-news{
+		height: 400rpx;
+		position: relative;
+		margin-bottom: 24rpx;
+		image{width: 100%;height: 100%;}
+		.text{
+			position: absolute;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			padding: 24rpx;
+			color: #fff;
+			background: rgba(0,0,0,.5);
+			z-index: 6;
+			.name{font-size: 35rpx;}
+		}
+	}
+	.u-body-item{
+		.u-body-item-title{
+			margin-bottom: 15rpx;
+		}
+		.foot{
+			font-size: 24rpx;
+			color: #ddd;
+			.time{
+				margin-left: 24rpx;
+			}
+		}
+	}
+	.u-card-wrap {
+		background-color: $u-bg-color;
+		padding: 1px;
+	}
+	.u-body-item {
+		// font-size: 32rpx;
+		color: #333;
+		padding: 20rpx 10rpx;
+	}
+		
+	.u-body-item image {
+		width: 120rpx;
+		flex: 0 0 120rpx;
+		height: 120rpx;
+		border-radius: 8rpx;
+		margin-left: 12rpx;
+	}
+}

+ 283 - 0
h5_web/pages/skillsTraining/skillsTraining.vue

@@ -0,0 +1,283 @@
+<template>
+	<view>
+		<image class="banner" src="../../static/img/banner-skillsTraining.png" mode="scaleToFill"></image>
+		<view class="statistics u-flex u-row-between f-padding f-mb">
+			<view class="statistics-item u-flex">我的学习时长:206小时</view>
+			<view class="statistics-item u-flex">我的考试通过数:32</view>
+		</view>
+		<u-card class="iconNav no-head" :border="false" :head-border-bottom="false" :foot-border-top="false" :full="true" border-radius="0">
+			<view class="card-head" slot="head">
+				<view class="card-head-title">综合服务</view>
+			</view>
+			<view class="iconNav-body u-flex u-flex-wrap u-row-left" slot="body">
+				<view class="iconNav-item" @click="openPage('pages/inbuild/inbuild')">
+					<image class="iconNav-item-img" src="../../static/img/index-service-01.png" mode="aspectFill"></image>
+					<view class="iconNav-item-text">网络课程</view>
+				</view>
+				<view class="iconNav-item" @click="openPage('pages/inbuild/inbuild')" >
+					<image class="iconNav-item-img" src="../../static/img/index-service-02.png" mode="aspectFill"></image>
+					<view class="iconNav-item-text">线下实训</view>
+				</view>
+				<view class="iconNav-item" @click="openPage('pages/inbuild/inbuild')" >
+					<image class="iconNav-item-img" src="../../static/img/index-service-03.png" mode="aspectFill"></image>
+					<view class="iconNav-item-text">参加考试</view>
+				</view>
+				<view class="iconNav-item" @click="openPage('pages/inbuild/inbuild')" >
+					<image class="iconNav-item-img" src="../../static/img/index-service-04.png" mode="aspectFill"></image>
+					<view class="iconNav-item-text">我的班级</view>
+				</view>
+			</view>
+		</u-card>
+		<u-search class="search f-padding" placeholder="搜索课程" v-model="keyword" @focus="selectShow = true" @search="search" @custom="search" ></u-search>
+		<u-select v-model="selectShow"  mode="mutil-column-auto" :list="selectList" @confirm="selectConfirm"></u-select>
+		
+		<u-card class="skill-list"
+		:body-style="{'padding-bottom':'0'}"
+		:border="false" 
+		:foot-border-top="false" 
+		:full="true" border-radius="0">
+			<view class="card-head" slot="head">
+				<view class="card-head-title">技能包</view>
+			</view>
+			<view class="news-list-body u-flex u-row-between u-flex-wrap" slot="body">
+				<u-nodata notice="暂无新闻" v-if="skillList.length==0"></u-nodata>
+				<view v-for="(item,index) in skillList" 
+				@click="skillClick(item)"
+				:key="item.artId" 
+				class="u-body-item">
+					<image :src="item.img" mode="scaleToFill"></image>
+					<view class="text">
+						<view class="name">{{item.name}}</view>
+						<view class="school">{{item.school}}</view>
+					</view>
+				</view>
+			</view>
+		</u-card>
+		
+		<u-card class="school-list"
+		:body-style="{'margin-bottom':'0','padding-bottom':'0'}"
+		:border="false" 
+		:foot-border-top="false" 
+		:full="true" border-radius="0">
+			<view class="card-head u-flex u-row-between" slot="head">
+				<view class="card-head-title">合作院校</view>
+				<view class="right" @click="openPage('pages/schools/schools')">
+					查看更多
+					<u-icon name="arrow-right" color="#969799" size="28"></u-icon>
+				</view>
+			</view>
+			<view class="school-list-body" slot="body">
+				<u-nodata notice="暂无合作院校" v-if="schoolList.length==0"></u-nodata>
+				<view class="school-logo-wrap u-flex u-row-between">
+					<image 
+					@click="schoolToggle(index)"
+					:class="{active:schoolActive==index}"
+					class="school-logo" 
+					v-for="(item,index) in schoolList" :key="item.id" 
+					:src="item.schoolLogoUrl" 
+					mode="aspectFit"></image>
+				</view>
+				<view class="name u-flex u-row-between" @click="schoolClick(schoolList[schoolActive])">
+					<view class="text">{{schoolList[schoolActive].schoolName}}</view>
+					<u-icon name="arrow-right" color="#969799" size="28"></u-icon>
+				</view>
+				<view class="content">
+					{{schoolList[schoolActive].detatil}}
+				</view>
+			</view>
+		</u-card>
+		<u-card class="support"
+		:body-style="{'margin-bottom':'24rpx'}"
+		:border="false" 
+		:foot-border-top="false" 
+		:full="true" border-radius="0">
+			<view class="card-head u-flex u-row-between" slot="head">
+				<view class="card-head-title">政策支持</view>
+			</view>
+			<view class="support-body" slot="body">
+				<u-nodata notice="暂无新闻" v-if="schoolList.length==0"></u-nodata>
+				<view class="big-news" @click="$u.route('/pages/newsDetails/newsDetails',{artId:bigNews.artId})">
+					<image :src="bigNews.artImage" mode="aspectFill"></image>
+					<view class="text">{{bigNews.artTitle}}</view>
+				</view>
+				<view v-for="(item,index) in newsList"
+				@click="$u.route('/pages/newsDetails/newsDetails',{artId:item.artId})"
+				:key="item.artId" 
+				class="u-body-item u-flex u-border-bottom u-col-between u-row-between">
+					<view class="news-text">
+						<view class="u-body-item-title u-line-2">{{item.artTitle}}</view>
+						<view class="foot u-flex">
+							<view class="">{{item.artCategoryName}}</view>
+							<view class="time">{{$u.timeFormat(item.createTime.replace(/-/g, '/'), 'mm月dd日 hh时MM分')}}</view>
+						</view>
+					</view>
+					<image :src="item.artImage" mode="aspectFill"></image>
+				</view>
+			</view>
+		</u-card>
+	</view>
+</template>
+<script>
+	import mock from "../../static/js/mock.js";
+	export default{
+		data(){
+			return{
+				schoolActive:0,
+				keyword:'',
+				selectShow:false,
+				newsList:[],
+				bigNews:[],
+				skillList:mock.skillList,
+				schoolList:[],
+				selectList:[
+					{
+						value: 1,
+						label: '贵州交通职业技术学院',
+						children: [
+							{
+								value: 2,
+								label: '技术1'
+							},
+							{
+								value: 5,
+								label: '技术2'
+							}
+						]
+					},
+					{
+						value: 8,
+						label: '贵州医学院',
+						children: [
+							{
+								value: 9,
+								label: '技术3'
+							}
+						]
+					}
+				],
+			}
+		},
+		onLoad(){
+			this.getNewsList();
+			this.getSchoolList();
+		},
+		onShow(){
+			
+		},
+		methods:{
+			getSchoolList(){
+				this.$u.api.getSchoolList({pageNum:1,pageSize:5}).then(res=>{
+					if(res.code ==200){
+						this.schoolList = res.rows
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+				})
+			},
+			openPage(path,msg) {
+			  this.$u.route({
+			    url: path,
+				params:{
+					msg:msg
+				}
+			  })
+			},
+			getNewsList(){
+				let params ={
+					artCategoryId:2,
+					pageNum:1,
+					pageSize:10
+				}
+				this.$u.api.getIndexNewsList(params)
+				.then(res=>{
+					this.newsList = res.rows;
+					this.bigNews = this.newsList[0];
+					this.newsList.shift();
+					console.log('this.newsList', this.newsList)
+					// console.log('newsList',JSON.parse(JSON.stringify(res.rows)));
+				})
+			},
+			selectConfirm(e){
+				console.log('selectConfirm',e);
+				this.keyword = e[0].label +' ' + e[1].label
+			},
+			search(e){
+				this.$u.route({
+					url: 'pages/index/index',
+					type:'switchTab'
+				});
+				console.log('selectConfirm',e)
+			},
+			schoolToggle(index){
+				this.schoolActive = index;
+				console.log('schoolToggle index',index)
+			},
+			skillClick(item){
+				console.log('item',item);
+				let analysisParams ={
+					platform:'1',//平台:1-H5 2-APP 3-小程序 4-PC端
+					pages:location.href,//页面路径
+					btnName:'',//按钮名称
+					btnEvent:'1',//按钮事件: 1-点击 2-长按 3-滑动
+					ipAddress:'',//IP地址
+					typeName:'技能培训关注度',//类型名称 例:学校关注度 、适应性考试等
+					typeCode:'',//类型编码 例:类型名称首字母缩写(XXGZD)
+					categoryName:item.name,//类别名称 例:XX学校,SS考试
+				};
+				// uni.request({
+				//     url:'/apis/cityjson?ie=utf-8',
+				// 	method:'GET',
+				// 	success: (res) => {
+				// 		// console.log('this',this);
+				//         const reg = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
+				// 		let ip = reg.exec(res.data);
+				// 		analysisParams.ipAddress = ip[0];
+				// 		console.log('analysisParams',analysisParams);
+				// 	}
+				// });
+				this.$u.api.postAnalysis(analysisParams).then(res=>{
+					console.log('res',res);
+				});
+				this.$u.route({
+					url: '/pages/inbuild/inbuild',
+					params: {
+						msg: '',
+						artId:item.artId
+					}
+				});
+			},
+			schoolClick(item){
+				console.log('schoolClick item',item);
+				let analysisParams ={
+					platform:'1',//平台:1-H5 2-APP 3-小程序 4-PC端
+					pages:location.href,//页面路径
+					btnName:'',//按钮名称
+					btnEvent:'1',//按钮事件: 1-点击 2-长按 3-滑动
+					ipAddress:'',//IP地址
+					typeName:'学校关注度',//类型名称 例:学校关注度 、适应性考试等
+					typeCode:'',//类型编码 例:类型名称首字母缩写(XXGZD)
+					categoryName:item.schoolName,//类别名称 例:XX学校,SS考试
+				};
+				this.$u.api.postAnalysis(analysisParams).then(res=>{
+					console.log('res',res);
+					if(res.code == 200){
+						location.href = item.schoolUrl
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+				});
+			}
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import  './skillsTraining.scss'
+</style>

+ 116 - 0
h5_web/pages/upgrade/upgrade.scss

@@ -0,0 +1,116 @@
+.banner{
+	width: 100%;
+	height: 600rpx;
+	margin-bottom: 24rpx;
+}
+.iconNav{
+	.iconNav-item{
+		width: 25%;
+		margin: 24rpx 0 0;
+		text-align: center;
+		.iconNav-item-img{width: 98rpx;height: 98rpx;}
+	}
+	&.no-head{
+		.card-head{display: none;}
+		/deep/ .u-card__head{padding: 0!important;}
+		/deep/ .u-card__body{padding-top: 0!important;}
+	}
+}
+.skill-list{
+	.u-body-item{
+		width: 48%;
+		height: 320rpx;
+		border-radius: 8rpx;
+		overflow: hidden;
+		position: relative;
+		margin-bottom: 24rpx;
+		image{width: 100%;height: 100%;}
+		.text{
+			position: absolute;
+			left: 24rpx;
+			right: 24rpx;
+			bottom: 24rpx;
+			z-index: 6;
+			color: #d8d8d8;
+			.name{font-size: 30rpx;}
+		}
+		&::after{
+			content: '';
+			width: 100%;
+			height: 100rpx;
+			position: absolute;
+			left: 0;
+			bottom: 0;
+			background: linear-gradient(0deg,#000,rgba(255,255,255,0));
+			z-index: 5;
+		}
+	}
+}
+.school-list{
+	.school-logo-wrap{
+		margin-bottom: 48rpx;
+		.school-logo{
+			width: 100rpx;
+			height: 100rpx;
+			border-radius: 50%;
+			opacity: 0.5;
+			&.active{
+				opacity: 1;
+			}
+		}
+	}
+	.name{
+		margin-bottom: 24rpx;
+		.text{
+			font-weight: bold;
+		}
+	}
+}
+.support{
+	.big-news{
+		height: 400rpx;
+		position: relative;
+		margin-bottom: 24rpx;
+		image{width: 100%;height: 100%;}
+		.text{
+			position: absolute;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			padding: 24rpx;
+			color: #fff;
+			background: rgba(0,0,0,.5);
+			z-index: 6;
+			.name{font-size: 35rpx;}
+		}
+	}
+	.u-body-item{
+		.u-body-item-title{
+			margin-bottom: 15rpx;
+		}
+		.foot{
+			font-size: 24rpx;
+			color: #ddd;
+			.time{
+				margin-left: 24rpx;
+			}
+		}
+	}
+	.u-card-wrap {
+		background-color: $u-bg-color;
+		padding: 1px;
+	}
+	.u-body-item {
+		// font-size: 32rpx;
+		color: #333;
+		padding: 20rpx 10rpx;
+	}
+		
+	.u-body-item image {
+		width: 120rpx;
+		flex: 0 0 120rpx;
+		height: 120rpx;
+		border-radius: 8rpx;
+		margin-left: 12rpx;
+	}
+}

+ 251 - 0
h5_web/pages/upgrade/upgrade.vue

@@ -0,0 +1,251 @@
+<template>
+	<view>
+		<image class="banner" src="../../static/img/banner-upgrade.png" mode="scaleToFill"></image>
+		<u-card class="skill-list"
+		margin="0 30rpx"
+		:body-style="{'padding-bottom':'0'}"
+		:border="false" 
+		:foot-border-top="false" 
+		:full="true" border-radius="0">
+			<view class="card-head" slot="head">
+				<view class="card-head-title">热门专业</view>
+			</view>
+			<view class="news-list-body u-flex u-row-between u-flex-wrap" slot="body">
+				<u-nodata notice="暂无热门专业" v-if="majorList.length==0"></u-nodata>
+				<view v-for="(item,index) in majorList" 
+				@click="skillClick(item)"
+				:key="item.artId" 
+				class="u-body-item">
+					<image :src="item.img" mode="scaleToFill"></image>
+					<view class="text">
+						<view class="name">{{item.name}}</view>
+						<view class="school">{{item.school}}</view>
+					</view>
+				</view>
+			</view>
+		</u-card>
+		
+		<u-card class="school-list"
+		:body-style="{'margin-bottom':'0','padding-bottom':'0'}"
+		:border="false" 
+		:foot-border-top="false" 
+		:full="true" border-radius="0">
+			<view class="card-head u-flex u-row-between" slot="head">
+				<view class="card-head-title">合作院校</view>
+				<view class="right" @click="openPage('pages/schools/schools')">
+					查看更多
+					<u-icon name="arrow-right" color="#969799" size="28"></u-icon>
+				</view>
+			</view>
+			<view class="school-list-body" slot="body">
+				<u-nodata notice="暂无合作院校" v-if="schoolList.length==0"></u-nodata>
+				<view class="school-logo-wrap u-flex u-row-between">
+					<image 
+					@click="schoolToggle(index)"
+					:class="{active:schoolActive==index}"
+					class="school-logo" 
+					v-for="(item,index) in schoolList" :key="item.id" 
+					:src="item.schoolLogoUrl" 
+					mode="aspectFit"></image>
+				</view>
+				<view class="name u-flex u-row-between" @click="schoolClick(schoolList[schoolActive])">
+					<view class="text">{{schoolList[schoolActive].schoolName}}</view>
+					<u-icon name="arrow-right" color="#969799" size="28"></u-icon>
+				</view>
+				<view class="content">
+					{{schoolList[schoolActive].detatil}}
+				</view>
+			</view>
+		</u-card>
+		<u-card class="support"
+		:body-style="{'margin-bottom':'24rpx'}"
+		:border="false" 
+		:foot-border-top="false" 
+		:full="true" border-radius="0">
+			<view class="card-head u-flex u-row-between" slot="head">
+				<view class="card-head-title">政策支持</view>
+			</view>
+			<view class="support-body" slot="body">
+				<u-nodata notice="暂无新闻" v-if="schoolList.length==0"></u-nodata>
+				<view class="big-news" @click="$u.route('/pages/newsDetails/newsDetails',{artId:bigNews.artId})">
+					<image :src="bigNews.artImage" mode="aspectFill"></image>
+					<view class="text">{{bigNews.artTitle}}</view>
+				</view>
+				<view v-for="(item,index) in newsList"
+				@click="$u.route('/pages/newsDetails/newsDetails',{artId:item.artId})"
+				:key="item.artId" 
+				class="u-body-item u-flex u-border-bottom u-col-between u-row-between">
+					<view class="news-text">
+						<view class="u-body-item-title u-line-2">{{item.artTitle}}</view>
+						<view class="foot u-flex">
+							<view class="">{{item.artCategoryName}}</view>
+							<view class="time">{{$u.timeFormat(item.createTime.replace(/-/g, '/'), 'mm月dd日 hh时MM分')}}</view>
+						</view>
+					</view>
+					<image :src="item.artImage" mode="aspectFill"></image>
+				</view>
+			</view>
+		</u-card>
+	</view>
+</template>
+
+<script>
+	import mock from "../../static/js/mock.js";
+	export default{
+		data(){
+			return{
+				schoolActive:0,
+				keyword:'',
+				selectShow:false,
+				newsList:[],
+				bigNews:[],
+				majorList:mock.majorList,
+				schoolList:[],
+				selectList:[
+					{
+						value: 1,
+						label: '贵州交通职业技术学院',
+						children: [
+							{
+								value: 2,
+								label: '技术1'
+							},
+							{
+								value: 5,
+								label: '技术2'
+							}
+						]
+					},
+					{
+						value: 8,
+						label: '贵州医学院',
+						children: [
+							{
+								value: 9,
+								label: '技术3'
+							}
+						]
+					}
+				],
+			}
+		},
+		onLoad(){
+			this.getNewsList();
+			this.getSchoolList();
+		},
+		onShow(){
+			
+		},
+		methods:{
+			getSchoolList(){
+				this.$u.api.getSchoolList({pageNum:1,pageSize:5}).then(res=>{
+					if(res.code ==200){
+						this.schoolList = res.rows
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+				})
+			},
+			openPage(path,msg) {
+			  this.$u.route({
+			    url: path,
+				params:{
+					msg:msg
+				}
+			  })
+			},
+			getNewsList(){
+				let params ={
+					artCategoryId:2,
+					pageNum:1,
+					pageSize:10
+				}
+				this.$u.api.getIndexNewsList(params)
+				.then(res=>{
+					this.newsList = res.rows;
+					this.bigNews = this.newsList[0];
+					this.newsList.shift();
+					console.log('this.newsList', this.newsList)
+					// console.log('newsList',JSON.parse(JSON.stringify(res.rows)));
+				})
+			},
+			selectConfirm(e){
+				console.log('selectConfirm',e);
+				this.keyword = e[0].label +' ' + e[1].label
+			},
+			search(e){
+				console.log('selectConfirm',e)
+			},
+			schoolToggle(index){
+				this.schoolActive = index;
+				console.log('schoolToggle index',index);
+			},
+			skillClick(item){
+				console.log('item',item);
+				let analysisParams ={
+					platform:'1',//平台:1-H5 2-APP 3-小程序 4-PC端
+					pages:location.href,//页面路径
+					btnName:'',//按钮名称
+					btnEvent:'1',//按钮事件: 1-点击 2-长按 3-滑动
+					ipAddress:'',//IP地址
+					typeName:'学历提升关注度',//类型名称 例:学校关注度 、适应性考试等
+					typeCode:'',//类型编码 例:类型名称首字母缩写(XXGZD)
+					categoryName:item.name,//类别名称 例:XX学校,SS考试
+				};
+				uni.request({
+				    url:'/apis/cityjson?ie=utf-8',
+					method:'GET',
+					success: (res) => {
+						// console.log('this',this);
+				        const reg = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
+						let ip = reg.exec(res.data);
+						analysisParams.ipAddress = ip[0];
+						console.log('analysisParams',analysisParams);
+					}
+				});
+				this.$u.api.postAnalysis(analysisParams).then(res=>{
+					console.log('res',res);
+				});
+				this.$u.route({
+					url: '/pages/inbuild/inbuild',
+					params: {
+						msg: '热门专业',
+						artId:item.artId
+					}
+				});
+			},
+			schoolClick(item){
+				console.log('schoolClick item',item);
+				let analysisParams ={
+					platform:'1',//平台:1-H5 2-APP 3-小程序 4-PC端
+					pages:location.href,//页面路径
+					btnName:'',//按钮名称
+					btnEvent:'1',//按钮事件: 1-点击 2-长按 3-滑动
+					ipAddress:'',//IP地址
+					typeName:'学校关注度',//类型名称 例:学校关注度 、适应性考试等
+					typeCode:'',//类型编码 例:类型名称首字母缩写(XXGZD)
+					categoryName:item.schoolName,//类别名称 例:XX学校,SS考试
+				};
+				this.$u.api.postAnalysis(analysisParams).then(res=>{
+					console.log('res',res);
+					if(res.code == 200){
+						location.href = item.schoolUrl
+					}else{
+						uni.showToast({
+							icon:'none',
+							title:res.msg
+						})
+					}
+				});
+			}
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import  './upgrade.scss'
+</style>

+ 952 - 0
h5_web/static/css/quill.bubble.scss

@@ -0,0 +1,952 @@
+/*!
+ * Quill Editor v1.3.7
+ * https://quilljs.com/
+ * Copyright (c) 2014, Jason Chen
+ * Copyright (c) 2013, salesforce.com
+ */
+.ql-container {
+  box-sizing: border-box;
+  font-family: Helvetica, Arial, sans-serif;
+  font-size: 13px;
+  height: 100%;
+  margin: 0px;
+  position: relative;
+}
+.ql-container.ql-disabled .ql-tooltip {
+  visibility: hidden;
+}
+.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {
+  pointer-events: none;
+}
+.ql-clipboard {
+  left: -100000px;
+  height: 1px;
+  overflow-y: hidden;
+  position: absolute;
+  top: 50%;
+}
+.ql-clipboard p {
+  margin: 0;
+  padding: 0;
+}
+.ql-editor {
+  box-sizing: border-box;
+  line-height: 1.42;
+  height: 100%;
+  outline: none;
+  overflow-y: auto;
+  padding: 12px 15px;
+  tab-size: 4;
+  -moz-tab-size: 4;
+  text-align: left;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+}
+.ql-editor > * {
+  cursor: text;
+}
+.ql-editor p,
+.ql-editor ol,
+.ql-editor ul,
+.ql-editor pre,
+.ql-editor blockquote,
+.ql-editor h1,
+.ql-editor h2,
+.ql-editor h3,
+.ql-editor h4,
+.ql-editor h5,
+.ql-editor h6 {
+  margin: 0;
+  padding: 0;
+  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol,
+.ql-editor ul {
+  padding-left: 1.5em;
+}
+.ql-editor ol > li,
+.ql-editor ul > li {
+  list-style-type: none;
+}
+.ql-editor ul > li::before {
+  content: '\2022';
+}
+.ql-editor ul[data-checked=true],
+.ql-editor ul[data-checked=false] {
+  pointer-events: none;
+}
+.ql-editor ul[data-checked=true] > li *,
+.ql-editor ul[data-checked=false] > li * {
+  pointer-events: all;
+}
+.ql-editor ul[data-checked=true] > li::before,
+.ql-editor ul[data-checked=false] > li::before {
+  color: #777;
+  cursor: pointer;
+  pointer-events: all;
+}
+.ql-editor ul[data-checked=true] > li::before {
+  content: '\2611';
+}
+.ql-editor ul[data-checked=false] > li::before {
+  content: '\2610';
+}
+.ql-editor li::before {
+  display: inline-block;
+  white-space: nowrap;
+  width: 1.2em;
+}
+.ql-editor li:not(.ql-direction-rtl)::before {
+  margin-left: -1.5em;
+  margin-right: 0.3em;
+  text-align: right;
+}
+.ql-editor li.ql-direction-rtl::before {
+  margin-left: 0.3em;
+  margin-right: -1.5em;
+}
+.ql-editor ol li:not(.ql-direction-rtl),
+.ql-editor ul li:not(.ql-direction-rtl) {
+  padding-left: 1.5em;
+}
+.ql-editor ol li.ql-direction-rtl,
+.ql-editor ul li.ql-direction-rtl {
+  padding-right: 1.5em;
+}
+.ql-editor ol li {
+  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+  counter-increment: list-0;
+}
+.ql-editor ol li:before {
+  content: counter(list-0, decimal) '. ';
+}
+.ql-editor ol li.ql-indent-1 {
+  counter-increment: list-1;
+}
+.ql-editor ol li.ql-indent-1:before {
+  content: counter(list-1, lower-alpha) '. ';
+}
+.ql-editor ol li.ql-indent-1 {
+  counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-2 {
+  counter-increment: list-2;
+}
+.ql-editor ol li.ql-indent-2:before {
+  content: counter(list-2, lower-roman) '. ';
+}
+.ql-editor ol li.ql-indent-2 {
+  counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-3 {
+  counter-increment: list-3;
+}
+.ql-editor ol li.ql-indent-3:before {
+  content: counter(list-3, decimal) '. ';
+}
+.ql-editor ol li.ql-indent-3 {
+  counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-4 {
+  counter-increment: list-4;
+}
+.ql-editor ol li.ql-indent-4:before {
+  content: counter(list-4, lower-alpha) '. ';
+}
+.ql-editor ol li.ql-indent-4 {
+  counter-reset: list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-5 {
+  counter-increment: list-5;
+}
+.ql-editor ol li.ql-indent-5:before {
+  content: counter(list-5, lower-roman) '. ';
+}
+.ql-editor ol li.ql-indent-5 {
+  counter-reset: list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-6 {
+  counter-increment: list-6;
+}
+.ql-editor ol li.ql-indent-6:before {
+  content: counter(list-6, decimal) '. ';
+}
+.ql-editor ol li.ql-indent-6 {
+  counter-reset: list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-7 {
+  counter-increment: list-7;
+}
+.ql-editor ol li.ql-indent-7:before {
+  content: counter(list-7, lower-alpha) '. ';
+}
+.ql-editor ol li.ql-indent-7 {
+  counter-reset: list-8 list-9;
+}
+.ql-editor ol li.ql-indent-8 {
+  counter-increment: list-8;
+}
+.ql-editor ol li.ql-indent-8:before {
+  content: counter(list-8, lower-roman) '. ';
+}
+.ql-editor ol li.ql-indent-8 {
+  counter-reset: list-9;
+}
+.ql-editor ol li.ql-indent-9 {
+  counter-increment: list-9;
+}
+.ql-editor ol li.ql-indent-9:before {
+  content: counter(list-9, decimal) '. ';
+}
+.ql-editor .ql-indent-1:not(.ql-direction-rtl) {
+  padding-left: 3em;
+}
+.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {
+  padding-left: 4.5em;
+}
+.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {
+  padding-right: 3em;
+}
+.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {
+  padding-right: 4.5em;
+}
+.ql-editor .ql-indent-2:not(.ql-direction-rtl) {
+  padding-left: 6em;
+}
+.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {
+  padding-left: 7.5em;
+}
+.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {
+  padding-right: 6em;
+}
+.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {
+  padding-right: 7.5em;
+}
+.ql-editor .ql-indent-3:not(.ql-direction-rtl) {
+  padding-left: 9em;
+}
+.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {
+  padding-left: 10.5em;
+}
+.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {
+  padding-right: 9em;
+}
+.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {
+  padding-right: 10.5em;
+}
+.ql-editor .ql-indent-4:not(.ql-direction-rtl) {
+  padding-left: 12em;
+}
+.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {
+  padding-left: 13.5em;
+}
+.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {
+  padding-right: 12em;
+}
+.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {
+  padding-right: 13.5em;
+}
+.ql-editor .ql-indent-5:not(.ql-direction-rtl) {
+  padding-left: 15em;
+}
+.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {
+  padding-left: 16.5em;
+}
+.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {
+  padding-right: 15em;
+}
+.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {
+  padding-right: 16.5em;
+}
+.ql-editor .ql-indent-6:not(.ql-direction-rtl) {
+  padding-left: 18em;
+}
+.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {
+  padding-left: 19.5em;
+}
+.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {
+  padding-right: 18em;
+}
+.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {
+  padding-right: 19.5em;
+}
+.ql-editor .ql-indent-7:not(.ql-direction-rtl) {
+  padding-left: 21em;
+}
+.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {
+  padding-left: 22.5em;
+}
+.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {
+  padding-right: 21em;
+}
+.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {
+  padding-right: 22.5em;
+}
+.ql-editor .ql-indent-8:not(.ql-direction-rtl) {
+  padding-left: 24em;
+}
+.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {
+  padding-left: 25.5em;
+}
+.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {
+  padding-right: 24em;
+}
+.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {
+  padding-right: 25.5em;
+}
+.ql-editor .ql-indent-9:not(.ql-direction-rtl) {
+  padding-left: 27em;
+}
+.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {
+  padding-left: 28.5em;
+}
+.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {
+  padding-right: 27em;
+}
+.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {
+  padding-right: 28.5em;
+}
+.ql-editor .ql-video {
+  display: block;
+  max-width: 100%;
+}
+.ql-editor .ql-video.ql-align-center {
+  margin: 0 auto;
+}
+.ql-editor .ql-video.ql-align-right {
+  margin: 0 0 0 auto;
+}
+.ql-editor .ql-bg-black {
+  background-color: #000;
+}
+.ql-editor .ql-bg-red {
+  background-color: #e60000;
+}
+.ql-editor .ql-bg-orange {
+  background-color: #f90;
+}
+.ql-editor .ql-bg-yellow {
+  background-color: #ff0;
+}
+.ql-editor .ql-bg-green {
+  background-color: #008a00;
+}
+.ql-editor .ql-bg-blue {
+  background-color: #06c;
+}
+.ql-editor .ql-bg-purple {
+  background-color: #93f;
+}
+.ql-editor .ql-color-white {
+  color: #fff;
+}
+.ql-editor .ql-color-red {
+  color: #e60000;
+}
+.ql-editor .ql-color-orange {
+  color: #f90;
+}
+.ql-editor .ql-color-yellow {
+  color: #ff0;
+}
+.ql-editor .ql-color-green {
+  color: #008a00;
+}
+.ql-editor .ql-color-blue {
+  color: #06c;
+}
+.ql-editor .ql-color-purple {
+  color: #93f;
+}
+.ql-editor .ql-font-serif {
+  font-family: Georgia, Times New Roman, serif;
+}
+.ql-editor .ql-font-monospace {
+  font-family: Monaco, Courier New, monospace;
+}
+.ql-editor .ql-size-small {
+  font-size: 0.75em;
+}
+.ql-editor .ql-size-large {
+  font-size: 1.5em;
+}
+.ql-editor .ql-size-huge {
+  font-size: 2.5em;
+}
+.ql-editor .ql-direction-rtl {
+  direction: rtl;
+  text-align: inherit;
+}
+.ql-editor .ql-align-center {
+  text-align: center;
+}
+.ql-editor .ql-align-justify {
+  text-align: justify;
+}
+.ql-editor .ql-align-right {
+  text-align: right;
+}
+.ql-editor.ql-blank::before {
+  color: rgba(0,0,0,0.6);
+  content: attr(data-placeholder);
+  font-style: italic;
+  left: 15px;
+  pointer-events: none;
+  position: absolute;
+  right: 15px;
+}
+.ql-bubble.ql-toolbar:after,
+.ql-bubble .ql-toolbar:after {
+  clear: both;
+  content: '';
+  display: table;
+}
+.ql-bubble.ql-toolbar button,
+.ql-bubble .ql-toolbar button {
+  background: none;
+  border: none;
+  cursor: pointer;
+  display: inline-block;
+  float: left;
+  height: 24px;
+  padding: 3px 5px;
+  width: 28px;
+}
+.ql-bubble.ql-toolbar button svg,
+.ql-bubble .ql-toolbar button svg {
+  float: left;
+  height: 100%;
+}
+.ql-bubble.ql-toolbar button:active:hover,
+.ql-bubble .ql-toolbar button:active:hover {
+  outline: none;
+}
+.ql-bubble.ql-toolbar input.ql-image[type=file],
+.ql-bubble .ql-toolbar input.ql-image[type=file] {
+  display: none;
+}
+.ql-bubble.ql-toolbar button:hover,
+.ql-bubble .ql-toolbar button:hover,
+.ql-bubble.ql-toolbar button:focus,
+.ql-bubble .ql-toolbar button:focus,
+.ql-bubble.ql-toolbar button.ql-active,
+.ql-bubble .ql-toolbar button.ql-active,
+.ql-bubble.ql-toolbar .ql-picker-label:hover,
+.ql-bubble .ql-toolbar .ql-picker-label:hover,
+.ql-bubble.ql-toolbar .ql-picker-label.ql-active,
+.ql-bubble .ql-toolbar .ql-picker-label.ql-active,
+.ql-bubble.ql-toolbar .ql-picker-item:hover,
+.ql-bubble .ql-toolbar .ql-picker-item:hover,
+.ql-bubble.ql-toolbar .ql-picker-item.ql-selected,
+.ql-bubble .ql-toolbar .ql-picker-item.ql-selected {
+  color: #fff;
+}
+.ql-bubble.ql-toolbar button:hover .ql-fill,
+.ql-bubble .ql-toolbar button:hover .ql-fill,
+.ql-bubble.ql-toolbar button:focus .ql-fill,
+.ql-bubble .ql-toolbar button:focus .ql-fill,
+.ql-bubble.ql-toolbar button.ql-active .ql-fill,
+.ql-bubble .ql-toolbar button.ql-active .ql-fill,
+.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-fill,
+.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-fill,
+.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-fill,
+.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-fill,
+.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-fill,
+.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-fill,
+.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-fill,
+.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-fill,
+.ql-bubble.ql-toolbar button:hover .ql-stroke.ql-fill,
+.ql-bubble .ql-toolbar button:hover .ql-stroke.ql-fill,
+.ql-bubble.ql-toolbar button:focus .ql-stroke.ql-fill,
+.ql-bubble .ql-toolbar button:focus .ql-stroke.ql-fill,
+.ql-bubble.ql-toolbar button.ql-active .ql-stroke.ql-fill,
+.ql-bubble .ql-toolbar button.ql-active .ql-stroke.ql-fill,
+.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
+.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
+.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
+.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
+.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
+.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
+.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,
+.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill {
+  fill: #fff;
+}
+.ql-bubble.ql-toolbar button:hover .ql-stroke,
+.ql-bubble .ql-toolbar button:hover .ql-stroke,
+.ql-bubble.ql-toolbar button:focus .ql-stroke,
+.ql-bubble .ql-toolbar button:focus .ql-stroke,
+.ql-bubble.ql-toolbar button.ql-active .ql-stroke,
+.ql-bubble .ql-toolbar button.ql-active .ql-stroke,
+.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke,
+.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke,
+.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke,
+.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke,
+.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke,
+.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke,
+.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
+.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
+.ql-bubble.ql-toolbar button:hover .ql-stroke-miter,
+.ql-bubble .ql-toolbar button:hover .ql-stroke-miter,
+.ql-bubble.ql-toolbar button:focus .ql-stroke-miter,
+.ql-bubble .ql-toolbar button:focus .ql-stroke-miter,
+.ql-bubble.ql-toolbar button.ql-active .ql-stroke-miter,
+.ql-bubble .ql-toolbar button.ql-active .ql-stroke-miter,
+.ql-bubble.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
+.ql-bubble .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
+.ql-bubble.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
+.ql-bubble .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
+.ql-bubble.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
+.ql-bubble .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
+.ql-bubble.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,
+.ql-bubble .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter {
+  stroke: #fff;
+}
+@media (pointer: coarse) {
+  .ql-bubble.ql-toolbar button:hover:not(.ql-active),
+  .ql-bubble .ql-toolbar button:hover:not(.ql-active) {
+    color: #ccc;
+  }
+  .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-fill,
+  .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-fill,
+  .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,
+  .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill {
+    fill: #ccc;
+  }
+  .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke,
+  .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke,
+  .ql-bubble.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,
+  .ql-bubble .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter {
+    stroke: #ccc;
+  }
+}
+.ql-bubble {
+  box-sizing: border-box;
+}
+.ql-bubble * {
+  box-sizing: border-box;
+}
+.ql-bubble .ql-hidden {
+  display: none;
+}
+.ql-bubble .ql-out-bottom,
+.ql-bubble .ql-out-top {
+  visibility: hidden;
+}
+.ql-bubble .ql-tooltip {
+  position: absolute;
+  transform: translateY(10px);
+}
+.ql-bubble .ql-tooltip a {
+  cursor: pointer;
+  text-decoration: none;
+}
+.ql-bubble .ql-tooltip.ql-flip {
+  transform: translateY(-10px);
+}
+.ql-bubble .ql-formats {
+  display: inline-block;
+  vertical-align: middle;
+}
+.ql-bubble .ql-formats:after {
+  clear: both;
+  content: '';
+  display: table;
+}
+.ql-bubble .ql-stroke {
+  fill: none;
+  stroke: #ccc;
+  stroke-linecap: round;
+  stroke-linejoin: round;
+  stroke-width: 2;
+}
+.ql-bubble .ql-stroke-miter {
+  fill: none;
+  stroke: #ccc;
+  stroke-miterlimit: 10;
+  stroke-width: 2;
+}
+.ql-bubble .ql-fill,
+.ql-bubble .ql-stroke.ql-fill {
+  fill: #ccc;
+}
+.ql-bubble .ql-empty {
+  fill: none;
+}
+.ql-bubble .ql-even {
+  fill-rule: evenodd;
+}
+.ql-bubble .ql-thin,
+.ql-bubble .ql-stroke.ql-thin {
+  stroke-width: 1;
+}
+.ql-bubble .ql-transparent {
+  opacity: 0.4;
+}
+.ql-bubble .ql-direction svg:last-child {
+  display: none;
+}
+.ql-bubble .ql-direction.ql-active svg:last-child {
+  display: inline;
+}
+.ql-bubble .ql-direction.ql-active svg:first-child {
+  display: none;
+}
+.ql-bubble .ql-editor h1 {
+  font-size: 2em;
+}
+.ql-bubble .ql-editor h2 {
+  font-size: 1.5em;
+}
+.ql-bubble .ql-editor h3 {
+  font-size: 1.17em;
+}
+.ql-bubble .ql-editor h4 {
+  font-size: 1em;
+}
+.ql-bubble .ql-editor h5 {
+  font-size: 0.83em;
+}
+.ql-bubble .ql-editor h6 {
+  font-size: 0.67em;
+}
+.ql-bubble .ql-editor a {
+  text-decoration: underline;
+}
+.ql-bubble .ql-editor blockquote {
+  border-left: 4px solid #ccc;
+  margin-bottom: 5px;
+  margin-top: 5px;
+  padding-left: 16px;
+}
+.ql-bubble .ql-editor code,
+.ql-bubble .ql-editor pre {
+  background-color: #f0f0f0;
+  border-radius: 3px;
+}
+.ql-bubble .ql-editor pre {
+  white-space: pre-wrap;
+  margin-bottom: 5px;
+  margin-top: 5px;
+  padding: 5px 10px;
+}
+.ql-bubble .ql-editor code {
+  font-size: 85%;
+  padding: 2px 4px;
+}
+.ql-bubble .ql-editor pre.ql-syntax {
+  background-color: #23241f;
+  color: #f8f8f2;
+  overflow: visible;
+}
+.ql-bubble .ql-editor img {
+  max-width: 100%;
+}
+.ql-bubble .ql-picker {
+  color: #ccc;
+  display: inline-block;
+  float: left;
+  font-size: 14px;
+  font-weight: 500;
+  height: 24px;
+  position: relative;
+  vertical-align: middle;
+}
+.ql-bubble .ql-picker-label {
+  cursor: pointer;
+  display: inline-block;
+  height: 100%;
+  padding-left: 8px;
+  padding-right: 2px;
+  position: relative;
+  width: 100%;
+}
+.ql-bubble .ql-picker-label::before {
+  display: inline-block;
+  line-height: 22px;
+}
+.ql-bubble .ql-picker-options {
+  background-color: #444;
+  display: none;
+  min-width: 100%;
+  padding: 4px 8px;
+  position: absolute;
+  white-space: nowrap;
+}
+.ql-bubble .ql-picker-options .ql-picker-item {
+  cursor: pointer;
+  display: block;
+  padding-bottom: 5px;
+  padding-top: 5px;
+}
+.ql-bubble .ql-picker.ql-expanded .ql-picker-label {
+  color: #777;
+  z-index: 2;
+}
+.ql-bubble .ql-picker.ql-expanded .ql-picker-label .ql-fill {
+  fill: #777;
+}
+.ql-bubble .ql-picker.ql-expanded .ql-picker-label .ql-stroke {
+  stroke: #777;
+}
+.ql-bubble .ql-picker.ql-expanded .ql-picker-options {
+  display: block;
+  margin-top: -1px;
+  top: 100%;
+  z-index: 1;
+}
+.ql-bubble .ql-color-picker,
+.ql-bubble .ql-icon-picker {
+  width: 28px;
+}
+.ql-bubble .ql-color-picker .ql-picker-label,
+.ql-bubble .ql-icon-picker .ql-picker-label {
+  padding: 2px 4px;
+}
+.ql-bubble .ql-color-picker .ql-picker-label svg,
+.ql-bubble .ql-icon-picker .ql-picker-label svg {
+  right: 4px;
+}
+.ql-bubble .ql-icon-picker .ql-picker-options {
+  padding: 4px 0px;
+}
+.ql-bubble .ql-icon-picker .ql-picker-item {
+  height: 24px;
+  width: 24px;
+  padding: 2px 4px;
+}
+.ql-bubble .ql-color-picker .ql-picker-options {
+  padding: 3px 5px;
+  width: 152px;
+}
+.ql-bubble .ql-color-picker .ql-picker-item {
+  border: 1px solid transparent;
+  float: left;
+  height: 16px;
+  margin: 2px;
+  padding: 0px;
+  width: 16px;
+}
+.ql-bubble .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {
+  position: absolute;
+  margin-top: -9px;
+  right: 0;
+  top: 50%;
+  width: 18px;
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before,
+.ql-bubble .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before,
+.ql-bubble .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before,
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before,
+.ql-bubble .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before,
+.ql-bubble .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before {
+  content: attr(data-label);
+}
+.ql-bubble .ql-picker.ql-header {
+  width: 98px;
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-label::before,
+.ql-bubble .ql-picker.ql-header .ql-picker-item::before {
+  content: 'Normal';
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
+  content: 'Heading 1';
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
+  content: 'Heading 2';
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
+  content: 'Heading 3';
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
+  content: 'Heading 4';
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
+  content: 'Heading 5';
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
+  content: 'Heading 6';
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
+  font-size: 2em;
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
+  font-size: 1.5em;
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
+  font-size: 1.17em;
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
+  font-size: 1em;
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
+  font-size: 0.83em;
+}
+.ql-bubble .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
+  font-size: 0.67em;
+}
+.ql-bubble .ql-picker.ql-font {
+  width: 108px;
+}
+.ql-bubble .ql-picker.ql-font .ql-picker-label::before,
+.ql-bubble .ql-picker.ql-font .ql-picker-item::before {
+  content: 'Sans Serif';
+}
+.ql-bubble .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
+.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
+  content: 'Serif';
+}
+.ql-bubble .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
+.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
+  content: 'Monospace';
+}
+.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
+  font-family: Georgia, Times New Roman, serif;
+}
+.ql-bubble .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
+  font-family: Monaco, Courier New, monospace;
+}
+.ql-bubble .ql-picker.ql-size {
+  width: 98px;
+}
+.ql-bubble .ql-picker.ql-size .ql-picker-label::before,
+.ql-bubble .ql-picker.ql-size .ql-picker-item::before {
+  content: 'Normal';
+}
+.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
+.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
+  content: 'Small';
+}
+.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
+.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
+  content: 'Large';
+}
+.ql-bubble .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
+.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
+  content: 'Huge';
+}
+.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
+  font-size: 10px;
+}
+.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
+  font-size: 18px;
+}
+.ql-bubble .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
+  font-size: 32px;
+}
+.ql-bubble .ql-color-picker.ql-background .ql-picker-item {
+  background-color: #fff;
+}
+.ql-bubble .ql-color-picker.ql-color .ql-picker-item {
+  background-color: #000;
+}
+.ql-bubble .ql-toolbar .ql-formats {
+  margin: 8px 12px 8px 0px;
+}
+.ql-bubble .ql-toolbar .ql-formats:first-child {
+  margin-left: 12px;
+}
+.ql-bubble .ql-color-picker svg {
+  margin: 1px;
+}
+.ql-bubble .ql-color-picker .ql-picker-item.ql-selected,
+.ql-bubble .ql-color-picker .ql-picker-item:hover {
+  border-color: #fff;
+}
+.ql-bubble .ql-tooltip {
+  background-color: #444;
+  border-radius: 25px;
+  color: #fff;
+}
+.ql-bubble .ql-tooltip-arrow {
+  border-left: 6px solid transparent;
+  border-right: 6px solid transparent;
+  content: " ";
+  display: block;
+  left: 50%;
+  margin-left: -6px;
+  position: absolute;
+}
+.ql-bubble .ql-tooltip:not(.ql-flip) .ql-tooltip-arrow {
+  border-bottom: 6px solid #444;
+  top: -6px;
+}
+.ql-bubble .ql-tooltip.ql-flip .ql-tooltip-arrow {
+  border-top: 6px solid #444;
+  bottom: -6px;
+}
+.ql-bubble .ql-tooltip.ql-editing .ql-tooltip-editor {
+  display: block;
+}
+.ql-bubble .ql-tooltip.ql-editing .ql-formats {
+  visibility: hidden;
+}
+.ql-bubble .ql-tooltip-editor {
+  display: none;
+}
+.ql-bubble .ql-tooltip-editor input[type=text] {
+  background: transparent;
+  border: none;
+  color: #fff;
+  font-size: 13px;
+  height: 100%;
+  outline: none;
+  padding: 10px 20px;
+  position: absolute;
+  width: 100%;
+}
+.ql-bubble .ql-tooltip-editor a {
+  top: 10px;
+  position: absolute;
+  right: 20px;
+}
+.ql-bubble .ql-tooltip-editor a:before {
+  color: #ccc;
+  content: "\D7";
+  font-size: 16px;
+  font-weight: bold;
+}
+.ql-container.ql-bubble:not(.ql-disabled) a {
+  position: relative;
+  white-space: nowrap;
+}
+.ql-container.ql-bubble:not(.ql-disabled) a::before {
+  background-color: #444;
+  border-radius: 15px;
+  top: -5px;
+  font-size: 12px;
+  color: #fff;
+  content: attr(href);
+  font-weight: normal;
+  overflow: hidden;
+  padding: 5px 15px;
+  text-decoration: none;
+  z-index: 1;
+}
+.ql-container.ql-bubble:not(.ql-disabled) a::after {
+  border-top: 6px solid #444;
+  border-left: 6px solid transparent;
+  border-right: 6px solid transparent;
+  top: 0;
+  content: " ";
+  height: 0;
+  width: 0;
+}
+.ql-container.ql-bubble:not(.ql-disabled) a::before,
+.ql-container.ql-bubble:not(.ql-disabled) a::after {
+  left: 0;
+  margin-left: 50%;
+  position: absolute;
+  transform: translate(-50%, -100%);
+  transition: visibility 0s ease 200ms;
+  visibility: hidden;
+}
+.ql-container.ql-bubble:not(.ql-disabled) a:hover::before,
+.ql-container.ql-bubble:not(.ql-disabled) a:hover::after {
+  visibility: visible;
+}

+ 399 - 0
h5_web/static/css/quill.core.scss

@@ -0,0 +1,399 @@
+/*!
+ * Quill Editor v1.3.7
+ * https://quilljs.com/
+ * Copyright (c) 2014, Jason Chen
+ * Copyright (c) 2013, salesforce.com
+ */
+.ql-container {
+  box-sizing: border-box;
+  font-family: Helvetica, Arial, sans-serif;
+  font-size: 13px;
+  height: 100%;
+  margin: 0px;
+  position: relative;
+}
+.ql-container.ql-disabled .ql-tooltip {
+  visibility: hidden;
+}
+.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {
+  pointer-events: none;
+}
+.ql-clipboard {
+  left: -100000px;
+  height: 1px;
+  overflow-y: hidden;
+  position: absolute;
+  top: 50%;
+}
+.ql-clipboard p {
+  margin: 0;
+  padding: 0;
+}
+.ql-editor {
+  box-sizing: border-box;
+  line-height: 1.42;
+  height: 100%;
+  outline: none;
+  overflow-y: auto;
+  padding: 12px 15px;
+  tab-size: 4;
+  -moz-tab-size: 4;
+  text-align: left;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+}
+.ql-editor > * {
+  cursor: text;
+}
+.ql-editor p,
+.ql-editor ol,
+.ql-editor ul,
+.ql-editor pre,
+.ql-editor blockquote,
+.ql-editor h1,
+.ql-editor h2,
+.ql-editor h3,
+.ql-editor h4,
+.ql-editor h5,
+.ql-editor h6 {
+  margin: 0;
+  padding: 0;
+  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol,
+.ql-editor ul {
+  padding-left: 1.5em;
+}
+.ql-editor ol > li,
+.ql-editor ul > li {
+  list-style-type: none;
+}
+.ql-editor ul > li::before {
+  content: '\2022';
+}
+.ql-editor ul[data-checked=true],
+.ql-editor ul[data-checked=false] {
+  pointer-events: none;
+}
+.ql-editor ul[data-checked=true] > li *,
+.ql-editor ul[data-checked=false] > li * {
+  pointer-events: all;
+}
+.ql-editor ul[data-checked=true] > li::before,
+.ql-editor ul[data-checked=false] > li::before {
+  color: #777;
+  cursor: pointer;
+  pointer-events: all;
+}
+.ql-editor ul[data-checked=true] > li::before {
+  content: '\2611';
+}
+.ql-editor ul[data-checked=false] > li::before {
+  content: '\2610';
+}
+.ql-editor li::before {
+  display: inline-block;
+  white-space: nowrap;
+  width: 1.2em;
+}
+.ql-editor li:not(.ql-direction-rtl)::before {
+  margin-left: -1.5em;
+  margin-right: 0.3em;
+  text-align: right;
+}
+.ql-editor li.ql-direction-rtl::before {
+  margin-left: 0.3em;
+  margin-right: -1.5em;
+}
+.ql-editor ol li:not(.ql-direction-rtl),
+.ql-editor ul li:not(.ql-direction-rtl) {
+  padding-left: 1.5em;
+}
+.ql-editor ol li.ql-direction-rtl,
+.ql-editor ul li.ql-direction-rtl {
+  padding-right: 1.5em;
+}
+.ql-editor ol li {
+  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+  counter-increment: list-0;
+}
+.ql-editor ol li:before {
+  content: counter(list-0, decimal) '. ';
+}
+.ql-editor ol li.ql-indent-1 {
+  counter-increment: list-1;
+}
+.ql-editor ol li.ql-indent-1:before {
+  content: counter(list-1, lower-alpha) '. ';
+}
+.ql-editor ol li.ql-indent-1 {
+  counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-2 {
+  counter-increment: list-2;
+}
+.ql-editor ol li.ql-indent-2:before {
+  content: counter(list-2, lower-roman) '. ';
+}
+.ql-editor ol li.ql-indent-2 {
+  counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-3 {
+  counter-increment: list-3;
+}
+.ql-editor ol li.ql-indent-3:before {
+  content: counter(list-3, decimal) '. ';
+}
+.ql-editor ol li.ql-indent-3 {
+  counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-4 {
+  counter-increment: list-4;
+}
+.ql-editor ol li.ql-indent-4:before {
+  content: counter(list-4, lower-alpha) '. ';
+}
+.ql-editor ol li.ql-indent-4 {
+  counter-reset: list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-5 {
+  counter-increment: list-5;
+}
+.ql-editor ol li.ql-indent-5:before {
+  content: counter(list-5, lower-roman) '. ';
+}
+.ql-editor ol li.ql-indent-5 {
+  counter-reset: list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-6 {
+  counter-increment: list-6;
+}
+.ql-editor ol li.ql-indent-6:before {
+  content: counter(list-6, decimal) '. ';
+}
+.ql-editor ol li.ql-indent-6 {
+  counter-reset: list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-7 {
+  counter-increment: list-7;
+}
+.ql-editor ol li.ql-indent-7:before {
+  content: counter(list-7, lower-alpha) '. ';
+}
+.ql-editor ol li.ql-indent-7 {
+  counter-reset: list-8 list-9;
+}
+.ql-editor ol li.ql-indent-8 {
+  counter-increment: list-8;
+}
+.ql-editor ol li.ql-indent-8:before {
+  content: counter(list-8, lower-roman) '. ';
+}
+.ql-editor ol li.ql-indent-8 {
+  counter-reset: list-9;
+}
+.ql-editor ol li.ql-indent-9 {
+  counter-increment: list-9;
+}
+.ql-editor ol li.ql-indent-9:before {
+  content: counter(list-9, decimal) '. ';
+}
+.ql-editor .ql-indent-1:not(.ql-direction-rtl) {
+  padding-left: 3em;
+}
+.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {
+  padding-left: 4.5em;
+}
+.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {
+  padding-right: 3em;
+}
+.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {
+  padding-right: 4.5em;
+}
+.ql-editor .ql-indent-2:not(.ql-direction-rtl) {
+  padding-left: 6em;
+}
+.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {
+  padding-left: 7.5em;
+}
+.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {
+  padding-right: 6em;
+}
+.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {
+  padding-right: 7.5em;
+}
+.ql-editor .ql-indent-3:not(.ql-direction-rtl) {
+  padding-left: 9em;
+}
+.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {
+  padding-left: 10.5em;
+}
+.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {
+  padding-right: 9em;
+}
+.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {
+  padding-right: 10.5em;
+}
+.ql-editor .ql-indent-4:not(.ql-direction-rtl) {
+  padding-left: 12em;
+}
+.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {
+  padding-left: 13.5em;
+}
+.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {
+  padding-right: 12em;
+}
+.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {
+  padding-right: 13.5em;
+}
+.ql-editor .ql-indent-5:not(.ql-direction-rtl) {
+  padding-left: 15em;
+}
+.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {
+  padding-left: 16.5em;
+}
+.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {
+  padding-right: 15em;
+}
+.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {
+  padding-right: 16.5em;
+}
+.ql-editor .ql-indent-6:not(.ql-direction-rtl) {
+  padding-left: 18em;
+}
+.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {
+  padding-left: 19.5em;
+}
+.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {
+  padding-right: 18em;
+}
+.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {
+  padding-right: 19.5em;
+}
+.ql-editor .ql-indent-7:not(.ql-direction-rtl) {
+  padding-left: 21em;
+}
+.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {
+  padding-left: 22.5em;
+}
+.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {
+  padding-right: 21em;
+}
+.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {
+  padding-right: 22.5em;
+}
+.ql-editor .ql-indent-8:not(.ql-direction-rtl) {
+  padding-left: 24em;
+}
+.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {
+  padding-left: 25.5em;
+}
+.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {
+  padding-right: 24em;
+}
+.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {
+  padding-right: 25.5em;
+}
+.ql-editor .ql-indent-9:not(.ql-direction-rtl) {
+  padding-left: 27em;
+}
+.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {
+  padding-left: 28.5em;
+}
+.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {
+  padding-right: 27em;
+}
+.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {
+  padding-right: 28.5em;
+}
+.ql-editor /deep/ .ql-video {
+  display: block;
+  width: 100%;
+  margin-bottom: 24rpx;
+  video{width: 100%;}
+}
+.ql-editor .ql-video.ql-align-center {
+  margin: 0 auto;
+}
+.ql-editor .ql-video.ql-align-right {
+  margin: 0 0 0 auto;
+}
+.ql-editor .ql-bg-black {
+  background-color: #000;
+}
+.ql-editor .ql-bg-red {
+  background-color: #e60000;
+}
+.ql-editor .ql-bg-orange {
+  background-color: #f90;
+}
+.ql-editor .ql-bg-yellow {
+  background-color: #ff0;
+}
+.ql-editor .ql-bg-green {
+  background-color: #008a00;
+}
+.ql-editor .ql-bg-blue {
+  background-color: #06c;
+}
+.ql-editor .ql-bg-purple {
+  background-color: #93f;
+}
+.ql-editor .ql-color-white {
+  color: #fff;
+}
+.ql-editor .ql-color-red {
+  color: #e60000;
+}
+.ql-editor .ql-color-orange {
+  color: #f90;
+}
+.ql-editor .ql-color-yellow {
+  color: #ff0;
+}
+.ql-editor .ql-color-green {
+  color: #008a00;
+}
+.ql-editor .ql-color-blue {
+  color: #06c;
+}
+.ql-editor .ql-color-purple {
+  color: #93f;
+}
+.ql-editor .ql-font-serif {
+  font-family: Georgia, Times New Roman, serif;
+}
+.ql-editor .ql-font-monospace {
+  font-family: Monaco, Courier New, monospace;
+}
+.ql-editor .ql-size-small {
+  font-size: 0.75em;
+}
+.ql-editor .ql-size-large {
+  font-size: 1.5em;
+}
+.ql-editor .ql-size-huge {
+  font-size: 2.5em;
+}
+.ql-editor .ql-direction-rtl {
+  direction: rtl;
+  text-align: inherit;
+}
+.ql-editor .ql-align-center {
+  text-align: center;
+}
+.ql-editor .ql-align-justify {
+  text-align: justify;
+}
+.ql-editor .ql-align-right {
+  text-align: right;
+}
+.ql-editor.ql-blank::before {
+  color: rgba(0,0,0,0.6);
+  content: attr(data-placeholder);
+  font-style: italic;
+  left: 15px;
+  pointer-events: none;
+  position: absolute;
+  right: 15px;
+}

+ 945 - 0
h5_web/static/css/quill.snow.scss

@@ -0,0 +1,945 @@
+/*!
+ * Quill Editor v1.3.7
+ * https://quilljs.com/
+ * Copyright (c) 2014, Jason Chen
+ * Copyright (c) 2013, salesforce.com
+ */
+.ql-container {
+  box-sizing: border-box;
+  font-family: Helvetica, Arial, sans-serif;
+  font-size: 13px;
+  height: 100%;
+  margin: 0px;
+  position: relative;
+}
+.ql-container.ql-disabled .ql-tooltip {
+  visibility: hidden;
+}
+.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {
+  pointer-events: none;
+}
+.ql-clipboard {
+  left: -100000px;
+  height: 1px;
+  overflow-y: hidden;
+  position: absolute;
+  top: 50%;
+}
+.ql-clipboard p {
+  margin: 0;
+  padding: 0;
+}
+.ql-editor {
+  box-sizing: border-box;
+  line-height: 1.42;
+  height: 100%;
+  outline: none;
+  overflow-y: auto;
+  padding: 12px 15px;
+  tab-size: 4;
+  -moz-tab-size: 4;
+  text-align: left;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+}
+.ql-editor > * {
+  cursor: text;
+}
+.ql-editor p,
+.ql-editor ol,
+.ql-editor ul,
+.ql-editor pre,
+.ql-editor blockquote,
+.ql-editor h1,
+.ql-editor h2,
+.ql-editor h3,
+.ql-editor h4,
+.ql-editor h5,
+.ql-editor h6 {
+  margin: 0;
+  padding: 0;
+  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol,
+.ql-editor ul {
+  padding-left: 1.5em;
+}
+.ql-editor ol > li,
+.ql-editor ul > li {
+  list-style-type: none;
+}
+.ql-editor ul > li::before {
+  content: '\2022';
+}
+.ql-editor ul[data-checked=true],
+.ql-editor ul[data-checked=false] {
+  pointer-events: none;
+}
+.ql-editor ul[data-checked=true] > li *,
+.ql-editor ul[data-checked=false] > li * {
+  pointer-events: all;
+}
+.ql-editor ul[data-checked=true] > li::before,
+.ql-editor ul[data-checked=false] > li::before {
+  color: #777;
+  cursor: pointer;
+  pointer-events: all;
+}
+.ql-editor ul[data-checked=true] > li::before {
+  content: '\2611';
+}
+.ql-editor ul[data-checked=false] > li::before {
+  content: '\2610';
+}
+.ql-editor li::before {
+  display: inline-block;
+  white-space: nowrap;
+  width: 1.2em;
+}
+.ql-editor li:not(.ql-direction-rtl)::before {
+  margin-left: -1.5em;
+  margin-right: 0.3em;
+  text-align: right;
+}
+.ql-editor li.ql-direction-rtl::before {
+  margin-left: 0.3em;
+  margin-right: -1.5em;
+}
+.ql-editor ol li:not(.ql-direction-rtl),
+.ql-editor ul li:not(.ql-direction-rtl) {
+  padding-left: 1.5em;
+}
+.ql-editor ol li.ql-direction-rtl,
+.ql-editor ul li.ql-direction-rtl {
+  padding-right: 1.5em;
+}
+.ql-editor ol li {
+  counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+  counter-increment: list-0;
+}
+.ql-editor ol li:before {
+  content: counter(list-0, decimal) '. ';
+}
+.ql-editor ol li.ql-indent-1 {
+  counter-increment: list-1;
+}
+.ql-editor ol li.ql-indent-1:before {
+  content: counter(list-1, lower-alpha) '. ';
+}
+.ql-editor ol li.ql-indent-1 {
+  counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-2 {
+  counter-increment: list-2;
+}
+.ql-editor ol li.ql-indent-2:before {
+  content: counter(list-2, lower-roman) '. ';
+}
+.ql-editor ol li.ql-indent-2 {
+  counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-3 {
+  counter-increment: list-3;
+}
+.ql-editor ol li.ql-indent-3:before {
+  content: counter(list-3, decimal) '. ';
+}
+.ql-editor ol li.ql-indent-3 {
+  counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-4 {
+  counter-increment: list-4;
+}
+.ql-editor ol li.ql-indent-4:before {
+  content: counter(list-4, lower-alpha) '. ';
+}
+.ql-editor ol li.ql-indent-4 {
+  counter-reset: list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-5 {
+  counter-increment: list-5;
+}
+.ql-editor ol li.ql-indent-5:before {
+  content: counter(list-5, lower-roman) '. ';
+}
+.ql-editor ol li.ql-indent-5 {
+  counter-reset: list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-6 {
+  counter-increment: list-6;
+}
+.ql-editor ol li.ql-indent-6:before {
+  content: counter(list-6, decimal) '. ';
+}
+.ql-editor ol li.ql-indent-6 {
+  counter-reset: list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-7 {
+  counter-increment: list-7;
+}
+.ql-editor ol li.ql-indent-7:before {
+  content: counter(list-7, lower-alpha) '. ';
+}
+.ql-editor ol li.ql-indent-7 {
+  counter-reset: list-8 list-9;
+}
+.ql-editor ol li.ql-indent-8 {
+  counter-increment: list-8;
+}
+.ql-editor ol li.ql-indent-8:before {
+  content: counter(list-8, lower-roman) '. ';
+}
+.ql-editor ol li.ql-indent-8 {
+  counter-reset: list-9;
+}
+.ql-editor ol li.ql-indent-9 {
+  counter-increment: list-9;
+}
+.ql-editor ol li.ql-indent-9:before {
+  content: counter(list-9, decimal) '. ';
+}
+.ql-editor .ql-indent-1:not(.ql-direction-rtl) {
+  padding-left: 3em;
+}
+.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {
+  padding-left: 4.5em;
+}
+.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {
+  padding-right: 3em;
+}
+.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {
+  padding-right: 4.5em;
+}
+.ql-editor .ql-indent-2:not(.ql-direction-rtl) {
+  padding-left: 6em;
+}
+.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {
+  padding-left: 7.5em;
+}
+.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {
+  padding-right: 6em;
+}
+.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {
+  padding-right: 7.5em;
+}
+.ql-editor .ql-indent-3:not(.ql-direction-rtl) {
+  padding-left: 9em;
+}
+.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {
+  padding-left: 10.5em;
+}
+.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {
+  padding-right: 9em;
+}
+.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {
+  padding-right: 10.5em;
+}
+.ql-editor .ql-indent-4:not(.ql-direction-rtl) {
+  padding-left: 12em;
+}
+.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {
+  padding-left: 13.5em;
+}
+.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {
+  padding-right: 12em;
+}
+.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {
+  padding-right: 13.5em;
+}
+.ql-editor .ql-indent-5:not(.ql-direction-rtl) {
+  padding-left: 15em;
+}
+.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {
+  padding-left: 16.5em;
+}
+.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {
+  padding-right: 15em;
+}
+.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {
+  padding-right: 16.5em;
+}
+.ql-editor .ql-indent-6:not(.ql-direction-rtl) {
+  padding-left: 18em;
+}
+.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {
+  padding-left: 19.5em;
+}
+.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {
+  padding-right: 18em;
+}
+.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {
+  padding-right: 19.5em;
+}
+.ql-editor .ql-indent-7:not(.ql-direction-rtl) {
+  padding-left: 21em;
+}
+.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {
+  padding-left: 22.5em;
+}
+.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {
+  padding-right: 21em;
+}
+.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {
+  padding-right: 22.5em;
+}
+.ql-editor .ql-indent-8:not(.ql-direction-rtl) {
+  padding-left: 24em;
+}
+.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {
+  padding-left: 25.5em;
+}
+.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {
+  padding-right: 24em;
+}
+.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {
+  padding-right: 25.5em;
+}
+.ql-editor .ql-indent-9:not(.ql-direction-rtl) {
+  padding-left: 27em;
+}
+.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {
+  padding-left: 28.5em;
+}
+.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {
+  padding-right: 27em;
+}
+.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {
+  padding-right: 28.5em;
+}
+.ql-editor .ql-video {
+  display: block;
+  max-width: 100%;
+}
+.ql-editor .ql-video.ql-align-center {
+  margin: 0 auto;
+}
+.ql-editor .ql-video.ql-align-right {
+  margin: 0 0 0 auto;
+}
+.ql-editor .ql-bg-black {
+  background-color: #000;
+}
+.ql-editor .ql-bg-red {
+  background-color: #e60000;
+}
+.ql-editor .ql-bg-orange {
+  background-color: #f90;
+}
+.ql-editor .ql-bg-yellow {
+  background-color: #ff0;
+}
+.ql-editor .ql-bg-green {
+  background-color: #008a00;
+}
+.ql-editor .ql-bg-blue {
+  background-color: #06c;
+}
+.ql-editor .ql-bg-purple {
+  background-color: #93f;
+}
+.ql-editor .ql-color-white {
+  color: #fff;
+}
+.ql-editor .ql-color-red {
+  color: #e60000;
+}
+.ql-editor .ql-color-orange {
+  color: #f90;
+}
+.ql-editor .ql-color-yellow {
+  color: #ff0;
+}
+.ql-editor .ql-color-green {
+  color: #008a00;
+}
+.ql-editor .ql-color-blue {
+  color: #06c;
+}
+.ql-editor .ql-color-purple {
+  color: #93f;
+}
+.ql-editor .ql-font-serif {
+  font-family: Georgia, Times New Roman, serif;
+}
+.ql-editor .ql-font-monospace {
+  font-family: Monaco, Courier New, monospace;
+}
+.ql-editor .ql-size-small {
+  font-size: 0.75em;
+}
+.ql-editor .ql-size-large {
+  font-size: 1.5em;
+}
+.ql-editor .ql-size-huge {
+  font-size: 2.5em;
+}
+.ql-editor .ql-direction-rtl {
+  direction: rtl;
+  text-align: inherit;
+}
+.ql-editor .ql-align-center {
+  text-align: center;
+}
+.ql-editor .ql-align-justify {
+  text-align: justify;
+}
+.ql-editor .ql-align-right {
+  text-align: right;
+}
+.ql-editor.ql-blank::before {
+  color: rgba(0,0,0,0.6);
+  content: attr(data-placeholder);
+  font-style: italic;
+  left: 15px;
+  pointer-events: none;
+  position: absolute;
+  right: 15px;
+}
+.ql-snow.ql-toolbar:after,
+.ql-snow .ql-toolbar:after {
+  clear: both;
+  content: '';
+  display: table;
+}
+.ql-snow.ql-toolbar button,
+.ql-snow .ql-toolbar button {
+  background: none;
+  border: none;
+  cursor: pointer;
+  display: inline-block;
+  float: left;
+  height: 24px;
+  padding: 3px 5px;
+  width: 28px;
+}
+.ql-snow.ql-toolbar button svg,
+.ql-snow .ql-toolbar button svg {
+  float: left;
+  height: 100%;
+}
+.ql-snow.ql-toolbar button:active:hover,
+.ql-snow .ql-toolbar button:active:hover {
+  outline: none;
+}
+.ql-snow.ql-toolbar input.ql-image[type=file],
+.ql-snow .ql-toolbar input.ql-image[type=file] {
+  display: none;
+}
+.ql-snow.ql-toolbar button:hover,
+.ql-snow .ql-toolbar button:hover,
+.ql-snow.ql-toolbar button:focus,
+.ql-snow .ql-toolbar button:focus,
+.ql-snow.ql-toolbar button.ql-active,
+.ql-snow .ql-toolbar button.ql-active,
+.ql-snow.ql-toolbar .ql-picker-label:hover,
+.ql-snow .ql-toolbar .ql-picker-label:hover,
+.ql-snow.ql-toolbar .ql-picker-label.ql-active,
+.ql-snow .ql-toolbar .ql-picker-label.ql-active,
+.ql-snow.ql-toolbar .ql-picker-item:hover,
+.ql-snow .ql-toolbar .ql-picker-item:hover,
+.ql-snow.ql-toolbar .ql-picker-item.ql-selected,
+.ql-snow .ql-toolbar .ql-picker-item.ql-selected {
+  color: #06c;
+}
+.ql-snow.ql-toolbar button:hover .ql-fill,
+.ql-snow .ql-toolbar button:hover .ql-fill,
+.ql-snow.ql-toolbar button:focus .ql-fill,
+.ql-snow .ql-toolbar button:focus .ql-fill,
+.ql-snow.ql-toolbar button.ql-active .ql-fill,
+.ql-snow .ql-toolbar button.ql-active .ql-fill,
+.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill,
+.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill,
+.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill,
+.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill,
+.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill,
+.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill,
+.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill,
+.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill,
+.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill {
+  fill: #06c;
+}
+.ql-snow.ql-toolbar button:hover .ql-stroke,
+.ql-snow .ql-toolbar button:hover .ql-stroke,
+.ql-snow.ql-toolbar button:focus .ql-stroke,
+.ql-snow .ql-toolbar button:focus .ql-stroke,
+.ql-snow.ql-toolbar button.ql-active .ql-stroke,
+.ql-snow .ql-toolbar button.ql-active .ql-stroke,
+.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke,
+.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke,
+.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke,
+.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke,
+.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke,
+.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke,
+.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
+.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
+.ql-snow.ql-toolbar button:hover .ql-stroke-miter,
+.ql-snow .ql-toolbar button:hover .ql-stroke-miter,
+.ql-snow.ql-toolbar button:focus .ql-stroke-miter,
+.ql-snow .ql-toolbar button:focus .ql-stroke-miter,
+.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter,
+.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter,
+.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
+.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
+.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
+.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
+.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
+.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
+.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,
+.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter {
+  stroke: #06c;
+}
+@media (pointer: coarse) {
+  .ql-snow.ql-toolbar button:hover:not(.ql-active),
+  .ql-snow .ql-toolbar button:hover:not(.ql-active) {
+    color: #444;
+  }
+  .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-fill,
+  .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-fill,
+  .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,
+  .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill {
+    fill: #444;
+  }
+  .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke,
+  .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke,
+  .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,
+  .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter {
+    stroke: #444;
+  }
+}
+.ql-snow {
+  box-sizing: border-box;
+}
+.ql-snow * {
+  box-sizing: border-box;
+}
+.ql-snow .ql-hidden {
+  display: none;
+}
+.ql-snow .ql-out-bottom,
+.ql-snow .ql-out-top {
+  visibility: hidden;
+}
+.ql-snow .ql-tooltip {
+  position: absolute;
+  transform: translateY(10px);
+}
+.ql-snow .ql-tooltip a {
+  cursor: pointer;
+  text-decoration: none;
+}
+.ql-snow .ql-tooltip.ql-flip {
+  transform: translateY(-10px);
+}
+.ql-snow .ql-formats {
+  display: inline-block;
+  vertical-align: middle;
+}
+.ql-snow .ql-formats:after {
+  clear: both;
+  content: '';
+  display: table;
+}
+.ql-snow .ql-stroke {
+  fill: none;
+  stroke: #444;
+  stroke-linecap: round;
+  stroke-linejoin: round;
+  stroke-width: 2;
+}
+.ql-snow .ql-stroke-miter {
+  fill: none;
+  stroke: #444;
+  stroke-miterlimit: 10;
+  stroke-width: 2;
+}
+.ql-snow .ql-fill,
+.ql-snow .ql-stroke.ql-fill {
+  fill: #444;
+}
+.ql-snow .ql-empty {
+  fill: none;
+}
+.ql-snow .ql-even {
+  fill-rule: evenodd;
+}
+.ql-snow .ql-thin,
+.ql-snow .ql-stroke.ql-thin {
+  stroke-width: 1;
+}
+.ql-snow .ql-transparent {
+  opacity: 0.4;
+}
+.ql-snow .ql-direction svg:last-child {
+  display: none;
+}
+.ql-snow .ql-direction.ql-active svg:last-child {
+  display: inline;
+}
+.ql-snow .ql-direction.ql-active svg:first-child {
+  display: none;
+}
+.ql-snow .ql-editor h1 {
+  font-size: 2em;
+}
+.ql-snow .ql-editor h2 {
+  font-size: 1.5em;
+}
+.ql-snow .ql-editor h3 {
+  font-size: 1.17em;
+}
+.ql-snow .ql-editor h4 {
+  font-size: 1em;
+}
+.ql-snow .ql-editor h5 {
+  font-size: 0.83em;
+}
+.ql-snow .ql-editor h6 {
+  font-size: 0.67em;
+}
+.ql-snow .ql-editor a {
+  text-decoration: underline;
+}
+.ql-snow .ql-editor blockquote {
+  border-left: 4px solid #ccc;
+  margin-bottom: 5px;
+  margin-top: 5px;
+  padding-left: 16px;
+}
+.ql-snow .ql-editor code,
+.ql-snow .ql-editor pre {
+  background-color: #f0f0f0;
+  border-radius: 3px;
+}
+.ql-snow .ql-editor pre {
+  white-space: pre-wrap;
+  margin-bottom: 5px;
+  margin-top: 5px;
+  padding: 5px 10px;
+}
+.ql-snow .ql-editor code {
+  font-size: 85%;
+  padding: 2px 4px;
+}
+.ql-snow .ql-editor pre.ql-syntax {
+  background-color: #23241f;
+  color: #f8f8f2;
+  overflow: visible;
+}
+.ql-snow .ql-editor img {
+  max-width: 100%;
+}
+.ql-snow .ql-picker {
+  color: #444;
+  display: inline-block;
+  float: left;
+  font-size: 14px;
+  font-weight: 500;
+  height: 24px;
+  position: relative;
+  vertical-align: middle;
+}
+.ql-snow .ql-picker-label {
+  cursor: pointer;
+  display: inline-block;
+  height: 100%;
+  padding-left: 8px;
+  padding-right: 2px;
+  position: relative;
+  width: 100%;
+}
+.ql-snow .ql-picker-label::before {
+  display: inline-block;
+  line-height: 22px;
+}
+.ql-snow .ql-picker-options {
+  background-color: #fff;
+  display: none;
+  min-width: 100%;
+  padding: 4px 8px;
+  position: absolute;
+  white-space: nowrap;
+}
+.ql-snow .ql-picker-options .ql-picker-item {
+  cursor: pointer;
+  display: block;
+  padding-bottom: 5px;
+  padding-top: 5px;
+}
+.ql-snow .ql-picker.ql-expanded .ql-picker-label {
+  color: #ccc;
+  z-index: 2;
+}
+.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill {
+  fill: #ccc;
+}
+.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke {
+  stroke: #ccc;
+}
+.ql-snow .ql-picker.ql-expanded .ql-picker-options {
+  display: block;
+  margin-top: -1px;
+  top: 100%;
+  z-index: 1;
+}
+.ql-snow .ql-color-picker,
+.ql-snow .ql-icon-picker {
+  width: 28px;
+}
+.ql-snow .ql-color-picker .ql-picker-label,
+.ql-snow .ql-icon-picker .ql-picker-label {
+  padding: 2px 4px;
+}
+.ql-snow .ql-color-picker .ql-picker-label svg,
+.ql-snow .ql-icon-picker .ql-picker-label svg {
+  right: 4px;
+}
+.ql-snow .ql-icon-picker .ql-picker-options {
+  padding: 4px 0px;
+}
+.ql-snow .ql-icon-picker .ql-picker-item {
+  height: 24px;
+  width: 24px;
+  padding: 2px 4px;
+}
+.ql-snow .ql-color-picker .ql-picker-options {
+  padding: 3px 5px;
+  width: 152px;
+}
+.ql-snow .ql-color-picker .ql-picker-item {
+  border: 1px solid transparent;
+  float: left;
+  height: 16px;
+  margin: 2px;
+  padding: 0px;
+  width: 16px;
+}
+.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {
+  position: absolute;
+  margin-top: -9px;
+  right: 0;
+  top: 50%;
+  width: 18px;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before,
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before,
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before {
+  content: attr(data-label);
+}
+.ql-snow .ql-picker.ql-header {
+  width: 98px;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item::before {
+  content: 'Normal';
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
+  content: 'Heading 1';
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
+  content: 'Heading 2';
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
+  content: 'Heading 3';
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
+  content: 'Heading 4';
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
+  content: 'Heading 5';
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
+  content: 'Heading 6';
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
+  font-size: 2em;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
+  font-size: 1.5em;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
+  font-size: 1.17em;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
+  font-size: 1em;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
+  font-size: 0.83em;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
+  font-size: 0.67em;
+}
+.ql-snow .ql-picker.ql-font {
+  width: 108px;
+}
+.ql-snow .ql-picker.ql-font .ql-picker-label::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item::before {
+  content: 'Sans Serif';
+}
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
+  content: 'Serif';
+}
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
+  content: 'Monospace';
+}
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
+  font-family: Georgia, Times New Roman, serif;
+}
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
+  font-family: Monaco, Courier New, monospace;
+}
+.ql-snow .ql-picker.ql-size {
+  width: 98px;
+}
+.ql-snow .ql-picker.ql-size .ql-picker-label::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item::before {
+  content: 'Normal';
+}
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
+  content: 'Small';
+}
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
+  content: 'Large';
+}
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
+  content: 'Huge';
+}
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
+  font-size: 10px;
+}
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
+  font-size: 18px;
+}
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
+  font-size: 32px;
+}
+.ql-snow .ql-color-picker.ql-background .ql-picker-item {
+  background-color: #fff;
+}
+.ql-snow .ql-color-picker.ql-color .ql-picker-item {
+  background-color: #000;
+}
+.ql-toolbar.ql-snow {
+  border: 1px solid #ccc;
+  box-sizing: border-box;
+  font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
+  padding: 8px;
+}
+.ql-toolbar.ql-snow .ql-formats {
+  margin-right: 15px;
+}
+.ql-toolbar.ql-snow .ql-picker-label {
+  border: 1px solid transparent;
+}
+.ql-toolbar.ql-snow .ql-picker-options {
+  border: 1px solid transparent;
+  box-shadow: rgba(0,0,0,0.2) 0 2px 8px;
+}
+.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label {
+  border-color: #ccc;
+}
+.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options {
+  border-color: #ccc;
+}
+.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item.ql-selected,
+.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item:hover {
+  border-color: #000;
+}
+.ql-toolbar.ql-snow + .ql-container.ql-snow {
+  border-top: 0px;
+}
+.ql-snow .ql-tooltip {
+  background-color: #fff;
+  border: 1px solid #ccc;
+  box-shadow: 0px 0px 5px #ddd;
+  color: #444;
+  padding: 5px 12px;
+  white-space: nowrap;
+}
+.ql-snow .ql-tooltip::before {
+  content: "Visit URL:";
+  line-height: 26px;
+  margin-right: 8px;
+}
+.ql-snow .ql-tooltip input[type=text] {
+  display: none;
+  border: 1px solid #ccc;
+  font-size: 13px;
+  height: 26px;
+  margin: 0px;
+  padding: 3px 5px;
+  width: 170px;
+}
+.ql-snow .ql-tooltip a.ql-preview {
+  display: inline-block;
+  max-width: 200px;
+  overflow-x: hidden;
+  text-overflow: ellipsis;
+  vertical-align: top;
+}
+.ql-snow .ql-tooltip a.ql-action::after {
+  border-right: 1px solid #ccc;
+  content: 'Edit';
+  margin-left: 16px;
+  padding-right: 8px;
+}
+.ql-snow .ql-tooltip a.ql-remove::before {
+  content: 'Remove';
+  margin-left: 8px;
+}
+.ql-snow .ql-tooltip a {
+  line-height: 26px;
+}
+.ql-snow .ql-tooltip.ql-editing a.ql-preview,
+.ql-snow .ql-tooltip.ql-editing a.ql-remove {
+  display: none;
+}
+.ql-snow .ql-tooltip.ql-editing input[type=text] {
+  display: inline-block;
+}
+.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
+  border-right: 0px;
+  content: 'Save';
+  padding-right: 0px;
+}
+.ql-snow .ql-tooltip[data-mode=link]::before {
+  content: "Enter link:";
+}
+.ql-snow .ql-tooltip[data-mode=formula]::before {
+  content: "Enter formula:";
+}
+.ql-snow .ql-tooltip[data-mode=video]::before {
+  content: "Enter video:";
+}
+.ql-snow a {
+  color: #06c;
+}
+.ql-container.ql-snow {
+  border: 1px solid #ccc;
+}

BIN
h5_web/static/icons/code-gray.png


BIN
h5_web/static/icons/code.png


BIN
h5_web/static/icons/index-gray.png


BIN
h5_web/static/icons/index-selected.png


BIN
h5_web/static/icons/index.png


BIN
h5_web/static/icons/mine-gray.png


BIN
h5_web/static/icons/mine.png


BIN
h5_web/static/img/banner-myBasicTraining.png


BIN
h5_web/static/img/banner-skillsTraining.png


BIN
h5_web/static/img/banner-upgrade.png


BIN
h5_web/static/img/basicTraining-bg.png


BIN
h5_web/static/img/default-avatar.png


BIN
h5_web/static/img/empty.png


BIN
h5_web/static/img/index-service-01.png


BIN
h5_web/static/img/index-service-02.png


BIN
h5_web/static/img/index-service-03.png


BIN
h5_web/static/img/index-service-04.png


BIN
h5_web/static/img/index-service-05.png


BIN
h5_web/static/img/index-service-06.png


BIN
h5_web/static/img/index-service-07.png


BIN
h5_web/static/img/index-service-08.png


BIN
h5_web/static/img/major-党务工作.png


BIN
h5_web/static/img/major-工商企业管理.png


BIN
h5_web/static/img/major-应用化工技术.png


BIN
h5_web/static/img/major-建筑工程技术.png


BIN
h5_web/static/img/major-机电一体化技术.png


BIN
h5_web/static/img/major-计算机应用技术.png


BIN
h5_web/static/img/major01.png


BIN
h5_web/static/img/major02.png


BIN
h5_web/static/img/major03.png


BIN
h5_web/static/img/major04.png


BIN
h5_web/static/img/major05.png


BIN
h5_web/static/img/major06.png


BIN
h5_web/static/img/mycode-bg.png


BIN
h5_web/static/img/skill-01.png


BIN
h5_web/static/img/skill-02.png


BIN
h5_web/static/img/skill-03.png


BIN
h5_web/static/img/skill-04.png


BIN
h5_web/static/img/skill-05.png


BIN
h5_web/static/img/skill-06.png


BIN
h5_web/static/img/skill-农产品食品检验员.png


BIN
h5_web/static/img/skill-化学检验员.png


BIN
h5_web/static/img/skill-工业机器人.png


BIN
h5_web/static/img/skill-工业机器人系统运维员.png


BIN
h5_web/static/img/skill-机床装调维修工.png


BIN
h5_web/static/img/skill-汽车维修.png


BIN
h5_web/static/img/skill-焊接.png


BIN
h5_web/static/img/skill-电工.png


BIN
h5_web/static/img/skill-车身修理.png


BIN
h5_web/static/img/tip.png


File diff suppressed because it is too large
+ 0 - 0
h5_web/static/js/jweixin-1.4.0.js


+ 46 - 0
h5_web/static/js/mock.js

@@ -0,0 +1,46 @@
+const mock = {
+	schoolList:[
+		{img:'https://static-data.eol.cn/upload/logo/516.jpg',school:'贵州民族大学',content:'贵州民族大学创建于1951年5月 17 日,隶属贵州省人民政府,是新中国创建最早的民族院校之一,是贵州省重点建设高校、贵州省人民政府和国家民委共建高校,中国政府奖学金来华留学生培养高校。学校现有两个校区,花溪校区坐落于山清水秀、被誉为“高原明珠”的风景名胜区贵阳市花溪区,大学城校区坐落在产城融合创新、生态文明示范的国家级新区贵安新区,占地面积共2825亩,有全日制在校生2万余人。建校以来,为社会培养输送了15万余名各级各类人才,为贵州及民族地区经济社会发展提供了重要的人才支撑。'},
+		{img:'http://www.gzu.edu.cn/_upload/tpl/00/02/2/template2/images/logo.png',school:'贵州大学',content:'贵州大学(Guizhou University),简称“贵大”,位于贵州省贵阳市,是教育部与贵州省人民政府“以部为主、部省合建”高校,是国家“世界一流学科建设高校”、“211工程”重点建设高校、“一省一校”重点建设高校,入选国家“2011计划”、“111计划”、中国政府奖学金来华留学生接收院校、孔子学院奖学金院校、全国深化创新创业教育改革示范高校、教育部“三全育人”综合改革试点单位、中西部高校联盟成员、国家大学生文化素质教育基地、教育部来华留学示范基地、教育部教育援外基地、科技部国际科技合作基地, [1]  、卓越法律人才教育培养计划、卓越农林人才教育培养计划、卓越工程师教育培养计划 [2]  、教育部“中西部高校综合实力提升工程”并成为教育部在西部地区重点建设的14所高水平大学之一,是浙江大学、中国农业大学、华东师范大学对口合作建设高校。'},
+		{img:'https://static-data.eol.cn/upload/logo/514.jpg',school:'贵州师范大学',content:'贵州师范大学的前身——“国立贵阳师范学院”创建于1941年,是当时全国八所国立师范学院之一。建校之初,以王克仁、齐泮林、曾景、萧文灿、王佩芬、朱厚锟、尹炎武、李独清、李锐夫、杨宪益、姚奠中、夏元瑮、谢六逸、谭戒甫、向义、熊铭青、郝新吾、汤炳正、赵咸云、梁祖荫等为代表的一批知名学者来校任教。'},
+		{img:'https://static-data.eol.cn/upload/logo/520.jpg',school:'贵州财经大学',content:'贵州财经大学,创办于1958年,原名贵州财经学院,2012年,经教育部批准更为现名,是贵州省委、省政府重点建设的贵州省经济管理人才培养基地。著名经济学家、“孙冶方经济学奖”获得者、南京大学原党委书记洪银兴教授任贵州财经大学名誉校长。'},
+		{img:'https://static-data.eol.cn/upload/logo/1071.jpg',school:'贵阳学院',content:' 贵阳学院是由贵阳师范高等专科学校和贵阳市金筑大学于2004年合并组建的、省市共建以市为主的全日制本科普通高校。贵阳师范高等专科学校建于1985年(其前身是1978年设立的“贵阳师范学院贵阳专科班”),2001年与贵州师范大学联合举办本科教育。贵阳市金筑大学是成立于1984年的高等专科学校。2009年,贵阳师范学校并入。2010年,学校作为全国试点学校通过了教育部本科教学工作合格评估。2017年8月,被确定为贵州省新建本科学校中唯一的新增硕士学位授予权按需推荐单位。'},
+	],
+	allSchoolList:[
+		{id:1,name:'贵州工业职业技术学院',number:6,use:'公办',img:'https://static-data.eol.cn/upload/logo/2567.jpg',url:'http://www.gzky.edu.cn'},
+		{id:2,name:'贵州交通职业技术学院',number:5,use:'公办',img:'https://static-data.eol.cn/upload/logo/2267.jpg',url:'http://www.gzjtzy.net'},
+		{id:3,name:'贵州职业技术学院',number:3,use:'公办',img:'https://static-data.eol.cn/upload/logo/2569.jpg',url:'https://www.gzvti.com/xysy.htm'},
+		{id:4,name:'贵州轻工职业技术学院',number:1,use:'公办',img:'https://static-data.eol.cn/upload/logo/2273.jpg',url:'http://www.gzqy.cn/index.jsp'},
+		{id:5,name:'贵州建设职业技术学院',number:2,use:'公办',img:'https://static-data.eol.cn/upload/logo/3197.jpg',url:'http://www.gzjszy.cn'},
+		{id:6,name:'贵州装备制造职业学院',number:2,use:'公办',img:'https://static-data.eol.cn/upload/logo/3301.jpg',url:'http://www.gzzbzy.cn'},
+		{id:7,name:'贵阳康养职业大学(原贵阳护理职业学院)',number:6,use:'公办',img:'https://static-data.eol.cn/upload/logo/1071.jpg',url:'http://www.gynvc.edu.cn'},
+		{id:8,name:'贵州水利水电职业技术学院',number:3,use:'公办',img:'https://static-data.eol.cn/upload/logo/3233.jpg',url:'http://www.gzsdzy.cn'},
+		{id:9,name:'贵州农业职业学院',number:2,use:'公办',img:'https://static-data.eol.cn/upload/logo/3234.jpg',url:'http://www.gznyzyxy.cn'},
+		{id:10,name:'贵州食品工程职业学院',number:3,use:'公办',img:'https://static-data.eol.cn/upload/logo/3300.jpg',url:'http://www.gzspzy.cn'},
+		{id:11,name:'贵州电子科技职业学院',number:2,use:'公办',img:'https://static-data.eol.cn/upload/logo/1071.jpg',url:'http://www.gzdzxy.cn'},
+		{id:12,name:'贵州电子商务职业技术学院',number:3,use:'公办',img:'https://static-data.eol.cn/upload/logo/3298.jpg',url:'http://www.gzdsxy.org.cn'},
+		{id:13,name:'贵阳职业技术学院',number:3,use:'公办',img:'https://static-data.eol.cn/upload/logo/1590.jpg',url:'http://www.gyvtc.edu.cn'},
+		{id:14,name:'贵阳幼儿师范高等专科学校',number:0,use:'公办',img:'https://static-data.eol.cn/upload/logo/3225.jpg',url:'http://www.gypec.edu.cn'},
+		{id:15,name:'贵州工商职业学院',number:4,use:'民办',img:'https://static-data.eol.cn/upload/logo/3117.jpg',url:'http://www.gzgszy.com'},
+		{id:16,name:'贵州城市职业学院',number:4,use:'民办',img:'https://static-data.eol.cn/upload/logo/2277.jpg',url:'http://www.gzcsxy.cn'},
+	],
+	majorList:[
+		{img:'/static/img/major-党务工作.png',name:'党务工作',school:''},
+		{img:'/static/img/major-工商企业管理.png',name:'工商企业管理',school:''},
+		{img:'/static/img/major-机电一体化技术.png',name:'机电一体化技术',school:''},
+		{img:'/static/img/major-计算机应用技术.png',name:'计算机应用技术',school:''},
+		{img:'/static/img/major-建筑工程技术.png',name:'建筑工程技术',school:''},
+		{img:'/static/img/major-应用化工技术.png',name:'应用化工技术',school:''},
+	],
+	skillList:[
+		{img:'/static/img/skill-车身修理.png',name:'车身修理',school:''},
+		{img:'/static/img/skill-电工.png',name:'电工',school:''},
+		{img:'/static/img/skill-工业机器人.png',name:'工业机器人',school:''},
+		{img:'/static/img/skill-焊接.png',name:'焊接',school:''},
+		{img:'/static/img/skill-化学检验员.png',name:'化学检验员',school:''},
+		{img:'/static/img/skill-机床装调维修工.png',name:'机床装调维修工',school:''},
+		{img:'/static/img/skill-农产品食品检验员.png',name:'农产品食品检验员',school:''},
+		{img:'/static/img/skill-汽车维修.png',name:'汽车维修',school:''},
+	],
+}
+export default mock;

+ 27 - 0
h5_web/store/$u.mixin.js

@@ -0,0 +1,27 @@
+import { mapState } from 'vuex'
+import store from "@/store"
+
+// 尝试将用户在根目录中的store/index.js的vuex的state变量,全部加载到全局变量中
+let $uStoreKey = [];
+try{
+	$uStoreKey = store.state ? Object.keys(store.state) : [];
+}catch(e){
+	
+}
+
+module.exports = {
+	beforeCreate() {
+		// 将vuex方法挂在到$u中
+		// 使用方法为:如果要修改vuex的state中的user.name变量为"史诗" => this.$u.vuex('user.name', '史诗')
+		// 如果要修改vuex的state的version变量为1.0.1 => this.$u.vuex('version', '1.0.1')
+		this.$u.vuex = (name, value) => {
+			this.$store.commit('$uStore', {
+				name,value
+			})
+		}
+	},
+	computed: {
+		// 将vuex的state中的所有变量,解构到全局混入的mixin中
+		...mapState($uStoreKey)
+	}
+}

+ 63 - 0
h5_web/store/index.js

@@ -0,0 +1,63 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+Vue.use(Vuex)
+
+let lifeData = {};
+
+try{
+	// 尝试获取本地是否存在lifeData变量,第一次启动APP时是不存在的
+	lifeData = uni.getStorageSync('lifeData');
+}catch(e){
+	
+}
+
+// 需要永久存储,且下次APP启动需要取出的,在state中的变量名
+
+let saveStateKeys = ['vuex_user', 'vuex_token','vuex_hasLogin'];
+
+// 保存变量到本地存储中
+const saveLifeData = function(key, value){
+	// 判断变量名是否在需要存储的数组中
+	if(saveStateKeys.indexOf(key) != -1) {
+		// 获取本地存储的lifeData对象,将变量添加到对象中
+		let tmp = uni.getStorageSync('lifeData');
+		// 第一次打开APP,不存在lifeData变量,故放一个{}空对象
+		tmp = tmp ? tmp : {};
+		tmp[key] = value;
+		// 执行这一步后,所有需要存储的变量,都挂载在本地的lifeData对象中
+		uni.setStorageSync('lifeData', tmp);
+	}
+}
+const store = new Vuex.Store({
+	state: {
+		// 如果上面从本地获取的lifeData对象下有对应的属性,就赋值给state中对应的变量
+		// 加上vuex_前缀,是防止变量名冲突,也让人一目了然
+		vuex_user: lifeData.vuex_user ? lifeData.vuex_user : {},
+		vuex_hasLogin:lifeData.vuex_hasLogin?lifeData.vuex_hasLogin:false,
+		vuex_token: lifeData.vuex_token ? lifeData.vuex_token : ''
+	},
+	mutations: {
+		$uStore(state, payload) {
+			// 判断是否多层级调用,state中为对象存在的情况,诸如user.info.score = 1
+			let nameArr = payload.name.split('.');
+			let saveKey = '';
+			let len = nameArr.length;
+			if(len >= 2) {
+				let obj = state[nameArr[0]];
+				for(let i = 1; i < len - 1; i ++) {
+					obj = obj[nameArr[i]];
+				}
+				obj[nameArr[len - 1]] = payload.value;
+				saveKey = nameArr[0];
+			} else {
+				// 单层级变量,在state就是一个普通变量的情况
+				state[payload.name] = payload.value;
+				saveKey = payload.name;
+			}
+			// 保存变量到本地,见顶部函数定义
+			saveLifeData(saveKey, state[saveKey])
+		}
+	}
+})
+
+export default store

+ 24 - 0
h5_web/template.h5.html

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+	<head>
+		<meta charset="utf-8">
+		<meta http-equiv="X-UA-Compatible" content="IE=edge">
+		<link rel="shortcut icon" type="image/x-icon" href="static/favicon.ico">
+		<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+		<title>
+			<%= htmlWebpackPlugin.options.title %>
+		</title>
+		<script>
+			document.addEventListener('DOMContentLoaded', function() {
+				document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
+			})
+		</script>
+		<link rel="stylesheet" href="<%= BASE_URL %>static/index.css" />
+	</head>
+	<body>
+		<noscript>
+			<strong>本站点必须要开启JavaScript才能运行</strong>
+		</noscript>
+		<div id="app"></div>
+	</body>
+</html>

+ 6 - 0
h5_web/uni.scss

@@ -0,0 +1,6 @@
+/**
+ * 下方引入的为uView UI的集成样式文件,为scss预处理器,其中包含了一些"u-"开头的自定义变量
+ * 使用的时候,请将下面的一行复制到您的uniapp项目根目录的uni.scss中即可
+ * uView自定义的css类名和scss变量,均以"u-"开头,不会造成冲突,请放心使用 
+ */
+@import 'uview-ui/theme.scss';

+ 6 - 0
h5_web/uni_modules/mescroll-uni/changelog.md

@@ -0,0 +1,6 @@
+## 1.3.7(2021-04-13)
+1. 新增`mescroll-swiper-sticky.vue`的示例, 轮播吸顶菜单导航  
+2. 新增`mescroll-empty.vue`的示例, 单独使用空布局组件  
+3. 简化tabs在具体项目中的使用,并简化对应的示例  
+4. mescroll-uni 支持动态禁止滚动的属性 disableScroll (注: mescroll-body不支持)  
+-by 小瑾同学

+ 19 - 0
h5_web/uni_modules/mescroll-uni/components/mescroll-body/mescroll-body.css

@@ -0,0 +1,19 @@
+.mescroll-body {
+	position: relative; /* 下拉刷新区域相对自身定位 */
+	height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
+	overflow: hidden; /* 当有元素写在mescroll-body标签前面时,可遮住下拉刷新区域 */
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 使sticky生效: 父元素不能overflow:hidden或者overflow:auto属性 */
+.mescroll-body.mescorll-sticky{
+	overflow: unset !important
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-safearea {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+}

+ 400 - 0
h5_web/uni_modules/mescroll-uni/components/mescroll-body/mescroll-body.vue

@@ -0,0 +1,400 @@
+<template>
+	<view 
+	class="mescroll-body mescroll-render-touch" 
+	:class="{'mescorll-sticky': sticky}"
+	:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}" 
+	@touchstart="wxsBiz.touchstartEvent" 
+	@touchmove="wxsBiz.touchmoveEvent" 
+	@touchend="wxsBiz.touchendEvent" 
+	@touchcancel="wxsBiz.touchendEvent"
+	:change:prop="wxsBiz.propObserver"
+	:prop="wxsProp"
+	>
+		<!-- 状态栏 -->
+		<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
+		
+		<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
+			<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+			<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+			<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+				<view class="downwarp-content">
+					<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
+					<view class="downwarp-tip">{{downText}}</view>
+				</view>
+			</view>
+	
+			<!-- 列表内容 -->
+			<slot></slot>
+
+			<!-- 空布局 -->
+			<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
+
+			<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
+			<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+			<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
+				<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+				<view v-show="upLoadType===1">
+					<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
+					<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
+				</view>
+				<!-- 无数据 -->
+				<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
+			</view>
+		</view>
+		
+		<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
+		<!-- #ifdef H5 -->
+		<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
+		<!-- #endif -->
+		
+		<!-- 适配iPhoneX -->
+		<view v-if="safearea" class="mescroll-safearea"></view>
+		
+		<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+		
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+<script src="../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from "../mescroll-uni/wxs/renderjs.js";
+	export default {
+		mixins: [renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	// 引入mescroll-uni.js,处理核心逻辑
+	import MeScroll from "../mescroll-uni/mescroll-uni.js";
+	// 引入全局配置
+	import GlobalOption from "../mescroll-uni/mescroll-uni-option.js";
+	// 引入国际化工具类
+	import mescrollI18n from '../mescroll-uni/mescroll-i18n.js';
+	// 引入回到顶部组件
+	import MescrollTop from "../mescroll-uni/components/mescroll-top.vue";
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from "../mescroll-uni/wxs/mixins.js";
+	
+	/**
+	 * mescroll-body 基于page滚动的下拉刷新和上拉加载组件, 支持嵌套原生组件, 性能好
+	 * @property {Object} down 下拉刷新的参数配置
+	 * @property {Object} up 上拉加载的参数配置
+	 * @property {Object} i18n 国际化的参数配置
+	 * @property {String, Number} top 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean, String} topbar 偏移量top是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+	 * @property {String, Number} bottom 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean} safearea 偏移量bottom是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+	 * @property {Boolean} fixed 是否通过fixed固定mescroll的高度, 默认true
+	 * @property {String, Number} height 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+	 * @property {Boolean} bottombar 底部是否偏移TabBar的高度 (仅在H5端的tab页生效)
+	 * @property {Boolean} sticky 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法隐藏
+	 * @event {Function} init 初始化完成的回调 
+	 * @event {Function} down 下拉刷新的回调
+	 * @event {Function} up 上拉加载的回调 
+	 * @event {Function} emptyclick 点击empty配置的btnText按钮回调
+	 * @event {Function} topclick 点击回到顶部的按钮回调
+	 * @event {Function} scroll 滚动监听 (需在 up 配置 onScroll:true 才生效)
+	 * @example <mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"> ... </mescroll-body>
+	 */
+	export default {
+		name: 'mescroll-body',
+		mixins: [WxsMixin],
+		components: {
+			MescrollTop
+		},
+		props: {
+			down: Object,
+			up: Object,
+			i18n: Object,
+			top: [String, Number],
+			topbar: [Boolean, String],
+			bottom: [String, Number],
+			safearea: Boolean,
+			height: [String, Number],
+			bottombar:{
+				type: Boolean,
+				default: true
+			},
+			sticky: Boolean
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
+				upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				windowHeight: 0, // 可使用窗口的高度
+				windowBottom: 0, // 可使用窗口的底部位置
+				statusBarHeight: 0 // 状态栏高度
+			};
+		},
+		computed: {
+			// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+			minHeight(){
+				return this.toPx(this.height || '100%') + 'px'
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top)
+			},
+			padTop() {
+				return this.numTop + 'px';
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom);
+			},
+			padBottom() {
+				return this.numBottom + 'px';
+			},
+			// 是否为重置下拉的状态
+			isDownReset() {
+				return this.downLoadType === 3 || this.downLoadType === 4;
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : '';
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return 'rotate(' + 360 * this.downRate + 'deg)'
+			},
+			// 文本提示
+			downText(){
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
+				switch (this.downLoadType){
+					case 1: return this.mescroll.optDown.textInOffset;
+					case 2: return this.mescroll.optDown.textOutOffset;
+					case 3: return this.mescroll.optDown.textLoading;
+					case 4: return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
+					default: return this.mescroll.optDown.textInOffset;
+				}
+			}
+		},
+		methods: {
+			//number,rpx,upx,px,% --> px的数值
+			toPx(num) {
+				if (typeof num === 'string') {
+					if (num.indexOf('px') !== -1) {
+						if (num.indexOf('rpx') !== -1) {
+							// "10rpx"
+							num = num.replace('rpx', '');
+						} else if (num.indexOf('upx') !== -1) {
+							// "10upx"
+							num = num.replace('upx', '');
+						} else {
+							// "10px"
+							return Number(num.replace('px', ''));
+						}
+					} else if (num.indexOf('%') !== -1) {
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
+						let rate = Number(num.replace('%', '')) / 100;
+						return this.windowHeight * rate;
+					}
+				}
+				return num ? uni.upx2px(Number(num)) : 0;
+			},
+			// 点击空布局的按钮回调
+			emptyClick() {
+				this.$emit('emptyclick', this.mescroll);
+			},
+			// 点击回到顶部的按钮回调
+			toTopClick() {
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
+			}
+		},
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
+		created() {
+			let vm = this;
+
+			let diyOption = {
+				// 下拉刷新的配置
+				down: {
+					inOffset() {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset() {
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					onMoving(mescroll, rate, downHight) {
+						// 下拉过程中的回调,滑动过程一直在执行;
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					beforeEndDownScroll(mescroll){
+						vm.downLoadType = 4; 
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
+							if(vm.downLoadType === 4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll);
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+					}
+				}
+			};
+			
+			let i18nType = mescrollI18n.getType() // 当前语言类型
+			let i18nOption = {type: i18nType} // 国际化配置
+			MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
+			MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
+			MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
+			MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
+			// 挂载语言包
+			vm.mescroll.i18n = i18nOption;
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是page的scroll,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				if(typeof y === 'string'){
+					// 滚动到指定view (y为css选择器)
+					setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let top = rect.top
+								top += vm.mescroll.getScrollTop()
+								uni.pageScrollTo({
+									scrollTop: top,
+									duration: t
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					},30)
+				} else{
+					// 滚动到指定位置 (y必须为数字)
+					uni.pageScrollTo({
+						scrollTop: y,
+						duration: t
+					})
+				}
+			});
+
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+			
+			// 全局配置监听
+			uni.$on("setMescrollGlobalOption", options=>{
+				if(!options) return;
+				let i18nType = options.i18n ? options.i18n.type : null
+				if(i18nType && vm.mescroll.i18n.type != i18nType){
+					vm.mescroll.i18n.type = i18nType
+					mescrollI18n.setType(i18nType)
+					MeScroll.extend(options, vm.mescroll.i18n[i18nType])
+				}
+				if(options.down){
+					let down = MeScroll.extend({}, options.down)
+					vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
+				}
+				if(options.up){
+					let up = MeScroll.extend({}, options.up)
+					vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
+				}
+			})
+		},
+		destroyed() {
+			// 注销全局配置监听
+			uni.$off("setMescrollGlobalOption")
+		}
+	};
+</script>
+
+<style>
+	@import "../mescroll-body/mescroll-body.css";
+	@import "../mescroll-uni/components/mescroll-down.css";
+	@import "../mescroll-uni/components/mescroll-up.css";
+</style>

Some files were not shown because too many files changed in this diff