master
xy 1 year ago
parent fb0055118b
commit 62d73ef052

@ -42,8 +42,11 @@ let apiApp = {
// 用户商城订单
userOrders: '/api/mobile/user/orders',
userOrdersDetail: '/api/mobile/user/orders-detail',
// 购物车
cartIndex: '/api/mobile/cart/index',
cartShow: '/api/mobile/cart/show',
cartSave: '/api/mobile/cart/save',
cartDestroy: '/api/mobile/cart/destroy',
hospital: '/api/mobile/hospital/hospital',
banner: '/api/mobile/other/banner',
@ -98,6 +101,13 @@ const install = (Vue, vm) => {
const hospitalList = (params = {}) => vm.$u.get(apiApp.hospital, params)
const banner = (params = {}) => vm.$u.get(apiApp.banner, params)
// end
// 购物车
const cartList = (params = {}) => vm.$u.get(apiApp.cartIndex, params)
const cartShow = (params = {}) => vm.$u.get(apiApp.cartShow, params)
const cartSave = (params = {}) => vm.$u.post(apiApp.cartSave, params)
const cartDestroy = (params = {}) => vm.$u.get(apiApp.cartDestroy, params)
// end
vm.$u.api = {
@ -122,7 +132,8 @@ const install = (Vue, vm) => {
userAddress,userAddressShow,userAddressSave,userAddressDestroy,
// 用户商城订单
userOrders,userOrdersDetail,
// 购物车
cartList, cartSave, cartShow, cartDestroy
};
}

@ -1,23 +1,608 @@
<template>
<view>
<tabbar></tabbar>
<navbar :use-search="false" title="购物车"></navbar>
<view class="wrap">
<view class="carts">
<view
class="cart"
v-for="item in carts"
:key="item.id"
@tap="
() => {
if (item.product_sku && item.product_sku.stock_num) {
pickCart = item;
}
}
"
>
<view class="top">
<u-checkbox
class="cart__checkbox"
shape="circle"
:disabled="!(item.product_sku && item.product_sku.stock_num)"
:value="pickCart.id === item.id"
></u-checkbox>
<u-image
class="cart__img"
width="154rpx"
height="154rpx"
lazy-load
:src="
item.product && item.product.image
? item.product.image.url
: vuex_default_icon
"
></u-image>
<view class="cart__info">
<view class="cart__info--name">{{
item.product ? item.product.name : ""
}}</view>
<view class="cart__info--type" @tap.stop="getDetail(item)">
<text>
{{ item.product_sku ? item.product_sku.name : "" }}
</text>
<u-icon
name="arrow-down"
:size="22"
style="margin-left: 6rpx"
color="#333"
></u-icon>
</view>
<view class="cart__info--price">
<text>¥</text>
<text>{{
item.product_sku ? item.product_sku.price : ""
}}</text>
</view>
</view>
</view>
<view
class="bottom"
v-if="item.product_sku && item.product_sku.stock_num"
>
<u-number-box
v-model="item.num"
:size="22"
:input-width="54"
:input-height="44"
:min="1"
:max="item.product_sku ? item.product_sku.stock_num : 0"
@change.stop="saveCart(item)"
></u-number-box>
</view>
</view>
</view>
<u-loadmore
:status="status"
:margin-bottom="40"
:load-text="{
loadmore: '点击加载更多',
loading: '努力加载中',
nomore: '实在没有了',
}"
@tap.stop="getCarts"
/>
<view class="recommend">
<view class="recommend-title">
<view class="cir"></view>
<view class="cir"></view>
<view class="cir"></view>
<text>猜你喜欢</text>
<view class="cir"></view>
<view class="cir"></view>
<view class="cir"></view>
</view>
<view class="recommend-wrap">
<view
class="product-item"
v-for="item in recommendPro"
:key="item.id"
@tap="
$u.throttle(() => {
toDetail(item);
}, 500)
"
>
<view class="top">
<image
class="product-item__img"
:src="item.image ? item.image.url : ''"
mode="aspectFill"
></image>
</view>
<view class="bottom">
<view class="product-item__title">
{{ item.name }}
</view>
<view class="product-item__price">
<view>
<text>¥</text>
<text>{{ item.price }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="bar" :style="'bottom:' + barOffsetBottom + 'px'">
<view class="total">
<text>合计</text>
<text>¥</text>
<text>{{
pickCart.product_sku ? pickCart.product_sku.price : 0.0
}}</text>
</view>
<u-button
ripple
shape="circle"
:hair-line="false"
:custom-style="{
color: '#fff',
background: 'linear-gradient(0deg, #ff2a00 0%, #f26011 100%)',
width: '154rpx',
height: '60rpx',
'line-height': '60rpx',
'margin-left': '14rpx',
}"
>付款</u-button
>
</view>
</view>
<!-- 类型选择 -->
<u-popup
v-model="showType"
mode="bottom"
border-radius="14"
safe-area-inset-bottom
>
<view class="content">
<scroll-view scroll-y="true" style="height: 800rpx">
<view class="product">
<view
class="product-item"
:class="{
'product-item-active': cartDetail.product_sku_id === item.id,
}"
v-for="item in productDetail.product_skus || []"
:key="item.id"
@click="
() => {
if (item.stock_num) {
cartDetail.product_sku_id = item.id;
}
}
"
>
<view class="top">
<image
class="product-item__img"
:src="item.image ? item.image.url : ''"
mode="aspectFill"
></image>
<image
class="product-item__soldout"
v-if="!item.stock_num"
src="~@/package_sub/static/Shop/yishouxing.png"
mode="aspectFit"
></image>
</view>
<view class="bottom">
<view class="product-item__title">
{{ item.name }}
</view>
<view class="product-item__price">
<view>
<text>¥</text>
<text>{{ item.price }}</text>
</view>
</view>
</view>
<u-icon
class="product-item-active__check"
name="checkmark-circle-fill"
color="red"
:size="40"
v-if="cartDetail.product_sku_id === item.id"
></u-icon>
</view>
</view>
</scroll-view>
<view class="confirm-btn">
<u-button
:hair-line="false"
:custom-style="{
'background-image':
'linear-gradient(-90deg, #e26165 0%, #c10d12 94%, #c10d12 100%)',
color: '#fff',
width: '80%',
'margin-top': '20rpx',
}"
@click="saveCart(cartDetail)"
>确定</u-button
>
</view>
</view>
</u-popup>
<tabbar class="tabbar"></tabbar>
</view>
</template>
<script>
import tabbar from "@/package_sub/pages/Shop/Tabbar.vue";
import navbar from "@/package_sub/pages/Shop/Navbar.vue";
export default {
components: {
tabbar
navbar,
tabbar,
},
data() {
return {};
}
}
return {
pickCart: {},
carts: [],
select: {
page: 1,
page_size: 10,
"show_relation[0]": "product.image",
"show_relation[1]": "productSku",
},
total: 0,
status: "loadmore",
barOffsetBottom: 500,
recommendPro: [],
cartDetail: {},
productDetail: {},
showType: false,
};
},
methods: {
async getDetail(cart) {
this.cartDetail = cart;
try {
const res = await this.$u.api.productDetail({
id: cart.product_id,
});
this.productDetail = res.detail;
this.showType = true;
} catch (err) {
console.error(err);
}
},
async saveCart(data) {
try {
await this.$u.api.cartSave(data);
const res = await this.$u.api.cartShow({
id: data.id,
"show_relation[0]": "product.image",
"show_relation[1]": "productSku",
});
let index = this.carts.findIndex((i) => i.id === res.id);
if (index !== -1) {
this.carts.splice(index, 1, res);
}
this.showType = false;
} catch (err) {}
},
//
getElRect(elClass, dataVal) {
return new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(this);
query
.select("." + elClass)
.fields(
{
size: true,
},
(res) => {
// resnull
if (!res) {
setTimeout(() => {
this.getElRect(elClass);
}, 10);
return;
}
if (dataVal) {
this["dataVal"] = res.top;
}
resolve(res);
}
)
.exec();
});
},
async getCarts(isRefresh = false) {
if (isRefresh) {
this.status = "loadmore";
this.select.page = 1;
this.carts = [];
}
if (this.carts.length >= this.total && !isRefresh) {
this.status = "nomore";
return;
}
try {
this.status = "loading";
const res = await this.$u.api.cartList(this.select);
this.carts.push(...res.data);
this.total = res.total;
this.select.page++;
} catch (err) {
console.error(err);
} finally {
this.status = "loadmore";
}
},
toDetail(item) {
this.$u.route({
url: "/package_sub/pages/Shop/ProductDetail",
params: {
id: item.id,
},
});
},
async getRecommendPro() {
try {
const res = await this.$u.api.productList({
page: 1,
page_size: 6,
sort_name: "sort",
});
this.recommendPro = res.list?.data;
} catch (err) {}
},
},
mounted() {
this.getElRect("tabbar").then((res) => {
this.barOffsetBottom = res.height;
});
},
created() {
this.getRecommendPro();
this.getCarts(true);
},
};
</script>
<style lang="scss">
page {
background: #f5f5f5;
}
.recommend {
padding-bottom: 60rpx;
position: relative;
z-index: 2;
&-title {
display: flex;
align-items: center;
justify-content: center;
color: red;
& > text {
padding: 0 6rpx;
}
.cir {
background: red;
border-radius: 100%;
margin: 0 4rpx;
}
.cir:first-child,
.cir:last-child,
.cir:nth-child(2),
.cir:nth-last-child(2) {
width: 5rpx;
height: 5rpx;
}
.cir:nth-child(3),
.cir:nth-last-child(3) {
width: 8rpx;
height: 8rpx;
transform: translateY(-4rpx);
}
}
&-wrap {
padding: 0 20rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 20rpx 56rpx;
margin-top: 36rpx;
.product-item {
background: #fff;
padding: 12rpx;
.bottom {
padding-top: 16rpx;
}
&__img {
width: 100%;
height: 266rpx;
}
&__title {
font-size: 25rpx;
color: #333333;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
word-break: break-all;
}
&__price {
display: flex;
align-items: center;
justify-content: space-between;
text {
color: red;
}
text:nth-child(1) {
font-size: 21rpx;
}
text:nth-child(2) {
font-size: 32rpx;
}
}
}
}
}
.carts {
margin-top: 20rpx;
.cart {
background: #fff;
border-radius: 8rpx;
margin: 20rpx;
padding: 26rpx 22rpx 16rpx;
.top {
display: flex;
justify-content: space-between;
}
.bottom {
margin-top: 14rpx;
display: flex;
justify-content: flex-end;
}
&__checkbox {
align-self: center;
}
&__img {
border-radius: 6rpx;
}
&__info {
flex: 1;
margin-left: 28rpx;
&--name {
font-size: 24rpx;
color: #333333;
}
&--type {
margin-top: 14rpx;
font-size: 21rpx;
display: inline-block;
color: #666;
border-radius: 8rpx;
background-color: #f5f5f5;
padding: 14rpx 12rpx;
}
&--price {
margin-top: 14rpx;
color: red;
font-size: 21rpx;
& > text:nth-child(2) {
font-size: 24rpx;
padding-left: 4rpx;
}
}
}
}
}
.wrap {
position: relative;
}
.bar {
width: 100%;
background: #fff;
position: sticky;
bottom: 400rpx;
display: flex;
justify-content: flex-end;
align-items: center;
z-index: 10;
padding-right: 20rpx;
padding-bottom: 20rpx;
padding-top: 20rpx;
filter: drop-shadow(0 -1rpx 1rpx #00000012);
.total {
text {
font-size: 25rpx;
color: #ff0000;
}
& > text:nth-child(1) {
color: #333333;
}
}
}
.product {
padding: 80rpx 20rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 40rpx;
&-item {
background: #f5f5f5;
border: 1px solid transparent;
padding: 10rpx;
border-radius: 10rpx;
position: relative;
.bottom {
padding-top: 16rpx;
}
.top {
position: relative;
}
&__soldout {
height: 266rpx;
width: 100%;
padding: 40rpx;
box-sizing: border-box;
background: #ffffff99;
opacity: 0.86;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
&__img {
width: 100%;
height: 266rpx;
}
&__title {
font-size: 25rpx;
color: #333333;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
word-break: break-all;
}
&__price {
display: flex;
align-items: center;
justify-content: space-between;
text {
color: red;
}
text:nth-child(1) {
font-size: 21rpx;
}
text:nth-child(2) {
font-size: 32rpx;
}
}
&-active {
border-color: red;
&__check {
position: absolute;
top: 10rpx;
right: 10rpx;
}
}
}
}
</style>

File diff suppressed because it is too large Load Diff

@ -251,6 +251,10 @@ export default {
font-weight: normal;
font-size: 24rpx;
color: $u-main-color;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 180rpx;
}
.item-container {

@ -0,0 +1,224 @@
<template>
<view class="wrap">
<u-navbar back-text=""
is-back
title="评论"
back-icon-color="#fff"
:back-text-style="{
color: '#fff'
}"
title-color="#fff"
:background="{
backgroundImage: 'linear-gradient(0deg, #c10d12 0%, #c10d12 4%, #e26165 99%, #e26165 100%)'
}">
</u-navbar>
<view class="all-reply">
<view class="all-reply-top">全部回复{{ comment.allReply }}</view>
<view class="item" v-for="(item, index) in commentList" :key="index">
<view class="comment">
<view class="top">
<view class="left">
<view class="heart-photo"><image :src="item.url" mode=""></image></view>
<view class="user-info">
<view class="name">{{ item.name }}</view>
<view class="date">{{ item.date }}</view>
</view>
</view>
<view class="right" :class="{ highlight: item.isLike }">
<view class="num">{{ item.likeNum }}</view>
<u-icon v-if="!item.isLike" name="thumb-up" class="like" :size="30" color="#9a9a9a" @click="getLike(index)"></u-icon>
<u-icon v-if="item.isLike" name="thumb-up-fill" class="like" :size="30" @click="getLike(index)"></u-icon>
</view>
</view>
<view class="reply" v-if="item.reply">
<view class="username">{{ item.reply.name }}</view>
<view class="text">{{ item.reply.contentStr }}</view>
</view>
<view class="content">{{ item.contentText }}</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
commentList: [],
comment: ''
};
},
onLoad() {
this.getReply();
},
methods: {
//
getLike(index) {
if (index === 0 || index > 0) {
this.commentList[index].isLike = !this.commentList[index].isLike;
if (this.commentList[index].isLike == true) {
this.commentList[index].likeNum++;
} else {
this.commentList[index].likeNum--;
}
} else {
if (this.comment.isLike == true) {
this.comment.isLike = !this.comment.isLike;
this.comment.likeNum--;
} else {
this.comment.isLike = !this.comment.isLike;
this.comment.likeNum++;
}
}
},
//
getReply() {
this.comment = {
id: 1,
name: '叶轻眉',
date: '12-25 18:58',
contentText: '我不信伊朗会没有后续反应,美国肯定会为今天的事情付出代价的',
url: 'https://cdn.uviewui.com/uview/template/SmilingDog.jpg',
allReply: 12,
likeNum: 33,
isLikes: false
};
this.commentList = [
{
name: '新八几',
date: '12-25 18:58',
contentText: '不要乱打广告啊喂!虽然是真的超好用',
url: 'https://cdn.uviewui.com/uview/template/SmilingDog.jpg',
likeNum: 33,
isLike: false,
reply: {
name: 'uview',
contentStr: 'uview是基于uniapp的一个UI框架代码优美简洁宇宙超级无敌彩虹旋转好用用它'
}
},
{
name: '叶轻眉1',
date: '01-25 13:58',
url: 'https://cdn.uviewui.com/uview/template/SmilingDog.jpg',
contentText: '我不信伊朗会没有后续反应,美国肯定会为今天的事情付出代价的',
allReply: 0,
likeNum: 11,
isLike: false,
reply: {
name: '粘粘',
contentStr: '今天吃什么,明天吃什么,晚上吃什么,我只是一只小猫咪为什么要烦恼这么多'
}
},
{
name: '叶轻眉2',
date: '03-25 13:58',
contentText: '我不信伊朗会没有后续反应,美国肯定会为今天的事情付出代价的',
allReply: 0,
likeNum: 21,
url: 'https://cdn.uviewui.com/uview/template/SmilingDog.jpg',
isLike: false,
allReply: 2,
reply: {
name: '豆包',
contentStr: '想吃冰糖葫芦粘豆包但没钱5555.........'
}
},
{
name: '叶轻眉3',
date: '06-20 13:58',
contentText: '我不信伊朗会没有后续反应,美国肯定会为今天的事情付出代价的',
allReply: 0,
likeNum: 150,
url: 'https://cdn.uviewui.com/uview/template/SmilingDog.jpg',
isLike: false
}
];
}
}
};
</script>
<style lang="scss" scoped>
page {
background-color: #f2f2f2;
}
.comment {
padding: 30rpx;
font-size: 32rpx;
background-color: #ffffff;
.top {
display: flex;
justify-content: space-between;
}
.left {
display: flex;
.heart-photo {
image {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
background-color: #f2f2f2;
}
}
.user-info {
margin-left: 10rpx;
.name {
color: $uni-color-primary;
font-size: 28rpx;
margin-bottom: 4rpx;
}
.date {
font-size: 20rpx;
color: $u-light-color;
}
}
}
.right {
display: flex;
font-size: 20rpx;
align-items: center;
color: #9a9a9a;
.like {
margin-left: 6rpx;
}
.num{
font-size: 26rpx;
color: #9a9a9a;
}
}
.highlight {
color: $uni-color-primary;
.num{
color: $uni-color-primary;
}
}
}
.all-reply {
margin-top: 10rpx;
padding-top: 20rpx;
background-color: #ffffff;
.all-reply-top {
margin-left: 20rpx;
padding-left: 20rpx;
border-left: solid 4rpx $uni-color-primary;
font-size: 30rpx;
font-weight: bold;
}
.item {
border-bottom: solid 2rpx $u-border-color;
}
.reply {
padding: 20rpx;
background-color: rgb(242, 242, 242);
border-radius: 12rpx;
margin: 10rpx 0;
.username {
font-size: 24rpx;
color: #7a7a7a;
}
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

@ -119,6 +119,12 @@
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/Shop/Reply",
"style": {
"navigationStyle": "custom"
}
}
]
}

Loading…
Cancel
Save