Refactor(AI智能):采取分包

This commit is contained in:
lifizer
2025-04-01 14:13:57 +08:00
parent 68cfae2893
commit 84562cf6f1
26 changed files with 22 additions and 19 deletions
+277
View File
@@ -0,0 +1,277 @@
<template>
<view
:class="{
'focus': inputOnFocus
}"
class="fix-page-bottom"
>
<view v-if="startTaskFlag" class="music-wrap">
<view class="item one" />
<view class="item two" />
<view class="item three" />
<view class="item four" />
<view class="item five" />
<view class="item six" />
<view class="item seven" />
</view>
<view v-if="inputModel === 'input'" class="bottom-body flex-center-between">
<view class="history-wrap" @click="historyViewHandle">
历史
</view>
<view class="input-container">
<!-- 输入模式切换 -->
<view
class="input-model"
@click="toggleInputModel"
>
<image
:src="webUrl + '/aiChat/microphone.png'"
class="icon"
mode="widthFix"
/>
</view>
<!-- 输入框 -->
<view class="input-wrap flex-center-between">
<input
v-model.trim="prompt"
:show-confirm-bar="false"
:disabled="loading"
:maxlength="-1"
class="inp"
@confirm="sendHandle"
@focus="promptInputFocusHandle"
@blur="promptInputBlurHandle"
/>
<view v-if="!prompt && !inputOnFocus" class="placeholder-txt">发消息...</view>
</view>
<view
class="send-btn"
@click="sendHandle"
>
<image
:src="webUrl + '/aiChat/send.png'"
class="icon"
mode="widthFix"
/>
</view>
</view>
</view>
<!-- 语音输入 -->
<view
v-if="inputModel === 'sound'"
class="sound-input-wrap"
@touchstart="startRecord"
@touchend="stopRecord"
>
<image
:src="webUrl + '/icon-20.png'"
class="icon"
mode="widthFix"
/>
按住说话
</view>
</view>
</template>
<script>
import {
makeFileTransTask,
getFileTransResult
} from '@/api/voiceToText/index'
import { VUE_APP_API_URL } from '@/config'
import settings from '@/config/baseSetting.js'
import cookie from '@/utils/store/cookie'
export default {
name: 'BottomSend',
props: {
loading: {
type: Boolean,
default: false
}
},
data() {
return {
token: cookie.get('login_status'),
userInfo: cookie.get('userInfo'),
webUrl: this.$VUE_APP_RESOURCES_URL,
// 输入模式:input-键盘,sound-语音
inputModel: 'input',
prompt: '',
recordManager: null,
// 录音权限是否开通
recordPermission: false,
taskId: '',
taskTimer: null,
startTaskFlag: false,
// 输入框是否聚焦
inputOnFocus: false
}
},
beforeDestroy() {
this.clearIntervalFn()
},
created() {
uni.getSetting({
success: res => {
if (res.authSetting['scope.record']) {
this.recordPermission = true
}
}
})
},
methods: {
promptInputFocusHandle() {
this.inputOnFocus = true
},
promptInputBlurHandle() {
this.inputOnFocus = false
},
toggleInputModel() {
if (!this.token) {
uni.reLaunch({ url: '/pages/auth/login' })
return
}
if (this.loading) return
if (this.inputModel === 'input') {
uni.getSetting({
success: res => {
if (!res.authSetting['scope.record']) {
uni.authorize({
scope: 'scope.record',
success: () => {
console.log('获取录音权限成功')
this.recordPermission = true
this.inputModel = 'sound'
},
fail: () => {
console.log('获取录音权限失败')
this.recordPermission = false
}
})
} else {
this.recordPermission = true
this.inputModel = 'sound'
}
}
})
return
}
if (this.inputModel === 'sound') {
this.clearIntervalFn()
this.inputModel = 'input'
return
}
},
startRecord() {
if (this.loading) {
this.$toast('正在检索')
return
}
// 正在识别,不触发相关动作
if (this.startTaskFlag || this.taskTimer) {
return
}
this.recordManager = uni.getRecorderManager()
console.log('启动录音')
this.startTaskFlag = true
this.recordManager.start({
duration: 60 * 1000,
frameSize: 1,
sampleRate: 16000,
format: 'wav'
})
},
stopRecord() {
const _this = this
// 正在识别,不触发相关动作
if (this.taskTimer) {
return
}
if (_this.recordManager) {
_this.recordManager.stop()
console.log('停止录音')
_this.startTaskFlag = false
_this.recordManager.onStop((res) => {
console.log('录音结束------------')
console.log(res)
const { tempFilePath, fileSize, duration } = res
if (tempFilePath && fileSize && duration) {
_this.taskId = ''
_this.clearIntervalFn()
uni.uploadFile({
url: `${VUE_APP_API_URL + settings.sysPrefix}/common/file/local/upload`,
filePath: tempFilePath,
header: {
'client-id': 'app',
Authorization: 'Bearer ' + (_this.token || '')
},
name: 'file',
success: uploadRes => {
console.log(JSON.parse(uploadRes.data))
_this.renderTransTask(uploadRes)
},
fail: err => {
console.log(err)
}
})
}
})
}
},
renderTransTask(uploadRes) {
const _this = this
makeFileTransTask({
file: `${VUE_APP_API_URL + settings.sysPrefix}` + JSON.parse(uploadRes.data).data.fileUrl
}).then(taskRes => {
_this.taskId = taskRes.data
_this.getTransResultReq()
})
},
getTransResultReq() {
const _this = this
uni.showLoading({ title: '识别中...' })
_this.taskTimer = setInterval(() => {
getFileTransResult({
taskId: _this.taskId
}).then(res => {
const { success, data = [] } = res
if (success && data) {
uni.hideLoading()
_this.clearIntervalFn()
if (data === '__NULL__') {
_this.$toast('豁然没有听清您说了什么,请再说一遍')
} else {
if (data.length > 0) {
const prompt = data[0].Text
if (prompt) {
_this.$emit('send', { prompt: prompt.replace('。', '') })
}
}
}
}
})
}, 1000)
},
clearIntervalFn() {
this.taskTimer && clearInterval(this.taskTimer)
this.taskTimer = null
setTimeout(() => {
this.startTaskFlag = false
}, 300)
},
sendHandle() {
if (this.loading) return
if (!this.prompt) {
this.$toast('请输入您想问的内容')
return
}
this.$emit('send', { prompt: this.prompt })
this.prompt = ''
this.inputOnFocus = false
},
historyViewHandle() {
this.$emit('history')
}
}
}
</script>
+156
View File
@@ -0,0 +1,156 @@
<template>
<view
:class="{
'show': showLayer
}"
class="history-wrap"
>
<view class="layer" @click="closeLayer" />
<view
:style="{
'padding-top': statusBarHeight + 'px'
}"
class="history-container"
>
<view class="history-type">
<view class="title">
<view class="txt">30天内</view>
<image
:src="webUrl + '/aiChat/delete.png'"
class="icon"
mode="widthFix"
/>
</view>
<view class="list-cont">
<view
v-for="(item, index) in list"
:key="index"
class="item"
>
<view class="item-txt one-t">
{{ item.title }}
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import {
getChatList
} from '@/api/chat/index'
export default {
name: 'AiHistoryList',
props: {
statusBarHeight: {
type: Number,
default: 0
}
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
showLayer: false,
list: [],
moreList: []
}
},
methods: {
showView() {
this.showLayer = true
getChatList().then(res => {
const { success, data } = res
if (success) {
this.list = data['今天']
this.moreList = data['超过30天']
}
})
},
closeLayer() {
this.showLayer = false
}
}
}
</script>
<style scoped lang="less">
.history-wrap {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 1001;
transform: translateX(-100%);
opacity: 0;
transition: all 0.3s;
.layer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 2;
background-color: rgba(0, 0, 0, 0.5);
}
.history-container {
position: absolute;
left: 0;
top: 0;
bottom: 0;
z-index: 5;
width: 75%;
padding: 40rpx;
box-sizing: border-box;
border-top-right-radius: 60rpx;
border-bottom-right-radius: 60rpx;
overflow-y: auto;
background-color: #fff;
.history-type + .history-type {
padding: 40rpx 0 0 0;
}
.history-type {
.title {
display: flex;
align-items: center;
justify-content: space-between;
.txt {
font-size: 34rpx;
line-height: 50rpx;
}
.icon {
display: block;
width: 40rpx;
height: 40rpx;
}
}
.list-cont {
padding: 24rpx 0 0 0;
.item + .item {
margin: 12rpx 0 0 0;
}
.item {
.item-txt {
display: inline-block;
max-width: 100%;
height: 60rpx;
padding: 0 24rpx;
box-sizing: border-box;
border-radius: 30rpx;
color: #333;
font-size: 28rpx;
line-height: 60rpx;
background-color: #F1F2FF;
}
}
}
}
}
&.show {
opacity: 1;
transform: translateX(0);
}
}
</style>