一、玩具开机提示语

先下载github代码,下面的操作,都是基于这个版本来的!

https://github.com/987334176/Intelligent_toy/archive/v1.2.zip

注意:由于涉及到版权问题,此附件没有图片和音乐。请参考链接,手动采集一下!

请参考链接:

https://www.cnblogs.com/xiao987334176/p/9647993.html#autoid-3-4-0

判断设备id

每一个玩具,都有设备id。如果在设备表中,提示找小主人。否则提示 联系厂家。

如果在玩具表中,提示开机!

进入flask项目,将jquery.min.js下载到static目录,下载链接如下:

https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js

使用jquery的原因,是因为要发送ajax的POST请求。使用$.post{}

修改 templates—>index.html,增加开机按钮

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <audio src="" autoplay="autoplay" controls id="player"></audio>
  9. <br>
  10. <input type="text" id="device_id">
  11. <button onclick="start_toy()">玩具开机键</button>
  12. <br>
  13. <button onclick="start_reco()">开始废话</button>
  14. <br>
  15. <button onclick="stop_reco()">发送语音</button>
  16. </body>
  17. <script src="/static/recorder.js"></script>
  18. <script src="/static/jquery.min.js"></script>
  19. <script type="application/javascript">
  20. // 获取音频文件
  21. var get_file = "http://192.168.11.24:9527/get_audio/";
  22. // 创建 WebSocket 对象
  23. var ws = new WebSocket("ws://192.168.11.24:9528/toy/123456");
  24. var reco = null;
  25. // 创建AudioContext对象
  26. var audio_context = new AudioContext();
  27. //要获取音频和视频
  28. navigator.getUserMedia = (navigator.getUserMedia ||
  29. navigator.webkitGetUserMedia ||
  30. navigator.mozGetUserMedia ||
  31. navigator.msGetUserMedia);
  32. // 拿到媒体对象,允许音频对象
  33. navigator.getUserMedia({audio: true}, create_stream, function (err) {
  34. console.log(err)
  35. });
  36. //创建媒体流容器
  37. function create_stream(user_media) {
  38. var stream_input = audio_context.createMediaStreamSource(user_media);
  39. // 给Recoder 创建一个空间,麦克风说的话,都可以录入。是一个流
  40. reco = new Recorder(stream_input);
  41. }
  42. function start_reco() { //开始录音
  43. reco.record(); //往里面写流
  44. }
  45. function stop_reco() { //停止录音
  46. reco.stop(); //停止写入流
  47. get_audio(); //调用自定义方法
  48. reco.clear(); //清空容器
  49. }
  50. function get_audio() { // 获取音频
  51. reco.exportWAV(function (wav_file) {
  52. ws.send(wav_file); //使用websocket连接发送数据给后端
  53. })
  54. }
  55. ws.onmessage = function (data) { // 客户端接收服务端数据时触发
  56. // console.log(get_file + data.data);
  57. var content = JSON.parse(data.data);
  58. console.log(content);
  59. // 修改id为player的src属性,实现自动播放
  60. document.getElementById("player").src = get_file + content.data;
  61. console.log(content.from_user + "给你点了一首歌");
  62. };
  63. function start_toy() { // 玩具开机
  64. // 获取输入的设备id
  65. var device_id = document.getElementById("device_id").value;
  66. // 发送post请求
  67. $.post(
  68. // 这里的地址必须是127.0.0.1,否则会有跨域问题
  69. "http://127.0.0.1:9527/device_toy_id",
  70. // 发送设备id
  71. {device_id: device_id},
  72. function (data) {
  73. console.log(data);
  74. }, "json"
  75. // 规定预期的服务器响应的数据类型为json
  76. );
  77. }
  78. </script>
  79. </html>

修改 serv—>toys.py,增加视图函数device_toy_id

  1. from flask import Blueprint, request, jsonify
  2. from setting import MONGO_DB
  3. from setting import RET
  4. from bson import ObjectId
  5. toy = Blueprint("toy", __name__)
  6. @toy.route("/toy_list", methods=["POST"])
  7. def toy_list(): # 玩具列表
  8. user_id = request.form.get("user_id") # 用户id
  9. # 查看用户信息
  10. user_info = MONGO_DB.users.find_one({"_id": ObjectId(user_id)})
  11. bind_toy = user_info.get("bind_toy") # 获取绑定的玩具
  12. bind_toy_id = [] # 玩具列表
  13. for toy_id in bind_toy: # 获取玩具列表中的所有玩具id
  14. bind_toy_id.append(ObjectId(toy_id))
  15. # 一次性查询多个玩具
  16. toys_list = list(MONGO_DB.toys.find({"_id": {"$in": bind_toy_id}}))
  17. for index,item in enumerate(toys_list):
  18. # 将_id转换为字符串
  19. toys_list[index]["_id"] = str(item.get("_id"))
  20. RET["code"] = 0
  21. RET["msg"] = ""
  22. RET["data"] = toys_list
  23. return jsonify(RET)
  24. @toy.route("/device_toy_id", methods=["POST"])
  25. def device_toy_id():
  26. device_id = request.form.get("device_id") # 获取设备id
  27. # 判断设备id是否在设备表中
  28. if MONGO_DB.devices.find_one({"device_id": device_id}):
  29. # 查询设备id是否在玩具表中
  30. toy_info = MONGO_DB.toys.find_one({"device_id": device_id})
  31. if toy_info:
  32. return jsonify("开机")
  33. else:
  34. # 已授权的设备,但是没有绑定主人
  35. return jsonify("找小主人")
  36. else:
  37. # 不在设备表中,说明是未授权,或者是冒牌的!
  38. return jsonify("联系玩具厂")

重启 manager.py,访问首页

输入一段数字,点击玩具开机键,效果如下:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图1

打开 MongoDB客户端,复制一个不在玩具表(toys)中的设备id。效果如下:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图2

复制一个,在玩具表中的设备id,效果如下:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图3

提示语

后端逻辑判断,大致搞定了。下面来录制提示语,这里使用百度ai的接口。

在项目根目录,新建目录utils,在此目录下新建baidu_ai.py

此时,目录结构如下:

  1. ./
  2. ├── audio
  3. ├── audio_img
  4. ├── device_code
  5. ├── im_serv.py
  6. ├── manager.py
  7. ├── QRcode.py
  8. ├── serv
  9. ├── content.py
  10. ├── devices.py
  11. ├── get_file.py
  12. └── toys.py
  13. ├── setting.py
  14. ├── static
  15. ├── jquery.min.js
  16. └── recorder.js
  17. ├── templates
  18. └── index.html
  19. ├── utils
  20. └── baidu_ai.py
  21. └── xiaopapa.py

修改 setting.py,增加百度AI的秘钥。注意:后5位被我修改了,请改为自己的!

  1. import pymongo
  2. client = pymongo.MongoClient(host="127.0.0.1", port=27017)
  3. MONGO_DB = client["bananabase"]
  4. RET = {
  5. # 0: false 2: True
  6. "code": 0,
  7. "msg": "", # 提示信息
  8. "data": {}
  9. }
  10. XMLY_URL = "http://m.ximalaya.com/tracks/" # 喜马拉雅链接
  11. CREATE_QR_URL = "http://qr.liantu.com/api.php?text=" # 生成二维码API
  12. # 文件目录
  13. import os
  14. AUDIO_FILE = os.path.join(os.path.dirname(__file__), "audio") # 音频
  15. AUDIO_IMG_FILE = os.path.join(os.path.dirname(__file__), "audio_img") # 音频图片
  16. DEVICE_CODE_PATH = os.path.join(os.path.dirname(__file__), "device_code") # 二维码
  17. # 百度AI配置
  18. APP_ID = "11712345"
  19. API_KEY = "3v3igzCkVFUDwFByNEE12345"
  20. SECRET_KEY = "jRnwLE7kzC1aRi2FD10OQY3y9Og12345"
  21. SPEECH = {
  22. "spd": 4,
  23. 'vol': 5,
  24. "pit": 8,
  25. "per": 4
  26. }

