微信小程序支持简洁的组件化编程

开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。自定义组件在使用时与基础组件非常相似。

这篇博文主要就是分享个自己实际项目中用到的 转盘自定义组件

项目源码地址:GitHub - hujinchen/zhuanpan2.0: 转盘组件

关于如何开始创建自定义组件、怎么写、如何使用类的,网上有挺多教程的,可自行去学习,这里就不介绍了

可以看下官方自定义组件的开发文档:

自定义组件 | 微信开放文档

先来看下实际项目中大概的效果图:

该组件是可以设置转盘的大小、转动的时间、转动的声音、不重复抽取、切换转盘选项还有概率等功能,都是可配置的

当然当前小程序中不涉及到概率的问题,但还是封装了一个,方便以后的使用

类似于页面,一个自定义组件由 json wxml wxss js 4个文件组成。要编写一个自定义组件,首先需要在 json 文件中进行自定义组件声明(将 component 字段设为 true 可这一组文件设为自定义组件)

同时,还要在 wxml 文件中编写组件模版,在 wxss 文件中加入组件样式,它们的写法与页面的写法类似。

好了接下来贴代码,注释都写在代码里了,相信你们应该看的懂哈!!!

zhuanpan.json:

{"component": true,"usingComponents": {}
}

zhuanpan.wxml:

<view class="canvas-container"><view animation="{{animationData}}" class="gb-wheel-content" style='width:{{size-2}}rpx;height:{{size}}rpx;'><!-- 扇形颜色背景 当选项长度等于2或者3时做了特殊处理 --><view class="canvas-list"><view class="canvas-item2" wx:for="{{awardsConfig.awards}}" wx:key="key" style="transform: rotate({{item.item2Deg}});background-color:{{awardsConfig.awards.length==2?item.color:''}};opacity:{{awardsConfig.awards.length==2?item.opacity:awardsConfig.awards.length==3?item.opacity:''}};width:{{size}}rpx;height:{{size/2-2}}rpx;transform-origin:{{size/2}}rpx {{size/2}}rpx"><view class="canvas-item2-after" style="transform: rotate({{item.afterDeg}});background-color:{{item.color}};opacity:{{awardsConfig.awards.length==3?'':item.opacity}};width:{{size/2}}rpx;height:{{size/2}}rpx;transform-origin: {{size/2}}rpx {{size/2}}rpx"></view><view wx:if='{{awardsConfig.awards.length==3}}' class="canvas-item2-after" style="background-color:{{item.color}};width:{{size/2}}rpx;height:{{size/2}}rpx;transform-origin: {{size/2}}rpx {{size/2}}rpx"></view></view></view><!-- 选项内容 --><view class="gb-wheel-list"><view class="gb-wheel-item" data-index="{{index}}" wx:for="{{awardsConfig.awards}}" wx:key='key'><view class="gb-wheel-icontent" style="height:262rpx;overflow:hidden;font-size:{{item.name.length>9?'20':'26'}}rpx;line-height:{{item.name.length>9?'20':'26'}}rpx;width:26rpx;padding-top:5rpx;transform: rotate({{index*turnNum}}turn);transform-origin: 50% {{size/2-2}}rpx"><text style='word-break:break-all;'>{{item.name}}</text></view></view></view></view><view class="img-container" style='width:100%;height:{{size}}rpx;'><!-- 转盘中间的按钮 --><image bindtap="_zhuan" src='https://gamesdata.oss-cn-hangzhou.aliyuncs.com/xiaojueding/canvas_button_go_unclick.png' style='width:{{size/4.4}}rpx;display:{{block1}};margin-top:16rpx' mode='widthFix'></image><image src='https://gamesdata.oss-cn-hangzhou.aliyuncs.com/xiaojueding/canvas_button_go_click.png' style='width:{{size/4.4}}rpx;display:{{block2}};margin-top:16rpx' mode='widthFix'></image><image bindtap="reset" src='https://gamesdata.oss-cn-hangzhou.aliyuncs.com/xiaojueding/canvas_button_reset_unclick.png' style='width:{{size/4.4}}rpx;display:{{block3}};margin-top:16rpx' mode='widthFix'></image><image src='https://gamesdata.oss-cn-hangzhou.aliyuncs.com/xiaojueding/canvas_button_reset_click.png' style='width:{{size/4.4}}rpx;display:{{block4}};margin-top:16rpx' mode='widthFix'></image></view>
</view>

