2025/2/8第一次更新

This commit is contained in:
爱吃咸鱼小猫咪
2025-02-08 18:50:38 +08:00
commit d7af560866
26519 changed files with 5046029 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
import { withNoopInstall } from '../../utils'
import Swiper from './src/swiper.vue'
export const TnSwiper = withNoopInstall(Swiper)
export default TnSwiper
export * from './src/swiper'
export type { TnSwiperInstance } from './src/instance'
@@ -0,0 +1,2 @@
export * from './swiper-custom'
export * from './use-swiper'
@@ -0,0 +1,99 @@
import { computed, toRef } from 'vue'
import { useComponentColor, useNamespace } from '../../../../hooks'
import { formatDomSizeValue } from '../../../../utils'
import type { CSSProperties } from 'vue'
import type { SwiperProps } from '../swiper'
type IndicatorColorClass = (active: boolean) => string
type IndicatorColorStyle = (active: boolean) => CSSProperties
export const useSwiperCustomStyle = (props: SwiperProps) => {
const ns = useNamespace('swiper')
// 解析颜色
const [indicatorBgColorClass, indicatorBgColoeStyle] = useComponentColor(
toRef(props, 'indicatorBgColor'),
'bg'
)
const [indicatorTextColorClass, indicatorTextColorStyle] = useComponentColor(
toRef(props, 'indicatorTextColor'),
'text'
)
const [indicatorActiveBgColorClass, indicatorActiveBgColorStyle] =
useComponentColor(toRef(props, 'indicatorActiveBgColor'), 'bg')
// swiper对应的样式
const swiperStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (props.width !== undefined) style.width = formatDomSizeValue(props.width)
if (props.height !== undefined)
style.height = formatDomSizeValue(props.height)
return style
})
// 指示器颜色对应的类
const indicatorColorClass = computed<IndicatorColorClass>(() => {
return (active: boolean) => {
const cls: string[] = []
if (props.indicatorType === 'number') {
if (indicatorBgColorClass.value) cls.push(indicatorBgColorClass.value)
if (indicatorTextColorClass.value)
cls.push(indicatorTextColorClass.value)
} else {
if (active) {
if (indicatorActiveBgColorClass.value)
cls.push(indicatorActiveBgColorClass.value)
} else {
if (indicatorBgColorClass.value) cls.push(indicatorBgColorClass.value)
}
}
return cls.join(' ')
}
})
// 指示器颜色对应的样式
const indicatorColorStyle = computed<IndicatorColorStyle>(() => {
return (active: boolean) => {
const style: CSSProperties = {}
if (props.indicatorType === 'number') {
if (!indicatorBgColorClass.value) {
style.backgroundColor =
indicatorBgColoeStyle.value || 'rgba(0, 0, 0, 0.25)'
}
if (indicatorTextColorStyle.value) {
style.color = indicatorTextColorStyle.value
} else if (
!indicatorTextColorClass.value &&
!indicatorBgColorClass.value
) {
style.color = 'var(--tn-color-white)'
}
} else {
if (active) {
if (!indicatorActiveBgColorClass.value)
style.backgroundColor =
indicatorActiveBgColorStyle.value || 'var(--tn-color-white)'
} else {
if (!indicatorBgColorClass.value)
style.backgroundColor =
indicatorBgColoeStyle.value || 'rgba(0, 0, 0, 0.25)'
}
}
return style
}
})
return {
ns,
swiperStyle,
indicatorColorClass,
indicatorColorStyle,
}
}
@@ -0,0 +1,56 @@
import { computed, nextTick, ref, watch } from 'vue'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../../constants'
import { isEmptyVariableInDefault } from '../../../../utils'
import type { SetupContext } from 'vue'
import type { SwiperEmits, SwiperProps } from '../swiper'
export const useSwiper = (
props: SwiperProps,
emits: SetupContext<SwiperEmits>['emit']
) => {
// 当前选中的SwiperItem索引
const currentSwiperIndex = ref<number>(
isEmptyVariableInDefault(props?.modelValue, 0)
)
watch(
() => props.modelValue,
(value) => (currentSwiperIndex.value = isEmptyVariableInDefault(value, 0))
)
// 轮播数据
const swiperData = computed<any[]>(() => {
if (props.data?.length) return props.data
if (props.blankCount)
return Array.from({ length: props.blankCount }).map((_, i) => i)
return []
})
// swiper数量
const swiperCount = computed<number>(() => swiperData.value?.length || 0)
// 处理swiper滑动事件
const swiperChangeHandle = (event: any) => {
const { current } = event.detail
if (props.modelValue === undefined || props.modelValue === 0)
currentSwiperIndex.value = current
emits(UPDATE_MODEL_EVENT, current)
nextTick(() => {
emits(CHANGE_EVENT, current)
})
}
// 处理item点击事件
const itemClickHandle = () => {
emits('item-click', currentSwiperIndex.value)
}
return {
swiperData,
currentSwiperIndex,
swiperCount,
swiperChangeHandle,
itemClickHandle,
}
}
@@ -0,0 +1,3 @@
import type Swiper from './swiper.vue'
export type TnSwiperInstance = InstanceType<typeof Swiper>
+135
View File
@@ -0,0 +1,135 @@
import { buildProps, definePropType, isNumber } from '../../../utils'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../constants'
import type { ExtractPropTypes } from 'vue'
export const swiperIndicatorPosition = [
'left-top',
'center-top',
'right-top',
'left-bottom',
'center-bottom',
'right-bottom',
] as const
export type SwiperIndicatorPosition = (typeof swiperIndicatorPosition)[number]
export const swiperIndicatorType = ['line', 'dot', 'number'] as const
export type SwiperIndicatorType = (typeof swiperIndicatorType)[number]
export const swiperProps = buildProps({
/**
* @description 当前选中item的索引值
*/
modelValue: {
type: Number,
default: 0,
},
/**
* @description swiper数据源
*/
data: {
type: definePropType<any[]>(Array),
default: [],
},
/**
* @description 空白swiper的数量,如果设置了data则以data的数据为准
*/
blankCount: Number,
/**
* @description 轮播图的宽度,默认单位rpx
*/
width: String,
/**
* @description 轮播图的高度,默认单位rpx
*/
height: String,
/**
* @description 是否自动播放
*/
autoplay: {
type: Boolean,
default: false,
},
/**
* @description 自动播放的时间间隔,单位ms
*/
interval: {
type: Number,
default: 5000,
},
/**
* @description 动画时长,单位ms
*/
duration: {
type: Number,
default: 500,
},
/**
* @description 是否循环播放
*/
loop: {
type: Boolean,
default: false,
},
/**
* @description 前边距,可用于露出前一项的一小部分,接受 px 和 rpx 值
*/
previousMargin: {
type: String,
default: '0px',
},
/**
* @description 后边距,可用于露出后一项的一小部分,接受 px 和 rpx 值
*/
nextMargin: {
type: String,
default: '0px',
},
/**
* @description 是否显示指示器
*/
indicator: Boolean,
/**
* @description 指示器的位置
*/
indicatorPosition: {
type: String,
values: swiperIndicatorPosition,
default: 'center-bottom',
},
/**
* @description 指示器的类型
*/
indicatorType: {
type: String,
values: swiperIndicatorType,
default: 'dot',
},
/**
* @description 指示器颜色,以tn开头使用图鸟内置的颜色
*/
indicatorBgColor: String,
/**
* @description 指示器激活时的颜色,以tn开头使用图鸟内置的颜色
*/
indicatorActiveBgColor: String,
/**
* @description 指示器文本颜色,以tn开头使用图鸟内置的颜色
*/
indicatorTextColor: String,
})
export const swiperEmits = {
[UPDATE_MODEL_EVENT]: (value: number) => isNumber(value),
/**
* @description 选项发生改变时触发
*/
[CHANGE_EVENT]: (value: number) => isNumber(value),
/**
* @description item点击事件
*/
'item-click': (value: number) => isNumber(value),
}
export type SwiperProps = ExtractPropTypes<typeof swiperProps>
export type SwiperEmits = typeof swiperEmits
+112
View File
@@ -0,0 +1,112 @@
<script lang="ts" setup>
import { swiperEmits, swiperProps } from './swiper'
import { useSwiper, useSwiperCustomStyle } from './composables'
const props = defineProps(swiperProps)
const emits = defineEmits(swiperEmits)
const {
swiperData,
currentSwiperIndex,
swiperCount,
swiperChangeHandle,
itemClickHandle,
} = useSwiper(props, emits)
const { ns, swiperStyle, indicatorColorClass, indicatorColorStyle } =
useSwiperCustomStyle(props)
</script>
// #ifdef MP-WEIXIN
<script lang="ts">
export default {
options: {
// 在微信小程序中将组件节点渲染为虚拟节点,更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
virtualHost: true,
},
}
</script>
// #endif
<template>
<view :class="[ns.b()]" :style="swiperStyle" @tap.stop="itemClickHandle">
<view :class="[ns.e('wrapper')]">
<swiper
:class="[ns.e('swiper')]"
:current="currentSwiperIndex"
:autoplay="autoplay"
:interval="interval"
:duration="duration"
:circular="loop"
:previous-margin="previousMargin"
:next-margin="nextMargin"
@change="swiperChangeHandle"
>
<swiper-item
v-for="(item, index) in swiperData"
:key="index"
:class="[ns.e('swiper-item')]"
>
<slot :active="index === currentSwiperIndex" :data="item" />
</swiper-item>
</swiper>
</view>
<!-- 指示器 -->
<view
v-if="indicator"
:class="[ns.e('indicator'), ns.em('indicator', indicatorPosition)]"
>
<!-- 矩形 -->
<view v-if="indicatorType === 'line'" :class="[ns.e('indicator-line')]">
<view
class="indicator-wrapper"
:class="[indicatorColorClass(false)]"
:style="indicatorColorStyle(false)"
>
<view
class="active-block"
:class="[indicatorColorClass(true)]"
:style="{
...indicatorColorStyle(true),
width: `${(100 / swiperData.length).toFixed(2)}%`,
transform: `translateX(${100 * currentSwiperIndex}%)`,
}"
/>
</view>
</view>
<!-- -->
<view v-if="indicatorType === 'dot'" :class="[ns.e('indicator-dot')]">
<view class="indicator-wrapper">
<view
v-for="(_, i) in swiperCount"
:key="i"
class="dot-item"
:class="[
indicatorColorClass(i === currentSwiperIndex),
{ active: i === currentSwiperIndex },
]"
:style="indicatorColorStyle(i === currentSwiperIndex)"
/>
</view>
</view>
<!-- 数值 -->
<view
v-if="indicatorType === 'number'"
:class="[ns.e('indicator-number')]"
>
<view
class="indicator-wrapper"
:class="[indicatorColorClass(false)]"
:style="indicatorColorStyle(false)"
>
<view class="current-index">{{ currentSwiperIndex + 1 }}</view>
<view class="sep">/</view>
<view class="swiper-count">{{ swiperCount }}</view>
</view>
</view>
</view>
</view>
</template>
<style lang="scss">
@import '../../../theme-chalk/src/swiper.scss';
</style>