feat(approval): 添加耗材核销功能及相关页面和接口
- 新增耗材核销页面及路由配置 - 添加核销码验证接口和功能 - 修改审批相关类型定义和接口 - 优化审批处理页面样式和交互
This commit is contained in:
parent
9ff0984fe1
commit
fcee668ebe
|
@ -1,5 +1,5 @@
|
|||
import { request } from '@/http/axios'
|
||||
import { SubmitApprovalRequestData, SubmitApprovalResponseData, SearchApiReturnApprovalQuery, ApiResponsePageData, ReturnApprovalEntity, HandleApprovalRequestData } from './type'
|
||||
import { SubmitApprovalRequestData, SubmitApprovalResponseData, SearchApiReturnApprovalQuery, ApiResponsePageData, ReturnApprovalEntity, HandleApprovalRequestData, SearchReturnApprovalAssetQuery, ReturnApprovalAssetDTO } from './type'
|
||||
import { ShopOrderGoodsEntity } from '../shop/type'
|
||||
|
||||
export const getApprovalListApi = (params: SearchApiReturnApprovalQuery) => {
|
||||
|
@ -10,6 +10,27 @@ export const getApprovalListApi = (params: SearchApiReturnApprovalQuery) => {
|
|||
})
|
||||
}
|
||||
|
||||
export const getApprovalAssetListApi = (params: SearchReturnApprovalAssetQuery) => {
|
||||
return request<ApiResponsePageData<ReturnApprovalAssetDTO>>({
|
||||
url: 'approval/list/asset',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const checkApprovalCodeApi = (params: {
|
||||
corpid: string,
|
||||
approvalType: number,
|
||||
code: string
|
||||
}) => {
|
||||
return request<ApiResponseMsgData<string>>(
|
||||
{
|
||||
url: 'approval/checkCode',
|
||||
method: 'post',
|
||||
params
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export const submitApprovalApi = (data: SubmitApprovalRequestData) => {
|
||||
|
|
|
@ -55,9 +55,17 @@ export interface ReturnApprovalEntity {
|
|||
externalGoodsId?: number
|
||||
/** 外部归属类型的审批ID */
|
||||
externalApprovalId?: number
|
||||
/** 审批码 */
|
||||
code?: string
|
||||
/** 审批码校验状态(0未核销 1已核销) */
|
||||
codeCheck?: number
|
||||
/** 企业微信id */
|
||||
corpid?: string
|
||||
/** 申请人企业UserID */
|
||||
applyUserid?: string
|
||||
/** 申请人姓名 */
|
||||
applyUserName?: string
|
||||
/** 审批人企业UserID */
|
||||
auditUserid?: string
|
||||
/** 申请数量 */
|
||||
applyQuantity?: number
|
||||
|
@ -79,6 +87,8 @@ export interface ReturnApprovalEntity {
|
|||
returnRemark: string
|
||||
/** 审核说明 */
|
||||
auditRemark: string
|
||||
/** 审批人姓名 */
|
||||
auditName: string
|
||||
/** 审批状态(1待审核 2已通过 3已驳回) */
|
||||
status: number
|
||||
/** 审批时间 */
|
||||
|
@ -89,20 +99,50 @@ export interface ReturnApprovalEntity {
|
|||
goodsName: string
|
||||
/** 封面图URL */
|
||||
coverImg: string
|
||||
/** 手机号 */
|
||||
/** 手机号码 */
|
||||
mobile: string
|
||||
/** 用户id */
|
||||
/** 企业微信用户ID或汇邦云用户ID */
|
||||
userid: string
|
||||
/** 用户姓名 */
|
||||
name: string
|
||||
/** 是否内部用户(0否 1汇邦云用户 2企业微信用户) */
|
||||
isInternal: number
|
||||
/** 审核人姓名 */
|
||||
auditName: string
|
||||
/** 支付方式 */
|
||||
paymentMethod?: string
|
||||
}
|
||||
|
||||
export interface ApprovalGoodsEntity {
|
||||
approvalGoodsId: number;
|
||||
approvalId: number;
|
||||
goodsName: string;
|
||||
goodsId: number;
|
||||
externalGoodsId?: number;
|
||||
corpid?: string;
|
||||
belongType: number;
|
||||
price: number;
|
||||
applyQuantity: number;
|
||||
coverImg?: string;
|
||||
}
|
||||
|
||||
export interface ReturnApprovalAssetDTO extends ReturnApprovalEntity {
|
||||
goodsList?: ApprovalGoodsEntity[];
|
||||
}
|
||||
|
||||
export interface SearchReturnApprovalAssetQuery {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
approvalId?: number;
|
||||
orderId?: number;
|
||||
goodsId?: number;
|
||||
status?: number;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
approvalType?: number;
|
||||
corpid?: string;
|
||||
code?: string;
|
||||
codeCheck?: number;
|
||||
}
|
||||
|
||||
export type SubmitApprovalResponseData = ApiResponseMsgData<{
|
||||
approvalId: number
|
||||
status: number
|
||||
|
|
|
@ -6,6 +6,7 @@ const tabbarItemList = computed(() => {
|
|||
return routes.filter(route => route.meta.layout?.tabbar?.showTabbar
|
||||
&& route.path !== '/cabinet'
|
||||
&& route.path!== '/approval/list'
|
||||
&& route.path!== '/approvalAsset/list'
|
||||
)
|
||||
.map(route => ({
|
||||
title: route.meta.title,
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
import { ref } from 'vue'
|
||||
import { showConfirmDialog, showSuccessToast, showFailToast, showToast, UploaderFileListItem, Popup, Picker } from 'vant'
|
||||
import axios from "axios"
|
||||
import { getApprovalOrderGoodsApi, handleApprovalApi } from '@/common/apis/approval'
|
||||
import { handleApprovalApi } from '@/common/apis/approval'
|
||||
import { openCabinetApi } from '@/common/apis/shop'
|
||||
import type { HandleApprovalRequestData } from '@/common/apis/approval/type'
|
||||
import type { ApprovalGoodsEntity, HandleApprovalRequestData } from '@/common/apis/approval/type'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useApprovalStore } from '@/pinia/stores/approval'
|
||||
import { useWxStore } from '@/pinia/stores/wx';
|
||||
|
@ -32,7 +32,7 @@ const formData = ref<HandleApprovalRequestData>({
|
|||
auditUserid: wxStore.userid
|
||||
})
|
||||
|
||||
const orderGoods = ref<ShopOrderGoodsEntity[]>([]);
|
||||
const orderGoods = ref<ApprovalGoodsEntity[]>([]);
|
||||
const submitting = ref(false)
|
||||
const fileList = ref<UploaderFileListItem[]>([])
|
||||
const uploading = ref(false)
|
||||
|
@ -156,9 +156,7 @@ onMounted(() => {
|
|||
return;
|
||||
}
|
||||
|
||||
getApprovalOrderGoodsApi(approvalStore.currentApproval.approvalId).then(({ data }) => {
|
||||
orderGoods.value = data;
|
||||
})
|
||||
orderGoods.value = approvalStore.currentApproval.goodsList || [];
|
||||
|
||||
if (approvalStore.currentApproval.status !== 1) {
|
||||
// 填充历史审批数据
|
||||
|
@ -267,9 +265,8 @@ const handleReject = () => {
|
|||
<van-nav-bar title="审批处理" left-text="返回" left-arrow fixed @click-left="() => router.go(-1)" />
|
||||
|
||||
<div class="content-wrapper">
|
||||
<van-cell-group >
|
||||
<van-cell title="申请人" :value="approvalStore.currentApproval?.name" />
|
||||
<van-cell title="手机号" :value="approvalStore.currentApproval?.mobile" />
|
||||
<van-cell-group>
|
||||
<van-cell title="申请人" :value="approvalStore.currentApproval?.applyUserName" />
|
||||
<van-cell title="当前状态" :value="statusMap[approvalStore.currentApproval?.status || 1]" />
|
||||
<!-- <van-cell title="领用说明" :value="approvalStore.currentApproval?.applyRemark" /> -->
|
||||
</van-cell-group>
|
||||
|
@ -290,7 +287,7 @@ const handleReject = () => {
|
|||
¥{{ item.price.toFixed(2) }}
|
||||
</span>
|
||||
<span class="quantity">
|
||||
×{{ item.quantity }}
|
||||
×{{ item.applyQuantity }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -300,10 +297,10 @@ const handleReject = () => {
|
|||
|
||||
<van-cell-group>
|
||||
<!-- 原有表单字段保持不变 -->
|
||||
<van-button type="primary" size="small"
|
||||
@click="handleOpenCabinet" :disabled="isButtonDisabled" style="margin-bottom: 12px">
|
||||
<!-- <van-button type="primary" size="small" @click="handleOpenCabinet" :disabled="isButtonDisabled"
|
||||
style="margin-bottom: 12px">
|
||||
打开柜子
|
||||
</van-button>
|
||||
</van-button> -->
|
||||
<van-field v-model="formData.auditRemark" label="审核说明" type="textarea" rows="2" autosize
|
||||
class="audit-remark-field" />
|
||||
</van-cell-group>
|
||||
|
@ -315,11 +312,13 @@ const handleReject = () => {
|
|||
</div>
|
||||
</van-popup>
|
||||
|
||||
<div class="submit-bar" v-if="approvalStore.currentApproval?.status !== 2">
|
||||
<van-button type="danger" block :loading="submitting" loading-text="提交中..." @click="handleReject">
|
||||
<div class="btn-group" v-if="approvalStore.currentApproval?.status !== 2">
|
||||
<van-button type="danger" :loading="submitting" loading-text="提交中..." @click="handleReject"
|
||||
style="flex: 1; height: 44px; border-radius: 8px; font-size: 16px;">
|
||||
拒绝
|
||||
</van-button>
|
||||
<van-button type="primary" block :loading="submitting" loading-text="提交中..." @click="handleComfirm">
|
||||
<van-button type="primary" :loading="submitting" loading-text="提交中..." @click="handleComfirm"
|
||||
style="flex: 1; height: 44px; border-radius: 8px; font-size: 16px;">
|
||||
同意
|
||||
</van-button>
|
||||
</div>
|
||||
|
@ -335,44 +334,44 @@ const handleReject = () => {
|
|||
.content-wrapper {
|
||||
padding-top: 46px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.product-list {
|
||||
.product-list {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.product-item {
|
||||
.product-item {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.product-image {
|
||||
.product-image {
|
||||
margin-right: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.product-info {
|
||||
.product-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.price-row {
|
||||
.price-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.product-price {
|
||||
.product-price {
|
||||
color: #e95d5d;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.quantity {
|
||||
.quantity {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
margin: 20px 0;
|
||||
|
@ -401,15 +400,13 @@ const handleReject = () => {
|
|||
}
|
||||
}
|
||||
|
||||
.submit-bar {
|
||||
position: sticky;
|
||||
bottom: 20px;
|
||||
.btn-group {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
margin: 8px 0;
|
||||
background-color: #ffffff;
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
margin: 0 16px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
|
|
|
@ -2,12 +2,6 @@
|
|||
<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 v-model="statusText" placeholder="请选择状态"
|
||||
@click="showStatusPicker = true" />
|
||||
<van-popup v-model:show="showStatusPicker" position="bottom">
|
||||
|
@ -31,18 +25,26 @@
|
|||
/>
|
||||
</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 class="btn-group">
|
||||
<van-button type="primary" native-type="submit" style="flex: 1; height: 44px; border-radius: 8px; font-size: 16px;">搜索</van-button>
|
||||
<van-button type="default" @click="showVerificationDialog = true" style="flex: 1; height: 44px; border-radius: 8px; font-size: 16px;">输入核销码</van-button>
|
||||
</div>
|
||||
|
||||
</van-form>
|
||||
|
||||
<van-dialog v-model:show="showVerificationDialog" title="输入核销码" @confirm="handleVerification">
|
||||
<van-field v-model="verificationCode" placeholder="请输入核销码" />
|
||||
</van-dialog>
|
||||
|
||||
<!-- 审批列表 -->
|
||||
<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}`" clickable
|
||||
@click="handleCellClick(item.approvalId)">
|
||||
<template #label>
|
||||
<div>商品名称:{{ item.goodsName }}</div>
|
||||
<div v-for="goods in item.goodsList" :key="goods.approvalGoodsId">
|
||||
商品名称:{{ goods.goodsName }}
|
||||
</div>
|
||||
<div>核销码:{{ item.code }}</div>
|
||||
<div>申请时间:{{ item.createTime }}</div>
|
||||
<van-tag :type="statusTagType(item.status)">
|
||||
{{ statusMap[item.status] }}
|
||||
|
@ -55,8 +57,9 @@
|
|||
|
||||
<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 { showToast } from 'vant'
|
||||
import { checkApprovalCodeApi, getApprovalAssetListApi } from '@/common/apis/approval'
|
||||
import type { SearchReturnApprovalAssetQuery, ReturnApprovalAssetDTO } from '@/common/apis/approval/type'
|
||||
import type { PickerConfirmEventParams } from 'vant/es';
|
||||
import { useApprovalStore } from '@/pinia/stores/approval';
|
||||
import { useWxStore } from '@/pinia/stores/wx';
|
||||
|
@ -65,9 +68,11 @@ const router = useRouter();
|
|||
const wxStore = useWxStore();
|
||||
|
||||
// 搜索参数
|
||||
const searchParams = reactive<SearchApiReturnApprovalQuery>({
|
||||
const searchParams = reactive<SearchReturnApprovalAssetQuery>({
|
||||
corpid: wxStore.corpid,
|
||||
approvalType: 1,
|
||||
code: '',
|
||||
codeCheck: 1,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
})
|
||||
|
@ -93,8 +98,12 @@ const statusText = ref('')
|
|||
const showDatePicker = ref(false)
|
||||
const dateRangeText = ref('')
|
||||
|
||||
// 核销码相关
|
||||
const showVerificationDialog = ref(false)
|
||||
const verificationCode = ref('')
|
||||
|
||||
// 列表相关
|
||||
const list = ref<ReturnApprovalEntity[]>([])
|
||||
const list = ref<ReturnApprovalAssetDTO[]>([])
|
||||
const loading = ref(false)
|
||||
const finished = ref(false)
|
||||
|
||||
|
@ -107,6 +116,30 @@ const handleCellClick = (approvalId: number) => {
|
|||
router.push(`/approval/handleApply/${approvalId}`);
|
||||
}
|
||||
|
||||
// 处理核销码提交
|
||||
const handleVerification = async () => {
|
||||
if (!verificationCode.value) {
|
||||
showToast('请输入核销码')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const params = {
|
||||
corpid: wxStore.corpid,
|
||||
approvalType: 1,
|
||||
code: verificationCode.value
|
||||
}
|
||||
await checkApprovalCodeApi(params)
|
||||
showToast('核销成功')
|
||||
showVerificationDialog.value = false
|
||||
verificationCode.value = ''
|
||||
// 刷新列表
|
||||
handleSearch()
|
||||
} catch (error) {
|
||||
console.error('核销失败', error)
|
||||
showToast('核销失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 状态标签类型
|
||||
const statusTagType = (status: number) => {
|
||||
switch (status) {
|
||||
|
@ -163,7 +196,7 @@ const handleReset = () => {
|
|||
const onLoad = async () => {
|
||||
try {
|
||||
searchParams.corpid = wxStore.corpid;
|
||||
const { data } = await getApprovalListApi(searchParams)
|
||||
const { data } = await getApprovalAssetListApi(searchParams)
|
||||
list.value.push(...data.rows)
|
||||
loading.value = false
|
||||
|
||||
|
@ -194,4 +227,14 @@ const onLoad = async () => {
|
|||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
margin: 8px 0;
|
||||
background-color: #ffffff;
|
||||
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
|
||||
padding: 16px;
|
||||
}
|
||||
</style>
|
|
@ -116,6 +116,11 @@ const filteredGoods = computed(() => {
|
|||
return goodsList.value.filter(goods =>
|
||||
goods.goodsName.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
);
|
||||
/* return goodsList.value.filter(goods => {
|
||||
const matchesSearch = !searchQuery.value || goods.goodsName.toLowerCase().includes(searchQuery.value.toLowerCase());
|
||||
const remainingStock = (goods.stock || 0) - (goods.totalStock || 0);
|
||||
return matchesSearch && remainingStock > 0;
|
||||
}); */
|
||||
});
|
||||
|
||||
// 获取商品列表
|
||||
|
|
|
@ -108,6 +108,12 @@ wxStore.refreshBalance();
|
|||
<span>审批中心</span>
|
||||
</div>
|
||||
</van-col>
|
||||
<van-col span="8">
|
||||
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/approvalAsset/list')">
|
||||
<van-icon name="comment-o" size="20px" />
|
||||
<span>耗材核销</span>
|
||||
</div>
|
||||
</van-col>
|
||||
</van-row>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { ReturnApprovalEntity } from '@/common/apis/approval/type'
|
||||
import type { ReturnApprovalAssetDTO } from '@/common/apis/approval/type'
|
||||
|
||||
export interface ApprovalDetail extends ReturnApprovalEntity {
|
||||
export interface ApprovalDetail extends ReturnApprovalAssetDTO {
|
||||
goodsName: string
|
||||
coverImg: string
|
||||
}
|
||||
|
|
|
@ -116,6 +116,25 @@ export const routes: RouteRecordRaw[] = [
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/approvalAsset/list',
|
||||
component: () => import('@/pages/approvalAsset/list.vue'),
|
||||
name: "ApprovalAsset",
|
||||
meta: {
|
||||
title: '耗材核销',
|
||||
keepAlive: false,
|
||||
layout: {
|
||||
navBar: {
|
||||
showNavBar: false,
|
||||
showLeftArrow: false
|
||||
},
|
||||
tabbar: {
|
||||
showTabbar: true,
|
||||
icon: "home-o"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/* {
|
||||
path: '/manage/goods',
|
||||
component: () => import('@/pages/manage/goods/goodsList.vue'),
|
||||
|
|
|
@ -16,6 +16,7 @@ declare module 'vue' {
|
|||
VanCellGroup: typeof import('vant/es')['CellGroup']
|
||||
VanCol: typeof import('vant/es')['Col']
|
||||
VanConfigProvider: typeof import('vant/es')['ConfigProvider']
|
||||
VanDialog: typeof import('vant/es')['Dialog']
|
||||
VanDivider: typeof import('vant/es')['Divider']
|
||||
VanEmpty: typeof import('vant/es')['Empty']
|
||||
VanField: typeof import('vant/es')['Field']
|
||||
|
|
Loading…
Reference in New Issue