| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- <template>
- <view :class="`wd-segmented ${customClass}`" :style="customStyle">
- <view
- :class="`wd-segmented__item is-${size} ${state.activeIndex === index ? 'is-active' : ''} ${
- disabled || (isObj(option) ? option.disabled : false) ? 'is-disabled' : ''
- }`"
- @click="handleClick(option, index)"
- v-for="(option, index) in options"
- :key="index"
- >
- <view class="wd-segmented__item-label">
- <slot name="label" v-if="$slots.label" :option="isObj(option) ? option : { value: option }"></slot>
- <template v-else>
- {{ isObj(option) ? option.value : option }}
- </template>
- </view>
- </view>
- <view :class="`wd-segmented__item--active ${activeDisabled ? 'is-disabled' : ''}`" :style="state.activeStyle"></view>
- </view>
- </template>
- <script lang="ts">
- export default {
- name: 'wd-segmented',
- options: {
- addGlobalClass: true,
- virtualHost: true,
- styleIsolation: 'shared'
- }
- }
- </script>
- <script setup lang="ts">
- import { computed, getCurrentInstance, onMounted, reactive, watch } from 'vue'
- import { getRect, isObj, objToStyle, addUnit, pause, isEqual } from '../common/util'
- import type { CSSProperties } from 'vue'
- import { segmentedProps, type SegmentedExpose, type SegmentedOption } from './types'
- const $item = '.wd-segmented__item'
- const props = defineProps(segmentedProps)
- const emit = defineEmits(['update:value', 'change', 'click'])
- const state = reactive({
- activeIndex: 0, // 选中项
- activeStyle: '' // 选中样式
- })
- const activeDisabled = computed(() => {
- return props.disabled || (props.options[0] && isObj(props.options[0]) ? props.options[0].disabled : false)
- })
- watch(
- () => props.value,
- () => {
- updateCurrentIndex()
- updateActiveStyle()
- if (props.vibrateShort) {
- uni.vibrateShort({})
- }
- },
- {
- immediate: false
- }
- )
- const { proxy } = getCurrentInstance() as any
- onMounted(async () => {
- updateCurrentIndex()
- await pause()
- updateActiveStyle(false)
- })
- /**
- * 更新滑块偏移量
- *
- */
- function updateActiveStyle(animation: boolean = true) {
- getRect($item, true, proxy).then((rects) => {
- const rect = rects[state.activeIndex]
- const style: CSSProperties = {
- position: 'absolute',
- width: addUnit(rect.width!),
- 'z-index': 0
- }
- const left = rects.slice(0, state.activeIndex).reduce((prev, curr) => prev + Number(curr.width), 0)
- if (left) {
- style.transform = `translateX(${left}px)`
- }
- if (animation) {
- style.transition = 'all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1)'
- }
- state.activeStyle = objToStyle(style)
- })
- }
- /**
- * 更新值
- */
- function updateValue(newValue: string | number, option: string | number | SegmentedOption) {
- if (!isEqual(newValue, props.value)) {
- emit('update:value', newValue)
- emit('change', isObj(option) ? option : { value: newValue })
- }
- }
- /**
- * 更新当前下标
- */
- function updateCurrentIndex() {
- const index = props.options.findIndex((option: string | number | SegmentedOption) => {
- const value = isObj(option) ? option.value : option
- return isEqual(value, props.value)
- })
- if (index >= 0) {
- state.activeIndex = index
- } else {
- const value = isObj(props.options[0]) ? props.options[0].value : props.options[0]
- updateValue(value, props.options[0])
- }
- }
- function handleClick(option: string | number | SegmentedOption, index: number) {
- const disabled = props.disabled || (isObj(option) ? option.disabled : false)
- if (disabled) {
- return
- }
- const value = isObj(option) ? option.value : option
- state.activeIndex = index
- updateActiveStyle()
- updateValue(value, option)
- emit('click', isObj(option) ? option : { value })
- }
- defineExpose<SegmentedExpose>({
- updateActiveStyle
- })
- </script>
- <style lang="scss" scoped>
- @import './index.scss';
- </style>
|