Compare commits
3 Commits
9e15542d82
...
42eae0b1cd
Author | SHA1 | Date |
---|---|---|
|
42eae0b1cd | |
|
bc46f40285 | |
|
9c066c0ad9 |
2
.env
2
.env
|
@ -7,4 +7,4 @@ VITE_APP_TITLE = 借还柜
|
||||||
VITE_ROUTER_HISTORY = hash
|
VITE_ROUTER_HISTORY = hash
|
||||||
|
|
||||||
## 是否开启 console 调试工具
|
## 是否开启 console 调试工具
|
||||||
VITE_CONSOLE = false
|
VITE_CONSOLE = true
|
||||||
|
|
|
@ -5,3 +5,6 @@ VITE_BASE_URL = http://localhost:8090/api
|
||||||
|
|
||||||
## 开发环境域名和静态资源公共路径(一般 / 或 ./ 都可以)
|
## 开发环境域名和静态资源公共路径(一般 / 或 ./ 都可以)
|
||||||
VITE_PUBLIC_PATH = /
|
VITE_PUBLIC_PATH = /
|
||||||
|
|
||||||
|
# 后端地址
|
||||||
|
VITE_APP_BASE_API = '/dev-api'
|
|
@ -4,3 +4,6 @@
|
||||||
VITE_BASE_URL = '/shop-api/api'
|
VITE_BASE_URL = '/shop-api/api'
|
||||||
## 打包构建静态资源公共路径(例如部署到 https://un-pany.github.io/mobvue/ 域名下就需要填写 /mobvue/)
|
## 打包构建静态资源公共路径(例如部署到 https://un-pany.github.io/mobvue/ 域名下就需要填写 /mobvue/)
|
||||||
VITE_PUBLIC_PATH = /shop/
|
VITE_PUBLIC_PATH = /shop/
|
||||||
|
|
||||||
|
# 后端地址
|
||||||
|
VITE_APP_BASE_API = '/shop-back-end'
|
|
@ -5,3 +5,6 @@ VITE_BASE_URL = '/shop-api/api'
|
||||||
|
|
||||||
## 打包构建静态资源公共路径(例如部署到 https://un-pany.github.io/ 域名下就需要填写 /)
|
## 打包构建静态资源公共路径(例如部署到 https://un-pany.github.io/ 域名下就需要填写 /)
|
||||||
VITE_PUBLIC_PATH = /shop/
|
VITE_PUBLIC_PATH = /shop/
|
||||||
|
|
||||||
|
# 后端地址
|
||||||
|
VITE_APP_BASE_API = '/shop-back-end'
|
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="80" height="80" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="5" y="5" width="90" height="90" rx="10" fill="#E8F5E9" stroke="#81C784"
|
||||||
|
stroke-width="2" />
|
||||||
|
<text x="50" y="60" font-family="Arial, sans-serif" font-size="24" font-weight="bold"
|
||||||
|
fill="#2E7D32" text-anchor="middle">空闲</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 473 B |
|
@ -30,6 +30,10 @@ onMounted(() => {
|
||||||
const code = urlParams.get('code') || undefined;
|
const code = urlParams.get('code') || undefined;
|
||||||
const state = urlParams.get('state') || undefined;
|
const state = urlParams.get('state') || undefined;
|
||||||
const corpid = urlParams.get('corpid') || undefined;
|
const corpid = urlParams.get('corpid') || undefined;
|
||||||
|
const isAdmin = urlParams.get('isAdmin') || undefined;
|
||||||
|
if (isAdmin == '1') {
|
||||||
|
wxStore.setIsCabinetAdmin(true);
|
||||||
|
}
|
||||||
if (code || state) {
|
if (code || state) {
|
||||||
wxStore.handleWxCallback({ corpid, code, state })
|
wxStore.handleWxCallback({ corpid, code, state })
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { request } from '@/http/axios'
|
||||||
|
import { SubmitApprovalRequestData, SubmitApprovalResponseData } from './type'
|
||||||
|
|
||||||
|
export const submitApprovalApi = (data: SubmitApprovalRequestData) => {
|
||||||
|
return request<SubmitApprovalResponseData>({
|
||||||
|
url: 'approval/submit',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
export interface SubmitApprovalRequestData {
|
||||||
|
orderGoodsId: number
|
||||||
|
returnQuantity: number
|
||||||
|
returnImages: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubmitApprovalResponseData = ApiResponseMsgData<{
|
||||||
|
approvalId: number
|
||||||
|
status: number
|
||||||
|
}>
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { request } from '@/http/axios'
|
||||||
|
import type { CabinetDetailResponse } from './type'
|
||||||
|
|
||||||
|
/** 获取智能柜详情接口 */
|
||||||
|
export function getCabinetDetailApi() {
|
||||||
|
return request<CabinetDetailResponse>({
|
||||||
|
url: 'cabinet/detail',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openCabinet(lockControlNo: number, pinNo: number) {
|
||||||
|
return request<ApiResponseData<void>>({
|
||||||
|
url: `cabinet/openCabinet/${lockControlNo}/${pinNo}`,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
export interface CabinetDetailDTO {
|
||||||
|
cabinetId: number
|
||||||
|
cabinetName: string
|
||||||
|
lockControlNo: number
|
||||||
|
cells: CellInfoDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CellInfoDTO {
|
||||||
|
cellId: number
|
||||||
|
pinNo: number
|
||||||
|
product?: ProductInfoDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProductInfoDTO {
|
||||||
|
goodsId: number
|
||||||
|
goodsName: string
|
||||||
|
price: number
|
||||||
|
coverImg: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CabinetDetailResponse = ApiResponseData<CabinetDetailDTO[]>
|
|
@ -32,7 +32,7 @@ export function getOpenIdApi(params: GetOpenIdRequestParams) {
|
||||||
/** 企业微信登录 */
|
/** 企业微信登录 */
|
||||||
export function qyLogin(params: QyLoginRequestParams) {
|
export function qyLogin(params: QyLoginRequestParams) {
|
||||||
return request<ApiResponseData<QyLoginDTO>>({
|
return request<ApiResponseData<QyLoginDTO>>({
|
||||||
url: "common/login/qy",
|
url: "payment/login/qy",
|
||||||
method: "get",
|
method: "get",
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,12 +3,16 @@ const router = useRouter()
|
||||||
|
|
||||||
const tabbarItemList = computed(() => {
|
const tabbarItemList = computed(() => {
|
||||||
const routes = router.getRoutes()
|
const routes = router.getRoutes()
|
||||||
return routes.filter(route => route.meta.layout?.tabbar?.showTabbar).map(route => ({
|
return routes.filter(route => route.meta.layout?.tabbar?.showTabbar)
|
||||||
|
.map(route => ({
|
||||||
title: route.meta.title,
|
title: route.meta.title,
|
||||||
icon: route.meta.layout?.tabbar?.icon,
|
icon: route.meta.layout?.tabbar?.icon,
|
||||||
path: route.path
|
path: route.path
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
import { useWxStoreOutside } from '@/pinia/stores/wx'
|
||||||
|
const wxStore = useWxStoreOutside()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { showConfirmDialog, showSuccessToast, showToast, UploaderFileListItem } from 'vant'
|
||||||
|
import axios from "axios"
|
||||||
|
import { submitApprovalApi } from '@/common/apis/approval'
|
||||||
|
import type { SubmitApprovalRequestData } from '@/common/apis/approval/type'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
const { VITE_APP_BASE_API } = import.meta.env;
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const formData = ref<SubmitApprovalRequestData>({
|
||||||
|
orderGoodsId: Number(route.query.orderGoodsId),
|
||||||
|
returnQuantity: 1,
|
||||||
|
returnImages: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => route.query.orderGoodsId, (newVal) => {
|
||||||
|
formData.value.orderGoodsId = newVal ? Number(newVal) : NaN
|
||||||
|
})
|
||||||
|
|
||||||
|
const orderId = ref(Number(route.query.orderId))
|
||||||
|
console.log('orderId: ', orderId.value)
|
||||||
|
watch(() => route.query.orderId, (newVal) => {
|
||||||
|
orderId.value = newVal? Number(newVal) : NaN
|
||||||
|
console.log('orderId: ', orderId.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const submitting = ref(false)
|
||||||
|
const fileList = ref<UploaderFileListItem[]>([])
|
||||||
|
const uploading = ref(false)
|
||||||
|
|
||||||
|
const validateForm = () => {
|
||||||
|
if (!formData.value.orderGoodsId || isNaN(formData.value.orderGoodsId)) {
|
||||||
|
showConfirmDialog({ title: '错误', message: '订单ID参数错误' })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (formData.value.returnQuantity < 1) {
|
||||||
|
showConfirmDialog({ title: '提示', message: '退还数量至少为1' })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFileUpload = async (items: UploaderFileListItem | UploaderFileListItem[]) => {
|
||||||
|
const files = Array.isArray(items) ? items : [items]
|
||||||
|
uploading.value = true
|
||||||
|
try {
|
||||||
|
const uploadPromises = files.map(async (item) => {
|
||||||
|
item.status = 'uploading'
|
||||||
|
item.message = '上传中...'
|
||||||
|
const file = item.file as File
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
|
||||||
|
const { data } = await axios.post<{
|
||||||
|
code: number
|
||||||
|
data: { url: string }
|
||||||
|
message?: string
|
||||||
|
}>(VITE_APP_BASE_API + '/file/upload', formData, {
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (data.code !== 0) {
|
||||||
|
throw new Error(data.message || '文件上传失败')
|
||||||
|
}
|
||||||
|
return { url: data.data.url }
|
||||||
|
})
|
||||||
|
|
||||||
|
const urls = await Promise.all(uploadPromises)
|
||||||
|
files.forEach((item, index) => {
|
||||||
|
item.status = 'done'
|
||||||
|
item.message = ''
|
||||||
|
item.url = urls[index].url
|
||||||
|
})
|
||||||
|
showToast('上传成功')
|
||||||
|
} catch (error) {
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '上传失败',
|
||||||
|
message: error instanceof Error ? error.message : '未知错误'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
uploading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!validateForm()) return
|
||||||
|
|
||||||
|
submitting.value = true
|
||||||
|
try {
|
||||||
|
formData.value.returnImages = fileList.value.map(item => item.url).join(',')
|
||||||
|
|
||||||
|
const { code, data } = await submitApprovalApi(formData.value)
|
||||||
|
|
||||||
|
if (code === 0) {
|
||||||
|
showSuccessToast('提交成功')
|
||||||
|
router.push('/order/' + orderId)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('提交失败:', error)
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '提交失败',
|
||||||
|
message: error instanceof Error ? error.message : '网络请求异常'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="approval-container">
|
||||||
|
<van-nav-bar title="退货审批" left-text="返回" left-arrow fixed @click-left="() => router.go(-1)" />
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<van-cell-group>
|
||||||
|
<!-- 移除订单ID和商品ID的输入框 -->
|
||||||
|
<van-field v-model="formData.returnQuantity" label="退还数量" type="number" :min="1" />
|
||||||
|
</van-cell-group>
|
||||||
|
|
||||||
|
<van-cell-group class="upload-section">
|
||||||
|
<van-cell title="凭证图片">
|
||||||
|
<template #extra>
|
||||||
|
<van-uploader v-model="fileList" multiple max-count="3" :after-read="handleFileUpload" />
|
||||||
|
</template>
|
||||||
|
</van-cell>
|
||||||
|
</van-cell-group>
|
||||||
|
|
||||||
|
<div class="submit-bar">
|
||||||
|
<van-button type="primary" block :loading="submitting" loading-text="提交中..." @click="handleSubmit">
|
||||||
|
提交申请
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.approval-container {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
padding-top: 46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-section {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-bar {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 16px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,315 @@
|
||||||
|
<template>
|
||||||
|
<div class="cabinet-container van-safe-area-bottom">
|
||||||
|
<van-sidebar v-model="activeCabinet" class="cabinet-sidebar" @change="onCabinetChange">
|
||||||
|
<van-sidebar-item v-for="cabinet in cabinetList" :key="cabinet.cabinetId" :title="cabinet.cabinetName" />
|
||||||
|
</van-sidebar>
|
||||||
|
|
||||||
|
<div class="product-list">
|
||||||
|
<van-cell v-for="locker in lockerList" :key="locker.lockerId" class="product-item">
|
||||||
|
<template #icon>
|
||||||
|
<van-image width="80" height="80"
|
||||||
|
:src="locker.coverImg ? locker.coverImg : `${publicPath}` + 'img/product-image.svg'" fit="cover"
|
||||||
|
radius="4" class="product-image">
|
||||||
|
</van-image>
|
||||||
|
<!-- <div v-else class="status-overlay">
|
||||||
|
<svg width="80" height="80" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="5" y="5" width="90" height="90" rx="10" fill="#E8F5E9" stroke="#81C784"
|
||||||
|
stroke-width="2" />
|
||||||
|
<text x="50" y="60" font-family="Arial, sans-serif" font-size="24" font-weight="bold"
|
||||||
|
fill="#2E7D32" text-anchor="middle">空闲</text>
|
||||||
|
</svg>
|
||||||
|
</div> -->
|
||||||
|
</template>
|
||||||
|
<div class="product-info">
|
||||||
|
<div class="goods-info">
|
||||||
|
<div v-if="locker.goodsName">
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="locker-number">格口 {{ locker.lockerNumber }}</div>
|
||||||
|
<div class="goods-price">¥{{ (locker.price || 0).toFixed(2) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="goods-name">{{ locker.goodsName }}</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="locker-number">格口 {{ locker.lockerNumber }}</div>
|
||||||
|
<div class="goods-price">¥0.00</div>
|
||||||
|
</div>
|
||||||
|
<div class="goods-name">空闲</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="button-group">
|
||||||
|
<van-button size="small" type="primary" class="detail-btn" @click="showLockerDetail(locker)">
|
||||||
|
详情
|
||||||
|
</van-button>
|
||||||
|
<van-button size="small" plain hairline :loading="openingLockerId === locker.lockerId"
|
||||||
|
@click="handleOpenLocker(locker)">
|
||||||
|
立即开启
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-cell>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { getCabinetDetailApi, openCabinet } from '@/common/apis/cabinet'
|
||||||
|
import type { CabinetDetailDTO } from '@/common/apis/cabinet/type'
|
||||||
|
import { useWxStoreOutside } from '@/pinia/stores/wx'
|
||||||
|
import { publicPath } from "@/common/utils/path"
|
||||||
|
|
||||||
|
const wxStore = useWxStoreOutside()
|
||||||
|
|
||||||
|
const activeCabinet = ref(0)
|
||||||
|
const cabinetList = ref<CabinetItem[]>([])
|
||||||
|
const lockerList = ref<LockerItem[]>([])
|
||||||
|
const openingLockerId = ref<number | null>(null)
|
||||||
|
const cabinetData = ref<CabinetDetailDTO[]>([]);
|
||||||
|
|
||||||
|
interface CabinetItem {
|
||||||
|
cabinetId: number
|
||||||
|
cabinetName: string
|
||||||
|
lockControlNo: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LockerItem {
|
||||||
|
lockerId: number
|
||||||
|
lockerNumber: number
|
||||||
|
status: 0 | 1
|
||||||
|
statusClass: 'available' | 'occupied'
|
||||||
|
goodsName?: string
|
||||||
|
price?: number
|
||||||
|
coverImg?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取柜机列表
|
||||||
|
const loadCabinetDetail = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await getCabinetDetailApi();
|
||||||
|
cabinetData.value = data;
|
||||||
|
cabinetList.value = data.map(cabinet => ({
|
||||||
|
cabinetId: cabinet.cabinetId,
|
||||||
|
cabinetName: cabinet.cabinetName,
|
||||||
|
lockControlNo: cabinet.lockControlNo
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 根据当前选中柜机加载格口数据
|
||||||
|
if (data.length > 0) {
|
||||||
|
updateLockerList(data[activeCabinet.value])
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取柜机详情失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新格口列表数据
|
||||||
|
const updateLockerList = (cabinet: CabinetDetailDTO) => {
|
||||||
|
lockerList.value = cabinet.cells.map(cell => ({
|
||||||
|
lockerId: cell.cellId,
|
||||||
|
lockerNumber: cell.pinNo,
|
||||||
|
status: cell.product ? 1 : 0,
|
||||||
|
statusClass: cell.product ? 'occupied' : 'available',
|
||||||
|
goodsName: cell.product?.goodsName,
|
||||||
|
price: cell.product?.price,
|
||||||
|
coverImg: cell.product?.coverImg
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听侧边栏切换事件
|
||||||
|
const onCabinetChange = (index: number) => {
|
||||||
|
activeCabinet.value = index
|
||||||
|
if (cabinetList.value.length > index) {
|
||||||
|
updateLockerList(cabinetData.value[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const showLockerDetail = (locker: LockerItem) => {
|
||||||
|
// 实现详情弹窗逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpenLocker = async (locker: LockerItem) => {
|
||||||
|
openingLockerId.value = locker.lockerId
|
||||||
|
try {
|
||||||
|
// 调用打开柜口接口
|
||||||
|
await openCabinet(cabinetList.value[activeCabinet.value].lockControlNo, locker.lockerNumber)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('打开柜口失败:', error)
|
||||||
|
} finally {
|
||||||
|
openingLockerId.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCabinetDetail()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cabinet-container {
|
||||||
|
display: flex;
|
||||||
|
height: calc(100vh - var(--van-tabbar-height));
|
||||||
|
}
|
||||||
|
|
||||||
|
.cabinet-sidebar {
|
||||||
|
width: 120px;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.locker-grid {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
:deep(.van-grid) {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.van-grid-item {
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
.van-grid-item__content {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-overlay {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.status-text {
|
||||||
|
color: white;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.locker-number {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.goods-name {
|
||||||
|
white-space: normal;
|
||||||
|
word-wrap: break-word;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.2;
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-price {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #e95d5d;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 0;
|
||||||
|
|
||||||
|
.detail-btn {
|
||||||
|
flex: 1;
|
||||||
|
background-color: #e95d5d;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.van-button--default) {
|
||||||
|
color: #e95d5d;
|
||||||
|
border-color: #e95d5d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px 16px 60px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: min(2.667vw, 20px) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 100px;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-name {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group .van-button {
|
||||||
|
font-size: 12px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.van-button) {
|
||||||
|
--van-button-mini-height: 24px;
|
||||||
|
--van-button-mini-padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.locker-number {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-price {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #e95d5d;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-btn {
|
||||||
|
flex: 1;
|
||||||
|
background-color: #e95d5d;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.van-button--default) {
|
||||||
|
color: #e95d5d;
|
||||||
|
border-color: #e95d5d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,6 +2,7 @@
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useWxStore } from '@/pinia/stores/wx'
|
import { useWxStore } from '@/pinia/stores/wx'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { publicPath } from "@/common/utils/path"
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const wxStore = useWxStore()
|
const wxStore = useWxStore()
|
||||||
|
@ -19,7 +20,7 @@ const balance = computed(() => wxStore.balance)
|
||||||
round
|
round
|
||||||
width="80"
|
width="80"
|
||||||
height="80"
|
height="80"
|
||||||
src="/img/1.jpg"
|
:src="`${publicPath}img/1.jpg`"
|
||||||
class="mr-4"
|
class="mr-4"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
|
@ -73,6 +74,7 @@ const balance = computed(() => wxStore.balance)
|
||||||
<!-- 个人中心按钮 -->
|
<!-- 个人中心按钮 -->
|
||||||
<van-cell-group>
|
<van-cell-group>
|
||||||
<van-cell title="订单列表" is-link @click="router.push('/order-list')" />
|
<van-cell title="订单列表" is-link @click="router.push('/order-list')" />
|
||||||
|
<van-cell title="柜机管理" is-link @click="router.push('/cabinet')" v-if="wxStore.isCabinetAdmin"/>
|
||||||
</van-cell-group>
|
</van-cell-group>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted } from 'vue'
|
import { onMounted } from 'vue'
|
||||||
import { useOrderStore } from '@/pinia/stores/order'
|
import { useOrderStore } from '@/pinia/stores/order'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
const orderStore = useOrderStore()
|
const orderStore = useOrderStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const statusMap: Record<number, string> = {
|
const statusMap: Record<number, string> = {
|
||||||
1: '待付款',
|
1: '待付款',
|
||||||
|
@ -29,9 +30,29 @@ const order = computed(() => {
|
||||||
return orderStore.orders.find(o => o.orderId === orderId.value)
|
return orderStore.orders.find(o => o.orderId === orderId.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const orderGoodsStatusMap: Record<number, string> = {
|
||||||
|
5: '审核中',
|
||||||
|
2: '已退货',
|
||||||
|
6: '退货未通过'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOrderGoodsStatusText = (status: number) => {
|
||||||
|
return orderGoodsStatusMap[status] || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRefund = (item: any) => {
|
||||||
|
router.push({
|
||||||
|
path: '/approval/submit',
|
||||||
|
query: {
|
||||||
|
orderGoodsId: item.orderGoods.orderGoodsId,
|
||||||
|
orderId: orderId.value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!order.value) {
|
if (!order.value) {
|
||||||
// Handle case when order is not found
|
// 订单不存在处理
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -62,15 +83,34 @@ onMounted(() => {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div class="product-info">
|
<div class="product-info">
|
||||||
<div class="product-name van-ellipsis">
|
<div class="product-name-price">
|
||||||
{{ item.goodsInfo.goodsName }}
|
<div class="product-name van-ellipsis">
|
||||||
</div>
|
{{ item.goodsInfo.goodsName }}
|
||||||
<div class="product-price">
|
</div>
|
||||||
¥{{ item.goodsInfo.price.toFixed(2) }}
|
<div class="product-price">
|
||||||
|
¥{{ item.orderGoods.price.toFixed(2) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-row">
|
<div class="action-row">
|
||||||
<p>数量: {{ item.orderGoods.quantity }}</p>
|
<p>数量: {{ item.orderGoods.quantity }}</p>
|
||||||
<p>小计: ¥{{ (item.orderGoods.price * item.orderGoods.quantity).toFixed(2) }}</p>
|
<p>小计: ¥{{ (item.orderGoods.price * item.orderGoods.quantity).toFixed(2) }}</p>
|
||||||
|
<template v-if="item.orderGoods.status === 1">
|
||||||
|
<van-button
|
||||||
|
type="primary"
|
||||||
|
size="mini"
|
||||||
|
class="refund-btn"
|
||||||
|
@click="handleRefund(item)"
|
||||||
|
>
|
||||||
|
退还
|
||||||
|
</van-button>
|
||||||
|
</template>
|
||||||
|
<span
|
||||||
|
v-if="[2,5,6].includes(item.orderGoods.status)"
|
||||||
|
class="status-text"
|
||||||
|
:class="`status-${item.orderGoods.status}`"
|
||||||
|
>
|
||||||
|
{{ getOrderGoodsStatusText(item.orderGoods.status) }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
|
@ -121,9 +161,42 @@ goods-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-name-price {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-name {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-price {
|
||||||
|
color: #ee0a24;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
.not-found {
|
.not-found {
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.refund-btn {
|
||||||
|
margin-left: auto;
|
||||||
|
color: #fff;
|
||||||
|
background: #ee0a24;
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 8px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -27,7 +27,11 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
balance.value = amount;
|
balance.value = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleWxCallback = async (params: { corpid?: string; code?: string; state?: string }) => {
|
const setIsCabinetAdmin = (isAdmin: boolean) => {
|
||||||
|
isCabinetAdmin.value = isAdmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleWxCallback = async (params: { corpid?: string; code?: string; state?: string; }) => {
|
||||||
console.log('handleWxCallback:', params)
|
console.log('handleWxCallback:', params)
|
||||||
if (params.code) {
|
if (params.code) {
|
||||||
code.value = params.code
|
code.value = params.code
|
||||||
|
@ -78,7 +82,8 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { code, state, openid, corpid, userid, balance, setOpenid, setBalance, handleWxCallback }
|
return { code, state, openid, corpid, userid, balance, isCabinetAdmin,
|
||||||
|
setOpenid, setBalance, handleWxCallback, setIsCabinetAdmin }
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,6 +29,11 @@ export const systemRoutes: RouteRecordRaw[] = [
|
||||||
|
|
||||||
/** 业务页面 */
|
/** 业务页面 */
|
||||||
export const routes: RouteRecordRaw[] = [
|
export const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: '/approval/submit',
|
||||||
|
component: () => import('@/pages/approval/submit.vue'),
|
||||||
|
meta: { requiresAuth: true }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/order-success',
|
path: '/order-success',
|
||||||
name: 'OrderSuccess',
|
name: 'OrderSuccess',
|
||||||
|
@ -63,6 +68,25 @@ export const routes: RouteRecordRaw[] = [
|
||||||
title: "订单结算"
|
title: "订单结算"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/cabinet',
|
||||||
|
component: () => import('@/pages/cabinet/index.vue'),
|
||||||
|
name: "Cabinet",
|
||||||
|
meta: {
|
||||||
|
title: '柜机管理',
|
||||||
|
keepAlive: true,
|
||||||
|
layout: {
|
||||||
|
navBar: {
|
||||||
|
showNavBar: false,
|
||||||
|
showLeftArrow: false
|
||||||
|
},
|
||||||
|
tabbar: {
|
||||||
|
showTabbar: false,
|
||||||
|
icon: "home-o"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
component: () => import("@/pages/product/ProductList.vue"),
|
component: () => import("@/pages/product/ProductList.vue"),
|
||||||
|
@ -103,6 +127,11 @@ export const routes: RouteRecordRaw[] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
/* export const routes: RouteRecordRaw[] = [
|
/* export const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: '/approval/submit',
|
||||||
|
component: () => import('@/pages/approval/submit.vue'),
|
||||||
|
meta: { requiresAuth: true }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/order-success',
|
path: '/order-success',
|
||||||
name: 'OrderSuccess',
|
name: 'OrderSuccess',
|
||||||
|
|
|
@ -30,5 +30,6 @@ declare module 'vue' {
|
||||||
VanTabbar: typeof import('vant/es')['Tabbar']
|
VanTabbar: typeof import('vant/es')['Tabbar']
|
||||||
VanTabbarItem: typeof import('vant/es')['TabbarItem']
|
VanTabbarItem: typeof import('vant/es')['TabbarItem']
|
||||||
VanTag: typeof import('vant/es')['Tag']
|
VanTag: typeof import('vant/es')['Tag']
|
||||||
|
VanUploader: typeof import('vant/es')['Uploader']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue