初始化项目
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 标题 -->
|
||||
<view class="page-title">收货地址</view>
|
||||
<!-- 表单组件 -->
|
||||
<view class="form-wrapper">
|
||||
<u-form :model="form" ref="uForm" label-width="140rpx">
|
||||
<u-form-item label="姓名" prop="name">
|
||||
<u-input v-model="form.name" placeholder="请输入收货人姓名" />
|
||||
</u-form-item>
|
||||
<u-form-item label="电话" prop="phone">
|
||||
<u-input v-model="form.phone" placeholder="请输入收货人手机号" />
|
||||
</u-form-item>
|
||||
<u-form-item label="地区" prop="region">
|
||||
<select-region ref="sRegion" v-model="form.region" />
|
||||
</u-form-item>
|
||||
<u-form-item label="详细地址" prop="detail" :border-bottom="false">
|
||||
<u-input v-model="form.detail" placeholder="街道门牌、楼层等信息" />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
<!-- 操作按钮 -->
|
||||
<view class="footer">
|
||||
<view class="btn-wrapper">
|
||||
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">保存</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SelectRegion from '@/components/select-region/select-region'
|
||||
import { isMobile } from '@/utils/verify'
|
||||
import * as AddressApi from '@/api/address'
|
||||
|
||||
// 表单字段元素
|
||||
const form = {
|
||||
name: '',
|
||||
phone: '',
|
||||
region: [],
|
||||
detail: ''
|
||||
}
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
name: [{
|
||||
required: true,
|
||||
message: '请输入姓名',
|
||||
trigger: ['blur', 'change']
|
||||
}],
|
||||
phone: [{
|
||||
required: true,
|
||||
message: '请输入手机号',
|
||||
trigger: ['blur', 'change']
|
||||
}, {
|
||||
// 自定义验证函数
|
||||
validator: (rule, value, callback) => {
|
||||
// 返回true表示校验通过,返回false表示不通过
|
||||
return isMobile(value)
|
||||
},
|
||||
message: '手机号码不正确',
|
||||
// 触发器可以同时用blur和change
|
||||
trigger: ['blur'],
|
||||
}],
|
||||
region: [{
|
||||
required: true,
|
||||
message: '请选择省市区',
|
||||
trigger: ['blur', 'change'],
|
||||
type: 'array'
|
||||
}],
|
||||
detail: [{
|
||||
required: true,
|
||||
message: '请输入详细地址',
|
||||
trigger: ['blur', 'change']
|
||||
}],
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SelectRegion
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form,
|
||||
rules,
|
||||
// 按钮禁用
|
||||
disabled: false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {},
|
||||
|
||||
// 必须要在onReady生命周期,因为onLoad生命周期组件可能尚未创建完毕
|
||||
onReady() {
|
||||
this.$refs.uForm.setRules(this.rules)
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 表单提交
|
||||
handleSubmit() {
|
||||
const app = this
|
||||
if (app.disabled) {
|
||||
return false
|
||||
}
|
||||
app.$refs.uForm.validate(valid => {
|
||||
if (valid) {
|
||||
app.disabled = true
|
||||
AddressApi.add(app.form)
|
||||
.then(result => {
|
||||
app.$toast(result.message)
|
||||
uni.navigateBack()
|
||||
})
|
||||
.finally(() => app.disabled = false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #f7f8fa;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.page-title {
|
||||
width: 94%;
|
||||
margin: 0 auto;
|
||||
padding-top: 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: rgba(69, 90, 100, 0.6);
|
||||
}
|
||||
|
||||
.form-wrapper {
|
||||
margin: 20rpx auto 20rpx auto;
|
||||
padding: 0 40rpx;
|
||||
width: 94%;
|
||||
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 16rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
// 底部操作栏
|
||||
.footer {
|
||||
margin-top: 80rpx;
|
||||
|
||||
.btn-wrapper {
|
||||
height: 100%;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
height: 86rpx;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-item-wechat {
|
||||
background: #0ba90b;
|
||||
margin-bottom: 26rpx;
|
||||
}
|
||||
|
||||
.btn-item-main {
|
||||
background: linear-gradient(to right, #f9211c, #ff6335);
|
||||
color: #fff;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,309 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="addres-list">
|
||||
<view class="address-item" v-for="(item, index) in list" :key="index">
|
||||
<view class="contacts">
|
||||
<text class="name">{{ item.name }}</text>
|
||||
<text class="phone">{{ item.phone }}</text>
|
||||
</view>
|
||||
<view class="address">
|
||||
<text class="region" v-for="(region, idx) in item.region" :key="idx">{{ region }}</text>
|
||||
<text class="detail">{{ item.detail }}</text>
|
||||
</view>
|
||||
<view class="line"></view>
|
||||
<view class="item-option">
|
||||
<view class="_left">
|
||||
<label class="item-radio" @click.stop="handleSetDefault(item.address_id)">
|
||||
<radio class="radio" color="#fa2209" :checked="item.address_id == defaultId"></radio>
|
||||
<text class="text">{{ item.address_id == defaultId ? '默认' : '选择' }}</text>
|
||||
</label>
|
||||
</view>
|
||||
<view class="_right">
|
||||
<view class="events">
|
||||
<view class="event-item" @click="handleUpdate(item.address_id)">
|
||||
<text class="iconfont icon-edit"></text>
|
||||
<text class="title">编辑</text>
|
||||
</view>
|
||||
<view class="event-item" @click="handleRemove(item.address_id)">
|
||||
<text class="iconfont icon-delete"></text>
|
||||
<text class="title">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<empty v-if="!list.length" :isLoading="isLoading" tips="亲,暂无收货地址" />
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="footer-fixed">
|
||||
<view class="btn-wrapper">
|
||||
<view class="btn-item btn-item-main" @click="handleCreate()">添加新地址</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as AddressApi from '@/api/address'
|
||||
import Empty from '@/components/empty'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Empty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
//当前页面参数
|
||||
options: {},
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 收货地址列表
|
||||
list: [],
|
||||
// 默认收货地址
|
||||
defaultId: null
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 当前页面参数
|
||||
this.options = options
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
// 获取页面数据
|
||||
this.getPageData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取页面数据
|
||||
getPageData() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([app.getDefaultId(), app.getAddressList()])
|
||||
.then(() => {
|
||||
// 列表排序把默认收货地址放到最前
|
||||
app.onReorder()
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取收货地址列表
|
||||
getAddressList() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
AddressApi.list()
|
||||
.then(result => {
|
||||
app.list = result.data.list
|
||||
resolve(result)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取默认的收货地址
|
||||
getDefaultId() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const app = this
|
||||
AddressApi.defaultId()
|
||||
.then(result => {
|
||||
app.defaultId = result.data.defaultId
|
||||
resolve(result)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 列表排序把默认收货地址放到最前
|
||||
onReorder() {
|
||||
const app = this
|
||||
app.list.sort(item => {
|
||||
return item.address_id == app.defaultId ? -1 : 1
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加新地址
|
||||
*/
|
||||
handleCreate() {
|
||||
this.$navTo('pages/address/create')
|
||||
},
|
||||
|
||||
/**
|
||||
* 编辑地址
|
||||
* @param {int} addressId 收货地址ID
|
||||
*/
|
||||
handleUpdate(addressId) {
|
||||
this.$navTo('pages/address/update', { addressId })
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除收货地址
|
||||
* @param {int} addressId 收货地址ID
|
||||
*/
|
||||
handleRemove(addressId) {
|
||||
const app = this
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "您确定要删除当前收货地址吗?",
|
||||
success({ confirm }) {
|
||||
confirm && app.onRemove(addressId)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 确认删除收货地址
|
||||
* @param {int} addressId 收货地址ID
|
||||
*/
|
||||
onRemove(addressId) {
|
||||
const app = this
|
||||
AddressApi.remove(addressId)
|
||||
.then(result => {
|
||||
app.getPageData()
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置为默认地址
|
||||
* @param {Object} addressId
|
||||
*/
|
||||
handleSetDefault(addressId) {
|
||||
const app = this
|
||||
AddressApi.setDefault(addressId)
|
||||
.then(result => {
|
||||
app.defaultId = addressId
|
||||
app.options.from === 'checkout' && uni.navigateBack()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.addres-list {
|
||||
padding-top: 20rpx;
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
padding-bottom: calc(constant(safe-area-inset-bottom) + 140rpx);
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 140rpx);
|
||||
}
|
||||
|
||||
// 项目内容
|
||||
.address-item {
|
||||
margin: 20rpx auto 20rpx auto;
|
||||
padding: 30rpx 40rpx;
|
||||
width: 94%;
|
||||
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 16rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.contacts {
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.name {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
font-size: 28rpx;
|
||||
|
||||
.region {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f3f3f3;
|
||||
}
|
||||
|
||||
.item-option {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 48rpx;
|
||||
|
||||
// 单选框
|
||||
.item-radio {
|
||||
font-size: 28rpx;
|
||||
|
||||
.radio {
|
||||
vertical-align: middle;
|
||||
transform: scale(0.76)
|
||||
}
|
||||
|
||||
.text {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
// 操作
|
||||
.events {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 48rpx;
|
||||
|
||||
.event-item {
|
||||
font-size: 28rpx;
|
||||
margin-right: 26rpx;
|
||||
color: #4c4c4c;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 底部操作栏
|
||||
.footer-fixed {
|
||||
position: fixed;
|
||||
bottom: var(--window-bottom);
|
||||
left: 0;
|
||||
right: 0;
|
||||
min-height: 120rpx;
|
||||
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;
|
||||
line-height: 80rpx;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
}
|
||||
|
||||
.btn-item-main {
|
||||
background: linear-gradient(to right, #f9211c, #ff6335);
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 标题 -->
|
||||
<view class="page-title">收货地址</view>
|
||||
<!-- 表单组件 -->
|
||||
<view class="form-wrapper">
|
||||
<u-form :model="form" ref="uForm" label-width="140rpx">
|
||||
<u-form-item label="姓名" prop="name">
|
||||
<u-input v-model="form.name" placeholder="请输入收货人姓名" />
|
||||
</u-form-item>
|
||||
<u-form-item label="电话" prop="phone">
|
||||
<u-input v-model="form.phone" placeholder="请输入收货人手机号" />
|
||||
</u-form-item>
|
||||
<u-form-item label="地区" prop="region">
|
||||
<select-region ref="sRegion" v-model="form.region" />
|
||||
</u-form-item>
|
||||
<u-form-item label="详细地址" prop="detail" :border-bottom="false">
|
||||
<u-input v-model="form.detail" placeholder="街道门牌、楼层等信息" />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
<!-- 操作按钮 -->
|
||||
<view class="footer">
|
||||
<view class="btn-wrapper">
|
||||
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">保存</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SelectRegion from '@/components/select-region/select-region'
|
||||
import { isMobile } from '@/utils/verify'
|
||||
import * as AddressApi from '@/api/address'
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
name: [{
|
||||
required: true,
|
||||
message: '请输入姓名',
|
||||
trigger: ['blur', 'change']
|
||||
}],
|
||||
phone: [{
|
||||
required: true,
|
||||
message: '请输入手机号',
|
||||
trigger: ['blur', 'change']
|
||||
}, {
|
||||
// 自定义验证函数
|
||||
validator: (rule, value, callback) => {
|
||||
// 返回true表示校验通过,返回false表示不通过
|
||||
return isMobile(value)
|
||||
},
|
||||
message: '手机号码不正确',
|
||||
// 触发器可以同时用blur和change
|
||||
trigger: ['blur'],
|
||||
}],
|
||||
region: [{
|
||||
required: true,
|
||||
message: '请选择省市区',
|
||||
trigger: ['blur', 'change'],
|
||||
type: 'array'
|
||||
}],
|
||||
detail: [{
|
||||
required: true,
|
||||
message: '请输入详细地址',
|
||||
trigger: ['blur', 'change']
|
||||
}],
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SelectRegion
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
name: '',
|
||||
phone: '',
|
||||
region: [],
|
||||
detail: ''
|
||||
},
|
||||
rules,
|
||||
// 加载中
|
||||
isLoading: true,
|
||||
// 按钮禁用
|
||||
disabled: false,
|
||||
// 当前收货地址ID
|
||||
addressId: null
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad({ addressId }) {
|
||||
// 当前收货地址ID
|
||||
this.addressId = addressId
|
||||
// 获取当前记录详情
|
||||
this.getDetail()
|
||||
},
|
||||
|
||||
// 必须要在onReady生命周期,因为onLoad生命周期组件可能尚未创建完毕
|
||||
onReady() {
|
||||
this.$refs.uForm.setRules(this.rules)
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取当前记录详情
|
||||
getDetail() {
|
||||
const app = this
|
||||
AddressApi.detail(app.addressId)
|
||||
.then(result => {
|
||||
const detail = result.data.detail
|
||||
app.createFormData(detail)
|
||||
})
|
||||
},
|
||||
|
||||
// 生成默认的表单数据
|
||||
createFormData(detail) {
|
||||
const { form } = this
|
||||
form.name = detail.name
|
||||
form.phone = detail.phone
|
||||
form.detail = detail.detail
|
||||
form.region = this.createRegion(detail)
|
||||
},
|
||||
|
||||
createRegion(detail) {
|
||||
return [{
|
||||
label: detail.region.province,
|
||||
value: detail.province_id
|
||||
}, {
|
||||
label: detail.region.city,
|
||||
value: detail.city_id
|
||||
}, {
|
||||
label: detail.region.region,
|
||||
value: detail.region_id
|
||||
}]
|
||||
},
|
||||
|
||||
// 表单提交
|
||||
handleSubmit() {
|
||||
const app = this
|
||||
if (app.disabled) {
|
||||
return false
|
||||
}
|
||||
app.$refs.uForm.validate(valid => {
|
||||
if (valid) {
|
||||
app.disabled = true
|
||||
AddressApi.edit(app.addressId, app.form)
|
||||
.then(result => {
|
||||
app.$toast(result.message)
|
||||
uni.navigateBack()
|
||||
})
|
||||
.finally(() => app.disabled = false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #f7f8fa;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.page-title {
|
||||
width: 94%;
|
||||
margin: 0 auto;
|
||||
padding-top: 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: rgba(69, 90, 100, 0.6);
|
||||
}
|
||||
|
||||
.form-wrapper {
|
||||
margin: 20rpx auto 20rpx auto;
|
||||
padding: 0 40rpx;
|
||||
width: 94%;
|
||||
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 16rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
// 底部操作栏
|
||||
.footer {
|
||||
margin-top: 80rpx;
|
||||
|
||||
.btn-wrapper {
|
||||
height: 100%;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
height: 86rpx;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-item-wechat {
|
||||
background: #0ba90b;
|
||||
margin-bottom: 26rpx;
|
||||
}
|
||||
|
||||
.btn-item-main {
|
||||
background: linear-gradient(to right, #f9211c, #ff6335);
|
||||
color: #fff;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container b-f p-b">
|
||||
<view class="article-title">
|
||||
<text class="f-32">{{ detail.title }}</text>
|
||||
</view>
|
||||
<view class="article-little dis-flex flex-x-between m-top10">
|
||||
<view class="article-little__left">
|
||||
<text class="article-views f-24 col-8">{{ detail.show_views }}次浏览</text>
|
||||
</view>
|
||||
<view class="article-little__right">
|
||||
<text class="article-views f-24 col-8">{{ detail.view_time }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="article-content m-top20">
|
||||
<mp-html :content="detail.content" />
|
||||
</view>
|
||||
<!-- 快捷导航 -->
|
||||
<shortcut />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Shortcut from '@/components/shortcut'
|
||||
import * as ArticleApi from '@/api/article'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Shortcut
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 当前文章ID
|
||||
articleId: null,
|
||||
// 加载中
|
||||
isLoading: true,
|
||||
// 当前文章详情
|
||||
detail: null
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 记录文章ID
|
||||
this.articleId = options.articleId
|
||||
// 获取文章详情
|
||||
this.getArticleDetail()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取文章详情
|
||||
getArticleDetail() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
ArticleApi.detail(app.articleId)
|
||||
.then(result => {
|
||||
app.detail = result.data.detail
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
const app = this
|
||||
// 构建页面参数
|
||||
const params = app.$getShareUrlParams({ articleId: app.articleId });
|
||||
return {
|
||||
title: app.detail.title,
|
||||
path: "/pages/article/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({ articleId: app.articleId });
|
||||
return {
|
||||
title: app.detail.title,
|
||||
path: "/pages/article/detail?" + params
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.article-content {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption"
|
||||
@up="upCallback">
|
||||
|
||||
<!-- tab栏 -->
|
||||
<u-tabs :list="tabList" :is-scroll="true" :current="curTab" active-color="#fd4a5f" :duration="0.2"
|
||||
@change="onChangeTab" />
|
||||
|
||||
<!-- 文章列表 -->
|
||||
<view class="article-list">
|
||||
<view class="article-item" :class="[`show-type__${item.show_type}`]" v-for="(item, index) in articleList.data"
|
||||
:key="index" @click="onTargetDetail(item.article_id)">
|
||||
<!-- 小图模式 -->
|
||||
<block v-if="item.show_type == 10">
|
||||
<view class="article-item__left flex-box">
|
||||
<view class="article-item__title">
|
||||
<text class="twoline-hide">{{ item.title }}</text>
|
||||
</view>
|
||||
<view class="article-item__footer m-top10">
|
||||
<text class="article-views f-24 col-8">{{ item.show_views }}次浏览</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="article-item__image">
|
||||
<image class="image" mode="widthFix" :src="item.image_url"></image>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 大图模式 -->
|
||||
<block v-if="item.show_type == 20">
|
||||
<view class="article-item__title">
|
||||
<text class="twoline-hide">{{ item.title }}</text>
|
||||
</view>
|
||||
<view class="article-item__image m-top20">
|
||||
<image class="image" mode="widthFix" :src="item.image_url"></image>
|
||||
</view>
|
||||
<view class="article-item__footer m-top10">
|
||||
<text class="article-views f-24 col-8">{{ item.show_views }}次浏览</text>
|
||||
</view>
|
||||
</block>
|
||||
</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 ArticleApi from '@/api/article'
|
||||
import * as CategoryApi from '@/api/article/category'
|
||||
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
|
||||
|
||||
const pageSize = 15
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
// 选项卡列表
|
||||
tabList: [],
|
||||
// 当前选项
|
||||
curTab: 0,
|
||||
// 文章列表
|
||||
articleList: getEmptyPaginateObj(),
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: true,
|
||||
// 每页数据的数量; 默认10
|
||||
page: { size: pageSize },
|
||||
// 数量要大于3条才显示无更多数据
|
||||
noMoreSize: 3,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
const app = this
|
||||
// 获取文章分类数据
|
||||
app.getCategoryList(options.categoryId)
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 上拉加载的回调 (页面初始化时也会执行一次)
|
||||
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
|
||||
* @param {Object} page
|
||||
*/
|
||||
upCallback(page) {
|
||||
const app = this
|
||||
// 设置列表数据
|
||||
app.getArticleList(page.num)
|
||||
.then(list => {
|
||||
const curPageLen = list.data.length
|
||||
const totalSize = list.data.total
|
||||
app.mescroll.endBySize(curPageLen, totalSize)
|
||||
})
|
||||
.catch(() => app.mescroll.endErr())
|
||||
},
|
||||
|
||||
// 获取文章分类数据
|
||||
getCategoryList(categoryId) {
|
||||
CategoryApi.list().then(result => {
|
||||
this.setTabList(result.data.list, categoryId)
|
||||
})
|
||||
},
|
||||
|
||||
// 设置选项卡数据
|
||||
setTabList(categoryList, categoryId) {
|
||||
const app = this
|
||||
app.tabList = [{ value: 0, name: '全部' }]
|
||||
categoryList.forEach(item => {
|
||||
app.tabList.push({ value: item.category_id, name: item.name })
|
||||
})
|
||||
if (categoryId > 0) {
|
||||
const findIndex = app.tabList.findIndex(item => item.value == categoryId)
|
||||
app.curTab = findIndex > -1 ? findIndex : 0
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取文章列表
|
||||
* @param {Number} pageNo 页码
|
||||
*/
|
||||
getArticleList(pageNo = 1) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
ArticleApi.list({ categoryId: app.getTabValue(), page: pageNo }, { load: false })
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.articleList.data = getMoreListData(newList, app.articleList, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 切换标签项
|
||||
onChangeTab(index) {
|
||||
// 设置当前选中的标签
|
||||
this.curTab = index
|
||||
// 刷新订单列表
|
||||
this.onRefreshList()
|
||||
},
|
||||
|
||||
// 获取当前标签项的值
|
||||
getTabValue() {
|
||||
const app = this
|
||||
return app.tabList.length ? app.tabList[app.curTab].value : 0
|
||||
},
|
||||
|
||||
// 刷新列表数据
|
||||
onRefreshList() {
|
||||
this.articleList = getEmptyPaginateObj()
|
||||
setTimeout(() => this.mescroll.resetUpScroll(), 120)
|
||||
},
|
||||
|
||||
// 跳转文章详情页
|
||||
onTargetDetail(articleId) {
|
||||
this.$navTo('pages/article/detail', { articleId })
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: '文章首页',
|
||||
path: "/pages/article/index?" + this.$getShareUrlParams()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享到朋友圈
|
||||
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta)
|
||||
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
|
||||
*/
|
||||
onShareTimeline() {
|
||||
return {
|
||||
title: '文章首页',
|
||||
path: "/pages/article/index?" + this.$getShareUrlParams()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
// 文章列表
|
||||
.article-list {
|
||||
padding-top: 20rpx;
|
||||
line-height: 1;
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
|
||||
.article-item {
|
||||
margin-bottom: 20rpx;
|
||||
padding: 30rpx;
|
||||
background: #fff;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.article-item__title {
|
||||
max-height: 74rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 38rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.article-item__image .image {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
// 小图模式
|
||||
.show-type__10 {
|
||||
display: flex;
|
||||
|
||||
.article-item__left {
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
|
||||
.article-item__title {
|
||||
// min-height: 72rpx;
|
||||
}
|
||||
|
||||
.article-item__image .image {
|
||||
width: 240rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 大图模式
|
||||
.show-type__20 .article-item__image .image {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,500 @@
|
||||
<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" :max="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
|
||||
// 清除checkedIds中无效的ID
|
||||
app.onClearInvalidId()
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 清除checkedIds中无效的ID
|
||||
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>
|
||||
@@ -0,0 +1,414 @@
|
||||
<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: () => {}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
list: {
|
||||
immediate: true,
|
||||
handler(newList) {
|
||||
// && this.curIndex === -1
|
||||
if (newList.length > 0) {
|
||||
this.curIndex = 0
|
||||
this.onRefreshList()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
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>
|
||||
@@ -0,0 +1,110 @@
|
||||
<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>
|
||||
@@ -0,0 +1,180 @@
|
||||
<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>
|
||||
@@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 搜索框 -->
|
||||
<search class="search" tips="搜索商品" @event="$navTo('pages/search/index')" />
|
||||
<select-province class='province' ref="choiceProvince" :provinces="provinceList" :value="province" @change="onProvinceChange" v-model="province" />
|
||||
<!-- 一级分类 -->
|
||||
<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 SelectProvince from '@/components/select-province/select-province'
|
||||
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,
|
||||
SelectProvince
|
||||
},
|
||||
mixins: [MescrollCompMixin],
|
||||
data() {
|
||||
return {
|
||||
// 枚举类
|
||||
PageCategoryStyleEnum,
|
||||
// 分类列表
|
||||
list: [],
|
||||
// 分类模板设置
|
||||
setting: {},
|
||||
trade: {},
|
||||
// 正在加载中
|
||||
isLoading: true,
|
||||
// 地区
|
||||
provinceList: [],
|
||||
province: '',
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad() {
|
||||
this.initPage()
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
// 每间隔5分钟自动刷新一次页面数据
|
||||
const curTime = new Date().getTime()
|
||||
if ((curTime - lastRefreshTime) > 5 * 60 * 1000) {
|
||||
this.initPage()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async initPage(){
|
||||
try {
|
||||
// 第一步:获取商城设置(等待返回)
|
||||
const setting = await SettingModel.data(false) // 如果你不传参数,默认根据配置决定是否用缓存
|
||||
console.log('商城设置:', setting)
|
||||
const trade = setting[SettingKeyEnum.TRADE.value]
|
||||
this.provinceList = trade.province_list.split(',')
|
||||
// 第二步:获取本地已选择的省份
|
||||
const savedProvince = uni.getStorageSync('province')
|
||||
|
||||
if (savedProvince) {
|
||||
this.province = savedProvince
|
||||
this.onRefreshPage()
|
||||
} else {
|
||||
this.$refs.choiceProvince.open()
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error('获取商城设置失败:', e)
|
||||
}
|
||||
},
|
||||
|
||||
// 刷新页面
|
||||
onRefreshPage() {
|
||||
// 记录刷新时间
|
||||
lastRefreshTime = new Date().getTime()
|
||||
// 获取页面数据
|
||||
this.getPageData()
|
||||
// 更新购物车角标
|
||||
setCartTabBadge()
|
||||
},
|
||||
|
||||
// 获取页面数据
|
||||
getPageData() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([
|
||||
// 获取分类模板设置
|
||||
// 优化建议: 可以将此处的false改为true 启用缓存
|
||||
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]
|
||||
this.trade = setting[SettingKeyEnum.TRADE.value]
|
||||
this.provinceList = this.trade.province_list.split(',')
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化分类列表数据
|
||||
* @param {Object} result
|
||||
*/
|
||||
initCategory(result) {
|
||||
this.list = result.data.list
|
||||
},
|
||||
|
||||
onProvinceChange(val) {
|
||||
this.province = val
|
||||
// 存储省份
|
||||
uni.setStorageSync('province', val)
|
||||
// 加载页面数据
|
||||
this.onRefreshPage()
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置分享内容
|
||||
*/
|
||||
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;
|
||||
width: 78%;
|
||||
}
|
||||
.province {
|
||||
position: fixed;
|
||||
top: var(--window-top);
|
||||
right: var(--window-right);
|
||||
width: 20%;
|
||||
height: 40px;
|
||||
z-index: 9;
|
||||
line-height: 40px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,545 @@
|
||||
<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 => {
|
||||
// 绑定手机号
|
||||
if (err.result) {
|
||||
const errData = err.result.data
|
||||
if(errData.isBindMobile){
|
||||
app.navToBindMobile()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 初始化数据
|
||||
initData({ order, setting, personal }) {
|
||||
const app = this
|
||||
app.order = order
|
||||
app.selectCouponId = order.couponId
|
||||
app.personal = personal
|
||||
app.setting = setting
|
||||
// 显示错误信息
|
||||
if (order.hasError) {
|
||||
app.$toast(order.errorMsg)
|
||||
}
|
||||
// 当前选择的配送方式
|
||||
app.curDelivery = order.delivery
|
||||
// 如果只有一种配送方式则不显示选项卡
|
||||
app.isShowTab = setting.deliveryType.length > 1
|
||||
// 当前选择支付方式 (如果是微信小程序默认使用微信支付)
|
||||
console.log(app.platform)
|
||||
if (app.platform === 'MP-WEIXIN') {
|
||||
app.curPayType = PayTypeEnum.WECHAT.value
|
||||
}
|
||||
console.log(app.curPayType)
|
||||
},
|
||||
|
||||
// 获取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 = -1
|
||||
// 重新获取订单信息
|
||||
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 => {
|
||||
console.log(err);
|
||||
if (err.result) {
|
||||
const errData = err.result.data
|
||||
if (errData.is_created) {
|
||||
app.navToMyOrder()
|
||||
return false
|
||||
}else if(errData.isBindMobile){
|
||||
app.navToBindMobile()
|
||||
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)
|
||||
},
|
||||
|
||||
// 跳转到绑定手机号页面(等待1秒)
|
||||
navToBindMobile() {
|
||||
setTimeout(() => {
|
||||
this.$navTo('pages/user/bind/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>
|
||||
@@ -0,0 +1,445 @@
|
||||
|
||||
// 配送信息
|
||||
.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
<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>
|
||||
@@ -0,0 +1,250 @@
|
||||
<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>
|
||||
@@ -0,0 +1,120 @@
|
||||
<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>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,162 @@
|
||||
<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>
|
||||
@@ -0,0 +1,158 @@
|
||||
<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>
|
||||
@@ -0,0 +1,173 @@
|
||||
<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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,227 @@
|
||||
.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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
<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>
|
||||
@@ -0,0 +1,473 @@
|
||||
<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, // 列表显示方式 (true列表、false平铺)
|
||||
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
|
||||
let location = options.location ? options.location : ''
|
||||
if(location == 1){
|
||||
uni.setNavigationBarTitle({
|
||||
title: '当前城市可用油券'
|
||||
});
|
||||
}
|
||||
// 设置默认列表显示方式
|
||||
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)
|
||||
if(curPageLen === 0){
|
||||
app.$toast('您所在省份暂无油卡')
|
||||
}
|
||||
})
|
||||
.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 || '',
|
||||
location: app.options.location || '',
|
||||
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>
|
||||
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption"
|
||||
@up="upCallback">
|
||||
<view class="help cont-box b-f" v-for="(item, index) in list.data" :key="index">
|
||||
<view class="title">
|
||||
<text>{{ item.title }}</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<text>{{ item.content }}</text>
|
||||
</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 HelpApi from '@/api/help'
|
||||
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.getHelpList(page.num)
|
||||
.then(list => {
|
||||
const curPageLen = list.data.length
|
||||
const totalSize = list.data.total
|
||||
app.mescroll.endBySize(curPageLen, totalSize)
|
||||
})
|
||||
.catch(() => app.mescroll.endErr())
|
||||
},
|
||||
|
||||
// 获取帮助列表
|
||||
getHelpList(pageNo = 1) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
HelpApi.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>
|
||||
.help {
|
||||
border-bottom: 1rpx solid #f6f6f9;
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 店铺页面组件 -->
|
||||
<Page :items="items" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setCartTabBadge } from '@/core/app'
|
||||
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();
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
// 更新购物车角标
|
||||
setCartTabBadge()
|
||||
},
|
||||
|
||||
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>
|
||||
@@ -0,0 +1,350 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
|
||||
<!-- 页面头部 -->
|
||||
<view class="header">
|
||||
<view class="title">
|
||||
<text>手机号登录</text>
|
||||
</view>
|
||||
<view class="sub-title">
|
||||
<text>未注册的手机号登录后将自动注册</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 表单 -->
|
||||
<view class="login-form">
|
||||
<!-- 手机号 -->
|
||||
<view class="form-item">
|
||||
<input class="form-item--input" type="number" v-model="mobile" maxlength="11" placeholder="请输入手机号码" />
|
||||
</view>
|
||||
<!-- 图形验证码 -->
|
||||
<view class="form-item">
|
||||
<input class="form-item--input" type="text" v-model="captchaCode" maxlength="5" placeholder="请输入图形验证码" />
|
||||
<view class="form-item--parts">
|
||||
<view class="captcha" @click="getCaptcha()">
|
||||
<image class="image" :src="captcha.base64"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 短信验证码 -->
|
||||
<view class="form-item">
|
||||
<input class="form-item--input" type="number" v-model="smsCode" maxlength="6" placeholder="请输入短信验证码" />
|
||||
<view class="form-item--parts">
|
||||
<view class="captcha-sms" @click="handelSmsCaptcha()">
|
||||
<text v-if="!smsState" class="activate">获取验证码</text>
|
||||
<text v-else class="un-activate">重新发送({{ times }})秒</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 登录按钮 -->
|
||||
<view class="login-button" @click="handleLogin">
|
||||
<text>登录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 微信授权手机号一键登录 -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<MpWeixinMobile :isParty="isParty" :partyData="partyData" />
|
||||
<!-- #endif -->
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import * as LoginApi from '@/api/login'
|
||||
import * as CaptchaApi from '@/api/captcha'
|
||||
import * as Verify from '@/utils/verify'
|
||||
import MpWeixinMobile from './mp-weixin-mobile'
|
||||
|
||||
// 倒计时时长(秒)
|
||||
const times = 60
|
||||
|
||||
// 表单验证场景
|
||||
const GET_CAPTCHA = 10
|
||||
const SUBMIT_LOGIN = 20
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MpWeixinMobile
|
||||
},
|
||||
|
||||
props: {
|
||||
// 是否存在第三方用户信息
|
||||
isParty: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
// 第三方用户信息数据
|
||||
partyData: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: false,
|
||||
// 图形验证码信息
|
||||
captcha: {},
|
||||
// 短信验证码发送状态
|
||||
smsState: false,
|
||||
// 倒计时
|
||||
times,
|
||||
// 手机号
|
||||
mobile: '',
|
||||
// 图形验证码
|
||||
captchaCode: '',
|
||||
// 短信验证码
|
||||
smsCode: ''
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
created() {
|
||||
console.log('main created')
|
||||
// 获取图形验证码
|
||||
this.getCaptcha()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取图形验证码
|
||||
getCaptcha() {
|
||||
const app = this
|
||||
CaptchaApi.image()
|
||||
.then(result => app.captcha = result.data)
|
||||
},
|
||||
|
||||
// 点击发送短信验证码
|
||||
handelSmsCaptcha() {
|
||||
const app = this
|
||||
if (!app.isLoading && !app.smsState && app.formValidation(GET_CAPTCHA)) {
|
||||
app.sendSmsCaptcha()
|
||||
}
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
formValidation(scene = GET_CAPTCHA) {
|
||||
const app = this
|
||||
// 验证获取短信验证码
|
||||
if (scene === GET_CAPTCHA) {
|
||||
if (!app.validteMobile(app.mobile) || !app.validteCaptchaCode(app.captchaCode)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// 验证提交登录
|
||||
if (scene === SUBMIT_LOGIN) {
|
||||
if (!app.validteMobile(app.mobile) || !app.validteSmsCode(app.smsCode)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 验证手机号
|
||||
validteMobile(str) {
|
||||
if (Verify.isEmpty(str)) {
|
||||
this.$toast('请先输入手机号')
|
||||
return false
|
||||
}
|
||||
if (!Verify.isMobile(str)) {
|
||||
this.$toast('请输入正确格式的手机号')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 验证图形验证码
|
||||
validteCaptchaCode(str) {
|
||||
if (Verify.isEmpty(str)) {
|
||||
this.$toast('请先输入图形验证码')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 验证短信验证码
|
||||
validteSmsCode(str) {
|
||||
if (Verify.isEmpty(str)) {
|
||||
this.$toast('请先输入短信验证码')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 请求发送短信验证码接口
|
||||
sendSmsCaptcha() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
CaptchaApi.sendSmsCaptcha({
|
||||
form: {
|
||||
captchaKey: app.captcha.key,
|
||||
captchaCode: app.captchaCode,
|
||||
mobile: app.mobile
|
||||
}
|
||||
})
|
||||
.then(result => {
|
||||
// 显示发送成功
|
||||
app.$toast(result.message)
|
||||
// 执行定时器
|
||||
app.timer()
|
||||
})
|
||||
.catch(() => app.getCaptcha())
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 执行定时器
|
||||
timer() {
|
||||
const app = this
|
||||
app.smsState = true
|
||||
const inter = setInterval(() => {
|
||||
app.times = app.times - 1
|
||||
if (app.times <= 0) {
|
||||
app.smsState = false
|
||||
app.times = times
|
||||
clearInterval(inter)
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
|
||||
// 点击登录
|
||||
handleLogin() {
|
||||
const app = this
|
||||
if (!app.isLoading && app.formValidation(SUBMIT_LOGIN)) {
|
||||
app.submitLogin()
|
||||
}
|
||||
},
|
||||
|
||||
// 确认登录
|
||||
submitLogin() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
store.dispatch('Login', {
|
||||
smsCode: app.smsCode,
|
||||
mobile: app.mobile,
|
||||
isParty: app.isParty,
|
||||
partyData: app.partyData
|
||||
})
|
||||
.then(result => {
|
||||
// 显示登录成功
|
||||
app.$toast(result.message)
|
||||
// 相应全局事件订阅: 刷新上级页面数据
|
||||
uni.$emit('syncRefresh', true)
|
||||
// 跳转回原页面
|
||||
setTimeout(() => app.onNavigateBack(1), 2000)
|
||||
})
|
||||
.catch(err => {
|
||||
// 跳转回原页面
|
||||
if (err.result.data.isBack) {
|
||||
setTimeout(() => app.onNavigateBack(1), 2000)
|
||||
}
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
/**
|
||||
* 登录成功-跳转回原页面
|
||||
*/
|
||||
onNavigateBack(delta = 1) {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack({
|
||||
delta: Number(delta || 1)
|
||||
})
|
||||
} else {
|
||||
this.$navTo('pages/index/index')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 100rpx 60rpx;
|
||||
min-height: 100vh;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
// 页面头部
|
||||
.header {
|
||||
margin-bottom: 60rpx;
|
||||
|
||||
.title {
|
||||
color: #191919;
|
||||
font-size: 54rpx;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
margin-top: 20rpx;
|
||||
color: #b3b3b3;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 输入框元素
|
||||
.form-item {
|
||||
display: flex;
|
||||
padding: 18rpx;
|
||||
border-bottom: 1rpx solid #f3f1f2;
|
||||
margin-bottom: 30rpx;
|
||||
height: 96rpx;
|
||||
|
||||
&--input {
|
||||
font-size: 28rpx;
|
||||
letter-spacing: 1rpx;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&--parts {
|
||||
min-width: 100rpx;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 图形验证码
|
||||
.captcha {
|
||||
height: 100%;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 192rpx;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// 短信验证码
|
||||
.captcha-sms {
|
||||
font-size: 28rpx;
|
||||
line-height: 50rpx;
|
||||
padding-right: 20rpx;
|
||||
|
||||
.activate {
|
||||
color: #cea26a;
|
||||
}
|
||||
|
||||
.un-activate {
|
||||
color: #9e9e9e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 登录按钮
|
||||
.login-button {
|
||||
width: 100%;
|
||||
height: 86rpx;
|
||||
margin-top: 80rpx;
|
||||
background: linear-gradient(to right, #ecb53c, #ff9211);
|
||||
color: #fff;
|
||||
border-radius: 80rpx;
|
||||
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.1);
|
||||
letter-spacing: 5rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<!-- 微信授权手机号一键登录 -->
|
||||
<view class="wechat-auth">
|
||||
<button class="btn-normal" open-type="getPhoneNumber" @getphonenumber="handelMpWeixinMobileLogin($event)" @click="clickPhoneNumber">
|
||||
<view class="wechat-auth-container">
|
||||
<image class="icon" src="../../../static/channel/wechat.png"></image>
|
||||
<text class="title">微信手机号一键登录</text>
|
||||
</view>
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { isEmpty } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// 是否存在第三方用户信息
|
||||
isParty: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
// 第三方用户信息数据
|
||||
partyData: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// 微信小程序登录凭证 (code)
|
||||
// 提交到后端,用于换取openid
|
||||
code: ''
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 按钮点击事件: 获取微信手机号按钮
|
||||
// 实现目的: 在getphonenumber事件触发之前获取微信登录code
|
||||
// 因为如果在getphonenumber事件中获取code的话,提交到后端的encryptedData会存在解密不了的情况
|
||||
async clickPhoneNumber() {
|
||||
this.code = await this.getCode()
|
||||
},
|
||||
|
||||
// 微信授权获取手机号一键登录
|
||||
// getphonenumber事件的回调方法
|
||||
async handelMpWeixinMobileLogin({ detail }) {
|
||||
const app = this
|
||||
if (detail.errMsg != 'getPhoneNumber:ok') {
|
||||
console.log('微信授权获取手机号失败', detail.errMsg)
|
||||
// app.$error(detail.errMsg)
|
||||
return
|
||||
}
|
||||
app.isLoading = true
|
||||
store.dispatch('LoginMpWxMobile', {
|
||||
code: app.code,
|
||||
encryptedData: detail.encryptedData,
|
||||
iv: detail.iv,
|
||||
isParty: app.isParty,
|
||||
partyData: app.partyData,
|
||||
refereeId: store.getters.refereeId
|
||||
})
|
||||
.then(result => {
|
||||
// 显示登录成功
|
||||
app.$toast(result.message)
|
||||
// 相应全局事件订阅: 刷新上级页面数据
|
||||
uni.$emit('syncRefresh', true)
|
||||
// 跳转回原页面
|
||||
setTimeout(() => app.onNavigateBack(1), 2000)
|
||||
})
|
||||
.catch(err => {
|
||||
const resultData = err.result.data
|
||||
// 显示错误信息
|
||||
if (isEmpty(resultData)) {
|
||||
app.$toast(err.result.message)
|
||||
}
|
||||
// 跳转回原页面
|
||||
if (resultData.isBack) {
|
||||
setTimeout(() => app.onNavigateBack(1), 2000)
|
||||
}
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取微信登录的code
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html
|
||||
getCode() {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
success: res => {
|
||||
console.log('code', res.code)
|
||||
resolve(res.code)
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 登录成功-跳转回原页面
|
||||
*/
|
||||
onNavigateBack(delta = 1) {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack({
|
||||
delta: Number(delta || 1)
|
||||
})
|
||||
} else {
|
||||
this.$navTo('pages/index/index')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 微信授权登录
|
||||
.wechat-auth {
|
||||
width: 320rpx;
|
||||
margin: 50rpx auto 0 auto;
|
||||
|
||||
.wechat-auth-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="wechatapp">
|
||||
<view class="header">
|
||||
<!-- <open-data class="avatar" type="userAvatarUrl"></open-data> -->
|
||||
<image class="image" :src="storeInfo && storeInfo.image_url ? storeInfo.image_url : '/static/default-avatar.png'"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="auth-title">申请获取以下权限</view>
|
||||
<view class="auth-subtitle">获得你的公开信息(昵称、头像等)</view>
|
||||
<view class="login-btn">
|
||||
<!-- 获取微信用户信息 -->
|
||||
<button class="button btn-normal" @click.stop="getUserProfile">授权登录</button>
|
||||
</view>
|
||||
<view class="no-login-btn">
|
||||
<button class="button btn-normal" @click="handleCancel">暂不登录</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { isEmpty } from '@/utils/util'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
|
||||
export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
// 商城基本信息
|
||||
storeInfo: undefined,
|
||||
// 微信小程序登录凭证 (code)
|
||||
// 提交到后端,用于换取openid
|
||||
code: ''
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
// 获取商城基本信息
|
||||
this.getStoreInfo()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取商城基本信息
|
||||
getStoreInfo() {
|
||||
SettingModel.item('store').then(store => this.storeInfo = store)
|
||||
|
||||
// SettingModel.h5Url(true)
|
||||
},
|
||||
|
||||
// 获取code
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html
|
||||
getCode() {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
success: res => {
|
||||
console.log('code', res.code)
|
||||
resolve(res.code)
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取微信用户信息(新版)
|
||||
getUserProfile() {
|
||||
const app = this
|
||||
wx.canIUse('getUserProfile') && wx.getUserProfile({
|
||||
lang: 'zh_CN',
|
||||
desc: '获取用户相关信息',
|
||||
success({ userInfo }) {
|
||||
console.log('用户同意了授权')
|
||||
console.log('userInfo:', userInfo)
|
||||
// 授权成功事件
|
||||
app.onAuthSuccess(userInfo)
|
||||
},
|
||||
fail() {
|
||||
console.log('用户拒绝了授权')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 授权成功事件
|
||||
// 这里分为两个逻辑:
|
||||
// 1.将code和userInfo提交到后端,如果存在该用户 则实现自动登录,无需再填写手机号
|
||||
// 2.如果不存在该用户, 则显示注册页面, 需填写手机号
|
||||
// 3.如果后端报错了, 则显示错误信息
|
||||
async onAuthSuccess(userInfo) {
|
||||
const app = this
|
||||
// 提交到后端
|
||||
store.dispatch('LoginMpWx', {
|
||||
partyData: {
|
||||
code: await app.getCode(),
|
||||
oauth: 'MP-WEIXIN',
|
||||
userInfo
|
||||
},
|
||||
refereeId: store.getters.refereeId
|
||||
})
|
||||
.then(result => {
|
||||
// 一键登录成功
|
||||
app.$toast(result.message)
|
||||
// 相应全局事件订阅: 刷新上级页面数据
|
||||
uni.$emit('syncRefresh', true)
|
||||
// 跳转回原页面
|
||||
setTimeout(() => app.onNavigateBack(), 2000)
|
||||
})
|
||||
.catch(err => {
|
||||
const resultData = err.result.data
|
||||
// 显示错误信息
|
||||
if (isEmpty(resultData)) {
|
||||
app.$toast(err.result.message)
|
||||
}
|
||||
// 跳转回原页面
|
||||
if (resultData.isBack) {
|
||||
setTimeout(() => app.onNavigateBack(1), 2000)
|
||||
}
|
||||
// 判断还需绑定手机号
|
||||
if (resultData.isBindMobile) {
|
||||
app.onEmitSuccess(userInfo)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 将oauth提交给父级
|
||||
// 这里要重新获取code, 因为上一次获取的code不能复用(会报错)
|
||||
async onEmitSuccess(userInfo) {
|
||||
const app = this
|
||||
app.$emit('success', {
|
||||
oauth: 'MP-WEIXIN', // 第三方登录类型: MP-WEIXIN
|
||||
code: await app.getCode(), // 微信登录的code, 用于换取openid
|
||||
userInfo // 微信用户信息
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 暂不登录
|
||||
*/
|
||||
handleCancel() {
|
||||
// 跳转回原页面
|
||||
this.onNavigateBack()
|
||||
},
|
||||
|
||||
/**
|
||||
* 登录成功-跳转回原页面
|
||||
*/
|
||||
onNavigateBack(delta = 1) {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack({
|
||||
delta: Number(delta || 1)
|
||||
})
|
||||
} else {
|
||||
this.$navTo('pages/index/index')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 0 60rpx;
|
||||
font-size: 32rpx;
|
||||
background: #fff;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.wechatapp {
|
||||
padding: 80rpx 0 48rpx;
|
||||
border-bottom: 1rpx solid #e3e3e3;
|
||||
margin-bottom: 72rpx;
|
||||
text-align: center;
|
||||
|
||||
.header {
|
||||
width: 190rpx;
|
||||
height: 190rpx;
|
||||
border: 4rpx solid #fff;
|
||||
margin: 0 auto 0;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow: 2rpx 0 10rpx rgba(50, 50, 50, 0.3);
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.auth-title {
|
||||
color: #585858;
|
||||
font-size: 34rpx;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.auth-subtitle {
|
||||
color: #888;
|
||||
margin-bottom: 88rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
padding: 0 20rpx;
|
||||
|
||||
.button {
|
||||
height: 88rpx;
|
||||
background: #04be01;
|
||||
color: #fff;
|
||||
font-size: 30rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.no-login-btn {
|
||||
margin-top: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
|
||||
.button {
|
||||
height: 88rpx;
|
||||
background: #dfdfdf;
|
||||
color: #fff;
|
||||
font-size: 30rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<view v-if="isLoad" class="login">
|
||||
<MpWeixin v-if="isMpWeixinAuth" @success="onGetUserInfoSuccess" />
|
||||
<Main v-else :isParty="isParty" :partyData="partyData" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Main from './components/main'
|
||||
import MpWeixin from './components/mp-weixin'
|
||||
import SettingKeyEnum from '@/common/enum/setting/Key'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Main,
|
||||
MpWeixin
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// 数据加载完成 [防止在微信小程序端onLoad和view渲染同步进行]
|
||||
isLoad: false,
|
||||
// 是否显示微信小程序授权登录
|
||||
isMpWeixinAuth: false,
|
||||
// 是否存在第三方用户信息
|
||||
isParty: false,
|
||||
// 第三方用户信息数据
|
||||
partyData: {}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
async onLoad(options) {
|
||||
console.log('login onLoad')
|
||||
// 设置当前是否显示微信小程序授权登录
|
||||
await this.setShowUserInfo()
|
||||
// 数据加载完成
|
||||
this.isLoad = true
|
||||
console.log('isLoad', true)
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 设置当前是否显示微信小程序授权登录
|
||||
* - 条件1: 只有微信小程序才显示获取用户信息按钮
|
||||
* - 条件2: 后台设置是否已开启该选项 [后台-客户端-注册设置]
|
||||
*/
|
||||
async setShowUserInfo() {
|
||||
console.log('setShowUserInfo start')
|
||||
const app = this
|
||||
// 判断当前客户端是微信小程序, 并且支持getUserProfile接口
|
||||
const isMpWeixin = app.platform === 'MP-WEIXIN' && wx.canIUse('getUserProfile')
|
||||
|
||||
// 获取后台设置
|
||||
await SettingModel.item(SettingKeyEnum.REGISTER.value, false)
|
||||
.then(setting => {
|
||||
app.isMpWeixinAuth = isMpWeixin && setting.isOauthMpweixin
|
||||
console.log('setShowUserInfo complete')
|
||||
})
|
||||
},
|
||||
|
||||
// 获取到用户信息的回调函数
|
||||
onGetUserInfoSuccess({ oauth, code, userInfo }) {
|
||||
// 记录第三方用户信息数据
|
||||
this.partyData = { oauth, code, userInfo }
|
||||
// 显示注册页面
|
||||
this.onShowRegister()
|
||||
},
|
||||
|
||||
// 显示注册页面
|
||||
onShowRegister() {
|
||||
// 是否显示微信小程序授权登录
|
||||
this.isMpWeixinAuth = false
|
||||
// 已获取到了第三方用户信息
|
||||
this.isParty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,306 @@
|
||||
<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>
|
||||
@@ -0,0 +1,136 @@
|
||||
<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>
|
||||
@@ -0,0 +1,571 @@
|
||||
<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>
|
||||
@@ -0,0 +1,915 @@
|
||||
<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" v-if="order.address">
|
||||
<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.use_status" class="stata-text">已加油</text>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view v-else-if="order.order_status === 10 && goods.oil_status" class="action-btn" @click.stop="layerPayQrcode(goods.order_goods_id)">核销码</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef !MP-WEIXIN -->
|
||||
<view v-else-if="order.order_status === 10 && goods.oil_status" class="action-btn" @click.stop="handlePayQrcode(goods.order_goods_id)">核销码</view>
|
||||
<!-- #endif -->
|
||||
</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 v-if="order.order_status === 10 && goods.oil_status && !goods.use_status" class="order-countdown">
|
||||
<text class="m-r-6">剩余时间</text>
|
||||
<count-down :date="goods.expiration_time" separator="zh" theme="text" />
|
||||
</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'
|
||||
import CountDown from '@/components/countdown'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CountDown
|
||||
},
|
||||
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)
|
||||
},
|
||||
|
||||
// 获取核销码,弹窗展示二维码
|
||||
layerPayQrcode(orderGoodsId) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/qrcode/index?id=`+orderGoodsId
|
||||
});
|
||||
},
|
||||
|
||||
// 获取核销码,跳转第三方连接
|
||||
handlePayQrcode(orderGoodsId) {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
OrderApi.qrCode(app.orderId, {orderGoodsId: orderGoodsId})
|
||||
.then(result => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/webview/webview?url=`+encodeURIComponent(result.data.qrCode)
|
||||
});
|
||||
app.isLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 复制指定内容
|
||||
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-countdown {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
font-size: 14px;
|
||||
color: #fa2209;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 订单信息
|
||||
.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>
|
||||
@@ -0,0 +1,202 @@
|
||||
<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>
|
||||
@@ -0,0 +1,676 @@
|
||||
<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-child" v-for="(goods, idx) in item.goods" :key="idx">
|
||||
<view class="goods-item">
|
||||
<!-- 商品图片 -->
|
||||
<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 class="goods-refund">
|
||||
<text v-if="goods.use_status" class="stata-text">已加油</text>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view v-else-if="item.order_status === 10 && goods.oil_status" class="action-btn" @click.stop="layerPayQrcode(goods.order_goods_id)">核销码</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef !MP-WEIXIN -->
|
||||
<view v-else-if="item.order_status === 10 && goods.oil_status" class="action-btn" @click.stop="handlePayQrcode(goods.order_goods_id)">核销码</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 过期剩余时间 -->
|
||||
<view v-if="item.order_status === 10 && goods.oil_status && !goods.use_status" class="order-countdown">
|
||||
<text class="m-r-6">剩余时间</text>
|
||||
<count-down :date="goods.expiration_time" separator="zh" theme="text" />
|
||||
</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'
|
||||
import CountDown from '@/components/countdown'
|
||||
|
||||
// 每页记录数量
|
||||
const pageSize = 15
|
||||
|
||||
// tab栏数据
|
||||
const tabs = [{
|
||||
name: `全部`,
|
||||
value: 'all'
|
||||
}, {
|
||||
name: `待支付`,
|
||||
value: 'payment'
|
||||
},
|
||||
{
|
||||
name: `进行中`,
|
||||
value: 'progress'
|
||||
},
|
||||
{
|
||||
name: `已完成`,
|
||||
value: 'complete'
|
||||
}
|
||||
/*{
|
||||
name: `待发货`,
|
||||
value: 'delivery'
|
||||
}, {
|
||||
name: `待收货`,
|
||||
value: 'received'
|
||||
}, {
|
||||
name: `待评价`,
|
||||
value: 'comment'
|
||||
}*/]
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody,
|
||||
CountDown
|
||||
},
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 获取核销码,弹窗展示二维码
|
||||
layerPayQrcode(orderGoodsId) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/qrcode/index?id=`+orderGoodsId
|
||||
});
|
||||
},
|
||||
|
||||
// 获取核销码,跳转第三方连接
|
||||
handlePayQrcode(orderGoodsId) {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
OrderApi.qrCode(app.orderId, {orderGoodsId: orderGoodsId})
|
||||
.then(result => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/webview/webview?url=`+encodeURIComponent(result.data.qrCode)
|
||||
});
|
||||
app.isLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 确认收货
|
||||
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-child{
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.order-countdown {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
font-size: 14px;
|
||||
color: #fa2209;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
// 商品项
|
||||
.goods-item {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 订单合计
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 商品售后
|
||||
.goods-refund {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10px;
|
||||
|
||||
.stata-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
border-radius: 28rpx;
|
||||
padding: 8rpx 26rpx;
|
||||
font-size: 24rpx;
|
||||
color: #383838;
|
||||
border: 1rpx solid #a8a8a8;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,127 @@
|
||||
<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>
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view v-if="base64" class="cate-img">
|
||||
<image class="image" mode="scaleToFill" :src="base64"></image>
|
||||
</view>
|
||||
<empty v-else :tips="tip" :isLoading="isLoading" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as OrderApi from '@/api/order'
|
||||
import Empty from '@/components/empty'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Empty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 二维码base64编码
|
||||
base64: '',
|
||||
// 正在加载中
|
||||
isLoading: true,
|
||||
// 空布局
|
||||
tip: '未获取到核销码'
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
let id = options.id ? options.id : ''
|
||||
if(id){
|
||||
// 获取支付二维码
|
||||
this.getQrCode(id)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 获取二维码的base64编码
|
||||
* @param {string} id
|
||||
*/
|
||||
getQrCode(id) {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
OrderApi.qrCodeImg({orderGoodsId: id})
|
||||
.then(result => {
|
||||
app.base64 = result.data.base64
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cate-img {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 100%;
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,429 @@
|
||||
<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>
|
||||
@@ -0,0 +1,483 @@
|
||||
<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>
|
||||
@@ -0,0 +1,263 @@
|
||||
<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>
|
||||
@@ -0,0 +1,227 @@
|
||||
<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>
|
||||
@@ -0,0 +1,383 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 地图显示 -->
|
||||
<map
|
||||
id="map"
|
||||
:latitude="userLocation.latitude"
|
||||
:longitude="userLocation.longitude"
|
||||
:markers="markers"
|
||||
show-location
|
||||
:scale="scale"
|
||||
style="width: 100%; height: 100vh;"
|
||||
@markertap="onMarkerTap">
|
||||
<cover-image class="img-map1" src="/static/center.png"></cover-image>
|
||||
<cover-image class="img-map2" src="/static/locate.png" @click="clickcontrol"></cover-image>
|
||||
</map>
|
||||
|
||||
<!-- 加油站列表 -->
|
||||
<view class="station-list">
|
||||
<view class="station-item" v-if="now.id != undefined">
|
||||
<view class="station-info">
|
||||
<text class="station-name">{{ now.name }}</text>
|
||||
<view>
|
||||
<text class="station-distance">距您 {{ now.distance }}km</text>
|
||||
<text class="station-addres">{{ now.address }}</text>
|
||||
</view>
|
||||
<view class="station-oil">
|
||||
<text>0#</text>
|
||||
<text>92#</text>
|
||||
<text>95#</text>
|
||||
</view>
|
||||
<view class="station-time-info">
|
||||
<view class="time-info-time">00:00-23:59 营业</view>
|
||||
<view class="time-info-plan">
|
||||
<text>便利点</text>
|
||||
<text>卫生间</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="station-action">
|
||||
<button class="navigate-button" @click="navigate()">导航加油站</button>
|
||||
<button class="navigate-button" @click="oil()">去购券</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="station-item" v-else>
|
||||
<view class="station-info">
|
||||
<text class="station-name">您所在的城市,暂无可用加油站</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import * as StationApi from '@/api/station'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
userLocation: {
|
||||
latitude: 0,
|
||||
longitude: 0
|
||||
},
|
||||
markers: [
|
||||
],
|
||||
scale: 13,
|
||||
now: {}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getUserLocation();
|
||||
},
|
||||
methods: {
|
||||
// 获取用户当前的位置
|
||||
getUserLocation() {
|
||||
// let latitude = '25.068413'
|
||||
// let longitude = '118.522990'
|
||||
// this.userLocation.latitude = latitude;
|
||||
// this.userLocation.longitude = longitude;
|
||||
// // 存储经纬度到缓存中
|
||||
// uni.setStorageSync('latitude', latitude)
|
||||
// uni.setStorageSync('longitude', longitude)
|
||||
|
||||
// uni.setStorageSync('province', '福建省')
|
||||
// uni.setStorageSync('city', '泉州市')
|
||||
// uni.setStorageSync('district', '南安市')
|
||||
// this.mapapiStation()
|
||||
let app = this
|
||||
// 获取经纬度
|
||||
uni.authorize({
|
||||
scope: 'scope.userLocation',
|
||||
success() {
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: (res) => {
|
||||
app.userLocation.latitude = res.latitude;
|
||||
app.userLocation.longitude = res.longitude;
|
||||
// 存储经纬度到缓存中
|
||||
uni.setStorageSync('latitude', res.latitude)
|
||||
uni.setStorageSync('longitude', res.longitude)
|
||||
|
||||
app.getRegion(res.latitude, res.longitude).then(region => {
|
||||
uni.setStorageSync('province', region.province)
|
||||
uni.setStorageSync('city', region.city)
|
||||
uni.setStorageSync('district', region.district)
|
||||
app.mapapiStation(res.latitude, res.longitude)
|
||||
}).catch(err => {
|
||||
console.error('获取区域信息失败:', err);
|
||||
});
|
||||
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取位置失败', err);
|
||||
}
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('授权失败', err);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取加油站点
|
||||
mapapiStation(latitude, longitude) {
|
||||
let app = this
|
||||
let key = '67NBZ-MHUH4-ODCU2-KNPZW-3OGJF-ARBRQ';
|
||||
let url = `https://apis.map.qq.com/ws/place/v1/search?key=${key}&keyword=中国石油&boundary=nearby(${latitude},${longitude},1000)&page_size=20`;
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: url,
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
var markers = [] //地图markers标记点
|
||||
var arr = res.data.data
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
markers.push({
|
||||
iconPath: "/static/marker.png",
|
||||
id: i,
|
||||
name: arr[i].title,
|
||||
latitude: arr[i].location.lat,
|
||||
longitude: arr[i].location.lng,
|
||||
address: arr[i].address,
|
||||
distance: (arr[i]._distance/1000).toFixed(2),
|
||||
width: 32,
|
||||
height: 32
|
||||
})
|
||||
}
|
||||
app.markers = markers
|
||||
app.now = markers[0]
|
||||
resolve(markers)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('请求失败:', err);
|
||||
reject(err); // 请求失败处理
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// 逆你解析地址
|
||||
getRegion(latitude, longitude) {
|
||||
let key = '67NBZ-MHUH4-ODCU2-KNPZW-3OGJF-ARBRQ'; // ETFBZ-F4XCW-YOQRI-RWBQM-BAZIQ-2VFS5
|
||||
let url = `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}&key=${key}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: url,
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
let data = res.data;
|
||||
if (data.status === 0) { // 腾讯地图API通常使用0表示成功
|
||||
let address = data.result.address; // 完整地址
|
||||
let province = data.result.address_component.province; // 省
|
||||
let city = data.result.address_component.city; // 市
|
||||
let district = data.result.address_component.district; // 区/县
|
||||
|
||||
let region = {
|
||||
address: address,
|
||||
province: province,
|
||||
city: city,
|
||||
district: district,
|
||||
};
|
||||
resolve(region); // 成功时返回region对象
|
||||
} else {
|
||||
console.error('逆地理编码失败:', data.message);
|
||||
reject(data.message); // 处理失败情况
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('请求失败:', err);
|
||||
reject(err); // 请求失败处理
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// 获取站点列表
|
||||
getStationList(pageNo = 1) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
StationApi.list({ page: pageNo })
|
||||
.then(result => {
|
||||
const newList = result.data.list
|
||||
app.markers = newList
|
||||
app.getRegion(newList[0].latitude, newList[0].longitude).then(region => {
|
||||
newList[0].address = region.address
|
||||
}).catch(err => {
|
||||
console.error('获取区域信息失败:', err);
|
||||
});
|
||||
app.now = newList[0]
|
||||
resolve(newList)
|
||||
})
|
||||
})
|
||||
},
|
||||
// 更新每个标记点与用户位置之间的距离
|
||||
updateDistances() {
|
||||
this.markers = this.markers.map(marker => {
|
||||
marker.distance = this.calculateDistance(
|
||||
this.userLocation.latitude,
|
||||
this.userLocation.longitude,
|
||||
marker.latitude,
|
||||
marker.longitude
|
||||
);
|
||||
return marker;
|
||||
});
|
||||
},
|
||||
// 计算两点之间的距离
|
||||
calculateDistance(lat1, lon1, lat2, lon2) {
|
||||
const radLat1 = this.rad(lat1);
|
||||
const radLat2 = this.rad(lat2);
|
||||
const a = radLat1 - radLat2;
|
||||
const b = this.rad(lon1) - this.rad(lon2);
|
||||
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
|
||||
Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
|
||||
s = s * 6378.137; // 地球半径(单位:公里)
|
||||
s = Math.round(s * 10000) / 10000;
|
||||
return s.toFixed(2);
|
||||
},
|
||||
rad(d) {
|
||||
return d * Math.PI / 180.0;
|
||||
},
|
||||
// 标记点点击事件处理
|
||||
onMarkerTap(event) {
|
||||
const markerId = event.detail.markerId;
|
||||
const marker = this.markers.find(m => m.id === markerId);
|
||||
if (marker) {
|
||||
// this.getRegion(marker.latitude, marker.longitude).then(region => {
|
||||
// marker.address = region.address
|
||||
// }).catch(err => {
|
||||
// console.error('获取区域信息失败:', err);
|
||||
// });
|
||||
this.now = marker;
|
||||
}
|
||||
},
|
||||
//右下角定位按钮的点击事件
|
||||
clickcontrol(){
|
||||
uni.createMapContext("map", this).moveToLocation({ //moveToLocation将地图中心移动到当前定位点,需要配合map组件的show-location使用
|
||||
latitude: this.userLocation.latitude,
|
||||
longitude: this.userLocation.longitude
|
||||
});
|
||||
},
|
||||
// 导航到标记点
|
||||
navigate() {
|
||||
uni.openLocation({
|
||||
latitude: Number(this.now.latitude),
|
||||
longitude: Number(this.now.longitude),
|
||||
name: this.now.name,
|
||||
address: this.now.address,
|
||||
scale: 18
|
||||
});
|
||||
},
|
||||
// 跳转商品详情页
|
||||
oil() {
|
||||
this.$navTo('pages/goods/list?location=1')
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.station-list {
|
||||
width: 100%;
|
||||
padding: 20rpx;
|
||||
position: fixed;
|
||||
bottom: 80rpx;
|
||||
height: 300rpx;
|
||||
}
|
||||
|
||||
.station-item {
|
||||
padding: 20rpx;
|
||||
background-color: #fff;
|
||||
margin-bottom: 10rpx;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
|
||||
height: 350rpx;
|
||||
}
|
||||
|
||||
.station-info {
|
||||
height: 250rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.station-name {
|
||||
font-size: 35rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.station-distance {
|
||||
font-size: 26rpx;
|
||||
margin-top: 4rpx;
|
||||
margin-right: 25rpx;
|
||||
}
|
||||
|
||||
.station-oil {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
.station-oil text{
|
||||
background-color: #5983b1;
|
||||
color: aliceblue;
|
||||
padding: 4rpx 8rpx;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.station-time-info{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
.station-time-info .time-info-time {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
.station-time-info .time-info-plan {
|
||||
font-size: 24rpx;
|
||||
display: flex;
|
||||
gap: 10rpx;
|
||||
}
|
||||
.station-time-info .time-info-plan text{
|
||||
padding: 4rpx;
|
||||
border: 1rpx solid #007aff;
|
||||
color: #007aff;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
.navigate-button {
|
||||
padding: 4rpx 14rpx;
|
||||
background-color: #007aff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4rpx;
|
||||
font-size: 22rpx;
|
||||
width: 150rpx;
|
||||
}
|
||||
|
||||
.navigate-button:active {
|
||||
background-color: #005bb5;
|
||||
}
|
||||
.station-action{
|
||||
display: flex;
|
||||
}
|
||||
.img-map1{
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
position: fixed;
|
||||
bottom: 47%;
|
||||
left: 46%;
|
||||
}
|
||||
.img-map2{
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
position: fixed;
|
||||
bottom: 30vh;
|
||||
right: 2vh;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
|
||||
<!-- 页面头部 -->
|
||||
<view class="header">
|
||||
<view class="title">
|
||||
<text>绑定您的手机号</text>
|
||||
</view>
|
||||
<view class="sub-title">
|
||||
<text>为了更好的服务您,请绑定手机号</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 表单 -->
|
||||
<view class="submit-form">
|
||||
<!-- 手机号 -->
|
||||
<view class="form-item">
|
||||
<input class="form-item--input" type="number" v-model="mobile" maxlength="11" placeholder="请输入手机号码" />
|
||||
</view>
|
||||
<!-- 图形验证码 -->
|
||||
<view class="form-item">
|
||||
<input class="form-item--input" type="text" v-model="captchaCode" maxlength="5" placeholder="请输入图形验证码" />
|
||||
<view class="form-item--parts">
|
||||
<view class="captcha" @click="getCaptcha()">
|
||||
<image class="image" :src="captcha.base64"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 短信验证码 -->
|
||||
<view class="form-item">
|
||||
<input class="form-item--input" type="number" v-model="smsCode" maxlength="6" placeholder="请输入短信验证码" />
|
||||
<view class="form-item--parts">
|
||||
<view class="captcha-sms" @click="handelSmsCaptcha()">
|
||||
<text v-if="!smsState" class="activate">获取验证码</text>
|
||||
<text v-else class="un-activate">重新发送({{ times }})秒</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 确认绑定 -->
|
||||
<view class="submit-button" @click="handleSubmit()">
|
||||
<text>确认绑定</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import * as UserApi from '@/api/user'
|
||||
import * as CaptchaApi from '@/api/captcha'
|
||||
import * as Verify from '@/utils/verify'
|
||||
|
||||
// 倒计时时长(秒)
|
||||
const times = 60
|
||||
|
||||
// 表单验证场景
|
||||
const GET_CAPTCHA = 10
|
||||
const FORM_SUBMIT = 20
|
||||
|
||||
export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: false,
|
||||
// 图形验证码信息
|
||||
captcha: {},
|
||||
// 短信验证码发送状态
|
||||
smsState: false,
|
||||
// 倒计时
|
||||
times,
|
||||
// 手机号
|
||||
mobile: '',
|
||||
// 图形验证码
|
||||
captchaCode: '',
|
||||
// 短信验证码
|
||||
smsCode: ''
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
created() {
|
||||
// 获取图形验证码
|
||||
this.getCaptcha()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取图形验证码
|
||||
getCaptcha() {
|
||||
const app = this
|
||||
CaptchaApi.image().then(result => app.captcha = result.data)
|
||||
},
|
||||
|
||||
// 点击发送短信验证码
|
||||
handelSmsCaptcha() {
|
||||
const app = this
|
||||
if (!app.isLoading && !app.smsState && app.formValidation(GET_CAPTCHA)) {
|
||||
app.sendSmsCaptcha()
|
||||
app.getCaptcha()
|
||||
}
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
formValidation(scene = GET_CAPTCHA) {
|
||||
const app = this
|
||||
// 验证获取短信验证码
|
||||
if (scene === GET_CAPTCHA) {
|
||||
if (!app.validteMobile(app.mobile) || !app.validteCaptchaCode(app.captchaCode)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// 验证表单提交
|
||||
if (scene === FORM_SUBMIT) {
|
||||
if (!app.validteMobile(app.mobile) || !app.validteSmsCode(app.smsCode)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 验证手机号
|
||||
validteMobile(str) {
|
||||
if (Verify.isEmpty(str)) {
|
||||
this.$toast('请先输入手机号')
|
||||
return false
|
||||
}
|
||||
if (!Verify.isMobile(str)) {
|
||||
this.$toast('请输入正确格式的手机号')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 验证图形验证码
|
||||
validteCaptchaCode(str) {
|
||||
if (Verify.isEmpty(str)) {
|
||||
this.$toast('请先输入图形验证码')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 验证短信验证码
|
||||
validteSmsCode(str) {
|
||||
if (Verify.isEmpty(str)) {
|
||||
this.$toast('请先输入短信验证码')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 请求发送短信验证码接口
|
||||
sendSmsCaptcha() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
CaptchaApi.sendSmsCaptcha({
|
||||
form: {
|
||||
captchaKey: app.captcha.key,
|
||||
captchaCode: app.captchaCode,
|
||||
mobile: app.mobile
|
||||
}
|
||||
})
|
||||
.then(result => {
|
||||
// 显示发送成功
|
||||
app.$toast(result.message)
|
||||
// 执行定时器
|
||||
app.timer()
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 执行定时器
|
||||
timer() {
|
||||
const app = this
|
||||
app.smsState = true
|
||||
const inter = setInterval(() => {
|
||||
app.times = app.times - 1
|
||||
if (app.times <= 0) {
|
||||
app.smsState = false
|
||||
app.times = times
|
||||
clearInterval(inter)
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
|
||||
// 点击提交
|
||||
handleSubmit() {
|
||||
const app = this
|
||||
if (!app.isLoading && app.formValidation(FORM_SUBMIT)) {
|
||||
app.onSubmitEvent()
|
||||
}
|
||||
},
|
||||
|
||||
// 确认提交事件
|
||||
onSubmitEvent() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
UserApi.bindMobile({ form: { smsCode: app.smsCode, mobile: app.mobile } })
|
||||
.then(result => {
|
||||
// 显示操作成功
|
||||
app.$toast(result.message)
|
||||
// 跳转回原页面
|
||||
setTimeout(() => {
|
||||
app.onNavigateBack(1)
|
||||
}, 2000)
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
/**
|
||||
* 提交成功-跳转回原页面
|
||||
*/
|
||||
onNavigateBack(delta) {
|
||||
uni.navigateBack({
|
||||
delta: Number(delta || 1)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 100rpx 60rpx;
|
||||
min-height: 100vh;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
// 页面头部
|
||||
.header {
|
||||
margin-bottom: 50rpx;
|
||||
|
||||
.title {
|
||||
color: #191919;
|
||||
font-size: 50rpx;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
margin-top: 20rpx;
|
||||
color: #b3b3b3;
|
||||
font-size: 25rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 输入框元素
|
||||
.form-item {
|
||||
display: flex;
|
||||
padding: 18rpx;
|
||||
border-bottom: 1rpx solid #f3f1f2;
|
||||
margin-bottom: 25rpx;
|
||||
height: 96rpx;
|
||||
|
||||
&--input {
|
||||
font-size: 26rpx;
|
||||
letter-spacing: 1rpx;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&--parts {
|
||||
min-width: 100rpx;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 图形验证码
|
||||
.captcha {
|
||||
height: 100%;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 192rpx;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// 短信验证码
|
||||
.captcha-sms {
|
||||
font-size: 22rpx;
|
||||
line-height: 50rpx;
|
||||
padding-right: 20rpx;
|
||||
|
||||
.activate {
|
||||
color: #cea26a;
|
||||
}
|
||||
|
||||
.un-activate {
|
||||
color: #9e9e9e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 提交按钮
|
||||
.submit-button {
|
||||
width: 100%;
|
||||
height: 86rpx;
|
||||
margin-top: 70rpx;
|
||||
// background: #cea26a;
|
||||
background: linear-gradient(to right, #ecb53c, #ff9211);
|
||||
text-align: center;
|
||||
line-height: 86rpx;
|
||||
color: #fff;
|
||||
border-radius: 80rpx;
|
||||
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.1);
|
||||
letter-spacing: 5rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,720 @@
|
||||
<template>
|
||||
<view v-if="!isFirstload" class="container">
|
||||
<!-- 页面头部 -->
|
||||
<view class="main-header" :style="{ height: platform == 'H5' ? '260rpx' : '320rpx', paddingTop: platform == 'H5' ? '0' : '80rpx' }">
|
||||
<image class="bg-image" src="/static/background/user-header2.png" mode="scaleToFill"></image>
|
||||
<!-- 用户信息 -->
|
||||
<view v-if="isLogin" class="user-info">
|
||||
<view class="user-avatar" @click="handlePersonal()">
|
||||
<avatar-image :url="userInfo.avatar_url" :width="100" />
|
||||
</view>
|
||||
<view class="user-content">
|
||||
<!-- 会员昵称 -->
|
||||
<view class="nick-name oneline-hide" @click="handlePersonal()">{{ userInfo.nick_name }}</view>
|
||||
<!-- 会员等级 -->
|
||||
<view v-if="userInfo.grade_id > 0 && userInfo.grade" class="user-grade">
|
||||
<view class="user-grade_icon">
|
||||
<image class="image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAA0lBMVEUAAAD/tjL/tzH/uDP/uC7/tjH/tzH/tzL/tTH+tTL+tjP/tDD/tTD+tzD/tjL/szD/uDH/tjL/tjL+tjD/tjT/szb/tzL/tTL+uTH+tjL/tjL/tjL/tTT/tjL/tjL+tjH/uTL/vDD/tjL/tjH/tzL9uS//tTL/nBr/sS7/tjH/ujL/szD/uTv+rzf/tzL+tzH+vDP+uzL+tjP+ry7+tDL9ki/7szf/sEX/tTL/tjL+tjL/tTH/tTT/tzH/tzL/tjP/sTX/uTP/wzX+rTn/vDX9vC8m8ckhAAAAOXRSTlMAlnAMB/vjxKWGMh0S6drMiVxPRkEY9PLy0ru0sKagmo5+dGtgVCMgBP716eXWyMGxqJGRe2o5KSmFNjaYAAABP0lEQVQ4y8XS13KDMBAF0AWDDe4t7r3ETu9lVxJgJ/n/X8rKAzHG5TE+Twz3zki7I/g/KXdghIbGJewrU4yzn08Ebgl6TuZzzuOC6W5es3HX6qsSz3NFShRU0MpucytDmOSpu3yULx3CA9RD1HjVedc0jSjqm6ZzhUjDsFDQhSp/OKj5GQvg0+ZCOixsbtDLAeTTOm/yGi8GyIphIVsgH737FEDV44LJa88IRKK/SetrwT9G/GUIr6vXjoy4GXn7+RboVXnghuSjaoGecwQxL2su3CwAKlO+QFoqxI4FMctHQhQd2OhxTu184jWUlI+rMTBTn1/IQcJHQ6GQdZ7pWiDaNdhTt330efISeiqYwQEzQpTlsURJLhzkEmpCPsERfeIUVyXr6MNuIyp5uziW6xURtt7hhGwzmMNJExfO4Bd9X0ZPqAxdNwAAAABJRU5ErkJggg==">
|
||||
</image>
|
||||
</view>
|
||||
<view class="user-grade_name">
|
||||
<text>{{ userInfo.grade.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 会员无等级时显示手机号 -->
|
||||
<view v-else class="mobile">{{ userInfo.mobile }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 未登录 -->
|
||||
<view v-else class="user-info" @click="handleLogin">
|
||||
<view class="user-avatar">
|
||||
<avatar-image :width="100" />
|
||||
</view>
|
||||
<view class="user-content">
|
||||
<view class="nick-name">未登录</view>
|
||||
<view class="login-tips">点击登录账号</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 绑定手机号 -->
|
||||
<view v-if="isLogin && !userInfo.mobile && setting[SettingKeyEnum.REGISTER.value].isManualBind" class="my-mobile"
|
||||
@click="handleBindMobile()">
|
||||
<view class="info">点击绑定手机号,确保账户安全</view>
|
||||
<view class="btn-bind">去绑定</view>
|
||||
</view>
|
||||
|
||||
<!-- 我的钱包 -->
|
||||
<view class="my-asset">
|
||||
<view class="asset-left flex-box dis-flex flex-x-around">
|
||||
<view class="asset-left-item" style="max-width: 200rpx;" @click="onTargetWallet">
|
||||
<view class="item-value dis-flex flex-x-center">
|
||||
<text>{{ isLogin ? assets.balance : '--' }}</text>
|
||||
</view>
|
||||
<view class="item-name dis-flex flex-x-center">
|
||||
<text class="oneline-hide">账户余额</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="asset-left-item" @click="onTargetPoints">
|
||||
<view class="item-value dis-flex flex-x-center">
|
||||
<text>{{ isLogin ? assets.points : '--' }}</text>
|
||||
</view>
|
||||
<view class="item-name dis-flex flex-x-center">
|
||||
<text class="oneline-hide">{{ setting[SettingKeyEnum.POINTS.value].points_name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="asset-left-item" @click="onTargetMyCoupon">
|
||||
<view class="item-value dis-flex flex-x-center">
|
||||
<text class="oneline-hide">{{ isLogin ? assets.coupon : '--' }}</text>
|
||||
</view>
|
||||
<view class="item-name dis-flex flex-x-center">
|
||||
<text>优惠券</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="asset-right">
|
||||
<view class="asset-right-item" @click="onTargetWallet">
|
||||
<view class="item-icon dis-flex flex-x-center">
|
||||
<text class="iconfont icon-qianbao"></text>
|
||||
</view>
|
||||
<view class="item-name dis-flex flex-x-center">
|
||||
<text>我的钱包</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单操作 -->
|
||||
<view class="order-navbar">
|
||||
<view class="order-navbar-item" v-for="(item, index) in orderNavbar" :key="index" @click="onTargetOrder(item)">
|
||||
<view class="item-icon">
|
||||
<text class="iconfont" :class="[`icon-${item.icon}`]"></text>
|
||||
</view>
|
||||
<view class="item-name">{{ item.name }}</view>
|
||||
<view class="item-badge" v-if="item.count && item.count > 0">
|
||||
<text v-if="item.count <= 99" class="text">{{ item.count }}</text>
|
||||
<text v-else class="text">99+</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 我的服务 -->
|
||||
<view class="my-service">
|
||||
<view class="service-title">我的服务</view>
|
||||
<view class="service-content clearfix">
|
||||
<block v-for="(item, index) in service" :key="index">
|
||||
<view v-if="item.type == 'link'" class="service-item" @click="handleService(item)">
|
||||
<view class="item-icon">
|
||||
<text class="iconfont" :class="[`icon-${item.icon}`]"></text>
|
||||
</view>
|
||||
<view class="item-name">{{ item.name }}</view>
|
||||
<view class="item-badge" v-if="item.count && item.count > 0">
|
||||
<text v-if="item.count <= 99" class="text">{{ item.count }}</text>
|
||||
<text v-else class="text">99+</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="item.type == 'button' && platform == 'MP-WEIXIN'" class="service-item">
|
||||
<button class="btn-normal" :open-type="item.openType">
|
||||
<view class="item-icon">
|
||||
<text class="iconfont" :class="[`icon-${item.icon}`]"></text>
|
||||
</view>
|
||||
<view class="item-name">{{ item.name }}</view>
|
||||
</button>
|
||||
</view>
|
||||
<view v-if="item.type == 'phone'" class="service-item">
|
||||
<button class="btn-normal" @click="callPhone()">
|
||||
<view class="item-icon">
|
||||
<text class="iconfont" :class="[`icon-${item.icon}`]"></text>
|
||||
</view>
|
||||
<view class="item-name">{{ item.name }}</view>
|
||||
</button>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<view v-if="isLogin" class="my-logout">
|
||||
<view class="logout-btn" @click="handleLogout()">
|
||||
<text>退出登录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import AvatarImage from '@/components/avatar-image'
|
||||
import { setCartTabBadge } from '@/core/app'
|
||||
import SettingKeyEnum from '@/common/enum/setting/Key'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
import * as UserApi from '@/api/user'
|
||||
import * as OrderApi from '@/api/order'
|
||||
import { checkLogin } from '@/core/app'
|
||||
|
||||
// 订单操作
|
||||
const orderNavbar = [
|
||||
{ id: 'all', name: '全部订单', icon: 'qpdingdan' },
|
||||
{ id: 'payment', name: '待支付', icon: 'daifukuan', count: 0 },
|
||||
// { id: 'delivery', name: '待发货', icon: 'daifahuo', count: 0 },
|
||||
// { id: 'received', name: '待收货', icon: 'daishouhuo', count: 0 },
|
||||
{ id: 'progress', name: '进行中', icon: 'daifahuo', count: 0 },
|
||||
{ id: 'complete', name: '已完成', icon: 'daishouhuo', count: 0 },
|
||||
]
|
||||
|
||||
/**
|
||||
* 我的服务
|
||||
* id: 标识; name: 标题名称; icon: 图标; type 类型(link和button); url: 跳转的链接
|
||||
*/
|
||||
const service = [
|
||||
{ id: 'address', name: '收货地址', icon: 'shouhuodizhi', type: 'link', url: 'pages/address/index' },
|
||||
{ id: 'coupon', name: '领券中心', icon: 'lingquan', type: 'link', url: 'pages/coupon/index' },
|
||||
// { id: 'myCoupon', name: '优惠券', icon: 'youhuiquan', type: 'link', url: 'pages/my-coupon/index' },
|
||||
{ id: 'contact', name: '在线客服', icon: 'kefu', type: 'button', openType: 'contact' },
|
||||
{ id: 'points', name: '我的积分', icon: 'jifen', type: 'link', url: 'pages/points/log' },
|
||||
{ id: 'refund', name: '退换/售后', icon: 'shouhou', type: 'link', url: 'pages/refund/index', count: 0 },
|
||||
{ id: 'orderCenter', name: '订单中心', icon: 'order-c', type: 'link', url: 'pages/order/center' },
|
||||
{ id: 'help', name: '我的帮助', icon: 'bangzhu', type: 'link', url: 'pages/help/index' },
|
||||
{ id: 'phone', name: '客服电话', icon: 'dianhua', type: 'phone', openType: 'contact' },
|
||||
]
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AvatarImage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 枚举类
|
||||
SettingKeyEnum,
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 首次加载
|
||||
isFirstload: true,
|
||||
// 是否已登录
|
||||
isLogin: false,
|
||||
// 系统设置
|
||||
setting: {},
|
||||
// 当前用户信息
|
||||
userInfo: {},
|
||||
// 账户资产
|
||||
assets: { balance: '--', points: '--', coupon: '--' },
|
||||
// 我的服务
|
||||
service,
|
||||
// 订单操作
|
||||
orderNavbar,
|
||||
// 当前用户待处理的订单数量
|
||||
todoCounts: { payment: 0, deliver: 0, received: 0 },
|
||||
tel: '18281516816'
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow(options) {
|
||||
this.onRefreshPage()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 刷新页面
|
||||
onRefreshPage() {
|
||||
// 更新购物车角标
|
||||
setCartTabBadge()
|
||||
// 判断是否已登录
|
||||
this.isLogin = checkLogin()
|
||||
// 获取页面数据
|
||||
this.getPageData()
|
||||
},
|
||||
|
||||
// 获取页面数据
|
||||
getPageData(callback) {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([app.getSetting(), app.getUserInfo(), app.getUserAssets(), app.getTodoCounts()])
|
||||
.then(result => {
|
||||
app.isFirstload = false
|
||||
// 初始化我的服务数据
|
||||
app.initService()
|
||||
// 初始化订单操作数据
|
||||
app.initOrderTabbar()
|
||||
// 执行回调函数
|
||||
callback && callback()
|
||||
})
|
||||
.catch(err => console.log('catch', err))
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 初始化我的服务数据
|
||||
initService() {
|
||||
const app = this
|
||||
const newService = []
|
||||
service.forEach(item => {
|
||||
if (item.id === 'points') {
|
||||
item.name = '我的' + app.setting[SettingKeyEnum.POINTS.value].points_name
|
||||
}
|
||||
// 数据角标
|
||||
if (item.count != undefined) {
|
||||
item.count = app.todoCounts[item.id]
|
||||
}
|
||||
newService.push(item)
|
||||
})
|
||||
app.service = newService
|
||||
},
|
||||
|
||||
// 初始化订单操作数据
|
||||
initOrderTabbar() {
|
||||
const app = this
|
||||
const newOrderNavbar = []
|
||||
orderNavbar.forEach(item => {
|
||||
if (item.count != undefined) {
|
||||
item.count = app.todoCounts[item.id]
|
||||
}
|
||||
newOrderNavbar.push(item)
|
||||
})
|
||||
app.orderNavbar = newOrderNavbar
|
||||
},
|
||||
|
||||
// 获取商城设置
|
||||
getSetting() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
SettingModel.data()
|
||||
.then(setting => {
|
||||
app.setting = setting
|
||||
resolve(setting)
|
||||
}).catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取当前用户信息
|
||||
getUserInfo() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
!app.isLogin ? resolve(null) : UserApi.info({}, { load: app.isFirstload })
|
||||
.then(result => {
|
||||
app.userInfo = result.data.userInfo
|
||||
resolve(app.userInfo)
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.result && err.result.status == 401) {
|
||||
app.isLogin = false
|
||||
resolve(null)
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取账户资产
|
||||
getUserAssets() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
!app.isLogin ? resolve(null) : UserApi.assets({}, { load: app.isFirstload })
|
||||
.then(result => {
|
||||
app.assets = result.data.assets
|
||||
resolve(app.assets)
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.result && err.result.status == 401) {
|
||||
app.isLogin = false
|
||||
resolve(null)
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取当前用户待处理的订单数量
|
||||
getTodoCounts() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
!app.isLogin ? resolve(null) : OrderApi.todoCounts({}, { load: app.isFirstload })
|
||||
.then(result => {
|
||||
app.todoCounts = result.data.counts
|
||||
resolve(app.todoCounts)
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.result && err.result.status == 401) {
|
||||
app.isLogin = false
|
||||
resolve(null)
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转到登录页
|
||||
handleLogin() {
|
||||
!this.isLogin && this.$navTo('pages/login/index')
|
||||
},
|
||||
|
||||
// 跳转到绑定手机号页面
|
||||
handleBindMobile() {
|
||||
this.$navTo('pages/user/bind/index')
|
||||
},
|
||||
|
||||
// 跳转到修改个人信息页
|
||||
handlePersonal() {
|
||||
this.$navTo('pages/user/personal/index')
|
||||
},
|
||||
|
||||
// 退出登录
|
||||
handleLogout() {
|
||||
const app = this
|
||||
uni.showModal({
|
||||
title: '友情提示',
|
||||
content: '您确定要退出登录吗?',
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
store.dispatch('Logout', {})
|
||||
.then(result => app.onRefreshPage())
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转到钱包页面
|
||||
onTargetWallet() {
|
||||
this.$navTo('pages/wallet/index')
|
||||
},
|
||||
|
||||
// 跳转到订单页
|
||||
onTargetOrder(item) {
|
||||
this.$navTo('pages/order/index', { dataType: item.id })
|
||||
},
|
||||
|
||||
// 跳转到我的积分页面
|
||||
onTargetPoints() {
|
||||
this.$navTo('pages/points/log')
|
||||
},
|
||||
|
||||
// 跳转到我的优惠券页
|
||||
onTargetMyCoupon() {
|
||||
this.$navTo('pages/my-coupon/index')
|
||||
},
|
||||
|
||||
// 跳转到服务页面
|
||||
handleService({ url }) {
|
||||
this.$navTo(url)
|
||||
},
|
||||
|
||||
// 拨打电话
|
||||
callPhone(){
|
||||
console.log(this.setting.trade.phone);
|
||||
let tel = this.tel
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: this.setting.trade.phone
|
||||
})
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 下拉刷新
|
||||
*/
|
||||
onPullDownRefresh() {
|
||||
// 获取首页数据
|
||||
this.getPageData(() => {
|
||||
uni.stopPullDownRefresh()
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 页面头部
|
||||
.main-header {
|
||||
background-color: #fff;
|
||||
// background-image: url('/static/background/user-header.png');
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
background-size: 100% 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// padding-top: 40rpx;
|
||||
padding-left: 30rpx;
|
||||
|
||||
.bg-image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
height: 100rpx;
|
||||
z-index: 1;
|
||||
|
||||
.user-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-left: 30rpx;
|
||||
color: #c59a46;
|
||||
|
||||
.nick-name {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
max-width: 270rpx;
|
||||
}
|
||||
|
||||
.mobile {
|
||||
margin-top: 15rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.user-grade {
|
||||
align-self: baseline;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #3c3c3c;
|
||||
margin-top: 12rpx;
|
||||
border-radius: 10rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
|
||||
.user-grade_icon .image {
|
||||
display: block;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
.user-grade_name {
|
||||
margin-left: 5rpx;
|
||||
font-size: 26rpx;
|
||||
color: #EEE0C3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.login-tips {
|
||||
margin-top: 12rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 角标组件
|
||||
.item-badge {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 55rpx;
|
||||
background: #fa2209;
|
||||
color: #fff;
|
||||
border-radius: 100%;
|
||||
min-width: 38rpx;
|
||||
height: 38rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 1rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
// 我的钱包
|
||||
.my-asset {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
padding: 40rpx 0;
|
||||
|
||||
.asset-right {
|
||||
width: 200rpx;
|
||||
border-left: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.asset-right-item {
|
||||
text-align: center;
|
||||
color: #545454;
|
||||
|
||||
.item-icon {
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
margin-top: 14rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.asset-left-item {
|
||||
max-width: 183rpx;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
padding: 0 16rpx;
|
||||
|
||||
.item-value {
|
||||
font-size: 34rpx;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
margin-top: 14rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 订单操作
|
||||
.order-navbar {
|
||||
display: flex;
|
||||
margin: 20rpx auto 20rpx auto;
|
||||
padding: 20rpx 0;
|
||||
width: 94%;
|
||||
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
|
||||
font-size: 30rpx;
|
||||
border-radius: 5rpx;
|
||||
background: #fff;
|
||||
|
||||
&-item {
|
||||
position: relative;
|
||||
width: 25%;
|
||||
|
||||
.item-icon {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
padding: 10rpx 0;
|
||||
color: #545454;
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 28rpx;
|
||||
color: #545454;
|
||||
text-align: center;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 我的服务
|
||||
.my-service {
|
||||
margin: 22rpx auto 22rpx auto;
|
||||
padding: 22rpx 0;
|
||||
width: 94%;
|
||||
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 5rpx;
|
||||
background: #fff;
|
||||
|
||||
.service-title {
|
||||
padding-left: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.service-content {
|
||||
|
||||
margin-bottom: -20rpx;
|
||||
|
||||
.service-item {
|
||||
position: relative;
|
||||
width: 25%;
|
||||
float: left;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.item-icon {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
padding: 14rpx 0;
|
||||
color: #ff3800;
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 28rpx;
|
||||
color: #545454;
|
||||
text-align: center;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
.my-logout {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 50rpx;
|
||||
|
||||
.logout-btn {
|
||||
width: 60%;
|
||||
margin: 0 auto;
|
||||
font-size: 28rpx;
|
||||
color: #616161;
|
||||
border-radius: 20rpx;
|
||||
border: 1px solid #dcdcdc;
|
||||
padding: 16rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
// 绑定手机号 样式1
|
||||
.my-mobile {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16rpx 40rpx;
|
||||
background: #FCEBD1;
|
||||
|
||||
.info {
|
||||
color: #cd8c0c;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.btn-bind {
|
||||
padding: 8rpx 24rpx;
|
||||
background-color: #EAB766;
|
||||
color: #fff;
|
||||
border-radius: 30rpx;
|
||||
font-size: 26rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定手机号 样式2
|
||||
.my-mobile2 {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 20rpx auto 20rpx auto;
|
||||
padding: 12rpx 40rpx;
|
||||
width: 94%;
|
||||
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
|
||||
font-size: 30rpx;
|
||||
border-radius: 5rpx;
|
||||
background: #fff;
|
||||
|
||||
.info {
|
||||
// color: #cd8c0c;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.btn-bind {
|
||||
padding: 8rpx 24rpx;
|
||||
background-color: #EAB766;
|
||||
color: #fff;
|
||||
border-radius: 30rpx;
|
||||
font-size: 26rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<!-- 标题 -->
|
||||
<view class="page-title">个人信息</view>
|
||||
<!-- 表单组件 -->
|
||||
<view class="form-wrapper">
|
||||
<u-form :model="form" ref="uForm" label-width="140rpx">
|
||||
<u-form-item label="头像">
|
||||
<button class="btn-normal" open-type="chooseAvatar" @click="onClickAvatar()" @chooseavatar="onChooseAvatar">
|
||||
<avatar-image :url="avatarUrl" :width="100" />
|
||||
</button>
|
||||
</u-form-item>
|
||||
<u-form-item label="昵称" prop="nickName">
|
||||
<u-input v-model="form.nickName" type="nickname" maxlength="30" placeholder="请输入昵称" @input="onInputNickName"
|
||||
@blur="onInputNickName" />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
<!-- 操作按钮 -->
|
||||
<view class="footer">
|
||||
<view class="btn-wrapper">
|
||||
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">保存</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import AvatarImage from '@/components/avatar-image'
|
||||
import * as UserApi from '@/api/user'
|
||||
import * as UploadApi from '@/api/upload'
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
nickName: [{
|
||||
required: true,
|
||||
message: '请输入用户昵称',
|
||||
trigger: ['blur', 'change']
|
||||
}]
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AvatarImage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 按钮禁用
|
||||
disabled: false,
|
||||
// 头像路径 (用于显示)
|
||||
avatarUrl: '',
|
||||
// 临时图片 (用于上传)
|
||||
tempFile: null,
|
||||
// 表单数据
|
||||
form: {
|
||||
avatarId: '',
|
||||
nickName: ''
|
||||
},
|
||||
// 验证规则
|
||||
rules,
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad() {
|
||||
// 获取当前用户信息
|
||||
this.getUserInfo()
|
||||
},
|
||||
|
||||
// 必须要在onReady生命周期,因为onLoad生命周期组件可能尚未创建完毕
|
||||
onReady() {
|
||||
this.$refs.uForm.setRules(this.rules)
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取当前用户信息
|
||||
getUserInfo() {
|
||||
const app = this
|
||||
UserApi.info()
|
||||
.then(result => {
|
||||
const userInfo = result.data.userInfo
|
||||
app.avatarUrl = userInfo.avatar_url
|
||||
app.form.avatarId = userInfo.avatar_id
|
||||
app.form.nickName = userInfo.nick_name
|
||||
})
|
||||
},
|
||||
|
||||
// 点击头像按钮事件
|
||||
onClickAvatar() {
|
||||
// #ifdef MP-WEIXIN
|
||||
return
|
||||
// #endif
|
||||
this.chooseImage()
|
||||
},
|
||||
|
||||
// 选择头像事件 - 仅限微信小程序
|
||||
// #ifdef MP-WEIXIN
|
||||
onChooseAvatar({ detail }) {
|
||||
const app = this
|
||||
app.avatarUrl = detail.avatarUrl
|
||||
app.tempFile = { path: app.avatarUrl }
|
||||
},
|
||||
// #endif
|
||||
|
||||
// 选择图片
|
||||
chooseImage() {
|
||||
const app = this
|
||||
// 选择图片
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
|
||||
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
|
||||
success({ tempFiles }) {
|
||||
app.tempFile = tempFiles[0]
|
||||
app.avatarUrl = app.tempFile.path
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 上传图片
|
||||
uploadFile() {
|
||||
const app = this
|
||||
return UploadApi.image([app.tempFile])
|
||||
.then(fileIds => {
|
||||
app.form.avatarId = fileIds[0]
|
||||
app.tempFile = null
|
||||
return true
|
||||
})
|
||||
.catch(() => {
|
||||
app.disabled = false
|
||||
return false
|
||||
})
|
||||
},
|
||||
|
||||
// 确认修改
|
||||
async handleSubmit() {
|
||||
const app = this
|
||||
// 判断是否重复提交
|
||||
if (app.disabled === true) return
|
||||
app.$refs.uForm.validate(async valid => {
|
||||
if (!valid) return
|
||||
// 按钮禁用
|
||||
app.disabled = true
|
||||
// 先上传头像图片
|
||||
if (app.tempFile && !await app.uploadFile()) {
|
||||
return
|
||||
}
|
||||
// 提交保存个人信息
|
||||
UserApi.personal({ form: app.form })
|
||||
.then(result => {
|
||||
app.$toast(result.message)
|
||||
setTimeout(() => {
|
||||
app.disabled = false
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
})
|
||||
.catch(err => app.disabled = false)
|
||||
})
|
||||
},
|
||||
|
||||
// 绑定昵称输入框 (用于微信小程序端快速填写昵称能力)
|
||||
onInputNickName(val) {
|
||||
if (val) {
|
||||
this.form.nickName = val
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #f7f8fa;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.container {}
|
||||
|
||||
.page-title {
|
||||
width: 94%;
|
||||
margin: 0 auto;
|
||||
padding-top: 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: rgba(69, 90, 100, 0.6);
|
||||
}
|
||||
|
||||
.form-wrapper {
|
||||
margin: 20rpx auto 20rpx auto;
|
||||
padding: 0 40rpx;
|
||||
width: 94%;
|
||||
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 16rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
|
||||
.footer {
|
||||
margin-top: 80rpx;
|
||||
|
||||
.btn-wrapper {
|
||||
height: 100%;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
height: 86rpx;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-item-wechat {
|
||||
background: #0ba90b;
|
||||
margin-bottom: 26rpx;
|
||||
}
|
||||
|
||||
.btn-item-main {
|
||||
background: linear-gradient(to right, #f9211c, #ff6335);
|
||||
color: #fff;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,127 @@
|
||||
<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>
|
||||
@@ -0,0 +1,286 @@
|
||||
<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-input">
|
||||
<input type="text" placeholder="请输十二位实体卡号" v-model="number" @input="onChangeNumber" />
|
||||
</view>
|
||||
<view class="recharge-label"></view> -->
|
||||
<view class="recharge-label">
|
||||
<text>卡密</text>
|
||||
</view>
|
||||
<view class="recharge-input">
|
||||
<input type="text" placeholder="请输入十六位卡密" v-model="key" @input="onChangeKey" />
|
||||
<image class="scan" src="/static/order/scan.png" @click="scanCode"></image>
|
||||
</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 CardApi from '@/api/card'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
import SettingKeyEnum from '@/common/enum/setting/Key'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 会员信息
|
||||
userInfo: {},
|
||||
// 分销设置
|
||||
setting: {},
|
||||
// 按钮禁用
|
||||
disabled: false,
|
||||
// 卡号,卡密
|
||||
number: '',
|
||||
key: ''
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 获取页面数据
|
||||
this.getPageData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 卡号输入框
|
||||
onChangeNumber(e) {
|
||||
this.number = e.target.value
|
||||
},
|
||||
|
||||
// 卡密输入框
|
||||
onChangeKey(e) {
|
||||
this.key = e.target.value
|
||||
},
|
||||
|
||||
// 获取页面数据
|
||||
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.EXCHANGE.value, false)
|
||||
.then(data => {
|
||||
app.setting = data
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
scanCode() {
|
||||
uni.scanCode({
|
||||
success: (res) => {
|
||||
// 处理扫码结果
|
||||
this.key = res.result
|
||||
},
|
||||
fail: (err) => {
|
||||
this.$error('扫码失败')
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 立即兑换
|
||||
onSubmit(e) {
|
||||
const app = this
|
||||
// 按钮禁用
|
||||
app.disabled = true
|
||||
// 提交到后端
|
||||
CardApi.submit({ card_number: app.number, secret_key: app.key })
|
||||
.then(result => {
|
||||
app.$success('兑换处理中,请稍后查看')
|
||||
this.number = ''
|
||||
this.key = ''
|
||||
setTimeout(() => {
|
||||
// 获取页面数据
|
||||
app.getPageData()
|
||||
}, 1500)}
|
||||
)
|
||||
.finally(() => app.disabled = false)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</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;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
image{
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
input {
|
||||
border: 1rpx solid rgb(228, 228, 228);
|
||||
border-radius: 10rpx;
|
||||
padding: 15rpx 16rpx;
|
||||
font-size: 26rpx;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 立即充值 */
|
||||
.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>
|
||||
@@ -0,0 +1,130 @@
|
||||
<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', item.state === 10 ? 'red' : '']">
|
||||
<view class="rec-status">
|
||||
<text>{{ item.card_number }} {{ item.describe }}</text>
|
||||
</view>
|
||||
<view class="rec-time">
|
||||
<text>{{ item.create_time }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view :class="['item-right', item.state === 10 ? 'red' : '']">
|
||||
<text>{{ 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/card/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;
|
||||
}
|
||||
}
|
||||
.red {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,218 @@
|
||||
<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 class="space-lower_item btn-recharge btn-group">
|
||||
<view v-if="setting.is_entrance" class="btn-submit" @click="onTargetRecharge()">充 值</view>
|
||||
<view v-if="share.is_entrance" class="btn-submit" @click="onTargetShare()">分 享</view>
|
||||
<view v-if="exchange.is_entrance" class="btn-submit" @click="onTargetExchange()">实体卡兑换</view>
|
||||
</view>
|
||||
<view class="space-lower_item item-lable dis-flex flex-x-around">
|
||||
<view class="lable-text" @click="onTargetBalanceLog()">
|
||||
<text>账单详情</text>
|
||||
</view>
|
||||
<view class="lable-text" @click="onTargetExchangeLog()">
|
||||
<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: {},
|
||||
// 实体卡兑换设置
|
||||
exchange: {},
|
||||
// 分享设置
|
||||
share: {},
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onShow(options) {
|
||||
// 获取页面数据
|
||||
this.getPageData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取页面数据
|
||||
getPageData() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([app.getUserInfo(), app.getSetting(), app.getExchange(), app.getShare()])
|
||||
.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)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取兑换设置
|
||||
getExchange() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
SettingModel.item(SettingKeyEnum.EXCHANGE.value, false)
|
||||
.then(data => {
|
||||
app.exchange = data
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取分享设置
|
||||
getShare() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
SettingModel.item(SettingKeyEnum.SHARE.value, false)
|
||||
.then(data => {
|
||||
app.share = data
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转分享页面
|
||||
onTargetShare() {
|
||||
this.$navTo('pages/wallet/share/index')
|
||||
},
|
||||
|
||||
// 跳转兑换页面
|
||||
onTargetExchange() {
|
||||
this.$navTo('pages/wallet/exchange/index')
|
||||
},
|
||||
|
||||
|
||||
// 跳转充值页面
|
||||
onTargetRecharge() {
|
||||
this.$navTo('pages/wallet/recharge/index')
|
||||
},
|
||||
|
||||
// 跳转充值记录页面
|
||||
onTargetRechargeOrder() {
|
||||
this.$navTo('pages/wallet/recharge/order')
|
||||
},
|
||||
|
||||
// 跳转账单详情页面
|
||||
onTargetBalanceLog() {
|
||||
this.$navTo('pages/wallet/balance/log')
|
||||
},
|
||||
|
||||
// 跳转兑换详情页面
|
||||
onTargetExchangeLog() {
|
||||
this.$navTo('pages/wallet/exchange/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 80rpx;
|
||||
}
|
||||
|
||||
.btn-group{
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.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>
|
||||
@@ -0,0 +1,300 @@
|
||||
<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>
|
||||
@@ -0,0 +1,127 @@
|
||||
<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>
|
||||
@@ -0,0 +1,269 @@
|
||||
<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-input">
|
||||
<input type="digit" placeholder="请输入分享金额" v-model="inputValue" @input="onChangeMoney" />
|
||||
</view>
|
||||
<view class="recharge-label"></view>
|
||||
<view class="recharge-label">
|
||||
<text>分享手机号</text>
|
||||
</view>
|
||||
<view class="recharge-input">
|
||||
<input type="text" placeholder="请输入手机号码" v-model="tel" @input="onChangeTel" />
|
||||
</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 SettingModel from '@/common/model/Setting'
|
||||
import SettingKeyEnum from '@/common/enum/setting/Key'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 会员信息
|
||||
userInfo: {},
|
||||
// 分享设置
|
||||
setting: {},
|
||||
// 按钮禁用
|
||||
disabled: false,
|
||||
// 自定义金额
|
||||
inputValue: '',
|
||||
tel: ''
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 获取页面数据
|
||||
this.getPageData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 金额输入框
|
||||
onChangeMoney(e) {
|
||||
this.inputValue = e.target.value
|
||||
this.selectedPlanId = 0
|
||||
},
|
||||
|
||||
// 手机号输入框
|
||||
onChangeTel(e) {
|
||||
this.tel = e.target.value
|
||||
},
|
||||
|
||||
// 获取页面数据
|
||||
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.SHARE.value, false)
|
||||
.then(data => {
|
||||
app.setting = data
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 立即充值
|
||||
onSubmit(e) {
|
||||
const app = this
|
||||
// 按钮禁用
|
||||
app.disabled = true
|
||||
// 提交到后端
|
||||
RechargeApi.share({ customMoney: app.inputValue, tel: app.tel })
|
||||
.then(result => {
|
||||
app.$success('分享成功')
|
||||
this.inputValue = ''
|
||||
this.tel = ''
|
||||
setTimeout(() => {
|
||||
// 获取页面数据
|
||||
app.getPageData()
|
||||
}, 1500)}
|
||||
)
|
||||
.finally(() => app.disabled = false)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</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>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- web-view 组件用于加载网页 -->
|
||||
<web-view :src="webUrl" @message="onMessage"></web-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 外部网页的URL
|
||||
webUrl: ''
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
console.log(options)
|
||||
this.webUrl = options.url
|
||||
},
|
||||
methods: {
|
||||
// onLoad(event) {
|
||||
// console.log('Webview loaded:', event);
|
||||
// // 检查 event.detail 是否存在并获取新 URL
|
||||
// if (event && event.detail && event.detail.src) {
|
||||
// this.webUrl = event.detail.src;
|
||||
// console.log('New URL:', this.webUrl);
|
||||
// } else {
|
||||
// console.warn('Load event does not contain expected src property');
|
||||
// }
|
||||
// },
|
||||
onError(event) {
|
||||
console.error('Error loading webview:', event);
|
||||
// 你可以根据 event.detail 进一步处理具体错误
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 样式可以根据需要自定义 */
|
||||
</style>
|
||||
Reference in New Issue
Block a user