Browse Source

移除不使用的文件

master
453530270@qq.com 2 years ago
parent
commit
7c82500031
  1. 11
      api/article/category.js
  2. 17
      api/article/index.js
  3. 11
      api/balance/log.js
  4. 35
      api/cart.js
  5. 11
      api/category/index.js
  6. 19
      api/checkout.js
  7. 23
      api/comment.js
  8. 16
      api/coupon.js
  9. 11
      api/express.js
  10. 23
      api/goods.js
  11. 11
      api/goods/service.js
  12. 22
      api/myCoupon.js
  13. 47
      api/order.js
  14. 11
      api/recharge/order.js
  15. 11
      api/recharge/plan.js
  16. 35
      api/refund.js
  17. 18
      api/upload.js
  18. 331
      pages/cal/index.vue
  19. 500
      pages/cart/index.vue
  20. 402
      pages/category/components/commodity.vue
  21. 110
      pages/category/components/primary.vue
  22. 180
      pages/category/components/secondary.vue
  23. 165
      pages/category/index.vue
  24. 523
      pages/checkout/index.vue
  25. 445
      pages/checkout/style.scss
  26. 274
      pages/comment/index.vue
  27. 250
      pages/coupon/index.vue
  28. 120
      pages/custom/index.vue
  29. 162
      pages/goods/components/Comment.vue
  30. 158
      pages/goods/components/Service.vue
  31. 173
      pages/goods/components/SkuPopup.vue
  32. 146
      pages/goods/components/SlideImage.vue
  33. 227
      pages/goods/detail.scss
  34. 290
      pages/goods/detail.vue
  35. 463
      pages/goods/list.vue
  36. 306
      pages/my-coupon/index.vue
  37. 136
      pages/order/center.vue
  38. 571
      pages/order/comment/index.vue
  39. 866
      pages/order/detail.vue
  40. 202
      pages/order/express/index.vue
  41. 596
      pages/order/index.vue
  42. 127
      pages/points/log.vue
  43. 429
      pages/refund/apply.vue
  44. 483
      pages/refund/detail.vue
  45. 263
      pages/refund/index.vue
  46. 227
      pages/search/index.vue
  47. 127
      pages/wallet/balance/log.vue
  48. 167
      pages/wallet/index.vue
  49. 300
      pages/wallet/recharge/index.vue
  50. 127
      pages/wallet/recharge/order.vue

11
api/article/category.js

@ -1,11 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'article.category/list'
}
// 页面数据
export function list() {
return request.get(api.list)
}

17
api/article/index.js

@ -1,17 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'article/list',
detail: 'article/detail'
}
// 文章列表
export function list(param, option) {
return request.get(api.list, param, option)
}
// 文章详情
export function detail(articleId) {
return request.get(api.detail, { articleId })
}

11
api/balance/log.js

@ -1,11 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'balance.log/list'
}
// 余额账单明细
export const list = (param) => {
return request.get(api.list, param)
}

35
api/cart.js

@ -1,35 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'cart/list',
total: 'cart/total',
add: 'cart/add',
update: 'cart/update',
clear: 'cart/clear'
}
// 购物车列表
export const list = () => {
return request.get(api.list, {}, { load: false })
}
// 购物车商品总数量
export const total = () => {
return request.get(api.total, {}, { load: false })
}
// 加入购物车
export const add = (goodsId, goodsSkuId, goodsNum) => {
return request.post(api.add, { goodsId, goodsSkuId, goodsNum })
}
// 更新购物车商品数量
export const update = (goodsId, goodsSkuId, goodsNum) => {
return request.post(api.update, { goodsId, goodsSkuId, goodsNum }, { isPrompt: false })
}
// 删除购物车中指定记录
export const clear = (cartIds = []) => {
return request.post(api.clear, { cartIds })
}

11
api/category/index.js

@ -1,11 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'category/list'
}
// 页面数据
export function list() {
return request.get(api.list)
}

19
api/checkout.js

@ -1,19 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
order: 'checkout/order',
submit: 'checkout/submit',
}
// mode: 结算模式 (buyNow立即购买 cart购物车)
// 结算台订单信息
export const order = (mode, param) => {
return request.get(api.order, { mode, ...param })
}
// 结算台订单提交
export const submit = (mode, data) => {
return request.post(api.submit, { mode, ...data })
}

23
api/comment.js

@ -1,23 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'comment/list',
listRows: 'comment/listRows',
total: 'comment/total'
}
// 商品评价列表
export const list = (goodsId, param, option) => {
return request.get(api.list, { ...param, goodsId }, option)
}
// 商品评价列表 (限制数量, 用于商品详情页展示)
export const listRows = (goodsId, limit = 5) => {
return request.get(api.listRows, { goodsId, limit })
}
// 商品评分总数
export const total = (goodsId) => {
return request.get(api.total, { goodsId })
}

16
api/coupon.js

@ -1,16 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'coupon/list'
}
// 优惠券列表
export const list = (param, option) => {
const options = {
isPrompt: true, //(默认 true 说明:本接口抛出的错误是否提示)
load: true, //(默认 true 说明:本接口是否提示加载动画)
...option
}
return request.get(api.list, param, options)
}

11
api/express.js

@ -1,11 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'express/list'
}
// 物流公司列表
export const list = (param) => {
return request.get(api.list, param)
}

23
api/goods.js

@ -1,23 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'goods/list',
detail: 'goods/detail',
specData: 'goods/specData'
}
// 商品列表
export const list = param => {
return request.get(api.list, param)
}
// 商品详情
export const detail = goodsId => {
return request.get(api.detail, { goodsId })
}
// 获取商品规格数据
export const specData = (goodsId) => {
return request.get(api.specData, { goodsId })
}

11
api/goods/service.js

@ -1,11 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'goods.service/list'
}
// 商品评价列表
export function list(goodsId) {
return request.get(api.list, { goodsId })
}

22
api/myCoupon.js

@ -1,22 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'myCoupon/list',
receive: 'myCoupon/receive'
}
// 我的优惠券列表
export const list = (param, option) => {
const options = {
isPrompt: true, //(默认 true 说明:本接口抛出的错误是否提示)
load: true, //(默认 true 说明:本接口是否提示加载动画)
...option
}
return request.get(api.list, param, options)
}
// 领取优惠券
export const receive = (couponId, data) => {
return request.post(api.receive, { couponId, ...couponId, data })
}

47
api/order.js

@ -1,47 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
todoCounts: 'order/todoCounts',
list: 'order/list',
detail: 'order/detail',
express: 'order/express',
cancel: 'order/cancel',
receipt: 'order/receipt',
pay: 'order/pay'
}
// 当前用户待处理的订单数量
export function todoCounts(param, option) {
return request.get(api.todoCounts, param, option)
}
// 我的订单列表
export function list(param, option) {
return request.get(api.list, param, option)
}
// 订单详情
export function detail(orderId, param) {
return request.get(api.detail, { orderId, ...param })
}
// 获取物流信息
export function express(orderId, param) {
return request.get(api.express, { orderId, ...param })
}
// 取消订单
export function cancel(orderId, data) {
return request.post(api.cancel, { orderId, ...data })
}
// 确认收货
export function receipt(orderId, data) {
return request.post(api.receipt, { orderId, ...data })
}
// 立即支付
export function pay(orderId, payType, param) {
return request.get(api.pay, { orderId, payType, ...param })
}

11
api/recharge/order.js

@ -1,11 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'recharge.order/list'
}
// 我的充值记录列表
export const list = (param) => {
return request.get(api.list, param)
}

11
api/recharge/plan.js

@ -1,11 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'recharge.plan/list'
}
// 充值套餐列表
export const list = (param) => {
return request.get(api.list, param)
}

35
api/refund.js

@ -1,35 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
list: 'refund/list',
goods: 'refund/goods',
apply: 'refund/apply',
detail: 'refund/detail',
delivery: 'refund/delivery'
}
// 售后单列表
export const list = (param, option) => {
return request.get(api.list, param, option)
}
// 订单商品详情
export const goods = (orderGoodsId, param) => {
return request.get(api.goods, { orderGoodsId, ...param })
}
// 申请售后
export const apply = (orderGoodsId, data) => {
return request.post(api.apply, { orderGoodsId, form: data })
}
// 售后单详情
export const detail = (orderRefundId, param) => {
return request.get(api.detail, { orderRefundId, ...param })
}
// 用户发货
export const delivery = (orderRefundId, data) => {
return request.post(api.delivery, { orderRefundId, form: data })
}

18
api/upload.js

@ -1,18 +0,0 @@
import request from '@/utils/request'
// api地址
const api = {
image: 'upload/image'
}
// 图片上传
export const image = files => {
// 文件上传大小, 2M
const maxSize = 1024 * 1024 * 2
// 执行上传
return new Promise((resolve, reject) => {
request.urlFileUpload({ files, maxSize })
.then(result => resolve(result.map(item => item.data.fileInfo.file_id), result))
.catch(reject)
})
}

331
pages/cal/index.vue

@ -1,331 +0,0 @@
<template>
<!-- 打卡日历页面 -->
<view class='all'>
<view class="bar">
<!-- 上一个月 -->
<view class="previous" @click="handleCalendar(0)">
<button class="barbtn">上一月</button>
</view>
<!-- 显示年月 -->
<view class="date">{{cur_year || "--"}} {{cur_month || "--"}} </view>
<!-- 下一个月 -->
<view class="next" @click="handleCalendar(1)">
<button class="barbtn">下一月</button>
</view>
</view>
<!-- 显示星期 -->
<view class="week">
<view class="week_ri" v-for="(item,index) in weeks_ch" :key="index">{{item}}</view>
</view>
<view class="myDateTable">
<view v-for="(item,j) in days" :key="j" class='dateCell'>
<view v-if="item.date==undefined || item.date == null" class='cell'>
<text :decode="true">&nbsp;&nbsp;</text>
</view>
<view v-else @click="clickSignUp(item.date)">
<!-- 已订餐到日期 -->
<view v-if="item.isSign == 5" class='cell yellow' style="background: #0000a8;color: #ffffff;">
<text>{{item.date}}</text>
</view>
<view class="cell white bg-red" style="background: #0000e7;color: #ffffff;" v-else-if="item.isSign == 2">
<text>{{item.date}}</text>
</view>
<!-- 点击的日期 -->
<view class="cell white bg-red" style="background: #ffaa7f;color: #ffffff;" v-else-if="item.isSign == 3">
<text>{{item.date}}</text>
</view>
<!-- 今天的日期 -->
<view v-else-if="item.date == today" class='cell yellow curday' style="color: #fa2299;">{{item.date}}</view>
<!-- 当前日期之后 -->
<view class="whiteColor cell" v-else>
<text>{{item.date}}</text>
</view>
</view>
</view>
</view>
<!-- 活动列表 -->
<view class="actlist">
<view class="act-item">
<view class="act-title">五月端午邀你去踏青</view>
<view class="act-desc">端午时节踏青开始</view>
</view>
<view class="act-item">
<view class="act-title">五月端午邀你去踏青</view>
<view class="act-desc">端午时节踏青开始</view>
</view>
<view class="act-item">
<view class="act-title">五月端午邀你去踏青</view>
<view class="act-desc">端午时节踏青开始</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
days: [],//
SignUp: [{'type':1,t:1},{'type':2,t:10}],//
cur_year: 0, //
cur_month: 0, //
today: parseInt(new Date().getDate()), //
toMonth: parseInt(new Date().getMonth() + 1), //
toYear: parseInt(new Date().getFullYear()), //
weeks_ch: ['日', '一', '二', '三', '四', '五', '六'],
};
},
props: {
//
sendYear: {
type: Number,
default: new Date().getFullYear()
},
//
sendMonth: {
type: Number,
default: new Date().getMonth() + 1
},
},
created() {
this.cur_year = this.sendYear;//
this.cur_month = this.sendMonth;//
this.calculateEmptyGrids(this.cur_year, this.cur_month);
this.calculateDays(this.cur_year, this.cur_month);
//
this.onJudgeSign(this.SignUp);
},
methods: {
//
getThisMonthDays(year, month) {
return new Date(year, month, 0).getDate()
},
//
getFirstDayOfWeek(year, month) {
return new Date(Date.UTC(year, month - 1, 1)).getDay();
},
// 1days
calculateEmptyGrids(year, month) {
//
this.days = [];
const firstDayOfWeek = this.getFirstDayOfWeek(year, month);
if (firstDayOfWeek > 0) {
for (let i = 0; i < firstDayOfWeek; i++) {
var obj = {
date: null,
isSign: false
}
this.days.push(obj);
}
}
},
// days
calculateDays(year, month) {
const thisMonthDays = this.getThisMonthDays(year, month);
for (let i = 1; i <= thisMonthDays; i++) {
var obj = {
date: i,
isSign: false
}
this.days.push(obj);
}
//console.log(this.days);
},
//
onJudgeSign(date) {
var signs = date;
var daysArr = this.days;
//
for(var i=0;i<signs.length;i++){
for (var j=0; j<daysArr.length;j++) {
if (daysArr[j].date == signs[i]['t']) {
if(signs[i]['type'] == 1){
daysArr[j].isSign = 2;//
}else{
daysArr[j].isSign = 5;//
}
}
}
}
this.days = daysArr;
},
//
handleCalendar(type) {
const cur_year = parseInt(this.cur_year);
const cur_month = parseInt(this.cur_month);
var newMonth;
var newYear = cur_year;
if (type === 0) { //
newMonth = cur_month - 1;
if (newMonth < 1) {
newYear = cur_year - 1;
newMonth = 12;
}
} else {
newMonth = cur_month + 1;
if (newMonth > 12) {
newYear = cur_year + 1;
newMonth = 1;
}
}
this.calculateEmptyGrids(newYear, newMonth);
this.calculateDays(newYear, newMonth);
//
this.SignUp =[{'type':1,t:3},{'type':2,t:11}],//
this.onJudgeSign(this.SignUp);
this.cur_year = newYear;
this.cur_month = newMonth;
// this.SignUp = []; //
this.$emit('dateChange',this.cur_year+"-"+this.cur_month); //
},
//
clickSignUp(t){
// console.log(this.cur_year)//
// console.log(this.cur_month)//
var t = t;//
var signs = this.SignUp;
var daysArr = this.days;
//,
for(var i=0;i<signs.length;i++){
for (var j=0; j<daysArr.length;j++) {
if (daysArr[j].date == signs[i]['t']) {
if(signs[i]['type'] == 1){
daysArr[j].isSign = 2;//
}else{
daysArr[j].isSign = 5;//
}
}
if(t == daysArr[j].date){
daysArr[j].isSign = 3
}
if(daysArr[j].isSign != 2 && t != daysArr[j].date && daysArr[j].isSign != 5){
daysArr[j].isSign = 1;
}
}
}
this.days = daysArr;
}
}
}
</script>
<style lang="less" scoped>
.all .bar {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 10rpx;
}
.bar .barbtn {
height: 30px;
line-height: 30px;
font-size: 12px;
}
/* 星期 */
.all .week {
display: flex;
/* flex-direction: row; */
/* justify-content: space-between; */
padding: 20rpx;
border-radius: 10px;
background-color: #fff;
width: 90%;
margin-left: 2%;
}
.week_ri{
margin-left: 19rpx;
padding: 0 48rpx 0 0;
}
.myDateTable {
margin: 2.5vw;
border-radius: 10px;
background: #fff;
}
.myDateTable .dateCell {
width: 11vw;
padding: 1vw;
display: inline-block;
text-align: center;
font-size: 16px;
}
.dateCell .cell {
display: flex;
border-radius: 50%;
height: 9vw;
justify-content: center;
align-items: center;
}
.greenColor {
color: #01b90b;
font-weight: bold;
}
.bgWhite {
background-color: #fff;
}
.bgGray {
background-color: rgba(255, 255, 255, 0.42);
}
.bgBlue {
font-size: 14px;
background-color: #4b95e6;
}
.redColor {
color: #ff0000;
}
.curday{
color: #fa2209;
font-size: 36rpx;
font-weight: 600;
}
.TipArea{
word-break:break-all;
word-wrap:break-word;
font-size: 14px;
padding: 10px;
}
.impTip{
display: inline-block;
color: #ff0000;
}
.actlist{
padding: 10rpx;
.act-item{
padding:20rpx;
color: #333;
border-bottom: #ccc 1px solid;
margin-top: 20rpx;
background: #fff;
.act-title{
font-size: 36rpx;
line-height: 1.8;
}
}
}
</style>

500
pages/cart/index.vue

