674 lines
11 KiB
Plaintext
674 lines
11 KiB
Plaintext
<template>
|
|
<view
|
|
class="cl-button"
|
|
:class="[
|
|
`cl-button--${size}`,
|
|
`cl-button--${type} `,
|
|
{
|
|
'cl-button--loading': loading,
|
|
'cl-button--disabled': disabled,
|
|
'cl-button--text': text,
|
|
'cl-button--border': border,
|
|
'cl-button--rounded': rounded,
|
|
'cl-button--icon': isIcon,
|
|
'cl-button--hover': isHover,
|
|
'is-dark': isDark
|
|
},
|
|
isHover ? hoverClass : '',
|
|
pt.className
|
|
]"
|
|
:key="cache.key"
|
|
:style="buttonStyle"
|
|
@tap.stop="onTap"
|
|
>
|
|
<button
|
|
class="cl-button__clicker"
|
|
:disabled="isDisabled"
|
|
:hover-class="hoverClass"
|
|
:hover-stop-propagation="hoverStopPropagation"
|
|
:hover-start-time="hoverStartTime"
|
|
:hover-stay-time="hoverStayTime"
|
|
:form-type="formType"
|
|
:open-type="openType"
|
|
:lang="lang"
|
|
:session-from="sessionFrom"
|
|
:send-message-title="sendMessageTitle"
|
|
:send-message-path="sendMessagePath"
|
|
:send-message-img="sendMessageImg"
|
|
:show-message-card="showMessageCard"
|
|
:app-parameter="appParameter"
|
|
:group-id="groupId"
|
|
:guild-id="guildId"
|
|
:public-id="publicId"
|
|
:phone-number-no-quota-toast="phoneNumberNoQuotaToast"
|
|
:createliveactivity="createliveactivity"
|
|
@getuserinfo="onGetUserInfo"
|
|
@contact="onContact"
|
|
@getphonenumber="onGetPhoneNumber"
|
|
@error="onError"
|
|
@opensetting="onOpenSetting"
|
|
@launchapp="onLaunchApp"
|
|
@chooseavatar="onChooseAvatar"
|
|
@chooseaddress="onChooseAddress"
|
|
@chooseinvoicetitle="onChooseInvoiceTitle"
|
|
@addgroupapp="onAddGroupApp"
|
|
@subscribe="onSubscribe"
|
|
@login="onLogin"
|
|
@getrealtimephonenumber="onGetRealtimePhoneNumber"
|
|
@agreeprivacyauthorization="onAgreePrivacyAuthorization"
|
|
@touchstart="onTouchStart"
|
|
@touchend="onTouchEnd"
|
|
@touchcancel="onTouchCancel"
|
|
></button>
|
|
|
|
<cl-loading
|
|
:color="loadingIcon.color"
|
|
:size="loadingIcon.size"
|
|
:pt="{
|
|
className: parseClass(['mr-[10rpx]', pt.loading?.className])
|
|
}"
|
|
v-if="loading && !disabled"
|
|
></cl-loading>
|
|
|
|
<cl-icon
|
|
:name="icon"
|
|
:color="leftIcon.color"
|
|
:size="leftIcon.size"
|
|
:pt="{
|
|
className: parseClass([
|
|
{
|
|
'mr-[8rpx]': !isIcon
|
|
},
|
|
pt.icon?.className
|
|
])
|
|
}"
|
|
v-if="icon"
|
|
></cl-icon>
|
|
|
|
<template v-if="!isIcon">
|
|
<cl-text
|
|
:color="textColor"
|
|
:pt="{
|
|
className: parseClass([
|
|
'cl-button__label',
|
|
{
|
|
'!text-sm': size == 'small'
|
|
},
|
|
pt.label?.className
|
|
])
|
|
}"
|
|
>
|
|
<slot></slot>
|
|
</cl-text>
|
|
|
|
<slot name="content"></slot>
|
|
</template>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref, useSlots, type PropType } from "vue";
|
|
import { get, isDark, parseClass, parsePt, useCache } from "@/cool";
|
|
import type { ClIconProps } from "../cl-icon/props";
|
|
import type { ClButtonType, PassThroughProps, Size } from "../../types";
|
|
import type { ClLoadingProps } from "../cl-loading/props";
|
|
|
|
defineOptions({
|
|
name: "cl-button"
|
|
});
|
|
|
|
// 组件属性定义
|
|
const props = defineProps({
|
|
// 样式穿透
|
|
pt: {
|
|
type: Object,
|
|
default: () => ({})
|
|
},
|
|
// 按钮类型
|
|
type: {
|
|
type: String as PropType<ClButtonType>,
|
|
default: "primary"
|
|
},
|
|
// 字体、图标颜色
|
|
color: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 图标
|
|
icon: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 文本按钮
|
|
text: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 圆角按钮
|
|
rounded: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 边框按钮
|
|
border: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 加载状态
|
|
loading: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 禁用状态
|
|
disabled: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 按钮尺寸
|
|
size: {
|
|
type: String as PropType<Size>,
|
|
default: "normal"
|
|
},
|
|
// 按钮点击态样式类
|
|
hoverClass: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 是否阻止点击态冒泡
|
|
hoverStopPropagation: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 按住后多久出现点击态
|
|
hoverStartTime: {
|
|
type: Number,
|
|
default: 20
|
|
},
|
|
// 手指松开后点击态保留时间
|
|
hoverStayTime: {
|
|
type: Number,
|
|
default: 70
|
|
},
|
|
// 表单提交类型
|
|
formType: {
|
|
type: String as PropType<"submit" | "reset">,
|
|
default: ""
|
|
},
|
|
// 开放能力类型
|
|
openType: {
|
|
type: String as PropType<
|
|
| "agreePrivacyAuthorization"
|
|
| "feedback"
|
|
| "share"
|
|
| "getUserInfo"
|
|
| "contact"
|
|
| "getPhoneNumber"
|
|
| "launchApp"
|
|
| "openSetting"
|
|
| "chooseAvatar"
|
|
| "getAuthorize"
|
|
| "lifestyle"
|
|
| "contactShare"
|
|
| "openGroupProfile"
|
|
| "openGuildProfile"
|
|
| "openPublicProfile"
|
|
| "shareMessageToFriend"
|
|
| "addFriend"
|
|
| "addColorSign"
|
|
| "addGroupApp"
|
|
| "addToFavorites"
|
|
| "chooseAddress"
|
|
| "chooseInvoiceTitle"
|
|
| "login"
|
|
| "subscribe"
|
|
| "favorite"
|
|
| "watchLater"
|
|
| "openProfile"
|
|
| "liveActivity"
|
|
| "getRealtimePhoneNumber"
|
|
>,
|
|
default: ""
|
|
},
|
|
// 语言
|
|
lang: {
|
|
type: String as PropType<"en" | "zh_CN" | "zh_TW">,
|
|
default: "zh_CN"
|
|
},
|
|
// 会话来源
|
|
sessionFrom: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 会话标题
|
|
sendMessageTitle: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 会话路径
|
|
sendMessagePath: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 会话图片
|
|
sendMessageImg: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 显示会话卡片
|
|
showMessageCard: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 打开 APP 时,向 APP 传递的参数
|
|
appParameter: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 群ID
|
|
groupId: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 公会ID
|
|
guildId: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 公众号ID
|
|
publicId: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 手机号获取失败时是否弹出错误提示
|
|
phoneNumberNoQuotaToast: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 是否创建直播活动
|
|
createliveactivity: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
});
|
|
|
|
// 事件定义
|
|
const emit = defineEmits([
|
|
"click",
|
|
"tap",
|
|
"getuserinfo",
|
|
"contact",
|
|
"getphonenumber",
|
|
"error",
|
|
"opensetting",
|
|
"launchapp",
|
|
"chooseavatar",
|
|
"chooseaddress",
|
|
"chooseinvoicetitle",
|
|
"addgroupapp",
|
|
"subscribe",
|
|
"login",
|
|
"getrealtimephonenumber",
|
|
"agreeprivacyauthorization"
|
|
]);
|
|
|
|
const slots = useSlots();
|
|
const { cache } = useCache(() => [
|
|
props.type,
|
|
props.text,
|
|
props.disabled,
|
|
props.loading,
|
|
props.color
|
|
]);
|
|
|
|
// 样式穿透类型
|
|
type PassThrough = {
|
|
className?: string;
|
|
label?: PassThroughProps;
|
|
icon?: ClIconProps;
|
|
loading?: ClLoadingProps;
|
|
};
|
|
|
|
// 样式穿透计算
|
|
const pt = computed(() => parsePt<PassThrough>(props.pt));
|
|
|
|
// 是否是图标按钮
|
|
const isIcon = computed(() => get(slots, "default") == null && get(slots, "content") == null);
|
|
|
|
// 文本颜色
|
|
const textColor = computed(() => {
|
|
if (props.color != "") {
|
|
return props.color;
|
|
}
|
|
|
|
let color = "light";
|
|
|
|
if (props.text) {
|
|
color = props.type;
|
|
|
|
if (props.disabled) {
|
|
color = "disabled";
|
|
}
|
|
}
|
|
|
|
if (props.type == "light") {
|
|
if (!isDark.value) {
|
|
color = "dark";
|
|
}
|
|
}
|
|
|
|
return color;
|
|
});
|
|
|
|
// 图标信息
|
|
const leftIcon = computed<ClIconProps>(() => {
|
|
let color = textColor.value;
|
|
let size: number | string;
|
|
|
|
switch (props.size) {
|
|
case "small":
|
|
size = 26;
|
|
break;
|
|
default:
|
|
size = 32;
|
|
break;
|
|
}
|
|
|
|
const ptIcon = pt.value.icon;
|
|
|
|
if (ptIcon != null) {
|
|
color = ptIcon.color ?? color;
|
|
size = ptIcon.size ?? size;
|
|
}
|
|
|
|
return {
|
|
size,
|
|
color
|
|
};
|
|
});
|
|
|
|
// 加载图标信息
|
|
const loadingIcon = computed<ClLoadingProps>(() => {
|
|
let color = textColor.value;
|
|
let size: number | string;
|
|
|
|
switch (props.size) {
|
|
case "small":
|
|
size = 22;
|
|
break;
|
|
default:
|
|
size = 24;
|
|
break;
|
|
}
|
|
|
|
const ptIcon = pt.value.loading;
|
|
|
|
if (ptIcon != null) {
|
|
color = ptIcon.color ?? color;
|
|
size = ptIcon.size ?? size;
|
|
}
|
|
|
|
return {
|
|
size,
|
|
color
|
|
};
|
|
});
|
|
|
|
// 按钮样式
|
|
const buttonStyle = computed(() => {
|
|
const style = {};
|
|
|
|
if (props.color != "") {
|
|
style["border-color"] = props.color;
|
|
}
|
|
|
|
return style;
|
|
});
|
|
|
|
// 是否禁用状态
|
|
const isDisabled = computed(() => props.disabled || props.loading);
|
|
|
|
// 点击事件处理
|
|
function onTap(e: UniPointerEvent) {
|
|
if (isDisabled.value) return;
|
|
|
|
emit("click", e);
|
|
emit("tap", e);
|
|
}
|
|
|
|
// 获取用户信息事件处理
|
|
function onGetUserInfo(e: UniEvent) {
|
|
emit("getuserinfo", e);
|
|
}
|
|
|
|
// 客服消息事件处理
|
|
function onContact(e: UniEvent) {
|
|
emit("contact", e);
|
|
}
|
|
|
|
// 获取手机号事件处理
|
|
function onGetPhoneNumber(e: UniEvent) {
|
|
emit("getphonenumber", e);
|
|
}
|
|
|
|
// 错误事件处理
|
|
function onError(e: UniEvent) {
|
|
emit("error", e);
|
|
}
|
|
|
|
// 打开设置事件处理
|
|
function onOpenSetting(e: UniEvent) {
|
|
emit("opensetting", e);
|
|
}
|
|
|
|
// 打开APP事件处理
|
|
function onLaunchApp(e: UniEvent) {
|
|
emit("launchapp", e);
|
|
}
|
|
|
|
// 选择头像事件处理
|
|
function onChooseAvatar(e: UniEvent) {
|
|
emit("chooseavatar", e);
|
|
}
|
|
|
|
// 选择收货地址事件处理
|
|
function onChooseAddress(e: UniEvent) {
|
|
emit("chooseaddress", e);
|
|
}
|
|
|
|
// 选择发票抬头事件处理
|
|
function onChooseInvoiceTitle(e: UniEvent) {
|
|
emit("chooseinvoicetitle", e);
|
|
}
|
|
|
|
// 添加群应用事件处理
|
|
function onAddGroupApp(e: UniEvent) {
|
|
emit("addgroupapp", e);
|
|
}
|
|
|
|
// 订阅消息事件处理
|
|
function onSubscribe(e: UniEvent) {
|
|
emit("subscribe", e);
|
|
}
|
|
|
|
// 登录事件处理
|
|
function onLogin(e: UniEvent) {
|
|
emit("login", e);
|
|
}
|
|
|
|
// 获取实时手机号事件处理
|
|
function onGetRealtimePhoneNumber(e: UniEvent) {
|
|
emit("getrealtimephonenumber", e);
|
|
}
|
|
|
|
// 同意隐私授权事件处理
|
|
function onAgreePrivacyAuthorization(e: UniEvent) {
|
|
emit("agreeprivacyauthorization", e);
|
|
}
|
|
|
|
// 点击态状态
|
|
const isHover = ref(false);
|
|
|
|
// 触摸开始事件处理
|
|
function onTouchStart() {
|
|
if (!isDisabled.value) {
|
|
isHover.value = true;
|
|
}
|
|
}
|
|
|
|
// 触摸结束事件处理
|
|
function onTouchEnd() {
|
|
isHover.value = false;
|
|
}
|
|
|
|
// 触摸取消事件处理
|
|
function onTouchCancel() {
|
|
isHover.value = false;
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@mixin button-type($color) {
|
|
@apply bg-#{$color}-500;
|
|
|
|
&.cl-button--hover {
|
|
@apply bg-#{$color}-600;
|
|
}
|
|
|
|
&.cl-button--text {
|
|
background-color: transparent;
|
|
|
|
&.cl-button--hover {
|
|
@apply bg-transparent opacity-50;
|
|
}
|
|
}
|
|
|
|
&.cl-button--border {
|
|
@apply border-#{$color}-500;
|
|
}
|
|
}
|
|
|
|
.cl-button {
|
|
@apply flex flex-row items-center justify-center relative box-border;
|
|
@apply border border-transparent border-solid;
|
|
overflow: visible;
|
|
transition-duration: 0.3s;
|
|
transition-property: background-color, border-color, opacity;
|
|
|
|
&__clicker {
|
|
@apply absolute p-0 m-0;
|
|
@apply w-full h-full;
|
|
@apply opacity-0;
|
|
@apply z-10;
|
|
}
|
|
|
|
&--small {
|
|
padding: 6rpx 14rpx;
|
|
border-radius: 12rpx;
|
|
|
|
&.cl-button--icon {
|
|
padding: 10rpx;
|
|
}
|
|
}
|
|
|
|
&--normal {
|
|
padding: 10rpx 28rpx;
|
|
border-radius: 16rpx;
|
|
|
|
&.cl-button--icon {
|
|
padding: 14rpx;
|
|
}
|
|
}
|
|
|
|
&--large {
|
|
padding: 14rpx 32rpx;
|
|
border-radius: 20rpx;
|
|
|
|
&.cl-button--icon {
|
|
padding: 18rpx;
|
|
}
|
|
}
|
|
|
|
&--rounded {
|
|
@apply rounded-full;
|
|
}
|
|
|
|
&--primary {
|
|
@include button-type("primary");
|
|
}
|
|
|
|
&--warn {
|
|
@include button-type("yellow");
|
|
}
|
|
|
|
&--error {
|
|
@include button-type("red");
|
|
}
|
|
|
|
&--info {
|
|
@include button-type("surface");
|
|
}
|
|
|
|
&--success {
|
|
@include button-type("green");
|
|
}
|
|
|
|
&--light {
|
|
@apply border-surface-700;
|
|
|
|
&.cl-button--hover {
|
|
@apply bg-surface-100;
|
|
}
|
|
|
|
&.is-dark {
|
|
&.cl-button--hover {
|
|
@apply bg-surface-700;
|
|
}
|
|
}
|
|
}
|
|
|
|
&--dark {
|
|
@apply bg-surface-700;
|
|
|
|
&.cl-button--hover {
|
|
@apply bg-surface-800;
|
|
}
|
|
}
|
|
|
|
&--disabled {
|
|
@apply bg-surface-300;
|
|
|
|
&.cl-button--border {
|
|
@apply border-surface-300;
|
|
}
|
|
}
|
|
|
|
&--loading {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
&.is-dark {
|
|
&.cl-button--disabled {
|
|
@apply bg-surface-400;
|
|
|
|
&.cl-button--border {
|
|
@apply border-surface-500;
|
|
}
|
|
}
|
|
|
|
&.cl-button--text {
|
|
@apply bg-transparent;
|
|
}
|
|
|
|
&.cl-button--light {
|
|
@apply border-surface-500;
|
|
}
|
|
}
|
|
}
|
|
|
|
.cl-button {
|
|
& + .cl-button {
|
|
@apply ml-2;
|
|
}
|
|
}
|
|
</style>
|