From a403393c1190994b473e679e1751794d9a1b9502 Mon Sep 17 00:00:00 2001
From: cloudroam <cloudroam>
Date: 星期二, 01 七月 2025 10:36:07 +0800
Subject: [PATCH] add: 分享+景点管理

---
 components/card/localtion-card.vue  |   95 +++++++
 pages/home/home.vue                 |    2 
 static/common/parking.png           |    0 
 static/common/wechat.png            |    0 
 components/title/section-title.vue  |    1 
 pages/home/home-main.vue            |   65 ++++
 pages.json                          |   26 +
 static/common/visit.png             |    0 
 sub-pages/hot-spot/index.vue        |  100 +++++++
 components/share-popup.vue          |   46 +++
 types/index.ts                      |   51 +++
 sub-pages/film-list/film-detail.vue |   10 
 sub-pages/film-list/film-list.vue   |    1 
 static/common/link.png              |    0 
 sub-pages/hot-spot/spot-detail.vue  |  366 ++++++++++++++++++++++++++++
 sub-pages/utils/api.ts              |   15 +
 static/common/wechat-moments.png    |    0 
 17 files changed, 755 insertions(+), 23 deletions(-)

diff --git a/components/card/localtion-card.vue b/components/card/localtion-card.vue
new file mode 100644
index 0000000..32a9584
--- /dev/null
+++ b/components/card/localtion-card.vue
@@ -0,0 +1,95 @@
+<template>
+    <view class="card" @click="handleClick(item)">
+        <view class="image-wrapper">
+            <image :src="item.locationUrl" mode="widthFix" class="card-image" />
+        </view>
+        <view class="card-title">
+            <up-text :lines="2" size="14px" :text="item.locationName" bold></up-text>
+        </view>
+        <view class="card-footer">
+            <view class="user-info">
+                <view class="user-text">
+                    <text class="nickname">{{ item.address }}</text>
+                </view>
+            </view>
+            <view class="opera-info">
+                <up-icon name="heart" size="30rpx" color="#999" />
+                <text>{{ item.locationWeight }}</text>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script setup lang="ts">
+defineProps<{
+    item: any
+}>()
+
+const emit = defineEmits(['click'])
+const handleClick = (item: any) => {
+    emit('click', item)
+}
+</script>
+
+<style scoped lang="scss">
+.card {
+    border-radius: 10rpx;
+    background-color: #ffffff;
+    font-size: 14px;
+    line-height: 20px;
+    color: rgb(51, 51, 51);
+    margin: 10rpx;
+
+    .image-wrapper {
+        width: 100%;
+        position: relative;
+        display: inline-block;
+
+        .card-image {
+            width: 100%;
+            border-radius: inherit;
+        }
+
+    }
+
+
+    .card-title {
+        padding: 10rpx;
+        font-weight: 500;
+    }
+
+    .card-footer {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 10rpx;
+    }
+
+    .user-info {
+        display: flex;
+        align-items: center;
+
+        .user-text {
+            font-size: 18rpx;
+            line-height: 14px;
+            margin-left: 10rpx;
+
+            .nickname {
+                font-weight: bold;
+                display: block;
+                color: #646464;
+            }
+        }
+    }
+
+    .opera-info {
+        display: flex;
+        align-items: center;
+
+        text {
+            margin-left: 10rpx;
+            font-size: 12px;
+        }
+    }
+}
+</style>
\ No newline at end of file
diff --git a/components/share-popup.vue b/components/share-popup.vue
index 9cd2ef9..f316f4d 100644
--- a/components/share-popup.vue
+++ b/components/share-popup.vue
@@ -4,14 +4,16 @@
     <view class="share-content">
       <view class="share-title">分享到</view>
       <view class="share-options">
-        <button class="share-item" open-type="share" @click="handleShare('wechat')">
+        <!-- 分享给好友 -->
+        <button class="share-item" open-type="share">
           <image src="/static/common/wechat.png" class="share-icon" />
           <text>微信好友</text>
         </button>