修改 baidu_ai.py,录制开机语音

  1. from aip import AipSpeech
  2. import os
  3. BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 项目根目录
  4. import sys
  5. sys.path.append(BASE_DIR) # 加入到系统环境变量中
  6. import setting # 导入setting
  7. client = AipSpeech(setting.APP_ID,setting.API_KEY,setting.SECRET_KEY)
  8. res = client.synthesis("欢迎来到嘉禾智能亲子互动乐园","zh",1,setting.SPEECH)
  9. with open(os.path.join(setting.AUDIO_FILE,"success.mp3"),"wb") as f:
  10. f.write(res)

执行 baidu_ai.py,会在audio目录生成 success.mp3 文件。试听一下,感觉萌萌哒!

注意:语言文件的保存路径是audio。为什么呢?因为前端会调用get_audio接口。它是从audio目录读取的!

修改 baidu_ai.py,录制没有小主人语音

  1. from aip import AipSpeech
  2. import os
  3. BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 项目根目录
  4. import sys
  5. sys.path.append(BASE_DIR) # 加入到系统环境变量中
  6. import setting # 导入setting
  7. client = AipSpeech(setting.APP_ID,setting.API_KEY,setting.SECRET_KEY)
  8. res = client.synthesis("亲,我还没有小主人,快帮我找一个吧","zh",1,setting.SPEECH)
  9. with open(os.path.join(setting.AUDIO_FILE,"Nobind.mp3"),"wb") as f:
  10. f.write(res)

执行 baidu_ai.py,会在audio目录生成 Nobind.mp3 文件。试听一下吧

修改 baidu_ai.py,录制联系玩具厂商语音

  1. from aip import AipSpeech
  2. import os
  3. BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 项目根目录
  4. import sys
  5. sys.path.append(BASE_DIR) # 加入到系统环境变量中
  6. import setting # 导入setting
  7. client = AipSpeech(setting.APP_ID,setting.API_KEY,setting.SECRET_KEY)
  8. res = client.synthesis("硬件设备不符,请联系玩具厂商","zh",1,setting.SPEECH)
  9. with open(os.path.join(setting.AUDIO_FILE,"Nodevice.mp3"),"wb") as f:
  10. f.write(res)

执行 baidu_ai.py,会在audio目录生成 Nodevice.mp3 文件。试听一下吧

修改 serv—>toys.py,返回语音文件名

  1. from flask import Blueprint, request, jsonify
  2. from setting import MONGO_DB
  3. from setting import RET
  4. from bson import ObjectId
  5. toy = Blueprint("toy", __name__)
  6. @toy.route("/toy_list", methods=["POST"])
  7. def toy_list(): # 玩具列表
  8. user_id = request.form.get("user_id") # 用户id
  9. # 查看用户信息
  10. user_info = MONGO_DB.users.find_one({"_id": ObjectId(user_id)})
  11. bind_toy = user_info.get("bind_toy") # 获取绑定的玩具
  12. bind_toy_id = [] # 玩具列表
  13. for toy_id in bind_toy: # 获取玩具列表中的所有玩具id
  14. bind_toy_id.append(ObjectId(toy_id))
  15. # 一次性查询多个玩具
  16. toys_list = list(MONGO_DB.toys.find({"_id": {"$in": bind_toy_id}}))
  17. for index,item in enumerate(toys_list):
  18. # 将_id转换为字符串
  19. toys_list[index]["_id"] = str(item.get("_id"))
  20. RET["code"] = 0
  21. RET["msg"] = ""
  22. RET["data"] = toys_list
  23. return jsonify(RET)
  24. @toy.route("/device_toy_id", methods=["POST"])
  25. def device_toy_id(): # 验证设备id
  26. RET["code"] = 0
  27. RET["msg"] = "开机成功"
  28. RET["data"] = {}
  29. device_id = request.form.get("device_id") # 获取设备id
  30. # 判断设备id是否在设备表中
  31. if MONGO_DB.devices.find_one({"device_id": device_id}):
  32. # 查询设备id是否在玩具表中
  33. toy_info = MONGO_DB.toys.find_one({"device_id": device_id})
  34. if toy_info:
  35. # RET添加键值,获取玩具id
  36. RET["data"]["toy_id"] = str(toy_info.get("_id"))
  37. # 音频文件
  38. RET["data"]["audio"] = "success.mp3"
  39. return jsonify(RET)
  40. else:
  41. # 已授权的设备,但是没有绑定主人
  42. RET["msg"] = "找小主人"
  43. RET["data"]["audio"] = "Nobind.mp3"
  44. return jsonify(RET)
  45. else:
  46. # 不在设备表中,说明是未授权,或者是冒牌的!
  47. RET["msg"] = "联系玩具厂"
  48. RET["data"]["audio"] = "Nodevice.mp3"
  49. return jsonify(RET)

修改 index.html,POST请求成功后,修改audio标签的文件路径。将ws.onmessage代码移植到下面!

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <audio src="" autoplay="autoplay" controls id="player"></audio>
  9. <br>
  10. <input type="text" id="device_id">
  11. <button onclick="start_toy()">玩具开机键</button>
  12. <br>
  13. <button onclick="start_reco()">开始废话</button>
  14. <br>
  15. <button onclick="stop_reco()">发送语音</button>
  16. </body>
  17. <script src="/static/recorder.js"></script>
  18. <script src="/static/jquery.min.js"></script>
  19. <script type="application/javascript">
  20. // 获取音频文件
  21. var get_file = "http://192.168.11.24:9527/get_audio/";
  22. var ws = null; // WebSocket 对象
  23. var reco = null;
  24. // 创建AudioContext对象
  25. var audio_context = new AudioContext();
  26. //要获取音频和视频
  27. navigator.getUserMedia = (navigator.getUserMedia ||
  28. navigator.webkitGetUserMedia ||
  29. navigator.mozGetUserMedia ||
  30. navigator.msGetUserMedia);
  31. // 拿到媒体对象,允许音频对象
  32. navigator.getUserMedia({audio: true}, create_stream, function (err) {
  33. console.log(err)
  34. });
  35. //创建媒体流容器
  36. function create_stream(user_media) {
  37. var stream_input = audio_context.createMediaStreamSource(user_media);
  38. // 给Recoder 创建一个空间,麦克风说的话,都可以录入。是一个流
  39. reco = new Recorder(stream_input);
  40. }
  41. function start_reco() { //开始录音
  42. reco.record(); //往里面写流
  43. }
  44. function stop_reco() { //停止录音
  45. reco.stop(); //停止写入流
  46. get_audio(); //调用自定义方法
  47. reco.clear(); //清空容器
  48. }
  49. function get_audio() { // 获取音频
  50. reco.exportWAV(function (wav_file) {
  51. ws.send(wav_file); //使用websocket连接发送数据给后端
  52. })
  53. }
  54. function start_toy() { // 玩具开机
  55. // 获取输入的设备id
  56. var device_id = document.getElementById("device_id").value;
  57. // 发送post请求
  58. $.post(
  59. // 这里的地址必须是127.0.0.1,否则会有跨域问题
  60. "http://127.0.0.1:9527/device_toy_id",
  61. // 发送设备id
  62. {device_id: device_id},
  63. function (data) {
  64. console.log(data);
  65. toy_id = data.data.toy_id; // 玩具id
  66. // 修改audio标签的src属性
  67. document.getElementById("player").src = get_file + data.data.audio;
  68. if (toy_id) { // 判断玩具id存在时
  69. ws = new WebSocket("ws://192.168.11.24:9528/toy/" + toy_id);
  70. ws.onmessage = function (data) {
  71. // console.log(get_file + data.data);
  72. var content = JSON.parse(data.data); //反序列化数据
  73. document.getElementById("player").src = get_file + content.data;
  74. console.log(content.from_user + "给你点了一首歌");
  75. }
  76. }
  77. }, "json"
  78. // 规定预期的服务器响应的数据类型为json
  79. );
  80. }
  81. </script>
  82. </html>

