Pārlūkot izejas kodu

feat:游记详情界面点赞收藏评论热门游记等

qiao 2 mēneši atpakaļ
vecāks
revīzija
42dc2b8101

+ 3 - 3
.env.development

@@ -1,7 +1,7 @@
 VITE_APP_ENV=development
 
-# VITE_APP_BASE_URL=http://192.168.101.101/api/
-VITE_APP_BASE_URL=http://1.94.207.143:8082/
+VITE_APP_BASE_URL=https://www.xiaoyaotravel.com/api/
+# VITE_APP_BASE_URL=http://1.94.207.143:8082/
 # VITE_APP_BASE_URL=http://192.168.1.204:8082
-
+VITE_APP_EMOJI_API=https://v.xiaoyaotravel.com/emoji/
 VITE_APP_IM_USER_SUFFIX=dev

+ 1 - 1
.env.production

@@ -2,6 +2,6 @@ VITE_APP_ENV=production
 
 # VITE_APP_BASE_URL=https://api.ztzhipin.com/api/
 VITE_APP_BASE_URL=https://m.xiaoyaotravel.com/api/
-
+VITE_APP_EMOJI_API=https://v.xiaoyaotravel.com/emoji/
 VITE_APP_IM_USER_SUFFIX=''
 

+ 1 - 1
.gitignore

@@ -20,5 +20,5 @@ logs
 
 # Local env files
 # .env
-.env.*
+# .env.*
 # !.env.example

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


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


BIN
src/assets/img/yj/no-comments.png


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


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


+ 22 - 51
src/pages/travel-notes/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="">
     <!-- <Navbar :title="`${menuName ?? ''}精品旅游`" /> -->
-    <div class="sticky top-0 z-50 bg-white w-full">
+    <div v-if="filterList.length" class="sticky top-0 z-50 bg-white w-full">
       <van-dropdown-menu active-color="#FF9300" ref="dropDownMenuRef">
         <van-dropdown-item @closed="onAreaFilterClose" :title="areaFilterTitle" ref="itemRef">
           <van-tree-select @click-nav="handleAreaClick" @click-item="handleFilterClick" v-model:active-id="activeId"
@@ -15,8 +15,7 @@
       <van-list v-if="dataList.length" v-model:loading="loading" :immediate-check="false" :finished="finished"
         finished-text="" @load="loadMore">
         <template v-for="itemData in dataList">
-          <NuxtLink class="group flex relative cursor-pointer bg-white  pb-10 mb-20" :to="`/yj/${itemData.id}`"
-            target="_blank">
+          <NuxtLink class="group flex relative cursor-pointer bg-white  pb-10 mb-20" :to="`/yj/${itemData.id}`">
             <div class="aspect-[120/80] h-80 shrink-0 rounded overflow-hidden bg-[#ddd]">
               <img :src="itemData.tourismUrl" class="w-full h-full rounded object-cover" alt="" srcset="" />
             </div>
@@ -32,14 +31,14 @@
               </div>
               <div class="flex justify-end items-center text-[12px] text-[#999]">
                 <div class="flex items-center mr-10">
-                  <van-icon name="eye-o" class="mr-5" />{{ itemData.pageViewCount }}
+                  <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" />{{ itemData.likeCount }}
+                  <van-icon name="good-job-o" class="mr-5" />{{ transferCount(itemData.likeCount) }}
                 </div>
-                <div class="flex items-center mr-10">
+                <!-- <div class="flex items-center mr-10">
                   <van-icon name="like-o" class="mr-5" />{{ itemData.hotValue }}
-                </div>
+                </div> -->
               </div>
             </div>
             <div class="w-full h-10 absolute top-[100%] left-0 flex items-center justify-between">
@@ -71,7 +70,7 @@ const activeIndex = ref('');
 const dropDownMenuRef = ref(null)
 // 获取筛选列表
 async function getFilters() {
-  const { data } = await request(`website/tourism/projectTravelNotes/travelNotesDirectoryList`)
+  const { data } = await request(`website/tourism/projectTravelNotes/travelNotesDirectoryList`).finally(()=>{closeToast()})
   if (!Array.isArray(data)) return
   data.map((item, index) => {
     data[index].id = item.areaId
@@ -110,6 +109,11 @@ watch(travelWriteType, () => {
 
 // 获取游记列表
 async function getList() {
+  showLoadingToast({
+    message: '加载中...',
+    forbidClick: true,
+    duration: 0,
+  })
   const param = {
     pageNum: pageNum.value,
     pageSize: pageSize.value,
@@ -123,7 +127,7 @@ async function getList() {
     param.travelWriteType = travelWriteType.value
   }
   loading.value = true
-  const { data } = await request(`website/tourism/projectTravelNotes/travelNotesPageList`, { query: param })
+  const { data } = await request(`website/tourism/projectTravelNotes/travelNotesPageList`, { query: param }).finally(()=>closeToast())
 
   dataList.value = dataList.value.concat(data.dataList)
   loading.value = false
@@ -154,41 +158,14 @@ function handleFilterClick(item) {
   document.title=`${item.id?'游记-'+item.text:'旅游笔记'}`
 }
 
-
-const menuName = useRouteQuery("menuName");
-
-const areaId = useRouteQuery("areaId");
-
-const countryId = useRouteQuery("countryId");
-
-// const id = useRouteParam("id");
-
-const searchStr = ref("");
-
-
-async function getProjects() {
-  const { data } = await request(
-    `website/tourism/projectTravelNotes/travelNotesPageList`,
-    {
-      query: {
-        pageNum: pageNum.value,
-        pageSize: pageSize.value,
-        // belongTab: id.value,
-        areaId: areaId.value,
-        countryId: countryId.value,
-        searchString: searchStr.value,
-      },
-    }
-  );
-  dataList.value = dataList.value.concat(data.dataList);
-  loading.value = false;
-  if (dataList.value.length >= data.totalCount) {
-    finished.value = true;
-  } else {
-    finished.value = false;
+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()
@@ -197,21 +174,15 @@ function convertTag(str=''){
   if(typeof str!=='string') return []
   return str.split('&')
 }
-function onSearch() {
-  pageNum.value = 1;
-  dataList.value = [];
-  // finished.value = false;
-  getProjects();
-}
 
 onMounted(() => {
-  getFilters()
   getList()
-});
+  getFilters()
+})
 
 useSeoMeta({
   title: `旅游笔记`,
-});
+})
 </script>
 
 <style lang="scss" scoped>

+ 366 - 26
src/pages/yj/[id].vue

@@ -1,7 +1,6 @@
 <template>
   <div>
-    <div v-if="['success'].includes(status)">
-
+    <div v-if="['success'].includes(status)" @click="pageClick">
       <div class="p-10">
         <img :src="detailData.tourismUrl" class="aspect-[316/204] w-full object-cover rounded" />
         <div class="flex mt-10">
@@ -90,34 +89,170 @@
             </van-row>
           </div>
         </div>
-        <div class="mt-10" v-for="con in detailData.travelNotesContent" :key="con.id">
+        <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="">
+            <img :src="con.content" class="w-full rounded-xl" alt="">
           </template>
           <template v-else>
             <div v-html="con.content"></div>
           </template>
         </div>
 
-        <div class="flex justify-end items-center text-[12px] text-[#999] mt-10 ">
+        <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 class="flex items-center mr-10">
-            <van-icon name="chat-o" class="mr-5" />{{ detailData.pageViewCount }}
+          <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">
-            <van-icon name="good-job-o" class="mr-5" />{{ detailData.likeCount }}
+          <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">
-            <van-icon name="like-o" class="mr-5" />{{ detailData.hotValue }}
+          <div class="flex items-center ml-15">
+            <img src="~/assets/img/yj/leftArrow.png" class="w-[28px]" alt="">
+            <div class="text-[#666666] text-[16px]">扫码添加好友</div>
+          </div>
+        </div>
+        <template v-if="commentList.length">
+          <template v-for="item in commentList" :key="item.id">
+            <div class="flex mt-10 justify-between">
+              <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-10">
+          <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="mt-5 divide-y">
+            <NuxtLink :to="`/yj/${item.id}`" class="group flex cursor-pointer items-center space-x-10 py-15" v-for="(item, index) in dataList.dataList
+" :key="item.id">
+              <img :src="item?.homeHotPicturesAfterConvert?.length ? item.homeHotPicturesAfterConvert[0] : ''"
+                class="h-80 w-80 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-20 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">
+              评论
+            </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-else class=" text-[#999] mt-100">
       <div class="text-center">
-        不好意思,游记走丢了~
+        不好意思,找不到这篇游记了~
       </div>
       <div @click="goBack" class="text-center underline mt-10 cursor-pointer text-[#4B99EA]">
         返回
@@ -128,29 +263,234 @@
 
 <script setup>
 import dashBorder2 from '~/assets/img/dash-border.png'
-const id = useRouteParam("id");
-const router = useRouter();
+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}`
-);
-console.log('detailData::', detailData)
-const htmlContent = computed(() => {
-  return detailData?.value?.tourTourismTravelNotesContent?.content?.replace(
-    /<img/g,
-    "<img style='width:100%; height:auto;'"
-  );
-});
+)
+
+// 热门游记列表
+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() {
+  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() {
+  showLoadingToast({
+    forbidClick: true,
+    duration: 0,
+  })
+  const { data } = await request("/website/comment/tourTravelNotesComment/list?travelNoteId=" + id.value).finally(()=>closeToast())
+  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
+  }).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: '游记链接分享失败' });
+  })
+}
 
-const lableList = computed(() => {
-  return detailData.value?.projectLabel?.split("&") ?? [];
-});
 function goBack() {
   router.back()
 }
 useSeoMeta({
   title: "游记详情",
 });
+onMounted(() => {
+  getComments()
+  getUserInfo()
+  isCollectTravelNotes()
+})
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped></style>

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

@@ -0,0 +1,23 @@
+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:'🚗'},
+]

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

@@ -0,0 +1,36 @@
+{
+  "[爱你]": "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"
+}