1
This commit is contained in:
@@ -0,0 +1,322 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch, onMounted, nextTick } from 'vue'
|
||||
import { getUid } from '../../core/util/xCoreUtil.uts';
|
||||
import { getDefaultColor } from "../../core/util/xCoreColorUtil.uts"
|
||||
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
|
||||
import { xConfig } from "../../config/xConfig.uts"
|
||||
|
||||
/**
|
||||
* @name 开关 xSwitch
|
||||
* @description 开关,用于直观的展示选项表单的选择。
|
||||
* @page /pages/index/switch
|
||||
* @category 表单组件
|
||||
* @constant 平台兼容
|
||||
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
|
||||
*/
|
||||
defineOptions({ name: 'xSwitch' })
|
||||
|
||||
export type xSwitchPropsType = {
|
||||
/**
|
||||
* 激活时的背景色,空值时取全局的值。
|
||||
*/
|
||||
color : string,
|
||||
/**
|
||||
* 未激活时的背景
|
||||
*/
|
||||
bgColor : string,
|
||||
/**
|
||||
* 未激活时的暗黑背景
|
||||
* 空取inputDarkColor
|
||||
*/
|
||||
darkBgColor : string,
|
||||
/**
|
||||
* 按钮的背景色
|
||||
*/
|
||||
btnColor : string,
|
||||
/**
|
||||
* 尺寸
|
||||
*/
|
||||
size : "small" | "noraml" | "large" | "normal",
|
||||
/**
|
||||
* 间隙,px单位
|
||||
*/
|
||||
space : number,
|
||||
/**
|
||||
* 当前打开的状态,默认为false
|
||||
* 等同v-model=""
|
||||
*/
|
||||
modelValue : boolean,
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disabled : boolean,
|
||||
/**
|
||||
* 是否加载中
|
||||
*/
|
||||
loading : boolean,
|
||||
/**
|
||||
* 开关文字数组第一个为开,后一个为关
|
||||
*/
|
||||
label : string[],
|
||||
/**
|
||||
* 圆角。空值时取全局值。
|
||||
*/
|
||||
round : string,
|
||||
/**
|
||||
* 激活时的按钮图标,不提供不显示
|
||||
*/
|
||||
activeIcon : string,
|
||||
/**
|
||||
* 未激活时的图标,不提供不显示
|
||||
*/
|
||||
icon : string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<xSwitchPropsType>(), {
|
||||
color: "",
|
||||
bgColor: "info",
|
||||
darkBgColor: "",
|
||||
btnColor: "white",
|
||||
size: "normal",
|
||||
space: 2,
|
||||
modelValue: false,
|
||||
disabled: false,
|
||||
loading: false,
|
||||
label: [] as string[],
|
||||
round: "",
|
||||
activeIcon: "",
|
||||
icon: ""
|
||||
})
|
||||
|
||||
|
||||
const emits = defineEmits([
|
||||
/**
|
||||
* 状态变换时触发。
|
||||
* @param {boolean} status - 当前的开关状态
|
||||
*/
|
||||
'change',
|
||||
/**
|
||||
* 组件被点击时触发。
|
||||
* @param {boolean} status - 当前的开关状态,这里的状态是在变更前。
|
||||
*/
|
||||
'click',
|
||||
/**
|
||||
* 等同v-model=""
|
||||
*/
|
||||
'update:modelValue'
|
||||
])
|
||||
|
||||
// 响应式状态
|
||||
const id = ref<string>("xSwitch" + getUid())
|
||||
const boxwidth = ref<number>(0)
|
||||
const boxheight = ref<number>(0)
|
||||
const opened = ref<boolean>(false)
|
||||
|
||||
// 计算属性,对齐原有命名
|
||||
const _activeIcon = computed<string>(() => props.activeIcon)
|
||||
const _icon = computed<string>(() => props.icon)
|
||||
const _round = computed<string>(() => {
|
||||
if (props.round == "") {
|
||||
return checkIsCssUnit(xConfig.switchRadius, xConfig.unit)
|
||||
}
|
||||
return checkIsCssUnit(props.round, xConfig.unit)
|
||||
})
|
||||
const _bgColor = computed<string>(() => {
|
||||
if (xConfig.dark == 'dark') {
|
||||
if (props.darkBgColor != '') return props.darkBgColor
|
||||
return xConfig.inputDarkColor
|
||||
}
|
||||
return getDefaultColor(props.bgColor)
|
||||
})
|
||||
const _activeBgColor = computed<string>(() => {
|
||||
if (props.color == "") {
|
||||
return getDefaultColor(xConfig.color)
|
||||
}
|
||||
return getDefaultColor(props.color)
|
||||
})
|
||||
const _btnColor = computed<string>(() => getDefaultColor(props.btnColor))
|
||||
const _size = computed<string>(() => {
|
||||
if (props.size == 'small') return '22px'
|
||||
if (props.size == 'large') return '38px'
|
||||
return '32px'
|
||||
})
|
||||
const _fontSize = computed<string>(() => {
|
||||
if (props.size == 'small') return '10px'
|
||||
if (props.size == 'large') return '12px'
|
||||
return '11px'
|
||||
})
|
||||
const _sizeWidth = computed<string>(() => {
|
||||
if (props.size == 'small') return '44px'
|
||||
if (props.size == 'large') return '76px'
|
||||
return '64px'
|
||||
})
|
||||
const _space = computed<number>(() => props.space)
|
||||
const _contentWidth = computed<number>(() => boxwidth.value - _space.value * 2)
|
||||
const _contentHeight = computed<number>(() => boxheight.value - _space.value * 2)
|
||||
const _maxLeftPos = computed<number>(() => _contentWidth.value - _contentHeight.value)
|
||||
const _label = computed<string[]>(() => props.label)
|
||||
const _loading = computed<boolean>(() => props.loading)
|
||||
const _disabled = computed<boolean>(() => props.disabled)
|
||||
const _animationFun = computed<string>(() => xConfig.animationFun)
|
||||
|
||||
|
||||
watch(():boolean => props.modelValue, (newvalue : boolean) => {
|
||||
if (newvalue != opened.value) {
|
||||
opened.value = newvalue
|
||||
}
|
||||
})
|
||||
|
||||
function onClick() {
|
||||
emits('click', opened.value)
|
||||
if (_loading.value || _disabled.value) return;
|
||||
opened.value = !opened.value
|
||||
emits('update:modelValue', opened.value)
|
||||
nextTick(() => {
|
||||
emits('change', opened.value)
|
||||
})
|
||||
}
|
||||
|
||||
function getNodes() {
|
||||
const width = parseInt(_sizeWidth.value)
|
||||
const height = parseInt(_size.value)
|
||||
boxwidth.value = width
|
||||
boxheight.value = height
|
||||
// 以下代码在sdk中有bug需要等sdk渲染完成才能具的知道结果。如何知道结果不清楚。
|
||||
// uni.createSelectorQuery().in(this)
|
||||
// .select(".xSwitch")
|
||||
// .boundingClientRect().exec((ret) => {
|
||||
// let nodeinfo = ret[0] as NodeInfo;
|
||||
// boxwidth.value = nodeinfo.width! as number;
|
||||
// boxheight.value = nodeinfo.height! as number;
|
||||
// })
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
opened.value = props.modelValue
|
||||
getNodes()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<view @click="onClick" :id="id" class="xSwitch" ref="xSwitch" :style="{
|
||||
height:_size,
|
||||
width:_sizeWidth,
|
||||
backgroundColor:_bgColor,
|
||||
borderRadius:_round
|
||||
}">
|
||||
<view :class="[_disabled?'xSwitchDisabled':'',opened?'xSwitchBgOn':'xSwitchOff']" class="xSwitchBg"
|
||||
:style="{backgroundColor:_activeBgColor,borderRadius:_round,'transition-timing-function':_animationFun}">
|
||||
</view>
|
||||
<view :class="[_disabled?'xSwitchDisabled':'']" class="xSwitchWrap" :style="{
|
||||
left:_space+'px',
|
||||
top:_space+'px',
|
||||
width:_contentWidth+'px',
|
||||
height:_contentHeight+'px',
|
||||
borderRadius:_round
|
||||
}">
|
||||
<view v-if="_label.length==2" class="xSwitchText">
|
||||
<text class="xSwitchTextLeft" :style="{fontSize:_fontSize}">{{!opened?'':_label[0]}}</text>
|
||||
<text class="xSwitchTextRight" :style="{fontSize:_fontSize}">{{opened?'':_label[1]}}</text>
|
||||
</view>
|
||||
<view class="xSwitchBtn" :style="{
|
||||
width:_contentHeight+'px',
|
||||
height:_contentHeight+'px',
|
||||
transform:`translateX(${opened?_maxLeftPos:0}px)`,
|
||||
backgroundColor:_btnColor,
|
||||
borderRadius:_round,
|
||||
'transition-timing-function':_animationFun
|
||||
}">
|
||||
<x-icon v-if="_loading" :spin="true" font-size="13" name="loader-line" :color="_activeBgColor"></x-icon>
|
||||
|
||||
<view v-if="!_loading" :class="[opened?'xSwitchIconOn':'xSwitchIconOff','xSwitchIcon']">
|
||||
<x-icon v-if="(opened?_activeIcon:_icon)!=''" :font-size="((_contentHeight*0.6).toString())"
|
||||
:color="(opened?_activeBgColor:_bgColor)" :name="(opened?_activeIcon:_icon)"></x-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<style scoped>
|
||||
.xSwitchIcon {
|
||||
transition-property: transform, opacity;
|
||||
transition-timing-function: ease-in;
|
||||
transition-duration: 250ms;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.xSwitchDisabled {
|
||||
opacity: 0.7;
|
||||
/* #ifdef WEB */
|
||||
cursor: no-drop !important;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.xSwitchTextLeft {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.xSwitchTextRight {
|
||||
color: #acacac;
|
||||
}
|
||||
|
||||
.xSwitch {
|
||||
height: 32rpx;
|
||||
position: relative;
|
||||
/* #ifdef WEB */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.xSwitchBg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition-duration: 350ms;
|
||||
transition-property: opacity, transform;
|
||||
/* transition-timing-function: ease; */
|
||||
}
|
||||
|
||||
.xSwitchBgOn {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.xSwitchOff {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.xSwitchWrap {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.xSwitchText {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 12%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.xSwitchBtn {
|
||||
position: absolute;
|
||||
/* border-radius: 200rpx; */
|
||||
transition-duration: 350ms;
|
||||
left: 0px;
|
||||
top: 0;
|
||||
transition-property: transform;
|
||||
/* transition-timing-function: cubic-bezier(.18,.89,.32,1); */
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user