-        <button class="share-item" open-type="share" @click="handleShare('moments')">
+        <!-- 分享到朋友圈 -->
+        <view class="share-item" @click="handleShareTimeline">
           <image src="/static/common/wechat-moments.png" class="share-icon" />
           <text>朋友圈</text>
-        </button>
+        </view>
         <view class="share-item" @click="handleCopyLink">
           <image src="/static/common/link.png" class="share-icon" />
           <text>复制链接</text>
@@ -48,10 +50,40 @@
   emit('update:show', false)
 }
 
-// 处理分享
-const handleShare = (type: 'wechat' | 'moments') => {
-  // 小程序分享通过页面配置和按钮的 open-type="share" 实现
-  // 分享内容在页面的 onShareAppMessage 中配置
+// 处理分享到朋友圈
+const handleShareTimeline = () => {
+  // #ifdef MP-WEIXIN
+  // 使用微信原生朋友圈分享
+  wx.openChannelsActivity({
+    finderUserName: '', // 视频号用户名,可选
+    success: (res) => {
+      console.log('打开朋友圈成功', res)
+      uni.showToast({
+        title: '已打开朋友圈',
+        icon: 'success'
+      })
+    },
+    fail: (err) => {
+      console.error('打开朋友圈失败', err)
+      // 如果打开失败,尝试使用小程序分享
+      uni.showModal({
+        title: '提示',
+        content: '无法直接打开朋友圈,请点击右上角分享到朋友圈',
+        showCancel: false
+      })
+    }
+  })
+  // #endif
+  
+  // #ifndef MP-WEIXIN
+  // 非微信小程序环境
+  uni.showToast({
+    title: '请点击右上角分享到朋友圈',
+    icon: 'none',
+    duration: 2000
+  })
+  // #endif
+  
   closePopup()
 }
 
diff --git a/components/title/section-title.vue b/components/title/section-title.vue
index d3fbf41..8e101ac 100644
--- a/components/title/section-title.vue
+++ b/components/title/section-title.vue
@@ -53,6 +53,7 @@
 })
 
 function go(url: string) {
+  console.log("url",url)
     if (url) {
         uni.navigateTo({
             url
diff --git a/pages.json b/pages.json
index 1ebaad6..6a19d6c 100644
--- a/pages.json
+++ b/pages.json
@@ -52,7 +52,9 @@
 					"path": "film-detail",
 					"style": {
 						"navigationBarTitleText": "",
-						"enablePullDownRefresh": true
+						"enablePullDownRefresh": true,
+						"enableShareTimeline": true,
+						"enableShareAppMessage": true
 					}
 				}
 				,{
@@ -122,6 +124,28 @@
 				}
 			]
 		}
+		,{
+			"root": "sub-pages/hot-spot",
+			"pages": [
+				{
+					"path": "index",
+					"style": {
+						"navigationBarTitleText": "场景博物馆",
+						"enablePullDownRefresh": true
+					}
+				}
+				,{
+					"path": "spot-detail",
+					"style": {
+						"navigationBarTitleText": "",
+						"enablePullDownRefresh": true,
+						"enableShareTimeline": true,
+						"enableShareAppMessage": true
+					}
+				}
+			]
+		}
+
 	],
 	"globalStyle": {
 		"navigationBarTextStyle": "black",
diff --git a/pages/home/home-main.vue b/pages/home/home-main.vue
index 3e545c6..e809a9b 100644
--- a/pages/home/home-main.vue
+++ b/pages/home/home-main.vue
@@ -2,23 +2,26 @@
     <view>
       <!-- <view class="card" v-if="!showVideo"> -->
         <view class="card">
-        <view class="main-title">每一帧画面,都藏着一个等待探索的世界</view>
+        <view class="main-title">{{ config.mainTitle }}</view>
         <view class="sub-title">
-          从经典场景到幕后故事,开启你的专属影视朝圣之旅
+          {{ config.subTitle }}
         </view>
   
         <view class="btn-group">
           <view class="custom-btn explore-btn" @click="startExplore">
-            <text class="btn-text">开始探索</text>
-            <up-icon name="play-right-fill" size="34rpx" color="black" class="btn-icon" />
+            <text class="btn-text">{{ config.btnText1 }}</text>
+            <up-icon
+                :name="config.btnIcon1"
+                :color="config.iconColor1"
+                size="34rpx" class="btn-icon" />
           </view>
   
           <view class="custom-btn route-btn" @click="hotRoute">
-            <text class="btn-text">热门路线</text>
+            <text class="btn-text">{{ config.btnText2 }}</text>
             <up-icon
-              name="/static/common/road-map-fill.png"
+                :name="config.btnIcon2"
+                :color="config.iconColor2"
               size="34rpx"
-              color="white"
               class="btn-icon"
             />
           </view>
@@ -54,7 +57,9 @@
   
   <script setup lang="ts">
   import { ref, computed } from 'vue'
-  
+  import { onLoad } from '@dcloudio/uni-app'
+  import { useGlobal } from '@/composables/useGlobal'
+  const { $http, $message, $store } = useGlobal()
   const showVideo = ref(false)
   const currentIndex = ref(0)
   
@@ -70,7 +75,17 @@
   ]
   
   const currentVideo = computed(() => videoList[currentIndex.value])
