美文网首页
微信小程序 — 翻译小工具

微信小程序 — 翻译小工具

作者: 蔡华鹏 | 来源:发表于2018-09-16 13:35 被阅读0次

    在看了微信小程序文档之后,觉得挺有意思的,就尝试着写了第一个微信小程序,主页面样式是这样的:

    主页面

    关键词: Promise、百度翻译api、微信小程序文档、移动端、iconfont、md5加密。

    描述: 搜索微信小程序“鹏城翻译”,可以翻译中文—外语外语—中文,可以实现语言切换功能,翻译多个国家语言,同时存储搜索过的历史记录,可以之后查看。

    代码步骤:

    • 首先要了解微信小程序文档、框架、组件【微信小程序文档
    • 整理思绪,分析页面功能,理清所需页面

    一、页面的实现

    • 将页面分成三个页面,分别是index、change、history
    • 将三个页面所需要的共同样式都写入app.wxss中,如下
    @import "./assets/iconfont/iconfont.wxss";
    .container {
      padding: 0;
      background-color: #f5fafe;
      height: 100vh;
      display: flex;
      flex-direction: column;
      /* align-items: center; */
      box-sizing: border-box;
      font-size: 30rpx;
      color: #333;
    } 
    .copyright {
      align-self: center;
      flex: 1;
      display: flex;
      align-items: flex-end;
      padding-bottom: 20rpx;
      font-size: 28rpx;
      color: #999;
    }
    .view-hover,
    .navigator-hover {
      background-color: #f3f3f3!important;
    }
    
    • 配置好app.json如下
    {
      "pages":[
        "pages/index/index",
        "pages/change/change",
        "pages/history/history"
      ],
      "window":{
        "backgroundColor":"#333",
        "backgroundTextStyle":"light",
        "navigationBarBackgroundColor": "#1c1b21",
        "navigationBarTitleText": "鹏城翻译",
        "navigationBarTextStyle":"#fff"
      },
      "tabBar":{
        "borderStyle":"white",
        "position":"top",
        "color":"#595959",
        "selectedColor":"#1c1b21",
        "list": [
          {
            "pagePath": "pages/index/index",
            "text":"翻译"
          },
          {
            "pagePath":"pages/history/history",
            "text":"历史"
          }
        ]
      }
    }
    
    • pages
      用于指定小程序由哪些页面组成,每一项都对应一个页面的 路径+文件名 信息。文件名不需要写文件后缀,框架会自动去寻找对于位置的 .json, .js, .wxml, .wxss 四个文件进行处理
      数组的第一项代表小程序的初始页面(首页)。小程序中新增/减少页面,都需要对 pages 数组进行修改

    • window
      用于设置小程序的状态栏、导航条、标题、窗口背景色

    • tabBar
      如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面

    index页面的实现

    index

    1、 在小程序中一个index页面主要是由index.wxml、index.wxss、index.js、等组成的。
    2、小程序中的WXML 中的动态数据均来自index.js中对应 Page 的 data。
    3、看下我的index.wxml页面代码:

    <!--index.wxml-->
    <view class="container page-index">
      <view class='change'>
        <navigator url='/pages/change/change' hover-class='navigator-hover'>
          <text>到{{curLang.chs}}</text>
          <text class='iconfont icon-down'></text>
        </navigator>
      </view>
      <view class='input-area'>
        <text class='iconfont icon-close' hidden='{{hideClearIcon}}' bindtap='onTapClose'></text>
        <view class='textarea-wrap'>
          <textarea placeholder='请输入要翻译的文本' placeholder-style='color:#8995a1' bindinput='onInput' bindconfirm='onConfirm' bindblur='onConfirm' value='{{query}}'></textarea>
        </view>
        <view class='text-area'>
          <view class='text-title'>译文</view>
          <view class='text-result' wx:for="{{result}}" wx:key="index">
            <text selectable='true'>{{item.dst}}</text>
          </view>
        </view>
      </view>
      <view class='copyright'>
        <text>@ 鹏城</text>
      </view>
    </view>
    

    在小程序中

    • 视图容器用<view></view>
    • navigator是页面链接,相当于html中的a标签
    • hover-class指定按下去的样式类。当 hover-class="none" 时,没有点击态效果
    • 数据绑定使用 Mustache 语法(双大括号)将变量包起来
    • 当用到icon图标的时候,可以新建一个文件夹,其中放入icon图标的代码文件,在app.wxss中应用,以便于所有文件引用,icon图标代码如下图:


      icon.wxss
    • hidden属性,后面值默认为false,在页面中显示出这条代码体现是效果,但在js中可以对hidden属性的默认值进行设置,是否在页面表现效果
    • bindtap在小程序中是代表此容器是可以点击的,在js中对bindtap的值进行操作就可以设置点击后要执行的事件
    • bindinput键盘输入时触发,event.detail = {value, cursor, keyCode}keyCode 为键值,2.1.0 起支持,处理函数可以直接 return 一个字符串,将替换输入框的内容。
    • bindconfirm点击完成按钮时触发,event.detail = {value: value}
    • bindblur输入框失去焦点时触发,event.detail = {value: value}
    • textarea多行输入框。该组件是原生组件,使用时请注意相关限制
      (1) placeholder输入框为空时占位符,与css相同
      (2) placeholder-style指定 placeholder 的样式
    • wx:for在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item

    那么index.wxml页面,差不多就可以完成了,然后就是index.wxss和index.js,与css大同小异,遇到不能用的属性是可以看下微信小程序api,总有你需要的效果,如下是index.wxss代码:

    /**index.wxss**/
    .change {
      color: #8995a1;
      font-size: 24rpx;
      padding: 20rpx 40rpx;
      display: flex;
      align-items: center;
    }
    .change .icon-down {
      color: #8995a1; 
      font-size: 20rpx;
    }
    input-area {
      position: relative;
    }
    .textarea-wrap {
      background: #fff;
      border-bottom: 1px solid #c7cee0;
    }
    .input-area textarea {
      background-color: #fff;
      padding: 30rpx 0 30rpx 30rpx;
      width: calc(100% - 48rpx);
      margin: 0;
      box-sizing: border-box;
    }
    .input-area .icon-close {
      position: absolute;
      right: 12rpx;
      top: 90rpx;
      z-index: 100;
      font-size: 40rpx;
      color: #888;
    }
    .input-area .text-area {
      min-height: 80rpx;
      padding: 40rpx;
      background-color: #fff;
    }
    .input-area .text-title {
      font-size: 28rpx;
      color: #8995a1;
    }
    .input-area .text-result {
      padding: 20rpx 0;
    }
    

    这里需要说的就是因为在文本中输入内容后会有一个icon显示,以便于我们删除文本中的内容,但如果输入框的宽度撑满,这个icon会被输入框遮住,所以我在写输入框的样式时,让输入框的宽度没有撑满,让出一些,以便于icon的点击

    index.js
    //index.js
    import { translate } from '../../utils/api.js'
    const app = getApp()
    Page({
      data:{
        query:'',
        hideClearIcon: true,
        result: [],
        curLang: {}
      },
      onLoad: function (options){
        // console.log('lonload')
        // console.log(options,55)
        if(options.query) {
          this.setData({query: options.query})
        }
      },
      onShow: function(){
        // console.log(this.data.curLang.lang)
        // console.log(app.globalData.curLang.lang)
        if(this.data.curLang.lang !== app.globalData.curLang.lang) {
          this.setData({curLang: app.globalData.curLang})
          this.onConfirm()
        }
      },
      onInput: function(e) {
        // console.log(e.detail.value)
        this.setData({'query': e.detail.value})
        if(this.data.query.length > 0) {
          this.setData({'hideClearIcon':false})
        }else{
          this.setData({ 'hideClearIcon': true})
        }
      },
      onTapClose: function(){
        this.setData({query: '',hideClearIcon: true})
      },
      onConfirm: function(){
        // console.log(this.data,'55')
        if(!this.data.query) return
        translate(this.data.query,{from:'auto',to: this.data.curLang.lang}).then(res=>{
          // console.log(res.trans_result,'99')
          //console.log(res,'5555')
          this.setData({'result': res.trans_result})
          
          //console.log(wx.getStorageSync('history'))
          let history = wx.getStorageSync('history') || []
          history.unshift({query: this.data.query, result: res.trans_result[0].dst})
          history.length = history.length > 10 ? 10 : history.length
          wx.setStorageSync('history', history)
        })
      }
    })
    
    Page()

    看了微信小程序的文档后都知道,在js文件中,Page(Object) 函数用来注册一个页面。接受一个 Object 类型参数,其指定页面的初始数据、生命周期回调、事件处理函数等。

    • data:
      data 是页面第一次渲染使用的初始数据
      将会以JSON字符串的形式由逻辑层传至渲染层,因此data中的数据必须是可以转成JSON的类型:字符串,数字,布尔值,对象,数组。
      渲染层可以通过 WXML 对数据进行绑定。

    • onLoad:
      onLoad(Object query)
      页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。

    • onShow:
      onShow()
      页面显示/切入前台时触发。

    • Page.prototype.setData(Object data, Function callback)
      setData 函数用于将数据从逻辑层发送到视图层(异步),同时改变对应的 this.data 的值(同步)
      Object 以 key: value 的形式表示,将 this.data 中的 key 对应的值改变成 value。

    其中 key 可以以数据路径的形式给出,支持改变数组中的某一项或对象的某个属性,如 array[2].message,a.b.c.d,并且不需要在 this.data 中预先定义。

    注意:
    1、 直接修改 this.data 而不调用 this.setData 是无法改变页面的状态的,还会造成数据不一致。
    2、 仅支持设置可 JSON 化的数据。
    3、 单次设置的数据不能超过1024kB,请尽量避免一次设置过多的数据。
    4、 请不要把 data 中任何一项的 value 设为 undefined ,否则这一项将不被设置并可能遗留一些潜在问题

    • onInput onTapClose onConfirm 这三个key都是WXML
      bindinputbindtapbindconfir/bindblur的值,然后在js中进行事件绑定

    页面逻辑

    1、实现在输入框中输入内容时,对onInput进行绑定,显示icon,通过设置hideClearIcon的值为false还是true来控制icon
    2、在icon显示后,点击icon删除输入内容,对onTapClose进行事件绑定
    3、在输入确定内容后对内容进行翻译,对onConfirm进行绑定,准备翻译,然后我想到翻译是一件事情,所以我对它进行了一个封装,在utils里加了一个api.js,这里我用到百度翻译的接口,所以需要查阅一下百度翻译api,以下是api.js:

    import md5 from './md5.min.js'
    
    const appid = '20180912000205732'
    const key = 'oowRg0jxzoSgwCfAfyVn'
    
    function translate(q, { from = 'auto', to = 'auto' } = { from: 'auto', to: 'auto'}) {
      return new Promise((resolve, reject) => {
        let salt = Date.now()
        let sign = md5(`${appid}${q}${salt}${key}`)
        wx.request({
          url: 'https://fanyi-api.baidu.com/api/trans/vip/translate',
          data: {
            q,
            from,
            to,
            appid,
            salt,
            sign
          },
          success(res) {
            if(res.data && res.data.trans_result) {
              // console.log(res,'1')
              // console.log(res.data,'2')
              // console.log(res.data.trans_result,'3')
              resolve(res.data)
            } else{
              reject({status: 'error', msg: '翻译失败'})
              wx.showToast({
                title: '翻译失败',
                icon: 'none',
                duration: 3000
              })
            }
          },
          fail() {
            reject({status: 'error', msg: '翻译失败'})
            wx.showToast({
              title: '网络异常',
              icon: 'none',
              duration: 3000
            })
          }
        })
      })
    }
    module.exports.translate = translate
    

    在微信小程序中用wx.request请求数据,需要注意的是百度翻译api要用md5进行加密,所以也需要在utils文件夹中再加入一个md5.min.js文件,这里md5文件最好是用压缩过的,再由import来引入api.js文件中,但我没有直接去使用它,而是用Promise对它进行一个封装

    代码解析:
    1、 wx.request发起 HTTPS 网络请求。使用前请注意阅读相关说明
    2、 wx.showToast(Object object)显示消息提示框
    3、 一个模块要想对外暴露其内部的私有变量与函数,只能通过 module.exports 实现。exports: 通过该属性,可以对外共享本模块的私有变量与函数

    最后在index.js页面中用import引入api.js,这样就完成了index页面的接口问题,再就是index页面的参数问题,如要翻译成什么语言,这就需要change页面来实现了

    change页面的实现

    change
    先看下我的change.wxml代码:
    <view class="container lang-list">
      <view class='title'>翻译成</view>
      <view class='item' data-chs="{{language.chs}}" data-lang="{{language.lang}}" data-index="{{index}}" wx:for="{{langList}}" wx:key="index" wx:for-item="language" bindtap='onTapItem' hover-class='view-hover'>
        <view class='item-inner'>
          <text>{{language.chs}}</text>
          <text class='iconfont icon-duihao' wx:if="{{index===curLang.index}}"></text>
        </view>
      </view>
    </view>
    
    • 其中data-chs data-lang data-index三个都是自定义属性
    • wx:key 如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 <input/> 中的输入内容,<switch/> 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。
      wx:key的值以两种形式提供:
      1、字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
      2、保留关键字*this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字,如:
      当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
      如不提供 wx:key,会报一个warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。
    • wx:for-item 可以指定数组当前元素的变量名,
    • wx:if在框架中,使用 wx:if="{{condition}}" 来判断是否需要渲染该代码块

    再看下change.js代码:

    //change.js
    const util = require('../../utils/util.js')
    const app = getApp()
    Page({
      data: {
        curLang: {},
        langList: app.globalData.langList
      },
      onShow: function(){
        this.setData({ curLang: app.globalData.curLang})
      },
      onTapItem: function(e) {
        let langObj = e.currentTarget.dataset  //存储自定义属性
        // console.log(e.currentTarget.dataset)
        // console.log(e)
        wx.setStorageSync('curLang',langObj)
        this.setData({'curLang': langObj})
        app.globalData.curLang = langObj
        wx.switchTab({
          url: '/pages/index/index',
        })
      }
    })
    

    写到这里的时候,我们发现需要一个函数来保存变量,以便于在index页面翻译,也便于在change页面选择翻译语言的时候引用,所以我把这个函数写在app.js中,方便其他页面调用,调用方法便是const app = getApp(),以下是app.js:

    //app.js
    App({
        onLaunch: function () {
          //展示本地存储能力
            this.globalData.curLang = wx.getStorageSync('curLang') || this.globalData.langList[0]
        },
        globalData: {
          curLang: {},
          langList: [
            {
              'chs': '英文',
              'lang': 'en',
              "index": 0
            },
            {
              'chs': '中文',
              'lang': 'zh',
              "index": 1
            },
            {
              'chs': '日语',
              'lang': 'jp',
              "index": 2
            },
            {
              'chs': '韩语',
              'lang': 'kor',
              "index": 3
            },
            {
              'chs': '法语',
              'lang': 'fra',
              "index": 4
            },
            {
              'chs': '西班牙语',
              'lang': 'spa',
              "index": 5
            },
            {
              'chs': '阿拉伯语',
              'lang': 'ara',
              "index": 6
            },
            {
              'chs': '文言文',
              'lang': 'wyw',
              "index": 7
            },
            {
              'chs': '泰语',
              'lang': 'th',
              "index": 8
            },
            {
              'chs': '繁体中文',
              'lang': 'cht',
              "index": 9
            }
          ]
        }
    })
    

    以上两个文件中的js需要注意的代码:

    • wx.setStorageSync

    wx.setStorageSync(string key, Object|string data)
    (1) string key
    本地缓存中指定的 key
    (2) Object|string data
    需要存储的内容

    • wx.getStorageSync

    Object|string wx.getStorageSync(string key)
    (1) string key
    本地缓存中指定的 key
    (2)Object|string data
    key对应的内容

    • wx.switchTab跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
    • 在执行onTapItem事件时,可以得到参数e,并且e.currentTarget.dataset可以储存自定义的属性,以便于页面使用

    然后在联系上最初的index页面,我们就可以选择需要翻译的语言了,同时在本地缓存中存储了当前翻译的语言,下次打开小程序时,还是上次保存的翻译语言

    history页面实现

    history

    看一看history.wxml:

    <!--pages/history/history.wxml-->
    <scroll-view scroll-y class="container">
      <view class="history-list">
        <view class="title">翻译历史</view>
        <view class="item" wx:for="{{history}}" wx:key="index" bindtap='onTapItem' data-query="{{item.query}}" data-langId="{{item.langIndex}}">
          <view class="query">{{item.query}}</view>
          <view class="result">{{item.result}}</view>
        </view>
      </view>
    </scroll-view>
    

    history.wxml中主要的就是把它当做一个数组去渲染,然后点击对应数组,跳转到index页面,进行当前语言翻译,而这些功能需要在history.js和change.js中去实现

    看一看history.js:

    //pages/history/history.js
    const app = getApp()
    
    Page({
      data: {
        history: []
      },
      onShow: function() {
        this.setData({history: wx.getStorageSync('history')})
      },
      onTapItem: function(e) {
        wx.reLaunch({
          url: `/pages/index/index?query=${e.currentTarget.dataset.query}`
        })
      }
    })
    

    当执行以上代码时,便会用到index.js中onConfirm事件的这一段代码:

    let history = wx.getStorageSync('history') || []
          history.unshift({query: this.data.query, result: res.trans_result[0].dst})
          history.length = history.length > 10 ? 10 : history.length
          wx.setStorageSync('history', history)
    

    其中存储好history的值,便于history页面去使用

    • db.command.unshift
      更新指令,对一个值为数组的字段,往数组头部添加一个或多个值。或字段原为空,则创建该字段并设数组为传入值。
    • wx.reLaunch(Object object)
      关闭所有页面,打开到应用内的某个页面
      (1) url : 需要跳转的应用内页面路径 , 路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 path?key=value&key2=value2,如果跳转的页面路径是 tabBar 页面则不能带参数

    当切换到index页面时,会用到index.js中的这些代码:

     onLoad: function (options){
        // console.log('lonload')
        // console.log(options,55)
        if(options.query) {
          this.setData({query: options.query})
        }
      },
      onShow: function(){
        // console.log(this.data.curLang.lang)
        // console.log(app.globalData.curLang.lang)
        if(this.data.curLang.lang !== app.globalData.curLang.lang) {
          this.setData({curLang: app.globalData.curLang})
          this.onConfirm()
        }
      }
    

    其中onLoad在加载页面中设置query的值,onShow在显示页面后翻译query的值

    这样就构成了整个小程序目前需要的需求,如果还想有其他更多的需求,可以在此代码上继续添加

    The early bird catches the worm

    相关文章

      网友评论

          本文标题:微信小程序 — 翻译小工具

          本文链接:https://www.haomeiwen.com/subject/juyggftx.html