Authored by Rickie

111

Showing 94 changed files with 4761 additions and 0 deletions

Too many changes to show.

To preserve performance only 94 of 94+ files are displayed.

/unpackage
.DS_Store
.vscode
\ No newline at end of file
... ...
<script>
export default {
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
</style>
... ...
import { request } from '../utils/request.js';
export const getDeviceList = () => {
return request({
url: '/device/list',
method: 'GET',
});
};
// 登录
export const loginApi = (data) => {
return request({
url: '/api/login',
method: 'POST',
data,
});
};
// 获取课程列表
export const courseListApi = (data) => {
return request({
url: '/api/course/get-list',
method: 'GET',
data,
});
};
// 获取选班数据
export const getSelectApi = (data) => {
return request({
url: '/api/course/get-detail',
method: 'POST',
data,
});
};
// 获取我的孩子
export const myStudentsApi = (data) => {
return request({
url: '/api/student/my-students',
method: 'GET',
data,
});
};
// 添加我的孩子
export const addStudentsApi = (data) => {
return request({
url: '/api/student/create',
method: 'POST',
data,
});
};
// 报名
export const signUpApi = (data) => {
return request({
url: '/api/course/sign-up',
method: 'POST',
data,
});
};
\ No newline at end of file
... ...
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>
... ...
import App from './App'
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
... ...
{
"name" : "learn-tiny",
"appid" : "__UNI__F0A7B80",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* 模块配置 */
"modules" : {},
/* 应用发布信息 */
"distribute" : {
/* android打包配置 */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios打包配置 */
"ios" : {},
/* SDK配置 */
"sdkConfigs" : {}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "wxa0a9919d4066da95",
"setting" : {
"urlCheck" : false,
"minified" : true
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3"
}
... ...
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "pages/my/my",
"style": {
"navigationBarTitleText": "我的"
}
},
{
"path" : "pages/login/login",
"style" :
{
"navigationBarTitleText" : "登录"
}
},
{
"path": "pages/signUp/signUp",
"style": {
"navigationBarTitleText": "报名"
}
},
{
"path": "pages/addStudent/addStudent",
"style": {
"navigationBarTitleText": "添加孩子"
}
}
],
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#f96118",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/tabBar/home.png",
"selectedIconPath": "static/tabBar/homeAc.png",
"text": "发现"
}, {
"pagePath": "pages/my/my",
"iconPath": "static/tabBar/my.png",
"selectedIconPath": "static/tabBar/myAc.png",
"text": "家长"
}]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#eaecfb"
},
"uniIdRouter": {}
}
\ No newline at end of file
... ...
<template>
<view class="container">
<uv-form labelPosition="left" :model="state.studentInfo" :rules="state.rules" ref="formRef" class="form-box">
<uv-form-item prop="name">
<uv-input v-model="state.studentInfo.name" shape="circle" placeholder="请输入学生真实姓名">
</uv-input>
</uv-form-item>
<uv-form-item prop="sex">
<view style="display: flex;justify-content: space-around;width: 100%;">
<view @click="state.studentInfo.sex = 0" class="select-item" :style="state.studentInfo.sex === 0 ? 'border-color:#6b75f6;' : ''">
<uv-icon name="man" color="#7175f0" size="16" style="margin-right: 10rpx;"></uv-icon>男
</view>
<view @click="state.studentInfo.sex = 1" class="select-item" :style="state.studentInfo.sex === 1 ? 'border-color:#6b75f6;' : ''">
<uv-icon name="woman" color="#ff5d5c" size="16" style="margin-right: 10rpx;"></uv-icon>女
</view>
<view @click="state.studentInfo.sex = 2" class="select-item" :style="state.studentInfo.sex === 2 ? 'border-color:#6b75f6;' : ''">
<uv-icon name="lock" color="#7175f0" size="16" style="margin-right: 10rpx;"></uv-icon>保密
</view>
</view>
</uv-form-item>
<uv-form-item prop="city">
<uv-input v-model="state.studentInfo.city" shape="circle" placeholder="请输入学生所在城市" />
</uv-form-item>
<uv-form-item prop="school">
<uv-input v-model="state.studentInfo.school" shape="circle" placeholder="请输入学生在读学校" />
</uv-form-item>
<uv-form-item prop="grade">
<uv-input v-model="state.studentInfo.grade" shape="circle" placeholder="请输入学生在读年级" />
</uv-form-item>
<uv-form-item>
<view @click="submit"
style="width: 100%;background-color: #7175f0;color: #fff;text-align: center;padding: 20rpx 0;border-radius: 9999rpx;">
确认添加
</view>
</uv-form-item>
</uv-form>
</view>
</template>
<script setup>
import {
reactive,
ref
} from "vue"
import { addStudentsApi } from "@/api/index.js"
const formRef = ref(null)
const state = reactive({
studentInfo: {
name: "",
sex: "",
city: "",
school: "",
grade: ""
},
rules: {
'name': {
type: 'string',
required: true,
message: '请输入学生真实姓名',
trigger: ['blur', 'change']
},
'sex': {
type: 'number',
required: true,
message: '请选择性别',
trigger: ['blur', 'change']
},
'city': {
type: 'string',
required: true,
message: '请输入学生所在城市',
trigger: ['blur', 'change']
},
'school': {
type: 'string',
required: true,
message: '请输入学生在读学校',
trigger: ['blur', 'change']
},
'grade': {
type: 'string',
required: true,
message: '请输入学生在读年级',
trigger: ['blur', 'change']
},
}
})
const submit = async () => {
try {
await formRef.value.validate()
const res = await addStudentsApi(state.studentInfo)
uni.showToast({
icon: 'success',
title: '添加成功'
})
formRef.value.resetFields()
uni.navigateBack()
} catch {}
}
</script>
<style lang="scss" scoped>
.container {
background: linear-gradient(135deg, #eaecfb 10%, #f6f7fb 90%);
background-color: red;
height: calc(100vh - var(--tab-bar-height));
overflow-y: auto;
padding: 0 24rpx;
font-size: 28rpx;
}
.form-box {
margin-top: 80rpx;
background-color: #fff;
padding: 20rpx;
border-radius: 40rpx;
}
.select-item {
background-color: #e9ecfc;
width: 28%;
display: flex;
justify-content: center;
align-items: center;
padding: 10rpx 0;
border-radius: 9999rpx;
border: 3rpx solid #e8ecfd;
}
</style>
\ No newline at end of file
... ...
<template>
<view class="container">
 <uv-navbar leftIcon="" placeholder bgColor="rgba(255, 255, 255, 0)">
<template v-slot:left>
<view class="uv-nav-slot">
XXXX
</view>
</template>
</uv-navbar>
<view class="content-box">
<view style="display: grid;grid-template-columns: 47.5% 47.5%;gap: 5%;">
<view @click="showGradesPop" class="select-item">
{{ state.grade }}<uv-icon name="arrow-down-fill" color="#7175f0" size="20"
style="margin-left: 6rpx;"></uv-icon>
</view>
<view @click="showAccountsPop" class="select-item">
{{ state.accounts }}<uv-icon name="arrow-down-fill" color="#7175f0" size="20" style="margin-left: 6rpx;"></uv-icon>
</view>
</view>
<view class="course-list">
<view v-for="item in state.courses" class="course-box">
<image :src="item.img" style="width: 160rpx;height: 160rpx;border-radius: 20rpx;"></image>
<view style="flex: 1;display: flex;">
<view style="flex: 1;padding: 0 20rpx;display: flex;flex-direction: column;justify-content: space-between;">
<view style="font-size: 30rpx;">{{ item.name }} - {{ item.subject }}</view>
<view style="color: #ff3838;font-weight: 600;">¥{{ item.price }}</view>
</view>
<view style="width: 160rpx;display: flex;align-items: center;justify-content: flex-end;">
<view @click="gotoSignUp(item.id)" style="width: 100%;;background-color: #f57c28;color: #fff;text-align: center;padding: 10rpx 0;border-radius: 9999rpx;">
去报名
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 年级弹窗 -->
<uv-popup ref="gradesRef" safeAreaInsetBottom closeable :round="20">
<view class="pop-box">
<view class="content-pop">
<view @click="selectGrade('全部年级')" class="pop-inner-item" style="margin: 20rpx 0;" :style="state.grade === '全部年级' ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''">
全部年级
</view>
<view v-for="item in state.gradOptions" style="margin-bottom: 30rpx;">
<view class="pop-title">
{{ item.title }}
</view>
<view class="pop-inner">
<view v-for="item1 in item.content" @click="selectGrade(item1)" class="pop-inner-item"
:style="state.grade === item1 ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''">
{{ item1 }}
</view>
</view>
</view>
</view>
</view>
</uv-popup>
<!-- 科目弹窗 -->
<uv-popup ref="accountsRef" safeAreaInsetBottom closeable :round="20">
<view class="pop-box">
<view class="content-pop">
<view @click="selectaccounts('全部科目')" class="pop-inner-item" style="margin: 20rpx 0;" :style="state.accounts === '全部科目' ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''">
全部科目
</view>
<view class="pop-inner">
<view v-for="item1 in state.accountsOptions" @click="selectaccounts(item1)" class="pop-inner-item"
:style="state.accounts === item1 ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''">
{{ item1 }}
</view>
</view>
</view>
</view>
</uv-popup>
</view>
</template>
<script setup>
import {
reactive,
ref,
onMounted
} from 'vue';
import { courseListApi } from "@/api/index.js"
const gradesRef = ref(null)
const accountsRef = ref(null)
const state = reactive({
grade: "全部年级",
accounts: "全部科目",
courses: [],
subject: [],
gradOptions: [{
title: "小学",
content: ["一年级", "二年级", "三年级", "四年级", "五年级", "六年级"]
},
{
title: "初中",
content: ["初一", "初二", "初三"]
},
{
title: "高中",
content: ["高一", "高二", "高三"]
}
],
accountsOptions: ["语文", "数学", "英语", "物理", "化学"]
})
onMounted(() => {
getCourseList()
})
const gotoSignUp = id => {
uni.navigateTo({
url: `/pages/signUp/signUp?id=${id}`
})
}
const getCourseList = async () => {
try {
const { data } = await courseListApi({
grade: state.grade === "全部年级" ? "" : state.grade,
subject: state.accounts === "全部科目" ? "" : state.accounts
})
state.courses = data.courses
} catch {}
}
const showGradesPop = () => {
gradesRef.value.open('bottom')
}
const showAccountsPop = () => {
accountsRef.value.open('bottom')
}
const selectGrade = item => {
state.grade = item
gradesRef.value.close()
getCourseList()
}
const selectaccounts = item => {
state.accounts = item
accountsRef.value.close()
getCourseList()
}
</script>
<style lang="scss" scoped>
.container {
background: linear-gradient(135deg, #d4d6fe 10%, #f6f7fb 90%);
background-color: red;
height: calc(100vh - var(--tab-bar-height));
display: flex;
flex-direction: column;
}
.content-box {
flex: 1;
overflow-y: auto;
padding: 0 24rpx;
display: flex;
flex-direction: column;
.course-list {
margin-top: 20rpx;
flex: 1;
.course-box {
display: flex;
justify-content: space-between;
margin-bottom: 30rpx;
background-color: #fff;
padding: 20rpx;
border-radius: 20rpx;
}
}
}
.select-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
padding: 16rpx 0;
border-radius: 9999rpx;
}
.pop-box {
padding: 70rpx 20rpx;
padding-bottom: calc(var(--tab-bar-height) + 20rpx);
.content-pop {
max-height: 40vh;
min-height: 200rpx;
overflow-y: auto;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
}
.pop-title {
margin-bottom: 20rpx;
position: relative;
padding-left: 12rpx;
&:before {
content: "";
width: 6rpx;
height: 28rpx;
border-radius: 3rpx;
background-color: #ffb50f;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
}
}
.pop-inner {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: (20rpx);
}
.pop-inner-item {
font-size: 28rpx;
border: 2rpx solid #ccc;
text-align: center;
padding: 10rpx 0;
border-radius: 9999rpx;
}
</style>
\ No newline at end of file
... ...
<template>
<view class="container">
<view class="input-box">
<uv-form labelPosition="left" :model="state.userInfo" :rules="state.rules" labelWidth="60" ref="formRef"
:labelStyle="{ fontSize: '28rpx' }">
<view style="margin-bottom: 60rpx;">
欢迎登录XXXXXXXX!
</view>
<uv-form-item label="姓名" prop="name" borderBottom>
<uv-input v-model="state.userInfo.name" placeholder="请输入姓名" border="none">
</uv-input>
</uv-form-item>
<uv-form-item label="手机号" prop="phonenumber" borderBottom>
<uv-input v-model="state.userInfo.phonenumber" placeholder="请输入手机号" border="none"></uv-input>
</uv-form-item>
<view @click="loginHandler" class="login-btn">
登 录
</view>
</uv-form>
</view>
</view>
</template>
<script setup>
import {
reactive,
ref
} from 'vue';
import {
loginApi
} from "@/api/index.js"
const formRef = ref(null)
const state = reactive({
userInfo: {
name: "",
phonenumber: ""
},
rules: {
'name': {
type: 'string',
required: true,
message: '请输入姓名',
trigger: ['blur', 'change']
},
'phonenumber': [{
type: 'string',
required: true,
message: '请输入手机号',
trigger: ['blur', 'change']
}, {
validator: (rule, value, callback) => {
const mobileRegexLoose = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/
return mobileRegexLoose.test(value);
},
message: '手机号码不正确',
trigger: ['blur'],
}],
}
})
const loginHandler = async () => {
try {
await formRef.value.validate()
const {
token
} = await loginApi(state.userInfo)
uni.showToast({
icon: 'success',
title: '登录成功'
})
uni.setStorageSync('token', token)
uni.navigateBack()
} catch {}
}
</script>
<style lang="scss" scoped>
.container {
background: linear-gradient(135deg, #eaecfb 10%, #f6f7fb 90%);
background-color: red;
height: 100vh;
overflow-y: auto;
padding: 0 28rpx;
}
.input-box {
position: relative;
margin-top: 120rpx;
padding: 48rpx;
padding-top: 80rpx;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 0 20rpx #eee;
}
::v-deep .uv-line {
border-bottom-style: dashed !important;
}
.login-btn {
margin-top: 80rpx;
background-color: #8386ef;
color: #fff;
text-align: center;
padding: 16rpx 0;
border-radius: 20rpx;
}
</style>
\ No newline at end of file
... ...
<template>
<view class="container">
<!-- 自定义标题栏 -->
<uv-navbar leftIcon="" placeholder bgColor="/static/topBg.png">
<template v-slot:center>
<view class="uv-nav-slot">
<image src="/static/my/myTitle.png" mode="heightFix" style="height: 44rpx;"></image>
</view>
</template>
</uv-navbar>
<view class="content-box">
<div class="content-padding">
<!-- 用户信息 -->
<view class="user-info">
<view class="center-avatar">
<view class="center-inner">
<image src="/static/my/avatar.png" style="width: 100%;height: 100%;"></image>
</view>
</view>
<view class="loginTo-btn">
登录/注册
</view>
</view>
<view class="nav-list">
<view class="nav-item">
<view style="display: flex;align-items: center;">
<uv-icon name="/static/my/student.png" :size="22"></uv-icon>
<view style="margin-left: 26rpx;color: #6a6a6a;">绑定学生</view>
</view>
<uv-icon name="arrow-right" :size="16"></uv-icon>
</view>
<view class="nav-item">
<view style="display: flex;align-items: center;">
<uv-icon name="/static/my/print.png" :size="24"></uv-icon>
<view style="margin-left: 26rpx;color: #6a6a6a;">打印记录</view>
</view>
<uv-icon name="arrow-right" :size="16"></uv-icon>
</view>
</view>
</div>
</view>
</view>
</template>
<script setup>
</script>
<style lang="scss" scoped>
.container {
background-color: #f7f8fa;
height: 100vh;
overflow-y: auto;
display: flex;
flex-direction: column;
}
.content-box {
flex: 1;
overflow-y: auto;
background-image: url('~@/static/bottomBg.png');
background-repeat: no-repeat;
background-size: 100% auto;
}
.content-padding {
padding: 0 28rpx;
}
.user-info {
width: 100%;
height: 440rpx;
background-image: url('~@/static/my/information.png');
background-repeat: no-repeat;
background-size: 100% auto;
background-position: bottom;
background-color: transparent;
position: relative;
.center-avatar {
position: absolute;
top: 20rpx;
left: 50%;
transform: translateX(-50%);
width: 34%;
aspect-ratio: 1/1;
background-color: #fff;
border-radius: 50%;
.center-inner {
width: 100%;
height: 100%;
border-radius: 50%;
overflow: hidden;
}
}
.loginTo-btn {
position: absolute;
left: 50%;
bottom: 60rpx;
transform: translateX(-50%);
color: #fff;
font-size: 44rpx;
}
}
.nav-list {
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 0 20rpx #eee;
padding: 10rpx 20rpx;
box-sizing: border-box;
margin-top: 40rpx;
.nav-item {
display: flex;
justify-content: space-between;
border-bottom: 2rpx dashed #d8d8d8;
padding: 30rpx 0;
&:last-child {
border: none;
}
}
}
</style>
\ No newline at end of file
... ...
<template>
<view class="container">
<view class="student-box">
<view class="title">
报名学员
</view>
<view style="display: flex;align-items: center;">
<template v-if="state.students.length">
<view @click="studentsRef.open('bottom')">{{ state.students[state.activeIndex].name }}</view>
<uv-icon @click="studentsRef.open('bottom')" name="arrow-down" color="#7175f0" size="14"
style="margin-left: 10rpx;"></uv-icon>
</template>
<template v-else>
<view @click="addStudents" style="color: #7175f0;">点击添加</view>
<uv-icon @click="addStudents" name="arrow-right" color="#7175f0" size="14"
style="margin-left: 10rpx;"></uv-icon>
</template>
</view>
</view>
<view class="student-box" style="margin-top: 30rpx;display: block;">
<view class="title" style="margin-bottom: 30rpx;">
选择地区
</view>
<view style="display: grid;grid-template-columns: repeat(3, 1fr);gap: 20rpx;">
<view v-for="item in state.selectData.schoolArr" @click="select1(item)"
:style="state.schoolName === item.text ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''"
:class="item.status === '可选' ? 'select-item' : 'select-item no-select'">
{{ item.text }}
</view>
</view>
</view>
<view class="student-box" style="margin-top: 30rpx;display: block;">
<view class="title" style="margin-bottom: 30rpx;">
选择班型
</view>
<view style="display: grid;grid-template-columns: repeat(3, 1fr);gap: 20rpx;">
<view v-for="item in state.selectData.classArr" @click="select2(item)"
:style="state.className === item.text ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''"
:class="item.status === '可选' ? 'select-item' : 'select-item no-select'">
{{ item.text }}
</view>
</view>
</view>
<view class="student-box" style="margin-top: 30rpx;display: block;">
<view class="title" style="margin-bottom: 30rpx;">
选择老师
</view>
<view style="display: grid;grid-template-columns: repeat(3, 1fr);gap: 20rpx;">
<view v-for="item in state.selectData.teacherArr" @click="select3(item)"
:style="state.teacherName === item.text ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''"
:class="item.status === '可选' ? 'select-item' : 'select-item no-select'">
{{ item.text }}
</view>
</view>
</view>
<view class="student-box" style="margin-top: 30rpx;display: block;">
<view class="title" style="margin-bottom: 30rpx;">
选择时段
</view>
<view>
<!-- body -->
<view class="body-box">
<view class="body-row">
<view class="cell" style="background-color: #c4d0fc;height: 70rpx;">
日期
</view>
<view v-for="item in state.selectData?.tableData?.x" class="cell"
style="background-color: #c4d0fc;height: 70rpx;">
{{ item }}
</view>
</view>
<!-- 行 -->
<view v-for="item in state.selectData?.tableData?.y" class="body-row">
<!-- 列 -->
<view class="cell">
{{ item }}
</view>
<view v-for="item1 in state.selectData?.tableData?.x" @click="selectTable(item, item1)"
:class="(!!state.courseDate && !!state.courseTime && state.courseDate === item1 && state.courseTime === item) ? 'cell select-one' : 'cell'">
{{ filterCell(item, item1) }}
</view>
</view>
</view>
</view>
</view>
<view @click="resetSelect" style="font-size: 24rpx;color: #7175f0;display: flex;justify-content: center;align-items: center;margin: 40rpx 0;">
<uv-icon name="reload" color="#7175f0" size="14" style="margin-right: 10rpx;"></uv-icon>重置所有选择
</view>
<view @click="signUpHandler" style="background-color: #7175f0;color: #fff;text-align: center;border-radius: 9999rpx;padding: 16rpx 0;margin-bottom: 70rpx;">
立即报名
</view>
<!-- 选择学员弹窗 -->
<uv-popup ref="studentsRef" safeAreaInsetBottom closeable :round="20">
<view class="pop-box">
<view class="content-pop">
<view v-for="(item, index) in state.students" @click="state.activeIndex = index"
class="pop-inner-item"
:style="state.activeIndex === index ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''">
{{ item.name }}({{ item.grade }})
</view>
<view @click="addStudents" class="pop-inner-item"
style="display: flex;align-items: center;justify-content: center;">
<uv-icon name="plus" size="14" style="margin-right: 10rpx;"></uv-icon> 新增学员
</view>
</view>
</view>
</uv-popup>
</view>
</template>
<script setup>
import {
onMounted,
reactive,
ref
} from "vue"
import {
myStudentsApi,
getSelectApi,
signUpApi
} from "@/api/index.js"
import {
onLoad
} from '@dcloudio/uni-app'
const studentsRef = ref(null)
const state = reactive({
students: [],
activeIndex: 0,
courseId: null,
selectData: {
classArr: [],
schoolArr: [],
teacherArr: [],
tableData: {}
},
schoolName: "",
className: "",
teacherName: "",
courseDate: "", // X
courseTime: "" // Y
})
onLoad((option) => {
state.courseId = option?.id || null
})
onMounted(async () => {
await getMyStudents()
getSelectCourseData()
})
const signUpHandler = async () => {
try {
if (!state.students.length) return uni.showToast({
icon: 'error',
title: '请选择报名学员'
})
if (!state.schoolName) return uni.showToast({
icon: 'error',
title: '请选择地区'
})
if (!state.className) return uni.showToast({
icon: 'error',
title: '请选择班型'
})
if (!state.teacherName) return uni.showToast({
icon: 'error',
title: '请选择老师'
})
if (!state.courseDate || !state.courseTime) return uni.showToast({
icon: 'error',
title: '请选择时段'
})
await signUpApi({
studentWorkNo: state.students[state.activeIndex]?.workNo,
courseId: state.courseId,
schoolName: state.schoolName,
className: state.className,
teacherName: state.teacherName,
courseDate: state.courseDate,
courseTime: state.courseTime
})
uni.showToast({
icon: 'success',
title: '报名成功'
})
resetSelect()
} catch {}
}
const resetSelect = () => {
state.schoolName = ""
state.className = ""
state.teacherName = ""
state.courseDate = ""
state.courseTime = ""
getSelectCourseData()
}
const select1 = item => {
if (item.status === '不可选') return;
if (state.schoolName === item.text) {
state.schoolName = ""
} else {
state.schoolName = item.text;
}
getSelectCourseData()
}
const select2 = item => {
if (item.status === '不可选') return;
if (state.className === item.text) {
state.className = ""
} else {
state.className = item.text;
}
getSelectCourseData()
}
const select3 = item => {
if (item.status === '不可选') return;
if (state.teacherName === item.text) {
state.teacherName = ""
} else {
state.teacherName = item.text
}
getSelectCourseData()
}
const selectTable = (item, item1) => {
if (state.courseTime === item && state.courseDate === item1) {
state.courseTime = ""
state.courseDate = ""
return
}
const arr = state.selectData?.tableData?.selectable.filter(v => (v.x === item && v.y === item1 && v.status ===
"可选"))
if (!arr.length) return false
state.courseTime = item
state.courseDate = item1
getSelectCourseData()
}
const filterCell = (item, item1) => {
const arr = state.selectData?.tableData?.selectable.filter(v => (v.x === item && v.y === item1 && v.status ===
"可选"))
if (arr.length) return "可选"
return ""
}
const getSelectCourseData = async () => {
if (state.courseId === null) return
const {
data
} = await getSelectApi({
courseId: state.courseId,
schoolName: state.schoolName,
className: state.className,
teacherName: state.teacherName,
courseDate: state.courseDate,
courseTime: state.courseTime
})
state.selectData = data
}
const getMyStudents = async () => {
const {
data
} = await myStudentsApi()
state.students = data.students
}
const addStudents = () => {
studentsRef.value.close()
uni.navigateTo({
url: "/pages/addStudent/addStudent"
})
}
</script>
<style lang="scss" scoped>
.container {
background: linear-gradient(135deg, #eaecfb 10%, #f6f7fb 90%);
background-color: red;
height: calc(100vh - var(--tab-bar-height));
overflow-y: auto;
padding: 0 24rpx;
font-size: 28rpx;
}
.title {
padding-left: 12rpx;
position: relative;
&:before {
content: "";
width: 6rpx;
height: 70%;
border-radius: 3rpx;
background-color: #f5b426;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
}
}
.student-box {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
}
.pop-box {
padding: 70rpx 20rpx;
padding-bottom: calc(var(--tab-bar-height) + 20rpx);
.content-pop {
max-height: 40vh;
min-height: 200rpx;
overflow-y: auto;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
}
.pop-inner-item {
font-size: 28rpx;
border: 2rpx solid #ccc;
text-align: center;
padding: 10rpx 0;
border-radius: 9999rpx;
margin-bottom: 40rpx;
}
.select-item {
text-align: center;
border: 2rpx solid #666;
padding: 8rpx 0;
border-radius: 9999rpx;
}
.no-select {
color: #ccc;
border: 2rpx solid #ccc;
}
.body-box {
width: 100%;
overflow-x: auto;
box-sizing: border-box;
border-right: 2rpx solid #a0b2f3;
border-bottom: 2rpx solid #a0b2f3;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
.body-row {
display: flex;
white-space: nowrap;
position: relative;
box-sizing: border-box;
min-width: fit-content;
.cell {
border: 2rpx solid #a0b2f3;
border-right: none;
border-bottom: none;
flex: 0 0 auto;
text-align: center;
font-size: 24rpx;
min-width: 180rpx;
height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
&:nth-child(1) {
width: 140rpx;
flex-shrink: 0;
background-color: #edf1ff;
position: sticky;
left: 0;
top: 0;
box-shadow: 4rpx 0 4rpx rgba(0, 0, 0, 0.3);
}
}
}
.select-one {
background-color: #8386ef;
color: #fff;
}
</style>
\ No newline at end of file
... ...
uni.addInterceptor({
returnValue (res) {
if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
return res;
}
return new Promise((resolve, reject) => {
res.then((res) => {
if (!res) return resolve(res)
return res[0] ? reject(res[0]) : resolve(res[1])
});
});
},
});
\ No newline at end of file
... ...
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16px;
/* 图片尺寸 */
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:20px;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;
html, body {
padding: 0;
bottom: 0;
height: 100%;
width: 100%;
}
uni-page-body {
height: 100%;
}
\ No newline at end of file
... ...
## 1.0.2(2023-07-02)
uv-action-sheet 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/actionSheet.html
## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
uv-action-sheet 底部操作菜单
... ...
export default {
props: {
// 标题,有值则显示,同时会显示关闭按钮
title: {
type: String,
default: ''
},
// 选项上方的描述信息
description: {
type: String,
default: ''
},
// 数据
actions: {
type: Array,
default: () => []
},
// 取消按钮的文字,不为空时显示按钮
cancelText: {
type: String,
default: ''
},
// 点击某个菜单项时是否关闭弹窗
closeOnClickAction: {
type: Boolean,
default: true
},
// 处理底部安全区(默认true)
safeAreaInsetBottom: {
type: Boolean,
default: true
},
// 小程序的打开方式
openType: {
type: String,
default: ''
},
// 点击遮罩是否允许关闭 (默认true)
closeOnClickOverlay: {
type: Boolean,
default: true
},
// 圆角值
round: {
type: [Boolean, String, Number],
default: 0
},
...uni.$uv?.props?.actionSheet
}
}
\ No newline at end of file
... ...
<template>
<uv-popup
ref="popup"
mode="bottom"
:safeAreaInsetBottom="safeAreaInsetBottom"
:round="round"
:close-on-click-overlay="closeOnClickOverlay"
@change="popupChange"
>
<view class="uv-action-sheet">
<view
class="uv-action-sheet__header"
v-if="title"
>
<text class="uv-action-sheet__header__title uv-line-1">{{title}}</text>
<view
class="uv-action-sheet__header__icon-wrap"
@tap.stop="cancel"
>
<uv-icon
name="close"
size="17"
color="#c8c9cc"
bold
></uv-icon>
</view>
</view>
<text
class="uv-action-sheet__description"
:style="[{
marginTop: `${title && description ? 0 : '18px'}`
}]"
v-if="description"
>{{description}}</text>
<slot>
<uv-line v-if="description"></uv-line>
<view class="uv-action-sheet__item-wrap">
<view v-for="(item, index) in actions" :key="index">
<!-- #ifdef MP -->
<button
class="uv-reset-button"
:openType="item.openType"
@getuserinfo="onGetUserInfo"
@contact="onContact"
@getphonenumber="onGetPhoneNumber"
@error="onError"
@launchapp="onLaunchApp"
@opensetting="onOpenSetting"
:lang="lang"
:session-from="sessionFrom"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
:app-parameter="appParameter"
@tap="selectHandler(index)"
:hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''"
>
<!-- #endif -->
<view
class="uv-action-sheet__item-wrap__item"
@tap.stop="selectHandler(index)"
:hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''"
:hover-stay-time="150"
>
<template v-if="!item.loading">
<text
class="uv-action-sheet__item-wrap__item__name"
:style="[itemStyle(index)]"
>{{ item.name }}</text>
<text
v-if="item.subname"
class="uv-action-sheet__item-wrap__item__subname"
>{{ item.subname }}</text>
</template>
<uv-loading-icon
v-else
custom-class="van-action-sheet__loading"
size="18"
mode="circle"
/>
</view>
<!-- #ifdef MP -->
</button>
<!-- #endif -->
<uv-line v-if="index !== actions.length - 1"></uv-line>
</view>
</view>
</slot>
<uv-gap
bgColor="#eaeaec"
height="6"
v-if="cancelText"
></uv-gap>
<view hover-class="uv-action-sheet--hover">
<text
@touchmove.stop.prevent
:hover-stay-time="150"
v-if="cancelText"
class="uv-action-sheet__cancel-text"
@tap="cancel"
>{{cancelText}}</text>
</view>
</view>
</uv-popup>
</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 button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
import props from './props.js';
/**
* ActionSheet 操作菜单
* @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
* @tutorial https://www.uvui.cn/components/actionSheet.html
* @property {Boolean} show 操作菜单是否展示 (默认 false )
* @property {String} title 操作菜单标题
* @property {String} description 选项上方的描述信息
* @property {Array<Object>} actions 按钮的文字数组,见官方文档示例
* @property {String} cancelText 取消按钮的提示文字,不为空时显示按钮
* @property {Boolean} closeOnClickAction 点击某个菜单项时是否关闭弹窗 (默认 true )
* @property {Boolean} safeAreaInsetBottom 处理底部安全区 (默认 true )
* @property {String} openType 小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error )
* @property {Boolean} closeOnClickOverlay 点击遮罩是否允许关闭 (默认 true )
* @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文
* @property {String} sessionFrom 会话来源,openType="contact"时有效
* @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效
* @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效
* @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效
* @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false )
* @property {String} appParameter 打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效
*
* @event {Function} select 点击ActionSheet列表项时触发
* @event {Function} close 点击取消按钮时触发
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效
* @event {Function} contact 客服消息回调,openType="contact"时有效
* @event {Function} getphonenumber 获取用户手机号回调,openType="getPhoneNumber"时有效
* @event {Function} error 当使用开放能力时,发生错误的回调,openType="error"时有效
* @event {Function} launchapp 打开 APP 成功的回调,openType="launchApp"时有效
* @event {Function} opensetting 在打开授权设置页后回调,openType="openSetting"时有效
* @example <uv-action-sheet ref="actionSheet" :actions="list" :title="title" ></uv-action-sheet>
*/
export default {
name: "uv-action-sheet",
mixins: [openType, button, mpMixin , mixin, props],
emits: ['close', 'select'],
computed: {
// 操作项目的样式
itemStyle() {
return (index) => {
let style = {};
if (this.actions[index].color) style.color = this.actions[index].color
if (this.actions[index].fontSize) style.fontSize = this.$uv.addUnit(this.actions[index].fontSize)
// 选项被禁用的样式
if (this.actions[index].disabled) style.color = '#c0c4cc'
return style;
}
},
},
methods: {
open() {
this.$refs.popup.open();
},
close() {
this.$refs.popup.close();
},
popupChange(e) {
if(!e.show) this.$emit('close');
},
// 点击取消按钮
cancel() {
this.close();
},
selectHandler(index) {
const item = this.actions[index]
if (item && !item.disabled && !item.loading) {
this.$emit('select', item)
if (this.closeOnClickAction) {
this.close();
}
}
},
}
}
</script>
<style lang="scss" scoped>
$show-lines: 1;
$show-reset-button: 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-action-sheet-reset-button-width:100% !default;
$uv-action-sheet-title-font-size: 16px !default;
$uv-action-sheet-title-padding: 12px 30px !default;
$uv-action-sheet-title-color: $uv-main-color !default;
$uv-action-sheet-header-icon-wrap-right:15px !default;
$uv-action-sheet-header-icon-wrap-top:15px !default;
$uv-action-sheet-description-font-size:13px !default;
$uv-action-sheet-description-color:14px !default;
$uv-action-sheet-description-margin: 18px 15px !default;
$uv-action-sheet-item-wrap-item-padding:15px !default;
$uv-action-sheet-item-wrap-name-font-size:16px !default;
$uv-action-sheet-item-wrap-subname-font-size:13px !default;
$uv-action-sheet-item-wrap-subname-color: #c0c4cc !default;
$uv-action-sheet-item-wrap-subname-margin-top:10px !default;
$uv-action-sheet-cancel-text-font-size:16px !default;
$uv-action-sheet-cancel-text-color:$uv-content-color !default;
$uv-action-sheet-cancel-text-font-size:15px !default;
$uv-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
.uv-reset-button {
width: $uv-action-sheet-reset-button-width;
}
.uv-action-sheet {
text-align: center;
&__header {
position: relative;
padding: $uv-action-sheet-title-padding;
&__title {
font-size: $uv-action-sheet-title-font-size;
color: $uv-action-sheet-title-color;
font-weight: bold;
text-align: center;
}
&__icon-wrap {
position: absolute;
right: $uv-action-sheet-header-icon-wrap-right;
top: $uv-action-sheet-header-icon-wrap-top;
}
}
&__description {
font-size: $uv-action-sheet-description-font-size;
color: $uv-tips-color;
margin: $uv-action-sheet-description-margin;
text-align: center;
}
&__item-wrap {
&__item {
padding: $uv-action-sheet-item-wrap-item-padding;
@include flex;
align-items: center;
justify-content: center;
flex-direction: column;
&__name {
font-size: $uv-action-sheet-item-wrap-name-font-size;
color: $uv-main-color;
text-align: center;
}
&__subname {
font-size: $uv-action-sheet-item-wrap-subname-font-size;
color: $uv-action-sheet-item-wrap-subname-color;
margin-top: $uv-action-sheet-item-wrap-subname-margin-top;
text-align: center;
}
}
}
&__cancel-text {
font-size: $uv-action-sheet-cancel-text-font-size;
color: $uv-action-sheet-cancel-text-color;
text-align: center;
padding: $uv-action-sheet-cancel-text-font-size;
}
&--hover {
background-color: $uv-action-sheet-cancel-text-hover-background-color;
}
}
</style>
... ...
{
"id": "uv-action-sheet",
"displayName": "uv-action-sheet 底部操作菜单 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.2",
"description": "该组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheet API,配置更加灵活,所有平台都表现一致。",
"keywords": [
"action-sheet",
"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",
"uv-popup",
"uv-icon",
"uv-line",
"uv-loading-icon",
"uv-gap"
],
"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"
}
}
}
}
}
\ No newline at end of file
... ...
## ActionSheet 操作菜单
> **组件名:uv-action-sheet**
本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。
本组件功能类似于uni的uni.showActionSheet API,配置更加灵活,所有平台都表现一致。
### <a href="https://www.uvui.cn/components/actionSheet.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
... ...
## 1.0.4(2023-12-06)
1. 阻止事件冒泡处理
## 1.0.3(2023-10-23)
1. 修复报错的BUG
## 1.0.2(2023-10-23)
1. 修复设置singleSize、multipleSize、space等值带单位,存在不显示的BUG
## 1.0.1(2023-09-13)
1. 添加依赖
## 1.0.0(2023-08-30)
1. 新增uv-album相册组件
... ...
<template>
<view class="uv-album">
<view
class="uv-album__row"
ref="uv-album__row"
v-for="(arr, index) in showUrls"
:forComputedUse="albumWidth"
:key="index"
>
<view
class="uv-album__row__wrapper"
v-for="(item, index1) in arr"
:key="index1"
:style="[imageStyle(index + 1, index1 + 1)]"
@tap.stop="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
>
<image
:src="getSrc(item)"
:mode="
urls.length === 1
? imageHeight > 0
? singleMode
: 'widthFix'
: multipleMode
"
:style="[
{
width: imageWidth,
height: imageHeight
}
]"
></image>
<view
v-if="
showMore &&
urls.length > rowCount * showUrls.length &&
index === showUrls.length - 1 &&
index1 === showUrls[showUrls.length - 1].length - 1
"
class="uv-album__row__wrapper__text"
>
<uv-text
:text="`+${urls.length - maxCount}`"
color="#fff"
:size="$uv.getPx(multipleSize) * 0.3"
align="center"
customStyle="justify-content: center"
></uv-text>
</view>
</view>
</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'
// #ifdef APP-NVUE
// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
const dom = uni.requireNativePlugin('dom')
// #endif
/**
* Album 相册
* @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
* @tutorial https://www.uvui.cn/components/album.html
* @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
* @property {String | Number} singleSize 单图时,图片长边的长度 (默认 180 )
* @property {String | Number} multipleSize 多图时,图片边长 (默认 70 )
* @property {String | Number} space 多图时,图片水平和垂直之间的间隔 (默认 6 )
* @property {String} singleMode 单图时,图片缩放裁剪的模式 (默认 'scaleToFill' )
* @property {String} multipleMode 多图时,图片缩放裁剪的模式 (默认 'aspectFill' )
* @property {String | Number} maxCount 取消按钮的提示文字 (默认 9 )
* @property {Boolean} previewFullImage 是否可以预览图片 (默认 true )
* @property {String | Number} rowCount 每行展示图片数量,如设置,singleSize和multipleSize将会无效 (默认 3 )
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
*
* @event {Function} albumWidth 某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送 (回调参数 width )
* @example <uv-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></uv-album>
*/
export default {
name: 'uv-album',
mixins: [mpMixin, mixin],
emits: ['albumWidth'],
props: {
// 图片地址,Array<String>|Array<Object>形式
urls: {
type: Array,
default: () => []
},
// 指定从数组的对象元素中读取哪个属性作为图片地址
keyName: {
type: String,
default: ''
},
// 单图时,图片长边的长度
singleSize: {
type: [String, Number],
default: 180
},
// 多图时,图片边长
multipleSize: {
type: [String, Number],
default: 70
},
// 多图时,图片水平和垂直之间的间隔
space: {
type: [String, Number],
default: 6
},
// 单图时,图片缩放裁剪的模式
singleMode: {
type: String,
default: 'scaleToFill'
},
// 多图时,图片缩放裁剪的模式
multipleMode: {
type: String,
default: 'aspectFill'
},
// 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
maxCount: {
type: [String, Number],
default: 9
},
// 是否可以预览图片
previewFullImage: {
type: Boolean,
default: true
},
// 每行展示图片数量,如设置,singleSize和multipleSize将会无效
rowCount: {
type: [String, Number],
default: 3
},
// 超出maxCount时是否显示查看更多的提示
showMore: {
type: Boolean,
default: true
},
...uni.$uv?.props?.album
},
data() {
return {
// 单图的宽度
singleWidth: 0,
// 单图的高度
singleHeight: 0,
// 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比
singlePercent: 0.6
}
},
watch: {
urls: {
immediate: true,
handler(newVal) {
if (newVal.length === 1) {
this.getImageRect()
}
}
}
},
computed: {
imageStyle() {
return (index1, index2) => {
const { space, rowCount, multipleSize, urls } = this;
const rowLen = this.showUrls.length;
const allLen = this.urls.length;
const style = {
marginRight: this.$uv.addUnit(space),
marginBottom: this.$uv.addUnit(space)
}
// 如果为最后一行,则每个图片都无需下边框
if (index1 === rowLen) style.marginBottom = 0
// 每行的最右边一张和总长度的最后一张无需右边框
if (
index2 === rowCount ||
(index1 === rowLen &&
index2 === this.showUrls[index1 - 1].length)
)
style.marginRight = 0
return style
}
},
// 将数组划分为二维数组
showUrls() {
const arr = []
this.urls.map((item, index) => {
// 限制最大展示数量
if (index + 1 <= this.maxCount) {
// 计算该元素为第几个素组内
const itemIndex = Math.floor(index / this.rowCount)
// 判断对应的索引是否存在
if (!arr[itemIndex]) {
arr[itemIndex] = []
}
arr[itemIndex].push(item)
}
})
return arr
},
imageWidth() {
return this.$uv.addUnit(
this.urls.length === 1 ? this.singleWidth : this.multipleSize
)
},
imageHeight() {
return this.$uv.addUnit(
this.urls.length === 1 ? this.singleHeight : this.multipleSize
)
},
// 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
// 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
albumWidth() {
let width = 0
if (this.urls.length === 1) {
width = this.singleWidth
} else {
width =
this.showUrls[0].length * this.$uv.getPx(this.multipleSize) +
this.$uv.getPx(this.space) * (this.showUrls[0].length - 1)
}
this.$emit('albumWidth', width)
return width
}
},
methods: {
// 预览图片
onPreviewTap(url) {
const urls = this.urls.map((item) => {
return this.getSrc(item)
})
uni.previewImage({
current: url,
urls
})
},
// 获取图片的路径
getSrc(item) {
return this.$uv.test.object(item) ?
(this.keyName && item[this.keyName]) || item.src :
item
},
// 单图时,获取图片的尺寸
// 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
// 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
getImageRect() {
const src = this.getSrc(this.urls[0])
uni.getImageInfo({
src,
success: (res) => {
// 判断图片横向还是竖向展示方式
const isHorizotal = res.width >= res.height
this.singleWidth = isHorizotal ?
this.singleSize :
(res.width / res.height) * this.$uv.getPx(this.singleSize)
this.singleHeight = !isHorizotal ?
this.singleSize :
(res.height / res.width) * this.singleWidth
},
fail: () => {
this.getComponentWidth()
}
})
},
// 获取组件的宽度
async getComponentWidth() {
// 延时一定时间,以获取dom尺寸
await this.$uv.sleep(30)
// #ifndef APP-NVUE
this.$uGetRect('.uv-album__row').then((size) => {
this.singleWidth = size.width * this.singlePercent
})
// #endif
// #ifdef APP-NVUE
// 这里ref="uv-album__row"所在的标签为通过for循环出来,导致this.$refs['uv-album__row']是一个数组
const ref = this.$refs['uv-album__row'][0]
ref &&
dom.getComponentRect(ref, (res) => {
this.singleWidth = res.size.width * this.singlePercent
})
// #endif
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
.uv-album {
@include flex(column);
&__row {
@include flex(row);
flex-wrap: wrap;
&__wrapper {
position: relative;
&__text {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
@include flex(row);
justify-content: center;
align-items: center;
}
}
}
}
</style>
\ No newline at end of file
... ...
{
"id": "uv-album",
"displayName": "uv-album 相册 全面兼容vue3+2、app、h5、小程序等多端",
"version": "1.0.4",
"description": "本组件提供一个类似相册的功能,让开发者开发起来更加得心应手,功能齐全,灵活配置可以,开箱即用。减少重复的模板代码",
"keywords": [
"album",
"uv-ui",
"uvui",
"相册",
"图片"
],
"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-text"
],
"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"
}
}
}
}
}
\ No newline at end of file
... ...
# Album 相册
> **组件名:uv-album**
本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。
功能齐全,灵活配置可以,开箱即用。减少重复的模板代码。
# <a href="https://www.uvui.cn/components/album.html" target="_blank">查看文档</a>
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
... ...
## 1.0.2(2023-06-01)
1. 修复点击触发两次实践的BUG
## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
uv-alert 警告提示组件
... ...
export default {
props: {
// 显示文字
title: {
type: String,
default: ''
},
// 主题,success/warning/info/error
type: {
type: String,
default: 'warning'
},
// 辅助性文字
description: {
type: String,
default: ''
},
// 是否可关闭
closable: {
type: Boolean,
default: false
},
// 是否显示图标
showIcon: {
type: Boolean,
default: false
},
// 浅或深色调,light-浅色,dark-深色
effect: {
type: String,
default: 'light'
},
// 文字是否居中
center: {
type: Boolean,
default: false
},
// 字体大小
fontSize: {
type: [String, Number],
default: 14
},
...uni.$uv?.props?.alert
}
}
\ No newline at end of file
... ...
<template>
<uv-transition
mode="fade"
:show="show"
>
<view
class="uv-alert"
:class="[`uv-alert--${type}--${effect}`]"
@tap.stop="clickHandler"
:style="[$uv.addStyle(customStyle)]"
>
<view
class="uv-alert__icon"
v-if="showIcon"
>
<uv-icon
:name="iconName"
size="18"
:color="iconColor"
></uv-icon>
</view>
<view
class="uv-alert__content"
:style="[{
paddingRight: closable ? '20px' : 0
}]"
>
<text
class="uv-alert__content__title"
v-if="title"
:style="[{
fontSize: $uv.addUnit(fontSize),
textAlign: center ? 'center' : 'left'
}]"
:class="[effect === 'dark' ? 'uv-alert__text--dark' : `uv-alert__text--${type}--light`]"
>{{ title }}</text>
<text
class="uv-alert__content__desc"
v-if="description"
:style="[{
fontSize: $uv.addUnit(fontSize),
textAlign: center ? 'center' : 'left'
}]"
:class="[effect === 'dark' ? 'uv-alert__text--dark' : `uv-alert__text--${type}--light`]"
>{{ description }}</text>
</view>
<view
class="uv-alert__close"
v-if="closable"
@tap.stop="closeHandler"
>
<uv-icon
name="close"
:color="iconColor"
size="15"
></uv-icon>
</view>
</view>
</uv-transition>
</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';
/**
* Alert 警告提示
* @description 警告提示,展现需要关注的信息。
* @tutorial https://www.uvui.cn/components/alertTips.html
*
* @property {String} title 显示的文字
* @property {String} type 使用预设的颜色 (默认 'warning' )
* @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选
* @property {Boolean} closable 关闭按钮(默认为叉号icon图标) (默认 false )
* @property {Boolean} showIcon 是否显示左边的辅助图标 ( 默认 false )
* @property {String} effect 多图时,图片缩放裁剪的模式 (默认 'light' )
* @property {Boolean} center 文字是否居中 (默认 false )
* @property {String | Number} fontSize 字体大小 (默认 14 )
* @property {Object} customStyle 定义需要用到的外部样式
* @event {Function} click 点击组件时触发
* @example <uv-alert :title="title" type = "warning" :closable="closable" :description = "description"></uv-alert>
*/
export default {
name: 'uv-alert',
mixins: [mpMixin, mixin, props],
emits: ['click'],
data() {
return {
show: true
}
},
computed: {
iconColor() {
return this.effect === 'light' ? this.type : '#fff'
},
// 不同主题对应不同的图标
iconName() {
switch (this.type) {
case 'success':
return 'checkmark-circle-fill';
break;
case 'error':
return 'close-circle-fill';
break;
case 'warning':
return 'error-circle-fill';
break;
case 'info':
return 'info-circle-fill';
break;
case 'primary':
return 'more-circle-fill';
break;
default:
return 'error-circle-fill';
}
}
},
methods: {
// 点击内容
clickHandler() {
this.$emit('click')
},
// 点击关闭按钮
closeHandler() {
this.show = false
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
.uv-alert {
position: relative;
background-color: $uv-primary;
padding: 8px 10px;
@include flex(row);
align-items: center;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
&--primary--dark {
background-color: $uv-primary;
}
&--primary--light {
background-color: #ecf5ff;
}
&--error--dark {
background-color: $uv-error;
}
&--error--light {
background-color: #FEF0F0;
}
&--success--dark {
background-color: $uv-success;
}
&--success--light {
background-color: #f5fff0;
}
&--warning--dark {
background-color: $uv-warning;
}
&--warning--light {
background-color: #FDF6EC;
}
&--info--dark {
background-color: $uv-info;
}
&--info--light {
background-color: #f4f4f5;
}
&__icon {
margin-right: 5px;
}
&__content {
@include flex(column);
flex: 1;
&__title {
color: $uv-main-color;
font-size: 14px;
font-weight: bold;
color: #fff;
margin-bottom: 2px;
}
&__desc {
color: $uv-main-color;
font-size: 14px;
flex-wrap: wrap;
color: #fff;
}
}
&__title--dark,
&__desc--dark {
color: #FFFFFF;
}
&__text--primary--light,
&__text--primary--light {
color: $uv-primary;
}
&__text--success--light,
&__text--success--light {
color: $uv-success;
}
&__text--warning--light,
&__text--warning--light {
color: $uv-warning;
}
&__text--error--light,
&__text--error--light {
color: $uv-error;
}
&__text--info--light,
&__text--info--light {
color: $uv-info;
}
&__close {
position: absolute;
top: 11px;
right: 10px;
}
}
</style>
... ...
{
"id": "uv-alert",
"displayName": "uv-alert 警告提示 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.2",
"description": "uv-alert 警告提示,展现需要关注的信息。灵活配置,功能齐全,兼容全端",
"keywords": [
"alert",
"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",
"uv-transition",
"uv-icon"
],
"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"
}
}
}
}
}
\ No newline at end of file
... ...
## Alert 警告提示
> **组件名:uv-alert**
警告提示,展现需要关注的信息。
当某个页面需要向用户显示警告的信息时。
非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭。
### <a href="https://www.uvui.cn/components/alert.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
... ...
## 1.0.5(2023-12-06)
1. 优化
## 1.0.4(2023-12-06)
1. 优化
## 1.0.3(2023-12-06)
1. 阻止事件冒泡处理,单个头像模式
## 1.0.2(2023-12-06)
1. 阻止事件冒泡处理
## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
uv-avatar 头像组件
... ...
export default {
props: {
// 头像图片组
urls: {
type: Array,
default: () => []
},
// 最多展示的头像数量
maxCount: {
type: [String, Number],
default: 5
},
// 头像形状
shape: {
type: String,
default: 'circle'
},
// 图片裁剪模式
mode: {
type: String,
default: 'scaleToFill'
},
// 超出maxCount时是否显示查看更多的提示
showMore: {
type: Boolean,
default: true
},
// 头像大小
size: {
type: [String, Number],
default: 40
},
// 指定从数组的对象元素中读取哪个属性作为图片地址
keyName: {
type: String,
default: ''
},
// 头像之间的遮挡比例
gap: {
type: [String, Number],
validator(value) {
return value >= 0 && value <= 1
},
default: 0.5
},
// 需额外显示的值
extraValue: {
type: [Number, String],
default: 0
},
...uni.$uv?.props?.avatarGroup
}
}
\ No newline at end of file
... ...
<template>
<view class="uv-avatar-group">
<view
class="uv-avatar-group__item"
v-for="(item, index) in showUrl"
:key="index"
:style="{
marginLeft: index === 0 ? 0 : $uv.addUnit(-size * gap)
}"
>
<uv-avatar
:size="size"
:shape="shape"
:mode="mode"
:src="$uv.test.object(item) ? keyName && item[keyName] || item.url : item"
></uv-avatar>
<view
class="uv-avatar-group__item__show-more"
v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
@tap="clickHandler"
>
<uv-text
color="#ffffff"
:size="size * 0.4"
:text="`+${extraValue || urls.length - showUrl.length}`"
align="center"
customStyle="justify-content: center"
></uv-text>
</view>
</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';
/**
* AvatarGroup 头像组
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
* @tutorial https://www.uvui.cn/components/avatar.html
*
* @property {Array} urls 头像图片组 (默认 [] )
* @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 )
* @property {String} shape 头像形状( 'circle' (默认) | 'square' )
* @property {String} mode 图片裁剪模式(默认 'scaleToFill' )
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
* @property {String | Number} size 头像大小 (默认 40 )
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
* @property {String | Number} gap 头像之间的遮挡比例(0.4代表遮挡40%) (默认 0.5 )
* @property {String | Number} extraValue 需额外显示的值
* @event {Function} showMore 头像组更多点击
* @example <uv-avatar-group:urls="urls" size="35" gap="0.4" ></uv-avatar-group:urls=>
*/
export default {
name: 'uv-avatar-group',
mixins: [mpMixin, mixin, props],
data() {
return {
}
},
computed: {
showUrl() {
return this.urls.slice(0, this.maxCount)
}
},
methods: {
clickHandler() {
this.$emit('showMore')
}
},
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
.uv-avatar-group {
@include flex;
&__item {
margin-left: -10px;
position: relative;
&--no-indent {
// 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持
margin-left: 0;
}
&__show-more {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.3);
@include flex;
align-items: center;
justify-content: center;
border-radius: 100px;
}
}
}
</style>
... ...
import { range } from '@/uni_modules/uv-ui-tools/libs/function/test.js'
export default {
props: {
// 头像图片路径(不能为相对路径)
src: {
type: String,
default: ''
},
// 头像形状,circle-圆形,square-方形
shape: {
type: String,
default: 'circle'
},
// 头像尺寸
size: {
type: [String, Number],
default: 40
},
// 裁剪模式
mode: {
type: String,
default: 'scaleToFill'
},
// 显示的文字
text: {
type: String,
default: ''
},
// 背景色
bgColor: {
type: String,
default: '#c0c4cc'
},
// 文字颜色
color: {
type: String,
default: '#fff'
},
// 文字大小
fontSize: {
type: [String, Number],
default: 18
},
// 显示的图标
icon: {
type: String,
default: ''
},
// 显示小程序头像,只对百度,微信,QQ小程序有效
mpAvatar: {
type: Boolean,
default: false
},
// 是否使用随机背景色
randomBgColor: {
type: Boolean,
default: false
},
// 加载失败的默认头像(组件有内置默认图片)
defaultUrl: {
type: String,
default: ''
},
// 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
colorIndex: {
type: [String, Number],
// 校验参数规则,索引在0-19之间
validator(n) {
return range(n, [0, 19]) || n === ''
},
default: ''
},
// 组件标识符
name: {
type: String,
default: ''
},
...uni.$uv?.props?.avatar
}
}
\ No newline at end of file
... ...
<template>
<view
class="uv-avatar"
:class="[`uv-avatar--${shape}`]"
:style="[{
backgroundColor: (text || icon) ? (randomBgColor ? colors[colorIndex !== '' ? colorIndex : $uv.random(0, 19)] : bgColor) : 'transparent',
width: $uv.addUnit(size),
height: $uv.addUnit(size),
}, $uv.addStyle(customStyle)]"
@tap="clickHandler"
>
<slot>
<!-- #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU -->
<open-data
v-if="mpAvatar && allowMp"
type="userAvatarUrl"
:style="[{
width: $uv.addUnit(size),
height: $uv.addUnit(size)
}]"
/>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU -->
<template v-if="mpAvatar && allowMp"></template>
<!-- #endif -->
<uv-icon
v-else-if="icon"
:name="icon"
:size="fontSize"
:color="color"
></uv-icon>
<uv-text
v-else-if="text"
:text="text"
:size="fontSize"
:color="color"
align="center"
customStyle="justify-content: center"
></uv-text>
<image
class="uv-avatar__image"
v-else
:class="[`uv-avatar__image--${shape}`]"
:src="avatarUrl || defaultUrl"
:mode="mode"
@error="errorHandler"
:style="[{
width: $uv.addUnit(size),
height: $uv.addUnit(size)
}]"
></image>
</slot>
</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';
const base64Avatar =
"data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z";
/**
* Avatar 头像
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
* @tutorial https://www.uvui.cn/components/avatar.html
*
* @property {String} src 头像路径,如加载失败,将会显示默认头像(不能为相对路径)
* @property {String} shape 头像形状 ( circle (默认) | square)
* @property {String | Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值 (默认 40 )
* @property {String} mode 头像图片的裁剪类型,与uni的image组件的mode参数一致,如效果达不到需求,可尝试传widthFix值 (默认 'scaleToFill' )
* @property {String} text 用文字替代图片,级别优先于src
* @property {String} bgColor 背景颜色,一般显示文字时用 (默认 '#c0c4cc' )
* @property {String} color 文字颜色 (默认 '#ffffff' )
* @property {String | Number} fontSize 文字大小 (默认 18 )
* @property {String} icon 显示的图标
* @property {Boolean} mpAvatar 显示小程序头像,只对百度,微信,QQ小程序有效 (默认 false )
* @property {Boolean} randomBgColor 是否使用随机背景色 (默认 false )
* @property {String} defaultUrl 加载失败的默认头像(组件有内置默认图片)
* @property {String | Number} colorIndex 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
* @property {String} name 组件标识符 (默认 'level' )
* @property {Object} customStyle 定义需要用到的外部样式
*
* @event {Function} click 点击组件时触发 index: 用户传递的标识符
* @example <uv-avatar :src="src" mode="square"></uv-avatar>
*/
export default {
name: 'uv-avatar',
emits: ['click'],
mixins: [mpMixin, mixin, props],
data() {
return {
// 如果配置randomBgColor参数为true,在图标或者文字的模式下,会随机从中取出一个颜色值当做背景色
colors: ['#ffb34b', '#f2bba9', '#f7a196', '#f18080', '#88a867', '#bfbf39', '#89c152', '#94d554', '#f19ec2',
'#afaae4', '#e1b0df', '#c38cc1', '#72dcdc', '#9acdcb', '#77b1cc', '#448aca', '#86cefa', '#98d1ee',
'#73d1f1',
'#80a7dc'
],
avatarUrl: this.src,
allowMp: false
}
},
watch: {
// 监听头像src的变化,赋值给内部的avatarUrl变量,因为图片加载失败时,需要修改图片的src为默认值
// 而组件内部不能直接修改props的值,所以需要一个中间变量
src: {
immediate: true,
handler(newVal) {
this.avatarUrl = newVal
// 如果没有传src,则主动触发error事件,用于显示默认的头像,否则src为''空字符等的时候,会无内容展示
if(!newVal) {
this.errorHandler()
}
}
}
},
computed: {
imageStyle() {
const style = {}
return style
}
},
created() {
this.init()
},
methods: {
init() {
// 目前只有这几个小程序平台具有open-data标签
// 其他平台可以通过uni.getUserInfo类似接口获取信息,但是需要弹窗授权(首次),不合符组件逻辑
// 故目前自动获取小程序头像只支持这几个平台
// #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU
this.allowMp = true
// #endif
},
// 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
isImg() {
return this.src.indexOf('/') !== -1
},
// 图片加载时失败时触发
errorHandler() {
this.avatarUrl = this.defaultUrl || base64Avatar
},
clickHandler() {
this.$emit('click', this.name)
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
.uv-avatar {
@include flex;
align-items: center;
justify-content: center;
&--circle {
border-radius: 100px;
}
&--square {
border-radius: 4px;
}
&__image {
&--circle {
border-radius: 100px;
}
&--square {
border-radius: 4px;
}
}
}
</style>
... ...
{
"id": "uv-avatar",
"displayName": "uv-avatar 头像 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.5",
"description": "uv-avatar 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。",
"keywords": [
"uv-avatar",
"uvui",
"uv-ui",
"avatar",
"头像"
],
"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-icon",
"uv-text"
],
"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"
}
}
}
}
}
\ No newline at end of file
... ...
## Avatar 头像
> **组件名:uv-avatar**
本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
### <a href="https://www.uvui.cn/components/avatar.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
... ...
## 1.0.2(2023-07-03)
1. 优化插槽自定义内容部分
2. 增加backToTop方法说明
## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
uv-back-top 返回顶部
... ...
export default {
props: {
// 返回顶部的形状,circle-圆形,square-方形
mode: {
type: String,
default: 'circle'
},
// 自定义图标
icon: {
type: String,
default: 'arrow-upward'
},
// 提示文字
text: {
type: String,
default: ''
},
// 返回顶部滚动时间
duration: {
type: [String, Number],
default: 100
},
// 滚动距离
scrollTop: {
type: [String, Number],
default: 0
},
// 距离顶部多少距离显示,单位px
top: {
type: [String, Number],
default: 400
},
// 返回顶部按钮到底部的距离,单位px
bottom: {
type: [String, Number],
default: 100
},
// 返回顶部按钮到右边的距离,单位px
right: {
type: [String, Number],
default: 20
},
// 层级
zIndex: {
type: [String, Number],
default: 9
},
// 图标的样式,对象形式
iconStyle: {
type: Object,
default: () => ({
color: '#909399',
fontSize: '19px'
})
},
...uni.$uv?.props?.backtop
}
}
\ No newline at end of file
... ...
<template>
<uv-transition mode="fade" :customStyle="backTopStyle" :show="show">
<slot>
<view class="uv-back-top" :style="[contentStyle]" @click="backToTop">
<uv-icon :name="icon" :custom-style="iconStyle"></uv-icon>
<text v-if="text" class="uv-back-top__text">{{text}}</text>
</view>
</slot>
</uv-transition>
</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';
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
/**
* backTop 返回顶部
* @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
* @tutorial https://www.uvui.cn/components/backTop.html
* @property {String} mode 返回顶部的形状,circle-圆形,square-方形 (默认 'circle' )
* @property {String} icon 自定义图标 (默认 'arrow-upward' ) 见官方文档示例
* @property {String} text 提示文字
* @property {String | Number} duration 返回顶部滚动时间 (默认 100)
* @property {String | Number} scrollTop 滚动距离 (默认 0 )
* @property {String | Number} top 距离顶部多少距离显示,单位px (默认 400 )
* @property {String | Number} bottom 返回顶部按钮到底部的距离,单位px (默认 100 )
* @property {String | Number} right 返回顶部按钮到右边的距离,单位px (默认 20 )
* @property {String | Number} zIndex 层级 (默认 9 )
* @property {Object<Object>} iconStyle 图标的样式,对象形式 (默认 {color: '#909399',fontSize: '19px'})
* @property {Object} customStyle 定义需要用到的外部样式
*
* @example <uv-back-top :scrollTop="scrollTop"></uv-back-top>
*/
export default {
name: 'uv-back-top',
emits: ['click'],
mixins: [mpMixin, mixin, props],
computed: {
backTopStyle() {
// 动画组件样式
const style = {
bottom: this.$uv.addUnit(this.bottom),
right: this.$uv.addUnit(this.right),
width: '40px',
height: '40px',
position: 'fixed',
zIndex: 10,
}
return style
},
show() {
return this.$uv.getPx(this.scrollTop) > this.$uv.getPx(this.top)
},
contentStyle() {
const style = {}
let radius = 0
// 是否圆形
if (this.mode === 'circle') {
radius = '100px'
} else {
radius = '4px'
}
// 为了兼容安卓nvue,只能这么分开写
style.borderTopLeftRadius = radius
style.borderTopRightRadius = radius
style.borderBottomLeftRadius = radius
style.borderBottomRightRadius = radius
return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
}
},
methods: {
backToTop() {
// #ifdef APP-NVUE
if (!this.$parent.$refs['uv-back-top']) {
this.$uv.error(`nvue页面需要给页面最外层元素设置"ref='uv-back-top'`)
}
dom.scrollToElement(this.$parent.$refs['uv-back-top'], {
offset: 0
})
// #endif
// #ifndef APP-NVUE
uni.pageScrollTo({
scrollTop: 0,
duration: this.duration
});
// #endif
this.$emit('click')
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
$uv-back-top-flex: 1 !default;
$uv-back-top-height: 100% !default;
$uv-back-top-background-color: #E1E1E1 !default;
$uv-back-top-tips-font-size: 12px !default;
.uv-back-top {
@include flex;
flex-direction: column;
align-items: center;
flex: $uv-back-top-flex;
height: $uv-back-top-height;
justify-content: center;
background-color: $uv-back-top-background-color;
&__tips {
font-size: $uv-back-top-tips-font-size;
transform: scale(0.8);
}
}
</style>
\ No newline at end of file
... ...
{
"id": "uv-back-top",
"displayName": "uv-back-top 返回顶部 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.2",
"description": "返回顶部 组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。",
"keywords": [
"uv-back-top",
"uvui",
"uv-ui",
"avatar",
"返回顶部"
],
"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-icon",
"uv-transition"
],
"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"
}
}
}
}
}
\ No newline at end of file
... ...
## BackTop 返回顶部
> **组件名:uv-back-top**
该组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
### <a href="https://www.uvui.cn/components/backTop.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
... ...
## 1.0.2(2023-06-04)
1. 修复type等属性为null的时候不显示徽标的BUG
## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
uv-badge 徽标数,数字角标
... ...
export default {
props: {
// 是否显示圆点
isDot: {
type: Boolean,
default: false
},
// 显示的内容
value: {
type: [Number, String],
default: ''
},
// 是否显示
show: {
type: Boolean,
default: true
},
// 最大值,超过最大值会显示 '{max}+'
max: {
type: [Number, String],
default: 999
},
// 主题类型,error|warning|success|primary
type: {
type: [String,undefined,null],
default: 'error'
},
// 当数值为 0 时,是否展示 Badge
showZero: {
type: Boolean,
default: false
},
// 背景颜色,优先级比type高,如设置,type参数会失效
bgColor: {
type: [String, null],
default: null
},
// 字体颜色
color: {
type: [String, null],
default: null
},
// 徽标形状,circle-四角均为圆角,horn-左下角为直角
shape: {
type: [String,undefined,null],
default: 'circle'
},
// 设置数字的显示方式,overflow|ellipsis|limit
// overflow会根据max字段判断,超出显示`${max}+`
// ellipsis会根据max判断,超出显示`${max}...`
// limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数
numberType: {
type: [String,undefined,null],
default: 'overflow'
},
// 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
offset: {
type: Array,
default: () => []
},
// 是否反转背景和字体颜色
inverted: {
type: Boolean,
default: false
},
// 是否绝对定位
absolute: {
type: Boolean,
default: false
},
...uni.$uv?.props?.badge
}
}
\ No newline at end of file
... ...
<template>
<text
v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)"
:class="[isDot ? 'uv-badge--dot' : 'uv-badge--not-dot', inverted && 'uv-badge--inverted', shape === 'horn' && 'uv-badge--horn', `uv-badge--${propsType}${inverted ? '--inverted' : ''}`]"
:style="[$uv.addStyle(customStyle), badgeStyle]"
class="uv-badge"
>{{ isDot ? '' :showValue }}</text>
</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';
/**
* badge 徽标数
* @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
* @tutorial https://www.uvui.cn/components/badge.html
*
* @property {Boolean} isDot 是否显示圆点 (默认 false )
* @property {String | Number} value 显示的内容
* @property {Boolean} show 是否显示 (默认 true )
* @property {String | Number} max 最大值,超过最大值会显示 '{max}+' (默认999)
* @property {String} type 主题类型,error|warning|success|primary (默认 'error' )
* @property {Boolean} showZero 当数值为 0 时,是否展示 Badge (默认 false )
* @property {String} bgColor 背景颜色,优先级比type高,如设置,type参数会失效
* @property {String} color 字体颜色 (默认 '#ffffff' )
* @property {String} shape 徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' )
* @property {String} numberType 设置数字的显示方式,overflow|ellipsis|limit (默认 'overflow' )
* @property {Array}} offset 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
* @property {Boolean} inverted 是否反转背景和字体颜色(默认 false )
* @property {Boolean} absolute 是否绝对定位(默认 false )
* @property {Object} customStyle 定义需要用到的外部样式
* @example <uv-badge :type="type" :count="count"></uv-badge>
*/
export default {
name: 'uv-badge',
mixins: [mpMixin, mixin, props],
computed: {
// 是否将badge中心与父组件右上角重合
boxStyle() {
let style = {};
return style;
},
// 整个组件的样式
badgeStyle() {
const style = {}
if(this.color) {
style.color = this.color
}
if (this.bgColor && !this.inverted) {
style.backgroundColor = this.bgColor
}
if (this.absolute) {
style.position = 'absolute'
// 如果有设置offset参数
if(this.offset.length) {
// top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top
const top = this.offset[0]
const right = this.offset[1] || top
style.top = this.$uv.addUnit(top)
style.right = this.$uv.addUnit(right)
}
}
return style
},
showValue() {
switch (this.numberType) {
case "overflow":
return Number(this.value) > Number(this.max) ? this.max + "+" : this.value
break;
case "ellipsis":
return Number(this.value) > Number(this.max) ? "..." : this.value
break;
case "limit":
return Number(this.value) > 999 ? Number(this.value) >= 9999 ?
Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value /
1e3 * 100) / 100 + "k" : this.value
break;
default:
return Number(this.value)
}
},
propsType(){
return this.type || 'error'
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
$uv-badge-primary: $uv-primary !default;
$uv-badge-error: $uv-error !default;
$uv-badge-success: $uv-success !default;
$uv-badge-info: $uv-info !default;
$uv-badge-warning: $uv-warning !default;
$uv-badge-dot-radius: 100px !default;
$uv-badge-dot-size: 8px !default;
$uv-badge-dot-right: 4px !default;
$uv-badge-dot-top: 0 !default;
$uv-badge-text-font-size: 11px !default;
$uv-badge-text-right: 10px !default;
$uv-badge-text-padding: 2px 5px !default;
$uv-badge-text-align: center !default;
$uv-badge-text-color: #FFFFFF !default;
.uv-badge {
border-top-right-radius: $uv-badge-dot-radius;
border-top-left-radius: $uv-badge-dot-radius;
border-bottom-left-radius: $uv-badge-dot-radius;
border-bottom-right-radius: $uv-badge-dot-radius;
@include flex;
line-height: $uv-badge-text-font-size;
text-align: $uv-badge-text-align;
font-size: $uv-badge-text-font-size;
color: $uv-badge-text-color;
&--dot {
height: $uv-badge-dot-size;
width: $uv-badge-dot-size;
}
&--inverted {
font-size: 13px;
}
&--not-dot {
padding: $uv-badge-text-padding;
}
&--horn {
border-bottom-left-radius: 0;
}
&--primary {
background-color: $uv-badge-primary;
}
&--primary--inverted {
color: $uv-badge-primary;
}
&--error {
background-color: $uv-badge-error;
}
&--error--inverted {
color: $uv-badge-error;
}
&--success {
background-color: $uv-badge-success;
}
&--success--inverted {
color: $uv-badge-success;
}
&--info {
background-color: $uv-badge-info;
}
&--info--inverted {
color: $uv-badge-info;
}
&--warning {
background-color: $uv-badge-warning;
}
&--warning--inverted {
color: $uv-badge-warning;
}
}
</style>
... ...
{
"id": "uv-badge",
"displayName": "uv-badge 徽标数,数字角标 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.2",
"description": "徽标数一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。",
"keywords": [
"uv-badge",
"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"
}
}
}
}
}
\ No newline at end of file
... ...
## Badge 徽标数
> **组件名:uv-badge**
该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
### <a href="https://www.uvui.cn/components/badge.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
... ...
## 1.0.15(2023-12-20)
1. 优化
## 1.0.14(2023-12-06)
1. 优化
## 1.0.13(2023-12-06)
1. 阻止事件冒泡处理
## 1.0.12(2023-10-19)
1. 增加后置插槽
## 1.0.11(2023-09-21)
1. 修复通过customStyle修改按钮宽度,组件中最外层节点不改变的问题
## 1.0.10(2023-09-15)
1. 按钮支持open-type="agreePrivacyAuthorization"
## 1.0.9(2023-09-11)
1. 增加参数iconSize,用于控制图标的大小
## 1.0.8(2023-09-10)
1. 修复多个按钮在一行宽度不正常的BUG
## 1.0.7(2023-09-07)
1. 修复warning颜色对应错误的BUG
## 1.0.6(2023-07-25)
1. 增加customTextStyle属性,方便自定义文字样式
## 1.0.5(2023-07-20)
1. 解决微信小程序动态设置hover-class点击态不消失的BUG
## 1.0.4(2023-06-29)
1. 修改上次更新出现nvue报错异常
## 1.0.3(2023-06-28)
修复:设置open-type="chooseAvatar"等值不生效的BUG
## 1.0.2(2023-06-01)
1. 修复按钮点击触发两次的BUG
## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
uv-button 按钮
... ...
$uv-button-active-opacity:0.75 !default;
$uv-button-loading-text-margin-left:4px !default;
$uv-button-text-color: #FFFFFF !default;
$uv-button-text-plain-error-color:$uv-error !default;
$uv-button-text-plain-warning-color:$uv-warning !default;
$uv-button-text-plain-success-color:$uv-success !default;
$uv-button-text-plain-info-color:$uv-info !default;
$uv-button-text-plain-primary-color:$uv-primary !default;
.uv-button {
&--active {
opacity: $uv-button-active-opacity;
}
&--active--plain {
background-color: rgb(217, 217, 217);
}
&__loading-text {
margin-left:$uv-button-loading-text-margin-left;
}
&__text,
&__loading-text {
color:$uv-button-text-color;
}
&__text--plain--error {
color:$uv-button-text-plain-error-color;
}
&__text--plain--warning {
color:$uv-button-text-plain-warning-color;
}
&__text--plain--success{
color:$uv-button-text-plain-success-color;
}
&__text--plain--info {
color:$uv-button-text-plain-info-color;
}
&__text--plain--primary {
color:$uv-button-text-plain-primary-color;
}
}
\ No newline at end of file
... ...
export default {
props: {
// 是否细边框
hairline: {
type: Boolean,
default: true
},
// 按钮的预置样式,info,primary,error,warning,success
type: {
type: String,
default: 'info'
},
// 按钮尺寸,large,normal,small,mini
size: {
type: String,
default: 'normal'
},
// 按钮形状,circle(两边为半圆),square(带圆角)
shape: {
type: String,
default: 'square'
},
// 按钮是否镂空
plain: {
type: Boolean,
default: false
},
// 是否禁止状态
disabled: {
type: Boolean,
default: false
},
// 是否加载中
loading: {
type: Boolean,
default: false
},
// 加载中提示文字
loadingText: {
type: [String, Number],
default: ''
},
// 加载状态图标类型
loadingMode: {
type: String,
default: 'spinner'
},
// 加载图标大小
loadingSize: {
type: [String, Number],
default: 14
},
// 开放能力,具体请看uniapp稳定关于button组件部分说明
// https://uniapp.dcloud.io/component/button
openType: {
type: String,
default: ''
},
// 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
// 取值为submit(提交表单),reset(重置表单)
formType: {
type: String,
default: ''
},
// 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
// 只微信小程序、QQ小程序有效
appParameter: {
type: String,
default: ''
},
// 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
hoverStopPropagation: {
type: Boolean,
default: true
},
// 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
lang: {
type: String,
default: 'en'
},
// 会话来源,open-type="contact"时有效。只微信小程序有效
sessionFrom: {
type: String,
default: ''
},
// 会话内消息卡片标题,open-type="contact"时有效
// 默认当前标题,只微信小程序有效
sendMessageTitle: {
type: String,
default: ''
},
// 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
// 默认当前分享路径,只微信小程序有效
sendMessagePath: {
type: String,
default: ''
},
// 会话内消息卡片图片,open-type="contact"时有效
// 默认当前页面截图,只微信小程序有效
sendMessageImg: {
type: String,
default: ''
},
// 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
// 用户点击后可以快速发送小程序消息,open-type="contact"时有效
showMessageCard: {
type: Boolean,
default: true
},
// 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
dataName: {
type: String,
default: ''
},
// 节流,一定时间内只能触发一次
throttleTime: {
type: [String, Number],
default: 0
},
// 按住后多久出现点击态,单位毫秒
hoverStartTime: {
type: [String, Number],
default: 0
},
// 手指松开后点击态保留时间,单位毫秒
hoverStayTime: {
type: [String, Number],
default: 200
},
// 按钮文字,之所以通过props传入,是因为slot传入的话
// nvue中无法控制文字的样式
text: {
type: [String, Number],
default: ''
},
// 按钮图标
icon: {
type: String,
default: ''
},
// 按钮图标大小
iconSize: {
type: [String, Number],
default: ''
},
// 按钮图标颜色
iconColor: {
type: String,
default: '#000000'
},
// 按钮颜色,支持传入linear-gradient渐变色
color: {
type: String,
default: ''
},
// 自定义按钮文本样式
customTextStyle: {
type: [Object,String],
default: ''
},
...uni.$uv?.props?.button
}
}
... ...
<template>
<view
class="uv-button-wrapper"
:style="[btnWrapperStyle]"
>
<!-- #ifndef APP-NVUE -->
<!-- #ifdef MP -->
<!-- 为了解决微信小程序动态设置hover-class点击态不消失的BUG -->
<view class="uv-button-wrapper--dis" v-if="disabled || loading"></view>
<button
:hover-start-time="Number(hoverStartTime)"
:hover-stay-time="Number(hoverStayTime)"
:form-type="formType"
:open-type="openType"
:app-parameter="appParameter"
:hover-stop-propagation="hoverStopPropagation"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:lang="lang"
:data-name="dataName"
:session-from="sessionFrom"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
@getphonenumber="onGetPhoneNumber"
@getuserinfo="onGetUserInfo"
@error="onError"
@opensetting="onOpenSetting"
@launchapp="onLaunchApp"
@contact="onContact"
@chooseavatar="onChooseavatar"
@agreeprivacyauthorization="onAgreeprivacyauthorization"
@addgroupapp="onAddgroupapp"
@chooseaddress="onChooseaddress"
@subscribe="onSubscribe"
@login="onLogin"
@im="onIm"
hover-class="uv-button--active"
class="uv-button uv-reset-button"
:style="[baseColor, $uv.addStyle(customStyle)]"
@tap="clickHandler"
:class="bemClass"
>
<!-- #endif -->
<!-- #ifndef MP -->
<button
:hover-start-time="Number(hoverStartTime)"
:hover-stay-time="Number(hoverStayTime)"
:form-type="formType"
:open-type="openType"
:app-parameter="appParameter"
:hover-stop-propagation="hoverStopPropagation"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:lang="lang"
:data-name="dataName"
:session-from="sessionFrom"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
:hover-class="!disabled && !loading ? 'uv-button--active' : ''"
class="uv-button uv-reset-button"
:style="[baseColor, $uv.addStyle(customStyle)]"
@tap="clickHandler"
:class="bemClass"
>
<!-- #endif -->
<template v-if="loading">
<uv-loading-icon
:mode="loadingMode"
:size="loadingSize * 1.15"
:color="loadingColor"
></uv-loading-icon>
<text
class="uv-button__loading-text"
:style="[
{ fontSize: textSize + 'px' },
$uv.addStyle(customTextStyle)
]"
>{{ loadingText || text }}</text>
</template>
<template v-else>
<uv-icon
v-if="icon"
:name="icon"
:color="iconColorCom"
:size="getIconSize"
:customStyle="{ marginRight: '2px' }"
></uv-icon>
<slot>
<text
class="uv-button__text"
:style="[
{ fontSize: textSize + 'px' },
$uv.addStyle(customTextStyle)
]"
>{{ text }}</text>
</slot>
<slot name="suffix"></slot>
</template>
</button>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view
:hover-start-time="Number(hoverStartTime)"
:hover-stay-time="Number(hoverStayTime)"
class="uv-button"
:hover-class="
!disabled && !loading && !color && (plain || type === 'info')
? 'uv-button--active--plain'
: !disabled && !loading && !plain
? 'uv-button--active'
: ''
"
@tap="clickHandler"
:class="bemClass"
:style="[baseColor, $uv.addStyle(customStyle)]"
>
<template v-if="loading">
<uv-loading-icon
:mode="loadingMode"
:size="loadingSize * 1.15"
:color="loadingColor"
></uv-loading-icon>
<text
class="uv-button__loading-text"
:style="[nvueTextStyle,$uv.addStyle(customTextStyle)]"
:class="[plain && `uv-button__text--plain--${type}`]"
>{{ loadingText || text }}</text>
</template>
<template v-else>
<uv-icon
v-if="icon"
:name="icon"
:color="iconColorCom"
:size="getIconSize"
></uv-icon>
<text
class="uv-button__text"
:style="[
{
marginLeft: icon ? '2px' : 0,
},
nvueTextStyle,
$uv.addStyle(customTextStyle)
]"
:class="[plain && `uv-button__text--plain--${type}`]"
>{{ text }}</text>
<slot name="suffix"></slot>
</template>
</view>
<!-- #endif -->
</view>
</template>
<script>
import throttle from '@/uni_modules/uv-ui-tools/libs/function/throttle.js';
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 button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
import props from "./props.js";
/**
* button 按钮
* @description Button 按钮
* @tutorial https://www.uvui.cn/components/button.html
* @property {Boolean} hairline 是否显示按钮的细边框 (默认 true )
* @property {String} type 按钮的预置样式,info,primary,error,warning,success (默认 'info' )
* @property {String} size 按钮尺寸,large,normal,mini (默认 normal)
* @property {String} shape 按钮形状,circle(两边为半圆),square(带圆角) (默认 'square' )
* @property {Boolean} plain 按钮是否镂空,背景色透明 (默认 false)
* @property {Boolean} disabled 是否禁用 (默认 false)
* @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈) (默认 false)
* @property {String | Number} loadingText 加载中提示文字
* @property {String} loadingMode 加载状态图标类型 (默认 'spinner' )
* @property {String | Number} loadingSize 加载图标大小 (默认 15 )
* @property {String} openType 开放能力,具体请看uniapp稳定关于button组件部分说明
* @property {String} formType 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
* @property {String} appParameter 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 (注:只微信小程序、QQ小程序有效)
* @property {Boolean} hoverStopPropagation 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效(默认 true )
* @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文(默认 en )
* @property {String} sessionFrom 会话来源,openType="contact"时有效
* @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效
* @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效
* @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效
* @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效(默认false)
* @property {String} dataName 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
* @property {String | Number} throttleTime 节流,一定时间内只能触发一次 (默认 0 )
* @property {String | Number} hoverStartTime 按住后多久出现点击态,单位毫秒 (默认 0 )
* @property {String | Number} hoverStayTime 手指松开后点击态保留时间,单位毫秒 (默认 200 )
* @property {String | Number} text 按钮文字,之所以通过props传入,是因为slot传入的话(注:nvue中无法控制文字的样式)
* @property {String} icon 按钮图标
* @property {String} iconColor 按钮图标颜色
* @property {String} color 按钮颜色,支持传入linear-gradient渐变色
* @property {Object} customStyle 定义需要用到的外部样式
* @event {Function} click 非禁止并且非加载中,才能点击
* @event {Function} getphonenumber open-type="getPhoneNumber"时有效
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
* @event {Function} error 当使用开放能力时,发生错误的回调
* @event {Function} opensetting 在打开授权设置页并关闭后回调
* @event {Function} launchapp 打开 APP 成功的回调
* @example <uv-button>月落</uv-button>
*/
export default {
name: "uv-button",
// #ifdef MP
mixins: [mpMixin, mixin, button, openType, props],
// #endif
// #ifndef MP
mixins: [mpMixin, mixin, props],
// #endif
emits: ['click'],
data() {
return {};
},
computed: {
// 生成bem风格的类名
bemClass() {
// this.bem为一个computed变量,在mixin中
if (!this.color) {
return this.bem("button",
["type", "shape", "size"],
["disabled", "plain", "hairline"]);
} else {
// 由于nvue的原因,在有color参数时,不需要传入type,否则会生成type相关的类型,影响最终的样式
return this.bem("button",
["shape", "size"],
["disabled", "plain", "hairline"]);
}
},
loadingColor() {
if (this.plain) {
// 如果有设置color值,则用color值,否则使用type主题颜色
return this.color ? this.color : '#3c9cff';
}
if (this.type === "info") {
return "#c9c9c9";
}
return "rgb(200, 200, 200)";
},
iconColorCom() {
// 如果是镂空状态,设置了color就用color值,否则使用主题颜色,
// uv-icon的color能接受一个主题颜色的值
if (this.iconColor) return this.iconColor;
if (this.plain) {
return this.color ? this.color : this.type;
} else {
return this.type === "info" ? "#000000" : "#ffffff";
}
},
baseColor() {
let style = {};
if (this.color) {
// 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
style.color = this.plain ? this.color : "white";
if (!this.plain) {
// 非镂空,背景色使用自定义的颜色
style["background-color"] = this.color;
}
if (this.color.indexOf("gradient") !== -1) {
// 如果自定义的颜色为渐变色,不显示边框,以及通过backgroundImage设置渐变色
// weex文档说明可以写borderWidth的形式,为什么这里需要分开写?
// 因为weex是阿里巴巴为了部门业绩考核而做的你懂的东西,所以需要这么写才有效
style.borderTopWidth = 0;
style.borderRightWidth = 0;
style.borderBottomWidth = 0;
style.borderLeftWidth = 0;
if (!this.plain) {
style.backgroundImage = this.color;
}
} else {
// 非渐变色,则设置边框相关的属性
style.borderColor = this.color;
style.borderWidth = "1px";
style.borderStyle = "solid";
}
}
return style;
},
// nvue版本按钮的字体不会继承父组件的颜色,需要对每一个text组件进行单独的设置
nvueTextStyle() {
let style = {};
// 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
if (this.type === "info") {
style.color = "#323233";
}
if (this.color) {
style.color = this.plain ? this.color : "white";
}
style.fontSize = this.textSize + "px";
return style;
},
// 字体大小
textSize() {
let fontSize = 14,
{ size } = this;
if (size === "large") fontSize = 16;
if (size === "normal") fontSize = 14;
if (size === "small") fontSize = 12;
if (size === "mini") fontSize = 10;
return fontSize;
},
// 设置图标大小
getIconSize() {
const size = this.iconSize ? this.iconSize : this.textSize * 1.35;
return this.$uv.addUnit(size);
},
// 设置外层盒子的宽度,其他样式不需要
btnWrapperStyle() {
const style = {};
const customStyle = this.$uv.addStyle(this.customStyle);
if(customStyle.width) style.width = customStyle.width;
return style;
}
},
methods: {
clickHandler() {
// 非禁止并且非加载中,才能点击
if (!this.disabled && !this.loading) {
// 进行节流控制,每this.throttle毫秒内,只在开始处执行
throttle(() => {
this.$emit("click");
}, this.throttleTime);
}
}
}
}
</script>
<style lang="scss" scoped>
$show-reset-button: 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';
/* #ifndef APP-NVUE */
@import "./vue.scss";
/* #endif */
/* #ifdef APP-NVUE */
@import "./nvue.scss";
/* #endif */
$uv-button-uv-button-height: 40px !default;
$uv-button-text-font-size: 15px !default;
$uv-button-loading-text-font-size: 15px !default;
$uv-button-loading-text-margin-left: 4px !default;
$uv-button-large-width: 100% !default;
$uv-button-large-height: 50px !default;
$uv-button-normal-padding: 0 12px !default;
$uv-button-large-padding: 0 15px !default;
$uv-button-normal-font-size: 14px !default;
$uv-button-small-min-width: 60px !default;
$uv-button-small-height: 30px !default;
$uv-button-small-padding: 0px 8px !default;
$uv-button-mini-padding: 0px 8px !default;
$uv-button-small-font-size: 12px !default;
$uv-button-mini-height: 22px !default;
$uv-button-mini-font-size: 10px !default;
$uv-button-mini-min-width: 50px !default;
$uv-button-disabled-opacity: 0.5 !default;
$uv-button-info-color: #323233 !default;
$uv-button-info-background-color: #fff !default;
$uv-button-info-border-color: #ebedf0 !default;
$uv-button-info-border-width: 1px !default;
$uv-button-info-border-style: solid !default;
$uv-button-success-color: #fff !default;
$uv-button-success-background-color: $uv-success !default;
$uv-button-success-border-color: $uv-button-success-background-color !default;
$uv-button-success-border-width: 1px !default;
$uv-button-success-border-style: solid !default;
$uv-button-primary-color: #fff !default;
$uv-button-primary-background-color: $uv-primary !default;
$uv-button-primary-border-color: $uv-button-primary-background-color !default;
$uv-button-primary-border-width: 1px !default;
$uv-button-primary-border-style: solid !default;
$uv-button-error-color: #fff !default;
$uv-button-error-background-color: $uv-error !default;
$uv-button-error-border-color: $uv-button-error-background-color !default;
$uv-button-error-border-width: 1px !default;
$uv-button-error-border-style: solid !default;
$uv-button-warning-color: #fff !default;
$uv-button-warning-background-color: $uv-warning !default;
$uv-button-warning-border-color: $uv-button-warning-background-color !default;
$uv-button-warning-border-width: 1px !default;
$uv-button-warning-border-style: solid !default;
$uv-button-block-width: 100% !default;
$uv-button-circle-border-top-right-radius: 100px !default;
$uv-button-circle-border-top-left-radius: 100px !default;
$uv-button-circle-border-bottom-left-radius: 100px !default;
$uv-button-circle-border-bottom-right-radius: 100px !default;
$uv-button-square-border-top-right-radius: 3px !default;
$uv-button-square-border-top-left-radius: 3px !default;
$uv-button-square-border-bottom-left-radius: 3px !default;
$uv-button-square-border-bottom-right-radius: 3px !default;
$uv-button-icon-min-width: 1em !default;
$uv-button-plain-background-color: #fff !default;
$uv-button-hairline-border-width: 0.5px !default;
.uv-button {
height: $uv-button-uv-button-height;
position: relative;
align-items: center;
justify-content: center;
@include flex;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
flex-direction: row;
&__text {
font-size: $uv-button-text-font-size;
}
&__loading-text {
font-size: $uv-button-loading-text-font-size;
margin-left: $uv-button-loading-text-margin-left;
}
&--large {
/* #ifndef APP-NVUE */
width: $uv-button-large-width;
/* #endif */
height: $uv-button-large-height;
padding: $uv-button-large-padding;
}
&--normal {
padding: $uv-button-normal-padding;
font-size: $uv-button-normal-font-size;
}
&--small {
/* #ifndef APP-NVUE */
min-width: $uv-button-small-min-width;
/* #endif */
height: $uv-button-small-height;
padding: $uv-button-small-padding;
font-size: $uv-button-small-font-size;
}
&--mini {
height: $uv-button-mini-height;
font-size: $uv-button-mini-font-size;
/* #ifndef APP-NVUE */
min-width: $uv-button-mini-min-width;
/* #endif */
padding: $uv-button-mini-padding;
}
&--disabled {
opacity: $uv-button-disabled-opacity;
}
&--info {
color: $uv-button-info-color;
background-color: $uv-button-info-background-color;
border-color: $uv-button-info-border-color;
border-width: $uv-button-info-border-width;
border-style: $uv-button-info-border-style;
}
&--success {
color: $uv-button-success-color;
background-color: $uv-button-success-background-color;
border-color: $uv-button-success-border-color;
border-width: $uv-button-success-border-width;
border-style: $uv-button-success-border-style;
}
&--primary {
color: $uv-button-primary-color;
background-color: $uv-button-primary-background-color;
border-color: $uv-button-primary-border-color;
border-width: $uv-button-primary-border-width;
border-style: $uv-button-primary-border-style;
}
&--error {
color: $uv-button-error-color;
background-color: $uv-button-error-background-color;
border-color: $uv-button-error-border-color;
border-width: $uv-button-error-border-width;
border-style: $uv-button-error-border-style;
}
&--warning {
color: $uv-button-warning-color;
background-color: $uv-button-warning-background-color;
border-color: $uv-button-warning-border-color;
border-width: $uv-button-warning-border-width;
border-style: $uv-button-warning-border-style;
}
&--block {
@include flex;
width: $uv-button-block-width;
}
&--circle {
border-top-right-radius: $uv-button-circle-border-top-right-radius;
border-top-left-radius: $uv-button-circle-border-top-left-radius;
border-bottom-left-radius: $uv-button-circle-border-bottom-left-radius;
border-bottom-right-radius: $uv-button-circle-border-bottom-right-radius;
}
&--square {
border-bottom-left-radius: $uv-button-square-border-top-right-radius;
border-bottom-right-radius: $uv-button-square-border-top-left-radius;
border-top-left-radius: $uv-button-square-border-bottom-left-radius;
border-top-right-radius: $uv-button-square-border-bottom-right-radius;
}
&__icon {
/* #ifndef APP-NVUE */
min-width: $uv-button-icon-min-width;
line-height: inherit !important;
vertical-align: top;
/* #endif */
}
&--plain {
background-color: $uv-button-plain-background-color;
}
&--hairline {
border-width: $uv-button-hairline-border-width !important;
}
}
</style>
... ...
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
// nvue下hover-class无效
$uv-button-before-top:50% !default;
$uv-button-before-left:50% !default;
$uv-button-before-width:100% !default;
$uv-button-before-height:100% !default;
$uv-button-before-transform:translate(-50%, -50%) !default;
$uv-button-before-opacity:0 !default;
$uv-button-before-background-color:#000 !default;
$uv-button-before-border-color:#000 !default;
$uv-button-active-before-opacity:.15 !default;
$uv-button-icon-margin-left:4px !default;
$uv-button-plain-uv-button-info-color:$uv-info;
$uv-button-plain-uv-button-success-color:$uv-success;
$uv-button-plain-uv-button-error-color:$uv-error;
$uv-button-plain-uv-button-warning-color:$uv-warning;
.uv-button-wrapper {
position: relative;
&--dis {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 9;
}
}
.uv-button {
width: 100%;
&__text {
white-space: nowrap;
line-height: 1;
}
&:before {
position: absolute;
top:$uv-button-before-top;
left:$uv-button-before-left;
width:$uv-button-before-width;
height:$uv-button-before-height;
border: inherit;
border-radius: inherit;
transform:$uv-button-before-transform;
opacity:$uv-button-before-opacity;
content: " ";
background-color:$uv-button-before-background-color;
border-color:$uv-button-before-border-color;
}
&--active {
&:before {
opacity: .15
}
}
&__icon+&__text:not(:empty),
&__loading-text {
margin-left:$uv-button-icon-margin-left;
}
&--plain {
&.uv-button--primary {
color: $uv-primary;
}
}
&--plain {
&.uv-button--info {
color:$uv-button-plain-uv-button-info-color;
}
}
&--plain {
&.uv-button--success {
color:$uv-button-plain-uv-button-success-color;
}
}
&--plain {
&.uv-button--error {
color:$uv-button-plain-uv-button-error-color;
}
}
&--plain {
&.uv-button--warning {
color:$uv-button-plain-uv-button-warning-color;
}
}
}
... ...
{
"id": "uv-button",
"displayName": "uv-button 按钮 全面兼容vue3+2、app、h5、小程序等多端",
"version": "1.0.15",
"description": "按钮组件内部实现以uni-app的button组件为基础,进行二次封装,灵活配置,功能齐全,兼容全端。",
"keywords": [
"uv-button",
"uvui",
"uv-ui",
"button",
"按钮"
],
"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-loading-icon",
"uv-icon"
],
"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"
}
}
}
}
}
\ No newline at end of file
... ...