第一次提交

This commit is contained in:
gxwebsoft
2023-08-04 13:04:21 +08:00
commit 7a73b38bd5
764 changed files with 166417 additions and 0 deletions

289
package/MyArea/MyArea.vue Normal file
View File

@@ -0,0 +1,289 @@
<template>
<view>
<!-- <u-navbar title-color="#fff" back-icon-color="#ffffff"
:is-back="true"
:background="background"
:back-text-style="{color: '#fff'}"
:title="title"
:back-icon-name="backIconName"
:back-text="backText">
</u-navbar> -->
<!-- <view class="areaPic">
<image style="border-radius: 20rpx;" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Finews.gtimg.com%2Fnewsapp_match%2F0%2F10998212738%2F0.jpg&refer=http%3A%2F%2Finews.gtimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1638439546&t=37da2d714ac7228094c6f4179783b9e0"></image>
</view> -->
<view class="wrap">
<view class="u-tabs-box">
<u-tabs-swiper activeColor="#00aa00" ref="tabs" :list="list" :current="current" @change="change" :is-scroll="false" swiperWidth="750"></u-tabs-swiper>
</view>
<swiper class="swiper-box" :current="swiperCurrent" @transition="transition" @animationfinish="animationfinish">
<swiper-item class="swiper-item">
<view class="orderList">
<view class="pic_of_Store">
<image src="https://i6.mifile.cn/b2c-men/oms/5/64/59ccc2b175c7f.jpg"></image>
</view>
<view class="store">
<view><text class="storename">好和家园菜鸟驿站</text></view>
<view><text class="storeplace">门店地址大学东路100号</text></view>
<view><text class="order_num">门店总订单数99</text></view>
<view>门店交易总额<text class="commissonMoney"><text style="font-size: 18px;">2000</text></text></view>
<view>提成金额<text class="commissonMoney"><text style="font-size: 18px;">2000</text></text></view>
</view>
</view>
</swiper-item>
<swiper-item class="swiper-item">
<view class="orderList">
<view class="pic_of_Store">
<image src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg2.kybimg.com%2Fforum%2F202006%2F12%2F090943mlvyeifcrrphznvr.jpeg&refer=http%3A%2F%2Fimg2.kybimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1638439490&t=a9de43b4c243c4710b0a9815bd4a4ea4"></image>
</view>
<view class="store">
<view><text class="storename">清华大学门店</text></view>
<view><text class="storeplace">门店地址大学东路100号</text></view>
<view>门店交易总额<text class="commissonMoney"><text style="font-size: 18px;">2000</text></text></view>
<view>提成金额<text class="commissonMoney"><text style="font-size: 18px;">2000</text></text></view>
</view>
</view>
<view class="orderList">
<view class="pic_of_Store">
<image src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fnimg.ws.126.net%2F%3Furl%3Dhttp%253A%252F%252Fdingyue.ws.126.net%252F2021%252F1101%252F5e420da5j00r1w6fz002wc000hs00b9m.jpg%26thumbnail%3D650x2147483647%26quality%3D80%26type%3Djpg&refer=http%3A%2F%2Fnimg.ws.126.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1638439282&t=ef66cdd4c5bdf63bce1fadf1368df4ed"></image>
</view>
<view class="store">
<view><text class="storename">CC杂货铺</text></view>
<view><text class="storeplace">门店地址大学东路100号</text></view>
<view>门店交易总额<text class="commissonMoney"><text style="font-size: 18px;">2000</text></text></view>
<view>提成金额<text class="commissonMoney"><text style="font-size: 18px;">2000</text></text></view>
</view>
</view>
<view class="orderList">
<view class="pic_of_Store">
<image src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.zp365.com%2Fzygw%2Flpgl%2F107836404607330.jpg&refer=http%3A%2F%2Fimage.zp365.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1638091983&t=ef8474b167d75feb66208684f63461a4"></image>
</view>
<view class="store">
<view><text class="storename">钱隆首府取货点</text></view>
<view><text class="storeplace">门店地址青秀区人民中路</text></view>
<view>门店交易总额<text class="commissonMoney"><text style="font-size: 18px;">2000</text></text></view>
<view>提成金额<text class="commissonMoney"><text style="font-size: 18px;">2000</text></text></view>
</view>
</view>
</swiper-item>
<swiper-item class="swiper-item">
<view>
</view>
</swiper-item>
<swiper-item class="swiper-item">
<view class="orderList">
<view class="pic_of_Store">
<image src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.zp365.com%2Fzygw%2Flpgl%2F107836404607330.jpg&refer=http%3A%2F%2Fimage.zp365.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1638091983&t=ef8474b167d75feb66208684f63461a4"></image>
</view>
<view class="store2">
<view><text class="storename">违规类型偷吃店内食品</text></view>
<view><text class="storeplace">违规内容</text>
</view>
<view><text class="storeplace2">当你发现时间是贼了他早已偷光你的选择爱情更像是一场高烧思念是紧跟着都好不了的咳</text>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title: '我的地区',
backText: '返回',
backIconName: 'nav-back',
isBack: true,
background: {
backgroundImage: 'linear-gradient(to right, #146930 , #77b633)',
},
orderList: [[], [], [], []],
list: [
{
name: '我的地区'
},
{
name: '订单统计'
},
{
name: '提现明细'
},
{
name: '违规记录',
}
],
current: 0,
swiperCurrent: 0,
tabsHeight: 0,
dx: 0,
loadStatus: ['loadmore','loadmore','loadmore','loadmore'],
};
},
onLoad() {
this.getOrderList(0);
this.getOrderList(1);
this.getOrderList(3);
},
methods: {
reachBottom() {
// 此tab为空数据
if(this.current != 2) {
this.loadStatus.splice(this.current,1,"loading")
setTimeout(() => {
this.getOrderList(this.current);
}, 1200);
}
},
// 页面数据
getOrderList(idx) {
for(let i = 0; i < 5; i++) {
let index = this.$u.random(0, this.dataList.length - 1);
let data = JSON.parse(JSON.stringify(this.dataList[index]));
data.id = this.$u.guid();
this.orderList[idx].push(data);
}
this.loadStatus.splice(this.current,1,"loadmore")
},
// tab栏切换
change(index) {
this.swiperCurrent = index;
this.getOrderList(index);
},
transition({ detail: { dx } }) {
this.$refs.tabs.setDx(dx);
},
animationfinish({ detail: { current } }) {
this.$refs.tabs.setFinishCurrent(current);
this.swiperCurrent = current;
this.current = current;
}
}
};
</script>
<style>
/* #ifndef H5 */
page {
height: 100%;
background-color: #f2f2f2;
}
/* #endif */
</style>
<style lang="scss" scoped>
.orderList{
display: flex;
background-color: #f3f3f3;
width: 92%;
height: 120px;
border-radius: 20px;
margin-top: 20px;
margin-left: 4%;
box-shadow: 2px 2px #DCDFE6;
}
.addressAndDealsituation{
display: flex;
flex-direction: row;
}
.pic_of_Store{
width: 40%;
display: flex;
height: 100%;
justify-content: center;
}
.pic_of_Store image{
width: 95%;
height: 90%;
margin-top: 6px;
border-radius: 20px;
}
.store{
display: flex;
width: 60%;
height: 100%;
flex-direction: column;
line-height: 20px;
justify-content: center;
margin-left: 5px;
}
.store2{
display: flex;
width: 60%;
height: 100%;
flex-direction: column;
line-height: 20px;
margin-left: 5px;
margin-top: 10rpx;
align-content: center;
margin-left: 15rpx
}
.TransactionAmount{
margin: 10px;
display: flex;
height: 28px;
width:100%;
position:absolute;
top: 150px;
}
.right{
margin: 10px;
margin-top: 20px;
color: #F0AD4E;
}
.pickUpadress{
margin-top: 20px;
margin-left: 10px;
}
.areaPic{
display: flex;
justify-content: center;
width: 100%;
}
.areaPic image{
width: 100%;
margin: 5%;
height: 200px;
}
.wrap {
display: flex;
flex-direction: column;
height: calc(100vh - var(--window-top));
width: 100%;
}
.swiper-box {
flex: 1;
}
.swiper-item {
height: 100%;
}
.commissonMoney{
color: #ff0000
}
.order_num{
color: #303030;
font-size: 15px;
}
.storeplace{
color: #303030;
font-size: 15px;
}
.storeplace2{
color: #303030;
font-size: 13px;
}
.storename{
color: #303030;
font-size: 15px;
font-weight: 600;
}
</style>

378
package/MyStore/MyStore.vue Normal file
View File

@@ -0,0 +1,378 @@
<template>
<view>
<view class="areaPic">
<image style="border-radius: 20rpx;" src="https://img2.baidu.com/it/u=1945464906,1635022113&fm=26&fmt=auto"></image>
</view>
<view class="wrap">
<view class="u-tabs-box">
<u-tabs-swiper activeColor="#00aa00" ref="tabs" :list="list" :current="current" @change="change" :is-scroll="false" swiperWidth="750"></u-tabs-swiper>
</view>
<swiper class="swiper-box" :current="swiperCurrent" @transition="transition" @animationfinish="animationfinish">
<swiper-item class="swiper-item">
<view class="orderList">
<view class="pic_of_Store">
<image src="https://i6.mifile.cn/b2c-men/oms/5/64/59ccc2b175c7f.jpg"></image>
</view>
<view class="store">
<view><text class="storename">好和家园菜鸟驿站</text></view>
<view><text class="storeplace">门店地址大学东路100号</text></view>
<view><text class="order_num">门店总订单数99</text></view>
<view><text class="storeplace">门店交易总额</text><text style="color: #ED1C24;"><text class="commissonMoney">2000</text></text></view>
<view><text class="storeplace">提成金额</text><text style="color: #ED1C24;"><text class="commissonMoney">2000</text></text></view>
</view>
</view>
</swiper-item>
<swiper-item class="swiper-item">
<view class="orderList">
<view class="pic_of_Store">
<image src="https://img2.baidu.com/it/u=1945464906,1635022113&fm=26&fmt=auto"></image>
</view>
<view class="store">
<view><text class="storename">钱隆首府取货点</text></view>
<view><text class="storeplace">门店地址青秀区人民中路</text></view>
<view><text class="storeplace">门店交易总额</text><text style="color: #ED1C24;"><text class="commissonMoney">2000</text></text></view>
<view><text class="storeplace">提成金额</text><text style="color: #ED1C24;"><text class="commissonMoney">2000</text></text></view>
</view>
</view>
<view class="orderList">
<view class="pic_of_Store">
<image src="https://img2.baidu.com/it/u=1945464906,1635022113&fm=26&fmt=auto"></image>
</view>
<view class="store">
<view><text class="storename">CC杂货铺</text></view>
<view><text class="storeplace">门店地址</text><text class="storeplace">大学东路100号</text></view>
<view><text class="storeplace">门店交易总额</text><text style="color: #ED1C24;"><text class="commissonMoney">2000</text></text></view>
<view><text class="storeplace">提成金额</text><text style="color: #ED1C24;"><text class="commissonMoney">2000</text></text></view>
</view>
</view>
<view class="orderList">
<view class="pic_of_Store">
<image src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.zp365.com%2Fzygw%2Flpgl%2F107836404607330.jpg&refer=http%3A%2F%2Fimage.zp365.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1638091983&t=ef8474b167d75feb66208684f63461a4"></image>
</view>
<view class="store">
<view><text class="storename">清华大学门店</text></view>
<view><text class="storeplace">门店地址大学东路100号</text></view>
<view><text class="storeplace">门店交易总额</text><text style="color: #ED1C24;"><text class="commissonMoney">2000</text></text></view>
<view><text class="storeplace">提成金额</text><text style="color: #ED1C24;"><text class="commissonMoney">2000</text></text></view>
</view>
</view>
</swiper-item>
<swiper-item class="swiper-item">
<view class="myriders">
<view class="mysender">
<view class="senderinfo">
<view class="sendername">张大强</view>
<view class="sendername2">骑手配送区域</view>
<view class="sendingAddress">北京市清华园</view>
</view>
<view class="senderinfo">
<view class="sendingAddress">已配送单数<text class="commissonMoney">434</text></view>
<view class="sendingAddress">累计提成<text style="color:#ED1C24"><text class="commissonMoney">900</text></text></view>
</view>
</view>
<view class="mysender">
<view class="senderinfo">
<view class="sendername">李牛</view>
<view class="sendername2">骑手配送区域</view>
<view class="sendingAddress">南宁西乡塘</view>
</view>
<view class="senderinfo">
<view class="sendingAddress">已配送单数<text class="commissonMoney">123</text></view>
<view class="sendingAddress">累计提成<text style="color:#ED1C24"><text class="commissonMoney">6000</text></text></view>
</view>
</view>
<view class="mysender">
<view class="senderinfo">
<view class="sendername">丁紫雷</view>
<view class="sendername2">骑手配送区域</view>
<view class="sendingAddress">重庆山城</view>
</view>
<view class="senderinfo">
<view class="sendingAddress">已配送单数<text class="commissonMoney">323</text></view>
<view class="sendingAddress">累计提成<text style="color:#ED1C24"><text class="commissonMoney">9000</text></text></view>
</view>
</view>
</view>
</swiper-item>
<swiper-item class="swiper-item">
<view class="orderList">
<view class="pic_of_Store">
<image src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.zp365.com%2Fzygw%2Flpgl%2F107836404607330.jpg&refer=http%3A%2F%2Fimage.zp365.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1638091983&t=ef8474b167d75feb66208684f63461a4"></image>
</view>
<view class="store2">
<view><text class="storename">违规类型偷吃店内食品</text></view>
<view><text class="storeplace">违规内容</text>
</view>
<view><text class="storeplace2">当你发现时间是贼了他早已偷光你的选择爱情更像是一场高烧思念是紧跟着都好不了的咳</text>
</view>
</view>
</view>
</swiper-item>
<swiper-item class="swiper-item">
<view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title: '我的门店',
backText: '返回',
backIconName: 'nav-back',
isBack: true,
background: {
backgroundImage: 'linear-gradient(to right, #146930 , #77b633)',
},
orderList: [[], [], [], []],
list: [
{
name: '门店详情'
},
{
name: '订单数据'
},
{
name: '我的骑手'
},
{
name: '违规记录',
},
{
name: '提成明细',
}
],
current: 0,
swiperCurrent: 0,
tabsHeight: 0,
dx: 0,
loadStatus: ['loadmore','loadmore','loadmore','loadmore'],
};
},
onLoad() {
this.getOrderList(0);
this.getOrderList(1);
this.getOrderList(3);
},
methods: {
reachBottom() {
// 此tab为空数据
if(this.current != 2) {
this.loadStatus.splice(this.current,1,"loading")
setTimeout(() => {
this.getOrderList(this.current);
}, 1200);
}
},
// 页面数据
getOrderList(idx) {
for(let i = 0; i < 5; i++) {
let index = this.$u.random(0, this.dataList.length - 1);
let data = JSON.parse(JSON.stringify(this.dataList[index]));
data.id = this.$u.guid();
this.orderList[idx].push(data);
}
this.loadStatus.splice(this.current,1,"loadmore")
},
// tab栏切换
change(index) {
this.swiperCurrent = index;
this.getOrderList(index);
},
transition({ detail: { dx } }) {
this.$refs.tabs.setDx(dx);
},
animationfinish({ detail: { current } }) {
this.$refs.tabs.setFinishCurrent(current);
this.swiperCurrent = current;
this.current = current;
}
}
};
</script>
<style>
/* #ifndef H5 */
page {
height: 100%;
background-color: #f2f2f2;
}
/* #endif */
</style>
<style lang="scss" scoped>
.orderList{
display: flex;
background-color: #f3f3f3;
width: 92%;
height: 120px;
border-radius: 20px;
margin-top: 20px;
margin-left: 4%;
box-shadow: 2px 2px #DCDFE6;
line-height: 60rpx;
}
.addressAndDealsituation{
display: flex;
flex-direction: row;
}
.pic_of_Store{
width: 40%;
display: flex;
height: 100%;
justify-content: center;
}
.pic_of_Store image{
width: 95%;
height: 90%;
border-radius: 20px;
margin-top: 6px;
}
.store{
display: flex;
width: 60%;
height: 100%;
flex-direction: column;
line-height: 20px;
justify-content: center;
margin-left: 5px;
}
.store2{
display: flex;
width: 60%;
height: 100%;
flex-direction: column;
line-height: 20px;
justify-content: center;
margin-left: 15rpx
;
}
.TransactionAmount{
margin: 10px;
display: flex;
height: 28px;
width:100%;
position:absolute;
top: 150px;
}
.right{
margin: 10px;
margin-top: 20px;
color: #F0AD4E;
}
.pickUpadress{
margin-top: 20px;
margin-left: 10px;
}
.myriders{
display: flex;
justify-content: center;
flex-direction: column;
margin-left: 4%;}
.mysender{
width: 95%;
display: flex;
height: 80px;
background-color: #ffffff;
margin-top: 20px;
flex-direction: column;
border: solid 2px #18B566;
border-radius: 10px;
}
.senderinfo{
width: 100%;
height: 50%;
display: flex;
border-radius: 10px;
}
.sendername{
height: 100%;
width:25%;
color: #000033;
display: flex;
background-color: #ffffff;
align-items: center;
justify-content: center;
border-radius: 10px;
font-size: 16px;
}
.sendername2{
height: 100%;
width:25%;
color: #000000;
display: flex;
background-color: #ffffff;
align-items: center;
justify-content: center;
border-radius: 10px;
}
.sendingAddress{
height: 100%;
width: 50%;
background-color: #ffffff;
color: #000000;
align-items: center;
justify-content: center;
display: flex;
border-radius: 10px;
}
.areaPic{
display: flex;
justify-content: center;
width: 100%;
}
.areaPic image{
width: 100%;
margin: 5%;
height: 200px;
}
.wrap {
display: flex;
flex-direction: column;
height: calc(100vh - var(--window-top));
width: 100%;
}
.swiper-box {
flex: 1;
}
.swiper-item {
height: 100%;
}
.commissonMoney{
font-size: 17px;
color: #ff0000
}
.order_num{
color: #303030;
font-size: 15px;
}
.storeplace{
color: #303030;
font-size: 15px;
}
.storeplace2{
color: #303030;
font-size: 13px;
}
.storename{
color: #333333;
font-size: 15px;
font-weight: 600;
}
</style>

View File

@@ -0,0 +1,282 @@
<template>
<view :style="appThemeStyle">
<u-form :model="form" ref="uForm" label-width="140rpx">
<!-- {{JSON.stringify(setting)}} -->
<u-form-item label="意向配送区域" prop="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入商户姓名" />
</u-form-item>
<u-form-item label="手机号码" prop="mobile">
<u-input v-model="form.mobile" maxlength="11" :disabled="disabled" placeholder="请输入商户手机号" />
</u-form-item>
<Partner :form="form"></Partner>
<u-form-item v-for="(item,imageIndex) in imageList" :key="imageIndex" :label="itemLabel[imageIndex]"
:prop="itemProp[imageIndex]">
<view class="image-preview">
<image mode="widthFix" :src="item.path" class="sfz" @click="chooseImage(imageIndex)"></image>
<text class="image-delete iconfont icon-shanchu" @click="deleteImage(imageIndex)"></text>
</view>
</u-form-item>
<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">{{ setting.words.apply.words.license.value }}</text>
</view>
</u-form>
<!-- 入驻协议弹窗 -->
<u-modal v-model="showLicense" title="入驻协议">
<scroll-view class="pops-content" :scroll-y="true">
<text>{{ setting.license }}</text>
</scroll-view>
</u-modal>
</view>
</template>
<script>
import SelectRegion from '@/components/select-region/select-region'
import {
isMobile
} from '@/utils/verify'
import Empty from '@/components/empty'
import * as ApplyApi from '@/api/apply.js'
import * as UploadApi from '@/api/upload'
import SettingModel from '@/common/model/dealer/Setting'
// 图片上传类型
const itemProp = ["sfz1", "sfz2", "yyzz"]
// 图片上传名称
const itemLabel = ["身份证(正)", "身份证(反)", "营业执照"]
// 用户选择的图片列表
const imageList = [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 100
},
{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/55faad35f5ee30f3e8791bdfd2f75eb9.png',
size: 100
}
]
// 表单字段元素
const form = {
// 入驻类型 商家10001,配送员10002,社区门店10003,省代10004
grade_id: 10001,
company: '',
real_name: '',
phone: '',
mobile: '',
region: [],
detail: '',
}
// 表单验证规则
const rules = {
real_name: [{
required: true,
message: '请输入您的真实姓名',
trigger: ['blur', 'change']
}],
company: [{
required: true,
message: '请输入您的公司名称',
trigger: ['blur', 'change']
}],
phone: [{
required: true,
message: '请输入联系电话',
trigger: ['blur', 'change']
}],
mobile: [{
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']
}],
}
const maxImageLength = 2
export default {
components: {
Empty
},
props: {
record: {
type: Object,
default: null
},
setting: {
type: Object,
default: null
},
},
data() {
return {
itemProp,
itemLabel,
imageList,
form,
rules,
// 入驻协议阅读状态
isRead: false,
// 显示入驻协议弹窗
showLicense: false,
// 按钮禁用
disabled: false,
}
},
created() {
// 设置分类列表高度
this.getSetting()
},
// onLoad(options) {
// const app = this
// this.getSetting()
// // 获取用户坐标
// app.getLocation((res) => {
// app.getData(res.longitude, res.latitude)
// })
// },
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules)
},
methods: {
// 获取分销设置
getSetting() {
const app = this
console.log("app.setting: ",this.setting);
// 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)
// })
},
// 显示入驻协议弹窗
handleShowLicense() {
this.showLicense = true
},
}
}
</script>
<style lang="scss" scoped>
.form-license {
padding: 20rpx;
}
// 上传凭证
.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;
}
}
}
}
.pops-content{padding: 20rpx;}
.sfz,
.sfz1,
.sfz2,
.yyzz {
width: 140rpx;
padding-top: 20rpx;
padding-right: 10rpx;
}
</style>

View File

@@ -0,0 +1,415 @@
<template>
<view class="container" :style="appThemeStyle">
<!-- 标题 -->
<view class="page-title">申请入驻</view>
<!-- 合伙人 -->
<Partner v-if=" currentType == 0 " :form="dataType[currentType]"
@handleSubmit="handleSubmit" @chooseImage="chooseImage" @deleteImage="deleteImage" @showLicense="handleShowLicense" />
<!-- 配送员 -->
<Delivery v-if=" currentType == 1 " :form="dataType[currentType]"
@handleSubmit="handleSubmit" @chooseImage="chooseImage" @deleteImage="deleteImage" />
<!-- 团长 -->
<Teamer v-if=" currentType == 2 " :form="dataType[currentType]"
@handleSubmit="handleSubmit" @chooseImage="chooseImage" @deleteImage="deleteImage" />
<!-- 个人商户 -->
<Personal v-if=" currentType == 3 " :form="dataType[currentType]"
@handleSubmit="handleSubmit" @chooseImage="chooseImage" @deleteImage="deleteImage" />
<!-- 入驻协议弹窗 -->
<u-modal v-model="showLicense" title="入驻协议">
<scroll-view class="pops-content" :scroll-y="true">
<text>{{ dataType[currentType].license }}</text>
</scroll-view>
</u-modal>
</view>
</template>
<script>
import * as ApplyApi from '@/api/apply.js'
import * as UploadApi from '@/api/upload'
import SettingModel from '@/common/model/dealer/Setting'
import Partner from './components/partner.vue'
import Delivery from './components/delivery.vue'
import Teamer from './components/teamer.vue'
import Personal from './components/personal.vue'
// 数据类型
const dataType = [
{
name: "商户",
title: '申请成为商户',
label: "partner",
regionText: '所在地区',
licenseTitle: '',
license: '',
submitText: '提交申请',
disabled: false,
longitude: '',
latitude: '',
imageProp: ["sfz1", "sfz2", "yyzz"],
itemLabel: ["身份证(正)", "身份证(反)", "营业执照"],
imageList: [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},
{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/55faad35f5ee30f3e8791bdfd2f75eb9.png',
size: 0
}],
},
{
name: "配送员",
title: '申请成为配送员',
label: "delivery",
regionText: '意向配送区域',
licenseTitle: '',
license: '',
submitText: '提交申请',
disabled: false,
longitude: '',
latitude: '',
imageProp: ["sfz1", "sfz2", "yyzz"],
itemLabel: ["身份证(正)", "身份证(反)", "营业执照"],
imageList: [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},
{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/55faad35f5ee30f3e8791bdfd2f75eb9.png',
size: 0
}],
},
{
name: "团长",
title: '申请成为团长',
label: "teamer",
regionText: '所在地区',
licenseTitle: '',
license: '',
submitText: '提交申请',
disabled: false,
longitude: '',
latitude: '',
imageProp: ["sfz1", "sfz2", "yyzz"],
itemLabel: ["身份证(正)", "身份证(反)", "营业执照"],
imageList: [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},
{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/55faad35f5ee30f3e8791bdfd2f75eb9.png',
size: 0
}],
},
{
name: "个人商户",
title: '申请成为个人商户',
label: "personal",
regionText: '所在地区',
submitText: '提交申请',
licenseTitle: '',
license: '',
disabled: false,
longitude: '',
latitude: '',
imageProp: ["sfz1", "sfz2", "yyzz"],
itemLabel: ["身份证(正)", "身份证(反)", "营业执照"],
imageList: [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},
{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/55faad35f5ee30f3e8791bdfd2f75eb9.png',
size: 0
}],
}
]
export default {
components: {
Partner,
Delivery,
Teamer,
Personal
},
data() {
return {
// 数据类型
dataType,
// 当前类型
currentType: 0,
// 文字设置
words: undefined,
// 显示入驻协议弹窗
showLicense: false,
// 入驻协议阅读状态
isRead: false,
// 当前记录
form: {},
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const app = this
// 切换类型
this.setDataType(options.dataType)
// 获取设置信息
this.getSetting()
// 获取用户坐标及当前入驻申请记录
app.getLocation((res) => {
app.getData(res.longitude, res.latitude)
})
},
methods: {
// 切换类型
setDataType(dataType) {
if (dataType == 'partner') {
this.currentType = 0
}
if (dataType == 'delivery') {
this.currentType = 1
}
if (dataType == 'teamer') {
this.currentType = 2
}
if (dataType == 'personal') {
this.currentType = 3
}
console.log("dataType: ",this.dataType[this.currentType]);
},
// 获取用户坐标
// 参考文档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
}
})
},
// 加载数据
getData(longitude, latitude) {
const app = this
const { currentType, dataType } = this
dataType[currentType].longitude = longitude
dataType[currentType].latitude = latitude
ApplyApi.detail().then(res => {
const detail = res.data.detail
console.log("detail: ",detail);
// 编辑信息
if (detail) {
app.createFormData(detail)
}
})
},
// 生成默认的表单数据
createFormData(detail) {
const {
form
} = this
form.region = this.createRegion(detail)
// 是否可编辑
this.disabled = true
// 状态
console.log("recore.apply_status: ", form.apply_status);
if (form.apply_status == 10) {
this.submitText = '待审核'
}
if (form.apply_status == 20) {
this.submitText = '审核通过'
}
if (form.apply_status == 30) {
this.submitText = '重新提交'
this.disabled = false
}
// 加载图片
this.imageList = detail.images.map(d => {
return {
path: d.image_url,
size: 101
}
})
form.images = detail.images.map(d => {
return d.image_id
})
},
// 地区
createRegion(detail) {
console.log("detail: ", 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
}]
},
// 获取分销设置
getSetting() {
const app = this
SettingModel.data()
.then(setting => {
app.dataType[app.currentType].licenseTitle = setting.words.apply.words.license.value
// // 赋值属性
app.dataType[app.currentType].license = setting.license.license
// 设置当前页面标题
app.setPageTitle()
})
},
// 设置当前页面标题
setPageTitle() {
uni.setNavigationBarTitle({
title: this.dataType[this.currentType].title
})
},
// 选择图片
chooseImage(imageIndex,tempFiles){
console.log("imageIndex: ",imageIndex);
console.log("tempFiles: ",tempFiles);
const {dataType, currentType} = this
dataType[currentType].imageList.splice(imageIndex,1,tempFiles)
},
// 删除图片
deleteImage(imageIndex) {
const { disabled } = this
if (disabled == true) {
return false
}
this.imageList[imageIndex].path = '/static/not-dealer.png'
},
// 显示入驻协议弹窗
handleShowLicense(e) {
this.showLicense = e
},
// 提交到后台
handleSubmit(form,imageList){
const app = this
if (app.disabled) {
return false
}
app.disabled = true
app.form = form
ApplyApi.checkAvailable(form).then(res => {
if (imageList.length > 0 && imageList[0].size != 100) {
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()
}
}).catch(err => {
console.log("err: ", err);
if (err.statusCode == 0) {
app.disabled = false
app.$toast(err.errMsg)
return false
}
})
},
// 上传图片
uploadFile() {
const app = this
const {
imageList,
form
} = app
// 批量上传
return new Promise((resolve, reject) => {
if (imageList.length > 0) {
UploadApi.image(imageList)
.then(fileIds => {
form.images = fileIds
resolve(fileIds)
})
.catch(reject)
} else {
resolve()
}
})
},
// 提交到后端
onSubmit() {
const app = this
const {
form
} = this
let req;
if (form.apply_status == 30) {
req = ApplyApi.edit(form.apply_id, form)
} else {
req = ApplyApi.add(form)
}
req.then(result => {
console.log("result: ", result);
app.$toast(result.message)
setTimeout(function() {
uni.navigateBack()
}, 2000)
})
.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);
}
.pops-content {
padding: 20rpx;
}
</style>

View File

@@ -0,0 +1,566 @@
<template>
<view class="container" :style="appThemeStyle">
<!-- 表单组件 -->
<view class="form-wrapper">
<u-form :model="form" ref="uForm" label-width="140rpx">
<u-form-item label="所在地区" prop="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="公司名称" prop="company">
<u-input v-model="form.company" :disabled="disabled" placeholder="请输入公司名称" />
</u-form-item>
<u-form-item label="联系电话" prop="phone">
<u-input v-model="form.phone" :disabled="disabled" placeholder="请输入联系电话" />
</u-form-item>
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入商户姓名" />
</u-form-item>
<u-form-item label="手机号码" prop="mobile">
<u-input v-model="form.mobile" maxlength="11" :disabled="disabled" placeholder="请输入商户手机号" />
</u-form-item>
<u-form-item v-for="(item,imageIndex) in imageList" :key="imageIndex" :label="itemLabel[imageIndex]"
:prop="itemProp[imageIndex]">
<view class="image-preview">
<image mode="widthFix" :src="item.path" class="sfz" @click="chooseImage(imageIndex)"></image>
<text class="image-delete iconfont icon-shanchu" @click="deleteImage(imageIndex)"></text>
</view>
</u-form-item>
<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">我已阅读并了解{{words}}</text>
</view>
<text @click="handleShowLicense()" class="f-28 c-violet">{{ words.license.value }}</text>
</view>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">{{ submitText }}
</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 SelectRegion from '@/components/select-region/select-region'
import {
isMobile
} from '@/utils/verify'
import * as ApplyApi from '@/api/apply.js'
import * as UploadApi from '@/api/upload'
import SettingModel from '@/common/model/dealer/Setting'
// 入驻类型
const dataType = [
"partner", // 合伙人
"teamer", // 团长
"delivery", // 配送员
"personal" // 个人商户
]
// 图片上传类型
const itemProp = ["sfz", "yyzz"]
// 图片上传名称
const itemLabel = ["身份证", "营业执照"]
// 用户选择的图片列表
const imageList = [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 100
},
{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/55faad35f5ee30f3e8791bdfd2f75eb9.png',
size: 100
}
]
// 表单字段元素
const form = {
// 入驻类型 合伙人10001,配送员10002,社区门店10003,省代10004
grade_id: 10001,
company: '',
real_name: '',
phone: '',
mobile: '',
region: [],
detail: '',
}
// 表单验证规则
const rules = {
real_name: [{
required: true,
message: '请输入您的真实姓名',
trigger: ['blur', 'change']
}],
company: [{
required: true,
message: '请输入您的公司名称',
trigger: ['blur', 'change']
}],
phone: [{
required: true,
message: '请输入联系电话',
trigger: ['blur', 'change']
}],
mobile: [{
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']
}],
}
const maxImageLength = 2
export default {
components: {
SelectRegion
},
data() {
return {
dataType,
itemProp,
itemLabel,
imageList,
form,
rules,
// 按钮禁用
disabled: false,
submitText: '提交申请',
// 文字设置
words: undefined,
// 显示入驻协议弹窗
showLicense: false,
// 入驻协议
license: undefined,
// 入驻协议阅读状态
isRead: false,
// 显示入驻协议弹窗
showLicense: false,
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.getSetting()
this.getData()
},
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules)
},
methods: {
// 加载数据
getData() {
const app = this
ApplyApi.detail().then(res => {
const detail = res.data.detail
// 编辑信息
if (detail) {
app.form = detail
app.createFormData(detail)
}
})
},
// 生成默认的表单数据
createFormData(detail) {
const {
form
} = this
form.company = detail.company
form.phone = detail.phone
form.region = this.createRegion(detail.shop)
// 是否可编辑
this.disabled = true
// 状态
if (form.apply_status == 10) {
this.submitText = '待审核'
}
if (form.apply_status == 20) {
this.submitText = '审核通过'
}
if (form.apply_status == 30) {
this.submitText = '重新提交'
this.disabled = false
}
// 加载图片
this.imageList = detail.images.map(d => {
return {
path: d.image_url,
size: 101
}
})
form.images = detail.images.map(d => {
return d.image_id
})
},
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
}]
},
// 获取分销设置
getSetting() {
console.log("setting: ");
const app = this
SettingModel.data()
.then(setting => {
// 赋值属性
app.words = setting.words.apply.words
app.background = setting.background.apply
app.license = setting.license.license
})
},
// 显示入驻协议弹窗
handleShowLicense() {
this.showLicense = true
},
// 选择图片
chooseImage(imageIndex) {
const app = this
const oldImageList = app.imageList
const {
form
} = this
// 是否可编辑
if (form.apply_status && form.apply_status != 30) {
app.disabled = true
return false
}
// 选择图片
uni.chooseImage({
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success({
tempFiles
}) {
// 删除第imageIndex个元素并在数组的第imageIndex个位置添加新元素:
app.imageList.splice(imageIndex, 1, tempFiles[0])
}
});
},
// 删除图片
deleteImage(imageIndex) {
const {
disabled
} = this
if (disabled == true) {
return false
}
this.imageList[imageIndex].path = '../../static/not-dealer.png'
},
// 选择微信地址
// #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 : []
}
})
},
// #endif
// 表单提交
handleSubmit() {
const app = this
if (app.disabled) {
return false
}
const {
form
} = this
const {
imageList
} = this
app.$refs.uForm.validate(valid => {
if (valid) {
app.disabled = true
ApplyApi.checkShop(form).then(res => {
// 判断是否需要上传图片
if (imageList[0].size == 100 || imageList[1].size == 100) {
app.$toast('请上传图片!')
app.disabled = false
return false
}
if (imageList.length > 0 && imageList[0].size != 101) {
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()
}
}).catch(err => {
console.log("err: ", err);
if (err.statusCode == 0) {
app.disabled = false
app.$toast(err.errMsg)
return false
}
})
}
})
},
// 上传图片
uploadFile() {
const app = this
const {
imageList,
form
} = app
// 批量上传
return new Promise((resolve, reject) => {
if (imageList.length > 0) {
UploadApi.image(imageList)
.then(fileIds => {
form.images = fileIds
resolve(fileIds)
})
.catch(reject)
} else {
resolve()
}
})
},
// 提交到后端
onSubmit() {
const app = this
const {
form
} = this
let req;
if (form.apply_status == 30) {
req = ApplyApi.edit(form.apply_id, form)
} else {
req = ApplyApi.add(form)
}
req.then(result => {
console.log("result: ", result);
app.$toast(result.message)
setTimeout(function() {
uni.navigateBack()
}, 2000)
})
.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);
// 禁用按钮
&.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;
}
.sfz,
.sfz1,
.sfz2,
.yyzz {
width: 140rpx;
padding-top: 20rpx;
padding-right: 10rpx;
}
</style>

View File

@@ -0,0 +1,327 @@
<template>
<view>
<!-- 表单组件 -->
<view class="form-wrapper" :style="appThemeStyle">
{{form.imageList}}{{form.license}}
<u-form :model="form" ref="uForm" label-width="140rpx">
<!-- {{JSON.stringify(setting)}} -->
<u-form-item label="所在地区" prop="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="公司名称" prop="company">
<u-input v-model="form.company" :disabled="disabled" placeholder="请输入公司名称" />
</u-form-item>
<u-form-item label="联系电话" prop="phone">
<u-input v-model="form.phone" :disabled="disabled" placeholder="请输入联系电话" />
</u-form-item>
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入商户姓名" />
</u-form-item>
<u-form-item label="手机号码" prop="mobile">
<u-input v-model="form.mobile" maxlength="11" :disabled="disabled" placeholder="请输入商户手机号" />
</u-form-item>
<u-form-item v-for="(item,imageIndex) in imageList" :key="imageIndex" :label="form.itemLabel[imageIndex]"
:prop="form.imageProp[imageIndex]">
<view class="image-preview">
<image mode="widthFix" :src="item.path" class="sfz" @click="chooseImage(imageIndex)"></image>
<text class="image-delete iconfont icon-shanchu" @click="deleteImage(imageIndex)"></text>
</view>
</u-form-item>
<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">{{ form.licenseTitle }}</text>
</view>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">{{ form.submitText }}
</view>
</view>
</view>
</view>
</template>
<script>
import SelectRegion from '@/components/select-region/select-region'
import {
isMobile
} from '@/utils/verify'
import Empty from '@/components/empty'
import * as ApplyApi from '@/api/apply.js'
import SettingModel from '@/common/model/dealer/Setting'
// 表单验证规则
const rules = {
real_name: [{
required: true,
message: '请输入您的真实姓名',
trigger: ['blur', 'change']
}],
company: [{
required: true,
message: '请输入您的公司名称',
trigger: ['blur', 'change']
}],
phone: [{
required: true,
message: '请输入联系电话',
trigger: ['blur', 'change']
}],
mobile: [{
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']
}],
}
const maxImageLength = 2
export default {
components: {
Empty
},
props: {
form: {
type: Object,
default: null
}
},
data() {
return {
rules,
// 入驻协议阅读状态
isRead: false
}
},
created() {},
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules)
},
methods: {
// 选择图片
chooseImage(imageIndex) {
const app = this
const oldImageList = app.imageList
const { form } = this
// 是否可编辑
if (form.apply_status && form.apply_status != 30) {
return false
}
// 选择图片
uni.chooseImage({
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success({
tempFiles
}) {
// 传递给父级
app.$emit('chooseImage',imageIndex,tempFiles[0])
}
});
},
// 删除图片
deleteImage(imageIndex) {
// 传递给父级
this.$emit('deleteImage', imageIndex)
},
// 显示入驻协议弹窗
handleShowLicense() {
this.$emit('showLicense',true)
this.showLicense = true
},
// 表单提交
handleSubmit() {
const app = this
const { form, imageList } = this
// 判断是否需要上传图片
if (imageList[0].size == 100 || imageList[1].size == 100 || imageList[2].size == 100) {
app.$toast('请上传图片!')
return false
}
app.$refs.uForm.validate(valid => {
if (valid) {
// 传递给父级
app.$emit('handleSubmit', form, imageList)
}
})
}
}
}
</script>
<style lang="scss" scoped>
.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;
}
.form-license {
padding: 20rpx;
}
// 上传凭证
.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;
}
}
}
}
.pops-content {
padding: 20rpx;
}
.sfz,
.sfz1,
.sfz2,
.yyzz {
width: 140rpx;
padding-top: 20rpx;
padding-right: 10rpx;
}
/* 底部操作栏 */
.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;
}
}
}
</style>

View File

@@ -0,0 +1,412 @@
<template>
<view>
<!-- 表单组件 -->
<view class="form-wrapper" :style="appThemeStyle">
<u-form :model="form" ref="uForm" label-width="140rpx">
<!-- {{JSON.stringify(setting)}} -->
<u-form-item label="所在地区" prop="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="公司名称" prop="company">
<u-input v-model="form.company" :disabled="disabled" placeholder="请输入公司名称" />
</u-form-item>
<u-form-item label="联系电话" prop="phone">
<u-input v-model="form.phone" :disabled="disabled" placeholder="请输入联系电话" />
</u-form-item>
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入商户姓名" />
</u-form-item>
<u-form-item label="手机号码" prop="mobile">
<u-input v-model="form.mobile" maxlength="11" :disabled="disabled" placeholder="请输入商户手机号" />
</u-form-item>
<Partner :form="form"></Partner>
<u-form-item v-for="(item,imageIndex) in imageList" :key="imageIndex" :label="itemLabel[imageIndex]"
:prop="itemProp[imageIndex]">
<view class="image-preview">
<image mode="widthFix" :src="item.path" class="sfz" @click="chooseImage(imageIndex)"></image>
<text class="image-delete iconfont icon-shanchu" @click="deleteImage(imageIndex)"></text>
</view>
</u-form-item>
<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">{{ setting.words.apply.words.license.value }}</text>
</view>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">{{ submitText }}
</view>
</view>
</view>
<!-- 入驻协议弹窗 -->
<u-modal v-model="showLicense" title="入驻协议">
<scroll-view class="pops-content" :scroll-y="true">
<text>{{ setting.license }}</text>
</scroll-view>
</u-modal>
</view>
</template>
<script>
import SelectRegion from '@/components/select-region/select-region'
import {
isMobile
} from '@/utils/verify'
import Empty from '@/components/empty'
import * as ApplyApi from '@/api/apply.js'
import SettingModel from '@/common/model/dealer/Setting'
// 图片上传类型
const itemProp = ["sfz1", "sfz2", "yyzz"]
// 图片上传名称
const itemLabel = ["身份证(正)", "身份证(反)", "营业执照"]
// 用户选择的图片列表
const imageList = [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 100
},
{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/55faad35f5ee30f3e8791bdfd2f75eb9.png',
size: 100
}
]
// 表单验证规则
const rules = {
real_name: [{
required: true,
message: '请输入您的真实姓名',
trigger: ['blur', 'change']
}],
company: [{
required: true,
message: '请输入您的公司名称',
trigger: ['blur', 'change']
}],
phone: [{
required: true,
message: '请输入联系电话',
trigger: ['blur', 'change']
}],
mobile: [{
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']
}],
}
const maxImageLength = 2
export default {
components: {
Empty
},
props: {
form: {
type: Object,
default: null
},
setting: {
type: Object,
default: null
},
disabled: {
type: Boolean,
default: false
},
imageList: {
type: Array,
default: []
},
submitText: {
type: String,
default: '提交申请'
}
},
data() {
return {
itemProp,
itemLabel,
rules,
// 入驻协议阅读状态
isRead: false,
// 显示入驻协议弹窗
showLicense: false,
}
},
created() {},
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules)
},
methods: {
// 选择图片
chooseImage(imageIndex) {
const app = this
const oldImageList = app.imageList
const { form } = this
// 是否可编辑
if (form.apply_status && form.apply_status != 30) {
return false
}
// 选择图片
uni.chooseImage({
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success({
tempFiles
}) {
// 删除第imageIndex个元素并在数组的第imageIndex个位置添加新元素:
app.imageList.splice(imageIndex, 1, tempFiles[0])
}
});
// 传递给父级
this.$emit('chooseImage',app.imageList)
},
// 删除图片
deleteImage(imageIndex) {
const { disabled } = this
if (disabled == true) {
return false
}
this.imageList[imageIndex].path = '../../static/not-dealer.png'
},
// 显示入驻协议弹窗
handleShowLicense() {
this.showLicense = true
},
// 表单提交
handleSubmit() {
const app = this
const { form } = this
app.$refs.uForm.validate(valid => {
if (valid) {
// 传递给父级
app.$emit('handleSubmit', form)
}
})
},
// 上传图片
uploadFile() {
const app = this
const {
imageList,
form
} = app
// 批量上传
return new Promise((resolve, reject) => {
if (imageList.length > 0) {
UploadApi.image(imageList)
.then(fileIds => {
form.images = fileIds
resolve(fileIds)
})
.catch(reject)
} else {
resolve()
}
})
},
// 提交到后端
onSubmit() {
const app = this
const {
form
} = this
let req;
if (form.apply_status == 30) {
req = ApplyApi.edit(form.apply_id, form)
} else {
req = ApplyApi.add(form)
}
req.then(result => {
console.log("result: ", result);
app.$toast(result.message)
setTimeout(function() {
uni.navigateBack()
}, 2000)
})
.finally(() => app.disabled = false)
}
}
}
</script>
<style lang="scss" scoped>
.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;
}
.form-license {
padding: 20rpx;
}
// 上传凭证
.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;
}
}
}
}
.pops-content {
padding: 20rpx;
}
.sfz,
.sfz1,
.sfz2,
.yyzz {
width: 140rpx;
padding-top: 20rpx;
padding-right: 10rpx;
}
/* 底部操作栏 */
.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;
}
}
}
</style>

View File

@@ -0,0 +1,412 @@
<template>
<view>
<!-- 表单组件 -->
<view class="form-wrapper" :style="appThemeStyle">
<u-form :model="form" ref="uForm" label-width="140rpx">
<!-- {{JSON.stringify(setting)}} -->
<u-form-item label="所在地区" prop="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="公司名称" prop="company">
<u-input v-model="form.company" :disabled="disabled" placeholder="请输入公司名称" />
</u-form-item>
<u-form-item label="联系电话" prop="phone">
<u-input v-model="form.phone" :disabled="disabled" placeholder="请输入联系电话" />
</u-form-item>
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入商户姓名" />
</u-form-item>
<u-form-item label="手机号码" prop="mobile">
<u-input v-model="form.mobile" maxlength="11" :disabled="disabled" placeholder="请输入商户手机号" />
</u-form-item>
<Partner :form="form"></Partner>
<u-form-item v-for="(item,imageIndex) in imageList" :key="imageIndex" :label="itemLabel[imageIndex]"
:prop="itemProp[imageIndex]">
<view class="image-preview">
<image mode="widthFix" :src="item.path" class="sfz" @click="chooseImage(imageIndex)"></image>
<text class="image-delete iconfont icon-shanchu" @click="deleteImage(imageIndex)"></text>
</view>
</u-form-item>
<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">{{ setting.words.apply.words.license.value }}</text>
</view>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">{{ submitText }}
</view>
</view>
</view>
<!-- 入驻协议弹窗 -->
<u-modal v-model="showLicense" title="入驻协议">
<scroll-view class="pops-content" :scroll-y="true">
<text>{{ setting.license }}</text>
</scroll-view>
</u-modal>
</view>
</template>
<script>
import SelectRegion from '@/components/select-region/select-region'
import {
isMobile
} from '@/utils/verify'
import Empty from '@/components/empty'
import * as ApplyApi from '@/api/apply.js'
import SettingModel from '@/common/model/dealer/Setting'
// 图片上传类型
const itemProp = ["sfz1", "sfz2", "yyzz"]
// 图片上传名称
const itemLabel = ["身份证(正)", "身份证(反)", "营业执照"]
// 用户选择的图片列表
const imageList = [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 100
},
{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/55faad35f5ee30f3e8791bdfd2f75eb9.png',
size: 100
}
]
// 表单验证规则
const rules = {
real_name: [{
required: true,
message: '请输入您的真实姓名',
trigger: ['blur', 'change']
}],
company: [{
required: true,
message: '请输入您的公司名称',
trigger: ['blur', 'change']
}],
phone: [{
required: true,
message: '请输入联系电话',
trigger: ['blur', 'change']
}],
mobile: [{
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']
}],
}
const maxImageLength = 2
export default {
components: {
Empty
},
props: {
form: {
type: Object,
default: null
},
setting: {
type: Object,
default: null
},
disabled: {
type: Boolean,
default: false
},
imageList: {
type: Array,
default: []
},
submitText: {
type: String,
default: '提交申请'
}
},
data() {
return {
itemProp,
itemLabel,
rules,
// 入驻协议阅读状态
isRead: false,
// 显示入驻协议弹窗
showLicense: false,
}
},
created() {},
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules)
},
methods: {
// 选择图片
chooseImage(imageIndex) {
const app = this
const oldImageList = app.imageList
const { form } = this
// 是否可编辑
if (form.apply_status && form.apply_status != 30) {
return false
}
// 选择图片
uni.chooseImage({
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success({
tempFiles
}) {
// 删除第imageIndex个元素并在数组的第imageIndex个位置添加新元素:
app.imageList.splice(imageIndex, 1, tempFiles[0])
}
});
// 传递给父级
this.$emit('chooseImage',app.imageList)
},
// 删除图片
deleteImage(imageIndex) {
const { disabled } = this
if (disabled == true) {
return false
}
this.imageList[imageIndex].path = '../../static/not-dealer.png'
},
// 显示入驻协议弹窗
handleShowLicense() {
this.showLicense = true
},
// 表单提交
handleSubmit() {
const app = this
const { form } = this
app.$refs.uForm.validate(valid => {
if (valid) {
// 传递给父级
app.$emit('handleSubmit', form)
}
})
},
// 上传图片
uploadFile() {
const app = this
const {
imageList,
form
} = app
// 批量上传
return new Promise((resolve, reject) => {
if (imageList.length > 0) {
UploadApi.image(imageList)
.then(fileIds => {
form.images = fileIds
resolve(fileIds)
})
.catch(reject)
} else {
resolve()
}
})
},
// 提交到后端
onSubmit() {
const app = this
const {
form
} = this
let req;
if (form.apply_status == 30) {
req = ApplyApi.edit(form.apply_id, form)
} else {
req = ApplyApi.add(form)
}
req.then(result => {
console.log("result: ", result);
app.$toast(result.message)
setTimeout(function() {
uni.navigateBack()
}, 2000)
})
.finally(() => app.disabled = false)
}
}
}
</script>
<style lang="scss" scoped>
.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;
}
.form-license {
padding: 20rpx;
}
// 上传凭证
.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;
}
}
}
}
.pops-content {
padding: 20rpx;
}
.sfz,
.sfz1,
.sfz2,
.yyzz {
width: 140rpx;
padding-top: 20rpx;
padding-right: 10rpx;
}
/* 底部操作栏 */
.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;
}
}
}
</style>

595
package/apply/delivery.vue Normal file
View File

@@ -0,0 +1,595 @@
<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="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入商户姓名" />
</u-form-item>
<u-form-item label="手机号码" prop="mobile">
<u-input v-model="form.mobile" maxlength="11" :disabled="disabled" placeholder="请输入商户手机号" />
</u-form-item>
<u-form-item v-for="(item,imageIndex) in imageList" :key="imageIndex" :label="itemLabel[imageIndex]"
:prop="itemProp[imageIndex]">
<view class="image-preview">
<image mode="widthFix" :src="item.path" class="sfz" @click="chooseImage(imageIndex)"></image>
<text class="image-delete iconfont icon-shanchu" @click="deleteImage(imageIndex)"></text>
</view>
</u-form-item>
<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>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">{{ submitText }}
</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 SelectRegion from '@/components/select-region/select-region'
import {
isMobile
} from '@/utils/verify'
import * as ApplyApi from '@/api/apply.js'
import * as UploadApi from '@/api/upload'
import SettingModel from '@/common/model/dealer/Setting'
// 入驻类型
const dataType = [
"partner", // 商家
"teamer", // 团长
"delivery", // 配送员
"personal" // 个人商户
]
// 图片上传类型
const itemProp = ["sfz1", "sfz2"]
// 图片上传名称
const itemLabel = ["身份证(正)", "身份证(反)"]
// 用户选择的图片列表
const imageList = [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
}
]
// 表单字段元素
const form = {
// 入驻类型 商家10001,配送员10002,社区门店10003,省代10004
grade_id: 10002,
company: '',
real_name: '',
phone: '',
mobile: '',
region: [],
detail: '',
}
// 表单验证规则
const rules = {
real_name: [{
required: true,
message: '请输入您的真实姓名',
trigger: ['blur', 'change']
}],
company: [{
required: true,
message: '请输入您的公司名称',
trigger: ['blur', 'change']
}],
phone: [{
required: true,
message: '请输入联系电话',
trigger: ['blur', 'change']
}],
mobile: [{
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']
}],
}
const maxImageLength = 2
export default {
components: {
SelectRegion
},
data() {
return {
dataType,
itemProp,
itemLabel,
imageList,
form,
rules,
// 按钮禁用
disabled: false,
submitText: '提交申请',
// 文字设置
words: undefined,
// 显示入驻协议弹窗
showLicense: false,
// 入驻协议
license: undefined,
// 入驻协议阅读状态
isRead: false,
// 显示入驻协议弹窗
showLicense: false,
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const app = this
app.getSetting()
// 获取用户坐标及当前入驻申请记录
app.getLocation((res) => {
app.getData(res.longitude, res.latitude)
})
},
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules)
},
methods: {
// 获取用户坐标
// 参考文档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
}
})
},
// 加载数据
getData(longitude,latitude) {
const app = this
console.log("long: ",longitude);
app.form.longitude = longitude
app.form.latitude = latitude
ApplyApi.detail().then(res => {
const detail = res.data.detail
console.log(detail);
// 编辑信息
if (detail) {
app.form = detail
app.createFormData(detail)
}
})
},
// 生成默认的表单数据
createFormData(detail) {
const {
form
} = this
form.company = detail.company
form.phone = detail.phone
form.region = this.createRegion(detail.shop)
// 是否可编辑
this.disabled = true
// 状态
if (form.apply_status == 10) {
this.submitText = '待审核'
}
if (form.apply_status == 20) {
this.submitText = '审核通过'
}
if (form.apply_status == 30) {
this.submitText = '重新提交'
this.disabled = false
}
// 加载图片
this.imageList = detail.images.map(d => {
return {
path: d.image_url,
size: 101
}
})
form.images = detail.images.map(d => {
return d.image_id
})
},
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
}]
},
// 获取分销设置
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)
})
},
// 显示入驻协议弹窗
handleShowLicense() {
this.showLicense = true
},
// 设置当前页面标题
setPageTitle(title) {
uni.setNavigationBarTitle({
title: '申请成为配送员'
})
},
// 选择图片
chooseImage(imageIndex) {
const app = this
const oldImageList = app.imageList
const {
form
} = this
// 是否可编辑
if (form.apply_status && form.apply_status != 30) {
app.disabled = true
return false
}
// 选择图片
uni.chooseImage({
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success({
tempFiles
}) {
// 删除第imageIndex个元素并在数组的第imageIndex个位置添加新元素:
app.imageList.splice(imageIndex, 1, tempFiles[0])
}
});
},
// 删除图片
deleteImage(imageIndex) {
const {
disabled
} = this
if (disabled == true) {
return false
}
this.imageList[imageIndex].path = '../../static/not-dealer.png'
},
// 选择微信地址
// #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 : []
}
})
},
// #endif
// 表单提交
handleSubmit() {
const app = this
console.log(app.disabled);
if (app.disabled) {
return false
}
const {
form
} = this
const {
imageList
} = this
app.$refs.uForm.validate(valid => {
if (valid) {
app.disabled = true
ApplyApi.checkAvailable(form).then(res => {
// 判断是否需要上传图片
if (imageList[0].size == 0 || imageList[1].size == 0) {
app.$toast('请上传图片!')
app.disabled = false
return false
}
if (imageList.length > 0 && imageList[0].size > 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()
}
}).catch(err => {
console.log("err: ", err);
if (err.statusCode == 0) {
app.disabled = false
app.$toast(err.errMsg)
return false
}
})
}
})
},
// 上传图片
uploadFile() {
const app = this
const {
imageList,
form
} = app
// 批量上传
return new Promise((resolve, reject) => {
if (imageList.length > 0) {
UploadApi.image(imageList)
.then(fileIds => {
form.images = fileIds
resolve(fileIds)
})
.catch(reject)
} else {
resolve()
}
})
},
// 提交到后端
onSubmit() {
const app = this
const {
form
} = this
let req;
if (form.apply_status == 30) {
req = ApplyApi.edit(form.apply_id, form)
} else {
req = ApplyApi.add(form)
}
req.then(result => {
console.log("result: ", result);
app.$toast(result.message)
setTimeout(function() {
uni.navigateBack()
}, 2000)
})
.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);
// 禁用按钮
&.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;
}
</style>

625
package/apply/index.vue Normal file
View File

@@ -0,0 +1,625 @@
<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="company">
<u-input v-model="form.company" :disabled="disabled" placeholder="请输入站点名称" />
</u-form-item>
<u-form-item label="门脸照" prop="shopImage">
<u-upload ref="shopImage" :fileList="fileList1" :action="action" :header="headers"
@delete="deletePic" index="1" name="file" :maxCount="1" @afterRead="afterRead">
</u-upload>
</u-form-item>
</view>
<!-- 标题 -->
<view class="page-title">经营信息</view>
<!-- 表单组件 -->
<view class="form-wrapper">
<u-form-item label="所在地区" prop="region">
<!-- {{form.region}}
<u-select v-if="show" :disabled="disabled" :list="cityList" v-model="form.region"></u-select>
<text @click="openSelect">{{ cityName }}</text> -->
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="站点地址" prop="address">
<u-input v-model="form.address" :disabled="disabled" placeholder="请输入站点地址" />
</u-form-item>
<u-form-item label="食品经营许可证" prop="shipincard">
<u-upload ref="shipinCardImage" :fileList="fileList2" :action="action" :header="headers"
@delete="deletePic" index="2" name="file" :maxCount="1">
</u-upload>
</u-form-item>
<u-form-item label="法定代表人身份证" prop="sfz">
<u-upload ref="sfzImage" :fileList="fileList3" :action="action" :header="headers"
@delete="deletePic" index="3" name="file" :maxCount="2">
</u-upload>
</u-form-item>
<u-form-item label="营业执照" prop="yyzz">
<u-upload ref="yyzzImage" :fileList="fileList4" :action="action" :header="headers"
@delete="deletePic" index="4" name="file" :maxCount="1">
</u-upload>
</u-form-item>
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入姓名" />
</u-form-item>
<u-form-item label="手机号码" prop="mobile">
<u-input v-model="form.mobile" maxlength="11" :disabled="disabled" placeholder="请输入手机号" />
</u-form-item>
<u-form-item label="微信号" prop="weixin">
<u-input v-model="form.weixin" :disabled="disabled" placeholder="请输入微信号" />
</u-form-item>
</view>
</u-form>
<view class="btn">
<u-button type="primary" shape="circle" @click="handleSubmit()">
{{ submitText }}
</u-button>
</view>
<view class="btn"></view>
</view>
</template>
<script>
import store from '@/store'
import SelectRegion from '@/components/select-region/select-region'
import {
isMobile
} from '@/utils/verify'
import * as ApplyApi from '@/api/apply.js'
import * as UploadApi from '@/api/upload'
import * as RegionApi from '@/api/region.js'
import SettingModel from '@/common/model/dealer/Setting'
// 入驻类型
const dataType = [
"partner", // 合伙人
"teamer", // 团长
"delivery", // 配送员
"personal" // 个人商户
]
// 表单字段元素
const form = {
// 入驻类型 合伙人10001,配送员10002,社区门店10003,省代10004
grade_id: 10001,
company: '',
real_name: '',
phone: '',
mobile: '',
region: [],
detail: '',
address: '',
shop_image: '',
shipin_card_image: '',
sfz_image1: '',
sfz_image2: '',
yyzz_image: '',
cityList: [],
}
// 表单验证规则
const rules = {
real_name: [{
required: true,
message: '请输入您的真实姓名',
trigger: ['blur', 'change']
}],
company: [{
required: true,
message: '请输入您的商户名称',
trigger: ['blur', 'change']
}],
address: [{
required: true,
message: '请输入站点地址',
trigger: ['blur', 'change']
}],
weixin: [{
required: true,
message: '请输入微信号',
trigger: ['blur', 'change']
}],
mobile: [{
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'
}]
}
export default {
components: {
SelectRegion
},
data() {
return {
dataType,
fileList1: [],
fileList2: [],
fileList3: [],
fileList4: [],
form,
rules,
show: false,
cityName: '选择所在城市',
// 按钮禁用
disabled: false,
submitText: '申请创建商户',
// 文字设置
words: undefined,
// 显示入驻协议弹窗
showLicense: false,
// 入驻协议
license: undefined,
// 入驻协议阅读状态
isRead: false,
// 显示入驻协议弹窗
showLicense: false,
action: 'https://dev.gxwebsoft.com/ylx/api/upload/image',
headers: {
'storeId': 10001,
'Access-Token': uni.getStorageSync('AccessToken'),
'platform': store.getters.platform
}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const app = this
app.getSetting()
// 获取用户坐标及当前入驻申请记录
app.getLocation((res) => {
app.getData(res.longitude, res.latitude)
})
app.getRegion()
},
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules)
},
methods: {
openSelect() {
this.show = !this.show
},
// 删除图片
deletePic(event) {
if (this.disabled) {
return false
}
this[`fileList${event.name}`].splice(event.index, 1)
},
// 获取用户坐标
// 参考文档https://uniapp.dcloud.io/api/location/location?id=getlocation
getLocation(callback) {
const app = this
uni.getLocation({
type: 'wgs84',
success: callback,
fail(e) {
app.$toast('获取定位失败,请点击右下角按钮重新尝试定位')
app.isAuthor = false
}
})
},
// 加载数据
getData(longitude, latitude) {
const app = this
app.form.longitude = longitude
app.form.latitude = latitude
ApplyApi.detail().then(res => {
const detail = res.data.detail
// 编辑信息
if (detail) {
app.form = detail
if (detail.apply_status == 10) {
app.submitText = '待审核'
}
if (detail.apply_status == 20) {
app.submitText = '审核通过'
}
if (detail.apply_status == 30) {
app.submitText = '重新提交'
}
app.createFormData(detail)
}
})
},
onAfterRead(e){
console.log("e: ",e);
},
getRegion() {
RegionApi.list({
status: 20,
level: 2
}).then(res => {
this.cityList = res.data.list.map(d => {
return {
label: d.name,
value: d.id,
code: d.code,
id: d.id,
name: d.name,
pid: d.pid,
status: d.status
}
})
})
},
// 生成默认的表单数据
createFormData(detail) {
const {
form
} = this
form.company = detail.company
form.phone = detail.phone
form.mobile = detail.mobile
form.region = this.createRegion(detail)
// 加载图片
this.fileList1.push({
'url': detail.shop_image
})
this.fileList2.push({
'url': detail.shipin_card_image
})
this.fileList3.push({
'url': detail.sfz_image1
})
this.fileList3.push({
'url': detail.sfz_image2
})
this.fileList4.push({
'url': detail.yyzz_image
})
// 是否可编辑
this.disabled = true
// 状态
if (form.apply_status == 10) {
this.submitText = '待审核'
}
if (form.apply_status == 20) {
this.submitText = '审核通过'
}
if (form.apply_status == 30) {
this.submitText = '重新提交'
this.disabled = false
}
},
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
}]
},
// 获取分销设置
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)
})
},
// 显示入驻协议弹窗
handleShowLicense() {
this.showLicense = true
},
// 设置当前页面标题
setPageTitle(title) {
uni.setNavigationBarTitle({
title: '商户入驻'
})
},
// 选择微信地址
// #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 : []
}
})
},
// #endif
afterRead(e){
console.log("e: ",e);
},
// 表单提交
handleSubmit() {
const app = this
if (app.disabled) {
return false
}
const {
form
} = this
if(app.$refs.shopImage.lists.length == 0){
app.$toast('请上传门脸照')
return false
}
if(app.$refs.shipinCardImage.lists.length == 0){
app.$toast('请上传食品经营许可证')
return false
}
if(app.$refs.sfzImage.lists.length != 2){
app.$toast('请上传法定代表人身份证正反面')
return false
}
if(app.$refs.yyzzImage.lists.length == 0){
app.$toast('请上传营业执照')
return false
}
app.$refs.uForm.validate(valid => {
if (valid) {
// 上传图片
if (app.fileList1.length == 0) {
form.shop_image = app.$refs.shopImage.lists[0].response.data.fileInfo.preview_url;
}
if (app.fileList2.length == 0) {
form.shipin_card_image = app.$refs.shipinCardImage.lists[0].response.data.fileInfo.preview_url;
}
if (app.fileList3.length == 0) {
form.sfz_image1 = app.$refs.sfzImage.lists[0].response.data.fileInfo.preview_url;
form.sfz_image2 = app.$refs.sfzImage.lists[1].response.data.fileInfo.preview_url;
}
if (app.fileList4.length == 0) {
form.yyzz_image = app.$refs.yyzzImage.lists[0].response.data.fileInfo.preview_url;
}
form.phone = form.mobile
ApplyApi.checkAvailable(form).then(res => {
app.disabled = true
app.onSubmit()
})
}
})
},
// 提交到后端
onSubmit() {
const app = this
const {
form
} = this
let req;
if (form.apply_status == 30) {
req = ApplyApi.edit(form.apply_id, form)
} else {
console.log('add');
req = ApplyApi.add(form)
}
req.then(result => {
console.log("result: ", result);
app.$toast(result.message)
setTimeout(function() {
uni.navigateBack()
}, 2000)
})
.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);
// 禁用按钮
&.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>

604
package/apply/personal.vue Normal file
View File

@@ -0,0 +1,604 @@
<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="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="公司名称" prop="company">
<u-input v-model="form.company" :disabled="disabled" placeholder="请输入公司名称" />
</u-form-item>
<u-form-item label="联系电话" prop="phone">
<u-input v-model="form.phone" :disabled="disabled" placeholder="请输入联系电话" />
</u-form-item>
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入商户姓名" />
</u-form-item>
<u-form-item label="手机号码" prop="mobile">
<u-input v-model="form.mobile" maxlength="11" :disabled="disabled" placeholder="请输入商户手机号" />
</u-form-item>
<u-form-item v-for="(item,imageIndex) in imageList" :key="imageIndex" :label="itemLabel[imageIndex]"
:prop="itemProp[imageIndex]">
<view class="image-preview">
<image mode="widthFix" :src="item.path" class="sfz" @click="chooseImage(imageIndex)"></image>
<text class="image-delete iconfont icon-shanchu" @click="deleteImage(imageIndex)"></text>
</view>
</u-form-item>
<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>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">{{ submitText }}
</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 SelectRegion from '@/components/select-region/select-region'
import {
isMobile
} from '@/utils/verify'
import * as ApplyApi from '@/api/apply.js'
import * as UploadApi from '@/api/upload'
import SettingModel from '@/common/model/dealer/Setting'
// 入驻类型
const dataType = [
"partner", // 合伙人
"teamer", // 团长
"delivery", // 配送员
"personal" // 个人商户
]
// 图片上传类型
const itemProp = ["sfz1", "sfz2", "yyzz"]
// 图片上传名称
const itemLabel = ["身份证(正)", "身份证(反)", "营业执照"]
// 用户选择的图片列表
const imageList = [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},
{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/55faad35f5ee30f3e8791bdfd2f75eb9.png',
size: 0
}
]
// 表单字段元素
const form = {
// 入驻类型 合伙人10001,配送员10002,社区门店10003,省代10004
grade_id: 10004,
company: '',
real_name: '',
phone: '',
mobile: '',
region: [],
detail: '',
}
// 表单验证规则
const rules = {
real_name: [{
required: true,
message: '请输入您的真实姓名',
trigger: ['blur', 'change']
}],
company: [{
required: true,
message: '请输入您的公司名称',
trigger: ['blur', 'change']
}],
phone: [{
required: true,
message: '请输入联系电话',
trigger: ['blur', 'change']
}],
mobile: [{
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']
}],
}
const maxImageLength = 2
export default {
components: {
SelectRegion
},
data() {
return {
dataType,
itemProp,
itemLabel,
imageList,
form,
rules,
// 按钮禁用
disabled: false,
submitText: '提交申请',
// 文字设置
words: undefined,
// 显示入驻协议弹窗
showLicense: false,
// 入驻协议
license: undefined,
// 入驻协议阅读状态
isRead: false,
// 显示入驻协议弹窗
showLicense: false,
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const app = this
app.getSetting()
// 获取用户坐标及当前入驻申请记录
app.getLocation((res) => {
app.getData(res.longitude, res.latitude)
})
},
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules)
},
methods: {
// 获取用户坐标
// 参考文档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
}
})
},
// 加载数据
getData(longitude,latitude) {
const app = this
app.form.longitude = longitude
app.form.latitude = latitude
ApplyApi.detail().then(res => {
const detail = res.data.detail
console.log(detail);
// 编辑信息
if (detail) {
app.form = detail
app.createFormData(detail)
}
})
},
// 生成默认的表单数据
createFormData(detail) {
const {
form
} = this
form.company = detail.company
form.phone = detail.phone
form.region = this.createRegion(detail.shop)
// 是否可编辑
this.disabled = true
// 状态
if (form.apply_status == 10) {
this.submitText = '待审核'
}
if (form.apply_status == 20) {
this.submitText = '审核通过'
}
if (form.apply_status == 30) {
this.submitText = '重新提交'
this.disabled = false
}
// 加载图片
this.imageList = detail.images.map(d => {
return {
path: d.image_url,
size: 101
}
})
form.images = detail.images.map(d => {
return d.image_id
})
},
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
}]
},
// 获取分销设置
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)
})
},
// 显示入驻协议弹窗
handleShowLicense() {
this.showLicense = true
},
// 设置当前页面标题
setPageTitle(title) {
uni.setNavigationBarTitle({
title: '申请成为商家'
})
},
// 选择图片
chooseImage(imageIndex) {
const app = this
const oldImageList = app.imageList
const {
form
} = this
// 是否可编辑
if (form.apply_status && form.apply_status != 30) {
app.disabled = true
return false
}
// 选择图片
uni.chooseImage({
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success({
tempFiles
}) {
// 删除第imageIndex个元素并在数组的第imageIndex个位置添加新元素:
app.imageList.splice(imageIndex, 1, tempFiles[0])
}
});
},
// 删除图片
deleteImage(imageIndex) {
const {
disabled
} = this
if (disabled == true) {
return false
}
this.imageList[imageIndex].path = '../../static/not-dealer.png'
},
// 选择微信地址
// #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 : []
}
})
},
// #endif
// 表单提交
handleSubmit() {
const app = this
console.log(app.disabled);
if (app.disabled) {
return false
}
const {
form
} = this
const {
imageList
} = this
app.$refs.uForm.validate(valid => {
if (valid) {
app.disabled = true
ApplyApi.checkAvailable(form).then(res => {
// 判断是否需要上传图片
if (imageList[0].size == 0 || imageList[1].size == 0 || imageList[2].size == 0) {
app.$toast('请上传图片!')
app.disabled = false
return false
}
if (imageList.length > 0 && imageList[0].size > 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()
}
}).catch(err => {
console.log("err: ", err);
if (err.statusCode == 0) {
app.disabled = false
app.$toast(err.errMsg)
return false
}
})
}
})
},
// 上传图片
uploadFile() {
const app = this
const {
imageList,
form
} = app
// 批量上传
return new Promise((resolve, reject) => {
if (imageList.length > 0) {
UploadApi.image(imageList)
.then(fileIds => {
form.images = fileIds
resolve(fileIds)
})
.catch(reject)
} else {
resolve()
}
})
},
// 提交到后端
onSubmit() {
const app = this
const {
form
} = this
let req;
if (form.apply_status == 30) {
req = ApplyApi.edit(form.apply_id, form)
} else {
req = ApplyApi.add(form)
}
req.then(result => {
console.log("result: ", result);
app.$toast(result.message)
setTimeout(function() {
uni.navigateBack()
}, 2000)
})
.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);
// 禁用按钮
&.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;
}
</style>

