mr-zhou-zhou 2 年之前
当前提交
cec882653c
共有 76 个文件被更改,包括 4868 次插入0 次删除
  1. 16 0
      .hbuilderx/launch.json
  2. 57 0
      App.vue
  3. 0 0
      common/amap-wx.js
  4. 31 0
      common/config.js
  5. 53 0
      common/loc.js
  6. 110 0
      common/mixin.js
  7. 170 0
      common/request/api.js
  8. 119 0
      common/request/index.js
  9. 318 0
      common/request/request.js
  10. 344 0
      common/tools.js
  11. 42 0
      components/empty.vue
  12. 198 0
      components/mix-popup/mix-popup.vue
  13. 63 0
      components/navbar-line.vue
  14. 96 0
      components/single-list.vue
  15. 54 0
      components/submit-btn.vue
  16. 20 0
      index.html
  17. 32 0
      main.js
  18. 74 0
      manifest.json
  19. 125 0
      pages.json
  20. 231 0
      pages/address/address.vue
  21. 199 0
      pages/address/addressManage.vue
  22. 52 0
      pages/home/home.vue
  23. 91 0
      pages/login/components/phoneCode.vue
  24. 202 0
      pages/login/components/vericode.vue
  25. 252 0
      pages/login/forget.vue
  26. 71 0
      pages/login/login.vue
  27. 288 0
      pages/login/mobileLogin.vue
  28. 284 0
      pages/login/register.vue
  29. 64 0
      pages/mang/components/zetank-notice.vue
  30. 445 0
      pages/mang/detail.vue
  31. 386 0
      pages/mang/mang.vue
  32. 19 0
      pages/mine/addrs.vue
  33. 19 0
      pages/mine/mine.vue
  34. 二进制
      static/addr-del.png
  35. 二进制
      static/addr-edit.png
  36. 二进制
      static/check-do.png
  37. 二进制
      static/check-un.png
  38. 二进制
      static/empty.png
  39. 二进制
      static/log_mima.png
  40. 二进制
      static/log_tel.png
  41. 二进制
      static/log_yan.png
  42. 二进制
      static/love-full.png
  43. 二进制
      static/love.png
  44. 二进制
      static/mang-ads.png
  45. 二进制
      static/mang-all.png
  46. 二进制
      static/mang-bg.png
  47. 二进制
      static/mang-buding.png
  48. 二进制
      static/mang-dangao.png
  49. 二进制
      static/mang-dianxin.png
  50. 二进制
      static/mang-listbg.png
  51. 二进制
      static/mang-loc.png
  52. 二进制
      static/mang-pdangao.png
  53. 二进制
      static/mang-phone.png
  54. 二进制
      static/mang-pisa.png
  55. 二进制
      static/mang-sear.png
  56. 二进制
      static/mang-tabbg (1).png
  57. 二进制
      static/mang-tabbg.png
  58. 二进制
      static/mang-tusi.png
  59. 二进制
      static/mang_star.png
  60. 二进制
      static/mangd-loc.png
  61. 二进制
      static/mangd-rarrow.png
  62. 二进制
      static/r-arrow.png
  63. 二进制
      static/tab/home-full.png
  64. 二进制
      static/tab/home.png
  65. 二进制
      static/tab/mang-full.png
  66. 二进制
      static/tab/mang.png
  67. 二进制
      static/tab/mine-full.png
  68. 二进制
      static/tab/mine.png
  69. 二进制
      static/tab/order-full.png
  70. 二进制
      static/tab/order.png
  71. 二进制
      static/temp/pro.png
  72. 27 0
      store/index.js
  73. 49 0
      store/modules/userInfo.js
  74. 151 0
      style/common.css
  75. 102 0
      style/icon-删除.css
  76. 14 0
      uni.scss

+ 16 - 0
.hbuilderx/launch.json

@@ -0,0 +1,16 @@
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+    "version": "0.0",
+    "configurations": [{
+     	"default" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"mp-weixin" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"type" : "uniCloud"
+     }
+    ]
+}

+ 57 - 0
App.vue

@@ -0,0 +1,57 @@
+<script>
+	import Vue from 'vue';
+	export default {
+		onLaunch: function() {
+			// this.$store.dispatch('userInfo/getinfo');
+			uni.getSystemInfo({
+				success: function(e) {
+					// #ifndef MP
+					Vue.prototype.StatusBar = e.statusBarHeight;
+					if (e.platform == 'android') {
+						Vue.prototype.CustomBar = e.statusBarHeight + 50;
+					} else {
+						Vue.prototype.CustomBar = e.statusBarHeight + 45;
+					};
+					// #endif
+			
+					// #ifdef MP-WEIXIN || MP-QQ
+					Vue.prototype.StatusBar = e.statusBarHeight;
+					
+					Vue.prototype.pxToRpx = 750 / e.windowWidth; //e.windowHeight会有差别顶部栏
+					let capsule = wx.getMenuButtonBoundingClientRect();
+					if (capsule) {
+						Vue.prototype.Custom = capsule;
+						// Vue.prototype.capsuleSafe = uni.upx2px(750) - capsule.left + uni.upx2px(750) - capsule.right;
+						Vue.prototype.CustomBar = capsule.bottom + capsule.top - e.statusBarHeight;
+					} else {
+						Vue.prototype.CustomBar = e.statusBarHeight + 50;
+					}
+					// #endif		
+				
+			
+					// #ifdef MP-ALIPAY
+					Vue.prototype.StatusBar = e.statusBarHeight;
+					Vue.prototype.CustomBar = e.statusBarHeight + e.titleBarHeight;
+					// #endif
+				}
+			})
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		}
+	}
+</script>
+
+<style lang='scss'>
+	@import "style/common.css";
+	/* @import "style/icon.css"; */
+	
+	.input-placeholder,
+	.placeholder {
+		color: $placeholder-color !important;
+	}
+	/*每个页面公共css */
+</style>

文件差异内容过多而无法显示
+ 0 - 0
common/amap-wx.js


+ 31 - 0
common/config.js

@@ -0,0 +1,31 @@
+export default{
+	
+	// consoleBaseUrl:"https://api.icookj.cn/",
+	// uploadImgUrl:'https://api.icookj.cn/api/common/upload'
+	
+	consoleBaseUrl:"https://icool.lanmaonet.com/",
+	uploadImgUrl:'https://icool.lanmaonet.com/api/common/upload'
+}
+export const STATUS = {
+	NOR:0,
+	SECING:1,  //二级市场寄卖中
+	GIVEING:2, //转赠中
+	MERGED:3,  //已合成 - 不会出现
+	LINKING:4, //转链中
+}
+export const SOURCE = {
+	CANG:0,
+	MANG:1,
+	SEC:2,
+	GIVE:3,
+	MERGE:4
+}
+export const INIT_SOURCE = {
+	CANG:0,
+	MANG:1,
+}
+export const PAY_STATUS = {
+	UNPAY:0,
+	PAYED:1,
+	CANCELED:2
+}

+ 53 - 0
common/loc.js

@@ -0,0 +1,53 @@
+import Tools from './tools.js';
+const amapFile = require('@/common/amap-wx.js')
+var myAmapFun = new amapFile.AMapWX({key: '01d42bb16e23baca11f1227164a200fd'}); //01d42bb16e23baca11f1227164a200fd使用本人高德
+
+//逆向解析
+function getNameByLatLng(latitude,longitude){
+	return new Promise((resolve,reject)=>{
+		//未使用微信授权api
+		myAmapFun.getRegeo({
+			location:longitude+','+latitude,
+			success: function(data){
+				// console.log("getRegeo data--",data);
+				resolve(data[0]);
+			},
+			fail: err=> {
+				reject("逆向地址解析错误");
+				console.log(err)
+			}
+		})
+	})
+}
+
+async function chooseLoc(){
+	return new Promise(async (resolve,reject)=>{
+	   let [cErr,cRes] = await uni.chooseLocation({type:'gcj02'});
+	   if(cErr){
+		   reject(cErr)
+		   return;
+	   }
+	   let {latitude,longitude} = cRes
+	   let loc = await getNameByLatLng(latitude,longitude) || {};
+	   // console.log('getNameByLatLng===',loc,loc.regeocodeData.addressComponent.province);
+	   if(loc.regeocodeData && loc.regeocodeData.addressComponent){
+		   let {province,city,district,township} = loc.regeocodeData.addressComponent;
+		   let address = district+township+loc.desc;
+		   resolve({
+			   province,
+			   city, 
+			   area:district,
+			   address,
+			   latitude,
+			   longitude,   
+		   })
+		   return;
+	   }
+	   return reject(false);	
+	})
+}
+
+export{
+   getNameByLatLng,
+   chooseLoc,
+}

+ 110 - 0
common/mixin.js

@@ -0,0 +1,110 @@
+
+import {mapState} from 'vuex';
+import Tools from '@/common/tools.js';
+export default{
+	data() {	
+		return{
+		}
+	},
+	computed:{
+		...mapState('userInfo',{
+			userInfo: (state) => state.userInfo
+		}),
+		//isOpen 为字符串'1或0'
+		// ...mapState('baseConfig',{
+		// 	isOpen: (state) => state.isOpen
+		// }),
+	},
+	methods: {
+		hasInfo(){
+			// console.log("mixinx.js----",this.userInfo,Boolean(Object.keys(this.userInfo).length > 0),this.userInfo.id)
+			return   this.userInfo && Boolean(Object.keys(this.userInfo).length > 0) && this.userInfo.id;
+		},
+		
+		// checkIsAuth(){
+		// 	return new Promise( async (resolve,reject)=>{
+		// 		if(this.userInfo.is_auth === 2){
+		// 			resolve(true);
+		// 			return;
+		// 		}
+		// 		let userInfo = await this.$store.dispatch('user/getUserInfo');
+		// 		if(userInfo.is_auth === 2){
+		// 			resolve(true);
+		// 		}else if(userInfo.is_auth === 0){
+		// 			this.$tools.msg('您尚未实名认证')
+		// 			reject(false);
+		// 		}else if(userInfo.is_auth === 1){
+		// 			this.$tools.msg('您实名认证审核中..')
+		// 			reject(false);
+		// 		}else if(userInfo.is_auth === -1){
+		// 			this.$tools.msg('您实名未通过,请重新申请')
+		// 			reject(false);
+		// 		}
+		// 	})
+		// },
+		
+		
+		/**
+		 * 跳转再封装,不支持复杂传参。
+		 * const openAnimation=['slide-in-right','slide-in-left','slide-in-top','slide-in-bottom','pop-in','fade-in','zoom-out','zoom-fade-out','none'];
+		 * const closeAnimation=['slide-out-right','slide-out-left','slide-out-top','slide-out-bottom','pop-out','fade-out','zoom-in','zoom-fade-in','none'];
+		 * animationDuration:500,
+		 * animationType:openAnimation[~~(Math.random()*openAnimation.length-1)],
+		 * 
+		 */
+		navTo(path, params = {}, opt={}) {
+			//params不能{a:JSON.stringify...}
+			if(Tools.inAction('navTo',this)){
+				return;
+			}
+			if(!path){
+				// Tools.msg('您跳转地址不存在哦~')
+				return;
+			}
+			opt={type:'push',isCheckLogin:false ,...opt}; //opt传一值补全
+			if(opt.isCheckLogin && !this.hasInfo()){
+				uni.navigateTo({
+					url:'/pages/login/login'
+				})
+				return;
+			}
+			let objParams = params;
+			let navObj={
+				push:'navigateTo',
+				replace:'redirectTo',
+				replaceAll:'reLaunch',
+				pushTab:'switchTab',
+			}
+			let pathUrl=path;
+			if(objParams && typeof objParams ==='object' && Object.keys(objParams).length > 0){
+				let pathArr=[];
+				Object.keys(objParams).forEach(k =>{
+					pathArr.push(`${k}=${objParams[k]}`);
+				})
+				pathUrl = path +'?'+pathArr.join('&');
+			}
+			// console.log('---',uni[navObj[opt.type]],pathUrl)
+			uni[navObj[opt.type]]({
+				url:pathUrl
+			})
+			
+		},
+		goBack() {
+			if (getCurrentPages().length < 2 && 'undefined' !== typeof __wxConfig) {
+				let url = '/' + __wxConfig.pages[0]
+				return uni.redirectTo({url})
+			}
+			uni.navigateBack({
+				delta: 1
+			});
+		},
+		// stopPrevent(){},
+		// developing(){
+		// 	Tools.msg('开发中....')
+		// },
+		// goDownload(){
+		// 	this.navTo('/pages/index/download')
+		// },
+		
+	},
+}

+ 170 - 0
common/request/api.js

