首页 > 编程语言 >Vue3 —— 组件练习题(附源码)

Vue3 —— 组件练习题(附源码)

时间:2022-11-15 15:48:06浏览次数:34  
标签:练习题 obj year list height 源码 Vue3 组件 margin

一、定义一个vue分页组件,实现客户端分页功能

1.1、子组件A(页数按钮)

<!-- 本组件用于遍历分页的页数按钮 -->
<template lang="">
    <div class="btn-box">
        <!-- 页数,下标+1 -->
        {{pzie + 1}}
    </div>
</template>
<script lang="ts">
import { ref } from 'vue'
export default {
    // 接收参数 "size"
    props: ['size'],
    setup (p:any) {
        // 响应式参数
        const pzie = ref(p.size)
        // 暴露出去页面上
        return { pzie }
    }
}
</script>
<style >
.btn-box{
    width: 30px;
    height: 30px;
    line-height: 30px;
    border-radius: 2.5px;
    background-color: #ddd;
}
</style>

 1.1、父组件

<!-- 本组件用于实现分页的功能 -->
<template lang="">
    <div>
        <!-- 表格里面的数据 -->
        <table border="1" width="60%">
            <tr>
                <th>商品编号</th>
                <th>商品名称</th>
                <th>商品价格</th>
            </tr>
            <!-- 遍历数据到表格上 -->
            <tr v-for="item in data" :key="item.id">
                <td>{{item.id}}</td>
                <td>{{item.name}}</td>
                <td>{{item.price}}</td>
            </tr>
        </table>
        <div class="btn2-box">
            <button @click = btnsx(0)>上一页</button>
            <!-- 显示页数的按钮 -->
            <!-- 功能:添加类、:size="i":(传下标到子组件里显示页数) -->
            <Btn class="Btn-st" v-for="(item, i) in list.btncount" :class="{'addclass': list.addclass==i}" :key="i" :size="i" @click="clicknext(i)"></Btn>
            <button @click = btnsx(1)>下一页</button>
            <div class="skip">
                跳转到:<input type="text" v-model="nums"> 页
                <button @click="skip()">Go</button>
            </div>
        </div>
       
    </div>
</template>
<script lang="ts">
// 导入子组件文件
import Btn from "../components/1.1 分页按钮.vue";
import { reactive, ref, toRefs } from 'vue'
export default {
    // 注册子组件
    components: {
        Btn
    },
    setup() {
        // 参数1:模拟数据库里的数据
        // 参数2:在页面上显示的数据
        // 参数3:显示在分页上的总条数、当前页、每页显示的数量、总页数
        const s: any = reactive({ sql: [], data: [], list: {} });

        // 绑定跳转页面的文本框数据
        let nums:any = ref(1);

        // 循环100条数据
        for (let i = 0; i < 100; i++) {
            // 把100条数据存储到sql数组里面
            s.sql.push({
                id: i + 1, // 编号,从1开始
                name: `桃子${i + 1}`, // 名称后面+1
                price: `${(i + 100) * 2}` // 价格
            })
        }
        // 添加属性到参数3里面
        s.list.toal = s.sql.length // 总数据长度
        s.list.danqianye = 1 // 当前页 默认为 1
        s.list.pagezie = 10  // 每一页显示数量
        s.list.btncount = s.list.toal / s.list.pagezie // 总页数 = 总数据长度 / 每一页显示数据数量
        s.list.addclass = 0; // 这个变量用于给显示页数的按钮(有颜色)添加类

        // 参数1 等于数据源(sql)起点下标 例如0 就等于从 s.sql[0] 开始拿数据
        // 参数2 等于数据源(sql)终点下标 例如10 就等于从 s.sql[9] 结束
        function createfenye(ii: any, i2: any) {
            // 每次进来都要清空之前所缓存的数据,要不然就实现不了下一页的效果
            s.data = []
            // 每一页的数据从参数1开始,到参数2结束
            for (let i = ii; i < i2; i++) {
                // 然后重新赋值给s.data数组
                s.data.push(s.sql[i])
            }

        }
        // 初始化的时候就从0条数据开始到10条数据结束获取数据
        createfenye(0, 10)

        // 该方法用于实现点击页数按钮功能,传入该页数按钮中的下标
        function clicknext(i: any) {
            // 当前页赋值为i,跟着下标的变化而变化当前页的值
            s.list.danqianye = i
            // 显示页数按钮(有颜色)的值也需要变化
            s.list.addclass = i

            // 例如 i 的下标等于 0 的时候  i+1,就代表它在第一页
            // 1 × 10 等于10, 10 - 10 = 0 说明它是从0 开始 赋值数据到 s.data 里边
            // i 的下标等于 0 终点就等于 i + 1 = 1, 1 x 10 = 10, 说明它是从10 结束 赋值
            createfenye(((i + 1) * 10 - 10), ((i + 1) * 10))
        }

        // 该方法用于实现点击上一页与下一页功能,传入1(下一页)或0(上一页)判断用户点击的是哪个按钮
        function btnsx(i: any) {
            if (i == 0) { // i为0,即点击了上一页
                // 获取到上一页的数据
                createfenye(((s.list.danqianye - 1) * 10 - 10), ((s.list.danqianye - 1) * 10))
                // 然后当前页也需要-1
                s.list.danqianye--
                // 显示页数按钮(有颜色)也需要后退一个
                s.list.addclass--
            } else {
                // 与上面相反
                createfenye(((s.list.danqianye + 1) * 10 - 10), ((s.list.danqianye + 1) * 10))
                s.list.danqianye++
                s.list.addclass++
            }
        }

        // 该方法用于跳转页面的
        function skip(){
            // 当前页赋值为i,跟着下标的变化而变化当前页的值
            s.list.danqianye = nums.value
            // 显示页数按钮(有颜色)的值也需要变化
            s.list.addclass = nums.value

            console.log(s.list.danqianye);
            console.log(s.list.addclass);
            
            createfenye(((s.list.danqianye) * 10 - 10), ((s.list.danqianye) * 10))
            s.list.addclass--
        }

        // 暴露到页面上
        return {
            ...toRefs(s),
            clicknext,
            btnsx,
            nums,
            skip
        }
    }
}
</script>
<style >
table {
    margin: auto;
    height: 350px;
    border-collapse: collapse;
}

th {
    background-color: pink;
    color: #fff;
}

.btn2-box {
    display: flex;
    width: 60%;
    margin: auto;
    margin-top: 30px;

}

.Btn-st {
    margin: 5px;
    cursor: pointer;
}

.addclass {
    background-color: pink;
    color: #fff;
}

.skip {
    width: 240px;
    height: 30px;
    /* border: 1px solid red; */
    margin-left: 30px;
    margin-top: 5px;
}
.skip input{
    width: 70px;
    height: 20px;
    text-align: center;
    outline: none;
}
.skip button{
    height: 26px;
    margin-left: 10px;
}

</style>

1.3、运行结果

二、使用vue3实现图书列表与详细展示功能

2.1、子组件A(可能想搜的)

<!-- 本组件为图书列表中的“ 可能想搜索的图书 ” 部分 -->
<template lang="">
    <!-- 把从父组件传过来的数据通过遍历循环出来 -->
    <div class="hot-min" v-for="(item,i) in list.hot">{{item}}</div>
</template>
<script lang="ts">
import { reactive, ref, toRefs } from 'vue';
export default {
    // 接收参数
    props: ['data'],
    setup(p:any) {
        // 定义一个变量把数据存储起来
        const s:any = reactive({list:[] });
        s.list.hot= p.data
        // 暴露出去页面上
        return { ...toRefs(s) }
    }
}
</script>
<style>
.hot-min {
    width: 80px;
    height: 40px;
    background-color: #fff;
    margin: 10px;
    line-height: 40px;
}
</style>

2.2、子组件B(图书列表)

<!-- 本组件用于显示所有的图书列表 -->
<template lang="">
    <div class="main" v-for="(item,i) in list.hot" @click="clicklist(item)">
        <div class="main_left">
            <img :src="item.tp" alt="" >
        </div>
        <div class="main_right" >
            <p style="font-size: 18px;">{{item.name}}</p>
            <p style="color: #aaa;font-size: 14px;">{{item.zz}}</p>
            <p style="color: red;font-size: 16px;">¥ {{item.price}}</p>
        </div> 
    </div> 
</template>

<script lang="ts">
// 导入路由文件
import { useRouter } from 'vue-router'
import { reactive, toRefs } from 'vue';
export default {
    setup() {
        const s: any = reactive({ list: [], list2: [] });
        s.list.hot = [
            { name: "web 前端开发", zz: "南方IT学院", price: "62.4", tp: "https://ts1.cn.mm.bing.net/th/id/R-C.5c5ec1769389901eac83e9cf75a5bd33?rik=GXnLovzVPOcEUg&riu=http%3a%2f%2fwww.tup.tsinghua.edu.cn%2fupload%2fbigbookimg3%2f080900-01.jpg&ehk=4efizNnDT%2fRsmCZVq5H2UkONQ%2bBT2oOu6%2br%2bP511Lv0%3d&risl=&pid=ImgRaw&r=0" },
            { name: "JavaScript语言精粹", zz: "清华大学出版社", price: "88.9", tp: "https://pic2.zhimg.com/v2-a4265bf1750016b9a83abdc60a8471c7_b.jpg" },
            { name: "HTML5权威指南", zz: "北京大学出版社", price: "108.0", tp: "https://codesheep.oss-cn-hangzhou.aliyuncs.com/blog/20200916233828.png" },
            { name: "ES6标准入门", zz: "清华大学出版社", price: "35.8", tp: "https://pic2.zhimg.com/v2-17c68027e62866178d5159374318ad7d_b.jpg" },
            { name: "图解css3", zz: "南方it学院", price: "11.2", tp: "https://pic2.zhimg.com/v2-64c56a356fa4777037ffd15233417585_b.jpg" },
            { name: "node.js", zz: "人民大学出版社", price: "77.9", tp: "https://ts1.cn.mm.bing.net/th/id/R-C.a6bfd782f3aa77ca473a208514a65e18?rik=r07N6f2SwTOl1w&riu=http%3a%2f%2fwww.fairysoftware.com%2fad_images%2fqian_duan_kai_fa_06.jpg&ehk=6nr%2fdlTyGl4EtlcAJu3KDeRyRLAH9VvZC8Ae58EB1y0%3d&risl=&pid=ImgRaw&r=0" },
        ]

        // 路由跳转
        const $router = useRouter();
        // 该方法实现点击每一个图书时,跳转到该图书的详情页上,通过query传递点击的图书信息参数过去
        function clicklist(item:any) {
            // path是路由路径
            $router.push({ path:'info', query:item }) 
            console.log(item);
        }


        // 暴露出去页面上
        return { ...toRefs(s),clicklist }
    }
}
</script>
<style>
/* 身体 */
.main {
    width: 90%;
    height: 100px;
    margin: auto;
    /* border: 1px solid magenta; */
    margin-top: 20px;
    display: flex;

}

.main_left {
    width: 35%;
    height: 100%;
}

.main_left img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.main_right {
    width: 65%;
    height: 100%;
    padding-bottom: 10px;
    border-bottom: 1px solid #aaa;

}

.main_right p {
    text-align: left;
    line-height: 20px;
    margin: 10px;
}
</style>

2.3、子组件C(图书详情页)

<!-- 本组件用于展示某一本图书信息的详情页 -->
<template lang="">
    <div class="headinfo">
        <svg t="1667807456323" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2517" width="25" height="32"><path d="M677.391515 873.916768c-7.86101 0-15.618586-2.999596-21.617778-8.895354L324.473535 533.721212c-11.998384-11.894949-11.998384-31.340606 0-43.235555L655.670303 159.288889c5.999192-5.999192 13.756768-8.895354 21.617778-8.895354 7.757576 0 15.618586 2.999596 21.617778 8.895354 11.894949 11.894949 11.894949 31.237172 0 43.235555L389.223434 512.103434 698.905859 821.785859c11.894949 11.998384 11.894949 31.340606 0 43.235555-5.895758 5.895758-13.756768 8.895354-21.514344 8.895354z m0 0" p-id="2518" fill="#707070"></path></svg>
        <span style="color:red;border-bottom: 2px solid #fb8768;">商品</span>
        <span>详情</span>
        <span>评论</span>
        <span>推荐</span>
        <svg t="1667807536833" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3774" width="25" height="32"><path d="M746.662019 512c0 51.835575 42.044582 93.865831 93.865831 93.865831 51.851948 0 93.865831-42.029232 93.865831-93.865831 0-51.836599-42.013883-93.865831-93.865831-93.865831C788.706601 418.135192 746.662019 460.163401 746.662019 512z" p-id="3775" fill="#515151"></path><path d="M89.604272 512c0 51.835575 42.043558 93.865831 93.864808 93.865831 51.822272 0 93.865831-42.029232 93.865831-93.865831 0-51.836599-42.043558-93.865831-93.865831-93.865831C131.648854 418.135192 89.604272 460.163401 89.604272 512z" p-id="3776" fill="#515151"></path><path d="M418.132634 512c0 51.835575 42.013883 93.865831 93.866854 93.865831 51.821249 0 93.864808-42.029232 93.864808-93.865831 0-51.836599-42.043558-93.865831-93.864808-93.865831C460.146517 418.135192 418.132634 460.163401 418.132634 512z" p-id="3777" fill="#515151"></path></svg>
    </div>
    <div class="picture">
        <!-- 用传过来的参数,直接渲染到页面 -->
        <img :src="route.query.tp" alt="">
    </div>
    <div class="text">
        <!-- 这部分也是获取路由跳转时传过来的信息,直接渲染到页面上 -->
        <p>{{route.query.name}}</p>
        <P style="color:#aaa;font-size: 16px;">{{route.query.zz}}</P>
        <P style="color:red;font-size: 16px;">¥ <span style="font-size: 20px;">{{route.query.price}}</span> </P>
    </div>
    <div class="bottom">
        <div>
            <svg t="1667808724702" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2531" width="32" height="32"><path d="M940.960749 425.143816c0.093121-17.86488-2.838651-35.791158-8.207939-52.113915-0.061398-0.463558-0.153496-0.89437-0.246617-1.26583-0.370437-1.788739-0.86367-3.517103-1.573845-5.183046L858.609159 186.54565c-12.527314-37.334303-48.50369-60.228733-90.589205-60.32083L260.369006 126.22482c-42.641169 0-75.778894 22.678512-87.781252 58.531068L94.833879 368.463909c-0.370437 1.079588-0.771573 2.684132-1.110287 4.319375-5.646604 17.339924-8.485255 35.143405-8.485255 53.008284 0.047072 47.364751 19.658735 91.617627 53.040007 123.1856l0.214894 269.90315c0 43.50484 35.452443 78.895885 79.049381 78.895885l580.622914-0.370437c43.535539-0.093121 78.957283-35.57524 78.957283-79.111802l-0.058328-260.299421C917.076782 526.08888 940.986331 477.395878 940.960749 425.143816zM798.072411 835.695287l-580.529793 0.370437c-9.564843 0-17.339924-7.713682-17.339924-17.217127l-0.185218-232.914724c16.764825 5.293563 35.163871 7.983835 55.045687 7.983835 50.632167-0.153496 97.531314-22.400173 129.496329-60.47535 31.81152 37.766139 78.340229 59.858296 128.880299 60.166311 50.138934-0.401136 96.543823-22.586414 128.200824-60.32083 31.965016 38.075177 78.926584 60.228733 129.805368 60.228733 15.559371-0.077771 30.233582-1.77646 43.916209-5.060249l0.051165 229.867318C815.413358 827.889507 807.606555 835.664587 798.072411 835.695287zM823.065641 520.114818c-13.977339 7.652284-31.410384 11.570523-51.68208 11.69332-37.58092 0-71.828932-19.068288-91.823312-51.434439-1.419326-3.055592-3.795443-8.114818-8.238638-12.989849-5.090948-5.708002-14.346753-12.464893-29.588899-12.464893-12.279674 0-23.418362 5.028527-29.712719 12.743232-4.16588 4.689812-6.448876 9.348925-8.084119 12.742209-19.74674 31.966039-53.718459 51.218522-90.527806 51.496861-37.272905-0.215918-71.335698-19.407002-91.206258-51.619658-1.388627-2.838651-3.703345-7.621585-7.312546-11.663644-14.502295-17.309224-46.960545-16.414855-59.610656-1.326205-4.659113 5.090948-7.096628 10.212596-8.670473 13.700023-19.931958 31.965016-54.211692 51.126424-91.607394 51.249221-19.931958 0-36.994566-3.734044-50.755987-11.07729l-0.030699 0c0 0 0 0-0.030699 0-35.297924-18.789948-57.235562-55.32198-57.266261-95.309716 0-11.81714 1.974981-23.727401 5.92392-35.328623 0.277316-0.832971 0.523933-1.759063 0.740874-2.715855l76.581166-181.054589c1.573845-4.628414 6.325056-18.72855 30.144554-18.72855l507.805468 0c9.780761 0.586354 26.628474 2.313695 32.613792 19.931958l71.706135 178.555675c0.278339 1.295506 0.617054 2.529613 0.926092 3.547803 3.980661 11.631922 5.954619 23.449062 5.954619 35.175127C879.375112 464.761116 857.80791 501.107929 823.065641 520.114818z" p-id="2532" fill="#707070"></path></svg>
            <p>店铺</p>
        </div>
        <div>
            <svg t="1667809633396" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4325" width="32" height="32"><path d="M510.7 894.6c-37.2 0-74.3-14.4-102.6-43.2l-0.2-0.2-269.6-277.9c-24.9-25.5-44-55.5-56.8-88.9-12.3-32.3-18.2-66.3-17.4-101.1 0.8-34.9 8.2-68.8 22.1-100.6 14.4-33 35.1-62.3 61.5-87 48.6-45.9 112.5-69.3 180-66.1 66.2 3.2 128.6 31.5 175.7 79.9l9 9.2 8.3-9.3c97.9-100.4 254-106.6 355.3-14.1l0.3 0.3c26.4 24.7 47.2 54 61.6 87.1 13.9 31.8 21.3 65.7 22.1 100.7 0.8 34.8-5.1 68.8-17.4 101.1-12.8 33.4-31.9 63.4-56.8 88.9l-272.4 278c-28.3 28.7-65.5 43.2-102.7 43.2zM458 800.5c29.1 29.6 76.4 29.5 105.5-0.1l272.3-277.9c35.7-36.6 54.8-85.5 53.7-137.5-1.2-52.3-22.7-100.7-60.6-136.2-73.1-66.6-186-61.8-257.5 10.9l-30.5 34.2-0.9 0.8c-15.2 14.8-38.7 14.6-53.5-0.5l-0.1-0.1-33-33.8c-34.7-35.6-80.5-56.4-129-58.8-48.4-2.3-94.2 14.4-128.8 47.1l-0.1 0.1c-38 35.5-59.6 83.9-60.8 136.2-1.2 52 17.9 100.9 53.7 137.7L458 800.5z" p-id="4326" fill="#707070"></path><path d="M339.7 288c-64.1 1.4-98.6 29.4-116.3 52.6-10.2 13.5-16 26.8-19.3 37-4.1 10.5-7.4 22.3-9.8 35.7-3.5 19.6 9.5 38.3 29 41.8 2.2 0.4 4.3 0.6 6.4 0.6 17.1 0 32.2-12.2 35.4-29.6 1.5-8.4 3.6-16 6.2-22.7l0.9-2.1c1.1-4 3.9-11.7 10.2-19.1 8.4-9.9 20.7-16.6 36.6-19.8 6.8-1.4 14.2-2.2 22.3-2.4 17.7-0.4 32.1-13.4 34.8-30.3 0.3-2.1 0.5-4.2 0.4-6.4-0.4-19.9-16.9-35.7-36.8-35.3z" p-id="4327" fill="#707070"></path></svg>
            <p>收藏</p>
        </div>
        <div>
            <svg t="1667810312978" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5320" width="32" height="32"><path d="M928.922574 99.438757l-67.214844 0c-15.159258 0-28.122501 10.878768-30.755468 25.803689l-15.35164 86.96977L222.034932 212.212216c-17.253966 0-31.233352 13.979386-31.233352 31.233352s13.979386 31.233352 31.233352 31.233352l582.540589 0-49.773613 281.97407c-8.103562 37.283138-29.688159 81.4388-86.410022 81.4388l-375.389646 0c-32.819476 0-57.504692-5.947456-76.324316-82.851986-0.11154-0.477884-0.243547-0.945535-0.376577-1.413186l-90.304725-306.293965c-4.89038-16.531512-22.23542-25.986861-38.797631-21.12718-16.541745 4.880147-25.997094 22.245653-21.12718 38.797631l90.101087 305.603233c15.830547 64.286142 44.582381 129.752156 136.829342 129.752156l375.389646 0c74.79959 0 129.97626-49.107441 147.605779-131.348514 0.081864-0.376577 0.152473-0.741897 0.213871-1.118474l71.697949-406.186046 41.014112 0c17.253966 0 31.233352-13.979386 31.233352-31.233352S946.17654 99.438757 928.922574 99.438757z" p-id="5321" fill="#707070"></path><path d="M361.610828 333.028862c-17.253966 0-31.233352 13.979386-31.233352 31.233352l0 30.470989c0 17.253966 13.979386 31.233352 31.233352 31.233352 17.253966 0 31.233352-13.979386 31.233352-31.233352l0-30.470989C392.84418 347.008248 378.86377 333.028862 361.610828 333.028862z" p-id="5322" fill="#707070"></path><path d="M605.376691 333.028862c-17.253966 0-31.233352 13.979386-31.233352 31.233352l0 30.470989c0 17.253966 13.979386 31.233352 31.233352 31.233352 17.253966 0 31.233352-13.979386 31.233352-31.233352l0-30.470989C636.610042 347.008248 622.630656 333.028862 605.376691 333.028862z" p-id="5323" fill="#707070"></path><path d="M560.28982 474.086505c-14.539134-9.292644-33.845853-5.083785-43.149752 9.455349-0.121773 0.193405-13.379729 19.449981-33.968649 19.449981-20.008706 0-32.453133-18.127869-33.287127-19.378349-9.17087-14.407128-28.274974-18.809391-42.824341-9.750061-14.650675 9.099239-19.155269 28.355815-10.044774 43.00649 11.214413 18.047028 41.980113 48.588625 86.156242 48.588625 43.952025 0 75.094302-30.308283 86.572728-48.222281C579.048045 502.717589 574.818721 483.389382 560.28982 474.086505z" p-id="5324" fill="#707070"></path><path d="M296.093649 739.519854c-51.668777 0-93.700055 42.031279-93.700055 93.700055 0 51.668777 42.031279 93.700055 93.700055 93.700055 51.668777 0 93.700055-42.031279 93.700055-93.700055C389.793704 781.550109 347.762425 739.519854 296.093649 739.519854zM296.093649 864.453261c-17.223267 0-31.233352-14.010085-31.233352-31.233352 0-17.223267 14.010085-31.233352 31.233352-31.233352s31.233352 14.010085 31.233352 31.233352C327.327 850.443176 313.316915 864.453261 296.093649 864.453261z" p-id="5325" fill="#707070"></path><path d="M670.89387 739.519854c-51.668777 0-93.700055 42.031279-93.700055 93.700055 0 51.668777 42.031279 93.700055 93.700055 93.700055 51.668777 0 93.700055-42.031279 93.700055-93.700055C764.593925 781.550109 722.56367 739.519854 670.89387 739.519854zM670.89387 864.453261c-17.223267 0-31.233352-14.010085-31.233352-31.233352 0-17.223267 14.010085-31.233352 31.233352-31.233352s31.233352 14.010085 31.233352 31.233352C702.127222 850.443176 688.117137 864.453261 670.89387 864.453261z" p-id="5326" fill="#707070"></path></svg>
            <p>购物车</p>
        </div>
        <div style="height: 60px;">
            <button class="btn" >立即购买</button>
            <button class="btn" style="background-color:#f2564a">加入购物车</button>
        </div>
    </div>
