panhui 3 ani în urmă
părinte
comite
a13c213d5e

+ 18 - 0
src/api/index.ts

@@ -185,4 +185,22 @@ export function fetchGetMoments<T>(data: any) {
         url: `/moments`,
         data: data
     })
+}
+
+export function fetchGetMomentsDetail<T>(id: any) {
+    return get<T>({
+        url: `/moments/get/${id}`
+    })
+}
+
+export function fetchChatRoles<T>(num: number) {
+    return get<T>({
+        url: '/chatRole/getRandom/' + num
+    })
+}
+
+export function fetchGetChatRole<T>(id: any) {
+    return get<T>({
+        url: '/chatRole/get/' + id
+    })
 }

+ 80 - 11
src/components/common/AgentPannel.vue

@@ -1,23 +1,92 @@
 <template>
     <div>
-        <div class="px-5">
-            <div class="bg-white mt-[12px] p-4 rounded-lg">
-                <moment-info></moment-info>
-            </div>
+        <div class="w-full">
+            <n-image class="!block" :src="chatRole['pic']" object-fit="scale-down" />
         </div>
+        <div class="translate-y-[-16px]">
+            <div class="rounded-t-2xl overflow-hidden chat-info">
+                <div class="px-5 py-4">
+                    <n-thing>
+                        <template #avatar>
+                            <n-avatar v-if="chatRole.pic" class="!block" round :size="60" :src="chatRole.pic">
+                            </n-avatar>
+                            <n-avatar v-else class="!block" round :size="60"> {{ chatRole.name?.[0] }} </n-avatar>
+                        </template>
+                        <template #header>
+                            <div class="text-lg font-semibold">{{ chatRole.name }}</div>
+                        </template>
+                        <template #header-extra> </template>
+                        <template #description>
+                            <div class="text-xs text-[#797A8A]">1.5w+人聊过|20个动态</div>
+                        </template>
+                        <template #footer>
+                            <div>
+                                {{ chatRole.describe }}
+                            </div>
+                        </template>
+                        <template #action>
+                            <n-space size="small" class="mt-2">
+                                <n-tag
+                                    :bordered="false"
+                                    :color="{
+                                        color: '#D0FEFF',
+                                        textColor: '#000'
+                                    }"
+                                    round
+                                    size="small"
+                                    v-for="(item, index) in labels"
+                                    :key="index"
+                                >
+                                    {{ item }}
+                                </n-tag>
+                            </n-space>
+                        </template>
+                    </n-thing>
+                </div>
+            </div>
 
-        <div class="title px-5 py-4 flex items-center relative">
-            <span class="text-base font-medium">全部评论</span>
-            <span class="text-xs text-[#797A8A] ml-[2px]">(1356)</span>
-            <img class="absolute bottom-2 left-[38px] w-[28px]" src="@/assets/png-xian.png" alt="" />
-        </div>
+            <div class="title px-5 py-4 flex items-center relative">
+                <span class="text-base font-medium">全部动态</span>
+                <span class="text-xs text-[#797A8A] ml-[2px]">(1356)</span>
+                <img class="absolute bottom-2 left-[38px] w-[28px]" src="@/assets/png-xian.png" alt="" />
+            </div>
 
-        <div>
-            <comment-info v-for="i in 10" :key="i"></comment-info>
+            <div>
+                <comment-info v-for="i in 10" :key="i"></comment-info>
+            </div>
         </div>
     </div>
 </template>
 
 <script setup lang="ts">
 import { MomentInfo, CommentInfo } from '@/components/common'
+import { NImage, NThing, NAvatar, NTag, NSpace } from 'naive-ui'
+import { fetchGetChatRole } from '@/api'
+import { useRoute } from 'vue-router'
+import { ref, computed } from 'vue'
+
+const route = useRoute()
+const chatRole = ref({
+    pic: '',
+    name: '',
+    labels: [],
+    describe: ''
+})
+fetchGetChatRole(route.query.id).then((res: any) => {
+    chatRole.value = res
+})
+
+const labels = computed(() => {
+    return chatRole.value?.labels
+})
 </script>
