1
This commit is contained in:
@@ -0,0 +1,379 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick, getCurrentInstance } from "vue"
|
||||
import { RADIO_BUTTON } from "../../interface.uts"
|
||||
import { getUid } from "../../core/util/xCoreUtil.uts"
|
||||
import { getDefaultColor } from "../../core/util/xCoreColorUtil.uts"
|
||||
import { checkIsCssUnit, getUnit } from "../../core/util/xCoreUtil.uts"
|
||||
import { xConfig } from "../../config/xConfig.uts"
|
||||
|
||||
/**
|
||||
* @name 单选按钮组 xRadioButton
|
||||
* @description 类似单选导航按钮式或者叫分割选择按钮。
|
||||
* @page /pages/index/radio-button
|
||||
* @category 导航组件
|
||||
* @constant 平台兼容
|
||||
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
|
||||
*/
|
||||
defineOptions({name:"xRadioButton"})
|
||||
|
||||
const proxy = getCurrentInstance()?.proxy??null;
|
||||
const emits = defineEmits([
|
||||
/**
|
||||
* 用户点击变换时触发。
|
||||
* @param {string} value - 当前选中的id
|
||||
* @param {number} index - 当前选中的索引值
|
||||
*/
|
||||
'change',
|
||||
/**
|
||||
* 用户点击按钮时触发,不管禁没禁用。
|
||||
* @param {string} value - 当前id
|
||||
* @param {number} index - 当前索引值
|
||||
*/
|
||||
'click',
|
||||
'update:modelValue'
|
||||
])
|
||||
|
||||
type xRadioButtonPropsType = {
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
list: RADIO_BUTTON[],
|
||||
/**
|
||||
* 当前选中的id值
|
||||
*/
|
||||
modelValue: string,
|
||||
/**
|
||||
* 背景
|
||||
*/
|
||||
bgColor: string,
|
||||
/**
|
||||
* 暗黑时的背景,如果不提供读取页面暗黑背景
|
||||
*/
|
||||
darkBgColor: string,
|
||||
/**
|
||||
* 激活的按钮块背景
|
||||
*/
|
||||
activeColor: string,
|
||||
/**
|
||||
* 激活的按钮块暗黑背景,如果不提供inputDarkColor
|
||||
*/
|
||||
darkActiveColor: string,
|
||||
/**
|
||||
* 激活时的文字颜色,默认取全局主题色。
|
||||
*/
|
||||
activeFontColor: string,
|
||||
/**
|
||||
* 文字颜色,暗黑时取白色
|
||||
*/
|
||||
fontColor: string,
|
||||
/**
|
||||
* 文字大小
|
||||
*/
|
||||
fontSize: string,
|
||||
/**
|
||||
* 按钮组高度
|
||||
*/
|
||||
height: string,
|
||||
/**
|
||||
* 四周内间隙
|
||||
*/
|
||||
space: string,
|
||||
/**
|
||||
* 圆角,空值时读取全局配置。
|
||||
*/
|
||||
round: string,
|
||||
/**
|
||||
* 文本样式,可修改文字的样式。
|
||||
*/
|
||||
textStyle: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<xRadioButtonPropsType>(), {
|
||||
list: [] as RADIO_BUTTON[],
|
||||
modelValue: "",
|
||||
bgColor: "#f3f5f8",
|
||||
darkBgColor: "",
|
||||
activeColor: "white",
|
||||
darkActiveColor: "",
|
||||
activeFontColor: "",
|
||||
fontColor: "#333333",
|
||||
fontSize: "16",
|
||||
height: "40",
|
||||
space: "2",
|
||||
round: "",
|
||||
textStyle: ""
|
||||
})
|
||||
|
||||
// 响应式数据
|
||||
const xRadioButton = ref<UniElement|null>(null)
|
||||
const nowId = ref<string>("")
|
||||
const id = ref<string>("xRadiobutton-button-id-" + getUid())
|
||||
const pos_x = ref<number>(0)
|
||||
const pos_w = ref<number>(0)
|
||||
const first = ref<boolean>(true)
|
||||
const resizeObserver = ref<UniResizeObserver | null>(null)
|
||||
|
||||
// 计算属性
|
||||
const _list = computed((): RADIO_BUTTON[] => props.list)
|
||||
const _textStyle = computed((): string => props.textStyle)
|
||||
|
||||
const _bgColor = computed((): string => {
|
||||
if (xConfig.dark == 'dark') {
|
||||
if (props.darkBgColor != "") return getDefaultColor(props.darkBgColor)
|
||||
return getDefaultColor(xConfig.backgroundColorContentDark)
|
||||
}
|
||||
return getDefaultColor(props.bgColor)
|
||||
})
|
||||
|
||||
const _activeColor = computed((): string => {
|
||||
if (xConfig.dark == 'dark') {
|
||||
if (props.darkActiveColor != "") return getDefaultColor(props.darkActiveColor)
|
||||
return getDefaultColor(xConfig.inputDarkColor)
|
||||
}
|
||||
return getDefaultColor(props.activeColor)
|
||||
})
|
||||
|
||||
const _activeFontColor = computed((): string => {
|
||||
if (props.activeFontColor == "") {
|
||||
return getDefaultColor(xConfig.color)
|
||||
}
|
||||
return getDefaultColor(props.activeFontColor)
|
||||
})
|
||||
|
||||
const _fontColor = computed((): string => {
|
||||
if (xConfig.dark == 'dark') return "#ffffff"
|
||||
return getDefaultColor(props.fontColor)
|
||||
})
|
||||
|
||||
const _fontSize = computed((): string => {
|
||||
let fontSize = checkIsCssUnit(props.fontSize, xConfig.unit);
|
||||
if (xConfig.fontScale == 1) return fontSize;
|
||||
let sizeNumber = parseInt(fontSize)
|
||||
if (isNaN(sizeNumber)) {
|
||||
sizeNumber = 14
|
||||
}
|
||||
return (sizeNumber * xConfig.fontScale).toString() + getUnit(fontSize)
|
||||
})
|
||||
|
||||
const _height = computed((): string => checkIsCssUnit(props.height, xConfig.unit))
|
||||
const _space = computed((): string => checkIsCssUnit(props.space, xConfig.unit))
|
||||
|
||||
const _round = computed((): string => {
|
||||
if (props.round == "") {
|
||||
return checkIsCssUnit(xConfig.radioButtonRadius, xConfig.unit)
|
||||
}
|
||||
return checkIsCssUnit(props.round, xConfig.unit)
|
||||
})
|
||||
|
||||
const _styleRadioButtonBgMap = computed((): Map<string, string> => {
|
||||
let styleMap = new Map<string, string>()
|
||||
styleMap.set("transitionDuration", first.value ? '0ms' : '350ms')
|
||||
styleMap.set("backgroundColor", _activeColor.value)
|
||||
styleMap.set("width", pos_w.value.toString() + 'px')
|
||||
const raiuds = parseInt(_round.value) - parseInt(_space.value)
|
||||
styleMap.set("borderRadius", checkIsCssUnit(raiuds, xConfig.unit))
|
||||
styleMap.set("transform", 'translateX(' + pos_x.value.toString() + 'px)')
|
||||
return styleMap;
|
||||
})
|
||||
|
||||
// 方法定义
|
||||
function getIcon(item: RADIO_BUTTON): string {
|
||||
if (item.icon == null) return ""
|
||||
return item.icon as string
|
||||
}
|
||||
|
||||
function setPos(): void {
|
||||
uni.createSelectorQuery().in(proxy)
|
||||
.selectAll(`.xRadioButtonBtnItem`)
|
||||
.boundingClientRect().exec((ret) => {
|
||||
let nodeinfo = ret[0] as NodeInfo[];
|
||||
let nowindex = nodeinfo.findIndex((el: NodeInfo): boolean => {
|
||||
let ids = el.id!;
|
||||
let oldid = id.value + '-' + nowId.value;
|
||||
return ids == oldid;
|
||||
})
|
||||
if (nowindex > -1) {
|
||||
let diffleft = 0;
|
||||
let difar = nodeinfo.slice(0, nowindex)
|
||||
difar.forEach((el: NodeInfo) => {
|
||||
diffleft += el.width!;
|
||||
})
|
||||
let nowitem = nodeinfo[nowindex]
|
||||
pos_x.value = diffleft
|
||||
pos_w.value = nowitem.width!;
|
||||
}
|
||||
nextTick(() => {
|
||||
first.value = false;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function itemClick(item: RADIO_BUTTON, index: number): void {
|
||||
/**
|
||||
* 点击按钮时触发.
|
||||
*/
|
||||
emits('click', item.id, index)
|
||||
if (nowId.value == item.id) return;
|
||||
let disabled = (item?.disabled ?? false) as boolean
|
||||
if (disabled == true) return;
|
||||
|
||||
nowId.value = item.id;
|
||||
setPos();
|
||||
/**
|
||||
* 等同v-model
|
||||
*/
|
||||
emits("update:modelValue", nowId.value);
|
||||
/**
|
||||
* 变换时触发。
|
||||
* @param value {string} 当前选中的id
|
||||
*/
|
||||
emits("change", nowId.value, index)
|
||||
}
|
||||
|
||||
// 监听器
|
||||
watch((): string => props.modelValue, (newValue: string): void => {
|
||||
if (nowId.value == newValue) return;
|
||||
nowId.value = newValue;
|
||||
setPos();
|
||||
})
|
||||
|
||||
// 生命周期
|
||||
onMounted((): void => {
|
||||
nowId.value = props.modelValue;
|
||||
setPos();
|
||||
|
||||
// #ifdef APP || WEB
|
||||
let ele = xRadioButton.value
|
||||
if (ele == null) return;
|
||||
if (resizeObserver.value == null) {
|
||||
resizeObserver.value = new UniResizeObserver((entries: Array<UniResizeObserverEntry>) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.target == ele) {
|
||||
setPos();
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
resizeObserver.value!.observe(ele!)
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
setTimeout(function () {
|
||||
setPos();
|
||||
}, 150);
|
||||
// #endif
|
||||
})
|
||||
|
||||
onBeforeUnmount((): void => {
|
||||
resizeObserver.value?.disconnect()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<view class="xRadioButton" ref="xRadioButton" :style="{
|
||||
height: _height,
|
||||
padding: _space,
|
||||
backgroundColor: _bgColor,
|
||||
borderRadius:_round
|
||||
}">
|
||||
<view class="xRadioButtonWrap">
|
||||
<view class="xRadioButtonBtns">
|
||||
<view @click="itemClick(item,index)"
|
||||
v-for="(item,index) in _list"
|
||||
:key="index"
|
||||
:id="id+'-'+item.id"
|
||||
:class="[
|
||||
`xRadioButtonBtnItem-ids-`+item.id,item.disabled==true?'xRadioButtonDisabled':'',
|
||||
]"
|
||||
class="xRadioButtonBtnItem"
|
||||
>
|
||||
<x-icon :style="{marginRight:item.title==''?'0px':'5px'}" v-if="getIcon(item) !=''" :name="item.icon"
|
||||
:font-size="_fontSize" :color="item.id==nowId?_activeFontColor:_fontColor"></x-icon>
|
||||
<text class="xRadioButtonDisabledText" :style="[
|
||||
{
|
||||
fontSize:_fontSize,
|
||||
color:item.id==nowId?_activeFontColor:_fontColor,
|
||||
},_textStyle
|
||||
]">{{item.title}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="xRadioButtonbgBtn" :style="_styleRadioButtonBgMap">
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<style scoped>
|
||||
.xRadioButtonDisabledText{
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
/* #ifdef APP */
|
||||
lines: 1;
|
||||
/* #endif */
|
||||
/* #ifdef MP||WEB */
|
||||
line-height:1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
/* #endif */
|
||||
}
|
||||
.xRadioButton {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
}
|
||||
|
||||
.xRadioButtonWrap {
|
||||
height:100%;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.xRadioButtonBtnItem {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 0 12px;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
flex-shrink: 0;
|
||||
|
||||
}
|
||||
/* #ifdef WEB */
|
||||
.xRadioButtonBtnItem{
|
||||
cursor: pointer;
|
||||
}
|
||||
.xRadioButtonDisabled{
|
||||
cursor: not-allowed;
|
||||
}
|
||||
/* #endif */
|
||||
.xRadioButtonDisabled{
|
||||
opacity: 0.5;
|
||||
}
|
||||
.xRadioButtonBtns {
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.xRadioButtonbgBtn {
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
width: 0px;
|
||||
transition-timing-function: cubic-bezier(.18, .89, .32, 1);
|
||||
transition-property: transform;
|
||||
transition-duration: 0ms;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user