重启 manager.py,访问首页,输入正确的设备id,效果如下:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图4

这个功能,还可以扩展。比如判断今天是否为小主人的生日。说:生日快乐!

或者阳历节日,也可以提醒!

二、为多个玩具发送点播

用户有一个玩具,或者多个玩具时。

如果点击这个按钮,需要用户选择,指定发送给哪一个玩具。

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图5

目前数据库中,只有一个用户。昨天已经添加了一个,具体操作,请从参考昨天的链接:

https://www.cnblogs.com/xiao987334176/p/9670063.html

现在再来添加一个!

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图6

绑定成功后,查看玩具表。有2条记录了

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图7

查看用户表,查看好友字段,会有2个!

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图8

建立连接

开2个页面,表示2个玩具。让2个玩具开机,需要2个合格的设备id。

打开玩具表,复制2个设备id

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图9

打开2个网页,左边输入 嘻嘻 的设备id

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图10

右边输入 小可爱 的设备id

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图11

在Console中,输入ws,回车。会出现一个websocket链接

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图12

注意:只要玩具开机了,就会建立 websocket连接!

查看Pycharm控制台输出:此时应该有2个websocket连接:

  1. {'5ba21c84e1253229c4acbd12': <geventwebsocket.websocket.WebSocket object at 0x000002DB8812BE18>, '5ba0f1f2e12532418089bf88': <geventwebsocket.websocket.WebSocket object at 0x000002DB88172590>}

那么APP页面,如何选择多个玩具呢?需要用到 弹出菜单

弹出菜单

mui框架内置了弹出菜单插件,弹出菜单显示内容不限,但必须包裹在一个含.mui-popover类的div中,如下即为一个弹出菜单内容:

  1. <div id="popover" class="mui-popover">
  2. <ul class="mui-table-view">
  3. <li class="mui-table-view-cell"><a href="#">Item1</a></li>
  4. <li class="mui-table-view-cell"><a href="#">Item2</a></li>
  5. <li class="mui-table-view-cell"><a href="#">Item3</a></li>
  6. <li class="mui-table-view-cell"><a href="#">Item4</a></li>
  7. <li class="mui-table-view-cell"><a href="#">Item5</a></li>
  8. </ul>
  9. </div>

要显示、隐藏如上菜单,mui推荐使用锚点方式,例如:

  1. <a href="#popover" id="openPopover" class="mui-btn mui-btn-primary mui-btn-block">打开弹出菜单</a>

点击如上定义的按钮,即可显示弹出菜单,再次点击弹出菜单之外的其他区域,均可关闭弹出菜单;这种使用方式最为简洁。

若希望通过js的方式控制弹出菜单,则通过如下一个方法即可:

  1. mui('.bottomPopover').popover(status[,anchor]);

status

  • ‘show’
    显示popover
  • ‘hide’
    隐藏popover
  • ‘toggle’
    自动识别处理显示隐藏状态
  1. mui('.bottomPopover').popover('toggle');//show hide toggle

本文参考链接:

http://dev.dcloud.net.cn/mui/ui/#ui_popover

修改 player.html,只修改html代码部分,js代码不用动!

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  7. <link href="css/mui.min.css" rel="stylesheet" />
  8. </head>
  9. <body>
  10. <header class="mui-bar mui-bar-nav">
  11. <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
  12. <h1 class="mui-title" id="title_text">正在播放</h1>
  13. </header>
  14. <div class="mui-content">
  15. <div class="mui-row" style="text-align: center;margin-top: 5px;">
  16. <img src="avatar/girl.jpg" style="width: 250px;height: 250px; border-radius: 50%;" id="avatar" />
  17. </div>
  18. <button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="play">播放</button>
  19. <button type="button" class="mui-btn mui-btn-yellow mui-btn-block" id="pause">暂停</button>
  20. <button type="button" class="mui-btn mui-btn-green mui-btn-block" id="resume">继续</button>
  21. <button type="button" class="mui-btn mui-btn-red mui-btn-block" id="stop">停止</button>
  22. <style type="text/css">
  23. #popover {
  24. height: 150px;
  25. width: 300px;
  26. }
  27. </style>
  28. <div id="popover" class="mui-popover">
  29. <div class="mui-scroll-wrapper">
  30. <div class="mui-scroll">
  31. <ul class="mui-table-view" id="toy_list" style="text-align: center;">
  32. <li class="mui-table-view-cell"><a href="#">Item1</a></li>
  33. <li class="mui-table-view-cell"><a href="#">Item2</a></li>
  34. </ul>
  35. </div>
  36. </div>
  37. </div>
  38. <!--<button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="send2toy">发送给玩具</button>-->
  39. <a href="#popover" id="openPopover" class="mui-btn mui-btn-primary mui-btn-block">发送给玩具</a>
  40. </div>
  41. </body>
  42. <script src="js/mui.js"></script>
  43. <script type="text/javascript">
  44. mui.init();
  45. var Sdata = null; //当前web页面
  46. var music_name = null; //歌曲名
  47. var player = null; //播放对象
  48. mui.plusReady(function() {
  49. Sdata = plus.webview.currentWebview(); // 当前web页面
  50. mui.toast(Sdata.content_id); // 弹窗显示由main.html传递的content_id
  51. //发送post请求
  52. mui.post(
  53. window.serv + "/content_one", {
  54. // 参数为content_id
  55. content_id: Sdata.content_id
  56. },
  57. function(data) {
  58. // 打印响应数据
  59. console.log(JSON.stringify(data));
  60. // 修改标题
  61. document.getElementById("title_text").innerText = "正在播放 : " + data.data.title;
  62. // 修改图片地址
  63. document.getElementById("avatar").src = window.serv_imge + data.data.avatar;
  64. // 调用自定义方法,播放音频
  65. // data是后端返回的数据,data.audio是音频文件名
  66. music_name = data.data.audio; // 歌曲名
  67. play_anything(music_name); //播放歌曲
  68. }
  69. );
  70. function play_anything(content) { //播放音频
  71. // 创建播放对象,拼接路径
  72. player = plus.audio.createPlayer(window.serv_audio + content);
  73. console.log(window.serv_audio + content); //打印路径
  74. // http://192.168.11.86:9527/get_audio/a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.mp3
  75. player.play(); // 播放音频
  76. }
  77. document.getElementById("play").addEventListener("tap", function() {
  78. player.play();
  79. });
  80. document.getElementById("pause").addEventListener("tap", function() {
  81. player.pause(); //暂停
  82. });
  83. document.getElementById("resume").addEventListener("tap", function() {
  84. player.resume(); //继续
  85. });
  86. document.getElementById("stop").addEventListener("tap", function() {
  87. player.stop(); // 停止,直接清空player中的对象
  88. });
  89. //发送给玩具
  90. document.getElementById("send2toy").addEventListener("tap", function() {
  91. var index = plus.webview.getWebviewById("HBuilder"); //index.html页面
  92. mui.fire(index, "send_music", { //发送音乐
  93. music_name: music_name, //歌曲名
  94. toy_id:"123456" // 发给玩具id为12345
  95. })
  96. });
  97. })
  98. </script>
  99. </html>

