| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- <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;
- @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>
|