This commit is contained in:
zjf committed 2024-11-18 09:50:35 +08:00
commit 07bd43fa9d
27 files changed
+1790 -758

No files matched your search

+2 -2
View File
@@ -1,7 +1,7 @@
# ******************** start 开发环境 start ********************
# VITE_APP_DEV_SERVICE =http://localhost:48080/app-api
VITE_APP_DEV_SERVICE =http://123.156.228.134:48080/app-api
VITE_APP_DEV_SERVICE =http://localhost:48080/app-api
# VITE_APP_DEV_SERVICE =http://123.156.228.134:48080/app-api
# ******************** end 开发环境 end ********************
+2
View File
@@ -16,8 +16,10 @@ onHide(() => {
@import "@/sheep/scss/global.scss";
@import url(@/uni_modules/tm-ui/css/remixicon.min.css);
$theme-green: #06be36;
$theme-blue: #0094ff;
$theme-gray: #999;
.botder_btn {
border: 1px solid #999;
+52
View File
@@ -87,6 +87,39 @@
}
]
},
{
"root": "pages/boxes",
"pages": [
{
"path": "apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "apply-list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/merchant",
"pages": [
@@ -126,6 +159,25 @@
}
]
},
{
"root": "pages/refund",
"pages": [
{
"path": "apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "history",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/wallet",
"pages": [
+3 -1
View File
@@ -26,7 +26,9 @@ onLoad((options: any) => {
}
});
onShow(() => {
if (pageRef.value) {
pageRef.value.reload(false)
}
})
</script>
+119
View File
@@ -0,0 +1,119 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { hooks, router } from "@@/index"
import { boxesApi } from "@@/api"
import zTabs from "@@/components/other/z-tabs/z-tabs.vue"
const data = {
tabs: [
{ id: null, name: '全部', disabled: false },
{ id: "0", name: '预约成功', disabled: false },
{ id: "1", name: '预约失败', disabled: false },
{ id: "2", name: '取消预约', disabled: false },
],
}
const state = reactive<any>({
pa: {
status: data.tabs[0].id,
},
})
onLoad(() => {
});
onShow(() => {
if (pageRef.value) {
pageRef.value.reload(false)
}
})
const pageRef = ref<any>(null)
const pageHook = hooks.usePage(pageRef, boxesApi.MyBookingPage, null, 'data.list')
const query = (page: number, pagesize: number) => {
pageHook.startPage(page, pagesize, false, state.pa);
}
const doTab = (e: any) => {
const target = data.tabs[e];
state.pa.status = target.id
pageRef.value.reload(true)
}
const doCard = (v: any) => {
router.jump('/pages/boxes/detail', { id: v.id })
}
const doCancelApply = async (v: any) => {
uni.showModal({
title: '提示',
content: '是否确定取消预约',
confirmColor: '#0094ff',
success: async (res) => {
if (!res.confirm) return
try {
uni.showLoading({ title: '取消预约中...', mask: true });
const _pa = { id: v.id }
const { data, code }: any = await boxesApi.cancel(_pa)
if (code != 0) throw new Error()
pageRef.value.reload(true)
uni.hideLoading()
uni.showToast({ title: '取消预约成功', icon: "none" });
} catch (error) {
console.error(error);
uni.hideLoading()
}
}
})
}
</script>
<template>
<crud-layout title="包厢预约" bg="/static/page/blue-bg.png">
<z-paging ref="pageRef" height="100%" :fixed="false" v-model="pageHook.list" :default-page-size="5"
safe-area-inset-bottom :loading-more-enabled="true" :show-scrollbar="false" @query="query"
:auto-clean-list-when-reload="false" :auto-scroll-to-top-when-reload="false" @scroll="pageHook.scroll">
<template #top>
<view class="bg-white rounded-16rpx px-20rpx m-x-10rpx m-t-15rpx">
<z-tabs :list="data.tabs" @change="doTab" />
</view>
</template>
<view class="h-20rpx" />
<view class="flex-col card p-20rpx m-b-20rpx" v-for="(v, i) in pageHook.list" :key="i" @tap="doCard(v)">
<view>
<view class="flex-row items-center justify-between">
<view class="main-text-28">{{ v.boxesName }}</view>
<view>
<tm-tag v-if="v.status == 0" color="success" size="xs">预约成功</tm-tag>
<tm-tag v-if="v.status == 1" color="error" size="xs">预约失败</tm-tag>
<tm-tag v-if="v.status == 2" color="info" size="xs">取消预约</tm-tag>
</view>
</view>
<view class="m-t-10rpx second-text-26">
<text>预约时间</text>
<text class="m-l-5rpx">
{{ v.bookingDate }} {{ hooks.useTime(v.startTime, "{h}:{i}") }} ~ {{
hooks.useTime(v.endTime, "{h}:{i}")
}}
</text>
</view>
<view class="m-t-10rpx second-text-26">
<text>提交时间</text>
<text class="m-l-5rpx">
{{ hooks.useTime(v.createTime, "") }}
</text>
</view>
</view>
<view class="flex-row items-center justify-end p-t-10rpx">
<view class="botder_btn" @tap.stop="doCancelApply(v)">取消预约</view>
</view>
</view>
</z-paging>
</crud-layout>
</template>
<style lang="scss" scoped></style>
+215
View File
@@ -0,0 +1,215 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { isEmpty, max, min } from "radash"
import dayjs from "dayjs"
import { hooks, router } from "@@/index"
import { boxesApi } from "@@/api"
const data = {
}
const state = reactive<any>({
detailType: "add",
detailId: null,
detail: null,
rechargeList: [],
selectRechargeList: [],
selectRechargeIdList: [],
pickerDate: {
start: "",
end: "",
}
})
const applyForm = reactive<any>({
displayTimeRange: "",
displayTimeRangeStr: "",
bxDate: null,
})
const applyFormRules = reactive<any>({
bxDate: [
{ required: true, message: '预约时间不能为空' }
],
})
onLoad((options: any) => {
const { type, id } = options
if (!isEmpty(type)) state.detailType = type
if (!isEmpty(id)) state.detailId = id
if (type === 'view') {
getDetail()
}
const today = dayjs()
state.pickerDate.start = dayjs().format('YYYY-MM-DD')
state.pickerDate.end = today.add(2, 'month').format('YYYY-MM-DD')
applyForm.bxDate = state.pickerDate.start
getDateRange()
});
onShow(() => {
})
const getDetail = async () => {
if (isEmpty(state.detailId)) return
const _pa = { id: state.detailId }
const { data } = await boxesApi.detail(_pa)
if (!isEmpty(data)) {
state.detail = data
}
}
const getDateRange = async () => {
const _pa = { id: state.detailId, currentDate: applyForm.bxDate }
const { data } = await boxesApi.get(_pa)
data.avaliabilityRespList.forEach(v => {
v.dateTime = `${v.bxDate} ${v.hour}`
v.timestamp = dayjs(v.dateTime).valueOf()
});
state.rechargeList = data.avaliabilityRespList
}
const bxDateConfirm = async (e: any) => {
getDateRange()
}
const doTimeItem = (v: any) => {
if (v.status == 1) {
uni.showToast({ title: '该时间段已被预约', icon: "none" })
return
}
const flag = state.selectRechargeList.some(i => i.id == v.id)
if (flag) {
state.selectRechargeList = state.selectRechargeList.filter(i => i.id != v.id)
if (state.selectRechargeList.length >= 2) {
const { timestamp: targetMin } = min(state.selectRechargeList, (i: any) => i.timestamp)
state.rechargeList.forEach(i => {
if (i.timestamp <= v.timestamp) {
state.selectRechargeList = state.selectRechargeList.filter(j => j.id != i.id)
}
})
}
} else {
state.selectRechargeList.push({ ...v })
if (state.selectRechargeList.length >= 2) {
const { timestamp: targetMax } = max(state.selectRechargeList, (i: any) => i.timestamp)
const { timestamp: targetMin } = min(state.selectRechargeList, (i: any) => i.timestamp)
state.rechargeList.forEach(i => {
if (i.timestamp >= targetMin && i.timestamp <= targetMax) {
state.selectRechargeList.push({ ...i })
}
})
}
}
state.selectRechargeIdList = state.selectRechargeList.map(v => v.id)
}
const applyFormRef = ref<any>(null)
const doSubmit = async () => {
applyFormRef.value.submit()
const { isPass } = applyFormRef.value.validate()
if (!isPass) return
try {
const { hour: hourMax } = max(state.selectRechargeList, (i: any) => i.timestamp)
const { hour: hourMin } = min(state.selectRechargeList, (i: any) => i.timestamp)
const _pa = {
boxesId: state.detailId,
startTime: hourMin,
endTime: hourMax,
bxDate: applyForm.bxDate,
}
const { data, code }: any = await boxesApi.apply(_pa)
if (code != 0) throw new Error()
uni.showLoading({ title: '预定中...', mask: false })
uni.hideLoading()
uni.showToast({ title: '申请成功', icon: "none" })
router.jump("/pages/boxes/apply-list")
} catch (error) {
console.error(error);
uni.hideLoading()
}
}
</script>
<template>
<crud-layout title="预定" mainIsScroll bg="/static/page/blue-bg.png">
<view class="h-30rpx" />
<view class="card p-x-30rpx p-y-40rpx">
<tm-form ref="applyFormRef" v-model="applyForm" :rules="applyFormRules" direction="vertical">
<tm-form-item name="bxDate" label="预约时间" :showBottom="false">
<tm-picker-date v-model="applyForm.bxDate" :start="state.pickerDate.start"
:end="state.pickerDate.end" v-model:model-str="applyForm.bxDate" format="YYYY-MM-DD"
@confirm="bxDateConfirm">
<view :style="{ pointerEvents: state.detailType == 'view' ? 'auto' : 'none' }">
<tm-input v-model="applyForm.bxDate" disabled placeholder="请选择使用时间..">
<template v-slot:rightIcon>
<text v-if="state.detailType != 'view'"
class="p-0.5rem color-#b1b1b1 iconfont icon-xiangxiajiantouxiao"></text>
</template>
</tm-input>
</view>
</tm-picker-date>
</tm-form-item>
</tm-form>
</view>
<view class="prefix_title m-t-35rpx">时间段</view>
<view class="recharge p-30rpx bg-white rounded-20rpx m-t-20rpx shadow-main">
<view
:class="[state.selectRechargeIdList.includes(v.id) ? 'recharge_item_active' : '', v.status == 1 ? 'recharge_item_disabled' : '']"
class="recharge_item" v-for="(v, i) in state.rechargeList" :key="i" @tap="doTimeItem(v)">
<text class="sub-text-24 text-center">{{ v.bxDate }}</text>
<text class="sub-text-24 text-center">{{ v.hour }}</text>
</view>
</view>
<template #main-bottom>
<view v-if="state.detailType != 'view'"
class="bg-#0094ff p-y-20rpx text-center rounded-40rpx m-x-30rpx m-y-30rpx sub-text-30 color-white!"
@tap="doSubmit">
提交
</view>
</template>
</crud-layout>
</template>
<style lang="scss" scoped>
:deep() {
.tmFormItemLabel {
padding-bottom: 10rpx !important;
}
}
.recharge_item_active {
background: #e9f5fe;
border: 1px solid #e9f5fe !important;
&>text {
color: #0094ff !important;
}
}
.recharge_item_disabled {
background: #999;
border: 1px solid #999 !important;
&>text {
color: white !important;
}
}
.recharge {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 15rpx;
border-bottom: 1px solid #f9f9f9;
&_item {
display: flex;
flex-direction: column;
border: 1px solid #cccccc;
border-radius: 16rpx;
padding: 20rpx 0;
}
}
</style>
+83
View File
@@ -0,0 +1,83 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { isEmpty } from "radash"
import { hooks, router } from "@@/index"
import { boxesApi } from "@@/api"
import zSwiper from "@zebra-ui/swiper/components/z-swiper/z-swiper.vue"
import zSwiperItem from "@zebra-ui/swiper/components/z-swiper-item/z-swiper-item.vue"
const data = {
}
const state = reactive<any>({
detailId: null,
detail: {},
})
onLoad((options: any) => {
if (!isEmpty(options.id)) {
state.detailId = options.id
}
});
onShow(() => {
getDetail()
})
const getDetail = async () => {
const _pa = { id: state.detailId }
const { data } = await boxesApi.detail(_pa)
state.detail = data
state.detail.imagesArray = data.images?.split(",")
}
const doSubmit = () => {
router.jump('/pages/boxes/apply', { id: state.detail.id })
}
</script>
<template>
<crud-layout title="" mainIsScroll tabBarType="just-flot-back" bg="/static/page/blue-bg.png">
<z-swiper>
<z-swiper-item v-for="(v, i) in state.detail.imagesArray" :key="i">
<image class="w-100vw" :src="v" mode="widthFix" />
</z-swiper-item>
</z-swiper>
<view class="p-x-15rpx">
<view class="flex-col justify-between card p-y-20rpx p-x-30rpx">
<view class="flex-row items-center justify-between">
<view class="flex-row items-center">
<view class="second-text-46 color-#f63a36!">¥{{ state.detail.price }}</view>
</view>
<view class="second-text-26">可容纳{{ state.detail.capacity }}</view>
</view>
<view class="flex-col m-t-15rpx">
<view class="sub-text-32">{{ state.detail.name }}</view>
</view>
</view>
<view class="h-15rpx" />
<view class="flex-col justify-between card p-y-20rpx p-x-30rpx">
<view class="prefix_title">详情</view>
<view class="m-t-15rpx" v-html="state.detail.description"></view>
</view>
</view>
<template #main-bottom>
<view class="bg-#0094ff p-y-20rpx text-center rounded-40rpx m-x-60rpx m-y-30rpx sub-text-30 color-white!"
@tap="doSubmit">
预定
</view>
</template>
</crud-layout>
</template>
<style lang="scss" scoped>
:deep() {
.layout_main>scroll-view {
padding: 0 !important;
}
}
</style>
+105
View File
@@ -0,0 +1,105 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { isEmpty } from "radash"
import { hooks, router } from "@@/index"
import { boxesApi } from "@@/api"
import zTabs from "@@/components/other/z-tabs/z-tabs.vue"
import CustomWaterfallsFlow from "@@/components/other/custom-waterfalls-flow/custom-waterfalls-flow.vue"
const data = {
tabs: [
{ id: null, name: '全部', disabled: false },
{ id: 0, name: '空闲', disabled: false },
{ id: 1, name: '部分空闲', disabled: false },
{ id: 2, name: '已满', disabled: false },
],
}
const state = reactive<any>({
detailId: null,
pa: {
status: data.tabs[0].id,
},
})
const pageRef = ref<any>(null)
const pageHook = hooks.usePage(pageRef, boxesApi.page, null, 'data.list')
const query = (page: number, pagesize: number) => {
pageHook.startPage(page, pagesize, false, state.pa);
}
onLoad((options: any) => {
if (!isEmpty(options.id)) {
state.detailId = options.id
state.pa.merchantId = options.id
}
});
onShow(() => {
if (pageRef.value) {
pageRef.value.reload(false)
}
})
const doTab = (e: any) => {
const target = data.tabs[e];
state.pa.status = target.id
pageRef.value.reload(true)
}
const doCard = (v: any) => {
router.jump('/pages/boxes/detail', { id: v.id })
}
</script>
<template>
<crud-layout title="包厢预定" bg="/static/page/blue-bg.png">
<z-paging ref="pageRef" height="100%" :fixed="false" v-model="pageHook.list" :default-page-size="5"
safe-area-inset-bottom :loading-more-enabled="true" :show-scrollbar="false" @query="query"
:auto-clean-list-when-reload="false" :auto-scroll-to-top-when-reload="false" @scroll="pageHook.scroll">
<template #top>
<view class="bg-white rounded-16rpx px-20rpx m-x-10rpx m-t-15rpx">
<z-tabs :list="data.tabs" @change="doTab" />
</view>
</template>
<view class="h-20rpx" />
<custom-waterfalls-flow v-model="pageHook.list" imageKey="images" :columnSpace="1" @wapperClick="doCard"
@imageClick="doCard">
<template v-slot:default="v">
<view class="p-x-15rpx p-t-10rpx p-b-20rpx">
<view class="sub-text-28 show-line">{{ v.name }}</view>
<view class="m-t-10rpx flex-row items-center">
<view class="second-text-32 color-#f63a36!">
¥{{ v.price }}
</view>
</view>
<view class="flex-row items-center m-t-10rpx">
<view class="second-text-26">可容纳{{ v.capacity }}人</view>
<view class="w-1px h-20rpx m-x-5rpx bg-#999" />
<view class="flex-row-center">
<tm-tag v-if="v.status == 0" size="xs" color="success">
{{ hooks.useEnum("boxesBxStatusEnum", v.status.toString()) }}
</tm-tag>
<tm-tag v-if="v.status == 1" size="xs">
{{ hooks.useEnum("boxesBxStatusEnum", v.status) }}
</tm-tag>
<tm-tag v-if="v.status == 2" size="xs" color="error">
{{ hooks.useEnum("boxesBxStatusEnum", v.status) }}
</tm-tag>
</view>
</view>
<view class="flex-row items-center m-t-10rpx">
<view class="second-text-26">{{ v.address }}</view>
</view>
</view>
</template>
</custom-waterfalls-flow>
</z-paging>
</crud-layout>
</template>
<style lang="scss" scoped></style>
@@ -65,7 +65,8 @@ const remove = () => {
<template>
<view class="flex-row items-start card p-x-15rpx p-y-25rpx m-b-20rpx"
<view :class="[props.mode == 'car' ? 'car_border' : '']"
class="flex-row items-start card p-x-15rpx p-y-25rpx m-b-20rpx"
@tap="props.productId && router.jump('/pages/merchant/product-detail', { id: props.productId })">
<image v-if="props.mode == 'default'" class=" size-180rpx rounded-12rpx" :src="props.pic" mode="aspectFill" />
<image v-if="props.mode == 'car'" class=" size-120rpx rounded-12rpx" :src="props.pic" mode="aspectFill" />
@@ -101,4 +102,8 @@ const remove = () => {
</view>
</template>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.car_border {
border: 1px solid #f2f2f2;
}
</style>
+15 -3
View File
@@ -50,7 +50,6 @@ const getDetail = async () => {
const getShopCar = async () => {
const _pa = { merchantId: state.detailId }
const { data } = await foodApi.getShopCar(_pa)
console.log(data, "data");
state.carList = data ?? []
state.totPrice = state.carList
@@ -149,7 +148,7 @@ const doVisibleCar = async () => {
state.showCarDialog = true
}
const doPay = async () => {
const submitPay = async () => {
try {
if (isEmpty(state.carList)) {
uni.showToast({ title: '请至少购买一件商品', icon: "none" })
@@ -171,6 +170,18 @@ const doPay = async () => {
}
}
const doPay = async () => {
uni.showModal({
title: '提示',
content: '是否确定购买',
confirmColor: '#0094ff',
success: async (res) => {
if (!res.confirm) return
submitPay()
}
})
}
const getHeight = () => {
const rpx = 480
return `calc(100vh - ${uni.upx2px(rpx)}px)`
@@ -239,7 +250,8 @@ const doFood = () => {
<text class="m-l-20rpx sub-text-30">联系食堂</text>
</view>
<view class="w-1px h-30rpx bg-#c4c4c4" />
<view class="flex-row-center">
<view class="flex-row-center"
@tap.stop="router.jump('/pages/boxes/list', { id: state.detailId })">
<view class="flex-row-center w-50rpx h-50rpx bg-#167aff rounded-full">
<text class="font-size-28rpx! color-white iconfont icon-yuding" />
</view>
+1 -1
View File
@@ -107,7 +107,7 @@ const doProduct = (v: any) => {
</template>
<view class="h-20rpx" />
<view class="m-x-10rpx">
<custom-waterfalls-flow :value="pageHook.list" imageKey="picUrl" :columnSpace="1"
<custom-waterfalls-flow v-model="pageHook.list" imageKey="picUrl" :columnSpace="1"
@wapperClick="doProduct" @imageClick="doProduct">
<template v-slot:default="v">
<view class="p-x-15rpx p-y-25rpx">
+76 -2
View File
@@ -4,7 +4,7 @@ import { onLoad, onShow } from "@dcloudio/uni-app";
import { isEmpty } from "radash"
import { hooks, constant, router } from "@@/index"
import { orderApi } from "@@/api"
import { orderApi, refundApi } from "@@/api"
const data = {
@@ -12,6 +12,7 @@ const data = {
const state = reactive<any>({
detailId: null,
detail: {},
refundDetail: {},
shopCarList: [],
})
@@ -30,6 +31,9 @@ const getDetail = async () => {
const _pa = { id: state.detailId };
const { data } = await orderApi.detail(_pa)
state.detail = data
if (state.detail.orderStatus == 10) {
getRefundDetail()
}
}
const getShopCarList = async () => {
@@ -39,6 +43,30 @@ const getShopCarList = async () => {
state.shopCarList = data
}
const getRefundDetail = async () => {
if (isEmpty(state.detailId)) return
const _pa = { id: state.detailId };
const { data } = await refundApi.get(_pa)
state.refundDetail = data
}
const doCancelRefund = async () => {
uni.showModal({
title: '提示',
content: '是否确定取消申请退款',
confirmColor: '#0094ff',
success: async (res) => {
if (!res.confirm) return
uni.showLoading({ title: '取消中...', mask: false });
const _pa = { id: state.refundDetail.id }
const data = refundApi.cancel(_pa)
uni.hideLoading()
uni.showToast({ title: '取消成功', icon: "none" });
router.jump("/pages/tabbar/order", {}, 2)
}
})
}
</script>
@@ -89,7 +117,7 @@ const getShopCarList = async () => {
<view class="flex-row items-start justify-between" v-for="(v, i) in state.shopCarList" :key="i"
@tap.stop="router.jump('/pages/merchant/product-detail', { id: v.productId })">
<view class="flex-row items-center">
<image class="w-160rpx h-160rpx" :src="v.productPic" mode="aspectFill" />
<image class="w-160rpx h-160rpx rounded-8rpx" :src="v.productPic" mode="aspectFill" />
<view class="flex-col m-l-15rpx second-text-26">
<view class="sub-text-28">{{ v.productName }}</view>
<view class="m-t-5rpx">¥ {{ hooks.useMoney(v.productPrice) }}</view>
@@ -110,6 +138,52 @@ const getShopCarList = async () => {
</view>
</view>
</view>
<!-- start 退款相关 start -->
<slot v-if="state.detail.orderStatus == 10">
<view class="flex-row items-center justify-between card p-x-20rpx p-y-30rpx m-b-20rpx"
@tap="router.jump('/pages/refund/history', { id: state.refundDetail.id })">
<view>
<view class="sub-text-28">
<view>退款申请待商家处理</view>
</view>
<view class="m-t-10rpx second-text-26">
{{ hooks.useTime(state.refundDetail.updateTime, "") }}
</view>
</view>
<view class="m-l-15rpx color-#999 font-size-26rpx! iconfont icon-youjiantou" />
</view>
<view class="flex-row items-center justify-between card p-x-20rpx p-y-30rpx m-b-20rpx">
<view class="sub-text-28">退款金额</view>
<view class="sub-text-28 color-red!">¥{{ hooks.useMoney(state.refundDetail.refundPrice) }}</view>
</view>
<view class="card p-x-20rpx p-y-30rpx space-y-10rpx m-b-20rpx second-text-24">
<view class="flex-row items-center">
<view>服务单号:</view>
<view>{{ state.refundDetail.no }}</view>
</view>
<view class="flex-row items-center">
<view>申请时间:</view>
<view>{{ hooks.useTime(state.refundDetail.createTime, "") }}</view>
</view>
<view class="flex-row items-center">
<view>售后类型:</view>
<view>{{ state.refundDetail.applyReason }}</view>
</view>
<view class="flex-row items-center">
<view>相关描述:</view>
<view>{{ state.refundDetail.applyDescription }}</view>
</view>
</view>
</slot>
<!-- end 退款相关 end -->
<view class="h-30rpx" />
<template #main-bottom>
<view v-if="state.detail.orderStatus == 10 && !state.detail.applyCancelTime"
class="bg-#999 p-y-20rpx text-center rounded-40rpx m-x-30rpx m-y-30rpx sub-text-30 color-white!"
@tap="doCancelRefund">
取消申请
</view>
</template>
</crud-layout>
</template>
+186
View File
@@ -0,0 +1,186 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { isEmpty } from "radash"
import { hooks, router } from "@@/index"
import { orderApi, refundApi, commonApi } from "@@/api"
const data = {
payType: [
{ text: "仅退款", disabled: false, type: "10", value: "10" },
{ text: "退款退货", disabled: true, type: "20", value: "20" },
]
}
const state = reactive<any>({
detailId: null,
detail: {},
shopCarList: [],
setting: {},
selectList: []
})
const applyForm = reactive<any>({
way: data.payType[0].type,
remark: "",
reason: "",
reasonStr: "",
})
onLoad((options: any) => {
if (!isEmpty(options.id)) {
state.detailId = options.id
}
});
onShow(() => {
getDetail()
getSetting()
getShopCarList()
})
const getSetting = async () => {
const _pa = {};
const { data } = await commonApi.setting(_pa)
state.setting = data
state.selectList = data.afterSaleRefundReasons.map(v => {
return {
id: v,
title: v,
}
})
}
const getDetail = async () => {
if (isEmpty(state.detailId)) return
const _pa = { id: state.detailId };
const { data } = await orderApi.detail(_pa)
state.detail = data
}
const getShopCarList = async () => {
if (isEmpty(state.detailId)) return
const _pa = { orderId: state.detailId };
const { data } = await orderApi.shopCarList(_pa)
state.shopCarList = data
}
const radioChange = (e: any) => {
switch (e) {
case "10":
state.selectList = state.setting.afterSaleRefundReasons.map(v => {
return {
id: v,
title: v,
}
})
break
case "20":
state.selectList = state.setting.afterSaleReturnReasons.map(v => {
return {
id: v,
title: v,
}
})
break
}
}
const doSubmit = async () => {
if (isEmpty(applyForm.reasonStr)) {
uni.showToast({ title: '请选择申请原因', icon: "none" });
return
}
const _pa = {
way: applyForm.way,
refundPrice: state.detail.practicalTotalPrice,
orderItemId: state.detailId,
images: [],
applyReason: applyForm.reasonStr,
applyDescription: applyForm.remark,
}
try {
uni.showLoading({ title: '申请中...', mask: false });
const data = await refundApi.create(_pa)
uni.hideLoading()
uni.showToast({ title: '申请成功', icon: "none" });
router.jump("/pages/order/detail", { id: state.detailId })
} catch (error) {
console.error(error);
uni.hideLoading()
}
}
</script>
<template>
<crud-layout title="申请售后" mainIsScroll bg="/static/page/blue-bg.png">
<view class="h-30rpx" />
<view class="flex-col justify-center card p-x-20rpx p-y-30rpx m-b-20rpx">
<view class="m-t-15rpx">
<view class="flex-row items-start justify-between" v-for="(v, i) in state.shopCarList" :key="i"
@tap.stop="router.jump('/pages/merchant/product-detail', { id: v.productId })">
<view class="flex-row items-center">
<image class="w-160rpx h-160rpx rounded-8rpx" :src="v.productPic" mode="aspectFill" />
<view class="flex-col m-l-15rpx second-text-26">
<view class="sub-text-28">{{ v.productName }}</view>
<view class="m-t-5rpx">¥ {{ hooks.useMoney(v.productPrice) }}</view>
<view class="m-t-5rpx">x {{ v.productCount }}</view>
</view>
</view>
<view class="flex-row-center">
<view class="sub-text-32">¥ {{ hooks.useMoney(v.totalPrice) }}</view>
</view>
</view>
<view class="m-y-15rpx h-1px bg-#999 bg-opacity-5" />
<view class="flex-row items-center justify-between">
<view class="sub-text-28">实付款</view>
<view class="flex-row items-end">
<view class="sub-text-28">¥</view>
<view class="m-l-5rpx sub-text-28">{{ hooks.useMoney(state.detail.practicalTotalPrice) }}</view>
</view>
</view>
</view>
</view>
<view class="card p-x-20rpx p-y-30rpx m-b-20rpx">
<view class="sub-text-28">售后类型</view>
<view class="m-t-20rpx">
<tm-radio-group @change="radioChange" direction="column" gap="20" v-model="applyForm.way">
<tm-radio :disabled="v.disabled" v-for="(v, i) in data.payType" :key="i" :value="v.type"
:label="v.text" />
</tm-radio-group>
</view>
</view>
<view class="flex-row items-center justify-between card p-x-20rpx p-y-30rpx m-b-20rpx">
<view class="sub-text-28">退款金额</view>
<view class="sub-text-28 color-red!">¥{{ hooks.useMoney(state.detail.practicalTotalPrice) }}</view>
</view>
<view class="card p-x-20rpx p-y-30rpx m-b-20rpx">
<tm-picker rang-key="title" rang-text="title" v-model="applyForm.reason"
v-model:model-str="applyForm.reasonStr" :list="state.selectList">
<view class="flex-row items-center justify-between">
<view class="sub-text-28">申请原因</view>
<view class="flex-row items-center second-text-24">
<text>{{ applyForm.reasonStr || '请选择申请原因' }} </text>
<text class="m-l-10rpx color-#b1b1b1 iconfont icon-xiangxiajiantouxiao" />
</view>
</view>
</tm-picker>
</view>
<view class="flex-col justify-center card p-x-20rpx p-y-30rpx m-b-20rpx">
<view class="sub-text-28">相关描述</view>
<view class="m-t-20rpx">
<tm-input v-model="applyForm.remark" type="textarea" :showClear="false"
placeholder="客官~请描述您遇到的问题"></tm-input>
</view>
</view>
<view class="h-30rpx" />
<template #main-bottom>
<view v-if="state.detailType != 'view'"
class="bg-#0094ff p-y-20rpx text-center rounded-40rpx m-x-30rpx m-y-30rpx sub-text-30 color-white!"
@tap="doSubmit">
提交申请
</view>
</template>
</crud-layout>
</template>
<style lang="scss" scoped></style>
+54
View File
@@ -0,0 +1,54 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { isEmpty } from "radash"
import { hooks } from "@@/index"
import { refundApi } from "@@/api"
const data = {
}
const state = reactive<any>({
detailId: null,
historyList: []
})
onLoad((options: any) => {
if (!isEmpty(options.id)) {
state.detailId = options.id
}
});
onShow(() => {
getHistory()
})
const getHistory = async () => {
if (isEmpty(state.detailId)) return
const _pa = { id: state.detailId };
const { data } = await refundApi.history(_pa)
state.historyList = data
}
</script>
<template>
<crud-layout mainIsScroll title="售后进度" bg="/static/page/blue-bg.png">
<view class="h-30rpx" />
<view class="flex card p-30rpx m-b-20rpx" v-for="(v, i) in state.historyList" :key="i">
<view class="m-t-10rpx size-12rpx rounded-full bg-green" />
<view class="m-l-15rpx">
<view class="sub-text-28">
<view>{{ v.content }}</view>
</view>
<view class="m-t-10rpx second-text-26">
{{ hooks.useTime(v.updateTime, "") }}
</view>
</view>
</view>
<view class="h-30rpx" />
</crud-layout>
</template>
<style lang="scss" scoped></style>
+1
View File
@@ -9,6 +9,7 @@ import { merchantApi, userApi, walletApi } from "@@/api"
const data = {
serviceList: [
{ text: '钱包管理', icon: '/static/icon/service_3.png', router: '/pages/wallet/list' },
{ text: '包厢预定', icon: '/static/icon/service_4.png', router: '/pages/boxes/apply-list' },
// { text: '车牌管理', icon: '/static/icon/service_2.png', router: '/pages/user/diff-price' },
{ text: '外来人员就餐', icon: '/static/icon/service_1.png', router: '/pages/apply/foreign-list' },
{ text: '跨单位就餐', icon: '/static/icon/service_1.png', router: '/pages/apply/stride-list' },
+5 -3
View File
@@ -12,7 +12,7 @@ const data = {
{ id: null, name: '全部', disabled: false },
{ id: 1, name: '未支付', disabled: false },
{ id: 5, name: '待核销', disabled: false },
{ id: 10, name: '已退款', disabled: false },
{ id: 10, name: '售后中', disabled: false },
{ id: 15, name: '已核销', disabled: false },
]
}
@@ -51,6 +51,8 @@ const doAppraise = () => {
}
const doRefund = (v: any) => {
router.jump("/pages/refund/apply", { id: v.id })
return
uni.showModal({
title: '提示',
content: '是否确定退款',
@@ -70,7 +72,7 @@ const doRefund = (v: any) => {
uni.hideLoading()
}
}
});
})
}
</script>
@@ -100,7 +102,7 @@ const doRefund = (v: any) => {
</view>
<tm-tag v-if="v.orderStatus == 1" color="info" size="xs">未支付</tm-tag>
<tm-tag v-if="v.orderStatus == 5" color="success" size="xs">待核销</tm-tag>
<tm-tag v-if="v.orderStatus == 10" color="error" size="xs">已退款</tm-tag>
<tm-tag v-if="v.orderStatus == 10" color="error" size="xs">售后中</tm-tag>
<tm-tag v-if="v.orderStatus == 15" size="xs">已核销</tm-tag>
</view>
<view class="m-t-5rpx">
+58
View File
@@ -0,0 +1,58 @@
import request from "@/sheep/utils/request";
export default {
page: (params: any) =>
request({
url: "boxes/info/api/page",
method: "GET",
params,
custom: {
showLoading: false,
},
}),
detail: (params: any) =>
request({
url: "boxes/info/api/getInfo",
method: "GET",
params,
custom: {
showLoading: false,
},
}),
apply: (data: any) =>
request({
url: "boxes/info/api/create",
method: "POST",
data,
custom: {
showLoading: false,
},
}),
cancel: (data: any) =>
request({
url: "boxes/info/api/cancel",
method: "GET",
data,
custom: {
showLoading: false,
},
}),
get: (data: any) =>
request({
url: "boxes/info/api/get",
method: "GET",
data,
custom: {
showLoading: false,
},
}),
MyBookingPage: (data: any) =>
request({
url: "boxes/info/api/getMyBookingPage",
method: "GET",
data,
custom: {
showLoading: false,
},
}),
};
+11 -1
View File
@@ -1,3 +1,13 @@
import request from "@/sheep/utils/request";
export default {};
export default {
setting: (params: any) =>
request({
url: "pay/setting/get",
method: "GET",
params,
custom: {
showLoading: false,
},
}),
};
+4
View File
@@ -1,5 +1,7 @@
export { default as applyApi } from "./apply";
export { default as boxesApi } from "./boxes";
export { default as commonApi } from "./common";
export { default as foodApi } from "./food";
@@ -12,6 +14,8 @@ export { default as payApi } from "./pay";
export { default as productApi } from "./product";
export { default as refundApi } from "./refund";
export { default as userApi } from "./user";
export { default as walletApi } from "./wallet";
+32
View File
@@ -0,0 +1,32 @@
import request from "@/sheep/utils/request";
export default {
create: (data: any) => {
return request({
url: "/hardware/trade-after-sale/create",
method: "POST",
data,
});
},
get: (params: any) => {
return request({
url: "/hardware/trade-after-sale/get-by-order-id",
method: "GET",
params,
});
},
history: (params: any) => {
return request({
url: "/hardware/trade-after-sale/history",
method: "GET",
params,
});
},
cancel: (params: any) => {
return request({
url: "/hardware/trade-after-sale/cancel",
method: "GET",
params,
});
},
};
@@ -155,7 +155,9 @@ page {
// background: #ecf4f9;
}
&_bottom {}
&_bottom {
box-shadow: 0 8rpx 40rpx rgba(0, 0, 0, 0.1);
}
}
&_foot {}
@@ -43,7 +43,7 @@
<script>
export default {
props: {
value: Array,
modelValue: Array,
column: { // 列的数量
type: [String, Number],
default: 2
@@ -75,7 +75,7 @@ export default {
data() {
return {
data: {
list: this.value ? this.value : [],
list: this.modelValue ? this.modelValue : [],
column: this.column < 2 ? 2 : this.column,
columnSpace: this.columnSpace <= 5 ? this.columnSpace : 5,
imageKey: this.imageKey,
@@ -144,14 +144,14 @@ export default {
// 刷新
refresh() {
if (!this.isLoaded) {
this.refreshDatas = this.value;
this.refreshDatas = this.modelValue;
return false;
};
setTimeout(() => {
this.refreshDatas = [];
this.isRefresh = true;
this.adds = [];
this.data.list = this.value ? this.value : [];
this.data.list = this.modelValue ? this.modelValue : [];
this.data.column = this.column < 2 ? 2 : this.column >= this.maxColumn ? this.maxColumn : this.column;
this.data.columnSpace = this.columnSpace <= 5 ? this.columnSpace : 5;
this.data.imageKey = this.imageKey;
@@ -265,7 +265,7 @@ export default {
}
},
watch: {
value: {
modelValue: {
deep: true,
handler(newValue, oldValue) {
setTimeout(() => {
File diff suppressed because it is too large. Load diff
+7
View File
@@ -22,8 +22,15 @@ const hardwarePayTypeEnum = {
7: "线上订餐二维码支付",
};
const boxesBxStatusEnum = {
"0": "今日空闲",
"1": "今日部分空闲",
"2": "今日已满",
};
export default {
hardwareOrderStatusEnum,
hardwareOrderTypeEnum,
hardwarePayTypeEnum,
boxesBxStatusEnum,
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { ref, computed, onMounted, watch, PropType } from 'vue';
import { ref, computed, onMounted, watch, type PropType } from 'vue';
import { useTmConfig } from "../../libs/config";
import { tmDate, type tmDateTypeTime, createDate } from '../../libs/tmDate';
import pickerItem from './../tm-picker-view/picker-item.vue';
@@ -528,7 +528,7 @@ onMounted(() => {
</view>
<tm-drawer @open="onOpen" :widthCoverCenter="true" :disabledScroll="true" :title="title" @close="onClose"
@confirm="onConfirm" @cancel="onCancel" :showFooter="true" v-model:show="show" :show-close="true" size="850">
<view v-if="yanchiDuration&&show" class="tmPickerDateWrap">
<view v-if="yanchiDuration && show" class="tmPickerDateWrap">
<picker-item @change-deep="change" :selected-index="_modelValueIndex" :list="dateList"
:cell-units="cellUnits.slice(0, dateList.length)"></picker-item>
</view>
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount, watch, PropType, getCurrentInstance, ComponentInstance, onUpdated, nextTick, VueElement, inject } from 'vue';
import { ref, computed, onMounted, onBeforeUnmount, watch, type PropType, getCurrentInstance, type ComponentInstance, onUpdated, nextTick, VueElement, inject } from 'vue';
import { arrayNumberValid, arrayNumberValidByStyleMP, covetUniNumber, arrayNumberValidByStyleBorderColor, linearValid, getUnit, getUid } from "../../libs/tool";
import { getDefaultColor, getDefaultColorObj, getOutlineColorObj, getTextColorObj, getThinColorObj } from "../../libs/colors";
import { useTmConfig } from "../../libs/config";
@@ -54,7 +54,7 @@ const props = defineProps({
* 当前选中项的id值
*/
modelValue: {
type: Array<string|number>,
type: Array<string | number>,
default: (): string[] => [] as string[]
},
/**
@@ -96,7 +96,7 @@ const props = defineProps({
const _cellUnits = computed(() => props.cellUnits)
const _list = computed(() => props.list)
const _nowShowList = ref<Record<string,any>[][]>([])
const _nowShowList = ref<Record<string, any>[][]>([])
const _maxDeep = computed(() => {
if (_list.value.length == 0) return 0;
function getdiepWidth(list: TM_PICKER_X_ITEM[]): number {
@@ -114,28 +114,28 @@ const _maxDeep = computed(() => {
const oninitFun = () => {
nowValue.value = props.modelValue.slice(0)
let currneinexs = getIndexsByids(nowValue.value).indexs;
_modelValueIndex.value = nowValue.value.length==0||currneinexs.length==0?new Array(_maxDeep.value).fill(0):currneinexs;
const {data,strs,ids} = getIdsByindexs(_modelValueIndex.value)
_modelValueIndex.value = nowValue.value.length == 0 || currneinexs.length == 0 ? new Array(_maxDeep.value).fill(0) : currneinexs;
const { data, strs, ids } = getIdsByindexs(_modelValueIndex.value)
_nowShowList.value = data.slice(0)
if(nowValue.value.length==0){
nowValue.value = ids.slice(0)
nextTick(()=>{
emits('update:modelStr', strs.join(","))
emits('update:modelValue', ids.slice(0))
})
if (nowValue.value.length == 0) {
nowValue.value = ids.slice(0)
nextTick(() => {
emits('update:modelStr', strs.join(","))
emits('update:modelValue', ids.slice(0))
})
}
}
const change = (ixs: number[]) => {
const {ids,data,strs} = getIdsByindexs(ixs)
const { ids, data, strs } = getIdsByindexs(ixs)
_modelValueIndex.value = getIndexsByids(ids).indexs;
_nowShowList.value = data.slice(0)
nowValue.value = ids;
/**
* 等同v-model
*/
@@ -148,14 +148,14 @@ const change = (ixs: number[]) => {
// 根据id返回索引
const getIndexsByids = (ids:Array<string|number>):{indexs:Array<number>,data:TM_PICKER_X_ITEM[][],strs:string[]}=> {
const getIndexsByids = (ids: Array<string | number>): { indexs: Array<number>, data: TM_PICKER_X_ITEM[][], strs: string[] } => {
let index = 0;
let val = ids.slice(0)
let indexs = [] as number[]
let data = [] as TM_PICKER_X_ITEM[][]
let strs = [] as string[]
if(_list.value.length==0||ids.length==0) return {indexs:[],data:[],strs:[]}
if (_list.value.length == 0 || ids.length == 0) return { indexs: [], data: [], strs: [] }
function getIndex(nodes: TM_PICKER_X_ITEM[]) {
if (val.length <= index || val.length == 0) return;
@@ -176,47 +176,47 @@ const getIndexsByids = (ids:Array<string|number>):{indexs:Array<number>,data:TM_
}
}
getIndex(_list.value)
return {indexs,data,strs}
return { indexs, data, strs }
}
//根据索引返回ids
const getIdsByindexs = (indexs:number[]):{ids:Array<string|number>,data:TM_PICKER_X_ITEM[][],strs:string[]} => {
const getIdsByindexs = (indexs: number[]): { ids: Array<string | number>, data: TM_PICKER_X_ITEM[][], strs: string[] } => {
let ids = [] as string[]
let data = [] as TM_PICKER_X_ITEM[][]
let index = 0;
let val = indexs.slice(0)
let strs = [] as string[]
if(_list.value.length==0||indexs.length==0) return {ids:[],data:[],strs:[]}
if (_list.value.length == 0 || indexs.length == 0) return { ids: [], data: [], strs: [] }
function getIds(nodes: TM_PICKER_X_ITEM[]) {
if (val.length <= index || val.length == 0) return;
let currentsIndex = val[index]
let id = ''
let str = ''
let children = [];
if(nodes.length-1 >= currentsIndex){
if (nodes.length - 1 >= currentsIndex) {
id = nodes[currentsIndex][props.rangKey]
str = nodes[currentsIndex][props.rangText]
children = nodes[currentsIndex]?.children??[]
}else{
children = nodes[currentsIndex]?.children ?? []
} else {
id = nodes[0][props.rangKey]
str = nodes[0][props.rangText]
children = nodes[0]?.children??[]
children = nodes[0]?.children ?? []
}
ids.push(id)
data.push(nodes)
strs.push(str)
if(children.length>0){
if (children.length > 0) {
index += 1
getIds(children)
}
}
getIds(_list.value)
return {ids,data,strs};
return { ids, data, strs };
}
const getNowCurrent = ()=>{
return getIdsByindexs(_modelValueIndex.value)
const getNowCurrent = () => {
return getIdsByindexs(_modelValueIndex.value)
}
// 获取相关数据。
@@ -233,11 +233,11 @@ onBeforeUnmount(() => {
watch(() => props.modelValue, (newValue: Array<string | number>) => {
if (newValue.join('') == nowValue.value.join('')) return;
nowValue.value = newValue.slice(0);
let defauleindexs = nowValue.value.length==0?new Array(_maxDeep.value).fill(0):getIndexsByids(nowValue.value).indexs;
let defauleindexs = nowValue.value.length == 0 ? new Array(_maxDeep.value).fill(0) : getIndexsByids(nowValue.value).indexs;
_modelValueIndex.value = defauleindexs;
const {data,strs} = getIdsByindexs(_modelValueIndex.value)
const { data, strs } = getIdsByindexs(_modelValueIndex.value)
_nowShowList.value = data.slice(0)
emits('update:modelStr', strs.join(","))
@@ -248,7 +248,7 @@ watch(() => props.list, (newValue) => {
oninitFun()
}, { deep: true })
defineExpose({getIndexsByids,getIdsByindexs,getNowCurrent,onInit:oninitFun})
defineExpose({ getIndexsByids, getIdsByindexs, getNowCurrent, onInit: oninitFun })
</script>
<script lang="ts">
@@ -263,14 +263,11 @@ export default {
</script>
<template>
<view class="tmPickerView">
<picker-item :rangKey="props.rangKey" :rangText="props.rangText"
:font-size="props.fontSize" :cellUnits="_cellUnits" @changeDeep="change"
:selectedIndex="_modelValueIndex" :list="_nowShowList"
<picker-item :rangKey="props.rangKey" :rangText="props.rangText" :font-size="props.fontSize"
:cellUnits="_cellUnits" @changeDeep="change" :selectedIndex="_modelValueIndex" :list="_nowShowList"
class="tmPickerViewItem"></picker-item>
</view>
</template>
<style scoped>
.tmPickerView {
}
.tmPickerView {}
</style>