</template>
<script lang="ts">
// 需要导入这个路由
import { useRoute } from 'vue-router';

export default {
    setup() {
        // 接收路由跳转时传过来的数据
        const route = useRoute()
        // 通过route.query,打印传递过来的数据
        console.log(route.query);

        return { route }
    }
}
</script>
<style scoped>
.headinfo {
    width: 100%;
    height: 33px;
    /* border: 1px solid magenta; */
    display: flex;
    justify-content: space-between;

}

.headinfo span {
    line-height: 35px;
}

.picture {
    height: 200px;
    border-bottom: 1px solid #ddd;
    margin-top: 20px;
    padding-bottom: 10px;
}

.picture img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.text {
    border-bottom: 1px solid #ddd;
}

.text p {
    text-align: left;
    margin-left: 20px;
    font-size: 18px;
}

.bottom {
    width: 100%;
    height: 60px;
    position: fixed;
    bottom: 0px;
    /* border: 1px solid red; */
    display: flex;
    justify-content: space-around;
}
.bottom svg{
    margin-top: 5px;
}
.bottom p {
    margin-top: -3px;
    font-size: 14px;
}

.btn {
    background-color: #febd21;
    width: 100px;
    height: 100%;
    border: none;
    color: #fff;
    font-size: 16px;
}
</style>

2.4、父组件

<!-- 本组件为父组件,用于展示图书列表的信息 -->
<template lang="">
    <div class="head">
        <svg style="margin-top: 3px;" t="1667737776727" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2516" width="32" height="32"><path d="M710.4 838.4 358.4 492.8c-12.8-12.8-32-12.8-44.8 0l0 0c-12.8 12.8-12.8 32 0 44.8l352 352c12.8 12.8 32 12.8 44.8 0l0 0C723.2 876.8 723.2 851.2 710.4 838.4z" p-id="2517" fill="#707070"></path><path d="M358.4 531.2l352-352c12.8-12.8 12.8-32 0-44.8l0 0c-12.8-12.8-32-12.8-44.8 0L313.6 486.4c-12.8 12.8-12.8 32 0 44.8l0 0C326.4 544 345.6 544 358.4 531.2z" p-id="2518" fill="#707070"></path></svg>
        <div class="textbox">
            <svg t="1667752526391" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2608" width="32" height="32"><path d="M663.006587 602.400314l-32.11848 0-12.063745-12.080118c40.177008-44.250786 64.278915-104.586905 64.278915-168.934382 0-144.831451-116.607671-261.4381-261.439123-261.4381-144.787449 0-261.386934 116.606648-261.386934 261.4381 0 144.831451 116.598462 261.387958 261.386934 261.387958 64.390455 0 124.725551-24.09372 168.993733-64.279938l12.011556 12.081141 0 32.162482 201.112213 201.051838 60.32691-60.335096L663.006587 602.400314zM421.664154 602.400314c-100.589875 0-181.021662-80.43281-181.021662-181.014499 0-100.641041 80.43281-181.014499 181.021662-181.014499 100.624668 0 181.005289 80.373458 181.005289 181.014499C602.669443 521.967504 522.288822 602.400314 421.664154 602.400314" p-id="2609" fill="#515151"></path></svg>
            <input type="text" />
        </div>
        <span class="screen">筛选</span>
    </div>
    <div class="Deputy_head">
        <span style="color:red">默认</span>
        <span >销量</span>
        <span >价格</span>
        <span >好评</span>
        <span >出版价格</span>
    </div>
    <div class="hot_search">
        <!-- 导入子组件 -->
        <Search :data="lists"></Search>
    </div>
    <!-- 身体 -->
    <List></List>
</template>
<script lang="ts">
// 导入子组件
import Search from '../components/2.1 热门搜索组件.vue'
import List from '../components/2.2 图书列表.vue'

import { reactive } from 'vue';
export default {
    // 注册组件
    components:{
        Search,
        List
    },
    setup() {
        // 定义“ 可能想搜索 ” 部分的数据,传到子组件里面
        // (本来图书列表页哪里也需要在父组件定义数据传过去的,但是写的时候,还没有想到,所以就直接在子组件写了,比较推荐用以下这种方式传过去)
        const lists = reactive(["vue2","vue3","react","node.js","ES6","MySql"])
        return {
            lists
        }
    }
}
</script>
<style>
/* 头部 */
.head{
    display: flex;
    justify-content: space-between;
    /* border: 1px solid red; */
}

.textbox{
    width: 250px;
    height: 40px;
    background-color: #eeeeee;
    border-radius: 20px;
    line-height: 40px;
}
.textbox svg{
    vertical-align: middle;
    margin-left: 5px;
}
.textbox input{
    font-size: 16px;
    border: none;
    outline: none;
    background-color: #eeeeee;
}
.screen{
    margin-right: 10px;
    line-height: 40px;
}
/* 副头部 */
.Deputy_head{
    display: flex;
    justify-content: space-between;
    margin: 20px 10px;
}
/* 热门搜索 */
.hot_search {
    width: 90%;
    height: 120px;
    margin: auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
    background-color: #f2f2f2;
    border-top: 1px solid #ddd;
    border-bottom: 1px solid #ddd;
}


</style>

2.5、运行结果

结果1:展示图书列表

 

 结果2:点击随意一本图书都可以,进入该详情页,因为已经实现了动态化

三、使用Vue 组件(component)完成一个精美的日历

3.1、组件A(天数)

<!-- 本组件用于接收父组件遍历过来的天数 -->
<template lang="">
    <div>
        {{day}}
    </div>
</template>
<script lang="ts">
export default {
    // 接收参数数据
    props:['day']
}
</script>
<style scoped>

</style>

3.2、组件B(选择年份)

<!-- 本组件用于:定义年份 -->
<template lang="">
    <div class="ya-box">
        <!-- 遍历数据 -->
        <div class="year" v-for="(item, i) in list" :key="i">
            <!-- 挖个坑,定义坑的名称,把年份数据传到父组件里面 -->
            <slot name="cc" :data="item"></slot>
        </div>
    </div>
