自定义任务包

This commit is contained in:
给我
2025-12-17 19:46:14 +08:00
parent 7246f8f3a8
commit 90e536c910
10 changed files with 2682 additions and 53 deletions

View File

@ -10,6 +10,14 @@ export function getSelectPage(params) {
params params
}) })
} }
// 自定义分页查询
export function selectPagezdy(params) {
return request({
url: api + 'tbZdxlFgxlrw/selectPagezdy',
method: 'GET',
params
})
}
// 修改数据 // 修改数据
export function updateData(data) { export function updateData(data) {

View File

@ -66,6 +66,10 @@ watch(() => props.lx, (val) => {
baseUrl.value = '/mosty-api/mosty-yjzl/tbZdxlFgxlrw/importListByMb' baseUrl.value = '/mosty-api/mosty-yjzl/tbZdxlFgxlrw/importListByMb'
// modelUrl.value = '/mosty-api/mosty-jcgl/tpjcglZnzb/importTemplate' // modelUrl.value = '/mosty-api/mosty-jcgl/tpjcglZnzb/importTemplate'
break; break;
case 'zdyrcrwb':
baseUrl.value = '/mosty-api/mosty-yjzl/tbZdxlFgxlrw/importListByMbZdy'
// modelUrl.value = '/mosty-api/mosty-jcgl/tpjcglZnzb/importTemplate'
break;
default: default:
break; break;
} }

View File

@ -576,6 +576,15 @@ export const privateRoutes = [{
icon: "article-ranking" icon: "article-ranking"
}, },
}, },
{
path: "/taskPage/CustomTaskPackage",
component: () => import("@/views/backOfficeSystem/service/taskPage/CustomTaskPackage/index"),
name: "CustomTaskPackage",
meta: {
title: '自定义日常任务包',
icon: "article-ranking"
},
},
{ {
path: "/taskPage/taskProgress", path: "/taskPage/taskProgress",
component: () => import("@/views/backOfficeSystem/service/taskPage/taskProgress/index"), component: () => import("@/views/backOfficeSystem/service/taskPage/taskProgress/index"),

View File

@ -25,11 +25,7 @@
<div class="searchBox" ref="searchBox"> <div class="searchBox" ref="searchBox">
<el-form :model="listQuery" :inline="true"> <el-form :model="listQuery" :inline="true">
<el-form-item label="所属部门"> <el-form-item label="所属部门">
<MOSTY.Department <MOSTY.Department width="100%" clearable v-model="listQuery.ssbmid" />
width="100%"
clearable
v-model="listQuery.ssbmid"
/>
</el-form-item> </el-form-item>
<el-form-item label="必巡点名称"> <el-form-item label="必巡点名称">
<el-input v-model="listQuery.bxdMc" placeholder="请输入" clearable /> <el-input v-model="listQuery.bxdMc" placeholder="请输入" clearable />
@ -116,7 +112,11 @@
label="必巡点来源" label="必巡点来源"
> >
<template #default="{ row }"> <template #default="{ row }">
<dict-tag :options="D_BZ_BXDLYDM" :value="row.bxdlydm" :tag="false" /> <dict-tag
:options="D_BZ_BXDLYDM"
:value="row.bxdlydm"
:tag="false"
/>
</template> </template>
</el-table-column> </el-table-column>
@ -147,8 +147,16 @@
label="数据状态" label="数据状态"
> >
<template #default="{ row }"> <template #default="{ row }">
<span v-if="row.xtSjzt == 0" style="background:red;padding:2px 4px;border-radius:2px;">注销</span> <span
<span v-if="row.xtSjzt == 1" style="background:green;padding:2px 4px;border-radius:2px;">正常</span> v-if="row.xtSjzt == 0"
style="background: red; padding: 2px 4px; border-radius: 2px"
>注销</span
>
<span
v-if="row.xtSjzt == 1"
style="background: green; padding: 2px 4px; border-radius: 2px"
>正常</span
>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
@ -224,7 +232,12 @@
/> />
</el-form-item> </el-form-item>
<el-form-item style="width: 48%" prop="bxdLx" label="必巡点类型"> <el-form-item style="width: 48%" prop="bxdLx" label="必巡点类型">
<el-select v-model="form.bxdLx" filterable style="width: 100%" placeholder="请选择必巡点类型"> <el-select
v-model="form.bxdLx"
filterable
style="width: 100%"
placeholder="请选择必巡点类型"
>
<el-option <el-option
v-for="dict in D_BZ_BXDLX" v-for="dict in D_BZ_BXDLX"
:key="dict.value" :key="dict.value"
@ -234,7 +247,12 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item style="width: 48%" prop="bxdLx" label="必巡点来源"> <el-form-item style="width: 48%" prop="bxdLx" label="必巡点来源">
<el-select v-model="form.bxdlydm" filterable style="width: 100%" placeholder="请选择必巡点来源"> <el-select
v-model="form.bxdlydm"
filterable
style="width: 100%"
placeholder="请选择必巡点来源"
>
<el-option <el-option
v-for="dict in D_BZ_BXDLYDM" v-for="dict in D_BZ_BXDLYDM"
:key="dict.value" :key="dict.value"
@ -248,7 +266,11 @@
prop="ssXfqyId" prop="ssXfqyId"
label="必巡点所属巡防区域" label="必巡点所属巡防区域"
> >
<el-select style="width: 100%" v-model="form.ssXfqyId" placeholder="请选择必巡点所属巡防区域"> <el-select
style="width: 100%"
v-model="form.ssXfqyId"
placeholder="请选择必巡点所属巡防区域"
>
<el-option <el-option
v-for="item in xfqyList" v-for="item in xfqyList"
:key="item.value" :key="item.value"
@ -259,14 +281,26 @@
</el-form-item> </el-form-item>
<el-form-item prop="jd" label="选择必巡点" style="width: 100%"> <el-form-item prop="jd" label="选择必巡点" style="width: 100%">
<div class="latlng"> <div class="latlng">
<el-input v-model="form.jd" @change="jdChange" clearable style="width: 45%" placeholder="经度" /> <el-input
<el-input v-model="form.wd" @change="wdChange" clearable style="width: 45%" placeholder="纬度" /> v-model="form.jd"
@change="jdChange"
clearable
style="width: 45%"
placeholder="经度"
/>
<el-input
v-model="form.wd"
@change="wdChange"
clearable
style="width: 45%"
placeholder="纬度"
/>
<el-button @click="chackLat">选取坐标</el-button> <el-button @click="chackLat">选取坐标</el-button>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item style="width: 100%"> <el-form-item style="width: 100%">
<div class="map"> <div class="map">
<GdMap v-if="dialogFormVisible"/> <GdMap v-if="dialogFormVisible" />
</div> </div>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -290,7 +324,11 @@ import { getXfqy } from "@/api/basicsmanage/patrolArea.js";
import { selectDeptPage } from "@/api/user-manage"; import { selectDeptPage } from "@/api/user-manage";
import { ref, reactive, onMounted, onUnmounted, getCurrentInstance } from "vue"; import { ref, reactive, onMounted, onUnmounted, getCurrentInstance } from "vue";
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const { D_BZ_BXDLX, D_BZ_PBLX,D_BZ_BXDLYDM } = proxy.$dict("D_BZ_BXDLX", "D_BZ_PBLX",'D_BZ_BXDLYDM'); const { D_BZ_BXDLX, D_BZ_PBLX, D_BZ_BXDLYDM } = proxy.$dict(
"D_BZ_BXDLX",
"D_BZ_PBLX",
"D_BZ_BXDLYDM"
);
const cascader = ref(null); const cascader = ref(null);
const searchBox = ref(null); // 搜索盒子 const searchBox = ref(null); // 搜索盒子
const tableHeight = ref(); // 表格高度 const tableHeight = ref(); // 表格高度
@ -367,7 +405,7 @@ function checkZqsc(rule, value, callback) {
function chackLat() { function chackLat() {
form.value.jd = ""; form.value.jd = "";
form.value.wd = ""; form.value.wd = "";
emitter.emit("drawShape", { type: "point", flag: "bxd_ht" ,isclear:true}); emitter.emit("drawShape", { type: "point", flag: "bxd_ht", isclear: true });
} }
function getLngLat(coordinates) { function getLngLat(coordinates) {
@ -480,14 +518,17 @@ function update(row) {
title.value = "修改必巡点"; title.value = "修改必巡点";
dialogFormVisible.value = true; dialogFormVisible.value = true;
if (row.jd && row.wd) { if (row.jd && row.wd) {
setTimeout(()=>{ setTimeout(() => {
emitter.emit("addPointArea", { emitter.emit("addPointArea", {
coords: [res], coords: [res],
icon: require("@/assets/point/xfq.png"), icon: require("@/assets/point/xfq.png"),
flag: "kfd" flag: "kfd"
}); });
emitter.emit("setMapCenter", { location: [res.jd,res.wd], zoomLevel: 13 }); emitter.emit("setMapCenter", {
},1000) location: [res.jd, res.wd],
zoomLevel: 13
});
}, 1000);
} }
}); });
} }
@ -513,11 +554,13 @@ function delDictItem(row) {
//获取列表数据 //获取列表数据
function getListData() { function getListData() {
loadingTable.value = true; loadingTable.value = true;
getBxd(listQuery.value).then((res) => { getBxd(listQuery.value)
.then((res) => {
tableData.value = res.records; tableData.value = res.records;
total.value = res.total; total.value = res.total;
loadingTable.value = false; loadingTable.value = false;
}).catch(()=>{ })
.catch(() => {
loadingTable.value = false; loadingTable.value = false;
}); });
} }
@ -529,7 +572,7 @@ function close() {
function submit() { function submit() {
elform.value.validate((valid) => { elform.value.validate((valid) => {
if (valid) { if (valid) {
form.value.ssbmid = form.value.ssbmdm form.value.ssbmid = form.value.ssbmdm;
btnLoading.value = true; btnLoading.value = true;
setTimeout(() => { setTimeout(() => {
btnLoading.value = false; btnLoading.value = false;

View File

@ -0,0 +1,313 @@
<script setup>
import { computed, getCurrentInstance, reactive, ref, nextTick } from "vue";
import MyTable from "@/components/aboutTable/MyTable.vue";
import { fetchTbAppendList, fetchTbZdxlFgdwBddSelectList } from "@/api/service/dailyTaskPackage";
const { proxy } = getCurrentInstance();
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
disabledIds: {
type: Array,
default: () => []
},
bddList: {
type: Array,
default: () => []
}
})
const formRef = ref(null)
const emits = defineEmits(["update:modelValue", "change"]);
const visible = computed({
get() {
return props.modelValue;
},
set(val) {
emits("update:modelValue", val);
}
})
const selectList = ref([])
const pageData = reactive({
tableConfiger: {
rowKey: 'bddId',
rowHieght: 61,
haveControls: false,
showSelectType: 'checkBox',
defaultSelectKeys: [],
loading: false
},
data: {},
tableData: [],
total: 0,
tableColumn: [
{
label: "必到点",
prop: "bddMc"
},
{
label: "打卡次数",
prop: "xlghDkcs",
showSolt:true
}
]
})
// 打卡点验证规则
const checkPointRules = [
{ required: true, message: '请输入打卡次数', trigger: 'change' },
{
validator: (rule, value, callback) => {
if (value < 1) {
callback(new Error('打卡次数必须大于0'));
} else {
callback();
}
},
trigger: 'change'
}
];
const handleClickClose = () => {
visible.value = false;
}
const handleChooseDataChange = (event) => {
if (!Array.isArray(event)) return
selectList.value = [...event]
// 重新初始化验证
nextTick(() => {
if (formRef.value) {
formRef.value.clearValidate()
}
})
}
const selectable = (row) => {
return !props.disabledIds.has(row.bddId)
}
// 自定义验证
const fieldErrors = ref({})
// 判断行是否被选中
const isRowSelected = (row) => {
return selectList.value.some(selected => selected.bddId === row.bddId)
}
// 检查指定索引是否有错误
const hasError = (index) => {
const propPath = `tableData[${index}].xlghDkcs`
return !!fieldErrors.value[propPath]
}
// 获取指定索引的错误信息
const getError = (index) => {
const propPath = `tableData[${index}].xlghDkcs`
return fieldErrors.value[propPath] || ''
}
// 验证单个字段(只为选中的行验证)
const validateField = async (index) => {
const row = pageData.tableData[index]
if (!isRowSelected(row)) return true // 未选中的行不验证
const propPath = `tableData[${index}].xlghDkcs`
return new Promise((resolve) => {
formRef.value.validateField(propPath, (errorMessage) => {
fieldErrors.value[propPath] = errorMessage
console.log(errorMessage);
resolve(!errorMessage)
})
})
}
// 只为选中的行验证
const validateSelectedCheckpoints = async () => {
if (!pageData.tableData) return true
const validations = pageData.tableData
.map((item, index) => isRowSelected(item) ? validateField(index) : Promise.resolve(true))
const results = await Promise.all(validations)
return results.every(result => result)
}
// // 防抖验证,避免频繁触发
// const debouncedValidate = debounce((index) => {
// validateField(index)
// }, 300)
//
// const handleInputChange = (index) => {
// debouncedValidate(index)
// }
//
// // 监听数据变化,自动验证选中的行
// watch(() => pageData.tableData,
// (newData) => {
// newData.forEach((row, index) => {
// if (isRowSelected(row)) {
// validateField(index)
// }
// })
// },
// { deep: true }
// )
const open = async (data) => {
selectList.value = [];
fieldErrors.value = {}
const res = await fetchTbZdxlFgdwBddSelectList(data?.id)
if (res && res.length > 0) {
pageData.tableData = res
pageData.tableConfiger.defaultSelectKeys = props.bddList?.map(i => {
if (i?.selected) return i?.bddId
})
pageData.data = data
visible.value = true;
} else {
proxy.$message({
type: "warning",
message: "所有必到点已全部下发任务"
})
}
}
const onSubmit = async () => {
if (selectList.value.length === 0) {
proxy.$message({
type: "warning",
message: "请选择打卡数据"
});
return
}
try {
const allValid = await validateSelectedCheckpoints()
if (!allValid) {
throw new Error('请完善选中的打卡点信息')
}
await formRef.value.validate()
await fetchTbAppendList({
bddList: selectList.value || [],
fgxlrwId: pageData.data?.id
})
emits("change", selectList.value)
visible.value = false;
} catch(error) {
console.log(error);
}
}
defineExpose({ open })
</script>
<template>
<el-form ref="formRef" :model="pageData">
<el-dialog v-model="visible" @close="handleClickClose">
<MyTable
:tableData="pageData.tableData"
:tableColumn="pageData.tableColumn"
:tableConfiger="pageData.tableConfiger"
@chooseData="handleChooseDataChange"
:selectable="selectable"
>
<template #xlghDkcs="{ row, $index }">
<div class="validation-container">
<el-input-number
style="width: 30%;"
:placeholder="`输入打卡点${$index + 1}需打卡次数`"
clearable
min="0"
:disabled="!selectable(row)"
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 pageData.tableData" :key="index">
<el-form-item v-if="isRowSelected(item)" :prop="`tableData[${index}].xlghDkcs`" :label="item?.bddMc" :rules="checkPointRules">
<el-input-number
style="width: 100%;"
:placeholder="`输入打卡点${index + 1}需打卡次数`"
clearable
v-model="item.xlghDkcs"
/>
</el-form-item>
</template>
</div>
<template #footer>
<el-button @click="handleClickClose">取消</el-button>
<el-button type="primary" @click="onSubmit">确定</el-button>
</template>
</el-dialog>
</el-form>
</template>
<style scoped lang="scss">
::v-deep {
.el-checkbox__input.is-disabled .el-checkbox__inner,
.el-checkbox__input.is-checked .el-checkbox__inner {
background-color: var(--el-checkbox-checked-bg-color);
border-color: var(--el-checkbox-checked-input-border-color);
}
.el-checkbox__input.is-disabled .el-checkbox__inner,
.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after {
border: 1px solid var(--el-checkbox-checked-icon-color);
border-left: 0;
border-top: 0;
}
}
.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>

View File

@ -0,0 +1,422 @@
<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="label">任务包基本信息</div>
<el-descriptions class="margin-top" :column="3" border>
<!-- <template #extra>-->
<!-- <el-button type="primary">Operation</el-button>-->
<!-- </template>-->
<el-descriptions-item label="方格名称">
{{ form?.fgMc }}
</el-descriptions-item>
<el-descriptions-item label="预警等级">
<dict-tag :options="D_ZDXL_FGXLRW_YJDJ" :value="form?.fgYjdj" :tag="false" />
</el-descriptions-item>
<el-descriptions-item label="警情数量">
{{ form?.fgYjjqsl }}
</el-descriptions-item>
<el-descriptions-item label="高发时段">
{{ form?.fgYjgfsd }}
</el-descriptions-item>
<el-descriptions-item label="高发类型">
{{ form?.fgJqtjLx }}
</el-descriptions-item>
<el-descriptions-item label="任务日期">
{{ form?.rwRq }}
</el-descriptions-item>
<el-descriptions-item label="小时统计">
{{ form?.fgJqtjXsLabel }}
</el-descriptions-item>
</el-descriptions>
<div class="label" style="margin-top: 1rem">任务包下发编辑信息</div>
<el-form ref="formRef" :model="form" :rules="rules" :inline="true">
<el-form-item prop="xlghSc" label="巡逻时长:">
<el-input-number style="width: 80%" min="0" :disabled="disabled" placeholder="请填写巡逻时长"
v-model="form.xlghSc"></el-input-number>
<el-tag size="small" type="warning">分钟</el-tag>
</el-form-item>
<el-form-item prop="xlghPcRy" label="盘查人员:">
<el-input placeholder="请填写盘查人员" clearable :disabled="disabled" v-model="form.xlghPcRy"></el-input>
</el-form-item>
<el-form-item prop="xlghPcCl" label="盘查车辆:">
<el-input placeholder="请填写盘查车辆" clearable :disabled="disabled" v-model="form.xlghPcCl"></el-input>
</el-form-item>
<el-form-item prop="xlghXllc" label="巡逻里程:">
<el-input-number style="width: 80%" min="0" :disabled="disabled" placeholder="请填写巡逻里程"
v-model="form.xlghXllc"></el-input-number>
<el-tag size="small" type="warning">公里</el-tag>
</el-form-item>
<el-form-item label="下发巡组:">
<el-input placeholder="请填写下发巡组" clearable :disabled="disabled" v-model="form.xfbbName"
@click="visible = true" readonly></el-input>
</el-form-item>
<div v-if="form?.rwZt !== '01'" style="flex: 1; display: flex; justify-content: flex-end">
<el-button :disabled="isButtonDisabled" @click="checkInCardOpen">追加打卡</el-button>
</div>
<el-row style="width: 100%">
<el-col>
<MyTable :tableData="form?.bddList" :tableColumn="pageData.tableColumn"
:tableConfiger="pageData.tableConfiger" @chooseData="handleChooseDataChange" :selectable="selectable">
<template #xlghDkcs="{ row, $index }">
<div class="validation-container">
<el-input-number style="width: 30%" :placeholder="`输入打卡点${$index + 1}需打卡次数`" clearable min="0"
:disabled="!selectable(row)" 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">
<el-form-item :prop="`bddList[${index}].xlghDkcs`" :label="item?.bddMc" :rules="checkPointRules">
<el-input-number style="width: 100%" :placeholder="`输入打卡点${index + 1}需打卡次数`" clearable
v-model="item.xlghDkcs" />
</el-form-item>
</template>
</div>
</el-col>
</el-row>
</el-form>
</div>
</div>
<distribute-patrol-team-dialog v-model="visible" @selection-change="handleChange" />
<add-check-in-card ref="addCheckInCardRef" :disabledIds="disabledIds" :bddList="form?.bddList"
v-model="checkInCardVisible" @change="handleSelectChange" />
</div>
</template>
<script setup>
import { ref, reactive, getCurrentInstance, computed, watch } from "vue";
import { updateData } from "@/api/service/dailyTaskPackage";
import DictTag from "@/components/DictTag/index.vue";
import MyTable from "@/components/aboutTable/MyTable.vue";
import DistributePatrolTeamDialog from "@/views/backOfficeSystem/service/taskPage/dailyTaskPackage/components/DistributePatrolTeamDialog.vue";
import AddCheckInCard from "@/views/backOfficeSystem/service/taskPage/dailyTaskPackage/components/addCheckInCard.vue";
const { proxy } = getCurrentInstance();
const { D_ZDXL_FGXLRW_YJDJ } = proxy.$dict("D_ZDXL_FGXLRW_YJDJ");
const btnLoading = ref(false); //按钮截流
const title = ref("修改");
const formRef = ref(null);
const addCheckInCardRef = ref(null);
const infoActive = ref(true);
const visible = ref(false);
const checkInCardVisible = ref(false);
const pageData = reactive({
tableConfiger: {
rowKey: "bddId",
rowHieght: 61,
haveControls: false,
showSelectType: "checkBox",
defaultSelectKeys: [],
loading: false
},
total: 0,
selectCheckList: [],
tableColumn: [
{
label: "必到点",
prop: "bddMc"
},
{
label: "打卡次数",
prop: "xlghDkcs",
showSolt: true
}
]
});
//级联选择器配置
const props = defineProps({
modelValue: {
type: Boolean,
default: false
}
});
const emits = defineEmits(["update:modelValue", "ok"]);
const dialogFormVisible = computed({
get() {
return props.modelValue;
},
set(val) {
emits("update:modelValue", val);
}
});
//表单数据
const form = ref({
bddList: [{ xlghDkcs: 1 }, { xlghDkcs: "" }]
});
const isButtonDisabled = computed(() => {
// const target = new Date('2025-09-18')
const target = new Date(form.value?.rwRq);
const now = new Date();
// 只比较日期,忽略时间
return target.setHours(0, 0, 0, 0) < now.setHours(0, 0, 0, 0);
});
const disabled = computed(() => {
// const targetDate = new Date(form.value?.rwRq);
// const today = new Date();
return form.value?.rwZt === "03" || isButtonDisabled.value;
});
// 打卡点验证规则
const checkPointRules = [
{ required: true, message: "请输入打卡次数", trigger: "change" },
{
validator: (rule, value, callback) => {
if (value < 1) {
callback(new Error("打卡次数必须大于0"));
} else {
callback();
}
},
trigger: "change"
}
];
//表单验证
const rules = reactive({
xlghXllc: [
{
required: true,
message: "请填写巡逻里程",
trigger: "change"
}
],
xlghSc: [
{
required: true,
message: "请填写巡逻时长",
trigger: "change"
}
],
xlghPcRy: [
{
required: true,
message: "请填写盘查人员",
trigger: "change"
}
],
xlghPcCl: [
{
required: true,
message: "请填写盘查车辆",
trigger: "change"
}
]
});
//新增
function open(row = {}, type) {
infoActive.value = type === "view";
form.value = { ...row, xfbbName: row?.xfxz };
form.value?.bddList?.forEach((item, index) => {
item.index = index; // 直接添加index属性
});
pageData.tableConfiger.defaultSelectKeys = form.value?.bddList?.map((i) => {
if (i?.selected) return i?.bddId;
});
title.value = type === "view" ? "查看详情" : "编辑信息";
dialogFormVisible.value = true;
}
//关闭弹窗
function close() {
formRef.value.resetFields();
dialogFormVisible.value = false;
}
const handleChange = (val) => {
form.value.xfbbId = val?.id;
form.value.xfbbQwJlId = val?.xfbbQwJlId?.[0];
form.value.xfbbName = val?.xfbbName;
};
const selectList = ref([]);
const handleChooseDataChange = (event) => {
selectList.value = [...event];
};
// 保存原始bddList的ID用于对比
const disabledIds = ref(new Set());
const updateDisabledIds = () => {
disabledIds.value = new Set(form.value?.bddList?.map((i) => i.bddId));
};
const selectable = (row) => {
return !disabledIds.value.has(row.bddId);
};
const handleSelectChange = (selection) => {
pageData.selectCheckList = selection;
// 添加新项目
const newItems = selection.filter(
(item) => !disabledIds.value.has(item.bddId)
);
form.value.bddList = [...form.value.bddList, ...newItems];
// 更新禁用ID集合
updateDisabledIds();
emits("ok", false);
};
// 监听form.bddList的变化确保禁用集合同步更新
watch(
() => form.value.bddList,
() => {
updateDisabledIds();
},
{ deep: true }
);
const checkInCardOpen = () => {
addCheckInCardRef.value?.open(form.value);
};
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 () => {
try {
await validateAllCheckpoints();
await formRef.value.validate();
await updateData({ ...form.value, bddList: selectList.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 {
::v-deep {
.el-table__header-wrapper .el-checkbox {
display: none;
}
.el-checkbox__input.is-disabled .el-checkbox__inner,
.el-checkbox__input.is-checked .el-checkbox__inner {
background-color: var(--el-checkbox-checked-bg-color);
border-color: var(--el-checkbox-checked-input-border-color);
}
.el-checkbox__input.is-disabled .el-checkbox__inner,
.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after {
border: 1px solid var(--el-checkbox-checked-icon-color);
border-left: 0;
border-top: 0;
}
}
.dialogWrapper {
margin: 20px 20px 0 20px;
.el-form--inline {
padding: 0 0 0rem 0;
}
}
}
.label {
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>

View File

@ -0,0 +1,370 @@
<script setup>
import { computed, getCurrentInstance, onMounted, reactive, ref } from "vue";
const { proxy } = getCurrentInstance();
const { D_BZ_XB, D_ZDXL_FGXLRW_YJDJ, D_ZDXL_FGXLRW_RWZT } = proxy.$dict("D_BZ_XB", "D_ZDXL_FGXLRW_YJDJ", "D_ZDXL_FGXLRW_RWZT");
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 EditAddFormDialog from "@/views/backOfficeSystem/service/taskPage/dailyTaskPackage/editAddFormDialog.vue";
import Export from "@/components/export/index.vue";
import { fetchIssueData, selectPagezdy } from "@/api/service/dailyTaskPackage";
const searchConfiger = reactive([
{
showType: "input",
prop: "fgMc",
placeholder: "请输入方格名称",
label: "方格名称"
},{
showType: "select",
prop: "fgYjdj",
placeholder: "请选择预警等级",
label: "预警等级:",
options: D_ZDXL_FGXLRW_YJDJ
},
{
showType: "date",
prop: "rwRq",
placeholder: "请选择任务日期",
label: "任务日期:",
},
])
const searchBox = ref() //搜索框
const deleteIds = ref([]) // 删除id
const isImport = ref(false);
const dialogFormVisible = ref(false);
const pageData = reactive({
visible: true,
tableData: [], //表格数据
keyCount: 0,
tableConfiger: {
rowHieght: 61,
showSelectType:'checkBox',
loading:false
},
total: 0,
pageConfiger: {
pageSize: 10,
pageCurrent: 1
}, //分页
controlsWidth: 210, //操作栏宽度
tableColumn: [
{
label: "方格名称 ",
prop: "fgMc"
},
{
label: "预警等级",
prop: "fgYjdj",
showSolt:true
},
{
label: "任务标题",
prop: "fgRwbt"
},
{
label: "任务日期",
prop: "rwRq",
},
{
label: "巡逻时长",
prop: "xlghSc",
showSolt:true
},
{
label: "盘查人员",
prop: "xlghPcRy",
},
{
label: "盘查车辆",
prop: "xlghPcCl",
},
{
label: "巡逻里程",
prop: "xlghXllc",
showSolt:true
},
{
label: "下发巡组",
prop: "xfxz",
},
{
label: "每个打卡点",
prop: "bddListLabel",
showOverflowTooltip: true,
},
{
label: "下发状态",
prop: "rwZt",
showSolt:true
},
]
})
const selectable = (row) => {
const targetDate = new Date(row?.rwRq);
const today = new Date();
return targetDate.toDateString() === today.toDateString() && (row?.rwZt === '01' || row?.rwZt === '02');
// return row?.rwZt === "01" || row?.rwZt === "02";
}
const editAddFormRef = ref(null);
const addEdit = (row, type) => {
editAddFormRef.value?.open(row, type)
}
const handleIssueAll = () => {
if (deleteIds.value.length === 0) {
proxy.$message({
message: "请选择勾选要下发的任务",
type: "warning"
});
return
}
handleIssue(deleteIds.value)
}
// 下发
const handleIssue = async (ids = []) => {
try {
await proxy.$confirm("是否进行下发操作", "警告", {type: "warning"})
const res = await fetchIssueData({ ids: ids?.join(',') })
if (res) {
proxy.$message({type: "success", message: "下发成功" });
handleFilter()
deleteIds.value = []
}
} catch (error) {
console.log(error);
}
}
// 删除
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 parseAndJoinLx = (jsonString, type = 'lx') => {
if (!jsonString) return '';
try {
let data = jsonString;
// 如果是字符串尝试解析为JSON
if (typeof jsonString === 'string') {
data = JSON.parse(jsonString);
}
// 处理数组情况
if (Array.isArray(data)) {
return data.map(item => item?.[type]).filter(Boolean).join(",");
}
// 处理对象情况
if (typeof data === 'object' && data !== null) {
return data?.[type] || '';
}
return '';
} catch (error) {
console.warn('数据处理失败:', error);
return '';
}
};
const getListData = async (params = {}) => {
pageData.tableConfiger.loading = true
try {
const res = await selectPagezdy({
...pageData.pageConfiger,
...params
})
pageData.tableData = res?.records?.map((item) => ({
...item,
xfxz: item?.xfbb?.fzrXm ? `${item?.xfbb?.fzrXm}警组` : '',
bddListLabel: item?.bddList?.map((i, idx) => (`卡点${idx + 1}-${i?.xlghDkcs}`))?.join(',') || '',
fgJqtjXsLabel: item.fgJqtjXs?JSON.parse(item?.fgJqtjXs)?.filter(i => (i?.count !== 0))?.map(i => (`${i?.hour}小时-${i?.count}`))?.join('、'): '',
fgJqtjLx: parseAndJoinLx(item?.fgJqtjLx, 'lx')
})) || []
// pageData.tableData[2].rwRq = '2025-09-18'
// pageData.tableData[0].rwZt = '01'
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 = (reset = true) => {
// reset 是否重置 pageCurrent = 1
if (reset) pageData.pageConfiger.pageCurrent = 1;
getListData();
}
const isVisibleDel = (row = {}) => {
const targetDate = new Date(row?.rwRq);
const today = new Date();
return targetDate.toDateString() === today.toDateString() && (row?.rwZt === '01' || row?.rwZt === '02');
}
// 删除的方法
//批量删除数据
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 typeof="danger" :disabled="deleteIds.length === 0" @click="handleIssueAll">
<span style="vertical-align: middle">批量下发</span>
</el-button>
<el-button type="primary" @click="isImport = true">
<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"
:selectable="selectable"
@chooseData="chooseData"
>
<template #fgYjdj="{ row }">
<dict-tag :options="D_ZDXL_FGXLRW_YJDJ" :value="row.fgYjdj" :tag="false" />
</template>
<template #rwZt="{ row }">
<dict-tag :options="D_ZDXL_FGXLRW_RWZT" :value="row.rwZt" :tag="false" />
</template>
<template #xlghXllc="{ row }">
<div>{{ `${row.xlghXllc}公里` }}</div>
</template>
<template #xlghSc="{ row }">
<div>{{ `${row.xlghSc}分钟` }}</div>
</template>
<!-- 操作 -->
<template #controls="{ row }">
<el-button size="small" v-if="isVisibleDel(row)" @click="addEdit(row, 'del')">编辑</el-button>
<el-button size="small" @click="addEdit(row, 'view')">查看详情</el-button>
<el-button size="small" v-if="isVisibleDel(row)" @click="handleIssue([row.id])">下发</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>
<edit-add-form-dialog ref="editAddFormRef" v-model="dialogFormVisible" @ok="handleFilter" />
<Export
:show="isImport"
lx="zdyrcrwb"
@closeImport="isImport = !isImport"
@handleImport="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

@ -68,9 +68,7 @@ const props = defineProps({
default: false default: false
} }
}); });
const emits = defineEmits(["update:modelValue", "ok"]); const emits = defineEmits(["update:modelValue", "ok"]);
const dialogFormVisible = computed({ const dialogFormVisible = computed({
get() { get() {
return props.modelValue; return props.modelValue;
@ -80,14 +78,11 @@ const dialogFormVisible = computed({
// 获取经纬度 // 获取经纬度
setTimeout(() => { setTimeout(() => {
emitter.emit("getMapClickCoordinates"); emitter.emit("getMapClickCoordinates");
// 更新地图标注位置 // 更新地图标注位置
emitter.on("mapClickCoordinates", async (res) => { emitter.on("mapClickCoordinates", async (res) => {
emitter.emit("deletePointArea"); emitter.emit("deletePointArea");
form.value.jd = res.lng; form.value.jd = res.lng;
form.value.wd = res.lat; form.value.wd = res.lat;
emitter.emit("addPointArea", { emitter.emit("addPointArea", {
coords: [{ jd: res.lng, wd: res.lat }], coords: [{ jd: res.lng, wd: res.lat }],
coordinates: res?.coordinates, coordinates: res?.coordinates,
@ -100,7 +95,6 @@ const dialogFormVisible = computed({
} else { } else {
emitter.off("getMapClickCoordinates"); emitter.off("getMapClickCoordinates");
} }
emits("update:modelValue", val); emits("update:modelValue", val);
} }
}); });
@ -109,7 +103,6 @@ const dialogFormVisible = computed({
const form = ref({ const form = ref({
bddList: [] bddList: []
}); });
//表单验证 //表单验证
const rules = reactive({ const rules = reactive({
bddMc: [ bddMc: [
@ -214,7 +207,6 @@ const setMap = (data, type = true) => {
]; ];
const text = mc; const text = mc;
const obj = [{ position: position, text, id, userData: data }]; const obj = [{ position: position, text, id, userData: data }];
emitter.emit("echoPlane", { emitter.emit("echoPlane", {
fontColor: "#12fdb8", fontColor: "#12fdb8",
coords: obj, coords: obj,
@ -224,11 +216,9 @@ const setMap = (data, type = true) => {
linecolor: "#1C97FF" linecolor: "#1C97FF"
}); });
emitter.emit("setMapCenter", { location: centerPoint, zoomLevel: 14 }); emitter.emit("setMapCenter", { location: centerPoint, zoomLevel: 14 });
// 更新地图标注位置 // 更新地图标注位置
if (type) return; if (type) return;
emitter.emit("deletePointArea"); emitter.emit("deletePointArea");
emitter.emit("addPointArea", { emitter.emit("addPointArea", {
coords: [{ jd: form.value?.jd, wd: form.value?.wd }], coords: [{ jd: form.value?.jd, wd: form.value?.wd }],
coordinates: [form.value?.jd, form.value?.wd], coordinates: [form.value?.jd, form.value?.wd],

View File

@ -7,8 +7,6 @@ import MyTable from "@/components/aboutTable/MyTable.vue";
import Pages from "@/components/aboutTable/Pages.vue"; import Pages from "@/components/aboutTable/Pages.vue";
import AddFgFormDialog from "./AddFgFormDialog.vue" import AddFgFormDialog from "./AddFgFormDialog.vue"
import { fetchTbZdxlFgdwBddSelectPage } from "@/api/service/taskProgress"; import { fetchTbZdxlFgdwBddSelectPage } from "@/api/service/taskProgress";
const searchConfiger = reactive([ const searchConfiger = reactive([
{ {
showType: "input", showType: "input",
@ -92,7 +90,6 @@ const getListData = async (params = {}) => {
...pageData.pageConfiger, ...pageData.pageConfiger,
...params ...params
}) })
pageData.tableData = res?.records || [] pageData.tableData = res?.records || []
pageData.total = res.total; pageData.total = res.total;
pageData.tableConfiger.loading = false; pageData.tableConfiger.loading = false;