准备工具:Eclipse、FileZilla、微信开发者工具、一个配置好SSL证书(https)的有域名的服务器

所需知识:SpringMVC框架、Java+HTML+CSS+JS、文件上传技术、Tomcat虚拟目录、接口调用与发布

成品介绍:将中文语音直接转化成英文语音。好久不用现在已下线。。。可以在微信搜我另外一个作品“纯音乐猜歌”玩玩,博客地址:

https://blog.csdn.net/qq_37518622/article/details/81040200

最近新作:python scrapy爬取豆瓣即将上映电影用邮件定时推送给自己

一、服务端

基本思路

1、将汉语语音转化为汉语文字

2、将汉语文字转化为英语文字

3、将英语文字转化为英语语音

步骤

1、注册百度语音账户,创建应用,获取API Key 和Secret Key,参照百度文档中心

2、看百度提供的文档编写识别语音的工具类

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;import javax.xml.bind.DatatypeConverter;import priv.xxf.jgsawvoice.entity.JSONArray;
import priv.xxf.jgsawvoice.entity.JSONObject;@SuppressWarnings("restriction")
public class VoiceRecogUtil {private static final String serverURL = "http://vop.baidu.com/server_api";// API地址// 开发密钥private static final String apiKey = "你的apiKey";private static final String secretKey = "你的secretKey";private static final String cuid = "随便定义个字符串";private static String token = "";// 根据密钥获取的tokenpublic static String getTextByVoice(String fileName) throws Exception {getToken();// 获取token// 发送请求得到结果String string = getResultString(fileName);// 解析jsonJSONArray jsonArray = new JSONObject(string).getJSONArray("result");int begin = jsonArray.toString().indexOf("\"");int end = jsonArray.toString().lastIndexOf("\"");String result = jsonArray.toString().substring(begin + 1, end);return result;}private static void getToken() throws Exception {String getTokenURL = "https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials" + "&client_id="+ apiKey + "&client_secret=" + secretKey;HttpURLConnection conn = (HttpURLConnection) new URL(getTokenURL).openConnection();token = new JSONObject(getResponse(conn)).getString("access_token");}@SuppressWarnings("restriction")private static String getResultString(String fileName) throws Exception {File pcmFile = new File(fileName);int index = fileName.lastIndexOf(".");String suffix = fileName.substring(index + 1);HttpURLConnection conn = (HttpURLConnection) new URL(serverURL).openConnection();// construct paramsJSONObject params = new JSONObject();params.put("format", suffix);// 音频后缀params.put("rate", 16000);// 比特率params.put("channel", "1");// 固定值params.put("token", token);// tokenparams.put("cuid", cuid);// 用户请求的唯一标识params.put("len", pcmFile.length());// 文件长度params.put("speech", DatatypeConverter.printBase64Binary(loadFile(pcmFile)));// base64编码后的音频文件// add request headerconn.setRequestMethod("POST");conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");conn.setDoInput(true);conn.setDoOutput(true);// send requestDataOutputStream wr = new DataOutputStream(conn.getOutputStream());wr.writeBytes(params.toString());wr.flush();wr.close();return getResponse(conn);}private static String getResponse(HttpURLConnection conn) throws Exception {if (conn.getResponseCode() != 200) {// request errorreturn "";}InputStream is = conn.getInputStream();BufferedReader rd = new BufferedReader(new InputStreamReader(is));String line;StringBuffer response = new StringBuffer();while ((line = rd.readLine()) != null) {response.append(line);response.append('\r');}rd.close();return response.toString();}private static byte[] loadFile(File file) throws IOException {InputStream is = new FileInputStream(file);long length = file.length();byte[] bytes = new byte[(int) length];int offset = 0;int numRead = 0;while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {offset += numRead;}if (offset < bytes.length) {is.close();throw new IOException("Could not completely read file " + file.getName());}is.close();return bytes;}
}

