Files
fad_oa/ruoyi-ui/src/views/oa/finance/profit/index.vue
2026-04-13 17:04:38 +08:00

432 lines
17 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 :inline="true" :model="queryParams" class="demo-form-inline" label-width="100px">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-form-item label="排序字段">
<el-select v-model="queryParams.sortField" placeholder="请选择" clearable @change="handleQuery">
<el-option label="盈亏额" value="profit_loss" />
<el-option label="启动时间" value="begin_time" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="1.5">
<el-form-item label="排序类型">
<el-select v-model="queryParams.sortOrder" placeholder="请选择" @change="handleQuery">
<el-option label="正序" value="asc" />
<el-option label="倒序" value="desc" />
</el-select>
</el-form-item>
</el-col>
<div style="float: right; margin-left: 10px;">
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
<el-button size="mini" circle icon="el-icon-refresh" @click="handleQuery" />
</el-tooltip>
<el-dropdown trigger="click" :hide-on-click="false" style="margin-left: 10px;">
<el-tooltip class="item" effect="dark" content="显隐列" placement="top">
<el-button size="mini" circle icon="el-icon-menu" />
</el-tooltip>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="column in columns" :key="column.prop">
<el-checkbox v-model="column.visible">{{ column.label }}</el-checkbox>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-row>
<!-- <el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item> -->
</el-form>
<!-- 盈亏列表表格 -->
<el-table :data="profitList" v-loading="loading" border style="width: 100%; margin-top: 20px;">
<el-table-column fixed="left" prop="projectName" label="项目名称" min-width="120" v-if="columnVisibility.projectName"
show-overflow-tooltip>
<template slot="header" slot-scope="scope">
<div style="text-align: center;">项目名称</div>
<el-input v-model="queryParams.projectName" placeholder="筛选" clearable @input="handleQuery" size="mini"
style="margin-top: 5px;" />
</template>
</el-table-column>
<el-table-column prop="projectNum" label="项目编号" min-width="120" v-if="columnVisibility.projectNum">
<template slot="header" slot-scope="scope">
<div style="text-align: center;">项目编号</div>
<el-input v-model="queryParams.projectNum" placeholder="筛选" clearable @input="handleQuery" size="mini"
style="margin-top: 5px;" />
</template>
</el-table-column>
<el-table-column prop="projectCode" label="项目代号" min-width="120" v-if="columnVisibility.projectCode">
<template slot="header" slot-scope="scope">
<div style="text-align: center;">项目代号</div>
<el-input v-model="queryParams.projectCode" placeholder="筛选" clearable @input="handleQuery" size="mini"
style="margin-top: 5px;" />
</template>
</el-table-column>
<el-table-column prop="projectStatus" label="项目状态" min-width="100" v-if="columnVisibility.projectStatus">
<template slot="header" slot-scope="scope">
<div style="text-align: center;">项目状态</div>
<el-select v-model="queryParams.projectStatus" placeholder="筛选" clearable @change="handleQuery" size="mini"
style="width: 100%; margin-top: 5px;">
<el-option label="进行中" :value="0" />
<el-option label="完结" :value="1" />
</el-select>
</template>
<template slot-scope="scope">
<el-tag :type="scope.row.projectStatus === '0' ? 'success' : 'info'">
{{ scope.row.projectStatus === '0' ? '进行中' : '完结' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="signingCompany" label="签约公司" min-width="120" v-if="columnVisibility.signingCompany">
<template slot="header" slot-scope="scope">
<div style="text-align: center;">签约公司</div>
<el-select v-model="queryParams.signingCompany" placeholder="筛选" clearable @change="handleQuery" size="mini"
style="width: 100%; margin-top: 5px;">
<el-option v-for="dict in dict.type.signing_company" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</template>
<template slot-scope="scope">
<dict-tag :options="dict.type.signing_company" :value="scope.row.signingCompany" />
</template>
</el-table-column>
<el-table-column prop="tradeType" label="贸易类型" min-width="100" v-if="columnVisibility.tradeType">
<template slot="header" slot-scope="scope">
<div style="text-align: center;">贸易类型</div>
<el-select v-model="queryParams.tradeType" placeholder="筛选" clearable @change="handleQuery" size="mini"
style="width: 100%; margin-top: 5px;">
<el-option v-for="dict in dict.type.sys_trade_type" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</template>
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_trade_type" :value="scope.row.tradeType" />
</template>
</el-table-column>
<el-table-column prop="originalFunds" label="原始合同金额" min-width="120" v-if="columnVisibility.originalFunds">
<template slot="header" slot-scope="scope">
<div style="text-align: center;">原始合同金额</div>
<div style="margin-top: 5px;">
<el-input v-model="queryParams.minContractAmount" placeholder="最小" clearable @input="handleQuery"
size="mini" style="width: 45%;" />
<span style="margin: 0 2px;">-</span>
<el-input v-model="queryParams.maxContractAmount" placeholder="最大" clearable @input="handleQuery"
size="mini" style="width: 45%;" />
</div>
</template>
<template slot-scope="scope">
<span>{{ scope.row.originalFunds == null ? scope.row.detailIncome : scope.row.originalFunds }}</span>
</template>
</el-table-column>
<el-table-column prop="totalIncomeCny" label="人民币金额(¥)" min-width="120"
v-if="columnVisibility.contractAmountCny" />
<el-table-column prop="totalExpenditure" label="支出(¥)" min-width="120" v-if="columnVisibility.totalExpenditure">
<template slot="header" slot-scope="scope">
<div style="text-align: center;">支出(¥)</div>
<!-- <div style="margin-top: 5px;">
<el-input v-model="queryParams.minTotalExpenditure" placeholder="最小" clearable @input="handleQuery" size="mini" style="width: 45%;" />
<span style="margin: 0 2px;">-</span>
<el-input v-model="queryParams.maxTotalExpenditure" placeholder="最大" clearable @input="handleQuery" size="mini" style="width: 45%;" />
</div> -->
</template>
</el-table-column>
<el-table-column prop="profitLoss" label="盈亏金额(元)" min-width="120" v-if="columnVisibility.profitLoss">
<template slot="header" slot-scope="scope">
<div style="text-align: center;">盈亏金额()</div>
<div style="margin-top: 5px;">
<el-input v-model="queryParams.minProfitLoss" placeholder="最小" clearable @input="handleQuery" size="mini"
style="width: 45%;" />
<span style="margin: 0 2px;">-</span>
<el-input v-model="queryParams.maxProfitLoss" placeholder="最大" clearable @input="handleQuery" size="mini"
style="width: 45%;" />
</div>
</template>
<template slot-scope="scope">
<span v-if="scope.row.profitLoss !== null && scope.row.profitLoss !== undefined">
<span :class="getProfitLossClass(scope.row)" :style="getProfitLossStyle(scope.row)">
{{ parseFloat(scope.row.profitLoss).toFixed(2) }}
</span>
</span>
</template>
</el-table-column>
<el-table-column prop="beginTime" label="启动时间" min-width="120" v-if="columnVisibility.beginTime">
<template slot="header" slot-scope="scope">
<div style="text-align: center;">启动时间</div>
<el-date-picker v-model="queryParams.beginTimeRange" type="daterange" range-separator="至"
start-placeholder="开始" end-placeholder="结束" value-format="yyyy-MM-dd" size="mini"
style="width: 100%; margin-top: 5px;" @change="handleQuery" />
</template>
</el-table-column>
<el-table-column prop="profitType" label="盈亏类型" min-width="100" v-if="columnVisibility.profitType">
<template slot="header" slot-scope="scope">
<div style="text-align: center;">盈亏类型</div>
<el-select v-model="queryParams.profitType" placeholder="筛选" clearable @change="handleQuery" size="mini"
style="width: 100%; margin-top: 5px;">
<el-option label="盈利" value="profit" />
<el-option label="亏损" value="loss" />
</el-select>
</template>
<template slot-scope="scope">
<el-tag v-if="scope.row.profitLoss !== null && scope.row.profitLoss !== undefined"
:type="scope.row.profitLoss >= 0 ? 'success' : 'danger'">
{{ scope.row.profitLoss >= 0 ? '盈利' : '亏损' }}
</el-tag>
<el-tag size="mini" style="margin-left: 6px;" v-if="getProfitRate(scope.row) !== null">{{
getProfitRateTag(scope.row) }}</el-tag>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination style="margin-top: 20px; text-align: right;" background
layout="total, sizes, prev, pager, next, jumper" :total="total" :page-size.sync="queryParams.pageSize"
:current-page.sync="queryParams.pageNum" :page-sizes="[10, 20, 50, 100]" @size-change="handleSizeChange"
@current-change="handlePageChange" />
</div>
</template>
<script>
import { listProfit } from '@/api/oa/finance/profit';
export default {
name: 'ProfitList',
dicts: ['sys_project_code', 'sys_trade_type', 'signing_company'],
data () {
return {
loading: false,
profitList: [],
total: 0,
// 盈亏阈值配置
profitThresholds: {
highProfit: 100000, // 高盈利阈值
highLoss: -50000, // 高亏损阈值
warningProfit: 50000, // 警告盈利阈值
warningLoss: -20000 // 警告亏损阈值
},
queryParams: {
projectName: '',
projectNum: '',
projectStatus: 1,
isDomestic: '',
minContractAmount: '',
maxContractAmount: '',
minUsdAmount: '',
maxUsdAmount: '',
minRmbAmount: '',
maxRmbAmount: '',
minProfitLoss: '',
maxProfitLoss: '',
beginTimeRange: [], // [beginTimeStart, beginTimeEnd]
profitType: '',
sortField: '',
sortOrder: 'desc',
pageNum: 1,
pageSize: 10,
projectCode: '',
minTotalExpenditure: '',
maxTotalExpenditure: '',
tradeType: '',
signingCompany: '',
},
// 列信息
columns: [
{ key: 0, prop: 'projectName', label: `项目名称`, visible: true },
{ key: 1, prop: 'projectNum', label: `项目编号`, visible: true },
{ key: 2, prop: 'projectCode', label: `项目代号`, visible: true },
{ key: 3, prop: 'projectStatus', label: `项目状态`, visible: true },
{ key: 4, prop: 'tradeType', label: `贸易类型`, visible: true },
{ key: 5, prop: 'signingCompany', label: `签约公司`, visible: true },
{ key: 5, prop: 'originalFunds', label: `原始合同金额`, visible: true },
{ key: 6, prop: 'contractAmountCny', label: `人民币金额(¥)`, visible: true },
{ key: 7, prop: 'totalExpenditure', label: `支出(¥)`, visible: true },
{ key: 8, prop: 'profitLoss', label: `盈亏金额(元)`, visible: true },
{ key: 9, prop: 'beginTime', label: `启动时间`, visible: true },
{ key: 10, prop: 'profitType', label: `盈亏类型`, visible: true }
],
}
},
computed: {
columnVisibility () {
return this.columns.reduce((acc, col) => {
acc[col.prop] = col.visible;
return acc;
}, {});
}
},
methods: {
getList () {
this.loading = true
// 拆分时间范围
const params = { ...this.queryParams }
if (params.beginTimeRange && params.beginTimeRange.length === 2) {
params.beginTimeStart = params.beginTimeRange[0]
params.beginTimeEnd = params.beginTimeRange[1]
}
delete params.beginTimeRange
listProfit(params).then(res => {
this.profitList = res.rows || []
this.total = res.total || 0
this.loading = false
}).catch(() => {
this.loading = false
})
},
handleQuery () {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery () {
this.queryParams = {
projectName: '',
projectNum: '',
projectStatus: '',
isDomestic: '',
minContractAmount: '',
maxContractAmount: '',
minUsdAmount: '',
maxUsdAmount: '',
minRmbAmount: '',
maxRmbAmount: '',
minProfitLoss: '',
maxProfitLoss: '',
beginTimeRange: [],
profitType: '',
sortField: '',
sortOrder: 'desc',
pageNum: 1,
pageSize: 10,
projectCode: '',
minTotalExpenditure: '',
maxTotalExpenditure: '',
tradeType: '',
}
this.getList()
},
handleSizeChange (val) {
this.queryParams.pageSize = val
this.getList()
},
handlePageChange (val) {
this.queryParams.pageNum = val
this.getList()
},
getProfitRate (row) {
// 计算盈亏百分比originalFunds为0则用detailIncome
const base = row.originalFunds && row.originalFunds !== 0 ? row.originalFunds : row.detailIncome;
if (!base || base === 0) return null;
return row.profitLoss / base;
},
getProfitRateTag (row) {
const rate = this.getProfitRate(row);
console.log(rate, 'rate' + row.projectName, row);
if (row.totalIncomeCny < 0) return '亏损';
if (rate === null) return '-';
if (rate >= 0.2) return '高盈利';
if (rate <= -0.2) return '高亏损';
if (rate < 0) return '亏损';
if (rate >= 0 && rate <= 0.05) return '低盈利';
return '盈利';
},
getProfitLossClass (row) {
const rate = this.getProfitRate(row);
if (rate === null) return '';
if (row.totalIncomeCny < 0) return 'profit-high-loss';
if (rate >= 0.2) {
return 'profit-high-profit';
} else if (rate <= -0.2) {
return 'profit-high-loss';
} else if (rate < 0) {
return 'profit-warning-loss';
} else if (rate >= 0 && rate <= 0.05) {
return 'profit-warning-profit';
} else {
return 'profit-normal-profit';
}
},
getProfitLossStyle (row) {
const rate = this.getProfitRate(row);
if (rate === null) return {};
if (row.totalIncomeCny < 0) return { color: '#F56C6C', padding: '2px 6px', borderRadius: '4px' };
if (rate >= 0.2) {
return {
color: '#67C23A',
fontWeight: 'bold',
fontSize: '14px',
backgroundColor: '#f0f9ff',
padding: '2px 6px',
borderRadius: '4px'
};
} else if (rate <= -0.2) {
return {
color: '#F56C6C',
fontWeight: 'bold',
fontSize: '14px',
backgroundColor: '#fef0f0',
padding: '2px 6px',
borderRadius: '4px'
};
} else if (rate < 0) {
return {
color: '#F56C6C',
fontWeight: 'bold',
backgroundColor: '#fef0f0',
padding: '2px 6px',
borderRadius: '4px'
};
} else if (rate >= 0 && rate <= 0.05) {
return {
color: '#E6A23C',
fontWeight: 'bold',
backgroundColor: '#fdf6ec',
padding: '2px 6px',
borderRadius: '4px'
};
} else {
return { color: '#67C23A' };
}
},
},
mounted () {
this.getList()
}
}
</script>
<style scoped>
.demo-form-inline .el-form-item {
margin-bottom: 10px;
}
/* 盈亏金额样式 */
.profit-high-profit {
border: 2px solid #67C23A;
box-shadow: 0 2px 4px rgba(103, 194, 58, 0.2);
}
.profit-high-loss {
border: 2px solid #F56C6C;
box-shadow: 0 2px 4px rgba(245, 108, 108, 0.2);
}
.profit-warning-profit {
border: 1px solid #E6A23C;
box-shadow: 0 1px 3px rgba(230, 162, 60, 0.2);
}
.profit-warning-loss {
border: 1px solid #F56C6C;
box-shadow: 0 1px 3px rgba(245, 108, 108, 0.2);
}
.profit-normal-profit {
/* 正常盈利样式 */
}
.profit-normal-loss {
/* 正常亏损样式 */
}
</style>