</template>
<script lang="ts">
import { reactive } from 'vue';
export default {
    setup() {
        // 定义年份的数据(当时没想到可以用循环遍历,所以先随意的定死了一些数据)
        const list = reactive([
            { year: '2015' },
            { year: '2016' },
            { year: '2017' },
            { year: '2018' },
            { year: '2019' },
            { year: '2020' },
            { year: '2021' },
            { year: '2022' },
            { year: '2023' },
            { year: '2024' },
            { year: '2025' }
        ]);
        // 循环添加多个年份,从2026年开始到5000年结束(年份区间可以自定义的)
        for (let i = 2026; i<5000;i++) {
            // 循环添加到list数组里面,强转i为字符串添加
            list.push({ year: String(i) })
        }

        // 暴露出去页面
        return { list }
    }
}
</script>
<style scoped>
.ya-box{
    width: 100%;
    height: 340px;
    /* border: 1px solid #eeeeee; */
    overflow-y: scroll;
    overflow-x: hidden;
}
.year {
    width: 100%;
    height: 30px;
    /* border: 1px solid lawngreen; */
    line-height: 30px;
    margin: 20px 0;
    font-weight: bold;
    cursor: pointer;
}
</style>

3.3、父组件

<!-- 本组件为日历组件的父组件 -->
<template>
    <div class="big-box">
        <!-- 左边的日历,选天数 -->
        <div id="left-box">
            <!-- 头部 -->
            <div class="ri-head" style="cursor: pointer;">
                <p style="color: #b2ebf2;padding-top: 15px;font-size: 16px;" @click="showYear()">{{ year }}年</p>
                <p>{{ month }}月{{ day }}日 星期{{ week }}</p>
            </div>
            <!-- 天数的容器 -->
            <div class="ri-box">
                <!-- 左右切换月份 -->
                <div class="riBox-head">
                    <!-- 点击这个为上个月 -->
                    <svg t="1667840633451" @click="nextorshang(1)" class="icon" viewBox="0 0 1024 1024"
                        version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5160" width="20" height="20">
                        <path
                            d="M677.391515 873.916768c-7.86101 0-15.618586-2.999596-21.617778-8.895354L324.473535 533.721212c-11.998384-11.894949-11.998384-31.340606 0-43.235555L655.670303 159.288889c5.999192-5.999192 13.756768-8.895354 21.617778-8.895354 7.757576 0 15.618586 2.999596 21.617778 8.895354 11.894949 11.894949 11.894949 31.237172 0 43.235555L389.223434 512.103434 698.905859 821.785859c11.894949 11.998384 11.894949 31.340606 0 43.235555-5.895758 5.895758-13.756768 8.895354-21.514344 8.895354z m0 0"
                            fill="#2c2c2c" p-id="5161"></path>
                    </svg>
                    <!-- 日期 -->
                    <span>{{ year }}年{{ month }}月</span>
                    <!-- 点击这个为下个月 -->
                    <svg t="1667840592593" @click="nextorshang(0)" class="icon" viewBox="0 0 1024 1024"
                        version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4143" width="20" height="20">
                        <path
                            d="M307.6 104.6c-14.2 14.2-14.2 37.2 0 51.4L655 503.4c2.8 2.9 2.8 7.5 0 10.3L307.6 861.2c-14.2 14.2-14.2 37.2 0 51.4 14.2 14.2 37.2 14.2 51.4 0l347.4-347.4c15.6-15.6 23.4-36 23.4-56.5s-7.8-41-23.4-56.5L359 104.6c-14.2-14.2-37.2-14.2-51.4 0z"
                            p-id="4144" fill="#2c2c2c"></path>
                    </svg>
                </div>
                <!-- 星期 -->
                <div class="riBox-mian">
                    <span>一</span>
                    <span>二</span>
                    <span>三</span>
                    <span>四</span>
                    <span>五</span>
                    <span>六</span>
                    <span>日</span>
                </div>
                <!-- 天数 -->
                <div v-for="item in list" :key="item.i2" style="cursor: pointer;">
                    <!-- 子组件:点击某一天的时候,切换过去,注意添加类,条件为:当前天等于循环过来的某一天 -->
                    <Ri @click="clcikhmrcs(item.day)" :day="item.day" class="ri-st"
                        :class="{ day_active: day == item.day }"></Ri>
                </div>
            </div>
        </div>

        <!-- 右边 -->
        <div id="right-box" style="display: none;margin-left: 50px;">
            <div class="ri-head" style="cursor: pointer;">
                <p style="color: #fff;padding-top: 15px;font-size: 16px;">{{ year }}年</p>
                <p style="color: #aae9f1;">{{ month }}月{{ day }}日 星期{{ week }}</p>
            </div>
            <div class="ri-box">
                <!-- 子组件 -->
                <Ri_year>
                    <!-- 包含了一个容器,负责装年份的数据 -->
                    <!-- 以下用了具名插槽#cc(与子组件的name必须一致)接受数据 -->
                    <template #cc="{ data }">
                        <div :class="{ year_active: year == data.year }" @click="clickYear(data.year)">
                            {{ data.year }}
                        </div>
                    </template>
                </Ri_year>
            </div>
        </div>

    </div>
</template>
<script lang="ts">
// 导入子组件
import Ri from "../components/3.1 日历.vue";
import Ri_year from "../components/3.2 日历选年份.vue";
import { reactive, toRefs } from 'vue'
export default {
    // 注册组件
    components: {
        Ri,
        Ri_year
    },
    setup() {
        // arr数组负责把获取到的星期数(阿拉伯数字)转为大写
        const arr = ["日", "一", "二", "三", "四", "五", "六"];
        
        // obj数组负责存储所有的天数、年份、月份、某天的数、星期数
        const obj: any = reactive({ list: [], year: "", month: 0, day: "", week: "" })
        
        // createyueday方法是根据传入的年份和月份判断该年份是闰年还是平年
        function createyueday(n: any, yue: any) {
            if ((n % 4 == 0 && !(n % 100 == 0)) || n % 400 == 0) {
                days('闰年', yue)
                // console.log('闰年')
            } else {
                days('平年', yue)
                // console.log('平年')
            }
            // 把年份和月份重新赋值给obj对象中的月份和年份,用于在页面上显示
            obj.year = n;
            obj.month = yue;
        }

        // days方法主要是用于判断闰年和平年中的2月份的天数,需要传入闰年或平年、月份两个参数
        function days(run: any, yue: any) {
            // 定义一个空数组,用于存储循环过后的所有天数
            let arr: any = []
            // 然后再加以判断,如果是闰年中的2月份
            if (yue == 2 && run == '闰年') {
                // 闰年的2月有29天,然后接收xunhuanday方法传过来的数组赋值给arr这个空数组里
                obj.list = xunhuanday(29, arr)
            } else if (yue == 2 && run == '平年') { // 平年中的2月份
                // 有28天
                obj.list = xunhuanday(28, arr)
            } else if (yue == 4 || yue == 6 || yue == 9 || yue == 11) { // 小月份
                // 只有30天
                obj.list = xunhuanday(30, arr)
            } else { // 大月份
                // 有31天
                obj.list = xunhuanday(31, arr)
            }
        }

        // xunhuanday用于接收具体的一个月结束天数进行循环,把一个月具体的多少天存储到数组里面
        function xunhuanday(index: any, arr: any) {
            // 例如是闰年的二月份,传进来的就是29,所以从0开始,到29结束
            for (let i = 0; i < index; i++) {
                // 添加数据到arr数组里面,把天数存储进去
                arr.push({ day: i + 1 })
            }
            // 最后返回数组
            return arr
        }

        // nextorshang方法用于切换月份,如果i2传进来的是1,就代表是上个月,传进来的是0,就代表下个月
        function nextorshang(i2: any) {
            if (i2 == 1) { // 下个月
                // 月份-1
                obj.month-- 

                // 一直减,如果月份小于1月
                if (obj.month < 1) {
                    // 就进去上一年了,所以年份需要-1
                    obj.year--
                    // 重新赋值年份,然后月份默认为上一年的12月
                    createyueday(obj.year, 12);
                }

                // 如果月份没有小于1月的话,就直接输出该年份和该月份
                createyueday(obj.year, obj.month)

            } else { // 下一个
                // 月份+1
                obj.month++
                // 同理,如果月份大于12月就进入下一年
                if (obj.month > 12) {
                    // 年份+1
                    obj.year++
                    // 重新赋值,下一年的话,默认月份为1月
                    createyueday(obj.year, 1);
                }

                // 不大于12月就输出该年份和该月份
                createyueday(obj.year, obj.month)
            }

            // 无论点击上个月还是下个月,切换之后的天数就默认为该月的1号
            obj.day = 1;
            // 星期数也是需要指定当前的年月日来获取星期数,所以把当前obj的年月日传进去
            obj.week = arr[new Date(obj.year + "-" + obj.month + "-" + 1).getDay()];
            console.log(obj.week);

            // 获取到的星期日为0,所以需要重新赋值星期数为7
            if (obj.week == 0) {
                obj.week = 7;
            }

        }

        // clcikhmrcs方法用于点击某一天时查看该天的具体日期数,传入具体的哪一天
        function clcikhmrcs(day: any) {
            // obj数组的天数重新赋值,改变当前天数
            obj.day = day;
            // 星期数也需要重新赋值,重新传天数进去
            obj.week = arr[new Date(obj.year + "-" + obj.month + "-" + day).getDay()];
        }

        // 点击头部中的年份显示选择年份的日历,把显示天数的日历隐藏
        function showYear() {
            // <HTMLDivElement>:ts的写法,用于操作dom元素时使用,否则会报错
            (<HTMLDivElement>document.getElementById("right-box")).style.display = "block";
            (<HTMLDivElement>document.getElementById("left-box")).style.display = "none";
        }

        // clickYear方法用于选择年份来查看具体的日期,传入选择的某一年份
        function clickYear(year: any) {
            console.log(year);
            // 把obj数组中的年份重新赋值
            obj.year = year;
            // 把重新赋值后的年份,和默认1月的数据渲染到页面上
            createyueday(obj.year, 1);
            // 天数也是默认1号
            obj.day = 1;
            // 重新传入年月日查询星期数
            obj.week = arr[new Date(obj.year + "-" + obj.month + "-" + 1).getDay()];
            // 选择完年份后,隐藏选择年份的日历,显示天数的日历出现
            (<HTMLDivElement>document.getElementById("right-box")).style.display = "none";
            (<HTMLDivElement>document.getElementById("left-box")).style.display = "block";
        }
       
        // 初始化日历,页面加载时,就显示以下的数据
        // 显示当前的年份、月份、天数、星期数
        createyueday(new Date().getFullYear(), new Date().getMonth() + 1)
        obj.day = new Date().getDate()
        obj.week = arr[new Date().getDay()];

        // 暴露出去
        return {
            nextorshang,
            ...toRefs(obj),
            clcikhmrcs,
            clickYear,
            showYear

        }
    }
}
</script>
<style >
.big-box {
    width: 750px;
    padding: 20px;
    /* border: 1px solid navy; */
    margin: auto;
    display: flex;
    justify-content: space-evenly;
}

.ri-head {
    width: 360px;
    height: 80px;
    margin: auto;
    background-color: #00bcd4;
    color: #fff;

}

.ri-head p {
    text-align: left;
    margin-left: 20px;
    font-size: 25px;
    margin-top: -10px;
}

.ri-box {
    display: flex;
    flex-wrap: wrap;
    width: 358px;
    border: 1px solid #eee;
    margin: auto;
    padding-bottom: 20px;
}

.ri-st {
    width: 30px;
    height: 30px;
    line-height: 30px;
    text-align: center;
    margin: 10px;

}

.riBox-head,
.riBox-mian {
    width: 100%;
    display: flex;
    justify-content: space-between;
    margin: auto;
    margin-top: 20px;
    padding: 0px 20px;
    /* border: 1px solid red; */
}

.riBox-mian {
    font-size: 14px;
    color: #909090;
    margin-left: -2px;
    justify-content: space-around;
    padding: 0px;
    margin-bottom: 10px;
}

.day_active {
    background-color: #00bcd4;
    color: #fff;
    border-radius: 15px;
}

.year_active {
    color: #fff;
    background-color: #00bcd4;
}
</style>

3.4、运行结果

结果1:展示日期

 

 结果2:选择年份,展示该年份的日期

四、使用动态插槽完成一个选项卡,点击卡片名称时动态切换。

4.1、子组件A(定义选项卡的数据)

<!-- 本组件用于:向父组件传值 -->
<template lang="">
    <!-- 遍历三个数组对象 -->
    <div v-for="(item,i) in list">
        <!-- slot标签相当于在子组件里面挖一个坑,到父组件里填上 -->
        <!-- 在这里需要定义一个名称,然后把遍历出来的数据传到父组件里面 -->
        <slot name="s1" :data="item"></slot>
    </div>
    <div v-for="(item,i) in list2">
        <slot name="s2" :data="item"></slot>
    </div>
    <div v-for="(item,i) in list3">
        <slot name="s3" :data="item"></slot>
    </div>
</template>
<script lang="ts">
import { reactive } from 'vue'
export default {
    setup() {
        // 定义选项卡的数据
        const list = reactive([
            { name: 'vue1' },
            { name: 'vue1' },
            { name: 'vue1' }
        ])
        const list2 = reactive([
            { name: 'vue2' },
            { name: 'vue2' },
            { name: 'vue2' }
        ])
        const list3 = reactive([
            { name: 'vue3' },
            { name: 'vue3' },
            { name: 'vue3' }
        ])
        // 暴露出去
        return {
            list,
            list2,
            list3
        }
    }
}
</script>
<style scoped>

</style>

4.2、父组件

<template lang="">
    <div class="big">
        <!-- 选项卡按钮 -->
        <!-- 点击按钮时,修改变量s的值,目的是:让s的值变为子组件那边所需要的值,看用户需要哪个数据,就点击哪个按钮,达到选项卡的效果 -->
        <button style="margin-left: 0px;" @click="s = 's1'"  :class="{active: s === 's1'}">vue1</button>
        <button @click="s = 's2'" :class="{active: s === 's2'}">vue2</button>
        <button @click="s = 's3'" :class="{active: s === 's3'}">vue3</button>
        <div class="big_box">
            <!-- 导入子组件 -->
            <Ez >
                <!-- # 相当于 v-slot -->
                <!-- 这个为作用域插槽 -->
                <!-- [s]定义的目的就是为了让具名发生变化,如果为s1,就传子组件那边对应的name -->
                <!-- 然后在这里接受子组件传过来的数据 -->
                <template #[s]="{data}">
                    <div class="item-box">
                        <!-- 输出数据到页面上 -->
                        {{data.name}}
                    </div>
                </template>
            </Ez>
        </div>
    </div>
</template>
<script lang="ts">
import Ez from "../components/4.1 动态插槽之儿子组件.vue";
import { ref }  from 'vue'
export default {
    components: {
        Ez 
    },
    setup() {
        let s = ref('s1')
        
       return {
        s
       }
    }
}
</script>
<style scoped>
.big{
    width: 350px;
    height: 390px;
    background-color: #636d76;
    margin: auto;
}    
button{
    width: 100px;
    height: 40px;
    background-color: #474b54;
    color: #ece6b2;
    border: none;
    margin-left: 10px;
    margin-top: 10px;
}
.big_box{
    width: 91.3%;
    height: 330px;
    background-color: #fff;
    margin: auto;
}

.item-box {
    width: 80%;
    padding: 10px;
    margin: auto;
    border-bottom: 1px solid #d7dee1;
}
.active{
    background-color: #fff;
    color: #333;
}
</style>

4.3、运行结果:

 

 五、使用动态组件完成一个选项卡,定义3个不同的组件,点击卡片名称时动态切换。

5.1、子组件A(第一页)

<template>
    <h3>测试KeepAlive组件强制被切换掉的组件仍然保持“存活”的状态</h3>
    <h1>{{s}}</h1>
    <h2>{{s}}</h2>
    <h3>{{s}}</h3>
    <hr>
    <button @click="s++">点我+1哦</button>
</template>
<script lang="ts">
import {ref} from 'vue'
export default {
    // 组件里的name属性
    name: 'a',
    setup () {
        const s = ref(1)
        return {
            s
        }
    }
}
</script>
<style scoped>
h1,h2,h3{
    background-color: #e2e2e2;
    color: #777;
    width: 90%;
    margin: auto;
    margin-top: 20px;
}
  
</style>

5.2、子组件B(第二页)

<template>
    <h3>测试KeepAlive组件强制被切换掉的组件仍然保持“存活”的状态</h3>
    <h1>{{s}}</h1>
    <h2>{{s}}</h2>
    <h3>{{s}}</h3>
    <hr>
    <button @click="s++">点我+1哦</button>
</template>
<script lang="ts">
import {ref} from 'vue'
export default {
    // 组件里的name属性
    name: 'b',
    setup () {
        const s = ref(10)
        return {
            s
        }
    }
}
</script>
<style scoped>
h1,h2,h3{
    background-color: #e2e2e2;
    color: #777;
    width: 90%;
    margin: auto;
    margin-top: 20px;
}
  
</style>

5.3、子组件C(第三页)

<template>
    <h3>测试KeepAlive组件强制被切换掉的组件仍然保持“存活”的状态</h3>
    <h1>{{s}}</h1>
    <h2>{{s}}</h2>
    <h3>{{s}}</h3>
    <hr>
    <button @click="s++">点我+1哦</button>
</template>
<script lang="ts">
import {ref} from 'vue'
export default {
    // 组件里的name属性
    name: 'c',
    setup () {
        const s = ref(100);
        return {
            s
        }
    }
}
</script>
<style scoped>
h1,h2,h3{
    background-color: #e2e2e2;
    color: #777;
    width: 90%;
    margin: auto;
    margin-top: 20px;
}
  
</style>

5.4、父组件

<template >
    <div class="box">
        <div class="box-head">
            <div class="bh-min" style="border-radius: 0px 0px 0px 20px;"  @click="componentId='A'" :class="{active: componentId === 'A'}">
                <svg t="1667842015073" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6350" width="20" height="20"><path d="M527.247 204.247c113.891 0 227.885 0.717 341.777-0.307 60.373-0.511 88.923 25.48 88.31 87.491-1.434 139.474 0.408 278.947-0.82 418.42-0.716 78.18-31.926 107.241-111.333 107.343-218.573 0.307-437.044 0.307-655.618-0.204-77.667-0.205-124.84-46.764-125.25-123.613-0.716-125.557-0.511-251.114-0.204-376.568 0.204-81.454 30.289-111.845 114.403-112.664 116.245-1.126 232.49-0.307 348.735-0.307v0.41z m-9.21 558.611c109.287 0 218.676-1.33 327.963 0.614 41.443 0.716 58.225-12.382 57.304-55.667-2.252-104.58-0.307-209.364-1.33-314.045-0.103-17.09 9.414-40.625-12.485-49.834-19.954-8.391-32.642 10.13-47.07 21.693-96.906 77.36-195.653 152.572-291.023 231.774-31.517 26.196-51.573 25.275-82.272-0.102-97.11-80.533-197.084-157.484-295.012-237.095-13.098-10.642-25.378-23.74-41.648-19.136-21.08 6.038-13.2 27.22-13.303 42.057-0.716 99.975 1.33 200.052-0.716 300.027-1.126 56.485 23.536 80.328 78.69 79.816 106.831-0.818 213.867-0.204 320.902-0.102zM139.525 262.677c19.851 18.317 25.991 24.763 32.95 30.29 60.885 48.298 121.259 97.211 182.86 144.384 159.837 122.488 160.246 122.488 316.707-2.148 66.41-52.904 131.492-107.343 211.205-172.628-255.411 0.102-490.562 0.102-743.722 0.102z" fill="#7061a7" p-id="6351"></path></svg>
                <span>选项卡 1</span>
            </div>    
            <div class="bh-min" @click="componentId='B'" :class="{active: componentId === 'B'}">
                <svg t="1667842222005" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7505" width="20" height="20"><path d="M1016.832 606.208q2.048 12.288-1.024 29.696t-10.24 35.328-17.408 32.256-22.528 20.48-21.504 6.144-20.48-4.096q-10.24-3.072-25.6-5.632t-31.232-1.024-31.744 6.656-27.136 17.408q-24.576 25.6-28.672 58.368t9.216 62.464q10.24 20.48-3.072 40.96-6.144 8.192-19.456 16.896t-29.184 15.872-33.28 11.264-30.72 4.096q-9.216 0-17.408-7.168t-11.264-15.36l-1.024 0q-11.264-31.744-38.4-54.784t-62.976-23.04q-34.816 0-62.976 23.04t-39.424 53.76q-5.12 12.288-15.36 17.92t-22.528 5.632q-14.336 0-32.256-5.12t-35.84-12.8-32.256-17.92-21.504-20.48q-5.12-7.168-5.632-16.896t7.68-27.136q11.264-23.552 8.704-53.76t-26.112-55.808q-14.336-15.36-34.816-19.968t-38.912-3.584q-21.504 1.024-44.032 8.192-14.336 4.096-28.672-2.048-11.264-4.096-20.992-18.944t-17.408-32.768-11.776-36.864-2.048-31.232q3.072-22.528 20.48-28.672 30.72-12.288 55.296-40.448t24.576-62.976q0-35.84-24.576-62.464t-55.296-38.912q-9.216-3.072-15.36-14.848t-6.144-24.064q0-13.312 4.096-29.696t10.752-31.744 15.36-28.16 18.944-18.944q8.192-5.12 15.872-4.096t16.896 4.096q30.72 12.288 64 7.68t58.88-29.184q12.288-12.288 17.92-30.208t7.168-35.328 0-31.744-2.56-20.48q-2.048-6.144-3.584-14.336t1.536-14.336q6.144-14.336 22.016-25.088t34.304-17.92 35.84-10.752 27.648-3.584q13.312 0 20.992 8.704t10.752 17.92q11.264 27.648 36.864 48.64t60.416 20.992q35.84 0 63.488-19.968t38.912-50.688q4.096-8.192 12.8-16.896t17.92-8.704q14.336 0 31.232 4.096t33.28 11.264 30.208 18.432 22.016 24.576q5.12 8.192 3.072 17.92t-4.096 13.824q-13.312 29.696-8.192 62.464t29.696 57.344 60.416 27.136 66.56-11.776q8.192-5.12 19.968-4.096t19.968 9.216q15.36 14.336 27.136 43.52t15.872 58.88q2.048 17.408-5.632 27.136t-15.872 12.8q-31.744 11.264-54.272 39.424t-22.528 64q0 34.816 18.944 60.928t49.664 37.376q7.168 4.096 12.288 8.192 11.264 9.216 15.36 23.552zM540.672 698.368q46.08 0 87.04-17.408t71.168-48.128 47.616-71.168 17.408-86.528-17.408-86.528-47.616-70.656-71.168-47.616-87.04-17.408-86.528 17.408-70.656 47.616-47.616 70.656-17.408 86.528 17.408 86.528 47.616 71.168 70.656 48.128 86.528 17.408z" p-id="7506" fill="#7061a7"></path></svg>
                <span>选项卡 2</span>
            </div>    
            <div class="bh-min" @click="componentId='C'" :class="{active: componentId === 'C'}">
                <svg t="1667842294039" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8570" width="20" height="20"><path d="M639.892491 415.930119 383.935495 415.930119c-17.717453 0-31.994625-14.277171-31.994625-31.994625s14.277171-31.994625 31.994625-31.994625L639.892491 351.94087c17.717453 0 31.994625 14.277171 31.994625 31.994625S657.609945 415.930119 639.892491 415.930119z" p-id="8571" fill="#7061a7"></path><path d="M579.17151 543.908618 383.935495 543.908618c-17.717453 0-31.994625-14.277171-31.994625-31.994625S366.390055 479.919368 383.935495 479.919368l195.236015 0c17.717453 0 31.994625 14.277171 31.994625 31.994625S596.888964 543.908618 579.17151 543.908618z" p-id="8572" fill="#7061a7"></path><path d="M962.246934 447.924744c0-211.74937-200.912481-383.935495-447.924744-383.935495S66.225433 236.175374 66.225433 447.924744c0 116.453553 62.957164 226.026541 172.874181 300.680665 14.621199 9.976818 34.574836 6.192508 44.379641-8.428691 9.976818-14.621199 6.192508-34.574836-8.428691-44.379641-92.027549-62.441122-144.835881-152.74853-144.835881-247.700319 0-176.486477 172.186125-319.946246 383.935495-319.946246s383.935495 143.631782 383.935495 319.946246-172.186125 319.946246-383.935495 319.946246c-2.064169 0-3.612296 0.688056-5.504452 1.204099-15.137242-2.752226-30.446498 5.160423-35.778935 20.125651-6.192508 17.373425-46.44381 46.615824-94.091718 73.794053 17.373425-58.140769 9.116748-70.697799 3.440282-78.954477-6.70855-9.976818-17.889467-15.997312-29.930455-15.997312-17.717453 0-31.994625 14.277171-31.994625 31.994625 0 5.84848 1.548127 11.180917 4.300353 15.997312-3.268268 18.233496-17.201411 60.892995-33.026709 99.768184-4.988409 12.040988-2.064169 25.974131 7.396607 35.090879 6.020494 5.84848 14.105157 8.944734 22.18982 8.944734 4.300353 0 8.77272-0.860071 13.073072-2.752226 36.466991-16.341341 147.588107-69.149672 187.667395-125.570301C765.290778 828.075928 962.246934 657.609945 962.246934 447.924744z" p-id="8573" fill="#7061a7"></path></svg>
                <span>选项卡 3</span>
            </div>    
        </div>
        <div class="box-mian">
            <!-- KeepAlive: (接地气说法)阻止组件卸载,从a页面 切换到b页面 但是a页面输入或保存的的数据不会被销毁--> 
            <!-- 可以让强制被切换掉的组件仍然保持“存活”的状态 -->
            <!-- 包含(include)/排除(exclude) -->
            <!-- 根据组件的 name 选项进行匹配,所以组件如果想要条件性地被 KeepAlive 缓存,就必须显式声明一个 name 选项。 -->
            <!-- :is -->
            <!-- 被传给 :is 的值可以是以下几种:
                    被注册的组件名
                    导入的组件对象 -->
            <KeepAlive include="a,b">
                <component :is="components[componentId]"></component>
            </KeepAlive>
        </div>
    </div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import A from '../components/5.1 动态组件之A组件.vue';
