日常提交
This commit is contained in:
parent
ef57aabc55
commit
0fb4c5772c
@ -12,6 +12,7 @@ declare module 'vue' {
|
||||
'CenterMap copy': typeof import('./src/components/centerMap copy.vue')['default']
|
||||
CodeDialog: typeof import('./src/components/code-dialog/index.vue')['default']
|
||||
Components: typeof import('./src/components/index.js')['default']
|
||||
copy: typeof import('./src/components/centerMap copy.vue')['default']
|
||||
CurrentTime: typeof import('./src/components/currentTime.vue')['default']
|
||||
CustomBack: typeof import('./src/components/customBack.vue')['default']
|
||||
CustomCarouselPicture: typeof import('./src/components/custom-carousel-picture/index.vue')['default']
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 136 KiB |
@ -14,14 +14,9 @@ import { registerGlobalComponents } from './plugins/globalComponents';
|
||||
import { registerElIcons } from './plugins/icon';
|
||||
import { registerMicroApps } from './plugins/micro';
|
||||
import { registerSplitpanes } from './plugins/splitpanes';
|
||||
import VueTianditu from 'vue-tianditu';
|
||||
import { map_config } from './config/map';
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(pinia).use(router).use(ElementPlus).use(Avue).use(VueTianditu, {
|
||||
v: map_config.tianditu.version,
|
||||
tk: map_config.tianditu.token,
|
||||
});
|
||||
app.use(pinia).use(router).use(ElementPlus).use(Avue);
|
||||
registerGlobalComponents(app);
|
||||
registerElIcons(app);
|
||||
registerSplitpanes(app);
|
||||
|
@ -41,16 +41,23 @@ export const constantRoutes = [
|
||||
...inputSuppliesRoutes,
|
||||
{
|
||||
path: '/sub-government-affairs-service/output-products',
|
||||
name: 'outputProducts',
|
||||
name: 'OutputProducts',
|
||||
component: Layout,
|
||||
redirect: '/sub-government-affairs-service/output-products/index',
|
||||
redirect: '/sub-government-affairs-service/output-products/output-info',
|
||||
meta: { title: '产出品管理', icon: 'Box' },
|
||||
children: [
|
||||
// 产出品概览
|
||||
{
|
||||
path: '/sub-government-affairs-service/output-products/index',
|
||||
component: () => import('@/views/outputProductsManage/index.vue'),
|
||||
name: 'outputProductsIndex',
|
||||
meta: { title: '产出品管理', icon: 'List' },
|
||||
path: '/sub-government-affairs-service/output-products/output-statistics',
|
||||
component: () => import('@/views/output-products/output-statistics/index.vue'),
|
||||
name: 'OutputStatistics',
|
||||
meta: { title: '产出品概览', icon: 'Box' },
|
||||
},
|
||||
{
|
||||
path: '/sub-government-affairs-service/output-products/output-info',
|
||||
component: () => import('@/views/output-products/output-info/index.vue'),
|
||||
name: 'OutputInfo',
|
||||
meta: { title: '产出品信息', icon: 'List' },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
17
sub-government-affairs-service/src/utils/mapLoader.js
Normal file
17
sub-government-affairs-service/src/utils/mapLoader.js
Normal file
@ -0,0 +1,17 @@
|
||||
// MapLoader.js
|
||||
export function loadTiandituApi(config) {
|
||||
return new Promise((resolve) => {
|
||||
if (window.T) {
|
||||
// 已经加载过,直接返回 T
|
||||
resolve(window.T);
|
||||
return;
|
||||
}
|
||||
const script = document.createElement('script');
|
||||
script.id = 'tianditu-script';
|
||||
script.src = `http://api.tianditu.gov.cn/api?v=${config.version}&tk=${config.token}`;
|
||||
script.onload = () => {
|
||||
resolve(window.T);
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<div class="custom-page">
|
||||
<div class="custom-search">
|
||||
<AreaCascader v-model:region-code="searchForm.regionCode" v-model:grid-id="searchForm.id" :width="600" />
|
||||
<el-button type="primary" @click="handleSearch"> 搜索 </el-button>
|
||||
<el-button @click="resetSearch"> 重置 </el-button>
|
||||
</div>
|
||||
<avue-crud
|
||||
ref="crudRef"
|
||||
v-model:page="pageData"
|
||||
@ -10,13 +15,6 @@
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
>
|
||||
<template #search>
|
||||
<div class="custom-search">
|
||||
<AreaCascader v-model:region-code="searchForm.regionCode" v-model:grid-id="searchForm.id" :width="600" />
|
||||
<el-button type="primary" @click="handleSearch"> 搜索 </el-button>
|
||||
<el-button @click="resetSearch"> 重置 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- <template #menu-left>
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd">新增网格</el-button>
|
||||
</template> -->
|
||||
@ -152,10 +150,10 @@ const filterObject = (obj) => {
|
||||
const crudData = ref([]);
|
||||
const crudOptions = reactive({
|
||||
...CRUD_OPTIONS,
|
||||
addBtn: false,
|
||||
searchBtn: false,
|
||||
emptyBtn: false,
|
||||
menu: false,
|
||||
header: false,
|
||||
height: 'calc(100vh - 340px)',
|
||||
selection: true,
|
||||
column: [
|
||||
{ label: '行政区划编码', prop: 'AreaCode', width: 150 },
|
||||
{ label: '行政区划', prop: 'gridAreaName', width: 150 },
|
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="custom-page" :style="`background-image: url(${getAssetsFile('images/output/output.png')})`"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, computed } from 'vue';
|
||||
import { getAssetsFile } from '@/utils';
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-page {
|
||||
height: calc(100vh - 150px);
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
</style>
|
@ -1,88 +1,207 @@
|
||||
<template>
|
||||
<div class="custom-page">
|
||||
<h1>林地</h1>
|
||||
<!-- 搜索 -->
|
||||
<el-form :inline="true" :model="searchForm" class="search-bar">
|
||||
<el-form-item label="关键词">
|
||||
<el-input v-model="searchForm.name" placeholder="请输入关键词" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item lable="">
|
||||
<AreaCascader v-model:region-code="searchForm.regionCode" v-model:grid-id="searchForm.id" :width="600" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch"> 搜索 </el-button>
|
||||
<el-button @click="resetSearch"> 重置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 四个固定 Tabs -->
|
||||
<LandSearch :search="searchForm" @on-search="handleSearch" @on-reset="handleReset" />
|
||||
<el-tabs v-model="activeTab" @tab-click="handleTabChange">
|
||||
<!-- <el-tab-pane label="待提交" name="0" /> -->
|
||||
<el-tab-pane label="待审核" name="1" />
|
||||
<el-tab-pane label="已通过" name="2" />
|
||||
<el-tab-pane label="已驳回" name="0" />
|
||||
</el-tabs>
|
||||
<avue-crud
|
||||
ref="crudRef"
|
||||
v-model:page="pageData"
|
||||
:data="crudData"
|
||||
:option="crudOptions"
|
||||
:table-loading="loading"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
>
|
||||
<!-- <template #menu-left>
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd">新增网格</el-button>
|
||||
</template> -->
|
||||
<template #menu="scope">
|
||||
<custom-table-operate :actions="crudOptions.actions" :data="scope" />
|
||||
</template>
|
||||
</avue-crud>
|
||||
<TableComponent
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:table-data="tableData"
|
||||
:current-page="pageData.currentPage"
|
||||
:page-size="pageData.pageSize"
|
||||
:total="pageData.total"
|
||||
:show-pagination="true"
|
||||
:show-border="true"
|
||||
:show-sort="true"
|
||||
style="height: calc(100vh - 320px)"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
|
||||
<el-form ref="form" :model="formData" :rules="rules" :disabled="isReadonly" label-width="100px" class="form-item">
|
||||
<p class="form-title">填写网格信息</p>
|
||||
<el-form-item label="网格名称" prop="gridName">
|
||||
<el-input v-model="formData.gridName" placeholder="请输入网格名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属行政区域" prop="gridAreaCode">
|
||||
<AreaSelect v-model="formData.gridAreaCode" :label="null" :emit-path="false" />
|
||||
</el-form-item>
|
||||
<el-form-item label="网格化地图" prop="scopeImg">
|
||||
<FileUploader v-model="formData.scopeImg" :limit="1" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="note">
|
||||
<el-input v-model="formData.note" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-tabs v-model="activeFormTab" class="tabs-wrapper">
|
||||
<el-tab-pane label="土地基本信息" name="basic">
|
||||
<p class="form-title">基本信息</p>
|
||||
<el-form ref="basicFormRef" :model="formData" :disabled="isReadonly" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="地块名称" prop="landName">
|
||||
<el-input v-model="formData.landName" placeholder="请输入地块名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="土地类型" prop="landType">
|
||||
<el-tree-select
|
||||
v-model="formData.landType"
|
||||
:data="landTypeOptions"
|
||||
:props="treeProps"
|
||||
placeholder="选择土地类型"
|
||||
clearable
|
||||
check-strictly
|
||||
:render-after-expand="false"
|
||||
@change="handleLandTypeChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="具体位置" prop="address">
|
||||
<el-input v-model="formData.address" placeholder="请输入具体位置" />
|
||||
</el-form-item>
|
||||
<el-form-item label="土壤类型" prop="soilTypeId">
|
||||
<url-select
|
||||
v-model="formData.soilTypeId"
|
||||
placeholder="选择土壤类型"
|
||||
url="/land-resource/baseInfo/soilTypePage"
|
||||
:params="{ current: 1, size: 100 }"
|
||||
label-key="soilType"
|
||||
value-key="id"
|
||||
:clearable="true"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="土地照片" prop="landUrl">
|
||||
<FileUploader v-model="formData.landUrl" :limit="1" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="面积(亩)" prop="area">
|
||||
<el-input-number v-model="formData.area" :min="0" :precision="2" :step="0.1" controls-position="right" style="width: 100%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="" label-width="0" prop="gridId">
|
||||
<AreaCascader v-model:region-code="formData.gridAreaCode" v-model:grid-id="formData.gridId" label="" :split-rows="true" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="土地范围" prop="scope">
|
||||
<el-input v-model="formData.scope" placeholder="请输入土地范围" />
|
||||
<!-- <Attrs v-model:attrs="formData.scope" type="add" accept="image/*" /> -->
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="土地承包信息" name="property">
|
||||
<p class="form-title">承包信息</p>
|
||||
<el-form ref="propertyFormRef" :model="formData" :disabled="isReadonly" label-width="150px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="土地承包经营权人" prop="propertyName">
|
||||
<el-input v-model="formData.propertyName" placeholder="请输入产权人姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系方式" prop="propertyPhone">
|
||||
<el-input v-model="formData.propertyPhone" placeholder="请输入产权人联系方式" />
|
||||
</el-form-item>
|
||||
<el-form-item label="土地经营权证书编号" prop="landCode">
|
||||
<el-input v-model="formData.landCode" placeholder="请输入产权编号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="土地经营权证书" prop="propertyCertificateUrl">
|
||||
<FileUploader v-model="formData.propertyCertificateUrl" :limit="1" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="12"></el-col> -->
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="土地流转信息" name="use">
|
||||
<p class="form-title">流转信息</p>
|
||||
<el-form ref="useForm" :model="formData" label-width="120px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否土地流转" prop="landTransfer">
|
||||
<el-radio-group v-model="formData.landTransfer" :disabled="isReadonly">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.landTransfer === '1'" label="土地受让方" prop="landUseName">
|
||||
<el-input v-model="formData.landUseName" placeholder="请输入土地受让方" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.landTransfer === '1'" label="联系电话" prop="landUsePhone">
|
||||
<el-input v-model="formData.landUsePhone" placeholder="请输入土地受让方联系方式" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.landTransfer === '1'" label="流转合同" prop="landCertificateUrl">
|
||||
<FileUploader v-model="formData.landCertificateUrl" :limit="1" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button v-if="!isReadonly" type="primary" @click="handleSubmit()"> 保存 </el-button>
|
||||
</div>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<template v-if="!isReadonly">
|
||||
<el-button type="primary" @click="submitAll">修改</el-button>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// ---------------------------------------------------------------------
|
||||
// avue-crud 通用代码
|
||||
// ---------------------------------------------------------------------
|
||||
import { ref, reactive, watch, onMounted, computed, nextTick } from 'vue';
|
||||
import { CRUD_OPTIONS } from '@/config';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { mockData } from './landData';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import LandSearch from './components/LandSearch.vue';
|
||||
import TableComponent from '@/components/tableComponent.vue';
|
||||
|
||||
const UserStore = useUserStore();
|
||||
const user = UserStore.getUserInfo();
|
||||
console.log('admin 属性:', user.admin);
|
||||
const searchForm = ref({});
|
||||
const handleSearch = (searchData) => {
|
||||
// 使用搜索条件
|
||||
console.log(searchData);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
// 重置后的逻辑
|
||||
};
|
||||
|
||||
const activeTab = ref('1');
|
||||
const handleTabChange = ({ name }) => {
|
||||
// activeTab.value = name;
|
||||
console.log('切换tab', activeTab.value);
|
||||
searchForm.value.landStatus = Number(activeTab.value);
|
||||
getData();
|
||||
};
|
||||
|
||||
const loading = ref(false);
|
||||
const tableData = ref([]);
|
||||
const pageData = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const columns = ref([
|
||||
{ label: '地块编号', prop: 'id', width: 160 },
|
||||
{ label: '地块名称', prop: 'landName', width: 170 },
|
||||
{ label: '面积', prop: 'area', formatter: (row, column, cellValue) => `${Number(cellValue).toFixed(2)} 亩` },
|
||||
{ label: '土地分类', prop: 'landTypeName' },
|
||||
{ label: '土壤类型', prop: 'soilTypeName' },
|
||||
{ label: '所属行政区域', prop: 'fullRegionName', width: 160 },
|
||||
{ label: '所属网格', prop: 'gridName', width: 90 },
|
||||
{ label: '具体位置', prop: 'address', width: 160 },
|
||||
{ label: '土地承包经营人', prop: 'propertyName' },
|
||||
{ label: '联系电话', prop: 'propertyPhone' },
|
||||
{ label: '土地经营权证书编号', prop: 'landCode', width: 160 },
|
||||
{ label: '是否土地流转', prop: 'isTransfer' },
|
||||
{ label: '土地受让方', prop: 'transferName' },
|
||||
{ label: '受让方联系电话', prop: 'transferPhone', width: 100 },
|
||||
{ label: '流转合同', prop: 'transferContract' },
|
||||
{ label: '信息填报人', prop: 'fillName' },
|
||||
{ label: '信息填报单位', prop: 'fillGroup' },
|
||||
{ label: '信息填报时间', prop: 'fillTime' },
|
||||
// { label: '信息更新人', prop: 'updateName' },
|
||||
// { label: '信息更新单位', prop: 'updateGroup' },
|
||||
{ label: '信息更新时间', prop: 'updateTime' },
|
||||
]);
|
||||
|
||||
const handlePageChange = ({ page, pageSize }) => {
|
||||
pageData.value.currentPage = page;
|
||||
pageData.value.pageSize = pageSize;
|
||||
getData();
|
||||
};
|
||||
|
||||
const visible = ref(false);
|
||||
const isReadonly = ref(false);
|
||||
const dialogTitle = ref();
|
||||
const activeTab = ref('1');
|
||||
const activeFormTab = ref('basic');
|
||||
const formData = ref({
|
||||
gridName: '',
|
||||
gridAreaCode: '',
|
||||
@ -95,165 +214,9 @@ const resetForm = () => {
|
||||
formData.value = { ...initialFormData };
|
||||
};
|
||||
|
||||
const pageData = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const searchForm = ref({
|
||||
gridName: '',
|
||||
keyword: '',
|
||||
regionCode: '',
|
||||
id: '',
|
||||
status: -1,
|
||||
});
|
||||
const initialSearchForm = { ...searchForm.value };
|
||||
const resetSearch = () => {
|
||||
searchForm.value = { ...initialSearchForm };
|
||||
};
|
||||
// 过滤对象,只保留有值的属性
|
||||
const filterObject = (obj) => {
|
||||
const newObj = {};
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const value = obj[key];
|
||||
// 检查值是否有效,排除空字符串、null 和 undefined
|
||||
if (value !== '' && value !== null && value !== undefined) {
|
||||
newObj[key] = value;
|
||||
}
|
||||
});
|
||||
return newObj;
|
||||
};
|
||||
|
||||
const crudData = ref([]);
|
||||
const crudOptions = reactive({
|
||||
...CRUD_OPTIONS,
|
||||
addBtn: false,
|
||||
header: false,
|
||||
searchBtn: false,
|
||||
emptyBtn: false,
|
||||
refreshBtn: false,
|
||||
height: 'calc(100vh - 360px)',
|
||||
column: [
|
||||
{ label: '地块编号', prop: 'id', width: 160 },
|
||||
{ label: '地块名称', prop: 'landName', width: 170 },
|
||||
{ label: '面积', prop: 'area', formatter: (row, column, cellValue) => `${Number(cellValue).toFixed(2)} 亩` },
|
||||
{ label: '土地分类', prop: 'landTypeName' },
|
||||
{ label: '土壤类型', prop: 'soilTypeName' },
|
||||
{ label: '所属行政区域', prop: 'fullRegionName', width: 160 },
|
||||
{ label: '所属网格', prop: 'gridName', width: 90 },
|
||||
{ label: '具体位置', prop: 'address', width: 160 },
|
||||
{ label: '土地承包经营人', prop: 'propertyName' },
|
||||
{ label: '联系电话', prop: 'propertyPhone' },
|
||||
{ label: '土地经营权证书编号', prop: 'landCode', width: 160 },
|
||||
{ label: '是否土地流转', prop: 'isTransfer' },
|
||||
{ label: '土地受让方', prop: 'transferName' },
|
||||
{ label: '受让方联系电话', prop: 'transferPhone', width: 100 },
|
||||
{ label: '流转合同', prop: 'transferContract' },
|
||||
{ label: '信息填报人', prop: 'fillName' },
|
||||
{ label: '信息填报单位', prop: 'fillGroup' },
|
||||
{ label: '信息填报时间', prop: 'fillTime' },
|
||||
// { label: '信息更新人', prop: 'updateName' },
|
||||
// { label: '信息更新单位', prop: 'updateGroup' },
|
||||
{ label: '信息更新时间', prop: 'updateTime' },
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
name: '查看',
|
||||
icon: 'view',
|
||||
event: ({ row }) => handleView(row),
|
||||
},
|
||||
{
|
||||
name: '编辑',
|
||||
icon: 'edit',
|
||||
event: ({ row }) => handleEdit(row),
|
||||
},
|
||||
{
|
||||
type: 'danger',
|
||||
name: '删除',
|
||||
icon: 'delete',
|
||||
event: ({ row }) => handleDelete(row),
|
||||
},
|
||||
],
|
||||
});
|
||||
const handleTabChange = ({ name }) => {
|
||||
// activeTab.value = name;
|
||||
console.log('切换tab', activeTab.value);
|
||||
getData();
|
||||
};
|
||||
const handleRefresh = async () => {
|
||||
searchForm.value = { ...initialSearchForm };
|
||||
getData();
|
||||
};
|
||||
const handleCurrentChange = (val) => {
|
||||
pageData.value.currentPage = val;
|
||||
};
|
||||
const handleSizeChange = (val) => {
|
||||
pageData.value.pageSize = val;
|
||||
};
|
||||
const handleView = (row) => {
|
||||
isReadonly.value = true;
|
||||
formData.value = { ...row };
|
||||
dialogTitle.value = '查看网格';
|
||||
// visible.value = true;
|
||||
};
|
||||
const handleEdit = (row) => {
|
||||
isReadonly.value = false;
|
||||
formData.value = { ...row };
|
||||
dialogTitle.value = '编辑网格';
|
||||
// visible.value = true;
|
||||
};
|
||||
const handleDelete = async (row) => {
|
||||
console.log('删除', row);
|
||||
// try {
|
||||
// await ElMessageBox.confirm('确认删除该网格吗?', '提示', {
|
||||
// confirmButtonText: '确定',
|
||||
// cancelButtonText: '取消',
|
||||
// type: 'warning',
|
||||
// });
|
||||
|
||||
// const response = await deleteGrid(row.id);
|
||||
|
||||
// ElMessage.success('删除成功');
|
||||
// getData();
|
||||
// } catch (error) {
|
||||
// if (error === 'cancel') {
|
||||
// ElMessage.info('已取消删除');
|
||||
// } else {
|
||||
// ElMessage.error('删除失败');
|
||||
// console.error('删除异常:', error);
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
console.log('提交表单:', formData.value);
|
||||
try {
|
||||
if (dialogTitle.value === '新增网格') {
|
||||
await createGrid(formData.value);
|
||||
ElMessage.success('新增成功');
|
||||
resetForm();
|
||||
visible.value = false;
|
||||
getData();
|
||||
} else {
|
||||
await updateGrid(formData.value);
|
||||
ElMessage.success('更新成功');
|
||||
resetForm();
|
||||
visible.value = false;
|
||||
getData();
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error.message || '新增失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// 业务代码
|
||||
// ---------------------------------------------------------------------
|
||||
import { createGrid, updateGrid, deleteGrid, fetchGridList, getGridDetail, exportGrid } from '@/apis/landResourceManagement/gridManagement';
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
import { mockData } from './landData';
|
||||
import { createLand, deleteLand, editLand, fetchLandList, getLandById, approveLand } from '@/apis/landResourceManagement/landManagement';
|
||||
import request from '@/utils/axios';
|
||||
const getData = async () => {
|
||||
// const filteredParams = filterObject(searchForm.value);
|
||||
// const response = await fetchGridList(filteredParams);
|
||||
@ -261,69 +224,50 @@ const getData = async () => {
|
||||
loading.value = true;
|
||||
// 模拟接口延迟
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
crudData.value = mockData
|
||||
tableData.value = mockData
|
||||
.filter((item) => item.status === activeTab.value)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
landTypeName: '林地',
|
||||
}));
|
||||
pageData.value.total = crudData.value.length;
|
||||
pageData.value.total = tableData.value.length;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
console.log('handleAdd');
|
||||
resetForm();
|
||||
isReadonly.value = false;
|
||||
dialogTitle.value = '新增网格';
|
||||
visible.value = true;
|
||||
const getLandDetail = async (id) => {
|
||||
const response = await getLandById(id);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
const landTypeOptions = ref([]);
|
||||
const treeProps = ref({
|
||||
value: 'id',
|
||||
label: 'landType',
|
||||
children: 'children',
|
||||
// disabled: (data) => {
|
||||
// return data.children && data.children.length > 0;
|
||||
// },
|
||||
});
|
||||
const fetchLandTypeData = async () => {
|
||||
try {
|
||||
const response = await request.get('/land-resource/baseInfo/landTree', { params: { status: '1' } });
|
||||
if (response.code === 200) {
|
||||
landTypeOptions.value = response.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取土地类型数据失败', error);
|
||||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
getData();
|
||||
};
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
fetchLandTypeData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-page {
|
||||
padding-bottom: 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.search-bar {
|
||||
padding-left: 20px;
|
||||
}
|
||||
:deep(.el-dialog__body) {
|
||||
padding: 20px;
|
||||
height: calc(100vh - 300px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.form-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin: 30px 0;
|
||||
color: #333333;
|
||||
}
|
||||
.form-item {
|
||||
width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
.custom-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
|
||||
.el-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,329 @@
|
||||
<template>
|
||||
<div class="custom-page">
|
||||
<h1>林地</h1>
|
||||
<!-- 搜索 -->
|
||||
<el-form :inline="true" :model="searchForm" class="search-bar">
|
||||
<el-form-item label="关键词">
|
||||
<el-input v-model="searchForm.name" placeholder="请输入关键词" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item lable="">
|
||||
<AreaCascader v-model:region-code="searchForm.regionCode" v-model:grid-id="searchForm.id" :width="600" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch"> 搜索 </el-button>
|
||||
<el-button @click="resetSearch"> 重置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 四个固定 Tabs -->
|
||||
<el-tabs v-model="activeTab" @tab-click="handleTabChange">
|
||||
<!-- <el-tab-pane label="待提交" name="0" /> -->
|
||||
<el-tab-pane label="待审核" name="1" />
|
||||
<el-tab-pane label="已通过" name="2" />
|
||||
<el-tab-pane label="已驳回" name="0" />
|
||||
</el-tabs>
|
||||
<avue-crud
|
||||
ref="crudRef"
|
||||
v-model:page="pageData"
|
||||
:data="crudData"
|
||||
:option="crudOptions"
|
||||
:table-loading="loading"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
>
|
||||
<!-- <template #menu-left>
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd">新增网格</el-button>
|
||||
</template> -->
|
||||
<template #menu="scope">
|
||||
<custom-table-operate :actions="crudOptions.actions" :data="scope" />
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
|
||||
<el-form ref="form" :model="formData" :rules="rules" :disabled="isReadonly" label-width="100px" class="form-item">
|
||||
<p class="form-title">填写网格信息</p>
|
||||
<el-form-item label="网格名称" prop="gridName">
|
||||
<el-input v-model="formData.gridName" placeholder="请输入网格名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属行政区域" prop="gridAreaCode">
|
||||
<AreaSelect v-model="formData.gridAreaCode" :label="null" :emit-path="false" />
|
||||
</el-form-item>
|
||||
<el-form-item label="网格化地图" prop="scopeImg">
|
||||
<FileUploader v-model="formData.scopeImg" :limit="1" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="note">
|
||||
<el-input v-model="formData.note" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button v-if="!isReadonly" type="primary" @click="handleSubmit()"> 保存 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// ---------------------------------------------------------------------
|
||||
// avue-crud 通用代码
|
||||
// ---------------------------------------------------------------------
|
||||
import { ref, reactive, watch, onMounted, computed, nextTick } from 'vue';
|
||||
import { CRUD_OPTIONS } from '@/config';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { mockData } from './landData';
|
||||
|
||||
const UserStore = useUserStore();
|
||||
const user = UserStore.getUserInfo();
|
||||
console.log('admin 属性:', user.admin);
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const visible = ref(false);
|
||||
const isReadonly = ref(false);
|
||||
const dialogTitle = ref();
|
||||
const activeTab = ref('1');
|
||||
const formData = ref({
|
||||
gridName: '',
|
||||
gridAreaCode: '',
|
||||
scope: '',
|
||||
scopeImg: '',
|
||||
note: '',
|
||||
});
|
||||
const initialFormData = { ...formData.value };
|
||||
const resetForm = () => {
|
||||
formData.value = { ...initialFormData };
|
||||
};
|
||||
|
||||
const pageData = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const searchForm = ref({
|
||||
gridName: '',
|
||||
keyword: '',
|
||||
regionCode: '',
|
||||
id: '',
|
||||
status: -1,
|
||||
});
|
||||
const initialSearchForm = { ...searchForm.value };
|
||||
const resetSearch = () => {
|
||||
searchForm.value = { ...initialSearchForm };
|
||||
};
|
||||
// 过滤对象,只保留有值的属性
|
||||
const filterObject = (obj) => {
|
||||
const newObj = {};
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const value = obj[key];
|
||||
// 检查值是否有效,排除空字符串、null 和 undefined
|
||||
if (value !== '' && value !== null && value !== undefined) {
|
||||
newObj[key] = value;
|
||||
}
|
||||
});
|
||||
return newObj;
|
||||
};
|
||||
|
||||
const crudData = ref([]);
|
||||
const crudOptions = reactive({
|
||||
...CRUD_OPTIONS,
|
||||
addBtn: false,
|
||||
header: false,
|
||||
searchBtn: false,
|
||||
emptyBtn: false,
|
||||
refreshBtn: false,
|
||||
height: 'calc(100vh - 360px)',
|
||||
column: [
|
||||
{ label: '地块编号', prop: 'id', width: 160 },
|
||||
{ label: '地块名称', prop: 'landName', width: 170 },
|
||||
{ label: '面积', prop: 'area', formatter: (row, column, cellValue) => `${Number(cellValue).toFixed(2)} 亩` },
|
||||
{ label: '土地分类', prop: 'landTypeName' },
|
||||
{ label: '土壤类型', prop: 'soilTypeName' },
|
||||
{ label: '所属行政区域', prop: 'fullRegionName', width: 160 },
|
||||
{ label: '所属网格', prop: 'gridName', width: 90 },
|
||||
{ label: '具体位置', prop: 'address', width: 160 },
|
||||
{ label: '土地承包经营人', prop: 'propertyName' },
|
||||
{ label: '联系电话', prop: 'propertyPhone' },
|
||||
{ label: '土地经营权证书编号', prop: 'landCode', width: 160 },
|
||||
{ label: '是否土地流转', prop: 'isTransfer' },
|
||||
{ label: '土地受让方', prop: 'transferName' },
|
||||
{ label: '受让方联系电话', prop: 'transferPhone', width: 100 },
|
||||
{ label: '流转合同', prop: 'transferContract' },
|
||||
{ label: '信息填报人', prop: 'fillName' },
|
||||
{ label: '信息填报单位', prop: 'fillGroup' },
|
||||
{ label: '信息填报时间', prop: 'fillTime' },
|
||||
// { label: '信息更新人', prop: 'updateName' },
|
||||
// { label: '信息更新单位', prop: 'updateGroup' },
|
||||
{ label: '信息更新时间', prop: 'updateTime' },
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
name: '查看',
|
||||
icon: 'view',
|
||||
event: ({ row }) => handleView(row),
|
||||
},
|
||||
{
|
||||
name: '编辑',
|
||||
icon: 'edit',
|
||||
event: ({ row }) => handleEdit(row),
|
||||
},
|
||||
{
|
||||
type: 'danger',
|
||||
name: '删除',
|
||||
icon: 'delete',
|
||||
event: ({ row }) => handleDelete(row),
|
||||
},
|
||||
],
|
||||
});
|
||||
const handleTabChange = ({ name }) => {
|
||||
// activeTab.value = name;
|
||||
console.log('切换tab', activeTab.value);
|
||||
getData();
|
||||
};
|
||||
const handleRefresh = async () => {
|
||||
searchForm.value = { ...initialSearchForm };
|
||||
getData();
|
||||
};
|
||||
const handleCurrentChange = (val) => {
|
||||
pageData.value.currentPage = val;
|
||||
};
|
||||
const handleSizeChange = (val) => {
|
||||
pageData.value.pageSize = val;
|
||||
};
|
||||
const handleView = (row) => {
|
||||
isReadonly.value = true;
|
||||
formData.value = { ...row };
|
||||
dialogTitle.value = '查看网格';
|
||||
// visible.value = true;
|
||||
};
|
||||
const handleEdit = (row) => {
|
||||
isReadonly.value = false;
|
||||
formData.value = { ...row };
|
||||
dialogTitle.value = '编辑网格';
|
||||
// visible.value = true;
|
||||
};
|
||||
const handleDelete = async (row) => {
|
||||
console.log('删除', row);
|
||||
// try {
|
||||
// await ElMessageBox.confirm('确认删除该网格吗?', '提示', {
|
||||
// confirmButtonText: '确定',
|
||||
// cancelButtonText: '取消',
|
||||
// type: 'warning',
|
||||
// });
|
||||
|
||||
// const response = await deleteGrid(row.id);
|
||||
|
||||
// ElMessage.success('删除成功');
|
||||
// getData();
|
||||
// } catch (error) {
|
||||
// if (error === 'cancel') {
|
||||
// ElMessage.info('已取消删除');
|
||||
// } else {
|
||||
// ElMessage.error('删除失败');
|
||||
// console.error('删除异常:', error);
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
console.log('提交表单:', formData.value);
|
||||
try {
|
||||
if (dialogTitle.value === '新增网格') {
|
||||
await createGrid(formData.value);
|
||||
ElMessage.success('新增成功');
|
||||
resetForm();
|
||||
visible.value = false;
|
||||
getData();
|
||||
} else {
|
||||
await updateGrid(formData.value);
|
||||
ElMessage.success('更新成功');
|
||||
resetForm();
|
||||
visible.value = false;
|
||||
getData();
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error.message || '新增失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// 业务代码
|
||||
// ---------------------------------------------------------------------
|
||||
import { createGrid, updateGrid, deleteGrid, fetchGridList, getGridDetail, exportGrid } from '@/apis/landResourceManagement/gridManagement';
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
const getData = async () => {
|
||||
// const filteredParams = filterObject(searchForm.value);
|
||||
// const response = await fetchGridList(filteredParams);
|
||||
// crudData.value = Array.isArray(response.data.records) ? response.data.records : [];
|
||||
loading.value = true;
|
||||
// 模拟接口延迟
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
crudData.value = mockData
|
||||
.filter((item) => item.status === activeTab.value)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
landTypeName: '林地',
|
||||
}));
|
||||
pageData.value.total = crudData.value.length;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
console.log('handleAdd');
|
||||
resetForm();
|
||||
isReadonly.value = false;
|
||||
dialogTitle.value = '新增网格';
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
getData();
|
||||
};
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-page {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.search-bar {
|
||||
padding-left: 20px;
|
||||
}
|
||||
:deep(.el-dialog__body) {
|
||||
padding: 20px;
|
||||
height: calc(100vh - 300px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.form-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin: 30px 0;
|
||||
color: #333333;
|
||||
}
|
||||
.form-item {
|
||||
width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
.custom-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
|
||||
.el-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<LandCrud type="forest" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LandCrud from './components/LandCrud.vue';
|
||||
</script>
|
@ -31,7 +31,7 @@
|
||||
@size-change="handleSizeChange"
|
||||
>
|
||||
<template #menu="scope">
|
||||
<custom-table-operate :actions="crudOptions.actions" :data="scope" />
|
||||
<custom-table-operate :actions="crudOptions.actions" :show="false" :data="scope" />
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
|
||||
|
@ -1,47 +1,458 @@
|
||||
<template>
|
||||
<div class="mapDiv">
|
||||
<tdt-map ref="tdtMapRef" :center="state.center" :zoom="state.zoom" :style="{ height: mapHeight }">
|
||||
<!-- <tdt-tile-layer :layer-type="'img_w'"></tdt-tile-layer> -->
|
||||
<!-- <tdt-tile-layer :layer-type="'cia_w'" :z-index="2"></tdt-tile-layer> -->
|
||||
<tdt-tilelayer :url="state.img_w_url" :z-index="1"></tdt-tilelayer>
|
||||
<tdt-tilelayer :url="state.cia_w_url" :z-index="2"></tdt-tilelayer>
|
||||
</tdt-map>
|
||||
<div class="custom-page">
|
||||
<!-- 左侧面板 -->
|
||||
<div class="panel-left">
|
||||
<el-input v-model="searchTerm" placeholder="搜索地块名称" clearable style="margin-bottom: 15px"></el-input>
|
||||
|
||||
<el-table :data="filteredFields" highlight-current-row style="width: 100%" @row-click="selectField">
|
||||
<el-table-column prop="name" label="名称"></el-table-column>
|
||||
<el-table-column prop="area" label="面积(㎡)">
|
||||
<template #default="{ row }">
|
||||
{{ row.area.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="stats">总面积: {{ totalArea.toFixed(2) }} ㎡</div>
|
||||
|
||||
<div class="button-group">
|
||||
<el-button type="primary" @click="startDrawing">新增地块</el-button>
|
||||
<el-button type="danger" :disabled="!selectedField" @click="removeField">删除地块</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧地图区域 -->
|
||||
<div class="panel-right">
|
||||
<div ref="mapContainer" class="map"></div>
|
||||
|
||||
<div class="layer-switch">
|
||||
<el-radio-group v-model="layerType" size="small" @change="changeBaseLayer">
|
||||
<el-radio-button label="vector">矢量</el-radio-button>
|
||||
<el-radio-button label="image">影像</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="field-layer-switch">
|
||||
<el-switch v-model="showFieldLayer" active-text="显示地块" @change="toggleFieldLayer"></el-switch>
|
||||
</div>
|
||||
|
||||
<div class="zoom-scale">缩放级别: {{ mapZoom }}, 比例尺: 1:{{ scale.toLocaleString() }}</div>
|
||||
|
||||
<el-dialog v-model="drawDialogVisible" title="新增地块">
|
||||
<el-form>
|
||||
<el-form-item label="地块名称">
|
||||
<el-input v-model="newFieldName"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="地块面积">
|
||||
<el-input :value="newFieldArea.toFixed(2) + ' ㎡'" readonly></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="cancelDrawing">取消</el-button>
|
||||
<el-button type="primary" @click="saveNewField">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, onMounted, ref } from 'vue';
|
||||
import { TdtMap, TdtTileLayer } from 'vue-tianditu';
|
||||
import { ref, computed, onMounted, watch } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { map_config } from '@/config/map';
|
||||
import { loadTiandituApi } from '@/utils/mapLoader';
|
||||
|
||||
const key = map_config.tianditu.token;
|
||||
// 模拟接口服务(使用localStorage)
|
||||
const fieldService = {
|
||||
getFields() {
|
||||
const fields = localStorage.getItem('fields');
|
||||
return Promise.resolve(fields ? JSON.parse(fields) : []);
|
||||
},
|
||||
createField(field) {
|
||||
return this.getFields().then((fields) => {
|
||||
field.id = Date.now();
|
||||
fields.push(field);
|
||||
localStorage.setItem('fields', JSON.stringify(fields));
|
||||
return field;
|
||||
});
|
||||
},
|
||||
deleteField(id) {
|
||||
return this.getFields().then((fields) => {
|
||||
const newFields = fields.filter((f) => f.id !== id);
|
||||
localStorage.setItem('fields', JSON.stringify(newFields));
|
||||
return newFields;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
center: [100.088, 23.883],
|
||||
zoom: 14,
|
||||
img_w_url: `http://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${key}`,
|
||||
cia_w_url: `http://t0.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${key}`,
|
||||
// 添加其他地图层URL
|
||||
// ...
|
||||
const mapContainer = ref(null);
|
||||
const mapObj = ref(null);
|
||||
const T = ref(null); // 天地图API对象
|
||||
|
||||
// 响应式状态
|
||||
const fields = ref([]);
|
||||
const selectedField = ref(null);
|
||||
const searchTerm = ref('');
|
||||
const newFieldName = ref('');
|
||||
const drawDialogVisible = ref(false);
|
||||
const newFieldCoords = ref([]);
|
||||
const newFieldArea = ref(0);
|
||||
const layerType = ref('image'); // 默认影像地图
|
||||
const showFieldLayer = ref(true);
|
||||
const mapZoom = ref(12);
|
||||
const scale = ref(0);
|
||||
|
||||
// 天地图图层
|
||||
const baseLayers = {
|
||||
vector: null,
|
||||
vectorLabel: null,
|
||||
image: null,
|
||||
imageLabel: null,
|
||||
};
|
||||
|
||||
// 覆盖物相关
|
||||
const fieldPolygons = ref([]); // 存储所有地块多边形对象
|
||||
const polygonTool = ref(null);
|
||||
|
||||
// 计算属性
|
||||
const filteredFields = computed(() => {
|
||||
const term = searchTerm.value.toLowerCase();
|
||||
return term ? fields.value.filter((f) => f.name.toLowerCase().includes(term)) : fields.value;
|
||||
});
|
||||
|
||||
// 确保地图有明确的高度
|
||||
const mapHeight = ref('100vh');
|
||||
const totalArea = computed(() => {
|
||||
return fields.value.reduce((sum, field) => sum + field.area, 0);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
console.log('地图组件已挂载');
|
||||
// 检查天地图API是否加载成功
|
||||
if (window.T) {
|
||||
console.log('天地图API已加载:', window.T);
|
||||
} else {
|
||||
console.error('天地图API未加载');
|
||||
}
|
||||
loadFields();
|
||||
loadTiandituApi(map_config.tianditu).then((tianditu) => {
|
||||
T.value = tianditu;
|
||||
initMap();
|
||||
});
|
||||
});
|
||||
|
||||
// 初始化地图
|
||||
const initMap = () => {
|
||||
if (!T.value) return;
|
||||
|
||||
// 创建地图实例
|
||||
const center = new T.value.LngLat(...map_config.tianditu.center);
|
||||
mapObj.value = new T.value.Map(mapContainer.value, {
|
||||
center: center,
|
||||
zoom: map_config.tianditu.zoom,
|
||||
minZoom: 3,
|
||||
maxZoom: 18,
|
||||
});
|
||||
|
||||
// 初始化图层
|
||||
initLayers();
|
||||
|
||||
// 添加控件
|
||||
mapObj.value.addControl(new T.value.Control.Zoom());
|
||||
mapObj.value.addControl(new T.value.Control.Scale());
|
||||
|
||||
// 事件监听
|
||||
mapObj.value.addEventListener('zoomend', updateScale);
|
||||
mapObj.value.addEventListener('moveend', updateScale);
|
||||
|
||||
// 初始比例尺
|
||||
updateScale();
|
||||
};
|
||||
|
||||
// 初始化图层
|
||||
const initLayers = () => {
|
||||
if (!T.value || !mapObj.value) return;
|
||||
|
||||
// 创建矢量地图图层
|
||||
const vecLayer = new T.value.TileLayer(
|
||||
`https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${map_config.tianditu.token}`,
|
||||
{ minZoom: 3, maxZoom: 18, zIndex: 1 }
|
||||
);
|
||||
|
||||
const cvaLayer = new T.value.TileLayer(
|
||||
`https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${map_config.tianditu.token}`,
|
||||
{ minZoom: 3, maxZoom: 18, zIndex: 2 }
|
||||
);
|
||||
|
||||
// 创建影像地图图层
|
||||
const imgLayer = new T.value.TileLayer(
|
||||
`https://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${map_config.tianditu.token}`,
|
||||
{ minZoom: 3, maxZoom: 18, zIndex: 1 }
|
||||
);
|
||||
|
||||
const ciaLayer = new T.value.TileLayer(
|
||||
`https://t0.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${map_config.tianditu.token}`,
|
||||
{ minZoom: 3, maxZoom: 18, zIndex: 2 }
|
||||
);
|
||||
|
||||
// 存储图层引用
|
||||
baseLayers.vector = vecLayer;
|
||||
baseLayers.vectorLabel = cvaLayer;
|
||||
baseLayers.image = imgLayer;
|
||||
baseLayers.imageLabel = ciaLayer;
|
||||
|
||||
// 默认加载影像地图
|
||||
mapObj.value.addLayer(imgLayer);
|
||||
mapObj.value.addLayer(ciaLayer);
|
||||
};
|
||||
|
||||
// 更新比例尺
|
||||
const updateScale = () => {
|
||||
if (mapObj.value) {
|
||||
mapZoom.value = mapObj.value.getZoom();
|
||||
scale.value = Math.round(mapObj.value.getScale());
|
||||
}
|
||||
};
|
||||
|
||||
// 切换底图函数
|
||||
const changeBaseLayer = () => {
|
||||
if (!mapObj.value) return;
|
||||
|
||||
// 移除所有基础图层
|
||||
[baseLayers.vector, baseLayers.vectorLabel, baseLayers.image, baseLayers.imageLabel].forEach((layer) => {
|
||||
mapObj.value.removeLayer(layer);
|
||||
});
|
||||
|
||||
// 添加新图层组合
|
||||
if (layerType.value === 'vector') {
|
||||
mapObj.value.addLayer(baseLayers.vector);
|
||||
mapObj.value.addLayer(baseLayers.vectorLabel);
|
||||
} else {
|
||||
mapObj.value.addLayer(baseLayers.image);
|
||||
mapObj.value.addLayer(baseLayers.imageLabel);
|
||||
}
|
||||
};
|
||||
// 加载地块数据
|
||||
const loadFields = () => {
|
||||
fieldService.getFields().then((data) => {
|
||||
fields.value = data;
|
||||
refreshOverlays();
|
||||
});
|
||||
};
|
||||
|
||||
// 刷新覆盖物
|
||||
const refreshOverlays = () => {
|
||||
if (!mapObj.value || !T.value) return;
|
||||
|
||||
// 清除所有地块多边形
|
||||
clearFieldOverlays();
|
||||
|
||||
if (!showFieldLayer.value) return;
|
||||
|
||||
// 绘制所有地块
|
||||
fields.value.forEach((field) => {
|
||||
const polygon = createPolygon(field, selectedField.value?.id === field.id);
|
||||
mapObj.value.addOverLay(polygon);
|
||||
fieldPolygons.value.push(polygon);
|
||||
});
|
||||
};
|
||||
// 清除所有地块覆盖物
|
||||
const clearFieldOverlays = () => {
|
||||
fieldPolygons.value.forEach((polygon) => {
|
||||
mapObj.value.removeOverLay(polygon);
|
||||
});
|
||||
fieldPolygons.value = [];
|
||||
};
|
||||
// 添加单个覆盖物
|
||||
const addFieldToMap = (field) => {
|
||||
const polygon = createPolygon(field);
|
||||
mapObj.value.addOverLay(polygon); // 正确方法
|
||||
return polygon;
|
||||
};
|
||||
|
||||
// 移除单个覆盖物
|
||||
const removeFieldFromMap = (polygon) => {
|
||||
mapObj.value.removeOverLay(polygon); // 正确方法
|
||||
};
|
||||
|
||||
// 清除所有覆盖物
|
||||
const clearAllOverlays = () => {
|
||||
mapObj.value.clearOverLays(); // 注意拼写
|
||||
};
|
||||
|
||||
// 创建多边形
|
||||
const createPolygon = (field, isSelected = false) => {
|
||||
const coords = field.geometry.coordinates[0].map((c) => new T.value.LngLat(c[0], c[1]));
|
||||
return new T.value.Polygon(coords, {
|
||||
color: isSelected ? '#ff0000' : '#1890ff',
|
||||
weight: isSelected ? 3 : 2,
|
||||
opacity: 0.8,
|
||||
fillColor: isSelected ? '#ffa39e' : '#e6f7ff',
|
||||
fillOpacity: 0.5,
|
||||
});
|
||||
};
|
||||
|
||||
// 选择地块
|
||||
const selectField = (field) => {
|
||||
selectedField.value = field;
|
||||
refreshOverlays();
|
||||
|
||||
if (mapObj.value && T.value) {
|
||||
const coords = field.geometry.coordinates[0];
|
||||
const center = coords
|
||||
.reduce(
|
||||
(acc, coord) => {
|
||||
return [acc[0] + coord[0], acc[1] + coord[1]];
|
||||
},
|
||||
[0, 0]
|
||||
)
|
||||
.map((v) => v / coords.length);
|
||||
|
||||
mapObj.value.panTo(new T.value.LngLat(center[0], center[1]));
|
||||
}
|
||||
};
|
||||
|
||||
// 开始绘制
|
||||
const startDrawing = () => {
|
||||
if (!mapObj.value || !T.value) return;
|
||||
|
||||
// 创建绘制工具
|
||||
polygonTool.value = new T.value.PolygonTool(mapObj.value, {
|
||||
showLabel: true,
|
||||
color: '#fa541c',
|
||||
weight: 3,
|
||||
opacity: 0.6,
|
||||
fillColor: '#ffbb96',
|
||||
fillOpacity: 0.4,
|
||||
});
|
||||
|
||||
polygonTool.value.open();
|
||||
|
||||
polygonTool.value.addEventListener('draw', (e) => {
|
||||
newFieldCoords.value = e.currentLnglats.map((lnglat) => [lnglat.lng, lnglat.lat]);
|
||||
newFieldArea.value = e.currentArea;
|
||||
drawDialogVisible.value = true;
|
||||
polygonTool.value.close();
|
||||
});
|
||||
};
|
||||
|
||||
// 保存新地块
|
||||
const saveNewField = () => {
|
||||
if (!newFieldName.value.trim()) {
|
||||
ElMessage.error('请输入地块名称');
|
||||
return;
|
||||
}
|
||||
|
||||
const newField = {
|
||||
id: Date.now(),
|
||||
name: newFieldName.value,
|
||||
area: newFieldArea.value,
|
||||
geometry: {
|
||||
type: 'Polygon',
|
||||
coordinates: [newFieldCoords.value],
|
||||
},
|
||||
};
|
||||
|
||||
fieldService.createField(newField).then(() => {
|
||||
ElMessage.success('地块创建成功');
|
||||
drawDialogVisible.value = false;
|
||||
newFieldName.value = '';
|
||||
loadFields();
|
||||
});
|
||||
};
|
||||
|
||||
// 取消绘制
|
||||
const cancelDrawing = () => {
|
||||
drawDialogVisible.value = false;
|
||||
refreshOverlays();
|
||||
};
|
||||
|
||||
// 删除地块
|
||||
const removeField = () => {
|
||||
if (!selectedField.value) return;
|
||||
|
||||
fieldService.deleteField(selectedField.value.id).then(() => {
|
||||
ElMessage.success('地块已删除');
|
||||
selectedField.value = null;
|
||||
loadFields();
|
||||
});
|
||||
};
|
||||
|
||||
// 切换地块图层显示
|
||||
const toggleFieldLayer = () => {
|
||||
refreshOverlays();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mapDiv {
|
||||
.custom-page {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
}
|
||||
.panel-left {
|
||||
width: 300px;
|
||||
height: calc(100vh - 130px);
|
||||
border-right: 1px solid #eee;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f8fafc;
|
||||
}
|
||||
.panel-right {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 100vh; /* 确保容器有明确高度 */
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.layer-switch {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
background: white;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.field-layer-switch {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 15px;
|
||||
background: white;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.zoom-scale {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.stats {
|
||||
padding: 12px;
|
||||
margin: 10px 0;
|
||||
background: #edf2ff;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
color: #364fc7;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.button-group > * {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,401 +0,0 @@
|
||||
<template>
|
||||
<div class="field-management">
|
||||
<!-- 左侧面板 -->
|
||||
<div class="panel left">
|
||||
<el-input v-model="searchTerm" placeholder="搜索地块名称" clearable style="margin-bottom: 15px"></el-input>
|
||||
|
||||
<el-table :data="filteredFields" highlight-current-row style="width: 100%" height="calc(100vh - 300px)" @row-click="selectField">
|
||||
<el-table-column prop="name" label="名称"></el-table-column>
|
||||
<el-table-column prop="area" label="面积(㎡)">
|
||||
<template #default="{ row }">
|
||||
{{ row.area.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="stats">总面积: {{ totalArea.toFixed(2) }} ㎡</div>
|
||||
|
||||
<div class="button-group">
|
||||
<el-button type="primary" @click="startDrawing">新增地块</el-button>
|
||||
<el-button type="danger" :disabled="!selectedField" @click="removeField">删除地块</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧地图区域 -->
|
||||
<div class="panel right">
|
||||
<tdt-map class="map" :center="mapCenter" :zoom="mapZoom" @init="onMapInit"> </tdt-map>
|
||||
|
||||
<div class="layer-switch">
|
||||
<el-radio-group v-model="layerType" size="small" @change="changeBaseLayer">
|
||||
<el-radio-button label="vector">矢量</el-radio-button>
|
||||
<el-radio-button label="image">影像</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="field-layer-switch">
|
||||
<el-switch v-model="showFieldLayer" active-text="显示地块"></el-switch>
|
||||
</div>
|
||||
|
||||
<div class="zoom-scale">缩放级别: {{ mapZoom }}, 比例尺: 1:{{ scale.toLocaleString() }}</div>
|
||||
|
||||
<el-dialog v-model="drawDialogVisible" title="新增地块">
|
||||
<el-form>
|
||||
<el-form-item label="地块名称">
|
||||
<el-input v-model="newFieldName"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="地块面积">
|
||||
<el-input :value="newFieldArea.toFixed(2) + ' ㎡'" readonly></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="cancelDrawing">取消</el-button>
|
||||
<el-button type="primary" @click="saveNewField">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch } from 'vue';
|
||||
import { TdtMap } from 'vue-tianditu';
|
||||
import { ElMessage } from 'element-plus';
|
||||
// import { map_config } from '../../config/map';
|
||||
|
||||
// 模拟接口服务(使用localStorage)
|
||||
const fieldService = {
|
||||
getFields() {
|
||||
const fields = localStorage.getItem('fields');
|
||||
return Promise.resolve(fields ? JSON.parse(fields) : []);
|
||||
},
|
||||
createField(field) {
|
||||
return this.getFields().then((fields) => {
|
||||
field.id = Date.now();
|
||||
fields.push(field);
|
||||
localStorage.setItem('fields', JSON.stringify(fields));
|
||||
return field;
|
||||
});
|
||||
},
|
||||
deleteField(id) {
|
||||
return this.getFields().then((fields) => {
|
||||
const newFields = fields.filter((f) => f.id !== id);
|
||||
localStorage.setItem('fields', JSON.stringify(newFields));
|
||||
return newFields;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// 响应式状态
|
||||
const fields = ref([]);
|
||||
const selectedField = ref(null);
|
||||
const searchTerm = ref('');
|
||||
const newFieldName = ref('');
|
||||
const drawDialogVisible = ref(false);
|
||||
const newFieldCoords = ref([]);
|
||||
const newFieldArea = ref(0);
|
||||
const layerType = ref('vector');
|
||||
const showFieldLayer = ref(true);
|
||||
const map = ref(null);
|
||||
const mapCenter = ref([100.088, 23.883]);
|
||||
const mapZoom = ref(12);
|
||||
const scale = ref(0);
|
||||
|
||||
// 天地图图层
|
||||
const baseLayers = {
|
||||
vector: null,
|
||||
vectorLabel: null,
|
||||
image: null,
|
||||
imageLabel: null,
|
||||
};
|
||||
|
||||
// 覆盖物图层
|
||||
const overlayLayer = ref(null);
|
||||
|
||||
// 计算属性
|
||||
const filteredFields = computed(() => {
|
||||
const term = searchTerm.value.toLowerCase();
|
||||
return term ? fields.value.filter((f) => f.name.toLowerCase().includes(term)) : fields.value;
|
||||
});
|
||||
|
||||
const totalArea = computed(() => {
|
||||
return fields.value.reduce((sum, field) => sum + field.area, 0);
|
||||
});
|
||||
|
||||
// 地图初始化
|
||||
const onMapInit = (mapInstance) => {
|
||||
map.value = mapInstance;
|
||||
|
||||
// 创建图层
|
||||
baseLayers.vector = new T.TileLayer('vec');
|
||||
baseLayers.vectorLabel = new T.TileLayer('cva');
|
||||
baseLayers.image = new T.TileLayer('img');
|
||||
baseLayers.imageLabel = new T.TileLayer('cia');
|
||||
|
||||
// 添加默认图层
|
||||
map.value.addLayer(baseLayers.image);
|
||||
map.value.addLayer(baseLayers.imageLabel);
|
||||
|
||||
// 创建覆盖物图层
|
||||
overlayLayer.value = new T.Map.OverlayLayer();
|
||||
map.value.addLayer(overlayLayer.value);
|
||||
|
||||
// 添加控件
|
||||
map.value.addControl(new T.Control.Zoom());
|
||||
map.value.addControl(new T.Control.Scale());
|
||||
|
||||
// 事件监听
|
||||
map.value.addEventListener('zoomend', updateScale);
|
||||
map.value.addEventListener('moveend', updateScale);
|
||||
|
||||
// 初始加载数据
|
||||
loadFields();
|
||||
};
|
||||
|
||||
// 更新比例尺
|
||||
const updateScale = () => {
|
||||
if (map.value) {
|
||||
mapZoom.value = map.value.getZoom();
|
||||
scale.value = Math.round(map.value.getScale());
|
||||
}
|
||||
};
|
||||
|
||||
// 切换底图
|
||||
const changeBaseLayer = () => {
|
||||
if (!map.value) return;
|
||||
|
||||
// 移除当前底图
|
||||
map.value.removeLayer(baseLayers.vector);
|
||||
map.value.removeLayer(baseLayers.vectorLabel);
|
||||
map.value.removeLayer(baseLayers.image);
|
||||
map.value.removeLayer(baseLayers.imageLabel);
|
||||
|
||||
// 添加新底图
|
||||
if (layerType.value === 'vector') {
|
||||
map.value.addLayer(baseLayers.vector);
|
||||
map.value.addLayer(baseLayers.vectorLabel);
|
||||
} else {
|
||||
map.value.addLayer(baseLayers.image);
|
||||
map.value.addLayer(baseLayers.imageLabel);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载地块数据
|
||||
const loadFields = () => {
|
||||
fieldService.getFields().then((data) => {
|
||||
fields.value = data;
|
||||
refreshOverlays();
|
||||
});
|
||||
};
|
||||
|
||||
// 刷新覆盖物
|
||||
const refreshOverlays = () => {
|
||||
if (!map.value || !overlayLayer.value) return;
|
||||
|
||||
// 清除所有覆盖物
|
||||
overlayLayer.value.clearOverLays();
|
||||
|
||||
// 如果不显示地块图层则跳过绘制
|
||||
if (!showFieldLayer.value) return;
|
||||
|
||||
// 绘制所有地块
|
||||
fields.value.forEach((field) => {
|
||||
const isSelected = selectedField.value?.id === field.id;
|
||||
const polygon = createPolygon(field, isSelected);
|
||||
overlayLayer.value.addOverLay(polygon);
|
||||
});
|
||||
};
|
||||
|
||||
// 创建多边形
|
||||
const createPolygon = (field, isSelected = false) => {
|
||||
const coords = field.geometry.coordinates[0].map((c) => new T.LngLat(c[0], c[1]));
|
||||
return new T.Polygon(coords, {
|
||||
color: isSelected ? '#ff0000' : '#1890ff',
|
||||
weight: isSelected ? 3 : 2,
|
||||
opacity: 0.8,
|
||||
fillColor: isSelected ? '#ffa39e' : '#e6f7ff',
|
||||
fillOpacity: 0.5,
|
||||
});
|
||||
};
|
||||
|
||||
// 选择地块
|
||||
const selectField = (field) => {
|
||||
selectedField.value = field;
|
||||
refreshOverlays();
|
||||
|
||||
// 居中显示选中地块
|
||||
if (map.value) {
|
||||
const coords = field.geometry.coordinates[0];
|
||||
const center = coords.reduce(
|
||||
(acc, coord) => {
|
||||
acc[0] += coord[0];
|
||||
acc[1] += coord[1];
|
||||
return acc;
|
||||
},
|
||||
[0, 0]
|
||||
);
|
||||
|
||||
center[0] /= coords.length;
|
||||
center[1] /= coords.length;
|
||||
|
||||
map.value.panTo(new T.LngLat(center[0], center[1]));
|
||||
}
|
||||
};
|
||||
|
||||
// 开始绘制
|
||||
const startDrawing = () => {
|
||||
if (!map.value) return;
|
||||
|
||||
// 清除当前覆盖物
|
||||
refreshOverlays();
|
||||
|
||||
// 创建绘制工具
|
||||
const polygonTool = new T.PolygonTool(map.value, {
|
||||
showLabel: true,
|
||||
color: '#fa541c',
|
||||
weight: 3,
|
||||
opacity: 0.6,
|
||||
fillColor: '#ffbb96',
|
||||
fillOpacity: 0.4,
|
||||
});
|
||||
|
||||
polygonTool.open();
|
||||
|
||||
// 绘制完成事件
|
||||
polygonTool.addEventListener('draw', (e) => {
|
||||
newFieldCoords.value = e.currentLnglats.map((lnglat) => [lnglat.lng, lnglat.lat]);
|
||||
newFieldArea.value = e.currentArea;
|
||||
drawDialogVisible.value = true;
|
||||
|
||||
// 清除临时图形
|
||||
polygonTool.close();
|
||||
});
|
||||
};
|
||||
|
||||
// 保存新地块
|
||||
const saveNewField = () => {
|
||||
if (!newFieldName.value.trim()) {
|
||||
ElMessage.error('请输入地块名称');
|
||||
return;
|
||||
}
|
||||
|
||||
const newField = {
|
||||
id: Date.now(),
|
||||
name: newFieldName.value,
|
||||
area: newFieldArea.value,
|
||||
geometry: {
|
||||
type: 'Polygon',
|
||||
coordinates: [newFieldCoords.value],
|
||||
},
|
||||
};
|
||||
|
||||
fieldService.createField(newField).then(() => {
|
||||
ElMessage.success('地块创建成功');
|
||||
drawDialogVisible.value = false;
|
||||
newFieldName.value = '';
|
||||
loadFields();
|
||||
});
|
||||
};
|
||||
|
||||
// 取消绘制
|
||||
const cancelDrawing = () => {
|
||||
drawDialogVisible.value = false;
|
||||
refreshOverlays();
|
||||
};
|
||||
|
||||
// 删除地块
|
||||
const removeField = () => {
|
||||
if (!selectedField.value) return;
|
||||
|
||||
fieldService.deleteField(selectedField.value.id).then(() => {
|
||||
ElMessage.success('地块已删除');
|
||||
selectedField.value = null;
|
||||
loadFields();
|
||||
});
|
||||
};
|
||||
|
||||
// 监听地块图层显示状态
|
||||
watch(showFieldLayer, refreshOverlays);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.field-management {
|
||||
display: flex;
|
||||
height: calc(100vh - 160px);
|
||||
}
|
||||
|
||||
.panel.left {
|
||||
width: 320px;
|
||||
padding: 15px;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.panel.right {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.layer-switch {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
background: white;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.field-layer-switch {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 15px;
|
||||
background: white;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.zoom-scale {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.stats {
|
||||
padding: 12px;
|
||||
margin: 10px 0;
|
||||
background: #edf2ff;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
color: #364fc7;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.button-group > * {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
@ -1,24 +1,31 @@
|
||||
<template>
|
||||
<div class="land-search">
|
||||
<el-form :model="search" inline label-width="100px">
|
||||
<el-form-item label="地块名称">
|
||||
<el-input v-model="search.name" placeholder="请输入地块名称" clearable />
|
||||
</el-form-item>
|
||||
<div class="search-row">
|
||||
<div class="search-item">
|
||||
<span class="search-label">关键词搜索:</span>
|
||||
<el-input v-model="localSearch.keyWord" placeholder="请输入关键词" clearable class="search-input" />
|
||||
</div>
|
||||
|
||||
<el-form-item label="所属区域">
|
||||
<AreaCascader v-model:region-code="search.regionCode" v-model:grid-id="search.id" :width="300" />
|
||||
</el-form-item>
|
||||
<div class="search-item">
|
||||
<span class="search-label">地块名称:</span>
|
||||
<el-input v-model="localSearch.name" placeholder="请输入地块名称" clearable class="search-input" />
|
||||
</div>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSearch">搜索</el-button>
|
||||
<el-button @click="onReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="search-item">
|
||||
<span class="search-label">所属区域-网格:</span>
|
||||
<AreaCascader v-model:region-code="localSearch.regionCode" v-model:grid-id="localSearch.id" :width="500" :label="null" />
|
||||
</div>
|
||||
|
||||
<div class="search-item">
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, watch, toRefs } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
search: {
|
||||
@ -27,33 +34,46 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:search', 'search', 'reset']);
|
||||
const emit = defineEmits(['on-search']);
|
||||
|
||||
const search = reactive({ ...props.search });
|
||||
const localSearch = ref({ ...props.search });
|
||||
|
||||
watch(
|
||||
search,
|
||||
(val) => {
|
||||
emit('update:search', { ...val });
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const onSearch = () => {
|
||||
emit('search');
|
||||
const handleSearch = () => {
|
||||
emit('on-search', { ...localSearch.value });
|
||||
};
|
||||
|
||||
const onReset = () => {
|
||||
Object.keys(search).forEach((key) => {
|
||||
search[key] = '';
|
||||
});
|
||||
emit('update:search', { ...search });
|
||||
emit('reset');
|
||||
const handleReset = () => {
|
||||
localSearch.value = {};
|
||||
handleSearch();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.land-search {
|
||||
margin-bottom: 16px;
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.search-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 24px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-label {
|
||||
font-size: 14px;
|
||||
margin-right: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user