+
+<style lang="less" scoped>
+.chat-info {
+    background: linear-gradient(180deg, #d0feff 0%, #ffffff 80px, #ffffff 100%);
+}
+
+.title{
+    background: linear-gradient(180deg, #FFFFFF 0%, #F5F6F8 100%);
+}
+</style>

+ 3 - 2
src/components/common/MomentInfo.vue

@@ -1,7 +1,8 @@
 <template>
     <n-thing @click="goDetail">
         <template #avatar>
-            <n-avatar class="!block" round :size="88"> 醉 </n-avatar>
+            <n-avatar v-if="chatRole.pic" class="!block" round :size="88" :src="chatRole.pic"> </n-avatar>
+            <n-avatar v-else class="!block" round :size="88"> {{ chatRole.name?.[0] }} </n-avatar>
         </template>
         <template #header>
             <div class="text-lg font-semibold">{{ chatRole.name }}</div>
@@ -78,7 +79,7 @@ const timeStr = computed(() => {
         if (isToday(new Date(props.info.createdAt))) {
             return formatDistanceToNow(new Date(props.info.createdAt), { addSuffix: true, locale: zhCN })
         } else {
-            return format(new Date(props.info.createdAt), 'yyyy-MM-dd HH:mm')
+            return format(new Date(props.info.createdAt), 'MM-dd HH:mm')
         }
     }
     return ''

+ 56 - 3
src/components/common/MomentsDetailPannel.vue

@@ -2,7 +2,7 @@
     <div>
         <div class="px-5">
             <div class="bg-white mt-[12px] p-4 rounded-lg">
-                <moment-info></moment-info>
+                <moment-info :info="moments"></moment-info>
             </div>
         </div>
 
@@ -12,12 +12,65 @@
             <img class="absolute bottom-2 left-[38px] w-[28px]" src="@/assets/png-xian.png" alt="" />
         </div>
 
-        <div>
-            <comment-info v-for="i in 10" :key="i"></comment-info>
+        <div class="overflow-hidden pb-11">
+            <div
+                v-for="(i, index) in 10"
+                class="comment-box"
+                :class="{ 'comment-center': showList[index] }"
+                :key="i"
+                ref="commentRef"
+            >
+                <comment-info></comment-info>
+            </div>
         </div>
     </div>
 </template>
 
 <script setup lang="ts">
 import { MomentInfo, CommentInfo } from '@/components/common'
+import { ref, watch, nextTick } from 'vue'
+import { useWindowScroll, useElementBounding, useWindowSize, useScroll } from '@vueuse/core'
+import { fetchGetMomentsDetail } from '@/api'
+import { useRoute } from 'vue-router'
+
+const commentRef = ref([])
+const showList = ref([] as any)
+const { y } = useWindowScroll()
+const { width, height } = useWindowSize()
+watch(
+    y,
+    () => {
+        nextTick(() => {
+            showList.value = commentRef.value.map((item: any) => {
+                return useElementBounding(item).top.value < height.value - 30
+            })
+        })
+    },
+    {
+        immediate: true
+    }
+)
+
+const route = useRoute()
+const moments = ref({})
+fetchGetMomentsDetail(route.query.id).then((res: any) => {
+    moments.value = res
+})
 </script>
+
+<style lang="less" scoped>
+.comment-box {
+    opacity: 0.2;
+    transition: all ease-in-out 0.3s;
+    &:nth-child(2n) {
+        transform: translate(-50px, 90px);
+    }
+    &:nth-child(2n + 1) {
+        transform: translate(50px, 90px);
+    }
+    &.comment-center {
+        transform: translate(0, 0);
+        opacity: 1;
+    }
+}
+</style>

+ 139 - 12
src/components/common/MomentsPannel.vue

@@ -1,16 +1,29 @@
 <template>
     <div>
-        <n-scrollbar x-scrollable>
-            <div class="flex">
-                <div class="flex flex-col items-center p-[10px]" v-for="i in 20" :key="i">
-                    <n-avatar round :size="60"> 醉 </n-avatar>
-                    <div class="text-xs leading-6 mt-1">醉汉</div>
+        <div class="overflow-x-auto roles-list" ref="rolesListRef">
+            <div class="flex px-[10px]">
+                <div
+                    class="roles flex flex-col items-center p-[10px] w-[80px] flex-shrink-0 overflow-hidden"
+                    :class="{ 'roles-show': showRoles[index] }"
+                    ref="rolesRef"
+                    v-for="(item, index) in chatRoles"
+                    :key="index"
+                    @click="goAgent(item.id)"
+                >
+                    <n-avatar round :size="60" :src="item.pic"></n-avatar>
+                    <div class="text-xs leading-6 mt-1 truncate w-full">{{ item.name }}</div>
                 </div>
             </div>
-        </n-scrollbar>
+        </div>
     </div>
-    <div class="px-5">
-        <div v-for="(item, index) in list" :key="index" class="bg-white mt-[12px] p-4 rounded-lg">
+    <div class="px-5 moment-list overflow-x-hidden pb-7">
+        <div
+            ref="target"
+            v-for="(item, index) in list"
+            :key="index"
+            class="moment-box bg-white mt-[12px] p-4 rounded-lg"
+            :class="{ 'moment-center': showList[index] }"
+        >
             <moment-info @goDetail="goDetail" :info="item"></moment-info>
         </div>
     </div>
@@ -20,8 +33,9 @@
 import { NScrollbar, NAvatar, NList, NListItem, NThing, NSpace, NTag, NIcon, NImage } from 'naive-ui'
 import { useRouter } from 'vue-router'
 import { MomentInfo } from '@/components/common'
-import { fetchGetMoments } from '@/api'
-import { ref } from 'vue'
+import { fetchGetMoments, fetchChatRoles } from '@/api'
+import { useWindowScroll, useElementVisibility, useElementBounding, useWindowSize, useScroll } from '@vueuse/core'
+import { ref, watch, computed, nextTick } from 'vue'
 
 const router = useRouter()
 function goDetail(id: any) {
@@ -34,7 +48,7 @@ function goDetail(id: any) {
 }
 getMoments()
 
-const list = ref([])
+const list = ref(<any>[])
 function getMoments() {
     fetchGetMoments({
         page: {
@@ -47,7 +61,120 @@ function getMoments() {
             }
         }
     }).then((res: any) => {
-        list.value = res.items
+        list.value = [...res.items, ...res.items, ...res.items]
+    })
+}
+
+getRoles()
+const chatRoles = ref(<any>[])
+function getRoles() {
+    fetchChatRoles(20).then((res: any) => {
+        chatRoles.value = [...res, ...res, ...res]
+    })
+}
+const target = ref([])
+const showList = ref(<any>[])
+const { y } = useWindowScroll()
+const { width, height } = useWindowSize()
+watch(y, () => {
+    showList.value = target.value.map((item: any) => {
+        return useElementBounding(item).top.value < height.value - 80
+    })
+})
+watch(list, () => {
+    nextTick(() => {
+        showList.value = target.value.map((item: any) => {
+            return useElementBounding(item).top.value < height.value - 80
+        })
+    })
+})
+
+const rolesListRef = ref(null)
+const { x: roleListX } = useScroll(rolesListRef)
+
+const rolesRef = ref([])
+const showRoles = ref(<any>[])
+watch(
+    chatRoles,
+    () => {
+        nextTick(() => {
+            showRoles.value = rolesRef.value.map(item => {
+                return useElementBounding(item).left.value < width.value
+            })
+        })
+    },
+    { immediate: true }
+)
+
+watch(
+    roleListX,
+    () => {
+        nextTick(() => {
+            showRoles.value = rolesRef.value.map(item => {
+                return useElementBounding(item).left.value < width.value
+            })
+        })
+    },
+    { immediate: true }
+)
+
+function goAgent(id: any) {
+    router.push({
+        name: 'agent',
+        query: {
+            id: id
+        }
     })
 }
 </script>
+
+<style lang="less" scoped>
+.moment-list {
+    :nth-child(n) {
+        &.moment-box {
+            transition: transform ease-in-out 0.3s;
+        }
+    }
+    :nth-child(2n + 1) {
+        &.moment-box {
+            transform: translateX(200%);
+        }
+    }
+
+    :nth-child(2n) {
+        &.moment-box {
+            transform: translateX(-200%);
+        }
+    }
+    .moment-box {
+        &.moment-center {
+            transform: translateX(0);
+        }
+    }
+
+    .targe {
+        position: absolute;
+        width: 100%;
+        height: 50px;
+        // background-color: red;
+        bottom: 0;
+        left: 0;
+        z-index: 0;
+    }
+}
+
+.roles {
+    transform: scale(0.1);
+    transition: transform ease-in-out 0.3s;
+
+    &.roles-show {
+        transform: scale(1);
+    }
+}
+
+.roles-list {
+    &::-webkit-scrollbar {
+        height: 5px;
+    }
+}
+</style>

+ 3 - 3
src/views/page/AgentView.vue

@@ -1,6 +1,6 @@
 <template>
-    <div class="page flex min-h-full flex-col bg-contain bg-no-repeat bg-[#F5F6F8]" :style="{ backgroundImage: `url(${bgImg})` }">
-        <n-page-header
+    <div class="page flex min-h-full flex-col bg-contain bg-no-repeat bg-[#F5F6F8]">
+        <!-- <n-page-header
             class="header"
             :class="[scrollY ? 'bg-white dark:bg-black' : '']"
             title="智能体"
@@ -9,7 +9,7 @@
             <template #extra>
                 <div class="w-[22px]"></div>
             </template>
-        </n-page-header>
+        </n-page-header> -->
         <agent-pannel></agent-pannel>
     </div>
 </template>