新增采购

This commit is contained in:
2026-06-27 10:40:54 +08:00
parent ce3998db74
commit 66d2b33db5
25 changed files with 1261 additions and 227 deletions

View File

@@ -32,6 +32,46 @@ export function delSupplier(ids) {
})
}
// 供应商往来流水
export function listSupplierTxn(supplierId) {
return request({
url: '/erp/supplierTxn/list',
method: 'get',
params: { supplierId }
})
}
export function supplierTxnSummary(supplierId) {
return request({
url: '/erp/supplierTxn/summary',
method: 'get',
params: { supplierId }
})
}
export function addSupplierTxn(data) {
return request({
url: '/erp/supplierTxn',
method: 'post',
data
})
}
export function updateSupplierTxn(data) {
return request({
url: '/erp/supplierTxn',
method: 'put',
data
})
}
export function delSupplierTxn(ids) {
return request({
url: `/erp/supplierTxn/${ids}`,
method: 'delete'
})
}
// 供应商价格
export function listSupplierPrice(query) {
return request({

View File

@@ -115,6 +115,31 @@ export function listDelivery(planId) {
})
}
// 到货记录页左侧:审核通过的计划
export function listDeliveryPlans(query) {
return request({
url: '/erp/purchasePlan/deliveryPlans',
method: 'get',
params: query
})
}
// 某计划的到货上传批次(每次上传一条)
export function listDeliveryBatches(planId) {
return request({
url: `/erp/purchasePlan/${planId}/deliveryBatches`,
method: 'get'
})
}
// 某批次的到货明细
export function listDeliveryByBatch(batchId) {
return request({
url: `/erp/purchasePlan/deliveryBatch/${batchId}`,
method: 'get'
})
}
// 删除到货明细
export function delDelivery(deliveryId) {
return request({

View File

@@ -0,0 +1,332 @@
<template>
<div class="pd-wb">
<div class="pd-main">
<!-- 审核通过的计划 -->
<aside class="pd-col pd-plans">
<div class="pd-col-tool">
<el-input
v-model="queryParams.keyword"
size="small"
clearable
placeholder="搜索计划号 / 合同号 / 供货商"
prefix-icon="el-icon-search"
@keyup.enter.native="handleQuery"
@clear="handleQuery"
/>
</div>
<ul class="pd-list" v-loading="loading">
<li
v-for="p in planList"
:key="p.planId"
class="pd-li"
:class="{ active: current.planId === p.planId }"
@click="selectPlan(p)"
>
<div class="pd-li-r1">
<span class="pd-no">{{ p.planNo }}</span>
<span class="pd-pct" :class="{ done: Number(p.progress) >= 100 }">{{ (Number(p.progress) || 0).toFixed(0) }}%</span>
</div>
<div class="pd-li-r2">{{ (p.contractCodes || []).join('、') || '无合同号' }}</div>
<el-progress :percentage="Number(p.progress) || 0" :stroke-width="4" :show-text="false" :color="progressColor" />
<div class="pd-li-r3">
<span>{{ p.supplier || '—' }}</span>
<span>{{ p.arrivedCount || 0 }}/{{ p.planQty || 0 }} <i v-if="p.planStatus === '1'" class="pd-arch">已齐</i></span>
</div>
</li>
<li v-if="!loading && !planList.length" class="pd-empty">暂无审核通过的计划</li>
</ul>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
:pager-count="5"
layout="prev, pager, next"
@pagination="getList"
/>
</aside>
<!-- 到货跟踪 -->
<section class="pd-col pd-detail">
<div v-if="!current.planId" class="pd-placeholder">
<i class="el-icon-truck"></i>
<p>从左侧选择一条审核通过的计划上传到货表格按卷号比对</p>
</div>
<div v-else>
<div class="pd-d-head">
<div>
<span class="pd-d-title">{{ current.planNo }}</span>
<span class="pd-badge" :class="current.planStatus === '1' ? 'p1' : 'p0'">{{ current.planStatus === '1' ? '已到齐' : '到货中' }}</span>
</div>
<el-upload
:headers="upload.headers"
:action="uploadUrl"
:show-file-list="false"
accept=".xlsx,.xls"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
>
<el-button type="primary" size="small" icon="el-icon-upload2">上传到货表格</el-button>
</el-upload>
</div>
<div class="pd-meta">
<div class="pd-meta-i"><label>合同号</label><span>{{ (current.contractCodes || []).join('、') || '—' }}</span></div>
<div class="pd-meta-i"><label>供货商</label><span>{{ current.supplier || '—' }}</span></div>
<div class="pd-meta-i"><label>已到货 / 要求</label><span>{{ current.arrivedCount || 0 }} / {{ current.planQty || 0 }} · {{ fmt(current.arrivedWeight) }} / {{ fmt(current.planWeight) }} T</span></div>
<div class="pd-meta-i wide">
<label>到货百分比按重量</label>
<el-progress
:percentage="Number(current.progress) || 0"
:stroke-width="14" :text-inside="true" :color="progressColor"
:format="p => p.toFixed(1) + '%'"
/>
</div>
<div class="pd-meta-i wide">
<label>到货百分比按卷数</label>
<el-progress
:percentage="Number(current.progressQty) || 0"
:stroke-width="14" :text-inside="true" color="#3a8a4d"
:format="p => p.toFixed(1) + '%'"
/>
</div>
</div>
<el-tabs v-model="activeTab" class="pd-tabs">
<el-tab-pane name="items">
<span slot="label">采购要求到货 重量{{ (Number(current.progress) || 0).toFixed(0) }}% / {{ (Number(current.progressQty) || 0).toFixed(0) }}%</span>
<p class="pd-req-tip">采购要求只说明买什么买多少到货按上传表格的卷号在钢卷表确认进度见上方与到货记录</p>
<el-table :data="current.items" border size="mini" max-height="420">
<el-table-column label="#" type="index" width="40" align="center" />
<el-table-column label="产品" prop="productType" min-width="70" show-overflow-tooltip />
<el-table-column label="材质" prop="material" min-width="78" show-overflow-tooltip />
<el-table-column label="牌号" prop="grade" min-width="72" />
<el-table-column label="宽度" prop="width" min-width="68" />
<el-table-column label="厚度" prop="thickness" min-width="68" />
<el-table-column label="宽公差" prop="widthTolerance" min-width="70" />
<el-table-column label="厚公差" prop="thicknessTolerance" min-width="70" />
<el-table-column label="重量(T)" prop="weight" min-width="76" align="right" />
<el-table-column label="数量" prop="quantity" min-width="58" align="right" />
<el-table-column label="供货商" prop="supplier" min-width="88" show-overflow-tooltip />
<template slot="empty"><span>无采购明细</span></template>
</el-table>
</el-tab-pane>
<el-tab-pane name="batches">
<span slot="label">到货记录{{ batches.length }} 次上传</span>
<div class="pd-batch-wrap">
<el-table
:data="batches"
border size="mini" max-height="200" v-loading="batchLoading"
highlight-current-row @row-click="viewBatch"
>
<el-table-column label="上传时间" prop="createTime" width="150" align="center" />
<el-table-column label="文件" prop="fileName" min-width="160" show-overflow-tooltip />
<el-table-column label="行数" prop="rowCount" width="64" align="center" />
<el-table-column label="匹配卷数" prop="matchedCount" width="84" align="center" />
<el-table-column label="上传后到货%" width="110" align="center">
<template slot-scope="s">{{ (Number(s.row.arrivedPercent) || 0).toFixed(0) }}%</template>
</el-table-column>
<el-table-column label="录入人" prop="createBy" width="90" align="center" />
<el-table-column label="操作" width="64" align="center">
<template slot-scope="s"><el-button type="text" size="mini" @click.stop="viewBatch(s.row)">查看</el-button></template>
</el-table-column>
<template slot="empty"><span>暂无上传记录点右上角上传到货表格</span></template>
</el-table>
<div class="pd-batch-rows" v-if="currentBatch.batchId">
<div class="pd-sub-title">{{ currentBatch.fileName }}到货明细卷号在钢卷表确认到货的高亮为已到货</div>
<el-table :data="batchRows" border stripe size="mini" max-height="300" v-loading="batchRowsLoading"
:row-class-name="rowClass">
<el-table-column label="日期" prop="arrivalDate" width="100" align="center" />
<el-table-column label="牌号" prop="grade" width="78" align="center" />
<el-table-column label="规格" prop="spec" width="105" align="center" />
<el-table-column label="卷号" prop="coilNo" width="120" align="center" />
<el-table-column label="单卷(T)" prop="coilWeight" width="78" align="right" />
<el-table-column label="车号" prop="truckNo" width="95" align="center" />
<el-table-column label="件数" prop="pieceCount" width="56" align="center" />
<el-table-column label="销售" prop="salesCode" width="80" align="center" />
<el-table-column label="到货" width="72" align="center">
<template slot-scope="s">
<span class="pd-mtag" :class="s.row.arrived === 1 ? 'yes' : 'no'">{{ s.row.arrived === 1 ? '已到货' : '未到' }}</span>
</template>
</el-table-column>
<template slot="empty"><span>该批次无明细</span></template>
</el-table>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</section>
</div>
</div>
</template>
<script>
import {
getPurchasePlan,
listDeliveryPlans,
listDeliveryBatches,
listDeliveryByBatch
} from '@/api/erp/purchasePlan'
import { getToken } from '@/utils/auth'
export default {
name: 'ErpPurchaseDelivery',
data() {
return {
loading: true,
total: 0,
planList: [],
queryParams: { pageNum: 1, pageSize: 20, keyword: undefined },
current: {},
activeTab: 'items',
batches: [],
batchLoading: false,
currentBatch: {},
batchRows: [],
batchRowsLoading: false,
upload: { headers: { Authorization: 'Bearer ' + getToken() } },
progressColor: '#5b8db8'
}
},
computed: {
uploadUrl() {
return process.env.VUE_APP_BASE_API + '/erp/purchasePlan/' + (this.current.planId || '') + '/importDelivery'
}
},
created() {
this.getList()
},
methods: {
getList(keepCurrent) {
this.loading = true
listDeliveryPlans(this.queryParams).then(res => {
this.planList = res.rows || []
this.total = res.total || 0
this.loading = false
if (!keepCurrent) {
if (this.planList.length) this.selectPlan(this.planList[0])
else this.current = {}
}
})
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
selectPlan(p) {
this.activeTab = 'items'
this.current = { ...p }
this.currentBatch = {}
this.batchRows = []
this.refreshDetail()
},
refreshDetail() {
const planId = this.current.planId
if (!planId) return
getPurchasePlan(planId).then(res => { this.current = { ...this.current, ...(res.data || {}) } })
this.loadBatches()
},
loadBatches() {
this.batchLoading = true
listDeliveryBatches(this.current.planId).then(res => {
this.batches = res.data || []
}).finally(() => { this.batchLoading = false })
},
viewBatch(b) {
this.currentBatch = { ...b }
this.batchRowsLoading = true
listDeliveryByBatch(b.batchId).then(res => { this.batchRows = res.data || [] }).finally(() => { this.batchRowsLoading = false })
},
rowClass({ row }) {
return row.arrived === 1 ? 'pd-row-matched' : ''
},
handleUploadSuccess(res) {
if (res.code === 200) {
const data = res.data || {}
if (data.kgConverted) {
this.$alert(res.msg, '导入完成(含单位纠正)', { dangerouslyUseHTMLString: true, type: 'warning' })
} else {
this.$modal.msgSuccess(res.msg || '导入成功')
}
this.activeTab = 'batches'
this.refreshDetail()
this.getList(true)
} else {
this.$alert(res.msg || '导入失败', '到货文件校验未通过', { dangerouslyUseHTMLString: true, type: 'error' })
}
},
handleUploadError() {
this.$modal.msgError('上传失败,请检查文件后重试')
},
fmt(v) {
return Number(v || 0).toLocaleString('zh-CN', { minimumFractionDigits: 0, maximumFractionDigits: 3 })
}
}
}
</script>
<style lang="scss" scoped>
$accent: #5b8db8;
$line: #e4e7ed;
$ink: #303133;
$sub: #909399;
.pd-wb { height: calc(100vh - 84px); display: flex; flex-direction: column; background: #f5f6f8; padding: 12px; box-sizing: border-box; }
.pd-main { flex: 1; display: flex; gap: 12px; min-height: 0; }
.pd-col { background: #fff; border: 1px solid $line; display: flex; flex-direction: column; min-height: 0; }
.pd-plans { width: 300px; flex-shrink: 0; }
.pd-detail { flex: 1; min-width: 0; overflow-y: auto; }
.pd-col-tool { padding: 10px; border-bottom: 1px solid $line; ::v-deep .el-input { width: 100%; } }
.pd-list { flex: 1; overflow-y: auto; margin: 0; padding: 0; list-style: none; }
.pd-li {
padding: 10px 12px; border-bottom: 1px solid #f0f2f5; cursor: pointer; border-left: 3px solid transparent;
&:hover { background: #f7f9fb; }
&.active { background: #eef3f8; border-left-color: $accent; }
}
.pd-li-r1 { display: flex; justify-content: space-between; align-items: center; }
.pd-no { font-size: 13px; font-weight: 600; color: $ink; }
.pd-pct { font-size: 12px; font-weight: 600; color: $sub; &.done { color: #3a8a4d; } }
.pd-li-r2 { font-size: 12px; color: $sub; margin: 4px 0 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pd-li-r3 { display: flex; justify-content: space-between; font-size: 11px; color: $sub; margin-top: 4px; }
.pd-arch { font-style: normal; color: #3a8a4d; margin-left: 6px; }
.pd-empty { text-align: center; color: $sub; padding: 36px 12px; font-size: 13px; }
.pd-placeholder {
height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; color: $sub;
i { font-size: 46px; margin-bottom: 12px; color: #d6dce1; }
p { font-size: 13px; }
}
.pd-d-head { display: flex; justify-content: space-between; align-items: center; padding: 14px 18px; border-bottom: 1px solid $line; }
.pd-d-title { font-size: 15px; font-weight: 600; color: $ink; margin-right: 8px; }
.pd-badge {
font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 2px; border: 1px solid #dcdfe6; color: $sub; background: #fafafa;
&.p1 { color: #3a8a4d; border-color: #b7d9bf; background: #f0f9f1; }
}
.pd-meta { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px 24px; padding: 16px 18px; }
.pd-meta-i {
display: flex; flex-direction: column; font-size: 13px;
label { color: $sub; font-size: 12px; margin-bottom: 3px; }
span { color: $ink; }
&.wide { grid-column: span 3; }
}
.pd-tabs { padding: 0 18px 18px; }
.pd-nocoil { color: #c0c4cc; }
.pd-req-tip { font-size: 12px; color: #909399; margin: 0 0 10px; line-height: 1.6; }
.pd-istat {
font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 2px; border: 1px solid #dcdfe6; color: $sub;
&.s2 { color: #3a8a4d; border-color: #b7d9bf; background: #f0f9f1; }
}
.pd-batch-rows { margin-top: 14px; }
.pd-sub-title { font-size: 13px; font-weight: 600; color: $ink; border-left: 3px solid $accent; padding-left: 8px; margin: 6px 0 10px; }
.pd-mtag {
font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 2px; border: 1px solid #dcdfe6; color: $sub;
&.yes { color: #3a8a4d; border-color: #b7d9bf; background: #f0f9f1; }
&.no { color: $sub; }
}
::v-deep .pd-row-matched { background: #f3f9f4; }
</style>

View File

@@ -126,9 +126,6 @@
<el-table-column label="牌号" min-width="75">
<template slot-scope="s"><el-input v-model="s.row.grade" size="mini" /></template>
</el-table-column>
<el-table-column label="卷号" min-width="95">
<template slot-scope="s"><el-input v-model="s.row.coilNo" size="mini" /></template>
</el-table-column>
<el-table-column label="宽度" min-width="75">
<template slot-scope="s"><el-input v-model="s.row.width" size="mini" /></template>
</el-table-column>
@@ -224,54 +221,16 @@
<el-table-column label="产品" prop="productType" min-width="70" show-overflow-tooltip />
<el-table-column label="材质" prop="material" min-width="80" show-overflow-tooltip />
<el-table-column label="牌号" prop="grade" min-width="75" />
<el-table-column label="卷号" prop="coilNo" min-width="95" show-overflow-tooltip />
<el-table-column label="宽度" prop="width" min-width="70" />
<el-table-column label="厚度" prop="thickness" min-width="70" />
<el-table-column label="宽公差" prop="widthTolerance" min-width="72" />
<el-table-column label="厚公差" prop="thicknessTolerance" min-width="72" />
<el-table-column label="重量(T)" prop="weight" min-width="78" align="right" />
<el-table-column label="数量" prop="quantity" min-width="60" align="right" />
<el-table-column label="已到货(T)" prop="arrivedWeight" min-width="82" align="right" />
<el-table-column label="到货状态" min-width="84" align="center">
<template slot-scope="s">
<span class="pp-istat" :class="'s' + (s.row.itemStatus || '0')">{{ itemStatusText(s.row.itemStatus) }}</span>
</template>
</el-table-column>
<el-table-column label="供货商" prop="supplier" min-width="90" show-overflow-tooltip />
<template slot="empty"><span>无明细</span></template>
</el-table>
</el-tab-pane>
<el-tab-pane label="到货进度" name="delivery">
<div class="pp-deliv-bar">
<span class="pp-deliv-tip" v-if="current.auditStatus !== '1'">审核通过后才能上传到货 Excel</span>
<el-upload
v-else
:headers="upload.headers"
:action="uploadUrl"
:show-file-list="false"
accept=".xlsx,.xls"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
>
<el-button type="primary" size="small" icon="el-icon-upload2">上传到货 Excel</el-button>
</el-upload>
<span class="pp-deliv-hint">上传后按牌号+规格回填明细到货量与状态</span>
</div>
<el-table :data="deliveryList" border stripe size="mini" max-height="340" v-loading="deliveryLoading">
<el-table-column label="日期" prop="arrivalDate" width="100" align="center" />
<el-table-column label="牌号" prop="grade" width="80" align="center" />
<el-table-column label="规格" prop="spec" width="110" align="center" />
<el-table-column label="卷号" prop="coilNo" width="110" align="center" />
<el-table-column label="单卷(T)" prop="coilWeight" width="80" align="right" />
<el-table-column label="车号" prop="truckNo" width="100" align="center" />
<el-table-column label="整车(T)" prop="truckWeight" width="80" align="right" />
<el-table-column label="件数" prop="pieceCount" width="56" align="center" />
<el-table-column label="销售" prop="salesCode" width="85" align="center" />
<el-table-column label="到站" prop="arrivalStation" width="90" align="center" />
<el-table-column label="" width="36" align="center">
<template slot-scope="s"><i class="el-icon-delete pp-del" @click="removeDelivery(s.row)" /></template>
</el-table-column>
<template slot="empty"><span>暂无到货记录</span></template>
</el-table>
<p class="pp-deliv-hint" v-if="current.auditStatus === '1'">到货上传与卷号比对请到到货记录页面</p>
</el-tab-pane>
</el-tabs>
</div>
@@ -370,7 +329,6 @@
<el-col :span="12"><el-form-item label="产品"><el-input v-model="batchFill.productType" placeholder="如 热轧卷板" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="材质"><el-input v-model="batchFill.material" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="牌号"><el-input v-model="batchFill.grade" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="卷号"><el-input v-model="batchFill.coilNo" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="宽度"><el-input v-model="batchFill.width" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="厚度"><el-input v-model="batchFill.thickness" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="宽公差"><el-input v-model="batchFill.widthTolerance" /></el-form-item></el-col>
@@ -408,12 +366,9 @@ import {
updatePurchasePlan,
delPurchasePlan,
submitPurchasePlan,
listDelivery,
delDelivery,
listContracts
} from '@/api/erp/purchasePlan'
import { listSupplier } from '@/api/erp/purchase'
import { getToken } from '@/utils/auth'
export default {
name: 'ErpPurchasePlan',
@@ -436,7 +391,7 @@ export default {
batchFillOpen: false,
batchFillNewRow: true,
batchRows: 0,
batchFill: { productType: '热轧卷板', material: '', grade: '', coilNo: '', width: '', thickness: '', widthTolerance: '', thicknessTolerance: '', weight: '', quantity: '', supplier: '' },
batchFill: { productType: '热轧卷板', material: '', grade: '', width: '', thickness: '', widthTolerance: '', thicknessTolerance: '', weight: '', quantity: '', supplier: '' },
newRowDefaults: null,
// 合同选择器
pickerOpen: false,
@@ -451,17 +406,10 @@ export default {
supplierList: [],
supplierTotal: 0,
supplierQuery: { pageNum: 1, pageSize: 10, name: undefined },
// 到货
deliveryList: [],
deliveryLoading: false,
upload: { headers: { Authorization: 'Bearer ' + getToken() } },
progressColor: '#5b8db8'
}
},
computed: {
uploadUrl() {
return process.env.VUE_APP_BASE_API + '/erp/purchasePlan/' + (this.current.planId || '') + '/importDelivery'
},
itemsWeight() {
return (this.form.items || []).reduce((s, i) => s + (Number(i.weight) || 0), 0).toFixed(3)
}
@@ -497,15 +445,13 @@ export default {
const planId = this.current.planId
if (!planId) return
getPurchasePlan(planId).then(res => { this.current = { ...this.current, ...(res.data || {}) } })
this.deliveryLoading = true
listDelivery(planId).then(res => { this.deliveryList = res.data || [] }).finally(() => { this.deliveryLoading = false })
},
// ---- 新增 / 编辑 ----
resetForm() {
this.form = { planId: null, planNo: '', supplier: '', purchaseDate: '', remark: '', items: [], orderIds: [], contractCodes: [] }
this.selectedContracts = []
this.newRowDefaults = null
this.batchFill = { productType: '热轧卷板', material: '', grade: '', coilNo: '', width: '', thickness: '', widthTolerance: '', thicknessTolerance: '', weight: '', quantity: '', supplier: '' }
this.batchFill = { productType: '热轧卷板', material: '', grade: '', width: '', thickness: '', widthTolerance: '', thicknessTolerance: '', weight: '', quantity: '', supplier: '' }
this.batchRows = 0
},
handleAdd() {
@@ -605,7 +551,6 @@ export default {
productType: it.productType || '热轧卷板',
material: it.material || '',
grade: it.grade || '',
coilNo: '',
width: it.width || '',
thickness: it.thickness || '',
widthTolerance: it.widthTolerance || '0',
@@ -618,7 +563,7 @@ export default {
})
},
blankItem() {
return { productType: '热轧卷板', material: '', grade: '', coilNo: '', width: '', thickness: '', widthTolerance: '0', thicknessTolerance: '0', weight: '', quantity: '', supplier: '' }
return { productType: '热轧卷板', material: '', grade: '', width: '', thickness: '', widthTolerance: '0', thicknessTolerance: '0', weight: '', quantity: '', supplier: '' }
},
addItem() {
// 优先用批量默认值,其次继承上一行,最后空行;重量每行不同故清空
@@ -641,7 +586,7 @@ export default {
},
batchFillKeys() {
const f = this.batchFill
const keys = ['productType', 'material', 'grade', 'coilNo', 'width', 'thickness', 'widthTolerance', 'thicknessTolerance', 'weight', 'quantity', 'supplier']
const keys = ['productType', 'material', 'grade', 'width', 'thickness', 'widthTolerance', 'thicknessTolerance', 'weight', 'quantity', 'supplier']
return keys.filter(k => f[k] !== '' && f[k] != null)
},
applyBatchFill() {
@@ -719,38 +664,11 @@ export default {
this.getList()
}).catch(() => {})
},
// ---- 到货 ----
handleUploadSuccess(res) {
if (res.code === 200) {
const data = res.data || {}
if (data.kgConverted) {
this.$alert(res.msg, '导入完成(含单位纠正)', { dangerouslyUseHTMLString: true, type: 'warning' })
} else {
this.$modal.msgSuccess(res.msg || '导入成功')
}
this.refreshDetail()
this.getList(true)
} else {
this.$alert(res.msg || '导入失败', '到货文件校验未通过', { dangerouslyUseHTMLString: true, type: 'error' })
}
},
handleUploadError() {
this.$modal.msgError('上传失败,请检查文件后重试')
},
removeDelivery(row) {
this.$modal.confirm('确定删除该到货记录吗?').then(() => {
return delDelivery(row.deliveryId)
}).then(() => {
this.$modal.msgSuccess('删除成功')
this.refreshDetail()
this.getList(true)
}).catch(() => {})
},
auditText(s) {
return { '0': '待审核', '1': '已通过', '2': '已驳回', '3': '待送审' }[s] || '—'
},
itemStatusText(s) {
return { '0': '未到货', '1': '部分到货', '2': '已到货' }[s] || '未到货'
return s === '2' ? '已到货' : '未到货'
}
}
}

View File

@@ -45,8 +45,9 @@
<el-table-column label="联系电话" prop="contactPhone" width="130" />
<el-table-column label="地址" prop="address" min-width="180" show-overflow-tooltip />
<el-table-column label="备注" prop="remark" min-width="140" show-overflow-tooltip />
<el-table-column label="操作" width="130" align="center" fixed="right">
<el-table-column label="操作" width="190" align="center" fixed="right">
<template slot-scope="s">
<el-button type="text" size="mini" icon="el-icon-notebook-2" @click="openTxn(s.row)" v-hasPermi="['erp:supplier:list']">往来</el-button>
<el-button type="text" size="mini" icon="el-icon-edit" @click="handleUpdate(s.row)" v-hasPermi="['erp:supplier:edit']">编辑</el-button>
<el-button type="text" size="mini" icon="el-icon-delete" @click="handleDelete(s.row)" v-hasPermi="['erp:supplier:remove']">删除</el-button>
</template>
@@ -115,11 +116,67 @@
<el-button type="primary" :loading="buttonLoading" @click="submitForm">确定</el-button>
</div>
</el-dialog>
<!-- 往来流水台账 -->
<el-dialog :title="'往来流水 · ' + (txnSupplier.name || '')" :visible.sync="txnOpen" width="860px" append-to-body>
<div class="sp-txn-sum">
<div class="sp-sum-i"><label>采购应付合计</label><span>{{ fmt(txnSum.payable) }}</span></div>
<div class="sp-sum-i"><label>付款合计</label><span>{{ fmt(txnSum.paid) }}</span></div>
<div class="sp-sum-i"><label>退货合计</label><span>{{ fmt(txnSum.returned) }}</span></div>
<div class="sp-sum-i bal"><label>当前应付余额</label><span :class="{ neg: Number(txnSum.balance) < 0 }">{{ fmt(txnSum.balance) }}</span></div>
</div>
<el-form :model="txnForm" :inline="true" size="small" class="sp-txn-form">
<el-form-item label="日期">
<el-date-picker v-model="txnForm.txnDate" type="date" value-format="yyyy-MM-dd" placeholder="日期" style="width:140px" />
</el-form-item>
<el-form-item label="类型">
<el-select v-model="txnForm.txnType" style="width:120px">
<el-option v-for="t in txnTypeOptions" :key="t.value" :label="t.label" :value="t.value" />
</el-select>
</el-form-item>
<el-form-item label="金额">
<el-input v-model="txnForm.amount" placeholder="金额" style="width:120px" />
</el-form-item>
<el-form-item label="单据号">
<el-input v-model="txnForm.docNo" placeholder="单据号" style="width:130px" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="txnForm.remark" placeholder="备注" style="width:140px" />
</el-form-item>
<el-form-item>
<el-button type="primary" size="mini" icon="el-icon-plus" :loading="txnSaving" @click="addTxn">记一笔</el-button>
</el-form-item>
</el-form>
<el-table :data="txnList" border size="mini" max-height="360" v-loading="txnLoading">
<el-table-column label="日期" prop="txnDate" width="110" align="center" />
<el-table-column label="类型" width="90" align="center">
<template slot-scope="s"><span class="sp-txn-tag" :class="'t' + s.row.txnType">{{ txnTypeText(s.row.txnType) }}</span></template>
</el-table-column>
<el-table-column label="金额" prop="amount" width="110" align="right">
<template slot-scope="s">{{ fmt(s.row.amount) }}</template>
</el-table-column>
<el-table-column label="应付余额" prop="balance" width="120" align="right">
<template slot-scope="s"><span :class="{ neg: Number(s.row.balance) < 0 }">{{ fmt(s.row.balance) }}</span></template>
</el-table-column>
<el-table-column label="单据号" prop="docNo" width="120" show-overflow-tooltip />
<el-table-column label="备注" prop="remark" min-width="120" show-overflow-tooltip />
<el-table-column label="录入人" prop="createBy" width="90" align="center" />
<el-table-column label="操作" width="60" align="center">
<template slot-scope="s"><i class="el-icon-delete sp-del" @click="delTxn(s.row)" /></template>
</el-table-column>
<template slot="empty"><span>暂无往来记录</span></template>
</el-table>
</el-dialog>
</div>
</template>
<script>
import { listSupplier, addSupplier, updateSupplier, delSupplier } from '@/api/erp/purchase'
import {
listSupplier, addSupplier, updateSupplier, delSupplier,
listSupplierTxn, supplierTxnSummary, addSupplierTxn, delSupplierTxn
} from '@/api/erp/purchase'
export default {
name: 'ErpSupplier',
@@ -145,7 +202,21 @@ export default {
rules: {
supplierCode: [{ required: true, message: '请输入供应商编码', trigger: 'blur' }],
name: [{ required: true, message: '请输入供应商名称', trigger: 'blur' }]
}
},
// 往来流水
txnOpen: false,
txnLoading: false,
txnSaving: false,
txnSupplier: {},
txnList: [],
txnSum: { payable: 0, paid: 0, returned: 0, balance: 0 },
txnForm: { txnDate: '', txnType: '1', amount: '', docNo: '', remark: '' },
txnTypeOptions: [
{ value: '1', label: '采购应付' },
{ value: '2', label: '付款' },
{ value: '3', label: '退货' },
{ value: '4', label: '其他' }
]
}
},
created() {
@@ -215,6 +286,55 @@ export default {
typeText(t) {
const o = this.typeOptions.find(x => x.value === t)
return o ? o.label : (t || '—')
},
// ---- 往来流水 ----
openTxn(row) {
this.txnSupplier = { ...row }
this.txnForm = { txnDate: this.today(), txnType: '1', amount: '', docNo: '', remark: '' }
this.txnOpen = true
this.loadTxn()
},
loadTxn() {
const id = this.txnSupplier.supplierId
this.txnLoading = true
listSupplierTxn(id).then(res => { this.txnList = res.data || [] }).finally(() => { this.txnLoading = false })
supplierTxnSummary(id).then(res => { this.txnSum = res.data || { payable: 0, paid: 0, returned: 0, balance: 0 } })
},
addTxn() {
if (this.txnForm.amount === '' || isNaN(Number(this.txnForm.amount))) {
this.$modal.msgWarning('请输入正确的金额')
return
}
this.txnSaving = true
addSupplierTxn({ ...this.txnForm, supplierId: this.txnSupplier.supplierId }).then(() => {
this.$modal.msgSuccess('已记账')
this.txnForm.amount = ''
this.txnForm.docNo = ''
this.txnForm.remark = ''
this.loadTxn()
}).finally(() => { this.txnSaving = false })
},
delTxn(row) {
this.$modal.confirm('确认删除该往来记录?').then(() => {
return delSupplierTxn(row.txnId)
}).then(() => {
this.$modal.msgSuccess('删除成功')
this.loadTxn()
}).catch(() => {})
},
txnTypeText(t) {
const o = this.txnTypeOptions.find(x => x.value === t)
return o ? o.label : '—'
},
fmt(v) {
const n = Number(v || 0)
return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
},
today() {
const d = new Date()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${d.getFullYear()}-${m}-${day}`
}
}
}
@@ -229,4 +349,22 @@ $accent: #5b8db8;
&.cC { color: #d6a256; }
&.cD { color: #c45656; }
}
/* 往来流水 */
.sp-txn-sum { display: flex; gap: 10px; margin-bottom: 12px; }
.sp-sum-i {
flex: 1; border: 1px solid #e4e7ed; border-radius: 3px; padding: 8px 12px; background: #fafbfc;
label { display: block; color: #909399; font-size: 12px; margin-bottom: 4px; }
span { font-size: 16px; font-weight: 600; color: #303133; }
&.bal { background: #eef3f8; border-color: #b9d2e6; span { color: $accent; } }
span.neg { color: #3a8a4d; }
}
.sp-txn-form { margin-bottom: 4px; ::v-deep .el-form-item { margin-bottom: 8px; } }
.sp-txn-tag {
font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 2px; border: 1px solid #dcdfe6; color: #909399;
&.t1 { color: #d6a256; border-color: #ecd4a6; background: #fdf6ec; }
&.t2 { color: #3a8a4d; border-color: #b7d9bf; background: #f0f9f1; }
&.t3 { color: #c45656; border-color: #e6c4c4; background: #fbf0f0; }
}
.sp-del { color: #c45656; cursor: pointer; }
</style>