必到点采集模块并且展示地图方格 新增任务进度展示所有方格和任务处理

This commit is contained in:
maojiacai
2025-09-12 11:45:52 +08:00
parent 94bd692870
commit d48e876ec1
12 changed files with 1187 additions and 29 deletions

View File

@ -0,0 +1,57 @@
import request from "@/utils/request";
const api = "/mosty-api/mosty-yjzl";
// 获取方格数据
export function fetchTbZdxlFgdwSelectList() {
return request({
url: `${api}/tbZdxlFgdw/selectList`
})
}
// 查询任务列表
export function fetchTbZdxlFgxlrwSelectList(params) {
return request({
url: `${api}/tbZdxlFgxlrw/selectList`,
params
})
}
export function fetchSelectListByBddxlrwId(params) {
return request({
url: `${api}/tbZdxlFgdwBddxlrwJl/selectListByBddxlrwId`,
params
})
}
// 必到点采集列表
export function fetchTbZdxlFgdwBddSelectPage(params) {
return request({
url: `${api}/tbZdxlFgdwBdd/selectPage`,
params
})
}
// 新增必到点采集数据
export function fetchTbZdxlFgdwBddSave(data) {
return request({
url: `${api}/tbZdxlFgdwBdd/save`,
method: 'POST',
data
})
}
// 修改新增必到点采集数据
export function fetchTbZdxlFgdwBddUpdate(data) {
return request({
url: `${api}/tbZdxlFgdwBdd/update`,
method: "POST",
data
})
}
// 获取方格详情
export function fetchTbZdxlFgdwId(id) {
return request({
url: `${api}/tbZdxlFgdw/${id}`
})
}

BIN
src/assets/lz/dw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -249,6 +249,11 @@ onMounted(() => {
let coords = [centerPoint.lng, centerPoint.lat]; let coords = [centerPoint.lng, centerPoint.lat];
emitter.emit("getcentercoord", coords); emitter.emit("getcentercoord", coords);
}); });
// 获取点击经纬度
emitter.on("getMapClickCoordinates", (res) => {
mapUtil.value.enableClickEvents()
})
}); });
//切换地图底图 //切换地图底图
const onMapImageChange = (val) => { const onMapImageChange = (val) => {
@ -329,6 +334,7 @@ onUnmounted(() => {
emitter.off("diffusionCircle"); emitter.off("diffusionCircle");
emitter.off("SsCircle"); emitter.off("SsCircle");
emitter.off("ClearssCircle"); emitter.off("ClearssCircle");
emitter.off("getMapClickCoordinates")
}); });
</script> </script>

View File

@ -697,6 +697,31 @@ export function MapUtil(map) {
break; break;
} }
} }
/** 获取经纬度*/
MapUtil.prototype.enableClickEvents = function() {
const _that = this;
if (_that.clickEventHandler) {
console.log('点击事件已经启用');
return;
}
// 添加点击事件处理函数
_that.clickEventHandler = (e) => {
const lng = e.lngLat.lng;
const lat = e.lngLat.lat;
emitter.emit("mapClickCoordinates", {
lng,
lat,
coordinates: [lng, lat],
timestamp: new Date().getTime()
});
};
_that.mMap.mapboxGLMap.on('click', _that.clickEventHandler);
};
} }
// 获取uuid 作为边界图层ID // 获取uuid 作为边界图层ID
function getUUid() { function getUUid() {

View File

@ -120,7 +120,7 @@ const props = defineProps({
}, },
selectable: { selectable: {
type: Function, type: Function,
default: () => {} default: () => []
} }
}); });
// 可选的时候选择的数据 // 可选的时候选择的数据

View File

@ -559,23 +559,41 @@ export const privateRoutes = [{
} }
}, },
{ {
path: "/service/taskPage", path: "/taskPage",
component: () => import("@/views/backOfficeSystem/service/taskPage/dailyTaskPackage/index"),
name: "taskPage", name: "taskPage",
redirect: "/taskPageIndex",
meta: { meta: {
title: '任务清单', title: '任务清单',
icon: "article-ranking" icon: "article-ranking"
}, },
children: [ children: [
{ {
path: "/taskPageIndex", path: "/taskPage/taskPageIndex",
component: () => import("@/views/backOfficeSystem/service/taskPage/dailyTaskPackage/index"), component: () => import("@/views/backOfficeSystem/service/taskPage/dailyTaskPackage/index"),
name: "taskPageIndex", name: "taskPageIndex",
meta: { meta: {
title: '日常任务包', title: '日常任务包',
icon: "article-ranking" icon: "article-ranking"
}, },
} },
{
path: "/taskPage/taskProgress",
component: () => import("@/views/backOfficeSystem/service/taskPage/taskProgress/index"),
name: "taskProgress",
meta: {
title: '任务进度',
icon: "article-ranking"
},
},
{
path: "/taskPage/collectPage",
component: () => import("@/views/backOfficeSystem/service/taskPage/collectPage/index"),
name: "collectPage",
meta: {
title: '必到点采集',
icon: "article-ranking"
},
},
] ]
}, },
{ {

View File

@ -0,0 +1,306 @@
<template>
<div>
<div v-if="dialogFormVisible" class="dialog">
<div class="head_box">
<span class="title">{{ title }}</span>
<div>
<el-button
v-if="!infoActive"
type="primary"
size="small"
@click="submit"
:loading="btnLoading"
>保存</el-button
>
<el-button size="small" @click="close">关闭</el-button>
</div>
</div>
<div class="dialogWrapper">
<el-form
ref="formRef"
:model="form"
:rules="rules"
:inline="true"
:disabled="infoActive"
>
<el-form-item prop="bddMc" label="必到点名称:">
<el-input
style="width: 80%;"
min="0"
placeholder="请填写必到点名称"
v-model="form.bddMc"
></el-input>
</el-form-item>
<el-form-item prop="fgdwId" label="所属方格:">
<el-select
:disabled="typeStatus === 'del'"
placeholder="请选择所属方格"
filterable
clearable
v-model="form.fgdwId"
@change="handleChange"
>
<el-option
v-for="item in fgList"
:key="item?.id"
:label="item?.mc"
:value="item?.id"
/>
</el-select>
</el-form-item>
<el-form-item prop="bddDz" label="必到点地址:">
<el-input
placeholder="请填必到点地址"
clearable
v-model="form.bddDz"
></el-input>
</el-form-item>
<el-form-item prop="jd" label="经度:">
<el-input
disabled
placeholder="请填写经度"
v-model="form.jd"
></el-input>
</el-form-item>
<el-form-item prop="wd" label="纬度:">
<el-input
disabled
placeholder="请填写纬度"
v-model="form.jd"
></el-input>
</el-form-item>
</el-form>
<div style="height: calc(100vh - 336px)">
<gd-map />
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, getCurrentInstance, computed, onMounted } from "vue";
import { fetchTbZdxlFgdwSelectList, fetchTbZdxlFgdwBddSave, fetchTbZdxlFgdwBddUpdate, fetchTbZdxlFgdwId } from "@/api/service/taskProgress";
const { proxy } = getCurrentInstance();
import GdMap from "@/components/Map/GdMap/index.vue"
import emitter from "@/utils/eventBus";
const btnLoading = ref(false); //按钮截流
const title = ref("新增必到点采集");
const formRef = ref(null);
const infoActive = ref(true);
const visible = ref(false);
const fgList = ref([])
const typeStatus = ref("")
//级联选择器配置
const props = defineProps({
modelValue: {
type: Boolean,
default: false
}
});
const emits = defineEmits(['update:modelValue', 'ok']);
const dialogFormVisible = computed({
get() {
return props.modelValue;
},
set(val) {
if (!infoActive.value && typeStatus?.value !== 'del') {
// 获取经纬度
setTimeout(() => {
emitter.emit("getMapClickCoordinates");
// 更新地图标注位置
emitter.on("mapClickCoordinates", async (res) => {
emitter.emit("deletePointArea")
form.value.jd = res.lng
form.value.wd = res.lat
emitter.emit("addPointArea", {
coords: [{ jd: res.lng, wd: res.lat }],
coordinates: res?.coordinates,
icon: require("@/assets/lz/dw.png"),
sizeX: 30,
sizeY: 35
});
})
}, 500);
} else {
emitter.off('getMapClickCoordinates')
}
emits("update:modelValue", val);
}
})
//表单数据
const form = ref({
bddList: []
});
//表单验证
const rules = reactive({
bddMc: [
{
required: true,
message: "请填写必到点名称",
trigger: "change"
}
],
fgdwId: [
{
required: true,
message: "请选择所属方格",
trigger: "change"
}
],
bddDz: [
{
required: true,
message: "请填必到点地址",
trigger: "change"
}
],
jd: [
{
required: true,
message: "请填写经度",
trigger: "change"
}
],
wd: [
{
required: true,
message: "请填写纬度",
trigger: "change"
}
],
});
// 获取方格
const getFgList = async () => {
const res = await fetchTbZdxlFgdwSelectList()
if (res && res?.length > 0) {
fgList.value = res
}
}
// 获取方格详情
const getFgDetails = async (id, type) => {
const res = await fetchTbZdxlFgdwId(id)
if (res) {
setMap(res, type)
}
}
//新增
function open(row = {}, type) {
typeStatus.value = type
infoActive.value = type === 'view';
form.value = { ...row, xfbbName: row?.xfxz };
if (type === 'view') {
title.value = "查看信息"
} else if (type === 'del') {
title.value = "编辑必到点采集"
} else {
title.value = "新增必到点采集"
}
if (type === 'view' || type === 'del') {
getFgDetails(form?.value?.fgdwId , !(type === 'view' || type === 'del'))
}
dialogFormVisible.value = true;
}
//关闭弹窗
function close() {
formRef.value.resetFields();
dialogFormVisible.value = false;
}
const handleChange = (val) => {
const data = fgList?.value?.find(i => i?.id === val)
setMap(data)
}
const setMap = (data, type = true) => {
emitter.emit("deletePointArea", "addfg")
const { x1 = "", y1 = "", x2 = "", y2 = "", id = "", zxX = "", zxY = "", mc = "" } = data
const centerPoint = [zxX, zxY]
const position = [[Number(x1),Number(y1)],[Number(x2),Number(y2)]]
const text = mc
const obj = [{ position: position, text, id, userData: data}]
emitter.emit("echoPlane", { fontColor:'#12fdb8',coords: obj, type:'rectangle', flag:'addfg', color:'rgba(2,20,51,0.5)', linecolor:'#1C97FF'})
emitter.emit("setMapCenter", { location: centerPoint, zoomLevel: 14 });
// 更新地图标注位置
if (type) return
emitter.emit("deletePointArea")
emitter.emit("addPointArea", {
coords: [{ jd: form.value?.jd, wd: form.value?.wd }],
coordinates: [form.value?.jd, form.value?.wd],
icon: require("@/assets/lz/dw.png"),
sizeX: 30,
sizeY: 35
});
}
//提交
const submit = async () => {
try {
await formRef.value.validate()
if (form.value?.id) {
await fetchTbZdxlFgdwBddSave(form.value)
} else {
await fetchTbZdxlFgdwBddUpdate(form.value)
}
proxy.$message({
type: "success",
message: "修改成功"
});
dialogFormVisible.value = false;
emits("ok")
} catch (error) {
console.log(error);
}
}
onMounted(() => {
getFgList()
})
defineExpose({ open })
</script>
<style lang="scss" scoped>
@import "~@/assets/css/layout.scss";
@import "~@/assets/css/element-plus.scss";
.dialog {
.dialogWrapper {
margin: 20px 20px 0 20px;
.el-form--inline {
padding: 0 0 0rem 0;
}
}
}
.label {
margin-bottom: 1rem;
}
</style>

View File

@ -0,0 +1,223 @@
<script setup>
import { computed, getCurrentInstance, onMounted, reactive, ref } from "vue";
const { proxy } = getCurrentInstance();
import PageTitle from "@/components/aboutTable/PageTitle.vue";
import Search from "@/components/aboutTable/Search.vue";
import MyTable from "@/components/aboutTable/MyTable.vue";
import Pages from "@/components/aboutTable/Pages.vue";
import AddFgFormDialog from "./AddFgFormDialog.vue"
import { fetchTbZdxlFgdwBddSelectPage } from "@/api/service/taskProgress";
const searchConfiger = reactive([
{
showType: "input",
prop: "bddMc",
placeholder: "请输入必到点名称",
label: "必到点名称"
},
{
showType: "input",
prop: "bddDz",
placeholder: "请输入必到点地址",
label: "必到点地址"
}
])
const searchBox = ref() //搜索框
const deleteIds = ref([]) // 删除id
const dialogFormVisible = ref(false);
const pageData = reactive({
visible: true,
tableData: [], //表格数据
keyCount: 0,
tableConfiger: {
rowHieght: 61,
loading:false
},
total: 0,
pageConfiger: {
pageSize: 10,
pageCurrent: 1
}, //分页
controlsWidth: 210, //操作栏宽度
tableColumn: [
{
label: "必到点名称",
prop: "bddMc"
},
{
label: "所属方格",
prop: "fgdwMc",
},
{
label: "必到点地址",
prop: "bddDz",
},
]
})
const addFgFormRef = ref(null);
const addEdit = (row = {}, type = "") => {
addFgFormRef.value?.open(row, type)
}
// 删除
const delDictItem = (id) => {
batchDelete(id)
}
const chooseData = (val) => {
if (val?.length > 0) {
deleteIds.value = val?.map((item) => item.id);
} else {
deleteIds.value = []
}
}
const onSearch = (val) => {
const { cz, ...ret } = val
pageData.pageConfiger.pageCurrent = 1;
getListData(ret);
}
// 表格高度计算
const tabHeightFn = () => {
pageData.tableHeight = window.innerHeight - searchBox.value.offsetHeight - 244;
window.onresize = function () { tabHeightFn(); };
};
const getListData = async (params = {}) => {
pageData.tableConfiger.loading = true
try {
const res = await fetchTbZdxlFgdwBddSelectPage({
...pageData.pageConfiger,
...params
})
pageData.tableData = res?.records || []
pageData.total = res.total;
pageData.tableConfiger.loading = false;
} catch (err) {
pageData.tableConfiger.loading = false;
}
}
const changeNo = (val) => {
pageData.pageConfiger.pageCurrent = val;
getListData();
}
const changeSize = (val) => {
pageData.pageConfiger.pageSize = val;
getListData();
}
const handleFilter = () => {
pageData.pageConfiger.pageCurrent = 1;
getListData();
}
// 删除的方法
//批量删除数据
const batchDelete = async (id) => {
await proxy.$confirm("确定要删除", "警告", { type: "warning" })
try {
// await zbPbDelete(id)
proxy.$message({
message: "删除成功",
type: "success"
});
getListData();
} catch (error) {
console.log(error);
}
};
// 批量删除
const handleDel = () => {
if (deleteIds.value.length === 0) {
proxy.$message({
message: "请选择勾选要删除的数据",
type: "warning"
});
return
}
batchDelete(deleteIds)
}
onMounted(() => {
proxy.mittBus.on("mittFn", (data) => { pageData.keyCount = data; });
tabHeightFn()
getListData()
})
</script>
<template>
<div class="container">
<div class="titleBox">
<PageTitle title="必到点采集">
<el-button type="primary" @click="addEdit({}, 'add')">
<span style="vertical-align: middle">新增</span>
</el-button>
<!-- <el-button typeof="danger" :disabled="deleteIds.length === 0" @click="handleDel">-->
<!-- <el-icon style="vertical-align: middle"><Delete /> </el-icon>-->
<!-- <span style="vertical-align: middle">批量删除</span>-->
<!-- </el-button>-->
</PageTitle>
</div>
<div ref="searchBox">
<Search :searchArr="searchConfiger" @submit="onSearch" />
</div>
<div class="tabBox">
<MyTable
:tableData="pageData.tableData"
:tableColumn="pageData.tableColumn"
:tableHeight="pageData.tableHeight"
:key="pageData.keyCount"
:tableConfiger="pageData.tableConfiger"
:controlsWidth="pageData.controlsWidth"
@chooseData="chooseData"
>
<!-- 操作 -->
<template #controls="{ row }">
<el-button size="small" @click="addEdit(row, 'del')">编辑</el-button>
<el-button size="small" @click="addEdit(row, 'view')">查看详情</el-button>
<!-- <el-button size="small" @click="delDictItem([row.id])">删除</el-button>-->
</template>
</MyTable>
<Pages
@changeNo="changeNo"
@changeSize="changeSize"
:tableHeight="pageData.tableHeight"
:pageConfiger="{
...pageData.pageConfiger,
total: pageData.total
}"
/>
</div>
<add-fg-form-dialog ref="addFgFormRef" v-model="dialogFormVisible" @ok="handleFilter" />
</div>
</template>
<style scoped lang="scss">
.container {
::v-deep {
.check, .el-upload-text {
display: none;
}
.el-descriptions__body {
background-color: transparent !important;
color: #fff;
}
}
}
</style>

