修改语音按钮获取录音授权

This commit is contained in:
田岩
2025-09-19 15:20:13 +08:00
parent a895c928bd
commit f5bdba03dc
15 changed files with 1538 additions and 1666 deletions
+1 -45
View File
@@ -1,50 +1,6 @@
# 开发环境
ENV = 'development'
# 'development'
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
# UAT
# VITE_APP_BASE_API_Url = 'http://25.18.122.78:9786'
# DEV
# app入口
# VITE_APP_BASE_API_Url = 'http://25.16.122.65:7001'
# VITE_APP_BASE_API_Url = 'http://25.16.122.92:9786'
# VITE_APP_BASE_API_Url = 'http://25.18.122.66:9786'
# h5专用地址x2
# VITE_APP_BASE_H5_API_Url = 'http://25.16.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;
+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,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>
+130
View File
@@ -0,0 +1,130 @@
<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??'');
console.log('userInfo', userInfo, this.textValue);
console.log('textValue', this.textValue);
},
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>
+1066 -1010
View File
File diff suppressed because it is too large Load Diff
+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';
-1
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