第一次提交
This commit is contained in:
Executable
+212
@@ -0,0 +1,212 @@
|
||||
<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="姓名" 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">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="btn-item btn-item-wechat" @click="chooseAddress()">选择微信收货地址</view>
|
||||
<!-- #endif -->
|
||||
<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: {
|
||||
|
||||
// 选择微信地址
|
||||
// #ifdef MP-WEIXIN
|
||||
chooseAddress() {
|
||||
const app = this
|
||||
const { form, $refs } = this
|
||||
uni.chooseAddress({
|
||||
success(res) {
|
||||
const names = $refs.sRegion.getOptionItemByNames(res)
|
||||
form.name = res.userName
|
||||
form.phone = res.telNumber
|
||||
form.detail = res.detailInfo
|
||||
form.region = names.length > 0 ? names : []
|
||||
},
|
||||
fail({ errMsg }) {
|
||||
app.$toast(errMsg)
|
||||
console.error('获取微信地址失败:', errMsg)
|
||||
}
|
||||
})
|
||||
},
|
||||
// #endif
|
||||
|
||||
// 表单提交
|
||||
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, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Executable
+307
@@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<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="appTheme.mainBg" :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: 0 auto 20rpx auto;
|
||||
padding: 30rpx 40rpx;
|
||||
width: 94%;
|
||||
box-shadow: 0 1rpx 5rpx 0 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;
|
||||
z-index: 11;
|
||||
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.btn-wrapper {
|
||||
height: 120rpx;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
height: 86rpx;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-shadow: 0 1rpx 5rpx 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.btn-item-main {
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Executable
+249
@@ -0,0 +1,249 @@
|
||||
<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="姓名" 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">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="btn-item btn-item-wechat" @click="chooseAddress()">选择微信收货地址</view>
|
||||
<!-- #endif -->
|
||||
<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)
|
||||
})
|
||||
},
|
||||
|
||||
// 选择微信地址
|
||||
// #ifdef MP-WEIXIN
|
||||
chooseAddress() {
|
||||
const { form, $refs } = this
|
||||
uni.chooseAddress({
|
||||
success(res) {
|
||||
const names = $refs.sRegion.getOptionItemByNames(res)
|
||||
form.name = res.userName
|
||||
form.phone = res.telNumber
|
||||
form.detail = res.detailInfo
|
||||
form.region = names.length > 0 ? names : []
|
||||
},
|
||||
fail({ errMsg }) {
|
||||
app.$toast(errMsg)
|
||||
console.error('获取微信地址失败:', errMsg)
|
||||
}
|
||||
})
|
||||
},
|
||||
// #endif
|
||||
|
||||
// 生成默认的表单数据
|
||||
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, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Executable
+107
@@ -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>
|
||||
Executable
+251
@@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<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="appTheme.mainBg" :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>
|
||||
</view>
|
||||
</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>
|
||||
Executable
+167
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<goods-sku-popup :value="value" @input="onChangeValue" border-radius="20" :localdata="goodsInfo" :mode="skuMode" :maskCloseAble="true"
|
||||
:priceColor="appTheme.mainBg" :buyNowBackgroundColor="appTheme.mainBg" :addCartColor="appTheme.viceText" :addCartBackgroundColor="appTheme.viceBg"
|
||||
:activedStyle="{ color: appTheme.mainBg, borderColor: appTheme.mainBg, backgroundColor: activedBtnBackgroundColor }"
|
||||
@open="openSkuPopup" @close="closeSkuPopup" @buy-now="buyNow" buyNowText="立即砍价" :maxBuyNum="1" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { hex2rgba } from '@/utils/color'
|
||||
import * as TaskApi from '@/api/bargain/task'
|
||||
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
|
||||
},
|
||||
// 砍价活动详情
|
||||
active: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
// 商品详情信息
|
||||
goods: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
goodsInfo: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 规格按钮选中时的背景色
|
||||
activedBtnBackgroundColor() {
|
||||
return hex2rgba(this.appTheme.mainBg, 0.1)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const app = this
|
||||
const { goods } = app
|
||||
app.goodsInfo = {
|
||||
_id: goods.goods_id,
|
||||
name: goods.goods_name,
|
||||
goods_thumb: goods.goods_image,
|
||||
sku_list: app.getSkuList(),
|
||||
spec_list: app.getSpecList()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 监听组件显示隐藏
|
||||
onChangeValue(val) {
|
||||
this.$emit('input', val)
|
||||
},
|
||||
|
||||
// 整理商品SKU列表
|
||||
getSkuList() {
|
||||
const app = this
|
||||
const { goods: { goods_name, goods_image, skuList } } = app
|
||||
const skuData = []
|
||||
skuList.forEach(item => {
|
||||
skuData.push({
|
||||
_id: item.id,
|
||||
goods_sku_id: item.goods_sku_id,
|
||||
goods_id: item.goods_id,
|
||||
goods_name: goods_name,
|
||||
image: item.image_url ? item.image_url : goods_image,
|
||||
price: item.goods_price * 100,
|
||||
stock: item.stock_num,
|
||||
spec_value_ids: item.spec_value_ids,
|
||||
sku_name_arr: app.getSkuNameArr(item.spec_value_ids)
|
||||
})
|
||||
})
|
||||
return skuData
|
||||
},
|
||||
|
||||
// 获取sku记录的规格值列表
|
||||
getSkuNameArr(specValueIds) {
|
||||
const app = this
|
||||
const defaultData = ['默认']
|
||||
const skuNameArr = []
|
||||
if (specValueIds) {
|
||||
specValueIds.forEach((valueId, groupIndex) => {
|
||||
const specValueName = app.getSpecValueName(valueId, groupIndex)
|
||||
skuNameArr.push(specValueName)
|
||||
})
|
||||
}
|
||||
return skuNameArr.length ? skuNameArr : defaultData
|
||||
},
|
||||
|
||||
// 获取指定的规格值名称
|
||||
getSpecValueName(valueId, groupIndex) {
|
||||
const app = this
|
||||
const { goods: { specList } } = app
|
||||
const res = specList[groupIndex].valueList.find(specValue => {
|
||||
return specValue.spec_value_id == valueId
|
||||
})
|
||||
return res.spec_value
|
||||
},
|
||||
|
||||
// 整理规格数据
|
||||
getSpecList() {
|
||||
const { goods: { specList } } = this
|
||||
const defaultData = [{ name: '默认', list: [{ name: '默认' }] }]
|
||||
const specData = []
|
||||
specList.forEach(group => {
|
||||
const children = []
|
||||
group.valueList.forEach(specValue => {
|
||||
children.push({ name: specValue.spec_value })
|
||||
})
|
||||
specData.push({
|
||||
name: group.spec_name,
|
||||
list: children
|
||||
})
|
||||
})
|
||||
return specData.length ? specData : defaultData
|
||||
},
|
||||
|
||||
// sku组件 开始-----------------------------------------------------------
|
||||
openSkuPopup() {
|
||||
// console.log("监听 - 打开sku组件")
|
||||
},
|
||||
|
||||
closeSkuPopup() {
|
||||
// console.log("监听 - 关闭sku组件")
|
||||
},
|
||||
|
||||
// 立即购买
|
||||
buyNow(selectShop) {
|
||||
const app = this
|
||||
TaskApi.partake({
|
||||
activeId: app.active.active_id,
|
||||
goodsSkuId: selectShop.goods_sku_id
|
||||
})
|
||||
.then(result => {
|
||||
// 跳转到砍价任务详情页
|
||||
const taskId = result.data.taskId
|
||||
this.$navTo('pages/bargain/task', { taskId })
|
||||
})
|
||||
// 隐藏当前弹窗
|
||||
this.onChangeValue(false)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
Executable
+413
@@ -0,0 +1,413 @@
|
||||
<template>
|
||||
<view v-show="!isLoading" class="container" :style="appThemeStyle">
|
||||
<!-- 商品图片轮播 -->
|
||||
<SlideImage v-if="!isLoading" :video="goods.video" :videoCover="goods.videoCover" :images="goods.goods_images" />
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view v-if="!isLoading" class="goods-info m-top20">
|
||||
<!-- 价格、销量 -->
|
||||
<view class="info-item info-item__top dis-flex flex-x-between flex-y-end">
|
||||
<view class="block-left dis-flex flex-y-center">
|
||||
<view class="active-tag">
|
||||
<text>限时砍价</text>
|
||||
</view>
|
||||
<!-- 砍价底价 -->
|
||||
<text class="floor-price__samll">¥</text>
|
||||
<text class="floor-price">{{ active.floor_price }}</text>
|
||||
<!-- 商品原价 -->
|
||||
<text class="original-price">¥{{ goods.goods_price_min }}</text>
|
||||
</view>
|
||||
<view class="block-right dis-flex">
|
||||
<!-- 销量 -->
|
||||
<view class="goods-sales">
|
||||
<text>已砍成{{ active.active_sales }}件</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 标题、分享 -->
|
||||
<view class="info-item info-item__name dis-flex flex-y-center">
|
||||
<view class="goods-name flex-box">
|
||||
<text class="twoline-hide">{{ goods.goods_name }}</text>
|
||||
</view>
|
||||
<view class="goods-share__line"></view>
|
||||
<view class="goods-share">
|
||||
<button class="share-btn dis-flex flex-dir-column" @click="onShowShareSheet()">
|
||||
<text class="share__icon iconfont icon-fenxiang"></text>
|
||||
<text class="f-24">分享</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 商品卖点 -->
|
||||
<view v-if="goods.selling_point" class="info-item info-item_selling-point">
|
||||
<text>{{ goods.selling_point }}</text>
|
||||
</view>
|
||||
<!-- 活动倒计时 -->
|
||||
<view v-if="active.is_end == false" class="info-item info-item_status info-item_countdown dis-flex flex-y-center">
|
||||
<text class="countdown-icon iconfont icon-naozhong"></text>
|
||||
<text>距离活动结束</text>
|
||||
<text class="m-r-10">还剩</text>
|
||||
<count-down :date="active.end_time" separator="zh" theme="text" />
|
||||
</view>
|
||||
<!-- 活动已结束 -->
|
||||
<view v-if="active.is_end == true" class="info-item info-item_status info-item_end">
|
||||
<text class="countdown-icon iconfont icon-naozhong"></text>
|
||||
<text>砍价活动已结束,下次记得早点来哦~</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 砍价玩法 -->
|
||||
<view class="bargain-rules m-top20 b-f" @click="handleShowRules()">
|
||||
<view class="item-title dis-flex">
|
||||
<view class="block-left flex-box">
|
||||
<text>砍价玩法</text>
|
||||
</view>
|
||||
<view class="block-right">
|
||||
<text class="show-more col-9">查看规则</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 砍价步骤 -->
|
||||
<view class="rule-simple dis-flex flex-x-around">
|
||||
<view class="simple-item dis-flex flex-dir-column flex-y-center">
|
||||
<view class="i-number dis-flex flex-x-center flex-y-center">
|
||||
<text class="f-30">1</text>
|
||||
</view>
|
||||
<view class="i-text f-28">点击砍价</view>
|
||||
</view>
|
||||
<view class="simple-item dis-flex flex-dir-column flex-y-center">
|
||||
<view class="i-number dis-flex flex-x-center flex-y-center">
|
||||
<text class="f-30">2</text>
|
||||
</view>
|
||||
<view class="i-text f-28">找人帮砍</view>
|
||||
</view>
|
||||
<view class="simple-item dis-flex flex-dir-column flex-y-center">
|
||||
<view class="i-number dis-flex flex-x-center flex-y-center">
|
||||
<text class="f-30">3</text>
|
||||
</view>
|
||||
<view class="i-text f-28">砍到最低</view>
|
||||
</view>
|
||||
<view class="simple-item dis-flex flex-dir-column flex-y-center">
|
||||
<view class="i-number dis-flex flex-x-center flex-y-center">
|
||||
<text class="f-30">4</text>
|
||||
</view>
|
||||
<view class="i-text f-28">优惠购买</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选择商品规格 -->
|
||||
<view v-if="goods.spec_type == 20" class="goods-choice m-top20 b-f" @click="onShowSkuPopup()">
|
||||
<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" :active="active" :goods="goods" />
|
||||
|
||||
<!-- 商品评价 -->
|
||||
<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>
|
||||
<!-- 客服 -->
|
||||
<customer-btn v-if="isShowCustomerBtn">
|
||||
<view class="fast-item">
|
||||
<view class="fast-icon">
|
||||
<text class="iconfont icon-kefu1"></text>
|
||||
</view>
|
||||
<view class="fast-text">
|
||||
<text>客服</text>
|
||||
</view>
|
||||
</view>
|
||||
</customer-btn>
|
||||
<!-- 购物车 (客服按钮不显示时) -->
|
||||
<view v-if="!isShowCustomerBtn" 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 v-if="active.is_start && !active.is_end" class="btn-item btn--main" @click="handleMainBtn(3)">
|
||||
<text>{{ isPartake? '继续砍价' : '立即砍价' }}</text>
|
||||
</view>
|
||||
<button v-else class="btn-item btn--gray">
|
||||
<text>{{ active.is_end ? '活动已结束' : '活动未开启' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分享菜单 -->
|
||||
<share-sheet v-model="showShareSheet" :shareTitle="goods.goods_name" :shareImageUrl="goods.goods_image" :posterApiCall="posterApiCall" :posterApiParam="{ activeId }" />
|
||||
|
||||
<!-- 砍价规则弹窗 -->
|
||||
<u-modal v-if="!isLoading" v-model="showRules" title="砍价规则">
|
||||
<scroll-view style="height: 610rpx;" :scroll-y="true">
|
||||
<view class="pops-content">
|
||||
<text>{{ setting.rulesDesc }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-modal>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSceneData } from '@/core/app'
|
||||
import ShareSheet from '@/components/share-sheet'
|
||||
import CustomerBtn from '@/components/customer-btn'
|
||||
import SkuPopup from './components/SkuPopup'
|
||||
import SlideImage from '../../goods/components/SlideImage'
|
||||
import Comment from '../../goods/components/Comment'
|
||||
// import Service from '../../goods/components/Service'
|
||||
import CountDown from '@/components/countdown'
|
||||
import * as GoodsApi from '@/api/goods'
|
||||
import * as CartApi from '@/api/cart'
|
||||
import * as ActiveApi from '@/api/bargain/active'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ShareSheet,
|
||||
CustomerBtn,
|
||||
// Shortcut,
|
||||
SlideImage,
|
||||
SkuPopup,
|
||||
Comment,
|
||||
// Service,
|
||||
CountDown
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 显示/隐藏SKU弹窗
|
||||
showSkuPopup: false,
|
||||
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
|
||||
skuMode: 3,
|
||||
// 显示/隐藏分享菜单
|
||||
showShareSheet: false,
|
||||
// 显示砍价规则
|
||||
showRules: false,
|
||||
// 获取商品海报图api方法
|
||||
posterApiCall: ActiveApi.poster,
|
||||
// 当前活动ID
|
||||
activeId: null,
|
||||
// 当前商品ID
|
||||
goodsId: null,
|
||||
// 活动详情
|
||||
active: {},
|
||||
// 商品详情
|
||||
goods: {},
|
||||
// 砍价设置
|
||||
setting: null,
|
||||
// 标记当前用户是否正在参与
|
||||
isPartake: null,
|
||||
// 砍价任务ID (当前用户参与的话才有值)
|
||||
taskId: null,
|
||||
// 购物车总数量
|
||||
cartTotal: 0,
|
||||
// 是否显示在线客服按钮
|
||||
isShowCustomerBtn: false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
async onLoad(options) {
|
||||
// 记录query参数
|
||||
this.onRecordQuery(options)
|
||||
// 加载页面数据
|
||||
this.onRefreshPage()
|
||||
// 是否显示在线客服按钮
|
||||
this.isShowCustomerBtn = await SettingModel.isShowCustomerBtn()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 记录query参数
|
||||
onRecordQuery(query) {
|
||||
const scene = getSceneData(query)
|
||||
this.activeId = query.activeId ? parseInt(query.activeId) : parseInt(scene.aid)
|
||||
this.goodsId = query.goodsId ? parseInt(query.goodsId) : parseInt(scene.gid)
|
||||
},
|
||||
|
||||
// 刷新页面数据
|
||||
onRefreshPage() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([app.getActiveDetail(), app.getGoodsDetail(), app.getCartTotal()])
|
||||
.then(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取砍价活动详情
|
||||
getActiveDetail() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
ActiveApi.detail(app.activeId)
|
||||
.then(result => {
|
||||
app.active = result.data.active
|
||||
app.setting = result.data.setting
|
||||
app.isPartake = result.data.isPartake
|
||||
app.taskId = result.data.taskId
|
||||
resolve(result)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取商品信息
|
||||
getGoodsDetail() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
GoodsApi.detail(app.goodsId, false)
|
||||
.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)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示/隐藏SKU弹窗
|
||||
*/
|
||||
onShowSkuPopup() {
|
||||
this.showSkuPopup = !this.showSkuPopup
|
||||
},
|
||||
|
||||
// 显示隐藏分享菜单
|
||||
onShowShareSheet() {
|
||||
this.showShareSheet = !this.showShareSheet
|
||||
},
|
||||
|
||||
// 显示砍价规则
|
||||
handleShowRules() {
|
||||
this.showRules = true
|
||||
},
|
||||
|
||||
// 跳转到首页
|
||||
onTargetHome(e) {
|
||||
this.$navTo('pages/index/index')
|
||||
},
|
||||
|
||||
// 跳转到购物车页
|
||||
onTargetCart() {
|
||||
this.$navTo('pages/cart/index')
|
||||
},
|
||||
|
||||
// 点击主按钮
|
||||
handleMainBtn() {
|
||||
const app = this
|
||||
// 发起新的砍价任务
|
||||
if (!app.isPartake) {
|
||||
return app.onShowSkuPopup()
|
||||
}
|
||||
// 已发起砍价则跳转到砍价任务详情页
|
||||
app.$navTo('pages/bargain/task', { taskId: app.taskId })
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
// 构建页面参数
|
||||
const app = this
|
||||
const params = app.$getShareUrlParams({
|
||||
activeId: app.activeId,
|
||||
goodsId: app.goodsId
|
||||
})
|
||||
return {
|
||||
title: app.goods.goods_name,
|
||||
path: `/pages/bargain/goods/index?${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({
|
||||
activeId: app.activeId,
|
||||
goodsId: app.goodsId
|
||||
})
|
||||
return {
|
||||
title: app.goods.goods_name,
|
||||
path: `/pages/bargain/goods/index?${params}`
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
</style>
|
||||
Executable
+298
@@ -0,0 +1,298 @@
|
||||
.container {
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
// 110 - 18 + 4
|
||||
padding-bottom: calc(constant(safe-area-inset-bottom) + 98rpx + 6rpx);
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 98rpx + 6rpx);
|
||||
}
|
||||
|
||||
/* 商品信息 */
|
||||
|
||||
.goods-info {
|
||||
background: #fff;
|
||||
padding: 25rpx 30rpx;
|
||||
}
|
||||
|
||||
.info-item__top {
|
||||
min-height: 40rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-item__top .active-tag {
|
||||
color: #fff;
|
||||
background: $main-bg;
|
||||
padding: 4rpx 10rpx;
|
||||
border-radius: 15rpx;
|
||||
font-size: 26rpx;
|
||||
text-align: center;
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
|
||||
.floor-price__samll {
|
||||
font-size: 26rpx;
|
||||
line-height: 1;
|
||||
color: $main-bg;
|
||||
margin-bottom: -10rpx;
|
||||
}
|
||||
|
||||
/* 商品价 */
|
||||
|
||||
.floor-price {
|
||||
color: $main-bg;
|
||||
margin-right: 15rpx;
|
||||
font-size: 42rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
font-size: 26rpx;
|
||||
line-height: 1;
|
||||
text-decoration: line-through;
|
||||
color: #959595;
|
||||
margin-bottom: -6rpx;
|
||||
}
|
||||
|
||||
.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: env(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 98rpx;
|
||||
}
|
||||
|
||||
// 快捷菜单
|
||||
.foo-item-fast {
|
||||
box-sizing: border-box;
|
||||
width: 256rpx;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.fast-item {
|
||||
position: relative;
|
||||
padding: 4rpx 10rpx;
|
||||
line-height: 1;
|
||||
// text-align: center;
|
||||
|
||||
.fast-icon {
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
&--home {
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
&--cart {
|
||||
.fast-icon { padding-left: 3px; }
|
||||
}
|
||||
|
||||
// 角标
|
||||
.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: 46rpx;
|
||||
}
|
||||
|
||||
.fast-text {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 操作按钮
|
||||
.foo-item-btn {
|
||||
flex: 1;
|
||||
|
||||
.btn-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// 立即砍价
|
||||
.btn-item {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
font-size: 30rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 0;
|
||||
&.btn--main {
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
}
|
||||
&.btn--gray {
|
||||
background-color: #ccc;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 活动状态
|
||||
.info-item_status {
|
||||
margin-top: 20rpx;
|
||||
padding: 15rpx 20rpx;
|
||||
font-size: 24rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
.info-item_status .countdown-icon {
|
||||
font-size: 28rpx;
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
|
||||
// 活动倒计时
|
||||
.info-item_countdown {
|
||||
background: #f0f9ff;
|
||||
color: #8f8f8f;
|
||||
}
|
||||
|
||||
.info-item_countdown .countdown-icon {
|
||||
color: #1397d8;
|
||||
}
|
||||
|
||||
// 活动已结束
|
||||
.info-item_end {
|
||||
background: #ccc;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
// 砍价玩法
|
||||
.bargain-rules {
|
||||
padding: 20rpx 0;
|
||||
font-size: 29rpx;
|
||||
|
||||
.item-title {
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.rule-simple {
|
||||
margin-top: 35rpx;
|
||||
color: #737373;
|
||||
}
|
||||
|
||||
.i-number {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 15rpx;
|
||||
border: 1rpx dashed #c0c0c0;
|
||||
}
|
||||
}
|
||||
|
||||
// 砍价规则(弹窗)
|
||||
.pops-content {
|
||||
padding: 30rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 44rpx;
|
||||
text-align: left;
|
||||
color: #606266;
|
||||
min-height: 320rpx;
|
||||
max-height: 640rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
Executable
+479
@@ -0,0 +1,479 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption" @up="upCallback">
|
||||
|
||||
<!-- 砍价会场 -->
|
||||
<view v-if="curTab == 0" class="bargain-hall">
|
||||
<!-- 商品列表 -->
|
||||
<view class="goods-item" v-for="(item, index) in activeList.data" :key="index">
|
||||
<view class="goods-item--container dis-flex" @click="onTargetActive(item)">
|
||||
<!-- 商品图片 -->
|
||||
<view class="goods-image">
|
||||
<image class="image" :src="item.goods.goods_image"></image>
|
||||
</view>
|
||||
<view class="goods-info">
|
||||
<!-- 商品名称 -->
|
||||
<view class="goods-name">
|
||||
<text class="twoline-hide">{{ item.goods.goods_name }}</text>
|
||||
</view>
|
||||
<!-- 参与的用户头像 -->
|
||||
<view v-if="item.helpsCount > 0" class="peoples dis-flex">
|
||||
<view class="user-list dis-flex">
|
||||
<view class="user-item-avatar" v-for="(help, hIdx) in item.helpList" :key="hIdx">
|
||||
<avatar-image :url="help.user.avatar_url" :width="36" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="people__text">
|
||||
<text>{{ item.helpsCount }}人正在砍价</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 商品原价 -->
|
||||
<view class="goods-price">
|
||||
<text>¥{{ item.goods.goods_price_min }}</text>
|
||||
</view>
|
||||
<!-- 砍价低价 -->
|
||||
<view class="floor-price">
|
||||
<text class="small">最低¥</text>
|
||||
<text class="big">{{ item.floor_price }}</text>
|
||||
</view>
|
||||
<!-- 操作按钮 -->
|
||||
<view class="opt-touch">
|
||||
<view class="touch-btn">
|
||||
<text>立即参加</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 我的砍价 -->
|
||||
<view v-if="curTab == 1" class="bargain-hall">
|
||||
<!-- 商品列表 -->
|
||||
<view class="goods-item" v-for="(item, index) in myList.data" :key="index">
|
||||
<view class="goods-item--container dis-flex" @click="onTargetTask(item.task_id)">
|
||||
<!-- 商品图片 -->
|
||||
<view class="goods-image">
|
||||
<image class="image" :src="item.goods.goods_image"></image>
|
||||
</view>
|
||||
<view class="goods-info">
|
||||
<!-- 商品名称 -->
|
||||
<view class="goods-name">
|
||||
<text class="twoline-hide">{{ item.goods.goods_name }}</text>
|
||||
</view>
|
||||
<!-- 砍价进度 -->
|
||||
<view class="task-rate">
|
||||
<block v-if="item.status == true">
|
||||
<text>已砍</text>
|
||||
<text class="col-m">{{ item.cut_money }}</text>
|
||||
<text>元,</text>
|
||||
<text>只差</text>
|
||||
<text class="col-m">{{ item.surplus_money }}</text>
|
||||
<text>元</text>
|
||||
</block>
|
||||
<block v-if="item.is_floor">
|
||||
<text>已砍至最低</text>
|
||||
<text class="col-m">{{ item.floor_price }}</text>
|
||||
<text>元</text>
|
||||
</block>
|
||||
</view>
|
||||
<!-- 任务状态 -->
|
||||
<view class="task-status dis-flex flex-y-center">
|
||||
<!-- 倒计时 -->
|
||||
<view v-if="item.status == true" class="count-down dis-flex flex-y-center">
|
||||
<text class="m-r-6">剩余</text>
|
||||
<count-down :date="item.end_time" separator="colon" theme="custom" />
|
||||
</view>
|
||||
<view v-if="item.status == false" class="task-status__text">
|
||||
<text class="col-m">{{ item.is_buy ? '砍价成功' : '已结束' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 操作按钮 -->
|
||||
<view v-if="item.status == true" class="opt-touch">
|
||||
<view class="touch-btn">
|
||||
<text>继续砍价</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部选项卡 -->
|
||||
<view class="footer-fixed">
|
||||
<view class="footer-container">
|
||||
<!-- 砍价会场 -->
|
||||
<view class="tabbar-item flex-box" :class="{ active: curTab == 0 }">
|
||||
<view class="tabbar-item-content dis-flex flex-x-center flex-y-center" @click="onChangeTab(0)">
|
||||
<view class="tabbar-item-icon">
|
||||
<text class="iconfont icon-shangcheng"></text>
|
||||
</view>
|
||||
<view class="tabbar-item-name">
|
||||
<text>砍价会场</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 分割线 -->
|
||||
<view class="tabbar-item__divider">
|
||||
<view class="divider-line"></view>
|
||||
</view>
|
||||
<!-- 我的砍价 -->
|
||||
<view class="tabbar-item flex-box" :class="{ active: curTab == 1 }">
|
||||
<view class="tabbar-item-content dis-flex flex-x-center flex-y-center" @click="onChangeTab(1)">
|
||||
<view class="tabbar-item-icon">
|
||||
<text class="iconfont icon-sy-yh"></text>
|
||||
</view>
|
||||
<view class="tabbar-item-name">
|
||||
<text>我的砍价</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AvatarImage from '@/components/avatar-image'
|
||||
import CountDown from '@/components/countdown'
|
||||
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 ActiveApi from '@/api/bargain/active'
|
||||
import * as TaskApi from '@/api/bargain/task'
|
||||
|
||||
const pageSize = 15
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody,
|
||||
AvatarImage,
|
||||
CountDown
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
// 是否正在加载中
|
||||
isLoading: true,
|
||||
// 当前tab索引
|
||||
curTab: 0,
|
||||
// 砍价会场商品列表
|
||||
activeList: getEmptyPaginateObj(),
|
||||
// 我的砍价列表
|
||||
myList: getEmptyPaginateObj(),
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: true,
|
||||
// 每页数据的数量; 默认10
|
||||
page: { size: pageSize },
|
||||
// 数量要大于3条才显示无更多数据
|
||||
noMoreSize: 3,
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
curTab(val) {
|
||||
// 设置页面标题
|
||||
uni.setNavigationBarTitle({ title: val == 0 ? '砍价会场' : '我的砍价' })
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 设置当前tab索引
|
||||
if (options.tab) {
|
||||
this.curTab = options.tab
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 上拉加载的回调 (页面初始化时也会执行一次)
|
||||
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
|
||||
* @param {Object} page
|
||||
*/
|
||||
upCallback(page) {
|
||||
const app = this
|
||||
// 设置列表数据
|
||||
app.getListData(page.num)
|
||||
.then(list => {
|
||||
const curPageLen = list.data.length
|
||||
const totalSize = list.data.total
|
||||
app.mescroll.endBySize(curPageLen, totalSize)
|
||||
})
|
||||
.catch(() => app.mescroll.endErr())
|
||||
},
|
||||
|
||||
// 获取列表数据(根据当前选项卡判断调用的方法)
|
||||
getListData(pageNo) {
|
||||
const apiFuc = {
|
||||
0: this.getActiveList,
|
||||
1: this.getMyList
|
||||
}
|
||||
return apiFuc[this.curTab](pageNo)
|
||||
},
|
||||
|
||||
// 获取砍价活动列表
|
||||
getActiveList(pageNo) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
ActiveApi.list({ page: pageNo }, { load: false })
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.activeList.data = getMoreListData(newList, app.activeList, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取我的砍价列表
|
||||
getMyList(pageNo) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
TaskApi.list({ page: pageNo }, { load: false })
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.myList.data = getMoreListData(newList, app.myList, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 切换当前选项卡
|
||||
onChangeTab(key = 0) {
|
||||
const app = this
|
||||
// 记录选项卡索引
|
||||
app.curTab = key
|
||||
// 刷新列表数据
|
||||
app.activeList = getEmptyPaginateObj()
|
||||
app.myList = getEmptyPaginateObj()
|
||||
app.mescroll.resetUpScroll()
|
||||
},
|
||||
|
||||
// 跳转到砍价商品详情页
|
||||
onTargetActive(item) {
|
||||
this.$navTo('pages/bargain/goods/index', { activeId: item.active_id, goodsId: item.goods_id })
|
||||
},
|
||||
|
||||
// 跳转到砍价任务详情
|
||||
onTargetTask(taskId) {
|
||||
this.$navTo('pages/bargain/task', { taskId })
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
// 构建页面参数
|
||||
const params = this.$getShareUrlParams()
|
||||
return {
|
||||
title: '砍价专区',
|
||||
path: `/pages/bargain/index?${params}`
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享到朋友圈
|
||||
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta)
|
||||
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
|
||||
*/
|
||||
onShareTimeline() {
|
||||
// 构建页面参数
|
||||
const params = this.$getShareUrlParams()
|
||||
return {
|
||||
title: '砍价专区',
|
||||
path: `/pages/bargain/index?${params}`
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
// 110 - 18 + 4
|
||||
padding-bottom: calc(constant(safe-area-inset-bottom) + 96rpx);
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 96rpx);
|
||||
}
|
||||
|
||||
.bargain-hall {
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
|
||||
// 砍价商品
|
||||
.goods-item {
|
||||
margin-bottom: 20rpx;
|
||||
background: #fff;
|
||||
padding: 20rpx 16rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.goods-image {
|
||||
.image {
|
||||
display: block;
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-info {
|
||||
width: 498rpx;
|
||||
padding-top: 8rpx;
|
||||
margin-left: 15rpx;
|
||||
position: relative;
|
||||
|
||||
.goods-name {
|
||||
font-size: 28rpx;
|
||||
min-height: 60rpx;
|
||||
}
|
||||
|
||||
// 正在参与的用户
|
||||
.peoples {
|
||||
margin-top: 15rpx;
|
||||
|
||||
.user-list {
|
||||
margin-right: 10rpx;
|
||||
|
||||
.user-item-avatar {
|
||||
margin-left: -8rpx;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.people__text {
|
||||
font-size: 24rpx;
|
||||
color: #818181;
|
||||
}
|
||||
}
|
||||
|
||||
// 商品原价
|
||||
.goods-price {
|
||||
margin-top: 15rpx;
|
||||
color: #818181;
|
||||
font-size: 25rpx;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
// 砍价底价
|
||||
.floor-price {
|
||||
color: $main-bg;
|
||||
|
||||
.small {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.big {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 砍价进度
|
||||
.task-rate {
|
||||
font-size: 25rpx;
|
||||
color: #a4a4a4;
|
||||
margin-top: 15rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 我的砍价 */
|
||||
// 砍价状态
|
||||
.task-status {
|
||||
margin-top: 32rpx;
|
||||
height: 58rpx;
|
||||
}
|
||||
|
||||
.task-status__text {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
// 倒计时
|
||||
.count-down {
|
||||
font-size: 25rpx;
|
||||
}
|
||||
|
||||
// 立即参加按钮
|
||||
.opt-touch {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 10rpx;
|
||||
}
|
||||
|
||||
.touch-btn {
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
background: #d3a975;
|
||||
border-radius: 30rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
}
|
||||
|
||||
// 底部选项卡
|
||||
.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);
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 96rpx;
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
font-size: 30rpx;
|
||||
// height: 42rpx;
|
||||
|
||||
&.active {
|
||||
.tabbar-item-content {
|
||||
color: $main-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.tabbar-item-icon {
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 分割线
|
||||
.tabbar-item__divider {
|
||||
padding: 22rpx 0;
|
||||
}
|
||||
|
||||
.divider-line {
|
||||
width: 1rpx;
|
||||
height: 62rpx;
|
||||
background: #ddd;
|
||||
}
|
||||
</style>
|
||||
Executable
+838
@@ -0,0 +1,838 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container">
|
||||
|
||||
<!-- 顶部操作栏 -->
|
||||
<view class="header dis-flex flex-x-between">
|
||||
<view class="item-touch" @click="$navTo('pages/index/index')">
|
||||
<text>返回首页</text>
|
||||
</view>
|
||||
<view class="item-touch" @click="handleShowRules()">
|
||||
<text>玩法详情</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="content">
|
||||
<!-- 砍价信息 -->
|
||||
<view class="infos-wrap">
|
||||
<view class="infos-top">
|
||||
<view class="infos-img">
|
||||
<avatar-image :url="task.user.avatar_url" :width="104" />
|
||||
</view>
|
||||
<view class="infos-name">
|
||||
<text>{{ task.user.nick_name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="infos-mask">
|
||||
<view class="infos-prompt" v-if="active.prompt_words">
|
||||
<text>{{ active.prompt_words }}</text>
|
||||
</view>
|
||||
<!-- 商品信息 -->
|
||||
<view class="infos-item" @click="$navTo('pages/bargain/goods/index', { activeId, goodsId: goods.goods_id })">
|
||||
<view class="infos-item-img">
|
||||
<image class="image" :src="goodsSkuInfo.goods_image ? goodsSkuInfo.goods_image : goods.goods_image"></image>
|
||||
</view>
|
||||
<view class="infos-item-info">
|
||||
<view class="infos-item-name">
|
||||
<text class="twoline-hide">{{ goods.goods_name }}</text>
|
||||
</view>
|
||||
<view class="infos-item-stock">
|
||||
<view class="stock-widget">
|
||||
<text>仅剩</text>
|
||||
<text class="stock-num">{{ goodsSkuInfo.stock_num }}</text>
|
||||
<text>件</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="infos-item-price dis-flex flex-y-end">
|
||||
<text class="price1 col-m">底价¥</text>
|
||||
<text class="price2 col-m">{{ task.floor_price }}</text>
|
||||
<text class="price3">¥{{ task.goods_price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<view class="connect">
|
||||
<view class="connect-ring bgf-ring--left">
|
||||
<text class="line"></text>
|
||||
</view>
|
||||
<view class="connect-ring bgf-ring--right">
|
||||
<text class="line"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 砍价进度 -->
|
||||
<view class="bargain-wrap">
|
||||
<!-- 已砍数目 -->
|
||||
<view class="bargain-info">
|
||||
<view v-if="task.status" class="bargain-ing">
|
||||
<block v-if="!task.is_floor">
|
||||
<text>已砍</text>
|
||||
<text class="focal col-m">{{ task.cut_money }}</text>
|
||||
<text>元,还差</text>
|
||||
<text class="focal col-m">{{ task.surplus_money }}</text>
|
||||
<text>元</text>
|
||||
</block>
|
||||
<block v-else>
|
||||
<text>已砍至最低</text>
|
||||
<text class="focal col-m">{{ task.floor_price }}</text>
|
||||
<text>元,砍价成功!</text>
|
||||
</block>
|
||||
</view>
|
||||
<view v-else class="bargain-ing">
|
||||
<text class="col-9">该砍价任务已结束~</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 砍价进度条 -->
|
||||
<view class="bgn__process m-top30">
|
||||
<view class="bgn__process-bottom">
|
||||
<view class="bgn__process-process process--ani" :style="{ width: `${task.bargain_rate}%` }"></view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 操作按钮 -->
|
||||
<view class="btn-container m-top30 dis-flex flex-x-center">
|
||||
<!-- 立即购买 -->
|
||||
<view v-if="showBuyBtn" class="btn-item btn-item__buy" :class="{ complete: task.is_floor }" @click="handleBuyNow()">
|
||||
<text>立即购买</text>
|
||||
</view>
|
||||
<!-- 分享给朋友 -->
|
||||
<button v-if="showShareBtn" open-type="share" class="btn-normal" @click="handleShareBtn()">
|
||||
<view class="btn-item btn-item__main">
|
||||
<text>邀请好友砍价</text>
|
||||
</view>
|
||||
</button>
|
||||
<!-- 砍一刀操作 -->
|
||||
<view v-if="showCatBtn" class="btn-item btn-item__main btn-item-long" @click="handleHelpCut()">
|
||||
<text>帮TA砍一刀</text>
|
||||
</view>
|
||||
<!-- 查看其他砍价活动 -->
|
||||
<view v-if="showOtherBtn" class="btn-item btn-item__main btn-item-long" @click="$navTo('pages/bargain/index')">
|
||||
<text>查看其他砍价活动</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 到期时间 -->
|
||||
<view class="bargain-p" v-if="task.status">
|
||||
<view class="bargain-people dis-flex flex-x-center flex-y-center">
|
||||
<text>活动还剩</text>
|
||||
<count-down :date="active.end_time" separator="zh" theme="text" />
|
||||
<text>结束,快来砍价吧~</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 好友助力榜 -->
|
||||
<view class="records-container" v-if="helpList.length">
|
||||
<view class="records">
|
||||
<view class="records-back"></view>
|
||||
<view class="records-content">
|
||||
<view class="records-h2">
|
||||
<text>好友助力榜</text>
|
||||
</view>
|
||||
<view class="friend-help">
|
||||
<view class="records-item" v-for="(help, idx) in helpList" :key="idx">
|
||||
<view class="records-left">
|
||||
<avatar-image :url="help.user.avatar_url" :width="70" />
|
||||
<text class="nick-name">{{ help.user.nick_name }}</text>
|
||||
</view>
|
||||
<view class="records-right">
|
||||
<text class="bold m-r-6">帮砍了</text>
|
||||
<text class="red">¥{{ help.cut_money }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 砍价规则弹窗 -->
|
||||
<u-modal v-if="!isLoading" v-model="showRules" title="砍价规则">
|
||||
<scroll-view style="height: 610rpx;" :scroll-y="true">
|
||||
<view class="pops-content">
|
||||
<text>{{ setting.rulesDesc }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-modal>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCurrentPage, buildUrL } from '@/core/app'
|
||||
import AvatarImage from '@/components/avatar-image'
|
||||
import CountDown from '@/components/countdown'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
import * as GoodsApi from '@/api/goods'
|
||||
import * as TaskApi from '@/api/bargain/task'
|
||||
import * as ActiveApi from '@/api/bargain/active'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AvatarImage,
|
||||
CountDown
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 是否正在加载中
|
||||
isLoading: true,
|
||||
|
||||
taskId: undefined, // 砍价任务ID
|
||||
activeId: undefined, // 砍价活动ID
|
||||
|
||||
task: {}, // 砍价任务详情
|
||||
active: {}, // 活动详情
|
||||
goods: {}, // 商品详情
|
||||
goodsSkuInfo: {}, // 商品SKU信息
|
||||
helpList: [], // 好友助力榜
|
||||
isCreater: false, // 是否为当前砍价任务的发起人
|
||||
isCut: false, // 当前是否已砍
|
||||
setting: {}, // 砍价规则
|
||||
|
||||
showRules: false, // 显示砍价规则
|
||||
disabled: false, // 按钮禁用状态
|
||||
|
||||
showBuyBtn: false, // 立即购买
|
||||
showShareBtn: false, // 邀请好友砍价
|
||||
showCatBtn: false, // 帮TA砍一刀
|
||||
showOtherBtn: false, // 查看其他砍价活动
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
this.taskId = options.taskId
|
||||
// 刷新页面数据
|
||||
this.onRefreshPage()
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 刷新页面数据
|
||||
onRefreshPage() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
// 获取砍价任务详情
|
||||
app.getTaskDetail()
|
||||
.then(result => {
|
||||
Promise.all([
|
||||
app.getActiveDetail(),
|
||||
app.getGoodsBasic(),
|
||||
app.getGoodsSku(),
|
||||
app.getHelpList()
|
||||
])
|
||||
.then(() => app.initShowBtn())
|
||||
.finally(() => app.isLoading = false)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取砍价任务详情
|
||||
getTaskDetail() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
TaskApi.detail(app.taskId)
|
||||
.then(result => {
|
||||
app.task = result.data.taskInfo
|
||||
app.activeId = app.task.active_id
|
||||
app.isCreater = result.data.isCreater
|
||||
app.isCut = result.data.isCut
|
||||
app.setting = result.data.setting
|
||||
resolve(result)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取砍价活动详情
|
||||
getActiveDetail() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
ActiveApi.detail(app.activeId)
|
||||
.then(result => {
|
||||
app.active = result.data.active
|
||||
resolve(result)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取商品信息
|
||||
getGoodsBasic() {
|
||||
const app = this
|
||||
const goodsId = app.task.goods_id
|
||||
return new Promise((resolve, reject) => {
|
||||
GoodsApi.basic(goodsId, false)
|
||||
.then(result => {
|
||||
app.goods = result.data.detail
|
||||
resolve(result)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取商品SKU信息
|
||||
getGoodsSku() {
|
||||
const app = this
|
||||
const goodsId = app.task.goods_id
|
||||
const goodsSkuId = app.task.goods_sku_id
|
||||
return new Promise((resolve, reject) => {
|
||||
GoodsApi.skuInfo(goodsId, goodsSkuId)
|
||||
.then(result => {
|
||||
app.goodsSkuInfo = result.data.skuInfo
|
||||
resolve(result)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取砍价活动详情
|
||||
getHelpList() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
TaskApi.helpList(app.taskId)
|
||||
.then(result => {
|
||||
app.helpList = result.data.list
|
||||
resolve(result)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 初始化:显示操作按钮
|
||||
initShowBtn() {
|
||||
const app = this
|
||||
// 立即购买
|
||||
const showBuyBtn = app.isCreater && !app.task.is_buy && app.task.status && (!app.active.is_floor_buy ||
|
||||
app.task.is_floor)
|
||||
// 帮砍一刀
|
||||
const showCatBtn = !app.isCreater && !app.isCut && !app.task.is_floor && app.task.status
|
||||
// 邀请好友砍价
|
||||
const showShareBtn = !showCatBtn && !app.task.is_floor && app.task.status
|
||||
// 查看其他砍价活动
|
||||
const showOtherBtn = !showBuyBtn && !showShareBtn && !showCatBtn
|
||||
app.showBuyBtn = showBuyBtn
|
||||
app.showCatBtn = showCatBtn
|
||||
app.showShareBtn = showShareBtn
|
||||
app.showOtherBtn = showOtherBtn
|
||||
},
|
||||
|
||||
// 显示砍价规则
|
||||
handleShowRules() {
|
||||
this.showRules = true
|
||||
},
|
||||
|
||||
// 立即购买
|
||||
handleBuyNow() {
|
||||
// 跳转到结算页
|
||||
const app = this
|
||||
app.$navTo('pages/checkout/index', {
|
||||
mode: 'bargain',
|
||||
taskId: app.taskId
|
||||
})
|
||||
},
|
||||
|
||||
// 帮砍一刀
|
||||
handleHelpCut() {
|
||||
const app = this
|
||||
app.disabled = true
|
||||
TaskApi.helpCut(app.taskId)
|
||||
.then(result => {
|
||||
app.$toast(result.message)
|
||||
setTimeout(() => app.onRefreshPage(), 1800)
|
||||
})
|
||||
.finally(() => app.disabled = false)
|
||||
},
|
||||
|
||||
// 点击分享按钮
|
||||
handleShareBtn() {
|
||||
// #ifndef MP
|
||||
this.handleCopyLink()
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 复制当前页面链接
|
||||
handleCopyLink() {
|
||||
const app = this
|
||||
app.getShareUrl().then(shareUrl => {
|
||||
// 复制到剪贴板
|
||||
uni.setClipboardData({
|
||||
data: shareUrl,
|
||||
success: () => app.$toast('复制链接成功,快去发送给朋友吧'),
|
||||
fail: err => app.$toast('复制失败')
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取分享链接 (H5外链)
|
||||
getShareUrl() {
|
||||
const { path, query } = getCurrentPage()
|
||||
return new Promise((resolve, reject) => {
|
||||
// 获取h5站点地址
|
||||
SettingModel.h5Url(true)
|
||||
.then(baseUrl => {
|
||||
// 生成完整的分享链接
|
||||
const shareUrl = buildUrL(baseUrl, path, query)
|
||||
resolve(shareUrl)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
// 构建页面参数
|
||||
const app = this
|
||||
const params = app.$getShareUrlParams({ taskId: app.taskId })
|
||||
return {
|
||||
title: app.active.share_title,
|
||||
path: `/pages/bargain/task?${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({ taskId: app.taskId })
|
||||
return {
|
||||
title: app.active.share_title,
|
||||
path: `/pages/bargain/task?${params}`
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
background: url('https://si.geilicdn.com/img-310900000167962321710a026860-unadjust_750_686.png') top no-repeat,
|
||||
linear-gradient(90deg, #fea044, #f9565d 63%, #e63378);
|
||||
background-size: 100% auto;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* 头部区域 */
|
||||
.header {
|
||||
padding: 30rpx 30rpx;
|
||||
|
||||
.item-touch {
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
padding: 7rpx 20rpx;
|
||||
background: rgba(0, 0, 0, 0.17);
|
||||
border-radius: 22rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding: 10rpx 30rpx 60rpx 30rpx;
|
||||
}
|
||||
|
||||
/* 砍价信息 */
|
||||
.infos-wrap {
|
||||
background: #fff;
|
||||
box-shadow: 0 4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
|
||||
padding: 0 30rpx 40rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.infos-top {
|
||||
position: relative;
|
||||
top: -42rpx;
|
||||
margin-bottom: -22rpx;
|
||||
}
|
||||
|
||||
.infos-img {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
padding: 8rpx;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
margin: 0 auto;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.infos-name {
|
||||
margin: 8rpx auto 0;
|
||||
width: 80%;
|
||||
font-size: 26rpx;
|
||||
color: #9a9a9a;
|
||||
text-align: center;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.infos-prompt {
|
||||
text-align: center;
|
||||
font-size: 30rpx;
|
||||
color: #222;
|
||||
line-height: 48rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.infos-item {
|
||||
margin-top: 40rpx;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.infos-item-img {
|
||||
flex: none;
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.infos-item-info {
|
||||
margin-left: 25rpx;
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.infos-item-name {
|
||||
font-size: 28rpx;
|
||||
color: #404040;
|
||||
line-height: 40rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.infos-item-stock {
|
||||
.stock-widget {
|
||||
display: inline-block;
|
||||
min-width: 100rpx;
|
||||
padding: 0 20rpx;
|
||||
background-image: linear-gradient(-90deg, #fe9c3f, #fb6253 99%);
|
||||
border-radius: 40rpx;
|
||||
height: 40rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
line-height: 40rpx;
|
||||
margin-top: 6rpx;
|
||||
|
||||
.stock-num {
|
||||
margin: 0 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.infos-item-price {
|
||||
font-size: 0;
|
||||
margin-top: 8rpx;
|
||||
|
||||
.price1 {
|
||||
font-size: 24rpx;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.price2 {
|
||||
margin-left: 4rpx;
|
||||
font-size: 36rpx;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.price3 {
|
||||
margin-left: 10rpx;
|
||||
font-size: 24rpx;
|
||||
color: #9a9a9a;
|
||||
line-height: 32rpx;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.connect {
|
||||
position: relative;
|
||||
height: 20rpx;
|
||||
}
|
||||
|
||||
.connect-ring {
|
||||
position: absolute;
|
||||
top: -28rpx;
|
||||
height: 76rpx;
|
||||
width: 20rpx;
|
||||
padding: 8rpx 6rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:after,
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
left: 0;
|
||||
height: 20rpx;
|
||||
width: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.line {
|
||||
z-index: 8;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
&.bgf-ring--left {
|
||||
left: 20rpx;
|
||||
|
||||
&:before {
|
||||
top: 0;
|
||||
background: #f4914e;
|
||||
}
|
||||
|
||||
&:after {
|
||||
bottom: 0;
|
||||
background: #f4914e;
|
||||
}
|
||||
}
|
||||
|
||||
&.bgf-ring--right {
|
||||
right: 20rpx;
|
||||
|
||||
&:before {
|
||||
top: 0;
|
||||
background: #e03e71;
|
||||
}
|
||||
|
||||
&:after {
|
||||
bottom: 0;
|
||||
background: #e03e71;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 砍价进度 */
|
||||
.bargain-wrap {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
padding: 40rpx 30rpx 30rpx;
|
||||
box-shadow: 0 4rpx 40rpx 0 rgba(144, 52, 52, 0.1);
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.bargain-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
color: #404040;
|
||||
line-height: 40rpx;
|
||||
|
||||
.focal {
|
||||
margin: 0 5rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bargain-p {
|
||||
margin-top: 40rpx;
|
||||
min-height: 32rpx;
|
||||
}
|
||||
|
||||
.bargain-people {
|
||||
font-size: 24rpx;
|
||||
color: #9a9a9a;
|
||||
text-align: center;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
/* 进度条 */
|
||||
.bgn__process {
|
||||
position: relative;
|
||||
padding: 30rpx 0;
|
||||
}
|
||||
|
||||
.bgn__process-bottom {
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
background-image: linear-gradient(0deg, #f0f2f7, #e8ebf3);
|
||||
}
|
||||
|
||||
.bgn__process-bottom,
|
||||
.bgn__process-process {
|
||||
position: relative;
|
||||
height: 30rpx;
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
|
||||
.bgn__process-process {
|
||||
background-image: linear-gradient(90deg, #ffc108, #fde586);
|
||||
background: #ffc108;
|
||||
|
||||
&.process--ani {
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 32rpx;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
margin-top: -16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.btn-container {
|
||||
.btn-item {
|
||||
color: #fff;
|
||||
height: 80rpx;
|
||||
font-size: 30rpx;
|
||||
border-radius: 15rpx;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-item__buy {
|
||||
width: 280rpx;
|
||||
margin-right: 30rpx;
|
||||
background-image: linear-gradient(90deg, #fa9e1b, #fe5b1b);
|
||||
box-shadow: #fe5b1b 0 22rpx 48rpx -22rpx;
|
||||
|
||||
&.complete {
|
||||
width: 360rpx;
|
||||
animation: btn_anim 0.9s linear infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-item__main {
|
||||
width: 320rpx;
|
||||
background-image: linear-gradient(90deg, #fe316c, #fd584e);
|
||||
box-shadow: #fd584e 0 22rpx 48rpx -22rpx;
|
||||
animation: btn_anim 0.9s linear infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.btn-item-long {
|
||||
max-width: 400rpx;
|
||||
}
|
||||
|
||||
/* 按钮动画 */
|
||||
@keyframes btn_anim {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
40% {
|
||||
-webkit-transform: scale(1.05);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
/* 好友助力榜 */
|
||||
.records-container {
|
||||
margin-top: 44rpx;
|
||||
}
|
||||
|
||||
.records {
|
||||
position: relative;
|
||||
color: #404040;
|
||||
box-shadow: 0 4rpx 40rpx 0 rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.records-back {
|
||||
position: absolute;
|
||||
left: -14rpx;
|
||||
right: -14rpx;
|
||||
top: -14rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 28rpx;
|
||||
z-index: 1;
|
||||
background: #cb272d;
|
||||
}
|
||||
|
||||
.records-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
background: #fff;
|
||||
padding: 40rpx 30rpx;
|
||||
}
|
||||
|
||||
.records-h2 {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 60rpx;
|
||||
align-items: center;
|
||||
font-weight: 700;
|
||||
font-size: 34rpx;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
|
||||
.friend-help {
|
||||
overflow: hidden;
|
||||
padding: 40rpx 0 20rpx;
|
||||
transition: max-height 0.6s ease-out;
|
||||
}
|
||||
|
||||
.records-left,
|
||||
.records-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.records-left {
|
||||
.nick-name {
|
||||
display: inline-block;
|
||||
margin-left: 14rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.records-left .nick-name,
|
||||
.records-right {
|
||||
font-size: 28rpx;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.records-right {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.red {
|
||||
color: #e53a40;
|
||||
}
|
||||
}
|
||||
|
||||
// 砍价规则(弹窗)
|
||||
.pops-content {
|
||||
padding: 30rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 44rpx;
|
||||
text-align: left;
|
||||
color: #606266;
|
||||
min-height: 320rpx;
|
||||
max-height: 640rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
Executable
+516
@@ -0,0 +1,516 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<!-- 页面顶部 -->
|
||||
<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="appTheme.mainBg" :checked="inArray(item.id, checkedIds)" />
|
||||
</label>
|
||||
<view class="goods-image" @click="onTargetGoods(item.goods_id)">
|
||||
<image class="image" :src="item.goods.goods_image" mode="scaleToFill"></image>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="goods-title" @click="onTargetGoods(item.goods_id)">
|
||||
<text class="twoline-hide">{{ item.goods.goods_name }}</text>
|
||||
</view>
|
||||
<view class="goods-props clearfix">
|
||||
<view class="goods-props-item" v-for="(props, idx) in item.goods.skuInfo.goods_props" :key="idx">
|
||||
<text>{{ props.value.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-foot">
|
||||
<view class="goods-price">
|
||||
<text class="unit">¥</text>
|
||||
<text class="value">{{ item.goods.skuInfo.goods_price }}</text>
|
||||
</view>
|
||||
<view class="stepper">
|
||||
<u-number-box :min="1" :value="item.goods_num" :step="1" @change="onChangeStepper($event, item)" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 购物车数据为空 -->
|
||||
<empty v-if="!list.length" :isLoading="isLoading" :custom-style="{ padding: '180rpx 50rpx' }" tips="您的购物车是空的, 快去逛逛吧">
|
||||
<view slot="slot" class="empty-ipt" @click="onTargetIndex">
|
||||
<text>去逛逛</text>
|
||||
</view>
|
||||
</empty>
|
||||
|
||||
<!-- 商品推荐 -->
|
||||
<recommended />
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view v-if="list.length" class="footer-fixed">
|
||||
<label class="all-radio" @click="handleCheckAll">
|
||||
<radio class="radio" :color="appTheme.mainBg" :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 Empty from '@/components/empty'
|
||||
import Recommended from '@/components/recommended'
|
||||
import { inArray, arrayIntersect, debounce } from '@/utils/util'
|
||||
import { checkLogin, setCartTotalNum, setCartTabBadge } from '@/core/app'
|
||||
import * as CartApi from '@/api/cart'
|
||||
|
||||
const CartIdsIndex = 'CartIds'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Empty,
|
||||
Recommended
|
||||
},
|
||||
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>
|
||||
.container {
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
// 页面顶部
|
||||
.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: $main-bg;
|
||||
margin: 0 2rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-edit {
|
||||
padding-left: 20rpx;
|
||||
|
||||
.icon {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.edit {
|
||||
color: $main-bg;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 购物车列表
|
||||
.cart-list {
|
||||
padding: 0 16rpx 0 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;
|
||||
margin-right: 10rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: 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: $main-bg;
|
||||
|
||||
.unit {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 空数据按钮
|
||||
.empty-ipt {
|
||||
margin: 0 auto;
|
||||
width: 250rpx;
|
||||
height: 70rpx;
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// 底部操作栏
|
||||
.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: $main-bg;
|
||||
|
||||
.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;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// 去结算按钮
|
||||
.btn-main {
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Executable
+297
@@ -0,0 +1,297 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<!-- 一级分类 -->
|
||||
<scroll-view class="cate-left" :scroll-y="true" :style="{ height: `${scrollHeight}px` }">
|
||||
<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 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>
|
||||
<!-- 加入购物车组件 -->
|
||||
<AddCartPopup ref="AddCartPopup" @addCart="onUpdateCartTabBadge" />
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
|
||||
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
|
||||
import { getEmptyPaginateObj, getMoreListData, setCartTabBadge } from '@/core/app'
|
||||
import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
|
||||
import Empty from '@/components/empty'
|
||||
import AddCartBtn from '@/components/add-cart-btn'
|
||||
import AddCartPopup from '@/components/add-cart-popup'
|
||||
import { rpx2px } from '@/utils/util'
|
||||
import * as GoodsApi from '@/api/goods'
|
||||
|
||||
const pageSize = 15
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody,
|
||||
Empty,
|
||||
AddCartBtn,
|
||||
AddCartPopup
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
props: {
|
||||
// 分类列表
|
||||
list: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
// 分类设置
|
||||
setting: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 枚举类
|
||||
PageCategoryStyleEnum,
|
||||
// 列表高度
|
||||
scrollHeight: 0,
|
||||
// 一级分类:指针
|
||||
curIndex: -1,
|
||||
// 商品列表
|
||||
goodsList: getEmptyPaginateObj(),
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: true,
|
||||
// 每页数据的数量; 默认10
|
||||
page: { size: pageSize },
|
||||
// 数量要大于3条才显示无更多数据
|
||||
noMoreSize: 3,
|
||||
// 返回顶部
|
||||
toTop: { right: 30, bottom: 48, zIndex: 9 }
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 设置分类列表高度
|
||||
this.setListHeight()
|
||||
},
|
||||
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.curIndex > -1 ? app.list[app.curIndex].category_id : 0
|
||||
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)
|
||||
})
|
||||
},
|
||||
|
||||
// 设置列表内容的高度
|
||||
setListHeight() {
|
||||
const { windowHeight } = uni.getSystemInfoSync()
|
||||
this.scrollHeight = windowHeight - rpx2px(96)
|
||||
},
|
||||
|
||||
// 一级分类:选中分类
|
||||
handleSelectNav(index) {
|
||||
this.curIndex = index
|
||||
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()
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding-left: 173rpx;
|
||||
}
|
||||
|
||||
// 分类内容
|
||||
.cate-content {
|
||||
z-index: 1;
|
||||
background: #fff;
|
||||
padding-top: 96rpx;
|
||||
}
|
||||
|
||||
// 一级分类+二级分类 20
|
||||
.cate-left {
|
||||
width: 173rpx;
|
||||
height: 100%;
|
||||
background: #f8f8f8;
|
||||
color: #444;
|
||||
|
||||
position: fixed;
|
||||
top: calc(96rpx + var(--window-top));
|
||||
left: var(--window-left);
|
||||
bottom: var(--window-bottom);
|
||||
}
|
||||
|
||||
// 左侧一级分类
|
||||
.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: $main-bg
|
||||
}
|
||||
}
|
||||
|
||||
// 商品列表
|
||||
.goods-list {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.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: $main-bg;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.price_y {
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+110
@@ -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">
|
||||
<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">
|
||||
<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>
|
||||
Executable
+180
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<view v-if="list.length > 0" class="secondary" :style="appThemeStyle">
|
||||
<!-- 二级分类 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: $main-bg;
|
||||
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>
|
||||
Executable
+166
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 搜索框 -->
|
||||
<search class="search" tips="搜索商品" @event="$navTo('pages/search/index')" />
|
||||
|
||||
<!-- 一级分类 -->
|
||||
<primary v-if="setting.style == PageCategoryStyleEnum.ONE_LEVEL_BIG.value || setting.style == PageCategoryStyleEnum.ONE_LEVEL_SMALL.value"
|
||||
:display="setting.style" :list="list" />
|
||||
|
||||
<!-- 二级分类 -->
|
||||
<secondary v-if="setting.style == PageCategoryStyleEnum.TWO_LEVEL.value" :list="list" />
|
||||
|
||||
<!-- 分类+商品 -->
|
||||
<commodity v-if="setting.style == PageCategoryStyleEnum.COMMODITY.value" ref="mescrollItem" :list="list" :setting="setting" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollCompMixin from '@/components/mescroll-uni/mixins/mescroll-comp'
|
||||
import { setCartTabBadge } from '@/core/app'
|
||||
import SettingKeyEnum from '@/common/enum/setting/Key'
|
||||
import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
import * as CategoryApi from '@/api/category'
|
||||
import Empty from '@/components/empty'
|
||||
import Search from '@/components/search'
|
||||
import Primary from './components/primary'
|
||||
import Secondary from './components/secondary'
|
||||
import Commodity from './components/commodity'
|
||||
|
||||
// 最后一次刷新时间
|
||||
let lastRefreshTime;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Search,
|
||||
Empty,
|
||||
Primary,
|
||||
Secondary,
|
||||
Commodity
|
||||
},
|
||||
mixins: [MescrollCompMixin],
|
||||
data() {
|
||||
return {
|
||||
// 枚举类
|
||||
PageCategoryStyleEnum,
|
||||
// 分类列表
|
||||
list: [],
|
||||
// 分类模板设置
|
||||
setting: {},
|
||||
// 正在加载中
|
||||
isLoading: true
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad() {
|
||||
// 加载页面数据
|
||||
this.onRefreshPage()
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
// 每间隔5分钟自动刷新一次页面数据
|
||||
const curTime = new Date().getTime()
|
||||
if ((curTime - lastRefreshTime) > 5 * 60 * 1000) {
|
||||
this.onRefreshPage()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 刷新页面
|
||||
onRefreshPage() {
|
||||
// 记录刷新时间
|
||||
lastRefreshTime = new Date().getTime()
|
||||
// 获取页面数据
|
||||
this.getPageData()
|
||||
// 更新购物车角标
|
||||
setCartTabBadge()
|
||||
},
|
||||
|
||||
// 获取页面数据
|
||||
getPageData() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([
|
||||
// 获取分类模板设置
|
||||
// 优化建议: 可以将此处的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]
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化分类列表数据
|
||||
* @param {Object} result
|
||||
*/
|
||||
initCategory(result) {
|
||||
this.list = result.data.list
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置分享内容
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
const app = this
|
||||
return {
|
||||
title: _this.templet.shareTitle,
|
||||
path: '/pages/category/index?' + app.$getShareUrlParams()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享到朋友圈
|
||||
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta)
|
||||
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
|
||||
*/
|
||||
onShareTimeline() {
|
||||
const app = this
|
||||
return {
|
||||
title: _this.templet.shareTitle,
|
||||
path: '/pages/category/index?' + app.$getShareUrlParams()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
// 搜索框
|
||||
.search {
|
||||
position: fixed;
|
||||
top: var(--window-top);
|
||||
left: var(--window-left);
|
||||
right: var(--window-right);
|
||||
z-index: 9;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,367 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 消息列表 -->
|
||||
<view class="shop-list" v-if="msg">
|
||||
<view class="item" v-for="(item,index) in msg.choices" :key="index">
|
||||
<!-- <image :src="item.logo" mode="aspectFit"></image> -->
|
||||
<view class="info">
|
||||
<!-- <text class="title">{{item.merchantName}}</text> -->
|
||||
<text class="desc">{{ item.message.content }}</text>
|
||||
<!-- <view class="tag">
|
||||
<view class="mr12">
|
||||
<u-tag size="mini" plain text="租车站"></u-tag>
|
||||
</view>
|
||||
<view class="mr12">
|
||||
<u-tag size="mini" type="warning" plain text="换点站"></u-tag>
|
||||
</view>
|
||||
<view class="mr12">
|
||||
<u-tag size="mini" type="success" plain text="买车站"></u-tag>
|
||||
</view>
|
||||
</view> -->
|
||||
<!-- <text class="distance">距离4.2km</text> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 定位按钮 -->
|
||||
<!-- <view v-if="!isAuthor" class="widget-location dis-flex flex-x-center flex-y-center" @click="onAuthorize()">
|
||||
<text class="iconfont icon-locate"></text>
|
||||
</view> -->
|
||||
<!-- <empty v-if="list.length == 0" :isLoading="isLoading" tips="亲,暂无消息" /> -->
|
||||
<!-- <view v-if="msg">
|
||||
<view v-for="(item,index) in msg.choices" :key="index">
|
||||
{{ item.message }}
|
||||
</view>
|
||||
</view> -->
|
||||
<view class="footer">
|
||||
<view class="send-box">
|
||||
<input class="input" type="text" v-model="text" />
|
||||
<button @click="onSubmit" class="submit-text">提交</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store/index.js'
|
||||
import storage from '../../utils/storage'
|
||||
import {
|
||||
ACCESS_TOKEN,
|
||||
USER_ID
|
||||
} from '@/store/mutation-types'
|
||||
import {
|
||||
pageMerchant
|
||||
} from '@/websoft/api/merchant.js'
|
||||
import { chat } from '@/websoft/api/chatgpt.js'
|
||||
import Empty from '@/components/empty'
|
||||
import {
|
||||
userId
|
||||
} from '../../config'
|
||||
import login from '../../websoft/api/login'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
// Search,
|
||||
Empty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '国内馆',
|
||||
// 正在加载中
|
||||
isLoading: false,
|
||||
// 是否授权了定位权限
|
||||
isAuthor: true,
|
||||
// 当前选择的门店ID
|
||||
selectedId: null,
|
||||
// 订单列表数据
|
||||
list: [],
|
||||
text: '',
|
||||
msg: null
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const app = this
|
||||
if (!store.getters.userId) {
|
||||
console.log("未登录1: ");
|
||||
return false;
|
||||
}
|
||||
// 获取用户坐标
|
||||
app.getLocation(res => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
},
|
||||
onShow() {
|
||||
const app = this
|
||||
if (app.list.length == 0) {
|
||||
app.getShopList(app.longitude, app.latitude)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取门店列表
|
||||
getShopList(longitude, latitude) {
|
||||
const app = this
|
||||
},
|
||||
|
||||
// 获取用户坐标
|
||||
// 参考文档:https://uniapp.dcloud.io/api/location/location?id=getlocation
|
||||
getLocation(callback) {
|
||||
const app = this
|
||||
uni.getLocation({
|
||||
success: callback,
|
||||
fail() {
|
||||
app.$toast('获取定位失败,请点击右下角按钮重新尝试定位')
|
||||
app.isAuthor = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 授权启用定位权限
|
||||
onAuthorize() {
|
||||
const app = this
|
||||
// #ifdef MP
|
||||
uni.openSetting({
|
||||
success(res) {
|
||||
if (res.authSetting['scope.userLocation']) {
|
||||
console.log('定位权限授权成功')
|
||||
app.isAuthor = true
|
||||
setTimeout(() => {
|
||||
// 获取用户坐标
|
||||
app.getLocation((res) => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
// 获取用户坐标
|
||||
app.getLocation((res) => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
|
||||
navTo(merchantId) {
|
||||
const navTo = uni.$u.route()
|
||||
navTo('pages/merchant/detail', {
|
||||
merchantId
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 选择门店
|
||||
*/
|
||||
onSelectedShop(merchantId, merchantCode) {
|
||||
uni.$u.route('pages/merchant/detail', {
|
||||
merchantId,
|
||||
merchantCode
|
||||
})
|
||||
},
|
||||
|
||||
changeCity() {
|
||||
this.$toast('切换区域')
|
||||
},
|
||||
|
||||
onSubmit() {
|
||||
const app = this
|
||||
chat({
|
||||
tenantId: 6,
|
||||
content: app.text
|
||||
}).then(res => {
|
||||
console.log("res: ",res);
|
||||
app.msg = res.data
|
||||
}).catch(err => {
|
||||
if(err.code == 1){
|
||||
app.$error("您的免费额度已用完")
|
||||
}
|
||||
console.log("err: ",err);
|
||||
})
|
||||
pageMerchant({}).then(res => {
|
||||
console.log("res: ",res);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bg {
|
||||
background-color: #F5F5F8
|
||||
}
|
||||
|
||||
.shop-list {
|
||||
.item {
|
||||
padding: 20rpx;
|
||||
margin: 20rpx auto;
|
||||
background-color: #ffffff;
|
||||
width: 660rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
image {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
margin-right: 40rpx;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 28rpx;
|
||||
display: flex;
|
||||
|
||||
.mr12 {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.distance {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-box {
|
||||
padding: 20rpx 36rpx 0 36rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.location {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #999999;
|
||||
|
||||
image {
|
||||
width: 60rpx;
|
||||
height: 60rpx
|
||||
}
|
||||
}
|
||||
|
||||
.u-subsection {
|
||||
width: 260rpx;
|
||||
margin-top: 7rpx
|
||||
}
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
display: flex;
|
||||
width: 80%;
|
||||
height: 64rpx
|
||||
}
|
||||
|
||||
// 搜索输入框
|
||||
.search-input {
|
||||
width: 90%;
|
||||
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;
|
||||
|
||||
.input-placeholder {
|
||||
color: #aba9a9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shop-avatar {
|
||||
width: 200rpx;
|
||||
margin-right: 24rpx
|
||||
}
|
||||
|
||||
// 搜索按钮
|
||||
.search-button {
|
||||
width: 25%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.button {
|
||||
height: 64rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 0 5px 5px 0;
|
||||
background: #2C71C7
|
||||
}
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
width: 100%;
|
||||
margin-left: 20rpx;
|
||||
|
||||
.title {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.about {
|
||||
display: flex;
|
||||
|
||||
.mr12 {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.u-body-item {
|
||||
align-items: stretch !important
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: 750rpx;
|
||||
position: absolute;
|
||||
bottom: 100rpx;
|
||||
.send-box {
|
||||
width: 700rpx;
|
||||
margin: auto;
|
||||
height: 90rpx;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
border-radius: 20rpx;
|
||||
.submit-text{ padding: 0 30rpx;}
|
||||
.input{
|
||||
width: 512rpx;
|
||||
height: 82rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+601
@@ -0,0 +1,601 @@
|
||||
<template>
|
||||
<view>
|
||||
<view v-if="!isLoading && order" class="container" :style="appThemeStyle">
|
||||
<!-- 订单信息 -->
|
||||
<view class="order-info">
|
||||
<!-- 支付剩余时间 -->
|
||||
<view class="order-countdown">
|
||||
<text class="m-r-6">剩余时间</text>
|
||||
<count-down :date="expirationTime" separator="zh" theme="text" />
|
||||
</view>
|
||||
<!-- 付款金额 -->
|
||||
<view class="order-amount">
|
||||
<text class="unit">¥</text>
|
||||
<text class="amount">{{ order.totalPrice }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付方式 -->
|
||||
<view class="payment-method">
|
||||
<view v-for="(item, index) in methods" :key="index" class="pay-item dis-flex flex-x-between"
|
||||
@click="handleSelectPayType(index)">
|
||||
<view class="item-left dis-flex flex-y-center">
|
||||
<view class="item-left_icon" :class="[item.method]">
|
||||
<text class="iconfont" :class="[item.icon]"></text>
|
||||
</view>
|
||||
<view class="item-left_text">
|
||||
<text>{{ item.method }}</text>
|
||||
</view>
|
||||
<view v-if="item.method === '余额支付'" class="user-balance">
|
||||
<text>(可用¥{{ personal.balance }}元)</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-right col-m" v-if="curPaymentItem && curPaymentItem.method == item.method">
|
||||
<text class="iconfont icon-check"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 确认按钮 -->
|
||||
<view class="footer-fixed">
|
||||
<view class="btn-wrapper">
|
||||
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">确认支付</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付确认弹窗 -->
|
||||
<!-- #ifdef H5 -->
|
||||
<u-modal v-if="tempUnifyData" v-model="showConfirmModal" title="支付确认" show-cancel-button confirm-text="已完成支付"
|
||||
:confirm-color="appTheme.mainBg" negative-top="100" :asyncClose="true"
|
||||
@confirm="onTradeQuery(tempUnifyData.outTradeNo, tempUnifyData.method)">
|
||||
<view class="modal-content">
|
||||
<text>请在{{ PayMethodClientNameEnum[tempUnifyData.method] }}内完成支付,如果您已经支付成功,请点击“已完成支付”按钮</text>
|
||||
</view>
|
||||
</u-modal>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<empty v-else :isLoading="isLoading" tips="订单不存在" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import storage from '@/utils/storage'
|
||||
import {
|
||||
inArray,
|
||||
urlEncode
|
||||
} from '@/utils/util'
|
||||
import {
|
||||
Alipay,
|
||||
Wechat
|
||||
} from '@/core/payment'
|
||||
import CountDown from '@/components/countdown'
|
||||
import {
|
||||
PayMethodEnum
|
||||
} from '@/common/enum/payment'
|
||||
import {
|
||||
PayStatusEnum
|
||||
} from '@/common/enum/order'
|
||||
import {
|
||||
getOrder,
|
||||
setPayStatus
|
||||
} from '@/websoft/api/order.js'
|
||||
import {
|
||||
pagePayment,
|
||||
alipay,
|
||||
payQuery,
|
||||
balance
|
||||
} from '@/websoft/api/payment.js'
|
||||
import {
|
||||
dateFormat
|
||||
} from '@/utils/util.js'
|
||||
import Empty from '@/components/empty'
|
||||
import * as CashierApi from '@/api/cashier'
|
||||
import { getUser } from '@/websoft/api/user.js'
|
||||
|
||||
// 支付方式对应的图标
|
||||
const PayMethodIconEnum = {
|
||||
[PayMethodEnum.WECHAT.value]: 'icon-wechat-pay',
|
||||
[PayMethodEnum.ALIPAY.value]: 'icon-alipay',
|
||||
[PayMethodEnum.BALANCE.value]: 'icon-balance-pay'
|
||||
}
|
||||
|
||||
// 支付方式的终端名称
|
||||
const PayMethodClientNameEnum = {
|
||||
[PayMethodEnum.WECHAT.value]: '微信',
|
||||
[PayMethodEnum.ALIPAY.value]: '支付宝'
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CountDown,
|
||||
Empty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 加载中
|
||||
isLoading: true,
|
||||
// 确认按钮禁用
|
||||
disabled: false,
|
||||
// 枚举类
|
||||
PayMethodEnum,
|
||||
PayMethodIconEnum,
|
||||
PayMethodClientNameEnum,
|
||||
// 当前选中的支付方式
|
||||
curPaymentItem: {
|
||||
method: '余额支付'
|
||||
},
|
||||
// 当前订单ID
|
||||
orderId: null,
|
||||
// 当前结算订单信息
|
||||
order: null,
|
||||
// 订单过期时间
|
||||
expirationTime: new Date(),
|
||||
// 个人信息
|
||||
personal: {
|
||||
balance: '0.00'
|
||||
},
|
||||
// 当前客户端的支付方式列表(后端根据platform判断)
|
||||
methods: [
|
||||
{
|
||||
id: 1,
|
||||
method: '余额支付',
|
||||
icon: 'icon-balance-pay'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
method: '支付宝',
|
||||
icon: 'icon-alipay'
|
||||
}
|
||||
],
|
||||
// 支付确认弹窗
|
||||
showConfirmModal: false,
|
||||
// #ifdef H5
|
||||
// 当前微信支付信息 (临时数据, 仅用于H5端)
|
||||
tempUnifyData: {
|
||||
outTradeNo: '',
|
||||
method: ''
|
||||
},
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad({
|
||||
orderId
|
||||
}) {
|
||||
// 记录订单ID
|
||||
this.orderId = Number(orderId)
|
||||
// 获取收银台信息
|
||||
this.getCashierInfo()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取收银台信息
|
||||
getCashierInfo() {
|
||||
const app = this
|
||||
const {
|
||||
orderId
|
||||
} = this
|
||||
app.isLoading = true
|
||||
// 加载订单
|
||||
getOrder(orderId)
|
||||
.then(res => {
|
||||
app.order = res.data
|
||||
app.isLoading = false
|
||||
// 处理订单过期时间(*1小时)
|
||||
var expirationTime = new Date().getTime() + 60 * 60 * 1000 * 0.20
|
||||
app.expirationTime = dateFormat('YYYY-mm-dd HH:MM:SS', new Date(expirationTime))
|
||||
|
||||
})
|
||||
.catch(e => {
|
||||
console.log("e: ", e);
|
||||
})
|
||||
// 查询余额
|
||||
getUser().then(res => app.personal = res.data)
|
||||
// pagePayment({}).then(res => {
|
||||
// app.methods = res.data.list
|
||||
// })
|
||||
// CashierApi.orderInfo(app.orderId, { client: app.platform })
|
||||
// .then(result => {
|
||||
// app.order = result.data.order
|
||||
// console.log("app.order: ",app.order);
|
||||
// app.personal = result.data.personal
|
||||
// app.methods = result.data.paymentMethods
|
||||
// app.isLoading = false
|
||||
// app.setDefaultPayType()
|
||||
// app.checkOrderPayStatus()
|
||||
// // #ifdef H5
|
||||
// // 判断当前页面来源于浏览器返回
|
||||
// this.performance()
|
||||
// // #endif
|
||||
// })
|
||||
},
|
||||
|
||||
// 设置默认的支付方式
|
||||
setDefaultPayType() {
|
||||
const app = this
|
||||
if (app.disabled) return
|
||||
const defaultIndex = app.methods.findIndex(item => item.is_default == true)
|
||||
defaultIndex > -1 && app.handleSelectPayType(defaultIndex)
|
||||
},
|
||||
|
||||
// 判断当前订单是否为已支付
|
||||
checkOrderPayStatus() {
|
||||
const app = this
|
||||
if (app.order.pay_status == PayStatusEnum.SUCCESS.value) {
|
||||
app.$toast('恭喜您,订单已付款成功')
|
||||
app.onSuccessNav()
|
||||
}
|
||||
},
|
||||
|
||||
// 选择支付方式
|
||||
handleSelectPayType(index) {
|
||||
this.curPaymentItem = this.methods[index]
|
||||
console.log("this.curPaymentItem: ", this.curPaymentItem);
|
||||
},
|
||||
|
||||
// 判断当前页面来源于浏览器返回
|
||||
// #ifdef H5
|
||||
performance() {
|
||||
const app = this
|
||||
// 判断订单状态, 异步回调会将订单状态变为已支付, 那么就不需要让用户手动查单了
|
||||
if (app.order.pay_status == PayStatusEnum.PENDING.value) {
|
||||
app.alipayPerformance()
|
||||
app.wechatPerformance()
|
||||
}
|
||||
},
|
||||
|
||||
// H5端支付宝支付完成跳转回当前页面时触发
|
||||
alipayPerformance() {
|
||||
const app = this
|
||||
app.tempUnifyData = Alipay.performance()
|
||||
if (app.tempUnifyData) {
|
||||
app.onTradeQuery(app.tempUnifyData.outTradeNo, app.tempUnifyData.method)
|
||||
}
|
||||
},
|
||||
|
||||
// H5端微信支付完成或返回时触发
|
||||
wechatPerformance() {
|
||||
const app = this
|
||||
app.tempUnifyData = Wechat.performance(app.orderId)
|
||||
if (app.tempUnifyData) {
|
||||
app.showConfirmModal = true
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
|
||||
// 确认支付
|
||||
handleSubmit() {
|
||||
const app = this
|
||||
const {
|
||||
orderId
|
||||
} = app.order
|
||||
// 判断是否选择了支付方式
|
||||
if (!app.curPaymentItem) {
|
||||
app.$toast('您还没有选择支付方式')
|
||||
return
|
||||
}
|
||||
// 按钮禁用
|
||||
if (app.disabled) return
|
||||
// app.disabled = true
|
||||
// .js
|
||||
console.log("curPaymentItem: ",app.curPaymentItem);
|
||||
if(app.curPaymentItem.method == '余额支付'){
|
||||
balance(orderId).then(result => app.onSubmitCallback(result)).catch(err => app.$error(err.message))
|
||||
}
|
||||
if(app.curPaymentItem.method == '支付宝'){
|
||||
alipay(orderId).then(result => app.onSubmitCallback(result)).catch(err => app.$error(err.message))
|
||||
}
|
||||
|
||||
|
||||
|
||||
// // 提交到后端API
|
||||
// CashierApi.orderPay(app.orderId, {
|
||||
// method: app.curPaymentItem.method,
|
||||
// client: app.platform,
|
||||
// extra: app.getExtraAsUnify(app.curPaymentItem.method)
|
||||
// })
|
||||
// .then(result => app.onSubmitCallback(result))
|
||||
// .finally(err => setTimeout(() => app.disabled = false, 10))
|
||||
},
|
||||
|
||||
// 获取第三方支付的扩展参数
|
||||
getExtraAsUnify(method) {
|
||||
if (method === PayMethodEnum.ALIPAY.value) {
|
||||
return Alipay.extraAsUnify()
|
||||
}
|
||||
if (method === PayMethodEnum.WECHAT.value) {
|
||||
return Wechat.extraAsUnify()
|
||||
}
|
||||
return {}
|
||||
},
|
||||
|
||||
// 订单提交成功后回调
|
||||
onSubmitCallback(result) {
|
||||
const app = this
|
||||
const method = app.curPaymentItem.method
|
||||
const tradeNO = result.data
|
||||
console.log("result订单提交成功后回调: ", tradeNO);
|
||||
// 余额支付
|
||||
if (method === '余额支付') {
|
||||
app.onShowSuccess(result)
|
||||
}
|
||||
// 发起支付宝支付
|
||||
if (method === '支付宝') {
|
||||
my.tradePay({
|
||||
// 调用统一收单交易创建接口(alipay.trade.create),获得返回字段支付宝交易号trade_no
|
||||
tradeNO: tradeNO,
|
||||
success: (res) => {
|
||||
console.log("res11: ", res);
|
||||
if (res.resultCode == "9000") {
|
||||
payQuery(app.orderId).then(result => {
|
||||
if(result.code == 0) {
|
||||
app.$success('支付成功')
|
||||
app.$navTo('pages/order/index')
|
||||
}
|
||||
})
|
||||
|
||||
// setPayStatus({
|
||||
// orderId: app.orderId,
|
||||
// payStatus: 20,
|
||||
// payMethod: 20
|
||||
// }).then(res => {
|
||||
// console.log("updateOrder: ",res);
|
||||
// app.$navTo('pages/order/index')
|
||||
// })
|
||||
}
|
||||
if (res.resultCode == "6001") {
|
||||
app.$navTo('pages/order/index')
|
||||
}
|
||||
// my.alert({
|
||||
// content: JSON.stringify(res),
|
||||
// });
|
||||
},
|
||||
fail: (res) => {
|
||||
my.alert({
|
||||
content: JSON.stringify(res),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Alipay.payment(paymentData)
|
||||
// .then(res => app.onPaySuccess(res))
|
||||
// .catch(err => app.onPayFail(err))
|
||||
}
|
||||
// 发起微信支付
|
||||
if (method === PayMethodEnum.WECHAT.value) {
|
||||
console.log('paymentData', paymentData)
|
||||
Wechat.payment({
|
||||
orderKey: app.orderId,
|
||||
...paymentData
|
||||
})
|
||||
.then(res => app.onPaySuccess(res))
|
||||
.catch(err => app.onPayFail(err))
|
||||
}
|
||||
},
|
||||
|
||||
// 订单支付成功的回调方法
|
||||
// 这里只是前端支付api返回结果success,实际订单是否支付成功 以后端的查单和异步通知为准
|
||||
onPaySuccess({
|
||||
res,
|
||||
option: {
|
||||
isRequireQuery,
|
||||
outTradeNo,
|
||||
method
|
||||
}
|
||||
}) {
|
||||
const app = this
|
||||
// 判断是否需要主动查单
|
||||
// isRequireQuery为true代表需要主动查单
|
||||
if (isRequireQuery) {
|
||||
app.onTradeQuery(outTradeNo, method)
|
||||
return true
|
||||
}
|
||||
this.onShowSuccess(res)
|
||||
},
|
||||
|
||||
// 显示支付成功信息并页面跳转
|
||||
onShowSuccess({
|
||||
message
|
||||
}) {
|
||||
this.$toast(message || '订单支付成功')
|
||||
this.onSuccessNav()
|
||||
},
|
||||
|
||||
// 订单支付失败
|
||||
onPayFail(err) {
|
||||
console.log('onPayFail', err)
|
||||
const errMsg = err.message || '订单未支付'
|
||||
this.$error(errMsg)
|
||||
},
|
||||
|
||||
// 已完成支付按钮事件: 请求后端查单
|
||||
onTradeQuery(outTradeNo, method) {
|
||||
const app = this
|
||||
// 交易查询
|
||||
// 查询第三方支付订单是否付款成功
|
||||
CashierApi.tradeQuery({
|
||||
outTradeNo,
|
||||
method,
|
||||
client: app.platform
|
||||
})
|
||||
.then(result => result.data.isPay ? app.onShowSuccess(result) : app.onPayFail(result))
|
||||
.finally(() => app.showConfirmModal = false)
|
||||
},
|
||||
|
||||
// 支付成功后的跳转
|
||||
onSuccessNav() {
|
||||
// 相应全局事件订阅: 刷新上级页面数据
|
||||
uni.$emit('syncRefreshOrder', true)
|
||||
// 获取上级页面
|
||||
const pages = getCurrentPages()
|
||||
const lastPage = pages.length < 2 ? null : pages[pages.length - 2]
|
||||
const backRoutes = [
|
||||
'pages/order/index',
|
||||
'pages/order/detail'
|
||||
]
|
||||
setTimeout(() => {
|
||||
if (lastPage && inArray(lastPage.route, backRoutes)) {
|
||||
uni.navigateBack()
|
||||
} else {
|
||||
this.$navTo('pages/order/index', {}, 'redirectTo')
|
||||
}
|
||||
}, 1200)
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #F4F4F4;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
background-color: #F4F4F4;
|
||||
}
|
||||
|
||||
// 订单信息
|
||||
.order-info {
|
||||
padding: 80rpx 0;
|
||||
text-align: center;
|
||||
|
||||
.order-countdown {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.order-amount {
|
||||
margin: 0 auto;
|
||||
max-width: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fb0f07;
|
||||
|
||||
.unit {
|
||||
font-size: 30rpx;
|
||||
margin-bottom: -18rpx;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 56rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 支付方式
|
||||
.payment-method {
|
||||
width: 94%;
|
||||
margin: 0 auto 20rpx auto;
|
||||
padding: 0 40rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
|
||||
.pay-item {
|
||||
padding: 26rpx 0;
|
||||
font-size: 28rpx;
|
||||
border-bottom: 1rpx solid rgb(248, 248, 248);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-left_icon {
|
||||
margin-right: 20rpx;
|
||||
font-size: 44rpx;
|
||||
|
||||
&.wechat {
|
||||
color: #00c800;
|
||||
}
|
||||
|
||||
&.alipay {
|
||||
color: #009fe8;
|
||||
}
|
||||
|
||||
&.balance {
|
||||
color: #ff9700;
|
||||
}
|
||||
}
|
||||
|
||||
.item-left_text {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.user-balance {
|
||||
margin-left: 20rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 支付确认弹窗
|
||||
.modal-content {
|
||||
padding: 40rpx 48rpx;
|
||||
font-size: 30rpx;
|
||||
line-height: 50rpx;
|
||||
text-align: left;
|
||||
color: #606266;
|
||||
// height: 620rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
// 底部操作栏
|
||||
.footer-fixed {
|
||||
position: fixed;
|
||||
bottom: var(--window-bottom);
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 11;
|
||||
box-shadow: 0 -4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
|
||||
background: #fff;
|
||||
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.btn-wrapper {
|
||||
height: 120rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
height: 80rpx;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-item-main {
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,378 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="goods-info">
|
||||
<view class="goods">
|
||||
<image v-if="record.image" :src="record.image" class="equipment-image" mode="aspectFit"></image>
|
||||
<image v-else src="../../static/goods/battery.png" class="equipment-image" mode="aspectFit"></image>
|
||||
<view class="info">
|
||||
<text class="goods-name">{{ record.equipmentName }}</text>
|
||||
<text class="goods-desc">归属门店:{{ record.merchantName }}</text>
|
||||
<text class="goods-desc">电池型号:{{ record.batteryModel }}</text>
|
||||
<text class="selling-point">{{ record.sellingPoint }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="order">
|
||||
<block v-if="record.equipmentCategory === '10'">
|
||||
<view class="title">
|
||||
<text>合计总金额</text>
|
||||
<text>¥{{ record.batteryPrice + record.batteryInsurance }}元</text>
|
||||
</view>
|
||||
<!-- <u-cell-group>
|
||||
<u-cell-item title="电池租金" :border-bottom="false" hover-class="none" :value="record.batteryRent"></u-cell-item>
|
||||
<u-cell-item title="电池押金" :border-bottom="false" hover-class="none" :value="record.batteryDeposit"></u-cell-item>
|
||||
<u-cell-item title="电池保险" :border-bottom="false" hover-class="none" :value="record.batteryInsurance"></u-cell-item>
|
||||
</u-cell-group> -->
|
||||
</block>
|
||||
<block v-if="record.equipmentCategory === '20'">
|
||||
<view class="title">
|
||||
<text>合计总金额</text>
|
||||
<text>¥{{ (record.downPayment).toFixed(2) }}元</text>
|
||||
</view>
|
||||
<view class="fenqi">
|
||||
<view class="item">
|
||||
<view class="name">销售价格</view>
|
||||
¥{{ record.batteryPrice }} 元
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="name">首付款</view>
|
||||
¥{{ record.downPayment }} 元
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="name">余额分期期数</view>
|
||||
{{ record.periods }} 期
|
||||
<!-- <u-number-box :min="6" :max="36" :value="record.periods" :step="6" @change="onChangeStepper($event)" /> -->
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="name">每期还款</view>
|
||||
¥{{ record.repayment }} 元
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="name">每期手续费</view>
|
||||
¥{{ record.serviceCharges }} 元
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="name">电动车保险</view>
|
||||
<view>¥{{ record.batteryInsurance }}元</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<block v-if="record.equipmentCategory === '30'">
|
||||
<view class="title">
|
||||
<text>合计总金额</text>
|
||||
<text>¥{{ (record.batteryRent + record.batteryDeposit + record.batteryInsurance).toFixed(2) }}元</text>
|
||||
</view>
|
||||
<u-cell-group>
|
||||
<u-cell-item :title="`租金(按${record.periodsType == 0 ? '周' : '月'})`" :border-bottom="false" hover-class="none" :value="record.batteryRent">元</u-cell-item>
|
||||
<u-cell-item title="分期期数" :border-bottom="false" hover-class="none" :value="record.periods">期</u-cell-item>
|
||||
<u-cell-item title="押金" :border-bottom="false" hover-class="none" :value="record.batteryDeposit">元</u-cell-item>
|
||||
<u-cell-item title="保险" :border-bottom="false" hover-class="none" :value="record.batteryInsurance">元</u-cell-item>
|
||||
</u-cell-group>
|
||||
</block>
|
||||
<block v-if="record.equipmentCategory === '40'">
|
||||
<view class="title">
|
||||
<text>合计总金额</text>
|
||||
<text>¥{{ (record.batteryRent + record.batteryDeposit + record.batteryInsurance).toFixed(2) }}元</text>
|
||||
</view>
|
||||
<view class="fenqi">
|
||||
<view class="item">
|
||||
<view class="name">电动车租金</view>
|
||||
¥{{ record.batteryRent }} 元
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="name">电动车押金</view>
|
||||
¥{{ record.batteryDeposit }} 元
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="name">电动车保险</view>
|
||||
{{ record.batteryInsurance }} 元
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<!-- <u-cell-group>
|
||||
<u-cell-item title="电动车租金" :border-bottom="false" hover-class="none" :value="`¥${record.batteryRent} 元`"></u-cell-item>
|
||||
<u-cell-item title="电动车押金" :border-bottom="false" hover-class="none" :value="record.batteryDeposit"></u-cell-item>
|
||||
<u-cell-item title="电动车保险" :border-bottom="false" hover-class="none" :value="record.batteryInsurance"></u-cell-item>
|
||||
</u-cell-group> -->
|
||||
</block>
|
||||
|
||||
<view class="xieyi">
|
||||
<checkbox-group @change="onAgree" style="display: flex; align-items: center;">
|
||||
<checkbox :checked="agree" />我已同意并阅读<div class="xieyi-text" @click="showXieyi">《服务协议》</div>与<div class="xieyi-text" @click="showXieyi">《隐私协议》</div>
|
||||
</checkbox-group>
|
||||
</view>
|
||||
</view>
|
||||
<view class="submit">
|
||||
<u-input v-model="dealerId" :disabled="disabled" placeholder="请输入推荐人用户ID" />
|
||||
</view>
|
||||
<view class="submit">
|
||||
<u-button type="primary" :disabled="!agree" @click="onBuy">立即下单</u-button>
|
||||
</view>
|
||||
<!-- <view class="submit">
|
||||
<u-button type="success" :disabled="agree" @click="copyCode(record.equipmentCode)">复制设备编号</u-button>
|
||||
</view> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { userId } from '@/config.js';
|
||||
import {
|
||||
getEquipmentGoods
|
||||
} from '@/websoft/api/equipment-goods.js'
|
||||
import { getUser } from '@/websoft/api/user.js'
|
||||
import { addOrder } from '@/websoft/api/order.js'
|
||||
import { dateFormat } from '@/utils/util.js'
|
||||
import { createOrderNo } from '@/utils/util.js'
|
||||
import store from '../../store';
|
||||
import {
|
||||
payQuery
|
||||
} from '@/websoft/api/payment.js'
|
||||
export default {
|
||||
components: {
|
||||
// Search
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
record: {},
|
||||
// 正在加载中
|
||||
isLoading: true,
|
||||
// 当前选择的设备ID
|
||||
goodsId: null,
|
||||
show: true,
|
||||
mode: 'range',
|
||||
month: 6,
|
||||
agree: false,
|
||||
dealerId: '',
|
||||
price: {
|
||||
batteryRent: 300,
|
||||
batteryDeposit: 300,
|
||||
batteryInsurance: 0
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
onLoad(option) {
|
||||
const app = this
|
||||
// 记录当前选择的门店ID
|
||||
app.goodsId = option.goodsId
|
||||
// 获取设备列表
|
||||
app.getEquipment()
|
||||
},
|
||||
onShow(){
|
||||
var time = new Date().getTime() + 60*60*1000*24
|
||||
const expirationTime = dateFormat('YYYY-mm-dd HH:MM:SS', new Date(time))
|
||||
console.log("time: ",time);
|
||||
console.log("expirationTime: ",expirationTime);
|
||||
payQuery(752).then(res => {
|
||||
console.log("res123: ",res);
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getEquipment() {
|
||||
const app = this
|
||||
const {
|
||||
goodsId
|
||||
} = this
|
||||
getEquipmentGoods(goodsId).then(res => {
|
||||
app.record = res.data
|
||||
console.log("res: ", app.record);
|
||||
})
|
||||
},
|
||||
onChangeStepper({ value }) {
|
||||
this.month = value
|
||||
},
|
||||
change(e) {
|
||||
console.log(e);
|
||||
},
|
||||
onInput(month) {
|
||||
this.month = month
|
||||
},
|
||||
onAgree(){
|
||||
this.agree = !this.agree
|
||||
},
|
||||
showXieyi(){
|
||||
this.$navTo('pages/help/xieyi')
|
||||
},
|
||||
copyCode(text){
|
||||
// #ifndef H5
|
||||
console.log("text: ",text);
|
||||
uni.setClipboardData({
|
||||
text: text,
|
||||
success: (result) => {
|
||||
this.$success("复制成功")
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
let textarea = document.createElement("textarea")
|
||||
textarea.value = text
|
||||
textarea.readOnly = "readOnly"
|
||||
document.body.appendChild(textarea)
|
||||
textarea.select() // 选中文本内容
|
||||
textarea.setSelectionRange(0, info.length)
|
||||
uni.showToast({ //提示
|
||||
title: '复制成功'
|
||||
})
|
||||
result = document.execCommand("copy")
|
||||
textarea.remove()
|
||||
// #endif
|
||||
// copy() {
|
||||
// let result
|
||||
// // #ifndef H5
|
||||
// //uni.setClipboardData方法就是讲内容复制到粘贴板
|
||||
// uni.setClipboardData({
|
||||
// data: this.downUrl.url, //要被复制的内容
|
||||
// success: () => { //复制成功的回调函数
|
||||
// uni.showToast({ //提示
|
||||
// title: '复制成功'
|
||||
// })
|
||||
// }
|
||||
// });
|
||||
// // #endif
|
||||
|
||||
|
||||
// }
|
||||
},
|
||||
onBuy(){
|
||||
const app = this
|
||||
const { record,dealerId } = this
|
||||
let total = 0.0
|
||||
// 未登录状态
|
||||
if(!uni.getStorageSync('userId')) {
|
||||
app.$error('请先登录',function(){
|
||||
app.$navTo('pages/login/login');
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if(record.equipmentCategory == '10'){
|
||||
total = (record.batteryPrice).toFixed(2)
|
||||
}
|
||||
if(record.equipmentCategory == '20'){
|
||||
total = (record.downPayment).toFixed(2)
|
||||
}
|
||||
if(record.equipmentCategory == '40' || record.equipmentCategory == '30'){
|
||||
total = (record.batteryDeposit + record.batteryRent + record.batteryInsurance).toFixed(2)
|
||||
}
|
||||
const testTotal = 0.01
|
||||
var time = new Date().getTime() + 60*60*1000*24
|
||||
const expirationTime = dateFormat('YYYY-mm-dd HH:MM:SS', new Date(time))
|
||||
app.expirationTime = expirationTime
|
||||
addOrder({
|
||||
orderNo: createOrderNo(),
|
||||
merchantCode: record.merchantCode,
|
||||
goodsId: app.goodsId,
|
||||
totalPrice: total,
|
||||
orderPrice: total,
|
||||
payPrice: total,
|
||||
month: app.month,
|
||||
expirationTime: expirationTime,
|
||||
batteryRent: record.batteryRent,
|
||||
batteryDeposit: record.batteryDeposit,
|
||||
batteryInsurance: record.batteryInsurance,
|
||||
orderSource: record.equipmentCategory,
|
||||
orderSourceId: record.goodsId,
|
||||
dealerId
|
||||
}).then(res => {
|
||||
const { orderId } = res.data
|
||||
app.$success("下单成功")
|
||||
setTimeout(() => {
|
||||
this.$navTo('pages/checkout/cashier/index', { orderId })
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-info {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
width: 700rpx;
|
||||
margin: 20rpx auto;
|
||||
|
||||
.goods {
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
|
||||
image {
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
margin: 20rpx;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20rpx;
|
||||
|
||||
.goods-name {
|
||||
font-size: 34rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.goods-desc {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
.selling-point{
|
||||
padding: 5px 0;
|
||||
color: #e6760e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.between-time {
|
||||
width: 500rpx;
|
||||
margin: auto;
|
||||
padding-bottom: 20rpx;
|
||||
text-align: center;
|
||||
|
||||
.select-slider {
|
||||
line-height: 2em;
|
||||
color: #e6760e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.order {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
width: 700rpx;
|
||||
margin: 20rpx auto;
|
||||
padding: 10rpx 0;
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 30rpx;
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.xieyi {
|
||||
padding: 20rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.submit {
|
||||
border-radius: 12rpx;
|
||||
width: 700rpx;
|
||||
margin: 20rpx auto;
|
||||
padding: 10rpx 0;
|
||||
}
|
||||
.xieyi-text{
|
||||
color: #0000ff;
|
||||
}
|
||||
.fenqi{
|
||||
padding: 30rpx;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
.item{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+601
@@ -0,0 +1,601 @@
|
||||
<template>
|
||||
<view class="container p-bottom" :style="appThemeStyle">
|
||||
<view v-if="order.goodsList.length">
|
||||
<!-- 实物订单:选择配送方式 -->
|
||||
<block v-if="order.orderType == OrderTypeEnum.PHYSICAL.value">
|
||||
<!-- 配送方式选项卡 -->
|
||||
<view v-if="isShowTab" class="swiper-tab dis-flex flex-y-center flex-x-around">
|
||||
<view class="swiper-tab-item" :class="{ on: curDelivery == DeliveryTypeEnum.EXPRESS.value }"
|
||||
@click="handleSwichDelivery(DeliveryTypeEnum.EXPRESS.value)">
|
||||
<text>快递配送</text>
|
||||
</view>
|
||||
<view class="swiper-tab-item" :class="{ on: curDelivery == DeliveryTypeEnum.EXTRACT.value }"
|
||||
@click="handleSwichDelivery(DeliveryTypeEnum.EXTRACT.value)">
|
||||
<text>上门自提</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快递配送:配送地址 -->
|
||||
<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>
|
||||
|
||||
<!-- 上门自提:自提门店 -->
|
||||
<block v-if="curDelivery == DeliveryTypeEnum.EXTRACT.value">
|
||||
<view class="flow-delivery" @click="onSelectExtractPoint()">
|
||||
<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.extractShop.shop_id">
|
||||
<view class="detail-content__title dis-flex">
|
||||
<text class="f-30">{{ order.extractShop.shop_name }}</text>
|
||||
</view>
|
||||
<view class="detail-content__describe">
|
||||
<text class="col-7">{{ order.extractShop.region.province }} {{ order.extractShop.region.city }}</text>
|
||||
<text class="col-7">{{ order.extractShop.region.region }} {{ order.extractShop.address }}</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 user-orderJtou"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 自提联系方式 -->
|
||||
<view class="flow-extract-contact b-f">
|
||||
<view class="contact-item dis-flex">
|
||||
<view class="item_label dis-flex flex-x-end flex-y-center">
|
||||
<text>联系人:</text>
|
||||
</view>
|
||||
<view class="item_ipt flex-box dis-flex flex-y-center">
|
||||
<input placeholder="请填写联系人姓名" v-model="linkman"></input>
|
||||
</view>
|
||||
</view>
|
||||
<view class="contact-item dis-flex">
|
||||
<view class="item_label dis-flex flex-x-end flex-y-center">
|
||||
<text>联系电话:</text>
|
||||
</view>
|
||||
<view class="item_ipt flex-box dis-flex flex-y-center">
|
||||
<input placeholder="请填写联系电话" v-model="phone"></input>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<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> -->
|
||||
<text>共{{ order.orderTotalNum }}件商品</text>
|
||||
</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="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>
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as Verify from '@/utils/verify'
|
||||
import * as CheckoutApi from '@/api/checkout'
|
||||
import * as SharpCheckoutApi from '@/api/sharp/checkout'
|
||||
import * as BargainCheckoutApi from '@/api/bargain/checkout'
|
||||
import * as GrouponCheckoutApi from '@/api/groupon/checkout'
|
||||
import { CouponTypeEnum } from '@/common/enum/coupon'
|
||||
import { OrderTypeEnum, DeliveryTypeEnum } from '@/common/enum/order'
|
||||
|
||||
const CouponColors = ['red', 'blue', 'violet', 'yellow']
|
||||
|
||||
// 根据指定mode获取对应的api类
|
||||
const getCheckoutApi = (mode) => {
|
||||
const apiEnum = {
|
||||
buyNow: CheckoutApi,
|
||||
cart: CheckoutApi,
|
||||
bargain: BargainCheckoutApi,
|
||||
sharp: SharpCheckoutApi,
|
||||
groupon: GrouponCheckoutApi
|
||||
}
|
||||
return apiEnum[mode]
|
||||
}
|
||||
|
||||
// 根据指定mode获取param
|
||||
const getModeParam = (mode, options) => {
|
||||
const param = {}
|
||||
// 结算模式: 立即购买
|
||||
if (mode === 'buyNow') {
|
||||
param.goodsId = options.goodsId
|
||||
param.goodsNum = options.goodsNum
|
||||
param.goodsSkuId = options.goodsSkuId
|
||||
}
|
||||
// 结算模式: 购物车
|
||||
if (mode === 'cart') {
|
||||
param.cartIds = options.cartIds
|
||||
}
|
||||
// 结算模式: 砍价活动
|
||||
if (mode === 'bargain') {
|
||||
param.taskId = options.taskId
|
||||
param.goodsSkuId = options.goodsSkuId
|
||||
}
|
||||
// 结算模式: 整点秒杀
|
||||
if (mode === 'sharp') {
|
||||
param.activeTimeId = options.activeTimeId
|
||||
param.sharpGoodsId = options.sharpGoodsId
|
||||
param.goodsSkuId = options.goodsSkuId
|
||||
param.goodsNum = options.goodsNum
|
||||
}
|
||||
// 结算模式: 多人拼团
|
||||
if (mode === 'groupon') {
|
||||
param.grouponGoodsId = options.grouponGoodsId
|
||||
param.taskId = options.taskId
|
||||
param.goodsSkuId = options.goodsSkuId
|
||||
param.goodsNum = options.goodsNum
|
||||
param.stepPeople = options.stepPeople
|
||||
}
|
||||
return param
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 枚举类
|
||||
OrderTypeEnum,
|
||||
DeliveryTypeEnum,
|
||||
CouponTypeEnum,
|
||||
// 当前页面参数
|
||||
options: {},
|
||||
// 配送方式
|
||||
isShowTab: false,
|
||||
DeliveryTypeEnum,
|
||||
curDelivery: null,
|
||||
// 自提信息
|
||||
selectedShopId: 0, // 选择的门店ID
|
||||
linkman: '', // 自提联系人
|
||||
phone: '', // 自提联系电话
|
||||
// 优惠券颜色组
|
||||
CouponColors,
|
||||
// 选择的优惠券
|
||||
selectCouponId: 0,
|
||||
// 是否使用积分抵扣
|
||||
isUsePoints: false,
|
||||
// 买家留言
|
||||
remark: '',
|
||||
// 禁用submit按钮
|
||||
disabled: false,
|
||||
// 是否显示积分说明
|
||||
showPoints: false,
|
||||
// 是否显示优惠券弹窗
|
||||
showPopup: false,
|
||||
// 订单信息 (从后端api中获取)
|
||||
order: {
|
||||
// 商品列表
|
||||
goodsList: [],
|
||||
// 优惠券列表
|
||||
couponList: [],
|
||||
// 是否存在收货地址
|
||||
existAddress: false,
|
||||
// 默认收货地址
|
||||
address: null,
|
||||
// 是否存在收货地址
|
||||
existAddress: false,
|
||||
// 当前用户收货城市是否存在配送规则中
|
||||
isIntraRegion: true,
|
||||
// 是否存在错误
|
||||
hasError: false,
|
||||
// 错误信息
|
||||
errorMsg: '',
|
||||
},
|
||||
// 个人信息
|
||||
personal: {},
|
||||
// 商城设置
|
||||
setting: {}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
this.options = options
|
||||
// 注册全局事件订阅: 选择自提门店
|
||||
uni.$on('syncSelectedId', selectedId => {
|
||||
this.selectedShopId = selectedId
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面的卸载
|
||||
*/
|
||||
onUnload() {
|
||||
// 卸载全局事件订阅: 选择自提门店
|
||||
uni.$off('syncSelectedId')
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
// 获取当前订单信息
|
||||
this.getOrderData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取订单数据
|
||||
getOrderData() {
|
||||
const app = this
|
||||
const { options: { mode } } = app
|
||||
// 请求的参数
|
||||
const params = app.getRequestParam()
|
||||
// 请求api
|
||||
getCheckoutApi(mode)
|
||||
.order(mode, params)
|
||||
.then(result => app.initData(result.data))
|
||||
.catch(err => err)
|
||||
},
|
||||
|
||||
// 初始化数据
|
||||
initData({ order, setting, personal }) {
|
||||
const app = this
|
||||
app.order = order
|
||||
app.personal = personal
|
||||
app.setting = setting
|
||||
// 显示错误信息
|
||||
if (order.hasError) {
|
||||
app.showToast(order.errorMsg, 3000)
|
||||
}
|
||||
// 当前选择的配送方式
|
||||
app.curDelivery = order.delivery
|
||||
// 如果只有一种配送方式则不显示选项卡
|
||||
app.isShowTab = setting.deliveryType.length > 1
|
||||
// 上门自提联系信息
|
||||
if (app.linkman === '') {
|
||||
app.linkman = order.lastExtract.linkman
|
||||
}
|
||||
if (app.phone === '') {
|
||||
app.phone = order.lastExtract.phone
|
||||
}
|
||||
},
|
||||
|
||||
// 获取api请求的参数
|
||||
getRequestParam() {
|
||||
const app = this
|
||||
const { options } = app
|
||||
// 结算模式的固定参数
|
||||
const modeParam = getModeParam(options.mode, options)
|
||||
// 订单结算参数(用户选择)
|
||||
const orderParam = {
|
||||
delivery: app.curDelivery || 0,
|
||||
shopId: app.selectedShopId || 0,
|
||||
couponId: app.selectCouponId || 0,
|
||||
isUsePoints: app.isUsePoints ? 1 : 0,
|
||||
}
|
||||
return { ...orderParam, ...modeParam }
|
||||
},
|
||||
|
||||
// 切换配送方式
|
||||
handleSwichDelivery(key) {
|
||||
this.curDelivery = key
|
||||
this.getOrderData()
|
||||
},
|
||||
|
||||
// 显示积分说明
|
||||
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.showToast(couponItem.not_apply_info)
|
||||
return
|
||||
}
|
||||
// 记录选中的优惠券id
|
||||
app.selectCouponId = couponItem.user_coupon_id
|
||||
// 重新获取订单信息
|
||||
app.getOrderData()
|
||||
// 隐藏优惠券弹层
|
||||
app.showPopup = false
|
||||
},
|
||||
|
||||
// 不使用优惠券
|
||||
handleNotUseCoupon() {
|
||||
const app = this
|
||||
app.selectCouponId = 0
|
||||
// 重新获取订单信息
|
||||
app.getOrderData()
|
||||
// 隐藏优惠券弹层
|
||||
app.showPopup = false
|
||||
},
|
||||
|
||||
// 快递配送:选择收货地址
|
||||
onSelectAddress() {
|
||||
this.$navTo('pages/address/index', { from: 'checkout' })
|
||||
},
|
||||
|
||||
// 上门自提:选择自提点
|
||||
onSelectExtractPoint() {
|
||||
this.$navTo('pages/shop/extract', { selectedId: this.selectedShopId })
|
||||
},
|
||||
|
||||
// 跳转到商品详情页
|
||||
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
|
||||
getCheckoutApi(app.options.mode)
|
||||
.submit(app.options.mode, app.getFormData())
|
||||
.then(result => {
|
||||
// 订单创建成功: 跳转到订单支付页
|
||||
const orderId = result.data.orderId
|
||||
setTimeout(() => {
|
||||
this.$navTo('pages/checkout/cashier/index', { orderId }, 'redirectTo')
|
||||
}, 100)
|
||||
})
|
||||
.catch(res => app.showToast(res.errMsg, 3000))
|
||||
.finally(() => setTimeout(() => app.disabled = false, 800))
|
||||
},
|
||||
|
||||
// 跳转到我的订单(等待1秒)
|
||||
navToMyOrder() {
|
||||
setTimeout(() => {
|
||||
this.$navTo('pages/order/index', {}, 'redirectTo')
|
||||
}, 1000)
|
||||
},
|
||||
|
||||
// 表单提交的数据
|
||||
getFormData() {
|
||||
const app = this
|
||||
const { options } = app
|
||||
// 表单数据
|
||||
const form = {
|
||||
delivery: app.curDelivery,
|
||||
couponId: app.selectCouponId || 0,
|
||||
shopId: app.selectedShopId || 0,
|
||||
linkman: app.linkman,
|
||||
phone: app.phone,
|
||||
isUsePoints: app.isUsePoints ? 1 : 0,
|
||||
remark: app.remark || '',
|
||||
}
|
||||
// 获取不同模式的参数
|
||||
const modeParam = getModeParam(options.mode, options)
|
||||
return { ...form, ...modeParam }
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
onVerifyFrom() {
|
||||
const app = this
|
||||
if (app.hasError) {
|
||||
app.showToast(app.errorMsg, 3000)
|
||||
return false
|
||||
}
|
||||
// 验证自提填写的联系方式
|
||||
if (app.curDelivery == DeliveryTypeEnum.EXTRACT.value) {
|
||||
app.linkman = app.linkman.trim()
|
||||
app.phone = app.phone.trim()
|
||||
if (app.selectedShopId <= 0) {
|
||||
app.showToast('请选择自提的门店')
|
||||
return false
|
||||
}
|
||||
if (Verify.isEmpty(app.linkman)) {
|
||||
app.showToast('请填写自提联系人')
|
||||
return false
|
||||
}
|
||||
if (Verify.isEmpty(app.phone)) {
|
||||
app.showToast('请填写自提联系电话')
|
||||
return false
|
||||
}
|
||||
if (!Verify.isPhone(app.phone)) {
|
||||
app.showToast('请输入正确的联系电话')
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 显示toast信息
|
||||
showToast(title, duration = 2000) {
|
||||
this.$refs.uToast.show({ title, duration })
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
</style>
|
||||
Executable
+475
@@ -0,0 +1,475 @@
|
||||
// 配送信息
|
||||
.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;
|
||||
margin-bottom: 25rpx;
|
||||
|
||||
.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: $main-bg;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.goods-name{
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 优惠券选择 */
|
||||
.popup__coupon {
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
padding: 30rpx;
|
||||
|
||||
.coupon__do_not {
|
||||
.control {
|
||||
width: 90%;
|
||||
height: 72rpx;
|
||||
color: #888;
|
||||
border: 1rpx solid #e3e3e3;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon__title {
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.coupon-list {
|
||||
/* #ifdef H5 */
|
||||
max-width: 1120rpx;
|
||||
margin: 0 auto;
|
||||
/* #endif */
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.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;
|
||||
border: 1rpx solid #fff;
|
||||
border-radius: 30rpx;
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 商品规格
|
||||
.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, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
text-align: center;
|
||||
line-height: 92rpx;
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 积分说明
|
||||
.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: $main-bg;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 选项卡:配送方式
|
||||
.swiper-tab {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
height: 85rpx;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid rgb(248, 248, 248);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
|
||||
.swiper-tab-item {
|
||||
width: 35%;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #777;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 2px solid #ffffff00;
|
||||
|
||||
&.on {
|
||||
color: $main-bg;
|
||||
border-bottom: 2px solid $main-bg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 门店自提联系人
|
||||
.flow-extract-contact {
|
||||
padding: 8rpx 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: #444;
|
||||
margin-bottom: 25rpx;
|
||||
|
||||
.contact-item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid rgb(248, 248, 248);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.item_label {
|
||||
margin-right: 26rpx;
|
||||
width: 150rpx;
|
||||
}
|
||||
|
||||
.item_ipt input {
|
||||
font-size: 28rpx;
|
||||
width: 100%;
|
||||
|
||||
.input-placeholder {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+276
@@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<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="appTheme.mainBg" :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>
|
||||
</view>
|
||||
</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>
|
||||
Executable
+251
@@ -0,0 +1,251 @@
|
||||
<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;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 30rpx;
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&.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>
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<!-- 店铺页面组件 -->
|
||||
<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>
|
||||
Executable
+316
@@ -0,0 +1,316 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container b-f">
|
||||
|
||||
<!-- 头部背景图 -->
|
||||
<view class="dealer-bg">
|
||||
<image class="image" mode="widthFix" :src="background"></image>
|
||||
</view>
|
||||
|
||||
<!-- 等待审核 -->
|
||||
<view v-if="isApplying" class="dealer-boot dis-flex flex-dir-column flex-y-center">
|
||||
<view class="boot__msg f-30 dis-flex flex-dir-column flex-y-center">
|
||||
<text class="msg__icon iconfont icon-shenhezhong"></text>
|
||||
<text class="msg__content m-top20 f-29 col-80">{{ words.wait_audit.value }}</text>
|
||||
</view>
|
||||
<!-- 去商城逛逛 -->
|
||||
<view class="boot__submit form-submit dis-flex flex-x-center">
|
||||
<button class="button" @click="$navTo('pages/index/index')">{{ words.goto_mall.value }}</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 填写申请信息 -->
|
||||
<view v-else class="dis-flex flex-dir-column flex-y-center">
|
||||
<view class="widget-form b-f m-top20 dis-flex flex-dir-column">
|
||||
<view class="form-title f-30">{{ words.title.value }}</view>
|
||||
<view class="form-box dis-flex flex-dir-column">
|
||||
<form @submit="handleSubmit">
|
||||
<view class="form-field dis-flex flex-y-center">
|
||||
<view class="field-label">邀请人</view>
|
||||
<view class="field-input">
|
||||
<text>{{ refereeName }}(请核对)</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-field dis-flex flex-y-center">
|
||||
<view class="field-label">姓名</view>
|
||||
<view class="field-input flex-box">
|
||||
<input class="input" name="name" placeholder="请输入真实姓名"></input>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-field dis-flex flex-y-center">
|
||||
<view class="field-label">手机号</view>
|
||||
<view class="field-input flex-box">
|
||||
<input class="input" name="mobile" placeholder="请输入手机号"></input>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 申请协议 -->
|
||||
<view class="form-license dis-flex flex-x-center flex-y-center">
|
||||
<view class="license-radio dis-flex flex-y-center" @click="isRead = !isRead">
|
||||
<text class="license-icon f-38 iconfont icon-radio" :class="[isRead ? 'c-violet' : 'col-bb']"></text>
|
||||
<text class="f-28 col-80">我已阅读并了解</text>
|
||||
</view>
|
||||
<text @click="handleShowLicense()" class="f-28 c-violet">【{{ words.license.value }}】</text>
|
||||
</view>
|
||||
<!-- 立即申请 -->
|
||||
<view class="form-submit dis-flex flex-x-center">
|
||||
<button formType="submit" :disabled="disabled">{{ words.submit.value }}</button>
|
||||
</view>
|
||||
</form>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 入驻协议弹窗 -->
|
||||
<u-modal v-model="showLicense" title="申请协议">
|
||||
<scroll-view class="pops-content" :scroll-y="true">
|
||||
<text>{{ license }}</text>
|
||||
</scroll-view>
|
||||
</u-modal>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as ApplyApi from '@/api/dealer/apply'
|
||||
import SettingModel from '@/common/model/dealer/Setting'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 当前是否为分销商
|
||||
isDealer: undefined,
|
||||
// 当前是否在申请中
|
||||
isApplying: undefined,
|
||||
// 推荐人昵称
|
||||
refereeName: undefined,
|
||||
// 文字设置
|
||||
words: undefined,
|
||||
// 背景图
|
||||
background: undefined,
|
||||
// 入驻协议
|
||||
license: undefined,
|
||||
// 入驻协议阅读状态
|
||||
isRead: false,
|
||||
// 显示入驻协议弹窗
|
||||
showLicense: false,
|
||||
// 按钮禁用
|
||||
disabled: false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
this.getSetting()
|
||||
this.getApplyStatus()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取分销设置
|
||||
getSetting() {
|
||||
const app = this
|
||||
SettingModel.data()
|
||||
.then(setting => {
|
||||
// 赋值属性
|
||||
app.words = setting.words.apply.words
|
||||
app.background = setting.background.apply
|
||||
app.license = setting.license.license
|
||||
// 设置当前页面标题
|
||||
app.setPageTitle(setting.words.apply.title)
|
||||
})
|
||||
},
|
||||
|
||||
// 分销商申请状态
|
||||
getApplyStatus() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
ApplyApi.status()
|
||||
.then(result => {
|
||||
const data = result.data
|
||||
app.isDealer = data.isDealer
|
||||
app.isApplying = data.isApplying
|
||||
app.refereeName = data.refereeName
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 设置当前页面标题
|
||||
setPageTitle(title) {
|
||||
uni.setNavigationBarTitle({ title: title.value })
|
||||
},
|
||||
|
||||
// 切换支付选项
|
||||
handleChecked(value) {
|
||||
this.payment = value
|
||||
},
|
||||
|
||||
// 显示入驻协议弹窗
|
||||
handleShowLicense() {
|
||||
this.showLicense = true
|
||||
},
|
||||
|
||||
// 表单提交
|
||||
handleSubmit({ detail }) {
|
||||
const app = this
|
||||
// 表单验证
|
||||
if (!app.onValidation(detail.value)) {
|
||||
return false
|
||||
}
|
||||
// 确认提交
|
||||
app.disabled = true
|
||||
ApplyApi.submit({ form: detail.value })
|
||||
.then(result => {
|
||||
app.$toast(result.message)
|
||||
setTimeout(() => uni.navigateBack(), 1200)
|
||||
})
|
||||
.finally(() => app.disabled = false)
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
onValidation(data) {
|
||||
const app = this
|
||||
// 验证可提现佣金
|
||||
if (!data.name) {
|
||||
app.$error('请填写姓名')
|
||||
return false
|
||||
}
|
||||
// 验证手机号
|
||||
if (!/^\+?\d[\d -]{8,12}\d/.test(data.mobile)) {
|
||||
app.$error('手机号格式不正确')
|
||||
return false
|
||||
}
|
||||
// 验证是否阅读协议
|
||||
if (!app.isRead) {
|
||||
app.$error('请先阅读分销商申请协议')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.c-violet {
|
||||
color: #786cff;
|
||||
}
|
||||
|
||||
.col-80 {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.col-bb {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.f-38 {
|
||||
font-size: 38rpx;
|
||||
}
|
||||
|
||||
.dealer-bg {
|
||||
.image {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.widget-form {
|
||||
position: relative;
|
||||
width: 700rpx;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1rpx 20rpx rgba(0, 0, 0, 0.21);
|
||||
border-radius: 12rpx;
|
||||
margin-top: -80rpx;
|
||||
|
||||
.form-title {
|
||||
padding: 0 40rpx;
|
||||
height: 90rpx;
|
||||
border-bottom: 1rpx solid #e7e7e7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-box {
|
||||
padding: 40rpx 35rpx;
|
||||
|
||||
.form-field {
|
||||
height: 80rpx;
|
||||
margin-bottom: 24rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
background-color: #f9f9f9;
|
||||
box-sizing: border-box;
|
||||
font-size: 28rpx;
|
||||
|
||||
.field-label {
|
||||
width: 130rpx;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.input {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-license {
|
||||
.license-icon {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.form-submit {
|
||||
margin-top: 40rpx;
|
||||
|
||||
button {
|
||||
font-size: 30rpx;
|
||||
background: #786cff;
|
||||
border: 1rpx solid #786cff;
|
||||
color: white;
|
||||
border-radius: 50rpx;
|
||||
padding: 0 120rpx;
|
||||
|
||||
&[disabled] {
|
||||
background: #8e84fc;
|
||||
border-color: #8e84fc;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 申请协议
|
||||
.pops-content {
|
||||
padding: 30rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 50rpx;
|
||||
text-align: left;
|
||||
color: #606266;
|
||||
min-height: 320rpx;
|
||||
max-height: 640rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// 等待审核
|
||||
.dealer-boot {
|
||||
padding: 10rpx 30rpx;
|
||||
margin-top: 80rpx;
|
||||
|
||||
.msg__icon {
|
||||
font-size: 120rpx;
|
||||
color: #8e84fc;
|
||||
}
|
||||
|
||||
.boot__submit {
|
||||
margin-top: 60rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+309
@@ -0,0 +1,309 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container b-f">
|
||||
|
||||
<!-- 分销商中心 -->
|
||||
<view class="center" v-if="isDealer">
|
||||
|
||||
<!-- 头部背景图 -->
|
||||
<view class="dealer-bg">
|
||||
<image class="image" mode="widthFix" :src="setting.background"></image>
|
||||
</view>
|
||||
|
||||
<!-- 内容区 -->
|
||||
<view class="widget-body b-f dis-flex flex-dir-column flex-y-center">
|
||||
<!-- 用户信息 -->
|
||||
<view class="widget widget__base m-top20 b-f dis-flex flex-dir-column">
|
||||
<view class="base__user f-30">
|
||||
<!-- 用户头像 -->
|
||||
<avatar-image class="user-avatar" :url="user.avatar_url" :width="150" :borderWidth="4"
|
||||
:borderColor="`#fff`" />
|
||||
<view class="user-nickName f-32">{{ user.nick_name }}</view>
|
||||
<view class="user-referee f-24 col-9">
|
||||
{{ setting.words.index.words.referee.value }}:{{ refereeName }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="base__capital dis-flex flex-dir-column">
|
||||
<!-- 佣金卡片 -->
|
||||
<view class="capital-card dis-flex">
|
||||
<view class="card-left">
|
||||
<view class="f-28 col-f">
|
||||
<text space="ensp">{{ setting.words.index.words.money.value }} {{ dealer.money }}</text>
|
||||
<text class="m-l-10">元</text>
|
||||
</view>
|
||||
<view class="f-28 col-f">
|
||||
<text space="ensp">{{ setting.words.index.words.freeze_money.value }} {{ dealer.freeze_money }}</text>
|
||||
<text class="m-l-10">元</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-right flex-box dis-flex flex-x-end flex-y-center">
|
||||
<view class="withdraw-btn f-26" @click="$navTo('pages/dealer/withdraw/apply')">
|
||||
{{ setting.words.index.words.withdraw.value }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 已提现金额 -->
|
||||
<view class="capital-already clear">
|
||||
<view class="already-left f-26 fl">{{ setting.words.index.words.total_money.value }}</view>
|
||||
<view class="already-right f-26 fr">{{ dealer.total_money }}元</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作列表 -->
|
||||
<view class="widget widget__operat clear b-f">
|
||||
<view class="operat__item" @click="$navTo('pages/dealer/withdraw/list')">
|
||||
<view class="item__icon">
|
||||
<text class="iconfont icon-zhangben" style="color:#F9BA21;"></text>
|
||||
</view>
|
||||
<view class="item__text f-26">{{ setting.words.withdraw_list.title.value }}</view>
|
||||
</view>
|
||||
<view class="operat__item" @click="$navTo('pages/dealer/order')">
|
||||
<view class="item__icon">
|
||||
<text class="iconfont icon-dingdan" style="color:#FF7575;"></text>
|
||||
</view>
|
||||
<view class="item__text f-26">{{ setting.words.order.title.value }}</view>
|
||||
</view>
|
||||
<view class="operat__item" @click="$navTo('pages/dealer/team')">
|
||||
<view class="item__icon">
|
||||
<text class="iconfont icon-tuandui" style="color:#59C78E;"></text>
|
||||
</view>
|
||||
<view class="item__text f-26">{{ setting.words.team.title.value }}</view>
|
||||
</view>
|
||||
<view class="operat__item" @click="$navTo('pages/dealer/poster')">
|
||||
<view class="item__icon">
|
||||
<text class="iconfont icon-erweima" style="color:#5fa5ff;"></text>
|
||||
</view>
|
||||
<view class="item__text f-26">{{ setting.words.poster.title.value }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 当前不是分销商 -->
|
||||
<view class="container b-f" v-if="!isDealer">
|
||||
<view class="no-dealer">
|
||||
<view class="no-icon dis-flex flex-x-center">
|
||||
<image src="/static/not-dealer.png"></image>
|
||||
</view>
|
||||
<view class="no-msg dis-flex flex-x-center f-30">{{ setting.words.index.words.not_dealer.value }}
|
||||
</view>
|
||||
<!-- 立即申请 -->
|
||||
<view class="no-submit form-submit">
|
||||
<view class="button" @click="$navTo('pages/dealer/apply')">{{ setting.words.index.words.apply_now.value }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AvatarImage from '@/components/avatar-image'
|
||||
import * as Api from '@/api/dealer'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AvatarImage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 当前用户信息
|
||||
user: undefined,
|
||||
// 当前是否为分销商
|
||||
isDealer: false,
|
||||
// 当前分销商信息
|
||||
dealer: undefined,
|
||||
// 推荐人昵称
|
||||
refereeName: undefined,
|
||||
// 分销设置
|
||||
setting: {
|
||||
background: undefined,
|
||||
words: undefined
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onShow(options) {
|
||||
this.getCenter()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取分销商中心数据
|
||||
getCenter() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Api.center()
|
||||
.then(result => {
|
||||
// api数据赋值
|
||||
const data = result.data
|
||||
app.isDealer = data.isDealer
|
||||
app.user = data.user
|
||||
app.dealer = data.dealer
|
||||
app.refereeName = data.refereeName
|
||||
app.setting = data.setting
|
||||
// 设置当前页面标题
|
||||
app.setPageTitle()
|
||||
app.isLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 设置当前页面标题
|
||||
setPageTitle() {
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.setting.words.index.title.value
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.dealer-bg {
|
||||
.image {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.widget-body {
|
||||
position: relative;
|
||||
|
||||
.widget {
|
||||
width: 88%;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.11);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.widget__base {
|
||||
margin-top: -60rpx;
|
||||
|
||||
.base__user {
|
||||
position: relative;
|
||||
padding: 15rpx 40rpx;
|
||||
border-bottom: 1rpx solid #e7e7e7;
|
||||
|
||||
.user-avatar {
|
||||
position: absolute;
|
||||
top: -75rpx;
|
||||
right: 60rpx;
|
||||
}
|
||||
|
||||
.user-nickName {
|
||||
margin-top: 30rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.base__capital {
|
||||
padding: 35rpx;
|
||||
|
||||
.capital-card {
|
||||
height: 200rpx;
|
||||
padding: 36rpx 0;
|
||||
background-color: #8e84fc;
|
||||
border-radius: 10rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.card-left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
padding-left: 32rpx;
|
||||
}
|
||||
|
||||
.card-right {
|
||||
.withdraw-btn {
|
||||
width: 130rpx;
|
||||
height: 50rpx;
|
||||
background: #fff;
|
||||
color: #8e84fc;
|
||||
border-radius: 25rpx;
|
||||
margin-right: 32rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.capital-already {
|
||||
padding: 20rpx;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 操作列表 */
|
||||
.widget__operat {
|
||||
padding: 50rpx;
|
||||
margin-top: 40rpx;
|
||||
|
||||
.operat__item {
|
||||
width: 33.33333%;
|
||||
float: left;
|
||||
margin-bottom: 50rpx;
|
||||
text-align: center;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.item__icon {
|
||||
margin-bottom: 8rpx;
|
||||
font-size: 58rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 当前不是分销商 */
|
||||
.no-dealer {
|
||||
padding-top: 150rpx;
|
||||
}
|
||||
|
||||
.no-icon {
|
||||
image {
|
||||
width: 420rpx;
|
||||
height: 240rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.no-msg {
|
||||
padding: 86rpx 0;
|
||||
}
|
||||
|
||||
.form-submit {
|
||||
.button {
|
||||
font-size: 30rpx;
|
||||
background: #786cff;
|
||||
border: 1rpx solid #786cff;
|
||||
color: white;
|
||||
border-radius: 50rpx;
|
||||
padding: 22rpx 0;
|
||||
width: 470rpx;
|
||||
box-sizing: border-box;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+223
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption"
|
||||
@up="upCallback">
|
||||
|
||||
<!-- tab栏 -->
|
||||
<u-tabs :list="tabList" :is-scroll="false" :current="curTab" active-color="#786cff" :duration="0.2"
|
||||
@change="onChangeTab" />
|
||||
|
||||
<!-- 列表数据 -->
|
||||
<view class="widget-list b-f">
|
||||
<view class="widget__detail" v-for="(item, index) in list.data" :key="index">
|
||||
<view class="detail__row dis-flex flex-x-between">
|
||||
<view class="detail__left f-24">订单号:{{ item.order.order_no }}</view>
|
||||
<view class="detail__right f-24 c-violet">
|
||||
{{ item.order.state_text }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail__row m-top10 dis-flex flex-x-between">
|
||||
<view class="detail__left dis-flex flex-y-center">
|
||||
<avatar-image class="user-avatar" :url="item.user.avatar_url" :width="100" :borderWidth="4"
|
||||
:borderColor="`#fff`" />
|
||||
<view class="user-info dis-flex flex-dir-column flex-x-center">
|
||||
<view class="user-nickName f-28">{{ item.user.nick_name }}</view>
|
||||
<view class="user-time f-24 c-80">消费金额:¥{{ item.order_price }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail__right dis-flex flex-dir-column flex-x-center flex-y-center">
|
||||
<view class="detail__money t-r col-m">
|
||||
<text class="f-26">+ </text>
|
||||
<text class="f-28">{{ item.my_money }}</text>
|
||||
</view>
|
||||
<view class="detail__time f-22 c-80">{{ item.create_time }}</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 AvatarImage from '@/components/avatar-image'
|
||||
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
|
||||
import * as Api from '@/api/dealer/order'
|
||||
import SettingModel from '@/common/model/dealer/Setting'
|
||||
|
||||
const pageSize = 15
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody,
|
||||
AvatarImage
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
// 选项卡列表
|
||||
tabList: [],
|
||||
// 当前选项
|
||||
curTab: 0,
|
||||
// 列表数据
|
||||
list: getEmptyPaginateObj(),
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: true,
|
||||
// 每页数据的数量; 默认10
|
||||
page: { size: pageSize },
|
||||
// 数量要大于12条才显示无更多数据
|
||||
noMoreSize: 12,
|
||||
// 空布局
|
||||
empty: {
|
||||
tip: '亲,暂无相关数据'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
this.getSetting()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取分销设置
|
||||
getSetting() {
|
||||
const app = this
|
||||
SettingModel.data()
|
||||
.then(setting => {
|
||||
const words = setting.words.order
|
||||
app.setPageTitle(words.title)
|
||||
app.setTabList(words.words)
|
||||
})
|
||||
},
|
||||
|
||||
// 设置页面标题
|
||||
setPageTitle(title) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: title.value
|
||||
})
|
||||
},
|
||||
|
||||
// 设置选项卡数据
|
||||
setTabList(words) {
|
||||
const app = this
|
||||
app.tabList = [
|
||||
{ value: -1, name: words.all.value },
|
||||
{ value: 0, name: words.unsettled.value },
|
||||
{ value: 1, name: words.settled.value }
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* 上拉加载的回调 (页面初始化时也会执行一次)
|
||||
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
|
||||
* @param {Object} page
|
||||
*/
|
||||
upCallback(page) {
|
||||
const app = this
|
||||
// 设置列表数据
|
||||
app.getList(page.num)
|
||||
.then(list => {
|
||||
const curPageLen = list.data.length
|
||||
const totalSize = list.data.total
|
||||
app.mescroll.endBySize(curPageLen, totalSize)
|
||||
})
|
||||
.catch(() => app.mescroll.endErr())
|
||||
},
|
||||
|
||||
// 获取提现列表
|
||||
getList(pageNo = 1) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
Api.list({ settled: app.getTabValue(), page: pageNo })
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.list.data = getMoreListData(newList, app.list, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取当前标签项的值
|
||||
getTabValue() {
|
||||
const app = this
|
||||
if (app.tabList.length) {
|
||||
return app.tabList[app.curTab].value
|
||||
}
|
||||
return -1
|
||||
},
|
||||
|
||||
// 切换标签项
|
||||
onChangeTab(index) {
|
||||
const app = this
|
||||
// 设置当前选中的标签
|
||||
app.curTab = index
|
||||
// 刷新订单列表
|
||||
app.onRefreshList()
|
||||
},
|
||||
|
||||
// 刷新列表数据
|
||||
onRefreshList() {
|
||||
this.list = getEmptyPaginateObj()
|
||||
setTimeout(() => {
|
||||
this.mescroll.resetUpScroll()
|
||||
}, 120)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.c-violet {
|
||||
color: #786cff;
|
||||
}
|
||||
|
||||
.c-80 {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
// 订单列表
|
||||
.widget-list {
|
||||
padding: 10rpx 20rpx 40rpx 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.widget__detail {
|
||||
padding: 20rpx 15rpx;
|
||||
box-sizing: border-box;
|
||||
font-size: 28rpx;
|
||||
border-bottom: 1rpx solid #e7e7e7;
|
||||
|
||||
.user-avatar {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
height: 100%;
|
||||
|
||||
.user-nickName {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.detail__money {
|
||||
width: 100%;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view v-if="imageUrl" class="poster">
|
||||
<image class="image" mode="widthFix" :src="imageUrl" @click="onPreviewImage"></image>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as Api from '@/api/dealer/poster'
|
||||
import SettingModel from '@/common/model/dealer/Setting'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 海报图url
|
||||
imageUrl: undefined
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
this.getSetting()
|
||||
this.getPoster()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取分销设置
|
||||
getSetting() {
|
||||
const app = this
|
||||
SettingModel.data()
|
||||
.then(setting => {
|
||||
const words = setting.words.poster
|
||||
app.setPageTitle(words.title)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取推广二维码
|
||||
getPoster() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Api.qrcode({ channel: app.platform })
|
||||
.then(result => {
|
||||
// api数据赋值
|
||||
app.imageUrl = result.data.imageUrl
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 设置当前页面标题
|
||||
setPageTitle(title) {
|
||||
uni.setNavigationBarTitle({ title: title.value })
|
||||
},
|
||||
|
||||
// 预览海报图
|
||||
onPreviewImage() {
|
||||
uni.previewImage({
|
||||
current: this.imageUrl,
|
||||
urls: [this.imageUrl]
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.poster .image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
Executable
+267
@@ -0,0 +1,267 @@
|
||||
<template>
|
||||
<view class="container" v-if="!isLoading">
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption"
|
||||
@up="upCallback">
|
||||
|
||||
<!-- tab栏 -->
|
||||
<u-tabs :list="tabList" :is-scroll="false" :current="curTab" active-color="#786cff" :duration="0.2"
|
||||
@change="onChangeTab" />
|
||||
|
||||
<!-- 团队总人数 -->
|
||||
<view class="widget-people f-28 col-9">{{ words.total_team.value }}:{{ teamTotal }}人</view>
|
||||
|
||||
<!-- 列表数据 -->
|
||||
<view class="widget-list b-f">
|
||||
<view class="widget__detail dis-flex flex-x-between" v-for="(item, index) in list.data" :key="index">
|
||||
<view class="detail__left dis-flex flex-y-center">
|
||||
<avatar-image class="user-avatar" :url="item.user.avatar_url" :width="100" :borderWidth="4"
|
||||
:borderColor="`#fff`" />
|
||||
<view class="user-info dis-flex flex-dir-column flex-x-center">
|
||||
<view class="user-nickName f-28">{{ item.user.nick_name }}</view>
|
||||
<view class="user-time col-9 f-24">{{ item.create_time }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail__right dis-flex flex-dir-column flex-x-center flex-y-center">
|
||||
<view class="detail__money">
|
||||
<text class="f-24">¥</text>
|
||||
<text class="f-34">{{ item.user.expend_money }}</text>
|
||||
</view>
|
||||
<view class="detail__member f-22" v-if="item.subDealer">
|
||||
{{ item.subDealer.first_num + item.subDealer.second_num + item.subDealer.third_num }}个成员
|
||||
</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 AvatarImage from '@/components/avatar-image'
|
||||
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
|
||||
import * as Api from '@/api/dealer/team'
|
||||
import * as DealerApi from '@/api/dealer'
|
||||
import SettingModel from '@/common/model/dealer/Setting'
|
||||
|
||||
const pageSize = 15
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody,
|
||||
AvatarImage
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
// 选项卡列表
|
||||
tabList: [],
|
||||
// 当前选项
|
||||
curTab: 0,
|
||||
// 列表数据
|
||||
list: getEmptyPaginateObj(),
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: true,
|
||||
// 每页数据的数量; 默认10
|
||||
page: { size: pageSize },
|
||||
// 数量要大于12条才显示无更多数据
|
||||
noMoreSize: 12,
|
||||
// 空布局
|
||||
empty: {
|
||||
tip: '亲,暂无相关数据'
|
||||
}
|
||||
},
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 文字设置
|
||||
words: undefined,
|
||||
// 团队总人数
|
||||
teamTotal: undefined
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([app.getSetting(), app.getDealerUser()])
|
||||
.then(result => {
|
||||
const setting = result[0]
|
||||
const dealer = result[1]
|
||||
app.setTabList(setting, dealer)
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取分销设置
|
||||
getSetting() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
SettingModel.data()
|
||||
.then(setting => {
|
||||
app.words = setting.words.team.words
|
||||
app.setPageTitle(setting)
|
||||
resolve(setting)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取分销用户信息
|
||||
getDealerUser() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
DealerApi.user()
|
||||
.then(result => resolve(result.data.dealer))
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 设置页面标题
|
||||
setPageTitle(setting) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: setting.words.team.title.value
|
||||
})
|
||||
},
|
||||
|
||||
// 设置选项卡数据
|
||||
setTabList(setting, dealer) {
|
||||
const app = this
|
||||
const words = setting.words.team.words
|
||||
app.tabList = [
|
||||
{ value: 1, name: words.first.value, count: dealer.first_num }
|
||||
]
|
||||
app.teamTotal = dealer.first_num
|
||||
if (setting.basic.level >= 2) {
|
||||
app.tabList.push({
|
||||
value: 2,
|
||||
name: words.second.value,
|
||||
count: dealer.second_num
|
||||
})
|
||||
app.teamTotal += dealer.second_num
|
||||
}
|
||||
if (setting.basic.level >= 3) {
|
||||
app.tabList.push({
|
||||
value: 3,
|
||||
name: words.third.value,
|
||||
count: dealer.third_num
|
||||
})
|
||||
app.teamTotal += dealer.third_num
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 上拉加载的回调 (页面初始化时也会执行一次)
|
||||
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
|
||||
* @param {Object} page
|
||||
*/
|
||||
upCallback(page) {
|
||||
const app = this
|
||||
// 设置列表数据
|
||||
app.getList(page.num)
|
||||
.then(list => {
|
||||
const curPageLen = list.data.length
|
||||
const totalSize = list.data.total
|
||||
app.mescroll.endBySize(curPageLen, totalSize)
|
||||
})
|
||||
.catch(() => app.mescroll.endErr())
|
||||
},
|
||||
|
||||
// 获取提现列表
|
||||
getList(pageNo = 1) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
Api.list({ level: app.getTabValue(), page: pageNo })
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.list.data = getMoreListData(newList, app.list, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取当前标签项的值
|
||||
getTabValue() {
|
||||
const app = this
|
||||
if (app.tabList.length) {
|
||||
return app.tabList[app.curTab].value
|
||||
}
|
||||
return 1
|
||||
},
|
||||
|
||||
// 切换标签项
|
||||
onChangeTab(index) {
|
||||
const app = this
|
||||
// 设置当前选中的标签
|
||||
app.curTab = index
|
||||
// 刷新订单列表
|
||||
app.onRefreshList()
|
||||
},
|
||||
|
||||
// 刷新列表数据
|
||||
onRefreshList() {
|
||||
this.list = getEmptyPaginateObj()
|
||||
setTimeout(() => {
|
||||
this.mescroll.resetUpScroll()
|
||||
}, 120)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
// 团队人数
|
||||
.widget-people {
|
||||
padding: 15rpx 25rpx;
|
||||
height: 65rpx;
|
||||
box-sizing: border-box;
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
// 列表内容
|
||||
.widget-list {
|
||||
padding: 10rpx 20rpx 40rpx 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.widget__detail {
|
||||
padding: 20rpx 15rpx;
|
||||
box-sizing: border-box;
|
||||
font-size: 28rpx;
|
||||
border-bottom: 1rpx solid #e7e7e7;
|
||||
|
||||
.user-avatar {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.user-nickName {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.detail__member {
|
||||
background-color: #786cff;
|
||||
color: #fff;
|
||||
padding: 2rpx 15rpx;
|
||||
border-radius: 6rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+358
@@ -0,0 +1,358 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container b-f">
|
||||
|
||||
<!-- 头部背景图 -->
|
||||
<view class="dealer-bg">
|
||||
<image class="image" mode="widthFix" :src="background"></image>
|
||||
</view>
|
||||
|
||||
<view class="widget-body">
|
||||
<form @submit="handleSubmit">
|
||||
<!-- 提现佣金 -->
|
||||
<view class="widget widget__capital m-top20 b-f dis-flex flex-dir-column">
|
||||
<view class="capital__item dis-flex flex-x-between flex-y-center">
|
||||
<view class="item__left">{{ words.capital.value }}:</view>
|
||||
<view class="item__right c-violet">
|
||||
<text class="f-24">¥</text>
|
||||
<text class="f-34">{{ dealer.money }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="capital__item dis-flex flex-y-center">
|
||||
<view class="item__left">{{ words.money.value }}:</view>
|
||||
<view class="item__right flex-box">
|
||||
<input class="input" name="money" :placeholder="words.money_placeholder.value"></input>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 最低提现金额 -->
|
||||
<view class="capital__lowest m-top20 col-7 t-r">
|
||||
{{ words.min_money.value }}{{ settlement.min_money }}元
|
||||
</view>
|
||||
|
||||
<!-- 提现方式 -->
|
||||
<view class="widget widget__form m-top20 b-f dis-flex flex-dir-column">
|
||||
<view class="form__title f-28">提现方式</view>
|
||||
<view class="form__box">
|
||||
<block v-for="(item, index) in settlement.pay_type" :key="index">
|
||||
<block v-if="item == PayTypeEnum.WECHAT.value">
|
||||
<!-- 微信支付 -->
|
||||
<view class="form__field dis-flex flex-y-center">
|
||||
<view class="form__radio dis-flex flex-y-center" @click="handleChecked(PayTypeEnum.WECHAT.value)">
|
||||
<text class="radio__icon iconfont icon-radio"
|
||||
:class="[payment == PayTypeEnum.WECHAT.value ? 'c-violet' : 'col-bb']"></text>
|
||||
<text class="f-28">{{ PayTypeEnum.WECHAT.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<block v-if="item == PayTypeEnum.ALIPAY.value">
|
||||
<!-- 支付宝 -->
|
||||
<view class="form__field dis-flex flex-y-center">
|
||||
<view class="form__radio dis-flex flex-y-center" @click="handleChecked(PayTypeEnum.ALIPAY.value)">
|
||||
<text class="radio__icon iconfont icon-radio"
|
||||
:class="[payment == PayTypeEnum.ALIPAY.value ? 'c-violet' : 'col-bb']"></text>
|
||||
<text class="f-28">{{ PayTypeEnum.ALIPAY.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<block v-if="payment == PayTypeEnum.ALIPAY.value">
|
||||
<view class="form__field dis-flex flex-y-center">
|
||||
<view class="field-input flex-box">
|
||||
<input class="input" name="alipay_name" placeholder="请输入真实姓名"></input>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form__field dis-flex flex-y-center">
|
||||
<view class="field-input flex-box">
|
||||
<input class="input" name="alipay_account" placeholder="请输入支付宝账号"></input>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
<block v-if="item == PayTypeEnum.BANK_CARD.value">
|
||||
<!-- 银行卡 -->
|
||||
<view class="form__field dis-flex flex-y-center">
|
||||
<view class="form__radio dis-flex flex-y-center" @click="handleChecked(PayTypeEnum.BANK_CARD.value)">
|
||||
<text class="radio__icon iconfont icon-radio"
|
||||
:class="[payment == PayTypeEnum.BANK_CARD.value ? 'c-violet' : 'col-bb']"></text>
|
||||
<text class="f-28">{{ PayTypeEnum.BANK_CARD.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<block v-if="payment == PayTypeEnum.BANK_CARD.value">
|
||||
<view class="form__field dis-flex flex-y-center">
|
||||
<view class="field-input flex-box">
|
||||
<input class="input" name="bank_name" placeholder="请输入真实姓名"></input>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form__field dis-flex flex-y-center">
|
||||
<view class="field-input flex-box">
|
||||
<input class="input" name="bank_account" placeholder="请输入开户行名称/地址"></input>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form__field dis-flex flex-y-center">
|
||||
<view class="field-input flex-box">
|
||||
<input class="input" name="bank_card" placeholder="请输入银行卡号"></input>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 提交申请 -->
|
||||
<view class="form-submit dis-flex flex-x-center">
|
||||
<button formType="submit" :disabled="disabled">{{ words.submit.value }}</button>
|
||||
</view>
|
||||
</form>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as DealerApi from '@/api/dealer'
|
||||
import * as WithdrawApi from '@/api/dealer/withdraw'
|
||||
import SettingModel from '@/common/model/dealer/Setting'
|
||||
import { PayTypeEnum } from '@/common/enum/dealer/withdraw'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 枚举类
|
||||
PayTypeEnum,
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 分销商用户信息
|
||||
dealer: undefined,
|
||||
// 当前提现方式(选中的)
|
||||
payment: undefined,
|
||||
// 分销结算设置
|
||||
settlement: undefined,
|
||||
// 文字设置
|
||||
words: undefined,
|
||||
// 背景图
|
||||
background: undefined,
|
||||
// 按钮禁用
|
||||
disabled: false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
this.getSetting()
|
||||
this.getDealer()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取分销设置
|
||||
getSetting() {
|
||||
const app = this
|
||||
SettingModel.data()
|
||||
.then(setting => {
|
||||
// 赋值属性
|
||||
app.payment = setting.settlement.pay_type[0]
|
||||
app.settlement = setting.settlement
|
||||
app.words = setting.words.withdraw_apply.words
|
||||
app.background = setting.background.withdraw_apply
|
||||
// 设置当前页面标题
|
||||
app.setPageTitle(setting.words.withdraw_apply.title)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取分销商
|
||||
getDealer() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
DealerApi.user()
|
||||
.then(result => app.dealer = result.data.dealer)
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 设置当前页面标题
|
||||
setPageTitle(title) {
|
||||
uni.setNavigationBarTitle({ title: title.value })
|
||||
},
|
||||
|
||||
// 切换支付选项
|
||||
handleChecked(value) {
|
||||
this.payment = value
|
||||
},
|
||||
|
||||
// 表单提交
|
||||
handleSubmit({ detail }) {
|
||||
const app = this
|
||||
// 表单验证
|
||||
if (!app.onValidation(detail.value)) {
|
||||
return false
|
||||
}
|
||||
// 确认是否提交
|
||||
uni.showModal({
|
||||
title: '友情提示',
|
||||
content: '确定提交提现申请吗?请确认填写无误',
|
||||
showCancel: true,
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
app.onSubmit(detail.value)
|
||||
} else if (res.cancel) {
|
||||
app.disabled = false
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 提交提现申请
|
||||
onSubmit(data) {
|
||||
const app = this
|
||||
app.disabled = true
|
||||
data.pay_type = app.payment
|
||||
WithdrawApi.submit({ form: data })
|
||||
.then(result => {
|
||||
app.$toast(result.message)
|
||||
setTimeout(() => uni.navigateBack(), 1200)
|
||||
})
|
||||
.finally(() => app.disabled = false)
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
onValidation(data) {
|
||||
const app = this
|
||||
const words = app.words
|
||||
// 验证可提现佣金
|
||||
if (app.dealer.money <= 0) {
|
||||
app.$error('当前没有' + words.capital.value)
|
||||
return false
|
||||
}
|
||||
// 验证提现金额
|
||||
if (!data.money || data.money.length < 1) {
|
||||
app.$error('请填写' + words.money.value)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.c-violet {
|
||||
color: #786cff;
|
||||
}
|
||||
|
||||
.col-bb {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.dealer-bg {
|
||||
.image {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.widget-body {
|
||||
position: relative;
|
||||
width: 88%;
|
||||
margin: 0 auto;
|
||||
|
||||
.widget {
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.11);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 提现金额
|
||||
.widget__capital {
|
||||
padding: 10rpx 0;
|
||||
margin-top: -60rpx;
|
||||
|
||||
.capital__item {
|
||||
height: 80rpx;
|
||||
padding: 10rpx 35rpx;
|
||||
font-size: 28rpx;
|
||||
border-bottom: 1rpx solid #e7e7e7;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item__left {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.item__right {
|
||||
.input {
|
||||
font-size: 28rpx;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.capital__lowest {
|
||||
padding-right: 20rpx;
|
||||
box-sizing: border-box;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
// 提现方式
|
||||
.widget__form {
|
||||
padding: 10rpx 0 20rpx 0;
|
||||
|
||||
.form__title {
|
||||
padding: 16rpx 35rpx;
|
||||
border-bottom: 1rpx solid #f3f3f3;
|
||||
}
|
||||
|
||||
.form__box {
|
||||
padding: 20rpx 35rpx;
|
||||
}
|
||||
|
||||
.form__field {
|
||||
height: 80rpx;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.radio__icon {
|
||||
font-size: 38rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
.input {
|
||||
background-color: #f9f9f9;
|
||||
// height: 70rpx;
|
||||
padding: 16rpx 20rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提交申请
|
||||
.form-submit {
|
||||
margin-top: 40rpx;
|
||||
|
||||
button {
|
||||
font-size: 30rpx;
|
||||
background: #786cff;
|
||||
border: 1rpx solid #786cff;
|
||||
color: white;
|
||||
border-radius: 50rpx;
|
||||
padding: 0 120rpx;
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
background: #8e84fc;
|
||||
border-color: #8e84fc;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+237
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption" @up="upCallback">
|
||||
|
||||
<!-- tab栏 -->
|
||||
<u-tabs :list="tabList" :is-scroll="false" :current="curTab" active-color="#786cff" :duration="0.2" @change="onChangeTab" />
|
||||
|
||||
<!-- 列表数据 -->
|
||||
<view class="widget-list">
|
||||
<view class="widget__detail dis-flex flex-x-between" v-for="(item, index) in list.data" :key="index">
|
||||
<view class="detail__left dis-flex flex-dir-column flex-x-around">
|
||||
<view class="detail__money f-30">提现 {{ item.money }}元</view>
|
||||
<view class="detail__time col-9 f-24">{{ item.create_time }}</view>
|
||||
</view>
|
||||
<view class="detail__right dis-flex flex-dir-column flex-x-center flex-y-center">
|
||||
<view class="detail__status" :class="[ApplyStatusColor[item.apply_status]]">
|
||||
<text>{{ ApplyStatusText[item.apply_status] }}</text>
|
||||
</view>
|
||||
<block v-if="item.apply_status == 30">
|
||||
<view class="detail__reason" @click="handleShowRejectReason(item)">驳回原因
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
|
||||
<!-- 砍价规则弹窗 -->
|
||||
<u-modal v-model="showRejectReason" title="驳回原因">
|
||||
<view class="pops-content">
|
||||
<text>{{ rejectReason }}</text>
|
||||
</view>
|
||||
</u-modal>
|
||||
|
||||
</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 Api from '@/api/dealer/withdraw'
|
||||
import SettingModel from '@/common/model/dealer/Setting'
|
||||
import { ApplyStatusEnum } from '@/common/enum/dealer/withdraw'
|
||||
|
||||
const pageSize = 15
|
||||
// 提现状态文字
|
||||
const ApplyStatusText = {}
|
||||
|
||||
// 提现状态颜色
|
||||
const ApplyStatusColor = {
|
||||
[ApplyStatusEnum.WAIT.value]: 'col-m',
|
||||
[ApplyStatusEnum.PASSED.value]: 'col-green',
|
||||
[ApplyStatusEnum.REJECT.value]: 'col-m',
|
||||
[ApplyStatusEnum.PAYMENT.value]: 'col-green'
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
// 枚举类
|
||||
ApplyStatusEnum,
|
||||
ApplyStatusColor,
|
||||
ApplyStatusText,
|
||||
// 选项卡列表
|
||||
tabList: [],
|
||||
// 当前选项
|
||||
curTab: 0,
|
||||
// 列表数据
|
||||
list: getEmptyPaginateObj(),
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: true,
|
||||
// 每页数据的数量; 默认10
|
||||
page: { size: pageSize },
|
||||
// 数量要大于12条才显示无更多数据
|
||||
noMoreSize: 12,
|
||||
// 空布局
|
||||
empty: {
|
||||
tip: '亲,暂无相关数据'
|
||||
}
|
||||
},
|
||||
// 驳回原因弹窗
|
||||
showRejectReason: false,
|
||||
rejectReason: ''
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
this.getSetting()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取分销设置
|
||||
getSetting() {
|
||||
const app = this
|
||||
SettingModel.data()
|
||||
.then(setting => {
|
||||
const words = setting.words.withdraw_list
|
||||
app.setPageTitle(words.title)
|
||||
app.setTabList(words.words)
|
||||
})
|
||||
},
|
||||
|
||||
// 设置页面标题
|
||||
setPageTitle(title) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: title.value
|
||||
})
|
||||
},
|
||||
|
||||
// 设置选项卡数据
|
||||
setTabList(words) {
|
||||
const app = this
|
||||
app.tabList = [
|
||||
{ value: -1, name: words.all.value },
|
||||
{ value: ApplyStatusEnum.WAIT.value, name: words.apply_10.value },
|
||||
{ value: ApplyStatusEnum.PASSED.value, name: words.apply_20.value },
|
||||
{ value: ApplyStatusEnum.PAYMENT.value, name: words.apply_40.value },
|
||||
{ value: ApplyStatusEnum.REJECT.value, name: words.apply_30.value }
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* 上拉加载的回调 (页面初始化时也会执行一次)
|
||||
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
|
||||
* @param {Object} page
|
||||
*/
|
||||
upCallback(page) {
|
||||
const app = this
|
||||
// 设置列表数据
|
||||
app.getList(page.num)
|
||||
.then(list => {
|
||||
const curPageLen = list.data.length
|
||||
const totalSize = list.data.total
|
||||
app.mescroll.endBySize(curPageLen, totalSize)
|
||||
})
|
||||
.catch(() => app.mescroll.endErr())
|
||||
},
|
||||
|
||||
// 获取提现列表
|
||||
getList(pageNo = 1) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
Api.list({ applyStatus: app.getTabValue(), page: pageNo })
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.list.data = getMoreListData(newList, app.list, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取当前标签项的值
|
||||
getTabValue() {
|
||||
const app = this
|
||||
if (app.tabList.length) {
|
||||
return app.tabList[app.curTab].value
|
||||
}
|
||||
return -1
|
||||
},
|
||||
|
||||
// 切换标签项
|
||||
onChangeTab(index) {
|
||||
const app = this
|
||||
// 设置当前选中的标签
|
||||
app.curTab = index
|
||||
// 刷新订单列表
|
||||
app.onRefreshList()
|
||||
},
|
||||
|
||||
// 刷新列表数据
|
||||
onRefreshList() {
|
||||
this.list = getEmptyPaginateObj()
|
||||
setTimeout(() => {
|
||||
this.mescroll.resetUpScroll()
|
||||
}, 120)
|
||||
},
|
||||
|
||||
// 显示驳回原因
|
||||
handleShowRejectReason(item) {
|
||||
this.showRejectReason = true
|
||||
this.rejectReason = item.reject_reason
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
// 提现明细列表
|
||||
.widget-list {
|
||||
padding: 16rpx 20rpx 40rpx 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.widget__detail {
|
||||
padding: 26rpx 15rpx;
|
||||
box-sizing: border-box;
|
||||
font-size: 26rpx;
|
||||
border-bottom: 1rpx solid #f2f2f2;
|
||||
}
|
||||
|
||||
.widget__detail .detail__money {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.widget__detail .detail__reason {
|
||||
color: #8e84fc;
|
||||
}
|
||||
|
||||
// 驳回原因 (弹窗)
|
||||
.pops-content {
|
||||
padding: 30rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 44rpx;
|
||||
text-align: left;
|
||||
color: #606266;
|
||||
height: 220rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
Executable
+441
@@ -0,0 +1,441 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<view class="addres-list">
|
||||
<view class="address-item">
|
||||
<view class="head-info">
|
||||
<view class="title">{{ title }}</view>
|
||||
<view class="description">{{ description }}</view>
|
||||
|
||||
</view>
|
||||
<u-form :label-width="200" :model="form" ref="uForm">
|
||||
<u-form-item left-icon="info-circle" label="剩余电量">
|
||||
<view class="item-right">{{ form.surplusPower }}</view>
|
||||
</u-form-item>
|
||||
<u-form-item left-icon="info-circle" label="设备电压">
|
||||
<view class="item-right">{{ form.equipmentVoltage }}</view>
|
||||
</u-form-item>
|
||||
<u-form-item left-icon="info-circle" label="GPS信号">
|
||||
<view class="item-right">{{ form.gps }}</view>
|
||||
</u-form-item>
|
||||
<u-form-item left-icon="info-circle" label="GSM信号">
|
||||
<view class="item-right">{{ form.gsm }}</view>
|
||||
</u-form-item>
|
||||
<u-form-item left-icon="info-circle" label="设备电流">
|
||||
<view class="item-right">{{ form.equipmentCurrent }}</view>
|
||||
</u-form-item>
|
||||
<u-form-item left-icon="info-circle" label="标称容量">
|
||||
<view class="item-right">{{ form.nominalCapacity }}</view>
|
||||
</u-form-item>
|
||||
<u-form-item left-icon="info-circle" label="设备温度">
|
||||
<view class="item-right">{{ form.equipmentTemperature }}</view>
|
||||
</u-form-item>
|
||||
<u-form-item left-icon="info-circle" label="电池温度">
|
||||
<view class="item-right">{{ form.batteryTemperature }}</view>
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
<view class="address-item">
|
||||
<u-row gutter="16" justify="center">
|
||||
<u-col span="4">
|
||||
<view class="demo-layout bg-purple">
|
||||
<text class="desc">最高电压</text>
|
||||
<text class="value1">3.168V</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="4">
|
||||
<view class="demo-layout bg-purple-light">
|
||||
<text class="desc">最低电压</text>
|
||||
<text class="value2">3.166V</text>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col span="4">
|
||||
<view class="demo-layout bg-purple-dark">
|
||||
<text class="desc">相差压值</text>
|
||||
<text class="value3">0.002V</text>
|
||||
</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
<view class="address-item">
|
||||
<view class="head-info">
|
||||
<view class="title">电芯电压</view>
|
||||
</view>
|
||||
<view class="content">
|
||||
<image class="image" src="../../../static/battery.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as AddressApi from '@/api/address'
|
||||
import Empty from '@/components/empty'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Empty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
//当前页面参数
|
||||
options: {},
|
||||
form: {
|
||||
surplusPower: '98%',
|
||||
equipmentVoltage: '82.6V',
|
||||
gps: '',
|
||||
gsm: '',
|
||||
equipmentCurrent: '12AH',
|
||||
nominalCapacity: '50AH',
|
||||
equipmentTemperature: '2℃',
|
||||
batteryTemperature: '35℃'
|
||||
},
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 收货地址列表
|
||||
list: [],
|
||||
// 默认收货地址
|
||||
defaultId: null,
|
||||
title: '设备信息',
|
||||
description: '要查看设备的相关参数信息'
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
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
|
||||
})
|
||||
},
|
||||
|
||||
navTo(url) {
|
||||
this.$navTo(url)
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加新地址
|
||||
*/
|
||||
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-bottom: calc(constant(safe-area-inset-bottom) + 140rpx);
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 140rpx);
|
||||
padding-top: 20rpx;
|
||||
|
||||
.head-info {
|
||||
width: 750rpx;
|
||||
margin: auto;
|
||||
display: felx;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.description {
|
||||
padding: 10rpx 0;
|
||||
font-size: 30rpx;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 项目内容
|
||||
.address-item {
|
||||
margin: 0 auto 20rpx auto;
|
||||
padding: 30rpx 40rpx;
|
||||
width: 94%;
|
||||
box-shadow: 0 1rpx 5rpx 0 rgba(0, 0, 0, 0.05);
|
||||
border-radius: 16rpx;
|
||||
background: #fff;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.demo-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.value1 {
|
||||
color: #de4e02;
|
||||
}
|
||||
|
||||
.value2 {
|
||||
color: #ff0089;
|
||||
}
|
||||
|
||||
.value3 {
|
||||
color: #0c10f5;
|
||||
}
|
||||
}
|
||||
|
||||
.image{
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.contacts {
|
||||
margin-bottom: 16rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.bms-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.item {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bms-image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 120rpx;
|
||||
|
||||
.bms-image-box {
|
||||
display: flex;
|
||||
font-size: 22rpx;
|
||||
margin-bottom: 18rpx;
|
||||
color: #00af00;
|
||||
|
||||
.bms-image-fbf {
|
||||
margin-top: 22rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 50rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
font-size: 28rpx;
|
||||
|
||||
.region {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f3f3f3;
|
||||
}
|
||||
|
||||
.line-align {
|
||||
width: 6rpx;
|
||||
height: 48rpx;
|
||||
border-right: 1rpx solid #f3f3f3;
|
||||
}
|
||||
|
||||
.item-option {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
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;
|
||||
z-index: 11;
|
||||
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.btn-wrapper {
|
||||
height: 120rpx;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
height: 86rpx;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-shadow: 0 1rpx 5rpx 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.btn-item-main {
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="goods-info">
|
||||
<view class="goods">
|
||||
<image src="../../static/goods/battery.png" mode="aspectFit"></image>
|
||||
<view class="info">
|
||||
<text class="goods-name">{{ record.equipmentName }}</text>
|
||||
<!-- <text class="goods-desc">归属门店:{{ record.merchantName }}</text> -->
|
||||
<text class="goods-desc">电池型号:{{ record.batteryModel }}</text>
|
||||
<text class="goods-desc">电池编号:{{ record.equipmentCode }}</text>
|
||||
<text class="goods-desc">是否激活:{{ record.isCtive == 0 ? '已激活' : '未激活' }}</text>
|
||||
<text class="goods-desc">BMS:{{ record.bms }}</text>
|
||||
<text class="goods-desc">工作状态:{{ record.workingStatus }}</text>
|
||||
<text class="goods-desc">租赁状态:{{ record.leaseStatus }}</text>
|
||||
<text class="goods-desc">电池状态:{{ record.batteryStatus }}</text>
|
||||
<text class="goods-desc">电池电量:{{ record.batteryPower }}</text>
|
||||
<text class="goods-desc">是否在线:{{ record.isOnline }}</text>
|
||||
<text class="goods-desc">总电压:{{ record.totalVoltage }}</text>
|
||||
<text class="goods-desc">剩余容量:{{ record.surplusCapacity }}</text>
|
||||
|
||||
|
||||
|
||||
<text class="selling-point" v-if="record.equipmentCategory === '锂电池租赁'">全新电池!连续组满15个月电池归客户</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="submit">
|
||||
<u-button type="primary" @click="onBuy">立即购买</u-button>
|
||||
</view>
|
||||
<!-- <view class="submit">
|
||||
<u-button type="success" :disabled="agree" @click="copyCode(record.equipmentCode)">复制设备编号</u-button>
|
||||
</view> -->
|
||||
<view class="submit">
|
||||
<u-button type="success" :disabled="agree" @click="gohome">返回首页</u-button>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { userId } from '@/config.js';
|
||||
import {
|
||||
getEquipment
|
||||
} from '@/websoft/api/equipment.js'
|
||||
import { getUser } from '@/websoft/api/user.js'
|
||||
import { addOrder } from '@/websoft/api/order.js'
|
||||
import { createOrderNo } from '@/utils/util.js'
|
||||
import store from '../../store';
|
||||
export default {
|
||||
components: {
|
||||
// Search
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
record: {},
|
||||
// 正在加载中
|
||||
isLoading: true,
|
||||
// 当前选择的设备ID
|
||||
equipmentId: null,
|
||||
merchantId: null,
|
||||
merchantCode: null,
|
||||
show: true,
|
||||
mode: 'range',
|
||||
month: 6,
|
||||
agree: false,
|
||||
price: {
|
||||
batteryRent: 300,
|
||||
batteryDeposit: 300,
|
||||
batteryInsurance: 0
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
onLoad(option) {
|
||||
const app = this
|
||||
// 记录当前选择的门店ID
|
||||
console.log("option: ",option);
|
||||
app.equipmentId = Number(option.equipmentId)
|
||||
app.merchantId = Number(option.merchantId)
|
||||
app.merchantCode = option.merchantCode
|
||||
// 获取设备列表
|
||||
app.getEquipment()
|
||||
},
|
||||
methods: {
|
||||
getEquipment() {
|
||||
const app = this
|
||||
const {
|
||||
equipmentId
|
||||
} = this
|
||||
getEquipment(equipmentId).then(res => {
|
||||
app.record = res.data
|
||||
console.log("res2222: ", app.record);
|
||||
})
|
||||
},
|
||||
gohome(){
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
},
|
||||
onChangeStepper({ value }) {
|
||||
this.month = value
|
||||
},
|
||||
change(e) {
|
||||
console.log(e);
|
||||
},
|
||||
onInput(month) {
|
||||
this.month = month
|
||||
},
|
||||
onAgree(){
|
||||
this.agree = !this.agree
|
||||
},
|
||||
showXieyi(){
|
||||
this.$navTo('pages/help/xieyi')
|
||||
},
|
||||
copyCode(text){
|
||||
// #ifndef H5
|
||||
console.log("text: ",text);
|
||||
uni.setClipboardData({
|
||||
text: text,
|
||||
success: (result) => {
|
||||
this.$success("复制成功")
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
let textarea = document.createElement("textarea")
|
||||
textarea.value = text
|
||||
textarea.readOnly = "readOnly"
|
||||
document.body.appendChild(textarea)
|
||||
textarea.select() // 选中文本内容
|
||||
textarea.setSelectionRange(0, info.length)
|
||||
uni.showToast({ //提示
|
||||
title: '复制成功'
|
||||
})
|
||||
result = document.execCommand("copy")
|
||||
textarea.remove()
|
||||
// #endif
|
||||
// copy() {
|
||||
// let result
|
||||
// // #ifndef H5
|
||||
// //uni.setClipboardData方法就是讲内容复制到粘贴板
|
||||
// uni.setClipboardData({
|
||||
// data: this.downUrl.url, //要被复制的内容
|
||||
// success: () => { //复制成功的回调函数
|
||||
// uni.showToast({ //提示
|
||||
// title: '复制成功'
|
||||
// })
|
||||
// }
|
||||
// });
|
||||
// // #endif
|
||||
|
||||
|
||||
// }
|
||||
},
|
||||
onBuy(){
|
||||
const app = this
|
||||
const { equipmentId } = this
|
||||
this.$navTo('pages/merchant/merchant', { equipmentId })
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-info {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
width: 700rpx;
|
||||
margin: 20rpx auto;
|
||||
|
||||
.goods {
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
|
||||
image {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
margin: 20rpx;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20rpx;
|
||||
|
||||
.goods-name {
|
||||
font-size: 34rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.goods-desc {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
.selling-point{
|
||||
padding: 5px 0;
|
||||
color: #e6760e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.between-time {
|
||||
width: 500rpx;
|
||||
margin: auto;
|
||||
padding-bottom: 20rpx;
|
||||
text-align: center;
|
||||
|
||||
.select-slider {
|
||||
line-height: 2em;
|
||||
color: #e6760e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.order {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
width: 700rpx;
|
||||
margin: 20rpx auto;
|
||||
padding: 10rpx 0;
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 30rpx;
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.xieyi {
|
||||
padding: 20rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.submit {
|
||||
border-radius: 12rpx;
|
||||
width: 700rpx;
|
||||
margin: 20rpx auto;
|
||||
padding: 10rpx 0;
|
||||
}
|
||||
.xieyi-text{
|
||||
color: #0000ff;
|
||||
}
|
||||
.fenqi{
|
||||
padding: 30rpx;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
.item{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+159
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<!-- 商品评价 -->
|
||||
<view v-if="!isLoading && list.length" class="goods-comment m-top20">
|
||||
<view class="item-title dis-flex">
|
||||
<view class="block-left flex-box">
|
||||
商品评价 (<text class="total">{{ total }}条</text>)
|
||||
</view>
|
||||
<view class="block-right">
|
||||
<text @click="onTargetToComment" class="show-more col-9">查看更多</text>
|
||||
<text class="iconfont icon-arrow-right col-9"></text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 评论列表 -->
|
||||
<view class="comment-list">
|
||||
<view class="comment-item" v-for="(item, index) in list" :key="index">
|
||||
<view class="comment-item_row dis-flex flex-y-center">
|
||||
<view class="user-info dis-flex flex-y-center">
|
||||
<avatar-image class="user-avatar" :url="item.user.avatar_url" :width="50" />
|
||||
<text class="user-name">{{ item.user.nick_name }}</text>
|
||||
</view>
|
||||
<!-- 评星 -->
|
||||
<view class="star-rating">
|
||||
<u-rate active-color="#f4a213" :current="rates[item.score]" :disabled="true" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-content m-top20">
|
||||
<text class="f-26 twoline-hide">{{ item.content }}</text>
|
||||
</view>
|
||||
<view class="comment-time">{{ item.create_time }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AvatarImage from '@/components/avatar-image'
|
||||
import * as CommentApi from '@/api/comment'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AvatarImage
|
||||
},
|
||||
props: {
|
||||
// 商品ID
|
||||
goodsId: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
// 加载多少条记录 默认2条
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 2
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 评星数据转换
|
||||
rates: { 10: 5, 20: 3, 30: 1 },
|
||||
// 评价列表数据
|
||||
list: [],
|
||||
// 评价总数量
|
||||
total: 0
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
// 加载评价列表数据
|
||||
this.getCommentList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 加载评价列表数据
|
||||
getCommentList() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
CommentApi.listRows(app.goodsId, app.limit)
|
||||
.then(result => {
|
||||
app.list = result.data.list
|
||||
app.total = result.data.total
|
||||
})
|
||||
.catch(err => err)
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 跳转到评论列表页
|
||||
onTargetToComment() {
|
||||
const app = this
|
||||
app.$navTo('pages/comment/index', { goodsId: app.goodsId })
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-comment {
|
||||
padding: 20rpx 30rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 25rpx;
|
||||
|
||||
.total {
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.show-more {
|
||||
margin-right: 8rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.comment-item {
|
||||
padding: 15rpx 5rpx;
|
||||
margin-bottom: 10rpx;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.comment-item_row {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
margin-right: 15rpx;
|
||||
|
||||
.user-avatar {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.item-content {
|
||||
color: #333;
|
||||
margin: 16rpx 0;
|
||||
max-height: 76rpx;
|
||||
line-height: 38rpx;
|
||||
}
|
||||
|
||||
.comment-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
</style>
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<view v-if="list.length" class="service-wrapper" :style="appThemeStyle">
|
||||
<!-- 服务简述 -->
|
||||
<view class="service-simple" @click="handlePopup">
|
||||
<view class="s-list">
|
||||
<view class="s-item" v-for="(item, index) in list" :key="index">
|
||||
<text class="item-icon iconfont icon-fuwu"></text>
|
||||
<text class="item-val">{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 扩展箭头 -->
|
||||
<view class="s-arrow f-26 col-9 t-r">
|
||||
<text class="iconfont icon-arrow-right"></text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 详情内容弹窗 -->
|
||||
<u-popup v-model="showPopup" mode="bottom" :closeable="true" :border-radius="26">
|
||||
<view class="service-content">
|
||||
<view class="title">服务</view>
|
||||
<scroll-view class="content-scroll" :scroll-y="true">
|
||||
<view class="s-list clearfix">
|
||||
<view class="s-item" v-for="(item, index) in list" :key="index">
|
||||
<text class="item-icon iconfont icon-fuwu"></text>
|
||||
<view class="item-val">{{ item.name }}</view>
|
||||
<view class="item-summary">{{ item.summary }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as ServiceApi from '@/api/goods/service'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// 商品ID
|
||||
goodsId: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 显示详情内容弹窗
|
||||
showPopup: false,
|
||||
// 服务列表数据
|
||||
list: []
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
// 获取商品服务列表
|
||||
this.getServiceList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取商品服务列表
|
||||
getServiceList() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
ServiceApi.list(app.goodsId)
|
||||
.then(result => app.list = result.data.list)
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 显示弹窗
|
||||
handlePopup() {
|
||||
this.showPopup = !this.showPopup
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.service-wrapper {
|
||||
min-height: 24rpx;
|
||||
margin-bottom: -24rpx;
|
||||
}
|
||||
|
||||
// 服务简述
|
||||
.service-simple {
|
||||
padding: 24rpx 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.s-list {
|
||||
flex: 1;
|
||||
margin-left: -15rpx;
|
||||
}
|
||||
|
||||
.s-item {
|
||||
float: left;
|
||||
font-size: 26rpx;
|
||||
margin: 8rpx 15rpx;
|
||||
|
||||
.item-icon {
|
||||
color: $main-bg;
|
||||
}
|
||||
|
||||
.item-val {
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 服务详细内容
|
||||
.service-content {
|
||||
padding: 24rpx;
|
||||
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 50rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content-scroll {
|
||||
min-height: 400rpx;
|
||||
max-height: 760rpx;
|
||||
}
|
||||
|
||||
.s-list {
|
||||
padding: 0 30rpx 0 80rpx;
|
||||
}
|
||||
|
||||
.s-item {
|
||||
position: relative;
|
||||
margin-bottom: 60rpx;
|
||||
|
||||
.item-icon {
|
||||
position: absolute;
|
||||
top: 6rpx;
|
||||
left: -50rpx;
|
||||
color: $main-bg;
|
||||
}
|
||||
|
||||
.item-val {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.item-summary {
|
||||
font-size: 26rpx;
|
||||
margin-top: 20rpx;
|
||||
color: #6d6d6d;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Executable
+189
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<goods-sku-popup :value="value" @input="onChangeValue" border-radius="20" :localdata="goodsInfo" :mode="skuMode" :maskCloseAble="true"
|
||||
:priceColor="appTheme.mainBg" :buyNowBackgroundColor="appTheme.mainBg" :addCartColor="appTheme.viceText" :addCartBackgroundColor="appTheme.viceBg"
|
||||
:activedStyle="{ color: appTheme.mainBg, borderColor: appTheme.mainBg, backgroundColor: activedBtnBackgroundColor }" @open="openSkuPopup"
|
||||
@close="closeSkuPopup" @add-cart="addCart" @buy-now="buyNow" buyNowText="立即购买" :maxBuyNum="maxBuyNum" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setCartTotalNum } from '@/core/app'
|
||||
import { hex2rgba } from '@/utils/color'
|
||||
import * as CartApi from '@/api/cart'
|
||||
import GoodsSkuPopup from '@/components/goods-sku-popup'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GoodsSkuPopup
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'input'
|
||||
},
|
||||
props: {
|
||||
// true 组件显示 false 组件隐藏
|
||||
value: {
|
||||
Type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
|
||||
skuMode: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 商品详情信息
|
||||
goods: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 商品信息
|
||||
goodsInfo: {},
|
||||
// 限购数量
|
||||
maxBuyNum: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 规格按钮选中时的背景色
|
||||
activedBtnBackgroundColor() {
|
||||
return hex2rgba(this.appTheme.mainBg, 0.1)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const app = this
|
||||
const { goods } = app
|
||||
app.goodsInfo = {
|
||||
_id: goods.goods_id,
|
||||
name: goods.goods_name,
|
||||
goods_thumb: goods.goods_image,
|
||||
sku_list: app.getSkuList(),
|
||||
spec_list: app.getSpecList()
|
||||
}
|
||||
app.maxBuyNum = app.getMaxBuyNum()
|
||||
},
|
||||
|
||||
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
|
||||
},
|
||||
|
||||
// 限购数量
|
||||
getMaxBuyNum() {
|
||||
const { goods } = this
|
||||
return goods.is_restrict ? goods.restrict_single : null
|
||||
},
|
||||
|
||||
// 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>
|
||||
Executable
+146
@@ -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>
|
||||
Executable
+229
@@ -0,0 +1,229 @@
|
||||
.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: $main-bg;
|
||||
margin-bottom: -10rpx;
|
||||
}
|
||||
|
||||
// 商品价
|
||||
.floor-price {
|
||||
color: $main-bg;
|
||||
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, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
}
|
||||
|
||||
// 购物车按钮
|
||||
.btn-item-deputy {
|
||||
background: linear-gradient(to right, $vice-bg, $vice-bg2);
|
||||
color: $vice-text;
|
||||
}
|
||||
}
|
||||
Executable
+328
@@ -0,0 +1,328 @@
|
||||
<template>
|
||||
<view v-show="!isLoading" class="container" :style="appThemeStyle">
|
||||
<!-- 商品图片轮播 -->
|
||||
<SlideImage v-if="!isLoading" :video="goods.video" :videoCover="goods.videoCover" :images="goods.goods_images" />
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view v-if="!isLoading" class="goods-info m-top20">
|
||||
<!-- 价格、销量 -->
|
||||
<view class="info-item info-item__top dis-flex flex-x-between flex-y-end">
|
||||
<view class="block-left dis-flex flex-y-center">
|
||||
<!-- 商品售价 -->
|
||||
<text class="floor-price__samll">¥</text>
|
||||
<text class="floor-price">{{ goods.goods_price_min }}</text>
|
||||
<!-- 会员价标签 -->
|
||||
<view v-if="goods.is_user_grade" class="user-grade">
|
||||
<text>会员价</text>
|
||||
</view>
|
||||
<!-- 划线价 -->
|
||||
<text v-if="goods.line_price_min > 0" class="original-price">¥{{ goods.line_price_min }}</text>
|
||||
</view>
|
||||
<view class="block-right dis-flex">
|
||||
<!-- 销量 -->
|
||||
<view class="goods-sales">
|
||||
<text>已售{{ goods.goods_sales }}件</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 标题、分享 -->
|
||||
<view class="info-item info-item__name dis-flex flex-y-center">
|
||||
<view class="goods-name flex-box">
|
||||
<text class="twoline-hide">{{ goods.goods_name }}</text>
|
||||
</view>
|
||||
<view class="goods-share__line"></view>
|
||||
<view class="goods-share">
|
||||
<button class="share-btn dis-flex flex-dir-column" @click="onShowShareSheet()">
|
||||
<text class="share__icon iconfont icon-fenxiang"></text>
|
||||
<text class="f-24">分享</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 商品卖点 -->
|
||||
<view v-if="goods.selling_point" class="info-item info-item_selling-point">
|
||||
<text>{{ goods.selling_point }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选择商品规格 -->
|
||||
<view v-if="goods.spec_type == 20" class="goods-choice m-top20 b-f" @click="onShowSkuPopup(1)">
|
||||
<view class="spec-list">
|
||||
<view class="flex-box">
|
||||
<text class="col-8">选择:</text>
|
||||
<text class="spec-name" v-for="(item, index) in goods.specList" :key="index">{{ item.spec_name }}</text>
|
||||
</view>
|
||||
<view class="f-26 col-9 t-r">
|
||||
<text class="iconfont icon-arrow-right"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品服务 -->
|
||||
<Service v-if="!isLoading" :goods-id="goodsId" />
|
||||
|
||||
<!-- 商品SKU弹窗 -->
|
||||
<SkuPopup v-if="!isLoading" v-model="showSkuPopup" :skuMode="skuMode" :goods="goods" @addCart="onAddCart" />
|
||||
|
||||
<!-- 商品评价 -->
|
||||
<Comment v-if="!isLoading" :goods-id="goodsId" :limit="2" />
|
||||
|
||||
<!-- 商品描述 -->
|
||||
<view v-if="!isLoading" class="goods-content m-top20">
|
||||
<view class="item-title b-f">
|
||||
<text>商品描述</text>
|
||||
</view>
|
||||
<block v-if="goods.content != ''">
|
||||
<view class="goods-content__detail b-f">
|
||||
<mp-html :content="goods.content" />
|
||||
</view>
|
||||
</block>
|
||||
<empty v-else tips="亲,暂无商品描述" />
|
||||
</view>
|
||||
|
||||
<!-- 商品推荐 -->
|
||||
<recommended />
|
||||
|
||||
<!-- 底部选项卡 -->
|
||||
<view class="footer-fixed">
|
||||
<view class="footer-container">
|
||||
<!-- 导航图标 -->
|
||||
<view class="foo-item-fast">
|
||||
<!-- 首页 -->
|
||||
<view class="fast-item fast-item--home" @click="onTargetHome">
|
||||
<view class="fast-icon">
|
||||
<text class="iconfont icon-shouye"></text>
|
||||
</view>
|
||||
<view class="fast-text">
|
||||
<text>首页</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 客服 -->
|
||||
<customer-btn v-if="isShowCustomerBtn">
|
||||
<view class="fast-item">
|
||||
<view class="fast-icon">
|
||||
<text class="iconfont icon-kefu1"></text>
|
||||
</view>
|
||||
<view class="fast-text">
|
||||
<text>客服</text>
|
||||
</view>
|
||||
</view>
|
||||
</customer-btn>
|
||||
<!-- 购物车 -->
|
||||
<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 v-if="isEnableCart" class="btn-item btn-item-deputy" @click="onShowSkuPopup(2)">
|
||||
<text>加入购物车</text>
|
||||
</view>
|
||||
<view class="btn-item btn-item-main" @click="onShowSkuPopup(3)">
|
||||
<text>立即购买</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷导航 -->
|
||||
<!-- <shortcut bottom="120rpx" /> -->
|
||||
|
||||
<!-- 分享菜单 -->
|
||||
<share-sheet v-model="showShareSheet" :shareTitle="goods.goods_name" :shareImageUrl="goods.goods_image" :posterApiCall="posterApiCall" :posterApiParam="{ goodsId }" />
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSceneData } from '@/core/app'
|
||||
import * as GoodsApi from '@/api/goods'
|
||||
import * as CartApi from '@/api/cart'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
import { GoodsTypeEnum } from '@/common/enum/goods'
|
||||
import Recommended from '@/components/recommended'
|
||||
import ShareSheet from '@/components/share-sheet'
|
||||
import CustomerBtn from '@/components/customer-btn'
|
||||
import SlideImage from './components/SlideImage'
|
||||
import SkuPopup from './components/SkuPopup'
|
||||
import Comment from './components/Comment'
|
||||
import Service from './components/Service'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Recommended,
|
||||
ShareSheet,
|
||||
CustomerBtn,
|
||||
SlideImage,
|
||||
SkuPopup,
|
||||
Comment,
|
||||
Service
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 当前商品ID
|
||||
goodsId: null,
|
||||
// 商品详情
|
||||
goods: {},
|
||||
// 购物车总数量
|
||||
cartTotal: 0,
|
||||
// 显示/隐藏SKU弹窗
|
||||
showSkuPopup: false,
|
||||
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
|
||||
skuMode: 1,
|
||||
// 显示/隐藏分享菜单
|
||||
showShareSheet: false,
|
||||
// 获取商品海报图api方法
|
||||
posterApiCall: GoodsApi.poster,
|
||||
// 是否支持加入购物车
|
||||
isEnableCart: false,
|
||||
// 是否显示在线客服按钮
|
||||
isShowCustomerBtn: false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
async onLoad(options) {
|
||||
// 记录query参数
|
||||
this.onRecordQuery(options)
|
||||
// 加载页面数据
|
||||
this.onRefreshPage()
|
||||
// 是否显示在线客服按钮
|
||||
this.isShowCustomerBtn = await SettingModel.isShowCustomerBtn()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 记录query参数
|
||||
onRecordQuery(query) {
|
||||
const scene = getSceneData(query)
|
||||
this.goodsId = query.goodsId ? parseInt(query.goodsId) : parseInt(scene.gid)
|
||||
},
|
||||
|
||||
// 刷新页面数据
|
||||
onRefreshPage() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([app.getGoodsDetail(), app.getCartTotal()])
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取商品信息
|
||||
getGoodsDetail() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
GoodsApi.detail(app.goodsId)
|
||||
.then(result => {
|
||||
app.goods = result.data.detail
|
||||
if (app.goods.goods_type == GoodsTypeEnum.PHYSICAL.value) {
|
||||
app.isEnableCart = true
|
||||
}
|
||||
resolve(result)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取购物车总数量
|
||||
getCartTotal() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
CartApi.total()
|
||||
.then(result => {
|
||||
app.cartTotal = result.data.cartTotal
|
||||
resolve(result)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 更新购物车数量
|
||||
onAddCart(total) {
|
||||
this.cartTotal = total
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示/隐藏SKU弹窗
|
||||
* @param {skuMode} 模式 1:都显示 2:只显示购物车 3:只显示立即购买
|
||||
*/
|
||||
onShowSkuPopup(skuMode = 1) {
|
||||
const app = this
|
||||
if (app.isEnableCart) {
|
||||
app.skuMode = skuMode
|
||||
} else {
|
||||
app.skuMode = 3
|
||||
}
|
||||
app.showSkuPopup = !app.showSkuPopup
|
||||
},
|
||||
|
||||
// 显示隐藏分享菜单
|
||||
onShowShareSheet() {
|
||||
this.showShareSheet = !this.showShareSheet
|
||||
},
|
||||
|
||||
// 跳转到首页
|
||||
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>
|
||||
Executable
+463
@@ -0,0 +1,463 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ native: true }" @down="downCallback" :up="upOption"
|
||||
@up="upCallback">
|
||||
<!-- 页面头部 -->
|
||||
<view class="header">
|
||||
<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>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
|
||||
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
|
||||
import * as GoodsApi from '@/api/goods'
|
||||
import { 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
|
||||
// 设置默认列表显示方式
|
||||
this.setShowView()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 上拉加载的回调 (页面初始化时也会执行一次)
|
||||
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
|
||||
* @param {Object} page
|
||||
*/
|
||||
upCallback(page) {
|
||||
const app = this
|
||||
// 设置列表数据
|
||||
app.getGoodsList(page.num)
|
||||
.then(list => {
|
||||
const curPageLen = list.data.length
|
||||
const totalSize = list.data.total
|
||||
app.mescroll.endBySize(curPageLen, totalSize)
|
||||
})
|
||||
.catch(() => app.mescroll.endErr())
|
||||
},
|
||||
|
||||
// 设置默认列表显示方式
|
||||
setShowView() {
|
||||
this.showView = uni.getStorageSync(showViewKey) || false
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取商品列表
|
||||
* @param {number} pageNo 页码
|
||||
*/
|
||||
getGoodsList(pageNo = 1) {
|
||||
const app = this
|
||||
console.log(app.options)
|
||||
const param = {
|
||||
sortType: app.sortType,
|
||||
sortPrice: Number(app.sortPrice),
|
||||
categoryId: app.options.categoryId || 0,
|
||||
goodsName: app.options.search || '',
|
||||
page: pageNo
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
GoodsApi.list(param)
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.list.data = getMoreListData(newList, app.list, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 切换排序方式
|
||||
handleSortType(newSortType) {
|
||||
const app = this
|
||||
const newSortPrice = newSortType === 'price' ? !app.sortPrice : true
|
||||
app.sortType = newSortType
|
||||
app.sortPrice = newSortPrice
|
||||
// 刷新列表数据
|
||||
app.list = getEmptyPaginateObj()
|
||||
app.mescroll.resetUpScroll()
|
||||
},
|
||||
|
||||
// 切换列表显示方式
|
||||
handleShowView() {
|
||||
const app = this
|
||||
app.showView = !app.showView
|
||||
uni.setStorageSync(showViewKey, app.showView)
|
||||
},
|
||||
|
||||
// 跳转商品详情页
|
||||
onTargetDetail(goodsId) {
|
||||
this.$navTo('pages/goods/detail', { goodsId })
|
||||
},
|
||||
|
||||
/**
|
||||
* 商品搜索
|
||||
*/
|
||||
handleSearch() {
|
||||
const searchPageUrl = 'pages/search/index'
|
||||
// 判断来源页面
|
||||
let pages = getCurrentPages()
|
||||
if (pages.length > 1 &&
|
||||
pages[pages.length - 2].route === searchPageUrl) {
|
||||
uni.navigateBack()
|
||||
return
|
||||
}
|
||||
// 跳转到商品搜索页
|
||||
this.$navTo(searchPageUrl)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 设置分享内容
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
// 构建分享参数
|
||||
return {
|
||||
title: "全部分类",
|
||||
path: "/pages/category/index?" + this.$getShareUrlParams()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享到朋友圈
|
||||
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta)
|
||||
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
|
||||
*/
|
||||
onShareTimeline() {
|
||||
// 构建分享参数
|
||||
return {
|
||||
title: "全部分类",
|
||||
path: "/pages/category/index?" + this.$getShareUrlParams()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 页面头部
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
|
||||
// 搜索框
|
||||
.search {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
// 切换显示方式
|
||||
.show-view {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
font-size: 36rpx;
|
||||
color: #505050;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
// 排序组件
|
||||
.store-sort {
|
||||
position: sticky;
|
||||
top: var(--window-top);
|
||||
display: flex;
|
||||
padding: 20rpx 0;
|
||||
font-size: 28rpx;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
z-index: 99;
|
||||
|
||||
.sort-item {
|
||||
flex-basis: 33.3333%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 50rpx;
|
||||
|
||||
&.active {
|
||||
color: $main-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-item-price .price-arrow {
|
||||
margin-left: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #000;
|
||||
|
||||
.icon {
|
||||
&.active {
|
||||
color: $main-bg;
|
||||
}
|
||||
|
||||
&.up {
|
||||
margin-bottom: -16rpx;
|
||||
}
|
||||
|
||||
&.down {
|
||||
margin-top: -16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 商品列表
|
||||
.goods-list {
|
||||
padding: 4rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// 单列显示
|
||||
.goods-list.column-1 {
|
||||
.goods-item {
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
margin-bottom: 12rpx;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
line-height: 1.6;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-item_left {
|
||||
display: flex;
|
||||
width: 300rpx;
|
||||
background: #fff;
|
||||
align-items: center;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 240rpx;
|
||||
height: 240rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-item_right {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
|
||||
.goods-name {
|
||||
margin-top: 10rpx;
|
||||
min-height: 68rpx;
|
||||
line-height: 1.3;
|
||||
white-space: normal;
|
||||
color: #484848;
|
||||
font-size: 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: $main-bg;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.price_y {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 平铺显示
|
||||
.goods-list.column-2 {
|
||||
.goods-item {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
float: left;
|
||||
box-sizing: border-box;
|
||||
padding: 6rpx;
|
||||
|
||||
.goods-image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
margin-top: 100%;
|
||||
}
|
||||
|
||||
.image {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.detail {
|
||||
padding: 8rpx;
|
||||
background: #fff;
|
||||
|
||||
.goods-name {
|
||||
min-height: 68rpx;
|
||||
line-height: 1.3;
|
||||
white-space: normal;
|
||||
color: #484848;
|
||||
font-size: 26rpx;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.detail-price {
|
||||
.goods-price {
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.line-price {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+214
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<goods-sku-popup :value="value" @input="onChangeValue" border-radius="20" :localdata="goodsInfo" :mode="skuMode" :maskCloseAble="true"
|
||||
:priceColor="appTheme.mainBg" :buyNowBackgroundColor="appTheme.mainBg" :addCartColor="appTheme.viceText" :addCartBackgroundColor="appTheme.viceBg"
|
||||
:activedStyle="{ color: appTheme.mainBg, borderColor: appTheme.mainBg, backgroundColor: activedBtnBackgroundColor }"
|
||||
@open="openSkuPopup" @close="closeSkuPopup" @buy-now="buyNow" buyNowText="立即购买" :maxBuyNum="maxBuyNum" :buyMode="buyMode" :isJoin="!!taskId"
|
||||
:stepPeople="stepPeople" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { hex2rgba } from '@/utils/color'
|
||||
import * as TaskApi from '@/api/bargain/task'
|
||||
import GoodsSkuPopup from './goods-sku-popup'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GoodsSkuPopup
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'input'
|
||||
},
|
||||
props: {
|
||||
// 显示隐藏
|
||||
value: {
|
||||
Type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
|
||||
skuMode: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 商品详情信息
|
||||
goods: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
// 购买模式 1:拼团购买 2:单独购买
|
||||
buyMode: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 拼单ID (仅参与拼单时传入)
|
||||
taskId: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
// 阶梯团人数 (仅参与拼单时传入)
|
||||
stepPeople: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
// 规格按钮选中时的背景色
|
||||
activedBtnBackgroundColor() {
|
||||
return hex2rgba(this.appTheme.mainBg, 0.1)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
buyMode(val) {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 商品信息
|
||||
goodsInfo: {},
|
||||
// 限购数量
|
||||
maxBuyNum: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 初始化SKU数据
|
||||
init() {
|
||||
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(),
|
||||
active_type: goods.active_type,
|
||||
steps_config: goods.steps_config,
|
||||
}
|
||||
app.maxBuyNum = app.getMaxBuyNum()
|
||||
},
|
||||
|
||||
// 监听组件显示隐藏
|
||||
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.groupon_price,
|
||||
stock: item.stock_num,
|
||||
spec_value_ids: item.spec_value_ids,
|
||||
sku_name_arr: app.getSkuNameArr(item.spec_value_ids),
|
||||
groupon_price: item.groupon_price,
|
||||
original_price: item.original_price,
|
||||
steps_price_config: item.steps_price_config,
|
||||
})
|
||||
})
|
||||
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, steps_config } } = 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
|
||||
},
|
||||
|
||||
// 限购数量
|
||||
getMaxBuyNum() {
|
||||
const { goods, buyMode } = this
|
||||
return (buyMode == 1 && goods.is_restrict) ? goods.restrict_single : null
|
||||
},
|
||||
|
||||
// sku组件 开始-----------------------------------------------------------
|
||||
openSkuPopup() {
|
||||
// console.log("监听 - 打开sku组件")
|
||||
},
|
||||
|
||||
closeSkuPopup() {
|
||||
// console.log("监听 - 关闭sku组件")
|
||||
},
|
||||
|
||||
// 立即购买
|
||||
buyNow(selectShop) {
|
||||
const app = this
|
||||
// 跳转到订单结算页
|
||||
app.$navTo('pages/checkout/index', app.getOrderParam(selectShop))
|
||||
// 隐藏当前弹窗
|
||||
app.onChangeValue(false)
|
||||
},
|
||||
|
||||
// 生成下单参数
|
||||
getOrderParam(selectShop) {
|
||||
const { goods, buyMode, taskId } = this
|
||||
const param = {
|
||||
goodsSkuId: selectShop.goods_sku_id,
|
||||
goodsNum: selectShop.buy_num
|
||||
}
|
||||
if (buyMode == 1) {
|
||||
param.mode = 'groupon'
|
||||
param.grouponGoodsId = goods.groupon_goods_id
|
||||
param.taskId = taskId
|
||||
param.stepPeople = selectShop.stepPeople
|
||||
} else {
|
||||
param.mode = 'buyNow'
|
||||
param.goodsId = goods.goods_id
|
||||
}
|
||||
return param
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
Executable
+193
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<!-- 进行中的团购 -->
|
||||
<view v-if="list.length" class="goods-task m-top20" :style="appThemeStyle">
|
||||
<view class="item-title dis-flex">
|
||||
<view class="block-left flex-box">
|
||||
<text>进行中的团购</text>
|
||||
</view>
|
||||
<view class="block-right">
|
||||
<text class="iconfont icon-arrow-right col-9" @click="onShowMore()"></text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 拼单列表 -->
|
||||
<view class="task-list">
|
||||
<view class="task-item" v-for="(item, index) in list" :key="index">
|
||||
<view class="user-info">
|
||||
<avatar-image class="user-avatar" :url="item.user.avatar_url" :width="60" />
|
||||
<text class="user-name oneline-hide">{{ item.user.nick_name }}</text>
|
||||
</view>
|
||||
<view class="task-status">
|
||||
<view class="people">
|
||||
<text>还差</text>
|
||||
<text class="col-m">{{ item.surplus_people }}人</text>
|
||||
<text>成团</text>
|
||||
</view>
|
||||
<view class="count-down">
|
||||
<text>剩余</text>
|
||||
<count-down :date="item.end_time" separator="colon" theme="text" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-action">
|
||||
<view class="button" @click="onTargetTask(item.task_id)">去参团</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 更多拼单弹窗 -->
|
||||
<u-modal v-model="showMore" title="可参与的拼单">
|
||||
<scroll-view style="max-height: 610rpx; touch-action: none;" :scroll-y="true">
|
||||
<view class="pops-content">
|
||||
<view class="task-item" v-for="(item, index) in moreList" :key="index">
|
||||
<view class="user-info">
|
||||
<avatar-image class="user-avatar" :url="item.user.avatar_url" :width="60" />
|
||||
<text class="user-name oneline-hide">{{ item.user.nick_name }}</text>
|
||||
</view>
|
||||
<view class="item-action">
|
||||
<view class="button" @click="onTargetTask(item.task_id)">去参团</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-modal>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AvatarImage from '@/components/avatar-image'
|
||||
import CountDown from '@/components/countdown'
|
||||
import * as TaskApi from '@/api/groupon/task'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AvatarImage,
|
||||
CountDown
|
||||
},
|
||||
props: {
|
||||
// 拼团商品ID
|
||||
grouponGoodsId: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
// 商品ID
|
||||
list: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 显示更多拼单
|
||||
showMore: false,
|
||||
// 更多拼单列表
|
||||
moreList: []
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 显示更多拼单
|
||||
onShowMore() {
|
||||
const app = this
|
||||
if (!app.moreList.length) {
|
||||
TaskApi.listByGoods(app.grouponGoodsId)
|
||||
.then(result => {
|
||||
app.moreList = result.data.list
|
||||
app.showMore = true
|
||||
})
|
||||
} else {
|
||||
app.showMore = true
|
||||
}
|
||||
},
|
||||
|
||||
// 跳转到评论列表页
|
||||
onTargetTask(taskId) {
|
||||
this.$navTo('pages/groupon/task/index', { taskId })
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-task {
|
||||
padding: 20rpx 30rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.task-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 6rpx;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.user-info {
|
||||
width: 260rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.user-avatar {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.task-status {
|
||||
width: 250rpx;
|
||||
padding-left: 36rpx;
|
||||
font-size: 26rpx;
|
||||
|
||||
.people {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.count-down {
|
||||
display: flex;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.item-action {
|
||||
.button {
|
||||
padding: 0 24rpx;
|
||||
line-height: 52rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
border-radius: 40rpx;
|
||||
color: #fff;
|
||||
background: $main-bg;
|
||||
}
|
||||
}
|
||||
|
||||
// 更多拼单
|
||||
.pops-content {
|
||||
padding: 40rpx 30rpx;
|
||||
|
||||
.task-item {
|
||||
margin-bottom: 44rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
+1453
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,450 @@
|
||||
<!-- 步进器 -->
|
||||
<template>
|
||||
<view class="number-box">
|
||||
<view class="u-icon-minus" @touchstart.prevent="btnTouchStart('minus')" @touchend.stop.prevent="clearTimer" :class="{ 'u-icon-disabled': disabled || inputVal <= min }"
|
||||
:style="{
|
||||
background: bgColor,
|
||||
height: inputHeight + 'rpx',
|
||||
color: color,
|
||||
fontSize: size + 'rpx',
|
||||
minHeight: '1.4em'
|
||||
}">
|
||||
<view :style="'font-size:'+(Number(size)+10)+'rpx'" class="num-btn">-</view>
|
||||
</view>
|
||||
<input :disabled="disabledInput || disabled" :cursor-spacing="getCursorSpacing" :class="{ 'u-input-disabled': disabled }"
|
||||
v-model="inputVal" class="u-number-input" @blur="onBlur"
|
||||
type="number" :style="{
|
||||
color: color,
|
||||
fontSize: size + 'rpx',
|
||||
background: bgColor,
|
||||
height: inputHeight + 'rpx',
|
||||
width: inputWidth + 'rpx',
|
||||
}" />
|
||||
<view class="u-icon-plus" @touchstart.prevent="btnTouchStart('plus')" @touchend.stop.prevent="clearTimer" :class="{ 'u-icon-disabled': disabled || inputVal >= max }"
|
||||
:style="{
|
||||
background: bgColor,
|
||||
height: inputHeight + 'rpx',
|
||||
color: color,
|
||||
fontSize: size + 'rpx',
|
||||
minHeight: '1.4em',
|
||||
}">
|
||||
<view :style="'font-size:'+(Number(size)+10)+'rpx'" class="num-btn">+</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
/**
|
||||
* numberBox 步进器
|
||||
* @description 该组件一般用于商城购物选择物品数量的场景。注意:该输入框只能输入大于或等于0的整数,不支持小数输入
|
||||
* @tutorial https://www.uviewui.com/components/numberBox.html
|
||||
* @property {Number} value 输入框初始值(默认1)
|
||||
* @property {String} bg-color 输入框和按钮的背景颜色(默认#F2F3F5)
|
||||
* @property {Number} min 用户可输入的最小值(默认0)
|
||||
* @property {Number} max 用户可输入的最大值(默认99999)
|
||||
* @property {Number} step 步长,每次加或减的值(默认1)
|
||||
* @property {Number} stepFirst 步进值,首次增加或最后减的值(默认step值和一致)
|
||||
* @property {Boolean} disabled 是否禁用操作,禁用后无法加减或手动修改输入框的值(默认false)
|
||||
* @property {Boolean} disabled-input 是否禁止输入框手动输入值(默认false)
|
||||
* @property {Boolean} positive-integer 是否只能输入正整数(默认true)
|
||||
* @property {String | Number} size 输入框文字和按钮字体大小,单位rpx(默认26)
|
||||
* @property {String} color 输入框文字和加减按钮图标的颜色(默认#323233)
|
||||
* @property {String | Number} input-width 输入框宽度,单位rpx(默认80)
|
||||
* @property {String | Number} input-height 输入框和按钮的高度,单位rpx(默认50)
|
||||
* @property {String | Number} index 事件回调时用以区分当前发生变化的是哪个输入框
|
||||
* @property {Boolean} long-press 是否开启长按连续递增或递减(默认true)
|
||||
* @property {String | Number} press-time 开启长按触发后,每触发一次需要多久,单位ms(默认250)
|
||||
* @property {String | Number} cursor-spacing 指定光标于键盘的距离,避免键盘遮挡输入框,单位rpx(默认200)
|
||||
* @event {Function} change 输入框内容发生变化时触发,对象形式
|
||||
* @event {Function} blur 输入框失去焦点时触发,对象形式
|
||||
* @event {Function} minus 点击减少按钮时触发(按钮可点击情况下),对象形式
|
||||
* @event {Function} plus 点击增加按钮时触发(按钮可点击情况下),对象形式
|
||||
* @example <number-box :min="1" :max="100"></number-box>
|
||||
*/
|
||||
export default {
|
||||
name: "NumberBox",
|
||||
emits: ["update:modelValue", "input", "change", "blur", "plus", "minus"],
|
||||
props: {
|
||||
// 预显示的数字
|
||||
value: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
modelValue: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#F2F3F5'
|
||||
},
|
||||
// 最小值
|
||||
min: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 最大值
|
||||
max: {
|
||||
type: Number,
|
||||
default: 99999
|
||||
},
|
||||
// 步进值,每次加或减的值
|
||||
step: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 步进值,首次增加或最后减的值
|
||||
stepFirst: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 是否只能输入 step 的倍数
|
||||
stepStrictly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否禁用加减操作
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// input的字体大小,单位rpx
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 26
|
||||
},
|
||||
// 加减图标的颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: '#323233'
|
||||
},
|
||||
// input宽度,单位rpx
|
||||
inputWidth: {
|
||||
type: [Number, String],
|
||||
default: 80
|
||||
},
|
||||
// input高度,单位rpx
|
||||
inputHeight: {
|
||||
type: [Number, String],
|
||||
default: 50
|
||||
},
|
||||
// index索引,用于列表中使用,让用户知道是哪个numberbox发生了变化,一般使用for循环出来的index值即可
|
||||
index: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 是否禁用输入框,与disabled作用于输入框时,为OR的关系,即想要禁用输入框,又可以加减的话
|
||||
// 设置disabled为false,disabledInput为true即可
|
||||
disabledInput: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 输入框于键盘之间的距离
|
||||
cursorSpacing: {
|
||||
type: [Number, String],
|
||||
default: 100
|
||||
},
|
||||
// 是否开启长按连续递增或递减
|
||||
longPress: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 开启长按触发后,每触发一次需要多久
|
||||
pressTime: {
|
||||
type: [Number, String],
|
||||
default: 250
|
||||
},
|
||||
// 是否只能输入大于或等于0的整数(正整数)
|
||||
positiveInteger: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(v1, v2) {
|
||||
// 只有value的改变是来自外部的时候,才去同步inputVal的值,否则会造成循环错误
|
||||
if(!this.changeFromInner) {
|
||||
this.inputVal = v1;
|
||||
// 因为inputVal变化后,会触发this.handleChange(),在其中changeFromInner会再次被设置为true,
|
||||
// 造成外面修改值,也导致被认为是内部修改的混乱,这里进行this.$nextTick延时,保证在运行周期的最后处
|
||||
// 将changeFromInner设置为false
|
||||
this.$nextTick(function(){
|
||||
this.changeFromInner = false;
|
||||
})
|
||||
}
|
||||
},
|
||||
modelValue(v1, v2) {
|
||||
// 只有value的改变是来自外部的时候,才去同步inputVal的值,否则会造成循环错误
|
||||
if(!this.changeFromInner) {
|
||||
this.inputVal = v1;
|
||||
// 因为inputVal变化后,会触发this.handleChange(),在其中changeFromInner会再次被设置为true,
|
||||
// 造成外面修改值,也导致被认为是内部修改的混乱,这里进行this.$nextTick延时,保证在运行周期的最后处
|
||||
// 将changeFromInner设置为false
|
||||
this.$nextTick(function(){
|
||||
this.changeFromInner = false;
|
||||
})
|
||||
}
|
||||
},
|
||||
inputVal(v1, v2) {
|
||||
// 为了让用户能够删除所有输入值,重新输入内容,删除所有值后,内容为空字符串
|
||||
if (v1 == '') return;
|
||||
let value = 0;
|
||||
// 首先判断是否数值,并且在min和max之间,如果不是,使用原来值
|
||||
let tmp = this.isNumber(v1);
|
||||
if (tmp && v1 >= this.min && v1 <= this.max) value = v1;
|
||||
else value = v2;
|
||||
// 判断是否只能输入大于等于0的整数
|
||||
if(this.positiveInteger) {
|
||||
// 小于0,或者带有小数点,
|
||||
if(v1 < 0 || String(v1).indexOf('.') !== -1) {
|
||||
value = v2;
|
||||
// 双向绑定input的值,必须要使用$nextTick修改显示的值
|
||||
this.$nextTick(() => {
|
||||
this.inputVal = v2;
|
||||
})
|
||||
}
|
||||
}
|
||||
// 发出change事件
|
||||
this.handleChange(value, 'change');
|
||||
},
|
||||
min(v1){
|
||||
if(v1 !== undefined && v1!="" && this.getValue() < v1){
|
||||
this.$emit("input",v1);
|
||||
}
|
||||
},
|
||||
max(v1){
|
||||
if(v1 !== undefined && v1!="" && this.getValue() > v1){
|
||||
this.$emit("input",v1);
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputVal: 1, // 输入框中的值,不能直接使用props中的value,因为应该改变props的状态
|
||||
timer: null, // 用作长按的定时器
|
||||
changeFromInner: false, // 值发生变化,是来自内部还是外部
|
||||
innerChangeTimer: null, // 内部定时器
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.inputVal = Number(this.getValue());
|
||||
},
|
||||
computed: {
|
||||
getCursorSpacing() {
|
||||
// 先将值转为px单位,再转为数值
|
||||
return Number(uni.upx2px(this.cursorSpacing));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getValue(){
|
||||
// #ifndef VUE3
|
||||
return this.value;
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
return this.modelValue;
|
||||
// #endif
|
||||
},
|
||||
// 点击退格键
|
||||
btnTouchStart(callback) {
|
||||
// 先执行一遍方法,否则会造成松开手时,就执行了clearTimer,导致无法实现功能
|
||||
this[callback]();
|
||||
// 如果没开启长按功能,直接返回
|
||||
if (!this.longPress) return;
|
||||
clearInterval(this.timer); //再次清空定时器,防止重复注册定时器
|
||||
this.timer = null;
|
||||
this.timer = setInterval(() => {
|
||||
// 执行加或减函数
|
||||
this[callback]();
|
||||
}, this.pressTime);
|
||||
},
|
||||
clearTimer() {
|
||||
this.$nextTick(() => {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
})
|
||||
},
|
||||
minus() {
|
||||
this.computeVal('minus');
|
||||
},
|
||||
plus() {
|
||||
this.computeVal('plus');
|
||||
},
|
||||
// 为了保证小数相加减出现精度溢出的问题
|
||||
calcPlus(num1, num2) {
|
||||
let baseNum, baseNum1, baseNum2;
|
||||
try {
|
||||
baseNum1 = num1.toString().split('.')[1].length;
|
||||
} catch (e) {
|
||||
baseNum1 = 0;
|
||||
}
|
||||
try {
|
||||
baseNum2 = num2.toString().split('.')[1].length;
|
||||
} catch (e) {
|
||||
baseNum2 = 0;
|
||||
}
|
||||
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
|
||||
let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2; //精度
|
||||
return ((num1 * baseNum + num2 * baseNum) / baseNum).toFixed(precision);
|
||||
},
|
||||
// 为了保证小数相加减出现精度溢出的问题
|
||||
calcMinus(num1, num2) {
|
||||
let baseNum, baseNum1, baseNum2;
|
||||
try {
|
||||
baseNum1 = num1.toString().split('.')[1].length;
|
||||
} catch (e) {
|
||||
baseNum1 = 0;
|
||||
}
|
||||
try {
|
||||
baseNum2 = num2.toString().split('.')[1].length;
|
||||
} catch (e) {
|
||||
baseNum2 = 0;
|
||||
}
|
||||
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
|
||||
let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2;
|
||||
return ((num1 * baseNum - num2 * baseNum) / baseNum).toFixed(precision);
|
||||
},
|
||||
computeVal(type) {
|
||||
uni.hideKeyboard();
|
||||
if (this.disabled) return;
|
||||
let value = 0;
|
||||
// 新增stepFirst开始
|
||||
// 减
|
||||
if (type === 'minus') {
|
||||
if(this.stepFirst > 0 && this.inputVal == this.stepFirst){
|
||||
value = this.min;
|
||||
}else{
|
||||
value = this.calcMinus(this.inputVal, this.step);
|
||||
}
|
||||
} else if (type === 'plus') {
|
||||
if(this.stepFirst > 0 && this.inputVal < this.stepFirst){
|
||||
value = this.stepFirst;
|
||||
}else{
|
||||
value = this.calcPlus(this.inputVal, this.step);
|
||||
}
|
||||
}
|
||||
if(this.stepStrictly){
|
||||
let strictly = value % this.step;
|
||||
if(strictly > 0){
|
||||
value -= strictly;
|
||||
}
|
||||
}
|
||||
if (value > this.max ) {
|
||||
value = this.max;
|
||||
}else if (value < this.min) {
|
||||
value = this.min;
|
||||
}
|
||||
// 新增stepFirst结束
|
||||
this.inputVal = value;
|
||||
this.handleChange(value, type);
|
||||
},
|
||||
// 处理用户手动输入的情况
|
||||
onBlur(event) {
|
||||
let val = 0;
|
||||
let value = event.detail.value;
|
||||
// 如果为非0-9数字组成,或者其第一位数值为0,直接让其等于min值
|
||||
// 这里不直接判断是否正整数,是因为用户传递的props min值可能为0
|
||||
if (!/(^\d+$)/.test(value) || value[0] == 0) val = this.min;
|
||||
val = +value;
|
||||
|
||||
// 新增stepFirst开始
|
||||
if(this.stepFirst > 0 && this.inputVal < this.stepFirst && this.inputVal>0){
|
||||
val = this.stepFirst;
|
||||
}
|
||||
// 新增stepFirst结束
|
||||
if(this.stepStrictly){
|
||||
let strictly = val % this.step;
|
||||
if(strictly > 0){
|
||||
val -= strictly;
|
||||
}
|
||||
}
|
||||
if (val > this.max) {
|
||||
val = this.max;
|
||||
} else if (val < this.min) {
|
||||
val = this.min;
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.inputVal = val;
|
||||
})
|
||||
this.handleChange(val, 'blur');
|
||||
},
|
||||
handleChange(value, type) {
|
||||
if (this.disabled) return;
|
||||
// 清除定时器,避免造成混乱
|
||||
if(this.innerChangeTimer) {
|
||||
clearTimeout(this.innerChangeTimer);
|
||||
this.innerChangeTimer = null;
|
||||
}
|
||||
// 发出input事件,修改通过v-model绑定的值,达到双向绑定的效果
|
||||
this.changeFromInner = true;
|
||||
// 一定时间内,清除changeFromInner标记,否则内部值改变后
|
||||
// 外部通过程序修改value值,将会无效
|
||||
this.innerChangeTimer = setTimeout(() => {
|
||||
this.changeFromInner = false;
|
||||
}, 150);
|
||||
this.$emit('input', Number(value));
|
||||
this.$emit("update:modelValue", Number(value));
|
||||
this.$emit(type, {
|
||||
// 转为Number类型
|
||||
value: Number(value),
|
||||
index: this.index
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 验证十进制数字
|
||||
*/
|
||||
isNumber(value) {
|
||||
return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.number-box {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-number-input {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
margin: 0 6rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.u-icon-plus,
|
||||
.u-icon-minus {
|
||||
width: 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-icon-plus {
|
||||
border-radius: 0 8rpx 8rpx 0;
|
||||
}
|
||||
|
||||
.u-icon-minus {
|
||||
border-radius: 8rpx 0 0 8rpx;
|
||||
}
|
||||
|
||||
.u-icon-disabled {
|
||||
color: #c8c9cc !important;
|
||||
background: #f7f8fa !important;
|
||||
}
|
||||
|
||||
.u-input-disabled {
|
||||
color: #c8c9cc !important;
|
||||
background-color: #f2f3f5 !important;
|
||||
}
|
||||
.num-btn{
|
||||
font-weight:550;
|
||||
position: relative;
|
||||
top:-4rpx;
|
||||
}
|
||||
|
||||
</style>
|
||||
Executable
+382
@@ -0,0 +1,382 @@
|
||||
<template>
|
||||
<view v-show="!isLoading" class="container" :style="appThemeStyle">
|
||||
<!-- 商品图片轮播 -->
|
||||
<SlideImage v-if="!isLoading" :video="goods.video" :videoCover="goods.videoCover" :images="goods.goods_images" />
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view v-if="!isLoading" class="goods-info m-top20">
|
||||
<!-- 价格、销量 -->
|
||||
<view class="info-item info-item__top dis-flex flex-x-between flex-y-end">
|
||||
<view class="block-left dis-flex flex-y-center">
|
||||
<view class="active-tag">
|
||||
<text>{{ goods.active_type != ActiveTypeEnum.NORMAL.value ? ActiveTypeEnum[goods.active_type].name2 : '多人拼团' }}</text>
|
||||
</view>
|
||||
<!-- 拼团价 -->
|
||||
<text class="floor-price__samll">¥</text>
|
||||
<text class="floor-price">{{ goods.groupon_price }}</text>
|
||||
<!-- 商品原价 -->
|
||||
<text class="original-price">¥{{ goods.original_price }}</text>
|
||||
</view>
|
||||
<view class="block-right dis-flex">
|
||||
<!-- 销量 -->
|
||||
<view class="goods-sales">
|
||||
<text>已抢{{ goods.active_sales }}件</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 标题、分享 -->
|
||||
<view class="info-item info-item__name dis-flex flex-y-center">
|
||||
<view class="goods-name flex-box">
|
||||
<text class="twoline-hide">{{ goods.goods_name }}</text>
|
||||
</view>
|
||||
<view class="goods-share__line"></view>
|
||||
<view class="goods-share">
|
||||
<button class="share-btn dis-flex flex-dir-column" @click="onShowShareSheet()">
|
||||
<text class="share__icon iconfont icon-fenxiang"></text>
|
||||
<text class="f-24">分享</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 商品卖点 -->
|
||||
<view v-if="goods.selling_point" class="info-item info-item_selling-point">
|
||||
<text>{{ goods.selling_point }}</text>
|
||||
</view>
|
||||
<!-- 活动倒计时 -->
|
||||
<view v-if="goods.active_status != ActiveStatusEnum.STATE_END.value" class="info-item info-item_status info-item_countdown dis-flex flex-y-center">
|
||||
<text class="countdown-icon iconfont icon-naozhong"></text>
|
||||
<text>距离拼团活动{{ goods.active_status == ActiveStatusEnum.STATE_SOON.value ? '开始' : '结束' }}</text>
|
||||
<text class="m-r-10">还剩</text>
|
||||
<count-down :date="goods.end_time" separator="zh" theme="text" />
|
||||
</view>
|
||||
<!-- 活动已结束 -->
|
||||
<view v-else class="info-item info-item_status info-item_end">
|
||||
<text class="countdown-icon iconfont icon-naozhong"></text>
|
||||
<text>拼团活动已结束,下次记得早点来哦~</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 凑团信息 -->
|
||||
<TaskList v-if="!isLoading" :grouponGoodsId="goods.groupon_goods_id" :list="goods.taskQuickJoinList" />
|
||||
|
||||
<!-- 选择商品规格 -->
|
||||
<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>
|
||||
|
||||
<!-- 拼团玩法 -->
|
||||
<view class="rule-nav m-top20 b-f" @click="handleShowRules()">
|
||||
<view class="top-nav dis-flex flex-x-between">
|
||||
<text>拼团玩法</text>
|
||||
<text class="f-25 col-9">查看规则</text>
|
||||
</view>
|
||||
<!-- 拼团步骤 -->
|
||||
<view class="rule-simple dis-flex flex-x-around">
|
||||
<view class="simple-item dis-flex flex-dir-column flex-y-center">
|
||||
<view class="i-number dis-flex flex-x-center flex-y-center">
|
||||
<text class="f-30">1</text>
|
||||
</view>
|
||||
<view class="i-text f-28">选择商品</view>
|
||||
</view>
|
||||
<view class="simple-item dis-flex flex-dir-column flex-y-center">
|
||||
<view class="i-number dis-flex flex-x-center flex-y-center">
|
||||
<text class="f-30">2</text>
|
||||
</view>
|
||||
<view class="i-text f-28">开团/参团</view>
|
||||
</view>
|
||||
<view class="simple-item dis-flex flex-dir-column flex-y-center">
|
||||
<view class="i-number dis-flex flex-x-center flex-y-center">
|
||||
<text class="f-30">3</text>
|
||||
</view>
|
||||
<view class="i-text f-28">邀请好友</view>
|
||||
</view>
|
||||
<view class="simple-item dis-flex flex-dir-column flex-y-center">
|
||||
<view class="i-number dis-flex flex-x-center flex-y-center">
|
||||
<text class="f-30">4</text>
|
||||
</view>
|
||||
<view class="i-text f-28">人满成团</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品SKU弹窗 -->
|
||||
<SkuPopup v-if="!isLoading" v-model="showSkuPopup" :skuMode="skuMode" :goods="goods" :buyMode="buyMode" />
|
||||
|
||||
<!-- 商品评价 -->
|
||||
<Comment v-if="!isLoading" :goods-id="goods.goods_id" :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>
|
||||
<!-- 客服 -->
|
||||
<customer-btn v-if="isShowCustomerBtn">
|
||||
<view class="fast-item">
|
||||
<view class="fast-icon">
|
||||
<text class="iconfont icon-kefu1"></text>
|
||||
</view>
|
||||
<view class="fast-text">
|
||||
<text>客服</text>
|
||||
</view>
|
||||
</view>
|
||||
</customer-btn>
|
||||
<!-- 购物车 (客服按钮不显示时) -->
|
||||
<view v-if="!isShowCustomerBtn" 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">
|
||||
<block v-if="goods.active_status == ActiveStatusEnum.STATE_BEGIN.value">
|
||||
<view v-if="goods.is_alone_buy" class="btn-item btn-item-deputy" @click="onShowSkuPopup(2)">
|
||||
<view class="price">¥{{ goods.original_price }}</view>
|
||||
<view>单独购买</view>
|
||||
</view>
|
||||
<view class="btn-item btn-item-main" @click="onShowSkuPopup(1)">
|
||||
<view class="price">¥{{ goods.groupon_price }}</view>
|
||||
<view>发起拼团</view>
|
||||
</view>
|
||||
</block>
|
||||
<view v-else class="btn-item btn-item-gray">
|
||||
<text>{{ goods.active_status == ActiveStatusEnum.STATE_SOON.value ? '活动未开始' : '活动已结束' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分享菜单 -->
|
||||
<share-sheet v-model="showShareSheet" :shareTitle="goods.goods_name" :shareImageUrl="goods.goods_image" :posterApiCall="posterApiCall" :posterApiParam="{ grouponGoodsId }" />
|
||||
|
||||
<!-- 拼团规则弹窗 -->
|
||||
<u-modal v-if="!isLoading" v-model="showRules" title="拼团规则">
|
||||
<scroll-view style="height: 610rpx; touch-action: none;" :scroll-y="true">
|
||||
<view class="pops-content">
|
||||
<text>{{ setting.ruleDetail }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-modal>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSceneData } from '@/core/app'
|
||||
import ShareSheet from '@/components/share-sheet'
|
||||
import CustomerBtn from '@/components/customer-btn'
|
||||
import SkuPopup from './components/SkuPopup'
|
||||
import TaskList from './components/TaskList'
|
||||
import SlideImage from '../../goods/components/SlideImage'
|
||||
import Comment from '../../goods/components/Comment'
|
||||
import CountDown from '@/components/countdown'
|
||||
import * as GrouponGoodsApi from '@/api/groupon/goods'
|
||||
import * as CartApi from '@/api/cart'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
import { ActiveTypeEnum, ActiveStatusEnum } from '@/common/enum/groupon'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ShareSheet,
|
||||
CustomerBtn,
|
||||
SlideImage,
|
||||
TaskList,
|
||||
SkuPopup,
|
||||
Comment,
|
||||
CountDown
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 枚举类
|
||||
ActiveTypeEnum,
|
||||
ActiveStatusEnum,
|
||||
// 显示/隐藏SKU弹窗
|
||||
showSkuPopup: false,
|
||||
// 按钮模式 1:都显示 2:只显示购物车 3:只显示立即购买
|
||||
skuMode: 3,
|
||||
// 购买模式 1:拼团购买 2:单独购买
|
||||
buyMode: 1,
|
||||
// 显示/隐藏分享菜单
|
||||
showShareSheet: false,
|
||||
// 获取商品海报图api方法
|
||||
posterApiCall: GrouponGoodsApi.poster,
|
||||
// 显示拼团规则
|
||||
showRules: false,
|
||||
// 拼团规则内容
|
||||
setting: {},
|
||||
// 当前拼团商品ID
|
||||
grouponGoodsId: null,
|
||||
// 拼团商品详情
|
||||
goods: {},
|
||||
// 购物车总数量
|
||||
cartTotal: 0,
|
||||
// 是否显示在线客服按钮
|
||||
isShowCustomerBtn: false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
async onLoad(options) {
|
||||
// 记录query参数
|
||||
this.onRecordQuery(options)
|
||||
// 加载页面数据
|
||||
this.onRefreshPage()
|
||||
// 是否显示在线客服按钮
|
||||
this.isShowCustomerBtn = await SettingModel.isShowCustomerBtn()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 记录query参数
|
||||
onRecordQuery(query) {
|
||||
const scene = getSceneData(query)
|
||||
this.grouponGoodsId = query.grouponGoodsId ? parseInt(query.grouponGoodsId) : parseInt(scene.gid)
|
||||
},
|
||||
|
||||
// 刷新页面数据
|
||||
onRefreshPage() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([app.getActiveDetail(), app.getCartTotal()])
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取拼团活动详情
|
||||
getActiveDetail() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
GrouponGoodsApi.detail(app.grouponGoodsId)
|
||||
.then(result => {
|
||||
app.goods = result.data.detail
|
||||
app.setting = result.data.setting
|
||||
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)
|
||||
})
|
||||
},
|
||||
|
||||
// 显示拼团规则
|
||||
handleShowRules() {
|
||||
this.showRules = true
|
||||
},
|
||||
|
||||
// 显示/隐藏SKU弹窗
|
||||
onShowSkuPopup(buyMode = 1) {
|
||||
this.buyMode = buyMode
|
||||
this.showSkuPopup = !this.showSkuPopup
|
||||
},
|
||||
|
||||
// 显示隐藏分享菜单
|
||||
onShowShareSheet() {
|
||||
this.showShareSheet = !this.showShareSheet
|
||||
},
|
||||
|
||||
// 跳转到首页
|
||||
onTargetHome(e) {
|
||||
this.$navTo('pages/index/index')
|
||||
},
|
||||
|
||||
// 跳转到购物车页
|
||||
onTargetCart() {
|
||||
this.$navTo('pages/cart/index')
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
// 构建页面参数
|
||||
const app = this
|
||||
const params = app.$getShareUrlParams({
|
||||
grouponGoodsId: app.grouponGoodsId
|
||||
})
|
||||
return {
|
||||
title: app.goods.goods_name,
|
||||
path: `/pages/groupon/goods/index?${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({
|
||||
grouponGoodsId: app.grouponGoodsId
|
||||
})
|
||||
return {
|
||||
title: app.goods.goods_name,
|
||||
path: `/pages/groupon/goods/index?${params}`
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
</style>
|
||||
Executable
+325
@@ -0,0 +1,325 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
.info-item__top .active-tag {
|
||||
color: #fff;
|
||||
background: linear-gradient(to right, #ffa600, #f5b914);
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 15rpx;
|
||||
font-size: 26rpx;
|
||||
text-align: center;
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
|
||||
.floor-price__samll {
|
||||
font-size: 26rpx;
|
||||
line-height: 1;
|
||||
color: $main-bg;
|
||||
margin-bottom: -10rpx;
|
||||
}
|
||||
|
||||
/* 商品价 */
|
||||
.floor-price {
|
||||
color: $main-bg;
|
||||
margin-right: 15rpx;
|
||||
font-size: 42rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
font-size: 26rpx;
|
||||
line-height: 1;
|
||||
text-decoration: line-through;
|
||||
color: #959595;
|
||||
margin-bottom: -6rpx;
|
||||
}
|
||||
|
||||
.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;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 0;
|
||||
color: #fff;
|
||||
|
||||
// 发起拼团
|
||||
&.btn-item-main {
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
}
|
||||
|
||||
// 单独购买
|
||||
&.btn-item-deputy {
|
||||
background: linear-gradient(to right, $vice-bg, $vice-bg2);
|
||||
color: $vice-text;
|
||||
}
|
||||
|
||||
// 活动结束
|
||||
&.btn-item-gray {
|
||||
background-color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 活动状态
|
||||
.info-item_status {
|
||||
margin-top: 20rpx;
|
||||
padding: 15rpx 20rpx;
|
||||
font-size: 24rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
.info-item_status .countdown-icon {
|
||||
font-size: 28rpx;
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
|
||||
// 活动倒计时
|
||||
.info-item_countdown {
|
||||
background: #f0f9ff;
|
||||
color: #8f8f8f;
|
||||
}
|
||||
|
||||
.info-item_countdown .countdown-icon {
|
||||
color: #1397d8;
|
||||
}
|
||||
|
||||
// 活动已结束
|
||||
.info-item_end {
|
||||
background: #ccc;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
// 拼团玩法
|
||||
.rule-nav {
|
||||
padding: 24rpx;
|
||||
font-size: 28rpx;
|
||||
|
||||
.rule-simple {
|
||||
margin-top: 35rpx;
|
||||
color: #737373;
|
||||
}
|
||||
|
||||
.i-number {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 15rpx;
|
||||
border: 1rpx dashed #c0c0c0;
|
||||
}
|
||||
}
|
||||
|
||||
// 拼团玩法
|
||||
.groupon-rules {
|
||||
padding: 20rpx 0;
|
||||
font-size: 29rpx;
|
||||
|
||||
.item-title {
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.rule-simple {
|
||||
margin-top: 35rpx;
|
||||
color: #737373;
|
||||
}
|
||||
|
||||
.i-number {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 15rpx;
|
||||
border: 1rpx dashed #c0c0c0;
|
||||
}
|
||||
}
|
||||
|
||||
// 拼团规则 (弹窗)
|
||||
.pops-content {
|
||||
padding: 30rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 44rpx;
|
||||
text-align: left;
|
||||
color: #606266;
|
||||
min-height: 320rpx;
|
||||
max-height: 640rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
Executable
+501
@@ -0,0 +1,501 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption" @up="upCallback">
|
||||
|
||||
<!-- 拼团海报图 -->
|
||||
<view v-if="curTab == 0 && setting.backdrop" class="banner">
|
||||
<image class="image" :src="setting.backdrop.src" mode="widthFix"></image>
|
||||
</view>
|
||||
|
||||
<!-- 拼团活动 -->
|
||||
<view v-if="curTab == 0" class="groupon-hall active-list">
|
||||
<view class="goods-item--container" v-for="(item, index) in goodsList.data" :key="index">
|
||||
<view class="goods-item" @click="onTargetGoods(item)">
|
||||
<!-- 商品图片 -->
|
||||
<view class="goods-item_left">
|
||||
<view v-if="item.active_type != ActiveTypeEnum.NORMAL.value" class="label">
|
||||
<text>{{ ActiveTypeEnum[item.active_type].name2 }}</text>
|
||||
</view>
|
||||
<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_situation">
|
||||
<u-tag class="people" :color="appTheme.mainBg" :border-color="appTheme.mainBg" :text="`${item.show_people}人团`"
|
||||
type="error" size="mini" mode="plain" />
|
||||
<u-tag v-if="item.active_sales" :color="appTheme.mainBg" :border-color="tagBorderColor" :bg-color="tagBackgroundColor"
|
||||
:text="`已团${item.active_sales}件`" type="error" size="mini" />
|
||||
</view>
|
||||
<view class="desc_footer">
|
||||
<view class="item-prices oneline-hide">
|
||||
<text class="price_x">¥{{ item.groupon_price }}</text>
|
||||
<text class="price_y cl-9">¥{{ item.original_price }}</text>
|
||||
</view>
|
||||
<view class="settlement">去拼团</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 我的拼团 -->
|
||||
<view v-if="curTab == 1" class="groupon-hall my-list">
|
||||
<view class="goods-item--container" v-for="(item, index) in myList.data" :key="index">
|
||||
<view class="goods-item" @click="onTargetTask(item)">
|
||||
<!-- 商品图片 -->
|
||||
<view class="goods-item_left">
|
||||
<view v-if="item.active_type != ActiveTypeEnum.NORMAL.value" class="label">
|
||||
<text>{{ ActiveTypeEnum[item.active_type].name2 }}</text>
|
||||
</view>
|
||||
<image class="image" :src="item.goods.goods_image"></image>
|
||||
</view>
|
||||
<view class="goods-item_right">
|
||||
<!-- 商品标题 -->
|
||||
<view class="goods-name">
|
||||
<text class="twoline-hide">{{ item.goods.goods_name }}</text>
|
||||
</view>
|
||||
<!-- 商品信息 -->
|
||||
<view class="goods-item_desc">
|
||||
<view class="desc_situation">
|
||||
<u-tag v-if="item.status == TaskStatusEnum.NORMAL.value" :text="`已拼${item.joined_people}人,还差${item.people - item.joined_people}人`"
|
||||
type="warning" size="mini" />
|
||||
<u-tag v-else class="people" :text="`${item.people}人团`" type="error" size="mini" mode="plain" />
|
||||
</view>
|
||||
<view class="desc_footer">
|
||||
<view class="item-status">
|
||||
<text>{{ TaskStatusEnum[item.status].name }}</text>
|
||||
</view>
|
||||
<view v-if="item.status == TaskStatusEnum.NORMAL.value" class="settlement">查看拼单</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部选项卡 -->
|
||||
<view class="footer-fixed">
|
||||
<view class="footer-container">
|
||||
<!-- 拼团活动 -->
|
||||
<view class="tabbar-item flex-box" :class="{ active: curTab == 0 }">
|
||||
<view class="tabbar-item-content dis-flex flex-x-center flex-y-center" @click="onChangeTab(0)">
|
||||
<view class="tabbar-item-icon">
|
||||
<text class="iconfont icon-shangcheng"></text>
|
||||
</view>
|
||||
<view class="tabbar-item-name">
|
||||
<text>拼团活动</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 分割线 -->
|
||||
<view class="tabbar-item__divider">
|
||||
<view class="divider-line"></view>
|
||||
</view>
|
||||
<!-- 我的砍价 -->
|
||||
<view class="tabbar-item flex-box" :class="{ active: curTab == 1 }">
|
||||
<view class="tabbar-item-content dis-flex flex-x-center flex-y-center" @click="onChangeTab(1)">
|
||||
<view class="tabbar-item-icon">
|
||||
<text class="iconfont icon-sy-yh"></text>
|
||||
</view>
|
||||
<view class="tabbar-item-name">
|
||||
<text>我的拼团</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { hex2rgba } from '@/utils/color'
|
||||
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
|
||||
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
|
||||
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
|
||||
import { ActiveStatusEnum, ActiveTypeEnum, TaskStatusEnum } from '@/common/enum/groupon'
|
||||
import * as TaskApi from '@/api/groupon/task'
|
||||
import * as GoodsApi from '@/api/groupon/goods'
|
||||
import SettingModel from '@/common/model/groupon/Setting'
|
||||
|
||||
const pageSize = 15
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
// 枚举类
|
||||
ActiveStatusEnum,
|
||||
ActiveTypeEnum,
|
||||
TaskStatusEnum,
|
||||
// 当前tab索引
|
||||
curTab: 0,
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: true,
|
||||
// 每页数据的数量; 默认10
|
||||
page: { size: pageSize },
|
||||
// 数量要大于3条才显示无更多数据
|
||||
noMoreSize: 3,
|
||||
},
|
||||
// 拼团设置
|
||||
setting: {},
|
||||
// 拼团商品列表
|
||||
goodsList: getEmptyPaginateObj(),
|
||||
// 我的拼单列表
|
||||
myList: getEmptyPaginateObj(),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
curTab(val) {
|
||||
// 设置页面标题
|
||||
uni.setNavigationBarTitle({ title: val == 0 ? '拼团活动' : '我的拼团' })
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 标签背景色
|
||||
tagBackgroundColor() {
|
||||
return hex2rgba(this.appTheme.mainBg, 0.1)
|
||||
},
|
||||
// 标签边框颜色
|
||||
tagBorderColor() {
|
||||
return hex2rgba(this.appTheme.mainBg, 0.6)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 设置当前tab索引
|
||||
if (options.tab) {
|
||||
this.curTab = options.tab
|
||||
}
|
||||
// 获取拼团设置
|
||||
this.getSetting()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取拼团设置
|
||||
getSetting() {
|
||||
SettingModel.data(true).then(setting => this.setting = setting)
|
||||
},
|
||||
|
||||
/**
|
||||
* 上拉加载的回调
|
||||
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
|
||||
* @param {Object} page
|
||||
*/
|
||||
upCallback(page) {
|
||||
const app = this
|
||||
// 设置列表数据
|
||||
app.getListData(page.num)
|
||||
.then(list => {
|
||||
const curPageLen = list.data.length
|
||||
const totalSize = list.data.total
|
||||
app.mescroll.endBySize(curPageLen, totalSize)
|
||||
})
|
||||
.catch(() => app.mescroll.endErr())
|
||||
},
|
||||
|
||||
// 获取列表数据(根据当前选项卡判断调用的方法)
|
||||
getListData(pageNo) {
|
||||
const apiFuc = {
|
||||
0: this.getGoodsList,
|
||||
1: this.getMyList
|
||||
}
|
||||
return apiFuc[this.curTab](pageNo)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取拼团商品列表
|
||||
* @param {Number} pageNo 页码
|
||||
*/
|
||||
getGoodsList(pageNo = 1) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
GoodsApi.list({ page: pageNo }, { load: false })
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.goodsList.data = getMoreListData(newList, app.goodsList, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取我的拼团列表
|
||||
getMyList(pageNo) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
TaskApi.myList({ page: pageNo }, { load: false })
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.myList.data = getMoreListData(newList, app.myList, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 切换当前选项卡
|
||||
onChangeTab(key = 0) {
|
||||
const app = this
|
||||
// 记录选项卡索引
|
||||
app.curTab = key
|
||||
// 刷新列表数据
|
||||
app.goodsList = getEmptyPaginateObj()
|
||||
app.myList = getEmptyPaginateObj()
|
||||
app.mescroll.resetUpScroll()
|
||||
},
|
||||
|
||||
// 跳转到拼团商品详情
|
||||
onTargetGoods(item) {
|
||||
this.$navTo('pages/groupon/goods/index', { grouponGoodsId: item.groupon_goods_id })
|
||||
},
|
||||
|
||||
// 跳转拼单详情页
|
||||
onTargetTask(item) {
|
||||
this.$navTo('pages/groupon/task/index', { taskId: item.task_id })
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
// 构建页面参数
|
||||
const params = this.$getShareUrlParams()
|
||||
return {
|
||||
title: '拼团活动',
|
||||
path: `/pages/groupon/index?${params}`
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享到朋友圈
|
||||
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta)
|
||||
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
|
||||
*/
|
||||
onShareTimeline() {
|
||||
// 构建页面参数
|
||||
const params = this.$getShareUrlParams()
|
||||
return {
|
||||
title: '拼团活动',
|
||||
path: `/pages/groupon/index?${params}`
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #F5F5F8;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
// 110 - 18 + 4
|
||||
padding-bottom: calc(constant(safe-area-inset-bottom) + 96rpx);
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 96rpx);
|
||||
}
|
||||
|
||||
.banner {
|
||||
z-index: 0;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.groupon-hall {
|
||||
padding: 0 24rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
&.active-list {
|
||||
margin-top: -80rpx;
|
||||
}
|
||||
|
||||
&.my-list {
|
||||
padding-top: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-item--container {
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
padding: 28rpx 24rpx;
|
||||
display: flex;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
border-radius: 14rpx;
|
||||
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
|
||||
.goods-item_left {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
margin-right: 20rpx;
|
||||
|
||||
.label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
background: linear-gradient(to right, #ffa600, #f5b914);
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
padding: 6rpx 8rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
.desc_situation {
|
||||
font-size: 26rpx;
|
||||
line-height: 1.3;
|
||||
color: $main-bg;
|
||||
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-status {
|
||||
color: $main-bg;
|
||||
}
|
||||
|
||||
.item-prices {
|
||||
padding-right: 6rpx;
|
||||
|
||||
.price_x {
|
||||
margin-right: 14rpx;
|
||||
color: $main-bg;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.price_y {
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.settlement {
|
||||
padding: 0 30rpx;
|
||||
line-height: 56rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
border-radius: 40rpx;
|
||||
color: #fff;
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 底部选项卡
|
||||
.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);
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 96rpx;
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
font-size: 30rpx;
|
||||
|
||||
&.active {
|
||||
.tabbar-item-content {
|
||||
color: $main-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.tabbar-item-icon {
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 分割线
|
||||
.tabbar-item__divider {
|
||||
padding: 22rpx 0;
|
||||
}
|
||||
|
||||
.divider-line {
|
||||
width: 1rpx;
|
||||
height: 62rpx;
|
||||
background: #ddd;
|
||||
}
|
||||
</style>
|
||||
Executable
+533
@@ -0,0 +1,533 @@
|
||||
<template>
|
||||
<view v-if="!isLoading && detail && goods" class="container">
|
||||
<!-- 背景区块 -->
|
||||
<view class="bg-layer"></view>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view class="goods-info" @click="onTargetGoods()">
|
||||
<view class="goods-image">
|
||||
<view v-if="detail.active_type != ActiveTypeEnum.NORMAL.value" class="label">
|
||||
<text>{{ ActiveTypeEnum[detail.active_type].name2 }}</text>
|
||||
</view>
|
||||
<image class="image" :src="goods.goods_image"></image>
|
||||
</view>
|
||||
<view class="goods-detail">
|
||||
<view class="goods-name">
|
||||
<text class="twoline-hide">{{ goods.goods_name }}</text>
|
||||
</view>
|
||||
<view class="goods-price">
|
||||
<text class="f-26 col-m">¥</text>
|
||||
<text class="m-price">{{ goods.groupon_price }}</text>
|
||||
<text class="line-price">¥{{ goods.original_price }}</text>
|
||||
</view>
|
||||
<view class="goods-tag">
|
||||
<text class="tag-item">{{ detail.people }}人团</text>
|
||||
<text v-if="goods.diff_price != '0.00'" class="tag-item">拼团省{{ goods.diff_price }}元</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 拼团成员 -->
|
||||
<view class="main">
|
||||
<!-- 拼团状态 (成功、失败) -->
|
||||
<view v-if="detail.status == TaskStatusEnum.FAIL.value" class="main_status main_status__fail">
|
||||
<text class="status-icon iconfont icon-shibai"></text>
|
||||
<text>超过有效时间,拼团失败</text>
|
||||
</view>
|
||||
<view v-if="detail.status == TaskStatusEnum.SUCCESS.value" class="main_status main_status__success">
|
||||
<text class="status-icon iconfont icon-success"></text>
|
||||
<text>拼团已成功</text>
|
||||
</view>
|
||||
<!-- 参团用户 -->
|
||||
<view class="main-user">
|
||||
<view v-for="(item, index) in detail.users" :key="item.id" class="user-item">
|
||||
<avatar-image :url="item.userInfo.avatar_url" :width="100" />
|
||||
<view v-if="item.is_leader" class="user-role"><text class="role-name">团长</text></view>
|
||||
</view>
|
||||
<!-- 虚位以待 -->
|
||||
<view v-for="(val, idx) in detail.people - detail.joined_people" :key="idx" class="user-item user-item__wait">
|
||||
<text class="iconfont icon-wenhao"></text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 拼单状态:拼团中 -->
|
||||
<view v-if="detail.status == TaskStatusEnum.NORMAL.value" class="main_tiem">
|
||||
<text>还差</text>
|
||||
<text class="main_timer_color">{{ detail.people - detail.joined_people }}</text>
|
||||
<text>个名额,</text>
|
||||
<!-- 倒计时 -->
|
||||
<count-down :date="detail.end_time" separator="colon" theme="custom" customBgColor="#FE5246" />
|
||||
<text>后结束</text>
|
||||
</view>
|
||||
<!-- 操作按钮 -->
|
||||
<block v-if="detail.status == TaskStatusEnum.NORMAL.value">
|
||||
<view v-if="!detail.is_join" class="button" @click="onShowSkuPopup()">立即参团</view>
|
||||
<!-- 分享给朋友 -->
|
||||
<button v-else open-type="share" class="button btn-normal" @click="handleShareBtn()">
|
||||
<view class="btn-item btn-item__main"><text>立即分享</text></view>
|
||||
</button>
|
||||
</block>
|
||||
<view v-else class="button" @click="onTargetGoods()">去开团</view>
|
||||
</view>
|
||||
<!-- 拼团须知 -->
|
||||
<view class="notice" @click="handleShowRules()">
|
||||
<text class="f-30">拼团须知</text>
|
||||
<text class="t-brief">{{ setting.ruleBrief }}</text>
|
||||
<text class="icon-arrow"></text>
|
||||
</view>
|
||||
|
||||
<!-- 商品SKU弹窗 -->
|
||||
<SkuPopup v-model="showSkuPopup" :skuMode="skuMode" :goods="goods" :buyMode="1" :taskId="detail.task_id" :stepPeople="detail.people" />
|
||||
|
||||
<!-- 拼团规则弹窗 -->
|
||||
<u-modal v-model="showRules" title="拼团规则">
|
||||
<scroll-view style="height: 610rpx; touch-action: none;" :scroll-y="true">
|
||||
<view class="pops-content">
|
||||
<text>{{ setting.ruleDetail }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</u-modal>
|
||||
<!-- 商品推荐 -->
|
||||
<recommended />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCurrentPage, buildUrL } from '@/core/app'
|
||||
import AvatarImage from '@/components/avatar-image'
|
||||
import CountDown from '@/components/countdown'
|
||||
import Recommended from '@/components/recommended'
|
||||
import SkuPopup from '../goods/components/SkuPopup'
|
||||
import { ActiveTypeEnum, ActiveStatusEnum, TaskStatusEnum } from '@/common/enum/groupon'
|
||||
import * as TaskApi from '@/api/groupon/task'
|
||||
import * as GoodsApi from '@/api/groupon/goods'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
import GrouponSettingModel from '@/common/model/groupon/Setting'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AvatarImage,
|
||||
SkuPopup,
|
||||
CountDown,
|
||||
Recommended
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 枚举类
|
||||
ActiveTypeEnum,
|
||||
ActiveStatusEnum,
|
||||
TaskStatusEnum,
|
||||
// 显示拼团规则
|
||||
showRules: false,
|
||||
// 显示/隐藏SKU弹窗
|
||||
showSkuPopup: false,
|
||||
// 按钮模式 1:都显示 2:只显示购物车 3:只显示立即购买
|
||||
skuMode: 3,
|
||||
// 当前拼单ID
|
||||
taskId: null,
|
||||
// 拼单详情
|
||||
detail: null,
|
||||
// 拼团商品
|
||||
goods: null,
|
||||
// 拼团设置
|
||||
setting: {}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad({ taskId }) {
|
||||
// 记录拼单ID
|
||||
this.taskId = taskId
|
||||
// 获取拼团设置
|
||||
this.getSetting()
|
||||
// 获取拼单详情
|
||||
this.getTaskDetail()
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 获取拼团设置
|
||||
getSetting() {
|
||||
GrouponSettingModel.data(true).then(setting => (this.setting = setting))
|
||||
},
|
||||
|
||||
// 获取拼单详情
|
||||
getTaskDetail() {
|
||||
const { taskId } = this
|
||||
this.isLoading = true
|
||||
TaskApi.detail(taskId)
|
||||
.then(result => {
|
||||
this.detail = result.data.detail
|
||||
this.goods = result.data.goods
|
||||
})
|
||||
.finally(() => (this.isLoading = false))
|
||||
},
|
||||
|
||||
// 跳转到拼团商品详情
|
||||
onTargetGoods() {
|
||||
const { goods } = this
|
||||
this.$navTo('pages/groupon/goods/index', { grouponGoodsId: goods.groupon_goods_id })
|
||||
},
|
||||
|
||||
// 点击分享按钮
|
||||
handleShareBtn() {
|
||||
// #ifndef MP
|
||||
this.handleCopyLink()
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 复制当前页面链接
|
||||
handleCopyLink() {
|
||||
const app = this
|
||||
app.getShareUrl().then(shareUrl => {
|
||||
// 复制到剪贴板
|
||||
uni.setClipboardData({
|
||||
data: shareUrl,
|
||||
success: () => app.$toast('复制链接成功,快去发送给朋友吧'),
|
||||
fail: err => app.$toast('复制失败')
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取分享链接 (H5外链)
|
||||
getShareUrl() {
|
||||
const { path, query } = getCurrentPage()
|
||||
return new Promise((resolve, reject) => {
|
||||
// 获取h5站点地址
|
||||
SettingModel.h5Url(true).then(baseUrl => {
|
||||
// 生成完整的分享链接
|
||||
const shareUrl = buildUrL(baseUrl, path, query)
|
||||
resolve(shareUrl)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 显示拼团规则
|
||||
handleShowRules() {
|
||||
this.showRules = true
|
||||
},
|
||||
|
||||
// 显示/隐藏SKU弹窗
|
||||
onShowSkuPopup() {
|
||||
this.showSkuPopup = !this.showSkuPopup
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
// 构建页面参数
|
||||
const app = this
|
||||
const params = app.$getShareUrlParams({ taskId: app.taskId })
|
||||
return {
|
||||
title: app.goods.goods_name,
|
||||
path: `/pages/groupon/task/index?${params}`
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享到朋友圈
|
||||
*/
|
||||
onShareTimeline() {
|
||||
// 构建页面参数
|
||||
const app = this
|
||||
const params = app.$getShareUrlParams({ taskId: app.taskId })
|
||||
return {
|
||||
title: app.goods.goods_name,
|
||||
path: `/pages/groupon/task/index?${params}`
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 箭头图标
|
||||
.icon-arrow {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-width: 10rpx;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent transparent #ccc;
|
||||
}
|
||||
|
||||
// 背景区块
|
||||
.bg-layer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
height: 250rpx;
|
||||
text-align: center;
|
||||
line-height: 100rpx;
|
||||
|
||||
&::after {
|
||||
width: 140%;
|
||||
height: 250rpx;
|
||||
position: absolute;
|
||||
left: -20%;
|
||||
top: 0;
|
||||
z-index: -1;
|
||||
content: '';
|
||||
border-radius: 0 0 50% 50%;
|
||||
background-image: linear-gradient(180deg, #ff5644, #fd7524);
|
||||
}
|
||||
}
|
||||
|
||||
// 商品信息
|
||||
.goods-info {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
margin-top: 30rpx;
|
||||
padding: 28rpx;
|
||||
padding-bottom: 22rpx;
|
||||
width: 92%;
|
||||
background: #fff;
|
||||
border-radius: 10rpx;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 5px 12px rgba(226, 226, 226, 0.62);
|
||||
display: flex;
|
||||
|
||||
.goods-image {
|
||||
position: relative;
|
||||
margin-right: 30rpx;
|
||||
|
||||
.label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
background: linear-gradient(to right, #ffa600, #f5b914);
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
padding: 6rpx 8rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 240rpx;
|
||||
height: 240rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-detail {
|
||||
background: #fff;
|
||||
|
||||
.goods-name {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
margin: 20rpx 0;
|
||||
color: #eb5841;
|
||||
|
||||
.m-price {
|
||||
display: inline-block;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.line-price {
|
||||
display: inline-block;
|
||||
margin: 10rpx;
|
||||
font-size: 24rpx;
|
||||
color: #8e8e8e;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-tag {
|
||||
margin-bottom: -10rpx;
|
||||
|
||||
.tag-item {
|
||||
display: inline-block;
|
||||
padding: 4rpx 12rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 36rpx;
|
||||
border-radius: 5rpx;
|
||||
color: #fa3534;
|
||||
background: #fef0f0;
|
||||
margin-right: 10rpx;
|
||||
margin-bottom: 10rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
display: block;
|
||||
clear: both;
|
||||
content: '';
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 拼团成员
|
||||
.main {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
margin-top: 30rpx;
|
||||
padding: 50rpx 20rpx;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
|
||||
.main-user {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
margin: 0 auto;
|
||||
width: 600rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.user-item {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
margin: 0 25rpx 25rpx 0;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:nth-child(5n + 0) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.user-role {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
height: 40rpx;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -1rpx;
|
||||
|
||||
.role-name {
|
||||
background: #eb5841;
|
||||
border-radius: 15rpx;
|
||||
display: inline-block;
|
||||
line-height: 1.4;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
width: 78rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&.user-item__wait {
|
||||
border-radius: 50%;
|
||||
background: #f8f8f8;
|
||||
border: 1rpx dashed #dbdbdb;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
text {
|
||||
color: #dbdbdb;
|
||||
font-size: 38rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
display: block;
|
||||
margin-top: 40rpx;
|
||||
width: 550rpx;
|
||||
line-height: 84rpx;
|
||||
font-size: 28rpx;
|
||||
background-image: linear-gradient(90deg, #fe5246, #fb265a);
|
||||
border: none;
|
||||
box-shadow: 0px 3px 8px rgba(255, 8, 15, 0.25);
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
color: #fff;
|
||||
border-radius: 40rpx;
|
||||
animation: btn_anim 0.9s linear infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮动画
|
||||
@keyframes btn_anim {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
40% {
|
||||
-webkit-transform: scale(1.05);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
/* 拼团状态 */
|
||||
.main_status {
|
||||
margin-bottom: 40rpx;
|
||||
font-size: 35rpx;
|
||||
text-align: center;
|
||||
|
||||
.status-icon {
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.main_status__fail {
|
||||
color: $main-bg;
|
||||
}
|
||||
|
||||
.main_status__success {
|
||||
color: #08b625;
|
||||
}
|
||||
|
||||
/* 倒计时 */
|
||||
.main_tiem {
|
||||
margin-bottom: 50rpx;
|
||||
font-size: 30rpx;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.main_timer_color {
|
||||
color: #fc8434;
|
||||
margin: 0 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 拼团须知
|
||||
.notice {
|
||||
display: flex;
|
||||
margin: auto;
|
||||
margin-top: 30rpx;
|
||||
padding: 35rpx 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 10rpx;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.t-brief {
|
||||
font-size: 26rpx;
|
||||
color: #a6a6a6;
|
||||
}
|
||||
}
|
||||
|
||||
// 拼团规则 (弹窗)
|
||||
.pops-content {
|
||||
padding: 30rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 44rpx;
|
||||
text-align: left;
|
||||
color: #606266;
|
||||
min-height: 320rpx;
|
||||
max-height: 640rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<view class="help">
|
||||
<block v-for="(item,index) in itemList" :key="index">
|
||||
<u-card :title="item.title">
|
||||
<view class="" slot="body">
|
||||
<u-parse :html="item.content"></u-parse>
|
||||
</view>
|
||||
</u-card>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pageArticle } from '@/websoft/api/article.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 列表数据
|
||||
itemList: [{
|
||||
head: "关于我们",
|
||||
body: "",
|
||||
open: true,
|
||||
disabled: true
|
||||
}],
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
const app = this
|
||||
pageArticle({
|
||||
categoryId: 43
|
||||
}).then(res => {
|
||||
app.itemList = res.data.list
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.help {
|
||||
border-bottom: 1rpx solid #f6f6f9;
|
||||
width: 750rpx;
|
||||
margin: 0rpx auto;
|
||||
}
|
||||
</style>
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<view class="help">
|
||||
<block v-for="(item,index) in itemList" :key="index">
|
||||
<u-card :title="item.title">
|
||||
<view class="" slot="body">
|
||||
<u-parse :html="item.content"></u-parse>
|
||||
</view>
|
||||
</u-card>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pageArticle } from '@/websoft/api/article.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 列表数据
|
||||
itemList: [{
|
||||
head: "关于我们",
|
||||
body: "",
|
||||
open: true,
|
||||
disabled: true
|
||||
}],
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
const app = this
|
||||
pageArticle({
|
||||
categoryId: 42
|
||||
}).then(res => {
|
||||
console.log("res: ",res);
|
||||
app.itemList = res.data.list
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.help {
|
||||
border-bottom: 1rpx solid #f6f6f9;
|
||||
width: 750rpx;
|
||||
margin: 0rpx auto;
|
||||
}
|
||||
</style>
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<view class="help">
|
||||
<block v-for="(item,index) in itemList" :key="index">
|
||||
<view class="content" slot="body">
|
||||
<u-parse :html="item.content"></u-parse>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pageArticle } from '@/websoft/api/article.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 列表数据
|
||||
itemList: [{
|
||||
head: "关于我们",
|
||||
body: "",
|
||||
open: true,
|
||||
disabled: true
|
||||
}],
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
const app = this
|
||||
pageArticle({
|
||||
categoryId: 51
|
||||
}).then(res => {
|
||||
app.itemList = res.data.list
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.help {
|
||||
border-bottom: 1rpx solid #f6f6f9;
|
||||
width: 750rpx;
|
||||
margin: 0rpx auto;
|
||||
.content{
|
||||
padding: 20rpx 40rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<!-- 店铺页面组件 -->
|
||||
<view class="top-bar" v-if="isLogin" @click="navTo('pages/user/equipment/index')">点击查看设备情况</view>
|
||||
<view class="login-bar" v-else>
|
||||
<text class="info">授权登录、获取头像昵称,加入安博驰</text>
|
||||
<text class="login" @click="navTo('pages/user/user')">立即登录</text>
|
||||
</view>
|
||||
<map id="map" class="map" :scale="scale" :show-location="true" :latitude="latitude" :longitude="longitude"></map>
|
||||
<view class="bottom-bar" @click="scan()">扫码设备二维码下单租赁</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { userId } from '@/config.js';
|
||||
import { getUser } from '@/websoft/api/user.js'
|
||||
import { login } from '@/websoft/api/login.js'
|
||||
import store from '@/store/index.js'
|
||||
import storage from '@/utils/storage'
|
||||
import {
|
||||
ACCESS_TOKEN,
|
||||
USER_ID
|
||||
} from '@/store/mutation-types'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
user: {},
|
||||
avatar: '/static/logo.png',
|
||||
nickName: 'Hello',
|
||||
latitude: 22.766777,
|
||||
longitude: 108.375152,
|
||||
scale: 10,
|
||||
isLogin: false,
|
||||
// #ifdef MP-ALIPAY
|
||||
canIUseAuthButton: my.canIUse('button.open-type.getAuthorize'),
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const app = this
|
||||
app.getLocation(res => {
|
||||
if(res.latitude &&res.longitude){
|
||||
app.latitude = res.latitude
|
||||
app.longitude = res.longitude
|
||||
app.scale = 16
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
onShow(){
|
||||
this.getUserInfo()
|
||||
},
|
||||
methods: {
|
||||
getUserInfo() {
|
||||
const { form } = this
|
||||
const app = this
|
||||
getUser().then(res => {
|
||||
if( res.code == 0 && res.data.username != 'www') {
|
||||
app.isLogin = true
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
// 检查是否登录
|
||||
checkLogin(){
|
||||
if(!!store.getters.userId && store.getters.userId != userId){
|
||||
this.isLogin = true
|
||||
}
|
||||
},
|
||||
// 获取用户坐标
|
||||
// 参考文档:https://uniapp.dcloud.io/api/location/location?id=getlocation
|
||||
getLocation(callback) {
|
||||
const app = this
|
||||
uni.getLocation({
|
||||
success: callback,
|
||||
fail() {
|
||||
app.$toast('获取定位失败,请点击右下角按钮重新尝试定位')
|
||||
app.isAuthor = false
|
||||
}
|
||||
})
|
||||
},
|
||||
// 跳转页面
|
||||
navTo(url) {
|
||||
uni.$u.route(url);
|
||||
},
|
||||
scan(){
|
||||
const app = this
|
||||
// 只允许从相机扫码
|
||||
// uni.scanCode({
|
||||
// success (res) {
|
||||
// console.log("res: ",res);
|
||||
// // app.$navTo('package/equipment/checkout/index')
|
||||
// // // app.$navTo(res.path)
|
||||
// my.alert({ title: res.code });
|
||||
// }
|
||||
// })
|
||||
my.scan({
|
||||
scanType: ['barCode'],
|
||||
success: res => {
|
||||
console.log("res: ",res);
|
||||
my.alert({ title: res.code });
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
background: #fff;
|
||||
}
|
||||
.top-bar{
|
||||
position: fixed;
|
||||
top: 10rpx;
|
||||
left: 25rpx;
|
||||
line-height: 50rpx;
|
||||
height: 50rpx;
|
||||
z-index: 1000;
|
||||
background-color: #fff;
|
||||
width: 700rpx;
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.get-phone-number{
|
||||
position: fixed;
|
||||
top: 300rpx;
|
||||
left: 25rpx;
|
||||
z-index: 1000;
|
||||
line-height: 70rpx;
|
||||
height: 70rpx;
|
||||
background-color: #0f80ff;
|
||||
color: #fff;
|
||||
width: 700rpx;
|
||||
margin: 25rpx auto;
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.bottom-bar{
|
||||
position: fixed;
|
||||
bottom: 10rpx;
|
||||
left: 25rpx;
|
||||
z-index: 1000;
|
||||
line-height: 70rpx;
|
||||
height: 70rpx;
|
||||
background-color: #0f80ff;
|
||||
color: #fff;
|
||||
width: 700rpx;
|
||||
margin: 25rpx auto;
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
.login-bar{
|
||||
position: fixed;
|
||||
top: 10rpx;
|
||||
left: 25rpx;
|
||||
z-index: 1000;
|
||||
line-height: 70rpx;
|
||||
height: 70rpx;
|
||||
color: #fff;
|
||||
width: 700rpx;
|
||||
margin: 17rpx auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.info{
|
||||
padding-left: 20rpx;
|
||||
background-color: #000000;
|
||||
opacity: 0.5;
|
||||
width: 100%;
|
||||
}
|
||||
.login{
|
||||
width: 200rpx;
|
||||
padding: 0 20rpx;
|
||||
background-color: #0f80ff !important;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+257
@@ -0,0 +1,257 @@
|
||||
<template>
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ native: true }" @down="downCallback"
|
||||
:up="upOption" @up="upCallback">
|
||||
<view class="live-room-list">
|
||||
<view v-for="(item, index) in list.data" :key="index" @click="onTargetLiveRoom(item.room_id)"
|
||||
:class="[`live-room-item live-status__${item.live_status}`]">
|
||||
<!-- 直播状态 -->
|
||||
<view class="room-head dis-flex flex-y-center">
|
||||
<!-- 直播中 -->
|
||||
<text v-if="item.live_status == 101" class="live-status_icon iconfont icon-zhibozhong"></text>
|
||||
<!-- 未开播 -->
|
||||
<text v-if="item.live_status == 102" class="live-status_icon iconfont icon-shijian-s"></text>
|
||||
<!-- 已结束 -->
|
||||
<text v-if="item.live_status >= 103" class="live-status_icon iconfont icon-shipin"></text>
|
||||
<!-- 状态说明 -->
|
||||
<text class="live-status_text">{{ item.live_status_text_1 }}</text>
|
||||
</view>
|
||||
<!-- 房间名称 -->
|
||||
<view class="room-name oneline-hide">
|
||||
<text>{{ item.room_name }}</text>
|
||||
</view>
|
||||
<!-- 房间封面 -->
|
||||
<view class="room-cover">
|
||||
<image class="image" :src="item.share_img" mode="aspectFill"></image>
|
||||
</view>
|
||||
<!-- 主播信息 -->
|
||||
<view class="room-anchor dis-flex">
|
||||
<view class="lay-left flex-box dis-flex flex-y-center">
|
||||
<!-- 主播头像 -->
|
||||
<!-- mix: 微信api未提供主播头像, 此处显示封面图 -->
|
||||
<view class="anchor-avatar">
|
||||
<image class="image" :src="item.share_img" mode="aspectFill"></image>
|
||||
</view>
|
||||
<!-- 主播昵称 -->
|
||||
<view class="anchor-name">
|
||||
<text>{{ item.anchor_name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="lay-right">
|
||||
<text class="live-status_text2">{{ item.live_status_text_2 }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getEmptyPaginateObj, getMoreListData, getShareParams } from '@/core/app'
|
||||
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
|
||||
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
|
||||
import { formatDate, dateFormat } from '@/utils/util'
|
||||
import * as Api from '@/api/live/room'
|
||||
|
||||
const pageSize = 15
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: true,
|
||||
// 每页数据的数量; 默认10
|
||||
page: { size: pageSize },
|
||||
// 数量要大于3条才显示无更多数据
|
||||
noMoreSize: 3,
|
||||
},
|
||||
// 直播间列表
|
||||
list: getEmptyPaginateObj()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 获取直播间列表
|
||||
this.getLiveRoomList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* 上拉加载的回调 (页面初始化时也会执行一次)
|
||||
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
|
||||
* @param {Object} page
|
||||
*/
|
||||
upCallback(page) {
|
||||
const app = this
|
||||
// 设置列表数据
|
||||
app.getLiveRoomList(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 页码
|
||||
*/
|
||||
getLiveRoomList(pageNo = 1) {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
Api.list({ page: pageNo }, { load: false })
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.list.data = getMoreListData(newList, app.list, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 进入直播间
|
||||
onTargetLiveRoom(roomId) {
|
||||
const { platform, $toast } = this
|
||||
if (platform !== 'MP-WEIXIN') {
|
||||
$toast('很抱歉,直播间仅支持微信小程序,请前往微信小程序端')
|
||||
return
|
||||
}
|
||||
const customParams = getShareParams({
|
||||
path: 'pages/index/index'
|
||||
})
|
||||
wx.navigateTo({
|
||||
url: `plugin-private://wx2b03c6e691cd7370/pages/live-player-plugin?room_id=${roomId}&custom_params=${encodeURIComponent(JSON.stringify(customParams))}`
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: '直播列表',
|
||||
path: "/pages/live/index?" + this.$getShareUrlParams()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享到朋友圈
|
||||
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta)
|
||||
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
|
||||
*/
|
||||
onShareTimeline() {
|
||||
return {
|
||||
title: '直播列表',
|
||||
path: "/pages/live/index?" + this.$getShareUrlParams()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.live-room-item {
|
||||
width: 710rpx;
|
||||
margin: 0 auto 20rpx auto;
|
||||
padding: 25rpx 24rpx;
|
||||
background: #fff;
|
||||
border-radius: 5rpx;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 2rpx 4rpx 0 rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:first-child {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.room-head {
|
||||
color: #b6b6b6;
|
||||
line-height: 40rpx;
|
||||
|
||||
.live-status_icon {
|
||||
margin-right: 15rpx;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.live-status_text {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 直播中
|
||||
.live-status__101 .room-head {
|
||||
color: #db384b;
|
||||
}
|
||||
|
||||
.live-status__102 .room-head {
|
||||
color: #db384b;
|
||||
}
|
||||
|
||||
// 房间名称
|
||||
.room-name {
|
||||
margin-top: 10rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
// 房间封面图
|
||||
.room-cover {
|
||||
margin-top: 15rpx;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 371rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 主播信息
|
||||
.room-anchor {
|
||||
margin-top: 20rpx;
|
||||
|
||||
.anchor-avatar {
|
||||
margin-right: 12rpx;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 45rpx;
|
||||
height: 45rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.anchor-name {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.live-status_text2 {
|
||||
color: #b6b6b6;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
// 直播中
|
||||
.live-status__101 .live-status_text2 {
|
||||
color: #db384b;
|
||||
}
|
||||
|
||||
.live-status__102 .live-status_text2 {
|
||||
color: #db384b;
|
||||
}
|
||||
</style>
|
||||
Executable
+377
@@ -0,0 +1,377 @@
|
||||
<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="20" placeholder="请输入登录账号|手机号码|邮箱" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<input class="form-item--input" type="text" v-model="password" maxlength="30" 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" style="display: none;">
|
||||
<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 storage from '@/utils/storage'
|
||||
import {
|
||||
ACCESS_TOKEN,
|
||||
USER_ID
|
||||
} from '@/store/mutation-types'
|
||||
import { getCaptcha, login, sendSmsCaptcha } from '@/websoft/api/login.js'
|
||||
import * as Verify from '@/utils/verify'
|
||||
import http from '@/websoft/api'
|
||||
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: '',
|
||||
password: '',
|
||||
// 图形验证码
|
||||
captchaCode: '',
|
||||
// 短信验证码
|
||||
smsCode: ''
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
created() {
|
||||
// 获取图形验证码
|
||||
this.getCaptcha()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取图形验证码
|
||||
getCaptcha() {
|
||||
const app = this
|
||||
getCaptcha()
|
||||
.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
|
||||
sendSmsCaptcha({
|
||||
phone: 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
|
||||
console.log("登录: ",'登录');
|
||||
login({username: app.mobile,password:app.password}).then(res => {
|
||||
// 老用户登录成功
|
||||
const expiryTime = 30 * 86400 // 过期时间30天
|
||||
storage.set(ACCESS_TOKEN, res.data.access_token, expiryTime)
|
||||
storage.set(USER_ID, res.data.user.userId, expiryTime)
|
||||
// 显示登录成功
|
||||
http.setConfig((config) => {
|
||||
config.header = {
|
||||
Authorization: res.data.access_token
|
||||
}
|
||||
return config
|
||||
})
|
||||
app.$toast(res.message)
|
||||
// 跳转回原页面
|
||||
setTimeout(() => {
|
||||
// app.onNavigateBack(1)
|
||||
app.$navTo('pages/user/user')
|
||||
}, 2000)
|
||||
})
|
||||
// store.dispatch('Login', {
|
||||
// smsCode: app.smsCode,
|
||||
// mobile: app.mobile,
|
||||
// isParty: app.isParty,
|
||||
// partyData: app.partyData,
|
||||
// refereeId: store.getters.refereeId
|
||||
// })
|
||||
// .then(result => {
|
||||
// // 显示登录成功
|
||||
// app.$toast(result.message)
|
||||
// // 跳转回原页面
|
||||
// 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: $main-bg;
|
||||
}
|
||||
|
||||
.un-activate {
|
||||
color: #9e9e9e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 登录按钮
|
||||
.login-button {
|
||||
width: 100%;
|
||||
height: 86rpx;
|
||||
margin-top: 80rpx;
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
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>
|
||||
Executable
+144
@@ -0,0 +1,144 @@
|
||||
<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, inArray } 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
|
||||
}
|
||||
if (detail.errMsg == 'getPhoneNumber:ok') {
|
||||
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)
|
||||
// 跳转回原页面
|
||||
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>
|
||||
Executable
+238
@@ -0,0 +1,238 @@
|
||||
<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)
|
||||
// 跳转回原页面
|
||||
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>
|
||||
Executable
+124
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<!-- 跳转到微信授权地址 -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import queryStringify from '@/js_sdk/queryStringify'
|
||||
import store from '@/store'
|
||||
import { isEmpty, urlEncode } from '@/utils/util'
|
||||
import * as Api from '@/api/wxofficial'
|
||||
|
||||
export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
// 页面来源是否为微信回调
|
||||
isCallback: false
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
// 处理微信回调
|
||||
this.onCallback()
|
||||
// 跳转到微信授权
|
||||
this.redirectUrl()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 处理微信回调
|
||||
onCallback() {
|
||||
// 接收微信传来的参数
|
||||
const wxParam = queryStringify.parse(window.location.search)
|
||||
if (!isEmpty(wxParam)) {
|
||||
const url = window.location.href.replace(window.location.search, '')
|
||||
window.location.href = url + '?' + urlEncode(wxParam)
|
||||
return
|
||||
}
|
||||
// 获取code参数
|
||||
const query = this.$route.query
|
||||
if (isEmpty(query) || !query.code) {
|
||||
return
|
||||
}
|
||||
// 请求后端获取微信用户信息
|
||||
this.isCallback = true
|
||||
Api.oauthUserInfo(query.code)
|
||||
.then(({ data }) => {
|
||||
console.log('用户同意了授权')
|
||||
console.log('userInfo:', data)
|
||||
// 授权成功事件
|
||||
this.onAuthSuccess(data)
|
||||
})
|
||||
},
|
||||
|
||||
// 授权成功事件
|
||||
// 这里分为两个逻辑:
|
||||
// 1.将openid和userInfo提交到后端,如果存在该用户 则实现自动登录,无需再填写手机号
|
||||
// 2.如果不存在该用户, 则显示注册页面, 需填写手机号
|
||||
// 3.如果后端报错了, 则显示错误信息
|
||||
async onAuthSuccess({ userInfo, encryptedData, iv }) {
|
||||
const app = this
|
||||
// 提交到后端
|
||||
store.dispatch('LoginWxOfficial', {
|
||||
partyData: { oauth: 'H5-WEIXIN', userInfo, encryptedData, iv },
|
||||
refereeId: store.getters.refereeId
|
||||
})
|
||||
.then(result => {
|
||||
// 一键登录成功
|
||||
app.$toast(result.message)
|
||||
// 跳转回原页面
|
||||
setTimeout(() => app.onNavigateBack(), 2000)
|
||||
})
|
||||
.catch(err => {
|
||||
const resultData = err.result.data
|
||||
// 显示错误信息
|
||||
if (isEmpty(resultData)) {
|
||||
app.$toast(err.result.message)
|
||||
}
|
||||
// 判断还需绑定手机号
|
||||
if (resultData.isBindMobile) {
|
||||
app.onEmitSuccess({ userInfo, encryptedData, iv })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转到微信授权
|
||||
redirectUrl() {
|
||||
if (this.isCallback) {
|
||||
return
|
||||
}
|
||||
const callbackUrl = window.location.href
|
||||
Api.oauthUrl(callbackUrl)
|
||||
.then(result => {
|
||||
const url = result.data.redirectUrl
|
||||
window.location.href = url
|
||||
})
|
||||
},
|
||||
|
||||
// 将oauth提交给父级
|
||||
async onEmitSuccess({ userInfo, encryptedData, iv }) {
|
||||
this.$emit('success', {
|
||||
oauth: 'H5-WEIXIN', // 第三方登录类型: H5-WEIXIN
|
||||
userInfo,
|
||||
encryptedData,
|
||||
iv
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 登录成功-跳转回原页面
|
||||
*/
|
||||
onNavigateBack(delta = 1) {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack({
|
||||
delta: Number(delta || 1)
|
||||
})
|
||||
} else {
|
||||
this.$navTo('pages/index/index')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<view v-if="isLoad" class="login" :style="appThemeStyle">
|
||||
<MpWeixin v-if="isMpWeixinAuth" @success="onGetUserInfoSuccess" />
|
||||
<WxOfficial v-else-if="isWxOfficialAuth" @success="onGetUserInfoSuccess" />
|
||||
<Main v-else :isParty="isParty" :partyData="partyData" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Main from './components/main'
|
||||
import MpWeixin from './components/mp-weixin'
|
||||
import WxOfficial from './components/wx-official'
|
||||
import SettingKeyEnum from '@/common/enum/setting/Key'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Main,
|
||||
MpWeixin,
|
||||
WxOfficial
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// 数据加载完成 [防止在微信小程序端onLoad和view渲染同步进行]
|
||||
isLoad: false,
|
||||
// 注册设置 (后台设置)
|
||||
setting: {},
|
||||
// 是否显示微信小程序授权登录
|
||||
isMpWeixinAuth: false,
|
||||
// 是否显示微信公众号授权登录
|
||||
isWxOfficialAuth: false,
|
||||
// 是否存在第三方用户信息
|
||||
isParty: false,
|
||||
// 第三方用户信息数据
|
||||
partyData: {}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
async onLoad(options) {
|
||||
// 获取注册设置
|
||||
// await this.getRegisterSetting()
|
||||
// 设置当前是否显示第三方授权登录
|
||||
await this.setShowUserInfo()
|
||||
// 数据加载完成
|
||||
this.isLoad = true
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取注册设置 [后台-客户端-注册设置]
|
||||
async getRegisterSetting() {
|
||||
await SettingModel.item(SettingKeyEnum.REGISTER.value, false)
|
||||
.then(setting => this.setting = setting)
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置当前是否显示第三方授权登录
|
||||
* - 条件1: 只有对应的客户端显示获取用户信息按钮, 例如微信小程序、微信公众号
|
||||
* - 条件2: 注册设置是否已开启该选项
|
||||
*/
|
||||
async setShowUserInfo() {
|
||||
const app = this
|
||||
// 判断当前客户端是微信小程序, 并且支持getUserProfile接口
|
||||
const isMpWeixin = app.platform === 'MP-WEIXIN' && wx.canIUse('getUserProfile')
|
||||
const isWxOfficial = app.platform === 'H5-WEIXIN'
|
||||
// 判断是否显示第三方授权登录
|
||||
app.isMpWeixinAuth = isMpWeixin && app.setting.isOauthMpweixin
|
||||
app.isWxOfficialAuth = isWxOfficial && app.setting.isOauthWxofficial
|
||||
},
|
||||
|
||||
// 获取到用户信息的回调函数
|
||||
onGetUserInfoSuccess(result) {
|
||||
// 记录第三方用户信息数据
|
||||
this.partyData = result
|
||||
// 显示注册页面
|
||||
this.onShowRegister()
|
||||
},
|
||||
|
||||
// 显示注册页面
|
||||
onShowRegister() {
|
||||
// 是否显示微信小程序授权登录
|
||||
if (this.partyData.oauth === 'MP-WEIXIN') {
|
||||
this.isMpWeixinAuth = false
|
||||
}
|
||||
// 是否显示微信小程序授权登录
|
||||
if (this.partyData.oauth === 'H5-WEIXIN') {
|
||||
this.isWxOfficialAuth = false
|
||||
}
|
||||
// 已获取到了第三方用户信息
|
||||
this.isParty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
Executable
+207
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<view class="ws-page">
|
||||
<view class="logo">
|
||||
<image src="../../static/logo.png" mode="widthFix"></image>
|
||||
</view>
|
||||
<!-- 支付宝授权手机号一键登录 -->
|
||||
<view class="alipay-auth">
|
||||
<button @click="login" :disabled="loding">支付宝授权一键登录</button>
|
||||
</view>
|
||||
<!-- 手机号登录 -->
|
||||
<view class="mobile-auth">
|
||||
<button @click="loginByUserName">账号登录</button>
|
||||
</view>
|
||||
<!-- <view class="xieyi">
|
||||
<checkbox-group @change="onAgree" style="display: flex; align-items: center;">
|
||||
<checkbox :checked="agree" class="agree" />登录即代表您已同意<div class="xieyi-text" @click="showXieyi">《服务协议与隐私协议》</div>
|
||||
</checkbox-group>
|
||||
</view> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { getAuthCode, getPhoneNumber, login, alipayLogin, register } from '@/websoft/api/login.js'
|
||||
import { ACCESS_TOKEN, USER_ID } from '@/store/mutation-types'
|
||||
import storage from '@/utils/storage'
|
||||
import http from '@/websoft/api'
|
||||
import { roleId } from '@/config.js'
|
||||
import {
|
||||
isEmpty,
|
||||
inArray
|
||||
} from '@/utils/util'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
agree: true,
|
||||
userId: '',
|
||||
loding: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 按钮点击事件: 获取支付宝手机号按钮
|
||||
login() {
|
||||
const app = this
|
||||
my.getAuthCode({
|
||||
scopes: 'auth_user',
|
||||
success: ({ authCode }) => {
|
||||
console.log("获取支付宝授权码: ",authCode);
|
||||
app.loding = true
|
||||
// 跟进授权码获取支付宝用户ID
|
||||
getAuthCode({authCode})
|
||||
.then(res => {
|
||||
store.dispatch('setUserInfo',res.data.user)
|
||||
store.dispatch('setUserId',res.data.user.userId)
|
||||
store.dispatch('setToken',res.data.access_token)
|
||||
const expiryTime = 30 * 86400 // 过期时间30天
|
||||
storage.set(ACCESS_TOKEN, res.data.access_token, expiryTime)
|
||||
storage.set(USER_ID, res.data.user.userId, expiryTime)
|
||||
// 显示登录成功
|
||||
http.setConfig((config) => {
|
||||
config.header = {
|
||||
Authorization: res.data.access_token
|
||||
}
|
||||
return config
|
||||
})
|
||||
app.$toast(res.message)
|
||||
// 跳转回原页面
|
||||
setTimeout(() => {
|
||||
app.$navTo('pages/user/user')
|
||||
}, 2000)
|
||||
})
|
||||
.catch(e => {
|
||||
app.$error(e.message)
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onAgree() {
|
||||
this.agree = !this.agree
|
||||
},
|
||||
|
||||
loginByUserName(){
|
||||
this.$navTo('pages/login/index')
|
||||
},
|
||||
|
||||
// // #ifdef MP-ALIPAY
|
||||
// my.getPhoneNumber({
|
||||
// success: (res) => {
|
||||
// let encryptedData = res.response;
|
||||
// getPhoneNumber({encryptedData}).then(response => {
|
||||
// console.log("授权手机号码: ",response);
|
||||
// const json = JSON.parse(response.data)
|
||||
// console.log("json: ",json);
|
||||
// if(json.mobile){
|
||||
// // 执行登录
|
||||
// app.onAuthSuccess(json.mobile)
|
||||
// }
|
||||
// })
|
||||
// },
|
||||
// fail: (res) => {
|
||||
// console.log(res);
|
||||
// },
|
||||
// });
|
||||
// // #endif
|
||||
|
||||
/**
|
||||
* 登录成功-跳转回原页面
|
||||
*/
|
||||
onNavigateBack(delta = 1) {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack({
|
||||
delta: Number(delta || 1)
|
||||
})
|
||||
} else {
|
||||
this.$navTo('pages/user/user')
|
||||
}
|
||||
},
|
||||
showXieyi(){
|
||||
this.$navTo('pages/help/xieyi')
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ws-page {
|
||||
padding: 40rpx;
|
||||
height: 70vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.logo {
|
||||
margin: 100rpx auto;
|
||||
text-align: center;
|
||||
|
||||
image {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.alipay-auth {
|
||||
margin: 12rpx 0;
|
||||
|
||||
button {
|
||||
background-color: #3869ea;
|
||||
color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-auth {
|
||||
margin: 12rpx 0;
|
||||
|
||||
button {
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.xieyi {
|
||||
margin-top: 100rpx;
|
||||
font-size: 28rpx;
|
||||
text-align: center;
|
||||
|
||||
text {
|
||||
color: #3869ea;
|
||||
}
|
||||
|
||||
.agree {
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 微信授权登录
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
.xieyi-text{
|
||||
color: #0000ff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,330 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 门脸图 -->
|
||||
<view class="shop-banner">
|
||||
<image :src="record.logo" class="logo" mode="aspectFit"></image>
|
||||
<view class="shop-info">
|
||||
<view class="shop-name">{{ record.merchantName }}</view>
|
||||
<view class="shop-desc">
|
||||
<view class="shop-desc-text"><u-rate :count="count" :gutter="-10" :value="value" readonly size="20" @change="onChangeStar"></u-rate></view>
|
||||
<!-- <view class="shop-desc-text">店铺粉丝 321</view> -->
|
||||
<view class="shop-desc-text">全部商品 {{ count }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="follow"><u-button size="mini">关注</u-button></view>
|
||||
</view>
|
||||
|
||||
<view class="category" v-for="(cate,index) in category" :key="index">
|
||||
<view class="category-name" v-if="cate.count > 0">{{ cate.dictDataName }}</view>
|
||||
<block v-for="(eq,i) in equipmentList" :key="i">
|
||||
<view class="goods" v-if="cate.dictDataCode == eq.equipmentCategory">
|
||||
<block v-if="eq.equipmentCategory == '10'">
|
||||
<image v-if="eq.image" :src="eq.image" class="equipment-image" mode="aspectFit"></image>
|
||||
<image v-else src="../../static/goods/battery.png" class="equipment-image" mode="aspectFit"></image>
|
||||
<view class="info">
|
||||
<text class="equipment-model">{{ eq.batteryModel }}</text>
|
||||
<text class="equipment-count">库存:{{ eq.stockTotal }}个</text>
|
||||
<text class="equipment-price">¥{{ eq.batteryPrice }}元</text>
|
||||
</view>
|
||||
<view class="tobuy">
|
||||
<u-button type="success" :disabled="disabled" size="mini" @click="buy(eq.goodsId)">立即购买</u-button>
|
||||
</view>
|
||||
</block>
|
||||
<block v-if="eq.equipmentCategory == '20'">
|
||||
<image v-if="eq.image" :src="eq.image" class="equipment-image" mode="aspectFit"></image>
|
||||
<image v-else src="../../static/goods/battery.png" class="equipment-image" mode="aspectFit"></image>
|
||||
<view class="info">
|
||||
<text class="equipment-model">{{ eq.batteryModel }}</text>
|
||||
<text class="equipment-count">库存:{{ eq.stockTotal }}个</text>
|
||||
<text class="equipment-price">¥{{ eq.repayment }}元/月</text>
|
||||
</view>
|
||||
<view class="tobuy">
|
||||
<u-button type="error" :disabled="disabled" size="mini" @click="buy(eq.goodsId)">立即购买</u-button>
|
||||
</view>
|
||||
</block>
|
||||
<block v-if="eq.equipmentCategory == '30'">
|
||||
<image v-if="eq.image" :src="eq.image" class="equipment-image" mode="aspectFit"></image>
|
||||
<image v-else src="../../static/goods/battery.png" class="equipment-image" mode="aspectFit"></image>
|
||||
<view class="info">
|
||||
<text class="equipment-model">{{ eq.batteryModel }}</text>
|
||||
<text class="equipment-count">库存:{{ eq.stockTotal }}个</text>
|
||||
<text class="equipment-price">¥{{ eq.batteryRent }}元/月</text>
|
||||
</view>
|
||||
<view class="tobuy">
|
||||
<u-button type="warning" :disabled="disabled" size="mini" @click="buy(eq.goodsId)">立即租用</u-button>
|
||||
</view>
|
||||
</block>
|
||||
<block v-if="eq.equipmentCategory == '40'">
|
||||
<image v-if="eq.image" :src="eq.image" class="equipment-image" mode="aspectFit"></image>
|
||||
<image v-else src="../../static/goods/battery.png" class="equipment-image" mode="aspectFit"></image>
|
||||
<view class="info">
|
||||
<text class="equipment-model">{{ eq.batteryModel }}</text>
|
||||
<text class="equipment-count">库存:{{ eq.stockTotal }}个</text>
|
||||
<text class="equipment-price">¥{{ eq.batteryRent }}元/月</text>
|
||||
</view>
|
||||
<view class="tobuy">
|
||||
<u-button type="primary" :disabled="disabled" size="mini" @click="buy(eq.goodsId)">立即租用</u-button>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<empty v-if="equipmentList.length == 0" :isLoading="isLoading" tips="商品已售罄" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import Search from '@/components/page/diyComponents/search/index.vue'
|
||||
// import {
|
||||
// getMoreListData
|
||||
// } from '@/core/app'
|
||||
// import * as ShopApi from '@/api/shop.js'
|
||||
|
||||
import Empty from '@/components/empty'
|
||||
import {
|
||||
listMerchant
|
||||
} from '@/websoft/api/merchant.js'
|
||||
import { pageEquipmentGoods } from '@/websoft/api/equipment-goods.js'
|
||||
import { getDictionaryOptions } from '@/websoft/api/dict.js'
|
||||
// import Empty from '@/components/empty'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
// Search,
|
||||
Empty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
record: {},
|
||||
// 正在加载中
|
||||
isLoading: true,
|
||||
// 是否授权了定位权限
|
||||
isAuthor: true,
|
||||
// 当前选择的商户ID
|
||||
merchantId: null,
|
||||
// 商户编号
|
||||
merchantCode: null,
|
||||
// 订单列表数据
|
||||
equipmentList: [],
|
||||
modelList: [],
|
||||
// 设备分类
|
||||
count: 0,
|
||||
value: 3,
|
||||
disabled: false,
|
||||
category: [
|
||||
{
|
||||
id: 0,
|
||||
name: '销售',
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: '分期',
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '以租代购',
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '租赁',
|
||||
count: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
onLoad(option) {
|
||||
console.log("option: ",option);
|
||||
const app = this
|
||||
// 记录当前选择的门店ID
|
||||
app.equipmentId = option.equipmentId
|
||||
app.merchantId = option.merchantId
|
||||
app.merchantCode = option.merchantCode
|
||||
// // 获取默认门店列表
|
||||
app.getMerchant()
|
||||
// // 获取字典数据
|
||||
app.getDict()
|
||||
|
||||
},
|
||||
methods: {
|
||||
getEquipment(){
|
||||
const app = this
|
||||
const { merchantCode } = this
|
||||
pageEquipmentGoods({merchantCode, status: 0}).then(res => {
|
||||
app.count = res.data.count
|
||||
app.equipmentList = res.data.list
|
||||
app.category[0].count = app.equipmentList.filter(d => d.equipmentCategory == '10').length
|
||||
app.category[1].count = app.equipmentList.filter(d => d.equipmentCategory == '20').length
|
||||
app.category[2].count = app.equipmentList.filter(d => d.equipmentCategory == '30').length
|
||||
app.category[3].count = app.equipmentList.filter(d => d.equipmentCategory == '40').length
|
||||
})
|
||||
},
|
||||
getDict(){
|
||||
const app = this
|
||||
getDictionaryOptions({dictCode: 'equipmentCategory'}).then(res => {
|
||||
app.category = res.data
|
||||
})
|
||||
getDictionaryOptions({dictCode: 'equipmentModel'}).then(res => {
|
||||
app.modelList = res.data
|
||||
})
|
||||
// 获取设备列表
|
||||
app.getEquipment()
|
||||
},
|
||||
// 获取门店列表
|
||||
getMerchant() {
|
||||
const app = this
|
||||
const { merchantId } = this
|
||||
app.isLoading = true
|
||||
console.log("merchantIdmerchantId: ",merchantId);
|
||||
listMerchant({merchantId})
|
||||
.then(result => {
|
||||
app.record = result.data[0]
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取用户坐标
|
||||
// 参考文档:https://uniapp.dcloud.io/api/location/location?id=getlocation
|
||||
getLocation(callback) {
|
||||
const app = this
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: callback,
|
||||
fail() {
|
||||
app.$toast('获取定位失败,请点击右下角按钮重新尝试定位')
|
||||
app.isAuthor = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 授权启用定位权限
|
||||
onAuthorize() {
|
||||
const app = this
|
||||
// #ifdef MP
|
||||
uni.openSetting({
|
||||
success(res) {
|
||||
if (res.authSetting['scope.userLocation']) {
|
||||
console.log('定位权限授权成功')
|
||||
app.isAuthor = true
|
||||
setTimeout(() => {
|
||||
// 获取用户坐标
|
||||
app.getLocation((res) => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
// 获取用户坐标
|
||||
app.getLocation((res) => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
|
||||
/**
|
||||
* 选择门店
|
||||
*/
|
||||
onSelectedShop(merchantId) {
|
||||
this.$navTo('package/shops/detail/index', {
|
||||
merchantId
|
||||
})
|
||||
},
|
||||
|
||||
buy(goodsId){
|
||||
console.log("goodsId: ",goodsId);
|
||||
this.$navTo('pages/checkout/checkout', { goodsId })
|
||||
},
|
||||
|
||||
changeCity() {
|
||||
this.$toast('切换区域')
|
||||
},
|
||||
onChangeStar(e){
|
||||
console.log("e: ",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shop-banner{
|
||||
background-color: #f3f3f3;
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.logo{
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 12rpx;
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
.shop-info{
|
||||
height: 100rpx;
|
||||
width: 560rpx;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex-direction: column;
|
||||
.shop-name{
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.shop-desc{
|
||||
display: flex;
|
||||
.shop-desc-text{
|
||||
font-size: 22rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.follow{
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
.category{
|
||||
width: 700rpx;
|
||||
margin: 20rpx auto;
|
||||
.category-name{
|
||||
margin-bottom: 20rpx;
|
||||
color: #999999;
|
||||
}
|
||||
.goods{
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.equipment-image{
|
||||
width: 240rpx;
|
||||
height: 160rpx;
|
||||
margin-right: 20rpx;
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
.info{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
height: 120rpx;
|
||||
min-width: 280rpx;
|
||||
.equipment-model{
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.equipment-count{
|
||||
color: #999999;
|
||||
}
|
||||
.equipment-price{
|
||||
color: #ff0000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,323 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 展馆图标 -->
|
||||
<view class="header-box">
|
||||
<view class="location" @click="changeCity">
|
||||
附近站点推荐
|
||||
</view>
|
||||
</view>
|
||||
<!-- 门店列表 -->
|
||||
<view class="shop-list">
|
||||
<view class="item" v-for="(item, index) in shopList" :key="index"
|
||||
@click="onSelectedShop(item.merchantId,item.merchantCode)">
|
||||
<image :src="item.logo" mode="aspectFit"></image>
|
||||
<view class="info">
|
||||
<text class="title">{{item.merchantName}}</text>
|
||||
<text class="desc">{{item.address}}</text>
|
||||
<view class="tag">
|
||||
<view class="mr12">
|
||||
<u-tag size="mini" plain text="租车站"></u-tag>
|
||||
</view>
|
||||
<view class="mr12">
|
||||
<u-tag size="mini" type="warning" plain text="换点站"></u-tag>
|
||||
</view>
|
||||
<view class="mr12">
|
||||
<u-tag size="mini" type="success" plain text="买车站"></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
<text class="distance">距离4.2km</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 定位按钮 -->
|
||||
<view v-if="!isAuthor" class="widget-location dis-flex flex-x-center flex-y-center" @click="onAuthorize()">
|
||||
<text class="iconfont icon-locate"></text>
|
||||
</view>
|
||||
<empty v-if="shopList.length == 0" :isLoading="isLoading" tips="请先登录" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store/index.js'
|
||||
import storage from '../../utils/storage'
|
||||
import { ACCESS_TOKEN, USER_ID } from '@/store/mutation-types'
|
||||
import { pageMerchant } from '@/websoft/api/merchant.js'
|
||||
import Empty from '@/components/empty'
|
||||
import { tenantId } from '@/config.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
// Search,
|
||||
Empty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '国内馆',
|
||||
// 正在加载中
|
||||
isLoading: false,
|
||||
// 是否授权了定位权限
|
||||
isAuthor: true,
|
||||
// 当前选择的门店ID
|
||||
selectedId: null,
|
||||
// 订单列表数据
|
||||
shopList: []
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const app = this
|
||||
if(!uni.getStorageSync('userId')){
|
||||
console.log("未登录1: ");
|
||||
return false;
|
||||
}
|
||||
// 获取用户坐标
|
||||
app.getLocation(res => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
},
|
||||
onShow() {
|
||||
const app = this
|
||||
console.log("app.shopList.length: ",app.$store.getters.token);
|
||||
if(app.shopList.length == 0){
|
||||
console.log("sssss: ");
|
||||
app.getShopList(app.longitude,app.latitude)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取门店列表
|
||||
getShopList(longitude, latitude) {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
pageMerchant({tenantId})
|
||||
.then(result => {
|
||||
if(result.code == 0){
|
||||
app.shopList = result.data.list
|
||||
}
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取用户坐标
|
||||
// 参考文档:https://uniapp.dcloud.io/api/location/location?id=getlocation
|
||||
getLocation(callback) {
|
||||
const app = this
|
||||
uni.getLocation({
|
||||
success: callback,
|
||||
fail() {
|
||||
app.$toast('获取定位失败,请点击右下角按钮重新尝试定位')
|
||||
app.isAuthor = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 授权启用定位权限
|
||||
onAuthorize() {
|
||||
const app = this
|
||||
// #ifdef MP
|
||||
uni.openSetting({
|
||||
success(res) {
|
||||
if (res.authSetting['scope.userLocation']) {
|
||||
console.log('定位权限授权成功')
|
||||
app.isAuthor = true
|
||||
setTimeout(() => {
|
||||
// 获取用户坐标
|
||||
app.getLocation((res) => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
// 获取用户坐标
|
||||
app.getLocation((res) => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
|
||||
navTo(merchantId) {
|
||||
const navTo = uni.$u.route()
|
||||
navTo('pages/merchant/detail', {
|
||||
merchantId
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 选择门店
|
||||
*/
|
||||
onSelectedShop(merchantId,merchantCode) {
|
||||
uni.$u.route('pages/merchant/detail', {
|
||||
merchantId,merchantCode
|
||||
})
|
||||
},
|
||||
|
||||
changeCity() {
|
||||
this.$toast('切换区域')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bg {
|
||||
background-color: #F5F5F8
|
||||
}
|
||||
|
||||
.shop-list {
|
||||
.item {
|
||||
padding: 20rpx;
|
||||
margin: 20rpx auto;
|
||||
background-color: #ffffff;
|
||||
width: 660rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
image {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
margin-right: 40rpx;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 28rpx;
|
||||
display: flex;
|
||||
|
||||
.mr12 {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.distance {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-box {
|
||||
padding: 20rpx 36rpx 0 36rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.location {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #999999;
|
||||
|
||||
image {
|
||||
width: 60rpx;
|
||||
height: 60rpx
|
||||
}
|
||||
}
|
||||
|
||||
.u-subsection {
|
||||
width: 260rpx;
|
||||
margin-top: 7rpx
|
||||
}
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
display: flex;
|
||||
width: 80%;
|
||||
height: 64rpx
|
||||
}
|
||||
|
||||
// 搜索输入框
|
||||
.search-input {
|
||||
width: 90%;
|
||||
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;
|
||||
|
||||
.input-placeholder {
|
||||
color: #aba9a9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shop-avatar {
|
||||
width: 200rpx;
|
||||
margin-right: 24rpx
|
||||
}
|
||||
|
||||
// 搜索按钮
|
||||
.search-button {
|
||||
width: 25%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.button {
|
||||
height: 64rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 0 5px 5px 0;
|
||||
background: #2C71C7
|
||||
}
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
width: 100%;
|
||||
margin-left: 20rpx;
|
||||
|
||||
.title {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.about {
|
||||
display: flex;
|
||||
|
||||
.mr12 {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.u-body-item {
|
||||
align-items: stretch !important
|
||||
}
|
||||
</style>
|
||||
Executable
+307
@@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<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="appTheme.mainBg" :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;
|
||||
border: 1rpx solid #fff;
|
||||
border-radius: 30rpx;
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&.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,311 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 消息列表 -->
|
||||
<view class="shop-list">
|
||||
<view class="item" v-for="(item, index) in list" :key="index"
|
||||
@click="onSelectedShop(item.merchantId,item.merchantCode)">
|
||||
<image :src="item.logo" mode="aspectFit"></image>
|
||||
<view class="info">
|
||||
<text class="title">{{item.merchantName}}</text>
|
||||
<text class="desc">{{item.address}}</text>
|
||||
<view class="tag">
|
||||
<view class="mr12">
|
||||
<u-tag size="mini" plain text="租车站"></u-tag>
|
||||
</view>
|
||||
<view class="mr12">
|
||||
<u-tag size="mini" type="warning" plain text="换点站"></u-tag>
|
||||
</view>
|
||||
<view class="mr12">
|
||||
<u-tag size="mini" type="success" plain text="买车站"></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
<text class="distance">距离4.2km</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 定位按钮 -->
|
||||
<view v-if="!isAuthor" class="widget-location dis-flex flex-x-center flex-y-center" @click="onAuthorize()">
|
||||
<text class="iconfont icon-locate"></text>
|
||||
</view>
|
||||
<empty v-if="list.length == 0" :isLoading="isLoading" tips="亲,暂无消息" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store/index.js'
|
||||
import storage from '../../utils/storage'
|
||||
import { ACCESS_TOKEN, USER_ID } from '@/store/mutation-types'
|
||||
import { pageMerchant } from '@/websoft/api/merchant.js'
|
||||
import { getSetting } from '@/websoft/api/setting.js'
|
||||
import Empty from '@/components/empty'
|
||||
import { userId } from '../../config'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
// Search,
|
||||
Empty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '国内馆',
|
||||
// 正在加载中
|
||||
isLoading: false,
|
||||
// 是否授权了定位权限
|
||||
isAuthor: true,
|
||||
// 当前选择的门店ID
|
||||
selectedId: null,
|
||||
// 订单列表数据
|
||||
list: []
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const app = this
|
||||
if(!store.getters.userId){
|
||||
console.log("未登录1: ");
|
||||
return false;
|
||||
}
|
||||
// 获取用户坐标
|
||||
app.getLocation(res => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
},
|
||||
onShow() {
|
||||
const app = this
|
||||
// getSetting('wx-official').then(res => {
|
||||
// console.log("res: ",res);
|
||||
// })
|
||||
if(app.list.length == 0){
|
||||
app.getShopList(app.longitude,app.latitude)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取门店列表
|
||||
getShopList(longitude, latitude) {
|
||||
const app = this
|
||||
},
|
||||
|
||||
// 获取用户坐标
|
||||
// 参考文档:https://uniapp.dcloud.io/api/location/location?id=getlocation
|
||||
getLocation(callback) {
|
||||
const app = this
|
||||
uni.getLocation({
|
||||
success: callback,
|
||||
fail() {
|
||||
app.$toast('获取定位失败,请点击右下角按钮重新尝试定位')
|
||||
app.isAuthor = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 授权启用定位权限
|
||||
onAuthorize() {
|
||||
const app = this
|
||||
// #ifdef MP
|
||||
uni.openSetting({
|
||||
success(res) {
|
||||
if (res.authSetting['scope.userLocation']) {
|
||||
console.log('定位权限授权成功')
|
||||
app.isAuthor = true
|
||||
setTimeout(() => {
|
||||
// 获取用户坐标
|
||||
app.getLocation((res) => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
// 获取用户坐标
|
||||
app.getLocation((res) => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
|
||||
navTo(merchantId) {
|
||||
const navTo = uni.$u.route()
|
||||
navTo('pages/merchant/detail', {
|
||||
merchantId
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 选择门店
|
||||
*/
|
||||
onSelectedShop(merchantId,merchantCode) {
|
||||
uni.$u.route('pages/merchant/detail', {
|
||||
merchantId,merchantCode
|
||||
})
|
||||
},
|
||||
|
||||
changeCity() {
|
||||
this.$toast('切换区域')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bg {
|
||||
background-color: #F5F5F8
|
||||
}
|
||||
|
||||
.shop-list {
|
||||
.item {
|
||||
padding: 20rpx;
|
||||
margin: 20rpx auto;
|
||||
background-color: #ffffff;
|
||||
width: 660rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
image {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
margin-right: 40rpx;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 28rpx;
|
||||
display: flex;
|
||||
|
||||
.mr12 {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.distance {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-box {
|
||||
padding: 20rpx 36rpx 0 36rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.location {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #999999;
|
||||
|
||||
image {
|
||||
width: 60rpx;
|
||||
height: 60rpx
|
||||
}
|
||||
}
|
||||
|
||||
.u-subsection {
|
||||
width: 260rpx;
|
||||
margin-top: 7rpx
|
||||
}
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
display: flex;
|
||||
width: 80%;
|
||||
height: 64rpx
|
||||
}
|
||||
|
||||
// 搜索输入框
|
||||
.search-input {
|
||||
width: 90%;
|
||||
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;
|
||||
|
||||
.input-placeholder {
|
||||
color: #aba9a9
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shop-avatar {
|
||||
width: 200rpx;
|
||||
margin-right: 24rpx
|
||||
}
|
||||
|
||||
// 搜索按钮
|
||||
.search-button {
|
||||
width: 25%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.button {
|
||||
height: 64rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 0 5px 5px 0;
|
||||
background: #2C71C7
|
||||
}
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
width: 100%;
|
||||
margin-left: 20rpx;
|
||||
|
||||
.title {
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.about {
|
||||
display: flex;
|
||||
|
||||
.mr12 {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.u-body-item {
|
||||
align-items: stretch !important
|
||||
}
|
||||
</style>
|
||||
Executable
+566
@@ -0,0 +1,566 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container" :style="appThemeStyle">
|
||||
<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.goodsImage" mode="scaleToFill"></image>
|
||||
</view>
|
||||
<!-- 商品信息 -->
|
||||
<view class="goods-content">
|
||||
<view class="goods-title"><text class="twoline-hide">{{ item.goodsName }}</text></view>
|
||||
<view class="goods-props clearfix">
|
||||
<view class="goods-props-item" v-for="(props, idx) in item.goodsProps" :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.goodsPrice }}</text>
|
||||
</view>
|
||||
<view class="goods-num">
|
||||
<text>×{{ item.totalNum }}</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: calc(constant(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: $main-bg;
|
||||
|
||||
.score-icon {
|
||||
background: $main-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&.score-review {
|
||||
color: $vice-bg;
|
||||
|
||||
.score-icon {
|
||||
background: $vice-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&.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: $main-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&.score-review {
|
||||
.score {
|
||||
background: $vice-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&.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;
|
||||
background: rgba(0, 0, 0, 0.64);
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
font-weight: bolder;
|
||||
font-size: 22rpx;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: 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;
|
||||
z-index: 11;
|
||||
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.btn-wrapper {
|
||||
height: 140rpx;
|
||||
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, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Executable
+323
@@ -0,0 +1,323 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<view class="reject" v-if="form.apply_status == 10">
|
||||
<u-alert-tips type="error" title="申请已提交:" description="请耐心等待工作人员的审核"></u-alert-tips>
|
||||
</view>
|
||||
<view class="reject" v-else-if="form.apply_status == 30">
|
||||
<u-alert-tips type="error" title="您的申请已被驳回:" :description="form.reject_reason"></u-alert-tips>
|
||||
</view>
|
||||
<u-form :model="form" ref="uForm" label-width="140rpx">
|
||||
<!-- 标题 -->
|
||||
<view class="page-title">身份证信息</view>
|
||||
<view class="form-wrapper">
|
||||
<u-form-item label="正面" prop="img1">
|
||||
<image :src="orderSourceData[0]" v-if="orderSourceData[0]" mode="aspectFill" style="width: 300rpx;height: 200rpx"></image>
|
||||
<image src="../../static/not-dealer.png" v-else mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(1)"></image>
|
||||
</u-form-item>
|
||||
<u-form-item label="反面" prop="img2">
|
||||
<image :src="orderSourceData[1]" v-if="orderSourceData[1]" mode="aspectFill" style="width: 300rpx;height: 200rpx"></image>
|
||||
<image src="../../static/not-dealer.png" v-else mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(2)"></image>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<!-- 标题 -->
|
||||
<view class="page-title">其他</view>
|
||||
<!-- 表单组件 -->
|
||||
<view class="form-wrapper">
|
||||
<u-form-item label="人车合照" prop="img3">
|
||||
<image :src="orderSourceData[2]" v-if="orderSourceData[2]" mode="aspectFill" style="width: 300rpx;height: 200rpx"></image>
|
||||
<image src="../../static/not-dealer.png" v-else mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(3)"></image>
|
||||
</u-form-item>
|
||||
<u-form-item label="车子照片" prop="img4">
|
||||
<image :src="orderSourceData[3]" v-if="orderSourceData[3]" mode="aspectFill" style="width: 300rpx;height: 200rpx"></image>
|
||||
<image src="../../static/not-dealer.png" v-else mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(4)"></image>
|
||||
</u-form-item>
|
||||
<u-form-item label="安装照片" prop="img5">
|
||||
<image :src="orderSourceData[4]" v-if="orderSourceData[4]" mode="aspectFill" style="width: 300rpx;height: 200rpx"></image>
|
||||
<image src="../../static/not-dealer.png" v-else mode="aspectFill" style="width: 300rpx;height: 200rpx" @click="chooseImage(5)"></image>
|
||||
</u-form-item>
|
||||
</view>
|
||||
</u-form>
|
||||
<view class="btn">
|
||||
<u-button type="primary" shape="circle" @click="handleSubmit()">
|
||||
{{ submitText }}
|
||||
</u-button>
|
||||
</view>
|
||||
<view class="btn">
|
||||
<u-button shape="circle" @click="resetting">
|
||||
重置
|
||||
</u-button>
|
||||
</view>
|
||||
<view class="btn"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
uploadFile
|
||||
} from '@/websoft/api/upload.js'
|
||||
import {
|
||||
pageOrder,
|
||||
removeOrder,
|
||||
receiptOrder,
|
||||
getOrder
|
||||
} from '@/websoft/api/order.js'
|
||||
import { fileUrl } from '@/config.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
order: {},
|
||||
orderSourceData: [],
|
||||
// 按钮禁用
|
||||
disabled: false,
|
||||
submitText: '证件已上传并确认收货',
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
const app = this
|
||||
app.orderId = options.orderId
|
||||
},
|
||||
onShow() {
|
||||
this.getData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 加载订单
|
||||
getData(){
|
||||
const app = this
|
||||
const { orderId } = this
|
||||
getOrder(orderId).then(res => {
|
||||
console.log("res: ",res);
|
||||
app.order = res.data
|
||||
if(res.data.orderSourceData.length > 0){
|
||||
app.orderSourceData = JSON.parse(res.data.orderSourceData)
|
||||
}
|
||||
})
|
||||
},
|
||||
// 重置
|
||||
resetting(){
|
||||
const app = this
|
||||
const { orderId } = app
|
||||
receiptOrder({
|
||||
orderId,
|
||||
orderSourceData: ''
|
||||
}).then(result => {
|
||||
app.$success("重置成功")
|
||||
app.orderSourceData = []
|
||||
// 刷新订单列表
|
||||
app.getData()
|
||||
})
|
||||
},
|
||||
// 表单提交
|
||||
handleSubmit() {
|
||||
const app = this
|
||||
this.$navTo('pages/order/index')
|
||||
},
|
||||
|
||||
// 上传图片
|
||||
chooseImage(id) {
|
||||
const app = this
|
||||
const { orderId } = this
|
||||
const type = 'photo' + id
|
||||
// 选择图片
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
|
||||
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
|
||||
success(chooseImageRes) {
|
||||
const tempFilePaths = chooseImageRes.tempFilePaths;
|
||||
uploadFile({
|
||||
filePath: tempFilePaths[0],
|
||||
fileType: 'image',
|
||||
name: 'file'
|
||||
}).then(res => {
|
||||
console.log("res: ", res);
|
||||
app.orderSourceData.push(fileUrl + res.data.path)
|
||||
receiptOrder({
|
||||
orderId,
|
||||
orderSourceData: JSON.stringify(app.orderSourceData)
|
||||
}).then(result => {
|
||||
app.$success("上传成功")
|
||||
// 刷新订单列表
|
||||
app.getData()
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
</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, $main-bg, $main-bg2);
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 上传凭证
|
||||
.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;
|
||||
background: rgba(0, 0, 0, 0.64);
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
font-weight: bolder;
|
||||
font-size: 22rpx;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-license {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.pops-content {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.sfz,
|
||||
.sfz1,
|
||||
.sfz2,
|
||||
.yyzz {
|
||||
width: 140rpx;
|
||||
padding-top: 20rpx;
|
||||
padding-right: 10rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 700rpx;
|
||||
margin: 20rpx auto;
|
||||
height: 100rpx;
|
||||
}
|
||||
|
||||
.reject {
|
||||
width: 700rpx;
|
||||
margin: 20rpx auto;
|
||||
}
|
||||
</style>
|
||||
Executable
+1577
File diff suppressed because it is too large
Load Diff
Executable
+266
@@ -0,0 +1,266 @@
|
||||
<template>
|
||||
<view v-if="!isLoading && express.length" class="container">
|
||||
|
||||
<!-- 标签栏 -->
|
||||
<u-tabs v-show="tabs.length > 1" class="my-tabs" :list="tabs" :isScroll="true" :current="curTab"
|
||||
:active-color="appTheme.mainBg" :duration="0.2" bar-width="60" @change="onChangeTab"></u-tabs>
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<view v-show="tabs.length > 1" class="deliver-goods-list i-card clearfix">
|
||||
<view class="goods-item" v-for="(goods, idx) in express[curTab].goods" :key="idx">
|
||||
<image class="goods-img" :src="goods.goods.goods_image" alt="商品图片" />
|
||||
<view class="title">共{{ goods.delivery_num }}件</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 物流信息 -->
|
||||
<view class="express i-card">
|
||||
<view class="info-item">
|
||||
<view class="item-lable">物流公司:</view>
|
||||
<view class="item-content">
|
||||
<text v-if="express[curTab].delivery_method == 20">无需物流</text>
|
||||
<text v-else>{{ express[curTab].express ? express[curTab].express.express_name : '--' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<view class="item-lable">物流单号:</view>
|
||||
<view class="item-content">
|
||||
<text>{{ express[curTab].express_no ? express[curTab].express_no : '--' }}</text>
|
||||
<view v-show="express[curTab].express_no" class="act-copy"
|
||||
@click.stop="handleCopy(express[curTab].express_no)">
|
||||
<text>复制</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 物流轨迹 -->
|
||||
<view class="logis-detail" v-if="express[curTab].traces && express[curTab].traces.length">
|
||||
<view class="logis-item" :class="{ first: index === 0 }" v-for="(item, index) in express[curTab].traces"
|
||||
: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.time }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as OrderApi from '@/api/order'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 当前标签索引
|
||||
curTab: 0,
|
||||
// 当前订单ID
|
||||
orderId: null,
|
||||
// 物流信息
|
||||
express: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tabs() {
|
||||
if (this.express && this.express.length) {
|
||||
return this.express.map((item, index) => {
|
||||
return { name: `包裹${index + 1}` }
|
||||
})
|
||||
}
|
||||
return []
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
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('复制成功')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 切换标签项
|
||||
onChangeTab(index) {
|
||||
this.curTab = index
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.my-tabs {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
// 通栏卡片
|
||||
.i-card {
|
||||
background: #fff;
|
||||
padding: 24rpx 24rpx;
|
||||
}
|
||||
|
||||
// 物流公司
|
||||
.express {
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.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: 6rpx;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 商品列表
|
||||
.deliver-goods-list {
|
||||
margin-bottom: -30rpx;
|
||||
|
||||
.goods-item {
|
||||
position: relative;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
width: 130rpx;
|
||||
height: 130rpx;
|
||||
float: left;
|
||||
margin-right: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.goods-img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #fff;
|
||||
padding: 4rpx 0;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 物流轨迹
|
||||
.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>
|
||||
Executable
+835
@@ -0,0 +1,835 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container" :style="appThemeStyle">
|
||||
|
||||
<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>
|
||||
|
||||
<!-- 快递配送:配送地址 -->
|
||||
<view v-if="order.delivery_type == DeliveryTypeEnum.EXPRESS.value" class="delivery-address i-card">
|
||||
<view class="link-man">
|
||||
<text class="name">{{ order.address.name }}</text>
|
||||
<text class="phone">{{ order.address.phone }}</text>
|
||||
</view>
|
||||
<view class="address">
|
||||
<text class="region" v-for="(region, idx) in order.address.region" :key="idx">{{ region }}</text>
|
||||
<text class="detail">{{ order.address.detail }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快递配送:门店自提 -->
|
||||
<view v-if="order.delivery_type == DeliveryTypeEnum.EXTRACT.value" class="delivery-extract i-card"
|
||||
@click="handleTargetExtract(order.extract_shop.shop_id)">
|
||||
<view class="extract-top">
|
||||
<text class="title">自提门店</text>
|
||||
<text class="subtitle">您须到该自提点取货</text>
|
||||
</view>
|
||||
<view class="shop-info">
|
||||
<view class="icon-location">
|
||||
<text class="iconfont icon-dingwei"></text>
|
||||
</view>
|
||||
<view class="shop-content">
|
||||
<view class="shop-name">
|
||||
<text>{{ order.extract_shop.shop_name }}</text>
|
||||
</view>
|
||||
<view class="shop-describe">
|
||||
<text class="item-text">{{ order.extract_shop.region.province }}</text>
|
||||
<text class="item-text">{{ order.extract_shop.region.city }}</text>
|
||||
<text class="item-text">{{ order.extract_shop.region.region }}</text>
|
||||
<text class="item-text">{{ order.extract_shop.address }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="icon-arrow">
|
||||
<text class="iconfont icon-arrow-right"></text>
|
||||
</view>
|
||||
</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.goods_price }}</text>
|
||||
</view>
|
||||
<view class="goods-num">
|
||||
<text>×{{ goods.total_num }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 商品售后 -->
|
||||
<view class="goods-refund">
|
||||
<text v-if="goods.refund" class="stata-text">已申请售后</text>
|
||||
</view>
|
||||
</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">
|
||||
<!-- 已申请取消 -->
|
||||
<view v-if="order.order_status == OrderStatusEnum.APPLY_CANCEL.value" class="f-28 col-8">取消申请中</view>
|
||||
<!-- 确认核销 -->
|
||||
<block v-else-if="order.pay_status == PayStatusEnum.SUCCESS.value && order.delivery_type == DeliveryTypeEnum.EXTRACT.value
|
||||
&& order.delivery_status == DeliveryStatusEnum.NOT_DELIVERED.value">
|
||||
<view class="btn-item active" @click="onConfirmExtract()">确认核销</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSceneData } from '@/core/app'
|
||||
import {
|
||||
DeliveryStatusEnum,
|
||||
DeliveryTypeEnum,
|
||||
OrderStatusEnum,
|
||||
PayStatusEnum,
|
||||
ReceiptStatusEnum
|
||||
} from '@/common/enum/order'
|
||||
import * as OrderApi from '@/api/shop/order'
|
||||
import { wxPayment } from '@/core/app'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 枚举类
|
||||
DeliveryStatusEnum,
|
||||
DeliveryTypeEnum,
|
||||
OrderStatusEnum,
|
||||
PayStatusEnum,
|
||||
ReceiptStatusEnum,
|
||||
// 当前订单ID
|
||||
orderId: null,
|
||||
// 加载中
|
||||
isLoading: true,
|
||||
// 当前订单详情
|
||||
order: {},
|
||||
// 当前设置
|
||||
setting: {}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 记录query参数
|
||||
this.onRecordQuery(options)
|
||||
// 获取当前订单信息
|
||||
this.getOrderDetail()
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 记录query参数
|
||||
onRecordQuery(query) {
|
||||
const scene = getSceneData(query)
|
||||
this.orderId = query.orderId ? parseInt(query.orderId) : parseInt(scene.oid)
|
||||
},
|
||||
|
||||
// 获取当前订单信息
|
||||
getOrderDetail() {
|
||||
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
|
||||
})
|
||||
},
|
||||
|
||||
// 复制指定内容
|
||||
handleCopy(value) {
|
||||
const app = this
|
||||
uni.setClipboardData({
|
||||
data: value,
|
||||
success() {
|
||||
app.$toast('复制成功')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转到门店详情页
|
||||
handleTargetExtract(shopId) {
|
||||
this.$navTo('pages/shop/detail', { shopId })
|
||||
},
|
||||
|
||||
// 跳转到物流跟踪页面
|
||||
handleTargetExpress() {
|
||||
this.$navTo('pages/order/express/index', { orderId: this.orderId })
|
||||
},
|
||||
|
||||
// 跳转到商品详情页面
|
||||
handleTargetGoods(goodsId) {
|
||||
this.$navTo('pages/goods/detail', { goodsId })
|
||||
},
|
||||
|
||||
// 确认核销订单
|
||||
onConfirmExtract() {
|
||||
const app = this
|
||||
uni.showModal({
|
||||
title: '友情提示',
|
||||
content: '确认要核销该订单吗?请确认买家已取到货~',
|
||||
success(o) {
|
||||
if (o.confirm) {
|
||||
OrderApi.extract(app.orderId)
|
||||
.then(result => {
|
||||
// 显示成功信息
|
||||
app.$success(result.message)
|
||||
setTimeout(() => {
|
||||
// 刷新当前订单数据
|
||||
app.getOrderDetail()
|
||||
}, 1500)
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #f4f4f4;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
padding-bottom: calc(constant(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;
|
||||
background-color: #fff;
|
||||
border-radius: 28rpx;
|
||||
border-color: rgb(102, 102, 102);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: #c7a157;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 通栏卡片
|
||||
.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-extract {
|
||||
margin-top: -50rpx;
|
||||
|
||||
.extract-top {
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon-location {
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.shop-content {
|
||||
flex: 1;
|
||||
margin-left: 26rpx;
|
||||
font-size: 24rpx;
|
||||
|
||||
.shop-name {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.shop-describe {
|
||||
color: #777;
|
||||
|
||||
.item-text {
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 收货地址
|
||||
.delivery-address {
|
||||
margin-top: -50rpx;
|
||||
|
||||
.link-man {
|
||||
line-height: 46rpx;
|
||||
color: #333;
|
||||
|
||||
.name {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
margin-top: 12rpx;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
|
||||
.detail {
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 物流公司
|
||||
.express {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.item-lable {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
|
||||
.act-copy {
|
||||
margin-left: 20rpx;
|
||||
padding: 2rpx 20rpx;
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
border: 1rpx solid #c1c1c1;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 右侧箭头
|
||||
.right-arrow {
|
||||
margin-left: 16rpx;
|
||||
// color: #777;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 商品列表
|
||||
.goods-list {
|
||||
|
||||
// 商品项
|
||||
.goods-item {
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
// 商品信息
|
||||
.goods-main {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
// 商品图片
|
||||
.goods-image {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 商品内容
|
||||
.goods-content {
|
||||
flex: 1;
|
||||
padding-left: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
|
||||
.goods-title {
|
||||
font-size: 26rpx;
|
||||
max-height: 76rpx;
|
||||
}
|
||||
|
||||
.goods-props {
|
||||
margin-top: 14rpx;
|
||||
height: 40rpx;
|
||||
color: #ababab;
|
||||
font-size: 24rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.goods-props-item {
|
||||
display: inline-block;
|
||||
margin-right: 14rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #F5F5F5;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 交易信息
|
||||
.goods-trade {
|
||||
padding-top: 16rpx;
|
||||
width: 150rpx;
|
||||
text-align: right;
|
||||
color: $uni-text-color-grey;
|
||||
font-size: 26rpx;
|
||||
|
||||
.goods-price {
|
||||
vertical-align: bottom;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.unit {
|
||||
margin-right: -2rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 商品售后
|
||||
.goods-refund {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.stata-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
border-radius: 28rpx;
|
||||
padding: 8rpx 26rpx;
|
||||
font-size: 24rpx;
|
||||
color: #383838;
|
||||
border: 1rpx solid #a8a8a8;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 订单信息
|
||||
.order-info {
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.item-lable {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
|
||||
.act-copy {
|
||||
margin-left: 20rpx;
|
||||
padding: 2rpx 20rpx;
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
border: 1rpx solid #c1c1c1;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 交易信息
|
||||
.trade-info {
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.item-lable {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background: #f1f1f1;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.trade-total {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.goods-price {
|
||||
margin-left: 12rpx;
|
||||
vertical-align: bottom;
|
||||
color: $main-bg;
|
||||
|
||||
.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: 180rpx;
|
||||
border-radius: 30rpx;
|
||||
padding: 12rpx 26rpx;
|
||||
font-size: 28rpx;
|
||||
color: #383838;
|
||||
text-align: center;
|
||||
border: 1rpx solid #a8a8a8;
|
||||
margin-left: 24rpx;
|
||||
|
||||
&.active {
|
||||
border: none;
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 弹出层-支付方式
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 弹出层 - 核销二维码
|
||||
.qrcode-popup {
|
||||
padding: 36rpx 30rpx;
|
||||
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 26rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pop-content {
|
||||
min-height: 260rpx;
|
||||
padding: 0 10rpx;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 510rpx;
|
||||
height: 510rpx;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+811
@@ -0,0 +1,811 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<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="appTheme.mainBg" :duration="0.2"
|
||||
@change="onChangeTab" />
|
||||
<!-- 订单列表 -->
|
||||
<view class="order-list">
|
||||
<view class="order-item" v-for="(item, index) in list" :key="index" v-if="item.equipmentGoods">
|
||||
<view class="item-top">
|
||||
<view class="item-top-left">
|
||||
<text class="order-time">{{ item.merchantName }}</text>
|
||||
</view>
|
||||
<view class="item-top-right">
|
||||
<text class="state-text" v-if="item.payStatus == 10">待支付</text>
|
||||
<text class="state-text" v-if="item.payStatus == 20 && item.receiptStatus == 10">待绑定</text>
|
||||
<text class="state-text" v-if="item.orderStatus == 30 && item.receiptStatus == 20">已完成</text>
|
||||
<text class="state-text" v-if="item.receiptStatus == 30">已退租</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 商品列表 -->
|
||||
<view class="goods-list" @click="handleTargetDetail(item.orderId)">
|
||||
<view class="goods-item">
|
||||
<!-- 商品图片 -->
|
||||
<view class="goods-image">
|
||||
<image v-if="item.image" :src="item.image" class="equipment-image" mode="aspectFit">
|
||||
</image>
|
||||
<image v-else class="image" src="/static/goods/battery.png" mode="scaleToFill"></image>
|
||||
</view>
|
||||
<!-- 商品信息 -->
|
||||
<view class="goods-content">
|
||||
<view class="goods-title">
|
||||
<text class="twoline-hide">
|
||||
下单类型:
|
||||
<span v-if="item.equipmentGoods.equipmentCategory == 10">销售</span>
|
||||
<span v-if="item.equipmentGoods.equipmentCategory == 20">分期</span>
|
||||
<span v-if="item.equipmentGoods.equipmentCategory == 30">以租代购</span>
|
||||
<span v-if="item.equipmentGoods.equipmentCategory == 40">租赁</span>
|
||||
</text>
|
||||
<text class="twoline-hide">订单编号:{{ item.orderNo }}</text>
|
||||
<text class="twoline-hide"
|
||||
v-if="item.orderStatus == 30">到期时间:{{ item.expirationTime }}</text>
|
||||
<text class="twoline-hide" v-else>下单时间:{{ item.payTime }}</text>
|
||||
<text class="twoline-hide"
|
||||
v-if="item.orderStatus == 30">设备编号:{{ item.equipment.equipmentCode }}</text>
|
||||
<text class="twoline-hide">设备名称:{{ item.equipmentGoods.goodsName }}</text>
|
||||
<text class="twoline-hide">设备型号:{{ item.equipmentGoods.batteryModel }}</text>
|
||||
<text class="twoline-hide yuqi-text" v-if="item.expirationDay < 0">逾期状态: (已逾期{{ Math.abs(item.expirationDay) }}天)</text>
|
||||
<text class="twoline-hide yuqi-text" v-if="item.expirationDay < 7 && item.expirationDay > 0">逾期状态: 即将过期</text>
|
||||
</view>
|
||||
<view class="goods-props clearfix">
|
||||
<!-- <view class="goods-props-item">
|
||||
<text>props.value.name</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<!-- 交易信息 -->
|
||||
<!-- <view class="goods-trade">
|
||||
<view class="goods-price">
|
||||
<text class="unit">¥</text>
|
||||
<text class="value">{{ item.batteryRent }}</text>
|
||||
</view>
|
||||
<view class="goods-num">
|
||||
<text>x1</text>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<!-- 订单合计 -->
|
||||
<view class="order-total">
|
||||
<!-- <text>押金¥{{ item.batteryDeposit }}, 保险¥{{ item.batteryInsurance }}, 订单金额</text> -->
|
||||
<text>合计</text>
|
||||
<text class="unit" style="color: #ff0000;">¥</text>
|
||||
<text class="money">{{ item.totalPrice }}</text>
|
||||
</view>
|
||||
<!-- 订单操作 -->
|
||||
<view v-if="item.orderStatus != OrderStatusEnum.CANCELLED.value" class="order-handle">
|
||||
<view class="btn-group clearfix">
|
||||
<!-- 未支付取消订单 -->
|
||||
<block v-if="item.payStatus == PayStatusEnum.PENDING.value">
|
||||
<view class="btn-item" @click="onCancel(item.orderId)">取消</view>
|
||||
</block>
|
||||
<!-- 已支付进行中的订单 -->
|
||||
<block v-if="item.orderStatus != OrderStatusEnum.APPLY_CANCEL.value">
|
||||
<!-- <block v-if="item.payStatus == PayStatusEnum.SUCCESS.value && item.deliveryStatus == DeliveryStatusEnum.NOT_DELIVERED.value">
|
||||
<view class="btn-item" @click="onCancel(item.orderId)">申请取消</view>
|
||||
</block> -->
|
||||
<!-- 订单核销码 -->
|
||||
<block v-if="item.payStatus == PayStatusEnum.SUCCESS.value && item.deliveryType == DeliveryTypeEnum.EXTRACT.value
|
||||
&& item.deliveryStatus == DeliveryStatusEnum.NOT_DELIVERED.value">
|
||||
<view class="btn-item active" @click="onExtractQRCode(item.orderId)">
|
||||
<text class="iconfont icon-qr-extract"></text>
|
||||
<text class="m-l-10">核销码</text>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
<!-- 已申请取消 -->
|
||||
<view v-else class="f-28 col-8">取消申请中</view>
|
||||
<!-- 未支付的订单 -->
|
||||
<block v-if="item.payStatus == PayStatusEnum.PENDING.value">
|
||||
<view class="btn-item active" @click="onPay(item.orderId)">去支付</view>
|
||||
</block>
|
||||
<!-- 确认收货 -->
|
||||
<block
|
||||
v-if="item.payStatus == PayStatusEnum.SUCCESS.value && item.deliveryStatus == DeliveryStatusEnum.NOT_DELIVERED.value && item.receiptStatus == ReceiptStatusEnum.NOT_RECEIVED.value">
|
||||
<view class="btn-item active" v-if="(item.orderSourceData && JSON.parse(item.orderSourceData).length == 5) || item.equipmentGoods.equipmentCategory == '10'" @click="onBind(item)">绑定设备</view>
|
||||
<view class="btn-item active" v-else @click="chooseImage(item.orderId)">确认收货</view>
|
||||
</block>
|
||||
<!-- 订单评价 -->
|
||||
<block v-if="item.orderStatus == OrderStatusEnum.COMPLETED.value && item.isComment == 0">
|
||||
<view class="btn-item" v-if="item.equipmentGoods.equipmentCategory == '40'"
|
||||
@click="handleChangeEquipment(item.orderId,item.equipment.equipmentId)">换电</view>
|
||||
<!-- <view class="btn-item" v-if="item.equipmentGoods.equipmentCategory == '锂电池租赁' || item.equipmentGoods.equipmentCategory == '电动车租赁'" @click="handleTargetDetail(item.orderId)">续费</view> -->
|
||||
<view class="btn-item" @click="handleTargetDetail(item.orderId)">详情</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
<!-- 核销二维码弹窗 -->
|
||||
<u-popup v-model="showQRCodePopup" mode="center" border-radius="26" :closeable="true">
|
||||
<view class="qrcode-popup">
|
||||
<view class="title">自提核销二维码</view>
|
||||
<view class="pop-content">
|
||||
<image v-if="qrcodeImage" class="image" :src="qrcodeImage"></image>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<!-- 绑定设备码 -->
|
||||
<u-popup v-model="showQRCodeBind" mode="center" border-radius="26" :closeable="true">
|
||||
<view class="qrcode-bind">
|
||||
<view class="title">请核对要绑定的设备编号是否正确</view>
|
||||
<view class="bind-content">
|
||||
<input v-model="bindValue" class="input" @confirm="onSearch" focus="true" placeholder="请输入设备编号"
|
||||
type="text"></input>
|
||||
</view>
|
||||
<view class="submit">
|
||||
<u-button type="error" @click="doBind">立即绑定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<!-- 换电 -->
|
||||
<u-popup v-model="showQRCodeBindChange" mode="center" border-radius="26" :closeable="true">
|
||||
<view class="qrcode-bind">
|
||||
<view class="title">请输入更换的新电池编号</view>
|
||||
<view class="bind-content">
|
||||
<input v-model="bindValue" class="input" @confirm="onSearch" focus="true" placeholder="请输入设备编号"
|
||||
type="text"></input>
|
||||
</view>
|
||||
<view class="submit">
|
||||
<u-button type="error" @click="doEquipment(true)">确定更换电池</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
DeliveryStatusEnum,
|
||||
DeliveryTypeEnum,
|
||||
OrderStatusEnum,
|
||||
PayStatusEnum,
|
||||
ReceiptStatusEnum
|
||||
} from '@/common/enum/order'
|
||||
import store from '@/store/index.js'
|
||||
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 * as EquipmentApi from '@/websoft/api/equipment.js'
|
||||
import {
|
||||
pageOrder,
|
||||
removeOrder,
|
||||
receiptOrder
|
||||
} from '@/websoft/api/order.js'
|
||||
import {
|
||||
bindEquipment
|
||||
} from '@/websoft/api/equipment.js'
|
||||
import {
|
||||
uploadFile
|
||||
} from '@/websoft/api/upload.js'
|
||||
import { fileUrl } from '@/config.js';
|
||||
|
||||
// 每页记录数量
|
||||
const pageSize = 15
|
||||
|
||||
// tab栏数据
|
||||
const tabs = [{
|
||||
name: `全部`,
|
||||
value: 'all'
|
||||
}, {
|
||||
name: `待支付`,
|
||||
value: 'payment'
|
||||
}, {
|
||||
name: `待绑定`,
|
||||
value: 'delivery'
|
||||
}, {
|
||||
name: `已完成`,
|
||||
value: 'comment'
|
||||
}]
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
// 枚举类
|
||||
DeliveryStatusEnum,
|
||||
DeliveryTypeEnum,
|
||||
OrderStatusEnum,
|
||||
PayStatusEnum,
|
||||
ReceiptStatusEnum,
|
||||
|
||||
// 当前页面参数
|
||||
options: {
|
||||
dataType: 'all'
|
||||
},
|
||||
// tab栏数据
|
||||
tabs,
|
||||
// 当前标签索引
|
||||
curTab: 0,
|
||||
// 订单列表数据
|
||||
list: [],
|
||||
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: true,
|
||||
// 每页数据的数量; 默认10
|
||||
page: {
|
||||
size: pageSize
|
||||
},
|
||||
// 数量要大于4条才显示无更多数据
|
||||
noMoreSize: 4,
|
||||
// 空布局
|
||||
empty: {
|
||||
tip: '亲,暂无订单记录'
|
||||
}
|
||||
},
|
||||
// 控制onShow事件是否刷新订单列表
|
||||
canReset: false,
|
||||
// 核销二维码弹窗
|
||||
showQRCodePopup: false,
|
||||
// 核销二维码图片url (通过后端获取)
|
||||
qrcodeImage: '',
|
||||
where: {},
|
||||
showQRCodeBind: false,
|
||||
bindValue: '',
|
||||
// 选择的设备
|
||||
equipment: {},
|
||||
equipmentId: null,
|
||||
showQRCodeBindChange: false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 初始化当前选中的标签
|
||||
this.initCurTab(options)
|
||||
// 注册全局事件订阅: 是否刷新订单列表
|
||||
uni.$on('syncRefreshOrder', canReset => {
|
||||
this.canReset = canReset
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
this.canReset && this.onRefreshList()
|
||||
this.canReset = false
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面的卸载
|
||||
*/
|
||||
onUnload() {
|
||||
// 卸载全局事件订阅
|
||||
uni.$off('syncRefreshOrder')
|
||||
},
|
||||
|
||||
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 => {
|
||||
// console.log("list: ",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
|
||||
const {
|
||||
curTab
|
||||
} = this
|
||||
// app.where.userId = uni.getStorageSync('userId')
|
||||
console.log("curTab: ", curTab);
|
||||
app.where = {}
|
||||
if (curTab == 0) {
|
||||
app.where = {}
|
||||
}
|
||||
if (curTab == 1) {
|
||||
app.where.payStatus = 10;
|
||||
}
|
||||
if (curTab == 2) {
|
||||
app.where.payStatus = 20;
|
||||
app.where.receiptStatus = 10;
|
||||
}
|
||||
if (curTab == 3) {
|
||||
app.where.payStatus = 20;
|
||||
app.where.receiptStatus = 20;
|
||||
}
|
||||
if (curTab == 4) {
|
||||
app.where.receiptStatus = 20;
|
||||
app.where.isComment = 0;
|
||||
}
|
||||
app.where.userId = uni.getStorageSync('userId');
|
||||
app.where.isRenew = 0;
|
||||
pageOrder(app.where).then(res => {
|
||||
app.list = res.data.list
|
||||
const curPageLen = res.data.list.length
|
||||
const totalSize = res.data.count
|
||||
console.log("totalSize: ", totalSize);
|
||||
app.mescroll.endBySize(curPageLen, totalSize)
|
||||
|
||||
app.list.map((d,index) => {
|
||||
const setTime = new Date(d.expirationTime);
|
||||
const nowTime = new Date();
|
||||
const restSec = setTime.getTime() - nowTime.getTime();
|
||||
// 剩余天数
|
||||
const day = parseInt(restSec / (60*60*24*1000));
|
||||
console.log("逾期天数: ",Math.abs(day));
|
||||
app.list[index].expirationDay = day
|
||||
})
|
||||
})
|
||||
// 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.totalNum = 0
|
||||
item.goods.forEach(goods => {
|
||||
item.totalNum += goods.totalNum
|
||||
})
|
||||
})
|
||||
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) {
|
||||
removeOrder(orderId)
|
||||
.then(result => {
|
||||
// 显示成功信息
|
||||
app.$toast(result.message)
|
||||
// 刷新订单列表
|
||||
app.onRefreshList()
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 确认收货
|
||||
onReceipt(orderId) {
|
||||
const app = this
|
||||
uni.showModal({
|
||||
title: '友情提示',
|
||||
content: '确认收到商品了吗?',
|
||||
success(o) {
|
||||
if (o.confirm) {
|
||||
OrderApi.receipt(orderId)
|
||||
.then(result => {
|
||||
// 显示成功信息
|
||||
app.$success(result.message)
|
||||
// 刷新订单列表
|
||||
app.onRefreshList()
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 上传图片
|
||||
chooseImage(orderId) {
|
||||
const app = this
|
||||
this.$navTo('pages/order/delivery', {
|
||||
orderId
|
||||
})
|
||||
return;
|
||||
// 选择图片
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
|
||||
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
|
||||
success(chooseImageRes) {
|
||||
const tempFilePaths = chooseImageRes.tempFilePaths;
|
||||
uploadFile({
|
||||
filePath: tempFilePaths[0],
|
||||
fileType: 'image',
|
||||
name: 'file'
|
||||
}).then(res => {
|
||||
console.log("res: ", res);
|
||||
const orderSourceData = JSON.stringify({
|
||||
orderId,
|
||||
photo: fileUrl + res.data.path
|
||||
})
|
||||
receiptOrder({
|
||||
orderId,
|
||||
orderSourceData
|
||||
}).then(result => {
|
||||
app.$success(result.message)
|
||||
// 刷新订单列表
|
||||
app.onRefreshList()
|
||||
})
|
||||
})
|
||||
// uni.uploadFile({
|
||||
// url: 'http://127.0.0.1:9090/api/open/file/upload', //仅为示例,非真实的接口地址
|
||||
// filePath: tempFiles,
|
||||
// name: 'file',
|
||||
// formData: {
|
||||
// 'authorization': 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJ1c2VybmFtZVwiOlwiMjA4ODIwMjk1OTA0NDIwNVwiLFwidGVuYW50SWRcIjo2fSIsImV4cCI6MTY3ODUwNDc1NSwiaWF0IjoxNjc4NDE4MzU1fQ.M-8RB724jmHmhMKut-AFW9i79vJVNdBDqoq-EInDses'
|
||||
// },
|
||||
// success: (uploadFileRes) => {
|
||||
// console.log(uploadFileRes.data);
|
||||
// }
|
||||
// });
|
||||
// tempFiles = [{path:'xxx', size:100}]
|
||||
// app.imageList = oldImageList.concat(tempFiles)
|
||||
}
|
||||
});
|
||||
},
|
||||
// 绑定设备
|
||||
onBind(item) {
|
||||
const app = this
|
||||
const {
|
||||
equipment
|
||||
} = item
|
||||
app.showQRCodeBind = true
|
||||
app.equipment = item
|
||||
// app.bindValue = equipment.equipmentCode
|
||||
},
|
||||
// 执行绑定
|
||||
doBind() {
|
||||
const app = this
|
||||
const {
|
||||
equipment,
|
||||
bindValue
|
||||
} = this
|
||||
bindEquipment({
|
||||
equipmentCode: bindValue,
|
||||
orderId: equipment.orderId
|
||||
}).then(result => {
|
||||
app.$success(result.message)
|
||||
app.showQRCodeBind = false
|
||||
// 刷新订单列表
|
||||
app.onRefreshList()
|
||||
}).catch(err => {
|
||||
app.$error(err.message)
|
||||
})
|
||||
console.log("this.bindValue: ", this.bindValue);
|
||||
},
|
||||
|
||||
// 获取核销二维码
|
||||
onExtractQRCode(orderId) {
|
||||
const app = this
|
||||
OrderApi.extractQrcode(orderId, {
|
||||
channel: app.platform
|
||||
})
|
||||
.then(result => {
|
||||
app.qrcodeImage = result.data.qrcode
|
||||
app.showQRCodePopup = true
|
||||
})
|
||||
},
|
||||
|
||||
// 点击去支付
|
||||
onPay(orderId) {
|
||||
this.$navTo('pages/checkout/cashier/index', {
|
||||
orderId
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转到订单详情页
|
||||
handleTargetDetail(orderId) {
|
||||
this.$navTo('pages/order/detail', {
|
||||
orderId
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转到订单评价页
|
||||
handleTargetComment(orderId) {
|
||||
this.$navTo('pages/order/comment/index', {
|
||||
orderId
|
||||
})
|
||||
},
|
||||
handleChangeEquipment(orderId, equipmentId) {
|
||||
const app = this
|
||||
app.orderId = orderId
|
||||
app.equipmentId = equipmentId
|
||||
app.showQRCodeBindChange = true
|
||||
},
|
||||
// 换电
|
||||
doEquipment(canReset = false) {
|
||||
const app = this
|
||||
const {
|
||||
orderId,
|
||||
bindValue,
|
||||
equipmentId
|
||||
} = this
|
||||
EquipmentApi.changeEquipment({
|
||||
equipmentCode: bindValue,
|
||||
orderId,
|
||||
equipmentId
|
||||
}).then(result => {
|
||||
app.$success(result.message)
|
||||
app.showQRCodeBindChange = false
|
||||
app.bindValue = ''
|
||||
// 刷新订单列表
|
||||
app.onRefreshList()
|
||||
}).catch(err => {
|
||||
app.$error(err.message)
|
||||
})
|
||||
console.log("this.bindValue: ", this.bindValue);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
</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: #333333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.state-text {
|
||||
color: $main-bg;
|
||||
}
|
||||
}
|
||||
|
||||
// 商品列表
|
||||
.goods-list {
|
||||
|
||||
// 商品项
|
||||
.goods-item {
|
||||
display: flex;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
// 商品图片
|
||||
.goods-image {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 商品内容
|
||||
.goods-content {
|
||||
flex: 1;
|
||||
padding-left: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
|
||||
.goods-title {
|
||||
width: 420rpx;
|
||||
font-size: 26rpx;
|
||||
max-height: 76rpx;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.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: 200rpx !important;
|
||||
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: 50rpx;
|
||||
padding-top: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.unit {
|
||||
margin-left: 8rpx;
|
||||
margin-right: -2rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.money {
|
||||
font-size: 28rpx;
|
||||
color: #ff0000;
|
||||
}
|
||||
}
|
||||
|
||||
// 订单操作
|
||||
.order-handle {
|
||||
.btn-group {
|
||||
|
||||
.btn-item {
|
||||
border-radius: 10rpx;
|
||||
padding: 8rpx 20rpx;
|
||||
margin-left: 15rpx;
|
||||
font-size: 26rpx;
|
||||
float: right;
|
||||
color: #383838;
|
||||
border: 1rpx solid #a8a8a8;
|
||||
|
||||
&:last-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $main-bg;
|
||||
border: 1rpx solid $main-bg;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 弹出层 - 核销二维码
|
||||
.qrcode-popup {
|
||||
padding: 36rpx 30rpx;
|
||||
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 26rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pop-content {
|
||||
min-height: 260rpx;
|
||||
padding: 0 10rpx;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 510rpx;
|
||||
height: 510rpx;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 弹出层 - 绑定设备码
|
||||
.qrcode-bind {
|
||||
width: 550rpx;
|
||||
height: 300rpx;
|
||||
padding: 36rpx 30rpx;
|
||||
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 26rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bind-content {
|
||||
padding: 0 10rpx;
|
||||
border: 1rpx solid #eee;
|
||||
font-size: 24rpx;
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
width: 510rpx;
|
||||
height: 510rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.submit {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.yuqi-text{
|
||||
color: #ff0000;
|
||||
}
|
||||
</style>
|
||||
Executable
+127
@@ -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>
|
||||
Executable
+434
@@ -0,0 +1,434 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container" :style="appThemeStyle">
|
||||
|
||||
<!-- 商品详情 -->
|
||||
<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
|
||||
// 表单验证
|
||||
if (!app.formData.content.trim().length) {
|
||||
app.$toast('请填写申请原因')
|
||||
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: #888888;
|
||||
|
||||
&.active {
|
||||
color: $main-bg;
|
||||
border: 1px solid $main-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 申请原因 */
|
||||
.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;
|
||||
background: rgba(0, 0, 0, 0.64);
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
font-weight: bolder;
|
||||
font-size: 22rpx;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 底部操作栏
|
||||
.footer-fixed {
|
||||
position: fixed;
|
||||
bottom: var(--window-bottom);
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 11;
|
||||
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.btn-wrapper {
|
||||
height: 140rpx;
|
||||
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, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Executable
+481
@@ -0,0 +1,481 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container p-bottom" :style="appThemeStyle">
|
||||
|
||||
<!-- 顶部状态栏 -->
|
||||
<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 == 10" 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, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Executable
+263
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<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="appTheme.mainBg" :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: 28rpx;
|
||||
height: 50rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.item {
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+227
@@ -0,0 +1,227 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<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: $main-bg;
|
||||
color: $main-text;
|
||||
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>
|
||||
Executable
+181
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<goods-sku-popup :value="value" @input="onChangeValue" border-radius="20" :localdata="goodsInfo" :mode="skuMode" :maskCloseAble="true"
|
||||
:priceColor="appTheme.mainBg" :buyNowBackgroundColor="appTheme.mainBg" :addCartColor="appTheme.viceText" :addCartBackgroundColor="appTheme.viceBg"
|
||||
:activedStyle="{ color: appTheme.mainBg, borderColor: appTheme.mainBg, backgroundColor: activedBtnBackgroundColor }" @open="openSkuPopup"
|
||||
@close="closeSkuPopup" @buy-now="buyNow" buyNowText="立即购买" :maxBuyNum="goods.limit_num" :noStockText="noStockText" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { hex2rgba } from '@/utils/color'
|
||||
import * as TaskApi from '@/api/bargain/task'
|
||||
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
|
||||
},
|
||||
// 砍价活动详情
|
||||
active: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
// 该商品已抢完时的按钮文字
|
||||
noStockText: {
|
||||
Type: String,
|
||||
default: "该商品已抢完"
|
||||
},
|
||||
// 商品详情信息
|
||||
goods: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
goodsInfo: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 规格按钮选中时的背景色
|
||||
activedBtnBackgroundColor() {
|
||||
return hex2rgba(this.appTheme.mainBg, 0.1)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const app = this
|
||||
const { goods } = app
|
||||
app.goodsInfo = {
|
||||
_id: goods.goods_id,
|
||||
name: goods.goods_name,
|
||||
goods_thumb: goods.goods_image,
|
||||
sku_list: app.getSkuList(),
|
||||
spec_list: app.getSpecList()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 监听组件显示隐藏
|
||||
onChangeValue(val) {
|
||||
this.$emit('input', val)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取商品信息
|
||||
* 这里可以看到每次打开SKU都会去重新请求商品信息,为的是每次打开SKU组件可以实时看到剩余库存
|
||||
*/
|
||||
findGoodsInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(goodsInfo)
|
||||
})
|
||||
},
|
||||
|
||||
// 整理商品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.seckill_price * 100,
|
||||
stock: item.seckill_stock,
|
||||
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组件")
|
||||
},
|
||||
|
||||
// 立即购买
|
||||
buyNow(selectShop) {
|
||||
const app = this
|
||||
// 跳转到订单结算页
|
||||
app.$navTo('pages/checkout/index', {
|
||||
mode: 'sharp',
|
||||
activeTimeId: app.active.active_time_id,
|
||||
sharpGoodsId: app.goods.sharp_goods_id,
|
||||
goodsSkuId: selectShop.goods_sku_id,
|
||||
goodsNum: selectShop.buy_num
|
||||
})
|
||||
// 隐藏当前弹窗
|
||||
app.onChangeValue(false)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
Executable
+339
@@ -0,0 +1,339 @@
|
||||
<template>
|
||||
<view v-show="!isLoading" class="container" :style="appThemeStyle">
|
||||
<!-- 商品图片轮播 -->
|
||||
<SlideImage v-if="!isLoading" :video="goods.video" :videoCover="goods.videoCover" :images="goods.goods_images" />
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view v-if="!isLoading" class="goods-info m-top20">
|
||||
<!-- 价格、销量 -->
|
||||
<view class="info-item info-item__top dis-flex flex-x-between flex-y-end">
|
||||
<view class="block-left dis-flex flex-y-center">
|
||||
<view class="active-tag">
|
||||
<text>限时秒杀</text>
|
||||
</view>
|
||||
<!-- 秒杀价 -->
|
||||
<text class="floor-price__samll">¥</text>
|
||||
<text class="floor-price">{{ goods.seckill_price }}</text>
|
||||
<!-- 商品原价 -->
|
||||
<text class="original-price">¥{{ goods.original_price }}</text>
|
||||
</view>
|
||||
<view class="block-right dis-flex">
|
||||
<!-- 销量 -->
|
||||
<view class="goods-sales">
|
||||
<text>已抢{{ active.sales_actual }}件</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 标题、分享 -->
|
||||
<view class="info-item info-item__name dis-flex flex-y-center">
|
||||
<view class="goods-name flex-box">
|
||||
<text class="twoline-hide">{{ goods.goods_name }}</text>
|
||||
</view>
|
||||
<view class="goods-share__line"></view>
|
||||
<view class="goods-share">
|
||||
<button class="share-btn dis-flex flex-dir-column" @click="onShowShareSheet()">
|
||||
<text class="share__icon iconfont icon-fenxiang"></text>
|
||||
<text class="f-24">分享</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 商品卖点 -->
|
||||
<view v-if="goods.selling_point" class="info-item info-item_selling-point">
|
||||
<text>{{ goods.selling_point }}</text>
|
||||
</view>
|
||||
<!-- 活动倒计时 -->
|
||||
<view v-if="active.active_status != GoodsStatusEnum.STATE_END.value" class="info-item info-item_status info-item_countdown dis-flex flex-y-center">
|
||||
<text class="countdown-icon iconfont icon-naozhong"></text>
|
||||
<text>距离秒杀{{ active.active_status == GoodsStatusEnum.STATE_SOON.value ? '开始' : '结束' }}</text>
|
||||
<text class="m-r-10">还剩</text>
|
||||
<count-down :date="active.count_down_time" separator="zh" theme="text" />
|
||||
</view>
|
||||
<!-- 活动已结束 -->
|
||||
<view v-else class="info-item info-item_status info-item_end">
|
||||
<text class="countdown-icon iconfont icon-naozhong"></text>
|
||||
<text>秒杀活动已结束,下次记得早点来哦~</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选择商品规格 -->
|
||||
<view v-if="goods.spec_type == 20" class="goods-choice m-top20 b-f" @click="onShowSkuPopup()">
|
||||
<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="goods.goods_id" /> -->
|
||||
|
||||
<!-- 商品SKU弹窗 -->
|
||||
<SkuPopup v-if="!isLoading" v-model="showSkuPopup" :skuMode="skuMode" :active="active" :goods="goods" :noStockText="noStockText" />
|
||||
|
||||
<!-- 商品评价 -->
|
||||
<Comment v-if="!isLoading" :goods-id="goods.goods_id" :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>
|
||||
<!-- 客服 -->
|
||||
<customer-btn v-if="isShowCustomerBtn">
|
||||
<view class="fast-item">
|
||||
<view class="fast-icon">
|
||||
<text class="iconfont icon-kefu1"></text>
|
||||
</view>
|
||||
<view class="fast-text">
|
||||
<text>客服</text>
|
||||
</view>
|
||||
</view>
|
||||
</customer-btn>
|
||||
<!-- 购物车 (客服按钮不显示时) -->
|
||||
<view v-if="!isShowCustomerBtn" 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 v-if="active.active_status == GoodsStatusEnum.STATE_BEGIN.value" class="btn-item btn--main" @click="onShowSkuPopup()">
|
||||
<text>立即购买</text>
|
||||
</view>
|
||||
<button v-else class="btn-item btn--gray">
|
||||
<text>{{ active.active_status == GoodsStatusEnum.STATE_SOON.value ? '活动未开始' : '活动已结束' }}</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分享菜单 -->
|
||||
<share-sheet v-model="showShareSheet" :shareTitle="goods.goods_name" :shareImageUrl="goods.goods_image" :posterApiCall="posterApiCall" :posterApiParam="{ activeTimeId, sharpGoodsId }" />
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSceneData } from '@/core/app'
|
||||
import ShareSheet from '@/components/share-sheet'
|
||||
import CustomerBtn from '@/components/customer-btn'
|
||||
import SkuPopup from './components/SkuPopup'
|
||||
import SlideImage from '../../goods/components/SlideImage'
|
||||
import Comment from '../../goods/components/Comment'
|
||||
// import Service from '../../goods/components/Service'
|
||||
import CountDown from '@/components/countdown'
|
||||
import * as SharpGoodsApi from '@/api/sharp/goods'
|
||||
import * as CartApi from '@/api/cart'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
import { ActiveStatusEnum, GoodsStatusEnum } from '@/common/enum/sharp'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ShareSheet,
|
||||
CustomerBtn,
|
||||
// Shortcut,
|
||||
SlideImage,
|
||||
SkuPopup,
|
||||
Comment,
|
||||
// Service,
|
||||
CountDown
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 枚举类
|
||||
ActiveStatusEnum,
|
||||
GoodsStatusEnum,
|
||||
// 显示/隐藏SKU弹窗
|
||||
showSkuPopup: false,
|
||||
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
|
||||
skuMode: 3,
|
||||
// 活动未开始时的文字
|
||||
noStockText: '该商品已抢完',
|
||||
// 显示/隐藏分享菜单
|
||||
showShareSheet: false,
|
||||
// 获取商品海报图api方法
|
||||
posterApiCall: SharpGoodsApi.poster,
|
||||
// 当前秒杀场次ID
|
||||
activeTimeId: null,
|
||||
// 当前秒杀商品ID
|
||||
sharpGoodsId: null,
|
||||
// 秒杀活动详情
|
||||
active: {},
|
||||
// 秒杀商品详情
|
||||
goods: {},
|
||||
// 购物车总数量
|
||||
cartTotal: 0,
|
||||
// 是否显示在线客服按钮
|
||||
isShowCustomerBtn: false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
async onLoad(options) {
|
||||
// 记录query参数
|
||||
this.onRecordQuery(options)
|
||||
// 加载页面数据
|
||||
this.onRefreshPage()
|
||||
// 是否显示在线客服按钮
|
||||
this.isShowCustomerBtn = await SettingModel.isShowCustomerBtn()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 记录query参数
|
||||
onRecordQuery(query) {
|
||||
const scene = getSceneData(query)
|
||||
this.activeTimeId = query.activeTimeId ? parseInt(query.activeTimeId) : parseInt(scene.aid)
|
||||
this.sharpGoodsId = query.sharpGoodsId ? parseInt(query.sharpGoodsId) : parseInt(scene.gid)
|
||||
},
|
||||
|
||||
// 刷新页面数据
|
||||
onRefreshPage() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([app.getActiveDetail(), app.getCartTotal()])
|
||||
.then(() => {
|
||||
const activeStatus = app.active.active_status
|
||||
if (activeStatus != GoodsStatusEnum.STATE_BEGIN.value) {
|
||||
app.skuMode = 4
|
||||
app.noStockText = activeStatus == GoodsStatusEnum.STATE_SOON.value ? '活动未开始' : '活动已结束'
|
||||
}
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取秒杀活动详情
|
||||
getActiveDetail() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
SharpGoodsApi.detail(app.activeTimeId, app.sharpGoodsId)
|
||||
.then(result => {
|
||||
app.active = result.data.active
|
||||
app.goods = result.data.goods
|
||||
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)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示/隐藏SKU弹窗
|
||||
*/
|
||||
onShowSkuPopup() {
|
||||
this.showSkuPopup = !this.showSkuPopup
|
||||
|
||||
},
|
||||
|
||||
// 显示隐藏分享菜单
|
||||
onShowShareSheet() {
|
||||
this.showShareSheet = !this.showShareSheet
|
||||
},
|
||||
|
||||
// 跳转到首页
|
||||
onTargetHome(e) {
|
||||
this.$navTo('pages/index/index')
|
||||
},
|
||||
|
||||
// 跳转到购物车页
|
||||
onTargetCart() {
|
||||
this.$navTo('pages/cart/index')
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
// 构建页面参数
|
||||
const app = this
|
||||
const params = app.$getShareUrlParams({
|
||||
activeTimeId: app.activeTimeId,
|
||||
sharpGoodsId: app.sharpGoodsId
|
||||
})
|
||||
return {
|
||||
title: app.goods.goods_name,
|
||||
path: `/pages/sharp/goods/index?${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({
|
||||
activeTimeId: app.activeTimeId,
|
||||
sharpGoodsId: app.sharpGoodsId
|
||||
})
|
||||
return {
|
||||
title: app.goods.goods_name,
|
||||
path: `/pages/sharp/goods/index?${params}`
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
</style>
|
||||
Executable
+306
@@ -0,0 +1,306 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
.info-item__top .active-tag {
|
||||
color: #fff;
|
||||
background: $main-bg;
|
||||
padding: 4rpx 10rpx;
|
||||
border-radius: 15rpx;
|
||||
font-size: 26rpx;
|
||||
text-align: center;
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
|
||||
.floor-price__samll {
|
||||
font-size: 26rpx;
|
||||
line-height: 1;
|
||||
color: $main-bg;
|
||||
margin-bottom: -10rpx;
|
||||
}
|
||||
|
||||
/* 商品价 */
|
||||
.floor-price {
|
||||
color: $main-bg;
|
||||
margin-right: 15rpx;
|
||||
font-size: 42rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
font-size: 26rpx;
|
||||
line-height: 1;
|
||||
text-decoration: line-through;
|
||||
color: #959595;
|
||||
margin-bottom: -6rpx;
|
||||
}
|
||||
|
||||
.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;
|
||||
width: 256rpx;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.fast-item {
|
||||
position: relative;
|
||||
padding: 4rpx 10rpx;
|
||||
line-height: 1;
|
||||
// text-align: center;
|
||||
|
||||
.fast-icon {
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
&--home {
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
&--cart {
|
||||
.fast-icon { padding-left: 3px; }
|
||||
}
|
||||
|
||||
// 角标
|
||||
.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: 46rpx;
|
||||
}
|
||||
|
||||
.fast-text {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 操作按钮
|
||||
.foo-item-btn {
|
||||
flex: 1;
|
||||
|
||||
.btn-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
// 立即砍价
|
||||
.btn-item {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
font-size: 30rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&.btn--main {
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
}
|
||||
&.btn--gray {
|
||||
background-color: #ccc;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 活动状态
|
||||
.info-item_status {
|
||||
margin-top: 20rpx;
|
||||
padding: 15rpx 20rpx;
|
||||
font-size: 24rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
.info-item_status .countdown-icon {
|
||||
font-size: 28rpx;
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
|
||||
// 活动倒计时
|
||||
.info-item_countdown {
|
||||
background: #f0f9ff;
|
||||
color: #8f8f8f;
|
||||
}
|
||||
|
||||
.info-item_countdown .countdown-icon {
|
||||
color: #1397d8;
|
||||
}
|
||||
|
||||
// 活动已结束
|
||||
.info-item_end {
|
||||
background: #ccc;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
// 砍价玩法
|
||||
.bargain-rules {
|
||||
padding: 20rpx 0;
|
||||
font-size: 29rpx;
|
||||
|
||||
.item-title {
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.rule-simple {
|
||||
margin-top: 35rpx;
|
||||
color: #737373;
|
||||
}
|
||||
|
||||
.i-number {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 15rpx;
|
||||
border: 1rpx dashed #c0c0c0;
|
||||
}
|
||||
}
|
||||
|
||||
// 砍价规则(弹窗)
|
||||
.pops-content {
|
||||
padding: 30rpx 48rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 44rpx;
|
||||
text-align: left;
|
||||
color: #606266;
|
||||
min-height: 320rpx;
|
||||
max-height: 640rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
Executable
+460
@@ -0,0 +1,460 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ native: true, auto: false }" @down="downCallback" :up="upOption"
|
||||
@up="upCallback">
|
||||
<!-- 秒杀会场场次tab -->
|
||||
<view class="sharp-tabs">
|
||||
<scroll-view :scroll-x="true" :scroll-left="scrollLeft" @scroll="scroll">
|
||||
<view class="sharp-tabs--container dis-flex">
|
||||
<view v-for="(item, index) in tabbar" :key="index" class="tabs-item dis-flex flex-dir-column flex-x-center flex-y-center"
|
||||
:class="{ active: curTabIndex == index }" @click="handleTab(index)">
|
||||
<block v-if="item.status == ActiveStatusEnum.STATE_NOTICE.value">
|
||||
<view class="item-title">{{ item.status_text }}</view>
|
||||
</block>
|
||||
<block v-else>
|
||||
<view class="item-time">{{ item.active_time }}</view>
|
||||
<view class="item-status">{{ item.status_text }}</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- 秒杀活动 -->
|
||||
<view v-if="tabbar.length" class="sharp-active dis-flex flex-dir-column flex-y-center">
|
||||
<!-- 活动状态 -->
|
||||
<view class="active-status">
|
||||
<text class="active-status--icon iconfont icon-artboard"></text>
|
||||
<text v-if="tabbar[curTabIndex].status != ActiveStatusEnum.STATE_NOTICE.value"
|
||||
class="active-status--time">{{ tabbar[curTabIndex].active_time }}</text>
|
||||
<text class="active-status--text">{{ tabbar[curTabIndex].status_text2 }}</text>
|
||||
</view>
|
||||
<!-- 倒计时 -->
|
||||
<view class="active--count-down dis-flex flex-y-center">
|
||||
<text class="m-r-10">{{ tabbar[curTabIndex].status == ActiveStatusEnum.STATE_BEGIN.value ? '距结束' : '距开始' }}</text>
|
||||
<count-down :date="tabbar[curTabIndex].count_down_time" separator="colon" theme="custom" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 秒杀商品列表 -->
|
||||
<view class="goods-hall">
|
||||
<view class="goods-item" v-for="(item, index) in goodsList.data" :key="index" @click="handleTargetGoods(item.sharp_goods_id)">
|
||||
<view class="goods-item--container dis-flex">
|
||||
<!-- 商品图片 -->
|
||||
<view class="goods-image">
|
||||
<image :src="item.goods_image"></image>
|
||||
</view>
|
||||
<view class="goods-info">
|
||||
<!-- 商品名称 -->
|
||||
<view class="goods-name">
|
||||
<text class="twoline-hide">{{ item.goods_name }}</text>
|
||||
</view>
|
||||
<!-- 秒杀进度条 -->
|
||||
<view class="sharp-progress dis-flex flex-y-center">
|
||||
<view class="yoo-progress" :style="{ backgroundColor: progressBackgroundColor }">
|
||||
<view class="yoo-progress--portion" :style="{ width: `${item.progress}%` }">
|
||||
</view>
|
||||
<text class="yoo-progress--text">{{ item.progress }}%</text>
|
||||
</view>
|
||||
<view class="sharp-sales">已抢{{ item.sales_actual }}件</view>
|
||||
</view>
|
||||
<!-- 秒杀活动价格 -->
|
||||
<view class="sharp-price dis-flex flex-y-end">
|
||||
<view class="seckill-price">
|
||||
<text class="f-24">¥</text>
|
||||
<text class="money">{{ item.seckill_price_min }}</text>
|
||||
</view>
|
||||
<view class="original-price">
|
||||
<text class="f-24">¥</text>
|
||||
<text class="money">{{ item.original_price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 操作按钮 -->
|
||||
<view class="opt-touch">
|
||||
<view class="touch-btn">
|
||||
<text>{{ tabbar[curTabIndex].status == ActiveStatusEnum.STATE_BEGIN.value ? '马上抢' : '查看商品' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CountDown from '@/components/countdown'
|
||||
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
|
||||
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
|
||||
import { hex2rgba } from '@/utils/color'
|
||||
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
|
||||
import { ActiveStatusEnum } from '@/common/enum/sharp'
|
||||
import * as HomeApi from '@/api/sharp/home'
|
||||
import * as GoodsApi from '@/api/sharp/goods'
|
||||
|
||||
const pageSize = 15
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MescrollBody,
|
||||
CountDown
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
// 是否正在加载中
|
||||
isLoading: true,
|
||||
// 当前tab索引
|
||||
curTabIndex: 0,
|
||||
// tab组件的左侧距离
|
||||
scrollLeft: 0,
|
||||
// 上拉加载配置
|
||||
upOption: {
|
||||
// 首次自动执行
|
||||
auto: false,
|
||||
// 每页数据的数量; 默认10
|
||||
page: { size: pageSize },
|
||||
// 数量要大于3条才显示无更多数据
|
||||
noMoreSize: 3,
|
||||
},
|
||||
// 枚举类
|
||||
ActiveStatusEnum,
|
||||
// 秒杀活动场次
|
||||
tabbar: [],
|
||||
// 秒杀商品列表
|
||||
goodsList: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 进度条背景颜色
|
||||
progressBackgroundColor() {
|
||||
return hex2rgba(this.appTheme.mainBg, 0.2)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
this.onRefreshPage()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 加载页面数据
|
||||
onRefreshPage() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
HomeApi.data()
|
||||
.then(result => {
|
||||
app.tabbar = result.data.tabbar
|
||||
app.goodsList = result.data.goodsList
|
||||
app.curTabIndex = 0
|
||||
app.scrollLeft = 0
|
||||
if (!app.goodsList.data.length) {
|
||||
app.mescroll.showEmpty()
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取商品列表
|
||||
* @param {Number} pageNo 页码
|
||||
*/
|
||||
getListData(pageNo = 1) {
|
||||
const app = this
|
||||
const activeTimeId = app.getCurTabbarId()
|
||||
return new Promise((resolve, reject) => {
|
||||
GoodsApi.list(activeTimeId, { page: pageNo }, { load: false })
|
||||
.then(result => {
|
||||
// 合并新数据
|
||||
const newList = result.data.list
|
||||
app.goodsList.data = getMoreListData(newList, app.goodsList, pageNo)
|
||||
resolve(newList)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
|
||||
// 下拉刷新的回调
|
||||
downCallback() {
|
||||
this.onRefreshPage()
|
||||
.finally(() => this.mescroll.endSuccess())
|
||||
},
|
||||
|
||||
/**
|
||||
* 上拉加载的回调
|
||||
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
|
||||
* @param {Object} page
|
||||
*/
|
||||
upCallback(page) {
|
||||
const app = this
|
||||
// 设置列表数据
|
||||
app.getListData(page.num)
|
||||
.then(list => {
|
||||
const curPageLen = list.data.length
|
||||
const totalSize = list.data.total
|
||||
app.mescroll.endBySize(curPageLen, totalSize)
|
||||
})
|
||||
.catch(() => app.mescroll.endErr())
|
||||
},
|
||||
|
||||
// 监听tab组件滚动
|
||||
scroll({ detail }) {
|
||||
this.scrollLeft = detail.scrollLeft
|
||||
},
|
||||
|
||||
// 点击切换标签(会场场次)
|
||||
handleTab(index) {
|
||||
const app = this
|
||||
app.curTabIndex = index
|
||||
// 刷新列表数据
|
||||
app.goodsList = getEmptyPaginateObj()
|
||||
app.mescroll.resetUpScroll()
|
||||
},
|
||||
|
||||
// 获取当前选择的会场
|
||||
getCurTabbar() {
|
||||
return this.tabbar[this.curTabIndex]
|
||||
},
|
||||
|
||||
// 获取当前会场场次ID
|
||||
getCurTabbarId() {
|
||||
const curTabbar = this.getCurTabbar()
|
||||
return curTabbar ? curTabbar.active_time_id : 0
|
||||
},
|
||||
|
||||
// 跳转到秒杀商品详情
|
||||
handleTargetGoods(sharpGoodsId) {
|
||||
this.$navTo('pages/sharp/goods/index', {
|
||||
activeTimeId: this.getCurTabbarId(),
|
||||
sharpGoodsId
|
||||
})
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
// 构建页面参数
|
||||
const params = this.$getShareUrlParams()
|
||||
return {
|
||||
title: '整点秒杀会场',
|
||||
path: `/pages/sharp/index?${params}`
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享到朋友圈
|
||||
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta)
|
||||
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
|
||||
*/
|
||||
onShareTimeline() {
|
||||
// 构建页面参数
|
||||
const params = this.$getShareUrlParams()
|
||||
return {
|
||||
title: '整点秒杀会场',
|
||||
path: `/pages/sharp/index?${params}`
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #efeff4;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
background: #efeff4;
|
||||
}
|
||||
|
||||
// 秒杀会场 (选项卡)
|
||||
.sharp-tabs {
|
||||
background: #fff;
|
||||
|
||||
.sharp-tabs--container {
|
||||
background: #30353c;
|
||||
}
|
||||
|
||||
// .sharp-tabs--empty {
|
||||
// padding-bottom: 30rpx;
|
||||
// }
|
||||
|
||||
.tabs-item {
|
||||
position: relative;
|
||||
min-width: 170rpx;
|
||||
height: 110rpx;
|
||||
background: #30353c;
|
||||
color: #fff;
|
||||
padding: 15rpx 45rpx;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
|
||||
.item-time {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.item-status {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $main-bg;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
bottom: -15rpx;
|
||||
left: 50%;
|
||||
margin-left: -12rpx;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 20rpx solid $main-bg;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 活动状态
|
||||
.sharp-active {
|
||||
background: #fff;
|
||||
padding: 26rpx 0;
|
||||
|
||||
.active-status {
|
||||
font-size: 32rpx;
|
||||
color: $main-bg;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.active-status--icon {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.active-status--time {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 倒计时
|
||||
.active--count-down {
|
||||
font-size: 26rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
// 商品列表
|
||||
.goods-hall {
|
||||
padding-top: 20rpx;
|
||||
|
||||
.goods-item {
|
||||
background: #fff;
|
||||
padding: 30rpx 16rpx;
|
||||
border-bottom: 1rpx solid #e7e7e7;
|
||||
|
||||
.goods-image {
|
||||
image {
|
||||
display: block;
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-info {
|
||||
width: 498rpx;
|
||||
padding-top: 8rpx;
|
||||
margin-left: 15rpx;
|
||||
position: relative;
|
||||
|
||||
.goods-name {
|
||||
font-size: 28rpx;
|
||||
min-height: 72rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 秒杀进度条
|
||||
.yoo-progress {
|
||||
position: relative;
|
||||
width: 70%;
|
||||
height: 28rpx;
|
||||
border-radius: 12rpx;
|
||||
background: #f8b6b6;
|
||||
|
||||
&--portion {
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
background: linear-gradient(to right, $main-bg2, $main-bg);
|
||||
}
|
||||
|
||||
&--text {
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.6;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 秒杀商品销量
|
||||
.sharp-sales {
|
||||
margin-left: 30rpx;
|
||||
font-size: 24rpx;
|
||||
color: $main-bg;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// 秒杀价格
|
||||
.sharp-price {
|
||||
margin-top: 40rpx;
|
||||
line-height: 1;
|
||||
|
||||
.seckill-price {
|
||||
font-size: 32rpx;
|
||||
color: $main-bg;
|
||||
margin-bottom: -2rpx;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
margin-left: 5rpx;
|
||||
font-size: 24rpx;
|
||||
color: #818181;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
// 立即参加按钮
|
||||
.opt-touch {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 10rpx;
|
||||
|
||||
.touch-btn {
|
||||
font-size: 26rpx;
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
border-radius: 30rpx;
|
||||
padding: 10rpx 30rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Executable
+198
@@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<view v-if="!isLoading" class="container">
|
||||
<view class="header">
|
||||
<view class="shop-logo">
|
||||
<image class="image" :src="detail.logo_url"></image>
|
||||
</view>
|
||||
<view class="shop-name">
|
||||
<text>{{ detail.shop_name }}</text>
|
||||
</view>
|
||||
<view v-if="detail.summary" class="shop-summary dis-flex">
|
||||
<text>门店简介:{{ detail.summary }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="content-item dis-flex flex-y-center">
|
||||
<view class="content-item__icon dis-flex">
|
||||
<text class="iconfont icon-shijian"></text>
|
||||
</view>
|
||||
<view class="content-item__text flex-box dis-flex">
|
||||
<text class="f-26">{{ detail.shop_hours }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-item dis-flex flex-y-center" @click="onOpenLocation()">
|
||||
<view class="content-item__icon dis-flex">
|
||||
<text class="iconfont icon-dingwei"></text>
|
||||
</view>
|
||||
<view class="content-item__text flex-box dis-flex">
|
||||
<text
|
||||
class="f-26">{{ detail.region.province }}{{ detail.region.city }}{{ detail.region.region }}{{ detail.address }}</text>
|
||||
</view>
|
||||
<view class="content-item__arrow dis-flex">
|
||||
<text class="iconfont icon-arrow-right"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-item dis-flex flex-y-center" @click="onMakePhoneCall()">
|
||||
<view class="content-item__icon dis-flex">
|
||||
<text class="iconfont icon-dianhua"></text>
|
||||
</view>
|
||||
<view class="content-item__text flex-box dis-flex">
|
||||
<text class="f-26">{{ detail.phone }}</text>
|
||||
</view>
|
||||
<view class="content-item__arrow dis-flex">
|
||||
<text class="iconfont icon-arrow-right"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as ShopApi from '@/api/shop'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 正在加载中
|
||||
isLoading: true,
|
||||
// 当前门店ID
|
||||
shopId: undefined,
|
||||
// 门店详情
|
||||
detail: null
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 记录当前门店ID
|
||||
this.shopId = options.shopId
|
||||
// 获取门店详情
|
||||
this.getShopDetail()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取门店详情
|
||||
getShopDetail() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
ShopApi.detail(app.shopId)
|
||||
.then(result => app.detail = result.data.detail)
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 拨打电话
|
||||
onMakePhoneCall() {
|
||||
const app = this
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: app.detail.phone
|
||||
})
|
||||
},
|
||||
|
||||
// 查看位置
|
||||
onOpenLocation() {
|
||||
const app = this
|
||||
const { detail } = app
|
||||
uni.openLocation({
|
||||
name: detail.shop_name,
|
||||
address: detail.region.province + detail.region.city + detail.region.region + detail.address,
|
||||
longitude: Number(detail.longitude),
|
||||
latitude: Number(detail.latitude),
|
||||
scale: 15
|
||||
})
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享当前页面
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
const app = this
|
||||
// 构建页面参数
|
||||
const params = app.$getShareUrlParams({ shopId: app.shopId })
|
||||
return {
|
||||
title: app.detail.shop_name,
|
||||
path: "/pages/shop/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({ shopId: app.shopId })
|
||||
return {
|
||||
title: app.detail.shop_name,
|
||||
path: "/pages/shop/detail?" + params
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
background: #fff;
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1rpx solid #f1f1f1;
|
||||
|
||||
.shop-logo,
|
||||
.shop-name {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.shop-logo {
|
||||
.image {
|
||||
width: 130rpx;
|
||||
height: 130rpx;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 30rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
margin-top: 16rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.shop-summary {
|
||||
padding: 20rpx;
|
||||
margin-top: 30rpx;
|
||||
font-size: 26rpx;
|
||||
line-height: 1.6;
|
||||
background: #f9f9f9;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 30rpx;
|
||||
|
||||
.content-item {
|
||||
padding: 12rpx 0;
|
||||
|
||||
.content-item__text {
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Executable
+198
@@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<view class="container b-f">
|
||||
<!-- 门店列表 -->
|
||||
<view class="shop-list">
|
||||
<view v-for="(item, index) in shopList" :key="index" @click="onSelectedShop(item.shop_id)" class="shop-item dis-flex flex-y-center">
|
||||
<view class="shop-item__content flex-box">
|
||||
<view class="shop-item__title">
|
||||
<text>{{ item.shop_name }}</text>
|
||||
</view>
|
||||
<view class="shop-item__address">
|
||||
<text>地址:{{ item.region.province }}{{ item.region.city }}{{ item.region.region }}{{ item.address }}</text>
|
||||
</view>
|
||||
<view class="shop-item__phone">
|
||||
<text>联系电话:{{ item.phone }}</text>
|
||||
</view>
|
||||
<view v-if="item.distance" class="shop-item__distance">
|
||||
<text class="iconfont icon-dingwei"></text>
|
||||
<text class="f-24">{{ item.distance_unit }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 选中状态 -->
|
||||
<view v-if="item.shop_id == selectedId" class="shop-item__right">
|
||||
<text class="iconfont icon-check1"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 定位按钮 -->
|
||||
<view v-if="!isAuthor" class="widget-location dis-flex flex-x-center flex-y-center" @click="onAuthorize()">
|
||||
<text class="iconfont icon-locate"></text>
|
||||
</view>
|
||||
<empty v-if="!shopList.length" :isLoading="isLoading" tips="亲,暂无自提门店哦" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as ShopApi from '@/api/shop'
|
||||
import Empty from '@/components/empty'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Empty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 正在加载中
|
||||
isLoading: true,
|
||||
// 是否授权了定位权限
|
||||
isAuthor: true,
|
||||
// 当前选择的门店ID
|
||||
selectedId: null,
|
||||
// 门店列表
|
||||
shopList: []
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad({ selectedId }) {
|
||||
const app = this
|
||||
// 记录当前选择的门店ID
|
||||
app.selectedId = selectedId ? selectedId : null
|
||||
// 获取默认门店列表
|
||||
app.getShopList()
|
||||
// 获取用户坐标
|
||||
app.getLocation(res => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取门店列表
|
||||
getShopList(longitude, latitude) {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
ShopApi.list({
|
||||
isCheck: 1,
|
||||
longitude: longitude ? longitude : '',
|
||||
latitude: latitude ? latitude : ''
|
||||
})
|
||||
.then(result => app.shopList = result.data.list)
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取用户坐标
|
||||
// 参考文档:https://uniapp.dcloud.io/api/location/location?id=getlocation
|
||||
getLocation(callback) {
|
||||
const app = this
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: callback,
|
||||
fail() {
|
||||
app.$toast('获取定位失败,请点击右下角按钮重新尝试定位')
|
||||
app.isAuthor = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 授权启用定位权限
|
||||
onAuthorize() {
|
||||
const app = this
|
||||
// #ifdef MP
|
||||
uni.openSetting({
|
||||
success(res) {
|
||||
if (res.authSetting['scope.userLocation']) {
|
||||
console.log('定位权限授权成功')
|
||||
app.isAuthor = true
|
||||
setTimeout(() => {
|
||||
// 获取用户坐标
|
||||
app.getLocation(res => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
// 获取用户坐标
|
||||
app.getLocation(res => {
|
||||
app.getShopList(res.longitude, res.latitude)
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
|
||||
/**
|
||||
* 选择门店
|
||||
*/
|
||||
onSelectedShop(selectedId) {
|
||||
const app = this
|
||||
// 设置选中的id
|
||||
app.selectedId = selectedId
|
||||
// 相应全局事件订阅: 选择自提门店
|
||||
uni.$emit('syncSelectedId', selectedId)
|
||||
// 返回上级页面
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shop-list .shop-item {
|
||||
padding: 20rpx 30rpx;
|
||||
min-height: 180rpx;
|
||||
font-size: 26rpx;
|
||||
line-height: 1.5;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.shop-item__title {
|
||||
font-size: 30rpx;
|
||||
color: #535353;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.shop-item__address,
|
||||
.shop-item__phone {
|
||||
color: #919396;
|
||||
}
|
||||
|
||||
.shop-item__distance {
|
||||
margin-top: 10rpx;
|
||||
color: #c1c1c1;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.shop-item__distance .iconfont {
|
||||
color: #81838e;
|
||||
margin-right: 5rpx;
|
||||
}
|
||||
|
||||
// 选中图标
|
||||
.shop-item__right {
|
||||
margin-left: 20rpx;
|
||||
color: #535353;
|
||||
font-size: 38rpx;
|
||||
}
|
||||
|
||||
// 定位图标
|
||||
.widget-location {
|
||||
position: fixed;
|
||||
right: calc(var(--window-right) + 40rpx);
|
||||
bottom: calc(var(--window-bottom) + 70rpx);
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
z-index: 200;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.2);
|
||||
color: #555;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
|
||||
<!-- 页面头部 -->
|
||||
<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: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
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,355 @@
|
||||
<template>
|
||||
<view class="container" :style="appThemeStyle">
|
||||
<view class="addres-list">
|
||||
<view class="address-item" v-for="(item, index) in list" :key="index">
|
||||
<view class="title">
|
||||
<text>设备编号:{{ item.equipmentCode }}</text>
|
||||
<u-tag v-if="item.isCtive == '已激活'" :text="item.isCtive" plain size="mini" type="success"></u-tag>
|
||||
<u-tag v-else :text="item.isCtive" plain size="mini" type="warning"></u-tag>
|
||||
</view>
|
||||
<view class="line"></view>
|
||||
<view class="contacts">
|
||||
<view class="bms-info">
|
||||
<text class="item">电池型号:{{ item.batteryModel }}</text>
|
||||
<text class="item">BMS模块:{{ item.bms }}</text>
|
||||
<text class="item">工作状态:{{ item.workingStatus }}</text>
|
||||
<text class="item">电池状态:健康</text>
|
||||
</view>
|
||||
<view class="bms-image">
|
||||
<view class="bms-image-box">
|
||||
<image class="image" src="/static/battery.png"></image>
|
||||
<text class="bms-image-fbf">67%</text>
|
||||
</view>
|
||||
<u-tag text="离线" plain size="mini" type="info"></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
<view class="line"></view>
|
||||
<view class="item-option">
|
||||
<view class="_left">
|
||||
<u-icon label="操作" size="40" name="map" @click="navTo('package/user/equipment/operation?equipmentId='+item.equipmentId)"></u-icon>
|
||||
</view>
|
||||
<view class="line-align"></view>
|
||||
<view class="_right">
|
||||
<view class="events">
|
||||
<u-icon label="详情" size="40" name="file-text" @click="navTo('package/user/equipment/detail?equipmentId='+item.equipmentId)"></u-icon>
|
||||
<!-- <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" 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 EquipmentApi from '@/websoft/api/equipment.js'
|
||||
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.getEquipment()])
|
||||
.then(() => {
|
||||
// 列表排序把默认收货地址放到最前
|
||||
app.onReorder()
|
||||
})
|
||||
.finally(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取收货地址列表
|
||||
getEquipment() {
|
||||
const app = this
|
||||
EquipmentApi.pageEquipment({userId: uni.getStorageSync('userId')}).then(res => {
|
||||
console.log("res: ",res);
|
||||
app.list = res.data.list
|
||||
})
|
||||
},
|
||||
|
||||
// 获取默认的收货地址
|
||||
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
|
||||
})
|
||||
},
|
||||
|
||||
navTo(url){
|
||||
this.$navTo(url)
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加新地址
|
||||
*/
|
||||
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-bottom: calc(constant(safe-area-inset-bottom) + 140rpx);
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 140rpx);
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
|
||||
// 项目内容
|
||||
.address-item {
|
||||
margin: 0 auto 20rpx auto;
|
||||
padding: 30rpx 40rpx;
|
||||
width: 94%;
|
||||
box-shadow: 0 1rpx 5rpx 0 rgba(0, 0, 0, 0.05);
|
||||
border-radius: 16rpx;
|
||||
background: #fff;
|
||||
.title{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.contacts {
|
||||
margin-bottom: 16rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.bms-info{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #666666;
|
||||
.item{
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
.name {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
}
|
||||
.bms-image{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 120rpx;
|
||||
.bms-image-box{
|
||||
display: flex;
|
||||
font-size: 22rpx;
|
||||
margin-bottom: 18rpx;
|
||||
color: #00af00;
|
||||
.bms-image-fbf{
|
||||
margin-top: 22rpx;
|
||||
}
|
||||
}
|
||||
.image{
|
||||
width: 50rpx; height: 60rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
font-size: 28rpx;
|
||||
|
||||
.region {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f3f3f3;
|
||||
}
|
||||
|
||||
.line-align{
|
||||
width: 6rpx;
|
||||
height: 48rpx;
|
||||
border-right: 1rpx solid #f3f3f3;
|
||||
}
|
||||
|
||||
.item-option {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
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;
|
||||
z-index: 11;
|
||||
|
||||
// 设置ios刘海屏底部横线安全区域
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.btn-wrapper {
|
||||
height: 120rpx;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
height: 86rpx;
|
||||
color: #fff;
|
||||
border-radius: 50rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-shadow: 0 1rpx 5rpx 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.btn-item-main {
|
||||
background: linear-gradient(to right, $main-bg, $main-bg2);
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Executable
+759
@@ -0,0 +1,759 @@
|
||||
<template>
|
||||
<view v-if="!isFirstload" class="container" :style="appThemeStyle">
|
||||
<!-- 页面头部 -->
|
||||
<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 class="oneline-hide">{{ isLogin ? assets.balance : '--' }}</text>
|
||||
</view>
|
||||
<view class="item-name dis-flex flex-x-center">
|
||||
<text>账户余额</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="asset-left-item" @click="onTargetPoints">
|
||||
<view class="item-value dis-flex flex-x-center">
|
||||
<text class="oneline-hide">{{ isLogin ? assets.points : '--' }}</text>
|
||||
</view>
|
||||
<view class="item-name dis-flex flex-x-center">
|
||||
<text>{{ 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>
|
||||
|
||||
<!-- 绑定手机号 (第2种样式) -->
|
||||
<!-- <view class="my-mobile2" @click="handleBindMobile()">
|
||||
<view class="info">点击绑定手机号,确保账户安全</view>
|
||||
<view class="btn-bind">去绑定</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' && item.enabled" 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 == 'contact' && item.enabled" class="service-item">
|
||||
<customer-btn>
|
||||
<view class="item-icon">
|
||||
<text class="iconfont" :class="[`icon-${item.icon}`]"></text>
|
||||
</view>
|
||||
<view class="item-name">{{ item.name }}</view>
|
||||
</customer-btn>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<view v-if="isLogin" class="my-logout">
|
||||
<view class="logout-btn" @click="handleLogout()">
|
||||
<text>退出登录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品推荐 -->
|
||||
<recommended />
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { inArray } from '@/utils/util'
|
||||
import AvatarImage from '@/components/avatar-image'
|
||||
import Recommended from '@/components/recommended'
|
||||
import CustomerBtn from '@/components/customer-btn'
|
||||
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: 标识; 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: 'refund', name: '退换/售后', icon: 'shouhou', type: 'link', url: 'pages/refund/index', count: 0 },
|
||||
{ id: 'help', name: '我的帮助', icon: 'bangzhu', type: 'link', url: 'pages/help/index' },
|
||||
{ id: 'contact', name: '在线客服', icon: 'kefu', type: 'contact' },
|
||||
{ id: 'points', name: '我的积分', icon: 'jifen', type: 'link', url: 'pages/points/log' },
|
||||
{ id: 'dealer', name: '分销中心', icon: 'fenxiao', type: 'link', url: 'pages/dealer/index' },
|
||||
{ id: 'groupon', name: '我的拼团', icon: 'pintuan', type: 'link', url: 'pages/groupon/index?tab=1' },
|
||||
{ id: 'bargain', name: '我的砍价', icon: 'kanjia', type: 'link', url: 'pages/bargain/index?tab=1' },
|
||||
]
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AvatarImage,
|
||||
Recommended,
|
||||
CustomerBtn
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inArray,
|
||||
// 枚举类
|
||||
SettingKeyEnum,
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 首次加载
|
||||
isFirstload: true,
|
||||
// 是否已登录
|
||||
isLogin: false,
|
||||
// 系统设置
|
||||
setting: {},
|
||||
// 当前用户信息
|
||||
userInfo: {},
|
||||
// 账户资产
|
||||
assets: { balance: '--', points: '--', coupon: '--' },
|
||||
// 我的服务
|
||||
service,
|
||||
// 订单操作
|
||||
orderNavbar,
|
||||
// 当前用户待处理的订单数量
|
||||
todoCounts: { payment: 0, deliver: 0, received: 0 }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onLoad(options) {},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
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
|
||||
})
|
||||
},
|
||||
|
||||
// 初始化我的服务数据
|
||||
async initService() {
|
||||
const app = this
|
||||
const isShowCustomerBtn = await SettingModel.isShowCustomerBtn()
|
||||
const newService = []
|
||||
service.forEach(item => {
|
||||
// 默认开启
|
||||
item.enabled = true
|
||||
// 我的积分
|
||||
if (item.id === 'points') {
|
||||
item.name = '我的' + app.setting[SettingKeyEnum.POINTS.value].points_name
|
||||
}
|
||||
// 是否显示分销中心
|
||||
if (item.id === 'dealer' && !app.setting._other.isEnabledDealer) {
|
||||
item.enabled = false
|
||||
}
|
||||
// 企业微信客服
|
||||
if (item.id === 'contact' && !isShowCustomerBtn) {
|
||||
item.enabled = false
|
||||
}
|
||||
// 数据角标
|
||||
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)
|
||||
},
|
||||
|
||||
// 在线客服
|
||||
handleContact() {
|
||||
// 商城客服设置
|
||||
const setting = this.setting[SettingKeyEnum.CUSTOMER.value]
|
||||
// 企业微信客服
|
||||
if (setting.provider == 'wxqykf') {
|
||||
if (!setting.config.wxqykf.url || !setting.config.wxqykf.corpId) {
|
||||
this.$toast('客服链接和企业ID不能为空')
|
||||
return
|
||||
}
|
||||
// #ifdef H5
|
||||
window.open(setting.config.wxqykf.url)
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.openCustomerServiceChat({
|
||||
extInfo: { url: setting.config.wxqykf.url },
|
||||
corpId: setting.config.wxqykf.corpId,
|
||||
success(res) {}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 下拉刷新
|
||||
*/
|
||||
onPullDownRefresh() {
|
||||
// 获取首页数据
|
||||
this.getPageData(() => {
|
||||
uni.stopPullDownRefresh()
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding-bottom: 60rpx;
|
||||
}
|
||||
|
||||
// 页面头部
|
||||
.main-header {
|
||||
// background-color: #FBF7EF;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
background-size: 100% 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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: $main-bg;
|
||||
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: $main-bg;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
margin-top: 14rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 订单操作
|
||||
.order-navbar {
|
||||
display: flex;
|
||||
margin: 20rpx auto 20rpx auto;
|
||||
padding: 20rpx 0 26rpx 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: $main-bg;
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 28rpx;
|
||||
color: #545454;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
.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>
|
||||
Executable
+235
@@ -0,0 +1,235 @@
|
||||
<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="12" 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
|
||||
})
|
||||
},
|
||||
|
||||
// 确认修改
|
||||
async handleSubmit() {
|
||||
const app = this
|
||||
// 判断是否重复提交
|
||||
if (app.disabled === true) return
|
||||
app.$refs.uForm.validate(async valid => {
|
||||
if (valid) {
|
||||
// 按钮禁用
|
||||
app.disabled = true
|
||||
// 先上传头像图片
|
||||
if (app.tempFile) {
|
||||
await app.uploadFile()
|
||||
}
|
||||
// 提交保存个人信息
|
||||
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, $main-bg, $main-bg2);
|
||||
color: $main-text;
|
||||
|
||||
// 禁用按钮
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,767 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="main-header">
|
||||
<image class="bg-image" :src="bgImage" mode="scaleToFill"></image>
|
||||
<image src="@/static/star1.svg" class="star star1" mode="widthFix"></image>
|
||||
<image src="@/static/star1.svg" class="star star2" mode="widthFix"></image>
|
||||
<image src="@/static/star1.svg" class="star star3" mode="widthFix"></image>
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info" @click="onLogin">
|
||||
<u-avatar :src="userInfo.avatar" :size="120" img-mode="aspectFill" show-level></u-avatar>
|
||||
<view class="user-content">
|
||||
<view class="nick-name">{{ userInfo.nickname }}</view>
|
||||
<view class="login-tips" v-for="(item,index) in userInfo.roles" :key="index">
|
||||
<u-tag :text="userInfo.mobile ? userInfo.mobile : item.roleName" plain size="mini" type="info"></u-tag>
|
||||
<!-- <u-tag v-if="userInfo.phone" :text="userInfo.mobile" plain size="mini" type="info"></u-tag> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 绑定手机号 -->
|
||||
<view class="my-mobile" v-if="userInfo.userId && !userInfo.phone">
|
||||
<view class="info">点击绑定手机号,确保账户安全</view>
|
||||
<button class="btn-bind" open-type="getAuthorize" size="mini" scope="phoneNumber" @click="bindPhone">去绑定</button>
|
||||
</view>
|
||||
<view class="my-mobile" v-if="userInfo.userId && !userInfo.avatar">
|
||||
<view class="info">点击获取头像昵称</view>
|
||||
<button
|
||||
open-type="getAuthorize"
|
||||
scope="userInfo"
|
||||
onGetAuthorize="getOpenUserInfo"
|
||||
onError="handleAuthError"
|
||||
size="mini"
|
||||
class="btn-bind"
|
||||
v-if="userInfo.username && userInfo.username !== 'www'"
|
||||
@click="onUpdateAvatar"
|
||||
>
|
||||
去获取
|
||||
</button>
|
||||
<!-- <button class="btn-bind" open-type="getAuthorize" size="mini" scope="phoneNumber" @click="bindPhone">获取头像昵称</button> -->
|
||||
</view>
|
||||
<!-- 我的钱包 -->
|
||||
<view class="my-asset">
|
||||
<view class="asset-left flex-box dis-flex flex-x-around">
|
||||
<view class="asset-left-item">
|
||||
<view class="item-value dis-flex flex-x-center">
|
||||
<text>{{ userInfo.userId ? userInfo.balance : '--' }}</text>
|
||||
</view>
|
||||
<view class="item-name dis-flex flex-x-center">
|
||||
<text>可提现</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="asset-left-item" style="display: flex;align-items: center;">
|
||||
<u-button shape="circle" size="mini"
|
||||
@click="navTo('package/dealer/withdraw/apply')">查看明细</u-button>
|
||||
</view> -->
|
||||
<view class="asset-left-item" style="display: flex;align-items: center;">
|
||||
<u-button shape="circle" type="primary" size="mini" @click="onApply">去提现</u-button>
|
||||
</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 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' && item.enabled" 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> -->
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="my-service">
|
||||
<view class="service-title">本月收入</view>
|
||||
<view class="service-content clearfix">
|
||||
<view class="service-info">
|
||||
<view class="item">
|
||||
<view class="desc">已到账收入</view>
|
||||
<!-- <u-icon name="question"></u-icon> -->
|
||||
<view><text class="money">0.00</text>元</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="desc">待结算收入</view>
|
||||
<view><text class="money">0.00</text>元</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="my-service" @click="navTo('pages/dealer/team')">
|
||||
<view class="service-title">我的邀请</view>
|
||||
<view class="service-content clearfix">
|
||||
<view class="service-info">
|
||||
<view class="item">
|
||||
<view class="desc">接受邀请</view>
|
||||
<view><text class="money">0</text>人</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="desc">邀请成功</view>
|
||||
<view><text class="money">0</text>人</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 绑定手机号 (第2种样式) -->
|
||||
<!-- <view class="my-mobile2" @click="handleBindMobile()">
|
||||
<view class="info">点击绑定手机号,确保账户安全</view>
|
||||
<view class="btn-bind">去绑定</view>
|
||||
</view> -->
|
||||
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<!-- <view v-if="isLogin" class="my-logout">
|
||||
<view class="logout-btn" @click="handleLogout()">
|
||||
<text>退出登录</text>
|
||||
</view>
|
||||
</view> -->
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store/index.js'
|
||||
import { getUser, updateUser } from '@/websoft/api/user.js'
|
||||
import { getAuthCode, getPhoneNumber, login, register } from '@/websoft/api/login.js'
|
||||
import { tenantId, roleId, username, password, appId } from '@/config.js';
|
||||
import { ACCESS_TOKEN, USER_ID } from '@/store/mutation-types'
|
||||
import storage from '@/utils/storage'
|
||||
import { getMobile } from '@/utils/util.js'
|
||||
import http from '@/websoft/api'
|
||||
|
||||
// 订单操作
|
||||
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: 'comment',
|
||||
name: '待评价',
|
||||
icon: 'lingquan',
|
||||
count: 0
|
||||
},
|
||||
]
|
||||
/**
|
||||
* 我的服务
|
||||
* id: 标识; name: 标题名称; icon: 图标; type 类型(link和button); url: 跳转的链接
|
||||
*/
|
||||
const service = [
|
||||
{id: 'get_order',name: '订单管理',icon: 'qpdingdan',type: 'link',url: 'pages/order/index'},
|
||||
{id: 'equipment',name: '设备管理',icon: 'shouhuodizhi',type: 'link',url: 'pages/user/equipment/index'},
|
||||
{id: 'help',name: '使用帮助',icon: 'bangzhu',type: 'link',url: 'pages/help/index'},
|
||||
{id: 'administration',name: '关于我们',icon: 'sy-yh',type: 'link',url: 'pages/help/about'},
|
||||
]
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
bgImage: 'https://file.wsdns.cn/20230313/778ccc230d644b6ebc3c768b7d45229d.png',
|
||||
// #ifdef MP-ALIPAY
|
||||
canIUseAuthButton: my.canIUse('button.open-type.getAuthorize'),
|
||||
// #endif
|
||||
// 首次加载
|
||||
isFirstload: true,
|
||||
// 是否已登录
|
||||
isLogin: false,
|
||||
// 系统设置
|
||||
setting: {},
|
||||
// 当前用户信息
|
||||
userInfo: {
|
||||
nickname: '未登录'
|
||||
},
|
||||
form: {},
|
||||
// 账户资产
|
||||
assets: {
|
||||
balance: '--',
|
||||
points: '--',
|
||||
coupon: '--',
|
||||
browse: '--',
|
||||
collection: '--',
|
||||
follow: '--'
|
||||
},
|
||||
// 我的服务
|
||||
service,
|
||||
// 订单操作
|
||||
orderNavbar,
|
||||
// 当前用户待处理的订单数量
|
||||
todoCounts: {
|
||||
payment: 0,
|
||||
deliver: 0,
|
||||
received: 0,
|
||||
notice: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 设置navbar标题、颜色
|
||||
uni.setNavigationBarColor({
|
||||
frontColor: '#000000',
|
||||
backgroundColor: '#ffffff'
|
||||
})
|
||||
},
|
||||
onShow() {
|
||||
this.getUserInfo()
|
||||
},
|
||||
methods: {
|
||||
getUserInfo() {
|
||||
const { form } = this
|
||||
const app = this
|
||||
getUser().then(res => {
|
||||
if( res.code == 0 && res.data.username != 'www') {
|
||||
console.log("获取用户信息: ",res.data);
|
||||
app.form = res.data
|
||||
app.userInfo = res.data
|
||||
store.dispatch('setUserInfo',res.data)
|
||||
app.isLogin = true
|
||||
}else{
|
||||
app.isLogin = false
|
||||
app.handleLogout()
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
// 跳转到服务页面
|
||||
handleService({
|
||||
url
|
||||
}) {
|
||||
if (url.slice(0, 4) == 'http') {
|
||||
wx.openCustomerServiceChat({
|
||||
extInfo: {
|
||||
url: 'https://work.weixin.qq.com/kfid/kfc1693a8d29b84bc5e'
|
||||
},
|
||||
corpId: 'ww1c3f872ba0a39228',
|
||||
success(res) {}
|
||||
})
|
||||
return;
|
||||
}
|
||||
if(!this.isLogin){
|
||||
return false;
|
||||
}
|
||||
console.log("url: ",url);
|
||||
this.$navTo(url)
|
||||
},
|
||||
onLogin(){
|
||||
const app = this
|
||||
// 未登录状态
|
||||
if(!app.isLogin){
|
||||
return this.$navTo('pages/login/login')
|
||||
}
|
||||
// #ifdef H5
|
||||
// 跳转登录页面
|
||||
return this.$navTo('pages/login/login')
|
||||
// #endif
|
||||
},
|
||||
onUpdateAvatar() {
|
||||
const { form, isLogin } = this
|
||||
const app = this
|
||||
// #ifdef MP-ALIPAY
|
||||
console.log("// 自动获取头像昵称: ");
|
||||
return false;
|
||||
// 自动获取头像昵称
|
||||
my.getOpenUserInfo({
|
||||
fail: (res) => {
|
||||
console.log("res1: ",res);
|
||||
},
|
||||
success: (res) => {
|
||||
let user = JSON.parse(res.response).response // 以下方的报文格式解析两层 response
|
||||
// 获取头像昵称
|
||||
if(user && user.avatar){
|
||||
form.avatar = user.avatar
|
||||
form.nickname = user.nickName
|
||||
updateUser(form).then(res => {
|
||||
console.log("res3: ",res);
|
||||
app.userInfo.avatar = user.avatar
|
||||
app.userInfo.nickname = user.nickName
|
||||
app.showToast('更新成功')
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
// 显示toast信息
|
||||
showToast(title, duration = 2000) {
|
||||
this.$refs.uToast.show({ title, duration })
|
||||
},
|
||||
// 绑定手机号码
|
||||
bindPhone() {
|
||||
const app = this
|
||||
const {
|
||||
form
|
||||
} = this
|
||||
my.getPhoneNumber({
|
||||
success: (res) => {
|
||||
let encryptedData = res.response;
|
||||
getPhoneNumber({encryptedData}).then(response => {
|
||||
console.log("授权手机号码: ",response);
|
||||
const json = JSON.parse(response.data)
|
||||
if(json.mobile){
|
||||
// 执行登录
|
||||
updateUser({phone: json.mobile}).then(res => {
|
||||
app.$toast('绑定成功')
|
||||
app.userInfo.phone = json.mobile
|
||||
})
|
||||
}else{
|
||||
app.bindPhone()
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: (res) => {
|
||||
console.log(res);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
handleLogout(){
|
||||
http.setConfig((config) => {
|
||||
config.header = {};
|
||||
config.header = {
|
||||
AppId: appId,
|
||||
tenantId: tenantId
|
||||
};
|
||||
return config
|
||||
})
|
||||
uni.clearStorage()
|
||||
uni.clearStorageSync()
|
||||
uni.redirectTo({
|
||||
url: '/pages/login/login'
|
||||
})
|
||||
},
|
||||
|
||||
onApply(){
|
||||
if(!this.isLogin){
|
||||
return false;
|
||||
}
|
||||
this.$navTo('package/dealer/withdraw/apply')
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding-bottom: 60rpx;
|
||||
}
|
||||
|
||||
// 页面头部
|
||||
.main-header {
|
||||
// background-color: #FBF7EF;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
background-size: 100% 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 30rpx;
|
||||
|
||||
.bg-image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.star {
|
||||
position: absolute;
|
||||
opacity: 0.1;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.star1 {
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
right: 80rpx;
|
||||
top: 34rpx;
|
||||
}
|
||||
|
||||
.star2 {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
left: 40rpx;
|
||||
top: 10rpx;
|
||||
}
|
||||
|
||||
.star3 {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
left: 140rpx;
|
||||
bottom: 34rpx;
|
||||
}
|
||||
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
height: 120rpx;
|
||||
z-index: 1;
|
||||
|
||||
.user-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-left: 30rpx;
|
||||
color: #ffffff;
|
||||
|
||||
.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: $main-bg;
|
||||
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;
|
||||
margin: 22rpx auto 22rpx auto;
|
||||
padding: 22rpx 0;
|
||||
width: 92%;
|
||||
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 24rpx;
|
||||
background: #fff;
|
||||
|
||||
.asset-right {
|
||||
width: 170rpx;
|
||||
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 {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
padding: 0 42rpx;
|
||||
|
||||
.item-value {
|
||||
font-size: 34rpx;
|
||||
color: $main-bg;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
margin-top: 14rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 订单操作
|
||||
.order-navbar {
|
||||
display: flex;
|
||||
margin: 20rpx auto 20rpx auto;
|
||||
padding: 0rpx 0 6rpx 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: 92%;
|
||||
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 24rpx;
|
||||
background: #fff;
|
||||
|
||||
.service-title {
|
||||
padding-left: 24rpx;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20rpx;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.service-content {
|
||||
|
||||
.service-info {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 52rpx;
|
||||
color: #999999;
|
||||
|
||||
.desc {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.money {
|
||||
color: #3c3c3c;
|
||||
font-weight: bold;
|
||||
font-size: 50rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
margin-bottom: -20rpx;
|
||||
|
||||
.shop-service-item {
|
||||
position: relative;
|
||||
width: 33.33%;
|
||||
float: left;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.item-icon {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
padding: 14rpx 0;
|
||||
color: $main-bg;
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 28rpx;
|
||||
color: #545454;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.service-item {
|
||||
position: relative;
|
||||
width: 25%;
|
||||
float: left;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.item-icon {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
padding: 14rpx 0;
|
||||
color: $main-bg;
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 28rpx;
|
||||
color: #545454;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
.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 {
|
||||
list-style-type: none;
|
||||
padding: 0px 16px !important;
|
||||
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;
|
||||
button::after{
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flex-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.more {
|
||||
padding-right: 20rpx;
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.main-header-btn {
|
||||
width: 700rpx;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
z-index: 9999;
|
||||
clear: both;
|
||||
display: flex;
|
||||
color: #ffffff;
|
||||
justify-content: space-around;
|
||||
padding: 12rpx 0;
|
||||
}
|
||||
</style>
|
||||
Executable
+127
@@ -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>
|
||||
Executable
+167
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<view class="container" v-if="!isLoading">
|
||||
<view class="space-upper">
|
||||
<view class="wallet-image">
|
||||
<image src="/static/wallet.png" mode="widthFix"></image>
|
||||
</view>
|
||||
<view class="wallet-account">
|
||||
<view class="wallet-account_balance">
|
||||
<text>{{ userInfo.balance }}</text>
|
||||
</view>
|
||||
<view class="wallet-account_lable">
|
||||
<text>账户余额(元)</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="space-lower">
|
||||
<view v-if="setting.is_entrance" class="space-lower_item btn-recharge">
|
||||
<view class="btn-submit" @click="onTargetRecharge()">充 值</view>
|
||||
</view>
|
||||
<view class="space-lower_item item-lable dis-flex flex-x-around">
|
||||
<view class="lable-text" @click="onTargetRechargeOrder()">
|
||||
<text>充值记录</text>
|
||||
</view>
|
||||
<view class="lable-text" @click="onTargetBalanceLog()">
|
||||
<text>账单详情</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as UserApi from '@/api/user'
|
||||
import SettingModel from '@/common/model/Setting'
|
||||
import SettingKeyEnum from '@/common/enum/setting/Key'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 会员信息
|
||||
userInfo: {},
|
||||
// 充值设置
|
||||
setting: {},
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onShow(options) {
|
||||
// 获取页面数据
|
||||
this.getPageData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 获取页面数据
|
||||
getPageData() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
Promise.all([app.getUserInfo(), app.getSetting()])
|
||||
.then(() => app.isLoading = false)
|
||||
},
|
||||
|
||||
// 获取会员信息
|
||||
getUserInfo() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
UserApi.info()
|
||||
.then(result => {
|
||||
app.userInfo = result.data.userInfo
|
||||
resolve(app.userInfo)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取充值设置
|
||||
getSetting() {
|
||||
const app = this
|
||||
return new Promise((resolve, reject) => {
|
||||
SettingModel.item(SettingKeyEnum.RECHARGE.value, false)
|
||||
.then(data => {
|
||||
app.setting = data
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转充值页面
|
||||
onTargetRecharge() {
|
||||
this.$navTo('pages/wallet/recharge/index')
|
||||
},
|
||||
|
||||
// 跳转充值记录页面
|
||||
onTargetRechargeOrder() {
|
||||
this.$navTo('pages/wallet/recharge/order')
|
||||
},
|
||||
|
||||
// 跳转账单详情页面
|
||||
onTargetBalanceLog() {
|
||||
this.$navTo('pages/wallet/balance/log')
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.space-upper {
|
||||
padding: 150rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wallet-image image {
|
||||
width: 360rpx;
|
||||
height: 261.72rpx;
|
||||
}
|
||||
|
||||
.wallet-account {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.wallet-account_balance {
|
||||
font-size: 56rpx;
|
||||
}
|
||||
|
||||
.wallet-account_lable {
|
||||
margin-top: 14rpx;
|
||||
color: #cec1c1;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.space-lower {
|
||||
margin-top: 30rpx;
|
||||
padding: 0 110rpx;
|
||||
}
|
||||
|
||||
.btn-recharge .btn-submit {
|
||||
width: 460rpx;
|
||||
height: 84rpx;
|
||||
margin: 0 auto;
|
||||
border-radius: 50rpx;
|
||||
background: #786cff;
|
||||
color: white;
|
||||
font-size: 30rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-lable {
|
||||
margin-top: 80rpx;
|
||||
font-size: 28rpx;
|
||||
color: rgb(94, 94, 94);
|
||||
padding: 0 100rpx;
|
||||
}
|
||||
</style>
|
||||
Executable
+507
@@ -0,0 +1,507 @@
|
||||
<template>
|
||||
<view class="container" v-if="personal.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>¥{{ personal.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 class="input" type="digit" placeholder="可输入自定义充值金额" v-model="inputValue" @input="onChangeMoney" />
|
||||
</view>
|
||||
|
||||
<!-- 支付方式 -->
|
||||
<view class="recharge-label m-top60">
|
||||
<text>支付方式</text>
|
||||
</view>
|
||||
<view class="payment-method">
|
||||
<view v-for="(item, index) in methods" :key="index" class="pay-item dis-flex flex-x-between"
|
||||
@click="handleSelectPayType(index)">
|
||||
<view class="item-left dis-flex flex-y-center">
|
||||
<view class="item-left_icon" :class="[item.method]">
|
||||
<text class="iconfont" :class="[PayMethodIconEnum[item.method]]"></text>
|
||||
</view>
|
||||
<view class="item-left_text">
|
||||
<text>{{ PayMethodEnum[item.method].name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-right col-m" v-if="curPaymentItem && curPaymentItem.method == item.method">
|
||||
<text class="iconfont icon-check"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 确认按钮 -->
|
||||
<view class="recharge-submit btn-submit">
|
||||
<form @submit="onSubmit">
|
||||
<button class="button" formType="submit" :disabled="disabled">立即充值</button>
|
||||
</form>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 充值描述 -->
|
||||
<view class="describe-panel">
|
||||
<view class="recharge-label">
|
||||
<text>充值说明</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<text space="ensp">{{ setting.describe }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付确认弹窗 -->
|
||||
<!-- #ifdef H5 -->
|
||||
<u-modal v-if="tempUnifyData" v-model="showConfirmModal" title="支付确认" show-cancel-button confirm-text="已完成支付"
|
||||
:confirm-color="appTheme.mainBg" negative-top="100" :asyncClose="true"
|
||||
@confirm="onTradeQuery(tempUnifyData.outTradeNo, tempUnifyData.method)">
|
||||
<view class="modal-content">
|
||||
<text>请在{{ PayMethodClientNameEnum[tempUnifyData.method] }}内完成支付,如果您已经支付成功,请点击“已完成支付”按钮</text>
|
||||
</view>
|
||||
</u-modal>
|
||||
<!-- #endif -->
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as RechargeApi from '@/api/recharge'
|
||||
import { PayMethodEnum } from '@/common/enum/payment'
|
||||
import { inArray, urlEncode } from '@/utils/util'
|
||||
import { Alipay, Wechat } from '@/core/payment'
|
||||
|
||||
// 支付方式对应的图标
|
||||
const PayMethodIconEnum = {
|
||||
[PayMethodEnum.WECHAT.value]: 'icon-wechat-pay',
|
||||
[PayMethodEnum.ALIPAY.value]: 'icon-alipay',
|
||||
}
|
||||
|
||||
// 支付方式的终端名称
|
||||
const PayMethodClientNameEnum = {
|
||||
[PayMethodEnum.WECHAT.value]: '微信',
|
||||
[PayMethodEnum.ALIPAY.value]: '支付宝'
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 正在加载
|
||||
isLoading: true,
|
||||
// 按钮禁用
|
||||
disabled: false,
|
||||
// 枚举类
|
||||
PayMethodEnum,
|
||||
PayMethodIconEnum,
|
||||
PayMethodClientNameEnum,
|
||||
// 个人信息
|
||||
personal: { balance: '0.00' },
|
||||
// 充值设置
|
||||
setting: {},
|
||||
// 充值方案列表
|
||||
planList: [],
|
||||
// 当前客户端的支付方式列表(后端根据platform判断)
|
||||
methods: [],
|
||||
// 当前选中的套餐ID
|
||||
selectedPlanId: 0,
|
||||
// 自定义金额
|
||||
inputValue: '',
|
||||
// 当前选中的支付方式
|
||||
curPaymentItem: null,
|
||||
// 支付确认弹窗
|
||||
showConfirmModal: false,
|
||||
// #ifdef H5
|
||||
// 当前微信支付信息 (临时数据, 仅用于H5端)
|
||||
tempUnifyData: { outTradeNo: '', method: '' },
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
// 获取页面数据
|
||||
this.getPageData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// 选择充值套餐
|
||||
onSelectPlan(planId) {
|
||||
this.selectedPlanId = planId
|
||||
this.inputValue = ''
|
||||
},
|
||||
|
||||
// 金额输入框
|
||||
onChangeMoney(e) {
|
||||
this.inputValue = e.target.value
|
||||
this.selectedPlanId = 0
|
||||
},
|
||||
|
||||
// 选择支付方式
|
||||
handleSelectPayType(index) {
|
||||
this.curPaymentItem = this.methods[index]
|
||||
},
|
||||
|
||||
// 获取页面数据
|
||||
getPageData() {
|
||||
const app = this
|
||||
app.isLoading = true
|
||||
return new Promise((resolve, reject) => {
|
||||
RechargeApi.center({ client: app.platform })
|
||||
.then(result => {
|
||||
app.setting = result.data.setting
|
||||
app.personal = result.data.personal
|
||||
app.planList = result.data.planList
|
||||
app.methods = result.data.paymentMethods
|
||||
app.isLoading = false
|
||||
// 默认选中的支付方式
|
||||
app.handleSelectPayType(0)
|
||||
// #ifdef H5
|
||||
// 判断当前页面来源于浏览器返回
|
||||
this.performance()
|
||||
// #endif
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 判断当前页面来源于浏览器返回
|
||||
// #ifdef H5
|
||||
performance() {
|
||||
this.alipayPerformance()
|
||||
this.wechatPerformance()
|
||||
},
|
||||
|
||||
// H5端支付宝支付完成跳转回当前页面时触发
|
||||
alipayPerformance() {
|
||||
const app = this
|
||||
app.tempUnifyData = Alipay.performance()
|
||||
if (app.tempUnifyData) {
|
||||
app.onTradeQuery(app.tempUnifyData.outTradeNo, app.tempUnifyData.method)
|
||||
}
|
||||
},
|
||||
|
||||
// H5端微信支付完成或返回时触发
|
||||
wechatPerformance() {
|
||||
const app = this
|
||||
app.tempUnifyData = Wechat.performance('recharge')
|
||||
console.log('wechatPerformance', app.tempUnifyData)
|
||||
if (app.tempUnifyData) {
|
||||
app.showConfirmModal = true
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
|
||||
// 立即充值
|
||||
onSubmit(e) {
|
||||
const app = this
|
||||
// 判断是否选择了支付方式
|
||||
if (!app.curPaymentItem) {
|
||||
app.$toast('您还没有选择支付方式')
|
||||
return
|
||||
}
|
||||
// 按钮禁用
|
||||
if (app.disabled) return
|
||||
app.disabled = true
|
||||
// 提交到后端
|
||||
RechargeApi.submit({
|
||||
planId: app.selectedPlanId,
|
||||
customMoney: app.inputValue,
|
||||
method: app.curPaymentItem.method,
|
||||
client: app.platform,
|
||||
extra: app.getExtraAsUnify(app.curPaymentItem.method)
|
||||
})
|
||||
.then(result => app.onSubmitCallback(result))
|
||||
.finally(err => {
|
||||
setTimeout(() => app.disabled = false, 10)
|
||||
})
|
||||
},
|
||||
|
||||
// 获取第三方支付的扩展参数
|
||||
getExtraAsUnify(method) {
|
||||
if (method === PayMethodEnum.ALIPAY.value) {
|
||||
return Alipay.extraAsUnify()
|
||||
}
|
||||
if (method === PayMethodEnum.WECHAT.value) {
|
||||
return Wechat.extraAsUnify()
|
||||
}
|
||||
return {}
|
||||
},
|
||||
|
||||
// 订单提交成功后回调
|
||||
onSubmitCallback(result) {
|
||||
const app = this
|
||||
const method = app.curPaymentItem.method
|
||||
const paymentData = result.data.payment
|
||||
// 余额支付
|
||||
if (method === PayMethodEnum.BALANCE.value) {
|
||||
app.onShowSuccess(result)
|
||||
}
|
||||
// 发起支付宝支付
|
||||
if (method === PayMethodEnum.ALIPAY.value) {
|
||||
console.log('paymentData', paymentData)
|
||||
Alipay.payment(paymentData)
|
||||
.then(res => app.onPaySuccess(res))
|
||||
.catch(err => app.onPayFail(err))
|
||||
}
|
||||
// 发起微信支付
|
||||
if (method === PayMethodEnum.WECHAT.value) {
|
||||
console.log('paymentData', paymentData)
|
||||
Wechat.payment({ orderKey: 'recharge', ...paymentData })
|
||||
.then(res => app.onPaySuccess(res))
|
||||
.catch(err => app.onPayFail(err))
|
||||
}
|
||||
},
|
||||
|
||||
// 订单支付成功的回调方法
|
||||
// 这里只是前端支付api返回结果success,实际订单是否支付成功 以后端的查单和异步通知为准
|
||||
onPaySuccess({ res, option: { isRequireQuery, outTradeNo, method } }) {
|
||||
const app = this
|
||||
// 判断是否需要主动查单
|
||||
// isRequireQuery为true代表需要主动查单
|
||||
if (isRequireQuery) {
|
||||
app.onTradeQuery(outTradeNo, method)
|
||||
return true
|
||||
}
|
||||
this.onShowSuccess(res)
|
||||
},
|
||||
|
||||
// 显示支付成功信息并页面跳转
|
||||
onShowSuccess({ message }) {
|
||||
this.$toast(message || '订单支付成功')
|
||||
this.onSuccessNav()
|
||||
},
|
||||
|
||||
// 订单支付失败
|
||||
onPayFail(err) {
|
||||
console.log('onPayFail', err)
|
||||
const errMsg = err.message || '订单未支付'
|
||||
this.$error(errMsg)
|
||||
},
|
||||
|
||||
// 已完成支付按钮事件: 请求后端查单
|
||||
onTradeQuery(outTradeNo, method) {
|
||||
const app = this
|
||||
// 交易查询
|
||||
// 查询第三方支付订单是否付款成功
|
||||
RechargeApi.tradeQuery({ outTradeNo, method, client: app.platform })
|
||||
.then(result => result.data.isPay ? app.onShowSuccess(result) : app.onPayFail(result))
|
||||
.finally(() => app.showConfirmModal = false)
|
||||
},
|
||||
|
||||
// 支付成功后的跳转
|
||||
onSuccessNav() {
|
||||
const pages = getCurrentPages()
|
||||
const lastPage = pages.length < 2 ? null : pages[pages.length - 2]
|
||||
const backRoutes = ['pages/wallet/index']
|
||||
if (lastPage && inArray(lastPage.route, backRoutes)) {
|
||||
setTimeout(() => uni.navigateBack(), 1000)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.$navTo('pages/wallet/index', {}, 'redirectTo')
|
||||
}, 1200)
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page,
|
||||
.container {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-bottom: 70rpx;
|
||||
}
|
||||
|
||||
.m-top60 {
|
||||
margin-top: 60rpx;
|
||||
}
|
||||
|
||||
// 账户面板
|
||||
.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: 30rpx;
|
||||
margin-bottom: 25rpx;
|
||||
}
|
||||
|
||||
.panel-balance {
|
||||
text-align: right;
|
||||
font-size: 46rpx;
|
||||
}
|
||||
|
||||
.recharge-panel {
|
||||
margin-top: 60rpx;
|
||||
padding: 0 60rpx;
|
||||
}
|
||||
|
||||
// 充值套餐
|
||||
.recharge-plan {
|
||||
margin-bottom: -20rpx;
|
||||
|
||||
.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: 40rpx;
|
||||
|
||||
.input {
|
||||
border: 1rpx solid rgb(228, 228, 228);
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx 26rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 立即充值
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
// 充值说明
|
||||
.describe-panel {
|
||||
margin-top: 50rpx;
|
||||
padding: 0 60rpx;
|
||||
|
||||
.content {
|
||||
font-size: 26rpx;
|
||||
line-height: 1.6;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
|
||||
// 支付方式
|
||||
.payment-method {
|
||||
|
||||
.pay-item {
|
||||
padding: 14rpx 0;
|
||||
font-size: 26rpx;
|
||||
|
||||
.item-left_icon {
|
||||
margin-right: 20rpx;
|
||||
font-size: 44rpx;
|
||||
|
||||
&.wechat {
|
||||
color: #00c800;
|
||||
}
|
||||
|
||||
&.alipay {
|
||||
color: #009fe8;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.item-left_text {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.user-balance {
|
||||
margin-left: 20rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 支付确认弹窗
|
||||
.modal-content {
|
||||
padding: 40rpx 48rpx;
|
||||
font-size: 30rpx;
|
||||
line-height: 50rpx;
|
||||
text-align: left;
|
||||
color: #606266;
|
||||
// height: 620rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
Executable
+127
@@ -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>
|
||||
Reference in New Issue
Block a user