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
+8
View File
@@ -0,0 +1,8 @@
import { withNoopInstall } from '../../utils'
import ReadMore from './src/read-more.vue'
export const TnReadMore = withNoopInstall(ReadMore)
export default TnReadMore
export * from './src/read-more'
export type { TnReadMoreInstance } from './src/instance'
@@ -0,0 +1,2 @@
export * from './read-more-custom'
export * from './use-read-more'
@@ -0,0 +1,40 @@
import { computed, toRef } from 'vue'
import { useComponentColor, useNamespace } from '../../../../hooks'
import type { CSSProperties } from 'vue'
import type { ReadMoreProps } from '../read-more'
export const useReadMoreCustomStyle = (props: ReadMoreProps) => {
const ns = useNamespace('read-more')
// 解析颜色
const [tipsColorClass, tipsColorStyle] = useComponentColor(
toRef(props, 'tipColor'),
'text'
)
// 操作区域的类
const operationAreaClass = computed<string>(() => {
const cls: string[] = [ns.e('operation-area')]
if (tipsColorClass.value) cls.push(tipsColorClass.value)
return cls.join(' ')
})
// 操作区域的样式
const operationAreaStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (!tipsColorClass.value) {
style.color = tipsColorStyle.value || 'var(--tn-color-primary)'
}
return style
})
return {
ns,
operationAreaClass,
operationAreaStyle,
}
}
@@ -0,0 +1,159 @@
import {
computed,
getCurrentInstance,
nextTick,
onMounted,
ref,
watch,
} from 'vue'
import { useSelectorQuery, useToggle } from '../../../../hooks'
import {
debugWarn,
formatDomSizeValue,
generateId,
isBoolean,
isPromise,
} from '../../../../utils'
import type { SetupContext } from 'vue'
import type { ReadMoreEmits, ReadMoreProps } from '../read-more'
export const useReadMore = (
props: ReadMoreProps,
emits: SetupContext<ReadMoreEmits>['emit']
) => {
const instance = getCurrentInstance()
if (!instance) {
debugWarn('TnReadMore', '请在 setup 中使用 useReadMore')
}
const componentId = `trm-${generateId()}`
const componentContentId = `${componentId}-content`
const { getSelectorNodeInfo } = useSelectorQuery(instance)
// 是否展开状态
const [expandStatus, toggleExpand] = useToggle(props.expand || false)
watch(
() => props.expand,
(val) => {
expandStatus.value = val
}
)
// 是否显示操作区域
const showOperationArea = computed<boolean>(
() => !expandStatus.value || (expandStatus.value && props.showFold)
)
// 操作区域高度
const foldOperationAreaHeight = 40
// 容器高度
const containerHeight = computed<string>(() => {
if (!expandStatus.value) {
return formatDomSizeValue(props.height)
} else {
return `calc(${formatDomSizeValue(
contentHeight.value,
'px'
)} + ${foldOperationAreaHeight}rpx)`
}
})
// 内容区域的高度
let contentHeight = ref(0)
// 获取内容区域容器信息
let initCount = 0
const getContentRectInfo = async () => {
try {
const rectInfo = await getSelectorNodeInfo(`#${componentContentId}`)
initCount = 0
contentHeight.value = rectInfo.height || 0
} catch (err) {
if (initCount > 10) {
initCount = 0
debugWarn('TnReadMore', `获取内容容器信息失败: ${err}`)
return
}
initCount++
setTimeout(() => {
getContentRectInfo()
}, 150)
}
}
// 重置内容容器的高度
const resetContentHeight = () => {
nextTick(() => {
getContentRectInfo()
})
}
// 设置为展开
const setExpand = () => {
emits('expand')
toggleExpand()
}
// 设置为折叠
const setFold = () => {
emits('fold')
toggleExpand()
}
// 切换折叠、展开状态
const toggleExpandStatus = () => {
if (expandStatus.value) {
setFold()
} else {
const { beforeExpand } = props
if (!beforeExpand) {
setExpand()
return
}
const shouldExpand = beforeExpand()
const isPromiseOrBoolean = [
isPromise(shouldExpand),
isBoolean(shouldExpand),
].includes(true)
if (!isPromiseOrBoolean) {
debugWarn(
'TnReadMore',
'beforeExpand 必须返回 Promise 或者 boolean 类型'
)
return
}
if (isPromise(shouldExpand)) {
shouldExpand.then((res) => {
if (res) {
setExpand()
}
})
} else {
if (shouldExpand) {
setExpand()
}
}
}
}
onMounted(() => {
nextTick(() => {
getContentRectInfo()
})
})
return {
componentContentId,
showOperationArea,
foldOperationAreaHeight,
containerHeight,
expandStatus,
toggleExpandStatus,
resetContentHeight,
}
}
@@ -0,0 +1,3 @@
import type ReadMore from './read-more.vue'
export type TnReadMoreInstance = InstanceType<typeof ReadMore>
@@ -0,0 +1,78 @@
import { buildProps, definePropType } from '../../../utils'
import type { ExtractPropTypes } from 'vue'
export type ReadMoreBeforeExpand = () => Promise<boolean> | boolean
export const readMoreProps = buildProps({
/**
* @description 默认是否展开
*/
expand: Boolean,
/**
* @description 默认显示内容的高度,默认单位是 rpx
*/
height: {
type: Number,
default: 250,
},
/**
* @description 是否显示收起按钮
*/
showFold: {
type: Boolean,
default: true,
},
/**
* @description 展开显示的文案
*/
expandText: {
type: String,
default: '显示更多',
},
/**
* @description 展开显示的图标
*/
expandIcon: {
type: String,
default: 'down',
},
/**
* @description 收起显示的文案
*/
foldText: {
type: String,
default: '收起',
},
/**
* @description 收起显示的图标
*/
foldIcon: {
type: String,
default: 'up',
},
/**
* @description 提示文案的颜色,使用tn开头则使用图鸟内置的颜色
*/
tipColor: String,
/**
* @description 展开前回调
*/
beforeExpand: {
type: definePropType<ReadMoreBeforeExpand>(Function),
},
})
export const readMoreEmits = {
/**
* @description 展开时触发
*/
expand: () => true,
/**
* @description 收起时触发
*/
fold: () => true,
}
export type ReadMoreProps = ExtractPropTypes<typeof readMoreProps>
export type ReadMoreEmits = typeof readMoreEmits
@@ -0,0 +1,76 @@
<script lang="ts" setup>
import TnIcon from '../../icon/src/icon.vue'
import { readMoreEmits, readMoreProps } from './read-more'
import { useReadMore, useReadMoreCustomStyle } from './composables'
const props = defineProps(readMoreProps)
const emits = defineEmits(readMoreEmits)
const {
componentContentId,
showOperationArea,
foldOperationAreaHeight,
containerHeight,
expandStatus,
toggleExpandStatus,
resetContentHeight,
} = useReadMore(props, emits)
const { ns, operationAreaClass, operationAreaStyle } =
useReadMoreCustomStyle(props)
defineExpose({
/**
* @description 重新设置内容容器的高度
*/
resetContentHeight,
})
</script>
<template>
<view :class="[ns.b()]" :style="{ height: containerHeight }">
<!-- 内容区域 -->
<view :id="componentContentId" :class="[ns.e('content')]">
<slot />
</view>
<!-- 操作区域 -->
<view
v-if="showOperationArea"
:class="[
operationAreaClass,
expandStatus ? ns.is('fold') : ns.is('expand'),
]"
:style="operationAreaStyle"
@tap.stop="toggleExpandStatus"
>
<template v-if="!expandStatus">
<slot name="expand">
<view class="expand" :class="[ns.e('operation-content')]">
<view class="text">{{ expandText }}</view>
<view class="icon">
<TnIcon :name="expandIcon" />
</view>
</view>
</slot>
</template>
<template v-else>
<slot name="fold">
<view
class="fold"
:class="[ns.e('operation-content')]"
:style="{ height: `${foldOperationAreaHeight}rpx` }"
>
<view class="text">{{ foldText }}</view>
<view class="icon">
<TnIcon :name="foldIcon" />
</view>
</view>
</slot>
</template>
</view>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/read-more.scss';
</style>