| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206 |
- <template>
- <div class="risk-hazard">
- <!-- 风险隐患标题 -->
- <div class="section-title">
- <h2>风险隐患</h2>
- </div>
- <!-- 六个部分布局,一行三个,共两行 -->
- <div class="six-panel-layout">
- <!-- 第一行 -->
- <div class="panel-row">
- <!-- 第一个:查获违禁品类别 -->
- <div class="panel-item">
- <div class="panel-header">
- <h3>查获违禁品类别</h3>
- </div>
- <!-- 描述卡片 -->
- <div class="describe-card">
- <div class="describe-content">
- {{ categoryStatsDescription }}
- </div>
- </div>
- <!-- 饼图 -->
- <div class="chart-container">
- <div ref="captureRankChartRef" class="echarts-chart"></div>
- </div>
- </div>
- <!-- 第二个:问题发现统计 -->
- <div class="panel-item">
- <div class="panel-header">
- <h3>问题发现统计</h3>
- </div>
- <!-- 描述卡片 -->
- <div class="describe-card">
- <div class="describe-content">
- {{ seizureTimeTrendDescription }}
- </div>
- </div>
- <!-- 折线图 -->
- <div class="chart-container">
- <div ref="problemDiscoveryChartRef" class="echarts-chart"></div>
- </div>
- </div>
- <!-- 第三个:隐匿物品查获部位 -->
- <div class="panel-item">
- <div class="panel-header">
- <h3>隐匿物品查获部位</h3>
- </div>
- <!-- 描述卡片 -->
- <div class="describe-card">
- <div class="describe-content">
- {{ concealmentPositionStatsDescription }}
- </div>
- </div>
- <!-- 饼图 -->
- <div class="chart-container">
- <div ref="problemRectificationChartRef" class="echarts-chart"></div>
- </div>
- </div>
- </div>
- <!-- 第二行 -->
- <div class="panel-row">
- <!-- 第四个:查获排名表格 -->
- <div class="panel-item" v-show="!isUserType">
- <div class="panel-header">
- <h3>查获排名</h3>
- </div>
- <!-- 描述卡片 -->
- <div class="describe-card">
- <div class="describe-content">
- {{ departmentRankingDescription }}
- </div>
- </div>
- <!-- 表格 -->
- <div class="table-container">
- <el-table :data="captureRankData" style="width: 100%" size="small">
- <el-table-column prop="rank" label="排名" align="center" />
- <el-table-column prop="department" label="大队" />
- <el-table-column prop="percentage" label="占比" align="center">
- <template #default="{ row }">
- <span>{{ row.percentage }}%</span>
- </template>
- </el-table-column>
- <el-table-column prop="count" label="数量" align="center" />
- </el-table>
- </div>
- </div>
- <!-- 第五个:查获岗位分布 -->
- <div class="panel-item" v-show="!isUserType">
- <div class="panel-header">
- <h3>查获岗位分布</h3>
- </div>
- <!-- 描述卡片 -->
- <div class="describe-card">
- <div class="describe-content">
- {{ postCategoryStatsDescription }}
- </div>
- </div>
- <!-- 饼图 -->
- <div class="chart-container">
- <div ref="rectificationStatsChartRef" class="echarts-chart"></div>
- </div>
- </div>
- <!-- 第六个:查获通道TOP5 -->
- <div class="panel-item" v-show="!isUserType">
- <div class="panel-header">
- <h3>查获通道TOP5</h3>
- </div>
- <!-- 描述卡片 -->
- <div class="describe-card">
- <div class="describe-content">
- {{ channelRankingStatsDescription }}
- </div>
- </div>
- <!-- 表格 -->
- <div class="table-container">
- <el-table :data="captureChannelData" style="width: 100%" size="small">
- <el-table-column prop="channel" label="查获通道" />
- <el-table-column prop="count" label="查获数量" align="center" />
- <el-table-column prop="area" label="所在区域" />
- </el-table>
- </div>
- </div>
- <!-- 第七个:移交公安数据 -->
- <div class="panel-item">
- <div class="panel-header">
- <h3>移交公安数据</h3>
- </div>
- <!-- 描述卡片 -->
- <div class="describe-card">
- <div class="describe-content">
- {{ policeTransferStats || '移交公安的违禁品数量共X件,主要集中于XX。' }}
- </div>
- </div>
- <!-- 表格 -->
- <div class="table-container">
- <el-table :data="policeTransferData" size="small" height="250" :scroll="{ x: 'max-content' }">
- <el-table-column prop="departmentName" label="主管" min-width="120" />
- <el-table-column prop="teamName" label="班组" min-width="120" />
- <el-table-column prop="userName" label="姓名" min-width="100" />
- <el-table-column prop="itemName" label="查获物品" min-width="150" />
- <el-table-column prop="quantity" label="查获数量" align="center" width="100" />
- <el-table-column prop="positionName" label="查获部位" min-width="120" />
- <el-table-column prop="seizureTime" label="查获时间" min-width="150" />
- <el-table-column prop="channelName" label="查获通道" min-width="120" />
- </el-table>
- </div>
- </div>
- <!-- 第八个:X光机漏检数据 -->
- <div class="panel-item" v-show="!isUserType">
- <div class="panel-header">
- <h3>X光机漏检数据</h3>
- </div>
- <!-- 描述卡片 -->
- <div class="describe-card">
- <div class="describe-content">
- {{ xrayMissStats || 'X光机漏检事件主要集中于以下开机员:张三、李四、王五,可针对性开展判图技能强化培训。' }}
- </div>
- </div>
- <!-- 表格 -->
- <div class="table-container">
- <el-table :data="xrayMissData" size="small" height="250" :scroll="{ x: 'max-content' }">
- <el-table-column prop="departmentName" label="主管" min-width="120" />
- <el-table-column prop="teamName" label="班组" min-width="120" />
- <el-table-column prop="userName" label="姓名" min-width="100" />
- <el-table-column prop="missItemName" label="漏检物品" min-width="150" />
- <el-table-column prop="missQuantity" label="漏检数量" align="center" width="100" />
- <el-table-column prop="missPosition" label="漏检部位" min-width="120" />
- <el-table-column prop="channelName" label="漏检通道" min-width="120" />
- </el-table>
- </div>
- </div>
- <!-- 第九个:可能异常查获数据 -->
- <div class="panel-item" v-show="!isTeamsType && !isUserType && !isDepartmentType">
- <div class="panel-header">
- <h3>可能异常查获数据</h3>
- </div>
- <!-- 描述卡片 -->
- <div class="describe-card" v-if="abnormalCaptureStats">
- <div class="describe-content">
- {{ abnormalCaptureStats || '旅检一科、旅检二科、旅检三科查获违禁品数量显著高于整体水平,旅检四科、旅检五科查获违禁品数量显著低于整体水平。' }}
- </div>
- </div>
- <!-- 表格 -->
- <div class="table-container">
- <el-table :data="abnormalCaptureData" size="small" height="250" :scroll="{ x: 'max-content' }">
- <el-table-column prop="departmentName" label="主管" min-width="120" />
- <el-table-column prop="teamName" label="班组" min-width="120" />
- <el-table-column prop="userName" label="姓名" min-width="100" />
- <el-table-column prop="seizureQuantity" label="查获数量" align="center" width="100" />
- </el-table>
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, onUnmounted, watch } from 'vue'
- import { useEcharts } from '@/hooks/chart.js'
- import {
- getCategoryStats,
- getSeizureTimeTrend,
- getConcealmentPositionStats,
- getDepartmentRanking,
- getPostCategoryStats,
- getChannelRankingStats,
- getPoliceData,
- getPoliceDataStats,
- getXrayMissCheck,
- getXrayMissCheckStats,
- getAbnormalSeizureData,
- getAbnormalSeizureStats
- } from '@/api/assistant/assistant.js'
- // 定义props接收queryForm参数
- const props = defineProps({
- queryForm: {
- type: Object,
- default: () => ({
- dateRangeQueryType: '',
- year: '',
- quarter: '',
- month: ''
- })
- },
- selectedDeptObject: {
- type: Object,
- default: null
- }
- })
- // 图表容器引用
- const captureRankChartRef = ref(null)
- const problemDiscoveryChartRef = ref(null)
- const problemRectificationChartRef = ref(null)
- const rectificationStatsChartRef = ref(null)
- // 图表实例
- const { setOption: setCaptureRankOption, dispose: disposeCaptureRank } = useEcharts(captureRankChartRef)
- const { setOption: setProblemDiscoveryOption, dispose: disposeProblemDiscovery } = useEcharts(problemDiscoveryChartRef)
- const { setOption: setProblemRectificationOption, dispose: disposeProblemRectification } = useEcharts(problemRectificationChartRef)
- const { setOption: setRectificationStatsOption, dispose: disposeRectificationStats } = useEcharts(rectificationStatsChartRef)
- // 六个API接口的响应式数据
- const categoryStatsData = ref({})
- const seizureTimeTrendData = ref({})
- const concealmentPositionStatsData = ref({})
- const departmentRankingData = ref({})
- const postCategoryStatsData = ref({})
- const channelRankingStatsData = ref({})
- // 计算属性:处理selectedDeptObject逻辑
- const selectedDeptType = computed(() => {
- return props.selectedDeptObject && props.selectedDeptObject?.deptType
- })
- // 计算属性:检查是否为STATION类型
- const isStationType = computed(() => {
- return selectedDeptType.value === 'STATION' || !selectedDeptType.value
- })
- // 计算属性:检查是否为STATION类型
- const isDepartmentType = computed(() => {
- return selectedDeptType.value === 'MANAGER'
- })
- const isBrigadeType = computed(() => {
- return selectedDeptType.value === 'BRIGADE'
- })
- // 计算属性:检查是否为TEAMS类型
- const isTeamsType = computed(() => {
- return selectedDeptType.value === 'TEAMS'
- })
- //
- const isUserType = computed(() => {
- return selectedDeptType.value === 'USER'
- })
- // 新增三个API接口的响应式数据
- const policeTransferData = ref([])
- const xrayMissData = ref([])
- const abnormalCaptureData = ref([])
- // 新增三个统计API接口的响应式数据
- const policeTransferStats = ref('')
- const xrayMissStats = ref('')
- const abnormalCaptureStats = ref('')
- // 计算属性:动态生成查获违禁品类别描述
- const categoryStatsDescription = computed(() => {
- if (!categoryStatsData.value || !categoryStatsData.value || !Array.isArray(categoryStatsData.value)) {
- return '查获物品以[占比最高物品类型]为主,占比达[X]%,其次为[第二高占比物品类型]([X]%)、[第三高占比物品类型]([X]%),需重点强化对应物品的安检识别与管控力度。'
- }
- const categoryData = categoryStatsData.value
- // 1. 按数量排序,获取前三名
- const sortedData = [...categoryData].sort((a, b) => (b.quantity || 0) - (a.quantity || 0))
- // 2. 计算总数量
- const totalCount = sortedData.reduce((sum, item) => sum + (item.quantity || 0), 0)
- // 3. 获取前三名数据
- const top1 = sortedData[0] || { categoryNameOne: '未知类型', quantity: 0 }
- const top2 = sortedData[1] || { categoryNameOne: '未知类型', quantity: 0 }
- const top3 = sortedData[2] || { categoryNameOne: '未知类型', quantity: 0 }
- // 4. 计算百分比
- const top1Percentage = totalCount > 0 ? ((top1.quantity / totalCount) * 100).toFixed(2) : '0.00'
- const top2Percentage = totalCount > 0 ? ((top2.quantity / totalCount) * 100).toFixed(2) : '0.00'
- const top3Percentage = totalCount > 0 ? ((top3.quantity / totalCount) * 100).toFixed(2) : '0.00'
- // 5. 生成描述文字
- return `查获物品以${top1.categoryNameOne || '未知类型'}为主,占比达${top1Percentage}%,其次为${top2.categoryNameOne || '未知类型'}(${top2Percentage}%)、${top3.categoryNameOne || '未知类型'}(${top3Percentage}%),需重点强化对应物品的安检识别与管控力度。`
- })
- // 计算属性:动态生成查获时间趋势描述
- const seizureTimeTrendDescription = computed(() => {
- if (!seizureTimeTrendData.value || !seizureTimeTrendData.value || !Array.isArray(seizureTimeTrendData.value)) {
- return '高峰时段:XX:XX、XX:XX左右(平均查获量达XX件),需强化对应时段的安检力度。低峰时段:XX:XX、XX:XX后(平均查获量XX件)'
- }
- const trendData = seizureTimeTrendData.value
- // 1. 按查获量排序,找出最高和最低的两个时段
- const sortedData = [...trendData].sort((a, b) => (b.total || 0) - (a.total || 0))
- // 2. 获取最高的两个时段
- const peakHours = sortedData.slice(0, 2).map(item => item.hourOfDay || '').filter(Boolean)
- // 3. 获取最低的两个时段
- const lowHours = sortedData.slice(-2).map(item => item.hourOfDay || '').filter(Boolean)
- // 4. 计算高峰时段平均查获量
- const peakData = trendData.filter(item => peakHours.some(hour => item.hourOfDay?.includes(hour)))
- const peakAvg = peakData.length > 0
- ? Math.round(peakData.reduce((sum, item) => sum + (item.total || 0), 0) / peakData.length)
- : 0
- // 5. 计算低峰时段平均查获量
- const lowData = trendData.filter(item => lowHours.some(hour => item.hourOfDay?.includes(hour)))
- const lowAvg = lowData.length > 0
- ? Math.round(lowData.reduce((sum, item) => sum + (item.total || 0), 0) / lowData.length)
- : 0
- // 6. 生成描述文字
- return `高峰时段:${peakHours.join('、')}左右(平均查获量达${peakAvg}件),需强化对应时段的安检力度。低峰时段:${lowHours.join('、')}后(平均查获量${lowAvg}件)`
- })
- // 计算属性:动态生成隐匿物品查获部位描述
- const concealmentPositionStatsDescription = computed(() => {
- if (!concealmentPositionStatsData.value || !Array.isArray(concealmentPositionStatsData.value)) {
- return '违禁品主要藏匿于"XX",是安检搜检的重点部位。'
- }
- const positionData = concealmentPositionStatsData.value
- // 1. 按查获量排序,找出查获量最高的部位
- const sortedData = [...positionData].sort((a, b) => (b.count || 0) - (a.count || 0))
- // 2. 获取查获量最高的部位
- const topPosition = sortedData[0] || { positionName: 'XX', count: 0 }
- // 3. 生成描述文字
- return `违禁品主要藏匿于"${topPosition.positionName || 'XX'}",是安检搜检的重点部位。`
- })
- // 计算属性:动态生成大队查获排名描述
- const departmentRankingDescription = computed(() => {
- if (!departmentRankingData.value || !Array.isArray(departmentRankingData.value)) {
- return 'XXXX是违禁品查获的主力大队,查获违禁物品数量为XX,占比XX%。'
- }
- const rankingData = departmentRankingData.value
- // 1. 按查获量排序,找出查获量最高的大队
- const sortedData = [...rankingData].sort((a, b) => (b.seizureCount || 0) - (a.seizureCount || 0))
- // 2. 获取查获量最高的大队
- const topDepartment = sortedData[0] || { departmentName: 'XXXX', seizureCount: 234 }
- const str = isStationType.value ? '大队' : isBrigadeType.value ? '主管' : isDepartmentType.value ? '班组' : isTeamsType.value ? '成员' : ''
- // 5. 生成描述文字
- return `${topDepartment.brigadeName || 'XXXX'}是违禁品查获的主力${str},查获违禁物品数量为${topDepartment.seizureCount || 'XX'},占比${topDepartment.currentRatio}%。`
- })
- // 计算属性:动态生成查获岗位分布描述
- const postCategoryStatsDescription = computed(() => {
- if (!postCategoryStatsData.value || !Array.isArray(postCategoryStatsData.value)) {
- return '"XX"的查获数量最多,为XX件,占比XX%。'
- }
- const postData = postCategoryStatsData.value
- // 1. 按查获量排序,找出查获量最高的岗位
- const sortedData = [...postData].sort((a, b) => (b.quantity || 0) - (a.quantity || 0))
- // 2. 获取查获量最高的岗位
- const topPost = sortedData[0] || { postName: 'XX', quantity: 124 }
- // 3. 计算总查获量
- const totalCount = postData.reduce((sum, item) => sum + (item.quantity || 0), 0)
- // 4. 计算占比
- const percentage = totalCount > 0 ? ((topPost.quantity / totalCount) * 100).toFixed(0) : '45'
- // 5. 生成描述文字
- return `"${topPost.postName || 'XX'}"的查获数量最多,为${topPost.quantity || 124}件,占比${percentage}%。`
- })
- // 计算属性:动态生成查获通道TOP5描述
- const channelRankingStatsDescription = computed(() => {
- if (!channelRankingStatsData.value || !Array.isArray(channelRankingStatsData.value)) {
- return '违禁物品查获主要集中于XXXX,查获数量为XX,占比XX%。'
- }
- const channelData = channelRankingStatsData.value
- // 1. 按查获量排序,找出查获量最高的通道
- const sortedData = [...channelData].sort((a, b) => (b.seizureQuantity || 0) - (a.seizureQuantity || 0))
- // 2. 获取查获量最高的通道
- const topChannel = sortedData[0] || { channelName: 'XXXX', seizureQuantity: 123 }
- // 3. 计算总查获量
- const totalCount = channelData.reduce((sum, item) => sum + (item.seizureQuantity || 0), 0)
- // 4. 计算占比
- const percentage = totalCount > 0 ? ((topChannel.seizureQuantity / totalCount) * 100).toFixed(0) : '23'
- // 5. 生成描述文字
- return `违禁物品查获主要集中于${topChannel.channelName || 'XXXX'},查获数量为${topChannel.seizureQuantity || 'XX'},占比${percentage}%。`
- })
- // 表格数据
- const captureRankData = ref([])
- const captureChannelData = ref([])
- // 处理query参数,当dateRangeQueryType为YEAR时添加yearOnYear: true
- const processQueryParams = (queryParams) => {
- const processedParams = { ...queryParams }
- if (processedParams.dateRangeQueryType === 'YEAR') {
- processedParams.yearOnYear = true
- } else {
- processedParams.chainRatio = true
- processedParams.yearOnYear = true
- }
- const selectedDept = props.selectedDeptObject
- const { deptType = "", id } = selectedDept ? selectedDept : { deptType: "", id: "" }
- let otherParams = {
- ...(deptType == 'BRIGADE' ? { brigadeId: id } : {}),
- ...(deptType == 'MANAGER' ? { departmentId: id } : {}),
- ...(deptType == 'TEAMS' ? { teamId: id } : {}),
- ...(deptType == 'USER' ? { userId: id } : {})
- }
- delete processedParams.deptId;
- return { ...processedParams, ...otherParams }
- }
- const handleAbnormalCaptureStats = (data) => {
- const { higList, lowList } = data;
- let first = !!higList && higList.map((item) => item.departmentName).length > 0 ? `${higList.map((item) => item.departmentName).join("、")}查获违禁品数量显著高于整体水平` : '';
- let second = !!lowList && lowList.map((item) => item.departmentName).length > 0 ? `${lowList.map((item) => item.departmentName).join("、")}查获违禁品数量显著低于整体水平` : '';
- return `${first}${!!second ? ',' : first && second ? '。' : ''}${second}`
- }
- const handlePoliceTransferStats = (data) => {
- const { totalQuantity, brigadeRankList } = data;
- const topDepartment = brigadeRankList.map(item => item.brigadeName).join('、')
- return `移交公安的违禁品数量共${totalQuantity}件,主要集中于${topDepartment}。`
- }
- // 调用API获取风险隐患数据
- const fetchRiskHazardData = async (queryParams) => {
- try {
- // 处理query参数
- const processedParams = processQueryParams(queryParams)
- // 按顺序调用六个API接口
- // 1. 查获违禁品类别统计
- const categoryStatsResponse = await getCategoryStats(processedParams)
- console.log('查获违禁品类别统计:', categoryStatsResponse)
- categoryStatsData.value = categoryStatsResponse.data?.categoryStats || []
- // 2. 查获时间趋势
- const seizureTimeTrendResponse = await getSeizureTimeTrend(processedParams)
- console.log('查获时间趋势:', seizureTimeTrendResponse)
- seizureTimeTrendData.value = seizureTimeTrendResponse?.data || []
- // 3. 隐匿物品查获部位统计
- const concealmentPositionStatsResponse = await getConcealmentPositionStats(processedParams)
- console.log('隐匿物品查获部位统计:', concealmentPositionStatsResponse)
- concealmentPositionStatsData.value = concealmentPositionStatsResponse?.data?.positionStats || []
- // 4. 大队查获排名(表格数据)
- const departmentRankingResponse = await getDepartmentRanking(processedParams)
- console.log('大队查获排名:', departmentRankingResponse)
- departmentRankingData.value = departmentRankingResponse.data || []
- // 5. 查获岗位分布统计
- const postCategoryStatsResponse = await getPostCategoryStats(processedParams)
- console.log('查获岗位分布统计:', postCategoryStatsResponse)
- postCategoryStatsData.value = postCategoryStatsResponse?.data?.postStats || []
- // 6. 查获通道TOP5(表格数据)
- const channelRankingStatsResponse = await getChannelRankingStats(processedParams)
- console.log('查获通道TOP5:', channelRankingStatsResponse)
- channelRankingStatsData.value = channelRankingStatsResponse?.data?.channelRankings || []
- // 7. 移交公安数据
- const policeDataResponse = await getPoliceData(processedParams)
- console.log('移交公安数据:', policeDataResponse)
- policeTransferData.value = policeDataResponse?.data || []
- // 8. X光机漏检数据
- const xrayMissCheckResponse = await getXrayMissCheck(processedParams)
- console.log('X光机漏检数据:', xrayMissCheckResponse)
- xrayMissData.value = xrayMissCheckResponse?.data || []
- // 9. 可能异常查获数据(只有当部门类型是STATION时才请求)
-
- if (isStationType.value || isBrigadeType.value || isDepartmentType.value) {
- const abnormalSeizureResponse = await getAbnormalSeizureData(processedParams)
- console.log('可能异常查获数据:', abnormalSeizureResponse)
- abnormalCaptureData.value = abnormalSeizureResponse?.data || []
- // 12. 可能异常查获统计数据(描述卡片文本)
- const abnormalSeizureStatsResponse = await getAbnormalSeizureStats(processedParams)
- console.log('可能异常查获统计数据:', abnormalSeizureStatsResponse, isStationType.value)
- // debugger
- abnormalCaptureStats.value = handleAbnormalCaptureStats(abnormalSeizureStatsResponse?.data)
- } else {
- abnormalCaptureData.value = []
- abnormalCaptureStats.value = ''
- }
- // 10. 移交公安统计数据(描述卡片文本)
- const policeDataStatsResponse = await getPoliceDataStats(processedParams)
- console.log('移交公安统计数据:', policeDataStatsResponse)
- policeTransferStats.value = handlePoliceTransferStats(policeDataStatsResponse?.data)
- // 11. X光机漏检统计数据(描述卡片文本)
- const xrayMissCheckStatsResponse = await getXrayMissCheckStats(processedParams)
- console.log('X光机漏检统计数据:', xrayMissCheckStatsResponse)
- let userName = xrayMissCheckStatsResponse?.data?.map(item => item.xrayOperatorName).join('、') || ''
- xrayMissStats.value = `X光机漏检事件主要集中于以下开机员:${userName},可针对性开展判图技能强化培训。`
- // 更新图表和表格数据
- updateChartsWithData()
- } catch (error) {
- console.error('获取风险隐患数据失败:', error)
- }
- }
- // 查获违禁品类别饼图配置
- const captureRankOptions = {
- tooltip: {
- trigger: 'item',
- formatter: '{a} <br/>{b}: {c}件 ({d}%)'
- },
- legend: {
- orient: 'vertical',
- left: 'left',
- top: 'center',
- textStyle: {
- color: '#333',
- fontSize: 12
- }
- },
- color: ['#FF9F43', '#54C6EB', '#A3D9B1', '#FF6B9D', '#9B59B6'],
- series: [
- {
- name: '违禁品类别',
- type: 'pie',
- radius: '60%',
- center: ['60%', '50%'],
- avoidLabelOverlap: false,
- itemStyle: {
- borderRadius: 10,
- borderColor: '#fff',
- borderWidth: 2
- },
- label: {
- show: true,
- formatter: '{b}: {c}件',
- fontSize: 12
- },
- emphasis: {
- label: {
- show: true,
- fontSize: 14,
- fontWeight: 'bold'
- },
- itemStyle: {
- shadowBlur: 10,
- shadowOffsetX: 0,
- shadowColor: 'rgba(0, 0, 0, 0.5)'
- }
- },
- labelLine: {
- show: true,
- length: 10,
- length2: 20
- },
- data: []
- }
- ]
- }
- // 问题发现折线图配置
- const problemDiscoveryOptions = {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow'
- },
- formatter: function (params) {
- return `${params[0].name}<br/>问题数量: <span style="color:#FF6B6B;font-weight:bold">${params[0].data}</span>`
- }
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true
- },
- xAxis: {
- type: 'category',
- data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月'],
- axisLine: {
- lineStyle: {
- color: '#999'
- }
- },
- axisLabel: {
- fontSize: 12
- }
- },
- yAxis: {
- type: 'value',
- name: '问题数量',
- axisLine: {
- lineStyle: {
- color: '#999'
- }
- },
- splitLine: {
- lineStyle: {
- color: '#f0f0f0'
- }
- }
- },
- series: [
- {
- name: '问题发现',
- type: 'line',
- smooth: true,
- symbol: 'circle',
- symbolSize: 6,
- itemStyle: {
- color: '#FF6B6B'
- },
- lineStyle: {
- color: '#FF6B6B',
- width: 3
- },
- label: {
- show: true,
- position: 'top',
- formatter: '{c}'
- },
- data: []
- }
- ]
- }
- // 隐匿物品查获部位饼图配置
- const problemRectificationOptions = {
- tooltip: {
- trigger: 'item',
- formatter: '{a} <br/>{b}: {c}件 ({d}%)'
- },
- legend: {
- orient: 'vertical',
- left: 'left',
- top: 'center',
- textStyle: {
- color: '#333',
- fontSize: 12
- }
- },
- color: ['#FF9F43', '#54C6EB', '#A3D9B1', '#FF6B9D', '#9B59B6'],
- series: [
- {
- name: '查获部位',
- type: 'pie',
- radius: '70%',
- center: ['50%', '50%'],
- avoidLabelOverlap: false,
- itemStyle: {
- borderRadius: 10,
- borderColor: '#fff',
- borderWidth: 2
- },
- label: {
- show: true,
- formatter: '{b}: {c}件',
- fontSize: 12
- },
- emphasis: {
- label: {
- show: true,
- fontSize: 14,
- fontWeight: 'bold'
- },
- itemStyle: {
- shadowBlur: 10,
- shadowOffsetX: 0,
- shadowColor: 'rgba(0, 0, 0, 0.5)'
- }
- },
- labelLine: {
- show: true,
- length: 10,
- length2: 20
- },
- data: []
- }
- ]
- }
- // 查获岗位分布饼图配置
- const rectificationStatsOptions = {
- tooltip: {
- trigger: 'item',
- formatter: '{a} <br/>{b}: {c}件 ({d}%)'
- },
- legend: {
- orient: 'vertical',
- left: 'left',
- top: 'center',
- textStyle: {
- color: '#333',
- fontSize: 12
- }
- },
- color: ['#FF9F43', '#54C6EB', '#A3D9B1', '#FF6B9D', '#9B59B6'],
- series: [
- {
- name: '岗位分布',
- type: 'pie',
- radius: '70%',
- center: ['60%', '50%'],
- avoidLabelOverlap: false,
- itemStyle: {
- borderRadius: 10,
- borderColor: '#fff',
- borderWidth: 2
- },
- label: {
- show: true,
- formatter: '{b}: {c}件',
- fontSize: 12
- },
- emphasis: {
- label: {
- show: true,
- fontSize: 14,
- fontWeight: 'bold'
- },
- itemStyle: {
- shadowBlur: 10,
- shadowOffsetX: 0,
- shadowColor: 'rgba(0, 0, 0, 0.5)'
- }
- },
- labelLine: {
- show: true,
- length: 10,
- length2: 20
- },
- data: []
- }
- ]
- }
- // 初始化图表
- onMounted(() => {
- // 确保DOM完全渲染后再初始化图表
- const initCharts = () => {
- // 查获违禁品类别饼图
- if (captureRankChartRef.value && captureRankChartRef.value.offsetHeight > 0) {
- setCaptureRankOption(captureRankOptions)
- }
- // 问题发现折线图
- if (problemDiscoveryChartRef.value && problemDiscoveryChartRef.value.offsetHeight > 0) {
- setProblemDiscoveryOption(problemDiscoveryOptions)
- }
- // 隐匿物品查获部位饼图
- if (problemRectificationChartRef.value && problemRectificationChartRef.value.offsetHeight > 0) {
- setProblemRectificationOption(problemRectificationOptions)
- }
- // 查获岗位分布饼图
- if (rectificationStatsChartRef.value && rectificationStatsChartRef.value.offsetHeight > 0) {
- setRectificationStatsOption(rectificationStatsOptions)
- }
- }
- // 立即尝试初始化
- initCharts()
- // 如果容器高度为0,延迟重试
- setTimeout(() => {
- initCharts()
- }, 200)
- })
- // 监听queryForm参数变化,调用API获取数据
- watch(() => props.queryForm, (newQueryForm) => {
- // 只有当所有必要的查询参数都存在时才调用API
- if (newQueryForm.dateRangeQueryType && newQueryForm.year) {
- fetchRiskHazardData(newQueryForm)
- }
- }, { deep: true })
- // 根据API数据更新图表和表格数据
- const updateChartsWithData = () => {
- // 1. 更新查获违禁品类别饼图
- updateCategoryStatsChart()
- // 2. 更新查获时间趋势折线图
- updateSeizureTimeTrendChart()
- // 3. 更新隐匿物品查获部位饼图
- updateConcealmentPositionStatsChart()
- // 4. 更新大队查获排名表格
- updateDepartmentRankingTable()
- // 5. 更新查获岗位分布饼图
- updatePostCategoryStatsChart()
- // 6. 更新查获通道TOP5表格
- updateChannelRankingStatsTable()
- console.log('风险隐患数据已更新,图表和表格已刷新')
- }
- // 更新查获违禁品类别饼图数据
- const updateCategoryStatsChart = () => {
- if (categoryStatsData.value && categoryStatsData.value) {
- const pieData = categoryStatsData.value
- // 转换数据格式
- const formattedData = pieData.map(item => ({
- name: item.categoryNameOne || '未知类别',
- value: item.quantity || 0
- }))
- // 更新饼图数据
- captureRankOptions.series[0].data = formattedData
- // 重新设置图表选项
- setCaptureRankOption(captureRankOptions)
- console.log('查获违禁品类别饼图已更新:', formattedData)
- } else {
- // 无数据时清空图表
- captureRankOptions.series[0].data = []
- setCaptureRankOption(captureRankOptions)
- }
- }
- // 更新查获时间趋势折线图数据
- const updateSeizureTimeTrendChart = () => {
- if (seizureTimeTrendData.value && seizureTimeTrendData.value) {
- const trendData = seizureTimeTrendData.value
- // 提取横坐标和时间数据
- const xAxisData = trendData.map(item => item.hourOfDay || '未知时间')
- const seriesData = trendData.map(item => item.total || 0)
- // 更新图表配置
- problemDiscoveryOptions.xAxis.data = xAxisData
- problemDiscoveryOptions.series[0].data = seriesData
- // 重新设置图表选项
- setProblemDiscoveryOption(problemDiscoveryOptions)
- console.log('查获时间趋势折线图已更新:', { xAxis: xAxisData, series: seriesData })
- } else {
- // 无数据时清空图表
- problemDiscoveryOptions.xAxis.data = []
- problemDiscoveryOptions.series[0].data = []
- setProblemDiscoveryOption(problemDiscoveryOptions)
- }
- }
- // 更新隐匿物品查获部位饼图数据
- const updateConcealmentPositionStatsChart = () => {
- if (concealmentPositionStatsData.value && concealmentPositionStatsData.value) {
- const pieData = concealmentPositionStatsData.value
- // 转换数据格式
- const formattedData = pieData.map(item => ({
- name: item.positionName || '未知部位',
- value: item.quantity || 0
- }))
- // 更新饼图数据
- problemRectificationOptions.series[0].data = formattedData
- // 重新设置图表选项
- setProblemRectificationOption(problemRectificationOptions)
- console.log('隐匿物品查获部位饼图已更新:', formattedData)
- } else {
- // 无数据时清空图表
- problemRectificationOptions.series[0].data = []
- setProblemRectificationOption(problemRectificationOptions)
- }
- }
- // 更新大队查获排名表格数据
- const updateDepartmentRankingTable = () => {
- if (departmentRankingData.value && departmentRankingData.value) {
- const tableData = departmentRankingData.value
- // 转换数据格式
- const formattedData = tableData.map((item, index) => ({
- rank: index + 1,
- department: item.brigadeName || '未知大队',
- percentage: item.currentRatio || 0,
- count: item.seizureCount || 0
- }))
- // 更新表格数据
- captureRankData.value = formattedData
- console.log('大队查获排名表格已更新:', formattedData)
- } else {
- // 无数据时清空表格
- captureRankData.value = []
- }
- }
- // 更新查获岗位分布饼图数据
- const updatePostCategoryStatsChart = () => {
- if (postCategoryStatsData.value && postCategoryStatsData.value) {
- const pieData = postCategoryStatsData.value
- // 转换数据格式
- const formattedData = pieData.map(item => ({
- name: item.postName || '未知岗位',
- value: item.quantity || 0
- }))
- // 更新饼图数据
- rectificationStatsOptions.series[0].data = formattedData
- // 重新设置图表选项
- setRectificationStatsOption(rectificationStatsOptions)
- console.log('查获岗位分布饼图已更新:', formattedData)
- } else {
- // 无数据时清空图表
- rectificationStatsOptions.series[0].data = []
- setRectificationStatsOption(rectificationStatsOptions)
- }
- }
- // 更新查获通道TOP5表格数据
- const updateChannelRankingStatsTable = () => {
- if (channelRankingStatsData.value && channelRankingStatsData.value) {
- const tableData = channelRankingStatsData.value
- // 转换数据格式
- const formattedData = tableData.map(item => ({
- channel: item.channelName || '未知通道',
- count: item.seizureQuantity || 0,
- area: item.regionalName || '未知区域'
- }))
- // 更新表格数据
- captureChannelData.value = formattedData
- console.log('查获通道TOP5表格已更新:', formattedData)
- } else {
- // 无数据时清空表格
- captureChannelData.value = []
- }
- }
- // 组件卸载时销毁图表
- onUnmounted(() => {
- disposeCaptureRank()
- disposeProblemDiscovery()
- disposeProblemRectification()
- disposeRectificationStats()
- })
- </script>
- <style scoped>
- .risk-hazard {
- width: 100%;
- }
- /* 风险隐患标题 */
- .section-title {
- margin: 14px 0 14px 0;
- text-align: left;
- }
- .section-title h2 {
- margin: 0;
- font-size: 18px;
- font-weight: 600;
- color: #333;
- }
- /* 六个部分布局 */
- .six-panel-layout {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 16px;
- grid-auto-flow: dense;
- /* 自动填充空白区域 */
- }
- .panel-row {
- display: contents;
- /* 让子元素直接参与网格布局 */
- }
- .panel-item {
- background: white;
- border-radius: 8px;
- padding: 20px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- display: flex;
- flex-direction: column;
- gap: 16px;
- min-width: 0;
- /* 防止内容溢出 */
- overflow: hidden;
- /* 隐藏溢出内容 */
- }
- .panel-header {
- padding-bottom: 0;
- }
- .panel-header h3 {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- margin: 0;
- }
- /* 描述卡片样式 */
- .describe-card {
- background: #F8F8F8;
- border: 1px dashed #CDCCCC;
- border-radius: 6px;
- padding: 7px 10px;
- text-align: left;
- }
- .describe-content {
- font-size: 13px;
- color: #666;
- line-height: 1.5;
- }
- .stat-number {
- font-weight: 600;
- color: #557DDB;
- font-size: 16px;
- }
- .stat-trend {
- font-weight: 500;
- }
- .stat-trend.up {
- color: #67C23A;
- }
- .stat-trend.down {
- color: #F56C6C;
- }
- /* 图表容器 */
- .chart-container {
- /* background: #F8F8F8;
- border-radius: 6px;
- padding: 16px; */
- min-height: 200px;
- height: 250px;
- position: relative;
- box-sizing: border-box;
- overflow: hidden;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- /* 防止被压缩 */
- }
- /* 表格容器 */
- .table-container {
- /* background: #F8F8F8; */
- flex: 1;
- border: 1px solid #E4E7ED;
- border-radius: 4px;
- max-height: 300px;
- overflow: auto;
- position: relative;
- }
- /* Element Plus表格横向滚动配置 */
- .table-container :deep(.el-table) {
- width: 100%;
- min-width: 100%;
- }
- .table-container :deep(.el-table .el-table__header-wrapper),
- .table-container :deep(.el-table .el-table__body-wrapper) {
- overflow-x: auto !important;
- }
- .table-container :deep(.el-table__header) {
- width: auto !important;
- }
- .table-container :deep(.el-table__body) {
- width: auto !important;
- }
- /* 表格偶数行背景色 */
- .table-container :deep(.el-table__body tr:nth-child(even)) {
- background-color: #F8F8F8;
- }
- /* 表格样式 - 列头和列内容 */
- .table-container :deep(.el-table__header th),
- .table-container :deep(.el-table__body td) {
- font-weight: 500;
- font-size: 13px;
- color: #333333;
- }
- .percentage {
- color: #557DDB;
- font-weight: 600;
- }
- /* ECharts图表样式 */
- .echarts-chart {
- width: 100% !important;
- height: 100% !important;
- min-height: 200px;
- display: block;
- }
- /* 响应式设计 */
- @media (max-width: 768px) {
- .panel-row {
- flex-direction: column;
- }
- }
- </style>
|