Kaynağa Gözat

feat : 1.游记详情的ui 见面改版
2.新增话题详情
3.新增我的收藏话题和收藏评论
4.新增他人主页

suwenjiang 1 ay önce
ebeveyn
işleme
997b6525f0
49 değiştirilmiş dosya ile 4508 ekleme ve 2871 silme
  1. 7 6
      .env.development
  2. 3 0
      package.json
  3. 166 126
      pnpm-lock.yaml
  4. 287 0
      single-add.vue
  5. 939 0
      src/assets/css/quill.snow.css
  6. 388 343
      src/assets/iconfont/demo_index.html
  7. 127 115
      src/assets/iconfont/iconfont.css
  8. 0 0
      src/assets/iconfont/iconfont.js
  9. 302 281
      src/assets/iconfont/iconfont.json
  10. 12 13
      src/assets/iconfont/iconfont.svg
  11. BIN
      src/assets/iconfont/iconfont.ttf
  12. BIN
      src/assets/iconfont/iconfont.woff
  13. BIN
      src/assets/iconfont/iconfont.woff2
  14. 9 0
      src/assets/img/profile-others/Female.svg
  15. 5 0
      src/assets/img/profile-others/Like.svg
  16. 5 0
      src/assets/img/profile-others/Like2.svg
  17. BIN
      src/assets/img/topic/topic.png
  18. BIN
      src/assets/img/topic/yj.png
  19. 4 5
      src/components/CreateNote/Form.vue
  20. 122 0
      src/components/CreateNote/Mention.vue
  21. 7 10
      src/components/Home/TravelNotes/Item.vue
  22. 11 18
      src/components/Home/TravelNotes/index.client.vue
  23. 1 1
      src/components/NavigationBar/LeftMenu.vue
  24. 56 0
      src/components/Profile/Collection/Comment.vue
  25. 43 0
      src/components/Profile/Collection/Topic.vue
  26. 19 21
      src/components/Profile/Collection/TravelNoteCell.vue
  27. 1 1
      src/components/Profile/Notes/Auditing/Item.vue
  28. 2 2
      src/components/Profile/Notes/Draft/Item.vue
  29. 4 2
      src/components/Profile/Notes/Draft/index.vue
  30. 31 14
      src/components/Profile/Notes/Published/Item.vue
  31. 26 6
      src/components/Profile/Notes/Published/index.vue
  32. 1 1
      src/components/Profile/Notes/Rejected/Item.vue
  33. 10 16
      src/components/Profile/Notes/Tabs.vue
  34. 0 786
      src/pages/_note-create/index.client.vue
  35. 0 513
      src/pages/_yj/[id].vue
  36. 0 23
      src/pages/_yj/emoji.js
  37. 0 36
      src/pages/_yj/emoji.json
  38. 0 2
      src/pages/chat/group-chat.vue
  39. 0 2
      src/pages/chat/single-chat.vue
  40. 282 301
      src/pages/note-create/index.client.vue
  41. 324 0
      src/pages/profile-others/[id].vue
  42. 174 33
      src/pages/profile/collection.client.vue
  43. 1 1
      src/pages/profile/index.vue
  44. 3 5
      src/pages/profile/notes/index.vue
  45. 329 0
      src/pages/topic/index.vue
  46. 393 122
      src/pages/travel-notes/index.vue
  47. 187 0
      src/pages/view-group-chat/[id].vue
  48. 217 65
      src/pages/yj/[id].vue
  49. 10 1
      src/utils/index.js

+ 7 - 6
.env.development

@@ -3,8 +3,8 @@ VITE_APP_ENV=development
 
 # 测试服
 # VITE_APP_BASE_URL=https://service.xiaoyaotravel.com/api/
-VITE_APP_BASE_URL=http://101.126.146.250:8082/
-VITE_APP_IM_URL=ws://101.126.146.250:8082/system/message
+# VITE_APP_BASE_URL=http://101.126.146.250:8082/
+# VITE_APP_IM_URL=ws://101.126.146.250:8082/system/message
 
 # 陈英政
 # VITE_APP_BASE_URL=http://101.126.146.250:8084/
@@ -31,12 +31,13 @@ VITE_APP_IM_URL=ws://101.126.146.250:8082/system/message
 # VITE_APP_IM_URL=ws://q9943037p3.goho.co/system/message
 
 # 张维本地
-# VITE_APP_BASE_URL=http://192.168.1.73:8082/
+VITE_APP_BASE_URL=http://192.168.1.73:8082/
 # 本地socoket
-# VITE_APP_IM_URL=ws://192.168.1.73:8082/system/message
+VITE_APP_IM_URL=ws://192.168.1.73:8082/system/message
+
 # 花生壳
-# VITE_APP_BASE_URL=http://4eqxwr.natappfree.cc
-# VITE_APP_IM_URL=ws://4eqxwr.natappfree.cc/system/message
+# VITE_APP_BASE_URL=http://5nfhmg.natappfree.cc
+# VITE_APP_IM_URL=ws://5nfhmg.natappfree.cc/system/message
 
 
 # VITE_APP_BASE_URL=http://192.168.1.204:8082

+ 3 - 0
package.json

@@ -30,6 +30,8 @@
     "nuxt-swiper": "^2.0.0",
     "pinia": "^2.2.2",
     "pinyin-pro": "^3.26.0",
+    "quill": "^2.0.3",
+    "quill-mention": "^6.0.2",
     "reconnecting-websocket": "^4.4.0",
     "recorder-core": "^1.3.25011100",
     "vconsole": "^3.15.1",
@@ -37,6 +39,7 @@
     "vue-clipboard3": "^2.0.0",
     "vue-cropper": "^1.1.4",
     "vue-draggable-plus": "^0.6.0",
+    "vue-mention": "^1.1.0",
     "vue-router": "latest",
     "vue3-seamless-scroll": "^2.0.1"
   },

Dosya farkı çok büyük olduğundan ihmal edildi
+ 166 - 126
pnpm-lock.yaml


+ 287 - 0
single-add.vue

@@ -0,0 +1,287 @@
+<template>
+  <div class="w-full h-[100vh]">
+    <ChatHeaderBar title="选择互关好友" />
+
+    <ChatSearch v-model:searchString="showName" @search="search" placeholder="请输入关键词" />
+
+    <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
+      <ChatEmpty
+        image="search"
+        v-if="!addDataList?.length && !loading"
+        title="暂无数据"
+        top="100"
+      />
+
+      <van-list
+        v-else-if="addDataList.length"
+        v-model:loading="loading"
+        error-text="获取失败"
+        finished-text="-- 没有更多了 --"
+        :finished="finished"
+        :immediate-check="false"
+      >
+        <!--    @load="getLoadList"  -->
+        <div style="height: calc(100vh - 170px)">
+          <van-checkbox-group v-model="checked">
+            <!-- <van-index-bar highlight-color="#FD9A00" index-list :sticky="false"> -->
+            <template v-for="(item, index) in addDataList" :key="item?.attentionIdDictMap?.userId">
+              <!-- <van-index-anchor index="A" /> -->
+              <van-cell center clickable @click="toggle(item)">
+                <template #icon>
+                  <div class="flex justify-start">
+                    <van-checkbox
+                      checked-color="#FD9A00"
+                      :name="item?.attentionIdDictMap?.userId"
+                      :ref="(el) => (checkboxRefs[item?.attentionIdDictMap?.userId] = el)"
+                      @click.stop="toggle(item)"
+                    />
+
+                    <div class="w-40 h-40 ml-13 mr-12 rounded-full overflow-hidden">
+                      <img
+                        v-if="item?.attentionIdDictMap?.headImageUrl"
+                        class="w-full h-full shrink-0 object-cover"
+                        :src="item?.attentionIdDictMap?.headImageUrl"
+                        alt=""
+                      />
+
+                      <img
+                        class="w-full h-full shrink-0 object-cover"
+                        src="~/assets/img/default_avatar.png"
+                        alt=""
+                      />
+                    </div>
+                  </div>
+                </template>
+                <template #title>
+                  <div class="flex items-center">
+                    <h1 class="text-xl text-black-3">
+                      {{ item?.attentionIdDictMap?.showName }}
+                    </h1>
+                    <van-tag
+                      v-if="item.fansStatus == 2"
+                      style="margin-left: 5px; padding: 3px 6px"
+                      color="#F7F8FA"
+                      text-color="#666666"
+                    >
+                      相互关注
+                    </van-tag>
+                  </div>
+                </template>
+              </van-cell>
+            </template>
+            <!-- </van-index-bar> -->
+          </van-checkbox-group>
+        </div>
+      </van-list>
+    </van-pull-refresh>
+    <div
+      class="w-full box-border p-16 pb-40 bg-white fixed bottom-0 left-0 flex justify-between items-center shadow-[0px_-4px_4px_0px_rgba(0,0,0,0.1)]"
+    >
+      <div class="shrink-0 flex justify-start items-center">
+        <div class="w-118 shrink-0 flex justify-start items-center overflow-hidden">
+          <div
+            v-for="(item, index) in checkedList.slice(0, 5)"
+            :key="index + 'avatar'"
+            :class="`w-36 h-36  ${index == 0 ? '' : '-ml-16'} shrink-0 rounded-full overflow-hidden`"
+          >
+            <img
+              v-if="item?.attentionIdDictMap?.headImageUrl"
+              class="w-full h-full object-cover"
+              :src="item?.attentionIdDictMap?.headImageUrl"
+              alt=""
+            />
+            <img
+              v-else
+              class="w-full h-full shrink-0 object-cover"
+              src="~/assets/img/default_avatar.png"
+              alt=""
+            />
+          </div>
+        </div>
+
+        <div v-if="checkedList.length > 5" class="shrink-0 w-24 h-24 ml-8">
+          <img class="w-full h-full object-cover" src="~/assets/img/chat/ellipsis.svg" alt="" />
+        </div>
+      </div>
+      <van-button
+        :loading="isLoading"
+        :disabled="checkedList.length > 0 ? false : true"
+        @click="handleCreateGroup"
+        style="width: 160px"
+        class="shrink-0"
+        block
+        size="large"
+        color="#FD9A00"
+        round
+      >
+        新建
+        <span v-if="checkedList.length">({{ checkedList.length }})</span>
+      </van-button>
+    </div>
+  </div>
+</template>
+<script setup>
+const route = useRoute()
+const router = useRouter()
+
+definePageMeta({
+  layout: false
+})
+
+onMounted(() => {
+  getList()
+})
+// 对方的那个id
+const toUserId = computed(() => route.query?.toUserId ?? '')
+const refreshing = ref(false)
+const loading = ref(false)
+const finished = ref(false)
+
+const checked = ref([])
+const checkedList = ref([])
+const showName = ref('')
+const checkboxRefs = ref([])
+
+// 字母的数组
+const letterList = ref([])
+
+const queryParams = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  flagPage: 1
+})
+
+const addDataList = ref([])
+const filterDataList = ref([])
+
+const searchText = computed(() => (showName.value ? '暂无互关好友' : '暂无搜索结果'))
+
+// 选中要邀请的人
+const toggle = (item) => {
+  let index = checkedList.value.findIndex(
+    (el) => el?.attentionIdDictMap?.userId == item?.attentionIdDictMap?.userId
+  )
+
+  if (index != -1) {
+    checkedList.value.splice(index, 1)
+  } else {
+    checkedList.value.push(item)
+  }
+
+  checkboxRefs.value[item?.attentionIdDictMap?.userId].toggle()
+}
+
+const search = () => {
+  finished.value = true
+  if (showName.value) {
+    addDataList.value = filterDataList.value.filter((item) =>
+      item.attentionIdDictMap.name.includes(showName.value)
+    )
+  } else {
+    addDataList.value = filterDataList.value
+  }
+}
+
+// 刷新
+const onRefresh = () => {
+  queryParams.pageNum = 1
+  addDataList.value = []
+  filterDataList.value = []
+  getList()
+}
+
+// 获取数据
+const getList = async () => {
+  try {
+    let url = `/website/tourism/fans/getFriends`
+
+    loading.value = true
+    let {
+      data: { dataList, totalCount }
+    } = await request(url, {
+      query: {
+        ...queryParams
+      }
+    })
+
+    if (Array.isArray(dataList) && dataList?.length) {
+      addDataList.value = dataList
+
+      nextTick(() => {
+        checkboxRefs.value[toUserId.value].toggle()
+        let item = dataList.find((el) => el?.attentionIdDictMap?.userId == toUserId.value)
+        if (item) {
+          checkedList.value.push(item)
+        } else {
+          router.back()
+        }
+      })
+    } else {
+      addDataList.value = []
+    }
+
+    loading.value = false
+    refreshing.value = false
+    if (addDataList.value.length >= totalCount) {
+      finished.value = true
+    } else {
+      finished.value = false
+    }
+  } catch (err) {
+  } finally {
+    refreshing.value = false
+    loading.value = false
+  }
+}
+
+// 节流
+const isLoading = ref(false)
+
+function handleKeyDown(event) {
+  // 启动节流
+  // 1000ms 后解除节流
+  // setTimeout(() => {
+  //   isThrottled.value = false
+  // }, 3000)
+}
+
+// 创建多人聊天
+async function handleCreateGroup() {
+  if (!isLoading.value) {
+    isLoading.value = true
+    try {
+      showLoadingToast({
+        message: '准备开始群聊...',
+        duration: 100000
+      })
+
+      let { data } = await request('/website/tourGroup/createGroup', {
+        method: 'post',
+        body: {
+          createType: 2,
+          ids: [...checked.value]
+        }
+      })
+
+      if (data) {
+        navigateTo({
+          path: '/chat/group-chat',
+          query: {
+            groupId: data
+          },
+          replace: true
+        })
+        isLoading.value = false
+      }
+    } catch (error) {
+    } finally {
+      closeToast()
+    }
+  }
+}
+
+useSeoMeta({
+  title: '我的消息'
+})
+</script>
+<style lang="scss" scoped></style>

+ 939 - 0
src/assets/css/quill.snow.css

