Преглед изворни кода

feat : 游记详情的ui 见面改版 _yj 文件夹是上一个版本的备份。

suwenjiang пре 1 месец
родитељ
комит
9fe18785a8
6 измењених фајлова са 901 додато и 209 уклоњено
  1. 6 4
      .env.development
  2. 8 8
      src/components/CreateNote/Form.vue
  3. 10 4
      src/pages/note-create/index.client.vue
  4. 476 0
      src/pages/test22.vue
  5. 314 139
      src/pages/yj/[id].vue
  6. 87 54
      src/utils/index.js

+ 6 - 4
.env.development

@@ -3,10 +3,12 @@ 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:8084/
-VITE_APP_IM_URL=ws://101.126.146.250:8084/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/
+# VITE_APP_IM_URL=ws://101.126.146.250:8084/system/message
 
 
 

+ 8 - 8
src/components/CreateNote/Form.vue

@@ -19,13 +19,13 @@
     </van-popup>
 
     <van-cell @click="showPublic = true" size="large" center is-link>
-      <template v-if="isPublic == 1" #icon>
+      <template v-if="visibleRange == 1" #icon>
         <span class="iconfont icon-lock text-[#FF9300] mr-12" style="font-size: 16px"></span>
       </template>
       <template v-else #icon>
         <span class="iconfont icon-unlock text-black-3 mr-12" style="font-size: 16px"></span>
       </template>
-      <template v-if="isPublic == 1" #title>
+      <template v-if="visibleRange == 1" #title>
         <span class="text-[#FF9300]">仅自己可见</span>
       </template>
       <template v-else #title>
@@ -41,20 +41,20 @@
       position="bottom"
     >
       <van-cell-group inset>
-        <van-cell @click="(isPublic = 0), (showPublic = false)" title="公开可见" center>
+        <van-cell @click="(visibleRange = 0), (showPublic = false)" title="公开可见" center>
           <template #icon>
             <span class="iconfont icon-unlock text-black-3 mr-12" style="font-size: 16px"></span>
           </template>
-          <template v-if="isPublic == 0" #right-icon>
+          <template v-if="visibleRange == 0" #right-icon>
             <!-- <van-icon color="#FF9300" name="arrow-down" :size="16" /> -->
             <img class="w-16 h-16" :src="check" alt="" />
           </template>
         </van-cell>
-        <van-cell @click="(isPublic = 1), (showPublic = false)" title="仅自己可见" center>
+        <van-cell @click="(visibleRange = 1), (showPublic = false)" title="仅自己可见" center>
           <template #icon>
             <span class="iconfont icon-lock text-black-3 mr-12" style="font-size: 16px"></span>
           </template>
-          <template v-if="isPublic == 1" #right-icon>
+          <template v-if="visibleRange == 1" #right-icon>
             <!-- <van-icon color="#FF9300" name="arrow-down" :size="16" /> -->
             <img class="w-16 h-16" :src="check" alt="" />
           </template>
@@ -282,7 +282,7 @@ const travelModeId = defineModel('travelMode')
 const travelNumber = defineModel('travelNumber')
 const chau = defineModel('chau')
 const chauId = defineModel('chauId')