使用模拟器访问,点击 发送给玩具,效果如下:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图13

上面的item固定死了,需要展示为当前用户的 玩具名。需要访问后端接口,查询当前用户的所有玩具

修改 player.html

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  7. <link href="css/mui.min.css" rel="stylesheet" />
  8. </head>
  9. <body>
  10. <header class="mui-bar mui-bar-nav">
  11. <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
  12. <h1 class="mui-title" id="title_text">正在播放</h1>
  13. </header>
  14. <div class="mui-content">
  15. <div class="mui-row" style="text-align: center;margin-top: 5px;">
  16. <img src="avatar/girl.jpg" style="width: 250px;height: 250px; border-radius: 50%;" id="avatar" />
  17. </div>
  18. <button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="play">播放</button>
  19. <button type="button" class="mui-btn mui-btn-yellow mui-btn-block" id="pause">暂停</button>
  20. <button type="button" class="mui-btn mui-btn-green mui-btn-block" id="resume">继续</button>
  21. <button type="button" class="mui-btn mui-btn-red mui-btn-block" id="stop">停止</button>
  22. <style type="text/css">
  23. #popover {
  24. height: 150px;
  25. width: 300px;
  26. }
  27. </style>
  28. <div id="popover" class="mui-popover">
  29. <div class="mui-scroll-wrapper">
  30. <div class="mui-scroll">
  31. <ul class="mui-table-view" id="toy_list" style="text-align: center;">
  32. </ul>
  33. </div>
  34. </div>
  35. </div>
  36. <!--<button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="send2toy">发送给玩具</button>-->
  37. <a href="#popover" id="openPopover" class="mui-btn mui-btn-primary mui-btn-block">发送给玩具</a>
  38. </div>
  39. </body>
  40. <script src="js/mui.js"></script>
  41. <script type="text/javascript">
  42. mui.init();
  43. var Sdata = null; //当前web页面
  44. var music_name = null; //歌曲名
  45. var player = null; //播放对象
  46. mui.plusReady(function() {
  47. Sdata = plus.webview.currentWebview(); // 当前web页面
  48. mui.toast(Sdata.content_id); // 弹窗显示由main.html传递的content_id
  49. //发送post请求
  50. mui.post(
  51. window.serv + "/content_one", {
  52. // 参数为content_id
  53. content_id: Sdata.content_id
  54. },
  55. function(data) {
  56. // 打印响应数据
  57. console.log(JSON.stringify(data));
  58. // 修改标题
  59. document.getElementById("title_text").innerText = "正在播放 : " + data.data.title;
  60. // 修改图片地址
  61. document.getElementById("avatar").src = window.serv_imge + data.data.avatar;
  62. // 调用自定义方法,播放音频
  63. // data是后端返回的数据,data.audio是音频文件名
  64. music_name = data.data.audio; // 歌曲名
  65. play_anything(music_name); //播放歌曲
  66. }
  67. );
  68. mui.post( //查询当前用户的玩具列表
  69. window.serv + "/toy_list", {
  70. user_id: plus.storage.getItem("user")
  71. },
  72. function(data) {
  73. console.log(JSON.stringify(data));
  74. for(var i = 0; i < data.data.length; i++) {
  75. // 执行定义方法create_toy,增加li标签
  76. create_toy(data.data[i]);
  77. }
  78. }
  79. );
  80. function play_anything(content) { //播放音频
  81. // 创建播放对象,拼接路径
  82. player = plus.audio.createPlayer(window.serv_audio + content);
  83. console.log(window.serv_audio + content); //打印路径
  84. // http://192.168.11.86:9527/get_audio/a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.mp3
  85. player.play(); // 播放音频
  86. }
  87. document.getElementById("play").addEventListener("tap", function() {
  88. player.play();
  89. });
  90. document.getElementById("pause").addEventListener("tap", function() {
  91. player.pause(); //暂停
  92. });
  93. document.getElementById("resume").addEventListener("tap", function() {
  94. player.resume(); //继续
  95. });
  96. document.getElementById("stop").addEventListener("tap", function() {
  97. player.stop(); // 停止,直接清空player中的对象
  98. });
  99. //发送给玩具
  100. document.getElementById("send2toy").addEventListener("tap", function() {
  101. var index = plus.webview.getWebviewById("HBuilder"); //index.html页面
  102. mui.fire(index, "send_music", { //发送音乐
  103. music_name: music_name, //歌曲名
  104. toy_id: "123456" // 发给玩具id为12345
  105. })
  106. });
  107. function create_toy(toy_info) { // 创建玩具
  108. // 构造下面的标签
  109. // <li class="mui-table-view-cell">
  110. // <a href="#">Item1</a>
  111. // </li>
  112. var litag = document.createElement("li");
  113. litag.className = "mui-table-view-cell"
  114. var atag = document.createElement("a");
  115. atag.id = toy_info._id;
  116. atag.innerText = toy_info.baby_name;
  117. litag.appendChild(atag);
  118. document.getElementById("toy_list").appendChild(litag);
  119. }
  120. })
  121. </script>
  122. </html>

重新访问一次,效果如下:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图14

点击 小豆芽 是没有效果的!需要增加点击事件。由于它是a标签,使用onclick

需要使用websocket发送数据。由于index.html建立了websocket连接,使用fire事件将数据发给index.html。

由index.html来发送数据!

修改player.html

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  7. <link href="css/mui.min.css" rel="stylesheet" />
  8. </head>
  9. <body>
  10. <header class="mui-bar mui-bar-nav">
  11. <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
  12. <h1 class="mui-title" id="title_text">正在播放</h1>
  13. </header>
  14. <div class="mui-content">
  15. <div class="mui-row" style="text-align: center;margin-top: 5px;">
  16. <img src="avatar/girl.jpg" style="width: 250px;height: 250px; border-radius: 50%;" id="avatar" />
  17. </div>
  18. <button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="play">播放</button>
  19. <button type="button" class="mui-btn mui-btn-yellow mui-btn-block" id="pause">暂停</button>
  20. <button type="button" class="mui-btn mui-btn-green mui-btn-block" id="resume">继续</button>
  21. <button type="button" class="mui-btn mui-btn-red mui-btn-block" id="stop">停止</button>
  22. <!--弹窗样式-->
  23. <style type="text/css">
  24. #popover {
  25. height: 150px;
  26. width: 300px;
  27. }
  28. </style>
  29. <div id="popover" class="mui-popover">
  30. <div class="mui-scroll-wrapper">
  31. <div class="mui-scroll">
  32. <ul class="mui-table-view" id="toy_list" style="text-align: center;">
  33. </ul>
  34. </div>
  35. </div>
  36. </div>
  37. <!--<button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="send2toy">发送给玩具</button>-->
  38. <a href="#popover" id="openPopover" class="mui-btn mui-btn-primary mui-btn-block">发送给玩具</a>
  39. </div>
  40. </body>
  41. <script src="js/mui.js"></script>
  42. <script type="text/javascript">
  43. mui.init();
  44. var Sdata = null; //当前web页面
  45. var music_name = null; //歌曲名
  46. var player = null; //播放对象
  47. mui.plusReady(function() {
  48. Sdata = plus.webview.currentWebview(); // 当前web页面
  49. mui.toast(Sdata.content_id); // 弹窗显示由main.html传递的content_id
  50. //发送post请求
  51. mui.post(
  52. window.serv + "/content_one", {
  53. // 参数为content_id
  54. content_id: Sdata.content_id
  55. },
  56. function(data) {
  57. // 打印响应数据
  58. console.log(JSON.stringify(data));
  59. // 修改标题
  60. document.getElementById("title_text").innerText = "正在播放 : " + data.data.title;
  61. // 修改图片地址
  62. document.getElementById("avatar").src = window.serv_imge + data.data.avatar;
  63. // 调用自定义方法,播放音频
  64. // data是后端返回的数据,data.audio是音频文件名
  65. music_name = data.data.audio; // 歌曲名
  66. play_anything(music_name); //播放歌曲
  67. }
  68. );
  69. mui.post( //查询当前用户的玩具列表
  70. window.serv + "/toy_list", {
  71. user_id: plus.storage.getItem("user")
  72. },
  73. function(data) {
  74. console.log(JSON.stringify(data));
  75. for(var i = 0; i < data.data.length; i++) {
  76. // 执行定义方法create_toy,增加li标签
  77. create_toy(data.data[i]);
  78. }
  79. }
  80. );
  81. function play_anything(content) { //播放音频
  82. // 创建播放对象,拼接路径
  83. player = plus.audio.createPlayer(window.serv_audio + content);
  84. console.log(window.serv_audio + content); //打印路径
  85. // http://192.168.11.86:9527/get_audio/a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.mp3
  86. player.play(); // 播放音频
  87. }
  88. document.getElementById("play").addEventListener("tap", function() {
  89. player.play();
  90. });
  91. document.getElementById("pause").addEventListener("tap", function() {
  92. player.pause(); //暂停
  93. });
  94. document.getElementById("resume").addEventListener("tap", function() {
  95. player.resume(); //继续
  96. });
  97. document.getElementById("stop").addEventListener("tap", function() {
  98. player.stop(); // 停止,直接清空player中的对象
  99. });
  100. //发送给玩具
  101. document.getElementById("send2toy").addEventListener("tap", function() {
  102. var index = plus.webview.getWebviewById("HBuilder"); //index.html页面
  103. mui.fire(index, "send_music", { //发送音乐
  104. music_name: music_name, //歌曲名
  105. toy_id: "123456" // 发给玩具id为12345
  106. })
  107. });
  108. function create_toy(toy_info) { // 创建玩具
  109. // 构造下面的标签
  110. // <li class="mui-table-view-cell">
  111. // <a href="#">Item1</a>
  112. // </li>
  113. var litag = document.createElement("li");
  114. litag.className = "mui-table-view-cell"
  115. var atag = document.createElement("a");
  116. atag.id = toy_info._id; // 玩具id
  117. atag.innerText = toy_info.baby_name;
  118. atag.onclick = function() { // 点击事件
  119. // 获取index页面
  120. var index = plus.webview.getWebviewById("HBuilder")
  121. mui.fire(index, "send_music", { // 使用fire发送数据给index
  122. music_name: music_name, //歌曲名
  123. //玩具id,注意:这里必须是this.id。它表示你点击的是哪个玩具
  124. toy_id: this.id
  125. })
  126. }
  127. litag.appendChild(atag);
  128. document.getElementById("toy_list").appendChild(litag);
  129. }
  130. })
  131. </script>
  132. </html>

index.html页面,就不需要修改了。因为它已经监听了 send_music 事件。

点击 小甜甜

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图15

此时,页面的第二个窗口,会自动播放歌曲。

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图16

那么点歌功能,就完成了!

三、聊天页面

之前我们写的phone.html,好像没咋用过。将phone.html重命名为 message.html

好友列表,来源于 用户表(users) 的friend_list字段!

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图17

修改index.html,将底部选项卡 中的 电话 改为 消息

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  6. <title></title>
  7. <script src="js/mui.js"></script>
  8. <link href="css/mui.min.css" rel="stylesheet" />
  9. </head>
  10. <body>
  11. <!--底部选项卡-->
  12. <nav class="mui-bar mui-bar-tab">
  13. <a class="mui-tab-item mui-active" id="index">
  14. <span class="mui-icon mui-icon-home"></span>
  15. <span class="mui-tab-label">首页</span>
  16. </a>
  17. <a class="mui-tab-item" id="message">
  18. <span class="mui-icon mui-icon-chatbubble"></span>
  19. <span class="mui-tab-label">消息</span>
  20. </a>
  21. <a class="mui-tab-item">
  22. <span class="mui-icon mui-icon-email"></span>
  23. <span class="mui-tab-label">邮件</span>
  24. </a>
  25. <a class="mui-tab-item" id="login">
  26. <span class="mui-icon mui-icon-gear"></span>
  27. <span class="mui-tab-label">设置</span>
  28. </a>
  29. </nav>
  30. </body>
  31. <script type="text/javascript" charset="utf-8">
  32. var ws = null; // websocket对象
  33. mui.init({
  34. subpages: [{
  35. url: "main.html",
  36. id: "main.html",
  37. styles: window.styles
  38. }]
  39. });
  40. mui.plusReady(function() {
  41. // console.log(JSON.stringify(plus.webview.currentWebview()))
  42. if(plus.storage.getItem("user")) { // 判断是否登录
  43. console.log('已结登录了!');
  44. //连接websocket连接
  45. ws = new WebSocket("ws://"+window.ws_serv+"/app/"+plus.storage.getItem("user"))
  46. // 客户端接收服务端数据时触发
  47. ws.onmessage = function() {};
  48. }
  49. });
  50. // 消息
  51. document.getElementById("message").addEventListener("tap", function() {
  52. mui.openWindow({
  53. url: "message.html",
  54. id: "message.html",
  55. styles: window.styles,
  56. extras: {
  57. // 传输用户id,给message.html
  58. user_id: plus.storage.getItem("user")
  59. }
  60. })
  61. });
  62. document.getElementById("index").addEventListener("tap", function() {
  63. mui.openWindow({
  64. url: "main.html",
  65. id: "main.html",
  66. styles: window.styles
  67. })
  68. })
  69. document.getElementById("login").addEventListener("tap", function() {
  70. mui.openWindow({
  71. url: "login.html",
  72. id: "login.html",
  73. styles: window.styles
  74. })
  75. })
  76. document.addEventListener("login",function(data){
  77. // fire事件接收消息,使用data.detail
  78. // index是为做显示区分
  79. mui.toast("index"+data.detail.msg)
  80. });
  81. document.addEventListener("send_music", function(data) { //监听send_music事件
  82. var music_name = data.detail.music_name; //获取player.html使用fire发送的music_name值
  83. var toy_id = data.detail.toy_id; //获取发送的玩具id
  84. send_str = { //构造数据
  85. music_name:music_name,
  86. toy_id:toy_id
  87. }
  88. // 发送数据给后端,注意要json序列化
  89. ws.send(JSON.stringify(send_str));
  90. });
  91. </script>
  92. </html>

底部选项卡,效果如下:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图18

