Explorar el Código

Merge branch 'dev' of http://1.94.207.143:3000/xyy/xyy-m into dev

# Conflicts:
#	src/components/Home/Menu.vue
#	src/utils/index.js
songzhen hace 3 meses
padre
commit
5af2aab770
Se han modificado 36 ficheros con 532 adiciones y 164 borrados
  1. BIN
      src/assets/img/empty.png
  2. 1 0
      src/assets/img/note-create/cancel.svg
  3. BIN
      src/assets/img/note-create/create_note_days.png
  4. BIN
      src/assets/img/note-create/create_note_endplace.jpg
  5. BIN
      src/assets/img/note-create/create_note_fee.png
  6. BIN
      src/assets/img/note-create/create_note_insert_img.png
  7. BIN
      src/assets/img/note-create/create_note_insert_title.png
  8. BIN
      src/assets/img/note-create/create_note_relation.png
  9. BIN
      src/assets/img/note-create/create_note_save_tmp.png
  10. BIN
      src/assets/img/note-create/create_note_star.png
  11. BIN
      src/assets/img/note-create/create_note_startdate.png
  12. BIN
      src/assets/img/note-create/create_note_title.png
  13. BIN
      src/assets/img/note-create/create_note_travel_mode.png
  14. BIN
      src/assets/img/note-create/create_note_travel_number.png
  15. BIN
      src/assets/img/note-create/note_create_banner_bg.png
  16. BIN
      src/assets/img/note-create/note_create_bg.png
  17. BIN
      src/assets/img/note-create/note_create_book.png
  18. BIN
      src/assets/img/note-create/note_create_content_bg.png
  19. BIN
      src/assets/img/note-create/note_create_entry.png
  20. BIN
      src/assets/img/note-create/note_create_start_icon.png
  21. BIN
      src/assets/img/note-create/note_create_success.png
  22. BIN
      src/assets/img/note-create/note_draft_cover_bg.jpg
  23. BIN
      src/assets/img/note-create/userinfo_modal.jpg
  24. BIN
      src/assets/img/yuanchuang.png
  25. 39 0
      src/components/Home/Menu.vue
  26. 55 0
      src/components/Profile/Notes/Auditing/Item.vue
  27. 60 0
      src/components/Profile/Notes/Auditing/index.vue
  28. 23 0
      src/components/Profile/Notes/Empty.vue
  29. 53 47
      src/components/Profile/Notes/Published/Item.vue
  30. 48 43
      src/components/Profile/Notes/Published/index.vue
  31. 1 1
      src/components/Profile/Notes/Tabs.vue
  32. 3 2
      src/components/Tabbar/index.vue
  33. 10 0
      src/composables/useLoading.js
  34. 33 16
      src/pages/profile/index/notes.vue
  35. 179 32
      src/pages/travel-notes/index.vue
  36. 27 23
      src/stores/useTabbar.js

BIN
src/assets/img/empty.png


+ 1 - 0
src/assets/img/note-create/cancel.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15" height="15" viewBox="0 0 15 15"><defs><style>.a{fill:#fff;stroke:#707070;}.b{clip-path:url(#a);}.c{fill:#fd9a00;stroke:#fd9a00;}</style><clipPath id="a"><rect class="a" width="15" height="15" transform="translate(193 256)"/></clipPath></defs><g class="b" transform="translate(-193 -256)"><g transform="translate(140.761 226.944)"><path class="c" d="M59.656,108.558a6.659,6.659,0,0,1-5.742-10.024,6.7,6.7,0,0,1,2.4-2.388.291.291,0,0,1,.293.5,6.073,6.073,0,1,0,4.033-.741.292.292,0,1,1,.095-.576,6.657,6.657,0,0,1-1.077,13.227Z" transform="translate(0 -64.992)"/><path class="c" d="M461.86,29.378l-2.448.6a.161.161,0,0,0-.079.267l1.776,1.859a.161.161,0,0,0,.271-.069l.673-2.457a.161.161,0,0,0-.193-.2Z" transform="translate(-400.361)"/></g></g></svg>