import B from '../components/5.2 动态组件之B组件.vue';
import C from '../components/5.3 动态组件之C组件.vue';

    let components:any = {
        A,B,C
    }

    const componentId = ref("A");

</script>
<style scoped>
.box{
    width: 45%;
    height: 250px;
    border-bottom: 3px solid #73679e;
    margin: auto;
}    
.box-head{
    width: 100%;
    height: 50px;
    display: flex;
    color: #333;
    background-color: #eaeaea;
    border-radius: 0px 20px 0px 20px;
}

.bh-min{
    width: 20%;
    height: 100%;
    /* border: 1px solid rebeccapurple; */

}
.bh-min svg{
    vertical-align: middle;
}
.bh-min span{
    vertical-align: middle;
    margin-left: 10px;
    line-height: 50px;
}

.active{
    fill:#fff;
    color: #fff;
    background-color: #795da6;
}
.active svg path{
    fill: #fff;
}

.box-mian{
    width: 100%;
    height: 200px;
    /* border: 1px solid lawngreen; */
}

</style>

5.5、运行结果

标签:练习题,obj,year,list,height,源码,Vue3,组件,margin
From: https://www.cnblogs.com/fairya/p/16873852.html

相关文章

  • vue3父组件调用子组件中的方法
    子组件中<scriptsetup>functionqueryOrder(){...代码省略}//使用<scriptsetup>的组件是默认关闭的——即通过模板引用或者$parent链获取到的组件的公开实......
  • C#万年历源码参考
    C#万年历源码参考万年历Class源码:   ///中国日历异常处理---注意,该异常处理必须是在Class之外,   publicclassChineseCalendarException:System.Exception ......
  • 在线直播系统源码,数组遍历提取,订单效果
    在线直播系统源码,数组遍历提取,订单效果 <!DOCTYPEhtml><html> <head>  <metacharset="UTF-8">  <metahttp-equiv="X-UA-Compatible"content="IE=edge"> ......
  • 直播平台源码,vue 写搜索效果
    直播平台源码,vue写搜索效果代码如下 <!DOCTYPEhtml><html><head><metacharset="utf-8"><title></title></head><body><divid="app"><h1>搜索水果</h1><inputtype=......
  • electron vue3 项目搭建
    electronvue3项目搭建一.vue项目搭建安装electron需要搭建vue项目,这里用的vue3项目。1.安装下载node 这里用的16版本https://nodejs.org/zh-cn/2.设置淘宝镜像......
  • vue3 获取组件实例 $ref方法踩坑 getCurrentInstance()
    ps:我使用的是element-plus框架,有一个需求是级联选择器,选择后需要获取最后的id和label一起传递给后端。获取id比较简单在change事件中使用value.pop()就可以获得但是获取......
  • Vue3+Vite简单使用
    前言Vue3大势不可阻挡,与之而来的就是Vite,尤雨溪极力推荐的前端开发与构建工具vue3原生支持TS,所以TS语法和vue3TS语法学起来vue中的vuex状态管理也用不顺手,看不顺......
  • 【Vue3】本地没问题,部署后 public 下的某些资源 404 不能访问
    如果你本地没问题,线上访问出现404,你得看看你public下面得资源文件夹命名是不是和.gitignore下得配置文件冲突了,我就是命名为dist导致直接被忽略了,重新改了个名字后......
  • vue源码分析-基础的数据代理检测
    简单回顾一下这个系列的前两节,前两节花了大量的篇幅介绍了Vue的选项合并,选项合并是Vue实例初始化的开始,Vue为开发者提供了丰富的选项配置,而每个选项都严格规定了合并的策......
  • vue源码分析-挂载流程和模板编译
    前面几节我们从newVue创建实例开始,介绍了创建实例时执行初始化流程中的重要两步,配置选项的资源合并,以及响应式系统的核心思想,数据代理。在合并章节,我们对Vue丰富的选项......