删除冗余代码
This commit is contained in:
parent
fe61729cb4
commit
860ce21b52
@ -1,219 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { getOrderEvaluateListApi, getOrderEvaluateTabsApi } from '@/api/order'
|
||||
import { ref } from 'vue'
|
||||
// 必须导入需要用到的页面生命周期(即使在当前页面上没有直接使用到)
|
||||
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
|
||||
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
|
||||
import type { orderEvaluateListResult } from '@/types/order'
|
||||
import type { TabItem } from '@/types/global'
|
||||
import { fullUrl, onShowRefreshData, previewImg } from '@/utils/common'
|
||||
|
||||
const props = defineProps<{
|
||||
goods_id: string
|
||||
}>()
|
||||
|
||||
const paging = ref()
|
||||
// 类似mixins,如果是页面滚动务必要写这一行,并传入当前ref绑定的paging,注意此处是paging,而非paging.value
|
||||
useZPaging(paging)
|
||||
|
||||
const tabList = ref<TabItem[]>([])
|
||||
const tabIndex = ref(0)
|
||||
const tabsChange = (index: number) => {
|
||||
tabIndex.value = index
|
||||
paging.value.reload()
|
||||
}
|
||||
|
||||
const dataList = ref<orderEvaluateListResult[]>([])
|
||||
const queryList = async (page: number, page_size: number) => {
|
||||
const res = await getOrderEvaluateListApi({
|
||||
page,
|
||||
page_size,
|
||||
goods_id: props.goods_id,
|
||||
type: tabList.value[tabIndex.value]?.value ?? '',
|
||||
})
|
||||
paging.value.complete(res.result.data)
|
||||
}
|
||||
|
||||
const getTabList = async () => {
|
||||
const res = await getOrderEvaluateTabsApi({
|
||||
goods_id: props.goods_id,
|
||||
})
|
||||
tabList.value = res.result.map((item) => ({
|
||||
...item,
|
||||
disabled: false,
|
||||
badge: { count: item.count },
|
||||
}))
|
||||
}
|
||||
|
||||
const groupedImages = (imageList: string[]) => {
|
||||
if (imageList.length <= 0) return []
|
||||
|
||||
const groups = []
|
||||
const images = imageList.map((item) => fullUrl(item))
|
||||
|
||||
for (let i = 0; i < images.length; i += 3) {
|
||||
groups.push(images.slice(i, i + 3))
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
onShowRefreshData(() => {
|
||||
getTabList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<z-paging ref="paging" v-model="dataList" @query="queryList" :safe-area-inset-bottom="true">
|
||||
<template #top>
|
||||
<z-tabs
|
||||
:list="tabList"
|
||||
@change="tabsChange"
|
||||
:current="tabIndex"
|
||||
:badge-style="{ backgroundColor: '#c3c3c3' }"
|
||||
/>
|
||||
</template>
|
||||
<view class="evaluate panel" v-for="(item, index) in dataList" :key="index">
|
||||
<view class="top">
|
||||
<shop-user-avatar class="portrait" :url="item.user_avatar" width="70" />
|
||||
<text class="name">{{ item.user_name }}</text>
|
||||
<text class="time">{{ item.create_time }}</text>
|
||||
</view>
|
||||
<view class="rate">
|
||||
<uni-rate :value="item.score" :size="18" readonly />
|
||||
<text class="spec">{{ item.goods_spec }}</text>
|
||||
</view>
|
||||
<text class="content">{{ item.content }}</text>
|
||||
<view class="content-img">
|
||||
<view class="image-group" v-for="(group, index) in groupedImages(item.images)" :key="index">
|
||||
<image
|
||||
v-for="(img, imgIndex) in group"
|
||||
:key="imgIndex"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
@click="previewImg(img, item.images)"
|
||||
:class="{
|
||||
'first-image': imgIndex === 0 && group.length > 1,
|
||||
'last-image': imgIndex === group.length - 1 && group.length > 1,
|
||||
'single-image': group.length === 1,
|
||||
'middle-image': imgIndex !== 0 && imgIndex !== group.length - 1,
|
||||
}"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bot"> </view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin-top: 10rpx;
|
||||
background-color: #fff;
|
||||
padding: 20rpx;
|
||||
margin: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.portrait {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.name {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.rate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.spec {
|
||||
::before {
|
||||
content: '|';
|
||||
margin: 0 10rpx;
|
||||
}
|
||||
|
||||
color: #666;
|
||||
font-size: 20rpx;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
padding-left: 7rpx;
|
||||
font-size: 26rpx;
|
||||
margin: 20rpx 0rpx;
|
||||
}
|
||||
|
||||
.content-img {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.image-group {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image-group image {
|
||||
height: 200rpx;
|
||||
width: 33%;
|
||||
margin-right: 1.5%;
|
||||
margin-bottom: 10rpx;
|
||||
object-fit: cover;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.image-group image.single-image {
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.image-group image.first-image {
|
||||
border-top-left-radius: 20rpx;
|
||||
border-bottom-left-radius: 20rpx;
|
||||
}
|
||||
|
||||
.image-group image.last-image {
|
||||
border-top-right-radius: 20rpx;
|
||||
border-bottom-right-radius: 20rpx;
|
||||
}
|
||||
|
||||
.image-group image.middle-image {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.image-group image:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,162 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { useAddressStore } from '@/stores/modules/address'
|
||||
import type { addressItem } from '@/types/address'
|
||||
import { pageUrl } from '@/utils/constants'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'close'): void
|
||||
}>()
|
||||
|
||||
//获取地址信息
|
||||
const props = defineProps<{
|
||||
list?: addressItem[]
|
||||
}>()
|
||||
|
||||
const addressStore = useAddressStore()
|
||||
|
||||
//选择收货地址
|
||||
const onSelectAddress = (item: addressItem) => {
|
||||
addressStore.changeSelectedAddress(item)
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const toAddressList = () => {
|
||||
uni.navigateTo({
|
||||
url: `${pageUrl['address-form']}`,
|
||||
})
|
||||
|
||||
emit('close')
|
||||
}
|
||||
|
||||
useAddressStore().changeScene('order')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="address-panel">
|
||||
<text class="close icon-close" @tap="emit('close')"></text>
|
||||
<view class="title">配送至</view>
|
||||
<view class="content">
|
||||
<view v-if="props.list!.length > 0">
|
||||
<view class="item" v-for="item in props.list" :key="item.id" @tap="onSelectAddress(item)">
|
||||
<view class="user">{{ item.name }} {{ item.phone }}</view>
|
||||
<view class="address">{{ item.full_location }} {{ item.address }}</view>
|
||||
<text
|
||||
class="icon"
|
||||
:class="item.id === addressStore.selectedAddress?.id ? 'icon-checked' : 'icon-check'"
|
||||
></text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<z-paging-empty-view
|
||||
empty-view-text="暂无收货地址"
|
||||
:empty-view-style="{
|
||||
top: '50rpx',
|
||||
}"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer">
|
||||
<view class="button primary">
|
||||
<view open-type="navigate" hover-class="navigator-hover" @tap="toAddressList">
|
||||
新增收货地址
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.address-panel {
|
||||
padding: 0 30rpx;
|
||||
border-radius: 10rpx 10rpx 0 0;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.title {
|
||||
line-height: 1;
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: normal;
|
||||
border-bottom: 1rpx solid #ddd;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 24rpx;
|
||||
top: 24rpx;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
min-height: 300rpx;
|
||||
max-height: 540rpx;
|
||||
overflow: auto;
|
||||
padding: 20rpx;
|
||||
|
||||
.item {
|
||||
padding: 30rpx 50rpx 30rpx 60rpx;
|
||||
background-size: 40rpx;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 center;
|
||||
background-image: url('~@/static/images/locate.png');
|
||||
position: relative;
|
||||
}
|
||||
.icon {
|
||||
color: #999;
|
||||
font-size: 40rpx;
|
||||
transform: translateY(-50%);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
}
|
||||
.icon-checked {
|
||||
color: #ff5f3c;
|
||||
}
|
||||
.icon-ring {
|
||||
color: #444;
|
||||
}
|
||||
.user {
|
||||
font-size: 28rpx;
|
||||
color: #444;
|
||||
font-weight: 500;
|
||||
}
|
||||
.address {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
.empty-text {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: #444;
|
||||
|
||||
.button {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
text-align: center;
|
||||
line-height: 72rpx;
|
||||
margin: 0 20rpx;
|
||||
color: #fff;
|
||||
border-radius: 72rpx;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #fff;
|
||||
background-color: #ff5f3c;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
background-color: #ffa868;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,87 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { goodsServiceItem } from '@/types/goods'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'close'): void
|
||||
}>()
|
||||
|
||||
//获取服务与承诺信息
|
||||
const props = defineProps<{
|
||||
list?: goodsServiceItem[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="service-panel">
|
||||
<text class="close icon-close" @tap="emit('close')"></text>
|
||||
<view class="title">服务说明</view>
|
||||
<view class="content">
|
||||
<view class="item" v-for="item in props.list" :key="item.id">
|
||||
<view class="dt">{{ item.name }}</view>
|
||||
<view class="dd">
|
||||
{{ item.content }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.service-panel {
|
||||
padding: 0 30rpx;
|
||||
border-radius: 10rpx 10rpx 0 0;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.title {
|
||||
line-height: 1;
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: normal;
|
||||
border-bottom: 1rpx solid #ddd;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 24rpx;
|
||||
top: 24rpx;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 20rpx 20rpx 150rpx 20rpx;
|
||||
|
||||
.item {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.dt {
|
||||
margin-bottom: 10rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #eaeaea;
|
||||
transform: translateY(-50%);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: -20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.dd {
|
||||
line-height: 1.6;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,153 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { goodsListItem } from '@/types/goods'
|
||||
import { getShareUrl } from './sharePoster'
|
||||
|
||||
const emits = defineEmits<{
|
||||
(event: 'close'): void
|
||||
(event: 'showPoster'): void
|
||||
}>()
|
||||
|
||||
const props = defineProps<{
|
||||
goods: goodsListItem
|
||||
}>()
|
||||
|
||||
const onCpoyLink = () => {
|
||||
const shareUrl = getShareUrl(props.goods.id)
|
||||
|
||||
uni.setClipboardData({
|
||||
data: shareUrl,
|
||||
success: function () {
|
||||
uni.showToast({
|
||||
title: '复制成功',
|
||||
icon: 'none',
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
emits('close')
|
||||
}
|
||||
const onShowSharePoster = () => {
|
||||
emits('close')
|
||||
emits('showPoster')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template v-if="path == ''">
|
||||
<view class="panel">
|
||||
<view class="content">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button class="content-item" open-type="share">
|
||||
<image class="icon" src="/static/icons/wechat.svg" />
|
||||
<view class="text">微信好友</view>
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
<button class="content-item" @tap="onShowSharePoster">
|
||||
<image class="icon" src="/static/icons/poster.svg" />
|
||||
<view class="text">生成海报</view>
|
||||
</button>
|
||||
<button class="content-item" @tap="onCpoyLink">
|
||||
<image class="icon" src="/static/icons/link.svg" />
|
||||
<view class="text">复制链接</view>
|
||||
</button>
|
||||
</view>
|
||||
<view class="footer">
|
||||
<view class="button" @tap="emits('close')"> 取消分享 </view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.panel {
|
||||
padding: 0 30rpx;
|
||||
border-radius: 10rpx 10rpx 0 0;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.title {
|
||||
line-height: 1;
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: normal;
|
||||
border-bottom: 1rpx solid #ddd;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 24rpx;
|
||||
top: 24rpx;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
justify-content: cneter;
|
||||
|
||||
.content-item {
|
||||
margin-top: 20rpx;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0 20px;
|
||||
font-size: 28rpx;
|
||||
color: #444;
|
||||
|
||||
.button {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
text-align: center;
|
||||
line-height: 72rpx;
|
||||
margin: 0 20rpx;
|
||||
border-radius: 72rpx;
|
||||
color: #fff;
|
||||
background-color: #ff5f3c;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
background: #f5f5f5;
|
||||
|
||||
.button {
|
||||
padding: 16rpx 32rpx;
|
||||
margin: 0 10rpx;
|
||||
|
||||
background: #ff784d;
|
||||
color: #ffffff;
|
||||
|
||||
border-radius: 40rpx;
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel {
|
||||
background: #b3b3b3;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,9 +0,0 @@
|
||||
import { useMemberStore, useSettingStore } from '@/stores'
|
||||
import { pageUrl } from '@/utils/constants'
|
||||
|
||||
export const getShareUrl = (goodsId: number | string) => {
|
||||
const settingData = useSettingStore().data
|
||||
const profileData = useMemberStore().profile
|
||||
|
||||
return `${settingData.h5_domain}${pageUrl['goods-detail']}?id=${goodsId}&sharer=${profileData?.id}`
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { useMemberStore } from '@/stores'
|
||||
import type { goodsListItem } from '@/types/goods'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { fullUrl, checkImage, getUserDefaultAvatar } from '@/utils/common'
|
||||
import { getShareInfoApi } from '@/api/goods'
|
||||
import { getShareUrl } from './sharePoster'
|
||||
import { usePopupStore } from '@/stores'
|
||||
|
||||
const props = defineProps<{
|
||||
goods: goodsListItem
|
||||
currentPrice: string
|
||||
currentOriginPrice: string
|
||||
goodsImage: string
|
||||
}>()
|
||||
|
||||
const sharePosterRef = ref()
|
||||
const painter = ref()
|
||||
|
||||
const state = reactive({
|
||||
posterImgPath: '',
|
||||
showPoster: false,
|
||||
userAvatarImg: useMemberStore().profile!.avatar,
|
||||
qrcodeUrl: '',
|
||||
})
|
||||
|
||||
const userImage = ref('')
|
||||
const open = async () => {
|
||||
userImage.value = fullUrl(state.userAvatarImg)
|
||||
await checkImage(userImage.value)
|
||||
.then((url) => {
|
||||
userImage.value = url
|
||||
})
|
||||
.catch(() => {
|
||||
userImage.value = getUserDefaultAvatar()
|
||||
})
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
const shareInfo = await getShareInfoApi(props.goods.id)
|
||||
state.qrcodeUrl = shareInfo.result.qrcode_url
|
||||
// #endif
|
||||
|
||||
await sharePosterRef.value?.open()
|
||||
state.showPoster = true
|
||||
state.posterImgPath = ''
|
||||
await uni.showLoading({
|
||||
title: '海报生成中…',
|
||||
mask: true,
|
||||
})
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
sharePosterRef.value.close()
|
||||
|
||||
state.showPoster = false
|
||||
}
|
||||
|
||||
const onDone = () => {}
|
||||
|
||||
const onSuccess = (imgPath: string) => {
|
||||
state.posterImgPath = imgPath
|
||||
uni.hideLoading()
|
||||
}
|
||||
|
||||
const onSave = () => {
|
||||
// #ifdef MP-WEIXIN
|
||||
painter.value.canvasToTempFilePathSync({
|
||||
fileType: 'jpg',
|
||||
pathType: 'url',
|
||||
quality: 1,
|
||||
success: (res: any) => {
|
||||
console.log(res.tempFilePath)
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'none',
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err)
|
||||
if (err.errMsg == 'saveImageToPhotosAlbum:fail cancel') {
|
||||
return uni.showToast({
|
||||
title: '取消保存',
|
||||
icon: 'none',
|
||||
})
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
},
|
||||
complete: () => {
|
||||
close()
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// #endif
|
||||
}
|
||||
|
||||
defineExpose({ sharePosterRef, open, close })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<uni-popup
|
||||
ref="sharePosterRef"
|
||||
type="center"
|
||||
:animation="false"
|
||||
:mask-click="false"
|
||||
@change="usePopupStore().onChangePopupProps"
|
||||
>
|
||||
<image :src="state.posterImgPath" mode="widthFix" style="width: 600rpx" />
|
||||
<view>
|
||||
<view class="poster">
|
||||
<l-painter
|
||||
ref="painter"
|
||||
isCanvasToTempFilePath
|
||||
@success="onSuccess"
|
||||
css="width: 750rpx; padding-bottom: 40rpx; background: #edca88; object-fit: contain;"
|
||||
hidden
|
||||
@done="onDone"
|
||||
:after-delay="500"
|
||||
>
|
||||
<l-painter-image
|
||||
:src="userImage"
|
||||
css="margin-left: 40rpx; margin-top: 40rpx; width: 100rpx; height: 100rpx; border-radius: 50%;"
|
||||
/>
|
||||
<l-painter-view css="margin-top: 40rpx; padding-left: 20rpx; display: inline-block">
|
||||
<l-painter-text
|
||||
:text="useMemberStore().profile?.nickname || ''"
|
||||
css="display: block; padding-bottom: 20rpx; color: #000; font-size: 32rpx; fontWeight: bold"
|
||||
/>
|
||||
<l-painter-text text="推荐了一个好物给您" css="color: #000; font-size: 24rpx" />
|
||||
</l-painter-view>
|
||||
<l-painter-view
|
||||
css="margin-left: 40rpx; margin-top: 30rpx; padding: 32rpx; box-sizing: border-box; background: #fff; border-radius: 16rpx; width: 670rpx; box-shadow: 0 20rpx 58rpx rgba(0,0,0,.15)"
|
||||
>
|
||||
<l-painter-image
|
||||
:src="fullUrl(props.goodsImage)"
|
||||
css="object-fit: fill; width: 600rpx; max-height: 550rpx; border-radius: 12rpx;"
|
||||
/>
|
||||
<l-painter-view
|
||||
css="margin-top: 32rpx; color: #FF0000; font-weight: bold; font-size: 28rpx; line-height: 1em;"
|
||||
>
|
||||
<l-painter-text text="¥" css="vertical-align: bottom" />
|
||||
<l-painter-text
|
||||
:text="`${props.currentPrice.split('.')[0]}`"
|
||||
css="vertical-align: bottom; font-size: 58rpx"
|
||||
/>
|
||||
<l-painter-text
|
||||
:text="`.${props.currentPrice.split('.')[1]}`"
|
||||
css="vertical-align: bottom"
|
||||
/>
|
||||
<l-painter-text
|
||||
:text="`¥${props.currentOriginPrice}`"
|
||||
css="vertical-align: bottom; padding-left: 10rpx; font-weight: normal; text-decoration: line-through; color: #999999"
|
||||
/>
|
||||
</l-painter-view>
|
||||
<l-painter-view css="margin-top: 30rpx">
|
||||
<l-painter-text
|
||||
:text="props.goods.name"
|
||||
css="line-clamp: 2; color: #333333; line-height: 1.5em; width: 620rpx; font-size: 30rpx; padding-right:32rpx; box-sizing: border-box"
|
||||
></l-painter-text>
|
||||
</l-painter-view>
|
||||
<l-painter-view css="margin-top: 30rpx; display: flex; flex-wrap: nowrap;">
|
||||
<l-painter-text
|
||||
text="长按或扫一扫识别二维码"
|
||||
css="float: left; padding-bottom: 10rpx; color: #999999; font-size: 26rpx; margin-top: 60rpx; margin-right: 160rpx"
|
||||
/>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<l-painter-image
|
||||
v-if="state.qrcodeUrl"
|
||||
:src="state.qrcodeUrl"
|
||||
css="float: right; width: 150rpx; height: 150rpx;"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef WEB -->
|
||||
<l-painter-qrcode
|
||||
:text="getShareUrl(props.goods.id)"
|
||||
css="float: right; width: 150rpx; height: 150rpx;"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
</l-painter-view>
|
||||
</l-painter-view>
|
||||
</l-painter>
|
||||
</view>
|
||||
<view class="action" v-if="state.posterImgPath">
|
||||
<view class="button cancel" @tap="close()">取消分享</view>
|
||||
<!-- #ifdef WEB -->
|
||||
<view>长按图片进行保存</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="button" @tap="onSave()">保存图片</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
background: #f5f5f5;
|
||||
|
||||
.button {
|
||||
padding: 16rpx 32rpx;
|
||||
margin: 0 10rpx;
|
||||
|
||||
background: #ff784d;
|
||||
color: #ffffff;
|
||||
|
||||
border-radius: 40rpx;
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel {
|
||||
background: #b3b3b3;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,864 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { getGoodsByIdApi } from '@/api/goods'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ref, watchEffect, reactive, computed } from 'vue'
|
||||
import type { goodsResult, goodsServiceItem, skuItem } from '@/types/goods'
|
||||
import type {
|
||||
SkuPopupEvent,
|
||||
SkuPopupInstance,
|
||||
SkuPopupLocaldata,
|
||||
} from '@/components/vk-data-goods-sku-popup/vk-data-goods-sku-popup'
|
||||
import { addCartApi } from '@/api/cart'
|
||||
import { useAddressStore } from '@/stores/modules/address'
|
||||
import { previewImg, fullUrl, arrayFullUrl, onShowRefreshData } from '@/utils/common'
|
||||
import { pageUrl, safeBottom, pageMateStyle, isIOSWithHomeIndicator } from '@/utils/constants'
|
||||
import { useCartStore } from '@/stores'
|
||||
import { postAddFavoriteApi, postCancelFavoriteApi } from '@/api/favorite'
|
||||
import _, { debounce } from 'lodash'
|
||||
import { onShareAppMessage } from '@dcloudio/uni-app'
|
||||
import { useMemberStore } from '@/stores'
|
||||
import { getAddressApi } from '@/api/address'
|
||||
import type { addressItem } from '@/types/address'
|
||||
import { computeConversion } from '@/utils/common'
|
||||
import servicePanel from '@/pagesGoods/pages/goods/components/servicePanel.vue'
|
||||
import addressPanel from '@/pagesGoods/pages/goods/components//addressPanel.vue'
|
||||
import sharePanel from '@/pagesGoods/pages/goods/components/sharePanel.vue'
|
||||
import sharePoster from '@/pagesGoods/pages/goods/components/sharePoster.vue'
|
||||
import { usePopupStore } from '@/stores'
|
||||
|
||||
const props = defineProps<{
|
||||
id: string | number
|
||||
scene?: string
|
||||
}>()
|
||||
|
||||
//接收参数
|
||||
const query = reactive({
|
||||
id: props.id,
|
||||
scene: props.scene,
|
||||
})
|
||||
|
||||
const cartStore = useCartStore()
|
||||
|
||||
//商品sku弹窗
|
||||
const isShowSku = ref(false)
|
||||
//商品sku数据
|
||||
const skuData = ref({} as SkuPopupLocaldata)
|
||||
//sku按钮模式
|
||||
enum skuskuModeType {
|
||||
both = 1,
|
||||
cart = 2,
|
||||
buy = 3,
|
||||
}
|
||||
const skuMode = ref<skuskuModeType>(skuskuModeType.both)
|
||||
//sku弹窗ref实例
|
||||
const skuPopupRef = ref<SkuPopupInstance>()
|
||||
//sku中被选中的值
|
||||
const selectAttrText = computed(() => {
|
||||
return skuPopupRef.value?.selectArr?.join(' ').trim() || '请选择商品规格'
|
||||
})
|
||||
// 计算当前显示的价格
|
||||
const selectedPrice = ref(0)
|
||||
const currentPrice = computed(() => selectedPrice.value || goods.value?.min_price || 0)
|
||||
// 计算当前显示的划线价
|
||||
const selectedOriginPrice = ref(0)
|
||||
const currentOriginPrice = computed(
|
||||
() => selectedOriginPrice.value || goods.value?.max_origin_price || 0,
|
||||
)
|
||||
|
||||
//打开sku弹窗
|
||||
const openSkuPopup = (mode: skuskuModeType) => {
|
||||
isShowSku.value = true
|
||||
skuMode.value = mode
|
||||
}
|
||||
|
||||
const goodsServiceList = ref<goodsServiceItem[]>([])
|
||||
const goodsServiceText = ref('')
|
||||
//商品详情
|
||||
const goods = ref<goodsResult>()
|
||||
const getGoodsDataById = async () => {
|
||||
const res = await getGoodsByIdApi(Number(query.id))
|
||||
goods.value = res.result
|
||||
|
||||
//设置sku组件所需数据
|
||||
skuData.value = {
|
||||
_id: res.result.id.toString(),
|
||||
name: res.result.name,
|
||||
goods_thumb: fullUrl(res.result.images[0]),
|
||||
spec_list: res.result.spec_list.map((spec) => {
|
||||
return {
|
||||
name: spec.name,
|
||||
list: spec.value,
|
||||
}
|
||||
}),
|
||||
sku_list: res.result.sku_list.map((sku) => {
|
||||
return {
|
||||
_id: sku.id.toString(),
|
||||
goods_id: res.result.id.toString(),
|
||||
goods_name: res.result.name,
|
||||
image: fullUrl(sku.image),
|
||||
price: sku.price,
|
||||
origin_price: sku.origin_price,
|
||||
sku_name_arr: sku.spec?.map((sku_name) => sku_name.value),
|
||||
stock: sku.stock,
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
//服务与承诺
|
||||
goodsServiceList.value = goods.value.goods_service
|
||||
goodsServiceText.value = goods.value.goods_service.map((item) => item.name).join(' ')
|
||||
}
|
||||
|
||||
//更新轮播图下标
|
||||
const currentIndex = ref(0)
|
||||
const updateIndex: UniHelper.SwiperOnChange = (event) => {
|
||||
currentIndex.value = event.detail.current
|
||||
}
|
||||
//弹出层ref
|
||||
const popup = ref<{
|
||||
open: (type?: UniHelper.UniPopupType) => void
|
||||
close: () => void
|
||||
}>()
|
||||
//调用弹出层
|
||||
const popupName = ref<'address' | 'service' | 'share' | 'sharePoster'>()
|
||||
const openPopup = (name: typeof popupName.value) => {
|
||||
popupName.value = name
|
||||
popup.value?.open()
|
||||
}
|
||||
|
||||
//添加购物车
|
||||
const onAddCart = (event: SkuPopupEvent) => {
|
||||
addCartApi({
|
||||
sku_id: event._id,
|
||||
num: event.buy_num,
|
||||
}).then((res) => {
|
||||
cartStore.setCartTotalNum(res.result.total_num)
|
||||
cartInfo.value = res.result.total_num
|
||||
isShowSku.value = false
|
||||
uni.showToast({
|
||||
title: '加入购物车成功',
|
||||
icon: 'success',
|
||||
mask: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//跳转下单页
|
||||
const onBuyNow = (event: SkuPopupEvent) => {
|
||||
console.log(event)
|
||||
isShowSku.value = false
|
||||
uni.navigateTo({
|
||||
url: `${pageUrl['order-create']}?sku_id=${event._id}&count=${event.buy_num}`,
|
||||
})
|
||||
}
|
||||
|
||||
//规格选择后处理
|
||||
const onSpecSelected = (skuData: skuItem) => {
|
||||
selectedPrice.value = skuData.price
|
||||
selectedOriginPrice.value = skuData.origin_price
|
||||
}
|
||||
|
||||
// 收货地址
|
||||
const addressStore = useAddressStore()
|
||||
const selectAddressText = computed(() => {
|
||||
return addressStore.selectedAddress
|
||||
? addressStore.selectedAddress.full_location
|
||||
: '请选择收货地址'
|
||||
})
|
||||
|
||||
const addressList = ref<addressItem[]>([])
|
||||
const openAddressPopup = () => {
|
||||
getAddressApi().then((res) => {
|
||||
addressList.value = res.result.data
|
||||
openPopup('address')
|
||||
})
|
||||
}
|
||||
|
||||
const getMoreEvaluate = () => {
|
||||
uni.navigateTo({
|
||||
url: `${pageUrl['goods-evaluate']}?goods_id=${query.id}`,
|
||||
})
|
||||
}
|
||||
|
||||
const cartInfo = ref(0)
|
||||
const menuButtons = computed<UniHelper.UniGoodsNavOption[]>(() => [
|
||||
{
|
||||
icon: 'shop',
|
||||
text: '首页',
|
||||
},
|
||||
{
|
||||
icon: 'cart',
|
||||
text: '购物车',
|
||||
info: cartInfo.value,
|
||||
},
|
||||
{
|
||||
icon: 'redo',
|
||||
text: '分享',
|
||||
},
|
||||
])
|
||||
const menuClick = (event: UniHelper.UniGoodsNavOnClickEvent) => {
|
||||
switch (event.index) {
|
||||
case 0:
|
||||
uni.switchTab({
|
||||
url: `${pageUrl['index']}`,
|
||||
})
|
||||
break
|
||||
case 1:
|
||||
uni.switchTab({
|
||||
url: `${pageUrl['cart']}`,
|
||||
})
|
||||
break
|
||||
case 2:
|
||||
showSharePopup()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const operateButtons: UniHelper.UniGoodsNavButton[] = [
|
||||
{
|
||||
text: '加入购物车',
|
||||
backgroundColor: '#ffa200',
|
||||
color: '#fff',
|
||||
},
|
||||
{
|
||||
text: '立即购买',
|
||||
backgroundColor: '#ff0000',
|
||||
color: '#fff',
|
||||
},
|
||||
]
|
||||
const buttonClick = (event: UniHelper.UniGoodsNavOnButtonClickEvent) => {
|
||||
switch (event.index) {
|
||||
case 0:
|
||||
openSkuPopup(skuskuModeType.cart)
|
||||
break
|
||||
case 1:
|
||||
openSkuPopup(skuskuModeType.buy)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const changeFavoriteStatus = debounce((goods) => {
|
||||
if (goods.favorite === true) {
|
||||
postCancelFavoriteApi({
|
||||
goods_id: goods.id,
|
||||
}).then((res) => {
|
||||
if (res.result) {
|
||||
goods.favorite = false
|
||||
uni.showToast({
|
||||
title: '商品取消收藏',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
postAddFavoriteApi({
|
||||
goods_id: goods.id,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.result) {
|
||||
goods.favorite = true
|
||||
uni.showToast({
|
||||
title: '商品收藏成功',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
}, 500)
|
||||
|
||||
watchEffect(() => {
|
||||
cartInfo.value = useCartStore().cartTotalNum
|
||||
})
|
||||
|
||||
const sharePopupRef = ref()
|
||||
// 分享弹窗
|
||||
const showSharePopup = () => {
|
||||
if (!useMemberStore().profile?.id) {
|
||||
return uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '登录解锁更多精彩,是否继续?',
|
||||
confirmText: '去登录',
|
||||
cancelText: '再看看',
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({ url: pageUrl['login'] })
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
openPopup('share')
|
||||
}
|
||||
|
||||
const sharePosterRef = ref()
|
||||
const goodsImage = ref('')
|
||||
const onShowPoster = () => {
|
||||
sharePosterRef.value.open()
|
||||
goodsImage.value = goods.value!.images[currentIndex.value] || goods.value!.images[0]
|
||||
}
|
||||
|
||||
onShareAppMessage(() => {
|
||||
sharePopupRef.value.close()
|
||||
|
||||
const sharePath = `${pageUrl['goods-detail']}?id=${goods.value?.id}&sharer=${
|
||||
useMemberStore().profile?.id
|
||||
}`
|
||||
console.log(sharePath)
|
||||
|
||||
return {
|
||||
title: goods.value?.name,
|
||||
path: sharePath,
|
||||
imageUrl: fullUrl(goods.value!.images[0]),
|
||||
}
|
||||
})
|
||||
|
||||
onShowRefreshData(() => {
|
||||
if (props.scene) {
|
||||
const params = decodeURIComponent(props.scene)
|
||||
.split('&')
|
||||
.reduce((acc: { [key: string]: any }, curr) => {
|
||||
const [key, value] = curr.split('=')
|
||||
acc[key] = value
|
||||
return acc
|
||||
}, {})
|
||||
console.log('解析scene', params)
|
||||
if (params.id) {
|
||||
query.id = params.id
|
||||
}
|
||||
}
|
||||
|
||||
getGoodsDataById()
|
||||
})
|
||||
|
||||
onLoad(() => {
|
||||
// #ifdef WEB
|
||||
// 兼容带有底部导航的IOS设备
|
||||
if (isIOSWithHomeIndicator()) {
|
||||
document.documentElement.classList.add('ios-device')
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.showShareMenu({
|
||||
withShareTicket: true,
|
||||
menus: ['shareAppMessage'],
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<page-meta :page-style="pageMateStyle">
|
||||
<!-- #endif -->
|
||||
<scroll-view scroll-y class="viewport" :style="safeBottom" v-if="goods">
|
||||
<!-- 基本信息 -->
|
||||
<view class="goods">
|
||||
<!-- 商品主图 -->
|
||||
<view class="preview">
|
||||
<swiper circular @change="updateIndex" autoplay>
|
||||
<swiper-item v-for="item in goods?.images" :key="item">
|
||||
<image
|
||||
class="image"
|
||||
:src="fullUrl(item)"
|
||||
@tap="previewImg(item, arrayFullUrl(goods!.images))"
|
||||
/>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view class="indicator">
|
||||
<text class="current">{{ currentIndex + 1 }}</text>
|
||||
<text class="split">/</text>
|
||||
<text class="total">{{ goods?.images.length }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel">
|
||||
<view class="meta">
|
||||
<view class="meta-top">
|
||||
<view class="price">
|
||||
<view class="origin">¥{{ currentOriginPrice }} </view>
|
||||
<view class="current">
|
||||
<text class="symbol">¥</text>
|
||||
<text class="number">{{ currentPrice }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="operate">
|
||||
<view class="operate-item" @tap="changeFavoriteStatus(goods!)" v-if="goods.favorite">
|
||||
<button class="operate-item-btn">
|
||||
<text class="icon icon-favorite-checked" />
|
||||
</button>
|
||||
<text class="icon-text">已收藏</text>
|
||||
</view>
|
||||
<view class="operate-item" @tap="changeFavoriteStatus(goods!)" v-else>
|
||||
<button class="operate-item-btn">
|
||||
<text class="icon icon-favorite-default" />
|
||||
</button>
|
||||
<text class="icon-text">收藏</text>
|
||||
</view>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="operate-item">
|
||||
<button open-type="contact" class="operate-item-btn">
|
||||
<text class="icon-handset"></text>
|
||||
</button>
|
||||
<view class="icon-text">
|
||||
<text>客服</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
<view class="name-box">
|
||||
<view class="name ellipsis">{{ goods?.name }} </view>
|
||||
<view class="sales">
|
||||
已售{{ computeConversion(goods.sales_init + goods.sales_real) }}</view
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel">
|
||||
<view class="action">
|
||||
<view class="item arrow" @tap="openSkuPopup(skuskuModeType.both)">
|
||||
<text class="label">已选</text>
|
||||
<text class="text ellipsis">{{ selectAttrText }}</text>
|
||||
</view>
|
||||
<view class="item arrow" @tap="openAddressPopup">
|
||||
<text class="label">送至</text>
|
||||
<text class="text ellipsis">{{ selectAddressText }}</text>
|
||||
</view>
|
||||
<view class="item arrow" @tap="openPopup('service')">
|
||||
<text class="label">服务</text>
|
||||
<text class="text ellipsis">{{ goodsServiceText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 弹出层 -->
|
||||
<uni-popup
|
||||
ref="popup"
|
||||
type="bottom"
|
||||
background-color="#fff"
|
||||
@change="usePopupStore().onChangePopupProps"
|
||||
>
|
||||
<address-panel
|
||||
v-if="popupName === 'address'"
|
||||
@close="popup?.close()"
|
||||
:list="addressList"
|
||||
/>
|
||||
<service-panel
|
||||
v-if="popupName === 'service'"
|
||||
:list="goodsServiceList"
|
||||
@close="popup?.close()"
|
||||
/>
|
||||
<share-panel
|
||||
v-if="popupName === 'share'"
|
||||
@close="popup?.close()"
|
||||
@showPoster="onShowPoster"
|
||||
:goods="goods"
|
||||
/>
|
||||
</uni-popup>
|
||||
</view>
|
||||
|
||||
<!-- 商品评价 -->
|
||||
<view class="panel">
|
||||
<view v-if="goods?.evaluate_count > 0" @click="getMoreEvaluate">
|
||||
<uni-section :title="`评价 (${computeConversion(goods?.evaluate_count)})`" type="line">
|
||||
<template #right>
|
||||
<view class="tip">
|
||||
<text> 好评率 {{ goods?.positive_rate }}%</text>
|
||||
<uni-icons type="forward" size="14" color="#909399" class="icon"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
<view class="eva-box" v-for="(evaluate, index) in goods?.evaluate" :key="index">
|
||||
<view class="top">
|
||||
<shop-user-avatar
|
||||
:url="evaluate.user_avatar"
|
||||
width="50"
|
||||
style="padding-right: 10rpx"
|
||||
/>
|
||||
<text class="name">{{ evaluate.user_name }}</text>
|
||||
<view class="rate">
|
||||
<uni-rate :value="evaluate.score" :size="20" readonly />
|
||||
</view>
|
||||
</view>
|
||||
<text class="content">{{ evaluate.content }}</text>
|
||||
<scroll-view class="scroll-view-container" :scroll-x="true">
|
||||
<image
|
||||
class="content-img"
|
||||
mode="aspectFill"
|
||||
v-for="(item, index) in arrayFullUrl(evaluate.images)"
|
||||
:key="index"
|
||||
:src="item"
|
||||
@click.stop="previewImg(item, evaluate.images)"
|
||||
/>
|
||||
</scroll-view>
|
||||
<view class="bot"> </view>
|
||||
</view>
|
||||
</uni-section>
|
||||
</view>
|
||||
<view v-else>
|
||||
<uni-section title="评价 (0)" type="line">
|
||||
<z-paging-empty-view empty-view-text="期待您的评价" :empty-view-fixed="false" />
|
||||
</uni-section>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel">
|
||||
<uni-section title="详情" type="line">
|
||||
<view class="properties">
|
||||
<view class="item" v-for="item in goods?.params" :key="item.key">
|
||||
<text class="label">{{ item.key }}</text>
|
||||
<text class="value">{{ item.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</uni-section>
|
||||
<view v-if="goods.detail_images.length > 0">
|
||||
<shop-image :src="goods?.detail_images" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view :style="{ paddingBottom: '5rpx' }"> </view>
|
||||
|
||||
<!-- sku弹窗 -->
|
||||
<vk-data-goods-sku-popup
|
||||
v-model="isShowSku"
|
||||
:localdata="skuData"
|
||||
:mode="skuMode"
|
||||
buy-now-background-color="#ff0000"
|
||||
add-cart-background-color="#ffa200"
|
||||
:amountType="0"
|
||||
ref="skuPopupRef"
|
||||
:actived-style="{
|
||||
color: '#ff0000',
|
||||
backgroundColor: '#fee8e6',
|
||||
borderColor: '#ff0000',
|
||||
}"
|
||||
@add-cart="onAddCart"
|
||||
@buy-now="onBuyNow"
|
||||
@spec-selected="onSpecSelected"
|
||||
class="sku-popup"
|
||||
/>
|
||||
|
||||
<!-- 海报弹窗 -->
|
||||
<sharePoster
|
||||
ref="sharePosterRef"
|
||||
:goods="goods"
|
||||
:currentPrice="currentPrice"
|
||||
:currentOriginPrice="currentOriginPrice"
|
||||
:goodsImage="goodsImage"
|
||||
/>
|
||||
|
||||
<!-- 用户操作 -->
|
||||
<view class="goods-nav" :style="safeBottom" v-show="goods">
|
||||
<uni-goods-nav
|
||||
:fill="true"
|
||||
:options="menuButtons"
|
||||
:buttonGroup="operateButtons"
|
||||
@click="menuClick"
|
||||
@buttonClick="buttonClick"
|
||||
/>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
</page-meta>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
swiper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
page {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.viewport {
|
||||
background-color: #f4f4f4;
|
||||
z-index: 999;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
margin: 15rpx 15rpx 20rpx;
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 30rpx;
|
||||
content: '\e6c2';
|
||||
color: #ccc;
|
||||
font-family: 'iconfont' !important;
|
||||
font-size: 32rpx;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 商品信息 */
|
||||
.goods {
|
||||
background-color: #fff;
|
||||
margin: 15rpx 15rpx 20rpx;
|
||||
|
||||
.preview {
|
||||
height: 750rpx;
|
||||
position: relative;
|
||||
.image {
|
||||
width: 750rpx;
|
||||
height: 750rpx;
|
||||
}
|
||||
.indicator {
|
||||
height: 40rpx;
|
||||
padding: 0 24rpx;
|
||||
line-height: 40rpx;
|
||||
border-radius: 30rpx;
|
||||
color: #fff;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
position: absolute;
|
||||
bottom: 30rpx;
|
||||
right: 30rpx;
|
||||
.current {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
.split {
|
||||
font-size: 24rpx;
|
||||
margin: 0 1rpx 0 2rpx;
|
||||
}
|
||||
.total {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.meta {
|
||||
.meta-top {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.operate {
|
||||
display: flex;
|
||||
|
||||
.operate-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.operate-item-btn {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background-color: transparent;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.icon-text {
|
||||
font-size: 20rpx;
|
||||
color: #9a9a9a;
|
||||
}
|
||||
|
||||
.icon-favorite-checked {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.price {
|
||||
height: 130rpx;
|
||||
padding: 0rpx 20rpx;
|
||||
color: red;
|
||||
font-size: 34rpx;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.origin {
|
||||
margin-top: 15rpx;
|
||||
color: #888;
|
||||
text-decoration: line-through;
|
||||
|
||||
::after {
|
||||
content: ¥;
|
||||
}
|
||||
}
|
||||
}
|
||||
.number {
|
||||
font-size: 50rpx;
|
||||
}
|
||||
.brand {
|
||||
width: 160rpx;
|
||||
height: 80rpx;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 26rpx;
|
||||
right: 30rpx;
|
||||
}
|
||||
.name {
|
||||
max-height: 88rpx;
|
||||
line-height: 1.4;
|
||||
margin: 20rpx;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
}
|
||||
.desc {
|
||||
line-height: 1;
|
||||
padding: 0 20rpx 30rpx;
|
||||
font-size: 24rpx;
|
||||
color: #cf4444;
|
||||
}
|
||||
.sales {
|
||||
margin: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
padding-left: 10rpx;
|
||||
background: #fff;
|
||||
|
||||
.item {
|
||||
height: 90rpx;
|
||||
padding-right: 60rpx;
|
||||
border-bottom: 1rpx solid #eaeaea;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&:last-child {
|
||||
border-bottom: 0 none;
|
||||
}
|
||||
}
|
||||
.label {
|
||||
width: 60rpx;
|
||||
color: #898b94;
|
||||
margin: 0 16rpx 0 10rpx;
|
||||
}
|
||||
.text {
|
||||
flex: 1;
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tip {
|
||||
font-size: 24rpx;
|
||||
color: #9a9a9a;
|
||||
|
||||
.icon {
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.eva-box {
|
||||
padding: 10rpx;
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 20rpx;
|
||||
|
||||
.name {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 26rpx;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.rate {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 0rpx 10rpx 10rpx;
|
||||
max-height: 100rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.scroll-view-container {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow-x: scroll;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
.content-img {
|
||||
margin-left: 10rpx;
|
||||
height: 150rpx;
|
||||
width: 150rpx;
|
||||
margin-bottom: 10rpx;
|
||||
margin-right: 10rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-top: 10rpx;
|
||||
scroll-snap-align: start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.properties {
|
||||
padding: 0 20rpx;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
line-height: 2;
|
||||
padding: 10rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
border-bottom: 1rpx dashed #ccc;
|
||||
}
|
||||
.label {
|
||||
width: 200rpx;
|
||||
}
|
||||
.value {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部工具栏 */
|
||||
.goods-nav {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* #ifdef WEB */
|
||||
// 当根元素有 ios-device 类时,兼容底部样式
|
||||
.ios-device {
|
||||
.viewport {
|
||||
padding-bottom: 34px !important;
|
||||
}
|
||||
|
||||
.goods-nav {
|
||||
padding-bottom: 34px !important;
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
@ -1,107 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue'
|
||||
import type { shopGoodsListInsatnce } from '@/types/component'
|
||||
|
||||
//接收参数
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
classify_id?: string
|
||||
name?: string
|
||||
coupon_id?: string
|
||||
empty_text?: string
|
||||
}>(),
|
||||
{
|
||||
classify_id: '',
|
||||
name: '',
|
||||
coupon_id: '',
|
||||
empty_text: '暂无商品数据',
|
||||
},
|
||||
)
|
||||
|
||||
const query = reactive({
|
||||
classify_id: props.classify_id,
|
||||
name: props.name,
|
||||
coupon_id: props.coupon_id,
|
||||
sort_field: '',
|
||||
sort_by: '',
|
||||
})
|
||||
|
||||
const shopGoodsListRef = ref<shopGoodsListInsatnce>()
|
||||
|
||||
const queryOptions = ref([
|
||||
{ title: '综合排序', value: 'all', type: 'click' },
|
||||
{ title: '销量优先', value: 'sales', type: 'click' },
|
||||
{
|
||||
title: '价格排序',
|
||||
value: '',
|
||||
type: 'sort',
|
||||
options: [{ value: 'asc' }, { value: 'desc' }],
|
||||
},
|
||||
])
|
||||
|
||||
const onChange = (data: any, index: number) => {
|
||||
console.log(data, index)
|
||||
}
|
||||
const onConfirm = (data: any) => {
|
||||
const { value, type } = data
|
||||
if (value === 'all') {
|
||||
Object.assign(query, { sort_field: data.value })
|
||||
} else if (value === 'sales') {
|
||||
Object.assign(query, { sort_field: data.value, sort_by: 'desc' })
|
||||
} else if (type === 'sort') {
|
||||
Object.assign(query, { sort_field: 'price', sort_by: value })
|
||||
}
|
||||
|
||||
shopGoodsListRef.value?.reload()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<shop-goods-list
|
||||
:query="query"
|
||||
:safe-area-inset-bottom="true"
|
||||
ref="shopGoodsListRef"
|
||||
:options="{
|
||||
emptyViewText: empty_text,
|
||||
backGroundColor: '#fff',
|
||||
}"
|
||||
>
|
||||
<template #top>
|
||||
<view v-if="query">
|
||||
<le-dropdown
|
||||
v-model:menuList="queryOptions"
|
||||
themeColor="#3185FF"
|
||||
:duration="300"
|
||||
:isCeiling="false"
|
||||
@onConfirm="onConfirm"
|
||||
@onChange="onChange"
|
||||
></le-dropdown>
|
||||
</view>
|
||||
</template>
|
||||
</shop-goods-list>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background-color: #ececec;
|
||||
}
|
||||
.toolbar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
background-color: #fff;
|
||||
height: 100rpx;
|
||||
padding: 0 20rpx var(--window-bottom);
|
||||
border-top: 1rpx solid #eaeaea;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
// 底部占位空盒子
|
||||
.toolbar-height {
|
||||
height: 190rpx;
|
||||
}
|
||||
</style>
|
@ -1,147 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { pageUrl } from '@/utils/constants'
|
||||
import { ref } from 'vue'
|
||||
|
||||
let name = ref('')
|
||||
|
||||
const history = ref<string[]>(uni.getStorageSync('search') || [])
|
||||
|
||||
const storeHistroy = () => {
|
||||
name.value = name.value.trim()
|
||||
if (name.value == '') return
|
||||
// 缓存搜索关键词
|
||||
const index = history.value.findIndex((item) => item === name.value)
|
||||
if (index !== -1) {
|
||||
history.value.splice(index, 1)
|
||||
}
|
||||
history.value.unshift(name.value)
|
||||
uni.setStorageSync('search', history.value)
|
||||
}
|
||||
|
||||
const search = async () => {
|
||||
await uni.navigateTo({
|
||||
url: `${pageUrl['goods-list']}?name=${name.value}`,
|
||||
})
|
||||
storeHistroy()
|
||||
}
|
||||
|
||||
const onTapHistory = async (keyword: string) => {
|
||||
await uni.navigateTo({
|
||||
url: `${pageUrl['goods-list']}?name=${keyword}`,
|
||||
})
|
||||
name.value = keyword
|
||||
storeHistroy()
|
||||
}
|
||||
|
||||
const onCancel = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const onClearHistory = () => {
|
||||
uni.showModal({
|
||||
title: '',
|
||||
content: '确定要清空历史搜索吗',
|
||||
showCancel: true,
|
||||
success: ({ confirm, cancel }) => {
|
||||
if (confirm) {
|
||||
uni.removeStorageSync('search')
|
||||
history.value = []
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const onDeleteHistory = (name: string) => {
|
||||
uni.showModal({
|
||||
title: '',
|
||||
content: '确定要删除该条历史搜索吗',
|
||||
showCancel: true,
|
||||
success: ({ confirm, cancel }) => {
|
||||
if (confirm) {
|
||||
const index = history.value.findIndex((item) => item === name)
|
||||
if (index !== -1) {
|
||||
history.value.splice(index, 1)
|
||||
uni.setStorageSync('search', history.value)
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="search">
|
||||
<uni-search-bar
|
||||
@confirm="search"
|
||||
:focus="true"
|
||||
v-model="name"
|
||||
radius="100"
|
||||
@cancel="onCancel"
|
||||
/>
|
||||
</view>
|
||||
<view class="search-history">
|
||||
<text>历史搜索</text>
|
||||
<view class="trash">
|
||||
<uni-icons type="trash" color="" size="24" @tap="onClearHistory" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="keyword">
|
||||
<button
|
||||
v-for="(item, index) in history"
|
||||
:key="index"
|
||||
@tap="onTapHistory(item)"
|
||||
class="keyword-button"
|
||||
aria-readonly
|
||||
@longtap="onDeleteHistory(item)"
|
||||
>
|
||||
{{ item }}
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
|
||||
.search {
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
|
||||
.search-history {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
|
||||
.trash {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 50rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.keyword {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 20px;
|
||||
float: left;
|
||||
|
||||
button {
|
||||
background-color: #f2f2f2;
|
||||
border-radius: 25rpx;
|
||||
color: #333333;
|
||||
font-size: 24rpx;
|
||||
align-items: center;
|
||||
margin: 0 10rpx 15rpx 15rpx;
|
||||
justify-content: flex-start;
|
||||
// padding-left: 10px;
|
||||
display: flex;
|
||||
|
||||
.delete-icon {
|
||||
margin-left: 5px;
|
||||
margin-right: -10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,40 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { pageUrl } from '@/utils/constants'
|
||||
|
||||
const pageList = [
|
||||
{
|
||||
title: '商城订单',
|
||||
icon: 'icon-shop-order',
|
||||
to: `${pageUrl['order-list']}`,
|
||||
},
|
||||
{
|
||||
title: '充值订单',
|
||||
icon: 'icon-recharge-order',
|
||||
to: `${pageUrl['recharge-order-list']}`,
|
||||
},
|
||||
]
|
||||
</script>
|
||||
<template>
|
||||
<uni-list class="list">
|
||||
<uni-list-item
|
||||
class="item"
|
||||
v-for="(item, index) in pageList"
|
||||
:key="index"
|
||||
showArrow
|
||||
:title="item.title"
|
||||
:to="item.to"
|
||||
>
|
||||
<template #header>
|
||||
<text class="icon" :class="item.icon"></text>
|
||||
</template>
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.item {
|
||||
line-height: 2em;
|
||||
}
|
||||
.icon {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
</style>
|
@ -1,612 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { fullUrl } from '@/utils/common'
|
||||
import { orderType, pageUrl, safeBottom, pageMateStyle } from '@/utils/constants'
|
||||
import {
|
||||
getCartOrderPreApi,
|
||||
getOrderPreApi,
|
||||
getRepurchaseOrderPreByOrderIdApi,
|
||||
postOrderApi,
|
||||
} from '@/api/order'
|
||||
import { useAddressStore } from '@/stores/modules/address'
|
||||
import type { orderPreResult } from '@/types/order'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { computed, ref } from 'vue'
|
||||
import shopCoupon from '@/components/shop-coupon/shop-coupon.vue'
|
||||
import type { couponListItem } from '@/types/coupon'
|
||||
import { debounce } from 'lodash'
|
||||
import { usePopupStore } from '@/stores/modules/popup'
|
||||
import type { InputNumberBoxEvent } from '@/components/vk-data-input-number-box/vk-data-input-number-box'
|
||||
|
||||
//获取地址store
|
||||
const addressStore = useAddressStore()
|
||||
addressStore.changeScene('order')
|
||||
|
||||
// 订单备注
|
||||
const buyerMessage = ref('')
|
||||
|
||||
const disabledSubmit = ref(false)
|
||||
|
||||
if (!addressStore.selectedAddress?.id) {
|
||||
disabledSubmit.value = true
|
||||
}
|
||||
|
||||
const selectCouponId = ref<string | number>('')
|
||||
const couponText = ref('')
|
||||
|
||||
//获取页面参数
|
||||
const query = defineProps<{
|
||||
sku_id?: string
|
||||
count?: string
|
||||
orderId?: string
|
||||
}>()
|
||||
|
||||
//获取下单前信息
|
||||
const orderPreData = ref<orderPreResult>()
|
||||
const orderPreParams = ref<{
|
||||
id: string
|
||||
sku_id: string
|
||||
num: string | number
|
||||
address_id: string | number
|
||||
coupon_id: string | number
|
||||
}>({
|
||||
id: query.orderId ?? '0',
|
||||
sku_id: query.sku_id ?? '0',
|
||||
num: query.count ?? 0,
|
||||
address_id: addressStore.selectedAddress?.id ?? '0',
|
||||
coupon_id: selectCouponId.value ?? '0',
|
||||
})
|
||||
|
||||
const getOrderPreData = async () => {
|
||||
orderPreParams.value = {
|
||||
...orderPreParams.value,
|
||||
coupon_id: selectCouponId.value ?? '0',
|
||||
}
|
||||
|
||||
let res: any = []
|
||||
|
||||
try {
|
||||
if (query.sku_id && query.count) {
|
||||
//直接下单
|
||||
res = await getOrderPreApi({
|
||||
sku_id: orderPreParams.value.sku_id,
|
||||
num: Number(orderPreParams.value.num),
|
||||
address_id: orderPreParams.value.address_id,
|
||||
coupon_id: orderPreParams.value.coupon_id,
|
||||
})
|
||||
} else if (query.orderId) {
|
||||
//再次购买下单
|
||||
res = await getRepurchaseOrderPreByOrderIdApi({
|
||||
id: orderPreParams.value.id,
|
||||
address_id: orderPreParams.value.address_id,
|
||||
coupon_id: orderPreParams.value.coupon_id,
|
||||
})
|
||||
} else {
|
||||
//购物车下单
|
||||
res = await getCartOrderPreApi({
|
||||
address_id: orderPreParams.value.address_id,
|
||||
coupon_id: orderPreParams.value.coupon_id,
|
||||
})
|
||||
}
|
||||
|
||||
disabledSubmit.value = false
|
||||
} catch (error: any) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: error.data.msg,
|
||||
})
|
||||
}
|
||||
|
||||
orderPreData.value = res.result
|
||||
|
||||
if (res.result.error) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: res.result.error,
|
||||
})
|
||||
disabledSubmit.value = true
|
||||
}
|
||||
}
|
||||
|
||||
//下单页收货地址
|
||||
const addressData = computed(() => {
|
||||
return addressStore.selectedAddress
|
||||
})
|
||||
|
||||
// 提交订单
|
||||
const onOrderSubmit = async () => {
|
||||
await checkOrder()
|
||||
}
|
||||
// 订单提交前校验
|
||||
const checkOrder = () => {
|
||||
if (!addressStore.selectedAddress?.id) {
|
||||
return uni.showToast({
|
||||
title: '请选择收货地址',
|
||||
icon: 'none',
|
||||
mask: true,
|
||||
})
|
||||
}
|
||||
debouncedSubmitOrder()
|
||||
}
|
||||
// 提交订单函数
|
||||
const debouncedSubmitOrder = debounce(async () => {
|
||||
const res = await postOrderApi({
|
||||
address_id: addressStore.selectedAddress!.id,
|
||||
remark: buyerMessage.value,
|
||||
goods: orderPreData.value!.goods.map((item) => ({
|
||||
sku_id: item.id,
|
||||
num: item.num,
|
||||
})),
|
||||
type: orderType.default,
|
||||
coupon_id: selectCouponId.value,
|
||||
})
|
||||
|
||||
if (!res.result.id) {
|
||||
return uni.showToast({
|
||||
icon: 'error',
|
||||
title: '订单提交失败',
|
||||
})
|
||||
}
|
||||
// 跳转详情
|
||||
uni.redirectTo({ url: `${pageUrl['order-detail']}?id=${res.result.id}` })
|
||||
}, 1000)
|
||||
|
||||
const couponPopup = ref<{
|
||||
open: (type?: UniHelper.UniPopupType) => void
|
||||
close: () => void
|
||||
}>()
|
||||
const openCouponPopup = () => {
|
||||
couponPopup.value?.open()
|
||||
}
|
||||
const closeCouponPopup = () => {
|
||||
couponPopup.value?.close()
|
||||
}
|
||||
|
||||
// 选中优惠券
|
||||
const selectCoupon = (item: couponListItem) => {
|
||||
selectCouponId.value = item.id
|
||||
getOrderPreData().then(() => {
|
||||
couponText.value = orderPreData.value!.summary.coupon_discount
|
||||
couponPopup.value?.close()
|
||||
})
|
||||
}
|
||||
// 清空已选择的优惠券
|
||||
const clearSelectCoupon = () => {
|
||||
selectCouponId.value = ''
|
||||
getOrderPreData().then(() => {
|
||||
couponPopup.value?.close()
|
||||
})
|
||||
}
|
||||
|
||||
const onChangeNum = debounce((event: InputNumberBoxEvent) => {
|
||||
orderPreParams.value.num = event.value
|
||||
selectCouponId.value = ''
|
||||
getOrderPreData()
|
||||
}, 500)
|
||||
|
||||
onShow(() => {
|
||||
getOrderPreData().then(() => {
|
||||
if (!orderPreData.value?.user_address) {
|
||||
addressStore.clearSelectedAddress()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<page-meta :page-style="pageMateStyle">
|
||||
<!-- #endif -->
|
||||
<view v-if="orderPreData">
|
||||
<scroll-view scroll-y class="viewport">
|
||||
<!-- 收货地址 -->
|
||||
<navigator
|
||||
v-if="addressData"
|
||||
class="shipment"
|
||||
hover-class="none"
|
||||
:url="`${pageUrl['address']}?from=order`"
|
||||
>
|
||||
<view class="user"> {{ addressData.name }} {{ addressData.phone }} </view>
|
||||
<view class="address"> {{ addressData.full_location }} {{ addressData.address }} </view>
|
||||
<text class="icon icon-right"></text>
|
||||
</navigator>
|
||||
<navigator
|
||||
v-else
|
||||
class="shipment"
|
||||
hover-class="none"
|
||||
:url="`${pageUrl['address']}?from=order`"
|
||||
>
|
||||
<view class="address"> 请选择收货地址 </view>
|
||||
<text class="icon icon-right"></text>
|
||||
</navigator>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view class="goods" v-if="orderPreData">
|
||||
<view v-for="item in orderPreData?.goods" :key="item.sku_id" class="item">
|
||||
<navigator :url="`${pageUrl['goods-detail']}?id=${item.goods_id}`" hover-class="none">
|
||||
<image class="picture" :src="fullUrl(item.image)" />
|
||||
</navigator>
|
||||
<view class="meta">
|
||||
<view class="name ellipsis"> {{ item.goods.name }} </view>
|
||||
<view class="attrs">{{ item.spec_text }}</view>
|
||||
<view class="prices">
|
||||
<view class="pay-price symbol">{{ item.price }}</view>
|
||||
<view class="price symbol">{{ item.origin_price }}</view>
|
||||
</view>
|
||||
<view class="count">
|
||||
<vk-data-input-number-box
|
||||
v-if="query.sku_id && query.count"
|
||||
:style="{ position: 'absolute', right: '0', bottom: '-10rpx' }"
|
||||
v-model="item.num"
|
||||
:index="item.id"
|
||||
:min="1"
|
||||
@plus="onChangeNum($event)"
|
||||
@minus="onChangeNum($event)"
|
||||
@blur="onChangeNum($event)"
|
||||
/>
|
||||
<view else="orderPreData?.goods.length > 1">x {{ item.num }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="related">
|
||||
<view class="item">
|
||||
<text class="text">订单备注</text>
|
||||
<input
|
||||
class="input"
|
||||
:cursor-spacing="30"
|
||||
placeholder="建议留言前先与商家沟通确认"
|
||||
v-model="buyerMessage"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付金额 -->
|
||||
<view class="settlement">
|
||||
<view class="item">
|
||||
<text class="text">商品总价: </text>
|
||||
<text class="number symbol">{{ orderPreData?.summary.total_price }}</text>
|
||||
</view>
|
||||
<view class="item">
|
||||
<text class="text">优惠券: </text>
|
||||
<text class="coupon" @tap="openCouponPopup" v-if="orderPreData.coupon_list.length > 0">
|
||||
<text v-if="selectCouponId" class="danger">-¥{{ couponText }}</text>
|
||||
<text v-else>
|
||||
<text class="danger">{{ orderPreData.coupon_list.length }}</text> 张优惠券可用
|
||||
</text>
|
||||
</text>
|
||||
<text v-else>无优惠券可用</text>
|
||||
</view>
|
||||
<view class="item">
|
||||
<text class="text">运费: </text>
|
||||
<text class="number symbol">{{ orderPreData?.summary.freight }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 优惠券弹出层 -->
|
||||
<uni-popup
|
||||
ref="couponPopup"
|
||||
type="share"
|
||||
background-color="#fff"
|
||||
class="coupon-popup"
|
||||
@change="usePopupStore().onChangePopupProps"
|
||||
>
|
||||
<view class="coupon-panel">
|
||||
<text class="close icon-close" @tap="closeCouponPopup"></text>
|
||||
<view class="title">选择优惠券</view>
|
||||
<view class="content">
|
||||
<shop-coupon
|
||||
:couponList="orderPreData.coupon_list"
|
||||
mode="user"
|
||||
buttonTitle="选择使用"
|
||||
@buttonClicked="selectCoupon"
|
||||
class="user-coupon-popup"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coupon-panel-footer">
|
||||
<view class="button primary" @tap="clearSelectCoupon"> 不使用优惠券</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<!-- 底部占位空盒子 -->
|
||||
<view class="toolbar-height"></view>
|
||||
|
||||
<!-- 吸底工具栏 -->
|
||||
<view class="toolbar" :style="safeBottom">
|
||||
<view class="total-pay symbol">
|
||||
<text class="number">{{ orderPreData?.summary.pay_price }}</text>
|
||||
</view>
|
||||
<view class="button" :class="{ disabled: disabledSubmit }" @tap="onOrderSubmit">
|
||||
<button class="button" :disabled="disabledSubmit">提交订单</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
</page-meta>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
.symbol::before {
|
||||
content: '¥';
|
||||
font-size: 80%;
|
||||
margin-right: 5rpx;
|
||||
}
|
||||
|
||||
.shipment {
|
||||
margin: 20rpx;
|
||||
padding: 30rpx 30rpx 30rpx 84rpx;
|
||||
font-size: 26rpx;
|
||||
border-radius: 10rpx;
|
||||
background: url('~@/static/images/locate.png') 20rpx center / 50rpx no-repeat #fff;
|
||||
position: relative;
|
||||
|
||||
.icon {
|
||||
font-size: 36rpx;
|
||||
color: #333;
|
||||
transform: translateY(-50%);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 20rpx;
|
||||
}
|
||||
|
||||
.user {
|
||||
color: #333;
|
||||
margin-bottom: 5rpx;
|
||||
}
|
||||
|
||||
.address {
|
||||
color: #666;
|
||||
width: 94%;
|
||||
}
|
||||
}
|
||||
|
||||
.goods {
|
||||
margin: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
padding: 30rpx 0;
|
||||
border-top: 1rpx solid #eee;
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.picture {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.meta {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.attrs {
|
||||
line-height: 1.8;
|
||||
padding: 0 15rpx;
|
||||
margin-top: 6rpx;
|
||||
margin-bottom: 10rpx;
|
||||
font-size: 24rpx;
|
||||
align-self: flex-start;
|
||||
border-radius: 4rpx;
|
||||
color: #888;
|
||||
background-color: #f7f7f8;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.prices {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-top: 6rpx;
|
||||
font-size: 28rpx;
|
||||
width: 50%;
|
||||
flex-flow: wrap;
|
||||
|
||||
.pay-price {
|
||||
margin-right: 10rpx;
|
||||
color: #cf4444;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
.count {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.related {
|
||||
margin: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
min-height: 80rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
margin: 20rpx 0;
|
||||
padding-right: 20rpx;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.item .text {
|
||||
width: 125rpx;
|
||||
}
|
||||
|
||||
.picker {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.picker::after {
|
||||
content: '\e6c2';
|
||||
}
|
||||
}
|
||||
|
||||
/* 结算清单 */
|
||||
.settlement {
|
||||
margin: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
// padding-bottom: 200rpx;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 80rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.coupon {
|
||||
&::after {
|
||||
font-family: 'iconfont' !important;
|
||||
content: '\e6c2';
|
||||
}
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: #cf4444;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-panel {
|
||||
.title {
|
||||
line-height: 1;
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: normal;
|
||||
border-bottom: 1rpx solid #ddd;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 24rpx;
|
||||
top: 24rpx;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
min-height: 400rpx;
|
||||
max-height: 650rpx;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-panel-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: #444;
|
||||
|
||||
.button {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
text-align: center;
|
||||
line-height: 72rpx;
|
||||
margin: 0 20rpx;
|
||||
color: #fff;
|
||||
border-radius: 72rpx;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #fff;
|
||||
background-color: #ff5f3c;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
background-color: #ffa868;
|
||||
}
|
||||
}
|
||||
|
||||
/* 吸底工具栏 */
|
||||
.toolbar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: calc(var(--window-bottom));
|
||||
z-index: 1;
|
||||
|
||||
background-color: #fff;
|
||||
height: 100rpx;
|
||||
padding: 0 20rpx;
|
||||
border-top: 1rpx solid #eaeaea;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-sizing: content-box;
|
||||
|
||||
.total-pay {
|
||||
font-size: 40rpx;
|
||||
color: #cf4444;
|
||||
|
||||
.decimal {
|
||||
font-size: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 220rpx;
|
||||
text-align: center;
|
||||
line-height: 72rpx;
|
||||
font-size: 26rpx;
|
||||
color: #fff;
|
||||
border-radius: 72rpx;
|
||||
background-color: #ff5f3c;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
// 底部占位空盒子
|
||||
.toolbar-height {
|
||||
height: 190rpx;
|
||||
}
|
||||
</style>
|
@ -1,261 +0,0 @@
|
||||
<template>
|
||||
<view v-if="deliveryList.length > 0">
|
||||
<z-tabs
|
||||
:list="deliveryList"
|
||||
@change="onChangeTabs"
|
||||
class="tabs"
|
||||
v-if="deliveryInfo.length > 1"
|
||||
:scroll-count="1"
|
||||
/>
|
||||
<view class="body">
|
||||
<!-- 商品详情 -->
|
||||
<view class="delivery">
|
||||
<view class="delivery-goods-list">
|
||||
<view class="goods-item" v-for="(goods, idx) in deliveryInfo[tabIndex].detail" :key="idx">
|
||||
<image class="goods-img" :src="fullUrl(goods.order_detail.image)" alt="商品图片" />
|
||||
<view class="title">共{{ goods.delivery_num }}件</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 物流信息 -->
|
||||
<view class="delivery-info">
|
||||
<view v-if="deliveryInfo[tabIndex].delivery_company">
|
||||
<view class="info-item">
|
||||
<view class="item-label">物流公司: </view>
|
||||
<view class="item-content">{{ deliveryInfo[tabIndex].delivery_company.name }}</view>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<view class="item-label">物流单号: </view>
|
||||
<view class="item-content">{{ deliveryInfo[tabIndex].delivery_no }}</view>
|
||||
<view class="item-content">
|
||||
<button class="copy-button" @tap="onCopy(deliveryInfo[tabIndex].delivery_no)">
|
||||
复制
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<view class="item-label address">收货地址: </view>
|
||||
<view class="item-content">
|
||||
{{ deliveryInfo[tabIndex].order_address.full_address }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="info-item">
|
||||
<view>无需物流</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 物流轨迹 -->
|
||||
<view class="delivery-tracks" v-if="deliveryList[tabIndex].value.length > 0">
|
||||
<view
|
||||
v-for="(item, index) in deliveryList[tabIndex].value"
|
||||
:key="index"
|
||||
class="track-item"
|
||||
:class="{ start: index === 0, end: index === deliveryList[tabIndex].value.length - 1 }"
|
||||
>
|
||||
<view class="track-item-content">
|
||||
<view class="item-content">
|
||||
<text>{{ item.context }}</text>
|
||||
</view>
|
||||
<view class="item-time">
|
||||
<text>{{ item.time }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<z-paging-empty-view :empty-view-fixed="false" empty-view-text="暂无物流轨迹信息" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view :style="safeBottom" class="bottom"></view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { safeBottom } from '@/utils/constants'
|
||||
import { getOrderDeliveryByIdApi } from '@/api/order'
|
||||
import type { orderDeliveryResult } from '@/types/order'
|
||||
import { ref } from 'vue'
|
||||
import { fullUrl, onShowRefreshData } from '@/utils/common'
|
||||
|
||||
const props = defineProps<{
|
||||
order_id: string
|
||||
}>()
|
||||
|
||||
let deliveryInfo: orderDeliveryResult[] = []
|
||||
const deliveryList = ref<any[]>([])
|
||||
const tabIndex = ref(0)
|
||||
|
||||
const onChangeTabs = (index: number) => {
|
||||
tabIndex.value = index
|
||||
}
|
||||
|
||||
const onCopy = (deliveryNo: string) => {
|
||||
uni.setClipboardData({ data: deliveryNo })
|
||||
}
|
||||
|
||||
onShowRefreshData(async () => {
|
||||
const res = await getOrderDeliveryByIdApi(props.order_id)
|
||||
deliveryInfo = res.result
|
||||
for (let i = 1; i <= deliveryInfo.length; i++) {
|
||||
deliveryList.value.push({
|
||||
name: '包裹' + i,
|
||||
value: deliveryInfo[i - 1].delivery_tracks,
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding-top: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.delivery {
|
||||
padding: 10rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.delivery-goods-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: -30rpx;
|
||||
|
||||
.goods-item {
|
||||
position: relative;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
width: 130rpx;
|
||||
height: 130rpx;
|
||||
margin-bottom: 30rpx;
|
||||
margin-left: 18rpx;
|
||||
}
|
||||
|
||||
.goods-img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #fff;
|
||||
padding: 4rpx 0;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.delivery-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.6;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
font-size: 24rpx;
|
||||
|
||||
.copy-button {
|
||||
display: flex;
|
||||
height: 34rpx;
|
||||
font-size: 24rpx;
|
||||
align-items: center;
|
||||
margin-left: 20rpx;
|
||||
border-radius: 50rpx;
|
||||
border: 1rpx solid #c1c1c1;
|
||||
}
|
||||
}
|
||||
|
||||
.item-label {
|
||||
color: #9d9d9d;
|
||||
margin-right: 20rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.track {
|
||||
font-size: 40rrpx;
|
||||
}
|
||||
|
||||
.delivery-tracks {
|
||||
padding: 30rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 25rpx;
|
||||
|
||||
.track-item {
|
||||
position: relative;
|
||||
padding-left: 20rpx;
|
||||
padding-bottom: 25rpx;
|
||||
border-left: 4rpx solid #ccc;
|
||||
|
||||
&.start {
|
||||
border-left: 5rpx solid red;
|
||||
&:after {
|
||||
background-color: red;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.end {
|
||||
padding-bottom: 0rpx;
|
||||
border-left: none;
|
||||
&::after {
|
||||
left: -13rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: ' ';
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: -9px;
|
||||
top: 0rpx;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
border-radius: 40rpx;
|
||||
background: #bdbdbd;
|
||||
border: 2rpx solid #fff;
|
||||
}
|
||||
|
||||
.track-item-content {
|
||||
position: relative;
|
||||
padding: 20rpx 20rpx;
|
||||
background-color: #f4f4f4;
|
||||
border-radius: 20rpx;
|
||||
|
||||
.item-content {
|
||||
font-size: 27rpx;
|
||||
padding-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.item-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
height: 50rpx;
|
||||
}
|
||||
</style>
|
@ -1,788 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { orderState, orderStateActions, orderStateList, pageUrl } from '@/utils/constants'
|
||||
import { deleteOrderApi, getOrderDetailByIdApi, putOrderCancelByIdApi } from '@/api/order'
|
||||
import type { orderResult } from '@/types/order'
|
||||
import { ref, computed } from 'vue'
|
||||
import { fullUrl, getPayPageUrl, formatPrice, onShowRefreshData } from '@/utils/common'
|
||||
import receiveButton from '@/pagesOrder/components/receiveButton.vue'
|
||||
import cancelButton from '@/pagesOrder/components/cancelButton.vue'
|
||||
import { safeBottom } from '@/utils/constants'
|
||||
|
||||
//订单详情
|
||||
const detailData = ref<orderResult>()
|
||||
const getDetailData = async () => {
|
||||
const res = await getOrderDetailByIdApi(orderId)
|
||||
detailData.value = res.result
|
||||
}
|
||||
|
||||
// 复制内容
|
||||
const onCopy = () => {
|
||||
// 设置系统剪贴板的内容
|
||||
uni.setClipboardData({ data: detailData.value!.order_no })
|
||||
}
|
||||
// 获取页面参数
|
||||
const query = defineProps<{
|
||||
id: string
|
||||
}>()
|
||||
|
||||
//订单ID
|
||||
const orderId = Number(query.id)
|
||||
|
||||
//倒计时结束事件
|
||||
const onTimeUp = () => {
|
||||
if (detailData.value?.status != orderState.canceled) {
|
||||
putOrderCancelByIdApi({
|
||||
id: detailData.value!.id,
|
||||
time_out: true,
|
||||
})
|
||||
}
|
||||
detailData.value!.status = orderState.canceled
|
||||
}
|
||||
|
||||
//删除订单
|
||||
const onDeleteOrder = () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除订单吗',
|
||||
showCancel: true,
|
||||
success: async ({ confirm, cancel }) => {
|
||||
if (confirm) {
|
||||
await deleteOrderApi({
|
||||
id: [orderId],
|
||||
})
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success',
|
||||
mask: true,
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({ url: `${pageUrl['order-list']}` })
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
onShowRefreshData(() => {
|
||||
getDetailData()
|
||||
}, true)
|
||||
|
||||
const showDelivery = computed(() => {
|
||||
return (
|
||||
[orderState.toReceived, orderState.toEvaluate, orderState.completed].includes(
|
||||
detailData.value!.status,
|
||||
) && detailData.value!.delivery.length > 0
|
||||
)
|
||||
})
|
||||
|
||||
const onCancel = () => {
|
||||
if (detailData.value?.pay_time) {
|
||||
detailData.value!.status = orderState.applyCancel
|
||||
} else {
|
||||
detailData.value!.status = orderState.canceled
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<scroll-view scroll-y class="viewport" id="scroller">
|
||||
<template v-if="detailData">
|
||||
<!-- 订单状态 -->
|
||||
<view class="overview" :style="{ paddingTop: '30rpx' }">
|
||||
<!-- 待付款状态:展示去支付按钮和倒计时 -->
|
||||
<template v-if="detailData.status === orderState.toPay">
|
||||
<view class="status icon-clock">{{ orderStateList[detailData.status].text }}</view>
|
||||
<view class="tips">
|
||||
<view class="time">
|
||||
<view>还剩</view>
|
||||
<uni-countdown
|
||||
:second="detailData.countdown"
|
||||
@timeup.once="onTimeUp"
|
||||
color="#fff"
|
||||
splitor-color="#fff"
|
||||
:show-day="false"
|
||||
:show-colon="false"
|
||||
/>
|
||||
<view>自动取消</view>
|
||||
</view>
|
||||
</view>
|
||||
<navigator
|
||||
class="button primary"
|
||||
:url="getPayPageUrl(orderId, detailData.pay_price)"
|
||||
hover-class="none"
|
||||
>
|
||||
去支付
|
||||
</navigator>
|
||||
</template>
|
||||
<!-- 其他订单状态:展示再次购买按钮 -->
|
||||
<template v-else>
|
||||
<!-- 订单状态文字 -->
|
||||
<view class="status"> {{ orderStateList[detailData.status].text }} </view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<view class="shipment" v-show="detailData.address">
|
||||
<view class="locate">
|
||||
<view class="user"> {{ detailData.address.name }} {{ detailData.address.phone }} </view>
|
||||
<view class="address">{{ detailData.address.full_address }} </view>
|
||||
</view>
|
||||
<!-- 配送状态 -->
|
||||
<view class="item" v-if="showDelivery">
|
||||
<view class="delivery-card">
|
||||
<template v-if="detailData.delivery.length > 1">
|
||||
<navigator
|
||||
class="delivery"
|
||||
:url="`${pageUrl['order-delivery']}?order_id=${orderId}`"
|
||||
hover-class="none"
|
||||
>
|
||||
<view v-if="detailData.status == orderState.toReceived" class="delivery-tip">
|
||||
<text>订单拆分为 {{ detailData.delivery.length }} 个包裹发货</text>
|
||||
</view>
|
||||
<view v-else class="delivery-tip">
|
||||
<text>订单拆分为多个包裹发货</text>
|
||||
<text>已发货 {{ detailData.delivery.length }} 个包裹</text>
|
||||
</view>
|
||||
<view class="more">
|
||||
<text class="icon-right"></text>
|
||||
</view>
|
||||
</navigator>
|
||||
</template>
|
||||
<template v-else>
|
||||
<navigator
|
||||
class="delivery"
|
||||
:url="`${pageUrl['order-delivery']}?order_id=${orderId}`"
|
||||
hover-class="none"
|
||||
v-for="(item, idx) in detailData.delivery"
|
||||
:key="idx"
|
||||
>
|
||||
<view class="delivery-info">
|
||||
<view class="info-item">
|
||||
<view class="item-label">物流公司</view>
|
||||
<view class="item-content">
|
||||
{{ item.delivery_company?.name || '无需物流' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<view class="item-label">物流单号</view>
|
||||
<view class="item-content">{{ item.delivery_no || '' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="more">
|
||||
<text class="icon-right"></text>
|
||||
</view>
|
||||
</navigator>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view class="goods" v-if="detailData.detail.length > 0">
|
||||
<view class="item" v-for="detail in detailData.detail" :key="detail.id">
|
||||
<navigator
|
||||
class="navigator"
|
||||
:url="`${pageUrl['goods-detail']}?id=${detail.goods_id}`"
|
||||
hover-class="none"
|
||||
>
|
||||
<view> <image class="cover" :src="fullUrl(detail.image)"></image> </view>
|
||||
<view class="meta">
|
||||
<view class="name ellipsis">{{ detail.goods_name }}</view>
|
||||
<view class="type">{{ detail.spec_text }}</view>
|
||||
<view class="price">
|
||||
<view class="actual">
|
||||
<text class="symbol">¥</text>
|
||||
<text>{{ detail.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="quantity">x{{ detail.num }}</view>
|
||||
</view>
|
||||
</navigator>
|
||||
|
||||
<view class="operate">
|
||||
<view
|
||||
v-if="
|
||||
orderStateActions.evaluatable.includes(detailData.status) &&
|
||||
detail.evaluate_time <= 0
|
||||
"
|
||||
>
|
||||
<navigator
|
||||
:url="`${pageUrl['order-evaluate']}?detail_id=${detail.id}`"
|
||||
open-type="navigate"
|
||||
hover-class="navigator-hover"
|
||||
class="button"
|
||||
>
|
||||
去评价
|
||||
</navigator>
|
||||
</view>
|
||||
|
||||
<view v-if="detail.apply_refund">
|
||||
<navigator
|
||||
:url="`${pageUrl['order-refund']}?order_id=${
|
||||
detailData.id
|
||||
}&detail_data=${JSON.stringify(detail)}`"
|
||||
open-type="navigate"
|
||||
hover-class="navigator-hover"
|
||||
class="button"
|
||||
>
|
||||
申请售后
|
||||
</navigator>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="goods">
|
||||
<view class="total">
|
||||
<view class="row">
|
||||
<view class="text">商品总价</view>
|
||||
<view class="symbol">{{ formatPrice(detailData.total_price) }}</view>
|
||||
</view>
|
||||
<view class="row">
|
||||
<view class="text">商品运费</view>
|
||||
<view class="symbol">{{ formatPrice(detailData.freight) }}</view>
|
||||
</view>
|
||||
<view class="row">
|
||||
<view class="text">优惠折扣</view>
|
||||
<view class="symbol discount">
|
||||
{{ formatPrice(detailData.discount_data.coupon_discount) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="row">
|
||||
<view class="text">应付金额</view>
|
||||
<view class="symbol">{{ formatPrice(detailData.pay_price) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息 -->
|
||||
<view class="detail">
|
||||
<view class="row">
|
||||
<view class="item">
|
||||
<view class="item-title"> 订单编号</view>
|
||||
<view>
|
||||
{{ detailData.order_no }}
|
||||
<text class="copy" @tap="onCopy">复制</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="item-title">下单时间</view>
|
||||
<view>{{ detailData.create_time }}</view>
|
||||
</view>
|
||||
<view class="item" v-if="detailData.pay_time">
|
||||
<view class="item-title">支付时间</view>
|
||||
<view> {{ detailData.pay_time }}</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="item-title">买家留言</view>
|
||||
<view style="width: 80%">{{ detailData.remark || '--' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="toolbar-height" :style="safeBottom"></view>
|
||||
<view class="toolbar" :style="safeBottom">
|
||||
<!-- 待付款状态:展示支付按钮 -->
|
||||
<template v-if="detailData.status === orderState.toPay">
|
||||
<cancelButton
|
||||
:status="detailData.status"
|
||||
:id="detailData.id"
|
||||
className="button secondary"
|
||||
@cancel="onCancel"
|
||||
/>
|
||||
<navigator
|
||||
class="button primary"
|
||||
:url="getPayPageUrl(detailData.id, detailData.pay_price)"
|
||||
hover-class="none"
|
||||
>
|
||||
去支付
|
||||
</navigator>
|
||||
</template>
|
||||
<!-- 其他订单状态:按需展示按钮 -->
|
||||
<template v-else>
|
||||
<view v-if="orderStateActions.deletable.includes(detailData.status)">
|
||||
<view class="button secondary" @tap="onDeleteOrder"> 删除订单 </view>
|
||||
</view>
|
||||
|
||||
<cancelButton
|
||||
:status="detailData.status"
|
||||
:id="detailData.id"
|
||||
className="button secondary"
|
||||
@cancel="onCancel"
|
||||
/>
|
||||
|
||||
<receiveButton
|
||||
:status="detailData.status"
|
||||
:id="detailData.id"
|
||||
:payment-trade-info="detailData.payment_trade"
|
||||
className="button secondary"
|
||||
/>
|
||||
|
||||
<view v-if="orderStateActions.evaluatable.includes(detailData.status)">
|
||||
<navigator
|
||||
:url="`${pageUrl['order-evaluate']}?order_id=${orderId}`"
|
||||
class="button secondary"
|
||||
hover-class="none"
|
||||
>
|
||||
一键评价
|
||||
</navigator>
|
||||
</view>
|
||||
|
||||
<navigator
|
||||
class="button primary"
|
||||
:url="`${pageUrl['order-create']}?orderId=${orderId}`"
|
||||
hover-class="none"
|
||||
>
|
||||
再次购买
|
||||
</navigator>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.6;
|
||||
padding: 20rpx;
|
||||
margin: 20rpx;
|
||||
border-radius: 30rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
width: 750rpx;
|
||||
color: #000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9;
|
||||
/* background-color: #f8f8f8; */
|
||||
background-color: transparent;
|
||||
|
||||
.wrap {
|
||||
position: relative;
|
||||
|
||||
.title {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
/* color: #000; */
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.back {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
font-size: 44rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
/* color: #000; */
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.viewport {
|
||||
background-color: #f7f7f8;
|
||||
}
|
||||
|
||||
.overview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 35rpx;
|
||||
color: #fff;
|
||||
background-image: url('~@/static/images/bg.png');
|
||||
background-size: cover;
|
||||
|
||||
.status {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.status::before {
|
||||
margin-right: 6rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin: 30rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
|
||||
.money {
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx;
|
||||
padding-top: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin-top: 30rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 260rpx;
|
||||
height: 64rpx;
|
||||
margin: 0 10rpx;
|
||||
text-align: center;
|
||||
line-height: 64rpx;
|
||||
font-size: 28rpx;
|
||||
color: #ff5f3c;
|
||||
border-radius: 68rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.shipment {
|
||||
line-height: 1.4;
|
||||
padding: 0 20rpx;
|
||||
margin: 20rpx 20rpx 20rpx;
|
||||
border-radius: 30rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.locate,
|
||||
.item {
|
||||
// min-height: 120rpx;
|
||||
padding: 30rpx 30rpx 25rpx 75rpx;
|
||||
background-size: 50rpx;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 6rpx center;
|
||||
}
|
||||
|
||||
.locate {
|
||||
background-image: url('~@/static/images/locate.png');
|
||||
border-bottom: 1rpx solid #eee;
|
||||
|
||||
.user {
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.address {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
background-image: url('~@/static/images/car.png');
|
||||
border-bottom: 1rpx solid #eee;
|
||||
position: relative;
|
||||
|
||||
.message {
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.delivery {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.delivery-tip {
|
||||
font-size: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
font-size: 24rpx;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
color: #999999;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.more {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods {
|
||||
margin: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 30rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.item {
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
|
||||
.navigator {
|
||||
display: flex;
|
||||
// margin: 20rpx 0;
|
||||
}
|
||||
|
||||
.cover {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.meta {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.type {
|
||||
line-height: 1.8;
|
||||
padding: 0 15rpx;
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
align-self: flex-start;
|
||||
border-radius: 4rpx;
|
||||
color: #888;
|
||||
background-color: #f7f7f8;
|
||||
max-width: 91%;
|
||||
}
|
||||
|
||||
.price {
|
||||
display: flex;
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.symbol {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.original {
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.actual {
|
||||
margin-left: 10rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.operate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
border-radius: 60rpx;
|
||||
height: 50rpx;
|
||||
width: 150rpx;
|
||||
align-items: center;
|
||||
border: 1rpx solid #ccc;
|
||||
color: #444;
|
||||
font-size: 20rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.quantity {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
font-size: 24rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-start;
|
||||
padding: 30rpx 0 0;
|
||||
|
||||
.button {
|
||||
width: 200rpx;
|
||||
height: 60rpx;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
line-height: 60rpx;
|
||||
margin-left: 20rpx;
|
||||
border-radius: 60rpx;
|
||||
border: 1rpx solid #ccc;
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #ff5f3c;
|
||||
border-color: #ff5f3c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.total {
|
||||
line-height: 1.4;
|
||||
font-size: 26rpx;
|
||||
padding: 20rpx 0;
|
||||
color: #666;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10rpx 0;
|
||||
}
|
||||
|
||||
.symbol::before {
|
||||
content: '¥';
|
||||
font-size: 80%;
|
||||
margin-right: 3rpx;
|
||||
}
|
||||
|
||||
.discount::before {
|
||||
content: '- ¥';
|
||||
font-size: 80%;
|
||||
margin-right: 3rpx;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #cf4444;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail {
|
||||
line-height: 1.4;
|
||||
padding: 20rpx;
|
||||
margin: 20rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
border-radius: 30rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 20rpx 0;
|
||||
|
||||
.item {
|
||||
padding: 10rpx 0;
|
||||
display: flex;
|
||||
|
||||
.item-title {
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.copy {
|
||||
border-radius: 20rpx;
|
||||
font-size: 20rpx;
|
||||
border: 1px solid #ccc;
|
||||
padding: 5rpx 20rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-height {
|
||||
height: 100rpx;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: calc(var(--window-bottom));
|
||||
z-index: 1;
|
||||
|
||||
height: 100rpx;
|
||||
padding: 0 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
border-top: 1rpx solid #ededed;
|
||||
border-bottom: 1rpx solid #ededed;
|
||||
background-color: #fff;
|
||||
box-sizing: content-box;
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: 150rpx;
|
||||
height: 60rpx;
|
||||
margin-left: 15rpx;
|
||||
font-size: 26rpx;
|
||||
border-radius: 72rpx;
|
||||
border: 1rpx solid #ccc;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.delete {
|
||||
order: 4;
|
||||
}
|
||||
|
||||
.button {
|
||||
order: 3;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
order: 2;
|
||||
color: #ff5f3c;
|
||||
border-color: #ff5f3c;
|
||||
}
|
||||
|
||||
.primary {
|
||||
order: 1;
|
||||
color: #fff;
|
||||
background-color: #ff5f3c;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,221 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { cleanContent } from '@/utils/common'
|
||||
import { postOrderEvaluateApi } from '@/api/order'
|
||||
import { ref } from 'vue'
|
||||
import { uploadApi } from '@/api/common'
|
||||
|
||||
const props = defineProps<{
|
||||
detail_id?: string
|
||||
order_id?: string
|
||||
}>()
|
||||
|
||||
const content = ref('')
|
||||
const images = ref([])
|
||||
|
||||
const score = ref(5)
|
||||
const rateArr = ['非常差', '差', '一般', '好', '非常好']
|
||||
const rateStr = ref(rateArr[score.value - 1])
|
||||
const onChange = (event: UniHelper.UniRateOnChangeEvent) => {
|
||||
rateStr.value = rateArr[event.value - 1]
|
||||
}
|
||||
|
||||
const tmpPathArr = ref<string[]>([])
|
||||
const imagePathArr = ref<string[]>([])
|
||||
const onSelect = (e: UniHelper.UniFilePickerOnSelectEvent) => {
|
||||
tmpPathArr.value = tmpPathArr.value.concat(e.tempFilePaths)
|
||||
}
|
||||
|
||||
const onDelete = (e: UniHelper.UniFilePickerOnDeleteEvent) => {
|
||||
// 移除图片
|
||||
const index = tmpPathArr.value.findIndex((item) => item === e.tempFilePath)
|
||||
tmpPathArr.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
const uploadPromises = tmpPathArr.value.map((item) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uploadApi(item).then((res) => {
|
||||
// console.log(res)
|
||||
imagePathArr.value.push(res.result.url)
|
||||
resolve(res)
|
||||
})
|
||||
})
|
||||
})
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '数据提交中...',
|
||||
})
|
||||
await Promise.all(uploadPromises)
|
||||
const res = await postOrderEvaluateApi({
|
||||
detail_id: props.detail_id ?? '',
|
||||
order_id: props.order_id ?? '',
|
||||
score: score.value,
|
||||
content: cleanContent(content.value),
|
||||
images: imagePathArr.value.join(','),
|
||||
})
|
||||
if (res.msg) {
|
||||
uni.showToast({
|
||||
title: res.msg,
|
||||
icon: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.showToast({
|
||||
title: '评价成功',
|
||||
icon: 'success',
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
}, 1500)
|
||||
} catch (err) {
|
||||
// 处理错误
|
||||
console.log(err)
|
||||
uni.showToast({
|
||||
title: '评价失败,请刷新重试',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="body">
|
||||
<view class="top">
|
||||
<text class="text">商品评价</text>
|
||||
<view class="rate">
|
||||
<uni-rate v-model="score" @change="onChange" />
|
||||
</view>
|
||||
<text class="rate-text">{{ rateStr }}</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<uni-easyinput
|
||||
type="textarea"
|
||||
autoHeight
|
||||
v-model="content"
|
||||
placeholder="请输入评价内容"
|
||||
:inputBorder="false"
|
||||
></uni-easyinput>
|
||||
</view>
|
||||
<view class="image">
|
||||
<uni-file-picker
|
||||
v-model="images"
|
||||
file-mediatype="image"
|
||||
mode="grid"
|
||||
file-extname="png,jpg"
|
||||
:limit="9"
|
||||
return-type="array"
|
||||
@select="onSelect"
|
||||
@delete="onDelete"
|
||||
:auto-upload="false"
|
||||
>
|
||||
<view class="upload">
|
||||
<uni-icons type="camera" color="#ff0000" size="30"></uni-icons>
|
||||
<text class="text">添加图片</text>
|
||||
</view>
|
||||
</uni-file-picker>
|
||||
</view>
|
||||
</view>
|
||||
<shop-submit-button @tap="onSubmit">提交评价</shop-submit-button>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
border: 1rpx solid #ccc;
|
||||
|
||||
.top {
|
||||
padding: 20rpx 15rpx 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
|
||||
.rate {
|
||||
margin: 0 20rpx 0 0rpx;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.rate-text {
|
||||
font-size: 20rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
margin-top: 10rpx;
|
||||
padding: 15rpx;
|
||||
|
||||
.uni-easyinput {
|
||||
width: 100%;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.5;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
margin-top: 10rpx;
|
||||
padding: 0;
|
||||
align-items: flex-start;
|
||||
|
||||
.upload {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
|
||||
.text {
|
||||
color: #666;
|
||||
font-size: 27rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sub-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
.button-container {
|
||||
background-color: white;
|
||||
padding: 10rpx;
|
||||
border-radius: 20rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
background-color: #ff5f3c;
|
||||
color: white;
|
||||
border-radius: 20rpx;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,438 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { deleteOrderApi, getOrderListApi } from '@/api/order'
|
||||
import type { orderListResult } from '@/types/order'
|
||||
import { ref, watch } from 'vue'
|
||||
import {
|
||||
orderState,
|
||||
orderStateList,
|
||||
orderType,
|
||||
orderStateActions,
|
||||
refundStateList,
|
||||
pageUrl,
|
||||
} from '@/utils/constants'
|
||||
import { fullUrl, getPayPageUrl } from '@/utils/common'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import cancelButton from '@/pagesOrder/components/cancelButton.vue'
|
||||
import { usePopupStore } from '@/stores'
|
||||
import ReceiveButton from '@/pagesOrder/components/receiveButton.vue'
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const paging = ref<ZPagingInstance>()
|
||||
const dataList = ref<orderListResult[]>([])
|
||||
// 当前组件是否已经加载过了
|
||||
const firstLoaded = ref(false)
|
||||
// 是否滚动到当前页
|
||||
const isCurrentPage = ref(false)
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
tabIndex: number
|
||||
currentIndex: number
|
||||
status: string
|
||||
}>(),
|
||||
{
|
||||
tableIndex: 0,
|
||||
currentIndex: 0,
|
||||
status: '',
|
||||
},
|
||||
)
|
||||
const queryList = (page: number, page_size: number) => {
|
||||
const params = {
|
||||
page,
|
||||
page_size,
|
||||
type: orderType.default,
|
||||
status: props.status,
|
||||
}
|
||||
getOrderListApi(params).then((res) => {
|
||||
res.result.data.map((item) => {
|
||||
if (item.status == orderState.toPay && item.countdown < 0) {
|
||||
item.status = orderState.canceled
|
||||
}
|
||||
})
|
||||
paging.value?.complete(res.result.data)
|
||||
})
|
||||
}
|
||||
|
||||
// 删除订单
|
||||
const onDeleteOrder = (id: number) => {
|
||||
uni.showModal({
|
||||
content: '确定要删除订单吗?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
deleteOrderApi({
|
||||
id: [id],
|
||||
}).then(() => {
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success',
|
||||
})
|
||||
setTimeout(() => {
|
||||
paging.value?.refresh()
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const onCancel = (event: { orderId: number; reason: string }) => {
|
||||
const index = dataList.value.findIndex((v) => v.id == event.orderId)
|
||||
if (index > -1) {
|
||||
dataList.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.currentIndex,
|
||||
(newVal) => {
|
||||
if (newVal === props.tabIndex) {
|
||||
// 懒加载,当滑动到当前的item时,才去加载
|
||||
if (!firstLoaded.value) {
|
||||
// 这里需要延迟渲染z-paging的原因是为了避免在一些平台上立即渲染可能引发的底层报错问题
|
||||
setTimeout(() => {
|
||||
isCurrentPage.value = true
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
|
||||
onShow(() => {
|
||||
if (props.currentIndex === props.tabIndex) {
|
||||
paging.value?.refresh()
|
||||
}
|
||||
})
|
||||
|
||||
const backToTop = ref(true)
|
||||
watch(
|
||||
() => usePopupStore().show,
|
||||
(newVal) => {
|
||||
backToTop.value = !newVal
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<!-- 在这个文件对每个tab对应的列表进行渲染 -->
|
||||
<template>
|
||||
<view class="content">
|
||||
<!-- 如果当前页已经加载过数据或者当前切换到的tab是当前页,才展示当前页数据(懒加载) -->
|
||||
<z-paging
|
||||
:enable-back-to-top="currentIndex === tabIndex"
|
||||
v-if="firstLoaded || isCurrentPage"
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
:auto-show-back-to-top="backToTop"
|
||||
:back-to-top-style="{
|
||||
display: backToTop ? '' : 'none',
|
||||
}"
|
||||
bg-color="#f6f6f6"
|
||||
>
|
||||
<view class="orders" v-for="(item, index) in dataList" :key="index">
|
||||
<view class="card">
|
||||
<view class="status">
|
||||
<text class="date">{{ item.create_time }}</text>
|
||||
<text v-if="item.status === orderState.applyCancel">
|
||||
{{ refundStateList.find((v) => v.id == item.refund_status)?.text ?? '' }}
|
||||
</text>
|
||||
<text v-else>{{ orderStateList[item.status].text ?? '' }}</text>
|
||||
<text
|
||||
class="icon-delete"
|
||||
v-show="orderStateActions.deletable.includes(item.status)"
|
||||
@tap="onDeleteOrder(item.id)"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<navigator
|
||||
v-for="sku in item.detail"
|
||||
:key="sku.id"
|
||||
class="goods"
|
||||
:url="`${pageUrl['order-detail']}?id=${item.id}`"
|
||||
hover-class="none"
|
||||
>
|
||||
<image class="cover" :src="fullUrl(sku.image)"></image>
|
||||
|
||||
<view class="meta">
|
||||
<view class="name ellipsis">{{ sku.goods_name }}</view>
|
||||
<view class="name meta-spec">{{ sku.spec }}</view>
|
||||
<view class="meta-quantity">x{{ sku.num }}</view>
|
||||
</view>
|
||||
</navigator>
|
||||
|
||||
<view class="payment">
|
||||
<text class="quantity">共{{ item.total_num }}件商品</text>
|
||||
<text v-if="orderStateActions.normalTransaction.includes(item.status)">实付</text>
|
||||
<text v-else>应付</text>
|
||||
<text class="amount"> <text class="symbol">¥</text>{{ item.pay_price }}</text>
|
||||
</view>
|
||||
|
||||
<view class="action">
|
||||
<template v-if="item.status === orderState.toPay && item.countdown > 0">
|
||||
<text class="pay-time">支付剩余</text>
|
||||
<countdown-timer :time="item.countdown * 1000" :autoStart="true">
|
||||
<template v-slot="{ day, hour, minute, second }">
|
||||
<view v-if="item.countdown > 86400">{{ day }}天{{ hour }}时{{ minute }}分</view>
|
||||
<view v-else-if="item.countdown < 60">{{ second }}秒</view>
|
||||
<view v-else>{{ hour }}时{{ minute }}分</view>
|
||||
</template>
|
||||
</countdown-timer>
|
||||
<cancelButton
|
||||
:status="item.status"
|
||||
:id="item.id"
|
||||
className="button secondary"
|
||||
@cancel="onCancel"
|
||||
/>
|
||||
<navigator
|
||||
class="button primary"
|
||||
:url="getPayPageUrl(item.id, item.pay_price)"
|
||||
hover-class="none"
|
||||
>
|
||||
去支付
|
||||
</navigator>
|
||||
</template>
|
||||
<template v-else>
|
||||
<cancelButton
|
||||
:status="item.status"
|
||||
:id="item.id"
|
||||
className="button secondary"
|
||||
@cancel="onCancel"
|
||||
/>
|
||||
|
||||
<ReceiveButton
|
||||
:status="item.status"
|
||||
:id="item.id"
|
||||
:paymentTradeInfo="item.payment_trade"
|
||||
className="button secondary"
|
||||
/>
|
||||
|
||||
<view v-if="orderStateActions.evaluatable.includes(item.status)">
|
||||
<navigator
|
||||
:url="`${pageUrl['order-evaluate']}?order_id=${item.id}`"
|
||||
class="button secondary"
|
||||
hover-class="none"
|
||||
>
|
||||
一键评价
|
||||
</navigator>
|
||||
</view>
|
||||
|
||||
<navigator
|
||||
class="button primary"
|
||||
:url="`${pageUrl['order-create']}?orderId=${item.id}`"
|
||||
hover-class="none"
|
||||
>
|
||||
再次购买
|
||||
</navigator>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 注意:父节点需要固定高度,z-paging的height:100%才会生效 */
|
||||
.content {
|
||||
height: 100%;
|
||||
}
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
.orders {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
.card {
|
||||
min-height: 100rpx;
|
||||
padding: 20rpx;
|
||||
margin: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-bottom: 15rpx;
|
||||
|
||||
.date {
|
||||
color: #666;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #ff9240;
|
||||
}
|
||||
|
||||
.icon-delete {
|
||||
line-height: 1;
|
||||
margin-left: 10rpx;
|
||||
padding-left: 10rpx;
|
||||
border-left: 1rpx solid #e3e3e3;
|
||||
}
|
||||
}
|
||||
|
||||
.goods {
|
||||
display: flex;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.cover {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
margin-right: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quantity {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
line-height: 1;
|
||||
padding: 6rpx 4rpx 6rpx 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
border-radius: 10rpx 0 0 0;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.meta {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
|
||||
.meta-spec {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
&-quantity {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
font-size: 24rpx;
|
||||
color: #9e9c9c;
|
||||
}
|
||||
}
|
||||
|
||||
.type {
|
||||
line-height: 1.8;
|
||||
padding: 0 15rpx;
|
||||
margin-top: 10rpx;
|
||||
font-size: 24rpx;
|
||||
align-self: flex-start;
|
||||
border-radius: 4rpx;
|
||||
color: #888;
|
||||
background-color: #f7f7f8;
|
||||
}
|
||||
|
||||
.more {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.payment {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
padding: 20rpx 0;
|
||||
text-align: right;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
|
||||
.quantity {
|
||||
font-size: 24rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.amount {
|
||||
color: #444;
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
|
||||
.symbol {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-top: 20rpx;
|
||||
|
||||
.pay-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 150rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 20rpx;
|
||||
border-radius: 60rpx;
|
||||
border: 1rpx solid #ccc;
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
color: #ff5f3c;
|
||||
border-color: #ff5f3c;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #fff;
|
||||
background-color: #ff5f3c;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,119 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import orderListItem from '@/pagesOrder/pages/list/components/order-list-item.vue'
|
||||
import { usePopupStore } from '@/stores'
|
||||
import { pageMateStyle } from '@/utils/constants'
|
||||
|
||||
//页面传参
|
||||
const query = withDefaults(
|
||||
defineProps<{
|
||||
status?: string
|
||||
}>(),
|
||||
{
|
||||
status: '',
|
||||
},
|
||||
)
|
||||
|
||||
const tabs = ref()
|
||||
const tabList = ref([
|
||||
{
|
||||
name: '全部',
|
||||
value: '',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
name: '待付款',
|
||||
value: '1',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
name: '待发货',
|
||||
value: '2',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
name: '待收货',
|
||||
value: '3',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
name: '待评价',
|
||||
value: '4',
|
||||
disabled: false,
|
||||
},
|
||||
])
|
||||
|
||||
// 当前tab下标
|
||||
const sub = tabList.value.findIndex((item) => item.value == query.status)
|
||||
const current = ref(sub == -1 ? 0 : sub)
|
||||
// tabs通知swiper切换
|
||||
const tabsChange = (index: number) => {
|
||||
current.value = index
|
||||
}
|
||||
|
||||
// swiper滑动中
|
||||
const swiperTransition = (e: UniHelper.SwiperOnTransitionEvent) => {
|
||||
if (!current.value) {
|
||||
tabs.value.setDx(e.detail.dx)
|
||||
}
|
||||
}
|
||||
|
||||
// swiper滑动结束
|
||||
const swiperAnimationfinish = (e: UniHelper.SwiperOnAnimationfinishEvent) => {
|
||||
current.value = e.detail.current
|
||||
if (current.value) {
|
||||
tabs.value.unlockDx()
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => usePopupStore().show,
|
||||
(newVal) => {
|
||||
tabList.value.forEach((item) => (item.disabled = newVal))
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- #ifdef MP-WEIXIN-->
|
||||
<page-mate :pageStyle="pageMateStyle">
|
||||
<!-- #endif -->
|
||||
<z-paging-swiper>
|
||||
<template #top>
|
||||
<z-tabs
|
||||
ref="tabs"
|
||||
:list="tabList"
|
||||
:current="current"
|
||||
@change="tabsChange"
|
||||
bg-color="#f6f6f6"
|
||||
/>
|
||||
</template>
|
||||
<swiper
|
||||
class="swiper"
|
||||
:current="current"
|
||||
@transition="swiperTransition"
|
||||
@animationfinish="swiperAnimationfinish"
|
||||
>
|
||||
<swiper-item class="swiper-item" v-for="(item, index) in tabList" :key="index">
|
||||
<order-list-item
|
||||
:tabIndex="index"
|
||||
:currentIndex="current"
|
||||
:status="tabList[current].value"
|
||||
></order-list-item>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</z-paging-swiper>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
</page-mate>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
.swiper {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
@ -1,244 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { pageUrl } from '@/utils/constants'
|
||||
import { getMemberProfileApi } from '@/api/member'
|
||||
import { payOrderApi } from '@/api/pay'
|
||||
import _ from 'lodash'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { onShowRefreshData } from '@/utils/common'
|
||||
|
||||
// 获取页面参数
|
||||
const props = defineProps<{
|
||||
orderId: string
|
||||
payPrice: string
|
||||
}>()
|
||||
|
||||
const state = reactive({
|
||||
orderId: Number(props.orderId),
|
||||
balance: '0.00',
|
||||
})
|
||||
|
||||
const getBalance = async () => {
|
||||
const res = await getMemberProfileApi()
|
||||
state.balance = res.result.money || '0.00'
|
||||
}
|
||||
|
||||
const payMethods = computed(() => {
|
||||
return [
|
||||
// #ifdef WEB
|
||||
{
|
||||
name: '支付宝支付',
|
||||
value: 'alipay',
|
||||
icon: '/static/icons/alipay.svg',
|
||||
},
|
||||
// #endif
|
||||
{
|
||||
name: '微信支付',
|
||||
value: 'wxpay',
|
||||
icon: '/static/icons/wechat-pay.svg',
|
||||
},
|
||||
{
|
||||
name: `余额支付(可用¥${state.balance})`,
|
||||
value: 'balance',
|
||||
icon: '/static/icons/balance.svg',
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const current = ref()
|
||||
const payWay = ref('')
|
||||
|
||||
const radioChange = (event: UniHelper.RadioGroupOnChangeEvent) => {
|
||||
payWay.value = event.detail.value
|
||||
}
|
||||
|
||||
const onPayment = async () => {
|
||||
if (!payWay.value) {
|
||||
return uni.showToast({
|
||||
title: '请选择支付方式',
|
||||
icon: 'none',
|
||||
mask: true,
|
||||
})
|
||||
}
|
||||
if (payWay.value === 'balance' && parseFloat(props.payPrice) > parseFloat(state.balance)) {
|
||||
return uni.showToast({
|
||||
title: '很抱歉,您的余额不足',
|
||||
icon: 'none',
|
||||
mask: true,
|
||||
})
|
||||
}
|
||||
try {
|
||||
const res = await payOrderApi({
|
||||
id: state.orderId,
|
||||
pay_way: payWay.value,
|
||||
})
|
||||
switch (payWay.value) {
|
||||
case 'wxpay':
|
||||
if (uni.getSystemInfoSync().uniPlatform === 'web') {
|
||||
doH5Wxpay(res)
|
||||
} else {
|
||||
doWxPay(res.result)
|
||||
}
|
||||
break
|
||||
case 'alipay':
|
||||
doAlipay(res)
|
||||
break
|
||||
case 'balance':
|
||||
successHandle()
|
||||
break
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.log(err)
|
||||
// 捕获异常并处理
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: err.data.msg || '支付失败',
|
||||
showCancel: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const doWxPay = async (result: WechatMiniprogram.RequestPaymentOption) => {
|
||||
wx.requestPayment({
|
||||
...result,
|
||||
success: () => {
|
||||
successHandle()
|
||||
},
|
||||
fail: (fail) => {
|
||||
failHandle(fail)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const doH5Wxpay = (res: any) => {
|
||||
wx.showLoading({
|
||||
title: '正在支付…',
|
||||
mask: true,
|
||||
})
|
||||
|
||||
location.href = res.result.h5_url
|
||||
}
|
||||
|
||||
const doAlipay = (res: any) => {
|
||||
wx.showLoading({
|
||||
title: '正在支付…',
|
||||
mask: true,
|
||||
})
|
||||
|
||||
location.href = res.result.url
|
||||
}
|
||||
|
||||
const successHandle = () => {
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({
|
||||
url: `${pageUrl['order-payment']}?id=${state.orderId}&pay_way=${payWay.value}`,
|
||||
})
|
||||
}, 500)
|
||||
}
|
||||
const failHandle = (err: any) => {
|
||||
console.warn('支付异常:')
|
||||
console.warn(err)
|
||||
if (err.errMsg == 'requestPayment:fail cancel') {
|
||||
uni.showToast({
|
||||
title: '订单取消支付~',
|
||||
icon: 'none',
|
||||
})
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: err.data.msg ? err.data.msg : '支付未完成',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onShowRefreshData(() => {
|
||||
getBalance()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="overview">
|
||||
<view class="money">
|
||||
<span class="currency">¥</span>
|
||||
<span class="integer">{{ props.payPrice.split('.')[0] }}.</span>
|
||||
<span class="decimal">{{ props.payPrice.split('.')[1] }}</span>
|
||||
</view>
|
||||
<view class="payment">
|
||||
<radio-group @change="radioChange" class="card">
|
||||
<label class="item" v-for="(item, index) in payMethods" :key="item.value">
|
||||
<view class="method">
|
||||
<span><image :src="item.icon" class="icon" /></span>
|
||||
{{ item.name }}
|
||||
</view>
|
||||
<view>
|
||||
<radio :value="item.value" :checked="current === index" />
|
||||
</view>
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
<shop-submit-button title="确认支付" @tap="onPayment"></shop-submit-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.overview {
|
||||
background-color: #f0f0f0;
|
||||
|
||||
.money {
|
||||
padding: 5%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
color: red;
|
||||
font-size: 50rpx;
|
||||
|
||||
.currency {
|
||||
font-size: 35rpx;
|
||||
vertical-align: sub;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.integer {
|
||||
font-size: 60rpx;
|
||||
}
|
||||
|
||||
.decimal {
|
||||
font-size: 50rpx;
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.payment {
|
||||
margin: 20rpx 20rpx 0;
|
||||
padding: auto;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 5%;
|
||||
}
|
||||
|
||||
.method {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,133 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { pageUrl } from '@/utils/constants'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { postPayNotifyApi } from '@/api/pay'
|
||||
import { ref } from 'vue'
|
||||
|
||||
// 获取页面参数
|
||||
const query = defineProps<{
|
||||
id: number | string
|
||||
pay_way: string
|
||||
}>()
|
||||
|
||||
const isPay = ref(false)
|
||||
|
||||
const checkPay = () => {
|
||||
if (query.pay_way === 'balance') {
|
||||
isPay.value = true
|
||||
return
|
||||
}
|
||||
|
||||
postPayNotifyApi({
|
||||
id: Number(query.id),
|
||||
pay_way: query.pay_way,
|
||||
})
|
||||
.then(() => {
|
||||
isPay.value = true
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn(err)
|
||||
if (err.errMsg == 'requestPayment:fail cancel') {
|
||||
uni.showToast({
|
||||
title: '订单取消支付~',
|
||||
icon: 'none',
|
||||
})
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: err.data.msg ? err.data.msg : '支付未完成',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({
|
||||
url: `${pageUrl['order-list']}`,
|
||||
})
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
checkPay()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<scroll-view enable-back-to-top class="viewport" scroll-y>
|
||||
<!-- 订单状态 -->
|
||||
<view class="overview" v-if="isPay">
|
||||
<view class="status icon-checked">支付成功</view>
|
||||
<view class="buttons">
|
||||
<navigator
|
||||
hover-class="none"
|
||||
class="button navigator"
|
||||
:url="`${pageUrl['index']}`"
|
||||
open-type="switchTab"
|
||||
>
|
||||
返回首页
|
||||
</navigator>
|
||||
<navigator
|
||||
hover-class="none"
|
||||
class="button navigator"
|
||||
:url="`${pageUrl['order-detail']}?id=${query.id}`"
|
||||
open-type="redirect"
|
||||
>
|
||||
查看订单
|
||||
</navigator>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.viewport {
|
||||
background-color: #f7f7f8;
|
||||
}
|
||||
|
||||
.overview {
|
||||
line-height: 1;
|
||||
padding: 50rpx 0;
|
||||
color: #fff;
|
||||
background-color: #ff7c22;
|
||||
|
||||
.status {
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status::before {
|
||||
display: block;
|
||||
font-size: 110rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 60rpx;
|
||||
}
|
||||
|
||||
.button {
|
||||
text-align: center;
|
||||
margin: 0 10rpx;
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
|
||||
&:first-child {
|
||||
width: 200rpx;
|
||||
border-radius: 64rpx;
|
||||
border: 1rpx solid #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,89 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { pageUrl } from '@/utils/constants'
|
||||
|
||||
// 获取页面参数
|
||||
const query = defineProps<{
|
||||
id: number
|
||||
}>()
|
||||
|
||||
const back = () => {
|
||||
uni.navigateBack({ delta: 1 })
|
||||
}
|
||||
|
||||
const orderId = ref(query.id)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<scroll-view enable-back-to-top class="viewport" scroll-y>
|
||||
<!-- 订单状态 -->
|
||||
<view class="overview">
|
||||
<view class="status icon-checked">收货成功</view>
|
||||
<view class="buttons">
|
||||
<view class="button navigator" @tap="back"> 返回 </view>
|
||||
<navigator
|
||||
hover-class="none"
|
||||
class="button navigator"
|
||||
:url="`${pageUrl['order-evaluate']}?order_id=${orderId}`"
|
||||
open-type="redirect"
|
||||
>
|
||||
去评价
|
||||
</navigator>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.viewport {
|
||||
background-color: #f7f7f8;
|
||||
}
|
||||
|
||||
.overview {
|
||||
line-height: 1;
|
||||
padding: 50rpx 0;
|
||||
color: #fff;
|
||||
background-color: #ff7c22;
|
||||
|
||||
.status {
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status::before {
|
||||
display: block;
|
||||
font-size: 110rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 60rpx;
|
||||
}
|
||||
|
||||
.button {
|
||||
text-align: center;
|
||||
margin: 0 10rpx;
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
|
||||
&:first-child {
|
||||
width: 200rpx;
|
||||
border-radius: 64rpx;
|
||||
border: 1rpx solid #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,77 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { getRechargeOrderListApi } from '@/api/recharge'
|
||||
import { onShowRefreshData } from '@/utils/common'
|
||||
import type { rechargeOrderListItem } from '@/types/member'
|
||||
import { ref } from 'vue'
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const paging = ref<ZPagingInstance>()
|
||||
const dataList = ref<rechargeOrderListItem[]>([])
|
||||
const queryList = (page: number, page_size: number) => {
|
||||
getRechargeOrderListApi({
|
||||
page,
|
||||
page_size,
|
||||
}).then((res) => {
|
||||
paging.value?.complete(res.result.data)
|
||||
})
|
||||
}
|
||||
|
||||
const getRechargeDesc = (item: rechargeOrderListItem) => {
|
||||
let str = ''
|
||||
if (parseFloat(item.gift_money) > 0) {
|
||||
str += `充值${item.money}元,赠送${item.gift_money}元`
|
||||
} else {
|
||||
str += `充值${item.money}元`
|
||||
}
|
||||
if (item.pay_status == 1) {
|
||||
str += '(充值中)'
|
||||
} else if (item.pay_status == 2) {
|
||||
str += '(已到账)'
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
onShowRefreshData(() => {
|
||||
paging.value?.refresh()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<shop-paginate-list ref="paging" v-model="dataList" @query="queryList">
|
||||
<uni-list class="list">
|
||||
<uni-list-item
|
||||
v-for="(item, index) in dataList"
|
||||
:key="index"
|
||||
class="item"
|
||||
:title="item.order_no"
|
||||
:note="getRechargeDesc(item)"
|
||||
>
|
||||
<template #footer>
|
||||
<view class="right">
|
||||
<text class="time">{{ item.create_time }}</text>
|
||||
<text class="money"> +{{ item.actual_money }}元 </text>
|
||||
</view>
|
||||
</template>
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
</shop-paginate-list>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.item {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.money {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
@ -1,545 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { fullUrl, previewImg } from '@/utils/common'
|
||||
import { getDeliveryCompanyApi } from '@/api/delivery'
|
||||
import { getOrderRefundDetailApi, postOrderRefundReturnApi } from '@/api/order'
|
||||
import type { deliveryCompanyItem } from '@/types/delivery'
|
||||
import type { orderRefundDetailResult } from '@/types/order'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { computed } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import { pageUrl } from '@/utils/constants'
|
||||
|
||||
const props = defineProps<{
|
||||
data: string
|
||||
id: number
|
||||
}>()
|
||||
|
||||
const orderData = ref<orderRefundDetailResult>()
|
||||
|
||||
const getOrderData = async () => {
|
||||
const res = await getOrderRefundDetailApi({ id: props.id })
|
||||
orderData.value = res.result
|
||||
}
|
||||
|
||||
// 审核状态
|
||||
enum auditStatusEnum {
|
||||
wait_audit = 1,
|
||||
agree = 2,
|
||||
refuse = 3,
|
||||
cancel = 4,
|
||||
}
|
||||
|
||||
// 显示协商历史
|
||||
const showHistory = computed(() => {
|
||||
return orderData.value?.audit_status == auditStatusEnum.refuse
|
||||
})
|
||||
// 显示退货地址
|
||||
const showDelivery = computed(() => {
|
||||
return orderData.value?.audit_status == auditStatusEnum.agree && orderData.value.refund_address
|
||||
})
|
||||
// 复制地址
|
||||
const copyAddress = () => {
|
||||
const fullAddress = `收货人:${orderData.value?.refund_address.name}联系电话:${orderData.value?.refund_address.phone}详细地址:${orderData.value?.refund_address.full_address}`
|
||||
|
||||
uni.setClipboardData({
|
||||
data: fullAddress,
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: '复制成功',
|
||||
icon: 'none',
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const deliveryCompanyOptions = ref<deliveryCompanyItem[]>([])
|
||||
const companyIndex = ref(0)
|
||||
|
||||
const getDeliveryCompanyData = async () => {
|
||||
// 有物流信息时不请求
|
||||
if (deliverForm.value.delivery_company && deliverForm.value.delivery_no) return
|
||||
|
||||
const res = await getDeliveryCompanyApi()
|
||||
res.result.unshift({ id: 0, name: '请选择物流公司', code: '' })
|
||||
deliveryCompanyOptions.value = res.result
|
||||
}
|
||||
|
||||
const deliverForm = ref({
|
||||
delivery_company_id: 0,
|
||||
delivery_company: '',
|
||||
delivery_no: '',
|
||||
})
|
||||
const deliverFormRules = ref({
|
||||
delivery_no: {
|
||||
rules: [{ required: true, errorMessage: '请输入物流单号' }],
|
||||
},
|
||||
})
|
||||
|
||||
const onChnageCompanyIndex = (event: UniHelper.SelectorPickerOnChangeEvent) => {
|
||||
companyIndex.value = event.detail.value
|
||||
const deliveryCompany = deliveryCompanyOptions.value.find((item) => item.id == companyIndex.value)
|
||||
|
||||
deliverForm.value.delivery_company_id = deliveryCompany!.id
|
||||
deliverForm.value.delivery_company = deliveryCompany!.name
|
||||
}
|
||||
|
||||
const formRef = ref()
|
||||
const onSubmit = async () => {
|
||||
if (!deliverForm.value.delivery_company_id) {
|
||||
uni.showToast({
|
||||
title: '请选择物流公司',
|
||||
icon: 'none',
|
||||
})
|
||||
return
|
||||
}
|
||||
await formRef.value.validate()
|
||||
const params = {
|
||||
id: props.id,
|
||||
...deliverForm.value,
|
||||
}
|
||||
const res = await postOrderRefundReturnApi(params)
|
||||
uni.showToast({
|
||||
title: res.msg,
|
||||
icon: 'none',
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
const refundTypeText = computed(() => {
|
||||
switch (orderData.value?.refund_type) {
|
||||
case 1:
|
||||
return '退货退款'
|
||||
case 2:
|
||||
return '换货'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
onLoad(async () => {
|
||||
await getOrderData()
|
||||
await getDeliveryCompanyData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="detail" v-if="orderData">
|
||||
<view class="status">
|
||||
<image class="image" src="/static/images/bg.png" />
|
||||
<text>{{ orderData.status_text }}</text>
|
||||
</view>
|
||||
<view class="order">
|
||||
<view class="card">
|
||||
<text class="title">售后商品</text>
|
||||
<navigator
|
||||
class="goods"
|
||||
:url="`${pageUrl['goods-detail']}?id=${orderData.detail.goods_id}`"
|
||||
hover-class="none"
|
||||
>
|
||||
<image class="cover" :src="fullUrl(orderData.detail.image)"></image>
|
||||
<view class="meta">
|
||||
<view class="name ellipsis">{{ orderData.detail.goods_name }}</view>
|
||||
<view class="meta-spec">{{ orderData.detail.spec }}</view>
|
||||
<view class="meta-quantity">x{{ orderData!.refund_num }}</view>
|
||||
</view>
|
||||
</navigator>
|
||||
<view class="detail">
|
||||
<view class="detail-item" v-if="parseFloat(orderData.apply_money) > 0">
|
||||
<text>申请金额</text>
|
||||
<text class="amount">¥{{ orderData.apply_money }}</text>
|
||||
</view>
|
||||
<view class="detail-item" v-if="parseFloat(orderData.refund_money) > 0">
|
||||
<text>退款金额</text>
|
||||
<text class="amount">¥{{ orderData.refund_money }}</text>
|
||||
</view>
|
||||
<view class="detail-item" v-if="parseFloat(orderData.refund_freight) > 0">
|
||||
<text>退款运费</text>
|
||||
<text>¥{{ orderData.refund_freight }}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text>申请时间</text>
|
||||
<text>{{ orderData.create_time }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="block" v-if="showHistory">
|
||||
<view class="history">
|
||||
<view class="header">
|
||||
<text class="title">协商历史</text>
|
||||
</view>
|
||||
<view class="history-item">
|
||||
<text>售后类型:{{ refundTypeText }}</text>
|
||||
</view>
|
||||
<view class="history-item">
|
||||
<text>申请原因:{{ orderData.remark }}</text>
|
||||
</view>
|
||||
<view class="history-item" v-if="orderData.remark_images">
|
||||
<text>申请凭证:</text>
|
||||
<view class="images-container">
|
||||
<image
|
||||
v-for="(item, index) in orderData.remark_images"
|
||||
:key="index"
|
||||
:src="item"
|
||||
mode="aspectFit"
|
||||
@tap="previewImg(item, orderData!.remark_images)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="history-item" v-if="orderData.refuse_reason">
|
||||
<text>商家回复:{{ orderData.refuse_reason }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="block" v-if="showDelivery">
|
||||
<view class="address">
|
||||
<view class="header">
|
||||
<text class="title">退货地址</text>
|
||||
<button class="copy-button" @tap="copyAddress">复制</button>
|
||||
</view>
|
||||
<view>
|
||||
<text>收货人:{{ orderData.refund_address.name }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text>联系电话:{{ orderData.refund_address.phone }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text>详细地址:{{ orderData.refund_address.full_address }}</text>
|
||||
</view>
|
||||
<view class="remark">
|
||||
<text>未与商家协商一致情况下,请勿使用平邮或到付邮寄方式</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="block" v-if="showDelivery">
|
||||
<view class="delivery">
|
||||
<view class="header">
|
||||
<text class="title">退货物流</text>
|
||||
</view>
|
||||
<uni-list v-if="orderData.user_ship_delivery">
|
||||
<uni-list-item
|
||||
title="物流公司"
|
||||
:rightText="orderData.user_ship_delivery.delivery_company"
|
||||
>
|
||||
</uni-list-item>
|
||||
<uni-list-item title="物流单号" :rightText="orderData.user_ship_delivery.delivery_no">
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
<view v-else>
|
||||
<uni-forms
|
||||
ref="formRef"
|
||||
label-width="100"
|
||||
validate-trigger="blur"
|
||||
:rules="deliverFormRules"
|
||||
:model="deliverForm"
|
||||
>
|
||||
<uni-forms-item required label="物流公司" name="delivery_company">
|
||||
<view style="display: flex; align-items: center; height: 35px">
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="deliveryCompanyOptions"
|
||||
range-key="name"
|
||||
:value="companyIndex"
|
||||
@change="onChnageCompanyIndex"
|
||||
>
|
||||
<text>{{ deliveryCompanyOptions[companyIndex]?.name }}</text>
|
||||
</picker>
|
||||
</view>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item required label="物流单号" name="delivery_no">
|
||||
<uni-easyinput
|
||||
v-model="deliverForm.delivery_no"
|
||||
placeholder="请输入物流单号"
|
||||
:disabled="Boolean(orderData.user_ship_status)"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
<button class="sub-button" @tap="onSubmit" v-if="!Boolean(orderData.user_ship_status)">
|
||||
录入物流信息
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="block" v-if="orderData.exchange_delivery">
|
||||
<view class="delivery">
|
||||
<view class="header">
|
||||
<text class="title">换货物流</text>
|
||||
</view>
|
||||
<uni-list>
|
||||
<uni-list-item title="物流公司" :rightText="orderData.exchange_delivery.delivery_company">
|
||||
</uni-list-item>
|
||||
<uni-list-item title="物流单号" :rightText="orderData.exchange_delivery.delivery_no">
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view :style="{ paddingBottom: '100rpx' }"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
height: 150rpx;
|
||||
|
||||
/* 图片样式 */
|
||||
.image {
|
||||
width: 100%;
|
||||
height: 150rpx;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* 文本样式 */
|
||||
text {
|
||||
z-index: 1;
|
||||
font-size: 36rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.order {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
.card {
|
||||
min-height: 100rpx;
|
||||
padding: 20rpx;
|
||||
margin: 20rpx 20rpx 0;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-bottom: 15rpx;
|
||||
|
||||
.date {
|
||||
color: #666;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #ff9240;
|
||||
}
|
||||
|
||||
.icon-delete {
|
||||
line-height: 1;
|
||||
margin-left: 10rpx;
|
||||
padding-left: 10rpx;
|
||||
border-left: 1rpx solid #e3e3e3;
|
||||
}
|
||||
}
|
||||
|
||||
.goods {
|
||||
display: flex;
|
||||
padding-top: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
|
||||
.cover {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
margin-right: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quantity {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
line-height: 1;
|
||||
padding: 6rpx 4rpx 6rpx 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
border-radius: 10rpx 0 0 0;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.meta {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
|
||||
&-spec {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
&-quantity {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
font-size: 24rpx;
|
||||
color: #9e9c9c;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
.detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 5rpx;
|
||||
}
|
||||
|
||||
.amount {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-top: 20rpx;
|
||||
|
||||
.pay-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 130rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 20rpx;
|
||||
border-radius: 60rpx;
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
color: #ff5f3c;
|
||||
border-color: #ff5f3c;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #fff;
|
||||
background-color: #ff5f3c;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
|
||||
.block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20rpx;
|
||||
margin: 20rpx 20rpx 0;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.address view {
|
||||
margin-bottom: 20rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.history view {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.images-container {
|
||||
display: flex;
|
||||
|
||||
image {
|
||||
width: 150rpx;
|
||||
height: 120rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 35rpx;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
display: flex;
|
||||
height: 50rpx;
|
||||
font-size: 24rpx;
|
||||
margin-right: initial;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.remark {
|
||||
padding: 10rpx 0 10rpx 0;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.sub-button {
|
||||
background-color: #ff5f3c;
|
||||
color: white;
|
||||
border-radius: 20rpx;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
margin: 20rpx;
|
||||
}
|
||||
</style>
|
@ -1,293 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { onLoad, onPageScroll, onReachBottom, onShow } from '@dcloudio/uni-app'
|
||||
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
|
||||
import { nextTick, ref } from 'vue'
|
||||
import type { TabItem } from '@/types/global'
|
||||
import { getOrderRefundListApi } from '@/api/order'
|
||||
import { fullUrl } from '@/utils/common'
|
||||
import type { orderRefundListResult } from '@/types/order'
|
||||
import { pageUrl } from '@/utils/constants'
|
||||
|
||||
const paging = ref()
|
||||
useZPaging(paging)
|
||||
|
||||
const props = defineProps<{
|
||||
status: string
|
||||
}>()
|
||||
|
||||
const tabList = ref<TabItem[]>([
|
||||
{ name: '全部', value: '' },
|
||||
{ name: '待处理', value: 1 },
|
||||
])
|
||||
|
||||
const dataList = ref<orderRefundListResult[]>([])
|
||||
|
||||
const queryList = (page: number, page_size: number) => {
|
||||
const params = {
|
||||
page,
|
||||
page_size,
|
||||
status: tabList.value[tabIndex.value].value,
|
||||
}
|
||||
getOrderRefundListApi(params).then((res) => {
|
||||
paging.value.complete(res.result.data)
|
||||
})
|
||||
}
|
||||
|
||||
const tabIndex = ref(Number(props.status) || 0)
|
||||
const tabsChange = (index: number) => {
|
||||
tabIndex.value = index
|
||||
paging.value.refresh()
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
nextTick(() => {
|
||||
paging.value.refresh()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="list">
|
||||
<z-paging
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
use-page-scroll
|
||||
@query="queryList"
|
||||
:safe-area-inset-bottom="true"
|
||||
:auto-show-back-to-top="true"
|
||||
:auto="false"
|
||||
>
|
||||
<template #top>
|
||||
<z-tabs
|
||||
:list="tabList"
|
||||
@change="tabsChange"
|
||||
:current="tabIndex"
|
||||
:badge-style="{ 'background-color': '#c3c3c3' }"
|
||||
/>
|
||||
</template>
|
||||
<view class="order">
|
||||
<view class="card" v-for="(item, index) in dataList" :key="index">
|
||||
<view class="status">
|
||||
<text class="date">{{ item.create_time }}</text>
|
||||
<text class="text">{{ item.status_text }}</text>
|
||||
</view>
|
||||
<navigator
|
||||
class="goods"
|
||||
hover-class="none"
|
||||
:url="`${pageUrl['order-refund-detail']}?id=${item.id}&data=${JSON.stringify(item)}`"
|
||||
>
|
||||
<image class="cover" :src="fullUrl(item.detail.image)"></image>
|
||||
<view class="meta">
|
||||
<view class="name ellipsis">{{ item.detail.goods_name }}</view>
|
||||
<view class="meta-spec">{{ item.detail.spec }}</view>
|
||||
<view class="meta-quantity">x{{ item.refund_num }}</view>
|
||||
</view>
|
||||
</navigator>
|
||||
</view>
|
||||
<!-- 占位符 -->
|
||||
<view style="padding-bottom: 10rpx"></view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.list {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.order {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
.card {
|
||||
min-height: 100rpx;
|
||||
padding: 20rpx;
|
||||
margin: 20rpx 20rpx 0;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-bottom: 15rpx;
|
||||
|
||||
.date {
|
||||
color: #666;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #ff9240;
|
||||
}
|
||||
|
||||
.icon-delete {
|
||||
line-height: 1;
|
||||
margin-left: 10rpx;
|
||||
padding-left: 10rpx;
|
||||
border-left: 1rpx solid #e3e3e3;
|
||||
}
|
||||
}
|
||||
|
||||
.goods {
|
||||
display: flex;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.cover {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
margin-right: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quantity {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
line-height: 1;
|
||||
padding: 6rpx 4rpx 6rpx 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
border-radius: 10rpx 0 0 0;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.meta {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
|
||||
&-spec {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
&-quantity {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
font-size: 24rpx;
|
||||
color: #9e9c9c;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.type {
|
||||
line-height: 1.8;
|
||||
padding: 0 15rpx;
|
||||
margin-top: 10rpx;
|
||||
font-size: 24rpx;
|
||||
align-self: flex-start;
|
||||
border-radius: 4rpx;
|
||||
color: #888;
|
||||
background-color: #f7f7f8;
|
||||
}
|
||||
|
||||
.more {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.payment {
|
||||
display: flex;
|
||||
// justify-content: flex-end;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
padding: 20rpx 0;
|
||||
text-align: right;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
|
||||
.quantity {
|
||||
font-size: 24rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.amount {
|
||||
color: #444;
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
|
||||
.symbol {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-top: 20rpx;
|
||||
|
||||
.pay-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 130rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 20rpx;
|
||||
border-radius: 60rpx;
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
color: #ff5f3c;
|
||||
border-color: #ff5f3c;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #fff;
|
||||
background-color: #ff5f3c;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,362 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { fullUrl } from '@/utils/common'
|
||||
import { safeBottom } from '@/utils/constants'
|
||||
import { postOrderRefundApi } from '@/api/order'
|
||||
import { computed, reactive } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import { pageUrl } from '@/utils/constants'
|
||||
import { uploadApi } from '@/api/common'
|
||||
|
||||
const props = defineProps<{
|
||||
order_id: number
|
||||
detail_data?: string
|
||||
}>()
|
||||
|
||||
const detailData = JSON.parse(props.detail_data ?? '{}')
|
||||
const refundTypeArr = [
|
||||
{ value: 1, text: '退货退款' },
|
||||
{ value: 2, text: '换货' },
|
||||
]
|
||||
const tmpPathArr = ref<string[]>([])
|
||||
const imagePathArr = ref<string[]>([])
|
||||
const onSelect = (e: UniHelper.UniFilePickerOnSelectEvent) => {
|
||||
tmpPathArr.value = tmpPathArr.value.concat(e.tempFilePaths)
|
||||
}
|
||||
const onDelete = (e: UniHelper.UniFilePickerOnDeleteEvent) => {
|
||||
// 移除图片
|
||||
const index = tmpPathArr.value.findIndex((item) => item === e.tempFilePath)
|
||||
tmpPathArr.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const applyForm = reactive({
|
||||
refund_type: 1,
|
||||
refund_num: 1,
|
||||
apply_money: detailData.pay_price,
|
||||
remark: '',
|
||||
remark_images: [],
|
||||
})
|
||||
|
||||
const maxApplyNum = computed(() => {
|
||||
return parseFloat(detailData.num)
|
||||
})
|
||||
const maxApplyAmount = computed(() => {
|
||||
return parseFloat(detailData.pay_price)
|
||||
})
|
||||
|
||||
// 提交申请
|
||||
const onSubmit = async () => {
|
||||
if (!applyForm.remark) {
|
||||
uni.showToast({
|
||||
title: '请填写申请说明',
|
||||
icon: 'none',
|
||||
})
|
||||
return
|
||||
}
|
||||
const uploadPromises = tmpPathArr.value.map((item) => {
|
||||
return new Promise((resolve) => {
|
||||
uploadApi(item).then((res) => {
|
||||
imagePathArr.value.push(res.result.url)
|
||||
resolve(res)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
try {
|
||||
await Promise.all(uploadPromises)
|
||||
|
||||
// 退款
|
||||
const res = await postOrderRefundApi({
|
||||
id: props.order_id,
|
||||
detail_id: detailData.id,
|
||||
refund_type: applyForm.refund_type,
|
||||
refund_num: applyForm.refund_num,
|
||||
remark: applyForm.remark,
|
||||
apply_money: applyForm.apply_money,
|
||||
remark_images: imagePathArr.value.join(','),
|
||||
})
|
||||
|
||||
if (res.result) {
|
||||
uni.showToast({
|
||||
title: '申请成功,请等待商家处理',
|
||||
icon: 'none',
|
||||
mask: true,
|
||||
})
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="panel goods">
|
||||
<view class="item">
|
||||
<navigator
|
||||
class="navigator"
|
||||
:url="`${pageUrl['goods-detail']}?id=${detailData.goods_id}`"
|
||||
hover-class="none"
|
||||
>
|
||||
<image class="cover" :src="fullUrl(detailData.image)"></image>
|
||||
<view class="meta">
|
||||
<view class="name ellipsis">{{ detailData.goods_name }}</view>
|
||||
<view class="type">{{ detailData.spec_text }}</view>
|
||||
<view class="price">
|
||||
<view class="actual">
|
||||
<text class="symbol">¥</text>
|
||||
<text>{{ detailData.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="quantity">x{{ detailData.num }}</view>
|
||||
</view>
|
||||
</navigator>
|
||||
</view>
|
||||
</view>
|
||||
<view class="panel">
|
||||
<view class="panel-item">
|
||||
<view class="item-title">
|
||||
<text>售后类型:</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<uni-data-checkbox mode="tag" v-model="applyForm.refund_type" :localdata="refundTypeArr" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="panel-item">
|
||||
<view class="item-title">
|
||||
<text>售后数量:</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<uni-number-box v-model="applyForm.refund_num" :min="1" :max="maxApplyNum" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="panel-item" v-if="applyForm.refund_type == 1">
|
||||
<view class="item-title">
|
||||
<text>申请金额:</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<uni-number-box v-model="applyForm.apply_money" :min="0.01" :max="maxApplyAmount" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="panel">
|
||||
<view class="panel-item2">
|
||||
<view class="item-title">申请说明</view>
|
||||
<view class="content">
|
||||
<uni-easyinput
|
||||
type="textarea"
|
||||
v-model="applyForm.remark"
|
||||
placeholder="描述清晰有助于商家快速处理售后问题"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="panel-item2">
|
||||
<view class="item-title">上传凭证(最多6张)</view>
|
||||
<view class="content">
|
||||
<uni-file-picker
|
||||
class="image"
|
||||
v-model="applyForm.remark_images"
|
||||
file-mediatype="image"
|
||||
mode="grid"
|
||||
file-extname="png,jpg"
|
||||
:limit="6"
|
||||
return-type="array"
|
||||
:auto-upload="false"
|
||||
@select="onSelect"
|
||||
@delete="onDelete"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sub-button" :style="safeBottom">
|
||||
<button @tap="onSubmit">提交申请</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f7f7f8;
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin: 20rpx 20rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.panel-item {
|
||||
display: flex; // 使用flex布局
|
||||
align-items: center; // 垂直居中
|
||||
justify-content: space-between; // 水平方向上的空间分布
|
||||
padding: 10rpx 0;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.panel-item2 {
|
||||
.item-title {
|
||||
padding: 10rpx 0 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.image {
|
||||
padding: 10rpx 0 10rpx 0;
|
||||
.uni-file-picker {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods {
|
||||
.item {
|
||||
.navigator {
|
||||
display: flex;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
.cover {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.meta {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.type {
|
||||
line-height: 1.8;
|
||||
padding: 0 15rpx;
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
align-self: flex-start;
|
||||
border-radius: 4rpx;
|
||||
color: #888;
|
||||
background-color: #f7f7f8;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.price {
|
||||
display: flex;
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.symbol {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.original {
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.actual {
|
||||
margin-left: 10rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.evaluate {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
font-size: 24rpx;
|
||||
bottom: 50rpx;
|
||||
|
||||
.button {
|
||||
width: 80rpx;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
margin-left: 20rpx;
|
||||
border-radius: 60rpx;
|
||||
border: 1rpx solid #ccc;
|
||||
color: #444;
|
||||
}
|
||||
}
|
||||
|
||||
.quantity {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
font-size: 24rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-start;
|
||||
padding: 30rpx 0 0;
|
||||
|
||||
.button {
|
||||
width: 200rpx;
|
||||
height: 60rpx;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
line-height: 60rpx;
|
||||
margin-left: 20rpx;
|
||||
border-radius: 60rpx;
|
||||
border: 1rpx solid #ccc;
|
||||
font-size: 26rpx;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #ff5f3c;
|
||||
border-color: #ff5f3c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sub-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
.button-container {
|
||||
background-color: white;
|
||||
padding: 10rpx;
|
||||
border-radius: 30rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
background-color: #ff5f3c;
|
||||
color: white;
|
||||
border-radius: 20rpx;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
margin: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user