feat(approval): 添加退还申请页面及文档更新

添加退还申请页面 submit.vue,实现表单提交、图片上传压缩功能
更新迁移文档,添加退还申请页面的迁移说明
在 pages.json 中注册新页面
This commit is contained in:
dzq 2025-11-05 09:52:52 +08:00
parent 077db01306
commit e37764de95
3 changed files with 386 additions and 1 deletions

View File

@ -13,3 +13,8 @@
@doc\thirdParty\src\pages\rental\index.vue
我的柜子页面也迁移到本项目。注意thirdParty下的是H5项目现在需要改为微信小程序uni-app。api需要使用原Product
List.vue中已经移植到本项目的相应apistores也需要使用移植后的pinia。生成的代码写到 @src\pages\order\ 文件夹下
参考已迁移至本项目的代码 @src\pages\index\ 和迁移文档 @doc\迁移工作总结.md 。将
@doc\thirdParty\src\pages\approval\submit.vue
退还申请页面也迁移到本项目。注意thirdParty下的是H5项目现在需要改为微信小程序uni-app。api需要使用原项目中已经移
植到本项目的相应apistores也需要使用移植后的pinia。生成的代码写到 @src\pages\approval\ 文件夹下

View File

@ -61,6 +61,14 @@
},
"excludeLoginPath": false
},
{
"path": "pages/approval/submit",
"type": "page",
"style": {
"navigationBarTitleText": "退还"
},
"enablePullDownRefresh": false
},
{
"path": "pages/index/checkout",
"type": "page"

View File

@ -0,0 +1,372 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import { submitApprovalApi } from '@/api/approval'
import type { SubmitApprovalRequestData } from '@/api/approval/types'
import { useOrderStore } from '@/pinia/stores/order'
import { useWxStoreOutside } from '@/pinia/stores/wx'
import { getEnvBaseUploadUrl } from '@/utils'
//
definePage({
style: {
navigationBarTitleText: '退还',
},
enablePullDownRefresh: false,
})
const orderStore = useOrderStore()
const wxStore = useWxStoreOutside()
//
const orderGoodsId = ref<number>(0)
const orderId = ref<number>(0)
onLoad((options: any) => {
console.log('页面参数:', options)
if (options.orderGoodsId) {
orderGoodsId.value = Number(options.orderGoodsId)
}
if (options.orderId) {
orderId.value = Number(options.orderId)
}
})
const formData = ref<SubmitApprovalRequestData>({
orderGoodsId: orderGoodsId.value,
returnQuantity: 1,
returnImages: '',
returnRemark: '',
corpid: wxStore.corpid,
applyUserid: wxStore.userid,
})
watch(orderGoodsId, (newVal) => {
formData.value.orderGoodsId = newVal
})
const submitting = ref(false)
//
interface UploadedFile {
url: string
}
const uploadedFiles = ref<UploadedFile[]>([])
const uploading = ref(false)
//
const uploadUrl = `${getEnvBaseUploadUrl()}/file/upload`
//
const chooseImages = () => {
uni.chooseMedia({
count: 3 - uploadedFiles.value.length,
mediaType: ['image'],
sourceType: ['album', 'camera'],
maxDuration: 30,
camera: 'back',
success: (res) => {
console.log('选择图片成功:', res)
uploadImages(res.tempFiles)
},
fail: (err) => {
console.error('选择图片失败:', err)
uni.showToast({
title: '选择图片失败',
icon: 'none',
})
},
})
}
//
// H5使 compressorjs
// File API Canvas使 uni.compressImage
const compressImage = (tempFilePath: string): Promise<string> => {
return new Promise((resolve, reject) => {
uni.compressImage({
src: tempFilePath,
quality: 0.8, // 0-1 compressorjs quality
maxWidth: 1280, // compressorjs maxWidth
maxHeight: 1280, // compressorjs maxHeight
success: (res) => {
console.log('图片压缩成功:', res)
resolve(res.tempFilePath)
},
fail: (err) => {
console.error('图片压缩失败:', err)
// 使
resolve(tempFilePath)
},
})
})
}
//
const uploadImages = (files: UniApp.MediaFile[]) => {
uploading.value = true
const uploadPromises = files.map(async (file) => {
//
const compressedPath = await compressImage(file.tempFilePath)
return new Promise<UploadedFile>((resolve, reject) => {
uni.uploadFile({
url: uploadUrl,
filePath: compressedPath,
name: 'file',
formData: {},
header: {
'Content-Type': 'multipart/form-data',
},
success: (uploadRes) => {
try {
const data = JSON.parse(uploadRes.data)
if (data.code === 0) {
resolve({ url: data.data.url })
} else {
reject(new Error(data.message || '上传失败'))
}
} catch (error) {
reject(new Error('响应解析失败'))
}
},
fail: (err) => {
reject(err)
},
})
})
})
Promise.all(uploadPromises)
.then((results) => {
uploadedFiles.value.push(...results)
uni.showToast({
title: '上传成功',
icon: 'success',
})
})
.catch((error) => {
console.error('上传失败:', error)
uni.showToast({
title: error.message || '上传失败',
icon: 'none',
})
})
.finally(() => {
uploading.value = false
})
}
//
const deleteImage = (index: number) => {
uploadedFiles.value.splice(index, 1)
}
//
const validateForm = () => {
if (!formData.value.orderGoodsId || isNaN(formData.value.orderGoodsId)) {
uni.showToast({
title: '订单ID参数错误',
icon: 'none',
})
return false
}
if (formData.value.returnQuantity < 1) {
uni.showToast({
title: '退还数量至少为1',
icon: 'none',
})
return false
}
return true
}
//
const handleSubmit = async () => {
if (!validateForm()) return
submitting.value = true
try {
// URL
formData.value.returnImages = uploadedFiles.value.map(item => item.url).join(',')
formData.value.corpid = wxStore.corpid
formData.value.applyUserid = wxStore.userid
console.log('提交数据:', formData.value)
const { code, data } = await submitApprovalApi(formData.value)
if (code === 0) {
//
orderStore.getOrders(
wxStore.corpid,
wxStore.openid,
wxStore.qyUserId,
0
)
uni.showModal({
title: '提交成功',
content: '退货申请已提交,等待管理员审核',
showCancel: false,
success: () => {
//
uni.navigateBack()
},
})
}
} catch (error) {
console.error('提交失败:', error)
uni.showModal({
title: '提交失败',
content: error instanceof Error ? error.message : '网络请求异常',
showCancel: false,
})
} finally {
submitting.value = false
}
}
</script>
<template>
<view class="approval-container">
<view class="content-wrapper">
<wd-cell-group>
<wd-input
v-model="formData.returnQuantity"
label="退还数量"
type="number"
:min="1"
placeholder="请输入退还数量"
/>
<wd-textarea
v-model="formData.returnRemark"
label="退货备注"
auto-height
placeholder="请输入退货备注"
/>
</wd-cell-group>
<wd-cell-group custom-class="upload-section">
<wd-cell title="凭证图片" value="">
<template #extra>
<view class="upload-area">
<view
v-for="(file, index) in uploadedFiles"
:key="index"
class="uploaded-image"
>
<image
:src="file.url"
mode="aspectFill"
class="image-preview"
/>
<view class="delete-btn" @tap="() => deleteImage(index)">
<wd-icon name="delete" size="16px" color="#fff" />
</view>
</view>
<view
v-if="uploadedFiles.length < 3"
class="upload-button"
@tap="chooseImages"
>
<wd-icon name="camera" size="32px" color="#ccc" />
<text class="upload-text">
{{ uploading ? '上传中...' : '点击上传' }}
</text>
</view>
</view>
</template>
</wd-cell>
</wd-cell-group>
<view class="submit-bar">
<wd-button
type="primary"
block
:loading="submitting"
:disabled="uploading"
@tap="handleSubmit"
>
{{ submitting ? '提交中...' : '提交申请' }}
</wd-button>
</view>
</view>
</view>
</template>
<style scoped>
.approval-container {
min-height: 100vh;
background-color: #f7f8fa;
}
.content-wrapper {
padding-top: 20rpx;
padding-bottom: 160rpx;
}
.upload-section {
margin: 20rpx 0;
}
.upload-area {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.uploaded-image {
position: relative;
width: 160rpx;
height: 160rpx;
border-radius: 8rpx;
overflow: hidden;
}
.image-preview {
width: 100%;
height: 100%;
}
.delete-btn {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 40rpx;
height: 40rpx;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.upload-button {
width: 160rpx;
height: 160rpx;
border: 2rpx dashed #ddd;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fff;
}
.upload-text {
margin-top: 16rpx;
font-size: 24rpx;
color: #999;
}
.submit-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 32rpx;
background-color: #fff;
box-shadow: 0 -4rpx 24rpx rgba(0, 0, 0, 0.1);
}
</style>