80
package/apply/select.vue Normal file
View File

@@ -0,0 +1,80 @@
<template>
<view class="wrap" :style="appThemeStyle">
<u-row gutter="16">
<u-col span="6">
<view class="wrap-item bg-purple-light" v-if="gradeId == 0 || gradeId == 10001" @click="navTo('package/apply/index')"><image src="http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/b437b0213516d446f2bbd2e4ca4aa1d3.png" class="wrap-avatar" mode="widthFix"></image><text class="item-text">商户入驻</text></view>
</u-col>
<!-- <u-col span="6">
<view class="wrap-item bg-purple-light" v-if="gradeId == 0 || gradeId == 10002" @click="navTo('package/apply/delivery')"><image src="http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/92ba7cadfa6459034c3570e9141c49f5.png" class="wrap-avatar" mode="widthFix"></image><text class="item-text">配送员入驻</text></view>
</u-col> -->
<u-col span="6">
<!-- <view class="wrap-item bg-purple-light" v-if="gradeId == 0 || gradeId == 10003" @click="navTo('package/apply/teamer')"><image src="http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/4df3d4d3a575467d9216b081ef23fe7a.png" class="wrap-avatar" mode="widthFix"></image><text class="item-text">团长入驻</text></view> -->
</u-col>
<!-- <u-col span="6">
<view class="wrap-item bg-purple-light" v-if="gradeId == 0 || gradeId == 10004" @click="navTo('package/apply/personal')"><image src="http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/127c0a1f401a1119937eb3995235dc26.png" class="wrap-avatar" mode="widthFix"></image><text class="item-text">个人商户入驻</text></view>
</u-col> -->
</u-row>
</view>
</template>
<script>
import * as ApplyApi from '@/api/apply.js'
export default {
data() {
return {
gradeId: 0,
disabled: false
}
},
onLoad(){
ApplyApi.detail().then(res => {
let detail = res.data.detail
if(detail){
this.gradeId = detail.grade_id
}
})
},
methods: {
navTo(url){
this.$navTo(url)
}
}
}
</script>
<style>
.wrap {
padding: 24rpx;
}
.u-row {
margin: 40rpx 0;
}
.wrap-item {
height: 120rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple-light {
background: #e5e9f2;
}
.bg-purple-dark {
background: #99a9bf;
}
.wrap-avatar{
width: 80rpx;
}
.item-text{
padding: 0 20rpx;
}
</style>

605
package/apply/teamer.vue Normal file
View File

@@ -0,0 +1,605 @@
<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="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="公司名称" prop="company">
<u-input v-model="form.company" :disabled="disabled" placeholder="请输入公司名称" />
</u-form-item>
<u-form-item label="联系电话" prop="phone">
<u-input v-model="form.phone" :disabled="disabled" placeholder="请输入联系电话" />
</u-form-item>
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入商家姓名" />
</u-form-item>
<u-form-item label="手机号码" prop="mobile">
<u-input v-model="form.mobile" maxlength="11" :disabled="disabled" placeholder="请输入商家手机号" />
</u-form-item>
<u-form-item v-for="(item,imageIndex) in imageList" :key="imageIndex" :label="itemLabel[imageIndex]"
:prop="itemProp[imageIndex]">
<view class="image-preview">
<image mode="widthFix" :src="item.path" class="sfz" @click="chooseImage(imageIndex)"></image>
<text class="image-delete iconfont icon-shanchu" @click="deleteImage(imageIndex)"></text>
</view>
</u-form-item>
<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>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">{{ submitText }}
</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 SelectRegion from '@/components/select-region/select-region'
import {
isMobile
} from '@/utils/verify'
import * as ApplyApi from '@/api/apply.js'
import * as UploadApi from '@/api/upload'
import SettingModel from '@/common/model/dealer/Setting'
// 入驻类型
const dataType = [
"partner", // 合伙人
"teamer", // 团长
"delivery", // 配送员
"personal" // 个人商户
]
// 图片上传类型
const itemProp = ["sfz1", "sfz2", "yyzz"]
// 图片上传名称
const itemLabel = ["身份证(正)", "身份证(反)", "营业执照"]
// 用户选择的图片列表
const imageList = [{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/6b52b67ed9291757163c10ef96be1a54.png',
size: 0
},
{
path: 'http://dev.gxwebsoft.com/hainiu/uploads/10001/20220522/55faad35f5ee30f3e8791bdfd2f75eb9.png',
size: 0
}
]
// 表单字段元素
const form = {
// 入驻类型 合伙人10001,配送员10002,社区门店10003,省代10004
grade_id: 10003,
company: '',
real_name: '',
phone: '',
mobile: '',
region: [],
detail: '',
}
// 表单验证规则
const rules = {
real_name: [{
required: true,
message: '请输入您的真实姓名',
trigger: ['blur', 'change']
}],
company: [{
required: true,
message: '请输入您的公司名称',
trigger: ['blur', 'change']
}],
phone: [{
required: true,
message: '请输入联系电话',
trigger: ['blur', 'change']
}],
mobile: [{
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']
}],
}
const maxImageLength = 2
export default {
components: {
SelectRegion
},
data() {
return {
dataType,
itemProp,
itemLabel,
imageList,
form,
rules,
// 按钮禁用
disabled: false,
submitText: '提交申请',
// 文字设置
words: undefined,
// 显示入驻协议弹窗
showLicense: false,
// 入驻协议
license: undefined,
// 入驻协议阅读状态
isRead: false,
// 显示入驻协议弹窗
showLicense: false,
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const app = this
app.getSetting()
// 获取用户坐标及当前入驻申请记录
app.getLocation((res) => {
app.getData(res.longitude, res.latitude)
})
},
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules)
},
methods: {
// 获取用户坐标
// 参考文档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
}
})
},
// 加载数据
getData(longitude,latitude) {
const app = this
app.form.longitude = longitude
app.form.latitude = latitude
ApplyApi.detail().then(res => {
const detail = res.data.detail
console.log(detail);
// 编辑信息
if (detail) {
app.form = detail
app.createFormData(detail)
}
})
},
// 生成默认的表单数据
createFormData(detail) {
const {
form
} = this
form.company = detail.company
form.phone = detail.phone
form.region = this.createRegion(detail.shop)
// 是否可编辑
this.disabled = true
// 状态
if (form.apply_status == 10) {
this.submitText = '待审核'
}
if (form.apply_status == 20) {
this.submitText = '审核通过'
}
if (form.apply_status == 30) {
this.submitText = '重新提交'
this.disabled = false
}
// 加载图片
this.imageList = detail.images.map(d => {
return {
path: d.image_url,
size: 101
}
})
form.images = detail.images.map(d => {
return d.image_id
})
},
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
}]
},
// 获取分销设置
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)
})
},
// 显示入驻协议弹窗
handleShowLicense() {
this.showLicense = true
},
// 设置当前页面标题
setPageTitle(title) {
uni.setNavigationBarTitle({
title: '申请成为团长'
})
},
// 选择图片
chooseImage(imageIndex) {
const app = this
const oldImageList = app.imageList
const {
form
} = this
// 是否可编辑
if (form.apply_status && form.apply_status != 30) {
app.disabled = true
return false
}
// 选择图片
uni.chooseImage({
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success({
tempFiles
}) {
// 删除第imageIndex个元素并在数组的第imageIndex个位置添加新元素:
app.imageList.splice(imageIndex, 1, tempFiles[0])
}
});
},
// 删除图片
deleteImage(imageIndex) {
const {
disabled
} = this
if (disabled == true) {
return false
}
this.imageList[imageIndex].path = '../../static/not-dealer.png'
},
// 选择微信地址
// #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 : []
}
})
},
// #endif
// 表单提交
handleSubmit() {
const app = this
console.log(app.disabled);
if (app.disabled) {
return false
}
const {
form
} = this
const {
imageList
} = this
app.$refs.uForm.validate(valid => {
if (valid) {
app.disabled = true
ApplyApi.checkAvailable(form).then(res => {
// 判断是否需要上传图片
if (imageList[0].size == 0 || imageList[1].size == 0 || imageList[2].size == 0) {
app.$toast('请上传图片!')
app.disabled = false
return false
}
if (imageList.length > 0 && imageList[0].size > 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()
}
}).catch(err => {
console.log("err: ", err);
if (err.statusCode == 0) {
app.disabled = false
app.$toast(err.errMsg)
return false
}
})
}
})
},
// 上传图片
uploadFile() {
const app = this
const {
imageList,
form
} = app
// 批量上传
return new Promise((resolve, reject) => {
if (imageList.length > 0) {
UploadApi.image(imageList)
.then(fileIds => {
form.images = fileIds
resolve(fileIds)
})
.catch(reject)
} else {
resolve()
}
})
},
// 提交到后端
onSubmit() {
const app = this
const {
form
} = this
let req;
if (form.apply_status == 30) {
req = ApplyApi.edit(form.apply_id, form)
} else {
req = ApplyApi.add(form)
}
req.then(result => {
console.log("result: ", result);
app.$toast(result.message)
setTimeout(function() {
uni.navigateBack()
}, 2000)
})
.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);
// 禁用按钮
&.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;
}
</style>

View File

@@ -0,0 +1,67 @@
<template>
<!-- 搜索框 -->
<view class="search-wrapper">
<view class="index-search" @click="onClick">
<view class="index-cont-search t-c">
<text class="search-icon iconfont icon-search"></text>
<text class="search-text">{{ tips }}</text>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
tips: {
type: String,
default: '搜索关键词'
}
},
data() {
return {}
},
methods: {
onClick() {
this.$emit('event')
}
}
}
</script>
<style lang="scss" scoped>
.search-wrapper {
padding: 13rpx;
}
.index-search {
border-bottom: 0;
border-radius: 50rpx;
overflow: hidden;
font-size: 28rpx;
color: #6d6d6d;
box-sizing: border-box;
line-height: 64rpx;
display: flex;
align-items: center;
.index-cont-search {
width: 100%;
font-size: 28rpx;
background: #f7f7f7;
display: flex;
justify-content: center;
align-items: center;
}
.index-cont-search .search-icon {
font-size: 28rpx;
}
.index-cont-search .search-text {
margin-left: 14rpx;
}
}
</style>

228
package/brand/detail.vue Normal file
View File

@@ -0,0 +1,228 @@
<template>
<view class="partner">
<!-- 表单组件 -->
<view class="form-wrapper" v-if="detail">
<u-form :model="form" ref="uForm" label-width="240rpx">
<u-form-item>
<image :src="detail.imageUrl" mode="widthFix" class="banner"></image>
<view class="brand-name">{{detail.name}}</view>
</u-form-item>
<u-form-item>
<view class="shop-box">
<text class="shop-name">品牌介绍</text>
<text>{{detail.describe}}</text>
</view>
</u-form-item>
<view class="buy-desc">如果有意向请填写以下信息给平台留言</view>
<!-- 表单组件 -->
<view class="form-wrapper">
<u-form :model="form" ref="uForm" label-width="140rpx">
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入姓名" />
</u-form-item>
<u-form-item label="电话" prop="phone">
<u-input v-model="form.phone" maxlength="11" :disabled="disabled" placeholder="请输入联系电话" />
</u-form-item>
<u-form-item label="性别" prop="sex">
<u-radio-group v-model="form.sex">
<u-radio @change="groupChange" v-for="(item, index) in sex" :key="index"
:name="item.name" :disabled="item.disabled">
{{item.name}}
</u-radio>
</u-radio-group>
</u-form-item>
<u-form-item label="定制预算" prop="money">
<u-input type="number" v-model="form.money" :disabled="disabled" placeholder="请输入定制预算" />
</u-form-item>
<u-form-item label="微信号" prop="weixin">
<u-input v-model="form.weixin" :disabled="disabled" placeholder="请输入微信号" />
</u-form-item>
<u-form-item label="意向区域" prop="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="整体需求" prop="comments">
<u-input v-model="form.comments" :disabled="disabled" placeholder="请输入整体需求" />
</u-form-item>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn">
<u-button type="primary" shape="circle" @click="handleSubmit()">提交</u-button>
</view>
</view>
</u-form>
</view>
<!-- 操作按钮 -->
<!-- <view class="footer">
<view class="btn">
<u-button type="primary" shape="circle">更新资料</u-button>
</view>
</view> -->
</view>
</template>
<script>
// 表单字段元素
const form = {
name: '',
phone: '',
sex: '',
money: '',
region: [],
weixin: '',
comments: '',
}
const sex = [{
name: '男',
value: 1,
disabled: false
},
{
name: '女',
value: 2,
disabled: false
}
]
import * as Api from '@/api/brand/index.js'
import * as ApplyApi from '@/api/brand/apply.js'
import SelectRegion from '@/components/select-region/select-region'
export default {
components: {
SelectRegion
},
data() {
return {
form,
disabled: false,
detail: {},
brandId: 0,
sex
}
},
onLoad(options) {
this.brandId = options.brandId
this.init()
},
methods: {
// 获取当前用户信息
init() {
const app = this
const {
brandId
} = this
console.log(brandId);
Api.detail(brandId).then(result => {
app.detail = result.data.detail
})
},
groupChange(e) {
this.form.sex = e
},
// 表单提交
handleSubmit() {
const app = this
// if (app.disabled) {
// return false
// }
const {
form,
brandId
} = this
form.brand_id = brandId
app.$refs.uForm.validate(valid => {
if (valid) {
app.disabled = true
app.onSubmit()
}
})
},
// 提交到后端
onSubmit() {
const app = this
const {
form
} = this
let req;
if (form.apply_status == 30) {
req = ApplyApi.edit(form.apply_id, form)
} else {
console.log('add');
req = ApplyApi.add(form)
}
req.then(result => {
console.log("result: ", result);
app.$toast(result.message)
setTimeout(function() {
uni.navigateBack()
}, 2000)
})
.finally(() => app.disabled = false)
}
}
}
</script>
<style lang="less">
.partner {
.banner {
padding-top: 20rpx;
width: 100%;
}
.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;
.shop-box {
text-align: center;
display: flex;
flex-direction: column;
.shop-name {
font-weight: bold;
font-size: 36rpx;
}
}
.logo {
width: 100rpx;
}
.shipin {
width: 400rpx;
}
}
.footer {
width: 500rpx;
margin: 50rpx auto;
padding-bottom: 50rpx !important;
}
}
.brand-name {
text-align: center;
}
.buy-desc {
text-align: center;
color: #ff0000;
margin-top: 40rpx;
}
.buy-text {
text-align: center;
}
</style>

68
package/brand/index.vue Normal file
View File

@@ -0,0 +1,68 @@
<template>
<view class="solution">
<!-- <view class="search">
<search class="search" :tips="options.search ? options.search : '搜索关键词'" @event="handleSearch" />
</view> -->
<view class="grid">
<u-grid :border="false">
<u-grid-item v-for="(item,index) in baseList" :key="index">
<image :src="item.imageUrl" class="avatar" mode="widthFix" @click="navTo('package/brand/detail?brandId=' + item.brandId)"></image>
<text class="grid-text" @click="navTo('package/brand/detail?brandId=' + item.brandId)">{{item.name}}</text>
</u-grid-item>
</u-grid>
</view>
</view>
</template>
<script>
import Search from './components/search'
import * as Api from '@/api/brand/index.js'
export default {
components: {
Search
},
data() {
return {
baseList: []
}
},
onLoad(){
this.init()
},
methods: {
init(){
const app = this
Api.list().then(res => {
app.baseList = res.data.list.data
})
},
navTo(url){
this.$navTo(url)
}
}
}
</script>
<style lang="less">
.solution {
background-color: #ffffff;
.search {
padding: 10rpx 20rpx;
}
.btn {
width: 700rpx;
border-radius: 100%;
margin: 25rpx auto;
}
.grid {
width: 700rpx;
margin: auto;
}
.avatar {
width: 120rpx;
}
}
</style>

76
package/city/index.vue Normal file
View File

@@ -0,0 +1,76 @@
<template>
<view class="container">
<u-index-list :scrollTop="scrollTop">
<view v-for="(item, index) in indexList" :key="index">
<block v-for="(d,i) in cityList" :key="i" v-if="d.letter == item">
<u-index-anchor :index="item" />
<view class="list-cell" @click="selectCityId(d)">
<text>{{ d.name }}</text>
</view>
</block>
</view>
</u-index-list>
</view>
</template>
<script>
import * as Api from '@/api/region.js'
import storage from '@/utils/storage'
export default {
data() {
return {
scrollTop: 0,
indexList: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
"T", "U",
"V", "W", "X", "Y", "Z"
],
cityList: []
}
},
onLoad() {
this.init()
},
onPageScroll(e) {
this.scrollTop = e.scrollTop;
},
methods: {
navTo(url) {
this.$navTo(url)
},
init(){
Api.list({status: 20}).then(res => {
this.cityList = res.data.list
console.log("res: ",res);
})
},
selectCityId(city){
storage.set('cityName', city.name)
storage.set('cityId', city.id)
uni.$emit('cityId', city.id)
// 返回上级页面
uni.navigateBack({
delta: 1
})
},
bindClick(e) {
console.log('点击item返回数据' + JSON.stringify(e))
}
}
}
</script>
<style lang="scss" scoped>
.list-cell {
display: flex;
box-sizing: border-box;
width: 100%;
padding: 10px 24rpx;
overflow: hidden;
color: #323233;
font-size: 14px;
line-height: 24px;
background-color: #fff;
}
</style>
</style>

View File

@@ -0,0 +1,402 @@
<template>
<view class="container" :style="appThemeStyle">
<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="item-prices oneline-hide">
<text class="goods-sales">已售{{ item.goods_sales }}</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>
<view class="buy-gouwuche">
<u-button size="mini" @click="handleAddCart(item)">取消收藏
</u-button>
</view>
</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: []
}
},
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
}
},
sortPrice: false, // 价格排序 (true高到低 false低到高)
sortType: 'hot', // 排序类型
}
},
created() {
// 设置分类列表高度
this.setListHeight()
},
methods: {
// 切换排序方式
handleSortType(newSortType) {
const app = this
const newSortPrice = newSortType === 'price' ? !app.sortPrice : true
app.sortType = newSortType
app.sortPrice = newSortPrice
// 刷新列表数据
app.getGoodsList()
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
// 设置列表数据
console.log(page.num);
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,
collection: true
}, {
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) {
GoodsApi.collection({goodsId: item.})
this.$emit('addCart')
},
// 更新购物车角标
onUpdateCartTabBadge() {
console.log('onUpdateCartTabBadge')
setCartTabBadge()
},
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #F6F6F6 !important;
}
// 分类内容
.cate-content {
z-index: 1;
background-color: #F6F6F6 !important;
padding-top: 0rpx;
}
// 一级分类+二级分类 20
.cate-left {
width: 173rpx;
height: 100%;
background: #f8f8f8;
color: #444;
position: fixed;
top: calc(286rpx + 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 {
padding: 20rpx;
}
.goods-item {
padding: 28rpx 22rpx;
display: flex;
box-sizing: border-box;
padding: 20rpx;
border: 1px solid #F6F6F6;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 12rpx;
}
.goods-item_left {
position: relative;
background: #fff;
margin-right: 20rpx;
.image {
display: block;
width: 280rpx;
height: 280rpx;
}
}
.goods-item_right {
position: relative;
flex: 1;
.goods-name {
display: block;
width: 100%;
min-height: 68rpx;
font-size: 34rpx;
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: flex-end;
position: absolute;
right: 0rpx;
bottom: 0rpx;
min-height: 44rpx;
.item-prices {
padding-right: 6rpx;
.price_x {
margin-right: 14rpx;
font-weight: 600;
color: #ff0000;
font-size: 38rpx;
}
.price_y {
color: #999;
text-decoration: line-through;
font-size: 24rpx;
}
}
}
}
.goods-sales {
padding-top: 8rpx !important;
color: #b3b3b3 !important;
font-size: 24rpx;
}
.search-box {
display: flex;
justify-content: space-around;
background: #fff;
border-radius: 14rpx;
padding: 20rpx 4rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.07);
}
// 排序组件
.store-sort {
margin-top: 24rpx;
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;
}
}
}
}
</style>

394
package/collection/index.vue Executable file
View File

@@ -0,0 +1,394 @@
<template>
<view class="container" :style="appThemeStyle">
<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="item-prices oneline-hide">
<text class="goods-sales">已售{{ item.goods_sales }}</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>
<view class="buy-gouwuche">
<u-button size="mini" @click="handleAddCart(item)">取消收藏
</u-button>
</view>
</view>
</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,
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],
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
}
},
sortPrice: false, // 价格排序 (true高到低 false低到高)
sortType: 'hot', // 排序类型
}
},
created() {
// 设置分类列表高度
this.setListHeight()
},
methods: {
// 切换排序方式
handleSortType(newSortType) {
const app = this
const newSortPrice = newSortType === 'price' ? !app.sortPrice : true
app.sortType = newSortType
app.sortPrice = newSortPrice
// 刷新列表数据
app.getGoodsList()
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
// 设置列表数据
console.log(page.num);
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,
collection: true
}, {
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) {
GoodsApi.collection({goodsId: item.goods_id}).then(() => {
this.getGoodsList()
})
},
// 更新购物车角标
onUpdateCartTabBadge() {
console.log('onUpdateCartTabBadge')
setCartTabBadge()
},
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #F6F6F6 !important;
}
// 分类内容
.cate-content {
z-index: 1;
background-color: #F6F6F6 !important;
padding-top: 0rpx;
}
// 一级分类+二级分类 20
.cate-left {
width: 173rpx;
height: 100%;
background: #f8f8f8;
color: #444;
position: fixed;
top: calc(286rpx + 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 {
padding: 20rpx;
}
.goods-item {
padding: 28rpx 22rpx;
display: flex;
box-sizing: border-box;
padding: 20rpx;
border: 1px solid #F6F6F6;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 12rpx;
}
.goods-item_left {
position: relative;
background: #fff;
margin-right: 20rpx;
.image {
display: block;
width: 280rpx;
height: 280rpx;
}
}
.goods-item_right {
position: relative;
flex: 1;
.goods-name {
display: block;
width: 100%;
min-height: 68rpx;
font-size: 34rpx;
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: flex-end;
position: absolute;
right: 0rpx;
bottom: 0rpx;
min-height: 44rpx;
.item-prices {
padding-right: 6rpx;
.price_x {
margin-right: 14rpx;
font-weight: 600;
color: #ff0000;
font-size: 38rpx;
}
.price_y {
color: #999;
text-decoration: line-through;
font-size: 24rpx;
}
}
}
}
.goods-sales {
padding-top: 8rpx !important;
color: #b3b3b3 !important;
font-size: 24rpx;
}
.search-box {
display: flex;
justify-content: space-around;
background: #fff;
border-radius: 14rpx;
padding: 20rpx 4rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.07);
}
// 排序组件
.store-sort {
margin-top: 24rpx;
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;
}
}
}
}
</style>

316
package/dealer/apply.vue Executable file
View File

@@ -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>

237
package/dealer/driver.vue Executable file
View File

@@ -0,0 +1,237 @@
<template>
<view class="container" v-if="!isLoading">
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption"
@up="upCallback">
<!-- 团队总人数 -->
<view class="widget-people f-28 col-9">
<text>司机列表({{total}})</text>
<u-button type="primary" size="mini">添加</u-button>
</view>
<!-- 列表数据 -->
<view class="widget-list b-f">
<view class="widget__detail dis-flex flex-x-between" v-for="(item, index) in list" :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.real_name }}</view>
<view class="user-time col-9 f-24">{{ item.mobile }}</view>
</view>
</view>
<view class="detail__right dis-flex flex-dir-column flex-x-center flex-y-center">
<view class="detail__money">
</view>
<view class="detail__member f-22">
派单
</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/shop/clerk'
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(),
total: 0,
// 上拉加载配置
upOption: {
// 首次自动执行
auto: true,
// 每页数据的数量; 默认10
page: { size: pageSize },
// 数量要大于12条才显示无更多数据
noMoreSize: 12,
// 空布局
empty: {
tip: '亲,暂无相关数据'
}
},
// 正在加载
isLoading: true,
// 文字设置
words: undefined,
// 团队总人数
teamTotal: undefined,
orderId: 0
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const app = this
console.log(options.orderId);
app.orderId = options.orderId
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
resolve(setting)
})
.catch(reject)
})
},
// 获取分销用户信息
getDealerUser() {
const app = this
const { shopId } = uni.getStorageSync('shopInfo')
Api.list({shopId}).then((result) => {
app.list = result.data.list
app.total = result.data.total
})
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中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: 0 25rpx;
height: 80rpx;
line-height: 60rpx;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
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>

321
package/dealer/index.vue Executable file
View File

@@ -0,0 +1,321 @@
<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="shopInfo.logoUrl" :width="150" :borderWidth="4"
:borderColor="`#fff`" />
<view class="user-nickName f-32">{{ shopInfo.shopName }}</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'
import * as UserApi from '@/api/user'
export default {
components: {
AvatarImage
},
data() {
return {
// 正在加载
isLoading: true,
// 当前商户信息
shopInfo: {},
// 当前是否为分销商
isDealer: false,
// 当前分销商信息
dealer: undefined,
// 推荐人昵称
refereeName: undefined,
// 分销设置
setting: {
background: undefined,
words: undefined
}
}
},
/**
* 生命周期函数--监听页面加载
*/
onShow(options) {
this.getCenter()
this.getUserInfo()
},
methods: {
// 获取当前用户信息
getUserInfo() {
const app = this
UserApi.info().then(result => {
app.shopInfo = result.data.shopInfo
})
},
// 获取分销商中心数据
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,
title: '我的商户'
})
}
}
}
</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: 24rpx;
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>

223
package/dealer/order.vue Executable file
View File

@@ -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>

80
package/dealer/poster.vue Executable file
View File

@@ -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>

236
package/dealer/team.vue Executable file
View File

@@ -0,0 +1,236 @@
<template>
<view class="container" v-if="!isLoading">
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption"
@up="upCallback">
<!-- 团队总人数 -->
<view class="widget-people f-28 col-9">
<text>司机列表({{total}})</text>
<!-- <u-button type="primary" size="mini">添加</u-button> -->
</view>
<!-- 列表数据 -->
<view class="widget-list b-f">
<view class="widget__detail dis-flex flex-x-between" v-for="(item, index) in list" :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.real_name }}</view>
<view class="user-time col-9 f-24">{{ item.mobile }}</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/shop/clerk'
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(),
total: 0,
// 上拉加载配置
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
resolve(setting)
})
.catch(reject)
})
},
// 获取分销用户信息
getDealerUser() {
const app = this
const { shopId } = uni.getStorageSync('shopInfo')
Api.list({shopId}).then((result) => {
app.list = result.data.list
app.total = result.data.total
})
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中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: 0 25rpx;
height: 80rpx;
line-height: 60rpx;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
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>

398
package/dealer/withdraw/apply.vue Executable file
View File

@@ -0,0 +1,398 @@
<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">{{ shopInfo.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="capital__lowest m-top20 col-7 t-r">
<u-button size="mini" @click="navTo('package/dealer/withdraw/list')">查看明细</u-button>
<text>提现手续费3%</text>
</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">
<u-alert-tips v-if="!shopInfo.alipayAccount" type="warning" :title="title"
:description="'首次提现将会保存,请填写正确的支付宝账号!'"></u-alert-tips>
<view class="form__field dis-flex flex-y-center">
<view class="field-input flex-box">
<input class="input" name="alipay_name" :value="shopInfo.alipayName"
:disabled="shopInfo.alipayName" 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" :value="shopInfo.alipayAccount"
:disabled="shopInfo.alipayAccount" 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 ShopApi from '@/api/shop.js'
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,
shopInfo: {},
shopId: 0,
// 分销商用户信息
dealer: undefined,
// 当前提现方式(选中的)
payment: undefined,
// 分销结算设置
settlement: undefined,
// 文字设置
words: undefined,
// 背景图
background: undefined,
// 按钮禁用
disabled: false
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.getSetting()
this.getDealer()
this.getShopInfo()
},
methods: {
navTo(url) {
this.$navTo(url)
},
getShopInfo() {
const shopInfo = uni.getStorageSync('shopInfo')
const {
shopId
} = shopInfo
this.shopId = shopId
ShopApi.detail(shopId).then(res => {
this.shopInfo = res.data.detail
})
},
// 获取分销设置
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
data.shopId = app.shopId
ShopApi.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;
display: flex;
justify-content: space-between;
}
// 提现方式
.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>

249
package/dealer/withdraw/list.vue Executable file
View File

@@ -0,0 +1,249 @@
<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">
<text>提现 {{ item.money }}</text>
<text v-if="item.pay_type == 10" class="iconfont icon-weixin"></text>
<text v-if="item.pay_type == 20" class="iconfont icon-alipay"></text>
<text v-if="item.pay_type == 30" class="iconfont icon-weixin"></text>
</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 f-28" :class="[ApplyStatusColor[item.apply_status]]">
<text v-if="item.apply_status == 10">待审核</text>
<text v-if="item.apply_status == 20">审核通过</text>
<text v-if="item.apply_status == 30">驳回</text>
<text v-if="item.apply_status == 40">已打款</text>
</view>
<block v-if="item.apply_status == 30">
<view class="detail__reason f-24" @click="triggerReasonMsg">查看原因
</view>
<u-modal v-model="showReasonMsg" title="驳回原因">
<view class="show-reason-msg">{{ item.reject_reason }}</view>
</u-modal>
</block>
</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 Api from '@/api/shop/withdraw'
import SettingModel from '@/common/model/dealer/Setting'
import { ApplyStatusEnum } from '@/common/enum/dealer/withdraw'
const pageSize = 15
// 提现状态文字
const ApplyStatusText = [10,20,30]
// 提现状态颜色
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(),
shopId: 0,
showReasonMsg: false,
// 上拉加载配置
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.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 }
]
},
triggerReasonMsg(){
this.showReasonMsg = !this.showReasonMsg
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中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
const { shopId } = uni.getStorageSync('shopInfo')
return new Promise((resolve, reject) => {
Api.list({ applyStatus: app.getTabValue(), page: pageNo, shopId })
.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-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;
}
.widget__detail .detail__money {
font-size: 30rpx;
margin-bottom: 12rpx;
}
.widget__detail .detail__reason {
margin-top: 15rpx;
color: #8e84fc;
}
.show-reason-msg{
padding: 20rpx;
line-height: 2rem;
color: #ff0000;
}
.detail__left{
.iconfont{
margin-left: 30rpx;
font-size: 24rpx;
}
.icon-weixin{
color: #0eaf00;
}
.icon-alipay{
color: #0f80ff;
}
}
</style>

View File

@@ -0,0 +1,206 @@
<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 { 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 : []
}
})
},
// #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);
// 禁用按钮
&.disabled {
opacity: 0.6;
}
}
}
</style>

305
package/deliver/address/index.vue Executable file
View File

@@ -0,0 +1,305 @@
<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-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;
}
.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);
}
}
</style>

View File

@@ -0,0 +1,244 @@
<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 : []
}
})
},
// #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);
// 禁用按钮
&.disabled {
opacity: 0.6;
}
}
}
</style>

View File

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

View File

@@ -0,0 +1,322 @@
<template>
<view style="height: 90vh;">
<u-navbar title-color="#fff" back-icon-color="#ffffff"
:is-fixed="true"
:is-back="true"
:background="topBackground"
:back-text-style="{color: '#fff'}"
:title="title"
:back-icon-name="backIconName"
:back-text="backText"
>
</u-navbar>
<view class="divison"></view>
<view class="myData">
<view class="datas">我的佣金<text style="color: #ED1C24;font-size: 18px;margin: 5rpx;">{{ myMoney }}</text></view>
<view class="datas">已配送单数<text style="color: #ED1C24;font-size: 18px;margin: 5rpx;">{{ orderNum }}</text></view>
</view>
<view class="pageContent">
<view class="u-tabs-box">
<u-tabs-swiper activeColor="#f29100" ref="tabs" :list="list" :current="current" @change="change" :is-scroll="false" ></u-tabs-swiper>
</view>
<swiper class="swiper-box" :current="swiperCurrent" @transition="transition" @animationfinish="animationfinish">
<!-- 待送达 -->
<swiper-item class="swiper-item">
<scroll-view scroll-y style="height: 100%;width: 100%;" @scrolltolower="reachBottom">
<view class="orderList" v-for="(item,index) in ongoinglist" :key="index">
<view class="orderMassage">
<view class="storePicture">
<image :src="item.store_picture" border-radius="20px" style="width: 90%;height: 90%;"></image>
</view>
<view class="details">
<view class="orderText">
<text >取货地址{{ item.take_addr }}</text>
</view>
<view class="orderText">
<text>配送地址{{ item.go_addr }}</text>
</view>
<view class="orderText">
<text style="color: #ffaa00;">配送预计时间{{ item.predict_time }}分钟</text>
</view>
<view class="orderText">
<view>佣金<text style="color: #ff0000"></text><text style="font-size: 18px;color: #ff0000;">{{ item.earn_money }}</text></view>
</view>
</view>
</view>
<view class="giveUp">
<u-button shape="square" type="success" size="mini" :plain="true">放弃订单</u-button>
</view>
</view>
</scroll-view>
</swiper-item>
<!-- 已送达 -->
<swiper-item class="swiper-item">
<scroll-view scroll-y style="height: 100%;width: 100%;" @scrolltolower="reachBottom" >
<view class="orderList2" v-for="(item,index) in completeorder" :key="index">
<view class="orderMassage2">
<view class="storePicture">
<image :src="item.store_picture" border-radius="20px" style="width: 90%;height: 80%;border-radius: 10rpx;"></image>
</view>
<view class="details">
<view>
<view class="orderText">
<text>取货地址{{ item.take_addr }}</text>
</view>
<view class="orderText">
<text>配送地址{{ item.go_addr }}</text>
</view>
<view class="orderText2">
<view>佣金<text style="color: #ff0000"></text><text style="font-size: 18px;color: #ff0000;">{{ item.earn_money }}</text></view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</swiper-item>
</swiper>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 顶部标题
title: '我的配送',
backText: '返回',
backIconName: 'nav-back',
isBack: true,
topBackground: {
backgroundImage: 'linear-gradient(to right, #146930 , #77b633)'
},
// 我的佣金和订单数
myMoney:888,
orderNum:55,
orderList: [[], []],
list:[
{
name:'待送达',
},
{
name:'已送达',
}
],
// 因为内部的滑动机制限制请将tabs组件和swiper组件的current用不同变量赋值
current: 0,
swiperCurrent: 0,
tabsHeight: 0,
dx: 0,
loadStatus: ['loadmore','loadmore'],
ongoinglist:[
{
store_id:1,
store_picture:'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fwx4.sinaimg.cn%2Flarge%2F006CjT3ggy1gvbdntuo3fj60n40d0abv02.jpg&refer=http%3A%2F%2Fwx4.sinaimg.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1638519744&t=f9c70870945fb51eb9f4aa93ef5a3390',
take_addr:'北京天安门广场一号广场楼',
go_addr:'清华大学',
predict_time:18,
earn_money:18,
},
{
store_id:2,
store_picture:'https://pic.rmb.bdstatic.com/bjh/news/f8510dd27a49a7d30befaef266d0a9a5.png',
take_addr:'南宁市良庆区总部基地',
go_addr:'广西大学',
predict_time:18,
earn_money:18,
}
],
completeorder:[
{
store_id:1,
store_picture:'https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00555-2517.jpg',
go_addr:'清华大学',
predict_time:18,
earn_money:18,
take_addr:'南宁市良庆区总部基地',
},
{
store_id:2,
store_picture:'https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00555-2517.jpg',
take_addr:'南宁市良庆区总部基地',
go_addr:'广西大学',
predict_time:18,
earn_money:18,
}
],
}
},
methods: {
// tab栏切换
change(index) {
this.swiperCurrent = index;
this.getOrderList(index);
},
transition({ detail: { dx } }) {
this.$refs.tabs.setDx(dx);
},
animationfinish({ detail: { current } }) {
this.$refs.tabs.setFinishCurrent(current);
this.swiperCurrent = current;
this.current = current;
},
// 页面数据 foodMaterialList
getOrderList(idx) {
if(idx==0){
for(let i = 0; i < 5; i++) {
let index = this.$u.random(0, this.ongoinglist.length - 1);
let data = JSON.parse(JSON.stringify(this.ongoinglist[index]));
data.id = this.$u.guid();
this.orderList[idx].push(data);
}
this.loadStatus.splice(this.current,3,"loadmore")
}else if(idx==1){
for(let i = 0; i < 5; i++) {
let index = this.$u.random(0, this.completeorder.length - 1);
let data = JSON.parse(JSON.stringify(this.completeorder[index]));
data.id = this.$u.guid();
this.orderList[idx].push(data);
}
this.loadStatus.splice(this.current,3,"loadmore")
}
},
reachBottom() {
// 此tab为空数据
if(this.current != 2) {
this.loadStatus.splice(this.current,10,"loading")
setTimeout(() => {
this.getOrderList(this.current);
}, 1200);
}
},
}
}
</script>
<style>
.myData{
display: flex;
justify-content: space-around;
align-items: center;
background-color: #FFFFFF;
margin-bottom: 5px;
}
.datas{
display: flex;
justify-content: center;
align-items: center;
width: 50%;
height: 40px;
font-size: 15px;
border-radius: 10px;
margin: 20rpx 10rpx;
}
.pageContent{
height: 100%;
}
.swiper-box {
height: 100%;
}
.swiper-item {
height: 100%;
}
.orderList{
margin-top: 20rpx;
height: 140px;
}
.orderList2{
margin-top: 20rpx;
}
.divison{
width: 100%;
height: 10rpx;
background-color: #e3e3e5;
}
.orderMassage{
display: flex;
flex-direction: row;
border: #f3f3f3 solid 1px;
border-radius: 10px;
height: 135px;
align-items: center;
background-color: #FFFFFF;
}
.orderMassage2{
display: flex;
flex-direction: row;
border: #f3f3f3 solid 1px;
border-radius: 10px;
height: 120px;
align-items: center;
background-color: #FFFFFF;
}
.storePicture{
display: flex;
justify-content: center;
align-items: center;
width: 45%;
height: 260rpx;
border-radius: 10rpx;
}
.details{
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-end;
width: 55%;
}
.orderText{
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
margin-top: 10rpx;
padding-left: 10rpx;
}
.orderText2{
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
margin-top: 20rpx;
padding-left: 10rpx;
}
.giveUp{
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: flex-end;
position: relative;
top: -70rpx;
margin-right: 15rpx;
color: #FFFFFF;
}
</style>