BIN
src/assets/img/note-create/create_note_days.png


BIN
src/assets/img/note-create/create_note_endplace.jpg


BIN
src/assets/img/note-create/create_note_fee.png


BIN
src/assets/img/note-create/create_note_insert_img.png


BIN
src/assets/img/note-create/create_note_insert_title.png


BIN
src/assets/img/note-create/create_note_relation.png


BIN
src/assets/img/note-create/create_note_save_tmp.png


BIN
src/assets/img/note-create/create_note_star.png


BIN
src/assets/img/note-create/create_note_startdate.png


BIN
src/assets/img/note-create/create_note_title.png


BIN
src/assets/img/note-create/create_note_travel_mode.png


BIN
src/assets/img/note-create/create_note_travel_number.png


BIN
src/assets/img/note-create/note_create_banner_bg.png


BIN
src/assets/img/note-create/note_create_bg.png


BIN
src/assets/img/note-create/note_create_book.png


BIN
src/assets/img/note-create/note_create_content_bg.png


BIN
src/assets/img/note-create/note_create_entry.png


BIN
src/assets/img/note-create/note_create_start_icon.png


BIN
src/assets/img/note-create/note_create_success.png


BIN
src/assets/img/note-create/note_draft_cover_bg.jpg


BIN
src/assets/img/note-create/userinfo_modal.jpg


BIN
src/assets/img/yuanchuang.png


+ 39 - 0
src/components/Home/Menu.vue

@@ -0,0 +1,39 @@
+<template>
+  <div class="flex item-center justify-around">
+    <NuxtLink
+      v-for="item in menuData"
+      :key="item.title"
+      :to="item.to"
+      class="flex flex-col items-center space-y-5"
+    >
+      <img :src="item.icon" class="w-50 h-50" alt="" srcset="" />
+      <span class="text-sm text-black-3">{{ item.title }}</span>
+    </NuxtLink>
+  </div>
+</template>
+
+<script setup>
+import HomeFoodIcon from '@/assets/img/home_food.png'
+import HomeTravleIcon from '@/assets/img/home_travel.png'
+import HomeLabourIcon from '@/assets/img/home_Labour.png'
+
+const menuData = [
+  {
+    icon: HomeFoodIcon,
+    title: '境外美食',
+    to: '/food'
+  },
+  {
+    icon: HomeTravleIcon,
+    title: '境外旅游',
+    to: '/travel'
+  },
+  {
+    icon: HomeLabourIcon,
+    title: '出国劳务',
+    to: '/labour'
+  }
+]
+</script>
+
+<style lang="scss" scoped></style>

+ 55 - 0
src/components/Profile/Notes/Auditing/Item.vue

