Merge branch 'develop' of http://25.13.9.101:9000/K17_AITS/tra-app into develop
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
<script>
|
||||
|
||||
export default {
|
||||
onLaunch: function () {
|
||||
uni.onTabBarMidButtonTap(() => {
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,10 @@
|
||||
import RecordPermission from '@/common/recordPermission.js'
|
||||
|
||||
export const initRecord = () => RecordPermission.requestPermission();
|
||||
|
||||
|
||||
export default function useIndexList() {
|
||||
return {
|
||||
|
||||
};
|
||||
}
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
@@ -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)); /* 宽度充满,高度自适应比例 */
|
||||
|
||||
@@ -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: '黑铁',
|
||||
|
||||
@@ -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>
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
## 1.0.14(2023-12-29)
|
||||
1. 修复上个版本引出的BUG
|
||||
## 1.0.13(2023-12-26)
|
||||
1. 修复抖音小程序滚到底不触发change的BUG
|
||||
## 1.0.12(2023-11-20)
|
||||
1. 修复issues反馈的问题uv-picker在组合式API的自定义组件中,columns动态赋值无法显示选项:https://gitee.com/climblee/uv-ui/issues/I8H0GQ
|
||||
## 1.0.11(2023-10-11)
|
||||
1. 将immediate-change默认值改为true,该值在于change回调的及时性,微信小程序生效
|
||||
## 1.0.10(2023-08-25)
|
||||
1. 增加round属性设置弹窗圆角,默认为0
|
||||
## 1.0.9(2023-08-24)
|
||||
1. 修复cli项目不返回值的问题
|
||||
## 1.0.8(2023-08-04)
|
||||
1. 优化
|
||||
## 1.0.7(2023-08-02)
|
||||
1. 改组件中删除uv-toolbar组件,请单独下载uv-toolbar组件
|
||||
## 1.0.6(2023-07-02)
|
||||
uv-picker 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/picker.html
|
||||
## 1.0.5(2023-06-26)
|
||||
1. 增加color参数
|
||||
2. 增加activeColor参数
|
||||
## 1.0.4(2023-06-15)
|
||||
1. 修改支付宝报错的BUG
|
||||
## 1.0.3(2023-06-12)
|
||||
1. setColumnValues的使用统一化,避免某些平台报错
|
||||
2. 取消change回调回传的组件实例,直接统一通过ref的方式调取setColumnValues方法
|
||||
## 1.0.2(2023-05-23)
|
||||
1. uv-toolbar组件新增下边框属性
|
||||
## 1.0.1(2023-05-16)
|
||||
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
|
||||
2. 优化部分功能
|
||||
## 1.0.0(2023-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-view的value
|
||||
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() {
|
||||
// 在这里使用deepClone拷贝后,vue3会自动转换成原始对象,这样处理是因为cli项目可能出现不返回值的情况
|
||||
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)) { // 把undefined转为合法假值0
|
||||
// 设置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) {
|
||||
// 替换innerColumns数组中columnIndex索引的值为values,使用的是数组的splice方法
|
||||
this.innerColumns.splice(columnIndex, 1, values)
|
||||
// 拷贝一份原有的innerIndex做临时变量,将大于当前变化列的所有的列的默认索引设置为0
|
||||
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) {
|
||||
// 进行同步阻塞,因为外部得到change事件之后,可能需要执行setColumnValues更新列的值
|
||||
// 索引如果在外部change的回调中调用getColumnValues的话,可能无法得到变更后的列值,这里进行一定延时,保证值的准确性
|
||||
(async () => {
|
||||
await this.$uv.sleep()
|
||||
})()
|
||||
return this.innerColumns[columnIndex]
|
||||
},
|
||||
// 设置整体各列的columns的值
|
||||
setColumns(columns) {
|
||||
this.innerColumns = this.$uv.deepClone(columns)
|
||||
// 如果在设置各列数据时,没有被设置默认的各列索引defaultIndex,那么用0去填充它,数组长度为列的数量
|
||||
if (this.innerIndex.length === 0) {
|
||||
this.innerIndex = new Array(columns.length).fill(0)
|
||||
}
|
||||
},
|
||||
// 获取各列选中值对应的索引
|
||||
getIndexs() {
|
||||
return this.innerIndex
|
||||
},
|
||||
// 获取各列选中的值
|
||||
getValues() {
|
||||
// 进行同步阻塞,因为外部得到change事件之后,可能需要执行setColumnValues更新列的值
|
||||
// 索引如果在外部change的回调中调用getValues的话,可能无法得到变更后的列值,这里进行一定延时,保证值的准确性
|
||||
(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>
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
|
||||

|
||||
|
||||
</a>
|
||||
|
||||
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
|
||||
@@ -0,0 +1,2 @@
|
||||
## 1.0.0(2023-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>
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||

|
||||
|
||||
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
|
||||
Reference in New Issue
Block a user