Răsfoiți Sursa

feat:1.新增 个人中心我的游记 页面 和图片
utils添加 formatImgSrc
composables 文件添加 useLoading.js

suwenjiang 3 luni în urmă
părinte
comite
8dcc32e14c
35 a modificat fișierele cu 334 adăugiri și 146 ștergeri
  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. 11 11
      src/components/Home/Menu.vue
  25. 55 0
      src/components/Profile/Notes/Auditing/Item.vue
  26. 60 0
      src/components/Profile/Notes/Auditing/index.vue
  27. 23 0
      src/components/Profile/Notes/Empty.vue
  28. 53 47
      src/components/Profile/Notes/Published/Item.vue
  29. 48 43
      src/components/Profile/Notes/Published/index.vue
  30. 1 1
      src/components/Profile/Notes/Tabs.vue
  31. 3 2
      src/components/Tabbar/index.vue
  32. 10 0
      src/composables/useLoading.js
  33. 33 16
      src/pages/profile/index/notes.vue
  34. 27 23
      src/stores/useTabbar.js
  35. 9 3
      src/utils/index.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


+ 11 - 11
src/components/Home/Menu.vue

@@ -13,27 +13,27 @@
 </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";
+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",
+    title: '境外美食',
+    to: '/food'
   },
   {
     icon: HomeTravleIcon,
-    title: "境外旅游",
-    to: "/travel",
+    title: '境外旅游',
+    to: '/travel'
   },
   {
     icon: HomeLabourIcon,
-    title: "出国劳务",
-    to: "/labour",
-  },
-];
+    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,

+ 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
+  }
+})

+ 9 - 3
src/utils/index.js

@@ -1,4 +1,10 @@
-const setIntervalImmediately = (fn, duration) =>
-  setInterval((() => (fn(), fn))(), duration);
+const setIntervalImmediately = (fn, duration) => setInterval((() => (fn(), fn))(), duration)
 
-export { setIntervalImmediately };
+function formatImgSrc(srcArr) {
+  if (Array.isArray(srcArr) && srcArr.length > 0) {
+    return srcArr[0] ?? ''
+  }
+  return ''
+}
+
+export { setIntervalImmediately, formatImgSrc }