Browse Source

增加研学课程购买和分享

vaecebyZ 9 months ago
parent
commit
357802a1f6
40 changed files with 6729 additions and 594 deletions
  1. 17 0
      api/activity.js
  2. 19 0
      api/goods.js
  3. 30 0
      api/order.js
  4. 82 0
      api/study.js
  5. 6 2
      components/zs-header/index.vue
  6. 109 1
      components/zs-skeleton/index.vue
  7. 2 1
      login/login/login.vue
  8. 2 1
      login/login/otherLogin.vue
  9. 9 0
      my/order/index.vue
  10. 60 2
      pages.json
  11. 315 0
      pages/activity/courseDetail.vue
  12. 302 0
      pages/activity/detail.vue
  13. 361 86
      pages/activity/index.vue
  14. 29 0
      pages/dev/index.vue
  15. 10 1
      pages/index/index.vue
  16. 119 0
      share/index.vue
  17. 225 0
      share/sharePosts.vue
  18. BIN
      static/check.png
  19. BIN
      static/eglass-check.png
  20. BIN
      static/user-edit.png
  21. 139 89
      study/index.vue
  22. 694 0
      study/pay/order.vue
  23. 727 0
      study/pay/orderPay.vue
  24. 483 222
      study/studyGoodsDetail.vue
  25. 159 0
      study/tourList/add.vue
  26. 202 0
      study/tourList/list.vue
  27. 481 189
      study/type.vue
  28. 28 0
      uni_modules/uni-calendar/changelog.md
  29. 544 0
      uni_modules/uni-calendar/components/uni-calendar/calendar.js
  30. 12 0
      uni_modules/uni-calendar/components/uni-calendar/i18n/en.json
  31. 8 0
      uni_modules/uni-calendar/components/uni-calendar/i18n/index.js
  32. 12 0
      uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json
  33. 12 0
      uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json
  34. 192 0
      uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue
  35. 657 0
      uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue
  36. 361 0
      uni_modules/uni-calendar/components/uni-calendar/util.js
  37. 85 0
      uni_modules/uni-calendar/package.json
  38. 103 0
      uni_modules/uni-calendar/readme.md
  39. 1 0
      utils/config.js
  40. 132 0
      utils/request.js.dev

+ 17 - 0
api/activity.js

@@ -58,3 +58,20 @@ export function getOrderDetail(data) {
 	})
 }
 
+// 研学活动列表
+// /goodsReserve/page
+export function studyList(data) {
+	return request({
+		url: '/zswl-cloud-shop/goodsReserve/page',
+		method:'post',
+		data
+	})
+}
+
+// 获取活动详情
+// /goodsReserve/getReserveDetail
+export function getStudyDetail(data) {
+	return request({
+		url: '/zswl-cloud-shop/goodsReserve/getReserveDetail?orderNo='+data,
+	})
+}

+ 19 - 0
api/goods.js

@@ -83,3 +83,22 @@ export function valid(data) {
 	})
 }
 
+
+// 研学商品详情的统计
+// goodsReserve/getStudyAbout
+export function getStudyAbout(data) {
+	return request({
+		url: '/zswl-cloud-shop/goodsReserve/getStudyAbout',
+		method:'get',
+		data
+	})
+}
+// 查询研学商品的购买时段
+// /goodsReserve/goodsBuyStatistic
+export function goodsBuyStatistic(data) {
+	return request({
+		url: '/zswl-cloud-shop/goodsReserve/goodsBuyStatistic',
+		method:'post',
+		data
+	})
+}

+ 30 - 0
api/order.js

@@ -62,3 +62,33 @@ export function calculateByInput(data) {
 // 		data
 // 	})
 // }
+
+
+// 研学商品价格计算
+// /goodsInfo/studyCalculate
+export function studyCalculate(data) {
+	return request({
+		url: '/zswl-cloud-shop/goodsInfo/studyCalculate',
+		method:'post',
+		data
+	})
+}
+
+// 预约订单
+// /goodsReserve/saveReserve
+export function saveReserve(data) {
+	return request({
+		url: '/zswl-cloud-shop/goodsReserve/saveReserve',
+		method:'post',
+		data
+	})
+}
+
+// 获取出游信息
+// /goodsReserve/getReserve
+export function getReserve(orderNo) {
+	return request({
+		url: '/zswl-cloud-shop/goodsReserve/getReserve?orderNo='+orderNo,
+
+	})
+}

+ 82 - 0
api/study.js

@@ -104,6 +104,14 @@ export function videoList(data) {
 		data
 	})
 }
+// 课程栏目推荐
+export function videoLists(data) {
+	return request({
+		url: '/zswl-cloud-shop/courseChapter/video',
+		data
+	})
+}
+
 
 // 课程详情
 export function videoDetail(data) {
@@ -146,3 +154,77 @@ export function studyGoods(data) {
 		data
 	})
 }
+
+// 获取栏目下的互动社区tab
+// community/getCommunitys
+export function getCommunitys(data) {
+	return request({
+		url: '/zswl-cloud-shop/community/getCommunitys',
+		method:'get',
+		data
+	})
+}
+
+// 获取出游人
+// /traveler/getAllTravelers
+export function getAllTravelers(data) {
+	return request({
+		url: '/zswl-cloud-shop/traveler/getAllTravelers',
+		method:'get',
+		data
+	})
+}
+// 保存出游人
+// /traveler/saveTraveler
+export function saveTraveler(data) {
+	return request({
+		url: '/zswl-cloud-shop/traveler/saveTraveler',
+		method:'post',
+		data
+	})
+}
+
+// 研学课程详情
+// goodsReserve/getCourseDetail 
+export function getCourseDetail({ orderNo,id}) {
+	return request({
+		url: `/zswl-cloud-shop/goodsReserve/getCourseDetail?id=${id}&orderNo=${orderNo}`,
+	})
+}
+// 视频进度上报
+// /dataViewPer/updatePer
+export function updatePer(data) {
+	return request({
+		url: '/zswl-cloud-shop/dataViewPer/updatePer',
+		method:'post',
+		data
+	})
+}
+
+// 研学心得发表
+// /communityItem/goodsSaveCommunityItem
+export function goodsSaveCommunityItem(data) {
+	return request({
+		url: '/zswl-cloud-shop/communityItem/goodsSaveCommunityItem',
+		method:'post',
+		data
+	})
+}
+
+// 研学心得详情
+// /communityItem/detail
+export function goodsCommunityItemDetail(data) {
+	return request({
+		url: '/zswl-cloud-shop/communityItem/detail?tabId='+data ,
+	})
+}
+
+// 研学心得列表
+// communityItem/pageItem
+export function goodsSharePage(data) {
+	return request({
+		url: '/zswl-cloud-shop/communityItem/pageItem',
+		method:'post',
+		data
+	})
+}

+ 6 - 2
components/zs-header/index.vue

@@ -1,8 +1,8 @@
 <template>
 	<view class="zs-header" :style="{height:statusBarHeight+navBareight +'px',background:background?'#fff':''}" @click="back">
-		<image class="back" :src="background?backIcon:whiteBack" mode=""></image>
+		<image class="back" :src="background?backIcon:color != '#fff' ? backIcon:whiteBack" mode=""></image>
 		<!-- <image class="back" :src="backIcon" mode=""></image> -->
-		<view class="title" :style="{color:background?'#222222':'#fff'}">
+		<view class="title" :style="{color:background?'#222222':color}">
 			{{title}}
 		</view>
 	</view>
@@ -31,6 +31,10 @@
 				type: String,
 				default: ''
 			},
+			color:{
+				type:String,
+				default:'#fff'
+			}
 		},
 		data() {
 			return {

+ 109 - 1
components/zs-skeleton/index.vue

@@ -664,7 +664,56 @@
 		  </view>
 		</template>
 		
-		
+		<view name="skeleton" class="sk-activity" v-else-if="type == 'activity'">
+			<view class="data-v-6362e18b data-v-6362e18b">
+				<view class="zs-header zs-header data-v-6362e18b data-v-6362e18b" style="height:87px;background:#fff;">
+					<view class="title title data-v-6362e18b data-v-6362e18b">
+						<view class="title-text data-v-6362e18b sk-transparent sk-text-14-2857-450 sk-text">平台活动</view>
+						<view class="title-text data-v-6362e18b sk-transparent sk-text-14-2857-911 sk-text">我的活动</view>
+					</view>
+				</view>
+				<view class="data-v-6362e18b data-v-6362e18b" style="margin-top:87px;">
+					<view class="activity activity data-v-6362e18b data-v-6362e18b">
+						<view  class="data-v-6362e18b data-v-6362e18b" data-event-opts="^load,loadMore">
+							<view class="zs-list list-index--zs-list" style="margin-top:0;">
+								<view class="item item data-v-6362e18b data-v-6362e18b" data-event-opts="tap,goDetail,$0,list,id,91,id">
+									<view class="data-v-6362e18b data-v-6362e18b">
+										<view class="zs-img-box index--zs-img-box data-v-ce5cf0a6 index--data-v-ce5cf0a6">
+											<image class="img1 index--img1 data-v-ce5cf0a6 index--data-v-ce5cf0a6 lazyzs1qRKubFGRruhkh9p0937QVEN68rUxnW index--lazyzs1qRKubFGRruhkh9p0937QVEN68rUxnW sk-image" data-event-opts="error,error,$event" mode="true" style="width:369px;height:166px;border-radius:8px 8px 0 0;"></image>
+										</view>
+									</view>
+									<view class="info info data-v-6362e18b data-v-6362e18b">
+										<view class="title data-v-6362e18b sk-transparent sk-text-14-2857-399 sk-text">活动回归测试(后台)</view>
+										<view class="desc data-v-6362e18b sk-transparent sk-text-14-2857-647 sk-text">活动时间: 2024-08-01 00:00 至 2024-08-31 23:59</view>
+									</view>
+								</view>
+								<view class="item item data-v-6362e18b data-v-6362e18b" data-event-opts="tap,goDetail,$0,list,id,90,id">
+									<view class="data-v-6362e18b data-v-6362e18b">
+										<view class="zs-img-box index--zs-img-box data-v-ce5cf0a6 index--data-v-ce5cf0a6">
+											<image class="img1 index--img1 data-v-ce5cf0a6 index--data-v-ce5cf0a6 lazyzsPctnsYinpTTAvbrYmGryZ8NgMbd4YXk index--lazyzsPctnsYinpTTAvbrYmGryZ8NgMbd4YXk sk-image" data-event-opts="error,error,$event" mode="true" style="width:369px;height:166px;border-radius:8px 8px 0 0;"></image>
+										</view>
+									</view>
+									<view class="info info data-v-6362e18b data-v-6362e18b">
+										<view class="title data-v-6362e18b sk-transparent sk-text-14-2857-784 sk-text">海报</view>
+										<view class="desc data-v-6362e18b sk-transparent sk-text-14-2857-858 sk-text">活动时间: 2024-08-01 00:00 至 2024-08-31 23:59</view>
+									</view>
+								</view>
+								<view class="item item data-v-6362e18b data-v-6362e18b" data-event-opts="tap,goDetail,$0,list,id,88,id">
+									<view class="data-v-6362e18b data-v-6362e18b">
+										<view class="zs-img-box index--zs-img-box data-v-ce5cf0a6 index--data-v-ce5cf0a6">
+											<image class="img1 index--img1 data-v-ce5cf0a6 index--data-v-ce5cf0a6 lazyzsxUZGFWS9vSpjdwlu8h3EyROiag6fzt1 index--lazyzsxUZGFWS9vSpjdwlu8h3EyROiag6fzt1 sk-image" data-event-opts="error,error,$event" mode="true" style="width:369px;height:166px;border-radius:8px 8px 0 0;"></image>
+										</view>
+									</view>
+									<view class="info info data-v-6362e18b data-v-6362e18b">
+										<view class="title data-v-6362e18b sk-transparent sk-text-14-2857-393 sk-text">活动回归测试</view>
+									</view>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+    </view>
 
 
 	</view>
@@ -1611,4 +1660,63 @@
 		  }
 
 	}
+	.sk-activity{
+		.sk-transparent {
+			color: transparent !important;
+		}
+		.sk-text-14-2857-450 {
+				background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
+				background-size: 100% 43.0769rpx;
+				position: relative !important;
+			}
+		.sk-text {
+				background-origin: content-box !important;
+				background-clip: content-box !important;
+				background-color: transparent !important;
+				color: transparent !important;
+				background-repeat: repeat-y !important;
+			}
+		.sk-text-14-2857-911 {
+				background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
+				background-size: 100% 43.0769rpx;
+				position: relative !important;
+			}
+		.sk-text-14-2857-399 {
+				background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
+				background-size: 100% 37.6923rpx;
+				position: relative !important;
+			}
+		.sk-text-14-2857-647 {
+				background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
+				background-size: 100% 32.3077rpx;
+				position: relative !important;
+			}
+		.sk-text-14-2857-784 {
+				background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
+				background-size: 100% 37.6923rpx;
+				position: relative !important;
+			}
+		.sk-text-14-2857-858 {
+				background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
+				background-size: 100% 32.3077rpx;
+				position: relative !important;
+			}
+		.sk-text-14-2857-393 {
+				background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
+				background-size: 100% 37.6923rpx;
+				position: relative !important;
+			}
+		.sk-image {
+				background: #EFEFEF !important;
+			}
+		.sk-container {
+				position: absolute;
+				left: 0;
+				top: 0;
+				width: 100%;
+				height: 100%;
+				overflow: hidden;
+				background-color: transparent;
+			}
+  }
 </style>

+ 2 - 1
login/login/login.vue