修改 message.html,发送post,请求好友列表

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>Document</title>
  6. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  7. <link rel="stylesheet" type="text/css" href="css/mui.css" />
  8. </head>
  9. <body>
  10. <header class="mui-bar mui-bar-nav">
  11. <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
  12. <h1 class="mui-title">我的好友</h1>
  13. </header>
  14. <div class="mui-content">
  15. <ul class="mui-table-view" id="friend_list">
  16. </ul>
  17. </div>
  18. </body>
  19. <script src="js/mui.js" type="text/javascript" charset="utf-8"></script>
  20. <script type="text/javascript">
  21. mui.init()
  22. var Sdata = null;
  23. mui.back = function(){};
  24. // 加载HTML5Puls
  25. mui.plusReady(function() {
  26. Sdata = plus.webview.currentWebview();
  27. // post请求
  28. mui.post(
  29. // 好友列表
  30. window.serv + "/friend_list",
  31. {user_id:Sdata.user_id},
  32. function(data){
  33. console.log(JSON.stringify(data));
  34. }
  35. )
  36. });
  37. </script>
  38. </html>

进入 flask项目,进入serv目录,新建文件friend.py

  1. from flask import Blueprint, request, jsonify
  2. from setting import MONGO_DB
  3. from setting import RET
  4. from bson import ObjectId
  5. fri = Blueprint("fri", __name__)
  6. @fri.route("/friend_list", methods=["POST"])
  7. def friend_list(): # 好友列表
  8. user_id = request.form.get("user_id")
  9. # 查询用户id信息
  10. res = MONGO_DB.users.find_one({"_id": ObjectId(user_id)})
  11. friend_list = res.get("friend_list") # 获取好友列表
  12. RET["code"] = 0
  13. RET["msg"] = ""
  14. RET["data"] = friend_list
  15. return jsonify(RET)

修改 manager.py,注册蓝图

  1. from flask import Flask, request,jsonify,render_template
  2. from setting import MONGO_DB
  3. from setting import RET
  4. from bson import ObjectId
  5. from serv import get_file
  6. from serv import content
  7. from serv import devices
  8. from serv import toys
  9. from serv import friend
  10. app = Flask(__name__)
  11. app.register_blueprint(get_file.getfile)
  12. app.register_blueprint(content.cont)
  13. app.register_blueprint(devices.devs)
  14. app.register_blueprint(toys.toy)
  15. app.register_blueprint(friend.fri)
  16. @app.route('/')
  17. def hello_world():
  18. return render_template("index.html")
  19. @app.route('/login',methods=["POST"])
  20. def login():
  21. """
  22. 登陆验证
  23. :return: settings -> RET
  24. """
  25. try:
  26. RET["code"] = 1
  27. RET["msg"] = "用户名或密码错误"
  28. RET["data"] = {}
  29. username = request.form.get("username")
  30. password = request.form.get("password")
  31. user = MONGO_DB.users.find_one({"username": username, "password": password})
  32. if user:
  33. # 由于user中的_id是ObjectId对象,需要转化为字符串
  34. user["_id"] = str(user.get("_id"))
  35. RET["code"] = 0
  36. RET["msg"] = "欢迎登陆"
  37. RET["data"] = {"user_id": user.get("_id")}
  38. except Exception as e:
  39. RET["code"] = 1
  40. RET["msg"] = "登陆失败"
  41. return jsonify(RET)
  42. @app.route('/reg',methods=["POST"])
  43. def reg():
  44. """
  45. 注册
  46. :return: {"code":0,"msg":"","data":""}
  47. """
  48. try:
  49. username = request.form.get("username")
  50. password = request.form.get("password")
  51. age = request.form.get("age")
  52. nickname = request.form.get("nickname")
  53. gender = request.form.get("gender")
  54. phone = request.form.get("phone")
  55. user_info = {
  56. "username": username,
  57. "password": password,
  58. "age": age,
  59. "nickname": nickname,
  60. # 判断gender==2,成立时为girl.jpg,否则为boy.jpg
  61. "avatar": "girl.jpg" if gender == 2 else "boy.jpg",
  62. "gender": gender,
  63. "phone": phone
  64. }
  65. res = MONGO_DB.users.insert_one(user_info)
  66. user_id = str(res.inserted_id)
  67. RET["code"] = 0
  68. RET["msg"] = "注册成功"
  69. RET["data"] = user_id
  70. except Exception as e:
  71. RET["code"] = 1
  72. RET["msg"] = "注册失败"
  73. return jsonify(RET)
  74. @app.route('/user_info', methods=["POST"])
  75. def user_info():
  76. user_id = request.form.get("user_id")
  77. # "password": 0 表示忽略密码字段
  78. res = MONGO_DB.users.find_one({"_id": ObjectId(user_id)}, {"password": 0})
  79. if res:
  80. res["_id"] = str(res.get("_id"))
  81. RET["code"] = 0
  82. RET["msg"] = ""
  83. RET["data"] = res
  84. return jsonify(res)
  85. if __name__ == '__main__':
  86. app.run("0.0.0.0", 9527, debug=True)

重启 manager.py

使用模拟器访问 消息 ,效果如下:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图19

这个页面还是空的。查看HBuilder控制台输出:

  1. {"code":0,"data":[{"friend_avatar":"girl.jpg","friend_chat":"5ba0f1f2e12532418089bf87","friend_id":"5ba0f1f2e12532418089bf88","friend_name":"小可爱","friend_remark":"小甜甜"},{"friend_avatar":"girl.jpg","friend_chat":"5ba21c84e1253229c4acbd11","friend_id":"5ba21c84e1253229c4acbd12","friend_name":"嘻嘻","friend_remark":"小豆芽"}],"msg":""} at message.html:37

已经得到了数据,下面开始渲染页面!

修改 message.html,渲染页面

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>Document</title>
  6. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  7. <link rel="stylesheet" type="text/css" href="css/mui.css" />
  8. </head>
  9. <body>
  10. <header class="mui-bar mui-bar-nav">
  11. <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
  12. <h1 class="mui-title">我的好友</h1>
  13. </header>
  14. <div class="mui-content">
  15. <ul class="mui-table-view" id="friend_list">
  16. </ul>
  17. </div>
  18. </body>
  19. <script src="js/mui.js" type="text/javascript" charset="utf-8"></script>
  20. <script type="text/javascript">
  21. mui.init()
  22. var Sdata = null;
  23. mui.back = function(){};
  24. // 加载HTML5Puls
  25. mui.plusReady(function() {
  26. Sdata = plus.webview.currentWebview();
  27. // post请求
  28. mui.post(
  29. // 好友列表
  30. window.serv + "/friend_list",
  31. {user_id:Sdata.user_id},
  32. function(data){
  33. console.log(JSON.stringify(data));
  34. // 循环好友列表
  35. for (var i = 0; i < data.data.length; i++) {
  36. // 执行自定义方法,渲染页面
  37. create_content(data.data[i]);
  38. }
  39. }
  40. )
  41. });
  42. function create_content(content){
  43. // <li class="mui-table-view-cell mui-media">
  44. // <a href="javascript:;">
  45. // <img class="mui-media-object mui-pull-left" src="../images/shuijiao.jpg">
  46. // <div class="mui-media-body">
  47. // 幸福
  48. // <p class='mui-ellipsis'>能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p>
  49. // </div>
  50. // </a>
  51. // </li>
  52. var litag = document.createElement("li");
  53. litag.className = "mui-table-view-cell mui-media";
  54. var atag = document.createElement("a");
  55. atag.id = content.friend_id;
  56. // 点击事件
  57. atag.onclick = function(){
  58. console.log(this.id);
  59. // open_chat(this.id);
  60. }
  61. var imgtag = document.createElement("img");
  62. imgtag.className = "mui-media-object mui-pull-left";
  63. imgtag.src = "avatar/" + content.friend_avatar;
  64. var divtag = document.createElement("div");
  65. divtag.className = "mui-media-body";
  66. divtag.innerText = content.friend_remark;
  67. var ptag = document.createElement("p");
  68. ptag.className = "mui-ellipsis";
  69. ptag.innerText = content.friend_name;
  70. litag.appendChild(atag);
  71. atag.appendChild(imgtag);
  72. atag.appendChild(divtag);
  73. divtag.appendChild(ptag);
  74. document.getElementById("friend_list").appendChild(litag);
  75. }
  76. </script>
  77. </html>

