feat: 接口调整,页面绘制

This commit is contained in:
杨航
2025-11-12 17:36:22 +08:00
parent 90a791ee0c
commit 6ef4717c38
11 changed files with 764 additions and 212 deletions
+3 -3
View File
@@ -4,12 +4,12 @@ ENV = 'development'
# VITE_APP_BASE_API_Url = 'https://aits.jlbank.com.cn:7001'
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
# VITE_APP_BASE_API_Url = 'http://192.168.247.200'
# VITE_APP_BASE_API_Url = 'http://aitscdn.jlbank.com.cn:7001'
# VITE_APP_BASE_API_Url = 'http://192.168.108.129'
# dev
#VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
# VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
# sit
# VITE_APP_BASE_API_Url = 'http://25.18.122.91:9786'
@@ -27,7 +27,7 @@ VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.65:7001'
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.91:9786'
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.78:9786'
VITE_APP_BASE_H5_API_Url_TRAAPP = 'http://192.168.247.200'
# VITE_APP_BASE_H5_API_Url_TRAAPP = 'http://192.168.247.200'
# VITE_APP_BASE_H5_API_Url_TRASTUDY = 'http://25.64.32.154:9602'
#VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://192.168.247.200'
# VITE_APP_BASE_H5_API_Url_TRAASK = 'http://25.64.32.154:9605'
+1 -1
View File
@@ -35,7 +35,7 @@ export const get_base_url = (url = '') => {
}
// #endif
// #ifdef APP-PLUS
return 'https://aitstest.jlbank.com.cn:7002' || ENV.VITE_APP_BASE_API_Url
return 'https://aitstest.jlbank.com.cn:7001' || ENV.VITE_APP_BASE_API_Url
// #endif
} else { // 其他环境,包括生产环境,sit环境,uat环境
+8
View File
@@ -41,4 +41,12 @@ export const queryTraPartnerCharacterInfoList = (data) => {
method: 'post',
data
});
};
// 陪练角色详情 + 对话详情 /traPartnerCharacterInfo/queryTraPartnerCharacterInfoById
export const queryTraPartnerCharacterInfoById = (data) => {
return request({
url: base_url + '/traPartnerCharacterInfo/queryTraPartnerCharacterInfoById',
method: 'post',
data
});
};
+60
View File
@@ -1,3 +1,4 @@
import { get_base_url } from '@/api/request';
// #ifdef APP-PLUS
import showDialog from '@/common/dialogMessage'
// #endif
@@ -277,6 +278,65 @@ export function setupKeyboardHeightListener(updateCallback) {
}
export const previewFileFnc = ({fileId, getFileSrc, fileName}) => {
let userInfo = getUserInfo()
let userParams = '/' + (userInfo?.loginName??'')
if (getPhoneEnvBool('AND')) {
common.navigateTo('/pages/index/preview?fileId=' + fileId +'&fileApiSrc=' + getFileSrc);
} else {
if (isDownloading.value) return
const base_url = get_base_url(getFileSrc);
let fileUrl = base_url + getFileSrc + fileId + userParams;
const localFilePath = '_doc/pdf_view/' + fileName + '.pdf';
isDownloading.value = true
downloadFileFnc(fileUrl, localFilePath).then((res) => {
uni.openDocument({
filePath: res,
showMenu: true,
success: function() {
console.log('打开文档成功');
}
})
}).finally(() => {
setTimeout(() => {
isDownloading.value = false
}, 1000)
});
}
}
const downloadFileFnc = async (fileUrl, localFilePath) => {
try {
//#ifdef APP-PLUS
return new Promise((resolve, reject) => {
console.log('开始下载:', fileUrl);
const dtask = plus.downloader.createDownload(
fileUrl, {
filename: localFilePath
},
(d, status) => {
if (status === 200) {
console.log('保存路径:', d.filename);
resolve(d.filename);
} else {
console.error('文件下载失败:', status);
reject(new Error(`下载失败,状态码: ${status}`));
}
}
);
dtask.start();
});
// #endif
//#ifdef H5
return new Promise((resolve, reject) => {
resolve();
});
// #endif
} catch (error) {
console.error('创建目录失败:', error);
throw error;
}
};
export default {
getPageCache,
+8 -4
View File
@@ -26,6 +26,7 @@ export default {
data() {
return {
fileId: '',
fileApiSrc: '/traask/traFilePreview/',
showUrl: '',
// viewerUrl: '/static/pdf-view/web/viewer.html',
viewerUrl: '/static/view-pdf/pdf.html',
@@ -36,6 +37,9 @@ export default {
},
onLoad(params) {
this.fileId = params.fileId || ''
if(params.fileApiSrc) {
this.fileApiSrc = params.fileApiSrc
}
const userInfo = getUserInfo();
this.userParams = '/' + (userInfo?.loginName??'')
},
@@ -45,8 +49,8 @@ export default {
fileId: this.fileId
})
}else{
const base_url = get_base_url('/traask/traFilePreview/');
let fileUrl = base_url + '/traask/traFilePreview/' + this.fileId + this.userParams;
const base_url = get_base_url(this.fileApiSrc);
let fileUrl = base_url + this.fileApiSrc + this.fileId + this.userParams;
this.showUrl = fileUrl
console.log(this.showUrl,111)
}
@@ -77,9 +81,9 @@ export default {
common.navigateBack()
},
openPdfFn(data) {
const base_url = get_base_url('/traask/traFilePreview/')
const base_url = get_base_url(this.fileApiSrc)
let _id = data?.fileId
let fileUrl = base_url + '/traask/traFilePreview/' + _id + this.userParams
let fileUrl = base_url + this.fileApiSrc + _id + this.userParams
this.showUrl = this.viewerUrl + '?url=' + fileUrl
console.log(this.showUrl)
this.showView = true
+3 -1
View File
@@ -68,6 +68,7 @@
} from "@/api/sparring.js"
import CardSwiper from './components/card-swiper.vue';
const traId = ref('')
const talkingType = ref('')
const detailInfo = ref({})
const roleList = ref([])
const currentIndex = ref(0)
@@ -92,10 +93,11 @@
}
const toTip = () => {
common.navigateTo('/pages/sparring/tip?traId=' + traId.value + '&roleId=' + activeRole.value.chaId)
common.navigateTo('/pages/sparring/tip?traId=' + traId.value + '&chaId=' + activeRole.value.chaId + '&type=' + talkingType.value)
}
onLoad((params) => {
traId.value = params.traId;
talkingType.value = params.type
});
onMounted(() => {
getDetailInfo()
@@ -22,7 +22,7 @@
<ImagePreview class="img-box" :src="itemInfo.ossAddr" :isCache="true" width="225" height="170" mode="scaleToFill">
</ImagePreview>
<view class="item-title">{{itemInfo.traName}}</view>
<scroll-view class="item-tags" :scroll-x="true">
<scroll-view class="item-tags" :scroll-x="true" :show-scrollbar="false">
<view class="item-tag-view">
<view :class="['item-tag', this.activeTag.includes(item.tagId)?'active-tag':'']" v-for="item in tagsShowList">
{{item.tagName}}</view>
@@ -70,9 +70,9 @@
},
computed: {
tagsShowList() {
if (!this.activeTag) return this.itemInfo.tagCataList || []
let _arr = (this.itemInfo.tagCataList || []).filter(t => this.activeTag.includes(t.tagId))
let _arr1 = (this.itemInfo.tagCataList || []).filter(t => !this.activeTag.includes(t.tagId))
if (!this.activeTag) return this.itemInfo.traPartnerTagList || []
let _arr = (this.itemInfo.traPartnerTagList || []).filter(t => this.activeTag.includes(t.tagId))
let _arr1 = (this.itemInfo.traPartnerTagList || []).filter(t => !this.activeTag.includes(t.tagId))
return _arr.concat(_arr1)
}
},
@@ -86,7 +86,7 @@
<style lang="scss" scoped>
.sparring-info {
padding:12rpx 15rpx 8rpx;
padding:0rpx 15rpx 24rpx;
overflow: hidden;
.item-title {
white-space: nowrap;
+519
View File
@@ -0,0 +1,519 @@
<template>
<view class="talk-btns" ref="talkBtnRef">
<view class="touch-btn" @touchstart="getRecordPermission" @touchend="hideOverlayTalking" @touchmove="handleMove">
<view class="center-text">
<image class="type-icon" src="@/static/images/sparring/talk-icon.png"></image>
{{'语音对话'}}
</view>
</view>
<uni-popup ref="talkModelRef" type="bottom" @click="showTalkingModel = false"
mask-background-color="rgba(0,0,0,0.9)">
<view class="overlay-box">
<view class="circle-bg-box">
<view class="icon-box">
<image src="/src/static/images/course/talk-icon.png" mode=""></image>
</view>
</view>
<view class="tips-box">松开发送</view>
<view class="btns-box">
<view :class="['close-btn', isOut ? 'is-out' : '']">
取消
<image class="btn-box" v-if="!isOut" src="/src/static/images/course/close.png" mode=""></image>
<image class="btn-box" v-else src="/src/static/images/course/close-active.png" mode=""></image>
</view>
</view>
<view :class="['talk-dialog', isOut ? 'is-out' : '']">
<image src="@/static/images/course/voice-white.gif" mode="" class="gif-box"></image>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import {
toRefs,
ref,
computed,
unref,
onMounted,
onUnmounted,
nextTick,
watch,
getCurrentInstance
} from 'vue';
import common from '../../../common/common';
import permission from '@/common/permission.js';
import {
getPhoneEnvBool
} from '@/common/common.js';
import {
useStorageStore
} from '@/store/storage.js';
const storageStore = useStorageStore();
const proxy = getCurrentInstance();
const emit = defineEmits(['uploadVoice', 'startRecord']);
const props = defineProps({
isDisabled: {
default: true,
type: Boolean
},
isLoading: {
default: false,
type: Boolean
},
loadingText: {
default: '发送回答中,请稍后再试!',
type: String
},
formatType: {
default: 'mp3', // 生成语音文件类型
type: String
},
});
const {
isDisabled,
onlyVoice,
formatType
} = toRefs(props);
const isLoadingState = computed(() => props.isLoading);
const showTalkingModel = ref(false);
const keyboardIsShow = ref(false);
const talkModelRef = ref();
storageStore.setTouchBtnZIndex(12);
const btnZindex = computed(() => storageStore.getTouchBtnZIndex);
watch(
() => showTalkingModel.value,
(val) => (val ? talkModelRef.value.open() : talkModelRef.value.close())
);
watch(
() => props.isLoading,
() => {}, {
deep: true,
immediate: true
}
);
watch(
() => props.isDisabled,
(val) => {
if (val) {
keyboardIsShow.value = false;
}
}, {
deep: true,
immediate: true
}
);
const answerValue = ref('');
const talkBtnRef = ref();
// 录音
const audioPath = ref('');
const audioObj = uni.createInnerAudioContext();
//#ifdef APP-PLUS
// app端
const recordObjCom = ref({});
const IsAndEnv = getPhoneEnvBool('AND');
const isStartNum = ref(0);
const isStopNum = ref(0);
const initRecord = () => {
recordObjCom.value = uni.getRecorderManager();
unref(recordObjCom)?.onStop((res, duration) => {
audioPath.value = res.tempFilePath;
if (!isOut.value) {
if (startFlag.value !== 2) {
closeModel(() => {
nextTick(() => {
common.msg('说话时间太短,没有听清!');
});
});
} else {
closeModel();
emit('uploadVoice', audioPath.value);
}
} else {
closeModel();
}
});
unref(recordObjCom).onStart(() => {
startFlag.value = 1;
setTimeout(() => {
startFlag.value = 2;
}, 1000);
let _recordPerm = uni.getAppAuthorizeSetting();
let _state = _recordPerm.microphoneAuthorized;
if (_state === 'authorized') {
if (isStopNum.value === isStartNum.value) {
stopRecord();
} else {
showTalkingModel.value = true;
}
}
});
};
const startFlag = ref(1);
const startRecord = () => {
isStartNum.value += 1;
unref(recordObjCom).start({
format: formatType.value
});
};
const stopRecord = () => {
isStopNum.value = isStartNum.value;
try {
if (unref(recordObjCom).stop) {
unref(recordObjCom).stop();
} else {
closeModel();
}
} catch {
closeModel();
}
};
const closeModel = (cb = () => {}) => {
isStopNum.value = isStartNum.value;
setTimeout(() => {
showTalkingModel.value = false;
cb && cb();
}, 350);
setTimeout(() => {
isTalking.value = false;
}, 800);
};
// #endif
//#ifndef APP-PLUS
const recordObjCom = ref({});
const initRecord = () => {};
// #endif
onMounted(async () => {
initRecord?.();
});
// 录音结束
// 记录 按下移动位置
const touchX = ref(0);
const touchY = ref(0);
const baseY = ref(0);
const windowInfo = uni.getWindowInfo();
// 计算是否超出按下盒子位置
const isOut = computed(() => {
let _btnHeight = (windowInfo.screenWidth / 750) * 234;
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);
const hasPermission = ref(false)
const getRecordPermission = (obj) => {
if (isDisabled.value) return;
if (isTalking.value) {
common.msg('操作频繁,请稍后重试!');
setTimeout(() => {
isTalking.value = false;
}, 500);
return;
}
if (!hasPermission.value) {
let _res = getPermission()
if (_res) {
hasPermission.value = true
showOverlayTalking(obj);
}
} else {
showOverlayTalking(obj);
}
};
const getPermission = async () => {
//#ifdef APP-PLUS
// 安卓端
if (IsAndEnv) {
let _recordPerm = await permission.requestAndroidPermission('android.permission.RECORD_AUDIO');
if (_recordPerm !== 1) {
common.show('提示信息', '当前页面需要使用的录音权限,是否前往开启?', true, '取消', '确定').then((res) => {
if (res) {
// 去开启权限
permission.gotoAppPermissionSetting();
} else {
// 取消
common.navigateBack();
}
});
return false
} else {
return true
}
} else {
let _recordPerm = uni.getAppAuthorizeSetting();
let _state = _recordPerm.microphoneAuthorized;
if (_state === 'denied') {
common.show('提示信息', '当前页面需要使用的录音权限,是否前往开启?', true, '取消', '确定').then((res) => {
if (res) {
// 去开启权限
uni.openAppAuthorizeSetting();
} else {
// 取消
common.navigateBack();
}
});
return false
} else if (_state === 'not determined') {
// startRecord()
return true
}
}
// #endif
//#ifndef APP-PLUS
return true
// #endif
}
const isMoving = ref(false);
const isTalking = ref(false);
const showOverlayTalking = (obj) => {
if (showTalkingModel.value) return;
if (isLoadingState.value) {
uni.showToast({
title: props.loadingText,
icon: 'none',
duration: 1000
});
return;
}
isMoving.value = true;
isTalking.value = true;
emit('startRecord');
recNum.value += 1;
let _recnum = recNum.value
if (!isDisabled.value) {
touchX.value = obj.changedTouches[0].pageX;
touchY.value = obj.changedTouches[0].pageY;
baseY.value = obj.currentTarget.offsetTop;
//#ifdef APP-PLUS
// canStart.value = false
startRecord();
// #endif
setTimeout(() => {
console.log(_recnum, recNum.value)
if (_recnum === recNum.value) {
hideOverlayTalking(recNum.value);
}
}, 59 * 1010);
} else {
showTalkingModel.value = false;
uni.showToast({
title: '暂无问题需要回答!',
icon: 'none',
duration: 1000
});
}
};
const hideOverlayTalking = (obj) => {
isMoving.value = false;
if (!showTalkingModel.value) return;
//#ifdef APP-PLUS
stopRecord();
// #endif
};
const handleMove = (obj) => {
if (isMoving.value) {
touchX.value = obj.changedTouches[0].pageX;
touchY.value = obj.changedTouches[0].pageY;
if (outScreen.value) {
hideOverlayTalking(obj);
}
}
};
</script>
<style lang="scss" scoped>
.talk-btns {
height: 150rpx;
background-color: #fff;
padding: 25rpx 30rpx 0;
position: relative;
z-index: v-bind(btnZindex);
.touch-btn {
height: 92rpx;
// box-shadow: 0 8rpx 20rpx 0 rgba(0, 0, 0, 0.06);
text-align: center;
line-height: 92rpx;
background-color: #06f;
font-weight: 400;
position: relative;
;
color: #fff;
font-size: 29rpx;
border-radius: 23rpx;
.center-text {
height: 35rpx;
line-height: 35rpx;
width: 100%;
position: absolute;
top: 50%;
transform: translateY(-50%);
display: flex;
justify-content: center;
}
.type-icon {
width: 35rpx;
height: 35rpx;
margin-right: 10rpx;
}
}
}
.overlay-box {
display: flex;
flex-direction: column-reverse;
// justify-content: flex-end;
width: 100%;
height: 100%;
.circle-bg-box {
height: 234rpx;
background: url(@/static/images/course/talking.png) no-repeat;
background-size: cover;
position: relative;
.icon-box {
position: absolute;
left: 50%;
top: 50%;
margin-left: -17rpx;
margin-top: -24rpx;
width: 34rpx;
height: 48rpx;
image {
width: 100%;
height: 100%;
}
}
}
.tips-box {
height: 40rpx;
line-height: 40rpx;
color: #c0c0c0;
text-align: center;
margin: 12rpx 0 24rpx;
}
.btns-box {
height: 270rpx;
// padding: 0 120rpx;
width: 154rpx;
margin: 0 auto 100rpx;
overflow: hidden;
font-size: 28rpx;
text-align: center;
line-height: 116rpx;
.close-btn {
float: left;
height: 100%;
width: 154rpx;
overflow: hidden;
color: #999;
&.is-out {
color: #fff;
}
}
.translate-btn {
float: right;
height: 100%;
width: 154rpx;
overflow: hidden;
color: #999;
}
.btn-box {
width: 100%;
height: 154rpx;
}
}
.talk-dialog {
width: 506rpx;
height: 234rpx;
margin: 0 auto;
background-color: #06f;
border-radius: 32rpx;
position: relative;
&.is-out {
background-color: #ff3e49;
&::after {
background-color: #ff3e49;
}
}
.gif-box {
position: absolute;
width: 200rpx;
height: 80rpx;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
&::after {
content: '';
width: 44rpx;
height: 44rpx;
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
background-color: #06f;
overflow: hidden;
position: absolute;
bottom: -22rpx;
left: 50%;
margin-left: -22rpx;
}
}
.white-bg-box {
height: 365rpx;
background-color: #fff;
overflow: hidden;
border-radius: 32rpx 32rpx 0 0;
padding: 58rpx 36rpx;
.box-title {
font-size: 32rpx;
font-weight: bold;
line-height: 46rpx;
margin-bottom: 46rpx;
}
.box-btns {
height: 46rpx;
line-height: 46rpx;
display: flex;
justify-content: space-between;
color: #999;
.is-active {
color: #06f;
}
}
}
}
::v-deep .uv-input__content__field-wrapper__field {
padding-right: 60rpx;
}
</style>
+50 -115
View File
@@ -12,21 +12,15 @@
<view class="body-top">
<view class="body-top-title">
{{detailInfo.traName}}
</view>
<view class="body-top-item">
已练习次数{{detailInfo.practiceTimes}}
</view>
<view class="body-top-item">
类型{{detailInfo.traInterTypName}}
</view>
<view class="body-top-item">
截止时间{{detailInfo.endTm}}
{{ detailInfo.traName }}
</view>
<view class="body-top-item"> 已练习次数{{ detailInfo.practiceTimes }} </view>
<view class="body-top-item"> 类型{{ detailInfo.traInterTypDesc }} </view>
<view class="body-top-item"> 截止时间{{ detailInfo.endTm }} </view>
<scroll-view class="body-top-tags" :scroll-x="true">
<view class="item-tag-view">
<view class="item-tag active-tag" v-for="item in tagsShowList">
{{item.tagName}}
<view class="item-tag active-tag" v-for="item in detailInfo.traPartnerTagList || []">
{{ item.tagName }}
</view>
</view>
</scroll-view>
@@ -37,7 +31,7 @@
陪练介绍
</view>
<view class="body-item-detail">
{{detailInfo.introduction}}
{{ detailInfo.introduction }}
</view>
</view>
<view class="body-item">
@@ -46,7 +40,7 @@
陪练目标
</view>
<view class="body-item-detail">
{{detailInfo.traTarget}}
{{ detailInfo.traTarget }}
</view>
</view>
<view class="body-item">
@@ -54,19 +48,16 @@
<image class="title-icon" src="@/static/images/sparring/detail-icon-3.png"></image>
陪练预计时长
</view>
<view class="body-item-detail">
{{detailInfo.traStartLimit+ ' ~ ' + detailInfo.traEndLimit}} 分钟
</view>
<view class="body-item-detail"> {{ detailInfo.traStartLimit + ' ~ ' + detailInfo.traEndLimit }} 分钟 </view>
</view>
<view class="body-item">
<view class="body-item" v-if="detailInfo.traPartnerDataVoList?.length > 0">
<view class="body-item-title">
<image class="title-icon" src="@/static/images/sparring/detail-icon-4.png"></image>
陪练参考资料
</view>
<view class="body-item-detail">
<view class="body-item-detail-file" v-for="item in 6" @click="previewFile(item)">
文件名称{{item}}.docx
</view>
<view class="body-item-detail-file" v-for="item in detailInfo.traPartnerDataVoList || []"
@click="previewFile(item)"> {{ item.dataName }}.{{ item.dataSuffix }} </view>
</view>
</view>
<view class="body-bottom">
@@ -86,111 +77,55 @@
onShow,
onLoad,
onUnload
} from '@dcloudio/uni-app';
import common from '@/common/common';
import ImagePreview from '@/components/image-preview/image-preview.vue';
} from '@dcloudio/uni-app'
import common from '@/common/common'
import ImagePreview from '@/components/image-preview/image-preview.vue'
import {
queryTraPartnerInfoById
} from "@/api/sparring.js"
} from '@/api/sparring.js'
import {
getPhoneEnvBool
} from '@/common/common';
const traId = ref('')
const detailInfo = ref({})
const isDownloading = ref(false)
const tagsShowList = ref([{
tagName: '公金业务'
}, {
tagName: '初级'
}, {
tagName: '销售类'
}, {
tagName: '公金业务'
}, {
tagName: '初级'
}, {
tagName: '销售类'
}, {
tagName: '公金业务'
}, {
tagName: '初级'
}, {
tagName: '销售类'
}])
previewFileFnc
} from '@/common/common'
const traId = ref('')
const detailInfo = ref({
traPartnerDataVoList: []
})
const isLoading = ref(false)
const getDetailInfo = () => {
if (isLoading.value) return
isLoading.value = true
queryTraPartnerInfoById({
id: traId.value
}).then(res => {
console.log(res)
detailInfo.value = res.body || {}
traId: traId.value
})
.then(res => {
detailInfo.value = res.body || {}
})
.catch(err => {
console.log(err)
}).finally(() => {
isLoading.value = false
})
}
const previewFile = info => {
previewFileFnc({
fileId: info.dataId,
getFileSrc: '/trapractice/traFilePreview/',
fileName: info.dataName
})
}
const previewFile = (info) => {
if (getPhoneEnvBool('AND')) {
common.navigateTo('/pages/index/preview?fileId=' + info.docId);
} else {
if (isDownloading.value) return
const base_url = get_base_url('/traask/traFilePreview/');
let fileUrl = base_url + '/traask/traFilePreview/' + info.docId + userParams;
const localFilePath = '_doc/pdf_view/' + info.docName + '.pdf';
isDownloading.value = true
downloadFileFnc(fileUrl, localFilePath).then((res) => {
uni.openDocument({
filePath: res,
showMenu: true,
success: function() {
console.log('打开文档成功');
}
})
}).finally(() => {
setTimeout(() => {
isDownloading.value = false
}, 1000)
});
}
}
const downloadFileFnc = async (fileUrl, localFilePath) => {
try {
//#ifdef APP-PLUS
return new Promise((resolve, reject) => {
console.log('开始下载:', fileUrl);
const dtask = plus.downloader.createDownload(
fileUrl, {
filename: localFilePath
},
(d, status) => {
if (status === 200) {
console.log('保存路径:', d.filename);
resolve(d.filename);
} else {
console.error('文件下载失败:', status);
reject(new Error(`下载失败,状态码: ${status}`));
}
}
);
dtask.start();
});
// #endif
//#ifdef H5
return new Promise((resolve, reject) => {
resolve();
});
// #endif
} catch (error) {
console.error('创建目录失败:', error);
throw error;
}
};
const toChooseRole = (type) => {
const toChooseRole = type => {
common.navigateTo('/pages/sparring/chooseRole?traId=' + traId.value + '&type=' + type)
}
onLoad((params) => {
traId.value = params.traId;
});
onLoad(params => {
traId.value = params.traId
})
onMounted(() => {
getDetailInfo()
})
onShow(() => {
getDetailInfo()
})
</script>
<style lang="scss" scoped>
@@ -230,7 +165,6 @@
margin: 0 auto;
border-radius: 23rpx;
overflow: hidden;
}
.top-title {
@@ -363,7 +297,8 @@
color: #06f;
font-size: 29rpx;
border-radius: 23rpx;
&:last-child{
&:last-child {
margin-left: 36rpx;
background: #06f;
color: #fff;
+2
View File
@@ -338,7 +338,9 @@
}
//
const toSparringDetail = item => {
if(item.traId){
common.navigateTo('/pages/sparring/detail?traId=' + item.traId)
}
}
onMounted(() => {
queryTraPartnerCatelogList().then(res => {
+105 -83
View File
@@ -14,66 +14,61 @@
<view class="body-content">
<view :class="['body-content-role', showRoleInfo?'':'hide-info']">
<view :class="['role-title']">
<ImagePreview class="role-title-avatar" :src="'/traoss/tras3/show/S3F0250181220722025110316545500000431780'" :isCache="true">
<ImagePreview class="role-title-avatar" :src="'/traoss/tras3/show/S3F0250181220722025110316545500000431780'"
:isCache="true">
</ImagePreview>
<view class="role-title-info" @click="showRoleInfo = !showRoleInfo">
<view class="arrow-r-icon"></view>
<view class="role-name card-title">
<text class="card-title-name">{{ '李想' }}</text>
<text class="card-title-age">{{ 128 }}</text>
<text class="card-title-img">
<image class="card-title-icon" v-if="gender === 'm'" src="@/static/images/me/man.png"></image>
<image class="card-title-icon" v-if="gender === 'f'" src="@/static/images/me/wuman.png"></image>
</text>
<text class="card-title-name">{{ detailInfo.chaName }}</text>
<text class="card-title-age">{{ detailInfo.chaAge }}</text>
<text class="card-title-img">
<image class="card-title-icon" v-if="detailInfo.gender === 'm'" src="@/static/images/me/man.png">
</image>
<image class="card-title-icon" v-if="detailInfo.gender === 'f'" src="@/static/images/me/wuman.png">
</image>
</text>
</view>
<view class="role-desc">预计时间 {{10}} ~ {{15}} 分钟</view>
<view class="role-desc">预计时间 {{detailInfo.traStartLimit}} ~ {{detailInfo.traEndLimit}} 分钟</view>
</view>
</view>
<view class="role-content">
<view class="role-content-item">
身份情况
<!-- {{ item.occupation }} -->
<!-- 身份情况 -->
{{ detailInfo.occupation }}
</view>
<view class="role-content-item">
性格特点
<!-- {{ item.personality }} -->
<!-- 性格特点 -->
{{ detailInfo.personality }}
</view>
<view class="role-content-item">
<!-- {{ item.thoughtProcess }} -->
提问思路
<!-- 提问思路 -->
{{ detailInfo.thoughtProcess }}
</view>
<view class="role-content-item">
<!-- {{ item.chaDesc }} -->
人物描述
<!-- 人物描述 -->
{{ detailInfo.chaDesc }}
</view>
</view>
</view>
<view class="body-scene">
<view class="body-scene-title">场景描述</view>
<!-- sceneDesc -->
{{ '陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称' }}
{{ detailInfo.sceneDesc }}
</view>
</view>
<view class="body-bottom">
<view class="body-bottom-btn" @click="toTip">
<!--
<image
class="type-icon"
src="@/static/images/sparring/video-icon.png"
v-if="itemInfo.traInterTyp === '03'"
></image>
<image
class="type-icon"
src="@/static/images/sparring/phone-icon.png"
v-if="itemInfo.traInterTyp === '02'"
></image>
v-if="itemInfo.traInterTyp === '01'"-->
<TouchBtn class="body-bottom" @uploadVoice="uploadRecordNow" @startRecord="changePlayItemId('')"
:isLoading="false" :isDisabled="false" />
<view class="body-bottom" v-if="false">
<view class="body-bottom-btn" @click="toTalking">
<view class="center-text">
<image
class="type-icon"
src="@/static/images/sparring/talk-icon.png"
></image>
{{'语音通话'}}
<image class="type-icon" src="@/static/images/sparring/video-icon.png"
v-if="detailInfo.traInterTyp === '03'"></image>
<image class="type-icon" src="@/static/images/sparring/phone-icon.png"
v-else-if="detailInfo.traInterTyp === '02'"></image>
<image v-else class="type-icon" src="@/static/images/sparring/talk-icon.png"></image>
{{detailInfo.traInterTypDesc}}
</view>
</view>
</view>
@@ -93,46 +88,51 @@
onUnload
} from '@dcloudio/uni-app';
import common from '@/common/common';
import TouchBtn from './components/touchBtn.vue';
import ImagePreview from '@/components/image-preview/image-preview.vue';
import {
queryTraPartnerCharacterInfoList
queryTraPartnerCharacterInfoById
} from "@/api/sparring.js"
import CardSwiper from './components/card-swiper.vue';
const traId = ref('')
const chaId = ref('')
const talkingType = ref('')
const detailInfo = ref({})
const roleList = ref([])
const currentIndex = ref(0)
const showRoleInfo = ref(false)
const showLoading = ref(true)
const gender = ref('m')
const activeRole = computed(()=>{
if(roleList.value.length>0){
return roleList.value[currentIndex.value]
}else{
return {}
}
})
const getDetailInfo = () => {
queryTraPartnerCharacterInfoList({
traId: traId.value
queryTraPartnerCharacterInfoById({
traId: traId.value,
chaId: chaId.value
}).then(res => {
roleList.value = res.body || []
setTimeout(()=> {
console.log(res)
detailInfo.value = {
...res.body
}
setTimeout(() => {
showLoading.value = false
}, Math.random() * 1000 + 500)
})
}
const onSwiperChange = (data) => {
console.log('轮播图切换:', e);
currentIndex.value = data.index;
const uploadRecordNow = (voicePath) => {
console.log(voicePath)
}
const toTip = () => {
common.navigateTo('/pages/sparring/tip?traId=' + traId.value + '&roleId=' + activeRole.value.chaId)
const toTalking = () => {
common.navigateTo('/pages/sparring/chooseRole?traId=' + traId.value + '&chaId=' + chaId)
}
const activeVoiceTalkingItemId = ref('')
//
const changePlayItemId = (id = '') => {
activeVoiceTalkingItemId.value = id
}
onLoad((params) => {
traId.value = params.traId;
chaId.value = params.chaId;
talkingType.value = params.type;
});
onMounted(() => {
getDetailInfo()
@@ -147,14 +147,16 @@
flex-direction: column;
background-image: url(@/static/images/sparring/bg.png);
background-size: cover;
// background-color: #f2f6fd;
.sparring-detail-loading{
.sparring-detail-loading {
position: fixed;
z-index: 20;
height: 100vh;
width: 100vw;
background-image: url(@/static/images/sparring/bg.png);
.loading-img{
.loading-img {
position: absolute;
width: 380rpx;
height: 590rpx;
@@ -170,6 +172,7 @@
background-size: cover;
}
}
.sparring-detail-body {
position: absolute;
overflow-x: hidden;
@@ -205,6 +208,7 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
// margin-bottom: 23rpx;
.arrow-icon {
position: absolute;
@@ -242,30 +246,35 @@
}
}
.body-content{
.body-content {
height: 100%;
padding: 10rpx 24rpx 20rpx;
box-sizing: border-box;
overflow-y: auto;
.body-content-role{
.body-content-role {
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
margin-bottom: 30rpx;
&.hide-info{
&.hide-info {
.arrow-r-icon::after {
transform: rotate(225deg) !important;
}
.role-content{
.role-content {
max-height: 0rpx !important;
padding-bottom: 0rpx !important;
}
}
.role-title{
.role-title {
height: 154rpx;
position: relative;
padding-left: 180rpx;
&-avatar{
&-avatar {
width: 92rpx;
height: 92rpx;
position: absolute;
@@ -275,14 +284,16 @@
border-radius: 50%;
overflow: hidden;
}
&-info{
&-info {
position: relative;
height: 100%;
box-sizing: border-box;
padding-top: 34rpx;
.role-name{
.role-name {
width: 100%;
&.card-title {
display: flex;
font-size: 29rpx;
@@ -293,33 +304,38 @@
text-align: left;
justify-content: start;
margin-bottom: 8rpx;
.card-title-name{
.card-title-name {
margin-right: 20rpx;
line-height: 44rpx;
}
.card-title-age{
.card-title-age {
margin-right: 20rpx;
line-height: 44rpx;
}
.card-title-img{
.card-title-img {
height: 44rpx;
width: 30rpx;
padding: 5rpx 0;
box-sizing: border-box;
}
.card-title-icon{
.card-title-icon {
height: 30rpx;
width: 30rpx;
}
}
}
.role-desc{
.role-desc {
width: 100%;
font-size: 25rpx;
color: #666;
}
.arrow-r-icon {
position: absolute;
width: 36rpx;
@@ -329,14 +345,14 @@
top: 50%;
transform: translateY(-50%);
}
.arrow-r-icon::before,
.arrow-r-icon::after {
content: '';
position: absolute;
transition: all 0.3s ease;
}
.arrow-r-icon::after {
top: 25%;
left: 25%;
@@ -348,16 +364,19 @@
}
}
}
.role-content{
.role-content {
max-height: 800rpx;
transition: all 0.3s ease;
padding-bottom: 12rpx;
&-item{
&-item {
position: relative;
padding: 8rpx 46rpx 8rpx 76rpx;
line-height: 42rpx;
font-size: 29rpx;
&::before{
&::before {
content: '';
display: block;
background-color: #000;
@@ -371,17 +390,18 @@
}
}
}
.body-scene{
.body-scene {
background: linear-gradient(209deg, #fefeff 0%, #d7dfff 100%);
border: 2rpx solid rgba(255, 255, 255, .65);
border: 2rpx solid rgba(255, 255, 255, .65);
position: relative;
line-height: 42rpx;
font-size: 29rpx;
border-radius: 16rpx;
overflow: hidden;
padding: 108rpx 46rpx 30rpx;
.body-scene-title{
.body-scene-title {
position: absolute;
left: 0;
top: 0;
@@ -415,7 +435,8 @@
font-size: 29rpx;
border-radius: 23rpx;
position: relative;
.center-text{
.center-text {
height: 35rpx;
line-height: 35rpx;
width: 100%;
@@ -425,7 +446,8 @@
display: flex;
justify-content: center;
}
.type-icon{
.type-icon {
width: 35rpx;
height: 35rpx;
margin-right: 10rpx;