@@ -133,10 +133,11 @@
 							this.decryptPhoneNumber(val)
 						})
 					}else{
-						
+						const inviteUserId = uni.getStorageSync('inviteCode') == 'undefined' ? null : uni.getStorageSync('inviteCode')
 						this.userInfo.phone = data.content
 						getLoginToken({
 							phoneNum: data.content,
+							inviteUserId,
 							platformType:1
 						}).then(msg => {
 							if(msg.state != 'Success') return

+ 2 - 1
login/login/otherLogin.vue

@@ -184,7 +184,8 @@
 					uni.showLoading({
 						title: '加载中'
 					});
-					getLoginToken({phoneNum:this.userInfo.phoneNum,platformType:1}).then(res=>{
+					const inviteUserId = uni.getStorageSync('inviteCode') == 'undefined' ? null : uni.getStorageSync('inviteCode')
+					getLoginToken({phoneNum:this.userInfo.phoneNum,platformType:1,inviteUserId}).then(res=>{
 						let userInfo = this.userInfo
 						userInfo.loginToken = res.content
 						if(res.state != 'Success') return

+ 9 - 0
my/order/index.vue

@@ -109,6 +109,15 @@
 			},
 			jump(item) {
 				// uni.setStorageSync('order',JSON.stringify(item))
+				
+				// 研学类型商品统一走研学订单详情
+				if(item.goodsModelList[0].goodsInfo.isStudy){
+					uni.navigateTo({
+						url: '/study/pay/orderPay?id='+item.orderNo
+					})
+					return
+				}
+
 				if(item.goodsModelList[0].jobFlowMap == 'Ticket'){
 					uni.navigateTo({
 						url: '/my/order/scenic/detail?id='+item.orderNo

+ 60 - 2
pages.json

@@ -27,9 +27,23 @@
 			"path": "pages/activity/index",
 			"style": {
 				"navigationBarTitleText": "活动",
+				"navigationStyle": "custom",
 				"enablePullDownRefresh": false
 			}
 		},
+		{
+			"path": "pages/activity/detail",
+			"style": {
+				"navigationStyle": "custom",
+				"enablePullDownRefresh": false
+			}
+		},
+		{
+			"path": "pages/activity/courseDetail",
+			"style": {
+				"navigationBarTitleText": "课程详情"
+			}
+		},
 		{
 			"path": "pages/my/index",
 			"style": {
@@ -455,10 +469,36 @@
 		},
 		{
 			"root": "study", //研学
-			"pages": [{
+			"pages": [
+				{
 					"path": "index",
 					"style": {
-						"navigationBarTitleText": "慧研学"
+						"navigationBarTitleText": "慧研学",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path" : "pay/order",
+					"style": {
+						"navigationBarTitleText": "提交订单"
+					}
+				},
+				{
+					"path" : "pay/orderPay",
+					"style": {
+						"navigationBarTitleText": "订单详情"
+					}
+				},
+				{
+					"path":"tourList/list",
+					"style": {
+						"navigationBarTitleText": "选择出游人"
+					}
+				},
+				{
+					"path":"tourList/add",
+					"style": {
+						"navigationBarTitleText": "添加出游人"
 					}
 				},
 				{
@@ -566,6 +606,24 @@
 					}
 				}
 			]
+		},
+		{
+			"root":"share",
+			"pages":[
+				{
+					"path":"index",
+					"style": {
+						"navigationBarTitleText": "研学分享"
+					}
+				},
+				{ 
+					"path":"sharePosts",
+					"style": {
+						"navigationBarTitleText": "研学心得",
+						"navigationStyle": "custom"
+					}
+				}
+			]
 		}
 
 	],

+ 315 - 0
pages/activity/courseDetail.vue

@@ -0,0 +1,315 @@
+<template>
+	<view class="courseDetail">
+		<video id="courseVideo" class="video" show-mute-btn enable-play-gesture page-gesture show-progress
+			show-fullscreen-btn show-play-btn enable-progress-gesture :src="info.courseGoodsDto.chapterUrl"
+			:autoplay="autoplay" controls @timeupdate="videoTimeUpdateEvent" @ended="onEnd">
+
+		</video>
+
+		<view class="content">
+			<view class="tab-box">
+				<view class="tab" :class="[tab == 0 ? 'active' : '']" @click="handleTab(0)">
+					简介
+				</view>
+				<view class="tab" :class="[tab == 1 ? 'active' : '']" @click="handleTab(1)">
+					章节
+				</view>
+			</view>
+
+			<view class="video-title">
+				{{ info.courseGoodsDto.courseName }}
+			</view>
+			<view class="type-box">
+				<view class="type">
+					课程:{{ info.goodsName }}
+				</view>
+				<view class="times">
+					10465次播放
+				</view>
+			</view>
+
+			<view class="intro" v-show="tab == 0">
+				<view class="intro-title">
+					课程简介
+				</view>
+				<view class="desc">
+					{{ info.courseGoodsDto.courseMsg }}
+				</view>
+			</view>
+
+			<!-- 章节 -->
+			<view class="catalogue" v-show="tab == 1">
+				<view class="item" v-for="(item, index) in info.courses" :key="index" @click="handleItem(item, index)">
+					<view class="num" :class="[current == index ? 'active' : '']">
+						{{ index + 1 < 10 ? '0' + (index + 1) : index + 1 }} </view>
+							<view class="video-info">
+								<view class="title" :class="[current == index ? 'active' : '']">
+									{{ item.courseName }}
+								</view>
+								<view class="info">
+									<view class="icon-box">
+										<image class="icon" src="../static/study/time.png" mode=""></image>
+										<view class="text">
+											09:05
+										</view>
+									</view>
+									<view class="icon-box">
+										<image class="icon" src="../static/study/play-icon.png" mode=""></image>
+										<view class="text">
+											09:05
+										</view>
+									</view>
+								</view>
+							</view>
+							<image class="btn" :src="current == index ? playIng : playBtn" mode=""></image>
+
+					</view>
+				</view>
+
+			</view>
+		</view>
+</template>
+
+<script>
+import { getCourseDetail, updatePer } from '@/api/study.js'
+export default {
+	data() {
+		return {
+			tab: 0,
+			current: 0,
+			autoplay: true,
+			info: {
+				chapters: [],
+				columnId: 0,
+				courseImg: '',
+				id: 0,
+				courseMsg: '',
+				courseName: ''
+			},
+			goods: {},
+			videoContext: {},
+			query: {},
+			timmer: null,
+			per: 0,
+			// list:[
+			// 	{
+			// 		title:'火箭发射',
+			// 		times:'09:05',
+			// 		playTimes:'1.9万',
+			// 		video:'https://sns-video-bd.xhscdn.com/spectrum/01e2e095923d542a01837003823d55acab_259.mp4',
+			// 		poster: 'https://gimg4.baidu.com/poster/src=http%3A%2F%2Ft15.baidu.com%2Fit%2Fu%3D4149304328%2C1810644736%26fm%3D225%26app%3D113%26f%3DJPEG%3Fw%3D1499%26h%3D843%26s%3D3352508488112DED7E265C1B03007098&refer=http%3A%2F%2Fwww.baidu.com&app=2004&size=f352,234&n=0&g=0n&q=100?sec=1703215065&t=01d1ccc946fc2cc3600627e2e1aac47f',
+			// 	},
+			// 	{
+			// 		title:'星舰发射',
+			// 		times:'09:05',
+			// 		playTimes:'1.9万',
+			// 		video:'http://flv0.bn.netease.com/00756cf0f624a4622e55ac120cfede7958e925f6eb2ac9a492d01cb163db2ba56e195feabfe3f2c361b91f26fbd86a4969bc4a9669f3e30eeb29cf18d8b8a4fe7727b72ddf34ad1a01d1502ca790f0f7fa40c55400983658284e046bafa6f501687a85211cd279686a278559e76f2a2b36321394c90916b4.mp4',
+			// 		poster:'https://nimg.ws.126.net/?url=http%3A%2F%2Fvideoimg.ws.126.net%2Fcover%2F20231221%2FVBtFr5Y9x_cover.jpg&thumbnail=750x2147483647&quality=75&type=jpg',
+
+
+			// 	}
+			// ],
+			playBtn: require('@/static/study/play-btn.png'),
+			playIng: require('@/static/study/playing.png'),
+		}
+	},
+	methods: {
+		// 视频播放结束
+		onEnd() {
+			if (this.current != this.info.chapters.length - 1) {
+				this.current++
+			}
+		},
+		handleTab(tab) {
+			this.tab = tab
+		},
+		handleItem(item, index) {
+			this.current = index
+			this.info.courseGoodsDto = item
+			this.videoContext.play()
+		},
+		videoDetail(query) {
+			this.query = {
+				dataId: query.id,
+				orderNo: query.orderNo
+			}
+			getCourseDetail(query).then(res => {
+				if (res.state == 'Success') {
+					this.info = res.content
+				}
+			})
+		},
+		videoTimeUpdateEvent(e) { // 播放进度改变
+			//当前进度
+			let currentTime = parseInt(e.detail.currentTime);
+			//总进度
+			let duration = parseInt(e.detail.duration);
+			const per = ((currentTime / duration) * 100).toFixed()
+			if (per % 5 == 0 && per != this.per) {
+				this.per = per
+				// console.log('上报进度')
+				updatePer({
+					...this.query,
+					per
+				}).then(res => {
+					console.log(res)
+				})
+			}
+			// console.log('当前进度:', currentTime, '总进度:', duration,'比例:',per);
+		},
+	},
+	onLoad(options) {
+		this.videoDetail({
+			id: options.id,
+			orderNo: options.orderNo
+		})
+		this.videoContext = uni.createVideoContext('courseVideo');
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.courseDetail {
+	.video {
+		width: 100%;
+		height: 420rpx;
+	}
+
+	.content {
+		padding: 0 30rpx;
+
+		.tab-box {
+			display: flex;
+			color: #222222;
+			font-size: 28rpx;
+			border-bottom: 1rpx solid #F0F0F0;
+
+			.tab {
+				flex: 1;
+				padding: 20rpx 0;
+				text-align: center;
+			}
+
+			.tab.active {
+				font-weight: bold;
+				position: relative;
+
+				&::after {
+					content: '';
+					width: 40rpx;
+					height: 8rpx;
+					border-radius: 4rpx;
+					background: #3879F9;
+					position: absolute;
+					bottom: 0%;
+					left: 50%;
+					transform: translateX(-50%);
+				}
+			}
+		}
+
+		.video-title {
+			width: 100%;
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #222222;
+			margin-top: 24rpx;
+			display: -webkit-box;
+			-webkit-box-orient: vertical;
+			-webkit-line-clamp: 2;
+			/* 显示的最大行数 */
+			overflow: hidden;
+		}
+
+		.type-box {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			color: #AAAAAA;
+			font-size: 24rpx;
+			padding: 28rpx 0;
+			border-bottom: 1rpx solid #F0F0F0;
+		}
+
+		.intro {
+			.intro-title {
+				margin: 24rpx 0;
+				font-weight: bold;
+				color: #222222;
+				font-size: 28rpx;
+			}
+
+			.desc {
+				font-size: 24rpx;
+				font-weight: 400;
+				color: #222222;
+				line-height: 40rpx;
+			}
+		}
+
+		.catalogue {
+			.item {
+				display: flex;
+				align-items: center;
+				padding: 20rpx 0;
+				border-bottom: 1rpx solid #F0F0F0;
+
+				.num.active {
+					color: #3879F9;
+				}
+
+				.num {
+					font-size: 28rpx;
+					font-weight: bold;
+					color: #AAAAAA;
+				}
+
+				.video-info {
+					flex: 1;
+					margin-left: 18rpx;
+
+					.title.active {
+						color: #3879F9;
+					}
+
+					.title {
+						font-weight: bold;
+						color: #333333;
+						font-size: 28rpx;
+					}
+
+					.info {
+						display: flex;
+						align-items: center;
+						margin-top: 12rpx;
+
+						.icon-box {
+							display: flex;
+							align-items: flex-end;
+							font-size: 24rpx;
+							color: #AAAAAA;
+							margin-right: 36rpx;
+
+							.icon {
+								width: 28rpx;
+								height: 28rpx;
+							}
+
+							.text {
+								margin-left: 6rpx;
+							}
+						}
+					}
+				}
+
+				.btn {
+					width: 40rpx;
+					height: 40rpx;
+				}
+			}
+		}
+
+
+	}
+}
+</style>

+ 302 - 0
pages/activity/detail.vue

@@ -0,0 +1,302 @@
+<template>
+  <view class="page">
+    <zs-header color="#000"></zs-header>
+    <view class="cover">
+      <image :src="goods.goodsPath" mode="aspectFit"></image>
+    </view>
+    <view class="title-box">
+      <view class="title">
+        {{ goods.goodsName }}
+      </view>
+      <view class="sub-title">
+        {{ goods.goodsDescribe }}
+      </view>
+    </view>
+    <view class="class-box">
+      <view class="class-title">
+        <view class="title">课程目录</view>
+        <view class="class-length">共{{ courses.length }}节课</view>
+      </view>
+
+
+      <view v-for="item in courses" class="class-item" @click="goCourse(item.id)">
+        <view class="image">
+          <image :src="item.courseImg" mode="aspectFit"></image>
+        </view>
+        <view class="item-info">
+          <view class="item-title">{{ item.courseName }}</view>
+          <view class="item-desc">{{ item.courseMsg }}</view>
+          <view class="item-progress">学习进度 {{ item.per }}%</view>
+        </view>
+      </view>
+
+    </view>
+
+    <view class="class-box">
+      <view class="class-title">
+        <view class="title">研学心得</view>
+        <view v-if="communityItemDetailVo" @click="edit(communityItemDetailVo)" class="class-length">编辑</view>
+      </view>
+      <view v-if="!communityItemDetailVo" class="slogan">
+        行走的课堂,心灵的旅行。快来记录你的心路历程吧
+      </view>
+      <view v-if="!communityItemDetailVo" class="btn">
+        <u-button shape="circle" customStyle="keepStudy" @click="toShare">记录研学历程</u-button>
+      </view>
+      <view v-if="communityItemDetailVo" class="community-item">
+        <view class="community-text">
+          {{ communityItemDetailVo.communityDetail }}
+        </view>
+        <view class="community-img" v-if="communityItemDetailVo.urls.length > 0">
+          <image v-for="url in communityItemDetailVo.urls.filter((e, i) => i <= 3)" :src="url" mode="widthFix"></image>
+        </view>
+      </view>
+    </view>
+
+    <view class="tab-group">
+      <view class="tab active" @click="handleTab(1)">
+        商品详情
+      </view>
+    </view>
+    <view class="desc-box">
+      <rich-text class="goods-desc" :nodes="goods.goodsDetail"></rich-text>
+    </view>
+
+  </view>
+</template>
+
+<script>
+import { getStudyDetail } from '@/api/activity'
+export default {
+  data() {
+    return {
+      goods: {
+
+      },
+      communityItemDetailVo: null,
+      courses: [],
+      orderNo: null,
+    };
+  },
+  methods: {
+    toShare() {
+      uni.navigateTo({
+        url: '/share/sharePosts?orderNo=' + this.orderNo
+      });
+    },
+    // 去课程详情页
+    goCourse(id) {
+      uni.navigateTo({
+        url: `./courseDetail?id=${id}&orderNo=${this.orderNo}`
+      })
+    },
+    getDetail(orderNo) {
+      getStudyDetail(orderNo).then(res => {
+        if (res.success) {
+          this.orderNo = orderNo
+          this.goods = res.content.goodsInfoVo
+          this.courses = res.content.courses
+          this.communityItemDetailVo = res.content.communityItemDetailVo
+          this.goods.goodsDetail = res.content.goodsInfoVo.goodsDetail.replace(/<img/gi, '<img class="img_class" ')
+        }
+      })
+    },
+    edit(row){
+      uni.navigateTo({
+        url: `/share/sharePosts?orderNo=${this.orderNo}&id=${row.id}`
+      });
+    }
+  },
+  onLoad({ orderNo }) {
+    this.getDetail(orderNo)
+  },
+  onShow() {
+    if (this.orderNo) {
+      this.getDetail(this.orderNo)
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.page {
+  background: #F9F9F9;
+  min-height: 100vh;
+
+  .cover {
+    height: 750rpx;
+    width: 750rpx;
+
+    image {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  .title-box {
+    margin: -176rpx 20rpx 0 20rpx;
+
+    .title {
+      font-size: 36rpx;
+      color: #FFFFFF;
+      font-weight: bold;
+      margin-bottom: 15rpx;
+    }
+
+    .sub-title {
+      font-size: 24rpx;
+      color: #FFFFFF;
+      font-weight: bold;
+    }
+  }
+
+  .class-box {
+    background: #fff;
+    border-radius: 20rpx 20rpx 0 0;
+    margin-top: 20rpx;
+    padding: 28rpx 20rpx 0 20rpx;
+    overflow: overlay;
+
+
+    .class-title {
+      display: flex;
+      justify-content: space-between;
+      
+      .title {
+        font-size: 32rpx;
+        color: #222222;
+        font-weight: bold;
+      }
+
+      .class-length {
+        font-size: 24rpx;
+        color: #AAAAAA;
+      }
+    }
+
+    .class-item {
+      display: flex;
+      justify-content: space-between;
+
+      .image {
+        width: 218rpx;
+        height: 164rpx;
+        margin-right: 20rpx;
+
+        image {
+          width: 100%;
+          height: 100%;
+          border-radius: 16rpx;
+        }
+      }
+
+      .item-info {
+        width: 500rpx;
+
+        .item-title {
+          font-size: 28rpx;
+          color: #222222;
+          font-weight: bold;
+          margin-bottom: 16rpx;
+        }
+
+        .item-desc {
+          font-size: 24rpx;
+          color: #AAAAAA;
+          margin-bottom: 40rpx;
+        }
+
+        .item-progress {
+          font-size: 24rpx;
+          color: #AAAAAA;
+        }
+      }
+
+      padding: 20rpx 0;
+      border-bottom: 1px solid #F0F0F0;
+
+      &:last-child {
+        border-bottom: none;
+      }
+    }
+
+    .slogan {
+      font-size: 24rpx;
+      color: #AAAAAA;
+      margin-top: 100rpx;
+      text-align: center;
+      margin-bottom: 28rpx;
+    }
+
+    .btn {
+      display: flex;
+      justify-content: center;
+      margin-bottom: 168rpx;
+      padding: 0 184rpx;
+    }
+
+    .community-item {
+
+
+      .community-text {
+        color: #222222;
+        font-size: 24rpx;
+        margin-top: 20rpx;
+        word-break: break-all;
+        display: -webkit-box;
+        -webkit-box-orient: vertical;
+        -webkit-line-clamp: 5;
+        /* 显示的最大行数 */
+        overflow: hidden;
+      }
+
+      .community-img {
+        margin-top: 22rpx;
+        display: flex;
+        overflow-y: scroll;
+
+        image {
+          width: 164rpx;
+          height: 164rpx;
+          border-radius: 16rpx;
+          margin-right: 18rpx;
+        }
+      }
+    }
+  }
+
+  .tab-group {
+    display: flex;
+    background: #fff;
+    margin-top: 20rpx;
+
+    .tab {
+      padding: 28rpx 0;
+      font-size: 32rpx;
+      color: #222222;
+      font-weight: Bold;
+      margin-left: 20rpx;
+    }
+
+    .tab.active {
+      font-weight: 600;
+    }
+  }
+
+}
+</style>
+<style lang="scss">
+.desc-box {
+  padding: 0 20rpx 20rpx 20rpx;
+
+  .goods-desc {
+    color: #222222;
+    font-size: 24rpx;
+
+    .img_class {
+      max-width: 100% !important;
+      vertical-align: bottom;
+    }
+  }
+}
+</style>

+ 361 - 86
pages/activity/index.vue

@@ -1,115 +1,390 @@
 <template>
-	<view class="activity">
-		<zs-list mt="0" @load="loadMore" :status="status">
-			<view class="item" v-for="item in list" :key="item.id" @click="goDetail(item.id)">
-				<zs-img :src="item.activityCover" width="710rpx" height="320rpx"></zs-img>
-				<view class="info">
-					<view class="title">
-						{{item.activityName}}
+	<view style="height: 100%;" @touchstart="startTouch" @touchmove="moveTouch" @touchend="endTouch">
+		<zs-skeleton type="activity" :loading="loading"></zs-skeleton>
+		<view class="zs-header" :style="{ height: statusBarHeight + navBareight + 'px', background: '#fff' }">
+			<view class="title">
+				<view @click="changeTab(0)" :class="['title-text', headerTab === 0 ? 'active' : '']">
+					平台活动
+				</view>
+				<view @click="changeTab(1)" :class="['title-text', headerTab === 1 ? 'active' : '']">
+					我的活动
+				</view>
+			</view>
+		</view>
+		<view :style="{ marginTop: statusBarHeight + navBareight + 'px' }">
+			<view class="activity" v-if="headerTab === 0">
+				<zs-list mt="0" @load="loadMore" :status="status">
+					<view class="item" v-for="item in list" :key="item.id" @click="goDetail(item.id)">
+						<zs-img :src="item.activityCover" width="710rpx" height="320rpx"></zs-img>
+						<view class="info">
+							<view class="title">
+								{{ item.activityName }}
+							</view>
+							<view class="desc">
+								活动时间: {{ item.activityStartTime }} 至 {{ item.activityEndTime }}
+							</view>
+						</view>
 					</view>
-					<view class="desc">
-					活动时间: {{ item.activityStartTime }} 至 {{ item.activityEndTime }}
+				</zs-list>
+			</view>
+			<view class="activity" v-if="headerTab === 1">
+				<zs-list mt="0" @load="loadEventMore" :status="eventStatus">
+					<view class="event-item" v-for="item in eventList" :key="item.id" @click="toDetailStudy(item.orderNo)">
+						<view class="info">
+							<view class="content">
+								<view class="main">
+									<view class="title">
+										{{ item.goodsInfoVo.goodsName || '低碳环保-云漫湖·森哒星生态度假哒星生态度哒星生态度哒星生态度' }}
+									</view>
+									<view class="description">
+										{{ item.goodsInfoVo.goodsDescribe || '景观中国天眼探索宇宙深度的秘密宇宙深度的秘宇宙深度的秘宇宙深度的秘宇宙深度的秘' }}
+									</view>
+									<view class="itinerary">
+										出发时间 {{ $u.timeFormat(item.reserveTime, 'yyyy-mm-dd hh:MM:ss') }}·{{ item.stateStr }}
+									</view>
+								</view>
+								<view class="image">
+									<img :src="item.goodsInfoVo.goodsPath || 'https://via.placeholder.com/82'" alt="活动图片">
+								</view>
+							</view>
+							<view class="footer">
+								<view class="slogan">
+									研学旅行规划先行,请合理安排时间
+								</view>
+								<view class="action">
+									立即学习
+								</view>
+							</view>
+						</view>
 					</view>
-				</view>
+				</zs-list>
 			</view>
-			
-		</zs-list>
+
+		</view>
+
 	</view>
 </template>
 
 <script>
-	import {activityList} from '@/api/activity'
-	export default {
-		data() {
-			return {
-				status: 'more',
-				list:[],
-				query:{
-					currentPage:1,
-					pageSize:10,
-					shopId:0,
-					state:2
-				}
-				
-			}
-		},
-		methods: {
-			loadMore(){
-				this.activityList()
+import { activityList, studyList } from '@/api/activity'
+export default {
+	data() {
+		return {
+			status: 'more',
+			eventStatus: 'more',
+			list: [],
+			eventList: [],
+			query: {
+				currentPage: 1,
+				pageSize: 10,
+				shopId: 0,
+				state: 2
 			},
-			goDetail(id){
-				uni.navigateTo({
-					url:'/signUp/index?scene='+id
-				})
+			eventQuery: {
+				currentPage: 1,
+				pageSize: 10,
+				shopId: 0,
+				state: 2
 			},
-			activityList(){
-				this.status = 'loading'
-				activityList(this.query).then(res=>{
-					if(res.state == 'Success'){
-						this.list = this.list.concat(res.content.records)
-						if (this.list.length >= res.content.total) {
-							this.status = 'noMore'
-						} else {
-							this.status = 'more'
-							this.query.pageCurrent++
-						}
+			loading: true,
+			statusBarHeight: 20,
+			navBareight: 45,
+			headerTab: 0,
+			startX: 0,
+			moveX: 0
+		}
+	},
+	methods: {
+		loadMore() {
+			this.activityList()
+		},
+		loadEventMore() {
+			this.getEventList()
+		},
+		goDetail(id) {
+			uni.navigateTo({
+				url: '/signUp/index?scene=' + id
+			})
+		},
+		goStudyDetail(id) {
+			uni.navigateTo({
+				url: '/pages/activity/detail?scene=' + id
+			})
+		},
+		activityList() {
+			this.status = 'loading'
+			activityList(this.query).then(res => {
+				if (res.state == 'Success') {
+					this.list = this.list.concat(res.content.records)
+					if (this.list.length >= res.content.total) {
+						this.status = 'noMore'
+					} else {
+						this.status = 'more'
+						this.query.pageCurrent++
 					}
-				})
-			}
+				}
+				this.loading = false
+			})
+		},
+		getEventList() {
+			this.eventStatus = 'loading'
+			studyList(this.eventQuery).then(res => {
+				if (res.state == 'Success') {
+					this.eventList = this.eventList.concat(res.content.records)
+					if (this.eventList.length >= res.content.total) {
+						this.eventStatus = 'noMore'
+					} else {
+						this.eventStatus = 'more'
+						this.eventQuery.pageCurrent++
+					}
+				}
+			})
 		},
-		onLoad() {
-			// this.activityList()
+		changeTab(v) {
+			this.headerTab = v
+			uni.vibrateShort()
 		},
-		onShareTimeline() {
-		  return {
-		    title: "慧研学惠生活",
-		    query: "id=1",
-		  };
+		startTouch(e) {
+			this.startX = e.changedTouches[0].clientX;
 		},
-		onShareAppMessage() {
-		  return {
-		    title: "慧研学惠生活",
-		    path: "/pages/activity/index",
-		  };
+		moveTouch(e) {
+			this.moveX = e.changedTouches[0].clientX - this.startX;
+		},
+		endTouch(e) {
+			console.log(this.moveX, this.headerTab)
+			if (Math.abs(this.moveX) > 90) { // 滑动距离超过90px时才进行切换  
+				if (this.moveX > 0) {
+					// 向右滑动  
+					if (this.headerTab === 0) {
+						this.headerTab = 1
+					} else {
+						this.headerTab = 0
+					}
+				} else {
+					// 向左滑动  
+					if (this.headerTab === 1) {
+						this.headerTab = 0
+					} else {
+						this.headerTab = 1
+					}
+				}
+				this.startX = 0; // 重置起始位置
+				this.moveX = 0; // 重置移动距离
+			}
 		},
+		toDetailStudy(orderNo) {
+			console.log('====================================');
+			console.log(orderNo);
+			console.log('====================================');
+			uni.navigateTo({
+				url: '/pages/activity/detail?orderNo=' + orderNo
+			})
+		}
+	},
+	onLoad() {
+		// this.activityList()
+	},
+	onShareTimeline() {
+		return {
+			title: "慧研学惠生活",
+			query: "id=1",
+		};
+	},
+	onShareAppMessage() {
+		return {
+			title: "慧研学惠生活",
+			path: "/pages/activity/index",
+		};
+	},
+	created() {
+		//获取手机系统信息 -- 状态栏高度
+		let {
+			statusBarHeight,
+		} = uni.getSystemInfoSync()
+		this.statusBarHeight = statusBarHeight
+		//去除
+		//#ifndef H5 || MP-ALIPAY ||APP-PLUS
+		//获取小程序胶囊的高度
+		let {
+			top,
+			bottom,
+			left
+		} = uni.getMenuButtonBoundingClientRect()
+		//高度 =(胶囊底部高低-状态栏高度)+(胶囊顶部高度-状态栏内的高度)
+		this.navBareight = (bottom - statusBarHeight) + (top - statusBarHeight)
+		//#endif
+		this.headerTab = 0
 	}
+}
 </script>
 
 <style lang="scss" scoped>
-	.activity {
-		padding: 20rpx;
-		background: #F9F9F9;
-		min-height: 100vh;
-		.zs-list {
-			.item{
-				margin-bottom: 20rpx;
-				border-radius: 16rpx 16rpx 16rpx 16rpx;
-				.info{
-					background: #fff;
-					padding: 20rpx 24rpx;
-					.title{
-						font-size: 28rpx;
-						color: #222222;
-						width: 640rpx;
-						white-space: nowrap;
+.zs-header {
+	position: fixed;
+	width: 100%;
+	// height: calc(44px + env(safe-area-inset-top));
+	top: 0;
+	left: 0;
+	z-index: 999;
+	display: flex;
+	align-items: flex-end;
+	justify-content: center;
+	box-sizing: border-box;
+	padding: 6px;
+	transition: background .3s ease-in-out;
+
+	.back {
+		width: 30rpx;
+		height: 30rpx;
+		position: absolute;
+		bottom: 10px;
+		// top: calc(16px + env(safe-area-inset-top));
+		left: 20rpx;
+	}
+
+	.title {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+
+		.title-text {
+			font-size: 32rpx;
+			color: #333333;
+			font-weight: 400;
+
+			&:nth-child(1) {
+				margin-right: 10rpx;
+			}
+
+			&:nth-child(2) {
+				margin-left: 10rpx;
+			}
+		}
+
+		.active {
+			font-weight: bold;
+			color: black;
+
+			&::after {
+				content: '';
+				display: block;
+				width: 100%;
+				height: 4rpx;
+				background: $uni-color-primary;
+				border-radius: 2rpx 2rpx 0 0;
+			}
+		}
+	}
+}
+
+.activity {
+	padding: 20rpx;
+	background: #F9F9F9;
+	min-height: 100vh;
+
+	.zs-list {
+		.item {
+			margin-bottom: 20rpx;
+			border-radius: 16rpx 16rpx 16rpx 16rpx;
+
+			.info {
+				background: #fff;
+				padding: 20rpx 24rpx;
+
+				.title {
+					font-size: 28rpx;
+					color: #222222;
+					width: 640rpx;
+					white-space: nowrap;
+					overflow: hidden;
+					text-overflow: ellipsis;
+				}
+
+				.desc {
+					font-size: 24rpx;
+					color: #AAAAAA;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					/* 弹性伸缩盒子模型显示 */
+					display: -webkit-box;
+					/* 限制在一个块元素显示的文本的行数 */
+					-webkit-line-clamp: 2;
+					/* 设置或检索伸缩盒对象的子元素的排列方式 */
+					-webkit-box-orient: vertical;
+					margin-top: 20rpx;
+				}
+			}
+		}
+
+		.event-item {
+			margin-bottom: 20rpx;
+			border-radius: 16rpx;
+
+			.info {
+				background: #fff;
+				padding: 24rpx;
+
+				.content {
+					display: flex;
+					justify-content: space-between;
+
+					.main {
+						width: 70%;
+
+						.title {
+							font-size: 28rpx;
+							color: #222222;
+							font-weight: bold;
+							white-space: nowrap;
+							overflow: hidden;
+							text-overflow: ellipsis;
+						}
+
+						.description,
+						.itinerary {
+							font-size: 24rpx;
+							color: #AAAAAA;
+							overflow: hidden;
+							text-overflow: ellipsis;
+							/* 弹性伸缩盒子模型显示 */
+							display: -webkit-box;
+							/* 限制在一个块元素显示的文本的行数 */
+							-webkit-line-clamp: 2;
+							/* 设置或检索伸缩盒对象的子元素的排列方式 */
+							-webkit-box-orient: vertical;
+							margin-top: 20rpx;
+						}
+					}
+
+					.image {
+						width: 164rpx;
+						height: 164rpx;
+						border-radius: 16rpx;
 						overflow: hidden;
-						text-overflow: ellipsis;
+
+						img {
+							width: 100%;
+							height: 100%;
+						}
 					}
-					.desc{
+				}
+
+				.footer {
+					display: flex;
+					justify-content: space-between;
+					margin-top: 20rpx;
+					background: #F9F9F9;
+					border-radius: 16rpx;
+					padding: 24rpx;
+
+					.slogan,
+					.action {
 						font-size: 24rpx;
 						color: #AAAAAA;
-						overflow: hidden;
-						text-overflow: ellipsis;
-						/* 弹性伸缩盒子模型显示 */
-						display: -webkit-box;
-						/* 限制在一个块元素显示的文本的行数 */
-						-webkit-line-clamp: 2;
-						/* 设置或检索伸缩盒对象的子元素的排列方式 */
-						-webkit-box-orient: vertical;
-						margin-top: 20rpx;
 					}
 				}
 			}
 		}
 	}
+}
 </style>

File diff suppressed because it is too large
+ 29 - 0
pages/dev/index.vue


+ 10 - 1
pages/index/index.vue

@@ -465,7 +465,16 @@
 		onHide() {
 		},
 		onLoad(query){
-			 uni.setStorageSync('inviteCode',decodeURIComponent(query.scene))
+			if(query.scene){
+				if(!uni.getStorageSync('token')){
+					uni.showToast({
+						title: '已经是平台用户',
+						icon: 'none'
+					});
+					return
+				}
+				uni.setStorageSync('inviteCode',decodeURIComponent(query.scene))
+			}
 		},
 		onShareTimeline() {
 		  return {

+ 119 - 0
share/index.vue

@@ -0,0 +1,119 @@
+<template>
+  <view class="share">
+    <zs-list mt="0" @load="loadShareMore" :status="shareStatus">
+      <view class="share-item" v-for="item in shareList" :key="item.id">
+        <view class="header">
+          <view class="avatar">
+            <u-avatar class="img" size="60" :src="item.imgPath"></u-avatar>
+            <view class="nickname">
+              {{ item.nickname }}
+            </view>
+          </view>
+          <view class="date">
+            {{ item.showTime }}
+          </view>
+        </view>
+        <view class=content>
+          {{ item.communityDetail }}
+        </view>
+        <view class="pics">
+          <u-album :urls="item.urls" :multipleSize="232"></u-album>
+        </view>
+      </view>
+    </zs-list>
+
+  </view>
+</template>
+
+<script>
+import { goodsSharePage } from '@/api/study'
+export default {
+  data() {
+    return {
+      shareStatus: 'more',
+      shareList: [],
+      shareQuery: {
+        currentPage: 1,
+        pageSize: 10,
+        goodsId: 0,
+        state: 2
+      },
+      loading: true,
+
+    };
+  },
+  methods: {
+    loadShareMore() {
+      this.getshareList()
+    },
+    getshareList() {
+      this.shareStatus = 'loading'
+      goodsSharePage(this.shareQuery).then(res => {
+        if (res.state == 'Success') {
+          this.shareList = this.shareList.concat(res.content.records)
+          if (this.shareList.length >= res.content.total) {
+            this.shareStatus = 'noMore'
+          } else {
+            this.shareStatus = 'more'
+            this.shareQuery.pageCurrent++
+          }
+        }
+      })
+    },
+  },
+  onLoad({ goodsId }) {
+    this.shareQuery.goodsId = goodsId
+    this.getshareList()
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.share {
+  border-top: 1px solid #F0F0F0;
+  min-height: 100vh;
+  background-color: #F9F9F9;
+
+  .share-item {
+    padding: 20rpx;
+    margin-bottom: 20rpx;
+    background: #FFFFFF;
+
+    .header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 24rpx;
+
+      .avatar {
+        display: flex;
+        align-items: center;
+
+        .nickname {
+          font-size: 28rpx;
+          color: #222222;
+          margin-left: 16rpx;
+        }
+      }
+
+      .date {
+        font-size: 24rpx;
+        color: #AAAAAA;
+      }
+
+
+    }
+    .content {
+      font-size: 28rpx;
+      color: #222222;
+      margin-bottom: 24rpx;
+    }
+    .pics{
+      border-radius: 16rpx;
+      overflow: hidden;
+      // background-color: red;
+    }
+  }
+  margin-bottom: 20rpx;
+}
+</style>

+ 225 - 0
share/sharePosts.vue

@@ -0,0 +1,225 @@
+<template>
+  <view class="sharePosts">
+    <zs-header title="研学心得" color="#000"></zs-header>
+    <view class="inputPosts">
+      
+      <u--input border="none" fontSize="44rpx" placeholderClass="placeholderClass" :customStyle="{
+        borderBottom: '1px solid #f0f0f0',
+        height: '120rpx'
+      }" placeholder="请填写标题" v-model="form.communityTitle"></u--input>
+
+      <u--textarea autoHeight border="none" v-model="form.communityDetail" placeholder="快来记录你的心路历程"></u--textarea>
+     
+      <u-upload uploadIcon="plus" width="164" height="164" :fileList="fileList" @afterRead="afterRead"
+        @delete="deletePic" name="1" :maxCount="9" @deletePic="deletePic" @oversize="oversize" accept="image"
+        :maxSize="10485760" previewImage="true"></u-upload>
+    </view>
+    <view class="buy-box">
+      <button class="buy-btn" :loading="loading" type="default" @click="handleUpLoad">确定</button>
+    </view>
+  </view>
+</template>
+
+<script>
+import {
+  uploadImg, finishUploadImg,
+} from '@/api/common.js';
+import { goodsSaveCommunityItem,goodsCommunityItemDetail } from "@/api/study"
+import crypto from 'crypto-js';
+import { Base64 } from 'js-base64';
+export default {
+  data() {
+    return {
+      fileList: [],
+      loading: false,
+      form: {
+        id: '',
+        orderNo: '',
+        communityTitle: '',
+        communityDetail: '',
+        urls: []
+      }
+    };
+  },
+  methods: {
+    oversize() {
+      uni.showToast({
+        title: '图片过大',
+        icon: 'none'
+      });
+    },
+    async afterRead(event) {
+      console.log(event)
+      const { file } = event
+      this.fileList.push(
+        {
+          ...file,
+          status: 'uploading',
+          message: '上传中'
+        }
+      )
+
+      uploadImg({
+        "fineName": file.url.substr(file.url.lastIndexOf('/') + 1)
+      }).then(res => {
+        if (res.state == 'Success') {
+
+          const date = new Date();
+          date.setHours(date.getHours() + 1);
+          const policyText = {
+            expiration: date.toISOString(), // 设置policy过期时间。
+            conditions: [
+              // 限制上传大小。
+              ["content-length-range", 0, 1024 * 1024 * 1024],
+            ],
+          };
+
+          let fileName = file.url.substr(file.url.lastIndexOf('/') + 1);
+          const host = 'https://' + res.content.bucket + '.' + res.content.endPoint;
+          const policy = Base64.encode(JSON.stringify(policyText));
+          const signature = crypto.enc.Base64.stringify(crypto.HmacSHA1(policy, res.content.token.accessKeySecret));;
+          const filePath = file.url; // 待上传文件的文件路径。
+          let paths = res.content.paths[0]
+          // paths.pop()
+          uni.uploadFile({
+            url: host, // 开发者服务器的URL。
+            filePath: filePath,
+            name: 'file', // 必须填file。
+            formData: {
+              key: paths.join('/'),
+              policy,
+              success_action_status: '200', //让服务端返回200,不然,默认会返回204
+              OSSAccessKeyId: res.content.token.accessKeyId,
+              signature,
+              'x-oss-security-token': res.content.token.securityToken // 使用STS签名时必传。
+            },
+            success: (res1) => {
+              if (res1.statusCode === 200) {
+                console.log('上传成功');
+                // 告知服务器上传成功地址
+                finishUploadImg(
+                  [{
+                    path: res.content.paths[0]
+                  }]).then(msg => {
+                    this.fileList.forEach((item, index) => {
+                      if (item.thumb == file.thumb) {
+                        this.fileList[index].status = 'success'
+                        this.fileList[index].message = ''
+                        this.fileList[index].url = host + '/' + paths.join('/')
+                      }
+                    })
+                    console.log(this.fileList)
+                  })
+              }
+            },
+            fail: err => {
+              console.log(err);
+            }
+          });
+        }
+      })
+
+
+    },
+    deletePic({ index }) {
+      this.fileList.splice(index, 1)
+    },
+    handleUpLoad() {
+      if (this.form.communityTitle && this.form.communityDetail) {
+        this.form.urls = this.fileList.map(item => item.url)
+        this.loading = true
+        uni.showLoading({
+          title: '保存中'
+        });
+        goodsSaveCommunityItem({
+          ...this.form,
+          urls: this.fileList.map(e => e.url)
+        }).then(res => {
+          this.loading = false
+          uni.hideLoading();
+          if (res.state == 'Success') {
+            uni.showToast({
+              title: '上传成功',
+              icon: 'success'
+            });
+            setTimeout(() => {
+              uni.navigateBack()
+            }, 1000)
+          }
+        })
+
+      } else {
+        uni.showToast({
+          title: '请填写标题和内容',
+          icon: 'none'
+        });
+      }
+    },
+    getPostDetail(id,orderNo){
+      goodsCommunityItemDetail(id).then(res=>{
+        if(res.state == 'Success'){
+          this.form = res.content
+          this.form.id = id
+          this.form.orderNo = orderNo
+          this.fileList = res.content.urls.map(e=>{
+            return {
+              url:e,
+              status:'success'
+            }
+          })
+        } 
+      })
+    }
+  },
+  onLoad({ orderNo,id }) {
+    this.form.orderNo = orderNo;
+    if(id){
+      this.getPostDetail(id,orderNo)
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.sharePosts {
+  background: #FFFFFF;
+
+  .inputPosts {
+    margin-top: 174rpx;
+    padding: 20rpx;
+    border-top: 1px solid #f0f0f0;
+  }
+}
+
+/deep/ .placeholderClass {
+  color: #AAAAAA;
+  font-size: 44rpx;
+}
+
+/deep/ .u-textarea {
+  padding: 40rpx 0 !important;
+}
+
+.buy-box {
+  position: fixed;
+  bottom: 0%;
+  left: 0%;
+  width: 100%;
+  background: #fff;
+  padding: 10rpx 24rpx env(safe-area-inset-bottom);
+  box-sizing: border-box;
+
+  .buy-btn {
+    width: 702rpx;
+    height: 80rpx;
+    line-height: 80rpx;
+    text-align: center;
+    background: #3B83FF;
+    border-radius: 40rpx;
+    font-weight: 600;
+    font-size: 28rpx;
+    color: #FFFFFF;
+  }
+
+}
+</style>

BIN
static/check.png


BIN
static/eglass-check.png


BIN
static/user-edit.png


+ 139 - 89
study/index.vue

@@ -1,9 +1,13 @@
 <template>
 	<view class="study">
+		<zs-header  title="慧研学" color="#000"></zs-header>
 		<view class="top-box">
 			<image class="bg" src="https://hyxhsh.oss-cn-chengdu.aliyuncs.com/63b7c68b71a69169d1b33f92/store/bdb/user/avatar/06qgjHTlTxWEb95dd5bef869e62f2a78c625ea6bef78.jpg/1.jpg" mode=""></image>
 			<view class="type-box">
-				<view class="type-item" v-for="item in typeList" :key="item.id" @click="handleType(item.id,item.secondaryImg)">
+				<view class="type-item" v-for="item in typeList" :key="item.id" @click="handleType(item.id,{
+					img:item.secondaryImg,
+					name:item.columnName
+				})">
 					<image class="icon" :src="item.columnImg" mode=""></image>
 					<view class="label">
 						{{item.columnName}}
@@ -11,85 +15,19 @@
 				</view>
 			</view>
 		</view>
-<!-- 		<view class="type-box">
-			<view class="left">
-				<view class="type-item" @click="handleType(singleType.id,singleType.secondaryImg)">
-					<view class="glass" :style="{'--colour':singleType.colour}">
-						<view class="title">
-							{{singleType.columnName}}
-						</view>
-						<view class="type">
-							{{singleType.columnMsg}}
-						</view>
-					</view>
-					<image class="icon" :src="singleType.columnImg" mode="center"></image>
-				</view>
-				
-			</view>
-			<view class="right">
-					<view class="type-item" v-for="item in typeList" :key="item.id" @click="handleType(item.id,item.secondaryImg)">
-						<view class="glass" :style="{'--colour':item.colour}">
-							<view class="title">
-								{{item.columnName}}
-							</view>
-							<view class="type">
-								{{item.columnMsg}}
-							</view>
-						</view>
-						<image class="icon" :src="item.columnImg" mode="center"></image>
-					</view>
-					
-			</view>
-		</view> -->
-		
-		<swiper class="swiper" @change="swiperChange" :indicator-dots="true" circular :autoplay="true"
-			:interval="3000" :duration="1000">
-			<swiper-item v-for="(item,index) in bannerList" :key="index">
-				<image class="swiper-item" mode="" :src="item.bannerImg">
-				</image>
-			</swiper-item>
-				
-		</swiper>
-		
-		<view class="type-title more-title">
-			互动社区
-			
-			<view class="more-box" @click="jump">
-				<view>
-					更多
-				</view>
-				<image class="more" src="../static/jiantou-icon.png" mode=""></image>
-			</view>
-		</view>
+
+
 		<scroll-view  class="tab-box" enable-flex scroll-x >
-			<view class="tab" :class="[tab == item.value?'active':'']" v-for="(item,index) in tabList" :key="index" @click="handleTab(item.value)">
+			<!-- <view class="tab" :class="[tab == item.value?'active':'']" v-for="(item,index) in tabList" :key="index" @click="handleTab(item.value)">
 				{{item.label}}
+			</view>  -->
+			<view class="tab active">
+				猜你喜欢
 			</view>
 			<view class="tab" @click="handleTest">
-				研学MBTI测试
+				MBTI测试
 			</view>
 		</scroll-view>
-		<view class="list">
-			<view class="item" v-for="item in communityList" :key="item.tabId" @click="goDetail(item)">
-				<zs-img :src="item.coverImg" width="344rpx" height="256rpx"></zs-img>
-				<view class="info">
-					<view class="title">
-						{{item.communityTitle}}
-					</view>
-					<view class="user-info">
-						<image class="head" :src="require('@/static/logo.png')||item.publishLogo" mode=""></image>
-						<view class="user-name">
-							{{item.publishUser|| "官方"}}
-						</view>
-					</view>
-				</view>
-			</view>
-			
-		</view>
-		
-		<view class="type-title">
-			精选推荐
-		</view>
 		
 		<!-- 列表 -->
 		<zs-list class="store-box" mt="0" @load="loadMore" :status="status">
@@ -101,12 +39,30 @@
 							{{item.goodsName}}
 						</view>
 						<view class="user-info">
-							<image class="head" :src="item.logoPath"></image>
+							<!-- <image class="head" :src="item.logoPath"></image> -->
 							<view class="user-name">
 								{{item.shopName}}
 							</view>
 						</view>
+						<view class="price-box">
+						<view class="left">
+							<view class="unit">
+								¥
+							</view>
+							<view class="price">
+							{{item.realPrice}}
+							</view>
+							<view class="old-price">
+							¥{{item.marketPrice}}
+							</view>
+						</view>
+						
+						<view class="right">
+							{{item.saleNum}}人已购
+						</view>
+					</view>
 					</view>
+				
 				</view>
 			</view>
 			<view class="right">
@@ -118,12 +74,29 @@
 							{{item.goodsName}}
 						</view>
 						<view class="user-info">
-							<image class="head" :src="item.logoPath" mode=""></image>
+							<!-- <image class="head" :src="item.logoPath" mode=""></image> -->
 							<view class="user-name">
 								{{item.shopName}}
 							</view>
 						</view>
+						<view class="price-box">
+						<view class="left">
+							<view class="unit">
+								¥
+							</view>
+							<view class="price">
+							{{item.realPrice}}
+							</view>
+							<view class="old-price">
+							¥{{item.marketPrice}}
+							</view>
+						</view>
+						<view class="right">
+							{{item.saleNum}}人已购
+						</view>
+					</view>
 					</view>
+				
 				</view>
 			</view>
 		
@@ -141,9 +114,9 @@
 			return {
 				status: 'more',
 				singleType:{
-					 columnName: "",
-					 columnMsg: "",
-					 columnImg: "",
+					columnName: "",
+					columnMsg: "",
+					columnImg: "",
 				},
 				typeList:[],
 				list: [],
@@ -165,11 +138,11 @@
 			// swiper变动
 			swiperChange(val) {
 			},
-			handleType(id,img){
+			handleType(id,{img,name}){
 				uni.navigateTo({
 					url:'/study/type?id='+id,
 					success(res) {
-						res.eventChannel.emit('img', img)
+						res.eventChannel.emit('img', img, name)
 					}
 				})
 			},
@@ -304,6 +277,7 @@
 				height: 480rpx;
 				position: relative;
 				left: -20rpx;
+				border-radius: 0 0 5% 5%;
 			}
 			.type-box{
 				display: flex;
@@ -373,15 +347,16 @@
 			.tab{
 				flex-shrink: 0;
 				padding: 10rpx 24rpx;
+				margin-top: 28rpx;
 				color: #AAAAAA;
 				background: #EEEEEE;
 				font-size: 28rpx;
 				margin-right: 20rpx;
-				border-radius: 30rpx;
+				border-radius: 8rpx;
 			}
 			.tab.active{
-				color: #3B83FF;
-				background: #CEE0FF;
+				color: #222222;
+				// background: #CEE0FF;
 			}
 		}
 		
@@ -393,20 +368,23 @@
 				box-shadow: 0rpx 0rpx 24rpx 2rpx rgba(0,0,0,0.08);
 				border-radius: 16rpx;
 				width: 344rpx;
-				height: 424rpx;
+				height: 566rpx;
 				margin-top: 20rpx;
 				display: flex;
 				flex-direction: column;
 				.info{
 					padding: 20rpx;
+					height: auto;
 					flex: 1;
 					display: flex;
 					flex-direction: column;
 					justify-content: space-between;
 					.title{
 						color: #222222;
-						font-size: 24rpx;
+						font-size: 28rpx;
+						height: 76rpx;
 						width: 100%;
+						font-weight: bold;
 						display: -webkit-box;
 						-webkit-box-orient: vertical;
 						-webkit-line-clamp: 2;      /* 显示的最大行数 */
@@ -423,11 +401,42 @@
 						}
 						.user-name{
 							color: #AAAAAA;
+							font-size: 24rpx;
+							// margin-left: 12rpx;
+						}
+					}
+					.price-box{
+						display: flex;
+						justify-content: space-between;
+						align-items: center;
+						margin-top: 12rpx;
+						.left{
+							display: flex;
+							align-items: flex-end;
+							.unit{
+								font-size: 24rpx;
+								color: $uni-color-primary;
+								font-weight: bold;
+							}
+							.price{
+								font-size: 32rpx;
+								color: $uni-color-primary;
+								font-weight: bold;
+							}
+							.old-price{
+								font-size: 20rpx;
+								color: #AAAAAA;
+								text-decoration: line-through;
+								margin-left: 10rpx;
+							}
+						}
+						.right{
 							font-size: 20rpx;
-							margin-left: 12rpx;
+							color: #AAAAAA;
 						}
 					}
 				}
+			
 			}
 		}
 		
@@ -447,13 +456,22 @@
 				box-shadow: 0rpx 0rpx 24rpx 2rpx rgba(0,0,0,0.08);
 				border-radius: 16rpx;
 				width: 344rpx;
+				height: 566rpx;
 				margin-top: 20rpx;
+				display: flex;
+				flex-direction: column;
 				.info{
 					padding: 20rpx;
+					flex: 1;
+					display: flex;
+					flex-direction: column;
+					justify-content: space-between;
 					.title{
 						color: #222222;
-						font-size: 24rpx;
+						font-size: 28rpx;
+						height: 76rpx;
 						width: 100%;
+						font-weight: bold;
 						display: -webkit-box;
 						-webkit-box-orient: vertical;
 						-webkit-line-clamp: 2;      /* 显示的最大行数 */
@@ -470,11 +488,43 @@
 						}
 						.user-name{
 							color: #AAAAAA;
+							font-size: 24rpx;
+							overflow: hidden;
+							// margin-left: 12rpx;
+						}
+					}
+					.price-box{
+						display: flex;
+						justify-content: space-between;
+						align-items: center;
+						margin-top: 12rpx;
+						.left{
+							display: flex;
+							align-items: flex-end;
+							.unit{
+								font-size: 24rpx;
+								color: $uni-color-primary;
+								font-weight: bold;
+							}
+							.price{
+								font-size: 32rpx;
+								color: $uni-color-primary;
+								font-weight: bold;
+							}
+							.old-price{
+								font-size: 20rpx;
+								color: #AAAAAA;
+								text-decoration: line-through;
+								margin-left: 10rpx;
+							}
+						}
+						.right{
 							font-size: 20rpx;
-							margin-left: 12rpx;
+							color: #AAAAAA;
 						}
 					}
 				}
+			
 			}
 		}
 		

+ 694 - 0
study/pay/order.vue

@@ -0,0 +1,694 @@
+<template>
+	<view class="pay">
+
+		<view class="pay-info">
+			<view class="goods-info">
+				<image class="goods-img" :src="info.goodsPath" mode=""></image>
+				<view class="info">
+					<view class="goods-name">
+						{{ info.goodsName }}
+					</view>
+					<view class="num">
+						{{ info.goodsDescribe }}
+					</view>
+					<view class="priceColumn">
+						<view class="price">
+							<text class="unit">¥</text>{{ info.realPrice }}
+						</view>
+						<view class="goodsNum">
+							x{{ realPay.reservePersons.length }}
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<view class="setoff">
+				<view class="text">
+					出发时间
+				</view>
+				<view class="date">
+					{{ reserveTimeFmt }}
+				</view>
+			</view>
+		</view>
+
+		<view class="appointment">
+			<view class="title">
+				预定人信息
+			</view>
+			<view class="name">
+				<view class="label">
+					姓名
+				</view>
+				<view class="value">
+					{{ userInfo.name }}
+				</view>
+			</view>
+			<view class="phone">
+				<view class="label">
+					手机号
+				</view>
+				<view class="value">
+					<text class="zone">+86</text>{{ userInfo.phoneNum }}
+				</view>
+			</view>
+			<view class="desc">
+				预定人信息用于接受验证信息
+			</view>
+		</view>
+		<view class="tour">
+			<view class="title-top">
+				<view class="title-text">
+					出游人信息
+				</view>
+				<view @click="addNewTourPerson" class="add">
+					+添加
+				</view>
+			</view>
+			<view v-for="(item, index) in realPay.reservePersons" :key="index" class="info-card">
+				<view class="user-name">
+					{{ item.userName }}
+					<view class="tag">
+						出行人{{ index + 1 }}
+					</view>
+				</view>
+				<view class="card-info">
+					身份证:{{ item.idCard }}
+				</view>
+			</view>
+
+		</view>
+
+		<view class="btn-box">
+			<view class="total-price">
+				<view class="label">
+					合计:
+				</view>
+				<view class="price">
+					¥ {{ payInfo.price }}
+				</view>
+			</view>
+
+			<button class="btn" type="default" :loading="loading" @click="creat">提交订单</button>
+		</view>
+		<uni-calendar ref="calendar" :lunar="true" :showMonth="true" style="z-index: 999;" :insert="false"
+			@confirm="confirm" />
+	</view>
+</template>
+
+<script>
+
+import {
+	studyCalculate, saveReserve
+} from '@/api/order.js';
+import {
+	creat
+} from '@/api/goods.js'
+import {
+	creatPayOrder,
+	queryPayOrder,
+	payDetails
+} from '@/api/payment.js'
+import guid from '@/utils/guid.js'
+export default {
+	data() {
+		return {
+			isVisual: false,//是否是虚拟商品
+			loading: false,
+			info: {},
+			realPay: {
+				goodsCouponName: '', //优惠券名
+				couponGoodsLogId: '', //优惠券id
+				goodsId: '', //商品id
+				price: '', //支付价格
+				offset: '', //实际抵扣值
+				discount: '', //优惠券值
+				condition: '', //优惠条件
+				originalPrice: '', //原价
+				reserveName: '',//预定人
+				reservePhone: '',//预定人手机
+				reserveTime: '',//预定时间
+				reservePersons: [],//出游人
+			},
+			form: {
+				account: ''
+			},
+			rules: {
+				'account': {
+					type: 'string',
+					required: true,
+					message: '请输入充值账号',
+					trigger: ['blur', 'change']
+				},
+			},
+			query: {
+				"msgType": "wx.unifiedOrder",
+				"orderDesc": "测试",
+				"orderNo": "",
+				"channel": 'ZhongShu',
+				"subOpenId": "",
+				"userId": ""
+			},
+			// 支付信息
+			payData: {
+
+			},
+			userInfo: {}
+		}
+	},
+	computed: {
+		// 正式支付时的信息
+		payInfo() {
+			return {
+				goodsCouponName: this.realPay.goodsCouponName || '', //优惠券名
+				couponGoodsLogId: this.info.couponId, //优惠券id
+				goodsId: this.realPay.goodsId || this.info.goodsId, //商品id
+				price: this.realPay.price, //支付价格
+				offset: this.realPay.offset, //实际抵扣值
+				discount: this.realPay.discount || 0, //优惠券值
+				condition: this.realPay.condition || '', //优惠条件
+				originalPrice: this.realPay.originalPrice, //原价
+			}
+		},
+		reserveTimeFmt() {
+			const date = new Date(this.info.reserveTime)
+			// const year = date.getFullYear()
+			const month = date.getMonth() + 1
+			const day = date.getDate()
+			return `${month}月${day}日`
+		}
+	},
+	methods: {
+		// 增加新的出游人
+		addNewTourPerson() {
+			uni.navigateTo({
+				url: '/study/tourList/list'
+			})
+		},
+		openCalendar() {
+			this.$refs.calendar.open();
+		},
+		confirm(e) {
+			console.log(e);
+		},
+		calculate(goodsId, shopId) {
+			uni.showLoading({
+				title: '计算中'
+			})
+			return new Promise((resolve, reject) => {
+				studyCalculate({
+					couponId: this.info.couponId,
+					"goodsId": goodsId,
+					"shopId": shopId,
+					"userId": JSON.parse(uni.getStorageSync('userInfo')).userId,
+					"number": this.realPay.reservePersons.length
+				}).then(res => {
+					uni.hideLoading()
+					if (res.state == 'Success') {
+						const reservePersons = this.realPay.reservePersons
+						this.realPay = res.content
+						this.realPay.reservePersons = reservePersons
+						this.info.couponId = res.content.couponId
+					}
+				})
+			})
+		},
+		choose() {
+			let that = this
+			uni.navigateTo({
+				url: './coupon?couponId=' + this.realPay.couponLogId,
+				success: function (res) {
+					// 通过eventChannel向被打开页面传送数据
+					res.eventChannel.emit('pay', that.info)
+				}
+			})
+		},
+		tempCreate() {
+			uni.reLaunch({
+				url: '/study/pay/orderPay'
+			})
+		},
+		//创建订单
+		creat() {
+			if (this.loading) return
+
+			if (this.realPay.reservePersons.length == 0) {
+				uni.showToast({
+					title: '请添加出游人',
+					icon: 'none'
+				})
+				return
+			}
+
+			const user = JSON.parse(uni.getStorageSync('userInfo'))
+			this.realPay.reserveName = user.name
+			this.realPay.reservePhone = user.phoneNum
+			this.realPay.reserveTime = this.info.reserveTime
+
+			this.loading = true
+			uni.showLoading({
+				title: '支付中'
+			})
+			let that = this
+			if (!this.payData.timeStamp) {
+				// 处理扩展字段  暂时只有研学商品和视频会员  视频会员是字符串的JSON  研学是字符串
+				let extend
+				try {
+					if (JSON.parse(this.info.extend)) {
+						extend = this.info.extend
+					}
+				} catch (e) {
+					extend = this.info.reservationTime || ''
+				}
+
+				creat({
+					discountId: (this.payInfo.couponGoodsLogId || this.payInfo.couponGoodsLogId == -1) ? [this
+						.payInfo.couponGoodsLogId
+					] : [],
+					extend,
+					channel: 'ZhongShu',
+					goodsList: this.info.goodsId ? [this.info.goodsId] : [],
+					idempotent: guid(),
+					shopId: this.info.shopId
+				}).then(res => {
+					this.loading = false
+					if (res.state == 'Success') {
+						if (!this.payInfo.price) { //价格为0
+							uni.hideLoading()
+							uni.reLaunch({
+								url: '/my/order/index'
+							})
+						} else {
+							this.query.orderNo = res.content.orderNo
+							this.query.subOpenId = JSON.parse(uni.getStorageSync('userInfo')).openId
+							this.query.orderDesc = this.info.goodsName
+							creatPayOrder(this.query).then(data => {
+								that.payData = JSON.parse(data.content.miniPayRequest)
+								if (data.content.miniPayRequest == null) return uni.hideLoading()
+								saveReserve({
+									...this.realPay,
+									orderNo: this.query.orderNo,
+									goodsId: this.info.goodsId,
+								}).then(res => {
+									if (res.state == 'Success') {
+										uni.requestPayment({
+											"provider": "wxpay",
+											"orderInfo": that.payData,
+											"appid": that.payData
+												.appId, // 微信开放平台 - 应用 - AppId,注意和微信小程序、公众号 AppId 可能不一致
+											"paySign": that.payData.paySign,
+											"nonceStr": that.payData.nonceStr, // 随机字符串
+											"package": that.payData.package, // 固定值
+											// "prepayid":  that.payData.package, // 统一下单订单号 
+											"timeStamp": that.payData.timeStamp, // 时间戳(单位:秒)
+											"signType": that.payData.signType, //签名算法
+											success(msg) {
+												console.log('msg', that.query.orderNo);
+												queryPayOrder(that.query.orderNo).then(res1 => {
+													if (res1.state == 'Success') {
+														uni.hideLoading()
+														uni.reLaunch({
+															url: '/my/order/index'
+														})
+													}
+												})
+											},
+											fail(e) {
+												console.log('err', e);
+												that.loading = false
+												uni.hideLoading()
+												uni.showToast({
+													title: '取消支付',
+													icon: 'fail'
+												})
+												// 取消支付后,获取支付信息以备再次支付
+												payDetails(that.query.orderNo).then(r => {
+													if (r.state == 'Success') {
+														that.payData = JSON.parse(r.content.miniPayRequest)
+													}
+												})
+											}
+										})
+									} else {
+										uni.showToast({
+											title: res.msg,
+											icon: 'none'
+										})
+										that.loading = false
+									}
+								})
+							})
+						}
+
+					}
+				})
+			} else { // 取消支付后再次支付
+				uni.requestPayment({
+					"provider": "wxpay",
+					"orderInfo": that.payData,
+					"appid": that.payData.appId, // 微信开放平台 - 应用 - AppId,注意和微信小程序、公众号 AppId 可能不一致
+					"paySign": that.payData.paySign,
+					"nonceStr": that.payData.nonceStr, // 随机字符串
+					"package": that.payData.package, // 固定值
+					// "prepayid":  that.payData.package, // 统一下单订单号 
+					"timeStamp": that.payData.timeStamp, // 时间戳(单位:秒)
+					"signType": that.payData.signType, //签名算法
+					success(msg) {
+						console.log('msg', msg);
+						queryPayOrder(that.query.orderNo).then(res1 => {
+							if (res1.state == 'Success') {
+								uni.hideLoading()
+								uni.reLaunch({
+									url: '/my/order/index'
+								})
+							}
+						})
+					},
+					fail(e) {
+						that.loading = false
+						uni.hideLoading()
+						uni.showToast({
+							title: '取消支付',
+							icon: 'fail'
+						})
+						// 取消支付后,获取支付信息以备再次支付
+						payDetails(that.query.orderNo).then(r => {
+							if (r.state == 'Success') {
+								that.payData = JSON.parse(r.content.miniPayRequest)
+							}
+						})
+						console.log('err', e);
+					}
+				})
+			}
+		}
+
+	},
+	onReady() {
+
+	},
+	onLoad() {
+		let userInfo = JSON.parse(uni.getStorageSync('userInfo'))
+		this.query.userId = userInfo.userId
+		this.userInfo = userInfo
+		let that = this
+		const eventChannel = this.getOpenerEventChannel();
+		eventChannel.on('pay', function (data) {
+			that.info = data
+			try {
+				let extend = JSON.parse(that.info.extend)
+				if (extend.account) {
+					that.isVisual = true
+					that.form.account = extend.account
+				}
+			} catch (e) {
+				//TODO handle the exception
+			}
+			console.log('data', data);
+			// that.calculate(that.info.goodsId, that.info.shopId)
+		})
+		uni.$on("updateData", (data) => {
+			this.realPay.reservePersons = data
+			that.calculate(that.info.goodsId, that.info.shopId)
+			this.$forceUpdate()
+		})
+	},
+	destoryed() {
+		uni.$off("updateData")
+	}
+}
+</script>
+
+<style lang="scss">
+.pay {
+	background: #F9F9F9;
+	min-height: 100vh;
+	padding-top: 20rpx;
+
+	.shop-info {
+		margin: 0 30rpx 20rpx;
+		width: 690rpx;
+		padding: 24rpx;
+		box-sizing: border-box;
+		background: #FFFFFF;
+		border-radius: 16rpx 16rpx 16rpx 16rpx;
+
+		.shop-name {
+			color: #181818;
+			font-size: 32rpx;
+		}
+
+		.address {
+			color: #999999;
+			font-size: 24rpx;
+			margin-top: 12rpx;
+		}
+	}
+
+	.pay-info {
+		margin: 0 30rpx;
+		background: #FFFFFF;
+		border-radius: 16rpx 16rpx 16rpx 16rpx;
+		padding: 24rpx;
+
+		.goods-info {
+			display: flex;
+			margin-bottom: 26rpx;
+
+			.goods-img {
+				width: 164rpx;
+				height: 164rpx;
+				border-radius: 16rpx;
+			}
+
+			.info {
+				margin-left: 28rpx;
+				flex: 1;
+
+				.goods-name {
+					font-weight: bold;
+					color: #181818;
+					font-size: 32rpx;
+					width: 100%;
+					word-break: break-all;
+					text-overflow: ellipsis;
+					overflow: hidden;
+					display: -webkit-box;
+					-webkit-box-orient: vertical;
+					-webkit-line-clamp: 2;
+					/* 这里是超出几行省略 */
+				}
+
+				.num {
+					color: #999999;
+					font-size: 24rpx;
+					margin-top: 16rpx;
+				}
+
+				.priceColumn {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					margin-top: 30rpx;
+
+					.price {
+						font-weight: bold;
+						color: #FF4D3A;
+						font-size: 32rpx;
+
+						.unit {
+							font-size: 20rpx;
+						}
+					}
+
+					.goodsNum {
+						font-size: 24rpx;
+						color: #AAAAAA;
+					}
+
+				}
+
+			}
+		}
+
+		.setoff {
+			border-top: 1px solid #F0F0F0;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			font-size: 24rpx;
+			padding-top: 24rpx;
+
+			.text {
+				color: #222222;
+			}
+
+			.date {
+				color: #AAAAAA;
+			}
+		}
+	}
+
+	.appointment {
+		padding: 24rpx;
+		background: #FFFFFF;
+		margin: 20rpx 30rpx;
+		border-radius: 16rpx 16rpx 16rpx 16rpx;
+
+		.title {
+			font-size: 28rpx;
+			color: #222222;
+			font-weight: bold;
+			margin-bottom: 24rpx;
+		}
+
+		.name {
+			border-top: 1px solid #F0F0F0;
+			padding: 24rpx 0;
+			display: flex;
+
+			.label {
+				font: 24rpx;
+				color: #222222;
+				width: 152rpx;
+			}
+
+			.value {
+				font-size: 28rpx;
+				font-weight: bold;
+			}
+		}
+
+		.phone {
+			border-top: 1px solid #F0F0F0;
+			padding: 24rpx 0;
+			display: flex;
+
+			.label {
+				font: 24rpx;
+				color: #222222;
+				width: 152rpx;
+			}
+
+			.value {
+				font-size: 28rpx;
+				font-weight: bold;
+
+				.zone {
+					padding-right: 22rpx;
+					margin-right: 20rpx;
+					border-right: 1px solid #F0F0F0;
+				}
+			}
+		}
+
+		.desc {
+			border-top: 1px solid #F0F0F0;
+			padding-top: 24rpx;
+			color: #AAAAAA;
+			font-size: 24rpx;
+		}
+	}
+
+	.tour {
+		padding: 24rpx;
+		background: #FFFFFF;
+		margin: 0 30rpx;
+		border-radius: 16rpx 16rpx 16rpx 16rpx;
+
+		.title-top {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+
+			.title-text {
+				font-size: 28rpx;
+				font-weight: bold;
+				color: #222222;
+			}
+
+			.add {
+				font-size: 24rpx;
+				color: #3B83FF;
+			}
+
+			margin-bottom: 24rpx;
+		}
+
+		.info-card {
+			border-top: 1px solid #F0F0F0;
+			padding: 24rpx 0;
+
+			.user-name {
+				font-size: 28rpx;
+				font-weight: bold;
+				color: #222222;
+				display: flex;
+				align-items: center;
+
+				.tag {
+					margin-left: 20rpx;
+					color: #222222;
+					font-size: 20rpx;
+					background-color: #F0F0F0;
+					border-radius: 18rpx;
+					line-height: 20rpx;
+					padding: 2rpx 12rpx;
+				}
+			}
+
+			.card-info {
+				margin: 20rpx 0;
+				font-size: 24rpx;
+				color: #222222;
+			}
+		}
+
+	}
+
+
+	.btn-box {
+		position: fixed;
+		bottom: 0%;
+		left: 0%;
+		width: 100%;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		box-sizing: border-box;
+		padding: 10rpx 30rpx env(safe-area-inset-bottom);
+		border-top: 1rpx solid #EEEEEE;
+
+		.total-price {
+			display: flex;
+			align-items: center;
+
+			.label {
+				color: #181818;
+				font-size: 28rpx;
+			}
+
+			.price {
+				font-size: 36rpx;
+				font-weight: 800;
+				color: #FF4848;
+				margin-left: 10rpx;
+			}
+		}
+
+		.btn {
+			width: 280rpx;
+			height: 80rpx;
+			line-height: 80rpx;
+			text-align: center;
+			background: #3B83FF;
+			box-shadow: inset 0rpx 6rpx 12rpx 2rpx rgba(255, 255, 255, 0.16);
+			border-radius: 46rpx 46rpx 46rpx 46rpx;
+			font-weight: 800;
+			color: #FFFFFF;
+			font-size: 28rpx;
+			margin: 0;
+		}
+	}
+}
+</style>

+ 727 - 0
study/pay/orderPay.vue

@@ -0,0 +1,727 @@
+<template>
+  <view class="pay">
+    <view class="state" v-if="info.goodsList[0].goodsState == 'WAIT_PAYMENT' && !isNotTime">
+      等待支付,剩余<u-count-down :time="closeTime" format="mm:ss" @finish="finish"></u-count-down>
+    </view>
+    <view class="state" v-else>
+      {{ info.goodsList[0] | filterType }}
+    </view>
+
+    <view class="pay-info">
+      <view class="goods-info">
+        <image class="goods-img"
+          src="http://zswl-dev.oss-cn-chengdu.aliyuncs.com/63b7c68b71a69169d1b33f92/store/65fe8e5874d67a7dbd842c5a/ext/GOODS_INFO/School.png/5.png"
+          mode=""></image>
+        <view class="info">
+          <view class="goods-name">
+            {{ info.goodsList[0].goodsInfo.goodsName }}
+          </view>
+          <view class="num">
+            {{ info.goodsList[0].goodsInfo.goodsDescribe }}
+          </view>
+          <view class="priceColumn">
+            <view class="price">
+              <text class="unit">¥</text>{{ info.goodsList[0].goodsInfo.realPrice }}
+            </view>
+            <view class="goodsNum">
+              x{{ reserve.persons.length || 1 }}
+            </view>
+          </view>
+        </view>
+      </view>
+
+      <view class="setoff">
+        <view class="text">
+          出发时间
+        </view>
+        <view class="date" @click="openCalendar">
+          {{ reserve.reserveTime }}
+        </view>
+      </view>
+    </view>
+
+    <view class="card-box"
+      v-if="info.goodsList[0].jobFlowMap != 'P802' && info.goodsList[0].goodsState == 'WAIT_USE' && info.goodsList[0].goodsName != '二维码支付'">
+      <view class="title-top">
+        <view class="title-text">
+          券码信息
+        </view>
+      </view>
+      <view class="code-box">
+        <view class="qrcode">
+          <uqrcode ref="uqrcode" :size="qrsize" type="2d" auto canvas-id="qrcode" :value="codeData" :loading="loading">
+            <template v-slot:loading>
+              <text style="color: black;">拼命加载中...</text>
+            </template>
+          </uqrcode>
+        </view>
+        <view class="coode">{{ codeData }}</view>
+      </view>
+
+    </view>
+
+
+    <view class="card-box">
+      <view class="title-top">
+        <view class="title-text">
+          预定人信息
+        </view>
+      </view>
+
+      <view class="info border">
+        <view class="label">
+          姓名
+        </view>
+        <view class="value">
+          {{ reserve.reserveName }}
+        </view>
+      </view>
+      <view class="info">
+        <view class="label">
+          手机号
+        </view>
+        <view class="value">
+          {{ reserve.reservePhone }}
+        </view>
+      </view>
+
+    </view>
+
+
+    <view class="card-box">
+      <view class="title-top">
+        <view class="title-text">
+          出游人信息
+        </view>
+      </view>
+
+      <template v-for="item in reserve.persons">
+        <view class="info border">
+          <view class="label">
+            姓名
+          </view>
+          <view class="value">
+            {{ item.userName }}
+          </view>
+        </view>
+        <view class="info">
+          <view class="label">
+            手机号
+          </view>
+          <view class="value">
+            {{ item.phone }}
+          </view>
+        </view>
+      </template>
+    </view>
+
+    <view class="card-box">
+      <view class="title-top">
+        <view class="title-text">
+          订单信息
+        </view>
+      </view>
+
+      <view class="info ">
+        <view class="label">
+          订单编号
+        </view>
+        <view class="value">
+          {{ info.orderNo }}
+        </view>
+      </view>
+      <view class="info">
+        <view class="label">
+          付款方式
+        </view>
+        <view class="value">
+          {{ info.payment | filterPay }}
+        </view>
+      </view>
+
+      <view class="info ">
+        <view class="label">
+          下单时间
+        </view>
+        <view class="value">
+          {{ $u.timeFormat(info.createTime, 'yyyy-mm-dd hh:MM:ss') }}
+        </view>
+      </view>
+      <view class="info">
+        <view class="label">
+          支付时间
+        </view>
+        <view class="value">
+          {{ payTime }}
+        </view>
+      </view>
+
+    </view>
+
+    <view class="card-box" style="margin-bottom: 160rpx;">
+      <view class="title-top">
+        <view class="title-text">
+          交易信息
+        </view>
+      </view>
+
+      <view class="info ">
+        <view class="label">
+          金额
+        </view>
+        <view class="value">
+          ¥{{ info.totalAmount }}
+        </view>
+      </view>
+      <view class="info">
+        <view class="label">
+          优惠券
+        </view>
+        <view class="value">
+          -¥{{ info.discountAmount }}
+        </view>
+      </view>
+
+      <view class="info ">
+        <view class="label">
+          合计
+        </view>
+        <view class="value">
+          ¥{{ info.payAmount }}
+        </view>
+      </view>
+    </view>
+
+    <view class="btn-box" v-if="info.goodsList[0].goodsState == 'WAIT_PAYMENT'">
+      <button class="cancel-btn" @click="cancel" :loading="btnLoading">
+        取消订单
+      </button>
+      <button class="btn" @click="pay" :loading="btnLoading">
+        立即支付
+      </button>
+    </view>
+
+  </view>
+</template>
+
+<script>
+import { qrCode, getReserve } from '@/api/order.js'
+import { payDetails, queryPayOrder, unRefund, refundIntervene } from '@/api/payment.js';
+import { cancelOrder } from '@/api/refuel.js'
+export default {
+  data() {
+    return {
+      info: {
+        goodsList: []
+      },
+      reserve: {
+        persons: [],
+      },
+      codeData: '123',
+      show: false,
+      loading: false,
+      pageLoading: true,
+      btnLoading: false,
+      btnLoading1: false,
+      oldBright: 0,
+      isNotTime: false,
+      qrsize: uni.upx2px(200)
+    };
+  },
+  methods: {
+    // 申请客服介入
+    refundIntervene() {
+      let that = this
+      uni.showModal({
+        title: '提示',
+        content: '确认申请客服介入吗?',
+        success: function (res) {
+          if (res.confirm) {
+            that.btnLoading1 = true
+            refundIntervene(that.info.goodsList[0].id).then(res => {
+              that.btnLoading1 = false
+              if (res.state == 'Success') {
+                that.payDetails(that.info.orderNo)
+              }
+            })
+          } else if (res.cancel) {
+            console.log('用户点击取消');
+          }
+        }
+      });
+
+    },
+    isRefund() {
+      if (!this.info.goodsList[0].verifyModel) {//没核销
+        return true
+      } else if (this.info.goodsList[0].verifyModel && (new Date().getTime() < (this.info.goodsList[0].verifyModel.checkTime + 1000 * 60 * 60 * 48))) {// 已核销 并且没超过48小时
+        return true
+      } else {
+        return false
+      }
+    },
+    finish() {
+      this.isNotTime = true
+      this.payDetails(this.info.orderNo)
+    },
+    refundDetail() {
+      uni.navigateTo({
+        url: `/my/order/refundDetail?id=${this.info.orderNo}`
+      })
+    },
+    checkCode() {
+      this.codeData = ''
+      this.$nextTick(() => {
+
+        this.show = true
+        this.loading = true
+        let that = this
+        // uni.getScreenBrightness({
+        // 	success(res) {
+        // 		// 获取用户手机亮度 保存起来 
+        // 		that.oldBright = res.value
+        // 		setTimeout(()=>{
+        // 			uni.setScreenBrightness({
+        // 				value:1
+        // 			})
+        // 		},200)
+        // 	}
+        // })
+
+        qrCode(this.info.goodsList[0].id).then(res => {
+          this.loading = false
+          if (res.state == 'Success') {
+            this.codeData = res.content
+          }
+        })
+
+      })
+    },
+    close() {
+      this.show = false
+      // uni.setScreenBrightness({
+      // 	value:this.oldBright
+      // })
+    },
+    handleCall() {
+      uni.makePhoneCall({
+        phoneNumber: '4000016553'//仅为示例
+      });
+    },
+    apply() {
+      let that = this
+      if (this.info.goodsList[0].jobFlowMap == 'XiaoJu') {
+        uni.navigateTo({
+          url: './webView'
+        })
+      } else {
+        uni.navigateTo({
+          url: './refund',
+          success: function (res) {
+            // 通过eventChannel向被打开页面传送数据
+            res.eventChannel.emit('orderInfo', that.info)
+          }
+        })
+      }
+    },
+    // 获取订单详情
+    payDetails(orderNo) {
+      payDetails(orderNo).then(res => {
+        this.pageLoading = false
+        this.info = res.content
+        if (this.info.goodsList[0].jobFlowMap == 'P802') {
+          this.isVisual = true
+        }
+        if (!this.info.goodsList[0].refundLog) {
+          this.info.goodsList[0].refundLog = {}
+        }
+        if (this.info.goodsList[0].jobFlowMap != 'P802' && this.info.goodsList[0].goodsState == 'WAIT_USE' && this.info.goodsList[0].goodsName != '二维码支付') {
+
+          qrCode(this.info.goodsList[0].id).then(res => {
+            this.loading = false
+            if (res.state == 'Success') {
+              this.codeData = res.content
+            }
+          })
+        }
+
+      })
+
+      getReserve(orderNo).then(res => {
+        if (res.state == 'Success') {
+          this.reserve = res.content
+        }
+      })
+    },
+    // 取消支付
+    cancel() {
+      uni.showLoading({
+        title: '取消中'
+      })
+
+
+      let obj = {}
+      try {
+        obj = JSON.parse(this.info.goodsList[0].extend)
+      } catch (error) {
+        obj = {}
+      }
+  
+      cancelOrder(obj).then(res => {
+        if (res.state == 'Success') {
+          uni.showToast({
+            title: '取消成功',
+            icon: 'success'
+          })
+          this.payDetails(this.info.orderNo)
+        }
+      })
+    },
+    // 支付
+    pay() {
+      let that = this
+      if (this.info.goodsList[0].jobFlowMap == 'XiaoJu') {
+        let { xjOrderId, tradeId } = JSON.parse(this.info.goodsList[0].extend)
+        uni.navigateToMiniProgram({
+          appId: "wx0d252f6ed9755862", // 滴滴加油小程序appId
+          path: `packageA/pages/open-energy-pay/index?orderId=${xjOrderId}&tradeId=${tradeId}`, // 滴滴加油收银台页面地址,需要拼接orderId和tradeId
+          envVersion: 'release', // 固定release
+        })
+      } else {
+        if (this.btnLoading) return
+        this.btnLoading = true
+        uni.showLoading({
+          title: '支付中'
+        })
+        let miniPayRequest = JSON.parse(this.info.payment.miniPayRequest)
+        uni.requestPayment({
+          "provider": "wxpay",
+          "orderInfo": miniPayRequest,
+          "appid": miniPayRequest.appId, // 微信开放平台 - 应用 - AppId,注意和微信小程序、公众号 AppId 可能不一致
+          "paySign": miniPayRequest.paySign,
+          "nonceStr": miniPayRequest.nonceStr, // 随机字符串
+          "package": miniPayRequest.package, // 固定值
+          // "prepayid":  miniPayRequest.package, // 统一下单订单号 
+          "timeStamp": miniPayRequest.timeStamp, // 时间戳(单位:秒)
+          "signType": miniPayRequest.signType, //签名算法
+          success(msg) {
+            console.log('msg', msg);
+            queryPayOrder(that.info.orderNo).then(res1 => {
+              if (res1.state == 'Success') {
+                uni.hideLoading()
+                uni.showToast({
+                  title: '支付成功',
+                  icon: 'success'
+                })
+                that.btnLoading = false
+                that.payDetails(that.info.orderNo)
+              }
+            })
+          },
+          fail(e) {
+            that.btnLoading = false
+            uni.hideLoading()
+            uni.showToast({
+              title: '取消支付',
+              icon: 'fail'
+            })
+            // 取消支付后,获取支付信息以备再次支付
+            that.payDetails(that.info.orderNo)
+            console.log('err', e, this);
+          }
+        })
+      }
+    },
+    // 取消退款申请
+    cancelReply() {
+      if (this.btnLoading) return
+      this.btnLoading = true
+      uni.showLoading({
+        title: '取消中'
+      })
+      console.log(this.info.goodsList[0])
+      unRefund({ id: this.info.goodsList[0].id }).then(res => {
+        this.btnLoading = false
+        uni.hideLoading()
+
+        if (res.state == 'Success') {
+          this.payDetails(this.info.orderNo)
+          uni.showToast({
+            title: '取消成功',
+            icon: 'success'
+          })
+        }
+      })
+    }
+  },
+  filters: {
+    filterType: function (val) {
+      if (val.refundLog && val.refundLog.refund == 'REFUSAL_REFUND' && !val.change) {
+        return '拒绝退款'
+      }
+      else if (val.goodsState == 'APPLY_REFUND') {
+        return '退款审核中'
+      } else if (val.goodsState == 'CLOSE') {
+        return '关闭订单'
+      } else if (val.goodsState == 'REFUNDED') {
+        return '已退款'
+      } else if (val.goodsState == 'REFUSAL_REFUND') {
+        return '拒绝退款'
+      } else if (val.goodsState == 'APPLY_REFUNDING') {
+        return '退款中'
+      } else if (val.goodsState == 'USED') {
+        return '订单已完成'
+      } else if (val.goodsState == 'WAIT_PAYMENT') {
+        return '待付款'
+      } else if (val.goodsState == 'WAIT_USE') {
+        return '待使用'
+      } else {
+        return ''
+      }
+    },
+    filterPay(val) {
+      if (val) {
+        if (val.paymentWay == 'wx.unifiedOrder') {
+          return '微信支付'
+        } else if (val.paymentWay == 'trade.create') {
+          return '支付宝支付'
+        } else if (val.paymentWay == 'uac.miniOrder') {
+          return '云闪付支付'
+        }
+      }
+      else {
+        return '-'
+      }
+    }
+  },
+  computed: {
+    closeTime() {
+      return (this.info.createTime + 1000 * 60 * 30) - new Date().getTime()
+    },
+    payTime() {
+      if (this.info.goodsList[0].extend && !JSON.parse(this.info.goodsList[0].extend).hasOwnProperty('account')) {
+        if (JSON.parse(this.info.goodsList[0].extend).hasOwnProperty('notifyOrderInfo')) {
+          return JSON.parse(this.info.goodsList[0].extend).notifyOrderInfo.payTime
+        } else {
+          return '-'
+        }
+      } else {
+        if (this.info.payment && this.info.payment.paymentTime) {
+          return uni.$u.timeFormat(this.info.payment.paymentTime, 'yyyy-mm-dd hh:MM:ss')
+        } else {
+          return '-'
+        }
+      }
+    }
+  },
+  onLoad(options) {
+    this.info.orderNo = options.id
+  },
+  onShow() {
+    this.pageLoading = true
+    this.payDetails(this.info.orderNo)
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.pay {
+  background: #F9F9F9;
+  min-height: 100vh;
+  padding: 20rpx;
+
+  .state {
+    display: flex;
+    color: #222222;
+    font-size: 32rpx;
+    font-weight: bold;
+    margin: 8rpx 0 28rpx 0;
+  }
+
+  .pay-info {
+    background: #FFFFFF;
+    border-radius: 16rpx 16rpx 16rpx 16rpx;
+    padding: 24rpx;
+
+    .goods-info {
+      display: flex;
+      margin-bottom: 26rpx;
+
+      .goods-img {
+        width: 164rpx;
+        height: 164rpx;
+        border-radius: 16rpx;
+      }
+
+      .info {
+        margin-left: 28rpx;
+        flex: 1;
+
+        .goods-name {
+          font-weight: bold;
+          color: #181818;
+          font-size: 32rpx;
+          width: 100%;
+          word-break: break-all;
+          text-overflow: ellipsis;
+          overflow: hidden;
+          display: -webkit-box;
+          -webkit-box-orient: vertical;
+          -webkit-line-clamp: 2;
+          /* 这里是超出几行省略 */
+        }
+
+        .num {
+          color: #999999;
+          font-size: 24rpx;
+          margin-top: 16rpx;
+        }
+
+        .priceColumn {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-top: 30rpx;
+
+          .price {
+            font-weight: bold;
+            color: #FF4D3A;
+            font-size: 32rpx;
+
+            .unit {
+              font-size: 20rpx;
+            }
+          }
+
+          .goodsNum {
+            font-size: 24rpx;
+            color: #AAAAAA;
+          }
+
+        }
+
+      }
+    }
+
+    .setoff {
+      border-top: 1px solid #F0F0F0;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      font-size: 24rpx;
+      padding-top: 24rpx;
+
+      .text {
+        color: #222222;
+      }
+
+      .date {
+        color: #AAAAAA;
+      }
+    }
+  }
+
+  .card-box {
+    padding: 24rpx;
+    background: #FFFFFF;
+    margin: 20rpx 0;
+    border-radius: 16rpx 16rpx 16rpx 16rpx;
+
+    .title-top {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+
+      .title-text {
+        font-size: 28rpx;
+        font-weight: bold;
+        color: #222222;
+      }
+
+      // margin-bottom: 24rpx;
+    }
+
+    .border {
+      border-top: 1px solid #F0F0F0;
+      margin-top: 20rpx;
+    }
+
+    .info {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding-top: 20rpx;
+
+      .label {
+        color: #AAAAAA;
+
+        font-size: 24rpx;
+      }
+
+      .value {
+        color: #222222;
+        font-size: 24rpx;
+      }
+    }
+
+    .code-box {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      flex-direction: column;
+
+      .qrcode {
+        width: 200rpx;
+        height: 200rpx;
+      }
+
+      .coode {
+        margin-top: 20rpx;
+        font-size: 28rpx;
+        color: #222222;
+      }
+    }
+  }
+
+  .btn-box {
+    position: fixed;
+    bottom: 0%;
+    left: 0%;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    box-sizing: border-box;
+    padding: 10rpx 24rpx env(safe-area-inset-bottom);
+    background: #fff;
+    border-top: 1rpx solid #EEEEEE;
+
+    .cancel-btn {
+      width: 344rpx;
+      height: 80rpx;
+      line-height: 80rpx;
+      text-align: center;
+      border-radius: 40rpx;
+      font-weight: 600;
+      font-size: 28rpx;
+      border: 1px solid #AAAAAA;
+      color: #AAAAAA;
+      margin: 0;
+    }
+
+    .btn {
+      width: 344rpx;
+      height: 80rpx;
+      line-height: 80rpx;
+      text-align: center;
+      background: #3B83FF;
+      border-radius: 40rpx;
+      font-weight: 600;
+      font-size: 28rpx;
+      color: #FFFFFF;
+      margin: 0;
+      margin-left: 20rpx;
+    }
+  }
+}
+</style>

+ 483 - 222
study/studyGoodsDetail.vue

@@ -2,53 +2,74 @@
 	<view class="goodsDetail">
 		<zs-skeleton type="goodsDetail" :loading="pageLoading"></zs-skeleton>
 		<image class="good-img" :src="info.goodsPath" mode="aspectFit"></image>
-		<view class="price-box">
-			<view class="left">
-				<view class="num-box">
-					<view class="unit">
-						¥
-					</view>
+
+		<view class="content">
+			<view class="price-box">
+				<view class="price">
+					<view class="unit">¥</view>
 					<view class="price">
-						{{info.realPrice}}
+						{{ info.realPrice }}
 					</view>
 				</view>
 				<view class="old-price">
-					市场价{{info.marketPrice}}
+					市场价{{ info.marketPrice }}
 				</view>
 			</view>
-			
-			<view class="right">
-				销量{{info.saleNum}}
-			</view>
-		</view>
-		<view class="goods-info">
-			<view class="goods-name">
-				{{info.goodsName}}
+			<view class="title">
+				{{ info.goodsName }}
 			</view>
 			<view class="desc">
-				{{info.goodsDescribe}}
+				{{ info.goodsDescribe }}
 			</view>
-		</view>
-		
-		<!-- 商品规格 -->
-		<template v-if="info.specs[0].specValues.length">
-			<view class="type-box" v-for="item in info.specs" :key="item.specId">
-				<view class="type-title">
-					{{item.specName}}
+			<view class="ag">
+				<u-avatar-group :urls="urls" size="44" gap="0.1" maxCount="10"></u-avatar-group>
+				<view class="">{{ info.saleNum || 0 }} 人已购</view>
+			</view>
+			<view class="subtitle">
+				出发日期
+			</view>
+
+			<view class="reservation" @click="openCalendar">
+				<view class="text">
+					<text style="color:#AAAAAA;margin-right:28rpx;"> 已预定 </text> {{ reserveTime ? '预定' + reserveTime + '出发' :
+						info.reservationDate || '' }}
+				</view>
+				<view>
+					<image class="more" src="../static/jiantou-icon.png" mode=""></image>
+				</view>
+			</view>
+			<view class="share" v-if="total > 0">
+				<view class="top">
+					<view class="title">研学分享</view>
+					<view class="more" @click="toShare">{{ total }}分享<image class="img" src="../static/jiantou-icon.png" mode="">
+						</image>
+					</view>
 				</view>
-				<view class="type-list">
-					<view class="type-item" :class="[active == d?'active':'' ]" v-for="(i,d) in item.specValues" :key="i.specId" @click="choose(d)">
-						{{i.specValue}}
+				<view class="user">
+					<view class="userinfo">
+						<view class="avatar">
+							<u-avatar size="60" :src="info.share.imgPath"></u-avatar>
+						</view>
+						<view class="username">{{ info.share.nickname }}</view>
+					</view>
+					<view class="date">{{ info.share.showTime }}</view>
+				</view>
+				<view class="share-content">
+					<view class="content-text">{{ info.share.communityDetail }}</view>
+					<view style="position: relative;" @click="clickImg">
+						<image class="content-image" :src="info.share.coverImg">
+						</image>
+						<view class="overlay">+{{ info.share.urls.length }}</view>
 					</view>
 				</view>
 			</view>
-		</template>
-		
+		</view>
+
 		<view class="tab-group">
-			<view class="tab" :class="[tab == 1 ? 'active':'']" @click="handleTab(1)">
+			<view class="tab" :class="[tab == 1 ? 'active' : '']" @click="handleTab(1)">
 				商品详情
 			</view>
-			<view class="tab" :class="[tab == 2 ? 'active':'']" @click="handleTab(2)">
+			<view class="tab" :class="[tab == 2 ? 'active' : '']" @click="handleTab(2)">
 				购买须知
 			</view>
 		</view>
@@ -56,12 +77,12 @@
 			<rich-text class="goods-desc" :nodes="info.goodsDetail"></rich-text>
 		</view>
 		<view class="list" v-show="tab == 2">
-			<view class="item" v-for="(item,index) in info.attrs" :key="index">
+			<view class="item" v-for="(item, index) in info.attrs" :key="index">
 				<view class="label">
-					{{item.attrName == 'validDay'?'有效期':item.attrName}}
+					{{ item.attrName == 'validDay' ? '有效期' : item.attrName }}
 				</view>
-				<view class="value" v-html="filterMsg(item.attrValue,item.attrName)">
-					
+				<view class="value" v-html="filterMsg(item.attrValue, item.attrName)">
+
 				</view>
 			</view>
 			<!-- <view class="item">
@@ -73,266 +94,485 @@
 				</view>
 			</view> -->
 		</view>
-		
-		
+
 		<view class="buy-box">
-			<image class="head" :src="info.shopInfo.logoPath" @click="goShopDetail(shopInfo)" mode=""></image>
+			<!-- <image class="head" :src="shopInfo.logoPath" @click="goShopDetail(shopInfo)" mode=""></image> -->
 			<view class="btn-box">
-				
-			<!-- 	<view class="group-btn" @click="handleBuy">
+				<view class="buy-btn" :class="[info.realStockNum ? '' : 'none']" @click="handleBuy">
 					<view class="label">
-						团购
+						预约咨询
 					</view>
-					<view class="price">
-						¥{{info.realPrice}}
-					</view>
-				</view> -->
-				<view class="buy-btn" :class="[info.realStockNum?'':'none']" @click="handleBuy">
+				</view>
+				<view class="buy-btn" :class="[info.realStockNum ? '' : 'none']" @click="handleBuy">
 					<view class="label">
-					 {{info.realStockNum?'会员专享价':'售罄'}}	
-					</view>
-					<view class="price">
-						¥{{info.realPrice}}
+						{{ info.realStockNum ? '报名支付' : '售罄' }}
 					</view>
 				</view>
 			</view>
 		</view>
-		
+
+		<uni-calendar @monthSwitch="chnageMonth" :selected="selected" ref="calendar" :showMonth="false"
+			style="z-index: 999;" :insert="false" @confirm="confirm">
+		</uni-calendar>
 	</view>
+
+
 </template>
 
 <script>
-	import {detail} from '@/api/goods.js'
-	export default {
-		data() {
-			return {
-				pageLoading:false,
-				active:0,
-				tab:1,
-				shopList:[],
-				info: {
-					specs:[{specValues:[]}]
-				},
-				shopInfo:{}
-			}
-		},
-		methods: {
-			choose(index){
-				this.active = index
-			},
-			filterMsg(val,name){
-				let msg = val.replace(/\n/g, "<br>")
-				if(name == 'validDay'){
-					msg = val + '天'
+import { detail, getStudyAbout, goodsBuyStatistic } from '@/api/goods.js'
+
+export default {
+	data() {
+		return {
+			pageLoading: false,
+			active: 0,
+			tab: 1,
+			shopList: [],
+			info: {
+				share: {
+					urls: []
 				}
-				return msg
 			},
-			jump(url){
+			total: 0,
+			shopInfo: {},
+			urls: [
+				"https://fakeimg.pl/22x22/?text=🫡&font_size=22",
+				"https://fakeimg.pl/22x22/?text=🤣&font_size=22",
+				"https://fakeimg.pl/22x22/?text=😍&font_size=22",
+				"https://fakeimg.pl/22x22/?text=😒&font_size=22",
+				"https://fakeimg.pl/22x22/?text=😉&font_size=22",
+				"https://fakeimg.pl/22x22/?text=😎&font_size=22",
+				"https://fakeimg.pl/22x22/?text=😁&font_size=22",
+				"https://fakeimg.pl/22x22/?text=😘&font_size=22",
+			],
+			goodsId: "",
+			selected: [],
+			reserveTime: "",
+		}
+	},
+	methods: {
+		choose(index) {
+			this.active = index
+		},
+		filterMsg(val, name) {
+			let msg = val.replace(/\n/g, "<br>")
+			if (name == 'validDay') {
+				msg = val + '天'
+			}
+			return msg
+		},
+		jump(url) {
+			uni.navigateTo({
+				url
+			})
+		},
+		handleTab(val) {
+			this.tab = val
+		},
+		handleBuy() {
+			if (!this.reserveTime) {
+				uni.showToast({
+					title: '请选择出发日期',
+					icon: 'none'
+				});
+				this.openCalendar()
+				return
+			}
+			this.info.reserveTime = this.reserveTime
+			if (!this.info.realStockNum) return
+			let that = this
+			if (uni.getStorageSync('token')) {
 				uni.navigateTo({
-					url
+					url: `/study/pay/order`,
+					success: function (res) {
+						// 通过eventChannel向被打开页面传送数据
+						res.eventChannel.emit('pay', that.info)
+					}
 				})
-			},
-			handleTab(val){
-				this.tab = val
-			},
-			handleBuy() {
-				if(!this.info.realStockNum) return
-					let that = this
-				if (uni.getStorageSync('token')) {
-					uni.navigateTo({
-						url:`/pay/pay`,
-						  success: function(res) {
-						    // 通过eventChannel向被打开页面传送数据
-						    res.eventChannel.emit('pay', that.info)
-						  }
+			} else {
+				uni.showModal({
+					title: '请登录',
+					confirmText: '去登录',
+					success(res) {
+						console.log(res);
+						if (res.confirm) {
+							uni.navigateTo({
+								url: `/login/login/login?redirect=/detail/goodsDetail/index&id=${that.info.goodsId}`
+							})
+						}
+					}
+				})
+			}
+
+		},
+		detail(goodsId) {
+			this.pageLoading = true
+			this.goodsId = goodsId
+			detail({ goodsId, resource: 2 }).then(res => {
+				this.pageLoading = false
+				if (res.state == 'Success') {
+					this.info = res.content
+					this.info.goodsDetail = res.content.goodsDetail.replace(/<img/gi, '<img class="img_class" ')
+					uni.setNavigationBarTitle({
+						title: this.info.goodsName
+					})
+					this.chnageMonth({
+						year: new Date().getFullYear(),
+						month: new Date().getMonth() + 1
 					})
-				} else {
-					uni.showModal({
-						title:'请登录',
-						confirmText:'去登录',
-						success(res){
-							console.log(res);
-							if(res.confirm){
-								uni.navigateTo({
-									url:`/login/login/login?redirect=/detail/goodsDetail/index&id=${that.info.goodsId}`
-								})
-							}
+					getStudyAbout({ goodsId }).then(({ content }) => {
+						try {
+							this.urls = content.buyStatisVo?.list?.map(e => e.imgPath);
+						} catch (error) {
+						}
+						this.info.reservationDate = content.msg
+						const { communityVo } = content
+						const { list, total } = communityVo
+						this.total = total
+						const [item] = list
+						this.info.share = {
+							communityDetail: item.communityDetail,
+							coverImg: item.coverImg,
+							nickname: item.nickname,
+							imgPath: item.imgPath,
+							showTime: item.showTime,
+							urls: item.urls
 						}
+						this.$forceUpdate()
 					})
 				}
-			
-			},
-			detail(goodsId){
-				this.pageLoading = true
-				detail({goodsId,resource:2}).then(res=>{
-					this.pageLoading = false
-					if(res.state == 'Success'){
-						this.info = res.content
-						this.info.goodsDetail = res.content.goodsDetail.replace(/<img/gi, '<img class="img_class" ')
-						uni.setNavigationBarTitle({
-							title:this.info.goodsName
-						})
+			})
+
+
+		},
+		goShopDetail(shopInfo) {
+			uni.setStorageSync('shopInfo', JSON.stringify(shopInfo))
+			uni.navigateTo({
+				url: `/detail/shopDetail/shopDetail`
+			})
+		},
+		openCalendar() {
+			this.$refs.calendar.open();
+		},
+		confirm({ fulldate }) {
+			this.reserveTime = fulldate + " 00:00:00"
+		},
+		// 更改月份后获取新月份的可售卖日期
+		chnageMonth({ year, month }) {
+			goodsBuyStatistic({
+				goodsId: this.info.goodsId,
+				year,
+				month
+			}).then(res => {
+				// console.log(res)
+				this.selected = res.content.map(e => {
+					if (e.isOpenSeal) {
+						return {
+							date: `${e.year}-${e.month}-${e.day}`,
+							info: '可选',
+							notic: false,
+							color: '#2979ff'
+						}
+					} else {
+						return {
+							date: `${e.year}-${e.month}-${e.day}`,
+							disable: true,
+							notic: false,
+							color: '#2979ff'
+						}
 					}
 				})
-			},
-			goShopDetail(shopInfo) {
-				uni.setStorageSync('shopInfo', JSON.stringify(this.info.shopInfo))
-				uni.navigateTo({
-					url: `/detail/shopDetail/shopDetail`
-				})
-			},
-			
+			})
+		},
+		clickImg() {
+
+			uni.previewImage({
+				urls: this.info.share.urls,
+				longPressActions: {
+					itemList: ['发送给朋友', '保存图片', '收藏'],
+					success: function (data) {
+						console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
+					},
+					fail: function (err) {
+						console.log(err.errMsg);
+					}
+				}
+			});
 		},
-		onLoad(option) {
-			// this.shopInfo = JSON.parse(uni.getStorageSync('shopInfo')) 
-			this.detail(option.id)
+		toShare() {
+			uni.navigateTo({
+				url: '/share/index?goodsId=' + this.goodsId
+			});
+		}
+	},
+	onLoad(option) {
+		try {
+			this.shopInfo = JSON.parse(uni.getStorageSync('shopInfo'))
+		} catch (error) {
+			console.log("未读取到对应缓存")
 		}
-		
+		this.detail(option.id)
 	}
+
+}
 </script>
 
-<style lang="scss" >
-.goodsDetail{
+<style lang="scss">
+.goodsDetail {
 	background: #F9F9F9;
 	padding-bottom: 200rpx;
 	min-height: 100vh;
-	.good-img{
+
+	.good-img {
 		width: 100%;
 		height: 750rpx;
 		vertical-align: bottom;
 	}
-	.price-box{
-		display: flex;
-		justify-content: space-between;
-		align-items: center;
-		width: 100%;
-		height: 116rpx;
-		box-sizing: border-box;
-		background: #fff;
-		// background-image: url('@/static/price-bg.png');
-		// background-repeat: no-repeat;
-		// background-size: 100% 116rpx;
-		padding: 24rpx 24rpx 0;
-		.left{
+
+	.content {
+		background-color: #FFFFFF;
+		padding: 20rpx;
+		border-radius: 16rpx 16rpx 0rpx 0rpx;
+		margin-top: -16rpx;
+		overflow: overlay;
+
+		.price-box {
 			display: flex;
 			align-items: center;
-			.num-box{
+
+			.price {
 				display: flex;
-				align-items: center;
-				.unit{
-					color: #FF4D3A;
+				align-items: baseline;
+				font-weight: bold;
+
+				.unit {
 					font-size: 20rpx;
-					font-weight: bold;
-					align-self: flex-end;
-					margin-bottom: 10rpx;
+					color: #FF4D3A;
 				}
-				.price{
-					font-weight: 600;
+
+				.price {
 					font-size: 56rpx;
 					color: #FF4D3A;
 				}
 			}
-			.old-price{
+
+			.old-price {
+				font-size: 24rpx;
+				color: #AAAAAA;
 				text-decoration: line-through;
-				margin-left: 12rpx;
-				font-weight: 300;
-				font-size: 20rpx;
+				margin-left: 22rpx;
 			}
 		}
-		.right{
-			font-size: 24rpx;
-			font-weight: 300;
-			font-size: 24rpx;
-		}
-	}
-	.goods-info{
-		padding: 0 24rpx 24rpx;
-		margin-bottom: 20rpx;
-		background: #fff;
-		
-		.goods-name{
-			font-size: 32rpx;
+
+		.title {
+			margin-top: 16rpx;
+			font-size: 36rpx;
 			color: #222222;
 			font-weight: bold;
+			display: -webkit-box;
+			-webkit-box-orient: vertical;
+			-webkit-line-clamp: 2;
+			/* 显示的最大行数 */
+			overflow: hidden;
 		}
-		.desc{
+
+		.desc {
+			margin-top: 16rpx;
 			font-size: 24rpx;
 			color: #AAAAAA;
+			display: -webkit-box;
+			-webkit-box-orient: vertical;
+			-webkit-line-clamp: 2;
+			/* 显示的最大行数 */
 			overflow: hidden;
-			text-overflow: ellipsis;
-			white-space: nowrap;
-			margin-top: 12rpx;
 		}
-	}
-	.type-box{
-		padding: 24rpx;
-		background: #fff;
-		margin-bottom: 20rpx;
-		.type-title{
+
+		.ag {
+			margin: 28rpx 0;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			font-size: 24rpx;
+			color: #AAAAAA;
+		}
+
+		.subtitle {
+			margin-bottom: 20rpx;
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+
+		.reservation {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
 			font-size: 28rpx;
-			color: #222222;
+			padding: 28rpx;
+			background-color: #F9F9F9;
+			margin-bottom: 28rpx;
+			border-radius: 8rpx;
+
+			.text {
+				color: #222222;
+			}
+
+			.more {
+				width: 16rpx;
+				height: 16rpx;
+			}
 		}
-		.type-list{
+
+		.share {
+			padding: 24rpx;
+			background-color: #F9F9F9;
+
+			.top {
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+
+				.title {
+					color: #222222;
+					font-weight: bold;
+					font-size: 32rpx;
+					margin-top: 0;
+				}
+
+				.more {
+					color: #AAAAAA;
+					font-size: 24rpx;
+
+					.img {
+						width: 16rpx;
+						height: 16rpx;
+						margin-left: 20rpx;
+					}
+				}
+
+				margin-bottom: 20rpx;
+			}
+
+
+			.user {
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+				margin-bottom: 16rpx;
+
+				.userinfo {
+					display: flex;
+					align-items: center;
+
+					.avatar {
+						width: 60rpx;
+						height: 60rpx;
+					}
+
+					.username {
+						margin-left: 16rpx;
+						font-size: 28rpx;
+						color: #222222;
+					}
+				}
+
+				.date {
+					font-size: 20rpx;
+					color: #AAAAAA;
+				}
+			}
+
+
+		}
+
+		.share-content {
 			display: flex;
-			flex-wrap: wrap;
 			justify-content: space-between;
-			.type-item{
-				width: 316rpx;
-				height: 52rpx;
+
+			.content-text {
+				padding: 8rpx;
+				width: 70%;
+				font-size: 24rpx;
+				color: #222222;
+				word-break: break-all;
+				display: -webkit-box;
+				-webkit-box-orient: vertical;
+				-webkit-line-clamp: 4;
+				/* 显示的最大行数 */
+				overflow: hidden;
+			}
+
+			.content-image {
+				position: relative;
+				width: 160rpx;
+				height: 160rpx;
+				margin-left: 28rpx;
+				border-radius: 16rpx 16rpx 16rpx 16rpx;
+			}
+
+			.overlay {
+				position: absolute;
+				bottom: 10rpx;
+				right: 0rpx;
+				background: rgba(0, 0, 0, 0.6);
+				color: white;
+				border-radius: 16rpx 0 16rpx 0;
 				text-align: center;
 				line-height: 52rpx;
-				background: #F0F0F0;
-				border-radius: 26rpx 26rpx 26rpx 26rpx;
-				font-weight: 300;
 				font-size: 24rpx;
-				color: #AAAAAA;
-				margin-top: 28rpx;
-			}
-			.type-item.active{
-				background: #EE4320;
-				color: #FFFFFF;
+				font-weight: bold;
+				width: 52rpx;
+				height: 52rpx;
 			}
 		}
+
+
+
 	}
 
-	
-	.tab-group{
+	.tab-group {
 		display: flex;
 		background: #fff;
-		.tab{
-			flex:1;
+
+		.tab {
+			flex: 1;
 			padding: 24rpx 0;
 			text-align: center;
 			font-size: 28rpx;
 			color: #222222;
 		}
-		.tab.active{
+
+		.tab.active {
 			font-weight: 600;
 		}
 	}
-	
-	.desc-box{
-		.goods-desc{
+
+	.desc-box {
+		.goods-desc {
 			color: #222222;
 			font-size: 24rpx;
-			.img_class{
-				max-width: 100%!important;
+
+			.img_class {
+				max-width: 100% !important;
 				vertical-align: bottom;
 			}
 		}
 	}
-	
-	.list{
+
+	.list {
 		background: #fff;
-		.item{
+
+		.item {
 			padding: 24rpx;
-			border-top:1rpx solid #F0F0F0;
-			.label{
+			border-top: 1rpx solid #F0F0F0;
+
+			.label {
 				font-weight: 600;
 				font-size: 28rpx;
 				color: #222222;
 			}
-			.value{
+
+			.value {
 				font-size: 24rpx;
 				color: #AAAAAA;
 				margin-top: 16rpx;
@@ -340,8 +580,8 @@
 			}
 		}
 	}
-	
-	.buy-box{
+
+	.buy-box {
 		position: fixed;
 		bottom: 0%;
 		left: 0%;
@@ -351,16 +591,21 @@
 		display: flex;
 		justify-content: space-between;
 		box-sizing: border-box;
-		.head{
+
+		.head {
 			width: 80rpx;
 			height: 80rpx;
 			background: #aaa;
 			border-radius: 50%;
 		}
-		.btn-box{
+
+		.btn-box {
+			width: 100%;
 			display: flex;
 			text-align: center;
-			.group-btn{
+			justify-content: space-between;
+
+			.group-btn {
 				width: 280rpx;
 				height: 80rpx;
 				background: #6499FF;
@@ -368,22 +613,27 @@
 				display: flex;
 				flex-direction: column;
 				justify-content: space-around;
-				.label{
+
+				.label {
 					font-weight: 600;
 					font-size: 28rpx;
 					color: #FFFFFF;
 				}
-				.price{
+
+				.price {
 					font-size: 24rpx;
 					color: #FFFFFF;
 				}
 			}
-			.buy-btn.none{
+
+			.buy-btn.none {
 				filter: grayscale(1);
 			}
-			.buy-btn{
-				// width: 280rpx;
-				width: 560rpx;
+
+
+			.buy-btn {
+				width: 344rpx;
+				// width: 560rpx;
 				height: 80rpx;
 				background: #3B83FF;
 				border-radius: 40rpx;
@@ -391,18 +641,29 @@
 				display: flex;
 				flex-direction: column;
 				justify-content: space-around;
-				.label{
+
+				&:nth-child(1) {
+					background: transparent;
+					border: 1rpx solid #3B83FF;
+
+					.label {
+						color: #3B83FF;
+					}
+				}
+
+				.label {
 					font-weight: 600;
 					font-size: 28rpx;
 					color: #FFFFFF;
 				}
-				.price{
+
+				.price {
 					font-size: 24rpx;
 					color: #FFFFFF;
 				}
 			}
 		}
 	}
-	
+
 }
 </style>

+ 159 - 0
study/tourList/add.vue

@@ -0,0 +1,159 @@
+<template>
+  <view class="add">
+    <view class="tips">
+      提醒:姓名与身份证一致,否则会影响出游
+    </view>
+
+    <view class="card">
+      <view class="form-item">
+        <view class="label">姓名</view>
+        <u--input customStyle="input" placeholder="请输入姓名" v-model="form.userName" border="none"></u--input>
+      </view>
+      <view class="form-item" @click="show = true">
+        <view class="label">证件类型</view>
+        <u--input customStyle="input" readonly placeholder="请选择证件类型" value="身份证" border="none"></u--input>
+      </view>
+      <view class="form-item">
+        <view class="label">证件号码</view>
+        <u--input customStyle="input" :maxlength="18" placeholder="请输入姓名" v-model="form.idCard"
+          border="none"></u--input>
+      </view>
+      <view class="form-item">
+        <view class="label">手机号码</view>
+        <u--input customStyle="input" :maxlength="11" placeholder="请输入号码" type="idcard" v-model="form.phone"
+          border="none"></u--input>
+      </view>
+      <view class="form-item" @click="showSex = true">
+        <view class="label">性别</view>
+        <u--input customStyle="input" readonly placeholder="请选择性别" v-model="sexLabel" border="none"></u--input>
+      </view>
+    </view>
+
+    <view class="buy-box">
+      <button :loading="loading" class="buy-btn" type="default" @click="handleAdd">确定</button>
+    </view>
+    <u-action-sheet :closeOnClickOverlay="true" @close="show = false" :closeOnClickAction="true" cancelText="取消"
+      :actions="option" :show="show"></u-action-sheet>
+    <u-action-sheet :closeOnClickOverlay="true" @close="showSex = false" :closeOnClickAction="true" cancelText="取消"
+      :actions="sexOption" @select="select" :show="showSex"></u-action-sheet>
+  </view>
+</template>
+
+<script>
+
+import { saveTraveler } from "@/api/study"
+export default {
+  data() {
+    return {
+      option: [
+        {
+          name: '身份证',
+        },
+      ],
+      sexOption: [
+        {
+          name: '男',
+        },
+        {
+          name: '女',
+        },
+      ],
+      form: {
+        id: '',
+        idCard: '',
+        cardType: '1',
+        phone: '',
+        sex: '',
+        userId: '',
+        userName: ''
+      },
+      sexLabel: '',
+      show: false,
+      showSex: false,
+      loading:false
+    };
+  },
+  methods: {
+    select({ name }) {
+      this.sexLabel = name
+      this.form.sex = name == "男" ? 1 : 2
+    },
+    handleAdd() {
+      this.loading = true
+      saveTraveler(this.form).then(res => {
+        this.loading = false
+        if (res.state == 'Success') {
+          uni.showToast({
+            title: '完成',
+            icon: 'none'
+          });
+          uni.navigateBack({
+            delta: 1
+          });
+        }
+      })
+    }
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.add {
+  background: #F9F9F9;
+  min-height: 100vh;
+  padding: 0 20rpx;
+}
+
+.tips {
+  color: #AAAAAA;
+  font-size: 28rpx;
+  padding: 28rpx 0;
+}
+
+.card {
+  padding: 24rpx;
+  background: #FFFFFF;
+  border-radius: 16rpx;
+
+  .form-item {
+    border-top: 1px solid #F0F0F0;
+    display: flex;
+
+    &:first-child {
+      border-top: none;
+    }
+
+    .label {
+      color: #222222;
+      font-size: 24rpx;
+      margin: 24rpx 0;
+      width: 152rpx;
+    }
+
+    .input {}
+  }
+}
+
+.buy-box {
+  position: fixed;
+  bottom: 0%;
+  left: 0%;
+  width: 100%;
+  background: #fff;
+  padding: 10rpx 24rpx env(safe-area-inset-bottom);
+  box-sizing: border-box;
+
+  .buy-btn {
+    width: 702rpx;
+    height: 80rpx;
+    line-height: 80rpx;
+    text-align: center;
+    background: #3B83FF;
+    border-radius: 40rpx;
+    font-weight: 600;
+    font-size: 28rpx;
+    color: #FFFFFF;
+  }
+
+}
+</style>

+ 202 - 0
study/tourList/list.vue

@@ -0,0 +1,202 @@
+<template>
+  <view class="list">
+
+    <view @click="add" class="adBbtn">
+      <view class="label">+添加出游人</view>
+    </view>
+
+    <view v-for="item in userList" @click="check(item)" class="user-card">
+      <view class="user-info">
+        <view class="user-name">
+          <view class="icon">
+            <image class="img" src="/static/user-edit.png" mode="aspectFit" />
+          </view>
+          <view class="name">
+            {{ item.userName }}
+          </view>
+        </view>
+        <view class="id-number">
+          <text class="label">身份证</text>{{ item.idCard }}
+        </view>
+      </view>
+      <view class="check">
+        <image v-if="!item.check" class="check-img" src="/static/check.png"></image>
+        <image v-else class="check-img" src="/static/eglass-check.png"></image>
+      </view>
+    </view>
+
+
+    <view class="btn-box">
+      <view class="total-price">
+        <view class="label">
+          已选择:{{ userList.filter(e => e.check).length }}人
+        </view>
+      </view>
+
+      <button class="btn" type="default" :loading="loading" @click="submit">确认</button>
+    </view>
+  </view>
+</template>
+
+<script>
+import { getAllTravelers } from "@/api/study";
+export default {
+  data() {
+    return {
+      userList: [],
+    };
+  },
+  methods: {
+    add() {
+      uni.navigateTo({
+        url: '/study/tourList/add'
+      });
+    },
+    check(user) {
+      if (user.check) {
+        user.check = false
+      } else {
+        user.check = true
+      }
+    },
+    submit() {
+      const selected = this.userList.filter(e => e.check)
+      if (selected.length === 0) {
+        uni.showToast({
+          title: '请选择出游人',
+          icon: 'none'
+        })
+        return
+      }
+      uni.$emit('updateData', selected)
+      uni.navigateBack()
+    }
+  },
+  onShow() {
+    getAllTravelers().then(({ content }) => {
+      this.userList = content.map(e => {
+        return {
+          ...e,
+          check: false
+        }
+      })
+    })
+  },
+
+};
+</script>
+
+<style lang="scss" scoped>
+.list {
+  background: #F9F9F9;
+  min-height: 100vh;
+  padding: 20rpx;
+
+  .adBbtn {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    background-color: #FFFFFF;
+
+    .label {
+      color: #3B83FF;
+      font-size: 28rpx;
+      font-weight: Bold;
+      padding: 24rpx 0;
+    }
+
+    margin: 0 0 20rpx 0;
+  }
+
+  .user-card {
+    padding: 24rpx;
+    background-color: #FFFFFF;
+    border-radius: 16rpx;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20rpx;
+
+
+    .user-info {
+      .user-name {
+        display: flex;
+        align-items: center;
+        margin-bottom: 20rpx;
+
+        .icon {
+          width: 36rpx;
+          height: 36rpx;
+          margin-right: 8rpx;
+
+          .img {
+            width: 100%;
+            height: 100%;
+          }
+        }
+
+        .name {
+          font-size: 28rpx;
+          color: #222222;
+          font-weight: bold;
+        }
+      }
+
+      .id-number {
+        padding-left: 44rpx;
+        font-size: 24rpx;
+        color: #222222;
+
+        .label {
+          margin-right: 20rpx;
+        }
+      }
+    }
+
+    .check {
+      width: 40rpx;
+      height: 40rpx;
+
+      .check-img {
+        width: 100%;
+        height: 100%;
+      }
+    }
+  }
+
+  .btn-box {
+    position: fixed;
+    bottom: 0%;
+    left: 0%;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    box-sizing: border-box;
+    padding: 10rpx 30rpx env(safe-area-inset-bottom);
+    border-top: 1rpx solid #EEEEEE;
+
+    .total-price {
+      .label {
+        color: #AAAAAA;
+        font-size: 28rpx;
+      }
+    }
+
+
+    .btn {
+      width: 280rpx;
+      height: 80rpx;
+      line-height: 80rpx;
+      text-align: center;
+      background: #3B83FF;
+      box-shadow: inset 0rpx 6rpx 12rpx 2rpx rgba(255, 255, 255, 0.16);
+      border-radius: 46rpx 46rpx 46rpx 46rpx;
+      font-weight: 800;
+      color: #FFFFFF;
+      font-size: 28rpx;
+      margin: 0;
+    }
+  }
+}
+</style>

+ 481 - 189
study/type.vue

@@ -1,270 +1,508 @@
 <template>
 	<view class="study-type">
 		<!-- 导航栏 -->
-		<zs-header :background="background"></zs-header>
+		<zs-header :background="background" color="#000" :title="title"></zs-header>
 		<image class="banner" :src="bg" mode=""></image>
-		
-		<!-- 横向 -->
-		<view v-for="(item,index) in pageContent" :key="item.id">
-			<view class="title-box">
-				<view class="type-title" v-if="item.list.length">
-					{{item.title}}
-				</view>
-				<view class="more" @click="goTypeList(item.id,item.title)">
+
+		<view style="margin-bottom: 0;" class="type-title more-title">
+			推荐线路
+			<view class="more-box" @click="jump">
+				<view>
 					更多
 				</view>
+				<image class="more" src="../static/jiantou-icon.png" mode=""></image>
+			</view>
+		</view>
+
+		<zs-list class="store-recommend-box" mt="0">
+			<view class="left">
+				<view class="store-item" v-for="(item, index) in recommendList" :key="index"
+					@click="goGoodsDetail(item.goodsId)">
+					<zs-img :src="item.logoPath" width="344rpx" height="344rpx" mode=""></zs-img>
+					<view class="info">
+						<view class="title">
+							{{ item.goodsName }}
+						</view>
+						<view class="user-info">
+							<!-- <image class="head" :src="item.logoPath"></image> -->
+							<view class="user-name">
+								{{ item.shopName }}
+							</view>
+						</view>
+						<view class="price-box">
+							<view class="left">
+								<view class="unit">
+									¥
+								</view>
+								<view class="price">
+									{{ item.realPrice }}
+								</view>
+								<view class="old-price">
+									¥{{ item.marketPrice }}
+								</view>
+							</view>
+
+							<view class="right">
+								{{ item.saleNum }}人已购
+							</view>
+						</view>
+					</view>
+
+				</view>
 			</view>
-			<!-- type-box横向 type-box1纵向 -->
-			<view class="type-box">
-				<view class="type-item" v-for="(i,d) in item.list" :key="i.goodsId" @click="goGoodsDetail(i.goodsId)">
-					<zs-img :src="i.logoPath" width="344rpx" height="344rpx" mode=""></zs-img>
+			<view class="right">
+
+				<view class="store-item" v-for="(item, index) in recommendList1" :key="index"
+					@click="goGoodsDetail(item.goodsId)">
+					<zs-img :src="item.logoPath" width="344rpx" height="344rpx" mode=""></zs-img>
 					<view class="info">
 						<view class="title">
-							{{i.goodsName||'冬季北疆·阿勒泰将军山喀纳斯-天木-白哈巴...'}}
+							{{ item.goodsName }}
 						</view>
-						<view class="desc">
-							{{i.goodsDescribe||'了解中国古代印染工艺非遗传承的现状'}}
+						<view class="user-info">
+							<!-- <image class="head" :src="item.logoPath" mode=""></image> -->
+							<view class="user-name">
+								{{ item.shopName }}
+							</view>
 						</view>
 						<view class="price-box">
 							<view class="left">
-								<!-- <view class="unit">
+								<view class="unit">
-								</view> -->
+								</view>
 								<view class="price">
-								¥{{i.realPrice||50}}
+									{{ item.realPrice }}
+								</view>
+								<view class="old-price">
+									¥{{ item.marketPrice }}
 								</view>
 							</view>
-							
 							<view class="right">
-								销量{{i.saleNum||999}}
+								{{ item.saleNum }}人已购
 							</view>
 						</view>
 					</view>
+
 				</view>
 			</view>
-			
+		</zs-list>
 
+
+		<view class="type-title more-title">
+			互动社区
+			<view class="more-box" @click="jump">
+				<view>
+					更多
+				</view>
+				<image class="more" src="../static/jiantou-icon.png" mode=""></image>
+			</view>
 		</view>
-		
+
+
+
+		<scroll-view class="tab-box" enable-flex scroll-x>
+			<view class="tab" :class="[tab == item.id ? 'active' : '']" v-for="(item, index) in tabList" :key="index"
+				@click="handleTab(item.id)">
+				{{ item.communityName }}
+			</view>
+			<view class="tab">
+				研学分享
+			</view>
+		</scroll-view>
+
 		<template>
-			<view class="type-title" v-if="list.length||list1.length">
-				科普视频
+			<!-- 列表 -->
+			<zs-list class="store-box" mt="0">
+				<view class="left">
+					<view class="store-item" v-for="(item, index) in communityList" :key="index" @click="goCommunity(item.tabId)">
+						<!-- <image class="play-icon" src="../static/play.png" mode="widthFix"></image> -->
+						<zs-img :src="item.coverImg" width="344rpx" height="256rpx"></zs-img>
+						<view class="info">
+							<view class="title">
+								{{ item.communityTitle }}
+							</view>
+							<view class="user-info">
+								<view class="head">
+									{{ item.communityName }}
+								</view>
+								<view class="user-name">
+									观看 {{ item.viewedCount }}
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="right">
+					<view class="store-item" v-for="(item, index) in communityList1" :key="index" @click="goCommunity(item.tabId)">
+						<image class="play-icon" src="../static/play.png" mode=""></image>
+						<zs-img :src="item.coverImg" width="344rpx" height="256rpx"></zs-img>
+						<view class="info">
+							<view class="title">
+								{{ item.communityTitle }}
+							</view>
+							<view class="user-info">
+								<view class="head">
+									{{ item.communityName }}
+								</view>
+								<view class="user-name">
+									阅读 {{ item.viewedCount }}
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view v-if="communityList.length == 0 && communityList1.length == 0" class="noneContent">
+					<view>没有数据</view>
+				</view>
+			</zs-list>
+		</template>
+
+		<view class="type-title more-title">
+			科普视频
+			<view class="more-box" @click="jump">
+				<view>
+					更多
+				</view>
+				<image class="more" src="../static/jiantou-icon.png" mode=""></image>
 			</view>
+		</view>
+
+		<template>
 			<!-- 列表 -->
 			<zs-list class="store-box" mt="0" @load="loadMore" :status="status">
-				<view class="left">	
-					<view class="store-item" v-for="(item,index) in list" :key="index" @click="goCourse(item.id)">
+				<view class="left">
+					<view class="store-item" v-for="(item, index) in list" :key="index" @click="goCourse(item.id)">
 						<image class="play-icon" src="../static/play.png" mode="widthFix"></image>
 						<zs-img :src="item.courseImg" width="344rpx" height="256rpx"></zs-img>
 						<view class="info">
 							<view class="title">
-								{{item.courseName}}
+								{{ item.courseName }}
 							</view>
 							<view class="user-info">
-								<image class="head" src="../static/logo.png" mode=""></image>
+								<view class="head" mode=""> 科普视频</view>
 								<view class="user-name">
-									研学官方账号
+									观看{{ item.viewedCount }}
 								</view>
 							</view>
 						</view>
 					</view>
 				</view>
 				<view class="right">
-			
-					<view class="store-item" v-for="(item,index) in list1" :key="index" @click="goCourse(item.id)">
+					<view class="store-item" v-for="(item, index) in list1" :key="index" @click="goCourse(item.id)">
 						<image class="play-icon" src="../static/play.png" mode=""></image>
 						<zs-img :src="item.courseImg" width="344rpx" height="256rpx"></zs-img>
 						<view class="info">
 							<view class="title">
-								{{item.courseName}}
+								{{ item.courseName }}
 							</view>
 							<view class="user-info">
-								<image class="head" src="../static/logo.png" mode=""></image>
+								<view class="head">科普视频</view>
+								<!-- <image class="head" src="../static/logo.png" mode=""></image> -->
 								<view class="user-name">
-									研学官方账号
+									观看{{ item.viewedCount }}
 								</view>
 							</view>
 						</view>
 					</view>
 				</view>
-			
+
 			</zs-list>
 		</template>
-		
+
+
 	</view>
 </template>
 
 <script>
-	import {getMenu,getPage,videoList,studyGoods} from '@/api/study.js';
-	export default {
-		data() {
-			return {
-				bg:'',
-				background:false,
-				pageContent:[],
-				status: 'more',
-				list: [],
-				list1: [],
-				query:{
-					columnId:0,
-					currentPage:1,
-					pageSize:10,
-				}
+import { getPage, videoList, studyGoods, studyGoodsByUser, getCommunitys, getItem, } from '@/api/study.js';
+export default {
+	data() {
+		return {
+			bg: '',
+			title: '',
+			background: false,
+			pageContent: [],
+			status: 'more',
+			list: [],
+			list1: [],
+			recommendList: [],
+			recommendList1: [],
+			communityList: [],
+			communityList1: [],
+			tab: 0,
+			tabList: [],
+			query: {
+				columnId: 0,
+				currentPage: 1,
+				pageSize: 10,
 			}
+		}
+	},
+	methods: {
+		// 去商品详情
+		goGoodsDetail(id) {
+			uni.navigateTo({
+				url: '/study/studyGoodsDetail?id=' + id
+			})
 		},
-		methods: {
-			// 去商品详情
-			goGoodsDetail(id){
-				uni.navigateTo({
-					url:'/study/studyGoodsDetail?id='+id
-				})
-			},
-			goTypeList(id,title){
-				uni.navigateTo({
-					url:`/study/studyList?id=${id}&title=${title}`
-				})
-			},
-			goDetail(){},
-			// 去课程详情页
-			goCourse(id){
-				uni.navigateTo({
-					url:'./courseDetail?id='+id
-				})
-			},
-			loadMore() {
-				this.videoList()
-			},
-			// 获取顶部栏目
-			getPage(columnId){
-				return new Promise((resolve,reject)=>{
-					getPage({currentPage:1,pageSize:10,columnId,status:1}).then(res=>{
-						if(res.state == 'Success'){
-							res.content.map(item=>{
-								let obj = {
-									id:item.id,
-									title:item.columnName,
-									list:[]
-								}
-								this.pageContent.push(obj)
-							})
-							console.log(this.pageContent);
-							resolve()
-						}
-					})
-				})
-			},
-			// 课程视频
-			videoList(){
-				this.status = 'loading'
-				videoList(this.query).then(res=>{
-					if(res.state == 'Success'){
-						let list = []
-						let list1 = []
-						res.content.records.map((item,index)=>{
-							if(index%2){
-								list1.push(item)
-							}else{
-								list.push(item)
+		goTypeList(id, title) {
+			uni.navigateTo({
+				url: `/study/studyList?id=${id}&title=${title}`
+			})
+		},
+		goDetail() { },
+		// 去课程详情页
+		goCourse(id) {
+			uni.navigateTo({
+				url: './courseDetail?id=' + id
+			})
+		},
+		goCommunity(id){
+			console.log(id)
+			uni.navigateTo({
+				url: './community/detail?id=' + id
+			});
+		},
+		loadMore() {
+			this.videoList()
+		},
+		// 获取顶部栏目
+		getPage(columnId) {
+			return new Promise((resolve, reject) => {
+				getPage({ currentPage: 1, pageSize: 10, columnId, status: 1 }).then(res => {
+					if (res.state == 'Success') {
+						res.content.map(item => {
+							let obj = {
+								id: item.id,
+								title: item.columnName,
+								list: []
 							}
+							this.pageContent.push(obj)
 						})
-						this.list = this.list.concat(list)
-						this.list1 = this.list1.concat(list1)
-						let total = this.list.length+this.list1.length
-						if(total>=res.content.total){
-							this.status = 'noMore'
-						}else{
-							this.status = 'more'
-							this.query.currentPage++
-						}
+						console.log(this.pageContent);
+						resolve()
 					}
 				})
-			},
-			getStudyGoods(){
-				this.pageContent.map(item=>{
-					studyGoods({columnId:item.id,currentPage:1,pageSize:4}).then(res=>{
-						if(res.state == 'Success'){
-							item.list = res.content.records
+			})
+		},
+		// 课程视频
+		videoList() {
+			this.status = 'loading'
+			videoList(this.query).then(res => {
+				if (res.state == 'Success') {
+					let list = []
+					let list1 = []
+					res.content.records.map((item, index) => {
+						if (index % 2) {
+							list1.push(item)
+						} else {
+							list.push(item)
 						}
 					})
+					this.list = this.list.concat(list)
+					this.list1 = this.list1.concat(list1)
+					let total = this.list.length + this.list1.length
+					if (total >= res.content.total) {
+						this.status = 'noMore'
+					} else {
+						this.status = 'more'
+						this.query.currentPage++
+					}
+				}
+			})
+		},
+		getStudyGoods() {
+			this.pageContent.map(item => {
+				studyGoods({ columnId: item.id, currentPage: 1, pageSize: 4 }).then(res => {
+					if (res.state == 'Success') {
+						item.list = res.content.records
+					}
 				})
-			}
-			
+			})
 		},
-		onLoad(options) {
-			this.query.columnId = options.id
-			this.getPage(options.id).then(()=>{
-				this.getStudyGoods()
+		studyRecommendByUser() {
+			studyGoodsByUser({
+				columnId: this.query.columnId,
+				currentPage: 1,
+				pageSize: 4
+			}).then(res => {
+				if (res.state == 'Success') {
+					let recommendList = []
+					let recommendList1 = []
+					res.content.records.map((item, index) => {
+						if (index % 2) {
+							recommendList1.push(item)
+						} else {
+							recommendList.push(item)
+						}
+					})
+					this.recommendList = this.recommendList.concat(recommendList)
+					this.recommendList1 = this.recommendList1.concat(recommendList1)
+				}
 			})
-			let that = this
-			const eventChannel = this.getOpenerEventChannel();
-			if(JSON.stringify(eventChannel) !=='{}'){
-				eventChannel.on('img', function(data) {
-					that.bg = data
-				})
-			}
 		},
-		onPageScroll(e) {
-				if(e.scrollTop >= 30){
-					this.background = true
-				}else{
-					this.background = false
+		getColumnTabs() {
+			getCommunitys({ columnId: this.query.columnId }).then(res => {
+				if (res.state == 'Success') {
+					this.tabList = res.content
+					if (this.tabList.length > 0) {
+						this.tab = this.tabList[0].id
+						this.getItem(this.tab)
+					}
 				}
+			})
 		},
-	}
+		// 点击社区tab
+		handleTab(tab) {
+			this.tab = tab
+			this.communityList = []
+			this.communityList1 = []
+			this.getItem(tab)
+		},
+		//获取社区分类列表
+		getItem(communityId) {
+			getItem({ communityId, currentPage: 1, pageSize: 2, state: 2 }).then(res => {
+				if (res.state == 'Success') {
+					let communityList = []
+					let communityList1 = []
+					res.content.records.map((item, index) => {
+						if (index % 2) {
+							communityList1.push(item)
+						} else {
+							communityList.push(item)
+						}
+					})
+					this.communityList = this.communityList.concat(communityList)
+					this.communityList1 = this.communityList1.concat(communityList1)
+				}
+			})
+		},
+	},
+	onLoad(options) {
+		this.query.columnId = options.id
+		// this.getPage(options.id).then(()=>{
+		// 	this.getStudyGoods()
+		// })
+		this.getColumnTabs()
+		this.studyRecommendByUser()
+		let that = this
+		const eventChannel = this.getOpenerEventChannel();
+		if (JSON.stringify(eventChannel) !== '{}') {
+			eventChannel.on('img', function (img, name) {
+				that.bg = img
+				that.title = name
+			})
+		}
+	},
+	onPageScroll(e) {
+		if (e.scrollTop >= 30) {
+			this.background = true
+		} else {
+			this.background = false
+		}
+	},
+}
 </script>
 
-<style lang="scss" >
-.study-type{
+<style lang="scss">
+.study-type {
 	padding: 0 20rpx;
 	background: #F9F9F9;
-	.banner{
+
+	.banner {
 		width: 750rpx;
-		height: 420rpx;
+		height: 480rpx;
 		vertical-align: bottom;
 		position: relative;
 		left: -20rpx;
 	}
-	.title-box{
+
+	.title-box {
 		display: flex;
 		align-items: center;
 		justify-content: space-between;
-		.more{
+
+		.more {
 			font-size: 20rpx;
 			color: #AAAAAA;
 		}
 	}
-	
-	.type-title{
+
+	.more-title {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+
+		.more-box {
+			display: flex;
+			align-items: center;
+			font-size: 24rpx;
+			color: #AAAAAA;
+
+			.more {
+				width: 20rpx;
+				height: 20rpx;
+				display: block;
+				margin-left: 6rpx;
+			}
+		}
+	}
+
+	.tab-box {
+		display: flex;
+		align-items: flex-start;
+
+		.tab {
+			flex-shrink: 0;
+			padding: 10rpx 24rpx;
+			// margin-top: 28rpx;
+			color: #AAAAAA;
+			background: #EEEEEE;
+			font-size: 28rpx;
+			margin-right: 20rpx;
+			border-radius: 8rpx;
+		}
+
+		.tab.active {
+			color: #222222;
+			// background: #CEE0FF;
+		}
+	}
+
+	.type-title {
 		font-weight: bold;
 		color: #222222;
 		font-size: 32rpx;
-		margin: 30rpx 0 20rpx;
+		margin: 20rpx 0 20rpx;
 	}
-	.type-box{
+
+	.type-box {
 		display: flex;
 		justify-content: space-between;
 		flex-wrap: wrap;
-		.type-item{
+
+		.type-item {
 			width: 340rpx;
 			margin-bottom: 20rpx;
 			// box-shadow: 0rpx 0rpx 24rpx 2rpx rgba(0, 0, 0, 0.08);
 			border-radius: 16rpx;
 			background: #fff;
-			
+
 			.icon {
 				width: 100%;
 				height: 300rpx;
 				border-radius: 16rpx 16rpx 0 0;
 			}
-			
-			.info{
+
+			.info {
 				flex: 1;
 				padding: 16rpx;
 				display: flex;
 				flex-direction: column;
 				justify-content: space-between;
 				position: relative;
-				.title{
+
+				.title {
 					font-size: 28rpx;
 					font-weight: bold;
 					width: 100%;
@@ -273,8 +511,8 @@
 					text-overflow: ellipsis;
 					margin-top: 10rpx;
 				}
-				
-				.desc{
+
+				.desc {
 					font-size: 24rpx;
 					color: #AAAAAA;
 					overflow: hidden;
@@ -287,40 +525,45 @@
 					-webkit-box-orient: vertical;
 					margin-top: 12rpx;
 				}
-				
-				.price-box{
+
+				.price-box {
 					display: flex;
 					justify-content: space-between;
 					align-items: center;
 					margin-top: 12rpx;
-					.left{
+
+					.left {
 						display: flex;
 						align-items: flex-end;
-						.unit{
+
+						.unit {
 							font-size: 20rpx;
 							color: $uni-color-primary;
 							font-weight: bold;
 						}
-						.price{
+
+						.price {
 							font-size: 32rpx;
 							color: $uni-color-primary;
 							font-weight: bold;
 						}
 					}
-					.right{
+
+					.right {
 						font-size: 24rpx;
 						color: #AAAAAA;
 					}
 				}
-				
+
 			}
 		}
 	}
-	
+
 	.swiper {
 		height: 272rpx;
 		border-radius: 16rpx;
 		margin-top: 28rpx;
+
 		.swiper-item {
 			width: 100%;
 			height: 100%;
@@ -328,26 +571,33 @@
 			object-fit: cover;
 		}
 	}
-	
+
+	.noneContent {
+		display: flex;
+		justify-content: center;
+		width: 100%;
+		padding-top: 10rpx;
+		color: #777777;
+		font-size: 28rpx;
+	}
+
 	.zs-list {
 		display: flex;
 		flex-wrap: wrap;
 		justify-content: space-between;
-		.left {
-			
-		}
-	
-		.right {
-			
-		}
-		
-		.store-item{
-			box-shadow: 0rpx 0rpx 24rpx 2rpx rgba(0,0,0,0.08);
+
+		.left {}
+
+		.right {}
+
+		.store-item {
+			box-shadow: 0rpx 0rpx 24rpx 2rpx rgba(0, 0, 0, 0.08);
 			border-radius: 16rpx;
 			width: 344rpx;
 			margin-top: 20rpx;
 			position: relative;
-			.play-icon{
+
+			.play-icon {
 				position: absolute;
 				top: 20rpx;
 				right: 30rpx;
@@ -355,35 +605,77 @@
 				height: 30rpx;
 				z-index: 2;
 			}
-			.info{
+
+			.info {
 				padding: 20rpx;
-				.title{
+
+				.title {
 					color: #222222;
-					font-size: 24rpx;
+					height: 76rpx;
+					font-size: 28rpx;
+					font-weight: bold;
 					width: 100%;
 					display: -webkit-box;
 					-webkit-box-orient: vertical;
-					-webkit-line-clamp: 2;      /* 显示的最大行数 */
+					-webkit-line-clamp: 2;
+					/* 显示的最大行数 */
 					overflow: hidden;
 				}
-				.user-info{
+
+				.user-info {
 					display: flex;
 					align-items: center;
+					justify-content: space-between;
 					margin-top: 20rpx;
-					.head{
-						width: 40rpx;
-						height: 40rpx;
-						border-radius: 50%;
+					font-size: 20rpx;
+					color: #AAAAAA;
+
+					.head {
+						font-size: 20rpx;
 					}
-					.user-name{
-						color: #AAAAAA;
+
+					.user-name {
 						font-size: 20rpx;
-						margin-left: 12rpx;
+					}
+				}
+
+				.price-box {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					margin-top: 12rpx;
+
+					.left {
+						display: flex;
+						align-items: flex-end;
+
+						.unit {
+							font-size: 24rpx;
+							color: $uni-color-primary;
+							font-weight: bold;
+						}
+
+						.price {
+							font-size: 32rpx;
+							color: $uni-color-primary;
+							font-weight: bold;
+						}
+
+						.old-price {
+							font-size: 20rpx;
+							color: #AAAAAA;
+							text-decoration: line-through;
+							margin-left: 10rpx;
+						}
+					}
+
+					.right {
+						font-size: 20rpx;
+						color: #AAAAAA;
 					}
 				}
 			}
 		}
 	}
-	
 }
 </style>

+ 28 - 0
uni_modules/uni-calendar/changelog.md

@@ -0,0 +1,28 @@
+## 1.4.11(2024-01-10)
+- 修复 回到今天时,月份显示不一致问题
+## 1.4.10(2023-04-10)
+- 修复 某些情况 monthSwitch 未触发的Bug
+## 1.4.9(2023-02-02)
+- 修复 某些情况切换月份错误的Bug
+## 1.4.8(2023-01-30)
+- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/161964)
+## 1.4.7(2022-09-16)
+- 优化 支持使用 uni-scss 控制主题色
+## 1.4.6(2022-09-08)
+- 修复 表头年月切换,导致改变当前日期为选择月1号,且未触发change事件的Bug
+## 1.4.5(2022-02-25)
+- 修复 条件编译 nvue 不支持的 css 样式的Bug
+## 1.4.4(2022-02-25)
+- 修复 条件编译 nvue 不支持的 css 样式的Bug
+## 1.4.3(2021-09-22)
+- 修复 startDate、 endDate 属性失效的Bug
+## 1.4.2(2021-08-24)
+- 新增 支持国际化
+## 1.4.1(2021-08-05)
+- 修复 弹出层被 tabbar 遮盖的Bug
+## 1.4.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.3.16(2021-05-12)
+- 新增 组件示例地址
+## 1.3.15(2021-02-04)
+- 调整为uni_modules目录规范

+ 544 - 0
uni_modules/uni-calendar/components/uni-calendar/calendar.js

@@ -0,0 +1,544 @@
+/**
+* @1900-2100区间内的公历、农历互转
+* @charset UTF-8
+* @github  https://github.com/jjonline/calendar.js
+* @Author  Jea杨(JJonline@JJonline.Cn)
+* @Time    2014-7-21
+* @Time    2016-8-13 Fixed 2033hex、Attribution Annals
+* @Time    2016-9-25 Fixed lunar LeapMonth Param Bug
+* @Time    2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
+* @Version 1.0.3
+* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+*/
+/* eslint-disable */
+var calendar = {
+
+  /**
+      * 农历1900-2100的润大小信息表
+      * @Array Of Property
+      * @return Hex
+      */
+  lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
+    0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
+    0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
+    0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
+    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
+    0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
+    0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
+    0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
+    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
+    0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
+    0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
+    0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
+    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
+    0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
+    0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
+    /** Add By JJonline@JJonline.Cn**/
+    0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
+    0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
+    0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
+    0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
+    0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
+    0x0d520], // 2100
+
+  /**
+      * 公历每个月份的天数普通表
+      * @Array Of Property
+      * @return Number
+      */
+  solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+  /**
+      * 天干地支之天干速查表
+      * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+      * @return Cn string
+      */
+  Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
+
+  /**
+      * 天干地支之地支速查表
+      * @Array Of Property
+      * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+      * @return Cn string
+      */
+  Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
+
+  /**
+      * 天干地支之地支速查表<=>生肖
+      * @Array Of Property
+      * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+      * @return Cn string
+      */
+  Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
+
+  /**
+      * 24节气速查表
+      * @Array Of Property
+      * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+      * @return Cn string
+      */
+  solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
+
+  /**
+      * 1900-2100各年的24节气日期速查表
+      * @Array Of Property
+      * @return 0x string For splice
+      */
+  sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
+
+  /**
+      * 数字转中文速查表
+      * @Array Of Property
+      * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+      * @return Cn string
+      */
+  nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
+
+  /**
+      * 日期转农历称呼速查表
+      * @Array Of Property
+      * @trans ['初','十','廿','卅']
+      * @return Cn string
+      */
+  nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+  /**
+      * 月份转农历称呼速查表
+      * @Array Of Property
+      * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+      * @return Cn string
+      */
+  nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
+
+  /**
+      * 返回农历y年一整年的总天数
+      * @param lunar Year
+      * @return Number
+      * @eg:var count = calendar.lYearDays(1987) ;//count=387
+      */
+  lYearDays: function (y) {
+    var i; var sum = 348
+    for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
+    return (sum + this.leapDays(y))
+  },
+
+  /**
+      * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+      * @param lunar Year
+      * @return Number (0-12)
+      * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+      */
+  leapMonth: function (y) { // 闰字编码 \u95f0
+    return (this.lunarInfo[y - 1900] & 0xf)
+  },
+
+  /**
+      * 返回农历y年闰月的天数 若该年没有闰月则返回0
+      * @param lunar Year
+      * @return Number (0、29、30)
+      * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+      */
+  leapDays: function (y) {
+    if (this.leapMonth(y)) {
+      return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
+    }
+    return (0)
+  },
+
+  /**
+      * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+      * @param lunar Year
+      * @return Number (-1、29、30)
+      * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+      */
+  monthDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
+    return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
+  },
+
+  /**
+      * 返回公历(!)y年m月的天数
+      * @param solar Year
+      * @return Number (-1、28、29、30、31)
+      * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+      */
+  solarDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+    var ms = m - 1
+    if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
+      return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
+    } else {
+      return (this.solarMonth[ms])
+    }
+  },
+
+  /**
+     * 农历年份转换为干支纪年
+     * @param  lYear 农历年的年份数
+     * @return Cn string
+     */
+  toGanZhiYear: function (lYear) {
+    var ganKey = (lYear - 3) % 10
+    var zhiKey = (lYear - 3) % 12
+    if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
+    if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
+    return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
+  },
+
+  /**
+     * 公历月、日判断所属星座
+     * @param  cMonth [description]
+     * @param  cDay [description]
+     * @return Cn string
+     */
+  toAstro: function (cMonth, cDay) {
+    var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+    var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+    return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
+  },
+
+  /**
+      * 传入offset偏移量返回干支
+      * @param offset 相对甲子的偏移量
+      * @return Cn string
+      */
+  toGanZhi: function (offset) {
+    return this.Gan[offset % 10] + this.Zhi[offset % 12]
+  },
+
+  /**
+      * 传入公历(!)y年获得该年第n个节气的公历日期
+      * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+      * @return day Number
+      * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+      */
+  getTerm: function (y, n) {
+    if (y < 1900 || y > 2100) { return -1 }
+    if (n < 1 || n > 24) { return -1 }
+    var _table = this.sTermInfo[y - 1900]
+    var _info = [
+      parseInt('0x' + _table.substr(0, 5)).toString(),
+      parseInt('0x' + _table.substr(5, 5)).toString(),
+      parseInt('0x' + _table.substr(10, 5)).toString(),
+      parseInt('0x' + _table.substr(15, 5)).toString(),
+      parseInt('0x' + _table.substr(20, 5)).toString(),
+      parseInt('0x' + _table.substr(25, 5)).toString()
+    ]
+    var _calday = [
+      _info[0].substr(0, 1),
+      _info[0].substr(1, 2),
+      _info[0].substr(3, 1),
+      _info[0].substr(4, 2),
+
+      _info[1].substr(0, 1),
+      _info[1].substr(1, 2),
+      _info[1].substr(3, 1),
+      _info[1].substr(4, 2),
+
+      _info[2].substr(0, 1),
+      _info[2].substr(1, 2),
+      _info[2].substr(3, 1),
+      _info[2].substr(4, 2),
+
+      _info[3].substr(0, 1),
+      _info[3].substr(1, 2),
+      _info[3].substr(3, 1),
+      _info[3].substr(4, 2),
+
+      _info[4].substr(0, 1),
+      _info[4].substr(1, 2),
+      _info[4].substr(3, 1),
+      _info[4].substr(4, 2),
+
+      _info[5].substr(0, 1),
+      _info[5].substr(1, 2),
+      _info[5].substr(3, 1),
+      _info[5].substr(4, 2)
+    ]
+    return parseInt(_calday[n - 1])
+  },
+
+  /**
+      * 传入农历数字月份返回汉语通俗表示法
+      * @param lunar month
+      * @return Cn string
+      * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+      */
+  toChinaMonth: function (m) { // 月 => \u6708
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+    var s = this.nStr3[m - 1]
+    s += '\u6708'// 加上月字
+    return s
+  },
+
+  /**
+      * 传入农历日期数字返回汉字表示法
+      * @param lunar day
+      * @return Cn string
+      * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+      */
+  toChinaDay: function (d) { // 日 => \u65e5
+    var s
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'; break
+      case 20:
+        s = '\u4e8c\u5341'; break
+      case 30:
+        s = '\u4e09\u5341'; break
+      default :
+        s = this.nStr2[Math.floor(d / 10)]
+        s += this.nStr1[d % 10]
+    }
+    return (s)
+  },
+
+  /**
+      * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+      * @param y year
+      * @return Cn string
+      * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+      */
+  getAnimal: function (y) {
+    return this.Animals[(y - 4) % 12]
+  },
+
+  /**
+      * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+      * @param y  solar year
+      * @param m  solar month
+      * @param d  solar day
+      * @return JSON object
+      * @eg:console.log(calendar.solar2lunar(1987,11,01));
+      */
+  solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
+    // 年份限定、上限
+    if (y < 1900 || y > 2100) {
+      return -1// undefined转换为数字变为NaN
+    }
+    // 公历传参最下限
+    if (y == 1900 && m == 1 && d < 31) {
+      return -1
+    }
+    // 未传参  获得当天
+    if (!y) {
+      var objDate = new Date()
+    } else {
+      var objDate = new Date(y, parseInt(m) - 1, d)
+    }
+    var i; var leap = 0; var temp = 0
+    // 修正ymd参数
+    var y = objDate.getFullYear()
+    var m = objDate.getMonth() + 1
+    var d = objDate.getDate()
+    var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
+    for (i = 1900; i < 2101 && offset > 0; i++) {
+      temp = this.lYearDays(i)
+      offset -= temp
+    }
+    if (offset < 0) {
+      offset += temp; i--
+    }
+
+    // 是否今天
+    var isTodayObj = new Date()
+    var isToday = false
+    if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
+      isToday = true
+    }
+    // 星期几
+    var nWeek = objDate.getDay()
+    var cWeek = this.nStr1[nWeek]
+    // 数字表示周几顺应天朝周一开始的惯例
+    if (nWeek == 0) {
+      nWeek = 7
+    }
+    // 农历年
+    var year = i
+    var leap = this.leapMonth(i) // 闰哪个月
+    var isLeap = false
+
+    // 效验闰月
+    for (i = 1; i < 13 && offset > 0; i++) {
+      // 闰月
+      if (leap > 0 && i == (leap + 1) && isLeap == false) {
+        --i
+        isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
+      } else {
+        temp = this.monthDays(year, i)// 计算农历普通月天数
+      }
+      // 解除闰月
+      if (isLeap == true && i == (leap + 1)) { isLeap = false }
+      offset -= temp
+    }
+    // 闰月导致数组下标重叠取反
+    if (offset == 0 && leap > 0 && i == leap + 1) {
+      if (isLeap) {
+        isLeap = false
+      } else {
+        isLeap = true; --i
+      }
+    }
+    if (offset < 0) {
+      offset += temp; --i
+    }
+    // 农历月
+    var month = i
+    // 农历日
+    var day = offset + 1
+    // 天干地支处理
+    var sm = m - 1
+    var gzY = this.toGanZhiYear(year)
+
+    // 当月的两个节气
+    // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+    var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
+    var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
+
+    // 依据12节气修正干支月
+    var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
+    if (d >= firstNode) {
+      gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
+    }
+
+    // 传入的日期的节气与否
+    var isTerm = false
+    var Term = null
+    if (firstNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 2]
+    }
+    if (secondNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 1]
+    }
+    // 日柱 当月一日与 1900/1/1 相差天数
+    var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+    var gzD = this.toGanZhi(dayCyclical + d - 1)
+    // 该日期所属的星座
+    var astro = this.toAstro(m, d)
+
+    return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
+  },
+
+  /**
+      * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+      * @param y  lunar year
+      * @param m  lunar month
+      * @param d  lunar day
+      * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+      * @return JSON object
+      * @eg:console.log(calendar.lunar2solar(1987,9,10));
+      */
+  lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
+    var isLeapMonth = !!isLeapMonth
+    var leapOffset = 0
+    var leapMonth = this.leapMonth(y)
+    var leapDay = this.leapDays(y)
+    if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+    if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
+    var day = this.monthDays(y, m)
+    var _day = day
+    // bugFix 2016-9-25
+    // if month is leap, _day use leapDays method
+    if (isLeapMonth) {
+      _day = this.leapDays(y, m)
+    }
+    if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
+
+    // 计算农历的时间差
+    var offset = 0
+    for (var i = 1900; i < y; i++) {
+      offset += this.lYearDays(i)
+    }
+    var leap = 0; var isAdd = false
+    for (var i = 1; i < m; i++) {
+      leap = this.leapMonth(y)
+      if (!isAdd) { // 处理闰月
+        if (leap <= i && leap > 0) {
+          offset += this.leapDays(y); isAdd = true
+        }
+      }
+      offset += this.monthDays(y, i)
+    }
+    // 转换闰月农历 需补充该年闰月的前一个月的时差
+    if (isLeapMonth) { offset += day }
+    // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+    var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+    var calObj = new Date((offset + d - 31) * 86400000 + stmap)
+    var cY = calObj.getUTCFullYear()
+    var cM = calObj.getUTCMonth() + 1
+    var cD = calObj.getUTCDate()
+
+    return this.solar2lunar(cY, cM, cD)
+  }
+}
+
+export default calendar

+ 12 - 0
uni_modules/uni-calendar/components/uni-calendar/i18n/en.json

@@ -0,0 +1,12 @@
+{
+	"uni-calender.ok": "ok",
+	"uni-calender.cancel": "cancel",
+	"uni-calender.today": "today",
+	"uni-calender.MON": "MON",
+	"uni-calender.TUE": "TUE",
+	"uni-calender.WED": "WED",
+	"uni-calender.THU": "THU",
+	"uni-calender.FRI": "FRI",
+	"uni-calender.SAT": "SAT",
+	"uni-calender.SUN": "SUN"
+}

+ 8 - 0
uni_modules/uni-calendar/components/uni-calendar/i18n/index.js

@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 12 - 0
uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json

@@ -0,0 +1,12 @@
+{
+	"uni-calender.ok": "确定",
+	"uni-calender.cancel": "取消",
+	"uni-calender.today": "今日",
+	"uni-calender.SUN": "日",
+	"uni-calender.MON": "一",
+	"uni-calender.TUE": "二",
+	"uni-calender.WED": "三",
+	"uni-calender.THU": "四",
+	"uni-calender.FRI": "五",
+	"uni-calender.SAT": "六"
+}

+ 12 - 0
uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json

@@ -0,0 +1,12 @@
+{
+	"uni-calender.ok": "確定",
+	"uni-calender.cancel": "取消",
+	"uni-calender.today": "今日",
+	"uni-calender.SUN": "日",
+	"uni-calender.MON": "一",
+	"uni-calender.TUE": "二",
+	"uni-calender.WED": "三",
+	"uni-calender.THU": "四",
+	"uni-calender.FRI": "五",
+	"uni-calender.SAT": "六"
+}

+ 192 - 0
uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue

@@ -0,0 +1,192 @@
+<template>
+	<view class="uni-calendar-item__weeks-box" :class="{
+		'uni-calendar-item--disable': weeks.disable,
+		'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay,
+		'uni-calendar-item--checked': (calendar.fullDate === weeks.fullDate && !weeks.isDay),
+		'uni-calendar-item--before-checked': weeks.beforeMultiple,
+		'uni-calendar-item--multiple': weeks.multiple,
+		'uni-calendar-item--after-checked': weeks.afterMultiple,
+	}" @click="choiceDate(weeks)">
+		<view class="uni-calendar-item__weeks-box-item">
+			<text v-if="selected && weeks.extraInfo && weeks.extraInfo.notic != false"
+				class="uni-calendar-item__weeks-box-circle"></text>
+			<text class="uni-calendar-item__weeks-box-text" :class="{
+				'uni-calendar-item--isDay-text': weeks.isDay,
+				'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked': calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked': weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked': weeks.afterMultiple,
+				'uni-calendar-item--disable': weeks.disable,
+			}">{{ weeks.date }}</text>
+			<text v-if="!lunar && !weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text': weeks.isDay,
+				'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked': calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked': weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked': weeks.afterMultiple,
+			}">{{ todayText }}</text>
+			<text v-if="lunar && !weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text': weeks.isDay,
+				'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked': calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked': weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked': weeks.afterMultiple,
+				'uni-calendar-item--disable': weeks.disable,
+			}">{{ weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一' ? weeks.lunar.IMonthCn : weeks.lunar.IDayCn) }}</text>
+			<text v-if="weeks.extraInfo && weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--extra': weeks.extraInfo.info,
+				'uni-calendar-item--isDay-text': weeks.isDay,
+				'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked': calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked': weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked': weeks.afterMultiple,
+				'uni-calendar-item--disable': weeks.disable,
+			}">{{ weeks.extraInfo.info }}</text>
+		</view>
+	</view>
+</template>
+
+<script>
+import { initVueI18n } from '@dcloudio/uni-i18n'
+import i18nMessages from './i18n/index.js'
+const { t } = initVueI18n(i18nMessages)
+
+export default {
+	emits: ['change'],
+	props: {
+		weeks: {
+			type: Object,
+			default() {
+				return {}
+			}
+		},
+		calendar: {
+			type: Object,
+			default: () => {
+				return {}
+			}
+		},
+		selected: {
+			type: Array,
+			default: () => {
+				return []
+			}
+		},
+		lunar: {
+			type: Boolean,
+			default: false
+		}
+	},
+	computed: {
+		todayText() {
+			return t("uni-calender.today")
+		},
+	},
+	methods: {
+		choiceDate(weeks) {
+			this.$emit('change', weeks)
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+$uni-font-size-base: 14px;
+$uni-text-color: #333;
+$uni-font-size-sm: 12px;
+$uni-color-error: #e43d33;
+$uni-opacity-disabled: 0.3;
+$uni-text-color-disable: #c0c0c0;
+$uni-primary: #2979ff !default;
+
+.uni-calendar-item__weeks-box {
+	flex: 1;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+}
+
+.uni-calendar-item__weeks-box-text {
+	font-size: $uni-font-size-base;
+	color: $uni-text-color;
+}
+
+.uni-calendar-item__weeks-lunar-text {
+	font-size: $uni-font-size-sm;
+	color: $uni-text-color;
+}
+
+.uni-calendar-item__weeks-box-item {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	width: 100rpx;
+	height: 100rpx;
+}
+
+.uni-calendar-item__weeks-box-circle {
+	position: absolute;
+	top: 5px;
+	right: 5px;
+	width: 8px;
+	height: 8px;
+	border-radius: 8px;
+	background-color: $uni-color-error;
+
+}
+
+.uni-calendar-item--disable {
+	background-color: rgba(249, 249, 249, $uni-opacity-disabled);
+	color: $uni-text-color-disable;
+	// visibility: hidden;
+	// opacity: 0;
+}
+
+.uni-calendar-item--isDay-text {
+	color: $uni-primary;
+}
+
+.uni-calendar-item--isDay {
+	background-color: $uni-primary;
+	opacity: 0.8;
+	color: #fff;
+}
+
+.uni-calendar-item--extra {
+	color: $uni-primary;
+	opacity: 0.8;
+}
+
+.uni-calendar-item--checked {
+	background-color: $uni-primary;
+	color: #fff;
+	opacity: 0.8;
+}
+
+.uni-calendar-item--multiple {
+	background-color: $uni-primary;
+	color: #fff;
+	opacity: 0.8;
+}
+
+.uni-calendar-item--before-checked {
+	background-color: #ff5a5f;
+	color: #fff;
+}
+
+.uni-calendar-item--after-checked {
+	background-color: #ff5a5f;
+	color: #fff;
+}
+</style>

+ 657 - 0
uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue

@@ -0,0 +1,657 @@
+<template>
+	<view class="uni-calendar">
+		<view v-if="!insert && show" class="uni-calendar__mask" :class="{ 'uni-calendar--mask-show': aniMaskShow }"
+			@click="clean"></view>
+		<view v-if="insert || show" class="uni-calendar__content"
+			:class="{ 'uni-calendar--fixed': !insert, 'uni-calendar--ani-show': aniMaskShow }">
+			<view v-if="title" class="uni-calendar__title">
+				{{ title }}
+			</view>
+			<view v-if="!insert && showCancelAndOk" class="uni-calendar__header uni-calendar--fixed-top">
+				<view class="uni-calendar__header-btn-box" @click="close">
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">{{ cancelText }}</text>
+				</view>
+				<view class="uni-calendar__header-btn-box" @click="confirm">
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">{{ okText }}</text>
+				</view>
+			</view>
+			<view class="uni-calendar__header">
+				<view class="uni-calendar__header-btn-box" @click.stop="pre">
+					<view class="uni-calendar__header-btn uni-calendar--left"></view>
+				</view>
+				<picker mode="date" :value="date" fields="month" @change="bindDateChange">
+					<text class="uni-calendar__header-text">{{ (nowDate.year || '') + ' / ' + (nowDate.month || '') }}</text>
+				</picker>
+				<view class="uni-calendar__header-btn-box" @click.stop="next">
+					<view class="uni-calendar__header-btn uni-calendar--right"></view>
+				</view>
+				<text class="uni-calendar__backtoday" @click="backToday">{{ todayText }}</text>
+
+			</view>
+			<view class="uni-calendar__box" @touchstart="startTouch" @touchmove="moveTouch" @touchend="endTouch">
+				<view v-if="showMonth" class="uni-calendar__box-bg">
+					<text class="uni-calendar__box-bg-text">{{ nowDate.month }}</text>
+				</view>
+				<view class="uni-calendar__weeks">
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{ SUNText }}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{ monText }}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{ TUEText }}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{ WEDText }}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{ THUText }}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{ FRIText }}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{ SATText }}</text>
+					</view>
+				</view>
+				<view class="uni-calendar__weeks" v-for="(item, weekIndex) in weeks" :key="weekIndex">
+					<view class="uni-calendar__weeks-item" v-for="(weeks, weeksIndex) in item" :key="weeksIndex">
+						<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected"
+							:lunar="lunar" @change="choiceDate"></calendar-item>
+					</view>
+				</view>
+				<view @click="confirm">
+					<slot name="btn"></slot>
+				</view>
+			</view>
+
+		</view>
+	</view>
+</template>
+
+<script>
+import Calendar from './util.js';
+import CalendarItem from './uni-calendar-item.vue'
+
+import { initVueI18n } from '@dcloudio/uni-i18n'
+import i18nMessages from './i18n/index.js'
+const { t } = initVueI18n(i18nMessages)
+
+/**
+ * Calendar 日历
+ * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=56
+ * @property {String} date 自定义当前时间,默认为今天
+ * @property {Boolean} lunar 显示农历
+ * @property {String} startDate 日期选择范围-开始日期
+ * @property {String} endDate 日期选择范围-结束日期
+ * @property {Boolean} range 范围选择
+ * @property {Boolean} insert = [true|false] 插入模式,默认为false
+ * 	@value true 弹窗模式
+ * 	@value false 插入模式
+ * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
+ * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
+ * @property {Boolean} showMonth 是否选择月份为背景
+ * @event {Function} change 日期改变,`insert :ture` 时生效
+ * @event {Function} confirm 确认选择`insert :false` 时生效
+ * @event {Function} monthSwitch 切换月份时触发
+ * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
+ */
+export default {
+	components: {
+		CalendarItem
+	},
+	emits: ['close', 'confirm', 'change', 'monthSwitch'],
+	props: {
+		date: {
+			type: String,
+			default: ''
+		},
+		selected: {
+			type: Array,
+			default() {
+				return []
+			}
+		},
+		lunar: {
+			type: Boolean,
+			default: false
+		},
+		startDate: {
+			type: String,
+			default: ''
+		},
+		endDate: {
+			type: String,
+			default: ''
+		},
+		range: {
+			type: Boolean,
+			default: false
+		},
+		insert: {
+			type: Boolean,
+			default: true
+		},
+		showMonth: {
+			type: Boolean,
+			default: true
+		},
+		clearDate: {
+			type: Boolean,
+			default: true
+		},
+		closeOnClickMask: {
+			type: Boolean,
+			default: true
+		},
+		showCancelAndOk: {
+			type: Boolean,
+			default: true
+		},
+		title: {
+			type: String,
+			default: ''
+		}
+	},
+	data() {
+		return {
+			show: false,
+			weeks: [],
+			calendar: {},
+			nowDate: '',
+			aniMaskShow: false,
+			moveX: 0,
+			startX: 0,
+		}
+	},
+	computed: {
+		/**
+		 * for i18n
+		 */
+
+		okText() {
+			return t("uni-calender.ok")
+		},
+		cancelText() {
+			return t("uni-calender.cancel")
+		},
+		todayText() {
+			return t("uni-calender.today")
+		},
+		monText() {
+			return t("uni-calender.MON")
+		},
+		TUEText() {
+			return t("uni-calender.TUE")
+		},
+		WEDText() {
+			return t("uni-calender.WED")
+		},
+		THUText() {
+			return t("uni-calender.THU")
+		},
+		FRIText() {
+			return t("uni-calender.FRI")
+		},
+		SATText() {
+			return t("uni-calender.SAT")
+		},
+		SUNText() {
+			return t("uni-calender.SUN")
+		},
+	},
+	watch: {
+		date(newVal) {
+			// this.cale.setDate(newVal)
+			this.init(newVal)
+		},
+		startDate(val) {
+			this.cale.resetSatrtDate(val)
+			this.cale.setDate(this.nowDate.fullDate)
+			this.weeks = this.cale.weeks
+		},
+		endDate(val) {
+			this.cale.resetEndDate(val)
+			this.cale.setDate(this.nowDate.fullDate)
+			this.weeks = this.cale.weeks
+		},
+		selected(newVal) {
+			this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
+			this.weeks = this.cale.weeks
+		}
+	},
+	created() {
+		this.cale = new Calendar({
+			selected: this.selected,
+			startDate: this.startDate,
+			endDate: this.endDate,
+			range: this.range,
+		})
+		this.init(this.date)
+	},
+	methods: {
+
+		startTouch(e) {
+			this.startX = e.changedTouches[0].clientX;
+		},
+		moveTouch(e) {
+			this.moveX = e.changedTouches[0].clientX - this.startX;
+		},
+		endTouch(e) {
+			if (Math.abs(this.moveX) > 90) {
+				// 滑动距离超过90px时才进行切换  
+				uni.vibrateShort()
+				if (this.moveX > 0) {
+					// 向右滑动  
+					if (this.headerTab === 0) {
+						this.next()
+					} else {
+						this.pre()
+					}
+				} else {
+					// 向左滑动  
+					if (this.headerTab === 1) {
+						this.pre()
+					} else {
+						this.next()
+					}
+				}
+				this.startX = 0; // 重置起始位置
+				this.moveX = 0; // 重置移动距离
+			}
+		},
+
+		// 取消穿透
+		clean() {
+			// console.log('click')
+			if (this.closeOnClickMask)
+				this.close()
+		},
+		bindDateChange(e) {
+			const value = e.detail.value + '-1'
+			this.setDate(value)
+
+			const { year, month } = this.cale.getDate(value)
+			this.$emit('monthSwitch', {
+				year,
+				month
+			})
+		},
+		/**
+		 * 初始化日期显示
+		 * @param {Object} date
+		 */
+		init(date) {
+			this.cale.setDate(date)
+			this.weeks = this.cale.weeks
+			this.nowDate = this.calendar = this.cale.getInfo(date)
+		},
+		/**
+		 * 打开日历弹窗
+		 */
+		open() {
+			// 弹窗模式并且清理数据
+			if (this.clearDate && !this.insert) {
+				this.cale.cleanMultipleStatus()
+				// this.cale.setDate(this.date)
+				this.init(this.date)
+			}
+			this.show = true
+			this.$nextTick(() => {
+				setTimeout(() => {
+					this.aniMaskShow = true
+				}, 50)
+			})
+		},
+		/**
+		 * 关闭日历弹窗
+		 */
+		close() {
+			this.aniMaskShow = false
+			this.$nextTick(() => {
+				setTimeout(() => {
+					this.show = false
+					this.$emit('close')
+				}, 300)
+			})
+		},
+		/**
+		 * 确认按钮
+		 */
+		confirm() {
+			this.setEmit('confirm')
+			this.close()
+		},
+		/**
+		 * 变化触发
+		 */
+		change() {
+			if (!this.insert) return
+			this.setEmit('change')
+		},
+		/**
+		 * 选择月份触发
+		 */
+		monthSwitch() {
+			let {
+				year,
+				month
+			} = this.nowDate
+			this.$emit('monthSwitch', {
+				year,
+				month: Number(month)
+			})
+		},
+		/**
+		 * 派发事件
+		 * @param {Object} name
+		 */
+		setEmit(name) {
+			let {
+				year,
+				month,
+				date,
+				fullDate,
+				lunar,
+				extraInfo
+			} = this.calendar
+			this.$emit(name, {
+				range: this.cale.multipleStatus,
+				year,
+				month,
+				date,
+				fulldate: fullDate,
+				lunar,
+				extraInfo: extraInfo || {}
+			})
+		},
+		/**
+		 * 选择天触发
+		 * @param {Object} weeks
+		 */
+		choiceDate(weeks) {
+			if (weeks.disable) {
+				// const { month,year } = this.calendar
+				// const clickmonth = weeks.month
+				// const clickyear = weeks.lunar.cYear
+				// console.log(month,year,clickmonth,clickyear)
+				// if (month > clickmonth) {
+				// 	if(year === clickyear || year < clickyear){
+				// 		this.pre()
+				// 	}else{
+				// 		this.next()
+				// 	}
+
+				// } else {
+				// 	this.next()
+				// }
+				return
+			}
+			uni.vibrateShort()
+			this.calendar = weeks
+			// 设置多选
+			this.cale.setMultiple(this.calendar.fullDate)
+			this.weeks = this.cale.weeks
+			this.change()
+		},
+		/**
+		 * 回到今天
+		 */
+		backToday() {
+			const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
+			const date = this.cale.getDate(new Date())
+			const todayYearMonth = `${date.year}-${date.month}`
+
+			this.init(date.fullDate)
+
+			if (nowYearMonth !== todayYearMonth) {
+				this.monthSwitch()
+			}
+
+			this.change()
+		},
+		/**
+		 * 上个月
+		 */
+		pre() {
+			const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
+			this.setDate(preDate)
+			this.monthSwitch()
+
+		},
+		/**
+		 * 下个月
+		 */
+		next() {
+			const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
+			this.setDate(nextDate)
+			this.monthSwitch()
+		},
+		/**
+		 * 设置日期
+		 * @param {Object} date
+		 */
+		setDate(date) {
+			this.cale.setDate(date)
+			this.weeks = this.cale.weeks
+			this.nowDate = this.cale.getInfo(date)
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+$uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
+$uni-border-color: #EDEDED;
+$uni-text-color: #333;
+$uni-bg-color-hover: #f1f1f1;
+$uni-font-size-base: 14px;
+$uni-text-color-placeholder: #808080;
+$uni-color-subtitle: #555555;
+$uni-text-color-grey: #999;
+
+.uni-calendar {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: column;
+}
+
+.uni-calendar__mask {
+	position: fixed;
+	bottom: 0;
+	top: 0;
+	left: 0;
+	right: 0;
+	background-color: $uni-bg-color-mask;
+	transition-property: opacity;
+	transition-duration: 0.3s;
+	opacity: 0;
+	/* #ifndef APP-NVUE */
+	z-index: 99;
+	/* #endif */
+}
+
+.uni-calendar--mask-show {
+	opacity: 1
+}
+
+.uni-calendar--fixed {
+	position: fixed;
+	/* #ifdef APP-NVUE */
+	bottom: 0;
+	/* #endif */
+	left: 0;
+	right: 0;
+	transition-property: transform;
+	transition-duration: 0.3s;
+	transform: translateY(460px);
+	/* #ifndef APP-NVUE */
+	bottom: calc(var(--window-bottom));
+	z-index: 99;
+	/* #endif */
+}
+
+.uni-calendar--ani-show {
+	transform: translateY(0);
+}
+
+.uni-calendar__content {
+	background-color: #fff;
+	border-radius: 16rpx 16rpx 0 0;
+	padding-bottom: 40rpx;
+}
+
+.uni-calendar__header {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	justify-content: center;
+	align-items: center;
+	height: 50px;
+	border-bottom-color: $uni-border-color;
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+}
+
+.uni-calendar__title {
+	font-size: 16px;
+	color: $uni-text-color;
+	padding: 24rpx 0 0 24rpx;
+}
+
+.uni-calendar--fixed-top {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	justify-content: space-between;
+	// border-top-color: $uni-border-color;
+	// border-top-style: solid;
+	// border-top-width: 1px;
+}
+
+.uni-calendar--fixed-width {
+	width: 50px;
+}
+
+.uni-calendar__backtoday {
+	position: absolute;
+	right: 0;
+	top: 25rpx;
+	padding: 0 5px;
+	padding-left: 10px;
+	height: 25px;
+	line-height: 25px;
+	font-size: 12px;
+	border-top-left-radius: 25px;
+	border-bottom-left-radius: 25px;
+	color: $uni-text-color;
+	background-color: $uni-bg-color-hover;
+}
+
+.uni-calendar__header-text {
+	text-align: center;
+	width: 100px;
+	font-size: $uni-font-size-base;
+	color: $uni-text-color;
+}
+
+.uni-calendar__header-btn-box {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	align-items: center;
+	justify-content: center;
+	width: 50px;
+	height: 50px;
+}
+
+.uni-calendar__header-btn {
+	width: 10px;
+	height: 10px;
+	border-left-color: $uni-text-color-placeholder;
+	border-left-style: solid;
+	border-left-width: 2px;
+	border-top-color: $uni-color-subtitle;
+	border-top-style: solid;
+	border-top-width: 2px;
+}
+
+.uni-calendar--left {
+	transform: rotate(-45deg);
+}
+
+.uni-calendar--right {
+	transform: rotate(135deg);
+}
+
+
+.uni-calendar__weeks {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+
+	background-color: #F0F0F0;
+	margin: 24rpx;
+	border-radius: 16rpx;
+}
+
+.uni-calendar__weeks-item {
+	background: #FFFFFF;
+	flex: 1;
+}
+
+.uni-calendar__weeks-day {
+	flex: 1;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	height: 45px;
+	border-bottom-color: #F5F5F5;
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+}
+
+.uni-calendar__weeks-day-text {
+	font-size: 14px;
+}
+
+.uni-calendar__box {
+	position: relative;
+}
+
+.uni-calendar__box-bg {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	justify-content: center;
+	align-items: center;
+	position: absolute;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+}
+
+.uni-calendar__box-bg-text {
+	font-size: 200px;
+	font-weight: bold;
+	color: $uni-text-color-grey;
+	opacity: 0.1;
+	text-align: center;
+	/* #ifndef APP-NVUE */
+	line-height: 1;
+	/* #endif */
+}
+</style>

+ 361 - 0
uni_modules/uni-calendar/components/uni-calendar/util.js

@@ -0,0 +1,361 @@
+import CALENDAR from './calendar.js'
+
+class Calendar {
+	constructor({
+		date,
+		selected,
+		startDate,
+		endDate,
+		range
+	} = {}) {
+		// 当前日期
+		this.date = this.getDate(new Date()) // 当前初入日期
+		// 打点信息
+		this.selected = selected || [];
+		// 范围开始
+		this.startDate = startDate
+		// 范围结束
+		this.endDate = endDate
+		this.range = range
+		// 多选状态
+		this.cleanMultipleStatus()
+		// 每周日期
+		this.weeks = {}
+		// this._getWeek(this.date.fullDate)
+	}
+	/**
+	 * 设置日期
+	 * @param {Object} date
+	 */
+	setDate(date) {
+		this.selectDate = this.getDate(date)
+		this._getWeek(this.selectDate.fullDate)
+	}
+
+	/**
+	 * 清理多选状态
+	 */
+	cleanMultipleStatus() {
+		this.multipleStatus = {
+			before: '',
+			after: '',
+			data: []
+		}
+	}
+
+	/**
+	 * 重置开始日期
+	 */
+	resetSatrtDate(startDate) {
+		// 范围开始
+		this.startDate = startDate
+
+	}
+
+	/**
+	 * 重置结束日期
+	 */
+	resetEndDate(endDate) {
+		// 范围结束
+		this.endDate = endDate
+	}
+
+	/**
+	 * 获取任意时间
+	 */
+	getDate(date, AddDayCount = 0, str = 'day') {
+		if (!date) {
+			date = new Date()
+		}
+		if (typeof date !== 'object') {
+			date = date.replace(/-/g, '/')
+		}
+		const dd = new Date(date)
+		switch (str) {
+			case 'day':
+				dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
+				break
+			case 'month':
+				if (dd.getDate() === 31 && AddDayCount>0) {
+					dd.setDate(dd.getDate() + AddDayCount)
+				} else {
+					const preMonth = dd.getMonth()
+					dd.setMonth(preMonth + AddDayCount) // 获取AddDayCount天后的日期
+					const nextMonth = dd.getMonth()
+					// 处理 pre 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
+					if(AddDayCount<0 && preMonth!==0 && nextMonth-preMonth>AddDayCount){
+						dd.setMonth(nextMonth+(nextMonth-preMonth+AddDayCount))
+					}
+					// 处理 next 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
+					if(AddDayCount>0 && nextMonth-preMonth>AddDayCount){
+						dd.setMonth(nextMonth-(nextMonth-preMonth-AddDayCount))
+					}
+				}
+				break
+			case 'year':
+				dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
+				break
+		}
+		const y = dd.getFullYear()
+		const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
+		const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
+		return {
+			fullDate: y + '-' + m + '-' + d,
+			year: y,
+			month: m,
+			date: d,
+			day: dd.getDay()
+		}
+	}
+
+
+	/**
+	 * 获取上月剩余天数
+	 */
+	_getLastMonthDays(firstDay, full) {
+		let dateArr = []
+		for (let i = firstDay; i > 0; i--) {
+			const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
+			dateArr.push({
+				date: beforeDate,
+				month: full.month - 1,
+				lunar: this.getlunar(full.year, full.month - 1, beforeDate),
+				disable: true
+			})
+		}
+		return dateArr
+	}
+	/**
+	 * 获取本月天数
+	 */
+	_currentMonthDys(dateData, full) {
+		let dateArr = []
+		let fullDate = this.date.fullDate
+		for (let i = 1; i <= dateData; i++) {
+			let nowDate = full.year + '-' + (full.month < 10 ?
+				full.month : full.month) + '-' + (i < 10 ?
+				'0' + i : i)
+			// 是否今天
+			let isDay = fullDate === nowDate
+			// 获取打点信息
+			let info = this.selected && this.selected.find((item) => {
+				if (this.dateEqual(nowDate, item.date)) {
+					return item
+				}
+			})
+
+			// 日期禁用
+			let disableBefore = true
+			let disableAfter = true
+			if (this.startDate) {
+				// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
+				// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
+				disableBefore = this.dateCompare(this.startDate, nowDate)
+			}
+
+			if (this.endDate) {
+				// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
+				// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
+				disableAfter = this.dateCompare(nowDate, this.endDate)
+			}
+			let multiples = this.multipleStatus.data
+			let checked = false
+			let multiplesStatus = -1
+			if (this.range) {
+				if (multiples) {
+					multiplesStatus = multiples.findIndex((item) => {
+						return this.dateEqual(item, nowDate)
+					})
+				}
+				if (multiplesStatus !== -1) {
+					checked = true
+				}
+			}
+			let data = {
+				fullDate: nowDate,
+				year: full.year,
+				date: i,
+				multiple: this.range ? checked : false,
+				beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate),
+				afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate),
+				month: full.month,
+				lunar: this.getlunar(full.year, full.month, i),
+				disable: !(disableBefore && disableAfter),
+				isDay
+			}
+			if (info) {
+				data.extraInfo = info
+				data.disable = info.disable  ?? false
+			}
+
+			dateArr.push(data)
+		}
+		return dateArr
+	}
+	/**
+	 * 获取下月天数
+	 */
+	_getNextMonthDays(surplus, full) {
+		let dateArr = []
+		for (let i = 1; i < surplus + 1; i++) {
+			dateArr.push({
+				date: i,
+				month: Number(full.month) + 1,
+				lunar: this.getlunar(full.year, Number(full.month) + 1, i),
+				disable: true
+			})
+		}
+		return dateArr
+	}
+
+	/**
+	 * 获取当前日期详情
+	 * @param {Object} date
+	 */
+	getInfo(date) {
+		if (!date) {
+			date = new Date()
+		}
+		const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
+		return dateInfo
+	}
+
+	/**
+	 * 比较时间大小
+	 */
+	dateCompare(startDate, endDate) {
+		// 计算截止时间
+		startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
+		// 计算详细项的截止时间
+		endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
+		if (startDate <= endDate) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+	/**
+	 * 比较时间是否相等
+	 */
+	dateEqual(before, after) {
+		// 计算截止时间
+		before = new Date(before.replace('-', '/').replace('-', '/'))
+		// 计算详细项的截止时间
+		after = new Date(after.replace('-', '/').replace('-', '/'))
+		if (before.getTime() - after.getTime() === 0) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+
+	/**
+	 * 获取日期范围内所有日期
+	 * @param {Object} begin
+	 * @param {Object} end
+	 */
+	geDateAll(begin, end) {
+		var arr = []
+		var ab = begin.split('-')
+		var ae = end.split('-')
+		var db = new Date()
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
+		var de = new Date()
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
+		for (var k = unixDb; k <= unixDe;) {
+			k = k + 24 * 60 * 60 * 1000
+			arr.push(this.getDate(new Date(parseInt(k))).fullDate)
+		}
+		return arr
+	}
+	/**
+	 * 计算阴历日期显示
+	 */
+	getlunar(year, month, date) {
+		return CALENDAR.solar2lunar(year, month, date)
+	}
+	/**
+	 * 设置打点
+	 */
+	setSelectInfo(data, value) {
+		this.selected = value
+		this._getWeek(data)
+	}
+
+	/**
+	 *  获取多选状态
+	 */
+	setMultiple(fullDate) {
+		let {
+			before,
+			after
+		} = this.multipleStatus
+
+		if (!this.range) return
+		if (before && after) {
+			this.multipleStatus.before = ''
+			this.multipleStatus.after = ''
+			this.multipleStatus.data = []
+		} else {
+			if (!before) {
+				this.multipleStatus.before = fullDate
+			} else {
+				this.multipleStatus.after = fullDate
+				if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
+				} else {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
+				}
+			}
+		}
+		this._getWeek(fullDate)
+	}
+
+	/**
+	 * 获取每周数据
+	 * @param {Object} dateData
+	 */
+	_getWeek(dateData) {
+		const {
+			year,
+			month
+		} = this.getDate(dateData)
+		let firstDay = new Date(year, month - 1, 1).getDay()
+		let currentDay = new Date(year, month, 0).getDate()
+		let dates = {
+			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
+			currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
+			nextMonthDays: [], // 下个月开始几天
+			weeks: []
+		}
+		let canlender = []
+		const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
+		dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
+		canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
+		let weeks = {}
+		// 拼接数组  上个月开始几天 + 本月天数+ 下个月开始几天
+		for (let i = 0; i < canlender.length; i++) {
+			if (i % 7 === 0) {
+				weeks[parseInt(i / 7)] = new Array(7)
+			}
+			weeks[parseInt(i / 7)][i % 7] = canlender[i]
+		}
+		this.canlender = canlender
+		this.weeks = weeks
+	}
+
+	//静态方法
+	// static init(date) {
+	// 	if (!this.instance) {
+	// 		this.instance = new Calendar(date);
+	// 	}
+	// 	return this.instance;
+	// }
+}
+
+
+export default Calendar

+ 85 - 0
uni_modules/uni-calendar/package.json

@@ -0,0 +1,85 @@
+{
+  "id": "uni-calendar",
+  "displayName": "uni-calendar 日历",
+  "version": "1.4.11",
+  "description": "日历组件",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "日历",
+    "",
+    "打卡",
+    "日历选择"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 103 - 0
uni_modules/uni-calendar/readme.md

@@ -0,0 +1,103 @@
+
+
+## Calendar 日历
+> **组件名:uni-calendar**
+> 代码块: `uCalendar`
+
+
+日历组件
+
+> **注意事项**
+> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
+> - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js)  
+> - 仅支持自定义组件模式
+> - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date()
+> - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意
+> - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动
+
+
+### 安装方式
+
+本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
+
+如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
+
+### 基本用法
+
+在 ``template`` 中使用组件
+
+```html
+<view>
+	<uni-calendar 
+	:insert="true"
+	:lunar="true" 
+	:start-date="'2019-3-2'"
+	:end-date="'2019-5-20'"
+	@change="change"
+	 />
+</view>
+```
+
+### 通过方法打开日历
+
+需要设置 `insert` 为 `false`
+
+```html
+<view>
+	<uni-calendar 
+	ref="calendar"
+	:insert="false"
+	@confirm="confirm"
+	 />
+	 <button @click="open">打开日历</button>
+</view>
+```
+
+```javascript
+
+export default {
+	data() {
+		return {};
+	},
+	methods: {
+		open(){
+			this.$refs.calendar.open();
+		},
+		confirm(e) {
+			console.log(e);
+		}
+	}
+};
+
+```
+
+
+## API
+
+### Calendar Props
+
+|  属性名	|    类型	| 默认值| 说明																													|
+| -	| -	| - | - |
+| date		| String	|-		| 自定义当前时间,默认为今天																							|
+| lunar		| Boolean	| false	| 显示农历																												|
+| startDate	| String	|-		| 日期选择范围-开始日期																									|
+| endDate	| String	|-		| 日期选择范围-结束日期																									|
+| range		| Boolean	| false	| 范围选择																												|
+| insert	| Boolean	| false	| 插入模式,可选值,ture:插入模式;false:弹窗模式;默认为插入模式														|
+|clearDate	|Boolean	|true	|弹窗模式是否清空上次选择内容	|
+| selected	| Array		|-		| 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]	|
+|showMonth	| Boolean	| true	| 是否显示月份为背景																									|
+
+### Calendar Events
+
+|  事件名		| 说明								|返回值|
+| -	|	-	| -	|
+| open	| 弹出日历组件,`insert :false` 时生效|- 	|
+
+
+
+
+
+## 组件示例
+
+点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar)

+ 1 - 0
utils/config.js

@@ -1,5 +1,6 @@
 const env = 'develop' // develop production
 
+// export const BASE_URL = env == 'production'?'https://api.zonelife.cn':'http://8.137.120.225:9006';
 export const BASE_URL = env == 'production'?'https://api.zonelife.cn':'https://api.dev.zonelife.cn';
 
 export const SHOP_ID =  env == 'production'?'6618e31330fed626f8b57f18':'6618d9be32e63427d1a17009';

+ 132 - 0
utils/request.js.dev

@@ -0,0 +1,132 @@
+//用BASEURL来表示域名地址
+// const BASE_URL = 'http://192.168.110.153:9002'
+// const BASE_URL = 'https://zswl.api.jpy.wang/zswl-cloud-bdb'
+// const BASE_URL = 'https://api.dev.zonelife.cn'
+import {
+	BASE_URL
+} from '@/utils/config.js'
+
+//封装请求方法
+export let request = (options) => {
+	return new Promise((resolve, reject) => {
+			//请求路径拼接,,其中options.url就是通过下面方法myRequest获取到接口部分的url
+			//method--请求方法,不是method的post就是get
+			console.log('requert',options.url.indexOf('/zswl-cloud-bdb'))
+			let base_url = BASE_URL
+			options.url = options.url.replace('/zswl-cloud-shop','');
+			base_url = options.url.indexOf('/zswl-cloud-bdb') != -1 ? 'https://api.dev.zonelife.cn' : BASE_URL
+			base_url = options.url.indexOf('/springbatchservice') != -1 ? 'https://api.dev.zonelife.cn' : base_url  
+			if(options.url.indexOf('http')==-1){
+				options.url = base_url + options.url
+			}
+			console.log(options.url)
+			//请求的参数,当没有参数的时候就是空对象
+			request.requestTask =uni.request({
+					url: options.url,
+					method: options.method || 'GET',
+					data: options.data || {},
+					header: {
+						accessToken: uni.getStorageSync('token')
+					},
+					success: (res) => {
+						if (res.statusCode !== 200) {
+							return uni.showToast({
+								title: '获取失败',
+								icon: 'error',
+							})
+							// uni.hideLoading()
+						} else if (res.data.msg != '成功') { //接口返回报错
+							uni.showToast({
+								title: res.data.content || res.data.msg || '请求失败',
+								icon: 'none',
+								duration:3000
+							})
+							console.error('报错接口:',base_url + options.url,'报错信息:', res.data);
+
+							// token过期或出了问题
+							if (res.data.exception && res.data.exception.type ==
+								'AuthenticationCredentialsNotFoundException') {
+									uni.showToast({
+										title: '登录过期',
+										icon: 'error'
+									})
+								if (uni.getStorageSync('refreshToken') && uni.getStorageSync('token') ) {
+									// 刷新token
+									uni.showLoading({
+										title: '刷新登录中',
+									})
+									uni.request({
+											url: base_url + '/zswl-cloud-bdb/user/refreshToken',
+											method: 'GET',
+											data: {
+												refreshToken: uni.getStorageSync('refreshToken')
+											},
+											success(r) {
+												uni.hideLoading()
+												if (r.data.content == '刷新令牌错误') {
+													uni.showToast({
+														title: '登录失效',
+														icon: 'fail'
+													})
+													uni.clearStorageSync()
+													uni.reLaunch({
+														url: '/pages/index/index'
+													})
+												} else {
+													
+													uni.setStorageSync('token', r.data.content) //登录状态
+													// 刷新页面
+													var pages = getCurrentPages();
+													var url = '/' + pages[pages.length - 1].route
+													uni.reLaunch({
+														url
+													})
+													
+											}
+										}
+									})
+								}else{
+									
+									uni.showModal({
+										title:'请登录',
+										confirmText:'去登录',
+										success(res){
+											if(res.confirm){
+												var pages = getCurrentPages()
+												let parmas = pages[pages.length - 1].options
+												console.log(11111,parmas);
+												let str = ''
+												for (let key in parmas) {
+													str += `&${key}=${parmas[key]}`
+												}
+												console.log(2222222,str);
+												var url = '/' + pages[pages.length - 1].route
+												console.log(333333,url);
+												uni.reLaunch({
+													url:'/login/login/login?redirect='+url+str
+												})
+											}else if (res.cancel) {
+												console.log('拒绝登录,跳转首页');
+												uni.switchTab({
+													url:'/pages/index/index'
+												})
+											}
+										}
+									})
+									
+								}
+						}
+
+					}
+					resolve(res.data)
+				},
+				//请求失败
+				fail: (err) => {
+					uni.showToast({
+						title: '请求接口失败'
+					})
+					reject(err)
+				}
+			})
+	})
+}

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