-  
+  // 添加配置响应式对象
+  const config = ref({
+    mainTitle: '每一帧画面,都藏着一个等待探索的世界',
+    subTitle: '从经典场景到幕后故事,开启你的专属影视朝圣之旅',
+    btnText1: '开始探索',
+    btnText2: '热门路线',
+    btnIcon1: 'play-right-fill',
+    btnIcon2: '/static/common/road-map-fill.png',
+    iconColor1: 'black',
+    iconColor2: 'white'
+  })
   function startExplore() {
     // 跳转到具体页面
     uni.navigateTo({
@@ -89,6 +104,38 @@
       icon: 'none',
     })
   }
+
+  onLoad((options: any) => {
+    getHomeConfig()
+  })
+
+  // 获取首页配置信息
+  const getHomeConfig = async () => {
+    try {
+      const { code, data } = await $http.request(
+          'get',
+          '/api/home/homeConfig/info',
+          {}
+      )
+      if (code == 0) {
+        console.log("接口返回数据:", data);
+        // 只更新接口返回的有效字段
+        Object.keys(config.value).forEach(key => {
+          const newValue = data[key];
+          console.log(`字段 ${key}: 接口值=${newValue}, 本地值=${config.value[key]}`);
+          if (newValue !== undefined && newValue !== null && newValue !== '') {
+            config.value[key] = newValue;
+          }
+        });
+        console.log("首页配置加载成功", config.value)
+      } else {
+      }
+    } catch (error) {
+      console.error('配置请求失败', error)
+    }
+  }
+
+
   </script>
   <style scoped>
   .card {
diff --git a/pages/home/home.vue b/pages/home/home.vue
index a1f260e..7095440 100644
--- a/pages/home/home.vue
+++ b/pages/home/home.vue
@@ -35,7 +35,7 @@
       <SectionTitle title="全球影视地标" optitle="查看全部" goUrl="/pages/home/home-more" />
       <GlobalGeo />
 
-      <SectionTitle title="场景博物馆" optitle="查看全部" goUrl="/pages/home/home-more" />
+      <SectionTitle title="场景博物馆" optitle="查看全部" goUrl="/sub-pages/hot-spot/index" />
       <SceneMuseumCard v-for="(item, index) in cardList" :key="index" :image="item.image" :title="item.title"
         :subtitle="item.subtitle" :readTime="item.readTime" />
 
diff --git a/static/common/link.png b/static/common/link.png
new file mode 100644
index 0000000..a08c7a1
--- /dev/null
+++ b/static/common/link.png
Binary files differ
diff --git a/static/common/parking.png b/static/common/parking.png
new file mode 100644
index 0000000..1425c94
--- /dev/null
+++ b/static/common/parking.png
Binary files differ
diff --git a/static/common/visit.png b/static/common/visit.png
new file mode 100644
index 0000000..614b53b
--- /dev/null
+++ b/static/common/visit.png
Binary files differ
diff --git a/static/common/wechat-moments.png b/static/common/wechat-moments.png
new file mode 100644
index 0000000..cb131fb
--- /dev/null
+++ b/static/common/wechat-moments.png
Binary files differ
diff --git a/static/common/wechat.png b/static/common/wechat.png
new file mode 100644
index 0000000..68bbedb
--- /dev/null
+++ b/static/common/wechat.png
Binary files differ
diff --git a/sub-pages/film-list/film-detail.vue b/sub-pages/film-list/film-detail.vue
index 3d89490..2d73253 100644
--- a/sub-pages/film-list/film-detail.vue
+++ b/sub-pages/film-list/film-detail.vue
@@ -183,7 +183,8 @@
     title: filmInfo.value?.coverTitle || '分享内容',
     desc: filmInfo.value?.filmContent?.substring(0, 50) || '',
     image: filmPictureList.value[0] || '',
-    url: `https://您的域名/sub-pages/film-list/film-detail?id=${filmInfo.value?.id}`
+    // url: `http://您的域名/sub-pages/film-list/film-detail?id=${filmInfo.value?.id}`
+    url: `/sub-pages/film-list/film-detail?id=${filmInfo.value?.id}`
   }
   showSharePopup.value = true
 }
