wyling 4 år sedan
förälder
incheckning
9258a1247c
100 ändrade filer med 7750 tillägg och 148 borttagningar
  1. 660 24
      package-lock.json
  2. 8 2
      package.json
  3. 13 12
      src/App.vue
  4. 14 0
      src/api/amap.js
  5. 55 0
      src/api/answer.js
  6. 84 0
      src/api/applist.js
  7. 3 0
      src/api/baseurl.js
  8. 146 0
      src/api/cinema.js
  9. 95 0
      src/api/home.js
  10. 60 0
      src/api/login.js
  11. 20 0
      src/api/order.js
  12. 18 0
      src/api/pay.js
  13. 42 0
      src/api/studytime.js
  14. 177 0
      src/common/css/animation.css
  15. 36 0
      src/components/appdes/index.vue
  16. 3 0
      src/components/loading/loading.js
  17. 177 0
      src/components/loading/loading.vue
  18. 193 0
      src/components/skeleton/index/index.vue
  19. 188 0
      src/components/skeleton/login.vue
  20. 66 0
      src/components/square/index.vue
  21. 3 0
      src/components/topbar/topbar.js
  22. 32 0
      src/components/topbar/topbar.vue
  23. 176 0
      src/components/xuan-switch/xuan-switch.vue
  24. 31 3
      src/main.js
  25. 76 73
      src/manifest.json
  26. 123 4
      src/pages.json
  27. 108 0
      src/pages/cinema/cinemalist.vue
  28. 36 0
      src/pages/cinema/citylist.vue
  29. 345 0
      src/pages/cinema/orderdes.vue
  30. 490 0
      src/pages/cinema/placeorder.vue
  31. 335 0
      src/pages/cinema/schedulelist.vue
  32. 319 0
      src/pages/cinema/selectseat.vue
  33. 449 0
      src/pages/index/components/applist.vue
  34. 414 0
      src/pages/index/components/cinema.vue
  35. 438 0
      src/pages/index/components/home.vue
  36. 82 0
      src/pages/index/components/login.vue
  37. 288 0
      src/pages/index/components/user.vue
  38. 120 30
      src/pages/index/index.vue
  39. 250 0
      src/pages/user/browserecord.vue
  40. 250 0
      src/pages/user/collectionList.vue
  41. 134 0
      src/pages/user/order.vue
  42. 118 0
      src/pages/user/set.vue
  43. 20 0
      src/pages/webview/webview.vue
  44. BIN
      src/static/imgs/banner.png
  45. BIN
      src/static/imgs/bf.png
  46. BIN
      src/static/imgs/bg.png
  47. BIN
      src/static/imgs/bgbzicon.png
  48. BIN
      src/static/imgs/bxh.png
  49. BIN
      src/static/imgs/ctsc.png
  50. BIN
      src/static/imgs/fx.png
  51. BIN
      src/static/imgs/fxh.png
  52. BIN
      src/static/imgs/gd.png
  53. BIN
      src/static/imgs/jx.png
  54. BIN
      src/static/imgs/llzj.png
  55. BIN
      src/static/imgs/lxkf.png
  56. BIN
      src/static/imgs/mockicon.png
  57. BIN
      src/static/imgs/order-icon.png
  58. BIN
      src/static/imgs/sc.png
  59. BIN
      src/static/imgs/sequentialicon.png
  60. BIN
      src/static/imgs/share.png
  61. BIN
      src/static/imgs/shmr.png
  62. BIN
      src/static/imgs/sjcs.png
  63. BIN
      src/static/imgs/sq.png
  64. BIN
      src/static/imgs/sz.png
  65. BIN
      src/static/imgs/true.png
  66. BIN
      src/static/imgs/wd.png
  67. BIN
      src/static/imgs/wdh.png
  68. BIN
      src/static/imgs/wdsc.png
  69. BIN
      src/static/imgs/wdsz.png
  70. BIN
      src/static/imgs/wxtb.png
  71. BIN
      src/static/imgs/wyjs.png
  72. BIN
      src/static/imgs/yjfk.png
  73. BIN
      src/static/imgs/ysc.png
  74. BIN
      src/static/imgs/zf.png
  75. BIN
      src/static/imgs/zt.png
  76. BIN
      src/static/imgs/zth.png
  77. BIN
      src/static/imgs/zz.png
  78. 10 0
      src/store/getters.js
  79. 17 0
      src/store/index.js
  80. 30 0
      src/store/modules/cinema.js
  81. 101 0
      src/store/modules/user.js
  82. 40 0
      src/utils/auth.js
  83. 7 0
      src/utils/errorCode.js
  84. 97 0
      src/utils/request - 副本.js
  85. 76 0
      src/utils/request.js
  86. 203 0
      src/utils/utils.js
  87. 1 0
      src/wxcomponents/vant/action-sheet/index.d.ts
  88. 62 0
      src/wxcomponents/vant/action-sheet/index.js
  89. 8 0
      src/wxcomponents/vant/action-sheet/index.json
  90. 69 0
      src/wxcomponents/vant/action-sheet/index.wxml
  91. 0 0
      src/wxcomponents/vant/action-sheet/index.wxss
  92. 1 0
      src/wxcomponents/vant/area/index.d.ts
  93. 232 0
      src/wxcomponents/vant/area/index.js
  94. 6 0
      src/wxcomponents/vant/area/index.json
  95. 20 0
      src/wxcomponents/vant/area/index.wxml
  96. 8 0
      src/wxcomponents/vant/area/index.wxs
  97. 1 0
      src/wxcomponents/vant/area/index.wxss
  98. 1 0
      src/wxcomponents/vant/button/index.d.ts
  99. 58 0
      src/wxcomponents/vant/button/index.js
  100. 7 0
      src/wxcomponents/vant/button/index.json

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 660 - 24
package-lock.json


+ 8 - 2
package.json

@@ -14,7 +14,7 @@
     "build:mp-kuaishou": "cross-env NODE_ENV=production UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build",
     "build:mp-qq": "cross-env NODE_ENV=production UNI_PLATFORM=mp-qq vue-cli-service uni-build",
     "build:mp-toutiao": "cross-env NODE_ENV=production UNI_PLATFORM=mp-toutiao vue-cli-service uni-build",
-    "build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build",
+    "build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch",
     "build:quickapp-native": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-native vue-cli-service uni-build",
     "build:quickapp-webview": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview vue-cli-service uni-build",
     "build:quickapp-webview-huawei": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build",
@@ -60,9 +60,15 @@
     "@vue/shared": "^3.0.0",
     "core-js": "^3.6.5",
     "flyio": "^0.6.2",
+    "node-sass": "^4.14.1",
     "regenerator-runtime": "^0.12.1",
+    "sass-loader": "^7.3.1",
     "vue": "^2.6.11",
-    "vuex": "^3.2.0"
+    "vuex": "^3.2.0",
+    "axios-miniprogram-adapter": "^0.3.2",
+    "crypto-js": "^4.0.0",
+    "qrcode": "^1.4.4",
+    "qs": "^6.10.1"
   },
   "devDependencies": {
     "@babel/runtime": "~7.12.0",

+ 13 - 12
src/App.vue

@@ -1,17 +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>
+	@import "/wxcomponents/vant/common/index.wxss";
+	@import "@/common/css/animation.css";
 	/*每个页面公共css */
-</style>
+	*{
+		margin: 0;
+		padding: 0;
+	}
+	html{
+		background-color: #F1F1F1;
+	}
+	.night{
+		filter: invert(1) hue-rotate(180deg);
+	}
+</style>

+ 14 - 0
src/api/amap.js

@@ -0,0 +1,14 @@
+import request from '@/utils/request'
+
+
+// 高德逆地理编码
+export function amapRegeo(params) {
+	return request({
+		url: 'https://restapi.amap.com/v3/geocode/regeo',
+		method: 'get',
+		params:{
+			key:'6476cd358ddea091d7da6d59e92b0659',
+			...params
+		}
+	})
+}

+ 55 - 0
src/api/answer.js

@@ -0,0 +1,55 @@
+import request from '@/utils/request'
+
+import {
+	baseUrl
+}
+from '@/api/baseurl'
+
+
+// 获取题库列表
+export function questionList(params) {
+	return request({
+		url: baseUrl + '/student/question/info/getQuestionInfoList',
+		method: 'get',
+		params
+	})
+}
+
+// 获取随机题库列表
+export function questionListRandom(params) {
+	return request({
+		url: baseUrl + '/student/question/info/getRandQuestionInfoList',
+		method: 'get',
+		params
+	})
+}
+
+// 获取错题收藏列表
+export function questionErrorList(params) {
+	return request({
+		url: baseUrl + '/student/question/error/getQuestionErrorList',
+		method: 'get',
+		params
+	})
+}
+
+// 新增错题
+export function questionErrorAdd(id) {
+	return request({
+		url: baseUrl + '/student/question/error',
+		method: 'post',
+		data: {
+			questionId: id
+		}
+	})
+}
+
+// 删除错题
+export function questionErrorDel(ids) {
+	return request({
+		url: baseUrl + `/student/question/error/${ids}`,
+		method: 'delete'
+	})
+}
+
+

+ 84 - 0
src/api/applist.js

@@ -0,0 +1,84 @@
+import request from '@/utils/request'
+
+import {
+	baseUrl
+}
+from '@/api/baseurl'
+
+
+
+// 按品类查询商家列表
+export function getTypeAppList(id,params) {
+	return request({
+		url: baseUrl + `/student/applet/customer/getCustomerListByProductId/${id}`,
+		method: 'get',
+		params
+	})
+}
+
+// 查询用户收藏列表
+export function collectionList(params) {
+	return request({
+		url: baseUrl + '/student/applet/collection/collectionList',
+		method: 'get',
+		params
+	})
+}
+
+// 查询用户浏览记录
+export function getBrowseRecordInfoList(params) {
+	return request({
+		url: baseUrl + '/student/applet/record/info/getBrowseRecordInfoList',
+		method: 'get',
+		params
+	})
+}
+
+// 查询品类列表
+export function typeList(params) {
+	return request({
+		url: baseUrl + '/student/applet/product/info/list',
+		method: 'get',
+		params
+	})
+}
+
+// 收藏商家
+export function addFavorites(id) {
+	return request({
+		url: baseUrl + `/student/applet/collection/collectionBusiness/${id}`,
+		method: 'put'
+	})
+}
+
+// 删除收藏
+export function delFavorites(ids) {
+	return request({
+		url: baseUrl + `/student/applet/collection/cancelCollection/${ids}`,
+		method: 'delete'
+	})
+}
+
+// 新增记录
+export function BrowseRecordAdd(id) {
+	return request({
+		url: baseUrl + `/student/applet/record/info/insertBrowseRecord/${id}`,
+		method: 'put'
+	})
+}
+
+// 删除记录
+export function BrowseRecordDel(ids) {
+	return request({
+		url: baseUrl + `/student/applet/record/info/${ids}`,
+		method: 'delete'
+	})
+}
+
+// 查询轮播图列表
+export function getCarouselChartList() {
+	return request({
+		url: baseUrl + `/student/applet/homePage/getHomePageDataList`,
+		method: 'get'
+	})
+}

+ 3 - 0
src/api/baseurl.js

@@ -0,0 +1,3 @@
+// export const baseUrl = 'http://192.168.8.219:8080';
+// export const baseUrl = 'http://192.168.8.213:8080/zzjs-admin';
+export const baseUrl = 'https://zzjs.zzxcx.net/prod-api';

+ 146 - 0
src/api/cinema.js

@@ -0,0 +1,146 @@
+import request from '@/utils/request'
+import qs from 'qs'
+
+import {
+	baseUrl
+}
+from '@/api/baseurl'
+let fileCommonApiUrl='/student/film/order/fileCommonApi'
+
+
+// 获取电影后台折扣参数
+export function getFilmDiscount(data) {
+	return request({
+		url: baseUrl + `/system/config/configKey/film_discount`,
+	})
+}
+
+// 获取即将上映电影列表
+export function getSoonList(data) {
+	return request({
+		url: baseUrl + fileCommonApiUrl,
+		method: 'post',
+		data: {
+			url: 'movieapi/movie-info/get-soon-list',
+			paramData: qs.stringify(data)
+		}
+	})
+}
+
+// 包含某电影的日期
+export function getShowDate(data) {
+	return request({
+		url: baseUrl + fileCommonApiUrl,
+		method: 'post',
+		data: {
+			url: 'movieapi/movie-info/get-show-date',
+			paramData: qs.stringify(data)
+		}
+	})
+}
+
+// 获取正在热映电影列表
+export function getHotList(data) {
+	return request({
+		url: baseUrl + fileCommonApiUrl,
+		method: 'post',
+		data: {
+			url: 'movieapi/movie-info/get-hot-list',
+			paramData: qs.stringify(data)
+		}
+	})
+}
+
+// 获取城市列表
+export function getCityList(data) {
+	return request({
+		url: baseUrl + fileCommonApiUrl,
+		method: 'post',
+		data: {
+			url: 'movieapi/movie-info/get-city-list',
+			paramData: qs.stringify(data)
+		}
+	})
+}
+
+// 获取影院列表
+export function getCinemaList(data) {
+	return request({
+		url: baseUrl + fileCommonApiUrl,
+		method: 'post',
+		data: {
+			url: 'movieapi/movie-info/get-cinema-list',
+			paramData: qs.stringify(data)
+		}
+	})
+}
+
+// 包含某电影的影院
+export function getShowList(data) {
+	return request({
+		url: baseUrl + fileCommonApiUrl,
+		method: 'post',
+		data: {
+			url: 'movieapi/movie-info/get-show-list',
+			paramData: qs.stringify(data)
+		}
+	})
+}
+
+// 某电影在某影院的场次排期
+export function getScheduleList(data) {
+	return request({
+		url: baseUrl + fileCommonApiUrl,
+		method: 'post',
+		data: {
+			url: 'movieapi/movie-info/get-schedule-list',
+			paramData: qs.stringify(data)
+		}
+	})
+}
+
+// 查账户余额
+export function getInfo(data) {
+	return request({
+		url: baseUrl + fileCommonApiUrl,
+		method: 'post',
+		data: {
+			url: 'api/user/info',
+			paramData: qs.stringify(data)
+		}
+	})
+}
+
+// 根据订单查影票信息
+export function orderQuery(data) {
+	return request({
+		url: baseUrl + fileCommonApiUrl,
+		method: 'post',
+		data: {
+			url: 'api/order/query',
+			paramData: qs.stringify(data)
+		}
+	})
+}
+
+// 某次电影的座位
+export function getSeat(data) {
+	return request({
+		url: baseUrl + fileCommonApiUrl,
+		method: 'post',
+		data: {
+			url: 'movieapi/movie-info/get-seat',
+			paramData: qs.stringify(data)
+		}
+	})
+}
+
+// 获取电影票支付数据
+export function prepareOrder(data) {
+	return request({
+		url: baseUrl + '/student/wx/prepareOrder',
+		method: 'post',
+		data
+	})
+}
+

+ 95 - 0
src/api/home.js

@@ -0,0 +1,95 @@
+import request from '@/utils/request'
+import md5 from 'crypto-js/md5'
+
+import {
+	baseUrl
+}
+from '@/api/baseurl'
+
+
+// 查询轮播图列表
+export function getHomePageDataList() {
+	return request({
+		url: baseUrl + `/student/applet/homePage/getHomePageDataList`,
+		method: 'get'
+	})
+}
+
+//拼多多密钥计算
+function calcAuth(params, client_secret) {
+
+	var arr = []
+	Object.keys(params).sort().forEach((key) => {
+		arr.push(key + params[key])
+	})
+	arr[0] = client_secret + arr[0]
+	arr[arr.length - 1] += client_secret
+
+
+	return md5(arr.join('')).toString().toUpperCase()
+}
+
+// 查询轮播图列表
+export function getPDD(data) {
+
+	let params = {
+		type: 'pdd.ddk.goods.recommend.get',
+		data_type: 'JSON',
+		client_id: 'fa0f2debb5b0444b9719919d7b8204d1',
+		pid: '1642187_203462988',
+		timestamp: new Date().getTime().toString().slice(0, 10),
+		...data
+	}
+
+	let client_secret = '3305b6bb37252495848e67fd991134651e308495'
+
+	params.sign = calcAuth(params, client_secret)
+
+	return request({
+		url: `https://gw-api.pinduoduo.com/api/router`,
+		method: 'get',
+		params
+	})
+}
+
+
+
+// 查询小程序跳转参数
+export async function getPddWxData(data) {
+
+	let params = {
+		type: 'pdd.ddk.goods.promotion.url.generate',
+		data_type: 'JSON',
+		client_id: 'fa0f2debb5b0444b9719919d7b8204d1',
+		p_id: '1642187_203462988',
+		timestamp: new Date().getTime().toString().slice(0, 10),
+		generate_we_app: true
+	}
+
+	let getPDDRes = await getPDD(data)
+	params.search_id = getPDDRes.goods_basic_detail_response.list[0].search_id
+	let arr = '['
+	getPDDRes.goods_basic_detail_response.list.map((val) => {
+		arr += '"' + val.goods_sign + '",'
+	})
+	params.goods_sign_list = arr.slice(0, -1) + ']'
+
+
+	let client_secret = '3305b6bb37252495848e67fd991134651e308495'
+
+	params.sign = calcAuth(params, client_secret)
+
+	let wxDataRes = await request({
+		url: `https://gw-api.pinduoduo.com/api/router`,
+		method: 'get',
+		params
+	})
+
+	let dataArr = []
+
+	getPDDRes.goods_basic_detail_response.list.map((val, index) => {
+		val.we_app_info = wxDataRes.goods_promotion_url_generate_response.goods_promotion_url_list[index].we_app_info
+	})
+	
+	return getPDDRes.goods_basic_detail_response.list
+}

+ 60 - 0
src/api/login.js

@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+import {
+	baseUrl
+}
+from '@/api/baseurl'
+
+//微信小程序登录code
+async function wxlogin() {
+	let wxLogin = await wx.login()
+	return wxLogin
+}
+
+// 登录方法
+export async function login() {
+	let res = await wxlogin();
+	return request({
+		url: baseUrl + '/login/jscode',
+		method: 'post',
+		params: {
+			jscode: res.code
+		},
+		headers: {
+			isLogin: true
+		}
+	})
+}
+
+// 更新用户信息
+export function updateUserInfo(data) {
+	return request({
+		url: baseUrl + '/student/user/info',
+		method: 'post',
+		data
+	})
+}
+
+// 获取用户详细信息
+export async function getInfo() {
+	return request({
+		url: baseUrl + '/getInfo',
+		method: 'get'
+	})
+}
+
+// 退出方法
+export function logout() {
+	return request({
+		url: '/logout',
+		method: 'post'
+	})
+}
+
+// 绑定用户身份证信息
+export function bindUserCard(data) {
+	return request({
+		url: baseUrl + '/gzpt/userInfo/bind',
+		method: 'put',
+		data
+	})
+}

+ 20 - 0
src/api/order.js

@@ -0,0 +1,20 @@
+import request from '@/utils/request'
+import {
+	baseUrl
+}
+from '@/api/baseurl'
+
+// 查询订单列表
+export function getOrderList(params) {
+	return request({
+		url: baseUrl + '/student/wx/order/list',
+		params
+	})
+}
+
+// 查询订单列表
+export function getWxOrder(path) {
+	return request({
+		url: baseUrl + `/student/wx/${path}`,
+	})
+}

+ 18 - 0
src/api/pay.js

@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+import {
+	baseUrl
+}
+from '@/api/baseurl'
+
+
+
+
+// 微信支付获取订单接口
+export function getPayData(data) {
+	return request({
+		url: baseUrl + '/student/wx/prepareOrder',
+		method: 'post',
+		data
+	})
+}

+ 42 - 0
src/api/studytime.js

@@ -0,0 +1,42 @@
+import request from '@/utils/request'
+
+import {
+	baseUrl
+}
+from '@/api/baseurl'
+
+
+
+
+// 上传学时
+export function addTime(data) {
+	return request({
+		url: baseUrl + '/student/gzpt-train-record',
+		method: 'post',
+		data
+	})
+}
+
+// 查询已完成科目学时
+export function getTime(data) {
+	return request({
+		url: baseUrl + '/student/gzpt-train-record/getTrainFinishKm',
+		method: 'get'
+	})
+}
+
+// 查询额定学时
+export function getTimeInfo(data) {
+	return request({
+		url: baseUrl + '/student/gzpt-train-record/getTrainSubjectCredit',
+		method: 'get'
+	})
+}
+
+// 获取学员详细信息
+export function getStudentInfo(data) {
+	return request({
+		url: baseUrl + '/student/gzpt/userInfo',
+		method: 'get'
+	})
+}

+ 177 - 0
src/common/css/animation.css

@@ -0,0 +1,177 @@
+/* 
+  Animation 微动画  
+ */
+
+
+/* Animation css */
+[class*=animation-] {
+    animation-duration: .1s;
+    animation-timing-function: ease-out;
+    animation-fill-mode: both
+}
+
+.animation-fade {
+    animation-name: fade;
+    animation-duration: .1s;
+    animation-timing-function: linear
+}
+
+.animation-scale-up {
+    animation-name: scale-up
+}
+
+.animation-scale-down {
+    animation-name: scale-down
+}
+
+.animation-slide-top {
+	animation-duration: .3s;
+    animation-name: slide-top
+}
+
+.animation-slide-bottom {
+	animation-duration: .3s;
+    animation-name: slide-bottom
+}
+
+.animation-slide-left {
+    animation-name: slide-left
+}
+
+.animation-slide-right {
+    animation-name: slide-right
+}
+
+.animation-shake {
+    animation-name: shake
+}
+
+.animation-reverse {
+    animation-direction: reverse
+}
+
+@keyframes fade {
+    0% {
+        opacity: 0
+    }
+
+    100% {
+        opacity: 1
+    }
+}
+
+@keyframes scale-up {
+    0% {
+        opacity: 0;
+        transform: scale(.2)
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1)
+    }
+}
+
+@keyframes scale-down {
+    0% {
+        opacity: 0;
+        transform: scale(1.8)
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1)
+    }
+}
+
+@keyframes slide-top {
+    0% {
+        /* opacity: 0; */
+        transform: translateY(-100%)
+    }
+
+    100% {
+        /* opacity: 1; */
+        transform: translateY(0)
+    }
+}
+
+@keyframes slide-bottom {
+    0% {
+        opacity: 0;
+        transform: translateY(100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateY(0)
+    }
+}
+
+@keyframes shake {
+
+    0%,
+    100% {
+        transform: translateX(0)
+    }
+
+    10% {
+        transform: translateX(-9px)
+    }
+
+    20% {
+        transform: translateX(8px)
+    }
+
+    30% {
+        transform: translateX(-7px)
+    }
+
+    40% {
+        transform: translateX(6px)
+    }
+
+    50% {
+        transform: translateX(-5px)
+    }
+
+    60% {
+        transform: translateX(4px)
+    }
+
+    70% {
+        transform: translateX(-3px)
+    }
+
+    80% {
+        transform: translateX(2px)
+    }
+
+    90% {
+        transform: translateX(-1px)
+    }
+}
+
+@keyframes slide-left {
+    0% {
+        opacity: 0;
+        transform: translateX(-100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateX(0)
+    }
+}
+
+@keyframes slide-right {
+    0% {
+        opacity: 0;
+        transform: translateX(100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateX(0)
+    }
+}

+ 36 - 0
src/components/appdes/index.vue

@@ -0,0 +1,36 @@
+<template>
+	<view class='list-item' v-for="(item, index) in applist" :key="index" @click="goMiniApp(item)">
+		<image class="avatar" mode="aspectFill" :src="item.appletLogoFileUrl || '/static/imgs/shmr.png'">
+			<view class="item-right">
+				<view class="top">
+					<van-icon class="icon" v-if="item.collectionStatus=='0'" @tap.stop='addSc(index)' name="/static/imgs/sc.png"
+					 size='15' />
+					<van-icon class="icon" v-else name="/static/imgs/ysc.png" @tap.stop='delSc(index)' size='15' />
+					<van-icon class="icon" name="/static/imgs/zf.png" @tap.stop='showShare=true' size='14' />
+					<!-- <van-icon class="icon" name="/static/imgs/bxh.png" size='12' /> -->
+				</view>
+				<view class="center">
+					<text class="details">{{item.appletIntroduce || '该商家暂无描述,敬请期待!'}}</text>
+				</view>
+				<view class="bottom">
+					<text class="title">{{item.corporateName}}</text>
+					<view class="right" v-if="false">
+						<view class="flex">
+							<van-icon class="icon" name="/static/imgs/评论.png" size='15' />
+							<text>54</text>
+						</view>
+						<view class="flex">
+							<van-icon class="icon" name="/static/imgs/好评率.png" size='15' />
+							<text>99%</text>
+						</view>
+					</view>
+				</view>
+			</view>
+	</view>
+</template>
+
+<script>
+</script>
+
+<style>
+</style>

+ 3 - 0
src/components/loading/loading.js

@@ -0,0 +1,3 @@
+import Vue from 'vue'
+import loading from './loading.vue'
+Vue.component('loading', loading)

+ 177 - 0
src/components/loading/loading.vue

@@ -0,0 +1,177 @@
+<template>
+	<view>
+		<div class="spinner">
+			<div class="spinner-container container1">
+				<div class="circle1"></div>
+				<div class="circle2"></div>
+				<div class="circle3"></div>
+				<div class="circle4"></div>
+			</div>
+			<div class="spinner-container container2">
+				<div class="circle1"></div>
+				<div class="circle2"></div>
+				<div class="circle3"></div>
+				<div class="circle4"></div>
+			</div>
+			<div class="spinner-container container3">
+				<div class="circle1"></div>
+				<div class="circle2"></div>
+				<div class="circle3"></div>
+				<div class="circle4"></div>
+			</div>
+		</div>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+
+			};
+		}
+	}
+</script>
+
+<style lang="scss">
+	.spinner {
+		margin: 100px auto;
+		width: 20px;
+		height: 20px;
+		position: relative;
+	}
+
+	.container1>div,
+	.container2>div,
+	.container3>div {
+		width: 6px;
+		height: 6px;
+		background-color: #333;
+
+		border-radius: 100%;
+		position: absolute;
+		-webkit-animation: bouncedelay 1.2s infinite ease-in-out;
+		animation: bouncedelay 1.2s infinite ease-in-out;
+		-webkit-animation-fill-mode: both;
+		animation-fill-mode: both;
+	}
+
+	.spinner .spinner-container {
+		position: absolute;
+		width: 100%;
+		height: 100%;
+	}
+
+	.container2 {
+		-webkit-transform: rotateZ(45deg);
+		transform: rotateZ(45deg);
+	}
+
+	.container3 {
+		-webkit-transform: rotateZ(90deg);
+		transform: rotateZ(90deg);
+	}
+
+	.circle1 {
+		top: 0;
+		left: 0;
+	}
+
+	.circle2 {
+		top: 0;
+		right: 0;
+	}
+
+	.circle3 {
+		right: 0;
+		bottom: 0;
+	}
+
+	.circle4 {
+		left: 0;
+		bottom: 0;
+	}
+
+	.container2 .circle1 {
+		-webkit-animation-delay: -1.1s;
+		animation-delay: -1.1s;
+	}
+
+	.container3 .circle1 {
+		-webkit-animation-delay: -1.0s;
+		animation-delay: -1.0s;
+	}
+
+	.container1 .circle2 {
+		-webkit-animation-delay: -0.9s;
+		animation-delay: -0.9s;
+	}
+
+	.container2 .circle2 {
+		-webkit-animation-delay: -0.8s;
+		animation-delay: -0.8s;
+	}
+
+	.container3 .circle2 {
+		-webkit-animation-delay: -0.7s;
+		animation-delay: -0.7s;
+	}
+
+	.container1 .circle3 {
+		-webkit-animation-delay: -0.6s;
+		animation-delay: -0.6s;
+	}
+
+	.container2 .circle3 {
+		-webkit-animation-delay: -0.5s;
+		animation-delay: -0.5s;
+	}
+
+	.container3 .circle3 {
+		-webkit-animation-delay: -0.4s;
+		animation-delay: -0.4s;
+	}
+
+	.container1 .circle4 {
+		-webkit-animation-delay: -0.3s;
+		animation-delay: -0.3s;
+	}
+
+	.container2 .circle4 {
+		-webkit-animation-delay: -0.2s;
+		animation-delay: -0.2s;
+	}
+
+	.container3 .circle4 {
+		-webkit-animation-delay: -0.1s;
+		animation-delay: -0.1s;
+	}
+
+	@-webkit-keyframes bouncedelay {
+
+		0%,
+		80%,
+		100% {
+			-webkit-transform: scale(0.0)
+		}
+
+		40% {
+			-webkit-transform: scale(1.0)
+		}
+	}
+
+	@keyframes bouncedelay {
+
+		0%,
+		80%,
+		100% {
+			transform: scale(0.0);
+			-webkit-transform: scale(0.0);
+		}
+
+		40% {
+			transform: scale(1.0);
+			-webkit-transform: scale(1.0);
+		}
+	}
+</style>

