generated from Leo_Ding/web-template
代码修改
This commit is contained in:
parent
b3377a317a
commit
a0188776e9
11
package-lock.json
generated
11
package-lock.json
generated
@ -8,6 +8,7 @@
|
||||
"name": "HaHa-Admin",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
"@ant-design/colors": "^7.0.0",
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"@icon-park/vue-next": "^1.4.2",
|
||||
@ -240,6 +241,11 @@
|
||||
"@algolia/requester-common": "4.19.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@amap/amap-jsapi-loader": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz",
|
||||
"integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw=="
|
||||
},
|
||||
"node_modules/@ant-design/colors": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-7.0.0.tgz",
|
||||
@ -5179,6 +5185,11 @@
|
||||
"@algolia/requester-common": "4.19.1"
|
||||
}
|
||||
},
|
||||
"@amap/amap-jsapi-loader": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz",
|
||||
"integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw=="
|
||||
},
|
||||
"@ant-design/colors": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-7.0.0.tgz",
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
"src/**/*.{js,vue}": "eslint --ext .js,.vue .eslintignore --no-cache --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
"@ant-design/colors": "^7.0.0",
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"@icon-park/vue-next": "^1.4.2",
|
||||
|
||||
926
src/components/Map/index.vue
Normal file
926
src/components/Map/index.vue
Normal file
@ -0,0 +1,926 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:open="visible"
|
||||
:title="title"
|
||||
:width="width"
|
||||
:maskClosable="false"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<!-- 错误提示区域 -->
|
||||
<a-alert
|
||||
v-if="errorMessage"
|
||||
:message="errorMessage"
|
||||
:description="errorDetails"
|
||||
type="error"
|
||||
show-icon
|
||||
style="margin-bottom: 12px"
|
||||
closable
|
||||
@close="clearError"
|
||||
/>
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<div class="search-section">
|
||||
<a-form layout="vertical" :model="searchForm">
|
||||
<a-form-item label="输入地址">
|
||||
<a-input-search
|
||||
v-model:value="searchForm.keyword"
|
||||
placeholder="输入需要查找的地址"
|
||||
enter-button
|
||||
@search="handleSearch"
|
||||
:disabled="!mapLoaded || isSearching"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="搜索结果" v-if="searchList.length > 0">
|
||||
<a-select
|
||||
v-model:value="selectedPlaceId"
|
||||
style="width: 100%"
|
||||
placeholder="请选择搜索结果"
|
||||
@change="handlePlaceSelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in searchList"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }} - {{ item.address }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
<!-- 地图容器,带加载状态 -->
|
||||
<div class="map-container" :style="{ height: mapHeight }">
|
||||
<div v-if="!mapLoaded" class="map-loading">
|
||||
<a-spin size="large" />
|
||||
<div class="loading-text">{{ loadingText }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 调试信息 (仅开发环境显示) -->
|
||||
<!-- <div v-if="debugMode && mapLoaded" class="debug-info">
|
||||
<div class="debug-header">调试信息</div>
|
||||
<pre>{{ debugInfo }}</pre>
|
||||
</div> -->
|
||||
|
||||
<div id="amap-container" class="amap-instance" ref="mapContainer"></div>
|
||||
</div>
|
||||
|
||||
<a-form :model="formState" layout="vertical" style="margin-top: 16px">
|
||||
<a-form-item label="经纬度">
|
||||
<a-input
|
||||
v-model:value="formState.lnglat"
|
||||
readonly
|
||||
placeholder="点击地图或搜索地点选择位置"
|
||||
>
|
||||
<template #suffix>
|
||||
<a-spin v-if="isGeocoding" size="small" />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="详细地址">
|
||||
<a-input
|
||||
v-model:value="formState.address"
|
||||
readonly
|
||||
placeholder="地址信息将自动显示"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, watch, nextTick, reactive, computed } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import AMapLoader from '@amap/amap-jsapi-loader'
|
||||
import { debounce } from 'lodash-es'
|
||||
|
||||
// 类型定义
|
||||
interface FormState {
|
||||
lnglat: string
|
||||
address: string
|
||||
location: { lng: number; lat: number } | null
|
||||
}
|
||||
|
||||
interface SearchPlace {
|
||||
id: string
|
||||
name: string
|
||||
address: string
|
||||
location: { lng: number; lat: number }
|
||||
}
|
||||
|
||||
interface LocationData {
|
||||
lng: number
|
||||
lat: number
|
||||
address: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
// 组件属性
|
||||
const props = defineProps({
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '选择位置'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '800px'
|
||||
},
|
||||
searchPlaceholder: {
|
||||
type: String,
|
||||
default: '请输入地点名称,如:北京西站、上海外滩'
|
||||
},
|
||||
defaultCenter: {
|
||||
type: Array as () => [number, number],
|
||||
default: () => [120.894522, 31.981269] // 南通
|
||||
},
|
||||
defaultZoom: {
|
||||
type: Number,
|
||||
default: 12
|
||||
},
|
||||
searchCity: {
|
||||
type: String,
|
||||
default: '全国'
|
||||
},
|
||||
mapHeight: {
|
||||
type: String,
|
||||
default: '400px'
|
||||
},
|
||||
debugMode: {
|
||||
type: Boolean,
|
||||
default: process.env.NODE_ENV === 'development'
|
||||
}
|
||||
})
|
||||
|
||||
// 组件事件
|
||||
const emit = defineEmits(['update:open', 'select', 'error', 'handleGetLng'])
|
||||
|
||||
// 状态管理
|
||||
const visible = ref(false)
|
||||
const mapLoaded = ref(false)
|
||||
const isGeocoding = ref(false)
|
||||
const isSearching = ref(false)
|
||||
const loadingText = ref('地图加载中...')
|
||||
const errorMessage = ref('')
|
||||
const errorDetails = ref('')
|
||||
const debugInfo = ref('')
|
||||
const selectedPlaceId = ref('')
|
||||
|
||||
const formState: FormState = reactive({
|
||||
lnglat: '',
|
||||
address: '',
|
||||
location: null
|
||||
})
|
||||
|
||||
const searchForm = reactive({
|
||||
keyword: ''
|
||||
})
|
||||
|
||||
const searchList = ref<SearchPlace[]>([])
|
||||
|
||||
// 地图实例
|
||||
const mapContainer = ref<HTMLElement>()
|
||||
let map: AMap.Map | null = null
|
||||
let marker: AMap.Marker | null = null
|
||||
let placeSearch: AMap.PlaceSearch | null = null
|
||||
let geocoder: AMap.Geocoder | null = null
|
||||
let infoWindow: AMap.InfoWindow | null = null
|
||||
let markers: AMap.Marker[] = []
|
||||
|
||||
// 防抖搜索 (300ms)
|
||||
const debouncedSearch = debounce((keyword: string) => {
|
||||
performSearch(keyword)
|
||||
}, 300)
|
||||
|
||||
// 日志记录
|
||||
const logDebug = (message: string) => {
|
||||
if (props.debugMode) {
|
||||
const timestamp = new Date().toLocaleTimeString()
|
||||
debugInfo.value = `[${timestamp}] ${message}\n${debugInfo.value}`
|
||||
|
||||
if (debugInfo.value.length > 5000) {
|
||||
debugInfo.value = debugInfo.value.substring(0, 5000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清除错误信息
|
||||
const clearError = () => {
|
||||
errorMessage.value = ''
|
||||
errorDetails.value = ''
|
||||
emit('error', null)
|
||||
}
|
||||
|
||||
// 记录错误信息
|
||||
const setError = (message: string, details?: string) => {
|
||||
errorMessage.value = message
|
||||
errorDetails.value = details || ''
|
||||
emit('error', { message, details })
|
||||
logDebug(`错误: ${message} - ${details}`)
|
||||
}
|
||||
|
||||
// 文本转义防XSS
|
||||
const escapeHtml = (str: string): string => {
|
||||
if (!str) return ''
|
||||
return str.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
// 清理地图资源
|
||||
const cleanupMap = () => {
|
||||
logDebug('开始清理地图资源')
|
||||
debouncedSearch.cancel()
|
||||
isSearching.value = false
|
||||
|
||||
clearMarkers()
|
||||
|
||||
if (placeSearch) {
|
||||
try {
|
||||
placeSearch.clear()
|
||||
placeSearch.destroy()
|
||||
placeSearch = null
|
||||
logDebug('搜索服务已销毁')
|
||||
} catch (e) {
|
||||
logDebug(`清理搜索服务时出错: ${e instanceof Error ? e.message : String(e)}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (infoWindow) {
|
||||
infoWindow.close()
|
||||
infoWindow = null
|
||||
logDebug('信息窗口已关闭')
|
||||
}
|
||||
|
||||
clearMarker()
|
||||
|
||||
if (map) {
|
||||
try {
|
||||
map.destroy()
|
||||
map = null
|
||||
logDebug('地图实例已销毁')
|
||||
} catch (e) {
|
||||
logDebug(`清理地图时出错: ${e instanceof Error ? e.message : String(e)}`)
|
||||
}
|
||||
}
|
||||
|
||||
mapLoaded.value = false
|
||||
isGeocoding.value = false
|
||||
clearError()
|
||||
|
||||
Object.assign(formState, {
|
||||
lnglat: '',
|
||||
address: '',
|
||||
location: null
|
||||
})
|
||||
searchForm.keyword = ''
|
||||
searchList.value = []
|
||||
selectedPlaceId.value = ''
|
||||
}
|
||||
|
||||
// 清除标记
|
||||
const clearMarker = () => {
|
||||
if (marker) {
|
||||
try {
|
||||
map?.remove(marker)
|
||||
marker = null
|
||||
logDebug('主标记已移除')
|
||||
} catch (e) {
|
||||
logDebug(`清理主标记时出错: ${e instanceof Error ? e.message : String(e)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清除所有搜索结果标记
|
||||
const clearMarkers = () => {
|
||||
markers.forEach(marker => {
|
||||
map?.remove(marker)
|
||||
})
|
||||
markers = []
|
||||
logDebug(`已清除 ${markers.length} 个搜索结果标记`)
|
||||
}
|
||||
|
||||
// 创建标记内容
|
||||
const createMarkerContent = (name: string, index: number) => {
|
||||
const escapedName = escapeHtml(name)
|
||||
return `
|
||||
<div style="
|
||||
background: #1890ff;
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
|
||||
">
|
||||
${index}. ${escapedName}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
// 显示位置信息窗口
|
||||
const showLocationInfo = (place: SearchPlace, isSearchResult: boolean = false) => {
|
||||
if (infoWindow) {
|
||||
infoWindow.close()
|
||||
}
|
||||
|
||||
const escapedName = escapeHtml(place.name)
|
||||
const escapedAddress = escapeHtml(place.address || '地址不详')
|
||||
|
||||
const content = `
|
||||
<div style="padding: 8px; max-width: 200px;">
|
||||
<div style="font-weight: bold; margin-bottom: 4px;">${escapedName}</div>
|
||||
<div style="color: #666; font-size: 12px;">${escapedAddress}</div>
|
||||
<div style="color: #999; font-size: 12px; margin-top: 4px;">
|
||||
${isSearchResult ? '搜索结果' : '选中位置'}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
infoWindow = new AMap.InfoWindow({
|
||||
content: content,
|
||||
offset: new AMap.Pixel(0, -30),
|
||||
position: [place.location.lng, place.location.lat]
|
||||
})
|
||||
|
||||
infoWindow.open(map, [place.location.lng, place.location.lat])
|
||||
logDebug(`显示位置信息: ${place.name}`)
|
||||
}
|
||||
|
||||
// 处理搜索结果
|
||||
const handleSearchComplete = (result: AMap.PlaceSearchResult) => {
|
||||
isSearching.value = false
|
||||
logDebug(`搜索完成: ${result.info}, 结果数: ${result.poiList?.pois?.length || 0}`)
|
||||
|
||||
if (result.info === 'OK' && result.poiList?.pois?.length > 0) {
|
||||
const pois = result.poiList.pois
|
||||
|
||||
// 更新搜索结果列表
|
||||
searchList.value = pois.map((poi: any) => ({
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
address: poi.address,
|
||||
location: {
|
||||
lng: poi.location.lng,
|
||||
lat: poi.location.lat
|
||||
}
|
||||
}))
|
||||
|
||||
// 清除之前的标记
|
||||
clearMarkers()
|
||||
|
||||
// 为每个结果添加标记
|
||||
pois.forEach((poi: any, index: number) => {
|
||||
const position = [poi.location.lng, poi.location.lat]
|
||||
const poiMarker = new AMap.Marker({
|
||||
position: position,
|
||||
map: map,
|
||||
title: poi.name,
|
||||
content: createMarkerContent(poi.name, index + 1),
|
||||
offset: new AMap.Pixel(-15, -42)
|
||||
})
|
||||
|
||||
// 点击标记事件
|
||||
poiMarker.on('click', () => {
|
||||
const place: SearchPlace = {
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
address: poi.address,
|
||||
location: { lng: poi.location.lng, lat: poi.location.lat }
|
||||
}
|
||||
updateSelectedLocation([poi.location.lng, poi.location.lat], place)
|
||||
showLocationInfo(place, true)
|
||||
selectedPlaceId.value = poi.id
|
||||
})
|
||||
|
||||
markers.push(poiMarker)
|
||||
})
|
||||
|
||||
// 自动选中第一个结果
|
||||
if (pois.length > 0) {
|
||||
setTimeout(() => {
|
||||
const firstPoi = pois[0]
|
||||
const place: SearchPlace = {
|
||||
id: firstPoi.id,
|
||||
name: firstPoi.name,
|
||||
address: firstPoi.address,
|
||||
location: { lng: firstPoi.location.lng, lat: firstPoi.location.lat }
|
||||
}
|
||||
selectedPlaceId.value = firstPoi.id
|
||||
updateSelectedLocation([firstPoi.location.lng, firstPoi.location.lat], place)
|
||||
map?.setCenter([firstPoi.location.lng, firstPoi.location.lat])
|
||||
map?.setZoom(16)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
map?.setFitView()
|
||||
} else {
|
||||
message.info('未找到相关地点')
|
||||
searchList.value = []
|
||||
logDebug('未找到相关地点')
|
||||
}
|
||||
}
|
||||
|
||||
// 执行实际搜索
|
||||
const performSearch = (keyword: string) => {
|
||||
if (!placeSearch) {
|
||||
setError('搜索服务未初始化', '请等待地图加载完成后再试')
|
||||
return
|
||||
}
|
||||
|
||||
if (!mapLoaded.value) {
|
||||
setError('地图未加载完成', '请等待地图加载完成后再试')
|
||||
return
|
||||
}
|
||||
|
||||
isSearching.value = true
|
||||
searchList.value = []
|
||||
selectedPlaceId.value = ''
|
||||
logDebug(`执行搜索: ${keyword}`)
|
||||
|
||||
try {
|
||||
placeSearch.clear()
|
||||
clearMarkers()
|
||||
placeSearch.search(keyword)
|
||||
} catch (e) {
|
||||
isSearching.value = false
|
||||
const errorMsg = e instanceof Error ? e.message : '搜索执行失败'
|
||||
setError('搜索执行失败', errorMsg)
|
||||
message.error('搜索过程中发生错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 创建搜索服务
|
||||
const createPlaceSearch = () => {
|
||||
if (!map) return
|
||||
|
||||
logDebug(`创建PlaceSearch实例,城市限制: ${props.searchCity}`)
|
||||
|
||||
try {
|
||||
placeSearch = new AMap.PlaceSearch({
|
||||
map: map,
|
||||
pageSize: 10,
|
||||
city: props.searchCity,
|
||||
citylimit: props.searchCity !== '全国',
|
||||
autoFitView: true
|
||||
})
|
||||
|
||||
placeSearch.on('complete', handleSearchComplete)
|
||||
placeSearch.on('error', (error: any) => {
|
||||
isSearching.value = false
|
||||
logDebug(`搜索错误: 代码=${error.errorCode}, 信息=${error.errorMsg}, 状态=${error.status}`)
|
||||
|
||||
let userMsg = '搜索服务出错,请重试'
|
||||
let details = `错误代码: ${error.errorCode}, 信息: ${error.errorMsg}`
|
||||
|
||||
switch(error.errorCode) {
|
||||
case 10001:
|
||||
userMsg = 'API Key错误或已过期'
|
||||
details += '\n解决方案: 检查并更新高德地图API Key'
|
||||
break
|
||||
case 10002:
|
||||
userMsg = '没有使用该服务的权限'
|
||||
details += '\n解决方案: 在高德开放平台为当前Key开启POI搜索权限'
|
||||
break
|
||||
case 10003:
|
||||
userMsg = '请求来源未被授权'
|
||||
details += '\n解决方案: 将当前域名添加到Key的Referer白名单中'
|
||||
break
|
||||
case 10004:
|
||||
userMsg = '服务调用配额不足'
|
||||
details += '\n解决方案: 检查服务调用配额或升级服务'
|
||||
break
|
||||
case 10005:
|
||||
userMsg = '服务暂不可用'
|
||||
details += '\n解决方案: 稍后重试或联系高德技术支持'
|
||||
break
|
||||
}
|
||||
|
||||
setError(userMsg, details)
|
||||
message.error(userMsg)
|
||||
})
|
||||
} catch (e) {
|
||||
const errorMsg = e instanceof Error ? e.message : '创建搜索服务失败'
|
||||
setError('搜索服务初始化失败', errorMsg)
|
||||
logDebug(`创建搜索服务失败: ${errorMsg}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建地理编码服务
|
||||
const createGeocoder = () => {
|
||||
try {
|
||||
geocoder = new AMap.Geocoder({
|
||||
radius: 1000,
|
||||
extensions: 'all',
|
||||
city: '全国'
|
||||
})
|
||||
logDebug('地理编码服务已创建')
|
||||
} catch (e) {
|
||||
const errorMsg = e instanceof Error ? e.message : '创建地理编码服务失败'
|
||||
setError('地理编码服务初始化失败', errorMsg)
|
||||
logDebug(`创建地理编码服务失败: ${errorMsg}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化地图插件
|
||||
const initPlugins = async (): Promise<void> => {
|
||||
logDebug('初始化地图插件')
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
AMap.plugin(['AMap.PlaceSearch', 'AMap.Geocoder', 'AMap.ToolBar', 'AMap.Scale'], () => {
|
||||
logDebug('插件加载回调触发')
|
||||
|
||||
try {
|
||||
createPlaceSearch()
|
||||
createGeocoder()
|
||||
|
||||
// 添加控件
|
||||
map?.addControl(new AMap.ToolBar())
|
||||
map?.addControl(new AMap.Scale())
|
||||
|
||||
logDebug('插件加载成功')
|
||||
resolve()
|
||||
} catch (e) {
|
||||
const errorMsg = e instanceof Error ? e.message : '插件初始化失败'
|
||||
logDebug(`插件初始化失败: ${errorMsg}`)
|
||||
reject(new Error(errorMsg))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 更新选中位置信息
|
||||
const updateSelectedLocation = async (position: [number, number], place?: SearchPlace) => {
|
||||
const [lng, lat] = position
|
||||
formState.lnglat = `${lng.toFixed(6)}, ${lat.toFixed(6)}`
|
||||
formState.location = { lng, lat }
|
||||
logDebug(`位置更新: ${formState.lnglat}`)
|
||||
|
||||
// 更新标记
|
||||
updateMarker(position)
|
||||
|
||||
// 地址反查
|
||||
if (geocoder) {
|
||||
isGeocoding.value = true
|
||||
geocoder.getAddress(position, (status, result) => {
|
||||
isGeocoding.value = false
|
||||
|
||||
if (status === 'complete' && result.regeocode) {
|
||||
formState.address = result.regeocode.formattedAddress
|
||||
logDebug(`地址反查成功: ${formState.address}`)
|
||||
|
||||
// 触发 handleGetLng 事件
|
||||
const locationData: LocationData = {
|
||||
lng,
|
||||
lat,
|
||||
address: formState.address,
|
||||
name: place?.name
|
||||
}
|
||||
emit('handleGetLng', locationData)
|
||||
} else if (place?.address) {
|
||||
formState.address = place.address
|
||||
logDebug(`使用POI地址: ${formState.address}`)
|
||||
|
||||
const locationData: LocationData = {
|
||||
lng,
|
||||
lat,
|
||||
address: place.address,
|
||||
name: place.name
|
||||
}
|
||||
emit('handleGetLng', locationData)
|
||||
} else {
|
||||
formState.address = '地址不详'
|
||||
logDebug('地址反查失败')
|
||||
|
||||
const locationData: LocationData = {
|
||||
lng,
|
||||
lat,
|
||||
address: '地址不详'
|
||||
}
|
||||
emit('handleGetLng', locationData)
|
||||
}
|
||||
})
|
||||
} else if (place?.address) {
|
||||
formState.address = place.address
|
||||
logDebug(`使用POI地址: ${formState.address}`)
|
||||
|
||||
const locationData: LocationData = {
|
||||
lng,
|
||||
lat,
|
||||
address: place.address,
|
||||
name: place.name
|
||||
}
|
||||
emit('handleGetLng', locationData)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新标记位置
|
||||
const updateMarker = (position: [number, number]) => {
|
||||
if (!map) return
|
||||
|
||||
clearMarker()
|
||||
|
||||
try {
|
||||
marker = new AMap.Marker({
|
||||
position: position,
|
||||
map: map,
|
||||
draggable: true,
|
||||
icon: new AMap.Icon({
|
||||
size: new AMap.Size(36, 36),
|
||||
image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
|
||||
imageSize: new AMap.Size(36, 36)
|
||||
})
|
||||
})
|
||||
|
||||
marker.on('dragend', (event) => {
|
||||
const newPosition = [event.lnglat.lng, event.lnglat.lat] as [number, number]
|
||||
updateSelectedLocation(newPosition)
|
||||
})
|
||||
|
||||
logDebug(`标记已更新到: ${position.join(', ')}`)
|
||||
} catch (e) {
|
||||
const errorMsg = e instanceof Error ? e.message : '创建标记失败'
|
||||
logDebug(`更新标记失败: ${errorMsg}`)
|
||||
message.error('更新位置标记失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理地图点击事件
|
||||
const handleMapClick = (e: any) => {
|
||||
const lng = e.lnglat.getLng()
|
||||
const lat = e.lnglat.getLat()
|
||||
|
||||
// 清除搜索结果
|
||||
clearMarkers()
|
||||
searchList.value = []
|
||||
selectedPlaceId.value = ''
|
||||
|
||||
updateSelectedLocation([lng, lat])
|
||||
|
||||
// 使用逆地理编码获取地址信息
|
||||
if (geocoder) {
|
||||
geocoder.getAddress([lng, lat], (status, result) => {
|
||||
if (status === 'complete' && result.regeocode) {
|
||||
const address = result.regeocode.formattedAddress
|
||||
|
||||
// 添加信息窗口
|
||||
if (infoWindow) {
|
||||
infoWindow.close()
|
||||
}
|
||||
|
||||
infoWindow = new AMap.InfoWindow({
|
||||
content: `
|
||||
<div style="padding:10px;min-width:200px;">
|
||||
<div style="font-weight:bold;margin-bottom:5px;">点击位置</div>
|
||||
<div>${address}</div>
|
||||
<div style="margin-top:8px;color:#666;">
|
||||
<div>经度: ${lng.toFixed(6)}</div>
|
||||
<div>纬度: ${lat.toFixed(6)}</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
offset: new AMap.Pixel(0, -35)
|
||||
})
|
||||
|
||||
infoWindow.open(map, [lng, lat])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化地图
|
||||
const initMap = async () => {
|
||||
logDebug('开始初始化地图')
|
||||
clearError()
|
||||
|
||||
if (!mapContainer.value) {
|
||||
const errorMsg = '地图容器元素未找到'
|
||||
setError('初始化失败', errorMsg)
|
||||
console.error(errorMsg)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
loadingText.value = '正在初始化地图...'
|
||||
|
||||
// 安全密钥配置
|
||||
window._AMapSecurityConfig = {
|
||||
securityJsCode: 'df197447a4adc77f0cb376a44462272c'
|
||||
}
|
||||
|
||||
// 加载地图 SDK
|
||||
const AMap = await AMapLoader.load({
|
||||
key: "38b334d84b1f89aa39d4efae76f0b341",
|
||||
version: "2.0",
|
||||
plugins: ['AMap.ToolBar', 'AMap.Scale', 'AMap.Geocoder', 'AMap.PlaceSearch']
|
||||
})
|
||||
|
||||
// 初始化地图
|
||||
map = new AMap.Map('amap-container', {
|
||||
zoom: props.defaultZoom,
|
||||
center: props.defaultCenter,
|
||||
viewMode: "3D",
|
||||
resizeEnable: true
|
||||
})
|
||||
|
||||
map.on('complete', () => {
|
||||
logDebug('地图加载完成')
|
||||
mapLoaded.value = true
|
||||
loadingText.value = '地图加载中...'
|
||||
|
||||
initPlugins().catch(error => {
|
||||
console.error('插件初始化失败:', error)
|
||||
setError('功能初始化失败', error.message)
|
||||
message.error('地图功能初始化失败: ' + error.message)
|
||||
})
|
||||
})
|
||||
|
||||
map.on('click', handleMapClick)
|
||||
|
||||
// 监听地图错误
|
||||
map.on('error', (error) => {
|
||||
const errorMsg = error.error || '地图加载错误'
|
||||
logDebug(`地图错误: ${errorMsg}`)
|
||||
setError('地图加载错误', errorMsg)
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : '地图初始化失败'
|
||||
logDebug(`地图初始化失败: ${errorMsg}`)
|
||||
setError('地图初始化失败', errorMsg)
|
||||
message.error('地图初始化失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (keyword: string) => {
|
||||
clearError()
|
||||
|
||||
if (!keyword?.trim()) {
|
||||
message.warning('请输入搜索关键词')
|
||||
debouncedSearch.cancel()
|
||||
searchList.value = []
|
||||
selectedPlaceId.value = ''
|
||||
clearMarkers()
|
||||
return
|
||||
}
|
||||
|
||||
if (!placeSearch) {
|
||||
message.warning('搜索服务初始化中,请稍后重试')
|
||||
logDebug('搜索服务尚未初始化,等待初始化后重试')
|
||||
|
||||
initPlugins().then(() => {
|
||||
handleSearch(keyword)
|
||||
}).catch(() => {
|
||||
message.error('搜索功能初始化失败,无法执行搜索')
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!mapLoaded.value) {
|
||||
message.warning('地图尚未加载完成,请稍后重试')
|
||||
return
|
||||
}
|
||||
|
||||
debouncedSearch(keyword.trim())
|
||||
}
|
||||
|
||||
// 处理地点选择
|
||||
const handlePlaceSelect = (placeId: string) => {
|
||||
const selectedPlace = searchList.value.find(item => item.id === placeId)
|
||||
if (selectedPlace) {
|
||||
updateSelectedLocation([selectedPlace.location.lng, selectedPlace.location.lat], selectedPlace)
|
||||
map?.setCenter([selectedPlace.location.lng, selectedPlace.location.lat])
|
||||
map?.setZoom(16)
|
||||
showLocationInfo(selectedPlace, true)
|
||||
}
|
||||
}
|
||||
|
||||
// 确认选择
|
||||
const handleOk = () => {
|
||||
if (!formState.location) {
|
||||
message.warning('请先在地图上选择一个位置')
|
||||
return
|
||||
}
|
||||
|
||||
emit('select', {
|
||||
lnglat: formState.lnglat,
|
||||
address: formState.address,
|
||||
location: formState.location
|
||||
})
|
||||
|
||||
handleCancel()
|
||||
}
|
||||
|
||||
// 取消选择
|
||||
const handleCancel = () => {
|
||||
visible.value = false
|
||||
emit('update:open', false)
|
||||
cleanupMap()
|
||||
}
|
||||
|
||||
// 同步弹框状态
|
||||
watch(
|
||||
() => props.open,
|
||||
(val) => {
|
||||
logDebug(`弹窗状态变化: ${val}`)
|
||||
visible.value = val
|
||||
if (val) {
|
||||
nextTick(() => {
|
||||
initMap()
|
||||
})
|
||||
} else {
|
||||
cleanupMap()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(visible, (val) => {
|
||||
if (!val) {
|
||||
emit('update:open', false)
|
||||
}
|
||||
})
|
||||
|
||||
// 组件卸载时清理
|
||||
onUnmounted(() => {
|
||||
logDebug('组件卸载,清理资源')
|
||||
cleanupMap()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.ant-input-search) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.map-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.amap-instance {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.map-loading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-top: 16px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.debug-info {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
padding: 8px;
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.debug-header {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
:deep(.ant-alert) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
@ -5,7 +5,7 @@
|
||||
<a-row :gutter="gutter">
|
||||
<a-col v-bind="colSpan">
|
||||
<a-form-item :label="'所在区域'" name="area">
|
||||
<a-input :placeholder="'请选择区域'" v-model:value="searchFormData.area"></a-input>
|
||||
<AreaCascader v-model:value="searchFormData.currentNode" @change="onAreaChange" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
@ -24,26 +24,30 @@
|
||||
<a-col v-bind="colSpan">
|
||||
<a-form-item :label="'等级'" name="level">
|
||||
<a-select v-model:value="searchFormData.level" @change="handleChange">
|
||||
<a-select-option value="jack">已结单</a-select-option>
|
||||
<a-select-option value="lucy">已作废</a-select-option>
|
||||
<a-select-option v-for="item in dicsStore.dictOptions.level" :key="item.dval"
|
||||
:value="item.dval">
|
||||
{{ item.introduction }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col v-bind="colSpan">
|
||||
<!-- <a-col v-bind="colSpan">
|
||||
<a-form-item :label="'可用钱包'" name="wallet">
|
||||
<a-select v-model:value="searchFormData.wallet" @change="handleChange">
|
||||
<a-select-option value="jack">已结单</a-select-option>
|
||||
<a-select-option value="lucy">已作废</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-col> -->
|
||||
|
||||
<a-col v-bind="colSpan">
|
||||
<a-form-item :label="'收否中标'" name="bidStatus">
|
||||
<a-form-item :label="'是否中标'" name="bidStatus">
|
||||
<a-select v-model:value="searchFormData.bidStatus" @change="handleChange">
|
||||
<a-select-option value="jack">已结单</a-select-option>
|
||||
<a-select-option value="lucy">已作废</a-select-option>
|
||||
<a-select-option v-for="item in dicsStore.dictOptions.WinTheBidding" :key="item.dval"
|
||||
:value="item.dval">
|
||||
{{ item.introduction }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
@ -135,6 +139,8 @@ import EditDialog from './components/EditDialog.vue'
|
||||
import DeviceManagementModal from './components/AddEquipments.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import storage from '@/utils/storage'
|
||||
import AreaCascader from '@/components/AreaCascader/index.vue'
|
||||
import { useDicsStore } from '@/store'
|
||||
|
||||
defineOptions({
|
||||
name: 'menu',
|
||||
@ -169,6 +175,7 @@ const columns = ref([
|
||||
const { listData, loading, showLoading, hideLoading, searchFormData, paginationState, resetPagination } =
|
||||
usePagination()
|
||||
const { resetForm } = useForm()
|
||||
const dicsStore = useDicsStore()
|
||||
|
||||
getMenuList()
|
||||
|
||||
|
||||
@ -1,15 +1,6 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:width="800"
|
||||
:open="modal.open"
|
||||
:title="modal.title"
|
||||
:confirm-loading="modal.confirmLoading"
|
||||
:after-close="onAfterClose"
|
||||
:cancel-text="cancelText"
|
||||
:ok-text="okText"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-modal :width="800" :open="modal.open" :title="modal.title" :confirm-loading="modal.confirmLoading"
|
||||
:after-close="onAfterClose" :cancel-text="cancelText" :ok-text="okText" @ok="handleOk" @cancel="handleCancel">
|
||||
<a-form layout="vertical" ref="formRef" :model="formData" :rules="formRules">
|
||||
<a-card class="mb-4">
|
||||
<a-row :gutter="16">
|
||||
@ -64,7 +55,8 @@
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item :label="'人员编制(个)'" name="staffCount">
|
||||
<a-input-number v-model:value="formData.staffCount" min="0" style="width: 100%"></a-input-number>
|
||||
<a-input-number v-model:value="formData.staffCount" min="0"
|
||||
style="width: 100%"></a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@ -75,12 +67,8 @@
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="24">
|
||||
<a-form-item :label="'服务中心地址'" name="address">
|
||||
<a-cascader
|
||||
v-model:value="formData.address"
|
||||
:options="addressOptions"
|
||||
placeholder="请选择省/市/区"
|
||||
style="width: 100%"
|
||||
></a-cascader>
|
||||
<a-cascader v-model:value="formData.address" :options="addressOptions"
|
||||
placeholder="请选择省/市/区" style="width: 100%"></a-cascader>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
@ -92,11 +80,7 @@
|
||||
<a-form-item :label="'地图定位地址'" name="location">
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="18">
|
||||
<a-input
|
||||
v-model:value="formData.location"
|
||||
placeholder="请选择地图位置"
|
||||
readonly
|
||||
></a-input>
|
||||
<a-input v-model:value="formData.location" placeholder="请选择地图位置" readonly></a-input>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-button type="primary" block @click="openMapSelector">选择地图位置</a-button>
|
||||
@ -112,30 +96,20 @@
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item :label="'建成时间'" name="buildTime">
|
||||
<a-date-picker
|
||||
v-model:value="formData.buildTime"
|
||||
format="YYYY-MM-DD"
|
||||
placeholder="请选择建成时间"
|
||||
></a-date-picker>
|
||||
<a-date-picker v-model:value="formData.buildTime" format="YYYY-MM-DD"
|
||||
placeholder="请选择建成时间"></a-date-picker>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item :label="'建筑面积(平方米)'" name="area">
|
||||
<a-input-number
|
||||
v-model:value="formData.area"
|
||||
min="0"
|
||||
style="width: 100%"
|
||||
placeholder="请输入建筑面积"
|
||||
></a-input-number>
|
||||
<a-input-number v-model:value="formData.area" min="0" style="width: 100%"
|
||||
placeholder="请输入建筑面积"></a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item :label="'服务介绍'" name="description">
|
||||
<a-textarea
|
||||
v-model:value="formData.description"
|
||||
rows="4"
|
||||
placeholder="请输入服务介绍"
|
||||
></a-textarea>
|
||||
<a-textarea v-model:value="formData.description" rows="4"
|
||||
placeholder="请输入服务介绍"></a-textarea>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@ -146,16 +120,10 @@
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item :label="'开始营业时间'" name="openTime">
|
||||
<a-time-picker
|
||||
v-model:value="formData.openTime"
|
||||
format="HH:mm"
|
||||
placeholder="请选择开始营业时间"
|
||||
></a-time-picker>
|
||||
<a-time-picker
|
||||
v-model:value="formData.closeTime"
|
||||
format="HH:mm"
|
||||
placeholder="请选择结束营业时间"
|
||||
></a-time-picker>
|
||||
<a-time-picker v-model:value="formData.openTime" format="HH:mm"
|
||||
placeholder="请选择开始营业时间"></a-time-picker>
|
||||
<a-time-picker v-model:value="formData.closeTime" format="HH:mm"
|
||||
placeholder="请选择结束营业时间"></a-time-picker>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
@ -183,14 +151,11 @@
|
||||
</span>
|
||||
</template>
|
||||
<div class="service-tags">
|
||||
<a-tag
|
||||
v-for="service in allServices"
|
||||
:key="service.value"
|
||||
<a-tag v-for="service in allServices" :key="service.value"
|
||||
:checked="formData.services?.includes(service.value)"
|
||||
@click="handleServiceClick(service.value)"
|
||||
:class="{ 'ant-tag-checkable-checked': formData.services?.includes(service.value) }"
|
||||
class="service-tag"
|
||||
>
|
||||
class="service-tag">
|
||||
{{ service.label }}
|
||||
</a-tag>
|
||||
</div>
|
||||
@ -228,7 +193,8 @@
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item :label="'床位数'" name="bedCount" :required="true">
|
||||
<a-input-number v-model:value="formData.bedCount" placeholder="请输入床位数" min="0" style="width: 100%"></a-input-number>
|
||||
<a-input-number v-model:value="formData.bedCount" placeholder="请输入床位数" min="0"
|
||||
style="width: 100%"></a-input-number>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
@ -244,11 +210,8 @@
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item :label="'资质附件'" name="qualificationFiles">
|
||||
<a-upload
|
||||
list-type="picture-card"
|
||||
v-model:file-list="formData.qualificationFiles"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<a-upload list-type="picture-card" v-model:file-list="formData.qualificationFiles"
|
||||
:before-upload="beforeUpload">
|
||||
<div v-if="formData.qualificationFiles.length < 5">
|
||||
<plus-outlined />
|
||||
<div class="ant-upload-text">上传</div>
|
||||
@ -258,11 +221,8 @@
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item :label="'站点图片'" name="siteImages">
|
||||
<a-upload
|
||||
list-type="picture-card"
|
||||
v-model:file-list="formData.siteImages"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<a-upload list-type="picture-card" v-model:file-list="formData.siteImages"
|
||||
:before-upload="beforeUpload">
|
||||
<div v-if="formData.siteImages.length < 5">
|
||||
<plus-outlined />
|
||||
<div class="ant-upload-text">上传</div>
|
||||
@ -274,6 +234,17 @@
|
||||
</a-card>
|
||||
|
||||
</a-form>
|
||||
|
||||
<MapPickerModal
|
||||
:open="showMapPicker"
|
||||
@update:open="mapVisible = $event"
|
||||
@handleGetLng="handleLocationChange"
|
||||
@select="handleLocationSelect" />
|
||||
<div v-if="selectedLocation">
|
||||
<p>经纬度: {{ selectedLocation.lnglat }}</p>
|
||||
<p>地址: {{ selectedLocation.address }}</p>
|
||||
<p>名称: {{ selectedLocation.placeName }}</p>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
@ -288,6 +259,7 @@ import { config } from '@/config'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import storage from '@/utils/storage'
|
||||
import { message } from 'ant-design-vue'
|
||||
import MapPickerModal from '@/components/Map/index.vue'
|
||||
const emit = defineEmits(['ok'])
|
||||
const { t } = useI18n() // 解构出t方法
|
||||
const { modal, showModal, hideModal, showLoading, hideLoading } = useModal()
|
||||
@ -404,6 +376,20 @@ const reqType = ['GET', 'POST', 'PUT', 'PATCH', 'HEAD', 'DELETE']
|
||||
const cancelText = ref(t('button.cancel'))
|
||||
const okText = ref(t('button.confirm'))
|
||||
const platform = ref('')
|
||||
const showMapPicker = ref(false)
|
||||
const location = ref('')
|
||||
|
||||
const handleLocationSelect = (lngLat) => {
|
||||
console.log('确认选择:', lngLat)
|
||||
formData.value.location = lngLat.address
|
||||
showMapPicker.value = false
|
||||
}
|
||||
|
||||
// 实时位置变化
|
||||
const handleLocationChange = (location) => {
|
||||
console.log('位置变化:', location)
|
||||
// 可以实时更新UI
|
||||
}
|
||||
|
||||
// 地址选项 - 实际项目中可以从接口获取
|
||||
const addressOptions = ref([
|
||||
@ -615,9 +601,7 @@ function handleServiceClick(value) {
|
||||
* 打开地图选择器
|
||||
*/
|
||||
function openMapSelector() {
|
||||
// 实际项目中可以调用地图组件或打开地图选择弹窗
|
||||
// 这里使用模拟数据
|
||||
formData.value.location = '北京市海淀区中关村大街1号 (经纬度: 39.9847, 116.3055)'
|
||||
showMapPicker.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -11,16 +11,18 @@
|
||||
|
||||
<a-col v-bind="colSpan">
|
||||
<a-form-item :label="'站点类型'" name="type">
|
||||
<a-select v-model:value="searchFormData.type" @change="handleChange">
|
||||
<a-select-option value="jack">已结单</a-select-option>
|
||||
<a-select-option value="lucy">已作废</a-select-option>
|
||||
<a-select v-model:value="searchFormData.type" placeholder="请选择站点类型" allow-clear>
|
||||
<a-select-option v-for="item in dicsStore.dictOptions.Station_Type" :key="item.dval"
|
||||
:value="item.dval">
|
||||
{{ item.introduction }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col v-bind="colSpan">
|
||||
<a-form-item :label="'所在区域'" name="area">
|
||||
<a-input :placeholder="'请选择区域'" v-model:value="searchFormData.area"></a-input>
|
||||
<AreaCascader v-model:value="searchFormData.area" @change="onAreaChange" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
@ -187,6 +189,8 @@ import ImportRecordsModal from '@/components/ImportRecord/index.vue'
|
||||
import ExportRecordsModal from '@/components/ExportRecord/index.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import storage from '@/utils/storage'
|
||||
import AreaCascader from '@/components/AreaCascader/index.vue'
|
||||
|
||||
|
||||
defineOptions({
|
||||
name: 'menu',
|
||||
@ -220,6 +224,8 @@ const { listData, loading, showLoading, hideLoading, searchFormData, paginationS
|
||||
usePagination()
|
||||
const { resetForm } = useForm()
|
||||
const editDialogRef = ref()
|
||||
import { useDicsStore } from '@/store'
|
||||
const dicsStore = useDicsStore()
|
||||
|
||||
getMenuList()
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user