@@ -0,0 +1,170 @@
+/**
+ * 接口列表文件
+ * auth代表接口是否需要token
+ * name:Request内做判断,url,auth,method,cacheType: local,session,isNprogress
+ */
+export default {
+    /**  用户 ↓ **/
+	user:{
+		userInfo:{
+			url:'api/user/userInfo',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		getUserOpenid:{
+			url:'api/user/getUserOpenid',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		wxMiniProgramLogin:{
+			url:'api/user/wxMiniProgramLogin',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		area_list:{
+			url:'api/area/area_list',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		profile:{
+			url:'api/user/profile',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		getuserinfo:{
+			url:'api/usercenter/getuserinfo',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		likeone:{
+			url:'api/Usercenter/likeone',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+	    signin:{
+			url:'api/Usersign/signin',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		voteshare:{
+			url:'api/wechatshare/voteshare',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		}
+	},
+	vip:{
+		vip_config:{
+			url:'api/pay/vip_config',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		vip_recharge:{
+			url:'api/pay/vip_recharge',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		
+	},
+	baseconfig:{
+		userinfo_enum:{
+			url:'api/baseconfig/userinfo_enum',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+			cacheType: 'session'
+		},
+		index:{
+			url:'api/baseconfig/index',
+			auth:false,
+			method:'POST',
+			isNprogress:true
+		}
+	},
+	usercenter: {
+		samecity:{
+			url:'api/usercenter/samecity',
+			auth:false,
+			method:'POST',
+			isNprogress:false,
+		},
+		newpeople:{
+			url:'api/usercenter/newpeople',
+			auth:false,
+			method:'POST',
+			isNprogress:false,
+		},
+	},
+    newvote: {
+		lists:{
+			url:'api/newvote/subject/lists',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		// info:{
+		// 	url:'api/newvote/subject/info',
+		// 	auth:false,
+		// 	method:'POST',
+		// 	isNprogress:true,
+		// },
+		//选手列表
+		playerlist:{
+			url:'api/newvote/subject/playerlist',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		//投票
+		record:{
+			url:'api/newvote/player/record',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		//排行榜
+		playerlist_votes:{
+			url:'api/newvote/subject/playerlist_votes',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		//人气榜
+		playerlist_renqi:{
+			url:'api/newvote/subject/playerlist_renqi',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		//选手详情
+		playerinfo:{
+			url:'api/newvote/subject/playerinfo',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		join:{
+			url:'api/newvote/Player/join',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		},
+		rule:{
+			url:'api/newvote/subject/info',
+			auth:false,
+			method:'POST',
+			isNprogress:true,
+		}
+	},
+	
+};

+ 119 - 0
common/request/index.js

@@ -0,0 +1,119 @@
+import Request from './request'
+import apiList from './api'
+// import store from '../../store/index.js'
+import Tools from '../tools.js'
+
+const TAG_NAME="REQUEST INDEX.JS";
+let cacheSessions = {}; //会话存储
+//data._isRefresh用于第二次强制请求
+export default function api(url, data = {},isNeedLoading=false) {
+	const request = new Request();
+	let api = getApiObj(url);
+	// console.log('api---',api,url);
+    let cacheType = api.cacheType;
+	const cacheKey = getCacheKey('',url,data)
+	/**
+	 * 正常:return加工后config,用于真正request
+	 * 异常:调用cancel后,request不在向下进行。type:2 resolve缓存结束。1reject
+	*/
+	request.interceptor.request((config, cancel) => { /* 请求之前拦截器 */
+	    const token = uni.getStorageSync('token') || '';
+		// console.log('request.interceptor.request',config);
+		let res;
+		//_isRefresh用于第二次请求
+		if( cacheType === 'local' && !data._isRefresh){
+			res = uni.getStorageSync(cacheKey);
+		}else if( cacheType === 'session' && !data._isRefresh){
+			// console.log('request.interceptor cacheType',cacheType,cacheSessions[cacheKey])
+			// 第一次session--要判断否则JSON.parse报错
+			if(cacheSessions[cacheKey]) res = JSON.parse(cacheSessions[cacheKey]);
+		}
+		if(res){
+			//调用者继续向下运行 cancel,本方法继续向下进行
+			cancel('已获得请求值,不再请求',res,2)
+			
+			// console.log('已获得请求值,不再请求---',cacheType,cacheSessions,res);
+			return;
+		}else if (api.auth && !token) {
+			//request 调用者直接报错不再向下进行。本方法继续向下进行
+			
+			cancel('暂未登录,已阻止此次API请求~')
+			setTimeout(()=>{
+				uni.redirectTo({
+					url:'/pages/index/loginMobile'
+				})
+			},800)
+			return;
+		}
+		//正常情况下,返config用于下步请求~
+		// console.log("request index",config);
+		if (token) {
+			config.data={...config.data,token};
+		}
+		return config;
+	});
+
+	request.interceptor.response((response) => { /* 请求之后拦截器 */
+	  
+	   //200情况
+       const data = response.data;
+       if( cacheType === 'local'){
+		  (data && data.code === 1) &&  uni.setStorage({key: cacheKey,data: data})
+       }else if(cacheType === 'session'){
+		   // console.log('request.interceptor response before--',cacheKey,cacheSessions[cacheKey],JSON.stringify(data.data || ''));
+		  (data && data.code === 1) && (cacheSessions[cacheKey] = JSON.stringify(data || '')); //统一request.js message还要用提示
+		  // console.log('request.interceptor response after----',cacheSessions[cacheKey])
+	   }
+       return response;
+		
+	}, (error) => { // 除200都在 ---request.js由validateStatus决定
+	    console.log('error--',error)
+	    if (error.statusCode) {
+			if(error.statusCode === 401){
+				const pages = getCurrentPages()
+				if(pages.length > 1 && pages[pages.length - 1].route== 'pages/login/login'){
+					   //解决同时多个请求,都跳登录页情况
+					   reject(TAG_NAME,'已在登录页,无需在次进入')
+					   return;
+				}
+				// console.log(TAG_NAME,options.url,'重新登录~')
+				uni.removeStorageSync('token')
+				uni.navigateTo({url:'/pages/login/login'})  
+				return;
+				// setTimeout(()=>{
+				//    //已有提示
+				//    uni.redirectTo({
+				// 	 url:'/pages/index/loginMobile'
+				//    })  
+				// },1200)
+			}
+	        Tools.msg(error.statusCode+' '+(error.data.msg|| '请求失败~'))
+	    }else{
+			Tools.msg('请求失败~')
+		}
+		return error;
+	})
+
+	return request.request({
+		url: api.url,
+		data,
+		method: api.method,
+		isNprogress: isNeedLoading || api.isNprogress,
+		header: api.header,
+	})
+
+}
+
+function getApiObj(url) {
+	let apiArray = url.split(".");
+	let api = apiList;
+	apiArray.forEach(v => {
+		api = api[v];
+	});
+	return api;
+}
+function getCacheKey(k, url,data={}){
+	// mydate.getDay()+ mydate.getHours()+ mydate.getMinutes()+mydate.getSeconds()+mydate.getMilliseconds()+ Math.round(Math.random() * 10000);
+	const key = k ? k : url;
+	return  "cache-"+key+JSON.stringify(data)
+}

+ 318 - 0
common/request/request.js

@@ -0,0 +1,318 @@
+import env from '../config.js'
+// import NProgress from 'nprogress'
+// import 'nprogress/nprogress.css'
+import store from '../../store/index.js'
+const TAG_NAME="REQUEST REQUEST.JS";
+import Tools from '../tools.js';
+
+export default class Request {
+	config = {
+		baseUrl: env.consoleBaseUrl,
+		header: {
+			'content-type': 'application/x-www-form-urlencoded',
+			// 'content-type': 'application/json',
+			// 'platform': uni.getStorageSync('platform'),
+		},
+		method: 'GET',
+		dataType: 'json',
+		// #ifndef MP-ALIPAY || APP-PLUS
+		responseType: 'text',
+		// #endif
+		custom: {},
+		// #ifdef MP-ALIPAY
+		timeout: 30000,
+		// #endif
+		// #ifdef APP-PLUS
+		sslVerify: false
+		// #endif
+	}
+
+	static posUrl(url) { /* 判断url是否为绝对路径 */
+		return /(http|https):\/\/([\w.]+\/?)\S*/.test(url)
+	}
+
+	// static addQueryString(params) {
+	// 	let paramsData = ''
+	// 	Object.keys(params).forEach(function(key) {
+	// 		paramsData += key + '=' + encodeURIComponent(params[key]) + '&'
+	// 	})
+	// 	return paramsData.substring(0, paramsData.length - 1)
+	// }
+
+	/**
+	 * @property {Function} request 请求拦截器
+	 * @property {Function} response 响应拦截器
+	 * @type {{request: Request.interceptor.request, response: Request.interceptor.response}}
+	 */
+	interceptor = {
+		/**
+		 * @param {Request~requestCallback} cb - 请求之前拦截,接收一个函数(config, cancel)=> {return config}。第一个参数为全局config,第二个参数为函数,调用则取消本次请求。
+		 */
+		request: (cb) => {
+			if (cb) {
+				this.requestBeforeFun = cb
+			}
+		},
+		/**
+		 * @param {Request~responseCallback} cb 响应拦截器,对响应数据做点什么
+		 * @param {Request~responseErrCallback} ecb 响应拦截器,对响应错误做点什么
+		 */
+		response: (cb, ecb) => {
+			if (cb && ecb) {
+				this.requestComFun = cb
+				this.requestComFail = ecb
+			}
+		}
+	}
+
+	requestBeforeFun(config) {
+		return config
+	}
+
+	requestComFun(response) {
+		return response
+	}
+
+	requestComFail(response) {
+		return response
+	}
+
+	/**
+	 * 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
+	 * @param { Number } statusCode - 请求响应体statusCode(只读)
+	 * @return { Boolean } 如果为true,则 resolve, 否则 reject
+	 */
+	validateStatus(statusCode) {
+		return statusCode === 200
+	}
+
+	/**
+	 * @Function
+	 * @param {Request~setConfigCallback} f - 设置全局默认配置
+	 */
+	setConfig(f) {
+		this.config = f(this.config)
+	}
+
+	/**
+	 * @Function
+	 * @param {Object} options - 请求配置项
+	 * @prop {String} options.url - 请求路径
+	 * @prop {Object} options.data - 请求参数
+	 * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
+	 * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
+	 * @prop {Object} [options.header = config.header] - 请求header
+	 * @prop {Object} [options.method = config.method] - 请求方法
+	 * @returns {Promise<unknown>}
+	 */
+	request(options = {}) {
+		options.baseUrl = this.config.baseUrl
+		options.dataType = options.dataType || this.config.dataType
+		// #ifndef MP-ALIPAY || APP-PLUS
+		options.responseType = options.responseType || this.config.responseType
+		// #endif
+		// #ifdef MP-ALIPAY
+		options.timeout = options.timeout || this.config.timeout
+		// #endif
+		options.url = options.url || ''
+		options.data = options.data || {}
+		options.params = options.params || {}
+		options.header = options.header || this.config.header
+		options.method = options.method || this.config.method
+		options.custom = { ...this.config.custom,
+			...(options.custom || {})
+		}
+		options.isNprogress = options.isNprogress || false 
+		// #ifdef APP-PLUS
+		options.sslVerify = options.sslVerify === undefined ? this.config.sslVerify : options.sslVerify
+		// #endif
+		// uni.showToast({
+		// 	icon: "loading",
+		// 	image: "/static/imgs//logo/logo.gif"
+		// })
+		return new Promise((resolve, reject) => {
+			let next = true
+			let handleRe = {}
+
+			options.complete = (response) => {
+				if(handleRe.isNprogress) {
+					// console.log('complete 支付成功 关闭弹窗~~',handleRe.isNprogress);
+					uni.hideLoading();
+				}
+				
+				response.config = handleRe
+				if (this.validateStatus(response.statusCode)) { // 成功
+				    response = this.requestComFun(response)
+					const data = response.data;
+					// const errLoginCode = ['100000','100001','100002','100003','100004'];
+					// if(data && data.code && errLoginCode.includes(data.code)){
+					//    const pages = getCurrentPages()
+					//    if(pages.length > 1 && pages[pages.length - 1].route== 'pages/index/login'){
+					// 	   //解决同时多个请求,都跳登录页情况
+					// 	   reject(TAG_NAME,'已在登录页,无需在次进入')
+					// 	   return;
+					//    }
+					//    console.log(TAG_NAME,options.url,'重新登录~')
+					//    uni.removeStorageSync('token')
+					//    setTimeout(()=>{
+					// 	   //已有提示
+					// 	   uni.redirectTo({
+					// 		 url:'/pages/index/login'
+					// 	   })  
+					//    },1200)
+					// }
+					if (data.code !== 1) { // 服务端返回的状态码不等于200,则reject()
+						uni.showToast({
+							title: data.msg || '请求出错,稍后重试',
+							icon: 'none',
+							duration: 2000,
+							mask: true
+						});
+						reject(response)
+					}else{
+						resolve(data)
+					}
+				}else {
+					response = this.requestComFail(response)
+					reject(response)
+				}
+			}
+			//type = 1 reject; type = 2 有缓存resolve,request是否向下进行
+			const cancel = (msg = 'handle cancel',data={},type=1, config = options) => {
+				const obj = {
+					msg: msg,
+					data,
+					config: config
+				}
+				type == 1 ? reject(obj) : resolve(data); //外层直接取res.code;
+				if(config.isNprogress) uni.hideLoading();  //store.commit('loading/HIDE')
+				next = false 
+			}
+
+			handleRe = { ...this.requestBeforeFun(options, cancel)}
+			const _config = { ...handleRe}
+			// console.log('_config---', next, _config);
+			if (!next) return;
+			delete _config.custom
+			if(_config.isNprogress) uni.showLoading({})
+			let mergeUrl = Request.posUrl(_config.url) ? _config.url : (_config.baseUrl + _config.url)
+			// 仅使用_config.data,未使用_config.params,且data内要添加token
+			// if (JSON.stringify(_config.params) !== '{}') {
+			// 	const paramsH = Request.addQueryString(_config.params);
+			// 	mergeUrl += mergeUrl.indexOf('?') === -1 ? `?${paramsH}` : `&${paramsH}`
+			// }
+			_config.url = mergeUrl
+			uni.request(_config)
+		})
+	}
+
+	get(url, options = {}) {
+		return this.request({
+			url,
+			method: 'GET',
+			...options
+		})
+	}
+
+	post(url, data, options = {}) {
+		return this.request({
+			url,
+			data,
+			method: 'POST',
+			...options
+		})
+	}
+
+	// upload(url, {
+	// 	// #ifdef APP-PLUS
+	// 	files,
+	// 	// #endif
+	// 	// #ifdef MP-ALIPAY
+	// 	fileType,
+	// 	// #endif
+	// 	filePath,
+	// 	name,
+	// 	header,
+	// 	formData,
+	// 	custom
+	// }) {
+	// 	return new Promise((resolve, reject) => {
+	// 		let next = true
+	// 		let handleRe = {}
+	// 		const globalHeader = { ...this.config.header
+	// 		}
+	// 		delete globalHeader['content-type']
+	// 		const pubConfig = {
+	// 			baseUrl: this.config.baseUrl,
+	// 			url,
+	// 			// #ifdef APP-PLUS
+	// 			files,
+	// 			// #endif
+	// 			// #ifdef MP-ALIPAY
+	// 			fileType,
+	// 			// #endif
+	// 			filePath,
+	// 			method: 'UPLOAD',
+	// 			name,
+	// 			header: header || globalHeader,
+	// 			formData,
+	// 			custom: { ...this.config.custom,
+	// 				...(custom || {})
+	// 			},
+	// 			complete: (response) => {
+	// 				response.config = handleRe
+	// 				if (response.statusCode === 200) { // 成功
+	// 					response = this.requestComFun(response)
+	// 					resolve(response)
+	// 				} else {
+	// 					response = this.requestComFail(response)
+	// 					reject(response)
+	// 				}
+	// 			}
+	// 		}
+	// 		const cancel = (t = 'handle cancel', config = pubConfig) => {
+	// 			const err = {
+	// 				errMsg: t,
+	// 				config: config
+	// 			}
+	// 			reject(err)
+	// 			next = false
+	// 		}
+
+	// 		handleRe = { ...this.requestBeforeFun(pubConfig, cancel)
+	// 		}
+	// 		const _config = { ...handleRe
+	// 		}
+	// 		if (!next) return
+	// 		delete _config.custom
+	// 		_config.url = Request.posUrl(_config.url) ? _config.url : (_config.baseUrl + _config.url)
+	// 		uni.uploadFile(_config)
+	// 	})
+	// }
+}
+
+/**
+ * setConfig回调
+ * @return {Object} - 返回操作后的config
+ * @callback Request~setConfigCallback
+ * @param {Object} config - 全局默认config
+ */
+/**
+ * 请求拦截器回调
+ * @return {Object} - 返回操作后的config
+ * @callback Request~requestCallback
+ * @param {Object} config - 全局config
+ * @param {Function} [cancel] - 取消请求钩子,调用会取消本次请求
+ */
+/**
+ * 响应拦截器回调
+ * @return {Object} - 返回操作后的response
+ * @callback Request~responseCallback
+ * @param {Object} response - 请求结果 response
+ */
+/**
+ * 响应错误拦截器回调
+ * @return {Object} - 返回操作后的response
+ * @callback Request~responseErrCallback
+ * @param {Object} response - 请求结果 response
+ */

+ 344 - 0
common/tools.js

@@ -0,0 +1,344 @@
+import config from './config.js'
+function msg(title = '', param={}){
+	if(!title){
+		return;
+	}
+	const duration = param.duration || 1500;
+	const mask = param.mask || false;
+	const icon = param.icon || 'none';
+	
+	let options = {
+		title,
+		duration,
+		mask,
+		icon
+	}
+	
+	if(param.type){
+	//	options.image = '/static/' + param.type + '.png';
+	}
+	uni.showToast(options);
+}
+
+function date(format, timeStamp){
+	
+	let date = new Date(timeStamp * 1000),
+		Y = date.getFullYear(),
+		m = date.getMonth() + 1,
+		d = date.getDate(),
+		H = date.getHours(),
+		i = date.getMinutes(),
+		s = date.getSeconds();
+	
+	m = m < 10 ? '0' + m : m;
+	d = d < 10 ? '0' + d : d;
+	H = H < 10 ? '0' + H : H;
+	i = i < 10 ? '0' + i : i;
+	s = s < 10 ? '0' + s : s;
+
+	return format.replace(/[YmdHis]/g, key=>{
+		return {Y,m,d,H,i,s}[key];
+	});
+}
+
+/**
+ * 节流原理:在一定时间内,只能触发一次
+ * 
+ * @param {Function} func 要执行的回调函数 
+ * @param {Number} wait 延时的时间
+ * @param {Boolean} immediate 是否立即执行
+ * @return null
+ * this.throttle(this.getResult, this.timeout, this.immediate);
+ * $tools.throttle(submit)
+ */
+let timer, flag;
+function throttle(func, wait = 2000, immediate = true) {
+	if (immediate) {
+		if (!flag) {
+			flag = true;
+			// 如果是立即执行,则在wait毫秒内开始时执行
+			typeof func === 'function' && func();
+			timer = setTimeout(() => {
+				flag = false;
+			}, wait);
+		}
+	} else {
+		if (!flag) {
+			flag = true
+			// 如果是非立即执行,则在wait毫秒内的结束处执行
+			timer = setTimeout(() => {
+				flag = false
+				typeof func === 'function' && func();
+			}, wait);
+		}
+		
+	}
+}
+/**
+ * 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数
+ * 
+ * @param {Function} func 要执行的回调函数 
+ * @param {Number} wait 延时的时间
+ * @param {Boolean} immediate 是否立即执行 
+ * @return null
+ * this.debounce(this.getResult, this.timeout, this.immediate);
+ */
+let timeout = null;
+function debounce(func, wait = 500, immediate = false) {
+		// 清除定时器
+		if (timeout !== null) clearTimeout(timeout);
+		// 立即执行,此类情况一般用不到
+		if (immediate) {
+			var callNow = !timeout;
+			timeout = setTimeout(function() {
+				timeout = null;
+			}, wait);
+			if (callNow) typeof func === 'function' && func();
+		} else {
+			// 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
+			timeout = setTimeout(function() {
+				typeof func === 'function' && func();
+			}, wait);
+		}
+	}
+
+function inAction(fn, that, timeout = 2000){
+	if(that['in' + fn]){
+		return true
+	}
+	that['in' + fn] = true;
+	setTimeout(()=>{
+		that['in' + fn] = false;
+	}, timeout)
+	return false;
+}
+
+
+// 返回上一页
+const prePage = (num=1) => {
+	let pages = getCurrentPages();
+	let prePage = pages[pages.length -1- num];
+	// console.log('prePage--',prePage);
+	// #ifdef H5
+	return prePage;
+	// #endif
+	return prePage.$vm;
+}
+
+
+// 上传图片--注按需要对res处理 JSON.parse()
+function uploadImg(formData){
+	return new Promise((resolve,reject)=>{
+		uni.chooseImage({
+			count:1,
+			success(chooseImageRes){
+				// resolve(chooseImageRes.tempFilePaths[0]);
+				const tempFilePaths = chooseImageRes.tempFilePaths[0];
+				// const token = sessionStorage.getItem('token');
+				const token = uni.getStorageSync('token');
+				uni.showLoading({
+					title: '请稍后..',
+					mask: true,
+				})
+				uni.uploadFile({
+					url:config.uploadImgUrl,
+					filePath: tempFilePaths,
+					name: 'file',
+					formData:formData,
+					header: {
+						'token':token,
+					},
+					success:(res)=>{
+						//需要JSON.parse
+						let data = JSON.parse(res.data);
+						if(!data || data.code !== 1 ){
+							msg('上传失败');
+							reject();
+							return;
+						}
+						// console.log('tools uploadImg---',data);
+						//即为全路径地址
+						let fullurl = data.data.fullurl;
+						resolve(fullurl);
+					},
+					fail:(err)=>{
+						console.log('err--',err);
+						msg('上传失败');
+						reject();
+					},
+					complete:()=> {
+						uni.hideLoading();
+					},
+				})
+			},
+			fail(err){
+				// msg('打开相册失败');
+				reject(err)
+			}
+		})
+	})
+}
+function uploadVideo(formData={}){
+	return new Promise((resolve,reject)=>{
+		uni.chooseVideo({
+			sourceType: ['camera', 'album'],
+			success(chooseVideoRes){
+				// resolve(chooseVideoRes.tempFilePaths[0]);
+				const tempFilePath = chooseVideoRes.tempFilePath;
+				// const token = sessionStorage.getItem('token');
+				const token = uni.getStorageSync('token');
+				uni.showLoading({
+					title: '请稍后..',
+					mask: true,
+				})
+				formData.act='video';
+				uni.uploadFile({
+					url:config.uploadVideoUrl,
+					filePath: tempFilePath,
+					name: 'file',
+					formData:formData,
+					header: {
+						'token':token,
+					},
+					success:(res)=>{
+						//需要JSON.parse
+						let data = JSON.parse(res.data);
+						if(!data || data.code !== 1 ){
+							msg('上传失败');
+							reject();
+							return;
+						}
+						// //即为全路径地址
+						resolve(data.data.fullurl);
+						// console.log('res---',res);
+					},
+					fail:(err)=>{
+						console.log('err--',err);
+						msg('上传失败');
+						reject();
+					},
+					complete:()=> {
+						uni.hideLoading();
+					},
+				})
+			},
+			fail(err){
+				// msg('打开相册失败');
+				reject(err)
+			}
+		})
+	})
+}
+// 查看单张图片
+function previewImg(urls,idx=0){
+	uni.previewImage({
+		current: urls[idx],
+		urls: urls,
+		indicator: "number"
+	})
+}
+
+
+//单个授权,针对定位await api.checkDeviceAuthorize('scope.userLocation','请开启定位,方便司机到达您的位置');
+let hasOpenDeviceAuthorizeModal = false;
+function checkDeviceAuthorize(scope,tipText) {
+	if (hasOpenDeviceAuthorizeModal) {
+		return
+	}
+	hasOpenDeviceAuthorizeModal = true
+	
+	return new Promise((resolve, reject) => {
+		uni.getSetting().then((result) => {
+			// this.authorizeCamera = result.authSetting['scope.camera']
+			// console.log('getSetting 查看所有授权',result);
+			let authSetting = result[1]['authSetting'];
+			if (authSetting[scope]) {
+				// console.log('getSetting 已授权',result);
+				// 授权成功
+				resolve({hasScoped:true})
+				hasOpenDeviceAuthorizeModal = false
+			} else {
+				// 没有授权,弹出授权窗口
+				// 注意: wx.authorize 只有首次调用会弹框,之后调用只返回结果,如果没有授权需要自行弹框提示处理
+				// console.log('getSetting 查询没有授权,uni.authorize发起授权')
+				uni.authorize({
+					scope: scope,
+					success: (res) => {
+						// console.log('authorize ok', res)
+					    resolve({initAuth:true})
+						hasOpenDeviceAuthorizeModal = false
+					},
+					fail: (err) => {
+						// console.log('authorize err 弹层提示', err)
+						openConfirm(scope,tipText,(isTrue)=>{
+							if(isTrue){
+								// resolve({popAuth:true})
+								reject(new Error('popAuth:true but onShow ok')); //onShow会得新调用
+								hasOpenDeviceAuthorizeModal = false
+							}else{
+								reject(new Error('authorize fail'))
+								hasOpenDeviceAuthorizeModal = false
+							}
+							
+						})
+					}
+				})
+			}
+		})
+	})
+};
+function openConfirm(scope,tipText,callback) {
+	return uni.showModal({
+		content: tipText,
+		confirmText: '确认',
+		cancelText: '取消',
+		success: (res) => {
+			// 点击“确认”时打开设置页面
+			if (res.confirm) {
+				// console.log('用户点击确认')
+				uni.openSetting({
+					success: (res) => {
+						// console.log("wx.openSetting success res..",res)
+						if(typeof callback == 'function' && res.authSetting[scope]==true){
+							callback(true);
+						}
+					},
+					fail:()=>{
+						callback(false)
+					}
+				})
+			} else {
+				// console.log('用户点击取消')
+				callback(false)
+			}
+		},
+	})
+};
+
+///**详情页处理富文本 details仅是变化替换部分a.replace(/<text>(abc)<\/text>/,function(match,$1) $1=abc---------------------------------------------------------------------- */
+const replaceDetail = (details = '') => {
+	//newContent仅是details替换后内容;
+  let newContent = details.replace(/<img[^>]*>/gi, function (match, capture) { //去除三标签
+    match = match.replace(/style="[^"]+"/gi, '').replace(/style='[^']+'/gi, '');
+    match = match.replace(/width="[^"]+"/gi, '').replace(/width='[^']+'/gi, '');
+    match = match.replace(/height="[^"]+"/gi, '').replace(/height='[^']+'/gi, '');
+    return match;
+  });
+  newContent = newContent.replace(/<br[^>]*\/>/gi, '');
+  newContent = newContent.replace(/<img/gi, '<img style="max-width:100%;height:auto;display:block;margin:0 auto;"');
+  return newContent;
+}
+
+export default {
+	msg,
+	date,
+	throttle,
+	debounce,
+	inAction,
+	prePage,
+	uploadImg,
+	uploadVideo,
+	previewImg,
+	checkDeviceAuthorize,
+	replaceDetail
+}

+ 42 - 0
components/empty.vue

@@ -0,0 +1,42 @@
+<template>
+        <view class="empty-tips flex-col align-center justify-center" :style="{marginTop:marginTop}">
+			<image src="/static/empty.png" mode="aspectFill" class="ic"></image>
+			<text class="txt">{{txt}}</text>
+		</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			txt:{
+				type: String,
+				default: '暂无记录'
+			},
+			marginTop:{
+				type: String,
+				default: '200rpx'
+			},
+		},
+		data() {
+			return {
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	
+	.empty-tips{
+		.ic{
+			width: 240rpx;
+			height: 240rpx;
+			margin-right: 20rpx;
+		}
+		.txt{
+			margin-top: 20rpx;
+			color:#999;
+		    font-size: 28rpx;	
+		}
+	
+	}
+</style>

+ 198 - 0
components/mix-popup/mix-popup.vue

@@ -0,0 +1,198 @@
+<template>
+	<!-- :style="{zIndex:zIndex}" -->
+	<view 
+		class="uni-popup" 
+		:class="[
+			{'uni-popup--show': state > 0},
+			{'uni-popup__center': type === 'center'}
+		]" 
+		@touchmove.stop.prevent="stopPrevent"
+	>
+		<view class="mask" :class="{'mask--show': state === 1}" @click="hide('mask')" v-show="isShowMask"/>
+		<view v-if="type==='right'" class="wrapper wrapper__right" :class="{'wrapper--show': state === 1}">
+			<view class="wrapper__box" @click.stop="stopPrevent">
+				<slot />
+			</view>
+		</view>
+		<!-- 底部弹窗 -->
+		<view v-if="type==='bottom'" class="wrapper wrapper__bottom" :class="{'wrapper--show': state === 1}">
+			<view class="wrapper__box" @click.stop="stopPrevent">
+				<slot />
+			</view>
+		</view>
+		<!-- 中间弹窗 -->
+		<view v-if="type==='center'" class="wrapper wrapper__center" :class="{'wrapper__center--show': state === 1}">
+			<view class="wrapper__center__box" @click.stop="stopPrevent">
+				<slot />
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'MixPopup',
+		props: {
+			// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+			type: {
+				type: String,
+				default: 'center'
+			},
+			isShowMask:{
+				type:Boolean,
+				default:true
+			},
+			//是否阻止点击mask隐藏
+			isStopMaskHide:{
+				type:Boolean,
+				value:false,
+			},
+			zIndex:{
+				type:Number,
+				value:1001,
+			},
+			
+		},
+		data() {
+			return {
+				state: 0,
+			}
+		},
+		created() {
+		  //	this.show()
+		},
+		methods: {
+			show() {
+				this.state = 2;
+				this.$nextTick(() => {
+					this.state = 1;
+				})
+			},
+			hide(type) {
+				
+				if(type == 'mask' && this.isStopMaskHide==true ){
+					return;
+				}
+				uni.hideKeyboard();
+				this.state = 2;
+				setTimeout(() => {
+					this.state = 0;
+				}, 350)
+				this.$emit('onHide');
+			},
+			toggle(){
+				if(this.state === 0){
+					this.show()
+				}else{
+					this.hide();
+				}
+			},
+			
+			stopPrevent() {},
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	// #ifndef APP-PLUS-NVUE */
+	// view{
+	// 	display: flex;
+	// 	flex-direction: column;
+	// }
+	// /* #endif
+	.uni-popup {
+		position: fixed;
+		/* #ifdef H5 */
+		top: var(--window-top);
+		/* #endif */
+		/* #ifndef H5 */
+		top: 0;
+		/* #endif */
+		bottom: 0;
+		left: 0;
+		right: 0;
+		// z-index: 101;
+		z-index: 1000;
+		overflow: hidden;
+		transform: translateX(750rpx);
+		
+		display: flex;
+		flex-direction: column;
+		
+		&--show{
+			transform: translateX(0);
+		}
+		&__center{
+			justify-content: center;
+			align-items: center;
+		}
+	}
+
+	.mask {
+		position: fixed;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		z-index: 101;
+		opacity: 0;
+		transition: opacity .38s;
+		background-color: rgba(0,0,0,.3);
+		
+		&--show{
+			opacity: 1;
+		}
+	}
+
+	.wrapper {
+		position: fixed;
+		z-index: 1002;
+		transition: transform .3s, opacity .3s;
+		
+		&__right{
+			flex-direction: column;
+			top: 0;
+			bottom: 0;
+			right: 0;
+			transform: translateX(610rpx);
+		}
+		&__bottom{
+			flex-direction: column;
+			left: 0;
+			bottom: 0;
+			right: 0;
+			transform: translateY(100%);
+		}	
+		&__center{
+			transform: scale(1.16);
+			opacity: 0;
+		}
+		&--show{
+			transform: translate(0rpx, 0rpx);
+		}
+		&__center--show{
+			
+			transform: scale(1);
+			opacity: 1;
+		}
+		&__box{
+			flex: 1;
+			position: relative;
+		}
+	}
+
+	/* .center {
+		#ifndef APP-NVUE 
+		display: flex;
+		flex-direction: column;
+		 #endif
+		bottom: 0;
+		left: 0;
+		right: 0;
+		top: 0;
+		justify-content: center;
+		align-items: center;
+		transform: scale(1.1);
+		opacity: 0;
+	}
+ */
+</style>

+ 63 - 0
components/navbar-line.vue

@@ -0,0 +1,63 @@
+<template>
+	<view>
+		<view class="cu-custom" :style="[{height:CustomBar + 'px'},{position: !hasPlacer ? 'absolute':'relative'}]">
+			<view class="cu-bar" :style="{background:bgColor,height: CustomBar + 'px',paddingTop: StatusBar + 'px'}">
+				<view class="bar-wrap">
+					<slot />
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				StatusBar: this.StatusBar,
+				CustomBar: this.CustomBar
+			};
+		},
+		name: 'cu-custom',
+		props: {
+			bgColor: {
+				type: String,
+				default: '#ffffff'
+			},
+			//默认设置relative
+			hasPlacer:{
+				type: Boolean,
+				default: true
+			}
+		},
+		computed:{
+			
+		},
+		methods: {
+		}
+	}
+</script>
+
+<style scoped>
+  .cu-custom {
+      display: block;
+      position: relative;
+  }
+  .cu-bar {
+      display: flex;
+	  position: fixed;
+      width: 100%;
+      top: 0;
+	  left: 0;
+	  right: 0;
+      z-index: 1024;  
+	  transition: background 1s linear;
+	  /* box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.1); */
+	 
+  }
+   .bar-wrap{
+	  position: relative;
+	  width: 100%;
+  }
+
+</style>

+ 96 - 0
components/single-list.vue

@@ -0,0 +1,96 @@
+<template>
+	<view class="single-list">
+	   <slot name="items" v-bind:list="list"></slot>	
+	</view>
+</template>
+
+<script>
+	const TAG_NAME = "SINGLE_LIST";
+	export default {
+		props: {
+			//请求数据
+			sendData:{
+				type:Object,
+				default:()=>({})
+			},
+			//请求地址
+			url:{
+				type:String,
+				default:''
+			}
+		},
+		data() {
+			return {
+				loadingType:'more',
+				pageNum:1,
+				pageSize:20,
+				list:[],
+			}
+		},
+		watch:{
+			/*onLoad内必须在this.$nextTick更改*/
+			sendData:{
+				handler(v,o){
+					// console.log(TAG_NAME,'sendData',v,o);
+					this.getList(true);
+				},
+				immediate:false,	//由父组件触发,因为可能onShow刷新
+				deep:true,
+			}
+		},
+		methods:{
+			//搜索
+			async getList(isRefresh=false){
+				// console.log('触发函数getList')
+				if(isRefresh){
+					this.loadingType='more'
+					this.pageNum=1
+					this.pageSize=20
+					this.list=[]
+				}
+				if (this.loadingType === 'loading' || this.loadingType == 'noMore') {
+					//防止重复加载
+					return;
+				}
+				// console.log('触发开始',this.pageNum)
+				this.loadingType = 'loading';
+				const sendData={
+					...this.sendData,
+					page: this.pageNum,
+					pagenum: this.pageSize,
+				}
+				let res = await this.$request(this.url,sendData);
+				uni.stopPullDownRefresh();
+				// this.isTriggerRefresh=false;
+				// this.isLoaded = true;
+				let result = res.data || [];
+				
+				if ( this.pageSize > result.length) {
+					this.loadingType = 'noMore';
+				} else {
+					this.loadingType = 'more';
+				}
+				// 页数加一
+				this.pageNum++;
+				if(isRefresh){
+					this.pageNum = 2;
+					this.list=result;
+					this.$emit('reqEnd',{loadingType:this.loadingType,pageOneRes:result}) //主要判断list是否空
+					return;
+				}
+				result.forEach(item => {
+					// item = Object.assign(item, this.handleStatus(item.status));
+					this.list.push(item);
+				});
+				this.$emit('reqEnd',{loadingType:this.loadingType,pageOneRes:this.list[0]})
+			},
+		}
+
+	}
+</script>
+
+<style lang="scss" scoped>
+	// .submit-btn{
+	// 	opacity: 0.6;
+	// }
+</style>

+ 54 - 0
components/submit-btn.vue

@@ -0,0 +1,54 @@
+<template>
+	<view class="submit-btn"  @tap="$tools.throttle(submit)">
+	   <slot name="btns" v-bind:isSubmitted="isSubmitted"></slot>	
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			isOnce:{
+				type:Boolean,
+				default:true
+			}
+		},
+
+		data() {
+			return {
+				isSubmitted:false,
+			}
+		},
+		beforeDestroy(){
+			if(this.submitTimer){
+				// console.log("beforeDestroy 销毁 this.timer--")
+				clearTimeout(this.submitTimer);
+				this.submitTimer = null;
+			}
+		},
+		methods:{
+			submit(){
+				if(this.isSubmitted){
+					return;
+				}
+				this.$emit("submit")
+			},
+			chgeStatus(){
+				this.isSubmitted = true;
+				if(!this.isOnce){
+					this.submitTimer = setTimeout(()=>{
+						// console.log("定时到---")
+						this.isSubmitted = false;
+					},3000)		
+				}
+				
+			},
+		}
+
+	}
+</script>
+
+<style lang="scss" scoped>
+	// .submit-btn{
+	// 	opacity: 0.6;
+	// }
+</style>

+ 20 - 0
index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>

+ 32 - 0
main.js

@@ -0,0 +1,32 @@
+import App from './App'
+import mixin from './common/mixin'
+import store from './store'
+import tools from './common/tools'
+import request from './common/request'
+
+
+import Vue from 'vue'
+
+import popup from './components/mix-popup/mix-popup.vue';
+import empty from './components/empty.vue';
+import submitBtn from './components/submit-btn.vue';
+import singleList from './components/single-list.vue';
+
+
+Vue.component('popup',popup)
+Vue.component('empty',empty)
+Vue.component('submitBtn',submitBtn)
+Vue.component('singleList',singleList)
+
+Vue.config.productionTip = false
+App.mpType = 'app'
+
+Vue.prototype.$store = store;
+Vue.prototype.$tools = tools;
+Vue.prototype.$request = request;
+
+Vue.mixin(mixin);
+const app = new Vue({
+    ...App
+})
+app.$mount()

+ 74 - 0
manifest.json

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

+ 125 - 0
pages.json

@@ -0,0 +1,125 @@
+{
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path" : "pages/home/home",
+			"style" :                                                                                    
+			{
+				"navigationBarTitleText": "",
+				"enablePullDownRefresh": false,
+				"navigationStyle": "custom"
+			}
+			
+		},
+	    {
+            "path" : "pages/login/login",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "navigationStyle": "custom"
+            }
+        },
+		{
+		    "path" : "pages/login/register",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "注册"
+		    }
+		},
+		{
+		    "path" : "pages/login/forget",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": ""
+		    }
+		},
+		{
+		    "path" : "pages/login/mobileLogin",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "手机号快捷登录"
+		    }
+		},
+		{
+		    "path" : "pages/mine/mine",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": ""
+		    }
+		},
+        {
+            "path" : "pages/mang/mang",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false,
+				"navigationStyle": "custom"
+            }
+            
+        },
+        {
+            "path" : "pages/address/address",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "地址管理",
+                "enablePullDownRefresh": false
+            }
+            
+        },
+		{
+		    "path" : "pages/address/addressManage",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "地址管理",
+		        "enablePullDownRefresh": false
+		    }
+		    
+		}
+        ,{
+            "path" : "pages/mang/detail",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "盲盒详情",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+    ],
+	"tabBar": {
+		"color": "#999999",
+		"selectedColor": "#92B99C",
+		"backgroundColor":"#ffffff",
+	    "list": [
+			{
+			  "pagePath": "pages/home/home",
+			  "iconPath": "static/tab/home.png",
+			  "selectedIconPath": "static/tab/home-full.png",
+			  "text": "首页"
+			},
+			{
+			  "pagePath": "pages/mang/mang",
+			  "iconPath": "static/tab/mang.png",
+			  "selectedIconPath": "static/tab/mang-full.png",
+			  "text": "盲盒"
+			},
+			// {
+			//   "pagePath": "pages/order/order",
+			//   "iconPath": "static/tab/order.png",
+			//   "selectedIconPath": "static/tab/order-full.png",
+			//   "text": "订单"
+			// },
+			{
+			  "pagePath": "pages/mine/mine",
+			  "iconPath": "static/tab/mine.png",
+			  "selectedIconPath": "static/tab/mine-full.png",
+			  "text": "我的"
+			}
+		]
+	},
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "盲go",
+		"navigationBarBackgroundColor": "#FFFFFF",
+		"backgroundColor": "#FFFFFF"
+	},
+	"uniIdRouter": {}
+}

+ 231 - 0
pages/address/address.vue

@@ -0,0 +1,231 @@
+<template>
+	<view class="page b-t">
+		<!-- 
+		<singleList ref="listRef" url="cang.mycollectionsecond" :sendData="reqData" @reqEnd="searchEnd">
+			<template v-slot:items="{list}">
+		 -->		
+		 <!--  -->
+				<view class="list b-b" v-for="(item, index) in addressList" :key="index" @click="checkAddress(item)">
+					<image :src="item.checked? '/static/check-do.png' : '/static/check-un.png' " mode="aspectFill" class="check"></image>
+					<view class="content flex-col">
+						<view class="u-box align-center">
+							<text class="name">{{item.name}}</text>
+							<text class="mobile">{{item.mobile}}</text>
+							<text v-if="item.is_default"  class="tag">默认</text>
+							<view class="ic-wrap del align-center justify-center">
+								<image src="/static/addr-del.png" mode="aspectFill" class="ic" @click.stop="deleteAddress(item.id,index)"></image>
+							</view>
+							<view class="ic-wrap edit align-center justify-center">
+								<image src="/static/addr-edit.png" mode="aspectFill" class="ic" @click.stop="addAddress('edit', item.id)"></image>
+							</view>
+						</view>
+						<view class="address-box">
+							<text class="address">{{item.addr+' '+item.desc}}</text>
+						</view>	
+					</view>
+				</view>
+		<!-- 		
+			    <empty v-else-if="sRes && sRes==='empty'"/>
+			</template>
+		</singleList>
+		-->
+		<button class="add-btn" @click="addAddress('add')">新增地址</button>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				source: 0,
+				addressList: [
+					{
+						id:1,
+						is_default:true,
+						addr:'山东省临沂市河东区',
+						desc:'山东省临沂市河东区山东省临沂市河东区山东省临沂市河东区山东省临沂市河东区山东省临沂市河东区',
+						name:'mork',
+						mobile:'15263970923',
+						checked:true,
+					},
+					{
+						id:1,
+						is_default:false,
+						addr:'山东省临沂市河东区',
+						desc:'山东省临沂市河东区山东省临沂市河东区山东省临沂市河东区山东省临沂市河东区山东省临沂市河东区',
+						name:'mork',
+						mobile:'15263970923',
+						checked:false,
+					},
+				],
+				reqData:{}, //请求数据
+				sRes:'', //搜索结果 empty,noMore
+			}
+		},
+		onLoad(option) {
+			// console.log(option.source);
+			this.source = option.source;
+			// this.$nextTick(()=>{
+			// 	this.reqData={
+			// 		type: this.cats[this.curCatIdx].state,
+			// 	}	
+			// })	
+		},
+		onShow() {
+			// this.refreshList();
+		},
+		methods: {
+			//获取我的收货地址
+			async getList() {
+				let res = await this.$api.request('/addons/unishop/address/all', 'POST', {
+					page: 1,
+					pagesize: 50
+				});
+				let list = res.data;
+				if (list) {
+					this.addressList = list;
+				}
+			},
+			//选择地址
+			checkAddress(item) {
+				if (this.source == 1) {
+					//this.$api.prePage()获取上一页实例,在App.vue定义
+					this.$api.prePage().addressData = item;
+					uni.navigateBack()
+				}
+			},
+			addAddress(type, id = 0) {
+				uni.navigateTo({
+					url: `/pages/address/addressManage?type=${type}&id=${id}`
+				})
+			},
+			//添加或修改成功之后回调
+			refreshList(data, type) {
+				//添加或修改后事件,这里直接在最前面添加了一条数据,实际应用中直接刷新地址列表即可
+				// this.addressList.unshift(data);
+				this.addressList=[];
+				this.getList();
+			},
+			async deleteAddress(id, index) {
+				let [error, res] = await uni.showModal({
+					title: '确定删除地址?',
+					content: this.addressList[index].address
+				})
+
+				if (res.confirm) {
+					let res = await this.$api.request('/addons/unishop/address/delete?id=' + id);
+					let data = res.data;
+					if (data) {
+						if (this.$api.prePage().addressData && this.$api.prePage().addressData.id) {
+							if (this.$api.prePage().addressData.id == this.addressList[index].id) {
+								this.$api.prePage().addressData = {};
+							}
+						}
+						this.addressList.splice(index, 1);
+					}
+				}
+
+			}
+		}
+	}
+</script>
+
+<style lang='scss'>
+	page {
+		background: #f8f8f8;
+	}
+	.page{
+		padding: 30rpx 20rpx 0;
+		min-height: 100vh;
+		padding-bottom: calc(var(--safe-area-inset-bottom) / 2);
+		position: relative;
+	}
+    .list {
+		padding: 20upx 20upx 50rpx 73rpx;
+		background: #fff;
+		position: relative;
+		border-radius: 10rpx;
+		margin-bottom: 24rpx;
+		.check{
+			position: absolute;
+			left: 17rpx;
+			top: 60rpx;
+			width: 34rpx;
+			height: 34rpx;
+			z-index: 2;
+		}
+	}
+	.u-box {
+		color: #999;
+		height: 48rpx;
+		.name{
+			font-size: 30rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+			color: #333333;
+			margin-right: 20rpx;
+		}
+		.mobile{
+			font-size: 30rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+			color: #333333;
+			margin-right: 12rpx;
+		}
+		.tag{
+			width: 61rpx;
+			height: 34rpx;
+			background: #92B99C;
+			border-radius: 4rpx;
+			line-height: 34rpx;
+			text-align: center;
+			font-size: 22rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+			color: #FFFFFF;
+		}
+		.ic-wrap{
+			height: 48rpx;
+			width: 64rpx;
+			&.del{
+				margin-left: auto;
+				.ic{
+					width: 28rpx;
+					height: 28rpx;
+				}
+			}
+			&.edit{
+				margin-left: 8rpx;
+			
+			}
+			.ic{
+				width: 24rpx;
+				height: 24rpx;
+			}
+		}
+	}
+	.address-box {
+		margin-top: 13rpx;
+		display: flex;
+		align-items: flex-start;
+		.address {
+			font-size: 24upx;
+			color: #999;
+			max-width: 600rpx;
+			line-height: 40rpx;
+		}
+	}
+
+	.add-btn {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		width: 710upx;
+		height: 80upx;
+		margin: 240rpx auto 10rpx;
+		font-size: 30upx;
+		color: #fff;
+		background-color: $base-color;
+		border-radius: 38upx;
+	}
+</style>