+ 193 - 0
src/components/skeleton/index/index.vue

@@ -0,0 +1,193 @@
+<template>
+	<view class="skeleton-box">
+		<view v-if="loading" class="skeleton">
+			<div class="spinner">
+			  <div class="spinner-container container1">
+			    <div class="circle1"></div>
+			    <div class="circle2"></div>
+			    <div class="circle3"></div>
+			    <div class="circle4"></div>
+			  </div>
+			  <div class="spinner-container container2">
+			    <div class="circle1"></div>
+			    <div class="circle2"></div>
+			    <div class="circle3"></div>
+			    <div class="circle4"></div>
+			  </div>
+			  <div class="spinner-container container3">
+			    <div class="circle1"></div>
+			    <div class="circle2"></div>
+			    <div class="circle3"></div>
+			    <div class="circle4"></div>
+			  </div>
+			</div>
+		</view>
+		<van-transition v-else-if="list.length==0" name="fade-up">
+			<view class="skeleton">
+				暂时没有数据
+			</view>
+		</van-transition>
+		<van-transition v-else name="fade-up">
+			<slot></slot>
+		</van-transition>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			loading: {
+				type: Boolean,
+				default: true
+			},
+			list: {
+				type: Array,
+				default: [1]
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.skeleton {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		padding: 50rpx;
+		height: 320rpx;
+	}
+
+	.led {
+		width: 10rpx;
+		height: 30rpx;
+		border-radius: 10rpx;
+		top: 50rpx;
+		position: absolute;
+		transform: rotate(calc(360deg / var(--all) * var(--i)));
+		transform-origin: 0 160rpx;
+		animation: loading 2s linear infinite;
+		animation-delay: calc(var(--i) * 1s);
+		background-color: green;
+	}
+
+	@keyframes loading {
+		from {
+			opacity: 0;
+		}
+
+		to {
+			opacity: 1;
+		}
+	}
+	
+	.spinner {
+	  margin: 100px auto;
+	  width: 20px;
+	  height: 20px;
+	  position: relative;
+	}
+	 
+	.container1 > div, .container2 > div, .container3 > div {
+	  width: 6px;
+	  height: 6px;
+	  background-color: #333;
+	 
+	  border-radius: 100%;
+	  position: absolute;
+	  -webkit-animation: bouncedelay 1.2s infinite ease-in-out;
+	  animation: bouncedelay 1.2s infinite ease-in-out;
+	  -webkit-animation-fill-mode: both;
+	  animation-fill-mode: both;
+	}
+	 
+	.spinner .spinner-container {
+	  position: absolute;
+	  width: 100%;
+	  height: 100%;
+	}
+	 
+	.container2 {
+	  -webkit-transform: rotateZ(45deg);
+	  transform: rotateZ(45deg);
+	}
+	 
+	.container3 {
+	  -webkit-transform: rotateZ(90deg);
+	  transform: rotateZ(90deg);
+	}
+	 
+	.circle1 { top: 0; left: 0; }
+	.circle2 { top: 0; right: 0; }
+	.circle3 { right: 0; bottom: 0; }
+	.circle4 { left: 0; bottom: 0; }
+	 
+	.container2 .circle1 {
+	  -webkit-animation-delay: -1.1s;
+	  animation-delay: -1.1s;
+	}
+	 
+	.container3 .circle1 {
+	  -webkit-animation-delay: -1.0s;
+	  animation-delay: -1.0s;
+	}
+	 
+	.container1 .circle2 {
+	  -webkit-animation-delay: -0.9s;
+	  animation-delay: -0.9s;
+	}
+	 
+	.container2 .circle2 {
+	  -webkit-animation-delay: -0.8s;
+	  animation-delay: -0.8s;
+	}
+	 
+	.container3 .circle2 {
+	  -webkit-animation-delay: -0.7s;
+	  animation-delay: -0.7s;
+	}
+	 
+	.container1 .circle3 {
+	  -webkit-animation-delay: -0.6s;
+	  animation-delay: -0.6s;
+	}
+	 
+	.container2 .circle3 {
+	  -webkit-animation-delay: -0.5s;
+	  animation-delay: -0.5s;
+	}
+	 
+	.container3 .circle3 {
+	  -webkit-animation-delay: -0.4s;
+	  animation-delay: -0.4s;
+	}
+	 
+	.container1 .circle4 {
+	  -webkit-animation-delay: -0.3s;
+	  animation-delay: -0.3s;
+	}
+	 
+	.container2 .circle4 {
+	  -webkit-animation-delay: -0.2s;
+	  animation-delay: -0.2s;
+	}
+	 
+	.container3 .circle4 {
+	  -webkit-animation-delay: -0.1s;
+	  animation-delay: -0.1s;
+	}
+	 
+	@-webkit-keyframes bouncedelay {
+	  0%, 80%, 100% { -webkit-transform: scale(0.0) }
+	  40% { -webkit-transform: scale(1.0) }
+	}
+	 
+	@keyframes bouncedelay {
+	  0%, 80%, 100% {
+	    transform: scale(0.0);
+	    -webkit-transform: scale(0.0);
+	  } 40% {
+	    transform: scale(1.0);
+	    -webkit-transform: scale(1.0);
+	  }
+	}
+</style>

+ 188 - 0
src/components/skeleton/login.vue

@@ -0,0 +1,188 @@
+<template>
+	<view class="skeleton-box">
+		<view v-if="loading" class="skeleton">
+			<div class="spinner">
+			  <div class="spinner-container container1">
+			    <div class="circle1"></div>
+			    <div class="circle2"></div>
+			    <div class="circle3"></div>
+			    <div class="circle4"></div>
+			  </div>
+			  <div class="spinner-container container2">
+			    <div class="circle1"></div>
+			    <div class="circle2"></div>
+			    <div class="circle3"></div>
+			    <div class="circle4"></div>
+			  </div>
+			  <div class="spinner-container container3">
+			    <div class="circle1"></div>
+			    <div class="circle2"></div>
+			    <div class="circle3"></div>
+			    <div class="circle4"></div>
+			  </div>
+			</div>
+		</view>
+		<van-transition v-else name="fade-up">
+			<slot></slot>
+		</van-transition>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			loading: {
+				type: Boolean,
+				default: true
+			},
+			list: {
+				type: Array,
+				default: [1]
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.skeleton {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		padding: 50rpx;
+		height: 320rpx;
+	}
+
+	.led {
+		width: 10rpx;
+		height: 30rpx;
+		border-radius: 10rpx;
+		top: 50rpx;
+		position: absolute;
+		transform: rotate(calc(360deg / var(--all) * var(--i)));
+		transform-origin: 0 160rpx;
+		animation: loading 2s linear infinite;
+		animation-delay: calc(var(--i) * 1s);
+		background-color: green;
+	}
+
+	@keyframes loading {
+		from {
+			opacity: 0;
+		}
+
+		to {
+			opacity: 1;
+		}
+	}
+
+	.spinner {
+	  margin: 100px auto;
+	  width: 20px;
+	  height: 20px;
+	  position: relative;
+	}
+	 
+	.container1 > div, .container2 > div, .container3 > div {
+	  width: 6px;
+	  height: 6px;
+	  background-color: #333;
+	 
+	  border-radius: 100%;
+	  position: absolute;
+	  -webkit-animation: bouncedelay 1.2s infinite ease-in-out;
+	  animation: bouncedelay 1.2s infinite ease-in-out;
+	  -webkit-animation-fill-mode: both;
+	  animation-fill-mode: both;
+	}
+	 
+	.spinner .spinner-container {
+	  position: absolute;
+	  width: 100%;
+	  height: 100%;
+	}
+	 
+	.container2 {
+	  -webkit-transform: rotateZ(45deg);
+	  transform: rotateZ(45deg);
+	}
+	 
+	.container3 {
+	  -webkit-transform: rotateZ(90deg);
+	  transform: rotateZ(90deg);
+	}
+	 
+	.circle1 { top: 0; left: 0; }
+	.circle2 { top: 0; right: 0; }
+	.circle3 { right: 0; bottom: 0; }
+	.circle4 { left: 0; bottom: 0; }
+	 
+	.container2 .circle1 {
+	  -webkit-animation-delay: -1.1s;
+	  animation-delay: -1.1s;
+	}
+	 
+	.container3 .circle1 {
+	  -webkit-animation-delay: -1.0s;
+	  animation-delay: -1.0s;
+	}
+	 
+	.container1 .circle2 {
+	  -webkit-animation-delay: -0.9s;
+	  animation-delay: -0.9s;
+	}
+	 
+	.container2 .circle2 {
+	  -webkit-animation-delay: -0.8s;
+	  animation-delay: -0.8s;
+	}
+	 
+	.container3 .circle2 {
+	  -webkit-animation-delay: -0.7s;
+	  animation-delay: -0.7s;
+	}
+	 
+	.container1 .circle3 {
+	  -webkit-animation-delay: -0.6s;
+	  animation-delay: -0.6s;
+	}
+	 
+	.container2 .circle3 {
+	  -webkit-animation-delay: -0.5s;
+	  animation-delay: -0.5s;
+	}
+	 
+	.container3 .circle3 {
+	  -webkit-animation-delay: -0.4s;
+	  animation-delay: -0.4s;
+	}
+	 
+	.container1 .circle4 {
+	  -webkit-animation-delay: -0.3s;
+	  animation-delay: -0.3s;
+	}
+	 
+	.container2 .circle4 {
+	  -webkit-animation-delay: -0.2s;
+	  animation-delay: -0.2s;
+	}
+	 
+	.container3 .circle4 {
+	  -webkit-animation-delay: -0.1s;
+	  animation-delay: -0.1s;
+	}
+	 
+	@-webkit-keyframes bouncedelay {
+	  0%, 80%, 100% { -webkit-transform: scale(0.0) }
+	  40% { -webkit-transform: scale(1.0) }
+	}
+	 
+	@keyframes bouncedelay {
+	  0%, 80%, 100% {
+	    transform: scale(0.0);
+	    -webkit-transform: scale(0.0);
+	  } 40% {
+	    transform: scale(1.0);
+	    -webkit-transform: scale(1.0);
+	  }
+	}
+</style>

+ 66 - 0
src/components/square/index.vue

@@ -0,0 +1,66 @@
+<template>
+	<view class="square">
+		<view class="content">
+			<view class="box">
+				<image :src="icon" mode="aspectFit" class="icon"></image>
+				<text class='font'>{{title}}</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			icon: String,
+			title: String
+		},
+		data() {
+			return {}
+		},
+		methods: {
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.square {
+		padding: 10rpx;
+	}
+
+	.content {
+		width: 100%;
+		padding-bottom: 100%;
+		background-color: #FFFFFF;
+		position: relative;
+		border-radius: 10rpx;
+		overflow: hidden;
+
+		.box {
+			position: absolute;
+			left: 50%;
+			top: 50%;
+			transform: translate(-50%, -50%);
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+			align-items: center;
+			width: 100%;
+			height: 100%;
+
+			.icon {
+				flex: 1;
+			}
+
+			.font {
+				font-size: 26rpx;
+				font-family: PingFang SC;
+				font-weight: 400;
+				color: #0F0404;
+				margin-top: -0.5em;
+				margin-bottom: 0.5em;
+			}
+		}
+	}
+</style>

+ 3 - 0
src/components/topbar/topbar.js

@@ -0,0 +1,3 @@
+import Vue from 'vue'
+import topbar from './topbar.vue'
+Vue.component('topbar', topbar)

+ 32 - 0
src/components/topbar/topbar.vue

@@ -0,0 +1,32 @@
+<template>
+	<view class="tabbar">
+		<view :style="{ 'margin-top': tophight.top + 'px', 'margin-bottom': '15rpx' }">
+			<view class="slot-box" :style="{'padding-left': `calc(100vw - ${tophight.right}px)`,'padding-right': `calc(100vw - ${tophight.left}px)`,height: tophight.height + 'px'}">
+				<slot></slot>
+			</view>
+		</view>
+	</view>
+
+</template>
+
+<script>
+	export default {
+		data: () => ({
+			tophight: uni.getMenuButtonBoundingClientRect(),
+		}),
+		mounted() {
+			console.log(uni.getMenuButtonBoundingClientRect())
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.tabbar {
+		display: flex;
+		background-color: #FFFFFF;
+		.slot-box{
+			display: flex;
+			align-items: center;
+		}
+	}
+</style>

+ 176 - 0
src/components/xuan-switch/xuan-switch.vue

@@ -0,0 +1,176 @@
+<template>
+  <view class="switch-container" :style="[{ background: bj_color}]">
+    <view class="switch_view">
+      <view 
+        class="switch-item" 
+        :class="{'checked_switch':isSwitch}"
+        :style="isSwitch?`color:${checked_color}`:''"
+        @click.prevent.stop="changeSwitch(true)"
+        :animation="animationData2"
+      >
+        {{switchList[0]}}
+      </view>
+      <view 
+        class="switch-item"
+        :class="{'checked_switch':!isSwitch}"
+        :style="!isSwitch?`color:${checked_color}`:''"
+        @click.prevent.stop="changeSwitch(false)"
+        :animation="animationData3"
+      >
+        {{switchList[1]}}
+      </view>
+    </view>
+    <view class="disabled" v-if="disabled"></view>
+    <view 
+      class="position_view" :animation="animationData1"
+      :style="[{ background: checked_bj_color}]"
+    ></view>
+  </view>
+</template>
+
+<script>
+export default {
+  props: {
+    switchList: {
+      type: Array,
+      default: ()=>{
+        return ["开","关"]
+      }
+    },
+    defaultSwitch:{
+      type:Boolean,
+      default:true
+    },
+    isShowModal:{//改变开关时,是否弹框提醒
+      type:Boolean,
+      default:false
+    },
+    disabled:{
+      type:Boolean,
+      default:false
+    },
+    bj_color:{
+      type:String,
+      default:'#fff',
+    },
+    checked_bj_color:{
+      type:String,
+      default:'#1989fa',
+    },
+    checked_color:{
+      type:String,
+      default:'#fff',
+    }
+  },
+  data () {
+    return {
+      isSwitch:true,
+      initAnimation:{},
+      animationData1: {},
+      animationData2: {},
+      animationData3: {}
+    }
+  },
+  created () {
+    this.initAnimation = uni.createAnimation({
+      duration: 500,
+      timingFunction: 'ease',
+    })
+    this.isSwitch = this.defaultSwitch
+    this.changeAnimation()
+  },
+  methods: {
+    changeSwitch(isSwitch) {
+      if(isSwitch == this.isSwitch || this.disabled){
+        return
+      }
+      if(this.isShowModal){
+        let index = isSwitch?0:1
+        let text =  this.switchList[index]
+        uni.showModal({
+          title: '提示',
+          content: `您确定要将其调整为${text}吗?`,
+          success: (res) => {
+            if(res.confirm){
+              this.isSwitch = isSwitch
+              this.changeAnimation()
+              this.callParentEvent(isSwitch)
+            }
+          }
+        })
+      }else{
+        this.isSwitch = isSwitch
+        this.changeAnimation()
+        this.callParentEvent(isSwitch)
+      }
+    },
+    changeAnimation(){
+      if(this.isSwitch){
+        this.animationData1 = this.initAnimation.left(0).width('60%').step().export()
+        this.animationData2 = this.initAnimation.width('60%').step().export()
+        this.animationData3 = this.initAnimation.width('40%').step().export()
+      }else{
+        this.animationData1 = this.initAnimation.left('40%').width('60%').step().export()
+        this.animationData2 = this.initAnimation.width('40%').step().export()
+        this.animationData3 = this.initAnimation.width('60%').step().export()
+      }
+    },
+    callParentEvent(){
+      this.$emit("change",this.isSwitch)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  .switch-container {
+    display: flex;
+    flex-direction: row;
+    width: 320upx;
+    height: 60upx;
+    border-radius: 30upx;
+    border: 1upx solid #E8E8E8;
+    position: relative;
+    .switch_view{
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      z-index: 1;
+      display: flex;
+	  
+      border-radius: 30upx;
+      .switch-item {
+        color: #666;
+        font-size: 26upx;
+        height: 100%;
+        width: 40%;
+        border-radius: 30upx;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+      }
+    }
+    .position_view{
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 60%;
+      height: 100%;
+      border-radius: 30upx;
+      background: $uni-color-primary;
+    }
+    .disabled{
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      z-index: 99;
+      background: #fff;
+      opacity: 0.6;
+      border-radius: 30upx;
+    }
+  }
+</style>

+ 31 - 3
src/main.js

@@ -1,11 +1,39 @@
 import Vue from 'vue'
 import App from './App'
+import store from './store'
+import Toast from '@/wxcomponents/vant/toast/toast';
+import loadSke from '@/components/skeleton/index/index.vue'
+Vue.component('loadSke', loadSke)
+import '@/components/topbar/topbar.js'
+import '@/components/loading/loading.js'
+import {
+	goMiniApp,
+	goPage,
+	clickJumpType,
+	wxPay,
+	getUTCDay,
+	getDay,
+	goPageGetData
+} from '@/utils/utils.js'
+import utils from '@/utils/utils.js'
 
 Vue.config.productionTip = false
-
 App.mpType = 'app'
 
+Vue.prototype.goMiniApp = goMiniApp
+Vue.prototype.goPage = goPage
+Vue.prototype.Toast = Toast
+Vue.prototype.clickJumpType = clickJumpType
+Vue.prototype.wxPay = wxPay
+Vue.prototype.getUTCDay = getUTCDay
+Vue.prototype.getDay = getDay
+Vue.prototype.goPageGetData = goPageGetData
+Vue.prototype.$utils = utils
+
+
+
 const app = new Vue({
-  ...App
+	store,
+	...App
 })
-app.$mount()
+app.$mount()

+ 76 - 73
src/manifest.json

@@ -1,75 +1,78 @@
 {
-	"name": "",
-	"appid": "",
-	"description": "",
-	"versionName": "1.0.0",
-	"versionCode": "100",
-	"transformPx": false,
-	"app-plus": { /* 5+App特有相关 */
-		"usingComponents": true,
-		"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.READ_CONTACTS\"/>",
-					"<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.WRITE_CONTACTS\"/>",
-					"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
-					"<uses-permission android:name=\"android.permission.CAMERA\"/>",
-					"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
-					"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
-					"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
-					"<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.CALL_PHONE\"/>",
-					"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
-					"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
-					"<uses-feature android:name=\"android.hardware.camera\"/>",
-					"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
-					"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
-				]
-			},
-			"ios": { /* ios打包配置 */
-
-			},
-			"sdkConfigs": { /* SDK配置 */
-
-			}
-		}
-	},
-	"quickapp": { /* 快应用特有相关 */
-
-	},
-	"mp-weixin": { /* 微信小程序特有相关 */
-		"appid": "",
-		"setting": {
-			"urlCheck": false
-		},
-		"usingComponents": true
-	},
-	"mp-alipay" : {
-        "usingComponents" : true
-    },
-    "mp-baidu" : {
-        "usingComponents" : true
-    },
-    "mp-toutiao" : {
-        "usingComponents" : true
-    },
-    "mp-qq" : {
-        "usingComponents" : true
+    "name" : "sqxp-uniapp",
+    "appid" : "__UNI__C992BC3",
+    "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" : "wx8f43db501343feab",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : true,
+            "minified" : true
+        },
+        "usingComponents" : true,
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "电影院位置定位请求"
+            }
+        }
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
     }
-}
+}

+ 123 - 4
src/pages.json