View File

@ -49,21 +49,31 @@
<el-table-column type="selection" width="40" align="center" /> <el-table-column type="selection" width="40" align="center" />
<el-table-column type="index" show-overflow-tooltip align="center" width="60px" label="序号" /> <el-table-column type="index" show-overflow-tooltip align="center" width="60px" label="序号" />
<el-table-column prop="ssbm" show-overflow-tooltip align="center" label="巡防部门" /> <el-table-column prop="ssbm" show-overflow-tooltip align="center" label="巡防部门" />
<el-table-column prop="xfrq" show-overflow-tooltip align="center" label="巡防日期"> <!-- <el-table-column prop="xfrq" show-overflow-tooltip align="center" label="巡防日期">
<template #default="{ row }"> <template #default="{ row }">
<div> {{ row.xfrq ? row.xfrq.substring(0, 10) : row.xfrq }} </div> <div> {{ row.xfrq ? row.xfrq.substring(0, 10) : row.xfrq }} </div>
</template> </template>
</el-table-column> </el-table-column> -->
<el-table-column prop="kssj" show-overflow-tooltip align="center" label="计划开始时间" v-if="!isShowMap" /> <!-- <el-table-column prop="kssj" show-overflow-tooltip align="center" label="计划开始时间" v-if="!isShowMap" />
<el-table-column prop="jssj" show-overflow-tooltip align="center" label="计划结束时间" v-if="!isShowMap" /> <el-table-column prop="jssj" show-overflow-tooltip align="center" label="计划结束时间" v-if="!isShowMap" />
<el-table-column prop="bbkssj" show-overflow-tooltip align="center" label="报备开始时间" /> <el-table-column prop="bbkssj" show-overflow-tooltip align="center" label="报备开始时间" />
<el-table-column prop="bbjssj" show-overflow-tooltip align="center" label="报备结束时间" /> <el-table-column prop="bbjssj" show-overflow-tooltip align="center" label="报备结束时间" /> -->
<el-table-column prop="fzrXm" show-overflow-tooltip align="center" label="负责人" v-if="!isShowMap" /> <el-table-column prop="fzrXm" show-overflow-tooltip align="center" label="负责人" v-if="!isShowMap" />
<el-table-column prop="jlList" show-overflow-tooltip align="center" label="民警" v-if="!isShowMap"> <el-table-column prop="jlList" show-overflow-tooltip align="center" label="民警" v-if="!isShowMap">
<template #default="{ row }"> <template #default="{ row }">
<el-tag class="mx-1" v-for="item in row.jlList" v-show="item.jllx == '01'" :key="item.id">{{ item.jlxm <el-checkbox-group v-model="formData.xfbbQwJlId" @change="handleChange($event, row)">
}}</el-tag> <template v-for="(item, index) in row.jlList" :key="index">
<el-checkbox
v-show="item.jllx == '01'"
:label="item.id"
:disabled="formData.xfbbQwJlId?.[0] !== item.id && formData.xfbbQwJlId?.length > 0"
>{{ item.jlxm }}
</el-checkbox>
</template>
</el-checkbox-group>
<!-- <el-tag class="mx-1" v-for="item in row.jlList" v-show="item.jllx == '01'" :key="item.id">{{ item.jlxm
}}</el-tag> -->
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="jlList" show-overflow-tooltip align="center" label="辅警" v-if="!isShowMap"> <el-table-column prop="jlList" show-overflow-tooltip align="center" label="辅警" v-if="!isShowMap">
@ -72,7 +82,7 @@
}}</el-tag> }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="wzlx" show-overflow-tooltip align="center" label="武装类型" v-if="!isShowMap"> <!-- <el-table-column prop="wzlx" show-overflow-tooltip align="center" label="武装类型" v-if="!isShowMap">
<template #default="{ row }"> <template #default="{ row }">
<dict-tag :options="D_BZ_WZLX" :value="row.wzlx" :tag="false"></dict-tag> <dict-tag :options="D_BZ_WZLX" :value="row.wzlx" :tag="false"></dict-tag>
</template> </template>
@ -93,7 +103,7 @@
<template #default="{ row }"> <template #default="{ row }">
<dict-tag :options="D_BZ_BBFS" :value="row.bblx" :tag="false"></dict-tag> <dict-tag :options="D_BZ_BBFS" :value="row.bblx" :tag="false"></dict-tag>
</template> </template>
</el-table-column> </el-table-column> -->
<!-- <el-table-column label="操作" align="right" fixed="right" :width="isShowMap ? '200px' : '390px'">--> <!-- <el-table-column label="操作" align="right" fixed="right" :width="isShowMap ? '200px' : '390px'">-->
<!-- <template #default="{ row }">--> <!-- <template #default="{ row }">-->
<!-- <el-button @click="lookGj(row, 'gjhf')" size="small">轨迹回放</el-button>--> <!-- <el-button @click="lookGj(row, 'gjhf')" size="small">轨迹回放</el-button>-->
@ -331,10 +341,13 @@ const dialogFormVisible = ref(false); //弹窗
const isShowMap = ref(false) //展示地图 const isShowMap = ref(false) //展示地图
const formData = ref({})
//搜索数据 //搜索数据
const listQuery = ref({ const listQuery = ref({
pageCurrent: 1, pageCurrent: 1,
pageSize: 20 pageSize: 20,
// bbzt: 0
}); });
//表单数据 //表单数据
const form = ref({}); const form = ref({});
@ -1007,7 +1020,7 @@ function delDictItem(row) {
//批量数据 //批量数据
const multipleUserRef = ref(null); const multipleUserRef = ref(null);
const multipleSelectionUser = ref([]) const multipleSelectionUser = ref([])
const handleSelectionChange = (val) => { const handleSelectionChange = async (val) => {
// ids.value = []; // ids.value = [];
// if (val) { // if (val) {
// val.forEach((item) => { // val.forEach((item) => {
@ -1015,17 +1028,40 @@ const handleSelectionChange = (val) => {
// }); // });
// } // }
await nextTick()
if (val?.length > 1) { if (val?.length > 1) {
let del_row = val?.shift(); let del_row = val?.shift();
multipleUserRef.value.toggleRowSelection(del_row, false); multipleUserRef.value.toggleRowSelection(del_row, false);
} }
multipleSelectionUser.value = val; multipleSelectionUser.value = val;
if (multipleSelectionUser.value?.[0]?.id !== formData.value?.id) {
formData.value.xfbbQwJlId = []
formData.value.id = ''
}
}; };
const handleClickClose = () => { const handleClickClose = () => {
// emits("selectionChange", []) // emits("selectionChange", [])
visible.value = false visible.value = false
} }
const handleChange = async (val, obj) => {
if (val?.length > 1) {
let del_row = val?.shift();
}
console.log(val);
formData.xfbbQwJlId = val
formData.value.id = obj?.id
await nextTick()
multipleUserRef.value.toggleRowSelection(obj, true)
multipleSelectionUser.value = [obj]
}
// 确定 // 确定
const onSubmit = () => { const onSubmit = () => {
if (multipleSelectionUser.value.length === 0) { if (multipleSelectionUser.value.length === 0) {
@ -1035,7 +1071,14 @@ const onSubmit = () => {
}); });
return return
} }
emits("selectionChange", multipleSelectionUser.value)
const obj = multipleSelectionUser.value?.[0]?.jlList?.find(i => i?.id == formData.value?.xfbbQwJlId[0])
emits("selectionChange", {
list: multipleSelectionUser.value,
xfbbName: `${multipleSelectionUser.value?.[0]?.fzrXm}-${obj?.jlxm}`,
...formData?.value
})
visible.value = false visible.value = false
} }

