第一次提交
This commit is contained in:
618
package/user/goods/cashier.vue
Executable file
618
package/user/goods/cashier.vue
Executable file
@@ -0,0 +1,618 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container" :style="appThemeStyle">
|
||||
|
||||
|
||||
<!-- 订单信息 -->
|
||||
<view class="order-info">
|
||||
<!-- 支付剩余时间 -->
|
||||
<view class="order-countdown">
|
||||
<text class="m-r-6">剩余时间</text>
|
||||
<count-down :date="order.expirationTime" separator="zh" theme="text" />
|
||||
</view>
|
||||
<!-- 付款金额 -->
|
||||
<view class="order-amount">
|
||||
<text class="unit">¥</text>
|
||||
<text class="amount">{{ order.pay_price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 购买时长 -->
|
||||
<u-card title="订单信息">
|
||||
<view class="duration-method" slot="body">
|
||||
<view class="pay-item dis-flex flex-x-between">
|
||||
<view class="item-left dis-flex flex-y-center">
|
||||
<view class="item-left_text">
|
||||
<text>购买时长</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-right col-m" @click="onShowDuration">
|
||||
<text class="duration-title">{{ duration[planId].label }}</text>
|
||||
<text class="iconfont icon-arrow-down"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="pay-item dis-flex flex-x-between">
|
||||
<view class="item-left dis-flex flex-y-center">
|
||||
<view class="item-left_text">
|
||||
<text>购买数量</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-right col-m">
|
||||
<text>{{ length }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<u-select v-model="showDuration" :list="duration" @confirm="onConfirm"></u-select>
|
||||
</view>
|
||||
</u-card>
|
||||
|
||||
<!-- 支付方式 -->
|
||||
<u-card title="付款方式">
|
||||
<view class="payment-method" slot="body">
|
||||
<view v-for="(item, index) in methods" :key="index" class="pay-item dis-flex flex-x-between"
|
||||
@click="handleSelectPayType(index)">
|
||||
<view class="item-left dis-flex flex-y-center">
|
||||
<view class="item-left_icon" :class="[item.method]">
|
||||
<text class="iconfont" :class="[PayMethodIconEnum[item.method]]"></text>
|
||||
</view>
|
||||
<view class="item-left_text">
|
||||
<text>{{ PayMethodEnum[item.method].name }}</text>
|
||||
</view>
|
||||
<view v-if="item.method === PayMethodEnum.BALANCE.value" class="user-balance">
|
||||
<text>(可用¥{{ personal.balance }}元)</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-right col-m" v-if="curPaymentItem && curPaymentItem.method == item.method">
|
||||
<text class="iconfont icon-check"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-card>
|
||||
|
||||
<!-- 确认按钮 -->
|
||||
<view class="footer-fixed">
|
||||
<view class="btn-wrapper">
|
||||
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">确认支付</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付确认弹窗 -->
|
||||
<!-- #ifdef H5 -->
|
||||
<u-modal v-if="tempUnifyData" v-model="showConfirmModal" title="支付确认" show-cancel-button confirm-text="已完成支付"
|
||||
:confirm-color="appTheme.mainBg" negative-top="100" :asyncClose="true"
|
||||
@confirm="onTradeQuery(tempUnifyData.outTradeNo, tempUnifyData.method)">
|
||||
<view class="modal-content">
|
||||
<text>请在{{ PayMethodClientNameEnum[tempUnifyData.method] }}内完成支付,如果您已经支付成功,请点击“已完成支付”按钮</text>
|
||||
</view>
|
||||
</u-modal>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import storage from '@/utils/storage'
|
||||
import {
|
||||
inArray,
|
||||
urlEncode
|
||||
} from '@/utils/util'
|
||||
import {
|
||||
Alipay,
|
||||
Wechat
|
||||
} from '@/core/payment'
|
||||
import CountDown from '@/components/countdown'
|
||||
import {
|
||||
PayMethodEnum
|
||||
} from '@/common/enum/payment'
|
||||
import {
|
||||
PayStatusEnum
|
||||
} from '@/common/enum/order'
|
||||
import * as Api from '@/api/order/popularize.js'
|
||||
|
||||
// 支付方式对应的图标
|
||||
const PayMethodIconEnum = {
|
||||
[PayMethodEnum.WECHAT.value]: 'icon-wechat-pay',
|
||||
[PayMethodEnum.ALIPAY.value]: 'icon-alipay',
|
||||
[PayMethodEnum.BALANCE.value]: 'icon-balance-pay'
|
||||
}
|
||||
|
||||
// 支付方式的终端名称
|
||||
const PayMethodClientNameEnum = {
|
||||
[PayMethodEnum.WECHAT.value]: '微信',
|
||||
[PayMethodEnum.ALIPAY.value]: '支付宝'
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CountDown
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 加载中
|
||||
isLoading: true,
|
||||
// 确认按钮禁用
|
||||
disabled: false,
|
||||
// 枚举类
|
||||
PayMethodEnum,
|
||||
PayMethodIconEnum,
|
||||
PayMethodClientNameEnum,
|
||||
// 当前选中的支付方式
|
||||
curPaymentItem: null,
|
||||
// 当前订单ID
|
||||
orderId: null,
|
||||
// 当前结算订单信息
|
||||
order: {},
|
||||
// 个人信息
|
||||
personal: {
|
||||
balance: '0.00'
|
||||
},
|
||||
// 当前客户端的支付方式列表(后端根据platform判断)
|
||||
methods: [],
|
||||
// 支付确认弹窗
|
||||
showConfirmModal: false,
|
||||
// #ifdef H5
|
||||
// 当前微信支付信息 (临时数据, 仅用于H5端)
|
||||
tempUnifyData: {
|
||||
outTradeNo: '',
|
||||
method: ''
|
||||
},
|
||||
// #endif
|
||||
// 购买商品数量
|
||||
length: 0,
|
||||
// 购买的商品ID
|
||||
goodsIds: [],
|
||||
|
||||
// 购买时长
|
||||
duration: [],
|
||||
// 套餐ID
|
||||
planId: 0,
|
||||
showDuration: false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 读取缓存数据
|
||||
const goodsIds = storage.get('selectGoodsIdsByPopularize')
|
||||
this.goodsIds = goodsIds
|
||||
// 购买数量
|
||||
this.length = goodsIds.length
|
||||
// 获取收银台信息
|
||||
this.getCashierInfo()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取收银台信息
|
||||
getCashierInfo() {
|
||||
const app = this
|
||||
const { goodsIds, planId, length } = this
|
||||
app.isLoading = true
|
||||
// 读取缓存数据
|
||||
Api.orderInfo({form: {planId,goodsIds,length}})
|
||||
.then(result => {
|
||||
app.order = result.data.order
|
||||
app.personal = result.data.personal
|
||||
app.methods = result.data.paymentMethods
|
||||
app.duration = result.data.planList.map((d,index) => {
|
||||
return {
|
||||
value: index,
|
||||
label: d.plan_name + '¥(' + d.money + ')'
|
||||
}
|
||||
})
|
||||
app.isLoading = false
|
||||
app.setDefaultPayType()
|
||||
app.checkOrderPayStatus()
|
||||
// #ifdef H5
|
||||
// 判断当前页面来源于浏览器返回
|
||||
this.performance()
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
|
||||
// 设置默认的支付方式
|
||||
setDefaultPayType() {
|
||||
const app = this
|
||||
if (app.disabled) return
|
||||
const defaultIndex = app.methods.findIndex(item => item.is_default == true)
|
||||
defaultIndex > -1 && app.handleSelectPayType(defaultIndex)
|
||||
},
|
||||
|
||||
// 判断当前订单是否为已支付
|
||||
checkOrderPayStatus() {
|
||||
const app = this
|
||||
if (app.order.pay_status == PayStatusEnum.SUCCESS.value) {
|
||||
app.$toast('恭喜您,订单已付款成功')
|
||||
app.onSuccessNav()
|
||||
}
|
||||
},
|
||||
|
||||
onShowDuration(index){
|
||||
this.showDuration = true
|
||||
},
|
||||
|
||||
onConfirm(item){
|
||||
this.planId = item[0].value
|
||||
this.getCashierInfo()
|
||||
},
|
||||
|
||||
bindPickerChange: function(e) {
|
||||
console.log('picker发送选择改变,携带值为', e.detail.value)
|
||||
this.index = e.detail.value
|
||||
},
|
||||
// 选择支付方式
|
||||
handleSelectPayType(index) {
|
||||
this.curPaymentItem = this.methods[index]
|
||||
},
|
||||
|
||||
// 选择时长类型
|
||||
handleSelectDurationType(key){
|
||||
this.selectDuration = key
|
||||
},
|
||||
|
||||
// 判断当前页面来源于浏览器返回
|
||||
// #ifdef H5
|
||||
performance() {
|
||||
const app = this
|
||||
// 判断订单状态, 异步回调会将订单状态变为已支付, 那么就不需要让用户手动查单了
|
||||
if (app.order.pay_status == PayStatusEnum.PENDING.value) {
|
||||
app.alipayPerformance()
|
||||
app.wechatPerformance()
|
||||
}
|
||||
},
|
||||
|
||||
// H5端支付宝支付完成跳转回当前页面时触发
|
||||
alipayPerformance() {
|
||||
const app = this
|
||||
app.tempUnifyData = Alipay.performance()
|
||||
if (app.tempUnifyData) {
|
||||
app.onTradeQuery(app.tempUnifyData.outTradeNo, app.tempUnifyData.method)
|
||||
}
|
||||
},
|
||||
|
||||
// H5端微信支付完成或返回时触发
|
||||
wechatPerformance() {
|
||||
const app = this
|
||||
app.tempUnifyData = Wechat.performance(app.orderId)
|
||||
if (app.tempUnifyData) {
|
||||
app.showConfirmModal = true
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
|
||||
// 确认支付
|
||||
handleSubmit() {
|
||||
const app = this
|
||||
const { goodsIds, planId, length } = this
|
||||
// 判断是否选择了支付方式
|
||||
if (!app.curPaymentItem) {
|
||||
app.$toast('您还没有选择支付方式')
|
||||
return
|
||||
}
|
||||
// 按钮禁用
|
||||
if (app.disabled) return
|
||||
app.disabled = true
|
||||
// 提交到后端API
|
||||
Api.orderPay({
|
||||
form: {
|
||||
method: app.curPaymentItem.method,
|
||||
client: app.platform,
|
||||
extra: app.getExtraAsUnify(app.curPaymentItem.method),
|
||||
goodsIds,
|
||||
planId,
|
||||
length
|
||||
}
|
||||
})
|
||||
.then(result => app.onSubmitCallback(result))
|
||||
.finally(err => {
|
||||
setTimeout(() => app.disabled = false, 10)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取第三方支付的扩展参数
|
||||
getExtraAsUnify(method) {
|
||||
if (method === PayMethodEnum.ALIPAY.value) {
|
||||
return Alipay.extraAsUnify()
|
||||
}
|
||||
if (method === PayMethodEnum.WECHAT.value) {
|
||||
return Wechat.extraAsUnify()
|
||||
}
|
||||
return {}
|
||||
},
|
||||
|
||||
// 订单提交成功后回调
|
||||
onSubmitCallback(result) {
|
||||
const app = this
|
||||
const method = app.curPaymentItem.method
|
||||
const paymentData = result.data.payment
|
||||
// 余额支付
|
||||
if (method === PayMethodEnum.BALANCE.value) {
|
||||
app.onShowSuccess(result)
|
||||
}
|
||||
// 发起支付宝支付
|
||||
if (method === PayMethodEnum.ALIPAY.value) {
|
||||
console.log('paymentData', paymentData)
|
||||
Alipay.payment(paymentData)
|
||||
.then(res => app.onPaySuccess(res))
|
||||
.catch(err => app.onPayFail(err))
|
||||
}
|
||||
// 发起微信支付
|
||||
if (method === PayMethodEnum.WECHAT.value) {
|
||||
console.log('paymentData', paymentData)
|
||||
Wechat.payment({
|
||||
orderKey: app.orderId,
|
||||
...paymentData
|
||||
})
|
||||
.then(res => app.onPaySuccess(res))
|
||||
.catch(err => app.onPayFail(err))
|
||||
}
|
||||
},
|
||||
|
||||
// 订单支付成功的回调方法
|
||||
// 这里只是前端支付api返回结果success,实际订单是否支付成功 以后端的查单和异步通知为准
|
||||
onPaySuccess({
|
||||
res,
|
||||
option: {
|
||||
isRequireQuery,
|
||||
outTradeNo,
|
||||
method
|
||||
}
|
||||
}) {
|
||||
const app = this
|
||||
// 判断是否需要主动查单
|
||||
// isRequireQuery为true代表需要主动查单
|
||||
if (isRequireQuery) {
|
||||
app.onTradeQuery(outTradeNo, method)
|
||||
return true
|
||||
}
|
||||
this.onShowSuccess(res)
|
||||
},
|
||||
|
||||
// 显示支付成功信息并页面跳转
|
||||
onShowSuccess({
|
||||
message
|
||||
}) {
|
||||
this.$toast(message || '订单支付成功')
|
||||
this.onSuccessNav()
|
||||
},
|
||||
|
||||
// 订单支付失败
|
||||
onPayFail(err) {
|
||||
console.log('onPayFail', err)
|
||||
const errMsg = err.message || '订单未支付'
|
||||
this.$error(errMsg)
|
||||
},
|
||||
|
||||
// 已完成支付按钮事件: 请求后端查单
|
||||
onTradeQuery(outTradeNo, method) {
|
||||
const app = this
|
||||
// 交易查询
|
||||
// 查询第三方支付订单是否付款成功
|
||||
Api.tradeQuery({
|
||||
outTradeNo,
|
||||
method,
|
||||
client: app.platform
|
||||
})
|
||||
.then(result => result.data.isPay ? app.onShowSuccess(result) : app.onPayFail(result))
|
||||
.finally(() => app.showConfirmModal = false)
|
||||
},
|
||||
|
||||
// 支付成功后的跳转
|
||||
onSuccessNav() {
|
||||
const pages = getCurrentPages()
|
||||
const lastPage = pages.length < 2 ? null : pages[pages.length - 2]
|
||||
const backRoutes = [
|
||||
'package/solution/index',
|
||||
'package/solution/detail'
|
||||
]
|
||||
if (lastPage && inArray(lastPage.route, backRoutes)) {
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1000)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.$navTo('package/user/goods/list', {}, 'redirectTo')
|
||||
}, 1200)
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #F4F4F4;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
background-color: #F4F4F4;
|
||||
}
|
||||
|
||||
// 订单信息
|
||||
.order-info {
|
||||
padding: 80rpx 0;
|
||||
text-align: center;
|
||||
|
||||
.order-countdown {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.order-amount {
|
||||
margin: 0 auto;
|
||||
max-width: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #111111;
|
||||
|
||||
.unit {
|
||||
font-size: 30rpx;
|
||||
margin-bottom: -18rpx;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 56rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.duration-title {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.duration-method {
|
||||
width: 94%;
|
||||
background-color: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
|
||||
.pay-item {
|
||||
padding: 26rpx 0;
|
||||
font-size: 28rpx;
|
||||
border-bottom: 1rpx solid rgb(248, 248, 248);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-left_icon {
|
||||
margin-right: 20rpx;
|
||||
font-size: 44rpx;
|
||||
|
||||
&.wechat {
|
||||
color: #00c800;
|
||||
}
|
||||
|
||||
&.alipay {
|
||||
color: #009fe8;
|
||||
}
|
||||
|
||||
&.balance {
|
||||
color: #ff9700;
|
||||
}
|
||||
}
|
||||
|
||||
.item-left_text {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.user-balance {
|
||||
margin-left: 20rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 支付方式
|
||||
.payment-method {
|
||||
width: 94%;
|
||||
background-color: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
|
||||
.pay-item {
|
||||
padding: 26rpx 0;
|
||||
font-size: 28rpx;
|
||||
border-bottom: 1rpx solid rgb(248, 248, 248);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-left_icon {
|
||||
margin-right: 20rpx;
|
||||
font-size: 44rpx;
|
||||
|
||||
&.wechat {
|
||||
color: #00c800;
|
||||
}
|
||||
|
||||
&.alipay {
|
||||
color: #009fe8;
|
||||
}
|
||||
|
||||
&.balance {
|
||||
color: #ff9700;
|
||||
}
|
||||
}
|
||||
|
||||
.item-left_text {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.user-balance {
|
||||
margin-left: 20rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 支付确认弹窗
|
||||
.modal-content {
|
||||
padding: 40rpx 48rpx;
|
||||
font-size: 30rpx;
|
||||
line-height: 50rpx;
|
||||
text-align: left;
|
||||
color: #606266;
|
||||
// height: 620rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
// 底部操作栏
|
||||
.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 40rpx;
|
||||
}
|
||||
|
||||
.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, $main-bg, $main-bg2);
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
159
package/user/goods/components/Comment.vue
Executable file
159
package/user/goods/components/Comment.vue
Executable file
@@ -0,0 +1,159 @@
|
||||
<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 col-9"></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 {
|
||||
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
package/user/goods/components/Service.vue
Executable file
158
package/user/goods/components/Service.vue
Executable file
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<view v-if="list.length" class="service-wrapper" :style="appThemeStyle">
|
||||
<!-- 服务简述 -->
|
||||
<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: $main-bg;
|
||||
}
|
||||
|
||||
.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: $main-bg;
|
||||
}
|
||||
|
||||
.item-val {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.item-summary {
|
||||
font-size: 26rpx;
|
||||
margin-top: 20rpx;
|
||||
color: #6d6d6d;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
179
package/user/goods/components/SkuPopup.vue
Executable file
179
package/user/goods/components/SkuPopup.vue
Executable file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<goods-sku-popup :value="value" @input="onChangeValue" border-radius="20" :localdata="goodsInfo" :mode="skuMode" :maskCloseAble="true"
|
||||
:priceColor="appTheme.mainBg" :buyNowBackgroundColor="appTheme.mainBg" :addCartColor="appTheme.viceText" :addCartBackgroundColor="appTheme.viceBg"
|
||||
:activedStyle="{ color: appTheme.mainBg, borderColor: appTheme.mainBg, backgroundColor: activedBtnBackgroundColor }"
|
||||
@open="openSkuPopup" @close="closeSkuPopup" @add-cart="addCart" @buy-now="buyNow" buyNowText="立即购买" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setCartTotalNum } from '@/core/app'
|
||||
import { hex2rgba } from '@/utils/color'
|
||||
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: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 规格按钮选中时的背景色
|
||||
activedBtnBackgroundColor() {
|
||||
return hex2rgba(this.appTheme.mainBg, 0.1)
|
||||
}
|
||||
},
|
||||
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
package/user/goods/components/SlideImage.vue
Executable file
146
package/user/goods/components/SlideImage.vue
Executable file
@@ -0,0 +1,146 @@
|
||||
<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>
|
||||
69
package/user/goods/components/search.vue
Executable file
69
package/user/goods/components/search.vue
Executable file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-wrapper">
|
||||
<view class="index-search" @click="onClick">
|
||||
<view class="index-cont-search t-c">
|
||||
<text class="search-icon iconfont icon-search"></text>
|
||||
<text class="search-text">{{ tips }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
tips: {
|
||||
type: String,
|
||||
default: '搜索商品'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('event')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-wrapper {
|
||||
background: #fff;
|
||||
padding: 13rpx;
|
||||
}
|
||||
|
||||
.index-search {
|
||||
border-bottom: 0;
|
||||
background: #fff;
|
||||
border-radius: 50rpx;
|
||||
overflow: hidden;
|
||||
font-size: 28rpx;
|
||||
color: #6d6d6d;
|
||||
box-sizing: border-box;
|
||||
line-height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.index-cont-search {
|
||||
width: 100%;
|
||||
font-size: 28rpx;
|
||||
background: #f7f7f7;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.index-cont-search .search-icon {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.index-cont-search .search-text {
|
||||
margin-left: 14rpx;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
228
package/user/goods/detail.scss
Executable file
228
package/user/goods/detail.scss
Executable file
@@ -0,0 +1,228 @@
|
||||
.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: #ff0000;
|
||||
margin-bottom: -10rpx;
|
||||
}
|
||||
|
||||
// 商品价
|
||||
.floor-price {
|
||||
color: #ff0000;
|
||||
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: #ff0000;
|
||||
}
|
||||
|
||||
// 选择商品规格
|
||||
.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, $main-bg, $main-bg2);
|
||||
}
|
||||
|
||||
// 购物车按钮
|
||||
.btn-item-deputy {
|
||||
background: linear-gradient(to right, $vice-bg, $vice-bg2);
|
||||
color: $vice-text;
|
||||
}
|
||||
}
|
||||
347
package/user/goods/detail.vue
Executable file
347
package/user/goods/detail.vue
Executable file
@@ -0,0 +1,347 @@
|
||||
<template>
|
||||
<view v-show="!isLoading" class="container" :style="appThemeStyle">
|
||||
<!-- 商品图片轮播 -->
|
||||
<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>
|
||||
<view class="goods-share__line"></view>
|
||||
<view class="goods-share">
|
||||
<button class="share-btn dis-flex flex-dir-column" @click="onShowShareSheet()">
|
||||
<text class="share__icon iconfont icon-fenxiang"></text>
|
||||
<text class="f-24">分享</text>
|
||||
</button>
|
||||
</view>
|
||||
</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>
|
||||
|
||||
<!-- 商品推荐 -->
|
||||
<recommended />
|
||||
|
||||
<!-- 底部选项卡 -->
|
||||
<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" @click="onCollection(goods.goods_id)">
|
||||
<view class="fast-item">
|
||||
<view class="fast-icon">
|
||||
<u-icon name="star-fill" color="#fd8008" v-if="goods.is_collection"></u-icon>
|
||||
<u-icon name="star" v-else></u-icon>
|
||||
</view>
|
||||
<view class="fast-text">
|
||||
<text>收藏</text>
|
||||
</view>
|
||||
</view>
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
<!-- 客服 (仅微信小程序端显示) -->
|
||||
<!-- #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" /> -->
|
||||
|
||||
<!-- 分享菜单 -->
|
||||
<share-sheet v-model="showShareSheet" :shareTitle="goods.goods_name" :shareImageUrl="goods.goods_image"
|
||||
:posterApiCall="posterApiCall" :posterApiParam="{ goodsId }" />
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSceneData } from '@/core/app'
|
||||
import * as GoodsApi from '@/api/goods'
|
||||
import * as CartApi from '@/api/cart'
|
||||
import Recommended from '@/components/recommended'
|
||||
import ShareSheet from '@/components/share-sheet'
|
||||
import SlideImage from './components/SlideImage'
|
||||
import SkuPopup from './components/SkuPopup'
|
||||
import Comment from './components/Comment'
|
||||
import Service from './components/Service'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Recommended,
|
||||
ShareSheet,
|
||||
SlideImage,
|
||||
SkuPopup,
|
||||
Comment,
|
||||
Service
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 当前商品ID
|
||||
goodsId: null,
|
||||
// 商品详情
|
||||
goods: {},
|
||||
// 购物车总数量
|
||||
cartTotal: 0,
|
||||
// 显示/隐藏SKU弹窗
|
||||
showSkuPopup: false,
|
||||
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
|
||||
skuMode: 1,
|
||||
// 显示/隐藏分享菜单
|
||||
showShareSheet: false,
|
||||
// 获取商品海报图api方法
|
||||
posterApiCall: GoodsApi.poster
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 记录query参数
|
||||
this.onRecordQuery(options)
|
||||
// 加载页面数据
|
||||
this.onRefreshPage()
|
||||
// 浏览历史
|
||||
this.saveFootMarkList(options.goodsId)
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 浏览历史记录
|
||||
saveFootMarkList(goodsId){
|
||||
const userId = uni.getStorageSync('userId')
|
||||
const arr = uni.getStorageSync(`FOOTMARKLIST_${userId}`) || []
|
||||
arr.unshift(Number(goodsId))
|
||||
return uni.setStorageSync(`FOOTMARKLIST_${userId}`,arr)
|
||||
},
|
||||
// 记录query参数
|
||||
onRecordQuery(query) {
|
||||
const scene = getSceneData(query)
|
||||
this.goodsId = query.goodsId ? parseInt(query.goodsId) : parseInt(scene.gid)
|
||||
},
|
||||
|
||||
// 刷新页面数据
|
||||
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) {
|
||||
const shopInfo = uni.getStorageSync('shopInfo')
|
||||
const clerkInfo = uni.getStorageSync('clerkInfo')
|
||||
if(shopInfo || clerkInfo){
|
||||
return this.$error('请使用会员账号购买')
|
||||
}
|
||||
this.skuMode = skuMode
|
||||
this.showSkuPopup = !this.showSkuPopup
|
||||
},
|
||||
|
||||
// 显示隐藏分享菜单
|
||||
onShowShareSheet() {
|
||||
this.showShareSheet = !this.showShareSheet
|
||||
},
|
||||
|
||||
// 跳转到首页
|
||||
onTargetHome(e) {
|
||||
const { goods } = this
|
||||
console.log("this.goods: ",goods);
|
||||
this.$navTo('package/shops/detail/index',goods)
|
||||
},
|
||||
|
||||
// 加入收藏
|
||||
onCollection(goodsId){
|
||||
GoodsApi.collection({goodsId})
|
||||
this.goods.is_collection = !this.goods.is_collection
|
||||
},
|
||||
|
||||
// 跳转到购物车页
|
||||
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>
|
||||
659
package/user/goods/list.vue
Executable file
659
package/user/goods/list.vue
Executable file
@@ -0,0 +1,659 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ native: true }"
|
||||
@down="downCallback" :up="upOption" @up="upCallback">
|
||||
<!-- 页面头部 -->
|
||||
<view class="header">
|
||||
<view class="search">
|
||||
<input v-model="searchValue" class="input" focus="true" placeholder="请输入您搜索的商品" type="text" @input="onInput" @confirm="onSearch"></input>
|
||||
</view>
|
||||
<u-dropdown>
|
||||
<u-dropdown-item v-model="parentId" :title="parentName" :options="parentList"></u-dropdown-item>
|
||||
<u-dropdown-item v-if="childrenList.length > 0" v-model="categoryId" :title="categoryName" :options="childrenList"></u-dropdown-item>
|
||||
<u-dropdown-item v-if="thirdList.length > 0" v-model="thirdId" :title="thirdName" :options="thirdList"></u-dropdown-item>
|
||||
</u-dropdown>
|
||||
</view>
|
||||
<!-- {{parentId}}
|
||||
{{categoryId}}
|
||||
{{thirdId}}
|
||||
{{JSON.stringify(childrenList)}}
|
||||
{{JSON.stringify(thirdList)}} -->
|
||||
<!-- 排序标签 -->
|
||||
<!-- <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">
|
||||
<!-- 单列显示 -->
|
||||
<view v-if="showView" class="dis-flex">
|
||||
<!-- 商品图片 -->
|
||||
<view class="goods-item_left" @click="onTargetDetail(item.goods_id)">
|
||||
<image class="image" :src="item.goods_image"></image>
|
||||
</view>
|
||||
<view class="goods-item_right">
|
||||
<!-- 商品名称 -->
|
||||
<view class="goods-name">
|
||||
<text class="twoline-hide" @click="onTargetDetail(item.goods_id)">{{ 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 class="buy-gouwuche" v-if="item.top_time == 0">
|
||||
<u-checkbox v-model="item.checked" @change="checkboxChange(item.goods_id)">置顶</u-checkbox>
|
||||
<!-- <u-button size="mini">置顶</u-button> -->
|
||||
</view>
|
||||
<view class="buy-gouwuche" v-else>
|
||||
<u-tag text="投放中"></u-tag>
|
||||
</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>
|
||||
<!-- 商品销量 -->
|
||||
<text class="col-9 f-24 u-text-right">已售{{ item.goods_sales }}件</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="gotobuy">
|
||||
<u-button v-if="checked.length > 0" type="primary" @click="goToBuy">去付款</u-button>
|
||||
</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 GoodsApi from '@/api/goods'
|
||||
import * as CategoryApi from '@/api/category'
|
||||
import {
|
||||
getEmptyPaginateObj,
|
||||
getMoreListData
|
||||
} from '@/core/app'
|
||||
import Search from './components/search.vue'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
const pageSize = 15
|
||||
const showViewKey = 'GoodsList-ShowView';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody,
|
||||
Search
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
showView: true, // 列表显示方式 (true列表、false平铺)
|
||||
sortType: 'all', // 排序类型
|
||||
sortPrice: false, // 价格排序 (true高到低 false低到高)
|
||||
options: {}, // 当前页面参数
|
||||
list: getEmptyPaginateObj(), // 商品列表数据
|
||||
searchValue: '',
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: true,
|
||||
// 每页数据的数量; 默认10
|
||||
page: {
|
||||
size: pageSize
|
||||
},
|
||||
// 数量要大于4条才显示无更多数据
|
||||
noMoreSize: 4,
|
||||
},
|
||||
parentId: 0,
|
||||
parentName: '商品分类',
|
||||
categoryId: 0,
|
||||
categoryName: '请选择',
|
||||
category: [],
|
||||
parentList: [],
|
||||
childrenList: [],
|
||||
// 三级分类
|
||||
thirdId: 0,
|
||||
thirdName: '请选择',
|
||||
thirdList: [],
|
||||
checked: [],
|
||||
time: ''
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 记录options
|
||||
this.options = options
|
||||
// 设置默认列表显示方式
|
||||
this.setShowView()
|
||||
// 加载商品分类
|
||||
this.getCategory()
|
||||
// 检查商品支付状态并设置置顶时间
|
||||
// this.time = this.$u.timeFrom(1665049088);
|
||||
// console.log("this.time: ",this.time);
|
||||
|
||||
},
|
||||
|
||||
watch: {
|
||||
parentId(val) {
|
||||
// 设置一级分类名称
|
||||
this.parentList.map(d => {
|
||||
if(d.value == val){
|
||||
this.parentName = d.label
|
||||
this.parentId = d.value
|
||||
this.categoryId = 0
|
||||
this.categoryName = '请选择'
|
||||
this.thirdId = 0
|
||||
d.children && this.getChildrenList(d.value)
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
categoryId(val){
|
||||
this.childrenList.map(d => {
|
||||
if(d.value == val){
|
||||
this.categoryName = d.label
|
||||
this.categoryId = d.value
|
||||
this.thirdId = 0
|
||||
d.children && this.getThirdList(d.value)
|
||||
}
|
||||
})
|
||||
this.getGoodsList()
|
||||
},
|
||||
thirdId(val){
|
||||
this.thirdList.map(d => {
|
||||
if(d.value == val){
|
||||
this.thirdName = d.label
|
||||
this.thirdId = d.value
|
||||
}
|
||||
})
|
||||
this.getGoodsList()
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
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())
|
||||
},
|
||||
// 选择一级分类后刷新左侧菜单
|
||||
getChildrenList(parentId){
|
||||
const app = this
|
||||
app.parentId = parentId
|
||||
app.category.map(d => {
|
||||
if(d.category_id == parentId){
|
||||
app.childrenList = d.children.map(item => {
|
||||
return {
|
||||
value: item.category_id,
|
||||
label: item.name,
|
||||
children: item.children
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 三级分类
|
||||
getThirdList(categoryId){
|
||||
const app = this
|
||||
app.categoryId = categoryId
|
||||
app.childrenList.map(d => {
|
||||
console.log(categoryId + "====",d.value);
|
||||
if(d.value == categoryId){
|
||||
app.thirdList = d.children.map(d => {
|
||||
return {
|
||||
value: d.category_id,
|
||||
label: d.name,
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 设置默认列表显示方式
|
||||
setShowView() {
|
||||
this.showView = uni.getStorageSync(showViewKey) || true
|
||||
},
|
||||
|
||||
onInput(e){
|
||||
this.searchValue = e.detail.value
|
||||
this.options.search = e.detail.value
|
||||
},
|
||||
|
||||
onSearch(){
|
||||
this.getGoodsList()
|
||||
},
|
||||
// 收集选中的goodsId
|
||||
checkboxChange(goodsId){
|
||||
const app = this
|
||||
// 查找数组是否含该元素并返回下标位置
|
||||
var index = app.checked.indexOf(goodsId)
|
||||
// 存在则删除不存在则插入
|
||||
if( index > -1){
|
||||
app.checked.splice(index,1)
|
||||
}else{
|
||||
app.checked.push(goodsId)
|
||||
}
|
||||
storage.set('selectGoodsIdsByPopularize', app.checked)
|
||||
},
|
||||
goToBuy(){
|
||||
this.$navTo('package/user/goods/cashier')
|
||||
},
|
||||
|
||||
getCategory(){
|
||||
CategoryApi.list().then(result => {
|
||||
this.category = result.data.list
|
||||
// 抽取一级分类
|
||||
this.parentList = result.data.list.map(d => {
|
||||
if(d.parent_id == 0){
|
||||
return {
|
||||
value: d.category_id,
|
||||
label: d.name,
|
||||
children: d.children
|
||||
}
|
||||
}
|
||||
})
|
||||
// console.log('顶部一级分类:',this.parentList);
|
||||
// 左侧二级分类
|
||||
// result.data.list.map(d => {
|
||||
// if(d.category_id == this.parentId){
|
||||
// this.childrenList = d.children
|
||||
// }
|
||||
// })
|
||||
// console.log(": ",this.childrenList);
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取商品列表
|
||||
* @param {number} pageNo 页码
|
||||
*/
|
||||
getGoodsList(pageNo = 1) {
|
||||
const app = this
|
||||
const { thirdId, categoryId, parentId } = this
|
||||
const { shopId } = uni.getStorageSync('shopInfo')
|
||||
let lastId = 0;
|
||||
if(parentId > 0) {
|
||||
lastId = parentId
|
||||
}
|
||||
if(categoryId > 0) {
|
||||
lastId = categoryId
|
||||
}
|
||||
if(thirdId > 0){
|
||||
lastId = thirdId
|
||||
}
|
||||
const param = {
|
||||
sortType: app.sortType,
|
||||
sortPrice: Number(app.sortPrice),
|
||||
categoryId: lastId || 0,
|
||||
goodsName: app.options.search || '',
|
||||
page: pageNo,
|
||||
shopId,
|
||||
// storeHouseGoods: true,
|
||||
}
|
||||
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;
|
||||
flex: 1;
|
||||
// 搜索框
|
||||
.search {
|
||||
flex: 1;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
// 切换显示方式
|
||||
.show-view {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
font-size: 36rpx;
|
||||
color: #505050;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
// 排序组件
|
||||
.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: $main-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-item-price .price-arrow {
|
||||
margin-left: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #000;
|
||||
|
||||
.icon {
|
||||
&.active {
|
||||
color: $main-bg;
|
||||
}
|
||||
|
||||
&.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;
|
||||
flex: 1;
|
||||
|
||||
.goods-name {
|
||||
margin-top: 10rpx;
|
||||
min-height: 68rpx;
|
||||
line-height: 1.3;
|
||||
white-space: normal;
|
||||
color: #484848;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.buy-gouwuche{
|
||||
position: absolute;
|
||||
right: 10rpx;
|
||||
bottom: 10rpx;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.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: $main-bg;
|
||||
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: 1.3;
|
||||
white-space: normal;
|
||||
color: #484848;
|
||||
font-size: 26rpx;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.detail-price {
|
||||
.goods-price {
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.line-price {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.gotobuy{
|
||||
position: fixed;
|
||||
width: 700rpx;
|
||||
margin-left: 25rpx;
|
||||
bottom: 50rpx;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user