@@ -3,14 +3,133 @@
 		{
 			"path": "pages/index/index",
 			"style": {
-				"navigationBarTitleText": "uni-app"
+				"navigationBarTitleText": "极速生活圈",
+				"navigationStyle": "custom",
+				"disableScroll": true
 			}
+		},
+		{
+			"path": "pages/user/set",
+			"style": {
+				"navigationBarTitleText": "我的设置"
+			}
+		},
+		{
+			"path": "pages/user/collectionList",
+			"style": {
+				"navigationBarTitleText": "我的收藏"
+			}
+		},
+		{
+			"path": "pages/user/browserecord",
+			"style": {
+				"navigationBarTitleText": "浏览足迹"
+			}
+		},
+		{
+			"path": "pages/webview/webview",
+			"style": {
+				"navigationBarTitleText": "",
+				"enablePullDownRefresh": false
+			}
+
+		},
+		{
+			"path": "pages/cinema/cinemalist",
+			"style": {
+				"navigationBarTitleText": "影院列表",
+				"enablePullDownRefresh": false
+			}
+
+		},
+		{
+			"path": "pages/cinema/schedulelist",
+			"style": {
+				"navigationBarTitleText": "",
+				"enablePullDownRefresh": false,
+				"backgroundColor": "#ffffff"
+			}
+
+		}, {
+			"path": "pages/cinema/selectseat",
+			"style": {
+				"navigationBarTitleText": "",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/cinema/placeorder",
+			"style": {
+				"navigationBarTitleText": "付款",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/user/order",
+			"style": {
+				"navigationBarTitleText": "全部订单",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/cinema/orderdes",
+			"style": {
+				"navigationBarTitleText": "订单详情",
+				"enablePullDownRefresh": false
+			}
+
+		}, {
+			"path": "pages/cinema/citylist",
+			"style": {
+				"navigationBarTitleText": "选择城市",
+				"enablePullDownRefresh": false
+			}
+
 		}
 	],
 	"globalStyle": {
 		"navigationBarTextStyle": "black",
-		"navigationBarTitleText": "uni-app",
+		"navigationBarTitleText": "",
 		"navigationBarBackgroundColor": "#F8F8F8",
-		"backgroundColor": "#F8F8F8"
+		"backgroundColor": "#F8F8F8",
+		"usingComponents": {
+			"van-tabbar": "./wxcomponents/vant/tabbar/index",
+			"van-tabbar-item": "./wxcomponents/vant/tabbar-item/index",
+			"van-search": "./wxcomponents/vant/search/index",
+			"van-icon": "./wxcomponents/vant/icon/index",
+			"van-toast": "./wxcomponents/vant/toast/index",
+			"van-row": "./wxcomponents/vant/row/index",
+			"van-col": "./wxcomponents/vant/col/index",
+			"van-transition": "./wxcomponents/vant/transition/index",
+			"van-image": "./wxcomponents/vant/image/index",
+			"van-checkbox": "./wxcomponents/vant/checkbox/index",
+			"van-checkbox-group": "./wxcomponents/vant/checkbox-group/index",
+			"van-radio": "./wxcomponents/vant/radio/index",
+			"van-radio-group": "./wxcomponents/vant/radio-group/index",
+			"van-cell": "./wxcomponents/vant/cell/index",
+			"van-cell-group": "./wxcomponents/vant/cell-group/index",
+			"van-switch": "./wxcomponents/vant/switch/index",
+			"van-popup": "./wxcomponents/vant/popup/index",
+			"van-skeleton": "./wxcomponents/vant/skeleton/index",
+			"van-field": "./wxcomponents/vant/field/index",
+			"van-area": "./wxcomponents/vant/area/index",
+			"van-button": "./wxcomponents/vant/button/index",
+			"van-divider": "./wxcomponents/vant/divider/index",
+			"van-count-down": "./wxcomponents/vant/count-down/index",
+			"van-dialog": "./wxcomponents/vant/dialog/index",
+			"van-tab": "./wxcomponents/vant/tab/index",
+			"van-tabs": "./wxcomponents/vant/tabs/index",
+			"van-share-sheet": "./wxcomponents/vant/share-sheet/index",
+			"van-index-bar": "./wxcomponents/vant/index-bar/index",
+			"van-index-anchor": "./wxcomponents/vant/index-anchor/index"
+		}
+	},
+	"condition": { //模式配置,仅开发期间生效
+		"current": 0, //当前激活的模式(list 的索引项)
+		"list": [{
+			"name": "", //模式名称
+			"path": "", //启动页面,必选
+			"query": "" //启动参数,在页面的onLoad函数里面得到
+		}]
 	}
-}
+}

+ 108 - 0
src/pages/cinema/cinemalist.vue