@@ -0,0 +1,55 @@
+<template>
+  <div class="group flex space-x-10 p-10 transition-all rounded-xl bg-[#FFF]">
+    <img
+      :src="formatImgSrc(data?.tourismUrlsAfterConvert) || noteDraftCoverBg"
+      class="aspect-[4/3] h-109 shrink-0 object-cover"
+    />
+
+    <div class="flex w-0 flex-1 flex-col justify-between">
+      <div class="mt-18">
+        <div class="line-clamp-1 break-all text-base font-semibold text-black-3">
+          {{ data?.projectTitle || '未命名草稿' }}
+        </div>
+        <div class="mt-2 text-sm text-black-3">
+          {{ $dayjs(data?.updateTime).format('YYYY/MM/DD') }}
+        </div>
+      </div>
+      <div @click="$emit('onRevoke')" class="flex mb-10 items-center space-x-5">
+        <div class="flex cursor-pointer items-center p-5 text-primary">
+          <span class="w-15 h-15">
+            <img
+              class="w-full h-full shrink-0 object-cover"
+              src="~/assets/img/note-create/cancel.svg"
+              alt=""
+            />
+          </span>
+          <span class="text-base">撤销审核</span>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import noteDraftCoverBg from '~/assets/img/note-create/note_draft_cover_bg.jpg'
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({})
+  }
+})
+
+defineEmits(['onRevoke'])
+
+function handleWrite() {
+  navigateTo({
+    path: '/note-create',
+    query: {
+      id: props.data.id
+    }
+  })
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 60 - 0
src/components/Profile/Notes/Auditing/index.vue

@@ -0,0 +1,60 @@
+<template>
+  <div class="w-full min-h-400 pb-10">
+    <!-- v-loading="loading" -->
+    <!-- v-if="!loading && !draftList.length" -->
+    <ProfileNotesEmpty v-if="!draftList.length" />
+    <!-- v-else-if="draftList.length" -->
+    <!-- <div class="flex flex-col divide-y"> -->
+    <div class="flex flex-col divide-y">
+      <ProfileNotesAuditingItem />
+      <!-- v-for="item in draftList"
+        :key="item.id"
+        :data="item"
+        @on-revoke="handleRevoke(item)" -->
+    </div>
+  </div>
+</template>
+
+<script setup>
+// const { loading, setLoading } = useLoading()
+// loading.value = true
+
+const draftList = ref([])
+
+async function getNotesList() {
+  // setLoading(true)
+  try {
+    const { data } = await request('/website/tourism/publishTravelNotes/getDraftList', {
+      query: {
+        pageNum: 1,
+        pageSize: 10000,
+        type: 1
+      }
+    })
+    draftList.value = data.dataList
+  } finally {
+    // setLoading(false)
+  }
+}
+
+async function handleRevoke(item) {
+  ElMessageBox.confirm('确定撤销审核吗?', '提示', {}).then(async () => {
+    try {
+      await request(`/website/tourism/publishTravelNotes/withdraw`, {
+        method: 'post',
+        body: {
+          writeId: item.id
+        }
+      })
+      ElMessage.success('撤销成功')
+      getNotesList()
+    } catch (error) {}
+  })
+}
+
+onMounted(() => {
+  // getNotesList()
+})
+</script>
+
+<style lang="scss" scoped></style>

+ 23 - 0
src/components/Profile/Notes/Empty.vue

@@ -0,0 +1,23 @@
+<template>
+  <div class="flex h-360 flex-col items-center justify-center space-y-10">
+    <img src="~/assets/img/empty.png" class="h-80 object-cover" />
+    <div class="text-base text-[#999999]">{{ title }}</div>
+    <NuxtLink
+      to="/note-create"
+      class="flex cursor-pointer items-center justify-center space-x-10 rounded-sm bg-primary px-20 py-5 text-base text-white hover:opacity-80"
+    >
+      去写游记
+    </NuxtLink>
+  </div>
+</template>
+
+<script setup>
+defineProps({
+  title: {
+    type: String,
+    default: '暂无游记'
+  }
+})
+</script>
+
+<style lang="scss" scoped></style>

+ 53 - 47
src/components/Profile/Notes/Published/Item.vue

@@ -1,60 +1,66 @@
 <template>
-  <!-- <div
-    class="group relative flex space-x-15 px-10 py-20 transition-all hover:bg-[#f8f8f8]"
-  > -->
-  <!-- <img
-      :src="formatImgSrc(data.tourismUrlsAfterConvert) || noteDraftCoverBg"
-      class="aspect-[4/3] h-180 shrink-0 rounded-xl object-cover"
-    />
-    <div class="flex-1">
-      <div class="min-h-140">
-        <NuxtLink
-          :to="`/yj/${data.id}`"
-          class="cursor-pointer truncate break-all text-2xl font-bold text-black-3 transition-all hover:text-primary"
-        >
-          {{ data.projectTitle }}
-        </NuxtLink>
-        <div class="mt-10 line-clamp-3 break-all text-base text-black-6">
-          {{ data.remarks }}
-        </div>
+  <van-swipe-cell>
+    <div class="group flex border-box p-10 transition-all mb-10 bg-[#fff] rounded-xl">
+      <div class="aspect-[4/3] h-109 border-box">
+        <van-image
+          width="100%"
+          height="100%"
+          fit="contain"
+          :src="formatImgSrc(data?.tourismUrlsAfterConvert) || noteDraftCoverBg"
+        />
       </div>
-      <div class="flex space-x-20 text-base text-black-6">
-        <div class="flex items-center space-x-2">
-          <span
-            class="iconfont icon-eye-fill text-black-9"
-            style="font-size: 16px"
-          ></span>
-          <div>{{ data.pageViewCount ?? 0 }}</div>
+
+      <div class="flex-1 ml-15">
+        <div class="min-h-70">
+          <NuxtLink
+            :to="`/yj/${data?.id}`"
+            class="cursor-pointer truncate break-all text-base font-bold text-black-3 transition-all hover:text-primary"
+          >
+            {{ data?.projectTitle }}
+          </NuxtLink>
+          <div class="line-clamp-3 text-sm leading-[17px] break-all text-black-6">
+            {{ data?.remarks }}
+          </div>
         </div>
-        <div v-if="data.endPlaceDictMap" class="flex items-center space-x-2">
-          <span
-            class="iconfont icon-location-fill text-black-9"
-            style="font-size: 16px"
-          ></span>
-          <div>{{ data.endPlaceDictMap?.name }}</div>
+        <div class="flex space-x-4 text-sm text-[#FD9A00]">
+          <div class="flex items-center space-x-2">
+            <span class="iconfont icon-eye-fill text-[#FD9A00]" style="font-size: 15px"></span>
+            <div>{{ data?.pageViewCount ?? 0 }}</div>
+          </div>
+          <div v-if="data?.endPlaceDictMap" class="flex items-center space-x-2">
+            <span class="iconfont icon-location-fill text-black-9" style="font-size: 15px"></span>
+            <div>{{ data?.endPlaceDictMap?.name }}</div>
+          </div>
         </div>
       </div>
     </div>
-    <div
-      @click="$emit('onNoteDown')"
-      class="absolute bottom-20 right-20 hidden cursor-pointer items-center space-x-5 rounded border border-[#f0f2f5] px-20 py-5 text-sm text-primary hover:bg-primary hover:text-white group-hover:flex"
-    >
-      下架
-    </div> -->
-  <!-- </div> -->
+    <template #right>
+      <van-button
+        square
+        text="下架"
+        @click="$emit('onNoteDown')"
+        type="danger"
+        class="delete-button"
+      />
+    </template>
+  </van-swipe-cell>
 </template>
 
 <script setup>
-// import noteDraftCoverBg from '~/assets/img/note-create/note_draft_cover_bg.jpg'
+import noteDraftCoverBg from '~/assets/img/note-create/note_draft_cover_bg.jpg'
 
-// const props = defineProps({
-//   data: {
-//     type: Object,
-//     default: () => ({})
-//   }
-// })
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({})
+  }
+})
 
