数字督察一体化平台-前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

954 lines
29 KiB

<template>
<div class="container">
<header>
<el-form>
<query-select
label="人员标签"
:data="tags"
v-model="query.tags"
@change="getList"
/>
<query-select
label="风险因素"
:data="riskReasons"
v-model="query.smallTags"
@change="getList"
/>
<query-select
label="学历"
:data="educations"
v-model="query.educationTags"
@change="getList"
/>
<query-select
label="年龄"
:data="ages"
v-model="query.ageTags"
@change="getList"
/>
<div class="query-row flex v-center">
<label class="text-center">人员信息</label>
<el-row>
<el-col :span="5">
<el-form-item label="姓名">
<el-input
style="width: 200px"
placeholder="请输入"
v-model="query.name"
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="身份证">
<el-input
style="width: 200px"
placeholder="请输入"
v-model="query.idCode"
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="手机号">
<el-input
style="width: 200px"
placeholder="请输入"
v-model="query.mobileNumber"
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="管控单位">
<depart-tree-select
v-model="query.controlDepartId"
placeholder="管控单位"
/>
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
<div class="mb-25 flex end mt-8">
<div>
<el-button type="primary" @click="getList">
<template #icon>
<icon name="el-icon-Search" />
</template>
查询
</el-button>
<el-button @click="reset">重置</el-button>
</div>
</div>
</header>
<div class="table-container" v-loading="loading">
<el-table :data="list">
<el-table-column width="70" align="center">
<template #default="{ row }">
<div class="flex v-center">
<img
:src="row.avatar"
style="height: 60px"
v-if="row.avatar"
/>
<img
src="/imgs/personal.svg"
v-else
style="height: 60px"
/>
</div>
</template>
</el-table-column>
<el-table-column label="姓名" prop="name" width="70" />
<el-table-column
label="性别"
prop="gender"
width="56"
align="center"
>
<template #default="{ row }">
<span>{{ getGender(row.gender) }}</span>
</template>
</el-table-column>
<el-table-column label="年龄" prop="age" width="56" />
<el-table-column label="身份证号" prop="idCode" width="174" />
<el-table-column
label="手机号"
prop="mobileNumber"
width="116"
/>
<el-table-column
label="管控单位"
prop="controlDepartName"
width="120"
show-overflow-tooltip
/>
<el-table-column label="人员标签" width="240">
<template #default="{ row }">
<div class="flex gap-4 wrap" v-if="row.tags">
<el-tag
v-for="item in row.tags.split(',')"
:key="item"
>{{ item }}
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="高风险因素">
<template #default="{ row }">
<el-scrollbar max-height="80px">
<div class="flex gap-4 wrap">
<el-tag
v-for="item in getSmallTags(row.smallTags)"
:type="item.type"
:key="item"
>{{ item.value }}
</el-tag>
</div>
</el-scrollbar>
</template>
</el-table-column>
<el-table-column
label="高风险指数"
prop="riskScore"
width="100"
align="center"
>
<template #default="{ row }">
<span>{{ row.riskScore.toFixed(1) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="90">
<template #default="{ row }">
<div>
<el-button
type="primary"
link
@click="handleShowDesc(row)"
>查看详情
</el-button>
</div>
<el-button
type="primary"
link
@click="handleShowNotification(row)"
>预警提醒
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex end mt-8">
<el-pagination
@size-change="getList"
@current-change="getList"
:page-sizes="[10, 20, 50]"
v-model:page-size="query.size"
v-model:current-page="query.current"
layout="total, sizes, prev, pager, next"
:total="total"
>
</el-pagination>
</div>
</div>
<el-dialog
title="风险人员详情"
v-model="show"
width="80vw"
top="2vh"
style="margin-bottom: 2vh"
class="dialog-body-nopadding"
>
<el-row :gutter="20">
<el-col :span="5">
<el-scrollbar max-height="calc(96vh - 92px)" class="ml-16">
<div v-for="item in personal.riskClues" :key="item">
<div class="risk-anchor-title flex between">
<span>{{ item.riskName }}</span>
<span style="color: #999">
<span style="color: #333">{{
item.score
}}</span>
</span>
</div>
<div
v-for="(risk, index) in item.riskIndexs"
:key="index"
class="risk-anchor pointer flex between"
:active="activeRiskIndex === risk.riskIndex"
@click="handlescrollbarTo(risk.riskIndex)"
:disabled="risk.score === 0"
>
<span>{{ risk.riskIndex }}</span>
<span style="color: #999">
<span style="color: #333">{{
risk.score
}}</span>
</span>
</div>
</div>
</el-scrollbar>
</el-col>
<el-col :span="19">
<el-scrollbar
height="calc(96vh - 92px)"
style="padding-right: 16px"
ref="riskClueScrollbarRef"
>
<el-row class="mt-20 mb-20">
<el-col :span="19">
<p class="text-primary mb-10">风险人员基本情况</p>
<el-row>
<el-col :span="4">
<img
:src="personal.riskPersonal?.avatar"
v-if="personal.riskPersonal?.avatar"
style="height: 180px; width: 140px"
/>
<img
src="/imgs/personal.svg"
v-else
style="height: 180px; width: 140px"
/>
</el-col>
<el-col :span="20">
<div class="row">
<div class="col col-12">
<label>姓名</label>
<span>{{ activeRow.name }}</span>
</div>
<div class="col col-12">
<label>性别</label>
<span>{{
getGender(activeRow.gender)
}}</span>
</div>
<div class="col col-12">
<label>年龄</label>
<span>{{ activeRow.age }}</span>
</div>
<div class="col col-12">
<label>证件号码</label>
<span>{{ activeRow.idCode }}</span>
</div>
<div class="col col-12">
<label>手机号码</label>
<span>{{
activeRow.mobileNumber
}}</span>
</div>
<div class="col col-12">
<label>管控单位</label>
<span>{{
activeRow.controlDepartName
}}</span>
</div>
</div>
</el-col>
</el-row>
</el-col>
<el-col :span="5">
<div class="flex center column text-center">
<div
class="socre-box"
:type="
getScoreType(
personal.riskPersonal?.riskScore
)
"
>
{{
personal.riskPersonal?.riskScore.toFixed(
1
)
}}
</div>
<span style="font-size: 24px" class="mt-10">{{
getRiskTitle(
personal.riskPersonal?.riskScore
)
}}</span>
</div>
</el-col>
</el-row>
<div style="min-height: 50vh" class="risk-clue-container">
<div
v-for="(item, index) in personal.riskClues"
:key="index"
class="mb-20"
>
<h4>{{ item.riskName }}</h4>
<div v-for="risk in item.riskIndexs" :key="risk">
<div
class="second-title mb-8 mt-20"
:id="risk.riskIndex"
>
{{ risk.riskIndex }}
</div>
<el-table :data="risk.clues">
<el-table-column
label="发生时间"
prop="eventTime"
width="180"
/>
<el-table-column
label="风险因素"
prop="riskName"
width="160"
show-overflow-tooltip
/>
<el-table-column
label="风险内容"
show-overflow-tooltip
>
<template #default="{ row }">
<span
style="white-space: pre-wrap"
>{{ row.data }}</span
>
</template>
</el-table-column>
<el-table-column
width="80"
label="分值"
prop="scoreResult"
>
<template #default="{ row }">
<span
v-if="row.scoreResult != null"
>{{ row.scoreResult }}</span
>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
<div class="mb-20"></div>
</el-scrollbar>
</el-col>
</el-row>
</el-dialog>
<alarm-dialog
v-model="showNotification"
:departId="activeDepartId"
@close="showNotification = false"
/>
</template>
<script lang="ts" setup>
import {
listRiskPersonnel,
getRiskPersonnel,
} from "@/api/sensitivePerception/riskPersonnel";
import { alarmNotificationCommit } from "~/api/work/alarm";
import feedback from "~/utils/feedback";
import { listPoliceAll } from "~/api/system/police";
const query = ref({
current: 1,
size: 10,
});
const list = ref<any[]>([]);
const total = ref(0);
const show = ref(false);
let showNotification = ref(false);
const activeDepartId = ref("");
const handleShowNotification = (row) => {
showNotification.value = true;
activeDepartId.value = row.controlDepartId;
};
const loading = ref(false);
function getList() {
loading.value = true;
listRiskPersonnel(query.value).then((data) => {
list.value = data.records;
total.value = data.total;
loading.value = false;
});
}
function reset() {
query.value = {
current: 1,
size: 10,
};
getList();
}
onMounted(() => {
getList();
});
const activeRow = ref({});
const personal = ref({
riskClueList: [],
});
async function handleShowDesc(row) {
activeRow.value = row;
activeRiskIndex.value = "";
const data = await getRiskPersonnel(row.id);
show.value = true;
personal.value = data;
}
const filterJsonData = (row) => {
if (row.data == undefined) {
return "";
}
try {
let j = JSON.parse(row.data);
if (j.sourceData != undefined) {
j = JSON.parse(row.sourceData);
}
let str = "";
for (let key in j) {
if (key == "eventTime") {
continue;
}
if (key == "idCode") {
continue;
}
if (key == "departId") {
continue;
}
if (key == "personId") {
continue;
}
if (key == "name") {
continue;
}
str += j[key] + "\n";
}
return str;
} catch (e) {
return row.data;
}
};
function getGender(val) {
if (val == 1) {
return "男";
}
if (val == 2) {
return "女";
}
return "";
}
const tags = [
{
label: "涉相关警情人员",
value: "涉相关警情人员",
},
{
label: "涉违法犯罪人员",
value: "涉违法犯罪人员",
},
{
label: "涉投诉举报信访人员",
value: "涉投诉举报信访人员",
},
{
label: "涉诉讼人员",
value: "涉诉讼人员",
},
{
label: "重点人员",
value: "重点人员",
},
{
label: "专项排查关注人员",
value: "专项排查关注人员",
},
{
label: "推送排查人员",
value: "推送排查人员",
},
{
label: "110纠纷报警3次以上的人员",
value: "110纠纷报警3次以上的人员",
},
{
label: "执法办案嫌疑人员",
value: "执法办案嫌疑人员",
},
{
label: "矛盾纠纷排查人员",
value: "矛盾纠纷排查人员",
},
{
label: "精神疾病人员",
value: "精神疾病人员",
},
{
label: "案件受害人员",
value: "案件受害人员",
},
{
label: "刑事前科人员",
value: "刑事前科人员",
},
{
label: "吸毒涉毒人员",
value: "吸毒涉毒人员",
},
{
label: "个人极端人员",
value: "个人极端人员",
},
{
label: "四无人员",
value: "四无人员",
},
{
label: "涉酒驾人员",
value: "涉酒驾人员",
},
{
label: "借贷人员",
value: "借贷人员",
},
{
label: "违禁物品网购人员",
value: "违禁物品网购人员",
},
{
label: "其他人员",
value: "其他人员",
},
];
const riskReasons = [
{
label: "在逃人员",
value: "在逃人员",
},
{
label: "重大刑事犯罪前科人员",
value: "重大刑事犯罪前科人员",
},
{
label: "肇事肇祸等严重精神障碍患者",
value: "肇事肇祸等严重精神障碍患者",
},
{
label: "个人极端案事件重点人员",
value: "个人极端案事件重点人员",
},
{
label: "涉稳人员",
value: "涉稳人员",
},
{
label: "涉毒人员",
value: "涉毒人员",
},
{
label: "重点上访人员",
value: "重点上访人员",
},
{
label: "已被打击处理",
value: "已被打击处理",
},
{
label: "受到刑政处罚",
value: "受到刑政处罚",
},
{
label: "受到刑事处罚",
value: "受到刑事处罚",
},
{
label: "取保候审",
value: "取保候审",
},
{
label: "监视居住",
value: "监视居住",
},
{
label: "其它潜在危害人员",
value: "其它潜在危害人员",
},
{
label: "殴打他人",
value: "殴打他人",
},
{
label: "故意伤害",
value: "故意伤害",
},
{
label: "寻衅滋事",
value: "寻衅滋事",
},
{
label: "精神障碍患者",
value: "精神障碍患者",
},
{
label: "厌世情绪",
value: "厌世情绪",
},
{
label: "报复社会",
value: "报复社会",
},
{
label: "遭遇不公",
value: "遭遇不公",
},
{
label: "自杀",
value: "自杀",
},
{
label: "家暴",
value: "家暴",
},
{
label: "离婚",
value: "离婚",
},
{
label: "酗酒",
value: "酗酒",
},
{
label: "有车",
value: "有车",
},
{
label: "纠纷",
value: "纠纷",
},
{
label: "无业人员",
value: "无业人员",
},
];
const educations = [
{
label: "小学及以下",
value: "小学及以下",
},
{
label: "初中",
value: "初中",
},
{
label: "高中/中专",
value: "高中/中专",
},
{
label: "大学",
value: "大学",
},
{
label: "大学以上",
value: "大学以上",
},
];
const ages = [
{
label: "16~24岁",
value: "年龄处于16岁至24岁之间",
},
{
label: "25~34岁",
value: "年龄处于25岁至34岁之间",
},
{
label: "35~55岁",
value: "年龄处于35岁至55岁之间",
},
{
label: "56~65岁",
value: "年龄处于56岁至65岁之间",
},
{
label: "66~75岁",
value: "年龄处于66岁至74岁之间",
},
];
function getSmallTags(val) {
if (!val) {
return [];
}
const arr = val.split(",").map((tag) => {
return getSmallTagObj(tag);
});
arr.sort((a, b) => a.sort - b.sort);
return arr;
}
function getSmallTagObj(item) {
if (
item.includes("在逃") ||
item.includes("重大刑事犯罪") ||
item.includes("个人极端") ||
item.includes("涉稳人员") ||
item.includes("涉毒") ||
item.includes("重点上访人员") ||
item.includes("已被打击处理") ||
item.includes("受到刑政处罚") ||
item.includes("受到刑事处罚") ||
item.includes("精神病") ||
item.includes("自杀") ||
item.includes("报复社会") ||
item.includes("厌世情绪") ||
item.includes("安排后事") ||
item.includes("作案") ||
item.includes("诱发刺激事件") ||
item.includes("暴力伤害行为")
) {
return {
type: "danger",
sort: 1,
value: item,
};
}
if (
item.includes("取保候审") ||
item.includes("监视居住") ||
item.includes("其它潜在危害人员") ||
item.includes("殴打他人") ||
item.includes("故意伤害") ||
item.includes("寻衅滋事") ||
item.includes("精神障碍患者") ||
item.includes("扬言滋事") ||
item.includes("家暴") ||
item.includes("离婚") ||
item.includes("酗酒") ||
item.includes("纠纷") ||
item.includes("无业人员")
) {
return {
type: "warning",
sort: 2,
value: item,
};
}
return {
type: "info",
sort: 3,
value: item,
};
}
function getColor(score) {
if (score >= 80) {
return "red";
}
return "#333";
}
const riskClueScrollbarRef = ref();
const activeRiskIndex = ref("");
function handlescrollbarTo(id) {
activeRiskIndex.value = id;
riskClueScrollbarRef.value.setScrollTop(
document.getElementById(id).offsetTop
);
}
function getScoreType(score) {
if (score >= 80) {
return "red";
}
if (score >= 60) {
return "orange";
}
if (score >= 40) {
return "yellow";
}
if (score >= 20) {
return "blue";
}
return "green";
}
function getRiskTitle(score) {
if (score >= 80) {
return "极高风险";
}
if (score >= 60) {
return "较高风险";
}
if (score >= 40) {
return "中级风险";
}
if (score >= 20) {
return "较低风险";
}
return "极低风险";
}
</script>
<style lang="scss" scoped>
.socre-box {
border-radius: 9px;
border: 2px solid #f11d16;
height: 153px;
line-height: 153px;
text-align: center;
font-size: 120px;
color: #fff;
&[type="red"] {
background: linear-gradient(180deg, #ff6641 0%, #d40505 100%);
border-color: #c51b08;
}
&[type="orange"] {
background: linear-gradient(180deg, #fe9e00 0%, #ec360c 100%);
border-color: #cd7242;
}
&[type="yellow"] {
background: linear-gradient(180deg, #fbc203 0%, #f08a02 100%);
border-color: #dfa100;
}
&[type="blue"] {
background: linear-gradient(180deg, #01b0f1 0%, #026cf1 100%);
border-color: #0693c9;
}
&[type="green"] {
background: linear-gradient(180deg, #23e84b 0%, #07b303 100%);
border-color: #0dc916;
}
}
.person-photo {
text-align: center;
margin-bottom: 10px;
}
.person-photo img {
max-height: 100%;
width: 200px;
}
.query-row {
padding: 8px 0;
box-shadow: inset 0px -1px 0px 0px #ebebeb;
label {
width: 126px;
line-height: 24px;
& + * {
width: calc(100% - 126px);
}
}
.el-form-item {
margin-bottom: 0;
}
}
.risk-anchor-title {
height: 60px;
line-height: 60px;
padding: 0 20px;
color: #222;
font-weight: 700;
}
.risk-anchor {
height: 40px;
line-height: 40px;
box-shadow: inset 0px -1px 0px 0px #e9ebfd;
padding: 0 20px;
&:hover,
&[active="true"] {
background: #d1d7ff;
}
&[disabled="true"] {
* {
color: #c2c2c2 !important;
}
}
}
.risk-clue-container {
> div:first-child {
--color: #926509;
--bg-color: #fff5df;
}
> div:nth-child(2) {
--color: #023950;
--bg-color: #e0f6ff;
}
> div:nth-child(3) {
--color: #7f0f25;
--bg-color: #f0f0f0;
}
> div {
--color: var(--primary-color);
--bg-color: #eff0f5;
h4 {
height: 58px;
line-height: 58px;
margin: 0;
background: var(--bg-color);
color: var(--color);
padding-left: 8px;
}
.second-title {
color: var(--color);
padding-left: 8px;
font-size: 16px;
}
:deep() {
.el-table {
thead {
color: #333;
}
th.el-table__cell {
background: var(--bg-color);
}
}
}
}
}
</style>