mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-15 21:32:49 +00:00
perf: 新增录音转文字
This commit is contained in:
parent
4fa54381a6
commit
7b1d352c95
@ -1348,6 +1348,55 @@ class DialogController extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/dialog/msg/convertrecord 25. 录音转文字
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__convertrecord
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
* @apiParam {String} base64 语音base64
|
||||
* @apiParam {Number} duration 语音时长(毫秒)
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__convertrecord()
|
||||
{
|
||||
$user = User::auth();
|
||||
$user->checkChatInformation();
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
//
|
||||
WebSocketDialog::checkDialog($dialog_id);
|
||||
//
|
||||
$path = "uploads/tmp/chat/" . date("Ym") . "/" . $dialog_id . "/";
|
||||
$base64 = Request::input('base64');
|
||||
$duration = intval(Request::input('duration'));
|
||||
if ($duration < 600) {
|
||||
return Base::retError('说话时间太短');
|
||||
}
|
||||
$data = Base::record64save([
|
||||
"base64" => $base64,
|
||||
"path" => $path,
|
||||
]);
|
||||
if (Base::isError($data)) {
|
||||
return Base::retError($data['msg']);
|
||||
}
|
||||
$recordData = $data['data'];
|
||||
$res = Extranet::openAItranscriptions($recordData['file']);
|
||||
if (Base::isError($res)) {
|
||||
return $res;
|
||||
}
|
||||
if (strlen($res['data']) < 1) {
|
||||
return Base::retError('转文字失败');
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/dialog/msg/sendfile 26. 文件上传
|
||||
*
|
||||
|
||||
@ -188,15 +188,53 @@
|
||||
v-transfer-dom
|
||||
:data-transfer="true"
|
||||
class="chat-input-record-transfer"
|
||||
:class="{cancel: touchLimitY}"
|
||||
:class="recordClassName"
|
||||
:style="recordTransferStyle"
|
||||
@click="stopRecord">
|
||||
<div v-if="recordDuration > 0" class="record-duration">{{recordFormatDuration}}</div>
|
||||
<div v-else class="record-loading"><Loading type="pure"/></div>
|
||||
<div class="record-cancel" @click.stop="stopRecord(true)">{{$L(touchLimitY ? '松开取消' : '向上滑动取消')}}</div>
|
||||
<div class="record-cancel" @click.stop="stopRecord(true)">{{$L(recordFormatTip)}}</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 录音转文字 -->
|
||||
<transition name="fade">
|
||||
<div
|
||||
v-if="recordConvertIng"
|
||||
v-transfer-dom
|
||||
:data-transfer="true"
|
||||
class="chat-input-convert-transfer"
|
||||
:style="recordTransferStyle">
|
||||
<div class="convert-box">
|
||||
<div class="convert-content">
|
||||
<Input
|
||||
type="textarea"
|
||||
class="convert-result"
|
||||
v-model="recordConvertResult"
|
||||
:rows="1"
|
||||
:autosize="{minRows: 1, maxRows: 5}"
|
||||
:placeholder="recordConvertStatus === 0 ? '...' : ''"/>
|
||||
</div>
|
||||
<ul class="convert-footer">
|
||||
<li @click="recordConvertIng=false">
|
||||
<i class="taskfont"></i>
|
||||
<span>{{$L('取消')}}</span>
|
||||
</li>
|
||||
<li @click="convertSend('voice')">
|
||||
<i class="taskfont voice"></i>
|
||||
<span>{{$L('发送原语音')}}</span>
|
||||
</li>
|
||||
<li @click="convertSend('result')">
|
||||
<i v-if="recordConvertStatus === 0" class="send"><Loading/></i>
|
||||
<i v-else-if="recordConvertStatus === 2" class="taskfont error"></i>
|
||||
<i v-else class="taskfont send"></i>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
|
||||
<Modal
|
||||
v-model="fullInput"
|
||||
:mask-closable="false"
|
||||
@ -335,6 +373,9 @@ export default {
|
||||
recordInter: null,
|
||||
recordState: "stop",
|
||||
recordDuration: 0,
|
||||
recordConvertIng: false,
|
||||
recordConvertStatus: 0, // 0: 转换中 1: 转换成功 2: 转换失败
|
||||
recordConvertResult: '',
|
||||
|
||||
touchStart: {},
|
||||
touchFocus: false,
|
||||
@ -453,6 +494,9 @@ export default {
|
||||
if (this.recordRec) {
|
||||
this.recordRec = null
|
||||
}
|
||||
if (this.recordConvertIng) {
|
||||
this.recordConvertIng = false
|
||||
}
|
||||
if (this.recordInter) {
|
||||
clearInterval(this.recordInter)
|
||||
}
|
||||
@ -563,6 +607,24 @@ export default {
|
||||
return `${minute}:${seconds}″${millisecond}`
|
||||
},
|
||||
|
||||
recordClassName({touchLimitX, touchLimitY}) {
|
||||
if (touchLimitY) {
|
||||
return 'cancel'
|
||||
} else if (touchLimitX) {
|
||||
return 'convert'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
|
||||
recordFormatTip({touchLimitX, touchLimitY}) {
|
||||
if (touchLimitY) {
|
||||
return '松开取消'
|
||||
} else if (touchLimitX) {
|
||||
return '转文字'
|
||||
}
|
||||
return '向上滑动取消'
|
||||
},
|
||||
|
||||
dialogData() {
|
||||
return this.dialogId > 0 ? (this.cacheDialogs.find(({id}) => id == this.dialogId) || {}) : {};
|
||||
},
|
||||
@ -1140,7 +1202,7 @@ export default {
|
||||
if (this.showMenu) {
|
||||
return;
|
||||
}
|
||||
if (this.stopRecord(this.touchLimitY)) {
|
||||
if (this.stopRecord(this.touchLimitY, this.touchLimitX)) {
|
||||
return;
|
||||
}
|
||||
if (this.touchLimitY || this.touchLimitX) {
|
||||
@ -1222,7 +1284,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
stopRecord(isCancel) {
|
||||
stopRecord(isCancel, isConvert = false) {
|
||||
switch (this.recordState) {
|
||||
case "ing":
|
||||
this.recordState = "stop";
|
||||
@ -1235,7 +1297,12 @@ export default {
|
||||
$A.messageWarning("说话时间太短") // 小于 600ms 不发送
|
||||
} else {
|
||||
this.recordBlob = blob;
|
||||
this.uploadRecord(duration);
|
||||
this.recordDuration = duration;
|
||||
if (isConvert === true) {
|
||||
this.convertRecord();
|
||||
} else {
|
||||
this.uploadRecord();
|
||||
}
|
||||
}
|
||||
}, (msg) => {
|
||||
this.recordRec.close();
|
||||
@ -1270,7 +1337,54 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
uploadRecord(duration) {
|
||||
convertRecord() {
|
||||
if (this.recordBlob === null) {
|
||||
this.recordConvertIng = false
|
||||
return;
|
||||
}
|
||||
this.recordConvertResult = ''
|
||||
this.recordConvertStatus = 0
|
||||
this.recordConvertIng = true
|
||||
//
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
this.$store.dispatch("call", {
|
||||
url: 'dialog/msg/convertrecord',
|
||||
data: {
|
||||
dialog_id: this.dialogId,
|
||||
base64: reader.result,
|
||||
duration: this.recordDuration,
|
||||
},
|
||||
method: 'post',
|
||||
}).then(({data}) => {
|
||||
this.recordConvertStatus = data ? 1 : 2
|
||||
this.recordConvertResult = data || this.$L('转文字失败')
|
||||
}).catch(({msg}) => {
|
||||
this.recordConvertStatus = 2
|
||||
this.recordConvertResult = msg
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(this.recordBlob);
|
||||
},
|
||||
|
||||
convertSend(type) {
|
||||
if (!this.recordConvertIng) {
|
||||
return;
|
||||
}
|
||||
if (type === 'voice') {
|
||||
this.uploadRecord();
|
||||
this.recordConvertIng = false
|
||||
} else {
|
||||
if (this.recordConvertStatus === 1) {
|
||||
this.$emit('on-send', this.recordConvertResult)
|
||||
this.recordConvertIng = false
|
||||
} else if (this.recordConvertStatus === 2) {
|
||||
this.convertRecord()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
uploadRecord() {
|
||||
if (this.recordBlob === null) {
|
||||
return;
|
||||
}
|
||||
@ -1279,7 +1393,7 @@ export default {
|
||||
this.$emit('on-record', {
|
||||
type: this.recordBlob.type,
|
||||
base64: reader.result,
|
||||
duration,
|
||||
duration: this.recordDuration,
|
||||
})
|
||||
};
|
||||
reader.readAsDataURL(this.recordBlob);
|
||||
|
||||
1
resources/assets/sass/dark.scss
vendored
1
resources/assets/sass/dark.scss
vendored
@ -571,6 +571,7 @@ body.dark-mode-reverse {
|
||||
}
|
||||
|
||||
.chat-input-record-transfer {
|
||||
&.convert,
|
||||
&.cancel {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
@ -723,12 +723,122 @@
|
||||
margin-top: 6px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
&.convert {
|
||||
background-color: #2db7f5;
|
||||
color: #ffffff;
|
||||
}
|
||||
&.cancel {
|
||||
background-color: #ff6565;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-input-convert-transfer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-direction: column;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
.convert-box {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
height: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.convert-content {
|
||||
position: relative;
|
||||
background-color: $primary-color;
|
||||
color: #000000;
|
||||
width: 88%;
|
||||
padding: 18px;
|
||||
border-radius: 14px;
|
||||
transform: translateY(-50%);
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -15px;
|
||||
right: 12%;
|
||||
transform: translateX(-50%);
|
||||
border-width: 8px;
|
||||
border-style: solid;
|
||||
border-color: $primary-color transparent transparent transparent;
|
||||
}
|
||||
.convert-result {
|
||||
.ivu-input {
|
||||
font-size: 18px;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
color: #000000;
|
||||
border-radius: 0;
|
||||
outline: none;
|
||||
resize: none;
|
||||
&::placeholder {
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.convert-footer {
|
||||
width: 88%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 64px;
|
||||
> li {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
width: 76px;
|
||||
height: 76px;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
> i {
|
||||
font-size: 18px;
|
||||
&.voice {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
&.send,
|
||||
&.error {
|
||||
font-size: 22px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #0a7600;
|
||||
background: #000000;
|
||||
font-weight: 600;
|
||||
.common-loading {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
}
|
||||
&.error {
|
||||
color: #ff0000;
|
||||
font-size: 30px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
> span {
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-input-full-input {
|
||||
.ivu-modal {
|
||||
.ivu-modal-content {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user