This commit is contained in:
aaron 2025-03-16 10:43:45 +08:00
parent bb4d562346
commit 619d17f9a1
2 changed files with 310 additions and 143 deletions

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="map-picker"> <div class="map-picker-container">
<a-form-item :label="label"> <a-form-item :label="label">
<a-auto-complete <a-auto-complete
v-model:value="searchAddress" v-model:value="searchAddress"
@ -9,6 +9,7 @@
@select="handleSelect" @select="handleSelect"
:loading="searchLoading" :loading="searchLoading"
allow-clear allow-clear
class="map-picker-input"
/> />
</a-form-item> </a-form-item>
@ -16,37 +17,24 @@
<div class="map-container"> <div class="map-container">
<div :id="mapContainerId" style="height: 300px;"></div> <div :id="mapContainerId" style="height: 300px;"></div>
</div> </div>
<div class="form-item-tip">在地图上点击选择准确位置或通过上方搜索框搜索地址</div>
</a-form-item> </a-form-item>
<a-form-item label="详细地址" required> <a-form-item label="详细地址" required>
<a-input v-model:value="address" placeholder="请输入详细地址" /> <a-input v-model:value="address" placeholder="请输入详细地址" />
<div class="form-item-tip">地址将用于配送员导航和定位请确保准确</div>
</a-form-item> </a-form-item>
<a-form-item label="经纬度" required> <!-- 隐藏经纬度输入字段但保持功能不变 -->
<a-input-group compact> <div style="display: none;">
<a-input-number <a-input-number v-model:value="longitude" :min="-180" :max="180" disabled />
v-model:value="longitude" <a-input-number v-model:value="latitude" :min="-90" :max="90" disabled />
:min="-180" </div>
:max="180"
style="width: 50%"
placeholder="经度"
disabled
/>
<a-input-number
v-model:value="latitude"
:min="-90"
:max="90"
style="width: 50%"
placeholder="纬度"
disabled
/>
</a-input-group>
</a-form-item>
</div> </div>
</template> </template>
<script> <script>
import { ref, onMounted, nextTick } from 'vue' import { ref, onMounted, nextTick, watch } from 'vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { loadAMap, createMap, createAutoComplete, createGeocoder } from '@/utils/amap.js' import { loadAMap, createMap, createAutoComplete, createGeocoder } from '@/utils/amap.js'
@ -80,29 +68,88 @@ export default {
const longitude = ref(props.modelValue.longitude) const longitude = ref(props.modelValue.longitude)
const latitude = ref(props.modelValue.latitude) const latitude = ref(props.modelValue.latitude)
// // props
watch(() => props.modelValue, (newVal, oldVal) => {
console.log('props变化:', newVal, oldVal)
//
if (
newVal.address !== address.value ||
newVal.longitude !== longitude.value ||
newVal.latitude !== latitude.value
) {
console.log('更新本地值:', newVal)
address.value = newVal.address
longitude.value = newVal.longitude
latitude.value = newVal.latitude
//
if (map.value && newVal.longitude && newVal.latitude) {
console.log('更新地图标记和中心点:', newVal.longitude, newVal.latitude)
// 使nextTickDOM
nextTick(() => {
try {
if (marker.value) {
marker.value.setPosition([newVal.longitude, newVal.latitude])
} else {
marker.value = new window.AMap.Marker({
position: [newVal.longitude, newVal.latitude],
map: map.value
})
}
map.value.setCenter([newVal.longitude, newVal.latitude])
} catch (error) {
console.error('更新地图标记失败:', error)
}
})
}
}
}, { deep: true, immediate: true })
//
watch([address, longitude, latitude], () => {
updateValue()
})
//
const updateValue = () => { const updateValue = () => {
emit('update:modelValue', { //
address: address.value, if (
longitude: longitude.value, address.value !== props.modelValue.address ||
latitude: latitude.value longitude.value !== props.modelValue.longitude ||
}) latitude.value !== props.modelValue.latitude
) {
emit('update:modelValue', {
address: address.value,
longitude: longitude.value,
latitude: latitude.value
})
}
} }
// //
const updateMarkerPosition = (lng, lat) => { const updateMarkerPosition = (lng, lat) => {
//
if (lng === longitude.value && lat === latitude.value && marker.value) {
return
}
console.log('更新标记位置:', lng, lat)
longitude.value = lng longitude.value = lng
latitude.value = lat latitude.value = lat
if (marker.value) { try {
marker.value.setPosition([lng, lat]) if (marker.value) {
} else { marker.value.setPosition([lng, lat])
marker.value = new window.AMap.Marker({ } else if (map.value) {
position: [lng, lat], marker.value = new window.AMap.Marker({
map: map.value position: [lng, lat],
}) map: map.value
})
}
} catch (error) {
console.error('更新标记位置失败:', error)
} }
updateValue() // updateValuewatch
} }
// //
@ -111,6 +158,7 @@ export default {
await loadAMap() await loadAMap()
if (!map.value) { if (!map.value) {
console.log('初始化地图,初始位置:', props.modelValue)
map.value = createMap(mapContainerId, { map.value = createMap(mapContainerId, {
zoom: 13, zoom: 13,
viewMode: '2D' viewMode: '2D'
@ -135,6 +183,14 @@ export default {
// //
if (props.modelValue.longitude && props.modelValue.latitude) { if (props.modelValue.longitude && props.modelValue.latitude) {
console.log('MapPicker初始化设置初始位置', props.modelValue)
//
map.value.on('complete', () => {
updateMarkerPosition(props.modelValue.longitude, props.modelValue.latitude)
map.value.setCenter([props.modelValue.longitude, props.modelValue.latitude])
})
//
updateMarkerPosition(props.modelValue.longitude, props.modelValue.latitude) updateMarkerPosition(props.modelValue.longitude, props.modelValue.latitude)
map.value.setCenter([props.modelValue.longitude, props.modelValue.latitude]) map.value.setCenter([props.modelValue.longitude, props.modelValue.latitude])
} }
@ -223,10 +279,26 @@ export default {
</script> </script>
<style scoped> <style scoped>
.map-picker-container {
width: 100%;
}
.map-picker-input {
width: 100%;
}
.map-container { .map-container {
border: 1px solid #d9d9d9; border: 1px solid #d9d9d9;
border-radius: 2px; border-radius: 4px;
overflow: hidden; overflow: hidden;
margin-bottom: 16px; margin-bottom: 8px;
width: 100%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.form-item-tip {
margin-top: 8px;
font-size: 0.8em;
color: #999;
} }
</style> </style>

View File

@ -79,15 +79,27 @@
<a-tag v-else color="orange">未设置</a-tag> <a-tag v-else color="orange">未设置</a-tag>
</template> </template>
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<div class="action-buttons"> <a-dropdown>
<a @click="handleEdit(record)">编辑</a> <a class="ant-dropdown-link" @click.prevent>
<a-divider type="vertical" /> 操作 <down-outlined />
<a @click="handleEditDeliveryPrice(record)">配送定价</a> </a>
<a-divider type="vertical" /> <template #overlay>
<a @click="handleEditProfitSharing(record)">设置分润</a> <a-menu>
<a-divider type="vertical" /> <a-menu-item key="edit">
<a @click="handleSetAdmin(record)">{{ record.admin ? '修改服务商' : '设置服务商' }}</a> <a @click="handleEdit(record)">编辑</a>
</div> </a-menu-item>
<a-menu-item key="deliveryPrice">
<a @click="handleEditDeliveryPrice(record)">配送定价</a>
</a-menu-item>
<a-menu-item key="profitSharing">
<a @click="handleEditProfitSharing(record)">设置分润</a>
</a-menu-item>
<a-menu-item key="admin">
<a @click="handleSetAdmin(record)">{{ record.admin ? '修改服务商' : '设置服务商' }}</a>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template> </template>
</template> </template>
</a-table> </a-table>
@ -109,7 +121,7 @@
@ok="handleSubmit" @ok="handleSubmit"
@cancel="handleCancel" @cancel="handleCancel"
:confirmLoading="confirmLoading" :confirmLoading="confirmLoading"
width="680px" width="900px"
> >
<template #footer> <template #footer>
<a-space> <a-space>
@ -124,62 +136,67 @@
ref="formRef" ref="formRef"
:model="formState" :model="formState"
:rules="rules" :rules="rules"
:label-col="{ span: 6 }" layout="vertical"
:wrapper-col="{ span: 16 }"
class="community-form" class="community-form"
> >
<a-form-item label="小区名称" name="name" required> <div class="form-content">
<a-input v-model:value="formState.name" placeholder="请输入小区名称" /> <div class="form-left">
</a-form-item> <a-form-item label="小区名称" name="name" required>
<a-input v-model:value="formState.name" placeholder="请输入小区名称" />
</a-form-item>
<map-picker <a-form-item
v-model="formState.location" label="企业微信Webhook"
label="地址搜索" name="webot_webhook"
/> :rules="[
{ required: false },
<a-form-item { type: 'url', message: '请输入正确的URL地址' }
label="企业微信Webhook" ]"
name="webot_webhook"
required
:rules="[
{ required: true, message: '请输入Webhook地址' },
{ type: 'url', message: '请输入正确的URL地址' }
]"
>
<a-input
v-model:value="formState.webot_webhook"
placeholder="请输入企业微信群机器人Webhook地址"
:maxLength="500"
/>
<div class="form-item-tip">
小区相关通知将推送到此企业微信群
</div>
</a-form-item>
<a-form-item
label="群二维码"
name="qy_group_qrcode"
required
:rules="[{ required: true, message: '请上传群二维码' }]"
>
<div class="qrcode-upload-wrapper">
<a-upload
v-model:file-list="fileList"
name="file"
:customRequest="handleQRCodeUpload"
@remove="handleQRCodeRemove"
list-type="picture-card"
accept=".jpg,.jpeg,.png"
:max-count="1"
@preview="previewQRCode"
> >
<div v-if="!fileList.length"> <a-input
<plus-outlined /> v-model:value="formState.webot_webhook"
<div style="margin-top: 8px">上传群二维码</div> placeholder="请输入企业微信群机器人Webhook地址"
:maxLength="500"
/>
<div class="form-item-tip">
小区相关通知将推送到此企业微信群选填
</div> </div>
</a-upload> </a-form-item>
<a-form-item
label="群二维码"
name="qy_group_qrcode"
required
:rules="[{ required: true, message: '请上传群二维码' }]"
>
<div class="qrcode-upload-wrapper">
<a-upload
v-model:file-list="fileList"
name="file"
:customRequest="handleQRCodeUpload"
@remove="handleQRCodeRemove"
list-type="picture-card"
accept=".jpg,.jpeg,.png"
:max-count="1"
@preview="previewQRCode"
>
<div v-if="!fileList.length">
<plus-outlined />
<div style="margin-top: 8px">上传群二维码</div>
</div>
</a-upload>
</div>
</a-form-item>
</div> </div>
</a-form-item>
<div class="form-right">
<map-picker
v-model="formState.location"
label="地址搜索"
:key="`map-picker-${currentId || 'new'}-${modalVisible}`"
/>
</div>
</div>
</a-form> </a-form>
</a-modal> </a-modal>
@ -660,7 +677,7 @@ export default defineComponent({
'location.longitude': [{ required: true, message: '请在地图上选择位置' }], 'location.longitude': [{ required: true, message: '请在地图上选择位置' }],
'location.latitude': [{ required: true, message: '请在地图上选择位置' }], 'location.latitude': [{ required: true, message: '请在地图上选择位置' }],
webot_webhook: [ webot_webhook: [
{ required: true, message: '请输入Webhook地址' }, { required: false },
{ type: 'url', message: '请输入正确的URL地址' } { type: 'url', message: '请输入正确的URL地址' }
], ],
qy_group_qrcode: [ qy_group_qrcode: [
@ -678,18 +695,27 @@ export default defineComponent({
const handleEdit = (record) => { const handleEdit = (record) => {
isEdit.value = true isEdit.value = true
currentId.value = record.id currentId.value = record.id
modalVisible.value = true
//
if (formRef.value) {
formRef.value.resetFields()
}
// location
const locationData = {
address: record.address || '',
longitude: parseFloat(record.longitude) || null,
latitude: parseFloat(record.latitude) || null
}
console.log('编辑小区,处理后的地址信息:', locationData)
// //
formState.value = { formState.value = {
name: record.name, name: record.name || '',
location: { location: locationData,
address: record.address, qy_group_qrcode: record.qy_group_qrcode || '',
longitude: record.longitude, status: record.status || 'UNOPEN',
latitude: record.latitude
},
qy_group_qrcode: record.qy_group_qrcode,
status: record.status,
webot_webhook: record.webot_webhook || '' webot_webhook: record.webot_webhook || ''
} }
@ -704,13 +730,27 @@ export default defineComponent({
} else { } else {
fileList.value = [] fileList.value = []
} }
//
modalVisible.value = true
// DOMMapPicker
nextTick(() => {
console.log('编辑小区,表单数据:', formState.value)
})
} }
// //
const showAddModal = () => { const showAddModal = () => {
isEdit.value = false isEdit.value = false
currentId.value = null currentId.value = null
modalVisible.value = true
//
if (formRef.value) {
formRef.value.resetFields()
}
// location
formState.value = { formState.value = {
name: '', name: '',
location: { location: {
@ -722,7 +762,11 @@ export default defineComponent({
status: 'UNOPEN', status: 'UNOPEN',
webot_webhook: '' webot_webhook: ''
} }
fileList.value = [] fileList.value = []
//
modalVisible.value = true
} }
// //
@ -1445,9 +1489,7 @@ export default defineComponent({
} }
:deep(.ant-form-item-label) { :deep(.ant-form-item-label) {
height: 28px; padding-bottom: 4px;
line-height: 28px;
padding-bottom: 0;
} }
:deep(.ant-form-item-control) { :deep(.ant-form-item-control) {
@ -1458,27 +1500,54 @@ export default defineComponent({
:deep(.ant-select), :deep(.ant-select),
:deep(.ant-auto-complete), :deep(.ant-auto-complete),
:deep(.ant-input-number) { :deep(.ant-input-number) {
height: 30px; height: 32px;
}
/* 垂直布局表单项样式优化 */
.community-form :deep(.ant-form-item-label) {
text-align: left;
line-height: 1.5;
margin-bottom: 4px;
}
.community-form :deep(.ant-form-item-label > label) {
height: auto;
}
/* 地图组件样式优化 */
:deep(.map-picker-container) {
height: 100%;
display: flex;
flex-direction: column;
}
:deep(.map-picker-input) {
margin-bottom: 8px;
}
:deep(.map-container) {
height: 250px !important;
min-height: auto !important;
} }
:deep(.ant-select-selector), :deep(.ant-select-selector),
:deep(.ant-auto-complete .ant-input) { :deep(.ant-auto-complete .ant-input) {
height: 30px !important; height: 32px !important;
line-height: 30px !important; line-height: 32px !important;
padding: 0 11px !important; padding: 0 11px !important;
.ant-select-selection-search-input { .ant-select-selection-search-input {
height: 28px !important; height: 30px !important;
} }
.ant-select-selection-item { .ant-select-selection-item {
line-height: 28px !important; line-height: 30px !important;
} }
} }
:deep(.ant-input-number-input) { :deep(.ant-input-number-input) {
height: 28px; height: 30px;
line-height: 28px; line-height: 30px;
} }
.form-item-tip { .form-item-tip {
@ -1508,19 +1577,6 @@ export default defineComponent({
} }
} }
/* 调整地图组件样式 */
:deep(.map-picker-container) {
margin-bottom: 12px;
}
:deep(.map-picker-input) {
margin-bottom: 8px;
}
:deep(.map-container) {
height: 200px !important; /* 减少地图高度 */
}
.status-tag { .status-tag {
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
@ -1534,17 +1590,6 @@ export default defineComponent({
} }
/* 调整 Select 组件的样式 */ /* 调整 Select 组件的样式 */
:deep(.ant-select-selector) {
height: 32px !important;
.ant-select-selection-item {
line-height: 30px !important; /* 调整文字行高 */
padding-top: 0 !important; /* 移除顶部内边距 */
padding-bottom: 0 !important; /* 移除底部内边距 */
}
}
/* 调整选项的样式 */
:deep(.ant-select-dropdown) { :deep(.ant-select-dropdown) {
.ant-select-item { .ant-select-item {
padding: 5px 12px; /* 调整选项内边距 */ padding: 5px 12px; /* 调整选项内边距 */
@ -1844,4 +1889,54 @@ export default defineComponent({
.admin-phone { .admin-phone {
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.65);
} }
/* 调整选项的样式 */
:deep(.ant-select-dropdown) {
.ant-select-item {
padding: 5px 12px; /* 调整选项内边距 */
line-height: 22px; /* 调整选项行高 */
}
}
/* 确保输入框内的文字垂直居中 */
:deep(.ant-select-selection-search-input) {
height: 30px !important;
line-height: 30px !important;
}
/* 确保占位符文字垂直居中 */
:deep(.ant-select-selection-placeholder) {
line-height: 30px !important;
}
.delivery-price-info {
line-height: 1.5;
font-size: 13px;
> div {
margin-bottom: 2px;
&:last-child {
margin-bottom: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 12px;
}
}
}
/* 左右布局样式 */
.form-content {
display: flex;
gap: 20px;
}
.form-left {
flex: 1;
max-width: 50%;
}
.form-right {
flex: 1;
max-width: 50%;
}
</style> </style>