weightTicketViewer.vue 13.6 KB
<template>
    <view class="detail-page">
        <scroll-view class="scroll" scroll-y>
            <!-- 基础信息 -->
            <view class="section">
                <text class="row company">{{ safeText(form.code) }}</text>
                <view class="row"><text class="label">类型</text><text class="value">{{ typeName }}</text></view>
                <view class="row"><text class="label">计划编号</text><text class="value">{{ safeText(planCodeText) }}</text></view>
                <view class="row"><text class="label">单位</text><text class="value">{{ safeText(form.unit) }}</text></view>
                <view class="row"><text class="label">时间</text><text class="value">{{ formatDate(form.firstGrossWeightTime) }}</text></view>
                <view class="row"><text class="label">供料单位</text><text class="value">{{ safeText(form.supplierName) }}</text></view>
                <view class="row"><text class="label">收料单位</text><text class="value">{{ safeText(form.companyShortName) }}</text></view>
            </view>

            <!-- 计划单明细 -->
            <view class="sub-table">
                <view class="sub-header">
                    <image class="sub-bar" src="/static/images/title.png" />
                    <text class="sub-title">计划单明细</text>
                </view>
                <view class="sub-body">
                    <view v-if="!planList.length" class="empty-tip">暂无明细</view>
                    <view v-for="(item, idx) in planList" :key="'p-' + (item.id || idx)" class="card">
                        <view class="card-index">第 {{ idx + 1 }} 行</view>
                        <view class="row"><text class="label">计划品种</text><text class="value">{{ safeText(item.productName) }}</text></view>
                        <view class="row"><text class="label">计划数量</text><text class="value">{{ formatNumber(item.quantity) }}</text></view>
                        <view class="row"><text class="label">计划编号</text><text class="value">{{ safeText(item.planCode) }}</text></view>
                    </view>
                </view>
            </view>

            <!-- 过磅明细 -->
            <view class="sub-table">
                <view class="sub-header">
                    <image class="sub-bar" src="/static/images/title.png" />
                    <text class="sub-title">过磅明细</text>
                </view>
                <view class="sub-body">
                    <view v-if="!weighList.length" class="empty-tip">暂无明细</view>
                    <view v-for="(item, idx) in weighList" :key="'w-' + (item.id || idx)" class="card">
                        <view class="card-index">第 {{ idx + 1 }} 行</view>
                        <view class="row"><text class="label">过磅品种</text><text class="value">{{ safeText(item.productName) }}</text></view>
                        <view class="row"><text class="label">开票品名</text><text class="value">{{ safeText(item.invoiceProductName) }}</text></view>
                        <view class="row"><text class="label">过磅数量</text><text class="value">{{ formatNumber(item.quantity) }}</text></view>
                        <view class="row"><text class="label">计划编号</text><text class="value">{{ safeText(item.planCode) }}</text></view>
                    </view>
                </view>
            </view>

            <!-- 申请数量明细(仅买断展示) -->
            <view v-if="isBuyout" class="sub-table">
                <view class="sub-header">
                    <image class="sub-bar" src="/static/images/title.png" />
                    <text class="sub-title">申请数量明细</text>
                </view>
                <view class="sub-body">
                    <view v-if="!applyList.length" class="empty-tip">暂无明细</view>
                    <view v-for="(item, idx) in applyList" :key="'a-' + (item.id || idx)" class="card">
                        <view class="card-index">第 {{ idx + 1 }} 行</view>
                        <view class="row"><text class="label">类别</text><text class="value">{{ safeText(item.category) }}</text></view>
                        <view class="row"><text class="label">数量</text><text class="value">{{ formatNumber(item.quantity) }}</text></view>
                    </view>
                </view>
            </view>

            <!-- 其他信息 -->
            <view class="title-header">
                <image class="title-header_icon" src="/static/images/title.png" />
                <span>其他信息</span>
            </view>
            <view class="section">
                <view class="row"><text class="label">物料名称</text><text class="value">{{ safeText(form.materialName) }}</text></view>
                <view class="row"><text class="label">过磅总重</text><text class="value">{{ formatNumber(form.totalGrossWeight) }}</text></view>
                <view class="row"><text class="label">实际伙重</text><text class="value">{{ formatNumber(form.actualImpurityWeight) }}</text></view>
                <view class="row"><text class="label">货物净重</text><text class="value">{{ formatNumber(form.netWeight) }}</text></view>
                <view class="row"><text class="label">净重大写</text><text class="value">{{ safeText(form.netWeightUpper) }}</text></view>
                <view class="row"><text class="label">来料扣杂类型</text><text class="value">{{ deductionNames }}</text></view>
                <view class="row"><text class="label">供应存欠料</text><text class="value">{{ safeText(form.supplierStockMaterial) }}</text></view>
                <view class="row"><text class="label">物流存欠料</text><text class="value">{{ safeText(form.logisticsStockMaterial) }}</text></view>
                <view class="row"><text class="label">车牌号</text><text class="value">{{ safeText(form.plateNumber) }}</text></view>
            </view>

            <!-- 业务摘要 -->
            <view class="title-header">
                <image class="title-header_icon" src="/static/images/title.png" />
                <span>业务摘要</span>
            </view>
            <view class="section">
                <text class="remark-text">{{ safeText(form.businessSummary) }}</text>
            </view>

            <!-- 特别说明 -->
            <view class="title-header">
                <image class="title-header_icon" src="/static/images/title.png" />
                <span>特别说明</span>
            </view>
            <view class="section">
                <text class="remark-text">{{ safeText(form.specialNote) }}</text>
            </view>

            <!-- 单据备注 -->
            <view class="title-header">
                <image class="title-header_icon" src="/static/images/title.png" />
                <span>单据备注</span>
            </view>
            <view class="section">
                <text class="remark-text">{{ safeText(form.documentRemark) }}</text>
            </view>
        </scroll-view>
    </view>
</template>

<script>
import { weighTicketDetailApi } from '@/api/procure-manage/weighTicket.js'
import { getDicByCodes, getDicName } from '@/utils/dic.js'

export default {
    name: 'WeightTicketViewer',
    props: { id: { type: [String, Number], default: '' } },
    data() {
        return {
            form: {},
            planList: [],
            weighList: [],
            applyList: [],
            buyout: false,
            dicOptions: {
                TYPE: [],
                DEDUCTION_TYPE: []
            }
        }
    },
    computed: {
        typeName() {
            const v = this.form.type == null ? '' : String(this.form.type)
            if (!v) return '-'
            return getDicName('TYPE', v, this.dicOptions.TYPE) || v
        },
        planCodeText() {
            const codes = Array.from(new Set((this.planList || []).map(it => it.planCode).filter(Boolean)))
            return codes.join(',')
        },
        deductionNames() {
            const codes = (this.form.deductionTypes == null ? '' : String(this.form.deductionTypes)).split(',').filter(Boolean)
            const names = codes.map(code => getDicName('DEDUCTION_TYPE', code, this.dicOptions.DEDUCTION_TYPE)).filter(Boolean)
            return names.length ? names.join('、') : '-'
        },
        isBuyout() {
            return this.buyout || (this.applyList || []).length > 0
        }
    },
    created() {
        this.loadDicData()
    },
    watch: {
        id: {
            immediate: true,
            handler(val) {
                const v = (val !== undefined && val !== null) ? String(val) : ''
                if (v) this.loadDetail(v)
            }
        }
    },
    methods: {
        async loadDicData() {
            try {
                const results = await getDicByCodes(['TYPE', 'DEDUCTION_TYPE'])
                this.dicOptions.TYPE = (results.TYPE && results.TYPE.data) || []
                this.dicOptions.DEDUCTION_TYPE = (results.DEDUCTION_TYPE && results.DEDUCTION_TYPE.data) || []
            } catch (e) {
                this.dicOptions = { TYPE: [], DEDUCTION_TYPE: [] }
            }
        },
        loadDetail(id) {
            weighTicketDetailApi({ id })
                .then(res => {
                    const data = res && res.data ? (res.data.data || res.data) : {}
                    this.form = Object.assign({}, data)
                    this.buyout = !!data.buyout
                    this.planList = Array.isArray(data.planLines) ? data.planLines.map(x => ({ ...x })) : []
                    this.weighList = Array.isArray(data.weighLines) ? data.weighLines.map(x => ({ ...x })) : []
                    this.applyList = Array.isArray(data.applyLines) ? data.applyLines.map(x => ({ ...x })) : []
                })
                .catch(() => {
                    uni.showToast({ title: '详情加载失败', icon: 'none' })
                })
        },
        safeText(v) {
            const s = v == null ? '' : String(v)
            return s.trim() ? s : '-'
        },
        formatNumber(v) {
            if (v === null || v === undefined || v === '') return '-'
            const n = Number(v)
            if (isNaN(n)) return String(v)
            return n.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
        },
        formatDate(v) {
            const s = v == null ? '' : String(v).trim()
            if (!s) return '-'
            return s
        }
    }
}
</script>

