JLBA202505130001_关于吉林银行新建AI智能培训系统的需求_补充一个未提交的文件2

This commit is contained in:
田岩
2026-03-04 14:29:20 +08:00
7 changed files with 187 additions and 135 deletions
+10 -1
View File
@@ -4,11 +4,20 @@ ENV = 'development'
#VITE_APP_BASE_API_Url = 'https://aits.jlbank.com.cn:7001'
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
# VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
# VITE_APP_BASE_API_Url = 'https://aits.jlbank.com.cn:7001'
#VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
# VITE_APP_BASE_API_Url = 'http://192.168.247.200'
# VITE_APP_BASE_API_Url = 'http://aitscdn.jlbank.com.cn:7001'
# dev
+2 -2
View File
@@ -2,9 +2,9 @@
ENV = 'development.yh'
# 'development.yh'
VITE_APP_BASE_H5_API_Url = 'http://25.18.122.65:7001'
# 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.91:9786'
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.78:9786'
# VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
# VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://25.64.32.154:9604'
+97 -92
View File
@@ -1,12 +1,15 @@
<template>
<view class="item" :class="{'no-touch':qnsTyp === 'ES' && !isDisabled && ['examination', 'practice', 'mistake_in', 'pk_in'].includes(props.mode)}">
<view class="item"
:class="{'no-touch':qnsTyp === 'ES' && !isDisabled && ['examination', 'practice', 'mistake_in', 'pk_in'].includes(props.mode)}">
<view class="top">
<!-- v-if="!['study'].includes(props.mode)" -->
<view class="type" v-if="!['study_answer', 'study_record'].includes(props.mode)">{{ subject_type_text }}</view>
<view class="type" v-if="!['study_answer','study_result', 'study_record'].includes(props.mode)">
{{ subject_type_text }}</view>
<view class="score" v-if="['settlement', 'examination', 'pk_in'].includes(props.mode)">
{{ item.tgtGrade }}
</view>
<view class="score answerMethod" v-if="['study','examination', 'practice'].includes(props.mode) && qnsTyp === 'ES'">
<view class="score answerMethod"
v-if="['study','examination', 'practice'].includes(props.mode) && qnsTyp === 'ES'">
{{ answerMethod }}
</view>
<view class="fl1"></view>
@@ -14,18 +17,9 @@
<!-- 反馈中心 -->
<slot name="title-right"></slot>
<view class="mark_and_feedback_div" v-if="['examination', 'practice', 'mistake_in'].includes(props.mode)">
<image
v-if="props.mode === 'examination'"
:src="markIcon(props.item.mark ? '#e8b531' : '#ABABAB')"
class="img"
@click="mark_click()"
></image>
<image
:src="feedbackIcon()"
class="img"
@click="feedback_click()"
v-if="props.mode !== 'mistake_in'"
></image>
<image v-if="props.mode === 'examination'" :src="markIcon(props.item.mark ? '#e8b531' : '#ABABAB')"
class="img" @click="mark_click()"></image>
<image :src="feedbackIcon()" class="img" @click="feedback_click()" v-if="props.mode !== 'mistake_in'"></image>
</view>
<template v-if="['mistake'].includes(props.mode)">
<view class="expand_button_div" @click="toggleExpand()">
@@ -34,70 +28,43 @@
<view class="toggle-text" :class="{ active: !animationState }">展开</view>
<view class="toggle-text" :class="{ active: animationState }">收起</view>
</view>
<uv-icon
class="click_text_icon"
name="arrow-right"
:class="{ click_text_icon_action: animationState }"
color="#0066FF"
></uv-icon>
<uv-icon class="click_text_icon" name="arrow-right" :class="{ click_text_icon_action: animationState }"
color="#0066FF"></uv-icon>
</view>
</template>
</view>
</view>
<view class="question_text" v-if="!['study_answer', 'study_record'].includes(props.mode)">
<view class="question_text" v-if="!['study_answer', 'study_result', 'study_record'].includes(props.mode)">
{{ subject_data.qnsContent }}
</view>
<view :style="{ height: animationHeight }" :class="{ collapse_wrapper: props.mode === 'mistake' }" ref="wrapperRef">
<view class="collapse-content" ref="contentRef">
<view class="option_div" v-if="!['study_answer', 'study_record'].includes(props.mode)">
<view class="option_div" v-if="!['study_answer', 'study_result','study_record'].includes(props.mode)">
<template v-if="qnsTyp === 'ES'">
<questionVue
:status="subject_data.es_status"
:dataInfo="props.item?.my_answer"
:isPreview="props.isPreview"
:activeVoiceId="subject_data.qnsId"
@buutton_click="questionButtonClick"
:button_complete_show="['practice', 'pk_in', 'mistake_in'].includes(props.mode)"
></questionVue>
<questionVue :status="subject_data.es_status" :dataInfo="props.item?.my_answer" :isPreview="props.isPreview"
:activeVoiceId="subject_data.qnsId" @buutton_click="questionButtonClick"
:button_complete_show="['practice', 'pk_in', 'mistake_in'].includes(props.mode)"></questionVue>
</template>
<template v-else>
<radioSubjectItem
v-for="(analysisVo, index) in subject_data.itemVos"
@click_option="click_option"
:key="index"
:analysisVo="analysisVo"
:my_answer="item_answer"
></radioSubjectItem>
<radioSubjectItem v-for="(analysisVo, index) in subject_data.itemVos" @click_option="click_option"
:key="index" :analysisVo="analysisVo" :my_answer="item_answer"></radioSubjectItem>
</template>
</view>
<view
class="option_div_button"
v-if="
<view class="option_div_button" v-if="
qnsTyp === 'MU' &&
['practice', 'study', 'study_answer', 'study_record', 'pk_in', 'mistake_in'].includes(props.mode) &&
!props.isPreview &&
clearResult
"
>
<uv-button
type="primary"
class="send_evaluate"
:custom-style="customStyle"
:throttleTime="300"
@click="muBtnClick"
:hairline="false"
>
">
<uv-button type="primary" class="send_evaluate" :custom-style="customStyle" :throttleTime="300"
@click="muBtnClick" :hairline="false">
确定
</uv-button>
</view>
<view
class="analysis_div"
v-if="
<view :class="['analysis_div', props.mode]" v-if="
!clearResult &&
['study', 'study_answer', 'study_record', 'mistake', 'settlement', 'practice', 'mistake_in', 'practice_record', 'pk_in'].includes(props.mode)
"
>
['study', 'study_answer', 'study_result','study_record', 'mistake', 'settlement', 'practice', 'mistake_in', 'practice_record', 'pk_in'].includes(props.mode)
">
<view class="analysis_title" v-if="qnsTyp !== 'ES'">
<view class="success_answer">正确答案:{{ correctResultLabel.join('') }}</view>
<view class="your_answer">
@@ -105,23 +72,17 @@
<text :class="right_or_wrong_class ? 'success' : 'error'">{{ myResultLabel.join('') }}</text>
</view>
</view>
<view class="reference_answer" v-if="qnsTyp === 'ES' && !['study', 'study_answer', 'study_record'].includes(props.mode)">
<view class="reference_answer"
v-if="qnsTyp === 'ES' && !['study', 'study_answer', 'study_result', 'study_record'].includes(props.mode)">
<view class="reference_answer_title">你的答案:</view>
{{ subject_data.anserResult ?? '' }}
</view>
<esAnalysis
v-if="qnsTyp === 'ES'"
:value="subject_data.itemVos[0]"
:judgeResult="judgeResultStatus"
:anserResult="subject_data.anserResult"
></esAnalysis>
<esAnalysis v-if="qnsTyp === 'ES'" :value="subject_data.itemVos[0]" :judgeResult="judgeResultStatus"
:anserResult="subject_data.anserResult"></esAnalysis>
<!-- 问答题专属参考答案 -->
<referenceAnswer
v-if="qnsTyp === 'ES'"
:value="subject_data.itemVos[0]"
:anserResult="subject_data.itemVos[0]?.itemAnswer"
></referenceAnswer>
<referenceAnswer v-if="qnsTyp === 'ES'" :value="subject_data.itemVos[0]"
:anserResult="subject_data.itemVos[0]?.itemAnswer"></referenceAnswer>
<view class="analysis_note" v-if="props.mode !== 'pk_in'">
试题解析:{{ subject_data?.analyContent }}
</view>
@@ -131,15 +92,30 @@
</view>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick, computed, getCurrentInstance, watch } from 'vue';
import {
ref,
reactive,
onMounted,
nextTick,
computed,
getCurrentInstance,
watch
} from 'vue';
import radioSubjectItem from './radio-subject-item.vue';
import esAnalysis from './es-analysis.vue';
import referenceAnswer from './reference-answer.vue';
import questionVue from './question.vue';
import { markIcon, feedbackIcon } from '@/common/imgSvg';
import { SUBJECT_TYPE } from '@/enum.js';
import {
markIcon,
feedbackIcon
} from '@/common/imgSvg';
import {
SUBJECT_TYPE
} from '@/enum.js';
import common from '@/common/common';
import {ANSWER_METHOD_MAP} from '@/enum'
import {
ANSWER_METHOD_MAP
} from '@/enum'
// 多选的确定按钮的样式
const customStyle = {
borderRadius: '16rpx',
@@ -147,7 +123,7 @@
backgroundColor: '#0066FF',
border: '1rpx solid rgb(12, 123, 245)'
};
const answerMethod = computed(() => ANSWER_METHOD_MAP[props.item?.answerMethod || ''] || '');
const emit = defineEmits(['subject_click', 'update:modelValue']);
const props = defineProps({
@@ -170,7 +146,9 @@
// 学习: study
// 考试结算页 settlement
// 练习记录页 practice_record, 'practice_record'
return ['examination', 'practice', 'mistake', 'mistake_in', 'study', 'study_answer', 'study_record', 'settlement', 'practice_record', 'pk_in'].includes(
return ['examination', 'practice', 'mistake', 'mistake_in', 'study', 'study_answer', 'study_record',
'settlement', 'practice_record', 'pk_in'
].includes(
value
);
}
@@ -213,8 +191,9 @@
(newVal) => {
// console.log('modelValue', newVal);
item_answer.value = typeof newVal === 'string' ? newVal.split(',') : newVal;
},
{ deep: true }
}, {
deep: true
}
);
const feedback_click = () => {
@@ -262,14 +241,15 @@
const myResultLabel = computed(() => {
if (!props.item.anserResult) return ['无'];
const anserResultArray = props.item.anserResult.split(',');
return subject_data.value.itemVos.filter((res) => anserResultArray.includes(res.itemId)).map((res) => res.itemNo);
return subject_data.value.itemVos.filter((res) => anserResultArray.includes(res.itemId)).map((res) => res
.itemNo);
});
// 判断选项是否完全正确
const right_or_wrong_class = computed(
() =>
correctResultLabel.value.length === myResultLabel.value.length &&
myResultLabel.value.every((res) => correctResultLabel.value.includes(res))
correctResultLabel.value.length === myResultLabel.value.length &&
myResultLabel.value.every((res) => correctResultLabel.value.includes(res))
);
const subject_data = computed({
@@ -317,7 +297,7 @@
if (item_answer.value.includes(analysisVo.itemId)) {
item_answer.value = item_answer.value.filter((id) => id !== analysisVo.itemId);
modelValue.value = item_answer.value;
if (!['study','study_answer', 'study_record'].includes(props.mode)) {
if (!['study', 'study_answer', 'study_record'].includes(props.mode)) {
muCheckbox('choose');
}
} else if (props.item.qnsTyp === 'SN') {
@@ -331,7 +311,7 @@
// 多选直接插入
item_answer.value.push(analysisVo.itemId);
modelValue.value = item_answer.value;
if (!['study','study_answer', 'study_record'].includes(props.mode)) {
if (!['study', 'study_answer', 'study_record'].includes(props.mode)) {
muCheckbox('choose');
}
} else if (props.item.qnsTyp === 'JD') {
@@ -370,6 +350,7 @@
// display: inline-block;
// cursor: pointer;
}
.toggle-container {
position: relative;
height: 42rpx;
@@ -377,6 +358,7 @@
font-size: 30rpx;
line-height: 42rpx;
}
.toggle-text {
/* 绝对定位使文本重叠 */
position: absolute;
@@ -394,17 +376,21 @@
.toggle-text.active {
opacity: 1;
transform: translateY(0);
z-index: 1; /* 确保激活文本在上方 */
z-index: 1;
/* 确保激活文本在上方 */
}
.collapse_wrapper {
overflow: hidden; /* 关键隐藏超出部分 */
overflow: hidden;
/* 关键隐藏超出部分 */
// height: 0; /* 初始高度为0 */
transition: height 0.3s ease-out;
}
.click_text_icon_action {
transform: rotate(-90deg);
}
.click_text_icon {
transition: transform 0.3s ease-out;
}
@@ -422,6 +408,7 @@
padding: 4rpx;
}
}
.expand_button_div {
display: flex;
align-items: center;
@@ -429,9 +416,11 @@
justify-content: space-between;
}
.option_div {
padding-top: 20rpx;
}
.option_div_button {
width: 100%;
height: 84rpx;
@@ -441,20 +430,27 @@
flex-direction: column;
align-items: center;
}
.analysis_div {
&.study_result {
.analysis_title {
margin-top: 0;
}
}
.analysis_title {
display: flex;
justify-content: space-between;
align-items: center;
margin: 32rpx 0 0 0;
.success_answer {
}
.success_answer {}
.your_answer {
.success {
color: #15b859;
}
.error {
color: #f5212d;
}
@@ -471,8 +467,10 @@
font-style: normal;
margin-top: 28rpx;
}
.reference_answer {
white-space: pre-line;
.reference_answer_title {
font-weight: 700;
font-size: 30rpx;
@@ -481,6 +479,7 @@
line-height: 30rpx;
margin: 30rpx 0;
}
font-weight: 400;
font-size: 30rpx;
color: #666666;
@@ -488,15 +487,20 @@
text-align: left;
}
}
.item {
// height: 170rpx;
overflow: hidden;
display: flex;
flex-direction: column;
padding-bottom: 40rpx;
user-select: none !important; /* 禁用文本选择 */
-webkit-user-select: none !important; /* iOS兼容 */
-webkit-touch-callout: none !important; /* 禁用iOS长按菜单 */
user-select: none !important;
/* 禁用文本选择 */
-webkit-user-select: none !important;
/* iOS兼容 */
-webkit-touch-callout: none !important;
/* 禁用iOS长按菜单 */
.top {
display: flex;
align-items: center;
@@ -521,7 +525,8 @@
color: #333333;
text-align: left;
}
.answerMethod{
.answerMethod {
font-size: 22rpx;
font-family: ArialMT;
color: #999;
@@ -555,4 +560,4 @@
margin-top: 20rpx;
}
}
</style>
</style>
+63 -31
View File
@@ -6,12 +6,24 @@
</view>
<view class="overlay-box" @click="playEnded" @touchmove.stop v-if="fileType === 'image'">
<movable-area scale-area class="detail-movable">
<movable-view class="detail-movable-view" :out-of-bounds="true" direction="all" @scale="onScale" :scale="true" scale-min="0.5"
scale-max="4" :scale-value="scale">
<movable-view class="detail-movable-view" :out-of-bounds="true" direction="all" @scale="onScale" :scale="true"
scale-min="0.5" scale-max="4" :scale-value="scale">
<image class="detail-img" :src="fileUrlShow" mode="aspectFit"></image>
</movable-view>
</movable-area>
</view>
<view class="overlay-box" @click="playEnded" @touchmove.stop v-if="fileType === 'imageList'">
<swiper class="stack-swiper" :current="currentIndex" previous-margin="0" next-margin="0" :autoplay="false">
<swiper-item v-for="(item, index) in srcList" :key="index" class="swiper-item">
<movable-area scale-area class="detail-movable">
<movable-view class="detail-movable-view" :out-of-bounds="true" direction="all" @scale="onScale"
:scale="true" scale-min="0.5" scale-max="4" :scale-value="scale">
<image class="detail-img" :src="initSrc(item)" mode="aspectFit"></image>
</movable-view>
</movable-area>
</swiper-item>
</swiper>
</view>
</uni-popup>
</template>
@@ -56,7 +68,8 @@
watching: false,
videoContext: null,
base_url: get_base_url(),
scale: 1
scale: 1,
currentIndex: 0
};
},
computed: {
@@ -79,44 +92,21 @@
src() {
return this.storageStore?.getFileSrc
},
srcList() {
return this.storageStore?.getFileSrcList
}
},
watch: {
fileId: {
handler(newval, oldval) {
if (this.fileType === 'image') {
if (newval.indexOf('/') === 0) {
this.fileUrlShow = this.base_url + newval + '?tk=' + getToken();
return;
}
if (newval.indexOf('http') === 0) {
this.fileUrlShow = newval + '?tk=' + getToken();
return;
}
if (newval && this.watching) {
this.getImagePreviewUrlBlob(newval);
}
}
if (this.fileType === 'video') {
if (newval.indexOf('http') === 0) {
this.fileUrlShow = newval + '?tk=' + getToken()
} else {
if (newval && this.watching) {
this.getPreviewUrlBlob(newval);
}
}
}
this.videoContext = uni.createVideoContext(`video` + this.fileId)
this.initFileId(newval, oldval)
},
immediate: true,
deep: true,
},
src: {
handler(newval, oldval) {
if (newval.indexOf('http') === 0) {
this.fileUrlShow = newval + '?tk=' + getToken()
} else {
this.fileUrlShow = this.base_url + newval + '?tk=' + getToken()
}
this.initSrc(newval, oldval)
},
immediate: true,
deep: true,
@@ -131,6 +121,41 @@
}
},
methods: {
initFileId(newval, oldval) {
if (this.fileType === 'image') {
if (newval.indexOf('/') === 0) {
this.fileUrlShow = this.base_url + newval + '?tk=' + getToken();
return;
}
if (newval.indexOf('http') === 0) {
this.fileUrlShow = newval + '?tk=' + getToken();
return;
}
if (newval && this.watching) {
this.getImagePreviewUrlBlob(newval);
}
}
if (this.fileType === 'video') {
if (newval.indexOf('http') === 0) {
this.fileUrlShow = newval + '?tk=' + getToken()
} else {
if (newval && this.watching) {
this.getPreviewUrlBlob(newval);
}
}
}
this.videoContext = uni.createVideoContext(`video` + this.fileId)
},
initSrc(newval, oldval) {
let _src
if (newval.indexOf('http') === 0) {
_src = newval + '?tk=' + getToken()
} else {
_src = this.base_url + newval + '?tk=' + getToken()
}
this.fileUrlShow = _src
return _src
},
getImagePreviewUrlBlob(id) {
if (!id) {
this.fileUrlShow = '';
@@ -303,6 +328,11 @@
z-index: 999;
background-color: rgba(0, 0, 0, 0.65);
.stack-swiper {
width: 100vw;
height: 100vh;
}
movable-view {
position: fixed;
display: flex;
@@ -322,10 +352,12 @@
movable-view image {
width: 100%;
}
.detail-img {
width: calc(100% - 40rpx);
height: calc(100% - 100rpx);
}
// .detail-img {
// position: absolute;
// left: 50%;
+6 -5
View File
@@ -69,7 +69,7 @@
<!-- 段落图片视频预览 -->
<view v-if="type === 3" style="overflow: hidden;padding-top: 10rpx;">
<ImagePreview :class="['cont-img-box',imgAddrs.length === 1?'cont-video-box':imgAddrs.length === 2?'cont-img-box2':'']" v-for="item in imgAddrs "
:imgId="item" :previewDetail="false" @click="previewImage(item)"></ImagePreview>
:imgId="item" :previewDetail="false" @click="previewImage(item, imgAddrs)"></ImagePreview>
<VideoPreview class="cont-img-box cont-video-box" v-if="(dataInfo.vidoAddrs || []).length > 0"
:fileId="dataInfo.vidoAddrs[0].vidAddr"
:picId="dataInfo.vidoAddrs[0].vidImgAddr" @click="previewVideo(dataInfo.vidoAddrs[0])">
@@ -354,7 +354,7 @@
};
});
const mode = computed(()=>{
return props.type == 5 ? 'study_answer' : 'study'
return props.type == 5 ? 'study_answer' : props.type == 8 ? 'study_result' : 'study'
}); // 考试 examination 练习practice 学习study 学习结果study_answer
const imgAddrs = computed(()=>{
@@ -476,12 +476,13 @@
fileSrc: obj.vidAddr
})
}
const previewImage = (obj)=>{
const previewImage = (obj, arr=[])=>{
storageStore.setShowPreviewDetail({
status: true,
fileType: 'image',
fileType: arr.length>0?'imageList':'image',
fileId: '',
fileSrc: obj
fileSrc: obj,
fileSrcList: arr
})
}
+3 -3
View File
@@ -33,14 +33,14 @@
</view>
</view>
<view class="talking-list talking-list-qns" v-show="talkingList.length === 0">
<scroll-view class="talking-scroll-list" :scroll-y="true" :scroll-top="scrollTop" :show-scrollbar="false"
<!-- <scroll-view class="talking-scroll-list" :scroll-y="true" :scroll-top="scrollTop" :show-scrollbar="false"
:scroll-with-animation="false">
<view class="question-item-box" v-show="talkingList.length === 0" v-for="item in questionList">
<view class="question-item" @click="sendText(item.hotContent)">
{{ item.hotContent }}
</view>
</view>
</scroll-view>
</view>
</scroll-view> -->
</view>
<!-- <view class="questions-view">
<scroll-view :scroll-x="true" :show-scrollbar="false">
+6 -1
View File
@@ -12,6 +12,7 @@ export const useStorageStore = defineStore('storageStore',{
fileType: 'iamge',
fileId: '',
fileSrc: '',
fileSrcList: [],
gettingVoiceId: ''
}),
getters:{
@@ -39,6 +40,9 @@ export const useStorageStore = defineStore('storageStore',{
getFileSrc(state){
return state.fileSrc
},
getFileSrcList(state){
return state.fileSrcList
},
getGettingVoiceId(state){
return state.gettingVoiceId
},
@@ -47,11 +51,12 @@ export const useStorageStore = defineStore('storageStore',{
setGettingVoiceId(id){
return this.gettingVoiceId = id
},
setShowPreviewDetail({ status = false, fileType = 'image', fileId = '', fileSrc = '' }){
setShowPreviewDetail({ status = false, fileType = 'image', fileId = '', fileSrc = '', fileSrcList = [] }){
this.showPreviewDetail = status
this.fileId = fileId
this.fileType = fileType
this.fileSrc = fileSrc
this.fileSrcList = fileSrcList
},
setTouchBtnZIndex(index = 12){
this.touchBtnZIndex = index