Merge remote-tracking branch 'upload/master'

pull/100/head
痴货 2024-08-23 09:52:07 +08:00
commit 354a3dc1c8
75 changed files with 4594 additions and 3102 deletions

View File

@ -216,7 +216,7 @@
"h5": { "h5": {
"template": "index.html", "template": "index.html",
"router": { "router": {
"mode": "hash", "mode": "history",
"base": "./" "base": "./"
}, },
"sdkConfigs": { "sdkConfigs": {
@ -236,4 +236,4 @@
"_spaceID": "192b4892-5452-4e1d-9f09-eee1ece40639", "_spaceID": "192b4892-5452-4e1d-9f09-eee1ece40639",
"locale": "zh-Hans", "locale": "zh-Hans",
"fallbackLocale": "zh-Hans" "fallbackLocale": "zh-Hans"
} }

View File

@ -90,6 +90,7 @@
"dependencies": { "dependencies": {
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"luch-request": "^3.0.8", "luch-request": "^3.0.8",
"pinia": "^2.0.33", "pinia": "^2.0.33",
"pinia-plugin-persist-uni": "^1.2.0", "pinia-plugin-persist-uni": "^1.2.0",

View File

@ -307,6 +307,18 @@
"title": "编辑地址" "title": "编辑地址"
} }
}, },
{
"path": "goods_details_store/index",
"style": {
"navigationBarTitleText": "自提门店"
},
"meta": {
"auth": true,
"sync": true,
"title": "地址管理",
"group": "用户中心"
}
},
{ {
"path": "wallet/money", "path": "wallet/money",
"style": { "style": {

View File

@ -1,6 +1,11 @@
<!-- 拼团订单的详情 --> <!-- 拼团订单的详情 -->
<template> <template>
<s-layout title="拼团详情" class="detail-wrap" :navbar="state.data && !state.loading ? 'inner': 'normal'" :onShareAppMessage="shareInfo"> <s-layout
title="拼团详情"
class="detail-wrap"
:navbar="state.data && !state.loading ? 'inner' : 'normal'"
:onShareAppMessage="shareInfo"
>
<view v-if="state.loading"></view> <view v-if="state.loading"></view>
<view v-if="state.data && !state.loading"> <view v-if="state.data && !state.loading">
<!-- 团长信息 + 活动信息 --> <!-- 团长信息 + 活动信息 -->
@ -22,7 +27,7 @@
priceColor="#E1212B" priceColor="#E1212B"
@tap=" @tap="
sheep.$router.go('/pages/goods/groupon', { sheep.$router.go('/pages/goods/groupon', {
id: state.data.headRecord.activityId id: state.data.headRecord.activityId,
}) })
" "
:style="[{ top: Number(statusBarHeight + 108) + 'rpx' }]" :style="[{ top: Number(statusBarHeight + 108) + 'rpx' }]"
@ -71,7 +76,9 @@
</view> </view>
<view class="countdown-title ss-flex" v-else> <view class="countdown-title ss-flex" v-else>
还差 还差
<view class="num">{{ state.data.headRecord.userSize - state.data.headRecord.userCount }}</view> <view class="num"
>{{ state.data.headRecord.userSize - state.data.headRecord.userCount }}</view
>
拼团成功 拼团成功
<view class="ss-flex countdown-time"> <view class="ss-flex countdown-time">
<view class="countdown-h ss-flex ss-row-center">{{ endTime.h }}</view> <view class="countdown-h ss-flex ss-row-center">{{ endTime.h }}</view>
@ -109,7 +116,11 @@
</view> </view>
</view> </view>
<!-- 还有几个坑位 --> <!-- 还有几个坑位 -->
<view class="default-avatar ss-m-r-24 ss-m-b-20" v-for="item in state.remainNumber" :key="item"> <view
class="default-avatar ss-m-r-24 ss-m-b-20"
v-for="item in state.remainNumber"
:key="item"
>
<image <image
:src="sheep.$url.static('/static/img/shop/avatar/unknown.png')" :src="sheep.$url.static('/static/img/shop/avatar/unknown.png')"
class="avatar-img" class="avatar-img"
@ -154,11 +165,7 @@
</view> </view>
<view v-else class="ss-flex ss-row-center"> <view v-else class="ss-flex ss-row-center">
<view v-if="state.data.orderId"> <view v-if="state.data.orderId">
<button <button class="ss-reset-button join-btn" :disabled="endTime.ms <= 0" @tap="onShare">
class="ss-reset-button join-btn"
:disabled="endTime.ms <= 0"
@tap="onShare"
>
邀请好友来拼团 邀请好友来拼团
</button> </button>
</view> </view>
@ -174,11 +181,11 @@
</view> </view>
</view> </view>
<!-- TODO 芋艿这里暂时没接入 --> <view v-if="!isEmpty(state.goodsInfo)">
<view v-if="state.data.goods"> <!-- 规格与数量弹框 -->
<s-select-groupon-sku <s-select-groupon-sku
:show="state.showSelectSku" :show="state.showSelectSku"
:goodsInfo="state.data.goods" :goodsInfo="state.goodsInfo"
:grouponAction="state.grouponAction" :grouponAction="state.grouponAction"
:grouponNum="state.grouponNum" :grouponNum="state.grouponNum"
@buy="onBuy" @buy="onBuy"
@ -186,6 +193,7 @@
@close="state.showSelectSku = false" @close="state.showSelectSku = false"
/> />
</view> </view>
</view> </view>
<s-empty v-if="!state.data && !state.loading" icon="/static/goods-empty.png" /> <s-empty v-if="!state.data && !state.loading" icon="/static/goods-empty.png" />
@ -196,25 +204,28 @@
import { computed, reactive } from 'vue'; import { computed, reactive } from 'vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { useDurationTime } from '@/sheep/hooks/useGoods'; import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
import { showShareModal } from '@/sheep/hooks/useModal'; import { showShareModal } from '@/sheep/hooks/useModal';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import CombinationApi from "@/sheep/api/promotion/combination"; import CombinationApi from '@/sheep/api/promotion/combination';
import SpuApi from '@/sheep/api/product/spu';
const headerBg = sheep.$url.css('/static/img/shop/user/withdraw_bg.png'); const headerBg = sheep.$url.css('/static/img/shop/user/withdraw_bg.png');
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2; const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
const state = reactive({ const state = reactive({
data: {}, // data: {}, //
loading: true, goodsId: 0, // ID
grouponAction: 'create', goodsInfo: {}, //
showSelectSku: false, showSelectSku: false, //
grouponNum: 0, selectedSkuPrice: {}, //
number: 0, activity: {}, //
activity: {}, grouponId: 0, // ID
grouponNum: 0, //
grouponAction: 'create', //
combinationHeadId: null, // combinationHeadId: null, //
loading: true,
}); });
// todo
const shareInfo = computed(() => { const shareInfo = computed(() => {
if (isEmpty(state.data)) return {}; if (isEmpty(state.data)) return {};
return sheep.$platform.share.getShareInfo( return sheep.$platform.share.getShareInfo(
@ -224,15 +235,14 @@
desc: state.data.goods?.subtitle, desc: state.data.goods?.subtitle,
params: { params: {
page: '5', page: '5',
query: state.data.id, query: state.data.headRecord.id,
}, },
}, },
{ {
type: 'groupon', // type: 'groupon', //
title: state.data.headRecord.spuName, // title: state.data.headRecord.spuName, //
image: sheep.$url.cdn(state.data.headRecord.picUrl), // image: sheep.$url.cdn(state.data.headRecord.picUrl), //
price: state.data.goods?.price, // price: fen2yuan(state.data.headRecord.combinationPrice), //
original_price: state.data.goods?.original_price, //
}, },
); );
}); });
@ -244,33 +254,33 @@
}); });
} }
// TODO //
function onCreateGroupon() { function onCreateGroupon() {
state.grouponAction = 'create'; state.grouponAction = 'create';
state.grouponId = 0; state.grouponId = 0;
state.showSelectSku = true; state.showSelectSku = true;
} }
// TODO //
function onSkuChange(e) { function onSkuChange(e) {
state.selectedSkuPrice = e; state.selectedSkuPrice = e;
} }
// TODO //
function onJoinGroupon() { function onJoinGroupon() {
state.grouponAction = 'join'; state.grouponAction = 'join';
state.grouponId = state.data.activityId; state.grouponId = state.data.headRecord.activityId;
state.combinationHeadId = state.data.id; state.combinationHeadId = state.data.headRecord.id;
state.grouponNum = state.data.num; state.grouponNum = state.data.headRecord.userSize;
state.showSelectSku = true; state.showSelectSku = true;
} }
// TODO //
function onBuy(sku) { function onBuy(sku) {
sheep.$router.go('/pages/order/confirm', { sheep.$router.go('/pages/order/confirm', {
data: JSON.stringify({ data: JSON.stringify({
order_type: 'goods', order_type: 'goods',
combinationActivityId: state.data.activity.id, combinationActivityId: state.activity.id,
combinationHeadId: state.combinationHeadId, combinationHeadId: state.combinationHeadId,
items: [ items: [
{ {
@ -295,8 +305,29 @@
state.remainNumber = remainNumber > 0 ? remainNumber : 0; state.remainNumber = remainNumber > 0 ? remainNumber : 0;
// //
const { data: activity } = await CombinationApi.getCombinationActivity(data.headRecord.activityId); const { data: activity } = await CombinationApi.getCombinationActivity(
data.headRecord.activityId,
);
state.activity = activity; state.activity = activity;
state.grouponNum = activity.userSize;
//
const { data: spu } = await SpuApi.getSpuDetail(activity.spuId);
state.goodsId = spu.id;
//
activity.products.forEach((product) => {
spu.price = Math.min(spu.price, product.combinationPrice); // SPU
});
state.goodsInfo = spu;
// 使
spu.skus.forEach((sku) => {
const product = activity.products.find((product) => product.skuId === sku.id);
if (product) {
sku.price = product.combinationPrice;
} else {
//
sku.stock = 0;
}
});
} else { } else {
state.data = null; state.data = null;
} }
@ -316,8 +347,7 @@
.recharge-box { .recharge-box {
position: relative; position: relative;
margin-bottom: 120rpx; margin-bottom: 120rpx;
background: v-bind(headerBg) center/750rpx 100% background: v-bind(headerBg) center/750rpx 100% no-repeat,
no-repeat,
linear-gradient(115deg, #f44739 0%, #ff6600 100%); linear-gradient(115deg, #f44739 0%, #ff6600 100%);
border-radius: 0 0 5% 5%; border-radius: 0 0 5% 5%;
height: 100rpx; height: 100rpx;

View File

@ -24,9 +24,9 @@
</view> </view>
<view class="border-bottom"> <view class="border-bottom">
<s-goods-item <s-goods-item
:img="record.picUrl" :img="record.picUrl"
:title="record.spuName" :title="record.spuName"
:price="record.combinationPrice" :price="record.combinationPrice"
> >
<template #groupon> <template #groupon>
<view class="ss-flex"> <view class="ss-flex">
@ -67,8 +67,8 @@
import { reactive } from 'vue'; import { reactive } from 'vue';
import { onLoad, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'; import { onLoad, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import _ from 'lodash'; import _ from 'lodash-es';
import {formatOrderColor} from "@/sheep/hooks/useGoods"; import { formatOrderColor } from '@/sheep/hooks/useGoods';
import { resetPagination } from '@/sheep/util'; import { resetPagination } from '@/sheep/util';
import CombinationApi from '@/sheep/api/promotion/combination'; import CombinationApi from '@/sheep/api/promotion/combination';
@ -121,7 +121,7 @@
if (code !== 0) { if (code !== 0) {
return; return;
} }
state.pagination.list = _.concat(state.pagination.list, data.list) state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total; state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore'; state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
} }

View File

@ -63,7 +63,7 @@
import { reactive } from 'vue'; import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import _ from 'lodash'; import _ from 'lodash-es';
import RewardActivityApi from '@/sheep/api/promotion/rewardActivity'; import RewardActivityApi from '@/sheep/api/promotion/rewardActivity';
import { formatRewardActivityRule } from '@/sheep/hooks/useGoods'; import { formatRewardActivityRule } from '@/sheep/hooks/useGoods';
import SpuApi from '@/sheep/api/product/spu'; import SpuApi from '@/sheep/api/product/spu';
@ -118,7 +118,7 @@
const { code, data } = await SpuApi.getSpuPage({ const { code, data } = await SpuApi.getSpuPage({
pageNo: state.pagination.pageNo, pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize, pageSize: state.pagination.pageSize,
...params ...params,
}); });
if (code !== 0) { if (code !== 0) {
return; return;

View File

@ -32,7 +32,7 @@
<script setup> <script setup>
import { reactive, watch } from 'vue'; import { reactive, watch } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import GoodsItem from './goods.vue'; import GoodsItem from './goods.vue';
import OrderItem from './order.vue'; import OrderItem from './order.vue';
import OrderApi from '@/sheep/api/trade/order'; import OrderApi from '@/sheep/api/trade/order';

View File

@ -1,74 +1,90 @@
<!-- 分销首页明细列表 --> <!-- 分销首页明细列表 -->
<template> <template>
<view class="distribution-log-wrap"> <view class="distribution-log-wrap">
<view class="header-box"> <view class="header-box">
<image class="header-bg" :src="sheep.$url.static('/static/img/shop/commission/title2.png')" /> <image class="header-bg" :src="sheep.$url.static('/static/img/shop/commission/title2.png')" />
<view class="ss-flex header-title"> <view class="ss-flex header-title">
<view class="title">实时动态</view> <view class="title">实时动态</view>
<text class="cicon-forward" /> <text class="cicon-forward" />
</view> </view>
</view> </view>
<scroll-view scroll-y="true" @scrolltolower="loadmore" class="scroll-box log-scroll" <scroll-view
scroll-with-animation="true"> scroll-y="true"
<view v-if="state.pagination.list"> @scrolltolower="loadmore"
<view class="log-item-box ss-flex ss-row-between" v-for="item in state.pagination.list" :key="item.id"> class="scroll-box log-scroll"
<view class="log-item-wrap"> scroll-with-animation="true"
<view class="log-item ss-flex ss-ellipsis-1 ss-col-center"> >
<view class="ss-flex ss-col-center"> <view v-if="state.pagination.list">
<image class="log-img" :src="sheep.$url.static('/static/img/shop/avatar/notice.png')" mode="aspectFill" /> <view
</view> class="log-item-box ss-flex ss-row-between"
<view class="log-text ss-ellipsis-1"> v-for="item in state.pagination.list"
:key="item.id"
>
<view class="log-item-wrap">
<view class="log-item ss-flex ss-ellipsis-1 ss-col-center">
<view class="ss-flex ss-col-center">
<image
class="log-img"
:src="sheep.$url.static('/static/img/shop/avatar/notice.png')"
mode="aspectFill"
/>
</view>
<view class="log-text ss-ellipsis-1">
{{ item.title }} {{ fen2yuan(item.price) }} {{ item.title }} {{ fen2yuan(item.price) }}
</view> </view>
</view> </view>
</view> </view>
<text class="log-time">{{ dayjs(item.createTime).fromNow() }}</text> <text class="log-time">{{ dayjs(item.createTime).fromNow() }}</text>
</view> </view>
</view> </view>
<!-- 加载更多 --> <!-- 加载更多 -->
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" color="#333333" <uni-load-more
@tap="loadmore" /> v-if="state.pagination.total > 0"
</scroll-view> :status="state.loadStatus"
</view> color="#333333"
@tap="loadmore"
/>
</scroll-view>
</view>
</template> </template>
<script setup> <script setup>
import sheep from '@/sheep'; import sheep from '@/sheep';
import { reactive } from 'vue'; import { reactive } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import BrokerageApi from '@/sheep/api/trade/brokerage'; import BrokerageApi from '@/sheep/api/trade/brokerage';
import { fen2yuan } from '../../../sheep/hooks/useGoods'; import { fen2yuan } from '../../../sheep/hooks/useGoods';
const state = reactive({ const state = reactive({
loadStatus: '', loadStatus: '',
pagination: { pagination: {
list: [], list: [],
total: 0, total: 0,
pageNo: 1, pageNo: 1,
pageSize: 1, pageSize: 1,
}, },
}); });
async function getLog() { async function getLog() {
state.loadStatus = 'loading'; state.loadStatus = 'loading';
const { code, data } = await BrokerageApi.getBrokerageRecordPage({ const { code, data } = await BrokerageApi.getBrokerageRecordPage({
pageNo: state.pagination.pageNo, pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize pageSize: state.pagination.pageSize,
}); });
if (code !== 0) { if (code !== 0) {
return; return;
} }
state.pagination.list = _.concat(state.pagination.list, data.list); state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total; state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore'; state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
} }
getLog(); getLog();
// //
function loadmore() { function loadmore() {
if (state.loadStatus === 'noMore') { if (state.loadStatus === 'noMore') {
return; return;
} }
@ -78,88 +94,88 @@
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.distribution-log-wrap { .distribution-log-wrap {
width: 690rpx; width: 690rpx;
margin: 0 auto; margin: 0 auto;
margin-bottom: 20rpx; margin-bottom: 20rpx;
border-radius: 12rpx; border-radius: 12rpx;
z-index: 3; z-index: 3;
position: relative; position: relative;
.header-box { .header-box {
width: 690rpx; width: 690rpx;
height: 76rpx; height: 76rpx;
position: relative; position: relative;
.header-bg { .header-bg {
width: 690rpx; width: 690rpx;
height: 76rpx; height: 76rpx;
} }
.header-title { .header-title {
position: absolute; position: absolute;
left: 20rpx; left: 20rpx;
top: 24rpx; top: 24rpx;
} }
.title { .title {
font-size: 28rpx; font-size: 28rpx;
font-weight: 500; font-weight: 500;
color: #ffffff; color: #ffffff;
line-height: 30rpx; line-height: 30rpx;
} }
.cicon-forward { .cicon-forward {
font-size: 30rpx; font-size: 30rpx;
font-weight: 400; font-weight: 400;
color: #ffffff; color: #ffffff;
line-height: 30rpx; line-height: 30rpx;
} }
} }
.log-scroll { .log-scroll {
height: 600rpx; height: 600rpx;
background: #fdfae9; background: #fdfae9;
padding: 10rpx 20rpx 0; padding: 10rpx 20rpx 0;
box-sizing: border-box; box-sizing: border-box;
border-radius: 0 0 12rpx 12rpx; border-radius: 0 0 12rpx 12rpx;
.log-item-box { .log-item-box {
margin-bottom: 20rpx; margin-bottom: 20rpx;
.log-time { .log-time {
// margin-left: 30rpx; // margin-left: 30rpx;
text-align: right; text-align: right;
font-size: 24rpx; font-size: 24rpx;
font-family: OPPOSANS; font-family: OPPOSANS;
font-weight: 400; font-weight: 400;
color: #c4c4c4; color: #c4c4c4;
} }
} }
.loadmore-wrap { .loadmore-wrap {
// line-height: 80rpx; // line-height: 80rpx;
} }
.log-item { .log-item {
// background: rgba(#ffffff, 0.2); // background: rgba(#ffffff, 0.2);
border-radius: 24rpx; border-radius: 24rpx;
padding: 6rpx 20rpx 6rpx 12rpx; padding: 6rpx 20rpx 6rpx 12rpx;
.log-img { .log-img {
width: 40rpx; width: 40rpx;
height: 40rpx; height: 40rpx;
border-radius: 50%; border-radius: 50%;
margin-right: 10rpx; margin-right: 10rpx;
} }
.log-text { .log-text {
max-width: 480rpx; max-width: 480rpx;
font-size: 24rpx; font-size: 24rpx;
font-weight: 500; font-weight: 500;
color: #333333; color: #333333;
} }
} }
} }
} }
</style> </style>

View File

@ -14,12 +14,18 @@
> >
<template #rightBottom> <template #rightBottom>
<view class="ss-flex ss-row-between"> <view class="ss-flex ss-row-between">
<view class="commission-num" v-if="item.brokerageMinPrice === undefined"></view> <view class="commission-num" v-if="item.brokerageMinPrice === undefined"
<view class="commission-num" v-else-if="item.brokerageMinPrice === item.brokerageMaxPrice"> >预计佣金计算中</view
>
<view
class="commission-num"
v-else-if="item.brokerageMinPrice === item.brokerageMaxPrice"
>
预计佣金{{ fen2yuan(item.brokerageMinPrice) }} 预计佣金{{ fen2yuan(item.brokerageMinPrice) }}
</view> </view>
<view class="commission-num" v-else> <view class="commission-num" v-else>
预计佣金{{ fen2yuan(item.brokerageMinPrice) }} ~ {{ fen2yuan(item.brokerageMaxPrice) }} 预计佣金{{ fen2yuan(item.brokerageMinPrice) }} ~
{{ fen2yuan(item.brokerageMaxPrice) }}
</view> </view>
<button <button
class="ss-reset-button share-btn ui-BG-Main-Gradient" class="ss-reset-button share-btn ui-BG-Main-Gradient"
@ -53,7 +59,7 @@
import $share from '@/sheep/platform/share'; import $share from '@/sheep/platform/share';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue'; import { reactive } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import { showShareModal } from '@/sheep/hooks/useModal'; import { showShareModal } from '@/sheep/hooks/useModal';
import SpuApi from '@/sheep/api/product/spu'; import SpuApi from '@/sheep/api/product/spu';
import BrokerageApi from '@/sheep/api/trade/brokerage'; import BrokerageApi from '@/sheep/api/trade/brokerage';

View File

@ -41,10 +41,7 @@
<view class="no-box ss-flex ss-col-center ss-row-between"> <view class="no-box ss-flex ss-col-center ss-row-between">
<text class="order-code">订单编号{{ item.bizId }}</text> <text class="order-code">订单编号{{ item.bizId }}</text>
<text class="order-state"> <text class="order-state">
{{ {{ item.status === 0 ? '待结算' : item.status === 1 ? '已结算' : '已取消' }}
item.status === 0 ? '待结算'
: item.status === 1 ? '已结算' : '已取消'
}}
( 佣金 {{ fen2yuan(item.price) }} ) ( 佣金 {{ fen2yuan(item.price) }} )
</text> </text>
</view> </view>
@ -77,7 +74,7 @@
import sheep from '@/sheep'; import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue'; import { reactive } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import { onPageScroll } from '@dcloudio/uni-app'; import { onPageScroll } from '@dcloudio/uni-app';
import { resetPagination } from '@/sheep/util'; import { resetPagination } from '@/sheep/util';
import BrokerageApi from '@/sheep/api/trade/brokerage'; import BrokerageApi from '@/sheep/api/trade/brokerage';

View File

@ -235,7 +235,7 @@
import sheep from '@/sheep'; import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { computed, reactive, ref } from 'vue'; import { computed, reactive, ref } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import { onPageScroll } from '@dcloudio/uni-app'; import { onPageScroll } from '@dcloudio/uni-app';
import BrokerageApi from '@/sheep/api/trade/brokerage'; import BrokerageApi from '@/sheep/api/trade/brokerage';

View File

@ -1,196 +1,243 @@
<!-- 分销 - 佣金明细 --> <!-- 分销 - 佣金明细 -->
<template> <template>
<s-layout class="wallet-wrap" title="佣金"> <s-layout class="wallet-wrap" title="佣金">
<!-- 钱包卡片 --> <!-- 钱包卡片 -->
<view class="header-box ss-flex ss-row-center ss-col-center"> <view class="header-box ss-flex ss-row-center ss-col-center">
<view class="card-box ui-BG-Main ui-Shadow-Main"> <view class="card-box ui-BG-Main ui-Shadow-Main">
<view class="card-head ss-flex ss-col-center"> <view class="card-head ss-flex ss-col-center">
<view class="card-title ss-m-r-10">当前佣金</view> <view class="card-title ss-m-r-10">当前佣金</view>
<view @tap="state.showMoney = !state.showMoney" class="ss-eye-icon" <view
:class="state.showMoney ? 'cicon-eye' : 'cicon-eye-off'" /> @tap="state.showMoney = !state.showMoney"
</view> class="ss-eye-icon"
<view class="ss-flex ss-row-between ss-col-center ss-m-t-30"> :class="state.showMoney ? 'cicon-eye' : 'cicon-eye-off'"
<view class="money-num">{{ state.showMoney ? fen2yuan(state.summary.withdrawPrice || 0) : '*****' }}</view> />
<view class="ss-flex"> </view>
<view class="ss-m-r-20"> <view class="ss-flex ss-row-between ss-col-center ss-m-t-30">
<button class="ss-reset-button withdraw-btn" @tap="sheep.$router.go('/pages/commission/withdraw')"> <view class="money-num">{{
提现 state.showMoney ? fen2yuan(state.summary.withdrawPrice || 0) : '*****'
</button> }}</view>
</view> <view class="ss-flex">
<button class="ss-reset-button balance-btn ss-m-l-20" @tap="state.showModal = true"> <view class="ss-m-r-20">
转余额 <button
</button> class="ss-reset-button withdraw-btn"
</view> @tap="sheep.$router.go('/pages/commission/withdraw')"
</view> >
提现
</button>
</view>
<button class="ss-reset-button balance-btn ss-m-l-20" @tap="state.showModal = true">
转余额
</button>
</view>
</view>
<view class="ss-flex"> <view class="ss-flex">
<view class="loading-money"> <view class="loading-money">
<view class="loading-money-title">冻结佣金</view> <view class="loading-money-title">冻结佣金</view>
<view class="loading-money-num"> <view class="loading-money-num">
{{ state.showMoney ? fen2yuan(state.summary.frozenPrice || 0) : '*****' }} {{ state.showMoney ? fen2yuan(state.summary.frozenPrice || 0) : '*****' }}
</view> </view>
</view> </view>
<view class="loading-money ss-m-l-100"> <view class="loading-money ss-m-l-100">
<view class="loading-money-title">可提现佣金</view> <view class="loading-money-title">可提现佣金</view>
<view class="loading-money-num"> <view class="loading-money-num">
{{ state.showMoney ? fen2yuan(state.summary.brokeragePrice || 0) : '*****' }} {{ state.showMoney ? fen2yuan(state.summary.brokeragePrice || 0) : '*****' }}
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<su-sticky> <su-sticky>
<!-- 统计 --> <!-- 统计 -->
<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between"> <view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
<uni-datetime-picker v-model="state.date" type="daterange" @change="onChangeTime" :end="state.today"> <uni-datetime-picker
<button class="ss-reset-button date-btn"> v-model="state.date"
<text>{{ dateFilterText }}</text> type="daterange"
<text class="cicon-drop-down ss-seldate-icon" /> @change="onChangeTime"
</button> :end="state.today"
</uni-datetime-picker> >
<button class="ss-reset-button date-btn">
<text>{{ dateFilterText }}</text>
<text class="cicon-drop-down ss-seldate-icon" />
</button>
</uni-datetime-picker>
<view class="total-box"> <view class="total-box">
<!-- TODO 芋艿这里暂时不考虑做 --> <!-- TODO 芋艿这里暂时不考虑做 -->
<!-- <view class="ss-m-b-10">总收入{{ state.pagination.income.toFixed(2) }}</view> --> <!-- <view class="ss-m-b-10">总收入{{ state.pagination.income.toFixed(2) }}</view> -->
<!-- <view>总支出{{ (-state.pagination.expense).toFixed(2) }}</view> --> <!-- <view>总支出{{ (-state.pagination.expense).toFixed(2) }}</view> -->
</view> </view>
</view> </view>
<su-tabs :list="tabMaps" @change="onChangeTab" :scrollable="false" :current="state.currentTab" /> <su-tabs
</su-sticky> :list="tabMaps"
<s-empty v-if="state.pagination.total === 0" icon="/static/data-empty.png" text="暂无数据"></s-empty> @change="onChangeTab"
:scrollable="false"
:current="state.currentTab"
/>
</su-sticky>
<s-empty
v-if="state.pagination.total === 0"
icon="/static/data-empty.png"
text="暂无数据"
></s-empty>
<!-- 转余额弹框 --> <!-- 转余额弹框 -->
<su-popup :show="state.showModal" type="bottom" round="20" @close="state.showModal = false" showClose> <su-popup
<view class="ss-p-x-20 ss-p-y-30"> :show="state.showModal"
<view class="model-title ss-m-b-30 ss-m-l-20">转余额</view> type="bottom"
<view class="model-subtitle ss-m-b-100 ss-m-l-20">将您的佣金转到余额中继续消费</view> round="20"
<view class="input-box ss-flex ss-col-center border-bottom ss-m-b-70 ss-m-x-20"> @close="state.showModal = false"
<view class="unit"></view> showClose
<uni-easyinput :inputBorder="false" class="ss-flex-1 ss-p-l-10" v-model="state.price" type="number" >
placeholder="请输入金额" /> <view class="ss-p-x-20 ss-p-y-30">
</view> <view class="model-title ss-m-b-30 ss-m-l-20">转余额</view>
<button class="ss-reset-button model-btn ui-BG-Main-Gradient ui-Shadow-Main" @tap="onConfirm"> <view class="model-subtitle ss-m-b-100 ss-m-l-20">将您的佣金转到余额中继续消费</view>
确定 <view class="input-box ss-flex ss-col-center border-bottom ss-m-b-70 ss-m-x-20">
</button> <view class="unit"></view>
</view> <uni-easyinput
</su-popup> :inputBorder="false"
class="ss-flex-1 ss-p-l-10"
v-model="state.price"
type="number"
placeholder="请输入金额"
/>
</view>
<button
class="ss-reset-button model-btn ui-BG-Main-Gradient ui-Shadow-Main"
@tap="onConfirm"
>
确定
</button>
</view>
</su-popup>
<!-- 钱包记录 --> <!-- 钱包记录 -->
<view v-if="state.pagination.total > 0"> <view v-if="state.pagination.total > 0">
<view class="wallet-list ss-flex border-bottom" v-for="item in state.pagination.list" :key="item.id"> <view
<view class="list-content"> class="wallet-list ss-flex border-bottom"
<view class="title-box ss-flex ss-row-between ss-m-b-20"> v-for="item in state.pagination.list"
<text class="title ss-line-1">{{ item.title }}</text> :key="item.id"
<view class="money"> >
<text v-if="item.price >= 0" class="add">+{{ fen2yuan(item.price) }}</text> <view class="list-content">
<text v-else class="minus">{{ fen2yuan(item.price) }}</text> <view class="title-box ss-flex ss-row-between ss-m-b-20">
</view> <text class="title ss-line-1">{{ item.title }}</text>
</view> <view class="money">
<text class="time">{{ sheep.$helper.timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}</text> <text v-if="item.price >= 0" class="add">+{{ fen2yuan(item.price) }}</text>
</view> <text v-else class="minus">{{ fen2yuan(item.price) }}</text>
</view> </view>
</view> </view>
<text class="time">{{
sheep.$helper.timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss')
}}</text>
</view>
</view>
</view>
<!-- <u-gap></u-gap> --> <!-- <u-gap></u-gap> -->
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{ <uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多', contentdown: '上拉加载更多',
}" /> }"
</s-layout> />
</s-layout>
</template> </template>
<script setup> <script setup>
import { computed, reactive } from 'vue'; import { computed, reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash-es';
import BrokerageApi from '@/sheep/api/trade/brokerage'; import BrokerageApi from '@/sheep/api/trade/brokerage';
import { fen2yuan } from '@/sheep/hooks/useGoods'; import { fen2yuan } from '@/sheep/hooks/useGoods';
import { resetPagination } from '@/sheep/util'; import { resetPagination } from '@/sheep/util';
const headerBg = sheep.$url.css('/static/img/shop/user/wallet_card_bg.png'); const headerBg = sheep.$url.css('/static/img/shop/user/wallet_card_bg.png');
const state = reactive({ const state = reactive({
showMoney: false, showMoney: false,
summary: {}, // summary: {}, //
today: '', today: '',
date: [], date: [],
currentTab: 0, currentTab: 0,
pagination: { pagination: {
list: [], list: [],
total: 0, total: 0,
pageNo: 1, pageNo: 1,
pageSize: 1, pageSize: 1,
}, },
loadStatus: '', loadStatus: '',
price: undefined, price: undefined,
showModal: false, showModal: false,
}); });
const tabMaps = [{ const tabMaps = [
name: '分佣', {
value: '1', // BrokerageRecordBizTypeEnum.ORDER name: '分佣',
}, value: '1', // BrokerageRecordBizTypeEnum.ORDER
{ },
name: '提现', {
value: '2', // BrokerageRecordBizTypeEnum.WITHDRAW name: '提现',
} value: '2', // BrokerageRecordBizTypeEnum.WITHDRAW
]; },
];
const dateFilterText = computed(() => { const dateFilterText = computed(() => {
if (state.date[0] === state.date[1]) { if (state.date[0] === state.date[1]) {
return state.date[0]; return state.date[0];
} else { } else {
return state.date.join('~'); return state.date.join('~');
} }
}); });
async function getLogList() { async function getLogList() {
state.loadStatus = 'loading'; state.loadStatus = 'loading';
let { code, data } = await BrokerageApi.getBrokerageRecordPage({ let { code, data } = await BrokerageApi.getBrokerageRecordPage({
pageSize: state.pagination.pageSize, pageSize: state.pagination.pageSize,
pageNo: state.pagination.pageNo, pageNo: state.pagination.pageNo,
bizType: tabMaps[state.currentTab].value, bizType: tabMaps[state.currentTab].value,
'createTime[0]': state.date[0] + ' 00:00:00', 'createTime[0]': state.date[0] + ' 00:00:00',
'createTime[1]': state.date[1] + ' 23:59:59', 'createTime[1]': state.date[1] + ' 23:59:59',
}); });
if (code !== 0) { if (code !== 0) {
return; return;
} }
state.pagination.list = _.concat(state.pagination.list, data.list); state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total; state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore'; state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
} }
function onChangeTab(e) { function onChangeTab(e) {
resetPagination(state.pagination);
state.currentTab = e.index;
getLogList();
}
function onChangeTime(e) {
state.date[0] = e[0];
state.date[1] = e[e.length - 1];
resetPagination(state.pagination); resetPagination(state.pagination);
getLogList(); state.currentTab = e.index;
} getLogList();
}
// function onChangeTime(e) {
async function onConfirm() { state.date[0] = e[0];
if (state.price <= 0) { state.date[1] = e[e.length - 1];
sheep.$helper.toast('请输入正确的金额'); resetPagination(state.pagination);
return; getLogList();
} }
uni.showModal({
title: '提示', //
content: '确认把您的佣金转入到余额钱包中?', async function onConfirm() {
success: async function(res) { if (state.price <= 0) {
if (!res.confirm) { sheep.$helper.toast('请输入正确的金额');
return;
}
uni.showModal({
title: '提示',
content: '确认把您的佣金转入到余额钱包中?',
success: async function (res) {
if (!res.confirm) {
return; return;
} }
const { code } = await BrokerageApi.createBrokerageWithdraw({ const { code } = await BrokerageApi.createBrokerageWithdraw({
type: 1, // type: 1, //
price: state.price * 100, price: state.price * 100,
@ -199,272 +246,273 @@
state.showModal = false; state.showModal = false;
await getAgentInfo(); await getAgentInfo();
onChangeTab({ onChangeTab({
index: 1 index: 1,
}); });
} }
} },
}); });
} }
async function getAgentInfo() { async function getAgentInfo() {
const { code, data } = await BrokerageApi.getBrokerageUserSummary(); const { code, data } = await BrokerageApi.getBrokerageUserSummary();
if (code !== 0) { if (code !== 0) {
return; return;
} }
state.summary = data; state.summary = data;
} }
onLoad(async (options) => { onLoad(async (options) => {
state.today = dayjs().format('YYYY-MM-DD'); state.today = dayjs().format('YYYY-MM-DD');
state.date = [state.today, state.today]; state.date = [state.today, state.today];
if (options.type === 2) { // tab if (options.type === 2) {
// tab
state.currentTab = 1; state.currentTab = 1;
} }
getLogList(); getLogList();
getAgentInfo(); getAgentInfo();
}); });
onReachBottom(() => { onReachBottom(() => {
if (state.loadStatus === 'noMore') { if (state.loadStatus === 'noMore') {
return; return;
} }
state.pagination.pageNo++; state.pagination.pageNo++;
getLogList(); getLogList();
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// //
.header-box { .header-box {
background-color: $white; background-color: $white;
padding: 30rpx; padding: 30rpx;
.card-box { .card-box {
width: 100%; width: 100%;
min-height: 300rpx; min-height: 300rpx;
padding: 40rpx; padding: 40rpx;
background-size: 100% 100%; background-size: 100% 100%;
border-radius: 30rpx; border-radius: 30rpx;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
z-index: 1; z-index: 1;
box-sizing: border-box; box-sizing: border-box;
&::after { &::after {
content: ''; content: '';
display: block; display: block;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 2; z-index: 2;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
background: v-bind(headerBg) no-repeat; background: v-bind(headerBg) no-repeat;
pointer-events: none; pointer-events: none;
} }
.card-head { .card-head {
color: $white; color: $white;
font-size: 24rpx; font-size: 24rpx;
} }
.ss-eye-icon { .ss-eye-icon {
font-size: 40rpx; font-size: 40rpx;
color: $white; color: $white;
} }
.money-num { .money-num {
font-size: 40rpx; font-size: 40rpx;
line-height: normal; line-height: normal;
font-weight: 500; font-weight: 500;
color: $white; color: $white;
font-family: OPPOSANS; font-family: OPPOSANS;
} }
.reduce-num { .reduce-num {
font-size: 26rpx; font-size: 26rpx;
font-weight: 400; font-weight: 400;
color: $white; color: $white;
} }
.withdraw-btn { .withdraw-btn {
width: 120rpx; width: 120rpx;
height: 60rpx; height: 60rpx;
line-height: 60rpx; line-height: 60rpx;
border-radius: 30px; border-radius: 30px;
font-size: 24rpx; font-size: 24rpx;
font-weight: 500; font-weight: 500;
background-color: $white; background-color: $white;
color: var(--ui-BG-Main); color: var(--ui-BG-Main);
} }
.balance-btn { .balance-btn {
width: 120rpx; width: 120rpx;
height: 60rpx; height: 60rpx;
line-height: 60rpx; line-height: 60rpx;
border-radius: 30px; border-radius: 30px;
font-size: 24rpx; font-size: 24rpx;
font-weight: 500; font-weight: 500;
color: $white; color: $white;
border: 1px solid $white; border: 1px solid $white;
} }
} }
} }
.loading-money { .loading-money {
margin-top: 56rpx; margin-top: 56rpx;
.loading-money-title { .loading-money-title {
font-size: 24rpx; font-size: 24rpx;
font-weight: 400; font-weight: 400;
color: #ffffff; color: #ffffff;
line-height: normal; line-height: normal;
margin-bottom: 30rpx; margin-bottom: 30rpx;
} }
.loading-money-num { .loading-money-num {
font-size: 30rpx; font-size: 30rpx;
font-family: OPPOSANS; font-family: OPPOSANS;
font-weight: 500; font-weight: 500;
color: #fefefe; color: #fefefe;
} }
} }
// //
.filter-box { .filter-box {
height: 120rpx; height: 120rpx;
padding: 0 30rpx; padding: 0 30rpx;
background-color: $bg-page; background-color: $bg-page;
.total-box { .total-box {
font-size: 24rpx; font-size: 24rpx;
font-weight: 500; font-weight: 500;
color: $dark-9; color: $dark-9;
} }
.date-btn { .date-btn {
background-color: $white; background-color: $white;
line-height: 54rpx; line-height: 54rpx;
border-radius: 27rpx; border-radius: 27rpx;
padding: 0 20rpx; padding: 0 20rpx;
font-size: 24rpx; font-size: 24rpx;
font-weight: 500; font-weight: 500;
color: $dark-6; color: $dark-6;
.ss-seldate-icon { .ss-seldate-icon {
font-size: 50rpx; font-size: 50rpx;
color: $dark-9; color: $dark-9;
} }
} }
} }
// tab // tab
.wallet-tab-card { .wallet-tab-card {
.tab-item { .tab-item {
height: 80rpx; height: 80rpx;
position: relative; position: relative;
.tab-title { .tab-title {
font-size: 30rpx; font-size: 30rpx;
} }
.cur-tab-title { .cur-tab-title {
font-weight: $font-weight-bold; font-weight: $font-weight-bold;
} }
.tab-line { .tab-line {
width: 60rpx; width: 60rpx;
height: 6rpx; height: 6rpx;
border-radius: 6rpx; border-radius: 6rpx;
position: absolute; position: absolute;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
bottom: 2rpx; bottom: 2rpx;
background-color: var(--ui-BG-Main); background-color: var(--ui-BG-Main);
} }
} }
} }
// //
.wallet-list { .wallet-list {
padding: 30rpx; padding: 30rpx;
background-color: #ffff; background-color: #ffff;
.head-img { .head-img {
width: 70rpx; width: 70rpx;
height: 70rpx; height: 70rpx;
border-radius: 50%; border-radius: 50%;
background: $gray-c; background: $gray-c;
} }
.list-content { .list-content {
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
flex: 1; flex: 1;
.title { .title {
font-size: 28rpx; font-size: 28rpx;
color: $dark-3; color: $dark-3;
width: 400rpx; width: 400rpx;
} }
.time { .time {
color: $gray-c; color: $gray-c;
font-size: 22rpx; font-size: 22rpx;
} }
} }
.money { .money {
font-size: 28rpx; font-size: 28rpx;
font-weight: bold; font-weight: bold;
font-family: OPPOSANS; font-family: OPPOSANS;
.add { .add {
color: var(--ui-BG-Main); color: var(--ui-BG-Main);
} }
.minus { .minus {
color: $dark-3; color: $dark-3;
} }
} }
} }
.model-title { .model-title {
font-size: 36rpx; font-size: 36rpx;
font-weight: bold; font-weight: bold;
color: #333333; color: #333333;
} }
.model-subtitle { .model-subtitle {
font-size: 26rpx; font-size: 26rpx;
color: #c2c7cf; color: #c2c7cf;
} }
.model-btn { .model-btn {
width: 100%; width: 100%;
height: 80rpx; height: 80rpx;
border-radius: 40rpx; border-radius: 40rpx;
font-size: 28rpx; font-size: 28rpx;
font-weight: 500; font-weight: 500;
color: #ffffff; color: #ffffff;
line-height: normal; line-height: normal;
} }
.input-box { .input-box {
height: 100rpx; height: 100rpx;
.unit { .unit {
font-size: 48rpx; font-size: 48rpx;
color: #333; color: #333;
font-weight: 500; font-weight: 500;
line-height: normal; line-height: normal;
} }
.uni-easyinput__placeholder-class { .uni-easyinput__placeholder-class {
font-size: 30rpx; font-size: 30rpx;
height: 40rpx; height: 40rpx;
line-height: normal; line-height: normal;
} }
} }
</style> </style>

View File

@ -16,13 +16,16 @@
<view class="title ss-m-t-50 ss-m-b-20 ss-m-x-20">{{ state.coupon.name }}</view> <view class="title ss-m-t-50 ss-m-b-20 ss-m-x-20">{{ state.coupon.name }}</view>
<view class="subtitle ss-m-b-50"> <view class="subtitle ss-m-b-50">
{{ fen2yuan(state.coupon.usePrice) }} {{ fen2yuan(state.coupon.usePrice) }}
{{ state.coupon.discountType === 1 {{
? '减 ' + fen2yuan(state.coupon.discountPrice) + ' 元' state.coupon.discountType === 1
: '打 ' + state.coupon.discountPercent / 10.0 + ' 折' }} ? '减 ' + fen2yuan(state.coupon.discountPrice) + ' 元'
: '打 ' + state.coupon.discountPercent / 10.0 + ' 折'
}}
</view> </view>
<button <button
class="ss-reset-button ss-m-b-30" class="ss-reset-button ss-m-b-30"
:class="state.coupon.canTake || state.coupon.status === 1 :class="
state.coupon.canTake || state.coupon.status === 1
? 'use-btn' // 使 ? 'use-btn' // 使
: 'disable-btn' : 'disable-btn'
" "
@ -31,7 +34,13 @@
> >
<text v-if="state.id > 0">{{ state.coupon.canTake ? '' : '' }}</text> <text v-if="state.id > 0">{{ state.coupon.canTake ? '' : '' }}</text>
<text v-else> <text v-else>
{{ state.coupon.status === 1 ? '立即使用' : state.coupon.status === 2 ? '已使用' : '已过期' }} {{
state.coupon.status === 1
? '立即使用'
: state.coupon.status === 2
? '已使用'
: '已过期'
}}
</text> </text>
</button> </button>
<view class="time ss-m-y-30" v-if="state.coupon.validityType === 2"> <view class="time ss-m-y-30" v-if="state.coupon.validityType === 2">
@ -140,7 +149,7 @@
import sheep from '@/sheep'; import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue'; import { reactive } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import CouponApi from '@/sheep/api/promotion/coupon'; import CouponApi from '@/sheep/api/promotion/coupon';
import { fen2yuan } from '@/sheep/hooks/useGoods'; import { fen2yuan } from '@/sheep/hooks/useGoods';
import SpuApi from '@/sheep/api/product/spu'; import SpuApi from '@/sheep/api/product/spu';
@ -176,7 +185,7 @@
const { code, data } = await SpuApi.getSpuPage({ const { code, data } = await SpuApi.getSpuPage({
categoryId: state.categoryId, categoryId: state.categoryId,
pageNo: state.pagination.pageNo, pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize pageSize: state.pagination.pageSize,
}); });
if (code !== 0) { if (code !== 0) {
return; return;
@ -197,7 +206,9 @@
// //
async function getCategoryList() { async function getCategoryList() {
const { data, code } = await CategoryApi.getCategoryListByIds(state.coupon.productScopeValues.join(',')); const { data, code } = await CategoryApi.getCategoryListByIds(
state.coupon.productScopeValues.join(','),
);
if (code !== 0) { if (code !== 0) {
return; return;
} }
@ -225,8 +236,10 @@
// //
async function getCouponContent() { async function getCouponContent() {
const { code, data } = state.id > 0 ? await CouponApi.getCouponTemplate(state.id) const { code, data } =
: await CouponApi.getCoupon(state.couponId); state.id > 0
? await CouponApi.getCouponTemplate(state.id)
: await CouponApi.getCoupon(state.couponId);
if (code !== 0) { if (code !== 0) {
return; return;
} }

View File

@ -45,7 +45,7 @@
<template #default> <template #default>
<button <button
class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center" class="ss-reset-button card-btn ss-flex ss-row-center ss-col-center"
:class=" item.status !== 1 ? 'disabled-btn': ''" :class="item.status !== 1 ? 'disabled-btn' : ''"
:disabled="item.status !== 1" :disabled="item.status !== 1"
@click.stop="sheep.$router.go('/pages/coupon/detail', { couponId: item.id })" @click.stop="sheep.$router.go('/pages/coupon/detail', { couponId: item.id })"
> >
@ -56,9 +56,14 @@
</view> </view>
</template> </template>
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{ <uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多', contentdown: '上拉加载更多',
}" @tap="loadMore" /> }"
@tap="loadMore"
/>
</s-layout> </s-layout>
</template> </template>
@ -66,7 +71,7 @@
import sheep from '@/sheep'; import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue'; import { reactive } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import { resetPagination } from '@/sheep/util'; import { resetPagination } from '@/sheep/util';
import CouponApi from '@/sheep/api/promotion/coupon'; import CouponApi from '@/sheep/api/promotion/coupon';
@ -78,7 +83,7 @@
list: [], list: [],
total: 0, total: 0,
pageNo: 1, pageNo: 1,
pageSize: 5 pageSize: 5,
}, },
loadStatus: '', loadStatus: '',
}); });
@ -106,9 +111,9 @@
function onTabsChange(e) { function onTabsChange(e) {
state.currentTab = e.index; state.currentTab = e.index;
state.type = e.value; state.type = e.value;
resetPagination(state.pagination) resetPagination(state.pagination);
if (state.currentTab === 0) { if (state.currentTab === 0) {
getData(); getData();
} else { } else {
getCoupon(); getCoupon();
} }
@ -135,7 +140,7 @@
const { data, code } = await CouponApi.getCouponPage({ const { data, code } = await CouponApi.getCouponPage({
pageNo: state.pagination.pageNo, pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize, pageSize: state.pagination.pageSize,
status: state.type status: state.type,
}); });
if (code !== 0) { if (code !== 0) {
return; return;
@ -177,13 +182,13 @@
// //
if (Option.type === 'all' || !Option.type) { if (Option.type === 'all' || !Option.type) {
getData(); getData();
// //
} else { } else {
Option.type === 'geted' Option.type === 'geted'
? (state.currentTab = 1) ? (state.currentTab = 1)
: Option.type === 'used' : Option.type === 'used'
? (state.currentTab = 2) ? (state.currentTab = 2)
: (state.currentTab = 3); : (state.currentTab = 3);
state.type = state.currentTab; state.type = state.currentTab;
getCoupon(); getCoupon();
} }

View File

@ -30,7 +30,7 @@
import CommentApi from '@/sheep/api/product/comment'; import CommentApi from '@/sheep/api/product/comment';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue'; import { reactive } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import commentItem from '../components/detail/comment-item.vue'; import commentItem from '../components/detail/comment-item.vue';
const state = reactive({ const state = reactive({

View File

@ -7,12 +7,16 @@
<detailSkeleton v-if="state.skeletonLoading" /> <detailSkeleton v-if="state.skeletonLoading" />
<!-- 下架/售罄提醒 --> <!-- 下架/售罄提醒 -->
<s-empty <s-empty
v-else-if="state.goodsInfo === null || state.activity.status !== 0 || state.activity.endTime < new Date().getTime()" v-else-if="
text="活动不存在或已结束" state.goodsInfo === null ||
icon="/static/soldout-empty.png" state.activity.status !== 0 ||
showAction state.activity.endTime < new Date().getTime()
actionText="返回上一页" "
@clickAction="sheep.$router.back()" text="活动不存在或已结束"
icon="/static/soldout-empty.png"
showAction
actionText="返回上一页"
@clickAction="sheep.$router.back()"
/> />
<block v-else> <block v-else>
<view class="detail-swiper-selector"> <view class="detail-swiper-selector">
@ -47,10 +51,7 @@
</view> </view>
</view> </view>
<view class="ss-flex ss-row-between"> <view class="ss-flex ss-row-between">
<view <view class="origin-price ss-flex ss-col-center" v-if="state.goodsInfo.price">
class="origin-price ss-flex ss-col-center"
v-if="state.goodsInfo.price"
>
单买价 单买价
<view class="origin-price-text"> <view class="origin-price-text">
{{ fen2yuan(state.goodsInfo.price) }} {{ fen2yuan(state.goodsInfo.price) }}
@ -79,7 +80,7 @@
<!-- 功能卡片 --> <!-- 功能卡片 -->
<view class="detail-cell-card detail-card ss-flex-col"> <view class="detail-cell-card detail-card ss-flex-col">
<!-- 规格 --> <!-- 规格 -->
<detail-cell-sku :sku="state.selectedSkuPrice" @tap="state.showSelectSku = true" /> <detail-cell-sku :sku="state.selectedSku" @tap="state.showSelectSku = true" />
</view> </view>
<!-- 参团列表 --> <!-- 参团列表 -->
@ -89,6 +90,7 @@
<s-select-groupon-sku <s-select-groupon-sku
:show="state.showSelectSku" :show="state.showSelectSku"
:goodsInfo="state.goodsInfo" :goodsInfo="state.goodsInfo"
:selectedSku="state.selectedSku"
:grouponAction="state.grouponAction" :grouponAction="state.grouponAction"
:grouponNum="state.grouponNum" :grouponNum="state.grouponNum"
@buy="onBuy" @buy="onBuy"
@ -123,7 +125,9 @@
" "
:disabled="state.goodsInfo.stock === 0 || state.activity.status !== 0" :disabled="state.goodsInfo.stock === 0 || state.activity.status !== 0"
> >
<view class="btn-price">{{ fen2yuan(state.activity.price || state.goodsInfo.price) }}</view> <view class="btn-price">{{
fen2yuan(state.selectedSku.price * state.selectedSku.count || state.activity.price * state.selectedSku.count || state.goodsInfo.price * state.selectedSku.count || state.goodsInfo.price)
}}</view>
<view v-if="state.activity.startTime > new Date().getTime()"></view> <view v-if="state.activity.startTime > new Date().getTime()"></view>
<view v-else-if="state.activity.endTime <= new Date().getTime()">已结束</view> <view v-else-if="state.activity.endTime <= new Date().getTime()">已结束</view>
<view v-else> <view v-else>
@ -141,7 +145,7 @@
import { reactive, computed } from 'vue'; import { reactive, computed } from 'vue';
import { onLoad, onPageScroll } from '@dcloudio/uni-app'; import { onLoad, onPageScroll } from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import detailNavbar from './components/detail/detail-navbar.vue'; import detailNavbar from './components/detail/detail-navbar.vue';
import detailCellSku from './components/detail/detail-cell-sku.vue'; import detailCellSku from './components/detail/detail-cell-sku.vue';
import detailTabbar from './components/detail/detail-tabbar.vue'; import detailTabbar from './components/detail/detail-tabbar.vue';
@ -149,29 +153,27 @@
import detailCommentCard from './components/detail/detail-comment-card.vue'; import detailCommentCard from './components/detail/detail-comment-card.vue';
import detailContentCard from './components/detail/detail-content-card.vue'; import detailContentCard from './components/detail/detail-content-card.vue';
import grouponCardList from './components/groupon/groupon-card-list.vue'; import grouponCardList from './components/groupon/groupon-card-list.vue';
import {useDurationTime, formatGoodsSwiper, fen2yuan} from '@/sheep/hooks/useGoods'; import { useDurationTime, formatGoodsSwiper, fen2yuan } from '@/sheep/hooks/useGoods';
import CombinationApi from "@/sheep/api/promotion/combination"; import CombinationApi from '@/sheep/api/promotion/combination';
import SpuApi from "@/sheep/api/product/spu"; import SpuApi from '@/sheep/api/product/spu';
const headerBg = sheep.$url.css('/static/img/shop/goods/groupon-bg.png'); const headerBg = sheep.$url.css('/static/img/shop/goods/groupon-bg.png');
const btnBg = sheep.$url.css('/static/img/shop/goods/groupon-btn.png'); const btnBg = sheep.$url.css('/static/img/shop/goods/groupon-btn.png');
const disabledBtnBg = sheep.$url.css( const disabledBtnBg = sheep.$url.css('/static/img/shop/goods/activity-btn-disabled.png');
'/static/img/shop/goods/activity-btn-disabled.png',
);
const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png'); const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png');
onPageScroll(() => {}); onPageScroll(() => {});
const state = reactive({ const state = reactive({
skeletonLoading: true, // skeletonLoading: true, //
goodsId: 0, // ID goodsId: 0, // ID
goodsInfo: {}, // goodsInfo: {}, //
goodsSwiper: [], // goodsSwiper: [], //
showSelectSku: false, // showSelectSku: false, //
selectedSkuPrice: {}, // selectedSku: {}, //
activity: {}, // activity: {}, //
grouponId: 0, // ID grouponId: 0, // ID
grouponNum: 0, // grouponNum: 0, //
grouponAction: 'create', // grouponAction: 'create', //
combinationHeadId: null, // combinationHeadId: null, //
}); });
@ -182,7 +184,7 @@
// //
function onSkuChange(e) { function onSkuChange(e) {
state.selectedSkuPrice = e; state.selectedSku = e;
} }
function onSkuClose() { function onSkuClose() {
@ -198,6 +200,7 @@
/** /**
* 去参团 * 去参团
*
* @param record 团长的团购记录 * @param record 团长的团购记录
*/ */
function onJoinGroupon(record) { function onJoinGroupon(record) {
@ -226,7 +229,6 @@
} }
// //
// TODO @
const shareInfo = computed(() => { const shareInfo = computed(() => {
if (isEmpty(state.activity)) return {}; if (isEmpty(state.activity)) return {};
return sheep.$platform.share.getShareInfo( return sheep.$platform.share.getShareInfo(
@ -261,9 +263,21 @@
// //
const { data: spu } = await SpuApi.getSpuDetail(activity.spuId); const { data: spu } = await SpuApi.getSpuDetail(activity.spuId);
state.goodsId = spu.id; state.goodsId = spu.id;
activity.products.forEach(product => { //
activity.products.forEach((product) => {
spu.price = Math.min(spu.price, product.combinationPrice); // SPU spu.price = Math.min(spu.price, product.combinationPrice); // SPU
}); });
// 使
spu.skus.forEach((sku) => {
const product = activity.products.find((product) => product.skuId === sku.id);
if (product) {
sku.price = product.combinationPrice;
} else {
//
sku.stock = 0;
}
});
// //
state.skeletonLoading = false; state.skeletonLoading = false;
if (code === 0) { if (code === 0) {
@ -475,8 +489,7 @@
} }
.groupon-box { .groupon-box {
background: v-bind(grouponBg) background: v-bind(grouponBg) no-repeat;
no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
} }

View File

@ -113,7 +113,7 @@
import detailCommentCard from './components/detail/detail-comment-card.vue'; import detailCommentCard from './components/detail/detail-comment-card.vue';
import detailContentCard from './components/detail/detail-content-card.vue'; import detailContentCard from './components/detail/detail-content-card.vue';
import detailActivityTip from './components/detail/detail-activity-tip.vue'; import detailActivityTip from './components/detail/detail-activity-tip.vue';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import SpuApi from '@/sheep/api/product/spu'; import SpuApi from '@/sheep/api/product/spu';
onPageScroll(() => {}); onPageScroll(() => {});
@ -156,11 +156,9 @@
data: JSON.stringify({ data: JSON.stringify({
items: [{ items: [{
skuId: e.id, skuId: e.id,
count: e.goods_num count: e.goods_num,
}], categoryId: state.goodsInfo.categoryId
// TODO 2 }]
deliveryType: 1,
pointStatus: false,
}), }),
}); });
} }

View File

@ -1,362 +1,398 @@
<template> <template>
<s-layout navbar="normal" :leftWidth="0" :rightWidth="0" tools="search" :defaultSearch="state.keyword" <s-layout
@search="onSearch"> navbar="normal"
<!-- 筛选 --> :leftWidth="0"
<su-sticky bgColor="#fff"> :rightWidth="0"
<view class="ss-flex"> tools="search"
<view class="ss-flex-1"> :defaultSearch="state.keyword"
<su-tabs :list="state.tabList" :scrollable="false" @change="onTabsChange" @search="onSearch"
:current="state.currentTab" /> >
</view> <!-- 筛选 -->
<view class="list-icon" @tap="state.iconStatus = !state.iconStatus"> <su-sticky bgColor="#fff">
<text v-if="state.iconStatus" class="sicon-goods-list" /> <view class="ss-flex">
<text v-else class="sicon-goods-card" /> <view class="ss-flex-1">
</view> <su-tabs
</view> :list="state.tabList"
</su-sticky> :scrollable="false"
@change="onTabsChange"
:current="state.currentTab"
/>
</view>
<view class="list-icon" @tap="state.iconStatus = !state.iconStatus">
<text v-if="state.iconStatus" class="sicon-goods-list" />
<text v-else class="sicon-goods-card" />
</view>
</view>
</su-sticky>
<!-- 弹窗 --> <!-- 弹窗 -->
<su-popup :show="state.showFilter" type="top" round="10" :space="sys_navBar + 38" backgroundColor="#F6F6F6" <su-popup
:zIndex="10" @close="state.showFilter = false"> :show="state.showFilter"
<view class="filter-list-box"> type="top"
<view class="filter-item" v-for="(item, index) in state.tabList[state.currentTab].list" round="10"
:key="item.value" :class="[{ 'filter-item-active': index === state.curFilter }]" :space="sys_navBar + 38"
@tap="onFilterItem(index)"> backgroundColor="#F6F6F6"
{{ item.label }} :zIndex="10"
</view> @close="state.showFilter = false"
</view> >
</su-popup> <view class="filter-list-box">
<view
class="filter-item"
v-for="(item, index) in state.tabList[state.currentTab].list"
:key="item.value"
:class="[{ 'filter-item-active': index === state.curFilter }]"
@tap="onFilterItem(index)"
>
{{ item.label }}
</view>
</view>
</su-popup>
<!-- 情况一单列布局 --> <!-- 情况一单列布局 -->
<view v-if="state.iconStatus && state.pagination.total > 0" class="goods-list ss-m-t-20"> <view v-if="state.iconStatus && state.pagination.total > 0" class="goods-list ss-m-t-20">
<view class="ss-p-l-20 ss-p-r-20 ss-m-b-20" v-for="item in state.pagination.list" :key="item.id"> <view
<s-goods-column class="ss-p-l-20 ss-p-r-20 ss-m-b-20"
v-for="item in state.pagination.list"
:key="item.id"
>
<s-goods-column
class="" class=""
size="lg" size="lg"
:data="item" :data="item"
:topRadius="10" :topRadius="10"
:bottomRadius="10" :bottomRadius="10"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })" @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
/> />
</view> </view>
</view> </view>
<!-- 情况二双列布局 --> <!-- 情况二双列布局 -->
<view v-if="!state.iconStatus && state.pagination.total > 0" <view
class="ss-flex ss-flex-wrap ss-p-x-20 ss-m-t-20 ss-col-top"> v-if="!state.iconStatus && state.pagination.total > 0"
<view class="goods-list-box"> class="ss-flex ss-flex-wrap ss-p-x-20 ss-m-t-20 ss-col-top"
<view class="left-list" v-for="item in state.leftGoodsList" :key="item.id"> >
<s-goods-column <view class="goods-list-box">
<view class="left-list" v-for="item in state.leftGoodsList" :key="item.id">
<s-goods-column
class="goods-md-box" class="goods-md-box"
size="md" size="md"
:data="item" :data="item"
:topRadius="10" :topRadius="10"
:bottomRadius="10" :bottomRadius="10"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })" @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
@getHeight="mountMasonry($event, 'left')" @getHeight="mountMasonry($event, 'left')"
> >
<template v-slot:cart> <template v-slot:cart>
<button class="ss-reset-button cart-btn" /> <button class="ss-reset-button cart-btn" />
</template> </template>
</s-goods-column> </s-goods-column>
</view> </view>
</view> </view>
<view class="goods-list-box"> <view class="goods-list-box">
<view class="right-list" v-for="item in state.rightGoodsList" :key="item.id"> <view class="right-list" v-for="item in state.rightGoodsList" :key="item.id">
<s-goods-column <s-goods-column
class="goods-md-box" class="goods-md-box"
size="md" size="md"
:topRadius="10" :topRadius="10"
:bottomRadius="10" :bottomRadius="10"
:data="item" :data="item"
@click="sheep.$router.go('/pages/goods/index', { id: item.id })" @click="sheep.$router.go('/pages/goods/index', { id: item.id })"
@getHeight="mountMasonry($event, 'right')" @getHeight="mountMasonry($event, 'right')"
> >
<template v-slot:cart> <template v-slot:cart>
<button class="ss-reset-button cart-btn" /> <button class="ss-reset-button cart-btn" />
</template> </template>
</s-goods-column> </s-goods-column>
</view> </view>
</view> </view>
</view> </view>
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{ <uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多', contentdown: '上拉加载更多',
}" @tap="loadMore" /> }"
<s-empty v-if="state.pagination.total === 0" icon="/static/soldout-empty.png" text="暂无商品" /> @tap="loadMore"
</s-layout> />
<s-empty v-if="state.pagination.total === 0" icon="/static/soldout-empty.png" text="暂无商品" />
</s-layout>
</template> </template>
<script setup> <script setup>
import { reactive } from 'vue'; import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import _ from 'lodash'; import _ from 'lodash-es';
import { resetPagination } from '@/sheep/util'; import { resetPagination } from '@/sheep/util';
import SpuApi from '@/sheep/api/product/spu'; import SpuApi from '@/sheep/api/product/spu';
const sys_navBar = sheep.$platform.navbar; const sys_navBar = sheep.$platform.navbar;
const emits = defineEmits(['close', 'change']); const emits = defineEmits(['close', 'change']);
const state = reactive({ const state = reactive({
pagination: { pagination: {
list: [], list: [],
total: 0, total: 0,
pageNo: 1, pageNo: 1,
pageSize: 6, pageSize: 6,
}, },
currentSort: undefined, currentSort: undefined,
currentOrder: undefined, currentOrder: undefined,
currentTab: 0, // tab currentTab: 0, // tab
curFilter: 0, // list curFilter: 0, // list
showFilter: false, showFilter: false,
iconStatus: false, // true - false - iconStatus: false, // true - false -
keyword: '', keyword: '',
categoryId: 0, categoryId: 0,
tabList: [{ tabList: [
name: '综合推荐', {
list: [{ name: '综合推荐',
label: '综合推荐' list: [
}, {
{ label: '综合推荐',
label: '价格升序', },
sort: 'price', {
order: true, label: '价格升序',
}, sort: 'price',
{ order: true,
label: '价格降序', },
sort: 'price', {
order: false, label: '价格降序',
}, sort: 'price',
], order: false,
}, },
{ ],
name: '销量', },
sort: 'salesCount', {
order: false name: '销量',
}, sort: 'salesCount',
{ order: false,
name: '新品优先', },
value: 'createTime', {
order: false name: '新品优先',
}, value: 'createTime',
], order: false,
loadStatus: '', },
leftGoodsList: [], // - ],
rightGoodsList: [], // - loadStatus: '',
}); leftGoodsList: [], // -
rightGoodsList: [], // -
});
// //
let count = 0; let count = 0;
let leftHeight = 0; let leftHeight = 0;
let rightHeight = 0; let rightHeight = 0;
// leftGoodsList + rightGoodsList // leftGoodsList + rightGoodsList
function mountMasonry(height = 0, where = 'left') { function mountMasonry(height = 0, where = 'left') {
if (!state.pagination.list[count]) { if (!state.pagination.list[count]) {
return; return;
} }
if (where === 'left') { if (where === 'left') {
leftHeight += height; leftHeight += height;
} else { } else {
rightHeight += height; rightHeight += height;
} }
if (leftHeight <= rightHeight) { if (leftHeight <= rightHeight) {
state.leftGoodsList.push(state.pagination.list[count]); state.leftGoodsList.push(state.pagination.list[count]);
} else { } else {
state.rightGoodsList.push(state.pagination.list[count]); state.rightGoodsList.push(state.pagination.list[count]);
} }
count++; count++;
} }
// //
function emptyList() { function emptyList() {
resetPagination(state.pagination); resetPagination(state.pagination);
state.leftGoodsList = []; state.leftGoodsList = [];
state.rightGoodsList = []; state.rightGoodsList = [];
count = 0; count = 0;
leftHeight = 0; leftHeight = 0;
rightHeight = 0; rightHeight = 0;
} }
// //
function onSearch(e) { function onSearch(e) {
state.keyword = e; state.keyword = e;
emptyList(); emptyList();
getList(state.currentSort, state.currentOrder); getList(state.currentSort, state.currentOrder);
} }
// //
function onTabsChange(e) { function onTabsChange(e) {
// //
if (state.tabList[e.index].list) { if (state.tabList[e.index].list) {
state.currentTab = e.index; state.currentTab = e.index;
state.showFilter = !state.showFilter; state.showFilter = !state.showFilter;
return; return;
} }
state.showFilter = false; state.showFilter = false;
// tab // tab
if (e.index === state.currentTab) { if (e.index === state.currentTab) {
return; return;
} }
state.currentTab = e.index; state.currentTab = e.index;
state.currentSort = e.sort; state.currentSort = e.sort;
state.currentOrder = e.order; state.currentOrder = e.order;
emptyList(); emptyList();
getList(e.sort, e.order); getList(e.sort, e.order);
} }
// tab list // tab list
const onFilterItem = (val) => { const onFilterItem = (val) => {
// //
// tabList[0] list // tabList[0] list
if (state.currentSort === state.tabList[0].list[val].sort if (
&& state.currentOrder === state.tabList[0].list[val].order) { state.currentSort === state.tabList[0].list[val].sort &&
state.showFilter = false; state.currentOrder === state.tabList[0].list[val].order
return; ) {
} state.showFilter = false;
return;
}
state.showFilter = false; state.showFilter = false;
// //
state.curFilter = val; state.curFilter = val;
state.tabList[0].name = state.tabList[0].list[val].label; state.tabList[0].name = state.tabList[0].list[val].label;
state.currentSort = state.tabList[0].list[val].sort; state.currentSort = state.tabList[0].list[val].sort;
state.currentOrder = state.tabList[0].list[val].order; state.currentOrder = state.tabList[0].list[val].order;
// + // +
emptyList(); emptyList();
getList(); getList();
} };
async function getList() { async function getList() {
state.loadStatus = 'loading'; state.loadStatus = 'loading';
const { code, data } = await SpuApi.getSpuPage({ const { code, data } = await SpuApi.getSpuPage({
pageNo: state.pagination.pageNo, pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize, pageSize: state.pagination.pageSize,
sortField: state.currentSort, sortField: state.currentSort,
sortAsc: state.currentOrder, sortAsc: state.currentOrder,
categoryId: state.categoryId, categoryId: state.categoryId,
keyword: state.keyword, keyword: state.keyword,
}); });
if (code !== 0) { if (code !== 0) {
return; return;
} }
state.pagination.list = _.concat(state.pagination.list, data.list) state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total; state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore'; state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
mountMasonry(); mountMasonry();
} }
// //
function loadMore() { function loadMore() {
if (state.loadStatus === 'noMore') { if (state.loadStatus === 'noMore') {
return; return;
} }
state.pagination.pageNo++; state.pagination.pageNo++;
getList(state.currentSort, state.currentOrder); getList(state.currentSort, state.currentOrder);
} }
onLoad((options) => { onLoad((options) => {
state.categoryId = options.categoryId; state.categoryId = options.categoryId;
state.keyword = options.keyword; state.keyword = options.keyword;
getList(state.currentSort, state.currentOrder); getList(state.currentSort, state.currentOrder);
}); });
// //
onReachBottom(() => { onReachBottom(() => {
loadMore(); loadMore();
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.goods-list-box { .goods-list-box {
width: 50%; width: 50%;
box-sizing: border-box; box-sizing: border-box;
.left-list { .left-list {
margin-right: 10rpx; margin-right: 10rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.right-list { .right-list {
margin-left: 10rpx; margin-left: 10rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
} }
.goods-box { .goods-box {
&:nth-last-of-type(1) { &:nth-last-of-type(1) {
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
&:nth-child(2n) { &:nth-child(2n) {
margin-right: 0; margin-right: 0;
} }
} }
.list-icon { .list-icon {
width: 80rpx; width: 80rpx;
.sicon-goods-card { .sicon-goods-card {
font-size: 40rpx; font-size: 40rpx;
} }
.sicon-goods-list { .sicon-goods-list {
font-size: 40rpx; font-size: 40rpx;
} }
} }
.goods-card { .goods-card {
margin-left: 20rpx; margin-left: 20rpx;
} }
.list-filter-tabs { .list-filter-tabs {
background-color: #fff; background-color: #fff;
} }
.filter-list-box { .filter-list-box {
padding: 28rpx 52rpx; padding: 28rpx 52rpx;
.filter-item { .filter-item {
font-size: 28rpx; font-size: 28rpx;
font-weight: 500; font-weight: 500;
color: #333333; color: #333333;
line-height: normal; line-height: normal;
margin-bottom: 24rpx; margin-bottom: 24rpx;
&:nth-last-child(1) { &:nth-last-child(1) {
margin-bottom: 0; margin-bottom: 0;
} }
} }
.filter-item-active { .filter-item-active {
color: var(--ui-BG-Main); color: var(--ui-BG-Main);
} }
} }
.tab-item { .tab-item {
height: 50px; height: 50px;
position: relative; position: relative;
z-index: 11; z-index: 11;
.tab-title { .tab-title {
font-size: 30rpx; font-size: 30rpx;
} }
.cur-tab-title { .cur-tab-title {
font-weight: $font-weight-bold; font-weight: $font-weight-bold;
} }
.tab-line { .tab-line {
width: 60rpx; width: 60rpx;
height: 6rpx; height: 6rpx;
border-radius: 6rpx; border-radius: 6rpx;
position: absolute; position: absolute;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
bottom: 10rpx; bottom: 10rpx;
background-color: var(--ui-BG-Main); background-color: var(--ui-BG-Main);
z-index: 12; z-index: 12;
} }
} }
</style> </style>

View File

@ -63,16 +63,13 @@
<detail-progress :percent="state.percent" /> <detail-progress :percent="state.percent" />
</view> </view>
<view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo?.name }}</view> <view class="title-text ss-line-2 ss-m-b-6">{{ state.goodsInfo.name || '' }}</view>
<view class="subtitle-text ss-line-1">{{ state.goodsInfo.introduction }}</view> <view class="subtitle-text ss-line-1">{{ state.goodsInfo.introduction }}</view>
</view> </view>
<!-- 功能卡片 --> <!-- 功能卡片 -->
<view class="detail-cell-card detail-card ss-flex-col"> <view class="detail-cell-card detail-card ss-flex-col">
<detail-cell-sku <detail-cell-sku :sku="state.selectedSku" @tap="state.showSelectSku = true" />
:sku="state.selectedSku"
@tap="state.showSelectSku = true"
/>
</view> </view>
<!-- 规格与数量弹框 --> <!-- 规格与数量弹框 -->
<s-select-seckill-sku <s-select-seckill-sku
@ -107,7 +104,9 @@
<button v-else class="ss-reset-button origin-price-btn ss-flex-col"> <button v-else class="ss-reset-button origin-price-btn ss-flex-col">
<view <view
class="no-original" class="no-original"
:class="state.goodsInfo.stock === 0 || timeStatusEnum !== TimeStatusEnum.STARTED ? '' : ''" :class="
state.goodsInfo.stock === 0 || timeStatusEnum !== TimeStatusEnum.STARTED ? '' : ''
"
> >
秒杀价 秒杀价
</view> </view>
@ -136,11 +135,11 @@
</template> </template>
<script setup> <script setup>
import {reactive, computed, ref} from 'vue'; import { reactive, computed, ref, unref } from 'vue';
import { onLoad, onPageScroll } from '@dcloudio/uni-app'; import { onLoad, onPageScroll } from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import {isEmpty, min} from 'lodash'; import { isEmpty, min } from 'lodash-es';
import {useDurationTime, formatGoodsSwiper, fen2yuan} from '@/sheep/hooks/useGoods'; import { useDurationTime, formatGoodsSwiper, fen2yuan } from '@/sheep/hooks/useGoods';
import detailNavbar from './components/detail/detail-navbar.vue'; import detailNavbar from './components/detail/detail-navbar.vue';
import detailCellSku from './components/detail/detail-cell-sku.vue'; import detailCellSku from './components/detail/detail-cell-sku.vue';
import detailTabbar from './components/detail/detail-tabbar.vue'; import detailTabbar from './components/detail/detail-tabbar.vue';
@ -148,15 +147,13 @@
import detailCommentCard from './components/detail/detail-comment-card.vue'; import detailCommentCard from './components/detail/detail-comment-card.vue';
import detailContentCard from './components/detail/detail-content-card.vue'; import detailContentCard from './components/detail/detail-content-card.vue';
import detailProgress from './components/detail/detail-progress.vue'; import detailProgress from './components/detail/detail-progress.vue';
import SeckillApi from "@/sheep/api/promotion/seckill"; import SeckillApi from '@/sheep/api/promotion/seckill';
import SpuApi from "@/sheep/api/product/spu"; import SpuApi from '@/sheep/api/product/spu';
import {getTimeStatusEnum, TimeStatusEnum} from "@/sheep/util/const"; import { getTimeStatusEnum, TimeStatusEnum } from '@/sheep/util/const';
const headerBg = sheep.$url.css('/static/img/shop/goods/seckill-bg.png'); const headerBg = sheep.$url.css('/static/img/shop/goods/seckill-bg.png');
const btnBg = sheep.$url.css('/static/img/shop/goods/seckill-btn.png'); const btnBg = sheep.$url.css('/static/img/shop/goods/seckill-btn.png');
const disabledBtnBg = sheep.$url.css( const disabledBtnBg = sheep.$url.css('/static/img/shop/goods/activity-btn-disabled.png');
'/static/img/shop/goods/activity-btn-disabled.png',
);
const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png'); const seckillBg = sheep.$url.css('/static/img/shop/goods/seckill-tip-bg.png');
const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png'); const grouponBg = sheep.$url.css('/static/img/shop/goods/groupon-tip-bg.png');
@ -199,9 +196,9 @@
}); });
} }
// TODO //
const shareInfo = computed(() => { const shareInfo = computed(() => {
if (isEmpty(activity)) return {}; if (isEmpty(unref(activity))) return {};
return sheep.$platform.share.getShareInfo( return sheep.$platform.share.getShareInfo(
{ {
title: activity.value.name, title: activity.value.name,
@ -221,36 +218,41 @@
); );
}); });
const activity = ref() const activity = ref();
const timeStatusEnum = ref('') const timeStatusEnum = ref('');
// //
const getActivity = async (id) => { const getActivity = async (id) => {
const { data } = await SeckillApi.getSeckillActivity(id) const { data } = await SeckillApi.getSeckillActivity(id);
activity.value = data activity.value = data;
timeStatusEnum.value = getTimeStatusEnum(activity.startTime, activity.endTime) timeStatusEnum.value = getTimeStatusEnum(activity.startTime, activity.endTime);
// //
await getSpu(data.spuId) await getSpu(data.spuId);
} };
//
const getSpu = async (id) => { const getSpu = async (id) => {
const { data } = await SpuApi.getSpuDetail(id) const { data } = await SpuApi.getSpuDetail(id);
// data.activity_type = 'seckill';
data.activity_type = 'seckill' state.goodsInfo = data;
state.goodsInfo = data
// //
state.goodsSwiper = formatGoodsSwiper(state.goodsInfo.sliderPicUrls); state.goodsSwiper = formatGoodsSwiper(state.goodsInfo.sliderPicUrls);
// //
state.goodsInfo.price = min([state.goodsInfo.price, ...activity.value.products.map(spu => spu.seckillPrice)]) state.goodsInfo.price = min([
state.goodsInfo.price,
...activity.value.products.map((spu) => spu.seckillPrice),
]);
// 使 // 使
data.skus.forEach(sku => { data.skus.forEach((sku) => {
const product = activity.value.products.find(product => product.skuId === sku.id); const product = activity.value.products.find((product) => product.skuId === sku.id);
if (product) { if (product) {
sku.price = product.seckillPrice; sku.price = product.seckillPrice;
sku.stock = Math.min(sku.stock, product.stock); sku.stock = Math.min(sku.stock, product.stock);
} else { // } else {
//
sku.stock = 0; sku.stock = 0;
} }
// //
@ -264,7 +266,7 @@
}); });
state.skeletonLoading = false; state.skeletonLoading = false;
} };
onLoad((options) => { onLoad((options) => {
// //
@ -274,7 +276,7 @@
} }
// //
getActivity(options.id) getActivity(options.id);
}); });
</script> </script>

View File

@ -107,6 +107,7 @@
skuId: item.sku.id, skuId: item.sku.id,
count: item.count, count: item.count,
cartId: item.id, cartId: item.id,
categoryId: item.spu.categoryId
}) })
goods_list.push({ goods_list.push({
// goods_id: item.goods_id, // goods_id: item.goods_id,
@ -124,12 +125,7 @@
} }
sheep.$router.go('/pages/order/confirm', { sheep.$router.go('/pages/order/confirm', {
data: JSON.stringify({ data: JSON.stringify({
// order_type: 'goods', items
// goods_list,
items,
// from: 'cart',
deliveryType: 1,
pointStatus: false,
}), }),
}); });
} }

View File

@ -63,7 +63,7 @@
import SpuApi from '@/sheep/api/product/spu'; import SpuApi from '@/sheep/api/product/spu';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { computed, reactive } from 'vue'; import { computed, reactive } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import { handleTree } from '@/sheep/util'; import { handleTree } from '@/sheep/util';
const state = reactive({ const state = reactive({
@ -131,12 +131,13 @@
getGoodsList(); getGoodsList();
} }
onLoad(async () => { onLoad(async (params) => {
await getList(); await getList();
// first
if (state.style === 'first_one' || state.style === 'first_two') { //
onMenu(0); const foundCategory = state.categoryList.find(category => category.id === params.id);
} // onMenu onMenu(0)
onMenu(foundCategory ? state.categoryList.indexOf(foundCategory) : 0);
}); });
onReachBottom(() => { onReachBottom(() => {

View File

@ -0,0 +1,261 @@
<!-- 下单界面收货地址 or 自提门店的选择组件 -->
<template>
<view class="allAddress" :style="state.isPickUp ? '':'padding-top:10rpx;'">
<view class="nav flex flex-wrap">
<view class="item font-color" :class="state.deliveryType === 1 ? 'on' : 'on2'"
@tap="switchDeliveryType(1)" v-if='state.isPickUp' />
<view class="item font-color" :class="state.deliveryType === 2 ? 'on' : 'on2'"
@tap="switchDeliveryType(2)" v-if='state.isPickUp' />
</view>
<!-- 情况一收货地址的选择 -->
<view class='address flex flex-wrap flex-center ss-row-between' @tap='onSelectAddress' v-if='state.deliveryType === 1'
:style="state.isPickUp ? '':'border-top-left-radius: 14rpx;border-top-right-radius: 14rpx;'">
<view class='addressCon' v-if="state.addressInfo.name">
<view class='name'>{{ state.addressInfo.name }}
<text class='phone'>{{ state.addressInfo.mobile }}</text>
</view>
<view class="flex flex-wrap">
<text class='default font-color' v-if="state.addressInfo.defaultStatus">[]</text>
<text class="line2">{{ state.addressInfo.areaName }} {{ state.addressInfo.detailAddress }}</text>
</view>
</view>
<view class='addressCon' v-else>
<view class='setaddress'>设置收货地址</view>
</view>
<view class='iconfont'>
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</view>
</view>
<!-- 情况二门店的选择 -->
<view class='address flex flex-wrap flex-center ss-row-between' v-else @tap="onSelectAddress">
<view class='addressCon' v-if="state.pickUpInfo.name">
<view class='name'>{{ state.pickUpInfo.name }}
<text class='phone'>{{ state.pickUpInfo.phone }}</text>
</view>
<view class="line1"> {{ state.pickUpInfo.areaName }}{{ ', ' + state.pickUpInfo.detailAddress }}
</view>
</view>
<view class='addressCon' v-else>
<view class='setaddress'>选择自提门店</view>
</view>
<view class='iconfont'>
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</view>
</view>
<view class='line'>
<image :src="sheep.$url.static('/static/images/line.png', 'local')" />
</view>
</view>
</template>
<script setup>
import { computed } from 'vue';
import sheep from '@/sheep';
import { isEmpty } from 'lodash-es';
const props = defineProps({
modelValue: {
type: Object,
default() {},
}
});
const emits = defineEmits(['update:modelValue']);
// computed
const state = computed({
get(){
return new Proxy(props.modelValue, {
set(obj, name, val) {
emits('update:modelValue', {
...obj,
[name]: val,
});
return true;
}
})
},
set(val){
emits('update:modelValue', val);
}
})
//
function onSelectAddress() {
let emitName = 'SELECT_ADDRESS'
let addressPage = '/pages/user/address/list?type=select';
if (state.value.deliveryType === 2){
emitName = 'SELECT_PICK_UP_INFO'
addressPage = '/pages/user/goods_details_store/index'
}
uni.$once(emitName, (e) => {
changeConsignee(e.addressInfo);
});
sheep.$router.go(addressPage);
}
// &
async function changeConsignee(addressInfo = {}) {
if (!isEmpty(addressInfo)) {
if (state.value.deliveryType === 1){
state.value.addressInfo = addressInfo;
}
if (state.value.deliveryType === 2){
state.value.pickUpInfo = addressInfo;
}
}
}
//
const switchDeliveryType = (type) =>{
state.value.deliveryType = type;
}
</script>
<style scoped lang="scss">
.allAddress .font-color{
color: #E93323!important
}
.line2{
width: 504rpx;
}
.textR {
text-align: right;
}
.line {
width: 100%;
height: 3rpx;
}
.line image {
width: 100%;
height: 100%;
display: block;
}
.address {
padding: 28rpx;
background-color: #fff;
box-sizing: border-box;
}
.address .addressCon {
width: 596rpx;
font-size: 26rpx;
color: #666;
}
.address .addressCon .name {
font-size: 30rpx;
color: #282828;
font-weight: bold;
margin-bottom: 10rpx;
}
.address .addressCon .name .phone {
margin-left: 50rpx;
}
.address .addressCon .default {
margin-right: 12rpx;
}
.address .addressCon .setaddress {
color: #333;
font-size: 28rpx;
}
.address .iconfont {
font-size: 35rpx;
color: #707070;
}
.allAddress {
width: 100%;
background: linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
// background-image: linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
// background-image: -webkit-linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
// background-image: -moz-linear-gradient(to bottom, #e93323 0%, #f5f5f5 100%);
//padding: 100rpx 30rpx 0 30rpx;
padding-top: 100rpx;
padding-bottom: 10rpx;
}
.allAddress .nav {
width: 690rpx;
margin: 0 auto;
}
.allAddress .nav .item {
width: 334rpx;
}
.allAddress .nav .item.on {
position: relative;
width: 230rpx;
}
.allAddress .nav .item.on::before {
position: absolute;
bottom: 0;
content: "快递配送";
font-size: 28rpx;
display: block;
height: 0;
width: 336rpx;
border-width: 0 20rpx 80rpx 0;
border-style: none solid solid;
border-color: transparent transparent #fff;
z-index: 2;
border-radius: 14rpx 36rpx 0 0;
text-align: center;
line-height: 80rpx;
}
.allAddress .nav .item:nth-of-type(2).on::before {
content: "到店自提";
border-width: 0 0 80rpx 20rpx;
border-radius: 36rpx 14rpx 0 0;
}
.allAddress .nav .item.on2 {
position: relative;
}
.allAddress .nav .item.on2::before {
position: absolute;
bottom: 0;
content: "到店自提";
font-size: 28rpx;
display: block;
height: 0;
width: 401rpx;
border-width: 0 0 60rpx 60rpx;
border-style: none solid solid;
border-color: transparent transparent #f7c1bd;
border-radius: 36rpx 14rpx 0 0;
text-align: center;
line-height: 60rpx;
}
.allAddress .nav .item:nth-of-type(1).on2::before {
content: "快递配送";
border-width: 0 60rpx 60rpx 0;
border-radius: 14rpx 36rpx 0 0;
}
.allAddress .address {
width: 690rpx;
max-height: 180rpx;
margin: 0 auto;
}
.allAddress .line {
width: 100%;
margin: 0 auto;
}
</style>

View File

@ -98,11 +98,7 @@
</view> </view>
<view class="modal-content content_box"> <view class="modal-content content_box">
<radio-group @change="onChange"> <radio-group @change="onChange">
<label <label class="radio ss-flex ss-col-center" v-for="item in state.reasonList" :key="item">
class="radio ss-flex ss-col-center"
v-for="item in state.reasonList"
:key="item"
>
<view class="ss-flex-1 ss-p-20">{{ item }}</view> <view class="ss-flex-1 ss-p-20">{{ item }}</view>
<radio <radio
:value="item" :value="item"
@ -153,9 +149,9 @@
], ],
reasonList: [], // reasonList: [], //
showModal: false, // showModal: false, //
currentValue: '' // currentValue: '', //
}); });
const formData = reactive({ let formData = reactive({
way: '', way: '',
applyReason: '', applyReason: '',
applyDescription: '', applyDescription: '',

View File

@ -1,156 +1,193 @@
<!-- 售后详情 --> <!-- 售后详情 -->
<template> <template>
<s-layout title="售后详情" :navbar="!isEmpty(state.info) && state.loading ? 'inner' : 'normal'"> <s-layout title="售后详情" :navbar="!isEmpty(state.info) && state.loading ? 'inner' : 'normal'">
<view class="content_box" v-if="!isEmpty(state.info) && state.loading"> <view class="content_box" v-if="!isEmpty(state.info) && state.loading">
<!-- 步骤条 --> <!-- 步骤条 -->
<view class="steps-box ss-flex" :style="[ <view
class="steps-box ss-flex"
:style="[
{ {
marginTop: '-' + Number(statusBarHeight + 88) + 'rpx', marginTop: '-' + Number(statusBarHeight + 88) + 'rpx',
paddingTop: Number(statusBarHeight + 88) + 'rpx', paddingTop: Number(statusBarHeight + 88) + 'rpx',
}, },
]"> ]"
<view class="ss-flex"> >
<view class="steps-item" v-for="(item, index) in state.list" :key="index"> <view class="ss-flex">
<view class="ss-flex"> <view class="steps-item" v-for="(item, index) in state.list" :key="index">
<text class="sicon-circleclose" <view class="ss-flex">
v-if="state.list.length - 1 === index && [61, 62, 63].includes(state.info.status)" /> <text
<text class="sicon-circlecheck" v-else class="sicon-circleclose"
:class="state.active >= index ? 'activity-color' : 'info-color'" /> v-if="state.list.length - 1 === index && [61, 62, 63].includes(state.info.status)"
/>
<text
class="sicon-circlecheck"
v-else
:class="state.active >= index ? 'activity-color' : 'info-color'"
/>
<view v-if="state.list.length - 1 !== index" class="line" <view
:class="state.active >= index ? 'activity-bg' : 'info-bg'" /> v-if="state.list.length - 1 !== index"
</view> class="line"
<view class="steps-item-title" :class="state.active >= index ? 'activity-color' : 'info-color'"> :class="state.active >= index ? 'activity-bg' : 'info-bg'"
{{ item.title }} />
</view> </view>
</view> <view
</view> class="steps-item-title"
</view> :class="state.active >= index ? 'activity-color' : 'info-color'"
>
{{ item.title }}
</view>
</view>
</view>
</view>
<!-- 服务状态 --> <!-- 服务状态 -->
<view class="status-box ss-flex ss-col-center ss-row-between ss-m-x-20" <view
@tap="sheep.$router.go('/pages/order/aftersale/log', { id: state.id })"> class="status-box ss-flex ss-col-center ss-row-between ss-m-x-20"
<view class=""> @tap="sheep.$router.go('/pages/order/aftersale/log', { id: state.id })"
<view class="status-text"> >
<view class="">
<view class="status-text">
{{ formatAfterSaleStatusDescription(state.info) }} {{ formatAfterSaleStatusDescription(state.info) }}
</view> </view>
<view class="status-time"> <view class="status-time">
{{ sheep.$helper.timeFormat(state.info.updateTime, 'yyyy-mm-dd hh:MM:ss') }} {{ sheep.$helper.timeFormat(state.info.updateTime, 'yyyy-mm-dd hh:MM:ss') }}
</view> </view>
</view> </view>
<text class="ss-iconfont _icon-forward" style="color: #666" /> <text class="ss-iconfont _icon-forward" style="color: #666" />
</view> </view>
<!-- 退款金额 --> <!-- 退款金额 -->
<view class="aftersale-money ss-flex ss-col-center ss-row-between"> <view class="aftersale-money ss-flex ss-col-center ss-row-between">
<view class="aftersale-money--title">退款总额</view> <view class="aftersale-money--title">退款总额</view>
<view class="aftersale-money--num">{{ fen2yuan(state.info.refundPrice) }}</view> <view class="aftersale-money--num">{{ fen2yuan(state.info.refundPrice) }}</view>
</view> </view>
<!-- 服务商品 --> <!-- 服务商品 -->
<view class="order-shop"> <view class="order-shop">
<s-goods-item <s-goods-item
:img=" state.info.picUrl" :img="state.info.picUrl"
:title=" state.info.spuName" :title="state.info.spuName"
:titleWidth="480" :titleWidth="480"
:skuText="state.info.properties.map((property) => property.valueName).join(' ')" :skuText="state.info.properties.map((property) => property.valueName).join(' ')"
:num=" state.info.count" :num="state.info.count"
/> />
</view> </view>
<!-- 服务内容 --> <!-- 服务内容 -->
<view class="aftersale-content"> <view class="aftersale-content">
<view class="aftersale-item ss-flex ss-col-center"> <view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">服务单号</view> <view class="item-title">服务单号</view>
<view class="item-content ss-m-r-16">{{ state.info.no }}</view> <view class="item-content ss-m-r-16">{{ state.info.no }}</view>
<button class="ss-reset-button copy-btn" @tap="onCopy"></button> <button class="ss-reset-button copy-btn" @tap="onCopy"></button>
</view> </view>
<view class="aftersale-item ss-flex ss-col-center"> <view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">申请时间</view> <view class="item-title">申请时间</view>
<view class="item-content"> <view class="item-content">
{{ sheep.$helper.timeFormat(state.info.createTime, 'yyyy-mm-dd hh:MM:ss') }} {{ sheep.$helper.timeFormat(state.info.createTime, 'yyyy-mm-dd hh:MM:ss') }}
</view> </view>
</view> </view>
<view class="aftersale-item ss-flex ss-col-center"> <view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">售后类型</view> <view class="item-title">售后类型</view>
<view class="item-content">{{ state.info.way === 10 ? '仅退款' : '退款退货' }}</view> <view class="item-content">{{ state.info.way === 10 ? '仅退款' : '退款退货' }}</view>
</view> </view>
<view class="aftersale-item ss-flex ss-col-center"> <view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">申请原因</view> <view class="item-title">申请原因</view>
<view class="item-content">{{ state.info.applyReason }}</view> <view class="item-content">{{ state.info.applyReason }}</view>
</view> </view>
<view class="aftersale-item ss-flex ss-col-center"> <view class="aftersale-item ss-flex ss-col-center">
<view class="item-title">相关描述</view> <view class="item-title">相关描述</view>
<view class="item-content">{{ state.info.applyDescription }}</view> <view class="item-content">{{ state.info.applyDescription }}</view>
</view> </view>
</view> </view>
</view> </view>
<!-- 操作区 --> <!-- 操作区 -->
<s-empty v-if="isEmpty(state.info) && state.loading" icon="/static/order-empty.png" text="暂无该订单售后详情" /> <s-empty
<su-fixed bottom placeholder bg="bg-white" v-if="!isEmpty(state.info)"> v-if="isEmpty(state.info) && state.loading"
<view class="foot_box"> icon="/static/order-empty.png"
<button class="ss-reset-button btn" v-if="state.info.buttons?.includes('cancel')" text="暂无该订单售后详情"
@tap="onApply(state.info.id)"> />
<su-fixed bottom placeholder bg="bg-white" v-if="!isEmpty(state.info)">
<view class="foot_box">
<button
class="ss-reset-button btn"
v-if="state.info.buttons?.includes('cancel')"
@tap="onApply(state.info.id)"
>
取消申请 取消申请
</button> </button>
<button class="ss-reset-button btn" v-if="state.info.buttons?.includes('delivery')" <button
@tap="sheep.$router.go('/pages/order/aftersale/return-delivery', { id: state.info.id })"> class="ss-reset-button btn"
v-if="state.info.buttons?.includes('delivery')"
@tap="sheep.$router.go('/pages/order/aftersale/return-delivery', { id: state.info.id })"
>
填写退货 填写退货
</button> </button>
<button class="ss-reset-button contcat-btn btn" @tap="sheep.$router.go('/pages/chat/index')"> <button
class="ss-reset-button contcat-btn btn"
@tap="sheep.$router.go('/pages/chat/index')"
>
联系客服 联系客服
</button> </button>
</view> </view>
</su-fixed> </su-fixed>
</s-layout> </s-layout>
</template> </template>
<script setup> <script setup>
import sheep from '@/sheep'; import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { reactive } from 'vue'; import { reactive } from 'vue';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import { fen2yuan, formatAfterSaleStatusDescription, handleAfterSaleButtons } from '@/sheep/hooks/useGoods'; import {
fen2yuan,
formatAfterSaleStatusDescription,
handleAfterSaleButtons,
} from '@/sheep/hooks/useGoods';
import AfterSaleApi from '@/sheep/api/trade/afterSale'; import AfterSaleApi from '@/sheep/api/trade/afterSale';
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2; const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png'); const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
const state = reactive({ const state = reactive({
id: 0, // id: 0, //
info: {}, // info: {}, //
loading: false, loading: false,
active: 0, // list active: 0, // list
list: [{ list: [
title: '提交申请', {
}, { title: '提交申请',
title: '处理中', },
}, { {
title: '完成' title: '处理中',
}], // },
}); {
title: '完成',
},
], //
});
function onApply(id) { function onApply(id) {
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '确定要取消此申请吗?', content: '确定要取消此申请吗?',
success: async function(res) { success: async function (res) {
if (!res.confirm) { if (!res.confirm) {
return; return;
} }
const { code } = await AfterSaleApi.cancelAfterSale(id); const { code } = await AfterSaleApi.cancelAfterSale(id);
if (code === 0) { if (code === 0) {
await getDetail(id); await getDetail(id);
} }
}, },
}); });
} }
// //
const onCopy = () => { const onCopy = () => {
sheep.$helper.copyText(state.info.no); sheep.$helper.copyText(state.info.no);
}; };
async function getDetail(id) { async function getDetail(id) {
state.loading = true; state.loading = true;
const { code, data } = await AfterSaleApi.getAfterSale(id); const { code, data } = await AfterSaleApi.getAfterSale(id);
if (code !== 0) { if (code !== 0) {
@ -170,173 +207,173 @@
} else if ([61, 62, 63].includes(state.info.status)) { } else if ([61, 62, 63].includes(state.info.status)) {
state.active = 2; state.active = 2;
} }
} }
onLoad((options) => { onLoad((options) => {
if (!options.id) { if (!options.id) {
sheep.$helper.toast(`缺少订单信息,请检查`); sheep.$helper.toast(`缺少订单信息,请检查`);
return return;
} }
state.id = options.id; state.id = options.id;
getDetail(options.id); getDetail(options.id);
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// //
.steps-box { .steps-box {
width: 100%; width: 100%;
height: 190rpx; height: 190rpx;
background: v-bind(headerBg) no-repeat, background: v-bind(headerBg) no-repeat,
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient)); linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
background-size: 750rpx 100%; background-size: 750rpx 100%;
padding-left: 72rpx; padding-left: 72rpx;
.steps-item { .steps-item {
.sicon-circleclose { .sicon-circleclose {
font-size: 24rpx; font-size: 24rpx;
color: #fff; color: #fff;
} }
.sicon-circlecheck { .sicon-circlecheck {
font-size: 24rpx; font-size: 24rpx;
} }
.steps-item-title { .steps-item-title {
font-size: 24rpx; font-size: 24rpx;
font-weight: 400; font-weight: 400;
margin-top: 16rpx; margin-top: 16rpx;
margin-left: -36rpx; margin-left: -36rpx;
width: 100rpx; width: 100rpx;
text-align: center; text-align: center;
} }
} }
} }
.activity-color { .activity-color {
color: #fff; color: #fff;
} }
.info-color { .info-color {
color: rgba(#fff, 0.4); color: rgba(#fff, 0.4);
} }
.activity-bg { .activity-bg {
background: #fff; background: #fff;
} }
.info-bg { .info-bg {
background: rgba(#fff, 0.4); background: rgba(#fff, 0.4);
} }
.line { .line {
width: 270rpx; width: 270rpx;
height: 4rpx; height: 4rpx;
} }
// //
.status-box { .status-box {
position: relative; position: relative;
z-index: 3; z-index: 3;
background-color: #fff; background-color: #fff;
border-radius: 20rpx 20rpx 0px 0px; border-radius: 20rpx 20rpx 0px 0px;
padding: 20rpx; padding: 20rpx;
margin-top: -20rpx; margin-top: -20rpx;
.status-text { .status-text {
font-size: 28rpx; font-size: 28rpx;
font-weight: 500; font-weight: 500;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.status-time { .status-time {
font-size: 24rpx; font-size: 24rpx;
font-weight: 400; font-weight: 400;
color: rgba(153, 153, 153, 1); color: rgba(153, 153, 153, 1);
} }
} }
// 退 // 退
.aftersale-money { .aftersale-money {
background-color: #fff; background-color: #fff;
height: 98rpx; height: 98rpx;
padding: 0 20rpx; padding: 0 20rpx;
margin: 20rpx; margin: 20rpx;
.aftersale-money--title { .aftersale-money--title {
font-size: 28rpx; font-size: 28rpx;
font-weight: 500; font-weight: 500;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
} }
.aftersale-money--num { .aftersale-money--num {
font-size: 28rpx; font-size: 28rpx;
font-family: OPPOSANS; font-family: OPPOSANS;
font-weight: 500; font-weight: 500;
color: #ff3000; color: #ff3000;
} }
} }
// order-shop // order-shop
.order-shop { .order-shop {
padding: 20rpx; padding: 20rpx;
background-color: #fff; background-color: #fff;
margin: 0 20rpx 20rpx 20rpx; margin: 0 20rpx 20rpx 20rpx;
} }
// //
.aftersale-content { .aftersale-content {
background-color: #fff; background-color: #fff;
padding: 20rpx; padding: 20rpx;
margin: 0 20rpx; margin: 0 20rpx;
.aftersale-item { .aftersale-item {
height: 60rpx; height: 60rpx;
.copy-btn { .copy-btn {
background: #eeeeee; background: #eeeeee;
color: #333; color: #333;
border-radius: 20rpx; border-radius: 20rpx;
width: 75rpx; width: 75rpx;
height: 40rpx; height: 40rpx;
font-size: 22rpx; font-size: 22rpx;
} }
.item-title { .item-title {
color: #999; color: #999;
font-size: 28rpx; font-size: 28rpx;
} }
.item-content { .item-content {
color: #333; color: #333;
font-size: 28rpx; font-size: 28rpx;
} }
} }
} }
// //
.foot_box { .foot_box {
height: 100rpx; height: 100rpx;
background-color: #fff; background-color: #fff;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
.btn { .btn {
width: 160rpx; width: 160rpx;
line-height: 60rpx; line-height: 60rpx;
background: rgba(238, 238, 238, 1); background: rgba(238, 238, 238, 1);
border-radius: 30rpx; border-radius: 30rpx;
padding: 0; padding: 0;
margin-right: 20rpx; margin-right: 20rpx;
font-size: 26rpx; font-size: 26rpx;
font-weight: 400; font-weight: 400;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
} }
} }
</style> </style>

View File

@ -1,187 +1,210 @@
<!-- 售后列表 --> <!-- 售后列表 -->
<template> <template>
<s-layout title="售后列表"> <s-layout title="售后列表">
<!-- tab --> <!-- tab -->
<su-sticky bgColor="#fff"> <su-sticky bgColor="#fff">
<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab" /> <su-tabs
</su-sticky> :list="tabMaps"
<s-empty v-if="state.pagination.total === 0" icon="/static/data-empty.png" text="暂无数据" /> :scrollable="false"
<!-- 列表 --> @change="onTabsChange"
<view v-if="state.pagination.total > 0"> :current="state.currentTab"
<view class="list-box ss-m-y-20" v-for="order in state.pagination.list" :key="order.id" />
@tap="sheep.$router.go('/pages/order/aftersale/detail', { id: order.id })"> </su-sticky>
<view class="order-head ss-flex ss-col-center ss-row-between"> <s-empty v-if="state.pagination.total === 0" icon="/static/data-empty.png" text="暂无数据" />
<text class="no">服务单号{{ order.no }}</text> <!-- 列表 -->
<text class="state">{{ formatAfterSaleStatus(order) }}</text> <view v-if="state.pagination.total > 0">
</view> <view
<s-goods-item class="list-box ss-m-y-20"
v-for="order in state.pagination.list"
:key="order.id"
@tap="sheep.$router.go('/pages/order/aftersale/detail', { id: order.id })"
>
<view class="order-head ss-flex ss-col-center ss-row-between">
<text class="no">服务单号{{ order.no }}</text>
<text class="state">{{ formatAfterSaleStatus(order) }}</text>
</view>
<s-goods-item
:img="order.picUrl" :img="order.picUrl"
:title="order.spuName" :title="order.spuName"
:skuText="order.properties.map((property) => property.valueName).join(' ')" :skuText="order.properties.map((property) => property.valueName).join(' ')"
:price="order.refundPrice" :price="order.refundPrice"
/> />
<view class="apply-box ss-flex ss-col-center ss-row-between border-bottom ss-p-x-20"> <view class="apply-box ss-flex ss-col-center ss-row-between border-bottom ss-p-x-20">
<view class="ss-flex ss-col-center"> <view class="ss-flex ss-col-center">
<view class="title ss-m-r-20">{{ order.way === 10 ? '仅退款' : '退款退货' }}</view> <view class="title ss-m-r-20">{{ order.way === 10 ? '仅退款' : '退款退货' }}</view>
<view class="value">{{ formatAfterSaleStatusDescription(order) }}</view> <view class="value">{{ formatAfterSaleStatusDescription(order) }}</view>
</view> </view>
<text class="_icon-forward"></text> <text class="_icon-forward"></text>
</view> </view>
<view class="tool-btn-box ss-flex ss-col-center ss-row-right ss-p-r-20"> <view class="tool-btn-box ss-flex ss-col-center ss-row-right ss-p-r-20">
<!-- TODO 功能缺失填写退货信息 --> <!-- TODO 功能缺失填写退货信息 -->
<view> <view>
<button class="ss-reset-button tool-btn" @tap.stop="onApply(order.id)" <button
v-if="order?.buttons.includes('cancel')">取消申请</button> class="ss-reset-button tool-btn"
</view> @tap.stop="onApply(order.id)"
</view> v-if="order?.buttons.includes('cancel')"
</view> >取消申请</button
</view> >
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{ </view>
</view>
</view>
</view>
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多', contentdown: '上拉加载更多',
}" @tap="loadMore" /> }"
</s-layout> @tap="loadMore"
/>
</s-layout>
</template> </template>
<script setup> <script setup>
import sheep from '@/sheep'; import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { reactive } from 'vue'; import { reactive } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import { formatAfterSaleStatus, formatAfterSaleStatusDescription, handleAfterSaleButtons } from '@/sheep/hooks/useGoods'; import {
formatAfterSaleStatus,
formatAfterSaleStatusDescription,
handleAfterSaleButtons,
} from '@/sheep/hooks/useGoods';
import AfterSaleApi from '@/sheep/api/trade/afterSale'; import AfterSaleApi from '@/sheep/api/trade/afterSale';
import { resetPagination } from '@/sheep/util'; import { resetPagination } from '@/sheep/util';
const state = reactive({ const state = reactive({
currentTab: 0, currentTab: 0,
showApply: false, showApply: false,
pagination: { pagination: {
list: [], list: [],
total: 0, total: 0,
pageNo: 1, pageNo: 1,
pageSize: 10 pageSize: 10,
}, },
loadStatus: '', loadStatus: '',
}); });
// TODO // TODO
const tabMaps = [{ const tabMaps = [
name: '全部', {
value: 'all', name: '全部',
}, value: 'all',
// { },
// name: '', // {
// value: 'nooper', // name: '',
// }, // value: 'nooper',
// { // },
// name: '', // {
// value: 'ing', // name: '',
// }, // value: 'ing',
// { // },
// name: '', // {
// value: 'completed', // name: '',
// }, // value: 'completed',
// { // },
// name: '', // {
// value: 'refuse', // name: '',
// }, // value: 'refuse',
]; // },
];
// //
function onTabsChange(e) { function onTabsChange(e) {
resetPagination(state.pagination); resetPagination(state.pagination);
state.currentTab = e.index; state.currentTab = e.index;
getOrderList(); getOrderList();
} }
// //
async function getOrderList() { async function getOrderList() {
state.loadStatus = 'loading'; state.loadStatus = 'loading';
let { data, code } = await AfterSaleApi.getAfterSalePage({ let { data, code } = await AfterSaleApi.getAfterSalePage({
// type: tabMaps[state.currentTab].value, // type: tabMaps[state.currentTab].value,
pageNo: state.pagination.pageNo, pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize, pageSize: state.pagination.pageSize,
}); });
if (code !== 0) { if (code !== 0) {
return; return;
} }
data.list.forEach(order => handleAfterSaleButtons(order)); data.list.forEach((order) => handleAfterSaleButtons(order));
state.pagination.list = _.concat(state.pagination.list, data.list); state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total; state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore'; state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
} }
function onApply(orderId) { function onApply(orderId) {
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '确定要取消此申请吗?', content: '确定要取消此申请吗?',
success: async function(res) { success: async function (res) {
if (!res.confirm) { if (!res.confirm) {
return; return;
} }
const { code } = await AfterSaleApi.cancelAfterSale(orderId); const { code } = await AfterSaleApi.cancelAfterSale(orderId);
if (code === 0) { if (code === 0) {
resetPagination(state.pagination); resetPagination(state.pagination);
await getOrderList(); await getOrderList();
} }
}, },
}); });
} }
onLoad(async (options) => { onLoad(async (options) => {
if (options.type) { if (options.type) {
state.currentTab = options.type; state.currentTab = options.type;
} }
await getOrderList(); await getOrderList();
}); });
// //
function loadMore() { function loadMore() {
if (state.loadStatus === 'noMore') { if (state.loadStatus === 'noMore') {
return return;
} }
state.pagination.pageNo++; state.pagination.pageNo++;
getOrderList(); getOrderList();
} }
// //
onReachBottom(() => { onReachBottom(() => {
loadMore(); loadMore();
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.list-box { .list-box {
background-color: #fff; background-color: #fff;
.order-head { .order-head {
padding: 0 25rpx; padding: 0 25rpx;
height: 77rpx; height: 77rpx;
} }
.apply-box { .apply-box {
height: 82rpx; height: 82rpx;
.title { .title {
font-size: 24rpx; font-size: 24rpx;
} }
.value { .value {
font-size: 22rpx; font-size: 22rpx;
color: $dark-6; color: $dark-6;
} }
} }
.tool-btn-box { .tool-btn-box {
height: 100rpx; height: 100rpx;
.tool-btn { .tool-btn {
width: 160rpx; width: 160rpx;
height: 60rpx; height: 60rpx;
background: #f6f6f6; background: #f6f6f6;
border-radius: 30rpx; border-radius: 30rpx;
font-size: 26rpx; font-size: 26rpx;
font-weight: 400; font-weight: 400;
} }
} }
} }
</style> </style>

View File

@ -1,13 +1,7 @@
<template> <template>
<s-layout title="确认订单"> <s-layout title="确认订单">
<!-- TODO这个判断先删除 v-if="state.orderInfo.need_address === 1" --> <!-- 头部地址选择配送地址自提地址 -->
<view class="bg-white address-box ss-m-b-14 ss-r-b-10" @tap="onSelectAddress"> <AddressSelection v-model="addressState" />
<s-address-item :item="state.addressInfo" :hasBorderBottom="false">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</s-address-item>
</view>
<!-- 商品信息 --> <!-- 商品信息 -->
<view class="order-card-box ss-m-b-14"> <view class="order-card-box ss-m-b-14">
@ -46,26 +40,58 @@
</text> </text>
</view> </view>
</view> </view>
<!-- TODO 芋艿接入积分 -->
<view <view
class="order-item ss-flex ss-col-center ss-row-between" class="order-item ss-flex ss-col-center ss-row-between"
v-if="state.orderPayload.order_type === 'score'" v-if="state.orderInfo.type === 0"
> >
<view class="item-title">扣除积分</view> <view class="item-title">积分抵扣</view>
<view class="ss-flex ss-col-center"> <view class="ss-flex ss-col-center">
{{ state.pointStatus ? '剩余积分' : '当前积分' }}
<image <image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')" :src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="score-img" class="score-img"
/> />
<text class="item-value ss-m-r-24">{{ state.orderInfo.score_amount }}</text> <text class="item-value ss-m-r-24">
{{ state.pointStatus ? state.orderInfo.totalPoint - state.orderInfo.usePoint : (state.orderInfo.totalPoint || 0) }}
</text>
<checkbox-group @change="changeIntegral">
<checkbox :checked='state.pointStatus' :disabled="!state.orderInfo.totalPoint || state.orderInfo.totalPoint <= 0" />
</checkbox-group>
</view> </view>
</view> </view>
<view class="order-item ss-flex ss-col-center ss-row-between"> <!-- 快递配置时信息的展示 -->
<view class="order-item ss-flex ss-col-center ss-row-between" v-if='addressState.deliveryType === 1'>
<view class="item-title">运费</view> <view class="item-title">运费</view>
<view class="ss-flex ss-col-center"> <view class="ss-flex ss-col-center">
<text class="item-value ss-m-r-24"> <text class="item-value ss-m-r-24" v-if="state.orderInfo.price.deliveryPrice > 0">
+{{ fen2yuan(state.orderInfo.price.deliveryPrice) }} +{{ fen2yuan(state.orderInfo.price.deliveryPrice) }}
</text> </text>
<view class='item-value ss-m-r-24' v-else></view>
</view>
</view>
<!-- 门店自提时需要填写姓名和手机号 -->
<view class="order-item ss-flex ss-col-center ss-row-between" v-if='addressState.deliveryType === 2'>
<view class="item-title">联系人</view>
<view class="ss-flex ss-col-center">
<uni-easyinput
maxlength="20"
placeholder="请填写您的联系姓名"
v-model="addressState.receiverName"
:inputBorder="false"
:clearable="false"
/>
</view>
</view>
<view class="order-item ss-flex ss-col-center ss-row-between" v-if='addressState.deliveryType === 2'>
<view class="item-title">联系电话</view>
<view class="ss-flex ss-col-center">
<uni-easyinput
maxlength="20"
placeholder="请填写您的联系电话"
v-model="addressState.receiverMobile"
:inputBorder="false"
:clearable="false"
/>
</view> </view>
</view> </view>
<!-- 优惠劵只有 type = 0 普通订单非拼团秒杀砍价才可以使用优惠劵 --> <!-- 优惠劵只有 type = 0 普通订单非拼团秒杀砍价才可以使用优惠劵 -->
@ -120,7 +146,7 @@
{{ state.orderInfo.items.reduce((acc, item) => acc + item.count, 0) }} {{ state.orderInfo.items.reduce((acc, item) => acc + item.count, 0) }}
</view> </view>
<view>合计</view> <view>合计</view>
<view class="total-num text-red"> {{ fen2yuan(state.orderInfo.price.payPrice) }} </view> <view class="total-num text-red"> {{ fen2yuan(state.orderInfo.price.payPrice) }}</view>
</view> </view>
</view> </view>
@ -159,14 +185,13 @@
</template> </template>
<script setup> <script setup>
import { reactive } from 'vue'; import { reactive, ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import AddressSelection from '@/pages/order/addressSelection.vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { isEmpty } from 'lodash';
import OrderApi from '@/sheep/api/trade/order'; import OrderApi from '@/sheep/api/trade/order';
import CouponApi from '@/sheep/api/promotion/coupon'; import CouponApi from '@/sheep/api/promotion/coupon';
import { fen2yuan } from '@/sheep/hooks/useGoods'; import { fen2yuan } from '@/sheep/hooks/useGoods';
import { WxaSubscribeTemplate } from '@/sheep/util/const';
const state = reactive({ const state = reactive({
orderPayload: {}, orderPayload: {},
@ -174,27 +199,30 @@
items: [], // items: [], //
price: {}, // price: {}, //
}, },
addressInfo: {}, //
showCoupon: false, // showCoupon: false, //
couponInfo: [], // couponInfo: [], //
showDiscount: false, // showDiscount: false, //
// ========== ==========
pointStatus: false, //使
}); });
// const addressState = ref({
function onSelectAddress() { addressInfo: {}, //
uni.$once('SELECT_ADDRESS', (e) => { deliveryType: 1, // 1 - 2 -
changeConsignee(e.addressInfo); isPickUp: true, // TODO puhui999:
}); pickUpInfo: {}, //
sheep.$router.go('/pages/user/address/list'); receiverName: '', //
} receiverMobile: '', //
});
// & // ========== ==========
async function changeConsignee(addressInfo = {}) { /**
if (!isEmpty(addressInfo)) { * 使用积分抵扣
state.addressInfo = addressInfo; */
} const changeIntegral = async () => {
state.pointStatus = !state.pointStatus;
await getOrderInfo(); await getOrderInfo();
} };
// //
async function onSelectCoupon(couponId) { async function onSelectCoupon(couponId) {
@ -205,10 +233,28 @@
// //
function onConfirm() { function onConfirm() {
if (!state.addressInfo.id) { if (addressState.value.deliveryType === 1 && !addressState.value.addressInfo.id) {
sheep.$helper.toast('请选择收货地址'); sheep.$helper.toast('请选择收货地址');
return; return;
} }
if (addressState.value.deliveryType === 2) {
if (!addressState.value.pickUpInfo.id) {
sheep.$helper.toast('请选择自提门店地址');
return;
}
if (addressState.value.receiverName === '' || addressState.value.receiverMobile === '') {
sheep.$helper.toast('请填写联系人或联系人电话');
return;
}
if (!/^[\u4e00-\u9fa5\w]{2,16}$/.test(addressState.value.receiverName)) {
sheep.$helper.toast('请填写您的真实姓名');
return;
}
if (!/^1(3|4|5|7|8|9|6)\d{9}$/.test(addressState.value.receiverMobile)) {
sheep.$helper.toast('请填写正确的手机号');
return;
}
}
submitOrder(); submitOrder();
} }
@ -218,12 +264,15 @@
items: state.orderPayload.items, items: state.orderPayload.items,
couponId: state.orderPayload.couponId, couponId: state.orderPayload.couponId,
remark: state.orderPayload.remark, remark: state.orderPayload.remark,
addressId: state.addressInfo.id, deliveryType: addressState.value.deliveryType,
deliveryType: 1, // TODO addressId: addressState.value.addressInfo.id, //
pointStatus: false, // TODO pickUpStoreId: addressState.value.pickUpInfo.id,//
receiverName: addressState.value.receiverName,//
receiverMobile: addressState.value.receiverMobile,//
pointStatus: state.pointStatus,
combinationActivityId: state.orderPayload.combinationActivityId, combinationActivityId: state.orderPayload.combinationActivityId,
combinationHeadId: state.orderPayload.combinationHeadId, combinationHeadId: state.orderPayload.combinationHeadId,
seckillActivityId: state.orderPayload.seckillActivityId seckillActivityId: state.orderPayload.seckillActivityId,
}); });
if (code !== 0) { if (code !== 0) {
return; return;
@ -245,12 +294,15 @@
const { data, code } = await OrderApi.settlementOrder({ const { data, code } = await OrderApi.settlementOrder({
items: state.orderPayload.items, items: state.orderPayload.items,
couponId: state.orderPayload.couponId, couponId: state.orderPayload.couponId,
addressId: state.addressInfo.id, deliveryType: addressState.value.deliveryType,
deliveryType: 1, // TODO addressId: addressState.value.addressInfo.id, //
pointStatus: false, // TODO pickUpStoreId: addressState.value.pickUpInfo.id,//
receiverName: addressState.value.receiverName,//
receiverMobile: addressState.value.receiverMobile,//
pointStatus: state.pointStatus,
combinationActivityId: state.orderPayload.combinationActivityId, combinationActivityId: state.orderPayload.combinationActivityId,
combinationHeadId: state.orderPayload.combinationHeadId, combinationHeadId: state.orderPayload.combinationHeadId,
seckillActivityId: state.orderPayload.seckillActivityId seckillActivityId: state.orderPayload.seckillActivityId,
}); });
if (code !== 0) { if (code !== 0) {
return; return;
@ -258,7 +310,7 @@
state.orderInfo = data; state.orderInfo = data;
// //
if (state.orderInfo.address) { if (state.orderInfo.address) {
state.addressInfo = state.orderInfo.address; addressState.value.addressInfo = state.orderInfo.address;
} }
} }

View File

@ -45,9 +45,9 @@
</image> </image>
<view class="ss-font-30">{{ formatOrderStatus(state.orderInfo) }}</view> <view class="ss-font-30">{{ formatOrderStatus(state.orderInfo) }}</view>
</view> </view>
<view class="ss-font-26 ss-m-x-20 ss-m-b-70">{{ <view class="ss-font-26 ss-m-x-20 ss-m-b-70">
formatOrderStatusDescription(state.orderInfo) {{ formatOrderStatusDescription(state.orderInfo) }}
}}</view> </view>
</view> </view>
<!-- 收货地址 --> <!-- 收货地址 -->
@ -126,6 +126,9 @@
</view> </view>
</view> </view>
<!-- 自提核销 -->
<PickUpVerify :order-info="state.orderInfo" :systemStore="systemStore" ref="pickUpVerifyRef"></PickUpVerify>
<!-- 订单信息 --> <!-- 订单信息 -->
<view class="notice-box"> <view class="notice-box">
<view class="notice-box__content"> <view class="notice-box__content">
@ -167,11 +170,14 @@
<text class="title">运费</text> <text class="title">运费</text>
<text class="detail">{{ fen2yuan(state.orderInfo.deliveryPrice) }}</text> <text class="detail">{{ fen2yuan(state.orderInfo.deliveryPrice) }}</text>
</view> </view>
<!-- TODO 芋艿优惠劵抵扣积分抵扣 -->
<view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.couponPrice > 0"> <view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.couponPrice > 0">
<text class="title">优惠劵金额</text> <text class="title">优惠劵金额</text>
<text class="detail">-¥{{ fen2yuan(state.orderInfo.couponPrice) }}</text> <text class="detail">-¥{{ fen2yuan(state.orderInfo.couponPrice) }}</text>
</view> </view>
<view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.pointPrice > 0">
<text class="title">积分抵扣</text>
<text class="detail">-¥{{ fen2yuan(state.orderInfo.pointPrice) }}</text>
</view>
<view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.discountPrice > 0"> <view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.discountPrice > 0">
<text class="title">活动优惠</text> <text class="title">活动优惠</text>
<text class="detail">¥{{ fen2yuan(state.orderInfo.discountPrice) }}</text> <text class="detail">¥{{ fen2yuan(state.orderInfo.discountPrice) }}</text>
@ -251,8 +257,8 @@
<script setup> <script setup>
import sheep from '@/sheep'; import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { reactive } from 'vue'; import { reactive, ref } from 'vue';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import { import {
fen2yuan, fen2yuan,
formatOrderStatus, formatOrderStatus,
@ -260,6 +266,8 @@
handleOrderButtons, handleOrderButtons,
} from '@/sheep/hooks/useGoods'; } from '@/sheep/hooks/useGoods';
import OrderApi from '@/sheep/api/trade/order'; import OrderApi from '@/sheep/api/trade/order';
import DeliveryApi from '@/sheep/api/trade/delivery';
import PickUpVerify from '@/pages/order/pickUpVerify.vue';
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2; const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png'); const headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
@ -270,6 +278,9 @@
comeinType: '', // comeinType: '', //
}); });
// ========== ==========
const systemStore = ref({}); //
// //
const onCopy = () => { const onCopy = () => {
sheep.$helper.copyText(state.orderInfo.no); sheep.$helper.copyText(state.orderInfo.no);
@ -294,7 +305,7 @@
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '确定要取消订单吗?', content: '确定要取消订单吗?',
success: async function (res) { success: async function(res) {
if (!res.confirm) { if (!res.confirm) {
return; return;
} }
@ -313,7 +324,7 @@
}); });
} }
// TODO //
async function onConfirm(orderId, ignore = false) { async function onConfirm(orderId, ignore = false) {
// //
// todo: // todo:
@ -366,15 +377,18 @@
}, },
}); });
} }
// #endif // #endif
// //
function onComment(id) { function onComment(id) {
sheep.$router.go('/pages/goods/comment/add', { sheep.$router.go('/pages/goods/comment/add', {
id id,
}); });
} }
const pickUpVerifyRef = ref();
async function getOrderDetail(id) { async function getOrderDetail(id) {
// //
let res; let res;
@ -389,6 +403,14 @@
if (res.code === 0) { if (res.code === 0) {
state.orderInfo = res.data; state.orderInfo = res.data;
handleOrderButtons(state.orderInfo); handleOrderButtons(state.orderInfo);
//
if (res.data.pickUpStoreId) {
const { data } = await DeliveryApi.getDeliveryPickUpStore(res.data.pickUpStoreId);
systemStore.value = data || {};
}
if (state.orderInfo.deliveryType === 2 && state.orderInfo.payStatus) {
pickUpVerifyRef.value && pickUpVerifyRef.value.markCode(res.data.pickUpVerifyCode);
}
} else { } else {
sheep.$router.back(); sheep.$router.back();
} }
@ -429,7 +451,7 @@
color: rgba(#fff, 0.9); color: rgba(#fff, 0.9);
width: 100%; width: 100%;
background: v-bind(headerBg) no-repeat, background: v-bind(headerBg) no-repeat,
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient)); linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
background-size: 750rpx 100%; background-size: 750rpx 100%;
box-sizing: border-box; box-sizing: border-box;

View File

@ -1,243 +1,284 @@
<!-- 订单列表 --> <!-- 订单列表 -->
<template> <template>
<s-layout title="我的订单"> <s-layout title="我的订单">
<su-sticky bgColor="#fff"> <su-sticky bgColor="#fff">
<su-tabs :list="tabMaps" :scrollable="false" @change="onTabsChange" :current="state.currentTab" /> <su-tabs
</su-sticky> :list="tabMaps"
<s-empty v-if="state.pagination.total === 0" icon="/static/order-empty.png" text="暂无订单" /> :scrollable="false"
<view v-if="state.pagination.total > 0"> @change="onTabsChange"
<view class="bg-white order-list-card-box ss-r-10 ss-m-t-14 ss-m-20" v-for="order in state.pagination.list" :current="state.currentTab"
:key="order.id" @tap="onOrderDetail(order.id)"> />
<view class="order-card-header ss-flex ss-col-center ss-row-between ss-p-x-20"> </su-sticky>
<view class="order-no">订单号{{ order.no }}</view> <s-empty v-if="state.pagination.total === 0" icon="/static/order-empty.png" text="暂无订单" />
<view class="order-state ss-font-26" :class="formatOrderColor(order)"> <view v-if="state.pagination.total > 0">
<view
class="bg-white order-list-card-box ss-r-10 ss-m-t-14 ss-m-20"
v-for="order in state.pagination.list"
:key="order.id"
@tap="onOrderDetail(order.id)"
>
<view class="order-card-header ss-flex ss-col-center ss-row-between ss-p-x-20">
<view class="order-no">订单号{{ order.no }}</view>
<view class="order-state ss-font-26" :class="formatOrderColor(order)">
{{ formatOrderStatus(order) }} {{ formatOrderStatus(order) }}
</view> </view>
</view> </view>
<view class="border-bottom" v-for="item in order.items" :key="item.id"> <view class="border-bottom" v-for="item in order.items" :key="item.id">
<s-goods-item <s-goods-item
:img="item.picUrl" :img="item.picUrl"
:title="item.spuName" :title="item.spuName"
:skuText="item.properties.map((property) => property.valueName).join(' ')" :skuText="item.properties.map((property) => property.valueName).join(' ')"
:price="item.price" :price="item.price"
:num="item.count" :num="item.count"
/> />
</view> </view>
<view class="pay-box ss-m-t-30 ss-flex ss-row-right ss-p-r-20"> <view class="pay-box ss-m-t-30 ss-flex ss-row-right ss-p-r-20">
<view class="ss-flex ss-col-center"> <view class="ss-flex ss-col-center">
<view class="discounts-title pay-color"> {{ order.productCount }} 件商品,总金额:</view> <view class="discounts-title pay-color"
<view class="discounts-money pay-color"> > {{ order.productCount }} 件商品,总金额:</view
{{ fen2yuan(order.payPrice) }} >
</view> <view class="discounts-money pay-color"> {{ fen2yuan(order.payPrice) }} </view>
</view> </view>
</view> </view>
<view class="order-card-footer ss-flex ss-col-center ss-p-x-20" <view
:class="order.buttons.length > 3 ? 'ss-row-between' : 'ss-row-right'"> class="order-card-footer ss-flex ss-col-center ss-p-x-20"
<view class="ss-flex ss-col-center"> :class="order.buttons.length > 3 ? 'ss-row-between' : 'ss-row-right'"
<button v-if="order.buttons.includes('combination')" class="tool-btn ss-reset-button" >
@tap.stop="onOrderGroupon(order)"> <view class="ss-flex ss-col-center">
<button
v-if="order.buttons.includes('combination')"
class="tool-btn ss-reset-button"
@tap.stop="onOrderGroupon(order)"
>
拼团详情 拼团详情
</button> </button>
<button v-if="order.buttons.length === 0" class="tool-btn ss-reset-button" <button
@tap.stop="onOrderDetail(order.id)"> v-if="order.buttons.length === 0"
class="tool-btn ss-reset-button"
@tap.stop="onOrderDetail(order.id)"
>
查看详情 查看详情
</button> </button>
<button v-if="order.buttons.includes('confirm')" class="tool-btn ss-reset-button" <button
@tap.stop="onConfirm(order)"> v-if="order.buttons.includes('confirm')"
class="tool-btn ss-reset-button"
@tap.stop="onConfirm(order)"
>
确认收货 确认收货
</button> </button>
<button v-if="order.buttons.includes('express')" class="tool-btn ss-reset-button" <button
@tap.stop="onExpress(order.id)"> v-if="order.buttons.includes('express')"
查看物流 class="tool-btn ss-reset-button"
</button> @tap.stop="onExpress(order.id)"
<button v-if="order.buttons.includes('cancel')" class="tool-btn ss-reset-button" >
@tap.stop="onCancel(order.id)"> 查看物流
取消订单 </button>
</button> <button
<button v-if="order.buttons.includes('comment')" class="tool-btn ss-reset-button" v-if="order.buttons.includes('cancel')"
@tap.stop="onComment(order.id)"> class="tool-btn ss-reset-button"
评价 @tap.stop="onCancel(order.id)"
</button> >
<button v-if="order.buttons.includes('delete')" class="delete-btn ss-reset-button" 取消订单
@tap.stop="onDelete(order.id)"> </button>
删除订单 <button
</button> v-if="order.buttons.includes('comment')"
<button v-if="order.buttons.includes('pay')" class="tool-btn ss-reset-button ui-BG-Main-Gradient" class="tool-btn ss-reset-button"
@tap.stop="onPay(order.payOrderId)"> @tap.stop="onComment(order.id)"
继续支付 >
</button> 评价
</view> </button>
</view> <button
</view> v-if="order.buttons.includes('delete')"
</view> class="delete-btn ss-reset-button"
@tap.stop="onDelete(order.id)"
>
删除订单
</button>
<button
v-if="order.buttons.includes('pay')"
class="tool-btn ss-reset-button ui-BG-Main-Gradient"
@tap.stop="onPay(order.payOrderId)"
>
继续支付
</button>
</view>
</view>
</view>
</view>
<!-- 加载更多 --> <!-- 加载更多 -->
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{ <uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多', contentdown: '上拉加载更多',
}" @tap="loadMore" /> }"
</s-layout> @tap="loadMore"
/>
</s-layout>
</template> </template>
<script setup> <script setup>
import { reactive } from 'vue'; import { reactive } from 'vue';
import { onLoad, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'; import { onLoad, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app';
import { import {
fen2yuan, fen2yuan,
formatOrderColor, formatOrderStatus, handleOrderButtons, formatOrderColor,
formatOrderStatus,
handleOrderButtons,
} from '@/sheep/hooks/useGoods'; } from '@/sheep/hooks/useGoods';
import sheep from '@/sheep'; import sheep from '@/sheep';
import _ from 'lodash'; import _ from 'lodash-es';
import { import { isEmpty } from 'lodash-es';
isEmpty
} from 'lodash';
import OrderApi from '@/sheep/api/trade/order'; import OrderApi from '@/sheep/api/trade/order';
import { resetPagination } from '@/sheep/util'; import { resetPagination } from '@/sheep/util';
// //
const state = reactive({ const state = reactive({
currentTab: 0, // tabMaps currentTab: 0, // tabMaps
pagination: { pagination: {
list: [], list: [],
total: 0, total: 0,
pageNo: 1, pageNo: 1,
pageSize: 5, pageSize: 5,
}, },
loadStatus: '' loadStatus: '',
}); });
const tabMaps = [{ const tabMaps = [
name: '全部' {
}, name: '全部',
{ },
name: '待付款', {
value: 0, name: '待付款',
}, value: 0,
{ },
name: '待发货', {
value: 10, name: '待发货',
}, value: 10,
{ },
name: '待收货', {
value: 20, name: '待收货',
}, value: 20,
{ },
name: '待评价', {
value: 30, name: '待评价',
}, value: 30,
]; },
];
// //
function onTabsChange(e) { function onTabsChange(e) {
if (state.currentTab === e.index) { if (state.currentTab === e.index) {
return; return;
} }
// //
resetPagination(state.pagination); resetPagination(state.pagination);
state.currentTab = e.index; state.currentTab = e.index;
getOrderList(); getOrderList();
} }
// //
function onOrderDetail(id) { function onOrderDetail(id) {
sheep.$router.go('/pages/order/detail', { sheep.$router.go('/pages/order/detail', {
id, id,
}); });
} }
// //
function onOrderGroupon(order) { function onOrderGroupon(order) {
sheep.$router.go('/pages/activity/groupon/detail', { sheep.$router.go('/pages/activity/groupon/detail', {
id: order.combinationRecordId, id: order.combinationRecordId,
}); });
} }
// //
function onPay(payOrderId) { function onPay(payOrderId) {
sheep.$router.go('/pages/pay/index', { sheep.$router.go('/pages/pay/index', {
id: payOrderId, id: payOrderId,
}); });
} }
// //
function onComment(id) { function onComment(id) {
sheep.$router.go('/pages/goods/comment/add', { sheep.$router.go('/pages/goods/comment/add', {
id, id,
}); });
} }
// TODO // TODO
async function onConfirm(order, ignore = false) { async function onConfirm(order, ignore = false) {
// //
// todo: // todo:
// 1.return // 1.return
// 2.mpConfirm,App.vueshow // 2.mpConfirm,App.vueshow
let isOpenBusinessView = true; let isOpenBusinessView = true;
if ( if (
sheep.$platform.name === 'WechatMiniProgram' && sheep.$platform.name === 'WechatMiniProgram' &&
!isEmpty(order.wechat_extra_data) && !isEmpty(order.wechat_extra_data) &&
isOpenBusinessView && isOpenBusinessView &&
!ignore !ignore
) { ) {
mpConfirm(order); mpConfirm(order);
return; return;
} }
// //
const { code } = await OrderApi.receiveOrder(order.id); const { code } = await OrderApi.receiveOrder(order.id);
if (code === 0) { if (code === 0) {
resetPagination(state.pagination); resetPagination(state.pagination);
await getOrderList(); await getOrderList();
} }
} }
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
// TODO // TODO
function mpConfirm(order) { function mpConfirm(order) {
if (!wx.openBusinessView) { if (!wx.openBusinessView) {
sheep.$helper.toast(`请升级微信版本`); sheep.$helper.toast(`请升级微信版本`);
return; return;
} }
wx.openBusinessView({ wx.openBusinessView({
businessType: 'weappOrderConfirm', businessType: 'weappOrderConfirm',
extraData: { extraData: {
merchant_id: '1481069012', merchant_id: '1481069012',
merchant_trade_no: order.wechat_extra_data.merchant_trade_no, merchant_trade_no: order.wechat_extra_data.merchant_trade_no,
transaction_id: order.wechat_extra_data.transaction_id, transaction_id: order.wechat_extra_data.transaction_id,
}, },
success(response) { success(response) {
console.log('success:', response); console.log('success:', response);
if (response.errMsg === 'openBusinessView:ok') { if (response.errMsg === 'openBusinessView:ok') {
if (response.extraData.status === 'success') { if (response.extraData.status === 'success') {
onConfirm(order, true); onConfirm(order, true);
} }
} }
}, },
fail(error) { fail(error) {
console.log('error:', error); console.log('error:', error);
}, },
complete(result) { complete(result) {
console.log('result:', result); console.log('result:', result);
}, },
}); });
} }
// #endif // #endif
// //
async function onExpress(id) { async function onExpress(id) {
sheep.$router.go('/pages/order/express/log', { sheep.$router.go('/pages/order/express/log', {
id, id,
}); });
} }
// //
async function onCancel(orderId) { async function onCancel(orderId) {
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '确定要取消订单吗?', content: '确定要取消订单吗?',
success: async function(res) { success: async function (res) {
if (!res.confirm) { if (!res.confirm) {
return; return;
} }
const { code } = await OrderApi.cancelOrder(orderId); const { code } = await OrderApi.cancelOrder(orderId);
if (code === 0) { if (code === 0) {
// //
@ -246,208 +287,209 @@
orderInfo.status = 40; orderInfo.status = 40;
handleOrderButtons(orderInfo); handleOrderButtons(orderInfo);
} }
}, },
}); });
} }
// //
function onDelete(orderId) { function onDelete(orderId) {
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '确定要删除订单吗?', content: '确定要删除订单吗?',
success: async function(res) { success: async function (res) {
if (res.confirm) { if (res.confirm) {
const { code } = await OrderApi.deleteOrder(orderId); const { code } = await OrderApi.deleteOrder(orderId);
if (code === 0) { if (code === 0) {
// //
let index = state.pagination.list.findIndex((order) => order.id === orderId); let index = state.pagination.list.findIndex((order) => order.id === orderId);
state.pagination.list.splice(index, 1); state.pagination.list.splice(index, 1);
} }
} }
}, },
}); });
} }
// //
async function getOrderList() { async function getOrderList() {
state.loadStatus = 'loading'; state.loadStatus = 'loading';
let { code, data } = await OrderApi.getOrderPage({ let { code, data } = await OrderApi.getOrderPage({
pageNo: state.pagination.pageNo, pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize, pageSize: state.pagination.pageSize,
status: tabMaps[state.currentTab].value, status: tabMaps[state.currentTab].value,
commentStatus: tabMaps[state.currentTab].value === 30 ? false : null commentStatus: tabMaps[state.currentTab].value === 30 ? false : null,
}); });
if (code !== 0) { if (code !== 0) {
return; return;
} }
data.list.forEach(order => handleOrderButtons(order)); data.list.forEach((order) => handleOrderButtons(order));
state.pagination.list = _.concat(state.pagination.list, data.list) state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total; state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore'; state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
} }
onLoad(async (options) => { onLoad(async (options) => {
if (options.type) { if (options.type) {
state.currentTab = options.type; state.currentTab = options.type;
} }
await getOrderList(); await getOrderList();
}); });
// //
function loadMore() { function loadMore() {
if (state.loadStatus === 'noMore') { if (state.loadStatus === 'noMore') {
return return;
} }
state.pagination.pageNo++; state.pagination.pageNo++;
getOrderList(); getOrderList();
} }
// //
onReachBottom(() => { onReachBottom(() => {
loadMore(); loadMore();
}); });
// //
onPullDownRefresh(() => { onPullDownRefresh(() => {
resetPagination(state.pagination); resetPagination(state.pagination);
getOrderList(); getOrderList();
setTimeout(function() { setTimeout(function () {
uni.stopPullDownRefresh(); uni.stopPullDownRefresh();
}, 800); }, 800);
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.score-img { .score-img {
width: 36rpx; width: 36rpx;
height: 36rpx; height: 36rpx;
margin: 0 4rpx; margin: 0 4rpx;
} }
.tool-btn { .tool-btn {
width: 160rpx; width: 160rpx;
height: 60rpx; height: 60rpx;
background: #f6f6f6; background: #f6f6f6;
font-size: 26rpx; font-size: 26rpx;
border-radius: 30rpx; border-radius: 30rpx;
margin-right: 10rpx; margin-right: 10rpx;
&:last-of-type { &:last-of-type {
margin-right: 0; margin-right: 0;
} }
} }
.delete-btn { .delete-btn {
width: 160rpx; width: 160rpx;
height: 56rpx; height: 56rpx;
color: #ff3000; color: #ff3000;
background: #fee; background: #fee;
border-radius: 28rpx; border-radius: 28rpx;
font-size: 26rpx; font-size: 26rpx;
margin-right: 10rpx; margin-right: 10rpx;
line-height: normal; line-height: normal;
&:last-of-type { &:last-of-type {
margin-right: 0; margin-right: 0;
} }
} }
.apply-btn { .apply-btn {
width: 140rpx; width: 140rpx;
height: 50rpx; height: 50rpx;
border-radius: 25rpx; border-radius: 25rpx;
font-size: 24rpx; font-size: 24rpx;
border: 2rpx solid #dcdcdc; border: 2rpx solid #dcdcdc;
line-height: normal; line-height: normal;
margin-left: 16rpx; margin-left: 16rpx;
} }
.swiper-box { .swiper-box {
flex: 1; flex: 1;
.swiper-item { .swiper-item {
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
} }
.order-list-card-box { .order-list-card-box {
.order-card-header { .order-card-header {
height: 80rpx; height: 80rpx;
.order-no { .order-no {
font-size: 26rpx; font-size: 26rpx;
font-weight: 500; font-weight: 500;
} }
.order-state {} .order-state {
} }
}
.pay-box { .pay-box {
.discounts-title { .discounts-title {
font-size: 24rpx; font-size: 24rpx;
line-height: normal; line-height: normal;
color: #999999; color: #999999;
} }
.discounts-money { .discounts-money {
font-size: 24rpx; font-size: 24rpx;
line-height: normal; line-height: normal;
color: #999; color: #999;
font-family: OPPOSANS; font-family: OPPOSANS;
} }
.pay-color { .pay-color {
color: #333; color: #333;
} }
} }
.order-card-footer { .order-card-footer {
height: 100rpx; height: 100rpx;
.more-item-box { .more-item-box {
padding: 20rpx; padding: 20rpx;
.more-item { .more-item {
height: 60rpx; height: 60rpx;
.title { .title {
font-size: 26rpx; font-size: 26rpx;
} }
} }
} }
.more-btn { .more-btn {
color: $dark-9; color: $dark-9;
font-size: 24rpx; font-size: 24rpx;
} }
.content { .content {
width: 154rpx; width: 154rpx;
color: #333333; color: #333333;
font-size: 26rpx; font-size: 26rpx;
font-weight: 500; font-weight: 500;
} }
} }
} }
:deep(.uni-tooltip-popup) { :deep(.uni-tooltip-popup) {
background: var(--ui-BG); background: var(--ui-BG);
} }
.warning-color { .warning-color {
color: #faad14; color: #faad14;
} }
.danger-color { .danger-color {
color: #ff3000; color: #ff3000;
} }
.success-color { .success-color {
color: #52c41a; color: #52c41a;
} }
.info-color { .info-color {
color: #999999; color: #999999;
} }
</style> </style>

View File

@ -0,0 +1,261 @@
<template>
<view class='order-details'>
<!-- 自提商品核销 -->
<view v-if="orderInfo.deliveryType === 2 && orderInfo.payStatus" class="writeOff borRadius14">
<view class="title">核销信息</view>
<view class="grayBg flex-center">
<view class="pictrue">
<image
v-if="!!painterImageUrl"
:src="painterImageUrl"
:style="{width: `${state.qrcodeSize}px`, height: `${state.qrcodeSize}px`}"
:show-menu-by-longpress="true"
/>
</view>
</view>
<view class="gear">
<image :src="sheep.$url.static('/static/images/writeOff.png', 'local')"></image>
</view>
<view class="num">{{ orderInfo.pickUpVerifyCode }}</view>
<view class="rules">
<!-- TODO puhui999: 需要后端放回使用 receiveTime 即可 -->
<!-- <view class="item">-->
<!-- <view class="rulesTitle flex flex-wrap align-center">-->
<!-- 核销时间-->
<!-- </view>-->
<!-- <view class="info">-->
<!-- 每日-->
<!-- <text class="time">2020-2-+52</text>-->
<!-- </view>-->
<!-- </view>-->
<view class="item">
<view class="rulesTitle flex flex-wrap align-center">
<text class="iconfont icon-shuoming1"></text>
使用说明
</view>
<view class="info">可将二维码出示给店员扫描或提供数字核销码</view>
</view>
</view>
</view>
<view v-if="orderInfo.deliveryType === 2" class="map flex flex-wrap align-center ss-row-between borRadius14">
<view>自提地址信息</view>
<view class="place cart-color flex flex-wrap flex-center" @tap="showMaoLocation">
查看位置
</view>
</view>
<!-- 海报画板默认隐藏只用来生成海报生成方式为主动调用 -->
<l-painter
v-if="showPainter"
isCanvasToTempFilePath
pathType="url"
@success="setPainterImageUrl"
hidden
ref="painterRef"
/>
</view>
</template>
<script setup>
import sheep from '@/sheep';
import { reactive, ref } from 'vue';
const props = defineProps({
orderInfo: {
type: Object,
default() {},
},
systemStore:{
type: Object,
default() {},
}
});
const state = reactive({
qrcodeSize: 145
})
/**
* 打开地图
*/
const showMaoLocation = () => {
console.log(props.systemStore);
if (!props.systemStore.latitude || !props.systemStore.longitude) {
sheep.$helper.toast('缺少经纬度信息无法查看地图!');
return
}
uni.openLocation({
latitude: props.systemStore.latitude,
longitude: props.systemStore.longitude,
scale: 8,
name: props.systemStore.name,
address: props.systemStore.areaName + props.systemStore.detailAddress,
});
}
/**
* 拨打电话
*/
const makePhone = () => {
uni.makePhoneCall({
phoneNumber: props.systemStore.phone
})
}
const painterRef = ref(); //
const painterImageUrl = ref(); // url
const showPainter = ref(true)
//
const renderPoster = async (poster) => {
await painterRef.value.render(poster);
};
//
const setPainterImageUrl = (path) => {
painterImageUrl.value = path;
showPainter.value = false
};
/**
* 生成核销二维码
*/
const markCode = (text) => {
renderPoster({
css: {
width: `${state.qrcodeSize}px`,
height: `${state.qrcodeSize}px`
},
views:[
{
type: 'qrcode',
text: text,
css: {
width: `${state.qrcodeSize}px`,
height: `${state.qrcodeSize}px`
}
}
]
})
}
defineExpose({
markCode
})
</script>
<style scoped lang="scss">
// TODO puhui999: bug
.borRadius14 {
border-radius: 14rpx !important;
}
.cart-color {
color: #E93323 !important;
border: 1px solid #E93323 !important
}
.order-details{
border-radius: 10rpx;
margin: 0 20rpx 20rpx 20rpx;
}
.order-details .writeOff {
background-color: #fff;
margin-top: 15rpx;
padding-bottom: 50rpx;
}
.order-details .writeOff .title {
font-size: 30rpx;
color: #282828;
height: 87rpx;
border-bottom: 1px solid #f0f0f0;
padding: 0 24rpx;
line-height: 87rpx;
}
.order-details .writeOff .grayBg {
background-color: #f2f5f7;
width: 590rpx;
height: 384rpx;
border-radius: 20rpx 20rpx 0 0;
margin: 50rpx auto 0 auto;
padding-top: 55rpx;
}
.order-details .writeOff .grayBg .pictrue {
width: 290rpx;
height: 290rpx;
}
.order-details .writeOff .grayBg .pictrue image {
width: 100%;
height: 100%;
}
.order-details .writeOff .gear {
width: 590rpx;
height: 30rpx;
margin: 0 auto;
}
.order-details .writeOff .gear image {
width: 100%;
height: 100%;
}
.order-details .writeOff .num {
background-color: #f0c34c;
width: 590rpx;
height: 84rpx;
color: #282828;
font-size: 48rpx;
margin: 0 auto;
border-radius: 0 0 20rpx 20rpx;
text-align: center;
padding-top: 4rpx;
}
.order-details .writeOff .rules {
margin: 46rpx 30rpx 0 30rpx;
border-top: 1px solid #f0f0f0;
padding-top: 10rpx;
}
.order-details .writeOff .rules .item {
margin-top: 20rpx;
}
.order-details .writeOff .rules .item .rulesTitle {
font-size: 28rpx;
color: #282828;
}
.order-details .writeOff .rules .item .rulesTitle .iconfont {
font-size: 30rpx;
color: #333;
margin-right: 8rpx;
margin-top: 5rpx;
}
.order-details .writeOff .rules .item .info {
font-size: 28rpx;
color: #999;
margin-top: 7rpx;
}
.order-details .writeOff .rules .item .info .time {
margin-left: 20rpx;
}
.order-details .map {
height: 86rpx;
font-size: 30rpx;
color: #282828;
line-height: 86rpx;
border-bottom: 1px solid #f0f0f0;
margin-top: 15rpx;
background-color: #fff;
padding: 0 24rpx;
}
.order-details .map .place {
font-size: 26rpx;
width: 176rpx;
height: 50rpx;
border-radius: 25rpx;
line-height: 50rpx;
text-align: center;
}
</style>

View File

@ -8,7 +8,7 @@
<view class="title">充值金额</view> <view class="title">充值金额</view>
<view class="num" :class="item.refundStatus === 10 ? 'danger-color' : 'success-color'"> <view class="num" :class="item.refundStatus === 10 ? 'danger-color' : 'success-color'">
{{ fen2yuan(item.payPrice) }} {{ fen2yuan(item.payPrice) }}
<text v-if="item.bonusPrice > 0"> {{ fen2yuan(item.bonusPrice)}} </text> <text v-if="item.bonusPrice > 0"> {{ fen2yuan(item.bonusPrice) }} </text>
</view> </view>
</view> </view>
<view class="status-box item ss-flex ss-col-center ss-row-between"> <view class="status-box item ss-flex ss-col-center ss-row-between">
@ -30,7 +30,9 @@
</view> </view>
<view class="time-box item ss-flex ss-col-center ss-row-between"> <view class="time-box item ss-flex ss-col-center ss-row-between">
<text class="item-title">充值时间</text> <text class="item-title">充值时间</text>
<view class="time"> {{ sheep.$helper.timeFormat(item.payTime, 'yyyy-mm-dd hh:MM:ss') }}</view> <view class="time">
{{ sheep.$helper.timeFormat(item.payTime, 'yyyy-mm-dd hh:MM:ss') }}</view
>
</view> </view>
</view> </view>
</view> </view>
@ -53,7 +55,7 @@
<script setup> <script setup>
import { reactive } from 'vue'; import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import _ from 'lodash'; import _ from 'lodash-es';
import PayWalletApi from '@/sheep/api/pay/wallet'; import PayWalletApi from '@/sheep/api/pay/wallet';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { fen2yuan } from '../../sheep/hooks/useGoods'; import { fen2yuan } from '../../sheep/hooks/useGoods';

View File

@ -50,7 +50,10 @@
</view> </view>
<!-- #ifdef MP --> <!-- #ifdef MP -->
<view class="subscribe-box ss-flex ss-m-t-44" v-if="showSubscribeBtn && state.orderType === 'goods'"> <view
class="subscribe-box ss-flex ss-m-t-44"
v-if="showSubscribeBtn && state.orderType === 'goods'"
>
<image class="subscribe-img" :src="sheep.$url.static('/static/img/shop/order/cargo.png')" /> <image class="subscribe-img" :src="sheep.$url.static('/static/img/shop/order/cargo.png')" />
<view class="subscribe-title ss-m-r-48 ss-m-l-16">获取实时发货信息与订单状态</view> <view class="subscribe-title ss-m-r-48 ss-m-l-16">获取实时发货信息与订单状态</view>
<view class="subscribe-start" @tap="subscribeMessage"></view> <view class="subscribe-start" @tap="subscribeMessage"></view>
@ -63,7 +66,7 @@
<script setup> <script setup>
import { onLoad, onHide, onShow } from '@dcloudio/uni-app'; import { onLoad, onHide, onShow } from '@dcloudio/uni-app';
import { reactive, computed, ref } from 'vue'; import { reactive, computed, ref } from 'vue';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import sheep from '@/sheep'; import sheep from '@/sheep';
import PayOrderApi from '@/sheep/api/pay/order'; import PayOrderApi from '@/sheep/api/pay/order';
import { fen2yuan } from '@/sheep/hooks/useGoods'; import { fen2yuan } from '@/sheep/hooks/useGoods';
@ -114,12 +117,12 @@
uni.showModal({ uni.showModal({
title: '支付结果', title: '支付结果',
showCancel: false, // showCancel: false, //
content: "支付成功", content: '支付成功',
success: () => { success: () => {
// showModal // showModal
autoSubscribeMessage(); autoSubscribeMessage();
} },
}) });
// #endif // #endif
// //
@ -153,8 +156,8 @@
} }
// #ifdef MP // #ifdef MP
const showSubscribeBtn = ref(false) // const showSubscribeBtn = ref(false); //
const SUBSCRIBE_BTN_STATUS_STORAGE_KEY = "subscribe_btn_status" const SUBSCRIBE_BTN_STATUS_STORAGE_KEY = 'subscribe_btn_status';
function subscribeMessage() { function subscribeMessage() {
if (state.orderType !== 'goods') { if (state.orderType !== 'goods') {
return; return;
@ -176,11 +179,11 @@
const subscribeBtnStatus = uni.getStorageSync(SUBSCRIBE_BTN_STATUS_STORAGE_KEY); const subscribeBtnStatus = uni.getStorageSync(SUBSCRIBE_BTN_STATUS_STORAGE_KEY);
if (!subscribeBtnStatus) { if (!subscribeBtnStatus) {
showSubscribeBtn.value = true; showSubscribeBtn.value = true;
return return;
} }
// 2. // 2.
subscribeMessage() subscribeMessage();
} }
// #endif // #endif

View File

@ -1,78 +1,118 @@
<!-- 收货地址的新增/编辑 --> <!-- 收货地址的新增/编辑 -->
<template> <template>
<s-layout :title="state.model.id ? '编辑地址' : '新增地址'"> <s-layout :title="state.model.id ? '编辑地址' : '新增地址'">
<uni-forms ref="addressFormRef" v-model="state.model" :rules="rules" validateTrigger="bind" <uni-forms
labelWidth="160" labelAlign="left" border :labelStyle="{ fontWeight: 'bold' }"> ref="addressFormRef"
<view class="bg-white form-box ss-p-x-30"> v-model="state.model"
<uni-forms-item name="name" label="收货人" class="form-item"> :rules="rules"
<uni-easyinput v-model="state.model.name" placeholder="请填写收货人姓名" :inputBorder="false" validateTrigger="bind"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal" /> labelWidth="160"
</uni-forms-item> labelAlign="left"
border
:labelStyle="{ fontWeight: 'bold' }"
>
<view class="bg-white form-box ss-p-x-30">
<uni-forms-item name="name" label="收货人" class="form-item">
<uni-easyinput
v-model="state.model.name"
placeholder="请填写收货人姓名"
:inputBorder="false"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
/>
</uni-forms-item>
<uni-forms-item name="mobile" label="手机号" class="form-item"> <uni-forms-item name="mobile" label="手机号" class="form-item">
<uni-easyinput v-model="state.model.mobile" type="number" placeholder="请输入手机号" :inputBorder="false" <uni-easyinput
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"> v-model="state.model.mobile"
</uni-easyinput> type="number"
</uni-forms-item> placeholder="请输入手机号"
<uni-forms-item name="areaName" label="省市区" @tap="state.showRegion = true" class="form-item"> :inputBorder="false"
<uni-easyinput v-model="state.model.areaName" disabled :inputBorder="false" placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
:styles="{ disableColor: '#fff', color: '#333' }" >
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal" </uni-easyinput>
placeholder="请选择省市区"> </uni-forms-item>
<template v-slot:right> <uni-forms-item
<uni-icons type="right" /> name="areaName"
</template> label="省市区"
</uni-easyinput> @tap="state.showRegion = true"
</uni-forms-item> class="form-item"
<uni-forms-item name="detailAddress" label="详细地址" :formItemStyle="{ alignItems: 'flex-start' }" >
:labelStyle="{ lineHeight: '5em' }" class="textarea-item"> <uni-easyinput
<uni-easyinput :inputBorder="false" type="textarea" v-model="state.model.detailAddress" v-model="state.model.areaName"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal" disabled
placeholder="请输入详细地址" clearable /> :inputBorder="false"
</uni-forms-item> :styles="{ disableColor: '#fff', color: '#333' }"
</view> placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
<view class="ss-m-y-20 bg-white ss-p-x-30 ss-flex ss-row-between ss-col-center default-box"> placeholder="请选择省市区"
<view class="default-box-title"> 设为默认地址 </view> >
<su-switch style="transform: scale(0.8)" v-model="state.model.defaultStatus" /> <template v-slot:right>
</view> <uni-icons type="right" />
</uni-forms> </template>
<su-fixed bottom :opacity="false" bg="" placeholder :noFixed="false" :index="10"> </uni-easyinput>
<view class="footer-box ss-flex-col ss-row-between ss-p-20"> </uni-forms-item>
<view class="ss-m-b-20"> <uni-forms-item
name="detailAddress"
label="详细地址"
:formItemStyle="{ alignItems: 'flex-start' }"
:labelStyle="{ lineHeight: '5em' }"
class="textarea-item"
>
<uni-easyinput
:inputBorder="false"
type="textarea"
v-model="state.model.detailAddress"
placeholderStyle="color:#BBBBBB;font-size:30rpx;font-weight:400;line-height:normal"
placeholder="请输入详细地址"
clearable
/>
</uni-forms-item>
</view>
<view class="ss-m-y-20 bg-white ss-p-x-30 ss-flex ss-row-between ss-col-center default-box">
<view class="default-box-title"> 设为默认地址 </view>
<su-switch style="transform: scale(0.8)" v-model="state.model.defaultStatus" />
</view>
</uni-forms>
<su-fixed bottom :opacity="false" bg="" placeholder :noFixed="false" :index="10">
<view class="footer-box ss-flex-col ss-row-between ss-p-20">
<view class="ss-m-b-20">
<button class="ss-reset-button save-btn ui-Shadow-Main" @tap="onSave"></button> <button class="ss-reset-button save-btn ui-Shadow-Main" @tap="onSave"></button>
</view> </view>
<button v-if="state.model.id" class="ss-reset-button cancel-btn" @tap="onDelete"> <button v-if="state.model.id" class="ss-reset-button cancel-btn" @tap="onDelete">
删除 删除
</button> </button>
</view> </view>
</su-fixed> </su-fixed>
<!-- 省市区弹窗 --> <!-- 省市区弹窗 -->
<su-region-picker :show="state.showRegion" @cancel="state.showRegion = false" @confirm="onRegionConfirm" /> <su-region-picker
</s-layout> :show="state.showRegion"
@cancel="state.showRegion = false"
@confirm="onRegionConfirm"
/>
</s-layout>
</template> </template>
<script setup> <script setup>
import { ref, reactive, unref } from 'vue'; import { ref, reactive, unref } from 'vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import _ from 'lodash'; import _ from 'lodash-es';
import { mobile } from '@/sheep/validate/form'; import { mobile } from '@/sheep/validate/form';
import AreaApi from '@/sheep/api/system/area'; import AreaApi from '@/sheep/api/system/area';
import AddressApi from '@/sheep/api/member/address'; import AddressApi from '@/sheep/api/member/address';
const addressFormRef = ref(null); const addressFormRef = ref(null);
const state = reactive({ const state = reactive({
showRegion: false, showRegion: false,
model: { model: {
name: '', name: '',
mobile: '', mobile: '',
detailAddress: '', detailAddress: '',
defaultStatus: false, defaultStatus: false,
areaName: '', areaName: '',
}, },
rules: {}, rules: {},
}); });
const rules = { const rules = {
name: { name: {
@ -85,93 +125,99 @@
}, },
mobile, mobile,
detailAddress: { detailAddress: {
rules: [{ rules: [
required: true, {
errorMessage: '请输入详细地址', required: true,
}] errorMessage: '请输入详细地址',
},
],
}, },
areaName: { areaName: {
rules: [{ rules: [
required: true, {
errorMessage: '请选择您的位置' required: true,
}] errorMessage: '请选择您的位置',
},
],
}, },
}; };
// //
const onRegionConfirm = (e) => { const onRegionConfirm = (e) => {
state.model.areaName = `${e.province_name} ${e.city_name} ${e.district_name}` state.model.areaName = `${e.province_name} ${e.city_name} ${e.district_name}`;
state.model.areaId = e.district_id; state.model.areaId = e.district_id;
state.showRegion = false; state.showRegion = false;
}; };
// //
const getAreaData = () => { const getAreaData = () => {
if (_.isEmpty(uni.getStorageSync('areaData'))) { if (_.isEmpty(uni.getStorageSync('areaData'))) {
AreaApi.getAreaTree().then((res) => { AreaApi.getAreaTree().then((res) => {
if (res.code === 0) { if (res.code === 0) {
uni.setStorageSync('areaData', res.data); uni.setStorageSync('areaData', res.data);
} }
}); });
} }
}; };
// //
const onSave = async () => { const onSave = async () => {
// //
const validate = await unref(addressFormRef) const validate = await unref(addressFormRef)
.validate() .validate()
.catch((error) => { .catch((error) => {
console.log('error: ', error); console.log('error: ', error);
}); });
if (!validate) { if (!validate) {
return; return;
} }
// //
const formData = { const formData = {
...state.model ...state.model,
};
const { code } =
state.model.id > 0
? await AddressApi.updateAddress(formData)
: await AddressApi.createAddress(formData);
if (code === 0) {
sheep.$router.back();
} }
const {code } = state.model.id > 0 ? await AddressApi.updateAddress(formData) };
: await AddressApi.createAddress(formData);
if (code === 0) {
sheep.$router.back();
}
};
// //
const onDelete = () => { const onDelete = () => {
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '确认删除此收货地址吗?', content: '确认删除此收货地址吗?',
success: async function(res) { success: async function (res) {
if (!res.confirm) { if (!res.confirm) {
return; return;
} }
const { code } = await AddressApi.deleteAddress(state.model.id); const { code } = await AddressApi.deleteAddress(state.model.id);
if (code === 0) { if (code === 0) {
sheep.$router.back(); sheep.$router.back();
} }
}, },
}); });
}; };
onLoad(async (options) => { onLoad(async (options) => {
// //
getAreaData(); getAreaData();
// id // id
if (options.id) { if (options.id) {
let { code, data} = await AddressApi.getAddress(options.id); let { code, data } = await AddressApi.getAddress(options.id);
if (code !== 0) { if (code !== 0) {
return; return;
} }
state.model = data; state.model = data;
} }
// //
if (options.data) { if (options.data) {
let data = JSON.parse(options.data); let data = JSON.parse(options.data);
const areaData = uni.getStorageSync('areaData'); const areaData = uni.getStorageSync('areaData');
const findAreaByName = (areas, name) => areas.find(item => item.name === name); const findAreaByName = (areas, name) => areas.find((item) => item.name === name);
let provinceObj = findAreaByName(areaData, data.province_name); let provinceObj = findAreaByName(areaData, data.province_name);
let cityObj = provinceObj ? findAreaByName(provinceObj.children, data.city_name) : undefined; let cityObj = provinceObj ? findAreaByName(provinceObj.children, data.city_name) : undefined;
@ -181,77 +227,79 @@
state.model = { state.model = {
...state.model, ...state.model,
areaId, areaId,
areaName: [data.province_name, data.city_name, data.district_name].filter(Boolean).join(" "), areaName: [data.province_name, data.city_name, data.district_name]
.filter(Boolean)
.join(' '),
defaultStatus: false, defaultStatus: false,
detailAddress: data.address, detailAddress: data.address,
mobile: data.mobile, mobile: data.mobile,
name: data.consignee, name: data.consignee,
}; };
} }
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep() { :deep() {
.uni-forms-item__label .label-text { .uni-forms-item__label .label-text {
font-size: 28rpx !important; font-size: 28rpx !important;
color: #333333 !important; color: #333333 !important;
line-height: normal !important; line-height: normal !important;
} }
.uni-easyinput__content-input { .uni-easyinput__content-input {
font-size: 28rpx !important; font-size: 28rpx !important;
color: #333333 !important; color: #333333 !important;
line-height: normal !important; line-height: normal !important;
padding-left: 0 !important; padding-left: 0 !important;
} }
.uni-easyinput__content-textarea { .uni-easyinput__content-textarea {
font-size: 28rpx !important; font-size: 28rpx !important;
color: #333333 !important; color: #333333 !important;
line-height: normal !important; line-height: normal !important;
margin-top: 8rpx !important; margin-top: 8rpx !important;
} }
.uni-icons { .uni-icons {
font-size: 40rpx !important; font-size: 40rpx !important;
} }
.is-textarea-icon { .is-textarea-icon {
margin-top: 22rpx; margin-top: 22rpx;
} }
.is-disabled { .is-disabled {
color: #333333; color: #333333;
} }
} }
.default-box { .default-box {
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
height: 100rpx; height: 100rpx;
.default-box-title { .default-box-title {
font-size: 28rpx; font-size: 28rpx;
color: #333333; color: #333333;
line-height: normal; line-height: normal;
} }
} }
.footer-box { .footer-box {
.save-btn { .save-btn {
width: 710rpx; width: 710rpx;
height: 80rpx; height: 80rpx;
border-radius: 40rpx; border-radius: 40rpx;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient)); background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: $white; color: $white;
} }
.cancel-btn { .cancel-btn {
width: 710rpx; width: 710rpx;
height: 80rpx; height: 80rpx;
border-radius: 40rpx; border-radius: 40rpx;
background: var(--ui-BG); background: var(--ui-BG);
} }
} }
</style> </style>

View File

@ -1,143 +1,166 @@
<!-- 收件地址列表 --> <!-- 收件地址列表 -->
<template> <template>
<s-layout title="收货地址" :bgStyle="{ color: '#FFF' }"> <s-layout title="收货地址" :bgStyle="{ color: '#FFF' }">
<view v-if="state.list.length"> <view v-if="state.list.length">
<s-address-item hasBorderBottom v-for="item in state.list" :key="item.id" :item="item" <s-address-item
@tap="onSelect(item)" /> hasBorderBottom
</view> v-for="item in state.list"
:key="item.id"
:item="item"
@tap="onSelect(item)"
/>
</view>
<su-fixed bottom placeholder> <su-fixed bottom placeholder>
<view class="footer-box ss-flex ss-row-between ss-p-20"> <view class="footer-box ss-flex ss-row-between ss-p-20">
<!-- 微信小程序和微信H5 --> <!-- 微信小程序和微信H5 -->
<button v-if="['WechatMiniProgram', 'WechatOfficialAccount'].includes(sheep.$platform.name)" <button
@tap="importWechatAddress" v-if="['WechatMiniProgram', 'WechatOfficialAccount'].includes(sheep.$platform.name)"
class="border ss-reset-button sync-wxaddress ss-m-20 ss-flex ss-row-center ss-col-center"> @tap="importWechatAddress"
<text class="cicon-weixin ss-p-r-10" style="color: #09bb07; font-size: 40rpx"></text> class="border ss-reset-button sync-wxaddress ss-m-20 ss-flex ss-row-center ss-col-center"
导入微信地址 >
</button> <text class="cicon-weixin ss-p-r-10" style="color: #09bb07; font-size: 40rpx"></text>
<button class="add-btn ss-reset-button ui-Shadow-Main" 导入微信地址
@tap="sheep.$router.go('/pages/user/address/edit')"> </button>
新增收货地址 <button
</button> class="add-btn ss-reset-button ui-Shadow-Main"
</view> @tap="sheep.$router.go('/pages/user/address/edit')"
</su-fixed> >
<s-empty v-if="state.list.length === 0 && !state.loading" text="暂无收货地址" icon="/static/data-empty.png" /> 新增收货地址
</s-layout> </button>
</view>
</su-fixed>
<s-empty
v-if="state.list.length === 0 && !state.loading"
text="暂无收货地址"
icon="/static/data-empty.png"
/>
</s-layout>
</template> </template>
<script setup> <script setup>
import { reactive, onBeforeMount } from 'vue'; import { onBeforeMount, reactive } from 'vue';
import { onShow } from '@dcloudio/uni-app'; import { onShow, onLoad } from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import AreaApi from '@/sheep/api/system/area'; import AreaApi from '@/sheep/api/system/area';
import AddressApi from '@/sheep/api/member/address'; import AddressApi from '@/sheep/api/member/address';
const state = reactive({ const state = reactive({
list: [], // list: [], //
loading: true, loading: true,
}); openType: '', //
});
// //
const onSelect = (addressInfo) => { const onSelect = (addressInfo) => {
uni.$emit('SELECT_ADDRESS', { if (state.openType !== 'select'){ //
addressInfo, return
}); }
sheep.$router.back(); uni.$emit('SELECT_ADDRESS', {
}; addressInfo,
});
sheep.$router.back();
};
// //
// TODO // TODO
function importWechatAddress() { function importWechatAddress() {
let wechatAddress = {}; let wechatAddress = {};
// #ifdef MP // #ifdef MP
uni.chooseAddress({ uni.chooseAddress({
success: (res) => { success: (res) => {
wechatAddress = { wechatAddress = {
consignee: res.userName, consignee: res.userName,
mobile: res.telNumber, mobile: res.telNumber,
province_name: res.provinceName, province_name: res.provinceName,
city_name: res.cityName, city_name: res.cityName,
district_name: res.countyName, district_name: res.countyName,
address: res.detailInfo, address: res.detailInfo,
region: '', region: '',
is_default: false, is_default: false,
}; };
if (!isEmpty(wechatAddress)) { if (!isEmpty(wechatAddress)) {
sheep.$router.go('/pages/user/address/edit', { sheep.$router.go('/pages/user/address/edit', {
data: JSON.stringify(wechatAddress), data: JSON.stringify(wechatAddress),
}); });
} }
}, },
fail: (err) => { fail: (err) => {
console.log('%cuni.chooseAddress,调用失败', 'color:green;background:yellow'); console.log('%cuni.chooseAddress,调用失败', 'color:green;background:yellow');
}, },
}); });
// #endif // #endif
// #ifdef H5 // #ifdef H5
sheep.$platform.useProvider('wechat').jssdk.openAddress({ sheep.$platform.useProvider('wechat').jssdk.openAddress({
success: (res) => { success: (res) => {
wechatAddress = { wechatAddress = {
consignee: res.userName, consignee: res.userName,
mobile: res.telNumber, mobile: res.telNumber,
province_name: res.provinceName, province_name: res.provinceName,
city_name: res.cityName, city_name: res.cityName,
district_name: res.countryName, district_name: res.countryName,
address: res.detailInfo, address: res.detailInfo,
region: '', region: '',
is_default: false, is_default: false,
}; };
if (!isEmpty(wechatAddress)) { if (!isEmpty(wechatAddress)) {
sheep.$router.go('/pages/user/address/edit', { sheep.$router.go('/pages/user/address/edit', {
data: JSON.stringify(wechatAddress), data: JSON.stringify(wechatAddress),
}); });
} }
}, },
}); });
// #endif // #endif
} }
onShow(async () => { onLoad((option) => {
state.list = (await AddressApi.getAddressList()).data; if (option.type) {
state.loading = false; state.openType = option.type;
}); }
});
onBeforeMount(() => { onShow(async () => {
if (!!uni.getStorageSync('areaData')) { state.list = (await AddressApi.getAddressList()).data;
return; state.loading = false;
} });
//
onBeforeMount(() => {
if (!!uni.getStorageSync('areaData')) {
return;
}
//
AreaApi.getAreaTree().then((res) => { AreaApi.getAreaTree().then((res) => {
if (res.code === 0) { if (res.code === 0) {
uni.setStorageSync('areaData', res.data); uni.setStorageSync('areaData', res.data);
} }
}); });
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.footer-box { .footer-box {
.add-btn { .add-btn {
flex: 1; flex: 1;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient)); background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
border-radius: 80rpx; border-radius: 80rpx;
font-size: 30rpx; font-size: 30rpx;
font-weight: 500; font-weight: 500;
line-height: 80rpx; line-height: 80rpx;
color: $white; color: $white;
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
.sync-wxaddress { .sync-wxaddress {
flex: 1; flex: 1;
line-height: 80rpx; line-height: 80rpx;
background: $white; background: $white;
border-radius: 80rpx; border-radius: 80rpx;
font-size: 30rpx; font-size: 30rpx;
font-weight: 500; font-weight: 500;
color: $dark-6; color: $dark-6;
margin-right: 18rpx; margin-right: 18rpx;
} }
} }
</style> </style>

View File

@ -5,7 +5,8 @@
<!-- 头部 --> <!-- 头部 -->
<view class="cart-header ss-flex ss-col-center ss-row-between ss-p-x-30"> <view class="cart-header ss-flex ss-col-center ss-row-between ss-p-x-30">
<view class="header-left ss-flex ss-col-center ss-font-26"> <view class="header-left ss-flex ss-col-center ss-font-26">
<text class="goods-number ui-TC-Main ss-flex">{{ state.pagination.total }}</text> 件商品
<text class="goods-number ui-TC-Main ss-flex">{{ state.pagination.total }}</text> 件商品
</view> </view>
<view class="header-right"> <view class="header-right">
<button <button
@ -77,7 +78,8 @@
<view class="footer-right"> <view class="footer-right">
<button <button
class="ss-reset-button ui-BG-Main-Gradient pay-btn ss-font-28 ui-Shadow-Main" class="ss-reset-button ui-BG-Main-Gradient pay-btn ss-font-28 ui-Shadow-Main"
@tap="onCancel"> @tap="onCancel"
>
取消收藏 取消收藏
</button> </button>
</view> </view>
@ -100,7 +102,7 @@
import sheep from '@/sheep'; import sheep from '@/sheep';
import { reactive } from 'vue'; import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import _ from 'lodash'; import _ from 'lodash-es';
import FavoriteApi from '@/sheep/api/product/favorite'; import FavoriteApi from '@/sheep/api/product/favorite';
import { resetPagination } from '@/sheep/util'; import { resetPagination } from '@/sheep/util';
@ -129,7 +131,7 @@
if (code !== 0) { if (code !== 0) {
return; return;
} }
state.pagination.list = _.concat(state.pagination.list, data.list) state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total; state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore'; state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
} }
@ -174,7 +176,7 @@
// //
function loadMore() { function loadMore() {
if (state.loadStatus === 'noMore') { if (state.loadStatus === 'noMore') {
return return;
} }
state.pagination.pageNo++; state.pagination.pageNo++;
getData(); getData();

View File

@ -81,11 +81,13 @@
</view> </view>
<view class="footer-right ss-flex"> <view class="footer-right ss-flex">
<button <button
:class="['ss-reset-button pay-btn ss-font-28 ', :class="[
{ 'ss-reset-button pay-btn ss-font-28 ',
'ui-BG-Main-Gradient': state.selectedSpuIdList.length > 0, {
'ui-Shadow-Main': state.selectedSpuIdList.length > 0 'ui-BG-Main-Gradient': state.selectedSpuIdList.length > 0,
}]" 'ui-Shadow-Main': state.selectedSpuIdList.length > 0,
},
]"
@tap="onDelete" @tap="onDelete"
> >
删除足迹 删除足迹
@ -120,9 +122,9 @@
import sheep from '@/sheep'; import sheep from '@/sheep';
import { reactive } from 'vue'; import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import _ from 'lodash'; import _ from 'lodash-es';
import SpuHistoryApi from "@/sheep/api/product/history"; import SpuHistoryApi from '@/sheep/api/product/history';
import {cloneDeep} from "@/sheep/helper/utils"; import { cloneDeep } from '@/sheep/helper/utils';
const sys_navBar = sheep.$platform.navbar; const sys_navBar = sheep.$platform.navbar;
const pagination = { const pagination = {

View File

@ -0,0 +1,275 @@
<template>
<s-layout title="选择自提门店" :bgStyle="{ color: '#FFF' }">
<view class="storeBox" ref="container">
<view class="storeBox-box" v-for="(item, index) in state.storeList" :key="index" @tap="checked(item)">
<view class="store-img">
<image :src="item.logo" class="img" />
</view>
<view class="store-cent-left">
<view class="store-name">{{ item.name }}</view>
<view class="store-address line1">
{{ item.areaName }}{{ ', ' + item.detailAddress }}
</view>
</view>
<view class="row-right ss-flex-col ss-col-center">
<view>
<!-- #ifdef H5 -->
<a class="store-phone" :href="'tel:' + item.phone">
<view class="iconfont">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</view>
</a>
<!-- #endif -->
<!-- #ifdef MP -->
<view class="store-phone" @click="call(item.phone)">
<view class="iconfont">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</view>
</view>
<!-- #endif -->
</view>
<view class="store-distance ss-flex ss-row-center" @tap.stop="showMaoLocation(item)">
<text class="addressTxt" v-if="item.distance">{{ item.distance.toFixed(2) }}</text>
<text class="addressTxt" v-else></text>
<view class="iconfont">
<view class="ss-rest-button">
<text class="_icon-forward" />
</view>
</view>
</view>
</view>
</view>
</view>
</s-layout>
</template>
<script setup>
import DeliveryApi from '@/sheep/api/trade/delivery';
import { onMounted, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import sheep from '@/sheep';
const LONGITUDE = 'user_longitude';
const LATITUDE = 'user_latitude';
const state = reactive({
loaded: false,
loading: false,
storeList: [],
system_store: {},
locationShow: false,
user_latitude: 0,
user_longitude: 0,
});
const call = (phone) => {
uni.makePhoneCall({
phoneNumber: phone,
});
};
const selfLocation = () => {
// #ifdef H5
const jsWxSdk = sheep.$platform.useProvider('wechat').jsWxSdk;
if (jsWxSdk.isWechat()) {
jsWxSdk.getLocation((res) => {
console.log(res);
state.user_latitude = res.latitude;
state.user_longitude = res.longitude;
uni.setStorageSync(LATITUDE, res.latitude);
uni.setStorageSync(LONGITUDE, res.longitude);
getList();
});
} else {
// #endif
uni.getLocation({
type: 'gcj02',
success: (res) => {
try {
state.user_latitude = res.latitude;
state.user_longitude = res.longitude;
uni.setStorageSync(LATITUDE, res.latitude);
uni.setStorageSync(LONGITUDE, res.longitude);
} catch {
}
getList();
},
complete: () => {
getList();
},
});
// #ifdef H5
}
// #endif
};
const showMaoLocation = (e) => {
// #ifdef H5
const jsWxSdk = sheep.$platform.useProvider('wechat').jsWxSdk;
if (jsWxSdk.isWechat()) {
jsWxSdk.openLocation({
latitude: Number(e.latitude),
longitude: Number(e.longitude),
name: e.name,
address: `${e.areaName}-${e.detailAddress}`
});
} else {
// #endif
uni.openLocation({
latitude: Number(e.latitude),
longitude: Number(e.longitude),
name: e.name,
address: `${e.areaName}-${e.detailAddress}`,
success: function() {
console.log('success');
},
});
// #ifdef H5
}
// #endif
};
/**
* 选中门店
*/
const checked = (addressInfo) => {
uni.$emit('SELECT_PICK_UP_INFO', {
addressInfo,
});
sheep.$router.back();
};
/**
* 获取门店列表数据
*/
const getList = async () => {
if (state.loading || state.loaded) {
return;
}
state.loading = true;
const { data, code } = await DeliveryApi.getDeliveryPickUpStoreList({
latitude: state.user_latitude,
longitude: state.user_longitude,
});
if (code !== 0) {
return;
}
state.loading = false;
state.storeList = data;
};
onMounted(() => {
if (state.user_latitude && state.user_longitude) {
getList();
} else {
selfLocation();
getList();
}
});
onLoad(() => {
try {
state.user_latitude = uni.getStorageSync(LATITUDE);
state.user_longitude = uni.getStorageSync(LONGITUDE);
} catch (e) {
// error
}
});
</script>
<style lang="scss" scoped>
.line1 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap
}
.geoPage {
position: fixed;
width: 100%;
height: 100%;
top: 0;
z-index: 10000;
}
.storeBox {
width: 100%;
background-color: #fff;
padding: 0 30rpx;
}
.storeBox-box {
width: 100%;
height: auto;
display: flex;
align-items: center;
padding: 23rpx 0;
justify-content: space-between;
border-bottom: 1px solid #eee;
}
.store-cent {
display: flex;
align-items: center;
width: 80%;
}
.store-cent-left {
//width: 45%;
flex: 2;
}
.store-img {
flex: 1;
width: 120rpx;
height: 120rpx;
border-radius: 6rpx;
margin-right: 22rpx;
}
.store-img .img {
width: 100%;
height: 100%;
}
.store-name {
color: #282828;
font-size: 30rpx;
margin-bottom: 22rpx;
font-weight: 800;
}
.store-address {
color: #666666;
font-size: 24rpx;
}
.store-phone {
width: 50rpx;
height: 50rpx;
color: #fff;
border-radius: 50%;
display: block;
text-align: center;
line-height: 48rpx;
background-color: #e83323;
margin-bottom: 22rpx;
text-decoration: none;
}
.store-distance {
font-size: 22rpx;
color: #e83323;
}
.iconfont {
font-size: 20rpx;
}
.row-right {
flex: 2;
//display: flex;
//flex-direction: column;
//align-items: flex-end;
//width: 33.5%;
}
</style>

View File

@ -26,7 +26,8 @@
<button <button
class="ss-reset-button avatar-action-btn" class="ss-reset-button avatar-action-btn"
open-type="chooseAvatar" open-type="chooseAvatar"
@chooseavatar="onChooseAvatar"> @chooseavatar="onChooseAvatar"
>
修改 修改
</button> </button>
<!-- #endif --> <!-- #endif -->
@ -154,10 +155,7 @@
</view> </view>
<view class="ss-flex ss-col-center"> <view class="ss-flex ss-col-center">
<view class="info ss-flex ss-col-center" v-if="state.thirdInfo"> <view class="info ss-flex ss-col-center" v-if="state.thirdInfo">
<image <image class="avatar ss-m-r-20" :src="sheep.$url.cdn(state.thirdInfo.avatar)" />
class="avatar ss-m-r-20"
:src="sheep.$url.cdn(state.thirdInfo.avatar)"
/>
<text class="name">{{ state.thirdInfo.nickname }}</text> <text class="name">{{ state.thirdInfo.nickname }}</text>
</view> </view>
<view class="bind-box ss-m-l-20"> <view class="bind-box ss-m-l-20">
@ -185,7 +183,7 @@
<script setup> <script setup>
import { computed, reactive, onBeforeMount } from 'vue'; import { computed, reactive, onBeforeMount } from 'vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { clone } from 'lodash'; import { clone } from 'lodash-es';
import { showAuthModal } from '@/sheep/hooks/useModal'; import { showAuthModal } from '@/sheep/hooks/useModal';
import FileApi from '@/sheep/api/infra/file'; import FileApi from '@/sheep/api/infra/file';
import UserApi from '@/sheep/api/member/user'; import UserApi from '@/sheep/api/member/user';
@ -198,14 +196,15 @@
const placeholderStyle = 'color:#BBBBBB;font-size:28rpx;line-height:normal'; const placeholderStyle = 'color:#BBBBBB;font-size:28rpx;line-height:normal';
const sexRadioMap = [{ const sexRadioMap = [
{
name: '男', name: '男',
value: '1', value: '1',
}, },
{ {
name: '女', name: '女',
value: '2', value: '2',
} },
]; ];
const userInfo = computed(() => sheep.$store('user').userInfo); const userInfo = computed(() => sheep.$store('user').userInfo);
@ -279,7 +278,7 @@
// //
async function onSubmit() { async function onSubmit() {
const { code } = await UserApi.updateUser({ const { code } = await UserApi.updateUser({
avatar: state.model.avatar, avatar: state.model.avatar,
nickname: state.model.nickname, nickname: state.model.nickname,
sex: state.model.sex, sex: state.model.sex,

View File

@ -13,7 +13,9 @@
/> />
</view> </view>
<view class="ss-flex ss-row-between ss-col-center ss-m-t-64"> <view class="ss-flex ss-row-between ss-col-center ss-m-t-64">
<view class="money-num">{{ state.showMoney ? fen2yuan(userWallet.balance) : '*****' }}</view> <view class="money-num">{{
state.showMoney ? fen2yuan(userWallet.balance) : '*****'
}}</view>
<button class="ss-reset-button topup-btn" @tap="sheep.$router.go('/pages/pay/recharge')"> <button class="ss-reset-button topup-btn" @tap="sheep.$router.go('/pages/pay/recharge')">
充值 充值
</button> </button>
@ -24,7 +26,12 @@
<su-sticky> <su-sticky>
<!-- 统计 --> <!-- 统计 -->
<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between"> <view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
<uni-datetime-picker v-model="state.data" type="daterange" @change="onChangeTime" :end="state.today"> <uni-datetime-picker
v-model="state.data"
type="daterange"
@change="onChangeTime"
:end="state.today"
>
<button class="ss-reset-button date-btn"> <button class="ss-reset-button date-btn">
<text>{{ dateFilterText }}</text> <text>{{ dateFilterText }}</text>
<text class="cicon-drop-down ss-seldate-icon"></text> <text class="cicon-drop-down ss-seldate-icon"></text>
@ -82,7 +89,7 @@
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import sheep from '@/sheep'; import sheep from '@/sheep';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash-es';
import PayWalletApi from '@/sheep/api/pay/wallet'; import PayWalletApi from '@/sheep/api/pay/wallet';
import { fen2yuan } from '@/sheep/hooks/useGoods'; import { fen2yuan } from '@/sheep/hooks/useGoods';
import { resetPagination } from '@/sheep/util'; import { resetPagination } from '@/sheep/util';
@ -98,7 +105,7 @@
list: [], list: [],
total: 0, total: 0,
pageNo: 1, pageNo: 1,
pageSize: 8 pageSize: 8,
}, },
summary: { summary: {
totalIncome: 0, totalIncome: 0,
@ -155,7 +162,7 @@
// //
async function getSummary() { async function getSummary() {
const { data, code } = await PayWalletApi.getWalletTransactionSummary({ const { data, code } = await PayWalletApi.getWalletTransactionSummary({
'createTime': [state.date[0] + ' 00:00:00', state.date[1] + ' 23:59:59'], createTime: [state.date[0] + ' 00:00:00', state.date[1] + ' 23:59:59'],
}); });
if (code !== 0) { if (code !== 0) {
return; return;
@ -226,8 +233,7 @@
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
background: v-bind(headerBg) background: v-bind(headerBg) no-repeat;
no-repeat;
pointer-events: none; pointer-events: none;
} }

View File

@ -24,19 +24,24 @@
<su-sticky :customNavHeight="sys_navBar"> <su-sticky :customNavHeight="sys_navBar">
<!-- 统计 --> <!-- 统计 -->
<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between"> <view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
<uni-datetime-picker v-model="state.date" type="daterange" @change="onChangeTime" :end="state.today"> <uni-datetime-picker
<button class="ss-reset-button date-btn"> v-model="state.date"
<text>{{ dateFilterText }}</text> type="daterange"
<text class="cicon-drop-down ss-seldate-icon"></text> @change="onChangeTime"
</button> :end="state.today"
</uni-datetime-picker> >
<button class="ss-reset-button date-btn">
<text>{{ dateFilterText }}</text>
<text class="cicon-drop-down ss-seldate-icon"></text>
</button>
</uni-datetime-picker>
<!-- TODO 芋艿优化 --> <!-- TODO 芋艿优化 -->
<!-- <view class="total-box">--> <!-- <view class="total-box">-->
<!-- <view class="ss-m-b-10">总收入{{ state.pagination.income }}</view>--> <!-- <view class="ss-m-b-10">总收入{{ state.pagination.income }}</view>-->
<!-- <view>总支出{{ -state.pagination.expense }}</view>--> <!-- <view>总支出{{ -state.pagination.expense }}</view>-->
<!-- </view>--> <!-- </view>-->
</view> </view>
<su-tabs <su-tabs
:list="tabMaps" :list="tabMaps"
@change="onChange" @change="onChange"
@ -83,7 +88,7 @@
import sheep from '@/sheep'; import sheep from '@/sheep';
import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import { computed, reactive } from 'vue'; import { computed, reactive } from 'vue';
import _ from 'lodash'; import _ from 'lodash-es';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import PointApi from '@/sheep/api/member/point'; import PointApi from '@/sheep/api/member/point';
import { resetPagination } from '@/sheep/util'; import { resetPagination } from '@/sheep/util';
@ -274,4 +279,4 @@
} }
} }
} }
</style> </style>

View File

@ -1,14 +0,0 @@
import request from '@/sheep/request';
// TODO 芋艿:暂不支持 socket 聊天
export default {
// 获取聊天token
unifiedToken: () =>
request({
url: 'unifiedToken',
custom: {
showError: false,
showLoading: false,
},
}),
};

View File

@ -7,7 +7,25 @@ const DeliveryApi = {
url: `/trade/delivery/express/list`, url: `/trade/delivery/express/list`,
method: 'get', method: 'get',
}); });
} },
// 获得自提门店列表
getDeliveryPickUpStoreList: (params) => {
return request({
url: `/trade/delivery/pick-up-store/list`,
method: 'GET',
params,
});
},
// 获得自提门店
getDeliveryPickUpStore: (id) => {
return request({
url: `/trade/delivery/pick-up-store/get`,
method: 'GET',
params: {
id,
},
});
},
}; };
export default DeliveryApi; export default DeliveryApi;

View File

@ -1,4 +1,5 @@
import request from '@/sheep/request'; import request from '@/sheep/request';
import { isEmpty } from '@/sheep/helper/utils';
const OrderApi = { const OrderApi = {
// 计算订单信息 // 计算订单信息
@ -13,6 +14,15 @@ const OrderApi = {
if (!(data.addressId > 0)) { if (!(data.addressId > 0)) {
delete data2.addressId; delete data2.addressId;
} }
if (!(data.pickUpStoreId > 0)) {
delete data2.pickUpStoreId;
}
if (isEmpty(data.receiverName)) {
delete data2.receiverName;
}
if (isEmpty(data.receiverMobile)) {
delete data2.receiverMobile;
}
if (!(data.combinationActivityId > 0)) { if (!(data.combinationActivityId > 0)) {
delete data2.combinationActivityId; delete data2.combinationActivityId;
} }

View File

@ -18,9 +18,7 @@
<view class="address-text"> <view class="address-text">
{{ props.item.detailAddress }} {{ props.item.detailAddress }}
</view> </view>
<view class="person-text"> <view class="person-text"> {{ props.item.name }} {{ props.item.mobile }} </view>
{{ props.item.name }} {{ props.item.mobile }}
</view>
</view> </view>
<view v-else> <view v-else>
<view class="address-text ss-m-b-10">请选择收货地址</view> <view class="address-text ss-m-b-10">请选择收货地址</view>
@ -47,7 +45,7 @@
* @slot - 默认插槽 * @slot - 默认插槽
*/ */
import sheep from '@/sheep'; import sheep from '@/sheep';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
const props = defineProps({ const props = defineProps({
item: { item: {
type: Object, type: Object,

View File

@ -9,7 +9,7 @@
<!-- 基础组件列表导航 --> <!-- 基础组件列表导航 -->
<s-menu-list v-if="type === 'MenuList'" :data="data" /> <s-menu-list v-if="type === 'MenuList'" :data="data" />
<!-- 基础组件宫格导航 --> <!-- 基础组件宫格导航 -->
<s-menu-grid v-if="type === 'MenuGrid'" :data="data" /> <s-menu-grid v-if="type === 'MenuGrid'" :data="data" :styles="styles" />
<!-- 基础组件弹窗广告 --> <!-- 基础组件弹窗广告 -->
<s-popup-image v-if="type === 'Popover'" :data="data" /> <s-popup-image v-if="type === 'Popover'" :data="data" />
<!-- 基础组件悬浮按钮 --> <!-- 基础组件悬浮按钮 -->
@ -47,13 +47,13 @@
<s-richtext-block v-if="type === 'PromotionArticle'" :data="data" :styles="styles" /> <s-richtext-block v-if="type === 'PromotionArticle'" :data="data" :styles="styles" />
<!-- 用户组件用户卡片 --> <!-- 用户组件用户卡片 -->
<s-user-card v-if="type === 'UserCard'" /> <s-user-card v-if="type === 'UserCard'" :data="data" :styles="styles" />
<!-- 用户组件用户订单 --> <!-- 用户组件用户订单 -->
<s-order-card v-if="type === 'UserOrder'" :data="data" /> <s-order-card v-if="type === 'UserOrder'" :data="data" :styles="styles" />
<!-- 用户组件用户资产 --> <!-- 用户组件用户资产 -->
<s-wallet-card v-if="type === 'UserWallet'" /> <s-wallet-card v-if="type === 'UserWallet'" :data="data" :styles="styles" />
<!-- 用户组件用户卡券 --> <!-- 用户组件用户卡券 -->
<s-coupon-card v-if="type === 'UserCoupon'" /> <s-coupon-card v-if="type === 'UserCoupon'" :data="data" :styles="styles" />
</view> </view>
</template> </template>

View File

@ -1,152 +1,176 @@
<!-- 装修营销组件优惠券 --> <!-- 装修营销组件优惠券 -->
<template> <template>
<scroll-view class="scroll-box" scroll-x scroll-anchoring> <scroll-view class="scroll-box" scroll-x scroll-anchoring :style="[bgStyle, { marginLeft: `${data.space}px` }]">
<view class="coupon-box ss-flex"> <view class="coupon-box ss-flex" :style="couponList.length === 2 ? couponBoxStyleTwo : couponBoxStyleNormal">
<view <view class="coupon-item" :style="[couponBg, { marginLeft: `${data.space}px` }]"
class="coupon-item" v-for="(item, index) in couponList" :key="index">
:style="[couponBg, { marginLeft: `${data.space}px` }]" <su-coupon :size="SIZE_LIST[columns - 1]" :textColor="data.textColor" background="" :couponId="item.id"
v-for="(item, index) in couponList" :title="item.name" :type="formatCouponDiscountType(item)" :value="formatCouponDiscountValue(item)"
:key="index" :sellBy="formatValidityType(item)">
> <template v-slot:btn>
<su-coupon <!-- 两列时领取按钮坚排 -->
:size="SIZE_LIST[columns - 1]" <button v-if="columns === 2" @click.stop="onGetCoupon(item.id)"
:textColor="data.textColor" class="ss-reset-button card-btn vertical" :style="[btnStyles]">
background="" <view class="btn-text">立即领取</view>
:couponId="item.id" </button>
:title="item.name" <button v-else class="ss-reset-button card-btn" :style="[btnStyles]"
:type="formatCouponDiscountType(item)" @click.stop="onGetCoupon(item.id)">
:value="formatCouponDiscountValue(item)" 立即领取
:sellBy="formatValidityType(item)" </button>
> </template>
<template v-slot:btn> </su-coupon>
<!-- 两列时领取按钮坚排 --> </view>
<button </view>
v-if="columns === 2" </scroll-view>
@click.stop="onGetCoupon(item.id)"
class="ss-reset-button card-btn vertical"
:style="[btnStyles]"
>
<view class="btn-text">立即领取</view>
</button>
<button
v-else
class="ss-reset-button card-btn"
:style="[btnStyles]"
@click.stop="onGetCoupon(item.id)"
>
立即领取
</button>
</template>
</su-coupon>
</view>
</view>
</scroll-view>
</template> </template>
<script setup> <script setup>
import sheep from '@/sheep'; import sheep from '@/sheep';
import CouponApi from '@/sheep/api/promotion/coupon'; import CouponApi from '@/sheep/api/promotion/coupon';
import { ref, onMounted } from 'vue'; import {
import { CouponTemplateValidityTypeEnum, PromotionDiscountTypeEnum } from "@/sheep/util/const"; ref,
import { floatToFixed2, formatDate } from "@/sheep/util"; onMounted,
computed
} from 'vue';
import {
CouponTemplateValidityTypeEnum,
PromotionDiscountTypeEnum
} from "@/sheep/util/const";
import {
floatToFixed2,
formatDate
} from "@/sheep/util";
const props = defineProps({ const props = defineProps({
data: { data: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
styles: { styles: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
}); });
const { columns, button } = props.data; const {
const SIZE_LIST = ['lg', 'md', 'xs'] columns,
const couponBg = { button
background: `url(${sheep.$url.cdn(props.data.bgImg)}) no-repeat top center / 100% 100%`, } = props.data;
}; const SIZE_LIST = ['lg', 'md', 'xs']
const btnStyles = { const couponBg = {
background: button.bgColor, background: `url(${sheep.$url.cdn(props.data.bgImg)}) no-repeat top center / 100% 100%`,
color: button.color, };
}; const btnStyles = {
background: button.bgColor,
color: button.color,
};
//
const couponBoxStyleNormal = {
'display': 'flex',
'justify-content': 'space-between'
};
//
const couponBoxStyleTwo = {
'display': 'flex',
'justify-content': 'space-around'
};
//
const bgStyle = computed(() => {
// props.styles
const {
bgType,
bgImg,
bgColor
} = props.styles;
// // bgType
const formatCouponDiscountType = (coupon) => { return {
if(coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) { background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
return 'reduce' };
} });
if(coupon.discountType === PromotionDiscountTypeEnum.PERCENT.type) { //
return 'percent' const formatCouponDiscountType = (coupon) => {
} if (coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
return `未知【${coupon.discountType}` return 'reduce'
} }
if (coupon.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
return 'percent'
}
return `未知【${coupon.discountType}`
}
// //
const formatCouponDiscountValue = (coupon) => { const formatCouponDiscountValue = (coupon) => {
if(coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) { if (coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
return floatToFixed2(coupon.discountPrice) return floatToFixed2(coupon.discountPrice)
} }
if(coupon.discountType === PromotionDiscountTypeEnum.PERCENT.type) { if (coupon.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
return coupon.discountPercent return coupon.discountPercent
} }
return `未知【${coupon.discountType}` return `未知【${coupon.discountType}`
} }
// //
const formatValidityType = (row) => { const formatValidityType = (row) => {
if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) { if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) {
return `${formatDate(row.validStartTime)}${formatDate(row.validEndTime)}` return `${formatDate(row.validStartTime)}${formatDate(row.validEndTime)}`
} }
if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) { if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) {
return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用` return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`
} }
return '未知【' + row.validityType + '】' return '未知【' + row.validityType + '】'
} }
const couponList = ref([]); const couponList = ref([]);
// //
async function onGetCoupon(id) { async function onGetCoupon(id) {
const { error, msg } = await CouponApi.takeCoupon(id); const {
if (error === 0) { error,
uni.showToast({ msg
title: msg, } = await CouponApi.takeCoupon(id);
icon: 'none', if (error === 0) {
}); uni.showToast({
return title: msg,
} icon: 'none',
await getCouponTemplateList() });
} return
const getCouponTemplateList = async () => { }
const { data } = await CouponApi.getCouponTemplateListByIds(props.data.couponIds.join(',')); await getCouponTemplateList()
couponList.value = data; }
} const getCouponTemplateList = async () => {
onMounted(() => { const {
getCouponTemplateList() data
}); } = await CouponApi.getCouponTemplateListByIds(props.data.couponIds.join(','));
couponList.value = data;
}
onMounted(() => {
getCouponTemplateList()
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.card-btn { .card-btn {
width: 140rpx; width: 140rpx;
height: 50rpx; height: 50rpx;
border-radius: 25rpx; border-radius: 25rpx;
font-size: 24rpx; font-size: 24rpx;
line-height: 50rpx; line-height: 50rpx;
&.vertical {
width: 50rpx;
height: 140rpx;
margin: auto 20rpx auto 0;
.btn-text { &.vertical {
font-size: 24rpx; width: 50rpx;
text-align: center; height: 140rpx;
writing-mode: vertical-lr; margin: auto 20rpx auto 0;
}
} .btn-text {
} font-size: 24rpx;
.coupon-item { text-align: center;
&:nth-of-type(1) { writing-mode: vertical-lr;
margin-left: 0 !important; }
} }
} }
</style>
.coupon-item {
&:nth-of-type(1) {
margin-left: 0 !important;
}
}
</style>

View File

@ -1,6 +1,6 @@
<!-- 装修用户组件用户卡券 --> <!-- 装修用户组件用户卡券 -->
<template> <template>
<view class="ss-coupon-menu-wrap ss-flex ss-col-center"> <view class="ss-coupon-menu-wrap ss-flex ss-col-center" :style="[bgStyle, { marginLeft: `${data.space}px` }]">
<view class="menu-item ss-flex-col ss-row-center ss-col-center" v-for="item in props.list" :key="item.title" <view class="menu-item ss-flex-col ss-row-center ss-col-center" v-for="item in props.list" :key="item.title"
@tap="sheep.$router.go(item.path, { type: item.type })" @tap="sheep.$router.go(item.path, { type: item.type })"
:class="item.type === 'all' ? 'menu-wallet' : 'ss-flex-1'"> :class="item.type === 'all' ? 'menu-wallet' : 'ss-flex-1'">
@ -15,6 +15,7 @@
* 装修组件 - 优惠券菜单 * 装修组件 - 优惠券菜单
*/ */
import sheep from '@/sheep'; import sheep from '@/sheep';
import { computed } from 'vue';
// //
const props = defineProps({ const props = defineProps({
@ -52,6 +53,28 @@
]; ];
}, },
}, },
//
data: {
type: Object,
default: () => ({}),
},
//
styles: {
type: Object,
default: () => ({}),
},
});
//
const bgStyle = computed(() => {
// props.styles
const { bgType, bgImg, bgColor } = props.styles;
// bgType
return {
background: bgType === 'img'
? `url(${bgImg}) no-repeat top center / 100% 100%`
: bgColor
};
}); });
</script> </script>

View File

@ -1,24 +1,19 @@
<!-- 订单确认的优惠劵选择弹窗 --> <!-- 订单确认的优惠劵选择弹窗 -->
<template> <template>
<su-popup <su-popup :show="show" type="bottom" round="20" @close="emits('close')" showClose backgroundColor="#f2f2f2">
:show="show"
type="bottom"
round="20"
@close="emits('close')"
showClose
backgroundColor="#f2f2f2"
>
<view class="model-box"> <view class="model-box">
<view class="title ss-m-t-16 ss-m-l-20 ss-flex">优惠券</view> <view class="title ss-m-t-16 ss-m-l-20 ss-flex">优惠券</view>
<scroll-view <scroll-view class="model-content" scroll-y :scroll-with-animation="false" :enable-back-to-top="true">
class="model-content" <!--可使用的优惠券区域-->
scroll-y
:scroll-with-animation="false"
:enable-back-to-top="true"
>
<view class="subtitle ss-m-l-20">可使用优惠券</view> <view class="subtitle ss-m-l-20">可使用优惠券</view>
<view v-for="(item, index) in state.couponInfo" :key="index"> <view v-for="(item, index) in state.couponInfo.filter(coupon => coupon.match)" :key="index">
<s-coupon-list :data="item" type="user" :disabled="false"> <s-coupon-list :data="item" type="user" :disabled="false">
<template v-slot:reason>
<view class="ss-flex ss-m-t-24">
<view class="reason-title">可用原因</view>
<view class="reason-desc">{{ item.description || '已达到使用门槛' }}</view>
</view>
</template>
<template #default> <template #default>
<label class="ss-flex ss-col-center" @tap="radioChange(item.id)"> <label class="ss-flex ss-col-center" @tap="radioChange(item.id)">
<radio <radio
@ -31,19 +26,18 @@
</template> </template>
</s-coupon-list> </s-coupon-list>
</view> </view>
<!-- TODO 芋艿未来接口需要支持下 <!--不可使用的优惠券区域-->
<view class="subtitle ss-m-t-40 ss-m-l-20">不可使用优惠券</view> <view class="subtitle ss-m-t-40 ss-m-l-20">不可使用优惠券</view>
<view v-for="item in state.couponInfo.cannot_use" :key="item.id"> <view v-for="item in state.couponInfo.filter(coupon => !coupon.match)" :key="item.id">
<s-coupon-list :data="item" type="user" :disabled="true"> <s-coupon-list :data="item" type="user" :disabled="true">
<template v-slot:reason> <template v-slot:reason>
<view class="ss-flex ss-m-t-24"> <view class="ss-flex ss-m-t-24">
<view class="reason-title"> 不可用原因</view> <view class="reason-title"> 不可用原因</view>
<view class="reason-desc">{{ item.cannot_use_msg }}</view> <view class="reason-desc">{{ item.description || '未达到使用门槛' }}</view>
</view> </view>
</template> </template>
</s-coupon-list> </s-coupon-list>
</view> </view>
-->
</scroll-view> </scroll-view>
</view> </view>
<view class="modal-footer ss-flex"> <view class="modal-footer ss-flex">
@ -57,7 +51,8 @@
const props = defineProps({ const props = defineProps({
modelValue: { // modelValue: { //
type: Object, type: Object,
default() {}, default() {
},
}, },
show: { show: {
type: Boolean, type: Boolean,
@ -84,7 +79,7 @@
// //
const onConfirm = () => { const onConfirm = () => {
emits('confirm', state.couponId); emits('confirm', state.couponId);
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep() { :deep() {
@ -96,25 +91,30 @@
.model-box { .model-box {
height: 60vh; height: 60vh;
} }
.title { .title {
font-size: 36rpx; font-size: 36rpx;
height: 80rpx; height: 80rpx;
font-weight: bold; font-weight: bold;
color: #333333; color: #333333;
} }
.subtitle { .subtitle {
font-size: 26rpx; font-size: 26rpx;
font-weight: 500; font-weight: 500;
color: #333333; color: #333333;
} }
.model-content { .model-content {
height: 54vh; height: 54vh;
} }
.modal-footer { .modal-footer {
width: 100%; width: 100%;
height: 120rpx; height: 120rpx;
background: #fff; background: #fff;
} }
.confirm-btn { .confirm-btn {
width: 710rpx; width: 710rpx;
margin-left: 20rpx; margin-left: 20rpx;
@ -123,12 +123,14 @@
border-radius: 40rpx; border-radius: 40rpx;
color: #fff; color: #fff;
} }
.reason-title { .reason-title {
font-weight: 600; font-weight: 600;
font-size: 20rpx; font-size: 20rpx;
line-height: 26rpx; line-height: 26rpx;
color: #ff0003; color: #ff0003;
} }
.reason-desc { .reason-desc {
font-weight: 600; font-weight: 600;
font-size: 20rpx; font-size: 20rpx;

View File

@ -11,7 +11,11 @@
<view v-if="tagStyle.show" class="tag-icon-box"> <view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image> <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view> </view>
<image class="xs-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFit"></image> <image
class="xs-img-box"
:src="sheep.$url.cdn(data.image || data.picUrl)"
mode="aspectFit"
></image>
<view <view
v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show" v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
class="xs-goods-content ss-flex-col ss-row-around" class="xs-goods-content ss-flex-col ss-row-around"
@ -39,14 +43,21 @@
<view v-if="tagStyle.show" class="tag-icon-box"> <view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image> <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view> </view>
<image class="sm-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image> <image
class="sm-img-box"
:src="sheep.$url.cdn(data.image || data.picUrl)"
mode="aspectFill"
></image>
<view <view
v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show" v-if="goodsFields.title?.show || goodsFields.name?.show || goodsFields.price?.show"
class="sm-goods-content" class="sm-goods-content"
:style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]" :style="[{ color: titleColor, width: titleWidth ? titleWidth + 'rpx' : '' }]"
> >
<view v-if="goodsFields.title?.show || goodsFields.name?.show" class="sm-goods-title ss-line-1 ss-m-b-16"> <view
v-if="goodsFields.title?.show || goodsFields.name?.show"
class="sm-goods-title ss-line-1 ss-m-b-16"
>
{{ data.title || data.name }} {{ data.title || data.name }}
</view> </view>
<view <view
@ -65,7 +76,11 @@
<view v-if="tagStyle.show" class="tag-icon-box"> <view v-if="tagStyle.show" class="tag-icon-box">
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image> <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view> </view>
<image class="md-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="widthFix"></image> <image
class="md-img-box"
:src="sheep.$url.cdn(data.image || data.picUrl)"
mode="widthFix"
></image>
<view <view
class="md-goods-content ss-flex-col ss-row-around ss-p-b-20 ss-p-t-20 ss-p-x-16" class="md-goods-content ss-flex-col ss-row-around ss-p-b-20 ss-p-t-20 ss-p-x-16"
:id="elId" :id="elId"
@ -106,7 +121,10 @@
</view> </view>
<view <view
v-if="(goodsFields.original_price?.show||goodsFields.marketPrice?.show) &&( data.original_price > 0|| data.marketPrice > 0)" v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0)
"
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex" class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
:style="[{ color: originPriceColor }]" :style="[{ color: originPriceColor }]"
> >
@ -141,7 +159,11 @@
<view v-if="grouponTag" class="groupon-tag ss-flex ss-row-center"> <view v-if="grouponTag" class="groupon-tag ss-flex ss-row-center">
<view class="tag-icon">拼团</view> <view class="tag-icon">拼团</view>
</view> </view>
<image class="lg-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image> <image
class="lg-img-box"
:src="sheep.$url.cdn(data.image || data.picUrl)"
mode="aspectFill"
></image>
<view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20"> <view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
<view> <view>
<view <view
@ -177,7 +199,10 @@
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }} {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
</view> </view>
<view <view
v-if="(goodsFields.original_price?.show||goodsFields.marketPrice?.show) &&( data.original_price > 0|| data.marketPrice > 0)" v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0)
"
class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS" class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS"
:style="[{ color: originPriceColor }]" :style="[{ color: originPriceColor }]"
> >
@ -192,9 +217,7 @@
</view> </view>
<slot name="cart"> <slot name="cart">
<view class="buy-box ss-flex ss-col-center ss-row-center" v-if="buttonShow"> <view class="buy-box ss-flex ss-col-center ss-row-center" v-if="buttonShow"> </view>
去购买
</view>
</slot> </slot>
</view> </view>
@ -204,7 +227,11 @@
<image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image> <image class="tag-icon" :src="sheep.$url.cdn(tagStyle.src || tagStyle.imgUrl)"></image>
</view> </view>
<image class="sl-img-box" :src="sheep.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image> <image
class="sl-img-box"
:src="sheep.$url.cdn(data.image || data.picUrl)"
mode="aspectFill"
></image>
<view class="sl-goods-content"> <view class="sl-goods-content">
<view> <view>
@ -241,7 +268,10 @@
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }} {{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
</view> </view>
<view <view
v-if="(goodsFields.original_price?.show||goodsFields.marketPrice?.show) &&( data.original_price > 0|| data.marketPrice > 0)" v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0)
"
class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex" class="goods-origin-price ss-m-t-16 font-OPPOSANS ss-flex"
:style="[{ color: originPriceColor }]" :style="[{ color: originPriceColor }]"
> >
@ -296,7 +326,7 @@
import { fen2yuan, formatSales } from '@/sheep/hooks/useGoods'; import { fen2yuan, formatSales } from '@/sheep/hooks/useGoods';
import { formatStock } from '@/sheep/hooks/useGoods'; import { formatStock } from '@/sheep/hooks/useGoods';
import goodsCollectVue from '@/pages/user/goods-collect.vue'; import goodsCollectVue from '@/pages/user/goods-collect.vue';
import { isArray } from 'lodash'; import { isArray } from 'lodash-es';
// //
const state = reactive({}); const state = reactive({});

View File

@ -1,154 +1,171 @@
<!-- 装修组件 - 拼团 --> <!-- 装修组件 - 拼团 -->
<template> <template>
<view> <view>
<view <view v-if="layoutType === 'threeCol'" class="goods-sm-box ss-flex ss-flex-wrap" :style="[{ margin: '-' + data.space + 'rpx' }]">
v-if="layoutType === 'threeCol'" <view v-for="product in productList" class="goods-card-box" :key="product.id" :style="[{padding: data.space + 'rpx',},]">
class="goods-sm-box ss-flex ss-flex-wrap" <s-goods-column
:style="[{ margin: '-' + data.space + 'rpx' }]" class="goods-card"
> size="sm"
<view :goodsFields="goodsFields"
v-for="product in productList" :tagStyle="badge"
:key="product.id" :data="product"
class="goods-card-box" :titleColor="data.fields.name?.color"
:style="[ :topRadius="data.borderRadiusTop"
{ :bottomRadius="data.borderRadiusBottom"
padding: data.space + 'rpx', @click="sheep.$router.go('/pages/goods/groupon', { id: props.data.activityId, })">
}, </s-goods-column>
]" </view>
> </view>
<s-goods-column <!-- 样式2 一行一个 图片左 文案右 -->
class="goods-card" <view class="goods-box" v-if="layoutType === 'oneCol'">
size="sm" <view class="goods-list" v-for="(product, index) in productList" :key="index" :style="[{ marginBottom: space + 'px' }]">
:goodsFields="data.fields" <s-goods-column
:tagStyle="tagStyle" class="goods-card" size="lg"
:data="product" :grouponTag="true"
:titleColor="data.fields.name?.color" :goodsFields="goodsFields"
:topRadius="data.borderRadiusTop" :tagStyle="badge"
:bottomRadius="data.borderRadiusBottom" :data="product"
@click=" :titleColor="data.fields.name?.color"
sheep.$router.go('/pages/goods/groupon', { :subTitleColor="data.fields.introduction?.color"
id: props.data.activityId, :topRadius="data.borderRadiusTop"
}) :bottomRadius="data.borderRadiusBottom"
" @click="sheep.$router.go('/pages/goods/groupon', { id: props.data.activityId, })">
></s-goods-column> <template v-slot:cart>
</view> <button class="ss-reset-button cart-btn" :style="[buyStyle]">
</view> {{ btnBuy?.type === 'text' ? btnBuy.text : '去拼团' }}
<!-- 样式2 一行一个 图片左 文案右 --> </button>
<view class="goods-box" v-if="layoutType === 'oneCol'"> </template>
<view </s-goods-column>
class="goods-list" </view>
v-for="(product, index) in productList" </view>
:key="index" </view>
:style="[{ marginBottom: space + 'px' }]"
>
<s-goods-column
class="goods-card"
size="lg"
:goodsFields="data.fields"
:tagStyle="tagStyle"
:data="product"
:titleColor="data.fields.name?.color"
:subTitleColor="data.fields.introduction?.color"
:topRadius="data.borderRadiusTop"
:bottomRadius="data.borderRadiusBottom"
@click="
sheep.$router.go('/pages/goods/groupon', {
id: props.data.activityId,
})
"
>
<template v-slot:cart>
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
{{ btnBuy?.type === 'text' ? btnBuy.text : '' }}
</button>
</template>
</s-goods-column>
</view>
</view>
</view>
</template> </template>
<script setup> <script setup>
/** /**
* 拼团 * 拼团
*/ */
import { computed, onMounted, ref } from 'vue'; import {
import sheep from '@/sheep'; computed,
import SpuApi from "@/sheep/api/product/spu"; onMounted,
import CombinationApi from "@/sheep/api/promotion/combination"; reactive,
ref
} from 'vue';
import sheep from '@/sheep';
import SpuApi from "@/sheep/api/product/spu";
import CombinationApi from "@/sheep/api/promotion/combination";
// //
const props = defineProps({ const props = defineProps({
data: { //
type: Object, data: {
default() {}, type: Object,
}, default: () => ({}),
styles: { },
type: Object, //
default() {}, styles: {
}, type: Object,
}); default: () => ({}),
},
});
//
const goodsFields = reactive({
//
price: { show: true },
//
stock: { show: true },
//
name: { show: true },
//
introduction: { show: true },
//
marketPrice: { show: true },
//
salesCount: { show: true },
});
let { layoutType, tagStyle, btnBuy, space } = props.data; let {
let { marginLeft, marginRight } = props.styles; layoutType,
badge,
btnBuy,
space,
} = props.data;
let {
marginLeft,
marginRight
} = props.styles;
// //
const buyStyle = computed(() => { const buyStyle = computed(() => {
let btnBuy = props.data.btnBuy; let btnBuy = props.data.btnBuy;
if (btnBuy?.type === 'text') { if (btnBuy?.type === 'text') {
return { return {
background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`, background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`,
}; };
} }
if (btnBuy?.type === 'img') { if (btnBuy?.type === 'img') {
return { return {
width: '54rpx', width: '54rpx',
height: '54rpx', height: '54rpx',
background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`, background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`,
backgroundSize: '100% 100%', backgroundSize: '100% 100%',
}; };
} }
}); });
const productList = ref([]); const productList = ref([]);
onMounted(async () => { onMounted(async () => {
// todo@owen Yudao // todo@owen Yudao
const { data: activity } = await CombinationApi.getCombinationActivity(props.data.activityId); const {
const { data: spu } = await SpuApi.getSpuDetail(activity.spuId) data: activity
productList.value = [spu]; } = await CombinationApi.getCombinationActivity(props.data.activityId);
}); const {
data: spu
} = await SpuApi.getSpuDetail(activity.spuId)
//
activity.products.forEach((product) => {
spu.price = Math.min(spu.price, product.combinationPrice); // SPU
});
productList.value = [spu];
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.goods-list { .goods-list {
position: relative; position: relative;
.cart-btn {
position: absolute; .cart-btn {
bottom: 10rpx; position: absolute;
right: 20rpx; bottom: 10rpx;
z-index: 11; right: 20rpx;
height: 50rpx; z-index: 11;
line-height: 50rpx; height: 50rpx;
padding: 0 20rpx; line-height: 50rpx;
border-radius: 25rpx; padding: 0 20rpx;
font-size: 24rpx; border-radius: 25rpx;
color: #fff; font-size: 24rpx;
} color: #fff;
} background: linear-gradient(90deg, #ff6600 0%, #fe832a 100%);
.goods-list { }
&:nth-last-of-type(1) { }
margin-bottom: 0 !important;
} .goods-list {
} &:nth-last-of-type(1) {
.goods-sm-box { margin-bottom: 0 !important;
margin: 0 auto; }
box-sizing: border-box; }
.goods-card-box {
flex-shrink: 0; .goods-sm-box {
overflow: hidden; margin: 0 auto;
width: 33.3%; box-sizing: border-box;
box-sizing: border-box;
} .goods-card-box {
} flex-shrink: 0;
</style> overflow: hidden;
width: 33.3%;
box-sizing: border-box;
}
}
</style>

View File

@ -31,7 +31,11 @@
></view> ></view>
<!-- 顶部导航栏-情况4装修组件导航栏-沉浸式 --> <!-- 顶部导航栏-情况4装修组件导航栏-沉浸式 -->
<s-custom-navbar v-if="navbar === 'custom' && navbarMode === 'inner'" :data="navbarStyle" :showLeftButton="showLeftButton" /> <s-custom-navbar
v-if="navbar === 'custom' && navbarMode === 'inner'"
:data="navbarStyle"
:showLeftButton="showLeftButton"
/>
<!-- 页面内容插槽 --> <!-- 页面内容插槽 -->
<slot /> <slot />
@ -58,7 +62,7 @@
*/ */
import { computed, reactive, ref } from 'vue'; import { computed, reactive, ref } from 'vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
import { onShow } from '@dcloudio/uni-app'; import { onShow } from '@dcloudio/uni-app';
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
import { onShareAppMessage } from '@dcloudio/uni-app'; import { onShareAppMessage } from '@dcloudio/uni-app';

View File

@ -1,363 +1,343 @@
<!-- 装修基础组件菜单导航金刚区 --> <!-- 装修基础组件菜单导航金刚区 -->
<template> <template>
<!-- 包裹层 --> <!-- 包裹层 -->
<view <view class="ui-swiper" :class="[props.mode, props.ui]"
class="ui-swiper" :style="[bgStyle, { height: swiperHeight + (menuList.length > 1 ? 50 : 0) + 'rpx' }]">
:class="[props.mode, props.bg, props.ui]" <!-- 轮播 -->
:style="[{ height: swiperHeight + (menuList.length > 1 ? 50 : 0) + 'rpx' }]" <swiper :circular="props.circular" :current="state.cur" :autoplay="props.autoplay" :interval="props.interval"
> :duration="props.duration" :style="[{ height: swiperHeight + 'rpx' }]" @change="swiperChange">
<!-- 轮播 --> <swiper-item v-for="(arr, index) in menuList" :key="index" :class="{ cur: state.cur == index }">
<swiper <!-- 宫格 -->
:circular="props.circular" <view class="grid-wrap">
:current="state.cur" <view v-for="(item, index) in arr" :key="index"
:autoplay="props.autoplay" class="grid-item ss-flex ss-flex-col ss-col-center ss-row-center"
:interval="props.interval" :style="[{ width: `${100 * (1 / data.column)}%`, height: '200rpx' }]" hover-class="ss-hover-btn"
:duration="props.duration" @tap="sheep.$router.go(item.url)">
:style="[{ height: swiperHeight + 'rpx' }]" <view class="menu-box ss-flex ss-flex-col ss-col-center ss-row-center">
@change="swiperChange" <view v-if="item.badge.show" class="tag-box"
> :style="[{ background: item.badge.bgColor, color: item.badge.textColor }]">
<swiper-item {{ item.badge.text }}
v-for="(arr, index) in menuList" </view>
:key="index" <image v-if="item.iconUrl" class="menu-icon" :style="[
:class="{ cur: state.cur == index }"
>
<!-- 宫格 -->
<view class="grid-wrap">
<view
v-for="(item, index) in arr"
:key="index"
class="grid-item ss-flex ss-flex-col ss-col-center ss-row-center"
:style="[{ width: `${100 * (1 / data.column)}%`, height: '200rpx' }]"
hover-class="ss-hover-btn"
@tap="sheep.$router.go(item.url)"
>
<view class="menu-box ss-flex ss-flex-col ss-col-center ss-row-center">
<view
v-if="item.badge.show"
class="tag-box"
:style="[{ background: item.badge.bgColor, color: item.badge.textColor }]"
>
{{ item.badge.text }}
</view>
<image
v-if="item.iconUrl"
class="menu-icon"
:style="[
{ {
width: props.iconSize + 'rpx', width: props.iconSize + 'rpx',
height: props.iconSize + 'rpx', height: props.iconSize + 'rpx',
}, },
]" ]" :src="sheep.$url.cdn(item.iconUrl)" mode="aspectFill"></image>
:src="sheep.$url.cdn(item.iconUrl)" <view v-if="data.layout === 'iconText'" class="menu-title"
mode="aspectFill" :style="[{ color: item.titleColor }]">
></image> {{ item.title }}
<view </view>
v-if="data.layout === 'iconText'" </view>
class="menu-title" </view>
:style="[{ color: item.titleColor }]" </view>
> </swiper-item>
{{ item.title }} </swiper>
</view> <!-- 指示点 -->
</view> <template v-if="menuList.length > 1">
</view> <view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle != 'tag'">
</view> <view class="line-box" v-for="(item, index) in menuList.length" :key="index"
</swiper-item> :class="[state.cur == index ? 'cur' : '', props.dotCur]"></view>
</swiper> </view>
<!-- 指示点 --> <view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle == 'tag'">
<template v-if="menuList.length > 1"> <view class="ui-tag radius" :class="[props.dotCur]" style="pointer-events: none">
<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle != 'tag'"> <view style="transform: scale(0.7)">{{ state.cur + 1 }} / {{ menuList.length }}</view>
<view </view>
class="line-box" </view>
v-for="(item, index) in menuList.length" </template>
:key="index" </view>
:class="[state.cur == index ? 'cur' : '', props.dotCur]"
></view>
</view>
<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle == 'tag'">
<view class="ui-tag radius" :class="[props.dotCur]" style="pointer-events: none">
<view style="transform: scale(0.7)">{{ state.cur + 1 }} / {{ menuList.length }}</view>
</view>
</view>
</template>
</view>
</template> </template>
<script setup> <script setup>
/** /**
* 轮播menu * 轮播menu
* *
* @property {Boolean} circular = false - 是否采用衔接滑动即播放到末尾后重新回到开头 * @property {Boolean} circular = false - 是否采用衔接滑动即播放到末尾后重新回到开头
* @property {Boolean} autoplay = true - 是否自动切换 * @property {Boolean} autoplay = true - 是否自动切换
* @property {Number} interval = 5000 - 自动切换时间间隔 * @property {Number} interval = 5000 - 自动切换时间间隔
* @property {Number} duration = 500 - 滑动动画时长,app-nvue不支持 * @property {Number} duration = 500 - 滑动动画时长,app-nvue不支持
* @property {Array} list = [] - 轮播数据 * @property {Array} list = [] - 轮播数据
* @property {String} ui = '' - 样式class * @property {String} ui = '' - 样式class
* @property {String} mode - 模式 * @property {String} mode - 模式
* @property {String} dotStyle - 指示点样式 * @property {String} dotStyle - 指示点样式
* @property {String} dotCur= 'ui-BG-Main' - 当前指示点样式,默认主题色 * @property {String} dotCur= 'ui-BG-Main' - 当前指示点样式,默认主题色
* @property {String} bg - 背景 * @property {String} bg - 背景
* *
* @property {String|Number} col = 4 - 一行数量 * @property {String|Number} col = 4 - 一行数量
* @property {String|Number} row = 1 - 几行 * @property {String|Number} row = 1 - 几行
* @property {String} hasBorder - 是否有边框 * @property {String} hasBorder - 是否有边框
* @property {String} borderColor - 边框颜色 * @property {String} borderColor - 边框颜色
* @property {String} background - 背景 * @property {String} background - 背景
* @property {String} hoverClass - 按压样式类 * @property {String} hoverClass - 按压样式类
* @property {String} hoverStayTime - 动画时间 * @property {String} hoverStayTime - 动画时间
* *
* @property {Array} list - 导航列表 * @property {Array} list - 导航列表
* @property {Number} iconSize - 图标大小 * @property {Number} iconSize - 图标大小
* @property {String} color - 标题颜色 * @property {String} color - 标题颜色
* *
*/ */
import { reactive, computed } from 'vue'; import {
import sheep from '@/sheep'; reactive,
computed
} from 'vue';
import sheep from '@/sheep';
// //
const state = reactive({ const state = reactive({
cur: 0, cur: 0,
}); });
// //
const props = defineProps({ const props = defineProps({
data: { //
type: Object, data: {
default() {}, type: Object,
}, default: () => ({}),
styles: { },
type: Object, //
default() {}, styles: {
}, type: Object,
circular: { default: () => ({}),
type: Boolean, },
default: true, circular: {
}, type: Boolean,
autoplay: { default: true,
type: Boolean, },
default: false, autoplay: {
}, type: Boolean,
interval: { default: false,
type: Number, },
default: 5000, interval: {
}, type: Number,
duration: { default: 5000,
type: Number, },
default: 500, duration: {
}, type: Number,
default: 500,
},
ui: {
type: String,
default: '',
},
mode: {
//default
type: String,
default: 'default',
},
dotStyle: {
type: String,
default: 'long', //default long tag
},
dotCur: {
type: String,
default: 'ui-BG-Main',
},
height: {
type: Number,
default: 300,
},
//
hasBorder: {
type: Boolean,
default: true,
},
//
borderColor: {
type: String,
default: 'red',
},
background: {
type: String,
default: 'blue',
},
hoverClass: {
type: String,
default: 'ss-hover-class', //'none'hover
},
//
col: {
type: [Number, String],
default: 3,
},
iconSize: {
type: Number,
default: 80,
},
color: {
type: String,
default: '#000',
},
});
ui: { //
type: String, const bgStyle = computed(() => {
default: '', // props.styles
}, const {
mode: { bgType,
//default bgImg,
type: String, bgColor
default: 'default', } = props.styles;
},
dotStyle: {
type: String,
default: 'long', //default long tag
},
dotCur: {
type: String,
default: 'ui-BG-Main',
},
bg: {
type: String,
default: 'bg-none',
},
height: {
type: Number,
default: 300,
},
// // bgType
hasBorder: { return {
type: Boolean, background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
default: true, };
}, });
//
borderColor: {
type: String,
default: 'red',
},
background: {
type: String,
default: 'blue',
},
hoverClass: {
type: String,
default: 'ss-hover-class', //'none'hover
},
//
col: {
type: [Number, String],
default: 3,
},
iconSize: {
type: Number,
default: 80,
},
color: {
type: String,
default: '#000',
},
});
// //
const menuList = computed(() => splitData(props.data.list, props.data.row * props.data.column)); const menuList = computed(() => splitData(props.data.list, props.data.row * props.data.column));
const swiperHeight = computed(() => props.data.row * (props.data.layout === 'iconText' ? 200 : 180)); const swiperHeight = computed(() => props.data.row * (props.data.layout === 'iconText' ? 200 : 180));
const windowWidth = sheep.$platform.device.windowWidth; const windowWidth = sheep.$platform.device.windowWidth;
// current change // current change
const swiperChange = (e) => { const swiperChange = (e) => {
state.cur = e.detail.current; state.cur = e.detail.current;
}; };
// //
const splitData = (oArr = [], length = 1) => { const splitData = (oArr = [], length = 1) => {
let arr = []; let arr = [];
let minArr = []; let minArr = [];
oArr.forEach((c) => { oArr.forEach((c) => {
if (minArr.length === length) { if (minArr.length === length) {
minArr = []; minArr = [];
} }
if (minArr.length === 0) { if (minArr.length === 0) {
arr.push(minArr); arr.push(minArr);
} }
minArr.push(c); minArr.push(c);
}); });
return arr; return arr;
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.grid-wrap { .grid-wrap {
width: 100%; width: 100%;
display: flex; display: flex;
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
overflow: hidden; overflow: hidden;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
} }
.menu-box {
position: relative;
z-index: 1;
transform: translate(0, 0);
.tag-box { .menu-box {
position: absolute; position: relative;
z-index: 2; z-index: 1;
top: 0; transform: translate(0, 0);
right: -6rpx;
font-size: 2em;
line-height: 1;
padding: 0.4em 0.6em 0.3em;
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
transform-origin: 100% 0;
border-radius: 200rpx;
white-space: nowrap;
}
.menu-icon { .tag-box {
transform: translate(0, 0); position: absolute;
width: 80rpx; z-index: 2;
height: 80rpx; top: 0;
padding-bottom: 10rpx; right: -6rpx;
} font-size: 2em;
line-height: 1;
padding: 0.4em 0.6em 0.3em;
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
transform-origin: 100% 0;
border-radius: 200rpx;
white-space: nowrap;
}
.menu-title { .menu-icon {
font-size: 24rpx; transform: translate(0, 0);
color: #333; width: 80rpx;
} height: 80rpx;
} padding-bottom: 10rpx;
}
::v-deep(.ui-swiper) { .menu-title {
position: relative; font-size: 24rpx;
z-index: 1; color: #333;
}
}
.ui-swiper-dot { ::v-deep(.ui-swiper) {
position: absolute; position: relative;
width: 100%; z-index: 1;
bottom: 20rpx;
height: 30rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
&.default .line-box { .ui-swiper-dot {
display: inline-flex; position: absolute;
border-radius: 50rpx; width: 100%;
width: 6px; bottom: 20rpx;
height: 6px; height: 30rpx;
border: 2px solid transparent; display: flex;
margin: 0 10rpx; align-items: center;
opacity: 0.3; justify-content: center;
position: relative; z-index: 2;
justify-content: center;
align-items: center;
&.cur { &.default .line-box {
width: 8px; display: inline-flex;
height: 8px; border-radius: 50rpx;
opacity: 1; width: 6px;
border: 0px solid transparent; height: 6px;
} border: 2px solid transparent;
margin: 0 10rpx;
opacity: 0.3;
position: relative;
justify-content: center;
align-items: center;
&.cur::after { &.cur {
content: ''; width: 8px;
border-radius: 50rpx; height: 8px;
width: 4px; opacity: 1;
height: 4px; border: 0px solid transparent;
background-color: #fff; }
}
}
&.long .line-box { &.cur::after {
display: inline-block; content: '';
border-radius: 100rpx; border-radius: 50rpx;
width: 6px; width: 4px;
height: 6px; height: 4px;
margin: 0 10rpx; background-color: #fff;
opacity: 0.3; }
position: relative; }
&.cur { &.long .line-box {
width: 24rpx; display: inline-block;
opacity: 1; border-radius: 100rpx;
} width: 6px;
height: 6px;
margin: 0 10rpx;
opacity: 0.3;
position: relative;
&.cur::after { &.cur {
} width: 24rpx;
} opacity: 1;
}
&.line { &.cur::after {}
bottom: 20rpx; }
.line-box { &.line {
display: inline-block; bottom: 20rpx;
width: 30px;
height: 3px;
opacity: 0.3;
position: relative;
&.cur { .line-box {
opacity: 1; display: inline-block;
} width: 30px;
} height: 3px;
} opacity: 0.3;
position: relative;
&.tag { &.cur {
justify-content: flex-end; opacity: 1;
position: absolute; }
bottom: 20rpx; }
right: 20rpx; }
}
} &.tag {
} justify-content: flex-end;
</style> position: absolute;
bottom: 20rpx;
right: 20rpx;
}
}
}
</style>

View File

@ -1,82 +1,104 @@
<!-- 装修基础组件宫格导航 --> <!-- 装修基础组件宫格导航 -->
<template> <template>
<uni-grid :showBorder="Boolean(data.border)" :column="data.column"> <view :style="[bgStyle, { marginLeft: `${data.space}px` }]">
<uni-grid-item <uni-grid :showBorder="Boolean(data.border)" :column="data.column">
v-for="(item, index) in data.list" <uni-grid-item v-for="(item, index) in data.list" :key="index" @tap="sheep.$router.go(item.url)">
:key="index" <view class="grid-item-box ss-flex ss-flex-col ss-row-center ss-col-center">
@tap="sheep.$router.go(item.url)" <view class="img-box">
> <view class="tag-box" v-if="item.badge.show"
<view class="grid-item-box ss-flex ss-flex-col ss-row-center ss-col-center"> :style="[{ background: item.badge.bgColor, color: item.badge.textColor }]">
<view class="img-box"> {{ item.badge.text }}
<view </view>
class="tag-box" <image class="menu-image" :src="sheep.$url.cdn(item.iconUrl)"></image>
v-if="item.badge.show" </view>
:style="[{ background: item.badge.bgColor, color: item.badge.textColor }]"
> <view class="title-box ss-flex ss-flex-col ss-row-center ss-col-center">
{{ item.badge.text }} <view class="grid-text" :style="[{ color: item.titleColor }]">
</view> {{ item.title }}
<image class="menu-image" :src="sheep.$url.cdn(item.iconUrl)"></image> </view>
</view> <view class="grid-tip" :style="[{ color: item.subtitleColor }]">
{{ item.subtitle }}
</view>
</view>
</view>
</uni-grid-item>
</uni-grid>
</view>
<view class="title-box ss-flex ss-flex-col ss-row-center ss-col-center">
<view class="grid-text" :style="[{ color: item.titleColor }]">
{{ item.title }}
</view>
<view class="grid-tip" :style="[{ color: item.subtitleColor }]">
{{ item.subtitle }}
</view>
</view>
</view>
</uni-grid-item>
</uni-grid>
</template> </template>
<script setup> <script setup>
import sheep from '@/sheep'; import sheep from '@/sheep';
import {
computed
} from 'vue';
const props = defineProps({ const props = defineProps({
data: { //
type: Object, data: {
default() {}, type: Object,
}, default: () => ({}),
}); },
//
styles: {
type: Object,
default: () => ({}),
},
});
//
const bgStyle = computed(() => {
// props.styles
const {
bgType,
bgImg,
bgColor
} = props.styles;
// bgType
return {
background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
};
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.menu-image { .menu-image {
width: 24px; width: 24px;
height: 24px; height: 24px;
} }
.grid-item-box {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
.img-box {
position: relative;
.tag-box {
position: absolute;
z-index: 2;
top: 0;
right: 0;
font-size: 2em;
line-height: 1;
padding: 0.4em 0.6em 0.3em;
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
transform-origin: 100% 0;
border-radius: 200rpx;
white-space: nowrap;
}
}
.title-box { .grid-item-box {
.grid-tip { flex: 1;
font-size: 24rpx; display: flex;
white-space: nowrap; flex-direction: column;
text-align: center; align-items: center;
} justify-content: center;
} height: 100%;
}
</style> .img-box {
position: relative;
.tag-box {
position: absolute;
z-index: 2;
top: 0;
right: 0;
font-size: 2em;
line-height: 1;
padding: 0.4em 0.6em 0.3em;
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
transform-origin: 100% 0;
border-radius: 200rpx;
white-space: nowrap;
}
}
.title-box {
.grid-tip {
font-size: 24rpx;
white-space: nowrap;
text-align: center;
}
}
}
</style>

View File

@ -1,6 +1,6 @@
<!-- 装修用户组件用户订单 --> <!-- 装修用户组件用户订单 -->
<template> <template>
<view class="ss-order-menu-wrap ss-flex ss-col-center"> <view class="ss-order-menu-wrap ss-flex ss-col-center" :style="[style, { marginLeft: `${data.space}px` }]">
<view <view
class="menu-item ss-flex-1 ss-flex-col ss-row-center ss-col-center" class="menu-item ss-flex-1 ss-flex-col ss-row-center ss-col-center"
v-for="item in orderMap" v-for="item in orderMap"
@ -67,8 +67,32 @@
path: '/pages/order/list', path: '/pages/order/list',
}, },
]; ];
//
const props = defineProps({
//
data: {
type: Object,
default: () => ({}),
},
//
styles: {
type: Object,
default: () => ({}),
},
});
//
const numData = computed(() => sheep.$store('user').numData); const numData = computed(() => sheep.$store('user').numData);
//
const style = computed(() => {
// props.styles
const { bgType, bgImg, bgColor } = props.styles;
// bgType
return {
background: bgType === 'img'
? `url(${bgImg}) no-repeat top center / 100% 100%`
: bgColor
};
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,160 +1,182 @@
<!-- 装修组件 - 秒杀 --> <!-- 装修组件 - 秒杀 -->
<template> <template>
<view> <view>
<!-- 样式一三列 - 上图下文 --> <!-- 样式一三列 - 上图下文 -->
<view <view v-if="layoutType === 'threeCol'" class="goods-sm-box ss-flex ss-flex-wrap" :style="[{ margin: '-' + data.space + 'rpx' }]">
v-if="layoutType === 'threeCol'" <view v-for="product in productList" :key="product.id" class="goods-card-box" :style="[{padding: data.space + 'rpx',},]">
class="goods-sm-box ss-flex ss-flex-wrap" <s-goods-column
:style="[{ margin: '-' + data.space + 'rpx' }]" class="goods-card" size="sm"
> :goodsFields="goodsFields"
<view :tagStyle="badge"
v-for="product in productList" :data="product"
:key="product.id" :titleColor="data.fields.name?.color"
class="goods-card-box" :topRadius="data.borderRadiusTop"
:style="[ :bottomRadius="data.borderRadiusBottom"
{ @click="sheep.$router.go('/pages/goods/seckill', { id: props.data.activityId, })">
padding: data.space + 'rpx', </s-goods-column>
}, </view>
]" </view>
> <!-- 样式二一列 - 左图右文 -->
<s-goods-column <view class="goods-box" v-if="layoutType === 'oneCol'">
class="goods-card" <view class="goods-list" v-for="(product, index) in productList" :key="index"
size="sm" :style="[{ marginBottom: space + 'px' }]">
:goodsFields="data.fields" <s-goods-column
:tagStyle="tagStyle" class="goods-card"
:data="product" size="lg"
:titleColor="data.fields.name?.color" :goodsFields="goodsFields"
:topRadius="data.borderRadiusTop" :seckillTag="true"
:bottomRadius="data.borderRadiusBottom" :tagStyle="badge"
@click=" :data="product"
sheep.$router.go('/pages/goods/seckill', { :titleColor="data.fields.name?.color"
id: props.data.activityId, :subTitleColor="data.fields.introduction?.color"
}) :topRadius="data.borderRadiusTop"
" :bottomRadius="data.borderRadiusBottom"
></s-goods-column> @click="sheep.$router.go('/pages/goods/seckill', { id: props.data.activityId, })">
</view> <template v-slot:cart>
</view> <button class="ss-reset-button cart-btn" :style="[buyStyle]">
<!-- 样式二一列 - 左图右文 --> {{ btnBuy?.type === 'text' ? btnBuy.text : '立即秒杀' }}
<view class="goods-box" v-if="layoutType === 'oneCol'"> </button>
<view </template>
class="goods-list" </s-goods-column>
v-for="(product, index) in productList" </view>
:key="index" </view>
:style="[{ marginBottom: space + 'px' }]" </view>
>
<s-goods-column
class="goods-card"
size="lg"
:goodsFields="data.fields"
:tagStyle="tagStyle"
:data="product"
:titleColor="data.fields.name?.color"
:subTitleColor="data.fields.introduction?.color"
:topRadius="data.borderRadiusTop"
:bottomRadius="data.borderRadiusBottom"
@click="
sheep.$router.go('/pages/goods/seckill', {
id: props.data.activityId,
})
"
>
<template v-slot:cart>
<button class="ss-reset-button cart-btn" :style="[buyStyle]">
{{ btnBuy?.type === 'text' ? btnBuy.text : '' }}
</button>
</template>
</s-goods-column>
</view>
</view>
</view>
</template> </template>
<script setup> <script setup>
/** /**
* 秒杀商品列表 * 秒杀商品列表
* *
* @property {Array} list 商品列表 * @property {Array} list 商品列表
*/ */
import { computed, onMounted, ref } from 'vue'; import {
import sheep from '@/sheep'; computed,
import SeckillApi from "@/sheep/api/promotion/seckill"; onMounted,
import SpuApi from "@/sheep/api/product/spu"; reactive,
ref
} from 'vue';
import sheep from '@/sheep';
import SeckillApi from "@/sheep/api/promotion/seckill";
import SpuApi from "@/sheep/api/product/spu";
// //
const props = defineProps({ const props = defineProps({
data: { //
type: Object, data: {
default() {}, type: Object,
}, default: () => ({}),
styles: { },
type: Object, //
default() {}, styles: {
}, type: Object,
}); default: () => ({}),
},
});
//
const goodsFields = reactive({
//
price: { show: true },
//
stock: { show: true },
//
name: { show: true },
//
introduction: { show: true },
//
marketPrice: { show: true },
//
salesCount: { show: true },
});
let {
layoutType,
badge,
btnBuy,
space,
} = props.data;
let {
marginLeft,
marginRight
} = props.styles;
let { layoutType, tagStyle, btnBuy, space } = props.data; //
let { marginLeft, marginRight } = props.styles; const buyStyle = computed(() => {
let btnBuy = props.data.btnBuy;
if (btnBuy?.type === 'text') {
return {
background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`,
};
}
if (btnBuy?.type === 'img') {
return {
width: '54rpx',
height: '54rpx',
background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`,
backgroundSize: '100% 100%',
};
}
});
// //
const buyStyle = computed(() => { const productList = ref([]);
let btnBuy = props.data.btnBuy; //
if (btnBuy?.type === 'text') { onMounted(async () => {
return { // todo@owen Yudao
background: `linear-gradient(to right, ${btnBuy.bgBeginColor}, ${btnBuy.bgEndColor})`, const {
}; data: activity
} } = await SeckillApi.getSeckillActivity(props.data.activityId);
if (btnBuy?.type === 'img') { const {
return { data: spu
width: '54rpx', } = await SpuApi.getSpuDetail(activity.spuId)
height: '54rpx', //
background: `url(${sheep.$url.cdn(btnBuy.imgUrl)}) no-repeat`, activity.products.forEach((product) => {
backgroundSize: '100% 100%', spu.price = Math.min(spu.price, product.seckillPrice); // SPU
}; });
} //
}); spu.stock = activity.stock
// - =
// spu.salesCount = activity.totalStock - activity.stock
const productList = ref([]); productList.value = [spu];
// });
onMounted(async () => {
// todo@owen Yudao
const { data: activity } = await SeckillApi.getSeckillActivity(props.data.activityId);
const { data: spu } = await SpuApi.getSpuDetail(activity.spuId)
productList.value = [spu];
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.header-box { .header-box {
height: 100rpx; height: 100rpx;
} }
.goods-list { .goods-list {
position: relative; position: relative;
&:nth-last-child(1) {
margin-bottom: 0 !important; &:nth-last-child(1) {
} margin-bottom: 0 !important;
.cart-btn { }
position: absolute;
bottom: 10rpx; .cart-btn {
right: 20rpx; position: absolute;
z-index: 11; bottom: 10rpx;
height: 50rpx; right: 20rpx;
line-height: 50rpx; z-index: 11;
padding: 0 20rpx; height: 50rpx;
border-radius: 25rpx; line-height: 50rpx;
font-size: 24rpx; padding: 0 20rpx;
color: #fff; border-radius: 25rpx;
} font-size: 24rpx;
} color: #fff;
.goods-sm-box { background: linear-gradient(90deg, #ff6600 0%, #fe832a 100%);
margin: 0 auto; }
box-sizing: border-box; }
.goods-card-box {
flex-shrink: 0; .goods-sm-box {
overflow: hidden; margin: 0 auto;
width: 33.3%; box-sizing: border-box;
box-sizing: border-box;
} .goods-card-box {
} flex-shrink: 0;
</style> overflow: hidden;
width: 33.3%;
box-sizing: border-box;
}
}
</style>

View File

@ -22,7 +22,7 @@
</view> </view>
</view> </view>
<view class="header-right-bottom ss-flex ss-col-center ss-row-between"> <view class="header-right-bottom ss-flex ss-col-center ss-row-between">
<view class="price-text"> {{ fen2yuan(goodsInfo.price) }}</view> <view class="price-text"> {{ fen2yuan(state.selectedSku.price || goodsInfo.price || state.selectedSku.marketPrice) }}</view>
<view class="stock-text ss-m-l-20"> <view class="stock-text ss-m-l-20">
库存{{ state.selectedSku.stock || goodsInfo.stock }} 库存{{ state.selectedSku.stock || goodsInfo.stock }}
@ -63,7 +63,7 @@
<view class="btn-title">{{ grouponNum + '人团' }}</view> <view class="btn-title">{{ grouponNum + '人团' }}</view>
</button> </button>
<button class="ss-reset-button btn-tox ss-flex-col" @tap="onBuy"> <button class="ss-reset-button btn-tox ss-flex-col" @tap="onBuy">
<view class="btn-price">{{ fen2yuan(goodsInfo.price) }}</view> <view class="btn-price">{{ fen2yuan(state.selectedSku.price * state.selectedSku.count || goodsInfo.price * state.selectedSku.count || state.selectedSku.marketPrice * state.selectedSku.count || goodsInfo.price) }}</view>
<view v-if="grouponAction === 'create'"></view> <view v-if="grouponAction === 'create'"></view>
<view v-else-if="grouponAction === 'join'">参与拼团</view> <view v-else-if="grouponAction === 'join'">参与拼团</view>
</button> </button>

View File

@ -1,31 +1,31 @@
<!-- 秒杀商品的 SKU 选择 s-select-sku.vue 类似 --> <!-- 秒杀商品的 SKU 选择 s-select-sku.vue 类似 -->
<template> <template>
<!-- 规格弹窗 --> <!-- 规格弹窗 -->
<su-popup :show="show" round="10" @close="emits('close')"> <su-popup :show="show" round="10" @close="emits('close')">
<!-- SKU 信息 --> <!-- SKU 信息 -->
<view class="ss-modal-box bg-white ss-flex-col"> <view class="ss-modal-box bg-white ss-flex-col">
<view class="modal-header ss-flex ss-col-center"> <view class="modal-header ss-flex ss-col-center">
<!-- 规格图 --> <!-- 规格图 -->
<view class="header-left ss-m-r-30"> <view class="header-left ss-m-r-30">
<image <image
class="sku-image" class="sku-image"
:src="sheep.$url.cdn(state.selectedSku.picUrl || state.goodsInfo.picUrl)" :src="sheep.$url.cdn(state.selectedSku.picUrl || state.goodsInfo.picUrl)"
mode="aspectFill" mode="aspectFill"
> >
</image> </image>
</view> </view>
<view class="header-right ss-flex-col ss-row-between ss-flex-1"> <view class="header-right ss-flex-col ss-row-between ss-flex-1">
<!-- 名称 --> <!-- 名称 -->
<view class="goods-title ss-line-2">{{ state.goodsInfo.name }}</view> <view class="goods-title ss-line-2">{{ state.goodsInfo.name }}</view>
<view class="header-right-bottom ss-flex ss-col-center ss-row-between"> <view class="header-right-bottom ss-flex ss-col-center ss-row-between">
<!-- 价格 --> <!-- 价格 -->
<view class="price-text"> <view class="price-text">
{{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }} {{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }}
</view> </view>
<!-- 秒杀价格标签 --> <!-- 秒杀价格标签 -->
<view class="tig ss-flex ss-col-center"> <view class="tig ss-flex ss-col-center">
<view class="tig-icon ss-flex ss-col-center ss-row-center"> <view class="tig-icon ss-flex ss-col-center ss-row-center">
<text class="cicon-alarm"></text> <text class="cicon-alarm"></text>
</view> </view>
<view class="tig-title">秒杀价</view> <view class="tig-title">秒杀价</view>
</view> </view>
@ -91,8 +91,8 @@
// active,nostock // active,nostock
import { computed, reactive, watch } from 'vue'; import { computed, reactive, watch } from 'vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import {convertProductPropertyList, fen2yuan} from "@/sheep/hooks/useGoods"; import { convertProductPropertyList, fen2yuan } from '@/sheep/hooks/useGoods';
import {min} from "lodash"; import { min } from 'lodash-es';
const emits = defineEmits(['change', 'addCart', 'buy', 'close']); const emits = defineEmits(['change', 'addCart', 'buy', 'close']);
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -107,7 +107,7 @@
singleLimitCount: { singleLimitCount: {
type: Number, type: Number,
default: 1, default: 1,
} },
}); });
const state = reactive({ const state = reactive({
goodsInfo: computed(() => props.modelValue), goodsInfo: computed(() => props.modelValue),
@ -120,7 +120,7 @@
const skuList = computed(() => { const skuList = computed(() => {
let skuPrices = state.goodsInfo.skus; let skuPrices = state.goodsInfo.skus;
for (let price of skuPrices) { for (let price of skuPrices) {
price.value_id_array = price.properties.map((item) => item.valueId) price.value_id_array = price.properties.map((item) => item.valueId);
} }
return skuPrices; return skuPrices;
}); });
@ -228,7 +228,7 @@
// property id SKU // property id SKU
for (let valueIndex in propertyList[propertyIndex]['values']) { for (let valueIndex in propertyList[propertyIndex]['values']) {
propertyList[propertyIndex]['values'][valueIndex]['disabled'] = propertyList[propertyIndex]['values'][valueIndex]['disabled'] =
noChooseValueIds.indexOf(propertyList[propertyIndex]['values'][valueIndex]['id']) < 0; // true or false noChooseValueIds.indexOf(propertyList[propertyIndex]['values'][valueIndex]['id']) < 0; // true or false
} }
} }
}; };
@ -258,7 +258,10 @@
const onSelectSku = (propertyId, valueId) => { const onSelectSku = (propertyId, valueId) => {
// //
let isChecked = true; // or let isChecked = true; // or
if (state.currentPropertyArray[propertyId] !== undefined && state.currentPropertyArray[propertyId] === valueId) { if (
state.currentPropertyArray[propertyId] !== undefined &&
state.currentPropertyArray[propertyId] === valueId
) {
// '' // ''
isChecked = false; isChecked = false;
state.currentPropertyArray.splice(propertyId, 1, ''); state.currentPropertyArray.splice(propertyId, 1, '');

View File

@ -66,7 +66,7 @@
css: { css: {
// //
width: sheep.$platform.device.windowWidth * 0.9, width: sheep.$platform.device.windowWidth * 0.9,
height: 550, height: 600,
}, },
views: [], views: [],
}); });

View File

@ -82,12 +82,12 @@ const groupon = async (poster) => {
type: 'text', type: 'text',
text: '2人团', text: '2人团',
css: { css: {
color: '#ff0000', color: '#fff',
fontSize: 30, fontSize: 12,
fontFamily: 'OPPOSANS', fontFamily: 'OPPOSANS',
position: 'fixed', position: 'fixed',
left: width * 0.3, left: width * 0.84,
top: width * 1.32, top: width * 1.3,
}, },
}, },
// #ifndef MP-WEIXIN // #ifndef MP-WEIXIN
@ -96,7 +96,7 @@ const groupon = async (poster) => {
text: poster.shareInfo.link, text: poster.shareInfo.link,
css: { css: {
position: 'fixed', position: 'fixed',
left: width * 0.75, left: width * 0.5,
top: width * 1.3, top: width * 1.3,
width: width * 0.2, width: width * 0.2,
height: width * 0.2, height: width * 0.2,

View File

@ -1,100 +1,108 @@
<!-- 装修商品组件标题栏 --> <!-- 装修商品组件标题栏 -->
<template> <template>
<view <view class="ss-title-wrap ss-flex ss-col-center" :class="[state.typeMap[data.textAlign]]" :style="[bgStyle, { marginLeft: `${data.space}px` }]">
class="ss-title-wrap ss-flex ss-col-center" <view class="title-content">
:class="[state.typeMap[data.textAlign]]" <!-- 主标题 -->
:style="[elStyles]" <view v-if="data.title" class="title-text" :style="[titleStyles]">{{ data.title }}</view>
> <!-- 副标题 -->
<view class="title-content"> <view v-if="data.description" :style="[descStyles]" class="sub-title-text">{{ data.description }}</view>
<!-- 主标题 --> </view>
<view v-if="data.title" class="title-text" :style="[titleStyles]">{{ data.title }}</view> <!-- 查看更多 -->
<!-- 副标题 --> <view v-if="data.more?.show" class="more-box ss-flex ss-col-center" @tap="sheep.$router.go(data.more.url)"
<view v-if="data.description" :style="[descStyles]" class="sub-title-text">{{ data.description }}</view> :style="{color: data.descriptionColor}">
</view> <view class="more-text" v-if="data.more.type !== 'icon'">{{ data.more.text }} </view>
<!-- 查看更多 --> <text class="_icon-forward" v-if="data.more.type !== 'text'"></text>
<view v-if="data.more?.show" class="more-box ss-flex ss-col-center" @tap="sheep.$router.go(data.more.url)" </view>
:style="{color: data.descriptionColor}"> </view>
<view class="more-text" v-if="data.more.type !== 'icon'">{{ data.more.text }} </view>
<text class="_icon-forward" v-if="data.more.type !== 'text'"></text>
</view>
</view>
</template> </template>
<script setup> <script setup>
/** /**
* 标题栏 * 标题栏
*/ */
import { reactive } from 'vue'; import {
import sheep from '@/sheep'; reactive,
computed
} from 'vue';
import sheep from '@/sheep';
// //
const state = reactive({ const state = reactive({
typeMap: { typeMap: {
left: 'ss-row-left', left: 'ss-row-left',
center: 'ss-row-center', center: 'ss-row-center',
}, },
}); });
// //
const props = defineProps({ const props = defineProps({
data: { //
type: Object, data: {
default() {}, type: Object,
}, default: () => ({}),
styles: { },
type: Object, //
default() {}, styles: {
}, type: Object,
}); default: () => ({}),
},
});
//
const bgStyle = computed(() => {
// props.styles
const {
bgType,
bgImg,
bgColor
} = props.styles;
// // bgType
const elStyles = { return {
background: `url(${sheep.$url.cdn(props.data.bgImgUrl)}) no-repeat top center / 100% auto`, background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
fontSize: `${props.data.titleSize}px`, };
fontWeight: `${props.data.titleWeight}px`, });
};
// //
const titleStyles = { const titleStyles = {
color: props.data.titleColor, color: props.data.titleColor,
fontSize: `${props.data.titleSize}px`, fontSize: `${props.data.titleSize}px`,
textAlign: props.data.textAlign textAlign: props.data.textAlign
}; };
// //
const descStyles = { const descStyles = {
color: props.data.descriptionColor, color: props.data.descriptionColor,
textAlign: props.data.textAlign, textAlign: props.data.textAlign,
fontSize: `${props.data.descriptionSize}px`, fontSize: `${props.data.descriptionSize}px`,
fontWeight: `${props.data.descriptionWeight}px`, fontWeight: `${props.data.descriptionWeight}px`,
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.ss-title-wrap { .ss-title-wrap {
height: 80rpx; height: 80rpx;
position: relative; position: relative;
.title-content { .title-content {
.title-text { .title-text {
font-size: 30rpx; font-size: 30rpx;
color: #333; color: #333;
} }
.sub-title-text { .sub-title-text {
font-size: 22rpx; font-size: 22rpx;
color: #999; color: #999;
} }
} }
.more-box { .more-box {
white-space: nowrap; white-space: nowrap;
font-size: 22rpx; font-size: 22rpx;
color: #999; color: #999;
position: absolute; position: absolute;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
right: 20rpx; right: 20rpx;
} }
} }
</style> </style>

View File

@ -1,6 +1,6 @@
<!-- 装修用户组件用户卡片 --> <!-- 装修用户组件用户卡片 -->
<template> <template>
<view class="ss-user-info-wrap ss-p-t-50"> <view class="ss-user-info-wrap ss-p-t-50" :style="[bgStyle, { marginLeft: `${data.space}px` }]">
<view class="ss-flex ss-col-center ss-row-between ss-m-b-20"> <view class="ss-flex ss-col-center ss-row-between ss-m-b-20">
<view class="left-box ss-flex ss-col-center ss-m-l-36"> <view class="left-box ss-flex ss-col-center ss-m-l-36">
<view class="avatar-box ss-m-r-24"> <view class="avatar-box ss-m-r-24">
@ -70,9 +70,15 @@
const isLogin = computed(() => sheep.$store('user').isLogin); const isLogin = computed(() => sheep.$store('user').isLogin);
// //
const props = defineProps({ const props = defineProps({
background: { //
type: String, data: {
default: '', type: Object,
default: () => ({}),
},
//
styles: {
type: Object,
default: () => ({}),
}, },
// //
avatar: { avatar: {
@ -96,7 +102,20 @@
default: '1', default: '1',
}, },
}); });
//
const bgStyle = computed(() => {
// props.styles
const { bgType, bgImg, bgColor } = props.styles;
// bgType
return {
background: bgType === 'img'
? `url(${bgImg}) no-repeat top center / 100% 100%`
: bgColor
};
});
//
function onBind() { function onBind() {
showAuthModal('changeMobile'); showAuthModal('changeMobile');
} }

View File

@ -1,6 +1,6 @@
<!-- 装修用户组件用户资产 --> <!-- 装修用户组件用户资产 -->
<template> <template>
<view class="ss-wallet-menu-wrap ss-flex ss-col-center"> <view class="ss-wallet-menu-wrap ss-flex ss-col-center" :style="[bgStyle, { marginLeft: `${data.space}px` }]">
<view class="menu-item ss-flex-1 ss-flex-col ss-row-center ss-col-center" <view class="menu-item ss-flex-1 ss-flex-col ss-row-center ss-col-center"
@tap="sheep.$router.go('/pages/user/wallet/money')"> @tap="sheep.$router.go('/pages/user/wallet/money')">
<view class="value-box ss-flex ss-col-bottom"> <view class="value-box ss-flex ss-col-bottom">
@ -42,8 +42,34 @@
*/ */
import { computed } from 'vue'; import { computed } from 'vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { fen2yuan } from '../../hooks/useGoods'; import { fen2yuan } from '../../hooks/useGoods';
//
const props = defineProps({
//
data: {
type: Object,
default: () => ({}),
},
//
styles: {
type: Object,
default: () => ({}),
},
});
//
const bgStyle = computed(() => {
// props.styles
const { bgType, bgImg, bgColor } = props.styles;
// bgType
return {
background: bgType === 'img'
? `url(${bgImg}) no-repeat top center / 100% 100%`
: bgColor
};
});
const userWallet = computed(() => sheep.$store('user').userWallet); const userWallet = computed(() => sheep.$store('user').userWallet);
const userInfo = computed(() => sheep.$store('user').userInfo); const userInfo = computed(() => sheep.$store('user').userInfo);
const numData = computed(() => sheep.$store('user').numData); const numData = computed(() => sheep.$store('user').numData);

View File

@ -23,6 +23,10 @@ export function isString(value) {
} }
export function isEmpty(value) { export function isEmpty(value) {
if (value === '' || value === undefined || value === null){
return true;
}
if (isArray(value)) { if (isArray(value)) {
return value.length === 0; return value.length === 0;
} }
@ -31,7 +35,7 @@ export function isEmpty(value) {
return Object.keys(value).length === 0; return Object.keys(value).length === 0;
} }
return value === '' || value === undefined || value === null; return false
} }
export function isBoolean(value) { export function isBoolean(value) {

View File

@ -14,7 +14,7 @@ export function showAuthModal(type = 'smsLogin') {
modal.$patch((state) => { modal.$patch((state) => {
state.auth = type; state.auth = type;
}); });
}, 200); }, 500);
closeAuthModal(); closeAuthModal();
} else { } else {
modal.$patch((state) => { modal.$patch((state) => {

View File

@ -1,9 +1,10 @@
/** /**
* 本模块封装微信浏览器下的一些方法 * 本模块封装微信浏览器下的一些方法
* 更多微信网页开发sdk方法,详见:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html * 更多微信网页开发sdk方法,详见:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
* the permission value is offline verifying 报错请参考 @see https://segmentfault.com/a/1190000042289419 解决
*/ */
import jweixin, { ready } from 'weixin-js-sdk'; import jweixin from 'weixin-js-sdk';
import $helper from '@/sheep/helper'; import $helper from '@/sheep/helper';
import AuthUtil from '@/sheep/api/member/auth'; import AuthUtil from '@/sheep/api/member/auth';
@ -38,7 +39,7 @@ export default {
timestamp: data.timestamp, timestamp: data.timestamp,
nonceStr: data.nonceStr, nonceStr: data.nonceStr,
signature: data.signature, signature: data.signature,
jsApiList: ['chooseWXPay'], // TODO 芋艿:后续可以设置更多权限; jsApiList: ['chooseWXPay', 'openLocation', 'getLocation','updateTimelineShareData','scanQRCode'], // TODO 芋艿:后续可以设置更多权限;
openTagList: data.openTagList openTagList: data.openTagList
}); });
} }
@ -77,7 +78,7 @@ export default {
}); });
}, },
//获取微信收货地址 TODO 芋艿:未测试 // 获取微信收货地址
openAddress(callback) { openAddress(callback) {
this.isReady(() => { this.isReady(() => {
jweixin.openAddress({ jweixin.openAddress({
@ -137,9 +138,10 @@ export default {
openLocation(data, callback) { openLocation(data, callback) {
this.isReady(() => { this.isReady(() => {
jweixin.openLocation({ jweixin.openLocation({
//根据传入的坐标打开地图 ...data,
latitude: data.latitude, success: function (res) {
longitude: data.longitude, console.log(res);
}
}); });
}); });
}, },

View File

@ -9,7 +9,7 @@
* @param {Object} device - 设备信息 * @param {Object} device - 设备信息
*/ */
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash-es';
// #ifdef H5 // #ifdef H5
import { isWxBrowser } from '@/sheep/helper/utils'; import { isWxBrowser } from '@/sheep/helper/utils';
// #endif // #endif

View File

@ -54,7 +54,6 @@ const mobileLogin = async (e) => {
} else { } else {
return resolve(false); return resolve(false);
} }
// TODO 芋艿shareInfo: uni.getStorageSync('shareLog') || {},
}); });
}; };

View File

@ -24,7 +24,6 @@ async function login(code = '', state = '') {
// 解密 code 发起登陆 // 解密 code 发起登陆
const loginResult = await AuthUtil.socialLogin(socialType, code, state); const loginResult = await AuthUtil.socialLogin(socialType, code, state);
if (loginResult.code === 0) { if (loginResult.code === 0) {
// TODO 芋艿shareLog
setOpenid(loginResult.data.openid); setOpenid(loginResult.data.openid);
return loginResult; return loginResult;
} }
@ -103,5 +102,5 @@ export default {
unbind, unbind,
getInfo, getInfo,
getOpenid, getOpenid,
jssdk: $wxsdk, jsWxSdk: $wxsdk,
}; };

View File

@ -4,13 +4,12 @@
*/ */
import Request from 'luch-request'; import Request from 'luch-request';
import { baseUrl, apiPath, tenantId } from '@/sheep/config'; import { apiPath, baseUrl, tenantId } from '@/sheep/config';
import $store from '@/sheep/store'; import $store from '@/sheep/store';
import $platform from '@/sheep/platform'; import $platform from '@/sheep/platform';
import { import { showAuthModal } from '@/sheep/hooks/useModal';
showAuthModal
} from '@/sheep/hooks/useModal';
import AuthUtil from '@/sheep/api/member/auth'; import AuthUtil from '@/sheep/api/member/auth';
import { getTerminal } from '@/sheep/util/const';
const options = { const options = {
// 显示操作成功消息 默认不显示 // 显示操作成功消息 默认不显示
@ -93,13 +92,12 @@ http.interceptors.request.use(
// 增加 token 令牌、terminal 终端、tenant 租户的请求头 // 增加 token 令牌、terminal 终端、tenant 租户的请求头
const token = getAccessToken(); const token = getAccessToken();
if (token) { if (token) {
config.header['Authorization'] = token; config.header['Authorization'] = token;
} }
// TODO 芋艿:特殊处理 config.header['terminal'] = getTerminal();
config.header['Accept'] = '*/*'; config.header['Accept'] = '*/*';
config.header['tenant-id'] = tenantId; config.header['tenant-id'] = tenantId;
config.header['terminal'] = '20';
// config.header['Authorization'] = 'Bearer test247';
return config; return config;
}, },
(error) => { (error) => {

View File

@ -1,6 +1,6 @@
import $store from '@/sheep/store'; import $store from '@/sheep/store';
import { showAuthModal, showShareModal } from '@/sheep/hooks/useModal'; import { showAuthModal, showShareModal } from '@/sheep/hooks/useModal';
import { isNumber, isString, isEmpty, startsWith, isObject, isNil, clone } from 'lodash'; import { isNumber, isString, isEmpty, startsWith, isObject, isNil, clone } from 'lodash-es';
import throttle from '@/sheep/helper/throttle'; import throttle from '@/sheep/helper/throttle';
const _go = ( const _go = (

View File

@ -57,7 +57,13 @@ const cart = defineStore({
// 移除购物车 // 移除购物车
async delete(ids) { async delete(ids) {
const { code } = await CartApi.deleteCart(ids.join(',')); let idsTemp = '';
if (Array.isArray(ids)) {
idsTemp = ids.join(',');
} else {
idsTemp = ids;
}
const { code } = await CartApi.deleteCart(idsTemp);
if (code === 0) { if (code === 0) {
await this.getList(); await this.getList();
} }

View File

@ -1,6 +1,6 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import $share from '@/sheep/platform/share'; import $share from '@/sheep/platform/share';
import { clone, cloneDeep } from 'lodash'; import { clone, cloneDeep } from 'lodash-es';
import cart from './cart'; import cart from './cart';
import app from './app'; import app from './app';
import { showAuthModal } from '@/sheep/hooks/useModal'; import { showAuthModal } from '@/sheep/hooks/useModal';
@ -68,12 +68,12 @@ const user = defineStore({
// 获取订单、优惠券等其他资产信息 // 获取订单、优惠券等其他资产信息
getNumData() { getNumData() {
OrderApi.getOrderCount().then(res => { OrderApi.getOrderCount().then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.numData.orderCount = res.data; this.numData.orderCount = res.data;
} }
}); });
CouponApi.getUnusedCouponCount().then(res => { CouponApi.getUnusedCouponCount().then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.numData.unusedCouponCount = res.data; this.numData.unusedCouponCount = res.data;
} }
@ -143,20 +143,22 @@ const user = defineStore({
} }
// 绑定推广员 // 绑定推广员
$share.bindBrokerageUser() $share.bindBrokerageUser();
}, },
// 登出系统 // 登出系统
async logout() { async logout() {
this.resetUserData(); this.resetUserData();
return !this.isLogin; return !this.isLogin;
} },
}, },
persist: { persist: {
enabled: true, enabled: true,
strategies: [{ strategies: [
key: 'user-store', {
}] key: 'user-store',
},
],
}, },
}); });

View File

@ -88,7 +88,7 @@
import { reactive, computed } from 'vue'; import { reactive, computed } from 'vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import { clone } from 'lodash'; import { clone } from 'lodash-es';
// //
const state = reactive({ const state = reactive({
@ -196,11 +196,11 @@
const onPreview = () => { const onPreview = () => {
if (!props.isPreview) return; if (!props.isPreview) return;
let previewImage = clone(props.list); let previewImage = clone(props.list);
previewImage.forEach((item,index) => { previewImage.forEach((item, index) => {
if(item.type === 'video') { if (item.type === 'video') {
previewImage.splice(index, 1); previewImage.splice(index, 1);
} }
}) });
uni.previewImage({ uni.previewImage({
urls: urls:
previewImage.length < 1 previewImage.length < 1

View File

@ -1,3 +1,36 @@
// ========== COMMON - 公共模块 ==========
/**
* 与后端Terminal枚举一一对应
*/
export const TerminalEnum = {
UNKNOWN: 0, // 未知, 目的:在无法解析到 terminal 时,使用它
WECHAT_MINI_PROGRAM: 10, //微信小程序
WECHAT_WAP: 11, // 微信公众号
H5: 20, // H5 网页
APP: 31, // 手机 App
};
/**
* uni-app 提供的平台转换为后端所需的 terminal值
*
* @return 终端
*/
export const getTerminal = () => {
const platformType = uni.getSystemInfoSync().uniPlatform;
// 与后端terminal枚举一一对应
switch (platformType) {
case 'app':
return TerminalEnum.APP;
case 'web':
return TerminalEnum.H5;
case 'mp-weixin':
return TerminalEnum.WECHAT_MINI_PROGRAM;
default:
return TerminalEnum.UNKNOWN;
}
};
// ========== MALL - 营销模块 ========== // ========== MALL - 营销模块 ==========
import dayjs from "dayjs"; import dayjs from "dayjs";

BIN
static/images/line.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
static/images/writeOff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB