detail.vue 15.8 KB
<template>
    <view class="page">
        <scroll-view class="scroll" scroll-y>
            <view class="detail-page">
                <!-- 基础信息 -->
                <view class="section">
                    <text class="row company">{{ safeText(form.code) }}</text>
                    <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.purchaseDepartmentName) }}</text></view>
                    <view class="row"><text class="label">区域</text><text class="value">{{ safeText(form.regionName) }}</text></view>
                    <view class="row"><text class="label">报价时效</text><text class="value">{{ safeText(form.validityPeriod) }}</text></view>
                    <view class="row"><text class="label">报价人</text><text class="value">{{ safeText(form.quoterName) }}</text></view>
                    <view class="row"><text class="label">是否长单操作</text><text class="value">{{ boolText(form.longTermOperation) }}</text></view>
                    <view class="row"><text class="label">是否终端客户</text><text class="value">{{ boolText(form.terminalCustomer) }}</text></view>
                    <view class="row"><text class="label">是否未点价</text><text class="value">{{ boolText(form.priced) }}</text></view>
                    <view v-if="form.priced === true" class="row"><text class="label">点价截止日期</text><text class="value">{{ formatDateOnly(form.pricingDeadline) }}</text></view>
                    <view v-if="form.priced === true" class="row"><text class="label">是否暂定价</text><text class="value">{{ boolText(form.temporaryPrice) }}</text></view>
                    <view class="row"><text class="label">其他费用凭票到厂另行结算</text><text class="value">{{ boolText(form.otherExpenseSettlement) }}</text></view>
                    <view class="row"><text class="label">交提货方式</text><text class="value">{{ getDeliveryMethodLabel() }}</text></view>
                    <view class="row"><text class="label">是否通货买断</text><text class="value">{{ boolText(form.commodityBuyout) }}</text></view>
                    <view class="row"><text class="label">是否质量买断</text><text class="value">{{ boolText(form.qualityBuyout) }}</text></view>
                    <view class="row"><text class="label">是否数量买断</text><text class="value">{{ boolText(form.quantityBuyout) }}</text></view>
                    <view class="row"><text class="label">是否送货上门</text><text class="value">{{ boolText(form.doorDelivery) }}</text></view>
                    <view class="row"><text class="label">装车费</text><text class="value">{{ safeText(form.loadingFee) }}</text></view>
                    <view class="row"><text class="label">开票品种</text><text class="value">{{ safeText(form.invoiceProductName) }}</text></view>
                    <view class="row"><text class="label">交货日期</text><text class="value">{{ formatDateOnly(form.deliveryDate) }}</text></view>
                    <view class="row"><text class="label">结算方式</text><text class="value">{{ safeText(form.settlementMethod) }}</text></view>
                    <view class="row"><text class="label">结算比例(%)</text><text class="value">{{ safeText(form.settlementRatio) }}</text></view>
                    <view class="row"><text class="label">协调事由</text><text class="value">{{ safeText(form.coordinationReason) }}</text></view>
                    <view class="row"><text class="label">票类状况</text><text class="value">{{ safeText(form.invoiceTypeStatus) }}</text></view>
                </view>

                <!-- 明细信息 -->
                <ProductRel mode="view" :list="productLineList" />

                <!-- 料质状况 -->
                <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">{{ boolText(form.materialConditionEnabled) }}</text></view>
                    <template v-if="form.materialConditionEnabled">
                        <view v-for="mc in materialConditionConfigs" :key="mc.field" class="material-group">
                            <text class="material-label">{{ mc.label }}</text>
                            <view class="tag-list">
                                <view
                                    v-for="opt in getDictOptions(mc.dictCode)"
                                    :key="opt.code"
                                    class="tag-btn"
                                    :class="{ active: isTagSelected(mc.field, opt.code) }"
                                >{{ opt.name }}</view>
                            </view>
                        </view>
                    </template>
                </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.remark) }}</text>
                </view>
            </view>
        </scroll-view>
        <detail-buttons :buttons="displayButtons" @click="handleButtonClick" />
    </view>
</template>
<script>
import DetailButtons from '@/components/detail-buttons/index.vue'
import ProductRel from './productRel.vue'
import { salesmanQuotationDetailApi, salesmanQuotationCancelApi } from '@/api/procure-manage/salesmanQuotation.js'
import { getDicByCodes } from '@/utils/dic.js'
import { getInfo } from '@/api/login.js'
export default {
    name: 'DetailSalesPrice',
    components: { DetailButtons, ProductRel },
    data() {
        return {
            id: '',
            form: {},
            productLineList: [],
            materialConditionConfigs: [
                { field: 'materialConditionCopperRice', label: '铜米', dictCode: 'COPPER_RICE' },
                { field: 'materialConditionBrightCopper', label: '光亮铜', dictCode: 'BRIGHT_COPPER' },
                { field: 'materialConditionFirstClass', label: '一特', dictCode: 'FIRST_CLASS' },
                { field: 'materialConditionSecondClass', label: '二特', dictCode: 'SECOND_CLASS' },
                { field: 'materialConditionTinCoated', label: '镀锡紫', dictCode: 'TIN_COATED' },
                { field: 'materialConditionScrap', label: '角料', dictCode: 'SCRAP' },
                { field: 'materialConditionWhiteYellow', label: '镀白黄', dictCode: 'WHITE_YELLOW' },
                { field: 'materialConditionPhosphorCopper', label: '磷铜', dictCode: 'PHOSPHOR_COPPER' }
            ],
            dictData: {},
            buttons: [
                { text: '编辑', visible: true, variant: 'outline', event: 'edit' },
                { text: '复制', visible: true, variant: 'primary', event: 'copy' },
                { text: '取消', visible: true, variant: 'outline', event: 'cancel' },
                { text: '生成计划', visible: true, variant: 'outline', event: 'plan' },
                { text: '审核', visible: true, variant: 'primary', event: 'approve' },
                { text: '审核详情', visible: true,  event: 'approveDetail' },
            ],
            investigatorId: '',
        }
    },
    computed: {
        displayButtons() {
            const s = this.form.approvalStatus || ''
            const c = this.form.createById || ''
            const p = this.form.status || ''
            return [
                { ...this.buttons[0], visible: (s === 'REFUSE' && c === this.investigatorId && this.$auth.hasPermi('procure-manage:sales-price:modify')) },
                { ...this.buttons[1], visible: this.$auth.hasPermi('procure-manage:sales-price:copy') },
                { ...this.buttons[2], visible: (s === 'REFUSE' && c === this.investigatorId && this.$auth.hasPermi('procure-manage:sales-price:cancel')) },
                { ...this.buttons[3], visible: (p === 'DEAL_CLOSED' && this.$auth.hasPermi('procure-manage:sales-price:create')) },
                { ...this.buttons[4], visible: (s === 'AUDIT' && this.form.showExamine && this.$auth.hasPermi('procure-manage:sales-price:approves')) },
                { ...this.buttons[5], visible: (this.form.showExamineDetail && this.$auth.hasPermi('procure-manage:sales-price:view')) },
            ]
        }
    },
    onLoad(query) {
        this.id = (query && query.id) || ''
        this.loadDictData()
        this.loadDetail()
        //  获取当前用户信息
        this.getUserInfo();
    },
    methods: {
        // 获取当前用户信息
        async getUserInfo() {
            getInfo()
            .then((res) => {
                this.investigatorId = res?.id || '';
            })
            .catch((err) => {
                console.error('获取用户信息失败:', err);
            });
        },
        async loadDictData() {
            try {
                const codes = [...this.materialConditionConfigs.map(mc => mc.dictCode), 'DELIVERY_METHOD']
                const res = await getDicByCodes(codes)
                this.dictData = res || {}
            } catch (e) {
                this.dictData = {}
            }
        },
        getDictOptions(dictCode) {
            const data = this.dictData[dictCode]
            return (data && data.data) ? data.data : []
        },
        isTagSelected(field, code) {
            const selected = this.form[field]
            return Array.isArray(selected) && selected.includes(code)
        },
        boolText(v) {
            if (typeof v !== 'boolean') return '-'
            return v ? '是' : '否'
        },
        getDeliveryMethodLabel() {
            const opts = this.getDictOptions('DELIVERY_METHOD') || []
            const m = opts.find(o => String(o.code != null ? o.code : o.value) === String(this.form.deliveryMode))
            return m ? (m.name || m.label || '-') : '-'
        },
        formatDateOnly(v) {
            const s = v == null ? '' : String(v).trim()
            if (!s) return '-'
            if (s.length >= 10) return s.slice(0, 10)
            return s
        },
        safeText(v) {
            const s = v == null ? '' : String(v)
            return s.trim() ? s : '-'
        },
        async loadDetail() {
            if (!this.id) return
            try {
                const res = await salesmanQuotationDetailApi({ id: this.id })
                const d = (res && res.data) ? res.data : {}
                const materialFields = this.materialConditionConfigs.map(mc => mc.field)
                materialFields.forEach(f => {
                    const v = d[f]
                    d[f] = (typeof v === 'string' && v) ? v.split(',').filter(Boolean) : (Array.isArray(v) ? v : [])
                })
                this.form = d
                this.productLineList = Array.isArray(d.quotationLineList) ? d.quotationLineList : (Array.isArray(d.salesmanQuotationLineList) ? d.salesmanQuotationLineList : [])
            } catch (e) {
                uni.showToast({ title: '加载详情失败', icon: 'none' })
            }
        },
        handleButtonClick(btn) {
            if (!btn || btn.disabled) return
            const e = btn.event || ''
            if (e === 'edit') return this.onEdit()
            if (e === 'copy') return this.onCopy()
            if (e === 'approve') return this.onApprove()
            if (e === 'approveDetail') return this.onApproveDetail()
            if (e === 'cancel') return this.onCancel()
        },
        onEdit() {
            uni.navigateTo({ url: '/pages/sales-price/modify?id=' + this.id })
        },
        onCopy() {
            uni.navigateTo({ url: '/pages/sales-price/copy?id=' + this.id })
        },
        onCancel() {
            const id = this.id || (this.form && this.form.id) || ''
            if (!id) return
            uni.showModal({
                title: '系统提示',
                content: '是否确定取消该流程?',
                confirmText: '确定',
                cancelText: '取消',
                success: (res) => {
                    if (res && res.confirm) {
                        salesmanQuotationCancelApi({ id }).then(() => {
                            uni.showToast({ title: '已取消', icon: 'none' })
                            try { uni.setStorageSync('SALES_PRICE_LIST_NEED_REFRESH', '1') } catch (e) {}
                            setTimeout(() => { uni.redirectTo({ url: '/pages/supplier-price/index' }) }, 300)
                        }).catch((e) => {
                            uni.showToast({ title: (e && e.msg) || '取消失败', icon: 'none' })
                        })
                    }
                }
            })
        },
        onApprove() {
            const id = this.id || (this.form && this.form.id) || ''
            if (!id) return
            const CACHE_KEY = 'sourceBusinessId'
            const TYPE = 'contractType'
            uni.setStorageSync(TYPE, 'SALESMAN_QUOTATION')
            uni.setStorageSync(CACHE_KEY, id)
            uni.navigateTo({ url: '/pages/flow/audit' })
        },
        onApproveDetail() {
            const id = this.id || (this.form && this.form.id) || ''
            if (!id) return
            const CACHE_KEY = 'sourceBusinessId'
            const TYPE = 'contractType'
            uni.setStorageSync(TYPE, 'SALESMAN_QUOTATION')
            uni.setStorageSync(CACHE_KEY, id)
            uni.navigateTo({ url: '/pages/flow/audit_detail' })
        }
    }
}
</script>
<style lang="scss" scoped>
.page {
    display: flex;
    flex-direction: column;
    height: 100%;
}