-const isPublic = defineModel('isPublic') //是否公开
+const visibleRange = defineModel('visibleRange') //是否公开
 const tourImGroupId = defineModel('tourImGroupId') //是否公开
 
 onMounted(() => {
@@ -392,7 +392,7 @@ async function getChatGroupList() {
 //选择群聊的名称
 const chatGroupName = computed(() => {
   if (tourImGroupId.value) {
-    return chatGroupList.value?.filter((i) => i.id == tourImGroupId.value)[0].groupName
+    return chatGroupList.value?.filter((i) => i.id == tourImGroupId.value)[0]?.groupName
   } else {
     return ''
   }

+ 10 - 4
src/pages/note-create/index.client.vue

@@ -219,7 +219,7 @@
           v-model:travelNumber="noteJson.travelNumber"
           v-model:chau="noteJson.chau"
           v-model:chauId="noteJson.chauId"
-          v-model:isPublic="noteJson.isPublic"
+          v-model:visibleRange="noteJson.visibleRange"
           v-model:tourImGroupId="noteJson.tourImGroupId"
         />
       </template>
@@ -490,7 +490,7 @@ const defaultNoteJson = {
   recommendationRate: null,
   travelNumber: null,
   travelNotesContent: [],
-  isPublic: 0, // 是否公开
+  visibleRange: 0, // 是否公开
   chau: null, // 洲
   tourImGroupId: null, // 群聊的id
   chauId: null, // 洲 id
@@ -550,7 +550,7 @@ async function getNoteDetail() {
       noteJson.imgUrls = data.imgUrls ?? []
       noteJson.topics = data.topics ?? []
       noteJson.mentions = data.mentions ?? []
-      noteJson.isPublic = data.isPublic ?? 0
+      noteJson.visibleRange = data.visibleRange ?? 0
       noteJson.travelNotesContent = data.travelNotesContent ?? []
       if (noteJson.travelNotesContent.length === 0) {
         noteJson.travelNotesContent.push({
@@ -979,7 +979,13 @@ const handleOperate = (operate) => {
 function handleTopicAndEit(parmas, pType) {
   let contentText = noteJson.travelNotesContent[editIndex.value].content
   if (pType == '#') {
-    contentText += ` ${parmas.name}`
+    if (contentText.includes('#')) {
+      contentText += ` ${parmas.name}`
+    } else {
+      contentText += `# ${parmas.name}`
+    }
+
+    noteJson.topics.push(parmas.name)
   }
   if (pType == '@') {
     if (contentText.includes('@')) {

+ 476 - 0
src/pages/test22.vue

@@ -0,0 +1,476 @@
+<template>
+  <div class="screen">
+    <div class="frame">
+      <header class="header">
+        <!-- <Close class="close-instance" color="#666666" /> -->
+        <div class="text-wrapper">单独购买</div>
+      </header>
+
+      <div class="input">
+        <div class="div">
+          <div class="item-label">
+            <div class="text">
+              <div class="label">出发日期</div>
+              <div class="required">*</div>
+            </div>
+          </div>
+          <div class="content">请选择出发日期</div>
+        </div>
+        <div class="view">
+          <div class="ICON" />
+        </div>
+      </div>
+
+      <div class="view-2">
+        <div class="text">
+          <div class="label">成人数量</div>
+          <div class="required-2">*</div>
+        </div>
+        <div class="view-3">
+          <Remove class="icon-instance-node" />
+          <div class="text-wrapper-2">0</div>
+          <Add class="icon-instance-node" />
+        </div>
+      </div>
+
+      <div class="view-2">
+        <div class="text">
+          <div class="label">儿童数量</div>
+          <div class="required-2">*</div>
+        </div>
+        <div class="view-3">
+          <!-- <Remove class="icon-instance-node" /> -->
+          <div class="text-wrapper-2">0</div>
+          <!-- <Add class="icon-instance-node" /> -->
+        </div>
+      </div>
+
+      <div class="input-2">
+        <div class="div">
+          <div class="item-label">
+            <div class="text">
+              <div class="label">联系人</div>
+              <div class="required-2">*</div>
+            </div>
+          </div>
+          <div class="content-2">请输入联系人</div>
+        </div>
+        <div class="view">
+          <div class="ICON" />
+        </div>
+      </div>
+
+      <div class="input-2">
+        <div class="div">
+          <div class="item-label">
+            <div class="text">
+              <div class="label">联系电话</div>
+              <div class="required-2">*</div>
+            </div>
+          </div>
+          <div class="content-2">请输入联系电话</div>
+        </div>
+        <div class="view">
+          <div class="ICON" />
+        </div>
+      </div>
+
+      <div class="input-2">
+        <div class="div">
+          <div class="item-label">
+            <div class="text">
+              <div class="label">微信号</div>
+              <div class="required-2">*</div>
+            </div>
+          </div>
+          <div class="content-2">请输入微信号</div>
+        </div>
+        <div class="view">
+          <div class="ICON" />
+        </div>
+      </div>
+
+      <div class="input-2">
+        <div class="div">
+          <div class="item-label">
+            <div class="text">
+              <div class="label">备用电话</div>
+              <div class="required">*</div>
+            </div>
+          </div>
+          <div class="content-2">请输入备用联系电话</div>
+        </div>
+        <div class="view">
+          <div class="ICON" />
+        </div>
+      </div>
+    </div>
+
+    <div class="view-4">
+      <div class="frame-2">
+        <div class="group">
+          <div class="text-wrapper-3">4500</div>
+          <div class="text-wrapper-4">.00</div>
+          <div class="text-wrapper-5">¥</div>
+        </div>
+      </div>
+      <div class="div-wrapper">
+        <div class="text-wrapper-6">提交</div>
+      </div>
+    </div>
+
+    <div class="ios-bars-home">
+      <div class="rectangle" />
+    </div>
+  </div>
+</template>
+
+<script></script>
+
+<style>
+.screen {
+  align-items: center;
+  background-color: #ffffff;
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  position: relative;
+}
+
+.screen .frame {
+  align-items: flex-start;
+  align-self: stretch;
+  display: flex;
+  flex: 0 0 auto;
+  flex-direction: column;
+  position: relative;
+  width: 100%;
+}
+
+.screen .header {
+  align-self: stretch;
+  background-color: #ffffff;
+  border-radius: 16px 16px 0px 0px;
+  height: 48px;
+  position: relative;
+  width: 100%;
+}
+
+.screen .close-instance {
+  height: 24px !important;
+  left: 12px !important;
+  position: absolute !important;
+  top: 12px !important;
+  width: 24px !important;
+}
+
+.screen .text-wrapper {
+  color: #333333;
+  font-family: 'PingFang SC-Semibold', Helvetica;
+  font-size: 16px;
+  font-weight: 400;
+  height: 24px;
+  left: 156px;
+  letter-spacing: 0;
+  line-height: 24px;
+  position: absolute;
+  top: 11px;
+  white-space: nowrap;
+}
+
+.screen .input {
+  align-items: center;
+  align-self: stretch;
+  background-color: var(--gray-white);
+  display: flex;
+  flex: 0 0 auto;
+  gap: 12px;
+  padding: 0px 0px 0px 12px;
+  position: relative;
+  width: 100%;
+}
+
+.screen .div {
+  align-items: flex-start;
+  display: flex;
+  flex: 1;
+  flex-grow: 1;
+  gap: 16px;
+  height: 56px;
+  padding: 16px 16px 16px 0px;
+  position: relative;
+}
+
+.screen .item-label {
+  align-items: center;
+  display: flex;
+  gap: 4px;
+  position: relative;
+  width: 81px;
+}
+
+.screen .text {
+  align-items: flex-start;
+  display: flex;
+  flex: 1;
+  flex-grow: 1;
+  gap: 4px;
+  position: relative;
+}
+
+.screen .label {
+  color: var(--texticonfont-gy1-100);
+  font-family: var(--body-large-font-family);
+  font-size: var(--body-large-font-size);
+  font-style: var(--body-large-font-style);
+  font-weight: var(--body-large-font-weight);
+  letter-spacing: var(--body-large-letter-spacing);
+  line-height: var(--body-large-line-height);
+  margin-top: -1px;
+  position: relative;
+  white-space: nowrap;
+  width: fit-content;
+}
+
+.screen .required {
+  color: var(--error-error6-normal);
+  font-family: var(--body-large-font-family);
+  font-size: var(--body-large-font-size);
+  font-style: var(--body-large-font-style);
+  font-weight: var(--body-large-font-weight);
+  letter-spacing: var(--body-large-letter-spacing);
+  line-height: var(--body-large-line-height);
+  margin-top: -1px;
+  opacity: 0;
+  position: relative;
+  white-space: nowrap;
+  width: fit-content;
+}
+
+.screen .content {
+  color: #999999;
+  flex: 1;
+  font-family: var(--body-large-font-family);
+  font-size: var(--body-large-font-size);
+  font-style: var(--body-large-font-style);
+  font-weight: var(--body-large-font-weight);
+  letter-spacing: var(--body-large-letter-spacing);
+  line-height: var(--body-large-line-height);
+  margin-top: -0.5px;
+  position: relative;
+}
+
+.screen .view {
+  align-items: center;
+  display: inline-flex;
+  flex: 0 0 auto;
+  gap: 4px;
+  justify-content: flex-end;
+  padding: 12px;
+  position: relative;
+}
+
+.screen .ICON {
+  background-image: url(./chevron-right.svg);
+  background-size: 100% 100%;
+  height: 24px;
+  position: relative;
+  width: 24px;
+}
+
+.screen .view-2 {
+  align-items: center;
+  align-self: stretch;
+  background-color: var(--white);
+  display: flex;
+  flex: 0 0 auto;
+  justify-content: space-between;
+  padding: 12px;
+  position: relative;
+  width: 100%;
+}
+
+.screen .required-2 {
+  color: var(--error-error6-normal);
+  font-family: var(--body-large-font-family);
+  font-size: var(--body-large-font-size);
+  font-style: var(--body-large-font-style);
+  font-weight: var(--body-large-font-weight);
+  letter-spacing: var(--body-large-letter-spacing);
+  line-height: var(--body-large-line-height);
+  margin-top: -1px;
+  position: relative;
+  white-space: nowrap;
+  width: fit-content;
+}
+
+.screen .view-3 {
+  align-items: center;
+  display: inline-flex;
+  flex: 0 0 auto;
+  gap: 16px;
+  height: 24px;
+  justify-content: flex-end;
+  position: relative;
+}
+
+.screen .icon-instance-node {
+  height: 22px !important;
+  position: relative !important;
+  width: 22px !important;
+}
+
+.screen .text-wrapper-2 {
+  color: var(--font-gy1-90);
+  font-family: var(---font-family);
+  font-size: var(---font-size);
+  font-style: var(---font-style);
+  font-weight: var(---font-weight);
+  letter-spacing: var(---letter-spacing);
+  line-height: var(---line-height);
+  margin-top: -1px;
+  position: relative;
+  text-align: center;
+  white-space: nowrap;
+  width: fit-content;
+}
+
+.screen .input-2 {
+  align-items: center;
+  align-self: stretch;
+  background-color: var(--gray-white);
+  display: flex;
+  flex: 0 0 auto;
+  gap: 16px;
+  padding: 0px 0px 0px 12px;
+  position: relative;
+  width: 100%;
+}
+
+.screen .content-2 {
+  color: var(--texticonfont-gy3-40);
+  flex: 1;
+  font-family: var(--body-large-font-family);
+  font-size: var(--body-large-font-size);
+  font-style: var(--body-large-font-style);
+  font-weight: var(--body-large-font-weight);
+  letter-spacing: var(--body-large-letter-spacing);
+  line-height: var(--body-large-line-height);
+  margin-top: -0.5px;
+  position: relative;
+}
+
+.screen .view-4 {
+  align-items: flex-start;
+  align-self: stretch;
+  display: flex;
+  flex: 0 0 auto;
+  gap: 8px;
+  justify-content: center;
+  padding: 0px 12px;
+  position: relative;
+  width: 100%;
+}
+
+.screen .frame-2 {
+  height: 40px;
+  position: relative;
+  width: 125px;
+}
+
+.screen .group {
+  height: 22px;
+  position: relative;
+  top: 14px;
+  width: 82px;
+}
+
+.screen .text-wrapper-3 {
+  color: #ff0000;
+  font-family: 'DIN-Medium', Helvetica;
+  font-size: 22px;
+  font-weight: 500;
+  left: 10px;
+  letter-spacing: 0;
+  line-height: 22px;
+  position: absolute;
+  text-align: center;
+  top: 0;
+  white-space: nowrap;
+}
+
+.screen .text-wrapper-4 {
+  color: #ff0000;
+  font-family: 'DIN-Medium', Helvetica;
+  font-size: 14px;
+  font-weight: 500;
+  left: 57px;
+  letter-spacing: 0;
+  line-height: 14px;
+  position: absolute;
+  text-align: center;
+  top: 7px;
+  white-space: nowrap;
+}
+
+.screen .text-wrapper-5 {
+  color: #ff0000;
+  font-family: 'DIN-Medium', Helvetica;
+  font-size: 10px;
+  font-weight: 500;
+  left: 0;
+  letter-spacing: 0;
+  line-height: 10px;
+  position: absolute;
+  text-align: center;
+  top: 11px;
+  white-space: nowrap;
+}
+
+.screen .div-wrapper {
+  align-items: center;
+  background-color: var(--brand-brand7-normal);
+  border-radius: 6px;
+  display: flex;
+  flex: 1;
+  flex-grow: 1;
+  gap: 4px;
+  justify-content: center;
+  overflow: hidden;
+  padding: 8px 16px;
+  position: relative;
+}
+
+.screen .text-wrapper-6 {
+  color: var(--texticonfont-wh1-100);
+  font-family: var(--mark-large-font-family);
+  font-size: var(--mark-large-font-size);
+  font-style: var(--mark-large-font-style);
+  font-weight: var(--mark-large-font-weight);
+  letter-spacing: var(--mark-large-letter-spacing);
+  line-height: var(--mark-large-line-height);
+  margin-top: -1px;
+  position: relative;
+  text-align: center;
+  white-space: nowrap;
+  width: fit-content;
+}
+
+.screen .ios-bars-home {
+  align-self: stretch;
+  background-color: #ffffff;
+  height: 34px;
+  position: relative;
+  width: 100%;
+}
+
+.screen .rectangle {
+  background-color: #000000;
+  border-radius: 100px;
+  height: 7px;
+  left: 120px;
+  position: relative;
+  top: 19px;
+  width: 136px;
+}
+</style>

+ 314 - 139
src/pages/yj/[id].vue

@@ -26,21 +26,7 @@
         </template>
         <template #right-icon>
           <span
-            @click="
-              () => {
-                let origin = location.origin
-                let url = `${origin}/yj/${id}`
-
-                navigator.clipboard.writeText(url).then(
-                  () => {
-                    showNotify({ type: 'success', message: '链接已复制' })
-                  },
-                  () => {
-                    showNotify({ message: '链接复制失败' })
-                  }
-                )
-              }
-            "
+            @click="share"
             class="iconfont icon-share text-black-9"
             style="font-size: 26px"
           ></span>
@@ -53,7 +39,7 @@
           indicator-color="#FF9300"
           style="--van-swipe-indicator-margin: 8px"
         >
-          <template v-if="detailData?.imgUrls && detailData?.imgUrls.length">
+          <template v-if="detailData?.imgUrls && detailData?.imgUrls?.length">
             <van-swipe-item
               v-for="image in detailData?.imgUrls"
               :key="image"
@@ -77,26 +63,26 @@
           </van-swipe-item>
         </van-swipe>
         <div
-          v-if="detailData?.imgUrls.length"
+          v-if="detailData?.imgUrls?.length"
           class="absolute top-10 right-20 z-1 w-37 h-24 rounded-full bg-black/[0.3] text-white flex justify-center items-center text-sm"
         >
-          {{ swipeItemIndex }}/{{ detailData?.imgUrls.length }}
+          {{ swipeItemIndex }}/{{ detailData?.imgUrls?.length }}
         </div>
       </div>
       <div class="p-10">
         <div class="flex">
           <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">
+          <div class="ml-10 text-[16px] text-black-3 leading-[28px] font-bold">
             <span v-if="detailData.isOrigin == 1" class="text-[#FD9A00]">【原创】</span>
             {{ detailData.projectTitle }}
           </div>
         </div>
 
         <div class="mt-10 w-full" v-for="con in detailData.travelNotesContent" :key="con.id">
-          <template v-if="con.type == 'image'">
+          <!-- <template v-if="con.type == 'image'">
             <img :src="con.content" class="w-full rounded-xl" alt="" />
-          </template>
-          <template v-else>
+          </template> -->
+          <template v-if="con.type !== 'image'">
             <div class="w-full" style="word-wrap: break-word" v-html="con.content"></div>
           </template>
         </div>
@@ -117,6 +103,7 @@
           is-link
           class="rounded-lg"
           value="45人"
+          @click="showGroupDialog = true"
         >
           <template #icon>
             <div class="w-30 h-30 overflow-hidden rounded-full mr-8">
@@ -128,42 +115,14 @@
           </template>
         </van-cell>
 
-        <div v-if="detailData.createTime" class="text-black-9 text-sm py-12 border-b-[1px]">
-          {{ detailData.createTime }} 发布
+        <div v-if="detailData.createTime" class="flex justify-between text-black-9 text-sm py-12">
+          <span>{{ detailData.createTime }} 发布</span>
+          <span>{{ formatNumberLetter(detailData.pageViewCount) }} 浏览</span>
         </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"
+          class="flex items-center h-[112px] border rounded-[20px] overflow-hidden pb-12"
         >
           <div
             class="w-[112px] h-full bg-[#F5F5F5] flex items-center justify-center rounded-[20px]"
@@ -172,13 +131,52 @@
           </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 class="text-[#666666] text-sm">长按保存图片,扫码添加好友</div>
           </div>
         </div>
+
+        <div class="my-12 border-b-[1px]"></div>
+
+        <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="flex justify-start">
+          <NuxtLink
+            :to="`/yj/${item.id}`"
+            :class="`w-106 group cursor-pointer flex flex-wrap box-border  items-center ${index == 2 ? '' : 'mr-17'} py-12`"
+            v-for="(item, index) in dataList.dataList.slice(0, 3)"
+            :key="item.id"
+          >
+            <img
+              :src="
+                item?.homeHotPicturesAfterConvert?.length ? item.homeHotPicturesAfterConvert[0] : ''
+              "
+              class="h-141 w-full shrink-0 mb-8 rounded-xl object-cover"
+              alt=""
+            />
+            <h1
+              class="w-full line-clamp-2 text-base font-medium text-black-3 group-hover:text-linkHover"
+            >
+              {{ item.projectTitle }}
+            </h1>
+          </NuxtLink>
+        </div>
+
+        <div class="text-black-6 text-base">
+          评论
+          <!-- <span v-if="commentList.length">({{ formatNumberLetter(commentList.length) }}条)</span> -->
+        </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">
+              <div class="w-37 h-37 bg-[#ddd] rounded-full shrink-0">
                 <img
                   v-if="item?.headImageUrlDictMap?.name"
                   class="w-full h-full object-cover rounded-full"
@@ -192,19 +190,18 @@
                   alt=""
                 />
               </div>
-              <div class="flex-1 pl-10 flex justify-between">
+              <div class="flex-1 pl-10 flex justify-between flex-wrap">
                 <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 class="text-black-6 text-sm">
+                    {{ item?.commentUserIdDictMap?.name }}
                   </div>
-                  <div class="text-[#666] text-[10px]">{{ item?.createTime }}</div>
+                  <!-- line-clamp-2 -->
+                  <div
+                    class="w-full text-base text-black-3"
+                    v-html="coveredContent(item?.commentContent)"
+                  ></div>
                 </div>
+
                 <div class="mt-3 shrink-0">
                   <van-icon
                     @click="deleteComment(item.id)"
@@ -221,42 +218,105 @@
                     color="#666"
                   />
                 </div>
+                <div class="w-full flex justify-between items-center text-black-6 text-[10px]">
+                  <span>
+                    {{ item?.createTime }}
+                  </span>
+                  <span class="text-sm">
+                    <span
+                      @click.stop="handleCommentCollect"
+                      class="iconfont icon-star text-black-3 mr-4"
+                      style="font-size: 13px"
+                    ></span>
+                    <span class="mr-10">{{ 999 }}</span>
+                    <span
+                      @click.stop="doCommenteStar"
+                      class="iconfont icon-Thumbs-up text-black-3 mr-4"
+                      style="font-size: 13px"
+                    ></span>
+                    <span>{{ 999 }}</span>
+                  </span>
+                </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">
+                <!--  style="border-left: 1px solid #ccc" -->
+                <div class="pl-14">
+                  <!-- class="flex justify-start mr-8 flex-wrap" -->
                   <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"
+                    <div class="flex justify-start items-start mb-8">
+                      <div class="w-30 h-30 bg-[#ddd] rounded-full shrink-0 mr-8">
+                        <img
+                          v-if="subItem?.headImageUrlDictMap?.name"
+                          class="w-full h-full object-cover rounded-full"
+                          :src="subItem?.headImageUrlDictMap?.name"
+                          alt=""
                         />
-                        <van-icon
-                          @click.stop="addReply(subItem, item.id)"
-                          name="comment-o"
-                          size="20px"
-                          color="#666"
+                        <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
+                        style="width: calc(100% - 38px)"
+                        class="flex justify-between items-start"
+                      >
+                        <div class="">
+                          <div class="text-base text-black-9">
+                            {{ subItem?.commentUserIdDictMap?.name }}
+                          </div>
+                          <span class="text-black-9 text-base">
+                            <span class="text-black-3">回复</span>
+                            {{ subItem?.replyUserIdDictMap?.name }}:
+                          </span>
+                          <span
+                            class="text-base text-black-3"
+                            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>
+                    <div
+                      class="w-full flex justify-between items-center text-black-6 text-[10px] mb-12"
+                    >
+                      <span>
+                        {{ subItem?.createTime }}
+                      </span>
+                      <span class="text-sm">
+                        <span
+                          @click.stop="handleCommentCollect"
+                          class="iconfont icon-star text-black-3 mr-4"
+                          style="font-size: 13px"
+                        ></span>
+                        <span class="mr-10">{{ 999 }}</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>
                     </div>
-                    <div class="text-[#666] text-[10px]">{{ subItem.createTime }}</div>
                   </div>
                 </div>
               </div>
@@ -273,54 +333,70 @@
             </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>
+
+        <van-dialog
+          width="311"
+          confirm-button-color="#FF9300"
+          v-model:show="showGroupDialog"
+          show-cancel-button
+        >
+          <template #title>
+            <h1 class="text-2xl">群聊详情</h1>
+          </template>
+
+          <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
-                :src="
-                  item?.homeHotPicturesAfterConvert?.length
-                    ? item.homeHotPicturesAfterConvert[0]
-                    : ''
-                "
-                class="h-65 w-65 shrink-0 rounded-xl object-cover"
-                alt=""
+                class="w-full h-full objct-cover"
+                src="https://fastly.jsdelivr.net/npm/@vant/assets/apple-3.jpeg"
               />
-              <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 class="w-full text-center mb-11">
+              <span
+                class="mb-12 rounded-md box-border mx-auto text-sm p-4 text-[#FF9300] bg-[#FF9300]/[0.1]"
+              >
+                {{ '金融英航' }}
+              </span>
+            </div>
+            <h1 class="w-full font-medium text-black-3 text-base text-center mb-12">
+              {{ '这是群聊名字这是群聊名字这是群聊名字这是群聊名字这是群聊名字(999)' }}
+            </h1>
+            <p class="w-full line-clamp-3 text-black-6 text-sm mx-auto mb-12">
+              {{
+                '前些时候网红名师的一席话,是撕开了皇帝的新装,还是纯属外行凑热闹?asdasdasda 这似乎是个见仁见智的问题。可这两天“张雪峰效应”的席卷全网,...'
+              }}
+            </p>
           </div>
-        </div>
+
+          <van-cell
+            size="small"
+            :clickable="false"
+            style="--van-cell-horizontal-padding: 24px"
+            center
+            is-link
+          >
+            <template #title>
+              <h1 class="text-sm text-black-3 line-clamp-1">{{ '何事秋风悲画扇丶' }}</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="" />
+              </div>
+            </template>
+            <template #value>
+              <span class="text-sm text-black-6">查看群主主页</span>
+            </template>
+          </van-cell>
+        </van-dialog>
+
         <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="showInput"
+          @click.stop=""
+          class="fixed bottom-0 left-0 w-full bg-white pt-10 pb-30 border"
+        >
           <div v-if="replyComment.id" class="text-center relative">
             回复
             <span class="text-[#f40]">{{ replyComment?.commentUserIdDictMap?.name }}:</span>
@@ -369,7 +445,7 @@
             </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 @click="closeEmojiBox" class="flex justify-end pr-15 text-[#999] text-sm">
               收起表情
             </div>
             <div class="flex items-center flex-wrap">
@@ -383,6 +459,45 @@
             </div>
           </div>
         </div>
+
+        <div
+          v-else
+          style="box-shadow: 0px -4px 4px 0px rgba(0, 0, 0, 0.1)"
+          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]"
+          >
+            留下足迹吧~
+          </div>
+
+          <div class="flex justify-end shrink-0 items-center text-base text-black-3">
+            <div @click="doStar" class="flex items-center mr-10">
+              <van-icon
+                v-if="detailData.isLike"
+                size="24"
+                color="#FF476A"
+                name="good-job"
+                class="mr-5"
+              />
+              <van-icon v-else name="good-job-o" color="#333333" size="24" class="mr-5" />
+              {{ detailData.likeCount }}
+            </div>
+            <div class="flex items-center mr-10" @click="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" />
+              收藏
+            </div>
+            <div class="flex items-center" @click="">
+              <span
+                class="iconfont icon-chat-message text-black-3 mr-5"
+                style="font-size: 24px"
+              ></span>
+              {{ '评论' }}
+            </div>
+          </div>
+        </div>
       </div>
     </div>
 
@@ -396,7 +511,6 @@
 </template>
 
 <script setup>
-import dashBorder2 from '~/assets/img/dash-border.png'
 import emojiJson from './emoji.json'
 const userInfoStore = useUserInfoStore()
 const authStore = useAuthStore()
@@ -405,6 +519,7 @@ const { token } = storeToRefs(authStore)
 const id = useRouteParam('id')
 const router = useRouter()
 const textareaRef = ref(null)
+const showInput = ref(false)
 
 // 游记内容
 const { data: detailData, status } = useMyFetch(
@@ -477,6 +592,50 @@ async function doStar() {
     .finally(() => (canDoStar.value = true))
 }
 
+// 评论的 收藏/取消收藏
+const showStar = ref(false)
+async function handleCommentCollect() {
+  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 ? '收藏成功' : '已取消收藏')
+}
+
+// 评论点赞
+const showLike = ref(false)
+const isCommentStarLoading = ref(true)
+async function doCommenteStar() {
+  if (!isCommentStarLoading.value) return
+  isCommentStarLoading.value = false
+  request(`/website/tourism/projectTravelNotes/userLikeTravelNotesUpdate`, {
+    method: 'post',
+    body: { travelNotesId: id.value }
+  })
+    .then(() => {
+      detailData.value.isLike = true
+      getStars()
+    })
+    .finally(() => (isCommentStarLoading.value = true))
+}
+
 // 获取用户信息
 const userInfo = ref({})
 async function getUserInfo() {
@@ -678,9 +837,21 @@ function share() {
   )
 }
 
+// 群聊弹窗
+const showGroupDialog = ref(false)
+
+// 显示输入框并获取焦点
+function openShowInput() {
+  showInput.value = true
+  nextTick(() => {
+    textareaRef.value && textareaRef.value.$el.focus()
+  })
+}
+
 function goBack() {
   router.back()
 }
+
 useSeoMeta({
   title: '游记详情',
   ogTitle: detailData?.projectTitle || '精美热门游记',
@@ -689,6 +860,10 @@ useSeoMeta({
   ogImage: ''
 })
 onMounted(() => {
+  useEventListener('click', openShowInput, { target: document })
+  useEventListener('mousedown', openShowInput, { target: document })
+  useEventListener('touchstart', openShowInput, { target: document })
+
   getComments()
   getUserInfo()
   isCollectTravelNotes()

+ 87 - 54
src/utils/index.js

@@ -1,84 +1,117 @@
-import accounting from "accounting";
+import accounting from 'accounting'
 
-const setIntervalImmediately = (fn, duration) =>
-  setInterval((() => (fn(), fn))(), duration);
+const setIntervalImmediately = (fn, duration) => setInterval((() => (fn(), fn))(), duration)
 
 function formatImgSrc(srcArr) {
   if (Array.isArray(srcArr) && srcArr.length > 0) {
-    return srcArr[0] ?? "";
+    return srcArr[0] ?? ''
   }
-  return "";
+  return ''
 }
 
 const isEmptyValue = (value) => {
-    if (value == null) return true
+  if (value == null) return true
 
-    if (Array.isArray(value) && value.length === 0) return true
+  if (Array.isArray(value) && value.length === 0) return true
 
-    if (typeof value === 'object' && Object.keys(value).length === 0) return true
+  if (typeof value === 'object' && Object.keys(value).length === 0) return true
 
-    return false
+  return false
 }
 
 const formatNumber = (n) => {
-    let num = Number(n ?? '')
+  let num = Number(n ?? '')
 
-    // 如果数字大于或等于10,000万,则显示为9999w+
-    if (num >= 10000 * 10000) {
-        return '9999万+'
-    }
-    // 如果数字大于或等于1万,则转换为以“万”为单位,不四舍五入,最多保留两位小数
-    else if (num >= 10000) {
-        let w = Math.floor(num / 10000) // 取整,避免四舍五入
-        let remainder = num % 10000
-
-        // 计算小数部分并限制为最多两位
-        let decimalPart = ''
-        if (remainder > 0) {
-            // 将余数转换为两位小数,但不进行四舍五入
-            decimalPart = ('.' + Math.floor(remainder / 100)).slice(0, 2)
-            // 移除结尾的0,如果有的话
-            decimalPart = decimalPart.replace(/\.?0+$/, '')
-        }
-
-        return `${w}${decimalPart}万`
+  // 如果数字大于或等于10,000万,则显示为9999w+
+  if (num >= 10000 * 10000) {
+    return '9999万+'
+  }
+  // 如果数字大于或等于1万,则转换为以“万”为单位,不四舍五入,最多保留两位小数
+  else if (num >= 10000) {
+    let w = Math.floor(num / 10000) // 取整,避免四舍五入
+    let remainder = num % 10000
+
+    // 计算小数部分并限制为最多两位
+    let decimalPart = ''
+    if (remainder > 0) {
+      // 将余数转换为两位小数,但不进行四舍五入
+      decimalPart = ('.' + Math.floor(remainder / 100)).slice(0, 2)
+      // 移除结尾的0,如果有的话
+      decimalPart = decimalPart.replace(/\.?0+$/, '')
     }
 
-    // 对于小于1万的数字直接输出
-    else {
-        return accounting.formatNumber(num)
-    }
+    return `${w}${decimalPart}万`
+  }
+
+  // 对于小于1万的数字直接输出
+  else {
+    return accounting.formatNumber(num)
+  }
 }
 
+// 字母
+const formatNumberLetter = (n) => {
+  let num = Number(n ?? '')
 
-const isValidJson = (jsonString) => {
-    try {
-        JSON.parse(jsonString);
-        return true;
-    } catch (e) {
-        return false;
+  // 如果数字大于或等于10,000万,则显示为9999w+
+  if (num >= 10000 * 10000) {
+    return '9999w+'
+  }
+  // 如果数字大于或等于1万,则转换为以“万”为单位,不四舍五入,最多保留两位小数
+  else if (num >= 10000) {
+    let w = Math.floor(num / 10000) // 取整,避免四舍五入
+    let remainder = num % 10000
+
+    // 计算小数部分并限制为最多两位
+    let decimalPart = ''
+    if (remainder > 0) {
+      // 将余数转换为两位小数,但不进行四舍五入
+      decimalPart = ('.' + Math.floor(remainder / 100)).slice(0, 2)
+      // 移除结尾的0,如果有的话
+      decimalPart = decimalPart.replace(/\.?0+$/, '')
     }
+
+    return `${w}${decimalPart}w`
+  }
+
+  // 对于小于1万的数字直接输出
+  else {
+    return accounting.formatNumber(num)
+  }
 }
 
+const isValidJson = (jsonString) => {
+  try {
+    JSON.parse(jsonString)
+    return true
+  } catch (e) {
+    return false
+  }
+}
 
 const uuid = function () {
-    function S4() {
-        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
-    }
-    return (S4() + S4() + "" + S4() + "" + S4() + "" + S4() + "" + S4() + S4() + S4());
-};
+  function S4() {
+    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
+  }
+  return S4() + S4() + '' + S4() + '' + S4() + '' + S4() + '' + S4() + S4() + S4()
+}
 
 function priceToArray(price) {
-  if (!price) return ["0", "00"];
-  const priceStr = price.toString();
-  if (!priceStr.includes(".")) {
-    return [priceStr, "00"];
+  if (!price) return ['0', '00']
+  const priceStr = price.toString()
+  if (!priceStr.includes('.')) {
+    return [priceStr, '00']
   }
-  return [priceStr.split(".")[0], priceStr.split(".")[1]];
+  return [priceStr.split('.')[0], priceStr.split('.')[1]]
 }
 
-
-export { setIntervalImmediately, formatImgSrc, formatNumber, isEmptyValue, isValidJson, uuid ,priceToArray};
-
-
-
+export {
+  setIntervalImmediately,
+  formatImgSrc,
+  formatNumber,
+  isEmptyValue,
+  isValidJson,
+  uuid,
+  priceToArray,
+  formatNumberLetter
+}