@@ -193,7 +194,9 @@
 const copyLink = () => {
   sharePopupShow.value = false
   uni.setClipboardData({
-    data: `https://你的域名/sub-pages/film-list/film-detail?id=${filmInfo.value?.id}`,
+    // data: `http://14.103.144.28/sub-pages/film-list/film-detail?id=${filmInfo.value?.id}`,
+    // 此处应该是一个小程序的外链
+    data: `http://14.103.144.28/sub-pages/film-list/film-detail?id=${filmInfo.value?.id}`,
     success: () => $message.showToast('链接已复制')
   })
 }
@@ -426,7 +429,8 @@
     return {
       title: filmInfo.value?.coverTitle || '分享内容',
       query: `id=${filmInfo.value?.id}`,
-      imageUrl: filmPictureList.value[0] || ''
+      imageUrl: filmPictureList.value[0] || '',
+      desc: filmInfo.value?.filmContent?.substring(0, 50) || ''
     }
   }
 })
diff --git a/sub-pages/film-list/film-list.vue b/sub-pages/film-list/film-list.vue
index 3987c0d..3257cd1 100644
--- a/sub-pages/film-list/film-list.vue
+++ b/sub-pages/film-list/film-list.vue
@@ -119,6 +119,7 @@
     type: '',
     current: filmPage.value,
     size: filmSize,
+	status: 'published',
 	keywords: keywords.value // 关键修改
   };
   const records = await getFilmWorksBase(query)
