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 Button from './src/button.vue'
export const TnButton = withNoopInstall(Button)
export default TnButton
export * from './src/button'
export type { TnButtonInstance } from './src/instance'
+268
View File
@@ -0,0 +1,268 @@
import { buildProps, iconPropType } from '../../../utils'
import { componentShapes, componentTypes } from '../../../constants'
import {
useComponentCustomStyleProp,
useComponentSizeProp,
} from '../../base/composables/use-component-common-props'
import type { ExtractPropTypes } from 'vue'
/**
* 按钮FormType有效值
*/
export const buttonFormTypes = ['submit', 'reset'] as const
/**
* 按钮OpenType有效值
*/
export const buttonOpenTypes = [
'feedback',
'share',
'contact',
'getPhoneNumber',
'getRealtimePhoneNumber',
'launchApp',
'openSetting',
'getUserInfo',
'chooseAvatar',
'agreePrivacyAuthorization',
] as const
export const buttonProps = buildProps({
/**
* @description 按钮宽度
*/
width: {
type: [String, Number],
},
/**
* @description 按钮高度
*/
height: {
type: [String, Number],
},
/**
* @description 按钮尺寸
*/
size: useComponentSizeProp,
/**
* @description 按钮形状
*/
shape: {
type: String,
values: componentShapes,
default: '',
},
/**
* @description 按钮颜色类型
*/
type: {
type: String,
values: componentTypes,
default: 'primary',
},
/**
* @description 按钮图标
*/
icon: {
type: iconPropType,
},
/**
* @description 是否加粗字体
*/
bold: Boolean,
/**
* @description 字体大小
*/
fontSize: {
type: [String, Number],
},
/**
* @description 背景颜色,以tn开头则使用图鸟内置的颜色
*/
bgColor: String,
/**
* @description 文字颜色,以tn开头则使用图鸟内置的颜色
*/
textColor: String,
/**
* @description 是否显示为文本按钮
*/
text: Boolean,
/**
* @description 是否为朴素按钮
*/
plain: Boolean,
/**
* @description 边框颜色,以tn开头则使用图鸟内置的颜色
*/
borderColor: String,
/**
* @description 是否加粗边框
*/
borderBold: Boolean,
/**
* @description 是否显示阴影
*/
shadow: Boolean,
/**
* @description 阴影颜色,以tn开头则使用图鸟内置的颜色
*/
shadowColor: String,
/**
* @description 点击时触发的类
*/
hoverClass: {
type: String,
default: 'tn-u-btn-hover',
},
/**
* @description 自定义样式
*/
customStyle: useComponentCustomStyleProp,
/**
* @description 自定义类
*/
customClass: String,
/**
* @description 是否禁用按钮
*/
disabled: Boolean,
/**
* @description 是否只为一个按钮,不作用任何样式
*/
onlyButton: Boolean,
/**
* @description 是否显示加载中
*/
loading: Boolean,
/**
* @description 是否防抖
*/
debounce: {
type: Boolean,
default: false,
},
/**
* @description 触发form表单的事件类型
*/
formType: {
type: String,
values: buttonFormTypes,
},
/**
* @description 按钮开放能力,具体能力参考官网https://uniapp.dcloud.io/component/button.html
*/
openType: {
type: String,
values: buttonOpenTypes,
},
/**
* @description 打开app时向app传递的参数, 在微信、QQ小程序和openType为launchApp时生效
*/
appParameter: {
type: String,
default: '',
},
/**
* @description 会话来源, 在微信小程序和openType为contact时生效
*/
sessionFrom: {
type: String,
default: '',
},
/**
* @description 会话内消息卡片标题, 默认为当前标题, 在微信小程序和openType为contact时生效
*/
sendMessageTitle: {
type: String,
default: '',
},
/**
* @description 会话内消息卡片点击跳转小程序路径, 默认为当前路径, 在微信小程序和openType为contact时生效
*/
sendMessagePath: {
type: String,
default: '',
},
/**
* @description 会话内消息卡片图片, 默认为截图, 在微信小程序和openType为contact时生效
*/
sendMessageImg: {
type: String,
default: '',
},
/**
* @description 是否显示会话内消息卡片, 设置此参数为true, 用户进入客服会话会在右下角显示"可能要发送的小程序"提示, 用户点击后可以快速发送小程序消息, 在微信小程序和openType为contact时生效
*/
showMessageCard: {
type: Boolean,
default: false,
},
/**
* @description 当手机号快速验证或手机号实时验证额度用尽时,是否对用户展示“申请获取你的手机号,但该功能使用次数已达当前小程序上限,暂时无法使用”的提示
*/
phoneNumberNoQuotaToast: {
type: Boolean,
default: true,
},
clickModifiers: {
type: String,
},
})
export const buttonEmits = {
/**
* @description 按钮点击事件
*/
click: () => true,
/**
* @description 获取用户手机号码回调
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getphonenumber: (e: any) => true,
/**
* @description 获取用户手机号实时验证回调
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getrealtimephonenumber: (e: any) => true,
/**
* @description 打开权限设置面板并关闭时回调
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
opensetting: (e: any) => true,
/**
* @description 打开APP成功时回调
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
launchapp: (e: any) => true,
/**
* @description 获取用户信息回调
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getuserinfo: (e: any) => true,
/**
* @description 获取用户头像回调
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
chooseavatar: (e: any) => true,
/**
* @description 同意隐私授权回调
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
agreeprivacyauthorization: (e: any) => true,
/**
* @description 客服消息回调
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
contact: (e: any) => true,
/**
* @description 开放能力调用发生错误时回调
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
error: (e: any) => true,
}
export type ButtonProps = ExtractPropTypes<typeof buttonProps>
export type ButtonEmits = typeof buttonEmits
export type ButtonFormType = ButtonProps['formType']
export type ButtonOpenType = ButtonProps['openType']
+119
View File
@@ -0,0 +1,119 @@
<script lang="ts" setup>
import TnIcon from '../../icon/src/icon.vue'
import TnLoading from '../../loading/src/loading.vue'
import { buttonEmits, buttonProps } from './button'
import { useButton, useButtonCustomStyle } from './composables'
const props = defineProps(buttonProps)
const emits = defineEmits(buttonEmits)
const {
buttonClick,
getPhoneNumber,
getRealTimePhoneNumber,
openSetting,
launchApp,
getUserInfo,
chooseAvatar,
agreePrivacyAuthorization,
contact,
openTypeError,
} = useButton(props, emits)
const { ns, buttonClass, buttonStyle } = useButtonCustomStyle(props)
</script>
// #ifdef MP-WEIXIN
<script lang="ts">
export default {
options: {
// 在微信小程序中将组件节点渲染为虚拟节点,更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
virtualHost: true,
},
}
</script>
// #endif
<template>
<button
v-if="props.clickModifiers === 'stop'"
class="tn-u-btn-clear"
:class="[buttonClass]"
:style="buttonStyle"
:hover-class="
props.disabled || props.loading || props.onlyButton ? '' : hoverClass
"
:disabled="disabled"
:form-type="formType"
:open-type="openType"
:app-parameter="appParameter"
:session-from="sessionFrom"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
:phone-number-no-quota-toast="phoneNumberNoQuotaToast"
@tap.stop="buttonClick"
@getphonenumber="getPhoneNumber"
@getrealtimephonenumber="getRealTimePhoneNumber"
@opensetting="openSetting"
@launchapp="launchApp"
@getuserinfo="getUserInfo"
@chooseavatar="chooseAvatar"
@agreeprivacyauthorization="agreePrivacyAuthorization"
@contact="contact"
@error="openTypeError"
>
<!-- TODO: loading状态 -->
<view v-if="loading" :class="[ns.m('loading')]">
<TnLoading show animation mode="flower" color="tn-gray" />
</view>
<!-- icon显示 -->
<view v-if="icon" :class="[ns.e('icon')]">
<TnIcon :name="icon" />
</view>
<slot v-else />
</button>
<button
v-else
class="tn-u-btn-clear"
:class="[buttonClass]"
:style="buttonStyle"
:hover-class="
props.disabled || props.loading || props.onlyButton ? '' : hoverClass
"
:disabled="disabled"
:form-type="formType"
:open-type="openType"
:app-parameter="appParameter"
:session-from="sessionFrom"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
:phone-number-no-quota-toast="phoneNumberNoQuotaToast"
@tap="buttonClick"
@getphonenumber="getPhoneNumber"
@getrealtimephonenumber="getRealTimePhoneNumber"
@opensetting="openSetting"
@launchapp="launchApp"
@getuserinfo="getUserInfo"
@chooseavatar="chooseAvatar"
@agreeprivacyauthorization="agreePrivacyAuthorization"
@contact="contact"
@error="openTypeError"
>
<!-- TODO: loading状态 -->
<view v-if="loading" :class="[ns.m('loading')]">
<TnLoading show animation mode="flower" color="tn-gray" />
</view>
<!-- icon显示 -->
<view v-if="icon" :class="[ns.e('icon')]">
<TnIcon :name="icon" />
</view>
<slot v-else />
</button>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/button.scss';
</style>
@@ -0,0 +1,135 @@
import { computed, toRef } from 'vue'
import { useComponentColor, useNamespace } from '../../../../hooks'
import { formatDomSizeValue, isEmpty } from '../../../../utils'
import type { CSSProperties } from 'vue'
import type { ButtonProps } from '../button'
export const useButtonCustomStyle = (props: ButtonProps) => {
const ns = useNamespace('button')
// 解析背景颜色
const [bgColorClass, bgColorStyle] = useComponentColor(
toRef(props, 'bgColor'),
'bg'
)
// 解析字体颜色
const [textColorClass, textColorStyle] = useComponentColor(
toRef(props, 'textColor'),
'text'
)
// 解析边框颜色
const [borderColorClass, borderColorStyle] = useComponentColor(
toRef(props, 'borderColor'),
'border'
)
// 解析阴影颜色
const [shadowColorClass, shadowColorStyle] = useComponentColor(
toRef(props, 'shadowColor'),
'shadow'
)
// 按钮动态类
const buttonClass = computed<string>(() => {
const cls: string[] = [ns.b()]
if (props.onlyButton) {
cls.push(ns.m('only-button'))
return cls.join(' ')
}
// 设置文字按钮
if (props.text) cls.push(ns.m('text'))
// 设置朴素按钮
if (props.plain) {
cls.push(ns.m('plain'))
if (props.borderBold) cls.push(ns.m('plain-bold'))
}
// 设置按钮颜色类型
if (props.type) {
if (props.text) {
if (!props.textColor) cls.push(`tn-type-${props.type}_text`)
} else if (props.plain) {
if (!props.borderColor) cls.push(`tn-type-${props.type}_border`)
} else {
if (!props.bgColor) cls.push(`tn-type-${props.type}_bg`)
}
}
// 设置按钮尺寸
if (props.size) cls.push(ns.m(props.size))
// 设置按钮形状
if (!props.text && props.shape) cls.push(ns.m(props.shape))
// 设置字体是否加粗
if (props.bold) cls.push('tn-text-bold')
// 设置背景颜色
if (!props.text && !props.plain) {
if (bgColorClass.value) cls.push(bgColorClass.value)
}
// 设置字体颜色
if (textColorClass.value) cls.push(textColorClass.value)
if (props.plain) {
// 设置边框颜色
if (borderColorClass.value) cls.push(borderColorClass.value)
}
// 设置阴影信息
if (props.shadow) {
cls.push('tn-shadow')
// 设置阴影颜色
if (shadowColorClass.value) cls.push(shadowColorClass.value)
}
if (props.customClass) cls.push(props.customClass)
return cls.join(' ')
})
// 按钮样式
const buttonStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (props.onlyButton) return style
// 设置按钮宽高
if (props.width) {
style.width = formatDomSizeValue(props.width)
if (props.shape === 'circle') style.height = style.width
}
if (props.height && props.shape !== 'circle')
style.height = formatDomSizeValue(props.height)
// 设置按钮字体大小
if (props.fontSize) style.fontSize = formatDomSizeValue(props.fontSize)
// 设置背景颜色
if (!props.text && !props.plain) {
if (bgColorStyle.value) style.backgroundColor = bgColorStyle.value
}
// 设置字体颜色
if (textColorStyle.value) {
style.color = textColorStyle.value
}
// 设置边框颜色
if (props.plain && borderColorStyle.value) {
style.borderColor = borderColorStyle.value
}
// 设置阴影颜色
if (props.shadow && shadowColorStyle.value)
style.boxShadow = shadowColorStyle.value
if (!isEmpty(props.customStyle)) {
Object.assign(style, props.customStyle)
}
return style
})
return {
ns,
buttonClass,
buttonStyle,
}
}
@@ -0,0 +1,2 @@
export * from './button-custom'
export * from './use-button'
@@ -0,0 +1,67 @@
import { debounce } from '../../../../libs/lodash'
import type { SetupContext } from 'vue'
import type { ButtonEmits, ButtonProps } from '../button'
export const useButton = (
props: ButtonProps,
emits: SetupContext<ButtonEmits>['emit']
) => {
// 按钮点击事件
const buttonClickHandle = () => {
if (props.disabled || props.loading) return
emits('click')
}
const buttonClick = props.debounce
? debounce(buttonClickHandle, 250)
: buttonClickHandle
// 获取手机号码回调
const getPhoneNumber = (e: any) => {
emits('getphonenumber', e)
}
// 获取手机号实时验证回调
const getRealTimePhoneNumber = (e: any) => {
emits('getrealtimephonenumber', e)
}
// 打开设置面板
const openSetting = (e: any) => {
emits('opensetting', e)
}
// 打开App成功回调
const launchApp = (e: any) => {
emits('launchapp', e)
}
// 获取用户信息回调
const getUserInfo = (e: any) => {
emits('getuserinfo', e)
}
// 获取用户头像回调
const chooseAvatar = (e: any) => {
emits('chooseavatar', e)
}
// 同意隐私授权回调
const agreePrivacyAuthorization = (e: any) => {
emits('agreeprivacyauthorization', e)
}
// 客服消息回调
const contact = (e: any) => {
emits('contact', e)
}
// 当使用开放能力时,发生错误的回调
const openTypeError = (e: any) => {
emits('error', e)
}
return {
buttonClick,
getPhoneNumber,
getRealTimePhoneNumber,
openSetting,
launchApp,
getUserInfo,
chooseAvatar,
agreePrivacyAuthorization,
contact,
openTypeError,
}
}
@@ -0,0 +1,3 @@
import type Button from './button.vue'
export type TnButtonInstance = InstanceType<typeof Button>