feat(approval): 添加退还申请页面及文档更新
添加退还申请页面 submit.vue,实现表单提交、图片上传压缩功能 更新迁移文档,添加退还申请页面的迁移说明 在 pages.json 中注册新页面
This commit is contained in:
parent
077db01306
commit
e37764de95
|
|
@ -13,3 +13,8 @@
|
||||||
@doc\thirdParty\src\pages\rental\index.vue
|
@doc\thirdParty\src\pages\rental\index.vue
|
||||||
我的柜子页面也迁移到本项目。注意thirdParty下的是H5项目,现在需要改为微信小程序uni-app。api需要使用原Product
|
我的柜子页面也迁移到本项目。注意thirdParty下的是H5项目,现在需要改为微信小程序uni-app。api需要使用原Product
|
||||||
List.vue中已经移植到本项目的相应api,stores也需要使用移植后的pinia。生成的代码写到 @src\pages\order\ 文件夹下
|
List.vue中已经移植到本项目的相应api,stores也需要使用移植后的pinia。生成的代码写到 @src\pages\order\ 文件夹下
|
||||||
|
|
||||||
|
参考已迁移至本项目的代码 @src\pages\index\ 和迁移文档 @doc\迁移工作总结.md 。将
|
||||||
|
@doc\thirdParty\src\pages\approval\submit.vue
|
||||||
|
退还申请页面也迁移到本项目。注意thirdParty下的是H5项目,现在需要改为微信小程序uni-app。api需要使用原项目中已经移
|
||||||
|
植到本项目的相应api,stores也需要使用移植后的pinia。生成的代码写到 @src\pages\approval\ 文件夹下
|
||||||
|
|
@ -61,6 +61,14 @@
|
||||||
},
|
},
|
||||||
"excludeLoginPath": false
|
"excludeLoginPath": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/approval/submit",
|
||||||
|
"type": "page",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "退还"
|
||||||
|
},
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/index/checkout",
|
"path": "pages/index/checkout",
|
||||||
"type": "page"
|
"type": "page"
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
Loading…
Reference in New Issue