预警分析

This commit is contained in:
给我
2026-01-14 18:30:19 +08:00
parent 9aa7078024
commit cc7f225199
8 changed files with 760 additions and 78 deletions

View File

@ -1,82 +1,30 @@
<template> <template>
<el-dialog <el-dialog :title="titleValue" width="1400px" :model-value="modelValue" append-to-body @close="closed">
:title="titleValue"
width="1400px"
:model-value="modelValue"
append-to-body
@close="closed"
>
<div> <div>
<el-form :model="listQuery" class="mosty-from-wrap" :inline="true"> <el-form :model="listQuery" class="mosty-from-wrap" :inline="true">
<el-form-item label="所属部门"> <el-form-item label="所属部门">
<MOSTY.Department width="100%" clearable v-model="listQuery.ssbmdm" /> <MOSTY.Department width="100%" clearable v-model="listQuery.ssbmdm" />
</el-form-item> </el-form-item>
<el-form-item label="用户名"> <el-form-item label="用户名">
<el-input <el-input placeholder="请输入用户名" v-model="listQuery.loginName" clearable></el-input>
placeholder="请输入用户名"
v-model="listQuery.loginName"
clearable
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="电话号码"> <el-form-item label="电话号码">
<el-input <el-input placeholder="请输入电话号码" v-model="listQuery.phone" clearable></el-input>
placeholder="请输入电话号码"
v-model="listQuery.phone"
clearable
></el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="success" @click="handleFilter">查询</el-button> <el-button type="success" @click="handleFilter">查询</el-button>
<el-button type="info" @click="reset()"> 重置 </el-button> <el-button type="info" @click="reset()"> 重置 </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div <div class="tabBox" :class="props.Single ? 'tabBoxRadio' : ''" style="margin-top: 0px">
class="tabBox" <el-table ref="multipleUserRef" @selection-change="handleSelectionChange" :data="tableData" border
:class="props.Single ? 'tabBoxRadio' : ''" :row-key="keyid" style="width: 100%" height="450">
style="margin-top: 0px" <el-table-column type="selection" width="55" :reserve-selection="true" />
> <el-table-column prop="loginName" align="center" label="用户名" width="150"></el-table-column>
<el-table <el-table-column prop="idEntityCard" align="center" label="身份证号"></el-table-column>
ref="multipleUserRef" <el-table-column prop="deptName" align="center" label="部门"></el-table-column>
@selection-change="handleSelectionChange" <el-table-column prop="inDustRialId" align="center" width="150" label="警号"></el-table-column>
:data="tableData" <el-table-column prop="mobile" width="150" align="center" label="电话"></el-table-column>
border
:row-key="keyid"
style="width: 100%"
height="450"
>
<el-table-column
type="selection"
width="55"
:reserve-selection="true"
/>
<el-table-column
prop="loginName"
align="center"
label="用户名"
width="150"
></el-table-column>
<el-table-column
prop="idEntityCard"
align="center"
label="身份证号"
></el-table-column>
<el-table-column
prop="deptName"
align="center"
label="部门"
></el-table-column>
<el-table-column
prop="inDustRialId"
align="center"
width="150"
label="警号"
></el-table-column>
<el-table-column
prop="mobile"
width="150"
align="center"
label="电话"
></el-table-column>
<el-table-column prop="sex" align="center" label="性别"> <el-table-column prop="sex" align="center" label="性别">
<template #default="{ row }"> <template #default="{ row }">
<span> {{ row.sex == 1 ? "男" : "女" }}</span> <span> {{ row.sex == 1 ? "男" : "女" }}</span>
@ -85,16 +33,9 @@
</el-table> </el-table>
</div> </div>
<div class="fenye" :style="{ top: tableHeight + 'px' }"> <div class="fenye" :style="{ top: tableHeight + 'px' }">
<el-pagination <el-pagination class="pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange"
class="pagination" :current-page="listQuery.current" :page-sizes="[10, 20, 50, 100]" :page-size="listQuery.size"
@size-change="handleSizeChange" layout="total, sizes, prev, pager, next, jumper" :total="total"></el-pagination>
@current-change="handleCurrentChange"
:current-page="listQuery.current"
:page-sizes="[10, 20, 50, 100]"
:page-size="listQuery.size"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div> </div>
</div> </div>
<template #footer> <template #footer>

View File

@ -0,0 +1,194 @@
<template>
<el-dialog :title="titleValue" width="1400px" :model-value="modelValue" append-to-body @close="closed">
<div>
<el-form :model="listQuery" class="mosty-from-wrap" :inline="true">
<el-form-item label="姓名">
<el-input placeholder="请输入姓名" v-model="listQuery.yjRyxm" clearable></el-input>
</el-form-item>
<el-form-item label="身份证号">
<el-input placeholder="请输入身份证号" v-model="listQuery.yjRysfzh" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button type="success" @click="handleFilter">查询</el-button>
<el-button type="info" @click="reset()"> 重置 </el-button>
</el-form-item>
</el-form>
<div class="tabBox" :class="props.Single ? 'tabBoxRadio' : ''" style="margin-top: 0px">
<el-table ref="multipleUserRef" @selection-change="handleSelectionChange" :data="tableData" border
:row-key="keyid" style="width: 100%" height="450">
<el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column prop="yjRyxm" align="center" label="姓名" width="150"></el-table-column>
<el-table-column prop="yjRysfzh" align="center" label="身份证号"></el-table-column>
<el-table-column prop="yjBt" align="center" label="标题"></el-table-column>
<el-table-column prop="yjbqmc" align="center" width="150" label="标签"></el-table-column>
<el-table-column prop="yjDz" width="150" align="center" label="预警地址"></el-table-column>
<el-table-column prop="yjTp" align="center" label="预警图片">
<template #default="{ row }">
<el-image :preview-src-list="[row.yjTp]" fit="cover" style="width: 100px; height: 100px"
:src="row.yjTp"></el-image>
</template>
</el-table-column>
</el-table>
</div>
<div class="fenye" :style="{ top: tableHeight + 'px' }">
<el-pagination class="pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="listQuery.pageCurrent" :page-sizes="[10, 20, 50, 100]" :page-size="listQuery.pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="total"></el-pagination>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="closed">取消</el-button>
<el-button type="primary" @click="onComfirm">确认</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import * as rule from "@/utils/rules.js";
import * as MOSTY from "@/components/MyComponents/index";
import { ElMessage } from "element-plus";
import { qcckGet, qcckPost } from "@/api/qcckApi.js";
import { defineProps, watch, ref, onMounted, nextTick } from "vue";
import { selectUnAccreditPage, selectUserDeptPage } from "@/api/user-manage";
const props = defineProps({
modelValue: {
type: Boolean,
required: true
},
titleValue: {
type: String,
default: "选择用户预警人员"
},
LeaderType: {
type: String,
default: ""
},
//是否单选
Single: {
type: Boolean,
default: true
},
roleIds: {
type: Array,
default: []
}
});
const total = ref(0);
const listQuery = ref({
pageCurrent: 1,
pageSize: 20
});
const form = ref({});
const tableData = ref([]);
const emits = defineEmits(["update:modelValue", "choosedUsers"]);
onMounted(() => {
handleFilter();
});
const closed = () => {
emits("update:modelValue", false);
};
const reset = () => {
listQuery.value = {
pageCurrent: 1,
pageSize: 20,
loginName: "",
phone: ""
};
getListData();
};
const keyid = (row) => {
return row.id;
};
// 为用户分配角色
const onComfirm = () => {
const userList = multipleSelectionUser.value;
let list = [];
let listId = [];
userList.forEach((val) => {
if (listId.indexOf(val.id) == -1) {
list.push(val);
listId.push(val.id);
}
});
emits("choosedUsers", list);
let data = { type: props.LeaderType, userList: userList };
emits("choosedUsersLeader", data);
closed();
};
/**
* pageSize 改变触发
*/
const handleSizeChange = (currentSize) => {
listQuery.value.pageSize = currentSize;
getListData();
};
/**
* 页码改变触发
*/
const handleCurrentChange = (currentPage) => {
listQuery.value.pageCurrent = currentPage;
getListData();
};
const getListData = () => {
const params = listQuery.value;
qcckPost(params, '/mosty-jcz/tbJczYjxx/getPageList').then((res) => {
tableData.value = res?.records;
total.value = Number(res.total);
multipleUser();
});
};
//列表回显
function multipleUser() {
tableData.value.forEach((item) => {
if (props.roleIds.some((id) => id == item.id)) {
multipleUserRef.value.toggleRowSelection(item, true);
}
});
}
const handleFilter = () => {
listQuery.value.pageCurrent = 1;
getListData();
};
const multipleUserRef = ref(null);
const multipleSelectionUser = ref([]);
const handleSelectionChange = (val) => {
if (props.Single) {
if (val.length > 1) {
let del_row = val.shift();
multipleUserRef.value.toggleRowSelection(del_row, false);
}
multipleSelectionUser.value = val;
} else {
multipleSelectionUser.value = val;
}
};
</script>
<style lang="scss" scoped>
@import "@/assets/css/layout.scss";
@import "@/assets/css/element-plus.scss";
</style>
<style>
.tabBoxRadio .el-checkbox__inner {
border-radius: 50% !important;
}
.tabBoxRadio .el-table__header-wrapper .el-checkbox {
display: none;
}
.el-dialog {
/* background: #050e33; */
}
.el-dialog__title {
/* color: #fff; */
}
</style>

View File

@ -440,6 +440,18 @@ export const publicRoutes = [
title: "过站汇总管理", title: "过站汇总管理",
icon: "article" icon: "article"
} }
},
{
path: "/CheckpointAnalysis",
name: "CheckpointAnalysis",
component: () =>
import(
"@/views/backOfficeSystem/peopleManag/CheckpointAnalysis/index"
),
meta: {
title: "卡口管控分析管理",
icon: "article"
}
} }
] ]
}, },

View File

@ -0,0 +1,98 @@
<template>
<div style="height: 100%; width: 100%" :id="echartsId"></div>
</template>
<script setup>
import * as echarts from "echarts";
import {
onMounted,
ref,
reactive,
defineProps,
onUnmounted,
watch,
nextTick
} from "vue";
const props = defineProps({
echartsId: {
type: String,
default: "lineId"
},
color: {
type: String,
default: "#fff"
},
data: {
type: Object,
default: {}
}
});
function chartFn() {
var myChart = echarts.init(document.getElementById(props.echartsId));
var option = {
legend: {
data: ['出城车辆', '入城车辆'],
textStyle: { color: "#fff" },
},
grid: {
top: "10%",
right: "0",
left: "0",
bottom: "10%",
containLabel: true
},
tooltip: {
trigger: "axis",
axisPointer: {
lineStyle: {
color: "#ddd"
}
},
backgroundColor: "rgba(255,255,255,1)",
padding: [5, 10],
textStyle: {
color: "#7588E4"
},
extraCssText: "box-shadow: 0 0 5px rgba(0,0,0,0.3)"
},
xAxis: {
type: "category",
boundaryGap: false,
data: props.data.rzcl.map((el) => el.time)
},
yAxis: {
type: "value"
},
series: [
{
name: "出城车辆",
type: "line",
stack: "Total",
smooth: true,
data: props.data.rzcl.map((el) => el.num)
},
{
name: "入城车辆",
type: "line",
stack: "Total",
smooth: true,
data: props.data.czcl.map((el) => el.num)
},
]
};
option && myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
}
onMounted(() => {
// chartFn();
});
watch(() => props.data, (val) => {
if (val) {
chartFn();
}
})
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,80 @@
<template>
<div style="height: 100%; width: 100%" :id="echartsId"></div>
</template>
<script setup>
import * as echarts from "echarts";
import {
onMounted,
ref,
reactive,
defineProps,
onUnmounted,
watch,
nextTick
} from "vue";
const props = defineProps({
echartsId: {
type: String,
default: "pieId"
},
color: {
type: String,
default: "#fff"
},
data: {
type: Array,
default: []
}
});
function chartFn() {
var myChart = echarts.init(document.getElementById(props.echartsId));
var option = {
legend: {
bottom: '5%',
left: 'center',
textStyle: { color: "#fff" },
},
grid: {
top: "0",
right: "0",
left: "0",
bottom: "12%",
containLabel: true
},
tooltip: {
trigger: "item",
},
series: [
{
name: '',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
labelLine: {
show: false
},
data: props.data
}
]
};
option && myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
}
onMounted(() => {
// chartFn();
});
watch(() => props.data, (val) => {
if (val) {
chartFn();
}
})
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,359 @@
<template>
<div>
<div class="dashboard-container">
<!-- 顶部数据卡片区域 -->
<div class="top-cards">
<el-card class="card-item" v-for="item in topData" :key="item">
<div class="card-title">{{ item.bt }}</div>
<div class="card-value">{{ item.num }}</div>
<div class="card-desc">{{ item.nr }}</div>
</el-card>
</div>
<!-- 中间图表+数据区域 -->
<div class="middle-section">
<!-- 左侧车流量折线图 -->
<el-card class="section-item">
<div class="section-title flex just-between align-center">
<div>进出镇区主干道卡口数据监测</div>
<!-- <el-select v-model="value" placeholder="请选择" style="width: 240px">
<el-option label="今日数量" value="0" />
</el-select> -->
</div>
<LineEchart :data="clData" style="height: 200px" />
<div class="traffic-footer">
<div>入镇口{{ clData.rck }}</div>
<div>出镇口{{ clData.cck }}</div>
<div>重点卡口{{ clData.zdkk }}</div>
</div>
</el-card>
<!-- 右侧人口结构环形图 -->
<el-card class="section-item">
<div class="section-title">入镇人员分析模型-人口数据统计</div>
<PieEcharts style="height: 210px" :data="yjfxData" />
<div class="population-footer">
<div v-for="item in yjfxData" :key="item">{{ item.bt }}{{ item.num }} ({{ item.bfb }}%)</div>
</div>
</el-card>
</div>
<!-- 底部数据+地图区域 -->
<div class="bottom-section">
<!-- 左侧预警人员表格 -->
<el-card class="section-item_small">
<div class="section-title">人员数据比对分析-预警人员信息</div>
<el-row>
<el-col :span="2">
<div>#</div>
</el-col>
<el-col :span="10">人员信息</el-col>
<el-col :span="8">预警类型</el-col>
<el-col :span="4">风险等级</el-col>
</el-row>
<el-row v-for="(el, index) in tableData" :key="el.id">
<el-col :span="2">
<div>{{ index + 1 }}</div>
</el-col>
<el-col :span="10"> {{ el.yjRyxm }} </el-col>
<el-col :span="8"> <dict-tag :options="D_BZ_YJLX" :value="el.yjLx" :tag="false" /> </el-col>
<el-col :span="4" style="text-align: center;"> <dict-tag :options="D_BZ_YJJB" :value="el.yjJb"
:tag="false" /> </el-col>
</el-row>
<el-button type="primary" style="width: 100%; margin-top: 10px">查看全部预警人员(63)</el-button>
</el-card>
<!-- 右侧地图+监控说明 -->
<el-card class="section-item map-card">
<div class="section-title flex just-between align-center">
<div>预警人员轨迹追踪系统-多源数据联动</div>
<div class="flex">
<el-input readonly @click="chooseUserVisible = true" v-model="listQuery.yjRyxm" placeholder="请选择预警人员"
clearable />
<!-- <el-button type="primary">轨迹跟踪</el-button> -->
</div>
</div>
<div class="map-container">
<div class="mapbox">
<GdMap />
</div>
</div>
</el-card>
</div>
</div>
</div>
<ChooseYjUser v-model="chooseUserVisible" @choosedUsers="saveUsers" :Single="true"></ChooseYjUser>
</template>
<script setup>
import GdMap from "@/components/GdMap/index.vue";
import { qcckGet, qcckPost } from "@/api/qcckApi.js";
import ChooseYjUser from "@/components/MyComponents/ChooseYjUser";
import LineEchart from "./components/lineEcharts.vue";
import PieEcharts from "./components/pieEcharts.vue";
import { reactive, ref, onMounted, getCurrentInstance } from "vue";
const { proxy } = getCurrentInstance();
const { D_BZ_YJLX, D_BZ_YJJB } = proxy.$dict("D_BZ_YJLX", "D_BZ_YJJB");
// 预警人员列表数据
const listQuery = ref({
pageCurrent: 1,
pageSize: 10
});
const topData = ref([]);
const clData = ref({});
const yjfxData = ref(null);
const value = ref();
const chooseUserVisible = ref(false);
const tableData = ref([]);
onMounted(() => {
Promise.all([getTopTj(), getclTj(), getYjfxTj(), getListData()]);
});
//查询条件
const queryCondition = ref({});
// 统计
const getTopTj = () => {
qcckGet({}, "/mosty-jcz/kktj/getKkgkfx").then((res) => {
topData.value = res;
});
};
const getclTj = () => {
qcckGet({}, "/mosty-jcz/kktj/getClkksjtj")
.then((res) => {
clData.value = res;
})
};
const getYjfxTj = () => {
qcckGet({}, "/mosty-jcz/kktj/getRjryfxmx")
.then((res) => {
yjfxData.value = res.map((el) => {
return { value: el.num, name: el.bt, ...el }
});
})
};
const getListData = () => {
qcckPost(listQuery.value, '/mosty-jcz/tbJczYjxx/getPageList').then((res) => {
tableData.value =
listQuery.value.pageCurrent == 1
? res.records
: tableData.value.concat(res.records);
});
};
const columnInfo = (val) => {
// 过站类型01 入林 02 出林) 01 入林 02 出林
dialogSearch.value.kkId = val.row.kkId;
switch (val.column.property) {
case "gzryNum":
dialogSearch.value.gzlx = "";
showGjry.value = true;
break;
case "rlclNum":
dialogSearch.value.gzlx = "01";
showGjry.value = true;
break;
case "clryNum":
dialogSearch.value.gzlx = "02";
showGjry.value = true;
break;
case "gzclNum":
dialogSearch.value.gzlx = "";
showGjcl.value = true;
break;
case "rlclNum":
dialogSearch.value.gzlx = "01";
showGjcl.value = true;
break;
case "clclNum":
dialogSearch.value.gzlx = "02";
showGjcl.value = true;
break;
case "gzwpNum":
dialogSearch.value.gzlx = "";
showGjwp.value = true;
break;
case "rlwpNum":
dialogSearch.value.gzlx = "01";
showGjwp.value = true;
break;
case "clwpNum":
dialogSearch.value.gzlx = "02";
showGjwp.value = true;
break;
}
};
//选择预警人员
const saveUsers = (users) => {
listQuery.value.yjRyxm = users[0].yjRyxm;
listQuery.value.yjRysfzh = users[0].yjRysfzh;
qcckPost({ yjRysfzh: listQuery.value.yjRysfzh }, '/mosty-jcz/tbJczYjxx/getPageList').then((res) => {
let list = res.records.filter((item) => {
return item.jd && item.wd
})
emitter.emit("echoPlane", {
fontColor: "#12fdb8",
coords: list,
type: "line",
flag: "yjry_gj",
color: "rgba(2,20,51,0.5)",
linecolor: "#1C97FF"
});
});
};
</script>
<style lang="scss" scoped>
.dashboard-container {
width: 100%;
height: 100vh;
box-sizing: border-box;
}
/* 顶部卡片 */
.top-cards {
display: flex;
gap: 20px;
margin-bottom: 10px;
}
.card-item {
flex: 1;
background-color: #1e1e30;
border: none;
color: #fff;
}
.card-item.warning {
border: 1px solid #f56c6c;
}
.card-title {
font-size: 14px;
color: #ccc;
margin-bottom: 8px;
}
.card-value {
font-size: 24px;
font-weight: bold;
}
.card-desc {
font-size: 12px;
color: #999;
margin-top: 8px;
}
/* 中间区域 */
.middle-section {
display: flex;
gap: 20px;
margin-bottom: 10px;
height: 300px;
}
.section-item {
flex: 1;
background-color: #1e1e30;
border: none;
color: #fff;
display: flex;
flex-direction: column;
}
.section-item_small {
width: 33%;
background-color: #1e1e30;
border: none;
color: #fff;
display: flex;
flex-direction: column;
}
.section-title {
font-size: 16px;
margin-bottom: 10px;
padding: 0 10px;
}
.chart-container {
flex: 1;
width: 100%;
}
.traffic-footer,
.population-footer {
display: flex;
justify-content: space-around;
padding: 10px 0;
font-size: 14px;
color: #ccc;
}
/* 底部区域 */
.bottom-section {
display: flex;
gap: 20px;
height: 340px;
/* 适配剩余高度 */
}
.map-card {
flex: 1;
}
.map-container {
width: 100%;
height: 100%;
position: relative;
.mapbox {
width: 100%;
padding: 0 10px;
height: 250px;
box-sizing: border-box;
background: #000;
}
}
.map-bg {
width: 100%;
height: 100%;
background: url("https://picsum.photos/800/400") center/cover;
opacity: 0.7;
}
.monitor-tip {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(20, 20, 30, 0.8);
padding: 20px;
border-radius: 8px;
}
.tip-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
.tip-desc {
font-size: 14px;
color: #ccc;
margin-bottom: 10px;
}
.tip-features {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 5px;
font-size: 12px;
color: #ccc;
}
::v-deep .el-col {
text-align: center;
}
</style>

