rpa机器人

This commit is contained in:
lSun 2025-10-23 17:55:27 +08:00
parent ae798bf252
commit 5dee166b6f
7 changed files with 1145 additions and 0 deletions

36
src/api/rpa/statistics.js Normal file
View File

@ -0,0 +1,36 @@
import request from '@/utils/request'
// 查询持证类型树
export function getTreeDataType(query) {
return request({
url: '/bmw/certificateStatistics/getTreeDataType',
method: 'get',
params: query
})
}
// 查询公司工程树
export function getTreeDataPro(query) {
return request({
url: '/bmw/certificateStatistics/getTreeDataPro',
method: 'get',
params: query
})
}
// 查询持证统计列表
export function listProjects(query) {
return request({
url: '/bmw/certificateStatistics/getCertificateTypeList',
method: 'get',
params: query
})
}
// 获取工种下拉列表
export const getPostTypeSelectListAPI = () => {
return request({
url: '/bmw/postType/listAll',
method: 'GET',
})
}

28
src/api/rpa/warning.js Normal file
View File

@ -0,0 +1,28 @@
import request from '@/utils/request'
// 查询持证类型树
export function getCategoryChartAPI(query) {
return request({
url: '/bmw/certificateStatistics/getCategoryChart',
method: 'get',
params: query
})
}
// 查询公司工程树
export function getStatusChartAPI(query) {
return request({
url: '/bmw/certificateStatistics/getStatusChart',
method: 'get',
params: query
})
}
// 查询持证统计列表
export function getCertificateWarningListAPI(query) {
return request({
url: '/bmw/certificateStatistics/getCertificateWarningList',
method: 'get',
params: query
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

View File

@ -0,0 +1,517 @@
<template>
<div class="app-container" style="display: flex;">
<!-- 左侧树形导航 -->
<div class="sidebar">
<!-- 树类型切换标签 -->
<div class="tree-tabs">
<div
:class="['tab-item', { active: activeTreeType === 'certificate' }]"
@click="switchTreeType('certificate')"
>
持证类型
</div>
<div
:class="['tab-item', { active: activeTreeType === 'project' }]"
@click="switchTreeType('project')"
>
公司工程
</div>
</div>
<!-- 树搜索 -->
<div class="tree-search">
<el-input
v-model="treeSearchKeyword"
placeholder="请输入关键字"
size="small"
clearable
>
<el-button
slot="append"
icon="el-icon-search"
@click="handleTreeSearch"
>
搜索
</el-button>
</el-input>
</div>
<!-- 树形控件 -->
<div class="tree-container">
<el-tree
v-if="activeTreeType === 'certificate'"
ref="certificateTree"
:data="certificateTreeData"
:props="treeProps"
:default-expand-all="false"
:expand-on-click-node="false"
:filter-node-method="filterNode"
node-key="id"
@node-click="handleTreeNodeClick"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ data.title }}</span>
</span>
</el-tree>
<el-tree
v-if="activeTreeType === 'project'"
ref="projectTree"
:data="projectTreeData"
:props="treeProps"
:default-expand-all="false"
:expand-on-click-node="false"
:filter-node-method="filterNode"
node-key="id"
@node-click="handleTreeNodeClick"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ data.title }}</span>
</span>
</el-tree>
</div>
</div>
<!-- 右侧内容区 -->
<div class="main-content">
<!-- 搜索表单 -->
<el-form :inline="true" :model="searchForm" size="small">
<el-form-item label="关键字">
<el-input
v-model="searchForm.keyword"
placeholder="姓名/身份证号"
clearable
style="width: 200px;"
/>
</el-form-item>
<el-form-item label="岗位类型">
<el-select v-model="searchForm.postId" placeholder="请选择" clearable style="width: 150px;">
<el-option v-for="dict in postTypeList" :key="dict.id" :label="dict.postName"
:value="dict.id"/>
</el-select>
</el-form-item>
<el-form-item label="证书状态">
<el-select v-model="searchForm.certificateState" placeholder="请选择" clearable style="width: 150px;">
<el-option label="--证件状态--" value=""/>
<el-option label="有效" value="1"/>
<el-option label="失效" value="2"/>
<el-option label="复审临期3个月" value="3"/>
<el-option label="复审临期1个月" value="4"/>
</el-select>
</el-form-item>
<el-form-item label="在职状态">
<el-select v-model="searchForm.onState" placeholder="请选择" clearable style="width: 150px;">
<el-option label="全部" value=""/>
<el-option label="在职" value="1"/>
<el-option label="离职" value="2"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button>
<el-button icon="el-icon-refresh-left" @click="handleReset">重置</el-button>
<el-button type="success" icon="el-icon-download" @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table
v-loading="loading"
:data="tableData"
>
<el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
<el-table-column prop="username" label="姓名" width="100" align="center" fixed="left" />
<el-table-column prop="idNumber" label="身份证号" width="220" align="center">
<template slot-scope="scope">
<span>{{ scope.row.showFullId ? scope.row.idNumber : maskIdNumber(scope.row.idNumber) }}</span>
<el-button
type="text"
size="mini"
:icon="scope.row.showFullId ? 'el-icon-view' : 'el-icon-view'"
@click="toggleIdVisibility(scope.row)"
style="margin-left: 5px;"
>
{{ scope.row.showFullId ? '隐藏' : '显示' }}
</el-button>
</template>
</el-table-column>
<el-table-column prop="phone" label="手机号" width="140" align="center"/>
<el-table-column prop="postName" label="工种" width="120" align="center"/>
<el-table-column prop="einTime" label="进场时间" width="160" align="center"/>
<el-table-column prop="exitTime" label="退场时间" width="160" align="center"/>
<el-table-column prop="workType" label="持证类别" width="110" align="center"/>
<el-table-column prop="workOperation" label="操作项目" width="150" align="center"/>
<el-table-column prop="reviewDate" label="应复审日期" min-width="150" align="center"/>
<el-table-column label="证件状态" min-width="150" align="center">
<template slot-scope="scope">
<span
:style="{ color: getStatusColor(scope.row.validStartDate, scope.row.validEndDate) }"
>
{{ getCertificateStatus(scope.row.validStartDate, scope.row.validEndDate) }}
</span>
</template>
</el-table-column>
<el-table-column label="证件有效期" min-width="200" align="center">
<template slot-scope="scope">
<span>
{{ formatDateRange(scope.row.validStartDate, scope.row.validEndDate) }}
</span>
</template>
</el-table-column>
<el-table-column prop="issuingAuthority" label="签发机关" min-width="150" align="center"/>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template slot-scope="scope">
<el-button
v-if="scope.row.certificatePhoto && scope.row.certificatePhoto !== 'null' && scope.row.certificatePhoto !== ''"
type="text"
size="small"
@click="handleView(scope.row)"
>
查看详情
</el-button>
<span v-else style="color: #999;">暂无数据</span>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-show="total > 0"
:total="total"
:page.sync="searchForm.pageNum"
:limit.sync="searchForm.pageSize"
@pagination="loadTableData"
/>
</div>
</div>
</template>
<script>
import {getTreeDataType, getTreeDataPro, listProjects,getPostTypeSelectListAPI} from "@/api/rpa/statistics";
export default {
name: 'CertificateStatisticsList',
dicts: ['rpa_robot'],
data() {
return {
// certificate-project-
activeTreeType: 'certificate',
//
treeSearchKeyword: '',
//
treeProps: {
children: 'children',
label: 'title'
},
//
certificateTreeData: [],
//
projectTreeData: [],
//
selectedTreeNode: null,
//
total: 0,
//
searchForm: {
pageNum: 1,
pageSize: 10,
keyword: '',
postId: '',
certificateState: '',
onState: '',
name:'',
proId:'',
subComId:'',
},
//
tableData: [],
//
loading: false,
postTypeList:[],
}
},
mounted() {
//
this.loadCertificateTreeData()
this.loadProjectTreeData()
this.loadTableData()
this.loadPostTypeList();
},
methods: {
loadPostTypeList() {
getPostTypeSelectListAPI().then(response => {
this.postTypeList = response.rows
})
},
//
switchTreeType(type) {
this.activeTreeType = type
this.treeSearchKeyword = ''
this.selectedTreeNode = null
this.loadTableData()
},
//
handleTreeSearch() {
const tree = this.activeTreeType === 'certificate'
? this.$refs.certificateTree
: this.$refs.projectTree
if (tree) {
tree.filter(this.treeSearchKeyword)
}
},
//
filterNode(value, data) {
if (!value) return true
return data.title.indexOf(value) !== -1
},
//
loadCertificateTreeData() {
//
getTreeDataType().then(response => {
this.certificateTreeData = response
}
)
},
//
loadProjectTreeData() {
//
getTreeDataPro().then(response => {
this.projectTreeData = response
}
)
},
//
handleTreeNodeClick(data) {
this.selectedTreeNode = data
console.log("selectedTreeNode",this.selectedTreeNode)
this.searchForm.name = data.name;
if(data.nodeType ==2){
this.searchForm.subComId = data.id;
this.searchForm.proId = '';
}
if(data.nodeType ==3){
this.searchForm.proId = data.id;
this.searchForm.subComId = '';
}
this.loadTableData()
},
//
loadTableData() {
this.loading = true
listProjects(this.searchForm).then((response) => {
this.tableData = response.rows
this.total = response.total
this.loading = false
})
},
//
handleSearch() {
this.loadTableData()
},
//
handleReset() {
this.searchForm = {
keyword: '',
postId: '',
certificateState: '',
onState: '',
name:'',
proId:'',
subComId:'',
}
this.loadTableData()
},
//
handleExport() {
this.download(
'bmw/certificateStatistics/exportOwnData',
{
...this.searchForm,
},
`rpa持证统计_${new Date().getTime()}.xlsx`,
)
},
//
maskIdNumber(idNumber) {
if (!idNumber || idNumber.length < 8) return idNumber
return idNumber.substring(0, 6) + '********' + idNumber.substring(14)
},
// /
toggleIdVisibility(row) {
this.$set(row, 'showFullId', !row.showFullId)
},
// -- ::
getCurrentDateTime() {
const now = new Date();
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
//
compare(time1, time2) {
if (!time1 || !time2) return -1;
const t1 = new Date(time1).getTime();
const t2 = new Date(time2).getTime();
if (t1 > t2) return 0;
if (t1 < t2) return 1;
return 2;
},
//
getCertificateStatus(startDate, endDate) {
const now = this.getCurrentDateTime();
const time1 = this.compare(now, startDate); // now > start?
const time2 = this.compare(now, endDate); // now < end?
if (time1 === 0 && time2 === 1) {
return '有效';
}
return '失效';
},
//
getStatusColor(startDate, endDate) {
const status = this.getCertificateStatus(startDate, endDate);
return status === '有效' ? 'green' : '#757575';
},
//
formatDateRange(start, end) {
const format = (dateStr) => {
if (!dateStr || dateStr === 'null') return '';
//
return dateStr.split(' ')[0];
};
const s = format(start);
const e = format(end);
return `${s} ~ ${e}`;
},
//
handleView(row) {
const url = '/data/' + certificatePhoto
window.open(url)
},
}
}
</script>
<style scoped>
.sidebar {
width: 280px;
background-color: #fff;
border-right: 1px solid #e8e8e8;
display: flex;
flex-direction: column;
}
.tree-tabs {
display: flex;
border-bottom: 1px solid #e8e8e8;
}
.tab-item {
flex: 1;
padding: 12px 0;
text-align: center;
cursor: pointer;
font-size: 14px;
color: #666;
background-color: #fafafa;
transition: all 0.3s;
}
.tab-item:hover {
color: #409eff;
}
.tab-item.active {
color: #409eff;
background-color: #fff;
font-weight: 500;
border-bottom: 2px solid #409eff;
}
.tree-search {
padding: 15px;
border-bottom: 1px solid #e8e8e8;
}
.tree-container {
flex: 1;
overflow-y: auto;
padding: 15px;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.main-content {
flex: 1;
padding-left: 20px;
overflow-y: auto;
}
.search-form {
background-color: #fff;
padding: 20px;
border-radius: 4px;
margin-bottom: 20px;
}
/* 响应式布局 */
@media (max-width: 768px) {
.sidebar {
width: 100%;
height: 300px;
}
.main-content {
padding: 10px;
}
}
</style>

View File

@ -0,0 +1,564 @@
<template>
<div id="content" class="app-container" style="display: flex;">
<!-- 左侧图表区域 -->
<div class="chart-section">
<div class="chart-container">
<h2>证件类别统计</h2>
<h6>取前三个证件类型</h6>
<div id="categoryChart" class="chart"></div>
<h2 style="margin-top: 40px;">证件状态统计</h2>
<div id="statusChart" class="chart"></div>
</div>
</div>
<!-- 右侧内容区域 -->
<div class="content-section">
<!-- 搜索表单 -->
<div class="search-header">
<el-form :inline="true" :model="searchForm" class="search-form">
<h2 style="display: inline-block; margin-right: 20px;">证件预警</h2>
<el-form-item>
<el-date-picker
v-model="searchForm.monthRange"
type="monthrange"
range-separator="~"
start-placeholder="开始月份"
end-placeholder="结束月份"
value-format="yyyy-MM"
:clearable="false"
style="width: 240px;">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.onState" placeholder="--在职状态--" clearable style="width: 140px;">
<el-option label="在职" value="1"></el-option>
<el-option label="离职" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.riskWarning" placeholder="--风险预警--" clearable style="width: 140px;">
<el-option label="即将过期" value="即将过期"></el-option>
<el-option label="当月过期" value="当月过期"></el-option>
<el-option label="已过期" value="已过期"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="handleSearch">搜索</el-button>
<el-button size="small" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 数据表格 -->
<div class="table-container">
<el-table
:data="tableData"
v-loading="loading"
>
<el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
<el-table-column prop="username" label="姓名" width="100" fixed="left"></el-table-column>
<el-table-column prop="idNumber" label="身份证号" width="220" align="center">
<template slot-scope="scope">
<span>{{ scope.row.showFullId ? scope.row.idNumber : maskIdNumber(scope.row.idNumber) }}</span>
<el-button
type="text"
size="mini"
:icon="scope.row.showFullId ? 'el-icon-view' : 'el-icon-view'"
@click="toggleIdVisibility(scope.row)"
style="margin-left: 5px;"
>
{{ scope.row.showFullId ? '隐藏' : '显示' }}
</el-button>
</template>
</el-table-column>
<el-table-column prop="einTime" label="入场时间" width="160"></el-table-column>
<el-table-column prop="exitTime" label="出场时间" width="160"></el-table-column>
<el-table-column prop="workType" label="持证类别" min-width="120"></el-table-column>
<el-table-column prop="workOperation" label="操作项目" min-width="200"></el-table-column>
<el-table-column prop="reviewDate" label="应复审日期" width="110"></el-table-column>
<el-table-column label="证件有效期" min-width="200" align="center">
<template slot-scope="scope">
<span>
{{ formatDateRange(scope.row.validStartDate, scope.row.validEndDate) }}
</span>
</template>
</el-table-column>
<el-table-column label="风险预警" width="120">
<template slot-scope="scope">
<div class="risk-warning-cell">
<img v-if="scope.row.riskWarning === '已过期'" src="@/assets/images/redWarning.png" width="28" height="22"/>
<img v-else-if="scope.row.riskWarning === '当月过期'" src="@/assets/images/grWarning.png" width="28" height="22"/>
<img v-else-if="scope.row.riskWarning === '即将过期'" src="@/assets/images/yeWarning.png" width="28" height="22"/>
<span style="margin-left: 8px;">{{ scope.row.riskWarning }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right">
<template slot-scope="scope">
<el-button
v-if="scope.row.certificatePhoto"
type="text"
size="small"
@click="viewCertificate(scope.row.certificatePhoto)">
详情
</el-button>
<span v-else>暂无数据</span>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-show="total > 0"
:total="total"
:page.sync="searchForm.pageNum"
:limit.sync="searchForm.pageSize"
@pagination="fetchTableData"
/>
</div>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import {getCategoryChartAPI, getStatusChartAPI, getCertificateWarningListAPI} from "@/api/rpa/warning";
export default {
name: 'CertificateWarningList',
data() {
//
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
const currentMonth = year + '-' + (month < 10 ? '0' + month : month)
return {
searchForm: {
pageNum: 1,
pageSize: 10,
monthRange: [currentMonth, currentMonth],
startTime:'',
endTime:'',
onState: '',
riskWarning: ''
},
tableData: [],
loading: false,
//
total: 0,
categoryChart: null,
statusChart: null,
eyeVisible: {}, //
idNumberVisible: {}, //
idNumberCache: {} //
}
},
mounted() {
this.initCharts()
this.getCategoryChart()
this.getStatusChart()
this.fetchTableData()
},
methods: {
//
initCharts() {
this.categoryChart = echarts.init(document.getElementById('categoryChart'))
this.statusChart = echarts.init(document.getElementById('statusChart'))
},
//
getCategoryChart() {
getCategoryChartAPI().then(response => {
const data = response;
const colors = ['#ff9eb2', '#7ec2ff', '#ffd663', '#37e2d2', '#9896ff']
let totalSum = 0
let formattedData = []
if (data && data.length > 0) {
totalSum = data.reduce((sum, item) => sum + Number(item.total), 0)
formattedData = data.map((item, index) => ({
value: Number(item.total),
name: item.name,
percentage: ((Number(item.total) / totalSum) * 100).toFixed(2),
itemStyle: {color: colors[index % colors.length]}
}))
}
const options = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)',
position: 'mouse'
},
legend: {
orient: 'vertical',
right: '2%',
top: 'middle',
itemWidth: 10,
itemHeight: 10,
itemGap: 15,
icon: 'circle',
formatter: (name) => {
const item = formattedData.find(item => item.name === name)
const num = item ? item.percentage : 0
return `${name} ${item ? item.value : 0}${num}%`
},
textStyle: {
fontSize: 12
}
},
series: [{
type: 'pie',
radius: ['40%', '70%'],
center: ['40%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 0,
borderWidth: 2,
borderColor: '#fff'
},
label: {
show: true,
position: 'center',
formatter: () => {
return `{total|${totalSum}}\n{name|证件总数}`
},
rich: {
total: {
fontSize: 28,
fontWeight: 'bold',
color: '#333'
},
name: {
fontSize: 14,
color: '#999'
}
}
},
emphasis: {
label: {
show: false
}
},
data: formattedData
}]
}
this.categoryChart.setOption(options)
//
this.categoryChart.on('click', (params) => {
this.openStatistics(params.name)
})
}).catch(error => {
console.error('获取证件类别统计失败:', error)
})
},
//
getStatusChart() {
getStatusChartAPI().then(response => {
const data = response
let chartData = []
let totalNum = 0
if (data && data.length > 0) {
const {effective, beOverdue, expiringSoon} = data[0]
const effectiveNum = Number(effective) || 0
const beOverdueNum = Number(beOverdue) || 0
const expiringSoonNum = Number(expiringSoon) || 0
totalNum = effectiveNum + beOverdueNum + expiringSoonNum
chartData = [
{
value: effectiveNum,
name: '有效',
percentage: totalNum > 0 ? ((effectiveNum / totalNum) * 100).toFixed(2) : 0,
itemStyle: {color: '#37e2d2'}
},
{
value: beOverdueNum,
name: '过期',
percentage: totalNum > 0 ? ((beOverdueNum / totalNum) * 100).toFixed(2) : 0,
itemStyle: {color: '#ff9eb2'}
},
{
value: expiringSoonNum,
name: '即将过期',
percentage: totalNum > 0 ? ((expiringSoonNum / totalNum) * 100).toFixed(2) : 0,
itemStyle: {color: '#ffd663'}
}
]
}
const options = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)',
position: 'mouse'
},
legend: {
orient: 'vertical',
right: '2%',
top: 'middle',
itemWidth: 10,
itemHeight: 10,
itemGap: 15,
icon: 'circle',
formatter: (name) => {
const item = chartData.find(item => item.name === name)
const num = item ? item.percentage : 0
return `${name} ${item ? item.value : 0}${num}%`
},
textStyle: {
fontSize: 12
}
},
series: [{
type: 'pie',
radius: ['40%', '70%'],
center: ['40%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 0,
borderWidth: 2,
borderColor: '#fff'
},
label: {
show: true,
position: 'center',
formatter: () => {
return `{total|${totalNum}}\n{name|证件总数}`
},
rich: {
total: {
fontSize: 28,
fontWeight: 'bold',
color: '#333'
},
name: {
fontSize: 14,
color: '#999'
}
}
},
emphasis: {
label: {
show: false
}
},
data: chartData
}]
}
this.statusChart.setOption(options)
//
this.statusChart.on('click', (params) => {
this.openStatistics(params.name)
})
}).catch(error => {
console.error('获取证件状态统计失败:', error)
})
},
//
fetchTableData() {
this.loading = true
//
if (this.searchForm.monthRange && this.searchForm.monthRange.length === 2) {
this.searchForm.startTime = this.searchForm.monthRange[0]
this.searchForm.endTime = this.searchForm.monthRange[1]
}
getCertificateWarningListAPI(this.searchForm).then((response) => {
this.tableData = response.rows
console.log(this.tableData)
this.total = response.total
this.loading = false
})
},
//
handleSearch() {
this.fetchTableData()
},
//
handleReset() {
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
const currentMonth = year + '-' + (month < 10 ? '0' + month : month)
this.searchForm = {
monthRange: [currentMonth, currentMonth],
onState: '',
riskWarning: '',
pageNum: 1,
pageSize: 10,
startTime:'',
endTime:'',
}
this.fetchTableData()
},
//
getIdNumber(row, index) {
if (!row.idNumber) return ''
// 使
if (this.idNumberCache[index]) {
return row.idNumber
}
//
return row.idNumber.slice(0, 4) + '*********' + row.idNumber.slice(14)
},
//
showEye(index) {
this.$set(this.eyeVisible, index, true)
},
//
hideEye(index) {
this.$set(this.eyeVisible, index, false)
},
//
toggleIdNumber(index, idNumber) {
this.$set(this.idNumberCache, index, !this.idNumberCache[index])
this.$forceUpdate()
},
//
viewCertificate(certificatePhoto) {
const url = '/data/' + certificatePhoto
window.open(url)
},
//
openStatistics(name) {
this.$router.push('/rpa/statistics');
},
//
maskIdNumber(idNumber) {
if (!idNumber || idNumber.length < 8) return idNumber
return idNumber.substring(0, 6) + '********' + idNumber.substring(14)
},
// /
toggleIdVisibility(row) {
this.$set(row, 'showFullId', !row.showFullId)
},
//
formatDateRange(start, end) {
const format = (dateStr) => {
if (!dateStr || dateStr === 'null') return '';
//
return dateStr.split(' ')[0];
};
const s = format(start);
const e = format(end);
return `${s} ~ ${e}`;
},
}
}
</script>
<style scoped>
.chart-section {
width: 33.33%;
height: 100%;
background-color: #fff;
overflow-y: auto;
}
.chart-container h2 {
font-size: 18px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
}
.chart-container h6 {
font-size: 12px;
color: #999;
margin-bottom: 20px;
}
.chart {
width: 100%;
height: 300px;
}
.content-section {
flex: 1;
overflow-y: auto;
}
.search-header {
background-color: #fff;
margin-bottom: 20px;
border-radius: 4px;
}
.search-form {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.table-container {
background-color: #fff;
border-radius: 4px;
}
.id-number-cell {
display: flex;
align-items: center;
justify-content: space-between;
}
.eye-icon {
cursor: pointer;
font-size: 16px;
color: #409EFF;
margin-left: 10px;
}
.eye-icon:hover {
color: #66b1ff;
}
.risk-warning-cell {
display: flex;
align-items: center;
}
.risk-warning-cell img {
margin-right: 8px;
}
/* 响应式布局 */
@media (max-width: 768px) {
.chart-section {
width: 100%;
height: auto;
}
.content-section {
width: 100%;
}
}
</style>