47、pc端類似微信聊天框案例之vue遞歸組件

最近需要做一個(gè)聊天框展示效果淆衷,消息里面還存在N層消息集合缸榄,毋庸置疑肯定是要遞歸實(shí)現(xiàn),正好vue中也有組件遞歸的使用祝拯,翻閱了之前寫過的組件遞歸案例vue遞歸組件案例

  • ps: 圖片就不提供了甚带,按照對(duì)應(yīng)擴(kuò)展名手動(dòng)添加

效果圖

image.png

案例

demo.vue

  <!--
    @author: SM
    @desc: 微信聊天記錄案例
-->
<template>
  <div style="background-color: white;display:inline-block">
    <chatrecordList :title="chatItem.msg.title" :item="chatItem.msg && chatItem.msg.item"/>
  </div>
</template>

<script>

// 導(dǎo)入聊天組件
import ChatrecordList from './chatrecordList'

export default {
    components: {
        ChatrecordList
    },
    data () {
        return {
            // 模擬數(shù)據(jù)
            chatItem: {
                msg:{
                    title: 'xx和xx聊天記錄',
                    item: [{
        "type": "ChatRecordText",
        "msgtime": 1699862191694,
        "content": {
            "content": "在嗎?需要參加一個(gè)需求會(huì)議佳头。"
        }
    },
    {
        "type": "ChatRecordFile",
        "msgtime": 1699862191694,
        "content": {
            "filename": "從入門到精通.zip",
            "filesize": "101010101",
            "fileext": "zip"
        }
    },
    {
        "type": "ChatRecordLink",
        "msgtime": 1699862191694,
        "content": {
            "title": "這是分享鏈接的標(biāo)題",
            "image_url": "https://fc1tn.baidu.com/it/u=3658245154,2773193846&fm=202&mola=new&crop=v1",
            "description": "鏈接描述"
        }
    },
    
    {
        "type": "chatrecord",
        "msgtime": 1699862191694,
        "content": {
            "title": "張三和李四聊天內(nèi)容",
            "item": [{
                    "type": "ChatRecordText",
                    "msgtime": 1699862191694,
                    "content": {
                        "content": "在嗎鹰贵?需要參加一個(gè)需求會(huì)議。"
                    }
                },
                {
                    "type": "ChatRecordFile",
                    "msgtime": 1699862191694,
                    "content": {
                        "filename": "2從入門到精通.zip",
                        "filesize": "101010101",
                        "fileext": "zip"
                    }
                },
                {
                    "type": "ChatRecordLink",
                    "msgtime": 1699862191694,
                    "content": {
                        "title": "2這是分享鏈接的標(biāo)題",
                        "image_url": "https://fc1tn.baidu.com/it/u=3658245154,2773193846&fm=202&mola=new&crop=v1",
                        "description": "鏈接描述"
                    },
                    "fileext": "zip"
                },
                {
                    "type": "ChatRecordLocation",
                    "msgtime": 1699862191694,
                    "content": {
                        "address": "2經(jīng)緯度",
                        "longitude": "116.374373",
                        "latitude": "39.91582"
                    }
                }
            ]
        }
    },

    {
        "type": "ChatRecordLocation",
        "msgtime": 1699862191694,
        "content": {
            "address": "經(jīng)緯度",
            "longitude": "116.374373",
            "latitude": "39.91582"
        }
    }
]
                }
            }
        }
    }
}

</script>
<style lang='scss' scoped>
</style>

chatrecordList.vue 遞歸組件

<!--
    @author: SM
    @desc: 微信聊天記錄遞歸組件
