更新:登录功能
This commit is contained in:
101
uni_modules/wot-design-uni/components/wd-segmented/index.scss
Normal file
101
uni_modules/wot-design-uni/components/wd-segmented/index.scss
Normal file
@@ -0,0 +1,101 @@
|
||||
@import './../common/abstracts/_mixin.scss';
|
||||
@import './../common/abstracts/variable.scss';
|
||||
|
||||
.wot-theme-dark {
|
||||
@include b(segmented) {
|
||||
background: $-dark-background2;
|
||||
|
||||
@include e(item) {
|
||||
color: $-dark-color3;
|
||||
|
||||
@include when(active) {
|
||||
color: $-dark-color;
|
||||
|
||||
@include when(disabled) {
|
||||
color: $-dark-color3;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(disabled) {
|
||||
color: $-dark-color-gray;
|
||||
}
|
||||
|
||||
@include m(active) {
|
||||
background-color: $-color-theme;
|
||||
|
||||
@include when(disabled) {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@include b(segmented) {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-items: flex-start;
|
||||
width: 100%;
|
||||
background-color: $-segmented-item-bg-color;
|
||||
padding: $-segmented-padding;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
@include e(item) {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
z-index: 1;
|
||||
min-height: 28px;
|
||||
line-height: 28px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
color: $-segmented-item-color;
|
||||
font-weight: 400;
|
||||
|
||||
@include when(active) {
|
||||
font-weight: 550;
|
||||
}
|
||||
|
||||
@include when(large) {
|
||||
min-height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
@include when(small) {
|
||||
min-height: 24px;
|
||||
line-height: 24px;
|
||||
padding: 0 7px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@include when(disabled) {
|
||||
cursor: no-drop;
|
||||
color: $-segmented-item-disabled-color;
|
||||
}
|
||||
|
||||
@include m(active) {
|
||||
background-color: $-segmented-item-acitve-bg;
|
||||
border-radius: 4px;
|
||||
height: calc(100% - $-segmented-padding * 2);
|
||||
@include when(disabled) {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include e(item-label) {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
}
|
||||
80
uni_modules/wot-design-uni/components/wd-segmented/types.ts
Normal file
80
uni_modules/wot-design-uni/components/wd-segmented/types.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* @Author: weisheng
|
||||
* @Date: 2024-03-18 11:22:03
|
||||
* @LastEditTime: 2024-11-24 23:04:55
|
||||
* @LastEditors: weisheng
|
||||
* @Description:
|
||||
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-segmented/types.ts
|
||||
* 记得注释
|
||||
*/
|
||||
import type { ComponentPublicInstance, ExtractPropTypes, PropType } from 'vue'
|
||||
import { baseProps, makeBooleanProp, makeRequiredProp, makeStringProp } from '../common/props'
|
||||
|
||||
export type SegmentedType = 'large' | 'middle' | 'small'
|
||||
|
||||
export interface SegmentedOption {
|
||||
value: string | number // 选中值
|
||||
disabled?: boolean // 是否禁用
|
||||
payload?: any // 更多数据
|
||||
}
|
||||
|
||||
export const segmentedProps = {
|
||||
...baseProps,
|
||||
|
||||
/**
|
||||
* 当前选中的值
|
||||
* 类型: string | number
|
||||
* 最低版本: 0.1.23
|
||||
*/
|
||||
value: makeRequiredProp([String, Number]),
|
||||
|
||||
/**
|
||||
* 是否禁用分段器
|
||||
* 类型: boolean
|
||||
* 默认值: false
|
||||
* 最低版本: 0.1.23
|
||||
*/
|
||||
disabled: makeBooleanProp(false),
|
||||
|
||||
/**
|
||||
* 控件尺寸
|
||||
* 类型: string
|
||||
* 可选值: 'large' | 'middle' | 'small'
|
||||
* 默认值: 'middle'
|
||||
* 最低版本: 0.1.23
|
||||
*/
|
||||
size: makeStringProp<SegmentedType>('middle'),
|
||||
|
||||
/**
|
||||
* 数据集合
|
||||
* 类型: string[] | number[] | SegmentedOption[]
|
||||
* 必需: 是
|
||||
* 默认值: []
|
||||
* 最低版本: 0.1.23
|
||||
*/
|
||||
options: {
|
||||
type: Array as PropType<string[] | number[] | SegmentedOption[]>,
|
||||
required: true,
|
||||
default: () => []
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换选项时是否振动
|
||||
* 类型: boolean
|
||||
* 默认值: false
|
||||
* 最低版本: 0.1.23
|
||||
*/
|
||||
vibrateShort: makeBooleanProp(false)
|
||||
}
|
||||
|
||||
export type SegmentedExpose = {
|
||||
/**
|
||||
* 更新滑块偏移量
|
||||
* @param animation 是否开启动画,默认开启
|
||||
*/
|
||||
updateActiveStyle: (animation?: boolean) => void
|
||||
}
|
||||
|
||||
export type SegmentedProps = ExtractPropTypes<typeof segmentedProps>
|
||||
|
||||
export type SegmentedInstance = ComponentPublicInstance<SegmentedProps, SegmentedExpose>
|
||||
@@ -0,0 +1,143 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user