feat: 银行卡数量统计,编码。
This commit is contained in:
@@ -113,3 +113,11 @@ export function getOpenCardList(query) {
|
|||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按银行统计银行卡数量
|
||||||
|
export function getBankCardStatistics() {
|
||||||
|
return request({
|
||||||
|
url: '/invest/analysis/bankCardStatistics',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
428
src/views/invest/bankCardStatistics/index.vue
Normal file
428
src/views/invest/bankCardStatistics/index.vue
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<div class="header-title">
|
||||||
|
<el-icon class="title-icon"><CreditCard /></el-icon>
|
||||||
|
银行卡统计分析
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="summary-cards">
|
||||||
|
<div class="summary-card gradient-blue">
|
||||||
|
<div class="icon-wrapper">
|
||||||
|
<el-icon class="card-icon"><Wallet /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<div class="card-title">银行总数</div>
|
||||||
|
<div class="card-num">{{ bankCount }}<span>家</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-card gradient-purple">
|
||||||
|
<div class="icon-wrapper">
|
||||||
|
<el-icon class="card-icon"><Document /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<div class="card-title">卡片总数</div>
|
||||||
|
<div class="card-num">{{ totalCards }}<span>张</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-card gradient-green">
|
||||||
|
<div class="icon-wrapper">
|
||||||
|
<el-icon class="card-icon"><CreditCard /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<div class="card-title">信用卡总数</div>
|
||||||
|
<div class="card-num">{{ creditCardBanksCount }}<span class="unit-text">行</span>{{ totalCreditCard }}<span class="unit-text">卡</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-card gradient-orange">
|
||||||
|
<div class="icon-wrapper">
|
||||||
|
<el-icon class="card-icon"><Coin /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<div class="card-title">储蓄卡总数</div>
|
||||||
|
<div class="card-num">{{ debitCardBanksCount }}<span class="unit-text">行</span>{{ totalDebitCard }}<span class="unit-text">卡</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-card gradient-cyan">
|
||||||
|
<div class="icon-wrapper">
|
||||||
|
<el-icon class="card-icon"><Postcard /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<div class="card-title">I类储蓄卡总数</div>
|
||||||
|
<div class="card-num">{{ totalDebitTypeOne }}<span>张</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-card gradient-lime">
|
||||||
|
<div class="icon-wrapper">
|
||||||
|
<el-icon class="card-icon"><Files /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="card-info">
|
||||||
|
<div class="card-title">II类储蓄卡总数</div>
|
||||||
|
<div class="card-num">{{ totalDebitTypeTwo }}<span>张</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<div class="table-header">
|
||||||
|
<span>银行卡统计明细</span>
|
||||||
|
<el-button type="primary" :icon="Refresh" @click="handleRefresh">刷新</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="bankCardList"
|
||||||
|
style="width: 100%"
|
||||||
|
:header-cell-style="{ background: '#f5f7fa', color: '#303133', fontWeight: '600' }"
|
||||||
|
:cell-style="{ padding: '16px 0' }"
|
||||||
|
height="600"
|
||||||
|
>
|
||||||
|
<el-table-column prop="bankName" label="银行名称" min-width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="bank-name">
|
||||||
|
<el-icon class="bank-icon"><Bank /></el-icon>
|
||||||
|
<span>{{ row.bankName }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="总卡数量" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag type="primary" size="large" effect="dark">
|
||||||
|
{{ row.creditCardCount + row.debitCardCount }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="信用卡数量" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="row.creditCardCount > 0 ? 'success' : 'info'" size="large"> {{ row.creditCardCount }} 张 </el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="储蓄卡数量" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="row.debitCardCount > 0 ? 'warning' : 'info'" size="large"> {{ row.debitCardCount }} 张 </el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="I类储蓄卡" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span class="number-badge type-one">{{ row.debitTypeOneCount }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="II类储蓄卡" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span class="number-badge type-two">{{ row.debitTypeTwoCount }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="BankCardStatistics">
|
||||||
|
import { getBankCardStatistics } from '@/api/invest/statisticAnalysis'
|
||||||
|
import { Refresh } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const bankCardList = ref([])
|
||||||
|
const bankCount = ref(0)
|
||||||
|
const totalCreditCard = ref(0)
|
||||||
|
const totalDebitCard = ref(0)
|
||||||
|
const totalDebitTypeOne = ref(0)
|
||||||
|
const totalDebitTypeTwo = ref(0)
|
||||||
|
const creditCardBanksCount = ref(0)
|
||||||
|
const debitCardBanksCount = ref(0)
|
||||||
|
|
||||||
|
const creditCardColor = ref('#67c23a')
|
||||||
|
const debitCardColor = ref('#e6a23c')
|
||||||
|
|
||||||
|
const totalCards = computed(() => totalCreditCard.value + totalDebitCard.value)
|
||||||
|
|
||||||
|
const getList = () => {
|
||||||
|
loading.value = true
|
||||||
|
getBankCardStatistics()
|
||||||
|
.then((res) => {
|
||||||
|
bankCardList.value = res.data || []
|
||||||
|
calculateTotals()
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const calculateTotals = () => {
|
||||||
|
bankCount.value = bankCardList.value.length
|
||||||
|
totalCreditCard.value = bankCardList.value.reduce((sum, item) => sum + (item.creditCardCount || 0), 0)
|
||||||
|
totalDebitCard.value = bankCardList.value.reduce((sum, item) => sum + (item.debitCardCount || 0), 0)
|
||||||
|
totalDebitTypeOne.value = bankCardList.value.reduce((sum, item) => sum + (item.debitTypeOneCount || 0), 0)
|
||||||
|
totalDebitTypeTwo.value = bankCardList.value.reduce((sum, item) => sum + (item.debitTypeTwoCount || 0), 0)
|
||||||
|
|
||||||
|
// 计算有信用卡的银行数量
|
||||||
|
creditCardBanksCount.value = bankCardList.value.filter((item) => item.creditCardCount > 0).length
|
||||||
|
// 计算有储蓄卡的银行数量
|
||||||
|
debitCardBanksCount.value = bankCardList.value.filter((item) => item.debitCardCount > 0).length
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRefresh = () => {
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.app-container {
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 20px;
|
||||||
|
min-height: calc(100vh - 84px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding-left: 12px;
|
||||||
|
border-left: 4px solid #667eea;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: linear-gradient(135deg, #ffffff 0%, #f8fbff 100%);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 12px rgba(102, 126, 234, 0.08);
|
||||||
|
|
||||||
|
.title-icon {
|
||||||
|
margin-right: 12px;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.summary-card {
|
||||||
|
background: linear-gradient(135deg, #ffffff 0%, #f8fbff 100%);
|
||||||
|
border-radius: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.08);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 8px 24px rgba(102, 126, 234, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wrapper {
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 20px;
|
||||||
|
background: linear-gradient(135deg, rgba(102, 126, 234, 0.08) 0%, rgba(118, 75, 162, 0.05) 100%);
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.1);
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-num {
|
||||||
|
font-size: 28px;
|
||||||
|
font-family: DIN, Arial, sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.unit-text {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #909399;
|
||||||
|
font-weight: 500;
|
||||||
|
background: none;
|
||||||
|
-webkit-text-fill-color: #909399;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
font-weight: 500;
|
||||||
|
background: none;
|
||||||
|
-webkit-text-fill-color: #909399;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.gradient-blue .card-icon {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.gradient-green .card-icon {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.gradient-orange .card-icon {
|
||||||
|
color: #e6a23c;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.gradient-purple .card-icon {
|
||||||
|
color: #9c27b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.gradient-cyan .card-icon {
|
||||||
|
color: #00bcd4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.gradient-lime .card-icon {
|
||||||
|
color: #aeea00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.08);
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(102, 126, 234, 0.3);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(102, 126, 234, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bank-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
|
||||||
|
.bank-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.number-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #f5f7fa;
|
||||||
|
color: #606266;
|
||||||
|
|
||||||
|
&.type-one {
|
||||||
|
background: linear-gradient(135deg, rgba(64, 158, 255, 0.1) 0%, rgba(64, 158, 255, 0.05) 100%);
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.type-two {
|
||||||
|
background: linear-gradient(135deg, rgba(103, 194, 58, 0.1) 0%, rgba(103, 194, 58, 0.05) 100%);
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-distribution {
|
||||||
|
.dist-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dist-label {
|
||||||
|
width: 60px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-progress) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式设计
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.summary-cards {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.app-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-cards {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -42,8 +42,8 @@ export default defineConfig(({ mode, command }) => {
|
|||||||
proxy: {
|
proxy: {
|
||||||
// https://cn.vitejs.dev/config/#server-proxy
|
// https://cn.vitejs.dev/config/#server-proxy
|
||||||
'/dev-api': {
|
'/dev-api': {
|
||||||
target: 'https://www.qdintc.com/prod-api/',
|
// target: 'https://www.qdintc.com/prod-api/',
|
||||||
// target: 'http://127.0.0.1:8080',
|
target: 'http://127.0.0.1:8080',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (p) => p.replace(/^\/dev-api/, '')
|
rewrite: (p) => p.replace(/^\/dev-api/, '')
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user