View File

@ -99,8 +99,38 @@
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-row style="width:100%;">
<el-col>
<MyTable
:tableData="form?.bddList"
:tableColumn="pageData.tableColumn"
:tableConfiger="pageData.tableConfiger"
>
<template #xlghDkcs="{ row, $index }">
<div class="validation-container">
<el-input-number
style="width: 30%;"
:placeholder="`输入打卡点${$index + 1}需打卡次数`"
clearable
min="0"
v-model="row.xlghDkcs"
:class="{ 'is-error': hasError($index) }"
@blur="validateField($index)"
/>
<!-- 展示错误信息 -->
<span
v-if="hasError($index)"
class="error-message"
>
{{ getError($index) }}
</span>
</div>
</template>
</MyTable>
<div style="display: none">
<template v-for="(item, index) in form?.bddList" :key="index"> <template v-for="(item, index) in form?.bddList" :key="index">
<el-form-item :prop="`bddList[${index}].xlghDkcs`" :label="`打卡点${index + 1}:`" :rules="checkPointRules"> <el-form-item :prop="`bddList[${index}].xlghDkcs`" :label="item?.bddMc" :rules="checkPointRules">
<el-input-number <el-input-number
style="width: 100%;" style="width: 100%;"
:placeholder="`输入打卡点${index + 1}需打卡次数`" :placeholder="`输入打卡点${index + 1}需打卡次数`"
@ -109,6 +139,9 @@
/> />
</el-form-item> </el-form-item>
</template> </template>
</div>
</el-col>
</el-row>
</el-form> </el-form>
</div> </div>
</div> </div>
@ -121,6 +154,7 @@
import { ref, reactive, getCurrentInstance, computed } from "vue"; import { ref, reactive, getCurrentInstance, computed } from "vue";
import { updateData } from "@/api/service/dailyTaskPackage"; import { updateData } from "@/api/service/dailyTaskPackage";
import DictTag from "@/components/DictTag/index.vue"; import DictTag from "@/components/DictTag/index.vue";
import MyTable from "@/components/aboutTable/MyTable.vue";
import DistributePatrolTeamDialog import DistributePatrolTeamDialog
from "@/views/backOfficeSystem/service/taskPage/dailyTaskPackage/components/DistributePatrolTeamDialog.vue"; from "@/views/backOfficeSystem/service/taskPage/dailyTaskPackage/components/DistributePatrolTeamDialog.vue";
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
@ -131,6 +165,27 @@ const formRef = ref(null);
const infoActive = ref(true); const infoActive = ref(true);
const visible = ref(false); const visible = ref(false);
const pageData = reactive({
tableConfiger: {
rowHieght: 61,
haveControls: false,
showSelectType: 'checkBox',
loading: false
},
total: 0,
tableColumn: [
{
label: "必到点",
prop: "bddMc"
},
{
label: "打卡次数",
prop: "xlghDkcs",
showSolt:true
},
]
})
//级联选择器配置 //级联选择器配置
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -152,7 +207,7 @@ const dialogFormVisible = computed({
//表单数据 //表单数据
const form = ref({ const form = ref({
bddList: [] bddList: [{ xlghDkcs: 1 }, { xlghDkcs: "" }]
}); });
// 打卡点验证规则 // 打卡点验证规则
@ -213,6 +268,10 @@ const rules = reactive({
function open(row = {}, type) { function open(row = {}, type) {
infoActive.value = type === 'view'; infoActive.value = type === 'view';
form.value = { ...row, xfbbName: row?.xfxz }; form.value = { ...row, xfbbName: row?.xfxz };
form.value?.bddList?.forEach((item, index) => {
item.index = index // 直接添加index属性
})
title.value = "编辑信息"; title.value = "编辑信息";
dialogFormVisible.value = true; dialogFormVisible.value = true;
} }
@ -224,12 +283,50 @@ function close() {
} }
const handleChange = (val) => { const handleChange = (val) => {
form.value.xfbbId = val?.[0]?.id; form.value.xfbbId = val?.id;
form.value.xfbbName = `${val?.[0]?.fzrXm}警组`; form.value.xfbbQwJlId = val?.xfbbQwJlId?.[0]
form.value.xfbbName = val?.xfbbName;
}
const fieldErrors = ref({})
// 检查指定索引是否有错误
const hasError = (index) => {
const propPath = `bddList[${index}].xlghDkcs`
return !!fieldErrors.value[propPath]
}
// 获取指定索引的错误信息
const getError = (index) => {
const propPath = `bddList[${index}].xlghDkcs`
return fieldErrors.value[propPath] || ''
}
// 验证单个字段
const validateField = async (propPath) => {
return new Promise((resolve) => {
formRef.value.validateField(propPath, (errorMessage) => {
fieldErrors.value[propPath] = errorMessage
resolve(!errorMessage)
})
})
}
const validateAllCheckpoints = async () => {
// if (!form.value.bddList) return true
const validations = form.value.bddList.map((_, index) =>
validateField(`bddList[${index}].xlghDkcs`)
)
const results = await Promise.all(validations)
return results.every(result => result)
} }
//提交 //提交
const submit = async () => { const submit = async () => {
console.log(await validateAllCheckpoints())
console.log(fieldErrors.value)
try { try {
await formRef.value.validate() await formRef.value.validate()
@ -267,4 +364,32 @@ defineExpose({ open })
.label { .label {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.validation-container {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
min-height: 60px; /* 预留错误信息空间 */
}
.error-message {
color: #f56c6c;
font-size: 12px;
line-height: 1;
padding: 2px 0;
display: block;
}
.is-error .el-input-number__inner {
border-color: #f56c6c;
}
/* 确保表格行高度适应错误信息 */
.el-table .cell {
min-height: 40px;
display: flex;
flex-direction: column;
justify-content: center;
}
</style> </style>

View File

@ -0,0 +1,169 @@
<script setup>
import PageTitle from "@/components/aboutTable/PageTitle.vue";
import GdMap from "@/components/Map/GdMap/index.vue"
import { onMounted, reactive, ref } from "vue";
import { fetchTbZdxlFgdwSelectList, fetchTbZdxlFgxlrwSelectList } from "@/api/service/taskProgress";
import emitter from "@/utils/eventBus";
import TaskDetailsDialog from "@/views/backOfficeSystem/service/taskPage/taskProgress/taskDetailsDialog.vue";
const visible = ref(false);
const taskDetailsRef = ref(null);
const data = reactive({
currentDate: "",
list: []
})
const fetchTaskList = async () => {
if (!data.currentDate) return
// const res = await fetchTbZdxlFgxlrwSelectList({ rwRq: data.currentDate });
const res = await fetchTbZdxlFgxlrwSelectList();
if (res && res?.length > 0) {
data.list = res;
emitter.emit("deletePointArea", "zdxl_fzyc")
let cc = []
const list = data.list?.map((el, index) => {
let centerPoint = [el.zxX,el.zxY]
if(index == 0) cc = centerPoint;
let position = [[Number(el.x1),Number(el.y1)],[Number(el.x2),Number(el.y2)]]
let text = el.mc
let obj = { position: position, text, id: el.id ,userData: el}
return obj;
})
emitter.emit("echoPlane", { fontColor:'red',coords: list, type:'rectangle', flag:'zdxl_fzyc', color:'rgba(2,20,51,0.5)', linecolor:'#ee0629'})
emitter.emit("setMapCenter", { location: cc, zoomLevel: 14 });
}
}
const getData = async () => {
const res = await fetchTbZdxlFgdwSelectList()
if (res && res?.length > 0) {
emitter.emit("deletePointArea", "tbZdxlFgdw")
let cc = []
const list = res?.map((el, index) => {
let centerPoint = [el.zxX,el.zxY]
if(index == 0) cc = centerPoint;
let position = [[Number(el.x1),Number(el.y1)],[Number(el.x2),Number(el.y2)]]
let text = el.mc
let obj = { position: position, text, id: el.id ,userData: el}
return obj;
})
emitter.emit("echoPlane", { fontColor:'#12fdb8',coords: list, type:'rectangle', flag:'tbZdxlFgdw', color:'rgba(2,20,51,0.5)', linecolor:'#1C97FF'})
emitter.emit("setMapCenter", { location: cc, zoomLevel: 14 });
}
}
const handleChange = (val) => {
fetchTaskList()
}
const handleItem = (item) => {
const { zxX, zxY } = item
emitter.emit("setMapCenter", { location: [zxX, zxY], zoomLevel: 14 });
}
onMounted(() => {
// 设置当前日期
const today = new Date();
data.currentDate = today.toISOString().split('T')[0]; // 格式化为 YYYY-MM-DD
getData()
fetchTaskList()
emitter.on("showFzycInfo", (data) => {
taskDetailsRef.value?.open(data?.info)
})
})
</script>
<template>
<div class="container">
<div class="titleBox">
<PageTitle title="任务进度" />
</div>
<div class="task-page">
<div style="height: 100vh; width: 80%;">
<gd-map />
</div>
<div class="right">
<div class="search">
<el-date-picker
v-model="data.currentDate"
placeholder="搜索"
type="date"
@change="handleChange"
value-format="YYYY-MM-DD"
/>
<el-button @click="handleChange">查询</el-button>
</div>
<div class="listWrapper">
<template v-for="(item, index) in data.list" :key="index">
<div class="item" @click="handleItem(item)">
<div>方格名称{{ item?.fgMc }}</div>
<!-- <div>必到点名称</div>-->
<div style="display: flex;align-items: center;">
<div>打卡进度</div>
<el-progress type="circle" width="50" :percentage="item?.bddAllProgress || 0" />
</div>
<div style="display: flex;align-items: center;">
<div>巡逻里程进度</div>
<el-progress type="circle" width="50" color="#e6a23c" :percentage="item?.xllcProgress || 0" />
</div>
</div>
</template>
</div>
</div>
</div>
<task-details-dialog ref="taskDetailsRef" v-model="visible" />
</div>
</template>
<style scoped lang="scss">
.task-page {
display: flex;
justify-content: space-between;
.right {
width: 19%;
}
.listWrapper {
margin-top: 20px;
overflow-y: auto;
height: calc(100vh - 236px);
.item {
display: flex;
flex-direction: column;
justify-content: space-between;
border-radius: 5px;
padding: 10px;
border: 1px solid #24b6dd;
width: 100%;
//height: 120px;
margin-bottom: 10px;
cursor: pointer;
&:hover {
color: #24b6dd;
}
::v-deep {
.el-progress__text {
color: #fff;
}
}
}
}
.search {
display: flex;
}
}
</style>

View File

@ -0,0 +1,186 @@
<template>
<div>
<div v-if="dialogFormVisible" class="dialog">
<div class="head_box">
<span class="title">{{ title }}</span>
<div>
<el-button
v-if="!infoActive"
type="primary"
size="small"
@click="submit"
:loading="btnLoading"
>保存</el-button
>
<el-button size="small" @click="close">关闭</el-button>
</div>
</div>
<div class="dialogWrapper">
<div class="tabsWrapper">
<template v-for="(item, index) in form?.bddList" :key="index">
<div :class="['item', { 'active': current === index }]" @click="handleItem(index)">{{ item?.bddMc }}</div>
</template>
</div>
<Suspense>
<template #default>
<GbMap mapid="mapDetail" />
</template>
</Suspense>
</div>
</div>
</div>
</template>
<script setup>
import { ref, getCurrentInstance, computed, defineAsyncComponent, onUnmounted } from "vue";
import { fetchSelectListByBddxlrwId } from "@/api/service/taskProgress";
import emitter from "@/utils/eventBus";
const { proxy } = getCurrentInstance();
const btnLoading = ref(false); //按钮截流
const title = ref("修改");
const formRef = ref(null);
const infoActive = ref(true);
const current = ref(0)
const GbMap = ref(null);
//级联选择器配置
const props = defineProps({
modelValue: {
type: Boolean,
default: false
}
});
const emits = defineEmits(['update:modelValue', 'ok']);
const formData = computed(() => form.value?.bddList[current.value])
const dialogFormVisible = computed({
get() {
return props.modelValue;
},
set(val) {
if (!val) {
location.reload();
}
emits("update:modelValue", val);
}
})
//表单数据
const form = ref({
bddList: []
});
const getData = async () => {
const { id = "" } = formData.value
const res = await fetchSelectListByBddxlrwId({ bddxlrwId: id })
if (res && res?.length > 0) {
console.log(res);
emitter.emit("deletePointArea", "fgxlrw")
const { zxX, zxY, x1, x2, y1, y2, id, fgMc } = form.value
let centerPoint = [zxX, zxY]
let position = [[Number(x1),Number(y1)],[Number(x2),Number(y2)]]
let text = fgMc
let obj = [{ position: position, text, id: id ,userData: form.value}]
// const list = data.list?.map((el, index) => {
// let centerPoint = [el.zxX,el.zxY]
// if(index == 0) cc = centerPoint;
// let position = [[Number(el.x1),Number(el.y1)],[Number(el.x2),Number(el.y2)]]
// let text = el.mc
// let obj = { position: position, text, id: el.id ,userData: el}
// return obj;
// })
emitter.emit("echoPlane", { fontColor:'#12fdb8',coords: obj, type:'rectangle', flag:'fgxlrw', color:'rgba(2,20,51,0.5)', linecolor:'#1C97FF'})
emitter.emit("setMapCenter", { location: centerPoint, zoomLevel: 14 });
}
}
const handleItem = (index) => {
current.value = index;
getData();
}
//新增
function open(row = {}, type) {
GbMap.value = defineAsyncComponent(() => import("@/components/Map/GdMap/index.vue"))
infoActive.value = !type;
form.value = { ...row, bddList: row?.bddList ? JSON.parse(row?.bddList) : [] };
title.value = "任务详情";
getData()
dialogFormVisible.value = true;
}
//关闭弹窗
function close() {
formRef.value?.resetFields();
dialogFormVisible.value = false;
}
//提交
const submit = async () => {
try {
await formRef.value.validate()
await updateData(form.value)
proxy.$message({
type: "success",
message: "修改成功"
});
dialogFormVisible.value = false;
emits("ok")
} catch (error) {
console.log(error);
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
@import "~@/assets/css/layout.scss";
@import "~@/assets/css/element-plus.scss";
.dialog {
.dialogWrapper {
height: calc(100vh - 256px);
margin: 20px 20px 0 20px;
.el-form--inline {
padding: 0 0 0rem 0;
}
.tabsWrapper {
display: flex;
margin-bottom: 0.53vw;
overflow-x: auto;
.item {
cursor: pointer;
padding: 0.27vw 0.53vw;
width: fit-content;
flex-shrink: 0;
border: 1px solid #EDEDED;
border-radius: 5px;
margin-right: 0.53vw;
&:last-child {
margin-right: 0;
}
}
.active {
color: #24b6dd;
border: 1px solid #24b6dd !important;
}
}
}
}
.label {
margin-bottom: 1rem;
}
</style>