feat: 新增政务端,保险端

This commit is contained in:
FlowerWater
2026-01-05 17:17:54 +08:00
parent 3c8b3cf442
commit e08b9694a5
18 changed files with 2742 additions and 9 deletions

View File

@@ -58,13 +58,36 @@ export default defineUniPages({
{ path: 'me/index', style: { navigationBarTitleText: '银行中心' } },
],
},
{
root: 'pagesGovernment',
pages: [
{ path: 'dashboard/index', style: { navigationBarTitleText: '政务工作台' } },
{ path: 'bank/list', style: { navigationBarTitleText: '银行管理' } },
{ path: 'bank/detail', style: { navigationBarTitleText: '银行详情' } },
{ path: 'supervise/list', style: { navigationBarTitleText: '合规检查' } },
{ path: 'risk/list', style: { navigationBarTitleText: '风险预警' } },
{ path: 'me/index', style: { navigationBarTitleText: '个人中心' } },
],
},
{
root: 'pagesInsurance',
pages: [
{ path: 'dashboard/index', style: { navigationBarTitleText: '保险工作台' } },
{ path: 'policy/list', style: { navigationBarTitleText: '保单管理' } },
{ path: 'policy/detail', style: { navigationBarTitleText: '保单详情' } },
{ path: 'claim/list', style: { navigationBarTitleText: '理赔处理' } },
{ path: 'claim/detail', style: { navigationBarTitleText: '理赔详情' } },
{ path: 'bank/list', style: { navigationBarTitleText: '合作银行' } },
{ path: 'me/index', style: { navigationBarTitleText: '个人中心' } },
],
},
],
// 分包预下载配置
preloadRule: {
'pages/login/index': {
network: 'all',
packages: ['pagesMerchant', 'pagesBank'],
packages: ['pagesMerchant', 'pagesBank', 'pagesGovernment', 'pagesInsurance'],
},
},
})

View File

@@ -19,7 +19,9 @@
"network": "all",
"packages": [
"pagesMerchant",
"pagesBank"
"pagesBank",
"pagesGovernment",
"pagesInsurance"
]
}
},
@@ -264,12 +266,6 @@
"navigationBarTitleText": "提现记录"
}
},
{
"path": "me/index",
"style": {
"navigationBarTitleText": "银行中心"
}
},
{
"path": "report/list",
"style": {
@@ -299,6 +295,102 @@
"style": {
"navigationBarTitleText": "拜访详情"
}
},
{
"path": "me/index",
"style": {
"navigationBarTitleText": "银行中心"
}
}
]
},
{
"root": "pagesGovernment",
"pages": [
// GENERATED BY UNI-PAGES, PLATFORM: H5
{
"path": "dashboard/index",
"style": {
"navigationBarTitleText": "政务工作台"
}
},
{
"path": "bank/list",
"style": {
"navigationBarTitleText": "银行管理"
}
},
{
"path": "bank/detail",
"style": {
"navigationBarTitleText": "银行详情"
}
},
{
"path": "supervise/list",
"style": {
"navigationBarTitleText": "合规检查"
}
},
{
"path": "risk/list",
"style": {
"navigationBarTitleText": "风险预警"
}
},
{
"path": "me/index",
"style": {
"navigationBarTitleText": "个人中心"
}
}
]
},
{
"root": "pagesInsurance",
"pages": [
// GENERATED BY UNI-PAGES, PLATFORM: H5
{
"path": "dashboard/index",
"style": {
"navigationBarTitleText": "保险工作台"
}
},
{
"path": "policy/list",
"style": {
"navigationBarTitleText": "保单管理"
}
},
{
"path": "policy/detail",
"style": {
"navigationBarTitleText": "保单详情"
}
},
{
"path": "claim/list",
"style": {
"navigationBarTitleText": "理赔处理"
}
},
{
"path": "claim/detail",
"style": {
"navigationBarTitleText": "理赔详情"
}
},
{
"path": "bank/list",
"style": {
"navigationBarTitleText": "合作银行"
}
},
{
"path": "me/index",
"style": {
"navigationBarTitleText": "个人中心"
}
}
]
}

View File

@@ -25,6 +25,8 @@ const clientTypes = [
{ type: ClientType.USER, ...CLIENT_TYPE_CONFIG[ClientType.USER] },
{ type: ClientType.MERCHANT, ...CLIENT_TYPE_CONFIG[ClientType.MERCHANT] },
{ type: ClientType.BANK, ...CLIENT_TYPE_CONFIG[ClientType.BANK] },
{ type: ClientType.GOVERNMENT, ...CLIENT_TYPE_CONFIG[ClientType.GOVERNMENT] },
{ type: ClientType.INSURANCE, ...CLIENT_TYPE_CONFIG[ClientType.INSURANCE] },
]
// 选择客户端类型

View File

