微信小程序 — 翻譯小工具

在看了微信小程序文檔之后,覺得挺有意思的,就嘗試著寫了第一個微信小程序,主頁面樣式是這樣的:

主頁面

關鍵詞: 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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容