View File

@@ -0,0 +1,271 @@
<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="#0f80ff" :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" @click="onRead(item.id, item.content)">
<u-avatar size="mini" mode="circle" v-if="item.sendUser" :src="item.sendUser.avatar_url">
</u-avatar>
<view class="content">
<text>{{ item.content }}</text>
<u-badge :is-dot="true" :offset="[0,0]" type="error" v-if="item.status == 0"></u-badge>
</view>
</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 f-28">
<u-tag v-if="item.status == 0" type="error" text="未读"></u-tag>
<u-tag v-if="item.status == 1" type="success" text="已读"></u-tag>
</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 Api from '@/api/user/notice.js'
import SettingModel from '@/common/model/dealer/Setting'
import {
ApplyStatusEnum
} from '@/common/enum/dealer/withdraw'
const pageSize = 15
// 提现状态文字
const ApplyStatusText = [0, 1]
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
// 枚举类
ApplyStatusEnum,
ApplyStatusText,
// 选项卡列表
tabList: [],
// 当前选项
curTab: 0,
// 列表数据
list: getEmptyPaginateObj(),
shopId: 0,
showReasonMsg: false,
// 上拉加载配置
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.withdraw_list
app.setTabList(words.words)
})
},
// 设置选项卡数据
setTabList(words) {
const app = this
app.tabList = [
{
value: 0,
name: '未读'
},
{
value: 1,
name: '已读'
}
]
},
triggerReasonMsg() {
this.showReasonMsg = !this.showReasonMsg
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中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({
status: 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 0
},
// 切换标签项
onChangeTab(index) {
const app = this
// 设置当前选中的标签
app.curTab = index
// 刷新订单列表
app.onRefreshList()
},
// 刷新列表数据
onRefreshList() {
this.list = getEmptyPaginateObj()
setTimeout(() => {
this.mescroll.resetUpScroll()
}, 120)
},
// 设为已读
onRead(id, content) {
const app = this
uni.showModal({
title: '查看消息',
content,
showCancel: false,
success({ confirm }) {
Api.status({id}).then(res => {
app.getList()
})
}
})
},
}
}
</script>
<style>
page {
background: #fff;
}
</style>
<style lang="scss" scoped>
// 提现明细列表
.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;
}
.widget__detail .detail__money {
font-size: 30rpx;
margin-bottom: 12rpx;
display: flex;
align-items: center;
}
.widget__detail .detail__reason {
margin-top: 15rpx;
color: #0f80ff;
}
.show-reason-msg {
padding: 20rpx;
line-height: 2rem;
color: #ff0000;
}
.detail__left {
.iconfont {
margin-left: 30rpx;
font-size: 24rpx;
}
.icon-weixin {
color: #0eaf00;
}
.icon-alipay {
color: #0f80ff;
}
}
.detail__status {
position: relative;
}
.detail__money {
.content {
position: relative;
}
}
.modal-content{
padding: 20rpx;
}
</style>

View File

@@ -0,0 +1,750 @@
<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">
<block v-for="(item, index) in list.data" :key="index">
<view class="shop-name" v-if="item.shopName">
<image :src="item.logoUrl" class="shop-name-logo" mode="widthFix"></image>
<text>{{ item.shopName }}</text>
</view>
<view class="order-item">
<view class="item-top">
<view class="item-top-left">
<text class="order-time">{{item.create_time}}</text>
</view>
<view class="item-top-right">
<text class="state-text">{{ item.state_text }}</text>
</view>
</view>
<!-- 商品列表 -->
<view class="goods-list">
<view class="goods-item" v-for="(goods, idx) in item.goods" :key="idx">
<!-- 商品图片 -->
<view class="goods-image" @click="onTargetDetail(goods.goods_id)">
<image class="image" :src="goods.goods_image" mode="scaleToFill"></image>
</view>
<!-- 商品信息 -->
<view class="goods-content">
<view class="goods-title"><text class="twoline-hide">{{ goods.goods_name }}</text>
</view>
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in goods.goods_props"
:key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
</view>
<!-- 交易信息 -->
<view class="goods-trade">
<view class="goods-price">
<text class="unit"></text>
<text
class="value">{{ goods.is_user_grade ? goods.grade_goods_price : goods.goods_price }}</text>
</view>
<view class="goods-num">
<text>×{{ goods.total_num }}</text>
</view>
</view>
</view>
</view>
<!-- 订单合计 -->
<view class="order-total">
<!-- <text class="create-time">{{ item.create_time }}</text> -->
<!-- <text>{{ item.total_num }}件商品总金额</text> -->
<!-- <text class="unit"></text> -->
<text class="money">合计{{ item.pay_price }}</text>
</view>
<!-- 订单操作 -->
<view v-if="item.order_status != OrderStatusEnum.CANCELLED.value" class="order-handle">
<view class="btn-group clearfix">
<!-- 未接单 -->
<block v-if="item.delivery_status == DeliveryStatusEnum.NOT_RECEIVING.value">
<view class="btn-box">
<u-button size="mini" @click="expressInfo(item.address)">配送地址</u-button>
<view style="margin-left: 15rpx;">
<u-button type="primary" size="mini" @click="onDelivery(item.order_id)">接单
</u-button>
</view>
<view style="margin-left: 15rpx;">
<u-button type="warning" size="mini"
@click="offDelivery(item.order_id, item.storePhone)">拒单</u-button>
</view>
</view>
</block>
<!-- 待签收 -->
<block
v-if="item.delivery_status == DeliveryStatusEnum.DELIVERED.value && item.receipt_status == ReceiptStatusEnum.NOT_RECEIVED.value">
<view class="btn-box2">
<view style="margin-left: 15rpx;">
<u-button type="primary" size="mini" @click="expressInfo(item.address)">配送地址</u-button>
</view>
<!-- <view style="margin-left: 15rpx;">
<u-button size="mini" @click="offDelivery(item.order_id)">退单</u-button>
</view> -->
</view>
</block>
<!-- 未支付取消订单 -->
<!-- <block v-if="item.pay_status == PayStatusEnum.PENDING.value">
<view class="btn-item" @click="onCancel(item.order_id)">取消</view>
</block> -->
<!-- 已支付进行中的订单 -->
<!-- <block v-if="item.order_status != OrderStatusEnum.APPLY_CANCEL.value"> -->
<!-- <block v-if="item.pay_status == PayStatusEnum.SUCCESS.value && item.delivery_status == DeliveryStatusEnum.NOT_DELIVERED.value">
<u-button type="primary" size="mini" class="btn-item" @click="onDelivery(item.order_id)">配送</u-button>
</block> -->
<!-- 订单核销码 -->
<!-- <block v-if="item.pay_status == PayStatusEnum.SUCCESS.value && item.delivery_type == DeliveryTypeEnum.EXTRACT.value
&& item.delivery_status == DeliveryStatusEnum.NOT_DELIVERED.value">
<view class="btn-item active" @click="onExtractQRCode(item.order_id)">
<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.pay_status == PayStatusEnum.PENDING.value">
<view class="btn-item active" @click="onPay(item.order_id)">去支付</view>
</block> -->
<!-- 确认收货 -->
<!-- <block v-if="item.delivery_status == DeliveryStatusEnum.DELIVERED.value && item.receipt_status == ReceiptStatusEnum.NOT_RECEIVED.value">
<view class="btn-item active" @click="onReceipt(item.order_id)">确认收货</view>
</block> -->
</view>
</view>
</view>
</block>
</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="showExpressInfo" mode="center" border-radius="26" :closeable="true">
<view class="qrcode-popup">
<view class="title">配送信息</view>
<view class="pop-content2">
<text>收货人姓名:{{ address.name }}</text>
<text>收货人电话:{{ address.phone }}</text>
<text>详细地址:{{ address.detail }}</text>
</view>
</view>
</u-popup>
<!-- 拒单弹窗 -->
<u-popup v-model="showOffDeliver" mode="center" border-radius="26" :closeable="true">
<view class="qrcode-popup">
<view class="title">拒单理由</view>
<view class="pop-content2">
<u-input v-model="offDeliverInfo" />
</view>
<view class="btn-box-off-deliver">
<u-button type="success" size="mini" @click="callShop">联系商家</u-button>
<u-button type="warning" size="mini" @click="onSubmit">确定提交</u-button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import {
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum
} from '@/common/enum/order'
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import {
getEmptyPaginateObj,
getMoreListData
} from '@/core/app'
import * as OrderApi from '@/api/order'
import * as UserApi from '@/api/user'
import {
wxPayment
} from '@/core/app'
// 每页记录数量
const pageSize = 15
// tab栏数据
const tabs = [{
name: `全部`,
value: 'driver'
}, {
name: `使用中`,
value: 'driverDaiJieDan'
},
// {
// name: `待配送`,
// value: 'delivery'
// },
{
name: `已过期`,
value: 'driverDaiShouHuo'
}, {
name: `已结束`,
value: 'driverOk'
}
]
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
// 枚举类
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum,
// 当前页面参数
options: {
dataType: 'all'
},
// tab栏数据
tabs,
// 当前标签索引
curTab: 0,
// 订单列表数据
list: getEmptyPaginateObj(),
// 上拉加载配置
upOption: {
// 首次自动执行
auto: true,
// 每页数据的数量; 默认10
page: {
size: pageSize
},
// 数量要大于4条才显示无更多数据
noMoreSize: 4,
// 空布局
empty: {
tip: '亲,暂无订单记录'
}
},
// 控制首次触发onShow事件时不刷新列表
canReset: false,
// 核销二维码弹窗
showQRCodePopup: false,
showExpressInfo: false,
showOffDeliver: false,
// 核销二维码图片url (通过后端获取)
qrcodeImage: '',
shopInfo: {},
address: {},
offDeliverInfo: undefined,
storePhone: null
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 初始化当前选中的标签
this.initCurTab(options)
this.getOrderList()
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
this.canReset && this.onRefreshList()
this.canReset = true
},
methods: {
// 初始化当前选中的标签
initCurTab(options) {
const app = this
if (options.dataType) {
const index = app.tabs.findIndex(item => item.value == options.dataType)
app.curTab = index > -1 ? index : 0
}
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
// 设置列表数据
app.getOrderList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
// 获取订单列表
getOrderList(pageNo = 1) {
const app = this
const {
clerk_id
} = uni.getStorageSync('clerkInfo')
return new Promise((resolve, reject) => {
OrderApi.list({
dataType: app.getTabValue(),
page: pageNo,
driverOrder: true,
clerk_id
}, {
load: false
})
.then(result => {
// 合并新数据
const newList = app.initList(result.data.list)
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
})
},
// 初始化订单列表数据
initList(newList) {
newList.data.forEach(item => {
item.total_num = 0
item.goods.forEach(goods => {
item.total_num += goods.total_num
})
})
return newList
},
// 获取当前标签项的值
getTabValue() {
return this.tabs[this.curTab].value
},
// 切换标签项
onChangeTab(index) {
const app = this
// 设置当前选中的标签
app.curTab = index
// 刷新订单列表
app.onRefreshList()
},
// 刷新订单列表
onRefreshList() {
this.list = getEmptyPaginateObj()
setTimeout(() => {
this.mescroll.resetUpScroll()
}, 120)
},
// 取消订单
onCancel(orderId) {
const app = this
uni.showModal({
title: '友情提示',
content: '确认要取消该订单吗?',
success(o) {
if (o.confirm) {
OrderApi.cancel(orderId)
.then(result => {
// 显示成功信息
app.$toast(result.message)
// 刷新订单列表
app.onRefreshList()
})
}
}
});
},
onDelivery(orderId) {
const app = this
OrderApi.onDeliver(orderId).then(result => {
// 显示成功信息
app.$success(result.message)
// 刷新订单列表
app.onRefreshList()
})
},
offDelivery(orderId, storePhone) {
this.showOffDeliver = true
this.orderId = orderId
this.storePhone = storePhone
},
callShop() {
const {
storePhone
} = this
uni.makePhoneCall({
phoneNumber: storePhone
})
},
onSubmit() {
const app = this
const {
orderId,
offDeliverInfo
} = this
OrderApi.offDeliver(orderId, {
offDeliverInfo
}).then(result => {
app.showOffDeliver = false
// 显示成功信息
app.$success(result.message)
// 刷新订单列表
app.onRefreshList()
})
},
doOffDelivery() {
OrderApi.offDeliver(orderId).then(result => {
// 显示成功信息
app.$success(result.message)
// 刷新订单列表
app.onRefreshList()
})
// uni.showModal({
// title: '拒单理由',
// content: '退单后将返回商家后台重新派单!',
// success(o) {
// if (o.confirm) {
// OrderApi.offDeliver(orderId).then(result => {
// // 显示成功信息
// app.$success(result.message)
// // 刷新订单列表
// app.onRefreshList()
// })
// }
// }
// });
},
expressInfo(address) {
this.showExpressInfo = true
this.address = address
console.log(address);
},
// 确认收货
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()
})
}
}
});
},
// 获取核销二维码
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
})
},
// 跳转商品详情页
onTargetDetail(goodsId) {
this.$navTo('pages/goods/detail', {
goodsId
})
},
},
}
</script>
<style lang="scss" scoped>
.shop-name {
width: 720rpx;
margin: auto;
height: 50rpx;
display: flex;
flex-direction: row;
align-items: center;
font-weight: bold;
.shop-name-logo {
width: 36rpx !important;
height: 36rpx !important;
border: 1rpx solid #ffffff;
border-radius: 100%;
margin: 0 10rpx;
}
}
// 项目内容
.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;
.store-house {
font-weight: bold;
font-size: 32rpx;
}
.order-time {
color: #777;
}
.state-text {
color: $main-bg;
}
}
// 商品列表
.goods-list {
// 商品项
.goods-item {
display: flex;
margin-bottom: 40rpx;
// 商品图片
.goods-image {
width: 180rpx;
height: 180rpx;
.image {
display: block;
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
// 商品内容
.goods-content {
flex: 1;
padding-left: 16rpx;
padding-top: 16rpx;
.goods-title {
font-size: 26rpx;
max-height: 76rpx;
}
.goods-props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
.goods-props-item {
display: inline-block;
margin-right: 14rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
width: auto;
}
}
}
// 交易信息
.goods-trade {
padding-top: 16rpx;
width: 150rpx;
text-align: right;
color: $uni-text-color-grey;
font-size: 26rpx;
.goods-price {
vertical-align: bottom;
margin-bottom: 16rpx;
.unit {
margin-right: -2rpx;
font-size: 24rpx;
}
}
}
}
}
// 订单合计
.order-total {
font-size: 26rpx;
vertical-align: bottom;
display: flex;
justify-content: space-between;
height: 40rpx;
margin-bottom: 30rpx;
.create-time {
color: #777;
}
.unit {
margin-left: 8rpx;
margin-right: -2rpx;
font-size: 26rpx;
}
.money {
font-size: 28rpx;
}
}
// 订单操作
.order-handle {
.btn-group {
.btn-box {
display: flex;
justify-content: flex-end;
padding: 12rpx 0;
}
.btn-box2 {
display: flex;
justify-content: flex-end;
}
.btn-item {
border-radius: 10rpx;
padding: 6rpx 20rpx;
margin-left: 15rpx;
font-size: 28rpx;
float: right;
&:last-child {
margin-left: 0;
}
&.active {
color: $main-bg;
border: 1rpx solid $main-bg;
}
}
}
}
// 弹出层 - 核销二维码
.qrcode-popup {
width: 700rpx;
padding: 36rpx 30rpx;
.btn-box-off-deliver {
display: flex;
justify-content: space-between;
margin-top: 10rpx !important;
}
.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;
}
}
.pop-content2 {
min-height: 120rpx;
padding: 0 10rpx;
display: flex;
flex-direction: column;
background-color: #f3f3f3;
.image {
display: block;
width: 510rpx;
height: 510rpx;
}
}
}
</style>

View File

@@ -0,0 +1,460 @@
<template>
<view v-if="!isLoading" class="container" :style="appThemeStyle">
<!-- 订单信息 -->
<view class="order-info">
<!-- 支付剩余时间 -->
<view class="order-countdown">
<text class="m-r-6">剩余时间</text>
<count-down :date="order.expirationTime" separator="zh" theme="text" />
</view>
<!-- 付款金额 -->
<view class="order-amount">
<text class="unit"></text>
<text class="amount">{{ order.pay_price }}</text>
</view>
</view>
<!-- 支付方式 -->
<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 v-if="item.method === PayMethodEnum.BALANCE.value" class="user-balance">
<text>(可用{{ personal.balance }})</text>
</view>
</view>
<view class="item-right col-m" v-if="curPaymentItem && curPaymentItem.method == item.method">
<text class="iconfont icon-check"></text>
</view>
</view>
</view>
<!-- 确认按钮 -->
<view class="footer-fixed">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">确认支付</view>
</view>
</view>
<!-- 支付确认弹窗 -->
<!-- #ifdef H5 -->
<u-modal v-if="tempUnifyData" v-model="showConfirmModal" title="支付确认" show-cancel-button confirm-text="已完成支付"
:confirm-color="appTheme.mainBg" negative-top="100" :asyncClose="true"
@confirm="onTradeQuery(tempUnifyData.outTradeNo, tempUnifyData.method)">
<view class="modal-content">
<text>请在{{ PayMethodClientNameEnum[tempUnifyData.method] }}内完成支付如果您已经支付成功请点击已完成支付按钮</text>
</view>
</u-modal>
<!-- #endif -->
</view>
</template>
<script>
import storage from '@/utils/storage'
import { inArray, urlEncode } from '@/utils/util'
import { Alipay, Wechat } from '@/core/payment'
import CountDown from '@/components/countdown'
import { PayMethodEnum } from '@/common/enum/payment'
import { PayStatusEnum } from '@/common/enum/order'
import * as Api from '@/api/solution/cashier.js'
// 支付方式对应的图标
const PayMethodIconEnum = {
[PayMethodEnum.WECHAT.value]: 'icon-wechat-pay',
[PayMethodEnum.ALIPAY.value]: 'icon-alipay',
[PayMethodEnum.BALANCE.value]: 'icon-balance-pay'
}
// 支付方式的终端名称
const PayMethodClientNameEnum = {
[PayMethodEnum.WECHAT.value]: '微信',
[PayMethodEnum.ALIPAY.value]: '支付宝'
}
export default {
components: {
CountDown
},
data() {
return {
// 加载中
isLoading: true,
// 确认按钮禁用
disabled: false,
// 枚举类
PayMethodEnum,
PayMethodIconEnum,
PayMethodClientNameEnum,
// 当前选中的支付方式
curPaymentItem: null,
// 当前订单ID
orderId: null,
// 当前结算订单信息
order: {},
// 个人信息
personal: { balance: '0.00' },
// 当前客户端的支付方式列表后端根据platform判断
methods: [],
// 支付确认弹窗
showConfirmModal: false,
// #ifdef H5
// 当前微信支付信息 (临时数据, 仅用于H5端)
tempUnifyData: { outTradeNo: '', method: '' },
// #endif
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ orderId }) {
// 记录订单ID
this.orderId = Number(orderId)
// 获取收银台信息
this.getCashierInfo()
},
methods: {
// 获取收银台信息
getCashierInfo() {
const app = this
app.isLoading = true
Api.orderInfo()
.then(result => {
app.order = result.data.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]
},
// 判断当前页面来源于浏览器返回
// #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
// 判断是否选择了支付方式
if (!app.curPaymentItem) {
app.$toast('您还没有选择支付方式')
return
}
// 按钮禁用
if (app.disabled) return
app.disabled = true
// 提交到后端API
Api.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 paymentData = result.data.payment
// 余额支付
if (method === PayMethodEnum.BALANCE.value) {
app.onShowSuccess(result)
}
// 发起支付宝支付
if (method === PayMethodEnum.ALIPAY.value) {
console.log('paymentData', paymentData)
Alipay.payment(paymentData)
.then(res => app.onPaySuccess(res))
.catch(err => app.onPayFail(err))
}
// 发起微信支付
if (method === PayMethodEnum.WECHAT.value) {
console.log('paymentData', paymentData)
Wechat.payment({ orderKey: app.orderId, ...paymentData })
.then(res => app.onPaySuccess(res))
.catch(err => app.onPayFail(err))
}
},
// 订单支付成功的回调方法
// 这里只是前端支付api返回结果success,实际订单是否支付成功 以后端的查单和异步通知为准
onPaySuccess({ res, option: { isRequireQuery, outTradeNo, method } }) {
const app = this
// 判断是否需要主动查单
// isRequireQuery为true代表需要主动查单
if (isRequireQuery) {
app.onTradeQuery(outTradeNo, method)
return true
}
this.onShowSuccess(res)
},
// 显示支付成功信息并页面跳转
onShowSuccess({ message }) {
this.$toast(message || '订单支付成功')
this.onSuccessNav()
},
// 订单支付失败
onPayFail(err) {
console.log('onPayFail', err)
const errMsg = err.message || '订单未支付'
this.$error(errMsg)
},
// 已完成支付按钮事件: 请求后端查单
onTradeQuery(outTradeNo, method) {
const app = this
// 交易查询
// 查询第三方支付订单是否付款成功
Api.tradeQuery({ outTradeNo, method, client: app.platform })
.then(result => result.data.isPay ? app.onShowSuccess(result) : app.onPayFail(result))
.finally(() => app.showConfirmModal = false)
},
// 支付成功后的跳转
onSuccessNav() {
const pages = getCurrentPages()
const lastPage = pages.length < 2 ? null : pages[pages.length - 2]
const backRoutes = [
// 'package/equipment/index',
'package/equipment/detail'
]
if (lastPage && inArray(lastPage.route, backRoutes)) {
setTimeout(() => {
uni.navigateBack()
}, 1000)
} else {
setTimeout(() => {
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: #111111;
.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);
// 禁用按钮
&.disabled {
opacity: 0.6;
}
}
}
</style>

View File

@@ -0,0 +1,718 @@
<template>
<view v-if="!isLoading" class="container" :style="appThemeStyle">
<!-- 订单信息 -->
<view class="order-info">
<!-- 支付剩余时间 -->
<view class="order-countdown">
<text class="m-r-6">剩余时间</text>
<count-down :date="order.expirationTime" separator="zh" theme="text" />
</view>
<!-- 付款金额 -->
<view class="order-amount">
<text class="unit"></text>
<text class="amount">{{ 300 + Number(order.pay_price) }}</text>
</view>
</view>
<view class="checkout-item">
<view class="head-info">
<view class="title">订单信息</view>
</view>
<view class="line"></view>
<view class="goods-info">
<image class="image" src="../../../static/order/status/wait_deliver.png" mode="widthFix"></image>
<view class="goods-info-box">
<view class="title">云芯威72v50AH锂电池</view>
<view class="desc">归属门店白沙店</view>
<view class="desc">租赁时长3个月</view>
</view>
</view>
<view class="line"></view>
<view class="contacts">
<view class="bms-info">
<view class="item">
<text>电池租金</text>
<text>300</text>
</view>
<view class="item">
<text>电池押金</text>
<text>300</text>
</view>
<view class="item">
<text>电池保险</text>
<text>30</text>
</view>
<view class="item">
<text>合计</text>
<text class="totao-price">{{ 300 + Number(order.pay_price) }}</text>
</view>
</view>
</view>
<view class="line"></view>
<view class="form-license dis-flex flex-x-center flex-y-center">
<view class="license-radio dis-flex flex-y-center" @click="setIsRead">
<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(0)" class="f-28 c-violet f-blue">{{ xieyiText[0] }}</text>
<text class="f-28 c-violet"></text>
<text @click="handleShowLicense(1)" class="f-28 c-violet f-blue">{{ xieyiText[1] }}</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="[PayMethodIconEnum[item.method]]"></text>
</view>
<view class="item-left_text">
<text>{{ PayMethodEnum[item.method].name }}</text>
</view>
<view v-if="item.method === PayMethodEnum.BALANCE.value" class="user-balance">
<text>(可用{{ personal.balance }})</text>
</view>
</view>
<view class="item-right col-m" v-if="curPaymentItem && curPaymentItem.method == item.method">
<text class="iconfont icon-check"></text>
</view>
</view>
</view>
<!-- 确认按钮 -->
<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 -->
<!-- 入驻协议弹窗 -->
<u-modal v-model="showLicense" :title="xieyiText[showConfirmIndex]">
<scroll-view class="pops-content" :scroll-y="true">
<text v-if="showConfirmIndex == 0">{{ license }}</text>
<text v-if="showConfirmIndex == 1">{{ license1 }}</text>
</scroll-view>
</u-modal>
</view>
</template>
<script>
import storage from '@/utils/storage'
import {
inArray,
urlEncode
} from '@/utils/util'
import {
Alipay,
Wechat
} from '@/core/payment'
import CountDown from '@/components/countdown'
import {
PayMethodEnum
} from '@/common/enum/payment'
import {
PayStatusEnum
} from '@/common/enum/order'
import * as Api from '@/api/solution/cashier.js'
import SettingModel from '@/common/model/dealer/Setting'
// 支付方式对应的图标
const PayMethodIconEnum = {
[PayMethodEnum.WECHAT.value]: 'icon-wechat-pay',
[PayMethodEnum.ALIPAY.value]: 'icon-alipay',
[PayMethodEnum.BALANCE.value]: 'icon-balance-pay'
}
// 支付方式的终端名称
const PayMethodClientNameEnum = {
[PayMethodEnum.WECHAT.value]: '微信',
[PayMethodEnum.ALIPAY.value]: '支付宝'
}
export default {
components: {
CountDown
},
data() {
return {
// 加载中
isLoading: true,
// 确认按钮禁用
disabled: true,
// 枚举类
PayMethodEnum,
PayMethodIconEnum,
PayMethodClientNameEnum,
// 当前选中的支付方式
curPaymentItem: null,
// 当前订单ID
orderId: null,
// 当前结算订单信息
order: {},
// 显示入驻协议弹窗
showLicense: false,
// 个人信息
personal: {
balance: '0.00'
},
// 入驻协议阅读状态
isRead: false,
// 当前客户端的支付方式列表后端根据platform判断
methods: [],
setting: {},
// 文字设置
words: undefined,
// 显示入驻协议弹窗
// 租赁服务协议
license: undefined,
// 隐私协议
license1: undefined,
// 支付确认弹窗
showConfirmModal: false,
showConfirmIndex: 0,
xieyiText: ["《租赁协议》", "《隐私协议》"],
// #ifdef H5
// 当前微信支付信息 (临时数据, 仅用于H5端)
tempUnifyData: {
outTradeNo: '',
method: ''
},
// #endif
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({
orderId
}) {
// 记录订单ID
this.orderId = Number(orderId)
// 获取收银台信息
this.getCashierInfo()
// 获取设置信息
this.getSetting()
},
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.license1 = setting.license.license1
// 设置当前页面标题
app.setPageTitle(setting.words.apply.title)
})
},
// 获取收银台信息
getCashierInfo() {
const app = this
app.isLoading = true
Api.orderInfo()
.then(result => {
app.order = result.data.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()
}
},
// 显示入驻协议弹窗
handleShowLicense(index) {
this.showLicense = true
this.showConfirmIndex = index
},
// 选择支付方式
handleSelectPayType(index) {
this.curPaymentItem = this.methods[index]
},
// 判断当前页面来源于浏览器返回
// #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
// 判断是否选择了支付方式
if (!app.curPaymentItem) {
app.$toast('您还没有同意租赁协议或选择支付方式')
return
}
// 按钮禁用
if (app.disabled) return
app.disabled = true
// 提交到后端API
Api.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 paymentData = result.data.payment
// 余额支付
if (method === PayMethodEnum.BALANCE.value) {
app.onShowSuccess(result)
}
// 发起支付宝支付
if (method === PayMethodEnum.ALIPAY.value) {
console.log('paymentData', paymentData)
Alipay.payment(paymentData)
.then(res => app.onPaySuccess(res))
.catch(err => app.onPayFail(err))
}
// 发起微信支付
if (method === PayMethodEnum.WECHAT.value) {
console.log('paymentData', paymentData)
Wechat.payment({
orderKey: app.orderId,
...paymentData
})
.then(res => app.onPaySuccess(res))
.catch(err => app.onPayFail(err))
}
},
// 订单支付成功的回调方法
// 这里只是前端支付api返回结果success,实际订单是否支付成功 以后端的查单和异步通知为准
onPaySuccess({
res,
option: {
isRequireQuery,
outTradeNo,
method
}
}) {
const app = this
// 判断是否需要主动查单
// isRequireQuery为true代表需要主动查单
if (isRequireQuery) {
app.onTradeQuery(outTradeNo, method)
return true
}
this.onShowSuccess(res)
},
// 显示支付成功信息并页面跳转
onShowSuccess({
message
}) {
this.$toast(message || '订单支付成功')
this.onSuccessNav()
},
setIsRead(){
this.isRead = true
this.disabled = false
},
// 订单支付失败
onPayFail(err) {
console.log('onPayFail', err)
const errMsg = err.message || '订单未支付'
this.$error(errMsg)
},
// 已完成支付按钮事件: 请求后端查单
onTradeQuery(outTradeNo, method) {
const app = this
// 交易查询
// 查询第三方支付订单是否付款成功
Api.tradeQuery({
outTradeNo,
method,
client: app.platform
})
.then(result => result.data.isPay ? app.onShowSuccess(result) : app.onPayFail(result))
.finally(() => app.showConfirmModal = false)
},
// 支付成功后的跳转
onSuccessNav() {
const pages = getCurrentPages()
const lastPage = pages.length < 2 ? null : pages[pages.length - 2]
const backRoutes = [
// 'package/equipment/index',
'package/equipment/detail'
]
if (lastPage && inArray(lastPage.route, backRoutes)) {
setTimeout(() => {
uni.navigateBack()
}, 1000)
} else {
setTimeout(() => {
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: #111111;
.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);
// 禁用按钮
&.disabled {
opacity: 0.6;
}
}
}
// 项目内容
.checkout-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;
}
.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;
}
}
.line {
margin: 20rpx 0;
border-bottom: 1rpx solid #f3f3f3;
}
.goods-info {
display: flex;
justify-content: flex-start;
align-items: center;
.image {
width: 90rpx;
height: 90rpx;
padding: 10rpx;
}
.goods-info-box{
margin-left: 20rpx;
.title{
font-weight: bold;
font-size: 38rpx;
margin-bottom: 10rpx;
}
.desc{
color: #999999;
}
}
}
.contacts {
margin-bottom: 16rpx;
display: flex;
justify-content: space-between;
.bms-info {
display: flex;
flex-direction: column;
width: 96%;
.item {
margin-bottom: 10rpx;
display: flex;
justify-content: space-between;
}
.name {
margin-right: 16rpx;
}
.totao-price {
color: #ff0000;
}
}
.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;
}
}
}
}
.f-blue {
color: #3f6fc1;
}
.c-violet {
color: #786cff;
}
.col-bb {
color: #bbb;
}
</style>

View File

@@ -0,0 +1,159 @@
<template>
<!-- 设备评价 -->
<view v-if="!isLoading && list.length" class="equipment-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
equipmentId: {
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.equipmentId, 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', { equipmentId: app.equipmentId })
}
}
}
</script>
<style lang="scss" scoped>
.equipment-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>

View File