@@ -0,0 +1,108 @@
+<template>
+	<view>
+		<van-tabs :active='active' :swipe-threshold='3' animated sticky id='tabs'>
+			<van-tab :title="getDay(dateItem)+' '+dateItem.slice(5)" v-for='(dateItem,dateIndex) in showDate' :key='index'>
+				<loadSke :loading='cinemaLoading' :list='cinemaList[dateItem]'>
+					<view class="cinema-box" v-for="(item,index) in cinemaList[dateItem]" :key='index' @click='goPage(`/pages/cinema/schedulelist?cinemaId=${item.cinemaId}&filmId=${filmId}`)'>
+						<view class="tit">
+							<text>{{item.cinemaName}}</text>
+							<text class="text-2">{{item.distance}}</text>
+						</view>
+						<text class="address">{{item.address}}</text>
+					</view>
+				</loadSke>
+			</van-tab>
+		</van-tabs>
+	</view>
+</template>
+
+<script>
+	import {
+		getCinemaList,
+		getShowList,
+		getShowDate
+	} from '@/api/cinema.js'
+	export default {
+		data: () => ({
+			active: 0,
+			cinemaList: {},
+			cinemaLoading: true,
+			dateLoading: true,
+			filmId: null,
+			cityId: null,
+			location: null,
+			showDate: []
+		}),
+		onLoad: function(option) {
+			this.filmId = option.filmId
+			this.cityId = option.cityId
+			this.location = option.location.split(',')
+		},
+		mounted() {
+			this.init()
+		},
+		methods: {
+			async init() {
+				let showDateRes = await getShowDate({
+					cityId: this.cityId,
+					filmId: this.filmId,
+				})
+				this.showDate = showDateRes.data.data.dateList
+				this.dateLoading = false
+				this.$nextTick(() => {
+					this.selectComponent('#tabs').resize();
+				})
+				this.showDate.forEach(async val => {
+					let showListRes = await getShowList({
+						cityId: this.cityId,
+						filmId: this.filmId,
+						latitude: this.location[1],
+						longitude: this.location[0],
+						page: 1,
+						limit: 30,
+						date: val
+					})
+					this.$set(this.cinemaList, val, showListRes.data.data.list)
+					this.cinemaLoading = false
+				})
+
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.cinema-box {
+		padding: 30rpx;
+		border-top: 1rpx solid #E8E8E8;
+		background-color: #FFFFFF;
+		display: flex;
+		flex-direction: column;
+
+		.tit {
+			display: flex;
+			justify-content: space-between;
+
+			.text-1 {
+				font-size: 30rpx;
+				color: #0F0404;
+			}
+
+			.text-2 {
+				font-size: 26rpx;
+				color: #999999;
+				white-space: nowrap;
+			}
+		}
+
+		.address {
+			margin-top: 20rpx;
+			width: 600rpx;
+			font-size: 26rpx;
+			color: #666666;
+			white-space: nowrap;
+			overflow: hidden;
+			text-overflow: ellipsis;
+		}
+	}
+</style>

+ 36 - 0
src/pages/cinema/citylist.vue

@@ -0,0 +1,36 @@
+<template>
+	<view>
+		<van-index-bar>
+			<loading v-if="loading" />
+			<view v-for="(item,index) in cityList" :key='index'>
+				<van-index-anchor :index="index" />
+				<van-cell @click='selectCity(sonitem)' :title="sonitem.regionName" v-for="(sonitem,sonindex) in item" :key='sonindex' />
+			</view>
+		</van-index-bar>
+	</view>
+</template>
+
+<script>
+	export default {
+		data: () => ({
+			cityList: [],
+			loading: true
+		}),
+		onLoad: async function(option) {
+			let data = await this.goPageGetData()
+			this.cityList = data.cityList
+			this.loading = false
+		},
+		methods: {
+			selectCity(obj) {
+				this.$store.commit('SET_CITYOBJ', obj)
+				this.$store.commit('SET_CITYID', obj.cityId)
+				uni.navigateBack()
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

+ 345 - 0
src/pages/cinema/orderdes.vue

@@ -0,0 +1,345 @@
+<template>
+	<view>
+		<loadSke :loading='loading'>
+			<view class="remind">请到影院现场柜台或取票机取票</view>
+			<view class="des-box">
+				<loadSke :loading='qrcodeLoading'>
+					<view v-if="ticketType==2">
+						<view v-if="orderDes.orderDataJson.filmMcpData" class="code-box">
+							<view class="code">
+								<text>取票码</text>
+								<text>{{orderDes.orderDataJson.filmMcpData.ticket.slice(4)}}</text>
+							</view>
+							<image class="qrcode" :src="filmDes.qrcode" mode="widthFix"></image>
+						</view>
+						<view v-else class="code-box">
+							<image src="https://t1-1305573081.cos.ap-shanghai.myqcloud.com/wxapp/static/imgs/%E8%AE%A2%E5%8D%95%E5%85%B3%E9%97%AD.png"
+							 class="qrcode-loading" mode="widthFix"></image>
+						</view>
+					</view>
+					<view v-else-if="ticketType!=2">
+						<view v-if="filmDes.orderStatus < 4" class="code-box">
+							<image src="https://t1-1305573081.cos.ap-shanghai.myqcloud.com/wxapp/static/imgs/%E7%94%B5%E5%BD%B1%E7%A5%A8%E7%AD%89%E5%BE%85.png"
+							 class="qrcode-loading" mode="widthFix"></image>
+						</view>
+						<view v-else-if="filmDes.orderStatus != 10" class="code-box"  v-for="(item,index) in filmDes.qrcode" :key='index'>
+							<view class="code">
+								<text>取票码</text>
+								<text>{{item.code}}</text>
+							</view>
+							<image class="qrcode" :src="item.qrcode" mode="widthFix"></image>
+						</view>
+						<view v-else class="code-box">
+							<image src="https://t1-1305573081.cos.ap-shanghai.myqcloud.com/wxapp/static/imgs/%E8%AE%A2%E5%8D%95%E5%85%B3%E9%97%AD.png"
+							 class="qrcode-loading" mode="widthFix"></image>
+						</view>
+					</view>
+				</loadSke>
+				<view class="order-num">
+					<text>订单号:{{orderDes.outTradeNo}}</text>
+					<text>{{filmDes.orderStatusStr}}</text>
+				</view>
+				<view class="des">
+					<text class="film-name">{{orderDes.orderDataJson.cinemaData.filmName}}</text>
+					<view class="film-time">
+						<text>时间</text>
+						<text>{{orderDes.orderDataJson.cinemaData.showTime}}</text>
+					</view>
+					<view class="film-address">
+						<text>影院</text>
+						<text>{{orderDes.orderDataJson.cinemaData.cinemaName}}</text>
+					</view>
+					<view class="film-video">
+						<text>影厅</text>
+						<text>{{orderDes.orderDataJson.cinemaData.hallName}}</text>
+					</view>
+					<view class="film-seat">
+						<text>座位</text>
+						<view class="seat-box">
+							<text v-for="(item,index) in seatList" :key='index'>{{item}}</text>
+						</view>
+					</view>
+				</view>
+				<view class="price">
+					<text>总价</text>
+					<text>¥{{orderDes.total/100}}</text>
+				</view>
+			</view>
+		</loadSke>
+	</view>
+</template>
+
+<script>
+	import {
+		getWxOrder
+	} from '@/api/order.js'
+	import {
+		orderQuery
+	} from '@/api/cinema.js'
+	import QRCode from 'qrcode'
+	export default {
+		data: () => ({
+			orderDes: {},
+			seatList: [],
+			outTradeNo: null,
+			loading: true,
+			pollingTiemId: null,
+			filmDes: {},
+			qrcode: null,
+			qrcodeLoading: true,
+			ticketType: null
+		}),
+		onLoad: function(option) {
+			this.outTradeNo = option.outTradeNo
+		},
+		mounted() {
+			this.init()
+		},
+		computed: {},
+		methods: {
+			async init() {
+				let orderRes = await getWxOrder(this.outTradeNo)
+				orderRes.data.orderDataJson = JSON.parse(orderRes.data.orderDataJson)
+				this.orderDes = orderRes.data
+				this.seatList = this.orderDes.orderDataJson.wxOrderCreateDTO.seat.split(',')
+				this.ticketType = this.orderDes.orderDataJson.wxOrderCreateDTO.ticketType
+				if (this.ticketType == 2) {
+					if (this.orderDes.orderDataJson.filmMcpData) {
+						this.qrcode = await this.qrcodeGenerate(this.orderDes.orderDataJson.filmMcpData.ticket.slice(4))
+					}
+				}
+				if (this.ticketType != 2) {
+					let filmRes = await orderQuery({
+						thirdOrderId: this.outTradeNo
+					})
+					this.filmDes = filmRes.data.data
+					if (this.filmDes.orderStatus < 4) {
+						this.polling()
+					} else if (this.filmDes.orderStatus < 10) {
+						this.qrcodeShow()
+					}
+				}
+				this.loading = false
+				this.qrcodeLoading = false
+			},
+			polling() {
+				this.pollingTiemId = setInterval(async () => {
+					let filmRes = await orderQuery({
+						thirdOrderId: this.outTradeNo
+					})
+					this.filmDes = filmRes.data.data
+					if (this.filmDes.orderStatus == 4 || this.filmDes.orderStatus == 5) {
+						clearInterval(this.pollingTiemId)
+						this.qrcodeShow()
+					}
+				}, 3000)
+			},
+			qrcodeShow() {
+				let qrcode = []
+				this.filmDes.ticketCode.forEach(async val => {
+					if (val.type == 1) {
+						let qr = await this.qrcodeGenerate(val.code)
+						qrcode.push({
+							qrcode: qr,
+							code: val.code
+						})
+					}
+				})
+				this.$set(this.filmDes, 'qrcode', qrcode)
+			},
+			async qrcodeGenerate(data) {
+				let qrcodeRes = await QRCode.toString(data, {
+					margin: 0,
+					errorCorrectionLevel: 'H'
+				});
+				return 'data:image/svg+xml;base64,' + Buffer(qrcodeRes).toString('base64');
+			}
+		},
+		beforeDestroy() {
+			clearInterval(this.pollingTiemId)
+		}
+	}
+</script>
+
+<style lang="scss">
+	.remind {
+		margin-top: 36rpx;
+		font-size: 26rpx;
+		font-weight: 400;
+		color: #0F0404;
+		text-align: center;
+	}
+
+	.des-box {
+		width: 564rpx;
+		background: #FFFFFF;
+		border-radius: 20rpx;
+		margin: auto;
+		margin-top: 30rpx;
+
+		.code-box {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			padding-bottom: 38rpx;
+
+			.code {
+				margin-top: 43rpx;
+
+				text {
+					font-size: 26rpx;
+					font-weight: 400;
+					color: #666666;
+					margin-right: 20rpx;
+
+					&:nth-child(2) {
+						color: #0F0404;
+					}
+				}
+			}
+
+			.qrcode {
+				margin-top: 43rpx;
+				width: 360rpx;
+				height: 360rpx;
+			}
+
+			.qrcode-loading {
+				margin-top: 43rpx;
+				width: 100%;
+			}
+		}
+
+		.order-num {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			padding: 34rpx 31rpx;
+			border-top: 1rpx dashed #D9D9D9;
+			border-bottom: 1rpx dashed #D9D9D9;
+			position: relative;
+
+			&::after {
+				content: '';
+				width: 24rpx;
+				height: 24rpx;
+				border-radius: 50%;
+				background-color: #F1F1F1;
+				position: absolute;
+				top: 0;
+				left: 0;
+				transform: translate(-50%, -50%);
+			}
+
+			&::before {
+				content: '';
+				width: 24rpx;
+				height: 24rpx;
+				border-radius: 50%;
+				background-color: #F1F1F1;
+				position: absolute;
+				top: 0;
+				right: 0;
+				transform: translate(50%, -50%);
+			}
+
+			text {
+				&:nth-child(1) {
+					width: 60%;
+					word-break: break-all;
+					font-size: 26rpx;
+					font-weight: 400;
+					color: #666666;
+				}
+
+				&:nth-child(2) {
+					padding: 10rpx;
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					background: #FFE5E5;
+					border: 1rpx solid #E31818;
+					border-radius: 20rpx;
+					font-size: 20rpx;
+					font-weight: 400;
+					color: #E31818;
+				}
+			}
+		}
+
+		.des {
+			padding: 45rpx 30rpx;
+			border-bottom: 1rpx dashed #D9D9D9;
+			display: flex;
+			flex-direction: column;
+
+			.film-name {
+				font-size: 30rpx;
+				font-weight: 400;
+				color: #0F0404;
+			}
+
+			text {
+				font-size: 26rpx;
+				font-weight: 400;
+				color: #666666;
+				margin-right: 20rpx;
+				white-space: nowrap;
+			}
+
+			.film-time {
+				text:nth-child(2) {
+					color: #999999;
+				}
+			}
+
+			.film-address {
+				text:nth-child(2) {
+					color: #999999;
+				}
+			}
+
+			.film-video {
+				text:nth-child(2) {
+					color: #999999;
+				}
+			}
+
+			.film-seat {
+				display: flex;
+				justify-content: flex-start;
+				align-items: stretch;
+				margin-top: 10rpx;
+
+				.seat-box {
+					display: flex;
+					flex-wrap: wrap;
+
+					text {
+						height: 40rpx;
+						padding: 4rpx 15rpx;
+						margin-bottom: 10rpx;
+						background: #EBEBEB;
+						border-radius: 4rpx;
+						color: #999999;
+					}
+				}
+			}
+		}
+
+		.price {
+			padding: 40rpx 30rpx;
+
+			text {
+				font-size: 26rpx;
+				font-weight: 400;
+				color: #666666;
+				margin-right: 20rpx;
+
+				&:nth-child(2) {
+					color: #E31818;
+				}
+			}
+		}
+	}
+</style>

+ 490 - 0
src/pages/cinema/placeorder.vue

@@ -0,0 +1,490 @@
+<template>
+	<view>
+		<view class="film-des">
+			<image class="left-img" :src="filmDes.pic" mode="widthFix"></image>
+			<view class="right-box">
+				<view class="film-name">
+					{{showItem.filmName}}
+				</view>
+				<view class="film-time">
+					{{showItem.showTime}}<br />
+					{{showItem.cinemaName}}<br />
+					{{showItem.hallName}}
+				</view>
+				<view class="film-caveat">
+					<view class="item">
+						<van-icon name="clear" color='#E31818' />
+						不支持退票
+					</view>
+					<view class="item">
+						<van-icon name="clear" color='#E31818' />
+						不支持改签
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="select-des">
+			<view class="tit-text">
+				选择购票方式
+			</view>
+			<van-radio-group :value="radioType" @change="radioOnChange">
+				<view class="options-box">
+					<view class="left-box">
+						<image class="img" src="https://t1-1305573081.cos.ap-shanghai.myqcloud.com/wxapp/static/imgs/%E7%89%B9%E6%83%A0%E8%B4%AD%E7%A5%A8.png"
+						 mode="widthFix"></image>
+						<view class="text-box">
+							<text class="top">特惠购票</text>
+							<text class="bottom">固定折扣,快速出票</text>
+						</view>
+					</view>
+					<view class="right-box">
+						<view class="text-box">
+							<text class="top">¥{{aPrice}}/张</text>
+							<text class="bottom">优惠 ¥{{aDiscount}}元</text>
+						</view>
+						<van-radio :name="1" checked-color="#07c160"></van-radio>
+					</view>
+				</view>
+				<view class="options-box">
+					<view class="left-box">
+						<image class="img" src="https://t1-1305573081.cos.ap-shanghai.myqcloud.com/wxapp/static/imgs/%E5%BF%AB%E9%80%9F%E8%B4%AD%E7%A5%A8.png"
+						 mode="widthFix"></image>
+						<view class="text-box">
+							<text class="top">快速购票</text>
+							<text class="bottom">无需等待,即买即兑</text>
+						</view>
+					</view>
+					<view class="right-box">
+						<view class="text-box">
+							<text class="top">¥{{(showItem.netPrice/100).toFixed(2)}}/张</text>
+						</view>
+						<van-radio :name="2" checked-color="#07c160"></van-radio>
+					</view>
+				</view>
+				<view class="options-box">
+					<view class="left-box">
+						<image class="img" src="https://t1-1305573081.cos.ap-shanghai.myqcloud.com/wxapp/static/imgs/%E7%AB%9E%E4%BB%B7.png"
+						 mode="widthFix"></image>
+						<view class="text-box">
+							<text class="top">竞价购买</text>
+							<text class="bottom">为您网罗最低的折扣,约耗时几分钟</text>
+						</view>
+					</view>
+					<view class="right-box">
+						<view class="text-box">
+							<text class="top">等待竞价</text>
+						</view>
+						<van-radio :name="3" checked-color="#07c160" disabled></van-radio>
+					</view>
+				</view>
+			</van-radio-group>
+		</view>
+		<view class="seat-select">
+			<text class="tit-text">无座时接受系统调座</text>
+			<van-switch :checked="acceptChangeSeat==1" @change="onChange" size="24px" />
+		</view>
+		<view class="seat-des">
+			<view class="tit-text">
+				购票须知
+			</view>
+			<view class="seat-box">
+				<view class="left">
+					座位:
+				</view>
+				<view class="right">
+					<view class="item" v-for="(item,index) in seatResult" :key='index'>
+						<text class="top">{{item.seatNo}}</text>
+						<text class="bottom">¥{{aPrice}}</text>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="buy-des">
+			<view class="left">
+				<text class="one">总价:</text>
+				<text class="two">¥{{allPrice}}</text>
+				<text class="three">优惠¥{{allDiscount}}元</text>
+			</view>
+			<view class="right">
+				<text class="text">{{seatResult.length}}张</text>
+				<button class="btn" type="default" @click="buy">去支付</button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		prepareOrder
+	} from '@/api/cinema.js'
+	export default {
+		data: () => ({
+			showItem: {},
+			seatResult: [],
+			radioType: 1,
+			acceptChangeSeat: 0,
+			orderRes: {}
+		}),
+		onLoad: async function(option) {
+			let data = await this.goPageGetData()
+			this.showItem = data.showItem
+			this.seatResult = data.seatResult.map((val) => {
+				return JSON.parse(val)
+			})
+		},
+		computed: {
+			filmDes() {
+				let des = {}
+				this.$store.state.cinema.filmList.forEach((val) => {
+					if (val.filmId == this.showItem.filmId) {
+						des = val
+					}
+				})
+				return des;
+			},
+			filmDiscount() {
+				return this.$store.state.cinema.filmDiscount;
+			},
+			discountRule() {
+				return this.$store.state.cinema.discountRule;
+			},
+			aPrice() {
+				let discountRule = (this.showItem.netPrice > 3900 ? this.discountRule.upDiscountRate : this.discountRule.downDiscountRate)
+				let filmDiscount = this.filmDiscount
+				let netPrice = this.showItem.netPrice / 100
+				return (netPrice * discountRule * filmDiscount).toFixed(2)
+			},
+			aDiscount() {
+				let discountRule = (this.showItem.netPrice > 3900 ? this.discountRule.upDiscountRate : this.discountRule.downDiscountRate)
+				let filmDiscount = this.filmDiscount
+				let netPrice = this.showItem.netPrice / 100
+				return (netPrice - netPrice * discountRule * filmDiscount).toFixed(2)
+			},
+			allPrice() {
+				let discountRule = (this.showItem.netPrice > 3900 ? this.discountRule.upDiscountRate : this.discountRule.downDiscountRate)
+				let filmDiscount = this.filmDiscount
+				let netPrice = this.showItem.netPrice / 100
+				if (this.radioType == 1) {
+					return (this.seatResult.length * netPrice * discountRule * filmDiscount).toFixed(2)
+				}
+				if (this.radioType == 2) {
+					return (this.seatResult.length * netPrice).toFixed(2)
+				}
+			},
+			allDiscount() {
+				let discountRule = (this.showItem.netPrice > 3900 ? this.discountRule.upDiscountRate : this.discountRule.downDiscountRate)
+				let filmDiscount = this.filmDiscount
+				let netPrice = this.showItem.netPrice / 100
+				if (this.radioType == 1) {
+					return (this.seatResult.length * (netPrice - netPrice * discountRule * filmDiscount)).toFixed(2)
+				}
+				if (this.radioType == 2) {
+					return (this.seatResult.length * (netPrice - netPrice)).toFixed(2)
+				}
+			},
+		},
+		methods: {
+			radioOnChange(e) {
+				this.orderRes = {}
+				this.radioType = e.detail
+			},
+			onChange({
+				detail
+			}) {
+				// 需要手动对 checked 状态进行更新
+				if (detail) {
+					this.acceptChangeSeat = 1
+				} else {
+					this.acceptChangeSeat = 0
+				}
+			},
+			async buy() {
+				//已有订单则直接请求历史数据
+				if (this.orderRes.code == 200) {
+					let payRes = await wx.requestPayment(this.orderRes.data)
+					console.log(payRes)
+					return false
+				}
+
+				let seat = [],
+					seatId = [],
+					seatNo = []
+				this.seatResult.forEach(val => {
+					seat.push(val.seatNo)
+					seatNo.push(val.seatNo)
+					seatId.push(val.seatId)
+				})
+				prepareOrder({
+					acceptChangeSeat: this.acceptChangeSeat,
+					seat: seat.join(','),
+					seatId: seatId.join('|'),
+					seatNo: seatNo.join('|'),
+					showId: this.showItem.showId,
+					cinemaId: this.showItem.cinemaId,
+					goodsPictureUrl: this.filmDes.pic,
+					goodsType: '1',
+					ticketType: this.radioType
+				}).then(async res => {
+					this.orderRes = res
+					let payRes = await wx.requestPayment(this.orderRes.data)
+					if (payRes.errMsg == 'requestPayment:ok') {
+						this.goPage(`/pages/cinema/orderdes?outTradeNo=${this.orderRes.data.outTradeNo}`, 'reLaunch')
+					}
+					console.log(payRes)
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.film-des {
+		padding: 40rpx 30rpx;
+		margin-top: 20rpx;
+		background-color: #FFFFFF;
+		display: flex;
+
+		.left-img {
+			width: 170rpx;
+			height: 236rpx;
+			margin-right: 20rpx;
+		}
+
+		.right-box {
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+
+			.film-name {
+				font-size: 30rpx;
+				font-weight: 400;
+				color: #0F0404;
+			}
+
+			.film-time {
+				font-size: 26rpx;
+				font-weight: 400;
+				color: #666666;
+				line-height: 39rpx;
+			}
+
+			.film-caveat {
+				display: flex;
+
+				.item {
+					display: flex;
+					align-items: center;
+					margin-right: 33rpx;
+					font-size: 26rpx;
+					font-weight: 400;
+					color: #0F0404;
+				}
+			}
+		}
+	}
+
+	.select-des {
+		background-color: #FFFFFF;
+		margin-top: 20rpx;
+		padding-bottom: 1rpx;
+
+		.tit-text {
+			padding: 30rpx;
+			font-size: 26rpx;
+			font-weight: 400;
+			color: #0F0404;
+			border-bottom: 1rpx solid #E8E8E8;
+		}
+
+		.options-box {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			margin: 30rpx;
+
+			.left-box {
+				display: flex;
+
+				.img {
+					width: 66rpx;
+					height: 66rpx;
+					margin-right: 20rpx;
+				}
+
+				.text-box {
+					display: flex;
+					flex-direction: column;
+
+					.top {
+						font-size: 26rpx;
+						font-weight: 400;
+						color: #0F0404;
+					}
+
+					.bottom {
+						font-size: 22rpx;
+						font-weight: 400;
+						color: #666666;
+					}
+				}
+			}
+
+			.right-box {
+				display: flex;
+				align-items: center;
+
+				.text-box {
+					display: flex;
+					flex-direction: column;
+					justify-content: space-around;
+					align-items: flex-end;
+					margin-right: 23rpx;
+
+					.top {
+						font-size: 26rpx;
+						font-weight: 400;
+						color: #E31818;
+					}
+
+					.bottom {
+						font-size: 20rpx;
+						font-weight: 400;
+						color: #605A5A;
+					}
+				}
+			}
+		}
+	}
+
+	.seat-select {
+		background-color: #FFFFFF;
+		margin-top: 20rpx;
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 20rpx 30rpx;
+
+		.tit-text {
+			font-size: 26rpx;
+			font-weight: 400;
+			color: #E31818;
+		}
+	}
+
+	.seat-des {
+		background-color: #FFFFFF;
+		margin-top: 20rpx;
+
+		.tit-text {
+			font-size: 26rpx;
+			font-weight: 400;
+			color: #0F0404;
+			padding: 30rpx;
+			border-bottom: 1rpx solid #E8E8E8;
+		}
+
+		.seat-box {
+			display: flex;
+			justify-content: flex-start;
+			padding: 10rpx 30rpx;
+
+			.left {
+				white-space: nowrap;
+				height: 96rpx;
+				margin-top: 10rpx;
+			}
+
+			.right {
+				display: flex;
+				flex-wrap: wrap;
+				justify-content: flex-start;
+				align-items: center;
+
+				.item {
+					width: 134rpx;
+					height: 96rpx;
+					margin: 10rpx;
+					background: #EBEBEB;
+					border-radius: 10rpx;
+					display: flex;
+					flex-direction: column;
+					justify-content: center;
+					align-items: center;
+
+					.top {
+						font-size: 26rpx;
+						font-weight: 400;
+						color: #666666;
+					}
+
+					.bottom {
+						font-size: 22rpx;
+						font-weight: 400;
+						color: #E31923;
+					}
+				}
+			}
+		}
+	}
+
+	.buy-des {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		background-color: #FFFFFF;
+		width: 100vw;
+		box-sizing: border-box;
+		position: fixed;
+		bottom: 0;
+		padding: 15rpx 30rpx;
+		padding-bottom: calc(10px + env(safe-area-inset-bottom)/2);
+
+		.left {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			width: 50%;
+
+			.one {
+				font-size: 26rpx;
+				color: #0F0404;
+			}
+
+			.two {
+				font-size: 36rpx;
+				color: #E31919;
+			}
+
+			.three {
+				font-size: 22rpx;
+				color: #E86125;
+			}
+		}
+
+		.right {
+			width: 40%;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+
+			.text {
+				font-size: 26rpx;
+				color: #0F0404;
+			}
+
+			.btn {
+				margin: 0;
+				width: 200rpx;
+				height: 80rpx;
+				background: linear-gradient(90deg, #E31818, #ED3E24, #ED4F24);
+				border-radius: 40rpx;
+				font-size: 30rpx;
+				color: #FFFFFF;
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				white-space: nowrap;
+			}
+		}
+	}
+</style>

+ 335 - 0
src/pages/cinema/schedulelist.vue

@@ -0,0 +1,335 @@
+<template>
+	<page-meta>
+		<navigation-bar :title="filmList[filmId].name" background-color="#FFFFFF" front-color="#000000" />
+	</page-meta>
+	<view class="schedulelist">
+		<loadSke :loading='listLoading'>
+			<view class="des-box">
+				<view class="address">
+					{{cinemaName}}
+				</view>
+				<swiper class="swiper-box" :current="current" previous-margin='200rpx' next-margin='200rpx' @change='swiperChange'>
+					<swiper-item v-for="(item,index) in scheduleListKey" :key='index' class="swiper-item">
+						<image class="image" :class="{imgBig:current==index}" :src="filmList[item].pic" mode="heightFix" :draggable="false" />
+					</swiper-item>
+				</swiper>
+				<view class="film-name">
+					{{filmList[filmId].name}}
+				</view>
+				<view class="film-des">
+					{{filmList[filmId].duration}}分钟 | {{filmList[filmId].filmTypes}} | {{filmList[filmId].cast}}
+				</view>
+			</view>
+
+
+			<view class="schedule-box">
+				<van-tabs :active='tabActive' :swipe-threshold='3' animated swipeable id="tabs" @change='tabsChange'>
+					<van-tab class='schedule-tab' :title="getDay(index)+' '+index.slice(5)" v-for='(item,index) in scheduleItem' :key='index'>
+						<view v-for="(sonItem,sonIndex) in item" :key='sonItem.showId' class="schedule-des" @click="clickBuy(sonItem)">
+							<view class="left-box">
+								<view class="item-box">
+									<text>{{sonItem.showTime.slice(11,16)}}</text>
+									<text class="bottom-text">{{sonItem.duration}}分钟</text>
+								</view>
+								<view class="type-box">
+									<text>{{sonItem.planType}}</text>
+									<text class="bottom-text">{{sonItem.hallName}}</text>
+								</view>
+							</view>
+							<view class="right-box">
+								<view class="price-box">
+									<text>{{(sonItem.netPrice/100*(sonItem.netPrice>3900?discountRule.upDiscountRate:discountRule.downDiscountRate)*filmDiscount).toFixed(2)}}元</text>
+									<text class="bottom-text old-price">{{sonItem.netPrice/100}}元</text>
+								</view>
+								<button v-if='discontinued(sonItem.stopSellTime)'>购 票</button>
+								<button v-else type="default" style="background: linear-gradient(90deg, #c0c0c0, #c0c0c0, #c0c0c0);">停 售</button>
+							</view>
+						</view>
+					</van-tab>
+				</van-tabs>
+			</view>
+		</loadSke>
+	</view>
+</template>
+
+<script>
+	import {
+		getScheduleList,
+		getfilmInfo
+	} from '@/api/cinema.js'
+	export default {
+		data: () => ({
+			listLoading: true,
+			tabActive: 0,
+			current: 0,
+			cinemaId: null,
+			filmId: null,
+			cinemaName: '',
+			scheduleList: {},
+			scheduleListKey: null,
+			discountRule: {},
+			scheduleItem: {}
+		}),
+		computed: {
+			filmList() {
+				let list = {}
+				this.$store.state.cinema.filmList.map((val) => {
+					list[val.filmId] = val
+				})
+				return list;
+			},
+			filmDiscount() {
+				return this.$store.state.cinema.filmDiscount;
+			}
+		},
+		onLoad: function(option) {
+			this.cinemaId = option.cinemaId;
+			this.filmId = option.filmId;
+		},
+		mounted() {
+			this.init()
+		},
+		methods: {
+			clickBuy(item) {
+				let flag = this.discontinued(item.stopSellTime)
+				if (flag) {
+					this.goPage(`/pages/cinema/selectseat`, null, {
+						showItem: item
+					})
+				}
+				return
+			},
+			init() {
+				this.listLoading = true
+				getScheduleList({
+					cinemaId: this.cinemaId
+				}).then(res => {
+					this.scheduleList = {}
+					this.discountRule = res.data.data.discountRule
+					this.$store.commit('SET_DISCOUNTRULE', res.data.data.discountRule)
+					this.cinemaName = res.data.data.list[0].cinemaName
+					res.data.data.list.map((val) => {
+						this.scheduleList[val.filmId] || (this.scheduleList[val.filmId] = {})
+						this.scheduleList[val.filmId][val.showDate] || (this.scheduleList[val.filmId][val.showDate] = [])
+						this.scheduleList[val.filmId][val.showDate].push(val)
+					})
+					this.scheduleListKey = Object.keys(this.scheduleList);
+					//定位影片位置
+					(this.filmId == 'undefined') && (this.filmId = this.scheduleListKey[0])
+					this.scheduleItem = this.scheduleList[this.filmId]
+					this.current = this.scheduleListKey.indexOf(this.filmId)
+					this.listLoading = false
+					setTimeout(() => {
+						this.selectComponent('#tabs').resize();
+					}, 500)
+				})
+			},
+			discontinued(date) {
+				let flag = (new Date(date.replace(/-/g, "/")).getTime() - new Date().getTime() - 1000 * 60 * 60) > 0
+				return flag
+			},
+			swiperChange(e) {
+				this.current = e.detail.current
+				this.filmId = this.scheduleListKey[this.current]
+				this.scheduleItem = {}
+				this.$nextTick(() => {
+					this.scheduleItem = this.scheduleList[this.filmId]
+					this.$nextTick(() => {
+						this.tabActive = 0
+						this.selectComponent('#tabs').resize();
+					})
+				})
+			},
+			tabsChange(e) {
+				this.tabActive = e.detail.index
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.button-box {
+		width: 100%;
+		height: 100rpx;
+		display: flex;
+		justify-content: space-around;
+		align-items: stretch;
+		background-color: #ffffff;
+
+		.problem-type {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+		}
+
+		.active {
+			border-bottom: 2px solid red;
+		}
+	}
+
+	.des-box {
+		background-image: url(https://t1-1305573081.cos.ap-shanghai.myqcloud.com/wxapp/static/imgs/filmBg.png);
+		padding: 35rpx 30rpx;
+		box-sizing: border-box;
+		width: 100%;
+		height: 690rpx;
+
+		.address {
+			font-size: 30rpx;
+			font-weight: bold;
+			color: #FFFFFF;
+		}
+
+		.swiper-box {
+			margin: auto;
+			margin-top: 60rpx;
+			padding: 0 45rpx;
+			box-sizing: border-box;
+			width: 100%;
+			height: 270rpx;
+
+			.swiper-item {
+				display: flex;
+				justify-content: center;
+				align-items: center;
+			}
+
+			.image {
+				height: 86%;
+			}
+
+			.imgBig {
+				height: 100% !important;
+			}
+		}
+
+		.film-name {
+			margin-top: 53rpx;
+			text-align: center;
+			font-size: 30rpx;
+			font-weight: bold;
+			color: #FFFFFF;
+		}
+
+		.film-des {
+			margin-top: 19rpx;
+			text-align: center;
+			font-size: 22rpx;
+			font-weight: 400;
+			color: #FFFFFF;
+			width: 100%;
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		}
+	}
+
+	.schedule-box {
+		width: 100vw;
+		min-height: calc(100vh - 690rpx + 122rpx - 10px - env(safe-area-inset-bottom)/2);
+		padding-bottom: calc(10px + env(safe-area-inset-bottom)/2);
+		background-color: #FFFFFF;
+		border-radius: 50rpx 50rpx 0px 0px;
+		margin-top: -122rpx;
+		overflow: hidden;
+
+		.schedule-tab {
+			overflow-y: auto;
+		}
+
+		.schedule-des {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			padding: 0 30rpx;
+			margin-top: 50rpx;
+
+			.bottom-text {
+				font-size: 22rpx;
+				font-weight: 400;
+				color: #999999;
+				width: 100rpx;
+				text-align: center;
+				white-space: nowrap;
+				overflow: hidden;
+				text-overflow: ellipsis;
+			}
+
+			.left-box {
+				width: 50%;
+				display: flex;
+				justify-content: space-around;
+				align-items: stretch;
+
+				.item-box {
+					display: flex;
+					flex-direction: column;
+					justify-content: space-between;
+					align-items: center;
+
+					&:first-child {
+						font-size: 40rpx;
+						font-weight: 400;
+						color: #0F0404;
+					}
+				}
+
+				.type-box {
+					display: flex;
+					flex-direction: column;
+					justify-content: space-between;
+					align-items: center;
+
+					&:first-child {
+						font-size: 26rpx;
+						font-weight: 400;
+						color: #0F0404;
+					}
+
+					:last-child {
+						width: 220rpx !important;
+					}
+				}
+			}
+
+			.right-box {
+				width: 45%;
+				display: flex;
+				justify-content: space-around;
+				align-items: center;
+
+				.price-box {
+					display: flex;
+					flex-direction: column;
+					justify-content: space-between;
+					align-items: center;
+
+					&:first-child {
+						font-size: 36rpx;
+						font-weight: 400;
+						color: #E31919;
+					}
+
+					.old-price {
+						text-decoration: line-through;
+					}
+				}
+
+				button {
+					margin: 0;
+					width: 126rpx;
+					height: 60rpx;
+					background: linear-gradient(90deg, #E31818, #ED3E24, #ED4F24);
+					border-radius: 30rpx;
+					font-size: 26rpx;
+					font-weight: 400;
+					color: #FFFFFF;
+					white-space: nowrap;
+					display: flex;
+					justify-content: center;
+					align-items: center;
+				}
+			}
+		}
+	}
+</style>

+ 319 - 0
src/pages/cinema/selectseat.vue

@@ -0,0 +1,319 @@
+<template>
+	<page-meta>
+		<navigation-bar :title="showItem.filmName" background-color="#FFFFFF" front-color="#000000" />
+	</page-meta>
+	<view>
+		<view class="seatSelection-box">
+			<view class="top-box">
+				<view class="address">
+					{{showItem.cinemaName}}
+				</view>
+				<view class="logo-box">
+					<view class="item">
+						<view class="seat selectable"></view>可选
+					</view>
+					<view class="item">
+						<view class="seat selected"></view>已选
+					</view>
+					<view class="item">
+						<view class="seat notSelect"></view>已售
+					</view>
+				</view>
+			</view>
+			<movable-area scale-area class="movable-area">
+				<movable-view scale out-of-bounds direction='all' scale-max="3" @scale='scaleChange'>
+					<view class="movable-box">
+						<view class="screen-box">
+							<image class="screen" src="https://t1-1305573081.cos.ap-shanghai.myqcloud.com/wxapp/static/imgs/%E9%93%B6%E5%B9%95.png"
+							 mode="widthFix"></image>
+							<text class="screen-name">{{showItem.hallName}}</text>
+						</view>
+						<loadSke :loading='loading'>
+							<view class="seat-box" :style="{'--col':collong}">
+								<van-checkbox-group :value="seatResult" @change="groupOnChange" :max="max">
+									<view class="col" v-for="(rowitem,rowindex) in rowlong" :key='rowindex'>
+										<view v-for="(colitem,colindex) in collong" :key='colindex'>
+											<van-checkbox use-icon-slot v-if="seatSet(rowindex,colindex,seatList)==='N'" :name="JSON.stringify(seatList[rowindex+1][colindex+1])">
+												<view class="seat" :class="{selected:selected(rowindex,colindex,seatResult)}" slot="icon"></view>
+											</van-checkbox>
+											<view v-else-if="seatSet(rowindex,colindex,seatList)==='LK'" class="seat notSelect"></view>
+											<view v-else class="seat noneSeat"></view>
+										</view>
+									</view>
+								</van-checkbox-group>
+							</view>
+						</loadSke>
+					</view>
+				</movable-view>
+			</movable-area>
+		</view>
+
+		<view class="bottom-box">
+			<view class="buy-des">
+				<view class="film-des">
+					{{showItem.showTime}}
+				</view>
+				<view class="item-box">
+					<view class="seat-item" v-for="(item,index) in seatResult" :key='index'>
+						<view class="">
+							{{JSON.parse(item).seatNo}}
+						</view>
+						<van-icon name="cross" @click='remove(item)' />
+					</view>
+				</view>
+			</view>
+			<view class="price-box">
+				<view class="left-box">
+					总共:{{seatResult.length}}张
+				</view>
+				<button v-if="seatResult.length>0" class="btn" type="default" @click="goPage(`/pages/cinema/placeorder`,null,{seatResult:seatResult,showItem:showItem})">下一步</button>
+				<button v-else class="btn" style="background: linear-gradient(90deg, #c0c0c0, #c0c0c0, #c0c0c0);" type="default">下一步</button>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	import {
+		getSeat
+	} from '@/api/cinema.js'
+	export default {
+		data: () => ({
+			showItem: null,
+			seatList: null,
+			rowlong: 0,
+			collong: 0,
+			seatResult: [],
+			max: null,
+			loading: true
+		}),
+		onLoad: async function(option) {
+			let data = await this.goPageGetData()
+			this.showItem = data.showItem
+		},
+		mounted() {
+			this.init()
+		},
+		methods: {
+			scaleChange(e) {
+				console.log(e)
+			},
+			groupOnChange(e) {
+				this.seatResult = e.detail
+			},
+			remove(item) {
+				this.seatResult.splice(this.seatResult.indexOf(item), 1)
+			},
+			seatSet(row, col, list) {
+				if (!list) {
+					return false
+				} else if (list[row + 1]) {
+					if (list[row + 1][col + 1])
+						return list[row + 1][col + 1].status
+				}
+			},
+			selected(row, col, seatResult) {
+				let flag = false
+				seatResult.forEach((val) => {
+					if (JSON.parse(val).columnNo == col + 1 && JSON.parse(val).rowNo == row + 1)
+						flag = true
+				})
+				return flag
+			},
+			loveSeat(row, col, list) {
+				return ''
+			},
+			async init() {
+				let seatRes = await getSeat({
+					showId: this.showItem.showId
+				})
+				console.log(seatRes)
+				this.max = seatRes.data.data.seatData.restrictions
+				seatRes.data.data.seatData.seats.forEach((val) => {
+					this.seatList || (this.seatList = {})
+					this.seatList[val.rowNo] || (this.seatList[val.rowNo] = {})
+					// this.$set(this.seatList[val.rowNo], val.columnNo, val)
+					if (parseInt(val.rowNo) > this.rowlong) {
+						this.rowlong = parseInt(val.rowNo)
+					}
+					if (parseInt(val.columnNo) > this.collong) {
+						this.collong = parseInt(val.columnNo)
+					}
+					this.seatList[val.rowNo][val.columnNo] = val
+				})
+				this.$nextTick(() => {
+					this.loading = false
+				})
+				console.log(this.seatList)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.movable-area {
+		width: 100vw;
+		min-height: 500rpx;
+	}
+
+	.movable-box {
+		width: 100vw;
+		min-height: 500rpx;
+	}
+
+	.seatSelection-box {
+		box-sizing: border-box;
+
+		.top-box {
+			padding: 30rpx;
+			box-sizing: border-box;
+
+			.address {
+				font-size: 30rpx;
+				font-weight: 400;
+				color: #0F0404;
+			}
+
+			.logo-box {
+				margin-top: 43rpx;
+				width: 50%;
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+
+				.item {
+					display: flex;
+					justify-content: center;
+					align-items: center;
+				}
+
+
+			}
+		}
+
+
+		.seat {
+			width: 26rpx;
+			height: 26rpx;
+			border: 1rpx solid #D9D9D9;
+			border-radius: 4rpx;
+			background: #FFFFFF;
+		}
+
+		.noneSeat {
+			opacity: 0;
+		}
+
+		.notSelect {
+			opacity: 1;
+			background: #FB3D46;
+		}
+
+		.selected {
+			opacity: 1;
+			background: #47CB79;
+		}
+
+
+
+		.screen-box {
+			position: relative;
+			margin-top: 47rpx;
+
+			.screen {
+				width: 100%;
+			}
+
+			.screen-name {
+				position: absolute;
+				left: 50%;
+				top: 30rpx;
+				transform: translateX(-50%);
+				white-space: nowrap;
+				font-size: 22rpx;
+				font-weight: 400;
+				color: #999999;
+			}
+		}
+
+		.seat-box {
+			margin-top: 50rpx;
+			width: calc(var(--col) * 30rpx);
+			margin: auto;
+
+			.col {
+				display: flex;
+				justify-content: space-around;
+				align-items: center;
+				margin-top: 15rpx;
+			}
+		}
+	}
+
+	.bottom-box {
+		position: fixed;
+		width: 100vw;
+		bottom: 0;
+
+		.buy-des {
+			background-color: #FFFFFF;
+			padding: 30rpx;
+
+			.film-des {
+				font-size: 26rpx;
+				font-weight: 400;
+				color: #666666;
+			}
+
+			.item-box {
+				display: flex;
+				justify-content: flex-start;
+				flex-wrap: wrap;
+
+				.seat-item {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					width: 200rpx;
+					background: #E6E6E6;
+					border-radius: 10rpx;
+					margin: 20rpx 20rpx 0rpx 0rpx;
+					padding: 20rpx;
+					box-sizing: border-box;
+				}
+			}
+		}
+
+		.price-box {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			background-color: #FFFFFF;
+			padding: 15rpx 30rpx;
+			padding-bottom: calc(15rpx + 10px + env(safe-area-inset-bottom)/2);
+			margin-top: 20rpx;
+
+			.left-box {
+				font-size: 26rpx;
+				font-weight: 400;
+				color: #0F0404;
+			}
+
+			.btn {
+				margin: 0;
+				width: 200rpx;
+				height: 80rpx;
+				background: linear-gradient(90deg, #E31818, #ED3E24, #ED4F24);
+				border-radius: 40rpx;
+				font-size: 30rpx;
+				font-weight: 400;
+				color: #FFFFFF;
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				white-space: nowrap;
+			}
+		}
+	}
+</style>

+ 449 - 0
src/pages/index/components/applist.vue

@@ -0,0 +1,449 @@
+<template>
+	<scroll-view scroll-y class="content" @scrolltolower='onBottom'>
+		<topbar />
+		<view class="back">
+			<view class="sticky">
+				<van-search class='top-search' shape="round" background="#fff" placeholder="请输入搜索关键词" @search="onSearch" @change='onSearchChange' />
+				<view class="grid">
+					<view class="grid-item" :class="{border:listType==0}" @click="listType=0">
+						<van-icon name="/static/imgs/jx.png" size="30px" />
+						<text class="text">精选</text>
+					</view>
+					<view class="grid-item" :class="{border:listType==item.id}" @click="listType=item.id" v-for="(item,index) in typeList"
+					 :key='index'>
+						<van-icon :name="item.fileUrl || '/static/imgs/wxtb.png'" size="30px" />
+						<text class="text">{{item.productName}}</text>
+					</view>
+					<view class="grid-item" :class="{border:listType==5}" @click="moreShow=!moreShow,typeMore()">
+						<van-icon v-if='moreShow' name="/static/imgs/sq.png" size="30px" />
+						<van-icon v-else name="/static/imgs/gd.png" size="30px" />
+						<text class="text">{{moreShow?'收起':'更多'}}</text>
+					</view>
+				</view>
+			</view>
+
+			<loadSke :loading='loading' :list='applist'>
+				<view class='list-item' v-for="(item, index) in applist" :key="index" @click="clickJumpType(item)">
+					<image class="avatar" mode="aspectFit" :src="item.appletLogoFileUrl || '/static/imgs/shmr.png'" />
+					<view class="top">
+						<van-icon class="icon" v-if="item.collectionStatus=='0'" @tap.stop='addSc(index)' name="star-o" color='#999999' />
+						<van-icon class="icon" v-else @tap.stop='delSc(index)' name="star" color='#dfdf00' />
+						<van-icon class="icon" name="ellipsis" size='20' color='#999999' @tap.stop='showShare=true' />
+					</view>
+					<view class="item-right">
+						<view class="bottom">
+							<text class="title">{{item.corporateName}}</text>
+							<text class="type">{{itemType(item)}}</text>
+							<view class="right" v-if="false">
+								<view class="flex">
+									<van-icon class="icon" name="/static/imgs/评论.png" size='15' />
+									<text>54</text>
+								</view>
+								<view class="flex">
+									<van-icon class="icon" name="/static/imgs/好评率.png" size='15' />
+									<text>99%</text>
+								</view>
+							</view>
+						</view>
+						<view class="center" v-if="item.appletIntroduce">
+							<text class="details">{{item.appletIntroduce || `Appid: ${item.appletAddress}`}}</text>
+						</view>
+
+					</view>
+				</view>
+				<view v-if="bottomLoading" class="bottom-loading">
+					正在加载中...
+				</view>
+				<view class="ad-box list-item">
+					<ad unit-id="adunit-d8c1548cc9663765"></ad>
+				</view>
+				<view class="ad-box list-item">
+					<ad-custom unit-id="adunit-66504f2e91ee7be4"></ad-custom>
+				</view>
+			</loadSke>
+
+
+
+		</view>
+
+
+
+		<van-share-sheet :show="showShare" :options="options" @select="onSelect" @close='shareClose' />
+	</scroll-view>
+
+</template>
+
+<script>
+	import {
+		debounce
+	} from '@/utils/utils.js'
+	import {
+		typeList,
+		getTypeAppList,
+		addFavorites,
+		delFavorites,
+		BrowseRecordAdd,
+		getCarouselChartList
+	} from '@/api/applist.js'
+	export default {
+		data() {
+			return {
+				comname: 'xueche',
+				active: 0,
+				tabActive: 0,
+				info: [{
+					"id": 12,
+					"pictureName": "山泉心品",
+					"fileId": "77",
+					"fileUrl": "http://image.jppt.com.cn/zzjs/2021-04-09/others/1617954376809.jpg",
+					"jumpUrl": "{\"appId\":\"wxb47185e82704dda8\"}",
+					"jumpUrlType": "goMiniApp",
+					"status": 0
+				}],
+				current: 0,
+				mode: 'round',
+				applist: [],
+				typeList: {},
+				listType: 0,
+				loading: true,
+				showShare: false,
+				moreShow: false,
+				options: [{
+					name: '微信',
+					icon: 'wechat',
+					openType: 'share'
+				}],
+				pageNum: 1,
+				pageSize: 10,
+				total: 1,
+				customerName: '',
+				bottomLoading: false
+			}
+		},
+		mounted() {
+			this.initAppList()
+		},
+		watch: {
+			listType(val) {
+				this.customerName = ''
+				this.pageNum = 1
+				this.loading = true
+				getTypeAppList(val, {
+					pageNum: this.pageNum,
+					pageSize: this.pageSize
+				}).then(res => {
+					this.applist = res.rows
+					this.total = res.total
+					this.$nextTick(() => {
+						this.loading = false
+					})
+				})
+			}
+		},
+		methods: {
+			itemType(item) {
+				if (item.jumpUrlType == 'goMiniApp') {
+					return '小程序'
+				}
+				if (item.jumpUrlType == 'goPage') {
+					return '子页面'
+				}
+				if (item.jumpUrlType == 'goWebView') {
+					return 'WEB'
+				}
+			},
+			onSearchChange(e) {
+				this.customerName = e.detail
+			},
+			onSearch(e) {
+				this.pageNum = 1
+				this.loading = true
+				getTypeAppList(this.listType, {
+					pageNum: this.pageNum,
+					pageSize: this.pageSize,
+					customerName: e.detail
+				}).then(res => {
+					this.applist = res.rows
+					this.total = res.total
+					this.$nextTick(() => {
+						this.loading = false
+					})
+				})
+			},
+			initAppList() {
+				typeList({
+					pid: 0
+				}).then(res => {
+					this.typeList = res.rows.slice(0, 8)
+				})
+				getTypeAppList(0, {
+					pageNum: 1,
+					pageSize: 10
+				}).then(res => {
+					this.applist = res.rows
+					this.total = res.total
+					this.$nextTick(() => {
+						this.loading = false
+					})
+				})
+			},
+			onBottom() {
+				if(this.bottomLoading) return
+				if (this.total - this.pageNum * this.pageSize <= 0) {
+					return
+				}
+				this.bottomLoading=true
+				this.pageNum++
+				getTypeAppList(this.listType, {
+					pageNum: this.pageNum,
+					pageSize: this.pageSize,
+					customerName: this.customerName
+				}).then(res => {
+					this.applist = this.applist.concat(res.rows)
+					this.$nextTick(()=>{
+						this.bottomLoading=false
+					})
+				})
+			},
+			typeMore() {
+				if (this.moreShow) {
+					typeList({
+						pid: 0
+					}).then(res => {
+						this.typeList = res.rows
+					})
+				} else {
+					this.typeList = this.typeList.slice(0, 8)
+				}
+			},
+			addSc(index) {
+				addFavorites(this.applist[index].id).then(res => {
+					if (res.code == 200) {
+						this.$set(this.applist[index], 'collectionStatus', '1')
+						this.Toast('收藏成功!');
+					}
+				})
+			},
+			delSc(index) {
+				delFavorites(this.applist[index].id).then(res => {
+					if (res.code == 200) {
+						this.$set(this.applist[index], 'collectionStatus', '0')
+						this.Toast('取消收藏成功!');
+					}
+				})
+			},
+			shareClose() {
+				this.showShare = false;
+			},
+			onSelect(event) {
+				this.Toast(event.detail.name);
+				this.onClose();
+			},
+			getAppList() {
+				featuredAppList({
+					pageNum: 1,
+					pageSize: 10
+				}).then(res => {
+					console.log(res)
+					this.applist = res.rows
+					this.$nextTick(() => {
+						this.loading = false
+					})
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.bottom-loading {
+		text-align: center;
+		padding: 10rpx;
+	}
+
+	.ad-box {
+		margin: 40rpx;
+		box-sizing: border-box;
+		display: flex;
+		justify-content: center !important;
+		align-items: center;
+	}
+
+	.content {
+		padding-bottom: 1rpx;
+		height: 100%;
+	}
+
+	.swiper-box {
+		width: 100vw;
+		height: 56.25vw;
+
+		.image {
+			width: 100%;
+			height: 100%;
+		}
+	}
+
+	.sticky {
+		position: sticky;
+		top: 0rpx;
+		left: 0;
+		background-color: #fff;
+		z-index: 9;
+		margin-bottom: 20rpx;
+
+		.grid {
+			display: flex;
+			flex-wrap: wrap;
+			justify-content: flex-start;
+			overflow: auto;
+
+			.grid-item {
+				flex-shrink: 0;
+				width: 20%;
+				height: 130rpx;
+				display: flex;
+				justify-content: center;
+				flex-direction: column;
+				align-items: center;
+				text-align: center;
+				font-size: 34rpx;
+				padding: 10rpx;
+				box-sizing: border-box;
+				position: relative;
+
+				.text {
+					font-size: .8em;
+					margin-top: 5rpx;
+					color: #999999;
+					font-weight: 400;
+				}
+			}
+
+			.border::after {
+				content: '';
+				position: absolute;
+				bottom: 5rpx;
+				width: 30rpx;
+				height: 5rpx;
+				background-color: red;
+			}
+		}
+	}
+
+	.list-item {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		background-color: #FFFFFF;
+		padding: 20rpx;
+		margin: 2rpx 0rpx;
+		position: relative;
+
+		.avatar {
+			flex-shrink: 0;
+			width: 100rpx;
+			height: 100rpx;
+			border-radius: 50%;
+			border: 1px solid #1989FA;
+			margin-right: 16rpx;
+		}
+
+		.top {
+			display: flex;
+			justify-content: flex-end;
+			position: absolute;
+			top: 10rpx;
+			right: 0;
+
+			.icon {
+				margin-top: -20rpx;
+				padding: 20rpx;
+			}
+		}
+
+		.item-right {
+			flex: 1;
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+			// height: 200rpx;
+
+
+
+			.center {
+				align-self: flex-start;
+
+				.details {
+					width: 436rpx;
+					// height: 70rpx;
+					font-size: 26rpx;
+					font-family: PingFang SC;
+					font-weight: 400;
+					color: #999999;
+					display: -webkit-box;
+					margin-bottom: 20rpx;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					word-wrap: break-word;
+					white-space: normal !important;
+					-webkit-line-clamp: 2;
+					-webkit-box-orient: vertical;
+				}
+			}
+
+
+			.bottom {
+				display: flex;
+				justify-content: flex-start;
+				align-items: center;
+				margin: 20rpx 0;
+
+				.title {
+					max-width: 300rpx;
+					white-space: nowrap;
+					text-overflow: ellipsis;
+					overflow: hidden;
+					word-break: break-all;
+					font-family: PingFang SC;
+					font-weight: 400;
+				}
+
+				.type {
+					margin-left: 10rpx;
+					padding: 3rpx 12rpx;
+					height: 30rpx;
+					border: 2rpx solid #999999;
+					border-radius: 20rpx;
+					font-size: 20rpx;
+					color: #999999;
+					line-height: 20rpx;
+					display: flex;
+					justify-content: center;
+					align-items: center;
+				}
+
+				.right {
+					width: 170rpx;
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					font-size: 22rpx;
+					font-family: PingFang SC;
+					font-weight: 400;
+					color: #999999;
+					line-height: 35rpx;
+
+					.flex {
+						display: flex;
+						justify-content: center;
+
+						.icon {
+							margin-right: 5rpx;
+						}
+					}
+				}
+			}
+		}
+	}
+</style>

+ 414 - 0
src/pages/index/components/cinema.vue

@@ -0,0 +1,414 @@
+<template>
+	<scroll-view scroll-y class="cinema">
+		<view class='topbar'>
+			<topbar>
+				<view class="topbar-box">
+					<view class="left-box" @click="goPage(`/pages/cinema/citylist?cityList=${JSON.stringify(cityList)}`,null,{cityList:cityList})">
+						<van-icon name="location" />
+						<text>{{address}}</text>
+						<van-icon name="arrow-down" />
+					</view>
+					<van-search class='top-search' shape="round" background="#fff" placeholder="请输入电影名称" @search="onSearch" @change='onSearchChange' />
+				</view>
+			</topbar>
+		</view>
+		<view class="tabs-box">
+			<van-tabs :active='tabActive' animated sticky offset-top='87' @change='tabsChange'>
+				<van-tab title="热映">
+					<loadSke :loading='hotLoading' :list='hotList'>
+						<view class="flex-box">
+							<view @click="goPage(`/pages/cinema/cinemalist?filmId=${item.filmId}&cityId=${cityId}&location=${location}`)"
+							 class="film-box" v-for="(item,index) in hotList" :key='item.filmId'>
+								<image :src="item.pic" mode="scaleToFill" class="img" draggable></image>
+								<view class="bottom-box">
+									<view class="tit">
+										<text class="text-1">{{item.name}}</text>
+										<text class="text-2">{{item.grade ?'评分'+item.grade : '暂无评分'}}</text>
+									</view>
+									<view class="des-box">
+										<view class="des">{{item.filmTypes}}</view>
+										<view class="des">{{item.cast}}</view>
+									</view>
+			
+									<button type="default" class="buy">
+										购 票
+									</button>
+								</view>
+							</view>
+						</view>
+					</loadSke>
+				</van-tab>
+				<van-tab title="影院">
+					<loadSke :loading='cinemaLoading' :list='cinemaList'>
+						<view class="cinema-box" v-for="(item,index) in cinemaList" :key='item.cinemaId' @click='goPage(`/pages/cinema/schedulelist?cinemaId=${item.cinemaId}&filmId=${filmId}`)'>
+							<view class="tit">
+								<text>{{item.cinemaName}}</text>
+								<text class="text-2">{{item.distance}}km</text>
+							</view>
+							<text class="address">{{item.address}}</text>
+						</view>
+					</loadSke>
+				</van-tab>
+				<van-tab title="即将上映">
+					<loadSke :loading='soonLoading'>
+						<view class="soon-box" v-for="(ObjItem,ObjIndex) in soonList" :key='ObjIndex'>
+							<view class="tit-text">
+								<van-icon class='icon' name="https://t1-1305573081.cos.ap-shanghai.myqcloud.com/wxapp/static/imgs/%E6%97%A5%E6%9C%9F.png" />
+								{{ObjIndex}}
+							</view>
+							<view class="flex-box">
+								<view class="film-box" v-for="(item,index) in ObjItem" :key='item.filmId'>
+									<image :src="item.pic" mode="scaleToFill" class="img" draggable></image>
+									<view class="bottom-box">
+										<view class="tit">
+											<text class="text-1">{{item.name}}</text>
+											<text class="text-2">{{item.grade ?'评分'+item.grade : '暂无评分'}}</text>
+										</view>
+										<view class="des-box">
+											<view class="des">{{item.filmTypes}}</view>
+											<view class="des">{{item.cast}}</view>
+										</view>
+									</view>
+								</view>
+							</view>
+						</view>
+					</loadSke>
+				</van-tab>
+			</van-tabs>
+		</view>
+		
+	</scroll-view>
+</template>
+
+
+<script>
+	import loadSke from '@/components/skeleton/index/index.vue'
+	import {
+		getPayData
+	} from '@/api/pay.js'
+	import {
+		amapRegeo
+	} from '@/api/amap.js'
+	import {
+		getHotList,
+		getSoonList,
+		getCityList,
+		getInfo,
+		getFilmDiscount,
+		getCinemaList
+	} from '@/api/cinema.js'
+	export default {
+		components: {
+			loadSke
+		},
+		data: () => ({
+			tabActive: 0,
+			hotList: [],
+			soonList: {},
+			hotLoading: true,
+			soonLoading: true,
+			location: [119.131390, 26.150210],
+			cityList: [],
+			address: '定位中',
+			cinemaList: {},
+			cinemaLoading: true
+		}),
+		async mounted() {
+			this.setFilmDiscount()
+			await this.getLocation()
+			this.address = this.$store.state.cinema.cityObj.regionName;
+			this.init()
+			this.cinemaListInit()
+		},
+		computed: {
+			cityId() {
+				return this.$store.state.cinema.cityId;
+			}
+		},
+		watch: {
+			cityId() {
+				this.address = this.$store.state.cinema.cityObj.regionName;
+				this.init()
+				this.cinemaListInit()
+			}
+		},
+		methods: {
+			onSearch(e){
+				console.log(e)
+			},
+			onSearchChange(e){
+				console.log(e)
+			},
+			tabsChange(e) {
+				this.tabActive = e.detail.index
+			},
+			setFilmDiscount() {
+				getFilmDiscount().then(res => {
+					this.$store.commit('SET_FILMDISCOUNT', res.data)
+				})
+			},
+			async getLocation() {
+				let location = await wx.getLocation()
+				this.location = [location.longitude.toFixed(6), location.latitude.toFixed(6)]
+				let address = await amapRegeo({
+					location: this.location.toString()
+				})
+				let cityListRes = await getCityList()
+				let cityList = cityListRes.data.data.list
+				let cityObj = {}
+				cityList.map((val) => {
+					cityObj[val.pinYin] || (cityObj[val.pinYin] = [])
+					cityObj[val.pinYin].push(val)
+					if (val.regionName == address.regeocode.addressComponent.city.slice(0, -1)) {
+						this.selectCity(val)
+					}
+				})
+				this.cityList = cityObj
+			},
+			async init() {
+				this.hotLoading = true
+				this.soonLoading = true
+				let hotListRes = await getHotList({
+					cityId: this.cityId
+				})
+				this.hotList = hotListRes.data.data.list
+				this.$nextTick(()=>{
+					this.hotLoading = false
+				})
+
+				let soonListRes = await getSoonList({
+					cityId: this.cityId
+				})
+				let soonListObj = {}
+				soonListRes.data.data.list.forEach((val) => {
+					soonListObj[val.publishDate.slice(0, 10)] || (soonListObj[val.publishDate.slice(0, 10)] = [])
+					soonListObj[val.publishDate.slice(0, 10)].push(val)
+				})
+				this.soonList = soonListObj
+				this.$store.commit('SET_FILMLIST', this.hotList.concat(soonListRes.data.data.list))
+				this.$nextTick(()=>{
+					this.soonLoading = false
+				})
+			},
+			async cinemaListInit() {
+				this.cinemaLoading = true
+				let showListRes = await getCinemaList({
+					cityId: this.cityId,
+					latitude: this.location[1],
+					longitude: this.location[0],
+				})
+				showListRes.data.data.list.map(val => {
+					val.distance = (Math.sqrt((val.latitude - this.location[1]) ** 2 + (val.longitude - this.location[0]) ** 2) *
+						111).toFixed(2)
+					return val
+				})
+				showListRes.data.data.list.sort(function(a, b) {
+					return a.distance - b.distance
+				})
+				this.cinemaList = showListRes.data.data.list
+				this.cinemaLoading = false
+			},
+			selectCity(obj) {
+				this.$store.commit('SET_CITYOBJ', obj)
+				this.$store.commit('SET_CITYID', obj.cityId)
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.cinema{
+		height: 100%;
+		position: relative;
+	}
+	
+	.topbar {
+		position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		z-index: 1;
+		.topbar-box {
+			display: flex;
+			align-items: center;
+		
+			.left-box {
+				min-width: 150rpx;
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				margin-left: 20rpx;
+				text{
+					white-space: nowrap;
+				}
+			}
+		}
+	}
+	
+	.tabs-box{
+		margin-top: 160rpx;
+	}
+
+	
+
+	.cinema-box {
+		padding: 30rpx;
+		border-top: 1rpx solid #E8E8E8;
+		background-color: #FFFFFF;
+		display: flex;
+		flex-direction: column;
+
+		.tit {
+			display: flex;
+			justify-content: space-between;
+
+			.text-1 {
+				font-size: 30rpx;
+				color: #0F0404;
+			}
+
+			.text-2 {
+				font-size: 26rpx;
+				color: #999999;
+				white-space: nowrap;
+			}
+		}
+
+		.address {
+			margin-top: 20rpx;
+			width: 600rpx;
+			font-size: 26rpx;
+			color: #666666;
+			white-space: nowrap;
+			overflow: hidden;
+			text-overflow: ellipsis;
+		}
+	}
+
+	.tab {
+		display: flex;
+		justify-content: space-around;
+		align-items: stretch;
+		background-color: #FFFFFF;
+		position: sticky;
+		top: 0;
+		left: 0;
+		z-index: 1;
+
+		.text {
+			padding: 30rpx 0;
+			font-size: 30rpx;
+			color: #0F0404;
+
+		}
+
+		.hover {
+			border-bottom: 6rpx solid red;
+		}
+
+		.location-box {
+			padding: 30rpx 0;
+			font-size: 30rpx;
+			color: #0F0404;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+		}
+
+	}
+
+	.flex-box {
+		display: flex;
+		justify-content: space-between;
+		flex-wrap: wrap;
+		background: #FFFFFF;
+		margin-top: 1rpx;
+		padding: 0rpx 20rpx;
+		padding-bottom: 20rpx;
+
+		.film-box {
+			width: 334rpx;
+			border-radius: 20rpx;
+			overflow: hidden;
+			background: #FFFFFF;
+			margin: 30rpx 11rpx 0;
+			box-shadow: 0px 2px 16px 0px rgba(68, 6, 6, 0.2);
+
+			.img {
+				width: 100%;
+				height: 456rpx;
+			}
+
+			.bottom-box {
+				padding: 30rpx 20rpx;
+
+				.tit {
+					display: flex;
+					justify-content: space-between;
+
+					.text-1 {
+						width: 200rpx;
+						font-size: 26rpx;
+						overflow: hidden;
+						text-overflow: ellipsis;
+						white-space: nowrap;
+					}
+
+					.text-2 {
+						font-size: 26rpx;
+						color: #ED4F24;
+						white-space: nowrap;
+					}
+				}
+
+				.des-box {
+					margin-top: 20rpx;
+
+					.des {
+						width: 100%;
+						font-size: 22rpx;
+						color: #666666;
+						overflow: hidden;
+						text-overflow: ellipsis;
+						white-space: nowrap;
+					}
+				}
+
+				.buy {
+					margin-top: 30rpx;
+					width: 126rpx;
+					height: 60rpx;
+					background: linear-gradient(90deg, #E31818, #ED3E24, #ED4F24);
+					border-radius: 30rpx;
+					font-size: 26rpx;
+					font-weight: 400;
+					color: #FFFFFF;
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					white-space: nowrap;
+				}
+			}
+
+		}
+
+	}
+
+	.soon-box {
+		margin-top: 20rpx;
+
+		.tit-text {
+			background-color: #FFF0E5;
+			padding: 28rpx 55rpx;
+			font-size: 26rpx;
+			font-weight: bold;
+			color: #0F0404;
+			display: flex;
+			align-items: center;
+
+			.icon {
+				margin-right: 20rpx;
+			}
+		}
+	}
+</style>

+ 438 - 0
src/pages/index/components/home.vue

@@ -0,0 +1,438 @@
+<template>
+	<scroll-view class="home" scroll-y @scrolltolower='onBottom'>
+		<!-- <van-tabs >
+		  <van-tab title="标签 1">内容 1</van-tab>
+		  <van-tab title="标签 2">内容 2</van-tab>
+		  <van-tab title="标签 3">内容 3</van-tab>
+		  <van-tab title="标签 4">内容 4</van-tab>
+		</van-tabs> -->
+		<view class="top-list">
+			<image class="background-img" style="width: 100vw;" src="https://t1-1305573081.cos.ap-shanghai.myqcloud.com/wxapp/static/imgs/%E9%A1%B6%E9%83%A8%E8%83%8C%E6%99%AF3.png"
+			 mode="widthFix"></image>
+			<topbar class="topbar-slot">
+				<view>
+					<text style="color: #FFFFFF;">极速生活圈</text>
+				</view>
+			</topbar>
+
+			<loadSke :loading='topLoading' :list='bannerList'>
+				<swiper class="swiper-box" :current="current" autoplay circular indicator-dots>
+					<swiper-item v-for="(item,index) in bannerList" :key='index'>
+						<image @click="clickJumpType(item)" class="image" :src="item.fileUrl" mode="scaleToFill" :draggable="false" />
+					</swiper-item>
+				</swiper>
+
+				<view class="eat-box">
+					<view class="eat" v-for="(item,index) in couponList" :key='index' @click="clickJumpType(item)">
+						<image :src="item.fileUrl" mode="scaleToFill" class="img"></image>
+						<view class="text-box">
+							<text class="text-1">{{item.pictureName}}</text>
+							<text class="text-2">{{item.dataDescribe.split(' ')[0]}}</text>
+							<text class="text-3">{{item.dataDescribe.split(' ')[1]}}</text>
+						</view>
+					</view>
+				</view>
+
+				<view class="menu-box">
+					<view class="menu" v-for="(item,index) in menuList" :key='index' @click="clickJumpType(item)">
+						<image :src="item.fileUrl" mode="scaleToFill" class="img"></image>
+						<text class="text-1">{{item.pictureName}}</text>
+						<text class="text-2">{{item.dataDescribe || '暂无描述'}}</text>
+					</view>
+				</view>
+			</loadSke>
+		</view>
+
+		<view class="bottom-ad" v-for="(item,index) in bottomAD" :key='index' @click="clickJumpType(item)">
+			<image style="width: 100%;" :src="item.fileUrl" mode="widthFix"></image>
+			<text>今日仅剩{{dynamicQuantity(index)}}个</text>
+			<button>免费领取</button>
+		</view>
+
+		<view class="item-box">
+			<view class="item-titBox">
+				<text class="item-tit">限时特价</text>
+				<text class="item-tit-right">更多类型 ></text>
+			</view>
+			<loadSke :loading='pddLoading' :list='pddList'>
+				<view class="flex-box">
+					<view class="film-box" v-for="(item,index) in pddList" :key='index' @click="goMiniApp(JSON.stringify({appId:item.we_app_info.app_id,path:item.we_app_info.page_path}))">
+						<image :src="item.goods_image_url" mode="widthFix" class="img" draggable></image>
+						<view class="bottom-box">
+							<view class="tit">
+								<text class="text-1">{{item.goods_name}}</text>
+							</view>
+							<view class="des-box">
+								<view class="des">{{item.unified_tags}}</view>
+							</view>
+							<view class="buy-box">
+								<text class="price">{{'¥'+(item.min_group_price)/100}}</text>
+								<button type="default" class="buy">活动价 {{'¥'+(item.min_group_price-item.coupon_discount)/100}}</button>
+							</view>
+						</view>
+					</view>
+					<view v-if="pddDataLoading" class="bottom-loading">
+						正在加载中...
+					</view>
+				</view>
+			</loadSke>
+		</view>
+	</scroll-view>
+</template>
+
+<script>
+	import {
+		getHomePageDataList,
+		getPddWxData
+	} from '@/api/home.js'
+	export default {
+		data: () => ({
+			bannerList: [],
+			couponList: [],
+			menuList: [],
+			bottomAD: [],
+			topLoading: true,
+			pddList: [],
+			pddLoading: true,
+			limit: 6,
+			page: 0,
+			pddDataLoading: false
+		}),
+		mounted() {
+			this.init()
+		},
+		methods: {
+			onBottom() {
+				if (this.pddDataLoading) return
+				this.pddDataLoading = true
+				this.page++
+				getPddWxData({
+					limit: this.limit,
+					offset: this.page * this.limit
+				}).then(res => {
+					this.pddList = this.pddList.concat(res)
+					this.$nextTick(() => {
+						this.pddLoading = false
+						this.pddDataLoading = false
+					})
+				})
+			},
+			dynamicQuantity(index) {
+				index++
+				let x = new Date().getHours() / 3
+				let num = ((x - 8) ** 2) + index * (8 - x) * 1 + index * 3 + 1
+				return parseInt(num)
+			},
+			init() {
+				getHomePageDataList().then(res => {
+					this.bannerList = res.data.carouselChart
+					this.couponList = res.data.coupon.slice(0, 4)
+					this.menuList = res.data.menu
+					this.bottomAD = res.data.bottomAD
+					this.$nextTick(() => {
+						this.topLoading = false
+					})
+				})
+				getPddWxData({
+					limit: this.limit,
+					offset: this.page * this.limit
+				}).then(res => {
+					this.pddList = res
+					this.$nextTick(() => {
+						this.pddLoading = false
+					})
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.home{
+		height: 100%;
+	}
+	
+	.top-list {
+		padding: 30rpx;
+		padding-top: 0;
+		box-sizing: border-box;
+		width: 100%;
+		background-color: #FFFFFF;
+		position: relative;
+
+		.topbar-slot {
+			z-index: 1;
+			align-self: center;
+			display: flex;
+			justify-content: center;
+		}
+
+		.background-img {
+			position: absolute;
+			top: 0;
+			left: 0;
+		}
+
+		.swiper-box {
+			width: 690rpx;
+			height: 342rpx;
+			overflow: hidden;
+
+			.image {
+				width: 100%;
+				height: 100%;
+				border-radius: 10px;
+			}
+		}
+
+		.eat-box {
+			margin-top: 30rpx;
+			display: flex;
+			justify-content: space-between;
+
+			.eat {
+				width: 158rpx;
+				height: 240rpx;
+				position: relative;
+
+				.img {
+					width: 100%;
+					height: 100%;
+				}
+
+				.text-box {
+					display: flex;
+					flex-direction: column;
+					justify-content: center;
+					align-items: center;
+					position: absolute;
+					bottom: 10rpx;
+					left: 50%;
+					width: 100%;
+					transform: translateX(-50%);
+
+					.text-1 {
+						font-size: 24rpx;
+						color: #0F0404;
+						text-align: center;
+					}
+
+					.text-2 {
+						margin-top: 5rpx;
+						font-size: 20rpx;
+						color: #FE3232;
+						text-align: center;
+					}
+
+					.text-3 {
+						font-size: 20rpx;
+						color: #666666;
+						text-align: center;
+					}
+				}
+
+			}
+		}
+
+		.menu-box {
+			display: flex;
+			justify-content: flex-start;
+			flex-wrap: wrap;
+			margin: 0 -10rpx;
+
+			.menu {
+				width: 20%;
+				padding: 0 10rpx;
+				box-sizing: border-box;
+				// height: 121rpx;
+				margin-top: 40rpx;
+				display: flex;
+				flex-direction: column;
+				justify-content: space-between;
+				align-items: center;
+
+				.img {
+					flex-shrink: 0;
+					width: 83rpx;
+					height: 83rpx;
+					border-radius: 50%;
+				}
+
+				.text-1 {
+					margin-top: 10rpx;
+					font-size: 24rpx;
+					color: #0F0404;
+					text-align: center;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+				}
+
+				.text-2 {
+					font-size: 20rpx;
+					color: #666666;
+					text-align: center;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+				}
+			}
+		}
+
+
+	}
+
+	.bottom-ad {
+		margin-top: -15rpx;
+		position: relative;
+
+		&:nth-child(2) {
+			margin-top: 20rpx;
+		}
+
+		text {
+			position: absolute;
+			top: 104rpx;
+			left: 220rpx;
+			padding: 10rpx 15rpx;
+			background: #FFE1D9;
+			font-size: 22rpx;
+			font-family: PingFang SC;
+			font-weight: 400;
+			color: #A40303;
+		}
+
+		button {
+			position: absolute;
+			top: 67rpx;
+			right: 40rpx;
+			display: inline-block;
+			margin: 0;
+			padding: 0rpx 26rpx;
+			background: linear-gradient(90deg, #E31818, #ED3E24, #ED4F24);
+			border-radius: 30rpx;
+			font-size: 26rpx;
+			font-family: PingFang SC;
+			font-weight: 400;
+			color: #FFFFFF;
+		}
+	}
+
+	.item-box {
+		background-color: #fff;
+		// margin-top: 40rpx;
+
+		.item-titBox {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			padding: 20rpx;
+			border-bottom: 1rpx solid #e8e8e8;
+
+			.item-tit {
+				border-left: 8rpx solid #E31818;
+				font-size: 30rpx;
+				padding: 11rpx;
+				margin-left: 20rpx;
+			}
+
+			.item-tit-right {
+				font-size: 26rpx;
+				color: #666666;
+				line-height: 58rpx;
+				padding: 11rpx;
+			}
+		}
+
+		.flex-box {
+			width: 100%;
+			display: flex;
+			justify-content: center;
+			align-items: flex-start;
+			// flex-direction: column;
+			flex-wrap: wrap;
+			background-color: #F8F8F8;
+			padding-bottom: 20rpx;
+
+			.film-box {
+				width: 334rpx;
+				border-radius: 20rpx;
+				overflow: hidden;
+				background: #FFFFFF;
+				margin: 30rpx 11rpx 0;
+
+				.img {
+					width: 100%;
+				}
+
+				.bottom-box {
+					padding: 30rpx 20rpx;
+
+					.tit {
+						display: flex;
+						justify-content: space-between;
+
+						.text-1 {
+							width: 100%;
+							font-size: 26rpx;
+							overflow: hidden;
+							text-overflow: ellipsis;
+							// white-space: nowrap;
+						}
+					}
+
+					.des-box {
+						margin-top: 10rpx;
+
+						.des {
+							width: 100%;
+							font-size: 22rpx;
+							color: #666666;
+							overflow: hidden;
+							text-overflow: ellipsis;
+							white-space: nowrap;
+						}
+					}
+
+					.buy-box {
+						margin-top: 20rpx;
+						display: flex;
+						justify-content: flex-start;
+						align-items: center;
+
+						.price {
+							// color: #E31818;
+							text-decoration: line-through;
+							font-size: 34rpx;
+						}
+
+						.buy {
+							height: 40rpx;
+							background: linear-gradient(90deg, #E31818, #ED3E24, #ED4F24);
+							border-radius: 20rpx;
+							margin-left: 10rpx;
+							font-size: 20rpx;
+							color: #FFFFFF;
+							display: flex;
+							justify-content: center;
+							align-items: center;
+							white-space: nowrap;
+						}
+					}
+
+				}
+
+			}
+
+		}
+
+		.bottom-loading{
+			margin-top: 5rpx;
+			padding: 10rpx;
+		}
+	}
+
+	.ad-video {
+		margin-top: 40rpx;
+	}
+</style>

+ 82 - 0
src/pages/index/components/login.vue

@@ -0,0 +1,82 @@
+<template>
+	<view class="login">
+		<loadSke :loading='getUserInfo'>
+			<view class="user-info">
+				<view class="avatar">
+					<open-data type="userAvatarUrl"></open-data>
+				</view>
+				<view class="nickname">
+					<open-data type="userNickName"></open-data>
+				</view>
+				<button open-type="getUserProfile" @tap="getUserProfile">授权用户头像信息</button>
+			</view>
+		</loadSke>
+	</view>
+</template>
+
+<script>
+	import loadSke from '@/components/skeleton/login.vue'
+	export default {
+		components: {
+			loadSke
+		},
+		props: ['login'],
+		data: () => ({
+			getUserInfo: true
+		}),
+		async created() {
+			this.init()
+		},
+		methods: {
+			async init() {
+				if (true) {
+					let loginRes = await this.$store.dispatch('Login')
+					if (loginRes.code == 200) {
+						this.$emit('update:login', true)
+					}else{
+						this.Toast('登录失败');
+						this.$emit('update:login', true)
+					}
+				} else {
+					this.getUserInfo = false
+				}
+			},
+			getUserProfile: function(e) {
+				wx.getUserProfile({
+					desc: '业务需要',
+					success: res => {
+						console.log(res)
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.login {
+		width: 100vw;
+		height: 80vh;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+
+		.user-info {
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			align-items: center;
+
+			.avatar {
+				border-radius: 50%;
+				width: 100px;
+				height: 100px;
+				overflow: hidden;
+			}
+
+			.nickname {
+				text-align: center;
+			}
+		}
+	}
+</style>

+ 288 - 0
src/pages/index/components/user.vue

@@ -0,0 +1,288 @@
+<template>
+	<scroll-view scroll-y class="content">
+		<view class="top">
+			<view class="avabox">
+				<van-image round class='img' width="200rpx" height="200rpx" :src="userData.headImage" />
+				<view class="avatitbox">
+					<text>{{userData.nickName}}</text>
+					<text v-if="userData.gzptUserId">{{studentInfo.logincode}}</text>
+					<view class="sync-btn" @tap='getUserProfile'>点击同步用户头像昵称</view>
+					<!-- <text v-else @click="idCardShow=!idCardShow">未绑定身份证点击绑定</text> -->
+				</view>
+			</view>
+		</view>
+
+		<view class="serviceBox">
+			<view class="tit">
+				<text>我的服务</text>
+			</view>
+			<view class="btnBox">
+				<button class="item-box" :plain='true' @click="goPage('/pages/user/order')">
+					<van-icon name="/static/imgs/order-icon.png" size="22px" />
+					<text class="text">我的订单</text>
+				</button>
+				<button class="item-box" :plain='true' @click="goPage('/pages/user/collectionList')">
+					<van-icon name="/static/imgs/wdsc.png" size="22px" />
+					<text class="text">我的收藏</text>
+				</button>
+				<button class="item-box" :plain='true' @click="goPage('/pages/user/browserecord')">
+					<van-icon name="/static/imgs/llzj.png" size="22px" />
+					<text class="text">浏览足迹</text>
+				</button>
+				<button class="item-box" :plain='true' open-type='feedback'>
+					<van-icon name="/static/imgs/yjfk.png" size="22px" />
+					<text class="text">意见反馈</text>
+				</button>
+			</view>
+
+		</view>
+
+		<view class="serviceBox">
+			<view class="tit">
+				<text>我的信息</text>
+			</view>
+			<van-cell title="我的设置" icon="/static/imgs/wdsz.png" is-link @tap="goPage('/pages/user/set')" />
+			<button class="server" open-type="contact" :plain='true'>
+				<van-cell title="联系客服" icon="/static/imgs/lxkf.png" is-link />
+			</button>
+
+		</view>
+		
+		<view class="ad-box serviceBox">
+			<ad unit-id="adunit-d8c1548cc9663765"></ad>
+		</view>
+		
+		<van-popup :show="idCardShow" round>
+			<view class="idCard">
+				<van-divider contentPosition="center">绑定身份证信息</van-divider>
+				<van-cell-group>
+					<van-field :value="idCard" @change='idCardput' label="身份证" maxlength='18' placeholder="请输入身份证号" />
+					<van-field :value="password" @change='passwordput' type="password" label="密码" placeholder="请输入密码" />
+					<van-cell title="归属驾校地区" :value="area" @click='popup=!popup' />
+				</van-cell-group>
+				<view class="btn-box">
+					<van-button @click='idCardShow=!idCardShow' type="default">取消绑定</van-button>
+					<van-button @click='submitBinding' type="info" :loading='loading' loading-text="绑定中..">确认绑定</van-button>
+				</view>
+			</view>
+		</van-popup>
+		<van-popup :show="popup" position="bottom">
+			<van-area :columns-placeholder="['请选择', '请选择']" value="35" @cancel='popup=!popup' @confirm='popup=!popup' :area-list="areaList"
+			 @change='regionSelection' columns-num='2' />
+		</van-popup>
+	</scroll-view>
+</template>
+
+<script>
+	import square from '@/components/square/index.vue'
+	import md5 from 'crypto-js/md5'
+	import Toast from '@/wxcomponents/vant/toast/toast';
+	import {
+		login,
+		getInfo,
+		bindUserCard,
+		updateUserInfo
+	} from '@/api/login.js'
+	import {
+		getStudentInfo
+	} from '@/api/studytime.js'
+	export default {
+		components: {
+			square
+		},
+		data() {
+			return {
+				loading: false,
+				idCardShow: false,
+				popup: false,
+				idCard: null,
+				password: null,
+				areaCode: null,
+				area: "请点击选择地区",
+				areaList: {
+					province_list: {
+						35: '福建省',
+					},
+					city_list: {
+						3501: '福州市',
+						3502: '厦门市',
+						3503: '莆田市',
+						3504: '三明市',
+						3505: '泉州市',
+						3506: '漳州市',
+						3507: '南平市',
+						3508: '龙岩市',
+						3509: '宁德市',
+					}
+				}
+			}
+		},
+		computed: {
+			userData() {
+				return this.$store.state.user.userInfo;
+			}
+		},
+		mounted() {
+			this.initUserInfo()
+		},
+		methods: {
+			async initUserInfo() {
+				await this.$store.dispatch('GetInfo')
+			},
+			idCardput(e) {
+				this.idCard = e.detail
+			},
+			passwordput(e) {
+				this.password = e.detail
+			},
+			submitBinding() {
+				console.log(this.areaCode, this.idCard, this.password)
+				this.loading = true
+				bindUserCard({
+					city: this.areaCode,
+					logincode: this.idCard,
+					password: md5(this.password).toString()
+				}).then((res) => {
+					if (res.code == 200) {
+						Toast.success('绑定成功');
+						this.idCardShow = false
+					} else if (res.code == 502) {
+						Toast.fail(res.msg);
+					} else {
+						Toast.fail('系统内部错误');
+					}
+					this.loading = false
+				})
+			},
+			regionSelection(e) {
+				console.log(e)
+				this.area = `${e.detail.values[0].name}  ${e.detail.values[1].name}`
+				this.areaCode = e.detail.values[1].code
+			},
+			async getUserProfile(e) {
+				let userInfo = await wx.getUserProfile({
+					desc: '业务需要'
+				})
+				let updateRes = await updateUserInfo({
+					headImage: userInfo.userInfo.avatarUrl,
+					nickName: userInfo.userInfo.nickName
+				})
+				if (updateRes.code == 200) {
+					// this.$store.dispatch('GetInfo')
+					this.$store.state.user.userInfo.headImage=userInfo.userInfo.avatarUrl
+					this.$store.state.user.userInfo.nickName=userInfo.userInfo.nickName
+				}
+			}
+		}
+
+	}
+</script>
+
+<style lang="scss" scoped>
+	.ad-box{
+		margin: 40rpx;
+		padding: 20rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+	
+	.server {
+		border: none;
+		background-color: none;
+		margin: 0;
+		padding: 0;
+		text-align: left;
+		display: flexbox;
+	}
+
+	.idCard {
+		width: 600rpx;
+		padding: 30rpx;
+		margin: auto;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.btn-box {
+		margin-top: 40rpx;
+		width: 100%;
+		display: flex;
+		justify-content: space-between;
+	}
+
+	.content {
+		height: 100%;
+	}
+
+	.top {
+		padding-top: 100rpx;
+		background-image: url("/static/imgs/bg.png");
+		background-size: 100% 100%;
+		background-repeat: no-repeat;
+
+		.avabox {
+			display: flex;
+			padding: 10%;
+
+			.img {
+				border-radius: 50%;
+			}
+
+			.sync-btn {
+				margin-top: 20rpx;
+				height: 40rpx;
+			}
+
+			.avatitbox {
+				flex: 1;
+				display: flex;
+				flex-direction: column;
+				justify-content: space-between;
+				padding: 30rpx;
+				color: #FFFFFF;
+				height: 100rpx;
+			}
+		}
+	}
+
+
+
+
+	.serviceBox {
+		background-color: #fff;
+		margin: 30rpx;
+		border-radius: 10rpx;
+
+		.tit {
+			padding: 26rpx;
+			border-bottom: 1rpx solid #E8E8E8;
+		}
+
+		.btnBox {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+
+			.item-box {
+				display: flex;
+				flex-direction: column;
+				justify-content: space-between;
+				align-items: center;
+				width: 200rpx;
+				margin-top: 20rpx;
+				padding: 20rpx;
+				border: none;
+
+				.text {
+					font-size: 26rpx;
+					font-family: PingFang SC;
+					font-weight: 400;
+					color: #666666;
+				}
+			}
+		}
+	}
+</style>

+ 120 - 30
src/pages/index/index.vue

@@ -1,49 +1,139 @@
 <template>
-	<view class="content">
-		<image class="logo" src="/static/logo.png"></image>
-		<view>
-			<text class="title">{{title}}</text>
-		</view>
+	<page-meta>
+		<navigation-bar :title="title" background-color="#FFFFFF" front-color="#000000" />
+	</page-meta>
+	<view v-if="!login">
+		<login :login.sync='login' />
 	</view>
+	<view v-else>
+		
+		<swiper class="swiper-box" :current-item-id="active" @change='swiperChange' easing-function="easeInCubic">
+			<swiper-item class="swiper-item" item-id='home'>
+				<home v-if="activeObj.home" ref='home' />
+			</swiper-item>
+			<swiper-item class="swiper-item" item-id='cinema'>
+				<cinema v-if="activeObj.cinema" ref='cinema' />
+			</swiper-item>
+			<swiper-item class="swiper-item" item-id='applist'>
+				<applist v-if="activeObj.applist" ref='applist' />
+			</swiper-item>
+			<swiper-item class="swiper-item" item-id='user'>
+				<user v-if="activeObj.user" ref='user' />
+			</swiper-item>
+		</swiper>
+
+		<!-- <home v-if="activeObj.home" v-show="active=='home'" ref='home' />
+		<cinema v-if="activeObj.cinema" v-show="active=='cinema'" ref='cinema'/>
+		<applist v-if="activeObj.applist" v-show="active=='applist'" ref='applist' />
+		<user v-if="activeObj.user" v-show="active=='user'"  ref='user' /> -->
+
+		<van-tabbar :active="active" @change="onChange" :placeholder='true' active-color="#E31818" inactive-color="#999999">
+			<van-tabbar-item name='home' icon='wap-home'>
+				<!-- <van-icon slot="icon" name="/static/imgs/fxh.png" /> -->
+				<!-- <van-icon slot="icon-active" name="/static/imgs/fx.png" /> -->
+				首页
+			</van-tabbar-item>
+			<van-tabbar-item name='cinema' icon='video'>
+				<!-- <van-icon slot="icon" name="/static/imgs/fxh.png" /> -->
+				<!-- <van-icon slot="icon-active" name="/static/imgs/fx.png" /> -->
+				电影
+			</van-tabbar-item>
+			<van-tabbar-item name='applist' icon='search'>
+				<!-- <van-icon slot="icon" name="/static/imgs/zth.png" /> -->
+				<!-- <van-icon slot="icon-active" name="/static/imgs/zt.png" /> -->
+				发现
+			</van-tabbar-item>
+			<van-tabbar-item name='user' icon='manager'>
+				<!-- <van-icon slot="icon" name="/static/imgs/wdh.png" /> -->
+				<!-- <van-icon slot="icon-active" name="/static/imgs/wd.png" /> -->
+				我的
+			</van-tabbar-item>
+		</van-tabbar>
+	</view>
+
+
+	<van-toast id="van-toast" />
 </template>
 
 <script>
+	import applist from './components/applist.vue'
+	import user from './components/user.vue'
+	import login from './components/login.vue'
+	import home from './components/home.vue'
+	import cinema from './components/cinema.vue'
 	export default {
+		components: {
+			applist,
+			user,
+			login,
+			home,
+			cinema
+		},
 		data() {
 			return {
-				title: 'Hello'
+				login: false,
+				active: 'home',
+				activeObj: {},
 			}
 		},
-		onLoad() {
-
+		onLoad: function(option) {
+			if (option.active) {
+				this.active = option.active
+			}
+			this.activeObj[this.active] = true
+		},
+		onShareAppMessage: function() {},
+		onReachBottom() {
+			if (this.active == 'home') {
+				this.$refs.home.onBottom()
+			}
+			if (this.active == 'applist') {
+				this.$refs.applist.onBottom()
+			}
+		},
+		computed: {
+			title() {
+				if (!this.login) return '登录'
+				switch (this.active) {
+					case 'home':
+						return '首页';
+					case 'cinema':
+						return '电影';
+					case 'applist':
+						return '发现';
+					case 'user':
+						return '我的';
+				}
+			}
 		},
 		methods: {
-
+			onChange(event) {
+				// event.detail 的值为当前选中项的索引
+				this.active = event.detail
+				this.activeObj[this.active] = true
+				// wx.startPullDownRefresh()
+				// this.$nextTick(function() {
+				// 	wx.stopPullDownRefresh()
+				// })
+			},
+			swiperChange(event){
+				this.active = event.detail.currentItemId
+				this.activeObj[this.active] = true
+				// wx.startPullDownRefresh()
+				// this.$nextTick(function() {
+				// 	wx.stopPullDownRefresh()
+				// })
+			}
 		}
 	}
 </script>
 
-<style>
-	.content {
-		display: flex;
-		flex-direction: column;
-		align-items: center;
-		justify-content: center;
-	}
-
-	.logo {
-		height: 200rpx;
-		width: 200rpx;
-		margin: 200rpx auto 50rpx auto;
-	}
+<style lang="scss">
+	.swiper-box {
+		height: calc(100vh - 100rpx - env(safe-area-inset-bottom));
 
-	.text-area {
-		display: flex;
-		justify-content: center;
-	}
-
-	.title {
-		font-size: 36rpx;
-		color: #8f8f94;
+		.swiper-item {
+			// overflow: auto;
+		}
 	}
 </style>

+ 250 - 0
src/pages/user/browserecord.vue

@@ -0,0 +1,250 @@
+<template>
+	<view class="content">
+		<loadSke :loading='loading' :list='applist'>
+			<view class='list-item' v-for="(item, index) in applist" :key="index" @click="goMiniApp({appId:item.appletAddress},item)">
+				<image class="avatar" mode="aspectFill" :src="item.appletLogoFileUrl || '/static/imgs/shmr.png'">
+					<view class="item-right">
+						<view class="top">
+							<van-icon class="icon" name="/static/imgs/zf.png" size='14' />
+							<van-icon class="icon" name="/static/imgs/bxh.png" @tap.stop='delSc(index)' size='12' />
+						</view>
+						<view class="center">
+							<text class="details">{{item.appletIntroduce || '该商家暂无描述,敬请期待!'}}</text>
+						</view>
+						<view class="bottom">
+							<text class="title">{{item.corporateName}}</text>
+						</view>
+					</view>
+			</view>
+		</loadSke>
+	</view>
+
+</template>
+
+<script>
+	import loadSke from '@/components/skeleton/index/index.vue'
+	import {
+		getBrowseRecordInfoList,
+		BrowseRecordDel
+	} from '@/api/applist.js'
+	export default {
+		components:{
+			loadSke
+		},
+		data() {
+			return {
+				active: 0,
+				current: 0,
+				mode: 'round',
+				applist: {},
+				loading:true,
+				pageNum: 1,
+				pageSize: 10,
+				total: 1,
+			}
+		},
+		mounted() {
+			this.initAppList()
+		},
+		onReachBottom(){
+			if(this.total-this.pageNum*this.pageSize<=0){
+				return
+			}
+			this.pageNum++
+			getBrowseRecordInfoList({
+				pageNum: this.pageNum,
+				pageSize: this.pageSize
+			}).then(res => {
+				this.applist = this.applist.concat(res.rows)
+			})
+		},
+		methods: {
+			initAppList() {
+				getBrowseRecordInfoList({
+					pageNum: this.pageNum,
+					pageSize: this.pageSize
+				}).then(res => {
+					this.applist = res.rows
+					this.total = res.total
+					this.$nextTick(()=>{
+						this.loading=false
+					})
+				})
+			},
+			delSc(index) {
+				BrowseRecordDel(this.applist[index].id).then(res => {
+					if (res.code == 200) {
+						this.$set(this.applist[index], 'collectionStatus', '0')
+						this.Toast('取消收藏成功!');
+						delete this.applist.splice(index, 1)
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.content {
+		padding-bottom: 1rpx;
+	}
+
+	.swiper-box {
+		position: sticky;
+		top: 0px;
+		left: 0;
+		height: 422rpx;
+		z-index: 9;
+	}
+
+	.sticky-top {
+		position: sticky;
+		top: 192rpx;
+		left: 0;
+		margin: 30rpx 30rpx;
+		border-radius: 20rpx;
+		overflow: hidden;
+		background-color: #4fc08d;
+		z-index: 10;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		// border-top-left-radius: 14px;
+		// border-top-right-radius: 14px;
+		padding-right: 3%;
+
+		.top-search {
+			flex: 1;
+		}
+	}
+
+	.sticky {
+		position: sticky;
+		top: 295rpx;
+		left: 0;
+		background-color: #fff;
+		z-index: 9;
+		margin: 30rpx 30rpx;
+		border-radius: 20rpx;
+
+		.grid {
+			display: flex;
+			justify-content: space-between;
+			padding: 1% 2%;
+			margin-bottom: 2%;
+			border-bottom-left-radius: 10px;
+			border-bottom-right-radius: 10px;
+
+			.grid-item {
+				width: 50px;
+				height: 60px;
+				display: flex;
+				justify-content: center;
+				flex-direction: column;
+				align-items: center;
+				font-size: 12px;
+			}
+		}
+	}
+
+	.list-item {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		background-color: #FFFFFF;
+		height: 170rpx;
+		padding: 34rpx 20rpx;
+		margin: 24rpx 30rpx;
+		border-radius: 20rpx;
+
+		.avatar {
+			flex-shrink: 0;
+			width: 161rpx;
+			height: 161rpx;
+			margin-right: 16rpx;
+		}
+
+		.item-right {
+			flex: 1;
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+			height: 200rpx;
+
+			.top {
+				.icon {
+					margin-top: -20rpx;
+					padding: 20rpx;
+				}
+
+				display: flex;
+				justify-content: flex-end;
+			}
+
+			.center {
+				align-self: flex-start;
+
+				.details {
+					width: 436rpx;
+					height: 70rpx;
+					font-size: 26rpx;
+					font-family: PingFang SC;
+					font-weight: 400;
+					color: #0F0404;
+					display: -webkit-box;
+					margin-bottom: 20rpx;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					word-wrap: break-word;
+					white-space: normal !important;
+					-webkit-line-clamp: 2;
+					-webkit-box-orient: vertical;
+				}
+			}
+
+
+			.bottom {
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+
+				.title {
+					padding: 3rpx 12rpx;
+					height: 30rpx;
+					background: #FFE6E6;
+					border: 1rpx solid #E31818;
+					border-radius: 2rpx;
+					font-size: 20rpx;
+					font-family: PingFang SC;
+					font-weight: 400;
+					color: #E31818;
+					line-height: 20rpx;
+					display: flex;
+					justify-content: center;
+					align-items: center;
+				}
+
+				.right {
+					width: 170rpx;
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					font-size: 22rpx;
+					font-family: PingFang SC;
+					font-weight: 400;
+					color: #999999;
+					line-height: 35rpx;
+
+					.flex {
+						display: flex;
+						justify-content: center;
+
+						.icon {
+							margin-right: 5rpx;
+						}
+					}
+				}
+			}
+		}
+	}
+</style>

+ 250 - 0
src/pages/user/collectionList.vue

@@ -0,0 +1,250 @@
+<template>
+	<view class="content">
+		<loadSke :loading='loading' :list='applist'>
+			<view class='list-item' v-for="(item, index) in applist" :key="index" @click="goMiniApp({appId:item.appletAddress},item)">
+				<image class="avatar" mode="aspectFill" :src="item.appletLogoFileUrl || '/static/imgs/shmr.png'">
+					<view class="item-right">
+						<view class="top">
+							<van-icon class="icon" name="/static/imgs/zf.png" size='14' />
+							<van-icon class="icon" name="/static/imgs/bxh.png" @tap.stop='delSc(index)' size='12' />
+						</view>
+						<view class="center">
+							<text class="details">{{item.appletIntroduce || '该商家暂无描述,敬请期待!'}}</text>
+						</view>
+						<view class="bottom">
+							<text class="title">{{item.corporateName}}</text>
+						</view>
+					</view>
+			</view>
+		</loadSke>
+	</view>
+
+</template>
+
+<script>
+	import loadSke from '@/components/skeleton/index/index.vue'
+	import {
+		collectionList,
+		delFavorites
+	} from '@/api/applist.js'
+	export default {
+		components: {
+			loadSke
+		},
+		data() {
+			return {
+				active: 0,
+				current: 0,
+				mode: 'round',
+				applist: {},
+				loading: true,
+				pageNum: 1,
+				pageSize: 10,
+				total: 1,
+			}
+		},
+		mounted() {
+			this.initAppList()
+		},
+		onReachBottom(){
+			if(this.total-this.pageNum*this.pageSize<=0){
+				return
+			}
+			this.pageNum++
+			collectionList({
+				pageNum: this.pageNum,
+				pageSize: this.pageSize
+			}).then(res => {
+				this.applist = this.applist.concat(res.rows)
+			})
+		},
+		methods: {
+			initAppList() {
+				collectionList({
+					pageNum: this.pageNum,
+					pageSize: this.pageSize
+				}).then(res => {
+					this.applist = res.rows
+					this.total = res.total
+					this.$nextTick(() => {
+						this.loading = false
+					})
+				})
+			},
+			delSc(index) {
+				delFavorites(this.applist[index].customerId).then(res => {
+					if (res.code == 200) {
+						this.$set(this.applist[index], 'collectionStatus', '0')
+						this.Toast('取消收藏成功!');
+						delete this.applist.splice(index, 1)
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.content {
+		padding-bottom: 1rpx;
+	}
+
+	.swiper-box {
+		position: sticky;
+		top: 0px;
+		left: 0;
+		height: 422rpx;
+		z-index: 9;
+	}
+
+	.sticky-top {
+		position: sticky;
+		top: 192rpx;
+		left: 0;
+		margin: 30rpx 30rpx;
+		border-radius: 20rpx;
+		overflow: hidden;
+		background-color: #4fc08d;
+		z-index: 10;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		// border-top-left-radius: 14px;
+		// border-top-right-radius: 14px;
+		padding-right: 3%;
+
+		.top-search {
+			flex: 1;
+		}
+	}
+
+	.sticky {
+		position: sticky;
+		top: 295rpx;
+		left: 0;
+		background-color: #fff;
+		z-index: 9;
+		margin: 30rpx 30rpx;
+		border-radius: 20rpx;
+
+		.grid {
+			display: flex;
+			justify-content: space-between;
+			padding: 1% 2%;
+			margin-bottom: 2%;
+			border-bottom-left-radius: 10px;
+			border-bottom-right-radius: 10px;
+
+			.grid-item {
+				width: 50px;
+				height: 60px;
+				display: flex;
+				justify-content: center;
+				flex-direction: column;
+				align-items: center;
+				font-size: 12px;
+			}
+		}
+	}
+
+	.list-item {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		background-color: #FFFFFF;
+		height: 170rpx;
+		padding: 34rpx 20rpx;
+		margin: 24rpx 30rpx;
+		border-radius: 20rpx;
+
+		.avatar {
+			flex-shrink: 0;
+			width: 161rpx;
+			height: 161rpx;
+			margin-right: 16rpx;
+		}
+
+		.item-right {
+			flex: 1;
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+			height: 200rpx;
+
+			.top {
+				.icon {
+					margin-top: -20rpx;
+					padding: 20rpx;
+				}
+
+				display: flex;
+				justify-content: flex-end;
+			}
+
+			.center {
+				align-self: flex-start;
+
+				.details {
+					width: 436rpx;
+					height: 70rpx;
+					font-size: 26rpx;
+					font-family: PingFang SC;
+					font-weight: 400;
+					color: #0F0404;
+					display: -webkit-box;
+					margin-bottom: 20rpx;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					word-wrap: break-word;
+					white-space: normal !important;
+					-webkit-line-clamp: 2;
+					-webkit-box-orient: vertical;
+				}
+			}
+
+
+			.bottom {
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+
+				.title {
+					padding: 3rpx 12rpx;
+					height: 30rpx;
+					background: #FFE6E6;
+					border: 1rpx solid #E31818;
+					border-radius: 2rpx;
+					font-size: 20rpx;
+					font-family: PingFang SC;
+					font-weight: 400;
+					color: #E31818;
+					line-height: 20rpx;
+					display: flex;
+					justify-content: center;
+					align-items: center;
+				}
+
+				.right {
+					width: 170rpx;
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					font-size: 22rpx;
+					font-family: PingFang SC;
+					font-weight: 400;
+					color: #999999;
+					line-height: 35rpx;
+
+					.flex {
+						display: flex;
+						justify-content: center;
+
+						.icon {
+							margin-right: 5rpx;
+						}
+					}
+				}
+			}
+		}
+	}
+</style>

+ 134 - 0
src/pages/user/order.vue

@@ -0,0 +1,134 @@
+<template>
+	<view>
+		<van-tabs :active="active" @change="onChange" animated sticky>
+			<van-tab title="近7日订单">
+				<loadSke :loading='orderLoading'>
+					<view class="order-item" v-for="(item,index) in orderList" :key='item' @click="goPage(`/pages/cinema/orderdes?outTradeNo=${item.outTradeNo}`)">
+						<image class="img" :src="item.goodsPictureUrl" mode="widthFix"></image>
+						<view class="des-box">
+							<text>订单名称:{{item.orderDataJson.cinemaData.filmName}}</text>
+							<text>订单日期:{{item.createTime}}</text>
+							<text> </text>
+							<text>订单价格:{{item.total/100}}</text>
+						</view>
+						<van-icon class='icon' name="arrow" />
+					</view>
+				</loadSke>
+			</van-tab>
+			<van-tab title="历史订单">
+				<loadSke :loading='orderLoading2'>
+					<view class="order-item ios-bottom" v-for="(item,index) in orderList2" :key='item' @click="goPage(`/pages/cinema/orderdes?outTradeNo=${item.outTradeNo}`)">
+						<image class="img" :src="item.goodsPictureUrl" mode="widthFix"></image>
+						<view class="des-box">
+							<text>订单名称:{{item.orderDataJson.cinemaData.filmName}}</text>
+							<text>订单日期:{{item.createTime}}</text>
+							<text> </text>
+							<text>订单价格:{{item.total/100}}</text>
+						</view>
+						<van-icon class='icon' name="arrow" />
+					</view>
+				</loadSke>
+			</van-tab>
+		</van-tabs>
+	</view>
+</template>
+
+<script>
+	import {
+		getOrderList
+	} from '@/api/order.js'
+	import {
+		orderQuery
+	} from '@/api/cinema.js'
+	export default {
+		data: () => ({
+			active: 0,
+			orderLoading: true,
+			orderLoading2: true,
+			orderList: {},
+			orderList2: {}
+		}),
+		mounted() {
+			this.init()
+			this.init2()
+			orderQuery({
+				thirdOrderId: '12021053117040103901269463601171'
+			})
+		},
+		methods: {
+			async init() {
+				let orderListRes = await getOrderList({
+					days: 7,
+					pageNum: 1,
+					pageSize: 10
+				})
+				orderListRes.rows.map(val => {
+					val.orderDataJson = JSON.parse(val.orderDataJson)
+					return val
+				})
+				this.orderList = orderListRes.rows
+				this.orderLoading = false
+			},
+			async init2() {
+				let orderListRes = await getOrderList({
+					pageNum: 1,
+					pageSize: 10
+				})
+				orderListRes.rows.map(val => {
+					val.orderDataJson = JSON.parse(val.orderDataJson)
+					return val
+				})
+				this.orderList2 = orderListRes.rows
+				this.orderLoading2 = false
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.ios-bottom{
+		padding-bottom: calc(10px + env(safe-area-inset-bottom)/2);
+	}
+	.order-item {
+		background-color: #FFFFFF;
+		margin-top: 10rpx;
+		padding: 30rpx;
+		display: flex;
+		justify-content: flex-start;
+		align-items: stretch;
+		position: relative;
+
+		&:nth-child(1) {
+			margin-top: 20rpx;
+		}
+
+		.img {
+			width: 112rpx;
+			height: 156rpx;
+			margin-right: 50rpx;
+		}
+
+		.des-box {
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+
+			text {
+				font-size: 26rpx;
+				font-weight: 400;
+				color: #666666;
+			}
+
+			text:nth-child(2) {
+				color: #0F0404 !important;
+			}
+		}
+
+		.icon {
+			position: absolute;
+			right: 30rpx;
+			top: 50%;
+			transform: translateY(-50%);
+		}
+	}
+</style>

+ 118 - 0
src/pages/user/set.vue

@@ -0,0 +1,118 @@
+<template>
+	<view class="">
+		<van-cell center title="我的昵称" :value="userData.nickName"/>
+		<van-cell title="我的头像" center>
+		  <image class="image-ava" slot="right-icon" :src="userData.headImage">
+		</van-cell>
+		<van-popup :show="idCardShow" round>
+			<view class="idCard">
+				<van-divider contentPosition="center">绑定身份证信息</van-divider>
+				<van-cell-group>
+					<van-field :value="idCard" @change='idCardput' label="身份证" maxlength='18' placeholder="请输入身份证号" />
+					<van-field :value="password" @change='passwordput' type="password" label="密码" placeholder="请输入密码" />
+					<van-cell title="归属驾校地区" :value="area" @click='popup=!popup' />
+				</van-cell-group>
+				<view class="btn-box">
+					<van-button @click='idCardShow=!idCardShow' type="default">取消绑定</van-button>
+					<van-button @click='submitBinding' type="info" :loading='loading' loading-text="绑定中..">确认绑定</van-button>
+				</view>
+			</view>
+		</van-popup>
+		<van-popup :show="popup" position="bottom">
+			<van-area :columns-placeholder="['请选择', '请选择']" value="35" @cancel='popup=!popup' @confirm='popup=!popup' :area-list="areaList"
+			 @change='regionSelection' columns-num='2' />
+		</van-popup>
+	</view>
+</template>
+
+<script>
+	import md5 from 'crypto-js/md5'
+	import {
+		login,
+		getInfo,
+		bindUserCard
+	} from '@/api/login.js'
+	export default {
+		data() {
+			return {
+				loading: false,
+				idCardShow: false,
+				popup: false,
+				idCard: null,
+				password: null,
+				areaCode: null,
+				area: "请点击选择地区",
+				areaList: {
+					province_list: {
+						35: '福建省',
+					},
+					city_list: {
+						3501: '福州市',
+						3502: '厦门市',
+						3503: '莆田市',
+						3504: '三明市',
+						3505: '泉州市',
+						3506: '漳州市',
+						3507: '南平市',
+						3508: '龙岩市',
+						3509: '宁德市',
+					}
+				}
+			}
+		},
+		computed: {
+			userData() {
+				return this.$store.getters.userInfo;
+			}
+		},
+		methods: {
+			regionSelection(e) {
+				console.log(e)
+				this.area = `${e.detail.values[0].name}  ${e.detail.values[1].name}`
+				this.areaCode = e.detail.values[1].code
+			},
+			idCardput(e) {
+				this.idCard = e.detail
+			},
+			passwordput(e) {
+				this.password = e.detail
+			},
+			submitBinding() {
+				console.log(this.areaCode, this.idCard, this.password)
+				this.loading = true
+				bindUserCard({
+					city: this.areaCode,
+					logincode: this.idCard,
+					password: md5(this.password).toString()
+				}).then((res) => {
+					if (res.code == 200) {
+						Toast.success('绑定成功');
+						this.idCardShow = false
+					} else if (res.code == 502) {
+						Toast.fail(res.msg);
+					} else {
+						Toast.fail('系统内部错误');
+					}
+					this.loading = false
+				})
+			},
+		}
+	}
+</script>
+
+<style>
+	.idCard {
+		width: 600rpx;
+		padding: 30rpx;
+		margin: auto;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+	}
+	.image-ava{
+		width: 100rpx;
+		height: 100rpx;
+		border-radius: 50%;
+	}
+</style>

+ 20 - 0
src/pages/webview/webview.vue

@@ -0,0 +1,20 @@
+<template>
+	<web-view :src="decodeURIComponent(src)"></web-view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				src: encodeURIComponent('https://sqxh.zzxcx.net')
+			};
+		},
+		onLoad: function (option) {
+		    this.src = option.src;
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

BIN
src/static/imgs/banner.png


BIN
src/static/imgs/bf.png


BIN
src/static/imgs/bg.png


BIN
src/static/imgs/bgbzicon.png


BIN
src/static/imgs/bxh.png


BIN
src/static/imgs/ctsc.png


BIN
src/static/imgs/fx.png


BIN
src/static/imgs/fxh.png


BIN
src/static/imgs/gd.png


BIN
src/static/imgs/jx.png


BIN
src/static/imgs/llzj.png


BIN
src/static/imgs/lxkf.png


BIN
src/static/imgs/mockicon.png


BIN
src/static/imgs/order-icon.png


BIN
src/static/imgs/sc.png


BIN
src/static/imgs/sequentialicon.png


BIN
src/static/imgs/share.png


BIN
src/static/imgs/shmr.png


BIN
src/static/imgs/sjcs.png


BIN
src/static/imgs/sq.png


BIN
src/static/imgs/sz.png


BIN
src/static/imgs/true.png


BIN
src/static/imgs/wd.png


BIN
src/static/imgs/wdh.png


BIN
src/static/imgs/wdsc.png


BIN
src/static/imgs/wdsz.png


BIN
src/static/imgs/wxtb.png


BIN
src/static/imgs/wyjs.png


BIN
src/static/imgs/yjfk.png


BIN
src/static/imgs/ysc.png


BIN
src/static/imgs/zf.png


BIN
src/static/imgs/zt.png


BIN
src/static/imgs/zth.png


BIN
src/static/imgs/zz.png


+ 10 - 0
src/store/getters.js

@@ -0,0 +1,10 @@
+const getters = {
+  token: state => state.user.token,
+  userInfo: state => state.user.userInfo,
+  avatar: state => state.user.avatar,
+  name: state => state.user.name,
+  introduction: state => state.user.introduction,
+  roles: state => state.user.roles,
+  permissions: state => state.user.permissions
+}
+export default getters

+ 17 - 0
src/store/index.js

@@ -0,0 +1,17 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import user from './modules/user'
+import cinema from './modules/cinema.js'
+import getters from './getters'
+
+Vue.use(Vuex)
+
+const store = new Vuex.Store({
+  modules: {
+    user,
+	cinema
+  },
+  getters
+})
+
+export default store

+ 30 - 0
src/store/modules/cinema.js

@@ -0,0 +1,30 @@
+const cinema = {
+	state: {
+		filmList: null,
+		filmDiscount: 1,
+		discountRule: {},
+		cityId: null,
+		cityObj:{}
+	},
+
+	mutations: {
+		SET_FILMLIST: (state, filmList) => {
+			state.filmList = filmList
+		},
+		SET_FILMDISCOUNT: (state, filmDiscount) => {
+			state.filmDiscount = filmDiscount
+		},
+		SET_DISCOUNTRULE: (state, discountRule) => {
+			state.discountRule = discountRule
+		},
+		SET_CITYID: (state, cityId) => {
+			state.cityId = cityId
+		},
+		SET_CITYOBJ: (state, cityObj) => {
+			state.cityObj = cityObj
+		}
+	}
+
+}
+
+export default cinema

+ 101 - 0
src/store/modules/user.js

@@ -0,0 +1,101 @@
+import {
+	login,
+	logout,
+	getInfo
+} from '@/api/login'
+import {
+	getToken,
+	setToken,
+	removeToken
+} from '@/utils/auth'
+
+const user = {
+	state: {
+		token: getToken(),
+		userInfo: '',
+		name: '',
+		avatar: '',
+		roles: [],
+		permissions: []
+	},
+
+	mutations: {
+		SET_TOKEN: (state, token) => {
+			state.token = token
+		},
+		SET_USERINFO: (state, userInfo) => {
+			state.userInfo = userInfo
+		},
+		SET_NAME: (state, name) => {
+			state.name = name
+		},
+		SET_AVATAR: (state, avatar) => {
+			state.avatar = avatar
+		},
+		SET_ROLES: (state, roles) => {
+			state.roles = roles
+		},
+		SET_PERMISSIONS: (state, permissions) => {
+			state.permissions = permissions
+		}
+	},
+
+	actions: {
+		// 登录
+		Login({
+			commit
+		}, userInfo) {
+			console.log('开始登录')
+			return new Promise((resolve, reject) => {
+				login().then(res => {
+					setToken(res.data.token)
+					commit('SET_TOKEN', res.data.token)
+					resolve(res)
+				}).catch(error => {
+					reject(error)
+				})
+			})
+		},
+
+		// 获取用户信息
+		GetInfo({
+			commit,
+			state
+		}) {
+			return new Promise((resolve, reject) => {
+				getInfo().then(res => {
+					commit('SET_USERINFO', res.data.student)
+					resolve(res)
+				}).catch(error => {
+					reject(error)
+				})
+			})
+		},
+
+		// 退出系统
+		// LogOut({ commit, state }) {
+		//   return new Promise((resolve, reject) => {
+		//     logout(state.token).then(() => {
+		//       commit('SET_TOKEN', '')
+		//       commit('SET_ROLES', [])
+		//       commit('SET_PERMISSIONS', [])
+		//       removeToken()
+		//       resolve()
+		//     }).catch(error => {
+		//       reject(error)
+		//     })
+		//   })
+		// },
+
+		// 前端 登出
+		// FedLogOut({ commit }) {
+		//   return new Promise(resolve => {
+		//     commit('SET_TOKEN', '')
+		//     removeToken()
+		//     resolve()
+		//   })
+		// }
+	}
+}
+
+export default user

+ 40 - 0
src/utils/auth.js

@@ -0,0 +1,40 @@
+// import Cookies from 'js-cookie'
+import {
+	login
+} from '@/api/login.js'
+
+const TokenKey = 'Admin-Token'
+
+//获取token
+export async function getToken() {
+	let tokenObj = uni.getStorageSync(TokenKey)
+	if (new Date().getTime() - tokenObj.date < 1000 * 60 * 28) {
+		return tokenObj.token
+	} else {
+		let res = await login()
+		setToken(res.data.token)
+		return res.data.token
+	}
+}
+
+//设置token
+export function setToken(token) {
+	let tokenObj = {
+		token,
+		date: new Date().getTime()
+	}
+	return uni.setStorageSync(TokenKey, tokenObj)
+}
+
+//刷新token时间
+export function refreshToken(token) {
+	let tokenObj = uni.getStorageSync(TokenKey)
+	if (!tokenObj) return
+	tokenObj.date = new Date().getTime()
+	return uni.setStorageSync(TokenKey, tokenObj)
+}
+
+//移除token
+export function removeToken() {
+	return uni.removeStorageSync(TokenKey)
+}

+ 7 - 0
src/utils/errorCode.js

@@ -0,0 +1,7 @@
+export default {
+  '401': '认证失败,无法访问系统资源',
+  '403': '当前操作没有权限',
+  '404': '访问资源不存在',
+  '500': '系统内部错误',
+  'default': '系统未知错误,请反馈给管理员'
+}

+ 97 - 0
src/utils/request - 副本.js

@@ -0,0 +1,97 @@
+import {
+	getToken,
+	setToken,
+	removeToken,
+	refreshToken
+} from '@/utils/auth'
+import {
+	login
+} from '@/api/login.js'
+import errorCode from '@/utils/errorCode'
+
+
+/**
+ * http请求封装
+ */
+const myAxios = (req) => {
+
+	let query = ''
+	
+	req.params && Object.keys(req.params).map((key, index) => {
+		if (index) {
+			query += '&' + key + '=' + encodeURIComponent(req.params[key])
+		} else {
+			query += '?' + key + '=' + encodeURIComponent(req.params[key])
+		}
+	})
+
+	let reqData = {
+		url: req.baseURL ? req.baseURL : '' + req.url + query,
+		method: req.method,
+		data: {
+			...req.data
+		},
+		header: {
+			...req.headers
+		},
+		timeout: req.timeout || 10000
+	}
+
+	// 请求拦截器
+	async function reqMiddleware(config) {
+
+		if (config.headers.isLogin) {
+			return config
+		}
+		let token = await getToken()
+		config.headers['Authorization'] = 'Bearer ' + token // 让每个请求携带自定义token 请根据实际情况自行修改
+		return config
+	}
+
+	// 响应拦截器
+	async function resMiddleware(res) {
+
+		const code = res.data.code || 200;
+		// 获取错误信息
+		const msg = errorCode[code] || res.data.msg || errorCode['default']
+		//重置Authorization时间
+		if (code !== 401) refreshToken()
+		
+		if (code === 401) {
+			console.log('拦截器信息:' + msg)
+			// this.$store.dispatch('Login')
+		} else if (code === 500) {
+			console.log('拦截器信息:' + msg)
+		} else if (code === 502) {
+			console.log('拦截器信息:' + msg)
+		} else if (code !== 200) {
+			console.log('拦截器信息:' + msg)
+		}
+		return res.data
+	}
+	// 2. 函数内部返回Promise 对象
+	return new Promise(async (resolve, reject) => {
+		reqData = await reqMiddleware(reqData)
+		// 3.wx.request()  小程序发送请求
+		wx.request({
+			// 把调用axios时传过来的所有参数解构赋值
+			...reqData,
+			// 请求成功之后调用的函数
+			success: async (result) => {
+				result = await resMiddleware(result)
+				resolve(result)
+			},
+			// 请求失败之后调用的函数
+			fail: (error) => {
+				reject(error)
+			},
+			complete: () => {
+
+			}
+		});
+	});
+
+}
+
+
+export default myAxios

+ 76 - 0
src/utils/request.js

@@ -0,0 +1,76 @@
+import {
+	getToken,
+	refreshToken
+} from '@/utils/auth'
+import errorCode from '@/utils/errorCode'
+import axios from 'axios'
+import mpAdapter from 'axios-miniprogram-adapter'
+axios.defaults.adapter = mpAdapter
+
+// 创建axios实例
+const service = axios.create({
+	// axios中请求配置有baseURL选项,表示请求URL公共部分
+	// baseURL: process.env.VUE_APP_BASE_API,
+	// 超时
+	// timeout: 10000
+})
+
+// request拦截器
+service.interceptors.request.use(
+	async config => {
+			// 是否需要设置 token
+			if (config.headers.isLogin) {
+				return config
+			}
+			config.headers['Authorization'] = 'Bearer ' + await getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
+			return config
+		},
+		error => {
+			console.log(error)
+			Promise.reject(error)
+		}
+)
+
+// 响应拦截器
+service.interceptors.response.use(
+	res => {
+		// 未设置状态码则默认成功状态
+		const code = res.data.code || 200;
+		// 获取错误信息
+		const msg = errorCode[code] || res.data.msg || errorCode['default']
+		//重置Authorization时间
+		if (code !== 401) refreshToken()
+
+		if (code === 401) {
+			console.log('拦截器信息:' + msg)
+		} else if (code === 500) {
+			console.log('拦截器信息:' + msg)
+		} else if (code === 502) {
+			console.log('拦截器信息:' + msg)
+		} else if (code !== 200) {
+			console.log('拦截器信息:' + msg)
+		}
+		return res.data
+	},
+	error => {
+		console.log('err' + error)
+		let {
+			message
+		} = error;
+		if (message == "Network Error") {
+			message = "后端接口连接异常";
+		} else if (message.includes("timeout")) {
+			message = "系统接口请求超时";
+		} else if (message.includes("Request failed with status code")) {
+			message = "系统接口" + message.substr(message.length - 3) + "异常";
+		}
+		// Message({
+		// 	message: message,
+		// 	type: 'error',
+		// 	duration: 5 * 1000
+		// })
+		return Promise.reject(error)
+	}
+)
+
+export default service

+ 203 - 0
src/utils/utils.js

@@ -0,0 +1,203 @@
+import {
+	BrowseRecordAdd
+} from '@/api/applist.js'
+import sha256 from 'crypto-js/sha256'
+
+//防抖函数
+export function debounce(func, fnThis, wait) {
+	var timer;
+	return () => {
+		timer && clearTimeout(timer);
+		timer = setTimeout(() => {
+			func.call(fnThis)
+			timer = null
+		}, wait);
+	};
+}
+
+//节流函数
+export function throttling(func, fnThis, wait) {
+	var timer;
+	return () => {
+		clearTimeout(timer);
+		timer = setTimeout(func.bind(fnThis), wait);
+	};
+}
+
+let goMiniAppFlag = false
+//跳转小程序
+export function goMiniApp(data, item) {
+
+	if (goMiniAppFlag) return
+	goMiniAppFlag = true
+
+	let myData = JSON.parse(data)
+
+	wx.navigateToMiniProgram({
+		...myData,
+		success: (res) => {
+			// 打开成功
+			goMiniAppFlag = false
+			item && BrowseRecordAdd(item.id)
+		},
+		fail: () => {
+			goMiniAppFlag = false
+		}
+	})
+}
+
+//跳转页面
+export function goPage(url, type, data) {
+	if (type == 'reLaunch') {
+		uni.reLaunch({
+			url
+		});
+		return
+	}
+	if (type == 'redirectTo') {
+		uni.redirectTo({
+			url
+		});
+		return
+	}
+	uni.navigateTo({
+		url,
+		success: function(res) {
+			// 通过eventChannel向被打开页面传送数据
+			res.eventChannel.emit('passParameters', data)
+		}
+	});
+}
+
+//接受参数
+export function goPageGetData() {
+	return new Promise((res) => {
+		const eventChannel = this.getOpenerEventChannel()
+		eventChannel.on('passParameters', function(data) {
+			res(data)
+		})
+	})
+}
+
+//根据类型跳转
+export function clickJumpType(item) {
+	if (item.jumpUrlType == 'goMiniApp') {
+		goMiniApp(item.jumpUrl)
+	}
+	if (item.jumpUrlType == 'goPage') {
+		goPage(item.jumpUrl)
+	}
+	if (item.jumpUrlType == 'goWebView') {
+		goPage(`/pages/webview/webview?src=${item.jumpUrl}`)
+	}
+}
+
+
+function base64_encode(str) {
+	var c1, c2, c3;
+	var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	var i = 0,
+		len = str.length,
+		string = '';
+
+	while (i < len) {
+		c1 = str.charCodeAt(i++) & 0xff;
+		if (i == len) {
+			string += base64EncodeChars.charAt(c1 >> 2);
+			string += base64EncodeChars.charAt((c1 & 0x3) << 4);
+			string += "==";
+			break;
+		}
+		c2 = str.charCodeAt(i++);
+		if (i == len) {
+			string += base64EncodeChars.charAt(c1 >> 2);
+			string += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
+			string += base64EncodeChars.charAt((c2 & 0xF) << 2);
+			string += "=";
+			break;
+		}
+		c3 = str.charCodeAt(i++);
+		string += base64EncodeChars.charAt(c1 >> 2);
+		string += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
+		string += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
+		string += base64EncodeChars.charAt(c3 & 0x3F)
+	}
+	return string
+}
+
+
+//微信支付调起支付
+export function wxPay(data) {
+
+	wx.requestPayment({
+		...data
+	})
+
+}
+
+//返回星期几
+export function getUTCDay(date) {
+	let UTCDay = new Date(date).getUTCDay()
+	switch (UTCDay) {
+		case 0:
+			return '周天';
+		case 1:
+			return '周一';
+		case 2:
+			return '周二';
+		case 3:
+			return '周三';
+		case 4:
+			return '周四';
+		case 5:
+			return '周五';
+		case 6:
+			return '周六';
+	}
+}
+
+//返回今天明天后天周几
+export function getDay(date) {
+	let UTCDay = new Date(date).getUTCDay()
+	let jetLag = new Date(date).getTime() - new Date().getTime()
+	let dayDif = jetLag / (1000 * 60 * 60 * 24)
+	if (dayDif < 2) {
+		if (dayDif < 0) {
+			return '今天'
+		}
+		if (dayDif < 1) {
+			return '明天'
+		}
+		if (dayDif < 2) {
+			return '后天'
+		}
+	}
+	switch (UTCDay) {
+		case 0:
+			return '周天';
+		case 1:
+			return '周一';
+		case 2:
+			return '周二';
+		case 3:
+			return '周三';
+		case 4:
+			return '周四';
+		case 5:
+			return '周五';
+		case 6:
+			return '周六';
+	}
+}
+
+export default {
+	goPageGetData,
+	clickJumpType,
+	debounce,
+	throttling,
+	goMiniApp,
+	goPage,
+	wxPay,
+	getUTCDay,
+	getDay
+}

+ 1 - 0
src/wxcomponents/vant/action-sheet/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 62 - 0
src/wxcomponents/vant/action-sheet/index.js

@@ -0,0 +1,62 @@
+import { VantComponent } from '../common/component';
+import { button } from '../mixins/button';
+import { openType } from '../mixins/open-type';
+VantComponent({
+  mixins: [button, openType],
+  props: {
+    show: Boolean,
+    title: String,
+    cancelText: String,
+    description: String,
+    round: {
+      type: Boolean,
+      value: true,
+    },
+    zIndex: {
+      type: Number,
+      value: 100,
+    },
+    actions: {
+      type: Array,
+      value: [],
+    },
+    overlay: {
+      type: Boolean,
+      value: true,
+    },
+    closeOnClickOverlay: {
+      type: Boolean,
+      value: true,
+    },
+    closeOnClickAction: {
+      type: Boolean,
+      value: true,
+    },
+    safeAreaInsetBottom: {
+      type: Boolean,
+      value: true,
+    },
+  },
+  methods: {
+    onSelect(event) {
+      const { index } = event.currentTarget.dataset;
+      const item = this.data.actions[index];
+      if (item && !item.disabled && !item.loading) {
+        this.$emit('select', item);
+        if (this.data.closeOnClickAction) {
+          this.onClose();
+        }
+      }
+    },
+    onCancel() {
+      this.$emit('cancel');
+    },
+    onClose() {
+      this.$emit('close');
+    },
+    onClickOverlay() {
+      this.$emit('click-overlay');
+      this.onClose();
+    },
+  },
+});

+ 8 - 0
src/wxcomponents/vant/action-sheet/index.json

@@ -0,0 +1,8 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-icon": "../icon/index",
+    "van-popup": "../popup/index",
+    "van-loading": "../loading/index"
+  }
+}

+ 69 - 0
src/wxcomponents/vant/action-sheet/index.wxml

@@ -0,0 +1,69 @@
+<wxs src="../wxs/utils.wxs" module="utils" />
+
+<van-popup
+  show="{{ show }}"
+  position="bottom"
+  round="{{ round }}"
+  z-index="{{ zIndex }}"
+  overlay="{{ overlay }}"
+  custom-class="van-action-sheet"
+  safe-area-inset-bottom="{{ safeAreaInsetBottom }}"
+  close-on-click-overlay="{{ closeOnClickOverlay }}"
+  bind:close="onClickOverlay"
+>
+  <view wx:if="{{ title }}" class="van-action-sheet__header">
+    {{ title }}
+    <van-icon
+      name="cross"
+      custom-class="van-action-sheet__close"
+      bind:click="onClose"
+    />
+  </view>
+  <view wx:if="{{ description }}" class="van-action-sheet__description van-hairline--bottom">
+    {{ description }}
+  </view>
+  <view wx:if="{{ actions && actions.length }}">
+    <!-- button外包一层view,防止actions动态变化,导致渲染时button被打散 -->
+    <button
+      wx:for="{{ actions }}"
+      wx:key="index"
+      open-type="{{ item.openType }}"
+      style="{{ item.color ? 'color: ' + item.color : '' }}"
+      class="{{ utils.bem('action-sheet__item', { disabled: item.disabled || item.loading }) }} {{ item.className || '' }}"
+      hover-class="van-action-sheet__item--hover"
+      data-index="{{ index }}"
+      bind:tap="onSelect"
+      bindgetuserinfo="bindGetUserInfo"
+      bindcontact="bindContact"
+      bindgetphonenumber="bindGetPhoneNumber"
+      binderror="bindError"
+      bindlaunchapp="bindLaunchApp"
+      bindopensetting="bindOpenSetting"
+      lang="{{ lang }}"
+      session-from="{{ sessionFrom }}"
+      send-message-title="{{ sendMessageTitle }}"
+      send-message-path="{{ sendMessagePath }}"
+      send-message-img="{{ sendMessageImg }}"
+      show-message-card="{{ showMessageCard }}"
+      app-parameter="{{ appParameter }}"
+    >
+      <block wx:if="{{ !item.loading }}">
+        {{ item.name }}
+        <view wx:if="{{ item.subname }}" class="van-action-sheet__subname" >{{ item.subname }}</view>
+      </block>
+      <van-loading wx:else custom-class="van-action-sheet__loading" size="22px" />
+    </button>
+  </view>
+  <slot />
+  <block wx:if="{{ cancelText }}">
+    <view class="van-action-sheet__gap" />
+    <view
+      class="van-action-sheet__cancel"
+      hover-class="van-action-sheet__cancel--hover"
+      hover-stay-time="70"
+      bind:tap="onCancel"
+    >
+      {{ cancelText }}
+    </view>
+  </block>
+</van-popup>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
src/wxcomponents/vant/action-sheet/index.wxss


+ 1 - 0
src/wxcomponents/vant/area/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 232 - 0
src/wxcomponents/vant/area/index.js

@@ -0,0 +1,232 @@
+import { VantComponent } from '../common/component';
+import { pickerProps } from '../picker/shared';
+import { requestAnimationFrame } from '../common/utils';
+const EMPTY_CODE = '000000';
+VantComponent({
+  classes: ['active-class', 'toolbar-class', 'column-class'],
+  props: Object.assign(Object.assign({}, pickerProps), {
+    value: {
+      type: String,
+      observer(value) {
+        this.code = value;
+        this.setValues();
+      },
+    },
+    areaList: {
+      type: Object,
+      value: {},
+      observer: 'setValues',
+    },
+    columnsNum: {
+      type: null,
+      value: 3,
+    },
+    columnsPlaceholder: {
+      type: Array,
+      observer(val) {
+        this.setData({
+          typeToColumnsPlaceholder: {
+            province: val[0] || '',
+            city: val[1] || '',
+            county: val[2] || '',
+          },
+        });
+      },
+    },
+  }),
+  data: {
+    columns: [{ values: [] }, { values: [] }, { values: [] }],
+    typeToColumnsPlaceholder: {},
+  },
+  mounted() {
+    requestAnimationFrame(() => {
+      this.setValues();
+    });
+  },
+  methods: {
+    getPicker() {
+      if (this.picker == null) {
+        this.picker = this.selectComponent('.van-area__picker');
+      }
+      return this.picker;
+    },
+    onCancel(event) {
+      this.emit('cancel', event.detail);
+    },
+    onConfirm(event) {
+      const { index } = event.detail;
+      let { value } = event.detail;
+      value = this.parseValues(value);
+      this.emit('confirm', { value, index });
+    },
+    emit(type, detail) {
+      detail.values = detail.value;
+      delete detail.value;
+      this.$emit(type, detail);
+    },
+    parseValues(values) {
+      const { columnsPlaceholder } = this.data;
+      return values.map((value, index) => {
+        if (
+          value &&
+          (!value.code || value.name === columnsPlaceholder[index])
+        ) {
+          return Object.assign(Object.assign({}, value), {
+            code: '',
+            name: '',
+          });
+        }
+        return value;
+      });
+    },
+    onChange(event) {
+      const { index, picker, value } = event.detail;
+      this.code = value[index].code;
+      this.setValues().then(() => {
+        this.$emit('change', {
+          picker,
+          values: this.parseValues(picker.getValues()),
+          index,
+        });
+      });
+    },
+    getConfig(type) {
+      const { areaList } = this.data;
+      return (areaList && areaList[`${type}_list`]) || {};
+    },
+    getList(type, code) {
+      if (type !== 'province' && !code) {
+        return [];
+      }
+      const { typeToColumnsPlaceholder } = this.data;
+      const list = this.getConfig(type);
+      let result = Object.keys(list).map((code) => ({
+        code,
+        name: list[code],
+      }));
+      if (code != null) {
+        // oversea code
+        if (code[0] === '9' && type === 'city') {
+          code = '9';
+        }
+        result = result.filter((item) => item.code.indexOf(code) === 0);
+      }
+      if (typeToColumnsPlaceholder[type] && result.length) {
+        // set columns placeholder
+        const codeFill =
+          type === 'province'
+            ? ''
+            : type === 'city'
+            ? EMPTY_CODE.slice(2, 4)
+            : EMPTY_CODE.slice(4, 6);
+        result.unshift({
+          code: `${code}${codeFill}`,
+          name: typeToColumnsPlaceholder[type],
+        });
+      }
+      return result;
+    },
+    getIndex(type, code) {
+      let compareNum = type === 'province' ? 2 : type === 'city' ? 4 : 6;
+      const list = this.getList(type, code.slice(0, compareNum - 2));
+      // oversea code
+      if (code[0] === '9' && type === 'province') {
+        compareNum = 1;
+      }
+      code = code.slice(0, compareNum);
+      for (let i = 0; i < list.length; i++) {
+        if (list[i].code.slice(0, compareNum) === code) {
+          return i;
+        }
+      }
+      return 0;
+    },
+    setValues() {
+      const picker = this.getPicker();
+      if (!picker) {
+        return;
+      }
+      let code = this.code || this.getDefaultCode();
+      const provinceList = this.getList('province');
+      const cityList = this.getList('city', code.slice(0, 2));
+      const stack = [];
+      const indexes = [];
+      const { columnsNum } = this.data;
+      if (columnsNum >= 1) {
+        stack.push(picker.setColumnValues(0, provinceList, false));
+        indexes.push(this.getIndex('province', code));
+      }
+      if (columnsNum >= 2) {
+        stack.push(picker.setColumnValues(1, cityList, false));
+        indexes.push(this.getIndex('city', code));
+        if (cityList.length && code.slice(2, 4) === '00') {
+          [{ code }] = cityList;
+        }
+      }
+      if (columnsNum === 3) {
+        stack.push(
+          picker.setColumnValues(
+            2,
+            this.getList('county', code.slice(0, 4)),
+            false
+          )
+        );
+        indexes.push(this.getIndex('county', code));
+      }
+      return Promise.all(stack)
+        .catch(() => {})
+        .then(() => picker.setIndexes(indexes))
+        .catch(() => {});
+    },
+    getDefaultCode() {
+      const { columnsPlaceholder } = this.data;
+      if (columnsPlaceholder.length) {
+        return EMPTY_CODE;
+      }
+      const countyCodes = Object.keys(this.getConfig('county'));
+      if (countyCodes[0]) {
+        return countyCodes[0];
+      }
+      const cityCodes = Object.keys(this.getConfig('city'));
+      if (cityCodes[0]) {
+        return cityCodes[0];
+      }
+      return '';
+    },
+    getValues() {
+      const picker = this.getPicker();
+      if (!picker) {
+        return [];
+      }
+      return this.parseValues(picker.getValues().filter((value) => !!value));
+    },
+    getDetail() {
+      const values = this.getValues();
+      const area = {
+        code: '',
+        country: '',
+        province: '',
+        city: '',
+        county: '',
+      };
+      if (!values.length) {
+        return area;
+      }
+      const names = values.map((item) => item.name);
+      area.code = values[values.length - 1].code;
+      if (area.code[0] === '9') {
+        area.country = names[1] || '';
+        area.province = names[2] || '';
+      } else {
+        area.province = names[0] || '';
+        area.city = names[1] || '';
+        area.county = names[2] || '';
+      }
+      return area;
+    },
+    reset(code) {
+      this.code = code || '';
+      return this.setValues();
+    },
+  },
+});

+ 6 - 0
src/wxcomponents/vant/area/index.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-picker": "../picker/index"
+  }
+}

+ 20 - 0
src/wxcomponents/vant/area/index.wxml

@@ -0,0 +1,20 @@
+<wxs src="./index.wxs" module="computed" />
+
+<van-picker
+  class="van-area__picker"
+  active-class="active-class"
+  toolbar-class="toolbar-class"
+  column-class="column-class"
+  show-toolbar
+  value-key="name"
+  title="{{ title }}"
+  loading="{{ loading }}"
+  columns="{{ computed.displayColumns(columns, columnsNum) }}"
+  item-height="{{ itemHeight }}"
+  visible-item-count="{{ visibleItemCount }}"
+  cancel-button-text="{{ cancelButtonText }}"
+  confirm-button-text="{{ confirmButtonText }}"
+  bind:change="onChange"
+  bind:confirm="onConfirm"
+  bind:cancel="onCancel"
+/>

+ 8 - 0
src/wxcomponents/vant/area/index.wxs

@@ -0,0 +1,8 @@
+/* eslint-disable */
+function displayColumns(columns, columnsNum) {
+  return columns.slice(0, +columnsNum);
+}
+
+module.exports = {
+  displayColumns: displayColumns,
+};

+ 1 - 0
src/wxcomponents/vant/area/index.wxss

@@ -0,0 +1 @@
+@import '../common/index.wxss';

+ 1 - 0
src/wxcomponents/vant/button/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 58 - 0
src/wxcomponents/vant/button/index.js

@@ -0,0 +1,58 @@
+import { VantComponent } from '../common/component';
+import { button } from '../mixins/button';
+import { openType } from '../mixins/open-type';
+import { canIUseFormFieldButton } from '../common/version';
+const mixins = [button, openType];
+if (canIUseFormFieldButton()) {
+  mixins.push('wx://form-field-button');
+}
+VantComponent({
+  mixins,
+  classes: ['hover-class', 'loading-class'],
+  data: {
+    baseStyle: '',
+  },
+  props: {
+    formType: String,
+    icon: String,
+    classPrefix: {
+      type: String,
+      value: 'van-icon',
+    },
+    plain: Boolean,
+    block: Boolean,
+    round: Boolean,
+    square: Boolean,
+    loading: Boolean,
+    hairline: Boolean,
+    disabled: Boolean,
+    loadingText: String,
+    customStyle: String,
+    loadingType: {
+      type: String,
+      value: 'circular',
+    },
+    type: {
+      type: String,
+      value: 'default',
+    },
+    dataset: null,
+    size: {
+      type: String,
+      value: 'normal',
+    },
+    loadingSize: {
+      type: String,
+      value: '20px',
+    },
+    color: String,
+  },
+  methods: {
+    onClick() {
+      if (!this.data.loading) {
+        this.$emit('click');
+      }
+    },
+    noop() {},
+  },
+});

+ 7 - 0
src/wxcomponents/vant/button/index.json

@@ -0,0 +1,7 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-icon": "../icon/index",
+    "van-loading": "../loading/index"
+  }
+}

Vissa filer visades inte eftersom för många filer har ändrats