项目初始化

This commit is contained in:
jerry
2025-01-21 01:46:34 +08:00
parent 364021b042
commit 48153e7761
962 changed files with 172070 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
<!-- 虚拟列表演示(不使用内置列表)(vue) -->
<!-- 写法较简单在页面中对当前需要渲染的虚拟列表数据进行for循环在vue3中兼容性良好 -->
<!-- 在各平台兼容性请查阅https://z-paging.zxlee.cn/module/virtual-list.html -->
<template>
<view class="content">
<!-- 如果页面中的cell高度是固定不变的则不需要设置cell-height-mode如果页面中高度是动态改变的则设置cell-height-mode="dynamic" -->
<!-- 原先的v-model修改为@virtualListChange="virtualListChange"并赋值处理后的虚拟列表 -->
<z-paging ref="paging" :auto="false" use-virtual-list :force-close-inner-list="true" :paging-style="{ paddingTop: 0 + 'px', paddingBottom: paddingBottom + 'rpx' }" cell-height-mode="dynamic" @scroll="scroll" @virtualListChange="virtualListChange" @query="queryList">
<!-- 需要固定在顶部不滚动的view放在slot="top"的view中如果需要跟着滚动则不要设置slot="top" -->
<template #top>
<su-navbar :title="title" statusBar></su-navbar>
<tab-box :tabList="tabList" :currentValue="currentIndex" @change="tabChange"></tab-box>
</template>
<!-- :id="`zp-id-${item.zp_index}`":key="item.zp_index" 必须写必须写 -->
<!-- 这里for循环的index不是数组中真实的index了请使用item.zp_index获取真实的index -->
<order-list @onCancel="onCancel" @onConfirm="onConfirm" @onOrderConfirm="onOrderConfirm" :virtualList="virtualList"></order-list>
</z-paging>
</view>
</template>
<script>
import TabBox from '@/pages/order/blind/components/tabBox.vue';
import OrderList from '@/pages/order/blind/components/orderList.vue';
import OrderApi from '@/sheep/api/trade/order';
import { WxaSubscribeTemplate } from '@/sheep/util/const';
import sheep from '@/sheep';
import {
formatBlindOrderStatus,
handleBlindOrderButtons,
fen2yuan,
} from '@/sheep/hooks/useGoods';
export default {
components: {
TabBox,
OrderList,
},
props: {
title: {
type: String,
default: '抢单中心',
},
},
data() {
return {
// 虚拟列表数组,通过@virtualListChange监听获得最新数组
virtualList: [],
scrollTop: 0,
paddingTop: 0,
paddingBottom: 0,
height: 0,
tabList: [{
name: '全部',
}, {
name: '待抢单',
value: 10,
}, {
name: '服务中',
value: 20,
}, {
name: '已完成',
value: 30,
}],
currentIndex: 0,
}
},
methods: {
scroll(e) {
this.scrollTop = e.detail.scrollTop;
},
// 监听虚拟列表数组改变并赋值给virtualList进行重新渲染
virtualListChange(vList) {
this.virtualList = vList;
},
queryList(pageNo, pageSize) {
// 组件加载时会自动触发此方法,因此默认页面加载时会自动触发,无需手动调用
// 这里的pageNo和pageSize会自动计算好直接传给服务器即可
// 模拟请求服务器获取分页数据,请替换成自己的网络请求
const params = {
pageNo: pageNo,
pageSize: pageSize,
status: this.tabList[this.currentIndex].value,
commentStatus: this.tabList[this.currentIndex].value === 30 ? false : null,
}
OrderApi.getBlindOrderPage(params).then(res => {
// 将请求的结果数组传递给z-paging
res.data.list.forEach((order) => handleBlindOrderButtons(order, sheep.$store('user').userInfo.id));
res.data.list.forEach((order) => formatBlindOrderStatus(order));
res.data.list.forEach((order) => order.brokeragePrice = fen2yuan(order.brokeragePrice));
this.$refs.paging.complete(res.data.list);
}).catch(res => {
// 如果请求失败写this.$refs.paging.complete(false);
// 注意每次都需要在catch中写这句话很麻烦z-paging提供了方案可以全局统一处理
// 在底层的网络请求抛出异常时写uni.$emit('z-paging-error-emit');即可
this.$refs.paging.complete(false);
})
},
tabChange(e) {
this.currentIndex = e;
this.$refs.paging.reload();
},
// 取消订单
async onCancel(orderId) {
var that = this;
uni.showModal({
title: '提示',
content: '确定要取消订单吗?',
success: async function (res) {
if (!res.confirm) {
return;
}
const { code } = await OrderApi.cancelOrder(orderId);
if (code === 0) {
that.$refs.paging.reload();
}
},
});
},
// 开始接单
async onOrderConfirm(order) {
var that = this;
uni.showModal({
title: '是否确认抢单',
content: '请确认符合老板要求',
success: async function (res) {
if (!res.confirm) {
return;
}
// 正常的确认收货流程
const { code } = await OrderApi.deliveryOrder(order.id);
if (code === 0) {
that.tabChange(2);
}
},
});
},
// 确认收货
async onConfirm(order) {
// #ifdef MP
// 订阅只能由用户主动触发,只能包一层 showModal 诱导用户点击
this.subscribeMessage();
// #endif
var that = this;
uni.showModal({
title: '确定要结束服务么?',
content: '请确认经过老板同意',
success: async function (res) {
if (!res.confirm) {
return;
}
// 正常的确认收货流程
const { code } = await OrderApi.receiveOrder(order.id);
if (code === 0) {
that.$refs.paging.reload();
}
},
});
},
subscribeMessage() {
const event = [WxaSubscribeTemplate.CLERK_ORDER];
event.push(WxaSubscribeTemplate.REWARD_SUCCESS);
sheep.$platform.useProvider('wechat').subscribeMessage(event, () => {
// 订阅后记录一下订阅状态
uni.removeStorageSync(WxaSubscribeTemplate.CLERK_ORDER);
uni.setStorageSync(WxaSubscribeTemplate.CLERK_ORDER, '已订阅');
});
},
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,248 @@
<template>
<view @click="onOrderDetail(order.id)" :id="`zp-id-${order.zp_index}`" :key="order.zp_index" v-for="(order,index) in orderList" class="order-card">
<view class="no-box">
<view class="order-no">
<u-icon name="order"></u-icon>
<text class="number">{{ order.no }}</text>
</view>
<view class="status">{{order.statusStr}}</view>
</view>
<view class="main-box" v-for="item in order.items" :key="item.id">
<view>
<u-avatar size="100" :src="order.avatar"></u-avatar>
</view>
<view class="right-box">
<view class="nickname-box">
<view class="nickname">{{order.nickname}}</view>
<view class="info">订单时间{{order.createTimeStr}}</view>
<view class="info">服务内容{{item.spuName}} {{item.properties.map((property) => property.valueName).join(' ')}}×{{item.count}}</view>
<view class="info" v-if="order.sex == '1'">性别要求女生</view>
<view class="info" v-if="order.sex == '0'">性别要求男生</view>
<view class="info" v-if="userInfo.id == order.workerUserId">
<text class="weixin">微信{{order.weixin}}</text>
<view @tap.stop="onCopy(order.weixin)">
<u-icon name="outline-copy" custom-prefix="iconfont"></u-icon>
</view>
</view>
<view class="info">
<text>剩余时间</text>
<view v-if="order.receiveTime">
<text v-if="order.end">已结束</text>
<view v-else>
<u-count-down :timestamp="order.timestamp" format="DD天HH时mm分ss秒" @end="order.end = true"></u-count-down>
</view>
</view>
<text v-else>暂未接单</text>
</view>
<view class="info" v-if="order.userRemark">备注{{order.userRemark}}</view>
<view class="info" v-if="order.cancelReason">取消原因{{order.cancelReason}}</view>
</view>
<view class="price-box">
<text class="price">{{order.brokeragePrice}}</text>
<text>/</text>
</view>
</view>
</view>
<view class="bottom-box">
<view class="btn-box">
<view v-if="order.buttons.includes('other')" class="btn">也被其它店员抢单</view>
<view v-if="order.buttons.includes('detail')" @tap.stop="onOrderDetail(order.id)" class="btn">查看详情</view>
<view v-if="order.buttons.includes('unpay')" @tap.stop="onOrderCancel(order.id, order.items[0].id)" class="btn">取消接单</view>
<view v-if="order.buttons.includes('cancel')" @tap.stop="onCancel(order.id)" class="btn">取消订单</view>
<view v-if="order.buttons.includes('invite')" @tap.stop="onBuy(order.items[0].spuId)" class="btn active">邀请好友抢单</view>
<view v-if="order.buttons.includes('invite2')" @tap.stop="onBuy(order.items[0].spuId)" class="btn active">邀请评价</view>
<view v-if="order.buttons.includes('agree')" @tap.stop="onOrderConfirm(order)" class="btn active">立即抢单</view>
<view v-if="order.buttons.includes('confirm')" @tap.stop="onConfirm(order)" class="btn active">结束服务</view>
</view>
</view>
</view>
</template>
<script>
import sheep from '@/sheep';
import dayjs from 'dayjs';
export default {
components: {
},
props: {
virtualList: {
type: Array,
default: [],
},
},
emits: ["onCancel", "onConfirm", "onOrderConfirm"],
data() {
return {
}
},
computed: {
userInfo: {
get() {
return sheep.$store('user').userInfo;
},
},
orderList() {
this.virtualList.forEach((order) => order.createTimeStr = sheep.$helper.timeFormat(order.createTime, 'yyyy-mm-dd hh:MM:ss'));
this.virtualList.forEach((order) => order.timestamp = order.receiveTime ? sheep.$helper.parseTimeData(order.receiveTime) : 0);
return this.virtualList;
},
},
methods: {
// 订单详情
onOrderDetail(id) {
sheep.$router.go('/pages/order/blind/detail', {
id,
});
},
onCancel(orderId) {
this.$emit('onCancel', orderId);
},
onBuy(id) {
sheep.$router.go('/pages/clerk/detail/index', {
id,
});
},
// 评价
onComment(id) {
sheep.$router.go('/pages/goods/comment/worker/add', {
id,
});
},
// 继续支付
onPay(payOrderId) {
sheep.$router.go('/pages/pay/worker/index', {
id: payOrderId,
});
},
onOrderCancel(orderId, itemId) {
sheep.$router.go('/pages/order/worker/aftersale/apply', {
orderId: orderId,
itemId: itemId
});
},
onOrderConfirm(order) {
this.$emit('onOrderConfirm', order);
},
// 复制
onCopy(text) {
sheep.$helper.copyText(text);
},
// 确认收货
onConfirm(order) {
this.$emit('onConfirm', order);
},
}
}
</script>
<style lang="scss" scoped>
.order-card {
padding: 10px;
margin-top: 10px;
background-color: #fff;
display: flex;
flex-direction: column;
flex: 1;
.no-box {
display: flex;
align-items: center;
justify-content: space-between;
flex: 1;
padding-bottom: 10px;
color: rgb(100, 101, 102);
.order-no {
.number {
margin-left: 5px;
font-size: 22rpx;
}
}
.status {
font-size: 22rpx;
}
}
.main-box {
display: flex;
padding: 10px 0;
border-top: 1px solid #fbf0f0;
border-bottom: 1px solid #fbf0f0;
margin-bottom: 10px;
.right-box {
display: flex;
justify-content: space-between;
flex: 1;
align-items: center;
margin-left: 10px;
.nickname {
font-size: 12px;
font-weight: bold;
margin-bottom: 10px;
}
.info {
font-size: 22rpx;
color: rgb(100, 101, 102);
line-height: 22rpx;
margin-bottom: 5px;
display: flex;
align-items: center;
.weixin {
margin-right: 5px;
}
}
.price-box {
font-size: 22rpx;
color: rgb(100, 101, 102);
.price {
font-size: 34rpx;
font-weight: bold;
color: var(--ui-BG-Main);
}
}
}
}
.bottom-box {
display: flex;
justify-content: flex-end;
.btn-box {
display: flex;
align-items: center;
}
.btn {
background-color: #ddd;
color: rgb(100, 101, 102);
font-size: 22rpx;
border-radius: 40px;
padding: 5px 10px;
margin-left: 10px;
min-width: 140rpx;
display: flex;
justify-content: center;
}
.active {
background-color: var(--ui-BG-Main);
color: #fff;
}
}
}
</style>

View File

@@ -0,0 +1,47 @@
<template>
<view>
<u-tabs :list="tabList" bg-color="#fff" font-size="28" active-color="var(--ui-BG-Main)" :is-scroll="false" v-model="current" @change="change"></u-tabs>
</view>
</template>
<script>
export default {
components: {
},
props: {
currentValue: 0,
tabList: {
type: Array,
default: [],
},
},
data() {
return {
}
},
computed: {
current: {
get() {
return this.currentValue;
},
set(newValue) {
}
},
},
methods: {
change(index) {
this.$emit('change', index);
},
}
}
</script>
<style lang="scss" scoped>
.tab-box {
background-color: #fff;
padding: 5px 0;
}
</style>

View File

@@ -0,0 +1,435 @@
<template>
<s-layout title="确认订单">
<!-- 商品信息 -->
<view class="order-card-box ss-m-b-14">
<s-goods-item
v-for="item in state.orderInfo.items"
:key="item.skuId"
:img="item.picUrl"
:title="item.spuName"
:skuText="item.properties.map((property) => property.valueName).join(' ')"
:price="item.price"
:num="item.count"
marginBottom="10"
/>
<view class="order-item ss-flex ss-col-center ss-row-between ss-p-x-20 bg-white ss-r-10">
<view class="item-title">订单备注</view>
<view class="ss-flex ss-col-center">
<uni-easyinput
maxlength="20"
placeholder="请输入备注内容"
v-model="state.orderPayload.remark"
:inputBorder="false"
:clearable="false"
/>
</view>
</view>
</view>
<!-- 价格信息 -->
<view class="bg-white total-card-box ss-p-20 ss-m-b-14 ss-r-10">
<view class="total-box-content border-bottom">
<view class="order-item ss-flex ss-col-center ss-row-between">
<view class="item-title">技能金额</view>
<view class="ss-flex ss-col-center">
<text class="item-value ss-m-r-24">
{{ fen2yuan(state.orderInfo.price.totalPrice) }}
</text>
</view>
</view>
<view
class="order-item ss-flex ss-col-center ss-row-between"
v-if="state.orderInfo.type === 6"
>
<view class="item-title">积分抵扣</view>
<view class="ss-flex ss-col-center">
{{ state.pointStatus ? '剩余积分' : '当前积分' }}
<image
:src="sheep.$url.static('/static/img/shop/goods/score1.svg')"
class="score-img"
/>
<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 v-if="isPass" class="order-item ss-flex ss-col-center ss-row-between">
<view class="item-title">TA添加你的微信号</view>
<view class="ss-flex ss-col-center">
<uni-easyinput
maxlength="20"
placeholder="输入正确微信号"
v-model="state.orderInfo.weixin"
:inputBorder="false"
:clearable="false"
/>
</view>
</view>
<!-- 优惠劵只有 type = 0 普通订单非拼团秒杀砍价才可以使用优惠劵 -->
<view
class="order-item ss-flex ss-col-center ss-row-between"
v-if="state.orderInfo.type === 6"
>
<view class="item-title">优惠券</view>
<view class="ss-flex ss-col-center" @tap="state.showCoupon = true">
<text class="item-value text-red" v-if="state.orderPayload.couponId > 0">
-{{ fen2yuan(state.orderInfo.price.couponPrice) }}
</text>
<text
class="item-value"
:class="state.couponInfo.length > 0 ? 'text-red' : 'text-disabled'"
v-else
>
{{
state.couponInfo.length > 0 ? state.couponInfo.length + ' 张可用' : '暂无可用优惠券'
}}
</text>
<text class="_icon-forward item-icon" />
</view>
</view>
<view
class="order-item ss-flex ss-col-center ss-row-between"
v-if="state.orderInfo.price.discountPrice > 0"
>
<view class="item-title">活动优惠</view>
<view class="ss-flex ss-col-center">
<!-- @tap="state.showDiscount = true" TODO puhui999折扣后续要把优惠信息打进去 -->
<text class="item-value text-red">
-{{ fen2yuan(state.orderInfo.price.discountPrice) }}
</text>
<text class="_icon-forward item-icon" />
</view>
</view>
<view
class="order-item ss-flex ss-col-center ss-row-between"
v-if="state.orderInfo.price.vipPrice > 0"
>
<view class="item-title">会员优惠</view>
<view class="ss-flex ss-col-center">
<text class="item-value text-red">
-{{ fen2yuan(state.orderInfo.price.vipPrice) }}
</text>
</view>
</view>
</view>
<view class="total-box-footer ss-font-28 ss-flex ss-row-right ss-col-center ss-m-r-28">
<view class="total-num ss-m-r-20">
{{ state.orderInfo.items.reduce((acc, item) => acc + item.count, 0) }}
</view>
<view>合计</view>
<view class="total-num text-red"> {{ fen2yuan(state.orderInfo.price.payPrice) }}</view>
</view>
</view>
<!-- 选择优惠券弹框 -->
<s-coupon-select
v-model="state.couponInfo"
:show="state.showCoupon"
@confirm="onSelectCoupon"
@close="state.showCoupon = false"
/>
<!-- 满额折扣弹框 TODO @puhui999折扣后续要把优惠信息打进去 -->
<s-discount-list
v-model="state.orderInfo"
:show="state.showDiscount"
@close="state.showDiscount = false"
/>
<!-- 底部 -->
<su-fixed bottom :opacity="false" bg="bg-white" placeholder :noFixed="false" :index="200">
<view class="footer-box border-top ss-flex ss-row-between ss-p-x-20 ss-col-center">
<view class="total-box-footer ss-flex ss-col-center">
<view class="total-num ss-font-30 text-red">
{{ fen2yuan(state.orderInfo.price.payPrice) }}
</view>
</view>
<button
class="ss-reset-button ui-BG-Main-Gradient ss-r-40 submit-btn ui-Shadow-Main"
@tap="onConfirm"
>
提交订单
</button>
</view>
</su-fixed>
<qrcode-modal />
</s-layout>
</template>
<script setup>
import { reactive, ref, watch, computed } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import AddressSelection from '@/pages/order/addressSelection.vue';
import qrcodeModal from '@/components/qrcode-modal/qrcode-modal.vue';
import sheep from '@/sheep';
import OrderApi from '@/sheep/api/trade/order';
import CouponApi from '@/sheep/api/promotion/coupon';
import { fen2yuan } from '@/sheep/hooks/useGoods';
const isPass = computed(() => {
return sheep.$store('user').tradeConfig.weixinEnabled;
});
const state = reactive({
orderPayload: {},
orderInfo: {
items: [], // 商品项列表
price: {}, // 价格信息
},
showCoupon: false, // 是否展示优惠劵
couponInfo: [], // 优惠劵列表
showDiscount: false, // 是否展示营销活动
// ========== 积分 ==========
pointStatus: false, //是否使用积分
});
const addressState = ref({
addressInfo: {}, // 选择的收货地址
deliveryType: 1, // 收货方式1-快递配送2-门店自提
isPickUp: true, // 门店自提是否开启 TODO puhui999: 默认开启,看看后端有开关的话接入
pickUpInfo: {}, // 选择的自提门店信息
weixin: '', // 收件人名称
receiverMobile: '', // 收件人手机
});
// ========== 积分 ==========
/**
* 使用积分抵扣
*/
const changeIntegral = async () => {
state.pointStatus = !state.pointStatus;
await getOrderInfo();
};
// 选择优惠券
async function onSelectCoupon(couponId) {
state.orderPayload.couponId = couponId;
await getOrderInfo();
state.showCoupon = false;
}
// 提交订单
function onConfirm() {
if (isPass && !state.orderInfo.weixin) {
sheep.$helper.toast('请填写正确的微信号');
return;
}
submitOrder();
}
// 创建订单&跳转
async function submitOrder() {
const { code, data } = await OrderApi.createWorkerOrder({
items: state.orderPayload.items,
couponId: state.orderPayload.couponId,
remark: state.orderPayload.remark,
deliveryType: addressState.value.deliveryType,
addressId: addressState.value.addressInfo.id, // 收件地址编号
pickUpStoreId: addressState.value.pickUpInfo.id,//自提门店编号
weixin: state.orderInfo.weixin,// 微信名称
receiverMobile: addressState.value.receiverMobile,// 选择门店自提时,该字段为联系人手机
pointStatus: state.pointStatus,
combinationActivityId: state.orderPayload.combinationActivityId,
combinationHeadId: state.orderPayload.combinationHeadId,
seckillActivityId: state.orderPayload.seckillActivityId,
workerClerkLevelId: state.orderPayload.workerClerkLevelId,
sex: state.orderPayload.sex,
blind: state.orderPayload.blind,
});
if (code !== 0) {
return;
}
// 更新购物车列表,如果来自购物车
if (state.orderPayload.items[0].cartId > 0) {
sheep.$store('cart').getList();
}
// 跳转到支付页面
sheep.$router.redirect('/pages/pay/worker/index', {
id: data.payOrderId,
});
}
// 检查库存 & 计算订单价格
async function getOrderInfo() {
// 计算价格
const { data, code } = await OrderApi.clerkOrder({
items: state.orderPayload.items,
couponId: state.orderPayload.couponId,
deliveryType: addressState.value.deliveryType,
addressId: addressState.value.addressInfo.id, // 收件地址编号
pickUpStoreId: addressState.value.pickUpInfo.id,//自提门店编号
weixin: state.orderInfo.weixin,// 微信名称
receiverMobile: addressState.value.receiverMobile,// 选择门店自提时,该字段为联系人手机
pointStatus: state.pointStatus,
combinationActivityId: state.orderPayload.combinationActivityId,
combinationHeadId: state.orderPayload.combinationHeadId,
seckillActivityId: state.orderPayload.seckillActivityId,
workerClerkLevelId: state.orderPayload.workerClerkLevelId,
});
if (code !== 0) {
return;
}
state.orderInfo = data;
// 设置收货地址
if (state.orderInfo.address) {
addressState.value.addressInfo = state.orderInfo.address;
}
}
// 获取可用优惠券
async function getCoupons() {
const { code, data } = await CouponApi.getMatchCouponList(
state.orderInfo.price.payPrice,
state.orderInfo.items.map((item) => item.spuId),
state.orderPayload.items.map((item) => item.skuId),
state.orderPayload.items.map((item) => item.categoryId),
);
if (code === 0) {
state.couponInfo = data;
}
}
onLoad(async (options) => {
if (!options.data) {
sheep.$helper.toast('参数不正确,请检查!');
return;
}
state.orderPayload = JSON.parse(options.data);
await getOrderInfo();
await getCoupons();
});
// 使用 watch 监听地址和配送方式的变化
watch(addressState, async (newAddress, oldAddress) => {
// 如果收货地址或配送方式有变化,则重新计算价格
if (newAddress.addressInfo.id !== oldAddress.addressInfo.id || newAddress.deliveryType !== oldAddress.deliveryType) {
await getOrderInfo();
}
});
</script>
<style lang="scss" scoped>
:deep() {
.uni-input-wrapper {
width: 320rpx;
}
.uni-easyinput__content-input {
font-size: 28rpx;
height: 72rpx;
text-align: right !important;
padding-right: 0 !important;
.uni-input-input {
font-weight: 500;
color: #333333;
font-size: 26rpx;
height: 32rpx;
margin-top: 4rpx;
}
}
.uni-easyinput__content {
display: flex !important;
align-items: center !important;
justify-content: right !important;
}
}
.score-img {
width: 36rpx;
height: 36rpx;
margin: 0 4rpx;
}
.order-item {
height: 80rpx;
.item-title {
font-size: 28rpx;
font-weight: 400;
}
.item-value {
font-size: 28rpx;
font-weight: 500;
font-family: OPPOSANS;
}
.text-disabled {
color: #bbbbbb;
}
.item-icon {
color: $dark-9;
}
.remark-input {
text-align: right;
}
.item-placeholder {
color: $dark-9;
font-size: 26rpx;
text-align: right;
}
}
.total-box-footer {
height: 90rpx;
.total-num {
color: #333333;
font-family: OPPOSANS;
}
}
.footer-box {
height: 100rpx;
.submit-btn {
width: 240rpx;
height: 70rpx;
font-size: 28rpx;
font-weight: 500;
.goto-pay-text {
line-height: 28rpx;
}
}
.cancel-btn {
width: 240rpx;
height: 80rpx;
font-size: 26rpx;
background-color: #e5e5e5;
color: $dark-9;
}
}
.title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
}
.subtitle {
font-size: 28rpx;
color: #999999;
}
.cicon-checkbox {
font-size: 36rpx;
color: var(--ui-BG-Main);
}
.cicon-box {
font-size: 36rpx;
color: #999999;
}
</style>

View File

@@ -0,0 +1,599 @@
<!-- 订单详情 -->
<template>
<s-layout title="订单详情" class="index-wrap" navbar="inner">
<!-- 订单状态 TODO -->
<view
class="state-box ss-flex-col ss-col-center ss-row-right"
:style="[
{
marginTop: '-' + Number(statusBarHeight + 88 + 22) + 'rpx',
paddingTop: Number(statusBarHeight + 88 + 22) + 'rpx',
},
]"
>
<view class="ss-flex ss-m-t-32 ss-m-b-20">
<image
v-if="
state.orderInfo.status_code == 'unpaid' ||
state.orderInfo.status === 10 || // 待发货
state.orderInfo.status_code == 'nocomment'
"
class="state-img"
:src="sheep.$url.static('/static/img/shop/order/order_loading.png')"
>
</image>
<image
v-if="
state.orderInfo.status_code == 'completed' ||
state.orderInfo.status_code == 'refund_agree'
"
class="state-img"
:src="sheep.$url.static('/static/img/shop/order/order_success.png')"
>
</image>
<image
v-if="state.orderInfo.status_code == 'cancel' || state.orderInfo.status_code == 'closed'"
class="state-img"
:src="sheep.$url.static('/static/img/shop/order/order_close.png')"
>
</image>
<image
v-if="state.orderInfo.status_code == 'noget'"
class="state-img"
:src="sheep.$url.static('/static/img/shop/order/order_express.png')"
>
</image>
<view class="ss-font-30">{{ formatBlindOrderStatus(state.orderInfo) }}</view>
</view>
<view class="ss-font-26 ss-m-x-20 ss-m-b-70">
{{ formatBlindOrderStatusDescription(state.orderInfo) }}
</view>
</view>
<!-- 收货地址 -->
<view class="order-address-box" v-if="state.orderInfo.receiverAreaId > 0">
<view class="ss-flex ss-col-center">
<text class="address-username">
{{ state.orderInfo.receiverName }}
</text>
<text class="address-phone">{{ state.orderInfo.receiverMobile }}</text>
</view>
<view class="address-detail">
{{ state.orderInfo.receiverAreaName }} {{ state.orderInfo.receiverDetailAddress }}
</view>
</view>
<view
class="detail-goods"
:style="[{ marginTop: state.orderInfo.receiverAreaId > 0 ? '0' : '-40rpx' }]"
>
<!-- 订单信 -->
<view class="order-list" v-for="item in state.orderInfo.items" :key="item.goods_id">
<view class="order-card">
<s-goods-item
@tap="onGoodsDetail(item.spuId)"
:img="item.picUrl"
:title="item.spuName"
:skuText="item.properties.map((property) => property.valueName).join(' ')"
:price="item.price"
:num="item.count"
>
<template #tool>
<view class="ss-flex">
<button
class="ss-reset-button apply-btn"
v-if="[10].includes(state.orderInfo.status) && item.afterSaleStatus === 0"
@tap.stop="
sheep.$router.go('/pages/order/blind/list', {
type: 1,
})
"
>
立即抢单
</button>
<button
class="ss-reset-button apply-btn"
v-if="[20].includes(state.orderInfo.status) && item.afterSaleStatus === 0 && state.orderInfo.workerUserId == sheep.$store('user').userInfo.id"
@tap.stop="
sheep.$router.go('/pages/order/worker/aftersale/apply', {
orderId: state.orderInfo.id,
itemId: item.id,
})
"
>
取消接单
</button>
</view>
</template>
<template #priceSuffix>
<button class="ss-reset-button tag-btn" v-if="item.status_text">
{{ item.status_text }}
</button>
</template>
</s-goods-item>
</view>
</view>
</view>
<!-- 自提核销 -->
<PickUpVerify :order-info="state.orderInfo" :systemStore="systemStore" ref="pickUpVerifyRef"></PickUpVerify>
<!-- 订单信息 -->
<view class="notice-box">
<view class="notice-box__content">
<view class="notice-item--center">
<view class="ss-flex ss-flex-1">
<text class="title">订单编号:</text>
<text class="detail">{{ state.orderInfo.no }}</text>
</view>
<button class="ss-reset-button copy-btn" @tap="onCopy">复制</button>
</view>
<view class="notice-item">
<text class="title">下单时间:</text>
<text class="detail">
{{ sheep.$helper.timeFormat(state.orderInfo.createTime, 'yyyy-mm-dd hh:MM:ss') }}
</text>
</view>
<view class="notice-item" v-if="state.orderInfo.payTime">
<text class="title">支付时间:</text>
<text class="detail">
{{ sheep.$helper.timeFormat(state.orderInfo.payTime, 'yyyy-mm-dd hh:MM:ss') }}
</text>
</view>
<view class="notice-item">
<text class="title">支付方式:</text>
<text class="detail">{{ state.orderInfo.payChannelName || '-' }}</text>
</view>
</view>
</view>
<!-- 价格信息 -->
<view class="order-price-box">
<view class="notice-item ss-flex ss-row-between">
<text class="title">订单总额</text>
<view class="ss-flex">
<text class="detail">¥{{ fen2yuan(state.orderInfo.totalPrice) }}</text>
</view>
</view>
<view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.couponPrice > 0">
<text class="title">优惠劵金额</text>
<text class="detail">-¥{{ fen2yuan(state.orderInfo.couponPrice) }}</text>
</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">
<text class="title">活动优惠</text>
<text class="detail">¥{{ fen2yuan(state.orderInfo.discountPrice) }}</text>
</view>
<view class="notice-item ss-flex ss-row-between" v-if="state.orderInfo.vipPrice > 0">
<text class="title">会员优惠</text>
<text class="detail">-¥{{ fen2yuan(state.orderInfo.vipPrice) }}</text>
</view>
<view class="notice-item ss-flex ss-row-between">
<text class="title">用户付款</text>
<text class="detail">¥{{ fen2yuan(state.orderInfo.payPrice) }}</text>
</view>
<view class="notice-item ss-flex ss-row-between">
<text class="title">佣金比例</text>
<text class="detail">{{state.orderInfo.brokeragePercent}}%</text>
</view>
<view class="notice-item all-rpice-item ss-flex ss-m-t-20">
<text class="title">{{ state.orderInfo.status == '30' ? '结算佣金' : '可得佣金' }}</text>
<text class="detail all-price">¥{{ fen2yuan(state.orderInfo.brokeragePrice) }}</text>
</view>
<view
class="notice-item all-rpice-item ss-flex ss-m-t-20"
v-if="state.orderInfo.refundPrice > 0"
>
<text class="title">已退款</text>
<text class="detail all-price">¥{{ fen2yuan(state.orderInfo.refundPrice) }}</text>
</view>
</view>
</s-layout>
</template>
<script setup>
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import { reactive, ref } from 'vue';
import { isEmpty } from 'lodash-es';
import {
fen2yuan,
formatBlindOrderStatus,
formatBlindOrderStatusDescription,
handleOrderButtons,
} from '@/sheep/hooks/useGoods';
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 headerBg = sheep.$url.css('/static/img/shop/order/order_bg.png');
const state = reactive({
orderInfo: {},
merchantTradeNo: '', // 商户订单号
comeinType: '', // 进入订单详情的来源类型
});
// ========== 门店自提(核销) ==========
const systemStore = ref({}); // 门店信息
// 复制
const onCopy = () => {
sheep.$helper.copyText(state.orderInfo.no);
};
// 去支付
function onPay(payOrderId) {
sheep.$router.go('/pages/pay/index', {
id: payOrderId,
});
}
// 查看商品
function onGoodsDetail(id) {
if(id) {
sheep.$router.go('/pages/clerk/detail/index', {
id,
});
}
}
// 取消订单
async function onCancel(orderId) {
uni.showModal({
title: '提示',
content: '确定要取消订单吗?',
success: async function(res) {
if (!res.confirm) {
return;
}
const { code } = await OrderApi.cancelOrder(orderId);
if (code === 0) {
await getOrderDetail(orderId);
}
},
});
}
// 查看物流
async function onExpress(id) {
sheep.$router.go('/pages/order/express/log', {
id,
});
}
// 确认收货
async function onConfirm(orderId, ignore = false) {
// 需开启确认收货组件
// todo: 芋艿:待接入微信
// 1.怎么检测是否开启了发货组件功能如果没有开启的话就不能在这里return出去
// 2.如果开启了走mpConfirm方法,需要在App.vue的show方法中拿到确认收货结果
let isOpenBusinessView = true;
if (
sheep.$platform.name === 'WechatMiniProgram' &&
!isEmpty(state.orderInfo.wechat_extra_data) &&
isOpenBusinessView &&
!ignore
) {
mpConfirm(orderId);
return;
}
// 正常的确认收货流程
const { code } = await OrderApi.receiveOrder(orderId);
if (code === 0) {
await getOrderDetail(orderId);
}
}
// #ifdef MP-WEIXIN
// 小程序确认收货组件
function mpConfirm(orderId) {
if (!wx.openBusinessView) {
sheep.$helper.toast(`请升级微信版本`);
return;
}
wx.openBusinessView({
businessType: 'weappOrderConfirm',
extraData: {
merchant_trade_no: state.orderInfo.wechat_extra_data.merchant_trade_no,
transaction_id: state.orderInfo.wechat_extra_data.transaction_id,
},
success(response) {
console.log('success:', response);
if (response.errMsg === 'openBusinessView:ok') {
if (response.extraData.status === 'success') {
onConfirm(orderId, true);
}
}
},
fail(error) {
console.log('error:', error);
},
complete(result) {
console.log('result:', result);
},
});
}
// #endif
// 评价
function onComment(id) {
sheep.$router.go('/pages/goods/comment/add', {
id,
});
}
const pickUpVerifyRef = ref();
async function getOrderDetail(id) {
// 对详情数据进行适配
let res;
if (state.comeinType === 'wechat') {
// TODO 芋艿:微信场景下
res = await OrderApi.getOrder(id, {
merchant_trade_no: state.merchantTradeNo,
});
} else {
res = await OrderApi.getOrder(id);
}
if (res.code === 0) {
state.orderInfo = res.data;
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 {
sheep.$router.back();
}
}
onLoad(async (options) => {
let id = 0;
if (options.id) {
id = options.id;
}
// TODO 芋艿:下面两个变量,后续接入
state.comeinType = options.comein_type;
if (state.comeinType === 'wechat') {
state.merchantTradeNo = options.merchant_trade_no;
}
await getOrderDetail(id);
});
</script>
<style lang="scss" scoped>
.score-img {
width: 36rpx;
height: 36rpx;
margin: 0 4rpx;
}
.apply-btn {
width: 140rpx;
height: 50rpx;
border-radius: 25rpx;
font-size: 24rpx;
border: 2rpx solid #dcdcdc;
line-height: normal;
margin-left: 16rpx;
}
.state-box {
color: rgba(#fff, 0.9);
width: 100%;
background: v-bind(headerBg) no-repeat,
linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
background-size: 750rpx 100%;
box-sizing: border-box;
.state-img {
width: 60rpx;
height: 60rpx;
margin-right: 20rpx;
}
}
.order-address-box {
background-color: #fff;
border-radius: 10rpx;
margin: -50rpx 20rpx 16rpx 20rpx;
padding: 44rpx 34rpx 42rpx 20rpx;
font-size: 30rpx;
box-sizing: border-box;
font-weight: 500;
color: rgba(51, 51, 51, 1);
.address-username {
margin-right: 20rpx;
}
.address-detail {
font-size: 26rpx;
font-weight: 500;
color: rgba(153, 153, 153, 1);
margin-top: 20rpx;
}
}
.detail-goods {
border-radius: 10rpx;
margin: 0 20rpx 20rpx 20rpx;
.order-list {
margin-bottom: 20rpx;
background-color: #fff;
.order-card {
padding: 20rpx 0;
.order-sku {
font-size: 24rpx;
font-weight: 400;
color: rgba(153, 153, 153, 1);
width: 450rpx;
margin-bottom: 20rpx;
.order-num {
margin-right: 10rpx;
}
}
.tag-btn {
margin-left: 16rpx;
font-size: 24rpx;
height: 36rpx;
color: var(--ui-BG-Main);
border: 2rpx solid var(--ui-BG-Main);
border-radius: 14rpx;
padding: 0 4rpx;
}
}
}
}
// 订单信息。
.notice-box {
background: #fff;
border-radius: 10rpx;
margin: 0 20rpx 20rpx 20rpx;
.notice-box__head {
font-size: 30rpx;
font-weight: 500;
color: rgba(51, 51, 51, 1);
line-height: 80rpx;
border-bottom: 1rpx solid #dfdfdf;
padding: 0 25rpx;
}
.notice-box__content {
padding: 20rpx;
.self-pickup-box {
width: 100%;
.self-pickup--img {
width: 200rpx;
height: 200rpx;
margin: 40rpx 0;
}
}
}
.notice-item,
.notice-item--center {
display: flex;
align-items: center;
line-height: normal;
margin-bottom: 24rpx;
.title {
font-size: 28rpx;
color: #999;
}
.detail {
font-size: 28rpx;
color: #333;
flex: 1;
}
}
}
.copy-btn {
width: 100rpx;
line-height: 50rpx;
border-radius: 25rpx;
padding: 0;
background: rgba(238, 238, 238, 1);
font-size: 22rpx;
font-weight: 400;
color: rgba(51, 51, 51, 1);
}
// 订单价格信息
.order-price-box {
background-color: #fff;
border-radius: 10rpx;
padding: 20rpx;
margin: 0 20rpx 20rpx 20rpx;
.notice-item {
line-height: 70rpx;
.title {
font-size: 28rpx;
color: #999;
}
.detail {
font-size: 28rpx;
color: #333;
font-family: OPPOSANS;
}
}
.all-rpice-item {
justify-content: flex-end;
align-items: center;
.title {
font-size: 26rpx;
font-weight: 500;
color: #333333;
line-height: normal;
}
.all-price {
font-size: 26rpx;
font-family: OPPOSANS;
line-height: normal;
color: $red;
}
}
}
// 底部
.footer-box {
height: 100rpx;
width: 100%;
box-sizing: border-box;
border-radius: 10rpx;
padding-right: 20rpx;
.cancel-btn {
width: 160rpx;
height: 60rpx;
background: #eeeeee;
border-radius: 30rpx;
margin-right: 20rpx;
font-size: 26rpx;
font-weight: 400;
color: #333333;
}
.pay-btn {
width: 160rpx;
height: 60rpx;
font-size: 26rpx;
border-radius: 30rpx;
font-weight: 500;
color: #fff;
}
}
</style>

View File

@@ -0,0 +1,45 @@
<template>
<view class="page-app theme-light main-green font-1">
<layout ref="order"></layout>
<s-menu-tools />
<s-auth-modal />
</view>
</template>
<script>
import layout from '@/pages/order/blind/components/layout.vue';
export default {
components: {
layout,
},
props: {
},
data() {
return {
currentIndex: 0,
}
},
onLoad(options) {
if (options.type) {
this.currentIndex = options.type;
}
this.$nextTick(() => {
this.$refs.order.tabChange(this.currentIndex);
});
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.page-app {
background-color: #fafafa;
padding-bottom: 140rpx;
height: calc(100vh);
padding-bottom: env(safe-area-inset-bottom);
}
</style>