<style lang="scss" scoped>
.detail-page {
    background: #f3f3f3;
    min-height: 100%;
}
.scroll {
    height: 100%;
}
.section {
    padding: 32rpx;
    background: #fff;
    margin-bottom: 20rpx;
}
.title-header {
    display: flex;
    align-items: center;
    padding: 24rpx 32rpx 12rpx;
    background-color: #ffffff;

    .title-header_icon {
        width: 36rpx;
        height: 36rpx;
        margin-right: 12rpx;
    }

    span {
        font-size: 34rpx;
        font-weight: 600;
        color: rgba(0, 0, 0, 0.9);
    }
}
.row {
    display: flex;
    margin-bottom: 28rpx;

    &:last-child {
        margin-bottom: 0;
    }

    &.company {
        font-size: 36rpx;
        font-weight: 600;
        color: rgba(0, 0, 0, 0.9);
        padding-top: 8rpx;
        line-height: 50rpx;
    }

    .label {
        max-width: 420rpx;
        margin-right: 20rpx;
        line-height: 32rpx;
        font-size: 28rpx;
        color: rgba(0, 0, 0, 0.6);
    }

    .value {
        flex: 1;
        line-height: 32rpx;
        font-size: 28rpx;
        color: rgba(0, 0, 0, 0.9);
        text-align: right;
        word-break: break-all;
    }
}
.remark-text {
    font-size: 28rpx;
    color: rgba(0, 0, 0, 0.9);
    line-height: 1.6;
    white-space: pre-wrap;
}
.sub-table {
    background: #fff;
    margin-bottom: 20rpx;
}
.sub-header {
    display: flex;
    align-items: center;
    padding: 16rpx 0 16rpx 32rpx;

    .sub-bar {
        width: 6rpx;
        height: 32rpx;
        margin-right: 14rpx;
    }

    .sub-title {
        font-size: 30rpx;
        font-weight: 600;
        color: rgba(0, 0, 0, 0.9);
        flex: 1;
        line-height: 42rpx;
    }
}
.sub-body {
    padding: 0 32rpx 24rpx;

    .empty-tip {
        text-align: center;
        padding: 60rpx 0;
        color: rgba(0, 0, 0, 0.4);
        font-size: 28rpx;
    }

    .card {
        position: relative;
        background: #fafafa;
        border-radius: 12rpx;
        padding: 32rpx 28rpx 20rpx;
        margin-bottom: 24rpx;

        .card-index {
            position: absolute;
            top: 0;
            right: 0;
            background: rgba(24, 144, 255, 0.08);
            color: $theme-primary;
            font-size: 22rpx;
            line-height: 36rpx;
            padding: 0 16rpx;
            border-bottom-left-radius: 12rpx;
            border-top-right-radius: 12rpx;
        }

        .row {
            align-items: center;
            justify-content: space-between;
            padding: 14rpx 0;
            margin-bottom: 0;
            border-bottom: 1rpx dashed #f2f2f2;

            &:last-child {
                border-bottom: none;
            }

            .label {
                flex-shrink: 0;
                width: 220rpx;
                max-width: none;
                margin-right: 0;
                font-size: 28rpx;
                color: rgba(0, 0, 0, 0.6);
                line-height: 40rpx;
            }

            .value {
                flex: 1;
                text-align: right;
                font-size: 28rpx;
                color: rgba(0, 0, 0, 0.9);
                line-height: 40rpx;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }
        }
    }
}
</style>