Compare commits
No commits in common. "08917db45bb1f2bd4e22e7e7f1331fb326ec11b7" and "7272342fe6955f1d9e1c62e4a2b6a03069cb05a7" have entirely different histories.
08917db45b
...
7272342fe6
@ -231,13 +231,6 @@ export interface PrisonerDashboardStatsRespVO {
|
|||||||
riskScore: number // 风险评估分
|
riskScore: number // 风险评估分
|
||||||
riskLevel: number // 风险等级
|
riskLevel: number // 风险等级
|
||||||
|
|
||||||
// 累计数据
|
|
||||||
violationCount: number // 累计违规次数
|
|
||||||
praiseDays: string // 累计表扬天数
|
|
||||||
praiseCount: number // 累计表扬次数
|
|
||||||
penaltyCount: number // 累计扣分次数
|
|
||||||
rewardCount: number // 累计加分次数
|
|
||||||
|
|
||||||
// 中心数据
|
// 中心数据
|
||||||
centerLeftData: CenterLeftData
|
centerLeftData: CenterLeftData
|
||||||
centerRightData: CenterRightData
|
centerRightData: CenterRightData
|
||||||
|
|||||||
@ -70,9 +70,10 @@ service.interceptors.request.use(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 监狱系统:即使 tenantEnable 为 false,也尝试获取并设置租户 ID
|
// 监狱系统:即使 tenantEnable 为 false,也尝试获取并设置租户 ID
|
||||||
// 如果缓存中没有租户 ID,使用默认值 1
|
const tenantId = getTenantId()
|
||||||
const tenantId = getTenantId() || 1
|
if (tenantId) {
|
||||||
config.headers['tenant-id'] = tenantId
|
config.headers['tenant-id'] = tenantId
|
||||||
|
}
|
||||||
const method = config.method?.toUpperCase()
|
const method = config.method?.toUpperCase()
|
||||||
// 防止 GET 请求缓存
|
// 防止 GET 请求缓存
|
||||||
if (method === 'GET') {
|
if (method === 'GET') {
|
||||||
|
|||||||
@ -10,19 +10,20 @@
|
|||||||
<div class="dashboard-content-top-center">
|
<div class="dashboard-content-top-center">
|
||||||
<div class="gauge-container">
|
<div class="gauge-container">
|
||||||
<div class="dashboard-content-top-center-data">
|
<div class="dashboard-content-top-center-data">
|
||||||
<!-- 左侧区域:上1下2排列 -->
|
<!-- 左侧第一个卡片 -->
|
||||||
<div class="info-field-item top-field">
|
<div class="info-card-item">
|
||||||
<div class="field-label">累计服刑天数</div>
|
<div class="card-number">{{ centerLeftData.top.value }}</div>
|
||||||
<div class="field-value">{{ servedDays }}</div>
|
<div class="card-label">{{ centerLeftData.top.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 左侧第二个卡片 -->
|
||||||
<div class="card-row">
|
<div class="card-row">
|
||||||
<div class="info-field-item">
|
<div class="info-card-item center-right-card-item">
|
||||||
<div class="field-label">累计违规次数</div>
|
<div class="card-number">{{ centerLeftData.middle.left.value }}</div>
|
||||||
<div class="field-value">{{ violationCount }}</div>
|
<div class="card-label">{{ centerLeftData.middle.left.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-field-item">
|
<div class="info-card-item center-right-card-item">
|
||||||
<div class="field-label">累计表扬次数</div>
|
<div class="card-number">{{ centerLeftData.middle.right.value }}</div>
|
||||||
<div class="field-value">{{ praiseCount }}</div>
|
<div class="card-label">{{ centerLeftData.middle.right.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -30,19 +31,20 @@
|
|||||||
<GaugeChart :height="'240px'" :value="gaugeValue" :name="gaugeName" />
|
<GaugeChart :height="'240px'" :value="gaugeValue" :name="gaugeName" />
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-content-top-center-data">
|
<div class="dashboard-content-top-center-data">
|
||||||
<!-- 右侧区域:上1下2排列 -->
|
<!-- 右侧第一个卡片 -->
|
||||||
<div class="info-field-item top-field">
|
<div class="info-card-item">
|
||||||
<div class="field-label">剩余刑期天数</div>
|
<div class="card-number">{{ centerRightData.top.value }}</div>
|
||||||
<div class="field-value">{{ remainingDays }}</div>
|
<div class="card-label">{{ centerRightData.top.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 右侧第二个卡片 -->
|
||||||
<div class="card-row">
|
<div class="card-row">
|
||||||
<div class="info-field-item">
|
<div class="info-card-item center-right-card-item">
|
||||||
<div class="field-label">累计扣分次数</div>
|
<div class="card-number">{{ centerRightData.middle.left.value }}</div>
|
||||||
<div class="field-value">{{ penaltyCount }}</div>
|
<div class="card-label">{{ centerRightData.middle.left.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-field-item">
|
<div class="info-card-item center-right-card-item">
|
||||||
<div class="field-label">累计加分次数</div>
|
<div class="card-number">{{ centerRightData.middle.right.value }}</div>
|
||||||
<div class="field-value">{{ rewardCount }}</div>
|
<div class="card-label">{{ centerRightData.middle.right.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -83,7 +85,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="dashboard-content-bottom-right">
|
<div class="dashboard-content-bottom-right">
|
||||||
<div class="dashboard-content-bottom-right-title">大帐统计</div>
|
<div class="dashboard-content-bottom-right-title">大帐统计</div>
|
||||||
<BarChart :data="barChartData" :balance="balance" />
|
<BarChart :height="'200px'" :data="barChartData" :card-data="barCardData" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -169,8 +171,12 @@ const centerRightData = ref({
|
|||||||
// 柱状图数据
|
// 柱状图数据
|
||||||
const barChartData = ref<{ category: string; monthlyStandard: number; perCapita: number }[]>([])
|
const barChartData = ref<{ category: string; monthlyStandard: number; perCapita: number }[]>([])
|
||||||
|
|
||||||
// 账户余额
|
// 卡片数据
|
||||||
const balance = ref(0)
|
const barCardData = ref({
|
||||||
|
inProgress: 0,
|
||||||
|
toWarehouse: 0,
|
||||||
|
outWarehouse: 0
|
||||||
|
})
|
||||||
|
|
||||||
// 基本信息数据
|
// 基本信息数据
|
||||||
const basicInfo = ref({
|
const basicInfo = ref({
|
||||||
@ -240,16 +246,6 @@ const rewardsPunishments = ref<{
|
|||||||
const currentTime = ref('')
|
const currentTime = ref('')
|
||||||
const prisonerName = ref('加载中...')
|
const prisonerName = ref('加载中...')
|
||||||
|
|
||||||
// 左侧区域三个字段数据
|
|
||||||
const servedDays = ref(0)
|
|
||||||
const violationCount = ref(0)
|
|
||||||
const praiseCount = ref(0)
|
|
||||||
|
|
||||||
// 右侧区域三个字段数据
|
|
||||||
const remainingDays = ref(0)
|
|
||||||
const penaltyCount = ref(0)
|
|
||||||
const rewardCount = ref(0)
|
|
||||||
|
|
||||||
// 格式化时间
|
// 格式化时间
|
||||||
const formatTime = () => {
|
const formatTime = () => {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
@ -283,14 +279,13 @@ const loadData = async (prisonerId: number) => {
|
|||||||
gaugeName.value = ''
|
gaugeName.value = ''
|
||||||
|
|
||||||
// 计算累计服刑天数和剩余刑期天数
|
// 计算累计服刑天数和剩余刑期天数
|
||||||
const servedDaysValue = res.servedDays || 0;
|
const servedDays = res.servedDays || 0;
|
||||||
servedDays.value = servedDaysValue
|
let remainingDays = 0;
|
||||||
remainingDays.value = 0;
|
|
||||||
if (res.imprisonmentDate && res.releaseDate) {
|
if (res.imprisonmentDate && res.releaseDate) {
|
||||||
const startDate = new Date(res.imprisonmentDate);
|
const startDate = new Date(res.imprisonmentDate);
|
||||||
const endDate = new Date(res.releaseDate);
|
const endDate = new Date(res.releaseDate);
|
||||||
const totalDays = Math.floor((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
|
const totalDays = Math.floor((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
|
||||||
remainingDays.value = Math.max(0, totalDays - servedDaysValue);
|
remainingDays = Math.max(0, totalDays - servedDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取计分考核数据 - 累计扣分次数和累计加分次数
|
// 获取计分考核数据 - 累计扣分次数和累计加分次数
|
||||||
@ -310,10 +305,6 @@ const loadData = async (prisonerId: number) => {
|
|||||||
console.error('获取计分考核数据失败:', error)
|
console.error('获取计分考核数据失败:', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新右侧区域三个字段数据
|
|
||||||
penaltyCount.value = totalPenaltyCount
|
|
||||||
rewardCount.value = totalRewardCount
|
|
||||||
|
|
||||||
// 获取狱情收集数据 - 累计违规次数
|
// 获取狱情收集数据 - 累计违规次数
|
||||||
let totalViolationCount = 0
|
let totalViolationCount = 0
|
||||||
try {
|
try {
|
||||||
@ -328,10 +319,6 @@ const loadData = async (prisonerId: number) => {
|
|||||||
console.error('获取狱情收集数据失败:', error)
|
console.error('获取狱情收集数据失败:', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新左侧区域三个字段数据
|
|
||||||
violationCount.value = totalViolationCount
|
|
||||||
praiseCount.value = res.praiseCount || 0
|
|
||||||
|
|
||||||
// 更新中心左侧数据
|
// 更新中心左侧数据
|
||||||
if (res.centerLeftData) {
|
if (res.centerLeftData) {
|
||||||
centerLeftData.value = {
|
centerLeftData.value = {
|
||||||
@ -387,8 +374,14 @@ const loadData = async (prisonerId: number) => {
|
|||||||
// 更新柱状图数据
|
// 更新柱状图数据
|
||||||
barChartData.value = res.consumptionMonthlyData || []
|
barChartData.value = res.consumptionMonthlyData || []
|
||||||
|
|
||||||
// 更新账户余额
|
// 更新消费汇总
|
||||||
balance.value = res.balance || 0
|
if (res.consumptionSummary) {
|
||||||
|
barCardData.value = {
|
||||||
|
inProgress: res.consumptionSummary.inProgress || 0,
|
||||||
|
toWarehouse: res.consumptionSummary.toWarehouse || 0,
|
||||||
|
outWarehouse: res.consumptionSummary.outWarehouse || 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 更新基本信息
|
// 更新基本信息
|
||||||
basicInfo.value = {
|
basicInfo.value = {
|
||||||
@ -396,7 +389,7 @@ const loadData = async (prisonerId: number) => {
|
|||||||
prisonNumber: res.prisonerNo || '',
|
prisonNumber: res.prisonerNo || '',
|
||||||
sentenceStart: res.imprisonmentDate || '',
|
sentenceStart: res.imprisonmentDate || '',
|
||||||
sentenceEnd: res.releaseDate || '',
|
sentenceEnd: res.releaseDate || '',
|
||||||
sentenceDays: res.sentenceDays || 0,
|
sentenceDays: res.servedDays || 0,
|
||||||
age: res.age || 0,
|
age: res.age || 0,
|
||||||
hometown: res.nativePlace || '',
|
hometown: res.nativePlace || '',
|
||||||
education: res.education || '',
|
education: res.education || '',
|
||||||
@ -557,7 +550,7 @@ onUnmounted(() => {
|
|||||||
.list-card-item {
|
.list-card-item {
|
||||||
width: 25%;
|
width: 25%;
|
||||||
height: 90%;
|
height: 90%;
|
||||||
background: rgba(45, 65, 131, 0.6);
|
background: #2d3d5f;
|
||||||
border: 1px solid rgba(56, 102, 141, 0.5);
|
border: 1px solid rgba(56, 102, 141, 0.5);
|
||||||
display: flex;
|
display: flex;
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
@ -600,55 +593,6 @@ onUnmounted(() => {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 纵向字段项样式
|
|
||||||
.info-field-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 12px 16px;
|
|
||||||
background: rgba(45, 65, 131, 0.6);
|
|
||||||
border: 1px solid rgba(56, 102, 141, 0.5);
|
|
||||||
border-radius: 6px;
|
|
||||||
box-shadow: inset 0 0 10px 0 rgba(43, 65, 131, 0.3);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: rgba(56, 102, 141, 0.8);
|
|
||||||
box-shadow: inset 0 0 15px 0 rgba(43, 65, 131, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 顶部字段样式(用于上1下2布局中的顶部字段)
|
|
||||||
.top-field {
|
|
||||||
height: 50%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字段标签样式
|
|
||||||
.field-label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255, 0.85);
|
|
||||||
line-height: 1.5;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字段数值样式
|
|
||||||
.field-value {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #ffffff;
|
|
||||||
line-height: 1.3;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-content-top-center-center {
|
.dashboard-content-top-center-center {
|
||||||
width: 40%;
|
width: 40%;
|
||||||
height: 220px;
|
height: 220px;
|
||||||
|
|||||||
@ -1,7 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="supply-chart-container" ref="containerRef">
|
<div class="supply-chart-container">
|
||||||
|
<!-- 卡片统计 -->
|
||||||
|
<div class="chart-cards">
|
||||||
|
<div class="chart-card-item">
|
||||||
|
<div class="card-value">{{ cardData.inProgress }}</div>
|
||||||
|
<div class="card-label">进行中</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-card-item">
|
||||||
|
<div class="card-value">{{ cardData.toWarehouse }}</div>
|
||||||
|
<div class="card-label">待入库</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart-card-item">
|
||||||
|
<div class="card-value">{{ cardData.outWarehouse }}</div>
|
||||||
|
<div class="card-label">已出库</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- 柱状图 -->
|
<!-- 柱状图 -->
|
||||||
<EChart ref="chartRef" :options="barOption" :height="height" />
|
<EChart :options="barOption" :height="height" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -9,7 +24,7 @@
|
|||||||
import type { EChartsOption } from 'echarts'
|
import type { EChartsOption } from 'echarts'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import EChart from '@/components/Echart/src/Echart.vue'
|
import EChart from '@/components/Echart/src/Echart.vue'
|
||||||
import { computed, ref, onMounted, watch } from 'vue'
|
import { computed, watch } from 'vue'
|
||||||
|
|
||||||
defineOptions({ name: 'BarChart' })
|
defineOptions({ name: 'BarChart' })
|
||||||
|
|
||||||
@ -19,45 +34,49 @@ interface ChartDataItem {
|
|||||||
perCapita: number
|
perCapita: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CardData {
|
||||||
|
inProgress: number
|
||||||
|
toWarehouse: number
|
||||||
|
outWarehouse: number
|
||||||
|
}
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
width?: number | string
|
width?: number
|
||||||
height?: string
|
height?: string
|
||||||
data?: ChartDataItem[]
|
data?: ChartDataItem[]
|
||||||
balance?: number
|
cardData?: CardData
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
width: '100%',
|
width: 400,
|
||||||
|
height: '300px',
|
||||||
data: () => [],
|
data: () => [],
|
||||||
balance: () => 0
|
cardData: () => ({
|
||||||
|
inProgress: 5,
|
||||||
|
toWarehouse: 5,
|
||||||
|
outWarehouse: 5
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const containerRef = ref<HTMLElement>()
|
|
||||||
const chartRef = ref()
|
|
||||||
|
|
||||||
// 创建图表配置
|
// 创建图表配置
|
||||||
const createChartOption = (): EChartsOption => {
|
const createChartOption = (): EChartsOption => {
|
||||||
const categories = props.data.map((item) => item.category)
|
const categories = props.data.map((item) => item.category)
|
||||||
const monthlyStandardData = props.data.map((item) => item.monthlyStandard ?? 0)
|
const monthlyStandardData = props.data.map((item) => item.monthlyStandard ?? 0)
|
||||||
const perCapitaData = props.data.map((item) => item.perCapita ?? 0)
|
const perCapitaData = props.data.map((item) => item.perCapita ?? 0)
|
||||||
|
|
||||||
// 动态计算最大值,确保能够显示所有数据
|
// 创建底色数据(最大值50)
|
||||||
const maxDataValue = Math.max(...monthlyStandardData, ...perCapitaData, 100)
|
const maxValue = 50
|
||||||
// 向上取整到百位,并留出 20% 空间
|
const monthlyStandardBgData = categories.map((_, index) => maxValue - monthlyStandardData[index])
|
||||||
const maxValue = Math.ceil(maxDataValue * 1.2 / 100) * 100
|
const perCapitaBgData = categories.map((_, index) => maxValue - perCapitaData[index])
|
||||||
|
|
||||||
// 创建底色数据(填充到 maxValue)
|
|
||||||
const monthlyStandardBgData = categories.map((_, index) => Math.max(0, maxValue - monthlyStandardData[index]))
|
|
||||||
const perCapitaBgData = categories.map((_, index) => Math.max(0, maxValue - perCapitaData[index]))
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
grid: {
|
grid: {
|
||||||
left: '8%',
|
left: '10%',
|
||||||
right: '8%',
|
right: '15%',
|
||||||
top: '25%',
|
top: '20%',
|
||||||
bottom: '18%',
|
bottom: '15%',
|
||||||
containLabel: false
|
containLabel: false
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
@ -81,8 +100,8 @@ const createChartOption = (): EChartsOption => {
|
|||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: maxValue,
|
max: 50,
|
||||||
interval: Math.ceil(maxValue / 5 / 100) * 100,
|
interval: 10,
|
||||||
axisLine: {
|
axisLine: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
@ -91,8 +110,7 @@ const createChartOption = (): EChartsOption => {
|
|||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
color: '#D8F0FF',
|
color: '#D8F0FF',
|
||||||
fontSize: 10,
|
fontSize: 10
|
||||||
formatter: (value: number) => value.toString()
|
|
||||||
},
|
},
|
||||||
splitLine: {
|
splitLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
@ -101,33 +119,17 @@ const createChartOption = (): EChartsOption => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 图例和余额放在同一行
|
|
||||||
legend: {
|
legend: {
|
||||||
data: [
|
data: ['支出', '收入'],
|
||||||
{ name: '支出', icon: 'rect' },
|
top: '5%',
|
||||||
{ name: '收入', icon: 'rect' }
|
right: '10%',
|
||||||
],
|
|
||||||
top: '3%',
|
|
||||||
left: '8%',
|
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: '#6D869A',
|
color: '#6D869A',
|
||||||
fontSize: 10
|
fontSize: 9
|
||||||
},
|
},
|
||||||
itemWidth: 12,
|
itemWidth: 9,
|
||||||
itemHeight: 8,
|
itemHeight: 9,
|
||||||
itemGap: 15
|
itemGap: 25
|
||||||
},
|
|
||||||
// 使用 title 显示余额,放在图例右侧
|
|
||||||
title: {
|
|
||||||
text: `账户余额: ${props.balance}元`,
|
|
||||||
left: 'auto',
|
|
||||||
right: '8%',
|
|
||||||
top: '3%',
|
|
||||||
textStyle: {
|
|
||||||
color: '#00d4ff',
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 'bold'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
@ -142,6 +144,7 @@ const createChartOption = (): EChartsOption => {
|
|||||||
},
|
},
|
||||||
formatter: function (params: any) {
|
formatter: function (params: any) {
|
||||||
let result = params[0].name + '<br/>'
|
let result = params[0].name + '<br/>'
|
||||||
|
// 只显示数据系列,不显示底色系列
|
||||||
params.forEach((param: any) => {
|
params.forEach((param: any) => {
|
||||||
if (param.seriesName === '支出' || param.seriesName === '收入') {
|
if (param.seriesName === '支出' || param.seriesName === '收入') {
|
||||||
result += param.marker + param.seriesName + ': ' + param.value + '<br/>'
|
result += param.marker + param.seriesName + ': ' + param.value + '<br/>'
|
||||||
@ -151,7 +154,7 @@ const createChartOption = (): EChartsOption => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
// 支出数据
|
// 支出数据(渐变)- 先绘制,作为底层
|
||||||
{
|
{
|
||||||
name: '支出',
|
name: '支出',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
@ -162,36 +165,40 @@ const createChartOption = (): EChartsOption => {
|
|||||||
type: 'linear',
|
type: 'linear',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
x2: 1,
|
x2: 0,
|
||||||
y2: 0,
|
y2: 1,
|
||||||
colorStops: [
|
colorStops: [
|
||||||
{ offset: 0, color: '#10A0F2' },
|
{
|
||||||
{ offset: 0.5, color: '#0D8BD9' },
|
offset: 0,
|
||||||
{ offset: 1, color: '#0A6EB0' }
|
color: '#10A0F2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(0, 82, 184, 0)'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
borderRadius: [2, 2, 0, 0]
|
|
||||||
},
|
},
|
||||||
barWidth: '25%',
|
barWidth: '20%',
|
||||||
barGap: '30%'
|
barGap: '20%'
|
||||||
},
|
},
|
||||||
// 支出底色
|
// 支出底色 - 后绘制,堆叠在数据上方
|
||||||
{
|
{
|
||||||
name: '支出底色',
|
name: '支出底色',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
stack: 'monthly',
|
stack: 'monthly',
|
||||||
data: monthlyStandardBgData,
|
data: monthlyStandardBgData,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: 'rgba(56, 102, 141, 0.3)'
|
color: '#38668D70'
|
||||||
},
|
},
|
||||||
barWidth: '25%',
|
barWidth: '20%',
|
||||||
barGap: '30%',
|
barGap: '20%',
|
||||||
silent: true,
|
silent: true,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: false
|
show: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 收入数据
|
// 收入数据(渐变)
|
||||||
{
|
{
|
||||||
name: '收入',
|
name: '收入',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
@ -202,30 +209,33 @@ const createChartOption = (): EChartsOption => {
|
|||||||
type: 'linear',
|
type: 'linear',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
x2: 1,
|
x2: 0,
|
||||||
y2: 0,
|
y2: 1,
|
||||||
colorStops: [
|
colorStops: [
|
||||||
{ offset: 0, color: '#FFA58D' },
|
{
|
||||||
{ offset: 0.5, color: '#E88F5A' },
|
offset: 0,
|
||||||
{ offset: 1, color: '#D07530' }
|
color: '#FFA58D'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(87, 140, 205, 0)'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
borderRadius: [2, 2, 0, 0]
|
|
||||||
},
|
},
|
||||||
barWidth: '25%',
|
barWidth: '20%',
|
||||||
barGap: '30%'
|
barGap: '80%'
|
||||||
},
|
},
|
||||||
// 收入底色
|
|
||||||
{
|
{
|
||||||
name: '收入底色',
|
name: '收入底色',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
stack: 'perCapita',
|
stack: 'perCapita',
|
||||||
data: perCapitaBgData,
|
data: perCapitaBgData,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: 'rgba(56, 102, 141, 0.3)'
|
color: '#38668D70'
|
||||||
},
|
},
|
||||||
barWidth: '25%',
|
barWidth: '20%',
|
||||||
barGap: '30%',
|
barGap: '80%',
|
||||||
silent: true,
|
silent: true,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: false
|
show: false
|
||||||
@ -237,6 +247,15 @@ const createChartOption = (): EChartsOption => {
|
|||||||
|
|
||||||
// 柱状图配置
|
// 柱状图配置
|
||||||
const barOption = computed(() => createChartOption())
|
const barOption = computed(() => createChartOption())
|
||||||
|
|
||||||
|
// 监听数据变化,更新图表
|
||||||
|
watch(
|
||||||
|
() => [props.data, props.cardData],
|
||||||
|
() => {
|
||||||
|
// 数据变化时,computed 会自动更新
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -246,4 +265,30 @@ const barOption = computed(() => createChartOption())
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chart-cards {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-card-item {
|
||||||
|
text-align: center;
|
||||||
|
padding: 4px;
|
||||||
|
background: rgba(56, 102, 141, 0.3);
|
||||||
|
border-radius: 8px;
|
||||||
|
min-width: 100px;
|
||||||
|
|
||||||
|
.card-value {
|
||||||
|
font-size: 1.6vh;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #00d4ff;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-label {
|
||||||
|
font-size: 1.5vh;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<div class="info-tag">{{ basicInfo.district }}</div>
|
<div class="info-tag">{{ basicInfo.district }}</div>
|
||||||
<div class="info-tag">狱政编号: {{ basicInfo.prisonNumber }}</div>
|
<div class="info-tag">狱政编号: {{ basicInfo.prisonNumber }}</div>
|
||||||
<div class="info-tag">
|
<div class="info-tag">
|
||||||
刑期:{{ basicInfo.sentenceStart }} --- {{ basicInfo.sentenceEnd }} (总天数:{{
|
刑期起/止日:{{ basicInfo.sentenceStart }}---{{ basicInfo.sentenceEnd }} ({{
|
||||||
basicInfo.sentenceDays
|
basicInfo.sentenceDays
|
||||||
}}天)
|
}}天)
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,7 +2,18 @@
|
|||||||
<div class="rewards-punishments-container">
|
<div class="rewards-punishments-container">
|
||||||
<!-- 标题栏 -->
|
<!-- 标题栏 -->
|
||||||
<div class="rewards-header">
|
<div class="rewards-header">
|
||||||
<span class="header-title">风险评估</span>
|
<span class="header-title">近期奖惩</span>
|
||||||
|
<div class="filter-tabs">
|
||||||
|
<div
|
||||||
|
v-for="tab in filterTabs"
|
||||||
|
:key="tab.value"
|
||||||
|
class="filter-tab"
|
||||||
|
:class="{ active: activeFilter === tab.value }"
|
||||||
|
@click="activeFilter = tab.value"
|
||||||
|
>
|
||||||
|
{{ tab.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 时间线列表 -->
|
<!-- 时间线列表 -->
|
||||||
@ -10,7 +21,7 @@
|
|||||||
<div class="timeline-content">
|
<div class="timeline-content">
|
||||||
<div class="timeline-line"></div>
|
<div class="timeline-line"></div>
|
||||||
<div class="timeline-items">
|
<div class="timeline-items">
|
||||||
<div v-for="(item, index) in listData" :key="index" class="timeline-item">
|
<div v-for="(item, index) in filteredList" :key="index" class="timeline-item">
|
||||||
<div class="timeline-dot" :class="item.type"></div>
|
<div class="timeline-dot" :class="item.type"></div>
|
||||||
<div class="timeline-card">
|
<div class="timeline-card">
|
||||||
<div class="card-type" :class="item.type">{{ item.typeText }}</div>
|
<div class="card-type" :class="item.type">{{ item.typeText }}</div>
|
||||||
@ -24,7 +35,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
|
|
||||||
defineOptions({ name: 'RecentRewardsPunishments' })
|
defineOptions({ name: 'RecentRewardsPunishments' })
|
||||||
|
|
||||||
@ -35,9 +46,29 @@ interface RewardPunishmentItem {
|
|||||||
content: string // 内容
|
content: string // 内容
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 过滤标签
|
||||||
|
const filterTabs = [
|
||||||
|
{ label: '全部', value: 'all' },
|
||||||
|
{ label: '奖励记录', value: 'reward' },
|
||||||
|
{ label: '惩罚记录', value: 'punishment' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const activeFilter = ref<string>('all')
|
||||||
|
|
||||||
// 数据列表 - 使用 ref 存储
|
// 数据列表 - 使用 ref 存储
|
||||||
const listData = ref<RewardPunishmentItem[]>([])
|
const listData = ref<RewardPunishmentItem[]>([])
|
||||||
|
|
||||||
|
// 过滤后的列表
|
||||||
|
const filteredList = computed(() => {
|
||||||
|
if (activeFilter.value === 'all') {
|
||||||
|
return listData.value
|
||||||
|
} else if (activeFilter.value === 'reward') {
|
||||||
|
return listData.value.filter((item) => item.type === 'reward')
|
||||||
|
} else {
|
||||||
|
return listData.value.filter((item) => item.type === 'danger')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 可以通过 props 接收外部数据
|
// 可以通过 props 接收外部数据
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@ -87,6 +118,24 @@ watch(
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filter-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tab {
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
background: rgba(56, 102, 141, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: #37599d;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 时间线容器
|
// 时间线容器
|
||||||
.timeline-container {
|
.timeline-container {
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
|
|||||||
@ -351,10 +351,6 @@ watch(
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getLoginFormCache()
|
getLoginFormCache()
|
||||||
getTenantByWebsite()
|
getTenantByWebsite()
|
||||||
// 初始化租户 ID - 确保登录前缓存中有租户 ID
|
|
||||||
if (!authUtil.getTenantId()) {
|
|
||||||
authUtil.setTenantId(Number(loginData.loginForm.tenantName) || 1)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -77,7 +77,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import { EvaluationTemplateApi } from '@/api/prison/evaluation'
|
import { EvaluationTemplateApi } from '@/api/prison/evaluation'
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
|
|
||||||
defineOptions({ name: 'EvaluationTemplateForm' })
|
defineOptions({ name: 'EvaluationTemplateForm' })
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
@click="openForm('create')"
|
@click="openForm('create')"
|
||||||
v-hasPermi="['prison:question:create', 'prison:questionnaire:update']"
|
v-hasPermi="['prison:question:create']"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 新建问题
|
<Icon icon="ep:plus" class="mr-5px" /> 新建问题
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -15,7 +15,7 @@
|
|||||||
type="success"
|
type="success"
|
||||||
plain
|
plain
|
||||||
@click="openPartDialog"
|
@click="openPartDialog"
|
||||||
v-hasPermi="['prison:question:create', 'prison:questionnaire:update']"
|
v-hasPermi="['prison:question:create']"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:folder" class="mr-5px" /> 分区管理
|
<Icon icon="ep:folder" class="mr-5px" /> 分区管理
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
plain
|
plain
|
||||||
:disabled="checkedIds.length === 0"
|
:disabled="checkedIds.length === 0"
|
||||||
@click="handleDeleteBatch"
|
@click="handleDeleteBatch"
|
||||||
v-hasPermi="['prison:question:delete', 'prison:questionnaire:update']"
|
v-hasPermi="['prison:question:delete']"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -135,7 +135,7 @@
|
|||||||
link
|
link
|
||||||
size="small"
|
size="small"
|
||||||
@click="openForm('update', question.id)"
|
@click="openForm('update', question.id)"
|
||||||
v-hasPermi="['prison:question:update', 'prison:questionnaire:update']"
|
v-hasPermi="['prison:question:update']"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:edit" /> 修改
|
<Icon icon="ep:edit" /> 修改
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -144,7 +144,7 @@
|
|||||||
link
|
link
|
||||||
size="small"
|
size="small"
|
||||||
@click="handleDelete(question.id)"
|
@click="handleDelete(question.id)"
|
||||||
v-hasPermi="['prison:question:delete', 'prison:questionnaire:update']"
|
v-hasPermi="['prison:question:delete']"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:delete" /> 删除
|
<Icon icon="ep:delete" /> 删除
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -193,11 +193,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain :icon="Plus" @click="addPartition" v-hasPermi="['prison:question:create', 'prison:questionnaire:update']">添加分区</el-button>
|
<el-button type="primary" plain :icon="Plus" @click="addPartition" v-hasPermi="['prison:question:create']">添加分区</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="savePartitions" type="primary" v-hasPermi="['prison:question:update', 'prison:questionnaire:update']">保存设置</el-button>
|
<el-button @click="savePartitions" type="primary" v-hasPermi="['prison:question:update']">保存设置</el-button>
|
||||||
<el-button @click="partDialogVisible = false">取消</el-button>
|
<el-button @click="partDialogVisible = false">取消</el-button>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user