diff --git a/sub-pages/hot-spot/index.vue b/sub-pages/hot-spot/index.vue
new file mode 100644
index 0000000..b6ac4cf
--- /dev/null
+++ b/sub-pages/hot-spot/index.vue
@@ -0,0 +1,100 @@
+<template>
+  <view :class="['app', theme]">
+    <view style="padding-top: 10rpx;">
+      <up-search height="70rpx" placeholder="搜索" search-icon-size="40rpx" margin="0rpx 30rpx" v-model="search"
+                 :show-action="true" @search="onSearch" />
+    </view>
+    <view class="list-view">
+      <up-waterfall v-model="films">
+        <template #left="{ leftList }">
+          <LocaltionCard v-for="(item, index) in leftList" :key="index" :item="item" @click="handleDetailClick(item)" />
+        </template>
+        <template #right="{ rightList }">
+          <LocaltionCard v-for="(item, index) in rightList" :key="index" :item="item" @click="handleDetailClick(item)" />
+        </template>
+      </up-waterfall>
+      <up-loadmore :status="filmStatus" :line="true" />
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import { onLoad, onShow, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
+import { FilmLocationVO, FilmLocationQueryDTO } from '@/types/index'
+import { useGlobal } from '@/composables/useGlobal'
+const { $http, $message } = useGlobal()
+import { useNavigator } from '@/composables/useNavigator'
+const { navigateTo } = useNavigator()
+import { getFilmLocationBase } from '@/sub-pages/utils/api'
+import LocaltionCard from "../../components/card/localtion-card.vue";
+
+const theme = ref('light')
+const search = ref('')
+const films = ref<FilmLocationVO[]>([])
+const filmPage = ref(1)
+const filmSize = 10
+const filmStatus = ref('loading')
+
+const onSearch = () => {
+  filmPage.value = 1
+  films.value = []
+  filmStatus.value = 'loadmore'
+  getFilmLocation(search.value)
+}
+
+const handleDetailClick = (item: FilmLocationVO) => {
+  const url = `/sub-pages/hot-spot/spot-detail?id=${item.id}`
+  navigateTo(url)
+}
+
+onLoad(() => {
+  const storedTheme = uni.getStorageSync('theme') || 'light'
+  theme.value = storedTheme
+})
+
+onShow(() => {
+  getFilmLocation()
+})
+
+onPullDownRefresh(async () => {
+  filmPage.value = 1
+  getFilmLocation()
+  uni.stopPullDownRefresh()
+})
+
+onReachBottom(() => {
+  getFilmLocation()
+})
+
+const getFilmLocation = async (keyword = '') => {
+  if (filmStatus.value === 'nomore') return
+  filmStatus.value = 'loading'
+
+  try {
+  const query: FilmLocationQueryDTO = {
+    locationName: keyword,
+    current: filmPage.value,
+    size: filmSize,
+    isEnabled: 1
+  }
+  const records = await getFilmLocationBase(query)
+  if (records && records.length > 0) {
+    const existingIds = new Set(films.value.map(item => item.id))
+    const uniqueRecords = records.filter(item => !existingIds.has(item.id))
+    films.value = [...films.value, ...uniqueRecords]
+    if (records.length < filmSize) {
+      filmStatus.value = 'noMore'
+    }
+    filmPage.value++
+  } else {
+    filmStatus.value = 'noMore'
+  }
+  } catch (e) {
+    filmStatus.value = 'loadmore' // 重置状态允许重试
+    $message.error('加载失败,请重试')
+  }
+}
+</script>
+
+<style lang="scss" scoped></style>
\ No newline at end of file
diff --git a/sub-pages/hot-spot/spot-detail.vue b/sub-pages/hot-spot/spot-detail.vue
new file mode 100644
index 0000000..ff20930
--- /dev/null
+++ b/sub-pages/hot-spot/spot-detail.vue
@@ -0,0 +1,366 @@
+<template>
+  <view class="spot-detail-page">
+    <!-- 顶部信息 -->
+    <view class="header">
+      <view class="header-info">
+        <view class="geo-row">
+          <view class="geo-path">{{ geoPath }}</view>
+          <image src="/static/common/earth.png" class="earth-icon" />
+        </view>
+        <view class="title-row">
+          <view class="title">{{ location?.locationName || '景点详情' }}</view>
+          <up-icon name="heart" size="40rpx" :color="collected ? '#FF4D4F' : '#ccc'" @click="toggleCollect" />
+        </view>
+      </view>
+    </view>
+
+    <!-- 主图 -->
+    <view class="main-image">
+      <image :src="mainImage" mode="aspectFill" class="main-img" />
+    </view>
+
+    <!-- 地址、类型、电话、身份准入等 -->
+    <view class="info-list">
+      <view class="info-item">{{ location?.address }}</view>
+      <view class="info-item"><up-icon name="/static/common/marker.png" size="32rpx" /> {{  location?.sceneType || '类型未知' }}</view>
+      <view class="info-item"><up-icon name="/static/common/parking.png" size="32rpx" /> {{ location?.parkingInfo || '无电话' }}</view>
+      <view class="info-item"><up-icon name="/static/common/visit.png" size="32rpx" />  {{ location?.isOpenVisitStr === '是' ? '允许参观' : '不允许参观' }}</view>
+    </view>
+
+    <!-- 介绍描述 -->
+    <view class="divider-line"></view>
+    <view class="desc-block">
+      <text>{{ location?.landmarkDesc || location?.visitInfo || '暂无介绍' }}</text>
+    </view>
+
+    <!-- 地图 -->
+    <view class="map-block" v-if="showMap">
+      <!-- 腾讯地图小程序组件(uni-app中需配置腾讯地图key) -->
+      <map
+          :latitude="location?.gpsLat"
+          :longitude="location?.gpsLng"
+          :markers="mapMarkers"
+          style="width: 100%; height: 300rpx;"
+          v-if="location?.gpsLat && location?.gpsLng"
+      />
+      <image v-else src="/static/common/geo-earth.png" class="map-placeholder" />
+    </view>
+
+    <!-- 实景图 -->
+    <view class="scene-block" v-if="sceneImages.length">
+      <view class="scene-title">实景图 <text class="scene-count">{{ sceneImages.length }}</text></view>
+      <view class="scene-list">
+        <image
+            v-for="(img, idx) in sceneImages"
+            :key="idx"
+            :src="img"
+            mode="aspectFill"
+            class="scene-image"
+            @tap="previewScene(idx)"
+        />
+      </view>
+    </view>
+
+    <!-- 关联影视作品 -->
+    <view class="film-block" v-if="relatedFilms.length">
+      <view class="film-title">关联影视作品</view>
+      <view class="film-list">
+        <view class="film-item" v-for="film in relatedFilms" :key="film.id">
+          <image :src="film.coverUrl" class="film-cover" />
+          <view class="film-info">
+            <view class="film-name">{{ film.nameCn }}</view>
+            <view class="film-en">{{ film.nameEn }}</view>
+            <view class="film-meta">
+              <text>{{ film.typeStr }}</text>
+              <text v-if="film.country"> / {{ film.country }}</text>
+              <text v-if="film.releaseYear"> / {{ film.releaseYear }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import { onLoad } from '@dcloudio/uni-app'
+import { useGlobal } from '@/composables/useGlobal'
+const { $http, $message } = useGlobal()
+import { FilmLocationVO, FilmWorks } from '@/types/index'
+
+const location = ref<FilmLocationVO>()
+const collected = ref(false)
+const geoPath = ref('亚洲-中国-海南省-海口市')
+const mainImage = ref('')
+const sceneImages = ref<string[]>([])
+const relatedFilms = ref<FilmWorks[]>([])
+const showMap = ref(true)
+
+
+onLoad(async (options: any) => {
+  const id = options.id
+  if (id) {
+    await getLocationDetail(id)
+    await getRelatedFilms(id)
+  }
+})
+
+const getLocationDetail = async (id: string) => {
+  const { code, data } = await $http.request('get', '/api/filmLocation/list/view', {
+    params: { id }
+  })
+  if (code === 0) {
+    location.value = data
+    // 主图优先locationUrl,其次visitorPhotos
+    if (data.locationUrl) {
+      mainImage.value = data.locationUrl
+      console.log(data.locationUrl)
+      console.log( mainImage.value)
+    } else if (data.visitorPhotos) {
+      try {
+        const arr = JSON.parse(data.visitorPhotos)
+        mainImage.value = arr[0]?.url || ''
+      } catch {
+        mainImage.value = ''
+      }
+    }
+    // 实景图
+    if (data.visitorPhotos) {
+      try {
+        const arr = JSON.parse(data.visitorPhotos)
+        sceneImages.value = arr.map((item: any) => item.url)
+      } catch {
+        sceneImages.value = []
+      }
+    } else {
+      sceneImages.value = []
+    }
+    // 拼接地理层级
+    geoPath.value = [
+      '亚洲',
+      '中国',
+      data.province,
+      data.city
+    ].filter(Boolean).join('-')
+  } else {
+    $message.showToast('获取景点信息失败')
+  }
+}
+
+// 地图标记
+const mapMarkers = ref([
+  {
+    id: 1,
+    latitude: location.value?.gpsLat,
+    longitude: location.value?.gpsLng,
+    iconPath: '/static/common/marker.png',
+    width: 40,
+    height: 40
+  }
+])
+
+const previewScene = (idx: number) => {
+  uni.previewImage({
+    current: sceneImages.value[idx],
+    urls: sceneImages.value
+  })
+}
+
+// 影视作品接口示例
+const getRelatedFilms = async (locationId: string) => {
+  // 假设接口 /api/filmWorks/related?locationId=xxx
+  const { code, data } = await $http.request('get', '/api/filmLocation/related', {
+    params: { locationId }
+  })
+  if (code === 0 && Array.isArray(data)) {
+    relatedFilms.value = data
+  } else {
+    relatedFilms.value = []
+  }
+}
+
+const toggleCollect = () => {
+  collected.value = !collected.value
+  $message.showToast(collected.value ? '已收藏' : '已取消收藏')
+}
+</script>
+
+<style scoped>
+.spot-detail-page {
+  background: #fff;
+  min-height: 100vh;
+}
+.header {
+  display: flex;
+  align-items: flex-start;
+  padding: 30rpx 20rpx 10rpx 20rpx;
+}
+.header-info {
+  flex: 1;
+  margin-left: 20rpx;
+}
+.geo-path {
+  color: #6c8fc5;
+  font-size: 22rpx;
+  margin-bottom: 10rpx;
+}
+.title-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-top: 30rpx; /* 增加名称与上方的间距 */
+}
+.title {
+  font-size: 36rpx;
+  font-weight: bold;
+  margin-right: 16rpx;
+}
+.earth-icon {
+  width: 144rpx; /* 放大三倍 */
+  height: 144rpx;
+}
+.main-image {
+  width: 100%;
+  height: 320rpx;
+  margin-top: 40rpx; /* 增加名称和图片之间的间隔 */
+  margin-bottom: 40rpx;
+}
+.main-img {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  border-radius: 10rpx;
+}
+/*.info-list {*/
+/*  padding: 0 20rpx;*/
+/*  margin-bottom: 20rpx;*/
+/*}*/
+/*.info-item {*/
+/*  font-size: 28rpx;*/
+/*  color: #333;*/
+/*  margin-bottom: 16rpx;*/
+/*  display: flex;*/
+/*  align-items: center;*/
+/*}*/
+/*.desc-block {*/
+/*  padding: 0 20rpx 30rpx 20rpx;*/
+/*  color: #666;*/
+/*  font-size: 26rpx;*/
+/*}*/
+.info-list {
+  padding: 20rpx; /* 增加内边距 */
+  margin: 20rpx 0; /* 调整上下外边距 */
+  background-color: #f9f9f9;
+  border-radius: 12rpx;
+}
+
+.info-item {
+  font-size: 28rpx;
+  color: #333;
+  margin-bottom: 20rpx; /* 调整间距 */
+  display: flex;
+  align-items: center;
+  line-height: 1.6;
+}
+
+.info-item:last-child {
+  margin-bottom: 0;
+}
+
+/* 添加分割线样式 */
+.divider-line {
+  height: 1px;
+  background-color: #eee;
+  margin: 30rpx 20rpx 20rpx 20rpx;
+}
+
+/* 调整描述区块的间距 */
+.desc-block {
+  padding: 0 20rpx 30rpx 20rpx;
+  color: #666;
+  font-size: 26rpx;
+  line-height: 1.8;
+  margin-top: 20rpx;
+}
+.map-block {
+  margin: 20rpx 0;
+}
+.map-placeholder {
+  width: 100%;
+  height: 300rpx;
+  object-fit: cover;
+  border-radius: 10rpx;
+}
+.scene-block {
+  padding: 0 20rpx 30rpx 20rpx;
+}
+.scene-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  margin-bottom: 10rpx;
+}
+.scene-count {
+  color: #888;
+  font-size: 22rpx;
+  margin-left: 10rpx;
+}
+.scene-list {
+  display: flex;
+  gap: 20rpx;
+  flex-wrap: wrap;
+}
+.scene-image {
+  width: 45vw;
+  height: 140rpx;
+  object-fit: cover;
+  border-radius: 8rpx;
+  margin-bottom: 10rpx;
+}
+.film-block {
+  padding: 0 20rpx 30rpx 20rpx;
+}
+.film-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  margin-bottom: 10rpx;
+}
+.film-list {
+  display: flex;
+  flex-direction: column;
+  gap: 20rpx;
+}
+.film-item {
+  display: flex;
+  align-items: flex-start;
+  gap: 20rpx;
+}
+.film-cover {
+  width: 120rpx;
+  height: 160rpx;
+  object-fit: cover;
+  border-radius: 8rpx;
+}
+.film-info {
+  flex: 1;
+}
+.film-name {
+  font-size: 28rpx;
+  font-weight: bold;
+}
+.film-en {
+  font-size: 22rpx;
+  color: #888;
+  margin-bottom: 6rpx;
+}
+.film-meta {
+  font-size: 22rpx;
+  color: #888;
+}
+.geo-row {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 15rpx;
+}
+</style>
\ No newline at end of file
diff --git a/sub-pages/utils/api.ts b/sub-pages/utils/api.ts
index 4dcc8a9..0316dea 100644
--- a/sub-pages/utils/api.ts
+++ b/sub-pages/utils/api.ts
@@ -83,4 +83,17 @@
         message.showToast('系统异常,无法获取数据');
       return null;
     }
