小蓝发现人之间面对面交流逐渐减少,倾诉的机会变少了,有些人比较腼腆保守,不敢大声说出自己的心里话,期盼之类的,容易造成压力过大,心愿便利贴可以匿名,提供大家安全隐秘方便的倾诉平台。 小蓝希望大家写下自己的目标、理想、愿望等,激励自己奋斗,积极向上......写出来做一个精神寄托。 可是,小蓝对 Vue 语法不熟,便利贴没能完成,快来帮助小蓝完成吧!
本题已经内置了初始代码,打开实验环境,目录结构如下:
├── css
│ ├── fonts
│ │ ├── element-icons.ttf
│ │ └── element-icons.woff
│ ├── index.css
│ └── wish.css
├── images
│ ├── bg.jfif
│ └── ding.png
├── index.html
└── js
├── index.js
└── vue.min.js
其中:
index.html
是主页面。css
是 element-ui
存放项目样式的文件夹。images
是图片文件夹。js
是项目所依赖的 js 库的文件夹。注意:打开环境后发现缺少项目代码,请复制下述命令至命令行进行下载。
wget https://labfile.oss.aliyuncs.com/courses/18164/wish.zip && unzip wish.zip && rm wish.zip
在浏览器中预览 index.html
页面效果如下:
在初始化的时候输入框并没有做验证,输入内容发布后,许愿贴并不能出现。
请在 index.html
文件中补全代码,具体需求如下:
本项目使用到的 element-ui
表单验证 API 如下:
表单属性
参数 | 说明 | 类型 |
---|---|---|
rules | 表单验证规则 | object |
表单项属性
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
prop | 表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的 | string | 传入 Form 组件的 model 中的字段 | - |
required | 是否必填 | boolean | - | false |
trigger | 验证时触发的事件 | string | click/focus/change/blur/input | - |
min | 最小长度 | number | - | - |
max | 最大长度 | number | - | - |
message | 验证不通过时的错误信息 | string | - | - |
使用示例
<el-form-item label="姓名" prop="activeName">
<el-input v-model="form.activeName" placeholder="请输入姓名"></el-input>
</el-form-item>
// ....
rules: {
activeName: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
}
完成后效果如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>心愿便利贴</title>
<!-- 引入element-ui样式 -->
<link rel="stylesheet" href="css/index.css">
<link rel="stylesheet" href="./css/wish.css">
<!-- 引入element-ui组件库 -->
<script src="./js/vue.min.js"></script>
<script src="./js/index.js"></script>
</head>
<body>
<div id="app">
<h1>心愿便利贴</h1>
<div class="container">
<!-- TODO 待修改的代码 -->
<div class="card" :class="item.css" v-for="(item,index) in wishList" :key="index">
<div class="header">
<img class="close" @click="closeCard(index)" src="./images/ding.png" alt="close">
</div>
<el-image
v-if="item.pic"
style="width: 100px; height: 100px"
:src="item.pic"
:preview-src-list="picList">
</el-image>
<div class="content">
{
{item.content}}
</div>
<div class="name">{
{item.name}}</div>
</div>
</div>
<div class="form">
<el-form ref="form" :rules="rules" label-position="left" :model="form" label-width="80px">
<el-form-item label="姓名" prop="name">
<el-input id="firstName" v-model="form.name" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="许愿内容" prop="content">
<el-input type="textarea" id="content" placeholder="请输入内容" v-model="form.content" show-word-limit></el-input>
</el-form-item>
<el-form-item label="图片上传">
<el-upload
ref="uploadRef"
action="#"
:limit="1"
list-type="picture-card"
:on-remove="handleRemove"
:on-change="getPic"
:auto-upload="false">
<i slot="default" class="el-icon-plus"></i>
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
:src="file.url"
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in"></i>
</span>
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemove(file)">
<i class="el-icon-delete"></i>
</span>
</span>
</div>
</el-upload>
</el-form-item>
<el-form-item>
<el-button id="onSubmit" type="primary" @click="onSubmit">发布</el-button>
<el-button @click="onRest">重置</el-button>
</el-form-item>
</el-form>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="form.pic" alt="">
</el-dialog>
</div>
</div>
</body>
<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
},
methods: {
// 提交方法
onSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
let obj = this.form;
obj.css = 'item' + this.num;
this.num++;
if(this.num > 4) {
this.num = 1;
}
this.wishList.push(obj)
this.form = {};
this.$refs.uploadRef.uploadFiles.pop()
console.log(this.wishList);
} else {
this.$message({
message: '提交错误!请检查输入内容',
type: 'warning'
});
return false;
}
});
},
// 关闭许愿卡
closeCard(index) {
this.wishList.splice(index,1)
},
// 重置表单
onRest() {
this.$refs['form'].resetFields();
},
// 图片删除
handleRemove(file) {
let index = this.$refs.uploadRef.uploadFiles.findIndex(e => e.uid === file.uid);
this.$refs.uploadRef.uploadFiles.splice(index,1);
},
// 模拟上传图片
getPic(e) {
this.form.pic = e.url;
this.picList.push(e.url)
},
// 预览图片
handlePictureCardPreview(file, fileList) {
this.form.pic = file.url;
this.dialogVisible = true;
}
}
});
</script>
</html>
1. 头部部分
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>心愿便利贴</title>
<!-- 引入element-ui样式 -->
<link rel="stylesheet" href="css/index.css">
<link rel="stylesheet" href="./css/wish.css">
<!-- 引入element-ui组件库 -->
<script src="./js/vue.min.js"></script>
<script src="./js/index.js"></script>
</head>
<!DOCTYPE html>
:声明文档类型为 HTML5。<meta charset="utf-8">
:设置字符编码为 UTF - 8,确保页面能正确显示各种字符。<title>心愿便利贴</title>
:设置页面标题为 “心愿便利贴”。link
标签:引入了 Element UI 的样式文件 css/index.css
和自定义的样式文件 ./css/wish.css
。script
标签:引入了 Vue.js 的压缩版本 vue.min.js
和可能包含 Element UI 相关配置的 index.js
文件。2. 主体部分-页面结构
<body>
<div id="app">
<h1>心愿便利贴</h1>
<div class="container">
<!-- TODO 待修改的代码 -->
<div class="card" :class="item.css" v-for="(item,index) in wishList" :key="index">
<div class="header">
<img class="close" @click="closeCard(index)" src="./images/ding.png" alt="close">
</div>
<el-image
v-if="item.pic"
style="width: 100px; height: 100px"
:src="item.pic"
:preview-src-list="picList">
</el-image>
<div class="content">
{
{item.content}}
</div>
<div class="name">{
{item.name}}</div>
</div>
</div>
<div id="app">
:Vue 实例挂载的根元素。<h1>心愿便利贴</h1>
:显示页面的标题。<div class="container">
:用于容纳心愿便利贴卡片的容器。v-for="(item,index) in wishList"
:使用 Vue 的 v-for
指令遍历 wishList
数组,为数组中的每个元素渲染一个 .card
元素。:key="index"
:为每个渲染的元素提供唯一的 key
,帮助 Vue 识别每个元素,提高渲染效率。@click="closeCard(index)"
:绑定点击事件,当点击关闭图标时,调用 closeCard
方法并传入当前元素的索引。<el-image>
:Element UI 的图片组件,用于显示上传的图片。v-if="item.pic"
表示只有当 item.pic
存在时才显示图片。3. 主体部分-表单部分
<div class="form">
<el-form ref="form" :rules="rules" label-position="left" :model="form" label-width="80px">
<el-form-item label="姓名" prop="name">
<el-input id="firstName" v-model="form.name" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="许愿内容" prop="content">
<el-input type="textarea" id="content" placeholder="请输入内容" v-model="form.content" show-word-limit></el-input>
</el-form-item>
<el-form-item label="图片上传">
<el-upload
ref="uploadRef"
action="#"
:limit="1"
list-type="picture-card"
:on-remove="handleRemove"
:on-change="getPic"
:auto-upload="false">
<i slot="default" class="el-icon-plus"></i>
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
:src="file.url"
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in"></i>
</span>
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemove(file)">
<i class="el-icon-delete"></i>
</span>
</span>
</div>
</el-upload>
</el-form-item>
<el-form-item>
<el-button id="onSubmit" type="primary" @click="onSubmit">发布</el-button>
<el-button @click="onRest">重置</el-button>
</el-form-item>
</el-form>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="form.pic" alt="">
</el-dialog>
</div>
</div>
</body>
<el-form>
:Element UI 的表单组件,ref="form"
用于在 Vue 实例中引用该表单,:rules="rules"
绑定表单验证规则,:model="form"
绑定表单数据。<el-form-item>
:表单中的每个表单项,prop
属性用于指定表单字段的名称,与验证规则和表单数据中的字段对应。<el-input>
:输入框组件,v-model
指令实现数据双向绑定。<el-upload>
:Element UI 的文件上传组件,action="#"
表示上传的地址,:limit="1"
限制只能上传一个文件,:on-remove="handleRemove"
和 :on-change="getPic"
分别绑定文件移除和文件改变的事件处理方法。<el-button>
:Element UI 的按钮组件,@click
绑定点击事件,分别调用 onSubmit
和 onRest
方法。<el-dialog>
:Element UI 的对话框组件,用于预览上传的图片,:visible.sync="dialogVisible"
实现对话框显示状态的双向绑定。1. 全局样式
* {
margin: 0;
padding: 0;
}
*
是通用选择器,它会选中页面上的所有元素。margin: 0;
和 padding: 0;
将所有元素的外边距和内边距都设置为 0,这样可以消除浏览器默认的外边距和内边距,使页面布局更加可控。2. body
元素样式
body {
background: rgb(50, 49, 50) url("../images/bg.jfif") no-repeat;
background-size: contain;
height: 100vh;
width: 100%;
}
background
是一个复合属性,rgb(50, 49, 50)
是背景颜色,url("../images/bg.jfif")
是背景图片的路径,no-repeat
表示背景图片不重复显示。background-size: contain;
使背景图片在保持其宽高比的前提下,尽可能大地适应元素,并且完全包含在元素内。height: 100vh;
将 body
元素的高度设置为视口高度的 100%,width: 100%;
将宽度设置为父元素宽度的 100%。3. .container
类样式
.container {
padding: 30px;
width: 80%;
margin: 100px auto 0;
overflow: hidden;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
padding: 30px;
为 .container
元素设置 30px 的内边距。width: 80%;
将宽度设置为父元素宽度的 80%。margin: 100px auto 0;
使元素在垂直方向上距离顶部 100px,水平方向上自动居中。overflow: hidden;
隐藏溢出元素内容的部分。display: flex;
将元素设置为弹性容器,使其子元素可以使用弹性布局。justify-content: space-between;
在主轴上均匀分布子元素,两端对齐。flex-wrap: wrap;
当子元素超出容器宽度时,允许子元素换行显示。4. .card
类样式
.card {
width: 250px;
height: 250px;
margin: 20px;
padding: 10px;
box-shadow: 0 2px 10px 1px #7f7f7f;
border-bottom-left-radius: 20px 500px;
border-bottom-right-radius: 500px 30px ;
border-top-right-radius: 5px 100px ;
font-family: '微软雅黑';
letter-spacing: 2px;
}
width: 250px;
和 height: 250px;
设置 .card
元素的宽度和高度。margin: 20px;
为元素设置 20px 的外边距。padding: 10px;
为元素设置 10px 的内边距。box-shadow: 0 2px 10px 1px #7f7f7f;
为元素添加阴影效果,水平偏移量为 0,垂直偏移量为 2px,模糊半径为 10px,扩展半径为 1px,颜色为 #7f7f7f
。border-bottom-left-radius
、border-bottom-right-radius
和 border-top-right-radius
分别为元素的左下角、右下角和右上角设置不同的圆角效果。font-family: '微软雅黑';
设置字体为 “微软雅黑”。letter-spacing: 2px;
设置字符间距为 2px。5. h1
元素样式
h1 {
line-height: 80px;
text-align: center;
color: wheat;
letter-spacing: 10px;
}
line-height: 80px;
设置行高为 80px。text-align: center;
使文本在元素内居中显示。color: wheat;
设置文本颜色为 wheat
。letter-spacing: 10px;
设置字符间距为 10px。6. .content
类样式
.content {
text-indent: 2em;
padding-top: 20px;
width: 100%;
}
text-indent: 2em;
使段落首行缩进 2 个字符的宽度。padding-top: 20px;
为元素顶部添加 20px 的内边距。width: 100%;
将宽度设置为父元素宽度的 100%。7. .name
类样式
.name {
text-align: right;
padding-top: 10px;
padding-right: 20px;
font-weight: bold;
}
text-align: right;
使文本在元素内右对齐。padding-top: 10px;
和 padding-right: 20px;
分别为元素顶部和右侧添加内边距。font-weight: bold;
设置字体加粗。8. .item1
、.item2
、.item3
、.item4
类样式
.item1 {
background-color: #6cde47;
}
.item2 {
background-color: #42baec;
}
.item3 {
background-color: #e3e197;
}
.item4 {
background-color: #ecc733;
}
这些类分别为元素设置不同的背景颜色,可以用于区分不同类型的卡片。
9. .header
类样式
.header {
width: 100%;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
}
width: 100%;
和 height: 30px;
设置元素的宽度和高度。display: flex;
将元素设置为弹性容器。justify-content: center;
在主轴上使子元素居中对齐。align-items: center;
在交叉轴上使子元素居中对齐。10. .close
类样式
.close {
width: 30px;
height: 30px;
cursor: pointer;
}
width: 30px;
和 height: 30px;
设置元素的宽度和高度。cursor: pointer;
当鼠标悬停在元素上时,鼠标指针变为手型,表示该元素可以点击。11. .form
类样式
.form {
height: 350px;
max-width: 390px;
width: 100%;
background-color: wheat;
border-radius: 5px;
padding: 25px;
position: fixed;
left: 0;
right: 0;
bottom: 0px;
margin: auto;
box-shadow: 0 4px 10px 1px bisque;
}
height: 350px;
设置元素的高度为 350px。max-width: 390px;
和 width: 100%;
使元素的宽度最大为 390px,并且在宽度小于 390px 时自适应父元素宽度。background-color: wheat;
设置背景颜色为 wheat
。border-radius: 5px;
为元素设置 5px 的圆角。padding: 25px;
为元素设置 25px 的内边距。position: fixed;
将元素固定在页面上,不随滚动条滚动。left: 0;
、right: 0;
和 bottom: 0px;
结合 margin: auto;
使元素在页面底部水平居中。box-shadow: 0 4px 10px 1px bisque;
为元素添加阴影效果。综上所述,这段 CSS 代码主要用于设置心愿便利贴应用的页面布局和样式,包括背景、卡片、表单等元素的样式。
1. Vue实例
<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
},
el: "#app"
:指定 Vue 实例挂载的 DOM 元素。data
:定义 Vue 实例的数据对象。 wishList
:用于存储所有的心愿便利贴数据。form
:用于存储表单输入的数据。rules
:定义表单验证规则,name
和 content
字段分别设置了必填和长度限制的验证规则,trigger: 'blur'
表示在输入框失去焦点时触发验证。num
:用于生成便利贴的样式类名。picList
:用于存储上传图片的 URL 列表。dialogVisible
:控制图片预览对话框的显示状态。disabled
:可能用于控制某些元素的禁用状态。2. 方法定义
methods: {
// 提交方法
onSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
let obj = this.form;
obj.css = 'item' + this.num;
this.num++;
if(this.num > 4) {
this.num = 1;
}
this.wishList.push(obj)
this.form = {};
this.$refs.uploadRef.uploadFiles.pop()
console.log(this.wishList);
} else {
this.$message({
message: '提交错误!请检查输入内容',
type: 'warning'
});
return false;
}
});
},
// 关闭许愿卡
closeCard(index) {
this.wishList.splice(index,1)
},
// 重置表单
onRest() {
this.$refs['form'].resetFields();
},
// 图片删除
handleRemove(file) {
let index = this.$refs.uploadRef.uploadFiles.findIndex(e => e.uid === file.uid);
this.$refs.uploadRef.uploadFiles.splice(index,1);
},
// 模拟上传图片
getPic(e) {
this.form.pic = e.url;
this.picList.push(e.url)
},
// 预览图片
handlePictureCardPreview(file, fileList) {
this.form.pic = file.url;
this.dialogVisible = true;
}
}
});
</script>
onSubmit
:提交表单的方法,调用 this.$refs['form'].validate
进行表单验证,如果验证通过,将表单数据添加到 wishList
中,并重置表单和上传文件列表;如果验证不通过,显示警告信息。closeCard
:关闭许愿卡的方法,根据传入的索引从 wishList
中移除对应的元素。onRest
:重置表单的方法,调用 this.$refs['form'].resetFields()
重置表单数据。handleRemove
:删除上传图片的方法,根据文件的 uid
从上传文件列表中移除对应的文件。getPic
:模拟上传图片的方法,将上传文件的 URL 赋值给 form.pic
并添加到 picList
中。handlePictureCardPreview
:预览图片的方法,将当前文件的 URL 赋值给 form.pic
,并显示图片预览对话框。四、工作流程▶️
showCard
类,触发卡片放大的动画效果。通过 HTML、CSS 和 JS 的协同工作,实现了一个完整的校园一卡通制卡页面,包括页面布局、样式设计、表单验证和动画效果。