Merge branch 'develop' into 'sit'

Develop

See merge request K17_AITS/tra-app!20
This commit is contained in:
李泉德
2025-07-15 16:24:07 +08:00
21 changed files with 168 additions and 105 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ ENV = 'development'
# DEV
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
# http://25.18.122.65:7001/download/dev-jax.apk
# app入口
#VITE_APP_BASE_API_Url = 'http://25.16.122.65:7001'
#VITE_APP_BASE_API_Url = 'http://25.16.122.91:9786'
+4
View File
@@ -4,6 +4,10 @@
{
"playground" : "standard",
"type" : "uni-app:app-android"
},
{
"playground" : "standard",
"type" : "uni-app:app-ios"
}
]
}
+5 -1
View File
@@ -1 +1,5 @@
npm i --registry=http://25.12.10.69:8081/repository/aliyun-npm/
npm install --registry=http://25.12.10.69:8081/repository/aliyun-npm/
npm 淘宝源下载 npm config set registry https://registry.npmmirror.com
npm config set registry http://25.12.10.69:8081/repository/aliyun-npm/
+2
View File
@@ -32,6 +32,8 @@
"@dcloudio/uni-quickapp-webview": "3.0.0-4060620250520001",
"crypto-js": "^3.1.9-1",
"markdown-it": "^14.1.0",
"markdown-it-attrs": "^4.3.1",
"markdown-it-highlightjs": "^4.2.0",
"pinia": "^2.3.1",
"terser": "^5.42.0",
"uuid": "^11.1.0",
@@ -64,6 +64,10 @@
watch: {
imgId: {
handler(newval, oldval) {
if(newval.indexOf('/') === 0) {
this.imgUrlShow = this.base_url + newval
return
}
if(newval.indexOf('http') === 0){
this.imgUrlShow = newval
return
@@ -128,7 +132,9 @@
},
mounted() {
if (this.imgId) {
if(this.imgId.indexOf('http') === 0){
if(this.imgId.indexOf('/') === 0) {
this.imgUrlShow = this.base_url + this.imgId
}else if(this.imgId.indexOf('http') === 0){
this.imgUrlShow = this.imgId
}else{
this.getPreviewUrlBlob(this.imgId);
@@ -7,42 +7,13 @@
<script>
// import {Marked} from 'marked';
import MarkdownIt from 'markdown-it';
import markdownItAttrs from 'markdown-it-attrs'
import markdownItHighlight from 'markdown-it-highlightjs'
export default {
name:"markdown-preview",
props: {
mdString: {
default:`## 我是二级标题
### 我是三级标题
| 我是表头 | 表头 2 |
| :----------- | :----------- |
|1|2|
在客服交流中使用标准术语是提高专业形象和服务质量的重要手段。以下是一些常见的客服标准用语及其适用场景:
1. **问候与介绍:**
- “您好,欢迎来到[公司名称],我是您的客服代表[姓名],很高兴为您服务。”
- “早上好/下午好/晚上好,[公司名称]客服中心,请问有什么可以帮到您的吗?”
2. **确认信息:**
- “为了更好地为您提供服务,能否麻烦您提供一下[具体需要的信息,如订单号、会员账号等]?”
- “我来确认一下,您的问题是关于[问题的具体描述],对吗?”
3. **表达理解与同情:**
- “非常理解您的感受,遇到这种情况确实会让人感到不便/烦恼。”
- “感谢您的耐心等待,我们正在积极处理您的问题。”
4. **解决问题:**
- “针对您提到的问题,我们可以采取[解决方案]的方式解决,您看这样可以吗?”
- “我已经记录了您的反馈,并将尽快转交给相关部门处理,预计[解决时间]内会有结果。”
5. **结束对话:**
- “如果您没有其他问题的话,那我们就先这样吧,祝您生活愉快!”
- “再次感谢您的来电,期待下次为您服务。再见!”
6. **特殊情况处理:**
- “很抱歉给您带来了不愉快的体验,这并不是我们希望发生的事情。我们会立即调查此事,并确保类似情况不再发生。”
- “对于您所反映的情况,我们深感歉意,但根据我们的规定,[解释原因]。不过,我可以尝试帮您寻找其他的解决方案。”
以上是客服交流中常用的标准化语言,实际应用时可根据具体情况适当调整,以保持沟通的真实性和亲和力。`,
default: '',
type: String
}
},
@@ -73,13 +44,31 @@
}
},
mounted(){
this.mdObj = new MarkdownIt()
this.mdObj = new MarkdownIt({
html: true,
linkify: true,
typographer: true
})
this.mdObj.use(markdownItAttrs)
this.mdObj.use(markdownItHighlight)
this.contentHtml = this.parseContent(this.mdString)
console.log(this.contentHtml)
}
}
</script>
<style>
<style scoped lang="scss">
pre{
background-color: #f4f4f4;
padding: 10px;
border-radius: 5px;
overflow: auto;
}
code{
font-family: monospace;
background-color: #e8e8e8;
padding: 2px 4px;
border-radius: 3px;
}
</style>
+2 -12
View File
@@ -162,7 +162,7 @@
},
{
"text": "学习",
"pagePath": "pages/study/index"
"pagePath": "pages/course/index"
},
{
"text": "场景",
@@ -198,15 +198,5 @@
"bounce": "none"
}
},
"uniIdRouter": {},
"condition" : { //模式配置,仅开发期间生效
"current": 0, //当前激活的模式(list 的索引项)
"list": [
{
"name": "", //模式名称
"path": "", //启动页面,必选
"query": "" //启动参数,在页面的onLoad函数里面得到
}
]
}
"uniIdRouter": {}
}
+7 -2
View File
@@ -147,8 +147,13 @@
></image>
</view>
<view class="icon text-btn" v-if="type === 1" @click.stop="translateVoice(dataInfo)"></view>
<view class="icon text-right-btn" @click.stop="finishQuestion(dataInfo)" v-if="isLast">完成</view>
<view class="icon text-right-btn" @click.stop="withdraw" v-show="showWithdraw">撤回
<view class="icon text-right-btn" @click.stop="finishQuestion(dataInfo)" v-show="isLast">完成</view>
<view
class="icon text-right-btn"
@click.stop="withdraw"
v-show="isLast"
>撤回
<!-- v-show="showWithdraw" -->
</view>
</view>
</view>
+32 -7
View File
@@ -38,7 +38,7 @@
</template>
<script setup>
import { toRefs, ref, computed, unref, onMounted, onUnmounted, nextTick } from 'vue';
import { toRefs, ref, computed, unref, onMounted, onUnmounted, nextTick, watch } from 'vue';
import common from '../../../common/common';
import permission from '@/common/permission.js'
const emit = defineEmits(['uploadVoice', 'submitAnswer','startRecord'])
@@ -46,9 +46,17 @@ const props = defineProps({
isDisabled:{
default: Boolean,
type: true
},
isLoading:{
default: false,
type: Boolean
}
})
const { isDisabled } = toRefs(props)
const isLoadingState = computed(() => props.isLoading)
watch(()=>props.isLoading,()=>{},{
deep:true,immediate:true
})
const showTalkingModel = ref(false)
const keyboardIsShow = ref(false)
const answerValue = ref('')
@@ -63,6 +71,12 @@ const initRecord = () => {
recordObjCom.value = uni.getRecorderManager()
unref(recordObjCom)?.onStop((res, duration)=>{
audioPath.value = res.tempFilePath
uni.saveFile({
tempFilePath:res.tempFilePath,
success:(res)=>{
console.log(res)
}
})
})
unref(recordObjCom).start({format:'mp3'})
stopRecord()
@@ -99,10 +113,6 @@ onMounted(async ()=>{
// }
// // #endif
})
// const playRecord = () => {
// audioObj.src = audioPath.value
// unref(audioObj).play()
// }
// 录音结束
// 记录 按下移动位置
@@ -116,6 +126,9 @@ const isOut = computed(() => {
let moveVal = (windowInfo.screenHeight - touchY.value) > _btnHeight
return moveVal
})
const outScreen = computed(()=>{
(windowInfo.screenHeight - touchY.value) <= 0
})
const recNum = ref(1)
const activeNum = ref(1)
@@ -135,8 +148,16 @@ const getRecordPermission = async (obj) => {
// #endif
}
const showOverlayTalking = (obj) => {
recNum.value += 1
if(isLoadingState.value){
uni.showToast({
title: '发送回答中,请稍后再试!',
icon: "none",
duration:1000
})
return
}
emit('startRecord')
recNum.value += 1
if(!isDisabled.value){
showTalkingModel.value = true
touchX.value = obj.changedTouches[0].pageX
@@ -150,7 +171,7 @@ const showOverlayTalking = (obj) => {
},59 * 1010)
}else{
uni.showToast({
title: '请先获取问题信息',
title: '暂无问题需要回答',
icon: "none",
duration:1000
})
@@ -169,6 +190,7 @@ const hideOverlayTalking = (obj) => {
},1000)
}
}else{
showTalkingModel.value = false
common.msg('录音失败,请重试!')
}
activeNum.value = recNum.value
@@ -177,6 +199,9 @@ const hideOverlayTalking = (obj) => {
const handleMove = (obj) => {
touchX.value = obj.changedTouches[0].pageX
touchY.value = obj.changedTouches[0].pageY
if(outScreen.value){
hideOverlayTalking(obj)
}
}
// 发送文本
const showKeyboard = () => {
+1 -1
View File
@@ -5,7 +5,7 @@
<view class="course-center-body">
<view class="body-title">
<!-- @click="test.testApi" -->
<uv-icon class="arrow-icon" name="arrow-left" @click="common.navigateBack()"/>
<uv-icon class="arrow-icon" name="arrow-left" @click="common.switchTab('/pages/index/index')"/>
课程中心
</view>
<view class="input-view" @click="toSearchPage">
+29 -10
View File
@@ -8,7 +8,7 @@
<image src="@/static/images/course/setting.png" alt="" class="image-icon" @click.stop="showChangeBgModelNow"></image>
</view>
<view class="body-title">
<uv-icon class="arrow-icon" name="arrow-left" @click="common.navigateBack()"/>
<uv-icon class="arrow-icon" name="arrow-left" @click="backRouter()"/>
语言选择开启学习之旅
</view>
<view class="voice-list">
@@ -43,7 +43,7 @@
<image src="@/static/images/course/setting.png" alt="" class="image-icon" @click.stop="showChangeBgModelNow"></image>
</view>
<view class="body-title" @click="scrollToBottomNow">
<uv-icon class="arrow-icon" name="arrow-left" @click="common.navigateBack()"/>
<uv-icon class="arrow-icon" name="arrow-left" @click="backRouter()"/>
课程学习
</view>
<view class="study-num">
@@ -82,7 +82,8 @@
@uploadVoice="uploadRecordNow"
@submitAnswer="submitAnswerNow"
@startRecord="changePlayItemId('')"
:isDisabled="((lastTalkingInfo.type||0) !== 4) || isUploadingVoice"
:isLoading="isUploadingVoice"
:isDisabled="((lastTalkingInfo.type||0) !== 4)"
/>
</view>
<uv-overlay :show="showSeekModel" opacity=".6" @click="showSeekModel = false">
@@ -194,7 +195,7 @@ const getData = async (flag) => {
step.value = 2
return
}
common.show('提示信息', '此门课程已有学习历史,请选择是否继续学习?',true, '重新开始','继续学习').then((res) => {
common.show('提示', '系统检测到您已存在学习记录,要继续学习',true, '重新学习','继续学习').then((res) => {
if(res) {
// 继续学习 获取学习历史接口
// 滚动到最下面
@@ -316,6 +317,9 @@ const nextStep = () => {
const pgrphNum = ref(1)
const showOrder = ref(0)
const isLastPgrph = ref(false)
const backRouter = ()=>{
common.redirectTo(`/pages/courseDetail/index?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study`)
}
// 获取段落
const getGraphInfoUnStudy = (flag = 0) => {
console.log(isLastPgrph.value)
@@ -333,7 +337,6 @@ const getGraphInfoUnStudy = (flag = 0) => {
requestBody: JSON.stringify(_data)
}).then(res=>{
let _body = res.body|| {}
console.log(_body)
if(!_body.chatBody) {
return
}
@@ -341,10 +344,10 @@ const getGraphInfoUnStudy = (flag = 0) => {
common.show('恭喜你已学完全部知识点', '有什么心得跟我们分享吗?',true, '取消','去评论').then((res) => {
if(res) {
// 去评论
common.navigateTo(`/pages/courseDetail/submit?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study`)
common.redirectTo(`/pages/courseDetail/submit?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study&imgId=${imageId.value}`)
}else{
// 取消
common.navigateTo(`/pages/courseDetail/index?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study`)
common.redirectTo(`/pages/courseDetail/index?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study`)
}
}).finally(() => {})
return
@@ -386,6 +389,7 @@ const getQuestionInfoUnStudy = (pgrphId) => {
.then(res=>{
let _id = new Date().getTime() + 'id'
let _body = res.body || {}
console.log(_body)
if(_body.chatBody && _body.chatBody?.stdyStat !== 'N'){
let _id = new Date().getTime() + 'id'
lastTalkingInfo.value = {
@@ -565,9 +569,11 @@ const sendMessage = (data) => {
}
// ws 结束
const startPgrphId = ref('')
const imageId = ref('')
onLoad((params) => {
crsId.value = params.crsId||'';
crsNum.value = params.crsNum||'';
imageId.value = params.imgId || ''
startPgrphId.value = params.pgrphId||'';
getData('1');
});
@@ -617,7 +623,7 @@ const uploadRecordNow = (voicePath) => {
userInfo: {...choiceTeacherInfo.value},
id: _id
})
isUploadingVoice.value = false
setIsUploadingVoice()
scrollToBottomNow()
// #endif
//#ifdef APP-PLUS
@@ -660,9 +666,9 @@ const uploadRecordNow = (voicePath) => {
})
scrollToBottomNow()
}else{}
isUploadingVoice.value = false
setIsUploadingVoice()
}).catch(err=>{
isUploadingVoice.value = false
setIsUploadingVoice()
talkingList.value = talkingList.value.filter(t=>t.id !== _id)
uni.showToast({
title: '系统异常,请联系管理员',
@@ -672,12 +678,22 @@ const uploadRecordNow = (voicePath) => {
})
// #endif
}
const setIsUploadingVoice = (flag = false) => {
if(flag){
isUploadingVoice.value = true
}else{
setTimeout(()=>{
isUploadingVoice.value = false
},100)
}
}
// 发送文本
const submitAnswerNow = (val, cb) => {
if(!testAnswer.value){
common.msg('每个问题最多连续发送五次答案!')
return
}
isUploadingVoice.value = true
let _data = {
qnsId: unref(lastTalkingInfo).qnsId,// 问题ID 必输项:true 类型:String
pgrphId: unref(lastTalkingInfo).pgrphId,// 段落ID 必输项:true 类型:String
@@ -691,6 +707,7 @@ const submitAnswerNow = (val, cb) => {
requestBody: JSON.stringify(_data)
})
.then(res=>{
setIsUploadingVoice()
let _id = new Date().getTime() + 'id'
let _body = res.body|| {}
if(!_body.chatBody) {
@@ -709,6 +726,8 @@ const submitAnswerNow = (val, cb) => {
})
scrollToBottomNow()
cb && cb()
}).catch(()=>{
setIsUploadingVoice()
})
}
// 撤回
@@ -78,7 +78,7 @@
<uni-popup ref="popup" background-color="red">
<view class="bottom_reply_input_div">
<uv-input v-model="reply_data.bbsContent" placeholderStyle='color:#999999;font-size:26rpx'
:placeholder="reply_data.placeholder" :focus="true" :maxlength="160">
:placeholder="reply_data.placeholder" :focus="true" :maxlength="100">
</uv-input>
<view class="" style="display: flex;flex-direction: column; margin:10rpx 20rpx;">
<view class="fl1"></view>
@@ -12,7 +12,6 @@
<view class="font_pf fz-24 study-status" >
{{ item.pgrphStdyStateName }}
</view>
</view>
<view class="bottom">
<view class="fz-30 font_pf text ellipsis-text">{{ item.pgrphContent }}</view>
+7 -4
View File
@@ -52,7 +52,7 @@
<view class="btn-box btn-box-height font_pf" v-if="[0,1].includes(tabAct)">
<uv-button type="primary"
@click="common.navigateTo('/pages/course/study?crsNum='+crsNum+'&crsId='+form.crsId)">去学习</uv-button>
@click="common.navigateTo('/pages/course/study?crsNum='+crsNum+'&crsId='+form.crsId + '&imgId=' + form.imgId)">去学习</uv-button>
<uv-button type="primary">去练习</uv-button>
<uv-button type="primary">去考试</uv-button>
@@ -115,7 +115,7 @@
};
const toStudyPrgh = (info) => {
common.navigateTo('/pages/course/study?crsNum=' + crsNum.value + '&crsId=' + form.value.crsId + '&pgrphId=' +
info.pgrphId)
info.pgrphId+ '&imgId=' + form.imgId)
}
const bgOpacity = computed(() => {
@@ -128,11 +128,13 @@
}
onLoad((params) => {
crsNum.value = params.crsNum;
getData();
uni.$on('refresh_comment_data', () => {
commentRef.value.getData();
})
});
onShow(()=>{
getData();
})
onUnload(() => {
uni.$off('refresh_comment_data')
});
@@ -201,7 +203,8 @@
.bg_div {
position: fixed;
width: 100%;
width: calc(100% + 4rpx);
left: -4rpx;
.back-image {
width: 100%;
+5
View File
@@ -70,6 +70,7 @@
"bbsContent": '', // 评价内容
})
const imgId = ref('')
const from = ref('')
const sliderChange = (event) => {
formData.bbsGrade = event.detail.value
}
@@ -86,12 +87,16 @@
common.msg('发表成功')
setTimeout(() => {
uni.$emit('refresh_comment_data')
if(from.value === 'study'){
} else {
}
common.navigateBack()
}, 500)
})
}
onLoad((params) => {
formData.crsNum = params.crsNum;
from.value = params.from;
imgId.value = params.imgId;
});
</script>
+13 -3
View File
@@ -1,9 +1,19 @@
<template>
<view>答案</view>
<view class="answer-content">
<MarkdownPreview/>
</view>
</template>
<script>
<script setup>
import MarkdownPreview from '@/components/markdown-preview/markdown-preview.vue';
</script>
<style>
<style scoped lang="scss">
.answer-content{
background-color: #fff;
padding: 20rpx;
border-radius: 15rpx;
padding-bottom: 30rpx;
position: relative;
}
</style>
+6 -5
View File
@@ -3,7 +3,7 @@
<!-- <MarkdownPreview/> -->
<view class="top-bg"></view>
<view class="bot-bg"></view>
<view class="index-dialog-body">
<scroll-view class="index-dialog-body" :scroll-y="true" upper-threshold="0" refresher-enabled="true" refresher-default-style="none" @refresherrefresh="scrolltolower">
<view class="seek-setting">
设置
<image src="@/static/images/course/setting.png" alt="" class="image-icon"></image>
@@ -84,7 +84,7 @@
@startRecord="startRecordCallback"
:isDisabled="false"
/>
</view>
</scroll-view>
</view>
</template>
@@ -92,7 +92,6 @@
import { ref } from 'vue'
import common from '@/common/common';
import TouchBtn from '@/pages/course/components/touchBtn.vue'
import MarkdownPreview from '@/components/markdown-preview/markdown-preview.vue';
import TalkingItem from './components/talking-item.vue';
const questionList = ref([
'对公小程序开户流程?',
@@ -126,14 +125,16 @@
const routerTo = (type) => {
switch(type){
case 'AI学':
common.navigateTo('/pages/course/index')
common.switchTab('/pages/course/index')
break;
default:
common.msg('页面建设中,敬请期待!')
break;
}
}
const scrolltolower = () => {
common.switchTab('/pages/index/index')
}
const uploadRecordNow = (voicePath) => {
}
+7 -9
View File
@@ -14,9 +14,7 @@
<view class="message_div">
<image src="@/static/images/index/message_b.png" class="message_img"></image>
<view class="message_text">消息</view>
<view class="message_num">
30
</view>
<view class="message_num">30</view>
</view>
</view>
@@ -80,7 +78,7 @@
<!-- 8个导航 -->
<view class="navigation_div">
<view class="group">
<view class="item" @click="common.navigateTo('/pages/course/index')">
<view class="item" @click="common.switchTab('/pages/course/index')">
<image class="icon" src="@/static/images/index/navigation_course.png"></image>
<view class="title">课程中心</view>
</view>
@@ -131,7 +129,7 @@
<uv-skeleton :loading="!item.crsNum" :rows="3" avatar avatar-shape="square" :title="false">
<view class="item" @click="goto_course_detail(item.crsNum)">
<view class="left">
<image-preview class="img-box img" :imgId="item.imgThumbId" mode="aspectFit"></image-preview>
<image-preview class="img-box img" :imgId="item.imgThumbId" mode="scaleToFill"></image-preview>
</view>
<view class="right">
<view class="title ellipsis-text">{{item.crsName}}</view>
@@ -164,7 +162,7 @@
<uv-skeleton :loading="!item.crsNum" :rows="3" avatar avatar-shape="square" :title="false">
<view class="item" @click="goto_course_detail(item.crsNum)">
<view class="left">
<image-preview class="img-box img" :imgId="item.imgThumbId" mode="aspectFit"></image-preview>
<image-preview class="img-box img" :imgId="item.imgThumbId" mode="scaleToFill"></image-preview>
</view>
<view class="right">
<view class="title ellipsis-text">{{item.crsName}}</view>
@@ -196,7 +194,7 @@
<uv-skeleton :loading="!item.crsNum" :rows="3" avatar avatar-shape="square" :title="false">
<view class="item" @click="goto_course_detail(item.crsNum)">
<view class="left">
<image-preview class="img-box img" :imgId="item.imgThumbId" mode="aspectFit"></image-preview>
<image-preview class="img-box img" :imgId="item.imgThumbId" mode="scaleToFill"></image-preview>
</view>
<view class="right">
<view class="title ellipsis-text">{{item.crsName}}</view>
@@ -383,7 +381,7 @@
width: 146rpx !important;
border-radius: 16rpx;
}
// 线
.bottom_line {
margin: 0 auto;
@@ -495,7 +493,7 @@
position: absolute;
top: 0;
left: 0;
width: 100%;
width: calc(100% + 4rpx);
height: 870rpx;
z-index: -1;
}
+14 -12
View File
@@ -74,7 +74,7 @@
<image class="icon" src="@/static/images/me/examination.png"></image>
<view class="title">考试记录</view>
</view>
<view class="item">
<view class="item" @click="goTest()">
<image class="icon" src="@/static/images/me/report.png"></image>
<view class="title">我的报告</view>
</view>
@@ -91,7 +91,7 @@
<view class="cell_div">
<uv-cell-group :border="false">
<uv-cell title="设置" :isLink="true" @click="common.navigateTo('/pages/setting/index')"></uv-cell>
<uv-cell title="客服" :isLink="true" @click="goTest"></uv-cell>
<uv-cell title="客服" :isLink="true"></uv-cell>
<uv-cell title="退出登录" :isLink="true" :clickable="false" :border="false"
@click="click_loginout()"></uv-cell>
</uv-cell-group>
@@ -192,16 +192,18 @@
imageAddr: '',
});
const goTest = () => {
const now = Date.now();
if (now - lastClickTime.value > 500) {
clickCount.value = 0;
}
clickCount.value++;
lastClickTime.value = now;
if (clickCount.value == 6) {
common.navigateTo('/pages/test/index');
clickCount.value = 0;
}
console.log('goTest');
common.navigateTo('/pages/test/index');
// const now = Date.now();
// if (now - lastClickTime.value > 500) {
// clickCount.value = 0;
// }
// clickCount.value++;
// lastClickTime.value = now;
// if (clickCount.value == 6) {
// common.navigateTo('/pages/test/index');
// clickCount.value = 0;
// }
};
+1
View File
@@ -24,6 +24,7 @@ import { get_base_url } from '../../api/request';
const url = get_base_url();
const ENV = import.meta.env
const appBaseInfo = uni.getAppBaseInfo()
console.log('appBaseInfo', appBaseInfo);
</script>
<style lang="scss" scoped>
+1 -1
View File
@@ -62,7 +62,7 @@
},
{
"text": "学习",
"pagePath": "pages/study/index"
"pagePath": "pages/course/index"
},
{
"text": "场景",