-  }
\ No newline at end of file
+  }
+
+export const getFilmLocationBase = async (query: FilmWorksQueryDTO) => {
+    const { code, data } = await http.request('get', '/api/filmLocation/list', {
+        params: query
+    });
+
+    if (code === 0) {
+        return data.records;
+    } else {
+        message.showToast('系统异常,无法获取数据');
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/types/index.ts b/types/index.ts
index f214c6d..7ba6aaa 100644
--- a/types/index.ts
+++ b/types/index.ts
@@ -304,4 +304,53 @@
     size: number;
     url: string;
     status: string;
-  }
\ No newline at end of file
+  }
+
+
+export interface FilmLocationVO {
+    address?: string,
+    arEntry?: string,
+    checkinCount?: number,
+    city?: string,
+    classicScene?: string,
+    endDate?: string,
+    filmId?: number,
+    gpsLat?: number,
+    gpsLng?: number,
+    id?: number,
+    isEnabled?: boolean,
+    isOpenVisit?: string,
+    isOpenVisitStr?: string,
+    landmarkDesc?: string,
+    locationName?: string,
+    locationUrl?: string,
+    locationWeight?: number,
+    operationWeight?: number,
+    parkingInfo?: string,
+    province?: string,
+    region?: string,
+    sceneType?: string,
+    startDate?: string,
+    surroundingFacilities?: string,
+    transportGuide?: string,
+    visitInfo?: string,
+    visitorPhotos?: string
+}
+
+export interface FilmWorksQueryDTO extends PaginationQuery {
+
+    /** 拍摄地点名称 */
+    locationName?: string;
+
+    /** 所在省 */
+    province?: string;
+
+    /** 所在市 */
+    city?: string;
+
+    /** 所在区 */
+    region?: string;
+
+    /** 启用/禁用(USER_ENABLED_OR_DISABLED) */
+    isEnabled?: boolean;
+}

--
Gitblit v1.9.3