@@ -0,0 +1,158 @@
<template>
<view v-if="list.length" class="service-wrapper" :style="appThemeStyle">
<!-- 服务简述 -->
<view class="service-simple" @click="handlePopup">
<view class="s-list">
<view class="s-item" v-for="(item, index) in list" :key="index">
<text class="item-icon iconfont icon-fuwu"></text>
<text class="item-val">{{ item.name }}</text>
</view>
</view>
<!-- 扩展箭头 -->
<view class="s-arrow f-26 col-9 t-r">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
<!-- 详情内容弹窗 -->
<u-popup v-model="showPopup" mode="bottom" :closeable="true" :border-radius="26">
<view class="service-content">
<view class="title">服务</view>
<scroll-view class="content-scroll" :scroll-y="true">
<view class="s-list clearfix">
<view class="s-item" v-for="(item, index) in list" :key="index">
<text class="item-icon iconfont icon-fuwu"></text>
<view class="item-val">{{ item.name }}</view>
<view class="item-summary">{{ item.summary }}</view>
</view>
</view>
</scroll-view>
</view>
</u-popup>
</view>
</template>
<script>
import * as ServiceApi from '@/api/equipment/service'
export default {
props: {
// 设备ID
equipmentId: {
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.equipmentId)
.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>

View File

@@ -0,0 +1,179 @@
<template>
<equipment-sku-popup :value="value" @input="onChangeValue" border-radius="20" :localdata="equipmentInfo" :mode="skuMode" :maskCloseAble="true"
:priceColor="appTheme.mainBg" :buyNowBackgroundColor="appTheme.mainBg" :addCartColor="appTheme.viceText" :addCartBackgroundColor="appTheme.viceBg"
:activedStyle="{ color: appTheme.mainBg, borderColor: appTheme.mainBg, backgroundColor: activedBtnBackgroundColor }"
@open="openSkuPopup" @close="closeSkuPopup" @add-cart="addCart" @buy-now="buyNow" buyNowText="立即租用" />
</template>
<script>
import { setCartTotalNum } from '@/core/app'
import { hex2rgba } from '@/utils/color'
import * as CartApi from '@/api/cart'
import EquipmentSkuPopup from '@/components/equipment-sku-popup'
export default {
components: {
EquipmentSkuPopup
},
model: {
prop: 'value',
event: 'input'
},
props: {
// true 组件显示 false 组件隐藏
value: {
Type: Boolean,
default: false
},
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
skuMode: {
type: Number,
default: 1
},
// 商品详情信息
equipment: {
type: Object,
default: {}
}
},
data() {
return {
equipmentInfo: {}
}
},
computed: {
// 规格按钮选中时的背景色
activedBtnBackgroundColor() {
return hex2rgba(this.appTheme.mainBg, 0.1)
}
},
created() {
const app = this
const { equipment } = app
app.equipmentInfo = {
_id: equipment.equipment_id,
name: equipment.equipment_name,
equipment_thumb: equipment.equipment_image,
sku_list: app.getSkuList(),
spec_list: app.getSpecList()
}
},
methods: {
// 监听组件显示隐藏
onChangeValue(val) {
this.$emit('input', val)
},
// 整理商品SKU列表
getSkuList() {
const app = this
const { equipment: { equipment_name, equipment_image, skuList } } = app
const skuData = []
skuList.forEach(item => {
skuData.push({
_id: item.id,
equipment_sku_id: item.equipment_sku_id,
equipment_id: item.equipment_id,
equipment_name: equipment_name,
image: item.image_url ? item.image_url : equipment_image,
price: item.equipment_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 { equipment: { specList } } = app
const res = specList[groupIndex].valueList.find(specValue => {
return specValue.spec_value_id == valueId
})
return res.spec_value
},
// 整理规格数据
getSpecList() {
const { equipment: { specList } } = this
const defaultData = [{ name: '默认', list: [{ name: '默认' }] }]
const specData = []
specList.forEach(group => {
const children = []
group.valueList.forEach(specValue => {
children.push({ name: specValue.spec_value })
})
specData.push({
name: group.spec_name,
list: children
})
})
return specData.length ? specData : defaultData
},
// sku组件 开始-----------------------------------------------------------
openSkuPopup() {
// console.log("监听 - 打开sku组件")
},
closeSkuPopup() {
// console.log("监听 - 关闭sku组件")
},
// 加入购物车按钮
addCart(selectShop) {
const app = this
const { equipment_id, equipment_sku_id, buy_num } = selectShop
CartApi.add(equipment_id, equipment_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('package/equipment/cashier/index', {
mode: 'buyNow',
equipmentId: selectShop.equipment_id,
equipmentSkuId: selectShop.equipment_sku_id,
equipmentNum: selectShop.buy_num
})
// 隐藏当前弹窗
this.onChangeValue(false)
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

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

View File

@@ -0,0 +1,69 @@
<template>
<!-- 搜索框 -->
<view class="search-wrapper">
<view class="index-search" @click="onClick">
<view class="index-cont-search t-c">
<text class="search-icon iconfont icon-search"></text>
<text class="search-text">{{ tips }}</text>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
tips: {
type: String,
default: '搜索商品'
}
},
data() {
return {}
},
methods: {
onClick() {
this.$emit('event')
}
}
}
</script>
<style lang="scss" scoped>
.search-wrapper {
background: #fff;
padding: 13rpx;
}
.index-search {
border-bottom: 0;
background: #fff;
border-radius: 50rpx;
overflow: hidden;
font-size: 28rpx;
color: #6d6d6d;
box-sizing: border-box;
line-height: 64rpx;
display: flex;
align-items: center;
.index-cont-search {
width: 100%;
font-size: 28rpx;
background: #f7f7f7;
display: flex;
justify-content: center;
align-items: center;
}
.index-cont-search .search-icon {
font-size: 28rpx;
}
.index-cont-search .search-text {
margin-left: 14rpx;
}
}
</style>

229
package/equipment/detail.scss Executable file
View File

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

348
package/equipment/detail.vue Executable file
View File

@@ -0,0 +1,348 @@
<template>
<view v-show="!isLoading" class="container" :style="appThemeStyle">
<!-- 设备图片轮播 -->
<SlideImage v-if="!isLoading" :video="equipment.video" :videoCover="equipment.videoCover" :images="equipment.equipment_images" />
<!-- 设备信息 -->
<view v-if="!isLoading" class="equipment-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">{{ equipment.equipment_price_min }}</text>
<!-- 会员价标签 -->
<view v-if="equipment.is_user_grade" class="user-grade">
<text>会员价</text>
</view>
<!-- 划线价 -->
<text v-if="equipment.line_price_min > 0" class="original-price">{{ equipment.line_price_min }}</text>
</view>
<view class="block-right dis-flex">
<!-- 销量 -->
<view class="equipment-sales">
<text>已售{{ equipment.equipment_sales }}</text>
</view>
</view>
</view>
<!-- 标题分享 -->
<view class="info-item info-item__name dis-flex flex-y-center">
<view class="equipment-name flex-box">
<text class="twoline-hide">{{ equipment.equipment_name }}</text>
</view>
<view class="equipment-share__line"></view>
<view class="equipment-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="equipment.selling_point" class="info-item info-item_selling-point">
<text>{{ equipment.selling_point }}</text>
</view>
</view>
<!-- 选择设备规格 -->
<view v-if="equipment.spec_type == 20" class="equipment-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 equipment.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" :equipment-id="equipmentId" />
<!-- 设备SKU弹窗 -->
<SkuPopup v-if="!isLoading" v-model="showSkuPopup" :skuMode="skuMode" :equipment="equipment" @addCart="onAddCart" />
<!-- 设备评价 -->
<Comment v-if="!isLoading" :equipment-id="equipmentId" :limit="2" />
<!-- 设备描述 -->
<view v-if="!isLoading" class="equipment-content m-top20">
<view class="item-title b-f">
<text>设备描述</text>
</view>
<block v-if="equipment.content != ''">
<view class="equipment-content__detail b-f">
<mp-html :content="equipment.content" />
</view>
</block>
<empty v-else tips="亲,暂无设备描述" />
</view>
<!-- 设备推荐 -->
<recommended />
<!-- 底部选项卡 -->
<view class="footer-fixed">
<view class="footer-container">
<!-- 导航图标 -->
<view class="foo-item-fast">
<!-- 首页 -->
<!-- <view class="fast-item fast-item--home" @click="onTargetHome">
<view class="fast-icon">
<text class="iconfont icon-shouye"></text>
</view>
<view class="fast-text">
<text>站点</text>
</view>
</view> -->
<!-- #ifdef MP-WEIXIN -->
<button class="btn-normal" @click="onCollection(equipment.equipment_id)">
<view class="fast-item">
<view class="fast-icon">
<u-icon name="star-fill" color="#fd8008" v-if="equipment.is_collection"></u-icon>
<u-icon name="star" v-else></u-icon>
</view>
<view class="fast-text">
<text>收藏</text>
</view>
</view>
</button>
<!-- #endif -->
<!-- 客服 (仅微信小程序端显示) -->
<!-- #ifdef MP-WEIXIN -->
<!-- <button class="btn-normal" open-type="contact">
<view class="fast-item">
<view class="fast-icon">
<text class="iconfont icon-kefu1"></text>
</view>
<view class="fast-text">
<text>客服</text>
</view>
</view>
</button> -->
<!-- #endif -->
<!-- 购物车 -->
<!-- <view class="fast-item fast-item--cart" @click="onTargetCart">
<view v-if="cartTotal > 0" class="fast-badge fast-badge--fixed">{{ cartTotal > 99 ? '99+' : cartTotal }}
</view>
<view class="fast-icon">
<text class="iconfont icon-gouwuche"></text>
</view>
<view class="fast-text">
<text>购物车</text>
</view>
</view> -->
</view>
<!-- 操作按钮 -->
<view class="foo-item-btn">
<view class="btn-wrapper">
<!-- <view class="btn-item btn-item-deputy" @click="onShowSkuPopup(2)">
<text>加入购物车</text>
</view> -->
<view class="btn-item btn-item-main" @click="onShowSkuPopup(3)">
<text>立即租用</text>
</view>
</view>
</view>
</view>
</view>
<!-- 快捷导航 -->
<!-- <shortcut bottom="120rpx" /> -->
<!-- 分享菜单 -->
<share-sheet v-model="showShareSheet" :shareTitle="equipment.equipment_name" :shareImageUrl="equipment.equipment_image"
:posterApiCall="posterApiCall" :posterApiParam="{ equipmentId }" />
</view>
</template>
<script>
import { getSceneData } from '@/core/app'
import * as EquipmentApi from '@/api/equipment'
import * as CartApi from '@/api/cart'
import Recommended from '@/components/recommended'
import ShareSheet from '@/components/share-sheet'
import SlideImage from './components/SlideImage'
import SkuPopup from './components/SkuPopup'
import Comment from './components/Comment'
import Service from './components/Service'
export default {
components: {
Recommended,
ShareSheet,
SlideImage,
SkuPopup,
Comment,
Service
},
data() {
return {
// 正在加载
isLoading: true,
// 当前设备ID
equipmentId: null,
// 设备详情
equipment: {},
// 购物车总数量
cartTotal: 0,
// 显示/隐藏SKU弹窗
showSkuPopup: false,
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
skuMode: 1,
// 显示/隐藏分享菜单
showShareSheet: false,
// 获取设备海报图api方法
posterApiCall: EquipmentApi.poster
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 记录query参数
this.onRecordQuery(options)
// 加载页面数据
this.onRefreshPage()
// 浏览历史
this.saveFootMarkList(options.equipmentId)
},
methods: {
// 浏览历史记录
saveFootMarkList(equipmentId){
const userId = uni.getStorageSync('userId')
const arr = uni.getStorageSync(`FOOTMARKLIST_${userId}`) || []
arr.unshift(Number(equipmentId))
return uni.setStorageSync(`FOOTMARKLIST_${userId}`,arr)
},
// 记录query参数
onRecordQuery(query) {
const scene = getSceneData(query)
this.equipmentId = query.equipmentId ? parseInt(query.equipmentId) : parseInt(scene.gid)
},
// 刷新页面数据
onRefreshPage() {
const app = this
app.isLoading = true
Promise.all([app.getEquipmentDetail(), app.getCartTotal()])
.finally(() => app.isLoading = false)
},
// 获取设备信息
getEquipmentDetail() {
const app = this
return new Promise((resolve, reject) => {
EquipmentApi.detail(app.equipmentId)
.then(result => {
app.equipment = result.data.detail
resolve(result)
})
.catch(reject)
})
},
// 获取购物车总数量
getCartTotal() {
const app = this
return new Promise((resolve, reject) => {
CartApi.total()
.then(result => {
app.cartTotal = result.data.cartTotal
resolve(result)
})
.catch(reject)
})
},
// 更新购物车数量
onAddCart(total) {
this.cartTotal = total
},
/**
* 显示/隐藏SKU弹窗
* @param {skuMode} 模式 1:都显示 2:只显示购物车 3:只显示立即购买
*/
onShowSkuPopup(skuMode = 1) {
const shopInfo = uni.getStorageSync('shopInfo')
const clerkInfo = uni.getStorageSync('clerkInfo')
if(shopInfo || clerkInfo){
return this.$error('请使用会员账号购买')
}
this.skuMode = skuMode
this.showSkuPopup = !this.showSkuPopup
},
// 显示隐藏分享菜单
onShowShareSheet() {
this.showShareSheet = !this.showShareSheet
},
// 跳转到首页
onTargetHome(e) {
const { equipment } = this
console.log("this.equipment: ",equipment);
this.$navTo('package/shops/detail/index',equipment)
},
// 加入收藏
onCollection(equipmentId){
EquipmentApi.collection({equipmentId})
this.equipment.is_collection = !this.equipment.is_collection
},
// 跳转到购物车页
onTargetCart() {
this.$navTo('pages/cart/index')
},
},
/**
* 分享当前页面
*/
onShareAppMessage() {
const app = this
// 构建页面参数
const params = app.$getShareUrlParams({
equipmentId: app.equipmentId,
})
return {
title: app.equipment.equipment_name,
path: `/pages/equipment/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({
equipmentId: app.equipmentId,
})
return {
title: app.equipment.equipment_name,
path: `/pages/equipment/detail?${params}`
}
}
}
</script>
<style>
page {
background: #fafafa;
}
</style>
<style lang="scss" scoped>
@import "./detail.scss";
</style>

465
package/equipment/list.vue Executable file
View File

@@ -0,0 +1,465 @@
<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>
<!-- 商品销量 -->
<text class="col-9 f-24 u-text-right">已售{{ item.goods_sales }}</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 * as GoodsApi from '@/api/goods'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
import Search from './components/search.vue'
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>

278
package/follow/index.vue Normal file
View File

@@ -0,0 +1,278 @@
<template>
<view>
<!-- 门店列表 -->
<view class="shop-list">
<u-card v-for="(item, index) in shopList" :key="index" @click="onSelectedShop(item)"
class="shop-item dis-flex flex-y-center" :border="false" :box-shadow="true" :show-head="false"
:head-border-bottom="false">
<view class="" slot="body">
<view class="shop-item__content flex-box">
<view class="u-body-item u-flex u-col-between u-p-t-0">
<u-avatar :src="item.logoUrl" :size="120" mode="square" :show-level="true" ></u-avatar>
<view class="shop-info">
<view class="title">{{item.shopName}}</view>
<view class="about">联系电话{{item.phone}}</view>
<view class="address">地址{{ item.region.province }}{{ item.region.city }}{{ item.region.region }}{{ item.address }}</view>
<view v-if="item.distance" class="shop-item__distance">
<text class="iconfont icon-dingwei"></text>
<text class="f-24">{{ item.distanceUnit }}</text>
</view>
</view>
</view>
</view>
<!-- 选中状态 -->
<!-- <view v-if="item.shop_id == selectedId" class="shop-item__right">
<text class="iconfont icon-check1"></text>
</view> -->
</view>
</u-card>
</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 class="shop-list">
<u-card v-for="(item,index) in list" :key="index" :border="false" :box-shadow="true" :show-head="false"
:head-border-bottom="false">
<view class="" slot="body">
<view class="u-body-item u-flex u-col-between u-p-t-0">
<image class="shop-avatar"
:src="item.logo_url"
mode="widthFix"></image>
<view class="shop-info">
<view class="title">{{item.shop_name}}</view>
<view class="about">{{item.summary}}</view>
</view>
</view>
</view>
</u-card>
</view> -->
</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'
export default {
components: {
Search,
Empty
},
data() {
return {
title: '国内馆',
// 正在加载中
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,
latitude,
follow: true
})
.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(item) {
this.$navTo('package/shops/detail/index',item)
},
changeCity() {
this.$toast('切换区域')
}
}
}
</script>
<style lang="scss" scoped>
.bg {
background-color: #F5F5F8
}
.shop-list {
.shop-item {
}
}
.header-box {
padding: 20rpx 36rpx 0 36rpx;
display: flex;
justify-content: space-between;
.location {
display: flex;
justify-content: center;
align-items: center;
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;
}
}
.u-body-item {
align-items: stretch !important
}
</style>

View File

@@ -0,0 +1,402 @@
<template>
<view class="container" :style="appThemeStyle">
<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="item-prices oneline-hide">
<text class="goods-sales">已售{{ item.goods_sales }}</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>
<view class="buy-gouwuche">
<u-button size="mini" @click="handleAddCart(item)">取消收藏
</u-button>
</view>
</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: []
}
},
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
}
},
sortPrice: false, // 价格排序 (true高到低 false低到高)
sortType: 'hot', // 排序类型
}
},
created() {
// 设置分类列表高度
this.setListHeight()
},
methods: {
// 切换排序方式
handleSortType(newSortType) {
const app = this
const newSortPrice = newSortType === 'price' ? !app.sortPrice : true
app.sortType = newSortType
app.sortPrice = newSortPrice
// 刷新列表数据
app.getGoodsList()
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
// 设置列表数据
console.log(page.num);
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,
collection: true
}, {
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) {
GoodsApi.collection({goodsId: item.})
this.$emit('addCart')
},
// 更新购物车角标
onUpdateCartTabBadge() {
console.log('onUpdateCartTabBadge')
setCartTabBadge()
},
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #F6F6F6 !important;
}
// 分类内容
.cate-content {
z-index: 1;
background-color: #F6F6F6 !important;
padding-top: 0rpx;
}
// 一级分类+二级分类 20
.cate-left {
width: 173rpx;
height: 100%;
background: #f8f8f8;
color: #444;
position: fixed;
top: calc(286rpx + 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 {
padding: 20rpx;
}
.goods-item {
padding: 28rpx 22rpx;
display: flex;
box-sizing: border-box;
padding: 20rpx;
border: 1px solid #F6F6F6;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 12rpx;
}
.goods-item_left {
position: relative;
background: #fff;
margin-right: 20rpx;
.image {
display: block;
width: 280rpx;
height: 280rpx;
}
}
.goods-item_right {
position: relative;
flex: 1;
.goods-name {
display: block;
width: 100%;
min-height: 68rpx;
font-size: 34rpx;
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: flex-end;
position: absolute;
right: 0rpx;
bottom: 0rpx;
min-height: 44rpx;
.item-prices {
padding-right: 6rpx;
.price_x {
margin-right: 14rpx;
font-weight: 600;
color: #ff0000;
font-size: 38rpx;
}
.price_y {
color: #999;
text-decoration: line-through;
font-size: 24rpx;
}
}
}
}
.goods-sales {
padding-top: 8rpx !important;
color: #b3b3b3 !important;
font-size: 24rpx;
}
.search-box {
display: flex;
justify-content: space-around;
background: #fff;
border-radius: 14rpx;
padding: 20rpx 4rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.07);
}
// 排序组件
.store-sort {
margin-top: 24rpx;
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;
}
}
}
}
</style>

398
package/footmark/index.vue Executable file
View File

@@ -0,0 +1,398 @@
<template>
<view class="container" :style="appThemeStyle">
<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="item-prices oneline-hide">
<text class="goods-sales">已售{{ item.goods_sales }}</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>
<!-- <view class="buy-gouwuche">
<u-button size="mini" @click="handleAddCart(item)">取消收藏
</u-button>
</view> -->
</view>
</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,
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],
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
}
},
sortPrice: false, // 价格排序 (true高到低 false低到高)
sortType: 'hot', // 排序类型
}
},
created() {
// 设置分类列表高度
this.setListHeight()
},
methods: {
// 切换排序方式
handleSortType(newSortType) {
const app = this
const newSortPrice = newSortType === 'price' ? !app.sortPrice : true
app.sortType = newSortType
app.sortPrice = newSortPrice
// 刷新列表数据
app.getGoodsList()
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
// 设置列表数据
console.log(page.num);
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
const userId = uni.getStorageSync('userId')
const arr = uni.getStorageSync(`FOOTMARKLIST_${userId}`) || []
console.log(arr);
return new Promise((resolve, reject) => {
GoodsApi.list({
categoryId,
page: pageNo,
footMark: true,
goodsIds: JSON.stringify(arr)
}, {
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) {
GoodsApi.collection({goodsId: item.goods_id}).then(() => {
this.getGoodsList()
})
},
// 更新购物车角标
onUpdateCartTabBadge() {
console.log('onUpdateCartTabBadge')
setCartTabBadge()
},
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #F6F6F6 !important;
}
// 分类内容
.cate-content {
z-index: 1;
background-color: #F6F6F6 !important;
padding-top: 0rpx;
}
// 一级分类+二级分类 20
.cate-left {
width: 173rpx;
height: 100%;
background: #f8f8f8;
color: #444;
position: fixed;
top: calc(286rpx + 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 {
padding: 20rpx;
}
.goods-item {
padding: 28rpx 22rpx;
display: flex;
box-sizing: border-box;
padding: 20rpx;
border: 1px solid #F6F6F6;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 12rpx;
}
.goods-item_left {
position: relative;
background: #fff;
margin-right: 20rpx;
.image {
display: block;
width: 280rpx;
height: 280rpx;
}
}
.goods-item_right {
position: relative;
flex: 1;
.goods-name {
display: block;
width: 100%;
min-height: 68rpx;
font-size: 34rpx;
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: flex-end;
position: absolute;
right: 0rpx;
bottom: 0rpx;
min-height: 44rpx;
.item-prices {
padding-right: 6rpx;
.price_x {
margin-right: 14rpx;
font-weight: 600;
color: #ff0000;
font-size: 38rpx;
}
.price_y {
color: #999;
text-decoration: line-through;
font-size: 24rpx;
}
}
}
}
.goods-sales {
padding-top: 8rpx !important;
color: #b3b3b3 !important;
font-size: 24rpx;
}
.search-box {
display: flex;
justify-content: space-around;
background: #fff;
border-radius: 14rpx;
padding: 20rpx 4rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.07);
}
// 排序组件
.store-sort {
margin-top: 24rpx;
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;
}
}
}
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<view class="partner">
<view class="banner">
<image src="https://dev.gxwebsoft.com/ylx/uploads/10001/20220815/d494a73f94f5e0a4b8562c77e5ec9ec3.jpg" class="banner" mode="widthFix"></image>
</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" :disabled="disabled" placeholder="请输入姓名" />
</u-form-item>
<u-form-item label="电话" prop="mobile">
<u-input v-model="form.mobile" maxlength="11" :disabled="disabled" placeholder="请输入联系电话" />
</u-form-item>
<u-form-item label="性别" prop="sex">
<u-radio-group v-model="form.sex" @change="groupChange">
<u-radio label="1"></u-radio>
<u-radio label="2"></u-radio>
</u-radio-group>
</u-form-item>
<u-form-item label="投资预算" prop="weixin">
<u-input type="number" v-model="form.yusuan" :disabled="disabled" placeholder="请输入投资预算" />
</u-form-item>
<u-form-item label="微信号" prop="weixin">
<u-input v-model="form.weixin" :disabled="disabled" placeholder="请输入微信号" />
</u-form-item>
<u-form-item label="意向地区" prop="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="整体需求" prop="comments">
<u-input v-model="form.comments" :disabled="disabled" placeholder="请输入整体需求" />
</u-form-item>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn">
<u-button type="primary" shape="circle">提交申请</u-button>
</view>
</view>
</view>
</template>
<script>
// 表单字段元素
const form = {
// 入驻类型 合伙人10001,配送员10002,社区门店10003,省代10004
grade_id: 10004,
company: '',
real_name: '',
phone: '',
mobile: '',
region: [],
detail: '',
}
import SelectRegion from '@/components/select-region/select-region'
export default {
components: {
SelectRegion
},
data() {
return {
form,
disabled: false,
imageList: []
}
},
methods: {
groupChange(){
}
}
}
</script>
<style lang="less">
.partner {
.banner {
width: 100%;
}
.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{
width: 500rpx;
margin: 20rpx auto;
}
}
</style>

View File

@@ -0,0 +1,275 @@
<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="#0f80ff" :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__time col-9 f-24 order-no">订单号2022092457995352</view>
<view class="detail__money f-30">
<u-avatar size="mini" mode="circle" v-if="item.sendUser" :src="item.sendUser.avatar_url">
</u-avatar>
<view class="content">
<text>{{ item.sendUser.nick_name }}</text>
<text class="detail__time col-9 f-24">消费金额268.00</text>
</view>
</view>
</view>
<view class="detail__right dis-flex flex-dir-column flex-x-center flex-y-center">
<view class="detail__status f-28">
<text>已完成</text>
<view>+ 56.00</view>
<!-- <u-tag v-if="item.status == 0" type="error" text="未结算"></u-tag>
<u-tag v-if="item.status == 1" type="success" text="已结算"></u-tag> -->
</view>
<view class="detail__time col-9 f-24">{{ item.create_time }}</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 Api from '@/api/user/notice.js'
import SettingModel from '@/common/model/dealer/Setting'
import {
ApplyStatusEnum
} from '@/common/enum/dealer/withdraw'
const pageSize = 15
// 提现状态文字
const ApplyStatusText = [0, 1]
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
// 枚举类
ApplyStatusEnum,
ApplyStatusText,
// 选项卡列表
tabList: [],
// 当前选项
curTab: 0,
// 列表数据
list: getEmptyPaginateObj(),
shopId: 0,
showReasonMsg: false,
// 上拉加载配置
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.withdraw_list
app.setTabList(words.words)
})
},
// 设置选项卡数据
setTabList(words) {
const app = this
app.tabList = [
{
value: 0,
name: '全部'
}
]
},
triggerReasonMsg() {
this.showReasonMsg = !this.showReasonMsg
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中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({
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)
},
// 设为已读
onRead(id, content) {
const app = this
uni.showModal({
title: '查看消息',
content,
showCancel: false,
success({ confirm }) {
Api.status({id}).then(res => {
app.getList()
})
}
})
},
}
}
</script>
<style>
page {
background: #fff;
}
</style>
<style lang="scss" scoped>
// 提现明细列表
.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;
}
.widget__detail .detail__money {
font-size: 30rpx;
margin-bottom: 12rpx;
display: flex;
align-items: center;
}
.widget__detail .detail__reason {
margin-top: 15rpx;
color: #0f80ff;
}
.show-reason-msg {
padding: 20rpx;
line-height: 2rem;
color: #ff0000;
}
.detail__left {
.iconfont {
margin-left: 30rpx;
font-size: 24rpx;
}
.icon-weixin {
color: #0eaf00;
}
.icon-alipay {
color: #0f80ff;
}
}
.detail__status {
position: relative;
}
.detail__money {
.content {
position: relative;
display: flex;
flex-direction: column;
padding-left: 10rpx;
}
}
.modal-content{
padding: 20rpx;
}
.order-no{
line-height: 2rem;
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<view class="partner">
<!-- 表单组件 -->
<view class="form-wrapper">
<view class="item-list">
<image :src="shopPosterUrl" mode="widthFix"></image>
<text>推广商户二维码</text>
</view>
</view>
<view class="form-wrapper">
<view class="item-list">
<image :src="userPosterUrl" mode="widthFix"></image>
<text>推广用户二维码</text>
</view>
</view>
</view>
</template>
<script>
import * as Api from '@/api/dealer/poster'
export default {
components: {},
data() {
return {
isLoading: false,
shopPosterUrl: undefined,
userPosterUrl: undefined
}
},
onLoad() {
this.getPoster()
},
methods: {
// 获取推广二维码
getPoster() {
const app = this
app.isLoading = true
Api.qrcode({ channel: app.platform })
.then(result => {
// api数据赋值
app.shopPosterUrl = result.data.shopPosterUrl
app.userPosterUrl = result.data.userPosterUrl
})
.finally(() => app.isLoading = false)
},
}
}
</script>
<style lang="less">
.partner {
.banner {
width: 100%;
}
.form-wrapper {
margin: 30rpx auto 50rpx auto;
padding: 30rpx 40rpx;
width: 94%;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
border-radius: 16rpx;
background: #fff;
.item-list{
display: flex;
flex-direction: column;
text-align: center;
image{
width: 70%;
margin: auto;
}
text{
line-height: 2rem;
font-size: 34rpx;
font-weight: bold;
}
}
}
.footer{
width: 500rpx;
margin: 20rpx auto;
}
}
</style>

View File

@@ -0,0 +1,303 @@
<template>
<view>
<!-- 展馆图标 -->
<!-- <view class="header-box">
<view class="location" @click="changeCity">
<image src="../../static/postion.svg"></image><text>南宁</text>
</view>
<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">
<button class="button" @click="onSearch" type="warn">搜索</button>
</view>
</view>
</view> -->
<!-- 门店列表 -->
<view class="shop-list">
<u-card v-for="(item, index) in shopList" :key="index" @click="onSelectedShop(item)"
class="shop-item dis-flex flex-y-center" :border="false" :box-shadow="true" :show-head="false"
:head-border-bottom="false">
<view class="" slot="body">
<view class="shop-item__content flex-box">
<view class="u-body-item u-flex u-col-between u-p-t-0">
<u-avatar :src="item.logoUrl" :size="120" mode="square" :show-level="true" ></u-avatar>
<view class="shop-info">
<view class="title">{{item.shopName}}</view>
<view class="address">站点地址{{ item.region.province }}{{ item.region.city }}{{ item.region.region }}{{ item.address }}</view>
<view class="address">获得收益<text class="first-commission-money">{{ item.firstCommissionMoney }}</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>
</view>
<!-- 选中状态 -->
<!-- <view v-if="item.shop_id == selectedId" class="shop-item__right">
<text class="iconfont icon-check1"></text>
</view> -->
</view>
</u-card>
</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 class="shop-list">
<u-card v-for="(item,index) in list" :key="index" :border="false" :box-shadow="true" :show-head="false"
:head-border-bottom="false">
<view class="" slot="body">
<view class="u-body-item u-flex u-col-between u-p-t-0">
<image class="shop-avatar"
:src="item.logo_url"
mode="widthFix"></image>
<view class="shop-info">
<view class="title">{{item.shop_name}}</view>
<view class="about">{{item.summary}}</view>
</view>
</view>
</view>
</u-card>
</view> -->
</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'
export default {
components: {
Search,
Empty
},
data() {
return {
title: '国内馆',
// 正在加载中
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,
latitude,
dealerReferee: true
})
.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(item) {
this.$navTo('package/shops/detail/index',item)
},
changeCity() {
this.$toast('切换区域')
}
}
}
</script>
<style lang="scss" scoped>
.bg {
background-color: #F5F5F8
}
.shop-list {
.shop-item {
}
}
.header-box {
padding: 20rpx 36rpx 0 36rpx;
display: flex;
justify-content: space-between;
.location {
display: flex;
justify-content: center;
align-items: center;
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;
}
}
.u-body-item {
align-items: stretch !important
}
.first-commission-money{
color: #ff0000;
}
</style>

View File

@@ -0,0 +1,321 @@
<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="equipment-list">
<view class="equipment-item--container" v-for="(item, index) in equipmentList.data" :key="index">
<view class="equipment-item" @click="onTargetEquipment(item.equipment_id)">
<!-- 设备图片 -->
<view class="equipment-item_left">
<image class="image" :src="item.equipment_image"></image>
</view>
<view class="equipment-item_right">
<!-- 设备标题 -->
<view class="equipment-name">
<text class="title">{{ item.equipment_name }}</text>
<text class="desc">可租 {{ item.stock_total }} </text>
</view>
<!-- 设备信息 -->
<view class="equipment-item_desc">
<view class="desc_footer">
<view class="item-prices oneline-hide">
<text class="price_x">¥{{ item.equipment_price_min }}/</text>
<!-- <text v-if="item.line_price_min > 0" class="price_y">¥{{ item.line_price_min }}</text> -->
</view>
<u-button size="mini" type="warning" @click="onTargetEquipment(item.equipment_id)">立即租用</u-button>
<!-- <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 EquipmentApi from '@/api/equipment'
import storage from '@/utils/storage'
const pageSize = 15
export default {
components: {
MescrollBody,
Empty,
AddCartBtn,
AddCartPopup
},
mixins: [MescrollMixin],
props: {
// 分类列表
list: {
type: Array,
default: []
},
// 分类设置
setting: {
type: Object,
default: () => {}
},
// 店铺ID
shopId:{
type: Number,
default: 10001
},
// 设备名称搜索
equipmentName:{
type: String,
default: ''
}
},
data() {
return {
// 枚举类
PageCategoryStyleEnum,
// 列表高度
scrollHeight: 0,
// 一级分类:指针
curIndex: -1,
// 设备列表
equipmentList: 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.getEquipmentList(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 页码
*/
getEquipmentList(pageNo = 1) {
const app = this
const categoryId = app.curIndex > -1 ? app.list[app.curIndex].category_id : 0
const { shopId, equipmentName } = this
return new Promise((resolve, reject) => {
EquipmentApi.list({ categoryId, page: pageNo, shopId, equipmentName, storeHouseEquipment: true }, { load: false })
.then(result => {
const newList = result.data.list
app.equipmentList.data = getMoreListData(newList, app.equipmentList, pageNo)
app.equipmentList.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.equipmentList = getEmptyPaginateObj()
setTimeout(() => this.mescroll.resetUpScroll(), 120)
},
// 跳转设备详情
onTargetEquipment(equipmentId) {
this.$navTo('package/equipment/detail', { equipmentId })
},
// 点击加入购物车
handleAddCart(item) {
this.$refs.AddCartPopup.handle(item)
this.$emit('addCart')
},
// 更新购物车角标
onUpdateCartTabBadge() {
console.log('onUpdateCartTabBadge')
setCartTabBadge()
},
}
}
</script>
<style lang="scss" scoped>
.container {
padding-left: 173rpx;
}
// 分类内容
.cate-content {
z-index: 1;
background: #fff;
padding-top: 286rpx;
}
// 一级分类+二级分类 20
.cate-left {
width: 173rpx;
height: 100%;
background: #f8f8f8;
color: #444;
position: fixed;
top: calc(286rpx + 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
}
}
// 设备列表
.equipment-list {
background: #fff;
}
.equipment-item {
padding: 28rpx 22rpx;
display: flex;
}
.equipment-item_left {
position: relative;
background: #fff;
margin-right: 20rpx;
.image {
display: block;
width: 180rpx;
height: 180rpx;
}
}
.equipment-item_right {
position: relative;
flex: 1;
.equipment-name {
display: flex;
flex-direction: column;
width: 100%;
min-height: 68rpx;
font-size: 28rpx;
line-height: 1.3;
color: #333;
.title{
font-weight: bold;
font-size: 30rpx;
}
.desc{
color: #666;
font-size: 24rpx;
}
}
}
.equipment-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: #ff0000;
font-size: 28rpx;
}
.price_y {
color: #999;
text-decoration: line-through;
font-size: 24rpx;
}
}
}
}
</style>

View File

@@ -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: 396rpx;
.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>

View File

@@ -0,0 +1,94 @@
<template>
<view class="diy-search">
<view class="inner" :class="itemStyle.searchStyle" @click="onTargetSearch">
<view class="search-input" :style="{ textAlign: itemStyle.textAlign }">
<text class="search-icon iconfont icon-search"></text>
<input v-model="searchValue" class="input" @confirm="onSearch" focus="true" placeholder="请输入关键词"
type="text"></input>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
searchValue: ''
}
},
methods: {
/**
* 搜索提交
*/
onSearch() {
const {
searchValue
} = this
if (searchValue) {
// 记录历史搜索
this.setHistory(searchValue)
// 跳转到商品列表页
this.$navTo('pages/goods/list', {
search: searchValue
})
}
},
}
}
</script>
<style lang="scss" scoped>
.diy-search {
background: #e2f2fb;
padding: 20rpx 40rpx;
font-size: 26rpx;
.inner {
width: 360rpx;
height: 60rpx;
background: #fff;
border-radius: 100rpx;
overflow: hidden;
&.radius {
border-radius: 10rpx;
}
&.round {
border-radius: 60rpx;
}
}
.search-input {
height: 60rpx;
color: #999;
padding: 0 20rpx;
display: flex;
align-items: center;
.search-icon {
margin-right: 8rpx;
}
}
.flow-delivery {
background-color: #2C71C7;
padding: 10rpx 26rpx;
color: #FFFFFF;
display: flex;
justify-content: space-between;
}
.col-title {
color: #FFFFFF;
}
.my-order {
border: 1px solid #fff;
border-radius: 20rpx;
padding: 0rpx 20rpx;
}
}
</style>

View File

@@ -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>

View File

@@ -0,0 +1,177 @@
<template>
<view>
<block v-if="shop.shopName">
<!-- 搜索框 -->
<view class="shop-banner" v-if="shop.backgroundUrl" :style="'background-image: url('+ shop.backgroundUrl +');background-size: 100%;'">
<view class="shop-logo">
<u-avatar :src="shop.logoUrl" :size="120" mode="square" :show-level="true"></u-avatar>
</view>
<view class="shop-info">
<view class="title">{{shop.shopName}}
<u-icon name="star-fill" color="#fd8008" v-if="shop.follow" @click="onFollow(shop.shopId)"></u-icon>
<u-icon name="star" v-else @click="onFollow(shop.shopId)"></u-icon>
</view>
<!-- <view class="about">联系电话{{ shop.phone }}</view> -->
<view class="address">地址{{ shop.fullAddress }}</view>
</view>
</view>
<view class="shop-banner" v-else>
<view class="shop-logo">
<u-avatar :src="shop.logoUrl" :size="120" mode="square" :show-level="true"></u-avatar>
</view>
<view class="shop-info">
<view class="title">{{shop.shopName}} 关注</view>
<!-- <view class="about">联系电话{{ shop.phone }}</view> -->
<view class="address">地址{{ shop.fullAddress }}</view>
</view>
</view>
<view class="diy-search">
<view class="inner">
<view class="search-input">
<text class="search-icon iconfont icon-search"></text>
<input v-model="searchValue" class="input" @confirm="onSearch" focus="true" placeholder="请输入关键词"
type="text"></input>
</view>
</view>
</view>
</block>
</view>
</template>
<script>
export default {
props: {
tips: {
type: String,
default: '搜索商品'
},
shop: {
type: Object,
default: () => {}
}
},
data() {
return {
searchValue: ''
}
},
methods: {
onClick() {
this.$emit('event')
},
onFollow(shopId){
this.$emit('onFollow',shopId)
},
/**
* 搜索提交
*/
onSearch() {
const {
searchValue
} = this
if (searchValue) {
this.$emit('event', searchValue)
}
},
}
}
</script>
<style lang="scss" scoped>
.shop-banner {
background-color: #2C71C7;
height: 200rpx;
position: fixed;
top: var(--window-top);
left: var(--window-left);
right: var(--window-right);
z-index: 9;
display: flex;
align-items: center;
.shop-logo {
height: 100%;
display: flex;
align-items: center;
padding-left: 24rpx;
}
.shop-logo image {
width: 140rpx;
height: 140rpx;
background-color: #FFFFFF;
}
.shop-info {
width: 100%;
margin-left: 20rpx;
height: 140rpx;
z-index: 10;
color: #FFFFFF;
.title {
font-size: 38rpx;
font-weight: bold;
padding-bottom: 10rpx;
}
}
}
.diy-search {
width: 750rpx;
background: #e2f2fb;
padding: 20rpx 40rpx;
font-size: 26rpx;
position: fixed;
z-index: 10;
top: 200rpx;
left: var(--window-left);
right: var(--window-right);
.inner {
width: 360rpx;
height: 60rpx;
background: #fff;
border-radius: 100rpx;
overflow: hidden;
&.radius {
border-radius: 10rpx;
}
&.round {
border-radius: 60rpx;
}
}
.search-input {
height: 60rpx;
color: #999;
padding: 0 20rpx;
display: flex;
align-items: center;
.search-icon {
margin-right: 8rpx;
}
}
.flow-delivery {
background-color: #2C71C7;
padding: 10rpx 26rpx;
color: #FFFFFF;
display: flex;
justify-content: space-between;
}
.col-title {
color: #FFFFFF;
}
.my-order {
border: 1px solid #fff;
border-radius: 20rpx;
padding: 0rpx 20rpx;
}
}
</style>

228
package/shops/detail/index.scss Executable file
View File

@@ -0,0 +1,228 @@
.container {
// 设置ios刘海屏底部横线安全区域
// 110 - 18 + 4
padding-bottom: calc(constant(safe-area-inset-bottom) + 106rpx + 6rpx);
padding-bottom: calc(env(safe-area-inset-bottom) + 106rpx + 6rpx);
}
// 商品信息
.goods-info {
background: #fff;
padding: 25rpx 30rpx;
}
.info-item__top {
min-height: 40rpx;
margin-bottom: 20rpx;
line-height: 1;
}
.floor-price__samll {
font-size: 26rpx;
line-height: 1;
color: #ff0000;
margin-bottom: -10rpx;
}
// 商品价
.floor-price {
color: #ff0000;
margin-right: 15rpx;
font-size: 42rpx;
}
.original-price {
font-size: 26rpx;
text-decoration: line-through;
color: #959595;
margin-right: 15rpx;
margin-bottom: -6rpx;
}
// 会员价标签
.user-grade {
background: #3c3c3c;
border-radius: 6rpx;
padding: 8rpx 14rpx;
margin-right: 15rpx;
font-size: 24rpx;
color: #EEE0C3;
}
.goods-sales {
font-size: 24rpx;
color: #959595;
}
.info-item__name .goods-name {
font-size: 30rpx;
}
/* 商品分享 */
.goods-share__line {
border-left: 1rpx solid #f4f4f4;
height: 60rpx;
margin: 0 30rpx;
}
.goods-share .share-btn {
line-height: normal;
padding: 0;
background: none;
border-radius: 0;
box-shadow: none;
font-size: 8pt;
border: none;
color: #191919;
}
.goods-share .share-btn::after {
border: none;
}
.goods-share .share__icon {
font-size: 40rpx;
margin-bottom: 5rpx;
}
// 商品卖点
.info-item_selling-point {
margin-top: 8rpx;
font-size: 24rpx;
color: #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);
}
// 购物车按钮
.btn-item-deputy {
background: linear-gradient(to right, $vice-bg, $vice-bg2);
color: $vice-text;
}
}

316
package/shops/detail/index.vue Executable file
View File

@@ -0,0 +1,316 @@
<template>
<view class="container" :style="appThemeStyle">
<!-- 店招 -->
<shop-banner :shop="shop" @onFollow="onFollow" @event="onSearch" />
<!-- 一级分类 -->
<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" :equipmentName="searchValue" :list="list" :shopId="shopId" :setting="setting" @addCart="onRefreshPage"/>
<!-- 底部选项卡 -->
<view class="footer-fixed" style="display: none;">
<view class="footer-container">
<!-- 导航图标 -->
<view class="foo-item-fast">
<!-- 首页 -->
<view class="fast-item fast-item--home" @click="onTargetHome">
<view class="fast-icon">
<text class="iconfont icon-shouye"></text>
</view>
<view class="fast-text">
<text>首页</text>
</view>
</view>
<!-- 客服 (仅微信小程序端显示) -->
<!-- #ifdef MP-WEIXIN -->
<button class="btn-normal" open-type="contact">
<view class="fast-item">
<view class="fast-icon">
<text class="iconfont icon-kefu1"></text>
</view>
<view class="fast-text">
<text>客服</text>
</view>
</view>
</button>
<!-- #endif -->
<!-- 购物车 -->
<view class="fast-item fast-item--cart" @click="onTargetCart">
<view v-if="cartTotal > 0" class="fast-badge fast-badge--fixed">{{ cartTotal > 99 ? '99+' : cartTotal }}
</view>
<view class="fast-icon">
<text class="iconfont icon-gouwuche"></text>
</view>
<view class="fast-text">
<text>购物车</text>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="foo-item-btn">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" @click="onTargetCart(3)">
<text>去结算</text>
</view>
</view>
</view>
</view>
</view>
</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 * as EquipmentApi from '@/api/equipment'
import * as CartApi from '@/api/cart'
import * as ShopApi from '@/api/shop.js'
import Empty from '@/components/empty'
import Search from './components/search'
import ShopBanner from './components/shop-banner'
import Primary from './components/primary'
import Secondary from './components/secondary'
import Commodity from './components/commodity'
import storage from '@/utils/storage'
// 最后一次刷新时间
let lastRefreshTime;
export default {
components: {
Search,
Empty,
Primary,
Secondary,
Commodity,
ShopBanner
},
mixins: [MescrollCompMixin],
data() {
return {
// 店铺信息
shop: {},
// 店铺ID
shopId: null,
// 枚举类
PageCategoryStyleEnum,
// 分类列表
list: [],
// 分类模板设置
setting: {},
// 正在加载中
isLoading: true,
// 购物车总数量
cartTotal: 0,
// 显示/隐藏SKU弹窗
showSkuPopup: false,
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
skuMode: 1,
// 显示/隐藏分享菜单
showShareSheet: false,
// 获取商品海报图api方法
posterApiCall: EquipmentApi.poster,
// 搜索关键词
searchValue: '',
// 刷新组件
isRefresh: true
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ shopId }) {
// 店铺ID
this.shopId = parseInt(shopId),
// 加载页面数据
this.onRefreshPage()
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 每间隔5分钟自动刷新一次页面数据
const curTime = new Date().getTime()
if ((curTime - lastRefreshTime) > 5 * 60 * 1000) {
this.onRefreshPage()
}
},
watch: {
searchValue(val){
this.searchValue = val
this.$navTo('pages/search/index')
}
},
methods: {
// 刷新页面
onRefreshPage() {
// 记录刷新时间
lastRefreshTime = new Date().getTime()
// 获取页面数据
this.getPageData()
// 更新购物车角标
setCartTabBadge()
},
// 关注
onFollow(shopId){
ShopApi.setFollow({shopId}).then(res => {
this.getPageData()
})
},
// 获取页面数据
getPageData() {
const app = this
const equipmentName = storage.get('shopSearchValue')
app.isLoading = true
Promise.all([
// 获取分类模板设置
// 优化建议: 可以将此处的false改为true 启用缓存
SettingModel.data(true),
// 获取分类列表
CategoryApi.list({shopId:app.shopId,equipmentName}),
// 店铺信息
ShopApi.detail(app.shopId)
])
.then(result => {
// 初始化分类模板设置
app.initSetting(result[0])
// 初始化分类列表数据
app.initCategory(result[1])
// 更新购物车角标
app.getCartTotal()
// 初始化店铺信息
app.initShop(result[2])
// 刷新子组件
app.isRefresh = true
})
.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
},
/**
* 初始化店铺信息
* @param {Object} result
*/
initShop(result){
this.shop = result.data.detail
},
// 获取购物车总数量
getCartTotal() {
const app = this
return new Promise((resolve, reject) => {
CartApi.total()
.then(result => {
app.cartTotal = result.data.cartTotal
resolve(result)
})
.catch(reject)
})
},
// 更新购物车数量
onAddCart(total) {
this.cartTotal = total
},
/**
* 显示/隐藏SKU弹窗
* @param {skuMode} 模式 1:都显示 2:只显示购物车 3:只显示立即购买
*/
onShowSkuPopup(skuMode = 1) {
this.skuMode = skuMode
this.showSkuPopup = !this.showSkuPopup
},
// 搜索
onSearch(searchValue){
this.searchValue = searchValue
},
// 跳转到首页
onTargetHome(e) {
this.$navTo('pages/index/index')
},
// 跳转到购物车页
onTargetCart() {
this.$navTo('pages/cart/index')
},
},
/**
* 设置分享内容
*/
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>
@import "./index.scss";
// 搜索框
.search {
position: fixed;
top: var(--window-top);
left: var(--window-left);
right: var(--window-right);
z-index: 9;
top: 200rpx;
padding-bottom: 20rpx;
}
</style>

299
package/shops/index.vue Normal file
View File

@@ -0,0 +1,299 @@
<template>
<view>
<!-- 展馆图标 -->
<view class="header-box">
<view class="location" @click="changeCity">
<image src="../../static/postion.svg"></image><text>南宁</text>
</view>
<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">
<button class="button" @click="onSearch" type="warn">搜索</button>
</view>
</view>
</view>
<!-- 门店列表 -->
<view class="shop-list">
<u-card v-for="(item, index) in shopList" :key="index" @click="onSelectedShop(item)"
class="shop-item dis-flex flex-y-center" :border="false" :box-shadow="true" :show-head="false"
:head-border-bottom="false">
<view class="" slot="body">
<view class="shop-item__content flex-box">
<view class="u-body-item u-flex u-col-between u-p-t-0">
<u-avatar :src="item.logo_url" :size="120" mode="square" :show-level="true" ></u-avatar>
<view class="shop-info">
<view class="title">{{item.shop_name}}</view>
<view class="about">联系电话{{item.phone}}</view>
<view class="address">地址{{ item.region.province }}{{ item.region.city }}{{ item.region.region }}{{ item.address }}</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>
</view>
<!-- 选中状态 -->
<!-- <view v-if="item.shop_id == selectedId" class="shop-item__right">
<text class="iconfont icon-check1"></text>
</view> -->
</view>
</u-card>
</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 class="shop-list">
<u-card v-for="(item,index) in list" :key="index" :border="false" :box-shadow="true" :show-head="false"
:head-border-bottom="false">
<view class="" slot="body">
<view class="u-body-item u-flex u-col-between u-p-t-0">
<image class="shop-avatar"
:src="item.logo_url"
mode="widthFix"></image>
<view class="shop-info">
<view class="title">{{item.shop_name}}</view>
<view class="about">{{item.summary}}</view>
</view>
</view>
</view>
</u-card>
</view> -->
</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'
export default {
components: {
Search,
Empty
},
data() {
return {
title: '国内馆',
// 正在加载中
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,
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(item) {
this.$navTo('package/shops/detail/index',item)
},
changeCity() {
this.$toast('切换区域')
}
}
}
</script>
<style lang="scss" scoped>
.bg {
background-color: #F5F5F8
}
.shop-list {
.shop-item {
}
}
.header-box {
padding: 20rpx 36rpx 0 36rpx;
display: flex;
justify-content: space-between;
.location {
display: flex;
justify-content: center;
align-items: center;
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;
}
}
.u-body-item {
align-items: stretch !important
}
</style>

232
package/solution/apply.vue Normal file
View File

@@ -0,0 +1,232 @@
<template>
<view class="partner">
<!-- 表单组件 -->
<view class="form-wrapper" v-if="setting">
<u-form :model="form" ref="uForm" label-width="240rpx">
<view class="brand-poster">
<image :src="setting.background" mode="widthFix" class="banner"></image>
<view class="brand-name" v-html="setting.content"></view>
</view>
<view class="buy-desc">如果有意向请填写以下信息给平台留言</view>
<!-- 表单组件 -->
<view class="form-wrapper">
<u-form :model="form" ref="uForm" label-width="140rpx">
<u-form-item label="姓名" prop="real_name">
<u-input v-model="form.real_name" :disabled="disabled" placeholder="请输入姓名" />
</u-form-item>
<u-form-item label="电话" prop="phone">
<u-input v-model="form.phone" maxlength="11" :disabled="disabled" placeholder="请输入联系电话" />
</u-form-item>
<u-form-item label="性别" prop="sex">
<u-radio-group v-model="form.sex">
<u-radio @change="groupChange" v-for="(item, index) in sex" :key="index"
:name="item.name" :disabled="item.disabled">
{{item.name}}
</u-radio>
</u-radio-group>
</u-form-item>
<u-form-item label="定制预算" prop="money">
<u-input type="number" v-model="form.money" :disabled="disabled" maxlength="11" placeholder="请输入定制预算" />
</u-form-item>
<u-form-item label="微信号" prop="weixin">
<u-input v-model="form.weixin" :disabled="disabled" placeholder="请输入微信号" />
</u-form-item>
<u-form-item label="意向区域" prop="region">
<select-region ref="sRegion" :disabled="disabled" v-model="form.region" />
</u-form-item>
<u-form-item label="整体需求" prop="comments">
<u-input v-model="form.comments" :disabled="disabled" placeholder="请输入整体需求" />
</u-form-item>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn">
<u-button type="primary" shape="circle" @click="handleSubmit()">提交</u-button>
</view>
</view>
</u-form>
</view>
<!-- 操作按钮 -->
<!-- <view class="footer">
<view class="btn">
<u-button type="primary" shape="circle">更新资料</u-button>
</view>
</view> -->
</view>
</template>
<script>
// 表单字段元素
const form = {
name: '',
phone: '',
sex: '',
money: '',
region: [],
weixin: '',
comments: '',
}
const sex = [{
name: '男',
value: 1,
disabled: false
},
{
name: '女',
value: 2,
disabled: false
}
]
import * as Api from '@/api/solution/index.js'
import * as ApplyApi from '@/api/solution/apply.js'
import * as SettingApi from '@/api/solution/setting.js'
import SelectRegion from '@/components/select-region/select-region'
export default {
components: {
SelectRegion
},
data() {
return {
form,
disabled: false,
detail: {},
solutionId: 0,
sex,
setting: {}
}
},
onLoad() {
this.init()
},
methods: {
// 获取当前用户信息
init() {
const app = this
SettingApi.data().then(result => {
app.setting = result.data.setting
// 设置当前页面标题
app.setPageTitle()
})
},
// 设置当前页面标题
setPageTitle() {
uni.setNavigationBarTitle({ title: this.setting.title })
},
groupChange(e) {
this.form.sex = e
},
// 表单提交
handleSubmit() {
const app = this
// if (app.disabled) {
// return false
// }
const {
form,
solutionId
} = this
form.brand_id = solutionId
app.$refs.uForm.validate(valid => {
if (valid) {
app.disabled = true
app.onSubmit()
}
})
},
// 提交到后端
onSubmit() {
const app = this
const {
form
} = this
let req;
if (form.apply_status == 30) {
req = ApplyApi.edit(form.apply_id, form)
} else {
console.log('add');
req = ApplyApi.add(form)
}
req.then(result => {
console.log("result: ", result);
app.$toast(result.message)
setTimeout(function() {
uni.navigateBack()
}, 2000)
})
.finally(() => app.disabled = false)
}
}
}
</script>
<style lang="less">
.partner {
.banner {
padding-top: 20rpx;
width: 100%;
}
.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;
.shop-box {
text-align: center;
display: flex;
flex-direction: column;
.shop-name {
font-weight: bold;
font-size: 36rpx;
}
}
.brand-poster{
display: flex;
flex-direction: column;
}
.logo {
width: 100rpx;
}
.shipin {
width: 400rpx;
}
}
.footer {
width: 500rpx;
margin: 50rpx auto;
padding-bottom: 50rpx !important;
}
}
.brand-name {
padding: 10rpx;
text-align: center;
}
.buy-desc {
text-align: center;
color: #ff0000;
margin-top: 40rpx;
}
.buy-text {
text-align: center;
}
</style>

View File

@@ -0,0 +1,238 @@
<template>
<view class="container" :style="appThemeStyle">
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption" @up="upCallback">
<!-- 文章列表 -->
<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.solution_id)">
<!-- 小图模式 -->
<block v-if="item.show_type == 10">
<view class="article-item__image article-item__left">
<image class="image" mode="widthFix" :src="item.image_url"></image>
</view>
<view class="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>
</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 Api from '@/api/solution/index.js'
import * as UserApi from '@/api/user'
import * as CategoryApi from '@/api/solution/category'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
const pageSize = 15
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
CategoryId: 0,
// 文章列表
articleList: getEmptyPaginateObj(),
userInfo: {},
// 订单类型
orderType: false,
// 上拉加载配置
upOption: {
// 首次自动执行
auto: true,
// 每页数据的数量; 默认10
page: { size: pageSize },
// 数量要大于3条才显示无更多数据
noMoreSize: 3,
}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const app = this
console.log("options: ",options);
app.CategoryId = options.categoryId
app.orderType = options.orderType
// 获取文章分类数据
app.getArticleList()
app.getUserInfo()
},
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())
},
/**
* 获取文章列表
* @param {Number} pageNo 页码
*/
getArticleList(pageNo = 1) {
const app = this
return new Promise((resolve, reject) => {
Api.list({ categoryId: app.CategoryId, page: pageNo }, { load: false })
.then(result => {
// 合并新数据
const newList = result.data.list
app.articleList.data = getMoreListData(newList, app.articleList, pageNo)
resolve(newList)
})
.catch(reject)
})
},
// 获取当前用户信息
getUserInfo() {
const app = this
UserApi.info()
.then(result => {
app.userInfo = result.data.userInfo
})
},
// 获取当前标签项的值
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) {
const { orderType } = this
if( orderType ){
return this.$navTo('package/solution/detail', { articleId })
}
return this.$navTo('package/solution/buy/index')
}
},
/**
* 分享当前页面
*/
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>

View File

@@ -0,0 +1,225 @@
<template>
<view class="partner">
<!-- 表单组件 -->
<view class="form-wrapper" v-if="setting">
<u-form :model="form" ref="uForm" label-width="240rpx">
<view class="brand-poster">
<image :src="setting.background" mode="widthFix" class="banner"></image>
<view class="brand-name" v-html="setting.content"></view>
</view>
<view class="buy-info">
<u-radio-group v-model="agree" placement="column">
<u-radio name="购买协议" @change="radioChange">同意<text class="show-agreement">购买协议</text></u-radio>
</u-radio-group>
<text class="price">{{ setting.money }}</text>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn">
<u-button :disabled="!agree" type="primary" shape="circle" @click="handleSubmit()">立即购买</u-button>
</view>
</view>
</u-form>
</view>
<u-modal v-model="show" ref="uModal" title="购买协议" @confirm="confirm">
<view v-html="setting.agreement" class="agreement-content"></view>
</u-modal>
</view>
</template>
<script>
// 表单字段元素
const form = {
name: '',
phone: '',
sex: '',
money: '',
region: [],
weixin: '',
comments: '',
}
const sex = [{
name: '男',
value: 1,
disabled: false
},
{
name: '女',
value: 2,
disabled: false
}
]
import * as Api from '@/api/solution/index.js'
import * as ApplyApi from '@/api/solution/apply.js'
import * as SettingApi from '@/api/solution/setting.js'
import SelectRegion from '@/components/select-region/select-region'
export default {
components: {
SelectRegion
},
data() {
return {
form,
disabled: false,
detail: {},
solutionId: 0,
sex,
setting: {
money: 0
},
agree: 0,
// 是否显示协议内容
show: false
}
},
onLoad() {
this.init()
},
methods: {
// 获取当前用户信息
init() {
const app = this
SettingApi.data().then(result => {
app.setting = result.data.setting
// 设置当前页面标题
app.setPageTitle()
})
},
// 设置当前页面标题
setPageTitle() {
uni.setNavigationBarTitle({
title: this.setting.title
})
},
radioChange() {
this.show = true
},
groupChange(e) {
this.form.sex = e
},
confirm(e){
let app = this
},
// 表单提交
handleSubmit() {
const app = this
const {
form
} = this
app.onSubmit()
},
// 提交到后端
onSubmit() {
const app = this
return this.$navTo('package/solution/cashier/index')
// const {
// form
// } = this
// let req;
// if (form.apply_status == 30) {
// req = ApplyApi.edit(form.apply_id, form)
// } else {
// console.log('add');
// req = ApplyApi.add(form)
// }
// req.then(result => {
// console.log("result: ", result);
// app.$toast(result.message)
// setTimeout(function() {
// uni.navigateBack()
// }, 2000)
// })
// .finally(() => app.disabled = false)
}
}
}
</script>
<style lang="less">
.partner {
.banner {
padding-top: 20rpx;
width: 100%;
}
.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;
.shop-box {
text-align: center;
display: flex;
flex-direction: column;
.shop-name {
font-weight: bold;
font-size: 36rpx;
}
}
.brand-poster{
display: flex;
flex-direction: column;
}
.logo {
width: 100rpx;
}
.shipin {
width: 400rpx;
}
}
.footer {
width: 500rpx;
margin: 50rpx auto;
padding-bottom: 50rpx !important;
}
}
.brand-name {
text-align: center;
}
.buy-desc {
text-align: center;
color: #ff0000;
margin-top: 40rpx;
}
.buy-info {
display: flex;
justify-content: space-between;
margin-top: 40rpx;
.price {
color: #ff0000;
}
.show-agreement{
color: #0000ff;
}
}
.buy-text {
text-align: center;
}
.agreement-content{
padding: 20rpx;
}
</style>

View File

@@ -0,0 +1,460 @@
<template>
<view v-if="!isLoading" class="container" :style="appThemeStyle">
<!-- 订单信息 -->
<view class="order-info">
<!-- 支付剩余时间 -->
<view class="order-countdown">
<text class="m-r-6">剩余时间</text>
<count-down :date="order.expirationTime" separator="zh" theme="text" />
</view>
<!-- 付款金额 -->
<view class="order-amount">
<text class="unit"></text>
<text class="amount">{{ order.pay_price }}</text>
</view>
</view>
<!-- 支付方式 -->
<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 v-if="item.method === PayMethodEnum.BALANCE.value" class="user-balance">
<text>(可用{{ personal.balance }})</text>
</view>
</view>
<view class="item-right col-m" v-if="curPaymentItem && curPaymentItem.method == item.method">
<text class="iconfont icon-check"></text>
</view>
</view>
</view>
<!-- 确认按钮 -->
<view class="footer-fixed">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">确认支付</view>
</view>
</view>
<!-- 支付确认弹窗 -->
<!-- #ifdef H5 -->
<u-modal v-if="tempUnifyData" v-model="showConfirmModal" title="支付确认" show-cancel-button confirm-text="已完成支付"
:confirm-color="appTheme.mainBg" negative-top="100" :asyncClose="true"
@confirm="onTradeQuery(tempUnifyData.outTradeNo, tempUnifyData.method)">
<view class="modal-content">
<text>请在{{ PayMethodClientNameEnum[tempUnifyData.method] }}内完成支付如果您已经支付成功请点击已完成支付按钮</text>
</view>
</u-modal>
<!-- #endif -->
</view>
</template>
<script>
import storage from '@/utils/storage'
import { inArray, urlEncode } from '@/utils/util'
import { Alipay, Wechat } from '@/core/payment'
import CountDown from '@/components/countdown'
import { PayMethodEnum } from '@/common/enum/payment'
import { PayStatusEnum } from '@/common/enum/order'
import * as Api from '@/api/solution/cashier.js'
// 支付方式对应的图标
const PayMethodIconEnum = {
[PayMethodEnum.WECHAT.value]: 'icon-wechat-pay',
[PayMethodEnum.ALIPAY.value]: 'icon-alipay',
[PayMethodEnum.BALANCE.value]: 'icon-balance-pay'
}
// 支付方式的终端名称
const PayMethodClientNameEnum = {
[PayMethodEnum.WECHAT.value]: '微信',
[PayMethodEnum.ALIPAY.value]: '支付宝'
}
export default {
components: {
CountDown
},
data() {
return {
// 加载中
isLoading: true,
// 确认按钮禁用
disabled: false,
// 枚举类
PayMethodEnum,
PayMethodIconEnum,
PayMethodClientNameEnum,
// 当前选中的支付方式
curPaymentItem: null,
// 当前订单ID
orderId: null,
// 当前结算订单信息
order: {},
// 个人信息
personal: { balance: '0.00' },
// 当前客户端的支付方式列表后端根据platform判断
methods: [],
// 支付确认弹窗
showConfirmModal: false,
// #ifdef H5
// 当前微信支付信息 (临时数据, 仅用于H5端)
tempUnifyData: { outTradeNo: '', method: '' },
// #endif
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ orderId }) {
// 记录订单ID
this.orderId = Number(orderId)
// 获取收银台信息
this.getCashierInfo()
},
methods: {
// 获取收银台信息
getCashierInfo() {
const app = this
app.isLoading = true
Api.orderInfo()
.then(result => {
app.order = result.data.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]
},
// 判断当前页面来源于浏览器返回
// #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
// 判断是否选择了支付方式
if (!app.curPaymentItem) {
app.$toast('您还没有选择支付方式')
return
}
// 按钮禁用
if (app.disabled) return
app.disabled = true
// 提交到后端API
Api.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 paymentData = result.data.payment
// 余额支付
if (method === PayMethodEnum.BALANCE.value) {
app.onShowSuccess(result)
}
// 发起支付宝支付
if (method === PayMethodEnum.ALIPAY.value) {
console.log('paymentData', paymentData)
Alipay.payment(paymentData)
.then(res => app.onPaySuccess(res))
.catch(err => app.onPayFail(err))
}
// 发起微信支付
if (method === PayMethodEnum.WECHAT.value) {
console.log('paymentData', paymentData)
Wechat.payment({ orderKey: app.orderId, ...paymentData })
.then(res => app.onPaySuccess(res))
.catch(err => app.onPayFail(err))
}
},
// 订单支付成功的回调方法
// 这里只是前端支付api返回结果success,实际订单是否支付成功 以后端的查单和异步通知为准
onPaySuccess({ res, option: { isRequireQuery, outTradeNo, method } }) {
const app = this
// 判断是否需要主动查单
// isRequireQuery为true代表需要主动查单
if (isRequireQuery) {
app.onTradeQuery(outTradeNo, method)
return true
}
this.onShowSuccess(res)
},
// 显示支付成功信息并页面跳转
onShowSuccess({ message }) {
this.$toast(message || '订单支付成功')
this.onSuccessNav()
},
// 订单支付失败
onPayFail(err) {
console.log('onPayFail', err)
const errMsg = err.message || '订单未支付'
this.$error(errMsg)
},
// 已完成支付按钮事件: 请求后端查单
onTradeQuery(outTradeNo, method) {
const app = this
// 交易查询
// 查询第三方支付订单是否付款成功
Api.tradeQuery({ outTradeNo, method, client: app.platform })
.then(result => result.data.isPay ? app.onShowSuccess(result) : app.onPayFail(result))
.finally(() => app.showConfirmModal = false)
},
// 支付成功后的跳转
onSuccessNav() {
const pages = getCurrentPages()
const lastPage = pages.length < 2 ? null : pages[pages.length - 2]
const backRoutes = [
'package/solution/index',
'package/solution/detail'
]
if (lastPage && inArray(lastPage.route, backRoutes)) {
setTimeout(() => {
uni.navigateBack()
}, 1000)
} else {
setTimeout(() => {
this.$navTo('package/solution/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: #111111;
.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);
// 禁用按钮
&.disabled {
opacity: 0.6;
}
}
}
</style>

View File

@@ -0,0 +1,67 @@
<template>
<!-- 搜索框 -->
<view class="search-wrapper">
<view class="index-search" @click="onClick">
<view class="index-cont-search t-c">
<text class="search-icon iconfont icon-search"></text>
<text class="search-text">{{ tips }}</text>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
tips: {
type: String,
default: '搜索关键词'
}
},
data() {
return {}
},
methods: {
onClick() {
this.$emit('event')
}
}
}
</script>
<style lang="scss" scoped>
.search-wrapper {
padding: 13rpx;
}
.index-search {
border-bottom: 0;
border-radius: 50rpx;
overflow: hidden;
font-size: 28rpx;
color: #6d6d6d;
box-sizing: border-box;
line-height: 64rpx;
display: flex;
align-items: center;
.index-cont-search {
width: 100%;
font-size: 28rpx;
background: #f7f7f7;
display: flex;
justify-content: center;
align-items: center;
}
.index-cont-search .search-icon {
font-size: 28rpx;
}
.index-cont-search .search-text {
margin-left: 14rpx;
}
}
</style>

107
package/solution/detail.vue Executable file
View File

@@ -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>

109
package/solution/index.vue Normal file
View File

@@ -0,0 +1,109 @@
<template>
<view class="solution">
<view class="search">
<search class="search" :tips="options.search ? options.search : '搜索关键词'" @event="onTargetDetail(2)" />
</view>
<view class="btn">
<u-button type="primary" shape="circle" text="门店定制解决方案预定" @click="onApply">门店定制解决方案预定</u-button>
</view>
<view class="grid">
<u-grid :border="false">
<u-grid-item v-for="(item,index) in baseList" :key="index" @click="onTargetDetail(item.category_id)">
<image
:src="item.image ? item.image.preview_url : 'https://file.gxwebsoft.com/20220820/5794a3c1ed9b4d6b94737e4ebc36ce90.png'"
class="avatar" mode="widthFix"></image>
<text class="grid-text">{{item.name}}</text>
</u-grid-item>
</u-grid>
</view>
</view>
</template>
<script>
import * as CategoryApi from '@/api/solution/category.js'
import * as SolutionOrderApi from '@/api/order.js'
import Search from './components/search'
export default {
components: {
Search
},
data() {
return {
baseList: [],
// 是否已购买门店解决方案
orderType: false
}
},
onLoad(options) {
const app = this
// 获取文章分类数据
app.getCategoryList(options.categoryId)
// 门店解决方案订单
app.getSolutionOrder()
},
methods: {
// 获取文章分类数据
getCategoryList(categoryId) {
CategoryApi.list({
type: 'solution'
}).then(result => {
this.baseList = result.data.list
})
},
// 门店解决方案订单
getSolutionOrder() {
const app = this
SolutionOrderApi.list({
dataType: 'all',
page: 1,
orderType: 1
}, {
load: false
}).then(result => {
if (result.data.list.total > 0) {
app.orderType = true
}
})
},
// 跳转文章详情页
onTargetDetail(categoryId) {
const app = this
const { orderType } = this
// 已付款
if (orderType) {
return this.$navTo('package/solution/article/index', {
categoryId, orderType
})
}
return this.$navTo('package/solution/buy/index')
},
onApply() {
this.$navTo('package/solution/apply')
}
}
}
</script>
<style lang="less">
.solution {
.search {
padding: 10rpx 20rpx;
background-color: #ffffff;
}
.btn {
width: 700rpx;
border-radius: 100%;
margin: 25rpx auto;
}
.grid {
width: 700rpx;
margin: auto;
}
.avatar {
width: 120rpx;
}
}
</style>

View File

@@ -0,0 +1,122 @@
<template>
<view class="partner">
<!-- 表单组件 -->
<view class="form-wrapper" v-if="shopInfo">
<u-form :model="form" ref="uForm" label-width="240rpx">
<u-form-item label="商户名称" prop="name">
{{shopInfo.shopName}}
</u-form-item>
<u-form-item label="联系人" prop="name">
{{shopInfo.linkman}}
</u-form-item>
<u-form-item label="联系方式" prop="name">
{{shopInfo.phone}}
</u-form-item>
<u-form-item label="站点地址" prop="name">
{{shopInfo.address}}
</u-form-item>
<u-form-item label="法定代表人身份" prop="name">
<image :src="applyInfo.sfz_image1" mode="widthFix" class="shipin"></image>
<image :src="applyInfo.sfz_image2" mode="widthFix" class="shipin"></image>
</u-form-item>
<u-form-item label="门脸照" prop="name">
<image :src="applyInfo.shop_image" mode="widthFix" class="shipin"></image>
</u-form-item>
<u-form-item label="微信号" prop="name">
{{applyInfo.weixin}}
</u-form-item>
<u-form-item label="食品经营许可证" prop="name">
<image :src="applyInfo.shipin_card_image" mode="widthFix" class="shipin"></image>
</u-form-item>
<u-form-item label="支付宝姓名" prop="name" v-if="shopInfo.alipayName">
{{shopInfo.alipayName}}
</u-form-item>
<u-form-item label="支付宝账号" prop="name" v-if="shopInfo.alipayAccount">
{{shopInfo.alipayAccount}}
</u-form-item>
</u-form>
</view>
<!-- 操作按钮 -->
<!-- <view class="footer">
<view class="btn">
<u-button type="primary" shape="circle">更新资料</u-button>
</view>
</view> -->
</view>
</template>
<script>
// 表单字段元素
const form = {
// 入驻类型 合伙人10001,配送员10002,社区门店10003,省代10004
gradeId: 10004,
company: '',
realName: '',
phone: '',
mobile: '',
region: [],
detail: '',
}
import * as UserApi from '@/api/user'
import SelectRegion from '@/components/select-region/select-region'
export default {
components: {
SelectRegion
},
data() {
return {
form,
disabled: false,
imageList: [],
shopInfo: null,
applyInfo: null
}
},
onLoad() {
this.getUserInfo()
},
methods: {
// 获取当前用户信息
getUserInfo() {
const app = this
UserApi.info().then(result => {
app.shopInfo = result.data.shopInfo
app.applyInfo = result.data.applyInfo
})
},
groupChange(){
}
}
}
</script>
<style lang="less">
.partner {
.banner {
width: 100%;
}
.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;
.logo{
width: 100rpx;
}
.shipin{
width: 200rpx;
}
}
.footer{
width: 500rpx;
margin: 20rpx auto;
}
}
</style>

206
package/user/address/create.vue Executable file
View File

@@ -0,0 +1,206 @@
<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 { 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 : []
}
})
},
// #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);
// 禁用按钮
&.disabled {
opacity: 0.6;
}
}
}
</style>

305
package/user/address/index.vue Executable file
View File

@@ -0,0 +1,305 @@
<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-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;
}
.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);
}
}
</style>

244
package/user/address/update.vue Executable file
View File

@@ -0,0 +1,244 @@
<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 : []
}
})
},
// #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);
// 禁用按钮
&.disabled {
opacity: 0.6;
}
}
}
</style>

View File

@@ -0,0 +1,109 @@
<template>
<view class="partner">
<!-- 表单组件 -->
<view class="form-wrapper" v-if="shopInfo">
<u-form :model="form" ref="uForm" label-width="240rpx">
<u-form-item>
<view class="shop-box">
<text class="shop-name">后台管理</text>
<text>地址https://dev.gxwebsoft.com/ylx-admin/</text>
<text>账号{{applyInfo.phone}}</text>
<text>密码默认为手机号码后6位</text>
</view>
</u-form-item>
</u-form>
</view>
<!-- 操作按钮 -->
<!-- <view class="footer">
<view class="btn">
<u-button type="primary" shape="circle">更新资料</u-button>
</view>
</view> -->
</view>
</template>
<script>
// 表单字段元素
const form = {
// 入驻类型 合伙人10001,配送员10002,社区门店10003,省代10004
grade_id: 10004,
company: '',
real_name: '',
phone: '',
mobile: '',
region: [],
detail: '',
}
import * as UserApi from '@/api/user'
import SelectRegion from '@/components/select-region/select-region'
export default {
components: {
SelectRegion
},
data() {
return {
form,
disabled: false,
imageList: [],
shopInfo: {},
applyInfo: {}
}
},
onLoad() {
this.getUserInfo()
},
methods: {
// 获取当前用户信息
getUserInfo() {
const app = this
UserApi.info().then(result => {
app.shopInfo = result.data.shopInfo
app.applyInfo = result.data.applyInfo
})
},
groupChange(){
}
}
}
</script>
<style lang="less">
.partner {
.banner {
padding-top: 20rpx;
width: 100%;
}
.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;
.shop-box{
display: flex;
flex-direction: column;
.shop-name{
font-weight: bold;
font-size: 36rpx;
}
}
.logo{
width: 100rpx;
}
.shipin{
width: 400rpx;
}
}
.footer{
width: 500rpx;
margin: 20rpx auto;
}
}
</style>

206
package/user/equipment/create.vue Executable file
View File

@@ -0,0 +1,206 @@
<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 { 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 : []
}
})
},
// #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);
// 禁用按钮
&.disabled {
opacity: 0.6;
}
}
}
</style>

441
package/user/equipment/detail.vue Executable file
View File

@@ -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>

358
package/user/equipment/index.vue Executable file
View File

@@ -0,0 +1,358 @@
<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.phone }}</text>
<u-tag text="已激活" plain size="mini" type="success"></u-tag>
</view>
<view class="line"></view>
<view class="contacts">
<view class="bms-info">
<text class="item">电池型号</text>
<text class="item">BMS模块硬件版</text>
<text class="item">工作状态正常</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')"></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')"></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" :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
})
},
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>

View File

@@ -0,0 +1,440 @@
<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="pause-circle" label="放电开关">
<u-switch slot="right" v-model="form.switchVal1"></u-switch>
</u-form-item>
<u-form-item left-icon="lock-open" label="放电开关">
<u-switch slot="right" v-model="form.switchVal2"></u-switch>
</u-form-item>
<u-form-item left-icon="reload" label="更换电池">
<u-tag text="更换" slot="right" plain type="error" @click="handleChangeEquipment"></u-tag>
</u-form-item>
</u-form>
</view>
</view>
<!-- 换电 -->
<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="doEquipment(true)">确定更换电池</u-button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import * as AddressApi from '@/api/address'
import Empty from '@/components/empty'
import * as EquipmentApi from '@/websoft/api/equipment.js'
export default {
components: {
Empty
},
data() {
return {
//当前页面参数
options: {},
equipmentId: {},
equipment: {},
form: {
switchVal1: false,
switchVal2: false,
},
// 正在加载
isLoading: true,
// 收货地址列表
list: [],
// 默认收货地址
defaultId: null,
title: '设备操作',
showQRCodeBind: false,
bindValue: '',
orderId: null,
description: '用户可控制设备的控制开关'
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 当前页面参数
this.options = options
this.equipmentId = options.equipmentId
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 获取页面数据
this.getPageData()
},
methods: {
// 获取页面数据
getPageData() {
const app = this
EquipmentApi.getEquipment(app.equipmentId).then(result => {
app.equipment = result.data
app.orderId = result.data.orderId
})
// 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)
})
},
handleChangeEquipment() {
const app = this
app.showQRCodeBind = true
},
doEquipment(canReset = false) {
const app = this
const {
equipmentId,
bindValue,
orderId
} = this
EquipmentApi.changeEquipment({
equipmentCode: bindValue,
equipmentId,
orderId
}).then(result => {
app.$success(result.message)
app.showQRCodeBind = false
app.getPageData()
}).catch(err => {
app.$error(err.message)
})
console.log("this.bindValue: ", this.bindValue);
},
// 获取默认的收货地址
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;
}
}
.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);
}
}
// 弹出层 - 绑定设备码
.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;
}
}
</style>

244
package/user/equipment/update.vue Executable file
View File

@@ -0,0 +1,244 @@
<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 : []
}
})
},
// #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);
// 禁用按钮
&.disabled {
opacity: 0.6;
}
}
}
</style>

618
package/user/goods/cashier.vue Executable file
View File

@@ -0,0 +1,618 @@
<template>
<view v-if="!isLoading" class="container" :style="appThemeStyle">
<!-- 订单信息 -->
<view class="order-info">
<!-- 支付剩余时间 -->
<view class="order-countdown">
<text class="m-r-6">剩余时间</text>
<count-down :date="order.expirationTime" separator="zh" theme="text" />
</view>
<!-- 付款金额 -->
<view class="order-amount">
<text class="unit"></text>
<text class="amount">{{ order.pay_price }}</text>
</view>
</view>
<!-- 购买时长 -->
<u-card title="订单信息">
<view class="duration-method" slot="body">
<view class="pay-item dis-flex flex-x-between">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_text">
<text>购买时长</text>
</view>
</view>
<view class="item-right col-m" @click="onShowDuration">
<text class="duration-title">{{ duration[planId].label }}</text>
<text class="iconfont icon-arrow-down"></text>
</view>
</view>
<view class="pay-item dis-flex flex-x-between">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_text">
<text>购买数量</text>
</view>
</view>
<view class="item-right col-m">
<text>{{ length }}</text>
</view>
</view>
<u-select v-model="showDuration" :list="duration" @confirm="onConfirm"></u-select>
</view>
</u-card>
<!-- 支付方式 -->
<u-card title="付款方式">
<view class="payment-method" slot="body">
<view v-for="(item, index) in methods" :key="index" class="pay-item dis-flex flex-x-between"
@click="handleSelectPayType(index)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon" :class="[item.method]">
<text class="iconfont" :class="[PayMethodIconEnum[item.method]]"></text>
</view>
<view class="item-left_text">
<text>{{ PayMethodEnum[item.method].name }}</text>
</view>
<view v-if="item.method === PayMethodEnum.BALANCE.value" class="user-balance">
<text>(可用{{ personal.balance }})</text>
</view>
</view>
<view class="item-right col-m" v-if="curPaymentItem && curPaymentItem.method == item.method">
<text class="iconfont icon-check"></text>
</view>
</view>
</view>
</u-card>
<!-- 确认按钮 -->
<view class="footer-fixed">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">确认支付</view>
</view>
</view>
<!-- 支付确认弹窗 -->
<!-- #ifdef H5 -->
<u-modal v-if="tempUnifyData" v-model="showConfirmModal" title="支付确认" show-cancel-button confirm-text="已完成支付"
:confirm-color="appTheme.mainBg" negative-top="100" :asyncClose="true"
@confirm="onTradeQuery(tempUnifyData.outTradeNo, tempUnifyData.method)">
<view class="modal-content">
<text>请在{{ PayMethodClientNameEnum[tempUnifyData.method] }}内完成支付如果您已经支付成功请点击已完成支付按钮</text>
</view>
</u-modal>
<!-- #endif -->
</view>
</template>
<script>
import storage from '@/utils/storage'
import {
inArray,
urlEncode
} from '@/utils/util'
import {
Alipay,
Wechat
} from '@/core/payment'
import CountDown from '@/components/countdown'
import {
PayMethodEnum
} from '@/common/enum/payment'
import {
PayStatusEnum
} from '@/common/enum/order'
import * as Api from '@/api/order/popularize.js'
// 支付方式对应的图标
const PayMethodIconEnum = {
[PayMethodEnum.WECHAT.value]: 'icon-wechat-pay',
[PayMethodEnum.ALIPAY.value]: 'icon-alipay',
[PayMethodEnum.BALANCE.value]: 'icon-balance-pay'
}
// 支付方式的终端名称
const PayMethodClientNameEnum = {
[PayMethodEnum.WECHAT.value]: '微信',
[PayMethodEnum.ALIPAY.value]: '支付宝'
}
export default {
components: {
CountDown
},
data() {
return {
// 加载中
isLoading: true,
// 确认按钮禁用
disabled: false,
// 枚举类
PayMethodEnum,
PayMethodIconEnum,
PayMethodClientNameEnum,
// 当前选中的支付方式
curPaymentItem: null,
// 当前订单ID
orderId: null,
// 当前结算订单信息
order: {},
// 个人信息
personal: {
balance: '0.00'
},
// 当前客户端的支付方式列表后端根据platform判断
methods: [],
// 支付确认弹窗
showConfirmModal: false,
// #ifdef H5
// 当前微信支付信息 (临时数据, 仅用于H5端)
tempUnifyData: {
outTradeNo: '',
method: ''
},
// #endif
// 购买商品数量
length: 0,
// 购买的商品ID
goodsIds: [],
// 购买时长
duration: [],
// 套餐ID
planId: 0,
showDuration: false
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 读取缓存数据
const goodsIds = storage.get('selectGoodsIdsByPopularize')
this.goodsIds = goodsIds
// 购买数量
this.length = goodsIds.length
// 获取收银台信息
this.getCashierInfo()
},
methods: {
// 获取收银台信息
getCashierInfo() {
const app = this
const { goodsIds, planId, length } = this
app.isLoading = true
// 读取缓存数据
Api.orderInfo({form: {planId,goodsIds,length}})
.then(result => {
app.order = result.data.order
app.personal = result.data.personal
app.methods = result.data.paymentMethods
app.duration = result.data.planList.map((d,index) => {
return {
value: index,
label: d.plan_name + '¥(' + d.money + ')'
}
})
app.isLoading = false
app.setDefaultPayType()
app.checkOrderPayStatus()
// #ifdef H5
// 判断当前页面来源于浏览器返回
this.performance()
// #endif
})
},
// 设置默认的支付方式
setDefaultPayType() {
const app = this
if (app.disabled) return
const defaultIndex = app.methods.findIndex(item => item.is_default == true)
defaultIndex > -1 && app.handleSelectPayType(defaultIndex)
},
// 判断当前订单是否为已支付
checkOrderPayStatus() {
const app = this
if (app.order.pay_status == PayStatusEnum.SUCCESS.value) {
app.$toast('恭喜您,订单已付款成功')
app.onSuccessNav()
}
},
onShowDuration(index){
this.showDuration = true
},
onConfirm(item){
this.planId = item[0].value
this.getCashierInfo()
},
bindPickerChange: function(e) {
console.log('picker发送选择改变携带值为', e.detail.value)
this.index = e.detail.value
},
// 选择支付方式
handleSelectPayType(index) {
this.curPaymentItem = this.methods[index]
},
// 选择时长类型
handleSelectDurationType(key){
this.selectDuration = key
},
// 判断当前页面来源于浏览器返回
// #ifdef H5
performance() {
const app = this
// 判断订单状态, 异步回调会将订单状态变为已支付, 那么就不需要让用户手动查单了
if (app.order.pay_status == PayStatusEnum.PENDING.value) {
app.alipayPerformance()
app.wechatPerformance()
}
},
// H5端支付宝支付完成跳转回当前页面时触发
alipayPerformance() {
const app = this
app.tempUnifyData = Alipay.performance()
if (app.tempUnifyData) {
app.onTradeQuery(app.tempUnifyData.outTradeNo, app.tempUnifyData.method)
}
},
// H5端微信支付完成或返回时触发
wechatPerformance() {
const app = this
app.tempUnifyData = Wechat.performance(app.orderId)
if (app.tempUnifyData) {
app.showConfirmModal = true
}
},
// #endif
// 确认支付
handleSubmit() {
const app = this
const { goodsIds, planId, length } = this
// 判断是否选择了支付方式
if (!app.curPaymentItem) {
app.$toast('您还没有选择支付方式')
return
}
// 按钮禁用
if (app.disabled) return
app.disabled = true
// 提交到后端API
Api.orderPay({
form: {
method: app.curPaymentItem.method,
client: app.platform,
extra: app.getExtraAsUnify(app.curPaymentItem.method),
goodsIds,
planId,
length
}
})
.then(result => app.onSubmitCallback(result))
.finally(err => {
setTimeout(() => app.disabled = false, 10)
})
},
// 获取第三方支付的扩展参数
getExtraAsUnify(method) {
if (method === PayMethodEnum.ALIPAY.value) {
return Alipay.extraAsUnify()
}
if (method === PayMethodEnum.WECHAT.value) {
return Wechat.extraAsUnify()
}
return {}
},
// 订单提交成功后回调
onSubmitCallback(result) {
const app = this
const method = app.curPaymentItem.method
const paymentData = result.data.payment
// 余额支付
if (method === PayMethodEnum.BALANCE.value) {
app.onShowSuccess(result)
}
// 发起支付宝支付
if (method === PayMethodEnum.ALIPAY.value) {
console.log('paymentData', paymentData)
Alipay.payment(paymentData)
.then(res => app.onPaySuccess(res))
.catch(err => app.onPayFail(err))
}
// 发起微信支付
if (method === PayMethodEnum.WECHAT.value) {
console.log('paymentData', paymentData)
Wechat.payment({
orderKey: app.orderId,
...paymentData
})
.then(res => app.onPaySuccess(res))
.catch(err => app.onPayFail(err))
}
},
// 订单支付成功的回调方法
// 这里只是前端支付api返回结果success,实际订单是否支付成功 以后端的查单和异步通知为准
onPaySuccess({
res,
option: {
isRequireQuery,
outTradeNo,
method
}
}) {
const app = this
// 判断是否需要主动查单
// isRequireQuery为true代表需要主动查单
if (isRequireQuery) {
app.onTradeQuery(outTradeNo, method)
return true
}
this.onShowSuccess(res)
},
// 显示支付成功信息并页面跳转
onShowSuccess({
message
}) {
this.$toast(message || '订单支付成功')
this.onSuccessNav()
},
// 订单支付失败
onPayFail(err) {
console.log('onPayFail', err)
const errMsg = err.message || '订单未支付'
this.$error(errMsg)
},
// 已完成支付按钮事件: 请求后端查单
onTradeQuery(outTradeNo, method) {
const app = this
// 交易查询
// 查询第三方支付订单是否付款成功
Api.tradeQuery({
outTradeNo,
method,
client: app.platform
})
.then(result => result.data.isPay ? app.onShowSuccess(result) : app.onPayFail(result))
.finally(() => app.showConfirmModal = false)
},
// 支付成功后的跳转
onSuccessNav() {
const pages = getCurrentPages()
const lastPage = pages.length < 2 ? null : pages[pages.length - 2]
const backRoutes = [
'package/solution/index',
'package/solution/detail'
]
if (lastPage && inArray(lastPage.route, backRoutes)) {
setTimeout(() => {
uni.navigateBack()
}, 1000)
} else {
setTimeout(() => {
this.$navTo('package/user/goods/list', {}, 'redirectTo')
}, 1200)
}
},
}
}
</script>
<style>
page {
background: #F4F4F4;
}
</style>
<style lang="scss" scoped>
.container {
background-color: #F4F4F4;
}
// 订单信息
.order-info {
padding: 80rpx 0;
text-align: center;
.order-countdown {
display: flex;
justify-content: center;
font-size: 26rpx;
color: #666666;
margin-bottom: 20rpx;
}
.order-amount {
margin: 0 auto;
max-width: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #111111;
.unit {
font-size: 30rpx;
margin-bottom: -18rpx;
}
.amount {
font-size: 56rpx;
}
}
}
.duration-title {
margin-right: 20rpx;
}
.duration-method {
width: 94%;
background-color: #ffffff;
border-radius: 20rpx;
.pay-item {
padding: 26rpx 0;
font-size: 28rpx;
border-bottom: 1rpx solid rgb(248, 248, 248);
&:last-child {
border-bottom: none;
}
.item-left_icon {
margin-right: 20rpx;
font-size: 44rpx;
&.wechat {
color: #00c800;
}
&.alipay {
color: #009fe8;
}
&.balance {
color: #ff9700;
}
}
.item-left_text {
font-size: 28rpx;
}
.item-right {
font-size: 32rpx;
}
.user-balance {
margin-left: 20rpx;
font-size: 26rpx;
}
}
}
// 支付方式
.payment-method {
width: 94%;
background-color: #ffffff;
border-radius: 20rpx;
.pay-item {
padding: 26rpx 0;
font-size: 28rpx;
border-bottom: 1rpx solid rgb(248, 248, 248);
&:last-child {
border-bottom: none;
}
.item-left_icon {
margin-right: 20rpx;
font-size: 44rpx;
&.wechat {
color: #00c800;
}
&.alipay {
color: #009fe8;
}
&.balance {
color: #ff9700;
}
}
.item-left_text {
font-size: 28rpx;
}
.item-right {
font-size: 32rpx;
}
.user-balance {
margin-left: 20rpx;
font-size: 26rpx;
}
}
}
// 支付确认弹窗
.modal-content {
padding: 40rpx 48rpx;
font-size: 30rpx;
line-height: 50rpx;
text-align: left;
color: #606266;
// height: 620rpx;
box-sizing: border-box;
}
// 底部操作栏
.footer-fixed {
position: fixed;
bottom: var(--window-bottom);
left: 0;
right: 0;
z-index: 11;
box-shadow: 0 -4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
background: #fff;
// 设置ios刘海屏底部横线安全区域
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
.btn-wrapper {
height: 120rpx;
display: flex;
align-items: center;
padding: 0 40rpx;
}
.btn-item {
flex: 1;
font-size: 28rpx;
height: 80rpx;
color: #fff;
border-radius: 50rpx;
display: flex;
justify-content: center;
align-items: center;
}
.btn-item-main {
background: linear-gradient(to right, $main-bg, $main-bg2);
// 禁用按钮
&.disabled {
opacity: 0.6;
}
}
}
</style>

View File

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

View File

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

View File

@@ -0,0 +1,179 @@
<template>
<goods-sku-popup :value="value" @input="onChangeValue" border-radius="20" :localdata="goodsInfo" :mode="skuMode" :maskCloseAble="true"
:priceColor="appTheme.mainBg" :buyNowBackgroundColor="appTheme.mainBg" :addCartColor="appTheme.viceText" :addCartBackgroundColor="appTheme.viceBg"
:activedStyle="{ color: appTheme.mainBg, borderColor: appTheme.mainBg, backgroundColor: activedBtnBackgroundColor }"
@open="openSkuPopup" @close="closeSkuPopup" @add-cart="addCart" @buy-now="buyNow" buyNowText="立即购买" />
</template>
<script>
import { setCartTotalNum } from '@/core/app'
import { hex2rgba } from '@/utils/color'
import * as CartApi from '@/api/cart'
import GoodsSkuPopup from '@/components/goods-sku-popup'
export default {
components: {
GoodsSkuPopup
},
model: {
prop: 'value',
event: 'input'
},
props: {
// true 组件显示 false 组件隐藏
value: {
Type: Boolean,
default: false
},
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
skuMode: {
type: Number,
default: 1
},
// 商品详情信息
goods: {
type: Object,
default: {}
}
},
data() {
return {
goodsInfo: {}
}
},
computed: {
// 规格按钮选中时的背景色
activedBtnBackgroundColor() {
return hex2rgba(this.appTheme.mainBg, 0.1)
}
},
created() {
const app = this
const { goods } = app
app.goodsInfo = {
_id: goods.goods_id,
name: goods.goods_name,
goods_thumb: goods.goods_image,
sku_list: app.getSkuList(),
spec_list: app.getSpecList()
}
},
methods: {
// 监听组件显示隐藏
onChangeValue(val) {
this.$emit('input', val)
},
// 整理商品SKU列表
getSkuList() {
const app = this
const { goods: { goods_name, goods_image, skuList } } = app
const skuData = []
skuList.forEach(item => {
skuData.push({
_id: item.id,
goods_sku_id: item.goods_sku_id,
goods_id: item.goods_id,
goods_name: goods_name,
image: item.image_url ? item.image_url : goods_image,
price: item.goods_price * 100,
stock: item.stock_num,
spec_value_ids: item.spec_value_ids,
sku_name_arr: app.getSkuNameArr(item.spec_value_ids)
})
})
return skuData
},
// 获取sku记录的规格值列表
getSkuNameArr(specValueIds) {
const app = this
const defaultData = ['默认']
const skuNameArr = []
if (specValueIds) {
specValueIds.forEach((valueId, groupIndex) => {
const specValueName = app.getSpecValueName(valueId, groupIndex)
skuNameArr.push(specValueName)
})
}
return skuNameArr.length ? skuNameArr : defaultData
},
// 获取指定的规格值名称
getSpecValueName(valueId, groupIndex) {
const app = this
const { goods: { specList } } = app
const res = specList[groupIndex].valueList.find(specValue => {
return specValue.spec_value_id == valueId
})
return res.spec_value
},
// 整理规格数据
getSpecList() {
const { goods: { specList } } = this
const defaultData = [{ name: '默认', list: [{ name: '默认' }] }]
const specData = []
specList.forEach(group => {
const children = []
group.valueList.forEach(specValue => {
children.push({ name: specValue.spec_value })
})
specData.push({
name: group.spec_name,
list: children
})
})
return specData.length ? specData : defaultData
},
// sku组件 开始-----------------------------------------------------------
openSkuPopup() {
// console.log("监听 - 打开sku组件")
},
closeSkuPopup() {
// console.log("监听 - 关闭sku组件")
},
// 加入购物车按钮
addCart(selectShop) {
const app = this
const { goods_id, goods_sku_id, buy_num } = selectShop
CartApi.add(goods_id, goods_sku_id, buy_num)
.then(result => {
// 显示成功
app.$toast(result.message)
// 隐藏当前弹窗
app.onChangeValue(false)
// 购物车商品总数量
const cartTotal = result.data.cartTotal
// 缓存购物车数量
setCartTotalNum(cartTotal)
// 传递给父级
app.$emit('addCart', cartTotal)
})
},
// 立即购买
buyNow(selectShop) {
// 跳转到订单结算页
this.$navTo('pages/checkout/index', {
mode: 'buyNow',
goodsId: selectShop.goods_id,
goodsSkuId: selectShop.goods_sku_id,
goodsNum: selectShop.buy_num
})
// 隐藏当前弹窗
this.onChangeValue(false)
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

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

View File

@@ -0,0 +1,69 @@
<template>
<!-- 搜索框 -->
<view class="search-wrapper">
<view class="index-search" @click="onClick">
<view class="index-cont-search t-c">
<text class="search-icon iconfont icon-search"></text>
<text class="search-text">{{ tips }}</text>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
tips: {
type: String,
default: '搜索商品'
}
},
data() {
return {}
},
methods: {
onClick() {
this.$emit('event')
}
}
}
</script>
<style lang="scss" scoped>
.search-wrapper {
background: #fff;
padding: 13rpx;
}
.index-search {
border-bottom: 0;
background: #fff;
border-radius: 50rpx;
overflow: hidden;
font-size: 28rpx;
color: #6d6d6d;
box-sizing: border-box;
line-height: 64rpx;
display: flex;
align-items: center;
.index-cont-search {
width: 100%;
font-size: 28rpx;
background: #f7f7f7;
display: flex;
justify-content: center;
align-items: center;
}
.index-cont-search .search-icon {
font-size: 28rpx;
}
.index-cont-search .search-text {
margin-left: 14rpx;
}
}
</style>

228
package/user/goods/detail.scss Executable file
View File

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

347
package/user/goods/detail.vue Executable file
View File

@@ -0,0 +1,347 @@
<template>
<view v-show="!isLoading" class="container" :style="appThemeStyle">
<!-- 商品图片轮播 -->
<SlideImage v-if="!isLoading" :video="goods.video" :videoCover="goods.videoCover" :images="goods.goods_images" />
<!-- 商品信息 -->
<view v-if="!isLoading" class="goods-info m-top20">
<!-- 价格销量 -->
<view class="info-item info-item__top dis-flex flex-x-between flex-y-end">
<view class="block-left dis-flex flex-y-center">
<!-- 商品售价 -->
<text class="floor-price__samll"></text>
<text class="floor-price">{{ goods.goods_price_min }}</text>
<!-- 会员价标签 -->
<view v-if="goods.is_user_grade" class="user-grade">
<text>会员价</text>
</view>
<!-- 划线价 -->
<text v-if="goods.line_price_min > 0" class="original-price">{{ goods.line_price_min }}</text>
</view>
<view class="block-right dis-flex">
<!-- 销量 -->
<view class="goods-sales">
<text>已售{{ goods.goods_sales }}</text>
</view>
</view>
</view>
<!-- 标题分享 -->
<view class="info-item info-item__name dis-flex flex-y-center">
<view class="goods-name flex-box">
<text class="twoline-hide">{{ goods.goods_name }}</text>
</view>
<view class="goods-share__line"></view>
<view class="goods-share">
<button class="share-btn dis-flex flex-dir-column" @click="onShowShareSheet()">
<text class="share__icon iconfont icon-fenxiang"></text>
<text class="f-24">分享</text>
</button>
</view>
</view>
<!-- 商品卖点 -->
<view v-if="goods.selling_point" class="info-item info-item_selling-point">
<text>{{ goods.selling_point }}</text>
</view>
</view>
<!-- 选择商品规格 -->
<view v-if="goods.spec_type == 20" class="goods-choice m-top20 b-f" @click="onShowSkuPopup(1)">
<view class="spec-list">
<view class="flex-box">
<text class="col-8">选择</text>
<text class="spec-name" v-for="(item, index) in goods.specList" :key="index">{{ item.spec_name }}</text>
</view>
<view class="f-26 col-9 t-r">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
</view>
<!-- 商品服务 -->
<Service v-if="!isLoading" :goods-id="goodsId" />
<!-- 商品SKU弹窗 -->
<SkuPopup v-if="!isLoading" v-model="showSkuPopup" :skuMode="skuMode" :goods="goods" @addCart="onAddCart" />
<!-- 商品评价 -->
<Comment v-if="!isLoading" :goods-id="goodsId" :limit="2" />
<!-- 商品描述 -->
<view v-if="!isLoading" class="goods-content m-top20">
<view class="item-title b-f">
<text>商品描述</text>
</view>
<block v-if="goods.content != ''">
<view class="goods-content__detail b-f">
<mp-html :content="goods.content" />
</view>
</block>
<empty v-else tips="亲,暂无商品描述" />
</view>
<!-- 商品推荐 -->
<recommended />
<!-- 底部选项卡 -->
<view class="footer-fixed">
<view class="footer-container">
<!-- 导航图标 -->
<view class="foo-item-fast">
<!-- 首页 -->
<view class="fast-item fast-item--home" @click="onTargetHome">
<view class="fast-icon">
<text class="iconfont icon-shouye"></text>
</view>
<view class="fast-text">
<text>站点</text>
</view>
</view>
<!-- #ifdef MP-WEIXIN -->
<button class="btn-normal" @click="onCollection(goods.goods_id)">
<view class="fast-item">
<view class="fast-icon">
<u-icon name="star-fill" color="#fd8008" v-if="goods.is_collection"></u-icon>
<u-icon name="star" v-else></u-icon>
</view>
<view class="fast-text">
<text>收藏</text>
</view>
</view>
</button>
<!-- #endif -->
<!-- 客服 (仅微信小程序端显示) -->
<!-- #ifdef MP-WEIXIN -->
<!-- <button class="btn-normal" open-type="contact">
<view class="fast-item">
<view class="fast-icon">
<text class="iconfont icon-kefu1"></text>
</view>
<view class="fast-text">
<text>客服</text>
</view>
</view>
</button> -->
<!-- #endif -->
<!-- 购物车 -->
<view class="fast-item fast-item--cart" @click="onTargetCart">
<view v-if="cartTotal > 0" class="fast-badge fast-badge--fixed">{{ cartTotal > 99 ? '99+' : cartTotal }}
</view>
<view class="fast-icon">
<text class="iconfont icon-gouwuche"></text>
</view>
<view class="fast-text">
<text>购物车</text>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="foo-item-btn">
<view class="btn-wrapper">
<view class="btn-item btn-item-deputy" @click="onShowSkuPopup(2)">
<text>加入购物车</text>
</view>
<view class="btn-item btn-item-main" @click="onShowSkuPopup(3)">
<text>立即购买</text>
</view>
</view>
</view>
</view>
</view>
<!-- 快捷导航 -->
<!-- <shortcut bottom="120rpx" /> -->
<!-- 分享菜单 -->
<share-sheet v-model="showShareSheet" :shareTitle="goods.goods_name" :shareImageUrl="goods.goods_image"
:posterApiCall="posterApiCall" :posterApiParam="{ goodsId }" />
</view>
</template>
<script>
import { getSceneData } from '@/core/app'
import * as GoodsApi from '@/api/goods'
import * as CartApi from '@/api/cart'
import Recommended from '@/components/recommended'
import ShareSheet from '@/components/share-sheet'
import SlideImage from './components/SlideImage'
import SkuPopup from './components/SkuPopup'
import Comment from './components/Comment'
import Service from './components/Service'
export default {
components: {
Recommended,
ShareSheet,
SlideImage,
SkuPopup,
Comment,
Service
},
data() {
return {
// 正在加载
isLoading: true,
// 当前商品ID
goodsId: null,
// 商品详情
goods: {},
// 购物车总数量
cartTotal: 0,
// 显示/隐藏SKU弹窗
showSkuPopup: false,
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
skuMode: 1,
// 显示/隐藏分享菜单
showShareSheet: false,
// 获取商品海报图api方法
posterApiCall: GoodsApi.poster
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 记录query参数
this.onRecordQuery(options)
// 加载页面数据
this.onRefreshPage()
// 浏览历史
this.saveFootMarkList(options.goodsId)
},
methods: {
// 浏览历史记录
saveFootMarkList(goodsId){
const userId = uni.getStorageSync('userId')
const arr = uni.getStorageSync(`FOOTMARKLIST_${userId}`) || []
arr.unshift(Number(goodsId))
return uni.setStorageSync(`FOOTMARKLIST_${userId}`,arr)
},
// 记录query参数
onRecordQuery(query) {
const scene = getSceneData(query)
this.goodsId = query.goodsId ? parseInt(query.goodsId) : parseInt(scene.gid)
},
// 刷新页面数据
onRefreshPage() {
const app = this
app.isLoading = true
Promise.all([app.getGoodsDetail(), app.getCartTotal()])
.finally(() => app.isLoading = false)
},
// 获取商品信息
getGoodsDetail() {
const app = this
return new Promise((resolve, reject) => {
GoodsApi.detail(app.goodsId)
.then(result => {
app.goods = result.data.detail
resolve(result)
})
.catch(reject)
})
},
// 获取购物车总数量
getCartTotal() {
const app = this
return new Promise((resolve, reject) => {
CartApi.total()
.then(result => {
app.cartTotal = result.data.cartTotal
resolve(result)
})
.catch(reject)
})
},
// 更新购物车数量
onAddCart(total) {
this.cartTotal = total
},
/**
* 显示/隐藏SKU弹窗
* @param {skuMode} 模式 1:都显示 2:只显示购物车 3:只显示立即购买
*/
onShowSkuPopup(skuMode = 1) {
const shopInfo = uni.getStorageSync('shopInfo')
const clerkInfo = uni.getStorageSync('clerkInfo')
if(shopInfo || clerkInfo){
return this.$error('请使用会员账号购买')
}
this.skuMode = skuMode
this.showSkuPopup = !this.showSkuPopup
},
// 显示隐藏分享菜单
onShowShareSheet() {
this.showShareSheet = !this.showShareSheet
},
// 跳转到首页
onTargetHome(e) {
const { goods } = this
console.log("this.goods: ",goods);
this.$navTo('package/shops/detail/index',goods)
},
// 加入收藏
onCollection(goodsId){
GoodsApi.collection({goodsId})
this.goods.is_collection = !this.goods.is_collection
},
// 跳转到购物车页
onTargetCart() {
this.$navTo('pages/cart/index')
},
},
/**
* 分享当前页面
*/
onShareAppMessage() {
const app = this
// 构建页面参数
const params = app.$getShareUrlParams({
goodsId: app.goodsId,
})
return {
title: app.goods.goods_name,
path: `/pages/goods/detail?${params}`
}
},
/**
* 分享到朋友圈
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta)
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
*/
onShareTimeline() {
const app = this
// 构建页面参数
const params = app.$getShareUrlParams({
goodsId: app.goodsId,
})
return {
title: app.goods.goods_name,
path: `/pages/goods/detail?${params}`
}
}
}
</script>
<style>
page {
background: #fafafa;
}
</style>
<style lang="scss" scoped>
@import "./detail.scss";
</style>

659
package/user/goods/list.vue Executable file
View File

@@ -0,0 +1,659 @@
<template>
<view class="container" :style="appThemeStyle">
<mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ native: true }"
@down="downCallback" :up="upOption" @up="upCallback">
<!-- 页面头部 -->
<view class="header">
<view class="search">
<input v-model="searchValue" class="input" focus="true" placeholder="请输入您搜索的商品" type="text" @input="onInput" @confirm="onSearch"></input>
</view>
<u-dropdown>
<u-dropdown-item v-model="parentId" :title="parentName" :options="parentList"></u-dropdown-item>
<u-dropdown-item v-if="childrenList.length > 0" v-model="categoryId" :title="categoryName" :options="childrenList"></u-dropdown-item>
<u-dropdown-item v-if="thirdList.length > 0" v-model="thirdId" :title="thirdName" :options="thirdList"></u-dropdown-item>
</u-dropdown>
</view>
<!-- {{parentId}}
{{categoryId}}
{{thirdId}}
{{JSON.stringify(childrenList)}}
{{JSON.stringify(thirdList)}} -->
<!-- 排序标签 -->
<!-- <view class="store-sort">
<view class="sort-item" :class="{ active: sortType === 'all' }" @click="handleSortType('all')">
<text>综合</text>
</view>
<view class="sort-item" :class="{ active: sortType === 'sales' }" @click="handleSortType('sales')">
<text>销量</text>
</view>
<view class="sort-item sort-item-price" :class="{ active: sortType === 'price' }" @click="handleSortType('price')">
<text>价格</text>
<view class="price-arrow">
<view class="icon up" :class="{ active: sortType === 'price' && !sortPrice }">
<text class="iconfont icon-arrow-up"></text>
</view>
<view class="icon down" :class="{ active: sortType === 'price' && sortPrice }">
<text class="iconfont icon-arrow-down"></text>
</view>
</view>
</view>
</view> -->
<!-- 商品列表 -->
<view class="goods-list clearfix" :class="['column-' + (showView ? '1' : '2')]">
<view class="goods-item" v-for="(item, index) in list.data" :key="index">
<!-- 单列显示 -->
<view v-if="showView" class="dis-flex">
<!-- 商品图片 -->
<view class="goods-item_left" @click="onTargetDetail(item.goods_id)">
<image class="image" :src="item.goods_image"></image>
</view>
<view class="goods-item_right">
<!-- 商品名称 -->
<view class="goods-name">
<text class="twoline-hide" @click="onTargetDetail(item.goods_id)">{{ item.goods_name }}</text>
</view>
<view class="goods-item_desc">
<!-- 商品卖点 -->
<view class="desc-selling_point dis-flex">
<text class="oneline-hide">{{ item.selling_point }}</text>
</view>
<!-- 商品销量 -->
<view class="desc-goods_sales dis-flex">
<text>已售{{ item.goods_sales }}</text>
</view>
<!-- 商品价格 -->
<view class="desc_footer">
<text class="price_x">¥{{ item.goods_price_min }}</text>
<text class="price_y col-9"
v-if="item.line_price_min > 0">¥{{ item.line_price_min }}</text>
</view>
</view>
<view class="buy-gouwuche" v-if="item.top_time == 0">
<u-checkbox v-model="item.checked" @change="checkboxChange(item.goods_id)">置顶</u-checkbox>
<!-- <u-button size="mini">置顶</u-button> -->
</view>
<view class="buy-gouwuche" v-else>
<u-tag text="投放中"></u-tag>
</view>
</view>
</view>
<!-- 多列显示 -->
<view v-else class="">
<!-- 商品图片 -->
<view class="goods-image">
<image class="image" mode="aspectFill" :src="item.goods_image"></image>
</view>
<view class="detail">
<!-- 商品名称 -->
<view class="goods-name">
<text class="twoline-hide">{{ item.goods_name }}</text>
</view>
<!-- 商品价格 -->
<view class="detail-price oneline-hide">
<text class="goods-price f-30 col-m">{{ item.goods_price_min }}</text>
<text v-if="item.line_price_min > 0"
class="line-price col-9 f-24">{{ item.line_price_min }}</text>
</view>
<!-- 商品销量 -->
<text class="col-9 f-24 u-text-right">已售{{ item.goods_sales }}</text>
</view>
</view>
</view>
</view>
<view class="gotobuy">
<u-button v-if="checked.length > 0" type="primary" @click="goToBuy">去付款</u-button>
</view>
</mescroll-body>
</view>
</template>
<script>
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import * as GoodsApi from '@/api/goods'
import * as CategoryApi from '@/api/category'
import {
getEmptyPaginateObj,
getMoreListData
} from '@/core/app'
import Search from './components/search.vue'
import storage from '@/utils/storage'
const pageSize = 15
const showViewKey = 'GoodsList-ShowView';
export default {
components: {
MescrollBody,
Search
},
mixins: [MescrollMixin],
data() {
return {
showView: true, // 列表显示方式 (true列表、false平铺)
sortType: 'all', // 排序类型
sortPrice: false, // 价格排序 (true高到低 false低到高)
options: {}, // 当前页面参数
list: getEmptyPaginateObj(), // 商品列表数据
searchValue: '',
// 上拉加载配置
upOption: {
// 首次自动执行
auto: true,
// 每页数据的数量; 默认10
page: {
size: pageSize
},
// 数量要大于4条才显示无更多数据
noMoreSize: 4,
},
parentId: 0,
parentName: '商品分类',
categoryId: 0,
categoryName: '请选择',
category: [],
parentList: [],
childrenList: [],
// 三级分类
thirdId: 0,
thirdName: '请选择',
thirdList: [],
checked: [],
time: ''
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 记录options
this.options = options
// 设置默认列表显示方式
this.setShowView()
// 加载商品分类
this.getCategory()
// 检查商品支付状态并设置置顶时间
// this.time = this.$u.timeFrom(1665049088);
// console.log("this.time: ",this.time);
},
watch: {
parentId(val) {
// 设置一级分类名称
this.parentList.map(d => {
if(d.value == val){
this.parentName = d.label
this.parentId = d.value
this.categoryId = 0
this.categoryName = '请选择'
this.thirdId = 0
d.children && this.getChildrenList(d.value)
}
})
},
categoryId(val){
this.childrenList.map(d => {
if(d.value == val){
this.categoryName = d.label
this.categoryId = d.value
this.thirdId = 0
d.children && this.getThirdList(d.value)
}
})
this.getGoodsList()
},
thirdId(val){
this.thirdList.map(d => {
if(d.value == val){
this.thirdName = d.label
this.thirdId = d.value
}
})
this.getGoodsList()
}
},
methods: {
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
// 设置列表数据
app.getGoodsList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
// 选择一级分类后刷新左侧菜单
getChildrenList(parentId){
const app = this
app.parentId = parentId
app.category.map(d => {
if(d.category_id == parentId){
app.childrenList = d.children.map(item => {
return {
value: item.category_id,
label: item.name,
children: item.children
}
})
}
})
},
// 三级分类
getThirdList(categoryId){
const app = this
app.categoryId = categoryId
app.childrenList.map(d => {
console.log(categoryId + "====",d.value);
if(d.value == categoryId){
app.thirdList = d.children.map(d => {
return {
value: d.category_id,
label: d.name,
}
})
}
})
},
// 设置默认列表显示方式
setShowView() {
this.showView = uni.getStorageSync(showViewKey) || true
},
onInput(e){
this.searchValue = e.detail.value
this.options.search = e.detail.value
},
onSearch(){
this.getGoodsList()
},
// 收集选中的goodsId
checkboxChange(goodsId){
const app = this
// 查找数组是否含该元素并返回下标位置
var index = app.checked.indexOf(goodsId)
// 存在则删除不存在则插入
if( index > -1){
app.checked.splice(index,1)
}else{
app.checked.push(goodsId)
}
storage.set('selectGoodsIdsByPopularize', app.checked)
},
goToBuy(){
this.$navTo('package/user/goods/cashier')
},
getCategory(){
CategoryApi.list().then(result => {
this.category = result.data.list
// 抽取一级分类
this.parentList = result.data.list.map(d => {
if(d.parent_id == 0){
return {
value: d.category_id,
label: d.name,
children: d.children
}
}
})
// console.log('顶部一级分类:',this.parentList);
// 左侧二级分类
// result.data.list.map(d => {
// if(d.category_id == this.parentId){
// this.childrenList = d.children
// }
// })
// console.log(": ",this.childrenList);
})
},
/**
* 获取商品列表
* @param {number} pageNo 页码
*/
getGoodsList(pageNo = 1) {
const app = this
const { thirdId, categoryId, parentId } = this
const { shopId } = uni.getStorageSync('shopInfo')
let lastId = 0;
if(parentId > 0) {
lastId = parentId
}
if(categoryId > 0) {
lastId = categoryId
}
if(thirdId > 0){
lastId = thirdId
}
const param = {
sortType: app.sortType,
sortPrice: Number(app.sortPrice),
categoryId: lastId || 0,
goodsName: app.options.search || '',
page: pageNo,
shopId,
// storeHouseGoods: true,
}
return new Promise((resolve, reject) => {
GoodsApi.list(param)
.then(result => {
// 合并新数据
const newList = result.data.list
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
.catch(reject)
})
},
// 切换排序方式
handleSortType(newSortType) {
const app = this
const newSortPrice = newSortType === 'price' ? !app.sortPrice : true
app.sortType = newSortType
app.sortPrice = newSortPrice
// 刷新列表数据
app.list = getEmptyPaginateObj()
app.mescroll.resetUpScroll()
},
// 切换列表显示方式
handleShowView() {
const app = this
app.showView = !app.showView
uni.setStorageSync(showViewKey, app.showView)
},
// 跳转商品详情页
onTargetDetail(goodsId) {
this.$navTo('pages/goods/detail', {
goodsId
})
},
/**
* 商品搜索
*/
handleSearch() {
const searchPageUrl = 'pages/search/index'
// 判断来源页面
let pages = getCurrentPages()
if (pages.length > 1 &&
pages[pages.length - 2].route === searchPageUrl) {
uni.navigateBack()
return
}
// 跳转到商品搜索页
this.$navTo(searchPageUrl)
}
},
/**
* 设置分享内容
*/
onShareAppMessage() {
// 构建分享参数
return {
title: "全部分类",
path: "/pages/category/index?" + this.$getShareUrlParams()
}
},
/**
* 分享到朋友圈
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta)
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
*/
onShareTimeline() {
// 构建分享参数
return {
title: "全部分类",
path: "/pages/category/index?" + this.$getShareUrlParams()
}
}
}
</script>
<style lang="scss" scoped>
// 页面头部
.header {
display: flex;
align-items: center;
background-color: #fff;
flex: 1;
// 搜索框
.search {
flex: 1;
padding: 0 20rpx;
}
// 切换显示方式
.show-view {
width: 60rpx;
height: 60rpx;
font-size: 36rpx;
color: #505050;
display: flex;
align-items: center;
}
}
// 排序组件
.store-sort {
position: sticky;
top: var(--window-top);
display: flex;
padding: 20rpx 0;
font-size: 28rpx;
background: #fff;
color: #000;
z-index: 99;
.sort-item {
flex-basis: 33.3333%;
display: flex;
justify-content: center;
align-items: center;
height: 50rpx;
&.active {
color: $main-bg;
}
}
.sort-item-price .price-arrow {
margin-left: 20rpx;
font-size: 24rpx;
color: #000;
.icon {
&.active {
color: $main-bg;
}
&.up {
margin-bottom: -16rpx;
}
&.down {
margin-top: -16rpx;
}
}
}
}
// 商品列表
.goods-list {
padding: 4rpx;
box-sizing: border-box;
}
// 单列显示
.goods-list.column-1 {
.goods-item {
width: 100%;
height: 280rpx;
margin-bottom: 12rpx;
padding: 20rpx;
box-sizing: border-box;
background: #fff;
line-height: 1.6;
&:last-child {
margin-bottom: 0;
}
}
.goods-item_left {
display: flex;
width: 300rpx;
background: #fff;
align-items: center;
.image {
display: block;
width: 240rpx;
height: 240rpx;
}
}
.goods-item_right {
position: relative;
flex: 1;
.goods-name {
margin-top: 10rpx;
min-height: 68rpx;
line-height: 1.3;
white-space: normal;
color: #484848;
font-size: 28rpx;
font-weight: bold;
}
.buy-gouwuche{
position: absolute;
right: 10rpx;
bottom: 10rpx;
}
}
.goods-item_desc {
margin-top: 8rpx;
}
.desc-selling_point {
width: 400rpx;
font-size: 24rpx;
color: #e49a3d;
}
.desc-goods_sales {
color: #999;
font-size: 24rpx;
}
.desc_footer {
font-size: 24rpx;
.price_x {
margin-right: 16rpx;
color: $main-bg;
font-size: 30rpx;
}
.price_y {
text-decoration: line-through;
}
}
}
// 平铺显示
.goods-list.column-2 {
.goods-item {
width: 50%;
}
}
.goods-item {
float: left;
box-sizing: border-box;
padding: 6rpx;
.goods-image {
position: relative;
width: 100%;
height: 0;
padding-bottom: 100%;
overflow: hidden;
background: #fff;
&:after {
content: '';
display: block;
margin-top: 100%;
}
.image {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
-o-object-fit: cover;
object-fit: cover;
}
}
.detail {
padding: 8rpx;
background: #fff;
.goods-name {
min-height: 68rpx;
line-height: 1.3;
white-space: normal;
color: #484848;
font-size: 26rpx;
margin-bottom: 4rpx;
}
.detail-price {
.goods-price {
margin-right: 8rpx;
}
.line-price {
text-decoration: line-through;
}
}
}
}
.gotobuy{
position: fixed;
width: 700rpx;
margin-left: 25rpx;
bottom: 50rpx;
}
</style>

View File

@@ -0,0 +1,109 @@
<template>
<view class="partner">
<!-- 表单组件 -->
<view class="form-wrapper" v-if="shopInfo">
<!-- <u-form :model="form" ref="uForm" label-width="240rpx">
<u-form-item>
<view class="shop-box">
<text class="shop-name">后台管理</text>
<text>地址https://dev.gxwebsoft.com/ylx-admin/</text>
<text>账号{{applyInfo.phone}}</text>
<text>密码默认为手机号码后6位</text>
</view>
</u-form-item>
</u-form> -->
</view>
<!-- 操作按钮 -->
<!-- <view class="footer">
<view class="btn">
<u-button type="primary" shape="circle">更新资料</u-button>
</view>
</view> -->
</view>
</template>
<script>
// 表单字段元素
const form = {
// 入驻类型 合伙人10001,配送员10002,社区门店10003,省代10004
grade_id: 10004,
company: '',
real_name: '',
phone: '',
mobile: '',
region: [],
detail: '',
}
import * as UserApi from '@/api/user'
import SelectRegion from '@/components/select-region/select-region'
export default {
components: {
SelectRegion
},
data() {
return {
form,
disabled: false,
imageList: [],
shopInfo: {},
applyInfo: {}
}
},
onLoad() {
this.getUserInfo()
},
methods: {
// 获取当前用户信息
getUserInfo() {
const app = this
UserApi.info().then(result => {
app.shopInfo = result.data.shopInfo
app.applyInfo = result.data.applyInfo
})
},
groupChange(){
}
}
}
</script>
<style lang="less">
.partner {
.banner {
padding-top: 20rpx;
width: 100%;
}
.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;
.shop-box{
display: flex;
flex-direction: column;
.shop-name{
font-weight: bold;
font-size: 36rpx;
}
}
.logo{
width: 100rpx;
}
.shipin{
width: 400rpx;
}
}
.footer{
width: 500rpx;
margin: 20rpx auto;
}
}
</style>

521
package/user/order/index.vue Executable file
View File

@@ -0,0 +1,521 @@
<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.data" :key="index">
<view class="item-top">
<view class="item-top-left">
<text class="order-time">{{ item.create_time }}</text>
</view>
<view class="item-top-right">
<text class="state-text">{{ item.state_text }}</text>
</view>
</view>
<!-- 商品列表 -->
<view class="goods-list">
<view class="goods-item" v-for="(goods, idx) in item.goods" :key="idx">
<!-- 商品图片 -->
<view class="goods-image">
<image class="image" :src="goods.goods_image" mode="scaleToFill"></image>
</view>
<!-- 商品信息 -->
<view class="goods-content">
<view class="goods-title"><text class="twoline-hide">{{ goods.goods_name }}</text></view>
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in goods.goods_props" :key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
</view>
<!-- 交易信息 -->
<view class="goods-trade">
<view class="goods-price">
<text class="unit"></text>
<text class="value">{{ goods.is_user_grade ? goods.grade_goods_price : goods.goods_price }}</text>
</view>
<view class="goods-num">
<text>×{{ goods.total_num }}</text>
</view>
</view>
</view>
</view>
<!-- 订单合计 -->
<view class="order-total">
<text>{{ item.total_num }}件商品总金额</text>
<text class="unit"></text>
<text class="money">{{ item.pay_price }}</text>
</view>
<!-- 订单操作 -->
<view v-if="item.order_status != OrderStatusEnum.CANCELLED.value" class="order-handle">
<view class="btn-group clearfix">
<!-- 未支付取消订单 -->
<!-- <block v-if="item.pay_status == PayStatusEnum.PENDING.value">
<view class="btn-item" @click="onCancel(item.order_id)">取消</view>
</block> -->
<!-- 已支付进行中的订单 -->
<!-- <block v-if="item.order_status != OrderStatusEnum.APPLY_CANCEL.value"> -->
<!-- <block v-if="item.pay_status == PayStatusEnum.SUCCESS.value && item.delivery_status == DeliveryStatusEnum.NOT_DELIVERED.value">
<u-button type="primary" size="mini" class="btn-item" @click="onDelivery(item.order_id)">配送</u-button>
</block> -->
<!-- 订单核销码 -->
<!-- <block v-if="item.pay_status == PayStatusEnum.SUCCESS.value && item.delivery_type == DeliveryTypeEnum.EXTRACT.value
&& item.delivery_status == DeliveryStatusEnum.NOT_DELIVERED.value">
<view class="btn-item active" @click="onExtractQRCode(item.order_id)">
<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.pay_status == PayStatusEnum.PENDING.value">
<view class="btn-item active" @click="onPay(item.order_id)">去支付</view>
</block> -->
<!-- 确认收货 -->
<!-- <block v-if="item.delivery_status == DeliveryStatusEnum.DELIVERED.value && item.receipt_status == ReceiptStatusEnum.NOT_RECEIVED.value">
<view class="btn-item active" @click="onReceipt(item.order_id)">确认收货</view>
</block> -->
</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>
</view>
</template>
<script>
import {
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum
} from '@/common/enum/order'
import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
import { getEmptyPaginateObj, getMoreListData } from '@/core/app'
import * as OrderApi from '@/api/order'
import * as UserApi from '@/api/user'
import { wxPayment } from '@/core/app'
// 每页记录数量
const pageSize = 15
// tab栏数据
const tabs = [{
name: `全部`,
value: 'all'
}, {
name: `待支付`,
value: 'payment'
}, {
name: `待发货`,
value: 'delivery'
}, {
name: `待收货`,
value: 'received'
}, {
name: `待评价`,
value: 'comment'
}]
export default {
components: {
MescrollBody
},
mixins: [MescrollMixin],
data() {
return {
// 枚举类
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum,
// 当前页面参数
options: { dataType: 'all' },
// tab栏数据
tabs,
// 当前标签索引
curTab: 0,
// 订单列表数据
list: getEmptyPaginateObj(),
// 上拉加载配置
upOption: {
// 首次自动执行
auto: true,
// 每页数据的数量; 默认10
page: { size: pageSize },
// 数量要大于4条才显示无更多数据
noMoreSize: 4,
// 空布局
empty: {
tip: '亲,暂无订单记录'
}
},
// 控制首次触发onShow事件时不刷新列表
canReset: false,
// 核销二维码弹窗
showQRCodePopup: false,
// 核销二维码图片url (通过后端获取)
qrcodeImage: '',
shopInfo: {},
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 初始化当前选中的标签
this.initCurTab(options)
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
this.canReset && this.onRefreshList()
this.canReset = true
},
methods: {
// 初始化当前选中的标签
initCurTab(options) {
const app = this
if (options.dataType) {
const index = app.tabs.findIndex(item => item.value == options.dataType)
app.curTab = index > -1 ? index : 0
}
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
// 设置列表数据
app.getOrderList(page.num)
.then(list => {
const curPageLen = list.data.length
const totalSize = list.data.total
app.mescroll.endBySize(curPageLen, totalSize)
})
.catch(() => app.mescroll.endErr())
},
// 获取订单列表
getOrderList(pageNo = 1) {
const app = this
const shopInfo = uni.getStorageSync('shopInfo')
return new Promise((resolve, reject) => {
OrderApi.list({ dataType: app.getTabValue(), page: pageNo, byShopId: shopInfo.shopId }, { load: false })
.then(result => {
// 合并新数据
const newList = app.initList(result.data.list)
app.list.data = getMoreListData(newList, app.list, pageNo)
resolve(newList)
})
})
},
// 初始化订单列表数据
initList(newList) {
newList.data.forEach(item => {
item.total_num = 0
item.goods.forEach(goods => {
item.total_num += goods.total_num
})
})
return newList
},
// 获取当前标签项的值
getTabValue() {
return this.tabs[this.curTab].value
},
// 切换标签项
onChangeTab(index) {
const app = this
// 设置当前选中的标签
app.curTab = index
// 刷新订单列表
app.onRefreshList()
},
// 刷新订单列表
onRefreshList() {
this.list = getEmptyPaginateObj()
setTimeout(() => {
this.mescroll.resetUpScroll()
}, 120)
},
// 取消订单
onCancel(orderId) {
const app = this
uni.showModal({
title: '友情提示',
content: '确认要取消该订单吗?',
success(o) {
if (o.confirm) {
OrderApi.cancel(orderId)
.then(result => {
// 显示成功信息
app.$toast(result.message)
// 刷新订单列表
app.onRefreshList()
})
}
}
});
},
onDelivery(orderId){
return this.$navTo('package/dealer/driver', { orderId })
console.log(orderId);
return false
},
// 确认收货
onReceipt(orderId) {
const app = this
uni.showModal({
title: '友情提示',
content: '确认收到商品了吗?',
success(o) {
if (o.confirm) {
OrderApi.receipt(orderId)
.then(result => {
// 显示成功信息
app.$success(result.message)
// 刷新订单列表
app.onRefreshList()
})
}
}
});
},
// 获取核销二维码
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 })
}
},
}
</script>
<style lang="scss" scoped>
// 项目内容
.order-item {
margin: 20rpx auto 20rpx auto;
padding: 30rpx 30rpx;
width: 94%;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
border-radius: 16rpx;
background: #fff;
}
// 项目顶部
.item-top {
display: flex;
justify-content: space-between;
font-size: 26rpx;
margin-bottom: 40rpx;
.order-time {
color: #777;
}
.state-text {
color: $main-bg;
}
}
// 商品列表
.goods-list {
// 商品项
.goods-item {
display: flex;
margin-bottom: 40rpx;
// 商品图片
.goods-image {
width: 180rpx;
height: 180rpx;
.image {
display: block;
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
// 商品内容
.goods-content {
flex: 1;
padding-left: 16rpx;
padding-top: 16rpx;
.goods-title {
font-size: 26rpx;
max-height: 76rpx;
}
.goods-props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
.goods-props-item {
display: inline-block;
margin-right: 14rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
width: auto;
}
}
}
// 交易信息
.goods-trade {
padding-top: 16rpx;
width: 150rpx;
text-align: right;
color: $uni-text-color-grey;
font-size: 26rpx;
.goods-price {
vertical-align: bottom;
margin-bottom: 16rpx;
.unit {
margin-right: -2rpx;
font-size: 24rpx;
}
}
}
}
}
// 订单合计
.order-total {
font-size: 26rpx;
vertical-align: bottom;
text-align: right;
height: 40rpx;
margin-bottom: 30rpx;
.unit {
margin-left: 8rpx;
margin-right: -2rpx;
font-size: 26rpx;
}
.money {
font-size: 28rpx;
}
}
// 订单操作
.order-handle {
.btn-group {
.btn-item {
border-radius: 10rpx;
padding: 6rpx 20rpx;
margin-left: 15rpx;
font-size: 28rpx;
float: right;
&: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;
}
}
}
</style>

View File

@@ -0,0 +1,402 @@
<template>
<view class="container" :style="appThemeStyle">
<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="item-prices oneline-hide">
<text class="goods-sales">已售{{ item.goods_sales }}</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>
<view class="buy-gouwuche">
<u-button size="mini" @click="handleAddCart(item)">取消收藏
</u-button>
</view>
</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: []
}
},
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
}
},
sortPrice: false, // 价格排序 (true高到低 false低到高)
sortType: 'hot', // 排序类型
}
},
created() {
// 设置分类列表高度
this.setListHeight()
},
methods: {
// 切换排序方式
handleSortType(newSortType) {
const app = this
const newSortPrice = newSortType === 'price' ? !app.sortPrice : true
app.sortType = newSortType
app.sortPrice = newSortPrice
// 刷新列表数据
app.getGoodsList()
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
// 设置列表数据
console.log(page.num);
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,
collection: true
}, {
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) {
GoodsApi.collection({goodsId: item.})
this.$emit('addCart')
},
// 更新购物车角标
onUpdateCartTabBadge() {
console.log('onUpdateCartTabBadge')
setCartTabBadge()
},
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #F6F6F6 !important;
}
// 分类内容
.cate-content {
z-index: 1;
background-color: #F6F6F6 !important;
padding-top: 0rpx;
}
// 一级分类+二级分类 20
.cate-left {
width: 173rpx;
height: 100%;
background: #f8f8f8;
color: #444;
position: fixed;
top: calc(286rpx + 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 {
padding: 20rpx;
}
.goods-item {
padding: 28rpx 22rpx;
display: flex;
box-sizing: border-box;
padding: 20rpx;
border: 1px solid #F6F6F6;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 12rpx;
}
.goods-item_left {
position: relative;
background: #fff;
margin-right: 20rpx;
.image {
display: block;
width: 280rpx;
height: 280rpx;
}
}
.goods-item_right {
position: relative;
flex: 1;
.goods-name {
display: block;
width: 100%;
min-height: 68rpx;
font-size: 34rpx;
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: flex-end;
position: absolute;
right: 0rpx;
bottom: 0rpx;
min-height: 44rpx;
.item-prices {
padding-right: 6rpx;
.price_x {
margin-right: 14rpx;
font-weight: 600;
color: #ff0000;
font-size: 38rpx;
}
.price_y {
color: #999;
text-decoration: line-through;
font-size: 24rpx;
}
}
}
}
.goods-sales {
padding-top: 8rpx !important;
color: #b3b3b3 !important;
font-size: 24rpx;
}
.search-box {
display: flex;
justify-content: space-around;
background: #fff;
border-radius: 14rpx;
padding: 20rpx 4rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.07);
}
// 排序组件
.store-sort {
margin-top: 24rpx;
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;
}
}
}
}
</style>

View File

@@ -0,0 +1,404 @@
<template>
<view class="container" :style="appThemeStyle">
<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="item-prices oneline-hide">
<text class="goods-sales">已售{{ item.goods_sales }}</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>
<view class="buy-gouwuche">
<u-button type="primary" size="mini">置顶</u-button>
</view>
</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: []
}
},
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
}
},
sortPrice: false, // 价格排序 (true高到低 false低到高)
sortType: 'hot', // 排序类型
}
},
created() {
// 设置分类列表高度
this.setListHeight()
},
methods: {
// 切换排序方式
handleSortType(newSortType) {
const app = this
const newSortPrice = newSortType === 'price' ? !app.sortPrice : true
app.sortType = newSortType
app.sortPrice = newSortPrice
// 刷新列表数据
app.getGoodsList()
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
// 设置列表数据
console.log(page.num);
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
const { sortType } = this
const { shopId } = uni.getStorageSync('shopInfo')
return new Promise((resolve, reject) => {
GoodsApi.list({
categoryId,
page: pageNo,
sortType,
shopId
}, {
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)
this.$emit('addCart')
},
// 更新购物车角标
onUpdateCartTabBadge() {
console.log('onUpdateCartTabBadge')
setCartTabBadge()
},
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #F6F6F6 !important;
}
// 分类内容
.cate-content {
z-index: 1;
background-color: #F6F6F6 !important;
padding-top: 0rpx;
}
// 一级分类+二级分类 20
.cate-left {
width: 173rpx;
height: 100%;
background: #f8f8f8;
color: #444;
position: fixed;
top: calc(286rpx + 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 {
padding: 20rpx;
}
.goods-item {
padding: 28rpx 22rpx;
display: flex;
box-sizing: border-box;
padding: 20rpx;
border: 1px solid #F6F6F6;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 12rpx;
}
.goods-item_left {
position: relative;
background: #fff;
margin-right: 20rpx;
.image {
display: block;
width: 280rpx;
height: 280rpx;
}
}
.goods-item_right {
position: relative;
flex: 1;
.goods-name {
display: block;
width: 100%;
min-height: 68rpx;
font-size: 34rpx;
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: flex-end;
position: absolute;
right: 0rpx;
bottom: 0rpx;
min-height: 44rpx;
.item-prices {
padding-right: 6rpx;
.price_x {
margin-right: 14rpx;
font-weight: 600;
color: #ff0000;
font-size: 38rpx;
}
.price_y {
color: #999;
text-decoration: line-through;
font-size: 24rpx;
}
}
}
}
.goods-sales {
padding-top: 8rpx !important;
color: #b3b3b3 !important;
font-size: 24rpx;
}
.search-box {
display: flex;
justify-content: space-around;
background: #fff;
border-radius: 14rpx;
padding: 20rpx 4rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.07);
}
// 排序组件
.store-sort {
margin-top: 24rpx;
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;
}
}
}
}
</style>

395
package/user/popularize/index.vue Executable file
View File

@@ -0,0 +1,395 @@
<template>
<view class="container" :style="appThemeStyle">
<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="item-prices oneline-hide">
<text class="goods-sales">已售{{ item.goods_sales }}</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>
<view class="buy-gouwuche">
<u-button type="primary" size="mini">置顶</u-button>
</view>
</view>
</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,
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],
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
}
},
sortPrice: false, // 价格排序 (true高到低 false低到高)
sortType: 'hot', // 排序类型
}
},
created() {
// 设置分类列表高度
this.setListHeight()
},
methods: {
// 切换排序方式
handleSortType(newSortType) {
const app = this
const newSortPrice = newSortType === 'price' ? !app.sortPrice : true
app.sortType = newSortType
app.sortPrice = newSortPrice
// 刷新列表数据
app.getGoodsList()
},
/**
* 上拉加载的回调 (页面初始化时也会执行一次)
* 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
* @param {Object} page
*/
upCallback(page) {
const app = this
// 设置列表数据
console.log(page.num);
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
const { shopId } = uni.getStorageSync('shopInfo')
return new Promise((resolve, reject) => {
GoodsApi.list({
categoryId,
page: pageNo,
topGoods: true,
shopId
}, {
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) {
GoodsApi.collection({goodsId: item.goods_id}).then(() => {
this.getGoodsList()
})
},
// 更新购物车角标
onUpdateCartTabBadge() {
console.log('onUpdateCartTabBadge')
setCartTabBadge()
},
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #F6F6F6 !important;
}
// 分类内容
.cate-content {
z-index: 1;
background-color: #F6F6F6 !important;
padding-top: 0rpx;
}
// 一级分类+二级分类 20
.cate-left {
width: 173rpx;
height: 100%;
background: #f8f8f8;
color: #444;
position: fixed;
top: calc(286rpx + 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 {
padding: 20rpx;
}
.goods-item {
padding: 28rpx 22rpx;
display: flex;
box-sizing: border-box;
padding: 20rpx;
border: 1px solid #F6F6F6;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 12rpx;
}
.goods-item_left {
position: relative;
background: #fff;
margin-right: 20rpx;
.image {
display: block;
width: 280rpx;
height: 280rpx;
}
}
.goods-item_right {
position: relative;
flex: 1;
.goods-name {
display: block;
width: 100%;
min-height: 68rpx;
font-size: 34rpx;
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: flex-end;
position: absolute;
right: 0rpx;
bottom: 0rpx;
min-height: 44rpx;
.item-prices {
padding-right: 6rpx;
.price_x {
margin-right: 14rpx;
font-weight: 600;
color: #ff0000;
font-size: 38rpx;
}
.price_y {
color: #999;
text-decoration: line-through;
font-size: 24rpx;
}
}
}
}
.goods-sales {
padding-top: 8rpx !important;
color: #b3b3b3 !important;
font-size: 24rpx;
}
.search-box {
display: flex;
justify-content: space-around;
background: #fff;
border-radius: 14rpx;
padding: 20rpx 4rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.07);
}
// 排序组件
.store-sort {
margin-top: 24rpx;
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;
}
}
}
}
</style>

View File

@@ -0,0 +1,90 @@
<template>
<view class="partner">
<!-- 表单组件 -->
<view v-if="userInfo" class="form-wrapper">
<u-form label-width="240rpx">
<u-form-item label="会员ID" prop="name">
{{userInfo.user_id}}
</u-form-item>
<u-form-item label="头像" prop="name">
<image :src="userInfo.avatar_url" mode="widthFix" class="logo"></image>
</u-form-item>
<u-form-item label="昵称" prop="name">
{{userInfo.nick_name}}
</u-form-item>
<u-form-item label="手机号码" prop="name">
{{userInfo.mobile}}
</u-form-item>
</u-form>
</view>
<!-- 操作按钮 -->
<!-- <view class="footer">
<view class="btn">
<u-button type="primary" shape="circle">更新资料</u-button>
</view>
</view> -->
</view>
</template>
<script>
import * as UserApi from '@/api/user'
import SelectRegion from '@/components/select-region/select-region'
export default {
components: {
SelectRegion
},
data() {
return {
disabled: false,
userInfo: null,
}
},
onLoad() {
this.getUserInfo()
},
methods: {
// 获取当前用户信息
getUserInfo() {
const app = this
const userInfo = uni.getStorageSync('userInfo')
if(userInfo){
app.userInfo = userInfo
return
}
UserApi.info().then(result => {
app.userInfo = result.data.userInfo
})
},
groupChange(){
}
}
}
</script>
<style lang="less">
.partner {
.banner {
width: 100%;
}
.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;
.logo{
width: 100rpx;
}
.shipin{
width: 400rpx;
}
}
.footer{
width: 500rpx;
margin: 20rpx auto;
}
}
</style>

116
package/user/shop/index.vue Normal file
View File

@@ -0,0 +1,116 @@
<template>
<view class="partner">
<!-- 表单组件 -->
<view class="form-wrapper" v-if="shopInfo">
<u-form :model="form" ref="uForm" label-width="240rpx">
<u-form-item>
<image :src="shopInfo.backgroundUrl" mode="widthFix" class="banner"></image>
</u-form-item>
<u-form-item>
<view class="shop-box">
<text class="shop-name">{{shopInfo.shopName}}</text>
<text v-if="shopInfo.region"><u-icon name="map" :size="26"></u-icon>{{shopInfo.region.province}}{{shopInfo.region.city}}{{shopInfo.region.region}}{{shopInfo.address}}</text>
</view>
</u-form-item>
<u-form-item>
<view class="shop-box">
<text class="shop-name">站点简介</text>
<text>{{shopInfo.summary}}</text>
</view>
</u-form-item>
</u-form>
</view>
<!-- 操作按钮 -->
<!-- <view class="footer">
<view class="btn">
<u-button type="primary" shape="circle">更新资料</u-button>
</view>
</view> -->
</view>
</template>
<script>
// 表单字段元素
const form = {
// 入驻类型 合伙人10001,配送员10002,社区门店10003,省代10004
grade_id: 10004,
company: '',
real_name: '',
phone: '',
mobile: '',
region: [],
detail: '',
}
import * as UserApi from '@/api/user'
import SelectRegion from '@/components/select-region/select-region'
export default {
components: {
SelectRegion
},
data() {
return {
form,
disabled: false,
imageList: [],
shopInfo: {},
applyInfo: {}
}
},
onLoad() {
this.getUserInfo()
},
methods: {
// 获取当前用户信息
getUserInfo() {
const app = this
UserApi.info().then(result => {
app.shopInfo = result.data.shopInfo
app.applyInfo = result.data.applyInfo
})
},
groupChange(){
}
}
}
</script>
<style lang="less">
.partner {
.banner {
padding-top: 20rpx;
width: 100%;
}
.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;
.shop-box{
display: flex;
flex-direction: column;
.shop-name{
font-weight: bold;
font-size: 36rpx;
}
}
.logo{
width: 100rpx;
}
.shipin{
width: 400rpx;
}
}
.footer{
width: 500rpx;
margin: 20rpx auto;
}
}
</style>