.scroll {
    flex: 1;
    padding: 8rpx 0 144rpx;
}

.detail-page {
    background: #f3f3f3;
}

.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);
    }
}

.section {
    padding: 32rpx;
    background: #fff;
    margin-bottom: 20rpx;
}

.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;
    }
}

.line-block {
    padding-bottom: 24rpx;
    margin-bottom: 24rpx;
    border-bottom: 1rpx solid #f0f0f0;

    &:last-child {
        border-bottom: none;
        margin-bottom: 0;
        padding-bottom: 0;
    }
}

.material-group {
    margin-top: 32rpx;

    &:first-child {
        margin-top: 0;
    }
}

.material-label {
    display: block;
    font-size: 28rpx;
    color: rgba(0, 0, 0, 0.6);
    margin-bottom: 20rpx;
}

.tag-list {
    display: flex;
    flex-wrap: wrap;
    gap: 16rpx;
}

.tag-btn {
    padding: 12rpx 24rpx;
    border: 1rpx solid #ddd;
    border-radius: 8rpx;
    font-size: 28rpx;
    color: #999;
    background: #f5f5f5;
}

.tag-btn.active {
    background: $theme-primary;
    border-color: $theme-primary;
    color: #fff;
}

.remark-text {
    font-size: 28rpx;
    color: rgba(0, 0, 0, 0.9);
    line-height: 1.6;
    white-space: pre-wrap;
}

.empty-tip {
    text-align: center;
    font-size: 28rpx;
    color: rgba(0, 0, 0, 0.4);
    padding: 24rpx 0;
}
</style>