feat: 响应预警 驻地台账

This commit is contained in:
huangchenhao 2026-04-02 16:57:40 +08:00
parent 35d22fffb9
commit ff0d7ee6b0
7 changed files with 835 additions and 2 deletions

View File

@ -56,7 +56,7 @@ const routes = [
{
path: '/riskWarning',
name: 'RiskWarning',
meta: {
meta: {
screen: true
},
component: () => import('../views/RiskWarning/index.vue')
@ -85,6 +85,18 @@ const routes = [
},
component: () => import('../views/ConstructionDepartment/ConstructionDepartment.vue')
},
// 响应预警
{
path: '/warningManagement',
name: 'warningManagement',
component: () => import('../views/WarningManagement/index.vue')
},
// 驻地台账
{
path: '/ledgerManagement',
name: 'ledgerManagement',
component: () => import('../views/LedgerManagement/index.vue')
},
]
const router = createRouter({

View File

@ -0,0 +1,201 @@
import { h, ref, onMounted, reactive, watch, toRaw, nextTick } from "vue";
import { request } from "@/utils/request";
import { useRoute, useRouter } from 'vue-router'
const tableData = ref([]); // 表格数据
const modelVisible = ref(false); // 弹窗状态
const drawerVisible = ref(false); // 抽屉状态
// 弹窗内容
const model = reactive({
title: '',
content: null,
props: {},
onCancel: null,
onConfirm: null,
width: '',
});
const form = reactive({
});
const INIT_FORM = {
};
// 抽屉内容
const drawer = reactive({
title: '',
content: null,
props: {},
onCancel: null,
onConfirm: null,
direction: 'rtl',
size: '50%'
});
const dialogRef = ref(null); // 弹窗实例
const drawerRef = ref(null); // 抽屉实例
const columns = [
{
prop: "xxx",
label: "所属区县",
},
{
prop: "xxx",
label: "项目名称",
},
{
prop: "xxx",
label: "驻地名称",
},
{
prop: "xxx",
label: "驻地类型",
},
{
prop: "xxx",
label: "坐标点位",
},
{
prop: "xxx",
label: "所属项目名称",
},
{
prop: "xxx",
label: "项目类型",
},
{
prop: "xxx",
label: "建设单位",
},
{
prop: "xxx",
label: "施工单位",
},
{
prop: "xxx",
label: "驻地地址",
},
{
prop: "xxx",
label: "行政区域",
},
{
prop: "xxx",
label: "驻地人数",
},
{
prop: "xxx",
label: "驻地风险等级",
},
{
prop: "xxx",
label: "房建类型",
},
{
prop: "xxx",
label: "搬迁状态",
},
{
prop: "xxx",
label: "吹哨人/电话",
},
{
prop: "xxx",
label: "建设单位包保责任人/电话",
},
{
prop: "xxx",
label: "施工单位包保责任人/电话",
},
{
prop: "xxx",
label: "驻地包保责任人/电话",
},
{
prop: "xxx",
label: "区县级包保责任人/电话",
},
{
prop: "xxx",
label: "市级包保责任人/电话",
},
{
prop: "xxx",
label: "备注",
},
]
// 过滤条件
const filterData = reactive({
title: "",
type: "",
warningLevel: "",
})
// 分页
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
pageSizes: [10, 20, 50],
layout: "prev, pager, next, jumper",
onChange: (page, pageSize) => {
pagination.current = page;
pagination.pageSize = pageSize;
getTableData(filterData);
},
});
// 获取列表
const getTableData = async (filterData) => {
try {
const res = await request({
url: '',
method: "GET",
params: {
}
})
} catch (error) {
}
}
// 预警类型选项
const typeOptions = [
{ label: "全部", value: "" },
{ label: "台风预警", value: "台风预警" },
{ label: "暴雨预警", value: "暴雨预警" },
{ label: "寒潮预警", value: "寒潮预警" },
{ label: "大雾预警", value: "大雾预警" },
{ label: "暴雪预警", value: "暴雪预警" },
{ label: "道路结冰预警", value: "道路结冰预警" },
{ label: "雷电预警", value: "雷电预警" },
{ label: "强对流预警", value: "强对流预警" },
];
// 预警级别选项
const warningLevelOptions = [
{ label: "全部", value: "" },
{ label: "红色预警", value: "红色预警" },
{ label: "橙色预警", value: "橙色预警" },
{ label: "黄色预警", value: "黄色预警" },
{ label: "蓝色预警", value: "蓝色预警" },
]
export default () => {
return {
modelVisible,
model,
drawerVisible,
drawer,
dialogRef,
drawerRef,
tableData,
filterData,
pagination,
columns,
}
}

View File

@ -0,0 +1,83 @@
<template>
<div class="root">
<div class="search-box">
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="项目名称" :suffix-icon="Search" />
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="驻地名称" :suffix-icon="Search" />
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="建设单位" :suffix-icon="Search" />
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="施工单位" :suffix-icon="Search" />
<el-select v-model="script.filterData.type" style="width: 240px; margin-right: 10px" size="large"
placeholder="项目类型" :suffix-icon="ArrowDown" :options="script.typeOptions" clearable />
<el-select v-model="script.filterData.warningLevel" style="width: 240px; margin-right: 10px" size="large"
placeholder="驻地类型" :suffix-icon="ArrowDown" :options="script.warningLevelOptions" clearable />
<el-select v-model="script.filterData.warningLevel" style="width: 240px; margin-right: 10px" size="large"
placeholder="驻地风险等级" :suffix-icon="ArrowDown" :options="script.warningLevelOptions" clearable />
<el-select v-model="script.filterData.warningLevel" style="width: 240px; margin-right: 10px" size="large"
placeholder="所属区县" :suffix-icon="ArrowDown" :options="script.warningLevelOptions" clearable />
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="吹哨人姓名" :suffix-icon="Search" />
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="建设单位责任人姓名" :suffix-icon="Search" />
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="区县级责任人姓名" :suffix-icon="Search" />
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="市级责任人姓名" :suffix-icon="Search" />
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="施工单位责任人姓名" :suffix-icon="Search" />
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="驻地责任人姓名" :suffix-icon="Search" />
</div>
<div class="event-box">
<el-button type="primary" color="#952DE6" @click="">导出</el-button>
<el-button type="primary" @click="">导入</el-button>
</div>
<DynamicTable :dataSource="script.tableData.value" :columns="script.columns" :autoHeight="true"
:pagination="script.pagination">
</DynamicTable>
<div class="model-box">
<MyDialog v-model="script.modelVisible.value" :title="script.model?.title"
:dynamicComponent="script.model?.content" :component-props="script.model?.props"
:onConfirm="script.model?.onConfirm" :onCancel="script.model?.onCancel" ref="dialogRef"
:width="script.model?.width">
</MyDialog>
<MyDrawer v-model="script.drawerVisible.value" :title="script.drawer?.title"
:dynamicComponent="script.drawer?.content" :component-props="script.drawer?.props"
:onConfirm="script.drawer?.onConfirm" :onCancel="script.drawer?.onCancel" ref="drawerRef"
:direction="script.drawer?.direction" :size="script.drawer?.size"></MyDrawer>
</div>
</div>
</template>
<script setup>
import DynamicTable from "../../component/DynamicTable";
import { Search, ArrowDown, CaretBottom } from "@element-plus/icons-vue";
import MyDialog from "../../component/MyDialog";
import MyDrawer from "../../component/MyDrawer";
import scriptFn from "./index.js";
const script = scriptFn();
const { dialogRef, drawerRef } = script;
</script>
<style scoped>
.search-box {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.root {
height: 100%;
padding: 25px;
}
.event-box {
margin: 20px 0;
display: flex;
justify-content: flex-start;
}
</style>

View File

@ -0,0 +1,265 @@
<template>
<div class="detail-container">
<el-form
ref="formRef"
:model="form"
label-position="right"
label-width="140px"
style="max-height: 60vh; overflow-y: auto; padding-right: 50px"
:rules="rules"
>
<el-row>
<el-form-item label="所属区县" prop="qxmc">
<el-select
v-model="qx"
filterable
remote
reserve-keyword
clearable
placeholder="输入区县名称查询"
:remote-method="remoteMethod_qx"
:loading="loading"
@change="handleSelect_qx"
value-key="index"
>
<el-option
v-for="(item, index) in qxList"
:key="index"
:label="item.qxmc"
:value="item.qxmc"
/>
</el-select>
</el-form-item>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="服务站名称" prop="mc">
<el-input maxlength="20" show-word-limit v-model="form.mc" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="负责人:" prop="fzr">
<el-select
v-model="ryxx"
filterable
remote
reserve-keyword
clearable
placeholder="输入人员名称 / 联系方式 查询"
:remote-method="remoteMethod"
:loading="loading"
@change="handleSelect"
value-key="userId"
>
<el-option
v-for="item in selectOptions"
:key="item.userId"
:label="item.realName"
:value="item"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="负责人添加经纬度:">
<el-select v-model="sfjwd">
<el-option label="是" value="是"></el-option>
<el-option label="否" value="否"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="sfjwd === '否'">
<el-col :span="12">
<el-form-item label="站点经度:" prop="jd">
<el-input v-model="form.jd" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="站点纬度:" prop="wd">
<el-input v-model="form.wd" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script setup>
import { ref, onMounted, watch, computed } from "vue";
import { request } from "@/utils/request";
const formRef = ref(null);
defineExpose({ formRef });
const props = defineProps({
form: {
type: Object,
default: () => ({}),
},
});
const sfjwd = ref("是");
const qx = ref("");
const ryxx = ref("");
const loading = ref(false);
const selectOptions = ref([]);
const qxList = ref([]);
//
const getUserList = async (key) => {
try {
const keyword = key;
let url = "";
if (keyword) {
url = `/snow-ops-platform/yhzry/getUserByKey?key=${keyword}`;
} else {
url = `/snow-ops-platform/yhzry/getUserByKey?key=`;
}
const res = await request({
url: url,
method: "GET",
});
if (res.code === "00000") {
return res.data;
} else {
throw new Error(res.message);
}
} catch (error) {
ElMessage.error(error.message);
console.log(error);
}
};
//
const remoteMethod = async (query) => {
if (query === "") {
selectOptions.value = [];
return [];
}
loading.value = true;
const res = await getUserList(query);
if (res) {
selectOptions.value = res;
}
loading.value = false;
};
//
const getQxList = async (key) => {
try {
const keyword = key;
let url = "";
if (keyword) {
url = `/snow-ops-platform/district/listDistricts?qxmc=${keyword}`;
} else {
url = `/snow-ops-platform/district/listDistricts?qxmc=`;
}
const res = await request({
url: url,
method: "GET",
});
if (res.code === "00000") {
return res.data;
} else {
throw new Error(res.message);
}
} catch (error) {}
};
//
const remoteMethod_qx = async (query) => {
loading.value = true;
const res = await getQxList(query);
if (res) {
qxList.value = res;
}
loading.value = false;
};
//
const handleSelect_qx = (value) => {
props.form.qxmc = value;
};
//
const handleSelect = (value) => {
console.log("value", value);
props.form.fzrXm = value.realName;
props.form.fzrSjhm = value.phone;
props.form.fzrUserId = value.userId;
};
const rules = computed(() => {
return {
mc: [
{
required: true,
validator: (rule, value, callback) => {
if (props.form.mc) {
callback();
} else {
callback(new Error("请输入服务站名称"));
}
},
trigger: "blur",
},
],
qxmc: [
{
required: true,
validator: (rule, value, callback) => {
if (props.form.qxmc) {
callback();
} else {
callback(new Error("请选择所属区县"));
}
},
trigger: "blur",
},
],
fzr: [
{
required: true,
validator: (rule, value, callback) => {
if (props.form.fzrUserId && props.form.fzrXm && props.form.fzrSjhm) {
callback();
} else {
callback(new Error("请选择负责人"));
}
},
trigger: "blur",
},
],
jd: [
{
required: sfjwd.value === "否",
validator: (rule, value, callback) => {
if (props.form.jd) {
callback();
} else {
callback(new Error("请输入站点经度"));
}
},
trigger: "blur",
},
],
wd: [
{
required: sfjwd.value === "否",
validator: (rule, value, callback) => {
if (props.form.wd) {
callback();
} else {
callback(new Error("请输入站点纬度"));
}
},
trigger: "blur",
},
],
};
});
</script>
<style>
</style>

View File

@ -0,0 +1,215 @@
import { h, ref, onMounted, reactive, watch, toRaw, nextTick } from "vue";
import { request } from "@/utils/request";
import { useRoute, useRouter } from 'vue-router'
import AddDialog from "./addDialog.vue";
const tableData = ref([]); // 表格数据
const modelVisible = ref(false); // 弹窗状态
const drawerVisible = ref(false); // 抽屉状态
// 弹窗内容
const model = reactive({
title: '',
content: null,
props: {},
onCancel: null,
onConfirm: null,
width: '',
});
const form = reactive({
});
const INIT_FORM = {
};
// 抽屉内容
const drawer = reactive({
title: '',
content: null,
props: {},
onCancel: null,
onConfirm: null,
direction: 'rtl',
size: '50%'
});
const dialogRef = ref(null); // 弹窗实例
const drawerRef = ref(null); // 抽屉实例
const columns = [
{
prop: "xxx",
label: "预警标题",
},
{
prop: "xxx",
label: "预警类型",
},
{
prop: "xxx",
label: "发送时间",
},
{
prop: "xxx",
label: "生效时间",
},
{
prop: "xxx",
label: "接收时间",
},
{
prop: "xxx",
label: "预警转发时间",
},
{
prop: "xxx",
label: "预警结束时间",
},
{
prop: "xxx",
label: "影响数量",
},
{
prop: "xxx",
label: "响应情况",
},
{
label: "操作",
fixed: "right",
width: 150,
render: (row) => () =>
h("div", { class: "action-btns" }, [
h(
ElButton,
{
type: "primary",
link: true,
onClick: async () => {
},
},
() => "详情"
),
h(
ElButton,
{
type: "primary",
link: true,
style: "margin-left: 10px;",
onClick: async () => {
},
},
() => "结束预警"
),
]),
},
]
// 过滤条件
const filterData = reactive({
title: "",
type: "",
warningLevel: "",
})
// 分页
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
pageSizes: [10, 20, 50],
layout: "prev, pager, next, jumper",
onChange: (page, pageSize) => {
pagination.current = page;
pagination.pageSize = pageSize;
getTableData(filterData);
},
});
// 获取预警列表
const getTableData = async (filterData) => {
try {
const res = await request({
url: '',
method: "GET",
params: {
}
})
} catch (error) {
}
}
// 预警类型选项
const typeOptions = [
{ label: "全部", value: "" },
{ label: "台风预警", value: "台风预警" },
{ label: "暴雨预警", value: "暴雨预警" },
{ label: "寒潮预警", value: "寒潮预警" },
{ label: "大雾预警", value: "大雾预警" },
{ label: "暴雪预警", value: "暴雪预警" },
{ label: "道路结冰预警", value: "道路结冰预警" },
{ label: "雷电预警", value: "雷电预警" },
{ label: "强对流预警", value: "强对流预警" },
];
// 预警级别选项
const warningLevelOptions = [
{ label: "全部", value: "" },
{ label: "红色预警", value: "红色预警" },
{ label: "橙色预警", value: "橙色预警" },
{ label: "黄色预警", value: "黄色预警" },
{ label: "蓝色预警", value: "蓝色预警" },
]
// 打开填报项目弹窗
const openAddDialog = () => {
model.title = '填报项目';
Object.assign(form, INIT_FORM);
model.props = {
form: form,
};
model.content = AddDialog;
model.onCancel = () => {
modelVisible.value = false;
};
model.onConfirm = async () => {
dialogType.value = '';
await dialogRef?.value?.dynamicComponentRef?.formRef.validate().then(() => {
console.log('@@@@@填报项目', form);
})
.catch((err) => {
ElMessage.error('请处理表单中的错误项');
});
};
model.width = "70%"
modelVisible.value = true;
}
export default () => {
const router = useRouter();
const gotoLedgerPage = () => {
router.push({
path: `/ledgerManagement`,
});
};
return {
tableData,
filterData,
typeOptions,
warningLevelOptions,
pagination,
columns,
gotoLedgerPage,
modelVisible,
model,
drawerVisible,
drawer,
dialogRef,
drawerRef,
openAddDialog,
}
}

View File

@ -0,0 +1,57 @@
<template>
<div class="root">
<div class="search-box">
<el-input v-model="script.filterData.title" style="width: 240px; margin-right: 10px" size="large"
placeholder="预警标题" :suffix-icon="Search" />
<el-select v-model="script.filterData.type" style="width: 240px; margin-right: 10px" size="large"
placeholder="预警类型" :suffix-icon="Search" :options="script.typeOptions" clearable />
<el-select v-model="script.filterData.warningLevel" style="width: 240px; margin-right: 10px" size="large"
placeholder="预警级别" :suffix-icon="Search" :options="script.warningLevelOptions" clearable />
</div>
<div class="event-box">
<el-button type="primary" @click="script.gotoLedgerPage">驻地台账</el-button>
<el-button type="primary" @click="script.openAddDialog">填报项目</el-button>
<el-button type="primary" @click="">导入停工项目</el-button>
<el-button type="primary" @click="">导入在建项目台账</el-button>
<el-button type="primary" color="#952DE6" @click="">导出</el-button>
</div>
<DynamicTable :dataSource="script.tableData.value" :columns="script.columns" :autoHeight="true"
:pagination="script.pagination">
</DynamicTable>
<div class="model-box">
<MyDialog v-model="script.modelVisible.value" :title="script.model?.title"
:dynamicComponent="script.model?.content" :component-props="script.model?.props"
:onConfirm="script.model?.onConfirm" :onCancel="script.model?.onCancel" ref="dialogRef"
:width="script.model?.width">
</MyDialog>
<MyDrawer v-model="script.drawerVisible.value" :title="script.drawer?.title"
:dynamicComponent="script.drawer?.content" :component-props="script.drawer?.props"
:onConfirm="script.drawer?.onConfirm" :onCancel="script.drawer?.onCancel" ref="drawerRef"
:direction="script.drawer?.direction" :size="script.drawer?.size">
</MyDrawer>
</div>
</div>
</template>
<script setup>
import DynamicTable from "../../component/DynamicTable";
import { Search } from "@element-plus/icons-vue";
import MyDialog from "../../component/MyDialog";
import MyDrawer from "../../component/MyDrawer";
import scriptFn from "./index.js";
const script = scriptFn();
</script>
<style scoped>
.root {
height: 100%;
padding: 25px;
}
.event-box {
margin: 20px 0;
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -93,7 +93,7 @@ export default defineConfig(({ command, mode }) => {
cors: true,
proxy: {
'/snow-ops-platform': {
target: 'http://8.137.54.85:8661/',
target: 'http://192.168.110.16:8661/',
changeOrigin: true,
},
}