前言
本章主要讲解第十四届蓝桥杯(Web 应用开发)模拟赛 3 期-职业院校组中的题目,接着往下看吧👇👇👇
文章目录
目录
前言
文章目录
第一题:网页ppt
第二题:西游记之西天取经
第三题:商品销量和销售额实时展示
第四题:蓝桥校园一卡通
第五题:会员权益领取中心
第六题:心愿便利贴
第七题:消失的 Token
第八题:趣购
第九题:分页组件
第十题:虚拟滚动列表
结语
第一题:网页ppt
题目要求:单击按钮实现页面切换。
考点:jquery的基本用法
解题思路:
①题目中已经给左右按钮都绑定了单击事件,在事件里共同调用了一个函数switchPage。所以switchPage函数就是用来实现页面切换功能的。且发现按钮禁用主要是靠disable
类。页面隐藏是style.display设置为none的原因。activeIndex属性就是当前页面的下标值(根据左右按钮里的单击事件里的内容可得)。
②首先我们将所有的页面全隐藏,然后将按钮设为全部可见状态。
③然后将下标为activeIndex的页面设置成可见状态。
④判断如果activeIndex为0也就是达到下标左边边界了,则把左按钮置为隐藏。如果activeIndex为4也就是达到下标右边边界了,则把右按钮置为隐藏。
⑤最后还要处理页码,格式为 "当前页码 / 总页码"
。
function switchPage() {
// TODO: 请补充该函数,实现根据activeIndex切换页面的功能,并且在到达最后一页或第一页时给相应的按钮添加disable类
Array.from(document.querySelectorAll('section')).forEach(e => e.style.display = 'none');
$('.left').removeClass('disable')//删除类
$('.right').removeClass('disable')
//显示当前页面
document.querySelectorAll('section')[activeIndex].style.display = 'block';
//处理页码
$('.page').text(`${activeIndex+1} / 5`)
//判断是否为下标边界
if(activeIndex===0){
$('.left').addClass('disable')//增加类
}
if(activeIndex===4){
$('.right').addClass('disable')
}
}
第二题:西游记之西天取经
题目要求:让动画无限循环。
考点:css动画animation
解题思路:
观察页面效果发现动画只走了八下,打开css发现animation: a1 0.8s steps(8)
并没有设置持续事件。所以现在默认是一次,我们将持续事件设置为infinite无限次就可以了。
.actor:first-child {
width: 200px;
height: 180px;
background: url(../images/west_01.png) no-repeat;
/* TODO 填空 */
animation: a1 0.8s steps(8) infinite;
}
.actor:nth-child(2) {
width: 200px;
height: 180px;
background: url(../images/west_02.png) no-repeat;
/* TODO 填空 */
animation: a2 0.8s steps(8) infinite;
}
.actor:nth-child(3) {
width: 170px;
height: 240px;
background: url(../images/west_03.png) no-repeat;
/* TODO 填空 */
animation: a3 0.8s steps(8) infinite;
}
.actor:last-child {
width: 210px;
height: 200px;
background: url(../images/west_04.png) no-repeat;
/* TODO 填空 */
animation: a4 0.8s steps(8) infinite;
}
第三题:商品销量和销售额实时展示
题目要求:达到如下效果
考点:js基础知识、echarts
解题思路:
①第一题补全 yAxis
的设置完全是送分的,直接告诉你写哪,写什么了。
②第二题首先要想的就是数据在哪?观察js发现里面封装了一个接口,在renderChart函数里还调用了这个接口。console.log(result)发现就是我们需要的数据。
③处理数据。处理成想要的样子。再将值赋值给charData中对应的值就可以了。
// 指定图表的配置项和数据
const charData = {
title: {
text: '云课课程销量和销售额看板',
width: 100,
height: 50,
textStyle: {
lineHeight: 50,
},
left: 'center',
},
grid: {
top: 80,
},
tooltip:{
show: true,
},
xAxis: {
data: [],
},
// TODO:补全 `yAxis` 的设置,要求“销售额”(即,配置项 `name`)的位置(即,配置项 `position`)在图表的左侧,“销量”(即,配置项 `name`)的位置(即,配置项 `position`)在图表的右侧。
yAxis: [{
type: 'value',
name: '销售额',
position: '',
},
{
type: 'value',
name: '销量',
position: '',
}],
series: [
{
name: '销售额',
type: 'line',
data: [],
yAxisIndex: 0,
smooth: true
},
{
name: '销量',
type: 'bar',
data: [],
yAxisIndex: 1,
smooth: true
}
]
};
……………………
async function renderChart() {
const result = await Ajax();
document.querySelector("#result").innerText = JSON.stringify(result);
const myChart = echarts.init(document.getElementById('main'));
// TODO:补全代码,正确给 X 轴的时间,以及 Y 轴的商品的销售额 saleObj 和销量赋值 countObj。
const data=result.data
let countdata={x:[],y:[]}
let saledata={x:[],y:[]}
countdata.x=Object.keys(data.countObj)
countdata.y=Object.values(data.countObj)
saledata.x=Object.keys(data.saleObj)
saledata.y=Object.values(data.saleObj)
charData.xAxis.data=countdata.x
charData.series[0].data=saledata.y
charData.series[1].data=countdata.y
myChart.setOption(charData, true);
document.querySelector("#data").innerText = JSON.stringify(charData);
}
renderChart();
let times = 0;
let timer = setInterval(() => {
renderChart();
++times == 6 && clearInterval(timer);
}, 2000)
第四题:蓝桥校园一卡通
题目要求:完成表单验证。
考点:js基础知识
解题思路:
①根据题目要求
②利用正则表达式的text属性判断文本框值是否符合要求。如果符合要求那么就将提示(不符合要求)的文字隐藏。如果不符合要求,那么提示文字显示,且为表单项(class=form-group
)添加类 class=has-error
。不在继续下面的代码了。这是为了防止最后那条代码运行。
③当然再最开始的时候将表单项(class=form-group
身上的类class=has-error全删除了。
④最后都满足要求那么就正确显示卡片。为卡片附上对应的值。
<script>
// 获取DOM元素对象
const studentName = document.getElementById("studentName"); // 姓名
const studentId = document.getElementById("studentId"); // 学号
const college = document.getElementById("college"); // 学院
const submit = document.getElementById("submit"); // 制卡按钮
const cardStyle = document.getElementById("cardStyle"); // 卡片
const item = document.querySelectorAll(".item") // 卡片项
submit.onclick = () => {
// TODO 待补充代码
let vailName=document.getElementById('vail_name')
let vailstudentId=document.getElementById('vail_studentId')
let groups=document.querySelectorAll('.form-group')
groups[0].classList.remove('has-error')
groups[1].classList.remove('has-error')
if(/^([\u4e00-\u9fa5]{2,4})$/g.test(studentName.value)){
vailName.style.display='none'
}else{
groups[0].classList.add('has-error')
vailName.style.display='block'
return
}
if(/^([0-9]{1,12})$/g.test(studentId.value)){
vailstudentId.style.display='none'
}else{
groups[1].classList.add('has-error')
vailstudentId.style.display='block'
return
}
item[0].innerText=`姓名:${studentName.value}`
item[1].innerText=`学号:${studentId.value}`
item[2].innerText=`学院:${college.value}`
// 添加 showCard 类显示放大一卡通的动画,请勿删除
cardStyle.classList.add('showCard')
}
</script>
第五题:会员权益领取中心
题目要求:页面布局题
解题思路:这题属实有些变态了,也太多了……每个人有每个人的写法吧,仅供参考。这种题,最好留到最后写吧,比较费时间。
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>会员权益领取中心</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div id="Claimcenter">
<header>
<div class="card">
<div>
<div class="card-l"></div>
<div class="card-r">
<ul class="card-ul">
<li>
<span>小兰同学</span>
<span>L57</span>
</li>
<li>
<span>已加入蓝桥云课 300 天</span>
</li>
<li>
<span>会员到期时间:2022-01-06</span>
</li>
</ul>
</div>
</div>
<div>
<span>我的邀请码:</span>
<span>7088DCA</span>
<span><span>邀请好友</span></span>
</div>
</div>
</header>
<main>
<div class="benefitsUpgrade">
<div>
<ul class="benefitsUpgrade-ul">
<li><img src="./images/left.png" alt=""></li>
<li>会员权益升级</li>
<li><img src="./images/right.png" alt=""></li>
</ul>
</div>
<div>
<div class="benefitsUpgrade-t">
<ul class="benefitsUpgrade-t-ul">
<li>
<div class="box1">会员专属特权</div>
<div class="box2">
<img src="./images/gaojivip.png" alt="">
<span>高级会员</span>
</div>
<div class="box3">
<img src="./images/vip.png" alt="">
<span>标准会员</span>
</div>
</li>
<li>
<div>
<img src="./images/icon-jpkc.png" alt="">
<span>50+ 备赛精品课</span>
</div>
<div>
<img src="./images/check.png" alt="">
</div>
<div>享受8折</div>
</li>
<li>
<div>
<img src="./images/icon-xm.png" alt="">
<span>200+ 企业实战项目</span>
</div>
<div>
<img src="./images/check.png" alt="">
</div>
<div>享受8折</div>
</li>
<li>
<div>
<img src="./images/icon-syk.png" alt="">
<span>400+ 编程实验课</span>
</div>
<div>
<img src="./images/check.png" alt="">
</div>
<div>
<img src="./images/check.png" alt="">
</div>
</li>
<li>
<div>
<img src="./images/icon-lj.png" alt="">
<span>7大热招岗位学习路径</span>
</div>
<div>
<img src="./images/check.png" alt="">
</div>
<div>仅基础阶段可学</div>
</li>
<li>
<div>
<img src="./images/icon-hj.png" alt="">
<span>保存 2 个开发环境</span>
</div>
<div>
<img src="./images/check.png" alt="">
</div>
<div>
<img src="./images/check.png" alt="">
</div>
</li>
<li>
<div>
<img src="./images/iconmb.png" alt="">
<span>50+ IT 行业经典简历模板</span>
</div>
<div>
<img src="./images/check.png" alt="">
</div>
<div>
<img src="./images/wu.png" alt="">
</div>
</li>
<li>
<div>
<img src="./images/icon-zdk.png" alt="">
<span>20讲简历面试指导课</span>
</div>
<div>
<img src="./images/check.png" alt="">
</div>
<div>
<img src="./images/wu.png" alt="">
</div>
</li>
<li>
<div>
<img src="./images/icon-mnms.png" alt="">
<span>模拟面试 1 次</span>
</div>
<div>
<img src="./images/check.png" alt="">
</div>
<div>
<img src="./images/wu.png" alt="">
</div>
</li>
<li>
<div>
<img src="./images/icon-tj.png" alt="">
<span>实习推荐 1 次</span>
</div>
<div>
<img src="./images/check.png" alt="">
</div>
<div>
<img src="./images/wu.png" alt="">
</div>
</li>
<li>
<div>
<img src="./images/icon-more.png" alt="">
<span>更多精选内容</span>
</div>
<div>
<img src="./images/check.png" alt="">
</div>
<div>
<img src="./images/wu.png" alt="">
</div>
</li>
</ul>
</div>
<div class="benefitsUpgrade-b">
<div class="top">
<div>每日仅2.1元</div>
<div>高级会员</div>
<div>标准会员</div>
</div>
<div class="order">订单详情</div>
<div class="open">开通时常</div>
<div class="open-year">
<div class="active">
<div>赠送30天</div>
<div>1年</div>
<img style="display: block;" src="./images/xiaogougou.png" alt="">
</div>
<div class="year">
<div>赠送30天</div>
<div>2年</div>
<img style="display: none;" src="./images/xiaogougou.png" alt="">
</div>
</div>
<!-- 清除浮动 -->
<div style="clear: both;"></div>
<div class="Preferential">
<div>
<img src="./images/xuanzhong1.png" alt="">
<span>优惠券</span>
</div>
<div>
<span>100元优惠卷</span>
<img src="./images/lujing.png" alt="">
</div>
</div>
<div class="ma">
<img src="./images/weixuanzhong1.png" alt="">
<span>优惠码</span>
</div>
<div class="open">支付方式</div>
<div class="open-year">
<div class="active">
<p><img src="./images/pay.png" alt=""></p>
<img style="display: block;" src="./images/xiaogougou.png" alt="">
</div>
<div class="year">
<p><img src="./images/chat.png" alt=""></p>
<img style="display: none;" src="./images/xiaogougou.png" alt="">
</div>
<div class="year">
<p><img src="./images/huabei.png" alt=""></p>
<img style="display: none;" src="./images/xiaogougou.png" alt="">
</div>
</div>
<!-- 清除浮动 -->
<div style="clear: both;"></div>
<div class="OrderAmount">
<div>订单金额</div>
<div>
<div>¥1299</div>
<div>¥720</div>
</div>
</div>
<div class="login">登入并开通</div>
<div class="checked">
<div><img src="./images/tucenggouxuan.png" alt=""></div>
<div>同意蓝桥云课VIP会员使用协议</div>
</div>
</div>
</div>
</div>
<div class="BoutiqueClass">
<div>
<ul class="BoutiqueClass-ul">
<li><img src="./images/left.png" alt=""></li>
<li class="title">备赛精品课</li>
<li><img src="./images/right.png" alt=""></li>
</ul>
<div>名师备赛精品课,助你一站式夺奖</div>
</div>
<div>
<ul class="BoutiqueClass-ul">
<li><img src="./images/zuoce.png" alt=""></li>
<li><img src="./images/courses1.png" alt=""></li>
<li><img src="./images/courses2.png" alt=""></li>
<li><img src="./images/courses3.png" alt=""></li>
<li><img src="./images/courses4.png" alt=""></li>
<li><img src="./images/youce.png" alt=""></li>
</ul>
</div>
</div>
</main>
</div>
</body>
</html>
/* TODO: 待补充代码*/
*{
margin: 0;
padding: 0;
}
li{
list-style: none;
}
/* header */
header{
width: 1440px;
height: 208px;
background: url('../images/top_banner.png') no-repeat;
background-size: 100% 100%;
margin: 0 auto;
padding-top: 32px;
}
.card{
position: relative;
width: 290px;
height: 176px;
background-image: linear-gradient(180deg, #191720 0%, #080810 100%);
border-radius: 4px;
margin-left: 1000px;
}
.card-l{
position: absolute;
width: 50px;
height: 50px;
background: url('../images/touxiang.png') no-repeat;
background-size: 100% 100%;
top: 41px;
left: 24px;
}
.card-r{
position: absolute;
top: 30px;
left: 94px;
}
.card-ul>li:nth-child(1)>span:nth-child(1){
width: 80px;
height: 20px;
font-family: PingFangSC-Regular;
font-size: 20px;
color: #FFFFFF;
letter-spacing: 0;
line-height: 20px;
font-weight: 400;
}
.card-ul>li:nth-child(1)>span:nth-child(2){
width: 22px;
height: 18px;
font-family: SourceHanSansCN-Heavy;
font-size: 12px;
color: #FFC200;
letter-spacing: 0;
font-weight: 900;
}
.card-ul>li:nth-child(2){
width: 147px;
height: 14px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #FFFFFF;
letter-spacing: 0;
line-height: 14px;
font-weight: 400;
margin-top: 13px;
}
.card-ul>li:nth-child(3){
width: 154px;
height: 12px;
font-family: PingFangSC-Regular;
font-size: 12px;
color: #C6C6C6;
letter-spacing: 0;
line-height: 12px;
font-weight: 400;
margin-top: 12px;
}
.card>div:nth-child(2){
position:absolute;
top: 130px;
left: 24px;
}
.card>div:nth-child(2)>span:nth-child(1){
width: 84px;
height: 14px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #FFFFFF;
letter-spacing: 0;
line-height: 14px;
font-weight: 400;
}
.card>div:nth-child(2)>span:nth-child(2){
width: 63px;
height: 14px;
font-family: PingFangSC-Semibold;
font-size: 14px;
color: #FF9630;
letter-spacing: 0;
line-height: 14px;
font-weight: 600;
}
.card>div:nth-child(2)>span:nth-child(3){
width: 69px;
height: 24px;
background-color: #FF9630;
border-radius: 4px;
margin-left: 27px;
}
.card>div:nth-child(2)>span:nth-child(3)>span{
width: 57px;
height: 20px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #FFFFFF;
letter-spacing: 0.14px;
font-weight: 400;
}
/* main */
main{
width: 1440px;
height: 100%;
margin: 0 auto;
padding-bottom: 48px;
background: #FCFDFF;
}
/* benefitsUpgrade */
.benefitsUpgrade{
padding: 60px 0;
overflow: hidden;
}
.benefitsUpgrade-t{
float: left;
width: 760px;
height: 704px;
margin-left: 150px;
background: #FFFFFF;
box-shadow: 0px 2px 10px 0px rgba(229,235,241,1);
border-radius: 10px;
}
.benefitsUpgrade-t-ul>li{
width: 100%;
height: 64px;
line-height: 64px;
display: flex;
justify-content:space-around;
}
.benefitsUpgrade-t-ul>li:nth-child(even){
background: #F7F8FA;
}
.benefitsUpgrade-t-ul>li>div:nth-child(1){
width: 250px;
}
.benefitsUpgrade-t-ul>li>div:nth-child(1)>img{
width: 42px;
height: 42px;
vertical-align: middle;
}
.benefitsUpgrade-t-ul>li>div:nth-child(1)>span{
width: 100%;
height: 14px;
}
.benefitsUpgrade-t-ul>li>div:nth-child(2){
width: 150px;
text-align: center;
}
.benefitsUpgrade-t-ul>li>div:nth-child(3){
width: 150px;
text-align: center;
}
.box1{
width: 96px;
height: 16px;
font-family: PingFangSC-Medium;
font-size: 16px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.box2>img,
.box3>img{
width: 19px;
height: 19px;
vertical-align: middle;
}
.box2>span,
.box3>span{
width: 64px;
height: 16px;
vertical-align: middle;
margin-left: 8px;
font-family: PingFangSC-Medium;
font-size: 16px;
color: #333333;
letter-spacing: 0;
line-height: 16px;
font-weight: 500;
}
.benefitsUpgrade-ul{
display: flex;
justify-content: center;
align-items: center;
}
.benefitsUpgrade>div:nth-child(2){
margin-top: 40px;
}
.benefitsUpgrade-ul>li:nth-child(2){
width: 180px;
height: 30px;
font-family: PingFangSC-Medium;
font-size: 30px;
color: #232A33;
letter-spacing: 0;
text-align: center;
line-height: 30px;
font-weight: 500;
}
/* benefitsUpgrade-b */
.benefitsUpgrade-b{
float: left;
width: 360px;
height: 704px;
margin-left: 20px;
background: #FFFFFF;
box-shadow: 0px 2px 10px 0px rgba(229,235,241,1);
border-radius: 10px;
}
.benefitsUpgrade-b .top{
position: relative;
width: 360px;
height: 56px;
background: #EEF0F6;
border-radius: 10px 10px 0px 0px;
}
.benefitsUpgrade-b .top>div:nth-child(1){
position:absolute;
width: 80px;
height: 24px;
line-height: 24px;
text-align: center;
background-image: linear-gradient(135deg, #FF5760 0%, #EE0000 100%);
border-radius: 0px 10px 0px 0px;
top: -11px;
font-family: PingFangSC-Regular;
font-size: 12px;
color: #FFFFFF;
letter-spacing: 0;
font-weight: 400;
}
.benefitsUpgrade-b .top>div:nth-child(2){
float: left;
width: 196px;
height: 56px;
line-height: 56px;
background: url('../images/vip-bg.png');
text-align: center;
font-family: PingFangSC-Medium;
font-size: 20px;
color: #F7D8A6;
letter-spacing: 0;
font-weight: 500;
}
.benefitsUpgrade-b .top>div:nth-child(3){
height: 56px;
line-height: 56px;
text-align: center;
font-family: PingFangSC-Medium;
font-size: 20px;
color: #999999;
letter-spacing: 0;
font-weight: 500;
}
.order{
width: 64px;
height: 22px;
line-height: 22px;
margin-top: 32px;
margin-left: 20px;
font-family: PingFangSC-Medium;
font-size: 16px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.open{
width: 56px;
height: 20px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #333333;
letter-spacing: 0;
line-height: 20px;
font-weight: 400;
margin-top: 32px;
margin-left: 20px;
}
.open-year{
margin-top: 23px;
margin-left: 20px;
}
.active{
position: relative;
float: left;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #333333;
letter-spacing: 0;
font-weight: 400;
border-radius: 4px;
border: 1px solid rgba(0,128,246,1);
}
.year{
position: relative;
float: left;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #333333;
letter-spacing: 0;
font-weight: 400;
border: 1px solid rgba(232,232,232,1);
border-radius: 4px;
margin-left: 10px;
}
.open-year>div>p>img{
vertical-align: middle;
}
.open-year>div>div:nth-child(1){
position:absolute;
width: 69px;
height: 23.58px;
line-height: 23.58px;
background: url('../images/bq.png');
text-align: center;
font-family: PingFangSC-Regular;
font-size: 12px;
color: #EE0000;
letter-spacing: 0;
font-weight: 400;
right: 0;
top: -13px;
}
.open-year>div>img{
position:absolute;
bottom: 0;
right: 0;
}
.Preferential{
display: flex;
margin-top: 34px;
margin-left: 20px;
justify-content: space-between;
}
.Preferential>div:nth-child(1)>span{
font-family: PingFangSC-Regular;
font-size: 14px;
color: #333333;
letter-spacing: 0;
line-height: 20px;
font-weight: 400;
vertical-align: middle;
}
.Preferential>div:nth-child(1)>img{
width: 16px;
height: 16px;
vertical-align: middle;
margin-right: 8px;
}
.Preferential>div:nth-child(2){
margin-right: 20px;
}
.Preferential>div:nth-child(2)>span{
font-family: PingFangSC-Regular;
font-size: 14px;
color: #333333;
letter-spacing: 0;
line-height: 20px;
font-weight: 400;
vertical-align: middle;
}
.Preferential>div:nth-child(2)>img{
width: 7px;
height: 10.5px;
vertical-align: middle;
margin-left: 10px;
}
.ma{
margin-top: 36px;
margin-left: 20px;
}
.ma>img{
width: 16px;
height: 16px;
margin-right: 8px;
vertical-align: middle;
}
.ma>span{
font-family: PingFangSC-Regular;
font-size: 14px;
color: #333333;
font-weight: 400;
vertical-align: middle;
}
.OrderAmount{
display: flex;
justify-content:space-between;
margin: 100px 20px 0 20px;
}
.OrderAmount>div{
display: flex;
}
.OrderAmount>div:nth-child(1){
width: 56px;
height: 30px;
line-height: 50px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #333333;
font-weight: 400;
}
.OrderAmount>div:nth-child(2)>div:nth-child(1){
width: 40px;
height: 30px;
line-height: 50px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #898989;
letter-spacing: 0;
text-align: center;
font-weight: 400;
/* 删除线 */
text-decoration-line:line-through;
}
.OrderAmount>div:nth-child(2)>div:nth-child(2){
width: 71px;
height: 30px;
font-family: PingFangSC-Semibold;
font-size: 30px;
color: #EE0000;
letter-spacing: 0;
text-align: center;
font-weight: 600;
}
.login{
width: 320px;
height: 44px;
line-height: 44px;
text-align: center;
background-image: linear-gradient(135deg, #FF5760 0%, #EE0000 100%);
border-radius: 100px;
font-family: PingFangSC-Regular;
font-size: 18px;
color: #FFFFFF;
letter-spacing: 0;
font-weight: 400;
margin: 20px 0 0 20px;
}
.checked>div{
float: left;
}
.checked>div:nth-child(1){
margin: 13px 0 0 20px;
}
.checked>div:nth-child(2){
margin: 11px 0 0 8px;
}
/* BoutiqueClass */
.BoutiqueClass>div:nth-child(1)>div{
width: 300px;
height: 28px;
font-family: PingFangSC-Regular;
font-size: 20px;
color: #898989;
letter-spacing: 0;
text-align: center;
font-weight: 400;
margin: 16px auto 40px auto;
}
.BoutiqueClass-ul{
display: flex;
justify-content: center;
align-items: center;
}
.title{
width: 150px;
height: 30px;
font-family: PingFangSC-Medium;
font-size: 30px;
color: #232A33;
letter-spacing: 0;
line-height: 30px;
font-weight: 500;
}
第六题:心愿便利贴
题目要求:完成验证功能并展示到页面。
考点:elmentui表单组件
解题思路:
题目给出了elmentui表单组件验证相关的知识和案例。所以根据案例写出来很快。可以说是送分题。
<!-- TODO 待修改的代码 -->
<div class="card" :class="item.css" v-for="(item,index) in wishList" :key="index">
<script>
const app = new Vue({
el: "#app",
data: {
wishList: [],
form: {
name:'',
content: '',
pic: ''
},
rules: {
// TODO 待补充验证的代码
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{ min: 2, max: 4, message: '长度在 2 到 4 个字符', trigger: 'blur' }
],
content:[
{ required: true, message: '请输入许愿内容', trigger: 'blur' },
{ min: 1, max: 30, message: '长度在 1 到 30 个字符', trigger: 'blur' }
]
},
num:1,
picList: [],
textarea: '',
dialogVisible: false,
disabled: false
},
});
</script>
第七题:消失的 Token
考点:这是一道排错题,主要是考察vuex的知识。
解题思路:
①我们应该先去看一下Vuex
相关的代码,在UserModule.js
中会发现UserModule
这个模块开启了命名空间。
②index.js
中是通过user
字段来引入UserModule
的,所以在使用UserModule
模块的内容时就需要注意要通过命名空间user
来引用,之后检查index.html
就很容易发现问题所在了。
<script>
// TODO 修改下面错误代码
var app = new Vue({
el: '#app',
data() { },
computed: {
welcome() {
// 修改处:通过命名空间引用
return store.getters.welcome
},
username() {
// 修改处:通过命名空间引用
return store.getters['user/username']
},
token() {
// 修改处:通过命名空间引用
return store.getters['user/token']
}
},
methods: {
// 回车/点击确认的回调事件
login(username) {
// 修改处:通过命名空间引用
username && store.commit('user/login', { username, token: 'sxgWKnLADfS8hUxbiMWyb' })
username && store.commit('say', '登录成功,欢迎你回来!')
}
}
})
</script>
第八题:趣购
题目要求:拖拉上面的商品实现添加到购物车并计算总和的操作。
解题思路:
①首先为需要为被拖拉的元素绑定属性draggable="true"
,表示元素可以拖动
②由于图片或者链接元素不用添加属性draggable="true"
也可以拖拉,为了防止用户拖拉错误,要为他们添加属性draggable="false"
阻止它们拖拉。
③为被拖拉的元素绑定用户拖拉开始事件dragstart
,获取需要用到的元素数据并将它们保存在 event dataTransfer
属性里。题目中提供了关于保存数据到event dataTransfer
的方法,如下图所示。
④为需要放置到的元素 drop
事件 (当元素或选中的文本在可释放目标上被释放时触发。)
⑤根据题目要求拖拉商品到放置区放开的时候就会添加进购物车并总计,所以在drop
事件中要拿到元素的数据。题目中提供了如何从获取event dataTransfer
属性里的数据(就是刚刚拖拉元素开始时保存到里面的数据)。
此时你会发现drop事件不能触发,题目中给了提示说想要 ondrop 能正确触发,有时需要在前置 dragover 事件中禁用默认行为
所有再为放置到的元素绑定dragover
事件(当元素或选中的文本被拖到一个可释放目标上时触发) ,再阻止默认行为,drop就可以正常触发了。
如下图所示,因为通过页面结构发现bought是用来存每一天数据的,bought里面有几条数据购物车上的小红点上就显示几,所以要将拿到的数据以一个对象的形式存到data的bought中。然后就需要展示在页面上的购物车商品和商品总计了。
⑥如下图所示,购物车商品是通过goodDetail的数据绑定的,总计是通过totalPrice的数据绑定的,所以要在计算里处理一下这两个数据变成效果的那样。
⑦我们先来算总计的数据。我们知道的是目前所有商品的数据都放在了this.bought里。它是以[{name:"畅销书",price:”30“}{name:"纸巾",price:”20“}]的形式存放商品的,所以我们可以使用reduce函数或者for……in……遍历来将所有的商品价格相加,再返回。
⑧购物车商品需要展示成畅销书*1收纳箱*2这样的形式,而目前数据是这样[{name:"畅销书",price:”30“}{name:"纸巾",price:”20“}]的形式,且目前的数据里存在相同的数据。还得把相同的商品数据归到一起。(直接讲不太好描述,我写在下面的代码的注释里了。)
<!-- TODO: 补充拖拽事件,请不要改动任何 id 属性 -->
<template>
<div class="container">
<div class="good-list">
<div v-for="good in goods" :key="good.name" class="good" draggable="true" @dragstart="dragstart($event,good)">
<img :src="good.cover" draggable="false"/>
<span>{{ good.name }}</span>
<span>¥{{ good.price }}</span>
</div>
</div>
<div id="trolley" class="trolley" @dragover="dragover" @drop="drop">
<span id="bought" class="bought" v-if="bought.length !== 0">{{
bought.length
}}</span>
<img src="./images/trolley.jpeg" />
</div>
<div class="result">
<div>
购物车商品:<span id="goods">{{ goodsDetail }}</span>
</div>
<div>
购物车商品总计:<span id="total">{{ totalPrice }}</span>
</div>
</div>
</div>
</template>
<script>
module.exports = {
data() {
return {
goods: [
{
name: "畅销书",
price: 30,
cover: "./images/book.jpeg",
},
{
name: "收纳箱",
price: 60,
cover: "./images/box.jpeg",
},
{
name: "纸巾",
price: 20,
cover: "./images/paper.jpeg",
},
{
name: "电视",
price: 1000,
cover: "./images/tv.jpg",
},
],
bought: [],
};
},
// TODO: 请补充实现代码
computed: {
//总计 最后面还放了第二种算总计的方法。
totalPrice() {
const {bought} = this;
var sum=0;
for(key in bought){
sum+=bought[key].price
}
return sum;
},
//
goodsDetail() {
//处理数据达到最后 例如畅销书*2 的效果
const {bought} = this;
const names=[] //用于存bought里的所有key值,便于使用reduce来计算出来重复出现的元素个数以{"畅销书":1,"纸巾":2}的形式返回。
const goods=[] //用于存处理后数据,数据格式为["畅销书*1","纸巾*2"]
//拿到所有的key值存于names中,此时names应该是这样的["畅销书","纸巾",“畅销书”……]
//可以直接Object.keys拿到所有key组成的数组
for(key in bought){
names.push(bought[key].name)
}
//计算names中重复出现的元素个数
function getRepeatNum(){
//这里将prev定义为一个空对象,next代表的是遍历names中的值
return names.reduce(function(prev,next){
prev[next] = (prev[next] + 1) || 1; //如果有就值+1如果没有就写入对象prev中且值为1
return prev;//返回最终结果
},{}); //将prev初始设置为一个空对象。
}
//将数据处理成这样["畅销书*1","纸巾*2"]的形式
for(key in getRepeatNum()){
goods.push(`${key}*${getRepeatNum()[key]}`)
}
return goods.join(' ')//将goods数组最后以空格分隔的字符串返回
},
},
methods: {
//拖动事件 当用户开始拖拽一个元素或选中的文本时触发
dragstart(e,good){
e.dataTransfer.setData("name", good.name);
e.dataTransfer.setData("price", good.price);
},
//放置事件 当元素或选中的文本被拖到一个可释放目标上时触发
dragover(e){
e.preventDefault()//阻止默认行为
},
//放置事件 当元素或选中的文本在可释放目标上被释放时触发
drop(e){
const {bought} = this
const name = e.dataTransfer.getData("name");
const price = e.dataTransfer.getData("price");
bought.push({name,price:Number(price)})
}
},
};
</script>
也可以通过produce函数来算商品总计。
totalPrice() {
const {bought} = this;
return bought.reduce((prev,next)=>{
prev+=next.price;
return prev;
},0)
},
第九题:分页组件
这道题虽然要求很多,但是也不要怕,按照要求一步一步的写不要跳要求写。因为是按照要求来给分的。
最终实现分页效果。
要求一:
首先使用axios调用接口获取数据,对result中的data和total进行赋值。注意result中data需要的数据是当前页的数据而不是全部数据,所以这里我使用slice(开始下标,结束下标)在原数据中截取当前页的数据在赋值给result的data。
async function ajax({ url, method = "get", data, query: { currentPage, pageSize } }) {
// TODO:根据函数参数 `query` 对象 `currentPage, pageSize` 获得当前页的数据
let result = {
data:[],
total:0
}
/*用于存放页面原数据*/
let resdata=[]
/*调接口存数据*/
let res=await axios[method ](url,data)
if(res.status===200){
resdata=res.data.data
result.total=res.data.total
}
/*当前页要展示的页面数据*/
result.data=resdata.slice((currentPage-1)*pageSize,currentPage*pageSize)
return result;
}
要求二:
按要求给出的条件写,<按钮最小为1,所以当前页面>1的时候才可以将对当前页码进行-1的操作。>按钮也是同理。最后要都要调一下this.initPagination()函数。(因为要求中写了当前页码改变时页面上的分页组件效果同步更新,而在注释里明确写了initPagination()函数是用在当前页码更新时调用的)
initEvents() {
this.root.querySelector("#btn-prev").addEventListener('click', () => {
// TODO:"<" 按钮的点击事件, 点击时 this.currentPage - 1
if(this.currentPage>1){
this.currentPage--;
this.initPagination();
}
})
this.root.querySelector("#btn-next").addEventListener('click', () => {
// TODO:">" 按钮的点击事件, 点击时 this.currentPage + 1
if(this.currentPage<this.totalPages){
this.currentPage++;
this.initPagination();
}
})
this.root.querySelector(".pager").addEventListener('click', (e) => {
if (e.target.nodeName.toLowerCase() === 'li') {
if (this.currentPage === e.target.innerText) return;
if (e.target.classList.contains('more')) return;
this.currentPage = Number(e.target.innerText)
}
this.initPagination();
});
}
要求三:
这里应该是本题最复杂的任务了,需要完成的是求出当前页码在页面中因该显示的页码数(有点绕口不要紧,我们接着往下看吧),需要考虑以下两种情况:
①totalPages<=pagerCount也就是说总的页码数量小于等于页码按钮的数量,那么直接遍历[1~totalPages]的数据添加到indexArr中去就可以了。
②totalPages>pagerCount当总的页码数大于页码按钮的数量时,还需要考虑以下三种情况的出现。
下面内容可能讲解的有些绕,所以我画了一张图,帮助大家理解,如下图所示。
1.页码靠左连续显示,例如[1,2,3,4,10]。当当前页码小于pagerCount-2,也就是靠左边显示连续页码的取值范围。那么页码范围就是[1~(pagerCount-1)],最后添加最后一个页码。
为什么当前页码小于pagerCount-2就是靠左连续显示,因为点击的时候靠左连续页码的最后一个页码时,页码开始连续中间显示,所以最大就是取到pagerCount-2。为什么是pagerCount-1?因为一共展示pageCount个页码按钮,最后一个页码一定有,所以前面就只有pageCount-1个页码按钮了。
2.页码中间连续显示,例如[1,3,4,5,10]。当页码大于pagerCount-2且小于currentPage<=totalPages-pagerCount+2,也就是中间的页码取值范围。最后就是添加响应的值到indexArr中了。注意:这里pageCount为奇数时和偶数时是不一样的。
3.页码靠右连续显示,例如[1,7,8,9,10]。当页码大于totalPages-pagerCount+2,也就是靠右边显示连续页码的取值范围。最后就是添加响应的值到indexArr中了。
注意:第二种情况下,我的最外层判断是用于判断页码是处于哪个位置显示的(右边、中间或是左边),最里面的循环求的是indexArr的值。
/**
* @description 得到分页数组 indexArr,如[1,2,3,4,10],[1,3,4,5,10],[1,7,8,9,10]
* @param {number} currentPage 当前页数,默认为第一页
* @param {number} totalPages 总的页码数
* @param {number} pagerCount 设置最大页码按钮数。 页码按钮的数量,当总页数超过该值时会折叠
* @return {Array} 分页数组 indexArr
*/
const createPaginationIndexArr = (currentPage, totalPages, pagerCount) => {
let indexArr = [];
// TODO:根据传参生成分页数组 indexArr
indexArr[0]=1 //第1个永远是1
if(totalPages<=pagerCount){
for(let i=1;i<totalPages;i++){
indexArr.push(i+1)
}
}else{
indexArr[pagerCount-1]=totalPages //最后1个永远是totalPages
if(currentPage<=pagerCount-2){
for(let i=1;i<pagerCount-1;i++){
indexArr[i]=i+1
}
}else if(currentPage<=totalPages-pagerCount+2){
let j=1;//用于计算下标的
for(let i=-Math.ceil((pagerCount-3)/2);i<=Math.floor((pagerCount-3)/2);i++){
indexArr[j++]=currentPage+i
}
}else{
let j=1;//用于计算下标的
for(let i=totalPages-pagerCount+2;i<totalPages;i++){
indexArr[j++]=i
}
}
}
return indexArr;
}
module.exports = {
createPaginationIndexArr
}
要求四:
计算template。如果后一个数与前一个数不是连续的数字,那么就添加<li class="number more">...</li>然后再添加<li class="number ${this.currentPage==next?'active' :''}">${next}</li>`,否则就直接添加<li class="number ${this.currentPage==next?'active' :''}">${next}</li>`。注意:要判断是否为当前页码,当前页码需要添加active类。
renderPagination(indexArr) {
let template = '';
// TODO:根据 indexArr 数组生成分页组件的字符串模板 template
template=indexArr.reduce((prev,next,index)=>{
if(indexArr[index]-indexArr[index-1]>1){
prev+=`<li class="number more">...</li>`
prev+=`<li class="number ${this.currentPage==next?'active' :''}">${next}</li>`
}else{
prev+=`<li class="number ${this.currentPage==next?'active' :''}">${next}</li>`
}
return prev;
},'')
this.root.innerHTML = `
<div class="pagination">
<div class="btn btn-left" id="btn-prev"><</div>
<ul class="pager">${template} </ul>
<div class="btn btn-right" id="btn-next">></div>
</div>`;
}
第十题:虚拟滚动列表
题目要求:只显示滚动到位置的数据,且往上再加载5条,往后再加载5条,优化效果。
考点:vue、scroll事件
解题思路:
①获取数据,将数据保存到list中,再将整个滚动框的高度给totalHeight。不给的话滚动条就不能被高度撑开后面的也就无法实现了。
②绑定滚动事件,计算出当前滚动位置第一个列表项的索引值。为当前的滚动高度除以一个的列表项高度就是有几个列表项,再向下取整。这个值就是当前滚动位置第一个列表项的索引值。
③然后为showlist返回当前可视区域的值,使用slice截取list中的相应数据。我们之前已经拿到了滚动条滚动到的第一个位置的值start,所以当start<buffer时(buffer是防止白屏出现,所以在可视范围上下都个加buffer个数据),因为start向上加buffer个,就需要索引值start-buffer,索引start索引值不可以小于buffer,不然会出现负数情况。
开始范围:起始索引<buffer,那么起始索引就是start不变,否则起始索引将要start-buffer
结束范围:起始索引+列表项可视范围内的个数+buffer
④最后设置列表偏移量translateY,不然滚动的时候数据是发生改变了,但位置却永远停留在最上面了。
<!-- TODO:补全 scroll 事件及其相关属性 -->
<template>
<div id="virtual-list" class="virtual-list" @scroll="scroll">
<div id="scroll-container" :style="{ height: totalHeight + 'px' }"></div>
<ul
id="list"
class="list"
:style="{
transform:
'translateY(' +
(start < buffer ? 0 : (start - buffer) * itemHeight) +
'px)',
}"
>
<li
v-for="item in showingList"
:key="item"
:style="{ height: itemHeight + 'px', lineHeight: itemHeight + 'px' }"
>
{{ item }}
</li>
</ul>
</div>
</template>
<script>
module.exports = {
data() {
return {
itemHeight: 60, // 每个列表项的高度,请勿更改
length: 10, // 列表项可视范围内的个数,请勿更改
buffer: 5, // 列表项可视范围外,防止出现白屏的 buffer 个数,上方 5 个,下方 5 个,可作为优化项实现
list: [],
totalHeight: 0,
scrollTop: 0,
start: 0,
};
},
computed: {
//处理数据,返回可以看到的数据
showingList(){
const {start,buffer,length,list} = this
let sliceStart= start < buffer ? start : start-buffer
let sliceEnd= start + length + buffer
return list.slice(sliceStart,sliceEnd);
},
},
methods: {
// TODO: 完成事件处理
scroll(e){
//e.currentTarget.scrollTop 滚动的高度
this.start=Math.floor(e.currentTarget.scrollTop/this.itemHeight)
}
},
mounted() {
// TODO: 完成数据请求
axios('./data.json').then(res=>{
this.list=res.data
this.totalHeight=this.list.length*this.itemHeight //总高度
})
},
};
</script>
🌷结语
希望本章内容对你有用。如有错误请帮忙及时指出,谢谢。最后,祝各位小伙伴们,在蓝桥杯中取得满意的成绩。