欢迎来到人人都可写代码,大家好,我是杨晓华,今天我们的课程内容是,项目实操,开发一个发布活动界面的H5。
这是要实现的H5发布活动界面展示效果,下面就是教大家如何制作的步骤:
1、在views的项目smart-zgw目录下,新建fa-bu.vue文件。这个文件由三部分style、template、script组成。stytle用于绑定样式、template用于设计标签界面、script用于数据交互操作。
2、template部分里编写的代码是
<template>
<div class="fabu-page">
<div class="clip-wp" id="clip-wp" v-show="isClip" >
<div id="container"></div>
<button id="save-img" @click="saveImg">截取</button>
</div>
<div class="fb-navbar ui-border-b" v-show="isClip == false">
<div class="fb-navbar-item" @click="link(0)" :class="queryType == 0 || queryType == null ? 'active' : ''">发布活动</div>
<div class="fb-navbar-item" @click="link(1)" :class="queryType == 1 ? 'active' : ''">我的发布</div>
</div>
<!--发布活动-->
<div v-if="queryType == 0">
<div class="tj_hb">
<input v-el:avatar hidden type="file" accept="image/*;capture=camera" name="img" @change="onChooseImage($event)">
<img :src="clipUrl" @click="hbPhoto()">
</div>
<!-- 压缩图片用的 -->
<canvas id="compressCanvas" style="position: fixed; z-index: -1; opacity: 0; top: -100%; left: -100%"></canvas>
<div>
<mt-field label="主题:" placeholder="活动主题" :value.sync="title"></mt-field>
<mt-cell class="mint-field" title="类型:" placeholder="请选择" @click="onType" is-link>
{{selectType}}
</mt-cell>
<mt-field label="日期时间:" placeholder="格式:2020-01-01 10:30" :value.sync="endTime"></mt-field>
<mt-field label="费用:" placeholder="现场付款,免费请填0" :value.sync="price"></mt-field>
<div v-show="selectType !== '线上活动'">
<mt-field label="地点:" placeholder="格式:市-详细地址" :value.sync="address"></mt-field>
</div>
<mt-field placeholder="详情描述" type="textarea" rows="5" :value.sync="remark"></mt-field>
</div>
<div class="upload-img-column">
<div class="words">添加详情图片 ({{imgTempList.length}}/5)</div>
<div class="upload-wrap">
<div class="box">
<label class="p dotted">
<input v-el:detail type="file" accept="image/jpg,image/jpeg,image/png" name="file"
@change="onChooseDetailImage($event)">
<img src="../../assets/img/jiahao.png" @click="detailPhoto()" alt="">
</label>
</div>
<template v-for="(imgIndex, imgItem) in imgTempList">
<div class="box">
<div class="p">
<img :src="imgItem">
<div class="delete" @click.stop="deleteImg(imgIndex)">
<img src="../../assets/img/close-icon.png" alt="">
</div>
</div>
</div>
</template>
</div>
</div>
<div class="footer">
<!--<mt-button class="preview" type="default" @click="preview">预览</mt-button>-->
<mt-button class="commit" type="primary" @click="commit">发布</mt-button>
</div>
<mt-popup :visible.sync="typeOption" position="bottom" class="select-box">
<div>
<div class="select-header">
<div class="title">选择类型</div>
<div class="close" @click="typeOption = false"></div>
</div>
<div class="select">
<label v-for="item in huoDongType" @click="setType(item.name)" :key="item.id">
<div class="line"></div>
<span class="text">{{item.name}}</span>
</label>
</div>
</div>
</mt-popup>
</div>
<!--我的发布-->
<div v-else>
<div class="list" v-if="listData.length > 0">
<div v-for="v in listData" v-on:click="openNews(v.id)">
<div class="article-item clearfix">
<div class="pull-left">
<img :src="v.banner" :alt="v.title">
</div>
<div class="item-intro">
<div class="item-title">{{v.title}}</div>
<p class="item-desc">{{v.status}} {{v.endTime}}</p>
</div>
</div>
</div>
</div>
<p class="order-list-empty" v-else>
<span></span>
还没有任何数据哦
</p>
<mt-button class="btn-more" size="large" type="primary" v-if="hasNext > 0" @click="loadingList">点击加载更多</mt-button>
</div>
<tabbar></tabbar>
</div>
</template>
3、style部分里编写的代码是:
<style lang="scss">
@import '../../assets/css/fn';
.fabu-page{
padding-bottom: 0.829rem;
.fb-navbar {
z-index: 100;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 0.512rem;
background-color: #fff;
.fb-navbar-item {
position: relative;
font-size: 0.170667rem;
float: left;
width: 50%;
height: 100%;
line-height: 0.512rem;
text-align: center;
color: #999;
&.active {
color: #333;
position: relative;
&:before {
content: "";
width: 50%;
height: 0.022756rem;
background: #ff004f;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
}
}
}
.list{
margin-top: 0.518rem;
.article-item{
position: relative;
padding: 5px;
background: #fff;
margin-bottom: 5px;
box-shadow: 1px 1px 1px #e7e7e7;
transition: all 0.2s;
img{
overflow: hidden;
height: 80px;
width: 130px;
margin-right: 15px;
}
.pull-left{
float: left;
}
.item-intro{
height: 80px;
overflow: hidden;
position: relative;
.item-title{
display: block;
font-size: 16px;
line-height: 1.5;
color: #333333;
width: 230px;
}
a{
text-decoration: none;
}
.item-desc{
font-size: 14px;
color: #e01845;
line-height: 1.5;
position:absolute;
bottom:0px;
}
}
}
}
.order-list-empty{
margin-top: 1.422222rem;
color: $desc;
text-align: center;
font-size: 0.159289rem;
span{
display: block;
width: 1.109333rem;
height: 1.109333rem;
background: url("../../assets/img/no_data.png") no-repeat 0 0;
background-size: cover;
margin: 0 auto 0.113778rem;
}
}
.tj_hb{
//text-align: center;
margin-top: .56rem;
img{
width: 100%;
height: 200px;
}
}
.select-box {
background: #fff;
width: 100%;
.select-header {
display: flex;
width: 100%;
height: 0.512rem;
.title {
font-size: 0.19rem;
-webkit-box-flex: 1;
-webkit-flex: 1;
flex: 1;
text-align: center;
text-indent: 0.512rem;
line-height: 0.512rem;
}
.close {
width: 0.512rem;
height: 0.512rem;
background: url("../../assets/img/close-icon.png") no-repeat center;
background-size: 0.147911rem;
}
}
.hover {
background-color: #ff004f;
border-color: #ff004f;
}
.hover::after {
border-color: #333;
-webkit-transform: rotate(45deg) scale(1);
transform: rotate(45deg) scale(1);
}
.select {
padding: 0.136533rem 0.170667rem 0.011333rem 0.170667rem;
p {
padding: 0.056889rem 0 0.136533rem 0;
}
label {
width: 100%;
display: block;
position: relative;
overflow: hidden;
margin-bottom: 0.136533rem;
span {
float: left;
}
.text {
color: #666;
font-size: 0.168rem;
margin-left: 0.113778rem;
margin-top: 0.136533rem;
}
input {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 0.216178rem;
}
}
}
.line{
background: #ced0d2;
height: 1px;
width: 100%;
}
}
.footer{
margin-top: 6px;
background-color: #fff;
.preview{
margin-left: 10px;
float: left;
width: 50%;
}
.commit{
//margin-right: 10px;
//float: right;
width: 100%;
}
}
/* 上传图片栏目 START */
.upload-img-column {
width: 100%;
padding: 10px;
}
.upload-img-column .words {
font-size: 14px;
}
.upload-img-column .upload-wrap {
width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.upload-img-column .upload-wrap .box {
width: 33.33%;
padding: 10px 10px 0 0;
}
.upload-img-column .upload-wrap .box .p {
width: 100%;
height: 0;
padding-bottom: 100%;
border-radius: 5px;
position: relative;
overflow: hidden;
}
.upload-img-column .upload-wrap .box .p img {
position: absolute;
width: 100%;
height: 100%;
border-radius: 5px;
overflow: hidden;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.upload-img-column .upload-wrap .box .p .delete {
width: 30px;
height: 30px;
text-align: center;
border-radius: 50%;
background: #e0e0e0;
position: absolute;
right: 0;
top: 0;
//z-index: 2;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
opacity: 0.8;
}
.upload-img-column .upload-wrap .box .p .delete:active {
-webkit-transform: scale(0.95);
-ms-transform: scale(0.95);
transform: scale(0.95);
}
.upload-img-column .upload-wrap .box .p .delete img {
width: 14px;
height: 14px;
}
.upload-img-column .upload-wrap .box .p.dotted {
display: block;
border: dotted 2px #999;
}
.upload-img-column .upload-wrap .box .p.dotted input {
display: none;
}
.upload-img-column .upload-wrap .box .p.dotted img {
width: 60%;
height: auto;
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
opacity: .8;
}
/* 上传图片栏目 END */
.clip-wp{
position: fixed;
width: 100%;
top: 0;
bottom: 0;
z-index: 11;
background-color: #000;
text-align: center;
#container{
background-color: #000;
text-align: center;
width: 100%;
left: 0;
right: 0;
top: 20px;
bottom: 80px;
margin: 0 auto;
position: absolute;
}
#save-img{
position: absolute;
bottom: 20px;
width: 90%;
left: 5%;
height: 42px;
line-height: 42px;
color: #fff;
background-color: #32c47c;
border-radius: 20px;
}
#image-box {
position: absolute;
left: 0px;
right: 0px;
bottom: 0px;
top: 0px;
margin: auto;
}
#cover-box {
position: absolute;
z-index: 9999;
display: none;
left: 0px;
right: 0px;
bottom: 0px;
top: 0px;
margin: auto;
}
}
.btn-more{
margin-bottom: 0.133333rem;
text-align: center;
color: #333;
background-color: #ecf2f7;
}
}
</style>
4、script部分里编写的代码是:
<script>
import api from '../../api'
import Tabbar from '../../components/tabbar'
import Popup from '../../components/popup'
import Button from '../../components/button'
import Cell from '../../components/cell'
import Field from '../../components/field'
import Toast from '../../components/toast'
import { getAllParam, setCookie, getCookie, clearCookie } from '../../utils/other.js'
import clip from '../../components/clip/clip.js'
import Indicator from '../../components/indicator'
export default {
components: [Tabbar,Popup, Button, Field, Cell,Toast],
data () {
return {
token: '',
typeOption: false,
selectType: '请选择',
title: '',
zhu_bf: '',
zhu_bf_phone: '',
address: '',
endTime: '',
price: '',
remark: '',
huoDongType: [{
id: 1,
name: '夏令营'
},{
id: 2,
name: '游乐园'
},{
id: 3,
name: '周边户外'
},{
id: 4,
name: '岁月摄影'
},{
id: 5,
name: '教育培训'
},{
id: 6,
name: '线上活动'
}] ,
queryType: 0,
hasNext: 0,
page: 1,
listData: [{
"id": "1",
"title": "30天宝妈成长陪伴--《宝妈加油站》线上训练营",
"status": '进行中',
"endTime": "2020-05-06",
"banner": "http://cdn.huodongxing.com/file/20161115/110189F058F20ABBDC8B0E40D81C448D7A/1540798324752.jpg",
"city": "深圳",
"price": 0,
"hdType": "线上活动"
}],
clipUrl: "https://wb-master.oss-cn-shenzhen.aliyuncs.com/photo/tj_hb.png",
imgTempList: [], //图片临时路径列表
isClip:false,
noScoll:function(evt){
this.isClip && evt.preventDefault();
},
clip:{}
}
},
route: {
data (transition) {
if (process.env.NODE_ENV === 'production') {
this.zhu_bf = getCookie('prod_zhu_bf')
this.zhu_bf_phone = getCookie('prod_zhu_bf_phone')
} else {
this.zhu_bf = getCookie('dev_zhu_bf')
this.zhu_bf_phone = getCookie('dev_zhu_bf_phone')
}
if(this.$route.query.type){
this.queryType = this.$route.query.type
//我的发布数据
if(this.queryType == 1)
{
this.getData()
}
}
}
},
methods: {
saveImg(){
this.isClip = false;
this.clip.save();
document.body.removeEventListener('touchmove',this.noScoll,false);
},
hbPhoto() {
//触发添加海报图片按钮
//console.log('onPhoto')
this.$els.avatar.dispatchEvent(new MouseEvent("click"));
},
detailPhoto() {
//触发添加详情图片按钮
//console.log('detailPhoto')
this.$els.detail.dispatchEvent(new MouseEvent("click"));
},
//选择海报图片
onChooseImage: function (event) {
this.clip = new clip('container',this);
this.clip.init(event.target.files[0]);
this.isClip = true;
document.body.addEventListener('touchmove',this.noScoll,false);
},
//选择图片
onChooseDetailImage: function (event) {
var that = this;
//判断图片数量是否已上限
var currentImgTempArray = that.imgTempList;
if (currentImgTempArray.length >= 5) {
Toast("最多上传5张图片");
return false;
}
//使用FileReader对文件对象进行操作
var reader = new FileReader();
reader.readAsDataURL(event.target.files[0]); //将读取到的文件编码成Data URL
reader.onload = function () { //读取完成时
var replaceSrc = reader.result; //文件输出的内容
//压缩图片处理
var image = new Image();
image.src = replaceSrc;
image.onload = function () {
//获取图片初始宽高
var width = image.width;
var height = image.height;
//判断图片宽度,再按比例设置宽度和高度的值
if (width > 1024) {
width = 1024;
height = Math.ceil(1024 * (image.height / image.width));
}
//将图片重新画入canvas中
var canvas = document.getElementById("compressCanvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
context.beginPath();
context.fillStyle = "#ffffff";
context.fillRect(0, 0, width, height);
context.fill();
context.closePath();
context.drawImage(image, 0, 0, width, height);
replaceSrc = canvas.toDataURL("image/png"); //canvas转DataURL(base64格式)
//将压缩后的路径 追加到临时路径数组中
var totalList = [];
if (currentImgTempArray.length > 0) {
totalList = currentImgTempArray.concat(replaceSrc);
} else {
totalList[0] = replaceSrc;
}
that.imgTempList = totalList;
};
};
},
deleteImg: function (idx) {
var that = this;
that.imgTempList.splice(idx, 1);
},
openNews (id) {
window.location.href = "#!/smart-zgw/my-fabu?id=" + id
},
commit() {
let params = {}
params.banner = this.clipUrl
params.fj_image = this.imgTempList
params.title = this.title
params.zhu_bf = this.zhu_bf
params.zhu_bf_phone = this.zhu_bf_phone
params.address = this.address
params.endTime = this.endTime
params.remark = this.remark
params.price = this.price
params.hdType = this.selectType
api.commitHuoDong.save(params).then(res => {
let data = res.data.message
Toast(data)
}, res => {
console.log(1111)
})
},
preview() {
//window.location.href = "#!/smart-zgw/yu-lan"
this.$route.router.go({path: 'yu-lan'})
},
onType() {
this.typeOption = true
console.log(this.typeOption)
},
setType(name) {
this.selectType = name
this.typeOption = false
},
getData() {
Indicator.open()
this.page = 1
let params = {}
params.zhu_bf_phone = this.zhu_bf_phone
params.page = this.page
params.pageSize = 30
api.myHuoDong.get(params).then(res => {
let data = res.data.data
this.listData = data.list
this.hasNext = data.page.pageCount - this.page
Indicator.close()
}, res => {
Indicator.close()
})
},
loadingList () {
Indicator.open()
this.page = this.page + 1
let params = {}
params.zhu_bf_phone = this.zhu_bf_phone
params.page = this.page
params.pageSize = 30
api.myHuoDong.get(params).then(res => {
let data = res.data.data.list
this.listData = this.listData.concat(data)
this.hasNext = res.data.data.page.pageCount - this.page
Indicator.close()
}, res => {
Indicator.close()
})
},
link(index) {
this.$route.router.go({path: 'fa-bu', query: {type: index}})
//window.location.href = "#!/smart-zgw/fa-bu?type="+index
}
},
filters: {
urlHttp (value) {
if (value && value.indexOf('http:') > -1) {
return location.protocol + value.split('http:')[1]
} else {
return value;
}
},
text (val) { // 转换成换行
if (val) {
return val.replace(/rn/g, '</br>')
}
},
enter (val) {
if (val) {
return val.replace(/rn/g, '</br>')
}
}
}
}
</script>
5、发布活动界面用到发布活动和读取我的发布活动数据的接口,在script里可以看到api.commitHuoDong和api.myHuoDong,在api目录的index.js文件里需设置这两个接口。
export default {
login: init(appHost, '/v2/topic/login?appid=zgw'),
getVerifyCode: init(appHost, ‘/v2/login/get-verify-code?appid=zgw’),
homeIndex: init(appHost, '/v2/topic/home-index?appid=zgw'),
commitHuoDong: init(appHost, '/v2/topic/fabu-huodong?appid=zgw'),
myHuoDong: init(appHost, '/v2/topic/my-huodong?appid=zgw'),
}
6、编写界面和数据交互后,还需要设置页面的访问路由,在route目录的index
.js文件里添加下面代码,在开发环境,输入http://localhost:8087/?#!/smart-zgw/fa-bu就可访问发布活动H5。
export function configRouter (router) {
router.map({
'/smart-zgw': {
component: require('../views/smart-zgw/wrap.vue'),
subRoutes: {
'home': {
component: require('../views/smart-zgw/home.vue'),
title: '这个娃亲子活动服务平台',
name: 'smartzgwhome'
},
'login': {
component: require('../views/smart-zgw/login.vue'),
title: '登录'
},
'xie-yi': {
component: require('../views/smart-zgw/xie-yi.vue'),
title: '《用户服务协议》'
},
'fa-bu': {
component: require('../views/smart-zgw/fa-bu.vue'),
title: '发布',
name: 'fabu'
}
}
开头:人人都可写代码-H5零基础编程-序言01
下一篇:人人都可写代码-H5零基础编程-活动订单界面实操08