View File

@ -27,10 +27,8 @@ const open = async (row) => {
visible.value = true visible.value = true
await nextTick() await nextTick()
emitter.emit('map-resize') emitter.emit('map-resize')
const res = await RyGjTrajectory({ sfzh: sfzh.value, days: 30 }) const res = await RyGjTrajectory({ sfzh: sfzh.value, days: 30 })
const list = Array.isArray(res) ? res : (res?.data?.data || []) const list = Array.isArray(res) ? res : (res?.data?.data || [])
if (!list || list.length === 0) { if (!list || list.length === 0) {
ElMessage({ message: '近30天无轨迹数据', type: 'info' }) ElMessage({ message: '近30天无轨迹数据', type: 'info' })
emitter.emit('removeElement', 'rygj') emitter.emit('removeElement', 'rygj')

View File

@ -5,7 +5,7 @@ function resolve(dir) {
} }
// const serverHost = "http://183.222.39.242:38006"//波哥 // const serverHost = "http://183.222.39.242:38006"//波哥
// const serverHost = "http://47.108.232.77:9537"; // const serverHost = "http://47.108.232.77:9537";
const serverHost = "http://192.168.3.170:8006" const serverHost = "http://j96926b5.natappfree.cc"
// const serverHost = "http://192.168.1.98:8006" // const serverHost = "http://192.168.1.98:8006"
// const serverHost = "http://n5e6d39a.natappfree.cc"//周 // const serverHost = "http://n5e6d39a.natappfree.cc"//周
// const serverHost = "http://192.168.2.206:8006"//线上 // const serverHost = "http://192.168.2.206:8006"//线上