@@ -0,0 +1,142 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '银行详情',
navigationBarBackgroundColor: '#fff',
navigationBarTextStyle: 'black',
},
}
</route>
<template>
<view class="bank-detail-page">
<view class="info-card">
<view class="head">
<text class="i-carbon-building-government text-4xl text-primary mb-2" />
<text class="name">{{ bankInfo.name }}</text>
<text class="code">机构代码{{ bankInfo.code }}</text>
</view>
</view>
<view class="section">
<view class="sec-title">核心指标</view>
<wd-cell-group border>
<wd-cell title="信贷余额" :value="`${bankInfo.loanAmount}万`" />
<wd-cell title="客户总数" :value="`${bankInfo.customerCount}户`" />
<wd-cell title="不良贷款率" :value="`${bankInfo.overdueRate}%`" />
<wd-cell title="资本充足率" :value="`${bankInfo.capitalRatio}%`" />
</wd-cell-group>
</view>
<view class="section">
<view class="sec-title">监管记录</view>
<view class="record-list">
<wd-step v-for="item in superviseRecords" :key="item.id" vertical>
<template #icon>
<view class="step-dot" :class="item.type"></view>
</template>
<view class="record-item">
<view class="flex justify-between">
<text class="font-bold">{{ item.title }}</text>
<text class="text-sm text-gray-500">{{ item.time }}</text>
</view>
<view class="mt-1 text-sm text-gray-600">
检查结论<text :class="item.result === 'pass' ? 'text-green-600' : 'text-orange-500'">{{ item.resultText }}</text>
</view>
</view>
</wd-step>
</view>
</view>
<view class="footer-btn">
<wd-button block type="primary" @click="handleSupervise">发起合规检查</wd-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const bankInfo = ref({
id: '1',
name: '某某商业银行',
code: 'BANK001',
status: 'normal',
loanAmount: 5680,
customerCount: 1234,
overdueRate: 1.2,
capitalRatio: 12.5,
})
const superviseRecords = ref([
{ id: 1, title: '2025年第四季度现场检查', time: '2025-12-15', type: 'check', result: 'pass', resultText: '合格' },
{ id: 2, title: '信贷资金流向专项排查', time: '2025-11-20', type: 'risk', result: 'pass', resultText: '合格' },
])
function handleSupervise() {
uni.showToast({ title: '已发起', icon: 'success' })
}
</script>
<style lang="scss" scoped>
.bank-detail-page {
min-height: 100vh;
background: #f5f7fa;
padding-bottom: 120rpx;
}
.text-primary { color: #0957DE; }
.info-card {
background: #fff;
padding: 40rpx 0;
text-align: center;
margin-bottom: 24rpx;
.head {
display: flex;
flex-direction: column;
align-items: center;
.name { font-size: 34rpx; font-weight: 600; color: #333; margin-bottom: 8rpx; }
.code { font-size: 24rpx; color: #999; }
}
}
.section {
background: #fff;
margin-bottom: 24rpx;
.sec-title {
padding: 24rpx 32rpx;
font-size: 30rpx;
font-weight: 600;
color: #333;
border-bottom: 1rpx solid #f5f5f5;
}
}
.record-list {
padding: 32rpx;
background: #fff;
}
.step-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: #e0e0e0;
&.check { background: #0957DE; }
&.risk { background: #ff9800; }
}
.footer-btn {
position: fixed;
bottom: 0;
width: 100%;
background: #fff;
padding: 20rpx 32rpx;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
}
</style>

View File

@@ -0,0 +1,161 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '银行监管',
navigationBarBackgroundColor: '#fff',
navigationBarTextStyle: 'black',
},
}
</route>
<template>
<view class="bank-list-page">
<view class="search-wrap">
<wd-search v-model="keyword" placeholder="请输入机构名称或编码" hide-cancel />
</view>
<view class="list-container">
<view
v-for="item in filteredList"
:key="item.id"
class="bank-card"
@click="goDetail(item.id)"
>
<view class="card-head">
<view class="name-box">
<text class="i-carbon-building text-xl mr-2 text-primary" />
<text class="name">{{ item.name }}</text>
</view>
<wd-tag type="success" plain v-if="item.status === 'normal'">正常</wd-tag>
<wd-tag type="warning" plain v-else>关注</wd-tag>
</view>
<view class="card-data">
<view class="data-item">
<text class="val">{{ item.loanAmount }}</text>
<text class="label">监管贷款余额()</text>
</view>
<view class="data-item">
<text class="val">{{ item.customerCount }}</text>
<text class="label">在贷户数</text>
</view>
<view class="data-item">
<text class="val text-red">{{ item.overdueRate }}%</text>
<text class="label">不良贷款率</text>
</view>
</view>
<view class="card-bar">
<text class="time">更新时间{{ item.updateTime }}</text>
<text class="action">查看详情 ></text>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
const keyword = ref('')
const bankList = ref([
{
id: '1',
name: '某某商业银行',
code: 'BANK001',
status: 'normal',
loanAmount: 5680,
customerCount: 1234,
overdueRate: 1.2,
updateTime: '2026-01-05 14:30',
},
{
id: '2',
name: '某某农村信用社',
code: 'BANK002',
status: 'warning',
loanAmount: 3420,
customerCount: 856,
overdueRate: 3.5,
updateTime: '2026-01-05 13:20',
},
])
const filteredList = computed(() => {
if (!keyword.value) return bankList.value
return bankList.value.filter(item => item.name.includes(keyword.value))
})
function goDetail(id: string) {
uni.navigateTo({ url: `/pagesGovernment/bank/detail?id=${id}` })
}
</script>
<style lang="scss" scoped>
.bank-list-page {
min-height: 100vh;
background: #f5f7fa;
}
.search-wrap {
padding: 20rpx 32rpx;
background: #fff;
position: sticky;
top: 0;
z-index: 10;
}
.list-container {
padding: 24rpx 32rpx;
}
.bank-card {
background: #fff;
border-radius: 12rpx;
padding: 32rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.02);
.card-head {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 24rpx;
border-bottom: 1rpx solid #f5f5f5;
.name-box {
display: flex;
align-items: center;
.name { font-size: 30rpx; font-weight: 600; color: #333; }
.text-primary { color: #0957DE; }
}
}
.card-data {
display: flex;
padding: 24rpx 0;
.data-item {
flex: 1;
text-align: center;
.val { display: block; font-size: 32rpx; font-weight: 600; color: #333; margin-bottom: 8rpx; }
.label { font-size: 24rpx; color: #999; }
.text-red { color: #d32f2f; }
}
}
.card-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20rpx;
border-top: 1rpx solid #f5f5f5;
font-size: 24rpx;
.time { color: #999; }
.action { color: #0957DE; }
}
}
</style>

View File

@@ -0,0 +1,334 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '政务工作台',
navigationBarBackgroundColor: '#0957DE',
navigationBarTextStyle: 'white',
},
}
</route>
<template>
<view class="gov-dashboard">
<!-- 顶部 Banner -->
<view class="header-bg">
<view class="user-welcome">
<view class="text-info">
<text class="greeting">欢迎回来监管员</text>
<text class="date">{{ currentDate }}</text>
</view>
<view class="avatar-box">
<text class="i-carbon-user-avatar" />
</view>
</view>
</view>
<!-- 核心指标卡片 -->
<view class="stats-overview">
<view class="stat-row">
<view class="stat-col">
<text class="num">{{ stats.bankCount }}</text>
<text class="label">监管机构</text>
</view>
<view class="divider" />
<view class="stat-col">
<text class="num primary">{{ stats.loanBalance }}</text>
<text class="label">监管贷款余额</text>
</view>
<view class="divider" />
<view class="stat-col">
<text class="num warning">{{ stats.nplRatio }}</text>
<text class="label">不良贷款率</text>
</view>
</view>
</view>
<!-- 功能导航 -->
<view class="grid-menu">
<view class="menu-item" @click="navigateTo('/pagesGovernment/bank/list')">
<view class="icon-wrap primary">
<text class="i-carbon-building-government" />
</view>
<text class="label">机构监管</text>
</view>
<view class="menu-item" @click="navigateTo('/pagesGovernment/supervise/list')">
<view class="icon-wrap success">
<text class="i-carbon-task-approved" />
</view>
<text class="label">贷款抽查</text>
</view>
<view class="menu-item" @click="navigateTo('/pagesGovernment/risk/list')">
<view class="icon-wrap warning">
<text class="i-carbon-warning-alt" />
</view>
<text class="label">风险监测</text>
</view>
<view class="menu-item" @click="navigateTo('/pagesGovernment/report/list')">
<view class="icon-wrap info">
<text class="i-carbon-chart-line-data" />
</view>
<text class="label">数据报送</text>
</view>
</view>
<!-- 待办事项 -->
<view class="section-card">
<view class="card-header">
<text class="title">待办事项</text>
<text class="more">查看全部</text>
</view>
<view class="todo-list">
<view v-for="item in todoList" :key="item.id" class="todo-item">
<view class="todo-icon">
<text class="i-carbon-notification-new" />
</view>
<view class="todo-content">
<text class="todo-title">{{ item.title }}</text>
<text class="todo-time">{{ item.time }}</text>
</view>
<wd-button size="small" type="primary" plain>处理</wd-button>
</view>
</view>
</view>
<!-- 风险动态 -->
<view class="section-card">
<view class="card-header">
<text class="title">风险动态</text>
</view>
<view class="risk-list">
<view v-for="alert in riskAlerts" :key="alert.id" class="risk-item">
<view class="risk-tag" :class="alert.level">{{ alert.levelText }}</view>
<text class="risk-msg">{{ alert.message }}</text>
<text class="risk-bank">{{ alert.bankName }}</text>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
import dayjs from 'dayjs'
const currentDate = computed(() => dayjs().format('YYYY年MM月DD日'))
const stats = ref({
bankCount: 24,
loanBalance: '128.5亿',
nplRatio: '1.8%',
})
const todoList = ref([
{ id: 1, title: '某某银行第二季度合规报告审核', time: '截止:今日 18:00' },
{ id: 2, title: '关于落实信贷风险排查的通知', time: '待发布' },
])
const riskAlerts = ref([
{ id: 1, level: 'high', levelText: '高', message: '信贷逾期率超过警戒线 2%', bankName: '某某农村信用社' },
{ id: 2, level: 'medium', levelText: '中', message: '大额交易集中度异常', bankName: '某某商业银行' },
])
function navigateTo(url: string) {
uni.navigateTo({ url })
}
</script>
<style lang="scss" scoped>
.gov-dashboard {
min-height: 100vh;
background: #f5f7fa;
padding-bottom: 40rpx;
}
.header-bg {
background: #0957DE;
padding: 40rpx 32rpx 100rpx;
border-bottom-left-radius: 40rpx;
border-bottom-right-radius: 40rpx;
position: relative;
.user-welcome {
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
margin-bottom: 40rpx;
.greeting { font-size: 36rpx; font-weight: 600; display: block; }
.date { font-size: 24rpx; opacity: 0.8; margin-top: 8rpx; display: block; }
.avatar-box {
width: 80rpx;
height: 80rpx;
background: rgba(255,255,255,0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 44rpx;
}
}
}
.stats-overview {
background: #fff;
border-radius: 16rpx;
padding: 40rpx 0;
box-shadow: 0 8rpx 24rpx rgba(9, 87, 222, 0.08);
position: relative;
margin-top: -60rpx; /* 上移效果 */
margin-left: 32rpx;
margin-right: 32rpx;
.stat-row {
display: flex;
align-items: center;
.stat-col {
flex: 1;
text-align: center;
.num {
font-size: 40rpx;
font-weight: 700;
color: #333;
display: block;
&.warning { color: #f57c00; }
}
.label {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
display: block;
}
}
.divider {
width: 1rpx;
height: 40rpx;
background: #eee;
}
}
}
.grid-menu {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20rpx;
background: #fff;
margin: 24rpx 32rpx;
padding: 32rpx 0;
border-radius: 16rpx;
.menu-item {
display: flex;
flex-direction: column;
align-items: center;
.icon-wrap {
width: 88rpx;
height: 88rpx;
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
margin-bottom: 12rpx;
&.primary { background: #e3f2fd; color: #0957DE; }
&.success { background: #e8f5e9; color: #38a169; }
&.warning { background: #fff3e0; color: #f57c00; }
&.info { background: #f3e5f5; color: #9c27b0; }
}
.label { font-size: 26rpx; color: #333; }
}
}
.section-card {
background: #fff;
margin: 24rpx 32rpx;
border-radius: 16rpx;
padding: 24rpx 32rpx;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.title { font-size: 32rpx; font-weight: 600; color: #333; display: flex; align-items: center; }
.title::before {
content: '';
display: inline-block;
width: 8rpx;
height: 32rpx;
background: #0957DE;
border-radius: 4rpx;
margin-right: 12rpx;
}
.more { font-size: 24rpx; color: #999; }
}
}
.todo-list {
.todo-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child { border-bottom: none; }
.todo-icon {
width: 64rpx;
height: 64rpx;
background: #f0f7ff;
border-radius: 50%;
color: #0957DE;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.todo-content {
flex: 1;
margin-right: 20rpx;
.todo-title { font-size: 28rpx; color: #333; display: block; margin-bottom: 4rpx; }
.todo-time { font-size: 24rpx; color: #999; }
}
}
}
.risk-list {
.risk-item {
background: #fdfdfd;
border: 1rpx solid #f0f0f0;
border-radius: 8rpx;
padding: 20rpx;
margin-bottom: 16rpx;
display: flex;
align-items: center;
.risk-tag {
font-size: 20rpx;
padding: 2rpx 8rpx;
border-radius: 4rpx;
margin-right: 12rpx;
&.high { background: #ffebee; color: #d32f2f; border: 1rpx solid #ef9a9a; }
&.medium { background: #fff3e0; color: #ef6c00; border: 1rpx solid #ffcc80; }
}
.risk-msg { flex: 1; font-size: 26rpx; color: #333; }
.risk-bank { font-size: 22rpx; color: #999; margin-left: 12rpx; }
}
}
</style>

View File

@@ -0,0 +1,98 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '个人中心',
navigationBarBackgroundColor: '#0957DE',
navigationBarTextStyle: 'white',
},
}
</route>
<template>
<view class="me-page">
<view class="user-info-box">
<view class="flex items-center">
<view class="avatar">
<text class="i-carbon-user-avatar-filled-alt text-4xl" />
</view>
<view class="ml-4">
<text class="name">监管员001</text>
<text class="dept">市金融监管局 · 银行监管处</text>
</view>
</view>
</view>
<view class="menu-group">
<wd-cell-group border>
<wd-cell title="消息通知" is-link>
<template #icon>
<text class="i-carbon-notification text-lg mr-2 text-blue-500" />
</template>
</wd-cell>
<wd-cell title="工作日志" is-link>
<template #icon>
<text class="i-carbon-catalog text-lg mr-2 text-green-500" />
</template>
</wd-cell>
<wd-cell title="系统设置" is-link>
<template #icon>
<text class="i-carbon-settings text-lg mr-2 text-gray-500" />
</template>
</wd-cell>
</wd-cell-group>
</view>
<view class="p-4 mt-4">
<wd-button type="error" plain block @click="handleLogout">退出登录</wd-button>
</view>
</view>
</template>
<script lang="ts" setup>
function handleLogout() {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
uni.reLaunch({ url: '/pages/login/index' })
}
}
})
}
</script>
<style lang="scss" scoped>
.me-page {
min-height: 100vh;
background: #f5f7fa;
}
.user-info-box {
background: #0957DE;
padding: 60rpx 40rpx 80rpx;
color: #fff;
.avatar {
width: 100rpx;
height: 100rpx;
background: rgba(255,255,255,0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.name { font-size: 36rpx; font-weight: 600; display: block; }
.dept { font-size: 26rpx; opacity: 0.8; margin-top: 8rpx; display: block; }
}
.menu-group {
margin-top: -20rpx;
background: #fff;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
overflow: hidden;
padding-top: 20rpx;
}
</style>

View File

@@ -0,0 +1,128 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '风险预警',
navigationBarBackgroundColor: '#fff',
navigationBarTextStyle: 'black',
},
}
</route>
<template>
<view class="risk-page">
<view class="list-wrap">
<view v-for="item in riskList" :key="item.id" class="risk-card">
<view class="left-line" :class="item.level"></view>
<view class="content">
<view class="head">
<text class="title">{{ item.title }}</text>
<text class="time">{{ item.time }}</text>
</view>
<view class="desc">{{ item.desc }}</view>
<view class="foot">
<view class="tag-box">
<text class="bank">{{ item.bankName }}</text>
<text class="level-tag" :class="item.level">{{ item.levelText }}</text>
</view>
<wd-button size="small" type="primary" plain>处置</wd-button>
</view>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const riskList = ref([
{
id: 1,
title: '信贷逾期率超标',
desc: '当前逾期率达2.5%,超过监管红线',
bankName: '某某农村信用社',
time: '10:30',
level: 'high',
levelText: '高风险'
},
{
id: 2,
title: '大额交易异常',
desc: '检测到单笔超大额资金流向敏感行业',
bankName: '某某商业银行',
time: '昨日',
level: 'medium',
levelText: '中风险'
},
])
</script>
<style lang="scss" scoped>
.risk-page {
min-height: 100vh;
background: #f5f7fa;
padding: 24rpx 32rpx;
}
.risk-card {
background: #fff;
border-radius: 12rpx;
overflow: hidden;
display: flex;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.02);
.left-line {
width: 8rpx;
background: #ccc;
&.high { background: #d32f2f; }
&.medium { background: #f57c00; }
}
.content {
flex: 1;
padding: 24rpx;
}
.head {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
.title { font-size: 30rpx; font-weight: 600; color: #333; }
.time { font-size: 24rpx; color: #999; }
}
.desc {
font-size: 26rpx;
color: #666;
line-height: 1.5;
margin-bottom: 20rpx;
}
.foot {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1rpx solid #f5f5f5;
padding-top: 20rpx;
.tag-box {
display: flex;
align-items: center;
.bank { font-size: 24rpx; color: #666; margin-right: 16rpx; }
.level-tag {
font-size: 20rpx;
padding: 2rpx 8rpx;
border-radius: 4rpx;
&.high { background: #fce4ec; color: #d32f2f; }
&.medium { background: #FFF3E0; color: #f57c00; }
}
}
}
}
</style>

View File

@@ -0,0 +1,134 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '贷款抽查',
navigationBarBackgroundColor: '#fff',
navigationBarTextStyle: 'black',
},
}
</route>
<template>
<view class="check-page">
<wd-tabs v-model="currentTab" sticky>
<wd-tab title="待执行" name="pending">
<view class="list-wrap">
<view v-for="item in pendingList" :key="item.id" class="task-card">
<view class="task-head">
<view class="title-box">
<text class="i-carbon-task text-lg mr-1 text-gray-500" />
<text class="title">{{ item.title }}</text>
</view>
<wd-tag type="warning" plain>待执行</wd-tag>
</view>
<view class="task-body">
<view class="row">
<text class="label">被检机构</text>
<text>{{ item.bankName }}</text>
</view>
<view class="row">
<text class="label">计划日期</text>
<text>{{ item.date }}</text>
</view>
</view>
<view class="task-foot">
<wd-button size="small" type="primary" @click="handleStart(item)">开始检查</wd-button>
</view>
</view>
</view>
</wd-tab>
<wd-tab title="已完成" name="completed">
<view class="list-wrap">
<view class="empty-tip">暂无已完成任务</view>
</view>
</wd-tab>
</wd-tabs>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const currentTab = ref('pending')
const pendingList = ref([
{ id: 1, title: '2026年第一季度信贷合规检查', bankName: '某某商业银行', date: '2026-01-20' },
{ id: 2, title: '反洗钱专项排查', bankName: '某某农村信用社', date: '2026-01-22' },
])
function handleStart(item: any) {
uni.showToast({ title: '开始任务', icon: 'none' })
}
</script>
<style lang="scss" scoped>
.check-page {
min-height: 100vh;
background: #f5f7fa;
}
.list-wrap {
padding: 24rpx 32rpx;
}
.task-card {
background: #fff;
position: relative;
overflow: hidden;
border-radius: 12rpx;
padding: 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.05);
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 6rpx;
background: #ff9800;
}
.task-head {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 24rpx;
.title-box {
flex: 1;
display: flex;
align-items: flex-start;
margin-right: 16rpx;
.title { font-size: 30rpx; font-weight: 600; color: #333; line-height: 1.4; }
}
}
.task-body {
.row {
font-size: 26rpx;
margin-bottom: 12rpx;
color: #333;
.label { color: #999; }
}
}
.task-foot {
display: flex;
justify-content: flex-end;
padding-top: 20rpx;
border-top: 1rpx solid #f5f5f5;
margin-top: 20rpx;
}
}
.empty-tip {
text-align: center;
color: #999;
padding-top: 100rpx;
font-size: 28rpx;
}
</style>

View File

@@ -0,0 +1,126 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '合作银行',
},
}
</route>
<template>
<view class="bank-list-page">
<wd-search v-model="keyword" placeholder="搜索银行名称" hide-cancel />
<view class="list-container">
<view class="section-title">签约银行</view>
<view class="bank-grid">
<view
v-for="item in bankList"
:key="item.id"
class="bank-item"
>
<view class="bank-icon">
<text class="i-carbon-building" />
</view>
<text class="name">{{ item.name }}</text>
<view class="tags">
<text class="tag">合作{{ item.years }}</text>
<text class="tag primary">保单{{ item.policyCount }}</text>
</view>
<view class="contact">
<text class="label">对接人</text>
<text class="val">{{ item.contact }}</text>
</view>
<wd-button size="small" plain class="mt-2">联系</wd-button>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const keyword = ref('')
const bankList = ref([
{ id: 1, name: '某某商业银行', years: 3, policyCount: 560, contact: '张经理' },
{ id: 2, name: '某某农村信用社', years: 5, policyCount: 1200, contact: '李经理' },
{ id: 3, name: '某某村镇银行', years: 2, policyCount: 320, contact: '王经理' },
{ id: 4, name: '某某城市银行', years: 1, policyCount: 150, contact: '赵经理' },
])
</script>
<style lang="scss" scoped>
.bank-list-page {
min-height: 100vh;
background: #f5f7fa;
padding: 24rpx 32rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin: 24rpx 0 16rpx;
}
.bank-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
}
.bank-item {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.02);
.bank-icon {
width: 80rpx;
height: 80rpx;
background: #e3f2fd;
color: #1976d2;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
margin-bottom: 16rpx;
}
.name {
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-bottom: 12rpx;
}
.tags {
display: flex;
gap: 8rpx;
margin-bottom: 16rpx;
.tag {
font-size: 20rpx;
padding: 2rpx 8rpx;
border-radius: 8rpx;
background: #f5f5f5;
color: #666;
&.primary { background: #e8f5e9; color: #38a169; }
}
}
.contact {
font-size: 22rpx;
color: #999;
margin-bottom: 12rpx;
.val { color: #666; }
}
}
</style>

View File

@@ -0,0 +1,200 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '理赔详情',
},
}
</route>
<template>
<view class="claim-detail">
<!-- 顶部状态 -->
<view class="status-header">
<view class="status-content">
<text class="label">当前状态</text>
<text class="status">{{ claim.statusText }}</text>
</view>
<text class="amount">¥{{ claim.amount.toLocaleString() }}</text>
</view>
<!-- 申请信息 -->
<view class="card">
<view class="card-title">申请信息</view>
<view class="info-list">
<view class="item">
<text class="label">理赔单号</text>
<text class="val">{{ claim.claimNo }}</text>
</view>
<view class="item">
<text class="label">申请时间</text>
<text class="val">{{ claim.createTime }}</text>
</view>
<view class="item">
<text class="label">理赔原因</text>
<text class="val">{{ claim.reason }}</text>
</view>
<view class="item">
<text class="label">附件材料</text>
<text class="link">查看附件 (3)</text>
</view>
</view>
</view>
<!-- 关联保单 -->
<view class="card">
<view class="card-title">关联保单</view>
<view class="policy-preview">
<view class="row">
<text class="label">保单号</text>
<text class="val">{{ claim.policyNo }}</text>
</view>
<view class="row">
<text class="label">被保险人</text>
<text class="val">{{ claim.customerName }}</text>
</view>
<view class="row">
<text class="label">承保金额</text>
<text class="val">¥{{ (500000).toLocaleString() }}</text>
</view>
</view>
<wd-button size="small" plain block class="mt-2">查看保单详情</wd-button>
</view>
<!-- 审核记录 -->
<view class="card">
<view class="card-title">审核流程</view>
<wd-steps :active="activeStep" vertical>
<wd-step title="提交申请" :description="claim.createTime" />
<wd-step title="初审" description="等待保险专员初审" />
<wd-step title="复核" description="等待理赔经理复核" />
<wd-step title="结案打款" />
</wd-steps>
</view>
<!-- 底部操作栏 -->
<view class="action-bar" v-if="claim.status === 'pending'">
<wd-button type="error" plain class="flex-1 mr-2" @click="handleReject">驳回</wd-button>
<wd-button type="primary" class="flex-1" @click="handleApprove">通过初审</wd-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const claim = ref({
id: '1',
claimNo: 'C20260105001',
amount: 50000,
status: 'pending',
statusText: '待初审',
createTime: '2026-01-05 10:00:00',
reason: '借款人长期失联确认贷款逾期超过90天',
policyNo: 'P202511010023',
customerName: '张某某',
bankName: '某某商业银行'
})
const activeStep = ref(1)
function handleReject() {
uni.showModal({
title: '驳回申请',
editable: true,
placeholderText: '请输入驳回原因',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '已驳回', icon: 'none' })
}
}
})
}
function handleApprove() {
uni.showModal({
title: '确认通过',
content: '确认通过初审并提交复核?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '已通过', icon: 'success' })
}
}
})
}
</script>
<style lang="scss" scoped>
.claim-detail {
min-height: 100vh;
background: #f5f7fa;
padding-bottom: 120rpx;
}
.status-header {
background: #0957DE;
color: #fff;
padding: 40rpx 32rpx;
display: flex;
justify-content: space-between;
align-items: center;
.label { font-size: 24rpx; opacity: 0.8; margin-right: 12rpx; }
.status { font-size: 36rpx; font-weight: 600; }
.amount { font-size: 48rpx; font-weight: 700; }
}
.card {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin: 24rpx 32rpx;
.card-title {
font-size: 30rpx;
font-weight: 600;
margin-bottom: 24rpx;
padding-left: 16rpx;
border-left: 6rpx solid #0957DE;
}
.info-list {
.item {
display: flex;
justify-content: space-between;
margin-bottom: 16rpx;
font-size: 28rpx;
.label { color: #718096; }
.val { color: #2d3748; text-align: right; max-width: 60%; }
.link { color: #2b6cb0; }
}
}
.policy-preview {
background: #f7fafc;
padding: 20rpx;
border-radius: 8rpx;
.row {
display: flex;
justify-content: space-between;
margin-bottom: 8rpx;
font-size: 26rpx;
.label { color: #718096; }
.val { color: #2d3748; font-weight: 500; }
}
}
}
.action-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 24rpx 32rpx;
background: #fff;
display: flex;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
}
</style>

View File

@@ -0,0 +1,255 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '理赔处理',
},
}
</route>
<template>
<view class="claim-list-page">
<view class="tab-header">
<view
v-for="item in tabs"
:key="item.value"
class="tab-item"
:class="{ active: currentTab === item.value }"
@click="currentTab = item.value"
>
{{ item.label }}
<view class="badge" v-if="item.count">{{ item.count }}</view>
</view>
</view>
<view class="list-content">
<view
v-for="item in filteredList"
:key="item.id"
class="claim-card"
:class="item.status"
@click="goDetail(item.id)"
>
<view class="card-top">
<text class="claim-no">CASE NO.{{ item.claimNo }}</text>
<text class="time">{{ item.time }}</text>
</view>
<view class="card-main">
<view class="amount-box">
<text class="label">申请金额</text>
<text class="amount">¥{{ item.amount.toLocaleString() }}</text>
</view>
<view class="info-box">
<view class="row">
<text class="label">申请银行</text>
<text class="val">{{ item.bankName }}</text>
</view>
<view class="row">
<text class="label">关联客户</text>
<text class="val">{{ item.customerName }}</text>
</view>
</view>
</view>
<view class="card-reason">
<text class="label">理赔原因</text>
<text class="text">{{ item.reason }}</text>
</view>
<view class="card-status-bar">
<text class="status-btn" :class="item.status">{{ item.statusText }}</text>
<text class="i-carbon-chevron-right text-gray-400" />
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
const currentTab = ref('pending')
const tabs = [
{ value: 'pending', label: '待处理', count: 5 },
{ value: 'processing', label: '审核中', count: 2 },
{ value: 'completed', label: '已结案', count: 0 },
]
const claimList = ref([
{
id: '1',
claimNo: 'C20260105001',
time: '2026-01-05 10:00',
amount: 50000,
bankName: '某某商业银行',
customerName: '张某某',
reason: '贷款逾期超过90天',
status: 'pending',
statusText: '待审核'
},
{
id: '2',
claimNo: 'C20260104008',
time: '2026-01-04 15:30',
amount: 120000,
bankName: '某某农村信用社',
customerName: '李某某',
reason: '借款人经营困难,无力还款',
status: 'pending',
statusText: '待审核'
},
{
id: '3',
claimNo: 'C20260103012',
time: '2026-01-03 09:20',
amount: 35000,
bankName: '某某村镇银行',
customerName: '王某某',
reason: '意外事故导致还款能力丧失',
status: 'processing',
statusText: '复核中'
}
])
const filteredList = computed(() => {
return claimList.value.filter(item => item.status === currentTab.value)
})
function goDetail(id: string) {
uni.navigateTo({ url: `/pagesInsurance/claim/detail?id=${id}` })
}
</script>
<style lang="scss" scoped>
.claim-list-page {
min-height: 100vh;
background: #f5f7fa;
}
.tab-header {
display: flex;
background: #fff;
padding: 0 32rpx;
position: sticky;
top: 0;
z-index: 10;
border-bottom: 1rpx solid #eee;
.tab-item {
flex: 1;
text-align: center;
padding: 28rpx 0;
font-size: 28rpx;
color: #666;
position: relative;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
&.active {
color: #0957DE;
font-weight: 600;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 6rpx;
background: #0957DE;
border-radius: 4rpx;
}
}
.badge {
background: #e53e3e;
color: #fff;
font-size: 20rpx;
padding: 2rpx 10rpx;
border-radius: 20rpx;
}
}
}
.list-content {
padding: 24rpx 32rpx;
}
.claim-card {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
position: relative;
overflow: hidden;
&.pending { border-left: 8rpx solid #ff9800; }
&.processing { border-left: 8rpx solid #2196f3; }
&.completed { border-left: 8rpx solid #4caf50; }
.card-top {
display: flex;
justify-content: space-between;
font-size: 24rpx;
color: #999;
margin-bottom: 20rpx;
.claim-no { font-family: monospace; }
}
.card-main {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20rpx;
.amount-box {
.label { display: block; font-size: 22rpx; color: #666; }
.amount { font-size: 40rpx; font-weight: 700; color: #333; }
}
.info-box {
text-align: right;
.row {
font-size: 24rpx;
margin-bottom: 4rpx;
.label { color: #999; }
.val { color: #333; }
}
}
}
.card-reason {
background: #f9f9f9;
padding: 16rpx;
border-radius: 8rpx;
font-size: 26rpx;
margin-bottom: 20rpx;
color: #555;
.label { color: #999; }
}
.card-status-bar {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1rpx solid #eee;
padding-top: 16rpx;
.status-btn {
font-size: 26rpx;
font-weight: 600;
&.pending { color: #f57c00; }
&.processing { color: #0957DE; }
&.completed { color: #38a169; }
}
}
}
</style>

View File

@@ -0,0 +1,355 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '保险工作台',
navigationBarBackgroundColor: '#0957DE',
navigationBarTextStyle: 'white',
},
}
</route>
<template>
<view class="dashboard">
<!-- 顶部欢迎区 -->
<view class="header-bg">
<view class="welcome-info">
<view class="text-info">
<text class="greeting">信贷保险平台</text>
<text class="date">{{ currentDate }}</text>
</view>
<view class="avatar-box">
<text class="i-carbon-user-avatar" />
</view>
</view>
</view>
<!-- 统计卡片 -->
<view class="stats-overview">
<view class="stat-row">
<view class="stat-col">
<text class="num">{{ stats.policyCount }}</text>
<text class="label">有效保单</text>
</view>
<view class="divider" />
<view class="stat-col">
<text class="num primary">{{ stats.totalPremium }}<text class="unit"></text></text>
<text class="label">保费总额</text>
</view>
<view class="divider" />
<view class="stat-col">
<text class="num warning">{{ stats.pendingClaim }}</text>
<text class="label">待理赔</text>
</view>
</view>
</view>
<!-- 快捷功能 -->
<view class="grid-menu">
<view class="menu-item" @click="navigateTo('/pagesInsurance/policy/list')">
<view class="icon-wrap primary">
<text class="i-carbon-document-protected" />
</view>
<text class="label">保单管理</text>
</view>
<view class="menu-item" @click="navigateTo('/pagesInsurance/claim/list')">
<view class="icon-wrap warning">
<text class="i-carbon-request-quote" />
</view>
<text class="label">理赔处理</text>
</view>
<view class="menu-item" @click="navigateTo('/pagesInsurance/bank/list')">
<view class="icon-wrap info">
<text class="i-carbon-building" />
</view>
<text class="label">合作银行</text>
</view>
<view class="menu-item" @click="navigateTo('/pagesInsurance/report/index')">
<view class="icon-wrap success">
<text class="i-carbon-chart-line" />
</view>
<text class="label">业务报表</text>
</view>
</view>
<!-- 待处理理赔 -->
<view class="section-card">
<view class="card-header">
<text class="title">待处理理赔</text>
<text class="more" @click="navigateTo('/pagesInsurance/claim/list')">查看全部</text>
</view>
<view class="claim-list">
<view v-for="item in pendingClaims" :key="item.id" class="claim-item" @click="goClaimDetail(item.id)">
<view class="claim-icon">
<text class="i-carbon-warning-hex" />
</view>
<view class="claim-content">
<text class="claim-title">{{ item.bankName }} - {{ item.customerName }}</text>
<text class="claim-desc">理赔金额¥{{ item.amount.toLocaleString() }}</text>
</view>
<view class="claim-time">{{ item.time }}</view>
<text class="i-carbon-chevron-right text-gray-300 ml-2" />
</view>
</view>
</view>
<!-- 业务趋势 -->
<view class="section-card">
<view class="card-header">
<text class="title">本月概览</text>
</view>
<view class="overview-grid">
<view class="overview-item">
<text class="label">新增保单</text>
<view class="val-box">
<text class="val">86</text>
<text class="trend up">+12%</text>
</view>
</view>
<view class="overview-item">
<text class="label">保费收入</text>
<view class="val-box">
<text class="val">¥128</text>
<text class="trend up">+8%</text>
</view>
</view>
<view class="overview-item">
<text class="label">理赔案件</text>
<view class="val-box">
<text class="val">12</text>
<text class="trend down">-5%</text>
</view>
</view>
<view class="overview-item">
<text class="label">赔付率</text>
<view class="val-box">
<text class="val">2.3%</text>
<text class="trend">-</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
import dayjs from 'dayjs'
const currentDate = computed(() => dayjs().format('YYYY年MM月DD日'))
const stats = ref({
policyCount: 1256,
totalPremium: 3680,
pendingClaim: 8,
claimAmount: 45.6,
})
const pendingClaims = ref([
{ id: '1', bankName: '某某商业银行', customerName: '张某某', amount: 50000, time: '30分钟前' },
{ id: '2', bankName: '某某农村信用社', customerName: '李某某', amount: 120000, time: '2小时前' },
])
function navigateTo(url: string) {
uni.navigateTo({ url })
}
function goClaimDetail(id: string) {
uni.navigateTo({ url: `/pagesInsurance/claim/detail?id=${id}` })
}
</script>
<style lang="scss" scoped>
.dashboard {
min-height: 100vh;
background: #f5f7fa;
padding-bottom: 40rpx;
}
.header-bg {
background: #0957DE;
padding: 40rpx 32rpx 100rpx;
border-bottom-left-radius: 40rpx;
border-bottom-right-radius: 40rpx;
position: relative;
.welcome-info {
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
margin-bottom: 40rpx;
.greeting { font-size: 36rpx; font-weight: 600; display: block; }
.date { font-size: 24rpx; opacity: 0.8; margin-top: 8rpx; display: block; }
.avatar-box {
width: 80rpx;
height: 80rpx;
background: rgba(255,255,255,0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 44rpx;
}
}
}
.stats-overview {
background: #fff;
border-radius: 16rpx;
padding: 40rpx 0;
box-shadow: 0 8rpx 24rpx rgba(9, 87, 222, 0.08);
position: relative;
margin-top: -60rpx;
margin-left: 32rpx;
margin-right: 32rpx;
.stat-row {
display: flex;
align-items: center;
.stat-col {
flex: 1;
text-align: center;
.num {
font-size: 40rpx;
font-weight: 700;
color: #333;
display: block;
&.primary { color: #0957DE; }
&.warning { color: #f57c00; }
.unit { font-size: 24rpx; font-weight: 400; margin-left: 4rpx; color: #666; }
}
.label { font-size: 24rpx; color: #999; margin-top: 8rpx; display: block; }
}
.divider { width: 1rpx; height: 40rpx; background: #eee; }
}
}
.grid-menu {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20rpx;
background: #fff;
margin: 24rpx 32rpx;
padding: 32rpx 0;
border-radius: 16rpx;
.menu-item {
display: flex;
flex-direction: column;
align-items: center;
.icon-wrap {
width: 88rpx;
height: 88rpx;
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
margin-bottom: 12rpx;
&.primary { background: #e3f2fd; color: #0957DE; }
&.success { background: #e8f5e9; color: #38a169; }
&.warning { background: #fff3e0; color: #f57c00; }
&.info { background: #f3e5f5; color: #9c27b0; }
}
.label { font-size: 26rpx; color: #333; }
}
}
.section-card {
background: #fff;
margin: 24rpx 32rpx;
border-radius: 16rpx;
padding: 24rpx 32rpx;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.title { font-size: 32rpx; font-weight: 600; color: #333; display: flex; align-items: center; }
.title::before {
content: '';
display: inline-block;
width: 8rpx;
height: 32rpx;
background: #0957DE;
border-radius: 4rpx;
margin-right: 12rpx;
}
.more { font-size: 24rpx; color: #999; }
}
}
.claim-list {
.claim-item {
display: flex;
align-items: center;
padding: 24rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child { border-bottom: none; }
.claim-icon {
width: 64rpx;
height: 64rpx;
background: #fff3e0;
border-radius: 50%;
color: #f57c00;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.claim-content {
flex: 1;
.claim-title { font-size: 28rpx; color: #333; display: block; margin-bottom: 4rpx; }
.claim-desc { font-size: 24rpx; color: #d32f2f; }
}
.claim-time { font-size: 24rpx; color: #999; }
}
}
.overview-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
.overview-item {
background: #f9f9f9;
padding: 20rpx;
border-radius: 8rpx;
.label { font-size: 24rpx; color: #666; display: block; margin-bottom: 8rpx; }
.val-box {
display: flex;
align-items: flex-end;
.val { font-size: 32rpx; font-weight: 600; color: #333; margin-right: 8rpx; }
.trend {
font-size: 22rpx;
color: #999;
&.up { color: #38a169; }
&.down { color: #d32f2f; }
}
}
}
}
</style>

View File

@@ -0,0 +1,103 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '个人中心',
navigationBarBackgroundColor: '#0957DE',
navigationBarTextStyle: 'white',
},
}
</route>
<template>
<view class="me-page">
<view class="user-info-box">
<view class="flex items-center">
<view class="avatar">
<text class="i-carbon-user-avatar text-4xl" />
</view>
<view class="ml-4">
<text class="name">保险专员007</text>
<text class="dept">核保部 / 高级专员</text>
</view>
</view>
</view>
<view class="menu-group">
<wd-cell-group border>
<wd-cell title="我的保单" is-link to="/pagesInsurance/policy/list">
<template #icon>
<text class="i-carbon-document-protected text-lg mr-2 text-blue-500" />
</template>
</wd-cell>
<wd-cell title="业绩统计" is-link>
<template #icon>
<text class="i-carbon-chart-line text-lg mr-2 text-orange-500" />
</template>
</wd-cell>
<wd-cell title="联系客服" is-link>
<template #icon>
<text class="i-carbon-customer-service text-lg mr-2 text-green-500" />
</template>
</wd-cell>
<wd-cell title="账户设置" is-link>
<template #icon>
<text class="i-carbon-settings text-lg mr-2 text-gray-500" />
</template>
</wd-cell>
</wd-cell-group>
</view>
<view class="p-4 mt-4">
<wd-button type="error" plain block @click="handleLogout">退出登录</wd-button>
</view>
</view>
</template>
<script lang="ts" setup>
function handleLogout() {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
uni.reLaunch({ url: '/pages/login/index' })
}
}
})
}
</script>
<style lang="scss" scoped>
.me-page {
min-height: 100vh;
background: #f5f7fa;
}
.user-info-box {
background: #0957DE;
padding: 60rpx 40rpx 80rpx;
color: #fff;
.avatar {
width: 100rpx;
height: 100rpx;
background: rgba(255,255,255,0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.name { font-size: 36rpx; font-weight: 600; display: block; }
.dept { font-size: 26rpx; opacity: 0.8; margin-top: 8rpx; display: block; }
}
.menu-group {
margin-top: -20rpx;
background: #fff;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
overflow: hidden;
padding-top: 20rpx;
}
</style>

View File

@@ -0,0 +1,231 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '保单详情',
},
}
</route>
<template>
<view class="policy-detail">
<!-- 保单状态头 -->
<view class="detail-header">
<view class="status-icon">
<text class="i-carbon-checkmark-filled text-4xl text-green-500" v-if="policy.status === 'active'" />
<text class="i-carbon-warning-filled text-4xl text-orange-500" v-else />
</view>
<text class="status-text">{{ policy.statusText }}</text>
<text class="policy-no">NO.{{ policy.policyNo }}</text>
</view>
<!-- 基本信息 -->
<view class="section">
<view class="section-title">基本信息</view>
<view class="info-grid">
<view class="info-item">
<text class="label">投保人</text>
<text class="value">{{ policy.customerName }}</text>
</view>
<view class="info-item">
<text class="label">身份证号</text>
<text class="value">{{ policy.idCard }}</text>
</view>
<view class="info-item">
<text class="label">联系电话</text>
<text class="value">{{ policy.phone }}</text>
</view>
<view class="info-item">
<text class="label">受益人</text>
<text class="value">{{ policy.beneficiary }}{{ policy.bankName }}</text>
</view>
</view>
</view>
<!-- 保障内容 -->
<view class="section">
<view class="section-title">保障内容</view>
<view class="coverage-list">
<view class="coverage-item">
<text class="name">个人消费信贷保证保险</text>
<text class="amount">¥{{ policy.amount.toLocaleString() }}</text>
</view>
</view>
<view class="divider" />
<view class="info-row">
<text class="label">保险费</text>
<text class="value price">¥{{ policy.premium.toLocaleString() }}</text>
</view>
<view class="info-row">
<text class="label">保险期限</text>
<text class="value">{{ policy.startDate }} {{ policy.endDate }}</text>
</view>
</view>
<!-- 相关理赔 -->
<view class="section" v-if="policy.claims.length > 0">
<view class="section-title">理赔记录</view>
<view class="claim-list">
<view v-for="claim in policy.claims" :key="claim.id" class="claim-item">
<view class="claim-header">
<text class="date">{{ claim.date }}</text>
<text class="status">{{ claim.status }}</text>
</view>
<text class="desc">{{ claim.reason }}</text>
</view>
</view>
</view>
<!-- 底部操作 -->
<view class="footer-actions">
<wd-button type="info" plain class="flex-1 mr-2">电子保单</wd-button>
<wd-button type="primary" class="flex-1">发起理赔</wd-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const policy = ref({
id: '1',
policyNo: 'P202601050001',
status: 'active',
statusText: '保障中',
customerName: '张某某',
idCard: '33010619900101****',
phone: '138****0000',
beneficiary: '某某商业银行',
bankName: '某某商业银行',
amount: 500000,
premium: 2500,
startDate: '2026-01-01',
endDate: '2027-01-01',
claims: [
// { id: 1, date: '2026-06-01', status: '处理中', reason: '贷款逾期申请理赔' }
]
})
</script>
<style lang="scss" scoped>
.policy-detail {
min-height: 100vh;
background: #f5f7fa;
padding-bottom: 120rpx;
}
.detail-header {
background: #0957DE;
color: #fff;
padding: 40rpx 32rpx 60rpx;
display: flex;
flex-direction: column;
align-items: center;
.status-icon {
width: 100rpx;
height: 100rpx;
background: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
}
.status-text {
font-size: 36rpx;
font-weight: 600;
}
.policy-no {
font-size: 26rpx;
opacity: 0.8;
margin-top: 8rpx;
}
}
.section {
background: #fff;
margin: 24rpx 32rpx;
padding: 32rpx;
border-radius: 16rpx;
margin-top: -20rpx;
position: relative;
z-index: 1;
& + .section {
margin-top: 24rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #1a202c;
margin-bottom: 24rpx;
padding-left: 16rpx;
border-left: 8rpx solid #0957DE;
}
}
.info-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24rpx;
.info-item {
.label {
display: block;
font-size: 24rpx;
color: #718096;
}
.value {
display: block;
font-size: 28rpx;
color: #2d3748;
margin-top: 8rpx;
font-weight: 500;
}
}
}
.coverage-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
.name { font-size: 28rpx; color: #2d3748; }
.amount { font-size: 32rpx; font-weight: 600; color: #1a202c; }
}
.divider {
height: 1rpx;
background: #e2e8f0;
margin: 24rpx 0;
}
.info-row {
display: flex;
justify-content: space-between;
margin-bottom: 12rpx;
font-size: 28rpx;
.label { color: #718096; }
.value {
color: #2d3748;
&.price { color: #e53e3e; font-weight: 600; }
}
}
.footer-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 24rpx 32rpx;
background: #fff;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
display: flex;
z-index: 100;
}
</style>

View File

@@ -0,0 +1,275 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '保单管理',
},
}
</route>
<template>
<view class="policy-list">
<!-- 搜索和筛选 -->
<view class="header-search">
<wd-search v-model="keyword" placeholder="搜索保单号/客户名" @search="onSearch" />
<view class="filter-tabs">
<view
v-for="tab in tabs"
:key="tab.value"
class="tab-item"
:class="{ active: currentTab === tab.value }"
@click="currentTab = tab.value"
>
{{ tab.label }}
</view>
</view>
</view>
<!-- 保单列表 -->
<view class="list-container">
<view
v-for="item in filteredList"
:key="item.id"
class="policy-card"
@click="goDetail(item.id)"
>
<view class="card-header">
<text class="policy-no">NO.{{ item.policyNo }}</text>
<text class="status-tag" :class="item.status">{{ item.statusText }}</text>
</view>
<view class="card-body">
<view class="info-row">
<text class="label">投保客户</text>
<text class="value">{{ item.customerName }}</text>
</view>
<view class="info-row">
<text class="label">贷款银行</text>
<text class="value">{{ item.bankName }}</text>
</view>
<view class="info-row">
<text class="label">保额/保费</text>
<text class="value price">
¥{{ item.amount.toLocaleString() }}
<text class="sub">/ ¥{{ item.premium.toLocaleString() }}</text>
</text>
</view>
<view class="info-row">
<text class="label">保险期限</text>
<text class="value">{{ item.startDate }} {{ item.endDate }}</text>
</view>
</view>
<view class="card-footer">
<text class="time">投保时间{{ item.createTime }}</text>
<wd-button size="small" plain @click.stop="handleRenew(item)">续保</wd-button>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
const keyword = ref('')
const currentTab = ref('active')
const tabs = [
{ label: '生效中', value: 'active' },
{ label: '即将到期', value: 'expiring' },
{ label: '已失效', value: 'expired' },
]
const policyList = ref([
{
id: '1',
policyNo: 'P202601050001',
customerName: '张某某',
bankName: '某某商业银行',
amount: 500000,
premium: 2500,
startDate: '2026-01-01',
endDate: '2027-01-01',
createTime: '2026-01-01 10:00',
status: 'active',
statusText: '保障中',
},
{
id: '2',
policyNo: 'P202512150023',
customerName: '李某某',
bankName: '某某农村信用社',
amount: 300000,
premium: 1500,
startDate: '2025-12-15',
endDate: '2026-12-15',
createTime: '2025-12-15 14:30',
status: 'active',
statusText: '保障中',
},
{
id: '3',
policyNo: 'P202501010001',
customerName: '王某某',
bankName: '某某村镇银行',
amount: 200000,
premium: 1000,
startDate: '2025-01-01',
endDate: '2026-01-01',
createTime: '2025-01-01 09:00',
status: 'expiring',
statusText: '即将到期',
},
])
const filteredList = computed(() => {
let list = policyList.value
if (currentTab.value !== 'all') {
list = list.filter(item => item.status === currentTab.value)
}
if (keyword.value) {
list = list.filter(item =>
item.policyNo.includes(keyword.value) ||
item.customerName.includes(keyword.value)
)
}
return list
})
function onSearch() {
// Implement search logic
}
function goDetail(id: string) {
uni.navigateTo({ url: `/pagesInsurance/policy/detail?id=${id}` })
}
function handleRenew(item: any) {
uni.showToast({ title: '发起续保', icon: 'none' })
}
</script>
<style lang="scss" scoped>
.policy-list {
min-height: 100vh;
background: #f5f7fa;
padding-bottom: 40rpx;
}
.header-search {
background: #fff;
padding: 24rpx 32rpx 0;
position: sticky;
top: 0;
z-index: 10;
}
.filter-tabs {
display: flex;
margin-top: 20rpx;
border-bottom: 1rpx solid #e2e8f0;
.tab-item {
flex: 1;
text-align: center;
padding: 24rpx 0;
font-size: 28rpx;
color: #718096;
position: relative;
&.active {
color: #0957DE;
font-weight: 600;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 6rpx;
background: #0957DE;
border-radius: 3rpx;
}
}
}
}
.list-container {
padding: 24rpx 32rpx;
}
.policy-card {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #f0f0f0;
.policy-no {
font-size: 28rpx;
color: #4a5568;
font-family: monospace;
}
.status-tag {
font-size: 24rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
&.active { background: #e3f2fd; color: #0957DE; }
&.expiring { background: #fff3e0; color: #f57c00; }
&.expired { background: #f5f5f5; color: #9e9e9e; }
}
}
.card-body {
.info-row {
display: flex;
justify-content: space-between;
margin-bottom: 16rpx;
font-size: 28rpx;
.label { color: #718096; }
.value {
color: #2d3748;
font-weight: 500;
&.price {
color: #e53e3e;
font-weight: 700;
.sub {
font-size: 24rpx;
color: #718096;
font-weight: 400;
}
}
}
}
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 24rpx;
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
.time {
font-size: 24rpx;
color: #a0aec0;
}
}
}
</style>

View File

@@ -12,6 +12,8 @@ export enum ClientType {
USER = 'user',
MERCHANT = 'merchant',
BANK = 'bank',
GOVERNMENT = 'government',
INSURANCE = 'insurance',
}
/** 客户端类型配置 */
@@ -37,6 +39,20 @@ export const CLIENT_TYPE_CONFIG = {
description: '账款审核、金融服务',
homePage: '/pagesBank/dashboard/index',
},
[ClientType.GOVERNMENT]: {
label: '政务端',
icon: 'i-carbon-building-government',
color: '#d53f8c',
description: '合规监管、风险预警',
homePage: '/pagesGovernment/dashboard/index',
},
[ClientType.INSURANCE]: {
label: '保险端',
icon: 'i-carbon-umbrella',
color: '#3182ce',
description: '保单管理、理赔服务',
homePage: '/pagesInsurance/dashboard/index',
},
}
export const useUserStore = defineStore('user', {

View File

@@ -123,13 +123,71 @@ export const bankTabbarList: CustomTabBarItem[] = [
},
]
// ==================== 政务端 Tabbar 配置 ====================
export const governmentTabbarList: CustomTabBarItem[] = [
{
text: '工作台',
pagePath: 'pagesGovernment/dashboard/index',
iconType: 'unocss',
icon: 'i-carbon-dashboard',
},
{
pagePath: 'pagesGovernment/supervise/list',
text: '检查',
iconType: 'unocss',
icon: 'i-carbon-task',
},
{
pagePath: 'pagesGovernment/report/list',
text: '报表',
iconType: 'unocss',
icon: 'i-carbon-document',
},
{
pagePath: 'pagesGovernment/me/index',
text: '我的',
iconType: 'unocss',
icon: 'i-carbon-user',
},
]
// ==================== 保险端 Tabbar 配置 ====================
export const insuranceTabbarList: CustomTabBarItem[] = [
{
text: '工作台',
pagePath: 'pagesInsurance/dashboard/index',
iconType: 'unocss',
icon: 'i-carbon-dashboard',
},
{
pagePath: 'pagesInsurance/policy/list',
text: '保单',
iconType: 'unocss',
icon: 'i-carbon-document-protected',
},
{
pagePath: 'pagesInsurance/claim/list',
text: '理赔',
iconType: 'unocss',
icon: 'i-carbon-request-quote',
},
{
pagePath: 'pagesInsurance/me/index',
text: '我的',
iconType: 'unocss',
icon: 'i-carbon-user',
},
]
// 根据客户端类型获取对应的 tabbar 配置
export type ClientTypeKey = 'user' | 'merchant' | 'bank'
export type ClientTypeKey = 'user' | 'merchant' | 'bank' | 'government' | 'insurance'
export function getTabbarListByClientType(clientType: ClientTypeKey): CustomTabBarItem[] {
const tabbarMap: Record<ClientTypeKey, CustomTabBarItem[]> = {
user: userTabbarList,
merchant: merchantTabbarList,
bank: bankTabbarList,
government: governmentTabbarList,
insurance: insuranceTabbarList,
}
return tabbarMap[clientType] || userTabbarList
}