进度填报大屏界面基本功能
This commit is contained in:
@ -1,8 +1,16 @@
|
||||
<template>
|
||||
<div class="daily_paper">
|
||||
<el-dialog v-model="isShowDialog" @close="onCancel" width="1000px" :close-on-click-modal="false" :destroy-on-close="true">
|
||||
<el-dialog
|
||||
v-model="isShowDialog"
|
||||
@close="onCancel"
|
||||
width="1000px"
|
||||
:close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
:lock-scroll="false"
|
||||
:append-to-body="false"
|
||||
>
|
||||
<template #header>
|
||||
<div v-drag="['.daily_paper .el-dialog', '.daily_paper .el-dialog__header']" style="font-size: 18px">{{ infoDetail.name }} 日报填写</div>
|
||||
<div style="font-size: 18px">{{ infoDetail.name }} 日报填写</div>
|
||||
</template>
|
||||
<div class="box">
|
||||
<div class="box-left">
|
||||
@ -197,6 +205,32 @@ const clickOpen = (row: any) => {
|
||||
|
||||
defineExpose({ openDialog });
|
||||
const emit = defineEmits(['getProgressList']);
|
||||
|
||||
let scrollTop = 0;
|
||||
|
||||
watch(
|
||||
() => isShowDialog.value,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
scrollTop = window.scrollY || document.documentElement.scrollTop;
|
||||
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
||||
|
||||
document.body.style.position = 'fixed';
|
||||
document.body.style.top = `-${scrollTop}px`;
|
||||
document.body.style.width = '100%';
|
||||
document.body.style.paddingRight = `${scrollbarWidth}px`; // 👈 补偿滚动条宽度
|
||||
} else {
|
||||
document.body.style.position = '';
|
||||
document.body.style.top = '';
|
||||
document.body.style.width = '';
|
||||
document.body.style.paddingRight = ''; // 👈 恢复
|
||||
|
||||
nextTick(() => {
|
||||
window.scrollTo(0, scrollTop);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -1,6 +1,14 @@
|
||||
<template>
|
||||
<div class="daily-paper-count">
|
||||
<el-dialog v-model="isShowDialog" @close="onCancel" width="65vw" :close-on-click-modal="false" :destroy-on-close="true">
|
||||
<el-dialog
|
||||
v-model="isShowDialog"
|
||||
@close="onCancel"
|
||||
width="70vw"
|
||||
:close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
:lock-scroll="false"
|
||||
:append-to-body="false"
|
||||
>
|
||||
<template #header>
|
||||
<div v-drag="['.daily-paper-count .el-dialog', '.daily-paper-count .el-dialog__header']" style="font-size: 18px">
|
||||
{{ infoDetail.name }} 日报填写
|
||||
@ -24,23 +32,23 @@
|
||||
<div style="margin-left: 45px" m="4">
|
||||
<el-table
|
||||
:border="true"
|
||||
:data="propsRow.detail"
|
||||
:data="propsRow.detailList"
|
||||
:header-cell-style="{ 'text-align': 'center' }"
|
||||
:cell-style="{ 'text-align': 'center' }"
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column label="序号" type="index" width="60px" />
|
||||
<el-table-column label="计划日期" prop="date" />
|
||||
<el-table-column label="数量" prop="planNum" width="60" />
|
||||
<el-table-column label="完成量" prop="finishedNum" width="60" />
|
||||
<el-table-column label="AI填报" prop="autoFill" width="60" />
|
||||
<el-table-column label="操作" class-name="small-padding" width="200px" fixed="right">
|
||||
<el-table-column label="数量" prop="planNumber" width="60" />
|
||||
<el-table-column label="完成量" prop="finishedNumber" width="70" />
|
||||
<el-table-column label="AI填报" prop="aiFill" width="70" />
|
||||
<el-table-column label="操作" class-name="small-padding" width="170px" fixed="right">
|
||||
<template #default="{ row: scopeRow, $index }">
|
||||
<el-button type="primary" link @click="handleDayAdd(scopeRow, propsRow)">
|
||||
<el-icon><ele-Plus /></el-icon>日报
|
||||
<el-icon><Plus /></el-icon>日报
|
||||
</el-button>
|
||||
<el-button type="success" link @click="handleView(scopeRow, propsRow)">
|
||||
<el-icon><ele-View /></el-icon>查看
|
||||
<el-icon><View /></el-icon>查看
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -49,8 +57,8 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="60px" />
|
||||
<el-table-column label="计划数量" prop="planNum" min-width="100px" />
|
||||
<el-table-column label="完成数量" prop="finishedNum" min-width="100px" />
|
||||
<el-table-column label="计划数量" prop="planNumber" min-width="100px" />
|
||||
<el-table-column label="完成数量" prop="finishedNumber" min-width="100px" />
|
||||
<el-table-column label="延期量" min-width="100px">
|
||||
<template #default="{ row: scopeRow }">
|
||||
<el-tag :type="filterW(scopeRow) ? 'danger' : 'success'">
|
||||
@ -58,10 +66,10 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="AI填报" prop="autoFill" min-width="100px" />
|
||||
<el-table-column label="AI填报" prop="aiFill" min-width="100px" />
|
||||
<el-table-column label="开始时间" min-width="100px">
|
||||
<template #default="{ row: scopeRow }">
|
||||
<span>{{ scopeRow.startAt.split(' ')[0] }}</span>
|
||||
<span>{{ scopeRow.startDate.split(' ')[0] }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -73,13 +81,14 @@
|
||||
@pagination="getWorkList"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:isSmall="5"
|
||||
class="float-left mt-4.5!"
|
||||
/>
|
||||
</div>
|
||||
<div class="box_right" v-if="showDayWork">
|
||||
<div class="time_submit">
|
||||
<span>{{ formDetail.submitTime }}</span>
|
||||
<el-button type="primary" :disabled="!checkedList.length || flag" @click="onUploadDaily" size="large">
|
||||
<el-icon><ele-Upload /></el-icon>提交日报
|
||||
<el-button type="primary" :disabled="!checkedList.length || flag" @click="onUploadDaily" size="small">
|
||||
<el-icon><Upload /></el-icon>提交日报
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
@ -97,14 +106,14 @@
|
||||
<el-table-column label="状态" align="center" min-width="100px">
|
||||
<template #default="{ row: scopeRow }">
|
||||
<el-tag :type="typeList[scopeRow.status]">
|
||||
{{ filterStatus(scopeRow.status) }}
|
||||
{{ filterStatus(Number(scopeRow.status)) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="detailTotal > 0"
|
||||
v-show="1"
|
||||
:total="detailTotal"
|
||||
v-model:page="detailQueryParams.pageNum"
|
||||
v-model:limit="detailQueryParams.pageSize"
|
||||
@ -116,8 +125,8 @@
|
||||
<div class="box_right" v-else>
|
||||
<div class="time_submit">
|
||||
<span>{{ formDetail.submitTime }}</span>
|
||||
<el-button type="danger" :disabled="single" @click="handleRemove(null)" size="large">
|
||||
<el-icon><ele-SemiSelect /></el-icon>批量移除
|
||||
<el-button type="danger" :disabled="single" @click="handleRemove(null)" size="small">
|
||||
<el-icon><Minus /></el-icon>批量移除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
@ -134,14 +143,14 @@
|
||||
<el-table-column label="编号" align="center" prop="name" min-width="100px" />
|
||||
<el-table-column label="填报方式" align="center" prop="status" min-width="100px">
|
||||
<template #default="{ row: scopeRow }">
|
||||
<span v-if="scopeRow.status === 2">手动填报</span>
|
||||
<span v-if="scopeRow.status === 3">AI识别</span>
|
||||
<span v-if="scopeRow.finishType === '1'">手动填报</span>
|
||||
<span v-if="scopeRow.finishType === '2'">AI识别</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" min-width="100px">
|
||||
<template #default="{ row: scopeRow }">
|
||||
<el-button type="danger" link @click="handleRemove(scopeRow)">
|
||||
<el-icon><ele-SemiSelect /></el-icon>移除
|
||||
<el-icon><Minus /></el-icon>移除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -175,7 +184,7 @@ import { workScheduleListQuery } from '@/api/progress/plan/types';
|
||||
|
||||
// 响应式状态
|
||||
const state = reactive<{
|
||||
expandRowKeys: number[];
|
||||
expandRowKeys: string[];
|
||||
loading: boolean;
|
||||
isShowDialog: boolean;
|
||||
queryParams: workScheduleListQuery;
|
||||
@ -186,7 +195,7 @@ const state = reactive<{
|
||||
workId: string;
|
||||
id: string;
|
||||
submitTime: string;
|
||||
finishedNum: number;
|
||||
finishedNumber: number;
|
||||
};
|
||||
detialList: any[];
|
||||
detailTotal: number;
|
||||
@ -225,12 +234,12 @@ const state = reactive<{
|
||||
workId: '',
|
||||
id: '',
|
||||
submitTime: '选择日期',
|
||||
finishedNum: 0
|
||||
finishedNumber: 0
|
||||
},
|
||||
detialList: [],
|
||||
detailTotal: 0,
|
||||
detailQueryParams: {
|
||||
pageSize: 20,
|
||||
pageSize: 10,
|
||||
pageNum: 1
|
||||
},
|
||||
loading1: false,
|
||||
@ -242,7 +251,7 @@ const state = reactive<{
|
||||
detialWordList: [],
|
||||
detailTotalWork: 0,
|
||||
detailQueryParamsWork: {
|
||||
pageSize: 20,
|
||||
pageSize: 10,
|
||||
pageNum: 1
|
||||
},
|
||||
single: true,
|
||||
@ -318,15 +327,13 @@ const resetForm = (bool: boolean) => {
|
||||
const getPvModuleList = () => {
|
||||
loading1.value = true;
|
||||
pvModuleList({
|
||||
workId: formDetail.value.workId,
|
||||
...detailQueryParams.value,
|
||||
type: infoDetail.value.work_type,
|
||||
status: 0
|
||||
id: formDetail.value.id,
|
||||
...detailQueryParams.value
|
||||
}).then((res: any) => {
|
||||
loading1.value = false;
|
||||
if (res.code === 0) {
|
||||
detialList.value = res.data.list;
|
||||
detailTotal.value = res.data.total;
|
||||
if (res.code === 200) {
|
||||
detialList.value = res.rows;
|
||||
detailTotal.value = res.total;
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -335,13 +342,13 @@ const getPvModuleList = () => {
|
||||
const getWorkList = (bool = false) => {
|
||||
loading.value = true;
|
||||
workScheduleList(queryParams.value).then((res: any) => {
|
||||
if (res.code === 0) {
|
||||
state.tableData = res.data.list.map((item: any, i: number) => {
|
||||
if (res.code === 200) {
|
||||
state.tableData = res.rows.map((item: any, i: number) => {
|
||||
item.index = i + 1;
|
||||
item.autoFill = item.detail?.reduce((sum: number, child: any) => sum + child.autoFill, 0) || 0;
|
||||
item.aiFill = item.detailList?.reduce((sum: number, child: any) => sum + child.aiFill, 0) || 0;
|
||||
return item;
|
||||
});
|
||||
state.total = res.data.total;
|
||||
state.total = res.total;
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
@ -366,7 +373,7 @@ const indexMethod = (index: number): number => {
|
||||
// 日报添加
|
||||
const handleDayAdd = (row: any, obj: any) => {
|
||||
resetForm(true);
|
||||
formDetail.value.id = obj.id;
|
||||
formDetail.value.id = row.id;
|
||||
formDetail.value.submitTime = row.date;
|
||||
state.updateRow = row;
|
||||
getPvModuleList();
|
||||
@ -376,8 +383,9 @@ const tableKey = (row: any) => row.id;
|
||||
|
||||
// 展开行处理
|
||||
const clickOpen = (row: any) => {
|
||||
const index = state.expandRowKeys.indexOf(row.id);
|
||||
index === -1 ? state.expandRowKeys.push(row.id) : state.expandRowKeys.splice(index, 1);
|
||||
const rowId = String(row.id);
|
||||
const index = state.expandRowKeys.indexOf(rowId);
|
||||
index === -1 ? state.expandRowKeys.push(rowId) : state.expandRowKeys.splice(index, 1);
|
||||
state.expandRowKeys = [...new Set(state.expandRowKeys)];
|
||||
};
|
||||
|
||||
@ -394,22 +402,21 @@ const onUploadDaily = () => {
|
||||
}
|
||||
|
||||
const obj = {
|
||||
ids: checkedList.value,
|
||||
workID: formDetail.value.workId,
|
||||
doneTime: formDetail.value.submitTime,
|
||||
planID: formDetail.value.id
|
||||
finishedDetailIdList: checkedList.value,
|
||||
id: formDetail.value.id
|
||||
};
|
||||
|
||||
state.flag = true;
|
||||
addDaily(obj).then((res: any) => {
|
||||
if (res.code === 0) {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('添加成功');
|
||||
if (state.updateRow) {
|
||||
state.updateRow.finishedNum += checkedList.value.length;
|
||||
state.updateRow.finishedNumber += checkedList.value.length;
|
||||
}
|
||||
checkedList.value = [];
|
||||
multipleTableRef.value?.clearSelection();
|
||||
getPvModuleList();
|
||||
getWorkList();
|
||||
} else {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
@ -420,10 +427,11 @@ const onUploadDaily = () => {
|
||||
// 查看日报
|
||||
const handleView = (row: any, obj: any) => {
|
||||
resetForm(false);
|
||||
getDailyBookList(row.date);
|
||||
state.updateRow = row;
|
||||
formDetail.value.id = obj.id;
|
||||
formDetail.value.id = row.id;
|
||||
formDetail.value.submitTime = row.date;
|
||||
getDailyBookList(row.date);
|
||||
|
||||
showDayWork.value = false;
|
||||
};
|
||||
|
||||
@ -431,12 +439,12 @@ const handleView = (row: any, obj: any) => {
|
||||
const getDailyBookList = (doneTime: string) => {
|
||||
detialWordList.value = [];
|
||||
getDailyBook({
|
||||
workId: formDetail.value.workId,
|
||||
type: infoDetail.value.work_type,
|
||||
doneTime
|
||||
id: formDetail.value.id,
|
||||
...detailQueryParams.value
|
||||
}).then((res: any) => {
|
||||
if (res.code === 0) {
|
||||
detialWordList.value = res.data.list || [];
|
||||
if (res.code === 200) {
|
||||
detialWordList.value = res.rows || [];
|
||||
detailTotalWork.value = res.total;
|
||||
} else {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
@ -453,10 +461,8 @@ const handleSelectionChangeWork = (selection: any[]) => {
|
||||
const handleRemove = (row?: any) => {
|
||||
const planID = row ? [row.id] : state.checkList;
|
||||
const obj = {
|
||||
planID,
|
||||
id: formDetail.value.id,
|
||||
workID: formDetail.value.workId,
|
||||
time: formDetail.value.submitTime
|
||||
detailIdList: planID,
|
||||
id: formDetail.value.id
|
||||
};
|
||||
|
||||
ElMessageBox.confirm('确认移除该条数据?', '温馨提示', {
|
||||
@ -466,12 +472,13 @@ const handleRemove = (row?: any) => {
|
||||
})
|
||||
.then(() => {
|
||||
deleteDaily(obj).then((res: any) => {
|
||||
if (res.code === 0) {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('移除成功');
|
||||
if (state.updateRow) {
|
||||
state.updateRow.finishedNum -= planID.length;
|
||||
state.updateRow.finishedNumber -= planID.length;
|
||||
}
|
||||
getDailyBookList(formDetail.value.submitTime);
|
||||
getWorkList();
|
||||
} else {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
@ -482,14 +489,14 @@ const handleRemove = (row?: any) => {
|
||||
|
||||
// 延期计算
|
||||
const filterW = (row: any): number => {
|
||||
const { finishedNum, planNum, endAt } = row;
|
||||
const { finishedNumber, planNumber, endAt } = row;
|
||||
if (!endAt) return 0;
|
||||
|
||||
const endTime = new Date(endAt).getTime();
|
||||
const now = new Date().getTime();
|
||||
|
||||
if (endTime <= now && planNum > finishedNum) {
|
||||
return planNum - finishedNum;
|
||||
if (endTime <= now && planNumber > finishedNumber) {
|
||||
return planNumber - finishedNumber;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
@ -500,6 +507,32 @@ defineExpose({
|
||||
closeDialog
|
||||
});
|
||||
const emit = defineEmits(['getProgressList']);
|
||||
|
||||
let scrollTop = 0;
|
||||
|
||||
watch(
|
||||
() => isShowDialog.value,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
scrollTop = window.scrollY || document.documentElement.scrollTop;
|
||||
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
||||
|
||||
document.body.style.position = 'fixed';
|
||||
document.body.style.top = `-${scrollTop}px`;
|
||||
document.body.style.width = '100%';
|
||||
document.body.style.paddingRight = `${scrollbarWidth}px`; // 👈 补偿滚动条宽度
|
||||
} else {
|
||||
document.body.style.position = '';
|
||||
document.body.style.top = '';
|
||||
document.body.style.width = '';
|
||||
document.body.style.paddingRight = ''; // 👈 恢复
|
||||
|
||||
nextTick(() => {
|
||||
window.scrollTo(0, scrollTop);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -253,6 +253,7 @@ const onSubmit = () => {
|
||||
workScheduleAddPlan(payload).then((res: any) => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('添加成功');
|
||||
emit('getProgressList');
|
||||
closeDialog();
|
||||
} else {
|
||||
ElMessage.error(res.message);
|
||||
@ -292,6 +293,7 @@ const fetchLastTime = (row: typeof infoDetail) => {
|
||||
|
||||
// Export function if needed externally
|
||||
defineExpose({ openDialog });
|
||||
const emit = defineEmits(['getProgressList']);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
@ -47,10 +47,27 @@
|
||||
border
|
||||
>
|
||||
<el-table-column label="" width="50" type="expand">
|
||||
<template #header>
|
||||
<el-icon
|
||||
class="cursor-pointer text-4! transform-rotate-z--90 transition-all-300"
|
||||
:class="!isExpandAll ? 'transform-rotate-z--90' : 'transform-rotate-z-90'"
|
||||
@click="handleToggleExpandAll"
|
||||
><Expand
|
||||
/></el-icon>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-card class="pl-25" shadow="hover">
|
||||
<el-table :data="scope.row.children" border>
|
||||
<el-table-column label="名称" align="center" prop="name" width="150" />
|
||||
<el-table-column label="名称" align="center" prop="name" width="170">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip :content="row.remark" placement="top" effect="dark" v-if="row.remark">
|
||||
<span class="flex items-center justify-center"
|
||||
><i class="iconfont icon-wenhao mr-0.5 text-3.5! text-#999"></i>{{ row.name }}</span
|
||||
>
|
||||
</el-tooltip>
|
||||
<span v-else>{{ row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||
<template #default="{ row }">
|
||||
<dict-tag :options="progress_status" :value="row.status" />
|
||||
@ -85,7 +102,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
<!-- <el-button
|
||||
type="warning"
|
||||
icon="Download"
|
||||
link
|
||||
@ -94,12 +111,13 @@
|
||||
v-hasPermi="['progress:progressCategory:add']"
|
||||
>
|
||||
导入数据
|
||||
</el-button>
|
||||
</el-button> -->
|
||||
<el-button
|
||||
type="warning"
|
||||
icon="Download"
|
||||
link
|
||||
size="small"
|
||||
v-if="scope.row.name === '光伏板'"
|
||||
@click="openDialog(scope.row, 'importTableStatus', '上传表格')"
|
||||
v-hasPermi="['progress:progressCategory:add']"
|
||||
>
|
||||
@ -254,9 +272,13 @@ const { queryParams, form, rules } = toRefs(data);
|
||||
const getList = async () => {
|
||||
if (!queryParams.value.matrixId) {
|
||||
const res = await getProjectSquare(currentProject.value.id);
|
||||
if (!matrixValue.value) matrixValue.value = res.rows[0].id;
|
||||
matrixOptions.value = res.rows;
|
||||
queryParams.value.matrixId = res.rows[0].id;
|
||||
if (res.rows.length === 0) {
|
||||
proxy?.$modal.msgWarning('当前项目下没有方阵,请先创建方阵');
|
||||
} else {
|
||||
if (!matrixValue.value) matrixValue.value = res.rows[0].id;
|
||||
matrixOptions.value = res.rows;
|
||||
queryParams.value.matrixId = res.rows[0].id;
|
||||
}
|
||||
}
|
||||
loading.value = true;
|
||||
const res = await listProgressCategory(queryParams.value);
|
||||
@ -288,6 +310,13 @@ const reset = () => {
|
||||
progressCategoryFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
//切换项目重置方阵
|
||||
const resetMatrix = () => {
|
||||
matrixValue.value = undefined;
|
||||
queryParams.value.matrixId = undefined;
|
||||
matrixOptions.value = [];
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
getList();
|
||||
@ -384,6 +413,7 @@ const listeningProject = watch(
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
resetMatrix();
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
282
src/views/progress/progressCategoryTemplate/index.vue
Normal file
282
src/views/progress/progressCategoryTemplate/index.vue
Normal file
@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="父类别id" prop="pid">
|
||||
<el-input v-model="queryParams.pid" placeholder="请输入父类别id" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="类别名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入类别名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="项目id" prop="projectId">
|
||||
<el-input v-model="queryParams.projectId" placeholder="请输入项目id" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['progress:progressCategoryTemplate:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
<el-table
|
||||
ref="progressCategoryTemplateTableRef"
|
||||
v-loading="loading"
|
||||
:data="progressCategoryTemplateList"
|
||||
row-key="id"
|
||||
:default-expand-all="isExpandAll"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
>
|
||||
<el-table-column label="类别名称" align="center" prop="name" />
|
||||
<el-table-column label="计量方式" align="center" prop="unitType" />
|
||||
<el-table-column label="工作类型" align="center" prop="workType" />
|
||||
<el-table-column label="项目id" align="center" prop="projectId" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['progress:progressCategoryTemplate:edit']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="新增" placement="top">
|
||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['progress:progressCategoryTemplate:add']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['progress:progressCategoryTemplate:remove']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<!-- 添加或修改进度类别模版对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="progressCategoryTemplateFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="父类别id" prop="pid">
|
||||
<el-tree-select
|
||||
v-model="form.pid"
|
||||
:data="progressCategoryTemplateOptions"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||
value-key="id"
|
||||
placeholder="请选择父类别id"
|
||||
check-strictly
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="类别名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入类别名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="项目id" prop="projectId">
|
||||
<el-input v-model="form.projectId" placeholder="请输入项目id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ProgressCategoryTemplate" lang="ts">
|
||||
import { listProgressCategoryTemplate, getProgressCategoryTemplate, delProgressCategoryTemplate, addProgressCategoryTemplate, updateProgressCategoryTemplate } from "@/api/progress/progressCategoryTemplate";
|
||||
import { ProgressCategoryTemplateVO, ProgressCategoryTemplateQuery, ProgressCategoryTemplateForm } from '@/api/progress/progressCategoryTemplate/types';
|
||||
|
||||
type ProgressCategoryTemplateOption = {
|
||||
id: number;
|
||||
name: string;
|
||||
children?: ProgressCategoryTemplateOption[];
|
||||
}
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;;
|
||||
|
||||
|
||||
const progressCategoryTemplateList = ref<ProgressCategoryTemplateVO[]>([]);
|
||||
const progressCategoryTemplateOptions = ref<ProgressCategoryTemplateOption[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
const isExpandAll = ref(true);
|
||||
const loading = ref(false);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const progressCategoryTemplateFormRef = ref<ElFormInstance>();
|
||||
const progressCategoryTemplateTableRef = ref<ElTableInstance>()
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
|
||||
const initFormData: ProgressCategoryTemplateForm = {
|
||||
id: undefined,
|
||||
pid: undefined,
|
||||
name: undefined,
|
||||
unitType: undefined,
|
||||
workType: undefined,
|
||||
projectId: undefined,
|
||||
remark: undefined,
|
||||
}
|
||||
|
||||
const data = reactive<PageData<ProgressCategoryTemplateForm, ProgressCategoryTemplateQuery>>({
|
||||
form: {...initFormData},
|
||||
queryParams: {
|
||||
pid: undefined,
|
||||
name: undefined,
|
||||
unitType: undefined,
|
||||
workType: undefined,
|
||||
projectId: undefined,
|
||||
params: {
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
id: [
|
||||
{ required: true, message: "主键id不能为空", trigger: "blur" }
|
||||
],
|
||||
pid: [
|
||||
{ required: true, message: "父类别id不能为空", trigger: "blur" }
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: "类别名称不能为空", trigger: "blur" }
|
||||
],
|
||||
unitType: [
|
||||
{ required: true, message: "计量方式不能为空", trigger: "change" }
|
||||
],
|
||||
projectId: [
|
||||
{ required: true, message: "项目id不能为空", trigger: "blur" }
|
||||
],
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询进度类别模版列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listProgressCategoryTemplate(queryParams.value);
|
||||
const data = proxy?.handleTree<ProgressCategoryTemplateVO>(res.data, "id", "pid");
|
||||
if (data) {
|
||||
progressCategoryTemplateList.value = data;
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询进度类别模版下拉树结构 */
|
||||
const getTreeselect = async () => {
|
||||
const res = await listProgressCategoryTemplate();
|
||||
progressCategoryTemplateOptions.value = [];
|
||||
const data: ProgressCategoryTemplateOption = { id: 0, name: '顶级节点', children: [] };
|
||||
data.children = proxy?.handleTree<ProgressCategoryTemplateOption>(res.data, "id", "pid");
|
||||
progressCategoryTemplateOptions.value.push(data);
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
}
|
||||
|
||||
// 表单重置
|
||||
const reset = () => {
|
||||
form.value = {...initFormData}
|
||||
progressCategoryTemplateFormRef.value?.resetFields();
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = (row?: ProgressCategoryTemplateVO) => {
|
||||
reset();
|
||||
getTreeselect();
|
||||
if (row != null && row.id) {
|
||||
form.value.pid = row.id;
|
||||
} else {
|
||||
form.value.pid = 0;
|
||||
}
|
||||
dialog.visible = true;
|
||||
dialog.title = "添加进度类别模版";
|
||||
}
|
||||
|
||||
/** 展开/折叠操作 */
|
||||
const handleToggleExpandAll = () => {
|
||||
isExpandAll.value = !isExpandAll.value;
|
||||
toggleExpandAll(progressCategoryTemplateList.value, isExpandAll.value)
|
||||
}
|
||||
|
||||
/** 展开/折叠操作 */
|
||||
const toggleExpandAll = (data: ProgressCategoryTemplateVO[], status: boolean) => {
|
||||
data.forEach((item) => {
|
||||
progressCategoryTemplateTableRef.value?.toggleRowExpansion(item, status)
|
||||
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status)
|
||||
})
|
||||
}
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row: ProgressCategoryTemplateVO) => {
|
||||
reset();
|
||||
await getTreeselect();
|
||||
if (row != null) {
|
||||
form.value.pid = row.pid;
|
||||
}
|
||||
const res = await getProgressCategoryTemplate(row.id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = "修改进度类别模版";
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
progressCategoryTemplateFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
if (form.value.id) {
|
||||
await updateProgressCategoryTemplate(form.value).finally(() => buttonLoading.value = false);
|
||||
} else {
|
||||
await addProgressCategoryTemplate(form.value).finally(() => buttonLoading.value = false);
|
||||
}
|
||||
proxy?.$modal.msgSuccess("操作成功");
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row: ProgressCategoryTemplateVO) => {
|
||||
await proxy?.$modal.confirm('是否确认删除进度类别模版编号为"' + row.id + '"的数据项?');
|
||||
loading.value = true;
|
||||
await delProgressCategoryTemplate(row.id).finally(() => loading.value = false);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess("删除成功");
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
479
src/views/progress/progressPaper/index.vue
Normal file
479
src/views/progress/progressPaper/index.vue
Normal file
@ -0,0 +1,479 @@
|
||||
<template>
|
||||
<div class="header flex justify-end">
|
||||
<el-form :model="queryParams" ref="form" label-width="80px" inline class="flex items-center">
|
||||
<el-form-item label="请选择项目:" prop="pid" label-width="100">
|
||||
<el-select v-model="selectedProjectId" placeholder="请选择" @change="handleSelect" clearable>
|
||||
<el-option v-for="item in ProjectList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="请选择方阵:" prop="pid" label-width="100">
|
||||
<el-select v-model="matrixValue" placeholder="请选择" @change="handleChange" clearable>
|
||||
<el-option v-for="item in matrixOptions" :key="item.id" :label="item.matrixName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="ol-map" id="olMap"></div>
|
||||
<div class="aside">
|
||||
<el-tree
|
||||
style="max-width: 600px"
|
||||
:data="progressCategoryList"
|
||||
ref="treeRef"
|
||||
show-checkbox
|
||||
@check-change="handleCheckChange"
|
||||
:props="treeProps"
|
||||
:load="loadNode"
|
||||
node-key="id"
|
||||
lazy
|
||||
@node-collapse="closeNode"
|
||||
@node-expand="openNode"
|
||||
/>
|
||||
</div>
|
||||
<div class="submit">
|
||||
<el-button type="primary" size="default" @click="submit">提交</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Map from 'ol/Map'; // OpenLayers的主要类,用于创建和管理地图
|
||||
import View from 'ol/View'; // OpenLayers的视图类,定义地图的视图属性
|
||||
import { Tile as TileLayer } from 'ol/layer'; // OpenLayers的瓦片图层类
|
||||
import { XYZ } from 'ol/source'; // OpenLayers的瓦片数据源,包括XYZ格式和OpenStreetMap专用的数据源
|
||||
import { defaults as defaultControls, defaults, FullScreen, MousePosition, ScaleLine } from 'ol/control';
|
||||
import { fromLonLat } from 'ol/proj';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { getProjectSquare, listProgressCategory, addDaily, workScheduleListPosition } from '@/api/progress/plan';
|
||||
import { ProgressCategoryVO, progressPlanDetailForm } from '@/api/progress/plan/types';
|
||||
import { Circle, Fill, Stroke, Style, Text } from 'ol/style';
|
||||
import Feature from 'ol/Feature';
|
||||
import { Point, Polygon } from 'ol/geom';
|
||||
import VectorSource from 'ol/source/Vector';
|
||||
import VectorLayer from 'ol/layer/Vector';
|
||||
import Node from 'element-plus/es/components/tree/src/model/node.mjs';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const ProjectList = computed(() => userStore.projects);
|
||||
const selectedProjectId = ref(userStore.selectedProject?.id || '');
|
||||
const treeRef = ref();
|
||||
const queryParams = ref({
|
||||
pid: undefined,
|
||||
name: undefined,
|
||||
unitType: undefined,
|
||||
projectId: currentProject.value.id,
|
||||
matrixId: undefined,
|
||||
params: {}
|
||||
});
|
||||
const submitForm = ref<progressPlanDetailForm>({
|
||||
id: '',
|
||||
finishedDetailIdList: [] as string[]
|
||||
});
|
||||
const loading = ref(false);
|
||||
const matrixOptions = ref([]);
|
||||
const matrixValue = ref<number | undefined>(matrixOptions.value.length > 0 ? matrixOptions.value[0].id : undefined);
|
||||
const progressCategoryList = ref<ProgressCategoryVO[]>([]);
|
||||
const treeProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
isLeaf: 'leaf',
|
||||
hasChildren: 'hasChildren' // 重要
|
||||
};
|
||||
|
||||
//切换项目
|
||||
const handleSelect = (projectId: string) => {
|
||||
const selectedProject = ProjectList.value.find((p) => p.id === projectId);
|
||||
if (selectedProject) {
|
||||
userStore.setSelectedProject(selectedProject);
|
||||
|
||||
resetMatrix();
|
||||
getList();
|
||||
}
|
||||
};
|
||||
|
||||
/** 进度类别树选中事件 */
|
||||
const handleCheckChange = (data: any, checked: boolean, indeterminate: boolean) => {
|
||||
const node: Node | undefined = treeRef.value?.getNode(data.id);
|
||||
if (node && node.level === 3) {
|
||||
console.log('第三级节点被选中:', data, '选中状态:', checked);
|
||||
}
|
||||
if (!node || node.level !== 3 || !checked) return;
|
||||
|
||||
const parent = node.parent;
|
||||
if (!parent) return;
|
||||
|
||||
// 遍历兄弟节点,取消选中除当前节点之外的其他第三级节点
|
||||
parent.childNodes.forEach((sibling: Node) => {
|
||||
if (sibling !== node) {
|
||||
treeRef.value.setChecked(sibling.data.id, false, false);
|
||||
}
|
||||
});
|
||||
submitForm.value.id = data.id; // 设置提交表单的id
|
||||
};
|
||||
|
||||
/** 关闭节点事件 */
|
||||
const closeNode = (node: any) => {
|
||||
// 清除子节点
|
||||
if (node.pid) {
|
||||
node.threeChildren.forEach((child: any) => {
|
||||
const feature = featureMap[child.id];
|
||||
if (feature && sharedSource.hasFeature(feature)) {
|
||||
sharedSource.removeFeature(feature);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** 打开节点事件 */
|
||||
const openNode = (node: any) => {
|
||||
// 清除子节点
|
||||
if (!node.pid) return;
|
||||
addPointToMap(node.threeChildren); // 添加点到地图
|
||||
};
|
||||
|
||||
//懒加载子节点
|
||||
const loadNode = async (node: any, resolve: (data: any[]) => void) => {
|
||||
if (node.level !== 2) {
|
||||
// 只对二级节点加载子节点
|
||||
resolve(node.data.children || []);
|
||||
return;
|
||||
}
|
||||
|
||||
const secondLevelNodeId = node.data.id;
|
||||
const res = await workScheduleListPosition(secondLevelNodeId); // 替换成你的 API
|
||||
|
||||
const children = res.data.detailList || [];
|
||||
|
||||
if (children.length === 0) {
|
||||
proxy?.$modal.msgWarning(`节点 "${node.data.name}" 为空`);
|
||||
resolve([]);
|
||||
}
|
||||
|
||||
// 标记子节点为叶子节点
|
||||
const threeLeafList = children.map((detail) => {
|
||||
return {
|
||||
...detail,
|
||||
name: detail.date, // 设置为叶子节点
|
||||
leaf: true // 标记为叶子节点
|
||||
};
|
||||
});
|
||||
progressCategoryList.value.forEach((item, i) => {
|
||||
let indexNum = item.children.findIndex((item) => item.id === secondLevelNodeId);
|
||||
if (indexNum !== -1) {
|
||||
item.children[indexNum].threeChildren = res.data.facilityList; // 将子节点添加到当前节点的threeChildren属性中
|
||||
}
|
||||
});
|
||||
|
||||
resolve(threeLeafList);
|
||||
};
|
||||
|
||||
/** 提交按钮点击事件 */
|
||||
const submit = () => {
|
||||
console.log('sunbmitForm', submitForm.value);
|
||||
addDaily(submitForm.value)
|
||||
.then(() => {
|
||||
proxy?.$modal.msgSuccess('提交成功');
|
||||
resetTreeAndMap();
|
||||
})
|
||||
.catch((error) => {
|
||||
proxy?.$modal.msgError(`提交失败: ${error.message}`);
|
||||
});
|
||||
};
|
||||
|
||||
//重置树形结构选中以及图层高亮
|
||||
const resetTreeAndMap = () => {
|
||||
// 重置树形结构选中状态
|
||||
treeRef.value?.setCheckedKeys([]);
|
||||
// 清除地图上的所有高亮
|
||||
const scale = Math.max(map.getView().getZoom() / 10, 1); // 获取当前缩放比例
|
||||
sharedSource.getFeatures().forEach((feature) => {
|
||||
if (feature.get('highlighted')) {
|
||||
feature.setStyle(defaultStyle(feature.get('name'), scale)); // 恢复默认样式
|
||||
feature.set('highlighted', false); // 重置高亮状态
|
||||
}
|
||||
});
|
||||
// 清空已完成列表
|
||||
submitForm.value.finishedDetailIdList = [];
|
||||
};
|
||||
|
||||
/** 方阵选择器改变事件 */
|
||||
const handleChange = (value: number) => {
|
||||
queryParams.value.matrixId = value;
|
||||
|
||||
getList();
|
||||
};
|
||||
|
||||
//切换项目重置方阵
|
||||
const resetMatrix = () => {
|
||||
matrixValue.value = undefined;
|
||||
queryParams.value.matrixId = undefined;
|
||||
matrixOptions.value = [];
|
||||
};
|
||||
|
||||
/** 查询进度类别列表 */
|
||||
const getList = async () => {
|
||||
if (!queryParams.value.matrixId) {
|
||||
const res = await getProjectSquare(currentProject.value.id);
|
||||
if (res.rows.length === 0) {
|
||||
proxy?.$modal.msgWarning('当前项目下没有方阵,请先创建方阵');
|
||||
} else {
|
||||
if (!matrixValue.value) matrixValue.value = res.rows[0].id;
|
||||
matrixOptions.value = res.rows;
|
||||
queryParams.value.matrixId = res.rows[0].id;
|
||||
}
|
||||
}
|
||||
loading.value = true;
|
||||
queryParams.value.projectId = currentProject.value.id;
|
||||
const res = await listProgressCategory(queryParams.value);
|
||||
const data = proxy?.handleTree<ProgressCategoryVO>(res.data, 'id', 'pid');
|
||||
if (data) {
|
||||
progressCategoryList.value = data;
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
let map: any = null;
|
||||
const layerData = reactive<any>({});
|
||||
const centerPosition = ref(fromLonLat([107.12932403398425, 23.805564054229908]));
|
||||
|
||||
const initOLMap = () => {
|
||||
// 创造地图实例
|
||||
map = new Map({
|
||||
// 设置地图容器的ID
|
||||
target: 'olMap',
|
||||
// 定义地图的图层列表,用于显示特定的地理信息。
|
||||
layers: [
|
||||
// 高德地图
|
||||
// TileLayer表示一个瓦片图层,它由一系列瓦片(通常是图片)组成,用于在地图上显示地理数据。
|
||||
new TileLayer({
|
||||
// 设置图层的数据源为XYZ类型。XYZ是一个通用的瓦片图层源,它允许你通过URL模板来获取瓦片
|
||||
source: new XYZ({
|
||||
url: 'https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}'
|
||||
})
|
||||
})
|
||||
],
|
||||
// 设置地图的视图参数
|
||||
// View表示地图的视图,它定义了地图的中心点、缩放级别、旋转角度等参数。
|
||||
view: new View({
|
||||
// fromLonLat是一个函数,用于将经纬度坐标转换为地图的坐标系统。
|
||||
center: centerPosition.value, //地图中心点
|
||||
zoom: 15, // 缩放级别
|
||||
minZoom: 0, // 最小缩放级别
|
||||
// maxZoom: 18, // 最大缩放级别
|
||||
constrainResolution: true // 因为存在非整数的缩放级别,所以设置该参数为true来让每次缩放结束后自动缩放到距离最近的一个整数级别,这个必须要设置,当缩放在非整数级别时地图会糊
|
||||
// projection: 'EPSG:4326' // 投影坐标系,默认是3857
|
||||
}),
|
||||
|
||||
//加载控件到地图容器中
|
||||
controls: defaultControls({
|
||||
zoom: false,
|
||||
rotate: false,
|
||||
attribution: false
|
||||
})
|
||||
});
|
||||
map.on('click', (e: any) => {
|
||||
const zoom = map.getView().getZoom();
|
||||
const scale = Math.max(zoom / 10, 1); // 缩放比例,根据需要调整公式
|
||||
map.forEachFeatureAtPixel(e.pixel, (feature: Feature) => {
|
||||
if (feature.get('status') === '2') return; // 如果是完成状态,直接返回
|
||||
const isHighlighted = feature.get('highlighted') === true;
|
||||
const geomType = feature.getGeometry().getType();
|
||||
if (isHighlighted) {
|
||||
feature.setStyle(defaultStyle(feature.get('name'), scale)); // 清除高亮样式
|
||||
feature.set('highlighted', false);
|
||||
submitForm.value.finishedDetailIdList = submitForm.value.finishedDetailIdList.filter((id) => id !== feature.get('id')); // 从已完成列表中移除
|
||||
return;
|
||||
}
|
||||
if (geomType === 'Polygon') {
|
||||
feature.setStyle(highlightStyle(feature.get('name'), scale));
|
||||
feature.set('highlighted', true);
|
||||
submitForm.value.finishedDetailIdList.push(feature.get('id')); // 添加到已完成列表
|
||||
}
|
||||
});
|
||||
});
|
||||
map.getView().on('change:resolution', () => {
|
||||
const zoom = map.getView().getZoom();
|
||||
const scale = Math.max(zoom / 10, 1); // 缩放比例,根据需要调整公式
|
||||
|
||||
sharedSource.getFeatures().forEach((feature) => {
|
||||
const style = feature.getStyle();
|
||||
|
||||
if (style instanceof Style && style.getText()) {
|
||||
style.getText().setScale(scale);
|
||||
feature.setStyle(style); // 重新应用样式
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const highlightStyle = (name, scale) => {
|
||||
return new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'orange',
|
||||
width: 4
|
||||
}),
|
||||
fill: new Fill({
|
||||
color: 'rgba(255,165,0,0.3)' // 半透明橙色
|
||||
}),
|
||||
text: new Text({
|
||||
font: '14px Microsoft YaHei',
|
||||
text: name,
|
||||
placement: 'line', // 👈 关键属性
|
||||
offsetX: 50, // 向右偏移 10 像素
|
||||
offsetY: 20, // 向下偏移 5 像素
|
||||
scale,
|
||||
fill: new Fill({ color: 'orange' })
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
const defaultStyle = (name, scale) => {
|
||||
return new Style({
|
||||
stroke: new Stroke({
|
||||
color: '#003366',
|
||||
width: 2
|
||||
}),
|
||||
text: new Text({
|
||||
font: '12px Microsoft YaHei',
|
||||
text: name,
|
||||
scale,
|
||||
placement: 'line', // 👈 关键属性
|
||||
offsetX: 50, // 向右偏移 10 像素
|
||||
offsetY: 20, // 向下偏移 5 像素
|
||||
fill: new Fill({ color: '#003366 ' })
|
||||
}),
|
||||
fill: new Fill({ color: 'skyblue' })
|
||||
});
|
||||
};
|
||||
|
||||
const successStyle = (name, scale) => {
|
||||
return new Style({
|
||||
stroke: new Stroke({
|
||||
color: '#2E7D32 ',
|
||||
width: 2
|
||||
}),
|
||||
text: new Text({
|
||||
font: '14px Microsoft YaHei',
|
||||
text: name,
|
||||
scale,
|
||||
placement: 'line', // 👈 关键属性
|
||||
offsetX: 50, // 向右偏移 10 像素
|
||||
offsetY: 20, // 向下偏移 5 像素
|
||||
fill: new Fill({ color: '#FFFFFF ' })
|
||||
}),
|
||||
fill: new Fill({ color: '#7bdd63 ' })
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建图层
|
||||
* @param {*} pointObj 坐标数组
|
||||
* @param {*} type 类型
|
||||
* @param {*} id 唯一id
|
||||
* @param {*} name 名称
|
||||
* */
|
||||
// 共享 source 和图层(全局一次性创建)
|
||||
const sharedSource = new VectorSource();
|
||||
const sharedLayer = new VectorLayer({
|
||||
source: sharedSource,
|
||||
renderMode: 'image' // 提高渲染性能
|
||||
} as any);
|
||||
// id => Feature 映射表
|
||||
const featureMap: Record<string, Feature> = {};
|
||||
const creatPoint = (pointObj: Array<any>, type: string, id: string, name?: string, status?: string) => {
|
||||
let geometry;
|
||||
|
||||
if (type === 'Point') {
|
||||
geometry = new Point(fromLonLat(pointObj));
|
||||
} else if (type === 'Polygon') {
|
||||
const coords = pointObj.map((arr: any) => fromLonLat(arr));
|
||||
geometry = new Polygon([coords]);
|
||||
}
|
||||
|
||||
const feature = new Feature({ geometry });
|
||||
const zoom = map.getView().getZoom();
|
||||
const scale = Math.max(zoom / 10, 1); // 缩放比例,根据需要调整公式
|
||||
const pointStyle = new Style({
|
||||
image: new Circle({
|
||||
radius: 2,
|
||||
fill: new Fill({ color: 'red' })
|
||||
}),
|
||||
text: new Text({
|
||||
font: '12px Microsoft YaHei',
|
||||
text: name,
|
||||
scale,
|
||||
fill: new Fill({ color: '#7bdd63' })
|
||||
})
|
||||
});
|
||||
|
||||
const polygonStyle = status == '2' ? successStyle(name, scale) : defaultStyle(name || '', scale);
|
||||
|
||||
feature.setStyle(type === 'Point' ? pointStyle : polygonStyle);
|
||||
feature.set('name', name || ''); // 设置名称
|
||||
feature.set('status', status || ''); // 设置完成状态 2为完成 其他为未完成
|
||||
feature.set('id', id || '');
|
||||
// 缓存 feature(用于后续判断)
|
||||
featureMap[id] = feature;
|
||||
};
|
||||
|
||||
// 添加点到地图
|
||||
const addPointToMap = (features: Array<any>) => {
|
||||
features.forEach((item: any) => {
|
||||
const fid = item.id;
|
||||
|
||||
// 没创建过就先创建
|
||||
if (!featureMap[fid]) {
|
||||
creatPoint(item.positions, item.type, fid, item.name, item.status);
|
||||
}
|
||||
|
||||
// 添加到共享 source 中(避免重复添加)
|
||||
const feature = featureMap[fid];
|
||||
if (!sharedSource.hasFeature(feature)) {
|
||||
sharedSource.addFeature(feature);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 地图初始化
|
||||
initOLMap();
|
||||
map.addLayer(sharedLayer);
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ol-map {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
.header {
|
||||
height: 90px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
.aside {
|
||||
position: absolute;
|
||||
top: 10%;
|
||||
left: 30px;
|
||||
width: 300px;
|
||||
height: 70vh;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
z-index: 3;
|
||||
overflow: auto;
|
||||
}
|
||||
.submit {
|
||||
position: absolute;
|
||||
bottom: 70px;
|
||||
right: 70px;
|
||||
z-index: 3;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user