Compare commits

..

No commits in common. "df72e1317c90a08ddab37db78a05f783c60581c4" and "9ff0984fe19867686d11ebe84a9f4870875a258c" have entirely different histories.

18 changed files with 250 additions and 643 deletions

View File

@ -1,6 +1,6 @@
import { request } from '@/http/axios'
import { SubmitApprovalRequestData, SubmitApprovalResponseData, SearchApiReturnApprovalQuery, ApiResponsePageData, ReturnApprovalEntity, HandleApprovalRequestData, SearchReturnApprovalAssetQuery, ReturnApprovalAssetDTO, HandleApprovalAssetRequestData, ApprovalGoodsCellEntity, ReturnApprovalDetailDTO } from './type'
import { OpenCabinetApiData, ShopOrderGoodsEntity } from '../shop/type'
import { SubmitApprovalRequestData, SubmitApprovalResponseData, SearchApiReturnApprovalQuery, ApiResponsePageData, ReturnApprovalEntity, HandleApprovalRequestData } from './type'
import { ShopOrderGoodsEntity } from '../shop/type'
export const getApprovalListApi = (params: SearchApiReturnApprovalQuery) => {
return request<ApiResponsePageData<ReturnApprovalEntity>>({
@ -10,27 +10,6 @@ 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) => {
@ -49,23 +28,6 @@ export const handleApprovalApi = (data: HandleApprovalRequestData) => {
})
}
export const handleApprovalAssetApi = (data: HandleApprovalRequestData) => {
return request<ApiResponseMsgData<string>>({
url: 'approval/handle/asset',
method: 'post',
data
})
}
export const allocateApprovalGoods = (data: HandleApprovalAssetRequestData) => {
return request<ApiResponseMsgData<string>>({
url: 'approval/handle/allocateApprovalGoods',
method: 'post',
data
})
}
export const getApprovalOrderGoodsApi = (approvalId: number) => {
return request<ApiResponseMsgData<ShopOrderGoodsEntity[]>>({
url: 'approval/getApprovalOrderGoods',
@ -73,32 +35,3 @@ export const getApprovalOrderGoodsApi = (approvalId: number) => {
params: { approvalId }
})
}
export const getApprovalGoodsCellApi = (approvalId: number) => {
return request<ApiResponseMsgData<ApprovalGoodsCellEntity[]>>(
{
url: 'approval/getApprovalGoodsCell',
method: 'get',
params: { approvalId }
}
)
}
export const getApprovalDetailAssetApi = (approvalId: number) => {
return request<ApiResponseMsgData<ReturnApprovalDetailDTO>>(
{
url: 'approval/detail/asset',
method: 'get',
params: { approval_id: approvalId }
}
)
}
/** 打开储物柜接口 */
export function openCabinetApi(approvalGoodsCellId: number, data: OpenCabinetApiData) {
return request<ApiResponseData<void>>({
url: `approval/openCabinet/${approvalGoodsCellId}`,
method: "post",
data
})
}

View File

@ -20,11 +20,6 @@ export interface HandleApprovalRequestData {
auditUserid: string
}
export interface HandleApprovalAssetRequestData extends HandleApprovalRequestData {
/** 审批商品ID */
approvalGoodsList: ApprovalGoodsEntity[]
}
export interface SearchApiReturnApprovalQuery {
pageNum: number
pageSize: number
@ -60,17 +55,9 @@ 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
@ -92,9 +79,7 @@ export interface ReturnApprovalEntity {
returnRemark: string
/** 审核说明 */
auditRemark: string
/** 审批人姓名 */
auditName: string
/** 审批状态(1待审核 2已通过 3已驳回 4开柜中) */
/** 审批状态(1待审核 2已通过 3已驳回) */
status: number
/** 审批时间 */
approvalTime?: string
@ -104,81 +89,20 @@ 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;
approvalQuantity?: number;
coverImg?: string;
}
export interface ReturnApprovalAssetDTO extends ReturnApprovalEntity {
goodsList?: ApprovalGoodsEntity[];
}
export interface ReturnApprovalDetailDTO extends ReturnApprovalAssetDTO {
statusStr: string;
approvalGoodsCellList?: ApprovalGoodsCellEntity[];
}
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;
handleStatus?: number;
searchStr?: string;
}
export interface ApprovalGoodsCellEntity {
/** 主键ID */
approvalGoodsCellId: number;
/** 审批ID */
approvalId: number;
/** 申请领用商品ID */
approvalGoodsId: number;
/** 商店ID */
shopId: number;
/** 柜机ID */
cabinetId: number;
/** 格口ID */
cellId: number;
/** 分配数量 */
allocateQuantity: number;
/** 商店名称 */
shopName: string;
/** 柜机名称 */
cabinetName: string;
/** 格口号 */
cellNo: number;
}
export type SubmitApprovalResponseData = ApiResponseMsgData<{
approvalId: number
status: number

View File

@ -50,18 +50,9 @@ export function qyLogin(params: QyLoginRequestParams) {
}
/** 根据openid获取用户订单信息 */
export function getOrdersByOpenIdApi(corpid: string, openid: string) {
export function getOrdersByOpenIdApi(openid: string) {
return request<ApiResponseData<GetOrdersByOpenIdDTO>>({
url: `order/user/${openid}`,
method: "get",
params: { corpid }
})
}
/** 根据openid获取用户订单信息 */
export function getOrdersByQyUserIdApi(qyUserId: number) {
return request<ApiResponseData<GetOrdersByOpenIdDTO>>({
url: `order/user/qy/${qyUserId}`,
method: "get"
})
}

View File

@ -44,7 +44,7 @@ function createInstance() {
return logout()
default:
// 不是正确的 code
return Promise.reject(new Error(apiData.msg || apiData.message || "Error"))
return Promise.reject(new Error(apiData.message || "Error"))
}
},
(error) => {

View File

@ -6,7 +6,6 @@ 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,

View File

@ -1,16 +1,16 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { showConfirmDialog, showDialog, UploaderFileListItem, Popup, Picker, Field, Stepper } from 'vant'
import { ref } from 'vue'
import { showConfirmDialog, showSuccessToast, showFailToast, showToast, UploaderFileListItem, Popup, Picker } from 'vant'
import axios from "axios"
import { allocateApprovalGoods, getApprovalGoodsCellApi, handleApprovalApi, handleApprovalAssetApi, openCabinetApi } from '@/common/apis/approval'
import type { ApprovalGoodsCellEntity, ApprovalGoodsEntity, HandleApprovalAssetRequestData, HandleApprovalRequestData, ReturnApprovalDetailDTO } from '@/common/apis/approval/type'
import { getApprovalOrderGoodsApi, handleApprovalApi } from '@/common/apis/approval'
import { openCabinetApi } from '@/common/apis/shop'
import type { HandleApprovalRequestData } from '@/common/apis/approval/type'
import { useRoute, useRouter } from 'vue-router'
import { useApprovalStore } from '@/pinia/stores/approval'
import { useWxStore } from '@/pinia/stores/wx';
import { getApprovalDetailAssetApi } from '@/common/apis/approval';
import Compressor from 'compressorjs';
import { useAb98UserStore } from '@/pinia/stores/ab98-user';
import { ShopOrderGoodsEntity } from '@/common/apis/shop/type';
import { useAb98UserStore } from '@/pinia/stores/ab98-user'
import { ShopOrderGoodsEntity } from '@/common/apis/shop/type'
const { VITE_APP_BASE_API } = import.meta.env;
const router = useRouter()
@ -21,9 +21,7 @@ const { openid, balance, corpidLogin, userid: qyUserid, name: qyName } = storeTo
const ab98UserStore = useAb98UserStore();
const { tel, userid: ab98Userid, name } = storeToRefs(ab98UserStore);
const detailData = ref<ReturnApprovalDetailDTO>();
const formData = ref<HandleApprovalAssetRequestData>({
const formData = ref<HandleApprovalRequestData>({
approvalId: approvalStore.currentApproval?.approvalId || 0,
status: 2,
returnAmount: approvalStore.currentApproval?.goodsPrice || 0,
@ -31,11 +29,10 @@ const formData = ref<HandleApprovalAssetRequestData>({
auditRemark: '',
userid: wxStore.userid,
corpid: wxStore.corpid,
auditUserid: wxStore.userid,
approvalGoodsList: []
auditUserid: wxStore.userid
})
const orderGoods = ref<ApprovalGoodsEntity[]>([]);
const orderGoods = ref<ShopOrderGoodsEntity[]>([]);
const submitting = ref(false)
const fileList = ref<UploaderFileListItem[]>([])
const uploading = ref(false)
@ -64,23 +61,72 @@ const validateForm = () => {
showConfirmDialog({ title: '提示', message: '驳回时必须填写审核说明' })
return false
}
//
if (detailData.value?.status === 1) {
for (const item of orderGoods.value) {
if (item.approvalQuantity != null && item.approvalQuantity < 0) {
showConfirmDialog({ title: '提示', message: `商品${item.goodsName}的审批数量不能为负数` })
return false
}
if (item.approvalQuantity != null && item.approvalQuantity > item.applyQuantity) {
showConfirmDialog({ title: '提示', message: `商品${item.goodsName}的审批数量不能超过申请数量` })
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
let compressedFile = file;
try {
compressedFile = await new Promise<File>((resolve, reject) => {
new Compressor(file, {
quality: 0.8,
maxWidth: 1280,
maxHeight: 1280,
success(result) {
resolve(new File([result], file.name, {
type: 'image/jpeg',
lastModified: Date.now()
}))
},
error(err) {
reject(err)
}
})
})
} catch (error) {
console.error('压缩失败:', error)
}
const formData = new FormData()
formData.append('file', compressedFile)
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
})
formData.value.auditImages = fileList.value.map(item => item.url).join(',')
} catch (error) {
showConfirmDialog({
title: '上传失败',
message: error instanceof Error ? error.message : '未知错误'
})
} finally {
uploading.value = false
}
}
const onStatusConfirm = ({ selectedOptions }: { selectedOptions: { value: number }[] }) => {
submitting.value = true
@ -104,89 +150,70 @@ const previewImage = (url: string) => {
currentPreviewImage.value = url
showPreview.value = true
}
const approvalGoodsCells = ref<ApprovalGoodsCellEntity[]>([]);
const approvalGoodsCellsWithNames = computed(() => {
return approvalGoodsCells.value.map(cell => {
const goods = orderGoods.value.find(g => g.approvalGoodsId === cell.approvalGoodsId);
return {
...cell,
goodsName: goods ? goods.goodsName : ''
};
});
});
const fetchApprovalDetail = async () => {
const approvalId = approvalStore.currentApproval?.approvalId
if (!approvalId) {
router.push('/approvalAsset/list');
onMounted(() => {
if (!approvalStore.currentApproval) {
router.push('/approval/list');
return;
}
getApprovalOrderGoodsApi(approvalStore.currentApproval.approvalId).then(({ data }) => {
orderGoods.value = data;
})
try {
const result = await getApprovalDetailAssetApi(approvalId);
if (result.code === 0) {
detailData.value = result.data;
orderGoods.value = (result.data.goodsList || []).map(item => ({
...item,
approvalQuantity: result.data.status === 1 ? item.applyQuantity : item.approvalQuantity
}));
if (result.data.status !== 1) {
formData.value = {
...formData.value,
corpid: wxStore.corpid,
status: result.data.status,
returnAmount: result.data.returnAmount,
auditRemark: result.data.auditRemark,
auditImages: result.data.auditImages
};
approvalGoodsCells.value = result.data.approvalGoodsCellList || [];
}
} else {
showDialog({ title: '错误', message: result.msg || '获取审批详情失败' });
if (approvalStore.currentApproval.status !== 1) {
//
formData.value = {
...formData.value,
corpid: wxStore.corpid,
status: approvalStore.currentApproval.status,
returnAmount: approvalStore.currentApproval.returnAmount,
auditRemark: approvalStore.currentApproval.auditRemark,
auditImages: approvalStore.currentApproval.auditImages
}
} catch (error) {
console.error('获取审批详情失败:', error);
showDialog({ title: '错误', message: '获取审批详情失败' });
}
}
onMounted(async () => {
await fetchApprovalDetail();
//
if (approvalStore.currentApproval.auditImages) {
fileList.value = approvalStore.currentApproval.auditImages.split(',').map(url => ({
url,
status: 'done',
message: '已上传'
}))
}
}
})
const handleOpenCell = async (approvalGoodsCellId: number) => {
if (!approvalGoodsCellId) {
showDialog({ title: '错误', message: '缺少格口信息' });
return;
const handleOpenCabinet = async () => {
if (!approvalStore.currentApproval?.orderId || !approvalStore.currentApproval?.orderGoodsId) {
showFailToast('缺少订单信息')
return
}
isButtonDisabled.value = true;
isButtonDisabled.value = true
try {
const isInternal = corpidLogin.value ? 2 : ab98Userid.value ? 1 : 0;
const data = {
userid: qyUserid.value,
isInternal: isInternal,
name: isInternal === 2 ? qyName.value : name.value,
mobile: tel.value,
operationType: 2
};
const result = await openCabinetApi(approvalGoodsCellId, data);
const result = await openCabinetApi(
approvalStore.currentApproval.orderId,
approvalStore.currentApproval.orderGoodsId, {
userid: isInternal === 2 ? qyUserid.value : ab98Userid.value,
isInternal: isInternal,
name: isInternal === 2 ? qyName.value : name.value,
mobile: tel.value,
operationType: 2
}
)
if (result.code !== 0) {
showDialog({ title: '错误', message: result.msg || '开启格口失败' });
return;
showFailToast(result.msg || '开启失败')
return
}
showDialog({ message: '格口已开启' });
showSuccessToast('柜口已开启')
} catch (error) {
showDialog({ title: '错误', message: '请求失败' });
showFailToast('请求失败')
} finally {
setTimeout(() => {
isButtonDisabled.value = false;
}, 5000);
isButtonDisabled.value = false
}, 5000)
}
};
}
const handleSubmit = async () => {
if (!validateForm()) return
@ -196,16 +223,15 @@ const handleSubmit = async () => {
formData.value.userid = wxStore.userid;
formData.value.corpid = wxStore.corpid;
formData.value.auditUserid = wxStore.userid;
const { code, msg } = await handleApprovalAssetApi(formData.value)
const { code, msg } = await handleApprovalApi(formData.value)
if (code === 0) {
showDialog({ message: '操作成功' })
showSuccessToast('操作成功')
await showConfirmDialog({
title: "操作成功",
message: `审批处理已完成`
})
router.push('/approvalAsset/list')
router.push('/approval/list')
} else {
console.error('操作失败code:', code, 'msg:', msg)
showConfirmDialog({
@ -229,53 +255,6 @@ const handleComfirm = () => {
handleSubmit();
}
const handleAllocateQuantity = async () => {
if (!validateForm()) return
// approvalGoodsList
formData.value.approvalGoodsList = orderGoods.value.map(item => ({
approvalGoodsId: item.approvalGoodsId || 0,
approvalId: formData.value.approvalId,
goodsId: item.goodsId,
goodsName: item.goodsName,
externalGoodsId: item.externalGoodsId,
corpid: item.corpid,
belongType: item.belongType,
price: item.price,
applyQuantity: item.applyQuantity,
approvalQuantity: item.approvalQuantity,
coverImg: item.coverImg
}))
submitting.value = true
try {
formData.value.userid = wxStore.userid;
formData.value.corpid = wxStore.corpid;
formData.value.auditUserid = wxStore.userid;
const { code, msg } = await allocateApprovalGoods(formData.value)
if (code === 0) {
showDialog({ message: '数量确定成功' })
await fetchApprovalDetail();
} else {
console.error('操作失败code:', code, 'msg:', msg)
showConfirmDialog({
title: '操作失败',
message: msg || '数量确定失败'
})
}
} catch (error) {
console.error('提交失败:', error)
showConfirmDialog({
title: '提交失败',
message: error instanceof Error ? error.message : '网络请求异常'
})
} finally {
submitting.value = false
}
}
const handleReject = () => {
formData.value.status = 3;
handleSubmit();
@ -288,9 +267,11 @@ 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="detailData?.applyUserName" />
<van-cell title="当前状态" :value="statusMap[detailData?.status || 1]" />
<van-cell-group >
<van-cell title="申请人" :value="approvalStore.currentApproval?.name" />
<van-cell title="手机号" :value="approvalStore.currentApproval?.mobile" />
<van-cell title="当前状态" :value="statusMap[approvalStore.currentApproval?.status || 1]" />
<!-- <van-cell title="领用说明" :value="approvalStore.currentApproval?.applyRemark" /> -->
</van-cell-group>
<van-cell-group class="product-list">
@ -309,54 +290,9 @@ const handleReject = () => {
¥{{ item.price.toFixed(2) }}
</span>
<span class="quantity">
申请数量{{ item.applyQuantity }}
×{{ item.quantity }}
</span>
</div>
<!-- 审批数量输入框 -->
<div v-if="detailData?.status === 1" class="approval-quantity">
<span class="label">审批数量:</span>
<van-stepper v-model.number="item.approvalQuantity" :min="0" :max="item.applyQuantity"
integer style="width: 180px;" />
</div>
<!-- 查看审批数量 -->
<div v-if="detailData?.status === 2 || detailData?.status === 4"
class="approval-quantity-view">
<span class="label">审批数量:</span>
<span class="value">{{ item.approvalQuantity }}</span>
</div>
</div>
</van-cell>
</template>
<div class="confirm-quantity-container">
<van-button class="confirm-quantity-btn" v-if="detailData?.status === 1" type="primary"
@click="handleAllocateQuantity" :loading="submitting" plain hairline size="small">
确定审批数量
</van-button>
</div>
</van-cell-group>
<van-cell-group class="approval-goods-list" v-if="approvalGoodsCells.length > 0">
<van-cell title="审批通过商品:" class="section-title" />
<template v-for="item in approvalGoodsCellsWithNames" :key="item.approvalGoodsCellId">
<van-cell class="product-item">
<div class="product-info">
<div class="product-name van-ellipsis">
{{ item.goodsName }}
</div>
<div class="van-ellipsis">
{{ item.shopName }}
</div>
<div class="van-ellipsis">
{{ item.cabinetName }} - 格口{{ item.cellNo }}
</div>
<div class="van-ellipsis">
分配数量{{ item.allocateQuantity }}
</div>
<div v-if="detailData?.status === 4" class="open-cell-btn">
<van-button type="primary" size="small"
@click="handleOpenCell(item.approvalGoodsCellId)" :disabled="isButtonDisabled">
打开格口
</van-button>
</div>
</div>
</van-cell>
</template>
@ -364,10 +300,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>
@ -379,14 +315,11 @@ const handleReject = () => {
</div>
</van-popup>
<div class="btn-group" v-if="detailData?.status === 1 || detailData?.status === 4">
<van-button type="danger" :loading="submitting" loading-text="提交中..." @click="handleReject"
style="flex: 1; height: 44px; border-radius: 8px; font-size: 16px;">
<div class="submit-bar" v-if="approvalStore.currentApproval?.status !== 2">
<van-button type="danger" block :loading="submitting" loading-text="提交中..." @click="handleReject">
拒绝
</van-button>
<van-button type="primary" :disabled="detailData?.status === 1" :loading="submitting"
loading-text="提交中..." @click="handleComfirm"
style="flex: 1; height: 44px; border-radius: 8px; font-size: 16px;">
<van-button type="primary" block :loading="submitting" loading-text="提交中..." @click="handleComfirm">
同意
</van-button>
</div>
@ -400,74 +333,46 @@ const handleReject = () => {
}
.content-wrapper {
padding-top: 46px;
padding-bottom: 100px;
}
.product-list {
margin-bottom: 20px;
}
.product-item {
align-items: flex-start;
}
.product-image {
margin-right: 12px;
border-radius: 4px;
}
.product-info {
display: flex;
flex-direction: column;
justify-content: space-between;
height: auto;
padding: 5px 0;
.product-name {
font-size: 14px;
color: #333;
font-weight: 500;
padding-top: 46px;
padding-bottom: 100px;
}
}
.approval-quantity,
.approval-quantity-view {
margin-top: 8px;
display: flex;
align-items: center;
}
.product-list {
margin-bottom: 20px;
}
.approval-quantity-view .label {
color: #666;
margin-right: 8px;
}
.product-item {
align-items: flex-start;
}
.approval-quantity-view .value {
/* font-weight: bold; */
}
.product-image {
margin-right: 12px;
border-radius: 4px;
}
.approval-quantity-view {
justify-content: flex-end;
}
.product-info {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 60px;
}
.price-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.price-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.product-price {
color: #e95d5d;
font-weight: bold;
font-size: 14px;
}
.product-price {
color: #e95d5d;
font-weight: bold;
font-size: 14px;
}
.quantity {
color: #666;
/* margin-right: 20px;
font-size: 13px; */
}
.quantity {
color: #666;
font-size: 13px;
}
.upload-section {
margin: 20px 0;
@ -496,38 +401,23 @@ const handleReject = () => {
}
}
.btn-group {
display: flex;
width: 100%;
gap: 12px;
margin: 8px 0;
background-color: #ffffff;
.submit-bar {
position: sticky;
bottom: 20px;
padding: 16px;
}
.confirm-quantity-container {
display: flex;
justify-content: flex-end;
margin-top: 8px;
}
.confirm-quantity-btn {
margin-bottom: 8px;
margin-right: 8px;
height: 36px;
width: 140px;
background: #fff;
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.1);
border-radius: 8px;
font-size: 16px;
padding: 0 20px;
margin: 0 16px;
z-index: 1;
}
.content-wrapper {
padding-bottom: 100px;
}
.audit-remark-field::v-deep .van-field__control {
background-color: #ecf3fc;
background-color: #fffbe6;
padding: 8px;
border-radius: 4px;
}
@ -547,4 +437,4 @@ const handleReject = () => {
background-color: #ebedf0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
</style>
</style>

View File

@ -126,8 +126,7 @@ const handleSubmit = async () => {
const { code, data } = await submitApprovalApi(formData.value)
if (code === 0) {
orderStore.getOrders(wxStore.corpid, wxStore.openid, wxStore.qyUserId);
orderStore.getOrders(wxStore.openid);
try {
await showConfirmDialog({
title: "提交成功",
@ -140,7 +139,7 @@ const handleSubmit = async () => {
console.error('提交失败:', error)
showConfirmDialog({
title: '提交失败',
message: error instanceof Error ? error.message : '网络请求异常'
message: error instanceof Error ? error.name + error.message + error.cause + error.stack : '网络请求异常'
})
} finally {
submitting.value = false

View File

@ -1,30 +1,48 @@
<template>
<div class="approval-list-container">
<!-- 状态切换标签页 -->
<van-tabs v-model:active="activeTab" @change="handleTabChange">
<van-tab title="待处理"></van-tab>
<van-tab title="已处理"></van-tab>
</van-tabs>
<!-- 搜索表单 -->
<van-form @submit="handleSearch">
<van-search v-model="searchParams.searchStr" placeholder="请输入搜索内容" />
<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">
<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-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"
class="approval-list">
<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 v-for="goods in item.goodsList" :key="goods.approvalGoodsId">
商品名称{{ goods.goodsName }}
</div>
<div>申领人{{ item.applyUserName }}</div>
<div>核销码{{ item.code }}</div>
<div>商品名称{{ item.goodsName }}</div>
<div>申请时间{{ item.createTime }}</div>
<van-tag :type="statusTagType(item.status)">
{{ statusMap[item.status] }}
@ -32,21 +50,13 @@
</template>
</van-cell>
</van-list>
<!-- 悬浮核销码按钮 -->
<van-button type="primary" @click="showVerificationDialog = true" class="floating-verification-btn">
<van-icon name="plus" style="font-size: 20px;" />
<!-- <span>核销码</span> -->
</van-button>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, watch, onUnmounted } from 'vue'
import { debounce } from 'lodash-es'
import { showDialog, showToast } from 'vant'
import { checkApprovalCodeApi, getApprovalAssetListApi } from '@/common/apis/approval'
import type { SearchReturnApprovalAssetQuery, ReturnApprovalAssetDTO } from '@/common/apis/approval/type'
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';
import { useApprovalStore } from '@/pinia/stores/approval';
import { useWxStore } from '@/pinia/stores/wx';
@ -55,14 +65,9 @@ const router = useRouter();
const wxStore = useWxStore();
//
const activeTab = ref(0);
const searchParams = reactive<SearchReturnApprovalAssetQuery>({
const searchParams = reactive<SearchApiReturnApprovalQuery>({
corpid: wxStore.corpid,
handleStatus: activeTab.value,
approvalType: 1,
code: '',
codeCheck: 1,
searchStr: '',
pageNum: 1,
pageSize: 10,
})
@ -79,8 +84,7 @@ const statusOptions = [
const statusMap: { [key: number]: string } = {
1: '待审核',
2: '已通过',
3: '已驳回',
4: '开柜中'
3: '已驳回'
}
const statusText = ref('')
@ -89,12 +93,8 @@ const statusText = ref('')
const showDatePicker = ref(false)
const dateRangeText = ref('')
//
const showVerificationDialog = ref(false)
const verificationCode = ref('')
//
const list = ref<ReturnApprovalAssetDTO[]>([])
const list = ref<ReturnApprovalEntity[]>([])
const loading = ref(false)
const finished = ref(false)
@ -107,30 +107,6 @@ const handleCellClick = (approvalId: number) => {
router.push(`/approval/handleApply/${approvalId}`);
}
//
const handleVerification = async () => {
if (!verificationCode.value) {
showDialog({ message: '请输入核销码' })
return
}
try {
const params = {
corpid: wxStore.corpid,
approvalType: 1,
code: verificationCode.value
}
await checkApprovalCodeApi(params)
showDialog({ message: '核销成功' })
showVerificationDialog.value = false
verificationCode.value = ''
//
handleSearch()
} catch (error) {
console.error('核销失败', error)
showDialog({ message: '核销失败' })
}
}
//
const statusTagType = (status: number) => {
switch (status) {
@ -159,12 +135,6 @@ const onDateConfirm = (values: Date[]) => {
showDatePicker.value = false
}
//
const handleTabChange = (tabIndex: number) => {
searchParams.handleStatus = tabIndex;
handleSearch();
};
//
const handleSearch = () => {
list.value = []
@ -172,10 +142,6 @@ const handleSearch = () => {
onLoad()
}
const debouncedHandleSearch = debounce(() => {
handleSearch();
}, 500);
//
const handleReset = () => {
Object.assign(searchParams, {
@ -197,11 +163,8 @@ const handleReset = () => {
const onLoad = async () => {
try {
searchParams.corpid = wxStore.corpid;
const { data } = await getApprovalAssetListApi(searchParams)
// approvalId
const existingIds = new Set(list.value.map(item => item.approvalId));
const newItems = data.rows.filter(item => !existingIds.has(item.approvalId));
list.value.push(...newItems);
const { data } = await getApprovalListApi(searchParams)
list.value.push(...data.rows)
loading.value = false
if (list.value.length >= data.total) {
@ -214,15 +177,6 @@ const onLoad = async () => {
finished.value = true
}
}
//
watch(() => searchParams.searchStr, (newVal) => {
debouncedHandleSearch();
});
onUnmounted(() => {
debouncedHandleSearch.cancel();
});
</script>
<style lang="scss" scoped>
@ -230,10 +184,6 @@ onUnmounted(() => {
padding: 12px;
}
.approval-list {
margin-top: 12px;
}
.van-cell__title {
font-size: 14px;
color: #333;
@ -244,28 +194,4 @@ onUnmounted(() => {
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;
}
.floating-verification-btn {
position: fixed;
bottom: 80px;
right: 20px;
border-radius: 50%;
width: 56px;
height: 56px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
padding: 0;
}
</style>

View File

@ -116,11 +116,6 @@ 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;
}); */
});
//

View File

@ -6,12 +6,11 @@
<van-row :gutter="[10, 10]" class="shop-row" justify="start">
<van-col v-for="shop in shopList" :key="shop.shopId" span="12" class="shop-col">
<div class="shop-item" @click="handleShopSelect(shop.shopId)">
<van-image :src="shop.coverImg || `${publicPath}product-image.png`" class="shop-cover-img" fit="cover" />
<div class="shop-info">
<van-icon name="shop-o" size="20" class="shop-icon" />
<div class="shop-name van-ellipsis">{{ shop.shopName }}</div>
</div>
</div>
<div class="shop-info">
<van-icon name="shop-o" size="20" class="shop-icon" />
<div class="shop-name van-ellipsis">{{ shop.shopName }}</div>
</div>
</div>
</van-col>
<van-col v-if="shopList.length % 2 === 0" span="12" class="shop-col"></van-col>
</van-row>
@ -342,25 +341,19 @@ onBeforeUnmount(() => {
}
.shop-item {
height: auto !important;
padding: 0;
height: 60px !important;
padding: 12px;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: transform 0.2s;
.shop-cover-img {
width: 100%;
height: 80px;
object-fit: cover;
}
.shop-info {
display: flex;
align-items: center;
justify-content: start;
padding: 12px;
height: 100%;
}
.shop-name {
@ -373,6 +366,7 @@ onBeforeUnmount(() => {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
}
}

View File

@ -108,14 +108,6 @@ 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-col span="8"></van-col>
<van-col span="8"></van-col>
</van-row>
</div>
</template>

View File

@ -50,7 +50,7 @@ function backToHome() {
}
watch(() => orderId.value, async (newVal) => {
await orderStore.getOrders(wxStore.corpid, wxStore.openid, wxStore.qyUserId)
await orderStore.getOrders(wxStore.openid)
currentOrder.value = orderStore.orders.find(o => o.orderId === newVal)
}, { immediate: true })
</script>

View File

@ -8,11 +8,11 @@ const orderStore = useOrderStore()
const wxStore = useWxStoreOutside()
onMounted(() => {
orderStore.getOrders(wxStore.corpid, wxStore.openid, wxStore.qyUserId)
orderStore.getOrders(wxStore.openid)
})
onBeforeRouteUpdate(() => {
orderStore.getOrders(wxStore.corpid, wxStore.openid, wxStore.qyUserId)
orderStore.getOrders(wxStore.openid)
})
const statusMap: Record<number, string> = {

View File

@ -137,16 +137,10 @@ const currentProducts = computed(() => {
//
onMounted(() => {
const urlParams = new URLSearchParams(window.location.search);
const shopIdParam = urlParams.get('shopId') || undefined;
if (showShopList.value) {
getShopListApi(wxStore.corpid).then((res) => {
if (res?.code === 0 && res?.data?.length > 0) {
shopList.value = res.data;
if (shopIdParam && shopList.value.some(shop => shop.shopId.toString() == shopIdParam)) {
handleShopSelect(parseInt(shopIdParam));
}
}
});
} else {
@ -226,7 +220,6 @@ async function handleAb98Bind() {
<van-row :gutter="[10, 10]" class="shop-row" justify="start">
<van-col v-for="shop in shopList" :key="shop.shopId" span="12" class="shop-col">
<div class="shop-item" @click="handleShopSelect(shop.shopId)">
<van-image :src="shop.coverImg || `${publicPath}product-image.png`" class="shop-cover-img" fit="cover" />
<div class="shop-info">
<van-icon name="shop-o" size="20" class="shop-icon" />
<div class="shop-name van-ellipsis">{{ shop.shopName }}</div>
@ -586,8 +579,8 @@ async function handleAb98Bind() {
}
.shop-item {
height: auto !important;
padding: 0;
height: 60px !important;
padding: 12px;
background: white;
border-radius: 8px;
overflow: hidden;
@ -598,12 +591,7 @@ async function handleAb98Bind() {
display: flex;
align-items: center;
justify-content: start;
padding: 12px;
}
.shop-cover-img {
width: 100%;
height: 80px;
object-fit: cover;
height: 100%;
}
.shop-name {

View File

@ -1,8 +1,8 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { ReturnApprovalAssetDTO } from '@/common/apis/approval/type'
import type { ReturnApprovalEntity } from '@/common/apis/approval/type'
export interface ApprovalDetail extends ReturnApprovalAssetDTO {
export interface ApprovalDetail extends ReturnApprovalEntity {
goodsName: string
coverImg: string
}

View File

@ -1,5 +1,5 @@
import { pinia } from "@/pinia"
import { getOrdersByOpenIdApi, getOrdersByQyUserIdApi } from "@@/apis/shop"
import { getOrdersByOpenIdApi } from "@@/apis/shop"
import type { ShopOrderEntity, ShopOrderGoodsEntity, Goods } from "@@/apis/shop/type"
export interface Order extends ShopOrderEntity {
@ -16,11 +16,9 @@ export const useOrderStore = defineStore("order", () => {
const orderGoods = ref<ShopOrderGoodsEntity[]>([])
const goods = ref<Goods[]>([])
const getOrders = async (corpid: string, openid: string, qyUserId: number) => {
const getOrders = async (openid: string) => {
try {
const { data } = qyUserId ?
await getOrdersByQyUserIdApi(qyUserId)
: await getOrdersByOpenIdApi(corpid, openid);
const { data } = await getOrdersByOpenIdApi(openid)
// 重组订单结构
orders.value = data.orders.map(order => ({

View File

@ -116,25 +116,6 @@ 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'),

View File

@ -16,7 +16,6 @@ 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']
@ -35,10 +34,8 @@ declare module 'vue' {
VanSidebar: typeof import('vant/es')['Sidebar']
VanSidebarItem: typeof import('vant/es')['SidebarItem']
VanStepper: typeof import('vant/es')['Stepper']
VanTab: typeof import('vant/es')['Tab']
VanTabbar: typeof import('vant/es')['Tabbar']
VanTabbarItem: typeof import('vant/es')['TabbarItem']
VanTabs: typeof import('vant/es')['Tabs']
VanTag: typeof import('vant/es')['Tag']
VanUploader: typeof import('vant/es')['Uploader']
}