483 lines
16 KiB
Vue
483 lines
16 KiB
Vue
<script setup lang="ts">
|
||
import { ref, onMounted, computed } from 'vue'
|
||
import { showConfirmDialog, showSuccessToast, showFailToast, showToast, UploaderFileListItem, Popup, Picker, Field, Stepper } from 'vant'
|
||
import axios from "axios"
|
||
import { getApprovalGoodsCellApi, handleApprovalApi, handleApprovalAssetApi, openCabinetApi } from '@/common/apis/approval'
|
||
import type { ApprovalGoodsCellEntity, ApprovalGoodsEntity, HandleApprovalAssetRequestData, 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 Compressor from 'compressorjs';
|
||
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()
|
||
const route = useRoute()
|
||
const approvalStore = useApprovalStore();
|
||
const wxStore = useWxStore();
|
||
const { openid, balance, corpidLogin, userid: qyUserid, name: qyName } = storeToRefs(wxStore);
|
||
const ab98UserStore = useAb98UserStore();
|
||
const { tel, userid: ab98Userid, name } = storeToRefs(ab98UserStore);
|
||
|
||
const formData = ref<HandleApprovalAssetRequestData>({
|
||
approvalId: approvalStore.currentApproval?.approvalId || 0,
|
||
status: 2,
|
||
returnAmount: approvalStore.currentApproval?.goodsPrice || 0,
|
||
auditImages: '',
|
||
auditRemark: '',
|
||
userid: wxStore.userid,
|
||
corpid: wxStore.corpid,
|
||
auditUserid: wxStore.userid,
|
||
approvalGoodsList: []
|
||
})
|
||
|
||
const orderGoods = ref<ApprovalGoodsEntity[]>([]);
|
||
const submitting = ref(false)
|
||
const fileList = ref<UploaderFileListItem[]>([])
|
||
const uploading = ref(false)
|
||
const isButtonDisabled = ref(false)
|
||
|
||
const showStatusPicker = ref(false)
|
||
const showPreview = ref(false)
|
||
const currentPreviewImage = ref('')
|
||
const statusMap: { [key: number]: string } = {
|
||
1: '待审核',
|
||
2: '已通过',
|
||
3: '已驳回'
|
||
}
|
||
|
||
const statusOptions = [
|
||
{ text: '待审核', value: 1 },
|
||
{ text: '通过', value: 2 },
|
||
{ text: '驳回', value: 3 }
|
||
]
|
||
const validateForm = () => {
|
||
if (!formData.value.approvalId || isNaN(formData.value.approvalId)) {
|
||
showConfirmDialog({ title: '错误', message: '审批单ID参数错误' })
|
||
return false
|
||
}
|
||
if (formData.value.status === 3 && !formData.value.auditRemark) {
|
||
showConfirmDialog({ title: '提示', message: '驳回时必须填写审核说明' })
|
||
return false
|
||
}
|
||
|
||
// 验证审批数量
|
||
if (approvalStore.currentApproval?.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 onStatusConfirm = ({ selectedOptions }: { selectedOptions: { value: number }[] }) => {
|
||
submitting.value = true
|
||
try {
|
||
if (!selectedOptions?.[0]?.value) {
|
||
throw new Error('请选择有效的审批状态')
|
||
}
|
||
formData.value.status = selectedOptions[0].value
|
||
showStatusPicker.value = false
|
||
} catch (error) {
|
||
showConfirmDialog({
|
||
title: '操作失败',
|
||
message: error instanceof Error ? error.message : '状态选择异常'
|
||
})
|
||
} finally {
|
||
submitting.value = false
|
||
}
|
||
}
|
||
|
||
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 : ''
|
||
};
|
||
});
|
||
});
|
||
|
||
onMounted(async () => {
|
||
if (!approvalStore.currentApproval) {
|
||
router.push('/approvalAsset/list');
|
||
return;
|
||
}
|
||
|
||
// 初始化订单商品数据,添加approvalQuantity字段
|
||
orderGoods.value = (approvalStore.currentApproval.goodsList || []).map(item => ({
|
||
...item,
|
||
approvalQuantity: item.approvalQuantity || item.applyQuantity
|
||
}));
|
||
|
||
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
|
||
};
|
||
|
||
// 获取已审批商品格口信息
|
||
try {
|
||
const result = await getApprovalGoodsCellApi(approvalStore.currentApproval.approvalId);
|
||
if (result.code === 0) {
|
||
approvalGoodsCells.value = result.data;
|
||
} else {
|
||
showFailToast(result.msg || '获取审批商品格口信息失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('获取审批商品格口信息失败:', error);
|
||
showFailToast('获取审批商品格口信息失败');
|
||
}
|
||
}
|
||
})
|
||
|
||
const handleOpenCell = async (approvalGoodsCellId: number) => {
|
||
if (!approvalGoodsCellId) {
|
||
showFailToast('缺少格口信息');
|
||
return;
|
||
}
|
||
|
||
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);
|
||
if (result.code !== 0) {
|
||
showFailToast(result.msg || '开启格口失败');
|
||
return;
|
||
}
|
||
showSuccessToast('格口已开启');
|
||
} catch (error) {
|
||
showFailToast('请求失败');
|
||
} finally {
|
||
setTimeout(() => {
|
||
isButtonDisabled.value = false;
|
||
}, 5000);
|
||
}
|
||
};
|
||
|
||
const handleSubmit = 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 handleApprovalAssetApi(formData.value)
|
||
|
||
if (code === 0) {
|
||
showSuccessToast('操作成功')
|
||
await showConfirmDialog({
|
||
title: "操作成功",
|
||
message: `审批处理已完成`
|
||
})
|
||
router.push('/approval/list')
|
||
} 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 handleComfirm = () => {
|
||
formData.value.status = 2;
|
||
handleSubmit();
|
||
}
|
||
|
||
const handleReject = () => {
|
||
formData.value.status = 3;
|
||
handleSubmit();
|
||
}
|
||
|
||
</script>
|
||
|
||
<template>
|
||
<div class="approval-container">
|
||
<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?.applyUserName" />
|
||
<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">
|
||
<template v-for="item in orderGoods" :key="item.goodsId">
|
||
<van-cell class="product-item">
|
||
<template #icon>
|
||
<van-image :src="item.coverImg" width="60" height="60" class="product-image" />
|
||
</template>
|
||
|
||
<div class="product-info">
|
||
<div class="product-name van-ellipsis">
|
||
{{ item.goodsName }}
|
||
</div>
|
||
<div class="price-row">
|
||
<span class="product-price">
|
||
¥{{ item.price.toFixed(2) }}
|
||
</span>
|
||
<span class="quantity">
|
||
申请数量:{{ item.applyQuantity }}
|
||
</span>
|
||
</div>
|
||
<!-- 审批数量输入框 -->
|
||
<div v-if="approvalStore.currentApproval?.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-else class="approval-quantity-view">
|
||
<span class="label">审批数量:</span>
|
||
<span class="value">{{ item.approvalQuantity }}</span>
|
||
</div>
|
||
</div>
|
||
</van-cell>
|
||
</template>
|
||
</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="product-name van-ellipsis">
|
||
{{ item.shopName }}
|
||
</div>
|
||
<div class="product-name van-ellipsis">
|
||
{{ item.cabinetName }} - 格口{{ item.cellNo }}
|
||
</div>
|
||
<div class="product-name van-ellipsis">
|
||
分配数量:{{ item.allocateQuantity }}
|
||
</div>
|
||
<div class="open-cell-btn">
|
||
<van-button type="primary" size="small" @click="handleOpenCell(item.approvalGoodsCellId)" :disabled="isButtonDisabled">
|
||
打开格口
|
||
</van-button>
|
||
</div>
|
||
</div>
|
||
</van-cell>
|
||
</template>
|
||
</van-cell-group>
|
||
|
||
<van-cell-group>
|
||
<!-- 原有表单字段保持不变 -->
|
||
<!-- <van-button type="primary" size="small" @click="handleOpenCabinet" :disabled="isButtonDisabled"
|
||
style="margin-bottom: 12px">
|
||
打开柜子
|
||
</van-button> -->
|
||
<van-field v-model="formData.auditRemark" label="审核说明" type="textarea" rows="2" autosize
|
||
class="audit-remark-field" />
|
||
</van-cell-group>
|
||
|
||
<van-popup v-model:show="showPreview" position="center" :style="{ width: '90%', height: '80%' }" round>
|
||
<div class="preview-wrapper">
|
||
<van-icon name="cross" class="close-icon" @click="showPreview = false" />
|
||
<van-image :src="currentPreviewImage" fit="contain" class="preview-image" />
|
||
</div>
|
||
</van-popup>
|
||
|
||
<div class="btn-group" v-if="approvalStore.currentApproval?.status === 1">
|
||
<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" :loading="submitting" loading-text="提交中..." @click="handleComfirm"
|
||
style="flex: 1; height: 44px; border-radius: 8px; font-size: 16px;">
|
||
同意
|
||
</van-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.approval-container {
|
||
padding: 12px 16px;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.approval-quantity,
|
||
.approval-quantity-view {
|
||
margin-top: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.approval-quantity-view .label {
|
||
color: #666;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.approval-quantity-view .value {
|
||
font-weight: bold;
|
||
}
|
||
.price-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.product-price {
|
||
color: #e95d5d;
|
||
font-weight: bold;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.quantity {
|
||
color: #666;
|
||
margin-right: 20px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.upload-section {
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.preview-wrapper {
|
||
position: relative;
|
||
height: 100%;
|
||
padding: 20px;
|
||
|
||
.close-icon {
|
||
position: absolute;
|
||
top: 10px;
|
||
right: 10px;
|
||
z-index: 1;
|
||
font-size: 24px;
|
||
color: #fff;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
border-radius: 50%;
|
||
padding: 4px;
|
||
}
|
||
|
||
.preview-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.btn-group {
|
||
display: flex;
|
||
width: 100%;
|
||
gap: 12px;
|
||
margin: 8px 0;
|
||
background-color: #ffffff;
|
||
padding: 16px;
|
||
}
|
||
|
||
.content-wrapper {
|
||
padding-bottom: 100px;
|
||
}
|
||
|
||
.audit-remark-field::v-deep .van-field__control {
|
||
background-color: #fffbe6;
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.clickable-status-field:not(:disabled)::v-deep .van-field__control {
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
background-color: #f7f8fa;
|
||
}
|
||
|
||
.clickable-status-field:not(:disabled):hover::v-deep .van-field__control {
|
||
background-color: #f2f3f5;
|
||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.clickable-status-field:not(:disabled):active::v-deep .van-field__control {
|
||
background-color: #ebedf0;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||
}
|
||
</style> |