+ 199 - 0
pages/address/addressManage.vue

@@ -0,0 +1,199 @@
+<template>
+	<view class="page">
+		<view class="row b-b">
+			<text class="tit">收货人</text>
+			<input class="input" type="text" v-model="name" placeholder="收货人姓名" placeholder-class="placeholder" />
+		</view>
+		<view class="row b-b">
+			<text class="tit">联系电话</text>
+			<input class="input" type="number" v-model="mobile" placeholder="联系电话" placeholder-class="placeholder" />
+		</view>
+		<view class="row b-b" v-on:click="chooseLoc()">
+			<text class="tit">收货地址</text>
+			<view class="input clamp2" :class="{placeholder: !addr}">
+				{{addr ? addr :'定位地址'}}
+			</view>
+			<image src="/static/r-arrow.png" mode="aspectFill" class="ic"></image>
+		</view>
+		<view class="row b-b">
+			<text class="tit">详细地址</text>
+			<input class="input clamp2" type="text" v-model="desc" placeholder="详细地址,楼号" placeholder-class="placeholder" />
+		</view>
+
+		<view class="row default-row">
+			<div class="l flex-col justify-center">
+				<text class="tit">设为默认</text>
+				<text class="tips">每次下单会默认推荐使用该地址</text>
+			</div>
+			
+			<switch :checked="is_default" color="#92B99C" @change="switchChange" />
+		</view>
+		<button class="add-btn" @click="confirm">提交</button>
+		
+	
+	</view>
+</template>
+
+<script>
+	import {chooseLoc} from '@/common/loc.js';
+	export default {
+		data() {
+			return {
+				name: '',
+				mobile: '',
+				addr:'',
+				desc:'',
+				is_default:'',
+			}
+		},
+		onLoad(option) {
+			let title = '新增收货地址';
+			if (option.type === 'edit') {
+				// this.getInfo(option.id);
+				title = '编辑收货地址'
+			} else {
+				// this.$refs.mpvueCityPicker.creat(this.pickerValueDefault);
+			}
+			this.manageType = option.type;
+			uni.setNavigationBarTitle({ 
+				title
+			})
+		},
+		methods: {
+			// 获取地址详情
+			async getInfo(id){
+				let  res= await this.$api.request(`/addons/unishop/address/info?id=${id}`);
+				let addressData = res.data;
+				if (addressData) {
+					console.log(addressData);
+					
+					this.addressData = addressData;
+					let pickerValueDefault = [];
+					pickerValueDefault.push(addressData.province_id);
+					pickerValueDefault.push(addressData.city_id);
+					pickerValueDefault.push(addressData.area_id);
+					this.pickerValueDefault = pickerValueDefault;
+					
+					this.$refs.mpvueCityPicker.creat(pickerValueDefault);
+				}
+			},
+			// 城市选择器
+			async chooseLoc() {
+				let  {
+				   province,
+				   city, 
+				   area,
+				   address,
+				   latitude,
+				   longitude,
+				} =await chooseLoc();
+				this.addr = `${province}${city} ${area}`;
+				this.desc = address;
+			},
+			//默认地址
+			switchChange(e) {
+				this.is_default = e.detail.value;
+			},
+			//提交
+			async confirm() {
+				//Deep Clone
+				if(this.$api.inAction('confirm',this)){
+					return;
+				}
+				let data = JSON.parse(JSON.stringify(this.addressData));
+				if (!data.name) {
+					this.$api.msg('请填写收货人姓名');
+					return;
+				}
+				if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(data.mobile)) {
+					this.$api.msg('请输入正确的手机号码');
+					return;
+				}
+				if (!data.address) {
+					this.$api.msg('请填详细地址信息');
+					return;
+				}
+				console.log(data.is_default);
+				data.is_default = data.is_default == true ? 1 : 0;
+				let action = this.manageType == 'edit' ? 'edit' : 'add';
+				let result = await this.$api.request('/addons/unishop/address/' + action, 'POST', data);
+				if (result) {
+					this.$api.prePage().refreshList(data, this.manageType);
+					setTimeout(() => {
+						uni.navigateBack()
+					}, 800)
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	page {
+		background: #fff;
+	}
+	.page{
+		padding: 16rpx 30rpx 0;
+		min-height: 100vh;
+		padding-bottom: calc(var(--safe-area-inset-bottom) / 2);
+	}
+	.row {
+		display: flex;
+		align-items: center;
+		position: relative;
+		height: 110upx;
+		background: #fff;
+		border-bottom: 2rpx solid #eee;
+		.tit {
+			flex-shrink: 0;
+			margin-right: 30rpx;
+			font-size: 28upx;
+			color: #333;
+		}
+		.tips{
+			margin-top: 20rpx;
+			font-size: 20rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+			color: #999999;
+		}
+		.input {
+			flex: 1;
+			font-size: 28upx;
+			color: #333;
+			line-height: 36rpx;
+		}
+        .ic{
+			flex-shrink: 0;
+			width: 24rpx;
+			height: 24rpx;
+			margin-left: auto;
+		}
+	}
+
+	.default-row {
+		margin-top: 16upx;
+
+		.tit {
+			flex: 1;
+		}
+
+		switch {
+			margin-left: auto;
+			transform: translateX(16upx) scale(.9);
+		}
+	}
+
+	.add-btn {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		width: 690upx;
+		height: 80upx;
+		margin: 120upx auto 10rpx;
+		font-size: 30rpx;
+		color: #fff;
+		background-color: $base-color;
+		border-radius: 38upx;
+	}
+</style>

+ 52 - 0
pages/home/home.vue

@@ -0,0 +1,52 @@
+<template>
+	<view class="page">
+		<navbar-line>
+			<image src="" mode="" class="logo"></image>
+		</navbar-line>
+		<view class="content">
+			
+		</view>
+	</view>
+</template>
+
+<script>
+	import navbarLine from '@/components/navbar-line.vue';
+	export default {
+		components:{
+			navbarLine
+		},
+		data() {
+			return {
+				
+			}
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style lang="scss">
+page{
+	background-color: #fff;
+}
+
+	.logo{
+		width: 141rpx;
+		height: 36rpx;
+		font-size: 34rpx;
+		font-family: PingFang SC;
+		font-weight: bold;
+		background-color: #333333;
+		line-height: 37rpx;
+		margin-left: 30rpx;
+		position: absolute;
+		top: 50%;
+		transform: translateY(-50%);
+	}
+	.content{
+		height: 1000rpx;
+		background-color: rgba(0,0,0,.6)
+	}
+	
+</style>

+ 91 - 0
pages/login/components/phoneCode.vue

@@ -0,0 +1,91 @@
+<template>
+	<view class="phone-code">
+		<view class="i-code" @tap="$tools.throttle(getCode)" :class="{disabled:isCountDown}">
+			{{countDownOrGetCodeText}}
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'phoneCode',
+	props: {
+		mobile: {
+			type: String,
+			default: ''
+		},
+		event: {
+			type: String,
+			default: ''
+		},
+	},
+	data() {
+		return {
+			isCountDown:false,     //如倒计时,更改countDownOrGetCodeText
+			countDownOrGetCodeText:"获取验证码",
+			countTime:60,
+			isOut:false,//去除定时
+			timer:'',
+		}
+	},
+	beforeDestroy() {
+		this.isOut=true;
+		// console.log('phoneCode 调一次')
+		if(this.timer){
+		    clearTimeout(this.timer);
+		    this.timer=null;				
+		}
+	},
+	methods: {
+		countDown(){
+		    if(this.isOut){
+		        return;
+		    }
+		    console.log('countDown定时一次')
+		    this.countDownOrGetCodeText="重新获取("+this.countTime+")";
+		    this.countTime--;
+		    if (this.countTime > 0) {
+		            this.timer=setTimeout(this.countDown, 1000);
+		    }else{
+		        this.countDownOrGetCodeText="获取验证码";
+		        this.isCountDown=false;
+		        this.countTime=60;
+		    }
+		},
+		async getCode(){
+		    // console.log('===',props.mobile)
+		    if(! this.$tools.match( this.mobile,'mobile')){
+		        return;
+		    }
+		    if(this.isCountDown==true){
+		        return;
+		    }
+		    this.isCountDown=true;
+		    try{
+		        await this.$request('user.sms',{mobile:this.mobile,event:this.event});
+		    }catch(err){
+				// console.log('err---',err)
+				this.$tools.msg(err.msg || '请求失败')
+		        this.isCountDown=false;
+				this.countDownOrGetCodeText="获取验证码"
+				this.countTime=60;
+		    }
+			if(this.isCountDown){
+				this.countDown(); 
+			}
+			   
+			
+		},
+	}
+}
+</script>
+<style lang="scss" scoped>
+	.i-code{
+		margin-left: auto;
+		font-size: 26rpx;
+		color: $base-color;
+		&.disabled{
+			color:#999;
+		}
+	}
+</style>

+ 202 - 0
pages/login/components/vericode.vue

@@ -0,0 +1,202 @@
+<template>
+  <view class="vericode">
+    <canvas class="canvas" canvas-id="codecanvas" id="codecanvas"> </canvas>
+    <canvas class="tcanvas" canvas-id="tcanvas" id="tcanvas"> </canvas>
+  </view>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      inDraw: false,
+      width: 0,
+      height: 0,
+      // 每次生成的验证码
+      vericode: "",
+      // 生成的数字个数
+      nums: 4,
+      // 混淆线的条数
+      drawNums: 1,
+      // 混淆线宽度
+      lineWidth: 1,
+    };
+  },
+  methods: {
+    async drawCode() {
+      const self = this;
+      if (this.inDraw) {
+        return;
+      }
+      this.inDraw = true;
+      const w = this.width;
+      const h = this.height;
+      let vericode = "";
+      const nums = this.nums;
+      const xspan = w / (nums + 1);
+      const ctx = uni.createCanvasContext("codecanvas", this);
+      ctx.clearRect(0, 0, w, h);
+      ctx.draw();
+      function drawBg() {
+        const imageData = Array.from({ length: w * h * 4 });
+        for (let i = 0; i < imageData.length; i += 4) {
+          imageData[i + 0] = Math.random() * 255;
+          imageData[i + 1] = Math.random() * 255;
+          imageData[i + 2] = Math.random() * 255;
+          imageData[i + 3] = 30;
+        }
+        uni.canvasPutImageData(
+          {
+            canvasId: "codecanvas",
+            x: 0,
+            y: 0,
+            width: w,
+            data: new Uint8ClampedArray(imageData),
+          },
+          self
+        );
+        return new Promise((resolv) => {
+          uni.canvasToTempFilePath(
+            {
+              x: 0,
+              y: 0,
+              width: w,
+              height: h,
+              canvasId: "codecanvas",
+              success: function (res) {
+                ctx.drawImage(res.tempFilePath, 0, 0);
+                resolv();
+              },
+            },
+            self
+          );
+        });
+      }
+
+      // 去掉大写 0和o gq和9 1和l 等abcdefhjkmnprstuvwyz
+      const source = "23456789";
+      const indexArr = Array.prototype.map.call(source, (e, i) => i);
+      function getAlpha() {
+        const len = indexArr.length - 1 < 0 ? 0 : indexArr.length - 1;
+        let i = Math.round(Math.random() * len);
+        const ch = source[indexArr[i]];
+        indexArr.splice(i, 1);
+        return ch;
+      }
+      const { drawText } = (() => {
+        let i = 0;
+        let fontbase = Math.round(h * 0.5);
+        let fontspan = fontbase / 2;
+        let font = Math.round(Math.random() * fontspan) + fontbase;
+        const tctx = uni.createCanvasContext("tcanvas", self);
+        function drawText() {
+          const xpos = (i++ % nums) * xspan;
+          font = fontbase + Math.round(fontspan * Math.random());
+          const txspan = 2 * xspan;
+          tctx.fillStyle = `rgb(${Math.random() * 255},${Math.random() * 255},${
+            Math.random() * 255
+          })`;
+          tctx.setFontSize(font*1.2);
+          const ybase = h / 2 + font / 2;
+          const xtrans = ((Math.random() - 0.5) * 2) / 5;
+          const ytrans = ((Math.random() - 0.5) * 2) / 5;
+          tctx.transform(1, xtrans, ytrans, 1, 0, 0);
+          const txt = getAlpha();
+          tctx.fillText(txt, 0.5 * txspan, ybase);
+          tctx.draw();
+          vericode += txt;
+          return new Promise((resolv) => {
+            uni.canvasToTempFilePath(
+              {
+                x: 0,
+                y: 0,
+                width: 2 * xspan,
+                height: h,
+                destWidth: 2 * xspan,
+                destHeight: h,
+                canvasId: "tcanvas",
+                success: function (res) {
+                  ctx.drawImage(res.tempFilePath, xpos, 0);
+                  tctx.clearRect(0, 0, txspan, h);
+                  tctx.draw();
+                  resolv();
+                },
+              },
+              self
+            );
+          });
+        }
+        return { drawText };
+      })();
+      function drawLine() {
+        const drawnums = self.drawNums;
+        for (let i = 0; i < drawnums; i++) {
+          ctx.beginPath();
+          ctx.lineWidth = self.lineWidth;
+          ctx.strokeStyle = `rgba(${Math.random() * 255},${
+            Math.random() * 255
+          },${Math.random() * 255},0.8)`;
+          // 左右非正中区域
+          const x1 =
+            ((Math.random() > 0.5 ? 1 : -1) * (Math.random() + 1) * w) / 4 +
+            w / 2;
+          // 上下非正中区域
+          const y1 =
+            ((Math.random() + 1) * h) / 4 + (Math.random() > 0.5 ? h / 2 : 0);
+          const x2 =
+            ((Math.random() > 0.5 ? 1 : -1) * (Math.random() + 1) * w) / 4 +
+            w / 2;
+          const y2 =
+            ((Math.random() + 1) * h) / 4 + (Math.random() > 0.5 ? h / 2 : 0);
+          ctx.moveTo(x1, y1);
+          ctx.lineTo(x2, y2);
+          ctx.closePath();
+          ctx.stroke();
+        }
+      }
+      await drawBg();
+      for (let i = 0; i < nums; i++) {
+        await drawText();
+      }
+
+      drawLine();
+      ctx.draw();
+      this.vericode = vericode;
+      this.inDraw = false;
+    },
+  },
+  mounted() {
+    const self = this;
+    const query = uni.createSelectorQuery().in(this);
+    query
+      .select(".vericode")
+      .boundingClientRect((data) => {
+        self.width = Math.round(data.width);
+        self.height = Math.round(data.height);
+        self.drawCode();
+      })
+      .exec();
+  },
+};
+</script>
+<style scoped>
+.vericode {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+}
+.canvas {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 1000px;
+  height: 1000px;
+}
+.tcanvas {
+  position: fixed;
+  top: 9999px;
+  width: 1000px;
+  height: 1000px;
+}
+</style>

+ 252 - 0
pages/login/forget.vue

@@ -0,0 +1,252 @@
+<template>
+	<view class="page">
+		<view class="p-header flex-col justify-center">
+			<view class="mtitle">忘记密码</view>
+			<view class="stitle">请按照要求重新设置密码</view>
+		</view>
+		
+		<view class="p-form">
+			<view class="c-item align-center">
+				<image src="/static/log_tel.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="number" class="inpt" placeholder="请输入手机号" v-model="mobile"/>
+				</view>
+			</view>
+			
+			<view class="c-item align-center">
+				<image src="/static/log_yan.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="text" class="inpt" placeholder="请输入验证码" v-model="captcha"/>
+				</view>
+				<view class="code-wrap" @tap="chgeCode">
+					<phoneCode ref="codeRef" :mobile="mobile" event="resetpwd" />
+				</view>
+			</view>
+			
+			<view class="c-item align-center">
+				<image src="/static/log_mima.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="password" class="inpt" placeholder="请设置新密码" v-model="newpassword"/>
+				</view>
+			</view>
+			<view class="c-item align-center">
+				<image src="/static/log_mima.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="password" class="inpt" placeholder="请再次确认新密码" v-model="repassword"/>
+				</view>
+			</view>
+			
+		</view>
+		<submit-btn @submit="submit" ref="submitRef">
+			<template v-slot:btns="{isSubmitted}">
+				<view class="p-btn" :class="{'disabled':isSubmitted}">
+					完成
+				</view>
+			</template>
+		</submit-btn>
+	</view>
+</template>
+
+<script>
+	import phoneCode from './components/phoneCode.vue'
+	export default {
+		components:{
+			phoneCode
+		},
+		data() {
+			return {
+				mobile:'',
+				captcha:'',
+				newpassword:'',
+				repassword:'',
+				// phone:'',
+				// password:'',
+				// code:'',
+			};
+		},
+		onLoad(opt) {
+			// this.openid = this.$Route.query.openid;
+			// this.accessToken = this.$Route.query.accessToken;
+		},
+		methods:{
+			chgeCode(){
+				this.$refs.codeRef.getCode();
+			},
+			async submit(){
+				if(! this.$tools.match(this.mobile,'mobile')){
+					return;
+				}else if(!this.$tools.match(this.captcha,'code') ){
+					return;
+				}else if(!this.$tools.match(this.newpassword,'pwd') ){
+					return;
+				}else if(!this.repassword){
+					this.$tools.msg('请输入确认密码')
+					return;
+				}else if( this.repassword !== this.newpassword){
+					this.$tools.msg('两次密码不一致')
+					return;
+				}
+				let sendData={
+					mobile:this.mobile,
+					captcha:this.captcha,
+					newpassword: this.newpassword,
+					repassword: this.repassword,
+				}
+				let res=await this.$request('user.resetpwd',sendData);
+				this.$tools.msg(res.msg);
+				this.$refs.submitRef.chgeStatus();
+				setTimeout(()=>{
+					this.goBack();
+				},800)
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page{
+		background-color: #fff;
+	}
+	.page{
+		min-height: 100vh;
+		width: 100%;
+		padding-bottom: 100rpx;
+	}
+	.p-header{
+		width: 750rpx;
+		height: 240rpx;
+		position: relative;
+		width: 630rpx;
+		margin: 0 auto;
+		.mtitle{
+			font-size: 48rpx;
+			font-family: PingFang SC;
+			font-weight: bold;
+			color: #333333;
+		}
+		.stitle{
+			margin-top: 22rpx;
+			font-size: 24rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+			color: #999999;
+		}
+	}
+	.p-form{
+		width: 630rpx;
+		margin: 0 auto;
+		.c-pwd{
+			height: 63rpx;
+			line-height: 63rpx;
+			width: 100%;
+			text-align: right;
+			font-size: 24rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+			color: #333333;
+		}
+		.c-item{
+			width: 100%;
+			height: 84rpx;
+			background-color: #F1F1F1;
+			padding: 0 26rpx;
+			margin-bottom: 30rpx;
+			border-radius: 40rpx;
+		}
+		.i-ic{
+			width: 30rpx;
+			height: 30rpx;
+			margin-right: 17rpx;
+		}
+		.i-inpt{
+			flex:1;
+			.inpt{
+				width: 100%;
+				height: 84rpx;
+				line-height: 84rpx;
+				color:#333;
+				font-size: 30rpx;
+			}
+			.code-inpt{
+				flex:1;
+				margin-right: 20rpx;
+				height: 103rpx;
+				line-height: 103rpx;
+				color:#333;
+				font-size: 26rpx;
+			}
+			.phone-code{
+				width: 200rpx;
+				height: 60rpx;
+				background: #0D91FF;
+				box-shadow: 0rpx 6rpx 10rpx 0rpx rgba(13, 145, 255, 0.28);
+				border-radius: 30rpx;
+				line-height: 60rpx;
+				text-align: center;
+				font-size: 26rpx;
+				color: #FFFFFF;
+			}
+		}
+		// .i-code{
+		// 	width: 180rpx;
+		// 	height: 70rpx;
+		// }
+	
+	}
+	.p-btn{
+		margin: 180rpx auto 0;
+		width: 630rpx;
+		height: 84rpx;
+		border-radius: 40rpx;
+		line-height: 84rpx;
+		text-align: center;
+		font-size: 34rpx;
+		font-family: PingFang SC;
+		font-weight: 500;
+		background-color: $base-color;
+		color:#fff;
+		&.disabled{
+			background-color: #eee;
+			box-shadow: none;
+			border: none;
+		}
+	}
+    .p-reg{
+		margin-top: 30rpx;
+		font-size: 28rpx;
+		font-family: PingFang SC;
+		font-weight: 500;
+		text-align: center;
+		width: 100%;
+		.stxt{
+			color: #999999;
+		}
+		.mtxt{
+			color: #333;
+		}
+	}
+	.p-tips{
+		position: absolute;
+		z-index: 2;
+		bottom: 30rpx;
+		left: 0;
+		width: 100%;
+		.ic{
+			width: 30rpx;
+			height: 30rpx;
+			margin-right: 22rpx;
+		}
+		.txt{
+			color:#999999;
+			font-size: 24rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+		}
+		.link{
+			color:#333333;
+			font-size: 24rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+		}
+	}
+</style>

+ 71 - 0
pages/login/login.vue

@@ -0,0 +1,71 @@
+<template>
+	<view class="page align-center flex-col">
+	    <view class="header-sec align-center flex-col">
+			<image src="" mode="" class="logo"></image>
+			<text class="txt">盲go小程序</text>
+		</view>
+		<view class="btn-auth">微信用户一键登录</view>
+		<view class="btn-mobile" @tap="navTo('/pages/login/mobileLogin')">手机号码登录</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			};
+		}
+	}
+</script>
+
+<style lang="scss">
+page{
+	background-color: #fff;
+}
+.header-sec{
+	width: 100%;
+	height: 832rpx;
+	padding-top: 160rpx;
+	.logo{
+		width: 180rpx;
+		height: 180rpx;
+		border-radius: 180rpx;
+		background-color: $base-color;
+	}
+	.txt{
+		margin-top: 33rpx;
+		font-size: 28rpx;
+		font-family: PingFang SC;
+		font-weight: 500;
+		color: #92B99C;
+	}
+}
+.btn-auth{
+	width: 630rpx;
+	height: 84rpx;
+	background: $base-color;
+	border-radius: 8rpx;
+	line-height: 84rpx;
+	text-align: center;
+	
+	font-size: 34rpx;
+	font-family: PingFang SC;
+	font-weight: 500;
+	color: #FFFFFF;
+}
+.btn-mobile{
+	margin-top: 30rpx;
+	
+	width: 630rpx;
+	height: 84rpx;
+	border-radius: 8rpx;
+	line-height: 84rpx;
+	text-align: center;
+	
+	font-size: 34rpx;
+	font-family: PingFang SC;
+	font-weight: 500;
+	color: $base-color;
+}
+</style>

+ 288 - 0
pages/login/mobileLogin.vue

@@ -0,0 +1,288 @@
+<template>
+	<view class="page">
+		<view class="p-header flex-col align-center justify-center">
+			<image src="/static/login_logo.png" mode="widthFix" class="img"></image>
+			<!-- <image src="/static/home_logo.png" mode="widthFix" class="ic"></image> -->
+		</view>
+		
+		<view class="p-form">
+			<view class="c-item align-center">
+				<image src="/static/log_tel.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="number" class="inpt" placeholder="请输入手机号" v-model="mobile"/>
+				</view>
+			</view>
+			
+			<view class="c-item align-center ">
+				<image src="/static/log_yan.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="number" class="inpt" placeholder="请输入验证码" v-model="code"/>
+				</view>
+				<view class="code-wrap" @tap="chgeCode">
+					<phoneCode ref="codeRef" :mobile="mobile" event="register" />
+				</view>
+			</view>
+			<view class="c-pwd last-c-item" @tap="navTo('/pages/login/forget')">
+				忘记密码?
+			</view>
+			
+			<!-- 
+			<view class="c-item align-center last-c-item">
+				<image src="/static/log_yao.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="text" class="inpt" placeholder="请输入邀请码(选填)" v-model="invite_no"/>
+				</view>
+			</view>
+			 -->
+		</view>
+		
+		<submit-btn @submit="submit" ref="submitRef">
+			<template v-slot:btns="{isSubmitted}">
+				<view class="p-btn" :class="{'disabled':isSubmitted}">
+					登录
+				</view>
+			</template>
+		</submit-btn>
+		<view class="p-reg" @tap="navTo('/pages/login/register')">
+			
+		</view>
+		<view class="p-login" @tap="navTo('/pages/login/register')">
+			<text class="stxt">还没有账号,请点击</text>
+			<text class="mtxt">注册</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	import phoneCode from './components/phoneCode.vue'
+	export default {
+		components:{
+			phoneCode,
+		},
+		data() {
+			return {
+				// isAgree:true,
+				mobile:'',
+				code:'',
+				password:'',
+				repassword:'',
+				invite_no:'',
+			};
+		},
+		onLoad(opt) {
+			// this.openid = this.$Route.query.openid;
+			// this.accessToken = this.$Route.query.accessToken;
+			if(opt.code){
+				this.invite_no = opt.code;
+			}
+		},
+		methods:{
+			chgeCode(){
+				this.$refs.codeRef.getCode();
+			},
+			async submit(){
+				// console.log('submit---');
+				// return;
+				// if(! this.isAgree){
+				// 	this.$tools.msg('您尚未同意协议')
+				// 	return;
+				// }else 
+				if(! this.$tools.match(this.mobile,'mobile')){
+					return;
+				}else if(!this.$tools.match(this.code,'code') ){
+					return;
+				}else if(!this.$tools.match(this.password,'pwd') ){
+					return;
+				}else if(!this.repassword){
+					this.$tools.msg('请输入确认密码')
+					return;
+				}else if( this.repassword !== this.password){
+					this.$tools.msg('两次密码不一致')
+					return;
+				}
+				
+				let sendData={
+					mobile:this.mobile,
+					code:this.code,
+					password: this.password,
+					repassword: this.repassword,
+					invite_no: this.invite_no,
+				}
+				let res=await this.$request('user.register',sendData);
+				// let userInfo = res.data.userinfo || {};
+				// localStorage.setItem('token',userInfo.token)
+				this.$tools.msg(res.msg);
+				this.$refs.submitRef.chgeStatus();
+				setTimeout(()=>{
+					this.navTo('/pages/login/login')
+				},800)
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page{
+		background-color: #fff;
+	}
+	.page{
+		// min-height: 100vh;
+		width: 100%;
+		// padding-bottom: 100rpx;
+	}
+	.p-header{
+		width: 750rpx;
+		// height: 470rpx;
+		height: 400rpx;
+		position: relative;
+		.img{
+			// width: 160rpx;
+			// height: 160rpx;
+			width: 300rpx;
+			height: 300rpx;
+		}
+		// .ic{
+		// 	margin-top: 16rpx;
+		// 	width: 112rpx;
+		// 	height: 38rpx;	
+		// }
+	}
+	.p-form{
+		width: 630rpx;
+		margin: 0 auto;
+		.c-pwd{
+			height: 63rpx;
+			line-height: 63rpx;
+			width: 100%;
+			text-align: right;
+			font-size: 24rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+			color: #333333;
+		}
+		.c-item{
+			width: 100%;
+			height: 84rpx;
+			background-color: #F1F1F1;
+			padding: 0 26rpx;
+			margin-bottom: 30rpx;
+			border-radius: 8rpx;
+		}
+		.i-ic{
+			width: 30rpx;
+			height: 30rpx;
+			margin-right: 17rpx;
+		}
+		.i-inpt{
+			flex:1;
+			.inpt{
+				width: 100%;
+				height: 84rpx;
+				line-height: 84rpx;
+				color:#333;
+				font-size: 30rpx;
+			}
+			.code-inpt{
+				flex:1;
+				margin-right: 20rpx;
+				height: 103rpx;
+				line-height: 103rpx;
+				color:#333;
+				font-size: 26rpx;
+			}
+			.phone-code{
+				width: 200rpx;
+				height: 60rpx;
+				background: #0D91FF;
+				box-shadow: 0rpx 6rpx 10rpx 0rpx rgba(13, 145, 255, 0.28);
+				border-radius: 30rpx;
+				line-height: 60rpx;
+				text-align: center;
+				font-size: 26rpx;
+				color: #FFFFFF;
+			}
+		}
+		// .i-code{
+		// 	width: 180rpx;
+		// 	height: 70rpx;
+		// }
+	
+	}
+	.last-c-item{
+		margin-bottom: 40rpx !important;
+	}
+	.p-btn{
+		margin: 0 auto;
+		width: 630rpx;
+		height: 84rpx;
+		border-radius: 8rpx;
+		line-height: 84rpx;
+		text-align: center;
+		font-size: 34rpx;
+		font-family: PingFang SC;
+		font-weight: 500;
+		background-color: $base-color;
+		color: #fff;
+		border-radius: 8rpx;
+		&.disabled{
+			background-color: #eee;
+			box-shadow: none;
+			border: none;
+		}
+	}
+	.p-login{
+		font-size: 28rpx;
+	    height: 80rpx;
+		line-height: 80rpx;
+		text-align: right;
+		width: 630rpx;
+		margin:16rpx auto 0;
+		font-weight: bold;
+		padding-right: 10rpx;
+		.stxt{
+			color: #999999;
+		}
+		.mtxt{
+			color: $base-color;
+			padding: 0 10rpx;
+		}
+	}
+    .p-reg{
+		margin-top: 30rpx;
+		font-size: 28rpx;
+		font-family: PingFang SC;
+		font-weight: 500;
+		text-align: center;
+		width: 100%;
+		.stxt{
+			color: #999999;
+		}
+		.mtxt{
+			color: #333;
+		}
+	}
+	.p-tips{
+		position: absolute;
+		z-index: 2;
+		bottom: 30rpx;
+		left: 0;
+		width: 100%;
+		.ic{
+			width: 30rpx;
+			height: 30rpx;
+			margin-right: 22rpx;
+		}
+		.txt{
+			color:#999999;
+			font-size: 24rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+		}
+		.link{
+			color:#333333;
+			font-size: 24rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+		}
+	}
+</style>

+ 284 - 0
pages/login/register.vue

@@ -0,0 +1,284 @@
+<template>
+	<view class="page">
+		<view class="p-header flex-col align-center justify-center">
+			<image src="/static/login_logo.png" mode="widthFix" class="img"></image>
+			<!-- <image src="/static/home_logo.png" mode="widthFix" class="ic"></image> -->
+		</view>
+		
+		<view class="p-form">
+			<view class="c-item align-center">
+				<image src="/static/log_tel.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="number" class="inpt" placeholder="请输入手机号" v-model="mobile"/>
+				</view>
+			</view>
+			
+			<view class="c-item align-center">
+				<image src="/static/log_yan.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="number" class="inpt" placeholder="请输入验证码" v-model="code"/>
+				</view>
+				<view class="code-wrap" @tap="chgeCode">
+					<phoneCode ref="codeRef" :mobile="mobile" event="register" />
+				</view>
+			</view>
+			
+			<view class="c-item align-center">
+				<image src="/static/log_mima.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="password" class="inpt" placeholder="请输入密码" v-model="password"/>
+				</view>
+			</view>
+			<view class="c-item align-center last-c-item">
+				<image src="/static/log_mima.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="password" class="inpt" placeholder="请再次确认密码" v-model="repassword"/>
+				</view>
+			</view>
+			<!-- 
+			<view class="c-item align-center last-c-item">
+				<image src="/static/log_yao.png" mode="widthFix" class="i-ic"></image>
+				<view class="i-inpt">
+					<input type="text" class="inpt" placeholder="请输入邀请码(选填)" v-model="invite_no"/>
+				</view>
+			</view>
+			 -->
+		</view>
+		<submit-btn @submit="submit" ref="submitRef">
+			<template v-slot:btns="{isSubmitted}">
+				<view class="p-btn" :class="{'disabled':isSubmitted}">
+					立即注册
+				</view>
+			</template>
+		</submit-btn>
+		<!-- <view class="p-login" @tap="navTo('/pages/login/login')">已注册,去登录</view> -->
+	</view>
+</template>
+
+<script>
+	import phoneCode from './components/phoneCode.vue'
+	export default {
+		components:{
+			phoneCode,
+		},
+		data() {
+			return {
+				// isAgree:true,
+				mobile:'',
+				code:'',
+				password:'',
+				repassword:'',
+				invite_no:'',
+			};
+		},
+		onLoad(opt) {
+			// this.openid = this.$Route.query.openid;
+			// this.accessToken = this.$Route.query.accessToken;
+			if(opt.code){
+				this.invite_no = opt.code;
+			}
+		},
+		methods:{
+			chgeCode(){
+				this.$refs.codeRef.getCode();
+			},
+			async submit(){
+				// console.log('submit---');
+				// return;
+				// if(! this.isAgree){
+				// 	this.$tools.msg('您尚未同意协议')
+				// 	return;
+				// }else 
+				if(! this.$tools.match(this.mobile,'mobile')){
+					return;
+				}else if(!this.$tools.match(this.code,'code') ){
+					return;
+				}else if(!this.$tools.match(this.password,'pwd') ){
+					return;
+				}else if(!this.repassword){
+					this.$tools.msg('请输入确认密码')
+					return;
+				}else if( this.repassword !== this.password){
+					this.$tools.msg('两次密码不一致')
+					return;
+				}
+				
+				let sendData={
+					mobile:this.mobile,
+					code:this.code,
+					password: this.password,
+					repassword: this.repassword,
+					invite_no: this.invite_no,
+				}
+				let res=await this.$request('user.register',sendData);
+				// let userInfo = res.data.userinfo || {};
+				// localStorage.setItem('token',userInfo.token)
+				this.$tools.msg(res.msg);
+				this.$refs.submitRef.chgeStatus();
+				setTimeout(()=>{
+					this.navTo('/pages/login/login')
+				},800)
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	page{
+		background-color: #fff;
+	}
+	.page{
+		// min-height: 100vh;
+		width: 100%;
+		// padding-bottom: 100rpx;
+	}
+	.p-header{
+		width: 750rpx;
+		// height: 470rpx;
+		height: 400rpx;
+		position: relative;
+		.img{
+			// width: 160rpx;
+			// height: 160rpx;
+			width: 300rpx;
+			height: 300rpx;
+		}
+		// .ic{
+		// 	margin-top: 16rpx;
+		// 	width: 112rpx;
+		// 	height: 38rpx;	
+		// }
+	}
+	.p-form{
+		width: 630rpx;
+		margin: 0 auto;
+		.c-pwd{
+			height: 63rpx;
+			line-height: 63rpx;
+			width: 100%;
+			text-align: right;
+			font-size: 24rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+			color: #333333;
+		}
+		.c-item{
+			width: 100%;
+			height: 84rpx;
+			background-color: #F1F1F1;
+			padding: 0 26rpx;
+			margin-bottom: 30rpx;
+			border-radius: 8rpx;
+		}
+		.i-ic{
+			width: 30rpx;
+			height: 30rpx;
+			margin-right: 17rpx;
+		}
+		.i-inpt{
+			flex:1;
+			.inpt{
+				width: 100%;
+				height: 84rpx;
+				line-height: 84rpx;
+				color:#333;
+				font-size: 30rpx;
+			}
+			.code-inpt{
+				flex:1;
+				margin-right: 20rpx;
+				height: 103rpx;
+				line-height: 103rpx;
+				color:#333;
+				font-size: 26rpx;
+			}
+			.phone-code{
+				width: 200rpx;
+				height: 60rpx;
+				background: #0D91FF;
+				box-shadow: 0rpx 6rpx 10rpx 0rpx rgba(13, 145, 255, 0.28);
+				border-radius: 30rpx;
+				line-height: 60rpx;
+				text-align: center;
+				font-size: 26rpx;
+				color: #FFFFFF;
+			}
+		}
+		// .i-code{
+		// 	width: 180rpx;
+		// 	height: 70rpx;
+		// }
+	
+	}
+	.last-c-item{
+		margin-bottom: 80rpx !important;
+	}
+	.p-btn{
+		margin: 0 auto;
+		width: 630rpx;
+		height: 84rpx;
+		border-radius: 8rpx;
+		line-height: 84rpx;
+		text-align: center;
+		font-size: 34rpx;
+		font-family: PingFang SC;
+		font-weight: 500;
+		background-color: $base-color;
+		color: #fff;
+		border-radius: 40rpx;
+		&.disabled{
+			background-color: #eee;
+			box-shadow: none;
+			border: none;
+		}
+	}
+	.p-login{
+		color:$base-color;
+		font-size: 28rpx;
+	    height: 80rpx;
+		line-height: 80rpx;
+		text-align: right;
+		width: 630rpx;
+		margin:16rpx auto 0;
+		font-weight: bold;
+		padding-right: 10rpx;
+	}
+    .p-reg{
+		margin-top: 30rpx;
+		font-size: 28rpx;
+		font-family: PingFang SC;
+		font-weight: 500;
+		text-align: center;
+		width: 100%;
+		.stxt{
+			color: #999999;
+		}
+		.mtxt{
+			color: #333;
+		}
+	}
+	.p-tips{
+		position: absolute;
+		z-index: 2;
+		bottom: 30rpx;
+		left: 0;
+		width: 100%;
+		.ic{
+			width: 30rpx;
+			height: 30rpx;
+			margin-right: 22rpx;
+		}
+		.txt{
+			color:#999999;
+			font-size: 24rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+		}
+		.link{
+			color:#333333;
+			font-size: 24rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+		}
+	}
+</style>

+ 64 - 0
pages/mang/components/zetank-notice.vue

@@ -0,0 +1,64 @@
+<template>
+	<view class="notice">
+		<view class="right_notice">
+		  <swiper class="notice_swiper" vertical easing-function="easeInOutCubic" :circular="true" :autoplay="true" :interval="interval">
+			<swiper-item v-for="(item,index) in noticeList" :key="index" class="sw_item" @click="clickNotice(item)">
+			   <text class="sw_text clamp">{{item.title}}</text>
+			</swiper-item>
+		  </swiper>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+  data() {
+    return {};
+  },
+  props: {
+	noticeList:{
+	  type:Array,
+	  default(){
+		  return [{id:0,title:"暂无"}]
+	  }
+	},
+	interval:{
+		type:Number,
+		default:5000
+	},
+  },
+  methods: {
+	  clickNotice(e){
+		  this.$emit('clickNotice',e)
+	  }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.notice{
+  height: 64upx;
+  line-height: 64upx;
+  margin: 0 3%;
+  // margin-top: 15upx;
+  padding: 0 10upx;
+  // box-shadow: 0upx 0upx 10upx #eee;
+  // border-radius: 8upx;
+}
+.right_notice{
+  float: left;
+  width: 100%;
+}
+.right_notice .notice_swiper{
+  height: 64upx;
+}
+.notice_swiper .sw_item{
+  height: 64upx;
+}
+.notice_swiper .sw_item .sw_text{
+  font-size: 24upx;
+  color: #333;
+  display: inline-block;
+  line-height: 64rpx;
+  max-width: 480rpx;
+}
+</style>

+ 445 - 0
pages/mang/detail.vue

@@ -0,0 +1,445 @@
+<template>
+	<view class="page">
+		<view class="swiper-sec">
+			<swiper :indicator-dots="true" :autoplay="false" :interval="3000" :duration="1000" class="swiper">
+				<swiper-item v-for="(item, idx) in 3" :key="idx" class="swiper-item">
+					<view class="img-wrap"><image src="/static/temp/pro.png" mode="aspectFill" class="img"></image></view>
+				</swiper-item>
+			</swiper>
+			<view class="ic-wrap align-center justify-center"><image src="/static/love-full.png" mode="aspectFill" class="ic"></image></view>
+		</view>
+		<view class="header-sec">
+			<view class="flex-col name-wrap">
+				<view class="flex-col">
+					<view class="flex-row">
+						<text class="text_2 clamp">幸福西饼Rice盲盒</text>
+						<view class="text-wrapper text_3 clamp">仅剩3份</view>
+					</view>
+					<image
+						src="/static/mangd-rarrow.png"
+						class="image_8 image_9"
+					/>
+				</view>
+				<view class="align-center group_10">
+					<view class="stars align-center">
+						 <image
+						  src="/static/mang_star.png"
+						  class="image_10"
+						  v-for="(item,idx) in 3"
+						  :key="idx"
+						/>	
+					</view>
+					<text class="text_4">随机搭配</text>
+				</view>
+			</view>
+			<view class="border-wrap"></view>
+			<view class="flex-row time-wrap">
+				<text class="text_5">自提时间</text>
+				<text class="text_6">今天15:00~22:00</text>
+			</view>
+			<view class="border-wrap"></view>
+			<view class="flex-col loc-wrap">
+				<view class="flex-row justify-between items-start">
+					<view class="flex-row">
+						<image
+							src="/static/temp/pro.png"
+							class="image_12"
+							mode="aspectFill"
+						/>
+						<view class="flex-col items-start group_15">
+							<text class="text_7 clamp">幸福西饼万达店</text>
+							<text class="text_8 clamp2">河东区万达广场1F东门入口</text>
+						</view>
+					</view>
+					<view class="flex-row group_16">
+						<view class="flex-col items-center image-wrapper">
+							<image
+								src="/static/mangd-loc.png"
+								class="image_13"
+								mode="aspectFill"
+							/>
+						</view>
+						<view class="section_5"><!--*--></view>
+						<view class="flex-col items-center image-wrapper view_3">
+							<image
+								src="/static/mang-phone.png"
+								class="image_14"
+								mode="aspectFill"
+							/>
+						</view>
+					</view>
+				</view>
+				<view class="group_17">
+					<text class="text_9">距离您</text>
+			 	<text class="text_10">864m</text>
+				</view>
+			</view>
+		</view>
+		<view class="line-wrap"></view>
+		<view class="flex-col items-start tips-wrap">
+			<text class="title">盲盒内能得到什么</text>
+			<text class="text_12">随机获得四拼蛋糕,奶油慕斯蛋糕二选一<br/>精致蛋挞、南瓜蛋糕二选一</text>
+		</view>
+		<view class="line-wrap"></view>
+		<view class="review-sec">
+			<view class="title-wrap align-center justify-between">
+				<text class="text_14">商品评价</text>
+				<view class="flex-row group_21">
+					<text class="text_15">查看全部</text>
+					<image
+						src="/static/mangd-rarrow.png"
+						class="image_8 image_15"
+						mode="aspectFill"
+			 	    />
+				</view>
+			</view>
+			<view class="item-wrap flex-col" v-for="(item, idx) in 3" :key="idx">
+				<view class="flex-row justify-between">
+					<view class="flex-row">
+						<image
+							src="/static/temp/pro.png"
+							class="image_16 image_22"
+							mode="aspectFill"
+						/>
+						<view class="flex-col items-start group_30">
+							<text class="text_20 clamp">甜品大师姐</text>
+							<text class="text_21">2022.07.13 10:56</text>
+						</view>
+					</view>
+					<view class="stars align-center">
+						 <image
+						  src="/static/mang_star.png"
+						  class="image_10"
+						  v-for="(item,idx) in 3"
+						  :key="idx"
+						/>	
+					</view>
+				</view>
+				<view class="flex-col group_31">
+					<view class="group_32">
+						<text class="text_22">这是我吃过的最好吃的一款,强烈推荐,口感细腻,想象不到的美味。太好吃了。</text>
+					</view>
+					<view class="imgs-wrap align-center">
+						<image
+							src="/static/temp/pro.png"
+							class="image_19 image_24"
+							mode="aspectFill"
+							v-for="(item,idx) in 6" :key="idx"
+							@tap="previewImg(idx)"
+						/>	
+					</view>
+					
+				</view>
+			</view>
+		</view>
+		<view class="line-wrap"></view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {};
+	},
+	methods:{
+		previewImg(idx){
+			let url = [];
+			this.$tools.previewImg(url)
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.swiper-sec{
+	position: relative;
+	.swiper{
+		width: 750rpx;
+		height: 750rpx;
+		.img-wrap{
+			position: relative;
+			&::after{
+				content: '';
+				position: absolute;
+				left: 0;
+				top: 0;
+				width: 100%;
+				height: 100%;
+				background-color: rgba(0,0,0,.4);
+			}
+		}
+		.img{
+			width: 750rpx;
+			height: 750rpx;
+		}
+	}
+    .ic-wrap{
+		position: absolute;
+		z-index: 2;
+		right: 0;
+		bottom: 0;
+		width: 118rpx;
+		height: 94rpx;
+		.ic{
+			width: 54rpx;
+			height: 54rpx;
+		}
+	}
+}
+.name-wrap {
+	padding: 34rpx 31rpx 37rpx;
+	.text_2 {
+		margin: 7rpx 0 6rpx;
+		color: #333333;
+		font-size: 32rpx;
+		font-family: PingFang;
+		letter-spacing: 1.28rpx;
+		max-width: 500rpx;
+	}
+	.text-wrapper {
+		margin-left: 10rpx;
+		padding: 10rpx 10rpx 11rpx;
+		background-color: #92b99c;
+		border-radius: 10rpx;
+		height: 44rpx;
+		max-width: 160rpx;
+	}
+	.text_3 {
+		margin-left: 10rpx;
+		margin-right: 5rpx;
+		color: #ffffff;
+		font-size: 24rpx;
+		font-family: PingFang;
+		// max-width: 110rpx;
+		text-align: center;
+	}
+	.image_8 {
+		width: 11rpx;
+		height: 18rpx;
+	}
+	.image_9 {
+		margin-right: 9rpx;
+		flex-shrink: 0;
+		align-self: flex-end;
+	}
+	.group_10 {
+		margin-top: 6rpx;
+	}
+	.image_10 {
+		width: 28rpx;
+		height: 28rpx;
+		margin-right: 4rpx;
+	}
+	.text_4 {
+		margin-left: 20rpx;
+		color: #999999;
+		font-size: 24rpx;
+		font-family: PingFang;
+	}
+}
+.border-wrap{
+	margin: 0 auto;
+	width: 710rpx;
+	height: 2rpx;
+	background-color: #eee;
+}
+.time-wrap {
+	padding: 40rpx 33rpx 39rpx;
+	.text_5 {
+		color: #333333;
+		font-size: 28rpx;
+		font-family: PingFang;
+		letter-spacing: 1.12rpx;
+	}
+	.text_6 {
+		margin-left: 28rpx;
+		margin-top: 3rpx;
+		color: #999999;
+		font-size: 24rpx;
+		font-family: PingFang;
+	}
+}
+.loc-wrap {
+	padding: 40rpx 30rpx 41rpx;
+	.image_12 {
+		margin-top: 5rpx;
+		width: 68rpx;
+		height: 68rpx;
+		border-radius: 68rpx;
+	}
+	.group_15 {
+		margin-left: 16rpx;
+	}
+	.text_7 {
+		color: #333333;
+		font-size: 30rpx;
+		font-family: PingFang;
+		max-width: 420rpx;
+	}
+	.text_8 {
+		margin-top: 20rpx;
+		color: #999999;
+		font-size: 24rpx;
+		font-family: PingFang;
+		max-width: 420rpx;
+		line-height: 36rpx;
+	}
+	.group_16 {
+		margin-right: 20rpx;
+		margin-top: 37rpx;
+	}
+	.image-wrapper {
+		align-self: center;
+		width: 28rpx;
+	}
+	.image_13 {
+		width: 28rpx;
+		height: 33rpx;
+	}
+	.section_5 {
+		margin-left: 41rpx;
+		background-color: #0000001a;
+		width: 1rpx;
+		height: 65rpx;
+	}
+	.view_3 {
+		margin-left: 40rpx;
+	}
+	.image_14 {
+		width: 28rpx;
+		height: 28rpx;
+	}
+	.group_17 {
+		margin-left: 3rpx;
+		align-self: flex-start;
+		line-height: 23rpx;
+		margin-top: 10rpx;
+	}
+	.text_9 {
+		color: #999999;
+		font-size: 24rpx;
+		font-family: PingFang;
+		line-height: 23rpx;
+	}
+	.text_10 {
+		color: #92b99c;
+		font-size: 24rpx;
+		font-family: PingFang;
+		line-height: 19rpx;
+	}
+}
+.line-wrap {
+	background-color: #0000000f;
+	height: 17rpx;
+}
+.tips-wrap {
+	padding: 40rpx 32rpx;
+	.title {
+		color: #333333;
+		font-size: 30rpx;
+		font-family: PingFang;
+	}
+	.text_12 {
+		margin-left: 4rpx;
+		margin-top: 20rpx;
+		color: #999999;
+		font-size: 24rpx;
+		font-family: PingFang;
+		line-height: 36rpx;
+	}
+}
+.review-sec{
+	padding-top: 35rpx;
+}
+.title-wrap {
+	height: 37rpx;
+	margin-bottom: 10rpx;
+	padding: 0 30rpx;
+	.text_14 {
+		color: #333333;
+		font-size: 28rpx;
+		font-family: PingFang;
+		letter-spacing: 1.12rpx;
+	}
+	.group_21 {
+		margin-right: 9rpx;
+	}
+	.text_15 {
+		color: #999999;
+		font-size: 24rpx;
+		font-family: PingFang;
+	}
+	.image_8 {
+		width: 11rpx;
+		height: 18rpx;
+	}
+	.image_15 {
+		margin-left: 11rpx;
+		margin-bottom: 3rpx;
+		flex-shrink: 0;
+	}
+}
+.item-wrap {
+	padding: 37rpx 33rpx 40rpx;
+	.image_16 {
+		width: 54rpx;
+		height: 54rpx;
+		border-radius: 54rpx;
+	}
+	.image_22 {
+		margin-top: 4rpx;
+	}
+	.group_30 {
+		margin-left: 20rpx;
+	}
+	.text_20 {
+		color: #333333;
+		font-size: 30rpx;
+		font-family: PingFang;
+		letter-spacing: 1.2rpx;
+		max-width: 300rpx;
+	}
+	.text_21 {
+		margin-top: 15rpx;
+		color: #999999;
+		font-size: 20rpx;
+		font-family: PingFang;
+	}
+	.image_10 {
+		width: 28rpx;
+		height: 28rpx;
+		margin-left: 4rpx;
+	}
+	.group_31 {
+		margin: 13.5rpx 24rpx 0 7rpx;
+	}
+	.group_32 {
+		line-height: 37rpx;
+	}
+	.text_22 {
+		color: #333333;
+		font-size: 26rpx;
+		font-family: PingFang;
+		line-height: 40rpx;
+		letter-spacing: 1.04rpx;
+	}
+	.imgs-wrap{
+		flex-wrap: wrap;
+		.image_19 {
+			width: 180rpx;
+			height: 180rpx;
+			margin-right: 20rpx;
+			border-radius: 10rpx;
+			margin-bottom: 20rpx;
+			// &:nth-child(4n){
+			// 	margin-right: 0;
+			// }
+		}
+		.image_24 {
+			margin-top: 16.5rpx;
+			align-self: flex-start;
+		}	
+	}
+	
+}
+</style>

+ 386 - 0
pages/mang/mang.vue

@@ -0,0 +1,386 @@
+<template>
+	<view class="page">
+		<navbar-line :bgColor="bgColor" :hasPlacer="false">
+			<view class="header-loc  align-center" @tap="chooseLoc">
+				<image src="/static/mang-loc.png" class="image_19" mode="aspectFill" />
+				<text class="text_43">沂蒙云谷·创意孵化中心</text>
+			</view>
+		</navbar-line>
+		<view class="p-header"><image src="/static/mang-bg.png" mode="aspectFill" class="img"></image></view>
+		<view class="align-center p-ads">
+			<image src="/static/mang-sear.png" class="image_20" mode="aspectFill" />
+
+			<view class="group_20"><zetank-notice colors="#55aaff" :noticeList="ads" :interval="3000"></zetank-notice></view>
+		</view>
+		<view class="p-entry">
+			<view class="flex-col items-center grid-item">
+				<image
+					src="/static/mang-dianxin.png"
+					class="image"
+				/>
+				<text class="text">点心</text>
+			</view>
+			<view class="flex-col items-center grid-item">
+				<image
+					src="/static/mang-dangao.png"
+					class="image"
+				/>
+				<text class="text">蛋糕</text>
+			</view>
+			<view class="flex-col items-center grid-item">
+				<image
+					src="/static/mang-buding.png"
+					class="image"
+				/>
+				<text class="text">布丁</text>
+			</view>
+			<view class="flex-col items-center grid-item">
+				<image
+					src="/static/mang-pdangao.png"
+					class="image"
+				/>
+				<text class="text">纸杯蛋糕</text>
+			</view>
+			<view class="flex-col items-center grid-item">
+				<image
+					src="/static/mang-pisa.png"
+					class="image"
+				/>
+				<text class="text">披萨</text>
+			</view>
+			<view class="flex-col items-center grid-item">
+				<image
+					src="/static/mang-tusi.png"
+					class="image"
+				/>
+				<text class="text">吐司</text>
+			</view>
+			<view class="flex-col items-center grid-item">
+				<image
+					src="/static/mang-all.png"
+					class="image"
+				/>
+				<text class="text">全部</text>
+			</view>
+		</view>
+	
+	    <view class="p-tabs align-center justify-between">
+			  <view class="flex-col text-wrapper">
+				   <image
+					  src="/static/mang-tabbg.png"
+					  class="image_8"
+				   />
+				  <text class="text_9">智能排序</text>
+			  </view>
+			  <view class="flex-col text-wrapper active">
+				  <image
+					  src="/static/mang-tabbg.png"
+					  class="image_8"
+				  />
+				  <text class="text_9">距离最近</text>
+			  </view>
+			  <view class="flex-col text-wrapper">
+				  <image
+					  src="/static/mang-tabbg.png"
+					  class="image_8"
+				  />
+				  <text class="text_9">评分最高</text>
+			 </view>
+		</view>
+		
+		<view class="p-list">
+			<view class="flex-row section_2 view_6" v-for="(item,idx) in 3" :key="idx" @tap="navTo('/pages/mang/detail')">
+			  <image
+			    src="/static/temp/pro.png"
+			    class="group_11"
+				mode="aspectFill"
+			  />
+			  <view class="flex-col text_21 view_7">
+			    <text class="text_25 clamp">稻香村糕点随机盲盒</text>
+				<view class="stars align-center">
+					 <image
+					  src="/static/mang_star.png"
+					  class="image_14"
+					  v-for="(item,idx) in 3"
+					  :key="idx"
+					/>	
+				</view>
+			    <text class="text_26">随机搭配</text>
+			    <view class="flex-row group_16">
+			      <view class="group_17">
+			        <text class="text_27">¥</text>
+			        <text class="text_28">38.8</text>
+			      </view>
+			      <text class="text_29">¥44~68</text>
+			    </view>
+			  </view>
+			  <text class="text_30">898m</text>
+			  <view class="flex-col text-wrapper_2"><text class="text_23 text_31 clamp">仅剩3份</text></view>
+			</view>
+		</view>
+		
+	
+	</view>
+</template>
+
+<script>
+import navbarLine from '@/components/navbar-line.vue';
+import zetankNotice from './components/zetank-notice.vue';
+export default {
+	components: {
+		navbarLine,
+		zetankNotice
+	},
+	data() {
+		return {
+			ads: [{ title: '1已帮助减少2486g数量的食品浪费' }, { title: '2已帮助减少2486g数量的食品浪费' }],
+			bgColor:'transparent',
+		};
+	},
+	onPageScroll(e) {
+		// console.log('---',e);
+		let scrollTop = e.scrollTop;
+		if(scrollTop > this.CustomBar - 10){
+			this.bgColor = "#ffffff";
+		}else{
+			this.bgColor = "transparent";
+		}
+	},
+	methods:{
+		async chooseLoc(){
+			this.navTo('/pages/address/address')
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+page {
+	background-color: #F7FEFA;
+}
+.header-loc {
+	position: absolute;
+	width: 100%;
+	padding: 0 30rpx;
+	top: 50%;
+	transform: translateY(-50%);
+	.image_19 {
+		width: 26rpx;
+		height: 28rpx;
+	}
+	.text_43 {
+		margin-left: 12rpx;
+		color: #333333;
+		font-size: 28rpx;
+		font-family: PingFang;
+		letter-spacing: 1.12rpx;
+	}
+}
+.p-header {
+	.img {
+		width: 750rpx;
+		height: 532rpx;
+	}
+}
+.p-ads {
+	margin: -32rpx auto 0;
+	width: 690rpx;
+	height: 72rpx;
+	padding: 0 22rpx;
+	background-image: url('/static/mang-ads.png');
+	background-size: 100% 100%;
+	background-repeat: no-repeat;
+	position: relative;
+	z-index: 2;
+	.image_20 {
+		width: 35rpx;
+		height: 35rpx;
+	}
+	.group_20 {
+		width: 100%;
+	}
+}
+.p-entry {
+	width: 690rpx;
+	background: #FFFFFF;
+	box-shadow: 0rpx 6rpx 10rpx 0rpx rgba(42,42,42,0.08);
+	
+	margin: 30rpx auto 0;
+	padding: 9.5rpx 12rpx 20.5rpx;
+	background-color: #ffffff;
+	box-shadow: 0px 6rpx 10rpx #2a2a2a14;
+	border-radius: 20rpx;
+	height: 350rpx;
+	display: grid;
+	grid-template-columns: repeat(4, 1fr);
+	.grid-item {
+		padding: 17rpx 4rpx;
+	}
+	.image {
+		width: 96rpx;
+		height: 96rpx;
+	}
+	.text {
+		margin-top: 7rpx;
+		color: #333333;
+		font-size: 24rpx;
+		font-family: PingFang;
+		line-height: 23rpx;
+		letter-spacing: 1.44rpx;
+	}
+}
+.p-tabs{
+	margin-left: 30rpx;
+	width: 530rpx;
+	.text-wrapper {
+		height: 85rpx;
+		position: relative;
+		&.active{
+			.image_8{
+				display: block;
+			}
+			.text_9{
+			  color: #333333;
+			}
+		}
+	  .image_8{
+		  width: 36rpx;
+		  height: 36rpx;
+		  display: none;
+		  position: absolute;
+		  z-index: 2;
+		  right: -14rpx;
+		  top:40%;
+		  transform: translateY(-50%);
+	  }
+		.text_9 {
+		  color: #999999;
+		  font-size: 30rpx;
+		  font-family: PingFang;
+		  letter-spacing: 1.2rpx;
+		  line-height: 85rpx;
+		}	  
+	}
+
+}
+.p-list{
+	.section_2 {
+	  width: 690rpx;
+	  margin:0 auto 24rpx;
+	  padding: 24rpx 14rpx 24rpx 24rpx;
+	  background-color: #ffffff;
+	  box-shadow: 0px 6rpx 10rpx #2a2a2a14;
+	  border-radius: 20rpx;
+	  position: relative;
+	  .text-wrapper_2 {
+	    padding: 8rpx 0 16rpx;
+	    background-image: url('/static/mang-listbg.png');
+	    background-size: 100% 100%;
+	    background-repeat: no-repeat;
+	    position: absolute;
+	    right: -10rpx;
+	    top: 26rpx;
+		width: 120rpx;
+	  }
+	  .text_23 {
+	    margin-left: 12rpx;
+	    margin-right: 6rpx;
+	    color: #ffffff;
+	    font-size: 24rpx;
+	    font-family: PingFang;
+		max-width: 120rpx;
+	  }
+	  // .text_31 {
+	  //   margin-left: 12rpx;
+	  //   margin-right: 6rpx;
+	  // }
+	}
+	.view_6 {
+	  padding: 24rpx 18rpx 24rpx 24rpx;
+	}
+	.group_11 {
+	  flex-shrink: 0;
+	  width: 200rpx;
+	  height: 200rpx;
+	  border-radius: 20rpx;
+	}
+	.text_21 {
+	  margin-top: 100rpx;
+	  color: #999999;
+	  font-size: 24rpx;
+	  font-family: PingFang;
+	  line-height: 18rpx;
+	}
+	.view_7 {
+	  margin-top: initial;
+	  color: initial;
+	  font-size: initial;
+	  font-family: initial;
+	  line-height: initial;
+	  margin-left: 19rpx;
+	  flex: 1 1 auto;
+	  align-self: center;
+	}
+	.text_25 {
+	  color: #333333;
+	  font-size: 30rpx;
+	  font-family: PingFang;
+	  max-width: 360rpx;
+	}
+	.stars{
+		 margin-top: 12rpx;
+		.image_14 {
+		  width: 28rpx;
+		  height: 28rpx;
+		  margin-right: 4rpx;
+		}
+	}
+	
+	.text_26 {
+	  margin-top: 20rpx;
+	  align-self: flex-start;
+	  color: #999999;
+	  font-size: 24rpx;
+	  font-family: PingFang;
+	  line-height: 23rpx;
+	}
+	.group_16 {
+	  margin-top: 37rpx;
+	  padding: 0 2rpx;
+	}
+	.group_17 {
+	  line-height: 28rpx;
+	  height: 29rpx;
+	}
+	.text_27 {
+	  color: #ff3e3e;
+	  font-size: 24rpx;
+	  font-family: PingFang;
+	  line-height: 19rpx;
+	}
+	.text_28 {
+	  color: #ff3e3e;
+	  font-size: 36rpx;
+	  font-family: PingFang;
+	  line-height: 28rpx;
+	}
+	.text_29 {
+	  margin-left: 10rpx;
+	  margin-top: 9rpx;
+	  color: #999999;
+	  font-size: 24rpx;
+	  font-family: PingFang;
+	  line-height: 20rpx;
+	  text-decoration: line-through;
+	}
+	.text_30 {
+	  z-index: 2;
+	  position: absolute;
+	  top: 130rpx;
+	  right: 20rpx;
+	  color: #999999;
+	  font-size: 24rpx;
+	  font-family: PingFang;
+	}
+}
+</style>

+ 19 - 0
pages/mine/addrs.vue

@@ -0,0 +1,19 @@
+<template>
+	<view class="page">
+		
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			};
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

+ 19 - 0
pages/mine/mine.vue

@@ -0,0 +1,19 @@
+<template>
+	<view>
+		abc
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			};
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

二进制
static/addr-del.png


二进制
static/addr-edit.png


二进制
static/check-do.png


二进制
static/check-un.png


二进制
static/empty.png


二进制
static/log_mima.png


二进制
static/log_tel.png


二进制
static/log_yan.png


二进制
static/love-full.png


二进制
static/love.png


二进制
static/mang-ads.png


二进制
static/mang-all.png


二进制
static/mang-bg.png


二进制
static/mang-buding.png


二进制
static/mang-dangao.png


二进制
static/mang-dianxin.png


二进制
static/mang-listbg.png


二进制
static/mang-loc.png


二进制
static/mang-pdangao.png


二进制
static/mang-phone.png


二进制
static/mang-pisa.png


二进制
static/mang-sear.png


二进制
static/mang-tabbg (1).png


二进制
static/mang-tabbg.png


二进制
static/mang-tusi.png


二进制
static/mang_star.png


二进制
static/mangd-loc.png


二进制
static/mangd-rarrow.png


二进制
static/r-arrow.png


二进制
static/tab/home-full.png


二进制
static/tab/home.png


二进制
static/tab/mang-full.png


二进制
static/tab/mang.png


二进制
static/tab/mine-full.png


二进制
static/tab/mine.png


二进制
static/tab/order-full.png


二进制
static/tab/order.png


二进制
static/temp/pro.png


+ 27 - 0
store/index.js

@@ -0,0 +1,27 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+
+// https://webpack.js.org/guides/dependency-management/#requirecontext
+const modulesFiles = require.context('./modules', true, /\.js$/)
+
+// you do not need `import app from './modules/app'`
+// it will auto require all vuex module from modules file
+const modules = modulesFiles.keys().reduce((modules, modulePath) => {
+  // set './app.js' => 'app'
+  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
+  const value = modulesFiles(modulePath)
+  // console.log('----',moduleName,value);
+  value.default.namespaced=true;
+  modules[moduleName] = value.default
+  return modules
+}, {})
+
+// this.$store.dispatch/commit/getters('baseconfig/getBaseconfig')
+const store = new Vuex.Store({
+	modules
+})
+
+export default store
+

+ 49 - 0
store/modules/userInfo.js

@@ -0,0 +1,49 @@
+import Request from '@/common/request';
+import Tools from '@/common/tools'
+const userInfo = {
+    namespaced:true,
+    state:{
+        userInfo:{},   
+    },
+    mutations:{
+        USER_INFO(state,info){
+            state.userInfo = info;
+        }        
+    },
+	actions: {
+		getopenid(){
+			return new Promise(async (resolve,reject)=>{
+				let loginRes = await uni.login({
+					provider: 'weixin'
+				});
+				// console.log('openid loginRes===',loginRes)
+				if (loginRes.hasOwnProperty('code')) {
+					let codeRes=await Request('user.getUserOpenid',{ code: loginRes.code});
+					// console.log('openid codeRes===',codeRes.data)
+					resolve(codeRes.data.openid)
+				} else {
+					Tools.msg('登录失败,请重启应用');
+					reject(false);
+				}       
+			})
+		},
+	    getinfo({ dispatch,commit }) {
+		   /**onLaunch即调用,查看token是否过期 */
+		   let token = uni.getStorageSync('token')
+		   if(!token){
+			   return false;
+		   }
+		   return new Promise((resolve, reject) => {
+			   Request('user.userInfo').then(res => {
+				   // console.log('usercenter.getUserInfo--',res);
+				   let myData= res.data || {}
+				   commit('USER_INFO', myData);
+				   resolve(myData)	
+			   }).catch(e => {
+				   reject(e)
+			   })
+		   })
+	    },
+	},
+}
+export default userInfo;

+ 151 - 0
style/common.css

@@ -0,0 +1,151 @@
+/* #ifndef APP-PLUS-NVUE */
+view,
+scroll-view,
+swiper,
+swiper-item,
+cover-view,
+cover-image,
+icon,
+text,
+rich-text,
+progress,
+button,
+checkbox,
+form,
+input,
+label,
+radio,
+slider,
+switch,
+textarea,
+navigator,
+audio,
+camera,
+image,
+video {
+	box-sizing: border-box;
+}
+
+image{
+	display: block;
+}
+text,view{
+	line-height: 1;
+	/* font-family: Helvetica Neue, Helvetica, sans-serif; */
+}
+button{
+	padding: 0;
+	margin: 0;
+	background-color: rgba(0,0,0,0);
+}
+button:after{
+	border: 0;
+}
+/* #endif */
+
+.clamp {
+	/* #ifdef APP-PLUS-NVUE */
+	lines: 1;
+	/* #endif */
+	/* #ifndef APP-PLUS-NVUE */
+	overflow: hidden;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	display: block;
+	/* #endif */
+}
+.clamp2 {
+	/* #ifdef APP-PLUS-NVUE */
+	lines: 2;
+	/* #endif */
+	/* #ifndef APP-PLUS-NVUE */
+	display: -webkit-box;
+	-webkit-box-orient: vertical;
+	-webkit-line-clamp: 2;
+	overflow: hidden;
+	/* #endif */
+}
+
+/* 布局 */
+button {
+  margin: 0;
+  padding: 0;
+  border: 1px solid transparent;
+  outline: none;
+  background-color: transparent;
+}
+
+button:active {
+  opacity: 0.6;
+}
+.flex-col {
+  display: flex;
+  flex-direction: column;
+}
+.flex-row {
+  display: flex;
+  flex-direction: row;
+}
+.justify-start {
+  display: flex;
+  justify-content: flex-start;
+}
+.justify-center {
+  display: flex;
+  justify-content: center;
+}
+
+.justify-end {
+  display: flex;
+  justify-content: flex-end;
+}
+.justify-evenly {
+  display: flex;
+  justify-content: space-evenly;
+}
+.justify-around {
+  display: flex;
+  justify-content: space-around;
+}
+.justify-between {
+  display: flex;
+  justify-content: space-between;
+}
+.align-start {
+  display: flex;
+  align-items: flex-start;
+}
+.align-center {
+  display: flex;
+  align-items: center;
+}
+.align-end {
+  display: flex;
+  align-items: flex-end;
+}
+.items-start {
+  display: flex;
+  align-items: flex-start;
+}
+
+.items-center {
+  display: flex;
+  align-items: center;
+}
+
+.items-end {
+  display: flex;
+  align-items: flex-end;
+}
+.fill{
+	flex:1;
+}
+.ml-auto{
+	margin-left: auto;
+}
+.mr-auto{
+	margin-right: auto;
+}
+ ::-webkit-scrollbar{ width: 0; height: 0; color: transparent; }
+
+

+ 102 - 0
style/icon-删除.css

@@ -0,0 +1,102 @@
+	/* project id 1729059 */
+	@font-face {
+	  font-family: 'zicon';  
+	  font-weight: normal;
+	  font-style: normal;
+	  src: url('//at.alicdn.com/t/font_3263006_uo45qjsid5p.ttf?t=1650244706728') format('truetype');
+	}
+	
+	[class*="zicon-"] {
+		font-family: "zicon" !important;
+		font-size: 16px;
+		font-style: normal;
+		-webkit-font-smoothing: antialiased;
+		-moz-osx-font-smoothing: grayscale;
+	}
+	
+
+	.zicon-xuanze:before{
+		content: "\e627";
+	}
+	.zicon-weixuan:before{
+		content: "\e629";
+	}	
+	.zicon-youjiantou:before{
+		content: "\e641";
+	}
+	
+	
+	.zicon-shangjiantou:before{
+		content: "\e60c";
+	}	
+	.zicon-xiajiantou:before{
+		content: "\e60d";
+	}
+	
+	.zicon-dingwei:before{
+		content: "\e60a";
+	}
+	.zicon-sousuo:before{
+		content: "\e600";
+	}
+	.zicon-bofang:before{
+		content: "\e61b";
+	}
+	.zicon-quxiao:before{
+		content: "\e64a";
+	}
+	
+	.zicon-eyeclose:before{
+		content: "\e6cf";
+	}
+	.zicon-eyeopen:before{
+		content: "\e6e1";
+	}
+	
+	.zicon-shanchu:before{
+		content: "\e8c1";
+	}
+	.zicon-addpic:before{
+		content: "\e700";
+	}
+	.zicon-del:before{
+		content: "\e8e7";
+	}
+	
+	.zicon-rarrow:before{
+		content: "\e62a";
+	}
+	.zicon-darrow:before{
+		content: "\e642";
+	}
+	
+	.zicon-shaixuan:before{
+		content: "\e610";
+	}
+	
+	.zicon-wenda:before{
+		content: "\e889";
+	}
+	.zicon-wenda1:before{
+		content: "\e8cd";
+	}
+	.zicon-wutu:before{
+		content: "\e63d";
+	}
+	.zicon-gengduo:before{
+		content: "\e601";
+	}
+	
+	.zicon-phone:before{
+		content: "\e678";
+	}
+	.zicon-ishipinshixiao:before{
+		content: "\ec7d";
+	}
+	
+	.zicon-jia:before{
+		content: "\e628";
+	}
+	.zicon-jian:before{
+		content: "\e62b";
+	}

+ 14 - 0
uni.scss

@@ -0,0 +1,14 @@
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+
+/* 文字尺寸 */
+$uni-font-size-sm:24rpx;
+$uni-font-size-base:28rpx;
+$uni-font-size-lg:32rpx;
+$base-color: #92B99C;
+$placeholder-color:#999999;

部分文件因为文件数量过多而无法显示