實(shí)現(xiàn)一個智能生活信息查詢的小秘書功能,支持查天氣、新聞、日歷、匯率、笑話、故事、百科、詩詞、郵編、區(qū)號、菜譜、股票、節(jié)目預(yù)告,還支持閑聊、算24點(diǎn)、數(shù)學(xué)計(jì)算、單位換算、購物、搜索等功能。
使用方式上支持搖一搖、點(diǎn)界面按鈕、手動輸入、下拉刷新這四種方式。
基本延用官方案例的目錄結(jié)構(gòu)和命名,index.xx是首頁面相關(guān)代碼,logs.xx是日志頁相關(guān)代碼。
比官方目錄結(jié)構(gòu)多了兩個文件:
config.js
因?yàn)橛玫揭恍┡渲?,放?js里封裝起來比較好。
pics/bg.jpg
小程序背景圖片,開發(fā)環(huán)境上加載本地文件作為背景圖片是生效的,預(yù)覽體驗(yàn)時不生效,網(wǎng)上有人說不支持本地文件,因此這里這個圖片其實(shí)沒有用到,只是暫時留著。
app.js
提供獲取用戶微信賬號昵稱和所在地,獲取當(dāng)前地理位置這兩個公共接口。
//app.jsconst corpusList = require('./config').corpusvar UTIL = require('./utils/util.js');App({ onShow: function () { UTIL.log('App Show') }, onHide: function () { UTIL.log('App Hide') }, onLaunch: function () { UTIL.log('App Launch') this.updateUserLocation() }, updateUserLocation: function() { var that = this wx.getLocation({ //type: 'wgs84', // gps原始坐標(biāo) type: 'gcj02', //國家標(biāo)準(zhǔn)加密坐標(biāo) success: function (res) { that.globalData.latitude = res.latitude that.globalData.longitude = res.longitude that.globalData.speed = res.speed //var accuracy = res.accuracy UTIL.log('REFRESH LOCATION: ' + that.globalData.latitude + ' | ' + that.globalData.longitude + ' , speed: ' + that.globalData.speed) }, fail: function(res) { UTIL.log('REFRESH LOCATION FAILED...') } }) }, getUserInfo:function(cb){ var that = this if(this.globalData.userInfo){ typeof cb == "function" && cb(this.globalData.userInfo) }else{ //調(diào)用登錄接口 wx.login({ success: function () { wx.getUserInfo({ success: function (res) { that.globalData.userInfo = res.userInfo that.globalData.custId = UTIL.getUserUnique(that.globalData.userInfo); typeof cb == "function" && cb(that.globalData.userInfo) } }) }, fail: function () { UTIL.log('登錄WX失敗了!') } }) } }, clearUserInfo: function() { var that = this that.globalData.userInfo = null; that.globalData.hasLogin = false; }, globalData:{ userInfo:null, corpus: corpusList, custId: '', latitude: 0.0, longitude: 0.0, speed: 0, }})
app.json
配置小程序窗體相關(guān)屬性:標(biāo)題名稱,背景色,網(wǎng)絡(luò)超時等。
配置首頁面為pages/index/index。
{ "pages": [ "pages/index/index", "pages/logs/logs" ], "window": { "backgroundTextStyle": "black", "navigationBarBackgroundColor": "#F8F8F8", "navigationBarTitleText": "遙知之 -- olami語義支持", "navigationBarTextStyle": "black", "backgroundColor": "#F8F8F8" }, "networkTimeout": { "request": 10000, "connectSocket": 10000, "uploadFile": 10000, "downloadFile": 10000 }}
app.wxss
配置了“遙知之”小程序container全局樣式
/**app.wxss**/.container { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; box-sizing: border-box;}
配置文件:config.js
保存一些配置信息,包括NLI的key和secret,還有小程序中預(yù)置的語料集合。
module.exports = { //NLI appkey appkey: `b4118cd178064b45b7c8f1242bcde31f`, //NLI appsecret appsecret: `7908028332a64e47b8336d71ad3ce9ab`, corpus: [ // '閑聊', // '天氣', // '詩詞', // '單位換算', // '新聞', // '算24點(diǎn)', // '菜譜', // '匯率', // '郵編', // '區(qū)號', // '股票', // '日歷', // '節(jié)目預(yù)告', // '笑話', // '故事', // '購物', // '數(shù)學(xué)運(yùn)算', // '百科', // '搜索', '你今年多大啦', '上海今天天氣如何', '北京的呢', '李白寫過什么詩', '我要聽李白的靜夜思', '背一首將進(jìn)酒', '一公里等于多少英尺', '我要看體育新聞', '4567算24點(diǎn)', '紅燒肉的做法', '1美元能換算多少人民幣', '查一下南昌的郵編', '鄭州的區(qū)號是多少', '中國石油的股價(jià)', '今年中秋節(jié)是哪一天', '明晚湖南衛(wèi)視放什么節(jié)目', '來個笑話', '講個故事聽聽', '我要買電腦', '1加到100等于多少', '黃山有多高', '百度搜一下薛之謙的照片', ]};
獲取隨機(jī)GUID:GUID.js
//表示全局唯一標(biāo)識符 (GUID)。function Guid(g) { var arr = new Array(); //存放32位數(shù)值的數(shù)組 if (typeof (g) == "string") { //如果構(gòu)造函數(shù)的參數(shù)為字符串 InitByString(arr, g); } else { InitByOther(arr); } //返回一個值,該值指示 Guid 的兩個實(shí)例是否表示同一個值。 this.Equals = function (o) { if (o && o.IsGuid) { return this.ToString() == o.ToString(); } else { return false; } } //Guid對象的標(biāo)記 this.IsGuid = function () { } //返回 Guid 類的此實(shí)例值的 String 表示形式。 this.ToString = function (format) { if (typeof (format) == "string") { if (format == "N" || format == "D" || format == "B" || format == "P") { return ToStringWithFormat(arr, format); } else { return ToStringWithFormat(arr, "D"); } } else { return ToStringWithFormat(arr, "D"); } } //由字符串加載 function InitByString(arr, g) { g = g.replace(/\{|\(|\)|\}|-/g, ""); g = g.toLowerCase(); if (g.length != 32 || g.search(/[^0-9,a-f]/i) != -1) { InitByOther(arr); } else { for (var i = 0; i < g.length; i++) { arr.push(g[i]); } } } //由其他類型加載 function InitByOther(arr) { var i = 32; while (i--) { arr.push("0"); } } /* 根據(jù)所提供的格式說明符,返回此 Guid 實(shí)例值的 String 表示形式。 N 32 位: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx D 由連字符分隔的 32 位數(shù)字 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx B 括在大括號中、由連字符分隔的 32 位數(shù)字:{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} P 括在圓括號中、由連字符分隔的 32 位數(shù)字:(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) */ function ToStringWithFormat(arr, format) { switch (format) { case "N": return arr.toString().replace(/,/g, ""); case "D": var str = arr.slice(0, 8) + "-" + arr.slice(8, 12) + "-" + arr.slice(12, 16) + "-" + arr.slice(16, 20) + "-" + arr.slice(20, 32); str = str.replace(/,/g, ""); return str; case "B": var str = ToStringWithFormat(arr, "D"); str = "{" + str + "}"; return str; case "P": var str = ToStringWithFormat(arr, "D"); str = "(" + str + ")"; return str; default: return new Guid(); } }}//Guid 類的默認(rèn)實(shí)例,其值保證均為零。Guid.Empty = new Guid();//初始化 Guid 類的一個新實(shí)例。Guid.NewGuid = function () { var g = ""; var i = 32; while (i--) { g += Math.floor(Math.random() * 16.0).toString(16); } return new Guid(g).ToString();}module.exports = { NewGuid: Guid.NewGuid}
計(jì)算MD5值:MD5.js
//md5加密算法function md5(string) { var x = Array(); var k, AA, BB, CC, DD, a, b, c, d; var S11 = 7, S12 = 12, S13 = 17, S14 = 22; var S21 = 5, S22 = 9, S23 = 14, S24 = 20; var S31 = 4, S32 = 11, S33 = 16, S34 = 23; var S41 = 6, S42 = 10, S43 = 15, S44 = 21; string = Utf8Encode(string); x = ConvertToWordArray(string); a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476; for (k = 0; k < x.length; k += 16) { AA = a; BB = b; CC = c; DD = d; a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478); d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756); c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB); b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE); a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF); d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A); c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613); b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501); a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8); d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF); c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1); b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE); a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122); d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193); c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E); b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821); a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562); d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340); c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51); b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA); a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D); d = GG(d, a, b, c, x[k + 10], S22, 0x2441453); c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681); b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8); a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6); d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6); c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87); b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED); a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905); d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8); c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9); b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A); a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942); d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681); c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122); b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C); a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44); d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9); c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60); b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70); a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6); d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA); c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085); b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05); a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039); d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5); c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8); b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665); a = II(a, b, c, d, x[k + 0], S41, 0xF4292244); d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97); c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7); b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039); a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3); d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92); c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D); b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1); a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F); d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0); c = II(c, d, a, b, x[k + 6], S43, 0xA3014314); b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1); a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82); d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235); c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB); b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391); a = AddUnsigned(a, AA); b = AddUnsigned(b, BB); c = AddUnsigned(c, CC); d = AddUnsigned(d, DD); } var temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d); return temp.toLowerCase();}function RotateLeft(lValue, iShiftBits) { return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));}function AddUnsigned(lX, lY) { var lX4, lY4, lX8, lY8, lResult; lX8 = (lX & 0x80000000); lY8 = (lY & 0x80000000); lX4 = (lX & 0x40000000); lY4 = (lY & 0x40000000); lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF); if (lX4 & lY4) { return (lResult ^ 0x80000000 ^ lX8 ^ lY8); } if (lX4 | lY4) { if (lResult & 0x40000000) { return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); } else { return (lResult ^ 0x40000000 ^ lX8 ^ lY8); } } else { return (lResult ^ lX8 ^ lY8); }}function F(x, y, z) { return (x & y) | ((~x) & z);}function G(x, y, z) { return (x & z) | (y & (~z));}function H(x, y, z) { return (x ^ y ^ z);}function I(x, y, z) { return (y ^ (x | (~z)));}function FF(a, b, c, d, x, s, ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b);}function GG(a, b, c, d, x, s, ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b);}function HH(a, b, c, d, x, s, ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b);}function II(a, b, c, d, x, s, ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b);}function ConvertToWordArray(string) { var lWordCount; var lMessageLength = string.length; var lNumberOfWords_temp1 = lMessageLength + 8; var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64; var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16; var lWordArray = Array(lNumberOfWords - 1); var lBytePosition = 0; var lByteCount = 0; while (lByteCount < lMessageLength) { lWordCount = (lByteCount - (lByteCount % 4)) / 4; lBytePosition = (lByteCount % 4) * 8; lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition)); lByteCount++; } lWordCount = (lByteCount - (lByteCount % 4)) / 4; lBytePosition = (lByteCount % 4) * 8; lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition); lWordArray[lNumberOfWords - 2] = lMessageLength << 3; lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29; return lWordArray;}function WordToHex(lValue) { var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount; for (lCount = 0; lCount <= 3; lCount++) { lByte = (lValue >>> (lCount * 8)) & 255; WordToHexValue_temp = "0" + lByte.toString(16); WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2); } return WordToHexValue;}function Utf8Encode(string) { var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if ((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext;}module.exports = { md5: md5}
olami免費(fèi)開放語義平臺NLI接口封裝:NLI.js
var app = getApp()const appkey = require('../config').appkeyconst appsecret = require('../config').appsecretvar MD5 = require('./MD5.js');var UTIL = require('./util.js');const requestUrl = "https://cn.olami.ai/cloudservice/api";const api = "nli";function getLocationJson() { var lat = app.globalData.latitude; var lng = app.globalData.longitude; if (lat < 180 && lng < 180) { lat *= 1000000; lng *= 1000000; } return { "position_type": 0, "longitude": lng, "latitude": lat };}//NLI接口訪問//參數(shù):corpus為句子;cb為回調(diào)函數(shù)(帶兩個參數(shù),第一個參數(shù)表示調(diào)用NLI是否成功,第二個參數(shù)是NLI的返回?cái)?shù)據(jù))function process(corpus, cb) { var timestamp = new Date().getTime(); var originalSign = appsecret + "api=" + api + "appkey=" + appkey + "timestamp=" + timestamp + appsecret; var sign = MD5.md5(originalSign); var locationJson = getLocationJson(); var rqdataJson = { "data": { "input_type": 1, "text": corpus, "location": locationJson }, "data_type": "stt" }; var rqdata = JSON.stringify(rqdataJson); UTIL.log('input:' + rqdata + '\r\n, custId:' + app.globalData.custId + ', originalSign:' + originalSign); wx.request({ url: requestUrl, data: { appkey: appkey, api: api, timestamp: timestamp, sign: sign, rq: rqdata, cusid: app.globalData.custId, }, header: { 'content-type': 'application/x-www-form-urlencoded' }, method: 'POST', success: function (result) { var data = result.data.data; var jsonData = JSON.stringify(data); typeof cb == "function" && cb(true, jsonData) }, fail: function ({errMsg}) { typeof cb == "function" && cb(false, jsonData) } })}module.exports = { process: process,}
其它utils:util.js
//獲取應(yīng)用實(shí)例var app = getApp()var Guid = require('./GUID.js');var uuidSaved = '';//將date格式的數(shù)據(jù)轉(zhuǎn)成 hh:mm:ss 字符串格式function formatTime(date) { var year = date.getFullYear() var month = date.getMonth() + 1 var day = date.getDate() var hour = date.getHours() var minute = date.getMinutes() var second = date.getSeconds() //return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') return [hour, minute, second].map(formatNumber).join(':')}function formatNumber(n) { n = n.toString() return n[1] ? n : '0' + n}//log接口封裝function log(obj) { console.log('<' + formatTime(new Date(Date.now())) + '>' + ' ' + obj) //往用戶緩存去寫 // var logs = wx.getStorageSync('logs') || [] // logs.unshift('[' + formatTime(new Date(Date.now())) + ']' + ' ' + obj) // wx.setStorageSync('logs', logs)}//獲取用戶唯一標(biāo)識,NLI接口中要上傳用戶唯一標(biāo)識,這里獲取:第一次登錄時生成的uuid+微信號所在地+昵稱function getUserUnique(userInfo) { //從緩存中讀取uuid if (typeof uuidSaved === 'undefined' || uuidSaved === '') { var tmpUuid = wx.getStorageSync('uuid'); if (typeof tmpUuid === 'undefined' || tmpUuid === '') { uuidSaved = Guid.NewGuid(); wx.setStorageSync('uuid', uuidSaved); } else { uuidSaved = tmpUuid; } } var unique = uuidSaved; if (userInfo != null) { unique += '_' + userInfo.province + '_' + userInfo.nickName; } log('getUserUnique() return:' + unique) return unique;}module.exports = { formatTime: formatTime, log: log, getUserUnique: getUserUnique}
logs.js
var util = require('../../utils/util.js')Page({ data: { logs: [] }, onLoad: function () { this.setData({ logs: (wx.getStorageSync('logs') || []).map(function (log) { var DATE = new Date(log); if (typeof DATE === 'date') { //當(dāng)log是一個毫秒數(shù),延用官方的案例,轉(zhuǎn)成對應(yīng)時間存LOG中(util.formatTime被我改成只輸出時分秒) return util.formatTime(new Date(log)) } //非毫秒數(shù)時,記錄LOG原始內(nèi)容,不作轉(zhuǎn)換 return log; }) }) }})
logs.json
{ "navigationBarTitleText": "調(diào)試LOG頁"}
logs.wxml
<!--logs.wxml--><view class="container log-list"> <block wx:for="{{logs}}" wx:for-item="log" wx:key="*this"> <text class="log-item">{{index + 1}}. {{log}}</text> </block></view>
logs.wxss
.log-list { display: flex; flex-direction: column; padding: 40rpx;}.log-item { margin: 10rpx;}
index.js
// index.js//獲取應(yīng)用實(shí)例var app = getApp()var UTIL = require('../../utils/util.js');var GUID = require('../../utils/GUID.js');var NLI = require('../../utils/NLI.js');var cursor = 0;var lastYYYTime = new Date().getTime();var domainCorpus = '';var lastCorpus = '';function log(obj) { UTIL.log(obj)}Page({ isShow: false, /** * 頁面的初始數(shù)據(jù) */ data: { //調(diào)試后門 isDbg: false, //輸入框文本 inputTxt: '', //輸出框文本 outputTxt: '' }, /** * 生命周期函數(shù)--監(jiān)聽頁面加載 */ onLoad: function (options) { log('index.onLoad') //調(diào)用應(yīng)用實(shí)例的方法獲取全局?jǐn)?shù)據(jù) app.getUserInfo(function (userInfo2) { log('user unique 1: ' + UTIL.getUserUnique(userInfo2)) }) log('user unique 2: ' + UTIL.getUserUnique(app.globalData.userInfo)) }, /** * 生命周期函數(shù)--監(jiān)聽頁面初次渲染完成 */ onReady: function () { log('index.onReady') }, /** * 生命周期函數(shù)--監(jiān)聽頁面顯示 */ onShow: function () { log('index.onShow') var that = this; that.isShow = true; //調(diào)用重力加速度傳感API模擬搖一搖接口 wx.onAccelerometerChange(function (e) { if (!that.isShow) { //當(dāng)前界面不顯示時不應(yīng)該調(diào)用 return } if (isBong(e.x) && isBong(e.y)) { if (new Date().getTime() - lastYYYTime <= 2000) { //1秒限制搖一次,避免搖一下觸發(fā)多次請求 log('搖的太頻繁啦,請等2秒再搖!' + e.x + ', '+ e.y + ', ' + e.z); return; } //更新最后一次成功搖的時間戳 lastYYYTime = new Date().getTime() //從語料庫中挑選語料運(yùn)行語義理解,顯示結(jié)果 var selectedCorpus = selectCorpusRunNli(that); //彈Toast窗提示當(dāng)前刷到哪句語料 wx.showToast({ title: selectedCorpus, icon: 'success', duration: 1500 }); } }) }, /** * 生命周期函數(shù)--監(jiān)聽頁面隱藏 */ onHide: function () { log('index.onHide') //頁面隱藏后,關(guān)掉搖一搖檢測 wx.stopAccelerometer(); }, /** * 生命周期函數(shù)--監(jiān)聽頁面卸載 */ onUnload: function () { log('index.onUnload') }, /** * 頁面相關(guān)事件處理函數(shù)--監(jiān)聽用戶下拉動作 */ onPullDownRefresh: function () { log('index.onPullDownRefresh') wx.stopPullDownRefresh(); //頁面下拉,觸發(fā)輪換語料理解 selectCorpusRunNli(this) }, /** * 頁面上拉觸底事件的處理函數(shù) */ onReachBottom: function () { log('index.onReachBottom') }, /** * 用戶點(diǎn)擊右上角分享 */ onShareAppMessage: function () { log('index.onShareAppMessage') wx.showToast({ title: '謝謝分享!', icon: 'success', duration: 1500 }); }, switchChange: function (e) { log('index.switchChange by:' + e.detail.value) }, //輸入文本框聚焦觸發(fā)清除輸入框中的內(nèi)容 bindFocusClear: function(e) { log('index.bindFocusClear') if (e.detail.value === '') { return; } log('clear: ' + e.detail.value) var self = this; self.setData({ inputTxt: '' }); }, //點(diǎn)擊完成按鈕時觸發(fā) bindConfirmControl: function(e) { var inputTxt = e.detail.value; log('index.bindConfirmControl input string: ' + inputTxt); //手動打字輸入語料運(yùn)行語義理解 singleCorpusRunNli(inputTxt, this) }, //測試按鈕觸發(fā)事件 bindTest: function () { log('index.bindTest') //測試按鈕替代搖一搖,從語料庫中選語料測試 selectCorpusRunNli(this) }, //快捷按鈕觸發(fā)語料運(yùn)行語義理解 bindCorpusGenerator: function (e) { log('index.bindCorpusGenerator') //獲取"data-cp"中的語料 var corpusList = e.target.dataset.cp.split('|'); //默認(rèn)頭一次(或新切換時)點(diǎn)擊,選用第一句語料 var corpus = corpusList[0]; if (domainCorpus !== corpusList[0]) { domainCorpus = corpusList[0]; } else { //否則在語料表中隨機(jī)挑選一個 corpus = getRandomItem(corpusList); //與上一句重復(fù)就換一句 if (lastCorpus === corpus) { corpus = getRandomItem(corpusList); if (lastCorpus === corpus) { corpus = getRandomItem(corpusList); if (lastCorpus === corpus) { corpus = getRandomItem(corpusList); } } } } //記錄最近一次使用的語料 lastCorpus = corpus; singleCorpusRunNli(corpus, this); }})//從語料數(shù)組中隨機(jī)挑選一條語料function getRandomItem(corpusList) { var ret = corpusList[0]; ret = corpusList[Math.floor(Math.random() * corpusList.length)]; return ret;}//解析NLI接口返回的數(shù)據(jù),從語義結(jié)果中篩選出適合顯示的文本內(nèi)容function getSentenceFromNliResult(nliResult) { var sentence; try { var resultJson = JSON.parse(nliResult); var nliArray = resultJson.nli; for (var i = 0; i < nliArray.length; i++) { var singleResult = nliArray[i]; sentence = singleResult.desc_obj.result; var content = ''; var appName = singleResult.type; //0: normal, 1: selection, 9 : openweb var tagType = 0; if (appName === 'selection') { appName = singleResult.desc_obj.type; tagType = 1; } else if (appName === 'openweb') { //appName = singleResult.desc_obj.type; tagType = 9; } switch (appName) { case 'joke' : case 'story' : sentence = singleResult.data_obj[0].content; break; case 'poem' : if (tagType === 1) { for (var k = 0; k < singleResult.data_obj.length; k++) { content += '第' + (k + 1) + '個: ' + singleResult.data_obj[k].author + ' ' + singleResult.data_obj[k].poem_name + '\n'; } } break; case 'cooking': if (tagType === 1) { for (var k = 0; k < singleResult.data_obj.length; k++) { content += '第' + (k + 1) + '個: ' + singleResult.data_obj[k].name + '\n'; } } else if (tagType === 0) { content = singleResult.data_obj[0].content; } break; case 'baike' : var filedNames = singleResult.data_obj[0].field_name; var filedValues = singleResult.data_obj[0].field_value; for (var k = 0; k < filedNames.length; k++) { content += filedNames[k] + ' : ' + filedValues[k] + '\n'; } break; case 'news' : if (tagType === 1) { for (var k = 0; k < singleResult.data_obj.length; k++) { content += singleResult.data_obj[k].title + '\n'; content += singleResult.data_obj[k].detail + '…………\n'; content += '【欲看此條新聞詳情請說第' + (k+1) + '個,或拷此鏈接到瀏覽器中打開:' + singleResult.data_obj[k].ref_url + '】\n\n'; } } else if (tagType === 0) { content = singleResult.data_obj[0].detail; } break; case 'stock' : var nowHour = new Date().getHours(); var isKaiPan = (nowHour >= 9) && (nowHour <= 15); for (var k = 0; k < singleResult.data_obj.length; k++) { content += singleResult.data_obj[k].name + ' : ' + singleResult.data_obj[k].id + '\n'; content += '開盤:' + singleResult.data_obj[k].price_start + '\n'; if (isKaiPan) { content += '現(xiàn)價(jià)(' + getHHMMSS(singleResult.data_obj[k].time) + '):' + singleResult.data_obj[k].cur_price + '\n'; } else { content += '收盤:' + singleResult.data_obj[k].price_end + '\n'; } content += '成交量:' + singleResult.data_obj[k].volume + '\n'; content += '成交額:' + singleResult.data_obj[k].amount + '\n'; content += '最高價(jià):' + singleResult.data_obj[k].price_high + '\n\n'; } break; case 'tvprogram': for (var k = 0; k < singleResult.data_obj.length; k++) { content += singleResult.data_obj[k].time + ' ' + singleResult.data_obj[k].name + '\n'; } break; case 'openweb': content += '【拷此鏈接到瀏覽器中打開:' + singleResult.data_obj[0].url + '】\n'; break; defalt : break; } if (content !== '') { sentence += '\n\n' + content; } log('NLI返回sentence:' + sentence) } } catch (e) { log('錯誤' + e.message + '發(fā)生在' + e.lineNumber + '行'); sentence = '沒明白你說的,換個話題?' } if (typeof sentence === 'undefined' || sentence === '') { sentence = '沒明白你說的什么意思'; } return sentence;}//由 毫秒數(shù)字符串 獲取 HH:mm:ss 格式時間function getHHMMSS(dateStr) { var dateVal = new Date(parseInt(dateStr)); var hh = dateVal.getHours() var mm = dateVal.getMinutes() var ss = dateVal.getSeconds() return ((hh >= 10) ? hh : '0' + hh) + ':' + ((mm >= 10) ? mm : '0' + mm) + ':' + ((ss >= 10) ? ss : '0' + ss)}//處理NLI語義結(jié)果function NliProcess(corpus, self) { var nliResult; NLI.process(corpus, function (isSuccess, result) { if (isSuccess) { } else { } nliResult = result; log("NLI RESULT IS:" + nliResult); typeof self !== 'undefined' && self.setData ({ outputTxt: getSentenceFromNliResult(nliResult) }) })}//如輸入是dbg且確認(rèn),則切換到debug模式,多顯示兩個按鈕function singleCorpusRunNli(inputTxt, self) { if (inputTxt === 'dbg' || inputTxt === 'DBG' || inputTxt === 'Dbg') { self.setData({ //打開調(diào)試按鈕 isDbg: true, inputTxt: '' }); // 打開調(diào)試 wx.setEnableDebug({ enableDebug: true }) return; } else if(inputTxt === 'gbd' || inputTxt === 'GBD' || inputTxt === 'Gbd') { self.setData({ //關(guān)閉調(diào)試按鈕 isDbg: false, inputTxt: '' }); // 關(guān)閉調(diào)試 wx.setEnableDebug({ enableDebug: false, }) return; } //正常用戶輸入文本 if (inputTxt !== '') { self.setData({ inputTxt: inputTxt }); NliProcess(inputTxt, self); }}//選擇預(yù)置語料運(yùn)行語義理解function selectCorpusRunNli(self) { var corpus = app.globalData.corpus; //順序選擇一句語料 var corpusSelected = corpus[cursor] self.setData({ //更新頁面input框顯示 inputTxt: corpusSelected }) log('selected corpus:' + corpusSelected) //調(diào)用語料處理,刷新輸出框結(jié)果 NliProcess(corpusSelected, self); //光標(biāo)后移,備下次挑選下一條語料 if (cursor++ >= corpus.length - 1) { cursor = 0; } return corpusSelected;}//檢測加速度傳感器靈敏度function isBong(val) { //目前靈敏度設(shè)置為0.8,且搖一搖只監(jiān)測了x和y軸變動,具體見調(diào)用此函數(shù)的地方 if (val >= 0.8 || val <= -0.8) { return true; } return false;}
index.json
首頁延用app.json中的配置,不需要單獨(dú)的配置,這里主要配置一個允許下拉。
{ "window": { "enablePullDownRefresh": true }}
index.wxml
這個是首頁的h5布局。
<view class="container"> <view class="page-section"> <view class="text-box" scroll-y="true"> <text style="max-width:200px;overflow-y:auto;height:200px;" selectable="true">{{outputTxt}}</text> </view> </view> <view class="page-section page-gap page-center"> <text selectable="true" class="text-head">語義理解基于“歐拉密”:cn.olami.ai</text> <text selectable="true" class="text-description little-gap-top">支持“搖一搖”、點(diǎn)按鈕、手動輸入、向下拉</text> </view> <view class="page-section"> <view class="weui-cells weui-cells_after-title"> <input class="weui-input" placeholder-style="color:#6aa84f" maxlength="50" placeholder="點(diǎn)此手動輸入" value="{{inputTxt}}" confirm-type ="send" bindconfirm="bindConfirmControl" bindfocus="bindFocusClear"/> </view> </view> <view class="button-selection page-gap"> <view class="{{isDbg?'button-show':'common-disappear'}}"> <button type="default" size="mini" bindtap="bindTest">調(diào)試</button> </view> <view class="button-selection2"> <button type="default" size="mini" data-cp="今天天氣|北京今天有雨嗎|西安今天冷不冷|今天適合洗車嗎|那后天上海怎么樣|北京明天有霧霾嗎|南京今天空氣污染指數(shù)有多高" bindtap="bindCorpusGenerator">天氣</button> <button type="default" class="little-gap-left" size="mini" data-cp="今天的體育新聞|看國內(nèi)新聞|今天有什么重大新聞|今天有沒有什么大事|有今天的財(cái)經(jīng)新聞嗎|釣魚島事件的最新進(jìn)展是什么|來個軍事新聞|接下來還有嗎" bindtap="bindCorpusGenerator">新聞</button> <button type="default" class="little-gap-left" size="mini" data-cp="今年什么時候中秋節(jié)|目前時間|明天星期幾|農(nóng)歷八月15是哪天|離國慶節(jié)還有幾天|還有多久到六點(diǎn)|今年是閏年嗎?" bindtap="bindCorpusGenerator">日歷</button> <button type="default" class="little-gap-left" size="mini" data-cp="100人民幣能換多少美金|美國用什么貨幣|100美金能換多少日元|英鎊現(xiàn)在多少錢|美元現(xiàn)在什么價(jià)格|幫我查查美元匯率" bindtap="bindCorpusGenerator">匯率</button> <button type="default" class="little-gap-left" size="mini" data-cp="來個段子|說個笑話聽聽|講個黑色幽默|講個笑話逗逗我|還要一個笑話|換一個|隨便來幾個笑話聽聽" bindtap="bindCorpusGenerator">笑話</button> </view> <view class="button-selection2"> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="我要看鬼故事|給我講個好聽的故事|下一個|換一個" bindtap="bindCorpusGenerator">故事</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="好無聊啊|你是機(jī)器人嗎|很高興見到你|你能嫁給我嗎|你的心上人是誰|你今天吃飯了嗎|你困了嗎|喂,你好嗎|你喜歡吃蔬菜嗎|你餓嗎|你今天過生日嗎|你什么時候出生的|你要喝水嗎|能和我聊會天嗎|你怎么那么笨啊|我在罵你|你聽不懂嗎|我要生氣啦 |你想要?dú)馑牢野怎么什么都不會|你真讓我失望|你寂寞嗎|你覺得自己聰明嗎|我好傷心啊|你有外號嗎|你需要休息嗎|你要不要休息會啊|你感覺累嗎|你臉皮真薄" bindtap="bindCorpusGenerator">閑聊</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="介紹一下劉德華|李雙江的老婆是誰|張學(xué)友簡介|黃山有多高|湖南省有多大|太湖有多大|印度有多少人口" bindtap="bindCorpusGenerator">百科</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="背一首李白的望廬山瀑布|春眠不覺曉下一句是什么|唯見長江天際流上一句是什么|將進(jìn)酒是誰寫的|李白有什么詩|李白還寫過什么詩|背首李白的詩" bindtap="bindCorpusGenerator">詩詞</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="6789算24點(diǎn)怎么算|用三三八八算出24|1357怎么等于24|1689怎么得到24|怎么用3567得到24|1357幫我刷24點(diǎn)|算24點(diǎn)數(shù)字為2346" bindtap="bindCorpusGenerator">24點(diǎn)</button> </view> <view class="button-selection2"> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="江西婺源的郵編是多少|(zhì)哪里的郵政編碼是201203|什么地方的郵政編碼是201203|我要你幫我查郵政編碼|我要查郵政編碼|告訴我北京的郵政編碼|給我查個郵政編碼|我要查郵編|幫我查郵政編碼|找一下上海的郵政編碼" bindtap="bindCorpusGenerator">郵編</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="幫忙查一下?lián)P州的區(qū)號|哪里的區(qū)號是021|區(qū)號021是哪里|我要查區(qū)號|幫我查區(qū)號|找一下上海的區(qū)號" bindtap="bindCorpusGenerator">區(qū)號</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="鱸魚可以怎么做|排骨怎么烹飪好吃|怎樣燉排骨|排骨可以做成什么菜|常見的魯菜有哪些|川菜有哪些菜式比較有名|有哪些川菜比較有名|哪些菜屬于魯菜|川菜怎么做|你會做川菜嗎|推薦幾種清真菜|介紹幾個湘菜給我" bindtap="bindCorpusGenerator">菜譜</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="今天的大盤指數(shù)|中國石化的行情怎么樣|今天的行情|中國石化的價(jià)格是多少|(zhì)中國石化的成交量怎么樣|上證指數(shù)現(xiàn)在多少點(diǎn)|查一下招商銀行今日的開盤價(jià)|今天上證指數(shù)幾點(diǎn)" bindtap="bindCorpusGenerator">股票</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="我想買個電風(fēng)扇|我要買兩袋大米|我要網(wǎng)購一盒牛奶|我要在京東買一臺電腦|我要買一部HTC的手機(jī)|我想買個飛利浦的剃須刀" bindtap="bindCorpusGenerator">購物</button> </view> <view class="button-selection2"> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="今晚番茄臺放什么節(jié)目|中央一臺今晚有什么節(jié)目|非誠勿擾今晚什么時候播出|7點(diǎn)到9點(diǎn)哪個臺有電影|晚上11點(diǎn)以后哪個臺放蜘蛛俠|我要看湖南衛(wèi)視" bindtap="bindCorpusGenerator">節(jié)目預(yù)告</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="3的平方加8再乘以6再除以2等于幾|5的平方根|6的七次方|99的三倍|19的三分之一是多少|(zhì)77的一半加13等于多少|(zhì)67個9加起來是多少|(zhì)5個15乘在一起等于幾|4乘以6加5乘以8加12乘以7|四分之三|7的9倍|三的一半|17個9|12的對數(shù)|23的自然對數(shù)|5加六加七再乘10|三只蘋果加五只蘋果等于幾只蘋果|19加7" bindtap="bindCorpusGenerator">計(jì)算</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="搜索薛之謙|幫我找一些九寨溝的圖片|我想看看范冰冰的照片|我要查一下白羽雞|幫我搜下科魯茲的性能" bindtap="bindCorpusGenerator">搜索</button> <button type="default" class="little-gap-left little-gap-top" size="mini" data-cp="1光年等于多少公里|1標(biāo)準(zhǔn)大氣壓等于多少帕|1標(biāo)準(zhǔn)大氣壓等于多少毫米汞柱|1兆瓦等于多少毫瓦|1簽卡等于多少焦|1納米等于多少毫米|1埃等于多少納米|1厘米等于多少英寸|1公斤等于多少克|1擔(dān)等于多少斤|1兩等于多少錢|1毫克等于多少微克|1畝等于多少公頃" bindtap="bindCorpusGenerator">單位換算</button> </view> </view></view><view class="little-gap-top button-selection2 button-show bottom-button"> <button type="primary" size="mini" open-type="contact">聯(lián)系作者</button> <button type="primary" size="mini" open-type="share">幫忙分享</button></view>
index.wxss
首頁用到的一些樣式,以及配置首頁背景圖片。
page{ background-color:beige; background-image: url(http://img.blog.csdn.net/20170720105808995?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFwcHljeHo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast); background-size: cover;}.page-section{ display: flex; flex-direction: column; margin-bottom: 10rpx;}.page-center { align-items: center;}.page-gap{ margin-top: 5rpx;}.button-selection { display: flex; flex-direction: column; justify-content: center; align-items: center;}.button-selection2 { justify-content: space-between; align-content: space-between; flex-shrink:1;}.little-gap-top { margin-top: 15rpx; }.little-gap-left { margin-left: 5rpx; }.text-description{ color: #ff0000; font-size: 28rpx;}.text-head{ color: #00BFFF; font-size: 36rpx;}.common-disappear { display: none}.button-show { display: flex; align-self: center; justify-content: center;}.text-box{ margin-bottom: 0rpx; margin-left: 50rpx; margin-right: 50rpx; padding: 40rpx 0; display: flex; min-height: 300rpx; max-width: 600rpx; width:600rpx; background-color: #ffffff; justify-content: center; align-items: center; text-align: left; font-size: 30rpx; color: #353535; line-height: 2em; word-wrap: break-word; border: 1px solid cornflowerblue;}.weui-cells { position: relative; margin-top: 1.17647059em; background-color: #DCDCDC; line-height: 1.41176471; font-size: 14px;}.weui-cells_after-title { margin-top: 0;}.weui-input { height: 2.58823529em; width: 17em; min-height: 2.58823529em; line-height: 2.58823529em;}.bottom-button { justify-content: space-around; flex-shrink:0;}
,在配置上稍微有些不同,下面我會重點(diǎn)說明一下。
點(diǎn)“配置模塊”進(jìn)去,會有兩個標(biāo)簽頁:
a. “NLI模塊”:我之前做過一個匯率換算的小DEMO 用olami開放語義平臺做匯率換算應(yīng)用 用的是使用“NLI模塊”的功能,其實(shí)就是語義接口。
b. “對話系統(tǒng)模塊”:這次做“遙知之”微信小程序,用的是他們平臺的“處理結(jié)果”的能力,就是對應(yīng)“對話系統(tǒng)模塊”這里。
注:有些朋友估計(jì)會有點(diǎn)弄不清楚,這里我舉個例子,比如“今天上海的天氣如何”這一句話:應(yīng)用配置在“NLI模塊”里勾天氣相對應(yīng)的模塊,接口會返回這一句話的意思“今天,上海,查天氣”; 應(yīng)用配置在“對話系統(tǒng)模塊”里勾“weather”,接口返回的就是今天上海天氣的查詢結(jié)果。
好了,弄明白上述兩塊,接下來就知道,這次我做的“遙知之”全是要結(jié)果輸出的,所以把“對話系統(tǒng)模塊”中所有的功能模塊全勾選上了,后面有配置優(yōu)先級的,優(yōu)先級是用來處理語義沖突時仲裁哪個模塊優(yōu)先的,我都沒有動,直接用默認(rèn)值,根據(jù)提示:數(shù)值越小優(yōu)先級越高,1000是優(yōu)先級最高。
“遙知之NLI能力”應(yīng)用中有個“測試”,需要的話,可點(diǎn)進(jìn)去輸入一些句子測輸出結(jié)果(注:這里是NLI接口輸出的一部分)。