廉志超 1 рік тому
коміт
d91479931d
100 змінених файлів з 11822 додано та 0 видалено
  1. 5 0
      .env.dev.js
  2. 18 0
      .env.js
  3. 5 0
      .env.prod.js
  4. 156 0
      App.vue
  5. 92 0
      api/config.js
  6. 143 0
      api/custom-request111.js
  7. 19 0
      api/index.js
  8. 157 0
      api/modules/mall.js
  9. 13 0
      api/modules/other.js
  10. 32 0
      api/modules/user.js
  11. 92 0
      api/upload.js
  12. 91 0
      components/add-btn-fixed 111.vue
  13. 70 0
      components/add-popup.vue
  14. 50 0
      components/img-swiper.vue
  15. 36 0
      components/loadmore.vue
  16. 143 0
      components/nav/dial-nav.vue
  17. 129 0
      components/nav/horizontal-scroll-nav.vue
  18. 63 0
      components/nav/icon-label-nav.vue
  19. 50 0
      components/nav/label-count.vue
  20. 162 0
      components/navbar/navbar-city-search.vue
  21. 181 0
      components/navbar/navbar-search.vue
  22. 228 0
      components/navbar/navbar-tab-search.vue
  23. 176 0
      components/navbar/navbar-top-search.vue
  24. 41 0
      components/navbar/navbar.vue
  25. 76 0
      components/no-data.vue
  26. 34 0
      components/popup-empty.vue
  27. 214 0
      components/privacy-popup.vue
  28. 122 0
      components/search/search.vue
  29. 108 0
      components/select-reason.vue
  30. 86 0
      components/tabs.vue
  31. 31 0
      components/tag.vue
  32. 80 0
      components/title-operate.vue
  33. 60 0
      main.js
  34. 154 0
      manifest.json
  35. 56 0
      mixins/smsCode.js
  36. 1 0
      note.md
  37. 37 0
      package.json
  38. 141 0
      pages-mall/components/after-sales/apply-reason.vue
  39. 114 0
      pages-mall/components/after-sales/back-all.vue
  40. 72 0
      pages-mall/components/after-sales/back-goods.vue
  41. 114 0
      pages-mall/components/after-sales/back-money.vue
  42. 59 0
      pages-mall/components/after-sales/back-status.vue
  43. 221 0
      pages-mall/components/after-sales/order-card1.vue
  44. 237 0
      pages-mall/components/after-sales/select-goods-pop.vue
  45. 75 0
      pages-mall/components/after-sales/select-item.vue
  46. 88 0
      pages-mall/components/evaluate-card.vue
  47. 200 0
      pages-mall/components/express/list.vue
  48. 29 0
      pages-mall/components/express/utils.js
  49. 120 0
      pages-mall/components/goods/goods-info.vue
  50. 282 0
      pages-mall/components/goods/goods-select-sku.vue
  51. 136 0
      pages-mall/components/goods/goods-select.vue
  52. 197 0
      pages-mall/components/goods/submitBar.vue
  53. 111 0
      pages-mall/components/line-info-operate.vue
  54. 102 0
      pages-mall/components/navbar-search.vue
  55. 123 0
      pages-mall/components/navbar-tab-search.vue
  56. 80 0
      pages-mall/components/order/express-goods-card.vue
  57. 179 0
      pages-mall/components/order/order-card.vue
  58. 193 0
      pages-mall/components/order/order-goods-card.vue
  59. 95 0
      pages-mall/components/order/order-operate.vue
  60. 95 0
      pages-mall/components/order/order-status.vue
  61. 97 0
      pages-mall/components/order/orderdetail-goods-card.vue
  62. 132 0
      pages-mall/components/order/sel-coupon-popup.vue
  63. 152 0
      pages-mall/components/order/submit-discounts.vue
  64. 94 0
      pages-mall/components/order/submit-goods-card.vue
  65. 92 0
      pages-mall/components/order/submit-operate.vue
  66. 248 0
      pages-mall/mixins/order-operate.js
  67. 353 0
      pages-mall/pages/after-sales/apply-status - 副本.vue
  68. 380 0
      pages-mall/pages/after-sales/apply-status.vue
  69. 225 0
      pages-mall/pages/after-sales/apply.vue
  70. 118 0
      pages-mall/pages/after-sales/back.vue
  71. 173 0
      pages-mall/pages/after-sales/components/setExpress.vue
  72. 239 0
      pages-mall/pages/after-sales/list.vue
  73. 139 0
      pages-mall/pages/after-sales/success.vue
  74. 140 0
      pages-mall/pages/evaluate/add.vue
  75. 36 0
      pages-mall/pages/evaluate/list.vue
  76. 225 0
      pages-mall/pages/goods/detail.vue
  77. 101 0
      pages-mall/pages/goods/list1.vue
  78. 136 0
      pages-mall/pages/order/detail.vue
  79. 188 0
      pages-mall/pages/order/express.vue
  80. 190 0
      pages-mall/pages/order/list.vue
  81. 185 0
      pages-mall/pages/order/pay-result.vue
  82. 210 0
      pages-mall/pages/order/pay.vue
  83. 305 0
      pages-mall/pages/order/submit.vue
  84. 104 0
      pages-mall/pages/zone/zone.vue
  85. BIN
      pages-mall/static/express-dot-active.png
  86. BIN
      pages-mall/static/express-dot.png
  87. BIN
      pages-mall/static/express-ing.png
  88. BIN
      pages-mall/static/express-over.png
  89. BIN
      pages-mall/static/icon-ali-pay.png
  90. BIN
      pages-mall/static/icon-wx-pay.png
  91. BIN
      pages-mall/static/select-item-1.png
  92. BIN
      pages-mall/static/select-item-2.png
  93. BIN
      pages-mall/static/select-item-3.png
  94. 241 0
      pages-mine/components/city-picker - 副本.vue
  95. 271 0
      pages-mine/components/city-picker.vue
  96. 127 0
      pages-mine/components/discounts-item.vue
  97. 151 0
      pages-mine/pages/address/add-or-update.vue
  98. 140 0
      pages-mine/pages/address/list.vue
  99. 202 0
      pages-mine/pages/collection.vue
  100. 154 0
      pages-mine/pages/discounts.vue

+ 5 - 0
.env.dev.js

@@ -0,0 +1,5 @@
+export default {
+    "NODE_ENV": 'development',
+    "apiUrl":"https://erp.521word.com",
+    "apiUrlPrefix":"/api",
+};

+ 18 - 0
.env.js

@@ -0,0 +1,18 @@
+import dev from './.env.dev.js';
+import prod from './.env.prod.js';
+/* // 开发环境
+const dev = {
+	"apiUrl":"https://erp.521word.com"
+}
+
+// 生产环境
+const production= {
+	"apiUrl":"https://erp.521word.com"
+} */
+// 注意:这里的属性名要和上面package.json中定义的扩展节点编译名称相同
+const ENV_CONFIG = {
+	dev,
+	prod,
+}
+console.log(ENV_CONFIG);
+module.exports = ENV_CONFIG

+ 5 - 0
.env.prod.js

@@ -0,0 +1,5 @@
+export default {
+    "NODE_ENV": 'production',
+    "apiUrl":"https://book.shuhi.com",
+    "apiUrlPrefix":"/api",
+};

+ 156 - 0
App.vue

@@ -0,0 +1,156 @@
+<script>
+export default {
+	globalData: {
+		// 胶囊距上距离
+		menuTop: 0,
+		// 导航栏高度
+		navBarHeight: 0,
+		// 胶囊距右方间距(方保持左、右间距一致)
+		menuRight: 0,
+		// 胶囊距底部间距(保持底部间距一致)
+		menuBotton: 0,
+		// 胶囊高度(自定义内容可与胶囊高度保证一致)
+		menuHeight: 0,
+		// 状态栏高度
+		statusBarHeight: 0,
+		// 安全距离
+		safeAreaHeight: 0,
+		// 胶囊宽度
+		menuWidth: 0,
+		// 窗口宽度
+		windowWidth:0,
+		// 窗口宽度高度
+		windowHeight:0,
+		// 常规子页面可操作区域高度
+		pageContentHeight:0,
+		
+	},
+	onLaunch(options) {
+		const that = this;
+		// 获取系统信息
+		const systemInfo = uni.getSystemInfoSync();
+		// 胶囊按钮位置信息
+		const menuButtonInfo = wx.getMenuButtonBoundingClientRect();
+		console.log(menuButtonInfo);
+		// 导航栏高度 = 状态栏到胶囊的间距(胶囊距上距离-状态栏高度) * 2 + 胶囊高度 + 状态栏高度
+		that.globalData.menuTop = menuButtonInfo.top - systemInfo.statusBarHeight;
+		that.globalData.menuBotton = menuButtonInfo.top - systemInfo.statusBarHeight;
+		that.globalData.menuWidth = menuButtonInfo.width;
+		that.globalData.navBarHeight = (menuButtonInfo.top - systemInfo.statusBarHeight) * 2 + menuButtonInfo.height + systemInfo.statusBarHeight;
+		that.globalData.menuRight = systemInfo.screenWidth - menuButtonInfo.right;
+		that.globalData.menuHeight = menuButtonInfo.height;
+		that.globalData.statusBarHeight = systemInfo.statusBarHeight;
+		that.globalData.safeAreaHeight = systemInfo.safeAreaInsets.bottom;
+		that.globalData.windowWidth = systemInfo.windowWidth;
+		that.globalData.windowHeight = systemInfo.windowHeight;
+		that.globalData.pageContentHeight = systemInfo.windowHeight - (that.globalData.navBarHeight+that.globalData.menuTop+that.globalData.menuBotton);
+		
+		console.log(that.globalData);
+		
+	}
+};
+</script>
+
+<style lang="scss">
+// ===
+// === 注意:此处导入的css,会作用于全部.vue文件,请适量导入
+// ===
+body{	
+	font-family: PingFang-SC-Regular, PingFang-SC;
+}
+page{
+	background-color: $app-theme-bg-gray-deep-color
+}
+@import 'uview-ui/index.scss';
+/* 解决小程序和app滚动条的问题 */
+/* #ifdef MP-WEIXIN || APP-PLUS */
+::-webkit-scrollbar {
+	display: none;
+	width: 0 !important;
+	height: 0 !important;
+	-webkit-appearance: none;
+	background: transparent;
+	color: transparent;
+}
+/* #endif */
+
+/* 解决H5 的问题 */
+/* #ifdef H5 */
+uni-scroll-view .uni-scroll-view::-webkit-scrollbar {
+	/* 隐藏滚动条,但依旧具备可以滚动的功能 */
+	display: none;
+	width: 0 !important;
+	height: 0 !important;
+	-webkit-appearance: none;
+	background: transparent;
+	color: transparent;
+}
+/* #endif */
+
+.shu-elip-1{
+	overflow:hidden;
+	text-overflow:ellipsis;
+	white-space:nowrap;
+}
+.shu-elip-2 {
+  display: -webkit-box;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  word-wrap: break-word;
+  white-space: normal !important;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+}
+// 导出 scss 变量用于在 script 下使用
+.price_color{
+	color: $app-theme-text-money-color;
+}
+.color_blue{
+	color: $app-theme-blue;
+}
+
+.btnGroup{
+	display: flex;
+	align-items: center;
+	.btn{
+		margin: 0 10rpx;
+	}
+}
+
+
+.mallbtn {	
+		min-width: 200rpx;
+		line-height: 66rpx;
+		padding: 0 20rpx;
+		border-radius: 36rpx;
+		color: #ffffff;
+		margin-right: 20rpx;
+		text-align: center;
+		flex: 1;
+		padding: 0 30rpx;
+}
+.soldOutBtn{
+	@extend .mallbtn;
+	background-color: $app-theme-nobuy-bg-color;
+}
+.joinCartBtn {
+	@extend .mallbtn;
+	background-color: $app-theme-joincart-bg-color;
+}
+.buyBtn {
+	@extend .mallbtn;
+	background-color: $app-theme-buybtn-bg-color;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+</style>

+ 92 - 0
api/config.js

@@ -0,0 +1,92 @@
+import ENV_CONFIG from '@/.env.js'
+
+// api前缀
+export const HTTP_URL_DEV_PREFIX = '/api'
+export const HTTP_URL_PROD_PREFIX = '/api'
+const env = ENV_CONFIG[process.env.ENV_TYPE];
+// 基于uview-ui的http配置
+export const UVIEWUI_HTTP_CONFIG = {
+	// 地址
+	baseUrl: env.apiUrl+env.apiUrlPrefix,
+	// 请求方式
+	method: 'post',
+	// 参数类型
+	dataType: 'json',
+	// 是否显示请求中的loading
+	showLoading: true,
+	// 请求loading中的文字提示
+	loadingText: '请求中...',
+	// 在此时间内,请求还没回来的话,就显示加载中动画,单位ms
+	loadingTime: 800,
+	// 是否在拦截器中返回服务端的原始数据
+	originalData: true,
+	// 展示loading的时候,是否给一个透明的蒙层,防止触摸穿透
+	loadingMask: true,
+	// header:{
+	// 	token:uni.getStorageSync('token')
+	// },
+}
+
+// 此处配置请求拦截器
+export const httpRequest = (config) => {
+	console.log('请求拦截器>>>>>',config);
+	const token = uni.getStorageSync('token');
+	console.log('token>>>>>',token);
+	config.header = {
+		token: token
+	}
+	return config;
+}
+
+// 此处配置响应拦截器
+export const httpResponse = (res) => {
+	return new Promise(function(resolve, reject) {
+		if (res.statusCode == 200) {
+			console.log('成功>>>>');
+			if(res.data.code==0){
+				uni.showToast({
+					icon:'none',
+					title:res.data.msg
+				})
+				return reject(res.data);
+			}
+			if(res.data.code==401){
+				console.log('接口未登录>>>>401');
+				// 未登录
+				uni.removeStorageSync('token');
+				uni.navigateTo({
+					url:'/pages/login/index'
+				})
+				return reject(res.data);
+			}
+			return resolve(res.data);
+		} 
+		if (res.statusCode == 401) {
+			console.log('未登录>>>>');
+			// 未登录
+			uni.navigateTo({
+				url:'/pages/login/index'
+			})
+			uni.removeStorageSync('token');
+			return reject(res.data);
+		}
+		if (res.statusCode == 500) {
+			console.log('服务器异常>>>>500');
+			uni.showToast({
+				title: '服务器异常,请联系客服!',
+				icon: 'none',
+				duration: 3000
+			})
+			return reject(res.data);
+		}
+		
+		return resolve(res.data);
+	})
+}
+
+// http安装方法
+export const installHttpConfig = (Vue, vm) => {
+	Vue.prototype.$u.http.setConfig(UVIEWUI_HTTP_CONFIG);
+	Vue.prototype.$u.http.interceptor.request = httpRequest
+	Vue.prototype.$u.http.interceptor.response = httpResponse
+}

+ 143 - 0
api/custom-request111.js

@@ -0,0 +1,143 @@
+/** 
+ * 自定义封装http请求
+ * 在此处可以自定义其他的请求方法
+ * 直接在main.js中引入挂载即可在页面中使用
+ * 也可以引入到/modules中,使用$u进行api的挂载
+ * 建议基于uniapp的request进行封装,请勿使用其他三方非uniapp请求
+ */
+
+export const VUE_APP_API_URL = ''
+
+export const httpRequest = (opts, data) => {
+	let httpDefaultOpts = {
+		url: baseUrl + opts.url,
+		data: data,
+		method: opts.method,
+		header: opts.method == 'GET' ? {
+			'X-Requested-With': 'XMLHttpRequest',
+			"Accept": "application/json",
+			"Content-Type": "application/json; charset=UTF-8"
+		} : {
+			'X-Requested-With': 'XMLHttpRequest',
+			'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
+		},
+		dataType: 'json',
+	}
+	let promise = new Promise(function(resolve, reject) {
+		uni.request(httpDefaultOpts).then(
+			(res) => {
+				resolve(res[1])
+			}
+		).catch(
+			(response) => {
+				reject(response)
+			}
+		)
+	})
+	return promise
+};
+
+export const httpRequestUploadFile = (opts, filePath, data) => {
+	let token = "";
+	uni.getStorage({
+		key: 'token',
+		success: function(ress) {
+			token = ress.data
+		}
+	});
+	// token="dd0847da213958adf77e88a2cfb661e5"
+	let httpDefaultOpts = {
+		url: baseUrl + opts.url,
+		header: {
+			token: token
+		},
+		name: "file",
+		filePath: filePath,
+		name: 'file',
+		formData: data,
+	}
+	let promise = new Promise(function(resolve, reject) {
+		uni.uploadFile(httpDefaultOpts).then(
+			(res) => {
+				resolve(res[1])
+			}
+		).catch(
+			(response) => {
+				reject(response)
+			}
+		)
+	})
+	return promise
+};
+
+//带Token请求
+export const httpTokenRequest = (opts, data) => {
+	let token = "";
+	uni.getStorage({
+		key: 'token',
+		success: function(ress) {
+			token = ress.data
+		}
+	});
+	// 测试用
+	token = "dd0847da213958adf77e88a2cfb661e5"
+	//此token是登录成功后后台返回保存在storage中的
+	let httpDefaultOpts = {
+		url: baseUrl + opts.url,
+		data: data,
+		method: opts.method,
+		header: opts.method == 'GET' ? {
+			'Token': token,
+			'X-Requested-With': 'XMLHttpRequest',
+			"Accept": "application/json",
+			"Content-Type": "application/json; charset=UTF-8"
+		} : {
+			'Token': token,
+			'X-Requested-With': 'XMLHttpRequest',
+			'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
+		},
+		dataType: 'json',
+	}
+	let promise = new Promise(function(resolve, reject) {
+		uni.request(httpDefaultOpts).then(
+			(res) => {
+				resolve(res[1])
+			}
+		).catch(
+			(response) => {
+				reject(response)
+			}
+		)
+	})
+	return promise
+};
+
+//自定义请求
+export const httpCustomeRequest = (opts, data) => {
+	let httpDefaultOpts = {
+		url: opts.url,
+		data: data,
+		method: opts.method,
+		header: opts.method == 'GET' ? {
+			'X-Requested-With': 'XMLHttpRequest',
+			"Accept": "application/json",
+			"Content-Type": "application/json; charset=UTF-8"
+		} : {
+			'X-Requested-With': 'XMLHttpRequest',
+			'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
+		},
+		dataType: 'json',
+	}
+	let promise = new Promise(function(resolve, reject) {
+		uni.request(httpDefaultOpts).then(
+			(res) => {
+				resolve(res[1])
+			}
+		).catch(
+			(response) => {
+				reject(response)
+			}
+		)
+	})
+	return promise
+};

+ 19 - 0
api/index.js

@@ -0,0 +1,19 @@
+// 用户模块api
+import {useWxApi} from './modules/user.js'
+
+// 商城模块api
+import {useMallApi} from './modules/mall.js'
+// 其他公用api
+import {useOtherApi} from './modules/other.js'
+
+
+
+// 导出api全局安装方法
+export const installApiModules = (Vue, vm) => {
+	// , useFileApi(Vue, vm), useMallApi(Vue, vm), useCommunityApi(Vue, vm)
+	vm.$u.api = Object.assign(
+	useWxApi(Vue, vm),
+	useMallApi(Vue, vm),
+	useOtherApi(Vue, vm)
+	)
+}

+ 157 - 0
api/modules/mall.js

@@ -0,0 +1,157 @@
+export const useMallApi = (Vue, vm) => {
+	return {
+		// 查询商城轮播图
+		getMallSwiperAjax: () => vm.$u.post('/client/index/getBaseElementList',{element_type:1}),
+		// 2=按钮组(金刚区)
+		getMallMenuAjax: () => vm.$u.post('/client/index/getBaseElementList',{element_type:2}),
+		// 三图接口
+		getSelfElementListAjax: () => vm.$u.post('/client/index/getSelfElementList'),
+		// 查询分类
+		getCateListAjax: () => vm.$u.post('/client/index/getCateList'),
+				
+		// 获取购物车数量
+		getCartNumsAjax: () => vm.$u.post('/client/shop_cart/getCartNums'),
+		/**商品列表
+		 * id	否	string	商品id
+		 * title	否	string	书名
+		 * cate_id	否	string	分类列表中的id 不是分类列表中的cate_id
+		 * tags_type 是	string	标签类型: default=默认,hot=热门,recommend=推荐
+		 * price_sc 否	string	desc=降序 asc=升序 (不传默认综合排序)
+		 * sales_sc否	string	desc=降序 asc=升序 (不传默认综合排序)
+		 * page	否	int	页码
+		 **/
+		getGoodsListAjax: (json) => vm.$u.post('/client/shopGoods/getList',json),
+/* 		// 热门商品,便于维护,分开来写,同商品列表接口,tags_type字段传 host
+		getHotListAjax: (json) => vm.$u.post('/client/shopGoods/getList',{
+			...json,
+			tags_type:'hot',
+		}),
+		// 推荐商品,便于维护,分开来写,同商品列表接口,tags_type字段传 host
+		getRecommendListAjax: (json) => vm.$u.post('/client/shopGoods/getList',{
+			...json,
+			tags_type:'recommend',
+		}), */
+		
+		// 商品详情
+		getGoodsDetailAjax: (id) => vm.$u.post('/client/shopGoods/getGoodsDetail',{id:id}),
+		
+		// 提交订单
+		submitOrderAjax: (json) => vm.$u.post('/client/shop_order/submitOrder',json),
+		
+		// 发起支付
+		payAjax: (order_no) => vm.$u.post('/client/pay/pay',{order_no:order_no}),
+		
+		// 加入购物车
+		addCartAjax: (json) => vm.$u.post('/client/shop_cart/addCart',json),
+		
+		// 购物车列表
+		getCartListAjax: (page) => vm.$u.post('/client/shop_cart/getCartList',{page:page}),
+		
+		// 变更购物车数量
+		updateCartNumsAjax: (json) => vm.$u.post('/client/shop_cart/updateCartNums',json),
+		
+		// 删除购物车商品
+		delCartGoodsAjax: (ids) => vm.$u.post('/client/shop_cart/delCartGoods',{cart_ids:ids}),
+		
+		/**
+		 * 提交购物车订单
+		 * address_id	地址id
+		 * cart_ids	购物车id串
+		 * remark	备注
+		 * user_coupon_id 用户优惠券id
+		 **/		
+		submitCartOrderAjax: (json) => vm.$u.post('/client/shop_order/submitCartOrder',json),
+		
+		/** 
+		 * 订单列表
+		 * status string	订单状态  不传则输出所有订单,10=待付款, 11=待发货, 12=待收货, 13=已完成,
+		 * page	int 页码
+		 **/		
+		getOrderListAjax: ({status,page}) => vm.$u.post('/client/shop_order/getOrderList',{status,page}),
+		
+		
+		/**
+		 * 订单详情
+		 * order_id int	订单id
+		 **/		
+		getOrderDetailAjax: (order_id) => vm.$u.post('/client/shop_order/getOrderDetail',{order_id:order_id}),
+		
+		/**
+		 * 取消订单
+		 * order_id int	订单id
+		 **/		
+		cancelOrderAjax: (order_id) => vm.$u.post('/client/shop_order/cancelOrder',{order_id:order_id}),
+		
+		/**
+		 * 申请售后
+		 * order_id int	订单id
+		 * order_detail_ids string	订单详情id串 用逗号隔开  1,2
+		 * refund_type int	售后类型:1=仅退款,2=退货
+		 * reason string	退款原因
+		 */		
+		refundOrderAjax: (json) => vm.$u.post('/client/shop_order/refundOrder',json),
+		
+		/**
+		 * 设置退货快递单号
+		 * order_detail_id int	订单详情id
+		 * expressCode string	快递单号
+		 **/		
+		setExpressCodeAjax: (json) => vm.$u.post('/client/shop_order/setExpressCode',json),
+
+		/**
+		 * 退款/售后订单列表
+		 * status string	订单状态: 不传则输出所有订单 10=待付款, 11=待发货, 12=待收货, 13=已完成,
+		 * page	int 页码
+		 * 
+		 *  返回事例: 子订单状态sub_status:
+		 * 10=正常(非售后订单),20=仅退款,21=卖家同意,22=卖家驳回,30=退货退款,
+		 * 31=卖家同意(用户填写退货单号),32=卖家驳回,33=等待卖家收货确认中,
+		 * 34=卖家确认收货,验货没问题金额已原路退回
+		 **/
+		getRefundOrderListAjax: (json) => vm.$u.post('/client/shop_order/getRefundOrderList',json),
+
+		/**
+		 * 查看物流
+		 * order_id int 订单id
+		 **/
+		getExpressDetailAjax: (order_id) => vm.$u.post('/client/shop_order/getExpressDetail',{order_id:order_id}),
+		
+		
+		/**确认收货 
+		 *		 order_id	是	int	订单id
+				order_detail_ids	是	string	确认收货的订单详情id串 多个用逗号隔开
+		 */
+		
+		orderConfirmAjax: (json) => vm.$u.post('/client/shop_order/orderConfirm',json),
+		
+		
+		
+		/**
+		 * 收藏商品列表 
+		 */
+		
+		collectionListAjax: (json) => vm.$u.post('/client/shopGoods/getCollectionList'),
+		
+		/**
+		 * 收藏/取消收藏商品列表 
+		 */
+		
+		collectonAjax: (id) => vm.$u.post('/client/shopGoods/collection',{goods_id:id}),
+		
+		/**
+		 * 批量删除收藏
+		 * ids :收藏列表id串,多个用逗号隔开
+		 **/
+		delCollectionAjax: (ids) => vm.$u.post('/client/shopGoods/delCollection',{ids:ids}),
+		
+		
+		/**
+		 * 售后详情
+		 * id :退款售后列表中的id
+		 **/
+		getRefundOrderDetailAjax: (id) => vm.$u.post('/client/shopOrder/getRefundOrderDetail',{id:id}),
+		
+		
+		
+	}
+}

+ 13 - 0
api/modules/other.js

@@ -0,0 +1,13 @@
+export const useOtherApi = (Vue, vm) => {
+	return {
+		// 获取地区列表
+		getRegionListAjax: () => vm.$u.post('/client/shop_address/getRegionList'),
+		// 获取平台基础配置信息
+		getBaseInfoAjax: () => vm.$u.post('/client/index/getBaseConfigs'),
+		// 获取文章详情
+		getArticleListAjax: (json) => vm.$u.post('/client/index/getArticleList',json),
+		// 获取快递类型列表
+		getExpressListAjax: () => vm.$u.post('/client/index/getExpressList'),
+		
+	}
+}

+ 32 - 0
api/modules/user.js

@@ -0,0 +1,32 @@
+export const useWxApi = (Vue, vm) => {
+	return {
+		// 登录
+		loginAjax: (json) => vm.$u.post('/client/userCenter/login', json),
+		// 用户信息
+		getUserBaseInfoAjax: () => vm.$u.post('/client/userCenter/baseInfo'),
+		// 设置用户信息
+		setUserBaseInfoAjax: (json) => vm.$u.post('/client/userCenter/setBaseInfo',json),
+		// 获取用户优惠券列表
+		getUserCouponListAjax: (page) => vm.$u.post('/client/userCenter/getUserCouponList',{page:page}),
+		
+		// 获取地区列表
+		// getRegionListAjax: () => vm.$u.post('/client/shop_address/getRegionList'),
+		// 获取默认收货地址
+		getDefaultAddressAjax: () => vm.$u.post('/client/shop_address/getDefaultAddress'),
+		// 删除收货地址
+		delAddressAjax: (id) => vm.$u.post('/client/shop_address/delAddress',{id:id}),
+		// 获取地址详情
+		getAddressDetailAjax: (id) => vm.$u.post('/client/shop_address/getAddressById',{id:id}),		
+		// 获取收货地址列表
+		getAddressListAjax: () => vm.$u.post('/client/shop_address/getAddressList'),
+		// 新增/编辑用户地址
+		updateAddressAjax: (json) => vm.$u.post('/client/shop_address/editAddress',json),
+		// 设置默认收货地址
+		setDefaultAddressAjax: (id) => vm.$u.post('/client/shop_address/setDefaultAddress',{id:id}),
+		
+		
+		// 设置默认收货地址
+		addFeedbackAjax: (model) => vm.$u.post('/client/userCenter/addFeedback',model),
+		
+	}
+}

+ 92 - 0
api/upload.js

@@ -0,0 +1,92 @@
+import ENV_CONFIG from '@/.env.js'
+
+// api前缀
+export const HTTP_URL_DEV_PREFIX = '/api'
+export const HTTP_URL_PROD_PREFIX = '/api'
+const env = ENV_CONFIG[process.env.ENV_TYPE];
+// 基于uview-ui的http配置
+export const UVIEWUI_HTTP_CONFIG = {
+	// 地址
+	baseUrl: env.apiUrl+env.apiUrlPrefix,
+	// 请求方式
+	method: 'post',
+	// 参数类型
+	dataType: 'json',
+	// 是否显示请求中的loading
+	showLoading: true,
+	// 请求loading中的文字提示
+	loadingText: '请求中...',
+	// 在此时间内,请求还没回来的话,就显示加载中动画,单位ms
+	loadingTime: 800,
+	// 是否在拦截器中返回服务端的原始数据
+	originalData: true,
+	// 展示loading的时候,是否给一个透明的蒙层,防止触摸穿透
+	loadingMask: true,
+	// header:{
+	// 	token:uni.getStorageSync('token')
+	// },
+}
+
+// 此处配置请求拦截器
+export const httpRequest = (config) => {
+	console.log('请求拦截器>>>>>',config);
+	const token = uni.getStorageSync('token');
+	console.log('token>>>>>',token);
+	config.header = {
+		token: token
+	}
+	return config;
+}
+
+// 此处配置响应拦截器
+export const httpResponse = (res) => {
+	return new Promise(function(resolve, reject) {
+		if (res.statusCode == 200) {
+			console.log('成功>>>>');
+			if(res.data.code==0){
+				uni.showToast({
+					icon:'none',
+					title:res.data.msg
+				})
+				return reject(res.data);
+			}
+			if(res.data.code==401){
+				console.log('接口未登录>>>>401');
+				// 未登录
+				uni.removeStorageSync('token');
+				uni.navigateTo({
+					url:'/pages/login/index'
+				})
+				return reject(res.data);
+			}
+			return resolve(res.data);
+		} 
+		if (res.statusCode == 401) {
+			console.log('未登录>>>>');
+			// 未登录
+			uni.navigateTo({
+				url:'/pages/login/index'
+			})
+			uni.removeStorageSync('token');
+			return reject(res.data);
+		}
+		if (res.statusCode == 500) {
+			console.log('服务器异常>>>>500');
+			uni.showToast({
+				title: '服务器异常,请联系客服!',
+				icon: 'none',
+				duration: 3000
+			})
+			return reject(res.data);
+		}
+		
+		return resolve(res.data);
+	})
+}
+
+// http安装方法
+export const installHttpConfig = (Vue, vm) => {
+	Vue.prototype.$u.http.setConfig(UVIEWUI_HTTP_CONFIG);
+	Vue.prototype.$u.http.interceptor.request = httpRequest
+	Vue.prototype.$u.http.interceptor.response = httpResponse
+}

+ 91 - 0
components/add-btn-fixed 111.vue

@@ -0,0 +1,91 @@
+<template>
+	<view class="slot" @touchmove="buttonMove" @touchstart="buttonStart" @touchend="buttonEnd" :style="{ top: buttonTop + 'px', left: buttonLeft + 'px' }">
+		<view class="btn" @click="click"><u-icon name="plus" size="54"></u-icon></view>
+	</view>
+</template>
+
+<script>
+var startPoint;
+export default {
+	data() {
+		return {
+			buttonTop: 0,
+			buttonLeft: 0,
+			windowHeight: '',
+			windowWidth: ''
+		};
+	},
+	mounted() {
+		let $this = this;
+		uni.getSystemInfo({
+			success(res) {
+				// 高度,宽度 单位为px
+				$this.windowHeight = res.windowHeight;
+				$this.windowWidth = res.windowWidth;
+				//这里定义按钮的初始位置
+				$this.buttonTop = res.windowHeight * 0.8;
+				//这里定义按钮的初始位置
+				$this.buttonLeft = res.windowWidth * 0.8;
+			}
+		});
+	},
+	methods: {
+		// 点击事件
+		click() {
+			this.$emit('click');
+		},
+		//以下是按钮拖动事件
+		buttonStart: function(e) {
+			//获取拖动开始点
+			startPoint = e.touches[0];
+		},
+		buttonMove: function(e) {
+			//获取拖动结束点
+			var endPoint = e.touches[e.touches.length - 1];
+			//计算在X轴上拖动的距离和在Y轴上拖动的距离
+			var translateX = endPoint.clientX - startPoint.clientX;
+			var translateY = endPoint.clientY - startPoint.clientY;
+			startPoint = endPoint; //重置开始位置
+			var buttonTop = this.buttonTop + translateY;
+			var buttonLeft = this.buttonLeft + translateX;
+			//判断是移动否超出屏幕
+			if (buttonLeft + 50 >= this.windowWidth) {
+				buttonLeft = this.windowWidth - 50;
+			}
+			if (buttonLeft <= 0) {
+				buttonLeft = 0;
+			}
+			if (buttonTop <= 0) {
+				buttonTop = 0;
+			}
+			if (buttonTop + 50 >= this.windowHeight) {
+				buttonTop = this.windowHeight - 50;
+			}
+			this.buttonTop = buttonTop;
+			this.buttonLeft = buttonLeft;
+		},
+		buttonEnd: function(e) {}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	position: fixed;
+	z-index: $app-zIndex-fixed;
+	box-shadow: $app-theme-shadow;
+	overflow: hidden;
+	border-radius: 50%;
+	.btn {
+		background-color: $app-theme-color;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		width: 100rpx;
+		height: 100rpx;
+		border-radius: 50%;
+		color: $app-theme-text-white-color;
+		overflow: hidden;
+	}
+}
+</style>

+ 70 - 0
components/add-popup.vue

@@ -0,0 +1,70 @@
+<template>
+	<u-popup v-model="show" mask mode="bottom" mask-close-able closeable border-radius="16" @close="close">
+		<view class="inner">
+			<view class="item" v-for="(item, index) in ops" :style="{ width: 100 / ops.length + '%' }" @click="chose(item, index)">
+				<u-image width="100rpx" height="100rpx" :src="item.icon"></u-image>
+				<view class="label">{{ item.label }}</view>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+export default {
+	props: {
+		// 配置项
+		ops: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		}
+	},
+	data() {
+		return {
+			show: false
+		};
+	},
+	methods: {
+		// 打开popup
+		open() {
+			this.show = true;
+		},
+		// 关闭
+		close() {
+			this.$emit('close');
+		},
+		// 选择
+		chose(item, index) {
+			if (item.type == 'page') {
+				uni.navigateTo({
+					url: item.url
+				});
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.inner {
+	padding: 100rpx 30rpx 80rpx 30rpx;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+
+	.item {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		flex-wrap: wrap;
+		.label {
+			margin-top: 16rpx;
+			width: 100%;
+			text-align: center;
+			font-size: 26rpx;
+			color: $app-theme-text-black-color;
+		}
+	}
+}
+</style>

+ 50 - 0
components/img-swiper.vue

@@ -0,0 +1,50 @@
+<template>
+	<view class="slot">
+		<u-swiper height="750" border-radius="0" indicator-pos="bottomRight" mode="number" img-mode="aspectFit" :list="list">
+		</u-swiper>
+		
+		<button class="shareBtn" open-type="share">
+			<u-icon size="36" name="zhuanfa"></u-icon>
+		</button>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'goods-img-swiper',
+	props: {
+		list: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		},
+	}
+};
+</script>
+
+<style scoped lang="scss">
+	.slot{
+		position: relative;
+	}
+	.shareBtn{
+		width: 80rpx;
+		height: 80rpx;
+		border-radius: 50%;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		flex-direction: column;
+		background-color: rgba(0,0,0,.6);
+		color: #fff;
+		position: absolute;
+		right: 10rpx;
+		top: 30rpx;
+		z-index: $app-zIndex-normal;
+		padding: 0;
+		&::after{
+			content: '';
+			border:none;
+		}
+	}
+</style>

+ 36 - 0
components/loadmore.vue

@@ -0,0 +1,36 @@
+<template>
+	<view class="loadmore">
+		<u-loadmore :status="loadtype.finish?'nomore':loadtype.loading?'loading':'loadmore'" :load-text="loadText" color="#aaa"/>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'no-data',
+	props: {
+		// 类型,缺省图片名称,路径:/static/nodata/...
+		loadtype: {
+			type: Object,
+			default: {}
+		},
+	},
+	data() {
+		return {
+			loadText: {
+				loadmore: '轻轻上拉',
+				loading: ' ',
+				nomore: '没有更多了'
+			},
+		};
+	},
+	methods: {
+		
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.loadmore{
+	padding: 30rpx;
+}
+</style>

+ 143 - 0
components/nav/dial-nav.vue

@@ -0,0 +1,143 @@
+<template>
+	<view class="slot" :class="{ shadow }">
+		<view class="nav-navigation">
+			<!-- 此处插槽用于扩展是否显示标题等 -->
+			<slot></slot>
+			<view class="nav-list" v-if="mode == 8 || mode == 4">
+				<view
+					class="nav-item"
+					style="width: 25%;"
+					:style="[{ marginTop: index > 3 ? marginTopLine : '' }]"
+					v-for="(item, index) in list"
+					:key="index"
+					@click="goNav(item)"
+				>
+					<view class="list-img"><u-image :width="imgSize" :height="imgSize" mode="widthFix" :src="item[imgName]"></u-image></view>
+					<view class="list-text" :style="{ fontSize: nameSize }">{{ item[labelName] }}</view>
+				</view>
+			</view>
+			<view class="nav-list" v-if="mode == 5||mode == 10">
+				<view
+					class="nav-item"
+					style="width: 20%;"
+					:style="[{ marginTop: index > 4 ? marginTopLine : '' }]"
+					v-for="(item, index) in list"
+					:key="index"
+					@click="goNav(item)"
+				>
+					<view class="list-img"><u-image :width="imgSize" :height="imgSize" mode="widthFix" :src="item[imgName]"></u-image></view>
+					<view class="list-text" :style="{ fontSize: nameSize }">{{ item[labelName] }}</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { goType } from '../../utils/pub';
+export default {
+	name: 'dial-nav',
+	props: {
+		// 显示模式
+		mode: {
+			type: Number,
+			default: 8
+		},
+		// 图片的别名
+		imgName: {
+			type: String,
+			default: 'img'
+		},
+		// 名称的别名
+		labelName: {
+			type: String,
+			default: 'name'
+		},
+		// 跳转路径的别名
+		urlName: {
+			type: String,
+			default: 'url'
+		},
+		// 配置项
+		list: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		},
+		// 是否显示阴影
+		shadow: {
+			type: Boolean,
+			default: false
+		},
+		// 图标大小
+		imgSize: {
+			type: String,
+			default: '84rpx'
+		},
+		// 名称的大小
+		nameSize: {
+			type: String,
+			default: '28rpx'
+		},
+		// 两行之间的距离(仅在8,10中)
+		marginTopLine: {
+			type: String,
+			default: '30rpx'
+		}
+	},
+	methods:{
+		goNav(e){
+			if(e.url){
+				this.$u.route({url:e[this.urlName]});
+				return ;
+			}
+			goType(e);
+		},
+	},
+
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	border-radius: 16rpx;
+	background-color: $app-theme-bg-color;
+	overflow: hidden;
+	&.shadow {
+		box-shadow: $app-theme-shadow;
+	}
+	.nav-navigation {
+		width: 100%;
+
+		.nav-list {
+			display: flex;
+			flex-wrap: wrap;
+			justify-content: flex-start;
+			padding-top: 30rpx;
+			padding-bottom: 30rpx;
+
+			.nav-item {
+				display: flex;
+				flex-wrap: wrap;
+				justify-content: center;
+				align-items: center;
+
+				.list-img {
+					width: 100%;
+					display: flex;
+					justify-content: center;
+					align-items: center;
+					margin-bottom: 16rpx;
+				}
+
+				.list-text {
+					text-align: center;
+					font-weight: 400;
+					color: $app-theme-text-black-color;
+				}
+			}
+		}
+	}
+}
+</style>

+ 129 - 0
components/nav/horizontal-scroll-nav.vue

@@ -0,0 +1,129 @@
+<template>
+	<view class="slot">
+		<scroll-view class="scroll" scroll-x="true">
+			<view class="item" v-for="(item, index) in list" :key="index">
+				<view class="title">
+					<image src="../../static/bg/bg-community.png"></image>
+					<text>{{ item.text }}</text>
+				</view>
+				<view class="info">
+					<image src="../../static/bg/bg-user.png"></image>
+					<view class="num">
+						<text>{{ item.num }}</text>
+						<text>人参与</text>
+					</view>
+				</view>
+				<u-image class="bg" width="360rpx" height="190rpx" :src="item.bg"></u-image>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'horizontal-scroll-nav',
+	props: {
+		// 列表
+		list: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	height: auto;
+	width: 100%;
+	.scroll {
+		width: 100%;
+		white-space: nowrap;
+	}
+	.item {
+		display: inline-block;
+		width: 360rpx;
+		height: 190rpx;
+		margin-right: 30rpx;
+		padding: 24rpx;
+		position: relative;
+		border-radius: 12rpx;
+		overflow: hidden;
+		&:nth-child(1) {
+			margin-left: 30rpx;
+		}
+		.bg {
+			position: absolute;
+			top: 0;
+			left: 0;
+			border-radius: 12rpx;
+			overflow: hidden;
+			z-index: 19;
+		}
+		.title {
+			width: calc(100% - 48rpx);
+			position: absolute;
+			left: 24rpx;
+			top: 24rpx;
+			z-index: 39;
+
+			image {
+				vertical-align: middle;
+				padding-right: 4rpx;
+				width: 32rpx;
+				height: 32rpx;
+			}
+
+			text {
+				vertical-align: middle;
+				font-size: 28rpx;
+				font-weight: 500;
+				color: #ffffff;
+				line-height: 44rpx;
+				white-space: pre-wrap;
+			}
+		}
+
+		.info {
+			display: flex;
+			align-items: center;
+			position: absolute;
+			left: 24rpx;
+			bottom: 24rpx;
+			z-index: 39;
+			image {
+				width: 26rpx;
+				height: 24rpx;
+				margin-right: 8rpx;
+				vertical-align: middle;
+			}
+
+			.num {
+				vertical-align: middle;
+				font-size: 20rpx;
+				font-weight: 500;
+				color: #ffffff;
+				line-height: 44rpx;
+
+				text:nth-child(1) {
+					vertical-align: middle;
+					font-weight: bold;
+					color: #f6f6f6;
+					font-size: 28rpx;
+					line-height: 2;
+				}
+
+				text:nth-child(2) {
+					vertical-align: bottom;
+					font-weight: bold;
+					color: #f6f6f6;
+					font-size: 20rpx;
+					line-height: 2.6;
+				}
+			}
+		}
+	}
+}
+</style>

+ 63 - 0
components/nav/icon-label-nav.vue

@@ -0,0 +1,63 @@
+<template>
+	<view class="slot">
+		<view class="tab" v-for="(item, index) in ops" :key="index" :class="{ active: currentTab == index }" @click="changeTab(item, index)">
+			<view class="icon">
+				<u-image width="56rpx" height="56rpx" :src="item.iconActive" v-show="currentTab == index"></u-image>
+				<u-image width="56rpx" height="56rpx" :src="item.icon" v-show="currentTab != index"></u-image>
+			</view>
+			<view class="label">{{ item.label }}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		ops: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		}
+	},
+	data() {
+		return {
+			currentTab: 0
+		};
+	},
+	methods: {
+		// 切换tab
+		changeTab(item, index) {
+			this.currentTab = index
+			this.$emit('change', { item, index });
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	display: flex;
+	justify-content: space-around;
+	align-items: center;
+	background-color: $app-theme-bg-color;
+	height: 150rpx;
+	.tab {
+		.icon {
+			display: flex;
+			justify-content: center;
+			margin-bottom: 14rpx;
+		}
+		.label {
+			font-size: 26rpx;
+			color: $app-theme-card-gray-deep-color;
+		}
+
+		&.active {
+			.label {
+				color: $app-theme-text-black-color;
+			}
+		}
+	}
+}
+</style>

+ 50 - 0
components/nav/label-count.vue

@@ -0,0 +1,50 @@
+<template>
+	<view class="slot">
+		<view class="item" :style="{ width: 100 / ops.length + '%' }" v-for="(item, index) in ops" :key="index" @click="$u.route({ url: item.url })">
+			<view class="count">{{ item.count || 0 }}</view>
+			<view class="label">{{ item.label || '未命名' }}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'label-count',
+	props: {
+		// 配置项
+		ops: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	background-color: $app-theme-bg-color;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	border-radius: 16rpx;
+	box-shadow: $app-theme-shadow;
+	padding: 40rpx 0;
+	.item {
+		text-align: center;
+		.count {
+			font-size: 40rpx;
+			font-family: DINAlternate-Bold, DINAlternate;
+			font-weight: bold;
+			color: $app-theme-text-black-color;
+			margin-bottom: 16rpx;
+		}
+		.label {
+			font-size: 24rpx;
+			font-weight: 400;
+			color: $app-theme-text-gray-color;
+		}
+	}
+}
+</style>

+ 162 - 0
components/navbar/navbar-city-search.vue

@@ -0,0 +1,162 @@
+<template>
+	<view class="slot">
+		<view class="navbar" :style="[{ minHeight: navBarHeight + 'px' }]">
+			<view
+				class="inner"
+				:style="[
+					{ minHeight: menuHeight + 'px' },
+					{ lineHeight: menuHeight + 'px' },
+					{ paddingLeft: menuRight * 2 + 'px' },
+					{ paddingRight: menuRight * 2 + 'px' },
+					{ paddingTop: navBarHeight - menuHeight - menuTop + 'px' },
+					{ paddingBottom: '20rpx' }
+				]"
+			>
+				<view class="loaction-slot">
+					<view class="loaction-title" v-if="locationStatus == 1"><text>正在获取地理位置...</text></view>
+					<view class="loaction-title" v-if="locationStatus == 2" @click="goCitySelectPage">
+						<text>{{ cityListSelected ? cityListSelected : locationCity }}</text>
+						<u-icon style="margin-left: 8rpx;" :size="18" color="#171717" name="arrow-down-fill"></u-icon>
+					</view>
+					<view class="loaction-title" v-if="locationStatus == 3" @click="getLocation(true)"><text>地理位置获取失败,点击重试</text></view>
+				</view>
+				<view class="search-slot" :style="{ paddingTop: menuTop + 'px' }">
+					<u-search @click="goSearchPage" disabled :placeholder="placeholder" :showAction="false" shape="square" borderRadius="0rpx" bg-color="#F4F5F8"></u-search>
+				</view>
+			</view>
+		</view>
+		<view class="slot-height" :style="[{ height: navBarHeight + menuHeight + menuTop + 11 + 'px' }]"></view>
+	</view>
+</template>
+
+<script>
+import { getLocation, getLocationAgain } from '@/utils/location.js';
+const app = getApp();
+export default {
+	name: 'navbar-city-search',
+	props: {
+		// 标题
+		title: {
+			type: String,
+			default: '标题'
+		},
+		// 占位内容
+		placeholder: {
+			type: String,
+			default: '请输入'
+		},
+		// 已经选择的城市
+		cityListSelected: {
+			type: String,
+			default: ''
+		}
+	},
+	data() {
+		return {
+			// 导航栏高度
+			menuTop: app.globalData.menuTop,
+			navBarHeight: app.globalData.navBarHeight,
+			menuRight: app.globalData.menuRight,
+			menuBotton: app.globalData.menuBotton,
+			menuHeight: app.globalData.menuHeight,
+			statusBarHeight: app.globalData.statusBarHeight,
+			// 定位数据
+			locationData: {},
+			// 当前城市
+			locationCity: '',
+			// 获取地理位置状态,1正在获取,2获取成功,3失败
+			locationStatus: 1
+		};
+	},
+	mounted() {
+		if (this.cityListSelected) {
+			this.locationStatus = 2;
+		} else {
+			this.getLocation();
+		}
+	},
+	methods: {
+		// 跳转城市选择页面
+		goCitySelectPage() {
+			uni.navigateTo({
+				url: '/pages/home/city-list?nowCity=' + this.locationCity
+			});
+		},
+
+		// 去搜索页面
+		goSearchPage() {
+			uni.navigateTo({
+				url: '/pages/search'
+			});
+		},
+
+		// 获取地理位置
+		async getLocation(isAgain = false) {
+			this.locationStatus = 1;
+			if (!isAgain) {
+				getLocation(res => {
+					this.setLocationData(res);
+				});
+			} else {
+				getLocationAgain(res => {
+					this.setLocationData(res);
+				});
+			}
+		},
+
+		// 设置定位数据
+		setLocationData(res) {
+			if (res.status) {
+				this.locationStatus = 2;
+				this.locationData = res.data;
+				this.locationCity = res.data.ad_info.city;
+				this.$store.commit('user/COMMIT_LOACTION_INFO', res.data);
+			} else {
+				this.locationStatus = 3;
+				uni.showToast({
+					title: '获取地理位置失败',
+					duration: 3000,
+					icon: 'none'
+				});
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	width: 100vw;
+}
+.navbar {
+	width: 100%;
+	position: fixed;
+	top: 0;
+	left: 0;
+	z-index: 899;
+	overflow: hidden;
+}
+
+.inner {
+	width: 100%;
+	height: 100%;
+	background-color: $app-theme-bg-color;
+}
+
+.loaction-slot {
+	display: flex;
+	align-items: center;
+	.loaction-title {
+		display: flex;
+		align-items: center;
+		text {
+			font-size: 32rpx;
+			font-family: PingFang SC;
+			font-weight: 400;
+			color: $app-theme-text-black-color;
+		}
+	}
+}
+.search-slot {
+}
+</style>

+ 181 - 0
components/navbar/navbar-search.vue

@@ -0,0 +1,181 @@
+<template>
+	<view class="slot">
+		<view class="toph">
+			<u-search
+				:placeholder="placeholder"
+				:value="value"
+				height="70"
+				@search="searchConfirm"
+				@custom="searchConfirm"
+			></u-search>
+			<view class="goodsSort">
+				<view class="item" :class="{'active':!sortTypes.price_sc && !sortTypes.sales_sc}" @click="changeSort(1)">
+					<text>综合</text>
+				</view>
+				<view class="item" :class="{'active':['asc','desc'].includes(sortTypes.price_sc)}" @click="changeSort(2)">
+					<text>价格</text>
+					<view :class="['sortIcon',sortTypes.price_sc]"></view>
+				</view>
+				<view class="item" :class="{'active':sortTypes.sales_sc=='desc'}" @click="changeSort(3)">
+					<text>销量</text>
+				</view>
+			</view>
+		</view>
+		<view class="siteBar"></view>
+	</view>
+</template>
+
+<script>
+const app = getApp();
+export default {
+	name: 'navbar-search',
+	props: {
+		title: {
+			type: String,
+			default: '搜索'
+		},
+		value: {
+			type: String,
+			default: ''
+		},
+		// 占位内容
+		placeholder: {
+			type: String,
+			default: '请输入'
+		},
+	},
+	data() {
+		return {
+			// 导航栏高度
+			navBarHeight: app.globalData.navBarHeight,
+			statusBarHeight: app.globalData.statusBarHeight,
+			menuRight: app.globalData.menuRight,
+			menuBotton: app.globalData.menuBotton,
+			menuHeight: app.globalData.menuHeight,
+			menuTop: app.globalData.menuTop,
+			menuWidth: app.globalData.menuWidth,
+			// 背景色
+			searchBgColor: this.$appTheme.appThemeSearchBgColor,
+			// 排序类型
+			sortTypes:{
+				price_sc:'',//desc 降序,asc升序
+				sales_sc:'',//desc 降序,asc升序
+			},
+			/* // priceSort: [{
+			// 		label: '默认排序',
+			// 		value: 1,
+			// 	},
+			// 	{
+			// 		label: '距离优先',
+			// 		value: 2,
+			// 	},
+			// 	{
+			// 		label: '价格优先',
+			// 		value: 3,
+			// 	}
+			// ],
+			// saleSort: [{
+			// 		label: '去冰',
+			// 		value: 1,
+			// 	},
+			// 	{
+			// 		label: '加冰',
+			// 		value: 2,
+			// 	},
+			// ], */
+		};
+	},
+	
+
+	methods: {		
+		// 搜索
+		searchConfirm(e) {
+			this.$emit('confirm', e);
+		},
+		changeSort(e){
+			switch (e){
+				case 1:
+					// 综合
+					this.sortTypes = {
+						price_sc:'',
+						sales_sc:'',
+					}
+					break;
+				case 2:
+					// 价格
+					this.sortTypes.price_sc = this.sortTypes.price_sc=='desc'?'asc':'desc';
+					this.sortTypes.sales_sc = '';
+					break;
+				case 3:
+				// 销量
+					this.sortTypes.sales_sc = 'desc';
+					this.sortTypes.price_sc = '';
+					break;
+				default:
+					break;
+			}
+			// this.sortType = e;
+			this.$emit('sortType', this.sortTypes);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.siteBar{
+	height: 140rpx;
+}
+.toph {
+	width: 100%;
+	position: fixed;
+	top: 0;
+	left: 0;
+	z-index: 899;
+	overflow: hidden;
+	background-color: $app-theme-bg-color;
+	box-sizing: border-box;
+	padding: 0 30rpx;
+}
+
+.goodsSort{
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	padding: 0 30rpx;
+	height: 80rpx;
+	box-sizing: content-box;
+	border-bottom: 1rpx solid $app-theme-border-color;
+	.item{
+		position: relative;
+		text-align: center;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		color: $app-theme-text-gray-color;
+		text{
+			font-size: 30rpx;
+		}
+		.sortIcon{
+			width: 18rpx;
+			height: 30rpx;
+			background-image: url('../../static/icon/sort.png');
+			background-position: 0 0;
+			background-size: auto 100%;
+			margin-left: 10rpx;
+			&.asc{
+				background-position: 42rpx 0;
+			}
+			&.desc{
+				background-position:20rpx 0;
+			}
+		}
+		&.active{
+			color: $app-theme-color;
+		}
+		&.priceAsc{
+			
+		}
+	}
+}
+
+</style>

+ 228 - 0
components/navbar/navbar-tab-search.vue

@@ -0,0 +1,228 @@
+<template>
+	<view class="slot">
+		<view class="slot-inner" :style="[showSearch ? { minHeight: navBarHeight + 'px' } : { height: navBarHeight + 'px' }]">
+			<view
+				v-if="showSearch"
+				class="inner-showSearch"
+				:style="[
+					{ minHeight: menuHeight + 'px' },
+					{ lineHeight: menuHeight + 'px' },
+					{ paddingLeft: menuRight * 2 + 'px' },
+					{ paddingRight: menuRight * 2 + 'px' },
+					{ paddingTop: navBarHeight - menuHeight - menuTop + 'px' },
+					{ paddingBottom: '20rpx' }
+				]"
+			>
+				<view class="page_title"
+					:style="[
+						{ minHeight: menuHeight + 'px' },
+						{ lineHeight: menuHeight + 'px' },
+					]"
+				>
+					{{pageTitle}}
+				</view>
+				<view class="search-slot" :style="{ paddingTop: '16px' }">
+					<!-- shape="square" 
+						borderRadius="0rpx"-->
+					<u-search
+						class="search-components"
+						@click="goSearchPage(url)"
+						disabled
+						:placeholder="placeholder"
+						:showAction="false"
+						:bg-color="searchBgColor"
+						:class="showSlot ? 'active' : ''"
+					></u-search>
+					<slot showSlot></slot>
+				</view>
+			</view>
+			<view class="inner" v-else>
+				<view
+					@click="$u.route({ type: 'navigateBack', delta: 1 })"
+					class="left"
+					v-if="showBack"
+					:style="[{ minHeight: menuHeight + 'px' }, { lineHeight: menuHeight + 'px' }, { left: menuRight * 2 + 'px' }, { bottom: menuBotton + 'px' }]"
+				>
+					<u-icon size="32" name="arrow-leftward"></u-icon>
+				</view>
+				<view class="page_title"
+					:style="[
+						{ minHeight: menuHeight + 'px' },
+						{ lineHeight: menuHeight + 'px' },
+					]"
+				>
+				<!-- { left: showBack ? menuRight * 2 + 40 + 'px' : menuRight * 2 + 'px' },
+					{ bottom: menuBotton + 'px' } -->
+					{{pageTitle}}
+				</view>
+			</view>
+		</view>
+		<!--  + 27 -->
+		<view class="slot-height" v-if="showSearch" :style="[{ height: navBarHeight + menuHeight+statusBarHeight + 'px' }]"></view>
+		<view class="slot-height" v-else :style="[{ height: navBarHeight + 'px' }]"></view>
+	</view>
+</template>
+
+<script>
+const app = getApp();
+export default {
+	name: 'navbar-tab-search',
+	props: {
+		// 配置项
+		tabOps: {
+			type: Array,
+			default: () => {
+				return ['tab1', 'tab2'];
+			}
+		},
+		pageTitle:{
+			type: String,
+			default: '书嗨',
+		},
+		// 占位内容
+		placeholder: {
+			type: String,
+			default: '请输入'
+		},
+		// 是否显示插槽,用于输入框右侧内容的显示
+		showSlot: {
+			type: Boolean,
+			default: false
+		},
+		// 是否显示搜索框
+		showSearch: {
+			type: Boolean,
+			default: false
+		},
+		// 跳转的url
+		url: {
+			type: String,
+			default: ''
+		},
+		// 是否显示返回按钮
+		showBack: {
+			type: Boolean,
+			default: false
+		}
+	},
+	data() {
+		return {
+			// 导航栏高度
+			navBarHeight: app.globalData.navBarHeight,
+			statusBarHeight: app.globalData.statusBarHeight,
+			menuRight: app.globalData.menuRight,
+			menuBotton: app.globalData.menuBotton,
+			menuHeight: app.globalData.menuHeight,
+			menuTop: app.globalData.menuTop,
+			// 当前tab
+			current: 0,
+			// 背景色
+			searchBgColor: this.$appTheme.appThemeSearchBgColor
+		};
+	},
+	
+	methods: {
+		// 切换tab
+		change(index) {
+			this.current = index;
+			this.$emit('change', index);
+		},
+
+		// 去搜索页面
+		goSearchPage(url) {
+			uni.navigateTo({
+				url
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+	.page_title{
+		text-align: center;
+		font-size: 32rpx;
+	}
+.slot {
+	width: 100vw;
+}
+.slot-inner {
+	width: 100%;
+	position: fixed;
+	top: 0;
+	left: 0;
+	z-index: 899;
+	overflow: hidden;
+}
+/* .tabList {
+	display: flex;
+	align-items: center;
+	.tab {
+		font-size: 36rpx;
+		font-weight: 400;
+		margin-right: 48rpx;
+		color: $app-theme-navbar-tab-color;
+		&.active {
+			font-weight: 500;
+			color: $app-theme-navbar-tab-color-active;
+			font-size: 40rpx;
+			position: relative;
+			&::before {
+				content: '';
+				width: 16rpx;
+				height: 6rpx;
+				background: $app-theme-navbar-tab-color-active;
+				border-radius: 1px;
+				position: absolute;
+				bottom: -10rpx;
+				left: 50%;
+				transform: translate(-50%, 0);
+			}
+		}
+	}
+}
+ */
+.inner {
+	width: 100%;
+	position: relative;
+	height: 100%;
+	background-color: $app-theme-bg-color;
+	.left {
+		position: absolute;
+		z-index: 999;
+		display: flex;
+		align-items: center;
+		// 防误触
+		width: 30x;
+		margin-right: 10px;
+	}
+	/* .tabList {
+		width: 50%;
+		position: absolute;
+		z-index: 999;
+		display: flex;
+		align-items: center;
+	} */
+}
+.inner-showSearch {
+	width: 100%;
+	height: 100%;
+	background-color: $app-theme-bg-color;
+	.tabList {
+		display: flex;
+		align-items: center;
+	}
+}
+.search-slot {
+	width: 100%;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	.search-components {
+		width: 100%;
+		&.active {
+			width: 86%;
+		}
+	}
+}
+</style>

+ 176 - 0
components/navbar/navbar-top-search.vue

@@ -0,0 +1,176 @@
+<template>
+	<view class="slot">
+		<view class="searchbox">
+			<u-search
+				class="search-components"
+				@click="goSearchPage(url)"
+				disabled
+				height="70"
+				:placeholder="placeholder"
+				:showAction="false"
+				:bg-color="searchBgColor"
+				:class="showSlot ? 'active' : ''"
+			></u-search>
+			<slot showSlot></slot>
+		</view>
+		<view class="siteBar"></view>
+	</view>
+</template>
+
+<script>
+const app = getApp();
+export default {
+	name: 'navbar-tab-search',
+	props: {
+		pageTitle:{
+			type: String,
+			default: '书嗨',
+		},
+		// 占位内容
+		placeholder: {
+			type: String,
+			default: '请输入'
+		},
+		// 是否显示插槽,用于输入框右侧内容的显示
+		showSlot: {
+			type: Boolean,
+			default: false
+		},
+		// 跳转的url
+		url: {
+			type: String,
+			default: ''
+		},
+		// 是否显示返回按钮
+		showBack: {
+			type: Boolean,
+			default: false
+		}
+	},
+	data() {
+		return {
+			// 导航栏高度
+			navBarHeight: app.globalData.navBarHeight,
+			statusBarHeight: app.globalData.statusBarHeight,
+			menuRight: app.globalData.menuRight,
+			menuBotton: app.globalData.menuBotton,
+			menuHeight: app.globalData.menuHeight,
+			menuTop: app.globalData.menuTop,
+			// 当前tab
+			current: 0,
+			// 背景色
+			searchBgColor: this.$appTheme.appThemeSearchBgColor
+		};
+	},
+	
+	methods: {
+		// 切换tab
+		change(index) {
+			this.current = index;
+			this.$emit('change', index);
+		},
+
+		// 去搜索页面
+		goSearchPage(url) {
+			uni.navigateTo({
+				url
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.siteBar {
+	height: 90rpx;
+}
+.slot-inner {
+	width: 100%;
+	position: fixed;
+	top: 0;
+	left: 0;
+	z-index: 899;
+	overflow: hidden;
+	background-color: #fff;
+}
+/* .tabList {
+	display: flex;
+	align-items: center;
+	.tab {
+		font-size: 36rpx;
+		font-weight: 400;
+		margin-right: 48rpx;
+		color: $app-theme-navbar-tab-color;
+		&.active {
+			font-weight: 500;
+			color: $app-theme-navbar-tab-color-active;
+			font-size: 40rpx;
+			position: relative;
+			&::before {
+				content: '';
+				width: 16rpx;
+				height: 6rpx;
+				background: $app-theme-navbar-tab-color-active;
+				border-radius: 1px;
+				position: absolute;
+				bottom: -10rpx;
+				left: 50%;
+				transform: translate(-50%, 0);
+			}
+		}
+	}
+}
+ */
+.inner {
+	width: 100%;
+	position: relative;
+	height: 100%;
+	background-color: $app-theme-bg-color;
+	.left {
+		position: absolute;
+		z-index: 999;
+		display: flex;
+		align-items: center;
+		// 防误触
+		width: 30x;
+		margin-right: 10px;
+	}
+	/* .tabList {
+		width: 50%;
+		position: absolute;
+		z-index: 999;
+		display: flex;
+		align-items: center;
+	} */
+}
+.inner-showSearch {
+	width: 100%;
+	height: 100%;
+	background-color: $app-theme-bg-color;
+	.tabList {
+		display: flex;
+		align-items: center;
+	}
+}
+.searchbox {
+	width: 100%;
+	padding: 0 30rpx 20rpx;
+	box-sizing: border-box;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	position: fixed;
+	top: 0;
+	left: 0;
+	z-index: 899;
+	overflow: hidden;
+	background-color: #fff;
+	.search-components {
+		width: 100%;
+		&.active {
+			// width: 86%;
+			margin-right: 30rpx;
+		}
+	}
+}
+</style>

+ 41 - 0
components/navbar/navbar.vue

@@ -0,0 +1,41 @@
+<template>
+	<u-navbar
+		:back-icon-name="backIconName"
+		:title="title"
+		:custom-back="backFunction"
+		:back-icon-color="$appTheme.appThemeTextBlackColor"
+		:title-color="$appTheme.appThemeTextBlackColor"
+		:is-back="isback"
+		:back-icon-size="32"
+		:border-bottom="false"
+	></u-navbar>
+</template>
+
+<script>
+export default {
+	name: 'navbar',
+	props: {
+		// 返回图标名称
+		backIconName: {
+			type: String,
+			default: 'arrow-leftward'
+		},
+		// 标题
+		title: {
+			type: String,
+			default: ''
+		},
+		// 自定义返回
+		backFunction: {
+			type: Function,
+			default: null
+		},
+		isback:{
+			type: Boolean,
+			default: true
+		},
+	}
+};
+</script>
+
+<style></style>

+ 76 - 0
components/no-data.vue

@@ -0,0 +1,76 @@
+<template>
+	<view class="slot">
+		<!-- <view class="img"><image :src="'../static/nodata/' + type + '.png'" mode="widthFix"></image></view>
+		<view class="tip">{{ returnTip(tip) }}</view> -->
+		<u-empty :text="returnTip(tip)" :mode="type"></u-empty>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'no-data',
+	props: {
+		// 类型,缺省图片名称,路径:/static/nodata/...
+		type: {
+			type: String,
+			default: 'list'
+		},
+		// 自定义文本
+		tip: {
+			type: String,
+			default: ''
+		}
+	},
+	data() {
+		return {
+			ops: [
+				{ type: 'list', tip: '暂无数据' },
+				// { type: 'collection', tip: '暂无收藏' },
+				// { type: 'fans', tip: '暂无粉丝' },
+				// { type: 'network', tip: '暂无网络' },
+				{ type: 'order', tip: '暂无订单' },
+				// { type: 'news', tip: '暂无消息' },
+				// { type: 'result', tip: '暂无搜索结果' }
+			]
+		};
+	},
+	methods: {
+		returnTip(tip) {
+			if (tip) {
+				return tip;
+			} else {
+				return this.ops.filter(item => item.type == this.type)[0].tip;
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	width: 100%;
+	// height: 100%;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	align-content: center;
+	flex-wrap: wrap;
+	margin-top: 100rpx;
+	.img {
+		width: 400rpx;
+		height: auto;
+		image {
+			width: 100%;
+			height: auto;
+		}
+	}
+	.tip {
+		margin-top: 48rpx;
+		width: 100%;
+		text-align: center;
+		font-size: 30rpx;
+		font-weight: 400;
+		color: $app-theme-text-color;
+	}
+}
+</style>

+ 34 - 0
components/popup-empty.vue

@@ -0,0 +1,34 @@
+<template>
+	<u-popup v-model="show" mask mode="bottom" mask-close-able closeable>
+		<view class="inner"><slot></slot></view>
+	</u-popup>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			show: false,
+			// 数据源
+			data: {}
+		};
+	},
+	methods: {
+		// 打开popup
+		open(data) {
+			this.data = data;
+			this.show = true;
+		},
+		// 关闭
+		close() {}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.inner {
+	padding: 30rpx 30rpx 0 30rpx;
+	display: flex;
+	align-items: flex-end;
+}
+</style>

+ 214 - 0
components/privacy-popup.vue

@@ -0,0 +1,214 @@
+<!--
+* @author: Jay
+* @description: 小程序隐私协议 弹窗
+* @createTime: 2023-08-31 10:07:54
+ -->
+<template>
+    <view v-if="showPop">
+        <view class="privacy-mask">
+            <view class="privacy-wrap">
+                <view class="privacy-title">
+                    用户隐私保护提示
+                </view>
+                <view class="privacy-desc">
+                    感谢您使用本小程序,在使用前您应当阅读井同意
+                    <text class="privacy-link" @tap="openPrivacyContract">《用户隐私保护指引》</text>,
+                    当点击同意并继续时,即表示您已理解并同意该条款内容,该条款将对您产生法律约束力;如您不同意,将无法继续使用小程序相关功能。
+                </view>
+                <view class="privacy-button-flex">
+                    <button class="privacy-button-btn bg-disagree" @tap="handleDisagree">
+                        不同意
+                    </button>
+                    <button id="agree-btn" class="privacy-button-btn bg-agree" open-type="agreePrivacyAuthorization"
+                        @agreeprivacyauthorization="handleAgree">
+                        同意并继续
+                    </button>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+    export default {
+        data() {
+            return {
+                showPop: false,
+                privacyAuthorization: null,
+                privacyResolves: new Set(),
+                closeOtherPagePopUpHooks: new Set(),
+            }
+        },
+        mounted() {
+            this.init()
+            this.curPageShow()
+        },
+        created() {
+            //查询微信侧记录的用户是否有待同意的隐私政策信息
+            wx.getPrivacySetting({
+                success(res) {
+                    console.log('隐私政策信息', res)
+                    // if (res.needAuthorization) {
+                    //     //打开弹窗
+                    //     that.popUp()
+                    // }
+                }
+            });
+        },
+        methods: {
+            // 监听何时需要提示用户阅读隐私政策
+            init() {
+                let that = this;
+                if (wx.onNeedPrivacyAuthorization) {
+                    wx.onNeedPrivacyAuthorization((resolve) => {
+                        if (typeof that.privacyAuthorization === 'function') {
+                            that.privacyAuthorization(resolve)
+                        }
+                    })
+                }
+            },
+            //初始化监听程序
+            curPageShow() {
+                this.privacyAuthorization = resolve => {
+                    this.privacyResolves.add(resolve)
+                    //打开弹窗
+                    this.popUp()
+                    // 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
+                    this.closeOtherPagePopUp(this.disPopUp)
+                }
+                this.closeOtherPagePopUpHooks.add(this.disPopUp)
+            },
+            // 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
+            closeOtherPagePopUp(closePopUp) {
+                this.closeOtherPagePopUpHooks.forEach(hook => {
+                    if (closePopUp !== hook) {
+                        hook()
+                    }
+                })
+            },
+            //打开隐私协议
+            openPrivacyContract() {
+                wx.openPrivacyContract({
+                    success(res) {
+                        console.log('打开隐私协议', res);
+                    },
+                    fail(err) {
+                        console.error('打开隐私协议失败', err)
+                    }
+                });
+            },
+            // 不同意
+            handleDisagree() {
+                this.privacyResolves.forEach(resolve => {
+                    resolve({
+                        event: 'disagree',
+                    })
+                })
+                this.privacyResolves.clear()
+                //关闭弹窗
+                this.disPopUp()
+                //退出小程序
+                // wx.exitMiniProgram();
+            },
+            // 同意并继续
+            handleAgree() {
+                this.privacyResolves.forEach(resolve => {
+                    resolve({
+                        event: 'agree',
+                        buttonId: 'agree-btn'
+                    })
+                })
+                this.privacyResolves.clear()
+                //关闭弹窗
+                this.disPopUp()
+            },
+            //打开弹窗
+            popUp() {
+                if (this.showPop === false) {
+                    this.showPop = true
+                }
+            },
+            //关闭弹窗
+            disPopUp() {
+                if (this.showPop === true) {
+                    this.showPop = false
+                }
+            },
+        }
+    }
+</script>
+
+<style lang="scss" scoped>
+    .privacy-mask {
+        position: fixed;
+        z-index: 5000;
+        top: 0;
+        right: 0;
+        left: 0;
+        bottom: 0;
+        background: rgba(0, 0, 0, 0.2);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+
+    .privacy-wrap {
+        width: 632rpx;
+        padding: 48rpx 30rpx;
+        box-sizing: border-box;
+        background: #fff;
+        border-radius: 16rpx;
+    }
+
+    .privacy-title {
+        padding: 0rpx 30rpx 40rpx 30rpx;
+        font-weight: 700;
+        font-size: 36rpx;
+        text-align: center;
+    }
+
+    .privacy-desc {
+        font-size: 30rpx;
+        color: #555;
+        line-height: 2;
+        text-align: left;
+        padding: 0 40rpx;
+    }
+
+    .privacy-link {
+        color: #2f80ed;
+    }
+
+    .privacy-button-flex {
+        display: flex;
+        padding: 20rpx 40rpx;
+    }
+
+    .privacy-button-btn {
+        color: #FFF;
+        font-size: 30rpx;
+        font-weight: 500;
+        line-height: 100rpx;
+        text-align: center;
+        height: 100rpx;
+        border-radius: 20rpx;
+        border: none;
+        background: #07c160;
+        flex: 1;
+        margin-right: 30rpx;
+        justify-content: center;
+    }
+
+    .privacy-button-btn::after {
+        border: none;
+    }
+
+    .bg-disagree {
+        color: #07c160;
+        background: #f2f2f2;
+    }
+
+    .bg-agree {
+        margin-right: 0rpx;
+    }
+</style>

+ 122 - 0
components/search/search.vue

@@ -0,0 +1,122 @@
+<template>
+	<view class="slot" :style="{ padding }">
+		<view class="search">
+			<u-search
+				class="search-components"
+				:placeholder="placeholder"
+				v-model="keyword"
+				bg-color="#F4F5F8"
+				height="70"
+				@focus="focus"
+				@search="search"
+				@custom="search"
+				@change="change"
+			></u-search>
+		</view>
+		<view class="list" v-show="showSearchList" v-if="showFastTip">
+			<view class="item" v-for="(item, index) in searchList" :key="index">{{ item }}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+const searchList = [];
+export default {
+	name: 'search',
+	props: {
+		// 占位内容
+		placeholder: {
+			type: String,
+			default: '请输入'
+		},
+		// 内边距
+		padding: {
+			type: String,
+			default: '20rpx 30rpx'
+		},
+		// 是否显示取消按钮
+		showCancelBtn: {
+			type: Boolean,
+			default: false
+		},
+		showFastTip:{
+			type: Boolean,
+			default: false
+		},
+	},
+	data() {
+		return {
+			// 关键字
+			keyword: '',
+			// 检索列表
+			showSearchList: false,
+			searchList: []
+		};
+	},
+	watch: {
+		// 监听关键字,如果有输入关键字,则打开待选词列表
+		keyword(newVal) {
+			if (newVal.length > 0) {
+				this.showSearchList = true;
+				this.searchList = searchList;
+			} else {
+				this.showSearchList = false;
+			}
+		}
+	},
+	methods: {
+		// 聚焦
+		focus() {},
+
+		// 内容改变
+		change(e) {
+			this.$emit('change', e);
+		},
+
+		// 取消搜索
+		cancelSearch() {
+			this.showSearchList = false;
+		},
+
+		// 搜索
+		search(e) {
+			this.$emit('search', e);
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.slot {
+}
+.search {
+	width: 100%;
+	// display: flex;
+	// justify-content: space-between;
+	// align-items: center;
+	// .search-components {
+	// 	width: 100%;
+	// 	&.active {
+	// 		width: 86%;
+	// 	}
+	// }
+}
+/deep/.u-content {
+	border-radius: 0 !important;
+}
+.cancel {
+	width: 14%;
+	text-align: center;
+}
+.list {
+	.item {
+		width: 100%;
+		height: 100rpx;
+		line-height: 100rpx;
+		border-bottom: 1rpx solid $app-theme-border-color;
+		&.last-child {
+			border: none;
+		}
+	}
+}
+</style>

+ 108 - 0
components/select-reason.vue

@@ -0,0 +1,108 @@
+<template>
+	<u-popup v-model="show" mask mode="bottom" mask-close-able border-radius="16">
+		<view class="header">
+			<view class="cancel" @click="close">取消</view>
+			<view class="title">{{ title }}</view>
+			<view class="submit" @click="submit">确认</view>
+		</view>
+		<view class="list">
+			<u-radio-group v-model="currentReasonIndex" @change="reasonChange">
+				<view class="item" v-for="(item, index) in ops" :key="index" @click="currentReasonIndex = index">
+					<view class="label">{{ item.label }}</view>
+					<view class="check"><u-radio :name="index" :active-color="radioColor"></u-radio></view>
+				</view>
+			</u-radio-group>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+export default {
+	props: {
+		// 标题
+		title: {
+			type: String,
+			default: ''
+		},
+		// 配置项
+		ops: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		}
+	},
+	data() {
+		return {
+			show: false,
+			currentReasonIndex: 5,
+			radioColor: this.$appTheme.appThemeColor
+		};
+	},
+	methods: {
+		// 打开popup
+		open(currentReasonIndex) {
+			this.currentReasonIndex = currentReasonIndex;
+			this.show = true;
+		},
+
+		// 选择原因
+		reasonChange(e) {},
+
+		// 关闭
+		close() {
+			this.show = false;
+		},
+
+		// 提交
+		submit() {
+			if (this.currentReasonIndex == null) {
+				uni.showToast({
+					title: '请选择' + this.title,
+					icon: 'none'
+				});
+				return;
+			}
+			this.$emit('change', this.currentReasonIndex);
+			this.close();
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.header {
+	padding: 26rpx 32rpx;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	.cancel {
+		font-size: 28rpx;
+		color: $app-theme-card-gray-deep-color;
+	}
+	.title {
+		font-size: 32rpx;
+		color: $app-theme-text-black-color;
+	}
+	.submit {
+		font-size: 28rpx;
+		color: $app-theme-color;
+	}
+}
+.list {
+	padding: 30rpx;
+	.item {
+		margin-bottom: 40rpx;
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		.label {
+			font-size: 30rpx;
+			color: $app-theme-text-color;
+		}
+		.check {
+			width: 36rpx;
+		}
+	}
+}
+</style>

+ 86 - 0
components/tabs.vue

@@ -0,0 +1,86 @@
+<template>
+	<view class="slot" :style="{ padding, backgroundColor }">
+		<u-tabs
+			name="text"
+			:list="listData"
+			:is-scroll="true"
+			:active-color="activeColor || default_activeColor"
+			:inactive-color="inactiveColor || default_inactiveColor"
+			:font-size="fontSize"
+			:current="current"
+			@change="change"
+			:bar-style="{ backgrond: activeBarColor || default_activeBarColor }"
+			bg-color="#FAFAFA"
+			bar-height="6"
+			:bg-color="backgroundColor"
+			:item-width="itemWidth"
+		></u-tabs>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'tabs',
+	data() {
+		return {
+			current: 0,
+			default_activeBarColor: this.$appTheme.appThemeColor,
+			default_activeColor: this.$appTheme.appThemeTextBlackColor,
+			default_inactiveColor: this.$appTheme.appThemeTextBlackColor
+		};
+	},
+	props: {
+		// 配置项
+		listData: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		},
+		// 激活颜色
+		activeBarColor: {
+			type: String,
+			default: null
+		},
+		activeColor: {
+			type: String,
+			default: null
+		},
+		inactiveColor: {
+			type: String,
+			default: null
+		},
+		// 内边距
+		padding: {
+			type: String,
+			default: '0 0rpx 20rpx'
+		},
+		// 背景色
+		backgroundColor: {
+			type: String,
+			default: '#fafafa'
+		},
+		// 文字大小
+		fontSize: {
+			type: [String, Number],
+			default: 30
+		},
+		// 宽度
+		itemWidth: {
+			type: [String, Number],
+			default: 'auto'
+		}
+	},
+	methods: {
+		change(index) {
+			this.current = index;
+			this.$emit('change', index);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+}
+</style>

+ 31 - 0
components/tag.vue

@@ -0,0 +1,31 @@
+<template>
+	<view class="slot" @click="click"><slot></slot></view>
+</template>
+
+<script>
+export default {
+	name: 'tag',
+	methods: {
+		click() {
+			this.$emit('click');
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	display: inline-block;
+	margin-right: 24rpx;
+	margin-bottom: 20rpx;
+	width: calc(100% / 3 - 24rpx);
+	height: 32px;
+	background: $app-theme-bg-gray-deep-color;
+	border-radius: 4px;
+	font-size: 26rpx;
+	text-align: center;
+	font-weight: 400;
+	line-height: 32px;
+	color: $app-theme-text-color;
+}
+</style>

+ 80 - 0
components/title-operate.vue

@@ -0,0 +1,80 @@
+<template>
+	<view class="slot" :style="{ padding, backgroundColor, alignItems: align }">
+		<view class="name" :style="[{ fontSize: titleSize }, titleColor ? { color: titleColor } : {}]">{{ title }}</view>
+		<view class="more" v-if="showMore" @click="clickMore">
+			{{ moreLabel ? moreLabel : '' }}
+			<u-icon size="30" name="arrow-right"></u-icon>
+		</view>
+		<!-- 自定义右侧占位 -->
+		<view class="more" v-else><slot></slot></view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'title-operate',
+	props: {
+		// 标题
+		title: {
+			type: String,
+			default: '标题'
+		},
+		// 显示更多
+		showMore: {
+			type: Boolean,
+			default: false
+		},
+		// 更多按钮的名称
+		moreLabel: {
+			type: String,
+			default: ''
+		},
+		// 内边距
+		padding: {
+			type: String,
+			default: '30rpx'
+		},
+		// 背景
+		backgroundColor: {
+			type: String,
+			default: ''
+		},
+		// 标题字体大小
+		titleSize: {
+			type: String,
+			default: '34rpx'
+		},
+		// 对齐方式
+		align: {
+			type: String,
+			default: 'flex-end'
+		},
+		// 标题颜色
+		titleColor: {
+			type: String,
+			default: ''
+		}
+	},
+	methods: {
+		clickMore() {
+			console.log('1111'),
+			this.$emit('clickMore');
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	display: flex;
+	justify-content: space-between;
+	.name {
+		color: $app-theme-text-black-color;
+	}
+	.more {
+		display: flex;
+		align-items: center;
+		color: $app-theme-text-gray-color;
+	}
+}
+</style>

+ 60 - 0
main.js

@@ -0,0 +1,60 @@
+import Vue from 'vue';
+import App from './App';
+Vue.config.productionTip = false;
+App.mpType = 'app';
+
+// 引入全局uview-ui
+import uView from 'uview-ui';
+Vue.use(uView);
+
+// uview-ui对小程序分享的mixin封装
+let mpShare = require('uview-ui/libs/mixin/mpShare.js');
+Vue.mixin(mpShare);
+
+// 引入全局组件
+import Navbar from '@/components/navbar/navbar.vue'
+Vue.component('Navbar', Navbar);
+import NoData from '@/components/no-data.vue'
+Vue.component('NoData', NoData);
+import LoadMore from '@/components/loadmore.vue'
+Vue.component('LoadMore', LoadMore);
+import TitleOperate from '@/components/title-operate.vue';
+Vue.component('TitleOperate', TitleOperate);
+// 引入全局主题变量
+import appTheme from '@/theme.scss';
+Vue.prototype.$appTheme = appTheme
+
+// 引入全局工具函数
+import {
+	replaceSale
+} from '@/utils/replace.js'
+Vue.prototype.$replaceSale = replaceSale
+import {
+	copyByUniappApi
+} from '@/utils/uniapp-api.js';
+Vue.prototype.$copyByUniappApi = copyByUniappApi
+// 引入环境变量
+import ENV_CONFIG from '@/.env.js'
+Vue.prototype.$env = ENV_CONFIG[process.env.ENV_TYPE];
+
+
+// 引入vuex
+import store from './store/index.js'
+Vue.prototype.$store = store
+
+const app = new Vue({
+	...store,
+	...App
+});
+
+// 以下内容需要在 new Vue() 之后引入注册,因为外部JS文件需要引用vue的实例,即this对象
+
+// 基于uview-ui的http配置
+import { installHttpConfig } from '@/api/config.js';
+Vue.use(installHttpConfig, app);
+
+// http接口API全局
+import { installApiModules } from '@/api';
+Vue.use(installApiModules, app);
+
+app.$mount();

+ 154 - 0
manifest.json

@@ -0,0 +1,154 @@
+{
+    "name" : "书嗨",
+    "appid" : "__UNI__5A1BAE0",
+    "description" : "基于 Uniapp + uView UI 的社区电商微信小程序 / App 开发模板。提供商城、社区、积分等 42 张页面模板。",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    "app-plus" : {
+        "optimization" : {
+            "subPackages" : true
+        },
+        "safearea" : {
+            "bottom" : {
+                "offset" : "none"
+            }
+        },
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "modules" : {
+            "Payment" : {}
+        },
+        "distribute" : {
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ],
+                "abiFilters" : [ "armeabi-v7a", "arm64-v8a" ]
+            },
+            "ios" : {
+                "idfa" : false
+            },
+            "sdkConfigs" : {
+                "ad" : {},
+                "payment" : {
+                    "weixin" : {
+                        "__platform__" : [ "ios", "android" ],
+                        "appid" : "wx5053af676215a296",
+                        "UniversalLinks" : ""
+                    }
+                }
+            },
+            "icons" : {
+                "android" : {
+                    "hdpi" : "unpackage/res/icons/72x72.png",
+                    "xhdpi" : "unpackage/res/icons/96x96.png",
+                    "xxhdpi" : "unpackage/res/icons/144x144.png",
+                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
+                },
+                "ios" : {
+                    "appstore" : "unpackage/res/icons/1024x1024.png",
+                    "ipad" : {
+                        "app" : "unpackage/res/icons/76x76.png",
+                        "app@2x" : "unpackage/res/icons/152x152.png",
+                        "notification" : "unpackage/res/icons/20x20.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "proapp@2x" : "unpackage/res/icons/167x167.png",
+                        "settings" : "unpackage/res/icons/29x29.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "spotlight" : "unpackage/res/icons/40x40.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
+                    },
+                    "iphone" : {
+                        "app@2x" : "unpackage/res/icons/120x120.png",
+                        "app@3x" : "unpackage/res/icons/180x180.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "notification@3x" : "unpackage/res/icons/60x60.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "settings@3x" : "unpackage/res/icons/87x87.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
+                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
+                    }
+                }
+            }
+        }
+    },
+    "quickapp" : {},
+    "mp-weixin" : {
+        "appid" : "wx5053af676215a296",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : true,
+            "minified" : true,
+            "postcss" : true
+        },
+        "optimization" : {
+            "subPackages" : false
+        },
+        "usingComponents" : true,
+        "permission" : {},
+		//小程序隐私协议
+		"__usePrivacyCheck__": true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true,
+        "component2" : true
+    },
+    "mp-qq" : {
+        "optimization" : {
+            "subPackages" : true
+        },
+        "appid" : ""
+    },
+    "mp-baidu" : {
+        "usingComponents" : true,
+        "appid" : ""
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true,
+        "appid" : ""
+    },
+    "h5" : {
+        "template" : "template.h5.html",
+        "router" : {
+            "mode" : "hash",
+            "base" : ""
+        },
+        "optimization" : {
+            "treeShaking" : {
+                "enable" : false
+            }
+        },
+        "title" : "书嗨"
+    },
+    "vueVersion" : "2",
+    "fallbackLocale" : "zh-Hans"
+}

+ 56 - 0
mixins/smsCode.js

@@ -0,0 +1,56 @@
+const mixinSmsCode = {
+	data() {
+		return {
+			smsCode: {
+				countDownTime: 60,
+				isGetting: false,
+				getted: false,
+				timer: null
+			}
+		}
+	},
+	methods: {
+		// 获取验证码方法
+		getCode(PHONE_KEY, callBack) {
+			if (!this[PHONE_KEY]) {
+				uni.showToast({
+					icon: 'none',
+					title: '请输入手机号'
+				});
+				return;
+			}
+			uni.showLoading({
+				title: '正在获取验证码'
+			});
+			callBack()
+		},
+
+		// 验证码请求后续
+		getSmsCodeOver() {
+			uni.hideLoading();
+			this.smsCode.isGetting = true;
+			this.smsCode.getted = true;
+			this.smsCode.timer = setInterval(() => {
+				if (this.smsCode.countDownTime > 0 && this.smsCode.countDownTime <= 60) {
+					this.smsCode.countDownTime--;
+				} else {
+					this.resetCountDown(false);
+				}
+			}, 1000);
+		},
+
+		// 倒计时初始化
+		resetCountDown(isInit) {
+			clearInterval(this.smsCode.timer);
+			this.smsCode = {
+				countDownTime: 60,
+				isGetting: false,
+				getted: isInit ? false : true,
+				timer: null
+			};
+			this.$forceUpdate();
+		},
+	}
+}
+
+export default mixinSmsCode

+ 1 - 0
note.md

@@ -0,0 +1 @@
+https://www.cnblogs.com/lovejielive/p/17669140.html

+ 37 - 0
package.json

@@ -0,0 +1,37 @@
+{
+    "name": "书嗨",
+    "version": "1.0",
+    "description": "",
+    "main": "main.js",
+    "id": "nx-temp-shop",
+    "displayName": "",
+    "keywords": [
+        "卖书",
+        "小程序",
+        "商城"
+    ],
+    "dcloudext": {
+        "category": [
+           
+        ]
+    },
+		"uni-app": {
+			"scripts": {
+				"dev": {
+					"title": "微信开发版(dev)",
+					"env": {
+						"ENV_TYPE": "dev",
+						"UNI_PLATFORM": "mp-weixin"
+						
+					}
+				},
+				"prod":{
+					"title":"微信正式版(prod)",
+					"env":{
+						"ENV_TYPE": "prod",
+						"UNI_PLATFORM":"mp-weixin"
+					}
+				}
+			}
+		}
+}

+ 141 - 0
pages-mall/components/after-sales/apply-reason.vue

@@ -0,0 +1,141 @@
+<!-- 售后申请 -->
+<template>
+	<view class="slot">
+		<view class="form">
+			<u-form :model="form" ref="form" label-width="140rpx">
+				<u-form-item label="退款原因">
+					<u-input v-model="form.reason" type="select" placeholder="请选择" @click="openReasonPopup" />
+				</u-form-item>
+				<u-form-item label="退款说明">
+					<u-input v-model="form.desc" placeholder="选填" />
+				</u-form-item>
+				<u-form-item label="退款金额" :border-bottom="false">
+					<u-input type="digit" v-model="form.user_refundFee" placeholder="-" />
+				</u-form-item>
+			</u-form>
+		</view>
+		<view class="btn">
+			<u-button type="primary" shape="circle" @click="submit"><text>提交</text></u-button>
+		</view>
+
+		<!-- 原因选择框 -->
+		<SelectReason ref="SelectReason" title="退款原因" :ops="reasonOps" @change="changeReason"></SelectReason>
+	</view>
+</template>
+
+<script>
+	var _self;
+	import SelectReason from '@/components/select-reason.vue';
+	export default {
+		name: 'apply-reason',
+		components: {
+			SelectReason
+		},
+		props: {
+			totalPrice:{
+				type:String|Number,
+				default:0
+			},
+		},
+		data() {
+			return {
+				// 表单
+				form: {
+					reasonId: null,
+					reason: '',
+					desc: '',
+					user_refundFee:null,
+				},
+				refundFeeMax:null,
+				// 原因
+				currentReasonIndex: null,
+				reasonOps: [{
+						label: '不喜欢/不想要',
+						value: '1'
+					},
+					{
+						label: '实物与描述不符合',
+						value: '2'
+					},
+					{
+						label: '卖家发错商品',
+						value: '3'
+					},
+					{
+						label: '与卖家协商一致',
+						value: '4'
+					},
+					{
+						label: '其他',
+						value: '-1'
+					}
+				]
+			};
+		},
+		watch: {
+			// 监听关键字,如果有输入关键字,则打开待选词列表
+			totalPrice:{
+				handler(newVal) {
+					console.log(newVal);
+					this.refundFeeMax = newVal;
+					this.form.user_refundFee = newVal;
+				},
+				deep:true,
+				immediate:true,
+			}
+		},
+		onLoad() {
+			_self = this;
+		},
+		methods: {
+			// 计算金额,
+			
+			// 打开原因选择框
+			openReasonPopup() {
+				this.$refs.SelectReason.open(this.currentReasonIndex);
+			},
+
+			// 选择原因回调
+			changeReason(index) {
+				console.log(index);
+				this.currentReasonIndex = index;
+				this.form.reasonId = this.reasonOps[index].value;
+				this.form.reason = this.reasonOps[index].label;
+			},
+
+			// 提交
+			submit() {
+				if (!this.form.reasonId) {
+					this.$u.toast('请选择退款原因');
+					return;
+				}
+				// if(this.refundFeeMax&&this.refundFeeMax<this.form.user_refundFee){
+				// 	this.$u.toast("退款金额不能超出选中商品总价值")
+				// 	return;
+				// }
+				let reason = this.form.reason;
+				console.log(this.form.desc);
+				if(this.form.desc){
+					reason+=`(${this.form.desc})`
+				}
+				this.$emit('done',{
+					reason: reason,
+					user_refundFee: this.form.user_refundFee || 0
+				})
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.form {
+		background-color: $app-theme-bg-color;
+		border-radius: 16rpx;
+		box-shadow: $app-theme-shadow;
+		padding: 0 30rpx;
+	}
+
+	.btn {
+		padding: 60rpx 0rpx;
+	}
+</style>

+ 114 - 0
pages-mall/components/after-sales/back-all.vue

@@ -0,0 +1,114 @@
+<template>
+	<view class="slot">
+		<view class="form">
+			<u-form :model="form" ref="form" label-width="140rpx">
+				<u-form-item label="退款原因">
+					<u-input v-model="form.reason" type="select" placeholder="请选择" @click="openReasonPopup" />
+				</u-form-item>
+				<u-form-item label="退款说明">
+					<u-input v-model="form.desc" placeholder="选填" />
+				</u-form-item>
+				<u-form-item label="退款金额" :border-bottom="false">
+					<u-input v-model="totalPrice" disabled placeholder="-" />
+				</u-form-item>
+			</u-form>
+		</view>
+		<view class="btn">
+			<u-button type="primary" shape="circle" @click="submit"><text>提交</text></u-button>
+		</view>
+
+		<!-- 原因选择框 -->
+		<SelectReason ref="SelectReason" title="退款原因" :ops="reasonOps" @change="changeReason"></SelectReason>
+	</view>
+</template>
+
+<script>
+	var _self;
+	import SelectReason from '@/components/select-reason.vue';
+	export default {
+		name: 'back-money',
+		components: {
+			SelectReason
+		},
+		props: {
+			totalPrice:{
+				type:String|Number,
+				default:0
+			},
+		},
+		data() {
+			return {
+				// 表单
+				form: {
+					reasonId: null,
+					reason: '',
+					desc: '',
+				},
+				
+				// 原因
+				currentReasonIndex: null,
+				reasonOps: [{
+						label: '不喜欢/不想要',
+						value: '1'
+					},
+					{
+						label: '实物与描述不符合',
+						value: '2'
+					},
+					{
+						label: '卖家发错商品',
+						value: '3'
+					},
+					{
+						label: '其他',
+						value: '-1'
+					}
+				]
+			};
+		},
+		onLoad() {
+			_self = this;
+		},
+		methods: {
+			// 计算金额,
+			
+			// 打开原因选择框
+			openReasonPopup() {
+				this.$refs.SelectReason.open(this.currentReasonIndex);
+			},
+
+			// 选择原因回调
+			changeReason(index) {
+				console.log(index);
+				this.currentReasonIndex = index;
+				this.form.reasonId = this.reasonOps[index].value;
+				this.form.reason = this.reasonOps[index].label;
+			},
+
+			// 提交
+			submit() {
+				if (!this.form.reasonId) {
+					this.$u.toast('请选择退款原因');
+					return;
+				}
+				this.$emit('done',{
+					refund_type:2,
+					reason: this.form.reason,
+				})
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.form {
+		background-color: $app-theme-bg-color;
+		border-radius: 16rpx;
+		box-shadow: $app-theme-shadow;
+		padding: 0 30rpx;
+	}
+
+	.btn {
+		padding: 60rpx 0rpx;
+	}
+</style>

+ 72 - 0
pages-mall/components/after-sales/back-goods.vue

@@ -0,0 +1,72 @@
+<template>
+	<view class="slot">
+		<view class="form">
+			<u-form :model="form" ref="form" label-width="140rpx">
+				<u-form-item label="换货原因"><u-input v-model="form.reason" type="select" placeholder="请选择" @click="openReasonPopup" /></u-form-item>
+				<u-form-item label="换货说明"><u-input v-model="form.desc" placeholder="选填" /></u-form-item>
+			</u-form>
+		</view>
+		<view class="btn">
+			<u-button type="primary" shape="circle" @click="submit"><text>提交</text></u-button>
+		</view>
+		<!-- 原因选择框 -->
+		<SelectReason ref="SelectReason" :ops="reasonOps" title="换货原因" @change="changeReason"></SelectReason>
+	</view>
+</template>
+
+<script>
+import SelectReason from '@/components/select-reason.vue';
+export default {
+	name: 'back-money',
+	components: {
+		SelectReason
+	},
+	data() {
+		return {
+			// 表单
+			form: {
+				reason: '',
+				desc: '',
+				money: '¥180.00'
+			},
+			// 原因
+			currentReasonIndex: null,
+			reasonOps: [
+				{ label: '尺码拍错/不喜欢/效果不好', value: '0' },
+				{ label: '材质、面料与商品描述不符', value: '1' },
+				{ label: '大小尺寸与商品描述不符', value: '2' },
+				{ label: '剪裁/做工瑕疵', value: '3' },
+				{ label: '颜色款式图案与描述不符', value: '4' },
+				{ label: '其他', value: '5' }
+			]
+		};
+	},
+	methods: {
+		// 打开原因选择框
+		openReasonPopup() {
+			this.$refs.SelectReason.open(this.currentReasonIndex);
+		},
+
+		// 选择原因回调
+		changeReason(e) {
+			this.currentReasonIndex = e;
+			this.form.reason = this.reasonOps[e].label;
+		},
+
+		// 提交
+		submit() {}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.form {
+	background-color: $app-theme-bg-color;
+	border-radius: 16rpx;
+	box-shadow: $app-theme-shadow;
+	padding: 0 30rpx;
+}
+.btn {
+	padding: 60rpx 0rpx;
+}
+</style>

+ 114 - 0
pages-mall/components/after-sales/back-money.vue

@@ -0,0 +1,114 @@
+<template>
+	<view class="slot">
+		<view class="form">
+			<u-form :model="form" ref="form" label-width="140rpx">
+				<u-form-item label="退款原因">
+					<u-input v-model="form.reason" type="select" placeholder="请选择" @click="openReasonPopup" />
+				</u-form-item>
+				<u-form-item label="退款说明">
+					<u-input v-model="form.desc" placeholder="选填" />
+				</u-form-item>
+				<u-form-item label="退款金额" :border-bottom="false">
+					<u-input v-model="totalPrice" disabled placeholder="-" />
+				</u-form-item>
+			</u-form>
+		</view>
+		<view class="btn">
+			<u-button type="primary" shape="circle" @click="submit"><text>提交</text></u-button>
+		</view>
+
+		<!-- 原因选择框 -->
+		<SelectReason ref="SelectReason" title="退款原因" :ops="reasonOps" @change="changeReason"></SelectReason>
+	</view>
+</template>
+
+<script>
+	var _self;
+	import SelectReason from '@/components/select-reason.vue';
+	export default {
+		name: 'back-money',
+		components: {
+			SelectReason
+		},
+		props: {
+			totalPrice:{
+				type:String|Number,
+				default:0
+			},
+		},
+		data() {
+			return {
+				// 表单
+				form: {
+					reasonId: null,
+					reason: '',
+					desc: '',
+				},
+				
+				// 原因
+				currentReasonIndex: null,
+				reasonOps: [{
+						label: '不喜欢/不想要',
+						value: '1'
+					},
+					{
+						label: '实物与描述不符合',
+						value: '2'
+					},
+					{
+						label: '卖家发错商品',
+						value: '3'
+					},
+					{
+						label: '其他',
+						value: '-1'
+					}
+				]
+			};
+		},
+		onLoad() {
+			_self = this;
+		},
+		methods: {
+			// 计算金额,
+			
+			// 打开原因选择框
+			openReasonPopup() {
+				this.$refs.SelectReason.open(this.currentReasonIndex);
+			},
+
+			// 选择原因回调
+			changeReason(index) {
+				console.log(index);
+				this.currentReasonIndex = index;
+				this.form.reasonId = this.reasonOps[index].value;
+				this.form.reason = this.reasonOps[index].label;
+			},
+
+			// 提交
+			submit() {
+				if (!this.form.reasonId) {
+					this.$u.toast('请选择退款原因');
+					return;
+				}
+				this.$emit('done',{
+					refund_type:1,
+					reason: this.form.reason,
+				})
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.form {
+		background-color: $app-theme-bg-color;
+		border-radius: 16rpx;
+		box-shadow: $app-theme-shadow;
+		padding: 0 30rpx;
+	}
+
+	.btn {
+		padding: 60rpx 0rpx;
+	}
+</style>

+ 59 - 0
pages-mall/components/after-sales/back-status.vue

@@ -0,0 +1,59 @@
+<template>
+	<view class="slot">
+		<view class="status"><text v-if="type == '0'">退款成功</text></view>
+		<view class="desc">
+			<text v-if="type == '0'">{{ ponitsDate }}</text>
+		</view>
+		<view class="operate"></view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'back-status',
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		// 类型
+		type: {
+			type: [String, Number],
+			default: '0'
+		},
+		// 当前节点日期
+		ponitsDate: {
+			type: String,
+			default: '2022-01-11 14:19:56'
+		}
+	},
+	data() {
+		return {};
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	position: relative;
+	padding: 30rpx;
+	background-color: $app-theme-color;
+	color: $app-theme-text-white-color;
+	margin-bottom: 24rpx;
+	.status {
+		margin-bottom: 20rpx;
+		font-size: 30rpx;
+	}
+	.desc {
+		font-size: 26rpx;
+	}
+	.operate {
+		position: absolute;
+		top: 30rpx;
+		right: 30rpx;
+	}
+}
+</style>

+ 221 - 0
pages-mall/components/after-sales/order-card1.vue

@@ -0,0 +1,221 @@
+<template>
+	<view class="slot">
+		<!-- 商品卡片 -->
+		<OrderGoodsCard :data="data" showBorderBottom>
+			<view class="status-slot">
+				<!-- 
+				 0=正常(非售后订单),20=仅退款,21=卖家同意,22=卖家驳回,
+				 30=退货退款,31=卖家同意(用户填写退货单号),
+				 32=卖家驳回,33=等待卖家收货确认中,
+				 34=卖家确认收货,验货没问题金额已原路退回
+				 -->
+				<text v-if="data.sub_status == 20">仅退款,客服审核中</text>
+				<text v-if="data.sub_status == 21">仅退款,审核通过</text>
+				<text v-if="data.sub_status == 22">仅退款,审核失败</text>
+				<text v-if="data.sub_status == 30">退货退款,客服审核中</text>
+				<text v-if="data.sub_status == 31">审核通过,请填写退货快递单号</text>
+				<text v-if="data.sub_status == 32">审核失败</text>
+				<text v-if="data.sub_status == 33">等待卖家收货确认</text>
+				<text v-if="data.sub_status == 34">卖家已确认收货,金额原路退回</text>
+			</view>
+		</OrderGoodsCard>
+		<view class="content" :class="{ showBorderBottom }" v-for="(item,index) in data.order_details" :key="item.id">
+			<view class="pic">
+				<u-image width="180rpx" height="180rpx" :src="item.goods_cover"></u-image>
+			</view>
+			<view class="info">
+				<view class="title">{{ item.goods_title }}</view>
+				<view class="desc">
+					<view class="sku item">
+						<text>规格</text>
+						<text>{{ item.goods_sku }}</text>
+					</view>
+					<!-- <view class="express item">
+						<text>运费</text>
+						<text>{{ data.order_details.express || '包邮' }}</text>
+					</view> -->
+				</view>
+				<view class="price">
+					<text>¥</text>
+					<text>{{ item.goods_price_selling }}</text>
+				</view>
+			</view>
+			<view class="status">
+				<view class="status-label">
+					<slot></slot>
+				</view>
+				<view class="num">
+					<text>×</text>
+					<text>{{ item.goods_nums }}</text>
+				</view>
+			</view>
+		</view>
+		<!-- 待付款 -->
+		<view class="operate">
+			<u-button type="primary" size="small" shape="circle" @click.stop="goPay(data)">售后详情</u-button>
+		</view>
+		<!-- 待发货 -->
+		<view class="operate" v-if="data.status == 11">
+			<view class="paystyle">
+				实付款:{{data.actual_total_amount}}
+			</view>
+			<!-- <u-button type="primary" size="small" shape="circle" @click="pushDelivery(data)">提醒发货</u-button> -->
+			<u-button type="default" style="margin-right: 24rpx;" size="small" shape="circle" @click="applyAfterSales(data.id)">申请售后</u-button>
+		</view>
+		<!-- 待收货 -->
+		<view class="operate" v-if="data.status == 12">
+			<u-button type="default" style="margin-right: 24rpx;" size="small" shape="circle" @click="applyAfterSales(data.id)">申请售后</u-button>
+			<u-button type="primary" size="small" shape="circle" @click.stop="confirmReceipt(data)">确认收货</u-button>
+		</view>
+		<!-- 待评价 -->
+		<!-- <view class="operate" v-if="data.status == 3">
+			<u-button type="default" style="margin-right: 24rpx;" size="small" shape="circle" @click="applyAfterSales()">申请售后</u-button>
+			<u-button type="primary" size="small" shape="circle" @click="goEvaluate()">去评价</u-button>
+		</view> -->
+		<!-- 已完成 -->
+		<view class="operate" v-if="data.status == 13">
+			<view class="paystyle">
+				实付款:{{data.actual_total_amount}}
+			</view>
+			<!-- <u-button type="default" style="margin-right: 24rpx;" size="small" shape="circle" @click="deleteOrder()">删除记录</u-button>
+			<u-button type="primary" size="small" shape="circle" @click.stop="goOrderInfo(data.id)">查看详情</u-button> -->
+		</view>
+		<!-- 已完成 -->
+		<view class="operate" v-if="data.status == 14">
+			<!-- <view class="paystyle">
+				实付款:{{data.actual_total_amount}}
+			</view> -->
+			<!-- <u-button type="default" style="margin-right: 24rpx;" size="small" shape="circle" @click="deleteOrder()">删除记录</u-button>
+			<u-button type="primary" size="small" shape="circle" @click.stop="goOrderInfo(data.id)">查看详情</u-button> -->
+		</view>
+	</view>
+</template>
+
+<script>
+import { orderOperate } from '@/pages-mall/mixins/order-operate.js';
+import OrderGoodsCard from '@/pages-mall/components/order/order-goods-card.vue';
+export default {
+	name: 'order-card',
+	// 工单操作方法混入
+	mixins: [orderOperate],
+	components: {
+		OrderGoodsCard
+	},
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		}
+	},
+	data() {
+		return {};
+	},
+	methods: {
+		// 付款
+		goPayResult(e) {
+			
+		},
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	background-color: $app-theme-bg-color;
+	border-radius: 16rpx;
+	box-shadow: $app-theme-shadow;
+	padding: 30rpx;
+	margin-top: 20rpx;
+	.goods {
+		display: flex;
+		align-items: center;
+		align-content: space-between;
+		padding-bottom: 26rpx;
+		border-bottom: 1rpx solid $app-theme-border-color;
+		margin-bottom: 32rpx;
+		.pic {
+			margin-right: 24rpx;
+		}
+		.info {
+			width: 280rpx;
+			margin-right: 20rpx;
+			.title {
+				width: 100%;
+				display: -webkit-box;
+				overflow: hidden;
+				-webkit-line-clamp: 2;
+				-webkit-box-orient: vertical;
+				font-size: 28rpx;
+				font-weight: 400;
+				color: $app-theme-text-black-color;
+				margin-bottom: 14rpx;
+			}
+			.desc {
+				display: flex;
+				justify-content: flex-start;
+				align-items: flex-end;
+				margin-bottom: 18rpx;
+				.item {
+					margin-right: 24rpx;
+					text:nth-child(1) {
+						font-size: 24rpx;
+						color: $app-theme-card-gray-color;
+						margin-right: 8rpx;
+					}
+					text:nth-child(2) {
+						font-size: 24rpx;
+						color: $app-theme-card-gray-deep-color;
+					}
+				}
+			}
+			.price {
+				text:nth-child(1) {
+					font-size: 16rpx;
+					color: $app-theme-text-black-color;
+				}
+				text:nth-child(2) {
+					font-size: 28rpx;
+					color: $app-theme-text-black-color;
+				}
+			}
+		}
+		.status {
+			width: 120rpx;
+			.status-label {
+				width: 100%;
+				text-align: right;
+				font-size: 28rpx;
+				color: $app-theme-color;
+				margin-bottom: 100rpx;
+			}
+			.num {
+				width: 100%;
+				text-align: right;
+				text:nth-child(1) {
+					font-size: 24rpx;
+					color: $app-theme-text-gray-white-color;
+					vertical-align: bottom;
+				}
+				text:nth-child(2) {
+					font-size: 28rpx;
+					color: $app-theme-text-gray-white-color;
+					vertical-align: bottom;
+				}
+			}
+		}
+	}
+}
+.operate {
+	display: flex;
+	justify-content: flex-end;
+	align-items: center;
+}
+.paystyle{
+	// font-weight: bold;
+	margin-right: 20rpx;
+	color: $app-theme-text-money-color;
+}
+</style>

+ 237 - 0
pages-mall/components/after-sales/select-goods-pop.vue

@@ -0,0 +1,237 @@
+<template>
+	<u-popup v-model="show"  mode="bottom" height="750rpx">
+		<view class="list">
+			<view class="item" v-for="(goods, index) in goodsList" :key="index">
+				<view class="left">
+					<u-checkbox shape="circle" active-color="#22ac38" icon-size="16" v-model="goods.checked" @change="checkGoods"></u-checkbox>
+				</view>
+				<view class="right" @click="goodsList[index].checked = !goodsList[index].checked">
+					<orderDetailGoodsCard :data="goods"></orderDetailGoodsCard>
+				</view>
+				
+			</view>
+		</view>
+		<!-- 底部按钮 -->
+		<view class="btn">
+			<view class="inner">
+				<view class="check">
+					<u-checkbox shape="circle" active-color="#22ac38" icon-size="16" v-model="checkAllStauts" @change="checkAllGoods"></u-checkbox>
+					<text>全选</text>
+				</view>
+				<view class="right">
+					<!-- ({{ goodsList.filter(item => item.checked).length }}) -->
+					<u-button type="primary" @click="confirmSel">确认选择</u-button>
+				</view>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+var _self;
+import orderDetailGoodsCard from '@/pages-mall/components/order/orderdetail-goods-card.vue';
+export default {
+	components: {
+		orderDetailGoodsCard
+	},
+	data() {
+		return {
+			show:false,
+			orderInfo:{},
+			goodsList:[],
+			checkAllStauts: false,
+		};
+	},
+	computed: {
+		checkAll() {
+			// this.totalPrice=0;
+			// this.goodsList.map(item => (item.checked ? (this.totalPrice += Number(item.amount) * item.nums) : 0));
+			if (this.goodsList.filter(item => item.checked).length == this.goodsList.length) {
+				this.checkAllStauts = true;
+			} else {
+				this.checkAllStauts = false;
+			}
+		}
+	},
+	onLoad() {
+		_self = this;
+	},
+	
+	methods: {
+		showPop(data){
+			console.log('data>>>',data);
+			this.show = true;
+			this.goodsList=[];
+			data.order_goods_info.forEach(e=>{
+				console.log(e);
+				if(e.sub_status=='10'){
+					e.checked = false;
+					this.goodsList.push(e);
+				}
+			});
+			console.log(this.goodsList);
+			this.$forceUpdate();
+		},
+		// 确认选择
+		//  :disabled="goodsList.filter(item => item.checked).length<=0"
+		confirmSel(){
+			let gs = [];
+			this.goodsList.forEach(item => {
+				if(item.checked){
+					gs.push(item);
+				}
+			});
+			this.$emit('done',gs);
+			this.show = false;
+			
+		},
+		// 全选?
+		checkAllGoods() {
+			if (this.checkAllStauts) {
+				this.goodsList.forEach(item => (item.checked = false));
+			} else {
+				this.goodsList.forEach(item => (item.checked = true));
+			}
+		},
+		// 单选
+		checkGoods(e){
+			
+			this.$forceUpdate();
+		},
+	
+	
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.page {
+	background: $app-theme-bg-gray-color;
+	padding: 24rpx;
+}
+.list {
+	padding-bottom: 120rpx;
+	.item {
+		display: flex;
+		align-items: center;
+		background-color: $app-theme-bg-color;
+		padding: 30rpx 24rpx 30rpx 24rpx;
+		border-bottom: 1rpx solid $app-theme-border-color;
+		position: relative;
+		.left {
+			display: flex;
+			align-items: center;
+			width: 46rpx;
+		}
+		.right {
+			width: calc(100% - 46rpx);
+			display: flex;
+			align-items: center;
+			.img {
+				width: 160rpx;
+				height: 160rpx;
+				margin-right: 20rpx;
+				image {
+					width: 100%;
+					height: 100%;
+				}
+			}
+			.info {
+				width: 418rpx;
+				.title {
+					font-size: 28rpx;
+					font-family: PingFang-SC-Regular, PingFang-SC;
+					font-weight: 400;
+					color: $app-theme-text-black-color;
+					padding-bottom: 16rpx;
+				}
+				.sku {
+					font-size: 22rpx;
+					font-family: PingFang-SC-Regular, PingFang-SC;
+					font-weight: 400;
+					background: $app-theme-bg-gray-color;
+					border-radius: 2px;
+					color: $app-theme-card-gray-deep-color;
+					padding: 4rpx 16rpx;
+				}
+				.operate {
+					padding-top: 30rpx;
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					.price {
+						text:nth-child(1) {
+							font-size: 24rpx;
+							font-family: PingFangSC-Medium, PingFang SC;
+							font-weight: 500;
+							color: $app-theme-text-money-color;
+						}
+						text:nth-child(2) {
+							font-size: 36rpx;
+							font-family: PingFangSC-Regular, PingFang SC;
+							font-weight: 400;
+							color: $app-theme-text-money-color;
+						}
+					}
+					.num {
+					}
+				}
+			}
+		}
+	}
+}
+
+.btn {
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	width: 100%;
+	background-color: $app-theme-bg-color;
+	min-height: 100rpx;
+
+	.inner {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+		.check {
+			padding-left: 30rpx;
+			display: flex;
+			align-items: center;
+			text {
+				font-size: 28rpx;
+				font-weight: 400;
+				color: $app-theme-text-black-color;
+			}
+		}
+		.right {
+			display: flex;
+			align-items: center;
+			.price {
+				margin-right: 54rpx;
+				text:nth-child(1) {
+					font-size: 28rpx;
+					font-weight: 400;
+					color: $app-theme-text-black-color;
+				}
+				text:nth-child(2) {
+					font-size: 24rpx;
+					font-weight: 500;
+					color: $app-theme-text-money-color;
+				}
+				text:nth-child(3) {
+					font-size: 36rpx;
+					font-weight: 400;
+					color: $app-theme-text-money-color;
+				}
+			}
+			/deep/button {
+				height: 100%;
+				line-height: 100rpx;
+				border-radius: 0 !important;
+			}
+		}
+	}
+}
+</style>

+ 75 - 0
pages-mall/components/after-sales/select-item.vue

@@ -0,0 +1,75 @@
+<template>
+	<view class="slot">
+		<view class="item" v-for="(item, index) in ops" :key="index" @click="change(item, index)">
+			<view class="left">
+				<u-image :src="item.icon" width="40rpx" height="40rpx" style="margin-right: 24rpx;"></u-image>
+				<view class="info">
+					<view class="title">{{ item.title }}</view>
+					<view class="desc">{{ item.desc }}</view>
+				</view>
+			</view>
+			<view class="right"><u-icon size="20" name="arrow-right" :color="arrowColor"></u-icon></view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		ops: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		}
+	},
+	data() {
+		return {
+			arrowColor: this.$appTheme.appThemeTextGrayColor
+		};
+	},
+	methods: {
+		change(item, index) {
+			this.$emit('change', { item, index });
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	padding: 30rpx;
+	box-shadow: $app-theme-shadow;
+	border-radius: 16rpx;
+	background-color: $app-theme-bg-color;
+	.item {
+		margin-bottom: 54rpx;
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		&:last-child {
+			margin-bottom: 0;
+		}
+		.left {
+			display: flex;
+			justify-content: flex-start;
+			align-items: center;
+			.info {
+				.title {
+					font-size: 28rpx;
+					color: $app-theme-text-black-color;
+					margin-bottom: 8rpx;
+				}
+				.desc {
+					font-size: 24rpx;
+					color: $app-theme-shop-gray-color;
+				}
+			}
+		}
+		.right {
+			display: flex;
+			align-items: center;
+		}
+	}
+}
+</style>

+ 88 - 0
pages-mall/components/evaluate-card.vue

@@ -0,0 +1,88 @@
+<template>
+	<view class="slot" :style="showBorderBottom ? '' : 'border-bottom:none;margin-bottom:0'">
+		<view class="user">
+			<view class="info">
+				<u-avatar size="80" src="../../../static/user/1.png"></u-avatar>
+				<view class="basic">
+					<view class="name">{{ data.userName }}</view>
+					<view class="evaluate"><u-rate :count="5" v-model="data.star" disabled :inactive-color="rateInActiveColor" :active-color="rateActiveColor"></u-rate></view>
+				</view>
+			</view>
+			<view class="date">{{ data.date }}</view>
+		</view>
+		<view class="content">{{ data.content }}</view>
+		<!-- <view class="pictures"><u-image width="164rpx" height="164rpx" src="../../../static/goods/4.png" v-for="(pic, cur) in data.pics" :key="cur"></u-image></view> -->
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'evaluate-card',
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		// 是否显示底部边框
+		showBorderBottom: {
+			type: Boolean,
+			default: false
+		}
+	},
+	data() {
+		return {
+			rateActiveColor: this.$appTheme.appThemeColor,
+			rateInActiveColor: this.$appTheme.appThemeCardGrayColor
+		};
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	border-bottom: 1rpx solid $app-theme-border-color;
+	padding-bottom: 24rpx;
+	margin-bottom: 24rpx;
+}
+.user {
+	display: flex;
+	justify-content: space-between;
+	align-items: flex-end;
+	.info {
+		display: flex;
+		justify-content: flex-start;
+		.basic {
+			margin-left: 16rpx;
+			display: flex;
+			justify-content: flex-start;
+			flex-wrap: wrap;
+			align-content: space-between;
+			.name {
+				width: 100%;
+				font-size: 28rpx;
+				color: $app-theme-text-black-color;
+				// 为了与 u-rate 组件左侧视觉对齐
+				padding-left: 10rpx;
+			}
+			.evaluate {
+			}
+		}
+	}
+	.date {
+		font-size: 26rpx;
+		color: $app-theme-text-gray-color;
+	}
+}
+.content {
+	padding-top: 26rpx;
+	padding-bottom: 24rpx;
+	font-size: 28rpx;
+	color: $app-theme-text-black-color;
+}
+
+.pictures {
+}
+</style>

+ 200 - 0
pages-mall/components/express/list.vue

@@ -0,0 +1,200 @@
+<template>
+	<view class="bg-white margin-top">
+		<view class="common-logistics">
+			<view class="logistic-item" v-for="(item, index) in logisticsData" :key="index">
+				<view class="total-wrap" :style="{ marginTop: item.isFirstNode ? '22rpx' : '6rpx' }">
+					<view class="item-container">
+						<view class="item-container-left" :class="[index == 0 ? 'text-1A1A1A' : 'text-808080']">
+							<view class="text-df">{{ item.time.split(" ")[0] }}</view>
+							<view class="text-sm">{{ item.time.split(" ")[1] }}</view>
+						</view>
+						<view class="item-container-center">
+							<view class="tag-container">
+								<image v-if="item.isFirstNode && String(item.status) != 'null'" :src="nodeIconUrl(item.status, index)" mode="scaleToFill"></image>
+								<view v-else class="item-tag-container">
+									<image
+										class="item-tag"
+										:src="[index == 0 ? '/pages-mall/static/express-dot-active.png' : '/pages-mall/static/express-dot.png']"
+										mode="scaleToFill"
+									></image>
+								</view>
+							</view>
+							<!--  :style="{ height: item.isFirstNode ? '145rpx' : '88rpx', paddingTop: item.isFirstNode ? '22rpx' : '8rpx' }" -->
+							<view class="line-container" :style="{ height: '88rpx', paddingTop:  '8rpx' }">
+								<!-- :style="{ height: item.isFirstNode ? '120rpx' : '80rpx' }" -->
+								<view v-if="index !== logisticsData.length - 1" class="line"  :style="{ height:'120rpx' }"></view>
+							</view>
+						</view>
+						<!--  :style="{ paddingTop: item.isFirstNode ? '0' : '8rpx' }" -->
+						<view class="item-container-right">
+						<!-- 	<view v-if="item.isFirstNode" class="item-title text-dm text-bold" :class="[index == 0 ? 'text-1A1A1A' : 'text-808080']">{{ item.status }}</view> -->
+						<!--  :style="{ marginTop: item.isFirstNode ? '10rpx' : '0' }" -->
+							<view class="item-desc text-dm" :class="[index == 0 ? 'text-1A1A1A' : 'text-999999']">
+								{{ item.status }}
+							</view>
+							<view class="item-time">{{ item.createTime }}</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		logisticsData: {
+			type: [Object, Array]
+		}
+	},
+	computed: {
+		nodeIconUrl() {
+			return function(data, isFirstIndex) {
+				// 0在途,1揽收,2疑难,3签收,4退签,5派件,6退回,7转单,10待清关,11清关中,12已清关,13清关异常,14收件人拒签
+				// 图标根据需要自行更改,这里只作演示
+				if (data == '在途') {
+					return isFirstIndex === 0 ? require('@/pages-mall/static/express-ing.png') : require('@/pages-mall/static/express-ing.png');
+				} else if (data == '揽收') {
+					return isFirstIndex === 0 ? require('@/pages-mall/static/express-ing.png') : require('@/pages-mall/static/express-ing.png');
+				} else if (data == '在途') {
+					return isFirstIndex === 0 ? require('@/pages-mall/static/express-ing.png') : require('@/pages-mall/static/express-ing.png');
+				} else if (data == '疑难') {
+					return isFirstIndex === 0 ? require('@/pages-mall/static/express-ing.png') : require('@/pages-mall/static/express-ing.png');
+				} else if (data == '签收') {
+					return isFirstIndex === 0 ? require('@/pages-mall/static/express-over.png') : require('@/pages-mall/static/express-over.png');
+				} else if (data == '退签') {
+					return isFirstIndex === 0 ? require('@/pages-mall/static/express-ing.png') : require('@/pages-mall/static/express-ing.png');
+				} else if (data == '派件') {
+					return isFirstIndex === 0 ? require('@/pages-mall/static/express-ing.png') : require('@/pages-mall/static/express-ing.png');
+				} else if (data == '退回') {
+					return isFirstIndex === 0 ? require('@/pages-mall/static/express-ing.png') : require('@/pages-mall/static/express-ing.png');
+				}
+			};
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.common-logistics {
+	height: auto;
+	box-sizing: border-box;
+	padding: 20rpx 20rpx 50rpx;
+}
+
+.item-container {
+	width: 100%;
+	height: auto;
+	display: flex;
+
+	.item-container-left {
+		width: 160rpx;
+		// max-width: 120rpx;
+	}
+
+	.item-container-center {
+		width: 44rpx;
+		height: auto;
+
+		.tag-container {
+			width: 44rpx;
+			height: 44rpx;
+
+			image {
+				width: 44rpx;
+				height: 44rpx;
+				border-radius: 50%;
+			}
+
+			.item-tag-container {
+				width: 44rpx;
+				height: 44rpx;
+				display: flex;
+				justify-content: center;
+				align-items: center;
+
+				.item-tag {
+					width: 48rpx;
+					height: 48rpx;
+					border-radius: 50%;
+				}
+			}
+		}
+
+		.line-container {
+			box-sizing: border-box;
+			width: 44rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+
+			.line {
+				width: 2rpx;
+				background-color: #dcdcdc;
+			}
+		}
+	}
+
+	.item-container-right {
+		width: 500rpx;
+		// max-width: 510rpx;
+		box-sizing: border-box;
+		padding: 0 10rpx 0 24rpx;
+
+		.item-title {
+			width: 100%;
+			height: 40rpx;
+			line-height: 44rpx;
+			color: #222;
+			font-size: 28rpx;
+		}
+
+		.item-desc {
+			margin-top: 16rpx;
+			width: 100%;
+			min-height: 30rpx;
+			line-height: 30rpx;
+			word-wrap: break-word;
+			word-break: normal;
+		}
+
+		.item-time {
+			margin-top: 12rpx;
+			width: 100%;
+			height: 34rpx;
+			line-height: 34rpx;
+			font-size: 24rpx;
+		}
+	}
+}
+
+.line-state {
+	width: 20rpx;
+	height: 20rpx;
+	border-radius: 50%;
+}
+
+.take-space {
+	width: 100%;
+	height: 80rpx;
+}
+
+.text-1A1A1A {
+	color: #1a1a1a;
+}
+
+.text-999999 {
+	color: #999999;
+}
+
+.text-808080 {
+	color: #808080;
+}
+.text-df{
+	font-size: 28rpx;
+}
+.text-sm{
+	font-size: 24rpx;
+}
+</style>

+ 29 - 0
pages-mall/components/express/utils.js

@@ -0,0 +1,29 @@
+import Vue from 'vue';
+export const setAttribute = function(data) {
+	if (Array.isArray(data) && data.length > 0) {
+		return data.map((item, index) => {
+			Vue.set(item, 'isFirstNode', false)
+			return item
+		})
+	} else {
+		return []
+	}
+}
+
+export const changeAttribute = function(testStrList, targetList) {
+	let cacheData = targetList;
+	testStrList.forEach((item, index) => {
+		let result_Index = targetList.findIndex(function(f_item, f_index) {
+			return String(f_item.status) == item
+		})
+		if (result_Index != -1) {
+			cacheData[result_Index].isFirstNode = true;
+		}
+	})
+	return cacheData;
+}
+
+export default {
+	setAttribute,
+	changeAttribute
+}

+ 120 - 0
pages-mall/components/goods/goods-info.vue

@@ -0,0 +1,120 @@
+<template>
+	<view class="goods_info">
+		<view class="title">
+			<u-tag text="HOT" type="error" size="mini" mode="dark" v-if="data.tags_type=='hot'" />
+			<u-tag text="推荐" type="success" size="mini" mode="dark" v-if="data.tags_type=='recommend'" />
+			<text>{{ data.title }}</text>
+		</view>
+		<view class="count">
+			<view class="price">
+				<text>¥</text>
+				<text>{{ selectedSku.price_selling||data.price_selling }}</text>
+				<!-- <text>¥{{ data.oldPrice }}</text> -->
+			</view>
+			<view class="sale">
+				<text>月销</text>
+				<!-- <text>{{ $replaceSale(data.stock_sales) }}</text> -->
+				<text>{{ data.stock_sales}}</text>
+			</view>
+		</view>
+		<!-- <view class="notice">
+			<text>恭喜小主,您有优惠券可用哦</text>
+			<text>查看</text>
+			<u-icon name="arrow-right" :color="appThemeBuyBtnBgColor" size="24"></u-icon>
+		</view> -->
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'goods-info',
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		// 选中的sku
+		selectedSku: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		// 商品类型
+		goodsType: {
+			type: String,
+			default: ''
+		}
+	},
+	data() {
+		return {
+			appThemeColor: this.$appTheme.appThemeColor
+		};
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.goods_info {
+	margin-bottom: 16rpx;
+	background-color: $app-theme-bg-color;
+}
+.count {
+	display: flex;
+	justify-content: space-between;
+	align-items: flex-end;
+	padding: 0 30rpx 26rpx;
+	.price {
+		font-weight: bold;
+		text:nth-child(1) {
+			font-size: 28rpx;
+			color: $app-theme-text-money-color;
+		}
+		text:nth-child(2) {
+			font-size: 40rpx;
+			color: $app-theme-text-money-color;
+			margin-right: 24rpx;
+		}
+		text:nth-child(3) {
+			font-size: 28rpx;
+			color: $app-theme-card-gray-light-color;
+			text-decoration-line: line-through;
+		}
+	}
+.sale {
+		text:nth-child(1) {
+			font-size: 28rpx;
+			color: $app-theme-card-gray-deep-color;
+			margin-right: 8rpx;
+		}
+		text:nth-child(2) {
+			font-size: 28rpx;
+			color: $app-theme-card-gray-deep-color;
+		}
+	}
+}
+.title {
+	padding: 26rpx 30rpx 16rpx;
+	text {
+		vertical-align: middle;
+		padding-left: 8rpx;
+		font-size: 32rpx;
+		font-weight: 500;
+		color: $app-theme-text-black-deep-color;
+	}
+}
+.notice {
+	padding: 0 30rpx 30rpx 30rpx;
+	font-size: 28rpx;
+	text:nth-child(1) {
+		color: $app-theme-text-gray-color;
+		margin-right: 20rpx;
+	}
+	text:nth-child(2) {
+		color: $app-theme-buybtn-bg-color;
+	}
+}
+</style>

+ 282 - 0
pages-mall/components/goods/goods-select-sku.vue

@@ -0,0 +1,282 @@
+<template>
+	<u-popup v-model="show" mask mode="bottom" mask-close-able closeable border-radius="16" safe-area-inset-bottom>
+		<view class="info">
+			<view class="pic"><u-image :src="data.cover" width="190rpx" height="190rpx"></u-image></view>
+			<view class="desc">
+				<view class="price">
+					<text>¥</text>
+					<text>{{ selectdSku.price_selling }}</text>
+				</view>
+				<view class="select">
+					<text>已选</text>
+					<text>{{ selectdSku.difference }}</text>
+				</view>
+			</view>
+		</view>
+		<view class="sku">
+			<view class="title">规格</view>
+			<view class="list">
+				<view class="item" :class="{ 'select': item.id == selectdSku.id,'disable':(item.stock_total<=0||Number(item.price_selling)<=0)}" v-for="(item, index) in data.sku" :key="index" @click="selectSkuFun(index)">
+					{{ item.difference }}
+					<span class="price">(优惠价:&yen;{{item.price_selling}})</span>
+				</view>
+			</view>
+		</view>
+		<view class="num">
+			<view class="title">数量</view>
+			<!-- :disabled-input="true" -->
+			<!-- {{selectdSku.stock_total}} -->
+			<u-number-box v-model="num" :min="1" :max="maxBuy"  @change="changeNum"></u-number-box>
+		</view>
+		<view class="operate">
+			<view class="soldOutBtn btn shu-elip-1" v-if="selectdSku.stock_total<=0||Number(data.price_selling)<=0">已售罄</view>
+			<template v-else>
+				<view class="joinCartBtn btn shu-elip-1" @click="addCartHandle">加入购物车</view>
+				<view class="buyBtn btn shu-elip-1" @click="bugNowHandle">立即购买</view>
+			</template>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+export default {
+	props:{		
+		expressInfo: {
+			type: Object,
+			default: ()=>{
+				return {max_buy_nums:-1}
+			}
+		}
+	},
+	computed:{
+		maxBuy(){
+			if(this.expressInfo.max_buy_nums==-1 || this.expressInfo.max_buy_nums>Number(this.selectdSku.stock_total)){
+				return Number(this.selectdSku.stock_total)
+			}else{
+				return this.expressInfo.max_buy_nums;
+			}
+		}
+	},
+	data() {
+		return {
+			show: false,
+			// 数据源
+			data: {},
+			
+			// 数量
+			num: 1,
+			selectdSkuIndex:0,
+			selectdSku:{
+				id:'',
+				difference:'',
+				price_selling:null,
+				stock_total:0,
+			},
+		};
+	},
+	methods: {
+		// 打开popup
+		open(data, index) {
+			console.log(333);
+			this.show = true;
+			this.data = data;
+			// this.selectSkuFun();
+			// this.selectdSku = selectSku;
+		},
+		initSkuFun(data){
+			this.data = data;
+			// 默认
+			this.selectdSkuIndex = this.data.sku[1]?1:0;
+			if(this.data.sku[this.selectdSkuIndex].stock_total<=0&&this.data.sku.findIndex(e=>e.stock_total>0)>-1){
+				this.selectdSkuIndex = this.data.sku.findIndex(e=>e.stock_total>0);
+				
+			}
+			this.selectSkuFun(this.selectdSkuIndex);
+		},
+		// 切换sku
+		selectSkuFun(index) {
+			console.log(index);
+			// if(this.data.sku[selI].stock_total<0||Number(this.data.sku[selI].price_selling)<=0){
+			// 	return false;
+			// }
+			this.selectdSkuIndex = index;
+			this.selectdSku = this.data.sku[index];
+			this.$emit('change', this.selectdSku);
+		},
+
+		// 数量改变时
+		changeNum(e) {},
+
+		// 关闭
+		close() {},
+
+		// 加入购物车
+		addCartHandle() {
+			// uni.showToast({
+			// 	title: '添加购物车成功',
+			// 	icon: 'none'
+			// });
+			this.$u.api.addCartAjax({
+				goods_id:this.data.id,
+				nums: this.num,
+				sku_id:this.selectdSku.id,//1是测试,应该传入skuId,外层没有返回,需要接口返回一下
+			}).then(({code})=>{
+				if(code==1){
+					this.$u.toast('添加购物车成功');
+					this.show = false;
+					// setTimeout(()=>{
+					// 	this.show = false;
+					// },800)
+				}
+			})
+		},
+
+		// 立即购买
+		bugNowHandle() {
+			const token = uni.getStorageSync('token');
+			if(!token){
+				uni.navigateTo({
+					url:'/pages/login/index'
+				})
+				return false;
+			}
+			// 数组形式,兼容多商品同时购买
+			let orderInfo = [
+				{
+					id: this.data.id,
+					cover: this.data.cover,
+					isbn:this.data.isbn,
+					price_selling:this.data.price_selling,
+					publish:this.data.publish,
+					title:this.data.title,
+					nums:this.num,					
+					selectdSku:this.selectdSku,
+				}
+			]
+			// return;
+					// expresstemplate:this.data.expresstemplate
+			uni.navigateTo({
+				url: '/pages-mall/pages/order/submit?orderInfo='+encodeURIComponent(JSON.stringify(orderInfo))
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.info {
+	padding: 30rpx 30rpx 0 30rpx;
+	display: flex;
+	align-items: flex-end;
+	.pic {
+		margin-right: 30rpx;
+	}
+	.desc {
+		.price {
+			margin-bottom: 20rpx;
+			text:nth-child(1) {
+				font-size: 24rpx;
+				color: $app-theme-text-money-color;
+				vertical-align: bottom;
+			}
+			text:nth-child(2) {
+				font-size: 36rpx;
+				color: $app-theme-text-money-color;
+				vertical-align: bottom;
+			}
+		}
+		.select {
+			text:nth-child(1) {
+				font-size: 24rpx;
+				color: $app-theme-card-gray-deep-color;
+				margin-right: 20rpx;
+			}
+			text:nth-child(2) {
+				font-size: 24rpx;
+				color: $app-theme-text-black-color;
+			}
+		}
+	}
+}
+.sku {
+	padding: 30rpx 30rpx 0 30rpx;
+	.title {
+		font-size: 28rpx;
+		color: $app-theme-text-black-color;
+		margin-bottom: 16rpx;
+	}
+	.list {
+		// display: flex;
+		// justify-content: flex-start;
+		// align-items: center;
+		// flex-wrap: wrap;
+		.item {
+			height: 80rpx;
+			padding: 0 30rpx;
+			margin-bottom: 24rpx;
+			// margin-right: 20rpx;
+			// background-color: $app-theme-sku-gray-color;
+			font-size: 28rpx;
+			// color: $app-theme-text-black-color;
+			border: 1rpx solid #eee;
+			color: $app-theme-color;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			border-radius: 10rpx;
+			&.select {
+				// color: $app-theme-color;
+				font-weight: bold;
+				border: 1rpx solid $app-theme-color;
+			}
+			&.disable{
+				color: #ddd;
+			}
+		}
+	}
+}
+.num {
+	padding: 30rpx;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	.title {
+		font-size: 28rpx;
+		color: $app-theme-text-black-color;
+	}
+}
+.operate {
+	display: flex;
+	align-items: center;
+	height: 100rpx;
+	padding-left: 20rpx;
+	/* /deep/button {
+		height: 100rpx;
+		line-height: 100rpx;
+		border-radius: 0 !important;
+		&::after {
+			border: initial;
+		}
+	} */
+	// .btn {
+	// 	min-width: 220rpx;
+	// 	line-height: 66rpx;
+	// 	padding: 0 30rpx;
+	// 	border-radius: 36rpx;
+	// 	color: #ffffff;
+	// 	margin-right: 20rpx;
+	// 	text-align: center;
+	// 	flex: 1;
+	// 	padding: 0 30rpx;
+	// }
+	// .soldOutBtn{
+	// 	background-color: $app-theme-nobuy-bg-color;
+	// }
+	// .joinCartBtn {
+	// 	background-color: $app-theme-joincart-bg-color;
+	// }
+	// .buyBtn {
+	// 	background-color: $app-theme-buybtn-bg-color;
+	// }
+}
+</style>

+ 136 - 0
pages-mall/components/goods/goods-select.vue

@@ -0,0 +1,136 @@
+<template>
+	<view class="slot">
+		<view class="item" @click="openSkuSelect">
+			<view class="info">
+				<view class="label">已选:</view>
+				<view class="value">{{ selectedSku.difference || '请选择规格' }}</view>
+			</view>
+			<view class="operate"><u-icon name="more-dot-fill" :color="iconColor"></u-icon></view>
+		</view>
+		<!-- <view class="item" @click="goAddressPage">
+			<view class="info">
+				<view class="label">送至</view>
+				<view class="value">{{ selectedAddress.detail_address || '请选择地址' }}</view>
+			</view>
+			<view class="operate"><u-icon name="more-dot-fill" :color="iconColor"></u-icon></view>
+		</view> -->
+		<view class="item" v-if="expressInfo&&expressInfo.express_type">
+			<!-- {{JSON.stringify(expressInfo)}} -->
+			<view class="info" :class="{'red':['1','3','4'].includes(expressInfo.express_type)}">
+			
+				<!-- <view class="label">优惠:</view> -->
+				<!-- "express_type": "1",//运费类型:1=全场包邮,2=全场不包邮,3=全场满x包邮,4=全场满x包邮偏远地区除外 -->
+				<view class="value">{{expressInfo|filterExpresToTitle}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'goods-select',
+	props: {
+		selectedSku: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		selectedAddress: {
+			type: Object,
+			default: {}
+		},
+		expressInfo: {
+			type: Object,
+			default: ()=>{
+				return {express_type:null}
+			}
+		}
+	},
+	data() {
+		return {
+			iconColor: this.$appTheme.appThemeTextGrayColor,
+			addressInfo:{},
+		};
+	},
+	computed: {
+		
+	},
+	onLoad() {
+		
+	},
+	filters:{
+		filterExpresToTitle(e){
+			switch (Number(e.express_type)){
+				case 1:
+					return "全场包邮"
+					break;
+				case 2:
+					return "基础运费"+e.base_express_fee+"元"
+					break;
+				case 3:
+					return "全场满"+e.min_buy_amount+"元包邮"
+					break;
+				case 4:
+					return "全场满"+e.min_buy_amount+"包邮(偏远地区除外)"
+					break;
+				default:
+					break;
+			}
+		}
+	},
+	methods: {		
+		
+		// 通知打开sku选择框
+		openSkuSelect() {
+			this.$emit('openSku');
+		},		
+		// 跳转收货地址页面
+		goAddressPage() {
+			uni.navigateTo({
+				url: '/pages-mine/pages/address/list?isSelect=true&isBack=true'
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	padding: 30rpx;
+	background-color: #fff;
+	margin-bottom: 16rpx;
+	.item {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		margin-bottom: 30rpx;
+		&:last-child {
+			margin-bottom: 0;
+		}
+		.info {
+			display: flex;
+			align-items: center;
+			.label {
+				font-size: 28rpx;
+				font-weight: 400;
+				color: $app-theme-card-gray-deep-color;
+				margin-right: 30rpx;
+			}
+			.value {
+				font-size: 28rpx;
+				font-weight: 400;
+				color: $app-theme-text-black-color;
+			}
+			&.red{
+				.value{
+					color:$app-theme-text-money-color;
+					
+				}
+			}
+		}
+		.operate {
+		}
+	}
+}
+</style>

+ 197 - 0
pages-mall/components/goods/submitBar.vue

@@ -0,0 +1,197 @@
+<template>
+	<view class="">	
+		<view class="submitBar">
+			<view class="left">
+				<button class="item serviceBtn" open-type="contact" >
+					<u-icon :size="40" name="server-fill"></u-icon>
+					<view class="text shu-elip-1">客服</view>
+				</button>
+			<!-- 	<view class="item">
+					<u-icon name="server-fill" :size="46" :color="$u.color['contentColor']"></u-icon>
+					<view class="text shu-elip-1">客服</view>
+				</view> -->
+				<view class="item collection" @click="collectionFun">
+					<u-icon :name="isCollection==1?'star-fill':'star'" :color="isCollection==1?'#ed3f14':'#333'" :size="40" ></u-icon>
+					<view class="text shu-elip-1">{{isCollection==1?'已收藏':'收藏'}}</view>
+				</view>
+				<view class="item car" @click="goShoppingCart">
+					<u-badge class="car-num" :count="cartNums" type="error" :absolute="true" :offset="[-5, 2]" v-if="cartNums>0"></u-badge>
+					<u-icon name="shopping-cart" :size="40"></u-icon>
+					<view class="text shu-elip-1">购物车</view>
+				</view>
+			</view>
+			<view class="right">
+				<view class="soldOutBtn" v-if="data.stock_total<=0||Number(data.price_selling)<=0">
+					已售罄
+				</view>
+				<template v-else>
+					<view class="joinCartBtn" @click="addShoppingCart">加入购物车</view>
+					<view class="buyBtn" @click="buyNow">立即购买</view>
+				</template>
+			</view>
+		</view>
+		
+		<view class="siteBar"></view>
+	</view>
+</template>
+<script>
+export default {
+	name: 'goods-operate',
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		}
+	},
+	watch:{
+		data(val){
+			this.isCollection = val.is_collection;
+		}
+	},
+	data() {
+		return {
+			isCollection:false,
+			cartNums:0,
+		};
+	},
+	mounted(){
+		this.isCollection = this.data.is_collection;
+		this.getCartNums();
+	},
+	methods: {
+		// 获取购物车数量
+		getCartNums(){
+			this.$u.api.getCartNumsAjax().then(({code,data})=>{
+				console.log('购物车数量>>>>>>',data)
+				this.cartNums = data;
+			})
+		},
+		// 加入购物车
+		addShoppingCart() {
+			this.$emit('addShoppingCart');
+		},
+		collectionFun(){
+			const token = uni.getStorageSync('token');
+			if(!token){
+				uni.navigateTo({
+					url:'/pages/login/index'
+				})
+				return false;
+			}
+			// this.isCollection = this.isCollection==1?2:1
+			this.$u.api.collectonAjax(this.data.id).then(({code,msg,data})=>{
+				if(code==1){
+					this.$u.toast(msg);
+					this.isCollection = data.is_collection;
+				}
+			})
+		},
+		// 立即购买
+		buyNow() {
+			this.$emit('buyNow');
+		},
+
+		// 去购物车
+		goShoppingCart() {
+			uni.switchTab({
+				url: '/pages/mall/shopping-cart'
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+	.siteBar{
+		height: 120rpx;
+		// padding-bottom: constant(safe-area-inset-bottom);
+		// padding-bottom: env(safe-area-inset-bottom);
+		// box-sizing: content-box;		
+		@extend .safeAreaPad;
+	}
+.submitBar {
+	border-top: solid 2rpx #f2f2f2;
+	// padding: 16rpx 0;
+	
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	width: 100%;
+	height: 120rpx;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	background-color: $app-theme-bg-color;
+	z-index: $app-zIndex-fixed;
+	// padding-bottom: constant(safe-area-inset-bottom);
+	// padding-bottom: env(safe-area-inset-bottom);
+	// box-sizing: content-box;
+	@extend .safeAreaPad;
+	
+	.left {
+		display: flex;
+		font-size: 24rpx;
+		.item {
+			width: 90rpx;
+			// margin: 0 15rpx;
+			text-align: center;
+			&.car {
+				position: relative;
+				.car-num {
+					// position: absolute;
+					// top: -10rpx;
+					// right: -10rpx;
+				}
+			}
+			// &.collection{
+			// 	width: ;
+			// }
+		}
+		.text{
+			font-size: 24rpx;
+			line-height: 1.5;
+			margin-top: 5rpx;
+		}
+	}
+	.right {
+		flex: 1;
+		display: flex;
+		font-size: 28rpx;
+		align-items: center;
+		justify-content: space-evenly;
+		margin-left: 20rpx;
+		// width: 480rpx;
+		.btn {
+			// flex: 1;
+			min-width: 200rpx;
+			line-height: 66rpx;
+			// padding: 0 20rpx;
+			border-radius: 36rpx;
+			color: #ffffff;
+			// margin-right: 20rpx;
+			text-align: center;
+		}
+		.cart {
+			background-color: #ff7900;
+		}
+		.buy {
+			background-color: #ed3f14;
+		}
+	}
+}
+
+.serviceBtn{
+	background-color: transparent;
+	padding: 0;
+	border: none;
+	line-height: 1;
+	&::after{
+		content: '';
+		border: none;
+		display: none;
+	}
+}
+</style>

+ 111 - 0
pages-mall/components/line-info-operate.vue

@@ -0,0 +1,111 @@
+<template>
+	<view class="slot">
+		<view class="line">
+			<view class="label">商品总价</view>
+			<view class="value">{{ orderInfo.total_amount}}</view>
+		</view>
+		<view class="line">
+			<view class="label">优惠券</view>
+			<view class="value">-{{ orderInfo.coupon_amount}}</view>
+		</view>
+		<view class="line">
+			<view class="label">运费</view>
+			<view class="value">{{ orderInfo.express_fee}}</view>
+		</view>
+		<view class="line">
+			<view class="label">应付款</view>
+			<view class="value price">{{ orderInfo.actual_total_amount}}</view>
+		</view>
+		<u-line color="#eee" margin="0 0 30rpx" />
+		<view class="line">
+			<view class="label">订单编号</view>
+			<view class="value">
+				{{ orderInfo.order_no}}
+				<text class="copy" @click="$copyByUniappApi(orderInfo.order_no)">复制</text>
+			</view>
+		</view>
+		<view class="line">
+			<view class="label">收货地址</view>
+			<view class="value">{{ addressInfo.realname}}, {{ addressInfo.mobile}},  {{ addressInfo.province+addressInfo.city+addressInfo.area+addressInfo.street}}</view>
+		</view>
+		<view class="line">
+			<view class="label">留言</view>
+			<view class="value">{{ orderInfo.remark}}</view>
+		</view>
+		<view class="line">
+			<view class="label">创建时间</view>
+			<view class="value">{{ orderInfo.createtime}}</view>
+		</view>
+		<view class="line" v-if="orderInfo.pay_time">
+			<view class="label">付款时间</view>
+			<view class="value">{{ orderInfo.pay_time}}</view>
+		</view>
+		<view class="line" v-if="orderInfo.send_time">
+			<view class="label">发货时间</view>
+			<view class="value">{{ orderInfo.send_time}}</view>
+		</view>
+		<view class="line" v-if="orderInfo.ack_time">
+			<view class="label">成交时间</view>
+			<view class="value">{{ orderInfo.ack_time}}</view>
+		</view>
+		<view class="line" v-if="orderInfo.cancel_time">
+			<view class="label">取消时间</view>
+			<view class="value">{{ orderInfo.cancel_time}}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'line-info-operate',
+	props: {
+		// 配置项
+		orderInfo: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		addressInfo: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	padding: 30rpx 0rpx;
+}
+.line {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	margin-bottom: 30rpx;
+	.label {
+		// width: 204rpx;
+		font-size: 28rpx;
+		color: $app-theme-text-color;
+	}
+	.value {
+		// width: 320rpx;
+		max-width: 500rpx;
+		flex: 1;
+		padding: 0 30rpx;
+		text-align: right;
+		font-size: 28rpx;
+		color: $app-theme-text-color;
+	}
+	.price{
+		color: $app-theme-text-money-color;
+	}
+	.copy {
+		font-size: 28rpx;
+		color: $app-theme-color;
+		margin-left: 20rpx;
+	}
+}
+</style>

+ 102 - 0
pages-mall/components/navbar-search.vue

@@ -0,0 +1,102 @@
+<!-- 分类列表中的搜索 -->
+<template>
+	<view class="slot">
+		<view class="navbar" :style="[{ minHeight: navBarHeight + 'px' }]">
+			<view
+				class="inner"
+				:style="[
+					{ minHeight: menuHeight + 'px' },
+					{ lineHeight: menuHeight + 'px' },
+					{ paddingLeft: menuRight * 2 + 'px' },
+					{ paddingRight: menuRight * 2 + 'px' },
+					{ paddingTop: navBarHeight - menuHeight - menuTop + 'px' },
+					{ paddingBottom: '20rpx' }
+				]"
+			>
+				<view class="title-slot">
+					<u-icon size="32" name="arrow-leftward" @click="$u.route({ type: 'navigateBack', delta: 1 })"></u-icon>
+					<view class="title shu-elip-1">{{ title }}</view>
+					<view></view>
+				</view>
+				<view class="search-slot" :style="{ paddingTop: menuTop + 'px' }">
+					<u-search @click="goSearchPage" disabled :placeholder="placeholder" :showAction="false" bg-color="#F4F5F8"></u-search>
+				</view>
+			</view>
+		</view>
+		<view class="slot-height" :style="[{ height: navBarHeight + menuHeight + menuTop + 11 + 'px' }]"></view>
+	</view>
+</template>
+
+<script>
+const app = getApp();
+export default {
+	name: 'navbar-search',
+	props: {
+		// 标题
+		title: {
+			type: String,
+			default: '标题'
+		},
+		// 占位内容
+		placeholder: {
+			type: String,
+			default: '请输入'
+		}
+	},
+	data() {
+		return {
+			// 导航栏高度
+			menuTop: app.globalData.menuTop,
+			navBarHeight: app.globalData.navBarHeight,
+			menuRight: app.globalData.menuRight,
+			menuBotton: app.globalData.menuBotton,
+			menuHeight: app.globalData.menuHeight,
+			statusBarHeight: app.globalData.statusBarHeight
+		};
+	},
+	methods: {
+		// 去搜索页面
+		goSearchPage() {
+			uni.navigateTo({
+				url: '/pages/search'
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	width: 100vw;
+}
+.navbar {
+	width: 100%;
+	position: fixed;
+	top: 0;
+	left: 0;
+	z-index: 899;
+	overflow: hidden;
+}
+
+.inner {
+	width: 100%;
+	height: 100%;
+	background-color: $app-theme-bg-color;
+}
+
+.title-slot {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	.back {
+	}
+	.title {
+		font-size: 36rpx;
+		font-family: PingFangSC-Regular, PingFang SC;
+		color: $app-theme-text-black-color;
+		max-width: 350rpx;
+	}
+}
+.search-slot {
+}
+</style>

+ 123 - 0
pages-mall/components/navbar-tab-search.vue

@@ -0,0 +1,123 @@
+<template>
+	<view class="navbar" :style="[{ minHeight: navBarHeight + 'px' }]">
+		<view class="searchTabhead">
+			<Navbar :title="title"></Navbar>
+			<view class="searchbox" v-if="isSearch">
+				<u-search :placeholder="placeholder" :showAction="false"  bg-color="#F4F5F8"></u-search>
+			</view>
+			<!-- <view class="tab-slot" :style="{'height':navBarHeight+'px'}">
+				<view class="tab" v-for="(tab, index) in tabOps" :key="index" :class="{ active: tabIndex == index }" @click="change(tab, index)">{{ tab.label }}</view>
+			</view> -->
+			<view class="tab-slot" >
+				<u-tabs :is-scroll="true" :list="tabOps" :current="tabIndex" @change="change"></u-tabs>
+			</view>
+		</view>
+		<view v-if="isSearch" class="siteBar" :style="[{ height: navBarHeight*2 + menuHeight+statusBarHeight+ 'px' }]"></view>
+		<view class="siteBar" v-else :style="[{ height: navBarHeight + menuHeight+statusBarHeight+ 'px' }]"></view>
+	</view>
+</template>
+
+<script>
+const app = getApp();
+export default {
+	name: 'navbar-tab-search',
+	props: {
+		// 标题
+		title: {
+			type: String,
+			default: '标题'
+		},
+		// 占位内容
+		placeholder: {
+			type: String,
+			default: '请输入'
+		},
+		// 标题
+		isSearch: {
+			type: Boolean,
+			default: false
+		},
+		// tab配置项
+		tabOps: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		}
+	},
+	data() {
+		return {
+			// 导航栏高度
+			menuTop: app.globalData.menuTop,
+			navBarHeight: app.globalData.navBarHeight,
+			menuRight: app.globalData.menuRight,
+			menuBotton: app.globalData.menuBotton,
+			menuHeight: app.globalData.menuHeight,
+			statusBarHeight: app.globalData.statusBarHeight,
+
+			// 当前tab
+			tabIndex: 0
+		};
+	},
+	methods: {
+		// 切换tab
+		change(index) {
+			console.log(index);
+			this.tabIndex = index;
+			this.$emit('change', index);
+		},
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.navbar {
+	width: 100vw;
+	overflow: hidden;
+}
+.searchTabhead {
+	position: fixed;
+	top: 0;
+	left: 0;
+	z-index: 899;
+	width: 100%;
+	background-color: $app-theme-bg-color;
+}
+.searchbox{
+	width: 100%;
+	height: 100%;
+	padding: 0 30rpx;
+	box-sizing: border-box;
+	background-color: $app-theme-bg-color;
+}
+
+.tab-slot {
+	width: 100%;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	// padding: 0 30rpx;
+	box-sizing: border-box;
+	// padding-top: 20rpx;
+	.tab {
+		font-size: 26rpx;
+		font-weight: 400;
+		color: $app-theme-navbar-tab-color;
+		&.active {
+			color: $app-theme-navbar-tab-color-active;
+			position: relative;
+			&::before {
+				content: '';
+				position: absolute;
+				bottom: 0;
+				left: 50%;
+				transform: translate(-50%, -50%);
+				width: 20rpx;
+				height: 4rpx;
+				background: $app-theme-navbar-tab-color-active;
+				border-radius: 2rpx;
+			}
+		}
+	}
+}
+</style>

+ 80 - 0
pages-mall/components/order/express-goods-card.vue

@@ -0,0 +1,80 @@
+<template>
+	<view class="slot">
+		<view class="pic"><u-image width="180rpx" height="180rpx" :src="data.logo"></u-image></view>
+		<view class="info">
+			<view class="title">{{ data.expName }}</view>
+			<view class="express">
+				<view class="left">
+					<text>运单号</text>
+					<text>{{ data.number }}</text>
+				</view>
+				<view class="right" @click="$copyByUniappApi(data.number)">复制</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'express-goods-card',
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		}
+	},
+	data() {
+		return {};
+	},
+	methods: {
+		
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	position: relative;
+	padding: 30rpx;
+	background-color: $app-theme-color;
+	color: $app-theme-text-white-color;
+	margin-bottom: 24rpx;
+	display: flex;
+	align-items: flex-start;
+	.pic {
+		border-radius: 16rpx;
+		overflow: hidden;
+		margin-right: 24rpx;
+	}
+	.info {
+		.title {
+			font-size: 28rpx;
+			color: $app-theme-text-white-color;
+			margin-bottom: 24rpx;
+		}
+		.express {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			.left {
+				text:nth-child(1) {
+					font-size: 24rpx;
+					color: rgba($app-theme-text-white-color, 0.8);
+					margin-right: 16rpx;
+				}
+				text:nth-child(2) {
+					font-size: 24rpx;
+					color: $app-theme-text-white-color;
+				}
+			}
+			.right {
+				font-size: 24rpx;
+				color: rgba($app-theme-text-white-color, 0.8);
+			}
+		}
+	}
+}
+</style>

+ 179 - 0
pages-mall/components/order/order-card.vue

@@ -0,0 +1,179 @@
+<template>
+	<view class="slot">
+		<!-- 商品卡片 -->
+		<OrderGoodsCard :data="data" showBorderBottom>
+			<view class="status-slot">
+				<text v-if="data.status == 10">待付款</text>
+				<text v-if="data.status == 11">待发货</text>
+				<text v-if="data.status == 12">待收货</text>
+				<text v-if="data.status == 13">已完成</text>
+				<text v-if="data.status == 14">交易关闭</text>
+				<text v-if="data.status == 15">售后订单</text>
+			</view>
+		</OrderGoodsCard>
+		<!-- 待付款 -->
+		<view class="operate" v-if="data.status == 10">
+			<view class="paystyle">
+				需付款:{{data.actual_total_amount}}
+			</view>
+			<u-button type="default" style="margin-right: 24rpx;" size="small" shape="circle" @click.stop="cancelOrder(data.id,'orderList')">取消订单</u-button>
+			<u-button type="primary" size="small" shape="circle" @click.stop="goPay(data)">去付款</u-button>
+		</view>
+		<!-- 待发货 -->
+		<view class="operate" v-if="data.status == 11">
+			<view class="paystyle">
+				实付款:{{data.actual_total_amount}}
+			</view>
+			<u-button type="default" style="margin-right: 24rpx;" size="small" shape="circle" @click="applyAfterSales(data.id)">申请售后</u-button>
+		</view>
+		<!-- 待收货 -->
+		<view class="operate" v-if="data.status == 12">
+			<u-button type="default" style="margin-right: 24rpx;" size="small" shape="circle" @click="applyAfterSales(data.id)">申请售后</u-button>
+			<u-button type="primary" size="small" shape="circle" @click.stop="confirmReceipt(data)">确认收货</u-button>
+		</view>
+		<!-- 已完成 -->
+		<view class="operate" v-if="data.status == 13">
+			<view class="paystyle">
+				实付款:{{data.actual_total_amount}}
+			</view>
+			<u-button type="primary" size="small" shape="circle" @click.stop="goOrderInfo(data.id)">订单详情</u-button>
+		</view>
+		<view class="operate" v-if="data.status == 15">
+			<view class="paystyle">
+				实付款:{{data.actual_total_amount}}
+			</view>
+			<u-button type="primary" size="small" shape="circle" @click.stop="goOrderInfo(data.id)">订单详情</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+import { orderOperate } from '@/pages-mall/mixins/order-operate.js';
+import OrderGoodsCard from '@/pages-mall/components/order/order-goods-card.vue';
+export default {
+	name: 'order-card',
+	// 工单操作方法混入
+	mixins: [orderOperate],
+	components: {
+		OrderGoodsCard
+	},
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		}
+	},
+	data() {
+		return {};
+	},
+	methods: {
+		// 付款
+		goPayResult(e) {
+			
+		},
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	background-color: $app-theme-bg-color;
+	border-radius: 16rpx;
+	box-shadow: $app-theme-shadow;
+	// padding: 30rpx;
+	margin-top: 20rpx;
+	.goods {
+		display: flex;
+		align-items: center;
+		align-content: space-between;
+		padding-bottom: 26rpx;
+		border-bottom: 1rpx solid $app-theme-border-color;
+		margin-bottom: 32rpx;
+		.pic {
+			margin-right: 24rpx;
+		}
+		.info {
+			width: 280rpx;
+			margin-right: 20rpx;
+			.title {
+				width: 100%;
+				display: -webkit-box;
+				overflow: hidden;
+				-webkit-line-clamp: 2;
+				-webkit-box-orient: vertical;
+				font-size: 28rpx;
+				font-weight: 400;
+				color: $app-theme-text-black-color;
+				margin-bottom: 14rpx;
+			}
+			.desc {
+				display: flex;
+				justify-content: flex-start;
+				align-items: flex-end;
+				margin-bottom: 18rpx;
+				.item {
+					margin-right: 24rpx;
+					text:nth-child(1) {
+						font-size: 24rpx;
+						color: $app-theme-card-gray-color;
+						margin-right: 8rpx;
+					}
+					text:nth-child(2) {
+						font-size: 24rpx;
+						color: $app-theme-card-gray-deep-color;
+					}
+				}
+			}
+			.price {
+				text:nth-child(1) {
+					font-size: 16rpx;
+					color: $app-theme-text-black-color;
+				}
+				text:nth-child(2) {
+					font-size: 28rpx;
+					color: $app-theme-text-black-color;
+				}
+			}
+		}
+		.status {
+			width: 120rpx;
+			.status-label {
+				width: 100%;
+				text-align: right;
+				font-size: 28rpx;
+				color: $app-theme-color;
+				margin-bottom: 100rpx;
+			}
+			.num {
+				width: 100%;
+				text-align: right;
+				text:nth-child(1) {
+					font-size: 24rpx;
+					color: $app-theme-text-gray-white-color;
+					vertical-align: bottom;
+				}
+				text:nth-child(2) {
+					font-size: 28rpx;
+					color: $app-theme-text-gray-white-color;
+					vertical-align: bottom;
+				}
+			}
+		}
+	}
+}
+.operate {
+	display: flex;
+	justify-content: flex-end;
+	align-items: center;
+	min-height: 90rpx;
+	border-top: 1rpx solid $app-theme-border-color;
+}
+.paystyle{
+	// font-weight: bold;
+	margin-right: 20rpx;
+	color: $app-theme-text-money-color;
+}
+</style>

+ 193 - 0
pages-mall/components/order/order-goods-card.vue

@@ -0,0 +1,193 @@
+<template>
+	<view @click.stop="goOrderInfo(data.id)">
+		<view class="sigleI" v-for="(item,index) in data.order_details" :key="item.id">
+			<view class="content">
+				<view class="pic">
+					<u-image width="180rpx" height="180rpx" :src="item.goods_cover"></u-image>
+				</view>
+				<view class="info">
+					<view class="title">{{ item.goods_title }}</view>
+					<view class="desc">
+						<view class="sku item">
+							<text>规格</text>
+							<text>{{ item.goods_sku }}</text>
+						</view>
+					</view>
+					<view class="price">
+						<text>¥</text>
+						<text>{{ item.goods_price_selling }}</text>
+					</view>
+				</view>
+				<view class="status">
+					<view class="status-label">
+						<slot></slot>
+					</view>
+					<view class="num">
+						<text>×</text>
+						<text>{{ item.goods_nums }}</text>
+					</view>
+				</view>
+			</view>
+			
+			<!-- {{data.status}}============{{item.sub_status}} -->
+			<!-- 
+			sub_status:10=正常(非售后订单),20=仅退款,21=卖家同意,22=卖家驳回,30=退货退款,31=卖家同意(用户填写退货单号),32=卖家驳回,33=等待卖家收货确认中,34=卖家确认收货,验货没问题金额已原路退回 
+			 -->
+			 <!-- String(data.status)=='15'&& -->
+			<view class="oparBtns" v-if="String(item.sub_status)!=='10'">
+				 <!-- <text v-if="item.sub_status=='20'">退款审核中</text>
+				 <text v-if="item.sub_status=='21'">退款成功</text>
+				 <text v-if="item.sub_status=='22'">退款失败</text>
+				 <text v-if="item.sub_status=='30'">退货申请中,等待客服审核</text>
+				 <text v-if="item.sub_status=='31'">售后中</text>
+				 <text v-if="item.sub_status=='32'">申请失败,如有疑问可联系客服</text>
+				 <text v-if="item.sub_status=='33'">等待卖家收货确认</text>
+				 <text v-if="item.sub_status=='34'">卖家已确认收货,正在退款中</text>
+				 <text v-if="item.sub_status=='344'">退款成功</text> -->
+				 <text class="descp">该商品存在售后</text>
+				 <!-- <u-button type="default" size="mini" shape="circle" @click="afterSalesDetail(item.order_refund_id)">前往查看</u-button> -->
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		orderOperate
+	} from '@/pages-mall/mixins/order-operate.js';
+	export default {
+		name: 'goods-card',
+		mixins: [orderOperate],
+		props: {
+			// 数据源
+			data: {
+				type: Object,
+				default: () => {
+					return {};
+				}
+			},
+			// 是否显示下边框
+			showBorderBottom: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {};
+		},
+		methods: {}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.sigleI{
+		border-bottom: 1rpx solid $app-theme-border-color;
+	}
+	.oparBtns{
+		padding: 15rpx 30rpx;
+		// text-align: right;
+	}
+	.content {
+		display: flex;
+		align-items: center;
+		align-content: space-between;
+		padding: 30rpx;
+
+		// &.showBorderBottom {
+		// 	margin-bottom: 30rpx;
+		// 	padding-bottom: 24rpx;
+		// 	border-bottom: 1rpx solid $app-theme-border-color;
+		// }
+
+		.pic {
+			margin-right: 24rpx;
+			border-radius: 16rpx;
+			overflow: hidden;
+		}
+
+		.info {
+			width: 280rpx;
+			margin-right: 20rpx;
+
+			.title {
+				width: 100%;
+				display: -webkit-box;
+				overflow: hidden;
+				-webkit-line-clamp: 2;
+				-webkit-box-orient: vertical;
+				font-size: 28rpx;
+				font-weight: 400;
+				color: $app-theme-text-black-color;
+				margin-bottom: 14rpx;
+			}
+
+			.desc {
+				display: flex;
+				justify-content: flex-start;
+				align-items: flex-end;
+				margin-bottom: 18rpx;
+
+				.item {
+					margin-right: 24rpx;
+
+					text:nth-child(1) {
+						font-size: 24rpx;
+						color: $app-theme-card-gray-color;
+						margin-right: 8rpx;
+					}
+
+					text:nth-child(2) {
+						font-size: 24rpx;
+						color: $app-theme-card-gray-deep-color;
+					}
+				}
+			}
+
+			.price {
+				text:nth-child(1) {
+					font-size: 16rpx;
+					color: $app-theme-text-black-color;
+				}
+
+				text:nth-child(2) {
+					font-size: 28rpx;
+					color: $app-theme-text-black-color;
+				}
+			}
+		}
+
+		.status {
+			width: 120rpx;
+
+			.status-label {
+				width: 100%;
+				text-align: right;
+				font-size: 28rpx;
+				color: $app-theme-color;
+				margin-bottom: 100rpx;
+				height: 28rpx;
+			}
+
+			.num {
+				width: 100%;
+				text-align: right;
+
+				text:nth-child(1) {
+					font-size: 24rpx;
+					color: $app-theme-text-gray-white-color;
+					vertical-align: bottom;
+				}
+
+				text:nth-child(2) {
+					font-size: 28rpx;
+					color: $app-theme-text-gray-white-color;
+					vertical-align: bottom;
+				}
+			}
+		}
+	}
+	.descp{
+		margin-right: 20rpx;
+		color: #aaa;
+	}
+</style>

+ 95 - 0
pages-mall/components/order/order-operate.vue

@@ -0,0 +1,95 @@
+<template>
+	<view class="slot">
+		<view class="inner">
+			<view class="price">
+					<text>¥</text>
+					<text>{{ data.actual_total_amount }}</text>
+			</view>
+			<view class="right btnGroup">
+				<view class="btn" v-if="data.status == '10'">
+					<u-button shape="circle" size="small" type="default" @click="cancelOrder(data.id)">取消订单</u-button>
+				</view>
+				<view class="btn" v-if="data.status == '10'">
+					<u-button shape="circle" size="small" type="primary" @click="goPay(data)">去付款</u-button>
+				</view>
+				<view class="btn" v-if="data.status == '11'||data.status == '12'">
+					<u-button type="default" size="small" shape="circle" @click="applyAfterSales(data.id)">申请售后</u-button>
+				</view>
+				<view class="btn" v-if="data.status == '11'">
+					<u-button shape="circle" size="small" type="primary" @click="pushDelivery">待发货</u-button>
+				</view>
+				<view class="btn" v-if="data.status == '12'">
+					<u-button shape="circle" size="small" type="primary" @click="confirmReceipt(data)" @refresh="refreshPage">确认收货</u-button>
+				</view>
+				<view class="btn" v-if="data.status == '13'">
+					<u-button shape="circle" size="small" type="primary">已完成</u-button>
+				</view>
+				<view class="btn" v-if="data.status == '14'">
+					<u-button shape="circle" size="small" type="default">交易关闭</u-button>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { orderOperate } from '@/pages-mall/mixins/order-operate.js';
+export default {
+	// 工单操作方法混入
+	mixins: [orderOperate],
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		}
+	},
+	methods: {
+		refreshPage(){
+			this.$emit('refresh')
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	width: 100%;
+	background-color: $app-theme-bg-color;
+	min-height: 100rpx;
+	box-shadow: $app-theme-shadow;
+	border-top: 1px solid $app-theme-border-color;
+	padding: 10rpx 0;
+	padding-bottom: constant(safe-area-inset-bottom);
+	padding-bottom: env(safe-area-inset-bottom);
+	.inner {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		height: 100%;
+		.price {
+			padding-left: 30rpx;
+			text:nth-child(1) {
+				font-size: 24rpx;
+				font-family: PingFangSC-Medium, PingFang SC;
+				font-weight: 500;
+				color: $app-theme-text-money-color;
+			}
+			text:nth-child(2) {
+				font-size: 36rpx;
+				font-family: PingFangSC-Regular, PingFang SC;
+				font-weight: 400;
+				color: $app-theme-text-money-color;
+			}
+		}
+	}
+}
+	.right {
+		padding-right: 30rpx;
+	}
+</style>

+ 95 - 0
pages-mall/components/order/order-status.vue

@@ -0,0 +1,95 @@
+<template>
+	<view class="slot">
+		<view class="status">
+			<text v-if="type == '10'">等待买家付款</text>
+			<text v-if="type == '11'">等待卖家发货</text>
+			<text v-if="type == '12'">运输中</text>
+			<!-- <text v-if="type == '3'">待评价</text> -->
+			<!-- <text v-if="type == '4'">退款售后</text> -->
+			<text v-if="type == '13'">已完成</text>
+			<text v-if="type == '14'">交易关闭</text>
+			<text v-if="type == '15'">存在售后单</text>
+		</view>
+		<view class="desc">
+			<text v-if="type == '10'">请尽快完成支付</text>
+			<!-- <text v-if="type == '11'">预计发货日期:{{ expressDate }}</text> -->
+			<!-- <text v-if="type == '12'">快件由 [{{ expressAddressStart }}] 发往 [{{ expressAddressEnd }}]</text> -->
+			<!-- <text v-if="type == '3'">您的订单还未评价</text> -->
+			<!-- <text v-if="type == '4'">当前订单商家已同意退款</text> -->
+			<text v-if="type == '5'">当前订单已完成</text>
+		</view>
+		<view class="operate">
+			<u-button size="mini" plain shape="circle" type="white" v-if="type == '12'" @click="goExpress(data)">查看物流</u-button>
+			<!-- <u-button size="mini" plain shape="circle" type="white" v-if="type == '3'" @click="goEvaluate()">去评价</u-button> -->
+			<!-- <u-button size="mini" plain shape="circle" type="white" v-if="type == '4'" @click="goBackMoney()">查看退款详情</u-button>
+			<u-button size="mini" plain shape="circle" type="white" v-if="type == '5'" @click="applyAfterSales()">申请售后</u-button> -->
+		</view>
+	</view>
+</template>
+
+<script>
+import { orderOperate } from '@/pages-mall/mixins/order-operate.js';
+export default {
+	name: 'order-status',
+	// 工单操作方法混入
+	mixins: [orderOperate],
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		// 类型
+		type: {
+			type: [String, Number],
+			default: '0'
+		},
+		// 付款截止日期
+		endDate: {
+			type: String,
+			default: '2022-01-11 14:19:56'
+		},
+		// 预计发货日期
+		expressDate: {
+			type: String,
+			default: '2022-01-11 14:19:56'
+		},
+		// 快递节点
+		expressAddressStart: {
+			type: String,
+			default: '广东中山转运中心'
+		},
+		expressAddressEnd: {
+			type: String,
+			default: '山东青岛转运中心'
+		}
+	},
+	data() {
+		return {};
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	position: relative;
+	padding: 30rpx;
+	background-color: $app-theme-color;
+	color: $app-theme-text-white-color;
+	margin-bottom: 24rpx;
+	.status {
+		margin-bottom: 20rpx;
+		font-size: 30rpx;
+	}
+	.desc {
+		font-size: 26rpx;
+	}
+	.operate {
+		position: absolute;
+		top: 30rpx;
+		right: 30rpx;
+	}
+}
+</style>

+ 97 - 0
pages-mall/components/order/orderdetail-goods-card.vue

@@ -0,0 +1,97 @@
+<template>
+	<view class="orderGoodsDetail" :style="showBorderBottom ? '' : 'border-bottom:none'">
+		<view class="img"><u-image width="160rpx" height="160rpx" :src="data.goods_cover"></u-image></view>
+		<view class="info">
+			<view class="title">{{ data.goods_title }}</view>
+			<view class="sku">
+				<u-tag size="mini" type="info" :text="data.goods_sku"></u-tag>
+				×{{ data.goods_nums }}
+			</view>
+			<view class="price">
+				<text>¥</text>
+				<text>{{ data.actual_total_amount }}</text>
+			</view>
+			<!-- <view class="tag">不支持七天无理由退货</view> -->
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'submit-goods-card',
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		// 是否显示底部边框
+		showBorderBottom: {
+			type: Boolean,
+			default: false
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.orderGoodsDetail {
+	display: flex;
+	align-items: center;
+	padding: 28rpx 0;
+	border-bottom: 1rpx solid $app-theme-border-color;
+	position: relative;
+	.img {
+		margin-right: 30rpx;
+	}
+	.info {
+		width: 420rpx;
+		position: relative;
+		.title {
+			width: 100%;
+			font-size: 28rpx;
+			color: $app-theme-text-black-color;
+			padding-bottom: 16rpx;
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		}
+		.sku {
+			font-size: 24rpx;
+			color: $app-theme-text-gray-color;
+		}
+		.price {
+			padding-top: 8rpx;
+			text:nth-child(1) {
+				font-size: 20rpx;
+				font-family: PingFangSC-Medium, PingFang SC;
+				font-weight: 500;
+				color: $app-theme-text-black-color;
+			}
+			text:nth-child(2) {
+				font-size: 28rpx;
+				font-family: PingFangSC-Regular, PingFang SC;
+				font-weight: 400;
+				color: $app-theme-text-black-color;
+			}
+		}
+		.tag {
+			margin-top: 8rpx;
+			background: rgba($app-theme-text-money-color, 0.1);
+			border-radius: 4rpx;
+			color: $app-theme-text-money-color;
+			padding: 4rpx;
+			font-size: 20rpx;
+			display: inline-block;
+		}
+	}
+	.num {
+		position: absolute;
+		top: 60rpx;
+		right: 30rpx;
+		color: $app-theme-text-gray-color;
+	}
+}
+</style>

+ 132 - 0
pages-mall/components/order/sel-coupon-popup.vue

@@ -0,0 +1,132 @@
+<template>
+	<u-popup mode="bottom" v-model="show">
+		<view class="content">
+			<scroll-view class="scbox" scroll-y="true">
+				<!-- 
+				<DiscountsItem
+				:select="selectIndex == index" 
+				v-for="(item, index) in couponList" 
+				:data="item" 
+				:key="index" 
+				@click="changeDiscount(item, index)"></DiscountsItem> 
+				 
+				 -->
+				<u-radio-group v-model="selCouponId" @change="changeDiscount">
+					<view class="item" v-for="(item, index) in couponList" :key="index">
+						<!--  :disabled="item.status==1" -->
+						<u-radio :name="item.id" :disabled="item.status!=2||(Number(item.min_amount)>Number(priceInfo.countPrice))" >
+							<view class="coupnInfo">
+								<view class="price_color">
+									{{item|filterCouponToTitle}}
+								</view>
+								<view class="time">
+									<view class="date">有效期至 {{ formatDateF(item.limit_time*1000) }}</view>
+								</view>
+							</view>
+						</u-radio>
+					</view>
+					<view class="item">
+						<u-radio :name="null">
+							<view class="coupnInfo">
+								不使用优惠券
+							</view>
+						</u-radio>
+					</view>
+					
+				</u-radio-group>
+			</scroll-view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import DiscountsItem from '@/pages-mine/components/discounts-item.vue';
+import { formatDate } from '@/utils/tools.js'
+export default {
+	name: 'sel-coupon-popup',
+	components: {
+		DiscountsItem
+	},
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		priceInfo: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		}
+	},
+	data() {
+		return {
+			show:false,
+			selCouponId:null,
+			couponList:[],
+			usingCoupon:{},
+		};
+	},
+	filters:{
+		filterCouponToTitle(e){
+			if(e.min_amount>0){
+				// 有满减
+				return e.title+'(满'+e.min_amount+'减'+e.amount+')';
+			}else{
+				return e.title+'(&yen;'+e.amount+')';
+			}
+		}
+	},
+	methods: {
+		formatDateF(v){
+			return formatDate(v);
+		},
+		showPopup(e){
+			console.log(e)
+			// couponList: Array(1), usingCoupon
+			this.couponList = e.couponList;
+			this.usingCoupon = e.usingCoupon;
+			
+			console.log(this.couponList,this.couponList)
+			this.show = true;
+		},
+		
+		changeDiscount(id) {
+			console.log(id)
+			if(id){
+				this.usingCoupon = this.couponList.find(e=>e.id==id)
+				this.$emit('done',this.usingCoupon)
+			}else{
+				this.$emit('done',{})
+			}
+			this.show = false;
+			return;
+			this.usingCoupon = item;
+		},
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.price_color{
+	font-size: 32rpx;
+	font-weight: 500;
+}
+.scbox{
+	box-sizing: border-box;
+	height: 600rpx;
+	padding: 30rpx;
+}
+.item{
+	margin-bottom: 30rpx;
+}
+.coupnInfo{
+	padding-left: 30rpx;
+}
+/deep/.u-radio{
+	float: none !important;
+}
+</style>

+ 152 - 0
pages-mall/components/order/submit-discounts.vue

@@ -0,0 +1,152 @@
+<template>
+	<view class="slot">
+		<view class="line">
+			<view class="labe">商品金额</view>
+			<view class="value">
+				<text>¥</text>
+				<text>{{data.countPrice}}</text>
+			</view>
+		</view>
+		<view class="line">
+			<view class="labe">运费</view>
+			<view class="value" v-if="data.freight==0">
+				<text>免运费</text>
+			</view>
+			<view class="value" v-else>
+				<text>¥</text>
+				<text>{{parseFloat(data.freight).toFixed(2)}}</text>
+			</view>
+		</view>
+		<!-- <view class="line">
+			<view class="labe">立减</view>
+			<view class="value money">
+				<text>-</text>
+				<text>¥</text>
+				<text>9.00</text>
+			</view>
+		</view> -->
+		<!-- {{JSON.stringify(usingCoupon)}} -->
+		<view class="line">
+			<view class="labe">优惠券</view>
+			<view class="value" v-if="couponList.length<1">
+				<text>无可用</text>
+			</view>
+			<view v-else class="value money" :class="data.discount>0?'money':'gray'" @click="goDiscountsPage">
+				<template v-if="data.discount>0">
+					<text>-&yen;{{data.discount}}</text>
+					<u-tag class="utag" :text="usingCoupon.title" type="success" mode="dark" bg-color="#fd4558" />
+				</template>
+				<!-- <text v-else>无可用</text> -->
+				<view class="more"><u-icon size="20" name="arrow-right"></u-icon></view>
+			</view>
+		</view>
+		<!-- <view class="total">
+			<text>合计:</text>
+			<text>¥</text>
+			<text>{{data.countPrice - data.discount}}</text>
+		</view> -->
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		data: {
+			type: Object,
+			default: () => {
+				return {
+					countPrice:0,
+					discount:0,
+				};
+			}
+		},
+		usingCoupon:{
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		couponList:{
+			type: Array,
+			default: () => {
+				return [];
+			}
+		},
+	},
+	
+	methods: {
+		goDiscountsPage() {
+			this.$emit('selCoupon');
+			return;
+			uni.navigateTo({
+				url: '/pages-mine/pages/discounts'
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	padding: 30rpx 30rpx 0 30rpx;
+	background-color: $app-theme-bg-color;
+	margin-top: 24rpx;
+}
+.line {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	margin-bottom: 30rpx;
+	.label {
+		font-size: 28rpx;
+		color: $app-theme-text-black-color;
+	}
+	.value {
+		// min-width: 100rpx;
+		padding-right: 24rpx;
+		font-size: 28rpx;
+		color: $app-theme-text-black-color;
+		flex: 1;
+		text-align: right;
+		display: flex;
+		align-items: center;
+		justify-content: flex-end;
+		min-height: 1.2em;
+		&.money {
+			color: $app-theme-text-money-color;
+		}
+		&.gray {
+			color: $app-theme-card-gray-color;
+			position: relative;
+			.more {
+				position: absolute;
+				right: -16rpx;
+				margin-left: 10rpx;
+				top: 50%;
+				transform: translate(-50%, -50%);
+			}
+		}
+	}
+}
+.total {
+	border-top: 1rpx solid $app-theme-border-color;
+	padding: 26rpx 0;
+	text-align: right;
+	text:nth-child(1) {
+		font-size: 28rpx;
+		color: $app-theme-text-black-color;
+	}
+	text:nth-child(2) {
+		font-size: 20rpx;
+		color: $app-theme-text-money-color;
+	}
+	text:nth-child(3) {
+		font-size: 28rpx;
+		color: $app-theme-text-money-color;
+	}
+}
+
+.utag{
+	margin: 0 10rpx;
+}
+</style>

+ 94 - 0
pages-mall/components/order/submit-goods-card.vue

@@ -0,0 +1,94 @@
+<template>
+	<view class="slot" :style="showBorderBottom ? '' : 'border-bottom:none'">
+		<view class="img"><u-image width="160rpx" height="160rpx" :src="data.cover"></u-image></view>
+		<view class="info">
+			<view class="title">{{ data.title }}</view>
+			<view class="sku">{{ data.selectdSku.difference }}</view>
+			<view class="price">
+				<text>¥</text>
+				<text>{{ data.selectdSku.price_selling }}</text>
+			</view>
+			<!-- <view class="tag">不支持七天无理由退货</view> -->
+		</view>
+		<view class="num">×{{ data.nums }}</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'submit-goods-card',
+	props: {
+		// 数据源
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+		// 是否显示底部边框
+		showBorderBottom: {
+			type: Boolean,
+			default: false
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.slot {
+	display: flex;
+	align-items: center;
+	padding: 28rpx 0;
+	border-bottom: 1rpx solid $app-theme-border-color;
+	position: relative;
+	.img {
+		margin-right: 30rpx;
+	}
+	.info {
+		width: 420rpx;
+		.title {
+			width: 100%;
+			font-size: 28rpx;
+			color: $app-theme-text-black-color;
+			padding-bottom: 16rpx;
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		}
+		.sku {
+			font-size: 22rpx;
+			color: $app-theme-text-gray-color;
+		}
+		.price {
+			padding-top: 8rpx;
+			text:nth-child(1) {
+				font-size: 20rpx;
+				font-family: PingFangSC-Medium, PingFang SC;
+				font-weight: 500;
+				color: $app-theme-text-black-color;
+			}
+			text:nth-child(2) {
+				font-size: 28rpx;
+				font-family: PingFangSC-Regular, PingFang SC;
+				font-weight: 400;
+				color: $app-theme-text-black-color;
+			}
+		}
+		.tag {
+			margin-top: 8rpx;
+			background: rgba($app-theme-text-money-color, 0.1);
+			border-radius: 4rpx;
+			color: $app-theme-text-money-color;
+			padding: 4rpx;
+			font-size: 20rpx;
+			display: inline-block;
+		}
+	}
+	.num {
+		position: absolute;
+		top: 30rpx;
+		right: 30rpx;
+		color: $app-theme-text-gray-color;
+	}
+}
+</style>

+ 92 - 0
pages-mall/components/order/submit-operate.vue

@@ -0,0 +1,92 @@
+<template>
+	<view class="">
+		<view class="submit_opar">
+			<view class="price">
+				<text>¥</text>
+				<text>{{ heji }}</text>
+			</view>
+			<view class="right">
+				<!-- <u-button type="primary" @click="goPayResult">提交订单</u-button> -->
+				<view class="buyBtn" @click="goPayResult">
+					提交订单
+				</view>
+			</view>
+		</view>
+		<view class="siteBar"></view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		}
+	},
+	computed:{
+		heji(){
+			let a = Number(this.data.countPrice) + Number(this.data.freight) - Number(this.data.discount);
+			return parseFloat(a).toFixed(2)
+		}
+	},
+	methods: {
+		goPayResult() {
+			this.$emit('submitOrder')
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+	.siteBar{
+		height: 100rpx;
+		// padding-bottom: constant(safe-area-inset-bottom);
+		// padding-bottom: env(safe-area-inset-bottom);
+		// box-sizing: content-box;
+		@extend .safeAreaPad;
+	}
+.submit_opar {
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	width: 100%;
+	background-color: $app-theme-bg-color;
+	height: 100rpx;
+	box-shadow: $app-theme-shadow;
+	// padding-bottom: constant(safe-area-inset-bottom);
+	// padding-bottom: env(safe-area-inset-bottom);
+	// box-sizing: content-box;
+	
+	@extend .safeAreaPad;
+	z-index: $app-zIndex-fixed;
+	
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	
+	.price {
+		padding-left: 30rpx;
+		text:nth-child(1) {
+			font-size: 24rpx;
+			font-family: PingFangSC-Medium, PingFang SC;
+			font-weight: 500;
+			color: $app-theme-text-money-color;
+		}
+		text:nth-child(2) {
+			font-size: 36rpx;
+			font-family: PingFangSC-Regular, PingFang SC;
+			font-weight: 400;
+			color: $app-theme-text-money-color;
+		}
+	}
+	.right {
+		display: flex;
+		align-items: center;
+		
+	}
+	
+}
+</style>

+ 248 - 0
pages-mall/mixins/order-operate.js

@@ -0,0 +1,248 @@
+var _self;
+export const orderOperate = {
+	data() {
+		return {
+			// 主题色
+			appThemeColor: this.$appTheme.appThemeColor
+		}
+	},
+	onload(){
+		_self = this;
+	},
+	methods: {
+		// 去付款
+		goPay(e) {
+			console.log(e);
+			// return;
+			this.$u.api.payAjax(e.order_no).then(({code,data})=>{
+				if(code==1){
+					var orderInfos = data;
+					uni.requestPayment({
+						"provider": "wxpay",
+						"orderInfo":orderInfos,
+						...data,
+						success: function(res) {
+							uni.showToast({
+								title: "支付成功",
+								icon: 'success',
+								success: () => {
+									setTimeout(() => {
+										uni.redirectTo({
+											url: '/pages-mall/pages/order/pay-result?payTotal='+e.total_amount+'&orderId='+e.order_no
+										})
+									}, 500)
+									// _self.subScribeMsg(e);
+								}
+							})
+						},
+						fail: function(err) {
+							uni.showModal({
+								content:'支付失败,请到我的订单中,重新支付',
+								success() {
+									// +this.orderInfo.order_no,
+									// uni.redirectTo({
+									// 	url:'/pages-mall/pages/order/detail'
+									// })
+								}
+							})
+						}
+					});
+				}
+			})
+		},
+		// 支付回调
+		subScribeMsg(e) {
+			uni.getSetting({
+				withSubscriptions: true,
+				success(res) {
+					console.log('1', res, '订阅信息', res.subscriptionsSetting);
+					if (!res.subscriptionsSetting.mainSwitch) {
+						uni.openSetting({ //打开设置页
+							success(res) {
+								console.log('打开设置页', res.authSetting);
+							}
+						})
+					} else {
+						uni.requestSubscribeMessage({
+							tmplIds: ['Sfa68XDGAUQveX50PcP_Ep6ngNz3luvXJNwzASjzzvQ',
+								'_VxaYVnVTj3aG_wengF8KNFDI3WRhOrFseBerx8xvXE',
+							],
+							success(res) {
+								console.log('requestSubscribeMessage 订阅信息', res);
+								if (res['Sfa68XDGAUQveX50PcP_Ep6ngNz3luvXJNwzASjzzvQ'] == "accept") { // 用户点击确定后
+									console.log('用户订阅点击确定按钮');
+									// _self.getSubMsg()
+								} else {
+									console.log('拒绝');
+								}
+							},
+							fail(errMessage) {
+								console.log("订阅消息 失败 ", errMessage);
+							},
+							// 不管成功或失败都要执行
+							complete() {
+								// if (_self.ordercode == null) return _self.getOrder();
+								// if (_self.ordercode != null) return _self.getPayOrder();
+								setTimeout(() => {
+									uni.redirectTo({
+										url: '/pages-mall/pages/order/pay-result?payTotal='+e.total_amount
+										
+									})
+								}, 500)
+							}
+						})
+					}
+				},
+			})
+		},
+		
+		// 查看详情
+		goOrderInfo(id) {
+			// uni.setStorageSync('orderInfo', this.data);
+			uni.navigateTo({
+				url: '/pages-mall/pages/order/detail?order_id='+id
+			});
+			
+		},
+
+		// 取消订单
+		cancelOrder(id,page) {
+			let _self = this;
+			uni.showModal({
+				title: '提示',
+				content: '是否取消当前订单?',
+				confirmColor: this.appThemeColor,
+				success: function(res) {
+					if (res.confirm) {
+						// TODO 接口
+						_self.$u.api.cancelOrderAjax(id).then(({code,data})=>{
+							if(code==1){
+								_self.$u.toast('取消成功');
+								// uni.redirectTo({
+								// 	url:'/pages-mall/pages/order/list'
+								// });
+								_self.$emit('refresh')
+								/* // uni.reload();
+								// if(page=='orderList'){
+								// 	// 从订单列表过来的,刷新订单列表
+								// 	uni.reload();
+									// _self.$router.go(0);
+								// } */
+							}
+						}).catch(()=>{
+									// _self.$router.go(0);
+							
+						})
+					}
+				}
+			});
+		},
+
+		// 提醒发货
+		pushDelivery() {
+			return;
+			uni.showModal({
+				title: '提示',
+				content: '是否对当前订单提醒发货?',
+				confirmColor: this.appThemeColor,
+				success: function(res) {
+					if (res.confirm) {
+						// TODO 接口
+					}
+				}
+			});
+		},
+
+		// 查看物流
+		goExpress(orderInfo) {
+			// uni.setStorageSync('orderInfo', this.data);
+			uni.navigateTo({
+				url: '/pages-mall/pages/order/express?orderInfo='+encodeURIComponent(JSON.stringify(orderInfo))
+			})
+		},
+
+		// 申请售后
+		applyAfterSales(orderId) {
+			// console.log(data);
+			// return;
+			uni.navigateTo({
+				url: '/pages-mall/pages/after-sales/apply?orderId='+orderId
+			})
+		},
+		
+
+		// 确认收货
+		confirmReceipt(data) {
+			console.log(data);
+			let _self = this;
+			uni.showModal({
+				title: '提示',
+				content: '是否确认收货?',
+				confirmColor: this.appThemeColor,
+				success: function(res) {
+					if (res.confirm) {
+						// TODO 接口
+						let ids = [];
+						data.order_details.forEach(e=>{
+							ids.push(e.id);
+						});
+						/* console.log({
+							order_id:data.id,
+							order_detail_ids:ids.toString()
+						})
+						return false; */
+						_self.$u.api.orderConfirmAjax({
+							order_id:data.id,
+							order_detail_ids:ids.toString()
+						}).then(({code,data})=>{
+							if(code==1){
+								_self.$emit('refresh')
+							}
+						}).catch(()=>{
+							_self.$u.toast('操作失败')
+						})
+					}
+				}
+			});
+		},
+
+		// 去评价
+		goEvaluate() {
+			uni.setStorageSync('orderInfo', this.data);
+			uni.navigateTo({
+				url: '/pages-mall/pages/evaluate/add'
+			})
+		},
+
+		// 删除记录
+		deleteOrder() {
+			uni.showModal({
+				title: '提示',
+				content: '是否删除当前订单?',
+				confirmColor: this.appThemeColor,
+				success: function(res) {
+					if (res.confirm) {
+						// TODO 接口
+					}
+				}
+			});
+		},
+		
+		// 售后详情
+		afterSalesDetail(id){
+			uni.navigateTo({
+				url: '/pages-mall/pages/after-sales/apply-status?id='+id
+			})
+			// uni.navigateTo({
+			// 	url: '/pages-mall/pages/after-sales/apply-status?applyInfo='+encodeURIComponent(JSON.stringify(data))
+			// })
+		},
+		// 查看退款详情
+		goBackMoney() {
+			uni.setStorageSync('orderInfo', this.data);
+			uni.navigateTo({
+				url: '/pages-mall/pages/after-sales/back'
+			})
+		}
+	}
+}

+ 353 - 0
pages-mall/pages/after-sales/apply-status - 副本.vue

@@ -0,0 +1,353 @@
+<template>
+	<view class="content">
+		<view class="statusDesc">
+			<view class="status">
+				{{applyInfo.refund_type==1?'仅退款':applyInfo.refund_type==2?'退货退款':''}}
+			</view>
+			<view class="applyDesc">
+				<template  v-if="applyInfo.status == 10">
+					<view class="title">审核中</view>
+				</template>
+				<template  v-if="applyInfo.status == 21">
+					<view class="title">审核通过,正在退款中</view>
+				</template>
+				<template  v-if="applyInfo.status == 22">
+					<view class="title">审核失败</view>
+				</template>
+				<template  v-if="applyInfo.status == 211">
+					<view class="title">退款成功</view>
+				</template>
+				<template  v-if="applyInfo.status == 31">
+					<view class="title">审核通过,请填写退货快递单号</view>
+				</template>
+				<template  v-if="applyInfo.status == 32">
+					<view class="title">审核未通过</view>
+					<view class="">
+						如有疑问,可联系客服
+					</view>
+				</template>
+				<template  v-if="applyInfo.status == 33">
+					<view class="title">等待卖家收货确认</view>
+				</template>
+				<template  v-if="applyInfo.status == 34">
+					<view class="title">卖家已确认收货,退款中</view>
+				</template>
+				<template  v-if="applyInfo.status == 344">
+					<view class="title">退款成功</view>
+				</template>
+				
+			</view>
+				
+		</view>
+		<!-- 退货退款,输入运单号 -->
+		<setExpress :refundId="applyInfo.id" v-if="applyInfo.status == 31" @done="setExpressSuccess"></setExpress>
+		<view class="conD">
+			<!-- <u-button class="returnBtn" :custom-style="returnBtnStyle" @click="goBack()">返回</u-button> -->
+		
+			<!--
+			10=待审核,21=卖家同意等待退款中,211=退款成功,
+			22=卖家驳回,31=卖家同意(用户填写退货单号),32=卖家驳回,33=等待卖家收货确认中,
+			34=卖家确认收货,验货没问题同意退款,344=退款成功 
+			 -->
+			 <!-- <TitleOperate title="申请类型" padding="10rpx 30rpx 24rpx 30rpx"></TitleOperate> -->
+			 <!-- <view class="baseP">
+				申请类型:{{applyInfo.refund_type==1?'仅退款':applyInfo.refund_type==2?'退货退款':''}}
+			 </view> -->
+			
+			<view class="conbox">
+				<TitleOperate title="售后商品" padding="30rpx 0 0"></TitleOperate>
+				<template>
+					<orderDetailGoodsCard v-for="(goods, index) in applyInfo.order_detail" :data="goods" :key="index"></orderDetailGoodsCard>
+				</template>
+			</view>
+			<view class="conbox">
+				<TitleOperate title="退款信息" padding="30rpx 0 30rpx"></TitleOperate>
+				<view class="binfo">
+					<view class="cell">
+						<view class="label">服务编号</view>
+						<view class="value">{{ applyInfo.refund_no}}</view>
+						<view class="operate">
+							<text class="copy" @click="$copyByUniappApi(applyInfo.refund_no)">复制</text>
+						</view>
+					</view>
+					<view class="cell">
+						<view class="label">退款原因</view>
+						<view class="value">{{ applyInfo.reason}}</view>
+					</view>
+					<view class="cell">
+						<view class="label">退款金额</view>
+						<view class="value money">&yen;{{ applyInfo.refundFee}}</view>
+					</view>
+					<view class="cell">
+						<view class="label">申请时间</view>
+						<view class="value">{{ formatDateF(applyInfo.createtime*1000)}}</view>
+					</view>
+					<view class="contact">
+						<button class="item serviceBtn" open-type="contact" >
+							联系客服
+						</button>
+					</view>
+				</view>
+			</view>
+
+			<!-- <u-steps direction="column" :list="statusList[status]" :current="current" :active-color="activeColor"></u-steps> -->
+			
+		</view>
+	</view>
+</template>
+
+<script>
+import { orderOperate } from '@/pages-mall/mixins/order-operate.js';
+import orderDetailGoodsCard from '@/pages-mall/components/order/orderdetail-goods-card.vue';
+import setExpress from './components/setExpress.vue'
+import { formatDate } from '@/utils/tools.js'
+	// 组件
+	export default {
+	mixins: [orderOperate],
+		components: {
+			orderDetailGoodsCard,
+			setExpress
+		},
+		data() {
+			return {
+				status: '1',
+				current: 1,
+				expressCode:null,
+				activeColor:'#22ac38',
+				applyInfo:{
+					refund_type:null,
+					status:null,
+					order_detail:[],
+				},
+				returnBtnStyle: {
+					marginTop: '30rpx',
+				},
+				statusList: {
+					'1': [{
+						name: '提交申请'
+					}, {
+						name: '审核中'
+					},{
+						name: '退款中'
+					}, {
+						name: '退款成功'
+					}, ],
+					'2': [{
+						name: '提交申请'
+					}, {
+						name: '审核中'
+					}, {
+						name: '买家寄件'
+					}, {
+						name: '卖家处理'
+					}, {
+						name: '退款中'
+					}, {
+						name: '完成'
+					}, ],
+				},
+
+			};
+		},
+
+		onLoad(opt) {
+			console.log(opt);
+			if(opt.refund_type){
+				// if(opt.status=='all'){
+				// 	this.status = '2'
+				// 	this.refund_type = '2'
+				// }else if(opt.status=='money'){
+				// 	this.status = '1'
+				// }else{
+				// 	this.status = opt.status;
+				// }
+				this.applyInfo.refund_type = opt.refund_type;
+			}
+			if(opt.status){
+				this.applyInfo.status = opt.status;
+			}
+			if(opt.applyInfo){
+				console.log('applyInfo.refund_id>>>>>',this.applyInfo.order_refund_id);
+				// 申请详情
+				try{
+					this.applyInfo = JSON.parse(decodeURIComponent(opt.applyInfo))
+					console.log(this.applyInfo);
+					console.log('》》》applyInfo.order_detail',this.applyInfo.order_detail);
+					this.status = this.applyInfo.refund_type;
+					/* 
+					 10=待审核,21=卖家同意等待退款中,211=退款成功,
+					 22=卖家驳回,31=卖家同意(用户填写退货单号),32=卖家驳回,33=等待卖家收货确认中,
+					 34=卖家确认收货,验货没问题同意退款,344=退款成功
+					 */
+					if(['10'].includes(this.applyInfo.status+'')){
+						this.current = 1;
+					}
+					if(['21'].includes(this.applyInfo.status+'')){
+						this.current = 2;
+					}
+					if(['211'].includes(this.applyInfo.status+'')){
+						this.current = 3;
+					}
+					if(['22'].includes(this.applyInfo.status+'')){
+						this.statusList[this.status][1].name='审核未通过';
+						this.activeColor = 'red';
+					}
+					if(['31'].includes(this.applyInfo.status+'')){
+						this.current = 2;
+					}
+					if(['32'].includes(this.applyInfo.status+'')){
+						this.statusList[this.status][1].name='审核未通过';
+						this.activeColor = 'red';
+					}
+					if(this.applyInfo.status=='33'){
+						this.current = 3;
+					}
+					if(this.applyInfo.status=='34'){
+						this.current = 4;
+					}
+					if(this.applyInfo.status=='344'){
+						this.current = 5;
+					}
+					console.log('this.current>>>>',this.current)
+				}catch(e){
+					console.log(e);
+					uni.navigateBack({
+						delta:1
+					})
+				}
+			}
+			
+		},
+		onBackPress() {
+			uni.navigateBack({
+				delta:-2
+			})
+		},
+
+		methods: {
+			formatDateF(v){
+				return formatDate(v,'all');
+			},
+			goBack() {
+				uni.navigateBack({
+					delta: 1
+				});
+			},
+			setExpressSuccess(){
+				this.applyInfo.status = 33;
+				this.current = 3;
+			},
+			/* expressCodeConfirm(){
+				if(!this.expressCode){
+					this.$u.toast('请输入物流单号')
+				}else{
+					this.$u.api.setExpressCodeAjax({
+						expressCode:this.expressCode,
+						refund_id:this.applyInfo.id,
+					}).then(({code,data})=>{
+						if(code==1){
+							this.applyInfo.status = 33;
+							this.current = 3;
+						}
+						
+					})
+				}
+			}, */
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.content {
+		// padding: 30rpx;
+	}
+
+	.conbox {
+		background-color: #fff;
+		margin-bottom: 30rpx;
+		padding: 0 30rpx;
+	}
+
+	.selgoodsb {
+		text-align: center;
+
+		.t {
+			margin-bottom: 20rpx;
+			color: #666;
+		}
+	}
+	// .baseP{
+	// 	font-size: 32rpx;
+	// 	padding: 30rpx 50rpx;
+	// 	color: $app-theme-color;
+	// }
+	.returnBtn{
+		margin-top: 30rpx;
+	}
+	.statusDesc{
+		position: relative;
+		padding: 30rpx;
+		background-color: $app-theme-color;
+		color: $app-theme-text-white-color;
+		margin-bottom: 24rpx;
+		.title{
+			font-size: 36rpx;
+			margin-bottom: 50rpx;
+		}
+		.status {
+			margin-bottom: 20rpx;
+			font-size: 30rpx;
+		}
+		.desc {
+			font-size: 26rpx;
+		}
+	}
+	.binfo{
+		// padding: 30rpx;
+	}
+	.cell {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 20rpx 0;
+		.label {
+			width: 204rpx;
+			font-size: 28rpx;
+			color: $app-theme-text-color;
+		}
+		.value {
+			width: 320rpx;
+			text-align: right;
+			font-size: 28rpx;
+			color: $app-theme-text-color;
+		}
+		.price{
+			color: $app-theme-text-money-color;
+		}
+		.operate {
+			width: 80rpx;
+			text-align: right;
+			.copy {
+				font-size: 28rpx;
+				color: $app-theme-color;
+			}
+		}
+	}
+	.contact{
+		border-top: 1rpx solid #eee;
+		padding: 20rpx;
+		color: $app-theme-color;
+	}
+	.serviceBtn{
+		background-color: transparent;
+		padding: 0;
+		border: none;
+		line-height: 1.2;
+		font-size: 30rpx;
+		&::after{
+			content: '';
+			border: none;
+			display: none;
+		}
+	}
+</style>

+ 380 - 0
pages-mall/pages/after-sales/apply-status.vue

@@ -0,0 +1,380 @@
+<template>
+	<view class="content" v-if="applyInfo.id">
+		<view class="statusDesc">
+			<view class="status">
+				{{applyInfo.refund_type==1?'仅退款':applyInfo.refund_type==2?'退货退款':''}}
+			</view>
+			<view class="applyDesc">
+				<template  v-if="applyInfo.status == 10">
+					<view class="title">审核中</view>
+				</template>
+				<template  v-if="applyInfo.status == 21">
+					<view class="title">审核通过,正在退款中</view>
+				</template>
+				<template  v-if="applyInfo.status == 22">
+					<view class="title">申请未通过</view>
+					<view class="">
+						如有疑问,可联系客服
+					</view>
+				</template>
+				<template  v-if="applyInfo.status == 211">
+					<view class="title">退款成功</view>
+				</template>
+				<template  v-if="applyInfo.status == 31">
+					<view class="title">审核通过,请填写退货快递单号</view>
+				</template>
+				<template  v-if="applyInfo.status == 32">
+					<view class="title">审核未通过</view>
+					<view class="">
+						如有疑问,可联系客服
+					</view>
+				</template>
+				<template  v-if="applyInfo.status == 33">
+					<view class="title">等待卖家收货确认</view>
+				</template>
+				<template  v-if="applyInfo.status == 34">
+					<view class="title">卖家已确认收货,正在退款中</view>
+				</template>
+				<template  v-if="applyInfo.status == 344">
+					<view class="title">退款成功</view>
+				</template>
+				
+			</view>
+				
+		</view>
+		<view class="refuseReason" v-if="applyInfo.refuse_reason">
+			拒绝原因:{{applyInfo.refuse_reason}}
+		</view>
+		<!-- 退货退款,输入运单号 -->
+		<setExpress :refundId="applyInfo.id" v-if="applyInfo.status == 31" @done="setExpressSuccess"></setExpress>
+		
+		<!-- <view class="tExpress" v-if="applyInfo.status == 33">		
+			<view class="con">
+				退货物流单号:{{applyInfo.express_no}}
+			</view>
+			<view class="operate">
+				<text class="copy" @click="$copyByUniappApi(applyInfo.refund_no)">复制</text>
+			</view>
+		</view> -->
+		<view class="tExpress cell" v-if="applyInfo.express_no">
+			<view class="label">退货物流单号</view>
+			<view class="value">{{ applyInfo.express_no}}</view>
+			<view class="operate">
+				<text class="copy" @click="$copyByUniappApi(applyInfo.express_no)">复制</text>
+			</view>
+		</view>
+		<view class="conD">
+			<!-- <u-button class="returnBtn" :custom-style="returnBtnStyle" @click="goBack()">返回</u-button> -->
+		
+			<!--
+			10=待审核,21=卖家同意等待退款中,211=退款成功,
+			22=卖家驳回,31=卖家同意(用户填写退货单号),32=卖家驳回,33=等待卖家收货确认中,
+			34=卖家确认收货,验货没问题同意退款,344=退款成功 
+			 -->
+			 <!-- <TitleOperate title="申请类型" padding="10rpx 30rpx 24rpx 30rpx"></TitleOperate> -->
+			 <!-- <view class="baseP">
+				申请类型:{{applyInfo.refund_type==1?'仅退款':applyInfo.refund_type==2?'退货退款':''}}
+			 </view> -->
+			
+			<view class="conbox">
+				<TitleOperate title="售后商品" padding="30rpx 0 0"></TitleOperate>
+				<template>
+					<orderDetailGoodsCard v-for="(goods, index) in applyInfo.order_detail" :data="goods" :key="index"></orderDetailGoodsCard>
+				</template>
+			</view>
+			<view class="conbox">
+				<TitleOperate title="售后信息" padding="30rpx 0 30rpx"></TitleOperate>
+				<view class="binfo">
+					<view class="cell">
+						<view class="label">服务编号</view>
+						<view class="value">{{ applyInfo.refund_no}}</view>
+						<view class="operate">
+							<text class="copy" @click="$copyByUniappApi(applyInfo.refund_no)">复制</text>
+						</view>
+					</view>
+					<view class="cell">
+						<view class="label">退款原因</view>
+						<view class="value">{{ applyInfo.reason}}</view>
+					</view>
+					<view class="cell">
+						<view class="label">退款金额</view>
+						<view class="value money">&yen;{{ applyInfo.refundFee}}</view>
+					</view>
+					<view class="cell">
+						<view class="label">申请时间</view>
+						<view class="value">{{ formatDateF(applyInfo.createtime*1000)}}</view>
+					</view>
+					<view class="cell" v-if="applyInfo.check_time">
+						<view class="label">审核时间</view>
+						<view class="value">{{ formatDateF(applyInfo.check_time*1000)}}</view>
+					</view>
+					<view class="cell" v-if="applyInfo.status == 33" >
+						<view class="label">买家发货时间</view>
+						<view class="value">{{ formatDateF(applyInfo.updatetime*1000)}}</view>
+					</view>
+					<view class="cell" v-if="applyInfo.status == 34" >
+						<view class="label">卖家确认收货时间</view>
+						<view class="value">{{ formatDateF(applyInfo.updatetime*1000)}}</view>
+					</view>
+					<view class="cell" v-if="applyInfo.refund_success_time">
+						<view class="label">退款到账时间</view>
+						<view class="value">{{ formatDateF(applyInfo.refund_success_time*1000)}}</view>
+					</view>
+					<!-- <view class="contact">
+						<button class="item serviceBtn" open-type="contact" >
+							联系客服
+						</button>
+					</view> -->
+				</view>
+			</view>
+
+			<!-- <u-steps direction="column" :list="statusList[status]" :current="current" :active-color="activeColor"></u-steps> -->
+			
+		</view>
+	
+		<view class="">
+			<view class="foot_opar">
+				<view class="oparbtn">
+					<u-button plain size="small"  open-type="contact">联系客服</u-button>
+				</view>
+				<view class="oparbtn"  v-if="applyInfo.status == 22 || applyInfo.status == 32">
+					<u-button type="primary" size="small" @click="applyAfterSales(applyInfo.order_id)">重新申请</u-button>
+				</view>
+			</view>
+			<view class="siteBar">
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { orderOperate } from '@/pages-mall/mixins/order-operate.js';
+import orderDetailGoodsCard from '@/pages-mall/components/order/orderdetail-goods-card.vue';
+import setExpress from './components/setExpress.vue'
+import { formatDate } from '@/utils/tools.js'
+	// 组件
+	export default {
+	mixins: [orderOperate],
+		components: {
+			orderDetailGoodsCard,
+			setExpress
+		},
+		data() {
+			return {
+				status: '1',
+				current: 1,
+				expressCode:null,
+				activeColor:'#22ac38',
+				applyInfo:{
+					refund_type:null,
+					status:null,
+					order_detail:[],
+				},
+				returnBtnStyle: {
+					marginTop: '30rpx',
+				},
+				statusList: {
+					'1': [{
+						name: '提交申请'
+					}, {
+						name: '审核中'
+					},{
+						name: '退款中'
+					}, {
+						name: '退款成功'
+					}, ],
+					'2': [{
+						name: '提交申请'
+					}, {
+						name: '审核中'
+					}, {
+						name: '买家寄件'
+					}, {
+						name: '卖家处理'
+					}, {
+						name: '退款中'
+					}, {
+						name: '完成'
+					}, ],
+				},
+
+			};
+		},
+
+		onLoad(opt) {
+			console.log(opt);
+		
+			if(opt.id){
+				this.getRefundOrderDetailFun(opt.id);
+			}else{
+				uni.navigateBack();
+			}
+		},
+		onBackPress() {
+			uni.navigateBack({
+				delta:-2
+			})
+		},
+		methods: {
+			// 退货物流查询
+			tuiExpress(express_no){
+				console.log('查看退货物流',express_no);
+			},
+			getRefundOrderDetailFun(id){
+				this.$u.api.getRefundOrderDetailAjax(id).then(({code,data})=>{
+					if(code==1){
+						this.applyInfo = data;
+					}
+				}).catch(()=>{})
+			},
+			formatDateF(v){
+				return formatDate(v,'all');
+			},
+			goBack() {
+				uni.navigateBack({
+					delta: 1
+				});
+			},
+			setExpressSuccess(){
+				this.applyInfo.status = 33;
+				this.current = 3;
+			},
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.siteBar{
+		height: 120rpx;
+		// padding-bottom: constant(safe-area-inset-bottom);
+		// padding-bottom: env(safe-area-inset-bottom);
+		// box-sizing: content-box;		
+		@extend .safeAreaPad;
+	}
+	.foot_opar{
+		border-top: solid 2rpx #f2f2f2;
+		padding: 0 20rpx;
+		box-sizing: border-box;
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		height: 120rpx;
+		display: flex;
+		justify-content: flex-end;
+		align-items: center;
+		background-color: $app-theme-bg-color;
+		z-index: $app-zIndex-fixed;
+		@extend .safeAreaPad;
+		.oparbtn{
+			padding: 0 10rpx;
+		}
+	}
+	.refuseReason{
+		padding: 20rpx 30rpx;
+		background-color: #fff;
+		margin: 20rpx 0;
+	}
+	.content {
+		// padding: 30rpx;
+	}
+
+	.conbox {
+		background-color: #fff;
+		margin-bottom: 30rpx;
+		padding: 0 30rpx;
+	}
+
+	.selgoodsb {
+		text-align: center;
+
+		.t {
+			margin-bottom: 20rpx;
+			color: #666;
+		}
+	}
+	// .baseP{
+	// 	font-size: 32rpx;
+	// 	padding: 30rpx 50rpx;
+	// 	color: $app-theme-color;
+	// }
+	.returnBtn{
+		margin-top: 30rpx;
+	}
+	.statusDesc{
+		position: relative;
+		padding: 30rpx;
+		background-color: $app-theme-color;
+		color: $app-theme-text-white-color;
+		margin-bottom: 24rpx;
+		.title{
+			font-size: 36rpx;
+			margin-bottom: 50rpx;
+		}
+		.status {
+			margin-bottom: 20rpx;
+			font-size: 30rpx;
+		}
+		.desc {
+			font-size: 26rpx;
+		}
+	}
+	.binfo{
+		// padding: 30rpx;
+	}
+	.cell {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 20rpx 0;
+		flex-shrink: 0;
+		.label {
+			// width: 204rpx;
+			font-size: 28rpx;
+			color: $app-theme-text-color;
+		}
+		.value {
+			// width: 320rpx;
+			flex: 1;
+			text-align: right;
+			font-size: 28rpx;
+			color: $app-theme-text-color;
+			padding: 0 30rpx;
+		}
+		.price{
+			color: $app-theme-text-money-color;
+		}
+		.operate {
+			// width: 80rpx;
+			text-align: right;
+			.copy {
+				font-size: 28rpx;
+				color: $app-theme-color;
+			}
+		}
+	}
+	.contact{
+		border-top: 1rpx solid #eee;
+		padding: 20rpx;
+		color: $app-theme-color;
+	}
+	.serviceBtn{
+		background-color: transparent;
+		padding: 0;
+		border: none;
+		line-height: 1.2;
+		font-size: 30rpx;
+		&::after{
+			content: '';
+			border: none;
+			display: none;
+		}
+	}
+	.tExpress{
+		padding: 20rpx 30rpx;
+		background-color: #fff;
+		margin-bottom: 30rpx;
+		font-size: 30rpx;
+		.label{font-size: inherit;}
+	}
+</style>

+ 225 - 0
pages-mall/pages/after-sales/apply.vue

@@ -0,0 +1,225 @@
+<template>
+	<view class="page">
+		<!-- 商品卡片 -->
+		<TitleOperate title="售后商品" padding="10rpx 30rpx 24rpx 30rpx"></TitleOperate>
+		<view class="goods">
+			<template v-for="(goods, index) in selSalesGoods">
+				<orderDetailGoodsCard :data="goods" :key="index"></orderDetailGoodsCard>
+			</template>
+			<view class="selgoodsb" v-if="canApplyList.length>1">
+				<view class="t" v-if="selSalesGoods.length==0">
+					该订单包含多个商品,请手动选择您要退款的商品
+				</view>
+				<u-button size="small" type="primary" @click="selSalesGoodsFun()">
+					{{selSalesGoods.length>0?'重新选择':'选择商品'}}</u-button>
+			</view>
+			<view class="selgoodsb" v-else-if="canApplyList.length<1">
+				<view class="t">
+					未找到可申请售后的商品
+				</view>
+			</view>
+		</view>
+		
+		<TitleOperate title="仅退款" padding="10rpx 30rpx 24rpx 30rpx" v-if="backType == '1'" showMore moreLabel="重新选择" @clickMore="backType='home'">
+		<!-- 	<view>
+				重新选择<u-icon size="20" name="arrow-right" :color="arrowColor"></u-icon>
+			</view> -->
+		</TitleOperate>
+		<TitleOperate title="退货退款" padding="10rpx 30rpx 24rpx 30rpx" v-if="backType == '2'" showMore moreLabel="重新选择" @clickMore="backType='home'">
+			<!-- <view>
+				重新选择<u-icon size="20" name="arrow-right" :color="arrowColor"></u-icon>
+			</view> -->
+		</TitleOperate>
+		<template v-if="selSalesGoods.length>0">
+			<!-- 操作项 -->
+			<SelectItem v-if="backType == 'home'" :ops="selectOps" @change="changBackType"></SelectItem>
+			<!-- 售后原因 -->
+			<ApplyReason v-else :backType="backType" :totalPrice="totalPrice" :goodslist="selSalesGoods"
+				@done="confirmApply"></ApplyReason>
+			<!-- 仅退款 -->
+			<!-- <BackMoney v-if="backType == '1'" :totalPrice="totalPrice" :goodslist="selSalesGoods"
+				@done="confirmApply"></BackMoney> -->
+			<!-- 退货退款 -->
+			<!-- <BackAll v-if="backType == '2'"></BackAll> -->
+		</template>
+		<selectGoodsPopup ref="selectGoodsPopup" @done="comfirmSel"></selectGoodsPopup>
+	</view>
+</template>
+
+<script>
+	// 组件
+	import orderDetailGoodsCard from '@/pages-mall/components/order/orderdetail-goods-card.vue';
+	import SelectItem from '@/pages-mall/components/after-sales/select-item.vue';
+	import ApplyReason from '@/pages-mall/components/after-sales/apply-reason.vue';
+	import BackGoods from '@/pages-mall/components/after-sales/back-goods.vue';
+	import selectGoodsPopup from '@/pages-mall/components/after-sales/select-goods-pop.vue';
+	import TitleOperate from '@/components/title-operate.vue';
+	export default {
+		components: {
+			orderDetailGoodsCard,
+			SelectItem,
+			BackGoods,
+			selectGoodsPopup,
+			TitleOperate,
+			ApplyReason
+		},
+		data() {
+			return {
+				orderId: null,
+				// 选择的售后类型
+				backType: 'home',
+				canApplyList:[],
+				selOpt: {},
+				// 详情
+				orderInfo: {},
+				// 售后类型的配置项
+				selectOps: [{
+						title: '仅退款',
+						desc: '未收到货(包含未签收),或与卖家协商同意前提下',
+						icon: require('@/pages-mall/static/select-item-1.png'),
+						type: '1',
+						value: 1
+					},
+					{
+						title: '退货退款',
+						desc: '未收到货(包含未签收),或与卖家协商同意前提下',
+						icon: require('@/pages-mall/static/select-item-2.png'),
+						type: '2',
+						value: 2
+					},
+					// {
+					// 	title: '换货',
+					// 	desc: '已收到货,需要退还收到的货物',
+					// 	icon: require('@/pages-mall/static/select-item-3.png'),
+					// 	type: 'goods'
+					// }
+				],
+
+				// 选择的退款商品
+				selSalesGoods: [],
+			};
+		},
+		computed: {
+			totalPrice() {
+				let totalPrice = 0;
+				this.selSalesGoods.map(item => (totalPrice += Number(item.actual_total_amount)));
+				return totalPrice;
+			},
+		},
+		onLoad(opt) {
+			this.orderId = opt.orderId;
+			console.log('this.orderId>>>',this.orderId)
+			if (!this.orderId) {
+				uni.navigateBack();
+			}
+			this.getOrderDetail();
+		},
+
+		methods: {
+			// 导航栏返回自定义函数
+			goBack() {
+				if (this.backType == 'home') {
+					uni.navigateBack({
+						delta: 1
+					});
+				} else {
+					this.backType = 'home';
+				}
+			},
+			// 选择退款商品
+			selSalesGoodsFun() {
+				this.$refs.selectGoodsPopup.showPop(this.orderInfo)
+			},
+			getOrderDetail() {
+				this.$u.api.getOrderDetailAjax(this.orderId).then(({code,data}) => {
+					if (code == 1) {
+						this.orderInfo = data;
+						console.log('>>>>>>>11111',data);
+						if(data.order_info.status=='11'){
+							this.selectOps = [{
+								title: '仅退款',
+								desc: '未收到货(包含未签收),或与卖家协商同意前提下',
+								icon: require('@/pages-mall/static/select-item-1.png'),
+								type: '1',
+								value: 1
+							}]
+						}
+						// 过滤可申请的商品列表
+						this.filCanApplyList();
+						
+					}
+				}).catch()
+			},
+			// 过滤可申请售后的商品列表
+			filCanApplyList(){
+				console.log(this.orderInfo.order_goods_info);
+				this.canApplyList = this.orderInfo.order_goods_info.filter(e=>(e.sub_status=='10'||e.sub_status=='22'||e.sub_status=='32'));
+				console.log(this.canApplyList);
+				if (this.canApplyList.length == 1) {
+					this.selSalesGoods = this.canApplyList;
+				}
+			},
+			// 选择售后类型
+			changBackType(e) {
+				this.backType = e.item.type;
+				this.selOpt = e.item;
+			},
+			// 多商品确认选择
+			comfirmSel(e) {
+				this.selSalesGoods = e;
+			},
+			// 确认退款
+			confirmApply(e) {
+				// uni.redirectTo({
+				// 	url: '/pages-mall/pages/after-sales/apply-status?refund_type='+this.backType+'&status=10'
+				// })
+				// return;
+				let ids = this.selSalesGoods.map((e) => {
+					return e.id
+				});
+				this.$u.api.refundOrderAjax({
+					order_id: this.orderInfo.order_info.id,
+					order_detail_ids: ids.toString(),
+					refund_type:this.selOpt.value,
+					...e,
+				}).then(({code,data}) => {
+					if (code == 1) {
+						// 默认进入对应售后场景的第一步状态
+						// uni.redirectTo({
+						// 	url: '/pages-mall/pages/after-sales/apply-status?refund_type='+this.backType+'&status=10'
+						// })
+						uni.redirectTo({
+							url: '/pages-mall/pages/after-sales/success?refundId='+data.id
+						})
+					}
+				}).catch(()=>{
+					
+				})
+
+			},
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.page {
+		padding: 30rpx;
+	}
+
+	.goods {
+		border-radius: 16rpx;
+		box-shadow: $app-theme-shadow;
+		padding: 30rpx;
+		margin-bottom: 24rpx;
+		background-color: $app-theme-bg-color;
+	}
+
+	.selgoodsb {
+		text-align: center;
+
+		.t {
+			margin-bottom: 20rpx;
+			color: #666;
+		}
+	}
+</style>

+ 118 - 0
pages-mall/pages/after-sales/back.vue

@@ -0,0 +1,118 @@
+<template>
+	<view class="page">
+		<!-- 退款状态 -->
+		<BackStatus :data="orderInfo" type="0"></BackStatus>
+		<!-- 退款信息 -->
+		<view class="back-info">
+			<view class="item">
+				<view class="label">退款金额</view>
+				<view class="value">¥180.00</view>
+			</view>
+			<view class="item">
+				<view class="label">退回至微信账户</view>
+				<view class="value">¥180.00</view>
+			</view>
+		</view>
+		<!-- 退款节点 -->
+		<view class="back-points"><u-steps style="width: 100%;" :list="backDotList" mode="dot" :current="backDotCurrent"></u-steps></view>
+		<!-- 退款商品及订单信息 -->
+		<view class="back-order-info">
+			<TitleOperate title="退款信息" titleSize="30rpx"></TitleOperate>
+			<view class="inner">
+				<OrderGoodsCard :data="orderInfo" showBorderBottom></OrderGoodsCard>
+				<LineInfoOperate :ops="backInfoOps"></LineInfoOperate>
+			</view>
+		</view>
+		<!-- 电话 -->
+		<view class="phone">
+			<u-icon name="phone-fill" :color="appThemeColor"></u-icon>
+			<text>售后电话</text>
+		</view>
+	</view>
+</template>
+
+<script>
+import BackStatus from '@/pages-mall/components/after-sales/back-status.vue';
+import OrderGoodsCard from '@/pages-mall/components/order/order-goods-card.vue';
+import TitleOperate from '@/components/title-operate.vue';
+import LineInfoOperate from '@/pages-mall/components/line-info-operate.vue';
+export default {
+	components: {
+		BackStatus,
+		OrderGoodsCard,
+		TitleOperate,
+		LineInfoOperate
+	},
+	data() {
+		return {
+			appThemeColor:this.$appTheme.appThemeColor,
+			// 订单详情
+			orderInfo: {},
+			// 退款节点
+			backDotCurrent: 1,
+			backDotList: [{ name: '买家申请' }, { name: '商家同意' }, { name: '退款成功' }],
+			// 退款信息配置项
+			backInfoOps: [
+				{ label: '退款原因', value: '不喜欢', operate: 'copy' },
+				{ label: '退款金额', value: '¥180.00', operate: '' },
+				{ label: '申请件数', value: '1', operate: '' },
+				{ label: '申请时间', value: '2021-01-11 14:19:56', operate: '' },
+				{ label: '退款编号', value: 'Wl0070672207892', operate: '' }
+			]
+		};
+	},
+	onLoad() {
+		this.orderInfo = uni.getStorageSync('orderInfo');
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.back-info {
+	padding: 0 30rpx;
+	background-color: $app-theme-bg-color;
+	margin-bottom: 24rpx;
+	.item {
+		padding-top: 28rpx;
+		padding-bottom: 28rpx;
+		border-bottom: 1rpx solid $app-theme-border-color;
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		.label {
+			font-size: 30rpx;
+			color: $app-theme-text-black-color;
+		}
+		.value {
+			font-size: 30rpx;
+			color: $app-theme-text-black-color;
+		}
+	}
+}
+
+.back-points {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	padding: 60rpx 30rpx;
+	background-color: $app-theme-bg-color;
+	margin-bottom: 24rpx;
+}
+
+.back-order-info {
+	background-color: $app-theme-bg-color;
+	.inner {
+		padding: 0 30rpx;
+	}
+}
+.phone {
+	padding: 30rpx;
+	background-color: $app-theme-bg-color;
+	text-align: center;
+	font-size: 28rpx;
+	color: $app-theme-text-gray-color;
+	text{
+		margin-left: 8rpx;
+	}
+}
+</style>

+ 173 - 0
pages-mall/pages/after-sales/components/setExpress.vue

@@ -0,0 +1,173 @@
+<template>
+	<view class="express">
+		<!-- <u-input v-model="expressCode" type="text" :border="true" placeholder="请填写退货快递单号" />
+		<u-button type="primary" @click="expressCodeConfirm()">确认</u-button> -->
+		<view class="address">
+			<view class="title">
+				退货地址:
+			</view>
+			<u-parse :html="expressInfo.company_address"></u-parse>
+		</view>
+		<view class="frombox">
+			<u-alert-tips type="warning" :title="'请填写正确的退货包裹运单信息'" ></u-alert-tips>
+			<u-form :model="model" ref="uForm" :rules="rules" label-width="160rpx"  :errorType="errorType">
+				<u-form-item label="运单号" prop="express_no">
+					<u-input v-model="model.express_no" />
+				</u-form-item>
+				<u-form-item label="物流公司" prop="express_name">
+					<u-input v-model="model.express_name" type="select"  :select-open="expressShow"  @click="expressShow = true" />
+				</u-form-item>
+				
+			</u-form>
+			<view style="margin-top: 30rpx;">
+				<u-button type="primary" @click="submit">确认</u-button>
+			</view>
+		</view>
+		<u-select mode="single-column" :list="expressList" v-model="expressShow" value-name="code" label-name="name" @confirm="selectConfirm"></u-select>
+	</view>
+</template>
+
+<script>
+	export default {
+		props:{
+			refundId:{
+				type:String||Number,
+				default:null
+			}
+		},
+		data() {
+			return {
+				address:'孙先生  18836586985 \r\n 和湖南那是大佛阿斯加德菲拉斯对接客服拉圣诞节快乐',
+				expressShow:false,
+				selExpress:{},
+				expressList:[{
+					code:'yunda',
+					name:'韵达'
+				}],
+				model:{
+					express_no:'',
+					express_name:'',
+				},
+				rules: {
+					express_no: [
+						{
+							required: true,
+							message: '请输入运单号',
+							trigger: ['blur', 'change']
+						}
+					],
+					express_name: [
+						{
+							required: true,
+							message: '请选择物流公司',
+							trigger: ['blur', 'change']
+						}
+					]
+				},
+				errorType:['message']
+			}
+		},
+		
+		computed: {
+			// 运费模板
+			expressInfo() {
+				return this.$store.state.pub.baseConfig;
+			},
+		},
+		mounted() {
+			this.getExpressList();
+			if(!this.$store.state.pub.baseConfig.company_address){
+				this.getExpressInfo();
+			}
+		},
+		methods: {
+			
+			// 获取运费模板信息
+			getExpressInfo(){
+				this.$u.api.getBaseInfoAjax().then(({data})=>{
+					this.$store.commit('pub/commitBaseConfig',data);
+				}).catch(()=>{
+					
+				})
+			},
+			// 选择商品类型回调
+			selectConfirm(e) {
+				console.log(e);
+				this.selExpress = e[0];
+				console.log(this.selExpress);
+				this.model.express_name = this.selExpress.label;
+				// this.model.goodsType = '';
+				// e.map((val, index) => {
+				// 	this.model.goodsType += this.model.goodsType == '' ? val.label : '-' + val.label;
+				// })
+			},
+			getExpressList(){
+				this.$u.api.getExpressListAjax().then(({code,data})=>{
+					// console.log(code,data);
+					if(code==1){
+						this.expressList = data;
+					}
+					
+				})
+			},
+			expressCodeConfirm(){
+				if(!this.model.express_no){
+					this.$u.toast('请输入物流单号')
+					return;
+				}
+				if(!this.selExpress.value){
+					this.$u.toast('请选择物流公司')
+					return;
+				}
+				/* console.log({
+					express_code:this.selExpress.value,
+					express_no:this.model.express_no,
+					refund_id:this.refundId,
+				})
+				return; */
+				this.$u.api.setExpressCodeAjax({
+					express_code:this.selExpress.value,
+					express_no:this.model.express_no,
+					refund_id:this.refundId,
+				}).then(({code,data})=>{
+					if(code==1){
+						this.$emit('done')
+					}
+					
+				})
+				
+			},
+			submit() {
+				this.$refs.uForm.validate(valid => {
+					if (valid) {
+						this.expressCodeConfirm();
+					} else {
+						console.log('验证失败');
+					}
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.address{
+		background-color: #fff;
+		padding: 0 30rpx 30rpx;
+		margin-bottom: 20rpx;
+		.title{
+			padding: 20rpx 0;
+			margin-bottom: 30rpx;
+			border-bottom: 1rpx solid #eee;
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+	}
+	.frombox{
+		background-color: #fff;
+		padding: 30rpx;
+	}
+.express{
+	
+}
+</style>

+ 239 - 0
pages-mall/pages/after-sales/list.vue

@@ -0,0 +1,239 @@
+<template>
+	<view class="page">
+		<view class="list" v-if="refundOrder&&refundOrder.length>0">
+			<view class="list_item" v-for="(item, index) in refundOrder" :key="index">
+				<view class="ht">
+					服务单号:{{item.refund_no}}
+				</view>
+				<view class="list_g_d" v-for="(son, sonIndex) in item.order_detail" :key="sonIndex">
+					<view class="content">
+						<view class="pic">
+							<u-image width="160rpx" height="160rpx" radius="20rpx" :src="son.goods_cover"></u-image>
+						</view>
+						<view class="info">
+							<view class="title">{{ son.goods_title }}</view>
+							<view class="desc">
+								<u-tag :text="son.goods_sku" size="mini" type="info" />
+								<text>x{{ son.goods_nums }}</text>
+								<!-- <view class="nums">
+									<text>数量:</text>
+									
+								</view> -->
+								<!-- <text class="price">申请金额:¥{{ son.total_amount }}</text> -->
+							</view>
+							
+						</view>
+					</view>
+					<!-- <view class="operate_son">
+						<u-button v-if="son.sub_status == 31" class="oparbtn" type="primary" size="mini" shape="circle" @click.stop="afterSalesDetail(son)">去填写单号</u-button>
+						<u-button v-else class="oparbtn" size="mini" shape="circle" @click.stop="afterSalesDetail(son)">售后详情</u-button>
+					</view> -->
+					
+				</view>
+				<view class="saleAfter_status">
+					<text v-if="item.status == 10">等待审核</text>
+					<text v-if="item.status == 21">审核通过,正在退款中</text>
+					<text class="color_blue" v-if="item.status == 211">退款成功</text>
+					<text class="price_color" v-if="item.status == 22">审核失败</text>
+					<text v-if="item.status == 31">审核通过,请填写退货快递单号</text>
+					<text class="price_color" v-if="item.status == 32">退货退款,审核未通过</text>
+					<text v-if="item.status == 33">等待卖家收货确认</text>
+					<text v-if="item.status == 34">卖家已确认收货,正在退款中</text>
+					<text class="color_blue" v-if="item.status == 344">退款成功</text>
+				</view>
+				<view class="operate">
+					<view class="paystyle">
+						申请金额:{{item.refundFee}}
+					</view>
+					<!-- <view class="" v-if="item.status==10">
+						等待客服审核
+					</view>
+					<view class="" v-if="item.status == 21">
+						退款已退回原账户
+					</view>
+					<view class="paystyle" v-if="['22','32'].includes(item.status)">
+						审核失败
+					</view> -->
+					<u-button v-if="item.status == 31" class="oparbtn" type="primary" size="mini" shape="circle" @click.stop="afterSalesDetail(item.id)">去填写单号</u-button>
+					<u-button v-else class="oparbtn" size="mini" shape="circle" @click.stop="afterSalesDetail(item.id)">售后详情</u-button>
+					<!-- <u-button class="oparbtn" v-if="['22','32'].includes(item.status)"  type="primary" size="small" shape="circle" @click.stop="applyAfterSales(item.order_id)">重新申请</u-button>
+					<u-button class="oparbtn" v-if="item.status==30"  type="primary" size="small" shape="circle" @click.stop="applyAfterSales(item.order_id)">重新申请</u-button> -->
+					
+				</view>
+			</view>
+			
+		</view>
+		<NoData height="60vh" type="order" v-if="finish&&refundOrder.length==0"></NoData>		
+		<!-- <u-loadmore v-else :status="finish?'nomore':loading?'loading':'loadmore'" :load-text="loadText" margin-top="30" /> -->
+		
+		<LoadMore v-else :loadtype="{finish,loading}"></LoadMore>
+	</view>
+</template>
+
+<script>
+// 组件
+import { orderOperate } from '@/pages-mall/mixins/order-operate.js';
+
+const app = getApp();
+var _self;
+export default {
+	mixins: [orderOperate],
+	data() {
+		return {
+			page:1,
+			loading:false,
+			finish:false,
+			loadText: {
+				loadmore: '轻轻上拉',
+				loading: ' ',
+				nomore: '没有更多了'
+			},
+			refundOrder:[],//售后订单
+		};
+	},
+	onLoad(ops) {
+		_self = this;
+	},
+	onShow() {
+		_self.getRefundOrderList();
+	},
+	
+	onPullDownRefresh(){
+		// uni.stopPullDownRefresh();
+		this.page=1;
+		this.finish=false;
+		this.refundOrder=[];
+		this.getData();
+	},
+	onReachBottom(){
+		console.log('滚动到底部')
+		this.getData();
+	},
+	methods: {
+		// 刷新页面
+		refreshData(){
+			uni.startPullDownRefresh();
+		},
+		// 更新数据
+		getData(){
+			this.getRefundOrderList();
+		},
+		// 退款售后
+		getRefundOrderList(){
+			if(this.finish){
+				return false;
+			}
+			this.loading = true;
+			this.$u.api.getRefundOrderListAjax({
+				page:this.page,
+			}).then(({code,data})=>{
+				uni.stopPullDownRefresh();
+				this.loading = false;
+				if(code==1){
+					if(data.last_page<=data.current_page){
+						this.finish = true;
+					}
+					if(data.current_page==1){
+						this.refundOrder = data.data;
+					}else{
+						this.refundOrder=this.refundOrder.concat(data.data);
+					}
+				}else{
+					this.finish = true;
+				}				
+				this.page = data.current_page+1;
+			}).catch(()=>{
+				uni.stopPullDownRefresh();
+				this.loading = false;
+				this.finish = true;
+			})
+		},
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.pagecon{
+	padding-bottom: 30rpx;
+}
+.list {
+	padding:30rpx 30rpx;
+	box-sizing: border-box;
+	&_item{
+		background-color: #ffffff;
+		border-radius: 16rpx;
+		box-shadow: 0px 2px 8px 0px rgba(27, 25, 86, 0.06);
+		margin-bottom: 20rpx;
+		.ht{
+			height: 80rpx;
+			display: flex;
+			align-items: center;
+			color: $app-theme-text-gray-color;
+			border-bottom: 1rpx solid $app-theme-border-color;
+			padding: 0 30rpx;
+		}
+		.paystyle{
+			// font-weight: bold;
+			margin-right: 20rpx;
+			color: $app-theme-text-money-color;
+		}
+	}
+}
+
+.list_g_d{
+	margin: 20rpx;
+	.content{
+		display: flex;
+		align-content: space-between;
+		.pic {
+			margin-right: 24rpx;
+		}
+		.info {
+			flex: 1;
+			.title {
+				width: 100%;
+				line-height: 1.2em;
+				// height: 2.4em;
+				display: -webkit-box;
+				overflow: hidden;
+				-webkit-line-clamp: 2;
+				-webkit-box-orient: vertical;
+				font-size: 28rpx;
+				font-weight: 400;
+				color: $app-theme-text-black-color;
+			}
+			.desc {
+				display: flex;
+				justify-content: space-between;
+				align-items: flex-end;
+				margin-top: 10rpx;
+			}
+			.price {
+				color: $app-theme-text-money-color;
+			}
+		}
+	}
+		
+}
+.saleAfter_status{
+	color: $app-theme-points-yellow-color;
+	margin: 10rpx 30rpx;
+	text-align: right;
+	
+}
+.operate{
+	display: flex;
+	justify-content: flex-end;
+	align-items: center;
+	padding: 0 30rpx;
+	height: 80rpx;
+	border-top: 1rpx solid $app-theme-border-color;
+	.oparbtn{
+		margin-left: 20rpx;
+	}
+}
+
+.operate_son {
+	text-align: right;
+}
+</style>

+ 139 - 0
pages-mall/pages/after-sales/success.vue

@@ -0,0 +1,139 @@
+<template>
+	<view class="content">
+		<view class="statusDesc">
+			<view class="status">
+				审核中
+			</view>
+			<view class="applyDesc">
+				<view class="title">请在“我的售后”页面查看详情</view>
+			</view>
+		</view>
+			<view class="conbox">
+				<!-- type="primary"  -->
+				<u-button @click="afterSalesDetail(refundId)">查看售后</u-button>
+			</view>
+	
+	</view>
+</template>
+
+<script>
+	// 组件
+import { orderOperate } from '@/pages-mall/mixins/order-operate.js';
+	export default {
+	mixins: [orderOperate],
+		data() {
+			return {
+				refundId:null,
+			};
+		},
+
+		onLoad(opt) {
+			console.log(opt);
+			this.refundId = opt.refundId;
+		},
+		onBackPress() {
+			uni.navigateBack({
+				delta:-3
+			})
+		},
+
+		methods: {
+			
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.content {
+		// padding: 30rpx;
+	}
+
+	.conbox {
+		background-color: #fff;
+		margin-bottom: 30rpx;
+		padding: 0 30rpx;
+	}
+
+	.selgoodsb {
+		text-align: center;
+
+		.t {
+			margin-bottom: 20rpx;
+			color: #666;
+		}
+	}
+	// .baseP{
+	// 	font-size: 32rpx;
+	// 	padding: 30rpx 50rpx;
+	// 	color: $app-theme-color;
+	// }
+	.returnBtn{
+		margin-top: 30rpx;
+	}
+	.statusDesc{
+		position: relative;
+		padding: 30rpx;
+		background-color: $app-theme-color;
+		color: $app-theme-text-white-color;
+		margin-bottom: 24rpx;
+		.title{
+			font-size: 36rpx;
+			margin-bottom: 50rpx;
+		}
+		.status {
+			margin-bottom: 20rpx;
+			font-size: 30rpx;
+		}
+		.desc {
+			font-size: 26rpx;
+		}
+	}
+	.binfo{
+		// padding: 30rpx;
+	}
+	.cell {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 20rpx 0;
+		.label {
+			width: 204rpx;
+			font-size: 28rpx;
+			color: $app-theme-text-color;
+		}
+		.value {
+			width: 320rpx;
+			text-align: right;
+			font-size: 28rpx;
+			color: $app-theme-text-color;
+		}
+		.price{
+			color: $app-theme-text-money-color;
+		}
+		.operate {
+			width: 80rpx;
+			text-align: right;
+			.copy {
+				font-size: 28rpx;
+				color: $app-theme-color;
+			}
+		}
+	}
+	.contact{
+		border-top: 1rpx solid #eee;
+		padding: 20rpx;
+		color: $app-theme-color;
+	}
+	.serviceBtn{
+		background-color: transparent;
+		padding: 0;
+		border: none;
+		line-height: 1.2;
+		font-size: 30rpx;
+		&::after{
+			content: '';
+			border: none;
+			display: none;
+		}
+	}
+</style>

+ 140 - 0
pages-mall/pages/evaluate/add.vue

@@ -0,0 +1,140 @@
+<template>
+	<view class="page">
+		<!-- 通知 -->
+		<view class="tip">
+			<text class="light">😛</text>
+			<text class="light">评价即可</text>
+			<text>抽奖</text>
+			<text>, 15字+图片还有机会成为</text>
+			<text class="light">「精选晒单」</text>
+		</view>
+		<!-- 表单 -->
+		<view class="card">
+			<!-- 商品 -->
+			<view class="goods"><OrderGoodsCard :data="orderInfo" showBorderBottom></OrderGoodsCard></view>
+			<!-- 评价 -->
+			<view class="evaluate">
+				<!-- 评分 -->
+				<u-form-item label-width="140rpx" :border-bottom="false" label="商品评分">
+					<u-rate size="38" slot="right" :count="5" v-model="form.star" :inactive-color="appThemeCardGrayColor" :active-color="appThemeColor"></u-rate>
+				</u-form-item>
+				<!-- 内容 -->
+				<view class="form">
+					<view class="content"><u-input height="200" type="textarea" placeholder="写下您的评价吧"></u-input></view>
+					<view class="pics">
+						<!-- 图片上传 -->
+						<u-upload
+							:deleteConfirmBtnColor="appThemeColor"
+							width="180"
+							height="180"
+							max-count="9"
+							:max-size="1024 * 1024 * 10"
+							:action="uploadUrl"
+							:auto-upload="true"
+							@on-success="uploadPicSuccess"
+						></u-upload>
+					</view>
+					<u-form-item label-width="140rpx" :border-bottom="false" label="匿名评价">
+						<u-switch size="40" slot="right" v-model="form.cryptonym" :active-color="appThemeColor"></u-switch>
+					</u-form-item>
+				</view>
+			</view>
+		</view>
+		<!-- 按钮 -->
+		<view class="btn">
+			<u-button type="primary" shape="circle" @click="submit"><text>提交</text></u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+import OrderGoodsCard from '@/pages-mall/components/order/order-goods-card.vue';
+export default {
+	components: {
+		OrderGoodsCard
+	},
+	data() {
+		return {
+			// 商品详情
+			orderInfo: {},
+			// 主题色
+			appThemeColor: this.$appTheme.appThemeColor,
+			appThemeCardGrayColor: this.$appTheme.appThemeCardGrayColor,
+			// 表单
+			form: {
+				star: 5,
+				cryptonym: false,
+				pics: []
+			},
+			// 上传地址
+			uploadUrl: ''
+		};
+	},
+	onLoad() {
+		this.orderInfo = uni.getStorageSync('orderInfo');
+	},
+	methods: {
+		// 上传图片成功
+		uploadPicSuccess(data, index, lists, name) {
+			let obj = {
+				src: data.data.src,
+				name: name || '未命名图片.png',
+				type: 0
+			};
+			this.form.pics.push(obj);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding: 30rpx;
+	padding-top: 110rpx;
+}
+.tip {
+	position: absolute;
+	top: 10vh;
+	left: 0;
+	width: 100%;
+	padding: 0rpx 30rpx;
+	height: 80rpx;
+	line-height: 80rpx;
+	background-color: rgba($app-theme-color, 0.1);
+	z-index: $app-zIndex-absolute;
+	text {
+		color: $app-theme-card-gray-deep-color;
+	}
+	.light {
+		color: $app-theme-color;
+		font-size: 28rpx;
+	}
+
+	text:first-child {
+		margin-right: 18rpx;
+	}
+}
+.card {
+	border-radius: 16rpx;
+	box-shadow: $app-theme-shadow;
+	padding: 30rpx;
+	margin-bottom: 24rpx;
+	background-color: $app-theme-bg-color;
+	.rate {
+		padding: 0 0 30rpx 0;
+		// 视觉对齐
+		margin-left: -8rpx;
+	}
+	.form {
+	}
+	.pics {
+		padding-bottom: 30rpx;
+		border-bottom: 1rpx solid $app-theme-border-color;
+		// 视觉对齐
+		margin-left: -12rpx;
+	}
+}
+.btn {
+	padding: 60rpx 0rpx;
+}
+</style>

+ 36 - 0
pages-mall/pages/evaluate/list.vue

@@ -0,0 +1,36 @@
+<template>
+	<view class="page">
+		<view class="evaluate-list">
+			<EvaluateCard
+				:data="evaluate"
+				v-for="(evaluate, evaluateIndex) in evaluateData"
+				:key="evaluateIndex"
+				:showBorderBottom="evaluateIndex != evaluateData.length - 1"
+			></EvaluateCard>
+		</view>
+	</view>
+</template>
+
+<script>
+import EvaluateCard from '@/pages-mall/components/evaluate-card.vue';
+export default {
+	components: {
+		EvaluateCard
+	},
+	data() {
+		return {
+			evaluateData: [
+				{ star: 4, content: '非常好用!会推荐给家人', date: '2022-01-12', userName: '演示用户', pics: ['http://sdyy.apicloud-system.com/images2/img/imgThree.png'] },
+				{ star: 4, content: '非常好用!会推荐给家人', date: '2022-01-12', userName: '演示用户', pics: ['http://sdyy.apicloud-system.com/images2/img/imgThree.png'] }
+			]
+		};
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.evaluate-list {
+	background-color: $app-theme-bg-color;
+	padding: 0 30rpx 0rpx 30rpx;
+}
+</style>

+ 225 - 0
pages-mall/pages/goods/detail.vue

@@ -0,0 +1,225 @@
+<template>
+	<view class="page">
+		<!-- 商品图片 -->
+		<ImgSwiper :list="[goodsDetail.cover]"></ImgSwiper>
+		<!-- 商品信息 -->
+		<GoodsInfo :data="goodsDetail" :goodsType="goodsType"  :selectedSku="selectedSku"></GoodsInfo>
+		<!-- 选择项 -->
+		<GoodsSelect :selectedSku="selectedSku" :expressInfo="expressInfo" :selectedAddress="selectedAddress" @openSku="openSkuPopup"></GoodsSelect>
+
+		<!-- 商品详情 -->
+		<TitleOperate title="商品详情" :backgroundColor="bgColor" titleSize="32rpx" align="center"></TitleOperate>
+		<!-- <view class="details-html" v-html="goodsDetail.html"></view> -->
+		<view class="detailsBox">
+			<view class="p">
+				<text>书名:{{goodsDetail.title||''}}</text>
+			</view>
+			<view class="p">
+				<text>定价:{{goodsDetail.price_market||''}}</text>
+			</view>
+			<view class="p">
+				<text>作者:{{goodsDetail.author||''}}</text>
+			</view>
+			<view class="p">
+				<text>出版社:{{goodsDetail.publish||''}}</text>
+			</view>
+			<view class="p">
+				<text>出版日期:{{goodsDetail.pub_date||''}}{{goodsDetail.pub_time?'-'+goodsDetail.pub_time:''}}</text>
+			</view>
+			<view class="p">
+				<text>ISBN:{{goodsDetail.isbn||''}}</text>
+			</view>
+		</view>
+		
+		<!-- 底部操作按钮 -->
+		<submitBar :data="goodsDetail" @addShoppingCart="addShoppingCart" @buyNow="buyNow"></submitBar>
+		<!-- 选择sku -->
+		<GoodsSelectSku ref="GoodsSelectSkuRef" :expressInfo="expressInfo" @addShoppingCart="addShoppingCart" @buyNow="buyNow" @change="changeSku"></GoodsSelectSku>
+		
+		<!-- <u-skeleton :loading="pageLoading" :animation="true" bgColor="#FFF"></u-skeleton> -->
+	</view>
+</template>
+
+<script>
+import TitleOperate from '@/components/title-operate.vue';
+import ImgSwiper from '@/components/img-swiper.vue';
+import GoodsInfo from '@/pages-mall/components/goods/goods-info.vue';
+import GoodsSelect from '@/pages-mall/components/goods/goods-select.vue';
+import submitBar from '@/pages-mall/components/goods/submitBar.vue';
+import GoodsSelectSku from '@/pages-mall/components/goods/goods-select-sku.vue';
+import CardGoods from '@/pages/mall/components/card.vue';
+var _self;
+export default {
+	components: {
+		TitleOperate,
+		ImgSwiper,
+		GoodsInfo,
+		GoodsSelect,
+		submitBar,
+		GoodsSelectSku,
+		CardGoods
+	},
+	data() {
+		return {
+			pageLoading:false,
+			// 背景色
+			bgColor: this.$appTheme.appThemeBgColor,
+			// 商品类型,normal、points
+			goodsType: 'normal',
+			goodsId:null,
+			goodsDetail:{},
+			// 相关商品
+			goodsData: [],
+			// 已选择sku
+			selectedSku: { difference: '', id: 0 },
+			// 已选择地址
+			selectedAddress: {},
+			// 浏览历史
+			browseHistory:[],
+		};
+	},
+	onLoad(ops) {
+		_self = this;
+		const token = uni.getStorageSync('token');
+		if(!token){
+			uni.navigateTo({
+				url:'/pages/login/index'
+			})
+			return false;
+		}
+		if (ops.goodsId){
+			this.goodsId = ops.goodsId;
+			this.getGoodsDetail();
+			if(!this.$store.state.pub.baseConfig.express_type){
+				this.getExpressInfo();
+			}
+		}else{
+			uni.navigateBack();
+		}
+	},
+	onShow() {
+		uni.$once('addressInfo',function(data){
+			console.log(data);
+			_self.selectedAddress = data;
+		})
+	},
+	computed: {
+		// 运费模板
+		expressInfo() {
+			return this.$store.state.pub.baseConfig;
+		},
+	},
+	methods: {
+		// 足迹
+		addBrowHistory(){
+			const hist = uni.getStorageSync('browseHistory');
+			console.log('browseHistory>>>',hist)
+			if(hist){
+				let index = hist.findIndex(e=>e.id===this.goodsDetail.id);
+				console.log('查到了index>>',index);
+				if(index>-1){
+					// 之前在缓存中存在了,先删除,在追加到第一位
+					hist.splice(index, 1);
+				}
+			//将 e 重新追加到数组第一位
+				this.browseHistory = [].concat(hist);
+			}
+			/* 
+			 author: "刘浪"
+			 cate_id: "61"
+			 code: "wk-755732"
+			 cover: "https://bookyun.oss-cn-qingdao.aliyuncs.com/bookpic/xyg9787569205107.jpg"
+			 express_template_id: 0
+			 expresstemplate: null
+			 id: 2
+			 is_collection: 1
+			 isbn: "9787569205107"
+			 price_selling: "0.01"
+			 pub_date: "2017-05"
+			 pub_time: "1"
+			 publish: "吉林大学出版社"
+			 sku: [{id: 28, admin_id: 1, goods_id: 2, stock_total: 100, price_selling: "0.01", need_score: null,…},…]
+			 spu: [{id: 10, spu_id: 1, goods_id: 2, name: "品相", item: "良品,一般,次品", createtime: 1711707864,…}]
+			 stock_sales: 11221
+			 stock_total: "278"
+			 tags_type: "hot"
+			 title: "医药数理统计"
+			 */
+			let json = {
+				id:this.goodsDetail.id,
+				cover:this.goodsDetail.cover,
+				title:this.goodsDetail.title,
+				publish:this.goodsDetail.publish,
+			};
+			// 追加到第一位
+			this.browseHistory.unshift(json);
+			//只保存前100
+			this.browseHistory.slice(0, 100);
+			uni.setStorageSync('browseHistory', this.browseHistory);
+		},
+		// 获取运费模板信息
+		getExpressInfo(){
+			this.$u.api.getBaseInfoAjax().then(({data})=>{
+				console.log(data);
+				this.$store.commit('pub/commitBaseConfig',data);
+			}).catch(()=>{
+				
+			})
+		},
+		// 查询商品详情
+		getGoodsDetail() {
+			this.pageLoading = true;
+			this.$u.api.getGoodsDetailAjax(this.goodsId).then(({code,data})=>{
+				this.pageLoading = false;
+				this.goodsDetail = data;
+				// this.selectedSku = this.goodsDetail.sku[1]||this.goodsDetail.sku[0];
+				this.$refs.GoodsSelectSkuRef.initSkuFun(this.goodsDetail);
+				
+				// 进入足迹
+				this.addBrowHistory();
+			});
+		},
+
+		// 打开选择sku的弹窗
+		openSkuPopup() {
+			console.log(222);
+			this.$refs.GoodsSelectSkuRef.open(this.goodsDetail);
+		},
+
+		// 切换sku
+		changeSku(e) {
+			this.selectedSku = e;
+		},
+
+		// 加入购物车
+		addShoppingCart() {
+			this.openSkuPopup();
+		},
+
+		// 立即购买
+		buyNow() {
+			console.log(111);
+			this.openSkuPopup();
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+	// .page{
+	// 	padding-bottom: 130rpx;
+	// }
+	.details-html {
+		background-color: $app-theme-bg-color;
+		padding: 0 30rpx 30rpx 30rpx;
+		margin-bottom: 100rpx;
+	}
+
+	.detailsBox{
+		padding: 30rpx;
+		background-color: #fff;
+		.p{
+			line-height: 1.5;
+		}
+	}
+</style>

+ 101 - 0
pages-mall/pages/goods/list1.vue

@@ -0,0 +1,101 @@
+<!-- 分类列表 -->
+<template>
+	<view class="page">
+		<!-- 带搜索框的一般导航栏 -->
+		<NavbarSearch :title="'分类/'+cateInfo.name" v-model="searchVal"  @sortType="sortTypeChange" @confirm="confirmSearch"></NavbarSearch>
+		<!-- 商品列表 -->
+		<view class="list">
+			<!-- 瀑布流组件 -->
+			<u-waterfall ref="topicWaterFall" v-model="goodsList" marginLeft="7rpx" marginRight="7rpx">
+				<template v-slot:left="{ leftList }">
+					<CardGoods v-for="(item, index) in leftList" :key="index" :data="item" :showOldMoney="item.oldMoney"></CardGoods>
+				</template>
+				<template v-slot:right="{ rightList }">
+					<CardGoods v-for="(item, index) in rightList" :key="index" :data="item" :showOldMoney="item.oldMoney"></CardGoods>
+				</template>
+			</u-waterfall>
+		</view>
+	</view>
+</template>
+
+<script>
+// 组件
+import CardGoods from '@/pages/mall/components/card.vue';
+// import NavbarSearch from '@/pages-mall/components/navbar-search.vue';
+import NavbarSearch from '@/components/navbar/navbar-search.vue';
+// 假数据
+import { recommendGoodsList as goodsList } from '@/static/test-data.js';
+export default {
+	components: {
+		CardGoods,
+		NavbarSearch
+	},
+	data() {
+		return {
+			searchVal:null,
+			cateInfo:{
+				name:null,
+			},
+			goodsList: goodsList,
+		};
+	},
+	onLoad(opt) {
+		console.log(opt.cateInfo)
+		if(opt.cateInfo){
+			console.log(this.cateInfo)
+			this.cateInfo = JSON.parse(decodeURIComponent(opt.cateInfo));
+			console.log(this.cateInfo)
+		}
+	},
+	methods:{
+		sortTypeChange(e) {
+			console.log('eee>>>>>',e);
+			this.sortType=e;
+			this.page = 1;
+			this.finish = false;
+			this.goodsList=[];
+			this.getGoodsList();
+		},
+		confirmSearch(e){
+			console.log('搜索关键字>>>>',e);
+			this.searchKeyWord = e;
+			this.getGoodsList();
+		},
+		// 查询商品
+		getGoodsList() {
+			if(this.finish)return false;
+			if(this.loading) return false;
+			this.loading = true;
+			this.$u.api.getGoodsListAjax({
+				title: this.searchKeyWord,
+				page: this.page,
+				...this.sortType
+			}).then(({code,data})=>{
+				this.loading = false;
+				if(code==1){
+					if(data.last_page<=data.current_page){
+						this.finish = true;
+					}
+					if(data.current_page==1){
+						this.goodsList = data.data;
+					}else{
+						this.goodsList=this.goodsList.concat(data.data);
+					}
+				}else{
+					this.finish = true;
+				}				
+				this.page = data.current_page+1;
+			}).catch(()=>{
+				this.loading = false;
+				this.finish = true;
+			})
+		},
+	},
+};
+</script>
+
+<style lang="scss" scoped>
+.list {
+	padding: 30rpx;
+}
+</style>

+ 136 - 0
pages-mall/pages/order/detail.vue

@@ -0,0 +1,136 @@
+<template>
+	<view class="page">
+		<!-- 订单状态 -->
+		<OrderStatus :type="orderInfo.status" :data="{order_details:goodsList,...orderInfo}"></OrderStatus>
+		<!-- 地址 -->
+		<view class="address">
+			<AddressCard :data="addressInfo" :isGoSelect="false"></AddressCard>
+		</view>
+		<!-- 商品列表 -->
+		<view class="goods">
+			<TitleOperate :title="'共' + goodsNums + '件商品'" titleSize="30rpx"></TitleOperate>
+			<view class="list">
+				<view class="" v-for="(goods, index) in goodsList" :key="index">
+					<!-- index != goodsList.length - 1 -->
+					<orderDetailGoodsCard :data="goods" :showBorderBottom="false"></orderDetailGoodsCard>
+					<!-- {{orderInfo.status}},,,{{goods.sub_status}} -->
+					<!-- String(orderInfo.status)=='15'&& -->
+					<view class="refundOpar" v-if="String(goods.sub_status)!=='10'">
+						<text class="desc">该商品存在售后</text>
+						<u-button shape="circle" size="small" type="default" @click="afterSalesDetail(goods.order_refund_id)">前往查看</u-button>
+					</view>
+				</view>
+				
+			</view>
+		</view>
+		<!-- 订单信息 -->
+		<view class="order-info">
+			<TitleOperate title="订单信息" titleSize="30rpx"></TitleOperate>
+			<view class="inner">
+				<LineInfoOperate :orderInfo="orderInfo" :addressInfo="addressInfo"></LineInfoOperate>
+			</view>
+		</view>
+		<!-- 操作按钮 -->
+		<OrderOperate :data="{order_details:goodsList,...orderInfo}" @refresh="refreshPage"></OrderOperate>
+	</view>
+</template>
+
+<script>
+import OrderStatus from '@/pages-mall/components/order/order-status.vue';
+import AddressCard from '@/pages/mine/components/address-card.vue';
+import TitleOperate from '@/components/title-operate.vue';
+import orderDetailGoodsCard from '@/pages-mall/components/order/orderdetail-goods-card.vue';
+import LineInfoOperate from '@/pages-mall/components/line-info-operate.vue';
+import OrderOperate from '@/pages-mall/components/order/order-operate.vue';
+// 组件
+import { orderOperate } from '@/pages-mall/mixins/order-operate.js';
+export default {
+	components: {
+		OrderStatus,
+		AddressCard,
+		orderDetailGoodsCard,
+		TitleOperate,
+		LineInfoOperate,
+		OrderOperate
+	},
+	mixins: [orderOperate],
+	data() {
+		return {
+			orderId:null,
+			// 订单信息
+			orderInfo: {},
+			// 地址信息
+			addressInfo: {},
+			// 商品列表
+			goodsList: [],
+			goodsNums:1,
+			// 订单信息配置项
+			// orderInfoOps: {},
+		};
+	},
+	onLoad(ops) {
+		if(!ops.order_id){
+			uni.navigateBack();
+			return;
+		}
+		this.orderId = ops.order_id;
+		this.getOrderDetail();
+		
+	},
+	methods:{
+		refreshPage(){
+			this.getOrderDetail();
+		},
+		getOrderDetail(){
+			this.$u.api.getOrderDetailAjax(this.orderId).then(({code,data})=>{
+				if(code==1){
+					const addressBaseInfo = data.address_info;
+					this.addressInfo = {
+						...addressBaseInfo,
+						address: addressBaseInfo.province+addressBaseInfo.city+addressBaseInfo.area,
+						detail_address: addressBaseInfo.province+addressBaseInfo.city+addressBaseInfo.area+addressBaseInfo.street
+					};
+					this.goodsList = data.order_goods_info;
+					console.log('this.goodsList>>>',this.goodsList.length,this.goodsList);
+					this.getGoodsNums();
+					this.orderInfo = data.order_info;
+				}
+			}).catch()
+		},
+		getGoodsNums(){
+			let num = 0;
+			this.goodsList.forEach(e=>num+=Number(e.goods_nums));
+			this.goodsNums = num;
+		},
+	},
+};
+</script>
+
+<style lang="scss" scoped>
+.page {
+	padding-bottom: 120rpx;
+}
+.address {
+	margin-bottom: 24rpx;
+	padding: 0 30rpx;
+	background-color: $app-theme-bg-color;
+}
+.goods,
+.order-info {
+	margin-bottom: 24rpx;
+	background-color: $app-theme-bg-color;
+	.list,
+	.inner {
+		padding: 0 30rpx;
+	}
+}
+.refundOpar{
+	display: flex;
+	align-items: center;
+	justify-content: flex-end;
+	color: red;
+	.desc{
+		margin-right: 20rpx;
+	}
+}
+</style>

+ 188 - 0
pages-mall/pages/order/express.vue

@@ -0,0 +1,188 @@
+<template>
+	<view class="page">
+		<!-- 物流信息卡片 -->
+		<ExpressGoodsCard :data="expressInfo"></ExpressGoodsCard>
+		<!-- 物流节点 -->
+		<view class="express-points">
+			<u-steps style="width: 100%;" active-color="#22ac38" :list="expressDotList" mode="dot" :current="expressInfo.deliverystatus"></u-steps>
+		</view>
+		<!-- 物流详细节点 -->
+		<view class="express-detail"><ExpressList :logisticsData="expressInfo.list"></ExpressList></view>
+	</view>
+</template>
+
+<script>
+import ExpressGoodsCard from '@/pages-mall/components/order/express-goods-card.vue';
+import ExpressList from '@/pages-mall/components/express/list.vue';
+import { setAttribute, changeAttribute } from '@/pages-mall/components/express/utils.js';
+const app = getApp();
+export default {
+	components: {
+		ExpressGoodsCard,
+		ExpressList
+	},
+	data() {
+		return {
+			activeColor:this.$appTheme.appThemeColor,
+			// 订单详情
+			orderInfo: {},
+			// 物流信息
+			expressInfo:{},
+			// 物流节点
+			expressDotList: [{ name: '已发货' }, { name: '运输中' }, { name: '派件中' }, { name: '已签收' }],
+			testStrList: ['在途', '揽收', '疑难', '签收', '退签', '派件', '退回', '转单', '待清关', '清关中', '已清关', '清关异常', '收件人拒签'],
+			logisticsData: [
+				/* {
+					context:
+						'客户签收人: 已签收 感谢使用圆通速递,期待再次为您服务 如有疑问请联系:xxxxxxxxx,投诉电话:020-xxxxxxxx。疫情期间圆通每天对网点多次消毒,快递小哥每天测量体温,佩戴口罩',
+					time: '2021-02-28 10:04:59',
+					ftime: '2021-02-28 10:04:59',
+					status: '签收',
+					areaCode: null,
+					areaName: null,
+					monthDay: '02-28',
+					hourMinute: '10:04'
+				},
+				{
+					context: '【广东省广州市海珠区工业大道公司】 派件中 派件人: 吴晓贤 电话 xxxxxxxxx 。 圆通快递小哥每天已测体温,请放心收寄快递 如有疑问,请联系:020-xxxxxxxx',
+					time: '2021-02-28 08:25:15',
+					ftime: '2021-02-28 08:25:15',
+					status: '派件',
+					areaCode: 'CNxxxxxxxxxxx',
+					areaName: '广东,广州市,海珠区',
+					monthDay: '02-28',
+					hourMinute: '08:25'
+				},
+				{
+					context: '【广东省广州市海珠区工业大道】 已发出',
+					time: '2021-02-28 06:56:54',
+					ftime: '2021-02-28 06:56:54',
+					status: '在途',
+					areaCode: null,
+					areaName: null,
+					monthDay: '02-28',
+					hourMinute: '06:56'
+				},
+				{
+					context: '【广东省广州市海珠区工业大道公司】 已收入',
+					time: '2021-02-28 06:06:54',
+					ftime: '2021-02-28 06:06:54',
+					status: '在途',
+					areaCode: 'CNxxxxxxxxxxx',
+					areaName: '广东,广州市,海珠区',
+					monthDay: '02-28',
+					hourMinute: '06:06'
+				},
+				{
+					context: '【广州转运中心】 已发出 下一站 【广东省广州市海珠区工业大道公司】',
+					time: '2021-02-28 00:58:47',
+					ftime: '2021-02-28 00:58:47',
+					status: '在途',
+					areaCode: 'CNxxxxxxxxxxxxx',
+					areaName: '广东,广州市',
+					monthDay: '02-28',
+					hourMinute: '00:58'
+				},
+				{
+					context: '【广州转运中心公司】 已收入',
+					time: '2021-02-28 00:19:02',
+					ftime: '2021-02-28 00:19:02',
+					status: '在途',
+					areaCode: 'CNxxxxxxxxxxxxx',
+					areaName: '广东,广州市',
+					monthDay: '02-28',
+					hourMinute: '00:19'
+				},
+				{
+					context: '【佛山转运中心】 已发出 下一站 【广州转运中心公司】',
+					time: '2021-02-27 21:09:35',
+					ftime: '2021-02-27 21:09:35',
+					status: '在途',
+					areaCode: 'CN440600000000',
+					areaName: '广东,佛山市',
+					monthDay: '02-27',
+					hourMinute: '21:09'
+				},
+				{
+					context: '【佛山转运中心公司】 已收入',
+					time: '2021-02-27 21:06:51',
+					ftime: '2021-02-27 21:06:51',
+					status: '在途',
+					areaCode: 'CN440600000000',
+					areaName: '广东,佛山市',
+					monthDay: '02-27',
+					hourMinute: '21:06'
+				},
+				{
+					context: '【广东省佛山市禅城】 已发出 下一站 【佛山转运中心公司】',
+					time: '2021-02-27 19:42:05',
+					ftime: '2021-02-27 19:42:05',
+					status: '在途',
+					areaCode: 'CN440604000000',
+					areaName: '广东,佛山市,禅城区',
+					monthDay: '02-27',
+					hourMinute: '19:42'
+				},
+				{
+					context: '【广东省佛山市南海市场部一部公司】 已收件 取件人: 陈xx (xxxxxxxxxxx)',
+					time: '2021-02-27 19:38:46',
+					ftime: '2021-02-27 19:38:46',
+					status: '揽收',
+					areaCode: null,
+					areaName: null,
+					monthDay: '02-27',
+					hourMinute: '19:38'
+				},
+				{
+					context: '【广东省佛山市禅城公司】 已收件 取件人: 吴xx (xxxxxxxxxxx)',
+					time: '2021-02-27 14:36:42',
+					ftime: '2021-02-27 14:36:42',
+					status: '揽收',
+					areaCode: null,
+					areaName: null,
+					monthDay: '02-27',
+					hourMinute: '14:36'
+				}
+			 */
+			]
+		};
+	},
+	onLoad(opt) {
+		if(!opt.orderInfo){
+			uni.navigateBack();
+			return;
+		}
+		// this.orderInfo = uni.getStorageSync('orderInfo');
+		this.orderInfo = JSON.parse(decodeURIComponent(opt.orderInfo));
+		console.log('>>>>>123456>',this.orderInfo)
+		// this.logisticsData = changeAttribute(this.testStrList, setAttribute(this.logisticsData));
+		this.getExpressDetail();
+	},
+	methods:{
+		getExpressDetail(){
+			this.$u.api.getExpressDetailAjax(this.orderInfo.id).then(({code,data})=>{
+				console.log(data);
+				if(code==1){
+					this.expressInfo = data;
+				}
+			}).catch(()=>{
+				
+			})
+		},
+	},
+};
+</script>
+
+<style lang="scss" scoped>
+.express-points {
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	padding: 60rpx 30rpx;
+	background-color: $app-theme-bg-color;
+	margin-bottom: 24rpx;
+}
+.express-detail {
+	background-color: $app-theme-bg-color;
+}
+</style>

+ 190 - 0
pages-mall/pages/order/list.vue

@@ -0,0 +1,190 @@
+<template>
+	<view class="page">
+		<view class="tabbox">
+			<u-tabs :is-scroll="false" :list="tabOps" :current="tabIndex" @change="changeTab"></u-tabs>
+		</view>
+		<view class="content">
+			<!-- 列表 
+			:style="{height:'calc(100vh - '+topHeight+'px)'}"-->
+			<view class="list" v-if="orderList[tabIndex]&&orderList[tabIndex].length>0">
+				<OrderCard v-for="(item, index) in orderList[tabIndex]" :key="index" :data="item" @refresh="refreshData"></OrderCard>
+			</view>
+			<NoData height="60vh" type="order" v-if="orderLoad[tabIndex].finish&&orderList[tabIndex].length==0"></NoData>		
+			<!-- <u-loadmore v-else :status="orderLoad[tabIndex].finish?'nomore':orderLoad[tabIndex].loading?'loading':'loadmore'" :load-text="loadText" margin-top="60" /> -->
+			
+			<LoadMore v-else :loadtype="orderLoad[tabIndex]"></LoadMore>
+		</view>
+	</view>
+</template>
+
+<script>
+// 组件
+import NavbarTabSearch from '@/pages-mall/components/navbar-tab-search.vue';
+import OrderCard from '@/pages-mall/components/order/order-card.vue';
+
+const app = getApp();
+var _self;
+export default {
+	components: { NavbarTabSearch, OrderCard },
+	data() {
+		return {
+			topHeight:app.globalData.statusBarHeight + app.globalData.navBarHeight*2 - app.globalData.menuHeight,
+			// 当前tab
+			tabIndex: 0,			
+			loadText: {
+				loadmore: '轻轻上拉',
+				loading: ' ',
+				nomore: '没有更多了'
+			},
+			tabOps:[
+				{ name: '全部', value: '' ,type:'all'},
+				{ name: '待付款', value: '10', type:'type10' },
+				{ name: '待发货', value: '11', type:'type11' },
+				{ name: '待收货', value: '12', type:'type12' },
+				{ name: '已完成', value: '13', type:'type13' },
+			],
+			orders:[],
+			orderList: [],//常规订单
+			refundOrder:[],//售后订单
+			orderLoad:[{
+				page:1,
+				loading:true,
+				finish:false,
+			},{
+				page:1,
+				loading:true,
+				finish:false,
+			},{
+				page:1,
+				loading:true,
+				finish:false,
+			},{
+				page:1,
+				loading:true,
+				finish:false,
+			},{
+				page:1,
+				loading:true,
+				finish:false,
+			},{
+				page:1,
+				loading:true,
+				finish:false,
+			},]
+		};
+	},
+	onLoad(ops) {
+		_self = this;
+		let selIndex=0;
+		if (ops.tabIndex) {
+			selIndex = Number(ops.tabIndex);
+		}
+		this.tabIndex = selIndex;
+		this.$nextTick(() => {
+			_self.changeTab(selIndex);
+		});
+	
+	},
+	onPullDownRefresh(){
+		// uni.stopPullDownRefresh();
+		this.orderLoad[this.tabIndex].page=1;
+		this.orderLoad[this.tabIndex].finish=false;
+		this.orderList[this.tabIndex]=[];
+		this.getData();
+	},
+	onShow() {
+		console.log('onShow.......')
+		this.refreshData();
+	},
+	onReachBottom(){
+		console.log('滚动到底部')
+		this.getData();
+	},
+	methods: {
+		// 刷新页面
+		refreshData(){
+			uni.startPullDownRefresh();
+		},
+		// 更新数据
+		getData(){
+			this.getOrderList();
+		},
+		// 切换tab的回调
+		changeTab(index) {
+			this.tabIndex = index;
+			if(this.orderLoad[index].finish){
+				// 该状态下已加载全部
+				return false;
+			}
+			if(!this.orderList[index]||this.orderList[index].length==0){
+				// 无数据说明没加载过,第一次加载
+				// this.orderList[index]=[];
+				this.orderLoad[index]={
+					page:1,
+					loading:false,
+					finish:false,
+				}
+			}
+			this.getData();
+		},
+		// 正常订单
+		getOrderList(){
+			// let thisTab = this.orderList[this.tabIndex];
+			if(this.orderLoad[this.tabIndex].finish){
+				return false;
+			}
+			this.orderLoad[this.tabIndex].loading = true;
+			this.$u.api.getOrderListAjax({
+				status:this.tabOps[this.tabIndex].value,
+				page:this.orderLoad[this.tabIndex].page,
+			}).then(({code,data})=>{
+				console.log(code,data);
+				console.log(this.tabIndex);
+				this.orderLoad[this.tabIndex].loading = false;
+				uni.stopPullDownRefresh();
+				if(code==1){
+					if(data.last_page<=data.current_page){
+						this.orderLoad[this.tabIndex].finish = true;
+					}
+					if(data.current_page==1){
+						this.orderList[this.tabIndex] = data.data;
+					}else{
+						this.orderList[this.tabIndex]=this.orderList[this.tabIndex].concat(data.data);
+					}
+				}else{
+					this.orderLoad[this.tabIndex].finish = true;
+				}				
+				this.orderLoad[this.tabIndex].page = data.current_page+1;	
+				this.$forceUpdate();
+			}).catch(()=>{
+				uni.stopPullDownRefresh();
+				this.orderLoad[this.tabIndex].loading = false;
+				this.orderLoad[this.tabIndex].finish = true;
+			})
+		},
+	
+	
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.page{
+	min-height: 100vh;
+}
+.list {
+	padding: 0 30rpx 30rpx;
+	box-sizing: border-box;
+	width: 100%;
+}
+.tabbox{
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 750rpx;
+	z-index: $app-zIndex-fixed;
+}
+.content{
+	padding-top: 80rpx;
+}
+</style>

+ 185 - 0
pages-mall/pages/order/pay-result.vue

@@ -0,0 +1,185 @@
+<template>
+	<view class="page">
+		<!-- 支付状态 -->
+		<view class="status">
+			<view class="status-success-icon"><u-icon name="checkbox-mark" :color="appThemeBgColor" size="44"></u-icon></view>
+			<view class="status-name">支付成功</view>
+			<view class="money">
+				<text>¥</text>
+				<text>{{payTotal}}</text>
+			</view>
+		</view>
+
+		<!-- 付款信息 -->
+		<view class="pay-info">
+			<view class="item">
+				<view class="label">付款方式</view>
+				<view class="value">微信支付</view>
+			</view>
+		</view>
+		<view class="oparBtn" v-if="orderId">
+			<u-button type="primary" @click="viewOrderInfo">查看订单</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	var _self;
+export default {
+	data() {
+		return {
+			appThemeBgColor: this.$appTheme.appThemeBgColor,
+			payTotal:0,
+			orderId:null,
+		};
+	},
+	onLoad(opt) {
+		_self = this;
+		console.log(opt);
+		_self.payTotal = opt.payTotal||0;
+		_self.orderId = opt.orderId||null;
+		console.log(_self.orderId);
+	},
+	onBackPress(options) {
+		console.log('监听到了返回按钮点击事件');
+		this.backpage();
+		/* if (options && options.from === 'navigateBack') {
+		  uni.navigateBack({
+		  	delta: 3
+		  });
+		} else {
+		  console.log('直接返回主界面');
+		  uni.switchTab({
+			  url:'pages/mall/index'
+		  })
+		  // 这里可以编写处理直接返回主界面的相关逻辑
+		} */
+  },
+	methods: {
+		backpage() {
+			uni.navigateBack({
+				delta: 3
+			});
+		},
+		viewOrderInfo(){
+			// this.subScribeMsg();
+			uni.navigateTo({
+				url: '/pages-mall/pages/order/detail?order_id='+this.orderId
+			});
+		},
+		// 支付回调
+		subScribeMsg() {
+			uni.getSetting({
+				withSubscriptions: true,
+				success(res) {
+					console.log('1', res, '订阅信息', res.subscriptionsSetting);
+					if (!res.subscriptionsSetting.mainSwitch) {
+						uni.openSetting({ //打开设置页
+							success(res) {
+								console.log('打开设置页', res.authSetting);
+							}
+						})
+					} else {
+						uni.requestSubscribeMessage({
+							tmplIds: ['Sfa68XDGAUQveX50PcP_Ep6ngNz3luvXJNwzASjzzvQ',
+								'_VxaYVnVTj3aG_wengF8KNFDI3WRhOrFseBerx8xvXE',
+							],
+							success(res) {
+								console.log('requestSubscribeMessage 订阅信息', res);
+								if (res['Sfa68XDGAUQveX50PcP_Ep6ngNz3luvXJNwzASjzzvQ'] == "accept") { // 用户点击确定后
+									console.log('用户订阅点击确定按钮');
+									// _self.getSubMsg()
+								} else {
+									console.log('拒绝');
+								}
+							},
+							fail(errMessage) {
+								console.log("订阅消息 失败 ", errMessage);
+							},
+							// 不管成功或失败都要执行
+							complete() {
+								// if (_self.ordercode == null) return _self.getOrder();
+								// if (_self.ordercode != null) return _self.getPayOrder();
+								uni.navigateTo({
+									url: '/pages-mall/pages/order/detail?order_id='+_self.orderId
+								});
+							}
+						})
+					}
+				},
+			})
+		},
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.page {
+	background-color: $app-theme-bg-color;
+}
+.status {
+	padding: 80rpx 0 84rpx 0;
+	.status-success-icon {
+		width: 88rpx;
+		height: 88rpx;
+		margin: 0 auto;
+		margin-bottom: 16rpx;
+		background-color: $app-theme-color;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		border-radius: 50%;
+	}
+	.status-name {
+		font-size: 34rpx;
+		font-family: PingFangSC-Medium, PingFang SC;
+		font-weight: 500;
+		color: $app-theme-color;
+		margin-bottom: 40rpx;
+		text-align: center;
+	}
+	.money {
+		text-align: center;
+		text:nth-child(1) {
+			font-size: 40rpx;
+			font-family: PingFangSC-Medium, PingFang SC;
+			font-weight: 500;
+			color: $app-theme-text-color;
+		}
+		text:nth-child(2) {
+			font-size: 60rpx;
+			font-family: Helvetica;
+			color: $app-theme-text-color;
+		}
+	}
+}
+
+.pay-info {
+	margin: 0 24rpx;
+	padding-bottom: 24rpx;
+	border-bottom: 1px solid $app-theme-border-color;
+
+	.item {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		margin-bottom: 24rpx;
+	}
+	.label {
+		font-size: 30rpx;
+		font-family: PingFangSC-Regular, PingFang SC;
+		font-weight: 400;
+		color: $app-theme-text-gray-color;
+	}
+	.value {
+		font-size: 30rpx;
+		font-family: PingFangSC-Regular, PingFang SC;
+		font-weight: 400;
+		color: $app-theme-text-color;
+	}
+}
+
+.oparBtn{
+	padding:100rpx 100rpx 0;
+}
+</style>

+ 210 - 0
pages-mall/pages/order/pay.vue

@@ -0,0 +1,210 @@
+<template>
+	<view class="page">
+		<!-- 金额展示 -->
+		<view class="money-show">
+			<view class="desc">实付金额</view>
+			<view class="money">
+				<text>¥</text>
+				<text>{{orderInfo.actual_total_amount}}</text>
+			</view>
+			<!-- <view class="time">支付剩余时间 07:58</view> -->
+		</view>
+
+		<!-- 支付方式 -->
+		<view class="pay-type">
+			<u-radio-group v-model="payType">
+				<view class="title">选择支付方式</view>
+				<view class="item" @click="payType = 'wxPay'">
+					<view class="left">
+						<view class="logo"><image src="../../static/icon-wx-pay.png" mode=""></image></view>
+						<view class="name">微信支付</view>
+					</view>
+
+					<view class="check"><u-radio shape="circle" :active-color="appThemeColor" icon-size="16" name="wxPay"></u-radio></view>
+				</view>
+				<!-- <view class="item" @click="payType = 'aliPay'">
+					<view class="left">
+						<view class="logo"><image src="../../static/icon-ali-pay.png" mode=""></image></view>
+						<view class="name">支付宝</view>
+					</view>
+					<view class="check"><u-radio shape="circle" :active-color="appThemeColor" icon-size="16" name="aliPay"></u-radio></view>
+				</view> -->
+			</u-radio-group>
+		</view>
+
+		<!-- 支付 -->
+		<view class="btn">
+			<u-button type="primary" shape="circle" @click="goPayResult">
+				<!-- <text v-show="payType == 'wxPay'">微信支付</text> -->
+				<!-- <text v-show="payType == 'aliPay'">支付宝支付</text> -->
+				<text>立即支付</text>
+				<text>¥{{orderInfo.actual_total_amount}}</text>
+			</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	var _self;
+export default {
+	data() {
+		return {
+			payType: 'wxPay',
+			appThemeColor: this.$appTheme.appThemeColor,
+			orderInfo:{},
+		};
+	},
+	onLoad(opt){
+		_self = this;
+		if(!opt.orderInfo){
+			uni.navigateBack();
+		}else{
+			this.orderInfo = JSON.parse(decodeURIComponent(opt.orderInfo));
+			console.log('>>>>>123456>',this.orderInfo)
+		}
+	},
+	methods: {
+		goPayResult() {
+			console.log('>>>>>>>>>',this.orderInfo);
+			/* 	setTimeout(() => {
+			uni.navigateTo({
+					url: '/pages-mall/pages/order/pay-result?payTotal='+_self.orderInfo.actual_total_amount+'&orderId='+_self.orderInfo.id
+				})
+			}, 500)
+			return; */
+			this.$u.api.payAjax(this.orderInfo.order_no).then(({code,data})=>{
+				if(code==1){
+					var orderInfos = data;
+					uni.requestPayment({
+						"provider": "wxpay",
+						"orderInfo":orderInfos,
+						...data,
+						success: function(res) {
+							uni.showToast({
+								title: "支付成功",
+								icon: 'success',
+								success: () => {
+									setTimeout(() => {
+										uni.redirectTo({
+											url: '/pages-mall/pages/order/pay-result?payTotal='+_self.orderInfo.actual_total_amount+'&orderId='+_self.orderInfo.id
+										})
+									}, 500)
+								}
+							})
+						},
+						fail: function(err) {
+							uni.showModal({
+								content:'支付失败,请到我的订单中,重新支付',
+								success() {
+									uni.redirectTo({
+										url:'/pages-mall/pages/order/detail'
+									})
+								}
+							})
+							console.log('fail:' + JSON.stringify(err));
+						}
+					});
+				}
+			})
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.page {
+	background-color: $app-theme-bg-color;
+}
+.money-show {
+	padding: 24rpx 0 48rpx 0;
+	.desc {
+		width: 100%;
+		text-align: center;
+		font-size: 24rpx;
+		font-family: PingFang-SC-Medium, PingFang-SC;
+		font-weight: 500;
+		color: #2d2b36;
+		margin-bottom: 8rpx;
+	}
+	.money {
+		margin-bottom: 22rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		text:nth-child(1) {
+			font-size: 48rpx;
+			font-family: PingFang-SC-Medium, PingFang-SC;
+			font-weight: 500;
+			color: #2d2b36;
+		}
+		text:nth-child(2) {
+			font-size: 72rpx;
+			font-family: DINAlternate-Bold, DINAlternate;
+			font-weight: bold;
+			color: #2d2b36;
+		}
+	}
+	.time {
+		text-align: center;
+		font-size: 24rpx;
+		font-family: PingFang-SC-Medium, PingFang-SC;
+		font-weight: 500;
+		color: #2d2b36;
+	}
+}
+
+.pay-type {
+	padding: 0 30rpx;
+	margin-top: 80rpx;
+	.title {
+		font-size: 30rpx;
+		font-family: PingFangSCSemibold-, PingFangSCSemibold;
+		font-weight: normal;
+		color: #2d2b36;
+		padding-bottom: 4rpx;
+	}
+	.item {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding-bottom: 24rpx;
+		padding-top: 26rpx;
+		border-bottom: 1rpx solid #efefef;
+		.left {
+			display: flex;
+			align-items: center;
+			justify-content: flex-start;
+			.logo {
+				width: 64rpx;
+				height: 64rpx;
+				margin-right: 30rpx;
+				image {
+					width: 100%;
+					height: 100%;
+				}
+			}
+			.name {
+				font-size: 28rpx;
+				font-family: PingFangSC-Regular, PingFang SC;
+				font-weight: 400;
+				color: #171717;
+			}
+		}
+
+		.check {
+		}
+	}
+}
+
+/deep/.u-icon {
+	display: flex !important;
+}
+
+.btn {
+	padding: 0 30rpx;
+	position: absolute;
+	bottom: 70rpx;
+	left: 0;
+	width: 100%;
+}
+</style>

+ 305 - 0
pages-mall/pages/order/submit.vue

@@ -0,0 +1,305 @@
+<template>
+	<view class="page">
+		<!-- 收货地址 -->
+		<view class="address">
+			<AddressCard :data="addressInfo" v-if="addressInfo.id"></AddressCard>
+			<view class="addbtn" v-else @click="selAddress()">
+				+选择地址
+			</view>
+		</view>
+		<view class="conbg">
+			<!-- 商品列表 -->
+			<view class="goods">
+				<SubmitGoodsCard v-for="(goods, index) in goodsList" :key="index" :data="goods" :showBorderBottom="index != goodsList.length - 1"></SubmitGoodsCard>
+			</view>
+			<!-- 优惠信息 -->
+			<SubmitDiscounts :data="priceInfo" :couponList="canUseCouponList" :usingCoupon="usingCoupon" @selCoupon="selCouponFun"></SubmitDiscounts>			
+			<u-field v-model="remark" label="留言" placeholder="请输入您的留言"></u-field>
+		</view>	
+		<!-- 提交订单 -->
+		<!-- {{priceInfo}} -->
+		<selCouponPopup :priceInfo="priceInfo" ref="selCouponPopupRef" @done="confirmSelCoupon"></selCouponPopup>		
+		
+		<!-- 底部按钮 -->
+		<SubmitOperate :data="priceInfo" @submitOrder="submitOrder"></SubmitOperate>
+	</view>
+</template>
+
+<script>
+import SubmitDiscounts from '@/pages-mall/components/order/submit-discounts.vue';
+import SubmitGoodsCard from '@/pages-mall/components/order/submit-goods-card.vue';
+import SubmitOperate from '@/pages-mall/components/order/submit-operate.vue';
+import AddressCard from '@/pages/mine/components/address-card.vue';
+import selCouponPopup from '@/pages-mall/components/order/sel-coupon-popup.vue';
+
+var _self;
+export default {
+	components: {
+		AddressCard,
+		SubmitDiscounts,
+		SubmitGoodsCard,
+		SubmitOperate,
+		selCouponPopup
+	},
+	data() {
+		return {
+			orderType:'single',// cart,购物车订单 single 详情页直接下的订单
+			cartIds:'',//有的话代表是购物车订单,无 常规订单
+			expresstemplate:{},
+			priceInfo: {
+				countPrice:0,//商品总价
+				discount:0,//优惠券
+				freight:0,//运费
+			},
+			usingCoupon:{},//使用中的优惠券信息
+			// canUseCoupon:[], //可用优惠券
+			// notUseCoupon:[],//不可用优惠券
+			thisTimeNum:Date.now(),
+			remark:'',
+			addressInfo: {},
+			goodsList: [],
+		};
+	},
+	async onLoad(opt) {
+		_self = this;
+		if(!opt.orderInfo){
+			uni.navigateBack();
+			return;
+		}
+		if(opt.cartIds){
+			this.cartIds = opt.cartIds;
+		}
+		let orders = JSON.parse(decodeURIComponent(opt.orderInfo));
+		this.goodsList = [].concat(orders);
+		
+		// 获取默认地址
+		await this.getDefaultAddress();
+		console.log('默认地址设置成功')
+		console.log('执行2222222')
+		// 是否有运费模板 express_type 运费类型
+		if(!this.baseConfig.express_type){
+			await this.getExpressInfo();
+		}
+		console.log('已经有运费模板了哦')
+		this.expressPipei();
+		
+		this.comoutedPrice();
+		this.getUserCouponList();
+	},
+	onShow() {
+		uni.$once('addressInfo',function(data){
+			_self.addressInfo = data;
+			_self.expressPipei();
+			// TODO 下面执行刷新的方法
+		})
+	},
+	computed: {
+		couponList() {
+			return this.$store.state.user.couponList;
+		},
+		canUseCouponList(){
+			// 是否可用优惠券
+			// this.priceInfo.countPrice status==2 未使用
+			let coupon = this.couponList.filter(item => item.status==2&&(item.min_amount<=this.priceInfo.countPrice));
+			return coupon;
+		},
+		// 运费模板
+		baseConfig() {
+			return this.$store.state.pub.baseConfig;
+		},
+		// 商品数量
+		goodsListCountNums(){
+			let nums = 0;
+			this.goodsList.forEach(e=>nums+=e.nums);
+			return nums;
+		},
+		
+	},
+	methods:{
+		// 获取运费模板信息
+		async getExpressInfo(){
+			console.log('来获取运费模板信息了哦')
+			await this.$u.api.getBaseInfoAjax().then(({data})=>{
+				this.$store.commit('pub/commitBaseConfig',data);
+				console.log('运费模板信息获取成功')
+			}).catch(()=>{
+				
+			})
+		},
+		// 邮费匹配
+		expressPipei(){
+			// extra_express_fee
+			if(!_self.addressInfo.id){
+				// 没有地址
+				console.log('没有地址')
+				return;
+			}
+			if(!this.baseConfig.express_type){
+				// 没有邮费模板
+				console.log('没有邮费模板')
+				return;
+			}
+			const base_express_fee = Number(this.baseConfig.base_express_fee);
+			const extra_express_fee = Number(this.baseConfig.extra_express_fee);
+			if(this.baseConfig.express_type=='2'){
+				// 不包邮
+				console.log('不包邮>>>>本书',_self.goodsListCountNums);
+				this.priceInfo.freight = base_express_fee+extra_express_fee*(_self.goodsListCountNums-1);
+				console.log(this.priceInfo.freight);
+				return;
+			}
+			if(this.baseConfig.express_type=='3'){
+				// 全场满x包邮
+				console.log('全场满x包邮')
+				if(this.priceInfo.countPrice>=this.baseConfig.min_buy_amount){
+					// 价格满了,包邮
+					this.priceInfo.freight = 0;
+				}else{
+					this.priceInfo.freight = base_express_fee+extra_express_fee*(_self.goodsListCountNums-1);
+				}
+				return;
+			}
+			if(this.baseConfig.express_type=='4'){
+				// 全场满x包邮
+				console.log('全场满x包邮')
+				const codes = this.baseConfig.codes.split(',');
+				const ucodes = this.addressInfo.addressCode.split(',')[0];
+				console.log(codes,ucodes);
+				if(codes.includes(ucodes)){
+					// 片段是否偏远地区
+					this.priceInfo.freight = base_express_fee+extra_express_fee*(_self.goodsListCountNums-1);
+				}else if(this.priceInfo.countPrice>=this.baseConfig.min_buy_amount){
+					this.priceInfo.freight = 0;
+				}else{
+					this.priceInfo.freight = base_express_fee+extra_express_fee*(_self.goodsListCountNums-1);
+				}
+				return;
+			}
+		},
+		// 选择地址
+		selAddress(){
+			uni.navigateTo({
+				url: '/pages-mine/pages/address/list?isSelect=true&isBack=true'
+			});
+		},
+		async getDefaultAddress(){
+			console.log('执行11111111111')
+			await this.$u.api.getDefaultAddressAjax().then(({data})=>{
+				if(!data) return
+				this.addressInfo = {
+					...data,
+					address:data.province+data.city+data.area,
+					detail_address:data.province+data.city+data.area+data.street,
+				};
+				
+			})
+		},
+		// 计算总价
+		comoutedPrice(){
+			let price = 0;
+			this.goodsList.map(item => {
+				price += Number(item.selectdSku.price_selling) * item.nums
+			});			
+			this.priceInfo.countPrice = parseFloat(price).toFixed(2);
+		},
+		
+		// 选择优惠券
+		selCouponFun(){
+			this.$refs.selCouponPopupRef.showPopup({couponList:this.couponList,usingCoupon:this.usingCoupon})
+		},
+		// 确认选择
+		confirmSelCoupon(e){
+			console.log('使用中,,',e);
+			this.usingCoupon = e;
+			this.priceInfo.discount = e.amount||0;
+			// this.$forceUpdate();
+		},
+		// 提交订单
+		submitOrder(){
+			if(!this.addressInfo?.id){
+				this.$u.toast('请选择收货地址');
+				return false;
+			}
+			if(this.cartIds){
+				// 购物车订单
+				this.submitCartGoods();
+				return;
+			}
+			// 常规订单
+			this.submitGoods();
+		},
+		// 提交购物车商品
+		submitCartGoods(){
+			let json = {
+				address_id:this.addressInfo.id,
+				cart_ids:this.cartIds,
+				remark: this.remark,
+				user_coupon_id:this.usingCoupon.id,
+			}
+			this.$u.api.submitCartOrderAjax(json).then(({code,data})=>{
+				if(code==1){
+					console.log(data);
+					uni.redirectTo({
+						url: '/pages-mall/pages/order/pay?orderInfo='+encodeURIComponent(JSON.stringify(data))
+					});
+				}
+			})
+		},
+		// 直接购买单个商品
+		submitGoods(){
+			const info = this.goodsList[0];
+			let json = {
+				goods_id: info.id,
+				nums: info.nums,
+				address_id: this.addressInfo.id,
+				pay_type: 'wx_miniapp',
+				sku_id: info.selectdSku.id,
+				remark: this.remark,
+				user_coupon_id:this.usingCoupon.id,
+			};
+			this.$u.api.submitOrderAjax(json).then(({code,data})=>{
+				if(code==1){
+					console.log(data);
+					uni.redirectTo({
+						url: '/pages-mall/pages/order/pay?orderInfo='+encodeURIComponent(JSON.stringify(data))
+					});
+				}
+			})
+		},
+		getUserCouponList(){
+			if(this.loading)return false;
+			this.$u.api.getUserCouponListAjax(1).then(({code,data})=>{
+				// this.discountsList = data.data;
+				this.$store.commit('user/commitCouponList',data.data);
+			}).catch(()=>{
+				// this.$store.commit('user/commitCouponList');
+			})
+		},
+		
+	},
+};
+</script>
+<style lang="scss" scoped>
+.page {
+	padding-top: 1rpx;
+	padding-bottom: 140rpx;
+}
+.address{
+	margin: 20rpx 0;
+	padding: 0 30rpx;
+	background-color: $app-theme-bg-color;
+}
+.goods {
+	// background-color: $app-theme-bg-color;
+	padding: 0 30rpx;
+}
+.conbg{
+	background-color: #fff;
+}
+.addbtn{
+	padding: 25rpx;
+	font-size: 36rpx;
+	color: $app-theme-color;
+	background-color: #fff;
+}
+</style>

+ 104 - 0
pages-mall/pages/zone/zone.vue

@@ -0,0 +1,104 @@
+<!-- 专区页面 -->
+<template>
+	<view class="zone">
+		<view class="listbox">
+			<Goods v-for="(item, index) in list" :key="index" :data="item"></Goods>
+		</view>
+		<NoData v-if="finish&&list.length<1"></NoData>
+		<LoadMore v-else :loadtype="{page, loading, finish}"></LoadMore>
+	</view>
+</template>
+
+<script>
+import Goods from '@/pages/mall/components/goods.vue';
+export default {
+	name: 'zone',
+	components: {
+		Goods
+	},
+	data(){
+		return {
+			type:'default',
+			page:1,
+			loading:false,
+			finish:false,
+			pullDown:false,
+			list: [],
+		}
+	},
+	onLoad(opt) {
+		if(opt){
+			this.type = opt.type;
+		}
+		uni.setNavigationBarTitle({
+			title:this.type=='hot'?'热门好物':this.type=='recommend'?'精选推荐':'好物推荐'
+		});
+		this.getList();
+	},
+	onReachBottom() {
+		this.getList();
+	},
+	onPullDownRefresh(){
+		this.pullDown = true;
+		this.reData();
+	},
+	methods: {
+		// 刷新数据
+		reData(){
+			this.page=1;
+			this.finish=false;
+			this.list=[];
+			this.getList();
+		},
+		getList(){
+			if(this.finish)return false;
+			if(this.loading)return false;
+			this.$u.api.getGoodsListAjax({
+				page:this.page,
+				tags_type:this.type,
+			}).then(({code,data})=>{
+				console.log(code,data)
+				if(this.pullDown){
+					uni.stopPullDownRefresh();
+					this.pullDown = false;
+				}
+				this.loading = false;
+				let returnData = data.data;
+				if(code==1){
+					if(data.last_page<=data.current_page){
+						this.finish = true;
+					}
+					returnData.forEach(e => {
+						e.checked = false;
+					})
+					if(data.current_page==1){
+						this.list = returnData;
+					}else{
+						this.list=this.list.concat(returnData);
+					}
+				}else{
+					this.finish = true;
+				}
+				this.page++;
+			}).catch(()=>{
+				this.loading = false;
+				this.finish = true;
+				if(this.pullDown){
+					uni.stopPullDownRefresh();
+					this.pullDown = false;
+				}
+			})
+		},
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+	.zone{
+		// background-color: #fff;
+	}
+	.listbox{
+		margin: 20rpx 20rpx 0;
+		background-color: #fff;
+	}
+</style>

BIN
pages-mall/static/express-dot-active.png


BIN
pages-mall/static/express-dot.png


BIN
pages-mall/static/express-ing.png


BIN
pages-mall/static/express-over.png


BIN
pages-mall/static/icon-ali-pay.png


BIN
pages-mall/static/icon-wx-pay.png


BIN
pages-mall/static/select-item-1.png


BIN
pages-mall/static/select-item-2.png


BIN
pages-mall/static/select-item-3.png


+ 241 - 0
pages-mine/components/city-picker - 副本.vue

@@ -0,0 +1,241 @@
+<template>
+	<u-popup
+		v-model="value"
+		mode="bottom"
+		:popup="false"
+		:mask="true"
+		:closeable="true"
+		:safe-area-inset-bottom="true"
+		close-icon-color="#ffffff"
+		:z-index="uZIndex"
+		:maskCloseAble="maskCloseAble"
+		@close="close"
+	>
+		<u-tabs v-if="value" :list="genTabsList" :is-scroll="true" :current="tabsIndex" @change="tabsChange" ref="tabs"></u-tabs>
+		<view class="area-box">
+			<view class="u-flex" :class="{ change: isChange }">
+				<view class="area-item">
+					<view class="u-padding-10 u-bg-gray" style="height: 100%;">
+						<scroll-view :scroll-y="true" style="height: 100%">
+							<u-cell-group>
+								<u-cell-item v-for="(item, index) in provinces" :title="item.label" :arrow="false" :index="index" :key="index" @click="provinceChange">
+									<u-icon v-if="isChooseP && province === index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
+								</u-cell-item>
+							</u-cell-group>
+						</scroll-view>
+					</view>
+				</view>
+				<view class="area-item">
+					<view class="u-padding-10 u-bg-gray" style="height: 100%;">
+						<scroll-view :scroll-y="true" style="height: 100%">
+							<u-cell-group v-if="isChooseP">
+								<u-cell-item v-for="(item, index) in citys" :title="item.label" :arrow="false" :index="index" :key="index" @click="cityChange">
+									<u-icon v-if="isChooseC && city === index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
+								</u-cell-item>
+							</u-cell-group>
+						</scroll-view>
+					</view>
+				</view>
+
+				<view class="area-item">
+					<view class="u-padding-10 u-bg-gray" style="height: 100%;">
+						<scroll-view :scroll-y="true" style="height: 100%">
+							<u-cell-group v-if="isChooseC">
+								<u-cell-item v-for="(item, index) in areas" :title="item.label" :arrow="false" :index="index" :key="index" @click="areaChange">
+									<u-icon v-if="isChooseA && area === index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
+								</u-cell-item>
+							</u-cell-group>
+						</scroll-view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import provinces from 'uview-ui/libs/util/province.js';
+import citys from 'uview-ui/libs/util/city.js';
+import areas from 'uview-ui/libs/util/area.js';
+export default {
+	name: 'city-picker',
+	props: {
+		// 通过双向绑定控制组件的弹出与收起
+		value: {
+			type: Boolean,
+			default: false
+		},
+		// 默认显示的地区,可传类似["河北省", "秦皇岛市", "北戴河区"]
+		defaultRegion: {
+			type: Array,
+			default() {
+				return [];
+			}
+		},
+		// 默认显示地区的编码,defaultRegion和areaCode同时存在,areaCode优先,可传类似["13", "1303", "130304"]
+		areaCode: {
+			type: Array,
+			default() {
+				return [];
+			}
+		},
+		// 是否允许通过点击遮罩关闭Picker
+		maskCloseAble: {
+			type: Boolean,
+			default: true
+		},
+		// 弹出的z-index值
+		zIndex: {
+			type: [String, Number],
+			default: 0
+		}
+	},
+	data() {
+		return {
+			regionList:[],
+			cityValue: '',
+			isChooseP: false, //是否已经选择了省
+			province: 0, //省级下标
+			provinces: provinces,
+			isChooseC: false, //是否已经选择了市
+			city: 0, //市级下标
+			citys: citys[0],
+			isChooseA: false, //是否已经选择了区
+			area: 0, //区级下标
+			areas: areas[0][0],
+			tabsIndex: 0
+		};
+	},
+	mounted() {
+		this.getRegionList();
+		this.init();
+	},
+	computed: {
+		isChange() {
+			return this.tabsIndex > 1;
+		},
+		genTabsList() {
+			let tabsList = [
+				{
+					name: '请选择'
+				}
+			];
+			if (this.isChooseP) {
+				tabsList[0]['name'] = this.provinces[this.province]['label'];
+				tabsList[1] = {
+					name: '请选择'
+				};
+			}
+			if (this.isChooseC) {
+				tabsList[1]['name'] = this.citys[this.city]['label'];
+				tabsList[2] = {
+					name: '请选择'
+				};
+			}
+			if (this.isChooseA) {
+				tabsList[2]['name'] = this.areas[this.area]['label'];
+			}
+			return tabsList;
+		},
+		uZIndex() {
+			// 如果用户有传递z-index值,优先使用
+			return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
+		}
+	},
+	methods: {		
+		getRegionList(){
+			this.$u.api.getRegionListAjax().then(({code,data})=>{
+				if(code==1){
+					this.regionList = true;
+				}
+			})
+		},
+		init() {
+			if (this.areaCode.length == 3) {
+				this.setProvince('', this.areaCode[0]);
+				this.setCity('', this.areaCode[1]);
+				this.setArea('', this.areaCode[2]);
+			} else if (this.defaultRegion.length == 3) {
+				this.setProvince(this.defaultRegion[0], '');
+				this.setCity(this.defaultRegion[1], '');
+				this.setArea(this.defaultRegion[2], '');
+			}
+		},
+		setProvince(label = '', value = '') {
+			this.provinces.map((v, k) => {
+				if (value ? v.value == value : v.label == label) {
+					this.provinceChange(k);
+				}
+			});
+		},
+		setCity(label = '', value = '') {
+			this.citys.map((v, k) => {
+				if (value ? v.value == value : v.label == label) {
+					this.cityChange(k);
+				}
+			});
+		},
+		setArea(label = '', value = '') {
+			this.areas.map((v, k) => {
+				if (value ? v.value == value : v.label == label) {
+					this.isChooseA = true;
+					this.area = k;
+				}
+			});
+		},
+		close() {
+			this.$emit('input', false);
+		},
+		tabsChange(index) {
+			this.tabsIndex = index;
+		},
+		provinceChange(index) {
+			this.isChooseP = true;
+			this.isChooseC = false;
+			this.isChooseA = false;
+			this.province = index;
+			this.citys = citys[index];
+			this.tabsIndex = 1;
+		},
+		cityChange(index) {
+			this.isChooseC = true;
+			this.isChooseA = false;
+			this.city = index;
+			this.areas = areas[this.province][index];
+			this.tabsIndex = 2;
+		},
+		areaChange(index) {
+			this.isChooseA = true;
+			this.area = index;
+			let result = {};
+			result.province = this.provinces[this.province];
+			result.city = this.citys[this.city];
+			result.area = this.areas[this.area];
+			this.$emit('city-change', result);
+			this.close();
+		},
+	}
+};
+</script>
+<style lang="scss" scoped>
+.area-box {
+	width: 100%;
+	overflow: hidden;
+	height: 800rpx;
+
+	> view {
+		width: 150%;
+		transition: transform 0.3s ease-in-out 0s;
+		transform: translateX(0);
+
+		&.change {
+			transform: translateX(-33.3333333%);
+		}
+	}
+
+	.area-item {
+		width: 33.3333333%;
+		height: 800rpx;
+	}
+}
+</style>

+ 271 - 0
pages-mine/components/city-picker.vue

@@ -0,0 +1,271 @@
+<template>
+	<u-popup
+		v-model="value"
+		mode="bottom"
+		:popup="false"
+		:mask="true"
+		:closeable="true"
+		:safe-area-inset-bottom="true"
+		close-icon-color="#ffffff"
+		:z-index="uZIndex"
+		:maskCloseAble="maskCloseAble"
+		@close="close"
+	>
+		<u-tabs v-if="value" :list="genTabsList" :is-scroll="true" :current="tabsIndex" @change="tabsChange" ref="tabs"></u-tabs>
+		<view class="area-box">
+			<view class="u-flex" :class="{ change: isChange }">
+				<view class="area-item">
+					<view class="u-padding-10 u-bg-gray" style="height: 100%;">
+						<scroll-view :scroll-y="true" style="height: 100%">
+							<u-cell-group>
+								<u-cell-item v-for="(item, index) in addressList" :title="item.name" :arrow="false" :index="index" :key="index" @click="provinceChange">
+									<u-icon v-show="isChooseP && province == index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
+								</u-cell-item>
+							</u-cell-group>
+						</scroll-view>
+					</view>
+				</view>
+				<view class="area-item">
+					<view class="u-padding-10 u-bg-gray" style="height: 100%;">
+						<scroll-view :scroll-y="true" style="height: 100%">
+							<u-cell-group v-if="isChooseP">
+								<u-cell-item v-for="(item, index) in addressList[province].children" :title="item.name" :arrow="false" :index="index" :key="index" @click="cityChange">
+									<u-icon v-show="isChooseC && city == index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
+								</u-cell-item>
+							</u-cell-group>
+						</scroll-view>
+					</view>
+				</view>
+
+				<view class="area-item">
+					<view class="u-padding-10 u-bg-gray" style="height: 100%;">
+						<scroll-view :scroll-y="true" style="height: 100%">
+							<u-cell-group v-if="isChooseC">
+								<u-cell-item v-for="(item, index) in addressList[province].children[city].children" :title="item.name" :arrow="false" :index="index" :key="index" @click="areaChange">
+									<u-icon v-show="isChooseA && area == index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
+								</u-cell-item>
+							</u-cell-group>
+						</scroll-view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+
+export default {
+	name: 'city-picker',
+	props: {
+		// 通过双向绑定控制组件的弹出与收起
+		value: {
+			type: Boolean,
+			default: false
+		},
+		// 默认显示的地区,可传类似["河北省", "秦皇岛市", "北戴河区"]
+		defaultRegion: {
+			type: Array,
+			default() {
+				return [];
+			}
+		},
+		// 默认显示地区的编码,defaultRegion和areaCode同时存在,areaCode优先,可传类似["13", "1303", "130304"]
+		areaCode: {
+			type: Array,
+			default() {
+				return [];
+			}
+		},
+		// 是否允许通过点击遮罩关闭Picker
+		maskCloseAble: {
+			type: Boolean,
+			default: true
+		},
+		// 弹出的z-index值
+		zIndex: {
+			type: [String, Number],
+			default: 0
+		}
+	},
+	data() {
+		return {
+			// addressList:[],
+			
+			
+			cityValue: '',
+			isChooseP: false, //是否已经选择了省
+			province: 0, //省级下标
+			provinces: [],
+			isChooseC: false, //是否已经选择了市
+			city: 0, //市级下标
+			citys: [],
+			isChooseA: false, //是否已经选择了区
+			area: 0, //区级下标
+			areas: [],
+			tabsIndex: 0
+		};
+	},
+	mounted() {
+		if(this.addressList.length<1){
+			this.getRegionList();
+		}else{
+			this.init();
+			
+		}
+		
+	},
+	computed: {
+		isChange() {
+			return this.tabsIndex > 1;
+		},
+		genTabsList() {
+			let tabsList = [
+				{
+					name: '请选择'
+				}
+			];
+			if (this.isChooseP) {
+				tabsList[0].name = this.addressList[this.province].name;
+				tabsList[1] = {
+					name: '请选择'
+				};
+			}
+			if (this.isChooseC) {
+				tabsList[1].name = this.addressList[this.province].children[this.city].name;
+				tabsList[2] = {
+					name: '请选择'
+				};
+			}
+			if (this.isChooseA) {
+				tabsList[2].name = this.addressList[this.province].children[this.city].children[this.area].name;
+			}
+			return tabsList;
+		},
+		uZIndex() {
+			// 如果用户有传递z-index值,优先使用
+			return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
+		},
+		// 地址列表
+		addressList() {
+			return this.$store.state.pub.addressList;
+		},
+	},
+	methods: {		
+		getRegionList(){
+			this.$u.api.getRegionListAjax().then(({code,data})=>{
+				if(code==1){
+					this.$store.commit('pub/commitAddressList',data)
+					this.init();
+				}
+			})
+		},
+		init() {
+			// 这里只会传code形式的,不传文本
+			if(this.areaCode[0]){
+				this.setProvince();
+			}
+		},
+		setProvince() {
+			// 查询省 索引
+			const list = this.addressList;
+			list.map((v, k) => {
+				if (v.code == this.areaCode[0]) {
+					this.provinceChange(k);
+					if(this.areaCode[1]){
+						// 查询城市索引
+						this.setCity(k);
+					}
+					
+				}
+			});
+		},
+		setCity(pi,ci,ai) {
+			// pi,ci,ai 省市区 索引
+			const list = this.addressList[pi].children;
+			list.map((v, k) => {
+				if (v.code == this.areaCode[1]) {
+					this.cityChange(k);
+					if(this.areaCode[2]){
+						// 查询地区索引
+						this.setArea(pi,k);
+					}
+				}
+			});
+		},
+		setArea(pi,ci,ai) {
+			const list = this.addressList[pi].children[ci].children;
+			list.map((v, k) => {
+				if (v.code == this.areaCode[2]) {
+					this.isChooseA = true;
+					this.area = k;
+				}
+			});
+		},
+		close() {
+			this.$emit('input', false);
+		},
+		tabsChange(index) {
+			this.tabsIndex = index;
+		},
+		provinceChange(index) {
+			this.isChooseP = true;
+			this.isChooseC = false;
+			this.isChooseA = false;
+			this.province = index;
+			this.citys = this.addressList[index].children;
+			this.tabsIndex = 1;
+		},
+		cityChange(index) {
+			this.isChooseC = true;
+			this.isChooseA = false;
+			this.city = index;
+			if(this.citys[index].children[0]){
+				this.areas = this.citys[index].children;
+				this.tabsIndex = 2;
+				return false;
+			}
+			let result = {};
+			
+			result.province = this.addressList[this.province];
+			result.city = this.addressList[this.province].children[this.city];
+			this.$emit('city-change', result);
+			this.close();
+		},
+		areaChange(index) {
+			this.isChooseA = true;
+			this.area = index;
+			let result = {};
+			
+			result.province = this.addressList[this.province];
+			result.city = this.addressList[this.province].children[this.city];
+			result.area = this.addressList[this.province].children[this.city].children[this.area];
+			
+			this.$emit('city-change', result);
+			this.close();
+		},
+	}
+};
+</script>
+<style lang="scss" scoped>
+.area-box {
+	width: 100%;
+	overflow: hidden;
+	height: 800rpx;
+
+	> view {
+		width: 150%;
+		transition: transform 0.3s ease-in-out 0s;
+		transform: translateX(0);
+
+		&.change {
+			transform: translateX(-33.3333333%);
+		}
+	}
+
+	.area-item {
+		width: 33.3333333%;
+		height: 800rpx;
+	}
+}
+</style>

+ 127 - 0
pages-mine/components/discounts-item.vue

@@ -0,0 +1,127 @@
+<template>
+	<view class="coupon" >
+		<view class="chuo" v-if="data.status==1">
+			已使用
+		</view>
+		<view class="chuo" v-else-if="data.status==3">
+			已过期
+		</view>
+		<view class="left">
+			<view class="discount">
+				<text>¥</text>
+				<text>{{ data.amount }}</text>
+			</view>
+			<view class="standard">满{{ data.min_amount }}可用</view>
+		</view>
+		<view class="right">
+			<view class="title">{{ data.title }}</view>
+			<view class="date">有效期至 {{ formatDateF(data.limit_time*1000) }}</view>
+		</view>
+		<view class="select" v-if="select"></view>
+	</view>
+</view>
+</template>
+
+<script>
+import { formatDate } from '@/utils/tools.js'
+const app = getApp();
+export default {
+	props: {
+		select:{
+			type:Boolean,
+			default:false
+		},
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		},
+	},
+	data() {
+		return {
+			
+		};
+	},
+	methods: {
+		formatDateF(v){
+			return formatDate(v);
+		},
+	},
+	onLoad() {
+		this.getUserCouponList();
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.coupon {
+	width: 100%;
+	height: 174rpx;
+	background-image: url('@/pages-mine/static/discounts-bg.png');
+	background-size: cover;
+	margin-bottom: 30rpx;
+	display: flex;
+	justify-content: flex-start;
+	align-items: flex-end;
+	padding-bottom: 34rpx;
+	position: relative;
+
+	.left {
+		margin-right: 60rpx;
+		margin-left: 50rpx;
+		.discount {
+			text:nth-child(1) {
+				font-size: 22rpx;
+				color: $app-theme-text-money-color;
+			}
+			text:nth-child(2) {
+				font-size: 64rpx;
+				color: $app-theme-text-money-color;
+			}
+		}
+		.standard {
+			font-size: 20rpx;
+			color: $app-theme-card-gray-color;
+		}
+	}
+	.right {
+		.title {
+			font-size: 32rpx;
+			color: $app-theme-text-black-color;
+			margin-bottom: 32rpx;
+		}
+		.date {
+			font-size: 20rpx;
+			color: $app-theme-card-gray-color;
+		}
+	}
+
+	.select {
+		height: 60rpx;
+		width: 60rpx;
+		background-image: url('@/pages-mine/static/discounts-select.png');
+		background-size: cover;
+		position: absolute;
+		top: 4rpx;
+		right: 4rpx;
+		z-index: $app-zIndex-absolute;
+	}
+}
+
+.chuo{
+	position: absolute;
+	width: 90rpx;
+	height: 90rpx;
+	border-radius: 50%;
+	color: #fff;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	background-color: rgba(0, 0, 0, .5);
+	top: 40rpx;
+	right: 40rpx;
+	transform: rotate(30deg);
+	font-size: 24rpx;
+}
+</style>

+ 151 - 0
pages-mine/pages/address/add-or-update.vue

@@ -0,0 +1,151 @@
+<template>
+	<view class="page">
+		<view class="form">
+			<u-form :model="form" ref="form" label-width="180rpx">
+				<u-form-item label="收货人">
+					<u-input v-model="form.realname" placeholder="请输入收货人姓名" />
+				</u-form-item>
+				<u-form-item label="联系方式">
+					<u-input v-model="form.mobile" placeholder="请输入联系方式" />
+				</u-form-item>
+				<u-form-item label="所在地区">
+					<u-input v-model="cityPickerLabel" type="select" placeholder="请选择所在地区"
+						@click="showCityPicker = true" />
+				</u-form-item>
+				<u-form-item label="详细地址">
+					<u-input v-model="form.street" placeholder="请输入详细地址" />
+				</u-form-item>
+				<u-form-item :border-bottom="false" label="设为默认地址">
+					<u-switch slot="right" v-model="form.is_default" :active-value="1" :inactive-value="0" :active-color="appThemeColor"></u-switch>
+				</u-form-item>
+			</u-form>
+		</view>
+		<view class="btn">
+			<u-button type="primary" shape="circle" @click="submit">
+				<u-icon name="plus"></u-icon>
+				<text>保存地址</text>
+			</u-button>
+		</view>
+		<!-- 省市区选择器 -->
+		<CityPicker v-model="showCityPicker" :area-code="selAddressCodes" @city-change="cityChange"></CityPicker>
+	</view>
+</template>
+
+<script>
+	import CityPicker from '@/pages-mine/components/city-picker.vue';
+	export default {
+		components: {
+			CityPicker
+		},
+		data() {
+			return {
+				// 标题
+				title: '收货地址',
+				appThemeColor: this.$appTheme.appThemeColor,
+				// 表单
+				form: {
+					id: null,
+					type: 'add',
+					realname: '',
+					mobile: '',
+					province: '',
+					city: '',
+					area: '',
+					street: '',
+					addressCode: '',
+					is_default: 0
+				},
+				// 省市区
+				showCityPicker: false,
+				cityPickerLabel: '',
+				selAddressCodes:[],
+			};
+		},
+		onLoad(ops) {
+			console.log(ops);
+			if (ops.id) {
+				this.form.id = ops.id;
+				this.form.type = 'edit';
+				this.title = '修改地址';
+				this.getAddressDetail(ops.id);
+			} else {
+				this.form.id = null;
+				this.form.type = 'add';
+				this.title = '新建地址';
+			}
+			uni.setNavigationBarTitle({
+				title:this.title
+			})
+		},
+		methods: {
+			// 省市区选择回调
+			cityChange(e) {
+				console.log(e);
+				this.cityPickerLabel = e.province.name + '-' + e.city.name;
+				if(e.area){
+					this.cityPickerLabel += ('-' +e.area.name);
+				}
+				this.form.province = e.province.name;
+				this.form.city = e.city.name;
+				this.selAddressCodes = [e.province.code , e.city.code];
+				if(e.area){
+					this.form.area = e.area.name;
+					this.selAddressCodes.push(e.area.code);
+				}
+				this.form.addressCode = this.selAddressCodes.toString();
+			},
+
+			// 提交表单
+			submit() {
+				this.$u.api.updateAddressAjax(this.form).then(({
+					code,
+					data
+				}) => {
+					if (code == 1) {
+						uni.showToast('保存成功');
+						uni.navigateBack();
+					}
+				}).catch((data)=>{
+					console.log(data);
+				})
+			},
+			// 编辑
+			getAddressDetail(id) {
+				uni.showLoading();
+				this.$u.api.getAddressDetailAjax(id).then(({code,data})=>{
+					uni.hideLoading();
+					if(code==1){
+						this.form = {
+							...this.form,
+							id:data.id,
+							realname:data.realname,
+							mobile:data.mobile,
+							province:data.province,
+							city:data.city,
+							area:data.area,
+							street:data.street,
+							addressCode:data.addressCode,
+							is_default:data.is_default,
+						};
+						this.cityPickerLabel = data.province + '-' + data.city;
+						if(data.area){
+							this.cityPickerLabel += ('-' + data.area);
+						}
+						this.selAddressCodes = data.addressCode.split(',');
+					}
+				}).catch(()=>{uni.hideLoading();})
+			},
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.form {
+		background-color: $app-theme-bg-color;
+		padding: 30rpx;
+	}
+
+	.btn {
+		padding: 60rpx 30rpx;
+	}
+</style>

+ 140 - 0
pages-mine/pages/address/list.vue

@@ -0,0 +1,140 @@
+<template>
+	<view class="page">
+		<NoData v-if="!loading&&list.length<1"></NoData>
+		<view class="list" v-else>
+			<!-- @open="open" -->
+			<u-swipe-action :show="item.show" :index="index" 
+						v-for="(item, index) in list" :key="item.id" 
+						@click="click" 
+						:options="options"
+						@open="open"
+					>
+					<AddressCard  :isBack="isBack" :data="item" :showEdit="!isSelect" :showBorderBottom="index != list.length - 1"></AddressCard>
+			</u-swipe-action>
+			
+		</view>
+		<view class="btn">
+			<u-button type="primary" shape="circle" @click="$u.route({ url: '/pages-mine/pages/address/add-or-update' })">
+				<u-icon name="plus"></u-icon>
+				<text>新建收货地址</text>
+			</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+import AddressCard from '@/pages/mine/components/address-card.vue';
+export default {
+	components: {
+		AddressCard
+	},
+	data() {
+		return {
+			isSelect: false,
+			isBack: false,
+			loading:false,
+			list: [],
+			options: [
+				{
+					text: '设为默认',
+					style: {
+						backgroundColor: '#22ac38'
+					}
+				},
+				{
+					text: '删除',
+					style: {
+						backgroundColor: '#dd524d'
+					}
+				}
+			]
+		};
+	},
+	onLoad(ops) {
+		if (ops.isSelect) {
+			this.isSelect = ops.isSelect;
+			this.isBack = ops.isBack;
+		}
+		// this.getAddressList();
+	},
+	onShow(ops) {
+		this.getAddressList();
+	},
+	methods:{
+		click(index, index1) {
+			if(index1 == 1) {
+				this.delFun(index);
+			} else {
+				this.setDefaultAddress(index);
+			}
+		},
+		// 删除
+		delFun(index) {
+			this.$u.api.delAddressAjax(this.list[index].id).then(({code,data})=>{
+				if(code==1){
+					this.list.splice(index, 1);
+					this.list[index].show = false;
+				}else{
+					this.$u.toast(`删除失败`);
+				}
+			})
+		},
+		// 设为默认
+		setDefaultAddress(index) {
+			this.$u.api.setDefaultAddressAjax(this.list[index].id).then(({code,data})=>{
+				uni.hideLoading();
+				if(code==1){
+					this.list.forEach(e=>{
+						e.is_default = 0;
+						e.show = false;
+					})
+					this.list[index].is_default = 1;
+				}
+			})
+		},
+		open(index) {
+			// 先将正在被操作的swipeAction标记为打开状态,否则由于props的特性限制,
+			// 原本为'false',再次设置为'false'会无效
+			this.list[index].show = true;
+			this.list.map((val, idx) => {
+				if(index != idx) this.list[idx].show = false;
+			})
+		},
+		getAddressList(){
+			this.loading = true;
+			this.$u.api.getAddressListAjax().then(({code,data})=>{
+				this.loading = false;
+				if(code==1){
+					data.forEach(e=>{
+						e.show = false;
+					})
+					this.list = data;
+					console.log(this.list);
+				}
+			}).catch(()=>{
+				this.loading = false;
+			})
+		},
+	},
+};
+</script>
+
+<style lang="scss" scoped>
+.page {
+	background-color: $app-theme-bg-color;
+}
+.list {
+	padding: 0 30rpx 180rpx;
+	// height: calc(100vh - 200rpx);
+}
+.btn {
+	position: fixed;
+	bottom: 40rpx;
+	left: 0;
+	padding: 30rpx;
+	width: 100%;
+	text {
+		margin-left: 8rpx;
+	}
+}
+</style>

+ 202 - 0
pages-mine/pages/collection.vue

@@ -0,0 +1,202 @@
+<template>
+	<view class="collection">
+		<view class="topEditOpar" v-if="list.length>0">
+			<view class="check" v-if="isEdit">
+				<u-checkbox shape="circle" active-color="#ed3f14" icon-size="16"  v-model="checkAllStauts" @change="checkAllGoods"></u-checkbox>
+				<text>全选</text>
+			</view>
+			<view class="text opartxt" v-if="isEdit" @click="delCollection">
+				移出收藏夹
+			</view>
+			<view class="text" v-else @click="isEdit = true">
+				管理
+			</view>
+		</view>
+		<view class="list">
+			<view class="list_item" v-for="(item, index) in list" :key="index">
+				<view class="selcheck" v-if="isEdit">
+					<u-checkbox shape="circle" active-color="#ed3f14" icon-size="16" :disabled="goods.status==2"
+						v-model="item.checked"></u-checkbox>
+				</view>
+				<view class="infobox">
+					<collection :data="item"></collection>
+				</view>
+				
+				
+			</view>
+			
+		</view>
+		<NoData v-if="finish&&list.length<1"></NoData>
+		<LoadMore v-else :loadtype="{page, loading, finish}"></LoadMore>
+	</view>
+</template>
+
+<script>
+	var _self;
+	import collection from '@/pages/mall/components/collection.vue';
+	export default {
+		components: {
+			collection
+		},
+		data() {
+			return {
+				isEdit:false,
+				checkAllStauts: false,
+				
+				page:1,
+				loading:false,
+				finish:false,
+				list: [],
+				pullDown:false,
+			};
+		},
+		computed: {
+			checkAll() {
+				if (this.list.length < 1) {
+					this.checkAllStauts = false;
+					return;
+				}
+				this.list.map(item => (item.checked ? (this.totalPrice += Number(item.amount) * item.nums) : 0));
+				if (this.list.filter(item => item.checked).length == this.list.length) {
+					this.checkAllStauts = true;
+				} else {
+					this.checkAllStauts = false;
+				}
+			},
+		},
+		onLoad(ops) {
+			// if (ops.isSelect) this.isSelect = ops.isSelect;
+			_self = this;
+			this.getCollectionList();
+		},
+		onReachBottom(){
+			this.getCollectionList();
+		},
+		onPullDownRefresh(){
+			this.pullDown = true;
+			this.reData();
+		},
+		methods: {
+			// 刷新数据
+			reData(){
+				this.page=1;
+				this.finish=false;
+				this.list=[];
+				this.getCollectionList();
+			},
+			getCollectionList(){
+				if(this.finish)return false;
+				if(this.loading)return false;
+				this.$u.api.collectionListAjax(this.page).then(({code,data})=>{
+					console.log(code,data)
+					if(this.pullDown){
+						uni.stopPullDownRefresh();
+						this.pullDown = false;
+					}
+					this.loading = false;
+					let returnData = data.data;
+					if(code==1){
+						if(data.last_page<=data.current_page){
+							this.finish = true;
+						}
+						returnData.forEach(e => {
+							e.checked = false;
+						})
+						if(data.current_page==1){
+							this.list = returnData;
+						}else{
+							this.list=this.list.concat(returnData);
+						}
+					}else{
+						this.finish = true;
+					}
+					this.page++;
+				}).catch(()=>{
+					this.loading = false;
+					this.finish = true;
+					if(this.pullDown){
+						uni.stopPullDownRefresh();
+						this.pullDown = false;
+					}
+				})
+			},
+			
+			checkAllGoods() {
+				if (this.checkAllStauts) {
+					this.list.forEach(item => (item.checked = false));
+				} else {
+					this.list.forEach(item => (item.checked = true));
+				}
+				// this.$forceUpdate();
+			},
+			
+			// 移出收藏夹
+			delCollection(){
+				let selItems = this.list.filter(item => item.checked);
+				if(selItems.length >0){
+					uni.showModal({
+						title: '提示',
+						content: '确认要移出收藏夹?',
+						success: function(res) {
+							if (res.confirm) {
+								_self.comfirmF(selItems)
+							}
+						}
+					});
+				}else{
+					this.isEdit = false;
+				}
+			},
+			comfirmF(selItems){			
+				let ids = selItems.map(e=>e.id);
+				console.log(ids);
+				this.$u.api.delCollectionAjax(ids.toString()).then(({code,msg})=>{
+					this.$u.toast(msg);
+					if(code==1){
+						this.reData();
+					}
+				})
+			},
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+.collection {
+	padding: 20rpx;
+	min-height: 100vh;
+	box-sizing: border-box;
+}
+.list {
+	background-color: #fff;
+	border-radius: 20rpx;
+}
+.list_item{
+	display: flex;
+	align-items: center;
+	.infobox{
+		flex: 1;
+	}
+}
+.selcheck{
+	width: 80rpx;
+	padding-left: 20rpx;
+}
+.topEditOpar{
+	display: flex;
+	padding: 10rpx 20rpx;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	text-align: center;
+	.text{
+		flex: 1;
+		text-align: right;
+		font-weight: bold;
+		font-size: 32rpx;
+	}
+	.opartxt{
+		color: $app-theme-deep-color;
+	}
+}
+</style>

+ 154 - 0
pages-mine/pages/discounts.vue

@@ -0,0 +1,154 @@
+<template>
+	<view class="pagecon">
+		<NoData v-if="finish&&discountsList.length<1"></NoData>
+		<view class="list">
+			<discountsItem :select="selectIndex == index" :data="item" v-for="(item, index) in discountsList" :key="index" @click="changeDiscount(item, index)"></discountsItem>
+			
+		</view>
+	</view>
+</template>
+
+<script>
+import { formatDate } from '@/utils/tools.js'
+import discountsItem from '../components/discounts-item.vue'
+const app = getApp();
+export default {
+	components: {
+		discountsItem
+	},
+	data() {
+		return {
+			topHeight:app.globalData.statusBarHeight + app.globalData.navBarHeight,
+			selectIndex: -1,
+			page:1,
+			loading:false,
+			finish:false,
+			discountsList: [
+				// {
+				// 	"id": 1,
+				// 	"user_id": 1,
+				// 	"title": "新用户注册",
+				// 	"amount": "5.00",
+				// 	"limit_day": 5,
+				// 	"limit_time": 1706600909,
+				// 	"status": "2",
+				// 	"createtime": 1706168909,
+				// 	"updatetime": 1706168909
+				// },
+			]
+		};
+	},
+	
+	onLoad() {
+		this.getUserCouponList();
+	},
+	onReachBottom(){
+		console.log('滚动到底部')
+		this.nextPage();
+	},
+	methods: {
+		formatDateF(v){
+			return formatDate(v);
+		},
+		changeDiscount(item, index) {
+			this.selectIndex = index;
+		},
+		getUserCouponList(){
+			if(this.loading)return false;
+			this.$u.api.getUserCouponListAjax(this.page).then(({code,data})=>{
+				console.log(code,data)
+				this.loading = false;
+				if(code==1){
+					if(data.last_page<=data.current_page){
+						this.finish = true;
+					}
+					if(data.current_page==1){
+						this.discountsList = data.data;
+					}else{
+						this.discountsList=this.discountsList.concat(data.data);
+					}
+				}else{
+					this.finish = true;
+				}
+			}).catch(()=>{
+				this.loading = false;
+				this.finish = true;
+			})
+		},
+		nextPage(e){
+			if(this.finish){
+				/* 已滚动到底部,不继续加载 */
+				return false;
+			}
+			console.log(e);
+			this.page++;
+			this.getUserCouponList();
+		},
+	},
+};
+</script>
+
+<style lang="scss" scoped>
+	.pagecon{
+		padding: 30rpx;
+		min-height: 100vh;
+		box-sizing: border-box;
+		background-color: $app-theme-bg-gray-color;
+	}
+.list {
+	// margin: 30rpx;
+	.item {
+		width: 100%;
+		height: 174rpx;
+		background-image: url('@/pages-mine/static/discounts-bg.png');
+		background-size: cover;
+		margin-bottom: 30rpx;
+		display: flex;
+		justify-content: flex-start;
+		align-items: flex-end;
+		padding-bottom: 34rpx;
+		position: relative;
+
+		.left {
+			margin-right: 60rpx;
+			margin-left: 50rpx;
+			.discount {
+				text:nth-child(1) {
+					font-size: 22rpx;
+					color: $app-theme-text-money-color;
+				}
+				text:nth-child(2) {
+					font-size: 64rpx;
+					color: $app-theme-text-money-color;
+				}
+			}
+			.standard {
+				font-size: 20rpx;
+				color: $app-theme-card-gray-color;
+			}
+		}
+		.right {
+			.title {
+				font-size: 32rpx;
+				color: $app-theme-text-black-color;
+				margin-bottom: 32rpx;
+			}
+			.date {
+				font-size: 20rpx;
+				color: $app-theme-card-gray-color;
+			}
+		}
+
+		.select {
+			height: 60rpx;
+			width: 60rpx;
+			background-image: url('@/pages-mine/static/discounts-select.png');
+			background-size: cover;
+			position: absolute;
+			top: 4rpx;
+			right: 4rpx;
+			z-index: $app-zIndex-absolute;
+		}
+	}
+}
+</style>

Деякі файли не було показано, через те що забагато файлів було змінено