zhuanpan.css:

/* components/zhuanpan/zhuanpan.wxss */.canvas-container {margin: 0 auto;position: relative;display: flex;align-items: center;justify-content: center;
}.img-container {margin: 0 auto;position: absolute;display: flex;align-items: center;justify-content: center;left: 0;top: 0;z-index:10;
}.gb-wheel-run {box-shadow: 0 0 5rpx 0rpx rgba(0, 0, 0, 0.98);width: 700rpx;height: 700rpx;border-radius: 50%;border: 30rpx solid #f1ecec;box-sizing: border-box;position: absolute;left: 27rpx;top: -19rpx;opacity: 0.7;
}.gb-wheel-content {position: relative;margin: 0 auto;z-index: 2;width: 660rpx;height: 660rpx;border-radius: 50%;border: 20rpx solid #f1ecec;box-shadow: 0 0 5rpx 0rpx rgba(0, 0, 0, 0.98);opacity: 0.7;overflow: hidden;
}.canvas-list {position: absolute;left: 0;top: 0;width: inherit;height: inherit;z-index: 8;
}.canvas-item2 {position: absolute;left: 0px;top: 0;width: 660rpx;height: 328rpx;color: #e4370e;font-weight: bold;transform-origin: 330rpx 330rpx;overflow: hidden;
}.canvas-item2-after {position: absolute;top: 0;left: 0;width: 330rpx;height: 330rpx;transform-origin: 330rpx 330rpx;opacity: 1;
}.gb-wheel-list {position: absolute;left: 0;top: 0;width: 100%;height: 100%;z-index: 9;
}.gb-wheel-item {position: absolute;left: 0;top: 0;width: 100%;height: 100%;color: #fff;text-shadow: 0 1px 1px rgba(255, 255, 255, 0.6);
}.gb-wheel-icontent {position: relative;display: block;padding-top: 50rpx;margin: 0 auto;text-align: center;transform-origin: 50% 328rpx;
}

zhuanpan.js:

