Commit 5d9e1f63 authored by June's avatar June

feature:购物车,弹窗组件,搜索列表,地址管理,确认订单

parent 6cc8edd2
......@@ -14,25 +14,19 @@ export default {
this.$store.state.user.appid = extConfig.appid
const token = this.$getStorage('token')
this.$store.state.user.token = token
this.getStoreInfo()
this.getStoreInfo() // 这个接口一定要先获取,因为如果店铺过期,有一个接口返回-11,后续所有接口不会再发起请求
// wx.login
login_wx()
if(token) {
this.setCount()
}
uni.setEnableDebug({
enableDebug: true
})
// uni.setEnableDebug({
// enableDebug: true
// })
},
methods: {
...mapActions('systemInfo', ['setSystemInfo', 'setMenuButtonInfo']),
...mapActions('user', ['setToken', 'setLoginStatus', 'getStoreInfo']),
...mapActions('cart', ['setCount']),
// 检查更新
checkUpdate() {
......@@ -63,7 +57,7 @@ export default {
}
</script>
<style>
<style lang="scss">
@import url("@/styles/layout.css");
/* 隐藏scrollbar 滚动条 */
......@@ -73,4 +67,7 @@ export default {
height:0;
color:transparent;
}
.primaryColor {
color: $primary!important;
}
</style>
......@@ -19,13 +19,11 @@ export function queryCity(code = '1') {
* @desc 浏览历史接口
* @param { String } last_time 上一次查询时间(yyyy-MM-dd)
*/
export function historyList() {
export function historyList(last_time) {
return request({
url: 'zsxcx/browse.htm',
method: 'post',
data: {
last_time
},
data: {last_time},
needMask: true
})
}
......@@ -36,7 +34,7 @@ export function historyList() {
* @param { Number } bro_id 浏览历史ID
* @param { Number } goods_id 商品ID
*/
export function delHistory() {
export function delHistory({bro_id, goods_id}) {
return request({
url: 'zsxcx/delBrowse.htm',
method: 'post',
......@@ -48,6 +46,17 @@ export function delHistory() {
})
}
/**
* @desc 清空全部历史
*/
export function clearAllHistory() {
return request({
url: 'zsxcx/delBrowseAll.htm',
method: 'post',
needMask: true
})
}
/**
* @desc 获取店铺信息
*/
......
// 订单相关
import { request } from "@/lib/service"
import { baseUrl } from '@/config'
/**
* @desc 提交订单前数据确认接口
* buy_type * 购买类型:0默认加入购物车 1立即购买
* cart_ids 选中的购物车IDs,多个则使用逗号分隔(buy_type=0时必填)
* sku_ids SKUIDs,多个则使用逗号分隔(buy_type=1时必填)
* cart_numbers 购买数量s,多个则使用逗号分隔(buy_type=1时必填)
*/
export function preConfirmOrderData(params) {
return request({
url: 'zsxcx/orderDetail.htm',
method: 'post',
data: params
})
}
/**
* @desc 提交订单
* look up docs
*/
export function confirmOrder(params) {
return request({
url: 'zsxcx/orderInfoXcx.htm',
method: 'post',
data: params
})
}
/**
* @desc 获取订单列表
......
// 判断是否符合购买条件
function judgePay(skuData) {
for(let i = 0, j = skuData.length; i < j; i ++) {
if(skuData[i].counts && skuData[i].counts < skuData[i].origin_number_sku) {
return false
}
}
return true
}
export default {
judgePay
}
\ No newline at end of file
......@@ -18,14 +18,17 @@
<view
class="sku-item py-2"
v-for="(item, index) in skuData"
:key="item.sku_id"
:key="index"
>
<view class="w-100 flex j-between a-center mb-2">
<view class="flex-1">{{item.spec_str}}</view>
<view class="step-wrap">
<input-number
:defaultVal="item.counts"
:index="index"
:defaultVal="item.origin_number_sku ? item.origin_number_sku : item.counts"
:min="item.origin_number_sku"
:params="{
index
}"
@change="numberChange"
/>
</view>
......@@ -33,14 +36,28 @@
<view class="w-100 flex j-between a-center">
<view class="flex-1 text-left price-txt">
<!-- 如果不是阶梯价 ? 价格 === 0 ? 询价 : 固定售价 -->
<text v-if="item.is_tiered === '1'">{{item.tiered_pri}}</text>
<text v-else-if="item.is_tiered === '0' && item.price === '0'">询价</text>
<text v-else>{{item.price}}</text>
<template v-if="goods.is_inquiry">
<text>询价</text>
</template>
<template v-else>
<!-- <text v-if="item.is_tiered === '1'">{{item.tiered_pri}}</text>
<text v-else-if="item.is_tiered === '0' && item.price === '0'">询价</text>
<text v-else>{{item.price}}</text> -->
<price
:is_inquiry="goods.is_inquiry"
:price="item.price"
/>
<text class="originPrice ml-1">{{item.original_price}}</text>
</template>
<text class="originPrice ml-1">{{item.original_price}}</text>
</view>
<view class="flex-1 text-center">库存:{{item.inventory}}</view>
<view class="payCount text-center">{{item.origin_number_sku}}件起购</view>
<view class="flex-1 text-center">
<text v-if="item.inventory > 0">{{'库存:' + item.inventory}}</text>
<text v-else class="primaryColor">库存需咨询商家</text>
</view>
<view class="payCount text-center">
<text v-if="~~item.origin_number_sku">{{item.origin_number_sku}}{{item.unit}}起购</text>
</view>
</view>
</view>
</scroll-view>
......@@ -48,7 +65,7 @@
<view class="flex w-100 a-center flex-column font-28 mt-2">
<view class="as-end">
<text class="btn-theme">{{totalCounts}}</text>
<text class=" ml-2 price btn-theme">{{totalPrice}}</text>
<text class=" ml-2 price btn-theme">¥{{goods.is_inquiry ? '--' : totalPrice}}</text>
</view>
</view>
......@@ -66,6 +83,7 @@ import uniPopup from '@/components/uni-popup/index.vue'
import price from '@/components/price/index.vue'
import inputNumber from '@/components/inputNumber/index.vue'
import { addCart } from '@/apis/carts.js'
import common from './common.js'
import { mapActions } from 'vuex'
export default {
name: 'goods-popup',
......@@ -74,7 +92,7 @@ export default {
return {
goods: null,
skuData: [],
currentSku: null, // 一次只能提交一个规格到购物车: 0,
// currentSku: null, // 一次只能提交一个规格到购物车: 0,
totalCounts: 0,
totalPrice: 0
}
......@@ -101,6 +119,7 @@ export default {
},
methods: {
...common,
...mapActions('cart', ['setCount']),
/**
* @param {Object} params
......@@ -115,8 +134,9 @@ export default {
console.log(params)
this.goods = params.goodsInfo
this.skuData = params.skuData
this.skuData.forEach(item => item.counts = 0)
this.skuData.forEach(item => item.counts = item.origin_number_sku || 1)
this.$refs.goodsPopup.open()
this.total()
}catch(e){
console.log(e)
this.$toast({title: e.msg || '程序错误'})
......@@ -128,53 +148,82 @@ export default {
this.$refs.goodsPopup.close()
this.showSku = false
this.detail = null
this.currentSku = null
// this.currentSku = null
this.skuData = []
this.goods = null
},
numberChange(e) {
console.log(e)
const skuData = this.skuData
const idx = e.index
// const skuData = this.skuData
const idx = e.params.index
const value = e.val
this.currentSku = {
goods_id: skuData[idx].goods_id,
cart_number: value,
sku_id: skuData[idx].sku_id
}
// this.currentSku = {
// goods_id: skuData[idx].goods_id,
// cart_number: value,
// sku_id: skuData[idx].sku_id
// }
this.$set(this.skuData[idx], 'counts', value)
this.total()
},
total() {
const skuData = this.skuData
this.totalCounts = skuData.reduce((pre, cur) => { return pre + cur.counts}, 0)
this.totalPrice = skuData.reduce((pre, cur) => { return pre + ~~cur.price * cur.counts }, 0)
this.totalCounts = skuData.reduce((pre, cur) => { return pre + ~~cur.counts}, 0)
this.totalPrice = skuData.reduce((pre, cur) => { return pre + ~~cur.price * ~~cur.counts }, 0)
},
// 加入购物车
async handleCart() {
console.log('33333333')
try{
console.log('00003333')
console.log(this.currentSku)
if(!this.currentSku) return
const { status, data } = await addCart(this.currentSku)
if(status) {
this.$toast({title: '加入购物车成功', cb: this.hide})
this.setCount()
handleCart() {
const skuData = this.skuData
if(!this.judgePay(skuData)) return this.$toast({title: '购买数量不能少于起购数量'})
const fn = []
for(let i = 0, j = skuData.length; i < j; i ++) {
if(skuData[i].counts) {
fn.push(addCart({
goods_id: skuData[i].goods_id,
cart_number: skuData[i].counts,
sku_id: skuData[i].sku_id
}))
}
}catch(e){
console.log(e)
this.$toast({title: '加入购物车失败'})
//TODO handle the exception
}
Promise.all(fn)
.then(() => {
this.$toast({title: '加入购物车成功', cb: this.hide})
this.setCount()
})
.catch(err => {
console.log(err)
this.$toast({title: '加入购物车失败'})
})
},
// 下单
handlePay() {
console.log('00000')
try{
const skuData = this.skuData
console.log(skuData)
if(skuData.length === 0) return
// if(this.totalPrice === 0) return this.$toast({title: '请选择规格数量'})
if(!this.judgePay(skuData)) return this.$toast({title: '购买数量不能少于起购数量'})
const sku_ids = []
const cart_numbers = []
for(let i = 0, j = skuData.length; i < j; i ++) {
if(skuData[i].counts) {
sku_ids.push(skuData[i].sku_id)
cart_numbers.push(skuData[i].counts)
}
}
const params = {
buy_type: 1,
sku_ids: sku_ids.join(','),
cart_numbers: cart_numbers.join(',')
}
uni.navigateTo({
url: `/pages/order/confirm-order?params=${encodeURIComponent(JSON.stringify(params))}`
})
}catch(e){
console.log(e)
}
}
}
}
......
......@@ -20,11 +20,12 @@
</template>
<script>
import { throttle } from '@/utils/common.js'
export default {
props: {
min: {
type: Number,
default: 1
default: 0
},
max: {
type: Number,
......@@ -34,9 +35,9 @@ export default {
type: Number,
default: 1
},
index: {
type: Number,
default: 0
params: {
type: Object,
default: () => {}
}
},
data() {
......@@ -45,12 +46,12 @@ export default {
}
},
watch: {
calVal(newVal) {
calVal: throttle(function(newVal) {
this.$emit('change', {
index: this.index,
params: this.params,
val: ~~newVal
})
}
}, 800)
},
methods: {
subtraction() {
......
......@@ -58,6 +58,10 @@ async function successRes(res, options, resolve, reject) {
})
})
break;
case -11: // 购买的服务过期
store.state.user.isOverdue = true
return Toast({title: data.rep_msg})
break;
default:
Toast({title: data.rep_msg})
reject({
......@@ -96,6 +100,15 @@ export function request (options) {
uni.showLoading({title: '加载中...', mask: true})
}
return new Promise((resolve, reject) => {
const isOverdue = store.state.user.isOverdue
if(isOverdue) {
reject({
status: false,
data: null,
msg: '服务已过期'
})
return
}
const token = store.state.user.token || ''
const appid = store.state.user.appid || ''
uni.request({
......@@ -132,13 +145,24 @@ export function upload(options) {
options.url = `${baseUrl}/${options.url}`
uni.showLoading({title: '上传中...', mask: true})
return new Promise((resolve, reject) => {
const isOverdue = store.state.user.isOverdue
if(isOverdue) {
reject({
status: false,
data: null,
msg: '服务已过期'
})
return
}
const token = store.state.user.token || ''
const appid = store.state.user.appid || ''
uni.uploadFile({
url: options.url,
filePath: options.tempFile,
header: {
// "Content-Type": "multipart/form-data",
token: token
'token': token,
'appid': appid
},
name: 'file',
success: res =>{
......
{
"pages": [
{
"path": "pages/home/index",
"style": {
......@@ -83,7 +84,7 @@
{
"path": "collection/index",
"style": {
"navigationBarTitleText": "账号管理"
"navigationBarTitleText": "收藏历史"
}
},
{
......
......@@ -5,7 +5,7 @@ async function getCartList() {
try{
const { status, data } = await cartsList()
if(status) {
this.cartList = data[0].cart_list2
this.cartList = data ? dealArr(data[0].cart_list2) : []
}
}catch(e){
this.$toast({title: '获取购物车失败'})
......@@ -33,6 +33,18 @@ async function deleteCarts() {
}
}
// 给数组的每一项添加checKed, 用于勾选,遍历统计勾选
function dealArr(arr) {
if(!arr) return arr
if(Array.isArray(arr)) {
for(let i = 0, j = arr.length; i < j; i ++) {
arr[i].checked = false
dealArr(arr[i].list)
}
}
return arr
}
export default {
getCartList,
editCartCount,
......
<template>
<view class="cart-wrap">
<view class="wrapper">
<view v-if="!token">
<empty-view
iconSrc="/static/images/common/icon-empty.png"
......@@ -8,7 +8,7 @@
<view class="login-btn" @click="handleLogin">登录</view>
</view>
<view v-else>
<view v-else style="margin-bottom: 98rpx;">
<template v-if="cartList.length === 0">
<empty-view
iconSrc="/static/images/common/icon-empty.png"
......@@ -22,37 +22,70 @@
class="list-item"
v-for="(item, index) in cartList"
:key="item.goods_id"
@click="handleCartItem(index, item)"
>
<view class="w-100 flex j-star a-center">
<view
class="mr-1"
:class="item.checked ? 'agreement-icon_active' : 'agreement-icon'"
@click="selectGoods(index, item)"
/>
<image
class="goods-cover mr-2"
:src="baseUrl + '/' + item.goods_thumb"
mode="aspectFit"
@click="handleDetail(item.goods_id)"
/>
<view class="goods-title flex flex-column j-between a-start flex-1 font-24">{{item.goods_name}}</view>
<view class="goods-title flex j-start a-center flex-1 font-24">{{item.goods_name}}</view>
</view>
<view
v-for="cart in item.list"
class="mb-2 ml-2"
v-for="(cart, cartIdx) in item.list"
:key="cart.cart_id"
>
<view class="w-100">
<view class="w-100">
<view class="flex j-start a-center mb-1">
<view
class="mr-1"
:class="cart.checked ? 'agreement-icon_active' : 'agreement-icon'"
@click="selectSku(index, cartIdx, cart.checked)"
></view>
<view class="w-100 flex j-between a-center">
<view class="specification font-24">{{cart.specification}}</view>
<view class="step-wrap">
<input-number
:defaultVal="cart.origin_number_sku > cart.cart_number ? cart.origin_number_sku : cart.cart_number"
:min="cart.origin_number_sku"
:params="{
wrapIdx: index,
innerIdx: cartIdx
}"
@change="numberChange"
/>
</view>
</view>
</view>
<view class="price flex j-start a-center">
<price
:is_inquiry="cart.is_inquiry"
:price="cart.cart_price"
/>
<text class="specification font-28 ml-2" v-if="cart.inventory > 0">{{'库存:' + cart.inventory}}</text>
<text class="specification font-28 ml-2 primaryColor" v-else>库存需咨询商家</text>
</view>
</view>
</view>
<text>{{cart.specification}}</text>
</view>
<!-- <view style="width: 240rpx;height: 64rpx;">
<input-number
:index="index"
:value="item.counts"
@change="numberChange"
/>
</view> -->
</view>
</view>
<view class="w-100 cart_info flex j-between a-center">
<text class="font-24">不含运费</text>
<text class="font-28 primary">合计:{{totalPrice}}</text>
<view class="font-28 py-2 px-1 primary">移除</view>
<view class="font-28 py-2 px-1 primary" @click="removeCart">移除</view>
<view class="font-28 confirm-cart">结算</view>
</view>
</template>
......@@ -62,10 +95,12 @@
<script>
import inputNumber from '@/components/inputNumber/index.vue'
import { mapState } from 'vuex'
import price from '@/components/price/index.vue'
import { mapState, mapActions } from 'vuex'
import cartModules from './cart-modules.js'
import { baseUrl } from '@/config/index.js'
import { delCart, editCart } from '@/apis/carts.js'
export default {
data() {
......@@ -76,7 +111,8 @@ export default {
},
components: {
inputNumber
inputNumber,
price
},
onPullDownRefresh() {
......@@ -84,7 +120,7 @@ export default {
setTimeout(function () {
this.getCartList()
uni.stopPullDownRefresh();
}, 1000);
}, 800);
},
......@@ -96,16 +132,22 @@ export default {
// 总价
totalPrice() {
return this.cartList.filter(item => item.checked).reduce((pre, cur) => {
return pre + (cur.price * cur.counts)
return pre + cur.list.filter(i => i.checked).reduce((p, c) => {
return p + (c.cart_price * c.cart_number)
}, 0)
}, 0)
}
},
created() {
this.getCartList()
onShow() {
if(this.token) {
this.setCount()
this.getCartList()
}
},
methods: {
...mapActions('cart', ['setCount']),
...cartModules,
handleLogin() {
......@@ -114,23 +156,79 @@ export default {
})
},
handleCartItem(index, item) {
item.checked = !item.checked
handleDetail(id) {
uni.navigateTo({
url: `/pages/goods/detail?goods_id=${id}`
})
},
// 选中/取消选中商品, 默认选中上级下的所有规格
selectGoods(index, item) {
const curStatus = !item.checked
item.checked = curStatus
item.list.forEach(item => item.checked = curStatus)
this.$set(this.cartList, index, item)
},
numberChange(e) {
this.cartList[e.index].counts = e.val
// 选中/取消规格, 父节点若没有选中要切换到选中状态
selectSku(wrapindex, idx, checked) {
this.$set(this.cartList[wrapindex], 'checked', true)
this.$set(this.cartList[wrapindex].list[idx], 'checked', !checked)
},
// 编辑数量
async numberChange(e) {
try{
const cart_id = this.cartList[e.params.wrapIdx].list[e.params.innerIdx].cart_id
const { status } = await editCart({cart_id, cart_number: e.val})
if(status) {
this.setCount()
this.$set(this.cartList[e.params.wrapIdx].list[e.params.innerIdx], 'cart_number', e.val)
}
}catch(e){
console.log(e)
//TODO handle the exception
}
},
// 移除购物车
removeCart() {
const cartList = this.cartList
const cart_ids = []
cartList.filter(item => item.checked).forEach(i => {
i.list.forEach(a => {
if(a.checked) {
cart_ids.push(a.cart_id)
}
})
})
if(cart_ids.length === 0) return this.$toast({title: '请先选择'})
uni.showModal({
title: '提示',
content: '您确定要删除吗',
confirmColor: '#FC401E',
success: async res => {
if(res.confirm) {
const { status } = await delCart(cart_ids.join(','))
if(status) {
this.getCartList()
this.setCount()
this.$toast({title: '删除成功'})
}
}
}
})
}
}
}
</script>
<style lang="scss" scoped>
.cart-wrap {
.wrapper {
width: 100%;
height: 100vh;
margin-bottom: 98rpx;
height: ca;
.login-btn {
width: 520rpx;
height: 72rpx;
......@@ -145,11 +243,17 @@ export default {
.list-wrap {
margin-top: 40rpx;
.list-item {
@include borderBox(40rpx, 20rpx);
@include borderBox(20rpx, 20rpx);
width: 690rpx;
background-color: #fff;
border-radius: 16rpx;
margin: 0 auto 20rpx;
.agreement-icon {
@include unSelected(34rpx, 34rpx);
}
.agreement-icon_active {
@include selected(34rpx, 34rpx);
}
.goods-cover {
flex: 0 0 160rpx;
width: 160rpx;
......@@ -159,12 +263,23 @@ export default {
height: 160rpx;
@include text-ellipsis(3);
}
.step-wrap {
flex: 0 0 224rpx;
width: 224rpx;
height: 64rpx;
}
.specification {
color: $desc;
}
.cart-unseleted {
@include unSelected(28rpx, 28rpx);
}
.cart-seleted {
@include selected(28rpx, 28rpx);
}
.price {
padding-left: 34rpx;
}
}
}
......
......@@ -71,7 +71,12 @@
<view class="title">{{good.goods_name}}</view>
<view class="w-100 flex j-between a-center">
<text class="price-txt">¥152.50</text>
<image class="cart-icon" src="/static/images/common/icon-cart.png" mode="aspectFit"></image>
<image
class="cart-icon"
src="/static/images/common/icon-cart.png"
mode="aspectFit"
@click.stop="handlePop(good)"
/>
</view>
</view>
</view>
......@@ -80,16 +85,20 @@
</pull-list>
</view>
</view>
<!-- 购物弹窗 -->
<goods-popup ref="popup" />
</view>
</template>
<script>
// 所有写法需要优化!!!
import pullList from '@/components/pull-list/index.vue'
import goodsPopup from '@/components/goods-popup/index.vue'
import { getCatetories, cateGoodsList } from '@/apis/category.js'
import { searchGoods } from '@/apis/goods.js'
import { searchGoods, goodsSku } from '@/apis/goods.js'
import { baseUrl } from '@/config/index.js'
import { mapState, mapActions } from 'vuex'
let cat_id = 0
// const params = {
......@@ -110,12 +119,24 @@ export default {
}
},
components: {
pullList
pullList,
goodsPopup
},
computed: {
...mapState({
token: state => state.user.token
})
},
async created() {
this.getCate()
},
onShow() {
if(this.token) {
this.setCount()
}
},
methods: {
...mapActions('cart', ['setCount']),
async getCate() {
try{
const { status, data } = await getCatetories()
......@@ -180,6 +201,35 @@ export default {
tolower() {
console.log('触底了')
},
// 点击拉起弹窗
async handlePop(goods) {
try{
uni.showLoading({
title: '加载中...',
mask: true
})
const { status, data } = await goodsSku(goods.goods_id)
if(status) {
console.log(data)
const params = {
goodsInfo: {
cover: `${this.baseUrl}/${goods.goods_thumb}`,
title: goods.goods_name,
is_inquiry: goods.is_inquiry
},
skuData: data
}
console.log(params)
this.$refs.popup.show(params)
}
uni.hideLoading()
}catch(e){
console.log(e)
uni.hideLoading()
//TODO handle the exception
}
}
}
}
......
......@@ -104,7 +104,7 @@
<image class="icon mb-1" src="/static/images/common/btn-phone.png" mode="aspectFit" />
<text>联系老板</text>
</view>
<navigator
<navigator
open-type="switchTab"
class="flex-1 left-item flex flex-column j-center a-center"
hover-class="none"
......@@ -112,6 +112,7 @@
>
<image class="icon mb-1" src="/static/images/common/icon-cart_gray.png" mode="aspectFit" />
<text>购物车</text>
<view class="cart_count">{{ cart_count <= 99 ? cart_count : '99+' }}</view>
</navigator>
<view class="flex-1 left-item flex flex-column j-center a-center" @click="handleCollect(detail.is_col)">
<image v-show="detail.is_col === 10" class="icon mb-1" src="/static/images/common/icon-collect_active.png" mode="aspectFit" />
......@@ -142,7 +143,7 @@ import goodsPopup from '@/components/goods-popup/index.vue'
import { goodDetail, goodsSku, collectGoods } from '@/apis/goods.js'
import { baseUrl } from '@/config/index.js'
import { makePhoneCall } from '@/utils/common.js'
import { mapState } from 'vuex'
let goods_id = null
export default {
data() {
......@@ -174,6 +175,10 @@ export default {
},
computed: {
...mapState({
cart_count: state => state.cart.cart_count
}),
logisticsTab() {
const detail = this.detail
if(!detail) return []
......@@ -259,8 +264,6 @@ export default {
this.swiperCurrent = e.detail.current
},
// 切换物流
handleLogistics(id) {
if(id === this.logistics_cur) return
......@@ -275,7 +278,8 @@ export default {
const params = {
goodsInfo: {
cover: `${this.baseUrl}/${detail.goods_thumb}`,
title: detail.goods_name
title: detail.goods_name,
is_inquiry: detail.is_inquiry
},
skuData: skuData
}
......@@ -382,11 +386,26 @@ export default {
border-top: 1rpx solid $line;
background-color: $white;
.left-item {
position: relative;
font-size: 20rpx;
.icon {
width: 34rpx;
height:34rpx;
}
.cart_count {
position: absolute;
right:0;
top: -16rpx;
font-size: 20rpx;
color: #fff;
background-color: $primary;
width: 32rpx;
height: 32rpx;
padding: 5rpx;
line-height: 32rpx;
text-align: center;
border-radius: 50%;
}
}
.right {
flex: 0 0 360rpx;
......
......@@ -2,11 +2,12 @@
<view class="w-100">
<view class="font-bold font-36 px-3 py-2">{{title}}</view>
<view class="list-wrap w-100 flex j-start a-center flex-wrap">
<view
<navigator
class="list-item mb-3 ml-3"
v-for="(item, index) in list"
:key="index"
@click="nav"
hover-class="none"
v-for="(item, idx) in list"
:key="idx"
:url="'/pages/goods/detail?goods_id=' + item.goods_id"
>
<image
class="goods_cover w-100"
......@@ -15,25 +16,27 @@
/>
<view class="goods-info flex flex-column j-between">
<view class="title font-28 my-1">{{item.goods_name}}</view>
<view class="w-10 </price>flex j-between a-center mt-1 mb-2">
<view class="w-100 flex j-between a-center mt-1 mb-2">
<price
:is_inquiry="item.is_inquiry"
:price="item.shop_price"
/>
<image
class="cart-btn"
class="cart-btn"
@click.stop="handlePop(item)"
src="/static/images/common/icon-cart.png"
mode="aspectFit"
/>
</view>
</view>
</view>
</navigator>
</view>
</view>
</template>
<script>
import price from '@/components/price/index.vue'
import { goodsSku } from '@/apis/goods.js'
import { baseUrl } from '@/config/index.js'
export default {
props: {
......@@ -58,18 +61,33 @@ export default {
},
methods: {
nav() {
uni.navigateTo({
url: `/pages/goods/detail?goods_id=9192&shop_id=101120&agent_id=0`
})
},
purchase(item) {
console.log(item)
this.$emit('purchase', {
goods_id: 7344,
sale_type: 0,
shop_id: 100737
})
async handlePop(goods) {
try{
uni.showLoading({
title: '加载中...',
mask: true
})
console.log(goods)
const { status, data } = await goodsSku(goods.goods_id)
if(status) {
console.log(data)
if(!data) return this.$toast({title: '该商品没有规格'})
const params = {
goodsInfo: {
cover: `${this.baseUrl}/${goods.goods_thumb}`,
title: goods.goods_name,
is_inquiry: goods.is_inquiry
},
skuData: data
}
this.$emit('purchase', params)
}
uni.hideLoading()
}catch(e){
console.log(e)
uni.hideLoading()
//TODO handle the exception
}
}
}
}
......
......@@ -140,7 +140,8 @@
:price="good.shop_price"
/>
<image
class="cart-btn"
class="cart-btn"
@click.stop="handlePop(good)"
src="/static/images/common/icon-cart.png"
mode="aspectFit"
/>
......@@ -156,7 +157,9 @@
</view>
</template>
<!-- <goods-popup ref="goodsPopup"></goods-popup> -->
<!-- 购物弹窗 -->
<goods-popup ref="popup" />
<button type="default" open-type="share" id="share"></button>
</view>
</template>
......@@ -166,18 +169,19 @@ import searchBar from '@/components/search-bar/index.vue'
import goodsModule from './components/goods-module.vue'
import goodsPopup from '@/components/goods-popup/index.vue'
import price from '@/components/price/index.vue'
import LoadMore from '@/utils/load-more.js'
import { checkLogin } from '@/utils/modules/login.js'
import indexModule from './index.js'
import { baseUrl } from '@/config/index.js'
import { makePhoneCall } from '@/utils/common.js'
import { goodsSku } from '@/apis/goods.js'
import { mapState, mapActions } from 'vuex'
export default {
data() {
return {
baseUrl,
module1: [2,1,1],
select_goods: null,
cate_data: null,
searchList: []
......@@ -206,6 +210,12 @@ export default {
this.getHomeCate()
},
onShow() {
if(this.token) {
this.setCount()
}
},
mounted() {
const storeInfo = this.storeInfo
if(storeInfo.shop_name) {
......@@ -246,7 +256,7 @@ export default {
...indexModule,
purchase(e) {
this.$refs.goodsPopup.show(e)
this.$refs.popup.show(e)
},
// 点击菜单
......@@ -258,7 +268,7 @@ export default {
break;
case 'collect':
uni.navigateTo({
url: '/pages/order/order-list'
url: '/subPages/collection/index'
})
break;
case 'order':
......@@ -271,6 +281,35 @@ export default {
break;
}
})
},
// 点击拉起弹窗
async handlePop(goods) {
try{
uni.showLoading({
title: '加载中...',
mask: true
})
const { status, data } = await goodsSku(goods.goods_id)
if(status) {
console.log(data)
const params = {
goodsInfo: {
cover: `${this.baseUrl}/${goods.goods_thumb}`,
title: goods.goods_name,
is_inquiry: goods.is_inquiry
},
skuData: data
}
console.log(params)
this.$refs.popup.show(params)
}
uni.hideLoading()
}catch(e){
console.log(e)
uni.hideLoading()
//TODO handle the exception
}
}
}
}
......
<template>
<view class="w-100">
<view class="nav-title w-100" :style="navTitleSty">我的</view>
<!-- userInfo -->
<view class="user-wrap w-100" @click="handleAvatar">
<view class="user-wrap flex j-start a-center w-100" @click="handleAvatar">
<view class="user-inner flex j-start a-center" v-if="token">
<image
v-if="userInfo.avatar"
......@@ -17,9 +19,11 @@
<image class="avatar mr-2" src="/static/images/common/default-avatar.png" mode="aspectFit"></image>
<view class="font-32">登录/注册</view>
</view>
<image class="bg" src="/static/images/common/bg.png" mode="aspectFit"></image>
</view>
<!-- bg -->
<image class="bg" src="/static/images/common/bg.png" mode="widthFix"></image>
<view class="w-100 order-wrap">
<view class="w-100 flex j-between a-center mb-4" @click="nav('order', 0)">
<view class="font-bold font-28">我的订单</view>
......@@ -85,6 +89,7 @@
<icon class="right_arrow" />
</view>
</view>
</view>
</template>
......@@ -102,8 +107,13 @@ export default {
computed: {
...mapState({
token: state => state.user.token,
userInfo: state => state.user.userInfo
userInfo: state => state.user.userInfo,
menuButtonInfo: state => state.systemInfo.menuButtonInfo
}),
navTitleSty() {
const menuButtonInfo = this.menuButtonInfo
return `height:${menuButtonInfo.height}px;line-height:${menuButtonInfo.height}px;top:${menuButtonInfo.top}px;`
}
},
onShow() { // 获取更新用户信息
if(this.token) {
......@@ -124,7 +134,7 @@ export default {
break;
case 'address':
uni.navigateTo({
url: '/pages/address/index'
url: '/subPages/address/index'
})
break;
case 'store':
......@@ -159,33 +169,45 @@ export default {
</script>
<style lang="scss" scoped>
.nav-title {
position: fixed;
left: 0;
right: 0;
z-index: 2;
text-align: center;
color: #fff;
font-weight: 500;
font-size: 34rpx;
}
.user-wrap {
position: relative;
@include borderBox(0, 30rpx);
height: 454rpx;
.user-inner {
padding-top: 216rpx;
color: #fff;
.avatar {
width: 140rpx;
height: 140rpx;
border-radius: 50%;
}
}
.bg {
position: absolute;
left: 0;
right: 0;
top: 0;
z-index: -1;
width: 100%;
height: 454rpx;
}
}
.bg {
position: absolute;
left: 0;
right: 0;
top: 0;
z-index: -1;
width: 750rpx;
}
.order-wrap {
@include borderBox(40rpx, 30rpx);
width: 690rpx;
height: 258rpx;
margin: -80rpx auto 0;
margin: -120rpx auto 0;
background-color: #fff;
border-radius: 8rpx;
.all-text {
......
<template>
<view>确认订单</view>
<view class="wrapper">
<view class="tab-wrap w-100 flex j-around a-center text-center">
<view
class="tab-item flex-1"
:class="current_tab === 1 ? 'tab-active' : null"
@click="handleTab(1)"
>卖家派送</view>
<view
class="tab-item flex-1"
:class="current_tab === 2 ? 'tab-active' : null"
@click="handleTab(2)"
>卖家自提或找物流</view>
</view>
<view class="address">
<navigator
v-if="orderDetail.address.length === 0"
class="font-32 font-bold"
hover-class="none"
url="/subPages/address/index"
>选择收获地址</navigator>
<view class="font-32 font-bold" v-else>1</view>
<view class="tips mt-2 pt-2">卖家派送指卖家视同默认卖家提供的运输服务及因此可能产生的费用, 如 有疑问可点击<text class="primaryColor" @click="handleConcact">联系商家</text></view>
</view>
<view class="address-store" v-show="current_tab === 2">
<view class="item">
<view class="left">自提地址</view>
<view>{{orderDetail.shop.pickup_address}}</view>
</view>
<view class="item">
<view class="left">自提时间</view>
<view>{{orderDetail.shop.pickup_hours}}</view>
</view>
<view class="item">
<view class="left">联系电话</view>
<view>{{orderDetail.shop.pickup_phone}}</view>
</view>
<view class="tips py-2">
自提可直接<text class="primaryColor" @click="handleConcact">联系商家</text>也可直接<text >物流冷运</text>
</view>
</view>
<view class="goods-info">
<block
v-for="item in orderDetail.shop.cartList2"
:key="item.goods_id"
>
<view class="flex j-start a-center mb-4">
<image class="cover mr-2" :src="baseUrl + '/' + item.goods_thumb" mode="aspectFit" />
<text class="title font-bold">{{item.goods_name}}</text>
</view>
<view
class="cart-item pb-2"
v-for="(sku, idx) in item.list"
:key="idx"
>
<view class="mt-2 mb-2">规格:{{sku.specification}}</view>
<view class="mb-2">单价:{{sku.cart_price + '/' + sku.unit}}</view>
<view class="flex j-between a-center">
<text>数量:{{sku.cart_number}}</text>
<text>小记:<text class="primaryColor">{{sku.cart_subtotal}}</text></text>
</view>
</view>
</block>
</view>
<view class="pay-info mb-2">
<view class="item py-2" @click="handleConcact">
<view class="item-left">开具发票</view>
<view class="item-right flex-1">
<text class="mr-2">联系老板</text>
<icon class="right-arrow"></icon>
</view>
</view>
<view class="item py-2">
<view class="item-left">买家留言</view>
<view class="item-right flex-1">
<input
class="font-28 w-100 pr-3"
type="text"
placeholder="选填, 请先和商家协商一致"
v-model="form.postscript"
/>
</view>
</view>
<view class="py-2">
<view class="pb-2">选择支付方式</view>
<view class="pay-item pb-2">
<view>在线支付</view>
<view class="select"></view>
</view>
<view class="pay-item">
<view>先货后款</view>
<view class="unselect"></view>
</view>
</view>
</view>
<view class="w-100 flex j-end a-center confirm">
<text class="font-24">不含运费</text>
<text class="total-price mx-4">合计:{{orderDetail.sum_amount}}</text>
<view class="confirm-btn mr-2" @click="handleConfirm">提交订单</view>
</view>
</view>
</template>
<script>
import { preConfirmOrderData, confirmOrder } from '@/apis/order.js'
import { makePhoneCall } from '@/utils/common.js'
import { baseUrl } from '@/config/index.js'
export default {
data() {
return {
baseUrl,
current_tab: 1,
orderDetail: {},
form: {
from: 6, // 来源:6专属小程序
buy_type: 0, //购买类型:0默认加入购物车 1立即购买
goods_before_pay: 0, // 是否为先货后款:0否 1是
sku_ids: '', // SKUIDs,多个则使用逗号分隔(buy_type=1时必填)
shipping_type: 1, // 配送类型1格利冷运2第三方(自提)
address_id: '1707', // 收货地址ID(shipping_type=1时必填)
cart_ids: '', // 选中的购物车IDs,多个则使用逗号分隔(buy_type=0时必填)
postscript: '', // 订单附言,由用户提交订单前填写
cart_numbers: '' // 购买数量s,多个则使用逗号分隔(buy_type=1时必填)
}
}
},
onLoad(options) {
const params = JSON.parse(decodeURIComponent(options.params))
this.form = Object.assign(this.form, params)
this.getPreOrderData(params)
},
methods: {
handleTab(idx) {
if(idx === this.current_tab) return
this.current_tab = idx
this.form.shipping_type = idx
},
handleConcact() {
this.$checkLogin(makePhoneCall)
},
// 获取提交订单前的数据
async getPreOrderData(params) {
try{
const { status, data } = await preConfirmOrderData(params)
if(status) {
this.orderDetail = data
} else {
uni.navigateBack()
}
}catch(e){
console.log(e)
//TODO handle the exception
}
},
// 提交订单
async handleConfirm() {
try{
const form = this.form
console.log(form)
if(form.shipping_type === 1 && !form.address_id) return this.$toast({title: '请选择收货地址'})
const { status, data } = await confirmOrder(form)
}catch(e){
this.$toast({title: e.msg || '程序错误,订单提交失败'})
//TODO handle the exception
}
}
}
}
</script>
<style>
<style lang="scss" scoped>
.wrapper {
padding-top: 110rpx;
margin-bottom: 98rpx;
.tab-wrap {
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 1;
height: 90rpx;
color: $desc;
background-color: #fff;
&::after {
display: inline-block;
content: '';
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 690rpx;
height: 2rpx;
background-color: $line;
}
.tab-item {
position: relative;
height: 90rpx;
line-height: 90rpx;
}
.tab-active {
color: $primary;
&::after {
display: inline-block;
content: '';
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 180rpx;
height: 2rpx;
z-index: 3;
background-color: $primary;
}
}
}
.address {
@include borderBox(20rpx, 20rpx);
width: 690rpx;
margin: 0 auto 20rpx;
border-radius: 8rpx;
background-color: #fff;
.tips {
font-size: 20rpx;
color: $desc;
border-top: 1rpx solid $line;
text {
color: $primary;
}
}
}
.address-store {
@include borderBox(0, 20rpx);
width: 690rpx;
margin: 0 auto 20rpx;
border-radius: 8rpx;
background-color: #fff;
.item{
display: flex;
justify-content: flex-start;
align-items: flex-start;
padding: 30rpx 0;
border-bottom: 1rpx solid $line;
.left {
flex: 0 0 120rpx;
}
}
.tips {
font-size: 20rpx;
}
}
.goods-info {
@include borderBox(40rpx, 20rpx);
width: 690rpx;
margin: 0 auto 20rpx;
border-radius: 8rpx;
background-color: #fff;
.cover {
flex: 0 0 160rpx;
width: 160rpx;
height: 160rpx;
}
.title {
@include text-ellipsis(3);
}
.cart-item {
border-bottom: 1rpx solid $line;
&:last-child {
border-bottom: none;
}
}
}
.pay-info {
@include borderBox(0, 20rpx);
width: 690rpx;
margin: 0 auto;
background-color: #fff;
font-size: 28rpx;
border-radius: 8rpx;
.item {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1rpx solid $line;
.item-left {
flex: 0 0 120rpx;
}
.item-right {
display: flex;
justify-content: flex-end;
align-items: center;
text-align: right;
.right-arrow {
@include right_arrow(16rpx, 34rpx);
}
}
}
.pay-item {
display: flex;
justify-content: space-between;
align-items: center;
.select {
@include selected(34rpx, 34rpx);
}
.unselect {
@include unSelected(34rpx, 34rpx);
}
}
}
.confirm {
position: fixed;
left: 0;
right: 0;
bottom: 0;
height: 98rpx;
z-index: 1;
background-color: #fff;
color: $desc;
.total-price {
font-weight: 600;
font-size: 28rpx;
color: $primary;
}
.confirm-btn {
width: 180rpx;
height: 72rpx;
line-height: 72rpx;
text-align: center;
border-radius: 36rpx;
font-size: 28rpx;
color: #fff;
background-color: $primary;
}
}
}
</style>
......@@ -11,81 +11,137 @@
@confirm="search"
/>
<image
class="search-icon" src="/static/images/common/icon-search.png"
class="search-icon ml-3" src="/static/images/common/icon-search.png"
mode="aspectFit"
/>
<image
class="nav-cart"
src="/static/images/common/icon-cart_gray.png"
mode="aspectFit"
@click="nav"
@click="navCart"
/>
</viwe>
<view class="filter-bar flex j-around a-center font-28 text-center w-100">
<view class="item flex-1">综合</view>
<view class="item flex-1">销量</view>
<view class="item item_active flex-1 flex j-center a-center">
<view
class="item flex-1"
:class="current_tab === 1 ? 'item_active' : null"
@click="handleTab(1)"
>综合</view>
<view
class="item flex-1"
:class="current_tab === 2 ? 'item_active' : null"
@click="handleTab(2)"
>销量</view>
<view
class="item flex-1 flex j-center a-center"
:class="current_tab === 3 || current_tab === 4 ? 'item_active' : null"
@click="handleTab(3)"
>
<view>价格</view>
<view class="sanjiao-wrap flex flex-column j-center a-center">
<view class="sanjiao iconfont icon-sanjiaoxing"></view>
<view class="sanjiao iconfont icon-sanjiaoxingbeifen1"></view>
<view
class="sanjiao iconfont icon-sanjiaoxing"
:class="searchParams.sort === 3 ? 'item_active' : null"
></view>
<view
class="sanjiao iconfont icon-sanjiaoxingbeifen1"
:class="searchParams.sort === 4 ? 'item_active' : null"
></view>
</view>
</view>
</view>
<!-- list -->
<view class="list w-100">
<pull-list
@tolower="tolower"
@refresh="refresh"
>
<block
v-for="(item, index) in searchList"
:key="index"
>
<view
class="goods-item"
<pull-list
@tolower="tolower"
@refresh="refresh"
>
<block
v-for="(item, index) in searchList"
:key="index"
>
<block v-if="item.length === 0">
<empty-view />
</block>
<block v-else>
<navigator
class="goods-item w-100 flex j-star a-center"
open-type="redirect"
hover-class="none"
v-for="good in item"
:key="good.goods_id"
:url="'/pages/goods/detail?goods_id=' + good.goods_id"
>
<image
<image
class="goods-cover mr-2"
:src="baseUrl + '/' + good.goods_thumb"
style="width: 300rpx;"
mode="widthFix"
mode="aspectFit"
/>
</view>
<view class="information flex-1 flex flex-column j-between a-start">
<view class="title font-bold font-28">{{good.goods_name}}</view>
<view class="font-24 counts">
<text class="mr-2">规格:{{good.specification}}</text>
<text>销量:{{good.virtual_quantity_sold}}</text>
</view>
<view class="font-28 flex w-100 j-between a-center">
<price
:is_inquiry="good.is_inquiry"
:price="good.shop_price"
/>
<image
class="cart-icon"
@click.stop="handlePop(good)"
src="/static/images/common/icon-cart.png"
mode="aspectFit"
/>
</view>
</view>
</navigator>
</block>
</pull-list>
</block>
</pull-list>
</view>
<!-- 购物弹窗 -->
<goods-popup ref="popup" />
</view>
</template>
<script>
import pullList from '@/components/pull-list/index.vue'
import { searchGoods } from '@/apis/goods.js'
import goodsPopup from '@/components/goods-popup/index.vue'
import price from '@/components/price/index.vue'
import { searchGoods, goodsSku } from '@/apis/goods.js'
import LoadMore from '@/utils/load-more.js'
import { baseUrl } from '@/config/index.js'
const searchParams = {
sort: 1, // 1综合优先 2销量优先 3价格升序 4价格降序
keywords: ''
}
export default {
data() {
return {
baseUrl,
searchList: []
searchList: [],
current_tab: 1, // sort参数从1开始,为了方便
searchParams: {
sort: 1, // 1综合优先 2销量优先 3价格升序 4价格降序
keywords: ''
}
}
},
components: {
pullList
pullList,
goodsPopup,
price
},
onLoad(options) {
this.loadmore = new LoadMore()
searchParams.keywords = options.val || ''
this.searchParams.keywords = options.val || ''
uni.setNavigationBarTitle({
title: options.val
})
......@@ -93,8 +149,34 @@ export default {
},
methods: {
search(e) {},
nav() {
handleTab(idx) {
const current_tab = this.current_tab
if(idx === current_tab && current_tab !== 3) return
this.current_tab = idx
if(current_tab === 3) { // 当tab位于价格
this.searchParams.sort = this.searchParams.sort === 3 ? 4 : 3
} else {
this.searchParams.sort = idx
}
this.loadmore.resetParams() // 切换请求缓存有bug,先不缓存,直接刷新参数重新请求
this.searchList = []
this.getData()
},
// 重新搜索
search(e) {
const value = e.detail.value
this.loadmore.resetParams()
this.searchList = []
uni.setNavigationBarTitle({
title: value
})
this.searchParams.keywords = value
this.getData()
},
// 前往购物车
navCart() {
uni.switchTab({
url: '/pages/carts/index'
})
......@@ -102,7 +184,7 @@ export default {
async getData() {
try{
const listRes = await this.loadmore.getList(searchParams, searchGoods)
const listRes = await this.loadmore.getList(this.searchParams, searchGoods)
if(listRes.status) {
this.$set(this.searchList, this.searchList.length, listRes.data)
}
......@@ -121,6 +203,35 @@ export default {
this.searchList = []
this.loadmore.resetParams()
this.getData()
},
// 点击拉起弹窗
async handlePop(goods) {
try{
uni.showLoading({
title: '加载中...',
mask: true
})
const { status, data } = await goodsSku(goods.goods_id)
if(status) {
console.log(data)
const params = {
goodsInfo: {
cover: `${this.baseUrl}/${goods.goods_thumb}`,
title: goods.goods_name,
is_inquiry: goods.is_inquiry
},
skuData: data
}
console.log(params)
this.$refs.popup.show(params)
}
uni.hideLoading()
}catch(e){
console.log(e)
uni.hideLoading()
//TODO handle the exception
}
}
}
}
......@@ -175,30 +286,49 @@ export default {
}
.item_active {
color: $primary;
.sanjiao-wrap {
width: 34rpx;
height: 24rpx;
line-height: 10rpx;
.sanjiao {
display: inline-block;
font-size: 12rpx!important;
color: #333;
&:nth-child(1) {
margin-bottom: 8rpx;
}
}
}
color: $primary!important;
}
.sanjiao-wrap {
width: 32rpx;
height: 22rpx;
line-height: 10rpx;
.sanjiao {
display: inline-block;
font-size: 12rpx!important;
color: #333;
&:nth-child(1) {
margin-bottom: 8rpx;
}
}
}
}
.list {
@include borderBox(20rpx, 20rpx);
height: calc(100vh - 200rpx);
background-color: #fff;
.goods-item {
@include borderBox(20rpx, 20rpx);
@include borderBox(20rpx, 0);
border-bottom: 1rpx solid $line;
.goods-cover {
flex: 0 0 160rpx;
width: 160rpx;
height: 160rpx;
}
.information {
height: 160rpx;
.title {
@include text-ellipsis(2);
}
.counts {
color: $desc;
}
.cart-icon {
width: 44rpx;
height: 42rpx;
}
}
}
}
</style>
......@@ -25,6 +25,9 @@
- 当前页面最高层级,如遮罩 zIndex 99
- 仅次于小程序原生弹窗 zIndex 100
#### warning
+ 2021/6/29,本日起的代码可能会引起不适,但我和代码都能跑(效率和质量不会成正比关系)
#### 未完成
+ 搜索列表
+ 下单
......
static/images/common/bg.png

80.3 KB | W: | H:

static/images/common/bg.png

32.3 KB | W: | H:

static/images/common/bg.png
static/images/common/bg.png
static/images/common/bg.png
static/images/common/bg.png
  • 2-up
  • Swipe
  • Onion skin
import { cartCount } from "@/apis/carts.js"
const whitePage = ["pages/home/index", "pages/category/index", "pages/carts/index"] // 非tabBar页面无法使用setTabBarBadge
const state = {
cart_count: 0
}
......@@ -16,10 +16,15 @@ const actions = {
const count = data.count
if(status && count > 0) {
commit('SETCOUNT', count)
uni.setTabBarBadge({
index: 2,
text: count > 99 ? '99+' : String(count)
})
const pages = getCurrentPages()
const curPage = whitePage.find(item => item === pages[pages.length - 1].__route__)
if(curPage) {
uni.setTabBarBadge({
index: 2,
text: count > 99 ? '99+' : String(count),
})
}
}
}
}
......
......@@ -3,7 +3,7 @@ $text: #333; // 正文色
$desc: #7f7f7f; // 描述色
$warning: #FFBB33; // 黄色 警告
$disabled: #cccccc;
$daner: #FC401E; // 红色 错误
$daner: #FC401E; // 红色 错误 单词写错了!!!
$line: #d9d9d9; // 线条色
$mainBg: #F8F8F8; // 部分按钮、背景色
......
page {
color: #333;
background-color: #F8F8F8;
font-size: 28rpx;
}
/* 去除image的间距 */
image {
......
......@@ -7,19 +7,20 @@
:value="cityIndex"
:range="cities"
>
<view v-if="!result">
请选择地区
<view class="flex j-between a-center" v-if="!result" style="color: #999">
省/市/区
<icon class="right-arrow"></icon>
</view>
<view v-else>
<view class="flex j-between a-center" v-else>
<!-- 可能没有第四级 -->
{{cities[0][cityIndex[0]] || ''}} {{cities[1][cityIndex[1]] || ''}} {{cities[2][cityIndex[2]] || ''}} {{ cities[3][cityIndex[3]] || ''}}
<text>{{cities[0][cityIndex[0]] || ''}} {{cities[1][cityIndex[1]] || ''}} {{cities[2][cityIndex[2]] || ''}} {{ cities[3][cityIndex[3]] || ''}}</text>
<icon class="right-arrow"></icon>
</view>
</picker>
</template>
<script>
import { throttle } from '@/utils/common.js'
import { queryCity } from '@/apis/common.js'
const cacheList = [null, null, null, null] // 分别为四级区域,其中第一级是不会变得
const cityCode = { // 四级联动的城市的code,第一级默认是1
......@@ -193,5 +194,7 @@ export default {
</script>
<style lang="scss" scoped>
.right-arrow {
@include right_arrow(14rpx, 34rpx);
}
</style>
<template>
<view class="wrapper">
<view>
<input
type="text"
placeholder="请输入联系人名字"
v-model="form.consignee"
/>
<view class="info-wrap my-2">
<view class="item">
<view class="hd"><text class="must_text">*</text>收货人:</view>
<input
class="flex-1"
type="text"
placeholder="请输入收件人姓名"
v-model="form.consignee"
/>
</view>
<view class="item">
<view class="hd"><text class="must_text">*</text>手机号:</view>
<input
class="flex-1"
type="number"
maxlength="11"
placeholder="请输入手机号"
v-model="form.mobile"
/>
</view>
<view class="item">
<view class="hd"><text class="must_text">*</text>地址:</view>
<view class="flex-1">
<city-picker
:cityForm="form"
@confirmCity="confirmCity"
/>
</view>
</view>
<view class="item">
<view class="hd"><text class="must_text">*</text>详细地址:</view>
<input
class="flex-1"
type="text"
placeholder="请输入详细地址"
v-model="form.address"
/>
</view>
<view class="item">
<view class="hd">公司名称:</view>
<input
class="flex-1"
type="text"
placeholder="请输入您的公司名称"
v-model="form.corporate_name"
/>
</view>
</view>
<view>
<input
type="number"
maxlength="11"
placeholder="请输入电话号码"
v-model="form.mobile"
/>
</view>
<view class="p-2" style="background-color: red;">
<city-picker :cityForm="form" @confirmCity="confirmCity"></city-picker>
</view>
<view>
<input
type="text"
placeholder="请输入详细地址"
v-model="form.address"
/>
<view class="default-wrap">
<view class="item" @click="handleDefault('is_default', form.is_default)">
<text>设为默认寄件地址</text>
<icon
:class="form.is_default ? 'cart-seleted' : 'cart-unseleted'"
/>
</view>
<view class="item" @click="handleDefault('is_send_default', form.is_send_default)">
<text>设为默认收件地址</text>
<icon
:class="form.is_send_default ? 'cart-seleted' : 'cart-unseleted'"
/>
</view>
</view>
<view>
设置为默认收货地址
<switch
:checked="form.is_default"
color="#04BE02"
@change="handleDefault('is_default', form.is_default)"
/>
<view class="w-100 btn-wrap flex j-around a-center">
<view class="btn del w-100" v-if="form.address_id" @click="handleDel">删除地址</view>
<view class="btn normal w-100" @click="handleAdd">{{form.address_id ? '编辑' : '添加'}}</view>
</view>
<view>
设置为默认发货地址
<switch
:checked="!!form.is_send_default"
color="#04BE02"
@change="handleDefault('is_send_default', form.is_send_default)"
/>
</view>
<view class="btn w-100" @click="handleAdd">添加</view>
</view>
</template>
<script>
import cityPicker from './components/city-picker/index.vue'
import { editAddress } from '@/apis/address.js'
import { editAddress, delAddress } from '@/apis/address.js'
import editModules from './editModuls.js'
export default {
data() {
......@@ -112,7 +137,20 @@ export default {
// 设置默认
handleDefault(key, val) {
this.$set(this.form, key, +!val)
console.log(val, ~~!~~val)
this.$set(this.form, key, ~~!~~val)
},
async handleDel() {
try{
const { status } = await delAddress(this.form.address_id)
if(status) {
this.$toast({title: '删除成功', cb: () => uni.navigateBack()})
}
}catch(e){
this.$toast({title: '程序错误,删除失败'})
//TODO handle the exception
}
},
confirmCity(e) {
......@@ -131,15 +169,72 @@ export default {
<style lang="scss" scoped>
.wrapper {
position: relative;
margin-bottom: 98rpx;
.btn {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 98rpx;
z-index: 1;
background-color: red;
.info-wrap {
background-color: #fff;
.item {
@include borderBox(20rpx, 30rpx);
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
border-bottom: 1rpx solid $line;
&:last-child {
border-bottom: none;
}
.hd {
flex: 0 0 150rpx
}
}
}
.must_text {
color: $daner;
}
.default-wrap {
background-color: #fff;
.item {
@include borderBox(20rpx, 30rpx);
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1rpx solid $line;
&:last-child {
border-bottom: none;
}
.cart-unseleted {
@include unSelected(34rpx, 34rpx);
}
.cart-seleted {
@include selected(34rpx, 34rpx);
}
}
}
.btn-wrap {
margin: 80rpx 0;
.btn {
box-sizing: border-box;
width: 330rpx;
height: 98rpx;
line-height: 98rpx;
text-align: center;
border-radius: 8rpx;
font-weight: 500;
background-color: $primary;
}
.del {
color: $desc;
border: 1rpx solid $line;
background-color: #fff;
}
.normal {
color: #fff;
background-color: $primary;
}
}
}
</style>
<template>
<view>
<view class="wrap">
<block
v-for="(item, index) in list"
:key="index"
>
<view
class="address-item flex j-between a-center w-100 mt-2"
v-for="(address, inneridx) in item"
:key="address.address_id"
@click="handleDetail(address)"
style="width: 100%;border-bottom: 1px solid red;"
>
<view>{{address.consignee}}</view>
<view>{{address.mobile}}</view>
<view>{{address.p_cn + address.d_cn + address.c_cn + address.st_cn + address.address}}</view>
<button type="default" @click="handleDel(address.address_id, index, inneridx)">删除地址</button>
<view class="flex-1">
<view class="mb-2">
<text class="mr-3">{{address.consignee}}</text>
<text>{{address.mobile}}</text>
</view>
<view>{{address.p_cn + address.d_cn + address.c_cn + address.st_cn + address.address}}</view>
</view>
<view class="right-arrow" />
<!-- <button type="default" @click="handleDel(address.address_id, index, inneridx)">删除地址</button> -->
</view>
</block>
<navigator url="/subPages/address/edit">
<button type="default">添加地址</button>
</navigator>
<navigator class="addresss-btn" url="/subPages/address/edit">添加地址</navigator>
</view>
</template>
......@@ -130,5 +133,31 @@ export default {
}
</script>
<style>
<style lang="scss" scoped>
.wrap {
margin-bottom: 98rpx;
.address-item {
@include borderBox(20rpx, 20rpx);
background-color: #fff;
.right-arrow {
flex: 0 0 16rpx;
margin-left: 10rpx;
@include right_arrow(16rpx, 34rpx);
}
}
.addresss-btn {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 2;
width: 100%;
height: 98rpx;
color: #fff;
line-height: 98rpx;
text-align: center;
background-color: $primary;
}
}
</style>
import { collectGoodsList, collectGoods } from '@/apis/goods.js'
import { historyList } from '@/apis/common.js'
// 获取收藏列表
async function getCollectData() {
try{
const { status, data } = await this.collectList.getList({}, collectGoodsList)
if(status) {
this.$set(this.collect_list, this.collect_list.length, data)
}
}catch(e){
console.log(e)
//TODO handle the exception
}
}
// 获取历史
async function getHistoryData() {
try{
const { status, data } = await historyList(this.last_time)
if(status && !data) {
this.$toast({title: '没有更多历史了'})
} else if(status && data) {
// this.$set(this.history_list, this.history_list.length, data)
this.history_list.push(...data)
this.last_time = data[data.length-1].date
}
}catch(e){
console.log(e)
//TODO handle the exception
}
}
export default {
getCollectData,
getHistoryData
}
\ No newline at end of file
<template>
<view class="wrapper w-100">
<view class="wrapper">
<!-- tab -->
<view class="tab-wrap w-100 flex j-between a-center text-center">
<view
class="flex-1 tab-item"
class="tab-item"
:class="tab_current === 0 ? 'tab-active' : null"
@click="handleTab(0)"
>收藏</view>
<view
class="flex-1"
class="tab-item"
:class="tab_current === 1 ? 'tab-active' : null"
@click="handleTab(1)"
>浏览历史</view>
......@@ -17,22 +17,108 @@
<!-- list -->
<swiper class="list-wrap" :current="tab_current">
<swiper-item>
<pull-list
@tolower="tolowerC"
@refresh="refreshC"
>
<view v-for="item in list" :key="item.goods_id">
<image :src="baseUrl + '/' + item.goods_thumb" style="width: 300rpx;" mode="widthFix"></image>
</view>
</pull-list>
<template v-if="collect_list.length === 0">
<empty-view text="暂无收藏"></empty-view>
</template>
<template v-else>
<pull-list
@tolower="tolowerC"
@refresh="refreshC"
>
<block
v-for="(item, index) in collect_list"
:key="index"
>
<view
class="goods-item"
v-for="(good, goodIdx) in item"
:key="good.goods_id"
@click="handleDetail(good.is_lose, good.goods_id)"
>
<view class="goods-cover">
<image
class="img"
:src="baseUrl +'/' + good.goods_thumb"
mode="aspectFit"
/>
<view v-if="good.is_lose === '1'" class="mask">
已下架
</view>
</view>
<view class="goods-info">
<view class="hd">
<view class="title">{{good.goods_name}}</view>
<view
class="btn"
@click.stop="handleUnCollect(good.goods_id, index, goodIdx)"
>取消收藏</view>
</view>
<view class="desc">规格:{{good.specification}}</view>
<view class="desc">总销量:{{good.virtual_quantity_sold}}</view>
<price
:is_inquiry="good.is_inquiry"
:price="good.shop_price"
/>
</view>
</view>
</block>
</pull-list>
</template>
</swiper-item>
<swiper-item>
<pull-list
@tolower="tolowerH"
@refresh="refreshH"
>
<view>1</view>
</pull-list>
<template v-if="history_list.length === 0">
<empty-view text="暂无历史记录"></empty-view>
</template>
<template v-else>
<pull-list
@tolower="tolowerH"
@refresh="refreshH"
>
<block
v-for="(item, index) in history_list"
:key="index"
>
<view class="py-2 flex j-between a-center desc">
<view>{{item.date}}</view>
<view v-if="index === 0" @click.stop="handleClearAll">全部删除</view>
</view>
<view
class="goods-item"
v-for="(good, goodIdx) in item.date_list"
:key="good.goods_id"
@click="handleDetail(good.is_lose, good.goods_id)"
>
<view class="goods-cover">
<image
class="img"
:src="baseUrl +'/' + good.goods_thumb"
mode="aspectFit"
/>
<view v-if="good.is_lose === '1'" class="mask">
已下架
</view>
</view>
<view class="goods-info">
<view class="hd">
<view class="title">{{good.goods_name}}</view>
<view
class="btn"
@click.stop="clearHistory(good, index, goodIdx)"
>删除历史</view>
</view>
<view class="desc">规格:{{good.specification}}</view>
<view class="desc">总销量:{{good.virtual_quantity_sold}}</view>
<price
:is_inquiry="good.is_inquiry"
:price="good.shop_price"
/>
</view>
</view>
</block>
</pull-list>
</template>
</swiper-item>
</swiper>
......@@ -41,57 +127,157 @@
<script>
import pullList from '@/components/pull-list/index.vue'
import { collectGoodsList } from '@/apis/goods.js'
import { historyList, test } from '@/apis/common.js'
import price from '@/components/price/index.vue'
import { collectGoods } from '@/apis/goods.js'
import { delHistory, clearAllHistory } from '@/apis/common.js'
import LoadMore from '@/utils/load-more.js'
import { baseUrl } from '@/config/index.js'
import fnModules from './fn-modules.js'
export default {
data() {
return {
baseUrl,
tab_current: 0,
list: []
collect_list: [],
history_list: [],
last_time: ''
}
},
components: {
pullList
pullList,
price
},
created() {
this.collectList = new LoadMore()
},
mounted() {
this.getCollect()
this.getCollectData()
},
methods: {
...fnModules,
// tab 切换
handleTab(idx) {
if(idx === this.tab_current) return
switch(idx) {
case 0:
if(this.collectList.length === 0) {
this.collectList.resetParams()
this.getCollectData()
}
break;
case 1:
if(this.history_list.length === 0) {
this.last_time = ''
this.getHistoryData()
}
break;
default:
break;
}
this.tab_current = idx
},
// 封装得不够透彻
tolowerC() {
this.getCollect()
console.log('收藏下拉')
},
refreshC() {
console.log('收藏下拉')
this.collectList.resetParams()
this.collect_list = []
this.getCollectData()
},
tolowerH() {
console.log('历史下拉')
this.getHistoryData()
},
refreshH() {
console.log('历史上拉')
this.last_time = ''
this.history_list = []
this.getHistoryData()
},
handleDetail(is_lose, goods_id) {
if(is_lose === '1') return this.$toast({title: '商品已失效'})
uni.navigateTo({
url: `/pages/goods/detail?goods_id=${goods_id}`
})
},
async getCollect() {
const list = await this.collectList.getList({ keywords: '大成'}, test)
console.log(list)
// 取消收藏
handleUnCollect(goods_id, wrapIdx, innerIdx) {
try{
uni.showModal({
title: '提示',
content: '您确定要删除这条收藏吗?',
confirmColor: '#FF661A',
success: async res => {
if(res.confirm) {
const { status } = await collectGoods(goods_id)
if(status) {
this.collect_list[wrapIdx].splice(innerIdx, 1)
this.$toast({title: '操作成功'})
}
}
}
})
}catch(e){
console.log(e)
this.$toast({title: '取消失败'})
//TODO handle the exception
}
},
// 删除单条历史
clearHistory(good, wrapIdx, innerIdx) {
try{
uni.showModal({
title: '提示',
content: '您确定要删除这条历史吗?',
confirmColor: '#FF661A',
success: async res => {
if(res.confirm) {
const { status, data } = await delHistory({bro_id: good.bro_id, goods_id: good.goods_id})
if(status) {
this.history_list[wrapIdx].date_list.splice(innerIdx, 1)
this.$toast({title: '删除成功'})
}
}
}
})
}catch(e){
console.log(e)
this.$toast({title: '删除失败'})
//TODO handle the exception
}
},
// 删除所有历史
async handleClearAll() {
try{
uni.showModal({
title: '提示',
content: '您确定要删除全部历史吗?',
confirmColor: '#FF661A',
success: async res => {
if(res.confirm) {
const { status } = await clearAllHistory()
if(status) {
this.history_list = []
this.$toast({title: '删除成功'})
}
}
}
})
}catch(e){
this.$toast({title: '删除失败'})
//TODO handle the exception
}
}
}
}
......@@ -99,19 +285,101 @@ export default {
<style lang="scss" scoped>
.wrapper {
min-height: 100vh;
background-color: #fff;
overflow: hidden;
.tab-wrap {
box-sizing: border-box;
width: 690rpx;
height: 98rpx;
border-bottom: 1px solid red;
line-height: 98rpx;
margin: 0 auto;
border-bottom: 2rpx solid $line;
.tab-item {
flex: 1;
}
.tab-active {
color: red;
position: relative;
color: $primary;
&::after {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
display: inline-block;
content: '';
width: 180rpx;
height: 2rpx;
background-color: $primary;
}
}
}
.list-wrap {
width: 100%;
box-sizing: border-box;
padding: 40rpx 0 0 0;
width: 690rpx;
height: calc(100vh - 98rpx);
margin: 0 auto;
.goods-item {
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 20rpx;
.goods-cover {
position: relative;
flex: 0 0 220rpx;
width: 220rpx;
height: 220rpx;
overflow: hidden;
border-radius: 8rpx;
margin-right: 20rpx;
.img {
width: 100%;
height: 100%;
}
.mask {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.3);
}
}
.goods-info {
height: 220rpx;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.hd {
width: 100%;
display: flex;
justify-content: space-between;
align-items: a;
.title {
flex: 1;
font-weight: bold;
@include text-ellipsis(1);
}
.btn {
color: $desc;
flex: 0 0 120rpx;
}
}
}
}
.desc {
font-size: 24rpx;
color: $desc;
}
}
}
</style>
<template>
<view class="wrapper w-100">
<view class="w-100 flex j-between a-center">
<view>头像</view>
<view
class="avatar"
@click="handleAvatar"
>
<template v-if="userInfo && userInfo.avatar">
<image
class="avatar-img"
:src="baseUrl + '/' + userInfo.avatar"
mode="aspectFit"
/>
</template>
<template v-else>
<open-data class="avatar-img" type="userAvatarUrl"></open-data>
</template>
<view class="userInfo">
<view class="item w-100 flex j-between a-center">
<view>头像</view>
<view
class="avatar"
@click="show"
>
<template v-if="userInfo && userInfo.avatar">
<image
class="avatar-img"
:src="baseUrl + '/' + userInfo.avatar"
mode="aspectFit"
/>
</template>
<template v-else>
<open-data class="avatar-img" type="userAvatarUrl"></open-data>
</template>
</view>
</view>
</view>
<view class="w-100 flex j-between a-center">
<view>昵称</view>
<view class="nickname">
<template v-if="userInfo && userInfo.nickname">
<input
type="text"
:value="userInfo.nickname"
confirm-type="done"
@confirm="changeNickname"
/>
</template>
<template v-else>
<open-data type="userNickName" lang="zh_CN"></open-data>
</template>
<view class="item w-100 flex j-between a-center">
<view>昵称</view>
<view class="nickname text-right">
<template v-if="userInfo && userInfo.nickname">
<input
type="text"
:value="userInfo.nickname"
confirm-type="done"
@confirm="changeNickname"
/>
</template>
<template v-else>
<open-data type="userNickName" lang="zh_CN"></open-data>
</template>
</view>
</view>
<view class="item w-100 flex j-between a-center">
<view>手机号</view>
<view class="nickname text-right">
{{userInfo.phone}}
</view>
</view>
</view>
<view class="logout-btn w-100 text-center" @click="logout">退出登录</view>
<uni-popup ref="popup" type="bottom">
<view class="w-100 actionSheet">
<view class="item" @click="editAavatara(0)">拍摄</view>
<view class="item" @click="editAavatara(1)">从相册选择</view>
<view class="item" @click="close">取消</view>
</view>
</uni-popup>
</view>
</template>
......@@ -61,22 +78,51 @@ export default {
methods: {
...mapActions('user', ['logout', 'setUserInfo']),
handleAvatar(e) {
uni.chooseImage({
count: 1,
success: async res => {
try{
const fileInfo = res.tempFiles[0]
if(fileInfo.size > 2097152) return this.$toast({title: '图片不能大于2M'})
const { status, data } = await editAvatar(fileInfo.path)
if(status) {
this.setUserInfo()
editAavatara(tapIndex) {
switch(tapIndex) {
case 0:
uni.chooseImage({
count: 1,
sourceType: ['camera'],
success: async res => {
try{
const fileInfo = res.tempFiles[0]
if(fileInfo.size > 2097152) return this.$toast({title: '图片不能大于2M'})
const { status, data } = await editAvatar(fileInfo.path)
if(status) {
this.setUserInfo()
this.$toast({title: '修改成功'})
}
}catch(e){
this.$toast({title: e.msg || '上传失败'})
}
}
}catch(e){
this.$toast({title: e.msg || '上传失败'})
}
})
break;
case 1:
uni.chooseImage({
count: 1,
sourceType: ['album'],
success: async res => {
try{
const fileInfo = res.tempFiles[0]
if(fileInfo.size > 2097152) return this.$toast({title: '图片不能大于2M'})
const { status, data } = await editAvatar(fileInfo.path)
if(status) {
this.setUserInfo()
this.$toast({title: '修改成功'})
}
}catch(e){
this.$toast({title: e.msg || '上传失败'})
}
}
})
break;
default:
break;
}
})
this.close()
},
async changeNickname(e) {
......@@ -84,10 +130,18 @@ export default {
const { status, data } = await editNickname(e.detail.value)
if(status) {
this.setUserInfo()
this.$toast({title: '修改成功'})
}
}catch(e){
this.$toast({title: e.msg || '修改失败'})
}
},
show() {
this.$refs.popup.open()
},
close() {
this.$refs.popup.close()
}
}
}
......@@ -96,14 +150,29 @@ export default {
<style lang="scss" scoped>
.wrapper {
margin-bottom: 98rpx;
.avatar {
width: 180rpx;
height: 180rpx;
.avatar-img {
width: 100%;
height: 100%;
padding-top: 57rpx;
color: #000;
.userInfo {
.item {
@include borderBox(20rpx, 30rpx);
background-color: #FFFFFF;
border-bottom: 1rpx solid $line;
&:last-child {
border-bottom: none;
}
.avatar {
width: 88rpx;
height: 88rpx;
border-radius: 8rpx;
.avatar-img {
width: 100%;
height: 100%;
}
}
}
}
.logout-btn {
position: fixed;
left: 0;
......@@ -113,7 +182,19 @@ export default {
line-height: 98rpx;
z-index: 1;
color: #fff;
background-color: red;
background-color: $primary;
}
.actionSheet {
text-align: center;
background-color: #fff;
.item {
padding: 24rpx 0;
border-bottom: 1rpx solid $line;
&:last-child {
border-bottom: none;
}
}
}
}
......
......@@ -32,20 +32,47 @@ export function throttle(callback, wait = 3000) {
const args = arguments;
const now = +new Date();
if (startTime && now < startTime + wait) {
console.log('000==')
clearTimeout(timer);
timer = setTimeout(function () {
startTime = now;
callback.apply(ctx, args);
}, wait);
} else {
console.logout("33")
startTime = now;
callback.apply(ctx, args);
}
}
}
/**
* @desc 防抖函数 cv的
* @param {需要防抖的函数} func
* @param {延迟时间} wait
* @param {是否立即执行} immediate
*/
export function debounce(func, wait = 1000, immediate = true) {
let timeout
console.log(func, wait)
return function(...args) {
console.log(args)
let context = this
if (timeout) clearTimeout(timeout)
if (immediate) {
let callNow = !timeout
timeout = setTimeout(function() {
timeout = null
}, wait)
if (callNow) func.apply(context, args)
} else {
timeout = setTimeout(function() {
func.apply(context, args)
}, wait)
}
}
}
/**
* 联系老板
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment