add 项目利润结算
parent
6b0dd8728b
commit
b90b6a1cb8
|
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<div v-loading="loading">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<span class="text-md font-bold">{{ settlement.projectName || '' }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-descriptions :column="3" direction="horizontal" class="mb-10px">
|
||||
<el-descriptions-item label="项目名称:">
|
||||
{{ settlement.projectName || '' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="枪数:">
|
||||
{{ settlement.piles || '' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="结算周期:">
|
||||
{{ formatQuarterPeriod(settlement.settlementPeriod) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="总期数:">
|
||||
{{ settlement.period ?? '' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="结算开始时间:">
|
||||
{{ formatYMD(settlement.startDate) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="结算结束时间:">
|
||||
{{ formatYMD(settlement.endDate) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="结算日期:">
|
||||
{{ formatDateAuto(settlement.settlementDate) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import * as ProjectSettlementApi from '@/api/crm/project/settlement'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
defineOptions({ name: 'CrmProjectSettlementDetailsHeader' })
|
||||
const props = defineProps<{
|
||||
settlement: ProjectSettlementApi.ProjectSettlementVO // 项目结算信息
|
||||
loading: boolean // 加载中
|
||||
}>()
|
||||
|
||||
// 结算周期格式化:'2025-2' => '2025年第二季度'
|
||||
function formatQuarterPeriod(val?: string) {
|
||||
if (!val) return ''
|
||||
const match = val.match(/^(\d{4})-(\d)$/)
|
||||
if (!match) return val
|
||||
const year = match[1]
|
||||
const quarter = match[2]
|
||||
const quarterMap = { '1': '第一季度', '2': '第二季度', '3': '第三季度', '4': '第四季度' }
|
||||
return `${year}年${quarterMap[quarter] || quarter}`
|
||||
}
|
||||
|
||||
// [2025,4,1] => '2025-04-01' 或字符串直接返回
|
||||
function formatYMD(val?: number[] | string) {
|
||||
if (!val) return ''
|
||||
if (typeof val === 'string') return val
|
||||
if (Array.isArray(val) && val.length === 3) {
|
||||
const [y, m, d] = val
|
||||
return `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// 金额保留两位小数
|
||||
function formatAmount(val: any) {
|
||||
if (val === null || val === undefined) return ''
|
||||
return Number(val).toFixed(2)
|
||||
}
|
||||
|
||||
// 支持时间戳、字符串、Date
|
||||
function formatDateAuto(val: any) {
|
||||
if (!val) return ''
|
||||
if (typeof val === 'number') {
|
||||
return formatDate(new Date(val), 'YYYY-MM-DD')
|
||||
}
|
||||
if (typeof val === 'string' || val instanceof Date) {
|
||||
return formatDate(val, 'YYYY-MM-DD')
|
||||
}
|
||||
return ''
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
<template>
|
||||
<ProjectSettlementDetailsHeader :settlement="settlement" :loading="loading" >
|
||||
<el-button
|
||||
|
||||
type="primary"
|
||||
:loading="exportLoading"
|
||||
@click="handleExportDetailRow(settlement)"
|
||||
v-hasPermi="['crm:project-settlement:export']"
|
||||
>
|
||||
导出明细
|
||||
</el-button>
|
||||
</ProjectSettlementDetailsHeader>
|
||||
|
||||
<div class="settlement-detail-section">
|
||||
<h2>一、收入项</h2>
|
||||
<el-table :data="incomeTable" border style="width: 100%; margin-bottom: 16px;">
|
||||
<el-table-column prop="name" label="科目名称" />
|
||||
<el-table-column prop="amount" label="金额/元" />
|
||||
<el-table-column prop="remark" label="说明" />
|
||||
</el-table>
|
||||
<div v-if="settlement.incomeRemark">备注:{{ settlement.incomeRemark }}</div>
|
||||
<div style="margin-bottom: 12px;">收入合计:<b>{{ settlement.totalIncome?.toFixed(2) ?? '--' }}</b></div>
|
||||
|
||||
<h2 style="margin-top: 32px;">二、支出项</h2>
|
||||
<el-table :data="expenseTable" border style="width: 100%; margin-bottom: 16px;">
|
||||
<el-table-column prop="name" label="科目名称" />
|
||||
<el-table-column prop="amount" label="金额/元" />
|
||||
<el-table-column prop="remark" label="说明" />
|
||||
</el-table>
|
||||
<div v-if="settlement.expenseRemark">备注:{{ settlement.expenseRemark }}</div>
|
||||
<div style="margin-bottom: 12px;">支出合计:<b>{{ settlement.totalExpenses?.toFixed(2) ?? '--' }}</b></div>
|
||||
|
||||
<h2 style="margin-top: 32px;">三、利润计算</h2>
|
||||
<el-table :data="profitTable" border style="width: 100%; margin-bottom: 16px;">
|
||||
<el-table-column prop="name" label="科目名称" />
|
||||
<el-table-column prop="amount" label="金额/元" />
|
||||
<el-table-column prop="remark" label="说明" />
|
||||
</el-table>
|
||||
<div>备注:当期利润如有亏损则自动结转至下一季度支出项</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import ProjectSettlementDetailsHeader from './ProjectSettlementDetailsHeader.vue'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import * as ProjectSettlementApi from '@/api/crm/project/settlement'
|
||||
import download from '@/utils/download'
|
||||
defineOptions({ name: 'CrmProjectSettlementDetail' })
|
||||
|
||||
const settlementId = ref(0) // 客户编号
|
||||
const loading = ref(true) // 加载中
|
||||
const message = useMessage() // 消息弹窗
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { push, currentRoute } = useRouter() // 路由
|
||||
const settlement = ref<ProjectSettlementApi.ProjectSettlementVO>({} as ProjectSettlementApi.ProjectSettlementVO) // 结算详情
|
||||
|
||||
const close = () => {
|
||||
delView(unref(currentRoute))
|
||||
push({ name: 'CrmProjectSettlement' })
|
||||
}
|
||||
|
||||
const getSettlement = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
settlement.value = await ProjectSettlementApi.getProjectSettlement(settlementId.value)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
const { params } = useRoute()
|
||||
onMounted(() => {
|
||||
if (!params.id) {
|
||||
message.warning('参数错误,项目结算编号不能为空!')
|
||||
close()
|
||||
return
|
||||
}
|
||||
settlementId.value = Number(params.id)
|
||||
getSettlement()
|
||||
})
|
||||
|
||||
/** 导出明细按钮操作 */
|
||||
const handleExportDetailRow = async (row: ProjectSettlementApi.ProjectSettlementVO) => {
|
||||
try {
|
||||
await message.exportConfirm()
|
||||
exportLoading.value = true
|
||||
// 格式化日期为 'YYYY-MM-DD HH:mm:ss'
|
||||
const formatDateTime = (d: any) => {
|
||||
if (!d) return ''
|
||||
if (typeof d === 'string') return d.length > 10 ? d : d + ' 00:00:00'
|
||||
if (typeof d === 'number') {
|
||||
const date = new Date(d)
|
||||
return `${date.getFullYear()}-${String(date.getMonth()+1).padStart(2,'0')}-${String(date.getDate()).padStart(2,'0')} 00:00:00`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
const params = {
|
||||
projectId: row.projectId,
|
||||
returnTime: [formatDateTime(row.startDate), formatDateTime(row.endDate)]
|
||||
}
|
||||
const data = await ProjectSettlementApi.exportProjectSettlementDetail(params)
|
||||
download.excel(data, `${row.projectName || '项目'}结算明细.xls`)
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 收入项表格数据
|
||||
const incomeTable = computed(() => [
|
||||
{ name: '实际营收', amount: settlement.value.actualRevenue, remark: '扣除平台管理费后的净收入' },
|
||||
{ name: '政府补贴收入', amount: settlement.value.governmentSubsidy, remark: '扣除应纳税额后的净补贴额' },
|
||||
{ name: '其他收入', amount: settlement.value.otherIncome, remark: '如项目或设备转让、征用赔偿款等' },
|
||||
])
|
||||
// 收入备注表格数据
|
||||
const incomeRemarkTable = computed(() => [
|
||||
{ name: settlement.value.incomeRemark ? settlement.value.incomeRemark : '无', remark: '其他需注明的内容' },
|
||||
])
|
||||
// 支出项表格数据
|
||||
const expenseTable = computed(() => [
|
||||
{ name: '电费成本', amount: settlement.value.electricityCost, remark: '以电力公司账单为准' },
|
||||
{ name: '场地租金', amount: settlement.value.venueRent, remark: '按《场地租赁协议》约定' },
|
||||
{ name: '场地押金', amount: settlement.value.venueDeposit, remark: '按《场地租赁协议》约定' },
|
||||
{ name: '项目推介费', amount: settlement.value.projectPromotion, remark: '付给项目推荐人' },
|
||||
{ name: '设备更新购置费', amount: settlement.value.equipmentUpgrade, remark: '同一场站设备更新' },
|
||||
{ name: '管理费用', amount: settlement.value.managementCost, remark: '按120元/抢/月计算(用于不可分割的公摊费用)' },
|
||||
{ name: '税收', amount: settlement.value.taxCost, remark: '电费进项与销项差额的13%为增值税,设备税金抵所得税' },
|
||||
{ name: '上期亏损结转', amount: settlement.value.previousLossCarryover, remark: '上期未弥补亏损金额(如有)' },
|
||||
{ name: '其他支出', amount: settlement.value.otherExpenses, remark: '如有请备注' },
|
||||
])
|
||||
// 支出备注表格数据
|
||||
|
||||
// 利润表格数据
|
||||
const profitTable = computed(() => [
|
||||
{ name: '当期利润/亏损', amount: settlement.value.grossProfit, remark: '收入合计-支出合计' },
|
||||
{ name: '当期可分配利润', amount: settlement.value.shareAmount < 0 ? 0 : settlement.value.shareAmount, remark: '当期利润亏损时填0' },
|
||||
])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.settlement-detail-section {
|
||||
margin: 24px 0 0 0;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue