feat(request): 重构 axios 请求并添加新功能

- 创建独立的 request 实例,避免全局 axios 默认配置冲突
- 添加文件上传下载功能
- 新增 get、post 方法的 loading 控制
-优化请求拦截器,统一处理 Token 和 loading
- 重构响应拦截器,统一处理登录失效和请求错误
master
fangjiakai 2025-06-10 18:04:50 +08:00
parent 087daf26a9
commit 4ed118ed74
40 changed files with 4844 additions and 120 deletions

View File

@ -15,5 +15,7 @@
<script type="text/javascript" src="/cesium91/CesiumUnminified/Cesium.js"></script>
<script type="text/javascript" src="/cesium91/CustomCesiumSDK.js"></script>
<link href="/cesium91/CesiumUnminified/Widgets/widgets.css" rel="stylesheet"/>
<script type="text/javascript"
src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=OElqFYoKiAH8KFtph8ftLKF5NlNrbCUr"></script>
</body>
</html>

View File

@ -1,7 +1,9 @@
<template>
<suspense>
<template #default>
<router-view />
<el-config-provider :locale="zhCn">
<router-view />
</el-config-provider>
</template>
<template #fallback>
<div>加载中...</div>
@ -9,6 +11,8 @@
</suspense>
</template>
<script setup></script>
<script setup>
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
</script>
<style lang="scss" scoped></style>

View File

@ -17,102 +17,6 @@
}
}
// 1-50marginpadding
@for $i from 1 through 50 {
.m-#{$i} {
margin: #{$i}px;
}
.mt-#{$i} {
margin-top: #{$i}px;
}
.mr-#{$i} {
margin-right: #{$i}px;
}
.mb-#{$i} {
margin-bottom: #{$i}px;
}
.ml-#{$i} {
margin-left: #{$i}px;
}
.mtb-#{$i} {
margin-top: #{$i}px;
margin-bottom: #{$i}px;
}
.mlr-#{$i} {
margin-left: #{$i}px;
margin-right: #{$i}px;
}
.p-#{$i} {
padding: #{$i}px;
}
.pt-#{$i} {
padding-top: #{$i}px;
}
.pr-#{$i} {
padding-right: #{$i}px;
}
.pb-#{$i} {
padding-bottom: #{$i}px;
}
.pl-#{$i} {
padding-left: #{$i}px;
}
.ptb-#{$i} {
padding-top: #{$i}px;
padding-bottom: #{$i}px;
}
.plr-#{$i} {
padding-left: #{$i}px;
padding-right: #{$i}px;
}
.m--#{$i} {
margin: -#{$i}px;
}
.mt--#{$i} {
margin-top: -#{$i}px;
}
.mr--#{$i} {
margin-right: -#{$i}px;
}
.mb--#{$i} {
margin-bottom: -#{$i}px;
}
.ml--#{$i} {
margin-left: -#{$i}px;
}
.mtb--#{$i} {
margin-top: -#{$i}px;
margin-bottom: -#{$i}px;
}
.mlr--#{$i} {
margin-left: -#{$i}px;
margin-right: -#{$i}px;
}
.p--#{$i} {
padding: -#{$i}px;
}
.pt--#{$i} {
padding-top: -#{$i}px;
}
.pr--#{$i} {
padding-right: -#{$i}px;
}
.pb--#{$i} {
padding-bottom: -#{$i}px;
}
.pl--#{$i} {
padding-left: -#{$i}px;
}
.ptb--#{$i} {
padding-top: -#{$i}px;
padding-bottom: -#{$i}px;
}
.plr--#{$i} {
padding-left: -#{$i}px;
padding-right: -#{$i}px;
}
}
* {
box-sizing: border-box;
font-size: 14px;
@ -145,13 +49,158 @@ a {
color: var(--el-color-primary);
}
.end {
.el-form-item__content {
justify-content: end;
}
}
.p0.el-descriptions__content {
padding: 0 !important;
}
.p0 .el-descriptions tr {
.el-descriptions__label {
border-left: none !important;
}
.el-descriptions__content {
border-right: none !important;
}
&:first-child .el-descriptions__cell {
border-top: none !important;
}
&:last-child .el-descriptions__cell {
border-bottom: none !important;
}
}
.p20 {
padding: 20px;
}
.mt {
margin-top: 16px;
}
.ml {
margin-left: 10px;
}
.mr {
margin-right: 10px;
}
.mb {
margin-bottom: 10px;
}
.text-blue {
color: #0000ff;
}
.text-yellow {
color: #bebe05;
}
.text-orange {
color: #de9004;
}
.text-red {
color: #ff0000;
}
.text-green {
color: #0bb20c;
}
.tc {
text-align: center;
}
.tr {
text-align: right;
}
.tl {
text-align: left;
}
.dn {
display: none;
}
//.print_use {
// display: none;
//}
.viewer-zoom-in, .viewer-zoom-out, .viewer-one-to-one, .viewer-reset, .viewer-prev, .viewer-play, .viewer-next, .viewer-rotate-left, .viewer-rotate-right, .viewer-flip-horizontal, .viewer-flip-vertical, .viewer-fullscreen, .viewer-fullscreen-exit, .viewer-close {
&::before {
font-size: unset !important;
}
}
.iframe{
position: relative;
border: none;
.vue-pdf__wrapper-annotation-layer {
height: 0 !important;
}
.w-e-bar-divider {
display: none !important;
}
//
@page {
size: auto;
margin: 3mm;
}
@media print {
@page {
size: auto;
}
body, html { //如果vue最外层id默认是#app。如果设置了height:100%;#app
height: auto !important;
}
.print_use {
border-collapse: collapse;
width: 100%;
display: table;
td, th {
border: 1px solid #eaeaea;
padding: 8px;
line-height: 1.6;
text-align: center;
color: #000 !important;
}
tr {
color: #000 !important;
}
}
.print_no_use {
display: none;
}
}
.my_primary{
width: calc(100vw - 252px);
background: #ffffff;
padding: 10px;
position: fixed;
left: 231px;
bottom: 0;
box-shadow: 0 0 10px #cccccc;
}
.my_main{
padding-bottom: 50px;
}

View File

@ -0,0 +1,77 @@
.el-select, .el-cascader, .el-date-editor.el-input, .el-date-editor.el-input__wrapper, .el-input__wrapper, .el-input-number {
width: 100% !important;
}
.el-pagination--small .el-select{
width: 100px !important;
}
.el-table .el-table__cell {
text-align: center !important;
}
.el-descriptions__label {
width: 200px;
}
.el-descriptions__content {
width: auto;
}
.el-divider__text {
font-size: 16px !important;
font-weight: 700 !important;
}
.el-form-item__label {
font-weight: 700;
}
.el-dialog {
--el-dialog-margin-top: 50px !important;
.el-dialog__header {
border-bottom: 1px solid #f1f1f1;
}
.el-dialog__body {
padding: 20px 0;
}
.el-dialog__footer {
border-top: 1px solid #f1f1f1;
}
}
.el-table {
* {
font-size: 12px;
}
th.el-table__cell {
--el-table-header-bg-color: rgb(245, 247, 250);
font-weight: bold;
color: rgb(0, 0, 0);
}
.el-table__cell {
padding: 8px 0 !important;
}
}
.el-page-header {
border-bottom: 1px solid #eaeaea;
padding: 0 20px 20px 20px;
margin: 0 -20px 20px -20px;
.el-page-header__content {
font-size: 17px;
}
}
.el-pagination .el-select{
width: 100px !important;
}
.el-scrollbar__wrap{
scroll-behavior: smooth;
}

View File

@ -0,0 +1,52 @@
import {
getDataDictionary,
getDataDictionaryTree,
getDepartmentTree,
} from "@/request/data_dictionary";
import { ref } from "vue";
// 学历
export const layoutFnGetDegree = async () => {
const resData = await getDataDictionary({
parentId: "ddce2eac1cf27e4b114231051ec9123b",
});
return ref(resData.dictionariesList);
};
// 评审模板类型
export const layoutFnGetCheckTemplateType = async () => {
const resData = await getDataDictionary({
parentId: "918ff8ae621595c86ed95d2c77484e1c",
});
return ref(resData.dictionariesList);
};
// 评审周期
export const layoutFnGetReviewCycle = async () => {
const resData = await getDataDictionary({
parentId: "4034603a5ae1042827340bc86fe26454",
});
return ref(resData.dictionariesList);
};
// 无法确定parentId的数据字典
export const layoutFnGetDataDictionary = async (parentId) => {
const resData = await getDataDictionary({ parentId });
return ref(resData.dictionariesList);
};
// 部门
export const layoutFnGetDepartmentTree = async (parentId) => {
const resData = await getDepartmentTree(parentId);
return ref(resData.deptTree);
};
// 企业类型
export const fnGetEnterpriseTypeList = async () => {
const resData = await getDataDictionary({
parentId: "71eb9a7f6fb55afdb8f9b4bea9b3e368",
});
return ref(resData.dictionariesList);
};
// 企业类型
export const fnGetHiddenTypeList = async () => {
const resData = await getDataDictionaryTree({
parentId: "e3af692a2137d4bcf90ec53f5590b176",
});
return ref(resData.dictionariesList);
};

View File

@ -0,0 +1,84 @@
<template>
<el-cascader
ref="cascaderRef"
v-model="modelValue"
:props="administrativeDivisionProps"
@change="change"
/>
</template>
<script setup>
import { layoutFnGetDataDictionary } from "@/assets/js/data_dictionary.js";
import { useVModels } from "@vueuse/core";
import { ref } from "vue";
defineOptions({
name: "LayoutCascader",
});
const props = defineProps({
modelValue: {
type: Array,
default: () => [],
required: true,
},
name: {
type: Array,
default: () => [],
},
level: {
type: [Number, String],
default: 3,
},
checkStrictly: {
type: Boolean,
default: true,
},
dictionariesId: {
type: String,
default: "",
},
isPressLevelSetLeaf: {
type: Boolean,
default: true,
},
});
const emits = defineEmits(["update:modelValue", "update:name"]);
const { modelValue, name } = useVModels(props, emits);
const cascaderRef = ref(null);
const administrativeDivisionProps = {
lazy: true,
lazyLoad: async (node, resolve) => {
const resData = await layoutFnGetDataDictionary(
node.data.dictionariesId || props.dictionariesId
);
resolve(
resData.value.map((item) => {
return {
dictionariesId: item.dictionariesId,
bianma: item.bianma,
name: item.name,
leaf: props.isPressLevelSetLeaf
? node.level >= props.level
: item.hasChildren === 0,
};
})
);
},
value: "bianma",
id: "dictionariesId",
label: "name",
children: "children",
checkStrictly: props.checkStrictly,
};
const change = () => {
name.value = getCheckedNodes()[0].pathLabels;
};
const getCheckedNodes = () => {
return cascaderRef.value.getCheckedNodes();
};
defineExpose({
getCheckedNodes,
});
</script>
<style scoped></style>

View File

@ -0,0 +1,71 @@
<template>
<el-col :span="24">
<div style="display: flex">
<el-form-item
label="经度"
:prop="longitudeProps"
:rules="longitudeRules"
style="flex: 1"
>
<el-input :model-value="longitude" disabled />
</el-form-item>
<el-form-item
label="纬度"
:prop="latitudeProps"
:rules="latitudeRules"
style="flex: 1"
>
<el-input :model-value="latitude" disabled />
</el-form-item>
<el-form-item v-if="!disabled" label-width="10px">
<el-button class="ml-10" type="primary" @click="mapVisible = true">
点击定位
</el-button>
</el-form-item>
</div>
</el-col>
<app-map
v-model:visible="mapVisible"
v-model:longitude="longitude"
v-model:latitude="latitude"
/>
</template>
<script setup>
import { ref } from "vue";
import AppMap from "@/components/map/map.vue";
defineProps({
longitudeProps: {
type: String,
default: "LONGITUDE",
},
latitudeProps: {
type: String,
default: "LATITUDE",
},
longitudeRules: {
type: Object,
default: () => ({}),
},
latitudeRules: {
type: Object,
default: () => ({}),
},
disabled: {
type: Boolean,
default: false,
},
});
const longitude = defineModel("longitude", {
type: [String, Number],
required: true,
});
const latitude = defineModel("latitude", {
type: [String, Number],
required: true,
});
const mapVisible = ref(false);
</script>
<style scoped lang="scss"></style>

131
src/components/map/map.vue Normal file
View File

@ -0,0 +1,131 @@
<template>
<el-dialog v-model="visible" title="编辑坐标">
<el-form label-position="right" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="关键字搜索">
<el-input v-model="localSearch" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label-width="10px">
<el-button type="primary" @click="fnLocalSearch"></el-button>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="坐标">
<el-input disabled :model-value="currentLongitude || longitude" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label-width="10px">
<el-input disabled :model-value="currentLatitude || latitude" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div
v-loading="loading"
element-loading-text="地图正在加载中..."
element-loading-background="rgba(0, 0, 0, 0.5)"
>
<div id="map_container" style="width: 100%; height: 500px" />
</div>
<template #footer>
<el-button @click="fnClose"></el-button>
<el-button type="primary" @click="fnConfirm"> </el-button>
</template>
</el-dialog>
</template>
<script setup>
import { nextTick, ref, watch } from "vue";
defineOptions({
name: "AppMap",
});
let mapInstance;
const visible = defineModel("visible", { type: Boolean, required: true });
const longitude = defineModel("longitude", {
type: [Number, String],
required: true,
});
const latitude = defineModel("latitude", {
type: [Number, String],
required: true,
});
const loading = ref(false);
const currentLongitude = ref("");
const currentLatitude = ref("");
const localSearch = ref("");
const emits = defineEmits(["submit"]);
const fnMapInit = async () => {
loading.value = true;
await nextTick();
mapInstance = new window.BMapGL.Map("map_container");
mapInstance.centerAndZoom(
new window.BMapGL.Point(
longitude.value || "116.3972282409668",
latitude.value || "39.90960456049752"
),
16
);
mapInstance.enableScrollWheelZoom(true);
loading.value = false;
if (longitude.value && latitude.value) {
const point = new window.BMapGL.Point(longitude.value, latitude.value);
const marker = new window.BMapGL.Marker(point);
mapInstance.addOverlay(marker);
}
mapInstance.addEventListener("click", function (event) {
mapInstance.clearOverlays();
const point = new window.BMapGL.Point(event.latlng.lng, event.latlng.lat);
const marker = new window.BMapGL.Marker(point);
mapInstance.addOverlay(marker);
currentLatitude.value = event.latlng.lat;
currentLongitude.value = event.latlng.lng;
});
};
const fnLocalSearch = () => {
if (localSearch.value) {
const local = new window.BMapGL.LocalSearch(mapInstance, {
renderOptions: { map: mapInstance },
});
local.search(localSearch.value);
}
};
const fnClose = () => {
if (mapInstance) {
mapInstance.destroy();
mapInstance = null;
}
currentLatitude.value = "";
currentLongitude.value = "";
localSearch.value = "";
visible.value = false;
};
const fnConfirm = () => {
latitude.value = currentLatitude.value;
longitude.value = currentLongitude.value;
emits("submit", {
latitude: currentLatitude.value,
longitude: currentLongitude.value,
});
fnClose();
};
watch(
() => visible.value,
(val) => {
if (val && !mapInstance) {
fnMapInit();
}
},
{
immediate: true,
}
);
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,37 @@
<template>
<el-pagination
small
:current-page="pagination.currentPage || 1"
:page-size="pagination.pageSize || 10"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total || 0"
@update:current-page="handleCurrentChange"
@update:page-size="handleSizeChange"
/>
</template>
<script setup>
defineOptions({
name: "AppPagination",
});
const emits = defineEmits(["get-data"]);
const pagination = defineModel("pagination", { type: Object, required: true });
const handleCurrentChange = (val) => {
pagination.value = {
currentPage: val,
pageSize: pagination.value.pageSize,
total: pagination.value.total,
};
emits("get-data");
};
const handleSizeChange = (val) => {
pagination.value = {
currentPage: 1,
pageSize: val,
total: pagination.value.total,
};
emits("get-data");
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,62 @@
<template>
<el-form
:model="modelValue"
:label-width="labelWidth"
@submit.prevent="emits('submit', 'search')"
>
<el-row :class="className">
<slot :collapse="collapse"></slot>
<el-col :span="showCollapseButton ? (collapse ? 6 : span) : span">
<el-form-item label-width="10px" class="end">
<el-button type="primary" native-type="submit"> 搜索 </el-button>
<el-button native-type="reset" @click="emits('submit', 'reset')">
重置
</el-button>
<app-search-collapse-button
v-if="showCollapseButton"
:change-search-collapse="changeSearchCollapse"
:collapse="collapse"
/>
</el-form-item>
</el-col>
<el-col v-if="slots.button" :span="24">
<el-form-item label-width="0">
<slot name="button"></slot>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script setup>
import AppSearchCollapseButton from "@/components/search_collapse_button/index.vue";
import useSearchCollapse from "@/hooks/useSearchCollapse.js";
import { onMounted, ref, useSlots } from "vue";
import { uniqueId } from "lodash-es";
const slots = useSlots();
const { collapse, changeSearchCollapse } = useSearchCollapse();
defineOptions({
name: "AppSearch",
});
defineProps({
labelWidth: {
type: String,
default: "100px",
},
});
const className = ref(uniqueId("_"));
const span = ref(6);
const showCollapseButton = ref(false);
const modelValue = defineModel({ type: Object, required: true });
const emits = defineEmits(["submit"]);
onMounted(() => {
const colEl = document.querySelectorAll(`.${className.value} .el-col`);
const colElLength = colEl.length;
const excludeLast = colElLength - (slots.button ? 2 : 1);
span.value = { 0: 24, 1: 18, 2: 12, 3: 6 }[excludeLast % 4];
showCollapseButton.value = excludeLast > 3;
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,42 @@
<template>
<el-button
v-if="collapse"
:icon="ArrowDown"
link
text
type="primary"
@click="changeSearchCollapse"
>
展开
</el-button>
<el-button
v-if="!collapse"
:icon="ArrowUp"
link
text
type="primary"
@click="changeSearchCollapse"
>
收起
</el-button>
</template>
<script setup>
import { ArrowDown, ArrowUp } from "@element-plus/icons-vue";
defineOptions({
name: "AppSearchCollapseButton",
});
defineProps({
collapse: {
type: Boolean,
required: true,
},
changeSearchCollapse: {
type: Function,
required: true,
},
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,196 @@
<template>
<el-table
ref="tableRef"
size="small"
:data="data"
:border="border"
:stripe="stripe"
:height="height"
:max-height="maxHeight"
:highlight-current-row="highlightCurrentRow"
:row-key="getRowKey"
:row-class-name="rowClassName"
:row-style="rowStyle"
:show-header="showHeader"
:show-summary="showSummary"
:summary-method="summaryMethod"
:span-method="spanMethod"
:default-expand-all="defaultExpandAll"
:tree-props="treeProps"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
:show-overflow-tooltip="showOverflowTooltip"
style="width: 100%"
@row-click="rowClick"
@row-dblclick="rowDblclick"
>
<el-table-column
v-if="showSelection"
type="selection"
:selectable="selectable"
reserve-selection
width="60"
:show-overflow-tooltip="false"
/>
<template v-if="showIndex">
<el-table-column v-if="showPagination" label="序号" width="60">
<template #default="{ $index }">
{{ serialNumber(pagination, $index) }}
</template>
</el-table-column>
<el-table-column
v-if="!showPagination"
label="序号"
width="60"
type="index"
/>
</template>
<slot></slot>
</el-table>
<div v-if="showPagination || slots.button" class="table_footer">
<div>
<slot name="button"></slot>
</div>
<app-pagination
v-if="showPagination"
v-model:pagination="pagination"
@get-data="emits('get-data')"
/>
</div>
</template>
<script setup>
import { useSlots, ref } from "vue";
import AppPagination from "@/components/pagination/index.vue";
import { serialNumber } from "@/assets/js/utils.js";
const slots = useSlots();
defineOptions({
name: "AppTable",
});
const props = defineProps({
data: {
type: Array,
default: () => [],
},
showPagination: {
type: Boolean,
default: true,
},
showIndex: {
type: Boolean,
default: true,
},
showSelection: {
type: Boolean,
default: false,
},
stripe: {
type: Boolean,
default: true,
},
border: {
type: Boolean,
default: true,
},
showHeader: {
type: Boolean,
default: true,
},
highlightCurrentRow: {
type: Boolean,
default: false,
},
showSummary: {
type: Boolean,
default: false,
},
defaultExpandAll: {
type: Boolean,
default: false,
},
rowKey: {
type: [String, Function],
},
maxHeight: {
type: [String, Number],
},
height: {
type: [String, Number],
},
rowClassName: {
type: Function,
},
rowStyle: {
type: Function,
},
summaryMethod: {
type: Function,
},
spanMethod: {
type: Function,
},
selectable: {
type: Function,
},
treeProps: {
type: Object,
default: () => ({ hasChildren: "hasChildren", children: "children" }),
},
headerCellStyle: {
type: Object,
default: () => ({}),
},
cellStyle: {
type: [Object, Function],
default: () => ({}),
},
showOverflowTooltip: {
type: Boolean,
default: true,
},
});
const pagination = defineModel("pagination", {
type: Object,
default: () => ({
currentPage: 1,
pageSize: 10,
total: 0,
}),
});
const emits = defineEmits(["get-data", "row-click", "row-dblclick"]);
const tableRef = ref(null);
const getRowKey = (row) => {
if (!props.rowKey) return;
if (typeof props.rowKey === "string") return row[props.rowKey];
else return props.rowKey(row);
};
const rowClick = (row, column, event) => {
emits("row-click", row, column, event);
};
const rowDblclick = (row, column, event) => {
emits("row-dblclick", row, column, event);
};
const getSelectionRows = () => {
return tableRef.value.getSelectionRows();
};
const clearSelection = () => {
return tableRef.value.clearSelection();
};
const toggleRowSelection = (value, selected = true) => {
tableRef.value.toggleRowSelection(value, selected);
};
defineExpose({
getSelectionRows,
clearSelection,
toggleRowSelection,
});
</script>
<style lang="scss" scoped>
.table_footer {
margin-top: 20px;
display: flex;
justify-content: space-between;
}
</style>

160
src/hooks/useListData.js Normal file
View File

@ -0,0 +1,160 @@
import { nextTick, ref } from "vue";
import { getDataType } from "@/assets/js/utils.js";
import {
getQueryCriteria,
setQueryCriteria,
} from "@/hooks/useQueryCriteria.js";
const verificationParameter = (api, options) => {
if (getDataType(api) !== "Function") throw new Error("api必须是一个函数");
if (getDataType(options) !== "Object")
throw new Error("options必须是一个对象");
if (options.immediate && getDataType(options.immediate) !== "Boolean")
throw new Error("options.immediate必须是一个布尔值");
if (options.usePagination && getDataType(options.usePagination) !== "Boolean")
throw new Error("options.usePagination必须是一个布尔值");
if (options.key && getDataType(options.key) !== "String")
throw new Error("options.key必须是一个字符串");
if (
options.callback &&
getDataType(options.callback) !== "Function" &&
getDataType(options.callback) !== "AsyncFunction"
)
throw new Error("options.callback必须是一个函数");
if (
options.beforeGetData &&
getDataType(options.beforeGetData) !== "Function" &&
getDataType(options.beforeGetData) !== "AsyncFunction"
)
throw new Error("options.beforeGetData必须是一个函数");
if (
options.defaultSearchForm &&
getDataType(options.defaultSearchForm) !== "Object"
)
throw new Error("options.defaultSearchForm必须是一个对象");
if (
options.clearSelection &&
getDataType(options.clearSelection) !== "Boolean"
)
throw new Error("options.clearSelection必须是一个布尔值");
if (
options.params &&
getDataType(options.params) !== "Object" &&
getDataType(options.params) !== "Function"
)
throw new Error("options.params必须是一个对象或者一个函数");
if (
options.isStorageQueryCriteria &&
getDataType(options.isStorageQueryCriteria) !== "Boolean"
)
throw new Error("options.isStorageQueryCriteria必须是一个布尔值");
if (
options.tabsActiveName &&
getDataType(options.tabsActiveName) !== "String"
)
throw new Error("options.tabsActiveName必须是一个字符串");
};
const getOptionParams = (params) => {
if (params) {
if (getDataType(params) === "Object") return params;
const paramsValue = params();
if (getDataType(paramsValue) !== "Object")
throw new Error("options.params为函数时必须存在返回值并且必须是一个对象");
else return paramsValue;
}
};
/**
* @param api {Function} 接口函数
* @param options {Object?: {callback, params, defaultSearchForm, immediate, usePagination, key, beforeGetData}} 配置项
* @param options.callback {Function?} 回调函数返回值第一个参数表格数据第二个参数后台返回的所有数据
* @param options.beforeGetData {Function?} 调用请求之前返回值第一个参数searchForm搜索表单数据
* @param options.params {(Object | Function)?} 其它接口参数
* @param options.defaultSearchForm {Object?} searchForm默认值
* @param options.immediate {Boolean?} 是否立即执行接口函数默认是
* @param options.usePagination {Boolean?} 是否使用分页默认是
* @param options.clearSelection {Boolean?} 调用resetPagination时是否清空表格选择数据默认是
* @param options.key {String?} 返回的存放数组的key默认list
* @param options.isStorageQueryCriteria {Boolean?} 是否保存查询条件默认是
* @param options.tabsActiveName {String?} 存在tabs组件时当前tabs的activeName用于缓存查询条件
* @return {Object} 返回对象包含以下属性list 表格数据pagination 分页数据searchForm 搜索表单数据tableRef 表格实例getData 获取数据函数resetPagination 重置分页函数
*/
export default function useListData(api, options = {}) {
verificationParameter(api, options);
const immediate = options.immediate ?? true;
const usePagination = options.usePagination ?? true;
const key = options.key ?? "list";
const defaultSearchForm = options.defaultSearchForm ?? {};
const clearSelection = options.clearSelection ?? true;
const isStorageQueryCriteria = options.isStorageQueryCriteria ?? true;
const defaultPagination = { currentPage: 1, pageSize: 10, total: 0 };
const list = ref([]);
const queryCriteria = getQueryCriteria();
const pagination = ref(queryCriteria.pagination || defaultPagination);
const searchForm = ref(JSON.parse(JSON.stringify(defaultSearchForm)));
let beforeGetDataParams = {};
const tableRef = ref(null);
const getData = async (params = {}) => {
if (options.beforeGetData) {
beforeGetDataParams = JSON.parse(
JSON.stringify(queryCriteria.searchForm || searchForm.value)
);
options.beforeGetData(beforeGetDataParams);
}
const resData = await api({
...(usePagination
? {
curPage: pagination.value.currentPage,
limit: pagination.value.pageSize,
}
: {}),
...searchForm.value,
...beforeGetDataParams,
...(queryCriteria.searchForm || {}),
...(getOptionParams(options.params) || {}),
...(getDataType(params) === "Object" ? params : {}),
});
if (usePagination) list.value = resData.page[key];
else list.value = resData[key];
if (usePagination) pagination.value.total = resData.page.totalCount;
options.callback && options.callback(list.value, resData);
!usePagination &&
clearSelection &&
tableRef.value &&
tableRef.value.clearSelection();
if (isStorageQueryCriteria) {
setQueryCriteria({
searchForm: {
...(queryCriteria.searchForm || {}),
...searchForm.value,
},
pagination: pagination.value,
tabsActiveName: options.tabsActiveName,
});
await nextTick();
searchForm.value = queryCriteria.searchForm || searchForm.value;
}
};
immediate && getData().then();
const resetPagination = async (params) => {
list.value = [];
pagination.value = defaultPagination;
await nextTick();
await getData(params);
clearSelection && tableRef.value && tableRef.value.clearSelection();
const cloneSearchForm = searchForm.value;
searchForm.value = {};
await nextTick();
searchForm.value = cloneSearchForm;
};
return {
list,
pagination,
searchForm,
tableRef,
getData: async (params) => await getData(params),
resetPagination: async (params) => await resetPagination(params),
};
}

View File

@ -0,0 +1,13 @@
import { ref } from "vue";
const useSearchCollapse = () => {
const collapse = ref(true);
const changeSearchCollapse = () => {
collapse.value = !collapse.value;
};
return {
collapse,
changeSearchCollapse,
};
};
export default useSearchCollapse;

View File

@ -1,6 +1,7 @@
import { createApp } from "vue";
import "@/assets/css/common.scss";
import "@/assets/css/transition.scss";
import "@/assets/css/element.scss";
import "dayjs/locale/zh-cn";
import App from "./App";
import pinia from "./pinia";

View File

@ -4,7 +4,7 @@ import router from "../router";
import pinia from "../pinia";
import { useUserStore } from "@/pinia/user.js";
import refreshToken from "@/assets/js/refreshToken.js";
import { getBaseUrl } from "@/assets/js/utils.js";
import { getDataType } from "@/assets/js/utils.js";
let loading = null;
let isTipTokenFailure = false;
@ -20,10 +20,16 @@ function endLoading() {
loading && loading.close();
}
axios.defaults.baseURL = getBaseUrl();
axios.defaults.timeout = 1000 * 60 * 10;
const request = axios.create({
baseURL: import.meta.env.VITE_BASE_URL,
timeout: 1000 * 60 * 10,
// headers: {
// 'Content-type': 'application/x-www-form-urlencoded;charset=UTF-8'
// }
});
// axios.defaults.withCredentials = true;
axios.interceptors.request.use(
request.interceptors.request.use(
async (config) => {
const userStore = useUserStore(pinia);
config.headers.Token = userStore.getToken;
@ -38,13 +44,13 @@ axios.interceptors.request.use(
(error) => Promise.reject(error)
);
axios.interceptors.response.use(
request.interceptors.response.use(
(config) => {
if (config.config.method === "get" || config.config.method === "GET") {
if (config.config.params.loading !== false) endLoading();
}
if (config.config.method === "post" || config.config.method === "POST") {
if (config.config.headers["Content-Type"] === "multipart/form-data") {
if (getDataType(config.config.data) === "FormData") {
endLoading();
} else {
if (JSON.parse(config.config.data)?.loading !== false) endLoading();
@ -53,7 +59,7 @@ axios.interceptors.response.use(
if (config.data.code === 401) {
if (!isTipTokenFailure) {
isTipTokenFailure = true;
ElMessage.error("登录失效,请重新登");
ElMessage.error("登录失效,请重新登");
router.push("/login").then();
isTipTokenFailure = false;
}
@ -76,10 +82,10 @@ axios.interceptors.response.use(
}
);
export function post(url, params = {}) {
export function post(url, params) {
const userStore = useUserStore(pinia);
return new Promise((resolve, reject) => {
axios
request
.post(url, {
corpinfoId: userStore.getUserInfo.corpinfoId,
userId: userStore.getUserInfo.userId,
@ -99,16 +105,14 @@ export function post(url, params = {}) {
});
}
export function get(url, params = {}) {
export function get(url, params) {
const userStore = useUserStore(pinia);
return new Promise((resolve, reject) => {
axios
request
.get(url, {
params: {
corpinfoId: userStore.getUserInfo.corpinfoId,
userId: userStore.getUserInfo.userId,
...params,
},
corpinfoId: userStore.getUserInfo.corpinfoId,
userId: userStore.getUserInfo.userId,
...params,
})
.then((res) => {
if (res.data.result === "success") {
@ -124,9 +128,9 @@ export function get(url, params = {}) {
});
}
export function upload(url, params = {}) {
export function upload(url, params) {
return new Promise((resolve, reject) => {
axios
request
.post(url, params, {
headers: {
"Content-Type": "multipart/form-data",

View File

@ -0,0 +1,21 @@
import { post } from "./axios";
// 获取部门
export const getDepartmentTree = (parentId = "0") =>
post("/oa/department/getTree", {
loading: false,
parentId,
});
// 获取数据字典
export const getDataDictionary = (params) =>
post("/sys/dictionaries/list", {
loading: false,
...params,
});
// 获取数据字典树
export const getDataDictionaryTree = (params) =>
post("/sys/dictionaries/listTree", {
loading: false,
...params,
});

91
src/request/kangzai.js Normal file
View File

@ -0,0 +1,91 @@
import { post } from "./axios";
/*
* 应急储备库信息表
*/
export const getEmergencyStoragePage = (params) =>
post("/busEmergencyStorage/listPage", params); // 获取应急储备库信息表
export const setEmergencyStorageDelete = (params) =>
post("/busEmergencyStorage/delete", params); // 获取应急储备库信息表
export const setEmergencyStorageAdd = (params) =>
post("/busEmergencyStorage/save", params);
export const setEmergencyStorageUpdate = (params) =>
post("/busEmergencyStorage/update", params);
/*
* 山洪村信息
*/
export const getMountainFloodVillagePage = (params) =>
post("/busMountainFloodVillage/listPage", params); // 获取山洪村信息表
export const setMountainFloodVillageDelete = (params) =>
post("/busMountainFloodVillage/delete", params);
export const setMountainFloodVillageAdd = (params) =>
post("/busMountainFloodVillage/save", params);
export const setMountainFloodVillageUpdate = (params) =>
post("/busMountainFloodVillage/update", params);
/*
*城市防涝点
*/
export const getUrbanFloodPointPage = (params) =>
post("/busUrbanFloodPoint/listPage", params);
export const setUrbanFloodPointDelete = (params) =>
post("/busUrbanFloodPoint/delete", params);
export const setUrbanFloodPointAdd = (params) =>
post("/busUrbanFloodPoint/save", params);
export const setUrbanFloodPointUpdate = (params) =>
post("/busUrbanFloodPoint/update", params);
/*
*地质灾害点信息
*/
export const getGeologicalDisasterPage = (params) =>
post("/busGeologicalDisaster/listPage", params);
export const setGeologicalDisasterDelete = (params) =>
post("/busGeologicalDisaster/delete", params);
export const setGeologicalDisasterAdd = (params) =>
post("/busGeologicalDisaster/save", params);
export const setGeologicalDisasterUpdate = (params) =>
post("/busGeologicalDisaster/update", params);
/*
* 水库信息BusReservoirBasicEntity
*/
export const getReservoirBasicPage = (params) =>
post("/busReservoirBasic/listPage", params);
export const setReservoirBasicDelete = (params) =>
post("/busReservoirBasic/delete", params);
export const setReservoirBasicAdd = (params) =>
post("/busReservoirBasic/save", params);
export const setReservoirBasicUpdate = (params) =>
post("/busReservoirBasic/update", params);
/*
* 河流 BusRiverEntity
*/
export const getRiverPage = (params) => post("/busRiver/listPage", params);
export const setRiverDelete = (params) => post("/busRiver/delete", params);
export const setRiverAdd = (params) => post("/busRiver/save", params);
export const setRiverUpdate = (params) => post("/busRiver/update", params);
/*
* 河道行洪隐患区村庄清单 BusRiverRiskVillagesEntity
*/
export const getRiverRiskVillagePage = (params) =>
post("/busRiverRiskVillages/listPage", params);
export const setRiverRiskVillageDelete = (params) =>
post("/busRiverRiskVillages/delete", params);
export const setRiverRiskVillageAdd = (params) =>
post("/busRiverRiskVillages/save", params);
export const setRiverRiskVillageUpdate = (params) =>
post("/busRiverRiskVillages/update", params);
/*
* 河流县区段信息 BusRiverSectionsEntity
*/
export const setRiverSectionsListPage = (params) =>
post("/busRiverSections/listPage", params);
export const setRiverSectionsDelete = (params) =>
post("/busRiverSections/delete", params);
export const setRiverSectionsAdd = (params) =>
post("/busRiverSections/save", params);
export const setRiverSectionsUpdate = (params) =>
post("/busRiverSections/update", params);

View File

@ -0,0 +1,47 @@
import { post } from "./axios";
export const getScheduleJobList = (params) =>
post("/sys/schedule/list", {
loading: false,
...params,
});
export const getScheduleJobInfo = (params) =>
post("/sys/schedule/info", {
loading: false,
...params,
});
export const addScheduleJob = (params) =>
post("/sys/schedule/save", {
loading: false,
...params,
});
export const updateScheduleJob = (params) =>
post("/sys/schedule/update", {
loading: false,
...params,
});
export const deleteScheduleJob = (params) =>
post("/sys/schedule/delete", {
loading: false,
...params,
});
export const pauseScheduleJob = (params) =>
post("/sys/schedule/pause", {
loading: false,
...params,
});
export const resumeScheduleJob = (params) =>
post("/sys/schedule/resume", {
loading: false,
...params,
});
export const runScheduleJob = (params) =>
post("/sys/schedule/run", {
loading: false,
...params,
});
export const getPayCode = (params) =>
post("/pay/getPayCode", {
loading: false,
...params,
});

View File

@ -0,0 +1,27 @@
import { post } from "@/request/axios";
export const getRoleList = (params) => post("/sys/role/listPage", params); // 角色管理列表
export const getRoleListAll = (params) => post("/sys/role/listAll", params); // 角色管理列表所有
export const setRoleDelete = (params) => post("/sys/role/delete", params); // 角色管理删除
export const setRoleAdd = (params) => post("/sys/role/save", params); // 角色管理添加
export const setRoleEdit = (params) => post("/sys/role/update", params); // 角色管理修改
export const getRoleView = (params) => post("/sys/role/info", params); // 角色管理查看
export const getDataDictionaryList = (params) =>
post("/sys/dictionaries/listPage", params); // 数据字典列表
export const setDataDictionaryDelete = (params) =>
post("/sys/dictionaries/delete", params); // 数据字典删除
export const setDataDictionaryAdd = (params) =>
post("/sys/dictionaries/save", params); // 数据字典添加
export const setDataDictionaryEdit = (params) =>
post("/sys/dictionaries/update", params); // 数据字典修改
export const getDataDictionaryInfo = (params) =>
post("/sys/dictionaries/info", params); // 数据字典查看
export const getDataDictionaryRepeat = (params) =>
post("/sys/dictionaries/findByBianma", params); // 数据字典验证编码是否重复
export const getRouteList = (params) => post("/sys/menu/list", params); // 菜单管理列表
export const getRouteView = (params) => post("/sys/menu/info", params); // 菜单管理查看
export const setRouteAdd = (params) => post("/sys/menu/save", params); // 菜单管理添加
export const setRouteEdit = (params) => post("/sys/menu/update", params); // 菜单管理修改
export const setRouteDelete = (params) => post("/sys/menu/delete", params); // 菜单管理删除
export const setRouteIcon = (params) => post("/sys/menu/icon", params); // 菜单管理修改图标
export const getLogsListPage = (params) => post("/sys/log/list", params);

View File

@ -0,0 +1,28 @@
import { post, upload } from "@/request/axios";
export const getDepartmentList = (params) =>
post("/oa/department/listPage", params); // 部门管理列表
export const setDepartmentDelete = (params) =>
post("/oa/department/delete", params); // 部门管理删除
export const setDepartmentAdd = (params) => post("/oa/department/save", params); // 部门管理添加
export const setDepartmentEdit = (params) =>
post("/oa/department/update", params); // 部门管理修改
export const getDepartmentView = (params) =>
post("/oa/department/info", params); // 部门管理查看
export const getJobList = (params) => post("/oa/post/listPage", params); // 岗位管理列表
export const getJobListAll = (params) => post("/oa/post/listAll", params); // 岗位管理列表所有
export const setJobDelete = (params) => post("/oa/post/delete", params); // 岗位管理删除
export const getJobView = (params) => post("/oa/post/info", params); // 岗位管理查看
export const setJobAdd = (params) => post("/oa/post/save", params); // 岗位管理添加
export const setJobEdit = (params) => post("/oa/post/update", params); // 岗位管理修改
export const getUserList = (params) => post("/sys/user/list", params); // 用户管理列表
export const getUserListAll = (params) => post("sys/user/listAll", params); // 用户管理列表所有
export const setUserDelete = (params) => post("/sys/user/delete", params); // 用户管理删除
export const setUserResetPassword = (params) =>
post("/sys/user/resetPassword", params); // 用户管理重置密码
export const getUserView = (params) => post("/sys/user/getInfo", params); // 用户管理查看
export const getUserUserNameRepeat = (params) =>
post("/sys/user/hasUser", params); // 用户管理用户名去重
export const setUserAdd = (params) => upload("/sys/user/save", params); // 用户管理添加
export const setUserEdit = (params) => upload("/sys/user/update", params); // 用户管理修改
export const setUserExercisesImport = (params) =>
upload("/sys/user/readExcel", params); // 人员导入

View File

@ -12,7 +12,7 @@ const routes = [
{
path: "/",
name: "app",
redirect: "/map",
redirect: "/login",
meta: { isLogin: true },
component: children,
children: [
@ -49,6 +49,109 @@ const routes = [
meta: { title: "首页" },
component: () => import("@/views/index/index"),
},
{
path: "/river",
name: "/river",
meta: {
title: "河流信息",
breadcrumb: true,
isMenu: true,
isSubMenu: false,
},
component: () => import("@/views/river/index"),
},
{
path: "/river/risk_village",
name: "/river/risk_village",
meta: {
title: "河道行洪隐患区村庄",
breadcrumb: true,
isMenu: false,
isSubMenu: true,
isBack: true,
activeMenu: "/river",
},
component: () => import("@/views/river/river_risk_village/index"),
},
{
path: "/river/river_section",
name: "/river/river_section",
meta: {
title: "河道流经县区",
breadcrumb: true,
isMenu: false,
isSubMenu: true,
isBack: true,
activeMenu: "/river",
},
component: () => import("@/views/river/river_section/index"),
},
{
path: "/reservoir_info",
name: "/reservoir_info",
meta: {
title: "水库信息",
breadcrumb: true,
isMenu: true,
isSubMenu: false,
},
component: () => import("@/views/reservoir_info/index"),
},
{
path: "/geological_disaster",
name: "/geological_disaster",
meta: {
title: "地质灾害点信息",
breadcrumb: true,
isMenu: true,
isSubMenu: false,
},
component: () => import("@/views/geological_disaster/index"),
},
{
path: "/urban_flood_point",
name: "/urban_flood_point",
meta: {
title: "城市防涝点详情",
breadcrumb: true,
isMenu: true,
isSubMenu: false,
},
component: () => import("@/views/urban_flood_point/index"),
},
{
path: "/mountain_flood_info",
name: "/mountain_flood_info",
meta: {
title: "山洪灾害村",
breadcrumb: true,
isMenu: true,
isSubMenu: false,
},
component: () => import("@/views/mountain_flood_info/index"),
},
{
path: "/emergency_storage",
name: "/emergency_storage",
meta: {
title: "应急储备库信息",
breadcrumb: true,
isMenu: true,
isSubMenu: false,
},
component: () => import("@/views/emergency_storage/index"),
},
{
path: "/system_management/data_dictionary",
name: "/system_management/data_dictionary",
meta: {
title: "数据字典",
breadcrumb: true,
isMenu: true,
isSubMenu: false,
},
component: () => import("@/views/system_management/data_dictionary/index"),
},
],
},
],

View File

@ -0,0 +1,200 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'edit' ? '修改' : '新增'"
:before-close="fnClose"
>
<el-form
ref="formRef"
:model="form"
:rules="data.rules"
label-width="100px"
style="margin-top: 20px"
>
<el-row>
<el-col :span="24">
<el-form-item label="应急库名称" prop="storageName">
<el-input
v-model="form.storageName"
placeholder="请输入应急库名称"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="属地" prop="area">
<layout-cascader
v-model="form.area"
v-model:name="form.areaName"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="false"
:level="4"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="应急库类型" prop="storageType">
<el-select
v-model="form.storageType"
placeholder="请选择应急库类型"
filterable
>
<el-option
v-for="item in storageTypeList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="管理单位" prop="managementUnit">
<el-input
v-model="form.managementUnit"
placeholder="请输入应急库名称"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<div style="display: flex">
<el-form-item label="经度" prop="longitude" style="flex: 1">
<el-input v-model="form.longitude" placeholder="请选择经度" />
</el-form-item>
<el-form-item label="纬度" prop="latitude" style="flex: 1">
<el-input v-model="form.latitude" placeholder="请选择纬度" />
</el-form-item>
<el-form-item label-width="10px">
<el-button
class="ml-10"
type="primary"
@click="fnSelectedPosition(form)"
>
点击定位
</el-button>
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="fnClose"> </el-button>
<el-button type="primary" @click="fnSubmit"> </el-button>
</template>
<app-map
v-model:visible="mapDialog.visible"
v-model:longitude="mapDialog.longitude"
v-model:latitude="mapDialog.latitude"
@submit="fnMapSubmit"
/>
</el-dialog>
</template>
<script setup>
import { debounce } from "throttle-debounce";
import useFormValidate from "@/hooks/useFormValidate.js";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import { ElMessage } from "element-plus";
import { reactive, ref } from "vue";
import { useVModels } from "@vueuse/core";
import { getDataDictionary } from "@/request/data_dictionary.js";
import AppMap from "@/components/map/map.vue";
import {
setEmergencyStorageAdd,
setEmergencyStorageUpdate,
} from "@/request/kangzai.js";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
type: {
type: String,
required: true,
default: "",
},
form: {
type: Object,
required: true,
default: () => ({}),
},
});
const { validate, formRef } = useFormValidate();
const storageTypeList = ref([]);
const fnGetStorageTypeList = async () => {
const resData = await getDataDictionary({
parentId: "j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y",
});
storageTypeList.value = resData.dictionariesList;
};
fnGetStorageTypeList();
const emits = defineEmits(["update:visible", "update:form", "getData"]);
const { visible, form } = useVModels(props, emits);
const data = reactive({
rules: {
storageName: [
{ required: true, message: "请输入应急库名称", trigger: "blur" },
],
area: [{ required: true, message: "请输入应急库区域", trigger: "blur" }],
longitude: [{ required: true, message: "请输入经度", trigger: "blur" }],
latitude: [{ required: true, message: "请输入纬度", trigger: "blur" }],
},
});
const mapDialog = ref({
visible: false,
longitude: "",
latitude: "",
});
const fnSelectedPosition = ({ longitude, latitude }) => {
mapDialog.value.visible = true;
mapDialog.value.longitude = longitude;
mapDialog.value.latitude = latitude;
};
const fnMapSubmit = ({ longitude, latitude }) => {
form.value.longitude = longitude;
form.value.latitude = latitude;
};
const fnClose = () => {
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await validate();
const form = props.form;
const param = {
...form,
areaName: form.areaName.join("-"),
province: form.area[0],
city: form.area[1],
county: form.area[2],
village: form.area[3],
street: form.area[4],
};
delete param.area;
if (props.type === "add") {
await setEmergencyStorageAdd({
...param,
});
}
if (props.type === "edit")
await setEmergencyStorageUpdate({
...param,
});
ElMessage.success("操作成功");
fnClose();
emits("getData");
},
{ atBegin: true }
);
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,167 @@
<template>
<div>
<app-search v-model="searchForm" @submit="resetPagination">
<el-col :span="6">
<el-form-item label="应急库名称" prop="storageName">
<el-input
v-model="searchForm.storageName"
placeholder="请输入应急库名称"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="属地" prop="area">
<layout-cascader
v-model="searchForm.area"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="true"
:level="5"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="应急库类型" prop="storageType">
<el-select
v-model="searchForm.storageType"
placeholder="请选择矿山类别"
filterable
>
<el-option
v-for="item in storageTypeList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
</app-search>
<app-table v-model:pagination="pagination" :data="list" @get-data="getData">
<el-table-column prop="storageName" label="应急库名称" />
<el-table-column prop="areaName" label="属地" />
<el-table-column prop="storageTypeName" label="应急库类型" />
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button type="primary" text link @click="fnAddOrEdit(row, 'edit')">
编辑
</el-button>
<el-button
type="primary"
text
link
@click="fnDelete(row.emergencyStorageId)"
>
删除
</el-button>
</template>
</el-table-column>
<template #button>
<el-button type="primary" @click="fnAddOrEdit('', 'add')">
新增
</el-button>
</template>
</app-table>
<add
v-model:form="data.addOrEditDialog.form"
v-model:visible="data.addOrEditDialog.visible"
:type="data.addOrEditDialog.type"
@get-data="resetPagination"
/>
</div>
</template>
<script setup>
import { ref, reactive, nextTick } from "vue";
import { getDataDictionary } from "@/request/data_dictionary.js";
import { ElMessage, ElMessageBox } from "element-plus";
import { debounce } from "throttle-debounce";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import AppTable from "@/components/table/index.vue";
import AppSearch from "@/components/search/index.vue";
import useListData from "@/hooks/useListData.js";
import {
getEmergencyStoragePage,
setEmergencyStorageDelete,
} from "@/request/kangzai.js";
import Add from "./components/add.vue";
const storageTypeList = ref([]);
const fnGetStorageTypeList = async () => {
const resData = await getDataDictionary({
parentId: "j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y",
});
storageTypeList.value = resData.dictionariesList;
};
fnGetStorageTypeList();
const { list, pagination, searchForm, resetPagination, getData } = useListData(
getEmergencyStoragePage,
{
beforeGetData: (searchForm) => {
const area = searchForm.area || [];
searchForm.province = area[0];
searchForm.city = area[1];
searchForm.county = area[2];
searchForm.village = area[3];
searchForm.street = area[4];
},
callback: (list) => {
list.forEach((item) => {
item.storageTypeName = storageTypeList.value.filter((temp) => {
return temp.bianma === item.storageType;
})[0]?.name;
});
},
}
);
const data = reactive({
addOrEditDialog: {
visible: false,
type: "",
form: {
storageName: "",
storageType: "",
area: [],
areaName: [],
managementUnit: "",
longitude: "",
latitude: "",
},
},
});
const fnDelete = debounce(
1000,
async (emergencyStorageId) => {
await ElMessageBox.confirm(`确定要删除吗?`, {
type: "warning",
});
await setEmergencyStorageDelete({ emergencyStorageId });
ElMessage.success("删除成功");
resetPagination();
},
{ atBegin: true }
);
const fnAddOrEdit = async (row, type) => {
data.addOrEditDialog.visible = true;
await nextTick();
data.addOrEditDialog.form = {};
data.addOrEditDialog.type = type;
if (type === "edit") {
data.addOrEditDialog.form = row;
data.addOrEditDialog.form.area = [
row.province,
row.city,
row.county,
row.village,
row.street,
];
data.addOrEditDialog.form.areaName = row.areaName.split("-");
}
};
</script>
<style scoped></style>

View File

@ -0,0 +1,353 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'edit' ? '修改' : '新增'"
:before-close="fnClose"
>
<el-form
ref="formRef"
:model="form"
:rules="data.rules"
label-width="100px"
style="margin-top: 20px"
>
<el-row>
<el-col :span="24">
<el-form-item label="灾害点名称" prop="disasterName">
<el-input
v-model="form.disasterName"
placeholder="请输入灾害点名称"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="属地" prop="area">
<layout-cascader
v-model="form.area"
v-model:name="form.areaName"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="false"
:level="4"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="地形地貌" prop="terrain">
<el-select v-model="form.terrain" placeholder="请选择地形地貌">
<el-option
v-for="item in terrainList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="灾害类型" prop="disasterType">
<el-select v-model="form.disasterType" placeholder="请选择灾害类型">
<el-option
v-for="item in disasterTypeList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="规模" prop="scale">
<el-select v-model="form.scale" placeholder="请选择规模">
<el-option
v-for="item in scaleList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="稳定性" prop="stability">
<el-select v-model="form.stability" placeholder="请选择稳定性">
<el-option
v-for="item in stabilityList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="户数" prop="households">
<el-input-number
v-model="form.households"
placeholder="请输入户数"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="人口数" prop="population">
<el-input-number
v-model="form.population"
placeholder="请输入人口数"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="险情等级" prop="dangerousSituation">
<el-select
v-model="form.dangerousSituation"
placeholder="请选择险情等级"
style="width: 100%"
>
<el-option
v-for="item in dangerousSituationList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="勘察情况" prop="surveySituation">
<el-input
v-model="form.surveySituation"
placeholder="请输入勘察情况"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="治理情况" prop="governanceSituation">
<el-input
v-model="form.governanceSituation"
placeholder="请输入治理情况"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="搬迁情况" prop="relocationSituation">
<el-input
v-model="form.relocationSituation"
placeholder="请输入搬迁情况"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="风险等级" prop="riskLevel">
<el-select v-model="form.riskLevel" placeholder="请选择风险等级">
<el-option label="低风险" value="1" />
<el-option label="中风险" value="2" />
<el-option label="高风险" value="3" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="规模与形态描述" prop="scaleDescription">
<el-input
v-model="form.scaleDescription"
type="textarea"
:rows="3"
placeholder="请输入规模与形态描述"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<div style="display: flex">
<el-form-item label="经度" prop="longitude" style="flex: 1">
<el-input v-model="form.longitude" placeholder="请选择经度" />
</el-form-item>
<el-form-item label="纬度" prop="latitude" style="flex: 1">
<el-input v-model="form.latitude" placeholder="请选择纬度" />
</el-form-item>
<el-form-item label-width="10px">
<el-button
class="ml-10"
type="primary"
@click="fnSelectedPosition"
>点击定位</el-button
>
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="fnClose"> </el-button>
<el-button type="primary" @click="fnSubmit"> </el-button>
</template>
<app-map
v-model:visible="mapDialog.visible"
v-model:longitude="mapDialog.longitude"
v-model:latitude="mapDialog.latitude"
@submit="fnMapSubmit"
/>
</el-dialog>
</template>
<script setup>
import { debounce } from "throttle-debounce";
import { getDataDictionary } from "@/request/data_dictionary.js";
import useFormValidate from "@/hooks/useFormValidate.js";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import { ElMessage } from "element-plus";
import { reactive, ref } from "vue";
import { useVModels } from "@vueuse/core";
import AppMap from "@/components/map/map.vue";
import {
setGeologicalDisasterAdd,
setGeologicalDisasterUpdate,
} from "@/request/kangzai.js";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
type: {
type: String,
required: true,
default: "",
},
form: {
type: Object,
required: true,
default: () => ({}),
},
});
const { validate, formRef } = useFormValidate();
const emits = defineEmits(["update:visible", "update:form", "getData"]);
const { visible, form } = useVModels(props, emits);
//
const terrainList = ref([]);
const disasterTypeList = ref([]);
const scaleList = ref([]);
const stabilityList = ref([]);
const dangerousSituationList = ref([]);
const fnGetDictData = async () => {
const [
terrainRes,
disasterTypeRes,
scaleRes,
stabilityRes,
dangerousSituationRes,
] = await Promise.all([
getDataDictionary({ parentId: "b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q" }),
getDataDictionary({ parentId: "c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r" }),
getDataDictionary({ parentId: "7d3a1b2c8e4f5947a6b0c8d9e0f1a2b3" }),
getDataDictionary({ parentId: "a4f5c6d7e8291047b3a2b1c0d9e8f7a6" }),
getDataDictionary({ parentId: "c8e9f0a1b2c347d56e7f8a9b0c1d2e3f" }),
]);
terrainList.value = terrainRes.dictionariesList;
disasterTypeList.value = disasterTypeRes.dictionariesList;
scaleList.value = scaleRes.dictionariesList;
stabilityList.value = stabilityRes.dictionariesList;
dangerousSituationList.value = dangerousSituationRes.dictionariesList;
};
fnGetDictData();
const data = reactive({
rules: {
disasterName: [
{ required: true, message: "请输入灾害点名称", trigger: "blur" },
],
area: [{ required: true, message: "请选择区域", trigger: "blur" }],
disasterType: [
{ required: true, message: "请选择灾害类型", trigger: "change" },
],
scale: [{ required: true, message: "请选择规模", trigger: "change" }],
stability: [{ required: true, message: "请选择稳定性", trigger: "change" }],
households: [{ required: true, message: "请输入户数", trigger: "blur" }],
population: [{ required: true, message: "请输入人口数", trigger: "blur" }],
dangerousSituation: [
{ required: true, message: "请选择险情等级", trigger: "change" },
],
surveySituation: [
{ required: true, message: "请选择勘察情况", trigger: "change" },
],
governanceSituation: [
{ required: true, message: "请选择治理情况", trigger: "change" },
],
relocationSituation: [
{ required: true, message: "请选择搬迁情况", trigger: "change" },
],
riskLevel: [
{ required: true, message: "请选择风险等级", trigger: "change" },
],
longitude: [{ required: true, message: "请输入经度", trigger: "blur" }],
latitude: [{ required: true, message: "请输入纬度", trigger: "blur" }],
},
});
const mapDialog = ref({
visible: false,
longitude: "",
latitude: "",
});
const fnSelectedPosition = () => {
mapDialog.value.visible = true;
mapDialog.value.longitude = form.value.longitude;
mapDialog.value.latitude = form.value.latitude;
};
const fnMapSubmit = ({ longitude, latitude }) => {
form.value.longitude = longitude;
form.value.latitude = latitude;
};
const fnClose = () => {
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await validate();
const param = {
...form.value,
areaName: form.value.areaName.join("-"),
province: form.value.area[0],
city: form.value.area[1],
county: form.value.area[2],
village: form.value.area[3],
street: form.value.area[4],
};
delete param.area;
if (props.type === "add") {
await setGeologicalDisasterAdd(param);
} else if (props.type === "edit") {
await setGeologicalDisasterUpdate(param);
}
ElMessage.success("操作成功");
fnClose();
emits("getData");
},
{ atBegin: true }
);
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,199 @@
<template>
<div>
<app-search v-model="searchForm" @submit="resetPagination">
<el-col :span="6">
<el-form-item label="灾害点名称" prop="disasterName">
<el-input
v-model="searchForm.disasterName"
placeholder="请输入灾害点名称"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="属地" prop="area">
<layout-cascader
v-model="searchForm.area"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="true"
:level="5"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="地形地貌" prop="terrain">
<el-select v-model="searchForm.terrain" placeholder="请选择地形地貌">
<el-option
v-for="item in terrainList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="灾害类型" prop="disasterType">
<el-select
v-model="searchForm.disasterType"
placeholder="请选择灾害类型"
>
<el-option
v-for="item in disasterTypeList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
</app-search>
<app-table v-model:pagination="pagination" :data="list" @get-data="getData">
<el-table-column prop="disasterName" label="灾害点名称" />
<el-table-column prop="areaName" label="属地" />
<el-table-column prop="terrainName" label="地形地貌" />
<el-table-column prop="disasterTypeName" label="灾害类型" />
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button type="primary" text link @click="fnAddOrEdit(row, 'edit')"
>编辑</el-button
>
<el-button
type="primary"
text
link
@click="fnDelete(row.geologicalDisasterId)"
>删除</el-button
>
</template>
</el-table-column>
<template #button>
<el-button type="primary" @click="fnAddOrEdit('', 'add')"
>新增</el-button
>
</template>
</app-table>
<add
v-model:form="data.addOrEditDialog.form"
v-model:visible="data.addOrEditDialog.visible"
:type="data.addOrEditDialog.type"
@get-data="resetPagination"
/>
</div>
</template>
<script setup>
import { ref, reactive, nextTick } from "vue";
import { getDataDictionary } from "@/request/data_dictionary.js";
import { ElMessage, ElMessageBox } from "element-plus";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import AppTable from "@/components/table/index.vue";
import AppSearch from "@/components/search/index.vue";
import useListData from "@/hooks/useListData.js";
import {
getGeologicalDisasterPage,
setGeologicalDisasterDelete,
} from "@/request/kangzai.js";
import Add from "./components/add.vue";
const terrainList = ref([]);
const disasterTypeList = ref([]);
const fnGetDictData = async () => {
const [terrainRes, disasterTypeRes] = await Promise.all([
getDataDictionary({ parentId: "b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q" }),
getDataDictionary({ parentId: "c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r" }),
]);
terrainList.value = terrainRes.dictionariesList;
disasterTypeList.value = disasterTypeRes.dictionariesList;
};
fnGetDictData();
const { list, pagination, searchForm, resetPagination, getData } = useListData(
getGeologicalDisasterPage,
{
beforeGetData: (searchForm) => {
const area = searchForm.area || [];
searchForm.province = area[0];
searchForm.city = area[1];
searchForm.county = area[2];
searchForm.village = area[3];
searchForm.street = area[4];
},
callback: (list) => {
list.forEach((item) => {
item.terrainName =
terrainList.value.find((dict) => dict.bianma === item.terrain)
?.name || item.terrain;
item.disasterTypeName =
disasterTypeList.value.find(
(dict) => dict.bianma === item.disasterType
)?.name || item.disasterType;
});
},
}
);
const data = reactive({
addOrEditDialog: {
visible: false,
type: "",
form: {
disasterName: "",
province: "",
city: "",
county: "",
village: "",
street: "",
area: [],
areaName: [],
terrain: "",
disasterType: "",
scaleDescription: "",
scale: "",
stability: "",
households: "",
population: "",
dangerousSituation: "",
surveySituation: "",
governanceSituation: "",
relocationSituation: "",
riskLevel: "",
longitude: "",
latitude: "",
},
},
});
const fnDelete = async (geologicalDisasterId) => {
await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
await setGeologicalDisasterDelete({ geologicalDisasterId });
ElMessage.success("删除成功");
resetPagination();
};
const fnAddOrEdit = async (row, type) => {
data.addOrEditDialog.visible = true;
await nextTick();
data.addOrEditDialog.form = {};
data.addOrEditDialog.type = type;
if (type === "edit") {
data.addOrEditDialog.form = row;
data.addOrEditDialog.form.area = [
row.province,
row.city,
row.county,
row.village,
row.street,
];
data.addOrEditDialog.form.areaName = row.areaName.split("-");
}
};
</script>
<style scoped></style>

View File

@ -0,0 +1,412 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'edit' ? '修改' : '新增'"
:before-close="fnClose"
>
<el-form
ref="formRef"
:model="form"
:rules="data.rules"
label-width="160px"
style="margin-top: 20px"
>
<el-row>
<el-col :span="24">
<el-form-item label="村庄名称" prop="villageName">
<el-input v-model="form.villageName" placeholder="请输入村庄名称" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="属地" prop="area">
<layout-cascader
v-model="form.area"
v-model:name="form.areaName"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="false"
:level="4"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="县级预警负责人" prop="countyWarningOfficer">
<el-input
v-model="form.countyWarningOfficer"
placeholder="请输入预警负责人"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="县级预警负责人电话" prop="countyWarningPhone">
<el-input
v-model="form.countyWarningPhone"
placeholder="请输入县级预警负责人电话"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="县级责任人" prop="countyOfficer">
<el-input
v-model="form.countyOfficer"
placeholder="请输入县级责任人"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="县级责任人职务" prop="countyOfficerPosition">
<el-input
v-model="form.countyOfficerPosition"
placeholder="请输入县级责任人职务"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="县级责任人电话" prop="countyOfficerPhone">
<el-input
v-model="form.countyOfficerPhone"
placeholder="请输入县级责任人电话"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="乡级责任人" prop="townshipOfficer">
<el-input
v-model="form.townshipOfficer"
placeholder="请输入乡级责任人姓名"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="乡级责任人职务" prop="townshipOfficerPosition">
<el-input
v-model="form.townshipOfficerPosition"
placeholder="请输入乡级责任人职务"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="乡级责任人电话" prop="townshipOfficerPhone">
<el-input
v-model="form.townshipOfficerPhone"
placeholder="请输入乡级责任人电话"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="村级责任人" prop="villageOfficer">
<el-input
v-model="form.villageOfficer"
placeholder="请输入村级责任人姓名"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="村级责任人职务" prop="villageOfficerPosition">
<el-input
v-model="form.villageOfficerPosition"
placeholder="请输入村级责任人职务"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="村级责任人电话" prop="villageOfficerPhone">
<el-input
v-model="form.villageOfficerPhone"
placeholder="请输入村级责任人电话"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="监测员姓名" prop="monitorName">
<el-input
v-model="form.monitorName"
placeholder="请输入监测员姓名"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="监测员职务" prop="monitorPosition">
<el-input
v-model="form.monitorPosition"
placeholder="请输入监测员职务"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="监测员电话" prop="monitorPhone">
<el-input
v-model="form.monitorPhone"
placeholder="请输入监测员电话"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="预警员姓名" prop="warningOfficerName">
<el-input
v-model="form.warningOfficerName"
placeholder="请输入请输入预警员姓名"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="预警员职务" prop="warningOfficerPosition">
<el-input
v-model="form.warningOfficerPosition"
placeholder="请输入预警员职务"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="请输入预警员电话" prop="warningOfficerPhone">
<el-input
v-model="form.warningOfficerPhone"
placeholder="请输入请输入预警员电话"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="转移员姓名" prop="transferOfficerName">
<el-input
v-model="form.transferOfficerName"
placeholder="请输入转移员姓名"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="转移员职务" prop="transferOfficerPosition">
<el-input
v-model="form.transferOfficerPosition"
placeholder="请输入转移员职务"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="转移员电话" prop="transferOfficerPhone">
<el-input
v-model="form.transferOfficerPhone"
placeholder="请输入转移员电话"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remarks">
<el-input
v-model="form.remarks"
type="textarea"
:rows="3"
placeholder="请输入备注"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<div style="display: flex">
<el-form-item label="经度" prop="longitude" style="flex: 1">
<el-input v-model="form.longitude" placeholder="请选择经度" />
</el-form-item>
<el-form-item label="纬度" prop="latitude" style="flex: 1">
<el-input v-model="form.latitude" placeholder="请选择纬度" />
</el-form-item>
<el-form-item label-width="10px">
<el-button
class="ml-10"
type="primary"
@click="fnSelectedPosition(form)"
>点击定位</el-button
>
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="fnClose"> </el-button>
<el-button type="primary" @click="fnSubmit"> </el-button>
</template>
<app-map
v-model:visible="mapDialog.visible"
v-model:longitude="mapDialog.longitude"
v-model:latitude="mapDialog.latitude"
@submit="fnMapSubmit"
/>
</el-dialog>
</template>
<script setup>
import { debounce } from "throttle-debounce";
import useFormValidate from "@/hooks/useFormValidate.js";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import { ElMessage } from "element-plus";
import { reactive, ref } from "vue";
import { useVModels } from "@vueuse/core";
import AppMap from "@/components/map/map.vue";
import {
setMountainFloodVillageAdd,
setMountainFloodVillageUpdate,
} from "@/request/kangzai.js";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
type: {
type: String,
required: true,
default: "",
},
form: {
type: Object,
required: true,
default: () => ({}),
},
});
const { validate, formRef } = useFormValidate();
const emits = defineEmits(["update:visible", "update:form", "getData"]);
const { visible, form } = useVModels(props, emits);
const data = reactive({
rules: {
villageName: [
{ required: true, message: "请输入村庄名称", trigger: "blur" },
],
area: [{ required: true, message: "请输入村庄区域", trigger: "blur" }],
countyWarningOfficer: [
{ required: true, message: "请输入县级预警负责人", trigger: "blur" },
],
countyWarningOfficerPhone: [
{ required: true, message: "请输入县级预警负责人手机号", trigger: "blur" },
],
countyOfficer: [
{ required: true, message: "请输入县级责任人姓名", trigger: "blur" },
],
countyOfficerPosition: [
{ required: true, message: "请输入县级责任人职务", trigger: "blur" },
],
countyOfficerPhone: [
{ required: true, message: "请输入县级责任人电话", trigger: "blur" },
],
townshipOfficer: [
{ required: true, message: "请输入乡级责任人姓名", trigger: "blur" },
],
townshipOfficerPosition: [
{ required: true, message: "请输入乡级责任人职务", trigger: "blur" },
],
townshipOfficerPhone: [
{ required: true, message: "请输入乡级责任人电话", trigger: "blur" },
],
villageOfficer: [
{ required: true, message: "请输入村级责任人姓名", trigger: "blur" },
],
villageOfficerPosition: [
{ required: true, message: "请输入村级责任人职务", trigger: "blur" },
],
villageOfficerPhone: [
{ required: true, message: "请输入村级责任人电话", trigger: "blur" },
],
monitorName: [
{ required: true, message: "请输入监测员姓名", trigger: "blur" },
],
monitorPosition: [
{ required: true, message: "请输入监测员职务", trigger: "blur" },
],
monitorPhone: [
{ required: true, message: "请输入监测员电话", trigger: "blur" },
],
warningOfficerName: [
{ required: true, message: "请输入预警员姓名", trigger: "blur" },
],
warningOfficerPosition: [
{ required: true, message: "请输入预警员职务", trigger: "blur" },
],
warningOfficerPhone: [
{ required: true, message: "请输入预警员电话", trigger: "blur" },
],
transferOfficerName: [
{ required: true, message: "请输入转移员姓名", trigger: "blur" },
],
transferOfficerPosition: [
{ required: true, message: "请输入转移员职务", trigger: "blur" },
],
transferOfficerPhone: [
{ required: true, message: "请输入转移员电话", trigger: "blur" },
],
longitude: [{ required: true, message: "请输入经度", trigger: "blur" }],
latitude: [{ required: true, message: "请输入纬度", trigger: "blur" }],
},
});
const mapDialog = ref({
visible: false,
longitude: "",
latitude: "",
});
const fnSelectedPosition = ({ longitude, latitude }) => {
mapDialog.value.visible = true;
mapDialog.value.longitude = longitude;
mapDialog.value.latitude = latitude;
};
const fnMapSubmit = ({ longitude, latitude }) => {
form.value.longitude = longitude;
form.value.latitude = latitude;
};
const fnClose = () => {
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await validate();
const param = {
...form.value,
areaName: form.value.areaName.join("-"),
province: form.value.area[0],
city: form.value.area[1],
county: form.value.area[2],
village: form.value.area[3],
street: form.value.area[4],
};
delete param.area;
if (props.type === "add") {
await setMountainFloodVillageAdd(param);
} else if (props.type === "edit") {
await setMountainFloodVillageUpdate(param);
}
ElMessage.success("操作成功");
fnClose();
emits("getData");
},
{ atBegin: true }
);
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,163 @@
<template>
<div>
<app-search v-model="searchForm" @submit="resetPagination">
<el-col :span="6">
<el-form-item label="村庄名称" prop="villageName">
<el-input
v-model="searchForm.villageName"
placeholder="请输入村庄名称"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="属地" prop="area">
<layout-cascader
v-model="searchForm.area"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="true"
:level="5"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="预警负责人" prop="countyWarningOfficer">
<el-input
v-model="searchForm.countyWarningOfficer"
placeholder="请输入预警负责人"
/>
</el-form-item>
</el-col>
</app-search>
<app-table v-model:pagination="pagination" :data="list" @get-data="getData">
<el-table-column prop="villageName" label="村庄名称" />
<el-table-column prop="areaName" label="属地" />
<el-table-column prop="countyWarningOfficer" label="县级预警负责人" />
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button type="primary" text link @click="fnAddOrEdit(row, 'edit')"
>编辑</el-button
>
<el-button
type="primary"
text
link
@click="fnDelete(row.mountainFloodVillageId)"
>删除</el-button
>
</template>
</el-table-column>
<template #button>
<el-button type="primary" @click="fnAddOrEdit('', 'add')"
>新增</el-button
>
</template>
</app-table>
<add
v-model:form="data.addOrEditDialog.form"
v-model:visible="data.addOrEditDialog.visible"
:type="data.addOrEditDialog.type"
@get-data="resetPagination"
/>
</div>
</template>
<script setup>
import { reactive, nextTick } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { debounce } from "throttle-debounce";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import AppTable from "@/components/table/index.vue";
import AppSearch from "@/components/search/index.vue";
import useListData from "@/hooks/useListData.js";
import {
getMountainFloodVillagePage,
setMountainFloodVillageDelete,
} from "@/request/kangzai.js";
import Add from "./components/add.vue";
const { list, pagination, searchForm, resetPagination, getData } = useListData(
getMountainFloodVillagePage,
{
beforeGetData: (searchForm) => {
const area = searchForm.area || [];
searchForm.province = area[0];
searchForm.city = area[1];
searchForm.county = area[2];
searchForm.village = area[3];
searchForm.street = area[4];
},
callback: () => {},
}
);
const data = reactive({
addOrEditDialog: {
visible: false,
type: "",
form: {
villageName: "",
province: "",
city: "",
county: "",
village: "",
street: "",
area: [],
areaName: [],
//
countyWarningOfficer: "",
countyWarningPhone: "",
countyOfficer: "",
countyOfficerPosition: "",
countyOfficerPhone: "",
townshipOfficer: "",
townshipOfficerPosition: "",
townshipOfficerPhone: "",
villageOfficer: "",
villageOfficerPosition: "",
villageOfficerPhone: "",
//
monitorName: "",
monitorPosition: "",
monitorPhone: "",
remarks: "",
longitude: "",
latitude: "",
},
},
});
const fnDelete = debounce(
1000,
async (mountainFloodVillageId) => {
await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
await setMountainFloodVillageDelete({ mountainFloodVillageId });
ElMessage.success("删除成功");
resetPagination();
},
{ atBegin: true }
);
const fnAddOrEdit = async (row, type) => {
data.addOrEditDialog.visible = true;
await nextTick();
data.addOrEditDialog.form = {};
data.addOrEditDialog.type = type;
if (type === "edit") {
data.addOrEditDialog.form = row;
data.addOrEditDialog.form.area = [
row.province,
row.city,
row.county,
row.village,
row.street,
];
data.addOrEditDialog.form.areaName = row.areaName.split("-");
}
};
</script>
<style scoped></style>

View File

@ -0,0 +1,212 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'edit' ? '修改' : '新增'"
:before-close="fnClose"
>
<el-form
ref="formRef"
:model="form"
:rules="data.rules"
label-width="200px"
style="margin-top: 20px"
>
<el-row>
<el-col :span="24">
<el-form-item label="水库名称" prop="reservoirName">
<el-input
v-model="form.reservoirName"
placeholder="请输入水库名称"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="水库类型" prop="reservoirType">
<el-select
v-model="form.reservoirType"
placeholder="请选择水库类型"
>
<el-option
v-for="item in reservoirTypeList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="水库等级" prop="reservoirLevel">
<el-select
v-model="form.reservoirLevel"
placeholder="请选择水库等级"
>
<el-option
v-for="item in reservoirLevelList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="极限库容(万立方米)" prop="maxCapacity">
<el-input v-model="form.maxCapacity" placeholder="请输入极限库容" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="当前库容(万立方米)" prop="currentCapacity">
<el-input
v-model="form.currentCapacity"
placeholder="请输入当前库容"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<div style="display: flex">
<el-form-item label="经度" prop="longitude" style="flex: 1">
<el-input v-model="form.longitude" placeholder="请选择经度" />
</el-form-item>
<el-form-item label="纬度" prop="latitude" style="flex: 1">
<el-input v-model="form.latitude" placeholder="请选择纬度" />
</el-form-item>
<el-form-item label-width="10px">
<el-button
class="ml-10"
type="primary"
@click="fnSelectedPosition"
>点击定位</el-button
>
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="fnClose"> </el-button>
<el-button type="primary" @click="fnSubmit"> </el-button>
</template>
<app-map
v-model:visible="mapDialog.visible"
v-model:longitude="mapDialog.longitude"
v-model:latitude="mapDialog.latitude"
@submit="fnMapSubmit"
/>
</el-dialog>
</template>
<script setup>
import { debounce } from "throttle-debounce";
import useFormValidate from "@/hooks/useFormValidate.js";
import { ElMessage } from "element-plus";
import { reactive, ref } from "vue";
import { useVModels } from "@vueuse/core";
import AppMap from "@/components/map/map.vue";
import {
setReservoirBasicAdd,
setReservoirBasicUpdate,
} from "@/request/kangzai.js";
import { getDataDictionary } from "@/request/data_dictionary.js";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
type: {
type: String,
required: true,
default: "",
},
form: {
type: Object,
required: true,
default: () => ({}),
},
});
const { validate, formRef } = useFormValidate();
const emits = defineEmits(["update:visible", "update:form", "getData"]);
const { visible, form } = useVModels(props, emits);
//
const reservoirTypeList = ref([]);
const reservoirLevelList = ref([]);
const fnGetDictData = async () => {
const [typeRes, levelRes] = await Promise.all([
getDataDictionary({ parentId: "848f91ce29ee1f2d078bc6ec69d440df" }),
getDataDictionary({ parentId: "a8b7c6d5e4f3g2h1i0j9k8l7m6n5o4p" }),
]);
reservoirTypeList.value = typeRes.dictionariesList;
reservoirLevelList.value = levelRes.dictionariesList;
};
fnGetDictData();
const data = reactive({
rules: {
reservoirName: [
{ required: true, message: "请输入水库名称", trigger: "blur" },
],
reservoirType: [
{ required: true, message: "请选择水库类型", trigger: "change" },
],
reservoirLevel: [
{ required: true, message: "请选择水库等级", trigger: "change" },
],
longitude: [{ required: true, message: "请输入经度", trigger: "blur" }],
latitude: [{ required: true, message: "请输入纬度", trigger: "blur" }],
},
});
const mapDialog = ref({
visible: false,
longitude: "",
latitude: "",
});
const fnSelectedPosition = () => {
mapDialog.value.visible = true;
mapDialog.value.longitude = form.value.longitude;
mapDialog.value.latitude = form.value.latitude;
};
const fnMapSubmit = ({ longitude, latitude }) => {
form.value.longitude = longitude;
form.value.latitude = latitude;
};
const fnClose = () => {
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await validate();
const param = {
...form.value,
};
if (props.type === "add") {
await setReservoirBasicAdd(param);
} else if (props.type === "edit") {
await setReservoirBasicUpdate(param);
}
ElMessage.success("操作成功");
fnClose();
emits("getData");
},
{ atBegin: true }
);
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,161 @@
<template>
<div>
<app-search v-model="searchForm" @submit="resetPagination">
<el-col :span="6">
<el-form-item label="水库名称" prop="reservoirName">
<el-input
v-model="searchForm.reservoirName"
placeholder="请输入水库名称"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="水库类型" prop="reservoirType">
<el-select
v-model="searchForm.reservoirType"
placeholder="请选择水库类型"
>
<el-option
v-for="item in reservoirTypeList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="水库等级" prop="reservoirLevel">
<el-select
v-model="searchForm.reservoirLevel"
placeholder="请选择水库等级"
>
<el-option
v-for="item in reservoirLevelList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
</app-search>
<app-table v-model:pagination="pagination" :data="list" @get-data="getData">
<el-table-column prop="reservoirName" label="水库名称" />
<el-table-column label="水库类型" prop="reservoirTypeLabel" />
<el-table-column label="水库等级" prop="reservoirLevelLabel" />
<el-table-column prop="maxCapacity" label="极限库容(万立方米)" />
<el-table-column prop="currentCapacity" label="当前库容(万立方米)" />
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button type="primary" text link @click="fnAddOrEdit(row, 'edit')"
>编辑</el-button
>
<el-button
type="primary"
text
link
@click="fnDelete(row.reservoirBasicId)"
>删除</el-button
>
</template>
</el-table-column>
<template #button>
<el-button type="primary" @click="fnAddOrEdit('', 'add')"
>新增</el-button
>
</template>
</app-table>
<add
v-model:form="data.addOrEditDialog.form"
v-model:visible="data.addOrEditDialog.visible"
:type="data.addOrEditDialog.type"
@get-data="resetPagination"
/>
</div>
</template>
<script setup>
import { ref, reactive, nextTick } from "vue";
import { getDataDictionary } from "@/request/data_dictionary.js";
import { ElMessage, ElMessageBox } from "element-plus";
import AppTable from "@/components/table/index.vue";
import AppSearch from "@/components/search/index.vue";
import useListData from "@/hooks/useListData.js";
import {
getReservoirBasicPage,
setReservoirBasicDelete,
} from "@/request/kangzai.js";
import Add from "./components/add.vue";
const reservoirTypeList = ref([]);
const reservoirLevelList = ref([]);
const fnGetDictData = async () => {
const [typeRes, levelRes] = await Promise.all([
getDataDictionary({ parentId: "848f91ce29ee1f2d078bc6ec69d440df" }),
getDataDictionary({ parentId: "a8b7c6d5e4f3g2h1i0j9k8l7m6n5o4p" }),
]);
reservoirTypeList.value = typeRes.dictionariesList;
reservoirLevelList.value = levelRes.dictionariesList;
};
fnGetDictData();
const { list, pagination, searchForm, resetPagination, getData } = useListData(
getReservoirBasicPage,
{
callback: (list) => {
list.forEach((item) => {
item.reservoirTypeLabel =
reservoirTypeList.value.find(
(dict) => dict.bianma === item.reservoirType
)?.name || item.reservoirType;
item.reservoirLevelLabel =
reservoirLevelList.value.find(
(dict) => dict.bianma === item.reservoirLevel
)?.name || item.reservoirLevel;
});
},
}
);
const data = reactive({
addOrEditDialog: {
visible: false,
type: "",
form: {
reservoirName: "",
reservoirType: "",
reservoirLevel: "",
maxCapacity: "",
currentCapacity: "",
longitude: "",
latitude: "",
},
},
});
const fnDelete = async (reservoirBasicId) => {
await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
await setReservoirBasicDelete({ reservoirBasicId });
ElMessage.success("删除成功");
resetPagination();
};
const fnAddOrEdit = async (row, type) => {
data.addOrEditDialog.visible = true;
await nextTick();
data.addOrEditDialog.form = {};
data.addOrEditDialog.type = type;
if (type === "edit") {
data.addOrEditDialog.form = row;
}
};
</script>
<style scoped></style>

View File

@ -0,0 +1,153 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'edit' ? '修改' : '新增'"
:before-close="fnClose"
>
<el-form
ref="formRef"
:model="form"
:rules="data.rules"
label-width="200px"
style="margin-top: 20px"
>
<el-row>
<el-col :span="24">
<el-form-item label="河流名称" prop="riverName">
<el-input v-model="form.riverName" placeholder="请输入河流名称" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="全长(km)" prop="totalLength">
<el-input
v-model.number="form.totalLength"
placeholder="请输入全长(km)"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="流域面积(km²)" prop="basinArea">
<el-input
v-model.number="form.basinArea"
placeholder="请输入流域面积(km²)"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item
label="占全市国土面积百分比(%)"
prop="cityAreaPercentage"
>
<el-input
v-model.number="form.cityAreaPercentage"
placeholder="请输入百分比"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="我市境内河长(km)" prop="inCityRiverLength">
<el-input
v-model.number="form.inCityRiverLength"
placeholder="请输入境内河长(km)"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="我市境内流域面积(km²)" prop="inCityBasinArea">
<el-input
v-model.number="form.inCityBasinArea"
placeholder="请输入境内流域面积(km²)"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="fnClose"> </el-button>
<el-button type="primary" @click="fnSubmit"> </el-button>
</template>
</el-dialog>
</template>
<script setup>
import { debounce } from "throttle-debounce";
import useFormValidate from "@/hooks/useFormValidate.js";
import { ElMessage } from "element-plus";
import { reactive } from "vue";
import { useVModels } from "@vueuse/core";
import { setRiverAdd, setRiverUpdate } from "@/request/kangzai.js";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
type: {
type: String,
required: true,
default: "",
},
form: {
type: Object,
required: true,
default: () => ({}),
},
});
const { validate, formRef } = useFormValidate();
const emits = defineEmits(["update:visible", "update:form", "getData"]);
const { visible, form } = useVModels(props, emits);
const data = reactive({
rules: {
riverName: [{ required: true, message: "请输入河流名称", trigger: "blur" }],
totalLength: [
{ required: true, message: "请输入全长(km)", trigger: "blur" },
],
basinArea: [
{ required: true, message: "请输入流域面积(km²)", trigger: "blur" },
],
cityAreaPercentage: [
{ required: true, message: "请输入百分比(%)", trigger: "blur" },
],
inCityRiverLength: [
{ required: true, message: "请输入境内河长(km)", trigger: "blur" },
],
inCityBasinArea: [
{ required: true, message: "请输入境内流域面积(km²)", trigger: "blur" },
],
},
});
const fnClose = () => {
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await validate();
const param = { ...form.value };
if (props.type === "add") {
await setRiverAdd(param);
} else if (props.type === "edit") {
await setRiverUpdate(param);
}
ElMessage.success("操作成功");
fnClose();
emits("getData");
},
{ atBegin: true }
);
</script>
<style lang="scss" scoped></style>

141
src/views/river/index.vue Normal file
View File

@ -0,0 +1,141 @@
<template>
<div>
<app-search v-model="searchForm" @submit="resetPagination">
<el-col :span="6">
<el-form-item label="河流名称" prop="riverName">
<el-input
v-model="searchForm.riverName"
placeholder="请输入河流名称"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="全长(km)" prop="totalLength">
<el-input v-model="searchForm.totalLength" placeholder="请输入全长" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="流域面积(km²)" prop="basinArea">
<el-input
v-model="searchForm.basinArea"
placeholder="请输入流域面积"
/>
</el-form-item>
</el-col>
</app-search>
<app-table v-model:pagination="pagination" :data="list" @get-data="getData">
<el-table-column prop="riverName" label="河流名称" />
<el-table-column prop="totalLength" label="全长(km)" />
<el-table-column prop="basinArea" label="流域面积(km²)" />
<el-table-column
prop="cityAreaPercentage"
label="占全市国土面积百分比(%)"
/>
<el-table-column prop="inCityRiverLength" label="我市境内河长(km)" />
<el-table-column prop="inCityBasinArea" label="我市境内流域面积(km²)" />
<el-table-column prop="countyCount" label="流经区县数" />
<el-table-column prop="riskVillageCount" label="河道行洪隐患区村庄数" />
<el-table-column label="操作" width="120" :show-overflow-tooltip="false">
<template #default="{ row }">
<el-button
type="primary"
text
link
@click="
router.push({
path: '/river/risk_village',
query: { riverId: row.riverId },
})
"
>
查看隐患村
</el-button>
<el-button
type="primary"
text
link
@click="
router.push({
path: '/river/river_section',
query: { riverId: row.riverId },
})
"
>
查看流经县区
</el-button>
<el-button type="primary" text link @click="fnAddOrEdit(row, 'edit')"
>编辑</el-button
>
<el-button type="primary" text link @click="fnDelete(row.riverId)"
>删除</el-button
>
</template>
</el-table-column>
<template #button>
<el-button type="primary" @click="fnAddOrEdit('', 'add')"
>新增</el-button
>
</template>
</app-table>
<add
v-model:form="data.addOrEditDialog.form"
v-model:visible="data.addOrEditDialog.visible"
:type="data.addOrEditDialog.type"
@get-data="resetPagination"
/>
</div>
</template>
<script setup>
import { reactive, nextTick } from "vue";
import { useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import AppTable from "@/components/table/index.vue";
import AppSearch from "@/components/search/index.vue";
import useListData from "@/hooks/useListData.js";
import { getRiverPage, setRiverDelete } from "@/request/kangzai.js";
import Add from "./components/add.vue";
const router = useRouter();
const { list, pagination, searchForm, resetPagination, getData } =
useListData(getRiverPage);
const data = reactive({
addOrEditDialog: {
visible: false,
type: "",
form: {
riverName: "",
totalLength: "",
basinArea: "",
cityAreaPercentage: "",
inCityRiverLength: "",
inCityBasinArea: "",
},
},
});
const fnDelete = async (riverId) => {
await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
await setRiverDelete({ riverId });
ElMessage.success("删除成功");
resetPagination();
};
const fnAddOrEdit = async (row, type) => {
data.addOrEditDialog.visible = true;
await nextTick();
data.addOrEditDialog.form = {};
data.addOrEditDialog.type = type;
if (type === "edit") {
data.addOrEditDialog.form = row;
}
};
</script>
<style scoped></style>

View File

@ -0,0 +1,136 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'edit' ? '修改' : '新增'"
:before-close="fnClose"
>
<el-form
ref="formRef"
:model="form"
:rules="data.rules"
label-width="120px"
style="margin-top: 20px"
>
<el-row>
<el-col :span="24">
<el-form-item label="所属区县" prop="area">
<layout-cascader
v-model="form.area"
v-model:name="form.areaName"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="false"
:level="4"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="隐患等级" prop="riskLevel">
<el-select v-model="form.riskLevel" placeholder="请选择隐患等级">
<el-option label="低风险" value="1" />
<el-option label="中风险" value="2" />
<el-option label="高风险" value="3" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="隐患描述" prop="riskDescription">
<el-input
v-model="form.riskDescription"
type="textarea"
:rows="3"
placeholder="请输入隐患描述"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="fnClose"> </el-button>
<el-button type="primary" @click="fnSubmit"> </el-button>
</template>
</el-dialog>
</template>
<script setup>
import { debounce } from "throttle-debounce";
import useFormValidate from "@/hooks/useFormValidate.js";
import { ElMessage } from "element-plus";
import { reactive } from "vue";
import { useVModels } from "@vueuse/core";
import {
setRiverRiskVillageAdd,
setRiverRiskVillageUpdate,
} from "@/request/kangzai.js";
import LayoutCascader from "@/components/layout_cascader/index.vue";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
type: {
type: String,
required: true,
default: "",
},
form: {
type: Object,
required: true,
default: () => ({}),
},
riverId: {
type: [String, Number],
required: true,
default: "",
},
});
const { validate, formRef } = useFormValidate();
const emits = defineEmits(["update:visible", "update:form", "getData"]);
const { visible, form } = useVModels(props, emits);
const data = reactive({
rules: {
area: [{ required: true, message: "请输入所属区县", trigger: "blur" }],
},
});
const fnClose = () => {
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await validate();
const form = props.form;
const param = {
...form,
riverId: props.riverId,
areaName: form.areaName.join("-"),
province: form.area[0],
city: form.area[1],
county: form.area[2],
village: form.area[3],
street: form.area[4],
};
delete param.area;
if (props.type === "add") {
await setRiverRiskVillageAdd(param);
} else if (props.type === "edit") {
await setRiverRiskVillageUpdate(param);
}
ElMessage.success("操作成功");
fnClose();
emits("getData");
},
{ atBegin: true }
);
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,157 @@
<template>
<div>
<app-search v-model="searchForm" @submit="resetPagination">
<el-col :span="6">
<el-form-item label="属地" prop="area">
<layout-cascader
v-model="searchForm.area"
v-model:name="searchForm.areaName"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="false"
:level="4"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="隐患等级" prop="riskLevel">
<el-select
v-model="searchForm.riskLevel"
placeholder="请选择隐患等级"
>
<el-option label="低风险" value="1" />
<el-option label="中风险" value="2" />
<el-option label="高风险" value="3" />
</el-select>
</el-form-item>
</el-col>
</app-search>
<app-table v-model:pagination="pagination" :data="list" @get-data="getData">
<el-table-column prop="areaName" label="村庄名称" />
<el-table-column prop="riskLevelLabel" label="隐患等级" />
<el-table-column prop="riskDescription" label="隐患描述" />
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button type="primary" text link @click="fnAddOrEdit(row, 'edit')"
>编辑</el-button
>
<el-button
type="primary"
text
link
@click="fnDelete(row.riverRiskVillagesId)"
>删除</el-button
>
</template>
</el-table-column>
<template #button>
<el-button type="primary" @click="fnAddOrEdit('', 'add')"
>新增</el-button
>
</template>
</app-table>
<add
v-model:form="data.addOrEditDialog.form"
v-model:visible="data.addOrEditDialog.visible"
:type="data.addOrEditDialog.type"
:river-id="riverId"
@get-data="resetPagination"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { useRoute } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import AppTable from "@/components/table/index.vue";
import AppSearch from "@/components/search/index.vue";
import useListData from "@/hooks/useListData.js";
import Add from "./components/add.vue";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import {
getRiverRiskVillagePage,
setRiverRiskVillageDelete,
} from "@/request/kangzai.js";
const route = useRoute();
// riverId
const riverId = ref(route.query.riverId);
if (!riverId.value) {
ElMessage.error("缺少关联河流ID");
//
}
const riskLevelMap = {
1: "低风险",
2: "中风险",
3: "高风险",
};
const { list, pagination, searchForm, resetPagination, getData } = useListData(
getRiverRiskVillagePage,
{
defaultSearchForm: {
riverId: riverId.value,
},
beforeGetData: (searchForm) => {
const area = searchForm.area || [];
searchForm.province = area[0];
searchForm.city = area[1];
searchForm.county = area[2];
searchForm.village = area[3];
searchForm.street = area[4];
},
callback: (list) => {
list.forEach((item) => {
item.riskLevelLabel = riskLevelMap[item.riskLevel] || "未知";
});
},
}
);
const data = reactive({
addOrEditDialog: {
visible: false,
type: "",
form: {
villageName: "",
area: [],
areaName: [],
riskLevel: "",
riskDescription: "",
},
},
});
const fnDelete = async (riverRiskVillagesId) => {
await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
await setRiverRiskVillageDelete({ riverRiskVillagesId });
ElMessage.success("删除成功");
resetPagination();
};
const fnAddOrEdit = async (row, type) => {
data.addOrEditDialog.visible = true;
data.addOrEditDialog.type = type;
if (type === "edit") {
data.addOrEditDialog.form = { ...row };
data.addOrEditDialog.form.area = [
row.province,
row.city,
row.county,
row.village,
row.street,
];
data.addOrEditDialog.form.areaName = row.areaName.split("-");
}
};
</script>
<style scoped></style>

View File

@ -0,0 +1,176 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'edit' ? '修改' : '新增'"
:before-close="fnClose"
>
<el-form
ref="formRef"
:model="form"
:rules="data.rules"
label-width="150px"
style="margin-top: 20px"
>
<el-row>
<el-col :span="24">
<el-form-item label="属地" prop="area">
<layout-cascader
v-model="form.area"
v-model:name="form.areaName"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="false"
:level="2"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="县区段长度(km)" prop="sectionLength">
<el-input
v-model.number="form.sectionLength"
placeholder="请输入县区段长度"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="最小河宽(m)" prop="riverWidthMin">
<el-input
v-model.number="form.riverWidthMin"
placeholder="请输入最小河宽"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="最大河宽(m)" prop="riverWidthMax">
<el-input
v-model.number="form.riverWidthMax"
placeholder="请输入最大河宽"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="平均河宽(m)" prop="riverWidthAvg">
<el-input
v-model.number="form.riverWidthAvg"
placeholder="请输入平均河宽"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="河段特征描述" prop="sectionDescription">
<el-input
v-model="form.sectionDescription"
type="textarea"
:rows="3"
placeholder="请输入河段特征描述"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="fnClose"> </el-button>
<el-button type="primary" @click="fnSubmit"> </el-button>
</template>
</el-dialog>
</template>
<script setup>
import { debounce } from "throttle-debounce";
import useFormValidate from "@/hooks/useFormValidate.js";
import { ElMessage } from "element-plus";
import { reactive } from "vue";
import { useVModels } from "@vueuse/core";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import {
setRiverSectionsAdd,
setRiverSectionsUpdate,
} from "@/request/kangzai.js";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
type: {
type: String,
required: true,
default: "",
},
form: {
type: Object,
required: true,
default: () => ({}),
},
riverId: {
type: [String, Number],
required: true,
default: "",
},
});
const { validate, formRef } = useFormValidate();
const emits = defineEmits(["update:visible", "update:form", "getData"]);
const { visible, form } = useVModels(props, emits);
const data = reactive({
rules: {
area: [{ required: true, message: "请选择属地", trigger: "change" }],
sectionLength: [
{ required: true, message: "请输入县区段长度", trigger: "blur" },
],
riverWidthMin: [
{ required: true, message: "请输入最小河宽", trigger: "blur" },
],
riverWidthMax: [
{ required: true, message: "请输入最大河宽", trigger: "blur" },
],
riverWidthAvg: [
{ required: true, message: "请输入平均河宽", trigger: "blur" },
],
sectionDescription: [
{ required: true, message: "请输入河段特征描述", trigger: "blur" },
],
},
});
const fnClose = () => {
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await validate();
const param = {
...form.value,
riverId: props.riverId,
province: form.value.area[0],
city: form.value.area[1],
county: form.value.area[2],
areaName: form.value.areaName.join("-"),
};
delete param.area;
if (props.type === "add") {
await setRiverSectionsAdd(param);
} else if (props.type === "edit") {
await setRiverSectionsUpdate(param);
}
ElMessage.success("操作成功");
fnClose();
emits("getData");
},
{ atBegin: true }
);
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,140 @@
<template>
<div>
<app-search v-model="searchForm" @submit="resetPagination">
<el-col :span="6">
<el-form-item label="县区" prop="area">
<layout-cascader
v-model="searchForm.area"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="false"
:level="2"
/>
</el-form-item>
</el-col>
</app-search>
<app-table v-model:pagination="pagination" :data="list" @get-data="getData">
<el-table-column prop="areaName" label="地区" />
<el-table-column prop="sectionLength" label="县区段长度(km)" />
<el-table-column prop="riverWidthMin" label="最小河宽(m)" />
<el-table-column prop="riverWidthMax" label="最大河宽(m)" />
<el-table-column prop="riverWidthAvg" label="平均河宽(m)" />
<el-table-column prop="sectionDescription" label="河段特征描述" />
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button type="primary" text link @click="fnAddOrEdit(row, 'edit')"
>编辑</el-button
>
<el-button
type="primary"
text
link
@click="fnDelete(row.riverSectionsId)"
>删除</el-button
>
</template>
</el-table-column>
<template #button>
<el-button type="primary" @click="fnAddOrEdit('', 'add')"
>新增</el-button
>
</template>
</app-table>
<add
v-model:form="data.addOrEditDialog.form"
v-model:visible="data.addOrEditDialog.visible"
:type="data.addOrEditDialog.type"
:river-id="riverId"
@get-data="resetPagination"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { useRoute } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import AppTable from "@/components/table/index.vue";
import AppSearch from "@/components/search/index.vue";
import useListData from "@/hooks/useListData.js";
import Add from "./components/add.vue";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import {
setRiverSectionsDelete,
setRiverSectionsListPage,
} from "@/request/kangzai.js";
const route = useRoute();
const riverId = ref(route.query.riverId);
if (!riverId.value) {
ElMessage.error("缺少关联河流ID");
}
const { list, pagination, searchForm, resetPagination, getData } = useListData(
setRiverSectionsListPage,
{
defaultSearchForm: {
riverId: riverId.value,
},
beforeGetData: (searchForm) => {
const area = searchForm.area || [];
searchForm.province = area[0];
searchForm.city = area[1];
searchForm.county = area[2];
},
}
);
const data = reactive({
addOrEditDialog: {
visible: false,
type: "",
form: {
province: "",
city: "",
county: "",
areaName: [],
sectionLength: "",
riverWidthMin: "",
riverWidthMax: "",
riverWidthAvg: "",
sectionDescription: "",
},
},
});
const fnDelete = async (riverSectionsId) => {
await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
await setRiverSectionsDelete({ riverSectionsId });
ElMessage.success("删除成功");
resetPagination();
};
const fnAddOrEdit = async (row, type) => {
data.addOrEditDialog.visible = true;
data.addOrEditDialog.type = type;
if (type === "edit") {
data.addOrEditDialog.form = { ...row };
data.addOrEditDialog.form.area = [row.province, row.city, row.county];
data.addOrEditDialog.form.areaName = row.areaName.split("-");
} else {
data.addOrEditDialog.form = {
province: "",
city: "",
county: "",
areaName: [],
sectionLength: "",
riverWidthMin: "",
riverWidthMax: "",
riverWidthAvg: "",
sectionDescription: "",
};
}
};
</script>
<style scoped></style>

View File

@ -0,0 +1,141 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'edit' ? '修改' : '新增'"
:before-close="fnClose"
>
<el-form
ref="formRef"
:model="form"
:rules="data.rules"
label-width="100px"
>
<el-form-item label="上级菜单">
<el-tag>{{ parentName }}</el-tag>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="编码" prop="bianma">
<el-input
v-model="form.bianma"
:disabled="type === 'edit'"
placeholder="请输入编码"
/>
</el-form-item>
<el-form-item label="排序" prop="orderBy">
<el-input v-model.number="form.orderBy" placeholder="请输入排序" />
</el-form-item>
<el-form-item label="备注" prop="descr">
<el-input
v-model="form.descr"
:autosize="{ minRows: 1 }"
type="textarea"
placeholder="请输入备注"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="fnClose"> </el-button>
<el-button type="primary" @click="fnSubmit"> </el-button>
</template>
</el-dialog>
</template>
<script setup>
import {
getDataDictionaryRepeat,
setDataDictionaryAdd,
setDataDictionaryEdit,
} from "@/request/system_management.js";
import { debounce } from "throttle-debounce";
import useFormValidate from "@/hooks/useFormValidate.js";
import { ElMessage } from "element-plus";
import { reactive } from "vue";
import { useVModels } from "@vueuse/core";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
type: {
type: String,
required: true,
default: "",
},
parentName: {
type: String,
required: true,
default: "",
},
parentId: {
type: String,
required: true,
default: "",
},
form: {
type: Object,
required: true,
default: () => ({}),
},
});
const emits = defineEmits(["update:visible", "update:form", "getData"]);
const { visible, form } = useVModels(props, emits);
const { validate, formRef } = useFormValidate();
const data = reactive({
rules: {
name: [
{ required: true, message: "字典名称不能为空", trigger: "change" },
{ min: 2, max: 30, message: "长度在2到30个字符", trigger: "blur" },
],
bianma: [
{ required: true, message: "字典编码名称不能为空", trigger: "change" },
{ min: 2, max: 30, message: "长度在2到30个字符", trigger: "blur" },
],
orderBy: [
{ required: true, message: "排序不能为空", trigger: ["change", "blur"] },
{
type: "number",
message: "排序必须为数字",
trigger: ["change", "blur"],
},
],
},
});
const fnClose = () => {
formRef.value.resetFields();
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await validate();
if (props.type === "add") {
const { dictionaries } = await getDataDictionaryRepeat({
bianma: form.value.bianma,
});
if (dictionaries) {
ElMessage.error("添加失败,编码重复");
return;
}
await setDataDictionaryAdd({
...form.value,
parentId: props.parentId,
dictionariesId: undefined,
});
}
if (props.type === "edit")
await setDataDictionaryEdit({
...form.value,
});
ElMessage.success("操作成功");
fnClose();
emits("getData");
},
{ atBegin: true }
);
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,130 @@
<template>
<div>
<app-table v-model:pagination="pagination" :data="list" @get-data="getData">
<el-table-column label="名称">
<template #default="{ row }">
<el-button
type="primary"
text
link
@click="
router.push({
path: '/system_management/data_dictionary',
query: {
parentName: row.name,
parentId: row.dictionariesId,
},
})
"
>
{{ row.name }} <el-icon><arrow-right /></el-icon>
</el-button>
</template>
</el-table-column>
<el-table-column prop="bianma" label="编码" />
<el-table-column prop="dictionariesId" label="ID" width="300" />
<el-table-column prop="orderBy" label="排序" width="50" />
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button
type="primary"
text
link
@click="fnAddOrEdit(row.dictionariesId, 'edit')"
>
编辑
</el-button>
<el-button
type="primary"
text
link
@click="fnDelete(row.dictionariesId)"
>
删除
</el-button>
</template>
</el-table-column>
<template #button>
<el-button type="primary" @click="fnAddOrEdit('', 'add')">
新增
</el-button>
<el-button
v-if="parentId !== '0'"
:icon="ArrowLeft"
@click="router.back()"
>
返回
</el-button>
</template>
</app-table>
<add
v-model:form="data.addOrEditDialog.form"
v-model:visible="data.addOrEditDialog.visible"
:parent-name="parentName"
:parent-id="parentId"
:type="data.addOrEditDialog.type"
@get-data="resetPagination"
/>
</div>
</template>
<script setup>
import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
import { reactive, ref } from "vue";
import { useRouter, onBeforeRouteUpdate, useRoute } from "vue-router";
import {
setDataDictionaryDelete,
getDataDictionaryList,
getDataDictionaryInfo,
} from "@/request/system_management.js";
import { ElMessage, ElMessageBox } from "element-plus";
import Add from "./components/add.vue";
import AppTable from "@/components/table/index.vue";
import useListData from "@/hooks/useListData.js";
const router = useRouter();
const route = useRoute();
const parentIdDefault = "0";
const parentNameDefault = "(无)此项为顶级菜单";
const parentId = ref(route.query.parentId || parentIdDefault);
const parentName = ref(route.query.parentName || parentNameDefault);
const { list, pagination, getData, resetPagination } = useListData(
getDataDictionaryList,
{
params: () => ({ parentId: parentId.value }),
}
);
const data = reactive({
addOrEditDialog: {
visible: false,
type: "",
form: {
name: "",
bianma: "",
orderBy: "",
descr: "",
},
},
});
onBeforeRouteUpdate((to) => {
parentId.value = to.query.parentId || parentIdDefault;
parentName.value = to.query.parentName || parentNameDefault;
resetPagination();
});
const fnDelete = async (dictionariesId) => {
await ElMessageBox.confirm(`确定要删除吗?`, { type: "warning" });
await setDataDictionaryDelete({ dictionariesId });
ElMessage.success("删除成功");
resetPagination();
};
const fnAddOrEdit = async (dictionariesId, type) => {
data.addOrEditDialog.visible = true;
data.addOrEditDialog.type = type;
if (type === "edit") {
const resData = await getDataDictionaryInfo({ dictionariesId });
data.addOrEditDialog.form = resData.dictionaries;
}
};
</script>
<style scoped></style>

View File

@ -0,0 +1,213 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'edit' ? '修改' : '新增'"
:before-close="fnClose"
>
<el-form
ref="formRef"
:model="form"
:rules="data.rules"
label-width="100px"
style="margin-top: 20px"
>
<el-row>
<el-col :span="24">
<el-form-item label="防涝点名称" prop="pointName">
<el-input v-model="form.pointName" placeholder="请输入防涝点名称" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="属地" prop="area">
<layout-cascader
v-model="form.area"
v-model:name="form.areaName"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="false"
:level="4"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="类型" prop="pointType">
<el-select v-model="form.pointType" placeholder="请选择类型">
<el-option
v-for="item in pointTypeList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="当前水位" prop="currentWaterLevel">
<el-input
v-model="form.currentWaterLevel"
placeholder="请输入当前水位"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remarks">
<el-input
v-model="form.remarks"
type="textarea"
:rows="3"
placeholder="请输入备注"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<div style="display: flex">
<el-form-item label="经度" prop="longitude" style="flex: 1">
<el-input
v-model="form.longitude"
placeholder="请选择经度"
/>
</el-form-item>
<el-form-item label="纬度" prop="latitude" style="flex: 1">
<el-input
v-model="form.latitude"
placeholder="请选择纬度"
/>
</el-form-item>
<el-form-item label-width="10px">
<el-button
class="ml-10"
type="primary"
@click="fnSelectedPosition"
>点击定位</el-button
>
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="fnClose"> </el-button>
<el-button type="primary" @click="fnSubmit"> </el-button>
</template>
<app-map
v-model:visible="mapDialog.visible"
v-model:longitude="mapDialog.longitude"
v-model:latitude="mapDialog.latitude"
@submit="fnMapSubmit"
/>
</el-dialog>
</template>
<script setup>
import { debounce } from "throttle-debounce";
import useFormValidate from "@/hooks/useFormValidate.js";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import { getDataDictionary } from "@/request/data_dictionary.js";
import { ElMessage } from "element-plus";
import { reactive, ref } from "vue";
import { useVModels } from "@vueuse/core";
import AppMap from "@/components/map/map.vue";
import {
setUrbanFloodPointAdd,
setUrbanFloodPointUpdate,
} from "@/request/kangzai.js";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
type: {
type: String,
required: true,
default: "",
},
form: {
type: Object,
required: true,
default: () => ({}),
},
});
const { validate, formRef } = useFormValidate();
const emits = defineEmits(["update:visible", "update:form", "getData"]);
const { visible, form } = useVModels(props, emits);
const pointTypeList = ref([]);
const fnGetPointTypeList = async () => {
const resData = await getDataDictionary({
parentId: "d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s", // ID
});
pointTypeList.value = resData.dictionariesList || [];
};
fnGetPointTypeList();
const data = reactive({
rules: {
pointName: [
{ required: true, message: "请输入防涝点名称", trigger: "blur" },
],
area: [{ required: true, message: "请选择区域", trigger: "blur" }],
pointType: [{ required: true, message: "请选择类型", trigger: "change" }],
longitude: [{ required: true, message: "请输入经度", trigger: "blur" }],
latitude: [{ required: true, message: "请输入纬度", trigger: "blur" }],
},
});
const mapDialog = ref({
visible: false,
longitude: "",
latitude: "",
});
const fnSelectedPosition = () => {
mapDialog.value.visible = true;
mapDialog.value.longitude = form.value.longitude;
mapDialog.value.latitude = form.value.latitude;
};
const fnMapSubmit = ({ longitude, latitude }) => {
form.value.longitude = longitude;
form.value.latitude = latitude;
};
const fnClose = () => {
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await validate();
const param = {
...form.value,
areaName: form.value.areaName.join("-"),
province: form.value.area[0],
city: form.value.area[1],
county: form.value.area[2],
village: form.value.area[3],
street: form.value.area[4],
};
delete param.area;
if (props.type === "add") {
await setUrbanFloodPointAdd(param);
} else if (props.type === "edit") {
await setUrbanFloodPointUpdate(param);
}
ElMessage.success("操作成功");
fnClose();
emits("getData");
},
{ atBegin: true }
);
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,168 @@
<template>
<div>
<app-search v-model="searchForm" @submit="resetPagination">
<el-col :span="6">
<el-form-item label="防涝点名称" prop="pointName">
<el-input
v-model="searchForm.pointName"
placeholder="请输入防涝点名称"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="属地" prop="area">
<layout-cascader
v-model="searchForm.area"
:dictionaries-id="'e725d2a91b8248f4b8f49889038df7de'"
:check-strictly="true"
:level="5"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="类型" prop="pointType">
<el-select v-model="searchForm.pointType" placeholder="请选择类型">
<el-option
v-for="item in pointTypeList"
:key="item.bianma"
:label="item.name"
:value="item.bianma"
/>
</el-select>
</el-form-item>
</el-col>
</app-search>
<app-table v-model:pagination="pagination" :data="list" @get-data="getData">
<el-table-column prop="pointName" label="防涝点名称" />
<el-table-column prop="areaName" label="属地" />
<el-table-column label="类型" prop="pointTypeLabel" />
<el-table-column prop="currentWaterLevel" label="当前水位" />
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button type="primary" text link @click="fnAddOrEdit(row, 'edit')"
>编辑</el-button
>
<el-button
type="primary"
text
link
@click="fnDelete(row.urbanFloodPointId)"
>删除</el-button
>
</template>
</el-table-column>
<template #button>
<el-button type="primary" @click="fnAddOrEdit('', 'add')"
>新增</el-button
>
</template>
</app-table>
<add
v-model:form="data.addOrEditDialog.form"
v-model:visible="data.addOrEditDialog.visible"
:type="data.addOrEditDialog.type"
@get-data="resetPagination"
/>
</div>
</template>
<script setup>
import { ref, reactive, nextTick } from "vue";
import { getDataDictionary } from "@/request/data_dictionary.js";
import { ElMessage, ElMessageBox } from "element-plus";
import LayoutCascader from "@/components/layout_cascader/index.vue";
import AppTable from "@/components/table/index.vue";
import AppSearch from "@/components/search/index.vue";
import useListData from "@/hooks/useListData.js";
import {
getUrbanFloodPointPage,
setUrbanFloodPointDelete,
} from "@/request/kangzai.js";
import Add from "./components/add.vue";
//
const pointTypeList = ref([]);
const fnGetPointTypeList = async () => {
const resData = await getDataDictionary({
parentId: "d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s", // ID
});
pointTypeList.value = resData.dictionariesList || [];
};
fnGetPointTypeList();
const { list, pagination, searchForm, resetPagination, getData } = useListData(
getUrbanFloodPointPage,
{
beforeGetData: (searchForm) => {
const area = searchForm.area || [];
searchForm.province = area[0];
searchForm.city = area[1];
searchForm.county = area[2];
searchForm.village = area[3];
searchForm.street = area[4];
},
callback: (list) => {
list.forEach((item) => {
item.pointTypeLabel =
pointTypeList.value.find((dict) => dict.bianma === item.pointType)
?.name || item.pointType;
});
},
}
);
const data = reactive({
addOrEditDialog: {
visible: false,
type: "",
form: {
pointName: "",
province: "",
city: "",
county: "",
village: "",
street: "",
area: [],
areaName: [],
pointType: "",
currentWaterLevel: "",
longitude: "",
latitude: "",
remarks: "",
},
},
});
const fnDelete = async (urbanFloodPointId) => {
await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
await setUrbanFloodPointDelete({ urbanFloodPointId });
ElMessage.success("删除成功");
resetPagination();
};
const fnAddOrEdit = async (row, type) => {
data.addOrEditDialog.visible = true;
await nextTick();
data.addOrEditDialog.form = {};
data.addOrEditDialog.type = type;
if (type === "edit") {
data.addOrEditDialog.form = row;
data.addOrEditDialog.form.area = [
row.province,
row.city,
row.county,
row.village,
row.street,
];
data.addOrEditDialog.form.areaName = row.areaName.split("-");
}
};
</script>
<style scoped></style>