@@ -0,0 +1,939 @@
+.ql-container {
+    box-sizing: border-box;
+    font-family: Helvetica, Arial, sans-serif;
+    font-size: 13px;
+    height: 100%;
+    margin: 0;
+    position: relative;
+}
+.ql-container.ql-disabled .ql-tooltip {
+    visibility: hidden;
+}
+.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {
+    pointer-events: none;
+}
+.ql-clipboard {
+    left: -100000px;
+    height: 1px;
+    overflow-y: hidden;
+    position: absolute;
+    top: 50%;
+    display: none;
+}
+.ql-clipboard p {
+    margin: 0;
+    padding: 0;
+}
+.ql-editor {
+    box-sizing: border-box;
+    line-height: 1.42;
+    height: 100%;
+    outline: none;
+    overflow-y: auto;
+    padding: 12px 15px;
+    tab-size: 4;
+    tab-size: 4;
+    text-align: left;
+    white-space: pre-wrap;
+    word-wrap: break-word;
+}
+.ql-editor > * {
+    cursor: text;
+}
+.ql-editor p,
+.ql-editor ol,
+.ql-editor ul,
+.ql-editor pre,
+.ql-editor blockquote,
+.ql-editor h1,
+.ql-editor h2,
+.ql-editor h3,
+.ql-editor h4,
+.ql-editor h5,
+.ql-editor h6 {
+    margin: 0;
+    padding: 0;
+    counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol,
+.ql-editor ul {
+    padding-left: 1.5em;
+}
+.ql-editor ol > li,
+.ql-editor ul > li {
+    list-style-type: none;
+}
+.ql-editor ul > li::before {
+    content: "\2022";
+}
+.ql-editor ul[data-checked="true"],
+.ql-editor ul[data-checked="false"] {
+    pointer-events: none;
+}
+.ql-editor ul[data-checked="true"] > li *,
+.ql-editor ul[data-checked="false"] > li * {
+    pointer-events: all;
+}
+.ql-editor ul[data-checked="true"] > li::before,
+.ql-editor ul[data-checked="false"] > li::before {
+    color: #777;
+    cursor: pointer;
+    pointer-events: all;
+}
+.ql-editor ul[data-checked="true"] > li::before {
+    content: "\2611";
+}
+.ql-editor ul[data-checked="false"] > li::before {
+    content: "\2610";
+}
+.ql-editor li::before {
+    display: inline-block;
+    white-space: nowrap;
+    width: 1.2em;
+}
+.ql-editor li:not(.ql-direction-rtl)::before {
+    margin-left: -1.5em;
+    margin-right: 0.3em;
+    text-align: right;
+}
+.ql-editor li.ql-direction-rtl::before {
+    margin-left: 0.3em;
+    margin-right: -1.5em;
+}
+.ql-editor ol li:not(.ql-direction-rtl),
+.ql-editor ul li:not(.ql-direction-rtl) {
+    padding-left: 1.5em;
+}
+.ql-editor ol li.ql-direction-rtl,
+.ql-editor ul li.ql-direction-rtl {
+    padding-right: 1.5em;
+}
+.ql-editor ol li {
+    counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+    counter-increment: list-0;
+}
+.ql-editor ol li::before {
+    content: counter(list-0, decimal) ". ";
+}
+.ql-editor ol li.ql-indent-1 {
+    counter-increment: list-1;
+}
+.ql-editor ol li.ql-indent-1::before {
+    content: counter(list-1, lower-alpha) ". ";
+}
+.ql-editor ol li.ql-indent-1 {
+    counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-2 {
+    counter-increment: list-2;
+}
+.ql-editor ol li.ql-indent-2::before {
+    content: counter(list-2, lower-roman) ". ";
+}
+.ql-editor ol li.ql-indent-2 {
+    counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-3 {
+    counter-increment: list-3;
+}
+.ql-editor ol li.ql-indent-3::before {
+    content: counter(list-3, decimal) ". ";
+}
+.ql-editor ol li.ql-indent-3 {
+    counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-4 {
+    counter-increment: list-4;
+}
+.ql-editor ol li.ql-indent-4::before {
+    content: counter(list-4, lower-alpha) ". ";
+}
+.ql-editor ol li.ql-indent-4 {
+    counter-reset: list-5 list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-5 {
+    counter-increment: list-5;
+}
+.ql-editor ol li.ql-indent-5::before {
+    content: counter(list-5, lower-roman) ". ";
+}
+.ql-editor ol li.ql-indent-5 {
+    counter-reset: list-6 list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-6 {
+    counter-increment: list-6;
+}
+.ql-editor ol li.ql-indent-6::before {
+    content: counter(list-6, decimal) ". ";
+}
+.ql-editor ol li.ql-indent-6 {
+    counter-reset: list-7 list-8 list-9;
+}
+.ql-editor ol li.ql-indent-7 {
+    counter-increment: list-7;
+}
+.ql-editor ol li.ql-indent-7::before {
+    content: counter(list-7, lower-alpha) ". ";
+}
+.ql-editor ol li.ql-indent-7 {
+    counter-reset: list-8 list-9;
+}
+.ql-editor ol li.ql-indent-8 {
+    counter-increment: list-8;
+}
+.ql-editor ol li.ql-indent-8::before {
+    content: counter(list-8, lower-roman) ". ";
+}
+.ql-editor ol li.ql-indent-8 {
+    counter-reset: list-9;
+}
+.ql-editor ol li.ql-indent-9 {
+    counter-increment: list-9;
+}
+.ql-editor ol li.ql-indent-9::before {
+    content: counter(list-9, decimal) ". ";
+}
+.ql-editor .ql-indent-1:not(.ql-direction-rtl) {
+    padding-left: 3em;
+}
+.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {
+    padding-left: 4.5em;
+}
+.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {
+    padding-right: 3em;
+}
+.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {
+    padding-right: 4.5em;
+}
+.ql-editor .ql-indent-2:not(.ql-direction-rtl) {
+    padding-left: 6em;
+}
+.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {
+    padding-left: 7.5em;
+}
+.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {
+    padding-right: 6em;
+}
+.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {
+    padding-right: 7.5em;
+}
+.ql-editor .ql-indent-3:not(.ql-direction-rtl) {
+    padding-left: 9em;
+}
+.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {
+    padding-left: 10.5em;
+}
+.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {
+    padding-right: 9em;
+}
+.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {
+    padding-right: 10.5em;
+}
+.ql-editor .ql-indent-4:not(.ql-direction-rtl) {
+    padding-left: 12em;
+}
+.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {
+    padding-left: 13.5em;
+}
+.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {
+    padding-right: 12em;
+}
+.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {
+    padding-right: 13.5em;
+}
+.ql-editor .ql-indent-5:not(.ql-direction-rtl) {
+    padding-left: 15em;
+}
+.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {
+    padding-left: 16.5em;
+}
+.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {
+    padding-right: 15em;
+}
+.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {
+    padding-right: 16.5em;
+}
+.ql-editor .ql-indent-6:not(.ql-direction-rtl) {
+    padding-left: 18em;
+}
+.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {
+    padding-left: 19.5em;
+}
+.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {
+    padding-right: 18em;
+}
+.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {
+    padding-right: 19.5em;
+}
+.ql-editor .ql-indent-7:not(.ql-direction-rtl) {
+    padding-left: 21em;
+}
+.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {
+    padding-left: 22.5em;
+}
+.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {
+    padding-right: 21em;
+}
+.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {
+    padding-right: 22.5em;
+}
+.ql-editor .ql-indent-8:not(.ql-direction-rtl) {
+    padding-left: 24em;
+}
+.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {
+    padding-left: 25.5em;
+}
+.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {
+    padding-right: 24em;
+}
+.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {
+    padding-right: 25.5em;
+}
+.ql-editor .ql-indent-9:not(.ql-direction-rtl) {
+    padding-left: 27em;
+}
+.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {
+    padding-left: 28.5em;
+}
+.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {
+    padding-right: 27em;
+}
+.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {
+    padding-right: 28.5em;
+}
+.ql-editor .ql-video {
+    display: block;
+    max-width: 100%;
+}
+.ql-editor .ql-video.ql-align-center {
+    margin: 0 auto;
+}
+.ql-editor .ql-video.ql-align-right {
+    margin: 0 0 0 auto;
+}
+.ql-editor .ql-bg-black {
+    background-color: #000;
+}
+.ql-editor .ql-bg-red {
+    background-color: #e60000;
+}
+.ql-editor .ql-bg-orange {
+    background-color: #f90;
+}
+.ql-editor .ql-bg-yellow {
+    background-color: #ff0;
+}
+.ql-editor .ql-bg-green {
+    background-color: #008a00;
+}
+.ql-editor .ql-bg-blue {
+    background-color: #06c;
+}
+.ql-editor .ql-bg-purple {
+    background-color: #93f;
+}
+.ql-editor .ql-color-white {
+    color: #fff;
+}
+.ql-editor .ql-color-red {
+    color: #e60000;
+}
+.ql-editor .ql-color-orange {
+    color: #f90;
+}
+.ql-editor .ql-color-yellow {
+    color: #ff0;
+}
+.ql-editor .ql-color-green {
+    color: #008a00;
+}
+.ql-editor .ql-color-blue {
+    color: #06c;
+}
+.ql-editor .ql-color-purple {
+    color: #93f;
+}
+.ql-editor .ql-font-serif {
+    font-family: Georgia, "Times New Roman", serif;
+}
+.ql-editor .ql-font-monospace {
+    font-family: Monaco, "Courier New", monospace;
+}
+.ql-editor .ql-size-small {
+    font-size: 0.75em;
+}
+.ql-editor .ql-size-large {
+    font-size: 1.5em;
+}
+.ql-editor .ql-size-huge {
+    font-size: 2.5em;
+}
+.ql-editor .ql-direction-rtl {
+    direction: rtl;
+    text-align: inherit;
+}
+.ql-editor .ql-align-center {
+    text-align: center;
+}
+.ql-editor .ql-align-justify {
+    text-align: justify;
+}
+.ql-editor .ql-align-right {
+    text-align: right;
+}
+.ql-editor.ql-blank::before {
+    color: #909399;
+    content: attr(data-placeholder);
+    left: 15px;
+    pointer-events: none;
+    position: absolute;
+    right: 15px;
+}
+.ql-snow.ql-toolbar::after,
+.ql-snow .ql-toolbar::after {
+    clear: both;
+    content: "";
+    display: table;
+}
+.ql-snow.ql-toolbar button,
+.ql-snow .ql-toolbar button {
+    background: none;
+    border: none;
+    cursor: pointer;
+    display: inline-block;
+    float: left;
+    height: 24px;
+    padding: 3px 5px;
+    width: 28px;
+}
+.ql-snow.ql-toolbar button svg,
+.ql-snow .ql-toolbar button svg {
+    float: left;
+    height: 100%;
+}
+.ql-snow.ql-toolbar button:active:hover,
+.ql-snow .ql-toolbar button:active:hover {
+    outline: none;
+}
+.ql-snow.ql-toolbar input.ql-image[type="file"],
+.ql-snow .ql-toolbar input.ql-image[type="file"] {
+    display: none;
+}
+.ql-snow.ql-toolbar button:hover,
+.ql-snow .ql-toolbar button:hover,
+.ql-snow.ql-toolbar button:focus,
+.ql-snow .ql-toolbar button:focus,
+.ql-snow.ql-toolbar button.ql-active,
+.ql-snow .ql-toolbar button.ql-active,
+.ql-snow.ql-toolbar .ql-picker-label:hover,
+.ql-snow .ql-toolbar .ql-picker-label:hover,
+.ql-snow.ql-toolbar .ql-picker-label.ql-active,
+.ql-snow .ql-toolbar .ql-picker-label.ql-active,
+.ql-snow.ql-toolbar .ql-picker-item:hover,
+.ql-snow .ql-toolbar .ql-picker-item:hover,
+.ql-snow.ql-toolbar .ql-picker-item.ql-selected,
+.ql-snow .ql-toolbar .ql-picker-item.ql-selected {
+    color: #06c;
+}
+.ql-snow.ql-toolbar button:hover .ql-fill,
+.ql-snow .ql-toolbar button:hover .ql-fill,
+.ql-snow.ql-toolbar button:focus .ql-fill,
+.ql-snow .ql-toolbar button:focus .ql-fill,
+.ql-snow.ql-toolbar button.ql-active .ql-fill,
+.ql-snow .ql-toolbar button.ql-active .ql-fill,
+.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill,
+.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill,
+.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill,
+.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill,
+.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill,
+.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill,
+.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill,
+.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill,
+.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
+.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,
+.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill {
+    fill: #06c;
+}
+.ql-snow.ql-toolbar button:hover .ql-stroke,
+.ql-snow .ql-toolbar button:hover .ql-stroke,
+.ql-snow.ql-toolbar button:focus .ql-stroke,
+.ql-snow .ql-toolbar button:focus .ql-stroke,
+.ql-snow.ql-toolbar button.ql-active .ql-stroke,
+.ql-snow .ql-toolbar button.ql-active .ql-stroke,
+.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke,
+.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke,
+.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke,
+.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke,
+.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke,
+.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke,
+.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
+.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
+.ql-snow.ql-toolbar button:hover .ql-stroke-miter,
+.ql-snow .ql-toolbar button:hover .ql-stroke-miter,
+.ql-snow.ql-toolbar button:focus .ql-stroke-miter,
+.ql-snow .ql-toolbar button:focus .ql-stroke-miter,
+.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter,
+.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter,
+.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
+.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
+.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
+.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
+.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
+.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
+.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,
+.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter {
+    stroke: #06c;
+}
+@media (pointer: coarse) {
+    .ql-snow.ql-toolbar button:hover:not(.ql-active),
+    .ql-snow .ql-toolbar button:hover:not(.ql-active) {
+        color: #444;
+    }
+    .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-fill,
+    .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-fill,
+    .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,
+    .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill {
+        fill: #444;
+    }
+    .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke,
+    .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke,
+    .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,
+    .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter {
+        stroke: #444;
+    }
+}
+.ql-snow {
+    box-sizing: border-box;
+}
+.ql-snow * {
+    box-sizing: border-box;
+}
+.ql-snow .ql-hidden {
+    display: none;
+}
+.ql-snow .ql-out-bottom,
+.ql-snow .ql-out-top {
+    visibility: hidden;
+}
+.ql-snow .ql-tooltip {
+    position: absolute;
+    transform: translateY(10px);
+}
+.ql-snow .ql-tooltip a {
+    cursor: pointer;
+    text-decoration: none;
+}
+.ql-snow .ql-tooltip.ql-flip {
+    transform: translateY(-10px);
+}
+.ql-snow .ql-formats {
+    display: inline-block;
+    vertical-align: middle;
+}
+.ql-snow .ql-formats::after {
+    clear: both;
+    content: "";
+    display: table;
+}
+.ql-snow .ql-stroke {
+    fill: none;
+    stroke: #444;
+    stroke-linecap: round;
+    stroke-linejoin: round;
+    stroke-width: 2;
+}
+.ql-snow .ql-stroke-miter {
+    fill: none;
+    stroke: #444;
+    stroke-miterlimit: 10;
+    stroke-width: 2;
+}
+.ql-snow .ql-fill,
+.ql-snow .ql-stroke.ql-fill {
+    fill: #444;
+}
+.ql-snow .ql-empty {
+    fill: none;
+}
+.ql-snow .ql-even {
+    fill-rule: evenodd;
+}
+.ql-snow .ql-thin,
+.ql-snow .ql-stroke.ql-thin {
+    stroke-width: 1;
+}
+.ql-snow .ql-transparent {
+    opacity: 0.4;
+}
+.ql-snow .ql-direction svg:last-child {
+    display: none;
+}
+.ql-snow .ql-direction.ql-active svg:last-child {
+    display: inline;
+}
+.ql-snow .ql-direction.ql-active svg:first-child {
+    display: none;
+}
+.ql-snow .ql-editor h1 {
+    font-size: 2em;
+}
+.ql-snow .ql-editor h2 {
+    font-size: 1.5em;
+}
+.ql-snow .ql-editor h3 {
+    font-size: 1.17em;
+}
+.ql-snow .ql-editor h4 {
+    font-size: 1em;
+}
+.ql-snow .ql-editor h5 {
+    font-size: 0.83em;
+}
+.ql-snow .ql-editor h6 {
+    font-size: 0.67em;
+}
+.ql-snow .ql-editor a {
+    text-decoration: underline;
+}
+.ql-snow .ql-editor blockquote {
+    border-left: 4px solid #ccc;
+    margin-bottom: 5px;
+    margin-top: 5px;
+    padding-left: 16px;
+}
+.ql-snow .ql-editor code,
+.ql-snow .ql-editor pre {
+    background-color: #f0f0f0;
+    border-radius: 3px;
+}
+.ql-snow .ql-editor pre {
+    white-space: pre-wrap;
+    margin-bottom: 5px;
+    margin-top: 5px;
+    padding: 5px 10px;
+}
+.ql-snow .ql-editor code {
+    font-size: 85%;
+    padding: 2px 4px;
+}
+.ql-snow .ql-editor pre.ql-syntax {
+    background-color: #23241f;
+    color: #f8f8f2;
+    overflow: visible;
+}
+.ql-snow .ql-editor img {
+    max-width: 100%;
+}
+.ql-snow .ql-picker {
+    color: #444;
+    display: inline-block;
+    float: left;
+    font-size: 14px;
+    font-weight: 500;
+    height: 24px;
+    position: relative;
+    vertical-align: middle;
+}
+.ql-snow .ql-picker-label {
+    cursor: pointer;
+    display: inline-block;
+    height: 100%;
+    padding-left: 8px;
+    padding-right: 2px;
+    position: relative;
+    width: 100%;
+}
+.ql-snow .ql-picker-label::before {
+    display: inline-block;
+    line-height: 22px;
+}
+.ql-snow .ql-picker-options {
+    background-color: #fff;
+    display: none;
+    min-width: 100%;
+    padding: 4px 8px;
+    position: absolute;
+    white-space: nowrap;
+}
+.ql-snow .ql-picker-options .ql-picker-item {
+    cursor: pointer;
+    display: block;
+    padding-bottom: 5px;
+    padding-top: 5px;
+}
+.ql-snow .ql-picker.ql-expanded .ql-picker-label {
+    color: #ccc;
+    z-index: 2;
+}
+.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill {
+    fill: #ccc;
+}
+.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke {
+    stroke: #ccc;
+}
+.ql-snow .ql-picker.ql-expanded .ql-picker-options {
+    display: block;
+    margin-top: -1px;
+    top: 100%;
+    z-index: 1;
+}
+.ql-snow .ql-color-picker,
+.ql-snow .ql-icon-picker {
+    width: 28px;
+}
+.ql-snow .ql-color-picker .ql-picker-label,
+.ql-snow .ql-icon-picker .ql-picker-label {
+    padding: 2px 4px;
+}
+.ql-snow .ql-color-picker .ql-picker-label svg,
+.ql-snow .ql-icon-picker .ql-picker-label svg {
+    right: 4px;
+}
+.ql-snow .ql-icon-picker .ql-picker-options {
+    padding: 4px 0;
+}
+.ql-snow .ql-icon-picker .ql-picker-item {
+    height: 24px;
+    width: 24px;
+    padding: 2px 4px;
+}
+.ql-snow .ql-color-picker .ql-picker-options {
+    padding: 3px 5px;
+    width: 152px;
+}
+.ql-snow .ql-color-picker .ql-picker-item {
+    border: 1px solid transparent;
+    float: left;
+    height: 16px;
+    margin: 2px;
+    padding: 0;
+    width: 16px;
+}
+.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {
+    position: absolute;
+    margin-top: -9px;
+    right: 0;
+    top: 50%;
+    width: 18px;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=""])::before,
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=""])::before,
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=""])::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=""])::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=""])::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=""])::before {
+    content: attr(data-label);
+}
+.ql-snow .ql-picker.ql-header {
+    width: 98px;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item::before {
+    content: "Normal";
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
+    content: "Heading 1";
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
+    content: "Heading 2";
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
+    content: "Heading 3";
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
+    content: "Heading 4";
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
+    content: "Heading 5";
+}
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
+    content: "Heading 6";
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
+    font-size: 2em;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
+    font-size: 1.5em;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
+    font-size: 1.17em;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
+    font-size: 1em;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
+    font-size: 0.83em;
+}
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
+    font-size: 0.67em;
+}
+.ql-snow .ql-picker.ql-font {
+    width: 108px;
+}
+.ql-snow .ql-picker.ql-font .ql-picker-label::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item::before {
+    content: "Sans Serif";
+}
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
+    content: "Serif";
+}
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
+    content: "Monospace";
+}
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
+    font-family: Georgia, "Times New Roman", serif;
+}
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
+    font-family: Monaco, "Courier New", monospace;
+}
+.ql-snow .ql-picker.ql-size {
+    width: 98px;
+}
+.ql-snow .ql-picker.ql-size .ql-picker-label::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item::before {
+    content: "Normal";
+}
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
+    content: "Small";
+}
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
+    content: "Large";
+}
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
+    content: "Huge";
+}
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
+    font-size: 10px;
+}
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
+    font-size: 18px;
+}
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
+    font-size: 32px;
+}
+.ql-snow .ql-color-picker.ql-background .ql-picker-item {
+    background-color: #fff;
+}
+.ql-snow .ql-color-picker.ql-color .ql-picker-item {
+    background-color: #000;
+}
+.ql-toolbar.ql-snow {
+    border: 1px solid #ccc;
+    box-sizing: border-box;
+    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+    padding: 8px;
+}
+.ql-toolbar.ql-snow .ql-formats {
+    margin-right: 15px;
+}
+.ql-toolbar.ql-snow .ql-picker-label {
+    border: 1px solid transparent;
+}
+.ql-toolbar.ql-snow .ql-picker-options {
+    border: 1px solid transparent;
+    box-shadow: rgb(0 0 0 / 20%) 0 2px 8px;
+}
+.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label {
+    border-color: #ccc;
+}
+.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options {
+    border-color: #ccc;
+}
+.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item.ql-selected,
+.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item:hover {
+    border-color: #000;
+}
+.ql-toolbar.ql-snow + .ql-container.ql-snow {
+    border-top: 0;
+}
+.ql-snow .ql-tooltip {
+    background-color: #fff;
+    border: 1px solid #ccc;
+    box-shadow: 0 0 5px #ddd;
+    color: #444;
+    padding: 5px 12px;
+    white-space: nowrap;
+}
+.ql-snow .ql-tooltip::before {
+    content: "Visit URL:";
+    line-height: 26px;
+    margin-right: 8px;
+}
+.ql-snow .ql-tooltip input[type="text"] {
+    display: none;
+    border: 1px solid #ccc;
+    font-size: 13px;
+    height: 26px;
+    margin: 0;
+    padding: 3px 5px;
+    width: 170px;
+}
+.ql-snow .ql-tooltip a.ql-preview {
+    display: inline-block;
+    max-width: 200px;
+    overflow-x: hidden;
+    text-overflow: ellipsis;
+    vertical-align: top;
+}
+.ql-snow .ql-tooltip a.ql-action::after {
+    border-right: 1px solid #ccc;
+    content: "Edit";
+    margin-left: 16px;
+    padding-right: 8px;
+}
+.ql-snow .ql-tooltip a.ql-remove::before {
+    content: "Remove";
+    margin-left: 8px;
+}
+.ql-snow .ql-tooltip a {
+    line-height: 26px;
+}
+.ql-snow .ql-tooltip.ql-editing a.ql-preview,
+.ql-snow .ql-tooltip.ql-editing a.ql-remove {
+    display: none;
+}
+.ql-snow .ql-tooltip.ql-editing input[type="text"] {
+    display: inline-block;
+}
+.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
+    border-right: 0;
+    content: "Save";
+    padding-right: 0;
+}
+.ql-snow .ql-tooltip[data-mode="link"]::before {
+    content: "Enter link:";
+}
+.ql-snow .ql-tooltip[data-mode="formula"]::before {
+    content: "Enter formula:";
+}
+.ql-snow .ql-tooltip[data-mode="video"]::before {
+    content: "Enter video:";
+}
+.ql-snow a {
+    color: #06c;
+}
+.ql-container.ql-snow {
+    border: 1px solid #ccc;
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 388 - 343
src/assets/iconfont/demo_index.html


+ 127 - 115
src/assets/iconfont/iconfont.css

@@ -1,9 +1,9 @@
 @font-face {
-  font-family: "iconfont"; /* Project id 4723464 */
-  src: url('iconfont.woff2?t=1737624547968') format('woff2'),
-       url('iconfont.woff?t=1737624547968') format('woff'),
-       url('iconfont.ttf?t=1737624547968') format('truetype'),
-       url('iconfont.svg?t=1737624547968#iconfont') format('svg');
+  font-family: "iconfont"; /* Project id 4820751 */
+  src: url('iconfont.woff2?t=1738918368855') format('woff2'),
+       url('iconfont.woff?t=1738918368855') format('woff'),
+       url('iconfont.ttf?t=1738918368855') format('truetype'),
+       url('iconfont.svg?t=1738918368855#iconfont') format('svg');
 }
 
 .iconfont {
@@ -14,96 +14,124 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
-.icon-share:before {
-  content: "\e7d6";
+.icon-Female:before {
+  content: "\e7e5";
 }
 
-.icon-Thumbs-up:before {
-  content: "\e7e3";
+.icon-Male:before {
+  content: "\e7e4";
 }
 
-.icon-chatgroup:before {
-  content: "\e7de";
+.icon-profile:before {
+  content: "\e60f";
 }
 
-.icon-unlock:before {
-  content: "\e7e0";
+.icon-arrow_right:before {
+  content: "\e60d";
 }
 
-.icon-more:before {
-  content: "\e7e1";
+.icon-fire-fill:before {
+  content: "\e866";
 }
 
-.icon-location:before {
-  content: "\e7e2";
+.icon-eye-fill:before {
+  content: "\e869";
 }
 
-.icon-lock:before {
-  content: "\e7dd";
+.icon-like-fill:before {
+  content: "\e86a";
 }
 
-.icon-edit-1:before {
-  content: "\e7d7";
+.icon-location-fill:before {
+  content: "\e868";
 }
 
-.icon-logout:before {
-  content: "\e7d8";
+.icon-close:before {
+  content: "\e7fc";
 }
 
-.icon-chat-message:before {
-  content: "\e7d9";
+.icon-caret-down:before {
+  content: "\e8ec";
 }
 
-.icon-chevron-down:before {
-  content: "\e7da";
+.icon-plus-circle-fill:before {
+  content: "\e845";
 }
 
-.icon-caret-down-small:before {
-  content: "\e7db";
+.icon-image-fill:before {
+  content: "\e860";
 }
 
-.icon-cart:before {
-  content: "\e7dc";
+.icon-setting:before {
+  content: "\e78e";
 }
 
-.icon-delete-three:before {
-  content: "\e7d5";
+.icon-edit-square:before {
+  content: "\e791";
 }
 
-.icon-copy:before {
-  content: "\e7d3";
+.icon-delete-two:before {
+  content: "\e7c3";
 }
 
-.icon-quote:before {
-  content: "\e7d4";
+.icon-plus:before {
+  content: "\e8fe";
 }
 
-.icon-send:before {
-  content: "\e7d2";
+.icon-star-fill:before {
+  content: "\e86b";
 }
 
-.icon-comment-two:before {
-  content: "\e7cd";
+.icon-star:before {
+  content: "\e7df";
 }
 
-.icon-eit:before {
-  content: "\e7ce";
+.icon-smile:before {
+  content: "\e783";
 }
 
-.icon-comments:before {
-  content: "\e7cf";
+.icon-message:before {
+  content: "\e78a";
 }
 
-.icon-comment-one:before {
-  content: "\e7d0";
+.icon-menu:before {
+  content: "\e7f4";
 }
 
-.icon-like:before {
-  content: "\e7d1";
+.icon-right:before {
+  content: "\e7eb";
 }
 
-.icon-set-top:before {
-  content: "\e7c7";
+.icon-left:before {
+  content: "\e7ec";
+}
+
+.icon-info-circle:before {
+  content: "\e77e";
+}
+
+.icon-caret-up:before {
+  content: "\e8ed";
+}
+
+.icon-close-one:before {
+  content: "\e7c0";
+}
+
+.icon-slightly-smiling-face:before {
+  content: "\e7c1";
+}
+
+.icon-pic:before {
+  content: "\e7c2";
+}
+
+.icon-voice-one:before {
+  content: "\e7c4";
+}
+
+.icon-peoples-two:before {
+  content: "\e7c5";
 }
 
 .icon-pic-two:before {
@@ -130,115 +158,99 @@
   content: "\e7cc";
 }
 
-.icon-close-one:before {
-  content: "\e7c0";
-}
-
-.icon-slightly-smiling-face:before {
-  content: "\e7c1";
-}
-
-.icon-pic:before {
-  content: "\e7c2";
-}
-
-.icon-voice-one:before {
-  content: "\e7c4";
-}
-
-.icon-peoples-two:before {
-  content: "\e7c5";
+.icon-set-top:before {
+  content: "\e7c7";
 }
 
-.icon-caret-up:before {
-  content: "\e8ed";
+.icon-comment-two:before {
+  content: "\e7cd";
 }
 
-.icon-info-circle:before {
-  content: "\e77e";
+.icon-eit:before {
+  content: "\e7ce";
 }
 
-.icon-left:before {
-  content: "\e7ec";
+.icon-comments:before {
+  content: "\e7cf";
 }
 
-.icon-right:before {
-  content: "\e7eb";
+.icon-comment-one:before {
+  content: "\e7d0";
 }
 
-.icon-menu:before {
-  content: "\e7f4";
+.icon-like:before {
+  content: "\e7d1";
 }
 
-.icon-message:before {
-  content: "\e78a";
+.icon-send:before {
+  content: "\e7d2";
 }
 
-.icon-smile:before {
-  content: "\e783";
+.icon-copy:before {
+  content: "\e7d3";
 }
 
-.icon-star:before {
-  content: "\e7df";
+.icon-quote:before {
+  content: "\e7d4";
 }
 
-.icon-star-fill:before {
-  content: "\e86b";
+.icon-delete-three:before {
+  content: "\e7d5";
 }
 
-.icon-plus:before {
-  content: "\e8fe";
+.icon-edit-1:before {
+  content: "\e7d7";
 }
 
-.icon-edit-square:before {
-  content: "\e791";
+.icon-logout:before {
+  content: "\e7d8";
 }
 
-.icon-delete-two:before {
-  content: "\e7c3";
+.icon-chat-message:before {
+  content: "\e7d9";
 }
 
-.icon-setting:before {
-  content: "\e78e";
+.icon-chevron-down:before {
+  content: "\e7da";
 }
 
-.icon-plus-circle-fill:before {
-  content: "\e845";
+.icon-caret-down-small:before {
+  content: "\e7db";
 }
 
-.icon-image-fill:before {
-  content: "\e860";
+.icon-cart:before {
+  content: "\e7dc";
 }
 
-.icon-close:before {
-  content: "\e7fc";
+.icon-lock:before {
+  content: "\e7dd";
 }
 
-.icon-caret-down:before {
-  content: "\e8ec";
+.icon-unlock:before {
+  content: "\e7e0";
 }
 
-.icon-location-fill:before {
-  content: "\e868";
+.icon-more:before {
+  content: "\e7e1";
 }
 
-.icon-fire-fill:before {
-  content: "\e866";
+.icon-location:before {
+  content: "\e7e2";
 }
 
-.icon-eye-fill:before {
-  content: "\e869";
+.icon-chatgroup:before {
+  content: "\e7de";
 }
 
-.icon-like-fill:before {
-  content: "\e86a";
+.icon-share:before {
+  content: "\e7d6";
 }
 
-.icon-arrow_right:before {
-  content: "\e60d";
+.icon-Thumbs-up:before {
+  content: "\e7e3";
 }
 
-.icon-profile:before {
-  content: "\e60f";
+.icon-eye:before {
+  content: "\e78f";
 }
 

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
src/assets/iconfont/iconfont.js


+ 302 - 281
src/assets/iconfont/iconfont.json

@@ -1,170 +1,219 @@
 {
-  "id": "4723464",
-  "name": "xyy-web",
+  "id": "4820751",
+  "name": "xxy-web-two",
   "font_family": "iconfont",
   "css_prefix_text": "icon-",
-  "description": "",
+  "description": "逍遥游",
   "glyphs": [
     {
-      "icon_id": "43223178",
-      "name": "share",
-      "font_class": "share",
-      "unicode": "e7d6",
-      "unicode_decimal": 59350
+      "icon_id": "43272273",
+      "name": "Female",
+      "font_class": "Female",
+      "unicode": "e7e5",
+      "unicode_decimal": 59365
     },
     {
-      "icon_id": "43223177",
-      "name": "Thumbs-up",
-      "font_class": "Thumbs-up",
-      "unicode": "e7e3",
-      "unicode_decimal": 59363
+      "icon_id": "43272253",
+      "name": "Male",
+      "font_class": "Male",
+      "unicode": "e7e4",
+      "unicode_decimal": 59364
     },
     {
-      "icon_id": "43210372",
-      "name": "chatgroup",
-      "font_class": "chatgroup",
-      "unicode": "e7de",
-      "unicode_decimal": 59358
+      "icon_id": "11813183",
+      "name": "profile",
+      "font_class": "profile",
+      "unicode": "e60f",
+      "unicode_decimal": 58895
     },
     {
-      "icon_id": "43209870",
-      "name": "unlock",
-      "font_class": "unlock",
-      "unicode": "e7e0",
-      "unicode_decimal": 59360
+      "icon_id": "14678715",
+      "name": "arrow_right",
+      "font_class": "arrow_right",
+      "unicode": "e60d",
+      "unicode_decimal": 58893
     },
     {
-      "icon_id": "43209869",
-      "name": "more",
-      "font_class": "more",
-      "unicode": "e7e1",
-      "unicode_decimal": 59361
+      "icon_id": "4936652",
+      "name": "fire-fill",
+      "font_class": "fire-fill",
+      "unicode": "e866",
+      "unicode_decimal": 59494
     },
     {
-      "icon_id": "43209868",
-      "name": "location",
-      "font_class": "location",
-      "unicode": "e7e2",
-      "unicode_decimal": 59362
+      "icon_id": "4936668",
+      "name": "eye-fill",
+      "font_class": "eye-fill",
+      "unicode": "e869",
+      "unicode_decimal": 59497
     },
     {
-      "icon_id": "43209895",
-      "name": "lock",
-      "font_class": "lock",
-      "unicode": "e7dd",
-      "unicode_decimal": 59357
+      "icon_id": "4936669",
+      "name": "like-fill",
+      "font_class": "like-fill",
+      "unicode": "e86a",
+      "unicode_decimal": 59498
     },
     {
-      "icon_id": "43103287",
-      "name": "edit-1",
-      "font_class": "edit-1",
-      "unicode": "e7d7",
-      "unicode_decimal": 59351
+      "icon_id": "4936659",
+      "name": "location-fill",
+      "font_class": "location-fill",
+      "unicode": "e868",
+      "unicode_decimal": 59496
     },
     {
-      "icon_id": "43103286",
-      "name": "logout",
-      "font_class": "logout",
-      "unicode": "e7d8",
-      "unicode_decimal": 59352
+      "icon_id": "4767096",
+      "name": "close",
+      "font_class": "close",
+      "unicode": "e7fc",
+      "unicode_decimal": 59388
     },
     {
-      "icon_id": "43103284",
-      "name": "chat-message",
-      "font_class": "chat-message",
-      "unicode": "e7d9",
-      "unicode_decimal": 59353
+      "icon_id": "6598339",
+      "name": "caret-down",
+      "font_class": "caret-down",
+      "unicode": "e8ec",
+      "unicode_decimal": 59628
     },
     {
-      "icon_id": "43103285",
-      "name": "chevron-down",
-      "font_class": "chevron-down",
-      "unicode": "e7da",
-      "unicode_decimal": 59354
+      "icon_id": "4936486",
+      "name": "plus-circle-fill",
+      "font_class": "plus-circle-fill",
+      "unicode": "e845",
+      "unicode_decimal": 59461
     },
     {
-      "icon_id": "43103282",
-      "name": "caret-down-small",
-      "font_class": "caret-down-small",
-      "unicode": "e7db",
-      "unicode_decimal": 59355
+      "icon_id": "4936630",
+      "name": "image-fill",
+      "font_class": "image-fill",
+      "unicode": "e860",
+      "unicode_decimal": 59488
     },
     {
-      "icon_id": "43103283",
-      "name": "cart",
-      "font_class": "cart",
-      "unicode": "e7dc",
-      "unicode_decimal": 59356
+      "icon_id": "4765891",
+      "name": "setting",
+      "font_class": "setting",
+      "unicode": "e78e",
+      "unicode_decimal": 59278
     },
     {
-      "icon_id": "43078637",
-      "name": "delete-three",
-      "font_class": "delete-three",
-      "unicode": "e7d5",
-      "unicode_decimal": 59349
+      "icon_id": "4765957",
+      "name": "edit-square",
+      "font_class": "edit-square",
+      "unicode": "e791",
+      "unicode_decimal": 59281
     },
     {
-      "icon_id": "43078629",
-      "name": "copy",
-      "font_class": "copy",
-      "unicode": "e7d3",
-      "unicode_decimal": 59347
+      "icon_id": "4766676",
+      "name": "delete",
+      "font_class": "delete-two",
+      "unicode": "e7c3",
+      "unicode_decimal": 59331
     },
     {
-      "icon_id": "43078628",
-      "name": "quote",
-      "font_class": "quote",
-      "unicode": "e7d4",
-      "unicode_decimal": 59348
+      "icon_id": "7834345",
+      "name": "plus",
+      "font_class": "plus",
+      "unicode": "e8fe",
+      "unicode_decimal": 59646
     },
     {
-      "icon_id": "43073198",
-      "name": "send",
-      "font_class": "send",
-      "unicode": "e7d2",
-      "unicode_decimal": 59346
+      "icon_id": "4936673",
+      "name": "star-fill",
+      "font_class": "star-fill",
+      "unicode": "e86b",
+      "unicode_decimal": 59499
     },
     {
-      "icon_id": "43073203",
-      "name": "comment-two",
-      "font_class": "comment-two",
-      "unicode": "e7cd",
-      "unicode_decimal": 59341
+      "icon_id": "4766954",
+      "name": "star",
+      "font_class": "star",
+      "unicode": "e7df",
+      "unicode_decimal": 59359
     },
     {
-      "icon_id": "43073202",
-      "name": "eit",
-      "font_class": "eit",
-      "unicode": "e7ce",
-      "unicode_decimal": 59342
+      "icon_id": "4765739",
+      "name": "smile",
+      "font_class": "smile",
+      "unicode": "e783",
+      "unicode_decimal": 59267
     },
     {
-      "icon_id": "43073200",
-      "name": "comments",
-      "font_class": "comments",
-      "unicode": "e7cf",
-      "unicode_decimal": 59343
+      "icon_id": "4765866",
+      "name": "message",
+      "font_class": "message",
+      "unicode": "e78a",
+      "unicode_decimal": 59274
     },
     {
-      "icon_id": "43073199",
-      "name": "comment-one",
-      "font_class": "comment-one",
-      "unicode": "e7d0",
-      "unicode_decimal": 59344
+      "icon_id": "4767059",
+      "name": "menu",
+      "font_class": "menu",
+      "unicode": "e7f4",
+      "unicode_decimal": 59380
     },
     {
-      "icon_id": "43073201",
-      "name": "like",
-      "font_class": "like",
-      "unicode": "e7d1",
-      "unicode_decimal": 59345
+      "icon_id": "4767011",
+      "name": "right",
+      "font_class": "right",
+      "unicode": "e7eb",
+      "unicode_decimal": 59371
     },
     {
-      "icon_id": "43063382",
-      "name": "set-top",
-      "font_class": "set-top",
-      "unicode": "e7c7",
-      "unicode_decimal": 59335
+      "icon_id": "4767012",
+      "name": "left",
+      "font_class": "left",
+      "unicode": "e7ec",
+      "unicode_decimal": 59372
+    },
+    {
+      "icon_id": "4765727",
+      "name": "info-circle",
+      "font_class": "info-circle",
+      "unicode": "e77e",
+      "unicode_decimal": 59262
+    },
+    {
+      "icon_id": "6598341",
+      "name": "caret-up",
+      "font_class": "caret-up",
+      "unicode": "e8ed",
+      "unicode_decimal": 59629
+    },
+    {
+      "icon_id": "43062458",
+      "name": "close-one",
+      "font_class": "close-one",
+      "unicode": "e7c0",
+      "unicode_decimal": 59328
+    },
+    {
+      "icon_id": "43062459",
+      "name": "slightly-smiling-face",
+      "font_class": "slightly-smiling-face",
+      "unicode": "e7c1",
+      "unicode_decimal": 59329
+    },
+    {
+      "icon_id": "43062460",
+      "name": "pic",
+      "font_class": "pic",
+      "unicode": "e7c2",
+      "unicode_decimal": 59330
+    },
+    {
+      "icon_id": "43062461",
+      "name": "voice-one",
+      "font_class": "voice-one",
+      "unicode": "e7c4",
+      "unicode_decimal": 59332
+    },
+    {
+      "icon_id": "43062462",
+      "name": "peoples-two ",
+      "font_class": "peoples-two",
+      "unicode": "e7c5",
+      "unicode_decimal": 59333
     },
     {
       "icon_id": "43063327",
@@ -188,221 +237,193 @@
       "unicode_decimal": 59337
     },
     {
-      "icon_id": "43063325",
-      "name": "jubaoguanli",
-      "font_class": "jubaoguanli",
-      "unicode": "e7ca",
-      "unicode_decimal": 59338
-    },
-    {
-      "icon_id": "43063323",
-      "name": "setting",
-      "font_class": "setting-one",
-      "unicode": "e7cb",
-      "unicode_decimal": 59339
-    },
-    {
-      "icon_id": "43063322",
-      "name": "delete-one",
-      "font_class": "delete-one",
-      "unicode": "e7cc",
-      "unicode_decimal": 59340
-    },
-    {
-      "icon_id": "43062458",
-      "name": "close-one",
-      "font_class": "close-one",
-      "unicode": "e7c0",
-      "unicode_decimal": 59328
-    },
-    {
-      "icon_id": "43062459",
-      "name": "slightly-smiling-face",
-      "font_class": "slightly-smiling-face",
-      "unicode": "e7c1",
-      "unicode_decimal": 59329
+      "icon_id": "43063325",
+      "name": "jubaoguanli",
+      "font_class": "jubaoguanli",
+      "unicode": "e7ca",
+      "unicode_decimal": 59338
     },
     {
-      "icon_id": "43062460",
-      "name": "pic",
-      "font_class": "pic",
-      "unicode": "e7c2",
-      "unicode_decimal": 59330
+      "icon_id": "43063323",
+      "name": "setting",
+      "font_class": "setting-one",
+      "unicode": "e7cb",
+      "unicode_decimal": 59339
     },
     {
-      "icon_id": "43062461",
-      "name": "voice-one",
-      "font_class": "voice-one",
-      "unicode": "e7c4",
-      "unicode_decimal": 59332
+      "icon_id": "43063322",
+      "name": "delete",
+      "font_class": "delete-one",
+      "unicode": "e7cc",
+      "unicode_decimal": 59340
     },
     {
-      "icon_id": "43062462",
-      "name": "peoples-two",
-      "font_class": "peoples-two",
-      "unicode": "e7c5",
-      "unicode_decimal": 59333
+      "icon_id": "43063382",
+      "name": "set-top",
+      "font_class": "set-top",
+      "unicode": "e7c7",
+      "unicode_decimal": 59335
     },
     {
-      "icon_id": "6598341",
-      "name": "caret-up",
-      "font_class": "caret-up",
-      "unicode": "e8ed",
-      "unicode_decimal": 59629
+      "icon_id": "43073203",
+      "name": "comment-two",
+      "font_class": "comment-two",
+      "unicode": "e7cd",
+      "unicode_decimal": 59341
     },
     {
-      "icon_id": "4765727",
-      "name": "info-circle",
-      "font_class": "info-circle",
-      "unicode": "e77e",
-      "unicode_decimal": 59262
+      "icon_id": "43073202",
+      "name": "tiji",
+      "font_class": "eit",
+      "unicode": "e7ce",
+      "unicode_decimal": 59342
     },
     {
-      "icon_id": "4767012",
-      "name": "left",
-      "font_class": "left",
-      "unicode": "e7ec",
-      "unicode_decimal": 59372
+      "icon_id": "43073200",
+      "name": "comments",
+      "font_class": "comments",
+      "unicode": "e7cf",
+      "unicode_decimal": 59343
     },
     {
-      "icon_id": "4767011",
-      "name": "right",
-      "font_class": "right",
-      "unicode": "e7eb",
-      "unicode_decimal": 59371
+      "icon_id": "43073199",
+      "name": "comment-one",
+      "font_class": "comment-one",
+      "unicode": "e7d0",
+      "unicode_decimal": 59344
     },
     {
-      "icon_id": "4767059",
-      "name": "menu",
-      "font_class": "menu",
-      "unicode": "e7f4",
-      "unicode_decimal": 59380
+      "icon_id": "43073201",
+      "name": "like",
+      "font_class": "like",
+      "unicode": "e7d1",
+      "unicode_decimal": 59345
     },
     {
-      "icon_id": "4765866",
-      "name": "message",
-      "font_class": "message",
-      "unicode": "e78a",
-      "unicode_decimal": 59274
+      "icon_id": "43073198",
+      "name": "send",
+      "font_class": "send",
+      "unicode": "e7d2",
+      "unicode_decimal": 59346
     },
     {
-      "icon_id": "4765739",
-      "name": "smile",
-      "font_class": "smile",
-      "unicode": "e783",
-      "unicode_decimal": 59267
+      "icon_id": "43078629",
+      "name": "copy",
+      "font_class": "copy",
+      "unicode": "e7d3",
+      "unicode_decimal": 59347
     },
     {
-      "icon_id": "4766954",
-      "name": "star",
-      "font_class": "star",
-      "unicode": "e7df",
-      "unicode_decimal": 59359
+      "icon_id": "43078628",
+      "name": "quote",
+      "font_class": "quote",
+      "unicode": "e7d4",
+      "unicode_decimal": 59348
     },
     {
-      "icon_id": "4936673",
-      "name": "star-fill",
-      "font_class": "star-fill",
-      "unicode": "e86b",
-      "unicode_decimal": 59499
+      "icon_id": "43078637",
+      "name": "delete-three",
+      "font_class": "delete-three",
+      "unicode": "e7d5",
+      "unicode_decimal": 59349
     },
     {
-      "icon_id": "7834345",
-      "name": "plus",
-      "font_class": "plus",
-      "unicode": "e8fe",
-      "unicode_decimal": 59646
+      "icon_id": "43103287",
+      "name": "edit-1",
+      "font_class": "edit-1",
+      "unicode": "e7d7",
+      "unicode_decimal": 59351
     },
     {
-      "icon_id": "4765957",
-      "name": "edit-square",
-      "font_class": "edit-square",
-      "unicode": "e791",
-      "unicode_decimal": 59281
+      "icon_id": "43103286",
+      "name": "logout",
+      "font_class": "logout",
+      "unicode": "e7d8",
+      "unicode_decimal": 59352
     },
     {
-      "icon_id": "4766676",
-      "name": "delete-two",
-      "font_class": "delete-two",
-      "unicode": "e7c3",
-      "unicode_decimal": 59331
+      "icon_id": "43103284",
+      "name": "chat-message",
+      "font_class": "chat-message",
+      "unicode": "e7d9",
+      "unicode_decimal": 59353
     },
     {
-      "icon_id": "4765891",
-      "name": "setting",
-      "font_class": "setting",
-      "unicode": "e78e",
-      "unicode_decimal": 59278
+      "icon_id": "43103285",
+      "name": "chevron-down",
+      "font_class": "chevron-down",
+      "unicode": "e7da",
+      "unicode_decimal": 59354
     },
     {
-      "icon_id": "4936486",
-      "name": "plus-circle-fill",
-      "font_class": "plus-circle-fill",
-      "unicode": "e845",
-      "unicode_decimal": 59461
+      "icon_id": "43103282",
+      "name": "caret-down-small",
+      "font_class": "caret-down-small",
+      "unicode": "e7db",
+      "unicode_decimal": 59355
     },
     {
-      "icon_id": "4936630",
-      "name": "image-fill",
-      "font_class": "image-fill",
-      "unicode": "e860",
-      "unicode_decimal": 59488
+      "icon_id": "43103283",
+      "name": "cart",
+      "font_class": "cart",
+      "unicode": "e7dc",
+      "unicode_decimal": 59356
     },
     {
-      "icon_id": "4767096",
-      "name": "close",
-      "font_class": "close",
-      "unicode": "e7fc",
-      "unicode_decimal": 59388
+      "icon_id": "43209895",
+      "name": "lock",
+      "font_class": "lock",
+      "unicode": "e7dd",
+      "unicode_decimal": 59357
     },
     {
-      "icon_id": "6598339",
-      "name": "caret-down",
-      "font_class": "caret-down",
-      "unicode": "e8ec",
-      "unicode_decimal": 59628
+      "icon_id": "43209870",
+      "name": "unlock",
+      "font_class": "unlock",
+      "unicode": "e7e0",
+      "unicode_decimal": 59360
     },
     {
-      "icon_id": "4936659",
-      "name": "location-fill",
-      "font_class": "location-fill",
-      "unicode": "e868",
-      "unicode_decimal": 59496
+      "icon_id": "43209869",
+      "name": "more",
+      "font_class": "more",
+      "unicode": "e7e1",
+      "unicode_decimal": 59361
     },
     {
-      "icon_id": "4936652",
-      "name": "fire-fill",
-      "font_class": "fire-fill",
-      "unicode": "e866",
-      "unicode_decimal": 59494
+      "icon_id": "43209868",
+      "name": "location",
+      "font_class": "location",
+      "unicode": "e7e2",
+      "unicode_decimal": 59362
     },
     {
-      "icon_id": "4936668",
-      "name": "eye-fill",
-      "font_class": "eye-fill",
-      "unicode": "e869",
-      "unicode_decimal": 59497
+      "icon_id": "43210372",
+      "name": "chatgroup",
+      "font_class": "chatgroup",
+      "unicode": "e7de",
+      "unicode_decimal": 59358
     },
     {
-      "icon_id": "4936669",
-      "name": "like-fill",
-      "font_class": "like-fill",
-      "unicode": "e86a",
-      "unicode_decimal": 59498
+      "icon_id": "43223178",
+      "name": "share",
+      "font_class": "share",
+      "unicode": "e7d6",
+      "unicode_decimal": 59350
     },
     {
-      "icon_id": "14678715",
-      "name": "arrow_right",
-      "font_class": "arrow_right",
-      "unicode": "e60d",
-      "unicode_decimal": 58893
+      "icon_id": "43223177",
+      "name": "Thumbs-up",
+      "font_class": "Thumbs-up",
+      "unicode": "e7e3",
+      "unicode_decimal": 59363
     },
     {
-      "icon_id": "11813183",
-      "name": "profile",
-      "font_class": "profile",
-      "unicode": "e60f",
-      "unicode_decimal": 58895
+      "icon_id": "4765896",
+      "name": "eye",
+      "font_class": "eye",
+      "unicode": "e78f",
+      "unicode_decimal": 59279
     }
   ]
 }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 12 - 13
src/assets/iconfont/iconfont.svg


BIN
src/assets/iconfont/iconfont.ttf


BIN
src/assets/iconfont/iconfont.woff


BIN
src/assets/iconfont/iconfont.woff2


+ 9 - 0
src/assets/img/profile-others/Female.svg

@@ -0,0 +1,9 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Female (&#229;&#165;&#179;&#230;&#128;&#167;)">
+<g id="Female (&#229;&#165;&#179;&#230;&#128;&#167;)_2">
+<path id="Vector" d="M11.1933 2.87323C9.59872 1.27859 7.01326 1.27859 5.41864 2.87323C4.62131 3.67055 4.22266 4.71556 4.22266 5.76057C4.22266 6.80559 4.62131 7.8506 5.41864 8.64793C7.01326 10.2426 9.59872 10.2426 11.1933 8.64793C12.788 7.0533 12.788 4.46788 11.1933 2.87323Z" stroke="#FF0FE3" stroke-linejoin="round"/>
+<path id="Vector_2" d="M5.38515 8.6145L1.67285 12.3268" stroke="#FF0FE3" stroke-linecap="round" stroke-linejoin="round"/>
+<path id="Vector_3" d="M5.59122 12.1205L1.87891 8.4082" stroke="#FF0FE3" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+</g>
+</svg>

+ 5 - 0
src/assets/img/profile-others/Like.svg

@@ -0,0 +1,5 @@
+<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Like (&#229;&#150;&#156;&#230;&#172;&#162;)">
+<path id="Vector" d="M4.99967 3.16675C2.97463 3.16675 1.33301 4.80838 1.33301 6.83341C1.33301 10.5001 5.66634 13.8334 7.99967 14.6088C10.333 13.8334 14.6663 10.5001 14.6663 6.83341C14.6663 4.80838 13.0247 3.16675 10.9997 3.16675C9.75957 3.16675 8.66324 3.78238 7.99967 4.72468C7.33611 3.78238 6.23977 3.16675 4.99967 3.16675Z" stroke="#999999" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+</svg>

+ 5 - 0
src/assets/img/profile-others/Like2.svg

@@ -0,0 +1,5 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Like (&#229;&#150;&#156;&#230;&#172;&#162;)">
+<path id="Vector" d="M4.99967 2.6665C2.97463 2.6665 1.33301 4.30814 1.33301 6.33317C1.33301 9.99984 5.66634 13.3332 7.99967 14.1086C10.333 13.3332 14.6663 9.99984 14.6663 6.33317C14.6663 4.30814 13.0247 2.6665 10.9997 2.6665C9.75957 2.6665 8.66324 3.28214 7.99967 4.22444C7.33611 3.28214 6.23977 2.6665 4.99967 2.6665Z" fill="#FF476A"/>
+</g>
+</svg>

BIN
src/assets/img/topic/topic.png


BIN
src/assets/img/topic/yj.png


+ 4 - 5
src/components/CreateNote/Form.vue

@@ -113,11 +113,10 @@
             >
               <template #icon>
                 <div class="w-44 h-44 rounded-full mr-8 overflow-hidden">
-                  <img
-                    v-if="item?.groupAvatarUrl"
-                    class="w-full h-full object-cover"
-                    :src="item?.groupAvatarUrl"
-                    alt=""
+                  <MultiHeader
+                    v-if="item?.memberHeadImg"
+                    :size="44"
+                    :imgUrls="item?.memberHeadImg"
                   />
                 </div>
               </template>

+ 122 - 0
src/components/CreateNote/Mention.vue

@@ -0,0 +1,122 @@
+<template>
+  <div class="demo">
+    <Mentionable
+      :keys="['#', '@']"
+      :items="items"
+      offset="6"
+      filtering-disabled
+      @open="loadIssues()"
+      @search="loadIssues($event)"
+    >
+      <textarea
+        v-model="text"
+        rows="6"
+        class="input"
+        placeholder="Enter text and then type # to trigger the mention"
+      />
+
+      <template #no-result>
+        <div class="dim">
+          {{ loading ? 'Loading...' : 'No result' }}
+        </div>
+      </template>
+
+      <template #item-#="{ item }">
+        <div class="issue">
+          <span class="number">#{{ item.value }}</span>
+          <span class="dim">
+            {{ item.label }}
+          </span>
+        </div>
+      </template>
+    </Mentionable>
+
+    <div class="preview">{{ text }}</div>
+  </div>
+</template>
+
+<script setup>
+import { Mentionable } from 'vue-mention'
+const text = ref('')
+const items = ref()
+const loading = ref(false)
+
+const issues = [
+  {
+    value: 123,
+    label: 'Error with foo bar'
+  },
+  {
+    value: 42,
+    label: 'Cannot read line'
+  },
+  {
+    value: 77,
+    label: 'I have a feature suggestion'
+  }
+]
+
+async loadIssues (searchText = null) {
+      loading.value = true
+     items.value = await fetchIssues(searchText)
+     loading.value = true
+}
+
+function fetchIssues (searchText = null) {
+  return new Promise(resolve => {
+    setTimeout(() => {
+      if (!searchText) {
+        resolve(issues)
+      } else {
+        const reg = new RegExp(searchText, 'i')
+        resolve(issues.filter(issue => reg.test(issue.label)))
+      }
+    }, 1000)
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+.demo {
+  margin: 24px 0;
+}
+
+.input {
+  width: 100%;
+  border: #ccc 1px solid;
+  border-radius: 6px;
+  resize: vertical;
+  min-height: 42px;
+  padding: 12px;
+  box-sizing: border-box;
+  line-height: 1.2em;
+  font-size: inherit;
+}
+
+.issue {
+  padding: 4px 6px;
+  border-radius: 4px;
+  cursor: pointer;
+}
+
+.mention-selected .issue {
+  background: rgb(139, 212, 255);
+}
+
+.issue .number {
+  font-family: monospace;
+}
+
+.dim {
+  color: #666;
+}
+
+.preview {
+  font-family: monospace;
+  white-space: pre-wrap;
+  margin-top: 12px;
+  padding: 12px;
+  background: #f8f8f8;
+  border-radius: 6px;
+}
+</style>

+ 7 - 10
src/components/Home/TravelNotes/Item.vue

@@ -13,19 +13,16 @@
         v-if="itemData.isOriginal === 1"
         class="absolute top-0 right-0 h-28 w-60 bg-[#ffffff80] rounded-bl-xl flex items-center justify-center"
       >
-        <img
-          src="~/assets/img/home/original_label.png"
-          class="inline-block h-20 object-cover"
-        />
+        <img src="~/assets/img/home/original_label.png" class="inline-block h-20 object-cover" />
       </div>
     </van-image>
-    <div class="px-10">
+    <div class="box-border h-52 px-10">
       <div class="truncate w-full mt-8 font-semibold text-black text-base">
         {{ itemData.projectTitle }}
       </div>
-      <div class="truncate w-full mt-8 text-black-3 text-sm">
+      <!-- <div class="truncate w-full mt-8 text-black-3 text-sm">
         {{ itemData.remarks }}
-      </div>
+      </div> -->
     </div>
   </NuxtLink>
 </template>
@@ -34,9 +31,9 @@
 defineProps({
   itemData: {
     type: Object,
-    default: () => ({}),
-  },
-});
+    default: () => ({})
+  }
+})
 </script>
 
 <style lang="scss" scoped></style>

+ 11 - 18
src/components/Home/TravelNotes/index.client.vue

@@ -1,10 +1,6 @@
 <template>
   <div class="px-20 relative">
-    <HomeSectionHeader
-      title="旅行游记"
-      subTitle="Hot items"
-      more-to-url="/travel-notes"
-    />
+    <HomeSectionHeader title="旅行游记" subTitle="Hot items" more-to-url="/travel-notes" />
     <swiper-container ref="containerRef" class="swiper">
       <swiper-slide v-for="(item, index) in travelNotesList" :key="item.id">
         <HomeTravelNotesItem :itemData="item" />
@@ -14,10 +10,7 @@
           to="/travel-notes"
           class="border aspect-[267/230] border-[#DFE0E4] rounded-xl bg-[#FAFAFA] flex flex-col items-center justify-center text-xl text-black-3"
         >
-          <span
-            class="iconfont icon-arrow_right"
-            style="font-size: 24px"
-          ></span>
+          <span class="iconfont icon-arrow_right" style="font-size: 24px"></span>
           <span>了解更多</span>
         </NuxtLink>
       </swiper-slide>
@@ -61,28 +54,28 @@
 <script setup>
 const { data } = await useMyFetch(
   `website/tourism/projectTravelNotes/homeList?pageNum=1&pageSize=10`
-);
-const travelNotesList = computed(() => data.value?.dataList ?? []);
+)
+const travelNotesList = computed(() => data.value?.dataList ?? [])
 
-const containerRef = ref(null);
+const containerRef = ref(null)
 const swiper = useSwiper(containerRef, {
   autoplay: {
-    delay: 2000,
+    delay: 2000
   },
   freeMode: {
-    enabled: true,
+    enabled: true
   },
   // freeMode: true,
   slidesPerView: 1.2,
-  spaceBetween: 17,
-});
+  spaceBetween: 17
+})
 
 function handleLeftClick() {
-  swiper.prev();
+  swiper.prev()
 }
 
 function handleRightClick() {
-  swiper.next();
+  swiper.next()
 }
 </script>
 

+ 1 - 1
src/components/NavigationBar/LeftMenu.vue

@@ -151,7 +151,7 @@ const profileMenu = [
   },
   {
     title: '我的收藏',
-    to: '/profile/collection'
+    to: '/profile/collection?tab=travelNotes'
   },
 
   {

+ 56 - 0
src/components/Profile/Collection/Comment.vue

@@ -0,0 +1,56 @@
+<template>
+  <!-- 评论 -->
+  <NuxtLink
+    :to="`/yj/${itemData.travelNoteId}`"
+    class="bg-white p-10 box-border flex flex-wrap space-x-10 rounded-xl"
+  >
+    <div class="flex-1 w-0">
+      <div class="flex">
+        <div @click.prevent="" class="w-32 h-32 shrink-0 border rounded-full mr-8 overflow-hidden">
+          <img
+            v-if="itemData?.headImage"
+            :src="itemData?.headImage"
+            class="w-full h-full object-cover"
+            alt=""
+          />
+          <img v-else :src="defaultAvatar" class="w-full h-full object-cover" alt="" />
+        </div>
+        <div>
+          <p class="text-black-6 text-sm mb-4 line-clamp-1">{{ itemData?.createName }}</p>
+          <p class="line-clamp-1 text-black-3 text-xl">{{ itemData?.commentContent }}</p>
+        </div>
+      </div>
+    </div>
+    <van-image
+      :src="formatImgSrc(itemData?.notice?.tourismUrlsAfterConvert) || noteDraftCoverBg"
+      width="71px"
+      height="47px"
+      radius="4px"
+      class="shrink-0"
+    ></van-image>
+    <div class="w-full flex justify-end">
+      <div
+        @click.prevent="$emit('onCancel')"
+        class="flex mt-20 font-semibold items-center justify-center h-25 border-current text-[#FF9300] text-sm border w-88 rounded-full mt-10"
+      >
+        取消收藏
+      </div>
+    </div>
+  </NuxtLink>
+</template>
+
+<script setup>
+import defaultAvatar from '~/assets/img/default_avatar.png'
+import noteDraftCoverBg from '~/assets/img/note-create/note_draft_cover_bg.jpg'
+
+defineProps({
+  itemData: {
+    type: Object,
+    default: () => {}
+  }
+})
+
+const emit = defineEmits(['onCancel'])
+</script>
+
+<style lang="scss" scoped></style>

+ 43 - 0
src/components/Profile/Collection/Topic.vue

@@ -0,0 +1,43 @@
+<template>
+  <!-- 话题 -->
+  <NuxtLink :to="`/yj/${itemData.id}`" class="bg-white p-10 box-border flex space-x-10 rounded-xl">
+    <van-image
+      :src="formatImgSrc(itemData.tourismUrlsAfterConvert)"
+      width="120px"
+      height="90px"
+      radius="8px"
+      class="shrink-0"
+    ></van-image>
+
+    <div class="flex-1 w-0 relative">
+      <div class="line-clamp-2 text-black-3 text-base font-semibold mb-4">
+        {{ itemData.name }}
+      </div>
+      <div class="mt-5 text-sm text-black-6">
+        <span class="iconfont icon-eye text-black-9 mr-8" style="font-size: 12px"></span>
+
+        {{ itemData.viewCount }}
+      </div>
+
+      <div
+        @click.prevent="$emit('onCancel')"
+        class="absolute bottom-0 right-0 flex mt-20 font-semibold items-center justify-center h-25 border-current text-[#FF9300] text-sm border w-88 rounded-full mt-10"
+      >
+        取消收藏
+      </div>
+    </div>
+  </NuxtLink>
+</template>
+
+<script setup>
+defineProps({
+  itemData: {
+    type: Object,
+    default: () => {}
+  }
+})
+
+const emit = defineEmits(['onCancel'])
+</script>
+
+<style lang="scss" scoped></style>

+ 19 - 21
src/components/Profile/Collection/TravelNoteCell.vue

@@ -1,41 +1,39 @@
 <template>
-  <NuxtLink
-    :to="`/yj/${itemData.id}`"
-    class="bg-white p-10 box-border flex space-x-10 rounded-xl"
-  >
+  <!-- 游记 -->
+  <NuxtLink :to="`/yj/${itemData.id}`" class="bg-white p-10 box-border flex space-x-10 rounded-xl">
     <van-image
-      :src="formatImgSrc(itemData.tourismUrlsAfterConvert)"
-      width="155px"
-      height="115px"
-      radius="10px"
+      :src="formatImgSrc(itemData.tourismUrlsAfterConvert) || noteDraftCoverBg"
+      width="120px"
+      height="90px"
+      radius="8px"
       class="shrink-0"
     ></van-image>
     <div class="flex-1 w-0">
-      <div class="truncate text-black-3 text-xl">
+      <div class="line-clamp-2 text-black-3 text-base font-semibold">
         {{ itemData.projectTitle }}
       </div>
-      <div class="line-clamp-3 mt-5 text-sm text-black-6">
-        {{ itemData.remarks }}
-      </div>
-      <div
-        @click.prevent="$emit('onCancel')"
-        class="flex items-center justify-center h-25 border-current text-[#FF4242] text-sm border w-64 rounded-md mt-10"
-      >
-        取消收藏
+      <div class="flex justify-end">
+        <div
+          @click.prevent="$emit('onCancel')"
+          class="flex mt-20 font-semibold items-center justify-center h-25 border-current text-[#FF9300] text-sm border w-88 rounded-full mt-10"
+        >
+          取消收藏
+        </div>
       </div>
     </div>
   </NuxtLink>
 </template>
 
 <script setup>
+import noteDraftCoverBg from '~/assets/img/note-create/note_draft_cover_bg.jpg'
 defineProps({
   itemData: {
     type: Object,
-    default: () => {},
-  },
-});
+    default: () => {}
+  }
+})
 
-const emit = defineEmits(["onCancel"]);
+const emit = defineEmits(['onCancel'])
 </script>
 
 <style lang="scss" scoped></style>

+ 1 - 1
src/components/Profile/Notes/Auditing/Item.vue

@@ -1,7 +1,7 @@
 <template>
   <van-swipe-cell>
     <div class="relative flex mb-10 border-[#fff] space-x-10 p-10 rounded-xl bg-[#FFF]">
-      <div class="w-147 h-109 border-box">
+      <div class="w-94 h-70 overflow-hidden rounded-lg border-box">
         <img
           :src="formatImgSrc(data?.tourismUrlsAfterConvert) || noteDraftCoverBg"
           class="w-full h-full shrink-0 object-cover"

+ 2 - 2
src/components/Profile/Notes/Draft/Item.vue

@@ -1,9 +1,9 @@
 <template>
   <van-swipe-cell>
     <div class="relative flex space-x-10 p-10 mb-10 bg-[#fff] rounded-xl">
-      <div class="w-147 h-109 border-box">
+      <div class="w-94 h-70 overflow-hidden rounded-lg border-box">
         <img
-          :src="formatImgSrc(data?.tourismUrlsAfterConvert) || noteDraftCoverBg"
+          :src="formatImgSrc(data?.imageUrl) || noteDraftCoverBg"
           class="w-full h-full shrink-0 object-cover"
         />
       </div>

+ 4 - 2
src/components/Profile/Notes/Draft/index.vue

@@ -47,11 +47,13 @@ async function getNotesList() {
   setLoading(true)
 
   try {
-    let { data } = await request('/website/tourism/publishTravelNotes/getDraftList', {
+    // let { data } = await request('/website/tourism/publishTravelNotes/getDraftList', {
+    let { data } = await request('/website/tourism/travelNotesTopic/getTravelNotesByState', {
       query: {
         pageNum: pageNum.value,
         pageSize: 10,
-        type: 0
+        // type: 0
+        state: 0
       }
     })
 

+ 31 - 14
src/components/Profile/Notes/Published/Item.vue

@@ -1,24 +1,27 @@
 <template>
   <van-swipe-cell>
-    <div class="relative group flex border-box p-10 mb-10 bg-[#fff] rounded-xl">
-      <div class="w-147 h-109 border-box">
+    <div class="relative group flex border-box p-12 mb-10 bg-[#fff] rounded-lg">
+      <div class="w-94 h-70 overflow-hidden border-box rounded-lg relative">
         <img
-          :src="formatImgSrc(data.tourismUrlsAfterConvert) || noteDraftCoverBg"
+          :src="formatImgSrc(data.imageUrl) || noteDraftCoverBg"
           class="w-full h-full shrink-0 object-cover"
         />
+        <div
+          v-if="data.isTop == 1"
+          class="absolute top-8 left-8 px-8 py-2 rounded-full text-white text-sm bg-[#F99406]/[0.6]"
+        >
+          置顶
+        </div>
       </div>
       <div class="w-152 ml-15">
-        <div class="min-h-70 w-full">
+        <div class="min-h-44 w-full">
           <NuxtLink
             :title="data?.projectTitle"
             :to="`/yj/${data?.id}`"
-            class="line-clamp-1 break-all text-base font-bold text-black-3 text-bold"
+            class="line-clamp-2 break-all text-base font-bold text-black-3 text-bold"
           >
             {{ data?.projectTitle }}
           </NuxtLink>
-          <div class="line-clamp-3 text-sm leading-[17px] break-all text-black-6">
-            {{ data?.remarks }}
-          </div>
         </div>
         <div class="flex space-x-4 text-sm text-[#FD9A00]">
           <div class="flex items-center space-x-2">
@@ -28,10 +31,6 @@
           <div class="pl-20 flex items-center space-x-2">
             <van-icon @click="$emit('onShare')" name="share-o" color="#FD9A00" />
           </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>
 
@@ -46,6 +45,15 @@
           text="下架"
           @click="$emit('onNoteDown')"
           type="danger"
+          color="#E37318"
+          class="delete-button"
+        />
+        <van-button
+          square
+          :text="showText(data.isTop)"
+          @click="$emit('onPinned', data)"
+          type="danger"
+          color="#D54941"
           class="delete-button"
         />
       </div>
@@ -63,8 +71,17 @@ const props = defineProps({
   }
 })
 
-defineEmits(['onNoteDown', 'onShare'])
-//
+defineEmits(['onNoteDown', 'onShare', 'onPinned'])
+
+// 展示置顶 是的文案
+// showText
+function showText(state) {
+  if (state == 1) {
+    return '取消置顶'
+  } else {
+    return '置顶'
+  }
+}
 </script>
 
 <style lang="scss" scoped>

+ 26 - 6
src/components/Profile/Notes/Published/index.vue

@@ -1,7 +1,6 @@
 <template>
   <div class="w-full box-border pb-10 px-1">
-    <!-- style="height: calc(100vh - 105px);" -->
-    <!-- 发布中 -->
+    <!-- 已发布 -->
     <ProfileNotesEmpty v-if="!noteList.length" />
     <div v-else-if="noteList.length">
       <van-list
@@ -16,10 +15,11 @@
       >
         <ProfileNotesPublishedItem
           v-for="item in noteList"
-          :key="item.id"
+          :key="item?.id"
           :data="item"
           @on-note-down="handleNoteDown(item)"
           @on-share="changeShowShare(item)"
+          @on-pinned="onPinned"
         />
       </van-list>
     </div>
@@ -29,7 +29,6 @@
 </template>
 
 <script setup>
-// import wx from 'weixin-js-sdk'
 const showShare = ref(false)
 
 const options = [
@@ -128,11 +127,13 @@ const getloadList = () => {
 async function getList() {
   setLoading(true)
   try {
-    let { data } = await request('/website/tourism/publishTravelNotes/getDraftList', {
+    // let { data } = await request('/website/tourism/publishTravelNotes/getDraftList', {
+    let { data } = await request('/website/tourism/travelNotesTopic/getTravelNotesByState', {
       query: {
         pageNum: pageNum.value,
         pageSize: 10,
-        type: 3
+        // type: 3
+        state: 3
       }
     })
 
@@ -182,6 +183,25 @@ async function handleNoteDown(item) {
     })
 }
 
+// 是否置顶
+async function onPinned(item) {
+  let body = {}
+  body.id = item.id
+  if (item.isTop == 1) {
+    body.isTop = 0
+  } else {
+    body.isTop = 1
+  }
+  const res = await request('/website/tourism/travelNotesTopic/toTop', {
+    method: 'post',
+    body
+  })
+
+  if (res && res.success) {
+    getList()
+  }
+}
+
 onMounted(() => {
   getList()
 })

+ 1 - 1
src/components/Profile/Notes/Rejected/Item.vue

@@ -1,7 +1,7 @@
 <template>
   <van-swipe-cell>
     <div class="relative group flex space-x-10 p-10 mb-10 bg-[#fff] rounded-xl">
-      <div class="w-147 h-109 border-box">
+      <div class="w-94 h-70 overflow-hidden rounded-lg border-box">
         <img
           :src="formatImgSrc(data?.tourismUrlsAfterConvert) || noteDraftCoverBg"
           class="w-full h-full shrink-0 object-cover"

+ 10 - 16
src/components/Profile/Notes/Tabs.vue

@@ -1,19 +1,12 @@
 <template>
-  <div>
-    <div class="flex items-center space-x-7 bg-[#F8F8F8] py-16">
-      <div
-        v-for="tab in tabs"
-        :key="tab.lable"
-        @click="handleClick(tab)"
-        class="flex w-78 h-34 text-14 cursor-pointer items-center justify-center rounded-full text-base transition-all"
-        :class="[
-          currentTab == tab.name ? 'bg-primary text-white font-bold' : 'bg-yellow text-black-3'
-        ]"
-      >
-        {{ tab.lable }}
-      </div>
-    </div>
-  </div>
+  <van-tabs
+    title-active-color="#FF9300"
+    title-inactive-color="#333333"
+    @click-tab="handleClick"
+    style="--van-tabs-bottom-bar-color: #ff9300; --van-tabs-bottom-bar-width: 30px; width: 100%"
+  >
+    <van-tab v-for="tab in tabs" :key="tab?.name" :title="tab?.lable"></van-tab>
+  </van-tabs>
 </template>
 
 <script setup>
@@ -26,7 +19,8 @@ const props = defineProps({
 
 const currentTab = useRouteQuery('tab')
 
-function handleClick(tab) {
+function handleClick({ title }) {
+  let tab = props.tabs?.find((item) => item?.lable == title)
   navigateTo({
     query: {
       tab: tab.name

+ 0 - 786
src/pages/_note-create/index.client.vue

@@ -1,786 +0,0 @@
-<template>
-  <div v-if="!loading" class="box-border pb-110">
-    <div v-if="!previewOptions.show">
-      <CreateNoteHeaderBanner v-model:bannerUrl="noteJson.travelNotesBanner" />
-      <div class="mt-12">
-        <van-cell-group class="border" inset>
-          <van-field
-            style="font-size: 16px; font-weight: 600"
-            size="large"
-            v-model="noteJson.projectTitle"
-            rows="1"
-            autosize
-            clearable
-            type="textarea"
-            @update:model-value="handleInsertOrEditProjectTitle"
-            placeholder="从这里开始游记大标题..."
-            maxlength="50"
-            show-word-limit
-          ></van-field>
-        </van-cell-group>
-      </div>
-      <CreateNoteForm
-        v-model:departureTime="noteJson.departureTime"
-        v-model:countTimes="noteJson.countTimes"
-        v-model:endPlace="noteJson.endPlace"
-        v-model:endPlaceId="noteJson.endPlaceId"
-        v-model:role="noteJson.role"
-        v-model:travelMode="noteJson.travelMode"
-        v-model:averageCost="noteJson.averageCost"
-        v-model:recommendationRate="noteJson.recommendationRate"
-        v-model:travelNumber="noteJson.travelNumber"
-      />
-
-      <div class="flex items-center pl-16 pt-16 w-full h-40">
-        <div class="w-2 h-14 bg-[#FF9300] mr-8"></div>
-        <h1 class="text-sm font-bold">编辑游记文章</h1>
-      </div>
-
-      <VueDraggable
-        :scroll="true"
-        :handle="'.drag'"
-        v-model="noteJson.travelNotesContent"
-        :options="dragOptions"
-      >
-        <template v-for="(item, index) in noteJson.travelNotesContent" :key="item.tmpId">
-          <div v-if="item.type === defaultSectionTitle.type" class="mb-12 box-border">
-            <van-cell-group
-              class="min-h-50 border flex h-full focus:border-[#FF9300] active:shadow-[_0px_6px_4px_0px_rgba(0,0,0,0.25)]"
-              inset
-            >
-              <van-field
-                v-model="item.content"
-                rows="1"
-                autosize
-                class="text-base h-full"
-                type="textarea"
-                trigger="onChange"
-                maxlength="30"
-                validate-trigger="onChange"
-                placeholder="请输入段落标题..."
-                @click-input="handleInsertOrEditTitleIndex(index)"
-                @update:model-value="handleInsertOrEditTitle"
-              >
-                <template #extra>
-                  <div class="flex">
-                    <div class="h-16 drag flex mr-10 items-center">
-                      <img class="w-16 h-16" src="~/assets/img/note-create/drag.svg" alt="" />
-                      <span class="text-sm text-black-3">拖拽</span>
-                    </div>
-                    <div class="w-16 h-16 z-0">
-                      <img
-                        @click="handleDeleteTitle(index)"
-                        class="w-full h-full"
-                        src="~/assets/img/note-create/delete.svg"
-                        alt=""
-                      />
-                    </div>
-                  </div>
-                </template>
-              </van-field>
-            </van-cell-group>
-          </div>
-
-          <div
-            v-else-if="item.type === defaultSectionContent.type"
-            class="mb-12 relative box-border"
-          >
-            <van-cell-group
-              class="border min-h-100 active:shadow-[_0px_6px_4px_0px_rgba(0,0,0,0.25)]"
-              inset
-            >
-              <van-field
-                autofocus
-                v-model="item.content"
-                rows="3"
-                autosize
-                maxlength="5000"
-                class="text-base"
-                type="textarea"
-                trigger="onChange"
-                validate-trigger="onChange"
-                placeholder="请在这里输入游记正文..."
-                @click-input="handleInsertOrEditTitleIndex(index)"
-                @update:model-value="handleInsertOrEditTitle"
-              >
-                <template #extra>
-                  <div class="flex">
-                    <div class="h-16 drag flex mr-10 items-center">
-                      <img class="w-16 h-16" src="~/assets/img/note-create/drag.svg" alt="" />
-                      <span class="text-sm text-black-3">拖拽</span>
-                    </div>
-                    <div class="w-16 h-16 z-0">
-                      <img
-                        @click="handleDeleteTitle(index)"
-                        class="w-full h-full"
-                        src="../../assets/img/note-create/delete.svg"
-                        alt=""
-                      />
-                    </div>
-                  </div>
-                </template>
-              </van-field>
-            </van-cell-group>
-          </div>
-          <!-- 有图片的样式 -->
-          <div v-else-if="item.type === defaultSectionImage.type" class="relative box-border mb-12">
-            <template v-if="item.content">
-              <div>
-                <van-cell-group
-                  class="border h-full active:shadow-[_0px_6px_4px_0px_rgba(0,0,0,0.25)]"
-                  inset
-                >
-                  <div v-if="item.content">
-                    <img :src="item.content" alt="Uploaded Image" />
-                  </div>
-                </van-cell-group>
-                <div
-                  class="w-150 rounded-xl box-border px-10 h-20 flex justify-between items-center h-16 absolute top-12 right-26 z-100 bg-[#fff]/[0.5]"
-                >
-                  <span @click="handleSaveCover(item)" class="text-sm text-black-3">
-                    设为封面图
-                  </span>
-                  <div class="drag flex items-center border-r-[1px] border-[#F3F3F3] pr-7">
-                    <img class="w-16 h-16" src="~/assets/img/note-create/drag.svg" alt="" />
-                    <span class="text-sm text-black-3">拖拽</span>
-                  </div>
-
-                  <div class="w-16 h-16 inline-block">
-                    <img
-                      @click="handleDeleteImage(index)"
-                      class="w-full h-full"
-                      src="../../assets/img/note-create/delete.svg"
-                      alt=""
-                    />
-                  </div>
-                </div>
-              </div>
-            </template>
-            <!-- 没有图片的样式 -->
-            <template v-else>
-              <div class="h-200">
-                <van-cell-group
-                  class="border h-full active:shadow-[_0px_6px_4px_0px_rgba(0,0,0,0.25)]"
-                  inset
-                >
-                  <div class="w-full h-full flex justify-center items-center">
-                    <button
-                      @click="handleSelectImage(index)"
-                      class="box-border active:bg-[#000]/[0.1] text-base flex justify-center items-center px-28 py-10 rounded-full border"
-                    >
-                      <div class="h-16 w-16 mr-8">
-                        <img class="w-full h-full" src="~/assets/img/note-create/img.svg" alt="" />
-                      </div>
-
-                      插入图片
-                    </button>
-                  </div>
-                </van-cell-group>
-
-                <div
-                  class="w-136 flex justify-between items-center h-16 absolute top-10 right-26 z-100"
-                >
-                  <span @click="showToast('请上传图片')" class="text-sm text-black-3">
-                    设为封面图
-                  </span>
-                  <div class="drag flex pr-7 items-center">
-                    <img class="w-16 h-16" src="~/assets/img/note-create/drag.svg" alt="" />
-                    <span class="text-sm text-black-3">拖拽</span>
-                  </div>
-
-                  <div class="w-16 h-16 inline-block">
-                    <img
-                      @click="handleDeleteImage(index)"
-                      class="w-full h-full drag"
-                      src="~/assets/img/note-create/delete.svg"
-                      alt=""
-                    />
-                  </div>
-                </div>
-              </div>
-            </template>
-          </div>
-        </template>
-      </VueDraggable>
-
-      <div class="px-16 flex justify-between">
-        <button
-          @click="handleInsertOrEditTitleOk(defaultSectionTitle)"
-          class="w-110 active:bg-[#000]/[0.1] h-44 box-border text-base text-black-3 flex justify-center rounded-md items-center bg-[#F3F3F3]"
-        >
-          <van-icon name="plus" class="mr-5" color="#FF9300" />
-          插入小标题
-        </button>
-        <button
-          @click="handleInsertOrEditTitleOk(defaultSectionContent)"
-          class="w-110 h-44 active:bg-[#000]/[0.1] box-border text-base text-black-3 flex justify-center rounded-md items-center bg-[#F3F3F3]"
-        >
-          <van-icon name="plus" class="mr-5" color="#FF9300" />
-          插入内容
-        </button>
-        <button
-          @click="handleInsertOrEditTitleOk(defaultSectionImage)"
-          class="w-110 active:bg-[#000]/[0.1] h-44 box-border text-base text-black-3 flex justify-center rounded-md items-center bg-[#F3F3F3]"
-        >
-          <van-icon name="plus" class="mr-5" color="#FF9300" />
-          插入图片
-        </button>
-      </div>
-
-      <div
-        class="fixed box-border px-16 pt-16 pb-31 shadow-[0_-4px_4px_0px_rgba(0,0,0,0.1)] bottom-0 left-0 w-full h-80 flex justify-between bg-white items-center"
-      >
-        <div class="flex justify-start items-center h-50">
-          <div class="w-50 text-center" @click="showDialog(draftDialogContent)">
-            <van-icon :name="draft" size="20px" />
-            <p class="text-sm text-black-3">草稿</p>
-          </div>
-          <div class="w-50 text-center" @click="handlePreview">
-            <van-icon name="eye-o" size="24px" />
-            <p class="text-sm text-black-3">预览</p>
-          </div>
-        </div>
-
-        <van-button
-          class="w-full h-full flex items-center"
-          size="large"
-          type="primary"
-          round
-          color="#FD9A00"
-          block
-          :loading="publishLoading"
-          @click="handlePublish"
-        >
-          <template #icon>
-            <van-image width="22" height="22" :src="upload" />
-          </template>
-          发布
-        </van-button>
-      </div>
-    </div>
-
-    <div v-else class="w-full h-full">
-      <div class="p-16">
-        <img
-          v-if="defaultNoteJson?.travelNotesBanner"
-          :src="defaultNoteJson?.travelNotesBanner"
-          class="aspect-[316/204] w-full object-cover rounded-lg"
-        />
-        <img
-          v-else
-          src="~/assets/img/note-create/note_create_banner_bg.png"
-          class="aspect-[316/204] w-full object-cover rounded-lg"
-        />
-
-        <div class="flex mt-10">
-          <img src="~/assets/img/article_title.png" class="w-[32px] h-[32px] shrink-0" alt="" />
-
-          <div
-            class="w-321 line-clamp-2 overflow-hidden truncate max-w-xs ml-10 text-xl whitespace-normal font-bold text-black-3"
-          >
-            {{ defaultNoteJson?.projectTitle ?? '游记标题' }}
-          </div>
-        </div>
-        <div class="flex justify-end text-[#999] text-[12px]">
-          {{ defaultNoteJson.createTime }}
-        </div>
-        <div
-          class="relative border-2 border-dashed border-[#E3E3E3] pt-15 pb-15 mt-12 rounded-lg pl-20 text-[#4B99EA] text-[12px]"
-          :style="{ background: `url(${dashBorder2})`, backgroundSize: '100% 100%' }"
-        >
-          <div class="">
-            <van-row>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/date.png" alt="" />
-                  <div class="font-bold leading-[20px]">出发时间/</div>
-                  <div class="leading-[20px] flex-1">
-                    {{ defaultNoteJson?.departureTime || '' }}
-                  </div>
-                </div>
-              </van-col>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/time.png" alt="" />
-                  <div class="font-bold leading-[20px]">出发天数/</div>
-                  <div class="leading-[20px] flex-1">{{ defaultNoteJson?.countTimes || '' }}</div>
-                </div>
-              </van-col>
-            </van-row>
-          </div>
-          <div class="mt-24">
-            <van-row>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img
-                    class="w-[20px] h-[20px] mr-5"
-                    src="~/assets/img/yj/relationship.png"
-                    alt=""
-                  />
-                  <div class="font-bold leading-[20px]">人物关系/</div>
-                  <div class="leading-[20px] flex-1">{{ defaultNoteJson?.role || '' }}</div>
-                </div>
-              </van-col>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/money.png" alt="" />
-                  <div class="font-bold leading-[20px]">人均费用/</div>
-                  <div class="leading-[20px] flex-1">{{ defaultNoteJson?.averageCost || '' }}</div>
-                </div>
-              </van-col>
-            </van-row>
-          </div>
-          <div class="mt-24">
-            <van-row>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/plan.png" alt="" />
-                  <div class="font-bold leading-[20px]">出行方式/</div>
-                  <div class="leading-[20px] flex-1">
-                    {{ defaultNoteJson?.travelMode || '' }}
-                  </div>
-                </div>
-              </van-col>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/target.png" alt="" />
-                  <div class="font-bold leading-[20px]">目的地/</div>
-                  <div class="leading-[20px] flex-1">
-                    {{ defaultNoteJson?.endPlace || '' }}
-                  </div>
-                </div>
-              </van-col>
-            </van-row>
-          </div>
-          <div class="mt-24">
-            <van-row>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/person.png" alt="" />
-                  <div class="font-bold leading-[20px]">游玩人数/</div>
-                  <div class="leading-[20px] flex-1">{{ defaultNoteJson?.travelNumber || '' }}</div>
-                </div>
-              </van-col>
-              <van-col span="12">
-                <div class="w-[90%] flex text-[#FD9A00]">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/star.png" alt="" />
-                  <div class="font-bold leading-[20px]">推荐指数/</div>
-                  <div class="leading-[20px] flex-1">
-                    {{ defaultNoteJson?.recommendationRate || '' }}
-                  </div>
-                </div>
-              </van-col>
-            </van-row>
-          </div>
-        </div>
-        <div class="mt-10" v-for="con in defaultNoteJson.travelNotesContent" :key="con.id">
-          <template v-if="con.type == 'image'">
-            <img :src="con.content" class="w-[full] rounded-xl" alt="" />
-          </template>
-
-          <template v-else>
-            <div class="text-sm text-black-3" v-html="con.content"></div>
-          </template>
-        </div>
-      </div>
-      <div
-        style="justify-content: space-between"
-        class="fixed box-border mb-15 p-16 shadow-[0_-4px_4px_0px_rgba(0,0,0,0.1)] bottom-0 left-0 w-full h-80 flex justify-between bg-white items-center"
-      >
-        <div class="w-165">
-          <van-button
-            @click="handlePreview"
-            style="color: #fd9a00"
-            class="w-full h-full flex items-center"
-            size="large"
-            type="primary"
-            round
-            color="#FFF7E6"
-          >
-            继续编辑
-          </van-button>
-        </div>
-
-        <div class="w-165">
-          <van-button
-            class="w-full h-full flex items-center"
-            size="large"
-            type="primary"
-            round
-            color="#FD9A00"
-            block
-            :loading="publishLoading"
-            @click="handlePublish"
-          >
-            <template #icon>
-              <van-image width="22" height="22" :src="upload" />
-            </template>
-            确定发布
-          </van-button>
-        </div>
-      </div>
-    </div>
-    <CreateNoteUserInfoModal
-      v-model:visible="userInfoOptions.show"
-      @submit-ok="handleCollectUserInfoOk"
-    />
-    <CreateNotePublishResultModal v-model:visible="publishResultModalOptions" />
-  </div>
-</template>
-
-<script setup>
-import { useFileDialog } from '@vueuse/core'
-import upload from '~/assets/img/note-create/upload.svg'
-import draft from '~/assets/img/note-create/draft.svg'
-// import { cloneDeep } from 'lodash-es'
-import { VueDraggable } from 'vue-draggable-plus'
-
-import { nanoid } from 'nanoid'
-
-const dragOptions = {
-  disabled: false, // 是否禁用拖拽
-  animation: 150 // 拖拽时的动画效果持续时间
-}
-
-const { open, onChange } = useFileDialog({
-  accept: '.png,.png,.jpeg,.JPG,Png '
-})
-
-const { loading, setLoading } = useLoading()
-loading.value = false
-
-const defaultSectionTitle = {
-  type: 'sectionTitle',
-  content: ''
-}
-const defaultSectionContent = {
-  type: 'sectionContent',
-  content: ''
-}
-const defaultSectionImage = {
-  type: 'image',
-  content: ''
-}
-
-const defaultNoteJson = {
-  travelNotesBanner: null,
-  projectTitle: null,
-  departureTime: null,
-  countTimes: null,
-  endPlaceId: null,
-  endPlace: null,
-  role: null,
-  travelMode: null,
-  averageCost: null,
-  recommendationRate: null,
-  travelNumber: null,
-  travelNotesContent: []
-}
-const noteJson = reactive(defaultNoteJson)
-
-watch(noteJson, () => {}, { deep: true, immediate: true })
-
-const id = useRouteQuery('id')
-
-// onMounted(() => {
-//   getPlaceOptions()
-// }),
-watch(
-  id,
-  () => {
-    getNoteDetail()
-  },
-  { immediate: true }
-)
-
-// 删除的弹窗内容
-const deleteDialogContent = {
-  title: '温馨提示',
-  message: '是否删除',
-  confirmButtonColor: '#FF9300'
-}
-
-// 保存草稿的弹窗内容
-const draftDialogContent = {
-  title: '是否保存到草稿箱',
-  message: '保存后,可在“我的-我的游记-草稿箱”查看',
-  confirmButtonText: '保存草稿',
-  confirmButtonColor: '#FF9300',
-  cancelButtonText: '不保存'
-}
-
-// 显示弹窗
-const showDialog = (parmas) => {
-  showConfirmDialog(parmas)
-    .then(() => {
-      handleSaveDraft()
-    })
-    .catch(() => {})
-}
-
-// // 获取目的地
-// const placeOptions = ref([])
-// async function getPlaceOptions() {
-//   const { data } = await request('/website/tourism/publishTravelNotes/getWriteBelongTab')
-//   placeOptions.value = data
-// }
-
-// 获取草稿详情
-async function getNoteDetail() {
-  try {
-    setLoading(true)
-    const res = await request(
-      `/website/tourism/publishTravelNotes/getDraftDetail?writeId=${id.value}`
-    )
-    const data = res.data ?? {}
-    Object.keys(noteJson).forEach((key) => {
-      noteJson[key] = data[key]
-      noteJson.travelNotesContent = data.travelNotesContent ?? []
-      if (noteJson.travelNotesContent.length === 0) {
-        noteJson.travelNotesContent.push({
-          type: defaultSectionContent.type,
-          content: '',
-          tmpId: nanoid()
-        })
-      }
-    })
-
-    // placeOptions.value.find(item=>item.id)
-    if (data?.endPlaceDictMap?.id) {
-      noteJson.endPlaceId = data?.endPlaceDictMap?.id
-      noteJson.endPlace = data?.endPlaceDictMap?.name
-    }
-    if (data?.endPlaceDictMap?.name) {
-      noteJson.endPlace = data?.endPlaceDictMap?.name
-      noteJson.endPlaceId = data?.endPlaceDictMap?.id
-    }
-
-    setLoading(false)
-  } catch (error) {
-    setLoading(false)
-  }
-}
-
-/************ 插入段落标题逻辑 ********** */
-const insertTilteOptions = reactive({
-  show: false,
-  content: null,
-  editIndex: null
-})
-
-const editIndex = ref(null)
-
-// 点击那个插入的内容那个就是框的下标
-function handleInsertOrEditTitleIndex(index) {
-  editIndex.value = index
-}
-
-// 点击编辑大标题,
-function handleInsertOrEditProjectTitle(value) {
-  noteJson.travelNotesContent.projectTitle = value
-}
-// 点击编辑或者新增段落标题,弹出dialog
-function handleInsertOrEditTitle(value) {
-  noteJson.travelNotesContent[editIndex.value].content = value
-}
-
-// 确认编辑或者新增段落标题
-function handleInsertOrEditTitleOk(newTitle) {
-  noteJson.travelNotesContent.push({
-    ...newTitle,
-    tmpId: nanoid()
-  })
-}
-
-// 删除段落标题
-function handleDeleteTitle(index) {
-  showConfirmDialog(deleteDialogContent)
-    .then(() => {
-      noteJson.travelNotesContent.splice(index, 1)
-    })
-    .catch(() => {})
-}
-
-/******************插入正文相关逻辑*******************/
-
-// 单个点击上传图片的事件
-const handleSelectImage = (index) => {
-  handleInsertOrEditTitleIndex(index)
-  open()
-}
-
-onChange(async (files) => {
-  if (!files.length) return
-
-  const formData = new FormData()
-  formData.append('uploadFile', files[0])
-  formData.append('asImage', true)
-  formData.append('fieldName', 'travelNotesBanner')
-  const maxSize = 5 * 1024 * 1024 // 10 MB
-  if (files[0].size > maxSize) {
-    showToast('上传图片过大,请重新上传')
-    return
-  } else {
-    try {
-      showLoadingToast({
-        message: '图片上传中...',
-        duration: 1000000
-      })
-
-      const { data } = await request('/admin/app/tourismProjectTravelNotesWrite/upload', {
-        method: 'post',
-        body: formData
-      })
-      const url = data.fileUrl
-      if (noteJson.travelNotesContent[editIndex.value].type == 'image') {
-        noteJson.travelNotesContent[editIndex.value].content = url
-        closeToast()
-      }
-    } catch (error) {
-      closeToast()
-      showToast('图片上传失败')
-    }
-  }
-})
-
-/******************插入图片逻辑*******************/
-
-function handleDeleteImage(index) {
-  showConfirmDialog(deleteDialogContent)
-    .then(() => {
-      if (noteJson.travelNotesContent[index].type === 'image') {
-        noteJson.travelNotesContent.splice(index, 1)
-      }
-    })
-    .catch(() => {})
-}
-
-// 设为封面图
-function handleSaveCover(item) {
-  noteJson.travelNotesContent.forEach((e) => {
-    if (e.type === 'image') {
-      e.cover = 0
-    }
-    item.cover = 1
-  })
-  showToast('设置封面成功,保存到草稿箱或发布才生效')
-}
-
-// 保存草稿
-async function handleSaveDraft() {
-  let parmas = {}
-
-  try {
-    parmas = { ...noteJson }
-    parmas.endPlace = noteJson.endPlaceId
-    await request('/website/tourism/publishTravelNotes/saveDraft', {
-      method: 'post',
-      body: {
-        ...parmas,
-        id: id?.value
-      }
-    })
-
-    showToast('草稿保存成功')
-  } finally {
-  }
-}
-
-// 预览
-const previewOptions = reactive({
-  show: false
-})
-
-function handlePreview() {
-  previewOptions.show = !previewOptions.show
-}
-
-// 收集个人信息
-const userInfoOptions = reactive({
-  show: false
-})
-
-function handleCollectUserInfoOk() {
-  requestPublish()
-}
-
-const publishResultModalOptions = ref(false)
-
-// 发布
-async function handlePublish() {
-  if (!noteJson.travelNotesBanner) {
-    showNotify({
-      type: 'warning',
-      message: '请设置游记头图',
-      duration: 3000
-    })
-    return
-  }
-  if (!noteJson.projectTitle) {
-    showNotify({
-      type: 'warning',
-      message: '请输入游记标题',
-      duration: 3000
-    })
-    return
-  }
-  if (!noteJson.endPlace) {
-    showNotify({
-      type: 'warning',
-      message: '请选择目的地',
-      duration: 3000
-    })
-    return
-  }
-  if (noteJson.travelNotesContent.length === 0) {
-    showNotify({
-      type: 'warning',
-      message: '游记内容不能为空',
-      duration: 3000
-    })
-    return
-  }
-
-  const { data: isPerfect } = await request('/website/tourism/publishTravelNotes/isPerfect')
-  if (isPerfect === 0) {
-    // 需要收集个人信息
-    userInfoOptions.show = true
-  } else {
-    requestPublish()
-  }
-}
-
-const publishLoading = ref(false)
-
-async function requestPublish() {
-  let parmas = {}
-  try {
-    publishLoading.value = true
-
-    parmas = { ...noteJson }
-    if (noteJson.endPlaceId) {
-      parmas.endPlace = noteJson.endPlaceId
-    }
-
-    await request('/website/tourism/publishTravelNotes/publishDraft', {
-      method: 'post',
-      body: {
-        ...parmas,
-        id: id.value
-      }
-    })
-
-    publishResultModalOptions.value = true
-    publishLoading.value = false
-  } catch (error) {
-    publishLoading.value = false
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-::v-deep .van-field:focus {
-  caret-color: #ff9300 !important;
-  outline: none;
-}
-</style>

+ 0 - 513
src/pages/_yj/[id].vue

@@ -1,513 +0,0 @@
-<template>
-  <div>
-    <div v-if="['success'].includes(status)" @click="pageClick">
-      <div class="p-10">
-        <img v-if="Array.isArray(detailData?.tourismUrlsAfterConvert)" :src="detailData?.tourismUrlsAfterConvert[0]" class="aspect-[316/204] w-full object-cover rounded" />
-        <div class="flex mt-10">
-          <img src="~/assets/img/article_title.png" class="w-[32px] h-[32px] shrink-0" alt="">
-          <div class="ml-10 text-[16px] text-[#333] leading-[28px] font-bold">
-            <span v-if="detailData.isOrigin == 1" class="text-[#FD9A00]">【原创】</span>
-            {{ detailData.projectTitle }}
-          </div>
-        </div>
-        <div class="flex justify-end text-[#999] text-[12px]">
-          {{ detailData.createTime }}
-        </div>
-        <div class="relative pt-15 pb-15 mt-8 rounded pl-20 text-[#4B99EA] text-[12px]"
-          :style="{ background: `url(${dashBorder2})`, backgroundSize: '100% 100%' }">
-          <div class="">
-            <van-row>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/date.png" alt="">
-                  <div class="font-bold leading-[20px]">出发时间/</div>
-                  <div class="leading-[20px] flex-1">{{ detailData?.departureTime || '' }}</div>
-                </div>
-              </van-col>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/time.png" alt="">
-                  <div class="font-bold leading-[20px]">出发天数/</div>
-                  <div class="leading-[20px] flex-1 ">{{ detailData?.countTimes || '' }}</div>
-                </div>
-              </van-col>
-            </van-row>
-
-          </div>
-          <div class="mt-24">
-            <van-row>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/relationship.png" alt="">
-                  <div class="font-bold leading-[20px]">人物关系/</div>
-                  <div class="leading-[20px] flex-1">{{ detailData?.role || '' }}</div>
-                </div>
-              </van-col>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/money.png" alt="">
-                  <div class="font-bold leading-[20px]">人均费用/</div>
-                  <div class="leading-[20px] flex-1">{{ detailData?.averageCost || '' }}</div>
-                </div>
-              </van-col>
-            </van-row>
-          </div>
-          <div class="mt-24">
-            <van-row>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/plan.png" alt="">
-                  <div class="font-bold leading-[20px]">出行方式/</div>
-                  <div class="leading-[20px] flex-1">{{ detailData?.travelModeDictMap?.name || '' }}</div>
-                </div>
-              </van-col>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/target.png" alt="">
-                  <div class="font-bold leading-[20px]">目的地/</div>
-                  <div class="leading-[20px] flex-1">{{ detailData?.endPlaceDictMap?.name || '' }}</div>
-                </div>
-              </van-col>
-            </van-row>
-          </div>
-          <div class="mt-24">
-            <van-row>
-              <van-col span="12">
-                <div class="w-[90%] flex">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/person.png" alt="">
-                  <div class="font-bold leading-[20px]">游玩人数/</div>
-                  <div class="leading-[20px] flex-1">{{ detailData?.travelNumber || '' }}</div>
-                </div>
-              </van-col>
-              <van-col span="12">
-                <div class="w-[90%] flex text-[#FD9A00]">
-                  <img class="w-[20px] h-[20px] mr-5" src="~/assets/img/yj/star.png" alt="">
-                  <div class="font-bold leading-[20px]">推荐指数/</div>
-                  <div class="leading-[20px] flex-1">{{ detailData?.recommendationRate || '' }}</div>
-                </div>
-              </van-col>
-            </van-row>
-          </div>
-        </div>
-        <div class="mt-10 w-full" v-for="con in detailData.travelNotesContent" :key="con.id">
-          <template v-if="con.type == 'image'">
-            <img :src="con.content" class="w-full rounded-xl" alt="">
-          </template>
-          <template v-else>
-            <div class="w-full" style="word-wrap: break-word" v-html="con.content"></div>
-          </template>
-        </div>
-
-        <div class="flex justify-end items-center text-[14px] text-[#999] mt-10 ">
-          <div class="flex items-center mr-10">
-            <van-icon name="eye-o" class="mr-5" />{{ detailData.pageViewCount }}
-          </div>
-          <div @click="doStar" class="flex items-center mr-10" :style="{ color: detailData.isLike ? '#FD9A00' : '' }">
-            <van-icon v-if="detailData.isLike" name="good-job" class="mr-5" />
-            <van-icon v-else name="good-job-o" class="mr-5" />
-            {{ detailData.likeCount }}
-          </div>
-          <div class="flex items-center mr-10" @click="handleCollect" :style="{ color: isCollect ? '#FD9A00' : '' }">
-            <van-icon v-if="isCollect" name="star" class="mr-5" />
-            <van-icon v-else name="star-o" class="mr-5" />
-            收藏
-          </div>
-
-          <div @click="share" class="flex items-center">
-            <img src="~/assets/img/yj/share.png" alt="" class="w-[12px] h-[12px] mr-5">分享
-          </div>
-        </div>
-        <div v-if="detailData?.contactCodeConvert"
-          class="flex items-center h-[112px] border rounded-[20px] overflow-hidden mt-10">
-          <div class="w-[112px] h-full bg-[#F5F5F5] flex items-center justify-center rounded-[20px]">
-            <img class="h-[80%] aspect-[1/1] " :src="detailData?.contactCodeConvert" alt="">
-          </div>
-          <div class="flex items-center ml-15">
-            <img src="~/assets/img/yj/leftArrow.png" class="w-[28px]" alt="">
-            <div class="text-[#666666] text-[12px]">长按保存图片,扫码添加好友</div>
-          </div>
-        </div>
-        <template v-if="commentList.length">
-          <template v-for="item in commentList" :key="item.id">
-            <div class="flex mt-10 justify-between" id="commentsBox">
-              <div class="w-[32px] h-[32px] bg-[#ddd] rounded-full shrink-0">
-                <img v-if="item?.headImageUrlDictMap?.name" class="w-full h-full object-cover rounded-full"
-                  :src="item?.headImageUrlDictMap?.name" alt="">
-                <img v-else class="w-full h-full object-cover rounded-full"
-                  src="https://www.xiaoyaotravel.com/_nuxt/default_avatar.gSq5JxK1.png" alt="">
-              </div>
-              <div class="flex-1 pl-10 flex justify-between">
-                <div>
-                  <div>
-                    <span class="text-[#f40] text-[12px]">{{ item?.commentUserIdDictMap?.name }}:</span>
-                    <span class="text-[14px] text-[#333]" v-html="coveredContent(item?.commentContent)"></span>
-                  </div>
-                  <div class="text-[#666] text-[10px]">{{ item?.createTime }}</div>
-                </div>
-                <div class="mt-3 shrink-0">
-                  <van-icon @click="deleteComment(item.id)" v-if="item.self" class="mr-15" name="delete-o" size="20px"
-                    color="#666" />
-                  <van-icon @click.stop="addReply(item)" name="comment-o" size="20px" color="#666" />
-                </div>
-              </div>
-            </div>
-            <template v-if="Array.isArray(item.childrenList) && item.childrenList.length">
-              <div class="pl-[32px] mt-10">
-                <div class="pl-12" style="border-left:1px solid #ccc">
-                  <div v-for="subItem in item.childrenList" :key="subItem.id">
-                    <div class="flex justify-between items-start">
-                      <div>
-                        <span class="text-[#f40] text-[12px]">{{ subItem?.commentUserIdDictMap?.name }}<span
-                            class="text-[#333]">回复</span>{{ subItem?.replyUserIdDictMap?.name }}:</span>
-                        <span class="text-[14px] text-[#333]" v-html="coveredContent(subItem?.commentContent)"></span>
-                      </div>
-                      <div class="flex items-center mt-3">
-                        <van-icon v-if="subItem.self" @click="deleteComment(subItem.id)" class="mr-15" name="delete-o"
-                          size="20px" color="#666" />
-                        <van-icon @click.stop="addReply(subItem, item.id)" name="comment-o" size="20px" color="#666" />
-                      </div>
-                    </div>
-                    <div class="text-[#666] text-[10px]">{{ subItem.createTime }}</div>
-                  </div>
-                </div>
-              </div>
-            </template>
-          </template>
-        </template>
-        <template v-else>
-          <div class="bg-[#F5F5F5] rounded-[20px] flex items-center justify-center h-[140px] mt-10">
-            <div>
-              <div>
-                <img class="w-[84px]" src="~/assets/img/yj/no-comments.png" alt="">
-              </div>
-              <div class="text-center text-[#999]">
-                暂无评论
-              </div>
-            </div>
-          </div>
-        </template>
-        <div class="mt-20">
-          <div class="flex items-center space-x-10">
-            <img src="~/assets/img/yj/recomend_travel_note_icon.png" class="h-[27px] w-[36px] object-cover" alt="" srcset="" />
-            <div class="text-[16px] font-bold text-black-3">热门游记</div>
-          </div>
-          <div class="">
-            <NuxtLink :to="`/yj/${item.id}`" class="group flex cursor-pointer items-center space-x-10 py-10 border-b" v-for="(item, index) in dataList.dataList
-" :key="item.id">
-              <div v-if="index<4" class="w-[15px] h-[15px] bg-[#000] text-[#fff] rounded flex items-center justify-center" :style="{background:['#FD9A00','#33B724','#F02626','#CACACA'][index]}" >
-                {{ index + 1 }}
-              </div>
-              <img :src="item?.homeHotPicturesAfterConvert?.length ? item.homeHotPicturesAfterConvert[0] : ''"
-                class="h-65 w-65 shrink-0 rounded-xl object-cover" alt="" />
-              <div class="w-0 flex-1">
-                <div class="truncate text-xl font-semibold text-black-3 group-hover:text-linkHover">
-                  {{ item.projectTitle }}
-                </div>
-                <div class="mt-5 flex items-center justify-between space-x-20 text-base">
-                  <div class="flex-1 truncate text-black-6">
-                    {{ item.remarks }}
-                  </div>
-                </div>
-              </div>
-            </NuxtLink>
-          </div>
-        </div>
-        <div class="h-[100px]"></div>
-        <div @click.stop="" class="fixed bottom-0 left-0 w-full bg-[#fff] pt-10 pb-30 border">
-          <div v-if="replyComment.id" class="text-center relative">
-            回复 <span class="text-[#f40]">{{ replyComment?.commentUserIdDictMap?.name }}:</span>
-            <div class="absolute right-10 top-0">
-              <van-icon name="cross" />
-            </div>
-          </div>
-          <div class="flex items-center ">
-            <div class="rounded-full overflow-hidden w-[32px] h-[32px] shrink-0 ml-5">
-              <img v-if="userInfo?.headImageUrl" class="w-full h-full object-cover rounded-full"
-                :src="userInfo?.headImageUrl" alt="" />
-              <img v-else class="w-full h-full object-cover rounded-full"
-                src="https://www.xiaoyaotravel.com/_nuxt/default_avatar.gSq5JxK1.png" alt="" />
-            </div>
-            <div
-              class="relative flex-1 ml-5 mr-5 pl-5 pr-5 pt-4 pb-4 rounded-full h-[50px] border border-[#FD9A00] flex items-center justify-between">
-              <textarea v-model="commentValue" ref="textareaRef"  :placeholder="replyComment.id ? '回复:' : '发布您的评论'"
-                @focus="textareaFocus" class="ml-8 flex-1 h-full" style="resize: none;"></textarea>
-              <img @click="openEmoji" src="~/assets/img/yj/emoji.png" class="w-[22px] h-[22px]" alt="">
-
-            </div>
-            <div @click="addComment"
-              class="pt-6 pb-6 pl-20 pr-20 mr-5 bg-[#FD9A00] text-[#fff] flex items-center justify-center rounded-full shrink-0">
-              评论
-            </div>
-          </div>
-          <div v-if="showEmoji" class=" h-[300px]  bg-[#fff] overflow-auto">
-            <div @click="closeEmojiBox" class="flex justify-end pr-15 text-[#999] text-[12px]">
-              收起表情
-            </div>
-            <div class="flex items-center flex-wrap">
-              <div v-for="(item, index) in emojiJson" :key="index"
-                class="hover:bg-[#ddd] text-[22px] w-[10%] aspect-[1/1] flex items-center justify-center">
-                <div @click="selectEmoji(index)" v-html="coveredContent(index)"></div>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-    
-    <div v-if="['error'].includes(status)">
-      <div class="text-center">
-        不好意思,找不到这篇游记了~
-      </div>
-      <div @click="goBack" class="text-center underline mt-10 cursor-pointer text-[#4B99EA]">
-        返回
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import dashBorder2 from '~/assets/img/dash-border.png'
-import emojiArr from './emoji'
-import emojiJson from './emoji.json'
-const userInfoStore = useUserInfoStore()
-const authStore = useAuthStore()
-const { token } = storeToRefs(authStore)
-
-const id = useRouteParam("id")
-const router = useRouter()
-const textareaRef = ref(null)
-
-// 游记内容
-const { data: detailData, status } = useMyFetch(
-  `website/tourism/projectTravelNotes/travelNotesDetail?id=${id.value}`
-)
-
-// 热门游记列表
-const { data: dataList } = await useMyFetch(
-  `/website/tourism/projectTravelNotes/homeList?isHotspot=1&pageNum=1&pageSize=4`
-)
-
-// 是否已收藏
-const isCollect = ref(false)
-async function isCollectTravelNotes() {
-  if (!token.value) return
-  const { data } = await request(`website/tourism/projectTravelNotes/isCollectTravelNotes?travelNotesId=${id.value}`)
-  isCollect.value = data
-}
-
-// 收藏/取消收藏
-async function handleCollect() {
-  if (!token.value) {
-    showConfirmDialog({
-      showConfirmDialog: true,
-      title: '提示',
-      message: '登录后可以收藏游记',
-      theme: 'round-button',
-    }).then(async () => { navigateTo({ path: '/login' }) })
-    return
-  }
-  const type = isCollect.value ? 0 : 1
-  showLoadingToast({
-    forbidClick: true,
-    duration: 0,
-  })
-  await request(`/website/tourism/projectTravelNotes/userCollectTravelNotesUpdate`, { method: 'post', body: { type, travelNotesId: id.value } }).finally(() => closeToast())
-  isCollectTravelNotes()
-  showToast(type == 1 ? '收藏成功' : '已取消收藏')
-}
-
-// 获取点赞数量
-async function getStars() {
-  const { data } = await request(`/website/tourism/projectTravelNotes/getLikeCount?travelNotesId=${id.value}`)
-  if (isNaN(data)) return
-  detailData.value.likeCount = data
-}
-
-// 点赞
-const canDoStar = ref(true)
-async function doStar() {
-  if (!canDoStar.value) return
-  canDoStar.value = false
-  request(`/website/tourism/projectTravelNotes/userLikeTravelNotesUpdate`, {
-    method: 'post',
-    body: { travelNotesId: id.value }
-  }).then(() => {
-    detailData.value.isLike = true
-    getStars()
-  }).finally(() => canDoStar.value = true)
-}
-
-// 获取用户信息
-const userInfo = ref({})
-async function getUserInfo() {
-  if (!token.value) return userInfo.value = {}
-  request('/website/tourism/user/view').then(res => {
-    userInfo.value = res.data || {}
-  }, () => {
-    userInfo.value = {}
-  })
-}
-
-// 评论内容
-const commentValue = ref('')
-
-// 是否展示表情
-const showEmoji = ref(false)
-
-// 文本域失焦
-function textareaFocus() {
-  showEmoji.value = false
-}
-
-// 获取评论列表
-const commentList = ref([])
-async function getComments() {
-  const { data } = await request("/website/comment/tourTravelNotesComment/list?travelNoteId=" + id.value)
-  if (!Array.isArray(data) || !data.length) return commentList.value = []
-  commentList.value = data
-}
-
-// 转换评论中的一些非字符emoji
-function coveredContent(val) {
-  if (!val) return ''
-  return val.replace(/\[.*?]/g, function (str) {
-    const baseApi = import.meta.env.VITE_APP_EMOJI_API
-    const emojiName = emojiJson[str]
-    if (!emojiName) return str
-    return `<img src=${baseApi}${emojiJson[str]} style="width: 20px; height: 20px;display: inline-block; vertical-align: middle"/>`
-  })
-}
-
-// 删除评论
-async function deleteComment(id) {
-  if (!id) return
-  showConfirmDialog({
-    message: '确认要删除这条评论吗?',
-    theme: 'round-button',
-  }).then(async () => {
-    showLoadingToast({
-      forbidClick: true,
-      duration: 0,
-    })
-    await request('/website/comment/tourTravelNotesComment/delete', {
-      method: 'post',
-      body: { id }
-    }).finally(() => closeToast())
-    showToast('删除成功')
-    getComments()
-  });
-}
-
-const cursorIndex = ref(0)
-// 打开表情
-function openEmoji() {
-  nextTick(() => {
-    textareaRef.value.selectionStart && (cursorIndex.value = textareaRef.value.selectionStart)
-    showEmoji.value = true
-  })
-}
-// 收起表情
-function closeEmojiBox() {
-  showEmoji.value = false
-  nextTick(() => {
-    textareaRef.value.focus()
-  })
-}
-// 选择表情
-function selectEmoji(emojiStr = '') {
-  const length = emojiStr.length
-  commentValue.value = commentValue.value.slice(0, cursorIndex.value) + emojiStr + commentValue.value.slice(cursorIndex.value)
-  nextTick(() => {
-    cursorIndex.value += length
-    textareaRef.value.setSelectionRange(cursorIndex.value, cursorIndex.value)
-  })
-}
-
-// 回复评论
-const replyComment = ref({})
-function addReply(item, parentId) {
-  if (!token.value) {
-    showConfirmDialog({
-      showConfirmDialog: true,
-      title: '提示',
-      message: '登录后可以发布评论',
-      theme: 'round-button',
-    }).then(async () => { navigateTo({ path: '/login' }) })
-    return
-  }
-  textareaRef.value.focus()
-    replyComment.value = item
-    parentId && (replyComment.value.parentId = parentId)
-}
-
-
-// 发布评论
-const canAddComment = ref(true)
-async function addComment() {
-  if (!canAddComment.value) return
-  if (!token.value) {
-    showConfirmDialog({
-      showConfirmDialog: true,
-      title: '提示',
-      message: '登录后可以发布评论',
-      theme: 'round-button',
-    }).then(async () => { navigateTo({ path: '/login' }) })
-    return
-  }
-  if (!commentValue.value.trim()) {
-    showToast('评论内容不能为空!')
-    return
-  }
-  canAddComment.value = false
-  const body = { travelNoteId: id.value, commentContent: commentValue.value }
-  if (replyComment.value.id) {
-    body.replyCommentId = replyComment.value.id
-    body.replyUserId = replyComment.value.createUserId
-    body.parentId = replyComment.value.parentId
-  }
-  request('website/comment/tourTravelNotesComment/add', { method: 'post', body }).then(() => {
-    commentValue.value = ''
-    showToast('评论发布成功')
-    getComments()
-    showEmoji.value = false
-    document.querySelector('#commentsBox').scrollIntoView({
-      behavior: 'smooth',
-      block: 'start' 
-    })
-  }).finally(() => canAddComment.value = true, replyComment.value = {}, cursorIndex.value = 0)
-}
-function pageClick() {
-  nextTick(() => {
-    showEmoji.value = false
-    textareaRef.value.blur()
-    replyComment.value = {}
-    cursorIndex.value = 0
-  })
-}
-// 分享
-function share() {
-  navigator.clipboard.writeText(location.href).then(() => {
-    showNotify({ type: 'success', message: '游记链接已复制成功,快去分享给好友吧!' });
-  }, (err) => {
-    showNotify({ message: '游记链接分享失败' });
-  })
-}
-
-function goBack() {
-  router.back()
-}
-useSeoMeta({
-  title: "游记详情",
-  ogTitle: detailData?.projectTitle || '精美热门游记',
-  description: '逍遥游,乐逍遥,自由自在',
-  ogDescription: detailData?.projectTitle || '精美热门游记',
-  ogImage: '',
-});
-onMounted(() => {
-  getComments()
-  getUserInfo()
-  isCollectTravelNotes()
-})
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 23
src/pages/_yj/emoji.js

@@ -1,23 +0,0 @@
-export default [
-    {name:'嘿嘿',emoji:'😀'},
-    {name:'羞涩微笑',emoji:'😊'},
-    {name:'笑哭了',emoji:'😂'},
-    {name:'微笑天使',emoji:'😇'},
-    {name:'喜笑颜开',emoji:'🥰'},
-    {name:'花痴',emoji:'😍'},
-    {name:'飞吻',emoji:'😘'},
-    {name:'亲亲',emoji:'😚'},
-    {name:'好吃',emoji:'😋'},
-    {name:'滑稽',emoji:'🤪'},
-    {name:'吐舌',emoji:'😝'},
-    {name:'发财',emoji:'🤑'},
-    {name:'大便',emoji:'💩'},
-    {name:'OK',emoji:'👌'},
-    {name:'耶',emoji:'✌️'},
-    {name:'拿捏',emoji:'🤏'},
-    {name:'爱你',emoji:'🤟'},
-    {name:'举手',emoji:'🙋'},
-    {name:'耸肩',emoji:'🤷'},
-    {name:'出租车',emoji:'🚕'},
-    {name:'汽车',emoji:'🚗'},
-]

+ 0 - 36
src/pages/_yj/emoji.json

@@ -1,36 +0,0 @@
-{
-  "[爱你]": "daxiao.png",
-  "[白眼]": "baiyan.png",
-  "[不开心]": "bukaixin.png",
-  "[馋]": "chan.png",
-  "[吹口哨]": "chuikoushao.png",
-  "[担心]": "danxin.png",
-  "[大笑]": "daxiao.png",
-  "[得意]": "deyi.png",
-  "[烦]": "fan.png",
-  "[飞吻]": "feiwen.png",
-  "[尴尬]": "ganga.png",
-  "[感冒]": "ganmao.png",
-  "[鬼脸]": "guilian.png",
-  "[害怕]": "haipa.png",
-  "[紧张]": "jinzhang.png",
-  "[开怀大笑]": "kaihuaidaxiao.png",
-  "[开心]": "kaixin.png",
-  "[恐怖]": "kongbu.png",
-  "[酷]": "ku.png",
-  "[魔鬼]": "mogui.png",
-  "[难受]": "nanshou.png",
-  "[轻松]": "qingsong.png",
-  "[傻笑]": "shaxiao.png",
-  "[生气]": "shengqi.png",
-  "[舒服]": "shufu.png",
-  "[睡觉]": "shuijiao.png",
-  "[酸]": "suanle.png",
-  "[调皮]": "tiaopi.png",
-  "[吐舌]": "tushe.png",
-  "[无语]": "wuyu.png",
-  "[嘻嘻]": "xixi.png",
-  "[犹豫]": "youyu.png",
-  "[眨眼]": "zhayan.png",
-  "[抓狂/难过]": "zhuakuangnanguo.png"
-}

+ 0 - 2
src/pages/chat/group-chat.vue

@@ -379,7 +379,6 @@ async function getAnnouncement() {
     if (Array.isArray(data.memberList) && data?.memberList?.length) {
       data.memberList.map((el) => {
         if (el.userId == userInfo.value.userId) {
-          console.log(el, 'el  前')
           // 头部的名称已自己的备注名优先
           if (el?.groupRemark) {
             el.groupName = el.groupRemark
@@ -387,7 +386,6 @@ async function getAnnouncement() {
             el.groupName = data?.groupName
           }
 
-          console.log(el, 'el  后')
           groupMemberInfo.value = el
         }
       })

+ 0 - 2
src/pages/chat/single-chat.vue

@@ -247,8 +247,6 @@ const sendImageMessage = async (file) => {
 }
 
 const handleSendMessage = async ({ type, messageContent }) => {
-  console.log(131)
-
   try {
     switch (type) {
       case 'text':

+ 282 - 301
src/pages/note-create/index.client.vue

@@ -24,99 +24,20 @@
       <div class="border-b-[1px] mx-16"></div>
 
       <div style="overflow: hidden; overflow-y: scroll" class="h-200 mb-12">
-        <VueDraggable
-          :scroll="true"
-          :handle="'.drag'"
-          v-model="noteJson.travelNotesContent"
-          :options="dragOptions"
-        >
-          <template v-for="(item, index) in noteJson.travelNotesContent" :key="item.tmpId">
-            <div v-if="item.type === defaultSectionTitle.type" class="mb-12 box-border">
-              <van-cell-group class="min-h-50 flex h-full focus:border-[#FF9300]" inset>
-                <van-field
-                  v-model="item.content"
-                  rows="1"
-                  autosize
-                  class="text-base h-full"
-                  type="textarea"
-                  trigger="onChange"
-                  maxlength="30"
-                  @focus="showExpandTextInput = true"
-                  @blur="showExpandTextInput = false"
-                  validate-trigger="onChange"
-                  placeholder="请输入段落标题..."
-                  @click-input="handleInsertOrEditTitleIndex(index)"
-                  @update:model-value="handleInsertOrEditTitle"
-                >
-                  <template v-if="noteJson.travelNotesContent.length > 1" #extra>
-                    <div class="flex">
-                      <div class="h-16 drag flex mr-10 items-center">
-                        <img class="w-16 h-16" src="~/assets/img/note-create/drag.svg" alt="" />
-                        <span class="text-sm text-black-3">拖拽</span>
-                      </div>
-                      <div class="w-16 h-16 z-0">
-                        <img
-                          @click="handleDeleteTitle(index)"
-                          class="w-full h-full"
-                          src="~/assets/img/note-create/delete.svg"
-                          alt=""
-                        />
-                      </div>
-                    </div>
-                  </template>
-                </van-field>
-              </van-cell-group>
-            </div>
-
-            <div
-              v-else-if="item.type === defaultSectionContent.type"
-              class="mb-12 relative box-border"
-            >
-              <!-- <van-cell-group class="min-h-100" inset> -->
-              <!-- <div
-                  contenteditable
-                  class="inputSectionContent w-full min-h-100"
-                  ref="inputDiv"
-                  @click="handleInsertOrEditTitleIndex2(event, index)"
-                  @input="handleInsertOrEditTitleText"
-                  v-html="item.content"
-                ></div> -->
-              <van-field
-                autofocus
-                v-model="item.content"
-                rows="3"
-                :ref="(el) => (checkboxRefs[item?.tmpId] = el)"
-                autosize
-                maxlength="5000"
-                class="text-base min-h-100"
-                type="textarea"
-                trigger="onChange"
-                validate-trigger="onChange"
-                placeholder="请在这里输入游记正文..."
-                @click-input="handleInsertOrEditTitleIndex(index)"
-                @update:model-value="handleInsertOrEditTitle"
-              >
-                <template v-if="noteJson.travelNotesContent.length > 1" #extra>
-                  <div class="flex">
-                    <div class="h-16 drag flex mr-10 items-center">
-                      <img class="w-16 h-16" src="~/assets/img/note-create/drag.svg" alt="" />
-                      <span class="text-sm text-black-3">拖拽</span>
-                    </div>
-                    <div class="w-16 h-16 z-0">
-                      <img
-                        @click="handleDeleteTitle(index)"
-                        class="w-full h-full"
-                        src="../../assets/img/note-create/delete.svg"
-                        alt=""
-                      />
-                    </div>
-                  </div>
-                </template>
-              </van-field>
-              <!-- </van-cell-group> -->
-            </div>
-          </template>
-        </VueDraggable>
+        <div class="mb-12 relative box-border">
+          <div
+            id="view-note"
+            contenteditable
+            class="inputSectionContent w-[93%] box-border min-h-100 mx-12 my-16 text-sm"
+            ref="inputDiv"
+            @click="handleInsertOrEditTitleIndex2"
+            @input="handleInsertOrEditTitleText"
+            v-html="noteJson?.tourTourismProjectTravelNotesWriteContentDto?.content"
+          ></div>
+          <!-- <div class="min-h-100 mx-12 my-16 w-full mb-20 text-sm">
+            <div ref="editor" id="editor" class="min-h-100"></div>
+          </div> -->
+        </div>
       </div>
 
       <div
@@ -159,7 +80,6 @@
           </div>
         </template>
       </div>
-
       <div class="px-16 flex justify-start mb-12">
         <button
           v-for="(operate, i) in userControlsList"
@@ -418,16 +338,42 @@
 import { useFileDialog } from '@vueuse/core'
 import upload from '~/assets/img/note-create/upload.svg'
 import draft from '~/assets/img/note-create/draft.svg'
-import { VueDraggable } from 'vue-draggable-plus'
+// import { VueDraggable } from 'vue-draggable-plus'
 import defaultAvatar from '~/assets/img/default_avatar.png'
-import { nanoid } from 'nanoid'
+// import { nanoid } from 'nanoid'
+// import Quill from 'quill'
+// import 'quill-mention/dist/quill.mention.min.css'
+// import { Mention, MentionBlot } from 'quill-mention'
+// Quill.register({ 'blots/mention': MentionBlot, 'modules/mention': Mention })
+
+// import { cloneDeep } from 'lodash-es'
+
 const userInfoStore = useUserInfoStore()
 const { userInfo } = storeToRefs(userInfoStore)
 
-const dragOptions = {
-  disabled: false, // 是否禁用拖拽
-  animation: 150 // 拖拽时的动画效果持续时间
-}
+// const editor = ref(null)
+// const editorInnerHTML = ref('')
+
+// const getMember = () => {
+//   let domStr = editor.value.outerHTML
+//   let reg = /<span[^>]+data-id=['"]([^'"]+)['"]+/g
+//   let result = [],
+//     temp
+//   while ((temp = reg.exec(domStr)) != null) {
+//     if (temp[0].includes('data-denotation-char="@"')) result.push({ id: temp[1] })
+//   }
+//   return result
+// }
+// const getTalk = () => {
+//   let domStr = editor.value.outerHTML
+//   let reg = /<span[^>]+data-id=['"]([^'"]+)['"]+/g
+//   let result = [],
+//     temp
+//   while ((temp = reg.exec(domStr)) != null) {
+//     if (temp[0].includes('data-denotation-char="#"')) result.push({ id: temp[1] })
+//   }
+//   return result
+// }
 
 const TOPIC_TEXT = 'topic'
 const EIT_TEXT = 'eit'
@@ -490,6 +436,9 @@ const defaultNoteJson = {
   recommendationRate: null,
   travelNumber: null,
   travelNotesContent: [],
+  tourTourismProjectTravelNotesWriteContentDto: {
+    content: ''
+  },
   visibleRange: 0, // 是否公开
   chau: null, // 洲
   tourImGroupId: null, // 群聊的id
@@ -512,12 +461,12 @@ watch(
   { immediate: true }
 )
 
-// 删除的弹窗内容
-const deleteDialogContent = {
-  title: '温馨提示',
-  message: '是否删除',
-  confirmButtonColor: '#FF9300'
-}
+// // 删除的弹窗内容
+// const deleteDialogContent = {
+//   title: '温馨提示',
+//   message: '是否删除',
+//   confirmButtonColor: '#FF9300'
+// }
 
 // 保存草稿的弹窗内容
 const draftDialogContent = {
@@ -545,22 +494,19 @@ async function getNoteDetail() {
       `/website/tourism/publishTravelNotes/getDraftDetail?writeId=${id.value}`
     )
     const data = res.data ?? {}
+
     Object.keys(noteJson).forEach((key) => {
       noteJson[key] = data[key]
       noteJson.imgUrls = data.imgUrls ?? []
       noteJson.topics = data.topics ?? []
       noteJson.mentions = data.mentions ?? []
       noteJson.visibleRange = data.visibleRange ?? 0
-      noteJson.travelNotesContent = data.travelNotesContent ?? []
-      if (noteJson.travelNotesContent.length === 0) {
-        noteJson.travelNotesContent.push({
-          type: defaultSectionContent.type,
-          content: '',
-          tmpId: nanoid()
-        })
-      }
+      noteJson.projectTitle = data.projectTitle ?? ''
     })
 
+    noteJson.tourTourismProjectTravelNotesWriteContentDto.content =
+      data?.tourTourismProjectTravelNotesWriteContent?.content ?? ''
+
     if (data?.endPlaceDictMap?.id) {
       noteJson.endPlaceId = data?.endPlaceDictMap?.id
       noteJson.endPlace = data?.endPlaceDictMap?.name
@@ -569,7 +515,9 @@ async function getNoteDetail() {
       noteJson.endPlaceId = data?.endPlaceDictMap?.id
       noteJson.endPlace = data?.endPlaceDictMap?.name
     }
-
+    // nextTick(() => {
+    //   console.log(noteJson, '789')
+    // })
     setLoading(false)
   } catch (error) {
     setLoading(false)
@@ -577,27 +525,22 @@ async function getNoteDetail() {
 }
 
 /************ 插入段落标题逻辑 ********** */
-const insertTilteOptions = reactive({
-  show: false,
-  content: null,
-  editIndex: null
-})
+// const insertTilteOptions = reactive({
+//   show: false,
+//   content: null,
+//   editIndex: null
+// })
 
 const editIndex = ref(null)
 
-// 点击那个插入的内容那个就是框的下标
-function handleInsertOrEditTitleIndex(index) {
-  editIndex.value = index
-}
-function handleInsertOrEditTitleIndex2(event, index) {
-  editIndex.value = index
+function handleInsertOrEditTitleIndex2(event) {
+  // editIndex.value = index
   // 隐藏建议框
-
-  if (!inputDiv.contains(event.target) && !suggestionsBox.contains(event.target)) {
-    suggestionsBox.style.display = 'none'
-  } else {
-    suggestionsBox.style.display = 'block'
-  }
+  // if (!inputDiv.contains(event.target) && !suggestionsBox.contains(event.target)) {
+  //   suggestionsBox.style.display = 'none'
+  // } else {
+  //   suggestionsBox.style.display = 'block'
+  // }
 }
 
 // 点击编辑大标题,
@@ -607,144 +550,52 @@ function handleInsertOrEditProjectTitle(value) {
     showToast('标题最多输入50个字哦')
   }
 }
-// 点击编辑或者新增段落标题,弹出dialog
-function handleInsertOrEditTitle(value) {
-  noteJson.travelNotesContent[editIndex.value].content = value
-}
 
-let currentMention = '' // 当前正在输入的 @ 用户名
+let currentMention2 = '' // 当前正在输入的 # 用户名
+
 let cursorPosition = { offsetX: 0, offsetY: 0 } // 光标位置
+const mentionIndex = ref(0) //记录@光标的位置
+const mentionIndex2 = ref(0) //记录#光标的位置
+// const mentionOptions = ref([]) //艾特及提及
+// const mentionLoading = ref(false)
+// const valueContent = ref('')
+// 点击编辑或者新增段落标题,
+const eidContent = ref('') //记录艾特后输入的内容
 
-// 点击编辑或者新增段落标题,弹出dialog
 function handleInsertOrEditTitleText(value) {
-  console.log(value)
   const selection = window.getSelection()
   cursorPosition = selection.getRangeAt(0).getBoundingClientRect()
-  noteJson.travelNotesContent[editIndex.value].content = value.data
-  let text = noteJson.travelNotesContent[editIndex.value].content
-  const lastAtSign = text.lastIndexOf('@')
-  // 如果最后输入的是 @,显示用户列表
-  if (lastAtSign !== -1 && text[lastAtSign + 1] !== ' ') {
-    currentMention = text.slice(lastAtSign + 1) // 获取 @ 后的部分
-    showSuggestions(currentMention)
-  } else {
-    suggestionsBox.style.display = 'none' // 隐藏用户列表
-  }
-}
+  let text = value.target.innerHTML
 
-// 显示用户列表并定位在光标附近
-function showSuggestions(mention) {
-  const filteredUsers = users.filter((user) => user.startsWith(mention))
-
-  if (filteredUsers.length > 0) {
-    suggestionsBox.innerHTML = '' // 清空上次的建议
-    filteredUsers.forEach((user) => {
-      const div = document.createElement('div')
-      div.textContent = user
-      div.addEventListener('click', () => insertMention(user))
-      suggestionsBox.appendChild(div)
-    })
+  let lastAtSign = -1
+  let lastAtSign2 = -1
 
-    suggestionsBox.style.display = 'block'
-    suggestionsBox.style.left = `${cursorPosition.left}px`
-    suggestionsBox.style.top = `${cursorPosition.top + 20}px`
+  if (text) {
+    lastAtSign = text.indexOf('@')
+    lastAtSign2 = text.indexOf('#')
   } else {
-    suggestionsBox.style.display = 'none'
+    lastAtSign = -1
+    lastAtSign2 = -1
   }
-}
-
-// 插入 @ 用户并改变颜色
-function insertMention(user) {
-  const selection = window.getSelection()
-  const range = selection.getRangeAt(0)
-  const mentionHTML = `<span class="mention">@${user}</span>`
-  range.deleteContents() // 删除选中的文本
-  range.insertNode(document.createRange().createContextualFragment(mentionHTML)) // 插入新的内容
-
-  suggestionsBox.style.display = 'none' // 隐藏用户列表
-}
-
-// 确认编辑或者新增段落标题
-function handleInsertOrEditTitleOk(newTitle) {
-  noteJson.travelNotesContent.push({
-    ...newTitle,
-    tmpId: nanoid()
-  })
-}
-
-// 删除段落标题
-function handleDeleteTitle(index) {
-  showConfirmDialog(deleteDialogContent)
-    .then(() => {
-      noteJson.travelNotesContent.splice(index, 1)
-    })
-    .catch(() => {})
-}
-
-/******************插入正文相关逻辑*******************/
 
-// 单个点击上传图片的事件
-const handleSelectImage = (index) => {
-  handleInsertOrEditTitleIndex(index)
-  open()
-}
-
-onChange(async (files) => {
-  if (!files.length) return
-
-  const formData = new FormData()
-  formData.append('uploadFile', files[0])
-  formData.append('asImage', true)
-  formData.append('fieldName', 'travelNotesBanner')
-  const maxSize = 5 * 1024 * 1024 // 10 MB
-  if (files[0].size > maxSize) {
-    showToast('上传图片过大,请重新上传')
-    return
-  } else {
-    try {
-      showLoadingToast({
-        message: '图片上传中...',
-        duration: 1000000
-      })
+  currentMention2 = text
+  // 如果最后输入的是 @,显示用户列表
+  if (lastAtSign !== -1 && text[lastAtSign + 1] !== ' ') {
+    eidContent.value = text.slice(lastAtSign) // 获取 @ 后的部分 包含@符号
 
-      const { data } = await request('/admin/app/tourismProjectTravelNotesWrite/upload', {
-        method: 'post',
-        body: formData
-      })
-      const url = data.fileUrl
-      if (noteJson.travelNotesContent[editIndex.value].type == 'image') {
-        noteJson.travelNotesContent[editIndex.value].content = url
-        closeToast()
-      }
-    } catch (error) {
-      closeToast()
-      showToast('图片上传失败')
-    }
+    showTopicEit.value = userControlsList.value[1].fn
+    getTopicList(userControlsList.value[1])
   }
-})
 
-/******************插入图片逻辑*******************/
+  if (lastAtSign2 !== -1 && text[lastAtSign2 + 1] !== ' ') {
+    eidContent.value = text.slice(lastAtSign2) // 获取 # 后的部分 包含@符号
 
-function handleDeleteImage(index) {
-  showConfirmDialog(deleteDialogContent)
-    .then(() => {
-      if (noteJson.travelNotesContent[index].type === 'image') {
-        noteJson.travelNotesContent.splice(index, 1)
-      }
-    })
-    .catch(() => {})
+    showTopicEit.value = userControlsList.value[0].fn
+    getTopicList(userControlsList.value[0])
+  }
 }
 
-// 设为封面图
-function handleSaveCover(item) {
-  noteJson.travelNotesContent.forEach((e) => {
-    if (e.type === 'image') {
-      e.cover = 0
-    }
-    item.cover = 1
-  })
-  showToast('设置封面成功,保存到草稿箱或发布才生效')
-}
+/******************插入正文相关逻辑*******************/
 
 // 保存草稿
 async function handleSaveDraft() {
@@ -925,17 +776,6 @@ function expandTextInput() {
   showExpandTextInput.value = !showExpandTextInput.value
 }
 
-// const getInputPosition = (content) => {
-//   try {
-//     if (!content) return 0
-//     const el = $el.querySelector('input, textarea')
-//     if (!el) return content.length - 1
-//     return el.selectionStart
-//   } catch (e) {
-//     // console.log(e, '??')
-//   }
-// }
-
 // 对应的操作
 const handleOperate = (operate) => {
   try {
@@ -944,23 +784,17 @@ const handleOperate = (operate) => {
         if (showTopicEit.value == TOPIC_TEXT) {
           showTopicEit.value = null
         } else {
-          noteJson.travelNotesContent[editIndex.value].content += '#'
+          noteJson.tourTourismProjectTravelNotesWriteContentDto.content += '#'
           showTopicEit.value = operate.fn
           getTopicList(operate)
         }
-
-        // const position = getInputPosition(noteJson.travelNotesContent[editIndex.value].content)
-
-        // const str = noteJson.travelNotesContent[editIndex.value].content
-        // inputValue.value = `${str.slice(0, position)}${'#'}${str.slice(position)}`
-
         break
       case EIT_TEXT:
         if (showTopicEit.value == EIT_TEXT) {
           showTopicEit.value = null
         } else {
           showTopicEit.value = operate.fn
-          noteJson.travelNotesContent[editIndex.value].content += '@'
+          noteJson.tourTourismProjectTravelNotesWriteContentDto.content += '@'
           getTopicList(operate)
         }
 
@@ -974,33 +808,89 @@ const handleOperate = (operate) => {
     }
   } catch (e) {}
 }
+const bindFn = () => {
+  // 获取文档中的所有 span 标签
+  const container = document.getElementById('view-note')
+  const spans = container.querySelectorAll('span')
+  spans.forEach((span) => {
+    if (span.getAttribute('data-denotation-char') === '@') {
+      span.addEventListener('click', (event) => {
+        // console.log('@用户ID', span.getAttribute('data-id'))
+        // console.log('@用户名字', span.innerHTML)
+        // alert('@用户:' + span.innerText)
+        navigateTo({
+          path: `/profile-others/${span.getAttribute('data-id')}`
+        })
+      })
+    }
+    if (span.getAttribute('data-denotation-char') === '#') {
+      span.addEventListener('click', (event) => {
+        // console.log('话题ID', span.getAttribute('data-id'))
+        // console.log('话题内容', span.innerHTML)
+        // alert('话题内容:' + span.innerText)
+        navigateTo({
+          path: '/topic',
+          query: {
+            topicName: span.innerText
+          }
+        })
+      })
+    }
+  })
+}
 
 // 点击话题的事件
 function handleTopicAndEit(parmas, pType) {
-  let contentText = noteJson.travelNotesContent[editIndex.value].content
-  if (pType == '#') {
-    if (contentText.includes('#')) {
-      contentText += ` ${parmas.name}`
-    } else {
-      contentText += `# ${parmas.name}`
+  nextTick(() => {
+    let contentText = currentMention2
+
+    if (pType == '#') {
+      if (contentText.includes('#')) {
+        contentText = contentText.replace(
+          new RegExp(eidContent.value, 'g'),
+          `<span class="mention" data-denotation-char='#' >#${parmas.name}</span> `
+        )
+        showTopicEit.value = null
+        eidContent.value = ''
+      } else {
+        contentText = contentText.replace(
+          new RegExp(eidContent.value, 'g'),
+          `<span class="mention" data-denotation-char='#'  >#${parmas.name}</span>`
+        )
+      }
+      let findIndex = noteJson.topics.findIndex((i) => i?.name == parmas.name)
+      if (findIndex == -1) {
+        noteJson.topics.push(parmas.name)
+      }
     }
+    if (pType == '@') {
+      if (contentText.includes('@')) {
+        contentText = contentText.replace(
+          new RegExp(eidContent.value, 'g'),
+          `<span class="mention" data-denotation-char="@" data-id="${parmas.userId}" >@${parmas.showName}</span>`
+        )
+
+        showTopicEit.value = null
+        eidContent.value = ''
+      } else {
+        contentText = contentText.replace(
+          new RegExp(eidContent.value, 'g'),
+          `<span class="mention" data-denotation-char="@" data-id="${parmas.userId}" >@${parmas.showName}</span>`
+        )
+      }
 
-    noteJson.topics.push(parmas.name)
-  }
-  if (pType == '@') {
-    if (contentText.includes('@')) {
-      contentText += ` ${parmas.showName}`
-    } else {
-      contentText += `@ ${parmas.showName}`
+      let findEidIndex = noteJson.mentions.findIndex((i) => i?.showName == parmas.showName)
+      if (findEidIndex == -1) {
+        noteJson.mentions.push({
+          userName: parmas.showName,
+          userId: parmas.userId
+        })
+      }
     }
 
-    noteJson.mentions.push({
-      userName: parmas.showName,
-      userId: parmas.userId
-    })
-  }
-
-  noteJson.travelNotesContent[editIndex.value].content = contentText
+    // console.log(contentText, ' 6666 ') // 确保 DOM 渲染完成后再获取
+    noteJson.tourTourismProjectTravelNotesWriteContentDto.content = contentText
+  })
 }
 
 // 内容是否为空
@@ -1012,10 +902,60 @@ function contentIsEmpty(content) {
   }
 }
 
-onMounted(() => {})
+// const initEdit = () => {
+//   new Quill(editor.value, {
+//     placeholder: '请输入内容...',
+//     modules: {
+//       mention: {
+//         allowedChars: /^[\u4e00-\u9fa5]*$/, //匹配中文搜索
+//         mentionDenotationChars: ['@', '#'],
+//         positioningStrategy: 'fixed',
+//         renderItem: (data) => {
+//           // 这里文档是可以返回html的,但是没有显示正确
+//           return data.value
+//         },
+//         renderLoading: () => {
+//           return 'Loading...'
+//         },
+//         source: (searchTerm, renderList, mentionChar) => {
+//           console.log(searchTerm, 'searchTerm')
+//           console.log(renderList, 'renderList')
+//           console.log(mentionChar, 'mentionChar')
+
+//           let list = []
+//           if (mentionChar === '@') {
+//             // 调用接口 获取数据 传searchTerm字段实现搜索效果
+//             getTopicList(userControlsList.value[1])
+//             list = cloneDeep(eitList.value)
+//           } else {
+//             // 调用接口 获取数据 传searchTerm字段实现搜索效果
+//             getTopicList(userControlsList.value[0])
+//             list = cloneDeep(topicList.value)
+//           }
+
+//           if (!searchTerm) {
+//             renderList(list, searchTerm)
+//           } else {
+//             const matches = []
+//             for (let i = 0; i < list.length; i++)
+//               if (list[i].value.toLowerCase().indexOf(searchTerm.toLowerCase()))
+//                 matches.push(list[i])
+//             renderList(matches, searchTerm)
+//           }
+//         }
+//       }
+//     }
+//   })
+// }
+
+// onMounted(() => {
+//   // initEdit()
+// })
 </script>
 
 <style lang="scss" scoped>
+/* @import '../../assets/css/quill.snow.css'; */
+
 ::v-deep .van-field:focus {
   caret-color: #ff9300 !important;
   outline: none;
@@ -1023,15 +963,56 @@ onMounted(() => {})
 
 .inputSectionContent:empty:before {
   content: '请输入内容...';
-  color: #aaa;
+  color: #e7e7e7;
+}
+.inputSectionContent:focus {
+  caret-color: #ff9300 !important;
+  outline: none;
+}
+
+::v-deep .mention {
+  color: #2c405b;
+  font-weight: bold;
+}
+
+.ql-editor {
+  border: 1px solid #a3a3a3;
+  border-radius: 6px;
+}
+
+.ql-editor:focus {
+  border: 1px solid #409eff;
+}
+
+.ql-mention-list-container {
+  width: 150px;
 }
 
-.custom-indicator {
-  position: absolute;
-  right: 5px;
-  bottom: 5px;
-  padding: 2px 5px;
-  font-size: 12px;
-  background: rgba(0, 0, 0, 0.1);
+.ql-mention-list-item {
+  font-size: 14px;
+  color: #606266;
+}
+
+.ql-mention-list-item.selected {
+  background-color: #f5f7fa !important;
+}
+
+.mention {
+  color: #409eff;
+  background-color: transparent;
+}
+
+.member-item {
+  display: flex;
+  align-items: center;
+  height: 30px;
+
+  .member-header {
+    object-fit: cover;
+    width: 24px;
+    height: 24px;
+    border-radius: 50%;
+    margin-right: 10px;
+  }
 }
 </style>

+ 324 - 0
src/pages/profile-others/[id].vue

@@ -0,0 +1,324 @@
+<template>
+  <div>
+    <div class="h-48 flex justify-between items-center bg-white px-12">
+      <span
+        @click="goToBack"
+        class="iconfont icon-left text-black-3 font-[600]"
+        style="font-size: 24px"
+      ></span>
+
+      <span></span>
+    </div>
+
+    <div
+      class="relative w-full h-176 box-border pt-20 pl-16 pb-16 pr-12 bg-[url('~/assets/img/profile/profile_banner.png')] bg-no-repeat bg-cover"
+    >
+      <div class="absolute top-0 left-0 w-full h-full bg-white/[0.6] pt-20 pl-16 pb-16 pr-12">
+        <div class="flex justify-start mb-12">
+          <div class="w-64 h-64 shrink-0 rounded-full relative">
+            <img
+              v-if="othersUserInfo?.headImageUrl"
+              class="w-full h-full object-cover rounded-full"
+              :src="othersUserInfo?.headImageUrl"
+              alt=""
+            />
+            <div
+              v-if="othersUserInfo?.sex"
+              class="absolute -bottom-10 left-1/2 -translate-x-1/2 z-2 w-20 h-20 rounded-full bg-white flex justify-center items-cneter"
+            >
+              <span
+                v-if="othersUserInfo?.sex == 2"
+                class="iconfont icon-Female text-[#FF0FE3]"
+                style="font-size: 14px"
+              ></span>
+              <span
+                v-if="othersUserInfo?.sex == 1"
+                class="iconfont icon-Male text-[#0F8FFF]"
+                style="font-size: 14px"
+              ></span>
+            </div>
+          </div>
+
+          <div class="ml-8">
+            <h1 class="text-black-3 font-medium text-xl mb-8">{{ othersUserInfo?.showName }}</h1>
+            <p class="text-sm text-black-3">{{ othersUserInfo?.personalSign }}</p>
+          </div>
+        </div>
+
+        <div class="w-full flex justify-between text-[13px]">
+          <div>
+            <span class="text-black-9 mr-4">关注</span>
+            <span class="text-black-3 font-medium">
+              {{ formatNumberLetter(othersUserInfo?.numberOfConcern) }}
+            </span>
+          </div>
+          <div>
+            <span class="text-black-9 mr-4">粉丝</span>
+            <span class="text-black-3 font-medium">
+              {{ formatNumberLetter(othersUserInfo?.numberOfFollowers) }}
+            </span>
+          </div>
+          <div>
+            <span class="text-black-9 mr-4">获赞</span>
+            <span class="text-black-3 font-medium">
+              {{ formatNumberLetter(othersUserInfo?.praiseNum) }}
+            </span>
+          </div>
+        </div>
+
+        <div class="mt-15 flex justify-between">
+          <button
+            @click="
+              navigateTo({
+                path: `/view-group-chat/${id}`
+              })
+            "
+            class="border-[#FD9A00] border rounded-full text-sm text-[#FD9A00] py-6 px-12 bg-transparent"
+          >
+            查看群聊
+          </button>
+          <div class="flex items-center">
+            <van-button
+              v-if="othersUserInfo?.state == 0 || othersUserInfo?.state == 4"
+              style="width: 72px"
+              size="small"
+              type="primary"
+              round
+              color="#FD9A00"
+              @click="handleFollow(othersUserInfo)"
+            >
+              {{ followState(othersUserInfo?.state).text }}
+            </van-button>
+
+            <button
+              v-if="othersUserInfo?.state == 1 || othersUserInfo?.state == 2"
+              class="border-black-9 border rounded-full text-sm text-black-9 py-6 px-12 bg-transparent"
+              @click="handleFollow(othersUserInfo)"
+            >
+              {{ followState(othersUserInfo?.state).text }}
+            </button>
+            <van-button
+              style="width: 72px; margin-left: 12px"
+              size="small"
+              type="primary"
+              round
+              color="#999"
+              @click="
+                handlePersonalLetter({
+                  getUserId: othersUserInfo?.userId,
+                  noticeType: 1
+                })
+              "
+            >
+              私信
+            </van-button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <van-list
+      v-if="notesList?.length"
+      v-model:loading="loading"
+      :immediate-check="false"
+      :finished="finished"
+      loading-text="加载中..."
+      error-text="获取失败"
+      @load="loadMore"
+    >
+      <div class="w-full box-border py-16 px-12 flex justify-between flex-wrap">
+        <NuxtLink
+          v-for="(i, index) in notesList"
+          :key="i?.id"
+          class="w-[49%] box-border mb-12"
+          :to="`/yj/${i.id}`"
+        >
+          <div class="relative w-172 h-172 rounded-2xl overflow-hidden mb-8">
+            <img class="w-full h-full object-cover" :src="i?.tourismUrl" alt="" />
+
+            <div
+              v-if="i.isTop == 1"
+              class="absolute top-12 left-12 text-white bg-[#F99406]/[0.6] text-sm box-border px-8 py-2 rounded-full"
+            >
+              置顶
+            </div>
+          </div>
+
+          <div class="truncate w-full text-[14px] font-bold mb-8">
+            <img
+              v-if="i?.isOriginal == 1"
+              src="~/assets/img/yuanchuang.png"
+              class="mt-3 w-30 h-16"
+              alt=""
+              style="float: left"
+            />
+            {{ i?.projectTitle }}
+          </div>
+          <div class="flex justify-between items-center box-border mr-8 overflow-hidden">
+            <div class="flex shrink-0 justify-start items-center">
+              <div class="w-18 h-18 shrink-0 rounded-full overflow-hidden">
+                <img class="w-full h-full object-cover" :src="i?.image" alt="" />
+              </div>
+              <span class="w-[70%] ml-4 text-black-9 text-sm">{{ i?.name }}</span>
+            </div>
+
+            <div class="flex shrink-0 justify-start items-cneter text-black-9 text-sm">
+              <img
+                v-if="i.isLike"
+                class="w-16 h-16 mr-4"
+                src="~/assets/img/profile-others/Like2.svg"
+                alt=""
+                @click="() => {}"
+              />
+              <img
+                v-else
+                class="w-16 h-16 mr-4"
+                src="~/assets/img/profile-others/Like.svg"
+                alt=""
+                @click="() => {}"
+              />
+              {{ formatNumberLetter(i?.likeCount) }}
+            </div>
+          </div>
+        </NuxtLink>
+      </div>
+    </van-list>
+    <Empty v-else title="暂无游记" />
+  </div>
+</template>
+<script setup>
+const id = useRouteParam('id')
+const router = useRouter()
+const userInfoStore = useUserInfoStore()
+const { userInfo } = storeToRefs(userInfoStore)
+
+const othersUserInfo = ref(null) //他人信息
+const notesList = ref([]) //下方游记
+const pageNum = ref(1)
+const pageSize = ref(10)
+const loading = ref(false)
+const finished = ref(false)
+// 关注
+const saveLoading = ref(false)
+const handleFollow = async (item) => {
+  try {
+    if (saveLoading.value) return
+    saveLoading.value = true
+    const { data } = await request(`/website/tourism/fans/saveConcern`, {
+      method: 'post',
+      body: {
+        attentionId: item.userId
+      }
+    }).finally(() => {
+      saveLoading.value = false
+    })
+    if (!data) return
+    othersUserInfo.value.state = data.fansStatus
+    showSuccessToast('操作成功')
+  } catch (e) {
+    showToast('操作失败')
+  } finally {
+    saveLoading.value = false
+  }
+}
+
+// 随机生成的唯一id
+function getLocalId() {
+  const random = Math.floor(Math.random() * 10000)
+  return Date.now() + '' + random
+}
+
+// 私信
+const handlePersonalLetter = async (body) => {
+  body.groupId = getLocalId()
+
+  body.sendUserId = userInfo.value.userId
+  const { data } = await request('/website/tourGroup/createMember', {
+    method: 'post',
+    body
+  })
+  if (data) {
+    navigateTo({
+      path: '/profile/my-news',
+      replace: true
+    })
+  }
+}
+
+// 获取他人主页的信息
+async function getHomePageDetailsDto() {
+  const { data } = await request('/website/tourism/travelNotesTopic/homePageDetailsDto', {
+    query: {
+      userId: id.value
+    }
+  })
+
+  if (data) {
+    othersUserInfo.value = data
+  }
+}
+
+function goToBack() {
+  router.back()
+}
+
+function loadMore() {
+  pageNum.value++
+  getTravelNotes()
+}
+
+// 分页查询他人主页的游记
+async function getTravelNotes() {
+  const param = {
+    pageNum: pageNum.value,
+    pageSize: pageSize.value
+  }
+
+  loading.value = true
+  const {
+    data: { dataList, totalCount }
+  } = await request('/website/tourism/travelNotesTopic/getTravelNotes', {
+    query: {
+      ...param,
+      userId: id.value
+    }
+  })
+
+  if (Array.isArray(dataList) && dataList.length) {
+    notesList.value = dataList
+    loading.value = false
+  } else {
+    loading.value = true
+    notesList.value = []
+  }
+
+  if (notesList.value.length >= totalCount) {
+    finished.value = true
+  } else {
+    finished.value = false
+  }
+}
+
+// 关注状态
+function followState(params) {
+  if (params == 0 || params == 4) {
+    return {
+      color: 'text-[#FD9A00] border-[#FD9A00]',
+      text: '关注'
+    }
+  }
+
+  if (params == 1 || params == 2) {
+    return {
+      color: 'text-black-9 border-black-9',
+      text: '已关注'
+    }
+  }
+}
+
+onMounted(() => {
+  getHomePageDetailsDto()
+  getTravelNotes()
+})
+</script>
+<style lang="scss" scoped></style>

+ 174 - 33
src/pages/profile/collection.client.vue

@@ -1,9 +1,13 @@
 <template>
+  <ProfileNotesTabs :tabs="tabs"></ProfileNotesTabs>
   <template v-if="!listData.length && !loading">
-    <van-empty description="暂无收藏得游记" />
+    <van-empty :description="showText(tab)" />
   </template>
   <template v-else-if="listData.length">
-    <div class="bg-[#f8f8f8] px-15 min-h-screen pt-15 flex flex-col space-y-15">
+    <div
+      v-if="tab == 'travelNotes'"
+      class="bg-[#f8f8f8] px-15 min-h-screen pt-15 flex flex-col space-y-15"
+    >
       <ProfileCollectionTravelNoteCell
         v-for="item in listData"
         :key="item.id"
@@ -11,55 +15,192 @@
         @on-cancel="handleCancel(item)"
       />
     </div>
+    <div
+      v-if="tab == 'comment'"
+      class="bg-[#f8f8f8] px-15 min-h-screen pt-15 flex flex-col space-y-15"
+    >
+      <ProfileCollectionComment
+        v-for="item in listData"
+        :key="item.id"
+        :itemData="item"
+        @on-cancel="handleCommentCancel(item)"
+      />
+    </div>
+    <div
+      v-if="tab == 'topic'"
+      class="bg-[#f8f8f8] px-15 min-h-screen pt-15 flex flex-col space-y-15"
+    >
+      <ProfileCollectionTopic
+        v-for="item in listData"
+        :key="item.id"
+        :itemData="item"
+        @on-cancel="handleTopicCancel(item)"
+      />
+    </div>
   </template>
 </template>
 
 <script setup>
-onMounted(() => {
-  getCollectionList();
-});
+const tabs = [
+  {
+    lable: '游记',
+    name: 'travelNotes',
+    to: ''
+  },
+  {
+    lable: '评论',
+    name: 'comment',
+    to: ''
+  },
+  {
+    lable: '话题',
+    name: 'topic',
+    to: ''
+  }
+]
+const route = useRoute()
+const tab = useRouteQuery('tab')
+const listData = ref([])
 
-const listData = ref([]);
+const { loading, setLoading } = useLoading()
 
-const { loading, setLoading } = useLoading();
+function showText() {
+  if (tab.value == 'travelNotes') {
+    return '暂无收藏得游记'
+  }
+  if (tab.value == 'comment') {
+    return '暂无收藏得评论'
+  }
+  if (tab.value == 'topic') {
+    return '暂无收藏得话题'
+  }
+}
 
+// 获取收藏得游记
 async function getCollectionList() {
-  setLoading(true);
-  const { data } = await request(
-    "/website/tourism/projectTravelNotes/userCollectTravelNotesList",
-    {
-      query: {
-        pageSize: 999,
-        pageNum: 1,
-      },
+  setLoading(true)
+  const { data } = await request('/website/tourism/projectTravelNotes/userCollectTravelNotesList', {
+    query: {
+      pageSize: 999,
+      pageNum: 1
     }
-  );
-  setLoading(false);
-  listData.value = data.dataList;
+  })
+  setLoading(false)
+  listData.value = data.dataList
+}
+// 获取收藏得评论
+async function getCollectionListComment() {
+  setLoading(true)
+  const { data } = await request('/website/comment/tourTravelNotesComment/getCollectComments', {
+    query: {
+      pageSize: 999,
+      pageNum: 1
+    }
+  })
+  setLoading(false)
+  listData.value = data.dataList
+}
+// 获取收藏得话题
+async function getCollectionListTopic() {
+  setLoading(true)
+  const { data } = await request('/website/tourism/travelNotesTopic/getSpecialCollection')
+  setLoading(false)
+  listData.value = data
 }
 
+// 取消游记收藏
 function handleCancel(item) {
   showConfirmDialog({
-    title: "提示",
-    message: "确认取消收藏吗",
+    title: '提示',
+    message: '确认取消收藏吗'
   }).then(() => {
-    request(
-      "/website/tourism/projectTravelNotes/userCollectTravelNotesUpdate",
-      {
-        method: "post",
-        body: {
-          travelNotesId: item.id,
-          type: 0,
-        },
+    request('/website/tourism/projectTravelNotes/userCollectTravelNotesUpdate', {
+      method: 'post',
+      body: {
+        travelNotesId: item.id,
+        type: 0
       }
-    )
+    })
       .then(() => {
-        showToast("取消收藏成功");
-        getCollectionList();
+        showToast('取消收藏成功')
+        getCollectionList()
       })
-      .catch(() => {});
-  });
+      .catch(() => {})
+  })
 }
+// 取消评论收藏
+function handleCommentCancel(item) {
+  showConfirmDialog({
+    title: '提示',
+    message: '确认取消收藏吗'
+  }).then(() => {
+    request('/website/comment/tourTravelNotesComment/commentCollectCancle', {
+      method: 'post',
+      body: item.id
+    })
+      .then(() => {
+        showToast('取消收藏成功')
+        getCollectionListComment()
+      })
+      .catch(() => {})
+  })
+}
+// 取消话题收藏
+function handleTopicCancel(item) {
+  showConfirmDialog({
+    title: '提示',
+    message: '确认取消收藏吗'
+  }).then(() => {
+    request('/website/tourism/travelNotesTopic/cancelCollection', {
+      method: 'post',
+      body: {
+        id: item.id
+      }
+    })
+      .then(() => {
+        showToast('取消收藏成功')
+        getCollectionListTopic()
+      })
+      .catch(() => {})
+  })
+}
+
+onMounted(() => {
+  if (tab.value == 'travelNotes') {
+    getCollectionList()
+  }
+  if (tab.value == 'comment') {
+    getCollectionListComment()
+  }
+  if (tab.value == 'topic') {
+    getCollectionListTopic()
+  }
+})
+
+watch(
+  tab,
+  (val) => {
+    if (val == 'travelNotes') {
+      getCollectionList()
+    }
+    if (val == 'comment') {
+      getCollectionListComment()
+    }
+    if (val == 'topic') {
+      getCollectionListTopic()
+    }
+    if (val) return
+
+    navigateTo({
+      path: route.fullPath,
+      query: {
+        tab: tabs[0].name
+      },
+      replace: true
+    })
+  },
+  { immediate: true }
+)
 </script>
 
 <style lang="scss" scoped></style>

+ 1 - 1
src/pages/profile/index.vue

@@ -133,7 +133,7 @@ const menuData = [
   {
     icon: profile_colection,
     label: '我的收藏',
-    to: '/profile/collection'
+    to: '/profile/collection?tab=travelNotes'
   },
   /*  {
       icon: profile_my_comment,

+ 3 - 5
src/pages/profile/notes/index.vue

@@ -1,10 +1,8 @@
 <template>
-  <div ref="container" class="w-full min-h-screen box-border pb-16 px-20 bg-[#F8F8F8]">
-    <van-sticky :offset-top="50" :container="container">
-      <ProfileNotesTabs :tabs="tabs"></ProfileNotesTabs>
-    </van-sticky>
+  <div ref="container" class="w-full min-h-screen box-border pb-16 bg-[#F8F8F8]">
+    <ProfileNotesTabs :tabs="tabs"></ProfileNotesTabs>
 
-    <div class="w-full">
+    <div class="w-full px-20 pt-12">
       <van-pull-refresh style="height: 100%" v-model="loading" @refresh="onRefresh">
         <div v-if="tab === 'published'" class="w-full min-h-screen">
           <ProfileNotesPublished />

+ 329 - 0
src/pages/topic/index.vue

@@ -0,0 +1,329 @@
+<template>
+  <template v-if="showSearch">
+    <div class="text-center">不好意思,找不到这篇话题了~</div>
+    <div @click="goBack" class="text-center underline mt-10 cursor-pointer text-[#4B99EA]">
+      返回
+    </div>
+  </template>
+  <template v-else>
+    <div class="w-full h-48 flex justify-between items-center px-12 mb-12">
+      <van-icon @click="router.back()" name="arrow-left" color="#333333" size="24" />
+
+      <span
+        class="iconfont icon-share text-black-3 ml-12"
+        style="font-size: 24px"
+        @click="copyLink"
+      ></span>
+    </div>
+
+    <div class="px-12 flex justify-between items-start mb-16">
+      <div class="flex justify-start items-start">
+        <div class="w-63 h-63 mr-8">
+          <img class="w-full h-full object-cover" src="~/assets/img/topic/topic.png" alt="" />
+        </div>
+        <div>
+          <h1 v-if="topicData?.name" class="mb-8 text-black-3 font-medium text-3xl">
+            #{{ topicData?.name }}
+          </h1>
+          <p class="text-black-6 text-base font-medium">
+            <span class="mr-5">{{ transferCount(topicData?.viewCount) }} 浏览</span>
+            <span>{{ transferCount(topicData?.commentCount) }} 讨论</span>
+          </p>
+        </div>
+      </div>
+
+      <van-button
+        v-if="topicData?.isCollect"
+        type="primary"
+        style="height: 28px"
+        size="normal"
+        color="#FF9300"
+        round
+        @click="handleTopicCancel"
+      >
+        <span class="iconfont icon-star-fill text-white" style="font-size: 16px"></span>
+        已收藏
+      </van-button>
+      <van-button
+        v-else
+        type="primary"
+        style="height: 28px"
+        size="normal"
+        color="#333333"
+        plain
+        round
+        @click="handleCollection"
+      >
+        <span class="iconfont icon-star text-black-3" style="font-size: 16px"></span>
+        收藏
+      </van-button>
+    </div>
+
+    <div class="flex justify-between px-12 mb-12">
+      <h1 class="text-black-3 text-xl font-medium">
+        帖子 {{ transferCount(topicData?.travelNotesCount) }}
+      </h1>
+      <span class="text-sm text-black-6">
+        <span
+          @click="changeIsHotAndIsNew(0)"
+          :class="`${isHot == 0 ? 'text-black-3 font-semibold' : ' '}`"
+        >
+          最热
+        </span>
+        <span
+          @click="changeIsHotAndIsNew(1)"
+          :class="`${isHot == 1 ? 'text-black-3 font-semibold' : ' '} ml-12 `"
+        >
+          最新
+        </span>
+      </span>
+    </div>
+    <van-list
+      v-if="dataList.length"
+      v-model:loading="loading"
+      :immediate-check="false"
+      :finished="finished"
+      loading-text="加载中..."
+      error-text="获取失败"
+      @load="loadMore"
+    >
+      <div class="flex justify-between flex-wrap px-12">
+        <template v-for="itemData in dataList">
+          <NuxtLink
+            class="w-[50%] relative cursor-pointer bg-white pb-10"
+            :to="`/yj/${itemData.id}`"
+          >
+            <div class="aspect-[172/172] h-172 shrink-0 rounded-2xl overflow-hidden bg-[#ddd] mb-8">
+              <img
+                v-if="itemData?.tourismUrl"
+                :src="itemData?.tourismUrl"
+                class="w-full h-full rounded-2xl object-cover"
+                alt=""
+                srcset=""
+              />
+              <img
+                v-else
+                src="~/assets/img/note-create/note_draft_cover_bg.jpg"
+                class="w-full h-full rounded-2xl object-cover"
+                alt=""
+                srcset=""
+              />
+            </div>
+            <div class="px-8 flex flex-col w-full justify-between">
+              <div class="truncate w-full text-base font-semibold text-black-3 mb-8">
+                <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 flex justify-between items-center">
+                <div class="w-[60%] flex justify-start items-center">
+                  <div class="w-18 h-18 rounded-full shrink-0 overflow-hidden">
+                    <img
+                      v-if="itemData?.createdHeadImageUrl"
+                      class="w-full h-full object-cover"
+                      :src="itemData?.createdHeadImageUrl"
+                      alt=""
+                    />
+                    <img
+                      v-else
+                      class="w-full h-full object-cover"
+                      src="~/assets/img/default_avatar.png"
+                      alt=""
+                    />
+                  </div>
+                  <span class="truncate ml-4 text-black-9 text-sm">
+                    {{ itemData?.createdShowName }}
+                  </span>
+                </div>
+
+                <div class="text-black-9 shrink-0 text-sm flex justify-start items-center">
+                  <img
+                    v-if="itemData?.isLike"
+                    @click.prevent="doSta(itemData)"
+                    class="w-16 h-16 mr-4"
+                    src="~/assets/img/profile-others/Like2.svg"
+                    alt=""
+                  />
+                  <img
+                    v-else
+                    @click.prevent="doStar(itemData)"
+                    class="w-16 h-16 mr-4"
+                    src="~/assets/img/profile-others/Like.svg"
+                    alt=""
+                  />
+                  <span>{{ transferCount(itemData?.likeCount) }}</span>
+                </div>
+              </div>
+            </div>
+          </NuxtLink>
+        </template>
+      </div>
+    </van-list>
+    <div v-else class="flex items-center justify-center h-[140px] mt-10">
+      <div>
+        <div>
+          <img class="w-84" src="../../assets/img/topic/yj.png" alt="" />
+        </div>
+        <div class="text-center text-[#999]">暂无游记</div>
+      </div>
+    </div>
+  </template>
+</template>
+<script setup>
+const route = useRoute()
+const router = useRouter()
+// 是否最新 和最热
+const isHot = ref(0)
+const showSearch = ref(false)
+
+const pageNum = ref(1)
+const pageSize = ref(10)
+const topicData = ref(null)
+const dataList = ref([])
+const loading = ref(true)
+const finished = ref(false)
+
+const topicName = computed(() => route.query.topicName ?? '2025年')
+
+// 搜索页面的请求
+const recordList = ref([])
+async function getSearchRecordList() {
+  const {
+    data: { dataList, totalCount }
+  } = await request('/website/tourism/projectSearchRecords/searchRecordList', {
+    pageNum: 1,
+    pageSize: 20
+  })
+
+  if (Array.isArray(dataList) && dataList.length) {
+    recordList.value = dataList
+  }
+}
+
+function goBack() {
+  router.back()
+}
+
+// 获取话题相关得游记
+async function getTravelNotesTopicList() {
+  showLoadingToast({
+    message: '加载中...',
+    forbidClick: true,
+    duration: 0
+  })
+  const param = {
+    pageNum: pageNum.value,
+    pageSize: pageSize.value
+  }
+
+  param.orderBy = isHot.value
+  param.topicName = topicName.value
+
+  const { data } = await request('/website/tourism/travelNotesTopic/list', {
+    query: param
+  }).finally(() => closeToast())
+
+  if (data) {
+    topicData.value = data
+    dataList.value = data.tourTravelNotesTopicNotesSearchVoList
+    loading.value = false
+    showSearch.value = false
+  } else {
+    topicData.value = {}
+    dataList.value = []
+    showSearch.value = true
+  }
+}
+// 切换最新和最热
+function changeIsHotAndIsNew(index) {
+  isHot.value = index
+  getTravelNotesTopicList()
+}
+
+// 是否收藏话题
+async function handleCollection() {
+  const res = await request('/website/tourism/travelNotesTopic/Collection', {
+    method: 'post',
+    body: {
+      id: topicData.value.id
+    }
+  }).finally(() => {})
+
+  if (res && res.success) {
+    showToast('收藏成功')
+    getTravelNotesTopicList()
+  }
+}
+
+// 取消话题收藏
+function handleTopicCancel() {
+  showConfirmDialog({
+    title: '提示',
+    message: '确认取消收藏吗',
+    confirmButtonColor: '#FF9300'
+  }).then(() => {
+    request('/website/tourism/travelNotesTopic/cancelCollection', {
+      method: 'post',
+      body: {
+        id: topicData.value.id
+      }
+    })
+      .then(() => {
+        showToast('取消收藏成功')
+        getTravelNotesTopicList()
+      })
+      .catch(() => {})
+  })
+}
+
+function loadMore() {
+  pageNum.value++
+  getTravelNotesTopicList()
+}
+
+// 分享
+const copyLink = () => {
+  // let origin = location.origin
+  // let url = `${origin}/topic?topicName=${topicData.name}`
+  let url = location.href
+
+  navigator.clipboard.writeText(url).then(
+    () => {
+      showNotify({ type: 'success', message: '链接已复制' })
+    },
+    () => {
+      showNotify({ message: '链接复制失败' })
+    }
+  )
+}
+
+// 游记得喜欢
+const canDoStar = ref(true)
+async function doStar(params) {
+  if (!canDoStar.value) return
+  canDoStar.value = false
+  request(`/website/tourism/projectTravelNotes/userLikeTravelNotesUpdate`, {
+    method: 'post',
+    body: { travelNotesId: params.id }
+  })
+    .then(() => {
+      getTravelNotesTopicList()
+      nextTick(async () => {
+        let objData = await dataList.value.find((item) => item.id == params.id)
+        objData.isLike = true
+        // objData.likeCount += 1
+      })
+    })
+    .finally(() => (canDoStar.value = true))
+}
+
+onMounted(() => {
+  getTravelNotesTopicList()
+})
+</script>
+<style lang="scss" scoped></style>

+ 393 - 122
src/pages/travel-notes/index.vue

@@ -1,95 +1,271 @@
 <template>
   <div>
-    <div v-if="filterList.length" class="sticky top-50 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">
-      <div v-if="recomendList.length" class="scrollbar w-full" style="overflow-x: auto">
-        <div class="flex ">
-          <NuxtLink :to="'/t/'+item.id" v-for="(item,index) in recomendList" :key="index" class="w-[110px] border rounded overflow-hidden shrink-0 mr-[12px]">
-            <div class="w-full h-[76px] bg-[#dedede]">
-              <img v-if="Array.isArray(item?.tourismUrlsAfterConvert)" :src="item?.tourismUrlsAfterConvert[0]" class="w-full h-full object-cover" alt="">
-            </div>
-            <div class="text-clip overflow-hidden text-[13px] text-[13px] px-5 h-[40px] w-full mt-5 overflow-ellipsis"
-              style="line-height:18px;height:36px">
-              {{ item.projectTitle }}
-            </div>
-            <div class="flex justify-end text-[#FF476A] text-[14px] font-semibold pr-10">
-              {{ item.priceUnit }}{{ item.price }}
-            </div>
-          </NuxtLink>
+    <template v-if="showSearch">
+      <div class="w-full h-48 px-12 mt-8">
+        <van-search
+          v-model="value"
+          show-action
+          shape="round"
+          placeholder="搜索游记"
+          clearable
+          @search="onSearch"
+        >
+          <template #action>
+            <div class="text-[#FF9300]" @click="onSearch">搜索</div>
+          </template>
+        </van-search>
+      </div>
+
+      <div class="px-12">
+        <div class="flex justify-between items-center mb-12">
+          <h1 class="font-semibold text-xl">历史搜索</h1>
+          <div class="text-sm">
+            <template v-if="showCancel">
+              <span class="text-black-9">
+                <span class="mr-12" @click="handleClearSearchRecord">全部删除</span>
+                |
+                <span @click="showCancel = false" class="ml-12 text-black-3">完成</span>
+              </span>
+            </template>
+            <template v-else>
+              <span
+                v-if="recordList.length"
+                @click="handleClearSearchRecord"
+                class="iconfont icon-delete-one text-black-9"
+                style="font-size: 20px"
+              ></span>
+            </template>
+          </div>
+        </div>
+        <div class="flex justify-start flex-wrap">
+          <div
+            v-for="(item, index) in recordList"
+            :key="item.id"
+            @click="showCancel = true"
+            class="h-38 bg-[#F7F7F7] box-border px-10 py-8 rounded-[4px] mr-8 mb-12"
+          >
+            {{ item?.searchKeyword }}
+            <span
+              v-if="showCancel"
+              @click.prevent="handleDeleteSearchRecordById(item?.id)"
+              class="iconfont icon-close text-black-6 ml-6"
+              style="font-size: 10px"
+            ></span>
+          </div>
         </div>
       </div>
-      <van-list v-if="dataList.length" v-model:loading="loading" :immediate-check="false" :finished="finished"
-        loading-text="加载中..." error-text="获取失败" @load="loadMore">
-        <template v-for="itemData in dataList">
-          <NuxtLink class="group flex relative cursor-pointer bg-white pb-10 mt-20" :to="`/yj/${itemData.id}`">
-            <div class="aspect-[120/80] h-80 shrink-0 rounded overflow-hidden bg-[#ddd]">
-              <img v-if="Array.isArray(itemData?.tourismUrlsAfterConvert)" :src="itemData?.tourismUrlsAfterConvert[0]"
-                class="w-full h-full rounded object-cover" alt="" srcset="" />
-            </div>
+    </template>
+    <template v-else>
+      <div v-if="filterList.length" class="sticky top-50 z-50 bg-white w-full">
+        <div class="w-full h-48 flex justify-between items-center px-12">
+          <van-icon @click="router.back()" name="arrow-left" color="#333333" size="24" />
+
+          <div
+            @click="changeBar(0)"
+            :class="`relative h-full  flex justify-center items-center  w-[38.4%]  text-center ${barIndex == 0 ? 'font-semibold text-xl text-[#FF9300]' : 'text-black-3 text-base'} `"
+          >
+            发现
+            <div
+              v-if="barIndex == 0"
+              class="barIndex absolute bottom-0 left-1/2 -translate-x-1/2"
+            ></div>
+          </div>
+          <div
+            @click="changeBar(1)"
+            :class="`relative h-full  flex justify-center items-center w-[38.4%] text-center ${barIndex == 1 ? 'font-semibold text-xl text-[#FF9300]' : 'text-black-3 text-base'} 
+           `"
+          >
+            关注
             <div
-              class="h-80 pl-[8px] flex flex-col text-[#FD9A00] justify-between w-[calc(100%-120px)]"
+              v-if="barIndex == 1"
+              class="barIndex absolute bottom-0 left-1/2 -translate-x-1/2"
+            ></div>
+          </div>
+          <div class="w-24 h-24 shrink-0">
+            <img
+              class="w-full h-full object-cover"
+              src="~/assets/img/visa/search.svg"
+              alt=""
+              @click="isShowSearch"
+            />
+          </div>
+        </div>
+        <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">
+        <div v-if="recomendList.length" class="scrollbar w-full mb-12" style="overflow-x: auto">
+          <div class="flex">
+            <NuxtLink
+              :to="'/t/' + item.id"
+              v-for="(item, index) in recomendList"
+              :key="index"
+              class="w-[110px] border rounded overflow-hidden shrink-0 mr-[12px]"
             >
-              <div class="truncate w-full text-[14px] font-bold">
+              <div class="w-full h-[76px] bg-[#dedede]">
                 <img
-                  v-if="itemData.isOriginal == 1"
-                  src="~/assets/img/yuanchuang.png"
-                  class="mt-3 w-[30px] h-[16px]"
+                  v-if="Array.isArray(item?.tourismUrlsAfterConvert)"
+                  :src="item?.tourismUrlsAfterConvert[0]"
+                  class="w-full h-full object-cover"
                   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="rounded-full text-[#666] border-[#666] text-[10px] pr-6 pl-6 float-left mr-10 shrink-0"
-                    style="border: 0.5px solid #666666"
-                  >
-                    {{ tag }}
+              <div
+                class="text-clip overflow-hidden text-[13px] text-[13px] px-5 h-[40px] w-full mt-5 overflow-ellipsis"
+                style="line-height: 18px; height: 36px"
+              >
+                {{ item.projectTitle }}
+              </div>
+              <div class="flex justify-end text-[#FF476A] text-[14px] font-semibold pr-10">
+                {{ item.priceUnit }}{{ item.price }}
+              </div>
+            </NuxtLink>
+          </div>
+        </div>
+        <van-list
+          v-if="dataList.length"
+          v-model:loading="loading"
+          :immediate-check="false"
+          :finished="finished"
+          loading-text="加载中..."
+          error-text="获取失败"
+          @load="loadMore"
+        >
+          <div class="w-full box-border flex justify-between flex-wrap">
+            <template v-for="itemData in dataList">
+              <!-- <NuxtLink
+              class="group flex relative cursor-pointer bg-white pb-10 mt-20"
+              :to="`/yj/${itemData.id}`"
+            >
+              <div class="aspect-[120/80] h-80 shrink-0 rounded overflow-hidden bg-[#ddd]">
+                <img
+                  v-if="Array.isArray(itemData?.tourismUrlsAfterConvert)"
+                  :src="itemData?.tourismUrlsAfterConvert[0]"
+                  class="w-full h-full rounded object-cover"
+                  alt=""
+                  srcset=""
+                />
+              </div>
+              <div
+                class="h-80 pl-[8px] flex flex-col text-[#FD9A00] justify-between w-[calc(100%-120px)]"
+              >
+                <div class="truncate w-full text-[14px] font-bold">
+                  <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="rounded-full text-[#666] border-[#666] text-[10px] pr-6 pl-6 float-left mr-10 shrink-0"
+                      style="border: 0.5px solid #666666"
+                    >
+                      {{ 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" />
+                    {{ transferCount(itemData.pageViewCount) }}
+                  </div>
+                  <div class="flex items-center mr-10">
+                    <van-icon name="good-job-o" class="mr-5" />
+                    {{ transferCount(itemData.likeCount) }}
                   </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" />
-                  {{ transferCount(itemData.pageViewCount) }}
+              <div class="w-full h-10 absolute top-[100%] left-0 flex items-center justify-between">
+                <div
+                  v-for="item in 40"
+                  class="w-[3%] h-[1px] border border-[#ddd]"
+                  style="transform: scale(0.5)"
+                ></div>
+              </div>
+            </NuxtLink> -->
+              <NuxtLink
+                class="w-[50%] relative cursor-pointer bg-white pb-10"
+                :to="`/yj/${itemData.id}`"
+              >
+                <div
+                  class="aspect-[172/172] h-172 shrink-0 rounded-2xl overflow-hidden bg-[#ddd] mb-8"
+                >
+                  <img
+                    v-if="Array.isArray(itemData?.tourismUrlsAfterConvert)"
+                    :src="itemData?.tourismUrlsAfterConvert[0]"
+                    class="w-full h-full rounded-2xl object-cover"
+                    alt=""
+                    srcset=""
+                  />
                 </div>
-                <div class="flex items-center mr-10">
-                  <van-icon name="good-job-o" class="mr-5" />
-                  {{ transferCount(itemData.likeCount) }}
+                <div class="px-8 flex flex-col w-full justify-between">
+                  <div class="truncate w-full text-base font-semibold text-black-3 mb-8">
+                    <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="flex justify-between items-center">
+                    <div class="flex justify-start items-center">
+                      <div class="w-18 h-18 rounded-full overflow-hidden">
+                        <img
+                          v-if="itemData?.createImage"
+                          class="w-full h-full object-cover"
+                          :src="itemData?.createImage"
+                          alt=""
+                        />
+                        <img
+                          v-else
+                          class="w-full h-full object-cover"
+                          src="~/assets/img/default_avatar.png"
+                          alt=""
+                        />
+                      </div>
+                      <span class="ml-4 text-black-9 text-sm">{{ itemData?.createName }}</span>
+                    </div>
+
+                    <div class="text-black-9 text-sm flex justify-start items-center">
+                      <img
+                        class="w-16 h-16 mr-4"
+                        src="~/assets/img/profile-others/Like.svg"
+                        alt=""
+                      />
+                      <span>{{ transferCount(itemData?.likeCount) }}</span>
+                    </div>
+                  </div>
                 </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-[3%] h-[1px] border border-[#ddd]"
-                style="transform: scale(0.5)"
-              ></div>
-            </div>
-          </NuxtLink>
-        </template>
-      </van-list>
-      <Empty v-if="!dataList.length && !loading" />
-    </div>
+              </NuxtLink>
+            </template>
+          </div>
+        </van-list>
+        <Empty v-if="!dataList.length && !loading" />
+      </div>
+    </template>
   </div>
 </template>
 
@@ -97,14 +273,25 @@
 const route = useRoute()
 const router = useRouter()
 
+// 最顶部导航的下标
+const barIndex = ref(0)
+
+// 是否显示搜索页面的
+const showSearch = ref(false)
+
+// 是否显示搜索历史记录的
+const showCancel = ref(false)
+// 搜索的内容
+const value = ref('')
+
+// 搜索的历史记录
+const searchHistoricalList = ref('')
+
 // 推荐项目
-const { data } = await useMyFetch(
-  `website/tourism/project/list?isHotspot=1&pageNum=1&pageSize=10`
-)
+const { data } = await useMyFetch(`website/tourism/project/list?isHotspot=1&pageNum=1&pageSize=10`)
 
 const recomendList = ref([])
 
-
 const AREA_TEXT = '地域'
 
 const areaId = useRouteParam('area')
@@ -124,15 +311,19 @@ const activeIndex = ref('')
 const dropDownMenuRef = ref(null)
 // 获取筛选列表
 async function getFilters() {
-  const { data } = await request(`/website/tourism/projectTravelNotes/travelNotesDirectoryList`).finally(() => { closeToast() })
+  const { data } = await request(
+    `/website/tourism/projectTravelNotes/travelNotesDirectoryList`
+  ).finally(() => {
+    closeToast()
+  })
 
   if (!Array.isArray(data)) return getList()
-  let country = null;
+  let country = null
   data.map((item, index) => {
     data[index].id = item.areaId
     data[index].text = item.areaName
     if (Array.isArray(item.children)) {
-      data[index].children.unshift({ countryName: '全部', countryId: item.areaId+',all' })
+      data[index].children.unshift({ countryName: '全部', countryId: item.areaId + ',all' })
       item.children.map((item2, index2) => {
         data[index].children[index2].id = item2.countryId
         data[index].children[index2].text = item2.countryName
@@ -140,35 +331,35 @@ async function getFilters() {
         data[index].children[index2].areaName = item.areaName
       })
     }
-    
-    if(route.query.area && item.areaId == route.query.area){
+
+    if (route.query.area && item.areaId == route.query.area) {
       curFilter.value.areaId = route.query.area
       document.title = item.areaName
       areaIndex.value = index + 1
       areaFilterTitle.value = item.areaName
     }
 
-    if(route.query.country){
-      const con = item.children.filter(c=>c.countryId == route.query.country)[0]
-      if(con){
+    if (route.query.country) {
+      const con = item.children.filter((c) => c.countryId == route.query.country)[0]
+      if (con) {
         country = con
       }
     }
   })
-  if(country){
+  if (country) {
     curFilter.value.countryId = route.query.country
     curFilter.value.id = route.query.country
-    areaFilterTitle.value = country.areaName +'-'+country.countryName
-  }else{
-    if(curFilter.value.areaId){
+    areaFilterTitle.value = country.areaName + '-' + country.countryName
+  } else {
+    if (curFilter.value.areaId) {
       curFilter.value.countryId = curFilter.value.areaId + ',all'
       curFilter.value.id = curFilter.value.areaId + ',all'
     }
   }
-  if(route.query.country && !route.query.area){
-    data.map((item,index)=>{
-      item.children.map(subItem=>{
-        if(subItem.id == route.query.country){
+  if (route.query.country && !route.query.area) {
+    data.map((item, index) => {
+      item.children.map((subItem) => {
+        if (subItem.id == route.query.country) {
           areaIndex.value = index + 1
         }
       })
@@ -226,8 +417,13 @@ async function getList() {
   if (travelWriteType.value) {
     param.travelWriteType = travelWriteType.value
   }
+  if (barIndex.value == 1) {
+    param.isFollow = 1
+  }
   loading.value = true
-  const { data } = await request(`/website/tourism/projectTravelNotes/travelNotesPageList`, { query: param }).finally(() => closeToast())
+  const { data } = await request(`/website/tourism/projectTravelNotes/travelNotesPageList`, {
+    query: param
+  }).finally(() => closeToast())
 
   dataList.value = dataList.value.concat(data.dataList)
   loading.value = false
@@ -237,6 +433,14 @@ async function getList() {
     finished.value = false
   }
 }
+
+// 改变头部下标
+function changeBar(index) {
+  barIndex.value = index
+  dataList.value = []
+  getList()
+}
+
 // 选择洲
 function handleAreaClick(index) {
   areaIndex.value = index
@@ -259,28 +463,18 @@ function handleFilterClick(item) {
   // getRecommend()
   // dropDownMenuRef.value && dropDownMenuRef.value.close()
   // document.title = `${item.id ? '游记-' + item.areaName + '-' + item.text : '旅游笔记'}`
-  
-  let search =''
-  if(item.areaId) search+='area='+item.areaId
-  if(item.id) {
+
+  let search = ''
+  if (item.areaId) search += 'area=' + item.areaId
+  if (item.id) {
     const i = item.countryId.split(',')
-    if(!i[1]){
-      search+=`${item.areaId?'&':''}country=${item.id}`
+    if (!i[1]) {
+      search += `${item.areaId ? '&' : ''}country=${item.id}`
     }
-    
   }
-  location.search=`${search}`
-  
+  location.search = `${search}`
 }
 
-function transferCount(num = 0) {
-  if (isNaN(num)) return 0
-  if (num > 10000) {
-    return (num / 10000).toFixed(1) + 'w'
-  } else {
-    return num
-  }
-}
 function loadMore() {
   pageNum.value++
   getList()
@@ -290,11 +484,11 @@ function convertTag(str = '') {
   return str.split('&')
 }
 
-async function getRecommend(){
-  const param={
-    isHotspot:1,
-    pageNum:1,
-    pageSize:10
+async function getRecommend() {
+  const param = {
+    isHotspot: 1,
+    pageNum: 1,
+    pageSize: 10
   }
   if (curFilter.value.areaId) param.areaId = curFilter.value.areaId
 
@@ -302,12 +496,82 @@ async function getRecommend(){
     const countryId = curFilter.value.id.split(',')
     if (!countryId[1]) param.countryId = curFilter.value.id
   }
-  const {data:{dataList}} = await request('website/tourism/project/list',{ query: param })
+  const {
+    data: { dataList }
+  } = await request('website/tourism/project/list', { query: param })
   recomendList.value = dataList || []
 }
+
+// 切换到搜索页面
+function isShowSearch() {
+  showSearch.value = true
+  getSearchRecordList()
+}
+
+// 搜索游记
+async function onSearch() {
+  const { data } = await request(
+    '/website/tourism/projectTravelNotes/travelNotesPageListBySearch',
+    {
+      query: {
+        pageNum: 1,
+        pageSize: 20,
+        searchString: value.value
+      }
+    }
+  ).finally(() => {
+    showSearch.value = false
+    value.value = ''
+  })
+
+  if (Array.isArray(data.dataList) && data?.dataList?.length) {
+    dataList.value = data.dataList
+  } else {
+    dataList.value = []
+  }
+  getSearchRecordList()
+}
+
+// 搜索页面的请求
+const recordList = ref([])
+async function getSearchRecordList() {
+  const {
+    data: { dataList, totalCount }
+  } = await request('/website/tourism/projectSearchRecords/searchRecordList', {
+    pageNum: 1,
+    pageSize: 20
+  })
+
+  if (Array.isArray(dataList) && dataList.length) {
+    recordList.value = dataList
+  }
+}
+
+//根据id 去清楚记录
+async function handleDeleteSearchRecordById(id) {
+  const res = await request('/website/tourism/projectSearchRecords/deleteSearchRecordById', {
+    method: 'post',
+    body: {
+      id
+    }
+  })
+
+  if (res && res.success) {
+    getSearchRecordList()
+  }
+}
+// 清空历史记录
+async function handleClearSearchRecord() {
+  const res = await request('/website/tourism/projectSearchRecords/clearSearchRecord', {
+    method: 'post'
+  })
+
+  if (res && res.success) {
+    recordList.value = []
+  }
+}
 onMounted(async () => {
   getFilters()
-  
 })
 
 useSeoMeta({
@@ -345,4 +609,11 @@ useSeoMeta({
   overflow: hidden;
   -webkit-box-orient: vertical;
 }
+
+.barIndex {
+  width: 40px;
+  height: 2px;
+  background-color: #ff9300;
+  border-radius: 50%;
+}
 </style>

+ 187 - 0
src/pages/view-group-chat/[id].vue

@@ -0,0 +1,187 @@
+<template>
+  <div class="h-[100vh] bg-[#F7F8FA]">
+    <div class="h-48 flex justify-between items-center bg-white px-12 mb-12">
+      <span
+        @click="goToBack"
+        class="iconfont icon-left text-black-3 font-[600]"
+        style="font-size: 24px"
+      ></span>
+
+      <span class="text-2xl font-semibold">{{ othersUserInfo?.showName }}的群聊</span>
+      <span></span>
+    </div>
+    <van-list
+      v-if="groupList?.length"
+      v-model:loading="loading"
+      :immediate-check="false"
+      :finished="finished"
+      loading-text="加载中..."
+      error-text="获取失败"
+      @load="loadMore"
+    >
+      <van-cell-group
+        v-for="(item, index) in groupList"
+        :key="item.id"
+        inset
+        style="margin-bottom: 13px"
+      >
+        <van-cell :title="item?.groupName" center :label="`${item?.count}人`">
+          <template #icon>
+            <div class="w-48 h-48 rounded-full mr-12">
+              <MultiHeader v-if="item?.memberHeadImg" :size="48" :img-urls="item?.memberHeadImg" />
+            </div>
+          </template>
+          <template #value>
+            <van-button
+              size="small"
+              class="follow-item__button"
+              :disabled="false"
+              :color="FANS_STATUS[item.status]?.bg"
+              type="primary"
+              @click.stop="handleJoinGroup(item)"
+              round
+            >
+              {{ FANS_STATUS[item.status]?.text }}
+            </van-button>
+          </template>
+        </van-cell>
+        <van-cell v-if="item.description">
+          <template #icon>
+            <h1 class="w-48 mr-12 text-base font-semibold">群介绍</h1>
+          </template>
+          <template #title>
+            <p class="line-clamp-2 text-base text-black/[0.6]">
+              {{ item.description }}
+            </p>
+          </template>
+        </van-cell>
+      </van-cell-group>
+    </van-list>
+    <Empty v-else title="暂无群聊" />
+  </div>
+</template>
+<script setup>
+const id = useRouteParam('id')
+const router = useRouter()
+const userInfoStore = useUserInfoStore()
+const { userInfo } = storeToRefs(userInfoStore)
+const FANS_STATUS = {
+  0: { status: 0, text: '申请加入 ', describe: '未关注', bg: '#FD9A00' },
+  1: { status: 1, text: '立即聊天', describe: '已关注', bg: '#FD9A00' },
+  4: { status: 4, text: '已申请', describe: '相互关注', bg: '#999' }
+}
+const loading = ref(true)
+const finished = ref(false)
+const pageNum = ref(1)
+const pageSize = ref(10)
+
+const othersUserInfo = ref(null) //他人信息
+const groupList = ref([]) //下方游记
+
+// 关注
+const saveLoading = ref(false)
+
+// 加入群聊
+const handleJoinGroup = (item) => {
+  if (!item.status) {
+    getGroupAdd(item.id)
+  }
+  if (item.status == 1) {
+    navigateTo({
+      path: '/chat/group-chat',
+      query: { groupId: item.id },
+      replace: true
+    })
+  }
+  // if (item.status == 2) {
+  //   navigateTo('/chat/group-chat', {
+  //     query: { groupId: item.id },
+  //     replace: true
+  //   })
+  // }
+}
+
+// 加群聊
+const getGroupAdd = async (groupId) => {
+  let { data } = await request('/website/tourMember/invite', {
+    method: 'post',
+    body: {
+      groupId,
+      ids: [userInfo.value.userId]
+    }
+  })
+  if (data) {
+    getList()
+  }
+}
+
+function goToBack() {
+  router.back()
+}
+
+// 获取他人主页的信息
+async function getHomePageDetailsDto() {
+  const { data } = await request('/website/tourism/travelNotesTopic/homePageDetailsDto', {
+    query: {
+      userId: id.value
+    }
+  })
+
+  if (data) {
+    othersUserInfo.value = data
+  }
+}
+
+function loadMore() {
+  pageNum.value++
+  getUserGroup()
+}
+// 获取他人公开的群聊
+async function getUserGroup() {
+  try {
+    const param = {
+      pageNum: pageNum.value,
+      pageSize: pageSize.value
+    }
+
+    loading.value = true
+    const {
+      data: { dataList, totalCount }
+    } = await request('/website/tourGroup/getUserGroup', {
+      query: {
+        ...param,
+        userId: id.value
+      }
+    })
+
+    if (Array.isArray(dataList) && dataList.length) {
+      groupList.value = groupList.value.concat(dataList)
+    } else {
+      groupList.value = []
+    }
+
+    loading.value = false
+    if (groupList.value.length >= totalCount) {
+      finished.value = true
+    } else {
+      finished.value = false
+    }
+  } catch (error) {}
+}
+
+onMounted(() => {
+  getHomePageDetailsDto()
+  getUserGroup()
+})
+</script>
+<style lang="scss" scoped>
+.follow-item {
+  .follow-item__button {
+    min-width: 60px;
+    padding: 0 16px;
+    border-radius: 100px;
+    text-align: center;
+    font-size: 14px;
+  }
+}
+</style>

+ 217 - 65
src/pages/yj/[id].vue

@@ -1,27 +1,29 @@
 <template>
   <div>
     <div v-if="['success'].includes(status)" @click="pageClick">
-      <van-cell center size="large" :title="userInfo.showName">
+      <van-cell center size="large" :title="detailData?.createName">
         <template #icon>
           <div class="w-30 h-30 rounded-full mr-8 overflow-hidden">
             <img
-              v-if="userInfo.headImageUrl"
+              v-if="detailData?.createImage"
               class="w-full h-full object-cover"
-              :src="userInfo.headImageUrl"
+              :src="detailData?.createImage"
               alt=""
             />
             <img v-else class="w-full h-full object-cover" :src="defaultAvatar" alt="" />
           </div>
         </template>
         <template #label>
-          <span class="mr-8">{{ detailData?.endPlaceDictMap?.name || '' }}</span>
+          <span class="mr-8">{{ detailData?.endArea || '' }}</span>
           <span>{{ detailData?.endPlaceDictMap?.name || '' }}</span>
         </template>
         <template #value>
           <div
-            :class="`mr-7 inline-block h-28 box-border rounded-full px-14 py-3  text-base  font-semibold border ${followState(detailData.status).color}`"
+            v-if="detailData?.userState != 3 || detailData?.createUser != userInfo?.userId"
+            @click="handleFollow"
+            :class="`mr-7   inline-block box-border  h-28  rounded-full px-14  py-1  text-base  font-semibold border ${followState(detailData?.userState)?.color}`"
           >
-            {{ followState(detailData.status).text }}
+            {{ followState(detailData?.userState)?.text }}
           </div>
         </template>
         <template #right-icon>
@@ -78,14 +80,21 @@
           </div>
         </div>
 
-        <div class="mt-10 w-full" v-for="con in detailData.travelNotesContent" :key="con.id">
-          <!-- <template v-if="con.type == 'image'">
+        <!-- <div class="mt-10 w-full" v-for="con in detailData.travelNotesContent" :key="con.id">
+          <template v-if="con.type == 'image'">
             <img :src="con.content" class="w-full rounded-xl" alt="" />
-          </template> -->
+          </template>
           <template v-if="con.type !== 'image'">
             <div class="w-full" style="word-wrap: break-word" v-html="con.content"></div>
           </template>
-        </div>
+        </div> -->
+        <div
+          id="view-note"
+          class="mt-10 w-full"
+          style="word-wrap: break-word"
+          @click="bindFn"
+          v-html="detailData?.tourTourismProjectTravelNotesWriteContent?.content"
+        ></div>
 
         <div class="text-sm text-black-6 py-12 box-border">
           <span class="mr-8">出发时间{{ contentIsEmpty(detailData?.departureTime) }}</span>
@@ -102,16 +111,23 @@
           center
           is-link
           class="rounded-lg"
-          value="45人"
+          :value="`${detailData?.tourImGroupCount}人`"
           @click="showGroupDialog = true"
         >
           <template #icon>
             <div class="w-30 h-30 overflow-hidden rounded-full mr-8">
-              <img class="w-full h-full object-cover" src="" alt="" />
+              <img
+                v-if="detailData?.tourImGroup?.groupAvatar"
+                class="w-full h-full objct-cover"
+                :src="detailData.tourImGroup?.groupAvatar"
+                alt=""
+              />
             </div>
           </template>
           <template #title>
-            <h1 class="text-sm text-black-3">{{ '群聊' }}</h1>
+            <h1 class="line-clamp-1 text-sm text-black-3">
+              {{ detailData.tourImGroup?.groupName }}
+            </h1>
           </template>
         </van-cell>
 
@@ -122,7 +138,7 @@
 
         <div
           v-if="detailData?.contactCodeConvert"
-          class="flex items-center h-[112px] border rounded-[20px] overflow-hidden pb-12"
+          class="flex items-center h-[112px] border rounded-[20px] overflow-hidden"
         >
           <div
             class="w-[112px] h-full bg-[#F5F5F5] flex items-center justify-center rounded-[20px]"
@@ -161,7 +177,7 @@
               alt=""
             />
             <h1
-              class="w-full line-clamp-2 text-base font-medium text-black-3 group-hover:text-linkHover"
+              class="w-full line-clamp-1 text-base font-medium text-black-3 group-hover:text-linkHover"
             >
               {{ item.projectTitle }}
             </h1>
@@ -170,7 +186,7 @@
 
         <div class="text-black-6 text-base">
           评论
-          <!-- <span v-if="commentList.length">({{ formatNumberLetter(commentList.length) }}条)</span> -->
+          <span v-if="commentCount > 0">({{ formatNumberLetter(commentCount) }}条)</span>
         </div>
 
         <template v-if="commentList.length">
@@ -195,8 +211,8 @@
                   <div class="text-black-6 text-sm">
                     {{ item?.commentUserIdDictMap?.name }}
                   </div>
-                  <!-- line-clamp-2 -->
                   <div
+                    @click.stop="addReply(item)"
                     class="w-full text-base text-black-3"
                     v-html="coveredContent(item?.commentContent)"
                   ></div>
@@ -211,12 +227,12 @@
                     size="20px"
                     color="#666"
                   />
-                  <van-icon
+                  <!-- <van-icon
                     @click.stop="addReply(item)"
                     name="comment-o"
                     size="20px"
                     color="#666"
-                  />
+                  /> -->
                 </div>
                 <div class="w-full flex justify-between items-center text-black-6 text-[10px]">
                   <span>
@@ -224,17 +240,18 @@
                   </span>
                   <span class="text-sm">
                     <span
-                      @click.stop="handleCommentCollect"
-                      class="iconfont icon-star text-black-3 mr-4"
+                      @click.stop="handleCommentCollect(item)"
+                      :class="`iconfont ${item.isCollect ? 'icon-star-fill text-[#FF9300] ' : 'icon-star text-black-3'}   mr-4`"
                       style="font-size: 13px"
                     ></span>
-                    <span class="mr-10">{{ 999 }}</span>
+
+                    <span class="mr-10">{{ item.collectNum }}</span>
                     <span
                       @click.stop="doCommenteStar"
                       class="iconfont icon-Thumbs-up text-black-3 mr-4"
                       style="font-size: 13px"
                     ></span>
-                    <span>{{ 999 }}</span>
+                    <span class="mr-10">{{ item.likeNum }}</span>
                   </span>
                 </div>
               </div>
@@ -269,10 +286,13 @@
                             {{ subItem?.commentUserIdDictMap?.name }}
                           </div>
                           <span class="text-black-9 text-base">
-                            <span class="text-black-3">回复</span>
+                            <span @click.stop="addReply(subItem, item.id)" class="text-black-3">
+                              回复
+                            </span>
                             {{ subItem?.replyUserIdDictMap?.name }}:
                           </span>
                           <span
+                            @detailData.value.click.stop="addReply(subItem, item.id)"
                             class="text-base text-black-3"
                             v-html="coveredContent(subItem?.commentContent)"
                           ></span>
@@ -286,12 +306,12 @@
                             size="20px"
                             color="#666"
                           />
-                          <van-icon
+                          <!-- <van-icon
                             @click.stop="addReply(subItem, item.id)"
                             name="comment-o"
                             size="20px"
                             color="#666"
-                          />
+                          /> -->
                         </div>
                       </div>
                     </div>
@@ -303,18 +323,18 @@
                       </span>
                       <span class="text-sm">
                         <span
-                          @click.stop="handleCommentCollect"
+                          @click.stop="handleCommentCollect(subItem)"
                           class="iconfont icon-star text-black-3 mr-4"
                           style="font-size: 13px"
                         ></span>
-                        <span class="mr-10">{{ 999 }}</span>
+                        <span class="mr-10">{{ subItem.collectNum }}</span>
                         <!-- icon-like-fill  doCommenteStar -->
                         <span
                           @click.stop="showLike = !showLike"
                           :class="`iconfont ${showLike ? ' icon-like-fill text-[#FF476A]' : 'icon-Thumbs-up text-black-3'}  mr-4`"
                           style="font-size: 13px"
                         ></span>
-                        <span>{{ 999 }}</span>
+                        <span class="mr-10">{{ subItem.likeNum }}</span>
                       </span>
                     </div>
                   </div>
@@ -327,7 +347,7 @@
           <div class="bg-[#F5F5F5] rounded-[20px] flex items-center justify-center h-[140px] mt-10">
             <div>
               <div>
-                <img class="w-[84px]" src="~/assets/img/yj/no-comments.png" alt="" />
+                <img class="w-84" src="~/assets/img/yj/no-comments.png" alt="" />
               </div>
               <div class="text-center text-[#999]">暂无评论</div>
             </div>
@@ -347,25 +367,26 @@
           <div class="w-263 mx-auto my-20 flex justify-center flex-wrap">
             <div class="w-44 h-44 rounded-full mb-11 overflow-hidden">
               <img
+                v-if="detailData?.tourImGroup?.groupAvatar"
                 class="w-full h-full objct-cover"
-                src="https://fastly.jsdelivr.net/npm/@vant/assets/apple-3.jpeg"
+                :src="detailData.tourImGroup?.groupAvatar"
+                alt=""
               />
             </div>
 
             <div class="w-full text-center mb-11">
               <span
+                v-if="detailData?.type?.typeName"
                 class="mb-12 rounded-md box-border mx-auto text-sm p-4 text-[#FF9300] bg-[#FF9300]/[0.1]"
               >
-                {{ '金融英航' }}
+                {{ detailData?.type?.typeName }}
               </span>
             </div>
             <h1 class="w-full font-medium text-black-3 text-base text-center mb-12">
-              {{ '这是群聊名字这是群聊名字这是群聊名字这是群聊名字这是群聊名字(999)' }}
+              {{ detailData?.tourImGroup?.groupName }}
             </h1>
-            <p class="w-full line-clamp-3 text-black-6 text-sm mx-auto mb-12">
-              {{
-                '前些时候网红名师的一席话,是撕开了皇帝的新装,还是纯属外行凑热闹?asdasdasda 这似乎是个见仁见智的问题。可这两天“张雪峰效应”的席卷全网,...'
-              }}
+            <p class="w-full line-clamp-3 text-black-6 text-sm mx-auto mb-12 text-center">
+              {{ detailData?.tourImGroup?.description }}
             </p>
           </div>
 
@@ -375,13 +396,20 @@
             style="--van-cell-horizontal-padding: 24px"
             center
             is-link
+            @click="
+              navigateTo({
+                path: `/profile-others/${detailData.groupOwnerId}`
+              })
+            "
           >
             <template #title>
-              <h1 class="text-sm text-black-3 line-clamp-1">{{ '何事秋风悲画扇丶' }}</h1>
+              <h1 class="text-sm text-black-3 line-clamp-1">
+                {{ detailData.groupOwnerName }}
+              </h1>
             </template>
             <template #icon>
               <div class="w-24 h-24 rounded-full overflow-hidden mr-8">
-                <img class="w-full h-full objct-cover" src="" alt="" />
+                <img class="w-full h-full objct-cover" :src="detailData.groupOwnerImage" alt="" />
               </div>
             </template>
             <template #value>
@@ -466,14 +494,14 @@
           class="fixed h-80 bottom-0 left-0 w-full bg-white flex justify-between items-center p-16"
         >
           <div
-            @click="openShowInput"
-            class="w-130 h-48 border rounded-full bg-[#F5F5F5] flex justify-center items-center text-black/[0.4]"
+            @click.stop="openShowInput"
+            class="w-130 h-48 border rounded-full bg-[#F5F5F5] flex justify-center items-center text-black/[0.4] bg-[#eee]"
           >
             留下足迹吧~
           </div>
 
           <div class="flex justify-end shrink-0 items-center text-base text-black-3">
-            <div @click="doStar" class="flex items-center mr-10">
+            <div @click.stop="doStar" class="flex items-center mr-10">
               <van-icon
                 v-if="detailData.isLike"
                 size="24"
@@ -482,19 +510,19 @@
                 class="mr-5"
               />
               <van-icon v-else name="good-job-o" color="#333333" size="24" class="mr-5" />
-              {{ detailData.likeCount }}
+              {{ formatNumberLetter(detailData.likeCount) }}
             </div>
-            <div class="flex items-center mr-10" @click="handleCollect">
+            <div class="flex items-center mr-10" @click.stop="handleCollect">
               <van-icon v-if="isCollect" size="24" color="#FD9A00" name="star" class="mr-5" />
               <van-icon v-else name="star-o" size="24" color="#333333" class="mr-5" />
-              收藏
+              {{ formatNumberLetter(detailData.likeCount) }}
             </div>
-            <div class="flex items-center" @click="">
+            <div class="flex items-center" @click.stop="">
               <span
                 class="iconfont icon-chat-message text-black-3 mr-5"
                 style="font-size: 24px"
               ></span>
-              {{ '评论' }}
+              {{ formatNumberLetter(commentCount) }}
             </div>
           </div>
         </div>
@@ -523,7 +551,12 @@ const showInput = ref(false)
 
 // 游记内容
 const { data: detailData, status } = useMyFetch(
-  `website/tourism/projectTravelNotes/travelNotesDetail?id=${id.value}`
+  `/website/tourism/projectTravelNotes/travelNotesDetail?id=${id.value}`,
+  {
+    headers: {
+      Authorization: token.value
+    }
+  }
 )
 
 // 热门游记列表
@@ -594,7 +627,7 @@ async function doStar() {
 
 // 评论的 收藏/取消收藏
 const showStar = ref(false)
-async function handleCommentCollect() {
+async function handleCommentCollect(parmas) {
   if (!token.value) {
     showConfirmDialog({
       showConfirmDialog: true,
@@ -606,16 +639,16 @@ async function handleCommentCollect() {
     })
     return
   }
-  const type = isCollect.value ? 0 : 1
+  const type = parmas.isCollect ? 0 : 1
   showLoadingToast({
     forbidClick: true,
     duration: 0
   })
-  await request(`/website/tourism/projectTravelNotes/userCollectTravelNotesUpdate`, {
+  await request(`/website/comment/tourTravelNotesComment/commentCollect`, {
     method: 'post',
-    body: { type, travelNotesId: id.value }
+    body: { commentId: parmas.id }
   }).finally(() => closeToast())
-  isCollectTravelNotes()
+  getComments()
   showToast(type == 1 ? '收藏成功' : '已取消收藏')
 }
 
@@ -652,14 +685,14 @@ async function getUserInfo() {
 
 // 关注状态
 function followState(params) {
-  if (!params) {
+  if (params == 0 || params == 4) {
     return {
       color: 'text-[#FD9A00] border-[#FD9A00]',
       text: '关注'
     }
   }
 
-  if (params == 1) {
+  if (params == 1 || params == 2) {
     return {
       color: 'text-black-9 border-black-9',
       text: '已关注'
@@ -667,6 +700,37 @@ function followState(params) {
   }
 }
 
+const saveLoading = ref(false)
+// 关注方法
+async function handleFollow() {
+  if (!token.value) {
+    showConfirmDialog({
+      showConfirmDialog: true,
+      title: '提示',
+      message: '登录后才可以关注',
+      theme: 'round-button'
+    }).then(async () => {
+      navigateTo({ path: '/login' })
+    })
+    return
+  }
+  try {
+    if (saveLoading.value) return
+    saveLoading.value = true
+    const { data } = await request(`/website/tourism/fans/saveConcern`, {
+      method: 'post',
+      body: {
+        attentionId: detailData.value.createUser
+      }
+    }).finally(() => (saveLoading.value = false))
+    if (!data) return
+    detailData.value.userState = data.fansStatus
+    showSuccessToast('操作成功')
+  } catch (e) {
+    showToast('操作失败')
+  }
+}
+
 // 内容是否为空
 function contentIsEmpty(content) {
   if (content) {
@@ -692,12 +756,21 @@ function textareaFocus() {
 
 // 获取评论列表
 const commentList = ref([])
+const commentCount = ref(0)
 async function getComments() {
   const { data } = await request(
     '/website/comment/tourTravelNotesComment/list?travelNoteId=' + id.value
   )
-  if (!Array.isArray(data) || !data.length) return (commentList.value = [])
-  commentList.value = data
+  if (
+    !Array.isArray(data.tourTravelNotesCommentVoList) ||
+    !data.tourTravelNotesCommentVoList.length
+  )
+    return (commentList.value = [])
+  commentList.value = data.tourTravelNotesCommentVoList
+
+  if (data.commentCount) {
+    commentCount.value = data.commentCount
+  }
 }
 
 // 转换评论中的一些非字符emoji
@@ -773,9 +846,12 @@ function addReply(item, parentId) {
     })
     return
   }
-  textareaRef.value.focus()
-  replyComment.value = item
-  parentId && (replyComment.value.parentId = parentId)
+  showInput.value = true
+  nextTick(() => {
+    textareaRef.value.focus()
+    replyComment.value = item
+    parentId && (replyComment.value.parentId = parentId)
+  })
 }
 
 // 发布评论
@@ -810,6 +886,7 @@ async function addComment() {
       showToast('评论发布成功')
       getComments()
       showEmoji.value = false
+      showInput.value = false
       document.querySelector('#commentsBox').scrollIntoView({
         behavior: 'smooth',
         block: 'start'
@@ -820,7 +897,7 @@ async function addComment() {
 function pageClick() {
   nextTick(() => {
     showEmoji.value = false
-    textareaRef.value.blur()
+    textareaRef?.value?.blur()
     replyComment.value = {}
     cursorIndex.value = 0
   })
@@ -837,6 +914,25 @@ function share() {
   )
 }
 
+// 去往话题详情
+function gotToTopic(isEdit) {
+  return isEdit
+    ? `${navigateTo({
+        path: '/topic',
+        query: {
+          topicName: parmas.name
+        }
+      })}`
+    : ''
+}
+
+// 去往他人页面
+function gotToProfileOthers(userId) {
+  navigateTo({
+    path: `/profile-others/${detailData.value.mentions[0]?.userId}`
+  })
+}
+
 // 群聊弹窗
 const showGroupDialog = ref(false)
 
@@ -851,6 +947,57 @@ function openShowInput() {
 function goBack() {
   router.back()
 }
+// 给预览的地方绑定点击事件
+const bindFn = async () => {
+  // 获取文档中的所有 span 标签
+  await nextTick(() => {
+    const container = document.getElementById('view-note')
+    const spans = container?.querySelectorAll('span')
+    console.log(spans, 'spans')
+
+    spans?.forEach((span) => {
+      console.log(
+        span.getAttribute('data-denotation-char'),
+        "span.getAttribute('data-denotation-char')"
+      )
+      // console.log('@用户ID', span.getAttribute('data-id'))
+      if (span.getAttribute('data-denotation-char') === '@') {
+        navigateTo({
+          path: `/profile-others/${span.getAttribute('data-id')}`
+        })
+        // span.addEventListener('click', (event) => {
+        //   console.log('@用户ID', span.getAttribute('data-id'))
+        //   // console.log('@用户名字', span.innerHTML)
+        //   // alert('@用户:' + span.innerText)
+        //   navigateTo({
+        //     path: `/profile-others/${span.getAttribute('data-id')}`
+        //   })
+        // })
+      }
+      if (span.getAttribute('data-denotation-char') === '#') {
+        let topicName = span.innerText.replace('#', '')
+        navigateTo({
+          path: '/topic',
+          query: {
+            topicName
+          }
+        })
+        // span.addEventListener('click', (event) => {
+        //   console.log('话题ID', span.getAttribute('data-id'))
+        //   console.log('话题内容', span.innerText)
+        //   let topicName = span.innerText.replace('#', '')
+
+        //   navigateTo({
+        //     path: '/topic',
+        //     query: {
+        //       topicName
+        //     }
+        //   })
+        // })
+      }
+    })
+  })
+}
 
 useSeoMeta({
   title: '游记详情',
@@ -860,9 +1007,9 @@ useSeoMeta({
   ogImage: ''
 })
 onMounted(() => {
-  useEventListener('click', openShowInput, { target: document })
-  useEventListener('mousedown', openShowInput, { target: document })
-  useEventListener('touchstart', openShowInput, { target: document })
+  // useEventListener('click', openShowInput, { target: document })
+  // useEventListener('mousedown', openShowInput, { target: document })
+  // useEventListener('touchstart', openShowInput, { target: document })
 
   getComments()
   getUserInfo()
@@ -870,4 +1017,9 @@ onMounted(() => {
 })
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+::v-deep .mention {
+  color: #2c405b;
+  font-weight: bold;
+}
+</style>

+ 10 - 1
src/utils/index.js

@@ -79,6 +79,14 @@ const formatNumberLetter = (n) => {
     return accounting.formatNumber(num)
   }
 }
+const transferCount = (num = 0) => {
+  if (isNaN(num)) return 0
+  if (num > 10000) {
+    return (num / 10000).toFixed(1) + 'w'
+  } else {
+    return num
+  }
+}
 
 const isValidJson = (jsonString) => {
   try {
@@ -113,5 +121,6 @@ export {
   isValidJson,
   uuid,
   priceToArray,
-  formatNumberLetter
+  formatNumberLetter,
+  transferCount
 }

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor