钢琴约课小程序(微信小程序原生,含用户端和教师端)

发布时间:2022-07-11
技术:微信小程序、html+js

概述

钢琴约课小程序(微信小程序原生,含用户端和教师端),包含排课页面、课程详情、请假、登录、注册、个人中心等。里面是已调用了接口,静态的话,把走接口的数据用静态数据代替即可。

详细


注意:

钢琴约课小程序(微信小程序原生,含用户端和教师端),包含排课页面、课程详情、请假、登录、注册、个人中心等。里面是已调用了接口,静态的话,把走接口的数据用静态数据代替即可。


1、项目结构图

image.png


以下对小程序界面解析介绍:


2、app.css的样式重置封装使用

重要部分如下:

/**
显示方式
*/
.dis-f {display: -webkit-box;display: -webkit-flex;display: -ms-flexbox;display: flex;}
.dis-b{ display: block;}
.dis-ib{   display: inline-block;}
.dis-i{   display: inline;}
/**
背景颜色  
*/
.bg-greenlack {  background: black;}
.bg-o {  background: #ff9018;}
.bg-r {  background: #ff2200;}
.bg-yellow {  background:linear-gradient(to right, #fbca30, #f3b220);}
.bg-green {  background:linear-gradient(to right, #49bf71,#55d67f, #2cb659);}
.bg-gray {  background: #7f8699;}
.bg-w{ background: #ffffff; overflow: hidden;}
.bg-3{ background: #f3f3f3; overflow: hidden;}
.bg-pri{ background: #ff6905;}
.bor{ border:1px solid #f6f6f6;}
.bor-t{ border-top:1px solid #f6f6f6;}
.bor-l{ border-left:1px solid #f6f6f6;}
.bor-b{ border-bottom:1px solid #f6f6f6;}
.bor-r{ border-right:1px solid #f6f6f6;}
.w100{ width: 100%; }
.bor-ra10{ border-radius:10rpx; }
.bor-ra20{ border-radius:10rpx; }
.bor-ra30{ border-radius:10rpx; }
.bor-ra40{ border-radius:10rpx; }
.bor-ra50{ border-radius:10rpx; }
.line{ width: 100%; height: 1px; background: #f3f3f3; clear: both;}
.nobor{ border: none;}
.jszc{ margin-top: 80rpx; color: #999999;}
/**
位置
*/
.f-l{ float: left;}
.f-c{ margin: 0 auto; float:initial;}
.f-r{ float: right;}
.po-r {  position: relative;}
.po-a {  position: absolute;}
.po-f {  position: fixed;}
.po-ft {  position: fixed; top: 0px;left: 0px;}
.po-fb {  position: fixed; bottom: 0px; left: 0px;}
.m0{margin: 0rpx;}
.m10{ margin: 10rpx; }
.m20{ margin: 20rpx; }
.m30{ margin: 30rpx; }
.m40{ margin: 40rpx; }
.mt0{margin-top: 0rpx;}
.mt10{ margin-top: 10rpx;}
.mt20{margin-top: 20rpx;}
.mt30{margin-top: 30rpx;}
.ml0{margin-left: 0rpx;}
.ml10{margin-left: 10rpx;}
.ml20{margin-left: 20rpx;}
.mr0{margin-right: 0rpx;}
.mr10{margin-right: 10rpx;}
.mr20{margin-right: 20rpx;}
.mb0{margin-bottom: 0rpx;}
.mb10{margin-bottom: 10rpx;}
.mb20{margin-bottom: 20rpx;}
.mb30{margin-bottom: 30rpx;}
.m020{margin:0 20rpx;}
.p0{ padding: 0rpx; }
.p10{ padding: 10rpx; }
.p20{ padding: 20rpx; }
.p30{ padding: 30rpx; }
.pt0{padding-top: 0rpx;}
.pt10{padding-top: 10rpx;}
.pt20{padding-top: 20rpx;}
.pl0{padding-left: 0rpx;}
.pl10{padding-left: 10rpx;}
.pl20{padding-left: 20rpx;}
.pr0{padding-right: 0rpx;}
.pr10{padding-right: 10rpx;}
.pr20{padding-right: 20rpx;}
.pb0{padding-bottom: 0rpx;}
.pb10{padding-bottom: 10rpx;}
.pb20{padding-bottom: 20rpx;}
.p010{padding:0 10rpx;}
.p020{padding:0 20rpx;}
.p030{padding:0 30rpx;}
.bg-sub{ background: #7479f6;}
.all-tip{  height: 80rpx; padding: 0 30rpx; color: #3f3f3f; line-height: 80rpx; overflow: hidden; background: #f0e4c1;}
.hide{ display: none; opacity: 0;}
.show{ display: block; opacity: 1;}
.line-30{line-height: 30rpx;}
.line-40{line-height: 40rpx;}
.line-50{line-height: 50rpx;}
.line-60{line-height: 60rpx;}
.line-70{line-height: 70rpx;}
.line-80{line-height: 80rpx;}
.line-90{line-height: 90rpx;}
.line-100{line-height: 100rpx;}
.h-30{height: 30rpx; overflow: hidden;}
.h-40{height: 40rpx; overflow: hidden;}
.h-50{height: 50rpx; overflow: hidden;}
.h-60{height: 60rpx; overflow: hidden;}
.h-70{height: 70rpx; overflow: hidden;}
.h-80{height: 80rpx; overflow: hidden;}
.h-90{height: 90rpx; overflow: hidden;}
.h-100{height: 100rpx; overflow: hidden;}
.h-120{height: 120rpx; overflow: hidden;}
.h-140{height: 140rpx; overflow: hidden;}
.h-160{height: 160rpx; overflow: hidden;}
.h-180{height: 180rpx; overflow: hidden;}
.h-200{height: 200rpx; overflow: hidden;}
.ovh{ overflow: hidden;}
/**
字体
*/
.t-c{ text-align: center;}
.t-l{ text-align: left;}
.t-r{ text-align: right;}
.f-18{ font-size: 18rpx;}
.f-24{ font-size: 24rpx;}
.f-26{ font-size: 26rpx;}
.f-28{ font-size: 28rpx;}
.f-30{ font-size: 30rpx;}
.f-32{ font-size: 32rpx;}
.f-34{ font-size: 34rpx;}
.f-36{ font-size: 34rpx;}
.f-38{ font-size: 34rpx;}
.f-40{ font-size: 40rpx;}
.f-45{ font-size: 45rpx;}
.f-50{ font-size: 50rpx;}
.f-55{ font-size: 55rpx;}
.f-60{ font-size: 60rpx;}
.ellipsis {  text-overflow: ellipsis;  white-space: nowrap;  overflow: hidden;  display: block;}
.bold{ font-weight: bold;}
/**
字体颜色
*/
.co-r{ color: #ee2b2b;}
.price{ color: #ff0000;}
.co-w{ color: #ffffff;}
.co-o{ color: #ff9000}
.co-g{ color:green;}
.co-y{ color: #fbd051}
.co-0{ color: #000000;}
.co-3{ color: #333333;}
.co-6{ color: #666666;}
.co-9{ color: #999999;}
.co-gray{color: #7f8699}
.ico1{ color:#ff1260;}
.ico2{ color:#fa741b;}
.ico3{ color:#7a67ff;}
.ico4{ color:#2cba80;}
.ico5{ color:#7479f6;}
.ico7{ color:#ff9018;}
/**
按钮
*/
.btn96{ height: 80rpx; line-height: 80rpx;font-size: 40rpx; background: #ec4e43; color: #ffffff; text-align:center; margin:40rpx auto;}
.btn96{ width:96%;}
.btn100{ width:100%;}
.btn-bottom{position: fixed; bottom: 0px; left: 0px; background: #7479f6; font-size: 36rpx; text-align: center;color: #ffffff; height: 80rpx; line-height: 80rpx;border-radius: 0px; width: 100%; }
.btn-sub{ border: 1px solid #999999; font-size: 28rpx; text-align: center;   border-radius: 10rpx; padding: 5rpx 20rpx; color: #999999; margin: 0 auto; display: block; line-height: 50rpx; height: 50rpx;  }
/**
栅格布局
*/
.row {  display: block;  margin: 0px;}
.col {  display: flex;  font-family: -apple-system-font, "Helvetica Neue", sans-serif;  font-size: 30rpx;}
.col>.col-1,.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9,.col-10, .col-11, .col-12 {  overflow: hidden;float: left;}
.col-1 {  width: 8.33333333333333%;}
.col-2 {  width: 16.6666666666666%;}
.col-3 {  width: 25%;}
.col-4 {  width: 33.3333333333333%;}
.col-5 {  width: 41.6666666666666%;}
.col-6 {  width: 50%;}
.col-7 {  width: 58.333333333333333%;}
.col-8 {  width: 66.66666666666666%;}
.col-9 {  width: 75%;}
.col-10 {  width: 83.33333333333333%;}
.col-11 {  width: 91.66666666666666%;}
.col-12 {  width: 100%;}
.img40{ width: 80rpx; height: 80rpx;}
.img60{ width: 120rpx; height: 120rpx;}
.img80{ width: 160rpx; height: 160rpx;}
.img100{ width: 200rpx; height: 200rpx;}
.z-99{ z-index: 99;}
.navigator-hover { opacity: 1; background: none;}
.kongbai{ height: 100rpx; width: 100%; overflow: hidden;}

小程序DOM使用如下:

image.png


3、VIEW的部分源码展示

<view class="container">
  <!--轮播图 GO-->
  <view class="swiper-box ovh po-r index-banner">
      <swiper indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}">
        <block   wx:for='{{banner}}' wx:key="index">
          <swiper-item data-index='{{index}}'>
            <navigator url="{{item.url}}" hover-class="navigator-hover">
              <image src="{{item.img_src}}" class="slide-image" width="355" height="200" />
            </navigator>
          </swiper-item>
        </block>
      </swiper>
    </view>
  <!--轮播图 END-->
  <!--功能图标 GO-->
  <view class='menu  bg-w  ovh  css3' wx:if="{{nav}}">
    <view class='items'>
      <view class="item po-r" wx:for="{{nav}}"  wx:key="index">
        <navigator open-type='switchTab' url='{{item.url}}' wx:if="{{item.url=='/pages/index/index' || item.url=='/pages/user/course-res' || item.url=='/pages/user/index' }}">
          <image class="fiximg" src="{{item.img_src}}"></image>
          <text class='co-3 f-30 t-c dis-b'>{{item.title}}</text>
        </navigator>
        <navigator url='{{item.url}}' wx:else>
          <image class="fiximg" src="{{item.img_src}}"></image>
          <text class='co-3 f-30 t-c dis-b'>{{item.title}}</text>
        </navigator>
      </view>
    </view>
  </view>
  <!--功能图标 END-->
  <view class='navbg ovh'>
    <text class='title line-80 t-c f-l p020 bold'>全部课程</text>
    <icon class='line-80 t-c f-r p020 iconfont iconquanbu' bindtap="showviewfc"></icon>
  </view>
  <!--按周显示课程 GO-->
  <block wx:if="{{curriculum}}">
    <view class="menu1 line-80 bg-w ovh">
      <text bindtap="tab" data-type="1" class="{{startweek==1 ? 'active' : ''}}">周一</text>
      <text bindtap="tab" data-type="2" class="  {{startweek==2 ? 'active' : ''}}">周二</text>
      <text bindtap="tab" data-type="3" class=" {{startweek==3 ? 'active' : ''}}">周三</text>
      <text bindtap="tab" data-type="4" class=" {{startweek==4 ? 'active' : ''}}">周四</text>
      <text bindtap="tab" data-type="5" class=" {{startweek==5 ? 'active' : ''}}">周五</text>
      <text bindtap="tab" data-type="6" class=" {{startweek==6 ? 'active' : ''}}">周六</text>
      <text bindtap="tab" data-type="7" class=" {{startweek==7 ? 'active' : ''}}">周日</text>
    </view>
  <!--课程列表 GO-->
    <view class='p30 bg-w ovh m-nav pr0'>
      <view class='item po-r ovh' wx:key="key" wx:for="{{curriculum}}">
        <navigator class='dis-ib' url='/pages/course/detial-list?id={{item.id}}'>
          <form bindsubmit='submit' report-submit='true' data-type="submit">
            <image mode="aspectFill" class='f-l mt20' src='{{item.coach.avatar}}'></image>
            <view class='wrap'>
              <view class='first pl20 line-40 h-40  f-32 ellipsis t-l'>{{item.title}}</view>
              <view class='cont pl20  f-28 ellipsis t-l co-9'>教师:{{item.coach.nickname}}</view>
              <view class='cont pl20   f-28 ellipsis t-l co-9'>履历:{{item.coach.bio}}</view>
              <view class='cont pl20   f-28 ellipsis t-l co-9'>时间:{{item.starttime}}</view>
              <view class='cont pl20   f-28 ellipsis t-l co-9'>地点:{{item.address}}</view>
            </view>
          </form>
        </navigator>
      </view>
    </view>
  </block>
  <!--课程列表 GO-->
  <block>
    <view class='p30 bg-w ovh pb0 mt30'>
      <text class='f-l co-3'>推荐课程</text>
      <navigator url='/pages/course/list' class='dis-ib f-r'>
        <text class='f-r ico7 f-24'>查看更多</text>
      </navigator>
    </view>
    <view class='p30 bg-w ovh m-children t-c'>
      <image mode='aspectFit' src='../../images/child.png'></image>
    </view>
  </block>
  <!-- 底线 -->
  <view class="title-footer p-r">
    <text class="f-24 co-9 cont t-c">已经到底啦</text>
    <view class="hr"></view>
  </view>
  <!-- 返回顶部 -->
  <view bindtap="goTop" class="widget-goTop" wx:if="{{floorstatus}}">
    <text class="iconfont icon-fanhuidingbu"></text>
  </view>
  <!--课程分类弹窗-->
  <view class='searbox po-f ovh bg-w css3 {{showview?"scrolll":"scrollr"}}'>
    <view class='po-r ovh p20'>
      <text class='f-r co-9 po-a close' bindtap='showhide'>关闭</text>
      <scroll-view class="scroll-view_H t-c" scroll-y style="width: 100%; height:94vh;padding-top:6vh;" catchtouchmove="true">
      <view class='seccat ovh p020 mb10 dis-ib co-9'  bindtap='changeid' data-id='' wx:key="{{key}}">全部</view>
        <view class='seccat ovh p020 mb10 dis-ib co-9' wx:for="{{catelist}}" bindtap='changeid' data-id='{{item.id}}' wx:key="index">{{item.name}}</view>
      </scroll-view>
    </view>
  </view>
</view>


view展示注意点:

1、在小程序开发工具中,不影响运行,但是要呈现演示数据,需要js中把api的变量定义成静态数据。

2、页面中学生端和教师端是通过判断用户的教师id来区分展示对应身份的界面,所以部分涉及身份的页面是连段代码,谨记。


4、JS首页完整代码展示

let App = getApp();

Page({
  data: {
    // banner轮播组件属性
    indicatorDots: true, // 是否显示面板指示点 
    autoplay: true, // 是否自动切换
    interval: 3000, // 自动切换时间间隔
    duration: 800, // 滑动动画时长
    imgHeights: {}, // 图片的高度
    imgCurrent: {}, // 当前banne所在滑块指针
    show: false,
    showview: false,
    items: {},
    category_id: '',
    startweek: 1,
    startPoint: ''
  },

  onLoad: function() {
    // 设置页面标题
    App.setTitle();
    // 获取首页数据
    this.getHomeData(); 
    this.getCategorylist();
    this.getIndexCurriculum();
  },

  onShow: function() {
    this.onLoad();
  },
  /**
   * 获取首页数据
   */
  getHomeData: function() {
    let that = this;
    App._get('index/index', {}, function(res) {
      that.setData(res.data);
    });
  },

  /**
   * 课程分类列表
   */
  getCategorylist: function() {
    let that = this;
    App._get('index/curriculumCategorylist', {}, function(result) {
      that.setData({
        catelist: result.data
      });
    });
  },

  /**
   * 获取分类id
   */

  changeid: function(e) {
    let that = this;
    var category_id = e.currentTarget.dataset.id;
    that.setData({
      category_id: category_id
    });
    that.getIndexCurriculum();
    that.showhide();
  },
  //触发分类弹窗
  showviewfc: function () {
    var that = this;
      that.setData({
        showview: true
      })
  },
  //关闭分类弹窗
  showhide: function () {
    var that = this;
    that.setData({
      showview: false
    })
  },

  /**
   * 获取 首页课程数据
   */
  getIndexCurriculum: function() {
    let that = this;
    App._get('index/curriculumlist', {
      startweek: that.data.startweek,
      category_id: that.data.category_id
    }, function(result) {
      that.setData(result.data);
    });
  },

  //周别切换
  tab: function(e) {
    var startweek = e.currentTarget.dataset.type;
    this.setData({
      startweek: startweek
    });
    this.getIndexCurriculum();
  },

  /**
   * 计算图片高度
   */
  imagesHeight: function(e) {
    let imgId = e.target.dataset.id,
      itemKey = e.target.dataset.itemKey,
      ratio = e.detail.width / e.detail.height, // 宽高比
      viewHeight = 750 / ratio, // 计算的高度值
      imgHeights = this.data.imgHeights;
    // 把每一张图片的对应的高度记录到数组里
    if (typeof imgHeights[itemKey] === 'undefined') {
      imgHeights[itemKey] = {};
    }
    imgHeights[itemKey][imgId] = viewHeight;
    // 第一种方式
    let imgCurrent = this.data.imgCurrent;
    if (typeof imgCurrent[itemKey] === 'undefined') {
      imgCurrent[itemKey] = Object.keys(this.data.items[itemKey].data)[0];
    }
    this.setData({
      imgHeights,
      imgCurrent
    });
  },

  //首页分享
  onShareAppMessage: function() {
    return {
      title: "小程序首页",
      desc: "",
      path: "/pages/index/index"
    };
  },
  /*
   *下拉显示
   */
  onPullDownRefresh() {
    var that = this;
    that.setData({
      show: true
    })
  },

  /**
   * 页面上拉触底事件的处理函数
   */
  move: function(e) {
    var curPoint = [e.touches[0].pageX, e.touches[0].pageY];
    var starPoint = this.data.startPoint;
    that.setData({
      show: false
    })
  },

});


5、教师端界面截图

image.pngimage.pngimage.pngimage.pngimage.pngimage.png


最后:

如果需要了解体验小程序后台及api、数据库可通过平台联系我。




本实例支付的费用只是购买源码的费用,如有疑问欢迎在文末留言交流,如需作者在线代码指导、定制等,在作者开启付费服务后,可以点击“购买服务”进行实时联系,请知悉,谢谢
手机上随时阅读、收藏该文章 ?请扫下方二维码