-->
<template>
  <div class="x-chatrecordlist-cls">
    <!-- {{item}} -->
    <div class="chart-box cursor-pointer" @click="showChart = true">
        <div class="chart-title">{{title}}</div>
        <div class="chart-content">
            <div class="list-item" v-for="(citem, index) in item" v-if="index < 3">
                <span>
                    {{citem.type == 'ChatRecordText' ? (citem[chatRecordType[citem.type]].content && citem[chatRecordType[citem.type]].content.length > 18 ? citem[chatRecordType[citem.type]].content.substring(0, 18) : citem[chatRecordType[citem.type]].content) : '['+chatRecordType[citem.type]+']'}}
                </span>
            </div>
        </div>
        <div class="chart-footer" v-if="item.length > 1">聊天記錄</div>
    </div>
    <!-- 彈出詳細(xì)聊天內(nèi)容  :modal="modal"  :modal="false" -->
    <el-dialog
     class="x-chatrecordlist-dialog-cls"
      :title="title"
      :visible.sync="showChart"
      :append-to-body="true"
      width="800px">
      <el-scrollbar style="height: 600px">
        <div class="x-chatrecordlist-info-cls" v-for="(citem, index) in item">
            <div> {{dayjs(citem.msgtime).format('YYYY-MM-DD HH:hh:mm')}} </div>
            <!-- 內(nèi)容區(qū)域 -->
            <div class="info-content">
                <!-- 文本 -->
                <div v-if="citem.type == 'ChatRecordText'">{{citem.content && citem.content.content}}</div>
                <div v-else-if="citem.type == 'ChatRecordFile'">
                    <!-- <el-tooltip class="item" effect="light" content="點(diǎn)擊可下載" placement="top"> -->
                        <!-- @click="downLoad(citem.content)" -->
                        <div class="newfile"  >
                            <img :src="formateFileType(citem.content, 'file')" alt="" />
                            <div class="leftdescri">
                            <div class="name">{{citem.content &&  citem.content.filename}}</div>
                            <div class="size">{{citem.content &&  citem.content.filesize | threeDecimal}}</div>
                            </div>
                        </div>
                    <!-- </el-tooltip> -->
                </div>
                <div v-else-if="citem.type == 'ChatRecordImage'">[圖片]</div>
                <div v-else-if="citem.type == 'ChatRecordVideo'">[視頻]</div>
                <div v-else-if="citem.type == 'ChatRecordLink'">
                    <div class="msgtypecard cursor-pointer">
                        <div @click="goPage(citem.content)">
                        <div class="card_name">
                            <div class="title">{{ citem.content &&  citem.content.title }}</div>
                        </div>
                        <div class="middle">
                            <img
                            :src="
                                citem.content &&  citem.content.image_url == ''
                                ? defaultLinkPic
                                : citem.content &&  citem.content.image_url
                            "
                            alt=""
                            style="width: 48px; height: 48px; margin-right: 5px;padding-bottom: 5px;"
                            />
                            <div class="desp">{{ citem.content &&  citem.content.description }}</div>
                        </div>
                        </div>
                    </div>
                </div>
                <div v-else-if="citem.type == 'ChatRecordLocation'">
                   <div style="width:400px;height:200px;position: relative; padding-bottom: 14px;"  @click="addmapImg(citem)">
                    <div class="mapadress" style="padding-bottom: 5px;">{{citem.content &&  citem.content.address}}</div>
                    <el-amap :center='[citem.content &&  citem.content.longitude, citem.content &&  citem.content.latitude]' zoom='15' >
                        <el-amap-marker vid="marker" :position="[citem.content &&  citem.content.longitude, citem.content &&  citem.content.latitude]" :label='{ content: citem.content &&  citem.content.title, offset:[-10,-23]}'></el-amap-marker>
                    </el-amap>
                    </div>
                </div>
                <div v-else-if="citem.type == 'chatrecord'">
                    <chatrecordList :title="citem.content && citem.content.title" :item="citem.content.item"/>
                </div>
                <div v-else>{{defaultMsg}}</div>
            </div>
        </div>
      </el-scrollbar>
    </el-dialog>
    <!-- 彈出詳細(xì)聊天內(nèi)容 -->

    <!-- map 預(yù)覽 -->
    <div class="shabowbox" v-if="mapImg" :style="{'z-index':  getPopupZIndex() + 1}">
        <div class="close" @click="mapImg = false">
            <i class="el-icon-circle-close"></i>
        </div>
        <div class="shabowboxvidoe">
            <el-amap :center='[mpaItem.longitude, mpaItem.latitude]' zoom='15' >
                <el-amap-marker vid="marker" :position='[mpaItem.longitude, mpaItem.latitude]' :label='{ content: mpaItem.title, offset:[-10,-23]}'></el-amap-marker>
            </el-amap>
        </div>
    </div>
    <!-- map 預(yù)覽 -->

  </div>
</template>

<script>
// z-index問題
import { PopupManager } from 'element-ui/lib/utils/popup';
// 文件類型判斷
import { loadFileType } from "./fileType.js";
// 日期工具類
import dayjs from 'dayjs'
//
export default {
    name: 'chatrecordList',
    data () {
        return {
            // 文件類型
            loadFileType,
            // 下載文件路徑
            action: process.env.VUE_APP_BASE_STATIC_PATH,
            mpaItem: {},
            // 預(yù)覽地圖
            mapImg:false,
            // 默認(rèn)圖片占位符
            defaultLinkPic: require("@/assets/file_icon/link1.png"),
            defaultMsg: '[該消息類型暫不能展示]',
            dayjs,
            // 展示聊天內(nèi)容
            showChart: false,
            // 定義顯示的聊天類別 v-if="citem.type == 'ChatRecordText'"
            chatRecordType: {
                ChatRecordText: 'content',
                ChatRecordFile: '文件',
                ChatRecordImage: '圖片',
                ChatRecordVideo: '視頻',
                ChatRecordLink: '鏈接',
                ChatRecordLocation: '位置',
                ChatRecordMixed: '聊天記錄',
                // 這個(gè)類別里面還有子集
                chatrecord: '聊天記錄'
            }
        }
    },
    // 外部透傳的值
    props: {
        // 數(shù)組
        item: Array,
        // 標(biāo)題
        title: String,
        // 彈出詳情是否需要遮照
        modal: {
            type: Boolean,
            default: true
        }
    },
    filters:{
        threeDecimal: function(value){
            value-=0 // 類型轉(zhuǎn)換
            let min=1024*8 // 1KB
            let max=1024*1024*8 // 1MB
            if((value>max)){
                return (value/max).toFixed(3)+'MB'
            }else if(value>min){
                return (value/min).toFixed(3)+'KB'
            }else{
                return (value/8).toFixed(3)+'B'
            }
        }
    },
    methods: {
         formateFileType(item, type) {
            // item = JSON.parse(item);
            if (type == "file") {
                let obj = this.loadFileType.find((citem) => {
                    return citem.extension == item.fileext;
                });

                if (obj) {
                    return obj.icon;
                } else {
                    return require("@/assets/file_icon/unknow.png");
                }
            }
        },
        // 下載文件
        downLoad(item) {
            item = JSON.parse(item);
            let url = this.action + item.attachment
            const link = document.createElement("a");
            link.href = url;
            link.setAttribute("download", item.filename); // 下載文件的名稱及文件類型后綴
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link); // 下載完成移除元素
            window.URL.revokeObjectURL(url); // 釋放掉blob對(duì)象
        },
        // z-index
        getPopupZIndex(){
            return PopupManager.nextZIndex();
        },
        // 跳轉(zhuǎn)鏈接
        goPage(chatItem) {
            chatItem = JSON.parse(chatItem);
            window.open(chatItem.link_url, "_blank");
        },
        // 地圖
        addmapImg(item){
            this.mpaItem = JSON.parse(item.content) || {}
            this.mapImg = true
        },
    }
}

</script>
<style lang='scss'>
    .x-chatrecordlist-cls {
        font-size: 14px;
        .chart-box {
            padding: 12px;
            border: 1px solid #ccc;
            border-radius: 6px;
            width: 260px;
            .chart-title {
                color: #222222;
            }
        }
        .chart-content {
            padding-top: 4px;
            max-height: 120px;
            overflow: hidden;
            font-weight: 800;
            .list-item {
                line-height: 22px;
            }
        }
        .chart-footer {
            border-top: 1px solid #ccc;
            margin-top: 8px;
            padding-top: 8px;
        }
        .chart-content, .chart-footer {
            font-size: 12px;
            color: #999999
        }

        // 彈出顯示
          .shabowbox {
            position: fixed;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.4);
            left: 0;
            top: 0;
            z-index: 99999999999999;
            .shabowboxvidoe {
                position: fixed;
                width: 800px;
                height: 475px;
                left: 50%;
                margin-left: -400px;
                top: 50%;
                margin-top: -235px;
                z-index: 2001;
                background: #fff;
            }
        }
        // 彈出顯示

          .close {
            position: fixed;
            width: 50px;
            height: 50px;
            right: 10px;
            z-index: 2012;
            top: 10px;
            text-align: center;
            line-height: 50px;
            font-size: 20px;
            color: #fff;
            cursor: pointer;
            font-size: 43px;
        }
    }
    .x-chatrecordlist-dialog-cls {
        .el-scrollbar__bar.is-vertical {
            display: none !important;
        }
        // 彈框里面的內(nèi)容
        .x-chatrecordlist-info-cls {
            border-bottom: 1px solid #ccc;
            margin-bottom: 6px;
            .info-content {
                color: #222222;
                padding-top: 14px;
                padding-bottom: 14px;
            }
             /* 新文件 */
            .newfile{
                display: flex;
                width: 237px;
                height: 68px;
                border: 1px solid rgba(224,224,224,1);
                border-radius: 4px;
                padding: 12px;
                img{
                width: 40px;
                height: 40px;
                margin-right: 12px;
                }
                .leftdescri{
                flex:1;
                .name{
                    font-size: 14px;
                    color: #222222;
                    letter-spacing: 0;
                    font-weight: 400;
                    margin-bottom: 7px;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    display: -webkit-box;
                    -webkit-line-clamp: 1;  /* 多行文本的行數(shù)  3 - 表示三行文本超出后在第三行后顯示(...) */
                    -webkit-box-orient: vertical;
                }
                .size{
                    font-size: 12px;
                    color: #666666;
                    letter-spacing: 0;
                    font-weight: 400;
                }
                }
            }
            // 鏈接康嘉、卡片樣式
            .msgtypecard {
                    width: 340px;
                    height: 112px;
                    margin: 10px;
                    margin-left: 5px;
                    font-size: 16px;
                    color: #222;
                    border-radius: 8px;
                    -webkit-box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2),
                    0 6px 8px 0 rgba(0, 0, 0, 0.19);
                    box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 8px 0 rgba(0, 0, 0, 0.19);
                    position: relative;
                    .card_name {
                    padding: 10px;
                    }
                    .title {
                    font-size: 16px;
                    color: #000;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    display: -webkit-box;
                    -webkit-line-clamp: 2;
                    -webkit-box-orient: vertical;
                    display: -moz-box;
                    -moz-line-clamp: 2;
                    -moz-box-orient: vertical;
                    word-wrap: break-word;
                    word-break: break-all;
                    white-space: normal;
                    }
                    .desp {
                    font-size: 14px;
                    color: rgb(68, 67, 67);
                    float: left;
                    width: 100%;
                    height: 100%;
                    // padding-top: 10px;
                    .title {
                        width: 100%;
                        overflow: hidden;
                        text-overflow: ellipsis;
                        display: -webkit-box;
                        -webkit-line-clamp: 2;
                        -webkit-box-orient: vertical;
                        display: -moz-box;
                        -moz-line-clamp: 2;
                        -moz-box-orient: vertical;
                        word-wrap: break-word;
                        word-break: break-all;
                        white-space: normal;
                    }
                    }
                    .middle {
                    width: 100%;
                    padding-left: 10px;
                    .desp {
                        font-size: 14px;
                        color: rgb(68, 67, 67);
                        float: left;
                        width: 70%;
                        height: 100%;
                        // padding-bottom: 10px;
                        overflow: hidden;
                        text-overflow: ellipsis;
                        display: -webkit-box;
                        -webkit-line-clamp: 2;
                        -webkit-box-orient: vertical;
                        display: -moz-box;
                        -moz-line-clamp: 2;
                        -moz-box-orient: vertical;
                        word-wrap: break-word;
                        word-break: break-all;
                        white-space: normal;
                    }
                    img {
                        float: right;
                        width: 25%;
                        height: 100%;
                    }
                    }
                    .card_foot {
                    position: absolute;
                    height: 20px;
                    border-top: 1px solid #efefef;
                    text-align: left;
                    bottom: 15px;
                    padding: 10px;
                    color: #333;
                    font-weight: bold;
                    width: 100%;
                    .weapp {
                        font-family: PingFangSC-Regular;
                        font-size: 12px;
                        color: #999999;
                        letter-spacing: 0;
                        font-weight: 400;
                    }
                    }
            } // end 卡片碉输、鏈接
        }
   }
