都2020年了,你还不会小程序开发吗

零基础手把手教你简单微信小程序项目实战开发

基于微信小程序的天气预报终端设计

本次开发是利用微信开发者工具Nightly V1.02版本进行开发的。
开发语言是WXML+WXSS+JS
天气查询设有当前天气,未来三天,一周天气,24小时温度及气温变化,相关生活指数,其他城市天气查询等功能。

写在前面的几句话

第一次写CSDNblog,可能存在许多排版等基本问题,大家见谅。代码借鉴GitHub上面的作者@jiulanrensan、@AsherSun,侵删。另外加上自己的整合,有了自己的一个项目。从网页前端过来学小程序,最开始玩微信小程序纯出于好奇兴趣,想了解小程序逻辑层,渲染层如何工作,进程加载,线程并发。本文主要通过天气预报小程序如何工作来说说如何获取用户位置信息,调用第三方API接口来传入数据。记录下这次简单的开发过程,也方便我以后再回头查阅。本文撰写的如果有何错误,直接评论区指出,轻喷。源码0积分即可下载学习操作。网盘链接我也放儿,提取码5hdp

先看效果图

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

在这里插入图片描述地图现在页没弄好,有点小bug
还没做好,嘿嘿

1项目设计内容

(1)首页登录模块能获取用户登录的信息,显示用户头像,并且有2个接口能进入天气查询和计算器。
(2)天气查询模块能显示当天的天气信息:温度,风向,风力,阴晴,PM2.5,未来三天,一周天气,24小时温度及气温变化,相关生活指数,其他城市天气查询和新冠肺炎疫情查询。
(3)计算器模块能做简单的15位的+、-、*、/、%运算,用户输入的不符合运算规则,条件语句判断后,控制台打印NaN,不做任何操作.
(4)将这3个板块整合到一起,具有良好的页面跳转和交互功能。

1.1整体思路

本次开发是利用微信开发者工具Nightly V1.02版本进行开发的,开发人员需要具备WXML+WXSS+JS三种前端语言基础即可开发调试。该开发工具有编辑器,模拟器,调试器,代码在编辑器内被编译运行后其效果会实时显示在模拟器上,如果编译不通过控制台会报错,提示报错信息,开发者可以在调试器上单步调试运行。模拟器选择的是iPhone6/7/8机型,网络选择为WiFi。本项目是设计天气预报加简易计算器的应用,主要介绍主板块天气查询,副板块简易计算器的开发难度不是很大,但也有很多细节之处。整体项目,分为三个大页面,主页面index,作为首页上面显示用户信息、头像。下面有2个模块,天气查询weather和计算器calculator,用户根据需求自行点击进入。我们用到微信平台提供给用户的大概设计模板,这里包括系统初始化组件app.json、app.js、app.wxss、project.config.json,是为了对整个项目进行初始化配置,这部分开发者不用担心,只需在上面进行操作修改为用户指定样式就行。由于在天气查询页面会实时获取用户的动态地理位置,需要用到百度地图的微信小程序JSAPI接口,因此我们还需要在https://lbsyun.baidu.com/上下载第三方工具链bmap-wx.min.js作为本项目的库函数封装在libs中,我们在设计开发该项目时,为了后期的维护和修改,将页面布局、页面样式、页面效果分别单独封装起来。另外在天气查询页面由于气候是多种的,我将"晴", “多云”, “阴”, “阵雨”, “雷阵雨”,“小雪”, “中雪”, “大雪”, “暴雪”, “雾”, “冻雨”,……气候类型抽离出来单独封装,并用数组、指针进行循环遍历,达到动态实时现实天气的目的。我将本次将要用到的png图片和icon图标都放在img文件夹下,当要用到时,直接绝对路径引用或者数组循环遍历。

2项目开发设计的总目录结构

在这里插入图片描述
libs库文件夹下放的是百度mapAPI文件;
pages文件夹下放index首页,weather天气查询,calculator计算器,city城市天气查询,virus新冠疫情查询
src文件夹下放icon图标和img背景图;
style文件夹下放整个页面的布局样式;
utils文件夹下放整个项目所用到的工具类;
project.config.json,site.json都是小程序逻辑层和渲染层的配置;
app.json,app.js,app.wxss用来实现页面的交互和跳转。

3项目的详细设计及功能的实现

3.1认识开发者工具及搭建环境

3.1.1注册账号申请APPID

1,注册账号。打开微信公众平台(https://mp.weixin.qq.com)右上角立即注册。
在这里插入图片描述
2,选择需要注册的账号类型,这里选择小程序-个人,在根据指引填写资料提交。
在这里插入图片描述
3,完成邮箱激活,并登记信息就可以有账号。
在这里插入图片描述
安装开发者工具
登录小程序后台 ,我们可以在菜单 “首页”-“设置”-“基本设置” 看到小程序的 AppID 了 ,小程序的 AppID 相当于小程序平台的一个身份证。
在这里插入图片描述
在这里插入图片描述

3.1.3安装并认识开发者工具

有小程序帐号之后,我们需要一个工具来开发小程序。前往开发者工具下载页面https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 根据自己的操作系统下载对应的安装包进行安装。打开小程序开发者工具,用微信扫码登录开发者工具即可。
在这里插入图片描述
登录页
在登录页,可以使用微信扫码登录开发者工具,开发者工具将使用这个微信帐号的信息进行小程序的开发和调试。
在这里插入图片描述
主界面
开发者工具主界面,从上到下,从左到右,分别为:菜单栏、工具栏、模拟器、资源管理器、编辑区、调试器 六大部分。
在这里插入图片描述

3.1.4登录开发者工具并创建项目

进入开发者工具扫码登陆,后点击小程序-创建新项目-选择文件存储路径E盘的本科毕业设计路径下-选择小程序名字本科毕业设计-小程序AppID:wxe344903caa37d
58f,创建成功进入到项目。
当符合以下条件时,可以在本地创建一个小程序项目
1.需要一个小程序的 AppID;如没有 AppID,可以选择申请使用测试号。
2.登录的微信号需要是该 AppID 的开发者;
3.需要选择一个空目录,或者选择的非空目录下存在 app.json 或者 project.config
.json。当选择空目录时,可以选择是否在该目录下生成一个简单的项目。
在这里插入图片描述

3.1.5搭建环境及配置项目

我们首先要做的是初始化项目配置,可以看到微信已经已经为开发者提供了一套Hello World程序。登录wx.login({success: res => {发送 res.code 到后台换取 openId, sessionKey, unionId,wx.getUserInfo({success: res => 可以将 res 发送给后台解码出 unionId, this.globalData.userInfo = res.userInfo,由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回,所以此处加入 callback 以防止这种情况,获取用户信息wx.getSetting({success: res => {if (res.authSetting[‘scope.userInfo’]) ,已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框。由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回, 在没有 open-type=getUserInfo 版本的兼容处理,所以此处加入 callback 以防止这种情况。点击即可获取头像昵称,微信授权允许,即可看到用户头像。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//index.js
//获取应用实例
var app = getApp()
Page({
    
    
  data: {
    
    
    userInfo: {
    
    
      avatar: '../../src/img/menu-img.jpg',
      name: "金阳",
      post: "前端开发实习生",
      desc: "本科毕业设计!欢迎大家使用我的小程序,若有好的意见或建议,请发到邮箱[email protected]"
    }
  },

  onShareAppMessage: function () {
    
    
    return {
    
    
      title: '欢迎转发我的小程序',
      path: '/pages/index/index.js'
    }
  },
  //事件处理函数
  weatherSearch: () => {
    
    
    wx.navigateTo({
    
    
      url: '../bar/index'
    });
  },
  calc: () => {
    
    
    wx.navigateTo({
    
    
      url: '../calculator/calculator'
    });
  },
  virus: () => {
    
    
    wx.navigateTo({
    
    
      url: '../virus/virus'
    });
  },
  map: () => {
    
    
    wx.navigateTo({
    
    
      url: '../map/map'
    });
  },
  calendarList: () => {
    
    
    wx.navigateTo({
    
    
      url: '../calendar/index'
    });
  },
  onLoad: function () {
    
    

  }
})

3.2天气查询

3.2.1登录首页

根据上文分析的设计框架,我们现在pages目录下依次创建index、weather、city、virus的文件夹。并在weather文件下创建weather.wxml、weather.wxss、weather.js、weather.json、在calculator目录下创建calculator.wxml、calculator.wxss、calculator.js、calculator.json、新建src文件夹,在src下面创建img文件夹和icon文件夹,分别用来存放背景图片.png和天气图标,ec-canvas文件夹用来存放天气元素的动态画布。要理清楚目录树的结构层次,才能在后面的页面跳转时做好代码的调用。我事先将我的用户头像放在src/img/menu-img.jpg路径下,在index.wxml中通过调用系统函数userinfo-avatar,判断用户是否上传了头像,有就显示没有就显示默认,调用userInfo.name函数,系统自动获取用户名字。 定义一对 标签,为“实用工具”,我们再在这对标签里面分别并列定义“天气&疫情”和“计算器”标签,再去index.wxss定义样式,用户信息显示为宽度100%,高度自动,文字居中显示,用户名宽度100%,高度2像素(用iPhone6作为模拟机,其整个屏幕是750像素),转到index.js页面,我们在Page的data中可以编辑 userInfo的文本文字内容,我们给文字设置下样式,文字大小0.8像素,文字居中左对齐字符间距0.2像素。
在这里插入图片描述

/**index.wxss**/
.placeholder{
    
    
    margin: 5px;
    padding: 0 10px;
    text-align: center;
    background-color: #EBEBEB;
    height: 2.3em;
    line-height: 2.3em;
    color: #333;
}
.userinfo-show{
    
    
  width: 100%;
  height: auto;
  margin-top: 1em;
  overflow: hidden;
  text-align: center;
}
.userinfo-avatar{
    
    
  width: 8em;
  height: 8em;
  margin: 0 auto;
  display: block;
  border-radius: 50%;
}
.userinfo-nickname{
    
    
  width: 100%;
  height: 2em;
  line-height: 2em;
  font-size: 1.2em;
  font-weight: bold;
}
.userinfo-post{
    
    
  font-size: .9em;
  color: #8B8B83;
  padding:0 2em;
}
.userinfo-desc{
    
    
  font-size: .8em;
  color: #B3B3B3;
  text-align: left;
  padding:1em 2em;
  text-indent: 1.6em;
}

3.2.2当天及未来三天天气信息的函数封装

先在weather.json中设置navigationBarTitleText导航栏名字为天气查询,该页面就配置完成,在weather.wxml设置天气界面的显示背景图,我们是外部引用map.png的方式作为该页面的大背景底色,模式为aspectFit,保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。图片版式为cover,重复覆盖,因为考虑到不同的机型,不同的操作系统,有可能使得图片失真,这样做就会在不同的机型上显示效果都是一样的比例。先申明todyWeather.style函数,其中包括了todayWeather.city、todayWeather.realtimeTemperature、todayWeather.temperature、todayWeather.wind、todayWeather.pm25函数。在weather.wxml中申明futureThreeDay函数,给该函数定义关键字key,并用*this指针指向该变量的地址,在该函数下调用weather-item下面的item.date函数,把未来三天的天气信息传给futureThreeDay函数。给未来三天天气添加ui图标元素,调用iconItem.src,将天气图标传入进来。调用item.temperature函数,能显示未来三天的温度。
在这里插入图片描述

3.2.3 设置24小时天气和页面动态特效

在weather.wxss中我们可以为刚刚在在wxml页面设置的元素配置样式,显示样式为block块级显示,不隐藏;地图图标设置高度20像素,宽度20像素,水平居中显示;实时温度显示字体大小为5像素,文字水平居中,高度2像素,宽度100%,不溢出;天气信息左对齐,字体大小14像素,行高22像素,高度22像素;天气图标高度5像素,宽度5像素;PM2.5信息高度30像素,行高30像素,右对齐,距离上部15像素,边框1像素,白色实线,圆角边框,宽度自动。这时候我们去weather.js中定义这些函数。在Page中的data里面定义变量todayWeather,在事件处理函数dealTodayData: function(data)中,给函数传一个data传参,定义返回值result来接受天气信息的结果。
在这里插入图片描述

3.2.4 天气类相关生活指数

在天气查询的下面有相关生活指数的提醒,定义生活指数,绑定用户点击事件,设置hover样式为none,给定背景图片,在天气类的wxss里设置一周的天气变化,气温,平均气温,风向,温度外部引用第三方百度mapAPI来传参。markLine:data: new Array(nowData.length).keys()].map((ele,index)用来显示表格虚线,通过eventChannel向被打开页面传送数据res.eventChannel.emitaccept DataFromOpenerPage.
在这里插入图片描述

3.2.5 设置页面效果

我们知道,一年的天气变化是很多的,我们不可能将这些所有的天气信息都封装在weather.js,这样做起来,很不利于后面的维护和二次开发,所以我们这里将各类天气信息抽离出来单独封装在weather文件夹下的util.js.我将所有的天气信息定义在数组里	let _arr = ["晴", "多云", "阴", "阵雨", "雷阵雨", ……],用变量str来指向这个数组,通过索引数组下标+1的方法来,用for循环来遍历数组,取出对应的天气信息,同样的,这种方式也可以取出天气对应的图标。用系统的外部引入函数module.exports来引入温度的事件处理函数dealTemperature: (t),将传参指向刚刚定义的数组,能得到此天气下对应的温度。这一步其实就要给图表加上数据,采用 wx.request传入url地址,这里我们调用函数usingComponents": { "ec-canvas": "../../ec-canvas/ec-canvas"采用canvas画布来实现整个页面的动态效果。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426173235669.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0ODA3NzU2,size_16,color_FFFFFF,t_70)

3.2.6城市天气查询
现在大多数天气查询App都有二级页面来定点查询别的城市天气,这里我预留了天气查询的借接口,位于页面的左上(见图3-2-5),在city.wxml里面设置页面布局,城市管理,热门城市searchCity(e)var newList = app.globalData.cities;

在这里插入图片描述
在city.wxss里面给宽高样式,调用函数canvas画布来做整个页面的背景色 显示样式display: flex;文字样式justify-content: space-between;宽width: 90%;页边距padding: 10rpx 0; margin: 0 auto;边框box-sizing: border-box;文字居中align-items: center
在这里插入图片描述
在城市查询板块下有热门城市管理 热门城市,还是给定宽高样式.hotCity{width: 84%; margin: 0 auto;} 字体大小font-size: 20rpx;字体颜色color: rgba(255, 255, 255);边距margin-top: 40rpx;margin-bottom: 10rpx;文字居左text-align: left;宽width: 100%
在这里插入图片描述

import * as echarts from '../../ec-canvas/echarts';

let chart = null;let chart2 = null;
var app =  getApp();
var qurl = 'https://www.tianqiapi.com/api?version=v1&appid=23035354&appsecret=PGgTiHl1&city='+ wx.getStorageSync("cityNow")
function setOption(chart, xdata, ydata) {
    
    
  var option = {
    
    
    title: {
    
    
      text: '  未来一周天气变化',
      textStyle:{
    
    
        color:"#FFFFFF",
        fontSize:16,
        fontWeight:"normal"
      },
      textAlign:'auto'
    },
    onShareAppMessage: function () {
    
    
      return {
    
    
        title: '欢迎转发我的小程序',
        path: '/pages/bar/index.js'
      }
    },
  tooltip: {
    
    
      trigger: 'axis',
      show:true,
      formatter:'{a0}: {c0}\n{a1}: {c1}\n{a2}: {c2}'
  },
  xAxis: {
    
    
      type: 'category',
      boundaryGap: false,
      data: xdata.map((ele,ind)=>{
    
    
        return ind===0?("今天"):ele.week
      }),
      axisLine:{
    
    
        lineStyle:{
    
    
          color:"#FFFFFF"
        }
      }
  },
  yAxis: [{
    
    
      min:function(value){
    
    
        return value.min
      },
      type: 'value',
      axisLabel: {
    
    
          formatter: '{value}'
      },
      name:"气温(℃)",
      nameLocation:"end",
      axisLine:{
    
    
        lineStyle:{
    
    
          color:"#FFFFFF"
        }
      }
  },{
    
    
    name:"",
    type: 'value',
    axisLine:{
    
    
      lineStyle:{
    
    
        color:"#FFFFFF"
      }
    }
  }],
  series: [
      {
    
    
          name: '平均气温',
          type: 'line',
          data: ydata.map((ele,ind)=>{
    
    
            return {
    
    
              value:ele.tem.replace("℃","")
            }
          }),
          smooth:true,
          symbol:"none"
      },
      {
    
    
          name:"风向",
          type:'line',
          yAxisIndex:1,
          data: ydata.map((ele,ind)=>{
    
    
            return {
    
    
              value:ele.win[0]+ele.win_speed.replace("<","小于")
            }
          })
      },
      {
    
    
        name:"天气",
        type:'line',
        yAxisIndex:1,
        data: ydata.map((ele,ind)=>{
    
    
          return  {
    
    
            value:ele.wea
          }
        })
      }
  ]
  };
  chart.setOption(option)
}
function setOption2(chart, xdata, ydata) {
    
    
  var nowData = ydata[0].hours.concat(ydata[1].hours).filter((ele,ind)=>{
    
    
    if(parseInt(ele.day.match(/[\u4e00-\u9fa5](\d{2})[\u4e00-\u9fa5]/)[1]) >= new Date().getHours() || parseInt(ele.day.match(/\d{2}/)[0]) > new Date().getDate()){
    
    
      return true
    }
  });
  function getMarkLine(ind){
    
    
      return [{
    
    
        symbol:"none",
        lineStyle:{
    
    
          color:"white"
        },
        coord:[ind,0]
      },{
    
    
        symbol:"none",
        lineStyle:{
    
    
          color:"white"
        },
        coord:[ind,parseInt(nowData[ind].tem.replace("℃",""))]
      }
    ]
  }
  var option = {
    
    
    title: {
    
    
      text: ''
    },
    grid:{
    
    
      left:20,
      top:30,
      right:20,
      bottom:40
    },
    xAxis: {
    
    
        type: 'category',
        show:true,
        boundaryGap:false,
        data: nowData.map((ele,ind)=>{
    
    
          return ele.day.match(/[\u4e00-\u9fa5](\d{2})[\u4e00-\u9fa5]/)[1]+"时"+"\n"+ele.wea
        }),
        axisLine:{
    
    
          show:false,
          lineStyle:{
    
    
            color:"#FFFFFF"
          }
        },
        "axisTick":{
    
         
          "show":false
        },
    },
    yAxis: [{
    
    
        type: 'value',
        show:false 
    }],
    series: [
        {
    
    
            name: '气温',
            type: 'line',
            data: nowData.map((ele,ind)=>{
    
    
              return ele.tem.replace("℃","")
            }),
            smooth:false,
            itemStyle : {
    
     normal: {
    
    
              label : {
    
    show: true},
              lineStyle:{
    
    
                color:"white"
              },
              color:"white"
            }},
            markLine:{
    
    /*显示虚线*/
                data: [...new Array(nowData.length).keys()].map((ele,index)=>{
    
    
                  return getMarkLine(index)
                })
              }
        }
    ]
  };
  chart.setOption(option)
}
Page({
    
    
  data: {
    
    
    weather:[],
    rain:false,
    three:[],
    ec:{
    
    
      lazyLoad:true
    },
    ec2:{
    
    
      lazyLoad:true
    },
    weatherInShortTerm:""
  },
  switch(){
    
    
    var that = this;
    wx.navigateTo({
    
    
      url: '/pages/city/city?style='+this.data.weather[0].wea_img,
      success: function(res) {
    
    
        // 通过eventChannel向被打开页面传送数据
        res.eventChannel.emit('acceptDataFromOpenerPage', {
    
    cityData:that.data.weather[0],cityName:that.data.location})
      }
    })
  },
  configure(){
    
    
    wx.openSetting({
    
    })
  },  
  initChart: function (xdata, ydata) {
    
       
    this.eComponent.init((canvas, width, height) => {
    
    
        const chart = echarts.init(canvas, null, {
    
    
            width: width,
            height: height
        });
        setOption(chart, xdata, ydata)
        this.chart = chart;
        return chart;
    });
    this.eComponent2.init((canvas, width, height) => {
    
    
      const chart2 = echarts.init(canvas, null, {
    
    
          width: width,
          height: height
      });
      setOption2(chart2, xdata, ydata)
      this.chart2 = chart2;
      return chart2;
  });
  },

3.2.8关于第三方API接口调用说明

项目天气接口来源于天气API、疫情接口来源于腾讯新闻。到这里,我们已经天气、新冠疫情等元素、样式、布局、天气信息和各种函数都封装完成,它们都通过API接口发送网络请求返回json的数据格式。这里我们要借助第三方工具来获取位置信息。我们需要调用百度地图的微信小程序API接口才能实时获取位置信息。第一步:先到百度开放平台(http://lbsyun.baidu.com/index.php?
title=wxjsapi/guide/key)申请ak,进入百度开放平台官网,点击右上角API控制台,登录百度账号,根据提示填写正确的邮箱,手机号码完成开发者注册。
在这里插入图片描述

第二步:再次点击进入API控制台,创建一个新应用 ,在创建的应用页面,填写应用名称,选择应用类型为“微信小程序”,勾选启用服务,填写AppID,点击提交即可看到申请成功的密钥(ak)。
在这里插入图片描述

第三步申请ak成功后在API控制台-我的应用里面就可以看到,复制ak,去微信公众平台的后台配置request合法请求域名。
在这里插入图片描述
在这里插入图片描述

第四步,下载百度地图的api,将下载好的文件解压拷贝到libs文件夹下,
第五步:在weather.js下引入我们下载的百度地图api的js文件,用绝对路径的方式引用let bmap = require(’…/…/libs/bmap-wx.js’),并且进行配置,通过 onLoad: function(options)初始化页面options跳转所带来的参数,调用百度地图bmap.BMapWX函数,传入我们刚刚申请的密钥ak,得到经纬度,由经纬度来绑定城市,获取到用户的位置。定义query = function()调用BMap.weather函数,发起天气请求,来获取该经纬度下的天气情况。

3.3简易计算器

简易计算器模块相比天气就要简单些,不论是从布局,样式方面,都要单一。能进行+、-、*、/、%运算,这是本项目的副板块,是为了方便用户有时候的计算方便。但是这个板块,需要有很多小细节要考虑到,因此在开发时也要考虑周全。

3.3.1初始化配置页面信息

我们先在calculator.wxml中配置计算器所需的按钮1,2,3,4,5,6,7,8,9,0,+,-,*,/,%,=,.,清屏,删除一共19个按钮,为每一个按钮绑定tapped点击事件,添加id属性。再去calculator.wxss中配置页面每个按钮的位置大小,整个container页面显示横向铺满,背景色#f1f3f3,每一个格子居中对齐,横向铺满,高度100像素;结果显示区域:横向宽度100%;按钮panel-btns:宽度:100%,竖向排列;按钮btns-rows:宽度100%,横向排列,背景色#f7f8f9;,每个按钮:文字水平居中,边框1像素实线,按钮垂直居中。我们在calculator.js中的Page函数下调用tapped点击函数即可,我们定义showAbout: function()函数调用系统wx.showModal函数就可以用来展示该计算器的产品信息说明。

3.3.2计算功能的实现

我将计算功能的部分单独封装在utils文件夹下的calc.js的文件中,先定义了一些静态区常量在内存中,方便后面的直接调用。
INIT: 0,//初始状态;
RESULT: 1,//结果状态;
FIRST_UNDOT: 2, //记录第一个操作数,且该操作数没有小数点;
FIRST_DOT: 3, //记录第一个操作数,且该操作数有小数点;
SECOND_UNDOT: 4, //记录第二个操作数,且该操作数没有小数点;
SECOND_DOT: 5 //记录第二个操作数,且该操作数有小数点;
let curState = STATE.INIT //状态机所在状态;
let curResult = 0 //计算结果
let opNum1 = ‘0’ //操作数1
let opNum2 = ‘’ //操作数2
let op = ‘’ //操作符
let displayNum = opNum1 //界面上应当显示的数值
let displayOp = “” //界面上应当显示的操作符
再重置程序,判断输入的是否为0~9数字,如果是显示区会显示出来,若输入的第一个为0,则就显示0,如果输入是清屏 清除并将数据设置为初始状态;如果是删除 判断被删掉的那个字符是否为“.”不然删掉后就不能打“.”了,当删到最后一个数字时默认为0;判断判断运算符的连接方式,判断最后一个字符是否为数字如果是就可以添加运算符不然就无法输入。如果用户连续输出多个操作符,则最后输入的一个会覆盖前面输入的操作符。最后调用module.exports函数将结果都返回,我们再去calculator.js文件中import { addOp, getVars, reset } from "…/…/utils/calc"调用外部引入即可实现。
在这里插入图片描述

'use strict'

const STATE = {
    
    
    INIT: 0,  //初始状态
    RESULT: 1,  //结果状态
    FIRST_UNDOT: 2,  //记录第一个操作数,且该操作数没有小数点
    FIRST_DOT: 3,   //记录第一个操作数,且该操作数有小数点
    SECOND_UNDOT: 4, //记录第二个操作数,且该操作数没有小数点
    SECOND_DOT: 5 //记录第二个操作数,且该操作数有小数点
}

let curState = STATE.INIT  //状态机所在状态
let curResult = 0   //计算结果
let opNum1 = '0'   //操作数1
let opNum2 = ''  //操作数2
let op = ''   //操作符

let displayNum = opNum1 //界面上应当显示的数值
let displayOp = ""  //界面上应当显示的操作符

/**
 * 重置程序状态
 */
function reset() {
    
    
    curState = STATE.INIT
    curResult = 0
    opNum1 = '0'
    opNum2 = ''
    op = ''
}

/**
 * 是否为零
 */
function isZero(code) {
    
    
    return code == '0'
}

/**
 * 是否数字
 */
function isNumber(code) {
    
    
    return code >= '0' && code <= '9'
}

/**
 * 是否操作符
 */
function isOperator(code) {
    
    
    return code == '+' || code == '-'
        || code == 'x' || code == '/' || code == '%'
}

/**
 * 是否小数点
 */
function isDot(code) {
    
    
    return code == '.'
}

/**
 * 是否是等号
 */
function isEquel(code) {
    
    
    return code == '='
}

/**
 * 是否清屏
 */
function isClear(code) {
    
    
    return code == 'c'
}

/**
 * 是否删除
 */
function isDelete(code) {
    
    
    return code == 'd'
}

/**
 * 转换为可现实的操作符
 */
function op2Show(code) {
    
    
    return code == '/' ? '÷' : (code == 'x' ? '×' : code)
}

/**
 *
 */
function tryAppend(num, code) {
    
    
    if (num.length < 15) {
    
    
        num += code
    }
    return num
}

function tryTrunc(num) {
    
    
    let str = '' + num
    if (str.length > 15) {
    
    
        str = str.substr(0, 15)
    }
    return str
}

/**
 *
 */
function tryDelete() {
    
    
    if (curState == STATE.SECOND_DOT
        || curState == STATE.SECOND_UNDOT) {
    
    
        if (opNum2.length > 0) {
    
    
            opNum2 = opNum2.substr(0, opNum2.length - 1)
        }
        if (opNum2 == '') {
    
    
            opNum2 = '0'
        }
        return
    } else {
    
    
        if (opNum1.length > 0 && opNum1 != '0') {
    
    
            opNum1 = opNum1.substr(0, opNum1.length - 1)
        }
        if (opNum1 == '') {
    
    
            opNum1 = '0'
        }
        return
    }
}

function tryCalc() {
    
    
    let n1 = parseFloat(opNum1)
    let n2 = parseFloat(opNum2)
    switch (op) {
    
    
        case '+':
            curResult = n1 + n2
            break
        case '-':
            curResult = n1 - n2
            break
        case 'x':
            curResult = n1 * n2
            break
        case '/':
            if (n2 == 0) {
    
    
                reset()
                curResult = 'NaN'
                displayOp = ''
            } else {
    
    
                curResult = n1 / n2
            }
            break
        case '%':
            if (n2 == 0) {
    
    
                reset()
                curResult = 'NaN'
                displayOp = ''
            } else {
    
    
                curResult = n1 % n2
            }
            break
    }
    curResult = tryTrunc(curResult)
}

function addOp(code) {
    
    
    switch (curState) {
    
    
        case STATE.RESULT:
        case STATE.INIT:
            if (isNumber(code) && !isZero(code)) {
    
    
                curState = STATE.FIRST_UNDOT
                opNum1 = code
            } else if (isDot(code)) {
    
    
                curState = STATE.FIRST_DOT
                opNum1 = '0.'
            } else if (isOperator(code)) {
    
    
                curState = STATE.SECOND_UNDOT
                opNum1 = '0'
                opNum2 = ''
                op = code
            }
            displayNum = opNum1
            displayOp = ''
            break
        case STATE.FIRST_UNDOT:
            displayOp = ''
            if (isNumber(code)) {
    
    
                if (!isZero(opNum1)) {
    
    
                    opNum1 = tryAppend(opNum1, code)
                } else {
    
    
                    opNum1 = code
                }
            } else if (isDot(code)) {
    
    
                curState = STATE.FIRST_DOT
                opNum1 = opNum1 == '' ? '0' : tryAppend(opNum1, '.')
            } else if (isDelete(code)) {
    
    
                tryDelete()
            } else if (isOperator(code)) {
    
    
                curState = STATE.SECOND_UNDOT
                op = code
                opNum2 = ''
                displayOp = op
            }
            displayNum = opNum1
            break
        case STATE.FIRST_DOT:
            displayOp = ''
            if (isNumber(code)) {
    
    
                opNum1 = tryAppend(opNum1, code)
            } else if (isDelete(code)) {
    
    
                tryDelete()
                if (opNum1.indexOf('.') < 0)
                    curState = STATE.FIRST_UNDOT
            } else if (isOperator(code)) {
    
    
                curState = STATE.SECOND_UNDOT
                op = code
                opNum2 = ''
                displayOp = op
            }
            displayNum = opNum1
            break
        case STATE.SECOND_UNDOT:
            if (isNumber(code)) {
    
    
                if (!isZero(opNum2)) {
    
    
                    opNum2 = tryAppend(opNum2, code)
                } else {
    
    
                    opNum2 = code
                }
                displayNum = opNum2
            } else if (isDot(code)) {
    
    
                curState = STATE.SECOND_DOT
                opNum2 = opNum2 == '' ? '0' : tryAppend(opNum2, '.')
                displayNum = opNum2
            } else if (isDelete(code)) {
    
    
                tryDelete()
                displayNum = opNum2
            } else if (isOperator(code)) {
    
    
                if (opNum2 != '') {
    
    
                    //直接计算
                    tryCalc()
                    curState = STATE.SECOND_UNDOT
                    opNum1 = curResult
                    opNum2 = ''
                    displayNum = curResult
                }
                op = code
                displayOp = op
            } else if (isEquel(code)) {
    
    
                if (opNum2 != '') {
    
    
                    tryCalc()
                    curState = STATE.RESULT
                    opNum1 = '0'
                    opNum2 = ''
                    displayNum = curResult
                }
                op = code
                displayOp = op
            }
            break
        case STATE.SECOND_DOT:
            if (isNumber(code)) {
    
    
                opNum2 = tryAppend(opNum2, code)
                displayNum = opNum2
            } else if (isDelete(code)) {
    
    
                tryDelete()
                if (opNum2.indexOf('.') < 0)
                    curState = STATE.SECOND_UNDOT
                displayNum = opNum2
            } else if (isOperator(code)) {
    
    
                if (opNum2 != '') {
    
    
                    //直接计算
                    tryCalc()
                    curState = STATE.SECOND_UNDOT
                    opNum1 = curResult
                    opNum2 = ''
                    displayNum = curResult
                }
                op = code
                displayOp = op
            } else if (isEquel(code)) {
    
    
                if (opNum2 != '') {
    
    
                    tryCalc()
                    curState = STATE.RESULT
                    opNum1 = '0'
                    opNum2 = ''
                    displayNum = curResult
                }
                op = code
                displayOp = op
            }
            break
    }
    if (isClear(code)) {
    
    
        reset()
        displayNum = opNum1
        displayOp = ''
    }
    displayOp = op2Show(displayOp)
}

reset()

module.exports = {
    
    
    reset, addOp, getVars(){
    
    
        return {
    
    curState, curResult, opNum1, opNum2, op, displayNum, displayOp}
    }
}

3.4页面跳转与交互

到这里,三大页面的功能都已经完成,现在要做的是将这三个页面能通过封装的函数调用起来,实现页面的跳转功能,因为我们要把这些功能能在同一个小程序里工作起来。我们要回到最开始的程序页面index.wxml,我们分别都给天气查询模块和计算器模块添加按钮,绑定点击事件bindtap="weatherSearch"和bindtap=“calc”,再去index.js调用事件处理函数天气页面weatherSearch: () => { wx.navigateTo({url:’…/weather/weather’});},,计算器页面事件处理函数calc: () => {wx.navigateTo({url: ‘…/calculator/calculator’});},添加需要跳转的地址即可。

3.4本项目的数据来源

获取当前用户地址
采用微信小程序自带的API接口wx.getLocation,获取当前位置的经纬度
调用第三方百度地图API接口
获取到经纬度后,调用百度地图API获取当前城市以及街道
API接口https://api.map.baidu.com/
调用第三方天气API接口
获取天气的API接口
API接口地址:https://www.tianqiapi.com
调用第三方腾讯疫情新闻API接口
API接口地址https://view.inews.qq.com
绘制中国地图,通过canvas画布来传入图表,调用mapData的库函数,

4 项目的代码调试及线上发布

4.1代码编译调试

打开调试器,运行小程序后观察控制台console打印的结果,在Network看的出整个项目的网络请求时长,Audits上面能对本次小程序从页面渲染、网络、JS脚本等方面评估小程序的性能进行综合评估。

在这里插入图片描述

代码上传后,开发人员可以提交代码以供审核。在这之前我们需要找到提交审核的页面。转到微信公众平台(http://mp.weixin.qq.com)并使用微信扫码登录。点击管理-版本管理,找到“开发版本”项,可以找到刚上传的代码。单击左侧的“提交审核”,使用管理员的微信扫码来验证并确认即可。接下来只要等待审核结果。(我的微信小程序已上线,直接在微信-小程序-搜本科毕业设计)
在这里插入图片描述

4.2 线上审核发布

小程序开发完成后,开发者要把本地代码上传至开发者公众台后台。打开需要提交新版本的项目,单击左侧菜单栏的“上传”按钮,扫码确认身份。填写所提交代码的版本号,项目说明等。确认正确后,代码会压缩上传到后台“开发版”。注意,“开发版本”只允许存放一个,新加载上传的代码会替换原来“开发版本”中的代码。

在这里插入图片描述
在这里插入图片描述

5 总结

本项目是基于微信开发者工具完成的,采用WXML+WXSS+JavaScript为开发语言,JSON为数据格式来实现的,因为是基于微信小程序的天气预报终端开发,不需要开发者购买域名和搭建服务器,微信提供了很多第三方API接口和云服务器,能节省经费。你在微信开发者工具的编辑器内写代码,编译通过后,模拟器上会显示微信端的效果,这种实时反馈是我特别喜欢的。在整个项目开发过程中,能让我把以前的学过的零零散散的旧知识都整合到一起,提高我的编程能力,更加熟练函数的封装调用,系统函数的调用,在整个项目的开发中,我也遇到许许多多bug,通过查询资料一一解决,我收获的更多,本项目也有不少新知识,比如第三方工具的使用,调用外部的API接口等,这些问题对我今后的编程思维有极大的促进作用。增强了我的动手能力和解决问题的能力,培养了我对编程的兴趣和热爱。

参考文献

[1] 王天泥. 当图书馆遇上微信小程序 图书与情报[J] 2016-12-15(12)23-25
[2] 李雪钊. 基于微信小程序的慢出行共享系统的设计与实现 首都经济贸易大学[D] 2018-05-01(26)55-57
[3] 张雪云,牟艳,张九博 基于微信小程序的电源监测管理系统设计 计算机与现代化[J] 2017-12-15(37)86-88
[4]王松 基于用户需求的高校图书馆微信小程序移动服务设计研究 东北师范大学[D] 2018-05-01(11)54-55
[5] 代洪彬 基于微信小程序的《计算机应用基础》微学习平台的设计与实现
广西师范学院[D] 2018-04-01(21)22-24
[6]韦玉辉; 苏兆伟; 潘梦诗 基于微信小程序的服装个性化定制系统设计与实现 服装学报[J] 2019-10-15(58)14-15
[7]王婷婷;谢晓茹;于佳微信小程序对移动互联网生态圈的影响[J].电脑知识与技术2018-11-15(74)34-36
[8]苏煜辉; 杨明戊; 陈正铭; 戴经国.浅析快应用与微信小程序技术的异同[J].电脑知识与技术,2018-11-05(75)19-21.
[9]荣蓉;穆心驰;实战微信小程序JavaScript、WXML与Flexbox综合开发[M]北京:电子工业出版社 , 2017.07:132-136
[10]刘明洋;微信小程序实战入门内含完整实例解析[M] 北京:人民邮电出版社 , 2017.10:56-78
[11] 闫小坤;微信小程序开发详解[M] 北京:清华大学出版社 , 2017.07:89-92
[12]厉业崧;王向辉;微信小程序入门[M] 北京:清华大学出版社 , 2017.11:23-26
[13]微信小程序百度百科https://baike.baidu.com/item/微信小程序/20171697?fr=aladdin
[14]微信小程序开发官方文档https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/#小程序简介
[15]百度地图开发平台JavaScript API https://lbsyun.baidu.com/index.php?title=jspopular3.0
[16]刘刚.微信小程序开发必备100Tip[M] 北京:电子工业出版社 , 2017.10
[17]朱继宏.微信小程序开发与运营[M] 北京:电子工业出版社 , 2018.11:12-25
[18]周文洁.微信小程序开发零基础入门[M] 北京:清华大学出版社 , 2019.01:36-39
[19]雷磊.微信小程序开发入门与实践[M] 北京:清华大学出版社 , 2017.04:22-31
[20]刘刚.微信小程序开发图解案例教程[M] 北京:人民邮电出版社 , 2019.01:89-93

猜你喜欢

转载自blog.csdn.net/qq_44807756/article/details/105774338