Files
klp-oa/klp-ui/src/views/erp/supplier/index.vue
2026-06-27 10:40:54 +08:00

371 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" size="small">
<el-form-item label="编码" prop="supplierCode">
<el-input v-model="queryParams.supplierCode" clearable placeholder="供应商编码" style="width:150px" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="queryParams.name" clearable placeholder="供应商名称" style="width:170px" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="queryParams.type" clearable placeholder="全部" style="width:140px">
<el-option v-for="t in typeOptions" :key="t.value" :label="t.label" :value="t.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['erp:supplier:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete()" v-hasPermi="['erp:supplier:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
</el-row>
<el-table v-loading="loading" :data="list" border size="small" @selection-change="onSelectionChange">
<el-table-column type="selection" width="46" align="center" />
<el-table-column label="供应商编码" prop="supplierCode" width="140" show-overflow-tooltip />
<el-table-column label="供应商名称" prop="name" min-width="180" show-overflow-tooltip />
<el-table-column label="类型" width="120" align="center">
<template slot-scope="s">{{ typeText(s.row.type) }}</template>
</el-table-column>
<el-table-column label="信用等级" width="90" align="center">
<template slot-scope="s"><span class="sp-credit" :class="'c' + (s.row.creditRating || '')">{{ s.row.creditRating || '—' }}</span></template>
</el-table-column>
<el-table-column label="联系人" prop="contactPerson" width="100" />
<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="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>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<el-dialog :title="title" :visible.sync="open" width="640px" append-to-body>
<el-form :model="form" :rules="rules" ref="form" label-width="90px" size="small">
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="供应商编码" prop="supplierCode">
<el-input v-model="form.supplierCode" placeholder="请输入编码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="供应商名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="类型" prop="type">
<el-select v-model="form.type" clearable placeholder="请选择" style="width:100%">
<el-option v-for="t in typeOptions" :key="t.value" :label="t.label" :value="t.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="信用等级" prop="creditRating">
<el-select v-model="form.creditRating" clearable placeholder="请选择" style="width:100%">
<el-option v-for="c in creditOptions" :key="c" :label="c" :value="c" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系人" prop="contactPerson">
<el-input v-model="form.contactPerson" placeholder="请输入联系人" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系电话" prop="contactPhone">
<el-input v-model="form.contactPhone" placeholder="请输入电话" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="地址" prop="address">
<el-input v-model="form.address" placeholder="请输入地址" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" :rows="2" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer">
<el-button @click="open = false">取消</el-button>
<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,
listSupplierTxn, supplierTxnSummary, addSupplierTxn, delSupplierTxn
} from '@/api/erp/purchase'
export default {
name: 'ErpSupplier',
data() {
return {
loading: true,
buttonLoading: false,
showSearch: true,
total: 0,
list: [],
ids: [],
multiple: true,
title: '',
open: false,
form: {},
queryParams: { pageNum: 1, pageSize: 20, supplierCode: undefined, name: undefined, type: undefined },
typeOptions: [
{ value: 'RAW', label: '原料供应商' },
{ value: 'AUX', label: '辅料供应商' },
{ value: 'OTHER', label: '其他' }
],
creditOptions: ['A', 'B', 'C', 'D'],
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() {
this.getList()
},
methods: {
getList() {
this.loading = true
listSupplier(this.queryParams).then(res => {
this.list = res.rows || []
this.total = res.total || 0
this.loading = false
})
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.resetForm('queryForm')
this.handleQuery()
},
onSelectionChange(sel) {
this.ids = sel.map(i => i.supplierId)
this.multiple = !sel.length
},
reset() {
this.form = { supplierId: null, supplierCode: '', name: '', type: undefined, creditRating: undefined, contactPerson: '', contactPhone: '', address: '', remark: '' }
this.resetForm('form')
},
handleAdd() {
this.reset()
this.title = '新增供应商'
this.open = true
},
handleUpdate(row) {
this.reset()
this.form = { ...row }
this.title = '编辑供应商'
this.open = true
},
submitForm() {
this.$refs['form'].validate(valid => {
if (!valid) return
this.buttonLoading = true
const api = this.form.supplierId ? updateSupplier : addSupplier
api(this.form).then(() => {
this.$modal.msgSuccess('保存成功')
this.open = false
this.getList()
}).finally(() => { this.buttonLoading = false })
})
},
handleDelete(row) {
const ids = row && row.supplierId ? row.supplierId : this.ids
const tip = row && row.name ? '「' + row.name + '」' : '选中的 ' + this.ids.length + ' 个供应商'
this.$modal.confirm('确认删除' + tip + '').then(() => {
return delSupplier(ids)
}).then(() => {
this.$modal.msgSuccess('删除成功')
this.getList()
}).catch(() => {})
},
handleExport() {
this.download('erp/supplier/export', { ...this.queryParams }, `supplier_${new Date().getTime()}.xlsx`)
},
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}`
}
}
}
</script>
<style lang="scss" scoped>
$accent: #5b8db8;
.sp-credit {
display: inline-block; min-width: 18px; font-size: 12px; font-weight: 600; color: #909399;
&.cA { color: #3a8a4d; }
&.cB { color: $accent; }
&.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>