Merge branch 'develop' of http://25.13.9.101:9000/K17_AITS/tra-app into develop

This commit is contained in:
杨航
2025-09-20 13:37:00 +08:00
32 changed files with 2535 additions and 1830 deletions
+3 -29
View File
@@ -1,7 +1,8 @@
# 开发环境
ENV = 'development'
# 'development'
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
# UAT
# VITE_APP_BASE_API_Url = 'http://25.18.122.78:9786'
@@ -16,35 +17,8 @@ VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
# h5专用地址x2
# VITE_APP_BASE_H5_API_Url = 'http://25.16.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.21:9786'
# VITE_APP_BASE_H5_API_Url = 'https://aitstest.jlbank.com.cn:7001'
# dev
VITE_APP_BASE_H5_API_Url = 'http://25.18.122.65:7001'
# sit
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.92:9786'
# UAT
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.78:9786'
# 任东
VITE_APP_BASE_H5_API_Url_TRAAPP = 'http://25.64.16.130:9601'
VITE_APP_BASE_H5_API_Url_TRASTUDY = 'http://25.64.16.130:9602'
# VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://25.64.16.140:9604'
# 张建成
# VITE_APP_BASE_H5_API_Url_TRAASK = 'http://25.64.16.144:9605'
# VITE_APP_BASE_H5_API_Url_TRASTUDY = 'http://25.64.16.144:9602'
# 王洋洋
# VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://25.64.16.140:9604'
# 于嘉文
# VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://25.64.16.143:9604'
# 孙宇
# VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://25.64.16.139:9604'
# 付丙鑫
# VITE_APP_BASE_H5_API_Url_TRASTUDY = 'http://25.64.16.145:9602'
Binary file not shown.
-1
View File
@@ -1,5 +1,4 @@
<script>
export default {
onLaunch: function () {
uni.onTabBarMidButtonTap(() => {
+93
View File
@@ -0,0 +1,93 @@
import {
getPhoneEnvBool
} from '@/common/common.js'
import common from '@/common/common.js'
const IsAndEnv = getPhoneEnvBool('AND')
import permission from '@/common/permission.js'
/**
* 录音权限工具类
* 统一处理安卓和iOS的录音权限状态:同意、拒绝、未设置
*/
const RecordPermission = {
hasRecordPermission: false,
/**
* 获取当前录音权限状态
* @returns {Promise<'granted'|'denied'|'undetermined'>} 权限状态
*/
getPermissionState() {
// #ifdef APP-PLUS
const authSetting = uni.getAppAuthorizeSetting();
const state = authSetting.microphoneAuthorized;
switch (state) {
case 'authorized':
return 'authorized';
case 'denied':
return 'denied';
case 'not determined':
return 'undetermined';
case 'config error':
return 'undetermined';
default:
return 'undetermined';
}
// #endif
// 非APP环境默认返回以获取方便h5调试
return 'undetermined';
},
/**
* 检查并请求录音权限
* @returns {Promise<boolean>} 是否获得录音权限
*/
requestPermission() {
if (this.hasRecordPermission) {
console.log('直接通过');
return Promise.resolve('authorized');
}
return new Promise(async (resolve, reject) => {
const state = this.getPermissionState();
// 已同意权限
console.log('权限状态', state);
if (state === 'authorized') {
this.hasRecordPermission = true; // 缓存权限状态
resolve('authorized')
};
if (state === 'denied') {
const result = await common.show(
'提示信息',
'当前页面需要使用录音权限,是否前往开启?'
)
if (result) {
// 前往权限设置页面
uni.openAppAuthorizeSetting();
reject('open_app_authorize_setting')
} else {
// 用户取消,返回上一页
reject('cancel')
}
}
if (state === 'undetermined') {
// #ifdef APP-PLUS
if (IsAndEnv) {
// 安卓需要再次请求权限
const permResult = await permission.requestAndroidPermission(
'android.permission.RECORD_AUDIO');
console.log('permResultx', permResult);
if(permResult === 1) {
}
} else {
reject('need_request');
}
// #endif
}
});
},
};
export default RecordPermission;
+17 -22
View File
@@ -3,7 +3,7 @@
<view class="line">
<text class="tr">维度评分</text>
<text class="td">{{ dimension_all_score }}</text>
<view class="td_success_error_img">
<image :src="select_status_icon" class="img"></image>
</view>
@@ -11,8 +11,9 @@
<view class="line" v-for="item in evalSettingVos">
<text class="tr">{{ item.gradeDimens }}</text>
<text class="td">{{ item.anlyGrade }}</text>
<view class="your_answer" v-if="item.dimensCode === '01'">
<rich-text :nodes="highlightedText" class="content" />
<view class="your_answer" v-if="item.dimensCode === '01' && passKeyword">
关键词{{ passKeyword }}
<!-- <rich-text :nodes="highlightedText" class="content" /> -->
</view>
</view>
</view>
@@ -33,13 +34,9 @@
type: Boolean,
default: false,
required: false
},
answerText: {
type: String,
default: '',
required: false
}
});
const passKeyword = computed(() => props.value.passKeyword);
const evalSettingVos = computed(() => props.value.evalSettingVos);
const status = computed(() => (props.judgeResult ? 'success' : 'error'));
const select_status_icon = computed(() => {
@@ -54,19 +51,19 @@
return props.value.relKeyword.split(/[,、]/).map((key) => key.trim());
});
const highlightedText = computed(() => {
// 如果没有关键词,直接返回原文本
if (!keywords.value.length) return '你的答案:' + props.answerText;
// 构建正则表达式,匹配所有关键词(不区分大小写)
// 对关键词进行转义,避免特殊字符影响正则
const escapedKeywords = keywords.value.map((keyword) => keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
// const highlightedText = computed(() => {
// // 如果没有关键词,直接返回原文本
// if (!keywords.value.length) return '你的答案:' + props.answerText;
// // 构建正则表达式,匹配所有关键词(不区分大小写)
// // 对关键词进行转义,避免特殊字符影响正则
// const escapedKeywords = keywords.value.map((keyword) => keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
// 创建正则表达式,g表示全局匹配,i表示不区分大小写
const regex = new RegExp(`(${escapedKeywords.join('|')})`, 'gi');
// // 创建正则表达式,g表示全局匹配,i表示不区分大小写
// const regex = new RegExp(`(${escapedKeywords.join('|')})`, 'gi');
// 替换匹配到的关键词,用span包裹并添加高亮样式
return '你的答案:' + props.answerText.replace(regex, (match) => `<span class="highlight">${match}</span>`);
});
// // 替换匹配到的关键词,用span包裹并添加高亮样式
// return '你的答案:' + props.answerText.replace(regex, (match) => `<span class="highlight">${match}</span>`);
// });
const dimension_all_score = computed(() => {
return (
evalSettingVos.value
@@ -76,9 +73,7 @@
.reduce((total, current) => total + current, 0)
);
});
watch(evalSettingVos.value, (newVal) => {
console.log('newVal2', newVal);
});
const click_option = () => {
emit('click_option', props.analysisVo);
};
+5 -2
View File
@@ -90,11 +90,14 @@
<text :class="right_or_wrong_class ? 'success' : 'error'">{{ myResultLabel.join('') }}</text>
</view>
</view>
<view class="reference_answer" v-if="qnsTyp === 'ES'">
<view class="reference_answer_title">你的答案:</view>
{{ subject_data.anserResult ?? '' }}
</view>
<esAnalysis
v-if="qnsTyp === 'ES'"
:value="subject_data.itemVos[0]"
:judgeResult="judgeResultStatus"
:answerText="subject_data.anserResult"
></esAnalysis>
<!-- 问答题专属参考答案 -->
@@ -174,7 +177,7 @@
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
});
let answerText = props.item?.my_answer || props.item.anserResult || [];
// item_answer 是答题结果,可以是对象 数组 字符串类型
const item_answer = ref(typeof answerText === 'string' ? answerText.split(',') : answerText);
+4 -3
View File
@@ -73,11 +73,11 @@
</template>
<script setup>
import { toRefs, ref, computed, unref, onMounted, onUnmounted, nextTick, watch, reactive } from 'vue';
import {ref, computed, onMounted, nextTick, watch, reactive } from 'vue';
import common from '@/common/common';
import permission from '@/common/permission.js';
import { getPhoneEnvBool } from '@/common/common.js';
import { initRecord } from '@/composables/useExamSubject.js';
const emit = defineEmits(['submit']);
const props = defineProps({
isDisabled: {
@@ -132,7 +132,8 @@
// const innerAudioContext = uni.createInnerAudioContext();
const startTimer = ref(null); // 延迟启动的定时器ID
const startFlag = ref(1);
const startRecord = (obj) => {
const startRecord = async (obj) => {
try {await initRecord()}catch(err) {return;}
if (props.isDisabled) return;
// 开始录音
console.log('开始录音', recordingStatus.value);
@@ -1,6 +1,6 @@
<template>
<view v-bind="$attrs">
<image :src="imgUrlShow" fit="cover" :mode="mode" @click="showDetail" style="width: 100%;height: 100%;"></image>
<image :src="imgUrlShow" :mode="mode" @click="showDetail" style="width: 100%;height: 100%;"></image>
<!-- <uv-overlay :show="showModel" opacity=".6" @click="showModel = false"> -->
<uni-popup ref="PopupRef" class="popup" mask-background-color="rgba(0,0,0,.2)">
<view class="overlay-box" @click="closePopup">
@@ -42,7 +42,7 @@
},
mode:{
type: String,
default: 'aspectFit'
default: 'scaleToFill'
},
previewDetail: {
default: '',
@@ -1,5 +1,5 @@
<template>
<view class="video-view-box" @click="showModelNow">
<view v-bind="$attrs" class="video-view-box" @click="showModelNow">
<imagePreviewVue :imgId="picId" class="preview-pic" v-if="picId"/>
<imagePreviewVue :src="picSrc" class="preview-pic" v-if="picSrc"/>
<view class="img-box">
@@ -165,11 +165,10 @@
height: 100rpx;
transform: translate(-50%, -50%);
}
.preview-pic{
width: 100%;
height: 100%;
position: absolute;
}
.preview-pic{
width: 100%;
height: 100%;
}
.overlay-box .video-cont-center{
width: 100vw;
max-height: 100%;
@@ -1,44 +0,0 @@
<template>
<view>
<view class="copyRightBox">
<view class="font" ref="sy" v-for="(item, index) in 30" :key="index">
{{ sytext }}
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {};
},
props: {
//载入的标签数据
sytext: {
type: String,
default: '吉AI学'
}
},
mounted() {}
};
</script>
<style>
.copyRightBox {
overflow: hidden;
width: 100%;
height: 700px;
pointer-events: none;
position: fixed;
top: 20px;
z-index: 99999;
}
.font {
float: left;
transform: rotate(-30deg);
margin-top: 220upx;
margin-left: 100upx;
font-size: 34upx;
color: rgba(100, 0, 0, 0.2);
letter-spacing: 4px;
}
</style>
+127
View File
@@ -0,0 +1,127 @@
<template>
<KeepAlive>
<view class="watermark-container" v-if="showWatermark">
<view class="watermark-item" v-for="row in rowCount" :key="row" :style="{ top: `${row * itemHeight}px` }">
<view
class="watermark-text"
v-for="(cols, col) in colCount"
:key="col"
:style="{
left: `${col * itemWidth}px`,
transform: `rotate(${rotate}deg)`,
fontSize: `${fontSize}px`,
color: color
}"
>
{{ textValue }}
</view>
</view>
</view>
</KeepAlive>
</template>
<script>
import { getUserInfo } from '@/common/common';
export default {
name: 'Watermark',
props: {
text: {
type: String,
default: '李想 0613',
validator: (val) => typeof val === 'string' && val.trim() !== ''
},
// 单个水印宽度(px
itemWidth: {
type: Number,
default: 180
},
// 单个水印高度(px
itemHeight: {
type: Number,
default: 120
},
// 旋转角度(度)
rotate: {
type: Number,
default: -30
},
// 字体大小(px
fontSize: {
type: Number,
default: 16
},
// 字体颜色
color: {
type: String,
default: 'rgba(30, 30, 70, 0.1)'
},
// 是否显示
showWatermark: {
type: Boolean,
default: true
}
},
data() {
return {
rowCount: 0, // 行数
colCount: 0, // 列数
windowWidth: 0, // 屏幕宽度
windowHeight: 0, // 屏幕高度
textValue: ''
};
},
mounted() {
// 获取屏幕尺寸(App端兼容方式)
this.getWindowInfo();
const userInfo = getUserInfo();
this.textValue = (userInfo?.userName??'') + ' ' + (userInfo?.loginName??'');
},
methods: {
getWindowInfo() {
uni.getSystemInfo({
success: (res) => {
this.windowWidth = res.windowWidth;
this.windowHeight = res.windowHeight;
this.calcGridCount(); // 计算需要多少行列覆盖屏幕
}
});
},
// 计算水印网格的行列数(确保完全覆盖屏幕)
calcGridCount() {
// 列数 = 屏幕宽度 / 单个水印宽度 + 2(冗余1列避免边缘空白)
this.colCount = Math.ceil(this.windowWidth / this.itemWidth) + 2;
// 行数 = 屏幕高度 / 单个水印高度 + 2(冗余1行避免边缘空白)
this.rowCount = Math.ceil(this.windowHeight / this.itemHeight) + 2;
}
}
};
</script>
<style scoped>
.watermark-container {
position: fixed;
top: var(--status-bar-height); /* 适配状态栏 */
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none; /* 不拦截点击事件 */
z-index: 99999;
overflow: hidden;
}
/* 行容器 */
.watermark-item {
position: absolute;
width: 100%;
height: auto;
}
/* 单个水印文本 */
.watermark-text {
position: absolute;
white-space: nowrap; /* 避免文本换行 */
user-select: none; /* 禁止选中 */
/* letter-spacing: 2px; */
}
</style>
+10
View File
@@ -0,0 +1,10 @@
import RecordPermission from '@/common/recordPermission.js'
export const initRecord = () => RecordPermission.requestPermission();
export default function useIndexList() {
return {
};
}
+225 -197
View File
@@ -1,22 +1,54 @@
<template>
<view class="talk-btns" ref="talkBtnRef">
<view class="touch-btn" @touchstart="getRecordPermission" @touchend="hideOverlayTalking" @touchmove="handleMove"
v-if="!keyboardIsShow">按住说话
<view
class="touch-btn"
@touchstart="getRecordPermission"
@touchend="hideOverlayTalking"
@touchmove="handleMove"
v-if="!keyboardIsShow"
>
按住说话
</view>
<view class="touch-btn" v-if="keyboardIsShow">
<uv-input class="input-box" ref="inputRef" v-model="answerValue" :maxlength="props.textValueLength"
:adjustPosition="adjustPosition" placeholderStyle='color:#999999;font-size:26rpx' placeholder="请输入文字">
</uv-input>
<uv-input
class="input-box"
ref="inputRef"
v-model="answerValue"
:maxlength="props.textValueLength"
:adjustPosition="adjustPosition"
placeholderStyle="color:#999999;font-size:26rpx"
placeholder="请输入文字"
></uv-input>
</view>
<view class="talk-btn-box" v-show="!onlyVoice">
<image class="talk-btn-icon" v-show="!keyboardIsShow" src="/src/static/images/course/jianpan.png" mode=""
@click="changeBtn"></image>
<image class="talk-btn-icon" v-show="keyboardIsShow && !answerValue" src="/src/static/images/course/record.png"
mode="" @click="changeBtn"></image>
<image class="talk-btn-icon" v-show="keyboardIsShow && !!answerValue" src="/src/static/images/course/send.png"
mode="" @click.stop="showKeyboard"></image>
<image
class="talk-btn-icon"
v-show="!keyboardIsShow"
src="/src/static/images/course/jianpan.png"
mode=""
@click="changeBtn"
></image>
<image
class="talk-btn-icon"
v-show="keyboardIsShow && !answerValue"
src="/src/static/images/course/record.png"
mode=""
@click="changeBtn"
></image>
<image
class="talk-btn-icon"
v-show="keyboardIsShow && !!answerValue"
src="/src/static/images/course/send.png"
mode=""
@click.stop="showKeyboard"
></image>
</view>
<uni-popup ref="talkModelRef" type="bottom" @click="showTalkingModel = false" mask-background-color="rgba(0,0,0,0.9)">
<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">
@@ -25,42 +57,30 @@
</view>
<view class="tips-box">松开发送</view>
<view class="btns-box">
<view :class="['close-btn',isOut?'is-out':'']">取消
<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':'']">
<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 { 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', 'submitAnswer', 'startRecord'])
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', 'submitAnswer', 'startRecord']);
const props = defineProps({
isDisabled: {
default: true,
@@ -93,59 +113,73 @@
adjustPosition: {
default: false,
type: Boolean
},
})
const {
isDisabled,
onlyVoice,
adjustPosition
} = toRefs(props)
const isLoadingState = computed(() => props.isLoading)
const showTalkingModel = ref(false)
const keyboardIsShow = ref(false)
const talkModelRef= ref()
storageStore.setTouchBtnZIndex(9)
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.canAnswer, () => {}, {
deep: true,
immediate: true
})
watch(() => props.onlyVoice, () => {}, {
deep: true,
immediate: true
})
watch(() => props.isDisabled, (val) => {
if (val) {
keyboardIsShow.value = false
}
}, {
deep: true,
immediate: true
})
});
const { isDisabled, onlyVoice, adjustPosition } = toRefs(props);
const isLoadingState = computed(() => props.isLoading);
const showTalkingModel = ref(false);
const keyboardIsShow = ref(false);
const talkModelRef = ref();
storageStore.setTouchBtnZIndex(9);
const btnZindex = computed(() => storageStore.getTouchBtnZIndex);
const answerValue = ref('')
const talkBtnRef = ref()
watch(
() => showTalkingModel.value,
(val) => (val ? talkModelRef.value.open() : talkModelRef.value.close())
);
watch(
() => props.isLoading,
() => {},
{
deep: true,
immediate: true
}
);
watch(
() => props.canAnswer,
() => {},
{
deep: true,
immediate: true
}
);
watch(
() => props.onlyVoice,
() => {},
{
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()
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 recordObjCom = ref({});
const IsAndEnv = getPhoneEnvBool('AND');
const isStartNum = ref(0);
const isStopNum = ref(0);
const initRecord = () => {
recordObjCom.value = uni.getRecorderManager()
recordObjCom.value = uni.getRecorderManager();
unref(recordObjCom)?.onStop((res, duration) => {
audioPath.value = res.tempFilePath
audioPath.value = res.tempFilePath;
if (!isOut.value) {
if (startFlag.value !== 2) {
// setTimeout(() => {
@@ -158,243 +192,237 @@
closeModel(() => {
nextTick(() => {
// showTalkingModel.value = false
common.msg('说话时间太短,没有听清!')
})
})
common.msg('说话时间太短,没有听清!');
});
});
} else {
closeModel()
emit('uploadVoice', audioPath.value)
closeModel();
emit('uploadVoice', audioPath.value);
}
} else {
closeModel()
closeModel();
}
})
});
unref(recordObjCom).onStart(() => {
startFlag.value = 1
startFlag.value = 1;
setTimeout(() => {
startFlag.value = 2
}, 1000)
let _recordPerm = uni.getAppAuthorizeSetting()
let _state = _recordPerm.microphoneAuthorized
console.log(isStopNum.value === isStartNum.value, _state)
startFlag.value = 2;
}, 1000);
let _recordPerm = uni.getAppAuthorizeSetting();
let _state = _recordPerm.microphoneAuthorized;
console.log(isStopNum.value === isStartNum.value, _state);
if (_state === 'authorized') {
if (isStopNum.value === isStartNum.value) {
stopRecord()
stopRecord();
} else {
showTalkingModel.value = true
showTalkingModel.value = true;
}
}
})
}
const startFlag = ref(1)
});
};
const startFlag = ref(1);
// const canStart = ref(true)
const startRecord = () => {
isStartNum.value += 1
isStartNum.value += 1;
unref(recordObjCom).start({
format: 'mp3'
})
}
});
};
const stopRecord = () => {
isStopNum.value = isStartNum.value
isStopNum.value = isStartNum.value;
try {
if (unref(recordObjCom).stop) {
unref(recordObjCom).stop()
unref(recordObjCom).stop();
} else {
closeModel()
closeModel();
}
} catch {
closeModel()
closeModel();
}
// setTimeout(() => {
// canStart.value = true
// }, 1000)
}
};
const closeModel = (cb = () => {}) => {
isStopNum.value = isStartNum.value
isStopNum.value = isStartNum.value;
setTimeout(() => {
showTalkingModel.value = false
cb && cb()
}, 350)
showTalkingModel.value = false;
cb && cb();
}, 350);
setTimeout(() => {
isTalking.value = false
}, 800)
}
isTalking.value = false;
}, 800);
};
// #endif
//#ifndef APP-PLUS
const recordObjCom = ref({})
const initRecord = () => {}
const recordObjCom = ref({});
const initRecord = () => {};
// #endif
onMounted(async () => {
initRecord?.()
initRecord?.();
//#ifdef APP-PLUS
// 安卓端
if (IsAndEnv) {
let _recordPerm = await permission.requestAndroidPermission('android.permission.RECORD_AUDIO')
let _recordPerm = await permission.requestAndroidPermission('android.permission.RECORD_AUDIO');
if (_recordPerm !== 1) {
common.show('提示信息', '当前页面需要使用的录音权限,是否前往开启?', true, '取消', '确定').then((res) => {
if (res) {
// 去开启权限
permission.gotoAppPermissionSetting()
permission.gotoAppPermissionSetting();
} else {
// 取消
common.navigateBack()
common.navigateBack();
}
})
});
}
} else {
let _recordPerm = uni.getAppAuthorizeSetting()
let _state = _recordPerm.microphoneAuthorized
let _recordPerm = uni.getAppAuthorizeSetting();
let _state = _recordPerm.microphoneAuthorized;
if (_state === 'denied') {
common.show('提示信息', '当前页面需要使用的录音权限,是否前往开启?', true, '取消', '确定').then((res) => {
if (res) {
// 去开启权限
uni.openAppAuthorizeSetting()
uni.openAppAuthorizeSetting();
} else {
// 取消
common.navigateBack()
common.navigateBack();
}
})
});
} else if (_state === 'not determined') {
// startRecord()
}
}
// #endif
// setTimeout(()=> {talkModelRef.value.open()}, 2000)
})
});
// 录音结束
// 记录 按下移动位置
const touchX = ref(0)
const touchY = ref(0)
const baseY = ref(0)
const windowInfo = uni.getWindowInfo()
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
})
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)
windowInfo.screenHeight - touchY.value <= 0;
});
const recNum = ref(1);
const activeNum = ref(1);
const getRecordPermission = (obj) => {
if(isDisabled.value) return
if (isDisabled.value) return;
if (isTalking.value) {
common.msg('操作频繁,请稍后重试!')
common.msg('操作频繁,请稍后重试!');
setTimeout(() => {
isTalking.value = false
}, 500)
return
isTalking.value = false;
}, 500);
return;
}
showOverlayTalking(obj)
}
const isMoving = ref(false)
const isTalking = ref(false)
showOverlayTalking(obj);
};
const isMoving = ref(false);
const isTalking = ref(false);
const showOverlayTalking = (obj) => {
if (showTalkingModel.value) return
if (showTalkingModel.value) return;
if (isLoadingState.value) {
uni.showToast({
title: props.loadingText,
icon: "none",
icon: 'none',
duration: 1000
})
return
});
return;
}
if (!props.canAnswer) {
uni.showToast({
title: props.canAnswerText,
icon: "none",
icon: 'none',
duration: 1000
})
return
});
return;
}
// if(!canStart.value){
// return
// }
isMoving.value = true
isTalking.value = true
emit('startRecord')
recNum.value += 1
isMoving.value = true;
isTalking.value = true;
emit('startRecord');
recNum.value += 1;
if (!isDisabled.value) {
touchX.value = obj.changedTouches[0].pageX
touchY.value = obj.changedTouches[0].pageY
baseY.value = obj.currentTarget.offsetTop
touchX.value = obj.changedTouches[0].pageX;
touchY.value = obj.changedTouches[0].pageY;
baseY.value = obj.currentTarget.offsetTop;
//#ifdef APP-PLUS
// canStart.value = false
startRecord()
startRecord();
// #endif
setTimeout(() => {
hideOverlayTalking()
}, 59 * 1010)
hideOverlayTalking();
}, 59 * 1010);
} else {
showTalkingModel.value = false
showTalkingModel.value = false;
uni.showToast({
title: '暂无问题需要回答!',
icon: "none",
icon: 'none',
duration: 1000
})
});
}
}
};
const hideOverlayTalking = (obj) => {
isMoving.value = false
if (!showTalkingModel.value) return
isMoving.value = false;
if (!showTalkingModel.value) return;
//#ifdef APP-PLUS
stopRecord()
stopRecord();
// #endif
}
};
const handleMove = (obj) => {
if (isMoving.value) {
touchX.value = obj.changedTouches[0].pageX
touchY.value = obj.changedTouches[0].pageY
touchX.value = obj.changedTouches[0].pageX;
touchY.value = obj.changedTouches[0].pageY;
if (outScreen.value) {
hideOverlayTalking(obj)
hideOverlayTalking(obj);
}
}
}
};
// 发送文本
const showKeyboard = () => {
if (isLoadingState.value) {
uni.showToast({
title: props.loadingText,
icon: "none",
icon: 'none',
duration: 1000
})
return
});
return;
}
if (!isDisabled.value) {
if (keyboardIsShow.value) {
if (answerValue.value.trim()) {
emit(
'submitAnswer',
answerValue.value,
changeBtn
)
emit('submitAnswer', answerValue.value, changeBtn);
} else {
// keyboardIsShow.value = !keyboardIsShow.value
common.msg("不能发送空白消息!")
common.msg('不能发送空白消息!');
}
} else {
keyboardIsShow.value = !keyboardIsShow.value
keyboardIsShow.value = !keyboardIsShow.value;
}
} else {
uni.showToast({
title: '暂无问题需要回答!',
icon: "none",
icon: 'none',
duration: 1000
})
});
}
}
};
const changeBtn = () => {
if (props.isDisabled) return
keyboardIsShow.value = !keyboardIsShow.value
answerValue.value = ''
}
if (props.isDisabled) return;
keyboardIsShow.value = !keyboardIsShow.value;
answerValue.value = '';
};
</script>
<style lang="scss" scoped>
@@ -406,7 +434,7 @@
z-index: v-bind(btnZindex);
.touch-btn {
height: 92rpx;
box-shadow: 0 8rpx 20rpx 0 rgba(0, 0, 0, .06);
box-shadow: 0 8rpx 20rpx 0 rgba(0, 0, 0, 0.06);
text-align: center;
line-height: 92rpx;
color: #000;
@@ -584,4 +612,4 @@
::v-deep .uv-input__content__field-wrapper__field {
padding-right: 60rpx;
}
</style>
</style>
+14 -10
View File
@@ -770,16 +770,20 @@ onShow(() => {
// getData('2');
// initsocketTask()
if(isPreview.value === 'S'){
sendMessage({
commond: 'STDY',
body: {
crsId: crsId.value,
teacherNo: choiceTeacher.value,
studyType: isPreview.value ? 'P' : 'S',
isContinue: isContinue.value,
stdyId: stdyId.value
}
})
studyStartApi({
crsId: crsId.value,
teacherNo: choiceTeacher.value,
studyType: isPreview.value ? 'P' : 'S',
isContinue: isContinue.value,
stdyId: stdyId.value
}).then(res=>{
const { body } = res
stdyId.value = body.stdyId || ''
stdyBatch.value = body.stdyBatch || ''
oldStdyId.value = body.oldStdyId || ''
logId.value = body.logId || ''
step.value = 2
}).catch(err=>{})
}
})
const watchFlag = ref(1)
+1066 -1010
View File
File diff suppressed because it is too large Load Diff
+9 -22
View File
@@ -103,8 +103,7 @@
:useEasing="true"
ref="accmExamCntRef"
></uv-count-to>
<span class="number"></span>
</view>
</view>
@@ -171,7 +170,7 @@
<image-preview
class="img-box img"
:imgId="item.imgId"
mode="aspectFill"
mode="scaleToFill"
:isCache="true"
:width="240"
:height="240"
@@ -179,13 +178,9 @@
</view>
<view class="right">
<view class="title ellipsis-text">{{ item.crsName }}</view>
<view class="note ellipsis-text">
更新{{ (item.mtTime || '').substr(0, 10) }}
</view>
<view class="note ellipsis-text">更新{{ (item.mtTime || '').substr(0, 10) }}</view>
<view class="button_div">
<view class="note ellipsis-text">
已学{{ item.accmStdyCnt }}人次
</view>
<view class="note ellipsis-text">已学{{ item.accmStdyCnt }}人次</view>
<view class="button">去学习</view>
</view>
</view>
@@ -214,7 +209,7 @@
<image-preview
class="img-box img"
:imgId="item.imgId"
mode="aspectFill"
mode="scaleToFill"
:isCache="true"
:width="240"
:height="240"
@@ -222,13 +217,9 @@
</view>
<view class="right">
<view class="title ellipsis-text">{{ item.crsName }}</view>
<view class="note ellipsis-text">
更新{{ (item.mtTime || '').substr(0, 10) }}
</view>
<view class="note ellipsis-text">更新{{ (item.mtTime || '').substr(0, 10) }}</view>
<view class="button_div">
<view class="note ellipsis-text">
已学{{ item.accmStdyCnt }}人次
</view>
<view class="note ellipsis-text">已学{{ item.accmStdyCnt }}人次</view>
<view class="button">去学习</view>
</view>
</view>
@@ -264,13 +255,9 @@
</view>
<view class="right">
<view class="title ellipsis-text">{{ item.crsName }}</view>
<view class="note ellipsis-text">
更新{{ (item.mtTime || '').substr(0, 10) }}
</view>
<view class="note ellipsis-text">更新{{ (item.mtTime || '').substr(0, 10) }}</view>
<view class="button_div">
<view class="note ellipsis-text">
已学{{ item.accmStdyCnt }}人次
</view>
<view class="note ellipsis-text">已学{{ item.accmStdyCnt }}人次</view>
<view class="button">去学习</view>
</view>
</view>
+6 -4
View File
@@ -508,19 +508,21 @@
}
.body-title {
width: 100%;
position: relative;
text-align: center;
height: 160rpx;
height: 180rpx;
padding-top: var(--status-bar-height);
margin-bottom: -20rpx;
line-height: calc(160rpx - var(--status-bar-height) - 20rpx);
margin-bottom: -10rpx;
line-height: calc(180rpx - var(--status-bar-height) - 10rpx);
font-family: PingFangSC;
font-weight: 600;
font-size: 32rpx;
color: #000000;
font-style: normal;
background-image: url('@/static/images/learningTasks/learningTasksBg.png');
background-repeat: no-repeat;
background-size: 100% calc(420rpx + var(--status-bar-height)); /* 宽度充满,高度自适应比例 */
+2 -5
View File
@@ -1,6 +1,5 @@
<template>
<view class="main_div max_page">
<nav-bar :is_seat="false"></nav-bar>
<view class="bg_div">
<image src="/static/images/me/bg.png" class="bg_img"></image>
@@ -14,7 +13,7 @@
class="img_100"
:src="user_info.imageAddr"
></image-preview>
<image v-else class="img_100" :src="defaultAvatarSrc"></image>
<image class="img_100" :src="defaultAvatar"></image>
</view>
<view class="personal_information_data">
<view class="name_sex_div">
@@ -145,7 +144,6 @@
</template>
<script setup>
import { ref, computed, reactive, nextTick } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import common from '@/common/common';
@@ -158,8 +156,7 @@
import { uploadFile } from '@/api/common';
import { setMascot } from '@/common/mascot.js';
import { queryCurrentUser } from '@/api/login.js';
import { defaultAvatar } from "@/enum.js"
const defaultAvatarSrc = computed(()=> defaultAvatar)
import { defaultAvatar } from '@/enum.js';
const badgeList = [
{
name: '黑铁',
-355
View File
@@ -1,355 +0,0 @@
<template>
<view class="main_div max_page">
<scroll-view
ref="scrollRef"
:scroll-y="true"
class="scroll-Y"
:refresher-enabled="false"
:show-scrollbar="false"
@scrolltolower="scrolltolower"
@refresherrefresh="onRefresh"
@refresherrestore="triggered = 'restore'"
:refresher-triggered="triggered"
refresher-default-style="none"
>
<view class="content">
<view
style="
position: absolute;
top: -90rpx;
width: 100%;
height: 90rpx;
display: flex;
align-items: center;
justify-content: center;
"
></view>
<view class="item" v-for="item in data_list" @click="read">
<view class="icon">
<image
v-if="item.noticeTyp === '01'"
class="img_100"
src="@/static/images/messageNotification/type_01.png"
mode="widthFix"
></image>
<image
v-if="item.noticeTyp === '02'"
class="img_100"
src="@/static/images/messageNotification/type_02.png"
mode="widthFix"
></image>
<image
v-if="item.noticeTyp === '03'"
class="img_100"
src="@/static/images/messageNotification/type_03.png"
mode="widthFix"
></image>
</view>
<view class="right">
<view class="top">
<view class="title">
{{ item.noticeTitle }}
</view>
<view class="time">
{{ item.ctTime?.substr(0, 16) }}
</view>
</view>
<view class="bottom">
<view class="note ellipsis-text">
{{ item.noticeContent }}
</view>
<!-- <view class="num_div"></view> -->
<view class="read_div" v-if="item.isRead"></view>
</view>
</view>
</view>
<uv-load-more v-if="total !== 0" :status="status" loadmore-text="轻轻上拉加载" :height="30"></uv-load-more>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, reactive, nextTick, onMounted } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { queryTraPersonalMessageNoticePaging } from '@/api/messageNotification.js';
const customStyle = {
backgroundColor: '#ffffff',
paddingLeft: '40rpx',
height: '78rpx',
paddingTop: '5px',
paddingBottom: '5px',
border: 'none'
};
const suffixIconStyle = {
color: '#2c8ef2',
fontSize: '50rpx'
};
const options = [
{
text: '删除',
style: {
backgroundColor: '#f56c6c'
}
}
];
const status = ref('loadmore'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const limit = 20;
const total = ref(-1);
const page = ref(0);
const data_list = reactive([]);
const triggered = ref(false);
const _freshing = ref(false);
const offset = ref(0);
const scrollRef = ref(null);
// 下拉过程中触发
const onPulling = (e) => {
page.value = 1;
total.value = -1;
page.data_list.length = 0;
getData();
};
// 刷新处理函数
const onRefresh = () => {
if (_freshing.value) return;
_freshing.value = true;
setTimeout(() => {
triggered.value = false;
_freshing.value = false;
}, 1000);
};
const getData = (from = '') => {
if (from == 'first') {
if (total.value !== -1) {
return;
}
status.value = 'loading';
total.value = -1;
page.value = 1;
data_list.length = 0;
} else {
if (status.value !== 'loadmore' && status.value !== '') return;
status.value = 'loading';
page.value++;
}
console.log(status.value, '阿斯顿撒');
setTimeout(() => {
queryTraPersonalMessageNoticePaging({
page: page.value,
limit: limit
})
.then((res) => {
total.value = res.total;
data_list.push(...res.body);
})
.finally(() => {
if (data_list.length >= total.value) {
status.value = 'nomore';
} else {
status.value = 'loadmore';
}
});
}, 200);
};
const scrolltolower = (item) => {
getData();
};
onMounted(() => {
// triggered.value = true;
getData();
// setTimeout(() => {
// triggered.value = true
// message_list.value.push(...[1, 2, 3, 4, 5])
// }, 1000)
console.log(scrollRef.value);
});
</script>
<style scoped lang="scss">
.pulldown-loading-box {
position: fixed;
left: 0;
right: 0;
z-index: 3;
transform: translateY(-100%);
transition: transform 0.3s;
z-index: 1;
}
.scroll-Y {
height: calc(100vh - 70rpx - var(--status-bar-height) - 78rpx - 40rpx);
position: relative;
overflow: hidden;
border-radius: 20rpx 20rpx 0 0;
.content {
width: 100%;
background-color: #fff;
box-shadow: 0 0 6rpx #b3d6ff;
position: relative;
.item {
height: 154rpx;
padding: 38rpx 0 38rpx 0;
margin: 0 38rpx;
display: flex;
justify-content: space-between;
align-items: center;
border-top: 2rpx solid #efefef;
.icon {
width: 78rpx;
height: 78rpx;
}
.right {
margin-left: 16rpx;
flex: 1;
.top {
display: flex;
justify-content: space-between;
.title {
font-family: PingFangSC;
font-weight: 600;
font-size: 30rpx;
color: #000000;
line-height: 46rpx;
text-align: left;
font-style: normal;
}
.time {
font-size: 23rpx;
color: #999999;
line-height: 30rpx;
text-align: right;
font-style: normal;
}
}
.bottom {
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
width: 100%;
.note {
font-family: PingFangSC;
font-weight: 400;
font-size: 26rpx;
color: #666666;
line-height: 35rpx;
text-align: left;
font-style: normal;
flex: 1;
}
.num_div {
padding: 0 8rpx;
height: 31rpx;
background: #ff6c75;
border-radius: 15rpx;
display: flex;
align-items: center;
justify-content: center;
font-family: ArialMT;
font-size: 22rpx;
color: #ffffff;
line-height: 26px;
min-width: 40rpx;
}
.read_div {
width: 16rpx;
height: 16rpx;
background-color: #d70c18;
border-radius: 100%;
}
}
}
}
}
}
.main_div {
display: flex;
flex-direction: column;
align-items: center;
background: linear-gradient(0deg, rgba(234, 246, 255, 0) 0%, #c3e6ff 100%);
// background-size: 100% 300px;
// background-repeat: no-repeat; /* 防止渐变重复平铺 */
padding-top: var(--status-bar-height);
overflow: hidden;
}
.body-title {
width: 100%;
height: 70rpx;
font-size: 33rpx;
padding-top: 10rpx;
font-weight: 500;
color: #000;
position: relative;
text-align: center;
.delete_div {
position: absolute;
right: 20rpx;
top: 17rpx;
width: 50rpx;
height: 50rpx;
.img {
width: 34rpx;
height: 34rpx;
}
}
.back_div {
position: absolute;
left: 26rpx;
height: 34rpx;
top: 17rpx;
.back-icon {
position: relative;
width: 50rpx;
height: 50rpx;
cursor: pointer;
display: flex;
align-items: center;
}
.back-icon::before,
.back-icon::after {
content: '';
position: absolute;
transition: all 0.3s ease;
}
.back-icon::after {
top: 25%;
left: 25%;
width: 40%;
height: 40%;
border-top: 4rpx solid #2b2c2e;
border-left: 4rpx solid #2b2c2e;
transform: rotate(-45deg);
}
}
}
.input-view {
width: 90%;
margin: 20rpx auto;
height: 78rpx;
}
</style>
+1 -1
View File
@@ -81,7 +81,7 @@
<script setup>
import { ref, reactive, computed, onMounted, nextTick, onUnmounted } from 'vue';
import { onLoad, onHide, onShow, onUnload, onBackPress } from '@dcloudio/uni-app';
import { onLoad, onHide, onShow, onUnload, onBackPress, onReady } from '@dcloudio/uni-app';
import Feedback from '@/pages/examination/components/feedback.vue';
import Dialog from '@/pages/examination/components/dialog.vue';
+108 -115
View File
@@ -14,25 +14,33 @@
<view class="font_pf">头像</view>
</template>
<template #value>
<view style="width: 42rpx; height: 42rpx;border-radius: 100%; overflow: hidden">
<view style="width: 42rpx; height: 42rpx; border-radius: 100%; overflow: hidden">
<image v-if="userInfo.imagePath" class="img_100" :src="userInfo.imagePath"></image>
<image-preview v-else class="img_100" :src="userInfo.imageAddr"></image-preview>
</view>
</template>
</uv-cell>
<uv-cell name="sex" isLink title="性别">
<!-- <uv-cell name="sex" isLink title="性别">
<template #title>
<text class="font_pf">性别</text>
</template>
<template #value>
<picker mode="selector" :value="index" range-key="label" style="width: 100%" :range="sexOptions"
@change="sexChange">
<view style="text-align: right">{{showGenderText}}</view>
<picker
mode="selector"
:value="index"
range-key="label"
style="width: 100%"
:range="sexOptions"
@change="sexChange"
>
<view style="text-align: right">{{ showGenderText }}</view>
</picker>
</template>
</uv-cell>
<uv-cell title="百禄形象" @click="gotoMascot()" :isLink="true" :border="false"></uv-cell>
<!-- <uv-cell :isLink="true" :border="false">
</uv-cell> -->
<uv-cell title="性别" @click="sexPicker.open()" :isLink="true" :value="showGenderText"></uv-cell>
<uv-cell title="百禄形象" @click="gotoMascot()" :isLink="true" :border="false" :value="mascotName"></uv-cell>
<!-- <uv-cell :isLink="true" :border="false">
<template #title>
<text class="font_pf">竞赛是否匿名</text>
</template>
@@ -45,113 +53,79 @@
</uv-cell> -->
</uv-cell-group>
</view>
<avatar-cropper v-if="avatarCropperStatus" mode="ratio" :imageUrl="avatarCropperUrl" :width="500" :height="500"
:delay="150" @cancel="onCancel" @confirm="onConfirm"></avatar-cropper>
<avatar-cropper
v-if="avatarCropperStatus"
mode="ratio"
:imageUrl="avatarCropperUrl"
:width="500"
:height="500"
:delay="150"
@cancel="onCancel"
@confirm="onConfirm"
></avatar-cropper>
<uv-picker ref="sexPicker" :columns="[sexOptions]" keyName="label" @confirm="sexConfirm"></uv-picker>
</view>
</template>
<script setup>
import {
reactive,
ref,
computed,
nextTick
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app';
import {
uploadFile
} from '@/api/common'
import {
queryCurrentUser
} from '@/api/login.js';
import { reactive, ref, computed, nextTick } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { uploadFile } from '@/api/common';
import { queryCurrentUser } from '@/api/login.js';
import common from '@/common/common';
import {
getUserInfo
} from '@/common/common';
import {
GENDER
} from '@/enum.js';
import {
updateDfSysUserExtandInfoGender,
updateDfSysUserExtandInfoImageAddr
} from "@/api/user.js"
import {
queryValidTraMascotInfo
} from "@/api/mascot.js"
import {
get_base_url
} from '@/api/request.js'
import { getUserInfo } from '@/common/common';
import { GENDER } from '@/enum.js';
import { updateDfSysUserExtandInfoGender, updateDfSysUserExtandInfoImageAddr } from '@/api/user.js';
import { queryValidTraMascotInfo } from '@/api/mascot.js';
import { get_base_url } from '@/api/request.js';
const index = ref(0);
const sexPicker = ref(null);
const avatarCropperUrl = ref('');
const aaa = ref('');
const userInfo = ref({
gender: '',
imagePath: '',
imageAddr: ''
imageAddr: '',
mascotInfo: null
});
const avatarCropperStatus = ref(true)
const sexOptions = GENDER
const anonymousOptions = [{
value: "y",
label: "是",
const avatarCropperStatus = ref(true);
const mascotName = computed(() => userInfo.value.mascotInfo?.mascotName || '');
const sexOptions = GENDER;
const anonymousOptions = [
{
value: 'y',
label: '是'
},
{
value: "n",
label: "否",
value: 'n',
label: '否'
}
] //
]; //
//
const gotoMascot = () => {
common.navigateTo('/pages/mascot/mascot_select_list?from=setting')
// common.loading()
// queryValidTraMascotInfo().then(({body}) => {
// console.log(body);
// common.setPageCache('mascotList', body)
// body.map(({imgAddr}) => {
// const url = imgAddr
// if (imgAddr){
// const img = new Image();
// img.onload = () => {
// console.log(`: ${url}`);
// // resolve(img);
// };
// img.onerror = () => {
// console.error(`: ${url}`);
// // reject(new Error(`Failed to load image: ${url}`));
// };
// img.src = base_url + url;
// }
// })
// common.hideLoading()
// common.navigateTo('/pages/mascot/mascot_select_list')
// })
}
common.navigateTo('/pages/mascot/mascot_select_list?from=setting');
};
//
const showGenderText = computed(() => {
const item = sexOptions.find((item) => item.value === userInfo.value.gender)
const item = sexOptions.find((item) => item.value === userInfo.value.gender);
if (item) {
return item.label
return item.label;
} else {
return '未填写'
return '未填写';
}
})
});
//
const showAnonymousText = computed(() => {
const item = anonymousOptions.find((item) => item.value === userInfo.value?.anonymous)
const item = anonymousOptions.find((item) => item.value === userInfo.value?.anonymous);
if (item) {
return item.label
return item.label;
} else {
return '否'
return '否';
}
})
});
//
const click_update_avatar = () => {
uni.chooseImage({
@@ -174,57 +148,76 @@
};
//
const onCancel = (value) => {
avatarCropperStatus.value = false
}
avatarCropperStatus.value = false;
};
//
const onConfirm = (file) => {
avatarCropperStatus.value = false
avatarCropperStatus.value = false;
nextTick(async () => {
common.loading('更新头像中')
common.loading('更新头像中');
try {
const data = {
bizScen: 'S3007',
thumbnailFlag: true,
thumbImgSize: 100
}
};
//
const res = await uploadFile(file.tempFilePath, data)
const res = await uploadFile(file.tempFilePath, data);
//
await updateDfSysUserExtandInfoImageAddr({
imageAddr: res.thumbFileId
})
});
//
const userData = await queryCurrentUser()
common.msg('修改成功')
userInfo.value.imageAddr = userData.imageAddr
userInfo.value.imagePath = ''
const userData = await queryCurrentUser();
common.msg('修改成功');
userInfo.value.imageAddr = userData.imageAddr;
userInfo.value.imagePath = '';
} catch {
common.msg('修改失败')
common.msg('修改失败');
}
common.hideLoading()
})
}
common.hideLoading();
});
};
//
const sexChange = (e) => {
userInfo.value.gender = sexOptions[e.detail.value].value;
common.loading("修改中")
const sexConfirm = (e) => {
userInfo.value.gender = e.value[0].value;
common.loading('修改中');
updateDfSysUserExtandInfoGender({
gender: userInfo.value.gender
}).then(async () => {
await queryCurrentUser()
common.msg('修改成功')
}).finally(() => {
common.hideLoading()
})
.then(async () => {
await queryCurrentUser();
common.msg('修改成功');
})
.finally(() => {
common.hideLoading();
});
};
// const sexChange = (e) => {
// userInfo.value.gender = sexOptions[e.detail.value].value;
// console.log('userInfo.value.gender', userInfo.value.gender);
// common.loading('');
// updateDfSysUserExtandInfoGender({
// gender: userInfo.value.gender
// })
// .then(async () => {
// await queryCurrentUser();
// common.msg('');
// })
// .finally(() => {
// common.hideLoading();
// });
// };
const anonymousChange = (e) => {
userInfo.value.anonymous = anonymousOptions[e.detail.value].value;
// common.loading("")
common.msg('修改成功')
common.msg('修改成功');
};
onLoad((params) => {
userInfo.value = getUserInfo()
userInfo.value = getUserInfo();
console.log('userInfo.value', userInfo.value);
});
</script>
@@ -255,7 +248,7 @@
}
.detail-main {
background-color: #F6F8FF;
background-color: #f6f8ff;
}
.title {
@@ -303,4 +296,4 @@
}
}
}
</style>
</style>
-2
View File
@@ -22,7 +22,6 @@
</template>
<script setup>
import watermark from '@/components/jm-watermark/jm-watermark.vue'
import common from '@/common/common';
import {
get_base_url
@@ -30,7 +29,6 @@
const url = get_base_url();
const ENV = import.meta.env
const appBaseInfo = uni.getAppBaseInfo()
console.log('appBaseInfo', appBaseInfo);
import showDialog from '@/common/dialogMessage'
const bbbb = () => {
+33
View File
@@ -0,0 +1,33 @@
## 1.0.142023-12-29
1. 修复上个版本引出的BUG
## 1.0.132023-12-26
1. 修复抖音小程序滚到底不触发change的BUG
## 1.0.122023-11-20
1. 修复issues反馈的问题uv-picker在组合式API的自定义组件中,columns动态赋值无法显示选项:https://gitee.com/climblee/uv-ui/issues/I8H0GQ
## 1.0.112023-10-11
1. 将immediate-change默认值改为true,该值在于change回调的及时性,微信小程序生效
## 1.0.102023-08-25
1. 增加round属性设置弹窗圆角,默认为0
## 1.0.92023-08-24
1. 修复cli项目不返回值的问题
## 1.0.82023-08-04
1. 优化
## 1.0.72023-08-02
1. 改组件中删除uv-toolbar组件,请单独下载uv-toolbar组件
## 1.0.62023-07-02
uv-picker 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/picker.html
## 1.0.52023-06-26
1. 增加color参数
2. 增加activeColor参数
## 1.0.42023-06-15
1. 修改支付宝报错的BUG
## 1.0.32023-06-12
1. setColumnValues的使用统一化,避免某些平台报错
2. 取消change回调回传的组件实例,直接统一通过ref的方式调取setColumnValues方法
## 1.0.22023-05-23
1. uv-toolbar组件新增下边框属性
## 1.0.12023-05-16
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.02023-05-10
uv-picker 选择器
@@ -0,0 +1,95 @@
export default {
props: {
// 是否展示顶部的操作栏
showToolbar: {
type: Boolean,
default: true
},
// 顶部标题
title: {
type: String,
default: ''
},
// 弹窗圆角
round: {
type: [String, Number],
default: 0
},
// 对象数组,设置每一列的数据
columns: {
type: Array,
default: () => []
},
// 是否显示加载中状态
loading: {
type: Boolean,
default: false
},
// 各列中,单个选项的高度
itemHeight: {
type: [String, Number],
default: 44
},
// 取消按钮的文字
cancelText: {
type: String,
default: '取消'
},
// 确认按钮的文字
confirmText: {
type: String,
default: '确定'
},
// 取消按钮的颜色
cancelColor: {
type: String,
default: '#909193'
},
// 确认按钮的颜色
confirmColor: {
type: String,
default: '#3c9cff'
},
// 文字颜色
color: {
type: String,
default: ''
},
// 选中文字的颜色
activeColor: {
type: String,
default: ''
},
// 每列中可见选项的数量
visibleItemCount: {
type: [String, Number],
default: 5
},
// 选项对象中,需要展示的属性键名
keyName: {
type: String,
default: 'text'
},
// 是否允许点击遮罩关闭选择器
closeOnClickOverlay: {
type: Boolean,
default: true
},
// 是否允许点击确认关闭选择器
closeOnClickConfirm: {
type: Boolean,
default: true
},
// 各列的默认索引
defaultIndex: {
type: Array,
default: () => [],
},
// 是否在手指松开时立即触发 change 事件。若不开启则会在滚动动画结束后触发 change 事件,只在微信2.21.1及以上有效
immediateChange: {
type: Boolean,
default: true
},
...uni.$uv?.props?.picker
}
}
@@ -0,0 +1,330 @@
<template>
<uv-popup
ref="pickerPopup"
mode="bottom"
:round="round"
:close-on-click-overlay="closeOnClickOverlay"
@change="popupChange"
>
<view class="uv-picker">
<uv-toolbar
v-if="showToolbar"
:cancelColor="cancelColor"
:confirmColor="confirmColor"
:cancelText="cancelText"
:confirmText="confirmText"
:title="title"
@cancel="cancel"
@confirm="confirm"
></uv-toolbar>
<!-- #ifdef MP-TOUTIAO -->
<picker-view
class="uv-picker__view"
:indicatorStyle="`height: ${$uv.addUnit(itemHeight)}`"
:value="innerIndex"
:immediateChange="immediateChange"
:style="{
height: `${$uv.addUnit(visibleItemCount * itemHeight)}`
}"
@pickend="changeHandler"
>
<!-- #endif -->
<!-- #ifndef MP-TOUTIAO -->
<picker-view
class="uv-picker__view"
:indicatorStyle="`height: ${$uv.addUnit(itemHeight)}`"
:value="innerIndex"
:immediateChange="immediateChange"
:style="{
height: `${$uv.addUnit(visibleItemCount * itemHeight)}`
}"
@change="changeHandler"
>
<!-- #endif -->
<!-- @pickend在这里为了解决抖音等滚到底不触发change兼容性问题 -->
<picker-view-column
v-for="(item, index) in innerColumns"
:key="index"
class="uv-picker__view__column"
>
<text
v-if="$uv.test.array(item)"
class="uv-picker__view__column__item uv-line-1"
v-for="(item1, index1) in item"
:key="index1"
:style="[{
height: $uv.addUnit(itemHeight),
lineHeight: $uv.addUnit(itemHeight),
fontWeight: index1 === innerIndex[index] ? 'bold' : 'normal'
},textStyle(index,index1)]"
>{{ getItemText(item1) }}</text>
</picker-view-column>
</picker-view>
<view
v-if="loading"
class="uv-picker--loading"
>
<uv-loading-icon mode="circle"></uv-loading-icon>
</view>
</view>
</uv-popup>
</template>
<script>
/**
* uv-picker
* @description 选择器
* @property {Boolean} showToolbar 是否显示顶部的操作栏默认 true
* @property {String} title 顶部标题
* @property {Array} columns 对象数组设置每一列的数据
* @property {Boolean} loading 是否显示加载中状态默认 false
* @property {String | Number} itemHeight 各列中单个选项的高度默认 44
* @property {String} cancelText 取消按钮的文字默认 '取消'
* @property {String} confirmText 确认按钮的文字默认 '确定'
* @property {String} cancelColor 取消按钮的颜色默认 '#909193'
* @property {String} confirmColor 确认按钮的颜色默认 '#3c9cff'
* @property {String} color 文字颜色默认 ''
* @property {String} activeColor 选中文字的颜色默认 ''
* @property {String | Number} visibleItemCount 每列中可见选项的数量默认 5
* @property {String} keyName 选项对象中需要展示的属性键名默认 'text'
* @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭选择器默认 false
* @property {Array} defaultIndex 各列的默认索引
* @property {Boolean} immediateChange 是否在手指松开时立即触发change事件默认 false
* @event {Function} close 关闭选择器时触发
* @event {Function} cancel 点击取消按钮触发
* @event {Function} change 当选择值变化时触发
* @event {Function} confirm 点击确定按钮返回当前选择的值
*/
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import props from './props.js';
export default {
name: 'uv-picker',
emits: ['confirm','cancel','close','change'],
mixins: [mpMixin, mixin, props],
computed: {
//
textStyle(){
return (index,index1) => {
const style = {};
// #ifndef APP-NVUE
style.display = 'block';
// #endif
if(this.color) {
style.color = this.color;
}
if(this.activeColor && index1 === this.innerIndex[index]) {
style.color = this.activeColor;
}
return style;
}
}
},
data() {
return {
//
lastIndex: [],
// picker-viewvalue
innerIndex: [],
//
innerColumns: [],
//
columnIndex: 0,
}
},
watch: {
//
defaultIndex: {
immediate: true,
handler(n) {
this.setIndexs(n, true)
}
},
// columns
columns: {
deep: true,
immediate: true,
handler(n) {
this.setColumns(n)
}
},
},
methods: {
open() {
this.$refs.pickerPopup.open();
},
close() {
this.$refs.pickerPopup.close();
},
popupChange(e) {
if(!e.show) this.$emit('close');
},
// item
getItemText(item) {
if (this.$uv.test.object(item)) {
return item[this.keyName]
} else {
return item
}
},
//
cancel() {
this.$emit('cancel');
this.close();
},
//
confirm() {
// 使deepClonevue3cli
this.$emit('confirm', this.$uv.deepClone({
indexs: this.innerIndex,
value: this.innerColumns.map((item, index) => item[this.innerIndex[index]]),
values: this.innerColumns
}));
if(this.closeOnClickConfirm) {
this.close();
}
},
//
changeHandler(e) {
const {
value
} = e.detail
let index = 0,
columnIndex = 0
//
for (let i = 0; i < value.length; i++) {
let item = value[i]
if (item !== (this.lastIndex[i] || 0)) { // undefined0
// columnIndex
columnIndex = i
// index
index = item
break // 使
}
}
this.columnIndex = columnIndex
const values = this.innerColumns
// ""
this.setLastIndex(value)
this.setIndexs(value)
this.$emit('change', {
value: this.innerColumns.map((item, index) => item[value[index]]),
index,
indexs: value,
// values
values,
columnIndex
})
},
// index
setIndexs(index, setLastIndex) {
this.innerIndex = this.$uv.deepClone(index)
if (setLastIndex) {
this.setLastIndex(index)
}
},
//
setLastIndex(index) {
// changeHandler
//
this.lastIndex = this.$uv.deepClone(index)
},
//
setColumnValues(columnIndex, values) {
// innerColumnscolumnIndexvalues使splice
this.innerColumns.splice(columnIndex, 1, values)
// innerIndex0
let tmpIndex = this.$uv.deepClone(this.innerIndex)
for (let i = 0; i < this.innerColumns.length; i++) {
if (i > this.columnIndex) {
tmpIndex[i] = 0
}
}
//
this.setIndexs(tmpIndex)
},
//
getColumnValues(columnIndex) {
// changesetColumnValues
// changegetColumnValues
(async () => {
await this.$uv.sleep()
})()
return this.innerColumns[columnIndex]
},
// columns
setColumns(columns) {
this.innerColumns = this.$uv.deepClone(columns)
// defaultIndex0
if (this.innerIndex.length === 0) {
this.innerIndex = new Array(columns.length).fill(0)
}
},
//
getIndexs() {
return this.innerIndex
},
//
getValues() {
// changesetColumnValues
// changegetValues
(async () => {
await this.$uv.sleep()
})()
return this.innerColumns.map((item, index) => item[this.innerIndex[index]])
}
},
}
</script>
<style lang="scss" scoped>
$show-lines: 1;
@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
.uv-picker {
position: relative;
&__view {
&__column {
@include flex;
flex: 1;
justify-content: center;
&__item {
@include flex;
justify-content: center;
align-items: center;
font-size: 16px;
text-align: center;
/* #ifndef APP-NVUE */
display: block;
/* #endif */
color: $uv-main-color;
&--disabled {
/* #ifndef APP-NVUE */
cursor: not-allowed;
/* #endif */
opacity: 0.35;
}
}
}
}
&--loading {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
@include flex;
justify-content: center;
align-items: center;
background-color: rgba(255, 255, 255, 0.87);
z-index: 1000;
}
}
</style>
+90
View File
@@ -0,0 +1,90 @@
{
"id": "uv-picker",
"displayName": "uv-picker 选择器 全面兼容vue3+2、app、h5、小程序等多端",
"version": "1.0.14",
"description": "uv-picker 此选择器用于单列,多列,多列联动的选择场景...",
"keywords": [
"uv-picker",
"uvui",
"uv-ui",
"picker",
"联动选择"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools",
"uv-popup",
"uv-loading-icon",
"uv-toolbar"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
## Picker 选择器
> **组件名:uv-picker**
此选择器用于单列,多列,多列联动的选择场景。
`uv-datetime-picker`等组件也用到了该组件,功能完善,需要特别注意的是`columns`参数的形式是数组嵌套。
# <a href="https://www.uvui.cn/components/picker.html" target="_blank">查看文档</a>
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP</small>
### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
+2
View File
@@ -0,0 +1,2 @@
## 1.0.02023-08-02
1. 新增工具条组件
@@ -0,0 +1,40 @@
export default {
props: {
// 是否展示工具条
show: {
type: Boolean,
default: true
},
// 是否显示下边框
showBorder: {
type: Boolean,
default: false
},
// 取消按钮的文字
cancelText: {
type: String,
default: '取消'
},
// 确认按钮的文字
confirmText: {
type: String,
default: '确认'
},
// 取消按钮的颜色
cancelColor: {
type: String,
default: '#909193'
},
// 确认按钮的颜色
confirmColor: {
type: String,
default: '#3c9cff'
},
// 标题文字
title: {
type: String,
default: ''
},
...uni.$uv?.props?.toolbar
}
}
@@ -0,0 +1,109 @@
<template>
<view
:class="['uv-toolbar',{'uv-border-bottom':showBorder}]"
@touchmove.stop.prevent="noop"
v-if="show"
>
<view
class="uv-toolbar__cancel__wrapper"
hover-class="uv-hover-class"
>
<text
class="uv-toolbar__wrapper__cancel"
@tap="cancel"
:style="{
color: cancelColor
}"
>{{ cancelText }}</text>
</view>
<text
class="uv-toolbar__title uv-line-1"
v-if="title"
>{{ title }}</text>
<view
class="uv-toolbar__confirm__wrapper"
hover-class="uv-hover-class"
>
<text
class="uv-toolbar__wrapper__confirm"
@tap="confirm"
:style="{
color: confirmColor
}"
>{{ confirmText }}</text>
</view>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import props from './props.js';
/**
* Toolbar 工具条
* @description
* @tutorial https://www.uvui.cn/components/toolbar.html
* @property {Boolean} show 是否展示工具条默认 true
* @property {Boolean} showBorder 是否展示工具条下方边框默认 false
* @property {String} cancelText 取消按钮的文字默认 '取消'
* @property {String} confirmText 确认按钮的文字默认 '确认'
* @property {String} cancelColor 取消按钮的颜色默认 '#909193'
* @property {String} confirmColor 确认按钮的颜色默认 '#3c9cff'
* @property {String} title 标题文字
* @event {Function}
* @example
*/
export default {
name: 'uv-toolbar',
emits: ['confirm', 'cancel'],
mixins: [mpMixin, mixin, props],
methods: {
//
cancel() {
this.$emit('cancel')
},
//
confirm() {
this.$emit('confirm')
}
}
}
</script>
<style lang="scss" scoped>
$show-lines: 1;
$show-hover: 1;
$show-border: 1;
$show-border-bottom: 1;
@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
$uv-tips-color: #909193 !default;
$uv-main-color: #303133 !default;
$uv-primary: #3c9cff !default;
.uv-toolbar {
height: 42px;
@include flex;
justify-content: space-between;
align-items: center;
&__wrapper {
&__cancel {
color: $uv-tips-color;
font-size: 15px;
padding: 0 15px;
}
}
&__title {
color: $uv-main-color;
padding: 0 60rpx;
font-size: 16px;
flex: 1;
text-align: center;
}
&__wrapper {
&__confirm {
color: $uv-primary;
font-size: 15px;
padding: 0 15px;
}
}
}
</style>
+87
View File
@@ -0,0 +1,87 @@
{
"id": "uv-toolbar",
"displayName": "uv-toolbar 工具条",
"version": "1.0.0",
"description": "该组价是仅用于uv-ui中一个公共小工具,提供一个取消和确定的样式,可以设置标题,主要用于弹窗顶部的选择确定工具条",
"keywords": [
"uv-toolbar",
"uvui",
"uv-ui",
"工具条",
"工具"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}
+31
View File
@@ -0,0 +1,31 @@
## Toolbar 工具条
> **组件名:uv-toolbar**
该组价是仅用于uv-ui中一个公共小工具,提供一个取消和确定的样式,可以设置标题,主要用于弹窗顶部的选择确定工具条。
### 基本使用
```vue
<uv-toolbar title="标题文字"></uv-toolbar>
```
### Toolbar Props
| 属性名 | 类型 | 默认值 | 说明 |
|:-|:-|:-|:-|
| show | Boolean | true | 是否展示工具条 |
| showBorder | Boolean | false | 是否显示下边框 |
| cancelText | String | '取消' | 取消按钮的文字 |
| confirmText | String | '确定' | 确定按钮的文字 |
| cancelColor | String | '#909193' | 取消按钮的颜色 |
| confirmColor | String | '#3c9cff' | 确认按钮的颜色 |
| title | String | - | 标题文字 |
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>