李红攀:V2.6.969,销售出库管理
This commit is contained in:
382
src/pages-erp/sale-out/detail/index.vue
Normal file
382
src/pages-erp/sale-out/detail/index.vue
Normal file
@@ -0,0 +1,382 @@
|
||||
<template>
|
||||
<view class="yd-page-container">
|
||||
<wd-navbar
|
||||
title="销售出库详情"
|
||||
left-arrow
|
||||
placeholder
|
||||
safe-area-inset-top
|
||||
fixed
|
||||
@click-left="handleBack"
|
||||
/>
|
||||
|
||||
<view class="p-24rpx pb-180rpx" :class="{ 'opacity-60': loading }">
|
||||
<view class="section-card">
|
||||
<view class="section-title">
|
||||
基本信息
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">出库单号</text>
|
||||
<text class="info-value">{{ detail.no || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">客户</text>
|
||||
<text class="info-value">{{ detail.customerName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">出库时间</text>
|
||||
<text class="info-value">{{ formatDate(detail.outTime) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">关联订单</text>
|
||||
<text class="info-value">{{ detail.orderNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">销售人员</text>
|
||||
<text class="info-value">{{ getSaleUserName() }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">状态</text>
|
||||
<view :class="getStatusClass(detail.status)">
|
||||
{{ getStatusText(detail.status) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">备注</text>
|
||||
<text class="info-value max-w-[420rpx] text-right">{{ detail.remark || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-card">
|
||||
<view class="section-title">
|
||||
金额信息
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">总数量</text>
|
||||
<text class="info-value">{{ formatCount(detail.totalCount) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">应收金额</text>
|
||||
<text class="info-value text-[#409eff]">¥{{ formatPrice(detail.totalPrice) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">已收金额</text>
|
||||
<text class="info-value text-[#52c41a]">¥{{ formatPrice(detail.receiptPrice) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">未收金额</text>
|
||||
<text class="info-value text-[#f5222d]">¥{{ formatPrice(unreceivedPrice) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-card">
|
||||
<view class="section-title">
|
||||
出库明细
|
||||
</view>
|
||||
<view v-if="!detail.items || detail.items.length === 0" class="py-40rpx text-center text-[#999]">
|
||||
暂无产品数据
|
||||
</view>
|
||||
<view
|
||||
v-for="(item, index) in detail.items"
|
||||
:key="item.id || index"
|
||||
class="item-card"
|
||||
>
|
||||
<view class="mb-12rpx text-28rpx text-[#333] font-semibold">
|
||||
{{ item.productName || '-' }}
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">仓库</text>
|
||||
<text class="info-value">{{ item.warehouseName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">单位</text>
|
||||
<text class="info-value">{{ item.productUnitName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">数量</text>
|
||||
<text class="info-value">{{ formatCount(item.count) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">单价</text>
|
||||
<text class="info-value">¥{{ formatPrice(item.productPrice) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">金额</text>
|
||||
<text class="info-value text-[#409eff]">¥{{ formatPrice(item.totalPrice) }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">批次</text>
|
||||
<text class="info-value">{{ item.batchNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">备注</text>
|
||||
<text class="info-value max-w-[420rpx] text-right">{{ item.remark || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="yd-detail-footer">
|
||||
<wd-button
|
||||
v-if="canEdit"
|
||||
type="primary"
|
||||
plain
|
||||
@click="handleEdit"
|
||||
>
|
||||
编辑
|
||||
</wd-button>
|
||||
<wd-button
|
||||
v-if="canApprove"
|
||||
type="success"
|
||||
@click="handleApprove"
|
||||
>
|
||||
审核
|
||||
</wd-button>
|
||||
<wd-button
|
||||
v-if="canDelete"
|
||||
type="error"
|
||||
@click="handleDelete"
|
||||
>
|
||||
删除
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { SaleOut } from '@/api/erp/sale-out'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useToast } from 'wot-design-uni'
|
||||
import { getApprovalRecordListByBiz } from '@/api/erp/approval-record'
|
||||
import { deleteSaleOut, getSaleOut, updateSaleOutStatus } from '@/api/erp/sale-out'
|
||||
import { getSimpleUserList } from '@/api/system/user'
|
||||
import { useAccess } from '@/hooks/useAccess'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: number | string
|
||||
}>()
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
})
|
||||
|
||||
const { hasAccessByCodes } = useAccess()
|
||||
const toast = useToast()
|
||||
const loading = ref(false)
|
||||
const detail = ref<SaleOut>({})
|
||||
const userList = ref<{ id?: number, nickname?: string }[]>([])
|
||||
|
||||
const unreceivedPrice = computed(() => {
|
||||
const totalPrice = Number(detail.value.totalPrice || 0)
|
||||
const receiptPrice = Number(detail.value.receiptPrice || 0)
|
||||
return Math.max(totalPrice - receiptPrice, 0)
|
||||
})
|
||||
|
||||
const canEdit = computed(() => {
|
||||
return hasAccessByCodes(['erp:sale-out:update']) && detail.value.status === 10 && !detail.value.hasApprovalRecords
|
||||
})
|
||||
|
||||
const canApprove = computed(() => {
|
||||
return hasAccessByCodes(['erp:sale-out:update-status']) && detail.value.status === 10
|
||||
})
|
||||
|
||||
const canDelete = computed(() => {
|
||||
return hasAccessByCodes(['erp:sale-out:delete']) && !detail.value.hasApprovalRecords
|
||||
})
|
||||
|
||||
function handleBack() {
|
||||
navigateBackPlus('/pages-erp/sale-out/index')
|
||||
}
|
||||
|
||||
function getStatusClass(status?: number) {
|
||||
switch (status) {
|
||||
case 10:
|
||||
return 'status-tag bg-[#fffbe6] text-[#faad14]'
|
||||
case 20:
|
||||
return 'status-tag bg-[#f6ffed] text-[#52c41a]'
|
||||
default:
|
||||
return 'status-tag bg-[#f5f5f5] text-[#999]'
|
||||
}
|
||||
}
|
||||
|
||||
function getStatusText(status?: number) {
|
||||
switch (status) {
|
||||
case 10:
|
||||
return '未审核'
|
||||
case 20:
|
||||
return '已审核'
|
||||
default:
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
|
||||
function formatDate(value?: string | number) {
|
||||
if (!value)
|
||||
return '-'
|
||||
const date = new Date(value)
|
||||
if (Number.isNaN(date.getTime()))
|
||||
return String(value).slice(0, 10)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
function formatCount(value?: number) {
|
||||
if (value === undefined || value === null)
|
||||
return '0.00'
|
||||
return Number(value).toFixed(2)
|
||||
}
|
||||
|
||||
function formatPrice(value?: number) {
|
||||
if (value === undefined || value === null)
|
||||
return '0.00'
|
||||
return Number(value).toFixed(2)
|
||||
}
|
||||
|
||||
function getSaleUserName() {
|
||||
if (detail.value.saleUserName)
|
||||
return detail.value.saleUserName
|
||||
const matched = userList.value.find(item => item.id === detail.value.saleUserId)
|
||||
return matched?.nickname || '-'
|
||||
}
|
||||
|
||||
async function loadUserList() {
|
||||
try {
|
||||
userList.value = await getSimpleUserList()
|
||||
} catch {
|
||||
userList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
async function appendApprovalFlag() {
|
||||
if (!detail.value.id)
|
||||
return
|
||||
try {
|
||||
const approvalList = await getApprovalRecordListByBiz(detail.value.id, 'erp_sale_out')
|
||||
detail.value.hasApprovalRecords = approvalList.length > 0
|
||||
} catch {
|
||||
detail.value.hasApprovalRecords = false
|
||||
}
|
||||
}
|
||||
|
||||
async function getDetail() {
|
||||
if (!props.id)
|
||||
return
|
||||
loading.value = true
|
||||
try {
|
||||
detail.value = await getSaleOut(Number(props.id))
|
||||
await appendApprovalFlag()
|
||||
} catch {
|
||||
toast.error('加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleEdit() {
|
||||
uni.navigateTo({ url: `/pages-erp/sale-out/form/index?id=${props.id}` })
|
||||
}
|
||||
|
||||
function handleApprove() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定审核该销售出库单吗?',
|
||||
success: async (res) => {
|
||||
if (!res.confirm || !props.id)
|
||||
return
|
||||
try {
|
||||
await updateSaleOutStatus(Number(props.id), 20)
|
||||
toast.success('审核成功')
|
||||
getDetail()
|
||||
} catch {
|
||||
// handled by http
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定删除该销售出库单吗?',
|
||||
success: async (res) => {
|
||||
if (!res.confirm || !props.id)
|
||||
return
|
||||
try {
|
||||
await deleteSaleOut([Number(props.id)])
|
||||
toast.success('删除成功')
|
||||
setTimeout(() => {
|
||||
handleBack()
|
||||
}, 500)
|
||||
} catch {
|
||||
// handled by http
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadUserList()
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.section-card {
|
||||
margin-bottom: 24rpx;
|
||||
border-radius: 12rpx;
|
||||
background: #fff;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin-bottom: 24rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.info-label {
|
||||
flex-shrink: 0;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.item-card {
|
||||
margin-bottom: 16rpx;
|
||||
border-radius: 8rpx;
|
||||
background: #fafafa;
|
||||
padding: 16rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user