-// defineEmits(['onNoteDown'])
+defineEmits(['onNoteDown'])
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.delete-button {
+  height: 100%;
+}
+</style>

+ 48 - 43
src/components/Profile/Notes/Published/index.vue

@@ -1,61 +1,66 @@
 <template>
-  <!-- v-loading="loading" -->
-  <div class="box-border min-h-400 pt-17 px-1">
-    发布页面
-    <!-- <ProfileNotesEmpty v-if="!loading && !noteList.length" /> -->
-    <!-- <div v-else-if="noteList.length">
-      <div class="mt-10 flex flex-col divide-y">
-        <ProfileNotesPublishedItem
-          v-for="item in noteList"
-          :key="item.id"
-          :data="item"
-          @on-note-down="handleNoteDown(item)"
-        />
-      </div>
-    </div> -->
+  <div class="w-full box-border min-h-400 pb-10 px-1">
+    <ProfileNotesEmpty v-if="!noteList.length" />
+
+    <ProfileNotesPublishedItem
+      v-for="item in noteList"
+      :key="item.id"
+      :data="item"
+      @on-note-down="handleNoteDown(item)"
+    />
   </div>
 </template>
 
 <script setup>
-// const { loading, setLoading } = useLoading()
-// loading.value = true
+const { loading, setLoading } = useLoading()
 
 const noteList = ref([])
+const pageNum = ref(1)
 
-async function getList() {
-  // setLoading(true)
+function getList() {
   try {
-    const { data } = await request('/website/tourism/publishTravelNotes/getDraftList', {
-      query: {
-        pageNum: 1,
-        pageSize: 10000,
-        type: 3
+    // let {
+    //   data: { dataList }
+    // } = await request('/website/tourism/publishTravelNotes/getDraftList', {
+    //   query: {
+    //     pageNum: pageNum.value,
+    //     pageSize: 10,
+    //     type: 3
+    //   }
+    // })
+    let dataList = [
+      {
+        id: '1'
       }
-    })
-    noteList.value = data.dataList
-  } finally {
-    // setLoading(false)
-  }
+    ]
+    console.log(1231)
+    if (Array.isArray(dataList) && dataList.length) {
+      console.log(1231444)
+
+      noteList.value = dataList
+      pageNum.value++
+    }
+  } catch (error) {}
 }
 
 // 下架
-// async function handleNoteDown(item) {
-//   ElMessageBox.confirm('下架后游记将存入草稿,确定下架吗?', '提示', {}).then(async () => {
-//     try {
-//       await request(`/website/tourism/publishTravelNotes/delist`, {
-//         method: 'post',
-//         body: {
-//           writeId: item.id
-//         }
-//       })
-//       ElMessage.success('下架成功')
-//       getList()
-//     } catch (error) {}
-//   })
-// }
+async function handleNoteDown(item) {
+  ElMessageBox.confirm('下架后游记将存入草稿,确定下架吗?', '提示', {}).then(async () => {
+    try {
+      await request(`/website/tourism/publishTravelNotes/delist`, {
+        method: 'post',
+        body: {
+          writeId: item.id
+        }
+      })
+      ElMessage.success('下架成功')
+      getList()
+    } catch (error) {}
+  })
+}
 
 onMounted(() => {
-  // getList()
+  getList()
 })
 </script>
 

+ 1 - 1
src/components/Profile/Notes/Tabs.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <div class="flex items-center space-x-7">
+    <div class="flex items-center space-x-7 bg-[#F8F8F8] py-16">
       <div
         v-for="tab in tabs"
         :key="tab.lable"

+ 3 - 2
src/components/Tabbar/index.vue

@@ -10,12 +10,13 @@
     <van-tabbar-item icon="home-o">首页</van-tabbar-item>
     <van-tabbar-item icon="search">境外旅游</van-tabbar-item>
     <van-tabbar-item icon="friends-o">出国劳务</van-tabbar-item>
+    <van-tabbar-item icon="friends-o">写游记</van-tabbar-item>
   </van-tabbar>
 </template>
 
 <script setup>
-const tabbarStore = useTabbarStore();
-const { index } = storeToRefs(tabbarStore);
+const tabbarStore = useTabbarStore()
+const { index } = storeToRefs(tabbarStore)
 </script>
 
 <style lang="scss" scoped></style>

+ 10 - 0
src/composables/useLoading.js

@@ -0,0 +1,10 @@
+export const useLoading = () => {
+  const loading = ref(false)
+
+  const setLoading = (val) => (loading.value = val)
+
+  return {
+    loading,
+    setLoading
+  }
+}

+ 33 - 16
src/pages/profile/index/notes.vue

@@ -1,19 +1,24 @@
 <template>
-  <div class="box-border py-16 px-20">
-    <ProfileNotesTabs :tabs="tabs"></ProfileNotesTabs>
-    <div class="mt-20">
-      <div v-if="tab === 'published'">
-        <ProfileNotesPublished />
-      </div>
-      <div v-else-if="tab === 'auditing'">
-        <!-- <ProfileNotesAuditing /> -->
-      </div>
-      <div v-else-if="tab === 'rejected'">
-        <!-- <ProfileNotesRejected /> -->
-      </div>
-      <div v-else-if="tab === 'draft'">
-        <!-- <ProfileNotesDraft /> -->
-      </div>
+  <div ref="container" class="w-full h-[200vh] box-border pb-16 px-20 bg-[#F8F8F8]">
+    <van-sticky :offset-top="0" :container="container">
+      <ProfileNotesTabs :tabs="tabs"></ProfileNotesTabs>
+    </van-sticky>
+
+    <div class="w-full h-full">
+      <van-pull-refresh v-model="loading" @refresh="onRefresh">
+        <div v-if="tab === 'published'">
+          <ProfileNotesPublished />
+        </div>
+        <div v-else-if="tab === 'auditing'">
+          <ProfileNotesAuditing />
+        </div>
+        <div v-else-if="tab === 'rejected'">
+          <!-- <ProfileNotesRejected /> -->
+        </div>
+        <div v-else-if="tab === 'draft'">
+          <!-- <ProfileNotesDraft /> -->
+        </div>
+      </van-pull-refresh>
     </div>
   </div>
 </template>
@@ -43,8 +48,20 @@ const tabs = [
 ]
 
 const route = useRoute()
-
+const container = ref(null)
 const tab = useRouteQuery('tab')
+const { loading, setLoading } = useLoading()
+
+const onRefresh = () => {
+  setTimeout(() => {
+    try {
+      showToast('刷新成功')
+      loading.value = false
+    } catch (err) {
+      showToast('刷新失败')
+    }
+  }, 1000)
+}
 
 watch(
   tab,

+ 179 - 32
src/pages/travel-notes/index.vue

@@ -1,34 +1,160 @@
 <template>
-  <div>
+  <div class="">
     <!-- <Navbar :title="`${menuName ?? ''}精品旅游`" /> -->
+    <div class="sticky top-0 z-50 bg-white w-full">
+      <van-dropdown-menu active-color="#FF9300" ref="dropDownMenuRef">
+        <van-dropdown-item @closed="onAreaFilterClose" :title="areaFilterTitle" ref="itemRef">
+          <van-tree-select @click-nav="handleAreaClick" @click-item="handleFilterClick" v-model:active-id="activeId"
+            v-model:main-active-index="areaIndex" :items="filterList" />
+        </van-dropdown-item>
+        <van-dropdown-item :title="writeTypeText" v-model="travelWriteType"
+          :options="writeTypeList"></van-dropdown-item>
+      </van-dropdown-menu>
+    </div>
     <div class="px-10 pt-20">
-      <van-search
-        v-model="searchStr"
-        placeholder="请输入搜索关键词"
-        shape="round"
-        @search="onSearch"
-      >
-      </van-search>
-      <van-list
-        v-if="dataList.length"
-        v-model:loading="loading"
-        :immediate-check="false"
-        :finished="finished"
-        finished-text=""
-        @load="loadMore"
-      >
-        <TravelProjectItem
-          v-for="item in dataList"
-          :key="item.id"
-          :item-data="item"
-        />
+      <van-list v-if="dataList.length" v-model:loading="loading" :immediate-check="false" :finished="finished"
+        finished-text="" @load="loadMore">
+        <template v-for="itemData in dataList">
+          <NuxtLink class="group flex relative cursor-pointer bg-white  pb-10 mb-20" :to="`/yj/${itemData.id}`"
+            target="_blank">
+            <div class="aspect-[120/80] h-80 shrink-0 rounded overflow-hidden bg-[#ddd]">
+              <img :src="itemData.tourismUrl" class="w-full h-full rounded object-cover" alt="" srcset="" />
+            </div>
+            <div class="h-80  pl-[5px] flex flex-col text-[#FD9A00] justify-between  w-[calc(100%-120px)]">
+              <div class="truncate w-full text-[14px] ">
+                <img v-if="itemData.isOriginal == 1" src="~/assets/img/yuanchuang.png"  class="mt-3 w-[30px] h-[16px]" alt="" style="float:left;">
+                {{ itemData.projectTitle }}
+              </div>
+              <div class="w-full overflow-x-auto scrollbar" v-if="itemData.noteLabel">
+                <div class="flex flex-nowrap">
+                  <div v-for="tag in convertTag(itemData.noteLabel)" class="p-2 rounded-full border border-[#FD9A00] text-[10px] pr-12 pl-12 float-left mr-10 shrink-0">{{ tag }}</div>
+                </div>
+              </div>
+              <div class="flex justify-end items-center text-[12px] text-[#999]">
+                <div class="flex items-center mr-10">
+                  <van-icon name="eye-o" class="mr-5" />{{ itemData.pageViewCount }}
+                </div>
+                <div class="flex items-center mr-10">
+                  <van-icon name="good-job-o" class="mr-5" />{{ itemData.likeCount }}
+                </div>
+                <div class="flex items-center mr-10">
+                  <van-icon name="like-o" class="mr-5" />{{ itemData.hotValue }}
+                </div>
+              </div>
+            </div>
+            <div class="w-full h-10 absolute top-[100%] left-0 flex items-center justify-between">
+              <div v-for="item in 40" class="w-[2%] h-[1px] border border-[#ddd]"></div>
+            </div>
+          </NuxtLink>
+        </template>
       </van-list>
-      <Empty v-else-if="!dataList.length && !loading" />
+      <Empty v-if="!dataList.length && !loading" />
     </div>
   </div>
 </template>
 
 <script setup>
+const AREA_TEXT = '地域'
+
+const filterList = ref([])//地域列表
+
+const curFilter = ref({})//当前筛选条件(国家)
+
+const areaIndex = ref(0)//地域索引
+
+const areaFilterTitle = ref(AREA_TEXT)
+
+const activeId = computed(() => curFilter.value.id)
+
+const activeIndex = ref('');
+
+const dropDownMenuRef = ref(null)
+// 获取筛选列表
+async function getFilters() {
+  const { data } = await request(`website/tourism/projectTravelNotes/travelNotesDirectoryList`)
+  if (!Array.isArray(data)) return
+  data.map((item, index) => {
+    data[index].id = item.areaId
+    data[index].text = item.areaName
+    if (Array.isArray(item.children)) {
+      item.children.map((item2, index2) => {
+        data[index].children[index2].id = item2.countryId
+        data[index].children[index2].text = item2.countryName
+        data[index].children[index2].areaId = item.areaId
+      })
+    }
+  })
+  filterList.value = [{ id: 0, text: '全部', children: [{ id: 0, text: '全部' }] }, ...data]
+}
+
+// 监听地域筛选框收起
+function onAreaFilterClose() { areaIndex.value = activeIndex.value }
+
+const pageNum = ref(1)
+const pageSize = ref(10)
+const dataList = ref([])
+const loading = ref(false)
+const finished = ref(false)
+// 是否原创 0 全部 1 原创 
+const travelWriteType = ref(0)
+const writeTypeList = ref([
+  { text: '全部', value: 0 },
+  { text: '原创', value: 1 },
+])
+const writeTypeText = computed(() => writeTypeList.value[travelWriteType.value].value ? '原创' : '是否原创')
+watch(travelWriteType, () => {
+  pageNum.value = 1
+  dataList.value = []
+  getList()
+})
+
+// 获取游记列表
+async function getList() {
+  const param = {
+    pageNum: pageNum.value,
+    pageSize: pageSize.value,
+  }
+
+  if (curFilter.value.id) {
+    param.areaId = curFilter.value.areaId
+    param.countryId = curFilter.value.id
+  }
+  if (travelWriteType.value) {
+    param.travelWriteType = travelWriteType.value
+  }
+  loading.value = true
+  const { data } = await request(`website/tourism/projectTravelNotes/travelNotesPageList`, { query: param })
+
+  dataList.value = dataList.value.concat(data.dataList)
+  loading.value = false
+  if (dataList.value.length >= data.totalCount) {
+    finished.value = true;
+  } else {
+    finished.value = false;
+  }
+}
+// 选择洲
+function handleAreaClick(index) { areaIndex.value = index }
+
+// 选择国家
+function handleFilterClick(item) {
+  curFilter.value = item
+  if (!item.id) {
+    areaFilterTitle.value = AREA_TEXT
+  } else {
+    const areaText = filterList.value[areaIndex.value].text || ''
+    const cityText = item.text || ''
+    activeIndex.value = areaIndex.value
+    areaFilterTitle.value = areaText + '-' + cityText
+  }
+  pageNum.value = 1
+  dataList.value = []
+  getList()
+  dropDownMenuRef.value && dropDownMenuRef.value.close()
+  document.title=`${item.id?'游记-'+item.text:'旅游笔记'}`
+}
+
+
 const menuName = useRouteQuery("menuName");
 
 const areaId = useRouteQuery("areaId");
@@ -38,11 +164,8 @@ const countryId = useRouteQuery("countryId");
 // const id = useRouteParam("id");
 
 const searchStr = ref("");
-const dataList = ref([]);
-const loading = ref(false);
-const finished = ref(false);
-const pageNum = ref(1);
-const pageSize = ref(10);
+
+
 async function getProjects() {
   const { data } = await request(
     `website/tourism/projectTravelNotes/travelNotesPageList`,
@@ -68,9 +191,12 @@ async function getProjects() {
 
 function loadMore() {
   pageNum.value++;
-  getProjects();
+  getList()
+}
+function convertTag(str=''){
+  if(typeof str!=='string') return []
+  return str.split('&')
 }
-
 function onSearch() {
   pageNum.value = 1;
   dataList.value = [];
@@ -79,12 +205,33 @@ function onSearch() {
 }
 
 onMounted(() => {
-  getProjects();
+  getFilters()
+  getList()
 });
 
 useSeoMeta({
-  title: `${menuName.value}精品旅游`,
+  title: `旅游笔记`,
 });
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+:root {
+  --tree-select-item-active-color: red;
+  /* 选中项背景颜色 */
+  --tree-select-item-active-text-color: red;
+  /* 选中项文本颜色 */
+  --van-tree-select-item-active-color: red;
+}
+.scrollbar::-webkit-scrollbar {
+  width: 0px;
+  height: 0px;
+  background-color: #ccc;
+}
+
+/* 滚动条滑块样式 */
+.scrollbar::-webkit-scrollbar-thumb {
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+  background-color: #555;
+}
+</style>

+ 27 - 23
src/stores/useTabbar.js

@@ -1,48 +1,52 @@
-import { defineStore } from "pinia";
+import { defineStore } from 'pinia'
 
-export const useTabbarStore = defineStore("tabbar", () => {
-  const index = ref(0);
+export const useTabbarStore = defineStore('tabbar', () => {
+  const index = ref(0)
 
   const tabbarList = [
     {
-      name: "首页",
-      path: "/",
+      name: '首页',
+      path: '/'
     },
     {
-      name: "境外旅游",
-      path: "/travel",
+      name: '境外旅游',
+      path: '/travel'
     },
     {
-      name: "出国劳务",
-      path: "/labour",
+      name: '出国劳务',
+      path: '/labour'
     },
-  ];
+    {
+      name: '写游记',
+      path: '/profile/notes?tab=published'
+    }
+  ]
 
   watch(index, async (val) => {
     tabbarList.forEach(async (item, i) => {
       if (i === val) {
         await navigateTo({
-          path: item.path,
-        });
-        return;
+          path: item.path
+        })
+        return
       }
-    });
-  });
+    })
+  })
 
-  const route = useRoute();
+  const route = useRoute()
   watch(
     () => route.path,
     (val) => {
-      const targetIndex = tabbarList.findIndex((item) => item.path === val);
-      index.value = targetIndex;
+      const targetIndex = tabbarList.findIndex((item) => item.path === val)
+      index.value = targetIndex
     },
     {
-      immediate: true,
+      immediate: true
     }
-  );
+  )
 
   return {
     tabbarList,
-    index,
-  };
-});
+    index
+  }
+})