<template>
|
<div class="app-container">
|
<el-form :model="filters" :inline="true">
|
<el-form-item label="会计科目:">
|
<el-cascader v-model="filters.subject" :options="subjectOptions" :props="{ label: 'name', value: 'code' }" placeholder="请选择会计科目" clearable style="width: 250px;" filterable />
|
</el-form-item>
|
<el-form-item label="期间:">
|
<el-date-picker v-model="filters.startMonth" type="month" placeholder="开始月份" value-format="YYYY-MM" style="width: 140px;" />
|
<span style="margin: 0 10px;">至</span>
|
<el-date-picker v-model="filters.endMonth" type="month" placeholder="结束月份" value-format="YYYY-MM" style="width: 140px;" />
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="getTableData">查询</el-button>
|
<el-button @click="resetFilters">重置</el-button>
|
<el-button @click="handlePrint" icon="Printer">打印</el-button>
|
<el-button @click="handleOut" icon="Download">导出</el-button>
|
</el-form-item>
|
</el-form>
|
|
<div class="ledger-header" v-if="currentSubject">
|
<h2>科目总账</h2>
|
<p>科目: {{ currentSubject.code }} {{ currentSubject.name }}</p>
|
<p>期间: {{ filters.startMonth }} 至 {{ filters.endMonth }}</p>
|
</div>
|
|
<div class="table_list">
|
<el-table :data="dataList" border style="width: 100%" show-summary :summary-method="getSummaries">
|
<el-table-column prop="date" label="日期" width="120" />
|
<el-table-column prop="voucherNo" label="凭证字号" width="120" />
|
<el-table-column prop="summary" label="摘要" min-width="200" show-overflow-tooltip />
|
<el-table-column prop="debit" label="借方" width="150">
|
<template #default="{ row }">
|
<span v-if="row.debit > 0" class="text-danger">¥{{ formatMoney(row.debit) }}</span>
|
<span v-else>-</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="credit" label="贷方" width="150">
|
<template #default="{ row }">
|
<span v-if="row.credit > 0" class="text-success">¥{{ formatMoney(row.credit) }}</span>
|
<span v-else>-</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="方向" width="80">
|
<template #default="{ row }">
|
<el-tag :type="row.direction === '借' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="余额" width="150">
|
<template #default="{ row }">
|
<span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">¥{{ formatMoney(Math.abs(row.balance)) }}</span>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
|
<el-empty v-if="!currentSubject" description="请选择会计科目查询" style="margin-top: 50px;" />
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted, computed } from "vue";
|
import { ElMessage } from "element-plus";
|
import { listAccountSubject } from "@/api/financialManagement/accountSubject";
|
import { getGeneralLedger } from "@/api/financialManagement/ledger";
|
|
defineOptions({
|
name: "科目总账",
|
});
|
|
const filters = reactive({
|
subject: [],
|
startMonth: "2024-01",
|
endMonth: "2024-03",
|
});
|
|
const dataList = ref([]);
|
const subjectOptions = ref([]);
|
|
const fallbackSubjects = [
|
{ code: "1001", name: "库存现金" },
|
{ code: "1002", name: "银行存款" },
|
{ code: "1122", name: "应收账款" },
|
{ code: "2202", name: "应付账款" },
|
{ code: "6001", name: "主营业务收入" },
|
];
|
|
const toCascaderTree = (nodes = []) =>
|
nodes
|
.filter(item => item.subjectCode && item.subjectName)
|
.map(item => ({
|
code: item.subjectCode,
|
name: item.subjectName,
|
children: toCascaderTree(item.children || []),
|
}));
|
|
const loadSubjectOptions = async () => {
|
try {
|
const { data } = await listAccountSubject({
|
current: 1,
|
size: 1000,
|
status: 0,
|
});
|
const options = toCascaderTree(data?.records || []);
|
if (options.length > 0) {
|
subjectOptions.value = options;
|
return;
|
}
|
} catch (error) {
|
// 全局拦截器已提示,下面走兜底科目
|
}
|
subjectOptions.value = fallbackSubjects.map(item => ({ ...item, children: [] }));
|
};
|
|
const currentSubject = computed(() => {
|
if (!filters.subject || filters.subject.length === 0) return null;
|
const code = filters.subject[filters.subject.length - 1];
|
return findSubject(subjectOptions.value, code);
|
});
|
|
const findSubject = (options, code) => {
|
for (const item of options) {
|
if (item.code === code) return item;
|
if (item.children && item.children.length > 0) {
|
const found = findSubject(item.children, code);
|
if (found) return found;
|
}
|
}
|
return null;
|
};
|
|
const formatMoney = (value) => {
|
if (value === undefined || value === null) return "0.00";
|
return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
};
|
|
// 联调约定:总账接口返回行数组(rowType/date/voucherNo/summary/debit/credit/direction/balance)
|
const getTableData = async () => {
|
if (!currentSubject.value) {
|
dataList.value = [];
|
return;
|
}
|
try {
|
const { data } = await getGeneralLedger({
|
subjectCode: currentSubject.value.code,
|
startMonth: filters.startMonth,
|
endMonth: filters.endMonth,
|
});
|
dataList.value = Array.isArray(data) ? data : data?.records || [];
|
} catch (error) {
|
// 提示由全局请求拦截器处理,这里仅防止未捕获异常
|
}
|
};
|
|
const resetFilters = () => {
|
filters.subject = [];
|
filters.startMonth = "2024-01";
|
filters.endMonth = "2024-03";
|
dataList.value = [];
|
};
|
|
const getSummaries = (param) => {
|
const { columns, data } = param;
|
const sums = [];
|
columns.forEach((column, index) => {
|
if (index === 0) {
|
sums[index] = "合计";
|
return;
|
}
|
if (column.property === "debit") {
|
const values = data.map(item => Number(item.debit));
|
const sum = values.reduce((prev, curr) => prev + curr, 0);
|
sums[index] = "¥" + formatMoney(sum);
|
} else if (column.property === "credit") {
|
const values = data.map(item => Number(item.credit));
|
const sum = values.reduce((prev, curr) => prev + curr, 0);
|
sums[index] = "¥" + formatMoney(sum);
|
} else {
|
sums[index] = "";
|
}
|
});
|
return sums;
|
};
|
|
const handlePrint = () => {
|
ElMessage.info("打印功能");
|
};
|
|
const handleOut = () => {
|
ElMessage.success("导出成功");
|
};
|
|
onMounted(async () => {
|
await loadSubjectOptions();
|
});
|
</script>
|
|
<style lang="scss" scoped>
|
.ledger-header {
|
text-align: center;
|
margin-bottom: 20px;
|
h2 {
|
margin: 0 0 10px 0;
|
}
|
p {
|
color: #606266;
|
margin: 5px 0;
|
}
|
}
|
|
.text-primary {
|
color: #409eff;
|
font-weight: bold;
|
}
|
|
.text-success {
|
color: #67c23a;
|
font-weight: bold;
|
}
|
|
.text-danger {
|
color: #f56c6c;
|
font-weight: bold;
|
}
|
|
.text-warning {
|
color: #e6a23c;
|
font-weight: bold;
|
}
|
</style>
|