3、创建百度翻译账户,获取securityKey,编写文字翻译工具类

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;public class LanguageTranslateUtil {private static final String TRANS_API_HOST = "http://api.fanyi.baidu.com/api/trans/vip/translate";// API地址private static final String appid = "你的appid";// 应用的idprivate static final String securityKey = "百度翻译的securityKey"//securityKeypublic static String getTransResultFromChToEn(String query) throws UnsupportedEncodingException {return getTransResult(query, "auto", "en");}public static String getTransResult(String query, String from, String to) throws UnsupportedEncodingException {Map<String, String> params = buildParams(query, from, to);String string = HttpGet.get(TRANS_API_HOST, params);// 解析json串得到结果int start = string.indexOf("\"dst\":");int end = string.lastIndexOf("\"");String result = string.substring(start + 7, end);return result;}private static Map<String, String> buildParams(String query, String from, String to)throws UnsupportedEncodingException {Map<String, String> params = new HashMap<String, String>();params.put("q", query);params.put("from", from);params.put("to", to);params.put("appid", appid);// 随机数String salt = String.valueOf(System.currentTimeMillis());params.put("salt", salt);// 签名String src = appid + query + salt + securityKey; // 加密前的原文params.put("sign", MD5.md5(src));return params;}// public static void main(String[] args) throws// UnsupportedEncodingException {// String en = getTransResultFromChToEn("你好");// System.out.println(en);// }
}

4、下载百度语音合成的SDK,放到WEB-INF下的lib目录,添加至构建路径,编写语音合成工具类,直接调用SDK的API

import java.io.File;
import java.io.IOException;
import java.util.HashMap;import org.json.JSONObject;import com.baidu.aip.speech.AipSpeech;
import com.baidu.aip.speech.TtsResponse;
import com.baidu.aip.util.Util;public class TextRecogUtil {// 设置APPID/AK/SKpublic static final String APP_ID = "你的appID";public static final String API_KEY = "和语音识别一样的api_key";public static final String SECRET_KEY = "和语音识别一样的secret_key";public static final String FILE_ROOT = "你的服务器上放语音文件的根目录";public static String getVoiceByText(String text, String file) {// 初始化一个AipSpeechAipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);// 可选:设置网络连接参数client.setConnectionTimeoutInMillis(2000);client.setSocketTimeoutInMillis(60000);// 可选:设置代理服务器地址, http和socket二选一,或者均不设置// client.setHttpProxy("proxy_host", proxy_port); // 设置http代理// client.setSocketProxy("proxy_host", proxy_port); // 设置socket代理// 调用接口HashMap<String, Object> options = new HashMap<String, Object>();options.put("spd", "5");// 语速options.put("pit", "0");// 音调options.put("vol", "15");// 音量options.put("per", "3");// 音色TtsResponse res = client.synthesis(text, "zh", 1, options);byte[] data = res.getData();JSONObject res1 = res.getResult();if (data != null) {try {// 若没有文件夹创建文件夹int index = file.lastIndexOf("/");String substring = file.substring(0, index);File temp1 = new File(substring);if (!temp1.exists()) {temp1.mkdirs();}File temp2 = new File(file);temp2.createNewFile();Util.writeBytesToFileSystem(data, file);} catch (IOException e) {e.printStackTrace();}}if (res1 != null) {System.out.println(res1.toString(2));}int path = file.indexOf("项目名");String suffix = file.substring(path);return FILE_ROOT + "/" + suffix;}}

5、编写上传音频文件的工具类

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;import org.springframework.web.multipart.MultipartFile;public class WeChatFileUploadUtil {private static final String TEMP_DIR = "/root/file/temp";//你服务器的临时文件路径//返回文件的唯一标识public static String getUploadFilePath(MultipartFile file) {String result;try {InputStream in = file.getInputStream();// 真正写到磁盘上String uuid = IDUtil.generateString(10);String filePath = TEMP_DIR + "/" + uuid + ".mp3";File temp = new File(filePath);OutputStream out = new FileOutputStream(temp);int length = 0;byte[] buf = new byte[100];while ((length = in.read(buf)) != -1) {out.write(buf, 0, length);}in.close();out.close();result = filePath;} catch (Exception e) {result = TEMP_DIR + "/error.mp3";//全局异常结果}return result;}public static void deleteTempFile(String file) {File temp = new File(file);if (temp.exists()) {temp.delete();}}
}

6、由于小程序录音的采样率与百度语音识别的采用率不同,还需要在服务器上安装ffmpeg转码。安装教程看这里。然后编写转码工具类

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;public class FileFormatTransformUtil {// private static final String FFMPEG_LOCATION =// "D:\\Eclipse\\eclipse\\WorkSpace\\src\\main\\resources\\ffmpeg.exe";//Windows下的ffmpeg路径public static String transformSound(String mp3, String path) {File file = new File(path);if (!file.exists()) {file.mkdirs();}String uuid = IDUtil.generateString(10);String result = path + "/" + uuid;List<String> commend = new ArrayList<String>();commend.add("ffmpeg");// 如果在Windows下换成FFMPEG_LOCATIONcommend.add("-y");commend.add("-i");commend.add(mp3);commend.add("-acodec");commend.add("pcm_s16le");commend.add("-f");commend.add("s16le");commend.add("-ac");commend.add("1");commend.add("-ar");commend.add("16000");commend.add(result + ".pcm");StringBuffer test = new StringBuffer();for (int i = 0; i < commend.size(); i++)test.append(commend.get(i) + " ");System.out.println("转化" + result + ".pcm成功" + new Date());ProcessBuilder builder = new ProcessBuilder();builder.command(commend);try {builder.start();} catch (IOException e) {e.printStackTrace();}return uuid;}// public static void main(String[] args) {// FileFormatTransformUtil.transformSound("D:\\temp\\1.mp3",// "D:\\temp\\2.pcm");// }
}

7、自定义消息实体类,用于返回请求的json串

public class Message {private String code;// 200正常,404异常private String msg;// 提示消息private String content;// 正常返回声音的url,异常返回全局异常声音的urlpublic String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}}

8、编写Controller,发布接口以便小程序调用

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;import net.sf.json.JSONObject;
import priv.xxf.jgsawvoice.entity.Message;
import priv.xxf.jgsawvoice.utils.FileFormatTransformUtil;
import priv.xxf.jgsawvoice.utils.LanguageTranslateUtil;
import priv.xxf.jgsawvoice.utils.TextRecogUtil;
import priv.xxf.jgsawvoice.utils.VoiceRecogUtil;
import priv.xxf.jgsawvoice.utils.WeChatFileUploadUtil;@Controller
public class VoiceTansformContoller {private static final String DEFAULT_USER_ID = "defaultdir";// 如果用户不授权就使用默认文件夹授权就根据用户名创建一个文件夹private static final String SRC_PATH = "/root/file/source";// 待转化音频private static final String DST_PATH = "/root/file/destination";// 转化后的音频private static final String ERROR_RESULT = "https:你的网站域名/资源文件夹/error.mp3";// 全局异常结果@RequestMapping("/getVoice")@ResponseBodypublic JSONObject index(@RequestParam(value = "file") MultipartFile file, String userId) throws Exception {// userId标识唯一用户,如果不授权存放至默认文件夹if (userId == null) {userId = DEFAULT_USER_ID;}Message message = new Message();try {// 中文的id转成英文try {userId = Long.parseLong(userId) + "";} catch (Exception e) {userId = LanguageTranslateUtil.getTransResultFromChToEn(userId).replaceAll(" ", "");}// 将用户的音频存放在服务器的临时文件夹,并获取唯一文件名String sourceFile = WeChatFileUploadUtil.getUploadFilePath(file);// 转化成能被百度语音识别的文件String uuid = FileFormatTransformUtil.transformSound(sourceFile, SRC_PATH + "/" + userId);// 删除临时文件WeChatFileUploadUtil.deleteTempFile(sourceFile);// 将音频识别成文字String text = VoiceRecogUtil.getTextByVoice(SRC_PATH + "/" + userId + "/" + uuid + ".pcm");System.out.println(text);// 翻译String englishText = LanguageTranslateUtil.getTransResultFromChToEn(text);System.out.println(englishText);// 将文字转化成语音存到对应文件夹String resultFile = TextRecogUtil.getVoiceByText(englishText,DST_PATH + "/" + userId + "/" + uuid + ".mp3");message.setCode("200");message.setMsg("success");message.setContent(resultFile);} catch (Exception e) {// 异常处理message.setCode("404");message.setMsg("fail");message.setContent(ERROR_RESULT);}JSONObject result = JSONObject.fromObject(message);return result;}}

9、将项目maven install一下,打包成war文件,丢到tomcat的webapp文件夹下,tomcat会自动解包,访问接口是否能调用成功

二、小程序

基本思路

1、调用小程序API录音

2、调用服务端接口转化音频

3、获取接口数据让小程序使用

步骤

1、到微信公众平台注册小程序开发账户并下载微信开发者工具,打开工具,输入项目目录、AppID、项目名,其中项目目录为将要创建的小程序的代码的根目录或是已有的项目的根目录

2、查看小程序API,先将界面绘画出来,编写wxml(相当于html),这里的很多i标签是配合wxss(相当于css)渲染页面用的

<!--index.wxml-->
<view class="container"><view class="wrapper"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></view><view class="userinfo"><button wx:if="{{!hasUserInfo && canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo"> 获取头像昵称 </button><block wx:else><image bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image></block><text class="author-info" style='margin-top:50px;'>作者邮箱: 1553197252@qq.com</text></view><audio poster="{{poster}}" name="{{name}}" author="{{author}}" src="{{src}}" id="myAudio" class='audio'></audio><view class="usermotto"><!--<text class="user-motto">{{motto}}</text>--><button class='button blue big rounded' bindlongtap='startRecord' bindtouchend='endRecord' style='margin-top:120px;'>录音</button><button class='button green big rounded' style='margin-left:20px;margin-top:120px;' bindtap='replayRecord'>回放</button></view>
</view>

3、编写wxss文件(太长,给出部分)

/**index.wxss**/
.container{
overflow: hidden;
background: #3e6fa3;
height: 150%;
}
.userinfo {display: flex;flex-direction: column;align-items: center;
}.userinfo-avatar {width: 128rpx;height: 128rpx;margin: 20rpx;border-radius: 50%;
}.userinfo-nickname {color: #aaa;
}.usermotto {margin-top: 100px;
}.btn-record{width: 120px;height: 60px;font: 13pt;line-height: 3.5em;background-color: green;display: inline-block;
}.btn-replay{width: 120px;height: 60px;font: 13pt;line-height: 3.5em;background-color: white;display: inline-block;
}.audio{display: none;}.wrapper {position: absolute;top: 50%;left: 50%;z-index: 2;-moz-perspective: 500px;-webkit-perspective: 500px;perspective: 500px;
}i {display: block;position: absolute;width: 8px;height: 8px;border-radius: 8px;opacity: 0;background: rgba(255, 255, 255, 0.5);box-shadow: 0px 0px 10px white;animation-name: spin;animation-duration: 3s;animation-iteration-count: infinite;animation-timing-function: ease-in-out;
}i:nth-child(1) {-moz-transform: rotate(11.6129deg) translate3d(80px, 0, 0);-ms-transform: rotate(11.6129deg) translate3d(80px, 0, 0);-webkit-transform: rotate(11.6129deg) translate3d(80px, 0, 0);transform: rotate(11.6129deg) translate3d(80px, 0, 0);animation-delay: 0.04839s;
}i:nth-child(2) {-moz-transform: rotate(23.22581deg) translate3d(80px, 0, 0);-ms-transform: rotate(23.22581deg) translate3d(80px, 0, 0);-webkit-transform: rotate(23.22581deg) translate3d(80px, 0, 0);transform: rotate(23.22581deg) translate3d(80px, 0, 0);animation-delay: 0.09677s;
}i:nth-child(3) {-moz-transform: rotate(34.83871deg) translate3d(80px, 0, 0);-ms-transform: rotate(34.83871deg) translate3d(80px, 0, 0);-webkit-transform: rotate(34.83871deg) translate3d(80px, 0, 0);transform: rotate(34.83871deg) translate3d(80px, 0, 0);animation-delay: 0.14516s;
}i:nth-child(4) {-moz-transform: rotate(46.45161deg) translate3d(80px, 0, 0);-ms-transform: rotate(46.45161deg) translate3d(80px, 0, 0);-webkit-transform: rotate(46.45161deg) translate3d(80px, 0, 0);transform: rotate(46.45161deg) translate3d(80px, 0, 0);animation-delay: 0.19355s;
}i:nth-child(5) {-moz-transform: rotate(58.06452deg) translate3d(80px, 0, 0);-ms-transform: rotate(58.06452deg) translate3d(80px, 0, 0);-webkit-transform: rotate(58.06452deg) translate3d(80px, 0, 0);transform: rotate(58.06452deg) translate3d(80px, 0, 0);animation-delay: 0.24194s;
}@keyframes spin {from {opacity: 0.0;}to {opacity: 0.6;transform: translate3d(-4px, -4px, 570px);}
}
#black {position: absolute;left: 10px;bottom: 10px;color: rgba(255, 255, 255, 0.6);text-decoration: none;
}
#black:after {content: 'Black & white';
}#black:target {top: 0;left: 0;width: 100%;height: 100%;z-index: 1;background: #111;cursor: default;
}
#black:target:after {content: 'xxxx';
}author-info{color: #840b2a;
}

