feat: 财务单据完善

This commit is contained in:
砂糖
2025-08-16 14:14:57 +08:00
parent 2e1d4f7a6f
commit e5137b4c0f
6 changed files with 219 additions and 83 deletions

View File

@@ -38,6 +38,7 @@
}, },
methods: { methods: {
getFinancialAccounts() { getFinancialAccounts() {
console.log(this.financialAccounts, 'financialAccounts');
this.options = this.financialAccounts.map(item => ({ this.options = this.financialAccounts.map(item => ({
label: item.accountName, label: item.accountName,
value: item.accountId value: item.accountId

View File

@@ -13,7 +13,8 @@ const mutations = {
const actions = { const actions = {
getFinancialAccounts({ commit }) { getFinancialAccounts({ commit }) {
listAccount({ pageSize: 1000 }).then(response => { listAccount({ pageSize: 1000 }).then(response => {
commit('SET_FINANCIAL_ACCOUNTS', response.rows); console.log(response.data, 'finance.response.rows');
commit('SET_FINANCIAL_ACCOUNTS', response.data);
}); });
} }
} }

View File

@@ -0,0 +1,124 @@
<template>
<div class="finance-voucher-component">
<div
v-for="(voucher, index) in voucherData"
:key="index"
class="voucher-item"
>
<!-- 主单据信息行 -->
<div class="voucher-header">
<div class="header-cell">
<el-checkbox v-model="voucher.checked"></el-checkbox>
</div>
<div class="header-cell">日期: {{ formatDate(voucher.docDate) }}</div>
<div class="header-cell">单据编号: {{ voucher.docNo }}</div>
<div class="header-cell">单据类型: {{ voucher.docType }}</div>
<div class="header-cell operation-group">
<el-button type="text" size="mini" @click.stop="handleView(voucher)">查看</el-button>
<el-button type="text" size="mini" @click.stop="handleDelete(voucher)">删除</el-button>
</div>
</div>
<!-- 凭证明细表格 -->
<el-table :data="voucher.detailList || []" border style="width: 100%;">
<el-table-column label="摘要" prop="voucherNo" />
<el-table-column label="科目" prop="accountId" />
<el-table-column label="借方金额" prop="debitAmount" align="right" />
<el-table-column label="贷方金额" prop="creditAmount" align="right" />
</el-table>
<!-- 总计行 -->
<div class="total-row">
<div class="total-cell" :colspan="3">总计</div>
<div class="total-cell" align="right">{{ calculateTotal(voucher.detailList, 'debitAmount') }}</div>
<div class="total-cell" align="right">{{ calculateTotal(voucher.detailList, 'creditAmount') }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "FinanceVoucherComponent",
props: {
voucherData: {
type: Array,
default: () => [],
},
},
data() {
return {
// 用于存储选中状态,若父组件需控制,可改为 prop 或 sync 修饰符
internalVoucherData: [],
};
},
watch: {
voucherData: {
handler(val) {
this.internalVoucherData = val.map((v) => ({
...v,
checked: false,
}));
},
immediate: true,
},
},
methods: {
handleView(voucher) {
this.$emit("view-voucher", voucher);
},
handleDelete(voucher) {
this.$emit("delete-voucher", voucher);
},
formatDate(dateStr) {
if (!dateStr) return "";
return new Date(dateStr).toLocaleDateString();
},
calculateTotal(detailList, field) {
if (!detailList || detailList.length === 0) return "0.00";
return detailList.reduce(
(sum, item) => sum + parseFloat(item[field] || 0),
0
).toFixed(2);
},
},
};
</script>
<style scoped>
.finance-voucher-component {
padding: 16px;
}
.voucher-item {
border: 1px solid #e6e6e6;
border-radius: 4px;
margin-bottom: 16px;
}
.voucher-header {
display: flex;
align-items: center;
background-color: #f5f5f5;
padding: 8px 16px;
}
.header-cell {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.operation-group {
display: flex;
gap: 8px;
}
.total-row {
display: flex;
align-items: center;
background-color: #f0f9ff;
padding: 8px 16px;
font-weight: bold;
}
.total-cell {
flex: 1;
text-align: left;
}
</style>

View File

@@ -13,6 +13,11 @@
<el-form-item label="关联订单" prop="relatedOrderId"> <el-form-item label="关联订单" prop="relatedOrderId">
<el-input v-model="form.relatedOrderId" placeholder="请输入关联订单" /> <el-input v-model="form.relatedOrderId" placeholder="请输入关联订单" />
</el-form-item> </el-form-item>
<el-form-item label="单据类型" prop="docType">
<el-select v-model="form.docType" placeholder="请选择单据类型">
<el-option v-for="item in dict.type.finance_voucher_type" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-form> </el-form>
</el-row> </el-row>
@@ -94,18 +99,20 @@
<script> <script>
import AmountSelect from '@/components/KLPService/AmountSelect/index.vue'; import AmountSelect from '@/components/KLPService/AmountSelect/index.vue';
import { addFinancialDocumentWithDetail, updateFinancialDocument } from "@/api/finance/financialDocument"; import { addFinancialDocumentWithDetail, updateFinancialDocument } from "@/api/finance/financialDocument";
import { updateJournalEntry } from "@/api/finance/jouneryEntry"; import { updateJournalEntry, addJournalEntry } from "@/api/finance/jouneryEntry";
export default { export default {
components: { components: {
AmountSelect AmountSelect
}, },
dicts: ['finance_voucher_type'],
data() { data() {
return { return {
form: { form: {
docNo: undefined, docNo: undefined,
docDate: undefined, docDate: undefined,
relatedOrderId: undefined relatedOrderId: undefined,
docType: undefined
}, },
tableData: [{ tableData: [{
voucherNo: '', voucherNo: '',
@@ -167,10 +174,18 @@ export default {
initData: { initData: {
handler(newVal) { handler(newVal) {
console.log(newVal, 'watchData'); console.log(newVal, 'watchData');
if (newVal) { if (newVal && newVal.detailList) {
if (newVal.detailList) {
this.tableData = newVal.detailList; this.tableData = newVal.detailList;
this.form.docNo = newVal.docNo;
this.form.docDate = newVal.docDate;
this.form.relatedOrderId = newVal.relatedOrderId;
this.form.docType = newVal.docType;
this.isCreate = false;
} else { } else {
this.form.docNo = undefined;
this.form.docDate = undefined;
this.form.relatedOrderId = undefined;
this.form.docType = undefined;
this.tableData = [{ this.tableData = [{
voucherNo: '', voucherNo: '',
accountId: undefined, accountId: undefined,
@@ -179,12 +194,6 @@ export default {
remark: '', remark: '',
voucherNo: '' voucherNo: ''
}] }]
}
this.form.docNo = newVal.docNo;
this.form.docDate = newVal.docDate;
this.form.relatedOrderId = newVal.relatedOrderId;
this.isCreate = false;
} else {
this.isCreate = true; this.isCreate = true;
} }
}, },
@@ -312,6 +321,38 @@ export default {
}, },
handleCreate() { handleCreate() {
if (!this.validateTableData()) {
return;
}
// 6. 提交数据
addFinancialDocumentWithDetail({
docNo: form.docNo,
docDate: form.docDate,
// 保持变量名一致
relatedOrderId: form.relatedOrderId,
amount: this.debitAmount,
details: validData.map((row, idx) => ({
// voucherNo: row.voucherNo,
voucherNo: row.voucherNo,
// 保持变量名一致
accountId: row.accountId,
debitAmount: row.debitAmount,
creditAmount: row.creditAmount,
remark: row.remark,
lineNo: idx + 1,
entryDate: form.docDate
}))
}).then(response => {
this.$message.success('凭证创建成功');
this.$emit('success');
}).catch(error => {
this.$message.error('凭证创建失败:' + (error.message || '未知错误'));
this.$emit('error', error);
});
},
validateTableData() {
// 收集所有错误信息 // 收集所有错误信息
const errors = []; const errors = [];
const { form, tableData } = this; const { form, tableData } = this;
@@ -321,6 +362,7 @@ export default {
if (!form.docDate) errors.push('单据日期必填'); if (!form.docDate) errors.push('单据日期必填');
// 注意原模板中是relatedOrderId保持变量名一致 // 注意原模板中是relatedOrderId保持变量名一致
if (!form.relatedOrderId) errors.push('关联订单必填'); if (!form.relatedOrderId) errors.push('关联订单必填');
if (!form.docType) errors.push('单据类型必填');
// 2. 过滤有效行并检查基本存在性 // 2. 过滤有效行并检查基本存在性
const validData = tableData.filter(row => this.isRowNotEmpty(row)); const validData = tableData.filter(row => this.isRowNotEmpty(row));
@@ -365,47 +407,37 @@ export default {
message: errors.map(error => `<p>${error}</p>`).join(''), message: errors.map(error => `<p>${error}</p>`).join(''),
type: 'error' type: 'error'
}) })
return; return false;
} }
return true;
// 6. 提交数据
addFinancialDocumentWithDetail({
docNo: form.docNo,
docDate: form.docDate,
// 保持变量名一致
relatedOrderId: form.relatedOrderId,
amount: this.debitAmount,
details: validData.map((row, idx) => ({
// voucherNo: row.voucherNo,
voucherNo: row.voucherNo,
// 保持变量名一致
accountId: row.accountId,
debitAmount: row.debitAmount,
creditAmount: row.creditAmount,
remark: row.remark,
lineNo: idx + 1,
entryDate: form.docDate
}))
}).then(response => {
this.$message.success('凭证创建成功');
this.$emit('success');
}).catch(error => {
this.$message.error('凭证创建失败:' + (error.message || '未知错误'));
this.$emit('error', error);
});
}, },
handleEdit(index) { handleEdit(index) {
// 找到对应的列变更 // 找到对应的列变更
const row = this.tableData[index]; const row = this.tableData[index];
if (row.entryId) {
updateJournalEntry(row).then(response => { updateJournalEntry(row).then(response => {
this.$message.success('明细变更成功'); this.$message.success('明细变更成功');
}).catch(error => { }).catch(error => {
this.$message.error('明细变更失败:' + (error.message || '未知错误')); this.$message.error('明细变更失败:' + (error.message || '未知错误'));
}); });
} else {
addJournalEntry({
...row,
documentId: this.initData.documentId,
}).then(response => {
this.$message.success('明细变更成功');
}).catch(error => {
this.$message.error('明细变更失败:' + (error.message || '未知错误'));
})
}
}, },
handleChange() { handleChange() {
if (!this.validateTableData()) {
return;
}
updateFinancialDocument({ updateFinancialDocument({
documentId: this.initData.documentId, documentId: this.initData.documentId,
docNo: this.form.docNo, docNo: this.form.docNo,

View File

@@ -73,37 +73,11 @@
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
<el-table v-loading="loading" :data="financialDocumentList" @selection-change="handleSelectionChange"> <FinanceVoucherTable
<el-table-column type="selection" width="55" align="center" /> :voucher-data="financialDocumentList"
<el-table-column label="单据ID" align="center" prop="documentId" v-if="true"/> @view-voucher="handleUpdate"
<el-table-column label="单据编号" align="center" prop="docNo" /> @delete-voucher="handleDelete"
<el-table-column label="单据类型" align="center" prop="docType" /> />
<el-table-column label="单据日期" align="center" prop="docDate" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.docDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="单据金额" align="center" prop="amount" />
<el-table-column label="关联订单ID" align="center" prop="relatedOrderId" />
<el-table-column label="单据状态" align="center" prop="status" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination <pagination
v-show="total > 0" v-show="total > 0"
@@ -123,11 +97,13 @@
<script> <script>
import { listFinancialDocumentWithDetail, getFinancialDocument, delFinancialDocument, addFinancialDocument, updateFinancialDocument } from "@/api/finance/financialDocument"; import { listFinancialDocumentWithDetail, getFinancialDocument, delFinancialDocument, addFinancialDocument, updateFinancialDocument } from "@/api/finance/financialDocument";
import CreateDocument from "./components/Voucher.vue"; import CreateDocument from "./components/Voucher.vue";
import FinanceVoucherTable from "./components/FinanceVoucherTable.vue"; // 引入定制表格
export default { export default {
name: "FinancialDocument", name: "FinancialDocument",
components: { components: {
CreateDocument CreateDocument,
FinanceVoucherTable
}, },
data() { data() {
return { return {
@@ -227,6 +203,8 @@ export default {
handleAdd() { handleAdd() {
this.reset(); this.reset();
this.open = true; this.open = true;
this.currentRow = {}
// this.isCreate = true;
this.title = "添加财务单据"; this.title = "添加财务单据";
}, },
/** 修改按钮操作 */ /** 修改按钮操作 */

View File

@@ -1,10 +1,10 @@
<template> <template>
<div style="position: relative; padding-top: 60px;"> <div style="position: relative;">
<div style="position: absolute; top: 10px; left: 10px;"> <div style="position: absolute; top: 10px; left: 10px;">
<!-- 策略切换单选框 --> <!-- 策略切换单选框 -->
<el-radio-group <el-radio-group
v-model="currentStrategy" v-model="currentStrategy"
size="small" size="mini"
> >
<el-radio label="stockIo">盘点单</el-radio> <el-radio label="stockIo">盘点单</el-radio>
<el-radio label="order">订单</el-radio> <el-radio label="order">订单</el-radio>
@@ -16,6 +16,7 @@
v-model="currentId" v-model="currentId"
:placeholder="`请选择${strategyLabels[currentStrategy]}`" :placeholder="`请选择${strategyLabels[currentStrategy]}`"
style="width: 200px;" style="width: 200px;"
size="mini"
> >
<el-option <el-option
v-for="item in currentList" v-for="item in currentList"
@@ -26,7 +27,6 @@
</el-select> </el-select>
</div> </div>
<div class="print-container" v-loading="loading"> <div class="print-container" v-loading="loading">
<BarCode :barcodes="drawerBarcodeData" /> <BarCode :barcodes="drawerBarcodeData" />
</div> </div>