@ -1,500 +0,0 @@
<template>
<view class="container">
<!-- 页面顶部 -->
<view v-if="list.length" class="head-info">
<view class="cart-total">
<text></text>
<text class="active">{{ total }}</text>
<text>件商品</text>
</view>
<view class="cart-edit" @click="handleToggleMode">
<view v-if="mode == 'normal'" class="normal">
<text class="icon iconfont icon-bianji"></text>
<text>编辑</text>
</view>
<view v-if="mode == 'edit'" class="edit">
<text>完成</text>
</view>
</view>
</view>
<!-- 购物车商品列表 -->
<view v-if="list.length" class="cart-list">
<view class="cart-item" v-for="(item, index) in list" :key="index">
<label class="item-radio" @click.stop="handleCheckItem(item.id)">
<radio class="radio" color="#fa2209" :checked="inArray(item.id, checkedIds)" />
</label>
<view class="goods-image" @click="onTargetGoods(item.goods_id)">
<image class="image" :src="item.goods.goods_image" mode="scaleToFill"></image>
</view>
<view class="item-content">
<view class="goods-title" @click="onTargetGoods(item.goods_id)">
<text class="twoline-hide">{{ item.goods.goods_name }}</text>
</view>
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in item.goods.skuInfo.goods_props" :key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
<view class="item-foot">
<view class="goods-price">
<text class="unit"></text>
<text class="value">{{ item.goods.skuInfo.goods_price }}</text>
</view>
<view class="stepper">
<u-number-box :min="1" :value="item.goods_num" :step="1" @change="onChangeStepper($event, item)" />
</view>
</view>
</view>
</view>
</view>
<!-- 购物车数据为空 -->
<empty v-if="!list.length" :isLoading="isLoading" :custom-style="{ padding: '180rpx 50rpx' }" tips="您的购物车是空的, 快去逛逛吧">
<view slot="slot" class="empty-ipt" @click="onTargetIndex">
<text>去逛逛</text>
</view>
</empty>
<!-- 底部操作栏 -->
<view v-if="list.length" class="footer-fixed">
<label class="all-radio" @click="handleCheckAll">
<radio class="radio" color="#fa2209" :checked="checkedIds.length > 0 && checkedIds.length === list.length" />
<text>全选</text>
</label>
<view class="total-info">
<text>合计</text>
<view class="goods-price">
<text class="unit"></text>
<text class="value">{{ totalPrice }}</text>
</view>
</view>
<view class="cart-action">
<view class="btn-wrapper">
<!-- dev:下面的disabled条件使用checkedIds.join方式判断 -->
<!-- dev:通常情况下vue项目使用checkedIds.length更合理, 但是length属性在微信小程序中不起作用 -->
<view v-if="mode == 'normal'" class="btn-item btn-main" :class="{ disabled: checkedIds.join() == '' }" @click="handleOrder()">
<text>去结算 {{ checkedIds.length > 0 ? `(${checkedIds.length})` : '' }}</text>
</view>
<view v-if="mode == 'edit'" class="btn-item btn-main" :class="{ disabled: !checkedIds.length }" @click="handleDelete()">
<text>删除</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { inArray, arrayIntersect, debounce } from '@/utils/util'
import { checkLogin, setCartTotalNum, setCartTabBadge } from '@/core/app'
import * as CartApi from '@/api/cart'
import Empty from '@/components/empty'
const CartIdsIndex = 'CartIds'
export default {
components: {
Empty
},
data() {
return {
inArray,
//
isLoading: true,
// : normal edit
mode: 'normal',
//
list: [],
//
total: null,
// ID
checkedIds: [],
//
totalPrice: '0.00'
}
},
watch: {
//
checkedIds: {
handler(val) {
//
this.onCalcTotalPrice()
//
uni.setStorageSync(CartIdsIndex, val)
},
immediate: false
},
//
total(val) {
//
setCartTotalNum(val)
setCartTabBadge()
}
},
/**
* 生命周期函数--监听页面显示
*/
onShow(options) {
//
checkLogin() ? this.getCartList() : this.isLoading = false
//
this.checkedIds = uni.getStorageSync(CartIdsIndex)
},
methods: {
// ()
onCalcTotalPrice() {
const app = this
//
const checkedList = app.list.filter(item => inArray(item.id, app.checkedIds))
//
let tempPrice = 0;
checkedList.forEach(item => {
// , 便 ()
const unitPrice = item.goods.skuInfo.goods_price * 100
tempPrice += unitPrice * item.goods_num
})
app.totalPrice = (tempPrice / 100).toFixed(2)
},
//
getCartList() {
const app = this
app.isLoading = true
CartApi.list()
.then(result => {
app.list = result.data.list
app.total = result.data.cartTotal
// checkedIdsID
app.onClearInvalidId()
})
.finally(() => app.isLoading = false)
},
// checkedIdsID
onClearInvalidId() {
const app = this
const listIds = app.list.map(item => item.id)
app.checkedIds = arrayIntersect(listIds, app.checkedIds)
},
//
handleToggleMode() {
this.mode = this.mode == 'normal' ? 'edit' : 'normal'
},
//
onChangeStepper({ value }, item) {
//
if (item.goods_num == value) return
//
if (!item.debounceHandle) {
item.oldValue = item.goods_num
item.debounceHandle = debounce(this.onUpdateCartNum, 500)
}
//
item.goods_num = value
// ()
item.debounceHandle(item, item.oldValue, value)
},
//
onUpdateCartNum(item, oldValue, newValue) {
const app = this
CartApi.update(item.goods_id, item.goods_sku_id, newValue)
.then(result => {
//
app.total = result.data.cartTotal
//
app.onCalcTotalPrice()
//
item.debounceHandle = null
})
.catch(err => {
//
item.goods_num = oldValue
setTimeout(() => app.$toast(err.errMsg), 10)
})
},
//
onTargetGoods(goodsId) {
this.$navTo('pages/goods/detail', { goodsId })
},
// ,
onTargetIndex() {
this.$navTo('pages/index/index')
},
//
handleCheckItem(cartId) {
const { checkedIds } = this
const index = checkedIds.findIndex(id => id === cartId)
index < 0 ? checkedIds.push(cartId) : checkedIds.splice(index, 1)
},
//
handleCheckAll() {
const { checkedIds, list } = this
this.checkedIds = checkedIds.length === list.length ? [] : list.map(item => item.id)
},
//
handleOrder() {
const app = this
if (app.checkedIds.length) {
const cartIds = app.checkedIds.join()
app.$navTo('pages/checkout/index', { mode: 'cart', cartIds })
}
},
//
handleDelete() {
const app = this
if (!app.checkedIds.length) {
return false
}
uni.showModal({
title: '友情提示',
content: '您确定要删除该商品吗?',
showCancel: true,
success({ confirm }) {
//
confirm && app.onClearCart()
}
})
},
//
onClearCart() {
const app = this
CartApi.clear(app.checkedIds)
.then(result => {
app.getCartList()
app.handleToggleMode()
})
}
}
}
</script>
<style>
page {
background: #f5f5f5;
}
</style>
<style lang="scss" scoped>
//
.head-info {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4rpx 30rpx;
// background-color: #fff;
height: 80rpx;
.cart-total {
font-size: 28rpx;
color: #333;
.active {
color: #FA2209;
margin: 0 2rpx;
}
}
.cart-edit {
padding-left: 20rpx;
.icon {
margin-right: 12rpx;
}
.edit {
color: #fa2209;
}
}
}
//
.cart-list {
padding: 0 16rpx 110rpx 16rpx;
}
.cart-item {
background: #fff;
border-radius: 12rpx;
display: flex;
align-items: center;
padding: 30rpx 16rpx;
margin-bottom: 24rpx;
.item-radio {
width: 56rpx;
height: 80rpx;
line-height: 80rpx;
margin-right: 10rpx;
text-align: center;
.radio {
transform: scale(0.76)
}
}
.goods-image {
width: 200rpx;
height: 200rpx;
.image {
display: block;
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
.item-content {
flex: 1;
padding-left: 24rpx;
.goods-title {
font-size: 28rpx;
max-height: 76rpx;
}
.goods-props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
.goods-props-item {
display: inline-block;
margin-right: 14rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
width: auto;
}
}
.item-foot {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20rpx;
.goods-price {
vertical-align: bottom;
color: $uni-text-color-active;
.unit {
font-size: 24rpx;
}
.value {
font-size: 32rpx;
}
}
}
}
}
//
.empty-ipt {
width: 220rpx;
margin: 0 auto;
font-size: 32rpx;
height: 64rpx;
line-height: 64rpx;
text-align: center;
color: #fff;
border-radius: 50rpx;
background: linear-gradient(to right, #f9211c, #ff6335);
}
//
.footer-fixed {
display: flex;
align-items: center;
height: 96rpx;
background: #fff;
padding: 0 30rpx;
position: fixed;
bottom: var(--window-bottom);
left: 0;
right: 0;
z-index: 11;
.all-radio {
width: 140rpx;
display: flex;
align-items: center;
.radio {
margin-bottom: -4rpx;
transform: scale(0.76)
}
}
.total-info {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 30rpx;
.goods-price {
vertical-align: bottom;
color: #fa2209;
.unit {
font-size: 24rpx;
}
.value {
font-size: 32rpx;
}
}
}
.cart-action {
width: 200rpx;
.btn-wrapper {
height: 100%;
display: flex;
align-items: center;
}
.btn-item {
flex: 1;
font-size: 28rpx;
height: 72rpx;
line-height: 72rpx;
text-align: center;
color: #fff;
border-radius: 50rpx;
}
//
.btn-main {
background: linear-gradient(to right, #f9211c, #ff6335);
//
&.disabled {
background: #ff9779;
}
}
}
}
</style>

402
pages/category/components/commodity.vue

@ -1,402 +0,0 @@
<template>
<view class="container">
<!-- 一级分类 -->
<scroll-view class="cate-left" :scroll-y="true" :style="{ height: `${scrollHeight}px` }" @touchmove.stop.prevent>
<text class="type-nav" :class="{ selected: curIndex == -1 }" @click="handleSelectNav(-1)">全部</text>
<text class="type-nav" :class="{ selected: curIndex == index }" v-for="(item, index) in list" :key="index" @click="handleSelectNav(index)">{{ item.name }}</text>
</scroll-view>
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption" :bottombar="false" @up="upCallback">
<view class="cate-content">
<!-- 子分类 -->
<view v-if="subCateList.length" class="sub-cate-list clearfix" :class="{ 'display-fold': !showSubCate }" @touchmove.stop.prevent>
<view class="nav-icon" @click="handleShowSubCate">
<text class="iconfont" :class="[ showSubCate ? 'icon-arrow-up' : 'icon-arrow-down' ]"></text>
</view>
<view class="sub-cate-item" :class="{ selected: curIndex2 == -1 }" @click="handleSelectSubCate(-1)">
<text>全部</text>
</view>
<view class="sub-cate-item" v-for="(item, index) in subCateList" :key="index" :class="{ selected: curIndex2 == index }" @click="handleSelectSubCate(index)">
<text>{{ item.name }}</text>
</view>
</view>
<!-- 商品列表 -->
<view class="goods-list">
<view class="goods-item--container" v-for="(item, index) in goodsList.data" :key="index">
<view class="goods-item" @click="onTargetGoods(item.goods_id)">
<!-- 商品图片 -->
<view class="goods-item_left">
<image class="image" :src="item.goods_image"></image>
</view>
<view class="goods-item_right">
<!-- 商品标题 -->
<view class="goods-name">
<text class="twoline-hide">{{ item.goods_name }}</text>
</view>
<!-- 商品信息 -->
<view class="goods-item_desc">
<view class="desc_footer">
<view class="item-prices oneline-hide">
<text class="price_x">¥{{ item.goods_price_min }}</text>
<text v-if="item.line_price_min > 0" class="price_y">¥{{ item.line_price_min }}</text>
</view>
<add-cart-btn v-if="setting.showAddCart" :btnStyle="setting.cartStyle" @click="handleAddCart(item)" />
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 遮罩层 -->
<view class="mask" v-show="showSubCate" @touchmove.stop.prevent @click="handleShowSubCate"></view>
<!-- 加入购物车组件 -->
<AddCartPopup ref="AddCartPopup" @addCart="onUpdateCartTabBadge" />
</view>
</mescroll-body>
</view>
</template>
<script>
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import { getEmptyPaginateObj, getMoreListData, setCartTabBadge } from '@/core/app'
import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
import Empty from '@/components/empty'
import AddCartBtn from '@/components/add-cart-btn'
import AddCartPopup from '@/components/add-cart-popup'
import { rpx2px } from '@/utils/util'
import * as GoodsApi from '@/api/goods'
const pageSize = 15
export default {
components: {
MescrollBody,
Empty,
AddCartBtn,
AddCartPopup
},
mixins: [MescrollMixin],
props: {
//
list: {
type: Array,
default: []
},
//
setting: {
type: Object,
default: () => {}
},
},
data() {
return {
//
PageCategoryStyleEnum,
//
scrollHeight: 0,
//
curIndex: -1,
//
showSubCate: false,
//
curIndex2: -1,
//
goodsList: getEmptyPaginateObj(),
//
upOption: {
//
auto: true,
// ; 10
page: { size: pageSize },
// 3
noMoreSize: 3,
//
toTop: { right: 30, bottom: 48, zIndex: 9 }
}
}
},
created() {
//
this.setListHeight()
},
computed: {
//
subCateList() {
if (this.list[this.curIndex] && this.list[this.curIndex].children) {
return this.list[this.curIndex].children
}
return []
}
},
methods: {
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
//
app.getGoodsList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
/**
* 获取商品列表
* @param {Number} pageNo 页码
*/
getGoodsList(pageNo = 1) {
const app = this
const categoryId = app.getCategoryId()
return new Promise((resolve, reject) => {
GoodsApi.list({ categoryId, page: pageNo }, { load: false })
.then(result => {
const newList = result.data.list
app.goodsList.data = getMoreListData(newList, app.goodsList, pageNo)
app.goodsList.last_page = newList.last_page
resolve(newList)
})
.catch(reject)
})
},
// ID
getCategoryId() {
const app = this
if (app.curIndex2 > -1) {
return app.subCateList[app.curIndex2].category_id
}
return app.curIndex > -1 ? app.list[app.curIndex].category_id : 0
},
//
setListHeight() {
const { windowHeight } = uni.getSystemInfoSync()
this.scrollHeight = windowHeight - rpx2px(88)
},
//
handleSelectNav(index) {
this.curIndex = index
this.onRefreshList()
this.showSubCate = false
this.curIndex2 = -1
},
//
handleSelectSubCate(index) {
this.curIndex2 = index
this.showSubCate = false
this.onRefreshList()
},
//
onRefreshList() {
this.goodsList = getEmptyPaginateObj()
setTimeout(() => this.mescroll.resetUpScroll(), 120)
},
//
onTargetGoods(goodsId) {
this.$navTo('pages/goods/detail', { goodsId })
},
//
handleAddCart(item) {
this.$refs.AddCartPopup.handle(item)
},
//
onUpdateCartTabBadge() {
console.log('onUpdateCartTabBadge')
setCartTabBadge()
},
//
handleShowSubCate() {
this.showSubCate = !this.showSubCate
}
}
}
</script>
<style lang="scss" scoped>
.container {
padding-left: 173rpx;
}
//
.cate-content {
z-index: 1;
background: #fff;
padding-top: 88rpx;
min-height: 300rpx;
}
// + 20
.cate-left {
position: fixed;
top: calc(88rpx + var(--window-top));
left: var(--window-left);
bottom: var(--window-bottom);
width: 173rpx;
height: 100%;
background: #f8f8f8;
color: #444;
}
//
.type-nav {
position: relative;
height: 90rpx;
z-index: 10;
display: block;
font-size: 26rpx;
display: flex;
justify-content: center;
align-items: center;
&.selected {
background: #fff;
border-right: none;
font-size: 28rpx;
color: #fa2209;
}
}
//
.goods-list {
background: #fff;
position: relative;
}
.goods-item {
padding: 28rpx 22rpx;
display: flex;
}
.goods-item_left {
position: relative;
background: #fff;
margin-right: 20rpx;
.image {
display: block;
width: 180rpx;
height: 180rpx;
}
}
.goods-item_right {
position: relative;
flex: 1;
.goods-name {
display: block;
width: 100%;
min-height: 68rpx;
font-size: 28rpx;
line-height: 1.3;
color: #333;
}
}
.goods-item_desc {
margin-top: 20rpx;
.people {
margin-right: 14rpx;
}
.desc_footer {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
right: 0rpx;
bottom: 0rpx;
min-height: 44rpx;
.item-prices {
padding-right: 6rpx;
.price_x {
margin-right: 14rpx;
color: rgb(240, 60, 60);
font-size: 28rpx;
}
.price_y {
color: #999;
text-decoration: line-through;
font-size: 24rpx;
}
}
}
}
//
.sub-cate-list {
background-color: #fff;
width: 100%;
z-index: 9;
padding: 8rpx 40rpx 0 14rpx;
overflow: hidden;
position: sticky;
top: calc(88rpx + var(--window-top));
&.display-fold {
height: 86rpx;
}
.nav-icon {
position: absolute;
right: 16rpx;
top: 12rpx;
font-size: 32rpx;
}
.sub-cate-item {
float: left;
background: #f8f8f8;
padding: 10rpx 30rpx;
margin-right: 22rpx;
margin-bottom: 24rpx;
font-size: 26rpx;
border-radius: 14rpx;
border: 1rpx solid #f8f8f8;
&.selected {
color: #fa2209;
border: 1rpx solid #fa2209;
}
}
}
//
.mask {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 8;
background-color: rgba(0, 0, 0, 0.4);
transition: all 0.3s ease-in-out 0s;
}
</style>

110
pages/category/components/primary.vue

@ -1,110 +0,0 @@
<template>
<view class="primary">
<!-- 一级分类(大图) 10 -->
<view v-if="list.length > 0 && display == PageCategoryStyleEnum.ONE_LEVEL_BIG.value" class="cate-content">
<view class="cate-wrapper cate_style__10 clearfix">
<view class="cate-item" v-for="(item, index) in list" :key="index" @click="onTargetGoodsList(item.category_id)">
<image v-if="item.image" class="image" mode="widthFix" :src="item.image.preview_url"></image>
</view>
</view>
</view>
<!-- 一级分类(小图) 11 -->
<view v-if="list.length > 0 && display == PageCategoryStyleEnum.ONE_LEVEL_SMALL.value" class="cate-content">
<view class="cate-wrapper cate_style__11 clearfix">
<view class="cate-item" v-for="(item, index) in list" :key="index" @click="onTargetGoodsList(item.category_id)">
<image v-if="item.image" class="image" mode="widthFix" :src="item.image.preview_url"></image>
<view class="cate-name">{{ item.name }}</view>
</view>
</view>
</view>
<empty v-if="!list.length" :tips="'亲,暂无商品分类' + display" />
</view>
</template>
<script>
import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
import Empty from '@/components/empty'
export default {
components: {
Empty
},
props: {
//
display: {
type: Number,
default: 10
},
//
list: {
type: Array,
default: []
}
},
data() {
return {
//
PageCategoryStyleEnum,
}
},
methods: {
//
onTargetGoodsList(categoryId) {
this.$navTo('pages/goods/list', { categoryId })
}
}
}
</script>
<style lang="scss" scoped>
//
.cate-content {
z-index: 1;
background: #fff;
padding-top: 96rpx;
.cate-wrapper {
padding: 0 20rpx 20rpx 20rpx;
box-sizing: border-box;
}
}
// () 10
.cate_style__10 .cate-item {
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.image {
display: block;
width: 100%;
height: auto;
}
}
// () 11
.cate_style__11 .cate-item {
float: left;
padding: 25rpx;
width: 33.3333%;
text-align: center;
box-sizing: border-box;
.image {
display: block;
width: 100%;
height: 33vw;
margin-bottom: 12rpx;
}
.cate-name {
font-size: 28rpx;
color: #555;
}
}
</style>

180
pages/category/components/secondary.vue

@ -1,180 +0,0 @@
<template>
<view v-if="list.length > 0" class="secondary">
<!-- 二级分类 20 -->
<view class="cate-content">
<!-- 左侧 一级分类 -->
<scroll-view class="cate-left" :scroll-y="true" :style="{ height: `${scrollHeight}px` }">
<text class="type-nav" :class="{ selected: curIndex == index }" v-for="(item, index) in list" :key="index"
@click="handleSelectNav(index)">{{ item.name }}</text>
</scroll-view>
<!-- 右侧 二级分类 -->
<scroll-view class="cate-right" :scroll-top="scrollTop" :scroll-y="true" :style="{ height: `${scrollHeight}px` }">
<view v-if="list[curIndex]" class="cate-right-cont">
<view class="cate-two-box">
<view class="cate-cont-box">
<view class="flex-three" v-for="(item, idx) in list[curIndex].children" :key="idx" @click="onTargetGoodsList(item.category_id)">
<view class="cate-img-padding">
<view v-if="item.image" class="cate-img">
<image class="image" mode="scaleToFill" :src="item.image.preview_url"></image>
</view>
</view>
<text class="name oneline-hide">{{ item.name }}</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<empty v-if="!list.length" />
</view>
</template>
<script>
import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
import Empty from '@/components/empty'
import { rpx2px } from '@/utils/util'
export default {
components: {
Empty
},
props: {
//
list: {
type: Array,
default: []
},
},
data() {
return {
//
PageCategoryStyleEnum,
//
scrollHeight: 0,
//
curIndex: 0,
//
scrollTop: 0,
}
},
created() {
//
this.setListHeight()
},
methods: {
//
setListHeight() {
const { windowHeight } = uni.getSystemInfoSync()
this.scrollHeight = windowHeight - rpx2px(96)
},
//
handleSelectNav(index) {
this.curIndex = index
this.scrollTop = 0
},
//
onTargetGoodsList(categoryId) {
this.$navTo('pages/goods/list', { categoryId })
}
}
}
</script>
<style lang="scss" scoped>
//
.cate-content {
display: flex;
z-index: 1;
background: #fff;
padding-top: 96rpx;
}
// + 20
.cate-left {
height: 100%;
display: flex;
flex-direction: column;
flex: 0 0 23%;
background: #f8f8f8;
color: #444;
}
.cate-right {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
.cate-right-cont {
width: 100%;
display: flex;
flex-flow: row wrap;
align-content: flex-start;
padding-top: 15rpx;
.cate-two-box {
width: 100%;
padding: 0 10px;
}
}
}
//
.type-nav {
position: relative;
height: 90rpx;
z-index: 10;
display: block;
font-size: 26rpx;
display: flex;
justify-content: center;
align-items: center;
&.selected {
background: #fff;
color: #fa2209;
border-right: none;
font-size: 28rpx;
}
}
//
.cate-cont-box {
margin-bottom: 30rpx;
padding-bottom: 10rpx;
background: #fff;
overflow: hidden;
.name {
display: block;
padding-bottom: 30rpx;
text-align: center;
font-size: 26rpx;
color: #444444;
}
.cate-img-padding {
padding: 16rpx 16rpx 4rpx 16rpx;
}
.cate-img {
position: relative;
width: 100%;
padding-top: 100%;
.image {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
border-radius: 10rpx;
}
}
}
</style>

165
pages/category/index.vue

@ -1,165 +0,0 @@
<template>
<view class="container">
<!-- 搜索框 -->
<search class="search" tips="搜索商品" @event="$navTo('pages/search/index')" />
<!-- 一级分类 -->
<primary v-if="setting.style == PageCategoryStyleEnum.ONE_LEVEL_BIG.value || setting.style == PageCategoryStyleEnum.ONE_LEVEL_SMALL.value"
:display="setting.style" :list="list" />
<!-- 二级分类 -->
<secondary v-if="setting.style == PageCategoryStyleEnum.TWO_LEVEL.value" :list="list" />
<!-- 分类+商品 -->
<commodity v-if="setting.style == PageCategoryStyleEnum.COMMODITY.value" ref="mescrollItem" :list="list" :setting="setting" />
</view>
</template>
<script>
import MescrollCompMixin from '@/components/mescroll-uni/mixins/mescroll-comp'
import { setCartTabBadge } from '@/core/app'
import SettingKeyEnum from '@/common/enum/setting/Key'
import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
import SettingModel from '@/common/model/Setting'
import * as CategoryApi from '@/api/category'
import Empty from '@/components/empty'
import Search from '@/components/search'
import Primary from './components/primary'
import Secondary from './components/secondary'
import Commodity from './components/commodity'
//
let lastRefreshTime;
export default {
components: {
Search,
Empty,
Primary,
Secondary,
Commodity
},
mixins: [MescrollCompMixin],
data() {
return {
//
PageCategoryStyleEnum,
//
list: [],
//
setting: {},
//
isLoading: true
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad() {
//
this.onRefreshPage()
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 5
const curTime = new Date().getTime()
if ((curTime - lastRefreshTime) > 5 * 60 * 1000) {
this.onRefreshPage()
}
},
methods: {
//
onRefreshPage() {
//
lastRefreshTime = new Date().getTime()
//
this.getPageData()
//
setCartTabBadge()
},
//
getPageData() {
const app = this
app.isLoading = true
Promise.all([
//
// : falsetrue
SettingModel.data(false),
//
CategoryApi.list()
])
.then(result => {
//
app.initSetting(result[0])
//
app.initCategory(result[1])
})
.finally(() => app.isLoading = false)
},
/**
* 初始化分类模板设置
* @param {Object} result
*/
initSetting(setting) {
this.setting = setting[SettingKeyEnum.PAGE_CATEGORY_TEMPLATE.value]
},
/**
* 初始化分类列表数据
* @param {Object} result
*/
initCategory(result) {
this.list = result.data.list
},
},
/**
* 设置分享内容
*/
onShareAppMessage() {
const app = this
return {
title: _this.templet.shareTitle,
path: '/pages/category/index?' + app.$getShareUrlParams()
}
},
/**
* 分享到朋友圈
* 本接口为 Beta 版本暂只在 Android 平台支持详见分享到朋友圈 (Beta)
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
*/
onShareTimeline() {
const app = this
return {
title: _this.templet.shareTitle,
path: '/pages/category/index?' + app.$getShareUrlParams()
}
}
}
</script>
<style>
page {
background: #fff;
}
</style>
<style lang="scss" scoped>
//
.search {
position: fixed;
top: var(--window-top);
left: var(--window-left);
right: var(--window-right);
z-index: 9;
}
</style>

523
pages/checkout/index.vue

@ -1,523 +0,0 @@
<template>
<view class="container p-bottom" v-if="order.goodsList.length">
<!-- 快递配送配送地址 -->
<view v-if="curDelivery == DeliveryTypeEnum.EXPRESS.value" @click="onSelectAddress" class="flow-delivery">
<view class="flow-delivery__detail dis-flex flex-y-center">
<view class="detail-location dis-flex">
<text class="iconfont icon-dingwei"></text>
</view>
<view class="detail-content flex-box">
<block v-if="order.address">
<view class="detail-content__title dis-flex">
<text class="f-30">{{ order.address.name }}</text>
<text class="detail-content__title-phone f-28">{{ order.address.phone }}</text>
</view>
<view class="address detail-content__describe">
<text class="region" v-for="(region, idx) in order.address.region" :key="idx">{{ region }}</text>
<text class="detail">{{ order.address.detail }}</text>
</view>
</block>
<block v-else>
<view class="detail-content__describe dis-flex">
<text class="col-6">请选择配送地址</text>
</view>
</block>
</view>
<view class="detail-arrow dis-flex">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
</view>
<!-- 商品列表 -->
<view class="m-top20">
<view class="checkout_list" v-for="(item, index) in order.goodsList" :key="index">
<view class="flow-shopList dis-flex" data-index="index" @click="onTargetGoods(item.goods_id)">
<!-- 商品图片 -->
<view class="flow-list-left">
<image mode="scaleToFill" :src="item.goods_image"></image>
</view>
<view class="flow-list-right flex-box">
<!-- 商品名称 -->
<text class="goods-name twoline-hide">{{ item.goods_name }}</text>
<!-- 商品规格 -->
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in item.skuInfo.goods_props" :key="idx">
<text class="group-name">{{ props.group.name }}: </text>
<text>{{ props.value.name }}</text>
</view>
</view>
<!-- 商品数量和单价 -->
<view class="flow-list-cont dis-flex flex-x-between flex-y-center">
<text class="small">×{{ item.total_num }}</text>
<text class="flow-cont" :class="[item.is_user_grade ? 'price-delete' : '']">{{ item.goods_price }}</text>
</view>
<!-- 会员折扣价 -->
<view v-if="item.is_user_grade" class="grade-price">
<text>会员折扣价{{ item.grade_goods_price }}</text>
</view>
</view>
</view>
</view>
<view class="flow-num-box b-f">
<text>{{ order.orderTotalNum }}件商品合计</text>
<text class="flow-money col-m">{{ order.orderTotalPrice }}</text>
</view>
</view>
<!-- 商品金额 -->
<view class="flow-all-money b-f m-top20">
<view class="flow-all-list dis-flex">
<text class="flex-five">订单总金额</text>
<view class="flex-five t-r">
<text class="col-m">{{ order.orderTotalPrice }}</text>
</view>
</view>
<!-- 优惠券 -->
<view class="flow-all-list dis-flex">
<text class="flex-five">优惠券</text>
<view class="flex-five t-r">
<view v-if="order.couponList.length > 0" @click="handleShowPopup()">
<text class="col-m" v-if="order.couponId > 0">-{{ order.couponMoney }}</text>
<text class="col-m" v-else>{{ order.couponList.length }}张优惠券</text>
<text class="right-arrow iconfont icon-arrow-right"></text>
</view>
<text v-else class="">无优惠券可用</text>
</view>
</view>
<!-- 积分抵扣 -->
<view v-if="order.isAllowPoints" class="points flow-all-list dis-flex flex-y-center">
<view class="block-left flex-five" @click="handleShowPoints()">
<text class="title">可用{{ setting.points_name }}抵扣</text>
<text class="iconfont icon-help"></text>
</view>
<view class="flex-five dis-flex flex-x-end flex-y-center">
<text class="points-money col-m">-{{ order.pointsMoney }}</text>
<u-switch v-model="isUsePoints" size="48" active-color="#07c160" @change="getOrderData()"></u-switch>
</view>
</view>
<!-- 配送费用 -->
<view v-if="curDelivery == DeliveryTypeEnum.EXPRESS.value" class="dis-flex flow-all-list">
<text class="flex-five">配送费用</text>
<view class="flex-five t-r">
<view v-if="order.address">
<text class="col-m" v-if="order.isIntraRegion">+{{ order.expressPrice }}</text>
<text v-else>不在配送范围</text>
</view>
<view v-else>
<text class="col-7">请先选择配送地址</text>
</view>
</view>
</view>
</view>
<!-- 支付方式 -->
<view class="pay-method flow-all-money b-f m-top20">
<view class="flow-all-list dis-flex">
<text class="flex-five">支付方式</text>
</view>
<!-- 微信支付 -->
<!-- #ifdef MP-WEIXIN -->
<view class="pay-item dis-flex flex-x-between" @click="handleSelectPayType(PayTypeEnum.WECHAT.value)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon wechat">
<text class="iconfont icon-wechat-pay"></text>
</view>
<view class="item-left_text">
<text>{{ PayTypeEnum.WECHAT.name }}</text>
</view>
</view>
<view class="item-right col-m" v-if="curPayType == PayTypeEnum.WECHAT.value">
<text class="iconfont icon-check"></text>
</view>
</view>
<!-- #endif -->
<!-- 余额支付 -->
<view class="pay-item dis-flex flex-x-between" @click="handleSelectPayType(PayTypeEnum.BALANCE.value)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon balance">
<text class="iconfont icon-balance-pay"></text>
</view>
<view class="item-left_text">
<text>{{ PayTypeEnum.BALANCE.name }}</text>
</view>
<view class="user-balance">
<text>(可用{{ personal.balance }})</text>
</view>
</view>
<view class="item-right col-m" v-if="curPayType == PayTypeEnum.BALANCE.value">
<text class="iconfont icon-check"></text>
</view>
</view>
</view>
<!-- 买家留言 -->
<view class="flow-all-money b-f m-top20">
<view class="ipt-wrapper dis-flex flow-all-list">
<input v-model="remark" placeholder="选填:买家留言(50字以内)"></input>
</view>
</view>
<!-- 提交订单 -->
<view class="flow-fixed-footer b-f m-top10">
<view class="dis-flex chackout-box">
<view class="chackout-left pl-12">实付款
<text class="col-m">{{ order.orderPayPrice }}</text>
</view>
<view class="chackout-right" @click="onSubmitOrder()">
<view class="flow-btn f-32" :class="{ disabled }">提交订单</view>
</view>
</view>
</view>
<!-- 积分说明弹窗 -->
<u-modal v-model="showPoints" :title="`${setting.points_name}说明`">
<scroll-view class="points-content" :scroll-y="true">
<text>{{ setting.points_describe }}</text>
</scroll-view>
</u-modal>
<!-- 优惠券弹出框 -->
<u-popup v-model="showPopup" mode="bottom">
<view class="popup__coupon">
<view class="coupon__title f-30">选择优惠券</view>
<!-- 优惠券列表 -->
<view class="coupon-list">
<scroll-view :scroll-y="true" style="height: 565rpx;">
<view class="coupon-item" v-for="(item, index) in order.couponList" :key="index">
<view class="item-wrapper"
:class="[item.is_apply ? 'color-' + CouponColors[index % CouponColors.length] : 'color-gray']"
@click="handleSelectCoupon(index)">
<view class="coupon-type">{{ CouponTypeEnum[item.coupon_type].name }}</view>
<view class="tip dis-flex flex-dir-column flex-x-center">
<view v-if="item.coupon_type == CouponTypeEnum.FULL_DISCOUNT.value">
<text class="f-30"></text>
<text class="money">{{ item.reduce_price }}</text>
</view>
<text class="money"
v-if="item.coupon_type == CouponTypeEnum.DISCOUNT.value">{{ item.discount }}</text>
<text class="pay-line">{{ item.min_price }}元可用</text>
</view>
<view class="split-line"></view>
<view class="content dis-flex flex-dir-column flex-x-between">
<view class="title">{{ item.name }}</view>
<view class="bottom dis-flex flex-y-center">
<view class="time flex-box">
<block v-if="item.start_time === item.end_time">{{ item.start_time }}</block>
<block v-else>{{ item.start_time }}~{{ item.end_time }}</block>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 不使用优惠券 -->
<view class="coupon__do_not dis-flex flex-y-center flex-x-center">
<view class="control dis-flex flex-y-center flex-x-center" @click="handleNotUseCoupon()">
<text class="f-26">不使用优惠券</text>
</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import * as Verify from '@/utils/verify'
import * as CheckoutApi from '@/api/checkout'
import { CouponTypeEnum } from '@/common/enum/coupon'
import DeliveryTypeEnum from '@/common/enum/order/DeliveryType'
import PayTypeEnum from '@/common/enum/order/PayType'
import { wxPayment } from '@/core/app'
const CouponColors = ['red', 'blue', 'violet', 'yellow']
export default {
data() {
return {
//
DeliveryTypeEnum,
PayTypeEnum,
CouponTypeEnum,
//
options: {},
//
CouponColors,
//
curDelivery: null,
//
curPayType: PayTypeEnum.BALANCE.value,
//
selectCouponId: 0,
// 使
isUsePoints: false,
//
remark: '',
// submit
disabled: false,
//
showPoints: false,
//
showPopup: false,
//
disabled: false,
// (api)
order: {
//
goodsList: [],
//
couponList: [],
//
existAddress: false,
//
address: null,
//
existAddress: false,
//
isIntraRegion: true,
//
hasError: false,
//
errorMsg: ''
},
//
personal: {},
//
setting: {}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.options = options
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
//
this.getOrderData()
},
methods: {
//
getOrderData() {
const app = this
//
const params = {
delivery: app.curDelivery || 0,
couponId: app.selectCouponId || 0,
isUsePoints: app.isUsePoints ? 1 : 0,
}
// api
CheckoutApi.order(app.options.mode, app.getRequestParam())
.then(result => {
app.initData(result.data)
})
.catch(err => err)
},
//
initData({ order, setting, personal }) {
const app = this
app.order = order
app.personal = personal
app.setting = setting
//
if (order.hasError) {
app.$toast(order.errorMsg)
}
//
app.curDelivery = order.delivery
//
app.isShowTab = setting.deliveryType.length > 1
// (使)
if (app.$platform === 'MP-WEIXIN') {
app.curPayType = PayTypeEnum.WECHAT.value
}
},
// api
getRequestParam() {
const app = this
const { options } = app
//
const modeParam = {}
// :
if (options.mode === 'buyNow') {
modeParam.goodsId = options.goodsId
modeParam.goodsNum = options.goodsNum
modeParam.goodsSkuId = options.goodsSkuId
}
// :
if (options.mode === 'cart') {
modeParam.cartIds = options.cartIds
}
// ()
const orderParam = {
delivery: app.curDelivery || 0,
couponId: app.selectCouponId || 0,
isUsePoints: app.isUsePoints ? 1 : 0,
}
return { ...orderParam, ...modeParam }
},
//
handleShowPoints() {
this.showPoints = true
},
//
handleShowPopup() {
this.showPopup = true
},
//
handleSelectCoupon(index) {
const app = this
const { couponList } = app.order
//
const couponItem = couponList[index]
//
if (!couponItem.is_apply) {
app.$toast(couponItem.not_apply_info)
return
}
// id
app.selectCouponId = couponItem.user_coupon_id
//
app.getOrderData()
//
app.showPopup = false
},
// 使
handleNotUseCoupon() {
const app = this
app.selectCouponId = 0
//
app.getOrderData()
//
app.showPopup = false
},
//
handleSelectPayType(value) {
this.curPayType = value
},
//
onSelectAddress() {
this.$navTo('pages/address/index', { from: 'checkout' })
},
//
onTargetGoods(goodsId) {
this.$navTo('pages/goods/detail', { goodsId })
},
//
onSubmitOrder() {
const app = this
if (app.disabled) {
return false
}
//
if (!app.onVerifyFrom()) {
return false
}
//
app.disabled = true
// api
CheckoutApi.submit(app.options.mode, app.getFormData())
.then(result => app.onSubmitCallback(result))
.catch(err => {
if (err.result) {
const errData = err.result.data
if (errData.is_created) {
app.navToMyOrder()
return false
}
}
app.disabled = false
})
},
//
onSubmitCallback(result) {
const app = this
//
if (result.data.payType == PayTypeEnum.WECHAT.value) {
wxPayment(result.data.payment)
.then(() => app.$success('支付成功'))
.catch(err => app.$error('订单未支付'))
.finally(() => {
app.disabled = false
app.navToMyOrder()
})
}
//
if (result.data.payType == PayTypeEnum.BALANCE.value) {
app.$success('支付成功')
app.disabled = false
app.navToMyOrder()
}
},
// (1)
navToMyOrder() {
setTimeout(() => {
this.$navTo('pages/order/index')
}, 1000)
},
//
getFormData() {
const app = this
const { options } = app
//
const form = {
delivery: app.curDelivery,
payType: app.curPayType,
couponId: app.selectCouponId || 0,
isUsePoints: app.isUsePoints ? 1 : 0,
remark: app.remark || '',
}
// -
if (options.mode === 'buyNow') {
form.goodsId = options.goodsId
form.goodsNum = options.goodsNum
form.goodsSkuId = options.goodsSkuId
}
// -
if (options.mode === 'cart') {
form.cartIds = options.cartIds || null
}
return form
},
//
onVerifyFrom() {
const app = this
if (app.hasError) {
app.$toast(app.errorMsg)
return false
}
return true
},
}
}
</script>
<style lang="scss" scoped>
@import "./style.scss";
</style>

445
pages/checkout/style.scss

@ -1,445 +0,0 @@
// 配送信息
.flow-delivery {
padding: 34rpx 30rpx;
background: #fff url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANYAAAANCAYAAADVGpDCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3Yjk4M2ExYy1jMDhkLTQ1OTktYTI0Ny1kZjNjYzdiYTQ5ZTgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDQwNkY3RkU5N0NGMTFFNUI3N0M4NTU4MzM2RjlFODIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDQwNkY3RkQ5N0NGMTFFNUI3N0M4NTU4MzM2RjlFODIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowNzgwZWI1NS03OGFhLTQzOTUtODQ4OC1lOWI5YmVlYTY1ZDciIHN0UmVmOmRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1OTRiYzUyMy1jMzc3LTExNzgtYTdkZS04NGY3YmM1ZGIxMDMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz556PLxAAACBElEQVR42tyaSyhEYRTHP48imlKibDQeSSlkSlEWLCRFsZNH5FE2FqQ8ErIRC9lIkTwXSpMkWWChhEJCSnlkoUZGSsr78f98n43CMFPu/Z/6NZuZ2zn33/+cb869XkmLx8IDEQaGQJbgiytQDSY3MyL+LYnL/HxPXSoHDIJQQq2WQQk4Dbbb/yUB29LJ+6e3B66VB3ZITbUIEqSpCGoJBP1ghtBUD6ARpEtTGSEhXzd+awE9oJzQUPegWdf3QlBPMhgDMYRa7YNisGWkpP5qrBQtVBShUHugUE9hs4fUtwG0utlEjRivoA/Ug1sj3vjffr8FNJEK1auPFHcE9UTq5pdK2PwcoAzMG7mjuRrRYEIfK9jiDJSCBZJ6ynSTsBBqNQ0qgdPISbq6vJCFbJOaagrEk5gqWNczRGiqG1Ah1LLMafRkf5pYIUKtZnMJDXUNasAIST2ZYFioRx9ssQaKwJFZEv5uYmWDXVJTrYBEElP562PfPKGpnkAbSDOTqb6aWAGgW6iHol5kQj2CdtAJngnqkc1hHMQRNr9DPaXWzZj8Z2PZtFCxhEIdaKE2CGqRJ4060AH8CLUaALX6f5VpBZLhI9SaeZXQVHKNLt84SCIxVbhQi5YuQlNd6OVElZlN9TGxrGBUn2PZ4lyoTdIsST0FQj0UDSLUak6ot3gcBLVY3wQYAJoVXxmNERajAAAAAElFTkSuQmCC') bottom left repeat-x;
background-size: 120rpx auto;
.detail-location {
font-size: 36rpx;
}
.detail-content {
padding: 0 20rpx;
.detail-content__title-phone {
margin-left: 10rpx;
}
.detail-content__describe {
font-size: 28rpx;
color: #777;
}
}
.detail-content__title {
margin-bottom: 6rpx;
}
}
// 买家留言
.flow-all-money {
.ipt-wrapper {
input {
font-size: 28rpx;
width: 100%;
height: 75rpx;
}
}
}
// 商品列表
.checkout_list {
padding: 20rpx 30rpx 4rpx 30rpx;
background: #fff;
border-bottom: 1rpx solid rgb(248, 248, 248);
.flow-shopList {
padding: 5rpx 0 10rpx;
border-bottom: 1rpx solid rgb(248, 248, 248);
&:last-child {
border-bottom: 0;
}
}
}
.flow-header-left {
padding-left: 90rpx;
}
/* 会员价 */
.flow-shopList {
.flow-list-right {
.flow-cont {
&.price-delete {
font-size: 26rpx;
color: #777;
text-decoration: line-through;
}
}
}
.grade-price {
padding-top: 8rpx;
font-size: 28rpx;
color: #ff495e;
text-align: right;
}
.goods-name{
font-size: 28rpx;
color: #333;
}
}
/* 优惠券选择 */
.popup__coupon {
width: 750rpx;
background: #fff;
box-sizing: border-box;
padding: 30rpx;
.coupon__do_not {
.control {
width: 90%;
height: 72rpx;
margin-bottom: 24rpx;
color: #888;
border: 1rpx solid #e3e3e3;
border-radius: 10rpx;
/* #ifdef H5 */
max-width: 1120rpx;
/* #endif */
}
}
.coupon__title {
text-align: center;
margin-bottom: 30rpx;
}
.coupon-list {
/* #ifdef H5 */
max-width: 1120rpx;
margin: 0 auto;
/* #endif */
}
.coupon-item {
overflow: hidden;
margin-bottom: 22rpx;
}
.item-wrapper {
width: 100%;
display: flex;
background: #fff;
border-radius: 8rpx;
color: #fff;
height: 180rpx;
.coupon-type {
position: absolute;
top: 0;
right: 0;
z-index: 10;
width: 128rpx;
padding: 6rpx 0;
background: #a771ff;
font-size: 20rpx;
text-align: center;
color: #ffffff;
transform: rotate(45deg);
transform-origin: 64rpx 64rpx;
}
&.color-blue {
background: linear-gradient(-125deg, #57bdbf, #2f9de2);
}
&.color-red {
background: linear-gradient(-128deg, #ff6d6d, #ff3636);
}
&.color-violet {
background: linear-gradient(-113deg, #ef86ff, #b66ff5);
.coupon-type {
background: #55b5ff;
}
}
&.color-yellow {
background: linear-gradient(-141deg, #f7d059, #fdb054);
}
&.color-gray {
background: linear-gradient(-113deg, #bdbdbd, #a2a1a2);
.coupon-type {
background: #9e9e9e;
}
}
.content {
flex: 1;
padding: 30rpx 20rpx;
border-radius: 16rpx 0 0 16rpx;
.title {
font-size: 32rpx;
}
.bottom {
.time {
font-size: 24rpx;
}
.receive {
height: 46rpx;
width: 122rpx;
line-height: 46rpx;
text-align: center;
border: 1rpx solid #fff;
border-radius: 30rpx;
color: #fff;
font-size: 24rpx;
&.state {
border: none;
}
}
}
}
.tip {
position: relative;
flex: 0 0 32%;
text-align: center;
border-radius: 0 16rpx 16rpx 0;
.money {
font-weight: bold;
font-size: 52rpx;
}
.pay-line {
font-size: 22rpx;
}
}
.split-line {
position: relative;
flex: 0 0 0;
border-left: 4rpx solid #fff;
margin: 0 10rpx 0 6rpx;
background: #fff;
&:before,
{
border-radius: 0 0 16rpx 16rpx;
top: 0;
}
&:after {
border-radius: 16rpx 16rpx 0 0;
bottom: 0;
}
&:before,
&:after {
content: '';
position: absolute;
width: 24rpx;
height: 12rpx;
background: #f7f7f7;
left: -14rpx;
z-index: 1;
}
}
}
}
/* 积分抵扣 */
.points {
.title {
margin-right: 5rpx;
}
.icon-help {
font-size: 28rpx;
}
.points-money {
margin-right: 20rpx;
}
}
/* 支付方式 */
.pay-method {
.pay-item {
padding: 24rpx 0;
font-size: 28rpx;
border-bottom: 1rpx solid rgb(248, 248, 248);
.item-left_icon {
margin-right: 20rpx;
font-size: 32rpx;
&.wechat {
color: #00c800;
}
&.balance {
color: #ff9700;
}
}
}
.user-balance {
margin-left: 20rpx;
font-size: 26rpx;
// color: #464646;
}
}
// 商品规格
.goods-props {
padding-top: 10rpx;
font-size: 24rpx;
color: #999;
.goods-props-item {
float: left;
.group-name {
margin-right: 6rpx;
}
}
}
// 右侧箭头
.right-arrow {
margin-left: 16rpx;
// color: #777;
font-size: 26rpx;
}
// 底部操作栏
.flow-fixed-footer {
position: fixed;
bottom: var(--window-bottom);
left: var(--window-left);
right: var(--window-right);
// width: 100%;
background: #fff;
border-top: 1px solid #eee;
z-index: 11;
// 设置ios刘海屏底部横线安全区域
padding-bottom: calc(constant(safe-area-inset-bottom) + var(--window-bottom));
padding-bottom: calc(env(safe-area-inset-bottom) + var(--window-bottom));
.chackout-left {
font-size: 28rpx;
line-height: 92rpx;
color: #777;
flex: 4;
padding-left: 12px;
}
.chackout-right {
font-size: 34rpx;
flex: 2;
}
// 提交按钮
.flow-btn {
background: linear-gradient(to right, #f9211c, #ff6335);
color: #fff;
text-align: center;
line-height: 92rpx;
display: block;
font-size: 28rpx;
// 禁用按钮
&.disabled {
background: #ff9779;
}
}
}
// 积分说明
.points-content {
padding: 30rpx 48rpx;
font-size: 28rpx;
line-height: 50rpx;
text-align: left;
color: #606266;
height: 620rpx;
box-sizing: border-box;
}
/* 共几件商品 */
.flow-num-box {
font-size: 28rpx;
color: #777;
padding: 16rpx 24rpx;
text-align: right;
}
/* app.scss */
.flow-shopList {
padding: 18rpx 0;
.flow-list-left {
margin-right: 20rpx;
image {
width: 180rpx;
height: 180rpx;
border: 1rpx solid #eee;
background: #fff;
}
}
.flow-list-right {
.flow-cont {
font-size: 28rpx;
color: #fa2209;
}
.small {
font-size: 26rpx;
color: #777;
}
.flow-list-cont {
padding-top: 10rpx;
}
}
}
.flow-all-money {
padding: 0 24rpx;
color: #444;
.flow-all-list {
font-size: 28rpx;
padding: 20rpx 0;
border-bottom: 1rpx solid rgb(248, 248, 248);
}
.flow-all-list:last-child {
border-bottom: none;
}
.flow-all-list-cont {
font-size: 28rpx;
padding: 10rpx 0;
}
.flow-arrow {
justify-content: flex-end;
align-items: center;
}
}

274
pages/comment/index.vue

@ -1,274 +0,0 @@
<template>
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption" @up="upCallback">
<!-- tab栏 -->
<u-tabs :list="tabs" :is-scroll="false" :current="curTab" active-color="#FA2209" :duration="0.2" @change="onChangeTab" />
<!-- 商品评价列表 -->
<view class="comment-list">
<view class="comment-item" v-for="(item, index) in list.data" :key="index">
<view class="item-head">
<!-- 用户信息 -->
<view class="user-info">
<avatar-image class="user-avatar" :url="item.user.avatar_url" :width="50" />
<text class="user-name f-26">{{ item.user.nick_name }}</text>
</view>
<!-- 评星 -->
<u-rate active-color="#f4a213" :current="rates[item.score]" :disabled="true" />
<!-- 评价日期-->
<view class="flex-box f-22 col-9 t-r">{{ item.create_time }}</view>
</view>
<!-- 评价内容 -->
<view class="item-content m-top20">
<text class="f-26">{{ item.content }}</text>
</view>
<!-- 评价图片 -->
<view class="images-list clearfix" v-if="item.images.length">
<view class="image-preview" v-for="(image, imgIdx) in item.images" :key="imgIdx">
<image class="image" mode="aspectFill" :src="image.image_url" @click="onPreviewImages(index, imgIdx)"></image>
</view>
</view>
<!-- 商品规格 -->
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in item.orderGoods.goods_props" :key="idx">
<text class="group-name">{{ props.group.name }}: </text>
<text>{{ props.value.name }}</text>
</view>
</view>
</view>
</view>
</mescroll-body>
</template>
<script>
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import AvatarImage from '@/components/avatar-image'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
import * as CommentApi from '@/api/comment'
const pageSize = 15
const tabs = [{
name: `全部`,
scoreType: -1
}, {
name: `好评`,
scoreType: 10
}, {
name: `中评`,
scoreType: 20
}, {
name: `差评`,
scoreType: 30
}]
export default {
components: {
MescrollBody,
AvatarImage
},
mixins: [MescrollMixin],
data() {
return {
// ID
goodsId: null,
//
curTab: 0,
//
list: getEmptyPaginateObj(),
//
total: { all: 0, negative: 0, praise: 0, review: 0 },
//
rates: { 10: 5, 20: 3, 30: 1 },
//
tabs,
//
upOption: {
//
auto: true,
// ; 10
page: { size: pageSize },
// 4
noMoreSize: 4,
//
empty: {
tip: '亲,暂无相关商品评价'
}
},
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
//
this.goodsId = options.goodsId
//
this.getTotal()
},
methods: {
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
//
app.getCommentList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
//
getCommentList(pageNo = 1) {
const app = this
return new Promise((resolve, reject) => {
CommentApi.list(app.goodsId, { scoreType: app.getScoreType(), page: pageNo }, { load: false })
.then(result => {
//
const newList = result.data.list
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
})
},
//
getScoreType() {
return this.tabs[this.curTab].scoreType
},
//
getTotal() {
const app = this
CommentApi.total(app.goodsId)
.then(result => {
// tab
const total = result.data.total
app.getTabs(total)
})
},
// tab
getTabs(total) {
const tabs = this.tabs
tabs[0].name = `全部(${total.all})`
tabs[1].name = `好评(${total.praise})`
tabs[2].name = `中评(${total.review})`
tabs[3].name = `差评(${total.negative})`
},
//
onChangeTab(index) {
const app = this
//
app.curTab = index
//
app.onRefreshList()
},
//
onRefreshList() {
this.list = getEmptyPaginateObj()
setTimeout(() => {
this.mescroll.resetUpScroll()
}, 120)
},
//
onPreviewImages(dataIdx, imgIndex) {
const app = this
const images = app.list.data[dataIdx].images
const imageUrls = images.map(item => item.image_url)
uni.previewImage({
current: imageUrls[imgIndex],
urls: imageUrls
})
}
}
}
</script>
<style lang="scss" scoped>
.comment-item {
padding: 30rpx;
box-sizing: border-box;
border-bottom: 1rpx solid #f7f7f7;
background: #fff;
}
.item-head {
display: flex;
align-items: center;
//
.user-info {
margin-right: 15rpx;
display: flex;
align-items: center;
.user-avatar {
margin-right: 15rpx;
}
.user-name {
color: #999;
}
}
}
//
.item-content {
font-size: 30rpx;
color: #333;
margin: 16rpx 0;
}
//
.images-list {
&::after {
clear: both;
content: " ";
display: table;
}
.image-preview {
float: left;
margin-bottom: 15rpx;
margin-right: 15rpx;
&:nth-child(3n+0) {
margin-right: 0;
}
.image {
display: block;
width: 220rpx;
height: 220rpx;
}
}
}
//
.goods-props {
font-size: 24rpx;
color: #999;
.goods-props-item {
float: left;
.group-name {
margin-right: 6rpx;
}
}
}
</style>

250
pages/coupon/index.vue

@ -1,250 +0,0 @@
<template>
<view class="container">
<view v-if="list.length" class="coupon-list">
<view class="coupon-item" v-for="(item, index) in list" :key="index">
<view class="item-wrapper"
:class="[ item.state.value ? 'color-' + color[index % color.length] : 'color-gray' ]">
<view class="coupon-type">{{ CouponTypeEnum[item.coupon_type].name }}</view>
<view class="tip dis-flex flex-dir-column flex-x-center">
<view v-if="item.coupon_type == CouponTypeEnum.FULL_DISCOUNT.value">
<text class="f-30"></text>
<text class="money">{{ item.reduce_price }}</text>
</view>
<text class="money" v-if="item.coupon_type == CouponTypeEnum.DISCOUNT.value">{{ item.discount }}</text>
<text class="pay-line">{{ item.min_price }}元可用</text>
</view>
<view class="split-line"></view>
<view class="content dis-flex flex-dir-column flex-x-between">
<view class="title oneline-hide">{{ item.name }}</view>
<view class="bottom dis-flex flex-y-center">
<view class="time flex-box">
<text v-if="item.expire_type == 10">领取{{ item.expire_day }}天内有效</text>
<text v-if="item.expire_type == 20">
<block v-if="item.start_time === item.end_time">{{ item.start_time }} 当天有效</block>
<block v-else>{{ item.start_time }}~{{ item.end_time }}</block>
</text>
</view>
<view class="receive" v-if="item.state.value" @click="receive(item.coupon_id)">
<text>立即领取</text>
</view>
<view v-else class="receive state">
<text>{{ item.state.text }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<empty v-if="!list.length" :isLoading="isLoading" />
</view>
</template>
<script>
import * as CouponApi from '@/api/coupon'
import * as MyCouponApi from '@/api/myCoupon'
import { CouponTypeEnum } from '@/common/enum/coupon'
import Empty from '@/components/empty'
const color = ['red', 'blue', 'violet', 'yellow']
export default {
components: {
Empty
},
data() {
return {
//
CouponTypeEnum,
//
color,
//
list: [],
//
isLoading: true
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
//
this.getCouponList()
},
methods: {
/**
* 获取优惠券列表
* @param {bool} load 是否显示loading弹窗
*/
getCouponList(load = true) {
const app = this
app.isLoading = true
CouponApi.list({}, { load })
.then(result => {
app.list = result.data.list
})
.finally(() => app.isLoading = false)
},
//
receive(couponId) {
const app = this
MyCouponApi.receive(couponId)
.then(result => {
//
app.$success(result.message)
//
app.getCouponList(false)
})
}
}
}
</script>
<style lang="scss" scoped>
.coupon-list {
padding: 20rpx;
}
.coupon-item {
position: relative;
overflow: hidden;
margin-bottom: 22rpx;
}
.item-wrapper {
width: 100%;
display: flex;
background: #fff;
border-radius: 8rpx;
color: #fff;
height: 180rpx;
.coupon-type {
position: absolute;
top: 0;
right: 0;
z-index: 10;
width: 128rpx;
padding: 3px 0;
background: #a771ff;
font-size: 20rpx;
text-align: center;
color: #ffffff;
transform: rotate(45deg);
transform-origin: 64rpx 64rpx;
}
&.color-blue {
background: linear-gradient(-125deg, #57bdbf, #2f9de2);
}
&.color-red {
background: linear-gradient(-128deg, #ff6d6d, #ff3636);
}
&.color-violet {
background: linear-gradient(-113deg, #ef86ff, #b66ff5);
.coupon-type {
background: #55b5ff;
}
}
&.color-yellow {
background: linear-gradient(-141deg, #f7d059, #fdb054);
}
&.color-gray {
background: linear-gradient(-113deg, #bdbdbd, #a2a1a2);
.coupon-type {
background: #9e9e9e;
}
}
.content {
flex: 1;
padding: 30rpx 20rpx;
border-radius: 8px 0 0 8px;
.title {
width: 400rpx;
font-size: 32rpx;
}
.bottom {
.time {
font-size: 24rpx;
}
.receive {
height: 46rpx;
width: 122rpx;
line-height: 46rpx;
text-align: center;
border: 1px solid #fff;
border-radius: 30rpx;
color: #fff;
font-size: 24rpx;
&.state {
border: none;
}
}
}
}
.tip {
position: relative;
flex: 0 0 32%;
text-align: center;
border-radius: 0 8px 8px 0;
.money {
font-weight: bold;
font-size: 52rpx;
}
.pay-line {
font-size: 22rpx;
}
}
.split-line {
position: relative;
flex: 0 0 0;
border-left: 4rpx solid #fff;
margin: 0 5px 0 3px;
background: #fff;
&:before,
{
border-radius: 0 0 16rpx 16rpx;
top: 0;
}
&:after {
border-radius: 16rpx 16rpx 0 0;
bottom: 0;
}
&:before,
&:after {
content: '';
position: absolute;
width: 24rpx;
height: 12rpx;
background: #f7f7f7;
left: -14rpx;
z-index: 1;
}
}
}
</style>

120
pages/custom/index.vue

@ -1,120 +0,0 @@
<template>
<view class="container">
<!-- 店铺页面组件 -->
<Page :items="items" />
</view>
</template>
<script>
import * as Api from '@/api/page'
import Page from '@/components/page'
const App = getApp()
export default {
components: {
Page
},
data() {
return {
//
options: {},
//
page: {},
//
items: []
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
//
this.options = options
//
this.getPageData()
},
methods: {
/**
* 加载页面数据
* @param {Object} callback
*/
getPageData(callback) {
const app = this
const pageId = app.options.pageId || 0
Api.detail(pageId)
.then(result => {
//
const { data: { pageData } } = result
app.page = pageData.page
app.items = pageData.items
//
app.setPageBar();
})
.finally(() => callback && callback())
},
/**
* 设置顶部导航栏
*/
setPageBar() {
const { page } = this
//
uni.setNavigationBarTitle({
title: page.params.title
})
// navbar
uni.setNavigationBarColor({
frontColor: page.style.titleTextColor === 'white' ? '#ffffff' : '#000000',
backgroundColor: page.style.titleBackgroundColor
})
}
},
/**
* 下拉刷新
*/
onPullDownRefresh() {
//
this.getPageData(() => {
uni.stopPullDownRefresh()
})
},
/**
* 分享当前页面
*/
onShareAppMessage() {
const app = this
const { page } = app
return {
title: page.params.share_title,
path: "/pages/index/index?" + app.$getShareUrlParams()
}
},
/**
* 分享到朋友圈
* 本接口为 Beta 版本暂只在 Android 平台支持详见分享到朋友圈 (Beta)
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
*/
onShareTimeline() {
const app = this
const { page } = app
return {
title: page.params.share_title,
path: "/pages/index/index?" + app.$getShareUrlParams()
}
}
}
</script>
<style lang="scss" scoped>
.container {
background: #fff;
}
</style>

162
pages/goods/components/Comment.vue

@ -1,162 +0,0 @@
<template>
<!-- 商品评价 -->
<view v-if="!isLoading && list.length" class="goods-comment m-top20">
<view class="item-title dis-flex">
<view class="block-left flex-box">
商品评价 (<text class="total">{{ total }}</text>)
</view>
<view class="block-right">
<text @click="onTargetToComment" class="show-more col-9">查看更多</text>
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
<!-- 评论列表 -->
<view class="comment-list">
<view class="comment-item" v-for="(item, index) in list" :key="index">
<view class="comment-item_row dis-flex flex-y-center">
<view class="user-info dis-flex flex-y-center">
<avatar-image class="user-avatar" :url="item.user.avatar_url" :width="50" />
<text class="user-name">{{ item.user.nick_name }}</text>
</view>
<!-- 评星 -->
<view class="star-rating">
<u-rate active-color="#f4a213" :current="rates[item.score]" :disabled="true" />
</view>
</view>
<view class="item-content m-top20">
<text class="f-26 twoline-hide">{{ item.content }}</text>
</view>
<view class="comment-time">{{ item.create_time }}</view>
</view>
</view>
</view>
</template>
<script>
import AvatarImage from '@/components/avatar-image'
import * as CommentApi from '@/api/comment'
export default {
components: {
AvatarImage
},
props: {
// ID
goodsId: {
type: Number,
default: null
},
// 2
limit: {
type: Number,
default: 2
}
},
data() {
return {
//
isLoading: true,
//
rates: { 10: 5, 20: 3, 30: 1 },
//
list: [],
//
total: 0
}
},
created() {
//
this.getCommentList()
},
methods: {
//
getCommentList() {
const app = this
app.isLoading = true
CommentApi.listRows(app.goodsId, app.limit)
.then(result => {
app.list = result.data.list
app.total = result.data.total
})
.catch(err => err)
.finally(() => app.isLoading = false)
},
//
onTargetToComment() {
const app = this
app.$navTo('pages/comment/index', { goodsId: app.goodsId })
}
}
}
</script>
<style lang="scss" scoped>
.goods-comment {
padding: 20rpx 30rpx;
background-color: #fff;
}
.item-title {
font-size: 28rpx;
margin-bottom: 25rpx;
.total {
margin: 0 4rpx;
}
.show-more {
margin-right: 8rpx;
font-size: 24rpx;
}
}
.comment-item {
padding: 15rpx 5rpx;
margin-bottom: 10rpx;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
margin-bottom: 0;
border-bottom: none;
}
.comment-item_row {
margin-bottom: 10rpx;
}
}
.user-info {
margin-right: 15rpx;
.user-avatar {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.user-name {
font-size: 24rpx;
}
}
.item-content {
color: #333;
margin: 16rpx 0;
max-height: 76rpx;
line-height: 38rpx;
}
.comment-time {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
</style>

158
pages/goods/components/Service.vue

@ -1,158 +0,0 @@
<template>
<view v-if="list.length" class="service-wrapper">
<!-- 服务简述 -->
<view class="service-simple" @click="handlePopup">
<view class="s-list">
<view class="s-item" v-for="(item, index) in list" :key="index">
<text class="item-icon iconfont icon-fuwu"></text>
<text class="item-val">{{ item.name }}</text>
</view>
</view>
<!-- 扩展箭头 -->
<view class="s-arrow f-26 col-9 t-r">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
<!-- 详情内容弹窗 -->
<u-popup v-model="showPopup" mode="bottom" :closeable="true" :border-radius="26">
<view class="service-content">
<view class="title">服务</view>
<scroll-view class="content-scroll" :scroll-y="true">
<view class="s-list clearfix">
<view class="s-item" v-for="(item, index) in list" :key="index">
<text class="item-icon iconfont icon-fuwu"></text>
<view class="item-val">{{ item.name }}</view>
<view class="item-summary">{{ item.summary }}</view>
</view>
</view>
</scroll-view>
</view>
</u-popup>
</view>
</template>
<script>
import * as ServiceApi from '@/api/goods/service'
export default {
props: {
// ID
goodsId: {
type: Number,
default: null
}
},
data() {
return {
//
isLoading: true,
//
showPopup: false,
//
list: []
}
},
created() {
//
this.getServiceList()
},
methods: {
//
getServiceList() {
const app = this
app.isLoading = true
ServiceApi.list(app.goodsId)
.then(result => app.list = result.data.list)
.finally(() => app.isLoading = false)
},
//
handlePopup() {
this.showPopup = !this.showPopup
}
}
}
</script>
<style lang="scss" scoped>
.service-wrapper {
min-height: 24rpx;
margin-bottom: -24rpx;
}
//
.service-simple {
padding: 24rpx 30rpx;
display: flex;
align-items: center;
.s-list {
flex: 1;
margin-left: -15rpx;
}
.s-item {
float: left;
font-size: 26rpx;
margin: 8rpx 15rpx;
.item-icon {
color: #FA2209;
}
.item-val {
margin-left: 12rpx;
}
}
}
//
.service-content {
padding: 24rpx;
.title {
font-size: 30rpx;
margin-bottom: 50rpx;
font-weight: bold;
text-align: center;
}
.content-scroll {
min-height: 400rpx;
max-height: 760rpx;
}
.s-list {
padding: 0 30rpx 0 80rpx;
}
.s-item {
position: relative;
margin-bottom: 60rpx;
.item-icon {
position: absolute;
top: 6rpx;
left: -50rpx;
color: #FA2209;
}
.item-val {
font-size: 28rpx;
}
.item-summary {
font-size: 26rpx;
margin-top: 20rpx;
color: #6d6d6d;
}
}
}
</style>

173
pages/goods/components/SkuPopup.vue

@ -1,173 +0,0 @@
<template>
<goods-sku-popup :value="value" @input="onChangeValue" border-radius="20" :localdata="goodsInfo" :mode="skuMode"
:maskCloseAble="true" @open="openSkuPopup" @close="closeSkuPopup" @add-cart="addCart" @buy-now="buyNow"
buyNowText="立即购买" />
</template>
<script>
import { setCartTotalNum } from '@/core/app'
import * as CartApi from '@/api/cart'
import GoodsSkuPopup from '@/components/goods-sku-popup'
export default {
components: {
GoodsSkuPopup
},
model: {
prop: 'value',
event: 'input'
},
props: {
// true false
value: {
Type: Boolean,
default: false
},
// 1: 2: 3:
skuMode: {
type: Number,
default: 1
},
//
goods: {
type: Object,
default: {}
}
},
data() {
return {
goodsInfo: {}
}
},
created() {
const app = this
const { goods } = app
app.goodsInfo = {
_id: goods.goods_id,
name: goods.goods_name,
goods_thumb: goods.goods_image,
sku_list: app.getSkuList(),
spec_list: app.getSpecList()
}
},
methods: {
//
onChangeValue(val) {
this.$emit('input', val)
},
// SKU
getSkuList() {
const app = this
const { goods: { goods_name, goods_image, skuList } } = app
const skuData = []
skuList.forEach(item => {
skuData.push({
_id: item.id,
goods_sku_id: item.goods_sku_id,
goods_id: item.goods_id,
goods_name: goods_name,
image: item.image_url ? item.image_url : goods_image,
price: item.goods_price * 100,
stock: item.stock_num,
spec_value_ids: item.spec_value_ids,
sku_name_arr: app.getSkuNameArr(item.spec_value_ids)
})
})
return skuData
},
// sku
getSkuNameArr(specValueIds) {
const app = this
const defaultData = ['默认']
const skuNameArr = []
if (specValueIds) {
specValueIds.forEach((valueId, groupIndex) => {
const specValueName = app.getSpecValueName(valueId, groupIndex)
skuNameArr.push(specValueName)
})
}
return skuNameArr.length ? skuNameArr : defaultData
},
//
getSpecValueName(valueId, groupIndex) {
const app = this
const { goods: { specList } } = app
const res = specList[groupIndex].valueList.find(specValue => {
return specValue.spec_value_id == valueId
})
return res.spec_value
},
//
getSpecList() {
const { goods: { specList } } = this
const defaultData = [{ name: '默认', list: [{ name: '默认' }] }]
const specData = []
specList.forEach(group => {
const children = []
group.valueList.forEach(specValue => {
children.push({ name: specValue.spec_value })
})
specData.push({
name: group.spec_name,
list: children
})
})
return specData.length ? specData : defaultData
},
// sku -----------------------------------------------------------
openSkuPopup() {
// console.log(" - sku")
},
closeSkuPopup() {
// console.log(" - sku")
},
//
addCart(selectShop) {
const app = this
const { goods_id, goods_sku_id, buy_num } = selectShop
CartApi.add(goods_id, goods_sku_id, buy_num)
.then(result => {
//
app.$toast(result.message)
//
app.onChangeValue(false)
//
const cartTotal = result.data.cartTotal
//
setCartTotalNum(cartTotal)
//
app.$emit('addCart', cartTotal)
})
},
//
buyNow(selectShop) {
//
this.$navTo('pages/checkout/index', {
mode: 'buyNow',
goodsId: selectShop.goods_id,
goodsSkuId: selectShop.goods_sku_id,
goodsNum: selectShop.buy_num
})
//
this.onChangeValue(false)
}
}
}
</script>
<style lang="scss" scoped>
</style>

146
pages/goods/components/SlideImage.vue

@ -1,146 +0,0 @@
<template>
<!-- 商品图片 -->
<view class="images-swiper">
<swiper class="swiper-box" :autoplay="autoplay" :duration="duration" :indicator-dots="indicatorDots"
:interval="interval" :circular="true" @change="setCurrent">
<!-- 主图视频 -->
<swiper-item v-if="video">
<view class="slide-video">
<video id="myVideo" class="video" :poster="videoCover.preview_url" :src="video.external_url" controls
x5-playsinline playsinline webkit-playsinline webkit-playsinline x5-video-player-type="h5"
x5-video-player-fullscreen x5-video-orientation="portrait" :enable-progress-gesture="false"
@play="onVideoPlay"></video>
</view>
</swiper-item>
<!-- 轮播图片 -->
<swiper-item v-for="(item, index) in images" :key="index" @click="onPreviewImages(index)">
<view class="slide-image">
<image class="image" :draggable="false" :src="item.preview_url"></image>
</view>
</swiper-item>
</swiper>
<view class="swiper-count">
<text>{{ currentIndex }}</text>
<text>/</text>
<text>{{ images.length + (video ? 1 : 0) }}</text>
</view>
</view>
</template>
<script>
export default {
props: {
//
video: {
type: Object,
default () {
return null
}
},
//
videoCover: {
type: Object,
default () {
return null
}
},
//
images: {
type: Array,
default: []
}
},
data() {
return {
indicatorDots: true, //
autoplay: true, //
interval: 4000, //
duration: 800, //
currentIndex: 1, //
}
},
methods: {
//
onVideoPlay(e) {
this.autoplay = false
},
//
setCurrent({ detail }) {
const app = this
app.currentIndex = detail.current + 1
},
//
onPreviewImages(index) {
const app = this
const imageUrls = []
app.images.forEach(item => {
imageUrls.push(item.preview_url);
});
uni.previewImage({
current: imageUrls[index],
urls: imageUrls
})
}
}
}
</script>
<style lang="scss" scoped>
// swiper
.images-swiper {
position: relative;
}
.swiper-box {
width: 100%;
height: 100vw;
/* #ifdef H5 */
max-width: 480px;
max-height: 480px;
margin: 0 auto;
/* #endif */
//
.slide-video {
width: 100%;
height: 100%;
.video {
display: block;
width: 100%;
height: 100%;
}
}
//
.slide-image {
position: relative;
width: 100%;
height: 100%;
.image {
display: block;
width: 100%;
height: 100%;
}
}
}
// swiper
.swiper-count {
position: absolute;
right: 36rpx;
bottom: 72rpx;
padding: 2rpx 18rpx;
background: rgba(0, 0, 0, 0.363);
border-radius: 50rpx;
color: #fff;
font-size: 26rpx;
}
</style>

227
pages/goods/detail.scss

@ -1,227 +0,0 @@
.container {
// 设置ios刘海屏底部横线安全区域
// 110 - 18 + 4
padding-bottom: calc(constant(safe-area-inset-bottom) + 106rpx + 6rpx);
padding-bottom: calc(env(safe-area-inset-bottom) + 106rpx + 6rpx);
}
// 商品信息
.goods-info {
background: #fff;
padding: 25rpx 30rpx;
}
.info-item__top {
min-height: 40rpx;
margin-bottom: 20rpx;
line-height: 1;
}
.floor-price__samll {
font-size: 26rpx;
line-height: 1;
color: #FA2209;
margin-bottom: -10rpx;
}
// 商品价
.floor-price {
color: #FA2209;
margin-right: 15rpx;
font-size: 42rpx;
}
.original-price {
font-size: 26rpx;
text-decoration: line-through;
color: #959595;
margin-right: 15rpx;
margin-bottom: -6rpx;
}
// 会员价标签
.user-grade {
background: #3c3c3c;
border-radius: 6rpx;
padding: 8rpx 14rpx;
margin-right: 15rpx;
font-size: 24rpx;
color: #EEE0C3;
}
.goods-sales {
font-size: 24rpx;
color: #959595;
}
.info-item__name .goods-name {
font-size: 30rpx;
}
/* 商品分享 */
.goods-share__line {
border-left: 1rpx solid #f4f4f4;
height: 60rpx;
margin: 0 30rpx;
}
.goods-share .share-btn {
line-height: normal;
padding: 0;
background: none;
border-radius: 0;
box-shadow: none;
font-size: 8pt;
border: none;
color: #191919;
}
.goods-share .share-btn::after {
border: none;
}
.goods-share .share__icon {
font-size: 40rpx;
margin-bottom: 5rpx;
}
// 商品卖点
.info-item_selling-point {
margin-top: 8rpx;
font-size: 24rpx;
color: #808080;
}
// 选择商品规格
.goods-choice {
padding: 26rpx 30rpx;
font-size: 28rpx;
.spec-list {
display: flex;
align-items: center;
.spec-name {
margin-right: 10rpx;
}
}
}
// 商品详情
.goods-content .item-title {
padding: 26rpx 30rpx;
font-size: 28rpx;
}
// 底部操作栏
.footer-fixed {
position: fixed;
bottom: var(--window-bottom);
left: 0;
right: 0;
display: flex;
z-index: 11;
box-shadow: 0 -4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
background: #fff;
// 设置ios刘海屏底部横线安全区域
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.footer-container {
width: 100%;
display: flex;
height: 106rpx;
}
// 快捷菜单
.foo-item-fast {
box-sizing: border-box;
min-width: 214rpx;
line-height: 1;
display: flex;
align-items: center;
justify-content: space-evenly;
margin-right: 12rpx;
.fast-item {
position: relative;
padding: 4rpx 0;
line-height: 1;
text-align: center;
width: 84rpx;
&--cart {
margin-left: 6rpx;
.fast-icon { margin-left: -12rpx; }
}
// 角标
.fast-badge {
display: inline-block;
box-sizing: border-box;
min-width: 16px;
padding: 0 3px;
color: #fff;
font-weight: 500;
font-size: 12px;
font-family: -apple-system-font, Helvetica Neue, Arial, sans-serif;
line-height: 1.2;
text-align: center;
background-color: #ee0a24;
border: 1px solid #fff;
border-radius: 999px;
}
.fast-badge--fixed {
position: absolute;
top: 0;
right: 0;
transform-origin: 100%
}
.fast-icon {
font-size: 44rpx;
margin-bottom: 8rpx;
}
.fast-text {
font-size: 22rpx;
}
}
}
// 操作按钮
.foo-item-btn {
flex: 1;
.btn-wrapper {
height: 100%;
display: flex;
align-items: center;
}
.btn-item {
flex: 1;
font-size: 28rpx;
height: 72rpx;
margin-right: 16rpx;
color: #fff;
border-radius: 50rpx;
display: flex;
justify-content: center;
align-items: center;
}
// 立即购买按钮
.btn-item-main {
background: linear-gradient(to right, #f9211c, #ff6335);
}
// 购物车按钮
.btn-item-deputy {
background: linear-gradient(to right, #ffa600, #ffbb00);
}
}

290
pages/goods/detail.vue

@ -1,290 +0,0 @@
<template>
<view v-show="!isLoading" class="container">
<!-- 商品图片轮播 -->
<SlideImage v-if="!isLoading" :video="goods.video" :videoCover="goods.videoCover" :images="goods.goods_images" />
<!-- 商品信息 -->
<view v-if="!isLoading" class="goods-info m-top20">
<!-- 价格销量 -->
<view class="info-item info-item__top dis-flex flex-x-between flex-y-end">
<view class="block-left dis-flex flex-y-center">
<!-- 商品售价 -->
<text class="floor-price__samll"></text>
<text class="floor-price">{{ goods.goods_price_min }}</text>
<!-- 会员价标签 -->
<view v-if="goods.is_user_grade" class="user-grade">
<text>会员价</text>
</view>
<!-- 划线价 -->
<text v-if="goods.line_price_min > 0" class="original-price">{{ goods.line_price_min }}</text>
</view>
<view class="block-right dis-flex">
<!-- 销量 -->
<view class="goods-sales">
<text>已售{{ goods.goods_sales }}</text>
</view>
</view>
</view>
<!-- 标题分享 -->
<view class="info-item info-item__name dis-flex flex-y-center">
<view class="goods-name flex-box">
<text class="twoline-hide">{{ goods.goods_name }}</text>
</view>
<!-- #ifdef MP-WEIXIN -->
<view class="goods-share__line"></view>
<view class="goods-share">
<button class="share-btn dis-flex flex-dir-column" open-type="share">
<text class="share__icon iconfont icon-fenxiang"></text>
<text class="f-24">分享</text>
</button>
</view>
<!-- #endif -->
</view>
<!-- 商品卖点 -->
<view v-if="goods.selling_point" class="info-item info-item_selling-point">
<text>{{ goods.selling_point }}</text>
</view>
</view>
<!-- 选择商品规格 -->
<view v-if="goods.spec_type == 20" class="goods-choice m-top20 b-f" @click="onShowSkuPopup(1)">
<view class="spec-list">
<view class="flex-box">
<text class="col-8">选择</text>
<text class="spec-name" v-for="(item, index) in goods.specList" :key="index">{{ item.spec_name }}</text>
</view>
<view class="f-26 col-9 t-r">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
</view>
<!-- 商品服务 -->
<Service v-if="!isLoading" :goods-id="goodsId" />
<!-- 商品SKU弹窗 -->
<SkuPopup v-if="!isLoading" v-model="showSkuPopup" :skuMode="skuMode" :goods="goods" @addCart="onAddCart" />
<!-- 商品评价 -->
<Comment v-if="!isLoading" :goods-id="goodsId" :limit="2" />
<!-- 商品描述 -->
<view v-if="!isLoading" class="goods-content m-top20">
<view class="item-title b-f">
<text>商品描述</text>
</view>
<block v-if="goods.content != ''">
<view class="goods-content__detail b-f">
<mp-html :content="goods.content" />
</view>
</block>
<empty v-else tips="亲,暂无商品描述" />
</view>
<!-- 底部选项卡 -->
<view class="footer-fixed">
<view class="footer-container">
<!-- 导航图标 -->
<view class="foo-item-fast">
<!-- 首页 -->
<view class="fast-item fast-item--home" @click="onTargetHome">
<view class="fast-icon">
<text class="iconfont icon-shouye"></text>
</view>
<view class="fast-text">
<text>首页</text>
</view>
</view>
<!-- 客服 (仅微信小程序端显示) -->
<!-- #ifdef MP-WEIXIN -->
<button class="btn-normal" open-type="contact">
<view class="fast-item">
<view class="fast-icon">
<text class="iconfont icon-kefu1"></text>
</view>
<view class="fast-text">
<text>客服</text>
</view>
</view>
</button>
<!-- #endif -->
<!-- 购物车 -->
<view class="fast-item fast-item--cart" @click="onTargetCart">
<view v-if="cartTotal > 0" class="fast-badge fast-badge--fixed">{{ cartTotal > 99 ? '99+' : cartTotal }}
</view>
<view class="fast-icon">
<text class="iconfont icon-gouwuche"></text>
</view>
<view class="fast-text">
<text>购物车</text>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="foo-item-btn">
<view class="btn-wrapper">
<view class="btn-item btn-item-deputy" @click="onShowSkuPopup(2)">
<text>加入购物车</text>
</view>
<view class="btn-item btn-item-main" @click="onShowSkuPopup(3)">
<text>立即购买</text>
</view>
</view>
</view>
</view>
</view>
<!-- 快捷导航 -->
<!-- <shortcut bottom="120rpx" /> -->
</view>
</template>
<script>
import * as GoodsApi from '@/api/goods'
import * as CartApi from '@/api/cart'
// import Shortcut from '@/components/shortcut'
import SlideImage from './components/SlideImage'
import SkuPopup from './components/SkuPopup'
import Comment from './components/Comment'
import Service from './components/Service'
export default {
components: {
// Shortcut,
SlideImage,
SkuPopup,
Comment,
Service
},
data() {
return {
//
isLoading: true,
// ID
goodsId: null,
//
goods: {},
//
cartTotal: 0,
// /SKU
showSkuPopup: false,
// 1: 2: 3:
skuMode: 1
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// ID
this.goodsId = parseInt(options.goodsId)
//
this.onRefreshPage()
},
methods: {
//
onRefreshPage() {
const app = this
app.isLoading = true
Promise.all([app.getGoodsDetail(), app.getCartTotal()])
.finally(() => app.isLoading = false)
},
//
getGoodsDetail() {
const app = this
return new Promise((resolve, reject) => {
GoodsApi.detail(app.goodsId)
.then(result => {
app.goods = result.data.detail
resolve(result)
})
.catch(reject)
})
},
//
getCartTotal() {
const app = this
return new Promise((resolve, reject) => {
CartApi.total()
.then(result => {
app.cartTotal = result.data.cartTotal
resolve(result)
})
.catch(reject)
})
},
//
onAddCart(total) {
this.cartTotal = total
},
/**
* 显示/隐藏SKU弹窗
* @param {skuMode} 模式 1:都显示 2:只显示购物车 3:只显示立即购买
*/
onShowSkuPopup(skuMode = 1) {
this.skuMode = skuMode
this.showSkuPopup = !this.showSkuPopup
},
//
onTargetHome(e) {
this.$navTo('pages/index/index')
},
//
onTargetCart() {
this.$navTo('pages/cart/index')
},
},
/**
* 分享当前页面
*/
onShareAppMessage() {
const app = this
//
const params = app.$getShareUrlParams({
goodsId: app.goodsId,
})
return {
title: app.goods.goods_name,
path: `/pages/goods/detail?${params}`
}
},
/**
* 分享到朋友圈
* 本接口为 Beta 版本暂只在 Android 平台支持详见分享到朋友圈 (Beta)
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
*/
onShareTimeline() {
const app = this
//
const params = app.$getShareUrlParams({
goodsId: app.goodsId,
})
return {
title: app.goods.goods_name,
path: `/pages/goods/detail?${params}`
}
}
}
</script>
<style>
page {
background: #fafafa;
}
</style>
<style lang="scss" scoped>
@import "./detail.scss";
</style>

463
pages/goods/list.vue

@ -1,463 +0,0 @@
<template>
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ native: true }" @down="downCallback" :up="upOption" @up="upCallback">
<!-- 页面头部 -->
<view class="header">
<search class="search" :tips="options.search ? options.search : '搜索商品'" @event="handleSearch" />
<!-- 切换列表显示方式 -->
<view class="show-view" @click="handleShowView">
<text class="iconfont icon-view-tile" v-if="showView"></text>
<text class="iconfont icon-view-list" v-else></text>
</view>
</view>
<!-- 排序标签 -->
<view class="store-sort">
<view class="sort-item" :class="{ active: sortType === 'all' }" @click="handleSortType('all')">
<text>综合</text>
</view>
<view class="sort-item" :class="{ active: sortType === 'sales' }" @click="handleSortType('sales')">
<text>销量</text>
</view>
<view class="sort-item sort-item-price" :class="{ active: sortType === 'price' }" @click="handleSortType('price')">
<text>价格</text>
<view class="price-arrow">
<view class="icon up" :class="{ active: sortType === 'price' && !sortPrice }">
<text class="iconfont icon-arrow-up"></text>
</view>
<view class="icon down" :class="{ active: sortType === 'price' && sortPrice }">
<text class="iconfont icon-arrow-down"></text>
</view>
</view>
</view>
</view>
<!-- 商品列表 -->
<view class="goods-list clearfix" :class="['column-' + (showView ? '1' : '2')]">
<view class="goods-item" v-for="(item, index) in list.data" :key="index" @click="onTargetDetail(item.goods_id)">
<!-- 单列显示 -->
<view v-if="showView" class="dis-flex">
<!-- 商品图片 -->
<view class="goods-item_left">
<image class="image" :src="item.goods_image"></image>
</view>
<view class="goods-item_right">
<!-- 商品名称 -->
<view class="goods-name">
<text class="twoline-hide">{{ item.goods_name }}</text>
</view>
<view class="goods-item_desc">
<!-- 商品卖点 -->
<view class="desc-selling_point dis-flex">
<text class="oneline-hide">{{ item.selling_point }}</text>
</view>
<!-- 商品销量 -->
<view class="desc-goods_sales dis-flex">
<text>已售{{ item.goods_sales }}</text>
</view>
<!-- 商品价格 -->
<view class="desc_footer">
<text class="price_x">¥{{ item.goods_price_min }}</text>
<text class="price_y col-9" v-if="item.line_price_min > 0">¥{{ item.line_price_min }}</text>
</view>
</view>
</view>
</view>
<!-- 多列显示 -->
<view v-else class="">
<!-- 商品图片 -->
<view class="goods-image">
<image class="image" mode="aspectFill" :src="item.goods_image"></image>
</view>
<view class="detail">
<!-- 商品标题 -->
<view class="goods-name">
<text class="twoline-hide">{{ item.goods_name }}</text>
</view>
<!-- 商品价格 -->
<view class="detail-price oneline-hide">
<text class="goods-price f-30 col-m">{{ item.goods_price_min }}</text>
<text v-if="item.line_price_min > 0" class="line-price col-9 f-24">{{ item.line_price_min }}</text>
</view>
</view>
</view>
</view>
</view>
</mescroll-body>
</template>
<script>
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import * as GoodsApi from '@/api/goods'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
import Search from '@/components/search'
const pageSize = 15
const showViewKey = 'GoodsList-ShowView';
export default {
components: {
MescrollBody,
Search
},
mixins: [MescrollMixin],
data() {
return {
showView: false, // (truefalse)
sortType: 'all', //
sortPrice: false, // (true false)
options: {}, //
list: getEmptyPaginateObj(), //
//
upOption: {
//
auto: true,
// ; 10
page: { size: pageSize },
// 4
noMoreSize: 4,
}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// options
this.options = options
//
this.setShowView()
},
methods: {
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
//
app.getGoodsList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
//
setShowView() {
this.showView = uni.getStorageSync(showViewKey) || false
},
/**
* 获取商品列表
* @param {number} pageNo 页码
*/
getGoodsList(pageNo = 1) {
const app = this
console.log(app.options)
const param = {
sortType: app.sortType,
sortPrice: Number(app.sortPrice),
categoryId: app.options.categoryId || 0,
goodsName: app.options.search || '',
page: pageNo
}
return new Promise((resolve, reject) => {
GoodsApi.list(param)
.then(result => {
//
const newList = result.data.list
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
.catch(reject)
})
},
//
handleSortType(newSortType) {
const app = this
const newSortPrice = newSortType === 'price' ? !app.sortPrice : true
app.sortType = newSortType
app.sortPrice = newSortPrice
//
app.list = getEmptyPaginateObj()
app.mescroll.resetUpScroll()
},
//
handleShowView() {
const app = this
app.showView = !app.showView
uni.setStorageSync(showViewKey, app.showView)
},
//
onTargetDetail(goodsId) {
this.$navTo('pages/goods/detail', { goodsId })
},
/**
* 商品搜索
*/
handleSearch() {
const searchPageUrl = 'pages/search/index'
//
let pages = getCurrentPages()
if (pages.length > 1 &&
pages[pages.length - 2].route === searchPageUrl) {
uni.navigateBack()
return
}
//
this.$navTo(searchPageUrl)
}
},
/**
* 设置分享内容
*/
onShareAppMessage() {
//
return {
title: "全部分类",
path: "/pages/category/index?" + this.$getShareUrlParams()
}
},
/**
* 分享到朋友圈
* 本接口为 Beta 版本暂只在 Android 平台支持详见分享到朋友圈 (Beta)
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
*/
onShareTimeline() {
//
return {
title: "全部分类",
path: "/pages/category/index?" + this.$getShareUrlParams()
}
}
}
</script>
<style lang="scss" scoped>
//
.header {
display: flex;
align-items: center;
background-color: #fff;
//
.search {
flex: 1;
}
//
.show-view {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
font-size: 36rpx;
color: #505050;
}
}
//
.store-sort {
position: sticky;
top: var(--window-top);
display: flex;
padding: 20rpx 0;
font-size: 28rpx;
background: #fff;
color: #000;
z-index: 99;
.sort-item {
flex-basis: 33.3333%;
display: flex;
justify-content: center;
align-items: center;
height: 50rpx;
&.active {
color: #e49a3d;
}
}
.sort-item-price .price-arrow {
margin-left: 20rpx;
font-size: 24rpx;
color: #000;
.icon {
&.active {
color: #e49a3d;
}
&.up {
margin-bottom: -16rpx;
}
&.down {
margin-top: -16rpx;
}
}
}
}
//
.goods-list {
padding: 4rpx;
box-sizing: border-box;
}
//
.goods-list.column-1 {
.goods-item {
width: 100%;
height: 280rpx;
margin-bottom: 12rpx;
padding: 20rpx;
box-sizing: border-box;
background: #fff;
line-height: 1.6;
&:last-child {
margin-bottom: 0;
}
}
.goods-item_left {
display: flex;
width: 300rpx;
background: #fff;
align-items: center;
.image {
display: block;
width: 240rpx;
height: 240rpx;
}
}
.goods-item_right {
position: relative;
// width: 450rpx;
flex: 1;
.goods-name {
margin-top: 10rpx;
min-height: 68rpx;
line-height: 1.3;
white-space: normal;
color: #484848;
font-size: 26rpx;
}
}
.goods-item_desc {
margin-top: 8rpx;
}
.desc-selling_point {
width: 400rpx;
font-size: 24rpx;
color: #e49a3d;
}
.desc-goods_sales {
color: #999;
font-size: 24rpx;
}
.desc_footer {
font-size: 24rpx;
.price_x {
margin-right: 16rpx;
color: #f03c3c;
font-size: 30rpx;
}
.price_y {
text-decoration: line-through;
}
}
}
//
.goods-list.column-2 {
.goods-item {
width: 50%;
}
}
.goods-item {
float: left;
box-sizing: border-box;
padding: 6rpx;
.goods-image {
position: relative;
width: 100%;
height: 0;
padding-bottom: 100%;
overflow: hidden;
background: #fff;
&:after {
content: '';
display: block;
margin-top: 100%;
}
.image {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
-o-object-fit: cover;
object-fit: cover;
}
}
.detail {
padding: 8rpx;
background: #fff;
.goods-name {
min-height: 68rpx;
line-height: 32rpx;
white-space: normal;
color: #484848;
font-size: 26rpx;
}
.detail-price {
.goods-price {
margin-right: 8rpx;
}
.line-price {
text-decoration: line-through;
}
}
}
}
</style>

306
pages/my-coupon/index.vue

@ -1,306 +0,0 @@
<template>
<view class="container">
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption"
@up="upCallback">
<!-- tab栏 -->
<u-tabs :list="tabs" :is-scroll="false" :current="curTab" active-color="#FA2209" :duration="0.2"
@change="onChangeTab" />
<!-- 优惠券列表 -->
<view class="coupon-list">
<view class="coupon-item" v-for="(item, index) in list.data" :key="index">
<view class="item-wrapper" :class="['color-' + (item.state.value ? color[index % color.length] : 'gray')]">
<view class="coupon-type">{{ CouponTypeEnum[item.coupon_type].name }}</view>
<view class="tip dis-flex flex-dir-column flex-x-center">
<view v-if="item.coupon_type == CouponTypeEnum.FULL_DISCOUNT.value">
<text class="f-30"></text>
<text class="money">{{ item.reduce_price }}</text>
</view>
<text class="money" v-if="item.coupon_type == CouponTypeEnum.DISCOUNT.value">{{ item.discount }}</text>
<text class="pay-line">{{ item.min_price }}元可用</text>
</view>
<view class="split-line"></view>
<view class="content dis-flex flex-dir-column flex-x-between">
<view class="title">{{ item.name }}</view>
<view class="bottom dis-flex flex-y-center">
<view class="time flex-box">
<block v-if="item.start_time === item.end_time">{{ item.start_time }} 当天有效</block>
<block v-else>{{ item.start_time }}~{{ item.end_time }}</block>
</view>
<view class="receive state">
<text>{{ item.state.text }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</mescroll-body>
</view>
</template>
<script>
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
import * as MyCouponApi from '@/api/myCoupon'
import { CouponTypeEnum } from '@/common/enum/coupon'
const color = ['red', 'blue', 'violet', 'yellow']
const pageSize = 15
const tabs = [{
name: `未使用`,
value: 'isUnused'
}, {
name: `已使用`,
value: 'isUse'
}, {
name: `已过期`,
value: 'isExpire'
}]
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
//
CouponTypeEnum,
//
color,
//
tabs,
//
curTab: 0,
//
list: getEmptyPaginateObj(),
//
upOption: {
//
auto: true,
// ; 10
page: { size: pageSize },
// 4
noMoreSize: 4,
//
empty: {
tip: '亲,暂无相关优惠券'
}
}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
methods: {
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
//
app.getCouponList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
/**
* 获取优惠券列表
*/
getCouponList(pageNo = 1) {
const app = this
return new Promise((resolve, reject) => {
MyCouponApi.list({ dataType: app.getTabValue(), page: pageNo }, { load: false })
.then(result => {
//
const newList = result.data.list
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
})
},
//
getTabValue() {
return this.tabs[this.curTab].value
},
//
onChangeTab(index) {
const app = this
//
app.curTab = index
//
app.onRefreshList()
},
//
onRefreshList() {
this.list = getEmptyPaginateObj()
setTimeout(() => {
this.mescroll.resetUpScroll()
}, 120)
},
}
}
</script>
<style lang="scss" scoped>
.coupon-list {
padding: 20rpx;
}
.coupon-item {
position: relative;
overflow: hidden;
margin-bottom: 22rpx;
}
.item-wrapper {
width: 100%;
display: flex;
background: #fff;
border-radius: 8rpx;
color: #fff;
height: 180rpx;
.coupon-type {
position: absolute;
top: 0;
right: 0;
z-index: 10;
width: 128rpx;
padding: 6rpx 0;
background: #a771ff;
font-size: 20rpx;
text-align: center;
color: #ffffff;
transform: rotate(45deg);
transform-origin: 64rpx 64rpx;
}
&.color-blue {
background: linear-gradient(-125deg, #57bdbf, #2f9de2);
}
&.color-red {
background: linear-gradient(-128deg, #ff6d6d, #ff3636);
}
&.color-violet {
background: linear-gradient(-113deg, #ef86ff, #b66ff5);
.coupon-type {
background: #55b5ff;
}
}
&.color-yellow {
background: linear-gradient(-141deg, #f7d059, #fdb054);
}
&.color-gray {
background: linear-gradient(-113deg, #bdbdbd, #a2a1a2);
.coupon-type {
background: #9e9e9e;
}
}
.content {
flex: 1;
padding: 30rpx 20rpx;
border-radius: 16rpx 0 0 16rpx;
.title {
font-size: 32rpx;
}
.bottom {
.time {
font-size: 24rpx;
}
.receive {
height: 46rpx;
width: 122rpx;
line-height: 46rpx;
text-align: center;
border: 1rpx solid #fff;
border-radius: 30rpx;
color: #fff;
font-size: 24rpx;
&.state {
border: none;
}
}
}
}
.tip {
position: relative;
flex: 0 0 32%;
text-align: center;
border-radius: 0 16rpx 16rpx 0;
.money {
font-weight: bold;
font-size: 52rpx;
}
.pay-line {
font-size: 22rpx;
}
}
.split-line {
position: relative;
flex: 0 0 0;
border-left: 4rpx solid #fff;
margin: 0 10rpx 0 6rpx;
background: #fff;
&:before,
{
border-radius: 0 0 16rpx 16rpx;
top: 0;
}
&:after {
border-radius: 16rpx 16rpx 0 0;
bottom: 0;
}
&:before,
&:after {
content: '';
position: absolute;
width: 24rpx;
height: 12rpx;
background: #f7f7f7;
left: -14rpx;
z-index: 1;
}
}
}
</style>

136
pages/order/center.vue

@ -1,136 +0,0 @@
<template>
<view class="container">
<!-- 订单页列表 -->
<view class="order-list">
<view class="order-item" v-for="(item, index) in orderList" :key="index" @click="$navTo(item.path)">
<view class="order-item-icon" :style="{ color: item.color }">
<text class="iconfont" :class="item.icon"></text>
</view>
<view class="order-item-name">{{ item.name }}</view>
<view class="icon-arrow">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" @click="$navTo('pages/index/index')">
<view class="btn-item-icon">
<text class="iconfont icon-shouye2"></text>
</view>
<view class="btn-item-name">
<text>返回首页</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import * as UserApi from '@/api/user'
//
const orderList = [{
name: '商城订单',
icon: 'icon-qpdingdan',
color: 'rgb(253 65 0)',
path: 'pages/order/index'
},
{
name: '充值订单',
icon: 'icon-jifen',
color: 'rgb(191, 150, 7)',
path: 'pages/wallet/recharge/order'
}
]
export default {
data() {
return {
orderList
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
//
this.getUserInfo()
},
methods: {
//
getUserInfo() {
const app = this
UserApi.info()
},
}
}
</script>
<style lang="scss" scoped>
.order-list {
padding: 0 25rpx;
background-color: #fff;
.order-item {
position: relative;
padding: 26rpx 16rpx;
border-bottom: 1rpx solid #eee;
display: flex;
justify-content: flex-start;
align-items: center;
font-size: 30rpx;
&:last-child {
border-bottom: none;
}
}
.order-item-icon {
font-size: 34rpx;
margin-right: 18rpx;
}
.icon-arrow {
position: absolute;
top: 26rpx;
right: 6rpx;
}
}
//
.footer {
position: fixed;
bottom: var(--window-bottom);
left: 0;
right: 0;
z-index: 11;
box-shadow: 0 -4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
background: #fff;
// ios线
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
.btn-item {
font-size: 30rpx;
height: 90rpx;
border-radius: 50rpx;
display: flex;
justify-content: center;
align-items: center;
}
.btn-item-icon {
font-size: 34rpx;
margin-right: 18rpx;
}
}
</style>

571
pages/order/comment/index.vue

@ -1,571 +0,0 @@
<template>
<view v-if="!isLoading" class="container">
<view class="goods-list">
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
<!-- 商品详情 -->
<view class="goods-main">
<!-- 商品图片 -->
<view class="goods-image">
<image class="image" :src="item.goods_image" mode="scaleToFill"></image>
</view>
<!-- 商品信息 -->
<view class="goods-content">
<view class="goods-title"><text class="twoline-hide">{{ item.goods_name }}</text></view>
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in item.goods_props" :key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
</view>
<!-- 交易信息 -->
<view class="goods-trade">
<view class="goods-price">
<text class="unit"></text>
<text class="value">{{ item.goods_price }}</text>
</view>
<view class="goods-num">
<text>×{{ item.total_num }}</text>
</view>
</view>
</view>
<!-- 选择评价 -->
<view class="score-row">
<view class="score-item score-praise" :class="{ active: formData[index].score == 10 }"
@click="setScore(index, 10)">
<view class="score">
<text class="score-icon iconfont icon-haoping"></text>
<text class="score-text">好评</text>
</view>
</view>
<view class="score-item score-review" :class="{ active: formData[index].score == 20 }"
@click="setScore(index, 20)">
<view class="score">
<text class="score-icon iconfont icon-zhongping"></text>
<text class="score-text">中评</text>
</view>
</view>
<view class="score-item score-negative" :class="{ active: formData[index].score == 30 }"
@click="setScore(index, 30)">
<view class="score">
<text class="score-icon iconfont icon-chaping"></text>
<text class="score-text">差评</text>
</view>
</view>
</view>
<!-- 评价内容 -->
<view class="form-content">
<textarea class="textarea" v-model="formData[index].content" maxlength="500"
placeholder="请输入评价内容 (留空则不评价)"></textarea>
</view>
<!-- 图片列表 -->
<view class="image-list">
<view class="image-preview" v-for="(image, imageIndex) in formData[index].imageList" :key="imageIndex">
<text class="image-delete iconfont icon-shanchu" @click="deleteImage(index, imageIndex)"></text>
<image class="image" mode="aspectFill" :src="image.path"></image>
</view>
<view v-if="!formData[index].imageList || formData[index].imageList.length < maxImageLength"
class="image-picker" @click="chooseImage(index)">
<text class="choose-icon iconfont icon-camera"></text>
<text class="choose-text">上传图片</text>
</view>
</view>
</view>
</view>
<!-- 底部操作按钮 -->
<view class="footer-fixed">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">确认提交</view>
</view>
</view>
</view>
</template>
<script>
import * as UploadApi from '@/api/upload'
import * as OrderCommentApi from '@/api/order/comment'
const maxImageLength = 6
export default {
data() {
return {
//
isLoading: true,
// ID
orderId: null,
//
goodsList: [],
//
formData: [],
//
maxImageLength,
//
disabled: false
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ orderId }) {
this.orderId = orderId
//
this.getGoodsList()
},
methods: {
//
getGoodsList() {
const app = this
app.isLoading = true
OrderCommentApi.list(app.orderId)
.then(result => {
app.goodsList = result.data.goodsList
app.initFormData()
app.isLoading = false
})
},
// form
initFormData() {
const { goodsList } = this
const formData = goodsList.map(item => {
return {
goods_id: item.goods_id,
order_goods_id: item.order_goods_id,
score: 10,
content: '',
imageList: [],
uploaded: []
}
})
this.formData = formData
},
//
setScore(index, score) {
this.formData[index].score = score
},
//
chooseImage(index) {
const app = this
const oldImageList = app.formData[index].imageList
//
uni.chooseImage({
count: maxImageLength - oldImageList.length,
sizeType: ['original', 'compressed'], //
sourceType: ['album', 'camera'], //
success({ tempFiles }) {
// tempFiles = [{path:'xxx', size:100}]
app.formData[index].imageList = oldImageList.concat(tempFiles)
}
});
},
//
deleteImage(index, imageIndex) {
this.formData[index].imageList.splice(imageIndex, 1)
},
//
handleSubmit() {
const app = this
//
if (app.disabled === true) return false
//
app.disabled = true
//
const imagesLength = app.getImagesLength()
if (imagesLength > 0) {
app.uploadFile()
.then(() => {
console.log('then')
app.onSubmit()
})
.catch(err => {
console.log('catch')
app.disabled = false
if (err.statusCode !== 0) {
app.$toast(err.errMsg)
}
console.log('err', err)
})
} else {
app.onSubmit()
}
},
//
getImagesLength() {
const { formData } = this
let imagesLength = 0
formData.forEach(item => {
if (item.content.trim()) {
imagesLength += item.imageList.length
}
})
return imagesLength
},
//
onSubmit() {
const app = this
OrderCommentApi.submit(app.orderId, app.formData)
.then(result => {
app.$toast(result.message)
setTimeout(() => {
app.disabled = false
uni.navigateBack()
}, 1500)
})
.catch(err => app.disabled = false)
},
//
uploadFile() {
const app = this
const { formData } = app
//
const files = []
formData.forEach((item, index) => {
if (item.content.trim() && item.imageList.length) {
const images = item.imageList.map(image => image)
files.push({ formDataIndex: index, images })
}
})
//
return new Promise((resolve, reject) => {
Promise.all(files.map((file, index) => {
return new Promise((resolve, reject) => {
UploadApi.image(file.images)
.then(fileIds => {
app.formData[index].uploaded = fileIds
resolve(fileIds)
})
.catch(reject)
})
}))
.then(resolve, reject)
})
}
}
}
</script>
<style lang="scss" scoped>
.container {
// ios线
padding-bottom: constant(env(safe-area-inset-bottom) + 140rpx);
padding-bottom: calc(env(safe-area-inset-bottom) + 140rpx);
}
.goods-list {
font-size: 28rpx;
padding-top: 30rpx;
}
.goods-item {
width: 94%;
background: #fff;
padding: 24rpx 24rpx;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
margin: 0 auto 30rpx auto;
border-radius: 20rpx;
.goods-detail {
padding: 24rpx 20rpx;
.left {
.goods-image {
display: block;
width: 150rpx;
height: 150rpx;
}
}
.right {
padding-left: 20rpx;
}
}
.score-row {
display: flex;
justify-content: space-around;
padding: 24rpx 20rpx;
.score-item {
display: flex;
justify-content: center;
align-items: center;
&.score-praise {
color: #ff4544;
.score-icon {
background: #ff4544;
}
}
&.score-review {
color: #fcb500;
.score-icon {
background: #fcb500;
}
}
&.score-negative {
color: #9b9b9b;
.score-icon {
background: #9b9b9b;
}
}
.score {
padding: 10rpx 20rpx 10rpx 10rpx;
border-radius: 30rpx;
.score-icon {
margin-right: 10rpx;
padding: 10rpx;
border-radius: 50%;
font-size: 30rpx;
color: #fff;
}
}
&.active {
.score {
color: #fff;
}
&.score-praise {
.score {
background: #ff4544;
}
}
&.score-review {
.score {
background: #fcb500;
}
}
&.score-negative {
.score {
background: #9b9b9b;
}
}
}
}
}
//
.form-content {
padding: 14rpx 10rpx;
.textarea {
width: 100%;
height: 220rpx;
padding: 12rpx;
border: 1rpx solid #e8e8e8;
border-radius: 5rpx;
box-sizing: border-box;
font-size: 26rpx;
}
}
.image-list {
padding: 0 20rpx;
margin-top: 20rpx;
margin-bottom: -20rpx;
&:after {
clear: both;
content: " ";
display: table;
}
.image {
display: block;
width: 100%;
height: 100%;
}
.image-picker,
.image-preview {
width: 184rpx;
height: 184rpx;
margin-right: 30rpx;
margin-bottom: 30rpx;
float: left;
&:nth-child(3n+0) {
margin-right: 0;
}
}
.image-picker {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 1rpx dashed #ccc;
color: #ccc;
.choose-icon {
font-size: 48rpx;
margin-bottom: 6rpx;
}
.choose-text {
font-size: 24rpx;
}
}
.image-preview {
position: relative;
.image-delete {
position: absolute;
top: -15rpx;
right: -15rpx;
height: 42rpx;
width: 42rpx;
line-height: 42rpx;
background: rgba(0, 0, 0, 0.64);
border-radius: 50%;
color: #fff;
font-weight: bolder;
font-size: 22rpx;
z-index: 10;
text-align: center;
}
}
}
}
//
.goods-main {
display: flex;
margin-bottom: 20rpx;
//
.goods-image {
width: 180rpx;
height: 180rpx;
.image {
display: block;
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
//
.goods-content {
flex: 1;
padding-left: 16rpx;
padding-top: 16rpx;
.goods-title {
font-size: 26rpx;
max-height: 76rpx;
}
.goods-props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
.goods-props-item {
display: inline-block;
margin-right: 14rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
width: auto;
}
}
}
//
.goods-trade {
padding-top: 16rpx;
width: 150rpx;
text-align: right;
color: $uni-text-color-grey;
font-size: 26rpx;
.goods-price {
vertical-align: bottom;
margin-bottom: 16rpx;
.unit {
margin-right: -2rpx;
font-size: 24rpx;
}
}
}
}
//
.footer-fixed {
position: fixed;
bottom: var(--window-bottom);
left: 0;
right: 0;
height: 96rpx;
z-index: 11;
box-shadow: 0 -4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
background: #fff;
// ios线
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
.btn-wrapper {
height: 100%;
display: flex;
align-items: center;
padding: 0 20rpx;
}
.btn-item {
flex: 1;
font-size: 28rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
color: #fff;
border-radius: 50rpx;
}
.btn-item-main {
background: linear-gradient(to right, #f9211c, #ff6335);
//
&.disabled {
background: #ff9779;
}
}
}
</style>

866
pages/order/detail.vue

@ -1,866 +0,0 @@
<template>
<view v-if="!isLoading" class="container">
<view class="header">
<!-- 订单状态 -->
<view class="order-status">
<view class="status-icon">
<!-- 进行中的订单 -->
<block v-if="order.order_status == OrderStatusEnum.NORMAL.value">
<!-- 待支付 -->
<block v-if="order.pay_status == PayStatusEnum.PENDING.value">
<image class="image" src="/static/order/status/wait_pay.png" mode="aspectFit"></image>
</block>
<!-- 待发货 -->
<block v-else-if="order.delivery_status == DeliveryStatusEnum.NOT_DELIVERED.value">
<image class="image" src="/static/order/status/wait_deliver.png" mode="aspectFit"></image>
</block>
<!-- 待收货 -->
<block v-else-if="order.receipt_status == ReceiptStatusEnum.NOT_RECEIVED.value">
<image class="image" src="/static/order/status/wait_receipt.png" mode="aspectFit"></image>
</block>
</block>
<!-- 已完成 -->
<block v-if="order.order_status == OrderStatusEnum.COMPLETED.value">
<image class="image" src="/static/order/status/received.png" mode="aspectFit"></image>
</block>
<!-- 已取消/待取消 -->
<block v-if="order.order_status == OrderStatusEnum.CANCELLED.value || order.order_status == OrderStatusEnum.APPLY_CANCEL.value">
<image class="image" src="/static/order/status/close.png" mode="aspectFit"></image>
</block>
</view>
<view class="status-text">
<text>{{ order.state_text }}</text>
</view>
</view>
<!-- 下一步操作 -->
<view class="next-action" v-if="order.order_status == OrderStatusEnum.NORMAL.value">
<view v-if="order.pay_status == PayStatusEnum.PENDING.value" class="action-btn" @click="onPay()">去支付</view>
<view v-if="order.delivery_status == DeliveryStatusEnum.DELIVERED.value && order.receipt_status == ReceiptStatusEnum.NOT_RECEIVED.value" class="action-btn" @click="onReceipt(order.order_id)">确认收货</view>
</view>
</view>
<!-- 快递配送配送地址 -->
<view class="delivery-address i-card">
<view class="link-man">
<text class="name">{{ order.address.name }}</text>
<text class="phone">{{ order.address.phone }}</text>
</view>
<view class="address">
<text class="region" v-for="(region, idx) in order.address.region" :key="idx">{{ region }}</text>
<text class="detail">{{ order.address.detail }}</text>
</view>
</view>
<!-- 物流信息 -->
<view v-if="order.delivery_type == DeliveryTypeEnum.EXPRESS.value && order.delivery_status == DeliveryStatusEnum.DELIVERED.value && order.express" class="express i-card" @click="handleTargetExpress()">
<view class="main">
<view class="info-item">
<view class="item-lable">物流公司</view>
<view class="item-content">
<text>{{ order.express.express_name }}</text>
</view>
</view>
<view class="info-item">
<view class="item-lable">物流单号</view>
<view class="item-content">
<text>{{ order.express_no }}</text>
<view class="act-copy" @click.stop="handleCopy(order.express_no)">
<text>复制</text>
</view>
</view>
</view>
</view>
<view class="right-arrow">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
<!-- 商品列表 -->
<view class="goods-list i-card">
<view class="goods-item" v-for="(goods, idx) in order.goods" :key="idx">
<view class="goods-main" @click="handleTargetGoods(goods.goods_id)">
<!-- 商品图片 -->
<view class="goods-image">
<image class="image" :src="goods.goods_image" mode="scaleToFill"></image>
</view>
<!-- 商品信息 -->
<view class="goods-content">
<view class="goods-title"><text class="twoline-hide">{{ goods.goods_name }}</text></view>
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in goods.goods_props" :key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
</view>
<!-- 交易信息 -->
<view class="goods-trade">
<view class="goods-price">
<text class="unit"></text>
<text class="value">{{ goods.is_user_grade ? goods.grade_goods_price : goods.goods_price }}</text>
</view>
<view class="goods-num">
<text>×{{ goods.total_num }}</text>
</view>
</view>
</view>
<!-- 商品售后 -->
<view class="goods-refund">
<text v-if="goods.refund" class="stata-text">已申请售后</text>
<view v-else-if="order.isAllowRefund" class="action-btn" @click.stop="handleApplyRefund(goods.order_goods_id)">申请售后</view>
</view>
</view>
</view>
<!-- 订单信息 -->
<view class="order-info i-card">
<view class="info-item">
<view class="item-lable">订单编号</view>
<view class="item-content">
<text>{{ order.order_no }}</text>
<view class="act-copy" @click="handleCopy(order.order_no)">
<text>复制</text>
</view>
</view>
</view>
<view class="info-item">
<view class="item-lable">下单时间</view>
<view class="item-content">
<text>{{ order.create_time }}</text>
</view>
</view>
<view class="info-item">
<view class="item-lable">买家留言</view>
<view class="item-content">
<text>{{ order.buyer_remark ? order.buyer_remark : '--' }}</text>
</view>
</view>
</view>
<!-- 结算信息 -->
<view class="trade-info i-card">
<view class="info-item">
<view class="item-lable">订单金额</view>
<view class="item-content">
<text>{{ order.total_price }}</text>
</view>
</view>
<view v-if="order.coupon_money > 0" class="info-item">
<view class="item-lable">优惠券抵扣</view>
<view class="item-content">
<text>-{{ order.coupon_money }}</text>
</view>
</view>
<view v-if="order.points_money > 0" class="info-item">
<view class="item-lable">{{ setting.points_name }}抵扣</view>
<view class="item-content">
<text>-{{ order.points_money }}</text>
</view>
</view>
<view class="info-item">
<view class="item-lable">运费</view>
<view class="item-content">
<text>+{{ order.express_price }}</text>
</view>
</view>
<view v-if="order.update_price.value != '0.00'" class="info-item">
<view class="item-lable">后台改价</view>
<view class="item-content">
<text>{{ order.update_price.symbol }}</text>
<text>{{ order.update_price.value }}</text>
</view>
</view>
<view class="divider"></view>
<view class="trade-total">
<text class="lable">实付款</text>
<view class="goods-price">
<text class="unit"></text>
<text class="value">{{ order.pay_price }}</text>
</view>
</view>
</view>
<!-- 底部操作按钮 -->
<view v-if="order.order_status != OrderStatusEnum.CANCELLED.value" class="footer-fixed">
<view class="btn-wrapper">
<!-- 未支付取消订单 -->
<block v-if="order.pay_status == PayStatusEnum.PENDING.value">
<view class="btn-item" @click="onCancel(order.order_id)">取消</view>
</block>
<!-- 已支付进行中的订单 -->
<block v-if="order.order_status != OrderStatusEnum.APPLY_CANCEL.value">
<block v-if="order.pay_status == PayStatusEnum.SUCCESS.value && order.delivery_status == DeliveryStatusEnum.NOT_DELIVERED.value">
<view class="btn-item" @click="onCancel(order.order_id)">申请取消</view>
</block>
</block>
<!-- 已申请取消 -->
<view v-else class="f-28 col-8">取消申请中</view>
<!-- 未支付的订单 -->
<block v-if="order.pay_status == PayStatusEnum.PENDING.value">
<view class="btn-item active" @click="onPay()">去支付</view>
</block>
<!-- 确认收货 -->
<block v-if="order.delivery_status == DeliveryStatusEnum.DELIVERED.value && order.receipt_status == ReceiptStatusEnum.NOT_RECEIVED.value">
<view class="btn-item active" @click="onReceipt(order.order_id)">确认收货</view>
</block>
<!-- 订单评价 -->
<block v-if="order.order_status == OrderStatusEnum.COMPLETED.value && order.is_comment == 0">
<view class="btn-item" @click="handleTargetComment(order.order_id)">评价</view>
</block>
</view>
</view>
<!-- 支付方式弹窗 -->
<u-popup v-model="showPayPopup" mode="bottom" border-radius="26" :closeable="true">
<view class="pay-popup">
<view class="title">请选择支付方式</view>
<view class="pop-content">
<!-- 微信支付 -->
<!-- #ifdef MP-WEIXIN -->
<view class="pay-item dis-flex flex-x-between" @click="onSelectPayType(PayTypeEnum.WECHAT.value)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon wechat">
<text class="iconfont icon-wechat-pay"></text>
</view>
<view class="item-left_text">
<text>{{ PayTypeEnum.WECHAT.name }}</text>
</view>
</view>
</view>
<!-- #endif -->
<!-- 余额支付 -->
<view class="pay-item dis-flex flex-x-between" @click="onSelectPayType(PayTypeEnum.BALANCE.value)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon balance">
<text class="iconfont icon-balance-pay"></text>
</view>
<view class="item-left_text">
<text>{{ PayTypeEnum.BALANCE.name }}</text>
</view>
</view>
</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import {
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum
} from '@/common/enum/order'
import * as OrderApi from '@/api/order'
import { wxPayment } from '@/core/app'
export default {
data() {
return {
//
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum,
// ID
orderId: null,
//
isLoading: true,
//
order: {},
//
setting: {},
//
showPayPopup: false,
// onShow
canReset: false,
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ orderId }) {
// ID
this.orderId = orderId
//
this.getOrderDetail()
// :
uni.$on('syncRefresh', (val, isCur) => {
if (!isCur) {
this.canReset = val
}
})
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
this.canReset && this.getOrderDetail()
this.canReset = false
},
methods: {
//
getOrderDetail(canReset = false) {
const app = this
app.isLoading = true
OrderApi.detail(app.orderId)
.then(result => {
app.order = result.data.order
app.setting = result.data.setting
app.isLoading = false
})
// :
canReset && uni.$emit('syncRefresh', true, true)
},
//
handleCopy(value) {
const app = this
uni.setClipboardData({
data: value,
success() {
app.$toast('复制成功')
}
})
},
//
handleTargetExpress() {
this.$navTo('pages/order/express/index', { orderId: this.orderId })
},
//
handleTargetGoods(goodsId) {
this.$navTo('pages/goods/detail', { goodsId })
},
//
handleApplyRefund(orderGoodsId) {
this.$navTo('pages/refund/apply', { orderGoodsId })
},
//
onCancel(orderId) {
const app = this
uni.showModal({
title: '友情提示',
content: '确认要取消该订单吗?',
success(o) {
if (o.confirm) {
OrderApi.cancel(orderId)
.then(result => {
//
app.$toast(result.message)
//
app.getOrderDetail(true)
})
}
}
});
},
//
onReceipt(orderId) {
const app = this
uni.showModal({
title: '友情提示',
content: '确认收到商品了吗?',
success(o) {
if (o.confirm) {
OrderApi.receipt(orderId)
.then(result => {
//
app.$success(result.message)
//
app.getOrderDetail(true)
})
}
}
});
},
//
onPay() {
//
this.showPayPopup = true
},
//
onSelectPayType(payType) {
const app = this
//
this.showPayPopup = false
//
OrderApi.pay(app.orderId, payType)
.then(result => app.onSubmitCallback(result))
.catch(err => err)
},
//
onSubmitCallback(result) {
const app = this
//
if (result.data.pay_type == PayTypeEnum.WECHAT.value) {
wxPayment(result.data.payment)
.then(() => {
app.$success('支付成功')
setTimeout(() => app.getOrderDetail(true), 1500)
})
.catch(err => app.$error('订单未支付'))
.finally(() => app.disabled = false)
}
//
if (result.data.pay_type == PayTypeEnum.BALANCE.value) {
app.$success('支付成功')
app.disabled = false
//
setTimeout(() => app.getOrderDetail(true), 1500)
}
},
//
handleTargetComment(orderId) {
this.$navTo('pages/order/comment/index', { orderId })
},
},
}
</script>
<style>
page {
background: #f4f4f4;
}
</style>
<style lang="scss" scoped>
.container {
// ios线
padding-bottom: constant(env(safe-area-inset-bottom) + 106rpx + 6rpx);
padding-bottom: calc(env(safe-area-inset-bottom) + 106rpx + 6rpx);
}
//
.header {
display: flex;
justify-content: space-between;
background-color: #e8c269;
height: 280rpx;
padding: 56rpx 30rpx 0 30rpx;
.order-status {
display: flex;
align-items: center;
height: 128rpx;
.status-icon {
width: 128rpx;
height: 128rpx;
.image {
display: block;
width: 100%;
height: 100%;
}
}
.status-text {
padding-left: 20rpx;
color: #fff;
font-size: 38rpx;
font-weight: bold;
}
}
.next-action {
display: flex;
align-items: center;
height: 128rpx;
.action-btn {
min-width: 152rpx;
height: 56rpx;
padding: 0 30rpx;
line-height: 56rpx;
background-color: #fff;
text-align: center;
border-radius: 28rpx;
border-color: rgb(102, 102, 102);
cursor: pointer;
user-select: none;
color: #c7a157;
}
}
}
//
.i-card {
background: #fff;
padding: 24rpx 24rpx;
width: 94%;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
margin: 0 auto 20rpx auto;
border-radius: 20rpx;
}
//
.delivery-address {
margin-top: -50rpx;
.link-man {
line-height: 46rpx;
color: #333;
.name {
margin-right: 10rpx;
}
}
.address {
margin-top: 12rpx;
color: #999;
font-size: 24rpx;
.detail {
margin-left: 6rpx;
}
}
}
//
.express {
display: flex;
align-items: center;
.main {
flex: 1;
}
.info-item {
display: flex;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
.item-lable {
display: flex;
align-items: center;
font-size: 24rpx;
color: #999;
margin-right: 30rpx;
}
.item-content {
flex: 1;
display: flex;
align-items: center;
font-size: 26rpx;
color: #333;
.act-copy {
margin-left: 20rpx;
padding: 2rpx 20rpx;
font-size: 22rpx;
color: #666;
border: 1rpx solid #c1c1c1;
border-radius: 16rpx;
}
}
}
//
.right-arrow {
margin-left: 16rpx;
// color: #777;
font-size: 26rpx;
}
}
//
.goods-list {
//
.goods-item {
margin-bottom: 40rpx;
&:last-child {
margin-bottom: 0;
}
//
.goods-main {
display: flex;
}
//
.goods-image {
width: 180rpx;
height: 180rpx;
.image {
display: block;
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
//
.goods-content {
flex: 1;
padding-left: 16rpx;
padding-top: 16rpx;
.goods-title {
font-size: 26rpx;
max-height: 76rpx;
}
.goods-props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
.goods-props-item {
display: inline-block;
margin-right: 14rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
width: auto;
}
}
}
//
.goods-trade {
padding-top: 16rpx;
width: 150rpx;
text-align: right;
color: $uni-text-color-grey;
font-size: 26rpx;
.goods-price {
vertical-align: bottom;
margin-bottom: 16rpx;
.unit {
margin-right: -2rpx;
font-size: 24rpx;
}
}
}
//
.goods-refund {
display: flex;
justify-content: flex-end;
.stata-text {
font-size: 24rpx;
color: #999;
}
.action-btn {
border-radius: 28rpx;
padding: 8rpx 26rpx;
font-size: 24rpx;
color: #383838;
border: 1rpx solid #a8a8a8;
}
}
}
}
//
.order-info {
.info-item {
display: flex;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
.item-lable {
display: flex;
align-items: center;
font-size: 24rpx;
color: #999;
margin-right: 30rpx;
}
.item-content {
flex: 1;
display: flex;
align-items: center;
font-size: 26rpx;
color: #333;
.act-copy {
margin-left: 20rpx;
padding: 2rpx 20rpx;
font-size: 22rpx;
color: #666;
border: 1rpx solid #c1c1c1;
border-radius: 16rpx;
}
}
}
}
//
.trade-info {
.info-item {
display: flex;
margin-bottom: 24rpx;
.item-lable {
font-size: 24rpx;
color: #999;
margin-right: 24rpx;
}
.item-content {
flex: 1;
font-size: 26rpx;
color: #333;
text-align: right;
}
}
.divider {
height: 1rpx;
background: #f1f1f1;
margin-bottom: 24rpx;
}
.trade-total {
display: flex;
justify-content: flex-end;
.goods-price {
margin-left: 12rpx;
vertical-align: bottom;
color: $uni-text-color-active;
.unit {
margin-right: -2rpx;
font-size: 24rpx;
}
}
}
}
/* 底部操作栏 */
.footer-fixed {
position: fixed;
bottom: var(--window-bottom);
left: 0;
right: 0;
z-index: 11;
box-shadow: 0 -4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
background: #fff;
// ios线
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
.btn-wrapper {
height: 106rpx;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 30rpx;
}
.btn-item {
min-width: 164rpx;
border-radius: 28rpx;
padding: 10rpx 24rpx;
font-size: 28rpx;
color: #383838;
text-align: center;
border: 1rpx solid #a8a8a8;
margin-left: 24rpx;
&.active {
color: #fff;
border: none;
background: linear-gradient(to right, #f9211c, #ff6335);
}
}
}
// -
.pay-popup {
padding: 24rpx;
.title {
font-size: 30rpx;
margin-bottom: 50rpx;
font-weight: bold;
text-align: center;
}
.pop-content {
min-height: 260rpx;
padding: 0 10rpx;
.pay-item {
padding: 20rpx 35rpx;
font-size: 28rpx;
border-bottom: 1rpx solid #f1f1f1;
&:last-child {
border-bottom: none;
}
.item-left_icon {
margin-right: 20rpx;
font-size: 32rpx;
&.wechat {
color: #00c800;
}
&.balance {
color: #ff9700;
}
}
}
}
}
//
</style>

202
pages/order/express/index.vue

@ -1,202 +0,0 @@
<template>
<view v-if="!isLoading" class="container">
<!-- 物流信息 -->
<view class="express i-card">
<view class="info-item">
<view class="item-lable">物流公司</view>
<view class="item-content">
<text>{{ express.express_name }}</text>
</view>
</view>
<view class="info-item">
<view class="item-lable">物流单号</view>
<view class="item-content">
<text>{{ express.express_no }}</text>
<view class="act-copy" @click.stop="handleCopy(express.express_no)">
<text>复制</text>
</view>
</view>
</view>
</view>
<!-- 物流动态 -->
<view class="logis-detail">
<view class="logis-item" :class="{ first: index === 0 }" v-for="(item, index) in express.list" :key="index">
<view class="logis-item-content">
<view class="logis-item-content__describe">
<text class="f-26">{{ item.context }}</text>
</view>
<view class="logis-item-content__time">
<text class="f-22">{{ item.ftime }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import * as OrderApi from '@/api/order'
export default {
data() {
return {
//
isLoading: true,
// ID
orderId: null,
//
express: {}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ orderId }) {
this.orderId = orderId
//
this.getExpress()
},
methods: {
//
getExpress() {
const app = this
app.isLoading = true
OrderApi.express(app.orderId)
.then(result => {
app.express = result.data.express
app.isLoading = false
})
},
//
handleCopy(value) {
const app = this
uni.setClipboardData({
data: value,
success() {
app.$toast('复制成功')
}
})
}
}
}
</script>
<style lang="scss" scoped>
//
.i-card {
background: #fff;
padding: 24rpx 24rpx;
// width: 94%;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
// margin: 0 auto 20rpx auto;
// border-radius: 20rpx;
}
//
.express {
// margin-top: 24rpx;
.info-item {
display: flex;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
.item-lable {
display: flex;
align-items: center;
font-size: 24rpx;
color: #999;
margin-right: 30rpx;
}
.item-content {
flex: 1;
display: flex;
align-items: center;
font-size: 26rpx;
color: #333;
.act-copy {
margin-left: 20rpx;
padding: 2rpx 20rpx;
font-size: 22rpx;
color: #666;
border: 1rpx solid #c1c1c1;
border-radius: 16rpx;
}
}
}
}
.logis-detail {
padding: 30rpx;
background-color: #fff;
.logis-item {
position: relative;
padding: 10px 0 10px 25px;
box-sizing: border-box;
border-left: 2px solid #ccc;
&.first {
border-left: 2px solid #f40;
&:after {
background: #f40;
}
.logis-item-content {
background: #ff6e39;
color: #fff;
&:after {
border-bottom-color: #ff6e39;
}
}
}
&:after {
content: ' ';
display: inline-block;
position: absolute;
left: -6px;
top: 30px;
width: 6px;
height: 6px;
border-radius: 10px;
background: #bdbdbd;
border: 2px solid #fff;
}
.logis-item-content {
position: relative;
background: #f9f9f9;
padding: 10rpx 20rpx;
box-sizing: border-box;
color: #666;
&:after {
content: '';
display: inline-block;
position: absolute;
left: -10px;
top: 18px;
border-left: 10px solid #fff;
border-bottom: 10px solid #f3f3f3;
}
}
}
}
</style>

596
pages/order/index.vue

@ -1,596 +0,0 @@
<template>
<view class="container">
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ native: true }" @down="downCallback" :up="upOption" @up="upCallback">
<!-- tab栏 -->
<u-tabs :list="tabs" :is-scroll="false" :current="curTab" active-color="#FA2209" :duration="0.2" @change="onChangeTab" />
<!-- 订单列表 -->
<view class="order-list">
<view class="order-item" v-for="(item, index) in list.data" :key="index">
<view class="item-top">
<view class="item-top-left">
<text class="order-time">{{ item.create_time }}</text>
</view>
<view class="item-top-right">
<text class="state-text">{{ item.state_text }}</text>
</view>
</view>
<!-- 商品列表 -->
<view class="goods-list" @click="handleTargetDetail(item.order_id)">
<view class="goods-item" v-for="(goods, idx) in item.goods" :key="idx">
<!-- 商品图片 -->
<view class="goods-image">
<image class="image" :src="goods.goods_image" mode="scaleToFill"></image>
</view>
<!-- 商品信息 -->
<view class="goods-content">
<view class="goods-title"><text class="twoline-hide">{{ goods.goods_name }}</text></view>
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in goods.goods_props" :key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
</view>
<!-- 交易信息 -->
<view class="goods-trade">
<view class="goods-price">
<text class="unit"></text>
<text class="value">{{ goods.is_user_grade ? goods.grade_goods_price : goods.goods_price }}</text>
</view>
<view class="goods-num">
<text>×{{ goods.total_num }}</text>
</view>
</view>
</view>
</view>
<!-- 订单合计 -->
<view class="order-total">
<text>{{ item.total_num }}件商品总金额</text>
<text class="unit"></text>
<text class="money">{{ item.pay_price }}</text>
</view>
<!-- 订单操作 -->
<view v-if="item.order_status != OrderStatusEnum.CANCELLED.value" class="order-handle">
<view class="btn-group clearfix">
<!-- 未支付取消订单 -->
<block v-if="item.pay_status == PayStatusEnum.PENDING.value">
<view class="btn-item" @click="onCancel(item.order_id)">取消</view>
</block>
<!-- 已支付进行中的订单 -->
<block v-if="item.order_status != OrderStatusEnum.APPLY_CANCEL.value">
<block v-if="item.pay_status == PayStatusEnum.SUCCESS.value && item.delivery_status == DeliveryStatusEnum.NOT_DELIVERED.value">
<view class="btn-item" @click="onCancel(item.order_id)">申请取消</view>
</block>
</block>
<!-- 已申请取消 -->
<view v-else class="f-28 col-8">取消申请中</view>
<!-- 未支付的订单 -->
<block v-if="item.pay_status == PayStatusEnum.PENDING.value">
<view class="btn-item active" @click="onPay(item.order_id)">去支付</view>
</block>
<!-- 确认收货 -->
<block v-if="item.delivery_status == DeliveryStatusEnum.DELIVERED.value && item.receipt_status == ReceiptStatusEnum.NOT_RECEIVED.value">
<view class="btn-item active" @click="onReceipt(item.order_id)">确认收货</view>
</block>
<!-- 订单评价 -->
<block v-if="item.order_status == OrderStatusEnum.COMPLETED.value && item.is_comment == 0">
<view class="btn-item" @click="handleTargetComment(item.order_id)">评价</view>
</block>
</view>
</view>
</view>
</view>
</mescroll-body>
<!-- 支付方式弹窗 -->
<u-popup v-model="showPayPopup" mode="bottom" border-radius="26" :closeable="true">
<view class="pay-popup">
<view class="title">请选择支付方式</view>
<view class="pop-content">
<!-- 微信支付 -->
<!-- #ifdef MP-WEIXIN -->
<view class="pay-item dis-flex flex-x-between" @click="onSelectPayType(PayTypeEnum.WECHAT.value)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon wechat">
<text class="iconfont icon-wechat-pay"></text>
</view>
<view class="item-left_text">
<text>{{ PayTypeEnum.WECHAT.name }}</text>
</view>
</view>
</view>
<!-- #endif -->
<!-- 余额支付 -->
<view class="pay-item dis-flex flex-x-between" @click="onSelectPayType(PayTypeEnum.BALANCE.value)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon balance">
<text class="iconfont icon-balance-pay"></text>
</view>
<view class="item-left_text">
<text>{{ PayTypeEnum.BALANCE.name }}</text>
</view>
</view>
</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import {
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum
} from '@/common/enum/order'
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
import * as OrderApi from '@/api/order'
import { wxPayment } from '@/core/app'
//
const pageSize = 15
// tab
const tabs = [{
name: `全部`,
value: 'all'
}, {
name: `待支付`,
value: 'payment'
}, {
name: `待发货`,
value: 'delivery'
}, {
name: `待收货`,
value: 'received'
}, {
name: `待评价`,
value: 'comment'
}]
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
//
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum,
//
options: { dataType: 'all' },
// tab
tabs,
//
curTab: 0,
//
list: getEmptyPaginateObj(),
//
upOption: {
//
auto: true,
// ; 10
page: { size: pageSize },
// 4
noMoreSize: 4,
//
empty: {
tip: '亲,暂无订单记录'
}
},
// onShow
canReset: false,
//
showPayPopup: false
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
//
this.initCurTab(options)
// :
uni.$on('syncRefresh', canReset => {
this.canReset = canReset
})
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
this.canReset && this.onRefreshList()
this.canReset = false
},
/**
* 生命周期函数--监听页面的卸载
*/
onUnload() {
//
uni.$off('syncRefresh')
},
methods: {
//
initCurTab(options) {
const app = this
if (options.dataType) {
const index = app.tabs.findIndex(item => item.value == options.dataType)
app.curTab = index > -1 ? index : 0
}
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
//
app.getOrderList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
//
getOrderList(pageNo = 1) {
const app = this
return new Promise((resolve, reject) => {
OrderApi.list({ dataType: app.getTabValue(), page: pageNo }, { load: false })
.then(result => {
//
const newList = app.initList(result.data.list)
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
})
},
//
initList(newList) {
newList.data.forEach(item => {
item.total_num = 0
item.goods.forEach(goods => {
item.total_num += goods.total_num
})
})
return newList
},
//
getTabValue() {
return this.tabs[this.curTab].value
},
//
onChangeTab(index) {
const app = this
//
app.curTab = index
//
app.onRefreshList()
},
//
onRefreshList() {
this.list = getEmptyPaginateObj()
setTimeout(() => {
this.mescroll.resetUpScroll()
}, 120)
},
//
onCancel(orderId) {
const app = this
uni.showModal({
title: '友情提示',
content: '确认要取消该订单吗?',
success(o) {
if (o.confirm) {
OrderApi.cancel(orderId)
.then(result => {
//
app.$toast(result.message)
//
app.onRefreshList()
})
}
}
});
},
//
onReceipt(orderId) {
const app = this
uni.showModal({
title: '友情提示',
content: '确认收到商品了吗?',
success(o) {
if (o.confirm) {
OrderApi.receipt(orderId)
.then(result => {
//
app.$success(result.message)
//
app.onRefreshList()
})
}
}
});
},
//
onPay(orderId) {
// id
this.payOrderId = orderId
//
this.showPayPopup = true
},
//
onSelectPayType(payType) {
const app = this
//
this.showPayPopup = false
//
OrderApi.pay(app.payOrderId, payType)
.then(result => app.onSubmitCallback(result))
},
//
onSubmitCallback(result) {
const app = this
//
if (result.data.pay_type == PayTypeEnum.WECHAT.value) {
wxPayment(result.data.payment)
.then(() => {
app.$success('支付成功')
setTimeout(() => {
app.onRefreshList()
}, 1500)
})
.catch(err => {
app.$error('订单未支付')
})
.finally(() => {
app.disabled = false
})
}
//
if (result.data.pay_type == PayTypeEnum.BALANCE.value) {
app.$success('支付成功')
app.disabled = false
setTimeout(() => {
app.onRefreshList()
}, 1500)
}
},
//
handleTargetDetail(orderId) {
this.$navTo('pages/order/detail', { orderId })
},
//
handleTargetComment(orderId) {
this.$navTo('pages/order/comment/index', { orderId })
}
},
}
</script>
<style lang="scss" scoped>
//
.order-item {
margin: 20rpx auto 20rpx auto;
padding: 30rpx 30rpx;
width: 94%;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
border-radius: 16rpx;
background: #fff;
}
//
.item-top {
display: flex;
justify-content: space-between;
font-size: 26rpx;
margin-bottom: 40rpx;
.order-time {
color: #777;
}
.state-text {
color: $uni-text-color-active;
}
}
//
.goods-list {
//
.goods-item {
display: flex;
margin-bottom: 40rpx;
//
.goods-image {
width: 180rpx;
height: 180rpx;
.image {
display: block;
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
//
.goods-content {
flex: 1;
padding-left: 16rpx;
padding-top: 16rpx;
.goods-title {
font-size: 26rpx;
max-height: 76rpx;
}
.goods-props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
.goods-props-item {
display: inline-block;
margin-right: 14rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
width: auto;
}
}
}
//
.goods-trade {
padding-top: 16rpx;
width: 150rpx;
text-align: right;
color: $uni-text-color-grey;
font-size: 26rpx;
.goods-price {
vertical-align: bottom;
margin-bottom: 16rpx;
.unit {
margin-right: -2rpx;
font-size: 24rpx;
}
}
}
}
}
//
.order-total {
font-size: 26rpx;
vertical-align: bottom;
text-align: right;
height: 40rpx;
margin-bottom: 30rpx;
.unit {
margin-left: 8rpx;
margin-right: -2rpx;
font-size: 26rpx;
}
.money {
font-size: 28rpx;
}
}
//
.order-handle {
.btn-group {
.btn-item {
border-radius: 10rpx;
padding: 6rpx 20rpx;
margin-left: 15rpx;
font-size: 28rpx;
float: right;
color: #383838;
border: 1rpx solid #a8a8a8;
&:last-child {
margin-left: 0;
}
&.active {
color: $uni-text-color-active;
border: 1rpx solid $uni-text-color-active;
}
}
}
}
// -
.pay-popup {
padding: 24rpx;
.title {
font-size: 30rpx;
margin-bottom: 40rpx;
font-weight: bold;
text-align: center;
}
.pop-content {
min-height: 260rpx;
padding: 0 10rpx;
.pay-item {
padding: 24rpx 35rpx;
font-size: 28rpx;
border-bottom: 1rpx solid #f1f1f1;
&:last-child {
border-bottom: none;
}
.item-left_icon {
margin-right: 20rpx;
font-size: 32rpx;
&.wechat {
color: #00c800;
}
&.balance {
color: #ff9700;
}
}
}
}
}
</style>

127
pages/points/log.vue

@ -1,127 +0,0 @@
<template>
<view class="container">
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption"
@up="upCallback">
<view class="log-list">
<view v-for="(item, index) in list.data" :key="index" class="log-item">
<view class="item-left flex-box">
<view class="rec-status">
<text>{{ item.describe }}</text>
</view>
<view class="rec-time">
<text>{{ item.create_time }}</text>
</view>
</view>
<view class="item-right" :class="[item.value > 0 ? 'col-green' : 'col-6']">
<text>{{ item.value > 0 ? '+' : '' }}{{ item.value }}</text>
</view>
</view>
</view>
</mescroll-body>
</view>
</template>
<script>
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import * as LogApi from '@/api/points/log'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
const pageSize = 15
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
//
list: getEmptyPaginateObj(),
//
upOption: {
//
auto: true,
// ; 10
page: { size: pageSize },
// 12
noMoreSize: 12,
//
empty: {
tip: '亲,暂无相关数据'
}
}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {},
methods: {
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
//
app.getLogList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
//
getLogList(pageNo = 1) {
const app = this
return new Promise((resolve, reject) => {
LogApi.list({ page: pageNo })
.then(result => {
//
const newList = result.data.list
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
})
}
}
}
</script>
<style lang="scss" scoped>
page,
.container {
background: #fff;
}
.log-list {
padding: 0 30rpx;
}
.log-item {
font-size: 28rpx;
padding: 20rpx 20rpx;
line-height: 1.8;
border-bottom: 1rpx solid rgb(238, 238, 238);
display: flex;
justify-content: center;
align-items: center;
}
.rec-status {
color: #333;
.rec-time {
color: rgb(160, 160, 160);
font-size: 26rpx;
}
}
</style>

429
pages/refund/apply.vue

@ -1,429 +0,0 @@
<template>
<view v-if="!isLoading" class="container">
<!-- 商品详情 -->
<view class="goods-detail b-f dis-flex flex-dir-row">
<view class="left">
<image class="goods-image" :src="goods.goods_image"></image>
</view>
<view class="right dis-flex flex-box flex-dir-column flex-x-around">
<view class="goods-name">
<text class="twoline-hide">{{ goods.goods_name }}</text>
</view>
<view class="dis-flex col-9 f-24">
<view class="flex-box">
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in goods.goods_props" :key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
</view>
<text class="t-r">×{{ goods.total_num }}</text>
</view>
</view>
</view>
<!-- 服务类型 -->
<view class="row-service b-f m-top20">
<view class="row-title">服务类型</view>
<view class="service-switch dis-flex">
<view class="switch-item" v-for="(item, index) in RefundTypeEnum.data" :key="index" :class="{ active: formData.type == item.value }"
@click="onSwitchService(item.value)">{{ item.name }}</view>
</view>
</view>
<!-- 申请原因 -->
<view class="row-textarea b-f m-top20">
<view class="row-title">申请原因</view>
<view class="content">
<textarea class="textarea" v-model="formData.content" maxlength="2000" placeholder="请详细填写申请原因,注意保持商品的完好,建议您先与卖家沟通"
placeholderStyle="color:#ccc"></textarea>
</view>
</view>
<!-- 退款金额 -->
<view v-if="formData.type == RefundTypeEnum.RETURN.value" class="row-money b-f m-top20 dis-flex">
<view class="row-title">退款金额</view>
<view class="money col-m">{{ goods.total_pay_price }}</view>
</view>
<!-- 上传凭证 -->
<view class="row-voucher b-f m-top20">
<view class="row-title">上传凭证 (最多6张)</view>
<view class="image-list">
<!-- 图片列表 -->
<view class="image-preview" v-for="(image, imageIndex) in imageList" :key="imageIndex">
<text class="image-delete iconfont icon-shanchu" @click="deleteImage(imageIndex)"></text>
<image class="image" mode="aspectFill" :src="image.path"></image>
</view>
<!-- 上传图片 -->
<view v-if="imageList.length < maxImageLength" class="image-picker" @click="chooseImage()">
<text class="choose-icon iconfont icon-camera"></text>
<text class="choose-text">上传图片</text>
</view>
</view>
</view>
<!-- 底部操作按钮 -->
<view class="footer-fixed">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">确认提交</view>
</view>
</view>
</view>
</template>
<script>
import { RefundTypeEnum } from '@/common/enum/order/refund'
import * as UploadApi from '@/api/upload'
import * as RefundApi from '@/api/refund'
const maxImageLength = 6
export default {
data() {
return {
//
RefundTypeEnum,
//
isLoading: true,
// id
orderGoodsId: null,
//
goods: {},
//
formData: {
// ID
images: [],
//
type: 10,
//
content: ''
},
//
imageList: [],
//
maxImageLength,
//
disabled: false
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ orderGoodsId }) {
this.orderGoodsId = orderGoodsId
//
this.getGoodsDetail()
},
methods: {
//
getGoodsDetail() {
const app = this
app.isLoading = true
RefundApi.goods(app.orderGoodsId)
.then(result => {
app.goods = result.data.goods
app.isLoading = false
})
},
//
onSwitchService(value) {
this.formData.type = value
},
//
chooseImage() {
const app = this
const oldImageList = app.imageList
//
uni.chooseImage({
count: maxImageLength - oldImageList.length,
sizeType: ['original', 'compressed'], //
sourceType: ['album', 'camera'], //
success({ tempFiles }) {
// tempFiles = [{path:'xxx', size:100}]
app.imageList = oldImageList.concat(tempFiles)
}
});
},
//
deleteImage(imageIndex) {
this.imageList.splice(imageIndex, 1)
},
//
handleSubmit() {
const app = this
const { imageList } = app
//
if (app.disabled === true) return false
//
app.disabled = true
//
if (imageList.length > 0) {
app.uploadFile()
.then(() => app.onSubmit())
.catch(err => {
app.disabled = false
if (err.statusCode !== 0) {
app.$toast(err.errMsg)
}
console.log('err', err)
})
} else {
app.onSubmit()
}
},
//
onSubmit() {
const app = this
RefundApi.apply(app.orderGoodsId, app.formData)
.then(result => {
app.$toast(result.message)
setTimeout(() => {
app.disabled = false
uni.navigateBack()
}, 1500)
})
.catch(err => app.disabled = false)
},
//
uploadFile() {
const app = this
const { imageList } = app
//
return new Promise((resolve, reject) => {
if (imageList.length > 0) {
UploadApi.image(imageList)
.then(fileIds => {
app.formData.images = fileIds
resolve(fileIds)
})
.catch(reject)
} else {
resolve()
}
})
}
}
}
</script>
<style lang="scss" scoped>
.container {
// ios线
padding-bottom: calc(constant(safe-area-inset-bottom) + 140rpx);
padding-bottom: calc(env(safe-area-inset-bottom) + 140rpx);
}
.row-title {
color: #888;
margin-bottom: 20rpx;
}
//
.goods-detail {
padding: 24rpx 20rpx;
.left {
.goods-image {
display: block;
width: 150rpx;
height: 150rpx;
}
}
.right {
padding-left: 20rpx;
}
.goods-props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
.goods-props-item {
display: inline-block;
margin-right: 14rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
width: auto;
}
}
}
/* 服务类型 */
.row-service {
padding: 24rpx 20rpx;
}
.service-switch {
.switch-item {
padding: 6rpx 30rpx;
margin-right: 25rpx;
border-radius: 10rpx;
border: 1px solid rgb(177, 177, 177);
color: #888;
&.active {
color: #fc1e56;
border: 1px solid #fc1e56;
}
}
}
/* 申请原因 */
.row-textarea {
padding: 24rpx 20rpx;
.textarea {
width: 100%;
height: 220rpx;
padding: 12rpx;
border: 1rpx solid #e8e8e8;
border-radius: 5rpx;
box-sizing: border-box;
font-size: 26rpx;
}
}
/* 退款金额 */
.row-money {
padding: 24rpx 20rpx;
.row-title {
margin-bottom: 0;
margin-right: 30rpx;
}
}
//
.row-voucher {
padding: 24rpx 20rpx;
.image-list {
padding: 0 20rpx;
margin-top: 20rpx;
margin-bottom: -20rpx;
&:after {
clear: both;
content: " ";
display: table;
}
.image {
display: block;
width: 100%;
height: 100%;
}
.image-picker,
.image-preview {
width: 184rpx;
height: 184rpx;
margin-right: 30rpx;
margin-bottom: 30rpx;
float: left;
&:nth-child(3n+0) {
margin-right: 0;
}
}
.image-picker {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 1rpx dashed #ccc;
color: #ccc;
.choose-icon {
font-size: 48rpx;
margin-bottom: 6rpx;
}
.choose-text {
font-size: 24rpx;
}
}
.image-preview {
position: relative;
.image-delete {
position: absolute;
top: -15rpx;
right: -15rpx;
height: 42rpx;
width: 42rpx;
line-height: 42rpx;
background: rgba(0, 0, 0, 0.64);
border-radius: 50%;
color: #fff;
font-weight: bolder;
font-size: 22rpx;
z-index: 10;
text-align: center;
}
}
}
}
//
.footer-fixed {
position: fixed;
bottom: var(--window-bottom);
left: 0;
right: 0;
z-index: 11;
box-shadow: 0 -4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
background: #fff;
// ios线
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
.btn-wrapper {
height: 120rpx;
display: flex;
align-items: center;
padding: 0 20rpx;
}
.btn-item {
flex: 1;
font-size: 28rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
color: #fff;
border-radius: 50rpx;
}
.btn-item-main {
background: linear-gradient(to right, #f9211c, #ff6335);
//
&.disabled {
background: #ff9779;
}
}
}
</style>

483
pages/refund/detail.vue

@ -1,483 +0,0 @@
<template>
<view v-if="!isLoading" class="container p-bottom">
<!-- 顶部状态栏 -->
<view class="detail-header dis-flex flex-y-center">
<view class="header-backdrop">
<image class="image" src="/static/order/refund-bg.png"></image>
</view>
<view class="header-state">
<text class="f-32 col-f">{{ detail.state_text }}</text>
</view>
</view>
<!-- 商品详情 -->
<view class="detail-goods b-f m-top20 dis-flex flex-dir-row" @click="onGoodsDetail(detail.orderGoods.goods_id)">
<view class="left">
<image class="goods-image" :src="detail.orderGoods.goods_image"></image>
</view>
<view class="right dis-flex flex-box flex-dir-column flex-x-around">
<view class="goods-name">
<text class="twoline-hide">{{ detail.orderGoods.goods_name }}</text>
</view>
<view class="dis-flex col-9 f-24">
<view class="flex-box">
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in detail.orderGoods.goods_props" :key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
</view>
<text class="t-r">×{{ detail.orderGoods.total_num }}</text>
</view>
</view>
</view>
<!-- 商品金额 -->
<view class="detail-order b-f row-block">
<view class="item dis-flex flex-x-end flex-y-center">
<text class="">商品金额</text>
<text class="col-m">{{ detail.orderGoods.total_pay_price }}</text>
</view>
</view>
<!-- 已退款金额 -->
<view v-if="detail.status == RefundStatusEnum.COMPLETED.value && detail.type == 10"
class="detail-order b-f row-block dis-flex flex-x-end flex-y-center">
<text class="">已退款金额</text>
<text class="col-m">{{ detail.refund_money }}</text>
</view>
<!-- 售后信息 -->
<view v-if="detail.status == RefundStatusEnum.REJECTED.value" class="detail-refund b-f m-top20">
<view class="detail-refund__row dis-flex">
<view class="text">
<text>售后类型</text>
</view>
<view class="flex-box">
<text>{{ RefundTypeEnum[detail.type].name }}</text>
</view>
</view>
<view class="detail-refund__row dis-flex">
<view class="text">
<text>申请原因</text>
</view>
<view class="flex-box">
<text>{{ detail.apply_desc }}</text>
</view>
</view>
<view v-if="detail.images.length > 0" class="detail-refund__row dis-flex">
<view class="text">
<text>申请凭证</text>
</view>
<view class="image-list flex-box">
<view class="image-preview" v-for="(item, index) in detail.images" :key="index">
<image class="image" mode="aspectFill" :src="item.image_url" @click="handlePreviewImages(index)"></image>
</view>
</view>
</view>
</view>
<!-- 售后信息 -->
<view v-if="detail.status.value == RefundStatusEnum.REJECTED.value" class="detail-refund b-f m-top20">
<view class="detail-refund__row dis-flex">
<view class="text">
<text class="col-m">拒绝原因</text>
</view>
<view class="flex-box">
<text>{{ detail.refuse_desc }}</text>
</view>
</view>
</view>
<!-- 退货物流信息 -->
<view v-if="detail.audit_status == AuditStatusEnum.REVIEWED.value && detail.is_user_send"
class="detail-address b-f m-top20">
<view class="detail-address__row address-title">
<text class="col-m">退货物流信息</text>
</view>
<view class="detail-address__row address-details">
<view class="address-details__row">
<text>物流公司{{ detail.express.express_name }}</text>
</view>
<view class="address-details__row">
<text>物流单号{{ detail.express_no }}</text>
</view>
<!-- <view class="address-details__row">
<text>发货状态{{ detail.is_user_send ? '已发货' : '未发货' }}</text>
</view> -->
<view class="address-details__row">
<text>发货时间{{ detail.send_time }}</text>
</view>
</view>
</view>
<!-- 商家收货地址 -->
<view v-if="detail.audit_status == AuditStatusEnum.REVIEWED.value" class="detail-address b-f m-top20">
<view class="detail-address__row address-title">
<text class="col-m">商家退货地址</text>
</view>
<view class="detail-address__row address-details">
<view class="address-details__row">
<text>收货人{{ detail.address.name }}</text>
</view>
<view class="address-details__row">
<text>联系电话{{ detail.address.phone }}</text>
</view>
<view class="address-details__row dis-flex">
<view class="text">
<text>详细地址</text>
</view>
<view class="address flex-box">
<text class="region" v-for="(region, idx) in detail.address.region" :key="idx">{{ region }}</text>
<text class="detail">{{ detail.address.detail }}</text>
</view>
</view>
</view>
<view class="detail-address__row address-tips">
<view class="f-26 col-9">
<text>· 未与卖家协商一致情况下请勿寄到付或平邮</text>
</view>
<view class="f-26 col-9">
<text>· 请填写真实有效物流信息</text>
</view>
</view>
</view>
<!-- 填写物流信息 -->
<form
v-if="detail.type == RefundTypeEnum.RETURN.value && detail.audit_status == AuditStatusEnum.REVIEWED.value && !detail.is_user_send"
@submit="onSubmit()">
<view class="detail-express b-f m-top20">
<view class="form-group dis-flex flex-y-center">
<view class="field">物流公司</view>
<view class="flex-box">
<picker mode="selector" :range="expressList" range-key="express_name" :value="expressIndex"
@change="onChangeExpress">
<text v-if="expressIndex > -1">{{ expressList[expressIndex].express_name }}</text>
<text v-else class="col-80">请选择物流公司</text>
</picker>
</view>
</view>
<view class="form-group dis-flex flex-y-center">
<view class="field">物流单号</view>
<view class="flex-box">
<input class="input" v-model="formData.expressNo" placeholder="请填写物流单号"></input>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn-wrapper">
<button class="btn-item btn-item-main btn-normal" :class="{ disabled }" formType="submit">确认发货</button>
</view>
</view>
</form>
</view>
</template>
<script>
import { AuditStatusEnum, RefundStatusEnum, RefundTypeEnum } from '@/common/enum/order/refund'
import * as RefundApi from '@/api/refund'
import * as ExpressApi from '@/api/express'
export default {
data() {
return {
//
AuditStatusEnum,
RefundStatusEnum,
RefundTypeEnum,
//
isLoading: true,
// ID
orderRefundId: null,
//
detail: {},
//
expressList: [],
//
formData: {
// ID
expressId: null,
//
expressNo: ''
},
//
expressIndex: -1,
//
disabled: false
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ orderRefundId }) {
// ID
this.orderRefundId = orderRefundId
//
this.getPageData()
},
methods: {
//
getPageData() {
const app = this
app.isLoading = true
Promise.all([app.getRefundDetail(), app.getExpressList()])
.then(result => {
app.isLoading = false
})
},
//
getRefundDetail() {
const app = this
return new Promise((resolve, reject) => {
RefundApi.detail(app.orderRefundId)
.then(result => {
app.detail = result.data.detail
resolve()
})
.catch(reject)
})
},
//
getExpressList() {
const app = this
return new Promise((resolve, reject) => {
ExpressApi.list()
.then(result => {
app.expressList = result.data.list
resolve()
})
.catch(reject)
})
},
//
onGoodsDetail(goodsId) {
this.$navTo('pages/goods/detail', { goodsId })
},
//
handlePreviewImages(index) {
const { detail: { images } } = this
const imageUrls = images.map(item => item.image_url)
uni.previewImage({
current: imageUrls[index],
urls: imageUrls
})
},
//
onChangeExpress(e) {
const expressIndex = e.detail.value
const { expressList } = this
this.expressIndex = expressIndex
this.formData.expressId = expressList[expressIndex].express_id
},
//
onSubmit() {
const app = this
//
if (app.disabled === true) return false
//
app.disabled = true
//
RefundApi.delivery(app.orderRefundId, app.formData)
.then(result => {
app.$toast(result.message)
setTimeout(() => {
app.disabled = false
uni.navigateBack()
}, 1500)
})
.catch(err => app.disabled = false)
}
}
}
</script>
<style lang="scss" scoped>
//
.detail-header {
position: relative;
width: 100%;
height: 140rpx;
.header-backdrop {
width: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 0;
.image {
display: block;
width: 100%;
height: 140rpx;
}
}
}
.header-state {
z-index: 1;
padding: 0 50rpx;
}
/* 商品详情 */
.detail-goods {
padding: 24rpx 20rpx;
.left {
.goods-image {
display: block;
width: 150rpx;
height: 150rpx;
}
}
.right {
padding-left: 20rpx;
}
.goods-props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
.goods-props-item {
display: inline-block;
margin-right: 14rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
width: auto;
}
}
}
.detail-order {
padding: 15rpx 20rpx;
font-size: 26rpx;
.item {
margin-bottom: 10rpx;
&:last-child {
margin-bottom: 0;
}
}
}
/* 售后详情 */
.detail-refund {
padding: 15rpx 20rpx;
}
.detail-refund__row {
margin: 20rpx 0;
}
/* 申请凭证 */
.image-list {
margin-bottom: -15rpx;
.image-preview {
margin: 0 15rpx 15rpx 0;
float: left;
.image {
display: block;
width: 180rpx;
height: 180rpx;
}
&:nth-child(3n+0) {
margin-right: 0;
}
}
}
/* 商家收货地址 */
.detail-address {
padding: 20rpx 34rpx;
}
.address-details {
padding: 8rpx 0;
border-bottom: 1px solid #eee;
.address-details__row {
margin: 14rpx 0;
}
}
.address-tips {
margin-top: 16rpx;
line-height: 46rpx;
}
.detail-address__row {
// margin: 18rpx 0;
}
/* 填写物流信息 */
.detail-express {
padding: 10rpx 30rpx;
}
.form-group {
height: 60rpx;
margin: 14rpx 0;
.input {
height: 100%;
font-size: 28rpx;
}
}
/* 底部操作栏 */
.footer {
margin-top: 60rpx;
.btn-wrapper {
height: 100%;
display: flex;
align-items: center;
padding: 0 20rpx;
}
.btn-item {
flex: 1;
font-size: 28rpx;
height: 80rpx;
color: #fff;
border-radius: 50rpx;
display: flex;
justify-content: center;
align-items: center;
}
.btn-item-main {
background: linear-gradient(to right, #f9211c, #ff6335);
//
&.disabled {
background: #ff9779;
}
}
}
</style>

263
pages/refund/index.vue

@ -1,263 +0,0 @@
<template>
<view class="container">
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ native: true }" @down="downCallback"
:up="upOption" @up="upCallback">
<!-- tab栏 -->
<u-tabs :list="tabs" :is-scroll="false" :current="curTab" active-color="#FA2209" :duration="0.2"
@change="onChangeTab" />
<!-- 退款/售后单 -->
<view class="widget-list">
<view class="widget-detail" v-for="(item, index) in list.data" :key="index">
<view class="row-block dis-flex flex-y-center">
<view class="flex-box">{{ item.create_time }}</view>
<view class="flex-box t-r">
<text class="col-m">{{ item.state_text }}</text>
</view>
</view>
<view class="detail-goods row-block dis-flex" @click.stop="handleTargetDetail(item.order_refund_id)">
<view class="goods-image">
<image class="image" :src="item.orderGoods.goods_image" mode="aspectFit"></image>
</view>
<view class="goods-right flex-box">
<view class="goods-name">
<text class="twoline-hide">{{ item.orderGoods.goods_name }}</text>
</view>
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in item.orderGoods.goods_props" :key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
<view class="goods-num t-r">
<text class="f-26 col-8">×{{ item.orderGoods.total_num }}</text>
</view>
</view>
</view>
<view class="detail-order row-block">
<view class="item dis-flex flex-x-end flex-y-center">
<text class="">付款金额</text>
<text class="col-m">{{ item.orderGoods.total_pay_price }}</text>
</view>
</view>
<view class="detail-operate row-block dis-flex flex-x-end flex-y-center">
<view class="detail-btn btn-detail" @click.stop="handleTargetDetail(item.order_refund_id)">查看详情</view>
</view>
</view>
</view>
</mescroll-body>
</view>
</template>
<script>
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
import * as RefundApi from '@/api/refund'
//
const pageSize = 15
// tab
const tabs = [{
name: '全部',
value: -1
}, {
name: '待处理',
value: 0
}]
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
//
list: getEmptyPaginateObj(),
// tabs
tabs,
//
curTab: 0,
//
upOption: {
//
auto: true,
// ; 10
page: { size: pageSize },
// 2
noMoreSize: 2,
//
empty: {
tip: '亲,暂无售后单记录'
}
},
// onShow
canReset: false,
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
this.canReset && this.onRefreshList()
// this.canReset = true
},
methods: {
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
//
app.getRefundList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
// 退/
getRefundList(pageNo = 1) {
const app = this
return new Promise((resolve, reject) => {
RefundApi.list({ state: app.getTabValue(), page: pageNo }, { load: false })
.then(result => {
//
const newList = result.data.list
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
})
},
//
onChangeTab(index) {
const app = this
//
app.curTab = index
//
app.onRefreshList()
},
//
onRefreshList() {
this.list = getEmptyPaginateObj()
setTimeout(() => {
this.mescroll.resetUpScroll()
}, 120)
},
//
getTabValue() {
return this.tabs[this.curTab].value
},
//
handleTargetDetail(orderRefundId) {
this.$navTo('pages/refund/detail', { orderRefundId })
},
}
}
</script>
<style lang="scss" scoped>
.widget-detail {
box-sizing: border-box;
background: #fff;
margin-bottom: 20rpx;
.row-block {
padding: 0 20rpx;
min-height: 70rpx;
}
.detail-goods {
padding: 20rpx;
background: #f9f9f9;
.goods-image {
margin-right: 20rpx;
.image {
display: block;
width: 200rpx;
height: 200rpx;
}
}
.goods-right {
padding: 15rpx 0;
}
.goods-name {
margin-bottom: 10rpx;
}
.goods-props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
.goods-props-item {
display: inline-block;
margin-right: 14rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
width: auto;
}
}
}
.detail-operate {
padding-bottom: 20rpx;
.detail-btn {
border-radius: 4px;
border: 1rpx solid #ccc;
padding: 8rpx 20rpx;
font-size: 28rpx;
color: #555;
margin-left: 10rpx;
}
}
.detail-order {
padding: 10rpx 20rpx;
font-size: 26rpx;
line-height: 50rpx;
height: 50rpx;
.item {
margin-bottom: 10rpx;
&:last-child {
margin-bottom: 0;
}
}
}
}
</style>

227
pages/search/index.vue

@ -1,227 +0,0 @@
<template>
<view class="container">
<view class="search-wrapper">
<view class="search-input">
<view class="search-input-wrapper">
<view class="left">
<text class="search-icon iconfont icon-search"></text>
</view>
<view class="right">
<input v-model="searchValue" class="input" focus="true" placeholder="请输入您搜索的商品" type="text"></input>
</view>
</view>
</view>
<view class="search-button">
<view class="button" @click="onSearch">搜索</view>
</view>
</view>
<view class="history" v-if="historySearch.length">
<view class="his-head">
<text class="title">最近搜索</text>
<text class="icon iconfont icon-delete" @click="clearSearch"></text>
</view>
<view class="his-list">
<view class="his-item" v-for="(val, index) in historySearch" :key="index">
<view class="history-button" @click="handleQuick(val)">{{ val }}</view>
</view>
</view>
</view>
<!-- </view> -->
</view>
</template>
<script>
const HISTORY_SEARCH = 'historySearch'
export default {
data() {
return {
historySearch: [],
searchValue: ''
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
//
this.historySearch = this.getHistorySearch()
},
methods: {
/**
* 获取历史搜索
*/
getHistorySearch() {
return uni.getStorageSync(HISTORY_SEARCH) || []
},
/**
* 搜索提交
*/
onSearch() {
const { searchValue } = this
if (searchValue) {
//
this.setHistory(searchValue)
//
this.$navTo('pages/goods/list', { search: searchValue })
}
},
/**
* 记录历史搜索
*/
setHistory(searchValue) {
const data = this.getHistorySearch()
const index = data.indexOf(searchValue)
index > -1 && data.splice(index, 1)
data.unshift(searchValue)
this.historySearch = data
this.onUpdateStorage()
},
/**
* 清空最近搜索记录
*/
clearSearch() {
this.historySearch = []
this.onUpdateStorage()
},
/**
* 更新历史搜索缓存
* @param {Object} data
*/
onUpdateStorage(data) {
uni.setStorageSync(HISTORY_SEARCH, this.historySearch)
},
/**
* 跳转到最近搜索
*/
handleQuick(search) {
this.$navTo('pages/goods/list', { search })
}
}
}
</script>
<style lang="scss" scoped>
.container {
padding: 20rpx;
min-height: 100vh;
background: #f7f7f7;
}
.search-wrapper {
display: flex;
height: 64rpx;
}
//
.search-input {
width: 80%;
background: #fff;
border-radius: 10rpx 0 0 10rpx;
box-sizing: border-box;
overflow: hidden;
.search-input-wrapper {
display: flex;
.left {
display: flex;
width: 60rpx;
justify-content: center;
align-items: center;
.search-icon {
display: block;
color: #b4b4b4;
font-size: 28rpx;
}
}
.right {
flex: 1;
input {
font-size: 28rpx;
height: 64rpx;
display: flex;
align-items: center;
.input-placeholder {
color: #aba9a9;
}
}
}
}
}
//
.search-button {
width: 20%;
box-sizing: border-box;
.button {
height: 64rpx;
font-size: 28rpx;
border-radius: 0 10rpx 10rpx 0;
background: #fa2209;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
}
//
.history {
.his-head {
font-size: 28rpx;
padding: 50rpx 0 0 0;
color: #777;
.icon {
float: right;
}
}
.his-list {
padding: 20rpx 0;
overflow: hidden;
.his-item {
width: 33.3%;
float: left;
padding: 10rpx;
box-sizing: border-box;
.history-button {
text-align: center;
padding: 14rpx;
line-height: 30rpx;
border-radius: 100rpx;
background: #fff;
font-size: 26rpx;
border: 1rpx solid #efefef;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
</style>

127
pages/wallet/balance/log.vue

@ -1,127 +0,0 @@
<template>
<view class="container">
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption"
@up="upCallback">
<view class="log-list">
<view v-for="(item, index) in list.data" :key="index" class="log-item">
<view class="item-left flex-box">
<view class="rec-status">
<text>{{ item.describe }}</text>
</view>
<view class="rec-time">
<text>{{ item.create_time }}</text>
</view>
</view>
<view class="item-right">
<text>{{ item.money > 0 ? '+' : '' }}{{ item.money }}</text>
</view>
</view>
</view>
</mescroll-body>
</view>
</template>
<script>
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import * as LogApi from '@/api/balance/log'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
const pageSize = 15
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
//
list: getEmptyPaginateObj(),
//
upOption: {
//
auto: true,
// ; 10
page: { size: pageSize },
// 12
noMoreSize: 12,
//
empty: {
tip: '亲,暂无账单明细'
}
}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {},
methods: {
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
//
app.getLogList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
//
getLogList(pageNo = 1) {
const app = this
return new Promise((resolve, reject) => {
LogApi.list({ page: pageNo })
.then(result => {
//
const newList = result.data.list
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
})
}
}
}
</script>
<style lang="scss" scoped>
page,
.container {
background: #fff;
}
.log-list {
padding: 0 30rpx;
}
.log-item {
font-size: 28rpx;
padding: 20rpx 20rpx;
line-height: 1.8;
border-bottom: 1rpx solid rgb(238, 238, 238);
display: flex;
justify-content: center;
align-items: center;
}
.rec-status {
color: #333;
.rec-time {
color: rgb(160, 160, 160);
font-size: 26rpx;
}
}
</style>

167
pages/wallet/index.vue

@ -1,167 +0,0 @@
<template>
<view class="container" v-if="!isLoading">
<view class="space-upper">
<view class="wallet-image">
<image src="/static/wallet.png" mode="widthFix"></image>
</view>
<view class="wallet-account">
<view class="wallet-account_balance">
<text>{{ userInfo.balance }}</text>
</view>
<view class="wallet-account_lable">
<text>账户余额()</text>
</view>
</view>
</view>
<view class="space-lower">
<view v-if="setting.is_entrance" class="space-lower_item btn-recharge">
<view class="btn-submit" @click="onTargetRecharge()"> </view>
</view>
<view class="space-lower_item item-lable dis-flex flex-x-around">
<view class="lable-text" @click="onTargetRechargeOrder()">
<text>充值记录</text>
</view>
<view class="lable-text" @click="onTargetBalanceLog()">
<text>账单详情</text>
</view>
</view>
</view>
</view>
</template>
<script>
import * as UserApi from '@/api/user'
import SettingModel from '@/common/model/Setting'
import SettingKeyEnum from '@/common/enum/setting/Key'
export default {
data() {
return {
//
isLoading: true,
//
userInfo: {},
//
setting: {},
}
},
/**
* 生命周期函数--监听页面加载
*/
onShow(options) {
//
this.getPageData()
},
methods: {
//
getPageData() {
const app = this
app.isLoading = true
Promise.all([app.getUserInfo(), app.getSetting()])
.then(() => app.isLoading = false)
},
//
getUserInfo() {
const app = this
return new Promise((resolve, reject) => {
UserApi.info()
.then(result => {
app.userInfo = result.data.userInfo
resolve(app.userInfo)
})
})
},
//
getSetting() {
const app = this
return new Promise((resolve, reject) => {
SettingModel.item(SettingKeyEnum.RECHARGE.value, false)
.then(data => {
app.setting = data
resolve(data)
})
})
},
//
onTargetRecharge() {
this.$navTo('pages/wallet/recharge/index')
},
//
onTargetRechargeOrder() {
this.$navTo('pages/wallet/recharge/order')
},
//
onTargetBalanceLog() {
this.$navTo('pages/wallet/balance/log')
}
}
}
</script>
<style>
page {
background: #fff;
}
</style>
<style lang="scss" scoped>
.container {
background: #fff;
}
.space-upper {
padding: 150rpx 0;
text-align: center;
}
.wallet-image image {
width: 360rpx;
height: 261.72rpx;
}
.wallet-account {
margin-top: 20rpx;
}
.wallet-account_balance {
font-size: 56rpx;
}
.wallet-account_lable {
margin-top: 14rpx;
color: #cec1c1;
font-size: 26rpx;
}
.space-lower {
margin-top: 30rpx;
padding: 0 110rpx;
}
.btn-recharge .btn-submit {
width: 460rpx;
height: 84rpx;
margin: 0 auto;
border-radius: 50rpx;
background: #786cff;
color: white;
font-size: 30rpx;
display: flex;
justify-content: center;
align-items: center;
}
.item-lable {
margin-top: 80rpx;
font-size: 28rpx;
color: rgb(94, 94, 94);
padding: 0 100rpx;
}
</style>

300
pages/wallet/recharge/index.vue

@ -1,300 +0,0 @@
<template>
<view class="container" v-if="userInfo.user_id">
<view class="account-panel dis-flex flex-y-center">
<view class="panel-lable">
<text>账户余额</text>
</view>
<view class="panel-balance flex-box">
<text>{{ userInfo.balance }}</text>
</view>
</view>
<view class="recharge-panel">
<view class="recharge-label">
<text>充值金额</text>
</view>
<view class="recharge-plan clearfix">
<block v-for="(item, index) in planList" :key="index">
<view class="recharge-plan_item" :class="{ active: selectedPlanId == item.plan_id }" @click="onSelectPlan(item.plan_id)">
<view class="plan_money">
<text>{{ item.money }}</text>
</view>
<view class="plan_gift" v-if="item.gift_money > 0">
<text>{{ item.gift_money }}</text>
</view>
</view>
</block>
</view>
<!-- 手动充值输入框 -->
<view class="recharge-input" v-if="setting.is_custom == 1">
<input type="digit" placeholder="请输入充值金额" v-model="inputValue" @input="onChangeMoney" />
</view>
<!-- 确认按钮 -->
<view class="recharge-submit btn-submit">
<form @submit="onSubmit">
<button class="button" formType="submit" :disabled="disabled">立即充值</button>
</form>
</view>
</view>
<!-- 充值描述 -->
<view class="recharge-describe">
<view class="recharge-label">
<text>充值说明</text>
</view>
<view class="content">
<text space="ensp">{{ setting.describe }}</text>
</view>
</view>
</view>
</template>
<script>
import * as UserApi from '@/api/user'
import * as RechargeApi from '@/api/recharge'
import * as PlanApi from '@/api/recharge/plan'
import SettingModel from '@/common/model/Setting'
import SettingKeyEnum from '@/common/enum/setting/Key'
import { wxPayment } from '@/core/app'
export default {
data() {
return {
//
isLoading: true,
//
userInfo: {},
//
setting: {},
//
planList: [],
//
disabled: false,
// id
selectedPlanId: 0,
//
inputValue: '',
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
//
this.getPageData()
},
methods: {
/**
* 选择充值套餐
*/
onSelectPlan(planId) {
this.selectedPlanId = planId
this.inputValue = ''
},
//
onChangeMoney(e) {
this.inputValue = e.target.value
this.selectedPlanId = 0
},
//
getPageData() {
const app = this
app.isLoading = true
Promise.all([app.getUserInfo(), app.getSetting(), app.getPlanList()])
.then(() => app.isLoading = false)
},
//
getPlanList() {
const app = this
return new Promise((resolve, reject) => {
PlanApi.list()
.then(result => {
app.planList = result.data.list
resolve(app.planList)
})
})
},
//
getUserInfo() {
const app = this
return new Promise((resolve, reject) => {
UserApi.info()
.then(result => {
app.userInfo = result.data.userInfo
resolve(app.userInfo)
})
})
},
//
getSetting() {
const app = this
return new Promise((resolve, reject) => {
SettingModel.item(SettingKeyEnum.RECHARGE.value, false)
.then(data => {
app.setting = data
resolve(data)
})
})
},
//
onSubmit(e) {
const app = this
//
app.disabled = true
//
RechargeApi.submit({ planId: app.selectedPlanId, customMoney: app.inputValue })
.then(result => app.wxPayment(result.data.payment))
.finally(() => app.disabled = false)
},
//
wxPayment(option) {
const app = this
wxPayment(option)
.then(() => {
app.$success('支付成功')
setTimeout(() => {
//
app.getPageData()
}, 1500)
})
.catch(err => app.$error('订单未支付'))
}
}
}
</script>
<style lang="scss" scoped>
page,
.container {
background: #fff;
}
.container {
padding-bottom: 70rpx;
}
/* 账户面板 */
.account-panel {
width: 650rpx;
height: 180rpx;
margin: 50rpx auto;
padding: 0 60rpx;
box-sizing: border-box;
border-radius: 12rpx;
color: #fff;
background: linear-gradient(-125deg, #a46bff, #786cff);
box-shadow: 0 5px 22px 0 rgba(0, 0, 0, 0.26);
}
.panel-lable {
font-size: 32rpx;
}
.recharge-label {
color: rgb(51, 51, 51);
font-size: 28rpx;
margin-bottom: 25rpx;
}
.panel-balance {
text-align: right;
font-size: 46rpx;
}
.recharge-panel {
margin-top: 60rpx;
padding: 0 60rpx;
}
/* 充值套餐 */
.recharge-plan {
.recharge-plan_item {
width: 192rpx;
padding: 15rpx 0;
float: left;
text-align: center;
color: #888;
border: 1rpx solid rgb(228, 228, 228);
border-radius: 10rpx;
margin: 0 20rpx 20rpx 0;
&:nth-child(3n + 0) {
margin-right: 0;
}
&.active {
color: #786cff;
border: 1rpx solid #786cff;
.plan_money {
color: #786cff;
}
}
}
}
.plan_money {
font-size: 32rpx;
color: rgb(82, 82, 82);
}
.plan_gift {
font-size: 25rpx;
}
.recharge-input {
margin-top: 25rpx;
input {
border: 1rpx solid rgb(228, 228, 228);
border-radius: 10rpx;
padding: 15rpx 16rpx;
font-size: 26rpx;
}
}
/* 立即充值 */
.recharge-submit {
margin-top: 70rpx;
}
.btn-submit {
.button {
font-size: 30rpx;
background: #786cff;
border: none;
color: white;
border-radius: 50rpx;
padding: 0 120rpx;
line-height: 3;
}
.button[disabled] {
background: #a098ff;
border-color: #a098ff;
color: white;
}
}
/* 充值说明 */
.recharge-describe {
margin-top: 50rpx;
padding: 0 60rpx;
.content {
font-size: 26rpx;
line-height: 1.6;
color: #888;
}
}
</style>

127
pages/wallet/recharge/order.vue

@ -1,127 +0,0 @@
<template>
<view class="container">
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption"
@up="upCallback">
<view class="log-list">
<view v-for="(item, index) in list.data" :key="index" class="log-item">
<view class="item-left flex-box">
<view class="rec-status">
<text>{{ '充值成功' }}</text>
</view>
<view class="rec-time">
<text>{{ item.pay_time }}</text>
</view>
</view>
<view class="item-right">
<text>+{{ item.actual_money }}</text>
</view>
</view>
</view>
</mescroll-body>
</view>
</template>
<script>
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import * as OrderApi from '@/api/recharge/order'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
const pageSize = 15
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
//
list: getEmptyPaginateObj(),
//
upOption: {
//
auto: true,
// ; 10
page: { size: pageSize },
// 12
noMoreSize: 12,
//
empty: {
tip: '亲,暂无充值记录'
}
}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {},
methods: {
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
//
app.getLogList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
//
getLogList(pageNo = 1) {
const app = this
return new Promise((resolve, reject) => {
OrderApi.list({ page: pageNo })
.then(result => {
//
const newList = result.data.list
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
})
}
}
}
</script>
<style lang="scss" scoped>
page,
.container {
background: #fff;
}
.log-list {
padding: 0 30rpx;
}
.log-item {
font-size: 28rpx;
padding: 20rpx 20rpx;
line-height: 1.8;
border-bottom: 1rpx solid rgb(238, 238, 238);
display: flex;
justify-content: center;
align-items: center;
}
.rec-status {
color: #333;
.rec-time {
color: rgb(160, 160, 160);
font-size: 26rpx;
}
}
</style>
Loading…
Cancel
Save