WorkerMan实现Web通讯(使用Vue实现前端页面逻辑)

2023-05-16

 需要使用到的扩展 https://github.com/tangbc/vue-virtual-scroll-list

最终效果

对话框

主页面 Main.vue

<template>
    <div>
        <!---chat message-->
        <el-dialog id="chat" top="8vh" :width="dialogWidth" center :show-close="false" :close-on-press-escape="false" :close-on-click-modal="false" :visible.sync="visible">
            <div slot="title" style="display: flex;align-items: center;position: relative;">
                <el-avatar :src="chat.img" :size="50" fit="fill" :alt="chat.title"/>
                <div>
                    <div v-html="chat.title" style="margin-left: 10px;text-align: left;color: #fff"/>
                    <div slot="title" v-html="chat.desc" style="margin-left: 10px;color: #fff"/>
                </div>
            </div>
            <el-row :gutter="24">
                <el-col :xl="{'span':18}" :lg="{'span':16}" :md="{'span':16}" :sm="{'span':16}" :xs="{'span':14}" id="leftBox">
                    <el-card>
                        <div class="main">
                            <div class="list-container">
                                <virtual-list v-show="!!chat.messageLists.length" class="stream scroll-touch" :class="{ overflow: overflow }" ref="vsl"
                                              :data-key="'uid'"
                                              :data-sources="chat.messageLists"
                                              :data-component="messageComponent"
                                              :estimate-size="70"
                                              :item-class="'stream-item'"
                                              :item-class-add="addItemClass"
                                              @resized="onItemRendered"
                                              @totop="onTotop">
                                    <div slot="header" v-show="overflow" class="header">
                                        <div class="spinner" v-show="!finished"></div>
                                        <div class="finished" v-show="finished">No more data</div>
                                    </div>
                                </virtual-list>
                                <div class="empty" v-show="!chat.messageLists.length">
                                    <div class="wrapper">
                                        <div class="icon"></div>
                                        <div class="tips">No chats</div>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="input-msg">
                            <emotion @clickEmotion="getEmotion" v-show="showEmotion" :height="300"/>
                            <div>
                                <el-tooltip effect="dark" content="房间名称" placement="top-start">
                                    <el-menu :default-active="chat.room_id"   background-color="#409EFF"
                                             text-color="#fff"
                                             active-text-color="#ffd04b" mode="horizontal" style="margin-bottom:10px;">
                                        <el-menu-item @click="setRoomID(room)" v-for="(room,index) in oauthConfig" :key="index" :index="room.id.toString()">
                                            {{room.name}}
                                        </el-menu-item>
                                    </el-menu>
                                </el-tooltip>
                                <el-tooltip effect="dark" content="发送表情" placement="top-start">
                                    <i @click="showEmotion = !showEmotion" class="el-icon-picture-outline-round icon"/>
                                </el-tooltip>
                                <el-upload :action="cgi.uploadUrl"
                                           :data="fileData"
                                           :headers="headers"
                                           :show-file-list="false"
                                           :on-success="uploadSuccess"
                                           :before-upload="beforeUpload" style="float: left">
                                    <el-tooltip effect="dark" content="发送文件和图片" placement="top-start">
                                        <i  @click="showEmotion = false" class="el-icon-picture-outline icon"/>
                                    </el-tooltip>
                                </el-upload>
                            </div>
                            <div contentEditable="true" ref="message" id="content" @focus="showEmotion = false" @keydown="setMsg">

                            </div>
                        </div>
                        <div class="input-button" style="text-align: right">
                            <el-tooltip effect="dark" content="Shift + Enter 快捷发送" placement="top-start">
                                <el-button type="primary" round plain size="medium" @click="sendMsg">发 送</el-button>
                            </el-tooltip>
                        </div>
                    </el-card>
                </el-col>
                <el-col :xl="{'span':6}" :lg="{'span':8}" :md="{'span':8}" :sm="{'span':8}" :xs="{'span':10}" id="rightBox">
                    <el-card>
                        <div style="background: #fff;min-height: 60px">
                            群公告:
                            <el-carousel tyle="cursor: pointer" :interval="4000" arrow="never" direction="vertical" indicator-position="none" height="100px">
                                <el-carousel-item v-for="(item,index) in groupAnnouncementConfig" :key="index">
                                    <div style="cursor: pointer;margin-top: 20px" v-html="item.name"/>
                                </el-carousel-item>
                            </el-carousel>
                        </div>
                        <el-divider/>
                        <div style="margin-bottom: 10px">
                            在线人数({{chat.userTotal}}/{{chat.userOnline}})
                        </div>
                        <el-autocomplete placeholder="搜索" v-model="chat.users" clearable :fetch-suggestions="querySearch" @clear="clearSearch" style="width: 100%"/>
                        <div class="user-list">
                            <el-menu style="width: 100%;">
                                <el-menu-item :users="user" v-contextmenu:contextmenu @click="sendUser(user,index)" v-for="(user,index) in chat.client_list_part" :key="index" :index="index.toString()">
                                    <el-avatar :size="30" :src="user.client_img" style="cursor: pointer"/>
                                    <span slot="title" style="font-size: 14px" v-html="user.client_name.replace(chat.users,'<b style=color:#0e82fc;font-weight:300>'+chat.users+'</b>')"/>
                                    <!--未读消息数-->
                                    <el-badge v-if="user.total" type="danger" :value="user.total" style="top: 10px;right: 15px"/>
                                    <!--在线-->
                                    <el-badge v-else-if="user.online" type="success" is-dot style="top: 12px;right: 10px"/>
                                    <!--离线-->
                                    <el-badge v-else-if="!user.online" type="info" is-dot style="top: 12px;right: 10px"/>
                                </el-menu-item>
                            </el-menu>
                        </div>
                        <!--右键菜单-->
                        <v-contextmenu ref="contextmenu" @contextmenu="menuRightChange">
                            <v-contextmenu-item @click="userInfoVisible = true"><i class="el-icon-postcard"/> 查看资料</v-contextmenu-item>
                            <v-contextmenu-item divider></v-contextmenu-item>
                            <v-contextmenu-item @click="sendToMessage"><i class="el-icon-chat-line-square"/> 发送消息</v-contextmenu-item>
                        </v-contextmenu>
                    </el-card>
                </el-col>
            </el-row>
        </el-dialog>
        <!---chat message-->
        <!--userInfo Start-->
        <UserInfo :user-info="targetUsers" :dialog-width="dialogWidth" :user-info-visible="userInfoVisible" :closeDialog="closeDialog"/>
        <!--userInfo End-->
    </div>
</template>

<script>
    import { mapGetters, mapActions } from 'vuex'
    import func from '../../api/func'
    import emotion from '../emotion/Index'
    import Push from 'push.js'
    import Item from './Item'
    import VirtualList from 'vue-virtual-scroll-list'
    import UserInfo from './UserInfo'
    export default {
        props: {
            chatVisible: {
                type: Boolean,
                default: () => true
            },
            dialogWidth: {
                type: String,
                default: () => '50%'
            },
            currentPage: {
                type: Number,
                default: () => 1
            },
            sizeLimit: {
                type: Number,
                default: () => 19
            }
        },
        name: 'chatRoom',
        data () {
            return {
                cgi: {uploadUrl: process.env.API_ROOT + 'v1/common/upload'},
                fileData: {},
                headers: {},
                inputMsg: '',
                emotionLists: [],
                showEmotion: false,
                chat: {},
                messageComponent: Item,
                overflow: false,
                finished: false,
                visible: this.chatVisible,
                targetUsers: {},
                userInfoVisible: false,
                userInfoDialogWidth: this.dialogWidth
            }
        },
        components: {
            UserInfo,
            emotion,
            VirtualList
        },
        computed: {
            ...mapGetters(['oauthConfig', 'groupAnnouncementConfig', 'userInfo'])
        },
        watch: {
            inputMsg: function () {
                this.inputMsg = this.$refs.message.innerHTML
            },
            chatVisible: function () {
                this.visible = this.chatVisible
                if (!this.visible) {
                    this.userInfoVisible = false
                }
            }
        },
        methods: {
            ...mapActions(['getOauthConfig', 'saveWeather']),
            /**
             * todo:获取目标用户的信息
             * @param item
             */
            menuRightChange (item) {
                if (item.data.attrs && item.data.attrs.users) {
                    this.targetUsers = item.data.attrs.users
                }
            },
            /**
             * todo:弹框关闭回调
             */
            closeDialog: function () {
                this.userInfoVisible = false
            },
            /**
             * todo:发送消息
             */
            sendToMessage: function () {
                this.sendUser(this.targetUsers, this.targetUsers.uid)
            },
            /**
             * todo:添加class
             * @param index
             */
            addItemClass: function (index) {
                if (this.chat.messageLists.length > 0) {
                    if (this.chat.messageLists[index].to_client_name === 'all') {
                        return this.userInfo.username === this.chat.messageLists[index].from_client_name ? 'creator' : ''
                    } else {
                        return this.userInfo.username === this.chat.messageLists[index].to_client_name ? '' : 'creator'
                    }
                }
            },
            /**
             * todo:记录读取
             */
            onItemRendered: function () {
                if (!this.$refs.vsl) {
                    return
                }
                if (this.$refs.vsl) {
                    const vsl = this.$refs.vsl
                    if (vsl) {
                        this.overflow = vsl.getScrollSize() > vsl.getClientSize()
                    }
                }
            },
            /**
             * todo:向上滚动获取数据
             */
            onTotop: function () {
                setTimeout(() => {
                    if (this.chat.currentPage <= Math.ceil(this.chat.messageTotal / this.chat.sizeLimit)) {
                        this.chat.currentPage++
                        // 获取聊天记录
                        let str = {
                            type: 'history',
                            from_client_name: this.userInfo.username,
                            from_client_id: this.userInfo.uuid,
                            to_client_name: this.chat.to_client_name,
                            to_client_id: this.chat.to_client_id,
                            room_id: this.chat.to_client_name === 'all' ? this.chat.room_id : '',
                            uid: this.userInfo.uuid,
                            page: this.chat.currentPage,
                            limit: this.chat.sizeLimit,
                            source: 'user'
                        }
                        this.userInfo.websocketServer.send(JSON.stringify(str))
                    }
                    this.finished = true
                }, 500)
            },
            /**
             * todo:workerMan-chat链接
             * @param ws
             */
            connect: function (ws) {
                let __this = this
                // 连接建立时发送登录信息
                ws.onopen = function () {
                    let str = {
                        type: 'login',
                        from_client_id: __this.userInfo.uuid,
                        client_name: __this.userInfo.username,
                        room_id: __this.chat.room_id,
                        client_img: __this.userInfo.avatar_url,
                        uid: __this.userInfo.uuid,
                        adcode: __this.userInfo.adcode
                    }
                    ws.send(JSON.stringify(str))
                }
                ws.onmessage = function (response) {
                    let data = JSON.parse(response.data)
                    switch (data['type']) {
                    // 心跳
                    case 'ping':
                        ws.send('{"type":"pong"}')
                        break
                    // 登陆
                    case 'login':
                        __this.chat.client_list = data.client_list
                        __this.chat.client_list_part = data.client_list
                        __this.setUsersLists()
                        __this.saveWeather(data.weather)
                        console.log(data)
                        break
                    // 发送消息
                    case 'say':
                        __this.say(data)
                        __this.chat.client_list = data.client_list
                        __this.chat.client_list_part = data.client_list
                        __this.setUsersLists()
                        __this.$refs.vsl.scrollToBottom()
                        console.log(data)
                        break
                    // 聊天记录
                    case 'history':
                        __this.chat.messageTotal = data.total
                        __this.chat.client_list = data.client_list
                        __this.chat.client_list_part = data.client_list
                        if (__this.chat.currentPage > 1) {
                            __this.chat.messageLists = __this.chat.messageLists.concat(data.message)
                        } else {
                            __this.chat.messageLists = data.message
                            __this.$refs.vsl.scrollToBottom()
                        }
                        __this.chat.messageLists.sort(function (a, b) {
                            return a.time > b.time ? 1 : -1
                        })
                        __this.setUsersLists()
                        console.log(data)
                        break
                    // 消息删除/消息撤回
                    case 'srem':
                    case 'recall':
                        __this.chat.messageLists = data.message
                        __this.chat.client_list = data.client_list
                        __this.chat.client_list_part = data.client_list
                        __this.setUsersLists()
                        __this.removeMessageList(data)
                        console.log(data)
                        break
                    case 'logout':
                        console.log(data)
                        break
                    }
                }
                ws.onclose = function () {
                    console.log('连接关闭,定时重连')
                    __this.connect(ws)
                }
                ws.onerror = function () {
                    console.log('出现错误')
                }
            },
            /**
             * todo:用户搜索
             * @param queryString
             * @param cb (回调函数 cb(result))
             */
            querySearch: function (queryString, cb) {
                let result = []
                for (let i in this.chat.client_list) {
                    if (this.chat.client_list[i].client_name.indexOf(queryString) >= 0) {
                        result.push(this.chat.client_list[i])
                    }
                }
                this.chat.client_list_part = result
            },
            /**
             * todo:清空搜索内容
             */
            clearSearch: function () {
                this.chat.client_list_part = this.chat.client_list
            },
            /**
             * todo:设置未读消息数
             */
            setUsersLists: function () {
                // 展示单个用户未读消息数
                let unreadMessage = []
                this.chat.userOnline = 0
                this.chat.userTotal = 0
                for (let i in this.chat.client_list) {
                    if (this.userInfo.username === this.chat.client_list[i]['client_name']) {
                        unreadMessage = this.chat.client_list[i]['unread']
                        this.chat.msgCount = this.chat.client_list[i]['unreadCount']
                        this.$emit('setMsgCount', this.chat.msgCount)
                    }
                    if (this.chat.client_list[i]['online']) {
                        this.chat.userOnline++
                    }
                    this.chat.userTotal++
                }
                if (unreadMessage.length > 0) {
                    for (let i in unreadMessage) {
                        for (let j in this.chat.client_list) {
                            if (unreadMessage[i]['form'] === j) {
                                this.chat.client_list[j]['total'] = parseInt(unreadMessage[i]['total'])
                            }
                        }
                    }
                }
            },
            /**
             * TODO:设置群聊房间号
             * @param room
             */
            setRoomID: function (room) {
                if (this.chat.room_id !== room.id.toString()) {
                    this.showEmotion = false
                    this.finished = false
                    this.chat.room_id = room.id.toString()
                    this.chat.title = room.name
                    this.chat.desc = room.value
                    this.chat.currentPage = 1
                    this.chat.img = 'https://cdn.pixabay.com/photo/2016/12/13/21/20/alien-1905155_960_720.png'
                    // 加入房间
                    let login = {
                        type: 'login',
                        from_client_id: this.userInfo.uuid,
                        client_name: this.userInfo.username,
                        room_id: this.chat.room_id,
                        client_img: this.userInfo.avatar_url,
                        uid: this.userInfo.uuid,
                        adcode: this.userInfo.adcode
                    }
                    this.userInfo.websocketServer.send(JSON.stringify(login))
                }
                // 群聊时,接收方为所有用户
                this.chat.to_client_id = 'all'
                this.chat.to_client_name = 'all'
                // 获取聊天记录
                let str = {
                    type: 'history',
                    from_client_name: this.userInfo.username,
                    from_client_id: this.chat.from_client_id,
                    to_client_name: this.chat.to_client_name,
                    to_client_id: this.chat.to_client_id,
                    room_id: this.chat.to_client_name === 'all' ? this.chat.room_id : '',
                    uid: this.userInfo.uuid,
                    page: this.chat.currentPage,
                    limit: this.chat.sizeLimit,
                    source: 'room'
                }
                console.log(this.userInfo)
                this.userInfo.websocketServer.send(JSON.stringify(str))
            },
            /**
             * TODO:设置发送给谁
             * @param user
             * @param clientId
             */
            sendUser: function (user, clientId) {
                this.finished = false
                this.chat.to_client_name = user.client_name
                this.chat.to_client_id = clientId === '0' ? 'all' : clientId
                this.chat.from_client_id = user.uid
                this.chat.uid = this.chat.to_client_id
                this.chat.title = user.client_name
                if (this.chat.users) {
                    this.chat.users = user.client_name
                }
                this.chat.img = user.client_img
                this.chat.desc = user.centerInfo.desc
                this.chat.room_id = ''
                user.total = 0
                this.chat.currentPage = 1
                // 获取聊天记录
                let str = {
                    type: 'history',
                    from_client_name: this.userInfo.username,
                    from_client_id: this.userInfo.uuid,
                    to_client_name: this.chat.to_client_name,
                    to_client_id: this.chat.to_client_id,
                    room_id: this.chat.to_client_name === 'all' ? this.chat.room_id : '',
                    uid: this.userInfo.uuid,
                    page: this.chat.currentPage,
                    limit: this.chat.sizeLimit,
                    source: 'user'
                }
                this.userInfo.websocketServer.send(JSON.stringify(str))
            },
            /**
             * TODO:获取发送内容
             */
            setMsg: function () {
                this.inputMsg = this.$refs.message.innerHTML
            },
            /**
             * TODO:发送表情
             * @param emotion
             */
            getEmotion: function (emotion) {
                this.chat.msg_type = 'text'
                this.inputMsg += "<img src='" + emotion.icon + "' width='50px' height='50px' style='object-fit: contain;' alt='" + emotion.emoji + "' title='" + emotion.title + "'>"
                this.$refs.message.innerHTML = this.inputMsg
            },
            /**
             * TODO:图片上传成功
             * @param response
             */
            uploadSuccess: function (response) {
                if (response && response.code === 200) {
                    switch (this.chat.msg_type) {
                    case 'img':
                        this.inputMsg += "<img src='" + response.item.src + "' width='100px' height='100px' style='object-fit: contain;' alt='" + this.chat.from_client_name + "'>"
                        break
                    case 'video':
                        this.inputMsg += "<video src='" + response.item.src + "' width='200px' height='200px' controls='controls'>"
                        break
                    }
                    this.$refs.message.innerHTML = this.inputMsg
                    return
                }
                this.$message({type: 'warning', message: response.msg})
            },
            /**
             * TODO:图片上传前
             * @param file
             */
            beforeUpload: function (file) {
                let ext = file.name.split('.')[1]
                switch (ext.toLocaleLowerCase()) {
                case 'jpg':
                case 'gif':
                case 'png':
                case 'jpeg':
                    this.chat.msg_type = 'img'
                    if (file.size > 2 * 1024 * 1024) {
                        this.$message({type: 'warning', message: 'upload image size error'})
                    }
                    break
                case 'mp4':
                    this.chat.msg_type = 'video'
                    if (file.size > 5 * 1024 * 1024) {
                        this.$message({type: 'warning', message: 'upload video size error'})
                    }
                    break
                default:
                    this.$message({type: 'warning', message: 'Unsupported file format'})
                    break
                }
            },
            /**
             * TODO:发送消息
             * @param data
             */
            say: function (data) {
                if (this.userInfo.username !== data['from_client_name']) {
                    if (data['to_client_id'] !== 'all') {
                        this.chat.title = data['from_client_name']
                        this.chat.to_client_name = data['from_client_name']
                        this.chat.to_client_id = data['from_client_id']
                    }
                    this.chat.messageLists.push(data)
                    this.pushMessage(data['content'])
                }
                if (this.chatVisible && this.userInfo.username === data['from_client_name']) {
                    if (data['to_client_id'] !== 'all') {
                        this.chat.title = data['to_client_name']
                        this.chat.to_client_name = data['to_client_name']
                        this.chat.to_client_id = data['to_client_id']
                    }
                }
            },
            /**
             * TODO:消息发送
             */
            sendMsg: function () {
                this.showEmotion = false
                this.inputMsg = this.$refs.message !== undefined ? this.$refs.message.innerHTML : ''
                if (this.inputMsg.trim() !== '') {
                    // 发送消息
                    let str = {
                        type: 'say',
                        to_client_id: this.chat.to_client_id,
                        to_client_name: this.chat.to_client_name,
                        from_client_name: this.userInfo.username,
                        from_client_id: this.userInfo.uuid,
                        msg_type: this.chat.msg_type,
                        content: this.inputMsg,
                        client_img: this.userInfo.avatar_url,
                        room_id: this.chat.to_client_name === 'all' ? this.chat.room_id : '',
                        uid: this.chat.uid,
                        time: func.setTime(func.getTimestamp() * 1000)
                    }
                    this.userInfo.websocketServer.send(JSON.stringify(str))
                    this.chat.messageLists.push(str)
                    this.$refs.vsl.scrollToBottom()
                    this.$refs.message.innerHTML = ''
                    this.inputMsg = ''
                    return
                }
                if (this.chatVisible) {
                    this.$refs.message.focus()
                    this.$message({type: 'info', message: 'Please enter message'})
                }
            },
            /**
             * todo:推送弹框消息
             * @param message
             */
            pushMessage: function (message) {
                Push.create('你有未读消息', {
                    body: message,
                    requireInteraction: true,
                    icon: 'https://www.fanglonger.com/favicon.ico',
                    timeout: 60000
                })
            },
            /**
             * todo:删除消息
             */
            removeMessageList: function (targetMessage) {
                if (this.chat.messageLists.length > 0) {
                    let i = 0
                    this.chat.messageLists.map((item, index) => {
                        if (this.compareJson(item, targetMessage)) {
                            i = index
                        }
                    })
                    let __this = this
                    let messageLists = []
                    Object.keys(__this.chat.messageLists).forEach(function (message) {
                        messageLists.push(__this.chat.messageLists[message])
                    })
                    messageLists.splice(i, 1)
                    this.chat.messageLists = messageLists
                }
            },
            /**
             * todo:json字符串比较
             * @param jsonA
             * @param jsonB
             * @param field
             * @returns {boolean}
             */
            compareJson: function (jsonA, jsonB, field = ['type']) {
                let a = Object.keys(jsonA)
                let b = Object.keys(jsonB)
                if (a.length !== b.length) {
                    return false
                }
                let total = a.length
                let num = field.length
                for (let i in jsonA) {
                    if (!field.includes(i) && jsonA[i] === jsonB[i]) {
                        num++
                    }
                }
                return num === total
            }
        },
        /**
         * todo:生命周期
         */
        created () {
            // 初始化聊天系统参数
            this.chat = {
                to_client_name: 'all',
                to_client_id: 'all',
                from_client_id: this.userInfo.uuid,
                from_client_name: this.userInfo.username,
                uid: this.userInfo.uuid,
                room_id: this.userInfo.room_id,
                title: this.userInfo.room_name,
                msgCount: 0,
                client_list: [],
                client_list_part: [],
                img: 'https://cdn.pixabay.com/photo/2016/12/13/21/20/alien-1905155_960_720.png',
                desc: '',
                messageLists: [],
                msg_type: 'text',
                users: '',
                userTotal: 0,
                userOnline: 0,
                currentPage: this.currentPage,
                sizeLimit: this.sizeLimit,
                messageTotal: 0
            }
            // 浏览器消息推送
            try {
                Push.Permission.request()
            } catch (e) {
                // eslint-disable-next-line no-unused-expressions
                e ? this.$message.error(JSON.stringify(e)) : ''
            }
            // 图片上传参数
            this.fileData.token = this.userInfo.token
            this.fileData.rand = false
            this.headers.Authorization = `${func.setPassword(func.setRandom(32), func.setRandom(12))}${this.userInfo.token}${func.setPassword(func.setRandom(32), func.setRandom(12))}`
            // 客服系统初始化
            this.connect(this.userInfo.websocketServer)
            this.getOauthConfig('RoomLists')
            this.getOauthConfig('GroupAnnouncementConfig')
            let __this = this
            // 键盘事件
            document.onkeydown = function (e) {
                if (e.code === 'Enter' && e.shiftKey) {
                    e.preventDefault()
                    __this.sendMsg()
                    return false
                }
            }
            // 撤回消息
            this.$on('recallMessage', (newMessage, oldMessage) => {
                // 消息撤回
                newMessage.type = 'recall'
                newMessage['recall_message'] = JSON.parse(JSON.stringify(oldMessage))
                this.removeMessageList(oldMessage)
                // 消息推送
                this.userInfo.websocketServer.send(JSON.stringify(newMessage))
                this.chat.messageLists.push(newMessage)
            })
            // 删除消息
            this.$on('deleteMessage', (newMessage, oldMessage) => {
                // 删除消息
                newMessage.type = 'srem'
                this.removeMessageList(oldMessage)
                this.userInfo.websocketServer.send(JSON.stringify(newMessage))
            })
        }
    }
</script>
<style>
.user-list{
    min-height:355px;
    max-height: 355px;
    overflow: hidden;
    overflow-y: auto;
    padding: 0 !important;
}
#content{
    border: 1px solid #eee;
    height: 95px;
    border-radius: 10px;
    -moz-border-radius:10px;
    -webkit-border-radius:10px;
    padding:10px;
    overflow: scroll;
    overflow-x: hidden;
}
.input-msg{
    min-height: 100px;
    margin-bottom: 10px;
    margin-top: 10px;
}
.input-msg .icon{
    font-size: 25px;
    margin:0 10px 10px 0;
    color: #000;
    cursor: pointer;
}
#leftBox {
    padding-left: 0 !important;
    padding-right: 2px !important;
}
#rightBox {
    padding: 0 !important;
}
#rightBox .el-input__inner{
    margin-bottom: 10px;
}
.el-autocomplete-suggestion {
    display: none;
}
#chat .el-dialog__header {
    background: #409EFF;
}
#chat .el-dialog--center .el-dialog__body {
    padding: 5px 15px 5px !important;
}
#chat .el-card {
    border-radius: 0 !important;
    -webkit-border-radius:  0 !important;
    -moz-border-radius:  0 !important;
}
</style>
<style lang="less">
#chat {
    .stream, .empty {
        position: relative;
        width: 100%;
        height: 320px;
        overflow-y: auto;
        border: 1px solid #eee;
        display: flex;
        flex-direction: column-reverse;

        &.overflow {
            flex-direction: column;
        }

        .stream-item {
            display: flex;
            align-items: center;
            padding: 1em;

            &.creator {
                flex-direction: row-reverse;
            }
        }
    }

    .empty {
        .wrapper {
            position: absolute;
            left: 0;
            top: 0;
            bottom: 0;
            right: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            color: #bfbfbf;
        }

        .icon {
            width: 70px;
            height: 70px;
            background-size: cover;
            background-image: url("data:image/svg+xml,%3Csvg t='1587779561352' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='1970' width='200' height='200'%3E%3Cpath d='M915.90919 428.28025l40.909735 6.785543 1.77339-10.488889 8.708336-12.93766-103.649662-69.574588-7.776104-5.219885-7.837502-2.918469L552.61093 223.932142l-6.92676-2.579755-6.962575 2.308579-347.57209 115.214045-8.804527 2.14792-5.704932 4.046153L53.550637 432.126858l9.832949 13.854543 4.493338 11.238972 39.739073-16.353456L75.734892 485.09114l96.368838 54.05001 0.107447 199.534476-2.349511 4.28356 2.352581 1.310855 0.001023 2.709715 4.859681 0 281.95872 157.194158 7.696286 4.260024 2.344395 1.277086 2.635013 1.726318 9.41851-3.080152 8.748245-3.419889 388.69979-159.572322 0.031722-9.44307L878.607632 546.373879l92.490507-35.759435L915.90919 428.28025zM561.218982 260.057921l245.843174 91.538833-245.843174 92.115977L561.218982 260.057921zM121.002888 475.127208l40.684608-56.45171 5.983271-8.358365 23.439852-32.558533 262.998903 123.622552L411.290297 637.993552 121.002888 475.127208zM203.051579 726.133985l-0.100284-169.629375 226.371704 126.989229 28.646434-91.38636 0.893346 276.69688L203.051579 726.133985zM473.818399 476.500485 225.704508 359.870184l304.674071-100.980879 0 196.376553L473.818399 476.500485zM847.766207 724.616422 489.771743 871.571379l-0.955768-301.186643 45.281299 109.166305 313.667909-121.215736L847.765183 724.616422zM551.061644 639.889739l-57.241702-137.940652 347.098299-130.082684 83.064834 123.892705L551.061644 639.889739z' p-id='1971' fill='%23bfbfbf'%3E%3C/path%3E%3C/svg%3E");
        }
    }

    .header {
        padding: .5em;
        .finished {
            font-size: 14px;
            text-align: center;
            color: #bfbfbf;
        }

        .spinner {
            font-size: 10px;
            margin: 0 auto;
            text-indent: -9999em;
            width: 15px;
            height: 15px;
            border-radius: 50%;
            background: #ffffff;
            background: linear-gradient(to right, #ccc 10%, rgba(255, 255, 255, 0) 42%);
            position: relative;
            animation: load3 1.4s infinite linear;
            transform: translateZ(0);
        }

        .spinner:before {
            width: 50%;
            height: 50%;
            background: #ccc;
            border-radius: 100% 0 0 0;
            position: absolute;
            top: 0;
            left: 0;
            content: '';
        }

        .spinner:after {
            background: #ffffff;
            width: 75%;
            height: 75%;
            border-radius: 50%;
            content: '';
            margin: auto;
            position: absolute;
            top: 0;
            left: 0;
            bottom: 0;
            right: 0;
        }

        @-webkit-keyframes load3 {
            0% {
                transform: rotate(0deg);
            }
            100% {
                transform: rotate(360deg);
            }
        }
        @keyframes load3 {
            0% {
                transform: rotate(0deg);
            }
            100% {
                transform: rotate(360deg);
            }
        }
    }
}
</style>
右鍵查看用戶信息
用戶信息彈框

用户信息组件 UserInfo.vue

<template>
    <div :id="id">
        <el-dialog :visible.sync="visible" :title="userInfo.client_name" :width="userInfoDialogWidth" center id="userInfo" @close="closeDialog" :show-close="false" :style="transformStyle">
            <div slot="title" style="display: flex;align-items: center;position: relative;">
                <el-avatar :src="userInfo.client_img" :size="50" fit="fill" :alt="userInfo.client_name"></el-avatar>
                <div>
                    <div v-html="userInfo.client_name" style="margin-left: 10px;text-align: left;color: #fff"></div>
                    <div slot="title" v-html="userInfo.desc" style="margin-left: 10px;color:#fff"></div>
                </div>
            </div>
            <el-row :gutter="24">
                <el-col :xl="{'span':22,'offset':1}" :lg="{'span':22,'offset':1}" :md="{'span':22,'offset':1}" :sm="{'span':22,'offset':1}" :xs="{'span':22,'offset':1}" class="userInfo">
                    <el-form :model="userInfo" label-width="56px" label-position="left">
                        <el-form-item label="账号:">
                            <span v-html="userInfo.uid"></span>
                        </el-form-item>
                        <el-form-item label="简介:">
                            <span v-html="userInfo.desc"></span>
                        </el-form-item>
                        <el-form-item label="户籍:">
                            <span v-html="userInfo.local"></span>
                        </el-form-item>
                        <el-form-item label="住址:">
                            <span v-html="userInfo.ip_address"></span>
                        </el-form-item>
                        <el-form-item label="标签:">
                            <el-tag style="margin-right: 5px" effect="plain" type="success" v-for="(tags,index) in userInfo.tags" :key="index">{{tags}}</el-tag>
                        </el-form-item>
                    </el-form>
                </el-col>
            </el-row>
        </el-dialog>
    </div>
</template>

<script>
    export default {
        name: 'UserInfo',
        props: {
            userInfoVisible: {
                type: Boolean,
                default: () => false
            },
            userInfo: {
                type: [Object, Array],
                default: () => {}
            },
            closeDialog: {
                type: Function | Boolean
            },
            dialogWidth: {
                type: String,
                default: () => '40%'
            },
            id: {
                type: String,
                default: () => 'userInfo'
            }
        },
        data () {
            return {
                visible: this.userInfoVisible,
                userInfoDialogWidth: this.dialogWidth,
                randomNum: 0,
                transformStyle: {}
            }
        },
        watch: {
            userInfoVisible: function () {
                this.visible = this.userInfoVisible
                this.userInfoDialogWidth = (parseInt(this.dialogWidth.replace('%', '')) - 15) + '%'
                this.getUserInfo()
            }
        },
        created () {
            this.userInfoDialogWidth = (parseInt(this.dialogWidth.replace('%', '')) - 15) + '%'
            this.randomNum = Math.ceil(Math.random() * 5 | 0)
            this.transformStyle = { transform: 'rotate(' + this.randomNum + 'deg)' }
        },
        methods: {
            /**
             * todo:获取用户信息
             */
            getUserInfo: function () {
                this.userInfo.desc = this.userInfo.centerInfo.desc || ''
                this.userInfo.tags = JSON.parse(this.userInfo.centerInfo.tags) || ''
                this.userInfo.ip_address = this.setLocal(JSON.parse(this.userInfo.centerInfo.ip_address)) || ''
                this.userInfo.local = this.setLocal(JSON.parse(this.userInfo.centerInfo.local)) || ''
            },
            /**
             * TODO:设置地址显示
             */
            setLocal: function (item) {
                if (item.length > 0) {
                    let str = item.join(',')
                    return str.replace(new RegExp(/,/g), ' / ')
                }
                return item
            }
        }
    }
</script>

<style lang="less">
#userInfo {
    .el-dialog__body{
        position: relative;
        overflow: hidden;
        background-size: contain;
        background-position: right;
        background-image: url('../../assets/default.jpg');
        background-repeat: no-repeat;
        padding-left: 0 !important;
    }
    .el-form-item {
        margin-bottom: 0!important;
    }
    .el-form-item__content{
        margin-left: 0 !important;
    }
    .el-dialog__header{
        background: #409EFF;
    }
    .clearfix {
        text-align: center;
        color: #000000;
        font-size: 20px;
    }
    .clearfix:before, .clearfix:after {
        display: table;
        content: "";
    }
    .clearfix:after {
        clear: both
    }
}
</style>
消息列表

消息列表组件 Item.vue

<template>
    <div ref="messageLists">
        <!--群聊模板信息展示-->
        <div ref="message" v-contextmenu:contextmenu :source="source" class="item" v-if="source.to_client_name === 'all'" :class="source.from_client_name === userInfo.username ? 'creator' : '' ">
            <div class="avatar">
                <el-avatar :src="source.client_img" />
            </div>
            <div class="body">
                <div class="name" v-if="source.from_client_name !== userInfo.username">{{source.username || source.from_client_name}} {{source.time}}</div>
                <div class="content">
                    <div class="text"><span v-html="source.from_client_name === userInfo.username ? unescape(source.content.replace('%ACCOUNT%','你')) : unescape(source.content.replace('%ACCOUNT%',source.from_client_name))"/></div>
                </div>
            </div>
        </div>
        <!--私聊模板信息展示-->
        <div ref="message" v-contextmenu:contextmenu :source="source" class="item" v-else  :class="source.to_client_name === userInfo.username ? '' : 'creator' ">
            <div class="avatar">
                <el-avatar :src="source.client_img" />
            </div>
            <div class="body">
                <div class="name" v-if="source.to_client_name === userInfo.username">{{source.username || source.from_client_name}} {{source.time}}</div>
                <div class="content">
                    <div class="text"><span v-html="source.from_client_name === userInfo.username ? unescape(source.content.replace('%ACCOUNT%','你')) : unescape(source.content.replace('%ACCOUNT%',source.from_client_name))"/></div>
                </div>
            </div>
        </div>
        <!--右键菜单-->
        <v-contextmenu ref="contextmenu" theme="bright" @contextmenu="menuRightChange" v-if="source.from_client_name === userInfo.username && showContextMenu">
            <v-contextmenu-item @click="setMessages('recall')">消息撤回</v-contextmenu-item>
            <v-contextmenu-item divider/>
            <v-contextmenu-item @click="setMessages('delete')">消息删除</v-contextmenu-item>
        </v-contextmenu>
        <v-contextmenu ref="contextmenu" v-else/>
    </div>
</template>

<script>
    import {mapGetters} from 'vuex'
    import func from '../../api/func'
    import mixins from '../../api/dispatch'
    export default {
        name: 'ChatRoomItem',
        props: {
            source: {
                type: Object,
                default: () => {}
            }
        },
        mixins: [mixins],
        data () {
            return {
                targetMessage: {},
                showContextMenu: true
            }
        },
        computed: {
            ...mapGetters(['userInfo'])
        },
        methods: {
            /**
             * TODO:字符串标签转换
             * @param html
             */
            unescape: function (html) {
                return html
                    .replace(html ? /&(?!#?\w+;)/g : /&/g, '&amp;')
                    .replace(/&lt;/g, '<')
                    .replace(/&gt;/g, '>')
                    .replace(/&quot;/g, '"')
                    .replace(/&amp;nbsp;/g, ' ')
                    // eslint-disable-next-line no-useless-escape
                    .replace(/&#39;/g, "\'")
            },
            /**
             * todo:右键菜单
             * @param item
             */
            menuRightChange: function (item) {
                if (item.data.attrs && item.data.attrs.source) {
                    this.targetMessage = item.data.attrs.source
                }
                this.showContextMenu = this.targetMessage.type !== 'recall'
                // 三分钟后消息不可删除亦不可撤回
                if (this.targetMessage.type !== 'recall') {
                    this.showContextMenu = Date.parse(this.targetMessage.time) / 1000 + 180 > Date.parse(new Date()) / 1000
                }
            },
            /**
             * todo:消息撤回/消息删除
             */
            setMessages: function (item) {
                switch (item) {
                // 撤回消息
                case 'recall' :
                    let recallMessage = JSON.parse(JSON.stringify(this.targetMessage))
                    recallMessage.content = '%ACCOUNT%撤回了一条消息'
                    recallMessage.username = 'systemRobot'
                    recallMessage.client_img = 'https://cdn.pixabay.com/photo/2016/12/13/21/20/alien-1905155_960_720.png'
                    recallMessage.time = func.setTime(new Date())
                    this.dispatch('chatRoom', 'recallMessage', recallMessage, this.targetMessage)
                    break
                // 删除消息
                case 'delete':
                    let deleteMessage = JSON.parse(JSON.stringify(this.targetMessage))
                    this.dispatch('chatRoom', 'deleteMessage', deleteMessage, this.targetMessage)
                    break
                }
            }
        }
    }
</script>

<style lang="less" scoped>
.item {
    display: flex;
    .avatar {
        width: 40px;
        height: 40px;
        border-radius: 50%;
        background: rgba(255, 192, 203, .2);
        img {
            display: block;
            width: 100%;
            height: 100%;
            border-radius: 50%;
        }
    }
    .body {
        flex: 1;
        padding-left: 1em;
        font-size: 16px;
        max-width: 560px;
        word-break: break-word;
        .name {
            padding-bottom: .2em;
            font-size: 12px;
        }
        .content {
            position: relative;
            color: #000;
            background-color: aliceblue;
            border-radius: 15px;
            padding: .5em 1em;
            cursor: default;
            &:after{
                content: '';
                position: absolute;
                right: 100%;
                top: 10px;
                width: 14px;
                height: 14px;
                border-width: 0;
                border-style: solid;
                border-color: transparent;
                border-bottom-width: 10px;
                border-bottom-color: currentColor;
                border-radius: 0 0 0 32px;
                color: aliceblue;
            }
        }
    }
    &.creator {
        transform: rotateX(180deg);
        direction: rtl;
        align-items: flex-end;
        .avatar {
            transform: rotateX(180deg);
        }
        .body {
            transform: rotate(180deg);
        }
        .text {
            transform: rotateY(180deg);
            direction: ltr;
        }
    }
}
</style>
emotion表情包

 图表Emotion.vue组件

<template>
    <div class="emotion">
        <div class="emotion-box" :style="{height: height + 'px' }">
            <el-tabs type="border-card" tab-position="top" @tab-click="changeEmotionLists" v-model="type" v-infinite-scroll="scrollEmotion">
                <el-tab-pane v-for="(emotionType,index) in emotionTypeLists" :name="emotionType.type" :key="index" :label="emotionType.title">
                    <div class="emotion-box-line" v-for="(emotion, index) in emotionList" :key="index">
                        <div class="emotion-item" >
                            <el-image :src="emotion.icon" :alt="emotion.title" style="width: 30px;height: 30px;" lazy @click.native="clickHandler(emotion)"></el-image>
                        </div>
                    </div>
                </el-tab-pane>
            </el-tabs>
        </div>
    </div>
</template>

<script>
    import apiLists from '../../api/api'
    export default {
        props: {
            height: {
                type: Number,
                default: () => 300
            }
        },
        data () {
            return {
                emotionTypeLists: [
                    {'type': '1', 'title': '表情', 'id': 'emotion'},
                    {'type': '2', 'title': '人物', 'id': 'person'},
                    {'type': '3', 'title': '动作', 'id': 'action'},
                    {'type': '4', 'title': '家庭', 'id': 'family'},
                    {'type': '5', 'title': '自然', 'id': 'natural'},
                    {'type': '6', 'title': '食物', 'id': 'food'},
                    {'type': '7', 'title': '体育', 'id': 'sport'},
                    {'type': '8', 'title': '建筑', 'id': 'building'},
                    {'type': '9', 'title': '用品', 'id': 'supplies'},
                    {'type': '10', 'title': '符号', 'id': 'symbol'},
                    {'type': '11', 'title': '国旗', 'id': 'flag'}
                ],
                type: '1',
                emotionList: [],
                limit: 55,
                page: 1,
                pages: 0,
                offsetPage: 0
            }
        },
        methods: {
            /**
             * todo:图标切换
             * @param typeObj
             */
            changeEmotionLists: function (typeObj) {
                this.type = typeObj.name
                this.page = 1
                this.emotionList = []
                this.showEmotionLists(this.page, this.limit)
            },
            /**
             * todo:点击图标
             * @param emotion
             */
            clickHandler (emotion) {
                this.$emit('clickEmotion', emotion)
            },
            /**
             * TODO:展示表情数据
             * @param page
             * @param limit
             */
            showEmotionLists: function (page, limit) {
                apiLists.EmotionLists({type: this.type, limit: limit, page: page}).then(response => {
                    this.pages = response.data.item.pages
                    let data = response.data.item.data
                    for (let i in data) {
                        this.emotionList.push(data[i])
                    }
                })
            },
            /**
             * TODO:数据流加载
             */
            scrollEmotion: function () {
                this.offsetPage = this.offsetPage + 1
                if (this.offsetPage <= this.pages) {
                    this.page++
                    this.showEmotionLists(this.page, this.limit)
                }
            }
        },
        mounted () {
            this.$nextTick(function () {
                this.showEmotionLists(this.page, this.limit)
            })
        }
    }
</script>
<style scoped>
    .emotion{
        position: absolute;
        bottom: 310px;
        width: 500px;
        /*left: 300px;*/
    }
    .emotion-box {
        margin: 0 auto;
        width: 100%;
        box-sizing: border-box;
        padding: 0 0 5px 10px;
        overflow-x: hidden;
        overflow-y: auto;
        position: initial;
        z-index:2000
    }
    .emotion-box-line {
        display: flex;
        float: left;
    }
    .emotion-item {
        flex: 1;
        margin-left: 13px;
        margin-top: 10px;
        cursor: pointer;
    }
</style>

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WorkerMan实现Web通讯(使用Vue实现前端页面逻辑) 的相关文章

  • esp8266烧录Html文件,实现内置网页控制设备!

    代码地址如下 xff1a http www demodashi com demo 14321 html 一 前言 xff1b 这个月也快结束了 xff0c 时间真快 xff0c 我服务器知识自学依然在路途中 xff0c 这几天听到热点网页配
  • Golang创建XML

    package main import 34 encoding xml 34 34 fmt 34 34 io ioutil 34 type Post struct XMLName xml Name 96 xml 34 post 34 96
  • xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    1 解压 tar zxvf wmsdk bundle xxxxxxx tar gz cd wmsdk bundle xxxxxxx 2 make installpkgs 或者installpkgs sh 3 安装gcc arm none e
  • 超声波换能器的几点总结

    超声波换能器是超声波测量的关键件 xff0c 必须保证超声波换能器的质量以及特性稳定 1 超声波换能器的激励信号频率接近谐振频率是触发信号强度最大 2 传感器是被动元器件 xff0c 激励信号进行激励会产生震动 xff0c 接收端接收信号
  • windows7安装ubuntu双系统教程

    转载自 xff1a http www cnblogs com masbay p 10745170 html windows7安装ubuntu双系统教程 一 先搞清楚自己电脑的类型 xff1a 本次安装的Ubuntu的电脑类型是MBR传统bi
  • STM32 串口详解

    目录 01 USART的特点 02 USART简介 2 1 数据传输模型 2 2 帧结构 2 3 波特率 03 STM32的USART 04 代码配置 01 USART的特点 USART是通用异步收发传输器 xff08 UniversalA
  • STM32串口开发之环形缓冲区

    01 简介 在之前的文章 stm32 串口详解 中 xff0c 我们讲解了串口的基本应用 xff0c 使用串口中断接收数据 xff0c 串口中断发送回包 xff08 一般可以使用非中断形式发送回包 xff0c 在数据接收不频繁的应用中 串口
  • 第六章 类域

    第六章 类域 一 类作用域 使用 访问全局变量 int x 61 1 namespace wd int x 61 20 class Test public Test int value x value void print int x co
  • 步进电机使用总结之噪声、振动的抑制

    不正确地驱动步进电机很容易导致电机发出 嗡嗡 的噪声和很大的振动 当驱动步进电机时 xff0c 如果发现步进电机处于静止状态时 xff0c 其内部都发出很明显的噪音 xff0c 有点类似线圈快速变化那种 xff0c 一般是由于线圈电流过大导
  • ADRC自抗扰控制学习

    入门 自抗扰控制01 xff1a 为何ADRC会成为百年PID算法的继承者 xff1f http news eeworld com cn mp ZLG a23516 jspx 自抗扰控制02 ADRC如何避免执行错误命令 http news
  • 关于串级PID控制的理解

    1 关于内环积分器 飞控里经常会用到串级PID控制 xff0c 通常设计方法为从内环到外环 xff0c 如速度环 位置环 内环通常为PD控制 xff0c 或P控制 xff0c 因为要保证一定带宽 xff0c 而积分器会抑制内环的带宽 xff
  • UMD代码格式

    span class token punctuation span span class token keyword function span span class token punctuation span root span cla
  • Tomcat Server.xml配置详解

    在理解Tomcat配置之前 xff0c 需要先熟悉一下Tomcat的架构 xff0c 便于更好的修改配置 一 Tomcat结构 server xff1a 即服务器 xff0c 每个tomcat程序启动后 xff0c 就是一个server s
  • Linux下U盘、SD卡挂载与卸载

    1 手动挂载 卸载 U盘 SD卡 对于ARM Linux来说 xff0c 第一次使用U盘或SD时 xff0c U盘这个文件目录是不能直接进入的 xff0c 我们需要对其进行挂载 xff0c 然后再接下来的使用中就可以直接进行使用了 通过再网
  • Java 阻塞队列--BlockingQueue

    1 什么是阻塞队列 xff1f 阻塞队列 xff08 BlockingQueue xff09 是一个支持两个附加操作的队列 这两个附加的操作是 xff1a 在队列为空时 xff0c 获取元素的线程会等待队列变为非空 当队列满时 xff0c
  • 大厂SQL经典面试题(二)留存问题

    留存率 是用户分析的核心指标之一 xff0c 留存问题也是一个经常考的题目 问题 现场写一道SQL 给定用户表Users 求出每个日期对应的活跃用户数 次日留存用户数 次日留存率 指标定义 某日活跃用户数 某日活跃的去重用户数 N日留存用户
  • 程序猿代码面试指南 PDF

    前言 今年是最难求职年 xff0c 我希望通过这篇文章能帮大家提高求职成功率 这篇文章分为简历篇 面试篇 谈薪酬篇 xff0c 包括了找工作过程中各个环节的技巧和防坑指南 这篇文章就是给大家分享左神这本 程序员代码面试指南 IT名企算法与数
  • 从 Java 代码逆向工程生成 UML 类图和序列图

    前言 本文面向于那些软件架构师 xff0c 设计师和开发人员 xff0c 他们想使用 IBM Rational Software Architect 从 Java 源代码来逆向工程生成 UML 类和序列图 逆向工程经常被用来从已有的源代码中
  • http的三次握手

    在http的三次握手当中 xff0c 首先客户端发起一个我要发送一个数据包的请求 xff0c 发送到服务端 xff0c 这里面呢会有一个标志SYN 61 1 Seq 61 X xff0c syn是一个标识 xff0c 就是我这是一个创建请求
  • SQL优化面试专题

    介绍 xff1a 无论您是创建Web应用程序的开发人员 xff0c 还是参与Web测试的DBA或测试人员 xff0c SQL方面的技巧在数据库编程和数据库验证中都非常重要 因此 xff0c 我们整理了QL性能优化方面的面试问题 SQL性能优

随机推荐

  • Docker容器:将带UI的程序直接转为Web应用,so easy

    摘要 xff1a 使用Docker容器 xff0c 将带UI的程序 xff0c 直接转换为Web应用 很方便 xff0c 跟大家分享一下 本文分享自华为云社区 使用Docker容器 xff0c 将带UI的程序 xff0c 直接转为Web应用
  • 38道多线程核心面试题(附答案)

    前言 今天给大家分享的是比较全面的多线程面试题 xff0c 大家在面试的过程中不免会被问到很多专业性的问题 xff0c 有的时候回答的并不是那么全面和精细 xff0c 这仅仅代表个人观点 1 如何预防死锁 xff1f 1 首先需要将死锁发生
  • Java程序员,最常用的20%技术有哪些?

    1 基本的数据结构和算法真的非常重要 xff1a 不管你做过多少项目或者是熟悉多少框架和工具 xff0c 面试和考察一个人还是大部分停留在基本功上 所以 xff0c 在每天工作开发之余 xff0c 应保证一定的时间段不断去打磨自己的基本功
  • Linux 程序编译过程详解

    大家肯定都知道计算机程序设计语言通常分为机器语言 汇编语言和高级语言三类 高级语言需要通过翻译成机器语言才能执行 xff0c 而翻译的方式分为两种 xff0c 一种是编译型 xff0c 另一种是解释型 xff0c 因此我们基本上将高级语言分
  • 关于485总线 A、B端上拉下拉电阻选择

    问 xff1a about rs485 用电阻上拉 B用电阻下拉 A B间用电阻连接 xff0c 这些电阻参数大致多少 xff1f 我们公司的设计是 TTL输入都用光偶隔离 输出加上拉和下拉 xff0c 中间加TVS和2个电阻串联 xff0
  • JAVA集合框架(一)-ARRAYLIST

    1 ArrayList的特点 存放的元素有序元素不唯一 可以重复 随机访问快插入删除元素慢非线程安全 2 底层实现 底层初始化 xff0c 使用一个Object类型的空对象数组 xff0c 初始长度为0 源码 Object类型对象数组引用
  • Java如何将两个数组合并为一个数组呢?

    数组 xff1a 数组 xff08 Array xff09 是有序的元素序列 1 若将有限个类型相同的变量的集合命名 xff0c 那么这个名称为数组名 组成数组的各个变量称为数组的分量 xff0c 也称为数组的元素 xff0c 有时也称为下
  • pca9548及vsc9548的设备树简单挂载

    简述 pca9548及vsc9548是iic拓展器件 xff0c 主要是防止iic器件地址冲突 通过写其0x0寄存器可切换0 7路iic 设备树挂载 这里用到了vsc9548 xff0c 且在第7路上挂在了eeprom器件 i2c 64 f
  • 类的作用域

    类的作用域简称类域 xff0c 它是指在类的定义中由一对花括号所括起来的部分 每一个类都具有该类的类域 xff0c 该类的成员局部于该类所属的类域中 在类的定义中可知 xff0c 类域中可以定义变量 xff0c 也可以定义函数 从这一点上看
  • 常见问题(持续更新)

    近期整理的初级开发遇到的问题 xff0c 希望对大家有用 1 Unsatisfied dependency expressed through field 39 baseMapper 39 于是在pom xml中搜索mybatis关键字 x
  • orb-slam中的orb特征

    1 ORB特征简介 ORB是Oriented FAST and Rotated BRIEF xff08 oFAST and rBRIEF xff09 的简称 xff0c ORB的名字已经说明了其来源 xff0c 其实ORB特征是采用FAST
  • 计算机组成原理 第二篇:总线 1.总线原理和意义

    总线是什么 总线是连接多个部件的信息传输线 是各部件共享的传输介质 总线可以传输的原理 总线实际上是由许多传输线或通路组成 每条线上保持的电平高低即是所传输的信号 每条线可一位一位地传输二进制代码 一串二进制代码可以在一段时间内逐一传输完成
  • 主流深度学习算法简介

    深度学习算法简介 1 深度学习主流算法包括 1 1 CNN 卷积神经网络 卷积神经网络 xff08 CNN xff09 是最常见的深度学习方法之 一 自20 世纪80 年代后期以来 xff0c CNN 已应用于视觉识别与分类任务 xff0c
  • 常见外贸英文术语(下)

    很多从事外贸行业的人都会用Skype IntBell AntTone等网络电话和客户沟通 xff0c 但是有时候会因为一些外贸行业的专业英文术语闹出笑话 今天就让我们来总结一些外贸常见英文术语 xff0c 让你和客户沟通更加顺畅 xff01
  • Phpstorm2018 使用破解补丁永久激活

    1 安装phpstorm xff0c 安装包请自行官网下载 http www jetbrains com phpstorm download 2 下载JetbrainsCrack jar文件 xff0c 存放至你的phpstorm执行文件同
  • va_start 与 va_end用法

    1 包含头文件 include lt stdarg h gt 2 使用方法 参考 http www cnblogs com hanyonglu archive 2011 05 07 2039916 html include lt stdio
  • jetson-sd卡制作(批量烧写)

    jetson系列如果使用sd卡开发 xff0c 开发完成后可以不用重新制作根文件系统 拷贝目前的SD卡即可实现批量烧写 一 开发好的SD卡制作img文件 1 将sd卡插到PC端 xff0c 查看sd卡设备 eg dev sdd fdisk
  • 结构体知识点

    结构体的结构如下 xff1a span class token comment 关键字struct是数据类型说明符 xff0c 指出下面说明的是结构体类型 span span class token keyword struct span
  • SurfaceView 的一般绘制View用法(一)

    前段时间写了不少关于自定义View相关的文章 xff0c 最近两个项目同时开工 xff0c 忙成狗了 xff0c 这不是不写博客的理由哈 xff0c 今晚写一篇关于SurfaceView相关的博客 xff0c 还是和以前一样 xff0c 今
  • WorkerMan实现Web通讯(使用Vue实现前端页面逻辑)

    需要使用到的扩展 https github com tangbc vue virtual scroll list 最终效果 对话框 主页面 Main vue lt template gt lt div gt lt chat message