647 lines
16 KiB
Vue
647 lines
16 KiB
Vue
<template>
|
|
<div class="dashboard-container">
|
|
<div class="prison-name">{{ prisonerName }}</div>
|
|
<div class="current-time">{{ currentTime }}</div>
|
|
<div class="dashboard-content">
|
|
<div class="dashboard-content-top">
|
|
<div class="dashboard-content-top-left">
|
|
<InfoCard :basic-info="basicInfo" :interview-records="interviewRecords" />
|
|
</div>
|
|
<div class="dashboard-content-top-center">
|
|
<div class="gauge-container">
|
|
<div class="dashboard-content-top-center-data">
|
|
<!-- 左侧第一个卡片 -->
|
|
<div class="info-card-item">
|
|
<div class="card-number">{{ centerLeftData.top.value }}</div>
|
|
<div class="card-label">{{ centerLeftData.top.label }}</div>
|
|
</div>
|
|
<!-- 左侧第二个卡片 -->
|
|
<div class="card-row">
|
|
<div class="info-card-item">
|
|
<div class="card-number">{{ centerLeftData.middle.left.value }}</div>
|
|
<div class="card-label">{{ centerLeftData.middle.left.label }}</div>
|
|
</div>
|
|
<div class="info-card-item">
|
|
<div class="card-number">{{ centerLeftData.middle.right.value }}</div>
|
|
<div class="card-label">{{ centerLeftData.middle.right.label }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="dashboard-content-top-center-center">
|
|
<GaugeChart :height="'240px'" :value="gaugeValue" :name="gaugeName" />
|
|
</div>
|
|
<div class="dashboard-content-top-center-data">
|
|
<!-- 右侧第一个卡片 -->
|
|
<div class="info-card-item">
|
|
<div class="card-number">{{ centerRightData.top.value }}</div>
|
|
<div class="card-label">{{ centerRightData.top.label }}</div>
|
|
</div>
|
|
<!-- 右侧第二个卡片 -->
|
|
<div class="card-row">
|
|
<div class="info-card-item">
|
|
<div class="card-number">{{ centerRightData.middle.left.value }}</div>
|
|
<div class="card-label">{{ centerRightData.middle.left.label }}</div>
|
|
</div>
|
|
<div class="info-card-item">
|
|
<div class="card-number">{{ centerRightData.middle.right.value }}</div>
|
|
<div class="card-label">{{ centerRightData.middle.right.label }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="list-container">
|
|
<div class="list-card-item">
|
|
<div class="list-card-item-icon icon-location"></div>
|
|
<div class="list-card-item-value">108</div>
|
|
</div>
|
|
<div class="list-card-item">
|
|
<div class="list-card-item-icon icon-person"></div>
|
|
<div class="list-card-item-value">108</div>
|
|
</div>
|
|
<div class="list-card-item">
|
|
<div class="list-card-item-icon icon-person2"></div>
|
|
<div class="list-card-item-value">108</div>
|
|
</div>
|
|
<div class="list-card-item">
|
|
<div class="list-card-item-icon icon-car"></div>
|
|
<div class="list-card-item-value">108</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="dashboard-content-top-right">
|
|
<ScoreAssessment :data="scoreRecords" />
|
|
</div>
|
|
</div>
|
|
<div class="dashboard-content-bottom">
|
|
<div class="dashboard-content-bottom-left">
|
|
<ConsumptionRecords
|
|
:data="consumptionRecords"
|
|
:relat-data="relationships"
|
|
:remit-data="remittanceRecords"
|
|
/>
|
|
</div>
|
|
<div class="dashboard-content-bottom-center">
|
|
<RecentRewardsPunishments :data="rewardsPunishments" />
|
|
</div>
|
|
<div class="dashboard-content-bottom-right">
|
|
<div class="dashboard-content-bottom-right-title">大帐统计</div>
|
|
<BarChart :height="'200px'" :data="barChartData" :card-data="barCardData" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
// @ts-ignore
|
|
import GaugeChart from './components/GaugeChart.vue'
|
|
// @ts-ignore
|
|
import BarChart from './components/BarChart.vue'
|
|
// @ts-ignore
|
|
import InfoCard from './components/InfoCard/Index.vue'
|
|
// @ts-ignore
|
|
import ScoreAssessment from './components/ScoreAssessment/Index.vue'
|
|
// @ts-ignore
|
|
import RecentRewardsPunishments from './components/RecentRewardsPunishments/Index.vue'
|
|
// @ts-ignore
|
|
import ConsumptionRecords from './components/ConsumptionRecords/Index.vue'
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { ElMessage } from 'element-plus'
|
|
import { DashboardApi } from '@/api/prison/dashboard'
|
|
|
|
defineOptions({ name: 'Dashboard' })
|
|
|
|
const route = useRoute()
|
|
|
|
// 仪表盘数据
|
|
const gaugeValue = ref(0)
|
|
const gaugeName = ref('')
|
|
|
|
// 中心左侧数据
|
|
const centerLeftData = ref({
|
|
top: {
|
|
value: '0',
|
|
label: '加载中...'
|
|
},
|
|
middle: {
|
|
left: {
|
|
value: '0',
|
|
label: '加载中...'
|
|
},
|
|
right: {
|
|
value: '0%',
|
|
label: '加载中...'
|
|
}
|
|
},
|
|
bottom: {
|
|
value: '0位',
|
|
label: '加载中...'
|
|
}
|
|
})
|
|
|
|
// 中心右侧数据
|
|
const centerRightData = ref({
|
|
top: {
|
|
value: '0',
|
|
label: '加载中...'
|
|
},
|
|
middle: {
|
|
left: {
|
|
value: '0',
|
|
label: '加载中...'
|
|
},
|
|
right: {
|
|
value: '0',
|
|
label: '加载中...'
|
|
}
|
|
},
|
|
bottomLeft: {
|
|
value: '0位',
|
|
label: '加载中...'
|
|
},
|
|
bottomRight: {
|
|
value: '0辆',
|
|
label: '加载中...'
|
|
}
|
|
})
|
|
|
|
// 柱状图数据
|
|
const barChartData = ref<{ category: string; monthlyStandard: number; perCapita: number }[]>([])
|
|
|
|
// 卡片数据
|
|
const barCardData = ref({
|
|
inProgress: 0,
|
|
toWarehouse: 0,
|
|
outWarehouse: 0
|
|
})
|
|
|
|
// 基本信息数据
|
|
const basicInfo = ref({
|
|
district: '',
|
|
prisonNumber: '',
|
|
sentenceStart: '',
|
|
sentenceEnd: '',
|
|
sentenceDays: 0,
|
|
age: 0,
|
|
hometown: '',
|
|
education: '',
|
|
maritalStatus: '',
|
|
children: '',
|
|
birthDate: '',
|
|
crimeType: '',
|
|
previousConvictions: '',
|
|
sentence: ''
|
|
})
|
|
|
|
// 心理访谈记录数据
|
|
const interviewRecords = ref<{ date: string; content: string }[]>([])
|
|
|
|
// 计分考核数据
|
|
const scoreRecords = ref<{
|
|
date: string
|
|
score: string
|
|
scoreType: 'positive' | 'negative'
|
|
finalScore: number
|
|
level: 'excellent' | 'good' | 'poor'
|
|
levelText: string
|
|
}[]>([])
|
|
|
|
// 消费记录数据
|
|
const consumptionRecords = ref<{
|
|
date: string
|
|
name: string
|
|
nameColor: string
|
|
category: string
|
|
amount: number
|
|
}[]>([])
|
|
|
|
// 汇款记录数据
|
|
const remittanceRecords = ref<{
|
|
date: string
|
|
name: string
|
|
nameColor: string
|
|
category: string
|
|
amount: number
|
|
}[]>([])
|
|
|
|
// 关系人数据
|
|
const relationships = ref<{
|
|
name: string
|
|
relate: string
|
|
color: string
|
|
}[]>([])
|
|
|
|
// 奖惩记录数据
|
|
const rewardsPunishments = ref<{
|
|
date: string
|
|
type: string
|
|
typeText: string
|
|
content: string
|
|
}[]>([])
|
|
|
|
// 当前时间
|
|
const currentTime = ref('')
|
|
const prisonerName = ref('加载中...')
|
|
|
|
// 格式化时间
|
|
const formatTime = () => {
|
|
const now = new Date()
|
|
const year = now.getFullYear()
|
|
const month = String(now.getMonth() + 1).padStart(2, '0')
|
|
const day = String(now.getDate()).padStart(2, '0')
|
|
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
|
const weekday = weekdays[now.getDay()]
|
|
const hours = String(now.getHours()).padStart(2, '0')
|
|
const minutes = String(now.getMinutes()).padStart(2, '0')
|
|
const seconds = String(now.getSeconds()).padStart(2, '0')
|
|
|
|
return `${year}年${month}月${day}日 ${weekday} ${hours}:${minutes}:${seconds}`
|
|
}
|
|
|
|
// 加载数据
|
|
const loadData = async (prisonerId: number) => {
|
|
try {
|
|
const res = await DashboardApi.getPrisonerStats(prisonerId)
|
|
if (!res) {
|
|
prisonerName.value = '未找到该罪犯'
|
|
ElMessage.warning('未找到该罪犯信息')
|
|
return
|
|
}
|
|
|
|
// 更新罪犯名称
|
|
prisonerName.value = res.prisonerName || '未知'
|
|
|
|
// 更新仪表盘
|
|
gaugeValue.value = res.riskScore || 0
|
|
gaugeName.value = ''
|
|
|
|
// 更新中心左侧数据
|
|
if (res.centerLeftData) {
|
|
centerLeftData.value = {
|
|
top: {
|
|
value: res.centerLeftData.topValue || '0',
|
|
label: res.centerLeftData.topLabel || ''
|
|
},
|
|
middle: {
|
|
left: {
|
|
value: res.centerLeftData.middleLeftValue || '0',
|
|
label: res.centerLeftData.middleLeftLabel || ''
|
|
},
|
|
right: {
|
|
value: res.centerLeftData.middleRightValue || '0',
|
|
label: res.centerLeftData.middleRightLabel || ''
|
|
}
|
|
},
|
|
bottom: {
|
|
value: res.centerLeftData.bottomValue || '0位',
|
|
label: res.centerLeftData.bottomLabel || ''
|
|
}
|
|
}
|
|
}
|
|
|
|
// 更新中心右侧数据
|
|
if (res.centerRightData) {
|
|
centerRightData.value = {
|
|
top: {
|
|
value: res.centerRightData.topValue || '0',
|
|
label: res.centerRightData.topLabel || ''
|
|
},
|
|
middle: {
|
|
left: {
|
|
value: res.centerRightData.middleLeftValue || '0',
|
|
label: res.centerRightData.middleLeftLabel || ''
|
|
},
|
|
right: {
|
|
value: res.centerRightData.middleRightValue || '0',
|
|
label: res.centerRightData.middleRightLabel || ''
|
|
}
|
|
},
|
|
bottomLeft: {
|
|
value: res.centerRightData.bottomLeftValue || '0位',
|
|
label: res.centerRightData.bottomLeftLabel || ''
|
|
},
|
|
bottomRight: {
|
|
value: res.centerRightData.bottomRightValue || '0辆',
|
|
label: res.centerRightData.bottomRightLabel || ''
|
|
}
|
|
}
|
|
}
|
|
|
|
// 更新柱状图数据
|
|
barChartData.value = res.consumptionMonthlyData || []
|
|
|
|
// 更新消费汇总
|
|
if (res.consumptionSummary) {
|
|
barCardData.value = {
|
|
inProgress: res.consumptionSummary.inProgress || 0,
|
|
toWarehouse: res.consumptionSummary.toWarehouse || 0,
|
|
outWarehouse: res.consumptionSummary.outWarehouse || 0
|
|
}
|
|
}
|
|
|
|
// 更新基本信息
|
|
basicInfo.value = {
|
|
district: res.prisonArea || '',
|
|
prisonNumber: res.prisonerNo || '',
|
|
sentenceStart: res.imprisonmentDate || '',
|
|
sentenceEnd: res.releaseDate || '',
|
|
sentenceDays: res.servedDays || 0,
|
|
age: res.age || 0,
|
|
hometown: res.nativePlace || '',
|
|
education: res.education || '',
|
|
maritalStatus: res.maritalStatus || '',
|
|
children: res.children || '',
|
|
birthDate: res.birthDate || '',
|
|
crimeType: res.crimeType || '',
|
|
previousConvictions: res.previousConvictions || '',
|
|
sentence: res.sentence || ''
|
|
}
|
|
|
|
// 更新访谈记录
|
|
interviewRecords.value = res.interviewRecords || []
|
|
|
|
// 更新计分考核数据
|
|
scoreRecords.value = res.scoreRecords || []
|
|
|
|
// 更新消费记录
|
|
consumptionRecords.value = res.consumptionRecords || []
|
|
|
|
// 更新汇款记录
|
|
remittanceRecords.value = res.remittanceRecords || []
|
|
|
|
// 更新关系人数据
|
|
relationships.value = res.relationships || []
|
|
|
|
// 更新奖惩记录
|
|
rewardsPunishments.value = res.rewardsPunishments || []
|
|
} catch (error) {
|
|
console.error('加载Dashboard数据失败:', error)
|
|
ElMessage.error('加载数据失败,请刷新页面重试')
|
|
}
|
|
}
|
|
|
|
let timeInterval: NodeJS.Timeout | null = null
|
|
|
|
onMounted(() => {
|
|
// 初始化时间
|
|
currentTime.value = formatTime()
|
|
|
|
// 每秒更新一次时间
|
|
timeInterval = setInterval(() => {
|
|
currentTime.value = formatTime()
|
|
}, 1000)
|
|
|
|
// 从 URL 获取罪犯 ID 并加载数据
|
|
const prisonerId = route.query.prisonerId as string
|
|
if (!prisonerId) {
|
|
prisonerName.value = '请选择罪犯'
|
|
ElMessage.warning('请从服刑人员列表选择要查看的罪犯')
|
|
return
|
|
}
|
|
const id = Number(prisonerId)
|
|
if (isNaN(id) || id <= 0) {
|
|
prisonerName.value = '无效的罪犯ID'
|
|
ElMessage.error('罪犯ID无效')
|
|
return
|
|
}
|
|
loadData(id)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
// 清理定时器
|
|
if (timeInterval) {
|
|
clearInterval(timeInterval)
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.dashboard-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
background-image: url('@/assets/imgs/dashboard/dashboard-back.jpg');
|
|
background-size: cover;
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
padding: 60px 20px 20px;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.prison-name {
|
|
position: absolute;
|
|
top: 12px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
color: white;
|
|
text-align: center;
|
|
width: 100%;
|
|
z-index: 1000;
|
|
font-size: 28px;
|
|
font-weight: bold;
|
|
text-shadow: 0 2px 4px rgba(0,0,0,0.5);
|
|
}
|
|
|
|
.current-time {
|
|
position: absolute;
|
|
top: 18px;
|
|
right: 32px;
|
|
color: white;
|
|
font-size: 16px;
|
|
z-index: 1000;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.dashboard-content {
|
|
margin: 0 auto;
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
}
|
|
|
|
.dashboard-content-top {
|
|
flex: 1;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 15px;
|
|
}
|
|
.dashboard-content-top-left {
|
|
width: 25%;
|
|
height: 100%;
|
|
}
|
|
.dashboard-content-top-center {
|
|
width: 50%;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
border: 1px solid rgba(56, 102, 141, 0.5);
|
|
border-radius: 8px;
|
|
padding: 12px;
|
|
}
|
|
|
|
.gauge-container {
|
|
width: 100%;
|
|
height: 70%;
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
.list-container {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 24px;
|
|
height: 30%;
|
|
}
|
|
|
|
.list-card-item {
|
|
width: 25%;
|
|
height: 90%;
|
|
background: #2d3d5f;
|
|
border: 1px solid rgba(56, 102, 141, 0.5);
|
|
display: flex;
|
|
padding-left: 15px;
|
|
justify-content: start;
|
|
align-items: center;
|
|
}
|
|
|
|
.list-card-item-icon {
|
|
width: 24px;
|
|
height: 24px;
|
|
margin-right: 12px;
|
|
}
|
|
.icon-location {
|
|
background: url('@/assets/imgs/dashboard/icon-location.svg') no-repeat center center;
|
|
background-size: 100% 100%;
|
|
}
|
|
.icon-person {
|
|
background: url('@/assets/imgs/dashboard/icon-person.svg') no-repeat center center;
|
|
background-size: 100% 100%;
|
|
}
|
|
.icon-person2 {
|
|
background: url('@/assets/imgs/dashboard/icon-person2.svg') no-repeat center center;
|
|
background-size: 100% 100%;
|
|
}
|
|
.icon-car {
|
|
background: url('@/assets/imgs/dashboard/icon-card.svg') no-repeat center center;
|
|
background-size: 100% 100%;
|
|
}
|
|
.list-card-item-value {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: #ffffff;
|
|
}
|
|
.dashboard-content-top-center-data {
|
|
width: 30%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
padding: 16px 0 10px;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.dashboard-content-top-center-center {
|
|
width: 40%;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: relative;
|
|
padding: 8px 0;
|
|
}
|
|
|
|
// 信息卡片通用样式
|
|
.info-card-item {
|
|
border-radius: 4px;
|
|
padding: 12px 16px;
|
|
box-shadow: inset 0 0 15px 0 #2b4183;
|
|
border: 1px solid #2b4183;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.card-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
width: 100%;
|
|
}
|
|
|
|
.card-number {
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
color: #ffffff;
|
|
line-height: 1.3;
|
|
margin-bottom: 8px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.card-label {
|
|
font-size: 14px;
|
|
color: rgba(255, 255, 255, 0.85);
|
|
line-height: 1.3;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.card-icon {
|
|
width: 18px;
|
|
height: 18px;
|
|
flex-shrink: 0;
|
|
margin-top: 2px;
|
|
}
|
|
.dashboard-content-top-right {
|
|
width: 25%;
|
|
height: 100%;
|
|
}
|
|
|
|
.dashboard-content-bottom {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 15px;
|
|
}
|
|
.dashboard-content-bottom-left {
|
|
width: 35%;
|
|
height: 100%;
|
|
}
|
|
.dashboard-content-bottom-center {
|
|
width: 28%;
|
|
height: 100%;
|
|
}
|
|
.dashboard-content-bottom-right {
|
|
width: 37%;
|
|
height: 100%;
|
|
border: 1px solid rgba(56, 102, 141, 0.5);
|
|
border-radius: 8px;
|
|
padding: 12px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
.dashboard-content-bottom-right-title {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: #ffffff;
|
|
margin-bottom: 10px;
|
|
}
|
|
}
|
|
</style>
|