Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/master'

yp 2 gadi atpakaļ
vecāks
revīzija
f69d3c45f3

+ 16 - 4
spring-cloud/server-basic/src/main/java/com/jd/controller/SmartExplainController.java

@@ -1,6 +1,11 @@
 package com.jd.controller;
 
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.jd.code.ConstString;
@@ -37,15 +42,13 @@ public class SmartExplainController {
     @PostMapping("/addBean")
     @ApiOperation(value = "2、新增")
     public Map<String, Object> addBean(SmartExplain smartExplain) {
-        smartExplain.setStatus(20);
-        smartExplain.setCreateTime(DateUtil.now());
-        return SendUtil.send(true, null, smartExplainService.save(smartExplain));
+        return SendUtil.send(true, null, smartExplainService.saveBean(smartExplain));
     }
 
     @PostMapping("/updateBean")
     @ApiOperation(value = "3、修改")
     public Map<String, Object> updateBean(SmartExplain smartExplain) {
-        return SendUtil.send(true, null, smartExplainService.updateById(smartExplain));
+        return SendUtil.send(true, null, smartExplainService.updateBean(smartExplain));
     }
 
     @PostMapping("/deleteById")
@@ -80,4 +83,13 @@ public class SmartExplainController {
         bean.setStatus(status);
         return SendUtil.send(true, null, smartExplainService.updateById(bean));
     }
+
+    @GetMapping("/findByType/{type}")
+    @ApiOperation(value = "根据类型查询当前启用的讲解方案")
+    public Map<String, Object> findByType(@PathVariable("type") Integer type) {
+        LambdaQueryWrapper<SmartExplain> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SmartExplain::getExplainType,type);
+        wrapper.eq(SmartExplain::getStatus,10);
+        return SendUtil.send(true, null, smartExplainService.getOne(wrapper));
+    }
 }

+ 3 - 0
spring-cloud/server-basic/src/main/java/com/jd/service/SmartExplainService.java

@@ -11,5 +11,8 @@ public interface SmartExplainService extends IService<SmartExplain> {
 
     Boolean disableOther(Integer type);
 
+    Boolean saveBean(SmartExplain smartExplain);
+
+    Boolean updateBean(SmartExplain smartExplain);
 
 }

+ 1 - 1
spring-cloud/server-basic/src/main/java/com/jd/service/impl/SafetyInfoServiceImpl.java

