添加小区 功能
This commit is contained in:
parent
fd51a431bb
commit
8930e13f3f
@ -6,6 +6,11 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>DM Admin</title>
|
||||
<link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" />
|
||||
<script type="text/javascript">
|
||||
window._AMapSecurityConfig = {
|
||||
securityJsCode: '93527b49270ba2142f47f0407da7c0d6'
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@ -17,3 +17,12 @@ export function getBuildingList(params) {
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 添加新小区
|
||||
export function createCommunity(data) {
|
||||
return request({
|
||||
url: '/api/community/',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
@ -16,7 +16,9 @@ import {
|
||||
Divider,
|
||||
Tag,
|
||||
Modal,
|
||||
Select
|
||||
Select,
|
||||
InputNumber,
|
||||
AutoComplete
|
||||
} from 'ant-design-vue'
|
||||
import 'ant-design-vue/dist/antd.css'
|
||||
|
||||
@ -39,5 +41,7 @@ app.use(Divider)
|
||||
app.use(Tag)
|
||||
app.use(Modal)
|
||||
app.use(Select)
|
||||
app.use(InputNumber)
|
||||
app.use(AutoComplete)
|
||||
|
||||
app.mount('#app')
|
||||
@ -1,20 +1,37 @@
|
||||
export function initAMap() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (window.AMap) {
|
||||
// 如果已经加载过,直接加载插件
|
||||
window.AMap.plugin(['AMap.PlaceSearch', 'AMap.AutoComplete'], () => {
|
||||
resolve(window.AMap);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载高德地图脚本
|
||||
const script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.async = true;
|
||||
script.src = `https://webapi.amap.com/maps?v=2.0&key=fd47f3d4f54b675693c7d59dcd2a6c5f`;
|
||||
script.src = `https://webapi.amap.com/maps?v=2.0&key=fd47f3d4f54b675693c7d59dcd2a6c5f&plugin=AMap.PlaceSearch,AMap.AutoComplete&callback=initAMapCallback`;
|
||||
|
||||
script.onerror = reject;
|
||||
script.onload = () => {
|
||||
// 创建回调函数
|
||||
window.initAMapCallback = () => {
|
||||
resolve(window.AMap);
|
||||
};
|
||||
|
||||
script.onerror = () => {
|
||||
reject(new Error('加载地图失败'));
|
||||
};
|
||||
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
// 添加地图工具函数
|
||||
export function createMap(container, options = {}) {
|
||||
return new window.AMap.Map(container, {
|
||||
zoom: 13,
|
||||
viewMode: '2D',
|
||||
...options
|
||||
});
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
<div class="community-list">
|
||||
<div class="table-header">
|
||||
<h1>小区列表</h1>
|
||||
<a-button type="primary" @click="showAddModal">添加小区</a-button>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
@ -36,14 +37,93 @@
|
||||
>
|
||||
<div id="map-container" style="height: 500px;"></div>
|
||||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="addModalVisible"
|
||||
title="添加小区"
|
||||
@ok="handleAdd"
|
||||
@cancel="handleCancel"
|
||||
:confirmLoading="confirmLoading"
|
||||
width="680px"
|
||||
>
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" :loading="confirmLoading" @click="handleAdd">
|
||||
保存
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formState"
|
||||
:rules="rules"
|
||||
:label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
>
|
||||
<a-form-item label="地址搜索">
|
||||
<a-auto-complete
|
||||
v-model:value="searchAddress"
|
||||
:options="searchOptions"
|
||||
placeholder="输入地址搜索"
|
||||
@change="handleSearch"
|
||||
@select="handleSelect"
|
||||
:loading="searchLoading"
|
||||
allow-clear
|
||||
>
|
||||
<template #option="{ value: val, label }">
|
||||
<div>{{ label }}</div>
|
||||
</template>
|
||||
</a-auto-complete>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label=" " :colon="false">
|
||||
<div class="map-container">
|
||||
<div id="add-map-container" style="height: 240px;"></div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="小区名称" name="name" required>
|
||||
<a-input v-model:value="formState.name" placeholder="请输入小区名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="详细地址" name="address" required>
|
||||
<a-input v-model:value="formState.address" placeholder="请输入详细地址" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="经纬度" required>
|
||||
<a-input-group compact>
|
||||
<a-input-number
|
||||
v-model:value="formState.longitude"
|
||||
:min="-180"
|
||||
:max="180"
|
||||
:controls="false"
|
||||
disabled
|
||||
style="width: 50%"
|
||||
placeholder="经度"
|
||||
/>
|
||||
<a-input-number
|
||||
v-model:value="formState.latitude"
|
||||
:min="-90"
|
||||
:max="90"
|
||||
:controls="false"
|
||||
disabled
|
||||
style="width: 50%"
|
||||
placeholder="纬度"
|
||||
/>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted, nextTick } from 'vue'
|
||||
import { message, Tag } from 'ant-design-vue'
|
||||
import { getCommunityList } from '@/api/community'
|
||||
import { initAMap } from '@/utils/amap'
|
||||
import { getCommunityList, createCommunity } from '@/api/community'
|
||||
import { initAMap, createMap } from '@/utils/amap'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
export default defineComponent({
|
||||
@ -200,6 +280,203 @@ export default defineComponent({
|
||||
mapVisible.value = false
|
||||
}
|
||||
|
||||
// 添加小区相关的响应式变量
|
||||
const addModalVisible = ref(false)
|
||||
const confirmLoading = ref(false)
|
||||
const searchLoading = ref(false)
|
||||
const searchAddress = ref('')
|
||||
const searchOptions = ref([])
|
||||
const addMap = ref(null)
|
||||
const addMarker = ref(null)
|
||||
const formRef = ref(null)
|
||||
|
||||
const formState = ref({
|
||||
name: '',
|
||||
address: '',
|
||||
longitude: null,
|
||||
latitude: null
|
||||
})
|
||||
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入小区名称' }],
|
||||
address: [{ required: true, message: '请输入详细地址' }],
|
||||
longitude: [{ required: true, message: '请选择位置获取经度' }],
|
||||
latitude: [{ required: true, message: '请选择位置获取纬度' }],
|
||||
}
|
||||
|
||||
// 显示添加模态框
|
||||
const showAddModal = async () => {
|
||||
addModalVisible.value = true
|
||||
await nextTick()
|
||||
initAddMap()
|
||||
}
|
||||
|
||||
// 更新标记位置和表单信息
|
||||
const updateMarkerPosition = (lng, lat, name = '') => {
|
||||
if (!addMap.value) return
|
||||
|
||||
formState.value.longitude = lng
|
||||
formState.value.latitude = lat
|
||||
|
||||
// 如果提供了名称,更新小区名称
|
||||
if (name && !formState.value.name) {
|
||||
formState.value.name = name
|
||||
}
|
||||
|
||||
if (addMarker.value) {
|
||||
addMarker.value.setPosition([lng, lat])
|
||||
} else {
|
||||
const AMap = window.AMap
|
||||
addMarker.value = new AMap.Marker({
|
||||
position: [lng, lat],
|
||||
map: addMap.value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 地图点击事件处理
|
||||
const initAddMap = async () => {
|
||||
try {
|
||||
await initAMap();
|
||||
|
||||
if (!addMap.value) {
|
||||
addMap.value = createMap('add-map-container', {
|
||||
center: [116.397428, 39.90923]
|
||||
});
|
||||
|
||||
// 添加点击事件
|
||||
addMap.value.on('click', (e) => {
|
||||
const { lng, lat } = e.lnglat;
|
||||
// 点击地图时,使用逆地理编码获取位置信息
|
||||
const geocoder = new window.AMap.Geocoder();
|
||||
geocoder.getAddress([lng, lat], (status, result) => {
|
||||
if (status === 'complete' && result.info === 'OK') {
|
||||
const address = result.regeocode;
|
||||
formState.value.address = address.formattedAddress;
|
||||
// 如果还没有设置小区名称,使用POI信息或地址信息作为默认名称
|
||||
if (!formState.value.name && address.pois && address.pois.length > 0) {
|
||||
formState.value.name = address.pois[0].name;
|
||||
}
|
||||
updateMarkerPosition(lng, lat);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化地图失败:', error);
|
||||
message.error('初始化地图失败,请刷新重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索地址
|
||||
const handleSearch = async (value) => {
|
||||
if (!value || value.length < 2) {
|
||||
searchOptions.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
searchLoading.value = true;
|
||||
await initAMap(); // 确保地图已加载
|
||||
|
||||
const autoComplete = new window.AMap.AutoComplete({
|
||||
city: '全国'
|
||||
});
|
||||
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
autoComplete.search(value, (status, result) => {
|
||||
if (status === 'complete') {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(new Error('搜索失败'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
searchOptions.value = result.tips.map(tip => ({
|
||||
value: tip.name,
|
||||
label: `${tip.name} (${tip.district})`,
|
||||
location: tip.location,
|
||||
address: tip.address || tip.district
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('搜索地址失败:', error);
|
||||
searchOptions.value = [];
|
||||
} finally {
|
||||
searchLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 选择地址
|
||||
const handleSelect = (value, option) => {
|
||||
const selectedOption = searchOptions.value.find(opt => opt.value === value);
|
||||
if (selectedOption) {
|
||||
// 更新地址和小区名称
|
||||
formState.value.address = selectedOption.address;
|
||||
formState.value.name = selectedOption.value; // 使用选中的地点名称作为小区名称
|
||||
|
||||
if (selectedOption.location) {
|
||||
updateMarkerPosition(
|
||||
selectedOption.location.lng,
|
||||
selectedOption.location.lat,
|
||||
selectedOption.value
|
||||
);
|
||||
addMap.value.setCenter([selectedOption.location.lng, selectedOption.location.lat]);
|
||||
} else {
|
||||
// 如果没有直接的位置信息,使用 PlaceSearch 获取详细信息
|
||||
const placeSearch = new window.AMap.PlaceSearch({
|
||||
city: '全国'
|
||||
});
|
||||
|
||||
placeSearch.search(value, (status, result) => {
|
||||
if (status === 'complete' && result.info === 'OK') {
|
||||
const poi = result.poiList.pois[0];
|
||||
formState.value.address = poi.address;
|
||||
updateMarkerPosition(
|
||||
poi.location.lng,
|
||||
poi.location.lat,
|
||||
poi.name
|
||||
);
|
||||
addMap.value.setCenter([poi.location.lng, poi.location.lat]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const handleAdd = () => {
|
||||
formRef.value.validate().then(async () => {
|
||||
try {
|
||||
confirmLoading.value = true
|
||||
const res = await createCommunity(formState.value)
|
||||
if (res.code === 200) {
|
||||
message.success('添加成功')
|
||||
addModalVisible.value = false
|
||||
fetchData() // 刷新列表
|
||||
} else {
|
||||
message.error(res.message || '添加失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('添加小区失败:', error)
|
||||
message.error('添加失败')
|
||||
} finally {
|
||||
confirmLoading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 取消添加
|
||||
const handleCancel = () => {
|
||||
formRef.value?.resetFields()
|
||||
addModalVisible.value = false
|
||||
searchAddress.value = ''
|
||||
if (addMarker.value) {
|
||||
addMarker.value.setMap(null)
|
||||
addMarker.value = null
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
@ -215,7 +492,20 @@ export default defineComponent({
|
||||
closeMap,
|
||||
getStatusText,
|
||||
getStatusColor,
|
||||
formatDateTime
|
||||
formatDateTime,
|
||||
addModalVisible,
|
||||
confirmLoading,
|
||||
searchLoading,
|
||||
searchAddress,
|
||||
formState,
|
||||
formRef,
|
||||
rules,
|
||||
showAddModal,
|
||||
handleAdd,
|
||||
handleCancel,
|
||||
handleSearch,
|
||||
searchOptions,
|
||||
handleSelect
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -236,4 +526,120 @@ export default defineComponent({
|
||||
:deep(.ant-table-content) {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
:deep(.ant-modal-body) {
|
||||
padding: 24px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-form) {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(.ant-input),
|
||||
:deep(.ant-input-number),
|
||||
:deep(.ant-auto-complete) {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
:deep(.ant-input-number-input) {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item-control-input) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-col) {
|
||||
padding-right: 12px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
:deep(.ant-select-item-option-content) {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
:deep(.ant-row) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(.ant-col) {
|
||||
padding-right: 8px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* 地图区域样式 */
|
||||
.map-section {
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.search-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.map-container {
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item-label) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
:deep(.ant-input),
|
||||
:deep(.ant-input-number),
|
||||
:deep(.ant-auto-complete) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-input-group) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
:deep(.ant-input-group .ant-input-number) {
|
||||
border-radius: 0;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-form) {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
:deep(.ant-modal-body) {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
:deep(.ant-modal-footer) {
|
||||
text-align: right;
|
||||
padding: 16px 24px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user