// components/zhuanpan/zhuanpan.js
//创建并返回内部 audio 上下文 innerAudioContext 对象
const start = wx.createInnerAudioContext();
const mid = wx.createInnerAudioContext();
const stop = wx.createInnerAudioContext();Component({options: {multipleSlots: true // 在组件定义时的选项中启用多slot支持},/*** 组件的属性列表* 用于组件自定义设置   组件的对外属性*/properties: {myProperty: {    // 属性名        myProperty2: String, 简化的定义方式type: String, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型)value: '',    // 属性默认 初始值(可选),如果未指定则会根据类型选择一个observer: function (newVal, oldVal, changedPath) {// 属性被改变时执行的函数(可选),也可以写成在methods段中定义的方法名字符串, 如:'_propertyChange'// 通常 newVal 就是新设置的数据, oldVal 是旧数据}},probability: {type: Boolean, // 概率开关,默认false 随机value: false},musicflg: {type: Boolean, // 转盘声音开关,默认truevalue: true},fastJuedin: {type: Boolean, // 快速转动转盘的开关,默认falsevalue: false},repeat: {type: Boolean, // 重复抽取开关,默认falsevalue: false},size: {type: Number, // 转盘大小,传入宽度即可value: 600},zhuanpanArr: { // 可以切换的转盘选项, 支持多个type: Array,value: [{id: 0,option: '转盘的标题名称',awards: [{id: 0,name: "最多17个选项",  // 选项名color: 'red',         // 选项的背景颜色probability: 10       // 概率 0代表永远也转不到这个选项,数字越大概率也就越大},{id: 1,name: "选项最多填13字", // 超过9个字时字体会变小点color: 'green',probability: 10}]}]},// 限制:最多17个选项, 单个选项最多填10-13个字,多余部分会隐藏awardsConfig: { // 默认的当前转盘选项 type: Object,value: {option: '我的小决定?',awards: [{id: 0,name: "最多17个选项",color: 'red',probability: 0},{id: 1,name: "选项最多填13字",color: 'green',probability: 0}],},observer: function (newVal, oldVal, changedPath) {if (newVal) {this.switchZhuanpan(newVal, true);}}}},/*** 私有数据,组件的初始数据* 可用于模版渲染   */data: {animationData: {}, // 转盘动画zhuanflg: false,   // 转盘是否可以点击切换的标志位fastTime: 7600,    // 转盘快速转动的时间slowTime: 3900,    // 转盘慢速转动的时间runDegs: 0,        // 转盘旋转了多少圈timer: null,       // 清除转盘的时间clearTimeout(timer)block1: 'block',   // 控制显示或隐藏转盘中心的图片block2: 'none',block3: 'none',block4: 'none'},//组件生命周期函数,在组件实例进入页面节点树时执行,注意此时不能调用 setDatacreated: function () { },// 组件生命周期函数,在组件实例进入页面节点树时执行attached: function () {console.log('===============attached===============');start.src = 'https://gamesdata.oss-cn-hangzhou.aliyuncs.com/xiaojueding/start.mp3'; // 转盘开始转动的音乐mid.src = 'https://gamesdata.oss-cn-hangzhou.aliyuncs.com/xiaojueding/mid.mp3';     // 快速决定时,转盘开始转动的音乐stop.src = 'https://gamesdata.oss-cn-hangzhou.aliyuncs.com/xiaojueding/stop.mp3';   // 转盘停止转动的音乐this.setData({awardsConfig: this.data.zhuanpanArr[0]})this.initAdards();},/*** 组件的方法列表* 更新属性和数据的方法与更新页面数据的方法类似*/methods: {/** 公有方法*///判断值是否为空isNull(str) {if (str == null || str == undefined || str == '') {return true;} else {return false;}},//初始化数据initAdards() {var that = this, awardsConfig = that.data.awardsConfig;var t = awardsConfig.awards.length;  // 选项长度var e = 1 / t, i = 360 / t, r = i - 90;for (var g = 0; g < t; g++) {awardsConfig.awards[g].item2Deg = g * i + 90 - i / 2 + "deg";//当前下标 * 360/长度 + 90 - 360/长度/2awardsConfig.awards[g].afterDeg = r + "deg";awardsConfig.awards[g].opacity = '1';}that.setData({turnNum: e, // 页面的单位是turnawardsConfig: awardsConfig,})that._change();//向父组件传出当前转盘的初始数据},//重置转盘reset() {var that = this, awardsConfig = that.data.awardsConfig;var animation = wx.createAnimation({duration: 1,timingFunction: "ease"});that.animation = animation;animation.rotate(0).step(), that.data.runDegs = 0;that.setData({animationData: animation.export(),block3: 'none',block4: 'block'})for (let x in awardsConfig.awards) {awardsConfig.awards[x].opacity = '1';}setTimeout(function () {that.setData({block1: 'block',block4: 'none',awardsConfig: awardsConfig,})}, 300)},//父组件需要切换当前转盘的选项//如果有需要切换不同转盘的选项时,可以调用这方法//data: 转盘的数据//flag: 当转盘在转动过程中如果你想停止的话,可以传个true值,默认可不传switchZhuanpan(data, flag) {this.setData({awardsConfig: data,block1: 'block',block1: 'none',block3: 'none',block4: 'none',zhuanflg: false,})this.initAdards();if (flag) {this.reset();clearTimeout(this.data.timer);start.stop();mid.stop();stop.stop();wx.removeStorageSync('repeatArr');}},/** 内部私有方法建议以下划线开头* triggerEvent 用于触发事件,过triggerEvent来给父组件传递信息的* 写法: this.triggerEvent('cancelEvent', { num: 1 })  // 可以将num通过参数的形式传递给父组件*/// GO转盘开始转动_zhuan() {var that = this, awardsConfig = that.data.awardsConfig, runDegs = that.data.runDegs;//>>> 是无符号移位运算符var r = Math.random() * awardsConfig.awards.length >>> 0, runNum = 8;/*=============不重复抽取=============*/if (that.data.repeat) {r = that._queryRepeat(r);} else {wx.removeStorageSync('repeatArr');console.log('是否开启了概率???', that.data.probability);//开启概率 probability这属性必须要传个tureif (that.data.probability) {r = that._openProbability();}}/*=============不重复抽取=============*/console.log('当前答案选项的下标=====', r);setTimeout(function () {//转盘开始转动音乐that.data.musicflg ? that.data.fastJuedin ? mid.play() : start.play() : '';//要转多少度degrunDegs = runDegs || 0, runDegs = runDegs + (360 - runDegs % 360) + (2160 - r * (360 / awardsConfig.awards.length));var animation = wx.createAnimation({duration: that.data.fastJuedin ? that.data.slowTime : that.data.fastTime,timingFunction: "ease"});that.animation = animation;//这动画执行的是差值 //如果第一次写rotate(360) 那么第二次再写rotate(360)将不起效果animation.rotate(runDegs).step(), 0 == r && (runDegs = 0);that.setData({animationData: animation.export(),block1: 'none',block2: 'block',zhuanflg: true,})that._setatZhuan(true);}, 100);that.setData({timer: setTimeout(function () {//转盘停止后,答案区域高亮显示,其他区域增加透明度for (let x in awardsConfig.awards) {if (x != r) {awardsConfig.awards[x].opacity = '0.3';} else {awardsConfig.awards[x].opacity = '1';}}//转盘停止后的音乐!that.data.musicflg ? '' : stop.play();that.setData({animationData: {},s_awards: awardsConfig.awards[r].name,//最终选中的结果awardsConfig: awardsConfig,block2: 'none',block3: 'block',zhuanflg: false,})that._myAwards();that._setatZhuan(false);}, that.data.fastJuedin ? that.data.slowTime : that.data.fastTime)})},// 开启概率 // 传的数越大概率越大// 传入0的话就永远摇不到这个选项_openProbability() {var that = this, awards = that.data.awardsConfig.awards, arr = [];//5, 5, 20, 10 ,30 ,30, 0for (let i in awards) {if (awards[i].probability != 0) {for (var x = 0; x < awards[i].probability; x++) {//把当前的概率数字 以当前选项下标的形式 都添加都空数组中,然后随机这个数组arr.push(i);}}}var s = Math.floor(Math.random() * arr.length);return arr[s];},//不重复抽取//r:随机数 当前选项进行随机_queryRepeat(r) {var that = this, flag = true, repeatArr = wx.getStorageSync('repeatArr'), repeatArr2 = [], awardsConfig = that.data.awardsConfig;if (that.isNull(repeatArr)) {repeatArr2.push(r), wx.setStorageSync('repeatArr', repeatArr2);return r;} else {var len = awardsConfig.awards.length, r = Math.random() * len >>> 0;for (let i in repeatArr) {if (r == repeatArr[i]) {flag = false;if (repeatArr.length == len) {wx.removeStorageSync('repeatArr');repeatArr2.push(r), wx.setStorageSync('repeatArr', repeatArr2);return r;} else {return that._queryRepeat();//递归调用}}}if (flag) {repeatArr.push(r), wx.setStorageSync('repeatArr', repeatArr);return r;}}},//初始化数据时向外传的参_change() {this.triggerEvent('myData', this.data.awardsConfig);// 向父组件传出当前转盘的数组数据},//当前转盘的结果_myAwards() {this.triggerEvent('myAwards', this.data.s_awards)},//转盘开始转动或者结速转动后的要传的值_setatZhuan(e) {this.triggerEvent('startZhuan', e);},}
})

以上就是主要的代码了,下面教你们怎么使用:

在要引用组件的页面的 json 文件中,写上:

{"usingComponents": {"zhuanpan": "/components/zhuanpan/zhuanpan"}
}

这里的 “zhuanpan”,就是你引用到页面时标签的名字,后面是组件的绝对路径

然后在 wxml 页面,引用:

<view style='margin:20px 0;text-align:center;width:100%;'>我帅吗?</view><!-- 引用组件↓↓↓ -->
<zhuanpan id='zhuanpan' bind:myData='getData' bind:myAwards="getAwards" bind:startZhuan="startZhuan" size='{{size}}' musicflg='{{musicflg}}' fastJuedin='{{fastJuedin}}' repeat='{{repeat}}' zhuanpanArr='{{xiaojuedingArr}}' awardsConfig='{{awardsConfig}}'></zhuanpan>
<!-- 引用组件↑↑↑ --><view style='width:100%;margin:5px;text-align:center;'>
转盘声音 <switch type='switch' bindchange="switch1Change1"/>
不重复抽取 <switch type='switch' bindchange="switch1Change2"/>
</view>
<view style='width:100%;margin:5px;text-align:center;'>
快速决定 <switch type='switch' bindchange="switch1Change3"/>
概率 <switch type='switch' bindchange="switch1Change4"/>
</view>

这些自定义属性不一定要全部填写,有需要用到的时候在填,这样就能简单的使用了,因为我在转盘组件的构造器中都有赋予初始值。先来看下只有单独一个标签时候的效果:

像这种不用闭合标签的写法也是可以的哈~~

<zhuanpan />

这样就就算引用成功啦! 只是有点丑,我们可以在 js 文件中在给这些属性赋予初始值下,在设置几个开关方法供你们参考怎么使用:

Page({data: {size: 600,//转盘大小,musicflg: false, //声音fastJuedin: false,//快速决定repeat: false,//不重复抽取probability: false,// 概率s_awards: '',//结果option: '标题',//转盘的总数据,想添加多个可以往这数组里添加一条格式一样的数据zhuanpanArr: [{id: 0,option: '我帅吗?',//转盘的标题名称awards: [{id: 0,                // id递增name: "帅",           // 选项名 超过9个字时字体会变小点 大于13个数时会隐藏掉超出的color: '#FFA827',    // 选项的背景颜色probability: 0       // 概率 0代表永远也转不到这个选项,数字越大概率也就越大,data中的probability属性设置为true时是才生效, 这属性也必须填写,不填写会出错},{id: 1,name: "很帅",color: '#AA47BC',probability: 10},{id: 2,name: "贼帅",color: '#42A5F6',probability: 10},{id: 3,name: "非常帅",color: '#66BB6A',probability: 10},{id: 4,name: "超级帅",color: '#FFA500',probability: 100},{id: 4,name: "宇宙无敌第一帅",color: '#FF4500',probability: 300}]}],//更改数据可以更改这属性,格式要像下面这样写才行awardsConfig: {option: '我帅吗?',//转盘的标题名称awards: [{id: 0,                // id递增name: "帅",           // 选项名 超过9个字时字体会变小点 大于13个数时会隐藏掉超出的color: '#FFA827',         // 选项的背景颜色probability: 0       // 概率 0代表永远也转不到这个选项,数字越大概率也就越大,data中的probability属性设置为true时是才生效, 这属性也必须填写,不填写会出错},{id: 1,name: "很帅",color: '#AA47BC',probability: 10},{id: 2,name: "贼帅",color: '#42A5F6',probability: 10},{id: 3,name: "非常帅",color: '#66BB6A',probability: 10},{id: 4,name: "超级帅",color: '#FFA500',probability: 100},{id: 4,name: "宇宙无敌第一帅",color: '#FF4500',probability: 300}]}},//接收当前转盘初始化时传来的参数getData(e) {this.setData({option: e.detail.option})},//接收当前转盘结束后的答案选项getAwards(e) {wx.showToast({title: e.detail,icon: 'none'})this.setData({s_awards: e.detail,})},//开始转动或者结束转动startZhuan(e) {this.setData({zhuanflg: e.detail ? true : false})},//切换转盘选项switchZhuanpan(e) {//当转盘停止时才执行切换转盘if (!this.data.zhuanflg) {var idx = e.currentTarget.dataset.idx, zhuanpanArr = this.data.zhuanpanArr, obj = {};for (let i in zhuanpanArr) {if (this.data.option != zhuanpanArr[i].option && zhuanpanArr[i].id == idx) {obj.option = zhuanpanArr[i].option;obj.awards = zhuanpanArr[i].awards;this.setData({awardsConfig: obj //其实默认要更改当前转盘的数据要传个这个对象,才有效果})break;}}}},//转盘声音switch1Change1(e) {var value = e.detail.value;if (this.data.zhuanflg) {wx.showToast({title: '当转盘停止转动后才有效',icon: 'none'})return;} else {this.setData({musicflg: value})}},//不重复抽取switch1Change2(e) {var value = e.detail.value;if (this.data.zhuanflg) {wx.showToast({title: '当转盘停止转动后才有效',icon: 'none'})return;} else {this.setData({repeat: value})}},//快速决定switch1Change3(e) {var value = e.detail.value;if (this.data.zhuanflg) {wx.showToast({title: '当转盘停止转动后才有效',icon: 'none'})return;} else {this.setData({fastJuedin: value})}},//概率 == 如果不重复抽取开启的话 概率是无效的switch1Change4(e) {var value = e.detail.value;if (this.data.zhuanflg) {wx.showToast({title: '当转盘停止转动后才有效',icon: 'none'})return;} else {this.setData({probability: value})}},onLoad: function () {//实例化组件对象,这样有需要时就能调用组件内的方法this.zhuanpan = this.selectComponent("#zhuanpan");//可以这样调用 示例:this.zhuanpan.switchZhuanpan(data); //上面这方法可用来切换转盘选项数据,参数可以看组件构造器中的switchZhuanpan方法}
})

注意:初始或切换转盘数据,都要像我上面data中的 awardsConfig 这种格式来定义

最终引用后的效果图:

我帅吗? 哈哈哈~~~  我把 ‘宇宙无敌第一帅’ 概率设为300,其他值设的很小,所以呢很经常转到这个结果。

主要注释都写在文件里了,还不是很懂的也可以下方留言哦!

微信小程序 自定义组件之《转盘》相关推荐

  1. 微信小程序自定义组件方案

    前言:小程序已于11月初开放了小程序组件功能,但事件方面还不是很完善,有的组件暂时可能还是要用其他方式来实现,这里简单记录下开发小程序自定义组件的要点. 在小程序官方开发组件开发功能之前,自定义组件的 ...

  2. 微信小程序自定义组件,提示组件

    微信小程序自定义组件,这里列举了一个常用的提示自定义组件,调用自定义组件中的方法和字段.仅供参考和学习. 编写组件: 在根目录下添加"components"目录,然后像添加Page ...

  3. 微信小程序自定义组件(二)

    微信小程序自定义组件 ps 由于作业部落貌似出了点问题,耽误了点时间,找了一个stackedit.io准备写.无奈,这是要自己建编辑器的节奏啊.没有一个能靠的注 为何存在组件 组件间的关系 使用rel ...

  4. 微信小程序--自定义组件(超详细 从新建到使用)

    微信小程序–自定义组件 微信小程序官网介绍! 本文提供给急需使用自定义组件人群,以下是博主个人理解和案例!可以辅助官网来看 介绍: 从小程序基础库版本 1.6.3 开始,小程序支持简洁的组件化编程.所 ...

  5. 微信小程序自定义组件子传父详解(多图)

    微信小程序自定义组件子传父详解 前言: 刚开始为了测试父传子,所以把页面的数组放在了父组件中 1. 然而子组件中绑定的自定义点击事件依然放在子组件的js文件中 2. 所以就会出现我们点击页面的文字能改 ...

  6. 微信小程序自定义组件的基本使用

    微信小程序自定义组件的基本使用 组件与模块类似,实现了功能的复用,提高开发速率,减少代码量 在开发过程中 , 总会遇到一些功能板块是相同或很类似的 .如两个不同页面都有搜索框 , 或者 导航栏等 . ...

  7. 基于canvas 2D实现微信小程序自定义组件-环形进度条

    基于canvas 2D实现微信小程序自定义组件-环形进度条 最近开发一个小程序项目博闻金榜答题小程序,需要使用到一个可以显示答题倒计时的组件,基于进度条实现,下面就主要介绍基于canvas2D实现一个 ...

  8. 微信小程序自定义组件-树形数据表格(进阶版)

    前言 一.下载引用 二.使用treegrid组件 三.使用文档 属性 事件 四.组件源码 利用递归思想编写的表格行--treegrid-treeline 树形表格--treegrid-treegrid ...

  9. 小程序组件onload_微信小程序自定义组件(一)

    好吧,突然发现学不完了,一下子,那就分开吧,由于时间太久,直接重新大致复习了一下 微信小程序自定义组件 微信小程序支持自定义组件 下方的目录 其中,components为组件目录,nodemodule ...

  10. 一步步教你实现微信小程序自定义组件

    一步步教你实现微信小程序自定义组件 更新时间:2022年03月21日 11:12:34   作者:naluduo233 之前做小程序开发的时候,对于开发来说比较头疼的莫过于自定义组件了,下面这篇文章主 ...

最新文章

  1. HttpServletRequest应用(转)
  2. springmvc 加载 慢_怎么加载spring框架这么慢是不是配置…-就业班
  3. python3高级语法:__slots__属性、property装饰器、上下文管理协议、__new__方法
  4. 【c++】11.重写、覆盖、using、typedef
  5. 剪切粘贴时总是上次的内容_自学PS:拷贝与粘贴都有哪些方法?编辑信息时错误了怎样恢复?...
  6. Web前端培训分享:Web前端到底是什么?
  7. 程序开发入门工具之CodeBlocks
  8. 2017.3.31 棋盘制作 失败总结
  9. 伟大骡子的一生和性能测试
  10. MySql、Oracle、MSSQL中的字符串的拼接
  11. 【 Codeforces Round #395 (Div. 2) D】Timofey and rectangles【四色定理】
  12. centos 下载tomcat8
  13. java ipmi关闭服务器,Dell服务器的IPMI/iKVM使用方法(开机,关机,重启,重装系统)...
  14. 什么是BI工具,好用的BI工具软件排名
  15. linux基础教程 黑鹰基地Linux运维特训班
  16. ​​​​​​​​​​​勾股数的规律
  17. 微信添加地址时选择地区功能是怎么实现的
  18. Android界面编程之利用单选框和复选框实现对学历和爱好进行选择
  19. 点燃我,温暖你(打火机与公主裙)真零基础爱心教程!
  20. 【区块链与密码学】第9-6讲:基于身份的群签名算法 I

热门文章

  1. VIM复制指令yank
  2. cesium之三维漫游飞行效果实现篇(转)
  3. java 熄灯问题_Java算法应用之熄灯问题解决
  4. 百位LOL英雄联盟角色合集
  5. N款在线图片处理工具,让你的效率翻倍
  6. 北京大学冬令营(PKUWC2018)总结
  7. AnimationController
  8. 126邮箱绑定QQ邮箱并微信提醒
  9. 第二人生的源码分析(9)登录界面显示
  10. 实测realme手机丢失定位功能