@@ -71,7 +71,7 @@ public class SafetyInfoServiceImpl implements SafetyInfoService {
     @Autowired
     private ThreeApiClient threeApiClient;
 
-    @Value("${contrl.connection-timeout}")
+    //@Value("${contrl.connection-timeout}")
     private Integer connectionTimeout = 30000;
 
     @Override

+ 81 - 1
spring-cloud/server-basic/src/main/java/com/jd/service/impl/SmartExplainServiceImpl.java

@@ -1,15 +1,95 @@
 package com.jd.service.impl;
 
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.jd.entity.SmartExplain;
 import com.jd.mapper.SmartExplainMapper;
 import com.jd.service.SmartExplainService;
+import com.jd.util.ConvertUtils;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+
 @Service
 public class SmartExplainServiceImpl extends ServiceImpl<SmartExplainMapper, SmartExplain> implements SmartExplainService {
+
+    private static final String VOCIE_HTTP = "http://23.37.100.80:8093";
+
+    @Value("${web.file}")
+    private String filePath;
+
+    @Value("${web.rootPath}")
+    private String rootPath;
+
     @Override
     public Boolean disableOther(Integer type) {
-        return baseMapper.disableOther(type)>0;
+        return baseMapper.disableOther(type) > 0;
+    }
+
+    @Override
+    public Boolean saveBean(SmartExplain smartExplain) {
+        //处理语音转换
+        JSONArray audioUriArr = JSONUtil.createArray();
+        JSONArray audioArr = JSONUtil.parseArray(smartExplain.getContent());
+        audioArr.forEach(t -> audioUriArr.put( this.play(t.toString())));
+        smartExplain.setAudioUrl(audioUriArr.toString());
+
+        smartExplain.setStatus(20);
+        smartExplain.setCreateTime(DateUtil.now());
+        return baseMapper.insert(smartExplain) > 0;
+    }
+
+    @Override
+    public Boolean updateBean(SmartExplain smartExplain) {
+        //处理语音转换
+        JSONArray audioUriArr = JSONUtil.createArray();
+        JSONArray audioArr = JSONUtil.parseArray(smartExplain.getContent());
+        audioArr.forEach(t -> audioUriArr.put( this.play(t.toString())));
+        smartExplain.setAudioUrl(audioUriArr.toString());
+
+        smartExplain.setCreateTime(DateUtil.now());
+        return baseMapper.updateById(smartExplain) > 0;
+    }
+
+    public String play(String txt) {
+        long startTime = System.currentTimeMillis();
+        HashMap<String, Object> paramMap = new HashMap<>();
+        paramMap.put("txt", txt);
+        String result = HttpUtil.post(VOCIE_HTTP+"/awaken/tts", paramMap);
+        if (!StrUtil.isBlank(result)) {
+            JSONObject obj = JSONUtil.parseObj(result);
+            System.err.println(obj);
+            // 下载音频文件
+            File file = FileUtil.file(filePath + "/" + rootPath + "/audio"+ "/" + obj.getStr("data"));
+            try{
+                if(!file.exists()){
+//                    file.mkdirs();
+                    file.createNewFile();
+                }
+            }catch (Exception e){
+                e.printStackTrace();
+                log.error("文件转换失败");
+            }
+            HttpUtil.downloadFile(VOCIE_HTTP + "/download/file?name=" + obj.getStr("data"), file);
+            // 转换格式
+            String path = filePath + "/" + rootPath + "/audio" + "/" + obj.getStr("data").substring(0, obj.getStr("data").lastIndexOf(".")) + ".wav";
+            try {
+                ConvertUtils.convertPcm2Wav(filePath + "/" + rootPath + "/audio" + "/" + obj.getStr("data"), path);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            System.err.println("转换耗时:" + (System.currentTimeMillis() - startTime));
+            return path.substring(path.lastIndexOf(filePath) + filePath.length());
+        }
+        return "";
     }
 }

+ 63 - 0
spring-cloud/server-basic/src/main/java/com/jd/util/ConvertUtils.java

@@ -0,0 +1,63 @@
+package com.jd.util;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+/**
+ * @Author: clf
+ * @Date: 2020-03-08
+ * @Description: 语音合成工具类
+ */
+public class ConvertUtils {
+ 
+    /**
+     * 转换音频文件
+     * @param src 需要转换的pcm音频路径
+     * @param target 保存转换后wav格式的音频路径
+     * @throws Exception
+     */
+    public static void convertPcm2Wav(String src, String target) throws Exception {
+        FileInputStream fis = new FileInputStream(src);
+        FileOutputStream fos = new FileOutputStream(target);
+ 
+        //计算长度
+        byte[] buf = new byte[1024 * 4];
+        int size = fis.read(buf);
+        int PCMSize = 0;
+        while (size != -1) {
+            PCMSize += size;
+            size = fis.read(buf);
+        }
+        fis.close();
+ 
+        //填入参数,比特率等等。这里用的是16位单声道 8000 hz
+        WaveHeader header = new WaveHeader();
+        //长度字段 = 内容的大小(PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
+        header.fileLength = PCMSize + (44 - 8);
+        header.FmtHdrLeth = 16;
+        header.BitsPerSample = 16;
+        header.Channels = 2;
+        header.FormatTag = 0x0001;
+        header.SamplesPerSec = 8000;
+        header.BlockAlign = (short)(header.Channels * header.BitsPerSample / 8);
+        header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
+        header.DataHdrLeth = PCMSize;
+ 
+        byte[] h = header.getHeader();
+ 
+        assert h.length == 44; //WAV标准,头部应该是44字节
+        //write header
+        fos.write(h, 0, h.length);
+        //write data stream
+        fis = new FileInputStream(src);
+        size = fis.read(buf);
+        while (size != -1) {
+            fos.write(buf, 0, size);
+            size = fis.read(buf);
+        }
+        fis.close();
+        fos.close();
+        System.out.println("Convert OK!");
+    }
+ 
+}

+ 19 - 0
spring-cloud/server-page/src/main/resources/static/ipad/index.html

@@ -201,6 +201,25 @@
                         <!--<div id="volume" style="width:100%"></div>-->
                     </div>
                 </div>
+
+                <div class="flow_con">
+                    <div class="flow_title">
+                        <img src="img/lckz.png" />
+                        <span>智能讲解</span>
+                    </div>
+                    <div id="smartExplainBtns" class="buts">
+                        <div class="layui-form" lay-filter="smartExplainForm" style="width: 100%;">
+                            <div class="layui-form-item">
+                                <input type="radio" name="smartExplainType" lay-filter="" value="10" title="视频" checked="">
+                                <input type="radio" name="smartExplainType" lay-filter="" value="20" title="图片">
+                            </div>
+                        </div>
+                        <button type="button" style="width: 30%;" data-type="1">播报</button>
+                        <button class="btn-color-disable" type="button" style="width: 30%;" data-type="2">停止</button>
+                        <button class="btn-color-disable" type="button" style="width: 30%;" data-type="3">暂停</button>
+                        <!--<div id="volume" style="width:100%"></div>-->
+                    </div>
+                </div>
                 <!-- 页面操作 -->
                 <!-- <div class="page_oper">
 						<div class="page_title">

+ 51 - 0
spring-cloud/server-page/src/main/resources/static/ipad/js/basic/index.js

@@ -346,6 +346,57 @@ function initClick() {
 			}
 		}
 	});
+
+	// 智能讲解播报
+	$('#smartExplainBtns').on('click', 'button', function() {
+		if ($(this).hasClass('btn-color-disable')) {
+			return;
+		}
+		var explainType = form.val("smartExplainForm").smartExplainType;
+		let type = $(this).attr('data-type');
+		if (type == 1) {
+			// 播报 发送播放消息
+			myWebsocket.websocket.send(JSON.stringify({
+				type: 'smartExplain',
+				event: 'open',
+				explainType
+			}));
+			$('#smartExplainBtns button').removeClass('btn-color-disable');
+			$('#smartExplainBtns button[data-type="1"]').addClass('btn-color-disable');
+			$('input[name="smartExplainType"]').attr('disabled', 'disabled');
+			form.render();
+		} else if (type == 2) {
+			// 停止 发送停止消息
+			myWebsocket.websocket.send(JSON.stringify({
+				type: 'smartExplain',
+				event: 'close',
+				explainType
+			}));
+			$('#smartExplainBtns button').addClass('btn-color-disable');
+			$('#smartExplainBtns button[data-type="1"]').removeClass('btn-color-disable');
+			$('input[name="smartExplainType"]').removeAttr('disabled');
+			form.render();
+		} else {
+			// 暂停、继续
+			if ($(this).text() == '继续') {
+				myWebsocket.websocket.send(JSON.stringify({
+					type: 'smartExplain',
+					event: 'start',
+					explainType
+				}));
+				$('#smartExplainBtns button[data-type="3"]').text('暂停');
+				form.render();
+			} else {
+				myWebsocket.websocket.send(JSON.stringify({
+					type: 'smartExplain',
+					event: 'stop',
+					explainType
+				}));
+				$('#smartExplainBtns button[data-type="3"]').text('继续');
+				form.render();
+			}
+		}
+	});
 }
 
 function switchSource() {

+ 153 - 47
spring-cloud/server-page/src/main/resources/static/page/js/basic/smartExplain.js

@@ -1,4 +1,4 @@
-let form, table, uploadInst;
+let form, table, uploadInst, upload;
 let ACCEPT = 'images';
 layui.config({
     base: 'js/encryption/'
@@ -134,41 +134,7 @@ layui.config({
         }
     });
 
-    uploadInst = upload.render({
-        elem: '#uploadBtn', //绑定元素
-        url: PAGE_BASIC + '/upload/fileUpload',
-        accept: ACCEPT,
-        headers: {
-            'timestamp': new Date().getTime(),
-            'Authorization': sessionStorage.TOKEN_TYPE + ' ' + sessionStorage.ACCESS_TOKEN
-        },
-        data: {
-            'prefix': ACCEPT
-        },
-        before: function (obj) {
-            layer.load(2); //上传loading
-        },
-        done: function (res) {
-            layer.closeAll('loading');
-            //上传完毕回调
-            console.info(res)
-            if (res.data) {
-                let fileUrl = $('#fileUrl').val();
-                if (fileUrl) {
-                    fileUrl = fileUrl + ';' + res.data.filePath;
-                } else {
-                    fileUrl = res.data.filePath;
-                }
-                $('#fileUrl').val(fileUrl);
-                console.info($('#fileUrl').val())
-            }
-        },
-        error: function (err) {
-            //请求异常回调
-            console.error(err)
-            layer.closeAll('loading');
-        }
-    });
+    uploadInst = loadUpload("#uploadBtn")
 
     // 验证表单
     form.verify({
@@ -240,6 +206,10 @@ layui.config({
     $(".back").click(function () {
         $(".main").addClass("layui-show").removeClass("layui-hide");
         $(".add").addClass("layui-hide").removeClass("layui-show");
+        $("#list").html($("#list .listItem:first"))
+        $("#list").addClass("layui-hide").removeClass("layui-show");
+        $("#list .listItem:first").find(".content").val("")
+        $("#list .listItem:first").find(".fileUrl").val("")
     });
 
 });
@@ -249,6 +219,9 @@ layui.config({
  */
 function addExplain() {
     let postData = form.val('explainInfo');
+    //封装参数
+    postData = loadParams(postData)
+    console.log(postData)
     let index = layer.load(2);
     ly.ajax({
         type: 'POST',
@@ -277,6 +250,8 @@ function addExplain() {
  */
 function updateExplain() {
     let postData = form.val('explainInfo');
+    //封装参数
+    postData = loadParams(postData)
     let index = layer.load(2);
     ly.ajax({
         type: 'POST',
@@ -312,11 +287,18 @@ function echoExplainInfo(id) {
         dataType: 'json',
         success: function (json) {
             if (json.result) {
+                let data = json.data
                 $('#id').val(id);
                 $('#explainName').val(json.data.explainName);
                 $('#explainType').val(json.data.explainType);
-                $('#fileUrl').val(json.data.fileUrl);
-                $('#content').val(json.data.content);
+                data.fileUrl = JSON.parse(data.fileUrl)
+                data.content = JSON.parse(data.content)
+                //加载文件列表
+                $("#list").empty()
+                loadFileList(data)
+
+                /*$('#fileUrl').val(json.data.fileUrl);
+                $('#content').val(json.data.content);*/
                 $('#list').removeClass('layui-hide');
                 if (json.data.explainType == 10) {
                     ACCEPT = 'video';
@@ -349,6 +331,45 @@ function echoExplainInfo(id) {
     });
 }
 
+function loadFileList(data) {
+    data.fileUrl.forEach((t, i) => {
+        let html = `<div class="listItem">
+                    <div class="layui-form-item">
+                        <label class="layui-form-label">
+                            <button type="button" class="layui-btn uploadBtn" id="uploadBtn` + i + `">
+                                <i class="layui-icon">&#xe67c;</i>上传文件
+                            </button>
+                        </label>
+                        <div class="layui-input-inline" style="display: flex;margin-left: 50px;">
+                            <input type="hidden" class="fileUrl" name="fileUrl" lay-verify="fileUrl" value="` + t + `">
+                            <input type="number" class="layui-input timestamp" style="width: 300px;" step="100" placeholder="间隔时间,最小100毫秒,最大10分钟" max="600000" autocomplete="off">
+                            毫秒
+                            <button type="button" class="layui-btn" style="margin-left: 20px;" onclick="insertTime(this);">
+                                <i class="layui-icon">&#xe624;</i>插入间隔时间
+                            </button>
+                            <button type="button" class="layui-btn" style="margin-left: 20px;" onclick="removeItem(this)">
+                            <i class="layui-icon">&#xe616;</i>移除
+                        </button>
+                        </div>
+                    </div>
+                    <div>
+                        <div class="layui-form-item">
+                            <label class="layui-form-label"><span class="font-red">*</span>语音内容:</label>
+                            <div class="layui-input-inline">
+                                <textarea class="layui-textarea content" id="content` + i + `" name="content" lay-verify="content" placeholder="请输入语音内容" autocomplete="off" ></textarea>
+                            </div>
+                        </div>
+                    </div>
+                </div>`;
+
+        $("#list").append(html)
+        //设置文本域的值
+        $("#content" + i).val(data.content[i])
+        //加载上传组件
+        loadUpload("#uploadBtn" + i)
+    })
+}
+
 /**
  * 删除智能讲解
  * @param id
@@ -391,8 +412,8 @@ function reloadTable() {
 /**
  * 插入语言间隔时间
  */
-function insertTime() {
-    let timestamp = $.trim($('#timestamp').val());
+function insertTime(obj) {
+    let timestamp = $.trim($(obj).parent().find(".timestamp").val());
     if (!timestamp) {
         layer.msg('请输入语音时间间隔', {icon: 5});
         return false;
@@ -401,13 +422,16 @@ function insertTime() {
         layer.msg('语音时间间隔,最小100毫秒,最大10分钟', {icon: 5});
         return false;
     }
-    let tc = document.getElementById("content");
+    let tc = $(obj).parent().parent().parent().find(".content")
+    //把jquery对象转换成js对象
+    tc = tc.get(0)
     let tclen = tc.value.length;
     tc.focus();
     // 兼容性检查
     if (typeof document.selection != "undefined") {
         // document.selection.createRange().text = timestamp;
     } else {
+        console.log(tc.selectionStart)
         tc.value =
             // 获取光标的开始位置
             tc.value.substr(0, tc.selectionStart) + `[p` + (timestamp).toString() + `]` + tc.value.substring(tc.selectionStart, tclen);
@@ -449,16 +473,44 @@ function updateStatus(id, status) {
  */
 function addItem() {
     let count = $('.content').length;
-    let html = `<div class="layui-form-item">
-                    <label class="layui-form-label"><span class="font-red">*</span>语音内容` + (++count) + `:</label>
-                    <div class="layui-input-inline" style="display: flex">
-                        <textarea class="layui-textarea content" lay-verify="content" placeholder="请输入语音内容" autocomplete="off"></textarea>
-                        <button type="button" class="layui-btn" style="margin-left: 20px;" onclick="removeItem(this)">
+    let html = `<div class="listItem">
+                    <div class="layui-form-item">
+                        <label class="layui-form-label">
+                            <button type="button" class="layui-btn uploadBtn" id="uploadBtn` + count + `">
+                                <i class="layui-icon">&#xe67c;</i>上传文件
+                            </button>
+                        </label>
+                        <div class="layui-input-inline" style="display: flex;margin-left: 50px;">
+                            <input type="hidden" class="fileUrl" name="fileUrl" lay-verify="fileUrl">
+                            <input type="number" class="layui-input timestamp" style="width: 300px;" step="100" placeholder="间隔时间,最小100毫秒,最大10分钟" max="600000" autocomplete="off">
+                            毫秒
+                            <button type="button" class="layui-btn" style="margin-left: 20px;" onclick="insertTime(this);">
+                                <i class="layui-icon">&#xe624;</i>插入间隔时间
+                            </button>
+                            <button type="button" class="layui-btn" style="margin-left: 20px;" onclick="removeItem(this)">
                             <i class="layui-icon">&#xe616;</i>移除
                         </button>
+                        </div>
+                    </div>
+                    <div>
+                        <div class="layui-form-item">
+                            <label class="layui-form-label"><span class="font-red">*</span>语音内容:</label>
+                            <div class="layui-input-inline">
+                                <textarea class="layui-textarea content" name="content" lay-verify="content" placeholder="请输入语音内容" autocomplete="off"></textarea>
+                            </div>
+                        </div>
                     </div>
                 </div>`;
     $('#list').append(html);
+    //添加完之后重新加载上传组件
+    loadUpload("#uploadBtn" + count).reload({
+            accept: ACCEPT,
+            acceptMime: ACCEPT == "images" ? 'image/*' : 'video/*',
+            data: {
+                'prefix': ACCEPT
+            },
+        }
+    )
 }
 
 /**
@@ -466,5 +518,59 @@ function addItem() {
  * @param obj
  */
 function removeItem(obj) {
-    $(obj).parent().parent().remove();
+    $(obj).parent().parent().parent().remove();
+}
+
+/**
+ * 加载上传组件
+ * @param el
+ * @returns {*}
+ */
+function loadUpload(el) {
+    return upload.render({
+        elem: el, //绑定元素
+        url: PAGE_BASIC + '/upload/fileUpload',
+        accept: ACCEPT,
+        headers: {
+            'timestamp': new Date().getTime(),
+            'Authorization': sessionStorage.TOKEN_TYPE + ' ' + sessionStorage.ACCESS_TOKEN
+        },
+        data: {
+            'prefix': ACCEPT
+        },
+        before: function (obj) {
+            layer.load(2); //上传loading
+        },
+        done: function (res) {
+            let item = this.item
+            layer.closeAll('loading');
+            //上传完毕回调
+            if (res.data) {
+                item.parent().parent().find(".fileUrl").val(res.data.filePath)
+            }
+        },
+        error: function (err) {
+            //请求异常回调
+            console.error(err)
+            layer.closeAll('loading');
+        }
+    });
+}
+
+/**
+ * 封装请求参数
+ * @param postData
+ */
+function loadParams(postData) {
+    //取到文件list
+    let items = $("#list").children(".listItem")
+    postData.fileUrl = []
+    postData.content = []
+    for (let i = 0; i < items.length; i++) {
+        postData.fileUrl.push($(items[i]).find(".fileUrl").val())
+        postData.content.push($(items[i]).find(".content").val())
+    }
+    postData.fileUrl = JSON.stringify(postData.fileUrl)
+    postData.content = JSON.stringify(postData.content)
+    return postData
 }

+ 1 - 1
spring-cloud/server-page/src/main/resources/static/page/js/constants.js

@@ -2,7 +2,7 @@
  * 配置项目请求接口地址
  */
 // var GATEWAY_URL = "http://23.37.100.80:8084";
-var GATEWAY_URL = "http://192.168.0.100:8084";
+ var GATEWAY_URL = "http://23.37.100.87:8084";
 // var GATEWAY_URL = "http://192.168.101.242:8084";
 var PAGE_URL = "http://23.37.1.104:5500";
 

+ 25 - 23
spring-cloud/server-page/src/main/resources/static/page/smartExplain.html

@@ -82,30 +82,32 @@
                     </select>
                 </div>
             </div>
-            <div class="operation layui-hide">
-                <div class="layui-form-item">
-                    <label class="layui-form-label">
-                        <button type="button" class="layui-btn" id="uploadBtn">
-                            <i class="layui-icon">&#xe67c;</i>上传文件
-                        </button>
-                    </label>
-                    <div class="layui-input-inline" style="display: flex;margin-left: 50px;">
-                        <input type="hidden" id="fileUrl" name="fileUrl" lay-verify="fileUrl">
-                        <input type="number" class="layui-input" style="width: 300px;" id="timestamp" step="100" placeholder="间隔时间,最小100毫秒,最大10分钟" max="600000" autocomplete="off">
-                        毫秒
-                        <button type="button" class="layui-btn" style="margin-left: 20px;" onclick="insertTime();">
-                            <i class="layui-icon">&#xe624;</i>插入间隔时间
-                        </button>
-                        <button type="button" class="layui-btn layui-hide" style="margin-left: 20px;" id="additionBtn" onclick="addItem()">
-                            <i class="layui-icon">&#xe61f;</i>更多
-                        </button>
-                    </div>
-                </div>
-                <div id="list">
+            <div id="list" class="operation layui-hide">
+                <div class="listItem">
                     <div class="layui-form-item">
-                        <label class="layui-form-label"><span class="font-red">*</span>语音内容:</label>
-                        <div class="layui-input-inline">
-                            <textarea class="layui-textarea" id="content" name="content" lay-verify="content" placeholder="请输入语音内容" autocomplete="off"></textarea>
+                        <label class="layui-form-label">
+                            <button type="button" class="layui-btn uploadBtn" id="uploadBtn">
+                                <i class="layui-icon">&#xe67c;</i>上传文件
+                            </button>
+                        </label>
+                        <div class="layui-input-inline" style="display: flex;margin-left: 50px;">
+                            <input class="fileUrl" type="hidden" id="fileUrl" name="fileUrl" lay-verify="fileUrl">
+                            <input type="number" class="layui-input timestamp" style="width: 300px;" id="timestamp" step="100" placeholder="间隔时间,最小100毫秒,最大10分钟" max="600000" autocomplete="off">
+                            毫秒
+                            <button type="button" class="layui-btn" style="margin-left: 20px;" onclick="insertTime(this);">
+                                <i class="layui-icon">&#xe624;</i>插入间隔时间
+                            </button>
+                            <button type="button" class="layui-btn layui-hide" style="margin-left: 20px;" id="additionBtn" onclick="addItem()">
+                                <i class="layui-icon">&#xe61f;</i>更多
+                            </button>
+                        </div>
+                    </div>
+                    <div>
+                        <div class="layui-form-item">
+                            <label class="layui-form-label"><span class="font-red">*</span>语音内容:</label>
+                            <div class="layui-input-inline">
+                                <textarea class="layui-textarea content" id="content" name="content" lay-verify="content" placeholder="请输入语音内容" autocomplete="off"></textarea>
+                            </div>
                         </div>
                     </div>
                 </div>