|
|
@@ -26,35 +26,33 @@
|
|
|
</MeCrud>
|
|
|
|
|
|
<AddAndEditOffline
|
|
|
- v-if="showAddEditForm"
|
|
|
- ref="addEditRef"
|
|
|
- :is-edit="modalAction === 'edit'"
|
|
|
- :form-data="modalForm"
|
|
|
- :img-list="imgList"
|
|
|
- :modal-action="modalAction"
|
|
|
- :pavilion-options="pavilionOptions"
|
|
|
- :artist-options="artistOptions"
|
|
|
- @cancel="handleFormCancel"
|
|
|
- @confirm="handleComponentConfirm"
|
|
|
- @upload="handleUpload"
|
|
|
- @search-location="handleAddressSelect"
|
|
|
- @pavilion-search="handlePavilionSearch"
|
|
|
- @artist-search="handleArtistSearch"
|
|
|
- />
|
|
|
+ v-if="showAddEditForm"
|
|
|
+ ref="addEditRef"
|
|
|
+ :is-edit="modalAction === 'edit'"
|
|
|
+ :form-data="modalForm"
|
|
|
+ :img-list="imgList"
|
|
|
+ :modal-action="modalAction"
|
|
|
+ :pavilion-options="pavilionOptions"
|
|
|
+ :artist-options="artistOptions"
|
|
|
+ @cancel="handleFormCancel"
|
|
|
+ @confirm="handleComponentConfirm"
|
|
|
+ @upload="handleUpload"
|
|
|
+ @search-location="handleAddressSelect"
|
|
|
+ @pavilion-search="handlePavilionSearch"
|
|
|
+ @artist-search="handleArtistSearch"
|
|
|
+ />
|
|
|
</CommonPage>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { MeCrud, MeModal, MeQueryItem } from '@/components'
|
|
|
+import { MeCrud, MeQueryItem } from '@/components'
|
|
|
import { useCrud } from '@/composables'
|
|
|
-import { withPermission } from '@/directives'
|
|
|
-import { formatDateTime, request } from '@/utils'
|
|
|
-import { NButton, NImage, NSwitch, NTag, NSelect, NUpload, NUploadDragger, NText, NRadio, NRadioGroup, NInputNumber, NDatePicker, NTimePicker } from 'naive-ui'
|
|
|
-import WangEditor from '@/components/common/WangEditor.vue'
|
|
|
-import AddAndEditOffline from './components/addAndEditOffline.vue'
|
|
|
-import api from './api'
|
|
|
-import pavilionApi from '../GalleryMgt/api'
|
|
|
+import { request } from '@/utils'
|
|
|
+import { NButton, NImage } from 'naive-ui'
|
|
|
import artistApi from '../ArtistMgt/api'
|
|
|
+import pavilionApi from '../GalleryMgt/api'
|
|
|
+import api from './api'
|
|
|
+import AddAndEditOffline from './components/addAndEditOffline.vue'
|
|
|
|
|
|
defineOptions({ name: 'OnlineExhibition' })
|
|
|
|
|
|
@@ -86,12 +84,12 @@ const addressLoading = ref(false)
|
|
|
// 图片上传相关状态
|
|
|
const imgList = ref({
|
|
|
cover: [],
|
|
|
- share: []
|
|
|
+ share: [],
|
|
|
})
|
|
|
|
|
|
// 防抖搜索展馆
|
|
|
let searchTimer = null
|
|
|
-const handlePavilionSearch = (query) => {
|
|
|
+function handlePavilionSearch(query) {
|
|
|
if (searchTimer) {
|
|
|
clearTimeout(searchTimer)
|
|
|
}
|
|
|
@@ -102,7 +100,7 @@ const handlePavilionSearch = (query) => {
|
|
|
|
|
|
// 防抖搜索艺术家
|
|
|
let artistSearchTimer = null
|
|
|
-const handleArtistSearch = (query) => {
|
|
|
+function handleArtistSearch(query) {
|
|
|
if (artistSearchTimer) {
|
|
|
clearTimeout(artistSearchTimer)
|
|
|
}
|
|
|
@@ -113,7 +111,7 @@ const handleArtistSearch = (query) => {
|
|
|
|
|
|
// 防抖搜索地址
|
|
|
let addressSearchTimer = null
|
|
|
-const handleAddressSearch = (query) => {
|
|
|
+function handleAddressSearch(query) {
|
|
|
if (addressSearchTimer) {
|
|
|
clearTimeout(addressSearchTimer)
|
|
|
}
|
|
|
@@ -123,12 +121,12 @@ const handleAddressSearch = (query) => {
|
|
|
}
|
|
|
|
|
|
// 加载展馆选项
|
|
|
-const loadPavilionOptions = async (name = '') => {
|
|
|
+async function loadPavilionOptions(name = '') {
|
|
|
pavilionLoading.value = true
|
|
|
try {
|
|
|
const params = {
|
|
|
pageSize: 20,
|
|
|
- pageNo: 1
|
|
|
+ pageNo: 1,
|
|
|
}
|
|
|
if (name) {
|
|
|
params.name = name,
|
|
|
@@ -136,21 +134,23 @@ const loadPavilionOptions = async (name = '') => {
|
|
|
}
|
|
|
const { data } = await pavilionApi.read(params)
|
|
|
pavilionOptions.value = data.pageData || []
|
|
|
- } catch (error) {
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
console.error('加载展馆列表失败:', error)
|
|
|
pavilionOptions.value = []
|
|
|
- } finally {
|
|
|
+ }
|
|
|
+ finally {
|
|
|
pavilionLoading.value = false
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 加载艺术家选项
|
|
|
-const loadArtistOptions = async (name = '') => {
|
|
|
+async function loadArtistOptions(name = '') {
|
|
|
artistLoading.value = true
|
|
|
try {
|
|
|
const params = {
|
|
|
pageSize: 20,
|
|
|
- pageNo: 1
|
|
|
+ pageNo: 1,
|
|
|
}
|
|
|
if (name) {
|
|
|
params.name = name
|
|
|
@@ -158,10 +158,12 @@ const loadArtistOptions = async (name = '') => {
|
|
|
}
|
|
|
const { data } = await artistApi.read(params)
|
|
|
artistOptions.value = data.pageData || []
|
|
|
- } catch (error) {
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
console.error('加载艺术家列表失败:', error)
|
|
|
artistOptions.value = []
|
|
|
- } finally {
|
|
|
+ }
|
|
|
+ finally {
|
|
|
artistLoading.value = false
|
|
|
}
|
|
|
}
|
|
|
@@ -176,7 +178,7 @@ function initMap() {
|
|
|
console.error('腾讯地图SDK未加载')
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 等待DOM完全渲染后再初始化地图
|
|
|
setTimeout(() => {
|
|
|
const mapContainer = document.getElementById('mapContainer')
|
|
|
@@ -197,7 +199,8 @@ function initMap() {
|
|
|
if (map) {
|
|
|
try {
|
|
|
map.destroy()
|
|
|
- } catch (error) {
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
console.warn('销毁旧地图实例失败:', error)
|
|
|
}
|
|
|
map = null
|
|
|
@@ -236,12 +239,13 @@ function initMap() {
|
|
|
|
|
|
// 在地图上标记位置
|
|
|
function markLocationOnMap(location, address, locationData) {
|
|
|
- if (!map || !location) return
|
|
|
-
|
|
|
+ if (!map || !location)
|
|
|
+ return
|
|
|
+
|
|
|
try {
|
|
|
// 更新标记位置
|
|
|
const position = new TMap.LatLng(location.lat, location.lng)
|
|
|
-
|
|
|
+
|
|
|
// 更新地图中心和缩放级别
|
|
|
if (map) {
|
|
|
map.setCenter(position)
|
|
|
@@ -253,51 +257,55 @@ function markLocationOnMap(location, address, locationData) {
|
|
|
marker.updateGeometries([{
|
|
|
id: 'marker1',
|
|
|
styleId: 'marker',
|
|
|
- position: position,
|
|
|
+ position,
|
|
|
}])
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 存储经纬度信息到modalForm
|
|
|
modalForm.value.latitude = location.lat
|
|
|
modalForm.value.longitude = location.lng
|
|
|
modalForm.value.city = locationData.city
|
|
|
-
|
|
|
+
|
|
|
console.log('地图标记成功:', { lat: location.lat, lng: location.lng, city: modalForm.value.city })
|
|
|
- } catch (error) {
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
console.error('地图标记失败:', error)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 处理地址选择 - 地图定位逻辑
|
|
|
-const handleAddressSelect = (locationData) => {
|
|
|
+function handleAddressSelect(locationData) {
|
|
|
console.log('选择的地址:', locationData)
|
|
|
-
|
|
|
+
|
|
|
if (locationData.location) {
|
|
|
// 在地图上标记位置
|
|
|
markLocationOnMap(locationData.location, locationData.address, locationData)
|
|
|
- } else if (locationData.address) {
|
|
|
+ }
|
|
|
+ else if (locationData.address) {
|
|
|
// 检查是否有传递过来的经纬度信息(编辑模式回显)
|
|
|
if (locationData.latitude && locationData.longitude) {
|
|
|
// 使用传递过来的经纬度进行地图定位
|
|
|
const savedLocation = {
|
|
|
lat: locationData.latitude,
|
|
|
- lng: locationData.longitude
|
|
|
+ lng: locationData.longitude,
|
|
|
}
|
|
|
markLocationOnMap(savedLocation, locationData.address, {
|
|
|
city: locationData.city,
|
|
|
- address: locationData.address
|
|
|
+ address: locationData.address,
|
|
|
})
|
|
|
- } else if (modalForm.value.latitude && modalForm.value.longitude) {
|
|
|
+ }
|
|
|
+ else if (modalForm.value.latitude && modalForm.value.longitude) {
|
|
|
// 使用表单中已保存的经纬度进行地图定位
|
|
|
const savedLocation = {
|
|
|
lat: modalForm.value.latitude,
|
|
|
- lng: modalForm.value.longitude
|
|
|
+ lng: modalForm.value.longitude,
|
|
|
}
|
|
|
markLocationOnMap(savedLocation, locationData.address, {
|
|
|
city: modalForm.value.city,
|
|
|
- address: locationData.address
|
|
|
+ address: locationData.address,
|
|
|
})
|
|
|
- } else {
|
|
|
+ }
|
|
|
+ else {
|
|
|
// 如果没有坐标,可以进行地址解析
|
|
|
console.log('需要进行地址解析:', locationData.address)
|
|
|
}
|
|
|
@@ -328,7 +336,7 @@ function loadTencentMapSDK() {
|
|
|
resolve()
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 创建script标签加载SDK
|
|
|
const script = document.createElement('script')
|
|
|
script.src = 'https://map.qq.com/api/gljs?v=1.exp&key=YCABZ-AFPRX-VD54O-TL3VN-TL7A3-KPBQJ'
|
|
|
@@ -359,35 +367,38 @@ async function handleUpload(options, type) {
|
|
|
try {
|
|
|
const formData = new FormData()
|
|
|
formData.append('file', file.file)
|
|
|
-
|
|
|
+
|
|
|
const response = await request.post('/file/upload', formData, {
|
|
|
headers: {
|
|
|
- 'Content-Type': 'multipart/form-data'
|
|
|
- }
|
|
|
+ 'Content-Type': 'multipart/form-data',
|
|
|
+ },
|
|
|
})
|
|
|
-
|
|
|
+
|
|
|
if (response.data) {
|
|
|
// 更新对应类型的图片列表
|
|
|
imgList.value[type] = [{
|
|
|
url: response.data,
|
|
|
- remoteUrl: response.data
|
|
|
+ remoteUrl: response.data,
|
|
|
}]
|
|
|
-
|
|
|
+
|
|
|
// 更新表单数据
|
|
|
if (type === 'cover') {
|
|
|
modalForm.value.coverImageUrl = response.data
|
|
|
- } else if (type === 'share') {
|
|
|
+ }
|
|
|
+ else if (type === 'share') {
|
|
|
modalForm.value.shareImageUrl = response.data
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
$message.success('图片上传成功')
|
|
|
options.onFinish()
|
|
|
- } else {
|
|
|
+ }
|
|
|
+ else {
|
|
|
throw new Error('上传响应格式错误')
|
|
|
}
|
|
|
- } catch (error) {
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
console.error('图片上传失败', error)
|
|
|
- $message.error('图片上传失败:' + (error.message || '未知错误'))
|
|
|
+ $message.error(`图片上传失败:${error.message || '未知错误'}`)
|
|
|
options.onError()
|
|
|
}
|
|
|
}
|
|
|
@@ -398,7 +409,7 @@ onMounted(async () => {
|
|
|
loadPavilionOptions()
|
|
|
// 初始加载艺术家选项
|
|
|
loadArtistOptions()
|
|
|
-
|
|
|
+
|
|
|
// 加载腾讯地图SDK并初始化地图
|
|
|
try {
|
|
|
await loadTencentMapSDK()
|
|
|
@@ -406,7 +417,8 @@ onMounted(async () => {
|
|
|
// setTimeout(() => {
|
|
|
// initMap()
|
|
|
// }, 500)
|
|
|
- } catch (error) {
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
console.error('地图SDK加载失败:', error)
|
|
|
}
|
|
|
})
|
|
|
@@ -421,9 +433,9 @@ const {
|
|
|
handleSave,
|
|
|
} = useCrud({
|
|
|
name: '线上展会',
|
|
|
- initForm: {
|
|
|
- display: true,
|
|
|
- setTop: false,
|
|
|
+ initForm: {
|
|
|
+ display: true,
|
|
|
+ setTop: false,
|
|
|
isBomb: false,
|
|
|
hot: false,
|
|
|
dateRange: null,
|
|
|
@@ -437,7 +449,7 @@ const {
|
|
|
shareImageUrl: '',
|
|
|
latitude: 0,
|
|
|
longitude: 0,
|
|
|
- city: ''
|
|
|
+ city: '',
|
|
|
},
|
|
|
doCreate: (formData) => {
|
|
|
// 格式化展览日期
|
|
|
@@ -447,7 +459,7 @@ const {
|
|
|
const endDate = new Date(formData.dateRange[1]).toISOString().split('T')[0]
|
|
|
openTime = `${startDate} - ${endDate}`
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const apiData = {
|
|
|
address: formData.address || '',
|
|
|
artistIdList: Array.isArray(formData.artistId) ? formData.artistId : (formData.artistId ? [formData.artistId] : []),
|
|
|
@@ -458,12 +470,12 @@ const {
|
|
|
longitude: formData.longitude || 0,
|
|
|
name: formData.name || '',
|
|
|
online: 0,
|
|
|
- openTime: openTime,
|
|
|
+ openTime,
|
|
|
openTimeDetail: formData.openTimeDetail || '',
|
|
|
pavilionId: formData.pavilionId || 0,
|
|
|
setHot: formData.hot ? 1 : 0,
|
|
|
setTop: formData.setTop ? 'A' : 'I',
|
|
|
- statusText: formData.statusText || ''
|
|
|
+ statusText: formData.statusText || '',
|
|
|
}
|
|
|
return api.create(apiData)
|
|
|
},
|
|
|
@@ -476,7 +488,7 @@ const {
|
|
|
const endDate = new Date(formData.dateRange[1]).toISOString().split('T')[0]
|
|
|
openTime = `${startDate} - ${endDate}`
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const apiData = {
|
|
|
id: formData.id,
|
|
|
address: formData.address || '',
|
|
|
@@ -488,12 +500,12 @@ const {
|
|
|
longitude: formData.longitude || 0,
|
|
|
name: formData.name || '',
|
|
|
online: 0,
|
|
|
- openTime: openTime,
|
|
|
+ openTime,
|
|
|
openTimeDetail: formData.openTimeDetail || '',
|
|
|
pavilionId: formData.pavilionId || 0,
|
|
|
setHot: formData.hot ? 1 : 0,
|
|
|
setTop: formData.setTop ? 'A' : 'I',
|
|
|
- statusText: formData.statusText || ''
|
|
|
+ statusText: formData.statusText || '',
|
|
|
}
|
|
|
return api.update(apiData)
|
|
|
},
|
|
|
@@ -504,8 +516,8 @@ const {
|
|
|
function handleAdd() {
|
|
|
// 重置表单数据
|
|
|
Object.assign(modalForm, {
|
|
|
- display: true,
|
|
|
- setTop: false,
|
|
|
+ display: true,
|
|
|
+ setTop: false,
|
|
|
isBomb: false,
|
|
|
hot: false,
|
|
|
dateRange: null,
|
|
|
@@ -521,25 +533,25 @@ function handleAdd() {
|
|
|
pavilionId: null,
|
|
|
latitude: 0,
|
|
|
longitude: 0,
|
|
|
- city: ''
|
|
|
+ city: '',
|
|
|
})
|
|
|
-
|
|
|
+
|
|
|
// 设置为新增模式
|
|
|
modalAction.value = 'add'
|
|
|
-
|
|
|
+
|
|
|
// 清空图片列表
|
|
|
imgList.value = {
|
|
|
cover: [],
|
|
|
- share: []
|
|
|
+ share: [],
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 重新加载选项
|
|
|
loadPavilionOptions()
|
|
|
loadArtistOptions()
|
|
|
-
|
|
|
+
|
|
|
// 切换到表单视图
|
|
|
showAddEditForm.value = true
|
|
|
-
|
|
|
+
|
|
|
// 延迟初始化地图,确保DOM已渲染
|
|
|
setTimeout(() => {
|
|
|
initMap()
|
|
|
@@ -565,21 +577,21 @@ async function handleOpen(options) {
|
|
|
// 重新加载选项
|
|
|
loadPavilionOptions()
|
|
|
loadArtistOptions()
|
|
|
-
|
|
|
+
|
|
|
// 切换到表单视图
|
|
|
showAddEditForm.value = true
|
|
|
-
|
|
|
+
|
|
|
// 延迟初始化地图,确保弹窗DOM已渲染
|
|
|
setTimeout(() => {
|
|
|
initMap()
|
|
|
}, 800)
|
|
|
-
|
|
|
+
|
|
|
// 清空图片列表
|
|
|
imgList.value = {
|
|
|
cover: [],
|
|
|
- share: []
|
|
|
+ share: [],
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 如果是编辑模式,调用接口获取展会详情
|
|
|
if (options.action === 'edit' && options.row && options.row.id) {
|
|
|
try {
|
|
|
@@ -589,10 +601,10 @@ async function handleOpen(options) {
|
|
|
if (detailData.imageUrl) {
|
|
|
imgList.value.cover = [{
|
|
|
url: detailData.imageUrl,
|
|
|
- remoteUrl: detailData.imageUrl
|
|
|
+ remoteUrl: detailData.imageUrl,
|
|
|
}]
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 如果详情中有pavilion信息,将其合并到展馆选项列表中
|
|
|
let pavilionId = ''
|
|
|
if (detailData.pavilion) {
|
|
|
@@ -602,11 +614,11 @@ async function handleOpen(options) {
|
|
|
}
|
|
|
pavilionId = detailData.pavilion.id
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 如果详情中有artistList信息,将其合并到艺术家选项列表中
|
|
|
let artistId = []
|
|
|
if (detailData.artistList && Array.isArray(detailData.artistList)) {
|
|
|
- detailData.artistList.forEach(artist => {
|
|
|
+ detailData.artistList.forEach((artist) => {
|
|
|
const existingArtist = artistOptions.value.find(a => a.id === artist.id)
|
|
|
if (!existingArtist) {
|
|
|
artistOptions.value.unshift(artist)
|
|
|
@@ -614,7 +626,7 @@ async function handleOpen(options) {
|
|
|
})
|
|
|
artistId = detailData.artistList.map(artist => artist.id)
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 处理日期范围回显
|
|
|
let dateRange = null
|
|
|
if (detailData.openTime && typeof detailData.openTime === 'string') {
|
|
|
@@ -627,18 +639,18 @@ async function handleOpen(options) {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 更新options中的row数据为接口返回的完整数据
|
|
|
options.row = {
|
|
|
...options.row,
|
|
|
id: detailData.id,
|
|
|
name: detailData.name || '',
|
|
|
- dateRange: dateRange,
|
|
|
+ dateRange,
|
|
|
startTime: detailData.startTime || null,
|
|
|
endTime: detailData.endTime || null,
|
|
|
statusText: detailData.statusText || '',
|
|
|
- pavilionId: pavilionId,
|
|
|
- artistId: artistId,
|
|
|
+ pavilionId,
|
|
|
+ artistId,
|
|
|
address: detailData.address || '',
|
|
|
description: detailData.description || '',
|
|
|
coverImageUrl: detailData.imageUrl || '',
|
|
|
@@ -649,15 +661,16 @@ async function handleOpen(options) {
|
|
|
hot: detailData.hot === 1,
|
|
|
latitude: detailData.latitude || 0,
|
|
|
longitude: detailData.longitude || 0,
|
|
|
- city: detailData.city || ''
|
|
|
+ city: detailData.city || '',
|
|
|
}
|
|
|
}
|
|
|
- } catch (error) {
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
console.error('获取展会详情失败:', error)
|
|
|
$message.error('获取展会详情失败')
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 调用原始的handleOpen方法
|
|
|
originalHandleOpen(options)
|
|
|
}
|
|
|
@@ -694,7 +707,7 @@ const columns = [
|
|
|
{
|
|
|
title: '展会状态',
|
|
|
key: 'statusText',
|
|
|
- width: 100
|
|
|
+ width: 100,
|
|
|
},
|
|
|
{
|
|
|
title: '展览时间',
|
|
|
@@ -758,7 +771,7 @@ const columns = [
|
|
|
async function handleOnline(row) {
|
|
|
row.onlineLoading = true
|
|
|
try {
|
|
|
- await api.update({ id: row.id})
|
|
|
+ await api.update({ id: row.id })
|
|
|
row.onlineLoading = false
|
|
|
$message.success('操作成功')
|
|
|
$table.value?.handleSearch()
|