重新访问,效果如下:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图20

点击小甜甜,查看HBuilder控制台输出:

  1. 5ba0f1f2e12532418089bf88 at message.html:63

它会打印出,好友id。那么下面就可以开始聊天了!

新建css文件

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图21

chat.css

  1. div.speech {
  2. float: left;
  3. margin: 0, 0;
  4. padding: 6px;
  5. table-layout: fixed;
  6. word-break: break-all;
  7. position: relative;
  8. background: -webkit-gradient( linear, 50% 0%, 50% 100%, from(#ffffff), color-stop(0.1, #ececec), color-stop(0.5, #dbdbdb), color-stop(0.9, #dcdcdc), to(#8c8c8c));
  9. border: 1px solid #989898;
  10. border-radius: 8px;
  11. }
  12. div.speech:before {
  13. content: '';
  14. position: absolute;
  15. width: 0;
  16. height: 0;
  17. left: 15px;
  18. top: -20px;
  19. border: 10px solid;
  20. border-color: transparent transparent #989898 transparent;
  21. }
  22. div.speech:after {
  23. content: '';
  24. position: absolute;
  25. width: 0;
  26. height: 0;
  27. left: 17px;
  28. top: -16px;
  29. border: 8px solid;
  30. border-color: transparent transparent #ffffff transparent;
  31. }
  32. div.speech.right {
  33. display: inline-block;
  34. box-shadow: -2px 2px 5px #CCC;
  35. margin-right: 10px;
  36. max-width: 75%;
  37. float: right;
  38. background: -webkit-gradient( linear, 50% 0%, 50% 100%, from(#e4ffa7), color-stop(0.1, #bced50), color-stop(0.4, #aed943), color-stop(0.8, #a7d143), to(#99BF40));
  39. }
  40. div.speech.right:before {
  41. content: '';
  42. position: absolute;
  43. width: 0;
  44. height: 0;
  45. top: 9px;
  46. bottom: auto;
  47. left: auto;
  48. right: -10px;
  49. border-width: 9px 0 9px 10px;
  50. border-color: transparent #989898;
  51. }
  52. div.speech.right:after {
  53. content: '';
  54. position: absolute;
  55. width: 0;
  56. height: 0;
  57. top: 10px;
  58. bottom: auto;
  59. left: auto;
  60. right: -8px;
  61. border-width: 8px 0 8px 9px;
  62. border-color: transparent #bced50;
  63. }
  64. div.left {
  65. display: inline-block;
  66. box-shadow: 2px 2px 2px #CCCCCC;
  67. margin-left: 10px;
  68. max-width: 75%;
  69. position: relative;
  70. background: -webkit-gradient( linear, 50% 0%, 50% 100%, from(#ffffff), color-stop(0.1, #eae8e8), color-stop(0.4, #E3E3E3), color-stop(0.8, #DFDFDF), to(#D9D9D9));
  71. }
  72. div.left:before {
  73. content: '';
  74. position: absolute;
  75. width: 0;
  76. height: 0;
  77. top: 9px;
  78. bottom: auto;
  79. left: -10px;
  80. border-width: 9px 10px 9px 0;
  81. border-color: transparent #989898;
  82. }
  83. div.left:after {
  84. content: '';
  85. position: absolute;
  86. width: 0;
  87. height: 0;
  88. top: 10px;
  89. bottom: auto;
  90. left: -8px;
  91. border-width: 8px 9px 8px 0;
  92. border-color: transparent #eae8e8;
  93. }
  94. .leftimg {
  95. float: left;
  96. margin-top: 10px;
  97. }
  98. .rightimg {
  99. float: right;
  100. margin-top: 10px;
  101. }
  102. .leftd {
  103. clear: both;
  104. float: left;
  105. margin-left: 10px;
  106. margin-top: 15px;
  107. }
  108. .rightd {
  109. clear: both;
  110. float: right;
  111. margin-top: 15px;
  112. margin-right: 10px;
  113. }
  114. .leftd_h {
  115. width: 45px;
  116. height: 35px;
  117. border-radius: 100%;
  118. display: block;
  119. float: left;
  120. overflow: hidden;
  121. }
  122. .leftd_h img {
  123. display: block;
  124. width: 100%;
  125. height: auto;
  126. }
  127. .rightd_h {
  128. width: 45px;
  129. height: 35px;
  130. border-radius: 100%;
  131. display: block;
  132. float: right;
  133. overflow: hidden;
  134. }
  135. .rightd_h img {
  136. display: block;
  137. width: 100%;
  138. height: auto;
  139. }
  140. .chat-other {
  141. background-color: red;
  142. margin-top: 10px;
  143. margin-left: 20px;
  144. }
  145. .chat-other-span {
  146. background-color: aquamarine;
  147. /*border-radius: 10%;*/
  148. height: 18px;
  149. }
  150. .chat-mine {
  151. margin-top: 10px;
  152. margin-right: 20px;
  153. text-align: right;
  154. }
  155. .chat-avatar {
  156. border-radius: 100%;
  157. width: 25px;
  158. height: 25px;
  159. }

新建文件chat.html

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图22

手势事件

在开发移动端的应用时,会用到很多的手势操作,比如滑动、长按等,为了方便开放者快速集成这些手势,mui内置了常用的手势事件,目前支持的手势事件见如下列表:

参考链接:

http://dev.dcloud.net.cn/mui/event/#gesture

这里, 只用到了 长按里面的 hold和release

修改 chat.html

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>Document</title>
  6. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  7. <link rel="stylesheet" type="text/css" href="css/mui.css" />
  8. <link rel="stylesheet" type="text/css" href="css/chat.css" />
  9. </head>
  10. <body>
  11. <header class="mui-bar mui-bar-nav">
  12. <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
  13. <h1 class="mui-title">与xxx进行对话</h1>
  14. </header>
  15. <div class="mui-content" id="chat_list">
  16. <div class="leftd">
  17. <img src="avatar/girl.jpg" class="leftd_h" />
  18. <div class="speech left">点击播放</div>
  19. </div>
  20. <div class="rightd">
  21. <img src="avatar/girl.jpg" class="rightd_h" />
  22. <div class="speech right">点击播放</div>
  23. </div>
  24. </div>
  25. <nav class="mui-bar mui-bar-tab">
  26. <a class="mui-tab-item mui-active" id="talk">
  27. <span class="mui-icon mui-icon-speech"></span>
  28. <span class="mui-tab-label">按住说话</span>
  29. </a>
  30. </nav>
  31. </body>
  32. <script src="js/mui.js" type="text/javascript" charset="utf-8"></script>
  33. <script type="text/javascript">
  34. mui.init({
  35. //手势事件配置
  36. gestureConfig: {
  37. tap: true, //默认为true
  38. doubletap: true, //默认为false
  39. longtap: false, //默认为false
  40. swipe: true, //默认为true
  41. drag: true, //默认为true
  42. hold: true, //默认为false,不监听
  43. release: true //默认为false,不监听
  44. }
  45. });
  46. var index = null;
  47. var Sdata=null;
  48. mui.plusReady(function() {
  49. index = plus.webview.getWebviewById("HBuilder");
  50. Sdata = plus.webview.currentWebview();
  51. })
  52. var rec = null;
  53. document.getElementById("talk").addEventListener("hold", function() {
  54. mui.toast("按住了");
  55. })
  56. document.getElementById("talk").addEventListener("release", function() {
  57. mui.toast("松开了");
  58. })
  59. function create_chat(who, p) {
  60. // 构建div,一次说话,就是一个div
  61. // <div class="leftd">
  62. // <img src="avatar/girl.jpg" class="leftd_h" />
  63. // <div class="speech left">点击播放</div>
  64. // </div>
  65. // 默认显示在左边
  66. var div1class = "leftd";
  67. var imgclass = "leftd_h";
  68. var div2class = "speech left";
  69. // 左右列表排序效果只是class不一样而已!
  70. // 这里做一个判断,当为self,class改为right
  71. if(who == "self") {
  72. div1class = "rightd";
  73. imgclass = "rightd_h";
  74. div2class = "speech right";
  75. }
  76. var div1tag = document.createElement("div");
  77. div1tag.className = div1class;
  78. var imgtag = document.createElement("img");
  79. imgtag.className = imgclass;
  80. imgtag.src = "avatar/girl.jpg"
  81. var div2tag = document.createElement("div");
  82. div2tag.className = div2class;
  83. div2tag.innerText = "点击播放";
  84. div1tag.appendChild(imgtag);
  85. div1tag.appendChild(div2tag);
  86. document.getElementById("chat_list").appendChild(div1tag);
  87. }
  88. // 生成几个div。一个div就是一次说话
  89. create_chat("self"); // self表示我
  90. create_chat("w"); // 这个可以随便写,表示其他
  91. create_chat("self");
  92. create_chat("2");
  93. create_chat("2");
  94. </script>
  95. </html>

修改 message.html,增加点击事件。点击时,跳转到chat.html页面

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>Document</title>
  6. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  7. <link rel="stylesheet" type="text/css" href="css/mui.css" />
  8. </head>
  9. <body>
  10. <header class="mui-bar mui-bar-nav">
  11. <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
  12. <h1 class="mui-title">我的好友</h1>
  13. </header>
  14. <div class="mui-content">
  15. <ul class="mui-table-view" id="friend_list">
  16. </ul>
  17. </div>
  18. </body>
  19. <script src="js/mui.js" type="text/javascript" charset="utf-8"></script>
  20. <script type="text/javascript">
  21. mui.init()
  22. var Sdata = null;
  23. mui.back = function(){};
  24. // 加载HTML5Puls
  25. mui.plusReady(function() {
  26. Sdata = plus.webview.currentWebview();
  27. // post请求
  28. mui.post(
  29. // 好友列表
  30. window.serv + "/friend_list",
  31. {user_id:Sdata.user_id},
  32. function(data){
  33. console.log(JSON.stringify(data));
  34. // 循环好友列表
  35. for (var i = 0; i < data.data.length; i++) {
  36. // 执行自定义方法,渲染页面
  37. create_content(data.data[i]);
  38. }
  39. }
  40. )
  41. });
  42. function create_content(content){
  43. // <li class="mui-table-view-cell mui-media">
  44. // <a href="javascript:;">
  45. // <img class="mui-media-object mui-pull-left" src="../images/shuijiao.jpg">
  46. // <div class="mui-media-body">
  47. // 幸福
  48. // <p class='mui-ellipsis'>能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p>
  49. // </div>
  50. // </a>
  51. // </li>
  52. var litag = document.createElement("li");
  53. litag.className = "mui-table-view-cell mui-media";
  54. var atag = document.createElement("a");
  55. atag.id = content.friend_id;
  56. // 点击事件
  57. atag.onclick = function(){
  58. console.log(this.id);
  59. open_chat(this.id); //执行自定义方法open_chat
  60. }
  61. var imgtag = document.createElement("img");
  62. imgtag.className = "mui-media-object mui-pull-left";
  63. imgtag.src = "avatar/" + content.friend_avatar;
  64. var divtag = document.createElement("div");
  65. divtag.className = "mui-media-body";
  66. divtag.innerText = content.friend_remark;
  67. var ptag = document.createElement("p");
  68. ptag.className = "mui-ellipsis";
  69. ptag.innerText = content.friend_name;
  70. litag.appendChild(atag);
  71. atag.appendChild(imgtag);
  72. atag.appendChild(divtag);
  73. divtag.appendChild(ptag);
  74. document.getElementById("friend_list").appendChild(litag);
  75. }
  76. function open_chat(friend_id){ // 打开chat.html
  77. mui.openWindow({
  78. url:"chat.html",
  79. id:"chat.html",
  80. extras:{
  81. // 传参给chat.html
  82. friend_id:friend_id
  83. }
  84. })
  85. }
  86. </script>
  87. </html>

使用模拟器访问,效果如下:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图23

四、app录音

由于时间关系,详细步骤略…

五、app与服务器端文件传输

由于时间关系,详细步骤略…

六、简单的对话

由于时间关系,详细步骤略…

今日总结:

  1. 1.玩具开机提示语
  2. 刚刚开机的时候:
  3. 1.授权问题(MD5授权码)提示语 请联系玩具厂商
  4. 2.绑定问题 提示语 快给我找一个小主人
  5. 3.成功 提示语:欢迎使用
  6. 2.为多个玩具发送点播:
  7. mpop 弹出菜单
  8. 3.聊天界面:
  9. <div class="leftd">
  10. <img src="avatar/girl.jpg" class="leftd_h" />
  11. <div class="speech left">点击播放</div>
  12. </div>
  13. <div class="rightd">
  14. <img src="avatar/girl.jpg" class="rightd_h" />
  15. <div class="speech right">点击播放</div>
  16. </div>
  17. 按住录音:
  18. hold: 按住事件 开始录音(回调函数)
  19. release: 松开事件 结束录音 执行录音中的回调函数
  20. 4.app录音:
  21. var rec = plus.audio.getRcorder()
  22. rec.record(
  23. {filename:"_doc/audio/",format:"amr"},
  24. function(success){ success //录音文件保存路径 },
  25. function(error){}
  26. )
  27. rec.stop()
  28. 5.app与服务器端文件传输(ws传输):
  29. 1.app使用dataURL方式打开录音文件 base64 文件
  30. 2.通过某个函数 Base64 格式的文件 转为 Blob 用于 websocket传输
  31. 3.Blob对象使用Ws发送至服务端
  32. 4.服务端保存文件(amr)
  33. 5.amr 转换为 mp3 使用 ffmpeg -i xxx.amr xxx.mp3
  34. 6.简单的对话(app向玩具(web)发起):
  35. app 1.发起两次 ws.send({to_user:}) 告诉服务端我要发给谁消息
  36. 2. ws.send(blob) app与服务器端文件传输
  37. websocket服务:
  38. 0.创建两个变量,用于接收to_user blob对象
  39. 1.收到用户的JSON字符串,to_user
  40. 获取对方的Websocket,用户send
  41. 2.收到用户的Blob对象,语音文件
  42. 保存成amr文件,转换成mp3
  43. 注意保存文件的路径
  44. 3.将转换完成的文件发送给 to_user
  45. 4.两个变量置空

由于时间关系,详细步骤略…,主要修改了3个文件。

MyApp: chat.html,index.html

banana: im_serv.py

最终效果,使用APP给 小甜甜 说一段话:

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图24

第二个网页,也就是小甜甜的,会自动播放声音

Day129 玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话 - 图25

完整代码,请参考github:

https://github.com/987334176/Intelligent_toy/archive/v1.3.zip

未完待续…