🎉🎉欢迎来到我的CSDN主页!🎉🎉
🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚
🌟推荐给大家我的专栏《微信小程序开发实战》。🎯🎯
👉点击这里,就可以查看我的主页啦!👇👇
🎁如果感觉还不错的话请给我点赞吧!🎁🎁
💖期待你的加入,一起学习,一起进步!💖💖
一、自定义组件
开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。
1.创建自定义组件
在微信小程序的根目录下创建一个文件夹名为components,继续在components文件夹下创建文件夹tabs,选中tabs右击新建component输入定义的组件名会创建出json、wxml、wxss、js4个文件。
首先需要在 json 文件中进行自定义组件声明(将 component 字段设为 true 可将这一组文件设为自定义组件)
javascript
{
"component": true
}
同时,还要在 wxml 文件中编写组件模板,在 wxss 文件中加入组件样式,它们的写法与页面的写法类似。
html
<!-- 这是自定义组件的内部WXML结构 -->
<view class="inner">
{{innerText}}
</view>
<slot></slot>
css
/* 这里的样式只应用于这个自定义组件 */
.inner {
color: red;
}
注意:在组件wxss中不应使用ID选择器、属性选择器和标签名选择器。
在自定义组件的 js 文件中,需要使用 Component() 来注册组件,并提供组件的属性定义、内部数据和自定义方法。组件的属性值和内部数据将被用于组件 wxml 的渲染,其中,属性值是可由组件外部传入的。
javascript
// components/tabs/tabs.js
Component({
properties: {
// 这里定义了innerText属性,属性值可以在组件使用时指定
innerText: {
type: String,
value: 'default value',
}
},
data: {
// 这里是一些组件内部数据
someData: {}
},
methods: {
// 这里是一个自定义方法
customMethod: function(){}
}
})
2.使用自定义组件
使用已注册的自定义组件前,首先要在页面的 json 文件中进行引用声明。此时需要提供每个自定义组件的标签名和对应的自定义组件文件路径
javascript
{
"usingComponents": {
"tabs": "/components/tabs/tabs"
}
}
这样,在页面的 wxml
中就可以像使用基础组件一样使用自定义组件。节点名即自定义组件的标签名,节点属性即传递给组件的属性值。
html
<tabs></tabs>
效果展示:
3.自定义组件传值
自定义组件传值需要注意的一个点就是不能以驼峰命名如果遇见是大写的字母要变成小写并在中间加一个"-",例如我们的自定义组件属性值"innerText",就要变成"inner-text"。
html
<tabs inner-text="自定义组件"></tabs>
效果演示:
小贴士:
如果你在创建自定义组件的时候出现以下问题
可以将以下代码复制到project.config.json中的setting中即可
javascript"ignoreDevUnusedFiles": false, "ignoreUploadUnusedFiles": false,
4.注意事项
一些需要注意的细节
- 因为 WXML 节点标签名只能是小写字母、中划线和下划线的组合,所以自定义组件的标签名也只能包含这些字符。
- 自定义组件也是可以引用自定义组件的,引用方法类似于页面引用自定义组件的方式(使用 usingComponents 字段)。
- 自定义组件和页面所在项目根目录名不能以"wx-"为前缀,否则会报错。
- 注意,是否在页面文件中使用 usingComponents 会使得页面的 this 对象的原型稍有差异,包括:
- 使用 usingComponents 页面的原型与不使用时不一致,即 Object.getPrototypeOf(this) 结果不同。
- 使用 usingComponents 时会多一些方法,如 selectComponent 。
- 出于性能考虑,使用 usingComponents 时, setData 内容不会被直接深复制,即 this.setData({ field: obj }) 后 this.data.field === obj 。(深复制会在这个值被组件间传递时发生。)
- 如果页面比较复杂,新增或删除 usingComponents 定义段时建议重新测试一下。
二、自定义组件案例
学会了自定义组件的基本概念下面我就带领大家编写一个我们会议OA系统中所需的组件。
1.创建自定义组件
tabs.wxml
html
<view class="tabs">
<view class="tabs_title">
<view wx:for="{{tabList}}" wx:key="id" class="title_item {{index==tabIndex?'item_active':''}}" bindtap="handleItemTap" data-index="{{index}}">
<view style="margin-bottom:5rpx">{{item}}</view>
<view style="width:30px" class="{{index==tabIndex?'item_active1':''}}"></view>
</view>
</view>
<view class="tabs_content">
<slot></slot>
</view>
</view>
tabs.wxss
css
.tabs {
position: fixed;
top: 0;
width: 100%;
background-color: #fff;
z-index: 99;
border-bottom: 1px solid #efefef;
padding-bottom: 20rpx;
}
.tabs_title {
/* width: 400rpx; */
width: 90%;
display: flex;
font-size: 9pt;
padding: 0 20rpx;
}
.title_item {
color: #999;
padding: 15rpx 0;
display: flex;
flex: 1;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
}
.item_active {
/* color:#ED8137; */
color: #000000;
font-size: 11pt;
font-weight: 800;
}
.item_active1 {
/* color:#ED8137; */
color: #000000;
font-size: 11pt;
font-weight: 800;
border-bottom: 6rpx solid #333;
border-radius: 2px;
}
tabs.js
javascript
var App = getApp();
Component({
/**
* 组件的属性列表
*/
properties: {
tabList:Object
},
/**
* 组件的初始数据
*/
data: {
tabIndex:0
},
/**
* 组件的方法列表
*/
methods: {
handleItemTap(e){
// 获取索引
const {index} = e.currentTarget.dataset;
// 触发 父组件的事件
this.triggerEvent("tabsItemChange",{index})
this.setData({
tabIndex:index
})
}
}
})
2.使用自定义组件
list.json
javascript
{
"usingComponents": {
"tabs": "/components/tabs/tabs"
}
}
list.js的data中定义属性
javascript
tabs:['会议中','已完成','已取消','全部会议']
list.wxml
html
<tabs tabList="{{tabs}}" bindtabsItemChange="tabsItemChange">
</tabs>
效果展示:
三、会议管理布局
现在我们需要考虑的就是点击相应的内容显示相应的数据,我们只需将所点击内容的index值传递,根据index值的不同进行不同数据的遍历即可。
1.定义所需数据
js
javascript
//定义所需数据
lists: [
{
'id': '1',
'image': '/static/persons/1.jpg',
'title': '对话产品总监 | 深圳·北京PM大会 【深度对话小米/京东/等产品总监】',
'num': '304',
'state': '进行中',
'time': '10月09日 17:59',
'address': '深圳市·南山区'
},
{
'id': '1',
'image': '/static/persons/2.jpg',
'title': 'AI WORLD 2016世界人工智能大会',
'num': '380',
'state': '已结束',
'time': '10月09日 17:39',
'address': '北京市·朝阳区'
},
{
'id': '1',
'image': '/static/persons/3.jpg',
'title': 'H100太空商业大会',
'num': '500',
'state': '进行中',
'time': '10月09日 17:31',
'address': '大连市'
},
{
'id': '1',
'image': '/static/persons/4.jpg',
'title': '报名年度盛事,大咖云集!2016凤凰国际论坛邀您"与世界对话"',
'num': '150',
'state': '已结束',
'time': '10月09日 17:21',
'address': '北京市·朝阳区'
},
{
'id': '1',
'image': '/static/persons/5.jpg',
'title': '新质生活 · 品质时代 2016消费升级创新大会',
'num': '217',
'state': '进行中',
'time': '10月09日 16:59',
'address': '北京市·朝阳区'
}
],
lists1: [
{
'id': '1',
'image': '/static/persons/1.jpg',
'title': '对话产品总监 | 深圳·北京PM大会 【深度对话小米/京东/等产品总监】',
'num': '304',
'state': '进行中',
'time': '10月09日 17:59',
'address': '深圳市·南山区'
},
{
'id': '1',
'image': '/static/persons/2.jpg',
'title': 'AI WORLD 2016世界人工智能大会',
'num': '380',
'state': '已结束',
'time': '10月09日 17:39',
'address': '北京市·朝阳区'
},
{
'id': '1',
'image': '/static/persons/3.jpg',
'title': 'H100太空商业大会',
'num': '500',
'state': '进行中',
'time': '10月09日 17:31',
'address': '大连市'
}
],
lists2: [
{
'id': '1',
'image': '/static/persons/1.jpg',
'title': '对话产品总监 | 深圳·北京PM大会 【深度对话小米/京东/等产品总监】',
'num': '304',
'state': '进行中',
'time': '10月09日 17:59',
'address': '深圳市·南山区'
},
{
'id': '1',
'image': '/static/persons/2.jpg',
'title': 'AI WORLD 2016世界人工智能大会',
'num': '380',
'state': '已结束',
'time': '10月09日 17:39',
'address': '北京市·朝阳区'
}
],
lists3: [
{
'id': '1',
'image': '/static/persons/1.jpg',
'title': '对话产品总监 | 深圳·北京PM大会 【深度对话小米/京东/等产品总监】',
'num': '304',
'state': '进行中',
'time': '10月09日 17:59',
'address': '深圳市·南山区'
},
{
'id': '1',
'image': '/static/persons/2.jpg',
'title': 'AI WORLD 2016世界人工智能大会',
'num': '380',
'state': '已结束',
'time': '10月09日 17:39',
'address': '北京市·朝阳区'
},
{
'id': '1',
'image': '/static/persons/3.jpg',
'title': 'H100太空商业大会',
'num': '500',
'state': '进行中',
'time': '10月09日 17:31',
'address': '大连市'
},
{
'id': '1',
'image': '/static/persons/4.jpg',
'title': '报名年度盛事,大咖云集!2016凤凰国际论坛邀您"与世界对话"',
'num': '150',
'state': '已结束',
'time': '10月09日 17:21',
'address': '北京市·朝阳区'
},
{
'id': '1',
'image': '/static/persons/5.jpg',
'title': '新质生活 · 品质时代 2016消费升级创新大会',
'num': '217',
'state': '进行中',
'time': '10月09日 16:59',
'address': '北京市·朝阳区'
}
]
}
2.定义内容切换事件
javascript
//定义点击内容显示相应的数据的方法
tabsItemChange(e) {
let tolists;
if (e.detail.index == 1) {
tolists = this.data.lists1;
} else if (e.detail.index == 2) {
tolists = this.data.lists2;
} else {
tolists = this.data.lists3;
}
this.setData({
lists: tolists
})
}
3.布局会议管理
wxml
html
<!-- 顶部信息栏 -->
<tabs tabList="{{tabs}}" bindtabsItemChange="tabsItemChange">
</tabs>
<!-- 内容显示区 -->
<view style="height: 60px;"></view>
<block wx:for-items="{{lists}}" wx:for-item="item" wx:key="item.id">
<view class="list" data-id="{{item.id}}">
<view class="list-img">
<image class="video-img" mode="scaleToFill" src="{{item.image}}"></image>
</view>
<view class="list-detail">
<view class="list-title"><text>{{item.title}}</text></view>
<view class="list-tag">
<view class="state">{{item.state}}</view>
<view class="join"><text class="list-num">{{item.num}}</text>人报名</view>
</view>
<view class="list-info"><text>{{item.address}}</text>|<text>{{item.time}}</text></view>
</view>
</view>
</block>
<view class="section">
<text>到底啦</text>
</view>
wxss
css
/* pages/meeting/list/list.wxss */
.section{
color: #aaa;
display: flex;
justify-content: center;
}
.list-info {
color: #aaa;
}
.list-num {
color: #e40909;
font-weight: 700;
}
.join {
padding: 0px 0px 0px 10px;
color: #aaa;
}
.state {
margin: 0px 6px 0px 6px;
border: 1px solid #93b9ff;
color: #93b9ff;
}
.list-tag {
padding: 3px 0px 10px 0px;
display: flex;
align-items: center;
}
.list-title {
display: flex;
justify-content: space-between;
font-size: 11pt;
color: #333;
font-weight: bold;
}
.list-detail {
display: flex;
flex-direction: column;
margin: 0px 0px 0px 15px;
}
.video-img {
width: 80px;
height: 80px;
}
.list {
display: flex;
flex-direction: row;
border-bottom: 1px solid #6b6e74;
padding: 10px;
}
.mobi-text {
font-weight: 700;
padding: 15px;
}
.mobi-icon {
border-left: 5px solid #e40909;
}
.mobi-title {
background-color: rgba(158, 158, 142, 0.678);
margin: 10px 0px 10px 0px;
}
.swiper-item {
height: 300rpx;
width: 100%;
border-radius: 10rpx;
}
.userinfo {
display: flex;
flex-direction: column;
align-items: center;
color: #aaa;
}
.userinfo-avatar {
overflow: hidden;
width: 128rpx;
height: 128rpx;
margin: 20rpx;
border-radius: 50%;
}
.usermotto {
margin-top: 200px;
}
效果演示:
四、投票管理布局
1.顶部导航栏
我们可以根据自定义组件完成,首先在相应的json文件中引用自定义组件。
javascript
"usingComponents": {
"tabs": "/components/tabs/tabs"
}
随后在js文件中定义我们所需要展示的内容。
javascript
tabs: ['发起投票', '投票进行中', '已结束投票', '全部投票']
最后直接在wxml中应用即可。
html
<tabs tabList="{{tabs}}" bindtabsItemChange="tabsItemChange"></tabs>
效果展示:
2.定义内容切换事件
我们这里和刚刚的会议管理有些不同,我们刚刚的会议管理是点击不同的菜单显示不同的数据,但是这个的内容可就不是数据了,而是各不相同的组件,所以针对这个事情我们要做一个内容切换事件。
首先在我们的wxml中定义好每个菜单需要展示的内容并写好hidden样式
html
<view class="{{componentStatus[0] ? '' : 'hidden'}}">发起投票</view>
<view class="{{componentStatus[1] ? '' : 'hidden'}}">投票进行中</view>
<view class="{{componentStatus[2] ? '' : 'hidden'}}">已结束投票</view>
<view class="{{componentStatus[3] ? '' : 'hidden'}}">全部投票</view>
css
.hidden {
display: none;
}
在js中定义好属性与事件
javascript
componentStatus: [true,false, false, false]
javascript
tabsItemChange(e) {
let index = e.detail.index;
//全部的组件赋值为false
const lists = [false, false, false, false];
//将所点击的组件赋值为true
lists[index] = true;
this.setData({
componentStatus: lists // 更新 data 中的 componentStatus 属性值
});
}
效果展示:
3.布局投票管理
结合以上知识我们只需要将需要展示的内容放入view中即可,我这里只做一个演示。
wxml
html
<!-- 发起投票 -->
<view class="{{componentStatus[0] ? '' : 'hidden'}}">
<view class="title-view">基本信息</view>
<input class="info-title" type="text" placeholder="选择所属会议" />
<input class="info-title" type="text" placeholder="投票标题(最多30个字符)" />
<view class="info-text">
<textarea bindinput="handleInput" placeholder="请输入文本内容">
</textarea>
</view>
<view class="image-container">
<image src="{{imageUrl}}" mode="aspectFit"></image>
</view>
<button class="upload-button" bindtap="handleUploadImage">上传图片</button>
<view class="title-view">选项设置</view>
<input class="info-title" type="text" placeholder="请输入第一项(最多30个字符)" />
<input class="info-title" type="text" placeholder="请输入第二项(最多30个字符)" />
<view class="title-view">时间设置</view>
<view class="time">
<text> 开始时间:</text>
<picker mode="date" bindchange="handleDateChange1">
<input placeholder="请选择日期" disabled value="{{selectedDate1}}" />
</picker>
<picker mode="time" bindchange="handleTimeChange1">
<input placeholder="请选择时间" disabled value="{{selectedTime1}}" />
</picker>
</view>
<view class="time">
<text>结束时间:</text>
<picker mode="date" bindchange="handleDateChange2">
<input placeholder="请选择日期" disabled value="{{selectedDate2}}" />
</picker>
<picker mode="time" bindchange="handleTimeChange2">
<input placeholder="请选择时间" disabled value="{{selectedTime2}}" />
</picker>
</view>
<button>发起投票</button>
</view>
wxss
css
/* pages/vote/list/list.wxss */
.hidden {
display: none;
}
.title-view{
background-color: beige;
font-weight: 700;
padding-left: 7px;
}
.info-title{
padding: 5px 5px 10px 5px;
border-top:1px solid rgb(129, 129, 127);
}
.info-text{
height: 100px;
padding: 5px 5px 10px 5px;
border-top:1px solid rgb(129, 129, 127);
}
.image{
padding-left: 55px;
display: flex;
align-items: center;
}
.time{
border-top:1px solid rgb(129, 129, 127);
padding: 5px 0px 5px 0px;
display: flex;
align-items: center;
}
.image-container{
padding-left: 60px;
}
js
javascript
// pages/vote/list/list.js
Page({
/**
* 页面的初始数据
*/
data: {
tabs: ['发起投票', '投票进行中', '已结束投票', '全部投票'],//顶部导航栏
componentStatus: [true, false, false, false],//用于处理内容显示
imageUrl: '',// 用于存储选择的图片路径
selectedDate1: '', // 用于开始存储选择的日期
selectedTime1: '',// 用于开始存储选择的时间
selectedDate2: '', // 用于结束存储选择的日期
selectedTime2: '' // 用于结束存储选择的时间
},
//结束时间选择
handleTimeChange2(e) {
const selectedTime = e.detail.value;
this.setData({
selectedTime2: selectedTime // 更新选择的时间,触发重新渲染
});
},
//结束日期选择
handleDateChange2(e) {
const selectedDate = e.detail.value;
this.setData({
selectedDate2: selectedDate // 更新选择的日期,触发重新渲染
});
},
//开始时间选择
handleTimeChange1(e) {
const selectedTime = e.detail.value;
this.setData({
selectedTime1: selectedTime // 更新选择的时间,触发重新渲染
});
},
//开始日期选择
handleDateChange1(e) {
const selectedDate = e.detail.value;
this.setData({
selectedDate1: selectedDate // 更新选择的日期,触发重新渲染
});
},
//图片选择器
handleUploadImage() {
wx.chooseImage({
count: 1,
success: (res) => {
const imagePath = res.tempFilePaths[0];
// 处理选择的图片路径
console.log('选择的图片路径:', imagePath);
this.setData({
imageUrl: imagePath // 更新图片路径,触发重新渲染
});
}
});
},
//点击导航栏进行切换下方内容
tabsItemChange(e) {
let index = e.detail.index;
//全部的组件赋值为false
const lists = [false, false, false, false];
//将所点击的组件赋值为true
lists[index] = true;
this.setData({
componentStatus: lists // 更新 data 中的 componentStatus 属性值
});
} ,
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
效果展示:
五、个人中心布局
相对之前的内容这个还是比较简单的大家直接看代码就好。
1.wxml
html
<!--pages/ucenter/index/index.wxml-->
<view class="userInfo">
<image class="userInfo-head" src="/static/persons/CSDN头像.jpg"></image>
<view class="userInfo-login">Java方文山</view>
<text class="userInfo-set">修改></text>
</view>
<view class="cells">
<view class="cell-items">
<image src="/static/tabBar/sdk.png" class="cell-items-icon"></image>
<text class="cell-items-title">我发布的会议</text>
<view class="cell-items-num">3</view>
<text class="cell-items-detail">></text>
</view>
<hr />
<view class="cell-items">
<image src="/static/tabBar/sdk.png" class="cell-items-icon"></image>
<text class="cell-items-title">我主持的会议</text>
<view class="cell-items-num">4</view>
<text class="cell-items-detail">></text>
</view>
</view>
<view class="cells">
<view class="cell-items">
<image src="/static/tabBar/coding.png" class="cell-items-icon"></image>
<text class="cell-items-title">投票的会议</text>
<view class="cell-items-num">10</view>
<text class="cell-items-detail">></text>
</view>
<hr />
<view class="cell-items">
<image src="/static/tabBar/coding.png" class="cell-items-icon"></image>
<text class="cell-items-title">未投票的会议</text>
<view class="cell-items-num">6</view>
<text class="cell-items-detail">></text>
</view>
</view>
<view class="cells">
<view class="cell-items">
<image src="/static/tabBar/template.png" class="cell-items-icon"></image>
<text class="cell-items-title">我参与的会议</text>
<view class="cell-items-num">11</view>
<text class="cell-items-detail">></text>
</view>
<hr />
<view class="cell-items">
<image src="/static/tabBar/template.png" class="cell-items-icon"></image>
<text class="cell-items-title">我审核的会议</text>
<view class="cell-items-num">4</view>
<text class="cell-items-detail">></text>
</view>
</view>
<view class="cells">
<view class="cell-items">
<image src="/static/tabBar/coding.png" class="cell-items-icon"></image>
<text class="cell-items-title">消息</text>
</view>
<hr />
<view class="cell-items">
<image src="/static/tabBar/component.png" class="cell-items-icon"></image>
<text class="cell-items-title">设置</text>
</view>
</view>
2.wxss
css
/* pages/ucenter/index/index.wxss */
.userInfo{
padding: 5px 0px 20px 10px;
display: flex;
align-items: center;
}
.userInfo-head{
height: 80px;
width: 80px;
border: 5px solid rgb(121, 212, 224);
}
.userInfo-login{
width: 245px;
padding-left: 10px;
font-weight: 700;
}
.userInfo-set{
color: rgb(146, 151, 155);
}
.cells{
border-top: 8px solid rgb(238, 238, 238);
}
.cell-items{
display: flex;
align-items: center;
border-top: 1px solid rgb(238, 238, 238);
padding-top: 20px;
padding-bottom: 20px;
}
.cell-items-icon{
height: 25px;
width: 25px;
padding: 0px 10px 0px 5px;
}
.cell-items-title{
width: 340px;
}
.cell-items-num{
width: 30px;
font-weight: 700;
color: rgb(218, 31, 31);
}
.cell-items-detail{
color: rgb(146, 151, 155);
}
效果演示:
六、总体效果展示
1.首页
2.会议
3.投票
4.个人中心
到这里我的分享就结束了,欢迎到评论区探讨交流!!
💖如果觉得有用的话还请点个赞吧 💖