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.

  1 +/unpackage
  2 +.DS_Store
  3 +.vscode
  1 +<script>
  2 + export default {
  3 + onLaunch: function() {
  4 + console.log('App Launch')
  5 + },
  6 + onShow: function() {
  7 + console.log('App Show')
  8 + },
  9 + onHide: function() {
  10 + console.log('App Hide')
  11 + }
  12 + }
  13 +</script>
  14 +
  15 +<style>
  16 + /*每个页面公共css */
  17 +</style>
  1 +import { request } from '../utils/request.js';
  2 +
  3 +export const getDeviceList = () => {
  4 + return request({
  5 + url: '/device/list',
  6 + method: 'GET',
  7 + });
  8 +};
  9 +
  10 +// 登录
  11 +export const loginApi = (data) => {
  12 + return request({
  13 + url: '/api/login',
  14 + method: 'POST',
  15 + data,
  16 + });
  17 +};
  18 +
  19 +// 获取课程列表
  20 +export const courseListApi = (data) => {
  21 + return request({
  22 + url: '/api/course/get-list',
  23 + method: 'GET',
  24 + data,
  25 + });
  26 +};
  27 +
  28 +// 获取选班数据
  29 +export const getSelectApi = (data) => {
  30 + return request({
  31 + url: '/api/course/get-detail',
  32 + method: 'POST',
  33 + data,
  34 + });
  35 +};
  36 +
  37 +// 获取我的孩子
  38 +export const myStudentsApi = (data) => {
  39 + return request({
  40 + url: '/api/student/my-students',
  41 + method: 'GET',
  42 + data,
  43 + });
  44 +};
  45 +
  46 +// 添加我的孩子
  47 +export const addStudentsApi = (data) => {
  48 + return request({
  49 + url: '/api/student/create',
  50 + method: 'POST',
  51 + data,
  52 + });
  53 +};
  54 +
  55 +// 报名
  56 +export const signUpApi = (data) => {
  57 + return request({
  58 + url: '/api/course/sign-up',
  59 + method: 'POST',
  60 + data,
  61 + });
  62 +};
  1 +<!DOCTYPE html>
  2 +<html lang="zh-CN">
  3 + <head>
  4 + <meta charset="UTF-8" />
  5 + <script>
  6 + var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
  7 + CSS.supports('top: constant(a)'))
  8 + document.write(
  9 + '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
  10 + (coverSupport ? ', viewport-fit=cover' : '') + '" />')
  11 + </script>
  12 + <title></title>
  13 + <!--preload-links-->
  14 + <!--app-context-->
  15 + </head>
  16 + <body>
  17 + <div id="app"><!--app-html--></div>
  18 + <script type="module" src="/main.js"></script>
  19 + </body>
  20 +</html>
  1 +import App from './App'
  2 +
  3 +import { createSSRApp } from 'vue'
  4 +
  5 +export function createApp() {
  6 + const app = createSSRApp(App)
  7 + return {
  8 + app
  9 + }
  10 +}
  1 +{
  2 + "name" : "learn-tiny",
  3 + "appid" : "__UNI__F0A7B80",
  4 + "description" : "",
  5 + "versionName" : "1.0.0",
  6 + "versionCode" : "100",
  7 + "transformPx" : false,
  8 + /* 5+App特有相关 */
  9 + "app-plus" : {
  10 + "usingComponents" : true,
  11 + "nvueStyleCompiler" : "uni-app",
  12 + "compilerVersion" : 3,
  13 + "splashscreen" : {
  14 + "alwaysShowBeforeRender" : true,
  15 + "waiting" : true,
  16 + "autoclose" : true,
  17 + "delay" : 0
  18 + },
  19 + /* 模块配置 */
  20 + "modules" : {},
  21 + /* 应用发布信息 */
  22 + "distribute" : {
  23 + /* android打包配置 */
  24 + "android" : {
  25 + "permissions" : [
  26 + "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
  27 + "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
  28 + "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
  29 + "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
  30 + "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
  31 + "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
  32 + "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
  33 + "<uses-permission android:name=\"android.permission.CAMERA\"/>",
  34 + "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
  35 + "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
  36 + "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
  37 + "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
  38 + "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
  39 + "<uses-feature android:name=\"android.hardware.camera\"/>",
  40 + "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
  41 + ]
  42 + },
  43 + /* ios打包配置 */
  44 + "ios" : {},
  45 + /* SDK配置 */
  46 + "sdkConfigs" : {}
  47 + }
  48 + },
  49 + /* 快应用特有相关 */
  50 + "quickapp" : {},
  51 + /* 小程序特有相关 */
  52 + "mp-weixin" : {
  53 + "appid" : "wxa0a9919d4066da95",
  54 + "setting" : {
  55 + "urlCheck" : false,
  56 + "minified" : true
  57 + },
  58 + "usingComponents" : true
  59 + },
  60 + "mp-alipay" : {
  61 + "usingComponents" : true
  62 + },
  63 + "mp-baidu" : {
  64 + "usingComponents" : true
  65 + },
  66 + "mp-toutiao" : {
  67 + "usingComponents" : true
  68 + },
  69 + "uniStatistics" : {
  70 + "enable" : false
  71 + },
  72 + "vueVersion" : "3"
  73 +}
  1 +{
  2 + "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
  3 + {
  4 + "path": "pages/index/index",
  5 + "style": {
  6 + "navigationBarTitleText": "",
  7 + "navigationStyle": "custom"
  8 + }
  9 + },
  10 + {
  11 + "path": "pages/my/my",
  12 + "style": {
  13 + "navigationBarTitleText": "我的"
  14 + }
  15 + },
  16 + {
  17 + "path" : "pages/login/login",
  18 + "style" :
  19 + {
  20 + "navigationBarTitleText" : "登录"
  21 + }
  22 + },
  23 + {
  24 + "path": "pages/signUp/signUp",
  25 + "style": {
  26 + "navigationBarTitleText": "报名"
  27 + }
  28 + },
  29 + {
  30 + "path": "pages/addStudent/addStudent",
  31 + "style": {
  32 + "navigationBarTitleText": "添加孩子"
  33 + }
  34 + }
  35 + ],
  36 + "tabBar": {
  37 + "color": "#7A7E83",
  38 + "selectedColor": "#f96118",
  39 + "list": [{
  40 + "pagePath": "pages/index/index",
  41 + "iconPath": "static/tabBar/home.png",
  42 + "selectedIconPath": "static/tabBar/homeAc.png",
  43 + "text": "发现"
  44 + }, {
  45 + "pagePath": "pages/my/my",
  46 + "iconPath": "static/tabBar/my.png",
  47 + "selectedIconPath": "static/tabBar/myAc.png",
  48 + "text": "家长"
  49 + }]
  50 + },
  51 + "globalStyle": {
  52 + "navigationBarTextStyle": "black",
  53 + "navigationBarBackgroundColor": "#eaecfb"
  54 + },
  55 + "uniIdRouter": {}
  56 +}
  1 +<template>
  2 + <view class="container">
  3 + <uv-form labelPosition="left" :model="state.studentInfo" :rules="state.rules" ref="formRef" class="form-box">
  4 + <uv-form-item prop="name">
  5 + <uv-input v-model="state.studentInfo.name" shape="circle" placeholder="请输入学生真实姓名">
  6 + </uv-input>
  7 + </uv-form-item>
  8 + <uv-form-item prop="sex">
  9 + <view style="display: flex;justify-content: space-around;width: 100%;">
  10 + <view @click="state.studentInfo.sex = 0" class="select-item" :style="state.studentInfo.sex === 0 ? 'border-color:#6b75f6;' : ''">
  11 + <uv-icon name="man" color="#7175f0" size="16" style="margin-right: 10rpx;"></uv-icon>男
  12 + </view>
  13 + <view @click="state.studentInfo.sex = 1" class="select-item" :style="state.studentInfo.sex === 1 ? 'border-color:#6b75f6;' : ''">
  14 + <uv-icon name="woman" color="#ff5d5c" size="16" style="margin-right: 10rpx;"></uv-icon>女
  15 + </view>
  16 + <view @click="state.studentInfo.sex = 2" class="select-item" :style="state.studentInfo.sex === 2 ? 'border-color:#6b75f6;' : ''">
  17 + <uv-icon name="lock" color="#7175f0" size="16" style="margin-right: 10rpx;"></uv-icon>保密
  18 + </view>
  19 + </view>
  20 + </uv-form-item>
  21 + <uv-form-item prop="city">
  22 + <uv-input v-model="state.studentInfo.city" shape="circle" placeholder="请输入学生所在城市" />
  23 + </uv-form-item>
  24 + <uv-form-item prop="school">
  25 + <uv-input v-model="state.studentInfo.school" shape="circle" placeholder="请输入学生在读学校" />
  26 + </uv-form-item>
  27 + <uv-form-item prop="grade">
  28 + <uv-input v-model="state.studentInfo.grade" shape="circle" placeholder="请输入学生在读年级" />
  29 + </uv-form-item>
  30 + <uv-form-item>
  31 + <view @click="submit"
  32 + style="width: 100%;background-color: #7175f0;color: #fff;text-align: center;padding: 20rpx 0;border-radius: 9999rpx;">
  33 + 确认添加
  34 + </view>
  35 + </uv-form-item>
  36 + </uv-form>
  37 + </view>
  38 +</template>
  39 +
  40 +<script setup>
  41 + import {
  42 + reactive,
  43 + ref
  44 + } from "vue"
  45 + import { addStudentsApi } from "@/api/index.js"
  46 +
  47 + const formRef = ref(null)
  48 +
  49 + const state = reactive({
  50 + studentInfo: {
  51 + name: "",
  52 + sex: "",
  53 + city: "",
  54 + school: "",
  55 + grade: ""
  56 + },
  57 + rules: {
  58 + 'name': {
  59 + type: 'string',
  60 + required: true,
  61 + message: '请输入学生真实姓名',
  62 + trigger: ['blur', 'change']
  63 + },
  64 + 'sex': {
  65 + type: 'number',
  66 + required: true,
  67 + message: '请选择性别',
  68 + trigger: ['blur', 'change']
  69 + },
  70 + 'city': {
  71 + type: 'string',
  72 + required: true,
  73 + message: '请输入学生所在城市',
  74 + trigger: ['blur', 'change']
  75 + },
  76 + 'school': {
  77 + type: 'string',
  78 + required: true,
  79 + message: '请输入学生在读学校',
  80 + trigger: ['blur', 'change']
  81 + },
  82 + 'grade': {
  83 + type: 'string',
  84 + required: true,
  85 + message: '请输入学生在读年级',
  86 + trigger: ['blur', 'change']
  87 + },
  88 + }
  89 + })
  90 +
  91 + const submit = async () => {
  92 + try {
  93 + await formRef.value.validate()
  94 +
  95 + const res = await addStudentsApi(state.studentInfo)
  96 + uni.showToast({
  97 + icon: 'success',
  98 + title: '添加成功'
  99 + })
  100 + formRef.value.resetFields()
  101 + uni.navigateBack()
  102 + } catch {}
  103 + }
  104 +</script>
  105 +
  106 +<style lang="scss" scoped>
  107 + .container {
  108 + background: linear-gradient(135deg, #eaecfb 10%, #f6f7fb 90%);
  109 + background-color: red;
  110 + height: calc(100vh - var(--tab-bar-height));
  111 + overflow-y: auto;
  112 + padding: 0 24rpx;
  113 + font-size: 28rpx;
  114 + }
  115 +
  116 + .form-box {
  117 + margin-top: 80rpx;
  118 + background-color: #fff;
  119 + padding: 20rpx;
  120 + border-radius: 40rpx;
  121 + }
  122 +
  123 + .select-item {
  124 + background-color: #e9ecfc;
  125 + width: 28%;
  126 + display: flex;
  127 + justify-content: center;
  128 + align-items: center;
  129 + padding: 10rpx 0;
  130 + border-radius: 9999rpx;
  131 + border: 3rpx solid #e8ecfd;
  132 + }
  133 +</style>
  1 +<template>
  2 + <view class="container">
  3 +  <uv-navbar leftIcon="" placeholder bgColor="rgba(255, 255, 255, 0)">
  4 + <template v-slot:left>
  5 + <view class="uv-nav-slot">
  6 + XXXX
  7 + </view>
  8 + </template>
  9 + </uv-navbar>
  10 +
  11 + <view class="content-box">
  12 + <view style="display: grid;grid-template-columns: 47.5% 47.5%;gap: 5%;">
  13 + <view @click="showGradesPop" class="select-item">
  14 + {{ state.grade }}<uv-icon name="arrow-down-fill" color="#7175f0" size="20"
  15 + style="margin-left: 6rpx;"></uv-icon>
  16 + </view>
  17 + <view @click="showAccountsPop" class="select-item">
  18 + {{ state.accounts }}<uv-icon name="arrow-down-fill" color="#7175f0" size="20" style="margin-left: 6rpx;"></uv-icon>
  19 + </view>
  20 + </view>
  21 +
  22 + <view class="course-list">
  23 + <view v-for="item in state.courses" class="course-box">
  24 + <image :src="item.img" style="width: 160rpx;height: 160rpx;border-radius: 20rpx;"></image>
  25 + <view style="flex: 1;display: flex;">
  26 + <view style="flex: 1;padding: 0 20rpx;display: flex;flex-direction: column;justify-content: space-between;">
  27 + <view style="font-size: 30rpx;">{{ item.name }} - {{ item.subject }}</view>
  28 + <view style="color: #ff3838;font-weight: 600;">¥{{ item.price }}</view>
  29 + </view>
  30 + <view style="width: 160rpx;display: flex;align-items: center;justify-content: flex-end;">
  31 + <view @click="gotoSignUp(item.id)" style="width: 100%;;background-color: #f57c28;color: #fff;text-align: center;padding: 10rpx 0;border-radius: 9999rpx;">
  32 + 去报名
  33 + </view>
  34 + </view>
  35 + </view>
  36 + </view>
  37 + </view>
  38 + </view>
  39 +
  40 + <!-- 年级弹窗 -->
  41 + <uv-popup ref="gradesRef" safeAreaInsetBottom closeable :round="20">
  42 + <view class="pop-box">
  43 + <view class="content-pop">
  44 + <view @click="selectGrade('全部年级')" class="pop-inner-item" style="margin: 20rpx 0;" :style="state.grade === '全部年级' ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''">
  45 + 全部年级
  46 + </view>
  47 + <view v-for="item in state.gradOptions" style="margin-bottom: 30rpx;">
  48 + <view class="pop-title">
  49 + {{ item.title }}
  50 + </view>
  51 + <view class="pop-inner">
  52 + <view v-for="item1 in item.content" @click="selectGrade(item1)" class="pop-inner-item"
  53 + :style="state.grade === item1 ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''">
  54 + {{ item1 }}
  55 + </view>
  56 + </view>
  57 + </view>
  58 + </view>
  59 + </view>
  60 + </uv-popup>
  61 +
  62 + <!-- 科目弹窗 -->
  63 + <uv-popup ref="accountsRef" safeAreaInsetBottom closeable :round="20">
  64 + <view class="pop-box">
  65 + <view class="content-pop">
  66 + <view @click="selectaccounts('全部科目')" class="pop-inner-item" style="margin: 20rpx 0;" :style="state.accounts === '全部科目' ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''">
  67 + 全部科目
  68 + </view>
  69 + <view class="pop-inner">
  70 + <view v-for="item1 in state.accountsOptions" @click="selectaccounts(item1)" class="pop-inner-item"
  71 + :style="state.accounts === item1 ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''">
  72 + {{ item1 }}
  73 + </view>
  74 + </view>
  75 + </view>
  76 + </view>
  77 + </uv-popup>
  78 + </view>
  79 +</template>
  80 +
  81 +<script setup>
  82 + import {
  83 + reactive,
  84 + ref,
  85 + onMounted
  86 + } from 'vue';
  87 + import { courseListApi } from "@/api/index.js"
  88 +
  89 + const gradesRef = ref(null)
  90 + const accountsRef = ref(null)
  91 +
  92 + const state = reactive({
  93 + grade: "全部年级",
  94 + accounts: "全部科目",
  95 + courses: [],
  96 + subject: [],
  97 + gradOptions: [{
  98 + title: "小学",
  99 + content: ["一年级", "二年级", "三年级", "四年级", "五年级", "六年级"]
  100 + },
  101 + {
  102 + title: "初中",
  103 + content: ["初一", "初二", "初三"]
  104 + },
  105 + {
  106 + title: "高中",
  107 + content: ["高一", "高二", "高三"]
  108 + }
  109 + ],
  110 + accountsOptions: ["语文", "数学", "英语", "物理", "化学"]
  111 + })
  112 +
  113 + onMounted(() => {
  114 + getCourseList()
  115 + })
  116 +
  117 + const gotoSignUp = id => {
  118 + uni.navigateTo({
  119 + url: `/pages/signUp/signUp?id=${id}`
  120 + })
  121 + }
  122 +
  123 + const getCourseList = async () => {
  124 + try {
  125 + const { data } = await courseListApi({
  126 + grade: state.grade === "全部年级" ? "" : state.grade,
  127 + subject: state.accounts === "全部科目" ? "" : state.accounts
  128 + })
  129 + state.courses = data.courses
  130 + } catch {}
  131 + }
  132 +
  133 + const showGradesPop = () => {
  134 + gradesRef.value.open('bottom')
  135 + }
  136 +
  137 + const showAccountsPop = () => {
  138 + accountsRef.value.open('bottom')
  139 + }
  140 +
  141 + const selectGrade = item => {
  142 + state.grade = item
  143 + gradesRef.value.close()
  144 + getCourseList()
  145 + }
  146 +
  147 + const selectaccounts = item => {
  148 + state.accounts = item
  149 + accountsRef.value.close()
  150 + getCourseList()
  151 + }
  152 +</script>
  153 +
  154 +<style lang="scss" scoped>
  155 + .container {
  156 + background: linear-gradient(135deg, #d4d6fe 10%, #f6f7fb 90%);
  157 + background-color: red;
  158 + height: calc(100vh - var(--tab-bar-height));
  159 + display: flex;
  160 + flex-direction: column;
  161 + }
  162 +
  163 + .content-box {
  164 + flex: 1;
  165 + overflow-y: auto;
  166 + padding: 0 24rpx;
  167 + display: flex;
  168 + flex-direction: column;
  169 +
  170 + .course-list {
  171 + margin-top: 20rpx;
  172 + flex: 1;
  173 + .course-box {
  174 + display: flex;
  175 + justify-content: space-between;
  176 + margin-bottom: 30rpx;
  177 + background-color: #fff;
  178 + padding: 20rpx;
  179 + border-radius: 20rpx;
  180 + }
  181 + }
  182 + }
  183 +
  184 + .select-item {
  185 + flex: 1;
  186 + display: flex;
  187 + align-items: center;
  188 + justify-content: center;
  189 + background-color: #fff;
  190 + padding: 16rpx 0;
  191 + border-radius: 9999rpx;
  192 + }
  193 +
  194 + .pop-box {
  195 + padding: 70rpx 20rpx;
  196 + padding-bottom: calc(var(--tab-bar-height) + 20rpx);
  197 +
  198 + .content-pop {
  199 + max-height: 40vh;
  200 + min-height: 200rpx;
  201 + overflow-y: auto;
  202 +
  203 + scrollbar-width: none;
  204 + -ms-overflow-style: none;
  205 +
  206 + &::-webkit-scrollbar {
  207 + display: none;
  208 + }
  209 + }
  210 + }
  211 +
  212 + .pop-title {
  213 + margin-bottom: 20rpx;
  214 + position: relative;
  215 + padding-left: 12rpx;
  216 +
  217 + &:before {
  218 + content: "";
  219 + width: 6rpx;
  220 + height: 28rpx;
  221 + border-radius: 3rpx;
  222 + background-color: #ffb50f;
  223 + position: absolute;
  224 + top: 50%;
  225 + left: 0;
  226 + transform: translateY(-50%);
  227 + }
  228 + }
  229 +
  230 + .pop-inner {
  231 + display: grid;
  232 + grid-template-columns: repeat(4, 1fr);
  233 + gap: (20rpx);
  234 + }
  235 +
  236 + .pop-inner-item {
  237 + font-size: 28rpx;
  238 + border: 2rpx solid #ccc;
  239 + text-align: center;
  240 + padding: 10rpx 0;
  241 + border-radius: 9999rpx;
  242 + }
  243 +</style>
  1 +<template>
  2 + <view class="container">
  3 + <view class="input-box">
  4 + <uv-form labelPosition="left" :model="state.userInfo" :rules="state.rules" labelWidth="60" ref="formRef"
  5 + :labelStyle="{ fontSize: '28rpx' }">
  6 + <view style="margin-bottom: 60rpx;">
  7 + 欢迎登录XXXXXXXX!
  8 + </view>
  9 + <uv-form-item label="姓名" prop="name" borderBottom>
  10 + <uv-input v-model="state.userInfo.name" placeholder="请输入姓名" border="none">
  11 + </uv-input>
  12 + </uv-form-item>
  13 + <uv-form-item label="手机号" prop="phonenumber" borderBottom>
  14 + <uv-input v-model="state.userInfo.phonenumber" placeholder="请输入手机号" border="none"></uv-input>
  15 + </uv-form-item>
  16 + <view @click="loginHandler" class="login-btn">
  17 + 登 录
  18 + </view>
  19 + </uv-form>
  20 + </view>
  21 + </view>
  22 +</template>
  23 +
  24 +<script setup>
  25 + import {
  26 + reactive,
  27 + ref
  28 + } from 'vue';
  29 + import {
  30 + loginApi
  31 + } from "@/api/index.js"
  32 +
  33 + const formRef = ref(null)
  34 +
  35 + const state = reactive({
  36 + userInfo: {
  37 + name: "",
  38 + phonenumber: ""
  39 + },
  40 + rules: {
  41 + 'name': {
  42 + type: 'string',
  43 + required: true,
  44 + message: '请输入姓名',
  45 + trigger: ['blur', 'change']
  46 + },
  47 + 'phonenumber': [{
  48 + type: 'string',
  49 + required: true,
  50 + message: '请输入手机号',
  51 + trigger: ['blur', 'change']
  52 + }, {
  53 + validator: (rule, value, callback) => {
  54 + const mobileRegexLoose = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/
  55 + return mobileRegexLoose.test(value);
  56 + },
  57 + message: '手机号码不正确',
  58 + trigger: ['blur'],
  59 + }],
  60 + }
  61 + })
  62 +
  63 + const loginHandler = async () => {
  64 + try {
  65 + await formRef.value.validate()
  66 +
  67 + const {
  68 + token
  69 + } = await loginApi(state.userInfo)
  70 + uni.showToast({
  71 + icon: 'success',
  72 + title: '登录成功'
  73 + })
  74 + uni.setStorageSync('token', token)
  75 + uni.navigateBack()
  76 + } catch {}
  77 + }
  78 +</script>
  79 +
  80 +<style lang="scss" scoped>
  81 + .container {
  82 + background: linear-gradient(135deg, #eaecfb 10%, #f6f7fb 90%);
  83 + background-color: red;
  84 + height: 100vh;
  85 + overflow-y: auto;
  86 + padding: 0 28rpx;
  87 + }
  88 +
  89 + .input-box {
  90 + position: relative;
  91 + margin-top: 120rpx;
  92 + padding: 48rpx;
  93 + padding-top: 80rpx;
  94 + background-color: #fff;
  95 + border-radius: 12rpx;
  96 + box-shadow: 0 0 20rpx #eee;
  97 + }
  98 +
  99 + ::v-deep .uv-line {
  100 + border-bottom-style: dashed !important;
  101 + }
  102 +
  103 + .login-btn {
  104 + margin-top: 80rpx;
  105 + background-color: #8386ef;
  106 + color: #fff;
  107 + text-align: center;
  108 + padding: 16rpx 0;
  109 + border-radius: 20rpx;
  110 + }
  111 +</style>
  1 +<template>
  2 + <view class="container">
  3 + <!-- 自定义标题栏 -->
  4 + <uv-navbar leftIcon="" placeholder bgColor="/static/topBg.png">
  5 + <template v-slot:center>
  6 + <view class="uv-nav-slot">
  7 + <image src="/static/my/myTitle.png" mode="heightFix" style="height: 44rpx;"></image>
  8 + </view>
  9 + </template>
  10 + </uv-navbar>
  11 +
  12 + <view class="content-box">
  13 + <div class="content-padding">
  14 + <!-- 用户信息 -->
  15 + <view class="user-info">
  16 + <view class="center-avatar">
  17 + <view class="center-inner">
  18 + <image src="/static/my/avatar.png" style="width: 100%;height: 100%;"></image>
  19 + </view>
  20 + </view>
  21 + <view class="loginTo-btn">
  22 + 登录/注册
  23 + </view>
  24 + </view>
  25 +
  26 + <view class="nav-list">
  27 + <view class="nav-item">
  28 + <view style="display: flex;align-items: center;">
  29 + <uv-icon name="/static/my/student.png" :size="22"></uv-icon>
  30 + <view style="margin-left: 26rpx;color: #6a6a6a;">绑定学生</view>
  31 + </view>
  32 + <uv-icon name="arrow-right" :size="16"></uv-icon>
  33 + </view>
  34 + <view class="nav-item">
  35 + <view style="display: flex;align-items: center;">
  36 + <uv-icon name="/static/my/print.png" :size="24"></uv-icon>
  37 + <view style="margin-left: 26rpx;color: #6a6a6a;">打印记录</view>
  38 + </view>
  39 + <uv-icon name="arrow-right" :size="16"></uv-icon>
  40 + </view>
  41 + </view>
  42 + </div>
  43 + </view>
  44 + </view>
  45 +</template>
  46 +
  47 +<script setup>
  48 +
  49 +</script>
  50 +
  51 +<style lang="scss" scoped>
  52 + .container {
  53 + background-color: #f7f8fa;
  54 + height: 100vh;
  55 + overflow-y: auto;
  56 + display: flex;
  57 + flex-direction: column;
  58 + }
  59 +
  60 + .content-box {
  61 + flex: 1;
  62 + overflow-y: auto;
  63 + background-image: url('~@/static/bottomBg.png');
  64 + background-repeat: no-repeat;
  65 + background-size: 100% auto;
  66 + }
  67 +
  68 + .content-padding {
  69 + padding: 0 28rpx;
  70 + }
  71 +
  72 + .user-info {
  73 + width: 100%;
  74 + height: 440rpx;
  75 + background-image: url('~@/static/my/information.png');
  76 + background-repeat: no-repeat;
  77 + background-size: 100% auto;
  78 + background-position: bottom;
  79 + background-color: transparent;
  80 + position: relative;
  81 +
  82 + .center-avatar {
  83 + position: absolute;
  84 + top: 20rpx;
  85 + left: 50%;
  86 + transform: translateX(-50%);
  87 + width: 34%;
  88 + aspect-ratio: 1/1;
  89 + background-color: #fff;
  90 + border-radius: 50%;
  91 +
  92 + .center-inner {
  93 + width: 100%;
  94 + height: 100%;
  95 + border-radius: 50%;
  96 + overflow: hidden;
  97 + }
  98 + }
  99 +
  100 + .loginTo-btn {
  101 + position: absolute;
  102 + left: 50%;
  103 + bottom: 60rpx;
  104 + transform: translateX(-50%);
  105 + color: #fff;
  106 + font-size: 44rpx;
  107 + }
  108 + }
  109 +
  110 + .nav-list {
  111 + background-color: #fff;
  112 + border-radius: 12rpx;
  113 + box-shadow: 0 0 20rpx #eee;
  114 + padding: 10rpx 20rpx;
  115 + box-sizing: border-box;
  116 + margin-top: 40rpx;
  117 +
  118 + .nav-item {
  119 + display: flex;
  120 + justify-content: space-between;
  121 + border-bottom: 2rpx dashed #d8d8d8;
  122 + padding: 30rpx 0;
  123 +
  124 + &:last-child {
  125 + border: none;
  126 + }
  127 + }
  128 + }
  129 +</style>
  1 +<template>
  2 + <view class="container">
  3 + <view class="student-box">
  4 + <view class="title">
  5 + 报名学员
  6 + </view>
  7 + <view style="display: flex;align-items: center;">
  8 + <template v-if="state.students.length">
  9 + <view @click="studentsRef.open('bottom')">{{ state.students[state.activeIndex].name }}</view>
  10 + <uv-icon @click="studentsRef.open('bottom')" name="arrow-down" color="#7175f0" size="14"
  11 + style="margin-left: 10rpx;"></uv-icon>
  12 + </template>
  13 + <template v-else>
  14 + <view @click="addStudents" style="color: #7175f0;">点击添加</view>
  15 + <uv-icon @click="addStudents" name="arrow-right" color="#7175f0" size="14"
  16 + style="margin-left: 10rpx;"></uv-icon>
  17 + </template>
  18 + </view>
  19 + </view>
  20 +
  21 + <view class="student-box" style="margin-top: 30rpx;display: block;">
  22 + <view class="title" style="margin-bottom: 30rpx;">
  23 + 选择地区
  24 + </view>
  25 + <view style="display: grid;grid-template-columns: repeat(3, 1fr);gap: 20rpx;">
  26 + <view v-for="item in state.selectData.schoolArr" @click="select1(item)"
  27 + :style="state.schoolName === item.text ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''"
  28 + :class="item.status === '可选' ? 'select-item' : 'select-item no-select'">
  29 + {{ item.text }}
  30 + </view>
  31 + </view>
  32 + </view>
  33 +
  34 + <view class="student-box" style="margin-top: 30rpx;display: block;">
  35 + <view class="title" style="margin-bottom: 30rpx;">
  36 + 选择班型
  37 + </view>
  38 + <view style="display: grid;grid-template-columns: repeat(3, 1fr);gap: 20rpx;">
  39 + <view v-for="item in state.selectData.classArr" @click="select2(item)"
  40 + :style="state.className === item.text ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''"
  41 + :class="item.status === '可选' ? 'select-item' : 'select-item no-select'">
  42 + {{ item.text }}
  43 + </view>
  44 + </view>
  45 + </view>
  46 +
  47 + <view class="student-box" style="margin-top: 30rpx;display: block;">
  48 + <view class="title" style="margin-bottom: 30rpx;">
  49 + 选择老师
  50 + </view>
  51 + <view style="display: grid;grid-template-columns: repeat(3, 1fr);gap: 20rpx;">
  52 + <view v-for="item in state.selectData.teacherArr" @click="select3(item)"
  53 + :style="state.teacherName === item.text ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''"
  54 + :class="item.status === '可选' ? 'select-item' : 'select-item no-select'">
  55 + {{ item.text }}
  56 + </view>
  57 + </view>
  58 + </view>
  59 +
  60 + <view class="student-box" style="margin-top: 30rpx;display: block;">
  61 + <view class="title" style="margin-bottom: 30rpx;">
  62 + 选择时段
  63 + </view>
  64 + <view>
  65 + <!-- body -->
  66 + <view class="body-box">
  67 + <view class="body-row">
  68 + <view class="cell" style="background-color: #c4d0fc;height: 70rpx;">
  69 + 日期
  70 + </view>
  71 + <view v-for="item in state.selectData?.tableData?.x" class="cell"
  72 + style="background-color: #c4d0fc;height: 70rpx;">
  73 + {{ item }}
  74 + </view>
  75 + </view>
  76 + <!-- 行 -->
  77 + <view v-for="item in state.selectData?.tableData?.y" class="body-row">
  78 + <!-- 列 -->
  79 + <view class="cell">
  80 + {{ item }}
  81 + </view>
  82 + <view v-for="item1 in state.selectData?.tableData?.x" @click="selectTable(item, item1)"
  83 + :class="(!!state.courseDate && !!state.courseTime && state.courseDate === item1 && state.courseTime === item) ? 'cell select-one' : 'cell'">
  84 + {{ filterCell(item, item1) }}
  85 + </view>
  86 + </view>
  87 + </view>
  88 + </view>
  89 + </view>
  90 +
  91 + <view @click="resetSelect" style="font-size: 24rpx;color: #7175f0;display: flex;justify-content: center;align-items: center;margin: 40rpx 0;">
  92 + <uv-icon name="reload" color="#7175f0" size="14" style="margin-right: 10rpx;"></uv-icon>重置所有选择
  93 + </view>
  94 +
  95 + <view @click="signUpHandler" style="background-color: #7175f0;color: #fff;text-align: center;border-radius: 9999rpx;padding: 16rpx 0;margin-bottom: 70rpx;">
  96 + 立即报名
  97 + </view>
  98 +
  99 + <!-- 选择学员弹窗 -->
  100 + <uv-popup ref="studentsRef" safeAreaInsetBottom closeable :round="20">
  101 + <view class="pop-box">
  102 + <view class="content-pop">
  103 + <view v-for="(item, index) in state.students" @click="state.activeIndex = index"
  104 + class="pop-inner-item"
  105 + :style="state.activeIndex === index ? 'border-color:#7175f0;color:#7175f0;background-color:#f6f7ff;' : ''">
  106 + {{ item.name }}({{ item.grade }})
  107 + </view>
  108 + <view @click="addStudents" class="pop-inner-item"
  109 + style="display: flex;align-items: center;justify-content: center;">
  110 + <uv-icon name="plus" size="14" style="margin-right: 10rpx;"></uv-icon> 新增学员
  111 + </view>
  112 + </view>
  113 + </view>
  114 + </uv-popup>
  115 + </view>
  116 +</template>
  117 +
  118 +<script setup>
  119 + import {
  120 + onMounted,
  121 + reactive,
  122 + ref
  123 + } from "vue"
  124 + import {
  125 + myStudentsApi,
  126 + getSelectApi,
  127 + signUpApi
  128 + } from "@/api/index.js"
  129 + import {
  130 + onLoad
  131 + } from '@dcloudio/uni-app'
  132 +
  133 + const studentsRef = ref(null)
  134 +
  135 + const state = reactive({
  136 + students: [],
  137 + activeIndex: 0,
  138 + courseId: null,
  139 + selectData: {
  140 + classArr: [],
  141 + schoolArr: [],
  142 + teacherArr: [],
  143 + tableData: {}
  144 + },
  145 + schoolName: "",
  146 + className: "",
  147 + teacherName: "",
  148 + courseDate: "", // X
  149 + courseTime: "" // Y
  150 + })
  151 +
  152 + onLoad((option) => {
  153 + state.courseId = option?.id || null
  154 + })
  155 +
  156 + onMounted(async () => {
  157 + await getMyStudents()
  158 + getSelectCourseData()
  159 + })
  160 +
  161 + const signUpHandler = async () => {
  162 + try {
  163 + if (!state.students.length) return uni.showToast({
  164 + icon: 'error',
  165 + title: '请选择报名学员'
  166 + })
  167 + if (!state.schoolName) return uni.showToast({
  168 + icon: 'error',
  169 + title: '请选择地区'
  170 + })
  171 + if (!state.className) return uni.showToast({
  172 + icon: 'error',
  173 + title: '请选择班型'
  174 + })
  175 + if (!state.teacherName) return uni.showToast({
  176 + icon: 'error',
  177 + title: '请选择老师'
  178 + })
  179 + if (!state.courseDate || !state.courseTime) return uni.showToast({
  180 + icon: 'error',
  181 + title: '请选择时段'
  182 + })
  183 + await signUpApi({
  184 + studentWorkNo: state.students[state.activeIndex]?.workNo,
  185 + courseId: state.courseId,
  186 + schoolName: state.schoolName,
  187 + className: state.className,
  188 + teacherName: state.teacherName,
  189 + courseDate: state.courseDate,
  190 + courseTime: state.courseTime
  191 + })
  192 + uni.showToast({
  193 + icon: 'success',
  194 + title: '报名成功'
  195 + })
  196 + resetSelect()
  197 + } catch {}
  198 + }
  199 +
  200 + const resetSelect = () => {
  201 + state.schoolName = ""
  202 + state.className = ""
  203 + state.teacherName = ""
  204 + state.courseDate = ""
  205 + state.courseTime = ""
  206 + getSelectCourseData()
  207 + }
  208 +
  209 + const select1 = item => {
  210 + if (item.status === '不可选') return;
  211 + if (state.schoolName === item.text) {
  212 + state.schoolName = ""
  213 + } else {
  214 + state.schoolName = item.text;
  215 + }
  216 + getSelectCourseData()
  217 + }
  218 + const select2 = item => {
  219 + if (item.status === '不可选') return;
  220 + if (state.className === item.text) {
  221 + state.className = ""
  222 + } else {
  223 + state.className = item.text;
  224 + }
  225 + getSelectCourseData()
  226 + }
  227 + const select3 = item => {
  228 + if (item.status === '不可选') return;
  229 + if (state.teacherName === item.text) {
  230 + state.teacherName = ""
  231 + } else {
  232 + state.teacherName = item.text
  233 + }
  234 + getSelectCourseData()
  235 + }
  236 + const selectTable = (item, item1) => {
  237 + if (state.courseTime === item && state.courseDate === item1) {
  238 + state.courseTime = ""
  239 + state.courseDate = ""
  240 + return
  241 + }
  242 + const arr = state.selectData?.tableData?.selectable.filter(v => (v.x === item && v.y === item1 && v.status ===
  243 + "可选"))
  244 + if (!arr.length) return false
  245 + state.courseTime = item
  246 + state.courseDate = item1
  247 + getSelectCourseData()
  248 + }
  249 +
  250 + const filterCell = (item, item1) => {
  251 + const arr = state.selectData?.tableData?.selectable.filter(v => (v.x === item && v.y === item1 && v.status ===
  252 + "可选"))
  253 + if (arr.length) return "可选"
  254 + return ""
  255 + }
  256 +
  257 + const getSelectCourseData = async () => {
  258 + if (state.courseId === null) return
  259 +
  260 + const {
  261 + data
  262 + } = await getSelectApi({
  263 + courseId: state.courseId,
  264 + schoolName: state.schoolName,
  265 + className: state.className,
  266 + teacherName: state.teacherName,
  267 + courseDate: state.courseDate,
  268 + courseTime: state.courseTime
  269 + })
  270 + state.selectData = data
  271 + }
  272 +
  273 + const getMyStudents = async () => {
  274 + const {
  275 + data
  276 + } = await myStudentsApi()
  277 + state.students = data.students
  278 + }
  279 +
  280 + const addStudents = () => {
  281 + studentsRef.value.close()
  282 + uni.navigateTo({
  283 + url: "/pages/addStudent/addStudent"
  284 + })
  285 + }
  286 +</script>
  287 +
  288 +<style lang="scss" scoped>
  289 + .container {
  290 + background: linear-gradient(135deg, #eaecfb 10%, #f6f7fb 90%);
  291 + background-color: red;
  292 + height: calc(100vh - var(--tab-bar-height));
  293 + overflow-y: auto;
  294 + padding: 0 24rpx;
  295 + font-size: 28rpx;
  296 + }
  297 +
  298 + .title {
  299 + padding-left: 12rpx;
  300 + position: relative;
  301 +
  302 + &:before {
  303 + content: "";
  304 + width: 6rpx;
  305 + height: 70%;
  306 + border-radius: 3rpx;
  307 + background-color: #f5b426;
  308 + position: absolute;
  309 + left: 0;
  310 + top: 50%;
  311 + transform: translateY(-50%);
  312 + }
  313 + }
  314 +
  315 + .student-box {
  316 + display: flex;
  317 + align-items: center;
  318 + justify-content: space-between;
  319 + background-color: #fff;
  320 + border-radius: 20rpx;
  321 + padding: 30rpx;
  322 + }
  323 +
  324 + .pop-box {
  325 + padding: 70rpx 20rpx;
  326 + padding-bottom: calc(var(--tab-bar-height) + 20rpx);
  327 +
  328 + .content-pop {
  329 + max-height: 40vh;
  330 + min-height: 200rpx;
  331 + overflow-y: auto;
  332 +
  333 + scrollbar-width: none;
  334 + -ms-overflow-style: none;
  335 +
  336 + &::-webkit-scrollbar {
  337 + display: none;
  338 + }
  339 + }
  340 + }
  341 +
  342 + .pop-inner-item {
  343 + font-size: 28rpx;
  344 + border: 2rpx solid #ccc;
  345 + text-align: center;
  346 + padding: 10rpx 0;
  347 + border-radius: 9999rpx;
  348 + margin-bottom: 40rpx;
  349 + }
  350 +
  351 + .select-item {
  352 + text-align: center;
  353 + border: 2rpx solid #666;
  354 + padding: 8rpx 0;
  355 + border-radius: 9999rpx;
  356 + }
  357 +
  358 + .no-select {
  359 + color: #ccc;
  360 + border: 2rpx solid #ccc;
  361 + }
  362 +
  363 + .body-box {
  364 + width: 100%;
  365 + overflow-x: auto;
  366 + box-sizing: border-box;
  367 + border-right: 2rpx solid #a0b2f3;
  368 + border-bottom: 2rpx solid #a0b2f3;
  369 +
  370 + scrollbar-width: none;
  371 + -ms-overflow-style: none;
  372 +
  373 + &::-webkit-scrollbar {
  374 + display: none;
  375 + }
  376 + }
  377 +
  378 + .body-row {
  379 + display: flex;
  380 + white-space: nowrap;
  381 + position: relative;
  382 + box-sizing: border-box;
  383 + min-width: fit-content;
  384 +
  385 + .cell {
  386 + border: 2rpx solid #a0b2f3;
  387 + border-right: none;
  388 + border-bottom: none;
  389 + flex: 0 0 auto;
  390 + text-align: center;
  391 + font-size: 24rpx;
  392 + min-width: 180rpx;
  393 + height: 60rpx;
  394 + display: flex;
  395 + justify-content: center;
  396 + align-items: center;
  397 + box-sizing: border-box;
  398 +
  399 + &:nth-child(1) {
  400 + width: 140rpx;
  401 + flex-shrink: 0;
  402 + background-color: #edf1ff;
  403 + position: sticky;
  404 + left: 0;
  405 + top: 0;
  406 + box-shadow: 4rpx 0 4rpx rgba(0, 0, 0, 0.3);
  407 + }
  408 + }
  409 + }
  410 +
  411 + .select-one {
  412 + background-color: #8386ef;
  413 + color: #fff;
  414 + }
  415 +</style>
  1 +uni.addInterceptor({
  2 + returnValue (res) {
  3 + if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
  4 + return res;
  5 + }
  6 + return new Promise((resolve, reject) => {
  7 + res.then((res) => {
  8 + if (!res) return resolve(res)
  9 + return res[0] ? reject(res[0]) : resolve(res[1])
  10 + });
  11 + });
  12 + },
  13 +});
  1 +/**
  2 + * 这里是uni-app内置的常用样式变量
  3 + *
  4 + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
  5 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
  6 + *
  7 + */
  8 +
  9 +/**
  10 + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
  11 + *
  12 + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
  13 + */
  14 +
  15 +/* 颜色变量 */
  16 +
  17 +/* 行为相关颜色 */
  18 +$uni-color-primary: #007aff;
  19 +$uni-color-success: #4cd964;
  20 +$uni-color-warning: #f0ad4e;
  21 +$uni-color-error: #dd524d;
  22 +
  23 +/* 文字基本颜色 */
  24 +$uni-text-color:#333;//基本色
  25 +$uni-text-color-inverse:#fff;//反色
  26 +$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
  27 +$uni-text-color-placeholder: #808080;
  28 +$uni-text-color-disable:#c0c0c0;
  29 +
  30 +/* 背景颜色 */
  31 +$uni-bg-color:#ffffff;
  32 +$uni-bg-color-grey:#f8f8f8;
  33 +$uni-bg-color-hover:#f1f1f1;//点击状态颜色
  34 +$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
  35 +
  36 +/* 边框颜色 */
  37 +$uni-border-color:#c8c7cc;
  38 +
  39 +/* 尺寸变量 */
  40 +
  41 +/* 文字尺寸 */
  42 +$uni-font-size-sm:12px;
  43 +$uni-font-size-base:14px;
  44 +$uni-font-size-lg:16px;
  45 +
  46 +/* 图片尺寸 */
  47 +$uni-img-size-sm:20px;
  48 +$uni-img-size-base:26px;
  49 +$uni-img-size-lg:40px;
  50 +
  51 +/* Border Radius */
  52 +$uni-border-radius-sm: 2px;
  53 +$uni-border-radius-base: 3px;
  54 +$uni-border-radius-lg: 6px;
  55 +$uni-border-radius-circle: 50%;
  56 +
  57 +/* 水平间距 */
  58 +$uni-spacing-row-sm: 5px;
  59 +$uni-spacing-row-base: 10px;
  60 +$uni-spacing-row-lg: 15px;
  61 +
  62 +/* 垂直间距 */
  63 +$uni-spacing-col-sm: 4px;
  64 +$uni-spacing-col-base: 8px;
  65 +$uni-spacing-col-lg: 12px;
  66 +
  67 +/* 透明度 */
  68 +$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
  69 +
  70 +/* 文章场景相关 */
  71 +$uni-color-title: #2C405A; // 文章标题颜色
  72 +$uni-font-size-title:20px;
  73 +$uni-color-subtitle: #555555; // 二级标题颜色
  74 +$uni-font-size-subtitle:26px;
  75 +$uni-color-paragraph: #3F536E; // 文章段落颜色
  76 +$uni-font-size-paragraph:15px;
  77 +
  78 +html, body {
  79 + padding: 0;
  80 + bottom: 0;
  81 + height: 100%;
  82 + width: 100%;
  83 +}
  84 +
  85 +uni-page-body {
  86 + height: 100%;
  87 +}
  1 +## 1.0.2(2023-07-02)
  2 +uv-action-sheet 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/actionSheet.html
  3 +## 1.0.1(2023-05-16)
  4 +1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
  5 +2. 优化部分功能
  6 +## 1.0.0(2023-05-10)
  7 +uv-action-sheet 底部操作菜单
  1 +export default {
  2 + props: {
  3 + // 标题,有值则显示,同时会显示关闭按钮
  4 + title: {
  5 + type: String,
  6 + default: ''
  7 + },
  8 + // 选项上方的描述信息
  9 + description: {
  10 + type: String,
  11 + default: ''
  12 + },
  13 + // 数据
  14 + actions: {
  15 + type: Array,
  16 + default: () => []
  17 + },
  18 + // 取消按钮的文字,不为空时显示按钮
  19 + cancelText: {
  20 + type: String,
  21 + default: ''
  22 + },
  23 + // 点击某个菜单项时是否关闭弹窗
  24 + closeOnClickAction: {
  25 + type: Boolean,
  26 + default: true
  27 + },
  28 + // 处理底部安全区(默认true)
  29 + safeAreaInsetBottom: {
  30 + type: Boolean,
  31 + default: true
  32 + },
  33 + // 小程序的打开方式
  34 + openType: {
  35 + type: String,
  36 + default: ''
  37 + },
  38 + // 点击遮罩是否允许关闭 (默认true)
  39 + closeOnClickOverlay: {
  40 + type: Boolean,
  41 + default: true
  42 + },
  43 + // 圆角值
  44 + round: {
  45 + type: [Boolean, String, Number],
  46 + default: 0
  47 + },
  48 + ...uni.$uv?.props?.actionSheet
  49 + }
  50 +}
  1 +
  2 +<template>
  3 + <uv-popup
  4 + ref="popup"
  5 + mode="bottom"
  6 + :safeAreaInsetBottom="safeAreaInsetBottom"
  7 + :round="round"
  8 + :close-on-click-overlay="closeOnClickOverlay"
  9 + @change="popupChange"
  10 + >
  11 + <view class="uv-action-sheet">
  12 + <view
  13 + class="uv-action-sheet__header"
  14 + v-if="title"
  15 + >
  16 + <text class="uv-action-sheet__header__title uv-line-1">{{title}}</text>
  17 + <view
  18 + class="uv-action-sheet__header__icon-wrap"
  19 + @tap.stop="cancel"
  20 + >
  21 + <uv-icon
  22 + name="close"
  23 + size="17"
  24 + color="#c8c9cc"
  25 + bold
  26 + ></uv-icon>
  27 + </view>
  28 + </view>
  29 + <text
  30 + class="uv-action-sheet__description"
  31 + :style="[{
  32 + marginTop: `${title && description ? 0 : '18px'}`
  33 + }]"
  34 + v-if="description"
  35 + >{{description}}</text>
  36 + <slot>
  37 + <uv-line v-if="description"></uv-line>
  38 + <view class="uv-action-sheet__item-wrap">
  39 + <view v-for="(item, index) in actions" :key="index">
  40 + <!-- #ifdef MP -->
  41 + <button
  42 + class="uv-reset-button"
  43 + :openType="item.openType"
  44 + @getuserinfo="onGetUserInfo"
  45 + @contact="onContact"
  46 + @getphonenumber="onGetPhoneNumber"
  47 + @error="onError"
  48 + @launchapp="onLaunchApp"
  49 + @opensetting="onOpenSetting"
  50 + :lang="lang"
  51 + :session-from="sessionFrom"
  52 + :send-message-title="sendMessageTitle"
  53 + :send-message-path="sendMessagePath"
  54 + :send-message-img="sendMessageImg"
  55 + :show-message-card="showMessageCard"
  56 + :app-parameter="appParameter"
  57 + @tap="selectHandler(index)"
  58 + :hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''"
  59 + >
  60 + <!-- #endif -->
  61 + <view
  62 + class="uv-action-sheet__item-wrap__item"
  63 + @tap.stop="selectHandler(index)"
  64 + :hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''"
  65 + :hover-stay-time="150"
  66 + >
  67 + <template v-if="!item.loading">
  68 + <text
  69 + class="uv-action-sheet__item-wrap__item__name"
  70 + :style="[itemStyle(index)]"
  71 + >{{ item.name }}</text>
  72 + <text
  73 + v-if="item.subname"
  74 + class="uv-action-sheet__item-wrap__item__subname"
  75 + >{{ item.subname }}</text>
  76 + </template>
  77 + <uv-loading-icon
  78 + v-else
  79 + custom-class="van-action-sheet__loading"
  80 + size="18"
  81 + mode="circle"
  82 + />
  83 + </view>
  84 + <!-- #ifdef MP -->
  85 + </button>
  86 + <!-- #endif -->
  87 + <uv-line v-if="index !== actions.length - 1"></uv-line>
  88 + </view>
  89 + </view>
  90 + </slot>
  91 + <uv-gap
  92 + bgColor="#eaeaec"
  93 + height="6"
  94 + v-if="cancelText"
  95 + ></uv-gap>
  96 + <view hover-class="uv-action-sheet--hover">
  97 + <text
  98 + @touchmove.stop.prevent
  99 + :hover-stay-time="150"
  100 + v-if="cancelText"
  101 + class="uv-action-sheet__cancel-text"
  102 + @tap="cancel"
  103 + >{{cancelText}}</text>
  104 + </view>
  105 + </view>
  106 + </uv-popup>
  107 +</template>
  108 +
  109 +<script>
  110 + import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  111 + import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  112 + import button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
  113 + import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
  114 + import props from './props.js';
  115 + /**
  116 + * ActionSheet 操作菜单
  117 + * @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
  118 + * @tutorial https://www.uvui.cn/components/actionSheet.html
  119 + * @property {Boolean} show 操作菜单是否展示 (默认 false )
  120 + * @property {String} title 操作菜单标题
  121 + * @property {String} description 选项上方的描述信息
  122 + * @property {Array<Object>} actions 按钮的文字数组,见官方文档示例
  123 + * @property {String} cancelText 取消按钮的提示文字,不为空时显示按钮
  124 + * @property {Boolean} closeOnClickAction 点击某个菜单项时是否关闭弹窗 (默认 true )
  125 + * @property {Boolean} safeAreaInsetBottom 处理底部安全区 (默认 true )
  126 + * @property {String} openType 小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error )
  127 + * @property {Boolean} closeOnClickOverlay 点击遮罩是否允许关闭 (默认 true )
  128 + * @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文
  129 + * @property {String} sessionFrom 会话来源,openType="contact"时有效
  130 + * @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效
  131 + * @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效
  132 + * @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效
  133 + * @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false )
  134 + * @property {String} appParameter 打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效
  135 + *
  136 + * @event {Function} select 点击ActionSheet列表项时触发
  137 + * @event {Function} close 点击取消按钮时触发
  138 + * @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效
  139 + * @event {Function} contact 客服消息回调,openType="contact"时有效
  140 + * @event {Function} getphonenumber 获取用户手机号回调,openType="getPhoneNumber"时有效
  141 + * @event {Function} error 当使用开放能力时,发生错误的回调,openType="error"时有效
  142 + * @event {Function} launchapp 打开 APP 成功的回调,openType="launchApp"时有效
  143 + * @event {Function} opensetting 在打开授权设置页后回调,openType="openSetting"时有效
  144 + * @example <uv-action-sheet ref="actionSheet" :actions="list" :title="title" ></uv-action-sheet>
  145 + */
  146 + export default {
  147 + name: "uv-action-sheet",
  148 + mixins: [openType, button, mpMixin , mixin, props],
  149 + emits: ['close', 'select'],
  150 + computed: {
  151 + // 操作项目的样式
  152 + itemStyle() {
  153 + return (index) => {
  154 + let style = {};
  155 + if (this.actions[index].color) style.color = this.actions[index].color
  156 + if (this.actions[index].fontSize) style.fontSize = this.$uv.addUnit(this.actions[index].fontSize)
  157 + // 选项被禁用的样式
  158 + if (this.actions[index].disabled) style.color = '#c0c4cc'
  159 + return style;
  160 + }
  161 + },
  162 + },
  163 + methods: {
  164 + open() {
  165 + this.$refs.popup.open();
  166 + },
  167 + close() {
  168 + this.$refs.popup.close();
  169 + },
  170 + popupChange(e) {
  171 + if(!e.show) this.$emit('close');
  172 + },
  173 + // 点击取消按钮
  174 + cancel() {
  175 + this.close();
  176 + },
  177 + selectHandler(index) {
  178 + const item = this.actions[index]
  179 + if (item && !item.disabled && !item.loading) {
  180 + this.$emit('select', item)
  181 + if (this.closeOnClickAction) {
  182 + this.close();
  183 + }
  184 + }
  185 + },
  186 + }
  187 + }
  188 +</script>
  189 +
  190 +<style lang="scss" scoped>
  191 + $show-lines: 1;
  192 + $show-reset-button: 1;
  193 + @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
  194 + @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
  195 + @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
  196 + $uv-action-sheet-reset-button-width:100% !default;
  197 + $uv-action-sheet-title-font-size: 16px !default;
  198 + $uv-action-sheet-title-padding: 12px 30px !default;
  199 + $uv-action-sheet-title-color: $uv-main-color !default;
  200 + $uv-action-sheet-header-icon-wrap-right:15px !default;
  201 + $uv-action-sheet-header-icon-wrap-top:15px !default;
  202 + $uv-action-sheet-description-font-size:13px !default;
  203 + $uv-action-sheet-description-color:14px !default;
  204 + $uv-action-sheet-description-margin: 18px 15px !default;
  205 + $uv-action-sheet-item-wrap-item-padding:15px !default;
  206 + $uv-action-sheet-item-wrap-name-font-size:16px !default;
  207 + $uv-action-sheet-item-wrap-subname-font-size:13px !default;
  208 + $uv-action-sheet-item-wrap-subname-color: #c0c4cc !default;
  209 + $uv-action-sheet-item-wrap-subname-margin-top:10px !default;
  210 + $uv-action-sheet-cancel-text-font-size:16px !default;
  211 + $uv-action-sheet-cancel-text-color:$uv-content-color !default;
  212 + $uv-action-sheet-cancel-text-font-size:15px !default;
  213 + $uv-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
  214 +
  215 + .uv-reset-button {
  216 + width: $uv-action-sheet-reset-button-width;
  217 + }
  218 +
  219 + .uv-action-sheet {
  220 + text-align: center;
  221 + &__header {
  222 + position: relative;
  223 + padding: $uv-action-sheet-title-padding;
  224 + &__title {
  225 + font-size: $uv-action-sheet-title-font-size;
  226 + color: $uv-action-sheet-title-color;
  227 + font-weight: bold;
  228 + text-align: center;
  229 + }
  230 +
  231 + &__icon-wrap {
  232 + position: absolute;
  233 + right: $uv-action-sheet-header-icon-wrap-right;
  234 + top: $uv-action-sheet-header-icon-wrap-top;
  235 + }
  236 + }
  237 +
  238 + &__description {
  239 + font-size: $uv-action-sheet-description-font-size;
  240 + color: $uv-tips-color;
  241 + margin: $uv-action-sheet-description-margin;
  242 + text-align: center;
  243 + }
  244 +
  245 + &__item-wrap {
  246 +
  247 + &__item {
  248 + padding: $uv-action-sheet-item-wrap-item-padding;
  249 + @include flex;
  250 + align-items: center;
  251 + justify-content: center;
  252 + flex-direction: column;
  253 +
  254 + &__name {
  255 + font-size: $uv-action-sheet-item-wrap-name-font-size;
  256 + color: $uv-main-color;
  257 + text-align: center;
  258 + }
  259 +
  260 + &__subname {
  261 + font-size: $uv-action-sheet-item-wrap-subname-font-size;
  262 + color: $uv-action-sheet-item-wrap-subname-color;
  263 + margin-top: $uv-action-sheet-item-wrap-subname-margin-top;
  264 + text-align: center;
  265 + }
  266 + }
  267 + }
  268 +
  269 + &__cancel-text {
  270 + font-size: $uv-action-sheet-cancel-text-font-size;
  271 + color: $uv-action-sheet-cancel-text-color;
  272 + text-align: center;
  273 + padding: $uv-action-sheet-cancel-text-font-size;
  274 + }
  275 +
  276 + &--hover {
  277 + background-color: $uv-action-sheet-cancel-text-hover-background-color;
  278 + }
  279 + }
  280 +</style>
  1 +{
  2 + "id": "uv-action-sheet",
  3 + "displayName": "uv-action-sheet 底部操作菜单 全面兼容小程序、nvue、vue2、vue3等多端",
  4 + "version": "1.0.2",
  5 + "description": "该组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheet API,配置更加灵活,所有平台都表现一致。",
  6 + "keywords": [
  7 + "action-sheet",
  8 + "uvui",
  9 + "uv-ui",
  10 + "操作菜单",
  11 + "菜单选择"
  12 +],
  13 + "repository": "",
  14 + "engines": {
  15 + "HBuilderX": "^3.1.0"
  16 + },
  17 + "dcloudext": {
  18 + "type": "component-vue",
  19 + "sale": {
  20 + "regular": {
  21 + "price": "0.00"
  22 + },
  23 + "sourcecode": {
  24 + "price": "0.00"
  25 + }
  26 + },
  27 + "contact": {
  28 + "qq": ""
  29 + },
  30 + "declaration": {
  31 + "ads": "无",
  32 + "data": "插件不采集任何数据",
  33 + "permissions": "无"
  34 + },
  35 + "npmurl": ""
  36 + },
  37 + "uni_modules": {
  38 + "dependencies": [
  39 + "uv-ui-tools",
  40 + "uv-popup",
  41 + "uv-icon",
  42 + "uv-line",
  43 + "uv-loading-icon",
  44 + "uv-gap"
  45 + ],
  46 + "encrypt": [],
  47 + "platforms": {
  48 + "cloud": {
  49 + "tcb": "y",
  50 + "aliyun": "y"
  51 + },
  52 + "client": {
  53 + "Vue": {
  54 + "vue2": "y",
  55 + "vue3": "y"
  56 + },
  57 + "App": {
  58 + "app-vue": "y",
  59 + "app-nvue": "y"
  60 + },
  61 + "H5-mobile": {
  62 + "Safari": "y",
  63 + "Android Browser": "y",
  64 + "微信浏览器(Android)": "y",
  65 + "QQ浏览器(Android)": "y"
  66 + },
  67 + "H5-pc": {
  68 + "Chrome": "y",
  69 + "IE": "y",
  70 + "Edge": "y",
  71 + "Firefox": "y",
  72 + "Safari": "y"
  73 + },
  74 + "小程序": {
  75 + "微信": "y",
  76 + "阿里": "y",
  77 + "百度": "y",
  78 + "字节跳动": "y",
  79 + "QQ": "y",
  80 + "钉钉": "u",
  81 + "快手": "u",
  82 + "飞书": "u",
  83 + "京东": "u"
  84 + },
  85 + "快应用": {
  86 + "华为": "u",
  87 + "联盟": "u"
  88 + }
  89 + }
  90 + }
  91 + }
  92 +}
  1 +## ActionSheet 操作菜单
  2 +
  3 +> **组件名:uv-action-sheet**
  4 +
  5 +本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。
  6 +
  7 +本组件功能类似于uni的uni.showActionSheet API,配置更加灵活,所有平台都表现一致。
  8 +
  9 +### <a href="https://www.uvui.cn/components/actionSheet.html" target="_blank">查看文档</a>
  10 +
  11 +### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
  12 +
  13 +#### 如使用过程中有任何问题,或者您对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 +## 1.0.4(2023-12-06)
  2 +1. 阻止事件冒泡处理
  3 +## 1.0.3(2023-10-23)
  4 +1. 修复报错的BUG
  5 +## 1.0.2(2023-10-23)
  6 +1. 修复设置singleSize、multipleSize、space等值带单位,存在不显示的BUG
  7 +## 1.0.1(2023-09-13)
  8 +1. 添加依赖
  9 +## 1.0.0(2023-08-30)
  10 +1. 新增uv-album相册组件
  1 +<template>
  2 + <view class="uv-album">
  3 + <view
  4 + class="uv-album__row"
  5 + ref="uv-album__row"
  6 + v-for="(arr, index) in showUrls"
  7 + :forComputedUse="albumWidth"
  8 + :key="index"
  9 + >
  10 + <view
  11 + class="uv-album__row__wrapper"
  12 + v-for="(item, index1) in arr"
  13 + :key="index1"
  14 + :style="[imageStyle(index + 1, index1 + 1)]"
  15 + @tap.stop="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
  16 + >
  17 + <image
  18 + :src="getSrc(item)"
  19 + :mode="
  20 + urls.length === 1
  21 + ? imageHeight > 0
  22 + ? singleMode
  23 + : 'widthFix'
  24 + : multipleMode
  25 + "
  26 + :style="[
  27 + {
  28 + width: imageWidth,
  29 + height: imageHeight
  30 + }
  31 + ]"
  32 + ></image>
  33 + <view
  34 + v-if="
  35 + showMore &&
  36 + urls.length > rowCount * showUrls.length &&
  37 + index === showUrls.length - 1 &&
  38 + index1 === showUrls[showUrls.length - 1].length - 1
  39 + "
  40 + class="uv-album__row__wrapper__text"
  41 + >
  42 + <uv-text
  43 + :text="`+${urls.length - maxCount}`"
  44 + color="#fff"
  45 + :size="$uv.getPx(multipleSize) * 0.3"
  46 + align="center"
  47 + customStyle="justify-content: center"
  48 + ></uv-text>
  49 + </view>
  50 + </view>
  51 + </view>
  52 + </view>
  53 +</template>
  54 +<script>
  55 + import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  56 + import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  57 + // #ifdef APP-NVUE
  58 + // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
  59 + const dom = uni.requireNativePlugin('dom')
  60 + // #endif
  61 +
  62 + /**
  63 + * Album 相册
  64 + * @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
  65 + * @tutorial https://www.uvui.cn/components/album.html
  66 + * @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式
  67 + * @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
  68 + * @property {String | Number} singleSize 单图时,图片长边的长度 (默认 180 )
  69 + * @property {String | Number} multipleSize 多图时,图片边长 (默认 70 )
  70 + * @property {String | Number} space 多图时,图片水平和垂直之间的间隔 (默认 6 )
  71 + * @property {String} singleMode 单图时,图片缩放裁剪的模式 (默认 'scaleToFill' )
  72 + * @property {String} multipleMode 多图时,图片缩放裁剪的模式 (默认 'aspectFill' )
  73 + * @property {String | Number} maxCount 取消按钮的提示文字 (默认 9 )
  74 + * @property {Boolean} previewFullImage 是否可以预览图片 (默认 true )
  75 + * @property {String | Number} rowCount 每行展示图片数量,如设置,singleSize和multipleSize将会无效 (默认 3 )
  76 + * @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
  77 + *
  78 + * @event {Function} albumWidth 某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送 (回调参数 width )
  79 + * @example <uv-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></uv-album>
  80 + */
  81 + export default {
  82 + name: 'uv-album',
  83 + mixins: [mpMixin, mixin],
  84 + emits: ['albumWidth'],
  85 + props: {
  86 + // 图片地址,Array<String>|Array<Object>形式
  87 + urls: {
  88 + type: Array,
  89 + default: () => []
  90 + },
  91 + // 指定从数组的对象元素中读取哪个属性作为图片地址
  92 + keyName: {
  93 + type: String,
  94 + default: ''
  95 + },
  96 + // 单图时,图片长边的长度
  97 + singleSize: {
  98 + type: [String, Number],
  99 + default: 180
  100 + },
  101 + // 多图时,图片边长
  102 + multipleSize: {
  103 + type: [String, Number],
  104 + default: 70
  105 + },
  106 + // 多图时,图片水平和垂直之间的间隔
  107 + space: {
  108 + type: [String, Number],
  109 + default: 6
  110 + },
  111 + // 单图时,图片缩放裁剪的模式
  112 + singleMode: {
  113 + type: String,
  114 + default: 'scaleToFill'
  115 + },
  116 + // 多图时,图片缩放裁剪的模式
  117 + multipleMode: {
  118 + type: String,
  119 + default: 'aspectFill'
  120 + },
  121 + // 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
  122 + maxCount: {
  123 + type: [String, Number],
  124 + default: 9
  125 + },
  126 + // 是否可以预览图片
  127 + previewFullImage: {
  128 + type: Boolean,
  129 + default: true
  130 + },
  131 + // 每行展示图片数量,如设置,singleSize和multipleSize将会无效
  132 + rowCount: {
  133 + type: [String, Number],
  134 + default: 3
  135 + },
  136 + // 超出maxCount时是否显示查看更多的提示
  137 + showMore: {
  138 + type: Boolean,
  139 + default: true
  140 + },
  141 + ...uni.$uv?.props?.album
  142 + },
  143 + data() {
  144 + return {
  145 + // 单图的宽度
  146 + singleWidth: 0,
  147 + // 单图的高度
  148 + singleHeight: 0,
  149 + // 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比
  150 + singlePercent: 0.6
  151 + }
  152 + },
  153 + watch: {
  154 + urls: {
  155 + immediate: true,
  156 + handler(newVal) {
  157 + if (newVal.length === 1) {
  158 + this.getImageRect()
  159 + }
  160 + }
  161 + }
  162 + },
  163 + computed: {
  164 + imageStyle() {
  165 + return (index1, index2) => {
  166 + const { space, rowCount, multipleSize, urls } = this;
  167 + const rowLen = this.showUrls.length;
  168 + const allLen = this.urls.length;
  169 + const style = {
  170 + marginRight: this.$uv.addUnit(space),
  171 + marginBottom: this.$uv.addUnit(space)
  172 + }
  173 + // 如果为最后一行,则每个图片都无需下边框
  174 + if (index1 === rowLen) style.marginBottom = 0
  175 + // 每行的最右边一张和总长度的最后一张无需右边框
  176 + if (
  177 + index2 === rowCount ||
  178 + (index1 === rowLen &&
  179 + index2 === this.showUrls[index1 - 1].length)
  180 + )
  181 + style.marginRight = 0
  182 + return style
  183 + }
  184 + },
  185 + // 将数组划分为二维数组
  186 + showUrls() {
  187 + const arr = []
  188 + this.urls.map((item, index) => {
  189 + // 限制最大展示数量
  190 + if (index + 1 <= this.maxCount) {
  191 + // 计算该元素为第几个素组内
  192 + const itemIndex = Math.floor(index / this.rowCount)
  193 + // 判断对应的索引是否存在
  194 + if (!arr[itemIndex]) {
  195 + arr[itemIndex] = []
  196 + }
  197 + arr[itemIndex].push(item)
  198 + }
  199 + })
  200 + return arr
  201 + },
  202 + imageWidth() {
  203 + return this.$uv.addUnit(
  204 + this.urls.length === 1 ? this.singleWidth : this.multipleSize
  205 + )
  206 + },
  207 + imageHeight() {
  208 + return this.$uv.addUnit(
  209 + this.urls.length === 1 ? this.singleHeight : this.multipleSize
  210 + )
  211 + },
  212 + // 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
  213 + // 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
  214 + albumWidth() {
  215 + let width = 0
  216 + if (this.urls.length === 1) {
  217 + width = this.singleWidth
  218 + } else {
  219 + width =
  220 + this.showUrls[0].length * this.$uv.getPx(this.multipleSize) +
  221 + this.$uv.getPx(this.space) * (this.showUrls[0].length - 1)
  222 + }
  223 + this.$emit('albumWidth', width)
  224 + return width
  225 + }
  226 + },
  227 + methods: {
  228 + // 预览图片
  229 + onPreviewTap(url) {
  230 + const urls = this.urls.map((item) => {
  231 + return this.getSrc(item)
  232 + })
  233 + uni.previewImage({
  234 + current: url,
  235 + urls
  236 + })
  237 + },
  238 + // 获取图片的路径
  239 + getSrc(item) {
  240 + return this.$uv.test.object(item) ?
  241 + (this.keyName && item[this.keyName]) || item.src :
  242 + item
  243 + },
  244 + // 单图时,获取图片的尺寸
  245 + // 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
  246 + // 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
  247 + getImageRect() {
  248 + const src = this.getSrc(this.urls[0])
  249 + uni.getImageInfo({
  250 + src,
  251 + success: (res) => {
  252 + // 判断图片横向还是竖向展示方式
  253 + const isHorizotal = res.width >= res.height
  254 + this.singleWidth = isHorizotal ?
  255 + this.singleSize :
  256 + (res.width / res.height) * this.$uv.getPx(this.singleSize)
  257 + this.singleHeight = !isHorizotal ?
  258 + this.singleSize :
  259 + (res.height / res.width) * this.singleWidth
  260 + },
  261 + fail: () => {
  262 + this.getComponentWidth()
  263 + }
  264 + })
  265 + },
  266 + // 获取组件的宽度
  267 + async getComponentWidth() {
  268 + // 延时一定时间,以获取dom尺寸
  269 + await this.$uv.sleep(30)
  270 + // #ifndef APP-NVUE
  271 + this.$uGetRect('.uv-album__row').then((size) => {
  272 + this.singleWidth = size.width * this.singlePercent
  273 + })
  274 + // #endif
  275 +
  276 + // #ifdef APP-NVUE
  277 + // 这里ref="uv-album__row"所在的标签为通过for循环出来,导致this.$refs['uv-album__row']是一个数组
  278 + const ref = this.$refs['uv-album__row'][0]
  279 + ref &&
  280 + dom.getComponentRect(ref, (res) => {
  281 + this.singleWidth = res.size.width * this.singlePercent
  282 + })
  283 + // #endif
  284 + }
  285 + }
  286 + }
  287 +</script>
  288 +
  289 +<style lang="scss" scoped>
  290 + @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
  291 + .uv-album {
  292 + @include flex(column);
  293 + &__row {
  294 + @include flex(row);
  295 + flex-wrap: wrap;
  296 + &__wrapper {
  297 + position: relative;
  298 + &__text {
  299 + position: absolute;
  300 + top: 0;
  301 + left: 0;
  302 + right: 0;
  303 + bottom: 0;
  304 + background-color: rgba(0, 0, 0, 0.3);
  305 + @include flex(row);
  306 + justify-content: center;
  307 + align-items: center;
  308 + }
  309 + }
  310 + }
  311 + }
  312 +</style>
  1 +{
  2 + "id": "uv-album",
  3 + "displayName": "uv-album 相册 全面兼容vue3+2、app、h5、小程序等多端",
  4 + "version": "1.0.4",
  5 + "description": "本组件提供一个类似相册的功能,让开发者开发起来更加得心应手,功能齐全,灵活配置可以,开箱即用。减少重复的模板代码",
  6 + "keywords": [
  7 + "album",
  8 + "uv-ui",
  9 + "uvui",
  10 + "相册",
  11 + "图片"
  12 +],
  13 + "repository": "",
  14 + "engines": {
  15 + "HBuilderX": "^3.1.0"
  16 + },
  17 + "dcloudext": {
  18 + "type": "component-vue",
  19 + "sale": {
  20 + "regular": {
  21 + "price": "0.00"
  22 + },
  23 + "sourcecode": {
  24 + "price": "0.00"
  25 + }
  26 + },
  27 + "contact": {
  28 + "qq": ""
  29 + },
  30 + "declaration": {
  31 + "ads": "无",
  32 + "data": "插件不采集任何数据",
  33 + "permissions": "无"
  34 + },
  35 + "npmurl": ""
  36 + },
  37 + "uni_modules": {
  38 + "dependencies": [
  39 + "uv-ui-tools",
  40 + "uv-text"
  41 + ],
  42 + "encrypt": [],
  43 + "platforms": {
  44 + "cloud": {
  45 + "tcb": "y",
  46 + "aliyun": "y"
  47 + },
  48 + "client": {
  49 + "Vue": {
  50 + "vue2": "y",
  51 + "vue3": "y"
  52 + },
  53 + "App": {
  54 + "app-vue": "y",
  55 + "app-nvue": "y"
  56 + },
  57 + "H5-mobile": {
  58 + "Safari": "y",
  59 + "Android Browser": "y",
  60 + "微信浏览器(Android)": "y",
  61 + "QQ浏览器(Android)": "y"
  62 + },
  63 + "H5-pc": {
  64 + "Chrome": "y",
  65 + "IE": "y",
  66 + "Edge": "y",
  67 + "Firefox": "y",
  68 + "Safari": "y"
  69 + },
  70 + "小程序": {
  71 + "微信": "y",
  72 + "阿里": "y",
  73 + "百度": "y",
  74 + "字节跳动": "y",
  75 + "QQ": "y",
  76 + "钉钉": "u",
  77 + "快手": "u",
  78 + "飞书": "u",
  79 + "京东": "u"
  80 + },
  81 + "快应用": {
  82 + "华为": "u",
  83 + "联盟": "u"
  84 + }
  85 + }
  86 + }
  87 + }
  88 +}
  1 +# Album 相册
  2 +
  3 +> **组件名:uv-album**
  4 +
  5 +本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。
  6 +
  7 +功能齐全,灵活配置可以,开箱即用。减少重复的模板代码。
  8 +
  9 +# <a href="https://www.uvui.cn/components/album.html" target="_blank">查看文档</a>
  10 +
  11 +## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
  12 +
  13 +### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
  14 +
  15 +<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
  16 +
  17 +![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
  18 +
  19 +</a>
  20 +
  21 +#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
  1 +## 1.0.2(2023-06-01)
  2 +1. 修复点击触发两次实践的BUG
  3 +## 1.0.1(2023-05-16)
  4 +1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
  5 +2. 优化部分功能
  6 +## 1.0.0(2023-05-10)
  7 +uv-alert 警告提示组件
  1 +export default {
  2 + props: {
  3 + // 显示文字
  4 + title: {
  5 + type: String,
  6 + default: ''
  7 + },
  8 + // 主题,success/warning/info/error
  9 + type: {
  10 + type: String,
  11 + default: 'warning'
  12 + },
  13 + // 辅助性文字
  14 + description: {
  15 + type: String,
  16 + default: ''
  17 + },
  18 + // 是否可关闭
  19 + closable: {
  20 + type: Boolean,
  21 + default: false
  22 + },
  23 + // 是否显示图标
  24 + showIcon: {
  25 + type: Boolean,
  26 + default: false
  27 + },
  28 + // 浅或深色调,light-浅色,dark-深色
  29 + effect: {
  30 + type: String,
  31 + default: 'light'
  32 + },
  33 + // 文字是否居中
  34 + center: {
  35 + type: Boolean,
  36 + default: false
  37 + },
  38 + // 字体大小
  39 + fontSize: {
  40 + type: [String, Number],
  41 + default: 14
  42 + },
  43 + ...uni.$uv?.props?.alert
  44 + }
  45 +}
  1 +<template>
  2 + <uv-transition
  3 + mode="fade"
  4 + :show="show"
  5 + >
  6 + <view
  7 + class="uv-alert"
  8 + :class="[`uv-alert--${type}--${effect}`]"
  9 + @tap.stop="clickHandler"
  10 + :style="[$uv.addStyle(customStyle)]"
  11 + >
  12 + <view
  13 + class="uv-alert__icon"
  14 + v-if="showIcon"
  15 + >
  16 + <uv-icon
  17 + :name="iconName"
  18 + size="18"
  19 + :color="iconColor"
  20 + ></uv-icon>
  21 + </view>
  22 + <view
  23 + class="uv-alert__content"
  24 + :style="[{
  25 + paddingRight: closable ? '20px' : 0
  26 + }]"
  27 + >
  28 + <text
  29 + class="uv-alert__content__title"
  30 + v-if="title"
  31 + :style="[{
  32 + fontSize: $uv.addUnit(fontSize),
  33 + textAlign: center ? 'center' : 'left'
  34 + }]"
  35 + :class="[effect === 'dark' ? 'uv-alert__text--dark' : `uv-alert__text--${type}--light`]"
  36 + >{{ title }}</text>
  37 + <text
  38 + class="uv-alert__content__desc"
  39 + v-if="description"
  40 + :style="[{
  41 + fontSize: $uv.addUnit(fontSize),
  42 + textAlign: center ? 'center' : 'left'
  43 + }]"
  44 + :class="[effect === 'dark' ? 'uv-alert__text--dark' : `uv-alert__text--${type}--light`]"
  45 + >{{ description }}</text>
  46 + </view>
  47 + <view
  48 + class="uv-alert__close"
  49 + v-if="closable"
  50 + @tap.stop="closeHandler"
  51 + >
  52 + <uv-icon
  53 + name="close"
  54 + :color="iconColor"
  55 + size="15"
  56 + ></uv-icon>
  57 + </view>
  58 + </view>
  59 + </uv-transition>
  60 +</template>
  61 +
  62 +<script>
  63 + import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  64 + import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  65 + import props from './props.js';
  66 + /**
  67 + * Alert 警告提示
  68 + * @description 警告提示,展现需要关注的信息。
  69 + * @tutorial https://www.uvui.cn/components/alertTips.html
  70 + *
  71 + * @property {String} title 显示的文字
  72 + * @property {String} type 使用预设的颜色 (默认 'warning' )
  73 + * @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选
  74 + * @property {Boolean} closable 关闭按钮(默认为叉号icon图标) (默认 false )
  75 + * @property {Boolean} showIcon 是否显示左边的辅助图标 ( 默认 false )
  76 + * @property {String} effect 多图时,图片缩放裁剪的模式 (默认 'light' )
  77 + * @property {Boolean} center 文字是否居中 (默认 false )
  78 + * @property {String | Number} fontSize 字体大小 (默认 14 )
  79 + * @property {Object} customStyle 定义需要用到的外部样式
  80 + * @event {Function} click 点击组件时触发
  81 + * @example <uv-alert :title="title" type = "warning" :closable="closable" :description = "description"></uv-alert>
  82 + */
  83 + export default {
  84 + name: 'uv-alert',
  85 + mixins: [mpMixin, mixin, props],
  86 + emits: ['click'],
  87 + data() {
  88 + return {
  89 + show: true
  90 + }
  91 + },
  92 + computed: {
  93 + iconColor() {
  94 + return this.effect === 'light' ? this.type : '#fff'
  95 + },
  96 + // 不同主题对应不同的图标
  97 + iconName() {
  98 + switch (this.type) {
  99 + case 'success':
  100 + return 'checkmark-circle-fill';
  101 + break;
  102 + case 'error':
  103 + return 'close-circle-fill';
  104 + break;
  105 + case 'warning':
  106 + return 'error-circle-fill';
  107 + break;
  108 + case 'info':
  109 + return 'info-circle-fill';
  110 + break;
  111 + case 'primary':
  112 + return 'more-circle-fill';
  113 + break;
  114 + default:
  115 + return 'error-circle-fill';
  116 + }
  117 + }
  118 + },
  119 + methods: {
  120 + // 点击内容
  121 + clickHandler() {
  122 + this.$emit('click')
  123 + },
  124 + // 点击关闭按钮
  125 + closeHandler() {
  126 + this.show = false
  127 + }
  128 + }
  129 + }
  130 +</script>
  131 +
  132 +<style lang="scss" scoped>
  133 + @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
  134 + @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
  135 + .uv-alert {
  136 + position: relative;
  137 + background-color: $uv-primary;
  138 + padding: 8px 10px;
  139 + @include flex(row);
  140 + align-items: center;
  141 + border-top-left-radius: 4px;
  142 + border-top-right-radius: 4px;
  143 + border-bottom-left-radius: 4px;
  144 + border-bottom-right-radius: 4px;
  145 +
  146 + &--primary--dark {
  147 + background-color: $uv-primary;
  148 + }
  149 +
  150 + &--primary--light {
  151 + background-color: #ecf5ff;
  152 + }
  153 +
  154 + &--error--dark {
  155 + background-color: $uv-error;
  156 + }
  157 +
  158 + &--error--light {
  159 + background-color: #FEF0F0;
  160 + }
  161 +
  162 + &--success--dark {
  163 + background-color: $uv-success;
  164 + }
  165 +
  166 + &--success--light {
  167 + background-color: #f5fff0;
  168 + }
  169 +
  170 + &--warning--dark {
  171 + background-color: $uv-warning;
  172 + }
  173 +
  174 + &--warning--light {
  175 + background-color: #FDF6EC;
  176 + }
  177 +
  178 + &--info--dark {
  179 + background-color: $uv-info;
  180 + }
  181 +
  182 + &--info--light {
  183 + background-color: #f4f4f5;
  184 + }
  185 +
  186 + &__icon {
  187 + margin-right: 5px;
  188 + }
  189 +
  190 + &__content {
  191 + @include flex(column);
  192 + flex: 1;
  193 +
  194 + &__title {
  195 + color: $uv-main-color;
  196 + font-size: 14px;
  197 + font-weight: bold;
  198 + color: #fff;
  199 + margin-bottom: 2px;
  200 + }
  201 +
  202 + &__desc {
  203 + color: $uv-main-color;
  204 + font-size: 14px;
  205 + flex-wrap: wrap;
  206 + color: #fff;
  207 + }
  208 + }
  209 +
  210 + &__title--dark,
  211 + &__desc--dark {
  212 + color: #FFFFFF;
  213 + }
  214 +
  215 + &__text--primary--light,
  216 + &__text--primary--light {
  217 + color: $uv-primary;
  218 + }
  219 +
  220 + &__text--success--light,
  221 + &__text--success--light {
  222 + color: $uv-success;
  223 + }
  224 +
  225 + &__text--warning--light,
  226 + &__text--warning--light {
  227 + color: $uv-warning;
  228 + }
  229 +
  230 + &__text--error--light,
  231 + &__text--error--light {
  232 + color: $uv-error;
  233 + }
  234 +
  235 + &__text--info--light,
  236 + &__text--info--light {
  237 + color: $uv-info;
  238 + }
  239 +
  240 + &__close {
  241 + position: absolute;
  242 + top: 11px;
  243 + right: 10px;
  244 + }
  245 + }
  246 +</style>
  1 +{
  2 + "id": "uv-alert",
  3 + "displayName": "uv-alert 警告提示 全面兼容小程序、nvue、vue2、vue3等多端",
  4 + "version": "1.0.2",
  5 + "description": "uv-alert 警告提示,展现需要关注的信息。灵活配置,功能齐全,兼容全端",
  6 + "keywords": [
  7 + "alert",
  8 + "uvui",
  9 + "uv-ui",
  10 + "警告提示"
  11 + ],
  12 + "repository": "",
  13 + "engines": {
  14 + "HBuilderX": "^3.1.0"
  15 + },
  16 + "dcloudext": {
  17 + "type": "component-vue",
  18 + "sale": {
  19 + "regular": {
  20 + "price": "0.00"
  21 + },
  22 + "sourcecode": {
  23 + "price": "0.00"
  24 + }
  25 + },
  26 + "contact": {
  27 + "qq": ""
  28 + },
  29 + "declaration": {
  30 + "ads": "无",
  31 + "data": "插件不采集任何数据",
  32 + "permissions": "无"
  33 + },
  34 + "npmurl": ""
  35 + },
  36 + "uni_modules": {
  37 + "dependencies": [
  38 + "uv-ui-tools",
  39 + "uv-transition",
  40 + "uv-icon"
  41 + ],
  42 + "encrypt": [],
  43 + "platforms": {
  44 + "cloud": {
  45 + "tcb": "y",
  46 + "aliyun": "y"
  47 + },
  48 + "client": {
  49 + "Vue": {
  50 + "vue2": "y",
  51 + "vue3": "y"
  52 + },
  53 + "App": {
  54 + "app-vue": "y",
  55 + "app-nvue": "y"
  56 + },
  57 + "H5-mobile": {
  58 + "Safari": "y",
  59 + "Android Browser": "y",
  60 + "微信浏览器(Android)": "y",
  61 + "QQ浏览器(Android)": "y"
  62 + },
  63 + "H5-pc": {
  64 + "Chrome": "y",
  65 + "IE": "y",
  66 + "Edge": "y",
  67 + "Firefox": "y",
  68 + "Safari": "y"
  69 + },
  70 + "小程序": {
  71 + "微信": "y",
  72 + "阿里": "y",
  73 + "百度": "y",
  74 + "字节跳动": "y",
  75 + "QQ": "y",
  76 + "钉钉": "u",
  77 + "快手": "u",
  78 + "飞书": "u",
  79 + "京东": "u"
  80 + },
  81 + "快应用": {
  82 + "华为": "u",
  83 + "联盟": "u"
  84 + }
  85 + }
  86 + }
  87 + }
  88 +}
  1 +## Alert 警告提示
  2 +
  3 +> **组件名:uv-alert**
  4 +
  5 +警告提示,展现需要关注的信息。
  6 +
  7 +当某个页面需要向用户显示警告的信息时。
  8 +
  9 +非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭。
  10 +
  11 +### <a href="https://www.uvui.cn/components/alert.html" target="_blank">查看文档</a>
  12 +
  13 +### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
  14 +
  15 +#### 如使用过程中有任何问题,或者您对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 +## 1.0.5(2023-12-06)
  2 +1. 优化
  3 +## 1.0.4(2023-12-06)
  4 +1. 优化
  5 +## 1.0.3(2023-12-06)
  6 +1. 阻止事件冒泡处理,单个头像模式
  7 +## 1.0.2(2023-12-06)
  8 +1. 阻止事件冒泡处理
  9 +## 1.0.1(2023-05-16)
  10 +1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
  11 +2. 优化部分功能
  12 +## 1.0.0(2023-05-10)
  13 +uv-avatar 头像组件
  1 +export default {
  2 + props: {
  3 + // 头像图片组
  4 + urls: {
  5 + type: Array,
  6 + default: () => []
  7 + },
  8 + // 最多展示的头像数量
  9 + maxCount: {
  10 + type: [String, Number],
  11 + default: 5
  12 + },
  13 + // 头像形状
  14 + shape: {
  15 + type: String,
  16 + default: 'circle'
  17 + },
  18 + // 图片裁剪模式
  19 + mode: {
  20 + type: String,
  21 + default: 'scaleToFill'
  22 + },
  23 + // 超出maxCount时是否显示查看更多的提示
  24 + showMore: {
  25 + type: Boolean,
  26 + default: true
  27 + },
  28 + // 头像大小
  29 + size: {
  30 + type: [String, Number],
  31 + default: 40
  32 + },
  33 + // 指定从数组的对象元素中读取哪个属性作为图片地址
  34 + keyName: {
  35 + type: String,
  36 + default: ''
  37 + },
  38 + // 头像之间的遮挡比例
  39 + gap: {
  40 + type: [String, Number],
  41 + validator(value) {
  42 + return value >= 0 && value <= 1
  43 + },
  44 + default: 0.5
  45 + },
  46 + // 需额外显示的值
  47 + extraValue: {
  48 + type: [Number, String],
  49 + default: 0
  50 + },
  51 + ...uni.$uv?.props?.avatarGroup
  52 + }
  53 +}
  1 +<template>
  2 + <view class="uv-avatar-group">
  3 + <view
  4 + class="uv-avatar-group__item"
  5 + v-for="(item, index) in showUrl"
  6 + :key="index"
  7 + :style="{
  8 + marginLeft: index === 0 ? 0 : $uv.addUnit(-size * gap)
  9 + }"
  10 + >
  11 + <uv-avatar
  12 + :size="size"
  13 + :shape="shape"
  14 + :mode="mode"
  15 + :src="$uv.test.object(item) ? keyName && item[keyName] || item.url : item"
  16 + ></uv-avatar>
  17 + <view
  18 + class="uv-avatar-group__item__show-more"
  19 + v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
  20 + @tap="clickHandler"
  21 + >
  22 + <uv-text
  23 + color="#ffffff"
  24 + :size="size * 0.4"
  25 + :text="`+${extraValue || urls.length - showUrl.length}`"
  26 + align="center"
  27 + customStyle="justify-content: center"
  28 + ></uv-text>
  29 + </view>
  30 + </view>
  31 + </view>
  32 +</template>
  33 +
  34 +<script>
  35 + import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  36 + import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  37 + import props from './props.js';
  38 + /**
  39 + * AvatarGroup 头像组
  40 + * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
  41 + * @tutorial https://www.uvui.cn/components/avatar.html
  42 + *
  43 + * @property {Array} urls 头像图片组 (默认 [] )
  44 + * @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 )
  45 + * @property {String} shape 头像形状( 'circle' (默认) | 'square' )
  46 + * @property {String} mode 图片裁剪模式(默认 'scaleToFill' )
  47 + * @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
  48 + * @property {String | Number} size 头像大小 (默认 40 )
  49 + * @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
  50 + * @property {String | Number} gap 头像之间的遮挡比例(0.4代表遮挡40%) (默认 0.5 )
  51 + * @property {String | Number} extraValue 需额外显示的值
  52 + * @event {Function} showMore 头像组更多点击
  53 + * @example <uv-avatar-group:urls="urls" size="35" gap="0.4" ></uv-avatar-group:urls=>
  54 + */
  55 + export default {
  56 + name: 'uv-avatar-group',
  57 + mixins: [mpMixin, mixin, props],
  58 + data() {
  59 + return {
  60 +
  61 + }
  62 + },
  63 + computed: {
  64 + showUrl() {
  65 + return this.urls.slice(0, this.maxCount)
  66 + }
  67 + },
  68 + methods: {
  69 + clickHandler() {
  70 + this.$emit('showMore')
  71 + }
  72 + },
  73 + }
  74 +</script>
  75 +
  76 +<style lang="scss" scoped>
  77 + @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
  78 + @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
  79 +
  80 + .uv-avatar-group {
  81 + @include flex;
  82 +
  83 + &__item {
  84 + margin-left: -10px;
  85 + position: relative;
  86 +
  87 + &--no-indent {
  88 + // 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持
  89 + margin-left: 0;
  90 + }
  91 +
  92 + &__show-more {
  93 + position: absolute;
  94 + top: 0;
  95 + bottom: 0;
  96 + left: 0;
  97 + right: 0;
  98 + background-color: rgba(0, 0, 0, 0.3);
  99 + @include flex;
  100 + align-items: center;
  101 + justify-content: center;
  102 + border-radius: 100px;
  103 + }
  104 + }
  105 + }
  106 +</style>
  1 +import { range } from '@/uni_modules/uv-ui-tools/libs/function/test.js'
  2 +export default {
  3 + props: {
  4 + // 头像图片路径(不能为相对路径)
  5 + src: {
  6 + type: String,
  7 + default: ''
  8 + },
  9 + // 头像形状,circle-圆形,square-方形
  10 + shape: {
  11 + type: String,
  12 + default: 'circle'
  13 + },
  14 + // 头像尺寸
  15 + size: {
  16 + type: [String, Number],
  17 + default: 40
  18 + },
  19 + // 裁剪模式
  20 + mode: {
  21 + type: String,
  22 + default: 'scaleToFill'
  23 + },
  24 + // 显示的文字
  25 + text: {
  26 + type: String,
  27 + default: ''
  28 + },
  29 + // 背景色
  30 + bgColor: {
  31 + type: String,
  32 + default: '#c0c4cc'
  33 + },
  34 + // 文字颜色
  35 + color: {
  36 + type: String,
  37 + default: '#fff'
  38 + },
  39 + // 文字大小
  40 + fontSize: {
  41 + type: [String, Number],
  42 + default: 18
  43 + },
  44 + // 显示的图标
  45 + icon: {
  46 + type: String,
  47 + default: ''
  48 + },
  49 + // 显示小程序头像,只对百度,微信,QQ小程序有效
  50 + mpAvatar: {
  51 + type: Boolean,
  52 + default: false
  53 + },
  54 + // 是否使用随机背景色
  55 + randomBgColor: {
  56 + type: Boolean,
  57 + default: false
  58 + },
  59 + // 加载失败的默认头像(组件有内置默认图片)
  60 + defaultUrl: {
  61 + type: String,
  62 + default: ''
  63 + },
  64 + // 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
  65 + colorIndex: {
  66 + type: [String, Number],
  67 + // 校验参数规则,索引在0-19之间
  68 + validator(n) {
  69 + return range(n, [0, 19]) || n === ''
  70 + },
  71 + default: ''
  72 + },
  73 + // 组件标识符
  74 + name: {
  75 + type: String,
  76 + default: ''
  77 + },
  78 + ...uni.$uv?.props?.avatar
  79 + }
  80 +}
  1 +<template>
  2 + <view
  3 + class="uv-avatar"
  4 + :class="[`uv-avatar--${shape}`]"
  5 + :style="[{
  6 + backgroundColor: (text || icon) ? (randomBgColor ? colors[colorIndex !== '' ? colorIndex : $uv.random(0, 19)] : bgColor) : 'transparent',
  7 + width: $uv.addUnit(size),
  8 + height: $uv.addUnit(size),
  9 + }, $uv.addStyle(customStyle)]"
  10 + @tap="clickHandler"
  11 + >
  12 + <slot>
  13 + <!-- #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU -->
  14 + <open-data
  15 + v-if="mpAvatar && allowMp"
  16 + type="userAvatarUrl"
  17 + :style="[{
  18 + width: $uv.addUnit(size),
  19 + height: $uv.addUnit(size)
  20 + }]"
  21 + />
  22 + <!-- #endif -->
  23 + <!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU -->
  24 + <template v-if="mpAvatar && allowMp"></template>
  25 + <!-- #endif -->
  26 + <uv-icon
  27 + v-else-if="icon"
  28 + :name="icon"
  29 + :size="fontSize"
  30 + :color="color"
  31 + ></uv-icon>
  32 + <uv-text
  33 + v-else-if="text"
  34 + :text="text"
  35 + :size="fontSize"
  36 + :color="color"
  37 + align="center"
  38 + customStyle="justify-content: center"
  39 + ></uv-text>
  40 + <image
  41 + class="uv-avatar__image"
  42 + v-else
  43 + :class="[`uv-avatar__image--${shape}`]"
  44 + :src="avatarUrl || defaultUrl"
  45 + :mode="mode"
  46 + @error="errorHandler"
  47 + :style="[{
  48 + width: $uv.addUnit(size),
  49 + height: $uv.addUnit(size)
  50 + }]"
  51 + ></image>
  52 + </slot>
  53 + </view>
  54 +</template>
  55 +
  56 +<script>
  57 + import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  58 + import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  59 + import props from './props.js';
  60 + const base64Avatar =
  61 + "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";
  62 + /**
  63 + * Avatar 头像
  64 + * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
  65 + * @tutorial https://www.uvui.cn/components/avatar.html
  66 + *
  67 + * @property {String} src 头像路径,如加载失败,将会显示默认头像(不能为相对路径)
  68 + * @property {String} shape 头像形状 ( circle (默认) | square)
  69 + * @property {String | Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值 (默认 40 )
  70 + * @property {String} mode 头像图片的裁剪类型,与uni的image组件的mode参数一致,如效果达不到需求,可尝试传widthFix值 (默认 'scaleToFill' )
  71 + * @property {String} text 用文字替代图片,级别优先于src
  72 + * @property {String} bgColor 背景颜色,一般显示文字时用 (默认 '#c0c4cc' )
  73 + * @property {String} color 文字颜色 (默认 '#ffffff' )
  74 + * @property {String | Number} fontSize 文字大小 (默认 18 )
  75 + * @property {String} icon 显示的图标
  76 + * @property {Boolean} mpAvatar 显示小程序头像,只对百度,微信,QQ小程序有效 (默认 false )
  77 + * @property {Boolean} randomBgColor 是否使用随机背景色 (默认 false )
  78 + * @property {String} defaultUrl 加载失败的默认头像(组件有内置默认图片)
  79 + * @property {String | Number} colorIndex 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
  80 + * @property {String} name 组件标识符 (默认 'level' )
  81 + * @property {Object} customStyle 定义需要用到的外部样式
  82 + *
  83 + * @event {Function} click 点击组件时触发 index: 用户传递的标识符
  84 + * @example <uv-avatar :src="src" mode="square"></uv-avatar>
  85 + */
  86 + export default {
  87 + name: 'uv-avatar',
  88 + emits: ['click'],
  89 + mixins: [mpMixin, mixin, props],
  90 + data() {
  91 + return {
  92 + // 如果配置randomBgColor参数为true,在图标或者文字的模式下,会随机从中取出一个颜色值当做背景色
  93 + colors: ['#ffb34b', '#f2bba9', '#f7a196', '#f18080', '#88a867', '#bfbf39', '#89c152', '#94d554', '#f19ec2',
  94 + '#afaae4', '#e1b0df', '#c38cc1', '#72dcdc', '#9acdcb', '#77b1cc', '#448aca', '#86cefa', '#98d1ee',
  95 + '#73d1f1',
  96 + '#80a7dc'
  97 + ],
  98 + avatarUrl: this.src,
  99 + allowMp: false
  100 + }
  101 + },
  102 + watch: {
  103 + // 监听头像src的变化,赋值给内部的avatarUrl变量,因为图片加载失败时,需要修改图片的src为默认值
  104 + // 而组件内部不能直接修改props的值,所以需要一个中间变量
  105 + src: {
  106 + immediate: true,
  107 + handler(newVal) {
  108 + this.avatarUrl = newVal
  109 + // 如果没有传src,则主动触发error事件,用于显示默认的头像,否则src为''空字符等的时候,会无内容展示
  110 + if(!newVal) {
  111 + this.errorHandler()
  112 + }
  113 + }
  114 + }
  115 + },
  116 + computed: {
  117 + imageStyle() {
  118 + const style = {}
  119 + return style
  120 + }
  121 + },
  122 + created() {
  123 + this.init()
  124 + },
  125 + methods: {
  126 + init() {
  127 + // 目前只有这几个小程序平台具有open-data标签
  128 + // 其他平台可以通过uni.getUserInfo类似接口获取信息,但是需要弹窗授权(首次),不合符组件逻辑
  129 + // 故目前自动获取小程序头像只支持这几个平台
  130 + // #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU
  131 + this.allowMp = true
  132 + // #endif
  133 + },
  134 + // 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
  135 + isImg() {
  136 + return this.src.indexOf('/') !== -1
  137 + },
  138 + // 图片加载时失败时触发
  139 + errorHandler() {
  140 + this.avatarUrl = this.defaultUrl || base64Avatar
  141 + },
  142 + clickHandler() {
  143 + this.$emit('click', this.name)
  144 + }
  145 + }
  146 + }
  147 +</script>
  148 +
  149 +<style lang="scss" scoped>
  150 + @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
  151 +
  152 + .uv-avatar {
  153 + @include flex;
  154 + align-items: center;
  155 + justify-content: center;
  156 +
  157 + &--circle {
  158 + border-radius: 100px;
  159 + }
  160 +
  161 + &--square {
  162 + border-radius: 4px;
  163 + }
  164 +
  165 + &__image {
  166 + &--circle {
  167 + border-radius: 100px;
  168 + }
  169 +
  170 + &--square {
  171 + border-radius: 4px;
  172 + }
  173 + }
  174 + }
  175 +</style>
  1 +{
  2 + "id": "uv-avatar",
  3 + "displayName": "uv-avatar 头像 全面兼容小程序、nvue、vue2、vue3等多端",
  4 + "version": "1.0.5",
  5 + "description": "uv-avatar 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。",
  6 + "keywords": [
  7 + "uv-avatar",
  8 + "uvui",
  9 + "uv-ui",
  10 + "avatar",
  11 + "头像"
  12 + ],
  13 + "repository": "",
  14 + "engines": {
  15 + "HBuilderX": "^3.1.0"
  16 + },
  17 + "dcloudext": {
  18 + "type": "component-vue",
  19 + "sale": {
  20 + "regular": {
  21 + "price": "0.00"
  22 + },
  23 + "sourcecode": {
  24 + "price": "0.00"
  25 + }
  26 + },
  27 + "contact": {
  28 + "qq": ""
  29 + },
  30 + "declaration": {
  31 + "ads": "无",
  32 + "data": "插件不采集任何数据",
  33 + "permissions": "无"
  34 + },
  35 + "npmurl": ""
  36 + },
  37 + "uni_modules": {
  38 + "dependencies": [
  39 + "uv-ui-tools",
  40 + "uv-icon",
  41 + "uv-text"
  42 + ],
  43 + "encrypt": [],
  44 + "platforms": {
  45 + "cloud": {
  46 + "tcb": "y",
  47 + "aliyun": "y"
  48 + },
  49 + "client": {
  50 + "Vue": {
  51 + "vue2": "y",
  52 + "vue3": "y"
  53 + },
  54 + "App": {
  55 + "app-vue": "y",
  56 + "app-nvue": "y"
  57 + },
  58 + "H5-mobile": {
  59 + "Safari": "y",
  60 + "Android Browser": "y",
  61 + "微信浏览器(Android)": "y",
  62 + "QQ浏览器(Android)": "y"
  63 + },
  64 + "H5-pc": {
  65 + "Chrome": "y",
  66 + "IE": "y",
  67 + "Edge": "y",
  68 + "Firefox": "y",
  69 + "Safari": "y"
  70 + },
  71 + "小程序": {
  72 + "微信": "y",
  73 + "阿里": "y",
  74 + "百度": "y",
  75 + "字节跳动": "y",
  76 + "QQ": "y",
  77 + "钉钉": "u",
  78 + "快手": "u",
  79 + "飞书": "u",
  80 + "京东": "u"
  81 + },
  82 + "快应用": {
  83 + "华为": "u",
  84 + "联盟": "u"
  85 + }
  86 + }
  87 + }
  88 + }
  89 +}
  1 +## Avatar 头像
  2 +
  3 +> **组件名:uv-avatar**
  4 +
  5 +本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
  6 +
  7 +### <a href="https://www.uvui.cn/components/avatar.html" target="_blank">查看文档</a>
  8 +
  9 +### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
  10 +
  11 +#### 如使用过程中有任何问题,或者您对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 +## 1.0.2(2023-07-03)
  2 +1. 优化插槽自定义内容部分
  3 +2. 增加backToTop方法说明
  4 +## 1.0.1(2023-05-16)
  5 +1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
  6 +2. 优化部分功能
  7 +## 1.0.0(2023-05-10)
  8 +uv-back-top 返回顶部
  1 +export default {
  2 + props: {
  3 + // 返回顶部的形状,circle-圆形,square-方形
  4 + mode: {
  5 + type: String,
  6 + default: 'circle'
  7 + },
  8 + // 自定义图标
  9 + icon: {
  10 + type: String,
  11 + default: 'arrow-upward'
  12 + },
  13 + // 提示文字
  14 + text: {
  15 + type: String,
  16 + default: ''
  17 + },
  18 + // 返回顶部滚动时间
  19 + duration: {
  20 + type: [String, Number],
  21 + default: 100
  22 + },
  23 + // 滚动距离
  24 + scrollTop: {
  25 + type: [String, Number],
  26 + default: 0
  27 + },
  28 + // 距离顶部多少距离显示,单位px
  29 + top: {
  30 + type: [String, Number],
  31 + default: 400
  32 + },
  33 + // 返回顶部按钮到底部的距离,单位px
  34 + bottom: {
  35 + type: [String, Number],
  36 + default: 100
  37 + },
  38 + // 返回顶部按钮到右边的距离,单位px
  39 + right: {
  40 + type: [String, Number],
  41 + default: 20
  42 + },
  43 + // 层级
  44 + zIndex: {
  45 + type: [String, Number],
  46 + default: 9
  47 + },
  48 + // 图标的样式,对象形式
  49 + iconStyle: {
  50 + type: Object,
  51 + default: () => ({
  52 + color: '#909399',
  53 + fontSize: '19px'
  54 + })
  55 + },
  56 + ...uni.$uv?.props?.backtop
  57 + }
  58 +}
  1 +<template>
  2 + <uv-transition mode="fade" :customStyle="backTopStyle" :show="show">
  3 + <slot>
  4 + <view class="uv-back-top" :style="[contentStyle]" @click="backToTop">
  5 + <uv-icon :name="icon" :custom-style="iconStyle"></uv-icon>
  6 + <text v-if="text" class="uv-back-top__text">{{text}}</text>
  7 + </view>
  8 + </slot>
  9 + </uv-transition>
  10 +</template>
  11 +
  12 +<script>
  13 + import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  14 + import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  15 + import props from './props.js';
  16 + // #ifdef APP-NVUE
  17 + const dom = weex.requireModule('dom')
  18 + // #endif
  19 + /**
  20 + * backTop 返回顶部
  21 + * @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
  22 + * @tutorial https://www.uvui.cn/components/backTop.html
  23 + * @property {String} mode 返回顶部的形状,circle-圆形,square-方形 (默认 'circle' )
  24 + * @property {String} icon 自定义图标 (默认 'arrow-upward' ) 见官方文档示例
  25 + * @property {String} text 提示文字
  26 + * @property {String | Number} duration 返回顶部滚动时间 (默认 100)
  27 + * @property {String | Number} scrollTop 滚动距离 (默认 0 )
  28 + * @property {String | Number} top 距离顶部多少距离显示,单位px (默认 400 )
  29 + * @property {String | Number} bottom 返回顶部按钮到底部的距离,单位px (默认 100 )
  30 + * @property {String | Number} right 返回顶部按钮到右边的距离,单位px (默认 20 )
  31 + * @property {String | Number} zIndex 层级 (默认 9 )
  32 + * @property {Object<Object>} iconStyle 图标的样式,对象形式 (默认 {color: '#909399',fontSize: '19px'})
  33 + * @property {Object} customStyle 定义需要用到的外部样式
  34 + *
  35 + * @example <uv-back-top :scrollTop="scrollTop"></uv-back-top>
  36 + */
  37 + export default {
  38 + name: 'uv-back-top',
  39 + emits: ['click'],
  40 + mixins: [mpMixin, mixin, props],
  41 + computed: {
  42 + backTopStyle() {
  43 + // 动画组件样式
  44 + const style = {
  45 + bottom: this.$uv.addUnit(this.bottom),
  46 + right: this.$uv.addUnit(this.right),
  47 + width: '40px',
  48 + height: '40px',
  49 + position: 'fixed',
  50 + zIndex: 10,
  51 + }
  52 + return style
  53 + },
  54 + show() {
  55 + return this.$uv.getPx(this.scrollTop) > this.$uv.getPx(this.top)
  56 + },
  57 + contentStyle() {
  58 + const style = {}
  59 + let radius = 0
  60 + // 是否圆形
  61 + if (this.mode === 'circle') {
  62 + radius = '100px'
  63 + } else {
  64 + radius = '4px'
  65 + }
  66 + // 为了兼容安卓nvue,只能这么分开写
  67 + style.borderTopLeftRadius = radius
  68 + style.borderTopRightRadius = radius
  69 + style.borderBottomLeftRadius = radius
  70 + style.borderBottomRightRadius = radius
  71 + return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
  72 + }
  73 + },
  74 + methods: {
  75 + backToTop() {
  76 + // #ifdef APP-NVUE
  77 + if (!this.$parent.$refs['uv-back-top']) {
  78 + this.$uv.error(`nvue页面需要给页面最外层元素设置"ref='uv-back-top'`)
  79 + }
  80 + dom.scrollToElement(this.$parent.$refs['uv-back-top'], {
  81 + offset: 0
  82 + })
  83 + // #endif
  84 +
  85 + // #ifndef APP-NVUE
  86 + uni.pageScrollTo({
  87 + scrollTop: 0,
  88 + duration: this.duration
  89 + });
  90 + // #endif
  91 + this.$emit('click')
  92 + }
  93 + }
  94 + }
  95 +</script>
  96 +
  97 +<style lang="scss" scoped>
  98 + @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
  99 + $uv-back-top-flex: 1 !default;
  100 + $uv-back-top-height: 100% !default;
  101 + $uv-back-top-background-color: #E1E1E1 !default;
  102 + $uv-back-top-tips-font-size: 12px !default;
  103 + .uv-back-top {
  104 + @include flex;
  105 + flex-direction: column;
  106 + align-items: center;
  107 + flex: $uv-back-top-flex;
  108 + height: $uv-back-top-height;
  109 + justify-content: center;
  110 + background-color: $uv-back-top-background-color;
  111 + &__tips {
  112 + font-size: $uv-back-top-tips-font-size;
  113 + transform: scale(0.8);
  114 + }
  115 + }
  116 +</style>
  1 +{
  2 + "id": "uv-back-top",
  3 + "displayName": "uv-back-top 返回顶部 全面兼容小程序、nvue、vue2、vue3等多端",
  4 + "version": "1.0.2",
  5 + "description": "返回顶部 组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。",
  6 + "keywords": [
  7 + "uv-back-top",
  8 + "uvui",
  9 + "uv-ui",
  10 + "avatar",
  11 + "返回顶部"
  12 + ],
  13 + "repository": "",
  14 + "engines": {
  15 + "HBuilderX": "^3.1.0"
  16 + },
  17 + "dcloudext": {
  18 + "type": "component-vue",
  19 + "sale": {
  20 + "regular": {
  21 + "price": "0.00"
  22 + },
  23 + "sourcecode": {
  24 + "price": "0.00"
  25 + }
  26 + },
  27 + "contact": {
  28 + "qq": ""
  29 + },
  30 + "declaration": {
  31 + "ads": "无",
  32 + "data": "插件不采集任何数据",
  33 + "permissions": "无"
  34 + },
  35 + "npmurl": ""
  36 + },
  37 + "uni_modules": {
  38 + "dependencies": [
  39 + "uv-ui-tools",
  40 + "uv-icon",
  41 + "uv-transition"
  42 + ],
  43 + "encrypt": [],
  44 + "platforms": {
  45 + "cloud": {
  46 + "tcb": "y",
  47 + "aliyun": "y"
  48 + },
  49 + "client": {
  50 + "Vue": {
  51 + "vue2": "y",
  52 + "vue3": "y"
  53 + },
  54 + "App": {
  55 + "app-vue": "y",
  56 + "app-nvue": "y"
  57 + },
  58 + "H5-mobile": {
  59 + "Safari": "y",
  60 + "Android Browser": "y",
  61 + "微信浏览器(Android)": "y",
  62 + "QQ浏览器(Android)": "y"
  63 + },
  64 + "H5-pc": {
  65 + "Chrome": "y",
  66 + "IE": "y",
  67 + "Edge": "y",
  68 + "Firefox": "y",
  69 + "Safari": "y"
  70 + },
  71 + "小程序": {
  72 + "微信": "y",
  73 + "阿里": "y",
  74 + "百度": "y",
  75 + "字节跳动": "y",
  76 + "QQ": "y",
  77 + "钉钉": "u",
  78 + "快手": "u",
  79 + "飞书": "u",
  80 + "京东": "u"
  81 + },
  82 + "快应用": {
  83 + "华为": "u",
  84 + "联盟": "u"
  85 + }
  86 + }
  87 + }
  88 + }
  89 +}
  1 +## BackTop 返回顶部
  2 +
  3 +> **组件名:uv-back-top**
  4 +
  5 +该组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
  6 +
  7 +### <a href="https://www.uvui.cn/components/backTop.html" target="_blank">查看文档</a>
  8 +
  9 +### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
  10 +
  11 +#### 如使用过程中有任何问题,或者您对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 +## 1.0.2(2023-06-04)
  2 +1. 修复type等属性为null的时候不显示徽标的BUG
  3 +## 1.0.1(2023-05-16)
  4 +1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
  5 +2. 优化部分功能
  6 +## 1.0.0(2023-05-10)
  7 +uv-badge 徽标数,数字角标
  1 +export default {
  2 + props: {
  3 + // 是否显示圆点
  4 + isDot: {
  5 + type: Boolean,
  6 + default: false
  7 + },
  8 + // 显示的内容
  9 + value: {
  10 + type: [Number, String],
  11 + default: ''
  12 + },
  13 + // 是否显示
  14 + show: {
  15 + type: Boolean,
  16 + default: true
  17 + },
  18 + // 最大值,超过最大值会显示 '{max}+'
  19 + max: {
  20 + type: [Number, String],
  21 + default: 999
  22 + },
  23 + // 主题类型,error|warning|success|primary
  24 + type: {
  25 + type: [String,undefined,null],
  26 + default: 'error'
  27 + },
  28 + // 当数值为 0 时,是否展示 Badge
  29 + showZero: {
  30 + type: Boolean,
  31 + default: false
  32 + },
  33 + // 背景颜色,优先级比type高,如设置,type参数会失效
  34 + bgColor: {
  35 + type: [String, null],
  36 + default: null
  37 + },
  38 + // 字体颜色
  39 + color: {
  40 + type: [String, null],
  41 + default: null
  42 + },
  43 + // 徽标形状,circle-四角均为圆角,horn-左下角为直角
  44 + shape: {
  45 + type: [String,undefined,null],
  46 + default: 'circle'
  47 + },
  48 + // 设置数字的显示方式,overflow|ellipsis|limit
  49 + // overflow会根据max字段判断,超出显示`${max}+`
  50 + // ellipsis会根据max判断,超出显示`${max}...`
  51 + // limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数
  52 + numberType: {
  53 + type: [String,undefined,null],
  54 + default: 'overflow'
  55 + },
  56 + // 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
  57 + offset: {
  58 + type: Array,
  59 + default: () => []
  60 + },
  61 + // 是否反转背景和字体颜色
  62 + inverted: {
  63 + type: Boolean,
  64 + default: false
  65 + },
  66 + // 是否绝对定位
  67 + absolute: {
  68 + type: Boolean,
  69 + default: false
  70 + },
  71 + ...uni.$uv?.props?.badge
  72 + }
  73 +}
  1 +<template>
  2 + <text
  3 + v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)"
  4 + :class="[isDot ? 'uv-badge--dot' : 'uv-badge--not-dot', inverted && 'uv-badge--inverted', shape === 'horn' && 'uv-badge--horn', `uv-badge--${propsType}${inverted ? '--inverted' : ''}`]"
  5 + :style="[$uv.addStyle(customStyle), badgeStyle]"
  6 + class="uv-badge"
  7 + >{{ isDot ? '' :showValue }}</text>
  8 +</template>
  9 +
  10 +<script>
  11 + import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  12 + import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  13 + import props from './props.js';
  14 + /**
  15 + * badge 徽标数
  16 + * @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
  17 + * @tutorial https://www.uvui.cn/components/badge.html
  18 + *
  19 + * @property {Boolean} isDot 是否显示圆点 (默认 false )
  20 + * @property {String | Number} value 显示的内容
  21 + * @property {Boolean} show 是否显示 (默认 true )
  22 + * @property {String | Number} max 最大值,超过最大值会显示 '{max}+' (默认999)
  23 + * @property {String} type 主题类型,error|warning|success|primary (默认 'error' )
  24 + * @property {Boolean} showZero 当数值为 0 时,是否展示 Badge (默认 false )
  25 + * @property {String} bgColor 背景颜色,优先级比type高,如设置,type参数会失效
  26 + * @property {String} color 字体颜色 (默认 '#ffffff' )
  27 + * @property {String} shape 徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' )
  28 + * @property {String} numberType 设置数字的显示方式,overflow|ellipsis|limit (默认 'overflow' )
  29 + * @property {Array}} offset 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
  30 + * @property {Boolean} inverted 是否反转背景和字体颜色(默认 false )
  31 + * @property {Boolean} absolute 是否绝对定位(默认 false )
  32 + * @property {Object} customStyle 定义需要用到的外部样式
  33 + * @example <uv-badge :type="type" :count="count"></uv-badge>
  34 + */
  35 + export default {
  36 + name: 'uv-badge',
  37 + mixins: [mpMixin, mixin, props],
  38 + computed: {
  39 + // 是否将badge中心与父组件右上角重合
  40 + boxStyle() {
  41 + let style = {};
  42 + return style;
  43 + },
  44 + // 整个组件的样式
  45 + badgeStyle() {
  46 + const style = {}
  47 + if(this.color) {
  48 + style.color = this.color
  49 + }
  50 + if (this.bgColor && !this.inverted) {
  51 + style.backgroundColor = this.bgColor
  52 + }
  53 + if (this.absolute) {
  54 + style.position = 'absolute'
  55 + // 如果有设置offset参数
  56 + if(this.offset.length) {
  57 + // top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top
  58 + const top = this.offset[0]
  59 + const right = this.offset[1] || top
  60 + style.top = this.$uv.addUnit(top)
  61 + style.right = this.$uv.addUnit(right)
  62 + }
  63 + }
  64 + return style
  65 + },
  66 + showValue() {
  67 + switch (this.numberType) {
  68 + case "overflow":
  69 + return Number(this.value) > Number(this.max) ? this.max + "+" : this.value
  70 + break;
  71 + case "ellipsis":
  72 + return Number(this.value) > Number(this.max) ? "..." : this.value
  73 + break;
  74 + case "limit":
  75 + return Number(this.value) > 999 ? Number(this.value) >= 9999 ?
  76 + Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value /
  77 + 1e3 * 100) / 100 + "k" : this.value
  78 + break;
  79 + default:
  80 + return Number(this.value)
  81 + }
  82 + },
  83 + propsType(){
  84 + return this.type || 'error'
  85 + }
  86 + }
  87 + }
  88 +</script>
  89 +
  90 +<style lang="scss" scoped>
  91 + @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
  92 + @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
  93 + $uv-badge-primary: $uv-primary !default;
  94 + $uv-badge-error: $uv-error !default;
  95 + $uv-badge-success: $uv-success !default;
  96 + $uv-badge-info: $uv-info !default;
  97 + $uv-badge-warning: $uv-warning !default;
  98 + $uv-badge-dot-radius: 100px !default;
  99 + $uv-badge-dot-size: 8px !default;
  100 + $uv-badge-dot-right: 4px !default;
  101 + $uv-badge-dot-top: 0 !default;
  102 + $uv-badge-text-font-size: 11px !default;
  103 + $uv-badge-text-right: 10px !default;
  104 + $uv-badge-text-padding: 2px 5px !default;
  105 + $uv-badge-text-align: center !default;
  106 + $uv-badge-text-color: #FFFFFF !default;
  107 +
  108 + .uv-badge {
  109 + border-top-right-radius: $uv-badge-dot-radius;
  110 + border-top-left-radius: $uv-badge-dot-radius;
  111 + border-bottom-left-radius: $uv-badge-dot-radius;
  112 + border-bottom-right-radius: $uv-badge-dot-radius;
  113 + @include flex;
  114 + line-height: $uv-badge-text-font-size;
  115 + text-align: $uv-badge-text-align;
  116 + font-size: $uv-badge-text-font-size;
  117 + color: $uv-badge-text-color;
  118 +
  119 + &--dot {
  120 + height: $uv-badge-dot-size;
  121 + width: $uv-badge-dot-size;
  122 + }
  123 +
  124 + &--inverted {
  125 + font-size: 13px;
  126 + }
  127 +
  128 + &--not-dot {
  129 + padding: $uv-badge-text-padding;
  130 + }
  131 +
  132 + &--horn {
  133 + border-bottom-left-radius: 0;
  134 + }
  135 +
  136 + &--primary {
  137 + background-color: $uv-badge-primary;
  138 + }
  139 +
  140 + &--primary--inverted {
  141 + color: $uv-badge-primary;
  142 + }
  143 +
  144 + &--error {
  145 + background-color: $uv-badge-error;
  146 + }
  147 +
  148 + &--error--inverted {
  149 + color: $uv-badge-error;
  150 + }
  151 +
  152 + &--success {
  153 + background-color: $uv-badge-success;
  154 + }
  155 +
  156 + &--success--inverted {
  157 + color: $uv-badge-success;
  158 + }
  159 +
  160 + &--info {
  161 + background-color: $uv-badge-info;
  162 + }
  163 +
  164 + &--info--inverted {
  165 + color: $uv-badge-info;
  166 + }
  167 +
  168 + &--warning {
  169 + background-color: $uv-badge-warning;
  170 + }
  171 +
  172 + &--warning--inverted {
  173 + color: $uv-badge-warning;
  174 + }
  175 + }
  176 +</style>
  1 +{
  2 + "id": "uv-badge",
  3 + "displayName": "uv-badge 徽标数,数字角标 全面兼容小程序、nvue、vue2、vue3等多端",
  4 + "version": "1.0.2",
  5 + "description": "徽标数一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。",
  6 + "keywords": [
  7 + "uv-badge",
  8 + "uvui",
  9 + "uv-ui",
  10 + "徽标数",
  11 + "数字角标"
  12 + ],
  13 + "repository": "",
  14 + "engines": {
  15 + "HBuilderX": "^3.1.0"
  16 + },
  17 + "dcloudext": {
  18 + "type": "component-vue",
  19 + "sale": {
  20 + "regular": {
  21 + "price": "0.00"
  22 + },
  23 + "sourcecode": {
  24 + "price": "0.00"
  25 + }
  26 + },
  27 + "contact": {
  28 + "qq": ""
  29 + },
  30 + "declaration": {
  31 + "ads": "无",
  32 + "data": "插件不采集任何数据",
  33 + "permissions": "无"
  34 + },
  35 + "npmurl": ""
  36 + },
  37 + "uni_modules": {
  38 + "dependencies": [
  39 + "uv-ui-tools"
  40 + ],
  41 + "encrypt": [],
  42 + "platforms": {
  43 + "cloud": {
  44 + "tcb": "y",
  45 + "aliyun": "y"
  46 + },
  47 + "client": {
  48 + "Vue": {
  49 + "vue2": "y",
  50 + "vue3": "y"
  51 + },
  52 + "App": {
  53 + "app-vue": "y",
  54 + "app-nvue": "y"
  55 + },
  56 + "H5-mobile": {
  57 + "Safari": "y",
  58 + "Android Browser": "y",
  59 + "微信浏览器(Android)": "y",
  60 + "QQ浏览器(Android)": "y"
  61 + },
  62 + "H5-pc": {
  63 + "Chrome": "y",
  64 + "IE": "y",
  65 + "Edge": "y",
  66 + "Firefox": "y",
  67 + "Safari": "y"
  68 + },
  69 + "小程序": {
  70 + "微信": "y",
  71 + "阿里": "y",
  72 + "百度": "y",
  73 + "字节跳动": "y",
  74 + "QQ": "y",
  75 + "钉钉": "u",
  76 + "快手": "u",
  77 + "飞书": "u",
  78 + "京东": "u"
  79 + },
  80 + "快应用": {
  81 + "华为": "u",
  82 + "联盟": "u"
  83 + }
  84 + }
  85 + }
  86 + }
  87 +}
  1 +## Badge 徽标数
  2 +
  3 +> **组件名:uv-badge**
  4 +
  5 +该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
  6 +
  7 +### <a href="https://www.uvui.cn/components/badge.html" target="_blank">查看文档</a>
  8 +
  9 +### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
  10 +
  11 +#### 如使用过程中有任何问题,或者您对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 +## 1.0.15(2023-12-20)
  2 +1. 优化
  3 +## 1.0.14(2023-12-06)
  4 +1. 优化
  5 +## 1.0.13(2023-12-06)
  6 +1. 阻止事件冒泡处理
  7 +## 1.0.12(2023-10-19)
  8 +1. 增加后置插槽
  9 +## 1.0.11(2023-09-21)
  10 +1. 修复通过customStyle修改按钮宽度,组件中最外层节点不改变的问题
  11 +## 1.0.10(2023-09-15)
  12 +1. 按钮支持open-type="agreePrivacyAuthorization"
  13 +## 1.0.9(2023-09-11)
  14 +1. 增加参数iconSize,用于控制图标的大小
  15 +## 1.0.8(2023-09-10)
  16 +1. 修复多个按钮在一行宽度不正常的BUG
  17 +## 1.0.7(2023-09-07)
  18 +1. 修复warning颜色对应错误的BUG
  19 +## 1.0.6(2023-07-25)
  20 +1. 增加customTextStyle属性,方便自定义文字样式
  21 +## 1.0.5(2023-07-20)
  22 +1. 解决微信小程序动态设置hover-class点击态不消失的BUG
  23 +## 1.0.4(2023-06-29)
  24 +1. 修改上次更新出现nvue报错异常
  25 +## 1.0.3(2023-06-28)
  26 + 修复:设置open-type="chooseAvatar"等值不生效的BUG
  27 +## 1.0.2(2023-06-01)
  28 +1. 修复按钮点击触发两次的BUG
  29 +## 1.0.1(2023-05-16)
  30 +1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
  31 +2. 优化部分功能
  32 +## 1.0.0(2023-05-10)
  33 +uv-button 按钮
  1 +$uv-button-active-opacity:0.75 !default;
  2 +$uv-button-loading-text-margin-left:4px !default;
  3 +$uv-button-text-color: #FFFFFF !default;
  4 +$uv-button-text-plain-error-color:$uv-error !default;
  5 +$uv-button-text-plain-warning-color:$uv-warning !default;
  6 +$uv-button-text-plain-success-color:$uv-success !default;
  7 +$uv-button-text-plain-info-color:$uv-info !default;
  8 +$uv-button-text-plain-primary-color:$uv-primary !default;
  9 +.uv-button {
  10 + &--active {
  11 + opacity: $uv-button-active-opacity;
  12 + }
  13 +
  14 + &--active--plain {
  15 + background-color: rgb(217, 217, 217);
  16 + }
  17 +
  18 + &__loading-text {
  19 + margin-left:$uv-button-loading-text-margin-left;
  20 + }
  21 +
  22 + &__text,
  23 + &__loading-text {
  24 + color:$uv-button-text-color;
  25 + }
  26 +
  27 + &__text--plain--error {
  28 + color:$uv-button-text-plain-error-color;
  29 + }
  30 +
  31 + &__text--plain--warning {
  32 + color:$uv-button-text-plain-warning-color;
  33 + }
  34 +
  35 + &__text--plain--success{
  36 + color:$uv-button-text-plain-success-color;
  37 + }
  38 +
  39 + &__text--plain--info {
  40 + color:$uv-button-text-plain-info-color;
  41 + }
  42 +
  43 + &__text--plain--primary {
  44 + color:$uv-button-text-plain-primary-color;
  45 + }
  46 +}
  1 +export default {
  2 + props: {
  3 + // 是否细边框
  4 + hairline: {
  5 + type: Boolean,
  6 + default: true
  7 + },
  8 + // 按钮的预置样式,info,primary,error,warning,success
  9 + type: {
  10 + type: String,
  11 + default: 'info'
  12 + },
  13 + // 按钮尺寸,large,normal,small,mini
  14 + size: {
  15 + type: String,
  16 + default: 'normal'
  17 + },
  18 + // 按钮形状,circle(两边为半圆),square(带圆角)
  19 + shape: {
  20 + type: String,
  21 + default: 'square'
  22 + },
  23 + // 按钮是否镂空
  24 + plain: {
  25 + type: Boolean,
  26 + default: false
  27 + },
  28 + // 是否禁止状态
  29 + disabled: {
  30 + type: Boolean,
  31 + default: false
  32 + },
  33 + // 是否加载中
  34 + loading: {
  35 + type: Boolean,
  36 + default: false
  37 + },
  38 + // 加载中提示文字
  39 + loadingText: {
  40 + type: [String, Number],
  41 + default: ''
  42 + },
  43 + // 加载状态图标类型
  44 + loadingMode: {
  45 + type: String,
  46 + default: 'spinner'
  47 + },
  48 + // 加载图标大小
  49 + loadingSize: {
  50 + type: [String, Number],
  51 + default: 14
  52 + },
  53 + // 开放能力,具体请看uniapp稳定关于button组件部分说明
  54 + // https://uniapp.dcloud.io/component/button
  55 + openType: {
  56 + type: String,
  57 + default: ''
  58 + },
  59 + // 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
  60 + // 取值为submit(提交表单),reset(重置表单)
  61 + formType: {
  62 + type: String,
  63 + default: ''
  64 + },
  65 + // 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
  66 + // 只微信小程序、QQ小程序有效
  67 + appParameter: {
  68 + type: String,
  69 + default: ''
  70 + },
  71 + // 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
  72 + hoverStopPropagation: {
  73 + type: Boolean,
  74 + default: true
  75 + },
  76 + // 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
  77 + lang: {
  78 + type: String,
  79 + default: 'en'
  80 + },
  81 + // 会话来源,open-type="contact"时有效。只微信小程序有效
  82 + sessionFrom: {
  83 + type: String,
  84 + default: ''
  85 + },
  86 + // 会话内消息卡片标题,open-type="contact"时有效
  87 + // 默认当前标题,只微信小程序有效
  88 + sendMessageTitle: {
  89 + type: String,
  90 + default: ''
  91 + },
  92 + // 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
  93 + // 默认当前分享路径,只微信小程序有效
  94 + sendMessagePath: {
  95 + type: String,
  96 + default: ''
  97 + },
  98 + // 会话内消息卡片图片,open-type="contact"时有效
  99 + // 默认当前页面截图,只微信小程序有效
  100 + sendMessageImg: {
  101 + type: String,
  102 + default: ''
  103 + },
  104 + // 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
  105 + // 用户点击后可以快速发送小程序消息,open-type="contact"时有效
  106 + showMessageCard: {
  107 + type: Boolean,
  108 + default: true
  109 + },
  110 + // 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
  111 + dataName: {
  112 + type: String,
  113 + default: ''
  114 + },
  115 + // 节流,一定时间内只能触发一次
  116 + throttleTime: {
  117 + type: [String, Number],
  118 + default: 0
  119 + },
  120 + // 按住后多久出现点击态,单位毫秒
  121 + hoverStartTime: {
  122 + type: [String, Number],
  123 + default: 0
  124 + },
  125 + // 手指松开后点击态保留时间,单位毫秒
  126 + hoverStayTime: {
  127 + type: [String, Number],
  128 + default: 200
  129 + },
  130 + // 按钮文字,之所以通过props传入,是因为slot传入的话
  131 + // nvue中无法控制文字的样式
  132 + text: {
  133 + type: [String, Number],
  134 + default: ''
  135 + },
  136 + // 按钮图标
  137 + icon: {
  138 + type: String,
  139 + default: ''
  140 + },
  141 + // 按钮图标大小
  142 + iconSize: {
  143 + type: [String, Number],
  144 + default: ''
  145 + },
  146 + // 按钮图标颜色
  147 + iconColor: {
  148 + type: String,
  149 + default: '#000000'
  150 + },
  151 + // 按钮颜色,支持传入linear-gradient渐变色
  152 + color: {
  153 + type: String,
  154 + default: ''
  155 + },
  156 + // 自定义按钮文本样式
  157 + customTextStyle: {
  158 + type: [Object,String],
  159 + default: ''
  160 + },
  161 + ...uni.$uv?.props?.button
  162 + }
  163 +}
  1 +<template>
  2 + <view
  3 + class="uv-button-wrapper"
  4 + :style="[btnWrapperStyle]"
  5 + >
  6 + <!-- #ifndef APP-NVUE -->
  7 + <!-- #ifdef MP -->
  8 + <!-- 为了解决微信小程序动态设置hover-class点击态不消失的BUG -->
  9 + <view class="uv-button-wrapper--dis" v-if="disabled || loading"></view>
  10 + <button
  11 + :hover-start-time="Number(hoverStartTime)"
  12 + :hover-stay-time="Number(hoverStayTime)"
  13 + :form-type="formType"
  14 + :open-type="openType"
  15 + :app-parameter="appParameter"
  16 + :hover-stop-propagation="hoverStopPropagation"
  17 + :send-message-title="sendMessageTitle"
  18 + :send-message-path="sendMessagePath"
  19 + :lang="lang"
  20 + :data-name="dataName"
  21 + :session-from="sessionFrom"
  22 + :send-message-img="sendMessageImg"
  23 + :show-message-card="showMessageCard"
  24 + @getphonenumber="onGetPhoneNumber"
  25 + @getuserinfo="onGetUserInfo"
  26 + @error="onError"
  27 + @opensetting="onOpenSetting"
  28 + @launchapp="onLaunchApp"
  29 + @contact="onContact"
  30 + @chooseavatar="onChooseavatar"
  31 + @agreeprivacyauthorization="onAgreeprivacyauthorization"
  32 + @addgroupapp="onAddgroupapp"
  33 + @chooseaddress="onChooseaddress"
  34 + @subscribe="onSubscribe"
  35 + @login="onLogin"
  36 + @im="onIm"
  37 + hover-class="uv-button--active"
  38 + class="uv-button uv-reset-button"
  39 + :style="[baseColor, $uv.addStyle(customStyle)]"
  40 + @tap="clickHandler"
  41 + :class="bemClass"
  42 + >
  43 + <!-- #endif -->
  44 + <!-- #ifndef MP -->
  45 + <button
  46 + :hover-start-time="Number(hoverStartTime)"
  47 + :hover-stay-time="Number(hoverStayTime)"
  48 + :form-type="formType"
  49 + :open-type="openType"
  50 + :app-parameter="appParameter"
  51 + :hover-stop-propagation="hoverStopPropagation"
  52 + :send-message-title="sendMessageTitle"
  53 + :send-message-path="sendMessagePath"
  54 + :lang="lang"
  55 + :data-name="dataName"
  56 + :session-from="sessionFrom"
  57 + :send-message-img="sendMessageImg"
  58 + :show-message-card="showMessageCard"
  59 + :hover-class="!disabled && !loading ? 'uv-button--active' : ''"
  60 + class="uv-button uv-reset-button"
  61 + :style="[baseColor, $uv.addStyle(customStyle)]"
  62 + @tap="clickHandler"
  63 + :class="bemClass"
  64 + >
  65 + <!-- #endif -->
  66 + <template v-if="loading">
  67 + <uv-loading-icon
  68 + :mode="loadingMode"
  69 + :size="loadingSize * 1.15"
  70 + :color="loadingColor"
  71 + ></uv-loading-icon>
  72 + <text
  73 + class="uv-button__loading-text"
  74 + :style="[
  75 + { fontSize: textSize + 'px' },
  76 + $uv.addStyle(customTextStyle)
  77 + ]"
  78 + >{{ loadingText || text }}</text>
  79 + </template>
  80 + <template v-else>
  81 + <uv-icon
  82 + v-if="icon"
  83 + :name="icon"
  84 + :color="iconColorCom"
  85 + :size="getIconSize"
  86 + :customStyle="{ marginRight: '2px' }"
  87 + ></uv-icon>
  88 + <slot>
  89 + <text
  90 + class="uv-button__text"
  91 + :style="[
  92 + { fontSize: textSize + 'px' },
  93 + $uv.addStyle(customTextStyle)
  94 + ]"
  95 + >{{ text }}</text>
  96 + </slot>
  97 + <slot name="suffix"></slot>
  98 + </template>
  99 + </button>
  100 + <!-- #endif -->
  101 + <!-- #ifdef APP-NVUE -->
  102 + <view
  103 + :hover-start-time="Number(hoverStartTime)"
  104 + :hover-stay-time="Number(hoverStayTime)"
  105 + class="uv-button"
  106 + :hover-class="
  107 + !disabled && !loading && !color && (plain || type === 'info')
  108 + ? 'uv-button--active--plain'
  109 + : !disabled && !loading && !plain
  110 + ? 'uv-button--active'
  111 + : ''
  112 + "
  113 + @tap="clickHandler"
  114 + :class="bemClass"
  115 + :style="[baseColor, $uv.addStyle(customStyle)]"
  116 + >
  117 + <template v-if="loading">
  118 + <uv-loading-icon
  119 + :mode="loadingMode"
  120 + :size="loadingSize * 1.15"
  121 + :color="loadingColor"
  122 + ></uv-loading-icon>
  123 + <text
  124 + class="uv-button__loading-text"
  125 + :style="[nvueTextStyle,$uv.addStyle(customTextStyle)]"
  126 + :class="[plain && `uv-button__text--plain--${type}`]"
  127 + >{{ loadingText || text }}</text>
  128 + </template>
  129 + <template v-else>
  130 + <uv-icon
  131 + v-if="icon"
  132 + :name="icon"
  133 + :color="iconColorCom"
  134 + :size="getIconSize"
  135 + ></uv-icon>
  136 + <text
  137 + class="uv-button__text"
  138 + :style="[
  139 + {
  140 + marginLeft: icon ? '2px' : 0,
  141 + },
  142 + nvueTextStyle,
  143 + $uv.addStyle(customTextStyle)
  144 + ]"
  145 + :class="[plain && `uv-button__text--plain--${type}`]"
  146 + >{{ text }}</text>
  147 + <slot name="suffix"></slot>
  148 + </template>
  149 + </view>
  150 + <!-- #endif -->
  151 + </view>
  152 +</template>
  153 +
  154 +<script>
  155 +import throttle from '@/uni_modules/uv-ui-tools/libs/function/throttle.js';
  156 +import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
  157 +import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
  158 +import button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
  159 +import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
  160 +import props from "./props.js";
  161 +/**
  162 + * button 按钮
  163 + * @description Button 按钮
  164 + * @tutorial https://www.uvui.cn/components/button.html
  165 + * @property {Boolean} hairline 是否显示按钮的细边框 (默认 true )
  166 + * @property {String} type 按钮的预置样式,info,primary,error,warning,success (默认 'info' )
  167 + * @property {String} size 按钮尺寸,large,normal,mini (默认 normal)
  168 + * @property {String} shape 按钮形状,circle(两边为半圆),square(带圆角) (默认 'square' )
  169 + * @property {Boolean} plain 按钮是否镂空,背景色透明 (默认 false)
  170 + * @property {Boolean} disabled 是否禁用 (默认 false)
  171 + * @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈) (默认 false)
  172 + * @property {String | Number} loadingText 加载中提示文字
  173 + * @property {String} loadingMode 加载状态图标类型 (默认 'spinner' )
  174 + * @property {String | Number} loadingSize 加载图标大小 (默认 15 )
  175 + * @property {String} openType 开放能力,具体请看uniapp稳定关于button组件部分说明
  176 + * @property {String} formType 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
  177 + * @property {String} appParameter 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 (注:只微信小程序、QQ小程序有效)
  178 + * @property {Boolean} hoverStopPropagation 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效(默认 true )
  179 + * @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文(默认 en )
  180 + * @property {String} sessionFrom 会话来源,openType="contact"时有效
  181 + * @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效
  182 + * @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效
  183 + * @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效
  184 + * @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效(默认false)
  185 + * @property {String} dataName 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
  186 + * @property {String | Number} throttleTime 节流,一定时间内只能触发一次 (默认 0 )
  187 + * @property {String | Number} hoverStartTime 按住后多久出现点击态,单位毫秒 (默认 0 )
  188 + * @property {String | Number} hoverStayTime 手指松开后点击态保留时间,单位毫秒 (默认 200 )
  189 + * @property {String | Number} text 按钮文字,之所以通过props传入,是因为slot传入的话(注:nvue中无法控制文字的样式)
  190 + * @property {String} icon 按钮图标
  191 + * @property {String} iconColor 按钮图标颜色
  192 + * @property {String} color 按钮颜色,支持传入linear-gradient渐变色
  193 + * @property {Object} customStyle 定义需要用到的外部样式
  194 + * @event {Function} click 非禁止并且非加载中,才能点击
  195 + * @event {Function} getphonenumber open-type="getPhoneNumber"时有效
  196 + * @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
  197 + * @event {Function} error 当使用开放能力时,发生错误的回调
  198 + * @event {Function} opensetting 在打开授权设置页并关闭后回调
  199 + * @event {Function} launchapp 打开 APP 成功的回调
  200 + * @example <uv-button>月落</uv-button>
  201 + */
  202 +export default {
  203 + name: "uv-button",
  204 + // #ifdef MP
  205 + mixins: [mpMixin, mixin, button, openType, props],
  206 + // #endif
  207 + // #ifndef MP
  208 + mixins: [mpMixin, mixin, props],
  209 + // #endif
  210 + emits: ['click'],
  211 + data() {
  212 + return {};
  213 + },
  214 + computed: {
  215 + // 生成bem风格的类名
  216 + bemClass() {
  217 + // this.bem为一个computed变量,在mixin中
  218 + if (!this.color) {
  219 + return this.bem("button",
  220 + ["type", "shape", "size"],
  221 + ["disabled", "plain", "hairline"]);
  222 + } else {
  223 + // 由于nvue的原因,在有color参数时,不需要传入type,否则会生成type相关的类型,影响最终的样式
  224 + return this.bem("button",
  225 + ["shape", "size"],
  226 + ["disabled", "plain", "hairline"]);
  227 + }
  228 + },
  229 + loadingColor() {
  230 + if (this.plain) {
  231 + // 如果有设置color值,则用color值,否则使用type主题颜色
  232 + return this.color ? this.color : '#3c9cff';
  233 + }
  234 + if (this.type === "info") {
  235 + return "#c9c9c9";
  236 + }
  237 + return "rgb(200, 200, 200)";
  238 + },
  239 + iconColorCom() {
  240 + // 如果是镂空状态,设置了color就用color值,否则使用主题颜色,
  241 + // uv-icon的color能接受一个主题颜色的值
  242 + if (this.iconColor) return this.iconColor;
  243 + if (this.plain) {
  244 + return this.color ? this.color : this.type;
  245 + } else {
  246 + return this.type === "info" ? "#000000" : "#ffffff";
  247 + }
  248 + },
  249 + baseColor() {
  250 + let style = {};
  251 + if (this.color) {
  252 + // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
  253 + style.color = this.plain ? this.color : "white";
  254 + if (!this.plain) {
  255 + // 非镂空,背景色使用自定义的颜色
  256 + style["background-color"] = this.color;
  257 + }
  258 + if (this.color.indexOf("gradient") !== -1) {
  259 + // 如果自定义的颜色为渐变色,不显示边框,以及通过backgroundImage设置渐变色
  260 + // weex文档说明可以写borderWidth的形式,为什么这里需要分开写?
  261 + // 因为weex是阿里巴巴为了部门业绩考核而做的你懂的东西,所以需要这么写才有效
  262 + style.borderTopWidth = 0;
  263 + style.borderRightWidth = 0;
  264 + style.borderBottomWidth = 0;
  265 + style.borderLeftWidth = 0;
  266 + if (!this.plain) {
  267 + style.backgroundImage = this.color;
  268 + }
  269 + } else {
  270 + // 非渐变色,则设置边框相关的属性
  271 + style.borderColor = this.color;
  272 + style.borderWidth = "1px";
  273 + style.borderStyle = "solid";
  274 + }
  275 + }
  276 + return style;
  277 + },
  278 + // nvue版本按钮的字体不会继承父组件的颜色,需要对每一个text组件进行单独的设置
  279 + nvueTextStyle() {
  280 + let style = {};
  281 + // 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
  282 + if (this.type === "info") {
  283 + style.color = "#323233";
  284 + }
  285 + if (this.color) {
  286 + style.color = this.plain ? this.color : "white";
  287 + }
  288 + style.fontSize = this.textSize + "px";
  289 + return style;
  290 + },
  291 + // 字体大小
  292 + textSize() {
  293 + let fontSize = 14,
  294 + { size } = this;
  295 + if (size === "large") fontSize = 16;
  296 + if (size === "normal") fontSize = 14;
  297 + if (size === "small") fontSize = 12;
  298 + if (size === "mini") fontSize = 10;
  299 + return fontSize;
  300 + },
  301 + // 设置图标大小
  302 + getIconSize() {
  303 + const size = this.iconSize ? this.iconSize : this.textSize * 1.35;
  304 + return this.$uv.addUnit(size);
  305 + },
  306 + // 设置外层盒子的宽度,其他样式不需要
  307 + btnWrapperStyle() {
  308 + const style = {};
  309 + const customStyle = this.$uv.addStyle(this.customStyle);
  310 + if(customStyle.width) style.width = customStyle.width;
  311 + return style;
  312 + }
  313 + },
  314 + methods: {
  315 + clickHandler() {
  316 + // 非禁止并且非加载中,才能点击
  317 + if (!this.disabled && !this.loading) {
  318 + // 进行节流控制,每this.throttle毫秒内,只在开始处执行
  319 + throttle(() => {
  320 + this.$emit("click");
  321 + }, this.throttleTime);
  322 + }
  323 + }
  324 + }
  325 + }
  326 +</script>
  327 +
  328 +<style lang="scss" scoped>
  329 +$show-reset-button: 1;
  330 +@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
  331 +@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
  332 +@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
  333 +
  334 +/* #ifndef APP-NVUE */
  335 +@import "./vue.scss";
  336 +/* #endif */
  337 +
  338 +/* #ifdef APP-NVUE */
  339 +@import "./nvue.scss";
  340 +/* #endif */
  341 +
  342 +$uv-button-uv-button-height: 40px !default;
  343 +$uv-button-text-font-size: 15px !default;
  344 +$uv-button-loading-text-font-size: 15px !default;
  345 +$uv-button-loading-text-margin-left: 4px !default;
  346 +$uv-button-large-width: 100% !default;
  347 +$uv-button-large-height: 50px !default;
  348 +$uv-button-normal-padding: 0 12px !default;
  349 +$uv-button-large-padding: 0 15px !default;
  350 +$uv-button-normal-font-size: 14px !default;
  351 +$uv-button-small-min-width: 60px !default;
  352 +$uv-button-small-height: 30px !default;
  353 +$uv-button-small-padding: 0px 8px !default;
  354 +$uv-button-mini-padding: 0px 8px !default;
  355 +$uv-button-small-font-size: 12px !default;
  356 +$uv-button-mini-height: 22px !default;
  357 +$uv-button-mini-font-size: 10px !default;
  358 +$uv-button-mini-min-width: 50px !default;
  359 +$uv-button-disabled-opacity: 0.5 !default;
  360 +$uv-button-info-color: #323233 !default;
  361 +$uv-button-info-background-color: #fff !default;
  362 +$uv-button-info-border-color: #ebedf0 !default;
  363 +$uv-button-info-border-width: 1px !default;
  364 +$uv-button-info-border-style: solid !default;
  365 +$uv-button-success-color: #fff !default;
  366 +$uv-button-success-background-color: $uv-success !default;
  367 +$uv-button-success-border-color: $uv-button-success-background-color !default;
  368 +$uv-button-success-border-width: 1px !default;
  369 +$uv-button-success-border-style: solid !default;
  370 +$uv-button-primary-color: #fff !default;
  371 +$uv-button-primary-background-color: $uv-primary !default;
  372 +$uv-button-primary-border-color: $uv-button-primary-background-color !default;
  373 +$uv-button-primary-border-width: 1px !default;
  374 +$uv-button-primary-border-style: solid !default;
  375 +$uv-button-error-color: #fff !default;
  376 +$uv-button-error-background-color: $uv-error !default;
  377 +$uv-button-error-border-color: $uv-button-error-background-color !default;
  378 +$uv-button-error-border-width: 1px !default;
  379 +$uv-button-error-border-style: solid !default;
  380 +$uv-button-warning-color: #fff !default;
  381 +$uv-button-warning-background-color: $uv-warning !default;
  382 +$uv-button-warning-border-color: $uv-button-warning-background-color !default;
  383 +$uv-button-warning-border-width: 1px !default;
  384 +$uv-button-warning-border-style: solid !default;
  385 +$uv-button-block-width: 100% !default;
  386 +$uv-button-circle-border-top-right-radius: 100px !default;
  387 +$uv-button-circle-border-top-left-radius: 100px !default;
  388 +$uv-button-circle-border-bottom-left-radius: 100px !default;
  389 +$uv-button-circle-border-bottom-right-radius: 100px !default;
  390 +$uv-button-square-border-top-right-radius: 3px !default;
  391 +$uv-button-square-border-top-left-radius: 3px !default;
  392 +$uv-button-square-border-bottom-left-radius: 3px !default;
  393 +$uv-button-square-border-bottom-right-radius: 3px !default;
  394 +$uv-button-icon-min-width: 1em !default;
  395 +$uv-button-plain-background-color: #fff !default;
  396 +$uv-button-hairline-border-width: 0.5px !default;
  397 +
  398 +.uv-button {
  399 + height: $uv-button-uv-button-height;
  400 + position: relative;
  401 + align-items: center;
  402 + justify-content: center;
  403 + @include flex;
  404 + /* #ifndef APP-NVUE */
  405 + box-sizing: border-box;
  406 + /* #endif */
  407 + flex-direction: row;
  408 +
  409 + &__text {
  410 + font-size: $uv-button-text-font-size;
  411 + }
  412 +
  413 + &__loading-text {
  414 + font-size: $uv-button-loading-text-font-size;
  415 + margin-left: $uv-button-loading-text-margin-left;
  416 + }
  417 +
  418 + &--large {
  419 + /* #ifndef APP-NVUE */
  420 + width: $uv-button-large-width;
  421 + /* #endif */
  422 + height: $uv-button-large-height;
  423 + padding: $uv-button-large-padding;
  424 + }
  425 +
  426 + &--normal {
  427 + padding: $uv-button-normal-padding;
  428 + font-size: $uv-button-normal-font-size;
  429 + }
  430 +
  431 + &--small {
  432 + /* #ifndef APP-NVUE */
  433 + min-width: $uv-button-small-min-width;
  434 + /* #endif */
  435 + height: $uv-button-small-height;
  436 + padding: $uv-button-small-padding;
  437 + font-size: $uv-button-small-font-size;
  438 + }
  439 +
  440 + &--mini {
  441 + height: $uv-button-mini-height;
  442 + font-size: $uv-button-mini-font-size;
  443 + /* #ifndef APP-NVUE */
  444 + min-width: $uv-button-mini-min-width;
  445 + /* #endif */
  446 + padding: $uv-button-mini-padding;
  447 + }
  448 +
  449 + &--disabled {
  450 + opacity: $uv-button-disabled-opacity;
  451 + }
  452 +
  453 + &--info {
  454 + color: $uv-button-info-color;
  455 + background-color: $uv-button-info-background-color;
  456 + border-color: $uv-button-info-border-color;
  457 + border-width: $uv-button-info-border-width;
  458 + border-style: $uv-button-info-border-style;
  459 + }
  460 +
  461 + &--success {
  462 + color: $uv-button-success-color;
  463 + background-color: $uv-button-success-background-color;
  464 + border-color: $uv-button-success-border-color;
  465 + border-width: $uv-button-success-border-width;
  466 + border-style: $uv-button-success-border-style;
  467 + }
  468 +
  469 + &--primary {
  470 + color: $uv-button-primary-color;
  471 + background-color: $uv-button-primary-background-color;
  472 + border-color: $uv-button-primary-border-color;
  473 + border-width: $uv-button-primary-border-width;
  474 + border-style: $uv-button-primary-border-style;
  475 + }
  476 +
  477 + &--error {
  478 + color: $uv-button-error-color;
  479 + background-color: $uv-button-error-background-color;
  480 + border-color: $uv-button-error-border-color;
  481 + border-width: $uv-button-error-border-width;
  482 + border-style: $uv-button-error-border-style;
  483 + }
  484 +
  485 + &--warning {
  486 + color: $uv-button-warning-color;
  487 + background-color: $uv-button-warning-background-color;
  488 + border-color: $uv-button-warning-border-color;
  489 + border-width: $uv-button-warning-border-width;
  490 + border-style: $uv-button-warning-border-style;
  491 + }
  492 +
  493 + &--block {
  494 + @include flex;
  495 + width: $uv-button-block-width;
  496 + }
  497 +
  498 + &--circle {
  499 + border-top-right-radius: $uv-button-circle-border-top-right-radius;
  500 + border-top-left-radius: $uv-button-circle-border-top-left-radius;
  501 + border-bottom-left-radius: $uv-button-circle-border-bottom-left-radius;
  502 + border-bottom-right-radius: $uv-button-circle-border-bottom-right-radius;
  503 + }
  504 +
  505 + &--square {
  506 + border-bottom-left-radius: $uv-button-square-border-top-right-radius;
  507 + border-bottom-right-radius: $uv-button-square-border-top-left-radius;
  508 + border-top-left-radius: $uv-button-square-border-bottom-left-radius;
  509 + border-top-right-radius: $uv-button-square-border-bottom-right-radius;
  510 + }
  511 +
  512 + &__icon {
  513 + /* #ifndef APP-NVUE */
  514 + min-width: $uv-button-icon-min-width;
  515 + line-height: inherit !important;
  516 + vertical-align: top;
  517 + /* #endif */
  518 + }
  519 +
  520 + &--plain {
  521 + background-color: $uv-button-plain-background-color;
  522 + }
  523 +
  524 + &--hairline {
  525 + border-width: $uv-button-hairline-border-width !important;
  526 + }
  527 +}
  528 +</style>
  1 +@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
  2 +// nvue下hover-class无效
  3 +$uv-button-before-top:50% !default;
  4 +$uv-button-before-left:50% !default;
  5 +$uv-button-before-width:100% !default;
  6 +$uv-button-before-height:100% !default;
  7 +$uv-button-before-transform:translate(-50%, -50%) !default;
  8 +$uv-button-before-opacity:0 !default;
  9 +$uv-button-before-background-color:#000 !default;
  10 +$uv-button-before-border-color:#000 !default;
  11 +$uv-button-active-before-opacity:.15 !default;
  12 +$uv-button-icon-margin-left:4px !default;
  13 +$uv-button-plain-uv-button-info-color:$uv-info;
  14 +$uv-button-plain-uv-button-success-color:$uv-success;
  15 +$uv-button-plain-uv-button-error-color:$uv-error;
  16 +$uv-button-plain-uv-button-warning-color:$uv-warning;
  17 +
  18 +.uv-button-wrapper {
  19 + position: relative;
  20 + &--dis {
  21 + position: absolute;
  22 + left: 0;
  23 + top: 0;
  24 + right: 0;
  25 + bottom: 0;
  26 + z-index: 9;
  27 + }
  28 +}
  29 +
  30 +.uv-button {
  31 + width: 100%;
  32 +
  33 + &__text {
  34 + white-space: nowrap;
  35 + line-height: 1;
  36 + }
  37 +
  38 + &:before {
  39 + position: absolute;
  40 + top:$uv-button-before-top;
  41 + left:$uv-button-before-left;
  42 + width:$uv-button-before-width;
  43 + height:$uv-button-before-height;
  44 + border: inherit;
  45 + border-radius: inherit;
  46 + transform:$uv-button-before-transform;
  47 + opacity:$uv-button-before-opacity;
  48 + content: " ";
  49 + background-color:$uv-button-before-background-color;
  50 + border-color:$uv-button-before-border-color;
  51 + }
  52 +
  53 + &--active {
  54 + &:before {
  55 + opacity: .15
  56 + }
  57 + }
  58 +
  59 + &__icon+&__text:not(:empty),
  60 + &__loading-text {
  61 + margin-left:$uv-button-icon-margin-left;
  62 + }
  63 +
  64 + &--plain {
  65 + &.uv-button--primary {
  66 + color: $uv-primary;
  67 + }
  68 + }
  69 +
  70 + &--plain {
  71 + &.uv-button--info {
  72 + color:$uv-button-plain-uv-button-info-color;
  73 + }
  74 + }
  75 +
  76 + &--plain {
  77 + &.uv-button--success {
  78 + color:$uv-button-plain-uv-button-success-color;
  79 + }
  80 + }
  81 +
  82 + &--plain {
  83 + &.uv-button--error {
  84 + color:$uv-button-plain-uv-button-error-color;
  85 + }
  86 + }
  87 +
  88 + &--plain {
  89 + &.uv-button--warning {
  90 + color:$uv-button-plain-uv-button-warning-color;
  91 + }
  92 + }
  93 +}
  1 +{
  2 + "id": "uv-button",
  3 + "displayName": "uv-button 按钮 全面兼容vue3+2、app、h5、小程序等多端",
  4 + "version": "1.0.15",
  5 + "description": "按钮组件内部实现以uni-app的button组件为基础,进行二次封装,灵活配置,功能齐全,兼容全端。",
  6 + "keywords": [
  7 + "uv-button",
  8 + "uvui",
  9 + "uv-ui",
  10 + "button",
  11 + "按钮"
  12 + ],
  13 + "repository": "",
  14 + "engines": {
  15 + "HBuilderX": "^3.1.0"
  16 + },
  17 + "dcloudext": {
  18 + "type": "component-vue",
  19 + "sale": {
  20 + "regular": {
  21 + "price": "0.00"
  22 + },
  23 + "sourcecode": {
  24 + "price": "0.00"
  25 + }
  26 + },
  27 + "contact": {
  28 + "qq": ""
  29 + },
  30 + "declaration": {
  31 + "ads": "无",
  32 + "data": "插件不采集任何数据",
  33 + "permissions": "无"
  34 + },
  35 + "npmurl": ""
  36 + },
  37 + "uni_modules": {
  38 + "dependencies": [
  39 + "uv-ui-tools",
  40 + "uv-loading-icon",
  41 + "uv-icon"
  42 + ],
  43 + "encrypt": [],
  44 + "platforms": {
  45 + "cloud": {
  46 + "tcb": "y",
  47 + "aliyun": "y"
  48 + },
  49 + "client": {
  50 + "Vue": {
  51 + "vue2": "y",
  52 + "vue3": "y"
  53 + },
  54 + "App": {
  55 + "app-vue": "y",
  56 + "app-nvue": "y"
  57 + },
  58 + "H5-mobile": {
  59 + "Safari": "y",
  60 + "Android Browser": "y",
  61 + "微信浏览器(Android)": "y",
  62 + "QQ浏览器(Android)": "y"
  63 + },
  64 + "H5-pc": {
  65 + "Chrome": "y",
  66 + "IE": "y",
  67 + "Edge": "y",
  68 + "Firefox": "y",
  69 + "Safari": "y"
  70 + },
  71 + "小程序": {
  72 + "微信": "y",
  73 + "阿里": "y",
  74 + "百度": "y",
  75 + "字节跳动": "y",
  76 + "QQ": "y",
  77 + "钉钉": "u",
  78 + "快手": "u",
  79 + "飞书": "u",
  80 + "京东": "u"
  81 + },
  82 + "快应用": {
  83 + "华为": "u",
  84 + "联盟": "u"
  85 + }
  86 + }
  87 + }
  88 + }
  89 +}