vue3聊天

40 阅读5分钟

highlight: an-old-hope

全局导入
import SocketIO from './plugins/io' // 导入插件
app.use(SocketIO, { // 应用
    connection: 'http://43.138.15.137:3000', // socket服务地址
})
parsion.json:
拼音依赖插件
   { "pinyin": "^4.0.0-alpha.0",
    "segmentit": "^2.0.3",}
    
    "postcss-pxtorem": "^6.1.0",
    聊天依赖
    "socket.io": "^4.7.5",
    "socket.io-client": "^4.7.5",
    需要安装
<template>
    <div>
        <div class="contact-wrap">
            <div class="my-header">
                <div class="back-wrap"><span class="iconfont"></span>
                </div>
                <div><span>
                        选择联系人
                    </span></div><!---->
            </div>
            <div class="listview">
                <ul
                    style="transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1); transition-duration: 0ms; transform: translate(0px, 0px) scale(1) translateZ(0px);">
                    <li class="list-group">
                        <div class="search-bar-wrap">
                            <div class="input-wrap"><span class="iconfont icon-sousuo"
                                    style="color: white;"></span><input placeholder="搜索用户昵称" class="input"><span
                                    class="iconfont icon-close" style="display: none;"></span></div>
                        </div>
                        <ul></ul>
                    </li>
                    <li class="list-group" v-for="(item, index) in list" :key="index">

                        <h2 class="list-group-title">{{ index }} </h2>

                        <ul>
                            <li class="list-group-item" v-for="item1, index1 in item" :key="index1">


                                <img :src="'http://43.138.15.137:3000' + item1.userAvatar" width="50" height="50"
                                    class="avatar">
                                <div class="main"><span class="name">{{ item1.userNickname }}</span><span
                                        class="desc">{{
                                            item1.userDesc }}</span></div><span class="iconfont icon-xiazai16"
                                    @click="liao(item)"></span>
                            </li>
                        </ul>
                    </li>
                </ul>
                <div class="list-shortcut">
                    <ul>
                        <li data-index="0" class="item current">
                            <p class="iconfont icon-sousuo" style="color: white;"></p>
                        </li>
                        <li data-index="1" class="item">
                            <p>F</p>
                        </li>
                        <li data-index="2" class="item">
                            <p>O</p>
                        </li>
                        <li data-index="3" class="item">
                            <p>Y</p>
                        </li>
                        <li data-index="4" class="item">
                            <p>#</p>
                        </li>
                    </ul>
                </div>
                <div class="list-fixed" style="display: none;">
                    <div class="fixed-title"> </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script setup>
import { ref } from 'vue'
import { userconatlAPI } from '../api/suoyinlan'
import { getFirstPinyinInitial } from '../uilts/index.js'
import { useRouter } from 'vue-router';

let router = useRouter()
let list = ref([])
let duixsj = ref({})
let getUserList = async () => {
    let dxq = await userconatlAPI(localStorage.getItem('userId'))

    dxq.data.data.forEach((item) => {
        item.initial = getFirstPinyinInitial(item.userNickname);
    });
    dxq.data.data.sort((a, b) => {
        const initialA = a.initial.toUpperCase();
        const initialB = b.initial.toUpperCase();
        if (initialA < initialB) return -1;
        if (initialA > initialB) return 1;
        return 0;
    });
    const groupedData = {};

    dxq.data.data.forEach((item) => {
        const { initial } = item;
        if (!groupedData[initial]) {
            groupedData[initial] = [];
        }
        groupedData[initial].push(item);
    });
    list.value = groupedData;
    console.log(list.value);
}
getUserList()

function liao(item) {
    duixsj.value = item
   let zhong =JSON.stringify( duixsj.value)
    router.push({ path: '/ChatWith', query:{ shuju:zhong } })
}
</script>
<style scoped>
.contact-wrap {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
}

.my-header {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    position: relative;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    color: #e8e8e9;
    height: 44px;
    line-height: 44px;
    font-size: 16px;
    z-index: 33;
    border-bottom: 0.5px solid rgba(41, 40, 37, 0.8);
    background: #161622;
}

.my-header .back-wrap {
    position: absolute;
    left: 10px;
}

.my-header .back-wrap {
    padding: 10px;
}

.listview {
    position: absolute;
    width: 100%;
    margin-top: 44px;
    top: 0;
    bottom: 0;
    overflow: hidden;
    background: #161622;
}

.listview .list-shortcut {
    position: absolute;
    right: 5px;
    top: 50%;
    -webkit-transform: translateY(-50%);
    transform: translateY(-50%);
    width: 20px;
    padding: 20px 0;
    border-radius: 10px;
    text-align: center;
    background: rgba(22, 24, 35, 0.98);
    font-family: Helvetica;
}

.listview .list-shortcut .item {
    padding: 3px;
    line-height: 1;
    color: #e8e8e9;
    font-size: 12px;
}

.listview .list-group {
    padding-bottom: 10px;
}

.listview .search-bar-wrap {
    padding: 10px 20px;
}

.input-wrap {
    width: 100%;
    background: #242630;
    border-radius: 5px;
    height: 44px;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
}

.input-wrap .iconfont {
    margin-left: 10px;
    font-size: 21px;
}

.input-wrap .input {
    height: 21px;
    background: #1a1b20;
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
    font-size: 14px;
    color: #e8e8e9;
    padding-left: 10px;
    background: none;
    caret-color: #face15;
    /* border: none;
    outline: none; */
}

.input-wrap .icon-close {
    font-size: 12px;
    margin-right: 10px;
    padding: 5px;
}

.input-wrap .iconfont {
    margin-left: 10px;
    font-size: 21px;
}

.listview .list-group .list-group-title {
    height: 30px;
    line-height: 30px;
    padding-left: 20px;
    font-size: 14px;
    color: #e8e8e9;
    background: #161622;
}

.listview .list-group .list-group-item {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    padding: 20px 0 0 20px;
}

.listview .list-group .list-group-item .avatar {
    border-radius: 50%;
}

.listview .list-group .list-group-item .main {
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    -ms-flex-direction: column;
    flex-direction: column;
    margin-left: 10px;
}

.listview .list-group .list-group-item .main .name {
    color: #e8e8e9;
    font-size: 14px;
}

.listview .list-group .list-group-item .main .desc {
    margin-top: 10px;
    color: #8b8c96;
    font-size: 12px;
}

.listview .list-group .list-group-item .icon-xiazai16 {
    margin-right: 30px;
    padding: 10px;
    font-size: 18px;
    color: white;
}
</style>

<template>
    <div>
        <div class="topBar"><span class="backbtn iconfont icon-left"></span>
            <p class="title">10086</p>
        </div>
        <div class="scroll-wrap scroll-wrap-bottom" style="color: white;overflow-y: scroll;" ref="hua">
            <ul>
                <div class="chat-item"><span class="time">13:10</span></div>
                <div class="chat-item" v-for="item, index in lefts" :key="index">
                    <div class="right" v-if="item.fromId == userId">
                        <div class="content">
                            {{ item.privateLetterContent }}
                        </div><img
                            src="http://43.138.15.137:3000/assets/avatar/2edc518d-06fd-47b1-bc8a-0b965833ff67.png"
                            alt="" width="40" height="40" class="avatar">
                    </div>
                    <div class="left" v-else><img
                            src="http://43.138.15.137:3000/assets/avatar/4f5305b1-c16c-4adc-985d-440e4dbf82e5.png"
                            alt="" width="40" height="40" class="avatar">
                        <div class="content">
                            {{ item.privateLetterContent }}
                        </div>
                    </div>
                </div>
            </ul>
        </div>
        <div class="input-bar"><input placeholder="  发送消息..." type="text" class="input" v-model="onbj.msg"
                @keyup.enter="fasong"><span class="iconfont icon-at"></span><span class="iconfont icon-check"></span>
        </div>
    </div>
</template>
<script setup>
import { useRoute } from 'vue-router';
let route = useRoute()
import { ref, reactive, computed, toRef, onMounted, onUpdated } from 'vue'
import { postprivateletterAPI, getliaotianAPI } from '../api/liaot'
import { inject } from "vue";

const socket = inject("socket"); // 注入到全局
let jies = ref([])
jies.value = JSON.parse(route.query.shuju)
console.log(jies.value[0].userId);
let onbj = reactive({
    msg: ''
})
let userId = ref(localStorage.getItem('userId'))
let lefts = ref([])
let rights = ref([])
let hua = ref(null)
async function fasong() {
    let message = await postprivateletterAPI(localStorage.getItem('userId'), jies.value[0].userId, {
        content: onbj.msg,
        fromUserId: localStorage.getItem('userId'),
        toUserId: jies.value[0].userId
    })
    let obj = {
        fromId: localStorage.getItem('userId'),
        toId: jies.value[0].userId,
        privateLetterContent: onbj.msg,
        createdAt: +new Date(),
        userAvatar: localStorage.getItem('userAvatar'),
        userNickname: localStorage.getItem('userNickname'),
    }
    socket.emit('sendPrivateLetter', obj) // sendPrivateLetter客户端和服务端协商好的事件
    console.log(message);
    lefts.value.push(message.data.data)
    liaotxinxi()
}
socket.on('receivePrivateLetter', data => { // receivePrivateLetter客户端和服务端协商好的事件
    lefts.value.push(data)
})
async function liaotxinxi() {
    let gwet = await getliaotianAPI(localStorage.getItem('userId'), jies.value[0].userId)
    lefts.value = gwet.data.data
    console.log(gwet);
}
liaotxinxi()
// function getbaos() {
//     if (hua.value) {
//         hua.value.scrollTop = hua.value.scrollTop - hua.value.clientHeight
//         console.log( hua.value.scrollTop = hua.value.scrollTop - document.clientHeight);
//     }
// }
// onUpdated(() => {
//     getbaos()
// })

</script>
<style scoped>
.topBar {
    position: fixed;
    z-index: 33;
    width: 100%;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    height: 44px;
    border-bottom: 1px solid rgba(41, 40, 37, 0.8);
    background: #161622;

}

.topBar .backbtn {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    height: 44px;
    width: 44px;
}

.topBar .title {
    margin-right: 44px;
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
    line-height: 44px;
    text-align: center;
    font-size: 16px;
}

.scroll-wrap-bottom {
    margin-bottom: 44px;
}

.scroll-wrap {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    margin-top: 44px;
}

.chat-item {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    width: 100%;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
}

.chat-item .time {
    margin-top: 10px;
    font-size: 10px;
}

.chat-item .right {
    -webkit-box-pack: end;
    -ms-flex-pack: end;
    justify-content: flex-end;
    margin-left: 100px;
}

.chat-item>div {
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    margin: 20px 0;
}

.chat-item .content {
    position: relative;
    border-radius: 5px;
    background: #fff;
    color: #000;
    padding: 10px;
    word-break: break-all;
}

.chat-item .right .content:after {
    display: block;
    position: absolute;
    top: 8px;
    right: -16px;
    border: 8px solid transparent;
    border-left: 8px solid #fff;
    content: '';
}

.chat-item .avatar {
    -ms-flex-negative: 0;
    flex-shrink: 0;
    border-radius: 50%;
    margin: 0 15px;
}

.chat-item .left {
    margin-right: 100px;
}

.chat-item>div {
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    margin: 20px 0;
}

.chat-item .avatar {
    -ms-flex-negative: 0;
    flex-shrink: 0;
    border-radius: 50%;
    margin: 0 15px;
}

.chat-item .content {
    position: relative;
    border-radius: 5px;
    background: #fff;
    color: #000;
    padding: 10px;
    word-break: break-all;
}

.chat-item .left .content:after {
    display: block;
    position: absolute;
    top: 8px;
    left: -16px;
    border: 8px solid transparent;
    border-right: 8px solid #fff;
    content: '';
}

.input-bar {
    position: fixed;
    width: 100%;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    height: 44px;
    position: absolute;
    bottom: 0;
    background: #161622;
    border-top: 1px solid rgba(41, 40, 37, 0.8);
}

.input-bar .input {
    background: #161622;
    -webkit-box-flex: 1;
    -ms-flex: 1;
    flex: 1;
    font-size: 14px;
    color: #e8e8e9;
    padding-left: 10px;
    caret-color: #face15;
}

.input-bar .iconfont {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    width: 44px;
}
</style>