</style>

fileType.vue 文件擴(kuò)展名對(duì)應(yīng)的圖片

//  擴(kuò)展名和下載類型的對(duì)應(yīng)關(guān)系
export const loadFileType = [{
        extension: 'doc',
        icon: require('@/assets/file_icon/word_1.png')
    },
    {
        extension: 'docx',
        icon: require('@/assets/file_icon/word_1.png')
    },
    {
        extension: 'xls',
        icon: require('@/assets/file_icon/excel.png')
    },
    {
        extension: 'xlsx',
        icon: require('@/assets/file_icon/excel.png')
    },
    {
        extension: 'ppt',
        icon: require('@/assets/file_icon/PPT.png')
    },
    {
        extension: 'pptx',
        icon: require('@/assets/file_icon/PPT.png')
    },
    {
        extension: 'mp3',
        icon: require('@/assets/file_icon/yinyue.png')
    },
    {
        extension: 'mp4',
        icon: require('@/assets/file_icon/yinyue.png')
    },
    {
        extension: 'm4a',
        icon: require('@/assets/file_icon/yinyue.png')
    },
    {
        extension: 'pdf',
        icon: require('@/assets/file_icon/PDF_.png')
    },
    {
        extension: 'zip',
        icon: require('@/assets/file_icon/zip.png')
    },
    {
        extension: 'jpeg',
        icon: require('@/assets/file_icon/pic.png')
    },
    {
        extension: 'jpg',
        icon: require('@/assets/file_icon/pic.png')
    },
    {
        extension: 'png',
        icon: require('@/assets/file_icon/pic.png')
    },
    {
        extension: 'txt',
        icon: require('@/assets/file_icon/txt.png')
    }
]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡信或評(píng)論聯(lián)系作者凄鼻。
  • 序言:七十年代末腊瑟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子块蚌,更是在濱河造成了極大的恐慌闰非,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件峭范,死亡現(xiàn)場(chǎng)離奇詭異财松,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)纱控,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門辆毡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人甜害,你說我怎么就攤上這事舶掖。” “怎么了尔店?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵眨攘,是天一觀的道長脑题。 經(jīng)常有香客問我递惋,道長,這世上最難降的妖魔是什么径筏? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任该肴,我火速辦了婚禮情竹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匀哄。我一直安慰自己秦效,他們只是感情好雏蛮,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棉安,像睡著了一般底扳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贡耽,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音鹊汛,去河邊找鬼蒲赂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛刁憋,可吹牛的內(nèi)容都是我干的滥嘴。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼至耻,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼若皱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尘颓,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤走触,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后疤苹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體互广,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年卧土,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惫皱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡尤莺,死狀恐怖旅敷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情颤霎,我是刑警寧澤媳谁,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站捷绑,受9級(jí)特大地震影響韩脑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粹污,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一段多、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧壮吩,春花似錦进苍、人聲如沸加缘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拣宏。三九已至,卻和暖如春杠人,著一層夾襖步出監(jiān)牢的瞬間勋乾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國打工嗡善, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辑莫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓罩引,卻偏偏與公主長得像各吨,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子袁铐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 事件輪詢
    鴻澤云碼閱讀 201評(píng)論 0 1
  • 10分鐘學(xué)會(huì)前端工程化(webpack4.0) 目錄 一揭蜒、概要 1.1、前端工程化 1.1.1剔桨、前端工程化的任務(wù) ...
    前端驛站閱讀 565評(píng)論 0 1
  • 081.什么是 Polyfill 屉更? Polyfill 指的是用于實(shí)現(xiàn)瀏覽器并不支持的原生 API 的代碼。 比如...
    造了個(gè)輪子閱讀 296評(píng)論 0 0
  • 什么是前端腳手架? 腳手架指的就是:有人幫你把這個(gè)開發(fā)過程中要用到的工具领炫、環(huán)境都配置好了偶垮,你就可以方便地直接開始做...
    醉生夢(mèng)死閱讀 167評(píng)論 0 0
  • 此文基于官方文檔,里面部分例子有改動(dòng)帝洪,加上了一些自己的理解 什么是組件似舵? 組件(Component)是 Vue.j...
    陸志均閱讀 3,807評(píng)論 5 14