4、编写js文件

//index.js
//获取应用实例
const app = getApp()
const recorderManager = wx.getRecorderManager()const options = {duration: 60000,sampleRate: 16000,numberOfChannels: 1,format: 'mp3',encodeBitRate: 24000//frameSize: 50
}var result
Page({data: {motto: '自定义motto',userInfo: {},hasUserInfo: false,canIUse: wx.canIUse('button.open-type.getUserInfo')},//事件处理函数bindViewTap: function() {wx.navigateTo({url: '../logs/logs'})},onLoad: function () {if (app.globalData.userInfo) {this.setData({userInfo: app.globalData.userInfo,hasUserInfo: true})} else if (this.data.canIUse){// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回// 所以此处加入 callback 以防止这种情况app.userInfoReadyCallback = res => {this.setData({userInfo: res.userInfo,hasUserInfo: true})}} else {// 在没有 open-type=getUserInfo 版本的兼容处理wx.getUserInfo({success: res => {app.globalData.userInfo = res.userInfothis.setData({userInfo: res.userInfo,hasUserInfo: true})}})}},getUserInfo: function(e) {console.log(e)app.globalData.userInfo = e.detail.userInfothis.setData({userInfo: e.detail.userInfo,hasUserInfo: true})},//录音键长按按钮startRecord: function(e){recorderManager.start(options)wx.showLoading({title: '正在录音',})},endRecord: function (e) {wx.hideLoading()recorderManager.stop()wx.showLoading({title: '正在翻译',})setTimeout(function () {wx.hideLoading()}, 5000)recorderManager.onStop((res) => {//录音结束后上传音频文件console.log('本地audio路径:', res.tempFilePath)wx.uploadFile({url: '接口地址', filePath: res.tempFilePath,name: 'file',//上传文件名(controller参数1)formData: {'userId': app.globalData.userInfo != null ? app.globalData.userInfo.nickName :'defaultdir'//userId(controller参数2)},success: function (res) {var data = res.datavar start = data.indexOf('https');var end = data.indexOf('mp3');result = data.substring(start,end+3)console.log(result)this.audioctx = wx.createAudioContext('myAudio');this.audioctx.setSrc(result);wx.hideLoading();// this.audioctx.autoplay=true;this.audioctx.play();// wx.playBackgroundAudio({//   dataUrl: result,//   title: '丁',//   coverImgUrl: '啊'// })},fail: function (res) {var data = res.datawx.showToast({title: '失败那',icon: 'none',duration: 1000})}})})recorderManager.onError((res) => {console.log('recorder stop', res)wx.showToast({title: '录音时间太短',icon: 'none',duration: 1000})})},replayRecord: function(e) {//回放函数audioctx = wx.createInnerAudioContext('myAudio');if (result == undefined){result ='提示url';//如果还没录音就点回放}audioctx.src = result;audioctx.play();console.log(app.globalData.userInfo != null ? app.globalData.userInfo : 'defaultdir')}})

5、上传小程序代码,查看审核规范,确保符合规范后再提交审核,等待结果,特别注意UI要符合规范,要提供作者联系方式

6、审核通过前,可以在小程序管理中添加用户并授予体验者权限,发送体验版二维码来访问体验版的小程序,审核通过后就直接能在微信小程序上搜到了

结语

百度还有很多API,都可以调用玩玩啊,还有其他公司的API,也可以尝试自己写个很6的算法做一个能很好地满足用户的程序,又或者是你手头有很好又很难找到的合法的资源都可以做个不错的个人小程序,我这个小程序实际上毫无软用,体验一下小程序开发罢了。总之我觉得有创新的思路还是最重要的,技术其次,可以慢慢学。

微信小程序个人开发全过程相关推荐

  1. 微信小程序开发与mysql_微信小程序云开发之云数据库入门

    微信小程序云开发之云数据库入门 介绍 开发者可以使用云开发开发微信小程序.小游戏,无需搭建服务器,即可使用云端能力. 其基础能力数据库,是一个JSON数据库,作用是无需自建数据库,就可以在微信小程序前 ...

  2. 微信小程序快速开发:视频指导版

    <微信小程序快速开发:视频指导版>是2017年5月由人民邮电出版社出版的图书,作者是易伟.本书根据微信小程序的内容,全面系统地介绍了微信小程序的搭建和开发.本书主要内容有小程序注册.编程基 ...

  3. 微信小程序云开发之云数据库入门

    微信小程序云开发之云数据库入门 介绍 开发者可以使用云开发开发微信小程序.小游戏,无需搭建服务器,即可使用云端能力. 其基础能力数据库,是一个JSON数据库,作用是无需自建数据库,就可以在微信小程序前 ...

  4. 微信小程序详细开发教程

    全栈开发之路怎么能少得了小程序开发呢,下面我将详细介绍一个微信小程序从开发的部署的全过程: 一.在开始之前,先解释以下几个误区: 1.微信小程序个人号是可以开发并发布的: 2.微信小程序发布的体验版和 ...

  5. access突然需要登录_早知道早好,微信小程序登录开发需要注意的事项

    最近公司要做一个企业微信的小程序,方便企业内的成员来登录,以便一些公司内的业务,只限于公司内的成员来操作,因为有微信小程序的开发经验,所以先当作微信小程序来开发了! 首先来讲一下这个企业微信小程序与微 ...

  6. 微信小程序在开发中遇到的问题与解决方法

    微信小程序在开发中遇到的问题与解决方法 参考文章: (1)微信小程序在开发中遇到的问题与解决方法 (2)https://www.cnblogs.com/zjjDaily/p/8032142.html ...

  7. python操作微信小程序云端数据库_微信小程序云开发之数据库操作

    本文实例为大家分享了微信小程序云开发之数据库操作的具体代码,供大家参考,具体内容如下 新建集合 1.打开云开发控制台,数据库 2.添加集合users 添加代码 onAdd: function () { ...

  8. python开发微信小程序-Django微信小程序后台开发教程的实现

    1 申请小程序,创建hello world小程序 2 添加交互框和按钮 index. wxml cal {{ result }} index.wxss /**index.wxss**/ .input ...

  9. 《微信小程序:开发入门及案例详解》—— 3.4 小结

    本节书摘来自华章出版社<微信小程序:开发入门及案例详解>一 书中的第3章,第3.4节,作者李骏 边思,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 3.4 小 ...

最新文章

  1. POJ 1806 Manhattan 2025
  2. 分析死锁并处理_整理贴
  3. LESSON 9.4 集成算法的参数空间与网格优化
  4. 中国互联网的“去中心化”与“中心化”之战
  5. 案例:使用正则表达式的爬虫
  6. Sql为什么连接不上服务器上的数据库
  7. IBatisNet的配置(SqlMap.config)
  8. KeyMob--移动广告聚合平台界的黑马
  9. Win11正式版版号 Win11正式版最新版本号介绍
  10. double、float、long占几个字节?
  11. 炫酷神器,AE插件Bodymovin.zxp的安装与使用
  12. PTP(IEEE1588),TSN时间同步方法
  13. 2017年全国大学生电子竞赛电源A题
  14. kindle导入电子书方法
  15. 大学英语精读第三版(第三册)学习笔记(原文及全文翻译)——8B - Dreams — What Do They Mean?(梦意味着什么?)
  16. Android适配全面屏/刘海屏
  17. 佳明比华为的手表好在哪
  18. JavaSSM-Mybatis框架使用
  19. (原创)使用AsyncTask(带修改线程池方式)+自定义ImageLoader+LRU算法对图片三级缓存及其显示优化(只有在ListView滑动停止的时候才去网络请求获取图片数据)
  20. 【QT】实现贪吃蛇小游戏(附源码)

热门文章

  1. 27.4.1 开发ActiveX控件
  2. HDU 4507 吉哥系列故事——恨7不成妻 详解(变态数位DP)
  3. npm i安装包依赖时 gyp ERR! stack Error: Can‘t find Python executable “python“, you can set the PYTHON env
  4. (C语言)的全称是什么?
  5. MD5、SHA1和android apk签名杂谈
  6. 简易操作系统:使用Python 做的图形界面 C 做的内核
  7. 各种中文乱码的解决方法 (转)
  8. [原创] 树莓派使用多个联通4G上网卡
  9. 史上最全 | 单目相机测距测速方法大盘点!
  10. Win10 设置取消快速启动栏图标,C++版