feat(approval): 添加审批中心功能,包括审批列表和提交审批
新增审批中心页面,支持管理员查看审批列表,包含搜索、分页和状态筛选功能。同时优化了提交审批页面,增加退货备注字段,并改进上传文件后的反馈提示。
This commit is contained in:
parent
42eae0b1cd
commit
9c81c228ee
|
@ -1,5 +1,13 @@
|
||||||
import { request } from '@/http/axios'
|
import { request } from '@/http/axios'
|
||||||
import { SubmitApprovalRequestData, SubmitApprovalResponseData } from './type'
|
import { SubmitApprovalRequestData, SubmitApprovalResponseData, SearchApiReturnApprovalQuery, ApiResponsePageData, ReturnApprovalEntity } from './type'
|
||||||
|
|
||||||
|
export const getApprovalListApi = (params: SearchApiReturnApprovalQuery) => {
|
||||||
|
return request<ApiResponsePageData<ReturnApprovalEntity>>({
|
||||||
|
url: 'approval/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const submitApprovalApi = (data: SubmitApprovalRequestData) => {
|
export const submitApprovalApi = (data: SubmitApprovalRequestData) => {
|
||||||
return request<SubmitApprovalResponseData>({
|
return request<SubmitApprovalResponseData>({
|
||||||
|
|
|
@ -2,6 +2,55 @@ export interface SubmitApprovalRequestData {
|
||||||
orderGoodsId: number
|
orderGoodsId: number
|
||||||
returnQuantity: number
|
returnQuantity: number
|
||||||
returnImages: string
|
returnImages: string
|
||||||
|
returnRemark: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchApiReturnApprovalQuery {
|
||||||
|
pageNum: number
|
||||||
|
pageSize: number
|
||||||
|
approvalId?: number
|
||||||
|
orderId?: number
|
||||||
|
goodsId?: number
|
||||||
|
status?: number
|
||||||
|
startTime?: string
|
||||||
|
endTime?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiResponsePageData<T> {
|
||||||
|
code: number
|
||||||
|
msg: string
|
||||||
|
data: {
|
||||||
|
total: number
|
||||||
|
rows: T[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReturnApprovalEntity {
|
||||||
|
approvalId: number
|
||||||
|
orderId: number
|
||||||
|
goodsId: number
|
||||||
|
/** 关联订单商品ID */
|
||||||
|
orderGoodsId: number
|
||||||
|
/** 归还数量 */
|
||||||
|
returnQuantity: number
|
||||||
|
/** 商品单价 */
|
||||||
|
goodsPrice: number
|
||||||
|
status: number
|
||||||
|
returnAmount: number
|
||||||
|
/** 归还图片路径数组 */
|
||||||
|
returnImages: string
|
||||||
|
/** 审核图片路径数组 */
|
||||||
|
auditImages: string
|
||||||
|
/** 归还说明 */
|
||||||
|
returnRemark: string
|
||||||
|
/** 审核说明 */
|
||||||
|
auditRemark: string
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
/** 商品名称 */
|
||||||
|
goodsName: string
|
||||||
|
/** 封面图URL */
|
||||||
|
coverImg: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SubmitApprovalResponseData = ApiResponseMsgData<{
|
export type SubmitApprovalResponseData = ApiResponseMsgData<{
|
||||||
|
|
|
@ -0,0 +1,218 @@
|
||||||
|
<template>
|
||||||
|
<div class="approval-list-container">
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<van-form @submit="handleSearch">
|
||||||
|
<van-field
|
||||||
|
v-model="searchParams.approvalId"
|
||||||
|
label="审批单号"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入审批单号"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-field
|
||||||
|
v-model="searchParams.orderId"
|
||||||
|
label="订单编号"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入订单编号"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-field
|
||||||
|
v-model="searchParams.goodsId"
|
||||||
|
label="商品ID"
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入商品ID"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<van-field
|
||||||
|
name="status"
|
||||||
|
label="审批状态"
|
||||||
|
readonly
|
||||||
|
clickable
|
||||||
|
:value="statusText"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
@click="showStatusPicker = true"
|
||||||
|
/>
|
||||||
|
<van-popup v-model:show="showStatusPicker" position="bottom">
|
||||||
|
<van-picker
|
||||||
|
:columns="statusOptions"
|
||||||
|
@confirm="onStatusConfirm"
|
||||||
|
@cancel="showStatusPicker = false"
|
||||||
|
/>
|
||||||
|
</van-popup>
|
||||||
|
|
||||||
|
<!-- <van-field
|
||||||
|
readonly
|
||||||
|
clickable
|
||||||
|
name="date"
|
||||||
|
:value="dateRangeText"
|
||||||
|
label="申请时间"
|
||||||
|
placeholder="选择时间范围"
|
||||||
|
@click="showDatePicker = true"
|
||||||
|
/>
|
||||||
|
<van-popup v-model:show="showDatePicker" position="bottom">
|
||||||
|
<van-datetime-picker
|
||||||
|
type="daterange"
|
||||||
|
@confirm="onDateConfirm"
|
||||||
|
@cancel="showDatePicker = false"
|
||||||
|
/>
|
||||||
|
</van-popup> -->
|
||||||
|
|
||||||
|
<div style="margin: 16px;">
|
||||||
|
<van-button block type="primary" native-type="submit">搜索</van-button>
|
||||||
|
<van-button
|
||||||
|
block
|
||||||
|
plain
|
||||||
|
type="primary"
|
||||||
|
style="margin-top: 10px;"
|
||||||
|
@click="handleReset"
|
||||||
|
>重置</van-button>
|
||||||
|
</div>
|
||||||
|
</van-form>
|
||||||
|
|
||||||
|
<!-- 审批列表 -->
|
||||||
|
<van-list
|
||||||
|
v-model:loading="loading"
|
||||||
|
:finished="finished"
|
||||||
|
finished-text="没有更多了"
|
||||||
|
@load="onLoad"
|
||||||
|
>
|
||||||
|
<van-cell
|
||||||
|
v-for="item in list"
|
||||||
|
:key="item.approvalId"
|
||||||
|
:title="`审批单号:${item.approvalId}`"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<div>商品名称:{{ item.goodsName }}</div>
|
||||||
|
<div>申请时间:{{ item.createTime }}</div>
|
||||||
|
<van-tag :type="statusTagType(item.status)">
|
||||||
|
{{ statusMap[item.status] }}
|
||||||
|
</van-tag>
|
||||||
|
</template>
|
||||||
|
</van-cell>
|
||||||
|
</van-list>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { getApprovalListApi } from '@/common/apis/approval'
|
||||||
|
import type { SearchApiReturnApprovalQuery, ReturnApprovalEntity } from '@/common/apis/approval/type'
|
||||||
|
import type { PickerConfirmEventParams } from 'vant/es';
|
||||||
|
|
||||||
|
// 搜索参数
|
||||||
|
const searchParams = reactive<SearchApiReturnApprovalQuery>({
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 状态选择相关
|
||||||
|
const showStatusPicker = ref(false)
|
||||||
|
const statusOptions = [
|
||||||
|
{ text: '全部', value: undefined },
|
||||||
|
{ text: '待审核', value: 0 },
|
||||||
|
{ text: '已通过', value: 1 },
|
||||||
|
{ text: '已拒绝', value: 2 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const statusMap: { [key: number]: string } = {
|
||||||
|
0: '待审核',
|
||||||
|
1: '已通过',
|
||||||
|
2: '已拒绝'
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = ref('')
|
||||||
|
|
||||||
|
// 时间选择相关
|
||||||
|
const showDatePicker = ref(false)
|
||||||
|
const dateRangeText = ref('')
|
||||||
|
|
||||||
|
// 列表相关
|
||||||
|
const list = ref<ReturnApprovalEntity[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const finished = ref(false)
|
||||||
|
|
||||||
|
// 状态标签类型
|
||||||
|
const statusTagType = (status: number) => {
|
||||||
|
switch (status) {
|
||||||
|
case 0: return 'warning'
|
||||||
|
case 1: return 'success'
|
||||||
|
case 2: return 'danger'
|
||||||
|
default: return 'default'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理状态选择
|
||||||
|
const onStatusConfirm = (e: PickerConfirmEventParams) => {
|
||||||
|
const { selectedOptions } = e;
|
||||||
|
searchParams.status = Number(selectedOptions[0]?.value);
|
||||||
|
showStatusPicker.value = false;
|
||||||
|
// 确保赋值给 statusText 的是字符串类型
|
||||||
|
statusText.value = String(selectedOptions[0]?.text || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理时间选择
|
||||||
|
const onDateConfirm = (values: Date[]) => {
|
||||||
|
const [start, end] = values
|
||||||
|
searchParams.startTime = start.toISOString().split('T')[0]
|
||||||
|
searchParams.endTime = end.toISOString().split('T')[0]
|
||||||
|
dateRangeText.value = `${searchParams.startTime} 至 ${searchParams.endTime}`
|
||||||
|
showDatePicker.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索处理
|
||||||
|
const handleSearch = () => {
|
||||||
|
list.value = []
|
||||||
|
searchParams.pageNum = 1
|
||||||
|
onLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const handleReset = () => {
|
||||||
|
Object.assign(searchParams, {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
approvalId: undefined,
|
||||||
|
orderId: undefined,
|
||||||
|
goodsId: undefined,
|
||||||
|
status: undefined,
|
||||||
|
startTime: undefined,
|
||||||
|
endTime: undefined
|
||||||
|
})
|
||||||
|
statusText.value = ''
|
||||||
|
dateRangeText.value = ''
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const onLoad = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await getApprovalListApi(searchParams)
|
||||||
|
list.value.push(...data.rows)
|
||||||
|
loading.value = false
|
||||||
|
|
||||||
|
if (list.value.length >= data.total) {
|
||||||
|
finished.value = true
|
||||||
|
} else {
|
||||||
|
searchParams.pageNum++
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取审批列表失败', error)
|
||||||
|
finished.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.approval-list-container {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
.van-cell__title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.van-cell__label {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -13,7 +13,8 @@ const route = useRoute()
|
||||||
const formData = ref<SubmitApprovalRequestData>({
|
const formData = ref<SubmitApprovalRequestData>({
|
||||||
orderGoodsId: Number(route.query.orderGoodsId),
|
orderGoodsId: Number(route.query.orderGoodsId),
|
||||||
returnQuantity: 1,
|
returnQuantity: 1,
|
||||||
returnImages: ''
|
returnImages: '',
|
||||||
|
returnRemark: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => route.query.orderGoodsId, (newVal) => {
|
watch(() => route.query.orderGoodsId, (newVal) => {
|
||||||
|
@ -71,10 +72,9 @@ const handleFileUpload = async (items: UploaderFileListItem | UploaderFileListIt
|
||||||
const urls = await Promise.all(uploadPromises)
|
const urls = await Promise.all(uploadPromises)
|
||||||
files.forEach((item, index) => {
|
files.forEach((item, index) => {
|
||||||
item.status = 'done'
|
item.status = 'done'
|
||||||
item.message = ''
|
item.message = '上传成功'
|
||||||
item.url = urls[index].url
|
item.url = urls[index].url
|
||||||
})
|
})
|
||||||
showToast('上传成功')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showConfirmDialog({
|
showConfirmDialog({
|
||||||
title: '上传失败',
|
title: '上传失败',
|
||||||
|
@ -96,7 +96,13 @@ const handleSubmit = async () => {
|
||||||
|
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
showSuccessToast('提交成功')
|
showSuccessToast('提交成功')
|
||||||
router.push('/order/' + orderId)
|
try {
|
||||||
|
await showConfirmDialog({
|
||||||
|
title: "提交成功",
|
||||||
|
message: `退货申请已提交,等待管理员审核`
|
||||||
|
})
|
||||||
|
} catch (error) { }
|
||||||
|
router.push('/order/' + orderId.value)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('提交失败:', error)
|
console.error('提交失败:', error)
|
||||||
|
@ -118,6 +124,7 @@ const handleSubmit = async () => {
|
||||||
<van-cell-group>
|
<van-cell-group>
|
||||||
<!-- 移除订单ID和商品ID的输入框 -->
|
<!-- 移除订单ID和商品ID的输入框 -->
|
||||||
<van-field v-model="formData.returnQuantity" label="退还数量" type="number" :min="1" />
|
<van-field v-model="formData.returnQuantity" label="退还数量" type="number" :min="1" />
|
||||||
|
<van-field v-model="formData.returnRemark" label="退货备注" type="textarea" rows="2" autosize />
|
||||||
</van-cell-group>
|
</van-cell-group>
|
||||||
|
|
||||||
<van-cell-group class="upload-section">
|
<van-cell-group class="upload-section">
|
||||||
|
|
|
@ -75,6 +75,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 title="柜机管理" is-link @click="router.push('/cabinet')" v-if="wxStore.isCabinetAdmin"/>
|
||||||
|
<van-cell title="审批中心" is-link @click="router.push('/approval/list')" v-if="wxStore.isCabinetAdmin"/>
|
||||||
</van-cell-group>
|
</van-cell-group>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -87,6 +87,25 @@ export const routes: RouteRecordRaw[] = [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/approval/list',
|
||||||
|
component: () => import('@/pages/approval/list.vue'),
|
||||||
|
name: "Approval",
|
||||||
|
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"),
|
||||||
|
|
|
@ -17,12 +17,16 @@ declare module 'vue' {
|
||||||
VanConfigProvider: typeof import('vant/es')['ConfigProvider']
|
VanConfigProvider: typeof import('vant/es')['ConfigProvider']
|
||||||
VanDivider: typeof import('vant/es')['Divider']
|
VanDivider: typeof import('vant/es')['Divider']
|
||||||
VanField: typeof import('vant/es')['Field']
|
VanField: typeof import('vant/es')['Field']
|
||||||
|
VanForm: typeof import('vant/es')['Form']
|
||||||
VanGrid: typeof import('vant/es')['Grid']
|
VanGrid: typeof import('vant/es')['Grid']
|
||||||
VanGridItem: typeof import('vant/es')['GridItem']
|
VanGridItem: typeof import('vant/es')['GridItem']
|
||||||
VanIcon: typeof import('vant/es')['Icon']
|
VanIcon: typeof import('vant/es')['Icon']
|
||||||
VanImage: typeof import('vant/es')['Image']
|
VanImage: typeof import('vant/es')['Image']
|
||||||
|
VanList: typeof import('vant/es')['List']
|
||||||
VanLoading: typeof import('vant/es')['Loading']
|
VanLoading: typeof import('vant/es')['Loading']
|
||||||
VanNavBar: typeof import('vant/es')['NavBar']
|
VanNavBar: typeof import('vant/es')['NavBar']
|
||||||
|
VanPicker: typeof import('vant/es')['Picker']
|
||||||
|
VanPopup: typeof import('vant/es')['Popup']
|
||||||
VanRadio: typeof import('vant/es')['Radio']
|
VanRadio: typeof import('vant/es')['Radio']
|
||||||
VanRadioGroup: typeof import('vant/es')['RadioGroup']
|
VanRadioGroup: typeof import('vant/es')['RadioGroup']
|
||||||
VanSidebar: typeof import('vant/es')['Sidebar']
|
VanSidebar: typeof import('vant/es')['Sidebar']
|
||||||
|
|
Loading…
Reference in New Issue