uniapp-dyckui 4.1.8 → 4.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/components/MyComs/Button/index.vue","../src/components/MyComs/Dialog/index.vue","../src/components/MyComs/Divider/index.vue","../src/components/MyComs/DropdownSelect/dropdownSelect.ts","../src/components/MyComs/DropdownSelect/index.vue","../src/components/MyComs/DropdownWithBadge/dropdownWithBadge.ts","../src/components/MyComs/DropdownWithBadge/index.vue","../src/components/MyComs/FilterDrawer/hasBadge.ts","../src/components/MyComs/FilterDrawer/useFilterDrawer.ts","../src/components/MyComs/FilterDrawer/index.vue","../src/components/MyComs/InfiniteScroll/index.vue","../src/components/MyComs/Popup/index.vue","../src/components/MyComs/PullRefresh/index.vue","../src/components/MyComs/Swiper/index.vue","../src/components/MyComs/Toast/index.vue","../src/components/MyComs/index.ts"],"sourcesContent":["<template>\n <view\n class=\"sw-button\" :class=\"[\n `sw-button--${type}`,\n `sw-button--${size}`,\n `sw-button--${shape}`,\n { 'is-disabled': disabled },\n { 'is-loading': loading },\n { 'is-block': block },\n { 'is-round': round },\n { 'is-plain': plain },\n { 'is-outline': outline },\n { 'is-link': link },\n ]\"\n :disabled=\"disabled || loading\"\n @click=\"handleClick\"\n >\n <!-- 加载动画 -->\n <template v-if=\"loading\">\n <view class=\"sw-button__loading\">\n <text class=\"loading-spinner\" />\n </view>\n </template>\n\n <!-- 图标 -->\n <template v-if=\"!loading\">\n <!-- 自定义图标插槽 -->\n <view v-if=\"$slots.icon\" class=\"sw-button__icon\">\n <slot name=\"icon\" />\n </view>\n <!-- 传统图标类名方式 -->\n <text v-if=\"icon\" class=\"sw-button__icon\" :class=\"[icon]\" />\n </template>\n\n <!-- 文本内容 -->\n <text v-if=\"$slots.default\" class=\"sw-button__text\">\n <slot />\n </text>\n </view>\n</template>\n\n<script setup lang=\"ts\">\n// 按钮类型\ntype ButtonType = 'primary' | 'success' | 'warning' | 'danger' | 'info'\n\n// 按钮尺寸\ntype ButtonSize = 'large' | 'normal' | 'small' | 'mini'\n\n// 按钮形状\ntype ButtonShape = 'default' | 'circle' | 'round'\n\n// 按钮样式\ntype ButtonStyle = 'solid' | 'outline' | 'plain' | 'link'\n\ninterface Props {\n // 按钮类型\n type?: ButtonType\n // 按钮尺寸\n size?: ButtonSize\n // 按钮形状\n shape?: ButtonShape\n // 是否禁用\n disabled?: boolean\n // 是否加载中\n loading?: boolean\n // 是否为块级元素\n block?: boolean\n // 是否为圆形\n round?: boolean\n // 是否为朴素按钮\n plain?: boolean\n // 是否为描边按钮\n outline?: boolean\n // 是否为链接按钮\n link?: boolean\n // 图标类名\n icon?: string\n // 自定义类名\n customClass?: string\n // 自定义样式\n customStyle?: Record<string, any>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n type: 'primary',\n size: 'normal',\n shape: 'default',\n disabled: false,\n loading: false,\n block: false,\n round: false,\n plain: false,\n outline: false,\n link: false,\n icon: '',\n customClass: '',\n customStyle: () => ({}),\n})\n\nconst emit = defineEmits<{\n (e: 'click', event: MouseEvent): void\n}>()\n\nfunction handleClick(event: MouseEvent) {\n if (!props.disabled && !props.loading) {\n emit('click', event)\n }\n}\n</script>\n\n<style lang=\"less\" scoped>\n// 主题色变量\n@primary-color: #409eff;\n@success-color: #67c23a;\n@warning-color: #e6a23c;\n@danger-color: #f56c6c;\n@info-color: #909399;\n\n// 尺寸变量\n@button-height-large: 88rpx;\n@button-height-normal: 72rpx;\n@button-height-small: 60rpx;\n@button-height-mini: 48rpx;\n\n@button-padding-large: 0 48rpx;\n@button-padding-normal: 0 32rpx;\n@button-padding-small: 0 24rpx;\n@button-padding-mini: 0 16rpx;\n\n@button-font-size-large: 32rpx;\n@button-font-size-normal: 28rpx;\n@button-font-size-small: 26rpx;\n@button-font-size-mini: 24rpx;\n\n// 圆角变量\n@button-border-radius: 8rpx;\n@button-round-border-radius: 40rpx;\n@button-circle-border-radius: 50%;\n\n// 基础样式\n.sw-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n vertical-align: middle;\n cursor: pointer;\n border: none;\n outline: none;\n font-size: @button-font-size-normal;\n font-weight: 400;\n line-height: 1;\n transition: all 0.3s ease;\n box-sizing: border-box;\n position: relative;\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n // 文本样式\n .sw-button__text {\n display: inline-block;\n line-height: 1;\n vertical-align: middle;\n }\n\n // 图标样式\n .sw-button__icon {\n display: inline-block;\n margin-right: 12rpx;\n font-size: inherit;\n line-height: inherit;\n vertical-align: middle;\n }\n\n // 加载动画\n .sw-button__loading {\n display: inline-flex;\n align-items: center;\n margin-right: 12rpx;\n vertical-align: middle;\n\n .loading-spinner {\n display: inline-block;\n width: 32rpx;\n height: 32rpx;\n border: 4rpx solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n }\n }\n}\n\n// 按钮类型\n.sw-button--primary {\n background-color: @primary-color;\n color: #fff;\n}\n\n.sw-button--success {\n background-color: @success-color;\n color: #fff;\n}\n\n.sw-button--warning {\n background-color: @warning-color;\n color: #fff;\n}\n\n.sw-button--danger {\n background-color: @danger-color;\n color: #fff;\n}\n\n.sw-button--info {\n background-color: @info-color;\n color: #fff;\n}\n\n// 按钮尺寸\n.sw-button--large {\n height: @button-height-large;\n padding: @button-padding-large;\n font-size: @button-font-size-large;\n border-radius: @button-border-radius;\n}\n\n.sw-button--normal {\n height: @button-height-normal;\n padding: @button-padding-normal;\n font-size: @button-font-size-normal;\n border-radius: @button-border-radius;\n}\n\n.sw-button--small {\n height: @button-height-small;\n padding: @button-padding-small;\n font-size: @button-font-size-small;\n border-radius: @button-border-radius;\n}\n\n.sw-button--mini {\n height: @button-height-mini;\n padding: @button-padding-mini;\n font-size: @button-font-size-mini;\n border-radius: @button-border-radius;\n}\n\n// 按钮形状\n.sw-button--circle {\n padding: 0;\n border-radius: @button-circle-border-radius;\n\n // 确保圆形按钮有固定的宽高\n &.sw-button--large {\n width: @button-height-large;\n }\n\n &.sw-button--normal {\n width: @button-height-normal;\n }\n\n &.sw-button--small {\n width: @button-height-small;\n }\n\n &.sw-button--mini {\n width: @button-height-mini;\n }\n\n .sw-button__icon {\n margin-right: 0;\n }\n}\n\n.sw-button--round {\n border-radius: @button-round-border-radius;\n}\n\n// 块级按钮\n.is-block {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: center;\n}\n\n// 朴素按钮\n.is-plain {\n background-color: #fff;\n\n &.sw-button--primary {\n color: @primary-color;\n border: 1px solid @primary-color;\n }\n\n &.sw-button--success {\n color: @success-color;\n border: 1px solid @success-color;\n }\n\n &.sw-button--warning {\n color: @warning-color;\n border: 1px solid @warning-color;\n }\n\n &.sw-button--danger {\n color: @danger-color;\n border: 1px solid @danger-color;\n }\n\n &.sw-button--info {\n color: @info-color;\n border: 1px solid @info-color;\n }\n\n &:disabled {\n opacity: 0.4;\n }\n}\n\n// 描边按钮\n.is-outline {\n background-color: transparent;\n\n &.sw-button--primary {\n color: @primary-color;\n border: 1px solid @primary-color;\n }\n\n &.sw-button--success {\n color: @success-color;\n border: 1px solid @success-color;\n }\n\n &.sw-button--warning {\n color: @warning-color;\n border: 1px solid @warning-color;\n }\n\n &.sw-button--danger {\n color: @danger-color;\n border: 1px solid @danger-color;\n }\n\n &.sw-button--info {\n color: @info-color;\n border: 1px solid @info-color;\n }\n}\n\n// 链接按钮\n.is-link {\n background-color: transparent;\n border: none;\n padding: 0;\n height: auto;\n line-height: inherit;\n\n &.sw-button--primary {\n color: @primary-color;\n }\n\n &.sw-button--success {\n color: @success-color;\n }\n\n &.sw-button--warning {\n color: @warning-color;\n }\n\n &.sw-button--danger {\n color: @danger-color;\n }\n\n &.sw-button--info {\n color: @info-color;\n }\n\n &:hover {\n text-decoration: underline;\n }\n}\n\n// 禁用状态\n.is-disabled {\n opacity: 0.6;\n}\n\n// 加载状态\n.is-loading {\n .sw-button__text {\n display: none;\n }\n\n .sw-button__loading {\n margin-right: 0;\n }\n}\n\n// 动画\n@keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n</style>\n","<template>\r\n <view v-if=\"showDialog\" class=\"dialog-wrapper\" @click=\"handleMaskClick\">\r\n <view class=\"dialog-container\" @click.stop>\r\n <!-- 上部分内容 -->\r\n <view class=\"dialog-content\">\r\n <view class=\"dialog-icon\">\r\n <!-- 图标插槽包装器 -->\r\n <slot name=\"icon\">\r\n <!-- 自定义字体图标 -->\r\n <text v-if=\"customIcon\" class=\"iconfont dialog-icon-custom\">{{ customIcon }}</text>\r\n <!-- 根据图标类型显示字体图标 -->\r\n <text v-else-if=\"iconType === 'phone'\" class=\"iconfont dialog-button-primary icon-shouji\" />\r\n <text v-else-if=\"iconType === 'screen'\" class=\"iconfont dialog-button-primary icon-xianshiqi\" />\r\n <text v-else-if=\"iconType === 'links'\" class=\"iconfont dialog-button-primary icon-xianshiqi1\" />\r\n <text v-else-if=\"iconType === 'links2'\" class=\"iconfont dialog-button-primary icon-lianjiewangzhi\" />\r\n <!-- 兼容旧版本的图片图标 -->\r\n <image v-else-if=\"icon\" :src=\"icon\" alt=\"dialog-icon\" />\r\n <!-- 默认图标 -->\r\n <view v-else class=\"default-icon\" />\r\n </slot>\r\n </view>\r\n <view class=\"dialog-title\">\r\n {{ title }}\r\n </view>\r\n <text class=\"dialog-description\">{{ description }}</text>\r\n </view>\r\n\r\n <!-- 分割线 -->\r\n <view class=\"dialog-divider\" />\r\n\r\n <!-- 下部分按钮 -->\r\n <view class=\"dialog-buttons\">\r\n <!-- 类型1: 取消和打开app -->\r\n <template v-if=\"type === 'open-app'\">\r\n <view class=\"dialog-button dialog-button-cancel\" @click=\"handleCancel\">\r\n 取消\r\n </view>\r\n <view class=\"dialog-button dialog-button-primary\" @click=\"handleConfirm\">\r\n 打开app\r\n </view>\r\n </template>\r\n\r\n <!-- 类型2: 知道了 -->\r\n <template v-else-if=\"type === 'confirm'\">\r\n <view class=\"dialog-button dialog-button-full dialog-button-primary\" @click=\"handleConfirm\">\r\n 知道了\r\n </view>\r\n </template>\r\n\r\n <!-- 类型3: 取消和继续访问 -->\r\n <template v-else-if=\"type === 'continue'\">\r\n <view class=\"dialog-button dialog-button-cancel\" @click=\"handleCancel\">\r\n 取消\r\n </view>\r\n <view class=\"dialog-button dialog-button-primary\" @click=\"handleConfirm\">\r\n 继续访问\r\n </view>\r\n </template>\r\n </view>\r\n </view>\r\n </view>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, watch } from 'vue'\r\n\r\n// 弹窗类型定义\r\ntype DialogType = 'open-app' | 'confirm' | 'continue'\r\n\r\n// Props定义\r\ninterface Props {\r\n // 控制弹窗显示/隐藏(支持v-model)\r\n modelValue?: boolean\r\n // 弹窗类型\r\n type?: DialogType\r\n // 弹窗标题\r\n title?: string\r\n // 弹窗说明文字\r\n description?: string\r\n // 图标路径(兼容旧版本)\r\n icon?: string\r\n // 图标类型:phone、screen、links、links2\r\n iconType?: 'phone' | 'screen' | 'links' | 'links2'\r\n // 自定义字体图标\r\n customIcon?: string\r\n // 是否显示遮罩层\r\n mask?: boolean\r\n // 遮罩层透明度\r\n maskOpacity?: number\r\n // 点击遮罩是否关闭弹窗\r\n closeOnMaskClick?: boolean\r\n}\r\n\r\n// Props默认值\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: false,\r\n type: 'confirm',\r\n title: '',\r\n description: '',\r\n icon: '',\r\n iconType: undefined,\r\n customIcon: '',\r\n mask: true,\r\n maskOpacity: 0.5,\r\n closeOnMaskClick: true,\r\n})\r\n\r\n// Emits定义\r\nconst emit = defineEmits<{\r\n (e: 'update:modelValue', value: boolean): void\r\n (e: 'confirm'): void\r\n (e: 'cancel'): void\r\n}>()\r\n\r\n// 本地状态\r\nconst showDialog = ref(props.modelValue)\r\n\r\n// 监听modelValue变化\r\nwatch(() => props.modelValue, (newVal) => {\r\n showDialog.value = newVal\r\n})\r\n\r\n// 监听showDialog变化\r\nwatch(() => showDialog.value, (newVal) => {\r\n emit('update:modelValue', newVal)\r\n})\r\n\r\n// 点击遮罩层\r\nfunction handleMaskClick() {\r\n if (props.closeOnMaskClick) {\r\n showDialog.value = false\r\n }\r\n}\r\n\r\n// 点击确认按钮\r\nfunction handleConfirm() {\r\n emit('confirm')\r\n // 确认后关闭弹窗\r\n showDialog.value = false\r\n}\r\n\r\n// 点击取消按钮\r\nfunction handleCancel() {\r\n emit('cancel')\r\n // 取消后关闭弹窗\r\n showDialog.value = false\r\n}\r\n</script>\r\n\r\n<style scoped>\r\n/* 弹窗容器 */\r\n.dialog-wrapper {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n z-index: 1000;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background-color: rgba(0, 0, 0, v-bind('maskOpacity'));\r\n animation: fadeIn 300ms ease;\r\n}\r\n\r\n/* 弹窗主体 */\r\n.dialog-container {\r\n width: 80%;\r\n max-width: 800rpx;\r\n background-color: #fff;\r\n border-radius: 32rpx;\r\n overflow: hidden;\r\n box-shadow: 0 8rpx 40rpx rgba(0, 0, 0, 0.15);\r\n animation: scaleIn 300ms ease;\r\n background-image: url('@/assets/u2.png');\r\n background-size: cover;\r\n background-position: center;\r\n}\r\n\r\n/* 上部分内容 */\r\n.dialog-content {\r\n padding: 60rpx 40rpx;\r\n text-align: center;\r\n}\r\n\r\n/* 图标 */\r\n.dialog-icon {\r\n margin-bottom: 40rpx;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n display: inline-block;\r\n border-radius: 50%;\r\n border: 2rpx solid rgb(151, 150, 150);\r\n}\r\n\r\n/* 确保dialog-icon容器内的所有直接子元素都应用相同的基础样式 */\r\n.dialog-icon > * {\r\n width: 120rpx;\r\n height: 120rpx;\r\n font-size: 80rpx;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n.dialog-icon image {\r\n object-fit: contain;\r\n}\r\n\r\n/* 字体图标样式 */\r\n.dialog-icon .iconfont {\r\n /* 已通过 .dialog-icon > * 应用基础样式 */\r\n}\r\n\r\n/* 自定义字体图标 */\r\n.dialog-icon-custom {\r\n color: #1989fa;\r\n}\r\n\r\n/* 成功图标 */\r\n.dialog-icon-success {\r\n color: #67c23a;\r\n}\r\n\r\n/* 警告图标 */\r\n.dialog-icon-warning {\r\n color: #e6a23c;\r\n}\r\n\r\n/* 错误图标 */\r\n.dialog-icon-error {\r\n color: #f56c6c;\r\n}\r\n\r\n/* 信息图标 */\r\n.dialog-icon-info {\r\n color: #909399;\r\n}\r\n\r\n.default-icon {\r\n width: 120rpx;\r\n height: 120rpx;\r\n background-color: #f0f0f0;\r\n border-radius: 50%;\r\n}\r\n\r\n/* 标题 */\r\n.dialog-title {\r\n font-size: 36rpx;\r\n font-weight: bold;\r\n color: #333;\r\n margin: 0 0 20rpx 0;\r\n}\r\n\r\n/* 说明文字 */\r\n.dialog-description {\r\n font-size: 28rpx;\r\n color: #666;\r\n margin: 0;\r\n line-height: 1.5;\r\n}\r\n\r\n/* 分割线 */\r\n.dialog-divider {\r\n height: 2rpx;\r\n background-color: #e5e5e5;\r\n margin: 0;\r\n}\r\n\r\n/* 按钮区域 */\r\n.dialog-buttons {\r\n display: flex;\r\n flex-direction: row;\r\n align-items: center;\r\n}\r\n\r\n/* 按钮 */\r\n.dialog-button {\r\n flex: 1;\r\n padding: 30rpx;\r\n border: none;\r\n background: none;\r\n font-size: 32rpx;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: background-color 0.3s ease;\r\n text-align: center;\r\n box-sizing: border-box;\r\n}\r\n\r\n/* 全宽按钮 */\r\n.dialog-button-full {\r\n width: 100%;\r\n}\r\n\r\n/* 取消按钮 */\r\n.dialog-button-cancel {\r\n color: #666;\r\n}\r\n\r\n.dialog-button-cancel:hover {\r\n background-color: #f5f5f5;\r\n}\r\n\r\n/* 主要按钮 */\r\n.dialog-button-primary {\r\n color: #00599c;\r\n}\r\n\r\n.dialog-button-primary:hover {\r\n background-color: #f0f9ff;\r\n}\r\n\r\n/* 动画效果 */\r\n@keyframes fadeIn {\r\n from {\r\n opacity: 0;\r\n }\r\n to {\r\n opacity: 1;\r\n }\r\n}\r\n\r\n@keyframes scaleIn {\r\n from {\r\n transform: scale(0.8);\r\n opacity: 0;\r\n }\r\n to {\r\n transform: scale(1);\r\n opacity: 1;\r\n }\r\n}\r\n</style>\r\n","<template>\n <view\n class=\"sw-divider\"\n :class=\"[\n { 'is-vertical': vertical },\n { 'is-dashed': dashed },\n { [`sw-divider--${position}`]: position !== 'center' },\n ]\"\n >\n <text v-if=\"$slots.default\" class=\"sw-divider__text\">\n <slot />\n </text>\n </view>\n</template>\n\n<script setup lang=\"ts\">\ninterface DividerProps {\n // 是否为垂直分隔线\n vertical?: boolean\n // 是否为虚线\n dashed?: boolean\n // 文本位置,可选值为 left、center、right\n position?: 'left' | 'center' | 'right'\n // 自定义类名\n customClass?: string\n // 自定义样式\n customStyle?: Record<string, any>\n}\n\nconst props = withDefaults(defineProps<DividerProps>(), {\n vertical: false,\n dashed: false,\n position: 'center',\n customClass: '',\n customStyle: () => ({}),\n})\n</script>\n\n<style scoped lang=\"less\">\n// 主题色变量\n@primary-color: #409eff;\n@gray-3: #c0c4cc;\n\n// 基础样式\n.sw-divider {\n background-color: @gray-3;\n position: relative;\n box-sizing: border-box;\n\n &.is-vertical {\n display: inline-block;\n width: 2rpx;\n height: 32rpx;\n margin: 0 24rpx;\n vertical-align: middle;\n }\n\n &:not(.is-vertical) {\n width: 100%;\n height: 2rpx;\n margin: 32rpx 0;\n }\n\n &.is-dashed {\n background: none;\n border-top: 2rpx dashed @gray-3;\n }\n\n &__text {\n position: absolute;\n background-color: #fff;\n padding: 0 32rpx;\n font-size: 28rpx;\n color: #606266;\n transform: translateY(-50%);\n\n &.is-vertical {\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 32rpx 0;\n }\n }\n\n &--left {\n .sw-divider__text {\n left: 0;\n padding-left: 0;\n }\n }\n\n &--center {\n .sw-divider__text {\n left: 50%;\n transform: translateX(-50%) translateY(-50%);\n }\n }\n\n &--right {\n .sw-divider__text {\n right: 0;\n padding-right: 0;\n }\n }\n}\n</style>\n","import type { DropdownSelectEmits, DropdownSelectProps } from './type'\nimport { computed, ref } from 'vue'\n\nexport function useDropdownSelect<T>(\n props: DropdownSelectProps<T>,\n emit: DropdownSelectEmits<T>,\n) {\n // 控制箭头旋转状态\n const isOpen = ref(false)\n // 查找当前应显示的文本\n const displayText = computed(() => {\n if (!props.options?.length) {\n return '请选择'\n }\n // 优先根据 modelValue 匹配\n if (props.modelValue !== undefined && props.modelValue !== null) {\n const matched = props.options.find(\n opt => opt.value === props.modelValue,\n )\n if (matched)\n return matched.text\n }\n // 无有效 modelValue 时,默认显示第一项文本(仅 UI 展示)\n return props.options[0]?.text || '请选择'\n })\n // 内部绑定值:用于控制 Vant 选中高亮\n const internalValue = computed({\n get() {\n if (props.modelValue !== undefined && props.modelValue !== null) {\n return props.modelValue\n }\n // 无外部值时,返回第一个选项的 value(仅用于 UI 高亮,不 emit)\n return props.options?.[0]?.value ?? ''\n },\n set(val) {\n emit('update:modelValue', val as T)\n },\n })\n // 事件处理\n const handleChange = (value: T) => {\n emit('change', value)\n }\n\n const handleOpen = () => {\n isOpen.value = true\n }\n const handleClose = () => {\n isOpen.value = false\n }\n\n return {\n displayText,\n internalValue,\n isOpen,\n handleChange,\n handleOpen,\n handleClose,\n }\n}\n","<template>\n <view class=\"dropdown-select\">\n <!-- 下拉选择触发器 -->\n <view class=\"dropdown-trigger\" @tap=\"togglePicker\">\n <slot v-bind=\"{ text: displayText }\">\n <view class=\"dropdown-title\">\n <text class=\"dropdown-text\">{{ displayText }}</text>\n <text class=\"dropdown-icon\" :class=\"{ 'is-active': isOpen }\">\n <slot name=\"icon\">\n <!-- 使用 UniApp 内置图标或自定义图标 -->\n <view class=\"uni-icon\" :class=\"iconName\" />\n </slot>\n </text>\n </view>\n </slot>\n </view>\n\n <!-- 选择器弹窗 -->\n <picker\n v-if=\"isOpen\"\n mode=\"selector\"\n :range=\"options\"\n range-key=\"text\"\n :value=\"selectedIndex\"\n class=\"dropdown-picker\"\n @change=\"onPickerChange\"\n @cancel=\"onPickerCancel\"\n >\n <!-- UniApp picker 组件会自动渲染选择器UI -->\n </picker>\n </view>\n</template>\n\n<script setup lang=\"ts\" generic=\"T\">\nimport type { DropdownSelectEmits, DropdownSelectProps } from './type'\nimport { computed, ref } from 'vue'\nimport { useDropdownSelect } from './dropdownSelect'\n\nconst props = withDefaults(\n defineProps<DropdownSelectProps<T>>(),\n {\n iconName: 'arrow-down',\n },\n)\nconst emit = defineEmits<DropdownSelectEmits<T>>()\n\nconst { displayText, internalValue, handleChange, handleOpen, handleClose } = useDropdownSelect<T>(props, emit)\n\n// 控制选择器显示状态\nconst isOpen = ref(false)\n\n// 计算当前选中项的索引\nconst selectedIndex = computed(() => {\n if (!props.options || !props.options.length)\n return 0\n return props.options.findIndex(opt => opt.value === internalValue.value)\n})\n\n// 切换选择器显示/隐藏\nfunction togglePicker() {\n isOpen.value = !isOpen.value\n if (isOpen.value) {\n handleOpen()\n }\n else {\n handleClose()\n }\n}\n\n// 选择器确认选择\nfunction onPickerChange(e: any) {\n const index = e.detail.value\n const selectedOption = props.options[index]\n handleChange(selectedOption.value as T)\n isOpen.value = false\n handleClose()\n}\n\n// 选择器取消选择\nfunction onPickerCancel() {\n isOpen.value = false\n handleClose()\n}\n</script>\n\n<style lang=\"less\">\n@import './dropdownSelect.less';\n</style>\n","import type { DropdownWithBadgeProps } from './type'\nimport { computed } from 'vue'\n\nexport function useDropdownWithBadge<T>(props: DropdownWithBadgeProps<T>) {\n const hasBadge = computed(() => {\n return props.hideBadge!(props.modelValue)\n })\n\n return { hasBadge }\n}\n","<template>\n <dropdown-select\n v-bind=\"{ modelValue, options }\" class=\"dropdown-with-badge\"\n v-on=\"emit\"\n >\n <template #default=\"{ text }\">\n <slot v-bind=\"{ text }\">\n <view class=\"dropdown-title\">\n <text class=\"dropdown-text\">{{ text }}</text>\n <text class=\"dropdown-icon\">\n <slot name=\"icon\">\n <!-- 使用自定义图标类名 -->\n <view class=\"uni-icon\" :class=\"iconName\" />\n </slot>\n </text>\n </view>\n <text v-show=\"!hasBadge\" class=\"dropdown-with-badge__dot\" />\n </slot>\n </template>\n </dropdown-select>\n</template>\n\n<script lang=\"ts\" setup generic=\"T\">\nimport type { DropdownWithBadgeEmits, DropdownWithBadgeProps } from './type'\nimport DropdownSelect from '../DropdownSelect'\nimport { useDropdownWithBadge } from './dropdownWithBadge'\n\nconst props = withDefaults(defineProps<DropdownWithBadgeProps<T>>(), {\n hideBadge: (modelValue?: T) => (modelValue === '' || modelValue === null || modelValue === undefined),\n iconName: 'filter-o',\n})\nconst emit = defineEmits<DropdownWithBadgeEmits<T>>()\n\nconst { hasBadge } = useDropdownWithBadge(props)\n</script>\n\n<style lang=\"less\">\n@import './dropdownWithBadge.less';\n</style>\n","import type { ComputedRef, Ref } from 'vue'\nimport { computed, isRef } from 'vue'\n\n/**\n * 判断是否应显示徽章(如筛选条件已应用)\n * @param model 表单数据或者自定义方法\n * @returns 响应式的布尔值 Ref<boolean>\n */\nexport function useHasBadge<T extends object>(\n model?: Ref<T> | T | (() => boolean),\n): ComputedRef<boolean> {\n if (!model) {\n return computed(() => false)\n }\n\n if (typeof model === 'function') {\n return computed(() => model())\n }\n if (isRef(model)) {\n return computed(() => {\n if (!model.value)\n return false\n const obj = model.value as Record<keyof T, any>\n for (const key in obj) {\n if (obj[key]) {\n return true\n }\n }\n return false\n })\n }\n return computed(() => {\n for (const key in model) {\n const val = model[key as keyof typeof model]\n if (val) {\n return true\n }\n }\n return false\n })\n}\n","import type { FilterDrawerEmits, FilterDrawerProps } from './type'\nimport { ref } from 'vue'\nimport { useHasBadge } from './hasBadge'\n\nexport function useFilterDrawer<T extends object>(props: FilterDrawerProps<T>, emit: FilterDrawerEmits) {\n // 使用ref替代useToggle\n const visible = ref(false)\n\n // 徽章状态\n const hasBadge = useHasBadge(props.modelValue)\n\n const openDrawer = () => {\n visible.value = true\n }\n\n const closeDrawer = () => {\n visible.value = false\n }\n\n const resetDrawer = () => {\n emit('reset')\n closeDrawer()\n }\n\n const queryDrawer = () => {\n emit('query')\n closeDrawer()\n }\n\n return {\n visible,\n openDrawer,\n closeDrawer,\n resetDrawer,\n queryDrawer,\n hasBadge,\n }\n}\n","<template>\n <view class=\"filter-drawer__trigger\" @click=\"openDrawer\">\n <slot name=\"trigger\">\n <!-- 使用自定义图标 -->\n <view class=\"uni-icon icon-filter-o\" />\n </slot>\n <text v-if=\"hasBadge\" class=\"filter-drawer__badge-dot\" />\n </view>\n\n <!-- 使用 UniApp 内置的 popup 组件 -->\n <view v-if=\"visible\" class=\"filter-drawer__popup-mask\" @click=\"closeDrawer\" />\n <view v-if=\"visible\" class=\"filter-drawer__popup\" :style=\"{ height: '100vh', width: '100vw' }\">\n <view class=\"filter-drawer__close\" @click=\"closeDrawer\">\n ×\n </view>\n <view class=\"filter-drawer\">\n <view class=\"filter-drawer__header\">\n <slot name=\"header\">\n <text class=\"filter-drawer__title\">筛选</text>\n </slot>\n </view>\n <view class=\"filter-drawer__body\">\n <slot />\n </view>\n <view class=\"filter-drawer__footer\">\n <slot name=\"footer\">\n <view class=\"filter-drawer__button filter-drawer__button--plain\" @click=\"resetDrawer\">\n 重置条件\n </view>\n <view class=\"filter-drawer__button\" @click=\"queryDrawer\">\n 查询\n </view>\n </slot>\n </view>\n </view>\n </view>\n</template>\n\n<script setup lang=\"ts\" generic=\"T extends object\">\nimport type { FilterDrawerEmits, FilterDrawerProps } from './type'\nimport { useFilterDrawer } from './useFilterDrawer'\n\nconst props = withDefaults(defineProps<FilterDrawerProps<T>>(), {\n hasBadge: false,\n})\nconst emit = defineEmits<FilterDrawerEmits>()\n\nconst { visible, openDrawer, closeDrawer, resetDrawer, queryDrawer, hasBadge } = useFilterDrawer(props, emit)\n</script>\n\n<style lang=\"less\">\n@import './filterDrawer.less';\n</style>\n","<template>\n <view\n ref=\"scrollContainer\"\n class=\"sw-infinite-scroll\"\n :style=\"containerStyle\"\n @scroll=\"handleScroll\"\n >\n <!-- 内容区域 -->\n <view class=\"sw-infinite-scroll__content\">\n <slot />\n </view>\n\n <!-- 加载状态 -->\n <view v-if=\"loading\" class=\"sw-infinite-scroll__loading\">\n <view class=\"loading-spinner\" />\n <text>{{ loadingText }}</text>\n </view>\n\n <!-- 无更多数据 -->\n <view v-if=\"finished && !loading\" class=\"sw-infinite-scroll__finished\">\n {{ finishedText }}\n </view>\n </view>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\n// 定义组件属性\ninterface Props {\n // 是否正在加载\n loading?: boolean\n // 是否已加载完成\n finished?: boolean\n // 触发加载的距离阈值 (px)\n offset?: number\n // 加载提示文字\n loadingText?: string\n // 加载完成提示文字\n finishedText?: string\n // 是否禁用滚动加载\n disabled?: boolean\n // 容器高度\n height?: string | number\n}\n\n// 设置默认属性\nconst props = withDefaults(defineProps<Props>(), {\n loading: false,\n finished: false,\n offset: 100,\n loadingText: '加载中...',\n finishedText: '没有更多了',\n disabled: false,\n height: 'auto',\n})\n\n// 定义组件事件\nconst emit = defineEmits<{\n // 触发加载事件\n (e: 'load'): void\n}>()\n\n// 容器引用\nconst scrollContainer = ref<any>(null)\n\n// 容器样式\nconst containerStyle = computed(() => {\n return {\n height: typeof props.height === 'number' ? `${props.height}px` : props.height,\n }\n})\n\n// 处理滚动事件\nfunction handleScroll() {\n if (!scrollContainer.value || props.loading || props.finished || props.disabled) {\n return\n }\n\n const { scrollTop, scrollHeight, clientHeight } = scrollContainer.value\n\n // 当滚动到底部附近时触发加载\n if (scrollHeight - scrollTop - clientHeight <= props.offset) {\n emit('load')\n }\n}\n\n// 监听窗口大小变化\nlet resizeTimeout: number | null = null\n\nfunction handleResize() {\n if (resizeTimeout) {\n clearTimeout(resizeTimeout)\n }\n // 使用标准setTimeout确保跨平台兼容\n resizeTimeout = setTimeout(() => {\n handleScroll()\n }, 100) as unknown as number\n}\n\nonMounted(() => {\n // 初始化检查是否已滚动到底部\n handleScroll()\n\n // #ifdef H5\n // 只有H5平台支持window对象\n window.addEventListener('resize', handleResize)\n // #endif\n})\n\nonUnmounted(() => {\n // #ifdef H5\n // 只有H5平台支持window对象\n window.removeEventListener('resize', handleResize)\n // #endif\n if (resizeTimeout) {\n clearTimeout(resizeTimeout)\n }\n})\n</script>\n\n<style lang=\"less\" scoped>\n// 基础样式\n.sw-infinite-scroll {\n overflow-y: auto;\n position: relative;\n\n // 内容区域\n &__content {\n min-height: 100%;\n }\n\n // 加载状态\n &__loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 40rpx 0;\n color: #909399;\n font-size: 28rpx;\n\n .loading-spinner {\n width: 32rpx;\n height: 32rpx;\n border: 4rpx solid #d9d9d9;\n border-top-color: #409eff;\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n margin-right: 16rpx;\n }\n }\n\n // 无更多数据\n &__finished {\n text-align: center;\n padding: 40rpx 0;\n color: #909399;\n font-size: 28rpx;\n }\n}\n\n// 加载动画\n@keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n</style>\n","<template>\r\n <view\r\n v-show=\"showPopup\"\r\n class=\"popup-wrapper\"\r\n :class=\"{\r\n 'modal': mask,\r\n 'non-modal': !mask,\r\n 'closing': isClosing,\r\n }\"\r\n @click.self=\"handleMaskClick\"\r\n >\r\n <!-- 遮罩层 -->\r\n <view\r\n v-if=\"mask\"\r\n class=\"popup-mask\"\r\n :style=\"{ opacity: maskOpacity }\"\r\n @click=\"handleMaskClick\"\r\n />\r\n\r\n <!-- 弹窗主体 -->\r\n <view\r\n ref=\"popupRef\"\r\n class=\"popup-container\"\r\n :class=\"[\r\n `position-${position}`,\r\n `animation-${animation}`,\r\n { closing: isClosing },\r\n ]\"\r\n @click.stop\r\n @touchmove.stop\r\n @animationend=\"handleAnimationEnd\"\r\n >\r\n <!-- 自定义头部 -->\r\n <view v-if=\"$slots.header\" class=\"popup-header\">\r\n <slot name=\"header\" />\r\n </view>\r\n\r\n <!-- 自定义标题 -->\r\n <view v-else-if=\"title\" class=\"popup-header\">\r\n <text class=\"popup-title\">{{ title }}</text>\r\n </view>\r\n\r\n <!-- 内容区域 -->\r\n <view class=\"popup-content\">\r\n <slot />\r\n </view>\r\n\r\n <!-- 自定义底部 -->\r\n <view v-if=\"$slots.footer\" class=\"popup-footer\">\r\n <slot name=\"footer\" />\r\n </view>\r\n\r\n <!-- 默认底部按钮 -->\r\n <view v-else-if=\"showConfirmButton || showCancelButton\" class=\"popup-footer\">\r\n <view\r\n v-if=\"showCancelButton\"\r\n class=\"popup-button popup-button-cancel\"\r\n @click=\"handleCancel\"\r\n >\r\n {{ cancelText }}\r\n </view>\r\n <view\r\n v-if=\"showConfirmButton\"\r\n class=\"popup-button popup-button-confirm\"\r\n @click=\"handleConfirm\"\r\n >\r\n {{ confirmText }}\r\n </view>\r\n </view>\r\n\r\n <!-- 关闭按钮 -->\r\n <view\r\n v-if=\"showCloseButton\"\r\n class=\"popup-close-button\"\r\n @click=\"handleClose\"\r\n >\r\n <text class=\"close-icon\">×</text>\r\n </view>\r\n </view>\r\n </view>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { onMounted, onUnmounted, ref, watch } from 'vue'\r\n\r\n// Props\r\ninterface Props {\r\n // 控制弹窗显示/隐藏(支持v-model)\r\n modelValue?: boolean\r\n // 弹窗标题\r\n title?: string\r\n // 弹窗位置:center, top, bottom, left, right\r\n position?: 'center' | 'top' | 'bottom' | 'left' | 'right'\r\n // 动画效果:fade, scale, slide\r\n animation?: 'fade' | 'scale' | 'slide'\r\n // 是否显示遮罩层\r\n mask?: boolean\r\n // 遮罩层透明度\r\n maskOpacity?: number\r\n // 点击遮罩是否关闭弹窗\r\n closeOnMaskClick?: boolean\r\n // 是否显示关闭按钮\r\n showCloseButton?: boolean\r\n // 是否支持Esc键关闭\r\n closeOnEsc?: boolean\r\n // 是否显示确认按钮\r\n showConfirmButton?: boolean\r\n // 确认按钮文本\r\n confirmText?: string\r\n // 是否显示取消按钮\r\n showCancelButton?: boolean\r\n // 取消按钮文本\r\n cancelText?: string\r\n // 弹窗宽度(百分比或像素)\r\n width?: string | number\r\n // 弹窗高度(百分比或像素)\r\n height?: string | number\r\n // 弹窗最大宽度\r\n maxWidth?: string | number\r\n // 是否禁止背景滚动\r\n lockScroll?: boolean\r\n}\r\n\r\n// 设置默认属性\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: false,\r\n title: '',\r\n position: 'center',\r\n animation: 'slide',\r\n mask: true,\r\n maskOpacity: 0.5,\r\n closeOnMaskClick: true,\r\n showCloseButton: true,\r\n closeOnEsc: true,\r\n showConfirmButton: false,\r\n confirmText: '确定',\r\n showCancelButton: false,\r\n cancelText: '取消',\r\n width: '50%',\r\n height: '100%',\r\n maxWidth: '500px',\r\n lockScroll: true,\r\n})\r\n\r\n// Emits\r\nconst emit = defineEmits<{\r\n (e: 'update:modelValue', value: boolean): void\r\n (e: 'open'): void\r\n (e: 'close'): void\r\n (e: 'maskClick'): void\r\n (e: 'confirm'): void\r\n (e: 'cancel'): void\r\n (e: 'closeButtonClick'): void\r\n}>()\r\n\r\n// Refs\r\nconst popupRef = ref<any>(null)\r\nconst showPopup = ref(props.modelValue)\r\nconst isClosing = ref(false)\r\nconst lastScrollY = ref(0)\r\n\r\n// Watchers\r\n// 监听modelValue变化\r\nwatch(() => props.modelValue, (newVal) => {\r\n showPopup.value = newVal\r\n if (newVal) {\r\n onOpen()\r\n }\r\n else {\r\n onClose()\r\n }\r\n})\r\n\r\n// 监听showPopup变化\r\nwatch(() => showPopup.value, (newVal) => {\r\n // 同步到v-model\r\n emit('update:modelValue', newVal)\r\n if (newVal) {\r\n onOpen()\r\n }\r\n else {\r\n onClose()\r\n }\r\n})\r\n\r\n// Methods\r\n// 弹窗打开时\r\nfunction onOpen() {\r\n // 锁定滚动\r\n if (props.lockScroll) {\r\n lockScroll()\r\n }\r\n\r\n // 添加键盘事件监听\r\n if (props.closeOnEsc) {\r\n window.addEventListener('keydown', handleKeyDown)\r\n }\r\n\r\n // 触发打开事件\r\n emit('open')\r\n}\r\n\r\n// 弹窗关闭时\r\nfunction onClose() {\r\n // 解锁滚动\r\n if (props.lockScroll) {\r\n unlockScroll()\r\n }\r\n\r\n // 移除键盘事件监听\r\n window.removeEventListener('keydown', handleKeyDown)\r\n\r\n // 触发关闭事件\r\n emit('close')\r\n}\r\n\r\n// 锁定滚动\r\nfunction lockScroll() {\r\n // #ifdef H5\r\n lastScrollY.value = window.scrollY\r\n document.body.style.position = 'fixed'\r\n document.body.style.top = `-${lastScrollY.value}px`\r\n document.body.style.left = '0'\r\n document.body.style.right = '0'\r\n document.body.style.overflow = 'hidden'\r\n // #endif\r\n\r\n // #ifdef MP\r\n uni.hideTabBar()\r\n // #endif\r\n}\r\n\r\n// 解锁滚动\r\nfunction unlockScroll() {\r\n // #ifdef H5\r\n document.body.style.position = ''\r\n document.body.style.top = ''\r\n document.body.style.left = ''\r\n document.body.style.right = ''\r\n document.body.style.overflow = ''\r\n window.scrollTo(0, lastScrollY.value)\r\n // #endif\r\n\r\n // #ifdef MP\r\n uni.showTabBar()\r\n // #endif\r\n}\r\n\r\n// 键盘事件处理\r\nfunction handleKeyDown(e) {\r\n // #ifdef H5\r\n if (e.key === 'Escape' && props.closeOnEsc && showPopup.value) {\r\n handleClose()\r\n }\r\n // #endif\r\n}\r\n\r\n// 点击遮罩层\r\nfunction handleMaskClick() {\r\n if (props.closeOnMaskClick) {\r\n handleClose()\r\n }\r\n emit('maskClick')\r\n}\r\n\r\n// 点击关闭按钮\r\nfunction handleClose() {\r\n isClosing.value = true\r\n emit('closeButtonClick')\r\n}\r\n\r\n// 动画结束事件处理\r\nfunction handleAnimationEnd() {\r\n if (isClosing.value) {\r\n isClosing.value = false\r\n showPopup.value = false\r\n }\r\n}\r\n\r\n// 点击确认按钮\r\nfunction handleConfirm() {\r\n emit('confirm')\r\n}\r\n\r\n// 点击取消按钮\r\nfunction handleCancel() {\r\n emit('cancel')\r\n handleClose()\r\n}\r\n\r\n// 生命周期\r\nonMounted(() => {\r\n if (showPopup.value) {\r\n onOpen()\r\n }\r\n})\r\n\r\nonUnmounted(() => {\r\n if (showPopup.value) {\r\n onClose()\r\n }\r\n})\r\n</script>\r\n\r\n<style scoped>\r\n/* 弹窗容器 */\r\n.popup-wrapper {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n z-index: 1000;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n pointer-events: none;\r\n}\r\n\r\n/* 模态模式 */\r\n.popup-wrapper.modal {\r\n pointer-events: auto;\r\n}\r\n\r\n/* 非模态模式 */\r\n.popup-wrapper.non-modal {\r\n pointer-events: none;\r\n}\r\n\r\n.popup-wrapper.non-modal .popup-container {\r\n pointer-events: auto;\r\n}\r\n\r\n/* 遮罩层 */\r\n.popup-mask {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n transition: opacity 0.3s ease;\r\n}\r\n\r\n/* 弹窗主体 */\r\n.popup-container {\r\n position: fixed;\r\n background-color: white;\r\n border-radius: 16rpx;\r\n box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);\r\n width: var(--popup-width, 100%);\r\n max-width: var(--popup-max-width, 750rpx);\r\n max-height: 90vh;\r\n overflow: hidden;\r\n pointer-events: auto;\r\n}\r\n\r\n/* 头部 */\r\n.popup-header {\r\n padding: 32rpx 40rpx;\r\n border-bottom: 2rpx solid #ebeef5;\r\n}\r\n\r\n.popup-title {\r\n margin: 0;\r\n font-size: 32rpx;\r\n font-weight: 600;\r\n color: #303133;\r\n}\r\n\r\n/* 内容 */\r\n.popup-content {\r\n padding: 40rpx;\r\n max-height: calc(90vh - 240rpx);\r\n overflow-y: auto;\r\n}\r\n\r\n/* 底部 */\r\n.popup-footer {\r\n padding: 32rpx 40rpx;\r\n border-top: 2rpx solid #ebeef5;\r\n display: flex;\r\n justify-content: flex-end;\r\n gap: 24rpx;\r\n}\r\n\r\n/* 按钮 */\r\n.popup-button {\r\n padding: 16rpx 32rpx;\r\n border: none;\r\n border-radius: 8rpx;\r\n font-size: 28rpx;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n text-align: center;\r\n box-sizing: border-box;\r\n}\r\n\r\n.popup-button-cancel {\r\n background-color: #f5f7fa;\r\n color: #606266;\r\n}\r\n\r\n.popup-button-cancel:hover {\r\n background-color: #e4e7ed;\r\n}\r\n\r\n.popup-button-confirm {\r\n background-color: #409eff;\r\n color: white;\r\n}\r\n\r\n.popup-button-confirm:hover {\r\n background-color: #66b1ff;\r\n}\r\n\r\n/* 关闭按钮 */\r\n.popup-close-button {\r\n position: absolute;\r\n top: 24rpx;\r\n right: 24rpx;\r\n width: 48rpx;\r\n height: 48rpx;\r\n background-color: transparent;\r\n border-radius: 50%;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: #909399;\r\n transition: all 0.3s ease;\r\n}\r\n\r\n.popup-close-button:hover {\r\n background-color: #f5f7fa;\r\n color: #606266;\r\n}\r\n\r\n/* 关闭图标 */\r\n.close-icon {\r\n font-size: 40rpx;\r\n font-weight: bold;\r\n line-height: 1;\r\n}\r\n\r\n/* 位置样式 */\r\n/* 居中 */\r\n.position-center {\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n}\r\n\r\n/* 顶部 */\r\n.position-top {\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n border-radius: 0 0 16rpx 16rpx;\r\n}\r\n\r\n/* 底部 */\r\n.position-bottom {\r\n bottom: 0;\r\n left: 0;\r\n width: 100%;\r\n border-radius: 16rpx 16rpx 0 0;\r\n}\r\n\r\n/* 左侧 */\r\n.position-left {\r\n top: 0;\r\n left: 0;\r\n height: 100%;\r\n border-radius: 0 16rpx 16rpx 0;\r\n}\r\n\r\n/* 右侧 */\r\n.position-right {\r\n top: 0;\r\n right: 0;\r\n height: 100%;\r\n border-radius: 16rpx 0 0 16rpx;\r\n}\r\n\r\n/* 动画样式 */\r\n/* 淡入淡出 */\r\n.animation-fade {\r\n animation: fadeIn 0.3s ease;\r\n}\r\n\r\n@keyframes fadeIn {\r\n from {\r\n opacity: 0;\r\n }\r\n to {\r\n opacity: 1;\r\n }\r\n}\r\n\r\n/* 缩放 */\r\n.animation-scale {\r\n animation: scaleIn 0.3s ease;\r\n}\r\n\r\n@keyframes scaleIn {\r\n from {\r\n opacity: 0;\r\n transform: translate(-50%, -50%) scale(0.8);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translate(-50%, -50%) scale(1);\r\n }\r\n}\r\n\r\n/* 顶部位置的缩放动画 */\r\n.position-top.animation-scale {\r\n animation: scaleInTop 0.3s ease;\r\n}\r\n\r\n@keyframes scaleInTop {\r\n from {\r\n opacity: 0;\r\n transform: translateY(-100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n/* 底部位置的缩放动画 */\r\n.position-bottom.animation-scale {\r\n animation: scaleInBottom 0.3s ease;\r\n}\r\n\r\n@keyframes scaleInBottom {\r\n from {\r\n opacity: 0;\r\n transform: translateY(100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n/* 左侧位置的缩放动画 */\r\n.position-left.animation-scale {\r\n animation: scaleInLeft 0.3s ease;\r\n}\r\n\r\n@keyframes scaleInLeft {\r\n from {\r\n opacity: 0;\r\n transform: translateX(-100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n}\r\n\r\n/* 右侧位置的缩放动画 */\r\n.position-right.animation-scale {\r\n animation: scaleInRight 0.3s ease;\r\n}\r\n\r\n@keyframes scaleInRight {\r\n from {\r\n opacity: 0;\r\n transform: translateX(100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n}\r\n\r\n/* 滑动动画 */\r\n.animation-slide {\r\n animation: slideIn 0.3s ease;\r\n}\r\n\r\n@keyframes slideIn {\r\n from {\r\n opacity: 0;\r\n transform: translate(-50%, -100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translate(-50%, -50%);\r\n }\r\n}\r\n\r\n/* 顶部位置的滑动动画 */\r\n.position-top.animation-slide {\r\n animation: slideInTop 0.3s ease;\r\n}\r\n\r\n@keyframes slideInTop {\r\n from {\r\n opacity: 0;\r\n transform: translateY(-100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n/* 底部位置的滑动动画 */\r\n.position-bottom.animation-slide {\r\n animation: slideInBottom 0.3s ease;\r\n}\r\n\r\n@keyframes slideInBottom {\r\n from {\r\n opacity: 0;\r\n transform: translateY(100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n/* 左侧位置的滑动动画 */\r\n.position-left.animation-slide {\r\n animation: slideInLeft 0.3s ease;\r\n}\r\n\r\n@keyframes slideInLeft {\r\n from {\r\n opacity: 0;\r\n transform: translateX(-100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n}\r\n\r\n/* 右侧位置的滑动动画 */\r\n.position-right.animation-slide {\r\n animation: slideInRight 0.3s ease;\r\n}\r\n\r\n@keyframes slideInRight {\r\n from {\r\n opacity: 0;\r\n transform: translateX(100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n}\r\n\r\n/* 关闭动画 */\r\n/* 淡入淡出关闭动画 */\r\n.animation-fade.closing {\r\n animation: fadeOut 0.3s ease;\r\n}\r\n\r\n@keyframes fadeOut {\r\n from {\r\n opacity: 1;\r\n }\r\n to {\r\n opacity: 0;\r\n }\r\n}\r\n\r\n/* 缩放关闭动画 */\r\n.animation-scale.closing {\r\n animation: scaleOut 0.3s ease;\r\n}\r\n\r\n@keyframes scaleOut {\r\n from {\r\n opacity: 1;\r\n transform: translate(-50%, -50%) scale(1);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translate(-50%, -50%) scale(0.8);\r\n }\r\n}\r\n\r\n/* 顶部位置的缩放关闭动画 */\r\n.position-top.animation-scale.closing {\r\n animation: scaleOutTop 0.3s ease;\r\n}\r\n\r\n@keyframes scaleOutTop {\r\n from {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateY(-100%);\r\n }\r\n}\r\n\r\n/* 底部位置的缩放关闭动画 */\r\n.position-bottom.animation-scale.closing {\r\n animation: scaleOutBottom 0.3s ease;\r\n}\r\n\r\n@keyframes scaleOutBottom {\r\n from {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateY(100%);\r\n }\r\n}\r\n\r\n/* 左侧位置的缩放关闭动画 */\r\n.position-left.animation-scale.closing {\r\n animation: scaleOutLeft 0.3s ease;\r\n}\r\n\r\n@keyframes scaleOutLeft {\r\n from {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateX(-100%);\r\n }\r\n}\r\n\r\n/* 右侧位置的缩放关闭动画 */\r\n.position-right.animation-scale.closing {\r\n animation: scaleOutRight 0.3s ease;\r\n}\r\n\r\n@keyframes scaleOutRight {\r\n from {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateX(100%);\r\n }\r\n}\r\n\r\n/* 滑动关闭动画 */\r\n.animation-slide.closing {\r\n animation: slideOut 0.3s ease;\r\n}\r\n\r\n@keyframes slideOut {\r\n from {\r\n opacity: 1;\r\n transform: translate(-50%, -50%);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translate(-50%, -100%);\r\n }\r\n}\r\n\r\n/* 顶部位置的滑动关闭动画 */\r\n.position-top.animation-slide.closing {\r\n animation: slideOutTop 0.3s ease;\r\n}\r\n\r\n@keyframes slideOutTop {\r\n from {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateY(-100%);\r\n }\r\n}\r\n\r\n/* 底部位置的滑动关闭动画 */\r\n.position-bottom.animation-slide.closing {\r\n animation: slideOutBottom 0.3s ease;\r\n}\r\n\r\n@keyframes slideOutBottom {\r\n from {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateY(100%);\r\n }\r\n}\r\n\r\n/* 左侧位置的滑动关闭动画 */\r\n.position-left.animation-slide.closing {\r\n animation: slideOutLeft 0.3s ease;\r\n}\r\n\r\n@keyframes slideOutLeft {\r\n from {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateX(-100%);\r\n }\r\n}\r\n\r\n/* 右侧位置的滑动关闭动画 */\r\n.position-right.animation-slide.closing {\r\n animation: slideOutRight 0.3s ease;\r\n}\r\n\r\n@keyframes slideOutRight {\r\n from {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateX(100%);\r\n }\r\n}\r\n</style>\r\n","<template>\r\n <view ref=\"containerRef\" class=\"infinite-scroll-container\" @touchstart=\"handleTouchStart\" @touchmove=\"handleTouchMove\" @touchend=\"handleTouchEnd\" @scroll=\"handleScroll\">\r\n <!-- 下拉刷新指示器 -->\r\n <view\r\n class=\"refresh-indicator\"\r\n :style=\"{ height: `${pullDistance}px` }\"\r\n >\r\n <view v-if=\"refreshStatus !== 'normal' && (refreshStatus === 'refreshing' || pullDistance > 0)\" class=\"refresh-content\">\r\n <view class=\"refresh-icon\">\r\n <!-- 加载中图标 -->\r\n <text v-if=\"props.refreshLoading\" class=\"refresh-loading\">●</text>\r\n <!-- 下拉箭头图标 -->\r\n <text v-else class=\"refresh-arrow\">↓</text>\r\n </view>\r\n <text class=\"refresh-text\">\r\n <template v-if=\"props.refreshLoading\">{{ props.refreshingText }}</template>\r\n <template v-else-if=\"refreshStatus === 'triggered'\">{{ props.releaseText }}</template>\r\n <template v-else>{{ props.refreshText }}</template>\r\n </text>\r\n </view>\r\n </view>\r\n\r\n <!-- 内容区域 -->\r\n <view class=\"infinite-scroll-content\">\r\n <slot />\r\n </view>\r\n\r\n <!-- 加载指示器 -->\r\n <view class=\"infinite-scroll-indicator\">\r\n <!-- 加载中 -->\r\n <view v-if=\"loading\" class=\"infinite-scroll-loading\">\r\n <view class=\"loading-spinner\">\r\n <text class=\"loading-icon\">●</text>\r\n </view>\r\n <text class=\"loading-text\">{{ loadingText }}</text>\r\n </view>\r\n\r\n <!-- 加载失败 -->\r\n <view v-else-if=\"error\" class=\"infinite-scroll-error\">\r\n <text class=\"error-icon\">×</text>\r\n <text class=\"error-text\">{{ errorText }}</text>\r\n <view class=\"retry-btn\" @click=\"onRetry\">\r\n {{ retryText }}\r\n </view>\r\n </view>\r\n\r\n <!-- 无更多数据 -->\r\n <view v-else-if=\"!hasMore\" class=\"infinite-scroll-no-more\">\r\n <text class=\"no-more-text\">{{ noMoreText }}</text>\r\n </view>\r\n\r\n <!-- 隐藏的触发区域 -->\r\n <view\r\n v-else\r\n class=\"infinite-scroll-trigger\"\r\n :style=\"{ height: `0px` }\"\r\n />\r\n </view>\r\n </view>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'\r\n\r\n// Props\r\ninterface Props {\r\n // 加载状态(无限滚动)\r\n loading?: boolean\r\n // 下拉刷新加载状态\r\n refreshLoading?: boolean\r\n // 是否还有更多数据\r\n hasMore?: boolean\r\n // 加载失败状态\r\n error?: boolean\r\n // 触发无限滚动的阈值距离(像素)\r\n threshold?: number\r\n // 下拉刷新触发阈值(像素)\r\n refreshThreshold?: number\r\n // 节流时间(毫秒)\r\n throttleTime?: number\r\n // 加载中提示文本\r\n loadingText?: string\r\n // 加载失败提示文本\r\n errorText?: string\r\n // 重试按钮文本\r\n retryText?: string\r\n // 无更多数据提示文本\r\n noMoreText?: string\r\n // 下拉刷新提示文本\r\n refreshText?: string\r\n // 释放可刷新提示文本\r\n releaseText?: string\r\n // 正在刷新提示文本\r\n refreshingText?: string\r\n // 刷新成功提示文本\r\n refreshSuccessText?: string\r\n // 刷新失败提示文本\r\n refreshErrorText?: string\r\n // 是否使用窗口作为滚动容器\r\n useWindowScroll?: boolean\r\n // 是否启用无限滚动\r\n enableInfiniteScroll?: boolean\r\n // 是否启用下拉刷新\r\n enableRefresh?: boolean\r\n}\r\n\r\n// 设置默认属性\r\nconst props = withDefaults(defineProps<Props>(), {\r\n loading: false,\r\n refreshLoading: false,\r\n hasMore: true,\r\n error: false,\r\n threshold: 30,\r\n refreshThreshold: 40,\r\n throttleTime: 200,\r\n loadingText: '加载中...',\r\n errorText: '加载失败',\r\n retryText: '点击重试',\r\n noMoreText: '没有更多数据了',\r\n refreshText: '下拉刷新',\r\n releaseText: '释放可刷新',\r\n refreshingText: '正在刷新...',\r\n refreshSuccessText: '刷新成功',\r\n refreshErrorText: '刷新失败',\r\n useWindowScroll: false,\r\n enableInfiniteScroll: false,\r\n enableRefresh: true,\r\n})\r\n\r\n// Emits\r\nconst emit = defineEmits<{\r\n (e: 'loadMore'): void\r\n (e: 'retry'): void\r\n (e: 'refresh'): void\r\n}>()\r\n\r\n// Refs\r\nconst containerRef = ref<any>(null)\r\nlet scrollContainer: any = null\r\nlet lastScrollTime = 0\r\n\r\n// 下拉刷新相关状态\r\nconst pullDistance = ref(0) // 当前下拉距离\r\nconst startY = ref(0) // 触摸起始Y坐标\r\nconst isPulling = ref(false) // 是否正在下拉\r\nconst isRefreshing = ref(false) // 是否正在刷新\r\nconst refreshStatus = ref('normal') // 刷新状态:normal(正常)、pulling(下拉中)、triggered(已触发刷新)、refreshing(刷新中)\r\n\r\n// Methods\r\n/**\r\n * 获取滚动容器\r\n * @returns {any} - 滚动容器对象\r\n */\r\nfunction getScrollContainer() {\r\n if (props.useWindowScroll) {\r\n // #ifdef H5\r\n return window\r\n // #endif\r\n // 其他平台返回null或使用默认容器\r\n return null\r\n }\r\n return containerRef.value\r\n}\r\n\r\n/**\r\n * 节流函数,限制函数的执行频率\r\n * @param {Function} fn - 要执行的函数\r\n * @param {number} delay - 延迟时间(毫秒)\r\n * @returns {Function} - 节流后的函数\r\n */\r\nfunction throttle(fn, delay) {\r\n return (...args) => {\r\n const now = Date.now()\r\n if (now - lastScrollTime >= delay) {\r\n lastScrollTime = now\r\n fn(...args)\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查是否需要加载更多数据\r\n */\r\nfunction checkLoadMore() {\r\n // 如果无限滚动未启用,直接返回\r\n if (!props.enableInfiniteScroll) {\r\n return\r\n }\r\n\r\n if (props.loading || !props.hasMore || props.error) {\r\n return\r\n }\r\n\r\n let scrollTop = 0\r\n let scrollHeight = 0\r\n let clientHeight = 0\r\n\r\n if (props.useWindowScroll) {\r\n // #ifdef H5\r\n // 使用窗口作为滚动容器\r\n scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop\r\n scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight\r\n clientHeight = window.innerHeight\r\n // #endif\r\n }\r\n else {\r\n // 使用组件容器作为滚动容器\r\n if (!containerRef.value)\r\n return\r\n // 确保容器具有滚动相关属性\r\n if (typeof containerRef.value.scrollTop !== 'number'\r\n || typeof containerRef.value.scrollHeight !== 'number'\r\n || typeof containerRef.value.clientHeight !== 'number') {\r\n return\r\n }\r\n scrollTop = containerRef.value.scrollTop\r\n scrollHeight = containerRef.value.scrollHeight\r\n clientHeight = containerRef.value.clientHeight\r\n }\r\n\r\n // 检查是否滚动到了阈值范围内\r\n // 如果内容高度小于等于容器高度,说明内容不足一屏,应该直接触发加载更多\r\n if ((props.useWindowScroll ? scrollTop > 0 : true) && (scrollHeight <= clientHeight || scrollHeight - scrollTop - clientHeight <= props.threshold)) {\r\n // 触发加载更多事件\r\n emit('loadMore')\r\n }\r\n}\r\n\r\n/**\r\n * 触发重试加载事件\r\n */\r\nfunction onRetry() {\r\n emit('retry')\r\n}\r\n\r\n// 触摸开始事件处理\r\n/**\r\n * 处理触摸开始事件,用于初始化下拉刷新\r\n * @param {TouchEvent} e - 触摸事件对象\r\n */\r\nfunction handleTouchStart(e) {\r\n // 如果未启用下拉刷新或正在刷新,则不处理\r\n if (!props.enableRefresh || props.refreshLoading) {\r\n return\r\n }\r\n\r\n // 如果滚动容器不在顶部,则不处理下拉刷新\r\n let scrollTop = 0\r\n if (props.useWindowScroll) {\r\n // #ifdef H5\r\n scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop\r\n // #endif\r\n }\r\n else {\r\n scrollTop = containerRef.value?.scrollTop || 0\r\n }\r\n\r\n if (scrollTop > 0) {\r\n return\r\n }\r\n\r\n // 记录触摸起始位置\r\n startY.value = e.touches[0].clientY\r\n isPulling.value = true\r\n refreshStatus.value = 'pulling'\r\n}\r\n\r\n// 触摸移动事件处理\r\n/**\r\n * 处理触摸移动事件,用于计算下拉距离和更新刷新状态\r\n * @param {TouchEvent} e - 触摸事件对象\r\n */\r\nfunction handleTouchMove(e) {\r\n if (!isPulling.value || props.refreshLoading) {\r\n return\r\n }\r\n\r\n // 计算下拉距离\r\n const currentY = e.touches[0].clientY\r\n const distance = currentY - startY.value\r\n\r\n // 只有向下拉才处理\r\n if (distance <= 0) {\r\n // 如果拉到顶部以下,恢复正常状态\r\n if (pullDistance.value > 0) {\r\n pullDistance.value = 0\r\n refreshStatus.value = 'normal'\r\n }\r\n return\r\n }\r\n\r\n // 调整下拉距离计算,确保有足够空间实现垂直居中\r\n pullDistance.value = Math.min(distance * 0.5, props.refreshThreshold * 1.5)\r\n\r\n // 更新刷新状态\r\n if (pullDistance.value >= props.refreshThreshold) {\r\n refreshStatus.value = 'triggered'\r\n }\r\n else {\r\n refreshStatus.value = 'pulling'\r\n }\r\n\r\n // 阻止默认滚动行为\r\n e.preventDefault()\r\n}\r\n\r\n// 触摸结束事件处理\r\n/**\r\n * 处理触摸结束事件,用于判断是否触发刷新\r\n */\r\nfunction handleTouchEnd() {\r\n if (!isPulling.value) {\r\n return\r\n }\r\n\r\n isPulling.value = false\r\n\r\n // 如果下拉距离达到阈值,触发刷新\r\n if (pullDistance.value >= props.refreshThreshold && !props.refreshLoading) {\r\n isRefreshing.value = true\r\n refreshStatus.value = 'refreshing'\r\n emit('refresh')\r\n }\r\n else {\r\n // 否则恢复原状\r\n pullDistance.value = 0\r\n refreshStatus.value = 'normal'\r\n }\r\n}\r\n\r\n/**\r\n * 处理滚动事件,检查是否需要加载更多(节流处理)\r\n */\r\nconst handleScroll = throttle(() => {\r\n checkLoadMore()\r\n}, props.throttleTime)\r\n\r\n// 添加滚动事件监听\r\n/**\r\n * 添加滚动事件监听\r\n */\r\nfunction addScrollListener() {\r\n // 使用nextTick确保containerRef已初始化\r\n nextTick(() => {\r\n scrollContainer = getScrollContainer()\r\n if (scrollContainer && typeof scrollContainer.addEventListener === 'function') {\r\n scrollContainer.addEventListener('scroll', handleScroll, { passive: true })\r\n // 初始检查一次\r\n checkLoadMore()\r\n }\r\n })\r\n}\r\n\r\n// 移除滚动事件监听\r\n/**\r\n * 移除滚动事件监听\r\n */\r\nfunction removeScrollListener() {\r\n if (scrollContainer && typeof scrollContainer.removeEventListener === 'function') {\r\n scrollContainer.removeEventListener('scroll', handleScroll)\r\n scrollContainer = null\r\n }\r\n}\r\n\r\n// 监听容器大小变化\r\n/**\r\n * 处理容器大小变化事件,检查是否需要加载更多(节流处理)\r\n */\r\nconst handleResize = throttle(() => {\r\n checkLoadMore()\r\n}, props.throttleTime)\r\n\r\n// 监听加载状态变化\r\nwatch(() => props.loading, (newVal) => {\r\n if (!newVal) {\r\n // 加载完成后,检查是否还需要继续加载\r\n // 使用nextTick确保DOM已更新\r\n nextTick(() => {\r\n checkLoadMore()\r\n })\r\n }\r\n})\r\n\r\n// 监听错误状态变化\r\nwatch(() => props.error, (newVal) => {\r\n if (!newVal) {\r\n // 错误状态清除后,检查是否还需要继续加载\r\n // 使用nextTick确保DOM已更新\r\n nextTick(() => {\r\n checkLoadMore()\r\n })\r\n }\r\n})\r\n\r\n// 监听下拉刷新加载状态变化\r\nwatch(() => props.refreshLoading, (newVal) => {\r\n isRefreshing.value = newVal\r\n\r\n if (newVal) {\r\n refreshStatus.value = 'refreshing'\r\n }\r\n else {\r\n // 刷新完成后,先重置pullDistance开始回弹\r\n pullDistance.value = 0\r\n // 保持refreshStatus为'refreshing'直到回弹动画完成\r\n // 然后再将状态设置为'normal'\r\n // 使用标准setTimeout确保跨平台兼容\r\n setTimeout(() => {\r\n refreshStatus.value = 'normal'\r\n }, 300)\r\n }\r\n})\r\n\r\n// 生命周期\r\nonMounted(() => {\r\n // 添加滚动事件监听\r\n addScrollListener()\r\n\r\n // 添加窗口大小变化监听\r\n // #ifdef H5\r\n window.addEventListener('resize', handleResize, { passive: true })\r\n // #endif\r\n})\r\n\r\nonUnmounted(() => {\r\n // 移除滚动事件监听\r\n removeScrollListener()\r\n\r\n // 移除窗口大小变化监听\r\n // #ifdef H5\r\n window.removeEventListener('resize', handleResize)\r\n // #endif\r\n})\r\n</script>\r\n\r\n<style scoped>\r\n.infinite-scroll-container {\r\n position: relative;\r\n width: 100%;\r\n height: 100%;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n}\r\n\r\n.infinite-scroll-content {\r\n width: 100%;\r\n}\r\n\r\n.infinite-scroll-indicator {\r\n width: 100%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n.infinite-scroll-loading {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 20rpx;\r\n color: #666;\r\n font-size: 28rpx;\r\n}\r\n\r\n.loading-spinner {\r\n animation: spin 0.8s linear infinite;\r\n color: #409eff;\r\n}\r\n\r\n.loading-icon {\r\n font-size: 40rpx;\r\n}\r\n\r\n@keyframes spin {\r\n 0% {\r\n transform: rotate(0deg);\r\n }\r\n 100% {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n.loading-text {\r\n font-size: 28rpx;\r\n color: #666;\r\n}\r\n\r\n.infinite-scroll-error {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 20rpx;\r\n color: #f56c6c;\r\n font-size: 28rpx;\r\n}\r\n\r\n.error-icon {\r\n font-size: 40rpx;\r\n color: #f56c6c;\r\n}\r\n\r\n.error-text {\r\n font-size: 28rpx;\r\n color: #f56c6c;\r\n}\r\n\r\n.retry-btn {\r\n padding: 8rpx 24rpx;\r\n background-color: #409eff;\r\n color: white;\r\n border: none;\r\n border-radius: 8rpx;\r\n font-size: 24rpx;\r\n cursor: pointer;\r\n transition: background-color 0.3s ease;\r\n box-sizing: border-box;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.retry-btn:hover {\r\n background-color: #66b1ff;\r\n}\r\n\r\n.infinite-scroll-no-more {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: #999;\r\n font-size: 28rpx;\r\n}\r\n\r\n.no-more-text {\r\n font-size: 28rpx;\r\n color: #999;\r\n}\r\n\r\n.infinite-scroll-trigger {\r\n width: 100%;\r\n}\r\n\r\n/* 下拉刷新样式 */\r\n.refresh-indicator {\r\n width: 100%;\r\n height: 0;\r\n overflow: hidden;\r\n transition: height 0.3s ease;\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.refresh-content {\r\n display: flex;\r\n flex-direction: row;\r\n align-items: center;\r\n justify-content: center;\r\n width: 100%;\r\n padding: 16rpx 0;\r\n color: white;\r\n font-size: 26rpx;\r\n align-self: center;\r\n flex-shrink: 0;\r\n margin-top: 10rpx;\r\n font-weight: 500;\r\n text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.3);\r\n border-radius: 20rpx;\r\n margin: 8rpx;\r\n backdrop-filter: blur(10rpx);\r\n}\r\n\r\n.refresh-icon {\r\n margin-right: 10rpx;\r\n margin-bottom: 0;\r\n}\r\n\r\n.refresh-icon svg {\r\n transition: transform 0.3s ease;\r\n color: #409eff;\r\n width: 36rpx;\r\n height: 36rpx;\r\n}\r\n\r\n.refresh-status-triggered .refresh-icon svg,\r\n.refresh-status-refreshing .refresh-icon svg {\r\n transform: rotate(0deg);\r\n}\r\n\r\n.refresh-status-refreshing .refresh-icon svg {\r\n animation: spin 0.8s linear infinite;\r\n}\r\n\r\n.refresh-text {\r\n font-size: 28rpx;\r\n color: white;\r\n}\r\n</style>\r\n","<template>\r\n <view class=\"sw-swiper\">\r\n <view\r\n class=\"swiper-wrapper\"\r\n :style=\"{\r\n transform: `translateX(-${currentIndex * 100}%)`,\r\n transition: `transform ${duration}ms ease`,\r\n }\"\r\n >\r\n <view\r\n v-for=\"(item, index) in items\"\r\n :key=\"index\"\r\n class=\"swiper-item\"\r\n >\r\n <slot :item=\"item\" :index=\"index\" />\r\n </view>\r\n </view>\r\n\r\n <!-- 指示器 -->\r\n <view v-if=\"showIndicators\" class=\"swiper-indicators\">\r\n <view\r\n v-for=\"(item, index) in items\"\r\n :key=\"index\"\r\n class=\"indicator-item\"\r\n :class=\"{ active: index === currentIndex }\"\r\n @click=\"currentIndex = index\"\r\n />\r\n </view>\r\n\r\n <!-- 左右箭头 -->\r\n <view\r\n v-if=\"showArrows\"\r\n class=\"swiper-arrow swiper-arrow-left\"\r\n @click=\"prev\"\r\n />\r\n <view\r\n v-if=\"showArrows\"\r\n class=\"swiper-arrow swiper-arrow-right\"\r\n @click=\"next\"\r\n />\r\n </view>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { onMounted, onUnmounted, ref, watch } from 'vue'\r\n\r\ninterface Props {\r\n items: any[]\r\n initialIndex?: number\r\n autoPlay?: boolean\r\n interval?: number\r\n duration?: number\r\n showIndicators?: boolean\r\n showArrows?: boolean\r\n loop?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n initialIndex: 0,\r\n autoPlay: true,\r\n interval: 3000,\r\n duration: 300,\r\n showIndicators: true,\r\n showArrows: false,\r\n loop: true,\r\n})\r\n\r\nconst emit = defineEmits<{\r\n (e: 'change', index: number): void\r\n (e: 'prev', index: number): void\r\n (e: 'next', index: number): void\r\n}>()\r\n\r\nconst currentIndex = ref(props.initialIndex)\r\nlet timer: any = null\r\n\r\n// 自动播放\r\nfunction startAutoPlay() {\r\n if (props.autoPlay && props.items.length > 1) {\r\n timer = setInterval(() => {\r\n next()\r\n }, props.interval)\r\n }\r\n}\r\n\r\nfunction stopAutoPlay() {\r\n if (timer) {\r\n clearInterval(timer)\r\n timer = null\r\n }\r\n}\r\n\r\n// 上一张\r\nfunction prev() {\r\n if (props.loop) {\r\n currentIndex.value = currentIndex.value === 0 ? props.items.length - 1 : currentIndex.value - 1\r\n }\r\n else if (currentIndex.value > 0) {\r\n currentIndex.value--\r\n }\r\n emit('prev', currentIndex.value)\r\n emit('change', currentIndex.value)\r\n}\r\n\r\n// 下一张\r\nfunction next() {\r\n if (props.loop) {\r\n currentIndex.value = currentIndex.value === props.items.length - 1 ? 0 : currentIndex.value + 1\r\n }\r\n else if (currentIndex.value < props.items.length - 1) {\r\n currentIndex.value++\r\n }\r\n emit('next', currentIndex.value)\r\n emit('change', currentIndex.value)\r\n}\r\n\r\n// 监听items变化\r\nwatch(() => props.items, (newItems) => {\r\n if (newItems.length < currentIndex.value + 1) {\r\n currentIndex.value = Math.min(currentIndex.value, newItems.length - 1)\r\n }\r\n stopAutoPlay()\r\n startAutoPlay()\r\n}, { deep: true })\r\n\r\n// 监听自动播放设置\r\nwatch(() => props.autoPlay, (autoPlay) => {\r\n if (autoPlay) {\r\n startAutoPlay()\r\n }\r\n else {\r\n stopAutoPlay()\r\n }\r\n})\r\n\r\nonMounted(() => {\r\n startAutoPlay()\r\n})\r\n\r\nonUnmounted(() => {\r\n stopAutoPlay()\r\n})\r\n\r\n// 暴露方法\r\ndefineExpose({\r\n prev,\r\n next,\r\n currentIndex,\r\n})\r\n</script>\r\n\r\n<style lang=\"less\" scoped>\r\n// 轮播图组件样式\r\n.sw-swiper {\r\n position: relative;\r\n width: 100%;\r\n overflow: hidden;\r\n\r\n .swiper-wrapper {\r\n display: flex;\r\n width: 100%;\r\n height: 100%;\r\n }\r\n\r\n .swiper-item {\r\n flex-shrink: 0;\r\n width: 100%;\r\n height: 100%;\r\n }\r\n\r\n // 指示器\r\n .swiper-indicators {\r\n position: absolute;\r\n bottom: 32rpx;\r\n left: 50%;\r\n transform: translateX(-50%);\r\n display: flex;\r\n gap: 16rpx;\r\n\r\n .indicator-item {\r\n width: 16rpx;\r\n height: 16rpx;\r\n border-radius: 50%;\r\n background-color: rgba(255, 255, 255, 0.5);\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n\r\n &.active {\r\n width: 16rpx;\r\n height: 16rpx;\r\n background-color: #fff;\r\n }\r\n }\r\n }\r\n\r\n // 箭头\r\n .swiper-arrow {\r\n position: absolute;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n width: 76rpx;\r\n height: 76rpx;\r\n border-radius: 50%;\r\n background-color: rgba(255, 255, 255, 0.85);\r\n border: none;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n z-index: 10;\r\n transition: all 0.3s ease;\r\n box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.15);\r\n\r\n &:hover {\r\n background-color: rgba(255, 255, 255, 1);\r\n transform: translateY(-50%) scale(1.1);\r\n }\r\n\r\n &:before {\r\n content: '';\r\n width: 24rpx;\r\n height: 24rpx;\r\n border-top: 4rpx solid #333;\r\n border-left: 4rpx solid #333;\r\n display: inline-block;\r\n }\r\n\r\n &.swiper-arrow-left {\r\n left: 30rpx;\r\n\r\n &:before {\r\n transform: rotate(-45deg);\r\n }\r\n }\r\n\r\n &.swiper-arrow-right {\r\n right: 30rpx;\r\n\r\n &:before {\r\n transform: rotate(135deg);\r\n }\r\n }\r\n }\r\n}\r\n</style>\r\n","<template>\r\n <view\r\n v-show=\"showToast\"\r\n ref=\"toastRef\"\r\n class=\"sw-toast\"\r\n :class=\"[\r\n `sw-toast--${type}`,\r\n `sw-toast--${position}`,\r\n { 'sw-toast--large': large },\r\n { 'sw-toast--text': !showIcon && type === 'text' },\r\n { 'sw-toast--show': showToast && !isClosing },\r\n { 'sw-toast--hide': showToast && isClosing },\r\n ]\"\r\n :style=\"customStyle\"\r\n @click=\"onClick\"\r\n @transitionend=\"handleTransitionEnd\"\r\n >\r\n <!-- 加载图标 -->\r\n <view v-if=\"loading\" class=\"sw-toast__loading\">\r\n <text v-if=\"!customIcon\" class=\"sw-toast__spinner\" />\r\n <text v-else class=\"sw-toast__custom-icon\">{{ customIcon }}</text>\r\n </view>\r\n\r\n <!-- 状态图标 -->\r\n <view v-else-if=\"showIcon\" class=\"sw-toast__icon\">\r\n <text v-if=\"type === 'success'\" class=\"sw-toast__icon--success iconfont icon-zhengqueshixin\" />\r\n <text v-else-if=\"type === 'fail'\" class=\"sw-toast__icon--fail iconfont icon-shanchu\" />\r\n <text v-else-if=\"type === 'warning'\" class=\"sw-toast__icon--warning iconfont icon-jurassic_warn\" />\r\n <text v-else-if=\"customIcon\" class=\"sw-toast__custom-icon\">{{ customIcon }}</text>\r\n </view>\r\n\r\n <!-- 文本内容 -->\r\n <text v-if=\"displayMessage\" class=\"sw-toast__text\" :class=\"{ 'sw-toast__text--long': longText }\">{{ displayMessage }}</text>\r\n </view>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\r\n\r\n// Props\r\ninterface Props {\r\n /**\r\n * 控制显示/隐藏(支持v-model)\r\n */\r\n modelValue?: boolean\r\n /**\r\n * 提示类型\r\n */\r\n type?: 'text' | 'loading' | 'success' | 'fail' | 'warning'\r\n /**\r\n * 显示位置\r\n */\r\n position?: 'top' | 'middle' | 'bottom'\r\n /**\r\n * 提示文字\r\n */\r\n message?: string\r\n /**\r\n * 自动关闭时间(毫秒),0表示不会自动关闭\r\n */\r\n duration?: number\r\n /**\r\n * 是否显示加载图标\r\n */\r\n loading?: boolean\r\n /**\r\n * 是否显示图标\r\n */\r\n icon?: boolean\r\n /**\r\n * 自定义图标\r\n */\r\n customIcon?: string\r\n /**\r\n * 是否为大尺寸\r\n */\r\n large?: boolean\r\n /**\r\n * 自定义样式\r\n */\r\n customStyle?: Record<string, any>\r\n /**\r\n * 是否开启倒计时功能\r\n */\r\n countdown?: boolean\r\n /**\r\n * 倒计时总时间(秒)\r\n */\r\n countdownSeconds?: number\r\n /**\r\n * 是否在倒计时数字外添加括号\r\n */\r\n countdownParentheses?: boolean\r\n /**\r\n * 倒计时后追加的文字描述\r\n */\r\n countdownSuffix?: string\r\n /**\r\n * 是否开启长文本模式(允许换行)\r\n */\r\n longText?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: false,\r\n type: 'text',\r\n position: 'middle',\r\n message: '',\r\n duration: 3000,\r\n loading: false,\r\n icon: true,\r\n customIcon: '',\r\n large: false,\r\n customStyle: () => ({}),\r\n countdown: false,\r\n countdownSeconds: 5,\r\n countdownParentheses: false,\r\n countdownSuffix: '',\r\n longText: false,\r\n})\r\n\r\n// Emits\r\nconst emit = defineEmits<{\r\n /**\r\n * 显示状态变化时触发\r\n */\r\n (e: 'update:modelValue', value: boolean): void\r\n /**\r\n * 关闭时触发\r\n */\r\n (e: 'close'): void\r\n /**\r\n * 点击时触发\r\n */\r\n (e: 'click'): void\r\n}>()\r\n\r\n// Refs\r\nconst toastRef = ref<any>(null)\r\nlet timer: number | null | ReturnType<typeof setTimeout> = null\r\nlet countdownTimer: number | null | ReturnType<typeof setInterval> = null\r\n\r\n// 内部状态\r\nconst showToast = ref(props.modelValue)\r\nconst isClosing = ref(false)\r\nconst currentCountdown = ref(props.countdownSeconds)\r\nconst originalMessage = ref(props.message)\r\n\r\n// 计算是否需要显示图标\r\nconst showIcon = computed(() => {\r\n if (props.loading)\r\n return true\r\n if (props.type === 'text')\r\n return props.icon\r\n return true\r\n})\r\n\r\n// 计算实际显示的消息(包含倒计时)\r\nconst displayMessage = computed(() => {\r\n if (props.countdown && showToast.value) {\r\n // 构建倒计时部分\r\n let countdownPart = ''\r\n if (props.countdownParentheses) {\r\n // 带括号的倒计时格式:(10秒后返回首页)\r\n countdownPart = `(${currentCountdown.value}秒${props.countdownSuffix})`\r\n }\r\n else {\r\n // 不带括号的倒计时格式:10秒后返回首页\r\n countdownPart = `${currentCountdown.value}秒${props.countdownSuffix}`\r\n }\r\n return `${originalMessage.value || ''} ${countdownPart}`\r\n }\r\n return props.message\r\n})\r\n\r\n// 点击事件处理\r\nfunction onClick() {\r\n emit('click')\r\n}\r\n\r\n// 关闭Toast\r\nfunction close() {\r\n if (!showToast.value)\r\n return\r\n\r\n // 清除倒计时定时器\r\n clearCountdownTimer()\r\n // 添加关闭动画\r\n isClosing.value = true\r\n emit('close')\r\n}\r\n\r\n// 自动关闭定时器\r\nfunction startTimer() {\r\n // 清除之前的定时器\r\n clearTimer()\r\n\r\n // 计算实际使用的duration:如果开启倒计时且countdownSeconds>0,则使用countdownSeconds*1000,否则使用props.duration\r\n const actualDuration = props.countdown && props.countdownSeconds > 0 ? props.countdownSeconds * 1000 : props.duration\r\n\r\n if (actualDuration > 0 && !props.loading) {\r\n timer = setTimeout(() => {\r\n close()\r\n }, actualDuration)\r\n }\r\n}\r\n\r\n// 清除定时器\r\nfunction clearTimer() {\r\n if (timer) {\r\n clearTimeout(timer)\r\n timer = null\r\n }\r\n}\r\n\r\n// 开始倒计时\r\nfunction startCountdown() {\r\n // 清除之前的倒计时定时器\r\n clearCountdownTimer()\r\n\r\n if (props.countdown && props.countdownSeconds > 0) {\r\n currentCountdown.value = props.countdownSeconds\r\n originalMessage.value = props.message\r\n\r\n countdownTimer = setInterval(() => {\r\n currentCountdown.value--\r\n if (currentCountdown.value <= 0) {\r\n clearCountdownTimer()\r\n // 如果开启了自动关闭,倒计时结束后关闭Toast\r\n if (props.duration > 0) {\r\n close()\r\n }\r\n }\r\n }, 1000)\r\n }\r\n}\r\n\r\n// 清除倒计时定时器\r\nfunction clearCountdownTimer() {\r\n if (countdownTimer) {\r\n clearInterval(countdownTimer)\r\n countdownTimer = null\r\n }\r\n // 重置当前倒计时\r\n currentCountdown.value = props.countdownSeconds\r\n}\r\n\r\n// 过渡结束处理\r\nfunction handleTransitionEnd() {\r\n if (isClosing.value) {\r\n showToast.value = false\r\n isClosing.value = false\r\n emit('update:modelValue', false)\r\n }\r\n}\r\n\r\n// 监听modelValue变化\r\nwatch(() => props.modelValue, (newVal) => {\r\n if (newVal) {\r\n // 显示Toast\r\n showToast.value = true\r\n isClosing.value = false\r\n startTimer()\r\n // 如果开启了倒计时,开始倒计时\r\n if (props.countdown) {\r\n startCountdown()\r\n }\r\n }\r\n else {\r\n // 隐藏Toast\r\n close()\r\n }\r\n})\r\n\r\n// 监听message变化,重新启动定时器\r\nwatch(() => props.message, () => {\r\n if (showToast.value && !props.loading) {\r\n startTimer()\r\n // 如果开启了倒计时,更新原始消息\r\n if (props.countdown) {\r\n originalMessage.value = props.message\r\n }\r\n }\r\n})\r\n\r\n// 监听countdown或countdownSeconds变化,重新开始倒计时\r\nwatch([() => props.countdown, () => props.countdownSeconds], () => {\r\n if (showToast.value && props.countdown) {\r\n startCountdown()\r\n }\r\n else {\r\n clearCountdownTimer()\r\n }\r\n})\r\n\r\n// 生命周期\r\nonMounted(() => {\r\n if (props.modelValue) {\r\n startTimer()\r\n // 如果开启了倒计时,开始倒计时\r\n if (props.countdown) {\r\n startCountdown()\r\n }\r\n }\r\n})\r\n\r\nonUnmounted(() => {\r\n clearTimer()\r\n clearCountdownTimer()\r\n})\r\n\r\n// 暴露方法\r\ndefineExpose({\r\n /**\r\n * 手动关闭Toast\r\n */\r\n close,\r\n})\r\n</script>\r\n\r\n<style scoped>\r\n.sw-toast {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 24rpx 40rpx;\r\n border-radius: 12rpx;\r\n font-size: 28rpx;\r\n line-height: 80rpx;\r\n color: #fff;\r\n background-color: rgba(0, 0, 0, 0.7);\r\n box-sizing: border-box;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n z-index: 9999;\r\n position: fixed;\r\n left: 50%;\r\n transform: translateX(-50%) translateY(0);\r\n user-select: none;\r\n opacity: 0;\r\n visibility: hidden;\r\n pointer-events: none;\r\n border: 2rpx solid rgba(255, 255, 255, 0.2);\r\n}\r\n\r\n/* 显示动画 */\r\n.sw-toast--show {\r\n opacity: 1;\r\n visibility: visible;\r\n pointer-events: auto;\r\n transform: translateX(-50%) translateY(0);\r\n}\r\n\r\n/* 隐藏动画 */\r\n.sw-toast--hide {\r\n opacity: 0;\r\n transform: translateX(-50%) translateY(-20rpx);\r\n}\r\n\r\n/* 位置样式 */\r\n.sw-toast--top {\r\n top: 100rpx;\r\n}\r\n\r\n.sw-toast--middle {\r\n top: 50%;\r\n transform: translate(-50%, -50%);\r\n}\r\n\r\n.sw-toast--middle.sw-toast--show {\r\n transform: translate(-50%, -50%);\r\n}\r\n\r\n.sw-toast--middle.sw-toast--hide {\r\n transform: translate(-50%, -60%);\r\n}\r\n\r\n.sw-toast--bottom {\r\n bottom: 100rpx;\r\n}\r\n\r\n/* 类型样式 */\r\n.sw-toast--text {\r\n background-color: rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n.sw-toast--loading {\r\n background-color: rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n.iconfont {\r\n font-size: 40rpx;\r\n vertical-align: middle;\r\n}\r\n.sw-toast--success {\r\n background-color: rgba(240, 249, 235, 1);\r\n height: 80rpx;\r\n border-width: 0rpx;\r\n border-radius: 12rpx;\r\n border-width: 2rpx;\r\n border-style: solid;\r\n box-sizing: border-box;\r\n text-align: left;\r\n font-size: 28rpx;\r\n color: #67c23a;\r\n border-color: rgba(227, 244, 218, 1);\r\n}\r\n\r\n.sw-toast--fail {\r\n background-color: #f5222d;\r\n border-width: 0rpx;\r\n background-color: rgba(254, 240, 240, 1);\r\n box-sizing: border-box;\r\n border-width: 2rpx;\r\n border-style: solid;\r\n border-color: rgba(253, 226, 226, 1);\r\n border-radius: 12rpx;\r\n height: 80rpx;\r\n display: flex;\r\n text-align: left;\r\n font-size: 28rpx;\r\n color: #f56c6c;\r\n}\r\n\r\n.sw-toast--warning {\r\n background-color: rgba(253, 246, 236, 1);\r\n height: 80rpx;\r\n border-width: 0rpx;\r\n border-radius: 12rpx;\r\n border-width: 2rpx;\r\n border-style: solid;\r\n box-sizing: border-box;\r\n text-align: left;\r\n font-size: 28rpx;\r\n color: #e6a23c;\r\n border-color: rgba(255, 236, 208, 1);\r\n}\r\n\r\n/* 大尺寸样式 */\r\n.sw-toast--large {\r\n padding: 32rpx 48rpx;\r\n font-size: 32rpx;\r\n line-height: 48rpx;\r\n}\r\n\r\n/* 加载图标 */\r\n.sw-toast__loading {\r\n margin-right: 16rpx;\r\n}\r\n\r\n/* 加载动画 */\r\n.sw-toast__spinner {\r\n display: inline-block;\r\n width: 40rpx;\r\n height: 40rpx;\r\n border: 4rpx solid rgba(255, 255, 255, 0.3);\r\n border-radius: 50%;\r\n border-top-color: #fff;\r\n animation: sw-toast-spin 0.8s linear infinite;\r\n}\r\n\r\n/* 自定义图标 */\r\n.sw-toast__custom-icon {\r\n font-size: 40rpx;\r\n}\r\n\r\n/* 状态图标 */\r\n.sw-toast__icon {\r\n margin-right: 16rpx;\r\n\r\n &--success,\r\n &--fail,\r\n &--warning {\r\n display: inline-block;\r\n width: 40rpx;\r\n height: 40rpx;\r\n font-size: 32rpx;\r\n font-weight: bold;\r\n text-align: center;\r\n line-height: 40rpx;\r\n }\r\n}\r\n\r\n/* 文本内容 */\r\n.sw-toast__text {\r\n margin: 0;\r\n white-space: nowrap;\r\n width: auto;\r\n}\r\n\r\n/* 长文本模式样式 */\r\n.sw-toast__text--long {\r\n word-wrap: break-word;\r\n overflow-wrap: break-word;\r\n white-space: pre-wrap;\r\n width: 100%;\r\n word-break: break-all;\r\n}\r\n\r\n/* 加载动画 */\r\n@keyframes sw-toast-spin {\r\n to {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n/* 响应式设计 */\r\n.sw-toast {\r\n max-width: 100%;\r\n padding: 16rpx 24rpx;\r\n font-size: 26rpx;\r\n line-height: 36rpx;\r\n}\r\n\r\n.sw-toast--large {\r\n padding: 24rpx 40rpx;\r\n font-size: 30rpx;\r\n line-height: 44rpx;\r\n}\r\n\r\n.sw-toast--top {\r\n top: 60rpx;\r\n}\r\n\r\n.sw-toast--bottom {\r\n bottom: 60rpx;\r\n}\r\n</style>\r\n","// 统一导出所有 MyComs 组件\r\n\r\n// 基础组件\r\nimport Button from './Button/index.vue'\r\nimport Dialog from './Dialog/index.vue'\r\nimport Divider from './Divider/index.vue'\r\n// 导出组合式函数\r\nimport { useDropdownSelect } from './DropdownSelect/dropdownSelect'\r\n// 表单组件\r\nimport DropdownSelect from './DropdownSelect/index.vue'\r\nimport { useDropdownWithBadge } from './DropdownWithBadge/dropdownWithBadge'\r\n\r\nimport DropdownWithBadge from './DropdownWithBadge/index.vue'\r\n// 筛选组件\r\nimport FilterDrawer from './FilterDrawer/index.vue'\r\n\r\nimport { useFilterDrawer } from './FilterDrawer/useFilterDrawer'\r\n// 交互组件\r\nimport InfiniteScroll from './InfiniteScroll/index.vue'\r\nimport Popup from './Popup/index.vue'\r\n\r\nimport PullRefresh from './PullRefresh/index.vue'\r\nimport Swiper from './Swiper/index.vue'\r\nimport Toast from './Toast/index.vue'\r\n\r\nexport type { DropdownSelectEmits, DropdownSelectProps } from './DropdownSelect/type'\r\n\r\n// 导出所有类型\r\nexport type * from './DropdownSelect/type'\r\nexport type { DropdownWithBadgeEmits, DropdownWithBadgeProps } from './DropdownWithBadge/type'\r\nexport type * from './DropdownWithBadge/type'\r\n\r\nexport type { FilterDrawerEmits, FilterDrawerProps } from './FilterDrawer/type'\r\nexport type * from './FilterDrawer/type'\r\n\r\n// 命名导出\r\nexport {\r\n Button,\r\n Dialog,\r\n Divider,\r\n DropdownSelect,\r\n DropdownWithBadge,\r\n FilterDrawer,\r\n InfiniteScroll,\r\n Popup,\r\n PullRefresh,\r\n Swiper,\r\n Toast,\r\n useDropdownSelect,\r\n useDropdownWithBadge,\r\n useFilterDrawer,\r\n}\r\n\r\n// 默认导出,包含所有组件\r\nexport default {\r\n Button,\r\n Dialog,\r\n Divider,\r\n useDropdownSelect,\r\n DropdownSelect,\r\n useDropdownWithBadge,\r\n DropdownWithBadge,\r\n FilterDrawer,\r\n useFilterDrawer,\r\n InfiniteScroll,\r\n Popup,\r\n PullRefresh,\r\n Swiper,\r\n Toast,\r\n}\r\n"],"names":["_createElementBlock","_openBlock","_hoisted_2","_createElementVNode","_Fragment","$slots","_hoisted_3","_renderSlot","_normalizeClass","_hoisted_4","_hoisted_1","_toDisplayString","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","index","_normalizeProps","_guardReactiveProps","_unref","_createBlock","DropdownSelect","_mergeProps","modelValue","options","_toHandlers","_withCtx","iconName","_withDirectives","_createTextVNode","_hoisted_8","_normalizeStyle","_renderList","DropdownWithBadge","FilterDrawer"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/components/MyComs/Button/index.vue","../src/components/MyComs/Dialog/index.vue","../src/components/MyComs/Divider/index.vue","../src/components/MyComs/DropdownSelect/dropdownSelect.ts","../src/components/MyComs/DropdownSelect/index.vue","../src/components/MyComs/DropdownWithBadge/dropdownWithBadge.ts","../src/components/MyComs/DropdownWithBadge/index.vue","../src/components/MyComs/FilterDrawer/hasBadge.ts","../src/components/MyComs/FilterDrawer/useFilterDrawer.ts","../src/components/MyComs/FilterDrawer/index.vue","../src/components/MyComs/InfiniteScroll/index.vue","../src/components/MyComs/Popup/index.vue","../src/components/MyComs/PullRefresh/index.vue","../src/components/MyComs/Swiper/index.vue","../src/components/MyComs/Toast/index.vue","../src/components/MyComs/index.ts"],"sourcesContent":["<template>\n <view\n class=\"sw-button\" :class=\"[\n `sw-button--${type}`,\n `sw-button--${size}`,\n `sw-button--${shape}`,\n { 'is-disabled': disabled },\n { 'is-loading': loading },\n { 'is-block': block },\n { 'is-round': round },\n { 'is-plain': plain },\n { 'is-outline': outline },\n { 'is-link': link },\n ]\"\n :disabled=\"disabled || loading\"\n @click=\"handleClick\"\n >\n <!-- 加载动画 -->\n <template v-if=\"loading\">\n <view class=\"sw-button__loading\">\n <text class=\"loading-spinner\" />\n </view>\n </template>\n\n <!-- 图标 -->\n <template v-if=\"!loading\">\n <!-- 自定义图标插槽 -->\n <view v-if=\"$slots.icon\" class=\"sw-button__icon\">\n <slot name=\"icon\" />\n </view>\n <!-- 传统图标类名方式 -->\n <text v-if=\"icon\" class=\"sw-button__icon\" :class=\"[icon]\" />\n </template>\n\n <!-- 文本内容 -->\n <text v-if=\"$slots.default\" class=\"sw-button__text\">\n <slot />\n </text>\n </view>\n</template>\n\n<script setup lang=\"ts\">\n// 按钮类型\ntype ButtonType = 'primary' | 'success' | 'warning' | 'danger' | 'info'\n\n// 按钮尺寸\ntype ButtonSize = 'large' | 'normal' | 'small' | 'mini'\n\n// 按钮形状\ntype ButtonShape = 'default' | 'circle' | 'round'\n\n// 按钮样式\ntype ButtonStyle = 'solid' | 'outline' | 'plain' | 'link'\n\ninterface Props {\n // 按钮类型\n type?: ButtonType\n // 按钮尺寸\n size?: ButtonSize\n // 按钮形状\n shape?: ButtonShape\n // 是否禁用\n disabled?: boolean\n // 是否加载中\n loading?: boolean\n // 是否为块级元素\n block?: boolean\n // 是否为圆形\n round?: boolean\n // 是否为朴素按钮\n plain?: boolean\n // 是否为描边按钮\n outline?: boolean\n // 是否为链接按钮\n link?: boolean\n // 图标类名\n icon?: string\n // 自定义类名\n customClass?: string\n // 自定义样式\n customStyle?: Record<string, any>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n type: 'primary',\n size: 'normal',\n shape: 'default',\n disabled: false,\n loading: false,\n block: false,\n round: false,\n plain: false,\n outline: false,\n link: false,\n icon: '',\n customClass: '',\n customStyle: () => ({}),\n})\n\nconst emit = defineEmits<{\n (e: 'click', event: MouseEvent): void\n}>()\n\nfunction handleClick(event: MouseEvent) {\n if (!props.disabled && !props.loading) {\n emit('click', event)\n }\n}\n</script>\n\n<style lang=\"less\" scoped>\n// 主题色变量\n@primary-color: #409eff;\n@success-color: #67c23a;\n@warning-color: #e6a23c;\n@danger-color: #f56c6c;\n@info-color: #909399;\n\n// 尺寸变量\n@button-height-large: 88rpx;\n@button-height-normal: 72rpx;\n@button-height-small: 60rpx;\n@button-height-mini: 48rpx;\n\n@button-padding-large: 0 48rpx;\n@button-padding-normal: 0 32rpx;\n@button-padding-small: 0 24rpx;\n@button-padding-mini: 0 16rpx;\n\n@button-font-size-large: 32rpx;\n@button-font-size-normal: 28rpx;\n@button-font-size-small: 26rpx;\n@button-font-size-mini: 24rpx;\n\n// 圆角变量\n@button-border-radius: 8rpx;\n@button-round-border-radius: 40rpx;\n@button-circle-border-radius: 50%;\n\n// 基础样式\n.sw-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n vertical-align: middle;\n cursor: pointer;\n border: none;\n outline: none;\n font-size: @button-font-size-normal;\n font-weight: 400;\n line-height: 1;\n transition: all 0.3s ease;\n box-sizing: border-box;\n position: relative;\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n // 文本样式\n .sw-button__text {\n display: inline-block;\n line-height: 1;\n vertical-align: middle;\n }\n\n // 图标样式\n .sw-button__icon {\n display: inline-block;\n margin-right: 12rpx;\n font-size: inherit;\n line-height: inherit;\n vertical-align: middle;\n }\n\n // 加载动画\n .sw-button__loading {\n display: inline-flex;\n align-items: center;\n margin-right: 12rpx;\n vertical-align: middle;\n\n .loading-spinner {\n display: inline-block;\n width: 32rpx;\n height: 32rpx;\n border: 4rpx solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n }\n }\n}\n\n// 按钮类型\n.sw-button--primary {\n background-color: @primary-color;\n color: #fff;\n}\n\n.sw-button--success {\n background-color: @success-color;\n color: #fff;\n}\n\n.sw-button--warning {\n background-color: @warning-color;\n color: #fff;\n}\n\n.sw-button--danger {\n background-color: @danger-color;\n color: #fff;\n}\n\n.sw-button--info {\n background-color: @info-color;\n color: #fff;\n}\n\n// 按钮尺寸\n.sw-button--large {\n height: @button-height-large;\n padding: @button-padding-large;\n font-size: @button-font-size-large;\n border-radius: @button-border-radius;\n}\n\n.sw-button--normal {\n height: @button-height-normal;\n padding: @button-padding-normal;\n font-size: @button-font-size-normal;\n border-radius: @button-border-radius;\n}\n\n.sw-button--small {\n height: @button-height-small;\n padding: @button-padding-small;\n font-size: @button-font-size-small;\n border-radius: @button-border-radius;\n}\n\n.sw-button--mini {\n height: @button-height-mini;\n padding: @button-padding-mini;\n font-size: @button-font-size-mini;\n border-radius: @button-border-radius;\n}\n\n// 按钮形状\n.sw-button--circle {\n padding: 0;\n border-radius: @button-circle-border-radius;\n\n // 确保圆形按钮有固定的宽高\n &.sw-button--large {\n width: @button-height-large;\n }\n\n &.sw-button--normal {\n width: @button-height-normal;\n }\n\n &.sw-button--small {\n width: @button-height-small;\n }\n\n &.sw-button--mini {\n width: @button-height-mini;\n }\n\n .sw-button__icon {\n margin-right: 0;\n }\n}\n\n.sw-button--round {\n border-radius: @button-round-border-radius;\n}\n\n// 块级按钮\n.is-block {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: center;\n}\n\n// 朴素按钮\n.is-plain {\n background-color: #fff;\n\n &.sw-button--primary {\n color: @primary-color;\n border: 1px solid @primary-color;\n }\n\n &.sw-button--success {\n color: @success-color;\n border: 1px solid @success-color;\n }\n\n &.sw-button--warning {\n color: @warning-color;\n border: 1px solid @warning-color;\n }\n\n &.sw-button--danger {\n color: @danger-color;\n border: 1px solid @danger-color;\n }\n\n &.sw-button--info {\n color: @info-color;\n border: 1px solid @info-color;\n }\n\n &:disabled {\n opacity: 0.4;\n }\n}\n\n// 描边按钮\n.is-outline {\n background-color: transparent;\n\n &.sw-button--primary {\n color: @primary-color;\n border: 1px solid @primary-color;\n }\n\n &.sw-button--success {\n color: @success-color;\n border: 1px solid @success-color;\n }\n\n &.sw-button--warning {\n color: @warning-color;\n border: 1px solid @warning-color;\n }\n\n &.sw-button--danger {\n color: @danger-color;\n border: 1px solid @danger-color;\n }\n\n &.sw-button--info {\n color: @info-color;\n border: 1px solid @info-color;\n }\n}\n\n// 链接按钮\n.is-link {\n background-color: transparent;\n border: none;\n padding: 0;\n height: auto;\n line-height: inherit;\n\n &.sw-button--primary {\n color: @primary-color;\n }\n\n &.sw-button--success {\n color: @success-color;\n }\n\n &.sw-button--warning {\n color: @warning-color;\n }\n\n &.sw-button--danger {\n color: @danger-color;\n }\n\n &.sw-button--info {\n color: @info-color;\n }\n\n &:hover {\n text-decoration: underline;\n }\n}\n\n// 禁用状态\n.is-disabled {\n opacity: 0.6;\n}\n\n// 加载状态\n.is-loading {\n .sw-button__text {\n display: none;\n }\n\n .sw-button__loading {\n margin-right: 0;\n }\n}\n\n// 动画\n@keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n</style>\n","<template>\r\n <view v-if=\"showDialog\" class=\"dialog-wrapper\" @click=\"handleMaskClick\">\r\n <view class=\"dialog-container\" @click.stop>\r\n <!-- 上部分内容 -->\r\n <view class=\"dialog-content\">\r\n <view class=\"dialog-icon\">\r\n <view class=\"dialog-icon-item\">\r\n <!-- 图标插槽包装器 -->\r\n <slot name=\"icon\">\r\n <!-- 自定义字体图标 -->\r\n <text v-if=\"customIcon\" class=\"iconfont dialog-icon-custom\">{{ customIcon }}</text>\r\n <!-- 根据图标类型显示字体图标 -->\r\n <text v-else-if=\"iconType === 'phone'\" class=\"iconfont dialog-button-primary icon-shouji\" />\r\n <text v-else-if=\"iconType === 'screen'\" class=\"iconfont dialog-button-primary icon-xianshiqi\" />\r\n <text v-else-if=\"iconType === 'links'\" class=\"iconfont dialog-button-primary icon-xianshiqi1\" />\r\n <text v-else-if=\"iconType === 'links2'\" class=\"iconfont dialog-button-primary icon-lianjiewangzhi\" />\r\n <!-- 兼容旧版本的图片图标 -->\r\n <image v-else-if=\"icon\" :src=\"icon\" alt=\"dialog-icon\" />\r\n <!-- 默认图标 -->\r\n <view v-else class=\"default-icon\" />\r\n </slot>\r\n </view>\r\n </view>\r\n <view class=\"dialog-title\">\r\n {{ title }}\r\n </view>\r\n <text class=\"dialog-description\">{{ description }}</text>\r\n </view>\r\n\r\n <!-- 分割线 -->\r\n <view class=\"dialog-divider\" />\r\n\r\n <!-- 下部分按钮 -->\r\n <view class=\"dialog-buttons\">\r\n <!-- 类型1: 取消和打开app -->\r\n <template v-if=\"type === 'open-app'\">\r\n <view class=\"dialog-button dialog-button-cancel dialog-button-line\" @click=\"handleCancel\">\r\n 取消\r\n </view>\r\n <view class=\"dialog-button dialog-button-primary\" @click=\"handleConfirm\">\r\n 打开app\r\n </view>\r\n </template>\r\n\r\n <!-- 类型2: 知道了 -->\r\n <template v-else-if=\"type === 'confirm'\">\r\n <view class=\"dialog-button dialog-button-full dialog-button-primary\" @click=\"handleConfirm\">\r\n 知道了\r\n </view>\r\n </template>\r\n\r\n <!-- 类型3: 取消和继续访问 -->\r\n <template v-else-if=\"type === 'continue'\">\r\n <view class=\"dialog-button dialog-button-cancel dialog-button-line\" @click=\"handleCancel\">\r\n 取消\r\n </view>\r\n <view class=\"dialog-button dialog-button-primary\" @click=\"handleConfirm\">\r\n 继续访问\r\n </view>\r\n </template>\r\n </view>\r\n </view>\r\n </view>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, watch } from 'vue'\r\n\r\n// 弹窗类型定义\r\ntype DialogType = 'open-app' | 'confirm' | 'continue'\r\n\r\n// Props定义\r\ninterface Props {\r\n // 控制弹窗显示/隐藏(支持v-model)\r\n modelValue?: boolean\r\n // 弹窗类型\r\n type?: DialogType\r\n // 弹窗标题\r\n title?: string\r\n // 弹窗说明文字\r\n description?: string\r\n // 图标路径(兼容旧版本)\r\n icon?: string\r\n // 图标类型:phone、screen、links、links2\r\n iconType?: 'phone' | 'screen' | 'links' | 'links2'\r\n // 自定义字体图标\r\n customIcon?: string\r\n // 是否显示遮罩层\r\n mask?: boolean\r\n // 遮罩层透明度\r\n maskOpacity?: number\r\n // 点击遮罩是否关闭弹窗\r\n closeOnMaskClick?: boolean\r\n}\r\n\r\n// Props默认值\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: false,\r\n type: 'confirm',\r\n title: '',\r\n description: '',\r\n icon: '',\r\n iconType: undefined,\r\n customIcon: '',\r\n mask: true,\r\n maskOpacity: 0.5,\r\n closeOnMaskClick: true,\r\n})\r\n\r\n// Emits定义\r\nconst emit = defineEmits<{\r\n (e: 'update:modelValue', value: boolean): void\r\n (e: 'confirm'): void\r\n (e: 'cancel'): void\r\n}>()\r\n\r\n// 本地状态\r\nconst showDialog = ref(props.modelValue)\r\n\r\n// 监听modelValue变化\r\nwatch(() => props.modelValue, (newVal) => {\r\n showDialog.value = newVal\r\n})\r\n\r\n// 监听showDialog变化\r\nwatch(() => showDialog.value, (newVal) => {\r\n emit('update:modelValue', newVal)\r\n})\r\n\r\n// 点击遮罩层\r\nfunction handleMaskClick() {\r\n if (props.closeOnMaskClick) {\r\n showDialog.value = false\r\n }\r\n}\r\n\r\n// 点击确认按钮\r\nfunction handleConfirm() {\r\n emit('confirm')\r\n // 确认后关闭弹窗\r\n showDialog.value = false\r\n}\r\n\r\n// 点击取消按钮\r\nfunction handleCancel() {\r\n emit('cancel')\r\n // 取消后关闭弹窗\r\n showDialog.value = false\r\n}\r\n</script>\r\n\r\n<style scoped>\r\n/* 弹窗容器 */\r\n.dialog-wrapper {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n z-index: 1000;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background-color: rgba(0, 0, 0, v-bind('maskOpacity'));\r\n animation: fadeIn 300ms ease;\r\n}\r\n\r\n/* 弹窗主体 */\r\n.dialog-container {\r\n width: 80%;\r\n max-width: 800rpx;\r\n background-color: #fff;\r\n border-radius: 32rpx;\r\n overflow: hidden;\r\n box-shadow: 0 8rpx 40rpx rgba(0, 0, 0, 0.15);\r\n animation: scaleIn 300ms ease;\r\n background-image: url('@/assets/u2.png');\r\n background-size: cover;\r\n background-position: center;\r\n}\r\n\r\n/* 上部分内容 */\r\n.dialog-content {\r\n padding: 60rpx 40rpx;\r\n text-align: center;\r\n}\r\n\r\n/* 图标 */\r\n.dialog-icon-item {\r\n margin-bottom: 40rpx;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n border-radius: 50%;\r\n border: 2rpx solid rgb(151, 150, 150);\r\n display: inline-block;\r\n}\r\n\r\n.dialog-icon image {\r\n object-fit: contain;\r\n}\r\n\r\n/* 字体图标样式 */\r\n.dialog-icon .iconfont {\r\n width: 120rpx;\r\n height: 120rpx;\r\n font-size: 80rpx;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n/* 自定义字体图标 */\r\n.dialog-icon-custom {\r\n color: #1989fa;\r\n}\r\n\r\n/* 成功图标 */\r\n.dialog-icon-success {\r\n color: #67c23a;\r\n}\r\n\r\n/* 警告图标 */\r\n.dialog-icon-warning {\r\n color: #e6a23c;\r\n}\r\n\r\n/* 错误图标 */\r\n.dialog-icon-error {\r\n color: #f56c6c;\r\n}\r\n\r\n/* 信息图标 */\r\n.dialog-icon-info {\r\n color: #909399;\r\n}\r\n\r\n.default-icon {\r\n width: 120rpx;\r\n height: 120rpx;\r\n background-color: #f0f0f0;\r\n border-radius: 50%;\r\n}\r\n\r\n/* 标题 */\r\n.dialog-title {\r\n font-size: 36rpx;\r\n font-weight: bold;\r\n color: #333;\r\n margin: 0 0 20rpx 0;\r\n}\r\n\r\n/* 说明文字 */\r\n.dialog-description {\r\n font-size: 28rpx;\r\n color: #666;\r\n margin: 0;\r\n line-height: 1.5;\r\n display: block;\r\n}\r\n\r\n/* 分割线 */\r\n.dialog-divider {\r\n height: 2rpx;\r\n background-color: #e5e5e5;\r\n margin: 0;\r\n}\r\n\r\n/* 按钮区域 */\r\n.dialog-buttons {\r\n display: flex;\r\n flex-direction: row;\r\n align-items: center;\r\n}\r\n\r\n/* 按钮 */\r\n.dialog-button {\r\n flex: 1;\r\n padding: 30rpx;\r\n border: none;\r\n background: none;\r\n font-size: 32rpx;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: background-color 0.3s ease;\r\n text-align: center;\r\n box-sizing: border-box;\r\n}\r\n\r\n/* 全宽按钮 */\r\n.dialog-button-full {\r\n width: 100%;\r\n}\r\n\r\n/* 取消按钮 */\r\n.dialog-button-cancel {\r\n color: #666;\r\n}\r\n\r\n.dialog-button-cancel:hover {\r\n background-color: #f5f5f5;\r\n}\r\n\r\n/* 主要按钮 */\r\n.dialog-button-primary {\r\n color: #00599c;\r\n}\r\n.dialog-button-line {\r\n border-right: 3rpx solid #e5e5e5;\r\n}\r\n\r\n.dialog-button-primary:hover {\r\n background-color: #f0f9ff;\r\n}\r\n\r\n/* 动画效果 */\r\n@keyframes fadeIn {\r\n from {\r\n opacity: 0;\r\n }\r\n to {\r\n opacity: 1;\r\n }\r\n}\r\n\r\n@keyframes scaleIn {\r\n from {\r\n transform: scale(0.8);\r\n opacity: 0;\r\n }\r\n to {\r\n transform: scale(1);\r\n opacity: 1;\r\n }\r\n}\r\n</style>\r\n","<template>\n <view\n class=\"sw-divider\"\n :class=\"[\n { 'is-vertical': vertical },\n { 'is-dashed': dashed },\n { [`sw-divider--${position}`]: position !== 'center' },\n ]\"\n >\n <text v-if=\"$slots.default\" class=\"sw-divider__text\">\n <slot />\n </text>\n </view>\n</template>\n\n<script setup lang=\"ts\">\ninterface DividerProps {\n // 是否为垂直分隔线\n vertical?: boolean\n // 是否为虚线\n dashed?: boolean\n // 文本位置,可选值为 left、center、right\n position?: 'left' | 'center' | 'right'\n // 自定义类名\n customClass?: string\n // 自定义样式\n customStyle?: Record<string, any>\n}\n\nconst props = withDefaults(defineProps<DividerProps>(), {\n vertical: false,\n dashed: false,\n position: 'center',\n customClass: '',\n customStyle: () => ({}),\n})\n</script>\n\n<style scoped lang=\"less\">\n// 主题色变量\n@primary-color: #409eff;\n@gray-3: #c0c4cc;\n\n// 基础样式\n.sw-divider {\n background-color: @gray-3;\n position: relative;\n box-sizing: border-box;\n\n &.is-vertical {\n display: inline-block;\n width: 2rpx;\n height: 32rpx;\n margin: 0 24rpx;\n vertical-align: middle;\n }\n\n &:not(.is-vertical) {\n width: 100%;\n height: 2rpx;\n margin: 32rpx 0;\n }\n\n &.is-dashed {\n background: none;\n border-top: 2rpx dashed @gray-3;\n }\n\n &__text {\n position: absolute;\n background-color: #fff;\n padding: 0 32rpx;\n font-size: 28rpx;\n color: #606266;\n transform: translateY(-50%);\n\n &.is-vertical {\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 32rpx 0;\n }\n }\n\n &--left {\n .sw-divider__text {\n left: 0;\n padding-left: 0;\n }\n }\n\n &--center {\n .sw-divider__text {\n left: 50%;\n transform: translateX(-50%) translateY(-50%);\n }\n }\n\n &--right {\n .sw-divider__text {\n right: 0;\n padding-right: 0;\n }\n }\n}\n</style>\n","import type { DropdownSelectEmits, DropdownSelectProps } from './type'\nimport { computed, ref } from 'vue'\n\nexport function useDropdownSelect<T>(\n props: DropdownSelectProps<T>,\n emit: DropdownSelectEmits<T>,\n) {\n // 控制箭头旋转状态\n const isOpen = ref(false)\n // 查找当前应显示的文本\n const displayText = computed(() => {\n if (!props.options?.length) {\n return '请选择'\n }\n // 优先根据 modelValue 匹配\n if (props.modelValue !== undefined && props.modelValue !== null) {\n const matched = props.options.find(\n opt => opt.value === props.modelValue,\n )\n if (matched)\n return matched.text\n }\n // 无有效 modelValue 时,默认显示第一项文本(仅 UI 展示)\n return props.options[0]?.text || '请选择'\n })\n // 内部绑定值:用于控制 Vant 选中高亮\n const internalValue = computed({\n get() {\n if (props.modelValue !== undefined && props.modelValue !== null) {\n return props.modelValue\n }\n // 无外部值时,返回第一个选项的 value(仅用于 UI 高亮,不 emit)\n return props.options?.[0]?.value ?? ''\n },\n set(val) {\n emit('update:modelValue', val as T)\n },\n })\n // 事件处理\n const handleChange = (value: T) => {\n emit('change', value)\n }\n\n const handleOpen = () => {\n isOpen.value = true\n }\n const handleClose = () => {\n isOpen.value = false\n }\n\n return {\n displayText,\n internalValue,\n isOpen,\n handleChange,\n handleOpen,\n handleClose,\n }\n}\n","<template>\n <view class=\"dropdown-select\">\n <!-- 下拉选择触发器 -->\n <view class=\"dropdown-trigger\" @tap=\"togglePicker\">\n <slot v-bind=\"{ text: displayText }\">\n <view class=\"dropdown-title\">\n <text class=\"dropdown-text\">{{ displayText }}</text>\n <text class=\"dropdown-icon\" :class=\"{ 'is-active': isOpen }\">\n <slot name=\"icon\">\n <!-- 使用 UniApp 内置图标或自定义图标 -->\n <view class=\"uni-icon\" :class=\"iconName\" />\n </slot>\n </text>\n </view>\n </slot>\n </view>\n\n <!-- 选择器弹窗 -->\n <picker\n v-if=\"isOpen\"\n mode=\"selector\"\n :range=\"options\"\n range-key=\"text\"\n :value=\"selectedIndex\"\n class=\"dropdown-picker\"\n @change=\"onPickerChange\"\n @cancel=\"onPickerCancel\"\n >\n <!-- UniApp picker 组件会自动渲染选择器UI -->\n </picker>\n </view>\n</template>\n\n<script setup lang=\"ts\" generic=\"T\">\nimport type { DropdownSelectEmits, DropdownSelectProps } from './type'\nimport { computed, ref } from 'vue'\nimport { useDropdownSelect } from './dropdownSelect'\n\nconst props = withDefaults(\n defineProps<DropdownSelectProps<T>>(),\n {\n iconName: 'arrow-down',\n },\n)\nconst emit = defineEmits<DropdownSelectEmits<T>>()\n\nconst { displayText, internalValue, handleChange, handleOpen, handleClose } = useDropdownSelect<T>(props, emit)\n\n// 控制选择器显示状态\nconst isOpen = ref(false)\n\n// 计算当前选中项的索引\nconst selectedIndex = computed(() => {\n if (!props.options || !props.options.length)\n return 0\n return props.options.findIndex(opt => opt.value === internalValue.value)\n})\n\n// 切换选择器显示/隐藏\nfunction togglePicker() {\n isOpen.value = !isOpen.value\n if (isOpen.value) {\n handleOpen()\n }\n else {\n handleClose()\n }\n}\n\n// 选择器确认选择\nfunction onPickerChange(e: any) {\n const index = e.detail.value\n const selectedOption = props.options[index]\n handleChange(selectedOption.value as T)\n isOpen.value = false\n handleClose()\n}\n\n// 选择器取消选择\nfunction onPickerCancel() {\n isOpen.value = false\n handleClose()\n}\n</script>\n\n<style lang=\"less\">\n@import './dropdownSelect.less';\n</style>\n","import type { DropdownWithBadgeProps } from './type'\nimport { computed } from 'vue'\n\nexport function useDropdownWithBadge<T>(props: DropdownWithBadgeProps<T>) {\n const hasBadge = computed(() => {\n return props.hideBadge!(props.modelValue)\n })\n\n return { hasBadge }\n}\n","<template>\n <dropdown-select\n v-bind=\"{ modelValue, options }\" class=\"dropdown-with-badge\"\n v-on=\"emit\"\n >\n <template #default=\"{ text }\">\n <slot v-bind=\"{ text }\">\n <view class=\"dropdown-title\">\n <text class=\"dropdown-text\">{{ text }}</text>\n <text class=\"dropdown-icon\">\n <slot name=\"icon\">\n <!-- 使用自定义图标类名 -->\n <view class=\"uni-icon\" :class=\"iconName\" />\n </slot>\n </text>\n </view>\n <text v-show=\"!hasBadge\" class=\"dropdown-with-badge__dot\" />\n </slot>\n </template>\n </dropdown-select>\n</template>\n\n<script lang=\"ts\" setup generic=\"T\">\nimport type { DropdownWithBadgeEmits, DropdownWithBadgeProps } from './type'\nimport DropdownSelect from '../DropdownSelect'\nimport { useDropdownWithBadge } from './dropdownWithBadge'\n\nconst props = withDefaults(defineProps<DropdownWithBadgeProps<T>>(), {\n hideBadge: (modelValue?: T) => (modelValue === '' || modelValue === null || modelValue === undefined),\n iconName: 'filter-o',\n})\nconst emit = defineEmits<DropdownWithBadgeEmits<T>>()\n\nconst { hasBadge } = useDropdownWithBadge(props)\n</script>\n\n<style lang=\"less\">\n@import './dropdownWithBadge.less';\n</style>\n","import type { ComputedRef, Ref } from 'vue'\nimport { computed, isRef } from 'vue'\n\n/**\n * 判断是否应显示徽章(如筛选条件已应用)\n * @param model 表单数据或者自定义方法\n * @returns 响应式的布尔值 Ref<boolean>\n */\nexport function useHasBadge<T extends object>(\n model?: Ref<T> | T | (() => boolean),\n): ComputedRef<boolean> {\n if (!model) {\n return computed(() => false)\n }\n\n if (typeof model === 'function') {\n return computed(() => model())\n }\n if (isRef(model)) {\n return computed(() => {\n if (!model.value)\n return false\n const obj = model.value as Record<keyof T, any>\n for (const key in obj) {\n if (obj[key]) {\n return true\n }\n }\n return false\n })\n }\n return computed(() => {\n for (const key in model) {\n const val = model[key as keyof typeof model]\n if (val) {\n return true\n }\n }\n return false\n })\n}\n","import type { FilterDrawerEmits, FilterDrawerProps } from './type'\nimport { ref } from 'vue'\nimport { useHasBadge } from './hasBadge'\n\nexport function useFilterDrawer<T extends object>(props: FilterDrawerProps<T>, emit: FilterDrawerEmits) {\n // 使用ref替代useToggle\n const visible = ref(false)\n\n // 徽章状态\n const hasBadge = useHasBadge(props.modelValue)\n\n const openDrawer = () => {\n visible.value = true\n }\n\n const closeDrawer = () => {\n visible.value = false\n }\n\n const resetDrawer = () => {\n emit('reset')\n closeDrawer()\n }\n\n const queryDrawer = () => {\n emit('query')\n closeDrawer()\n }\n\n return {\n visible,\n openDrawer,\n closeDrawer,\n resetDrawer,\n queryDrawer,\n hasBadge,\n }\n}\n","<template>\n <view class=\"filter-drawer__trigger\" @click=\"openDrawer\">\n <slot name=\"trigger\">\n <!-- 使用自定义图标 -->\n <view class=\"uni-icon icon-filter-o\" />\n </slot>\n <text v-if=\"hasBadge\" class=\"filter-drawer__badge-dot\" />\n </view>\n\n <!-- 使用 UniApp 内置的 popup 组件 -->\n <view v-if=\"visible\" class=\"filter-drawer__popup-mask\" @click=\"closeDrawer\" />\n <view v-if=\"visible\" class=\"filter-drawer__popup\" :style=\"{ height: '100vh', width: '100vw' }\">\n <view class=\"filter-drawer__close\" @click=\"closeDrawer\">\n ×\n </view>\n <view class=\"filter-drawer\">\n <view class=\"filter-drawer__header\">\n <slot name=\"header\">\n <text class=\"filter-drawer__title\">筛选</text>\n </slot>\n </view>\n <view class=\"filter-drawer__body\">\n <slot />\n </view>\n <view class=\"filter-drawer__footer\">\n <slot name=\"footer\">\n <view class=\"filter-drawer__button filter-drawer__button--plain\" @click=\"resetDrawer\">\n 重置条件\n </view>\n <view class=\"filter-drawer__button\" @click=\"queryDrawer\">\n 查询\n </view>\n </slot>\n </view>\n </view>\n </view>\n</template>\n\n<script setup lang=\"ts\" generic=\"T extends object\">\nimport type { FilterDrawerEmits, FilterDrawerProps } from './type'\nimport { useFilterDrawer } from './useFilterDrawer'\n\nconst props = withDefaults(defineProps<FilterDrawerProps<T>>(), {\n hasBadge: false,\n})\nconst emit = defineEmits<FilterDrawerEmits>()\n\nconst { visible, openDrawer, closeDrawer, resetDrawer, queryDrawer, hasBadge } = useFilterDrawer(props, emit)\n</script>\n\n<style lang=\"less\">\n@import './filterDrawer.less';\n</style>\n","<template>\n <view\n ref=\"scrollContainer\"\n class=\"sw-infinite-scroll\"\n :style=\"containerStyle\"\n @scroll=\"handleScroll\"\n >\n <!-- 内容区域 -->\n <view class=\"sw-infinite-scroll__content\">\n <slot />\n </view>\n\n <!-- 加载状态 -->\n <view v-if=\"loading\" class=\"sw-infinite-scroll__loading\">\n <view class=\"loading-spinner\" />\n <text>{{ loadingText }}</text>\n </view>\n\n <!-- 无更多数据 -->\n <view v-if=\"finished && !loading\" class=\"sw-infinite-scroll__finished\">\n {{ finishedText }}\n </view>\n </view>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\n// 定义组件属性\ninterface Props {\n // 是否正在加载\n loading?: boolean\n // 是否已加载完成\n finished?: boolean\n // 触发加载的距离阈值 (px)\n offset?: number\n // 加载提示文字\n loadingText?: string\n // 加载完成提示文字\n finishedText?: string\n // 是否禁用滚动加载\n disabled?: boolean\n // 容器高度\n height?: string | number\n}\n\n// 设置默认属性\nconst props = withDefaults(defineProps<Props>(), {\n loading: false,\n finished: false,\n offset: 100,\n loadingText: '加载中...',\n finishedText: '没有更多了',\n disabled: false,\n height: 'auto',\n})\n\n// 定义组件事件\nconst emit = defineEmits<{\n // 触发加载事件\n (e: 'load'): void\n}>()\n\n// 容器引用\nconst scrollContainer = ref<any>(null)\n\n// 容器样式\nconst containerStyle = computed(() => {\n return {\n height: typeof props.height === 'number' ? `${props.height}px` : props.height,\n }\n})\n\n// 处理滚动事件\nfunction handleScroll() {\n if (!scrollContainer.value || props.loading || props.finished || props.disabled) {\n return\n }\n\n const { scrollTop, scrollHeight, clientHeight } = scrollContainer.value\n\n // 当滚动到底部附近时触发加载\n if (scrollHeight - scrollTop - clientHeight <= props.offset) {\n emit('load')\n }\n}\n\n// 监听窗口大小变化\nlet resizeTimeout: number | null = null\n\nfunction handleResize() {\n if (resizeTimeout) {\n clearTimeout(resizeTimeout)\n }\n // 使用标准setTimeout确保跨平台兼容\n resizeTimeout = setTimeout(() => {\n handleScroll()\n }, 100) as unknown as number\n}\n\nonMounted(() => {\n // 初始化检查是否已滚动到底部\n handleScroll()\n\n // #ifdef H5\n // 只有H5平台支持window对象\n window.addEventListener('resize', handleResize)\n // #endif\n})\n\nonUnmounted(() => {\n // #ifdef H5\n // 只有H5平台支持window对象\n window.removeEventListener('resize', handleResize)\n // #endif\n if (resizeTimeout) {\n clearTimeout(resizeTimeout)\n }\n})\n</script>\n\n<style lang=\"less\" scoped>\n// 基础样式\n.sw-infinite-scroll {\n overflow-y: auto;\n position: relative;\n\n // 内容区域\n &__content {\n min-height: 100%;\n }\n\n // 加载状态\n &__loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 40rpx 0;\n color: #909399;\n font-size: 28rpx;\n\n .loading-spinner {\n width: 32rpx;\n height: 32rpx;\n border: 4rpx solid #d9d9d9;\n border-top-color: #409eff;\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n margin-right: 16rpx;\n }\n }\n\n // 无更多数据\n &__finished {\n text-align: center;\n padding: 40rpx 0;\n color: #909399;\n font-size: 28rpx;\n }\n}\n\n// 加载动画\n@keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n</style>\n","<template>\r\n <view\r\n v-show=\"showPopup\"\r\n class=\"popup-wrapper\"\r\n :class=\"{\r\n 'modal': mask,\r\n 'non-modal': !mask,\r\n 'closing': isClosing,\r\n }\"\r\n @click.self=\"handleMaskClick\"\r\n >\r\n <!-- 遮罩层 -->\r\n <view\r\n v-if=\"mask\"\r\n class=\"popup-mask\"\r\n :style=\"{ opacity: maskOpacity }\"\r\n @click=\"handleMaskClick\"\r\n />\r\n\r\n <!-- 弹窗主体 -->\r\n <view\r\n ref=\"popupRef\"\r\n class=\"popup-container\"\r\n :class=\"[\r\n `position-${position}`,\r\n `animation-${animation}`,\r\n { closing: isClosing },\r\n ]\"\r\n @click.stop\r\n @touchmove.stop\r\n @animationend=\"handleAnimationEnd\"\r\n >\r\n <!-- 自定义头部 -->\r\n <view v-if=\"$slots.header\" class=\"popup-header\">\r\n <slot name=\"header\" />\r\n </view>\r\n\r\n <!-- 自定义标题 -->\r\n <view v-else-if=\"title\" class=\"popup-header\">\r\n <text class=\"popup-title\">{{ title }}</text>\r\n </view>\r\n\r\n <!-- 内容区域 -->\r\n <view class=\"popup-content\">\r\n <slot />\r\n </view>\r\n\r\n <!-- 自定义底部 -->\r\n <view v-if=\"$slots.footer\" class=\"popup-footer\">\r\n <slot name=\"footer\" />\r\n </view>\r\n\r\n <!-- 默认底部按钮 -->\r\n <view v-else-if=\"showConfirmButton || showCancelButton\" class=\"popup-footer\">\r\n <view\r\n v-if=\"showCancelButton\"\r\n class=\"popup-button popup-button-cancel\"\r\n @click=\"handleCancel\"\r\n >\r\n {{ cancelText }}\r\n </view>\r\n <view\r\n v-if=\"showConfirmButton\"\r\n class=\"popup-button popup-button-confirm\"\r\n @click=\"handleConfirm\"\r\n >\r\n {{ confirmText }}\r\n </view>\r\n </view>\r\n\r\n <!-- 关闭按钮 -->\r\n <view\r\n v-if=\"showCloseButton\"\r\n class=\"popup-close-button\"\r\n @click=\"handleClose\"\r\n >\r\n <text class=\"close-icon\">×</text>\r\n </view>\r\n </view>\r\n </view>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { onMounted, onUnmounted, ref, watch } from 'vue'\r\n\r\n// Props\r\ninterface Props {\r\n // 控制弹窗显示/隐藏(支持v-model)\r\n modelValue?: boolean\r\n // 弹窗标题\r\n title?: string\r\n // 弹窗位置:center, top, bottom, left, right\r\n position?: 'center' | 'top' | 'bottom' | 'left' | 'right'\r\n // 动画效果:fade, scale, slide\r\n animation?: 'fade' | 'scale' | 'slide'\r\n // 是否显示遮罩层\r\n mask?: boolean\r\n // 遮罩层透明度\r\n maskOpacity?: number\r\n // 点击遮罩是否关闭弹窗\r\n closeOnMaskClick?: boolean\r\n // 是否显示关闭按钮\r\n showCloseButton?: boolean\r\n // 是否支持Esc键关闭\r\n closeOnEsc?: boolean\r\n // 是否显示确认按钮\r\n showConfirmButton?: boolean\r\n // 确认按钮文本\r\n confirmText?: string\r\n // 是否显示取消按钮\r\n showCancelButton?: boolean\r\n // 取消按钮文本\r\n cancelText?: string\r\n // 弹窗宽度(百分比或像素)\r\n width?: string | number\r\n // 弹窗高度(百分比或像素)\r\n height?: string | number\r\n // 弹窗最大宽度\r\n maxWidth?: string | number\r\n // 是否禁止背景滚动\r\n lockScroll?: boolean\r\n}\r\n\r\n// 设置默认属性\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: false,\r\n title: '',\r\n position: 'center',\r\n animation: 'slide',\r\n mask: true,\r\n maskOpacity: 0.5,\r\n closeOnMaskClick: true,\r\n showCloseButton: true,\r\n closeOnEsc: true,\r\n showConfirmButton: false,\r\n confirmText: '确定',\r\n showCancelButton: false,\r\n cancelText: '取消',\r\n width: '50%',\r\n height: '100%',\r\n maxWidth: '500px',\r\n lockScroll: true,\r\n})\r\n\r\n// Emits\r\nconst emit = defineEmits<{\r\n (e: 'update:modelValue', value: boolean): void\r\n (e: 'open'): void\r\n (e: 'close'): void\r\n (e: 'maskClick'): void\r\n (e: 'confirm'): void\r\n (e: 'cancel'): void\r\n (e: 'closeButtonClick'): void\r\n}>()\r\n\r\n// Refs\r\nconst popupRef = ref<any>(null)\r\nconst showPopup = ref(props.modelValue)\r\nconst isClosing = ref(false)\r\nconst lastScrollY = ref(0)\r\n\r\n// Watchers\r\n// 监听modelValue变化\r\nwatch(() => props.modelValue, (newVal) => {\r\n showPopup.value = newVal\r\n if (newVal) {\r\n onOpen()\r\n }\r\n else {\r\n onClose()\r\n }\r\n})\r\n\r\n// 监听showPopup变化\r\nwatch(() => showPopup.value, (newVal) => {\r\n // 同步到v-model\r\n emit('update:modelValue', newVal)\r\n if (newVal) {\r\n onOpen()\r\n }\r\n else {\r\n onClose()\r\n }\r\n})\r\n\r\n// Methods\r\n// 弹窗打开时\r\nfunction onOpen() {\r\n // 锁定滚动\r\n if (props.lockScroll) {\r\n lockScroll()\r\n }\r\n\r\n // 添加键盘事件监听\r\n if (props.closeOnEsc) {\r\n window.addEventListener('keydown', handleKeyDown)\r\n }\r\n\r\n // 触发打开事件\r\n emit('open')\r\n}\r\n\r\n// 弹窗关闭时\r\nfunction onClose() {\r\n // 解锁滚动\r\n if (props.lockScroll) {\r\n unlockScroll()\r\n }\r\n\r\n // 移除键盘事件监听\r\n window.removeEventListener('keydown', handleKeyDown)\r\n\r\n // 触发关闭事件\r\n emit('close')\r\n}\r\n\r\n// 锁定滚动\r\nfunction lockScroll() {\r\n // #ifdef H5\r\n lastScrollY.value = window.scrollY\r\n document.body.style.position = 'fixed'\r\n document.body.style.top = `-${lastScrollY.value}px`\r\n document.body.style.left = '0'\r\n document.body.style.right = '0'\r\n document.body.style.overflow = 'hidden'\r\n // #endif\r\n\r\n // #ifdef MP\r\n uni.hideTabBar()\r\n // #endif\r\n}\r\n\r\n// 解锁滚动\r\nfunction unlockScroll() {\r\n // #ifdef H5\r\n document.body.style.position = ''\r\n document.body.style.top = ''\r\n document.body.style.left = ''\r\n document.body.style.right = ''\r\n document.body.style.overflow = ''\r\n window.scrollTo(0, lastScrollY.value)\r\n // #endif\r\n\r\n // #ifdef MP\r\n uni.showTabBar()\r\n // #endif\r\n}\r\n\r\n// 键盘事件处理\r\nfunction handleKeyDown(e) {\r\n // #ifdef H5\r\n if (e.key === 'Escape' && props.closeOnEsc && showPopup.value) {\r\n handleClose()\r\n }\r\n // #endif\r\n}\r\n\r\n// 点击遮罩层\r\nfunction handleMaskClick() {\r\n if (props.closeOnMaskClick) {\r\n handleClose()\r\n }\r\n emit('maskClick')\r\n}\r\n\r\n// 点击关闭按钮\r\nfunction handleClose() {\r\n isClosing.value = true\r\n emit('closeButtonClick')\r\n}\r\n\r\n// 动画结束事件处理\r\nfunction handleAnimationEnd() {\r\n if (isClosing.value) {\r\n isClosing.value = false\r\n showPopup.value = false\r\n }\r\n}\r\n\r\n// 点击确认按钮\r\nfunction handleConfirm() {\r\n emit('confirm')\r\n}\r\n\r\n// 点击取消按钮\r\nfunction handleCancel() {\r\n emit('cancel')\r\n handleClose()\r\n}\r\n\r\n// 生命周期\r\nonMounted(() => {\r\n if (showPopup.value) {\r\n onOpen()\r\n }\r\n})\r\n\r\nonUnmounted(() => {\r\n if (showPopup.value) {\r\n onClose()\r\n }\r\n})\r\n</script>\r\n\r\n<style scoped>\r\n/* 弹窗容器 */\r\n.popup-wrapper {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n z-index: 1000;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n pointer-events: none;\r\n}\r\n\r\n/* 模态模式 */\r\n.popup-wrapper.modal {\r\n pointer-events: auto;\r\n}\r\n\r\n/* 非模态模式 */\r\n.popup-wrapper.non-modal {\r\n pointer-events: none;\r\n}\r\n\r\n.popup-wrapper.non-modal .popup-container {\r\n pointer-events: auto;\r\n}\r\n\r\n/* 遮罩层 */\r\n.popup-mask {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n transition: opacity 0.3s ease;\r\n}\r\n\r\n/* 弹窗主体 */\r\n.popup-container {\r\n position: fixed;\r\n background-color: white;\r\n border-radius: 16rpx;\r\n box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);\r\n width: var(--popup-width, 100%);\r\n max-width: var(--popup-max-width, 750rpx);\r\n max-height: 90vh;\r\n overflow: hidden;\r\n pointer-events: auto;\r\n}\r\n\r\n/* 头部 */\r\n.popup-header {\r\n padding: 32rpx 40rpx;\r\n border-bottom: 2rpx solid #ebeef5;\r\n}\r\n\r\n.popup-title {\r\n margin: 0;\r\n font-size: 32rpx;\r\n font-weight: 600;\r\n color: #303133;\r\n}\r\n\r\n/* 内容 */\r\n.popup-content {\r\n padding: 40rpx;\r\n max-height: calc(90vh - 240rpx);\r\n overflow-y: auto;\r\n}\r\n\r\n/* 底部 */\r\n.popup-footer {\r\n padding: 32rpx 40rpx;\r\n border-top: 2rpx solid #ebeef5;\r\n display: flex;\r\n justify-content: flex-end;\r\n gap: 24rpx;\r\n}\r\n\r\n/* 按钮 */\r\n.popup-button {\r\n padding: 16rpx 32rpx;\r\n border: none;\r\n border-radius: 8rpx;\r\n font-size: 28rpx;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n text-align: center;\r\n box-sizing: border-box;\r\n}\r\n\r\n.popup-button-cancel {\r\n background-color: #f5f7fa;\r\n color: #606266;\r\n}\r\n\r\n.popup-button-cancel:hover {\r\n background-color: #e4e7ed;\r\n}\r\n\r\n.popup-button-confirm {\r\n background-color: #409eff;\r\n color: white;\r\n}\r\n\r\n.popup-button-confirm:hover {\r\n background-color: #66b1ff;\r\n}\r\n\r\n/* 关闭按钮 */\r\n.popup-close-button {\r\n position: absolute;\r\n top: 24rpx;\r\n right: 24rpx;\r\n width: 48rpx;\r\n height: 48rpx;\r\n background-color: transparent;\r\n border-radius: 50%;\r\n cursor: pointer;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: #909399;\r\n transition: all 0.3s ease;\r\n}\r\n\r\n.popup-close-button:hover {\r\n background-color: #f5f7fa;\r\n color: #606266;\r\n}\r\n\r\n/* 关闭图标 */\r\n.close-icon {\r\n font-size: 40rpx;\r\n font-weight: bold;\r\n line-height: 1;\r\n}\r\n\r\n/* 位置样式 */\r\n/* 居中 */\r\n.position-center {\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n}\r\n\r\n/* 顶部 */\r\n.position-top {\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n border-radius: 0 0 16rpx 16rpx;\r\n}\r\n\r\n/* 底部 */\r\n.position-bottom {\r\n bottom: 0;\r\n left: 0;\r\n width: 100%;\r\n border-radius: 16rpx 16rpx 0 0;\r\n}\r\n\r\n/* 左侧 */\r\n.position-left {\r\n top: 0;\r\n left: 0;\r\n height: 100%;\r\n border-radius: 0 16rpx 16rpx 0;\r\n}\r\n\r\n/* 右侧 */\r\n.position-right {\r\n top: 0;\r\n right: 0;\r\n height: 100%;\r\n border-radius: 16rpx 0 0 16rpx;\r\n}\r\n\r\n/* 动画样式 */\r\n/* 淡入淡出 */\r\n.animation-fade {\r\n animation: fadeIn 0.3s ease;\r\n}\r\n\r\n@keyframes fadeIn {\r\n from {\r\n opacity: 0;\r\n }\r\n to {\r\n opacity: 1;\r\n }\r\n}\r\n\r\n/* 缩放 */\r\n.animation-scale {\r\n animation: scaleIn 0.3s ease;\r\n}\r\n\r\n@keyframes scaleIn {\r\n from {\r\n opacity: 0;\r\n transform: translate(-50%, -50%) scale(0.8);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translate(-50%, -50%) scale(1);\r\n }\r\n}\r\n\r\n/* 顶部位置的缩放动画 */\r\n.position-top.animation-scale {\r\n animation: scaleInTop 0.3s ease;\r\n}\r\n\r\n@keyframes scaleInTop {\r\n from {\r\n opacity: 0;\r\n transform: translateY(-100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n/* 底部位置的缩放动画 */\r\n.position-bottom.animation-scale {\r\n animation: scaleInBottom 0.3s ease;\r\n}\r\n\r\n@keyframes scaleInBottom {\r\n from {\r\n opacity: 0;\r\n transform: translateY(100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n/* 左侧位置的缩放动画 */\r\n.position-left.animation-scale {\r\n animation: scaleInLeft 0.3s ease;\r\n}\r\n\r\n@keyframes scaleInLeft {\r\n from {\r\n opacity: 0;\r\n transform: translateX(-100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n}\r\n\r\n/* 右侧位置的缩放动画 */\r\n.position-right.animation-scale {\r\n animation: scaleInRight 0.3s ease;\r\n}\r\n\r\n@keyframes scaleInRight {\r\n from {\r\n opacity: 0;\r\n transform: translateX(100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n}\r\n\r\n/* 滑动动画 */\r\n.animation-slide {\r\n animation: slideIn 0.3s ease;\r\n}\r\n\r\n@keyframes slideIn {\r\n from {\r\n opacity: 0;\r\n transform: translate(-50%, -100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translate(-50%, -50%);\r\n }\r\n}\r\n\r\n/* 顶部位置的滑动动画 */\r\n.position-top.animation-slide {\r\n animation: slideInTop 0.3s ease;\r\n}\r\n\r\n@keyframes slideInTop {\r\n from {\r\n opacity: 0;\r\n transform: translateY(-100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n/* 底部位置的滑动动画 */\r\n.position-bottom.animation-slide {\r\n animation: slideInBottom 0.3s ease;\r\n}\r\n\r\n@keyframes slideInBottom {\r\n from {\r\n opacity: 0;\r\n transform: translateY(100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n}\r\n\r\n/* 左侧位置的滑动动画 */\r\n.position-left.animation-slide {\r\n animation: slideInLeft 0.3s ease;\r\n}\r\n\r\n@keyframes slideInLeft {\r\n from {\r\n opacity: 0;\r\n transform: translateX(-100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n}\r\n\r\n/* 右侧位置的滑动动画 */\r\n.position-right.animation-slide {\r\n animation: slideInRight 0.3s ease;\r\n}\r\n\r\n@keyframes slideInRight {\r\n from {\r\n opacity: 0;\r\n transform: translateX(100%);\r\n }\r\n to {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n}\r\n\r\n/* 关闭动画 */\r\n/* 淡入淡出关闭动画 */\r\n.animation-fade.closing {\r\n animation: fadeOut 0.3s ease;\r\n}\r\n\r\n@keyframes fadeOut {\r\n from {\r\n opacity: 1;\r\n }\r\n to {\r\n opacity: 0;\r\n }\r\n}\r\n\r\n/* 缩放关闭动画 */\r\n.animation-scale.closing {\r\n animation: scaleOut 0.3s ease;\r\n}\r\n\r\n@keyframes scaleOut {\r\n from {\r\n opacity: 1;\r\n transform: translate(-50%, -50%) scale(1);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translate(-50%, -50%) scale(0.8);\r\n }\r\n}\r\n\r\n/* 顶部位置的缩放关闭动画 */\r\n.position-top.animation-scale.closing {\r\n animation: scaleOutTop 0.3s ease;\r\n}\r\n\r\n@keyframes scaleOutTop {\r\n from {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateY(-100%);\r\n }\r\n}\r\n\r\n/* 底部位置的缩放关闭动画 */\r\n.position-bottom.animation-scale.closing {\r\n animation: scaleOutBottom 0.3s ease;\r\n}\r\n\r\n@keyframes scaleOutBottom {\r\n from {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateY(100%);\r\n }\r\n}\r\n\r\n/* 左侧位置的缩放关闭动画 */\r\n.position-left.animation-scale.closing {\r\n animation: scaleOutLeft 0.3s ease;\r\n}\r\n\r\n@keyframes scaleOutLeft {\r\n from {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateX(-100%);\r\n }\r\n}\r\n\r\n/* 右侧位置的缩放关闭动画 */\r\n.position-right.animation-scale.closing {\r\n animation: scaleOutRight 0.3s ease;\r\n}\r\n\r\n@keyframes scaleOutRight {\r\n from {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateX(100%);\r\n }\r\n}\r\n\r\n/* 滑动关闭动画 */\r\n.animation-slide.closing {\r\n animation: slideOut 0.3s ease;\r\n}\r\n\r\n@keyframes slideOut {\r\n from {\r\n opacity: 1;\r\n transform: translate(-50%, -50%);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translate(-50%, -100%);\r\n }\r\n}\r\n\r\n/* 顶部位置的滑动关闭动画 */\r\n.position-top.animation-slide.closing {\r\n animation: slideOutTop 0.3s ease;\r\n}\r\n\r\n@keyframes slideOutTop {\r\n from {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateY(-100%);\r\n }\r\n}\r\n\r\n/* 底部位置的滑动关闭动画 */\r\n.position-bottom.animation-slide.closing {\r\n animation: slideOutBottom 0.3s ease;\r\n}\r\n\r\n@keyframes slideOutBottom {\r\n from {\r\n opacity: 1;\r\n transform: translateY(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateY(100%);\r\n }\r\n}\r\n\r\n/* 左侧位置的滑动关闭动画 */\r\n.position-left.animation-slide.closing {\r\n animation: slideOutLeft 0.3s ease;\r\n}\r\n\r\n@keyframes slideOutLeft {\r\n from {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateX(-100%);\r\n }\r\n}\r\n\r\n/* 右侧位置的滑动关闭动画 */\r\n.position-right.animation-slide.closing {\r\n animation: slideOutRight 0.3s ease;\r\n}\r\n\r\n@keyframes slideOutRight {\r\n from {\r\n opacity: 1;\r\n transform: translateX(0);\r\n }\r\n to {\r\n opacity: 0;\r\n transform: translateX(100%);\r\n }\r\n}\r\n</style>\r\n","<template>\r\n <view ref=\"containerRef\" class=\"infinite-scroll-container\" @touchstart=\"handleTouchStart\" @touchmove=\"handleTouchMove\" @touchend=\"handleTouchEnd\" @scroll=\"handleScroll\">\r\n <!-- 下拉刷新指示器 -->\r\n <view\r\n class=\"refresh-indicator\"\r\n :style=\"{ height: `${pullDistance}px` }\"\r\n >\r\n <view v-if=\"refreshStatus !== 'normal' && (refreshStatus === 'refreshing' || pullDistance > 0)\" class=\"refresh-content\">\r\n <view class=\"refresh-icon\">\r\n <!-- 加载中图标 -->\r\n <text v-if=\"props.refreshLoading\" class=\"refresh-loading\">●</text>\r\n <!-- 下拉箭头图标 -->\r\n <text v-else class=\"refresh-arrow\">↓</text>\r\n </view>\r\n <text class=\"refresh-text\">\r\n <template v-if=\"props.refreshLoading\">{{ props.refreshingText }}</template>\r\n <template v-else-if=\"refreshStatus === 'triggered'\">{{ props.releaseText }}</template>\r\n <template v-else>{{ props.refreshText }}</template>\r\n </text>\r\n </view>\r\n </view>\r\n\r\n <!-- 内容区域 -->\r\n <view class=\"infinite-scroll-content\">\r\n <slot />\r\n </view>\r\n\r\n <!-- 加载指示器 -->\r\n <view class=\"infinite-scroll-indicator\">\r\n <!-- 加载中 -->\r\n <view v-if=\"loading\" class=\"infinite-scroll-loading\">\r\n <view class=\"loading-spinner\">\r\n <text class=\"loading-icon\">●</text>\r\n </view>\r\n <text class=\"loading-text\">{{ loadingText }}</text>\r\n </view>\r\n\r\n <!-- 加载失败 -->\r\n <view v-else-if=\"error\" class=\"infinite-scroll-error\">\r\n <text class=\"error-icon\">×</text>\r\n <text class=\"error-text\">{{ errorText }}</text>\r\n <view class=\"retry-btn\" @click=\"onRetry\">\r\n {{ retryText }}\r\n </view>\r\n </view>\r\n\r\n <!-- 无更多数据 -->\r\n <view v-else-if=\"!hasMore\" class=\"infinite-scroll-no-more\">\r\n <text class=\"no-more-text\">{{ noMoreText }}</text>\r\n </view>\r\n\r\n <!-- 隐藏的触发区域 -->\r\n <view\r\n v-else\r\n class=\"infinite-scroll-trigger\"\r\n :style=\"{ height: `0px` }\"\r\n />\r\n </view>\r\n </view>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'\r\n\r\n// Props\r\ninterface Props {\r\n // 加载状态(无限滚动)\r\n loading?: boolean\r\n // 下拉刷新加载状态\r\n refreshLoading?: boolean\r\n // 是否还有更多数据\r\n hasMore?: boolean\r\n // 加载失败状态\r\n error?: boolean\r\n // 触发无限滚动的阈值距离(像素)\r\n threshold?: number\r\n // 下拉刷新触发阈值(像素)\r\n refreshThreshold?: number\r\n // 节流时间(毫秒)\r\n throttleTime?: number\r\n // 加载中提示文本\r\n loadingText?: string\r\n // 加载失败提示文本\r\n errorText?: string\r\n // 重试按钮文本\r\n retryText?: string\r\n // 无更多数据提示文本\r\n noMoreText?: string\r\n // 下拉刷新提示文本\r\n refreshText?: string\r\n // 释放可刷新提示文本\r\n releaseText?: string\r\n // 正在刷新提示文本\r\n refreshingText?: string\r\n // 刷新成功提示文本\r\n refreshSuccessText?: string\r\n // 刷新失败提示文本\r\n refreshErrorText?: string\r\n // 是否使用窗口作为滚动容器\r\n useWindowScroll?: boolean\r\n // 是否启用无限滚动\r\n enableInfiniteScroll?: boolean\r\n // 是否启用下拉刷新\r\n enableRefresh?: boolean\r\n}\r\n\r\n// 设置默认属性\r\nconst props = withDefaults(defineProps<Props>(), {\r\n loading: false,\r\n refreshLoading: false,\r\n hasMore: true,\r\n error: false,\r\n threshold: 30,\r\n refreshThreshold: 40,\r\n throttleTime: 200,\r\n loadingText: '加载中...',\r\n errorText: '加载失败',\r\n retryText: '点击重试',\r\n noMoreText: '没有更多数据了',\r\n refreshText: '下拉刷新',\r\n releaseText: '释放可刷新',\r\n refreshingText: '正在刷新...',\r\n refreshSuccessText: '刷新成功',\r\n refreshErrorText: '刷新失败',\r\n useWindowScroll: false,\r\n enableInfiniteScroll: false,\r\n enableRefresh: true,\r\n})\r\n\r\n// Emits\r\nconst emit = defineEmits<{\r\n (e: 'loadMore'): void\r\n (e: 'retry'): void\r\n (e: 'refresh'): void\r\n}>()\r\n\r\n// Refs\r\nconst containerRef = ref<any>(null)\r\nlet scrollContainer: any = null\r\nlet lastScrollTime = 0\r\n\r\n// 下拉刷新相关状态\r\nconst pullDistance = ref(0) // 当前下拉距离\r\nconst startY = ref(0) // 触摸起始Y坐标\r\nconst isPulling = ref(false) // 是否正在下拉\r\nconst isRefreshing = ref(false) // 是否正在刷新\r\nconst refreshStatus = ref('normal') // 刷新状态:normal(正常)、pulling(下拉中)、triggered(已触发刷新)、refreshing(刷新中)\r\n\r\n// Methods\r\n/**\r\n * 获取滚动容器\r\n * @returns {any} - 滚动容器对象\r\n */\r\nfunction getScrollContainer() {\r\n if (props.useWindowScroll) {\r\n // #ifdef H5\r\n return window\r\n // #endif\r\n // 其他平台返回null或使用默认容器\r\n return null\r\n }\r\n return containerRef.value\r\n}\r\n\r\n/**\r\n * 节流函数,限制函数的执行频率\r\n * @param {Function} fn - 要执行的函数\r\n * @param {number} delay - 延迟时间(毫秒)\r\n * @returns {Function} - 节流后的函数\r\n */\r\nfunction throttle(fn, delay) {\r\n return (...args) => {\r\n const now = Date.now()\r\n if (now - lastScrollTime >= delay) {\r\n lastScrollTime = now\r\n fn(...args)\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查是否需要加载更多数据\r\n */\r\nfunction checkLoadMore() {\r\n // 如果无限滚动未启用,直接返回\r\n if (!props.enableInfiniteScroll) {\r\n return\r\n }\r\n\r\n if (props.loading || !props.hasMore || props.error) {\r\n return\r\n }\r\n\r\n let scrollTop = 0\r\n let scrollHeight = 0\r\n let clientHeight = 0\r\n\r\n if (props.useWindowScroll) {\r\n // #ifdef H5\r\n // 使用窗口作为滚动容器\r\n scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop\r\n scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight\r\n clientHeight = window.innerHeight\r\n // #endif\r\n }\r\n else {\r\n // 使用组件容器作为滚动容器\r\n if (!containerRef.value)\r\n return\r\n // 确保容器具有滚动相关属性\r\n if (typeof containerRef.value.scrollTop !== 'number'\r\n || typeof containerRef.value.scrollHeight !== 'number'\r\n || typeof containerRef.value.clientHeight !== 'number') {\r\n return\r\n }\r\n scrollTop = containerRef.value.scrollTop\r\n scrollHeight = containerRef.value.scrollHeight\r\n clientHeight = containerRef.value.clientHeight\r\n }\r\n\r\n // 检查是否滚动到了阈值范围内\r\n // 如果内容高度小于等于容器高度,说明内容不足一屏,应该直接触发加载更多\r\n if ((props.useWindowScroll ? scrollTop > 0 : true) && (scrollHeight <= clientHeight || scrollHeight - scrollTop - clientHeight <= props.threshold)) {\r\n // 触发加载更多事件\r\n emit('loadMore')\r\n }\r\n}\r\n\r\n/**\r\n * 触发重试加载事件\r\n */\r\nfunction onRetry() {\r\n emit('retry')\r\n}\r\n\r\n// 触摸开始事件处理\r\n/**\r\n * 处理触摸开始事件,用于初始化下拉刷新\r\n * @param {TouchEvent} e - 触摸事件对象\r\n */\r\nfunction handleTouchStart(e) {\r\n // 如果未启用下拉刷新或正在刷新,则不处理\r\n if (!props.enableRefresh || props.refreshLoading) {\r\n return\r\n }\r\n\r\n // 如果滚动容器不在顶部,则不处理下拉刷新\r\n let scrollTop = 0\r\n if (props.useWindowScroll) {\r\n // #ifdef H5\r\n scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop\r\n // #endif\r\n }\r\n else {\r\n scrollTop = containerRef.value?.scrollTop || 0\r\n }\r\n\r\n if (scrollTop > 0) {\r\n return\r\n }\r\n\r\n // 记录触摸起始位置\r\n startY.value = e.touches[0].clientY\r\n isPulling.value = true\r\n refreshStatus.value = 'pulling'\r\n}\r\n\r\n// 触摸移动事件处理\r\n/**\r\n * 处理触摸移动事件,用于计算下拉距离和更新刷新状态\r\n * @param {TouchEvent} e - 触摸事件对象\r\n */\r\nfunction handleTouchMove(e) {\r\n if (!isPulling.value || props.refreshLoading) {\r\n return\r\n }\r\n\r\n // 计算下拉距离\r\n const currentY = e.touches[0].clientY\r\n const distance = currentY - startY.value\r\n\r\n // 只有向下拉才处理\r\n if (distance <= 0) {\r\n // 如果拉到顶部以下,恢复正常状态\r\n if (pullDistance.value > 0) {\r\n pullDistance.value = 0\r\n refreshStatus.value = 'normal'\r\n }\r\n return\r\n }\r\n\r\n // 调整下拉距离计算,确保有足够空间实现垂直居中\r\n pullDistance.value = Math.min(distance * 0.5, props.refreshThreshold * 1.5)\r\n\r\n // 更新刷新状态\r\n if (pullDistance.value >= props.refreshThreshold) {\r\n refreshStatus.value = 'triggered'\r\n }\r\n else {\r\n refreshStatus.value = 'pulling'\r\n }\r\n\r\n // 阻止默认滚动行为\r\n e.preventDefault()\r\n}\r\n\r\n// 触摸结束事件处理\r\n/**\r\n * 处理触摸结束事件,用于判断是否触发刷新\r\n */\r\nfunction handleTouchEnd() {\r\n if (!isPulling.value) {\r\n return\r\n }\r\n\r\n isPulling.value = false\r\n\r\n // 如果下拉距离达到阈值,触发刷新\r\n if (pullDistance.value >= props.refreshThreshold && !props.refreshLoading) {\r\n isRefreshing.value = true\r\n refreshStatus.value = 'refreshing'\r\n emit('refresh')\r\n }\r\n else {\r\n // 否则恢复原状\r\n pullDistance.value = 0\r\n refreshStatus.value = 'normal'\r\n }\r\n}\r\n\r\n/**\r\n * 处理滚动事件,检查是否需要加载更多(节流处理)\r\n */\r\nconst handleScroll = throttle(() => {\r\n checkLoadMore()\r\n}, props.throttleTime)\r\n\r\n// 添加滚动事件监听\r\n/**\r\n * 添加滚动事件监听\r\n */\r\nfunction addScrollListener() {\r\n // 使用nextTick确保containerRef已初始化\r\n nextTick(() => {\r\n scrollContainer = getScrollContainer()\r\n if (scrollContainer && typeof scrollContainer.addEventListener === 'function') {\r\n scrollContainer.addEventListener('scroll', handleScroll, { passive: true })\r\n // 初始检查一次\r\n checkLoadMore()\r\n }\r\n })\r\n}\r\n\r\n// 移除滚动事件监听\r\n/**\r\n * 移除滚动事件监听\r\n */\r\nfunction removeScrollListener() {\r\n if (scrollContainer && typeof scrollContainer.removeEventListener === 'function') {\r\n scrollContainer.removeEventListener('scroll', handleScroll)\r\n scrollContainer = null\r\n }\r\n}\r\n\r\n// 监听容器大小变化\r\n/**\r\n * 处理容器大小变化事件,检查是否需要加载更多(节流处理)\r\n */\r\nconst handleResize = throttle(() => {\r\n checkLoadMore()\r\n}, props.throttleTime)\r\n\r\n// 监听加载状态变化\r\nwatch(() => props.loading, (newVal) => {\r\n if (!newVal) {\r\n // 加载完成后,检查是否还需要继续加载\r\n // 使用nextTick确保DOM已更新\r\n nextTick(() => {\r\n checkLoadMore()\r\n })\r\n }\r\n})\r\n\r\n// 监听错误状态变化\r\nwatch(() => props.error, (newVal) => {\r\n if (!newVal) {\r\n // 错误状态清除后,检查是否还需要继续加载\r\n // 使用nextTick确保DOM已更新\r\n nextTick(() => {\r\n checkLoadMore()\r\n })\r\n }\r\n})\r\n\r\n// 监听下拉刷新加载状态变化\r\nwatch(() => props.refreshLoading, (newVal) => {\r\n isRefreshing.value = newVal\r\n\r\n if (newVal) {\r\n refreshStatus.value = 'refreshing'\r\n }\r\n else {\r\n // 刷新完成后,先重置pullDistance开始回弹\r\n pullDistance.value = 0\r\n // 保持refreshStatus为'refreshing'直到回弹动画完成\r\n // 然后再将状态设置为'normal'\r\n // 使用标准setTimeout确保跨平台兼容\r\n setTimeout(() => {\r\n refreshStatus.value = 'normal'\r\n }, 300)\r\n }\r\n})\r\n\r\n// 生命周期\r\nonMounted(() => {\r\n // 添加滚动事件监听\r\n addScrollListener()\r\n\r\n // 添加窗口大小变化监听\r\n // #ifdef H5\r\n window.addEventListener('resize', handleResize, { passive: true })\r\n // #endif\r\n})\r\n\r\nonUnmounted(() => {\r\n // 移除滚动事件监听\r\n removeScrollListener()\r\n\r\n // 移除窗口大小变化监听\r\n // #ifdef H5\r\n window.removeEventListener('resize', handleResize)\r\n // #endif\r\n})\r\n</script>\r\n\r\n<style scoped>\r\n.infinite-scroll-container {\r\n position: relative;\r\n width: 100%;\r\n height: 100%;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n}\r\n\r\n.infinite-scroll-content {\r\n width: 100%;\r\n}\r\n\r\n.infinite-scroll-indicator {\r\n width: 100%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n.infinite-scroll-loading {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 20rpx;\r\n color: #666;\r\n font-size: 28rpx;\r\n}\r\n\r\n.loading-spinner {\r\n animation: spin 0.8s linear infinite;\r\n color: #409eff;\r\n}\r\n\r\n.loading-icon {\r\n font-size: 40rpx;\r\n}\r\n\r\n@keyframes spin {\r\n 0% {\r\n transform: rotate(0deg);\r\n }\r\n 100% {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n.loading-text {\r\n font-size: 28rpx;\r\n color: #666;\r\n}\r\n\r\n.infinite-scroll-error {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 20rpx;\r\n color: #f56c6c;\r\n font-size: 28rpx;\r\n}\r\n\r\n.error-icon {\r\n font-size: 40rpx;\r\n color: #f56c6c;\r\n}\r\n\r\n.error-text {\r\n font-size: 28rpx;\r\n color: #f56c6c;\r\n}\r\n\r\n.retry-btn {\r\n padding: 8rpx 24rpx;\r\n background-color: #409eff;\r\n color: white;\r\n border: none;\r\n border-radius: 8rpx;\r\n font-size: 24rpx;\r\n cursor: pointer;\r\n transition: background-color 0.3s ease;\r\n box-sizing: border-box;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.retry-btn:hover {\r\n background-color: #66b1ff;\r\n}\r\n\r\n.infinite-scroll-no-more {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: #999;\r\n font-size: 28rpx;\r\n}\r\n\r\n.no-more-text {\r\n font-size: 28rpx;\r\n color: #999;\r\n}\r\n\r\n.infinite-scroll-trigger {\r\n width: 100%;\r\n}\r\n\r\n/* 下拉刷新样式 */\r\n.refresh-indicator {\r\n width: 100%;\r\n height: 0;\r\n overflow: hidden;\r\n transition: height 0.3s ease;\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.refresh-content {\r\n display: flex;\r\n flex-direction: row;\r\n align-items: center;\r\n justify-content: center;\r\n width: 100%;\r\n padding: 16rpx 0;\r\n color: white;\r\n font-size: 26rpx;\r\n align-self: center;\r\n flex-shrink: 0;\r\n margin-top: 10rpx;\r\n font-weight: 500;\r\n text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.3);\r\n border-radius: 20rpx;\r\n margin: 8rpx;\r\n backdrop-filter: blur(10rpx);\r\n}\r\n\r\n.refresh-icon {\r\n margin-right: 10rpx;\r\n margin-bottom: 0;\r\n}\r\n\r\n.refresh-icon svg {\r\n transition: transform 0.3s ease;\r\n color: #409eff;\r\n width: 36rpx;\r\n height: 36rpx;\r\n}\r\n\r\n.refresh-status-triggered .refresh-icon svg,\r\n.refresh-status-refreshing .refresh-icon svg {\r\n transform: rotate(0deg);\r\n}\r\n\r\n.refresh-status-refreshing .refresh-icon svg {\r\n animation: spin 0.8s linear infinite;\r\n}\r\n\r\n.refresh-text {\r\n font-size: 28rpx;\r\n color: white;\r\n}\r\n</style>\r\n","<template>\r\n <view class=\"sw-swiper\">\r\n <view\r\n class=\"swiper-wrapper\"\r\n :style=\"{\r\n transform: `translateX(-${currentIndex * 100}%)`,\r\n transition: `transform ${duration}ms ease`,\r\n }\"\r\n >\r\n <view\r\n v-for=\"(item, index) in items\"\r\n :key=\"index\"\r\n class=\"swiper-item\"\r\n >\r\n <slot :item=\"item\" :index=\"index\" />\r\n </view>\r\n </view>\r\n\r\n <!-- 指示器 -->\r\n <view v-if=\"showIndicators\" class=\"swiper-indicators\">\r\n <view\r\n v-for=\"(item, index) in items\"\r\n :key=\"index\"\r\n class=\"indicator-item\"\r\n :class=\"{ active: index === currentIndex }\"\r\n @click=\"currentIndex = index\"\r\n />\r\n </view>\r\n\r\n <!-- 左右箭头 -->\r\n <view\r\n v-if=\"showArrows\"\r\n class=\"swiper-arrow swiper-arrow-left\"\r\n @click=\"prev\"\r\n />\r\n <view\r\n v-if=\"showArrows\"\r\n class=\"swiper-arrow swiper-arrow-right\"\r\n @click=\"next\"\r\n />\r\n </view>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { onMounted, onUnmounted, ref, watch } from 'vue'\r\n\r\ninterface Props {\r\n items: any[]\r\n initialIndex?: number\r\n autoPlay?: boolean\r\n interval?: number\r\n duration?: number\r\n showIndicators?: boolean\r\n showArrows?: boolean\r\n loop?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n initialIndex: 0,\r\n autoPlay: true,\r\n interval: 3000,\r\n duration: 300,\r\n showIndicators: true,\r\n showArrows: false,\r\n loop: true,\r\n})\r\n\r\nconst emit = defineEmits<{\r\n (e: 'change', index: number): void\r\n (e: 'prev', index: number): void\r\n (e: 'next', index: number): void\r\n}>()\r\n\r\nconst currentIndex = ref(props.initialIndex)\r\nlet timer: any = null\r\n\r\n// 自动播放\r\nfunction startAutoPlay() {\r\n if (props.autoPlay && props.items.length > 1) {\r\n timer = setInterval(() => {\r\n next()\r\n }, props.interval)\r\n }\r\n}\r\n\r\nfunction stopAutoPlay() {\r\n if (timer) {\r\n clearInterval(timer)\r\n timer = null\r\n }\r\n}\r\n\r\n// 上一张\r\nfunction prev() {\r\n if (props.loop) {\r\n currentIndex.value = currentIndex.value === 0 ? props.items.length - 1 : currentIndex.value - 1\r\n }\r\n else if (currentIndex.value > 0) {\r\n currentIndex.value--\r\n }\r\n emit('prev', currentIndex.value)\r\n emit('change', currentIndex.value)\r\n}\r\n\r\n// 下一张\r\nfunction next() {\r\n if (props.loop) {\r\n currentIndex.value = currentIndex.value === props.items.length - 1 ? 0 : currentIndex.value + 1\r\n }\r\n else if (currentIndex.value < props.items.length - 1) {\r\n currentIndex.value++\r\n }\r\n emit('next', currentIndex.value)\r\n emit('change', currentIndex.value)\r\n}\r\n\r\n// 监听items变化\r\nwatch(() => props.items, (newItems) => {\r\n if (newItems.length < currentIndex.value + 1) {\r\n currentIndex.value = Math.min(currentIndex.value, newItems.length - 1)\r\n }\r\n stopAutoPlay()\r\n startAutoPlay()\r\n}, { deep: true })\r\n\r\n// 监听自动播放设置\r\nwatch(() => props.autoPlay, (autoPlay) => {\r\n if (autoPlay) {\r\n startAutoPlay()\r\n }\r\n else {\r\n stopAutoPlay()\r\n }\r\n})\r\n\r\nonMounted(() => {\r\n startAutoPlay()\r\n})\r\n\r\nonUnmounted(() => {\r\n stopAutoPlay()\r\n})\r\n\r\n// 暴露方法\r\ndefineExpose({\r\n prev,\r\n next,\r\n currentIndex,\r\n})\r\n</script>\r\n\r\n<style lang=\"less\" scoped>\r\n// 轮播图组件样式\r\n.sw-swiper {\r\n position: relative;\r\n width: 100%;\r\n overflow: hidden;\r\n\r\n .swiper-wrapper {\r\n display: flex;\r\n width: 100%;\r\n height: 100%;\r\n }\r\n\r\n .swiper-item {\r\n flex-shrink: 0;\r\n width: 100%;\r\n height: 100%;\r\n }\r\n\r\n // 指示器\r\n .swiper-indicators {\r\n position: absolute;\r\n bottom: 32rpx;\r\n left: 50%;\r\n transform: translateX(-50%);\r\n display: flex;\r\n gap: 16rpx;\r\n\r\n .indicator-item {\r\n width: 16rpx;\r\n height: 16rpx;\r\n border-radius: 50%;\r\n background-color: rgba(255, 255, 255, 0.5);\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n\r\n &.active {\r\n width: 16rpx;\r\n height: 16rpx;\r\n background-color: #fff;\r\n }\r\n }\r\n }\r\n\r\n // 箭头\r\n .swiper-arrow {\r\n position: absolute;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n width: 76rpx;\r\n height: 76rpx;\r\n border-radius: 50%;\r\n background-color: rgba(255, 255, 255, 0.85);\r\n border: none;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n z-index: 10;\r\n transition: all 0.3s ease;\r\n box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.15);\r\n\r\n &:hover {\r\n background-color: rgba(255, 255, 255, 1);\r\n transform: translateY(-50%) scale(1.1);\r\n }\r\n\r\n &:before {\r\n content: '';\r\n width: 24rpx;\r\n height: 24rpx;\r\n border-top: 4rpx solid #333;\r\n border-left: 4rpx solid #333;\r\n display: inline-block;\r\n }\r\n\r\n &.swiper-arrow-left {\r\n left: 30rpx;\r\n\r\n &:before {\r\n transform: rotate(-45deg);\r\n }\r\n }\r\n\r\n &.swiper-arrow-right {\r\n right: 30rpx;\r\n\r\n &:before {\r\n transform: rotate(135deg);\r\n }\r\n }\r\n }\r\n}\r\n</style>\r\n","<template>\r\n <view\r\n v-show=\"showToast\"\r\n ref=\"toastRef\"\r\n class=\"sw-toast\"\r\n :class=\"[\r\n `sw-toast--${type}`,\r\n `sw-toast--${position}`,\r\n { 'sw-toast--large': large },\r\n { 'sw-toast--text': !showIcon && type === 'text' },\r\n { 'sw-toast--show': showToast && !isClosing },\r\n { 'sw-toast--hide': showToast && isClosing },\r\n ]\"\r\n :style=\"customStyle\"\r\n @click=\"onClick\"\r\n @transitionend=\"handleTransitionEnd\"\r\n >\r\n <!-- 加载图标 -->\r\n <view v-if=\"loading\" class=\"sw-toast__loading\">\r\n <text v-if=\"!customIcon\" class=\"sw-toast__spinner\" />\r\n <text v-else class=\"sw-toast__custom-icon\">{{ customIcon }}</text>\r\n </view>\r\n\r\n <!-- 状态图标 -->\r\n <view v-else-if=\"showIcon\" class=\"sw-toast__icon\">\r\n <text v-if=\"type === 'success'\" class=\"sw-toast__icon--success iconfont icon-zhengqueshixin\" />\r\n <text v-else-if=\"type === 'fail'\" class=\"sw-toast__icon--fail iconfont icon-shanchu\" />\r\n <text v-else-if=\"type === 'warning'\" class=\"sw-toast__icon--warning iconfont icon-jurassic_warn\" />\r\n <text v-else-if=\"customIcon\" class=\"sw-toast__custom-icon\">{{ customIcon }}</text>\r\n </view>\r\n\r\n <!-- 文本内容 -->\r\n <text v-if=\"displayMessage\" class=\"sw-toast__text\" :class=\"{ 'sw-toast__text--long': longText }\">{{ displayMessage }}</text>\r\n </view>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, onMounted, onUnmounted, ref, watch } from 'vue'\r\n\r\n// Props\r\ninterface Props {\r\n /**\r\n * 控制显示/隐藏(支持v-model)\r\n */\r\n modelValue?: boolean\r\n /**\r\n * 提示类型\r\n */\r\n type?: 'text' | 'loading' | 'success' | 'fail' | 'warning'\r\n /**\r\n * 显示位置\r\n */\r\n position?: 'top' | 'middle' | 'bottom'\r\n /**\r\n * 提示文字\r\n */\r\n message?: string\r\n /**\r\n * 自动关闭时间(毫秒),0表示不会自动关闭\r\n */\r\n duration?: number\r\n /**\r\n * 是否显示加载图标\r\n */\r\n loading?: boolean\r\n /**\r\n * 是否显示图标\r\n */\r\n icon?: boolean\r\n /**\r\n * 自定义图标\r\n */\r\n customIcon?: string\r\n /**\r\n * 是否为大尺寸\r\n */\r\n large?: boolean\r\n /**\r\n * 自定义样式\r\n */\r\n customStyle?: Record<string, any>\r\n /**\r\n * 是否开启倒计时功能\r\n */\r\n countdown?: boolean\r\n /**\r\n * 倒计时总时间(秒)\r\n */\r\n countdownSeconds?: number\r\n /**\r\n * 是否在倒计时数字外添加括号\r\n */\r\n countdownParentheses?: boolean\r\n /**\r\n * 倒计时后追加的文字描述\r\n */\r\n countdownSuffix?: string\r\n /**\r\n * 是否开启长文本模式(允许换行)\r\n */\r\n longText?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n modelValue: false,\r\n type: 'text',\r\n position: 'middle',\r\n message: '',\r\n duration: 3000,\r\n loading: false,\r\n icon: true,\r\n customIcon: '',\r\n large: false,\r\n customStyle: () => ({}),\r\n countdown: false,\r\n countdownSeconds: 5,\r\n countdownParentheses: false,\r\n countdownSuffix: '',\r\n longText: false,\r\n})\r\n\r\n// Emits\r\nconst emit = defineEmits<{\r\n /**\r\n * 显示状态变化时触发\r\n */\r\n (e: 'update:modelValue', value: boolean): void\r\n /**\r\n * 关闭时触发\r\n */\r\n (e: 'close'): void\r\n /**\r\n * 点击时触发\r\n */\r\n (e: 'click'): void\r\n}>()\r\n\r\n// Refs\r\nconst toastRef = ref<any>(null)\r\nlet timer: number | null | ReturnType<typeof setTimeout> = null\r\nlet countdownTimer: number | null | ReturnType<typeof setInterval> = null\r\n\r\n// 内部状态\r\nconst showToast = ref(props.modelValue)\r\nconst isClosing = ref(false)\r\nconst currentCountdown = ref(props.countdownSeconds)\r\nconst originalMessage = ref(props.message)\r\n\r\n// 计算是否需要显示图标\r\nconst showIcon = computed(() => {\r\n if (props.loading)\r\n return true\r\n if (props.type === 'text')\r\n return props.icon\r\n return true\r\n})\r\n\r\n// 计算实际显示的消息(包含倒计时)\r\nconst displayMessage = computed(() => {\r\n if (props.countdown && showToast.value) {\r\n // 构建倒计时部分\r\n let countdownPart = ''\r\n if (props.countdownParentheses) {\r\n // 带括号的倒计时格式:(10秒后返回首页)\r\n countdownPart = `(${currentCountdown.value}秒${props.countdownSuffix})`\r\n }\r\n else {\r\n // 不带括号的倒计时格式:10秒后返回首页\r\n countdownPart = `${currentCountdown.value}秒${props.countdownSuffix}`\r\n }\r\n return `${originalMessage.value || ''} ${countdownPart}`\r\n }\r\n return props.message\r\n})\r\n\r\n// 点击事件处理\r\nfunction onClick() {\r\n emit('click')\r\n}\r\n\r\n// 关闭Toast\r\nfunction close() {\r\n if (!showToast.value)\r\n return\r\n\r\n // 清除倒计时定时器\r\n clearCountdownTimer()\r\n // 添加关闭动画\r\n isClosing.value = true\r\n emit('close')\r\n}\r\n\r\n// 自动关闭定时器\r\nfunction startTimer() {\r\n // 清除之前的定时器\r\n clearTimer()\r\n\r\n // 计算实际使用的duration:如果开启倒计时且countdownSeconds>0,则使用countdownSeconds*1000,否则使用props.duration\r\n const actualDuration = props.countdown && props.countdownSeconds > 0 ? props.countdownSeconds * 1000 : props.duration\r\n\r\n if (actualDuration > 0 && !props.loading) {\r\n timer = setTimeout(() => {\r\n close()\r\n }, actualDuration)\r\n }\r\n}\r\n\r\n// 清除定时器\r\nfunction clearTimer() {\r\n if (timer) {\r\n clearTimeout(timer)\r\n timer = null\r\n }\r\n}\r\n\r\n// 开始倒计时\r\nfunction startCountdown() {\r\n // 清除之前的倒计时定时器\r\n clearCountdownTimer()\r\n\r\n if (props.countdown && props.countdownSeconds > 0) {\r\n currentCountdown.value = props.countdownSeconds\r\n originalMessage.value = props.message\r\n\r\n countdownTimer = setInterval(() => {\r\n currentCountdown.value--\r\n if (currentCountdown.value <= 0) {\r\n clearCountdownTimer()\r\n // 如果开启了自动关闭,倒计时结束后关闭Toast\r\n if (props.duration > 0) {\r\n close()\r\n }\r\n }\r\n }, 1000)\r\n }\r\n}\r\n\r\n// 清除倒计时定时器\r\nfunction clearCountdownTimer() {\r\n if (countdownTimer) {\r\n clearInterval(countdownTimer)\r\n countdownTimer = null\r\n }\r\n // 重置当前倒计时\r\n currentCountdown.value = props.countdownSeconds\r\n}\r\n\r\n// 过渡结束处理\r\nfunction handleTransitionEnd() {\r\n if (isClosing.value) {\r\n showToast.value = false\r\n isClosing.value = false\r\n emit('update:modelValue', false)\r\n }\r\n}\r\n\r\n// 监听modelValue变化\r\nwatch(() => props.modelValue, (newVal) => {\r\n if (newVal) {\r\n // 显示Toast\r\n showToast.value = true\r\n isClosing.value = false\r\n startTimer()\r\n // 如果开启了倒计时,开始倒计时\r\n if (props.countdown) {\r\n startCountdown()\r\n }\r\n }\r\n else {\r\n // 隐藏Toast\r\n close()\r\n }\r\n})\r\n\r\n// 监听message变化,重新启动定时器\r\nwatch(() => props.message, () => {\r\n if (showToast.value && !props.loading) {\r\n startTimer()\r\n // 如果开启了倒计时,更新原始消息\r\n if (props.countdown) {\r\n originalMessage.value = props.message\r\n }\r\n }\r\n})\r\n\r\n// 监听countdown或countdownSeconds变化,重新开始倒计时\r\nwatch([() => props.countdown, () => props.countdownSeconds], () => {\r\n if (showToast.value && props.countdown) {\r\n startCountdown()\r\n }\r\n else {\r\n clearCountdownTimer()\r\n }\r\n})\r\n\r\n// 生命周期\r\nonMounted(() => {\r\n if (props.modelValue) {\r\n startTimer()\r\n // 如果开启了倒计时,开始倒计时\r\n if (props.countdown) {\r\n startCountdown()\r\n }\r\n }\r\n})\r\n\r\nonUnmounted(() => {\r\n clearTimer()\r\n clearCountdownTimer()\r\n})\r\n\r\n// 暴露方法\r\ndefineExpose({\r\n /**\r\n * 手动关闭Toast\r\n */\r\n close,\r\n})\r\n</script>\r\n\r\n<style scoped>\r\n.sw-toast {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 24rpx 40rpx;\r\n border-radius: 12rpx;\r\n font-size: 28rpx;\r\n line-height: 80rpx;\r\n color: #fff;\r\n background-color: rgba(0, 0, 0, 0.7);\r\n box-sizing: border-box;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n z-index: 9999;\r\n position: fixed;\r\n left: 50%;\r\n transform: translateX(-50%) translateY(0);\r\n user-select: none;\r\n opacity: 0;\r\n visibility: hidden;\r\n pointer-events: none;\r\n border: 2rpx solid rgba(255, 255, 255, 0.2);\r\n}\r\n\r\n/* 显示动画 */\r\n.sw-toast--show {\r\n opacity: 1;\r\n visibility: visible;\r\n pointer-events: auto;\r\n transform: translateX(-50%) translateY(0);\r\n}\r\n\r\n/* 隐藏动画 */\r\n.sw-toast--hide {\r\n opacity: 0;\r\n transform: translateX(-50%) translateY(-20rpx);\r\n}\r\n\r\n/* 位置样式 */\r\n.sw-toast--top {\r\n top: 100rpx;\r\n}\r\n\r\n.sw-toast--middle {\r\n top: 50%;\r\n transform: translate(-50%, -50%);\r\n}\r\n\r\n.sw-toast--middle.sw-toast--show {\r\n transform: translate(-50%, -50%);\r\n}\r\n\r\n.sw-toast--middle.sw-toast--hide {\r\n transform: translate(-50%, -60%);\r\n}\r\n\r\n.sw-toast--bottom {\r\n bottom: 100rpx;\r\n}\r\n\r\n/* 类型样式 */\r\n.sw-toast--text {\r\n background-color: rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n.sw-toast--loading {\r\n background-color: rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n.iconfont {\r\n font-size: 40rpx;\r\n vertical-align: middle;\r\n}\r\n.sw-toast--success {\r\n background-color: rgba(240, 249, 235, 1);\r\n height: 80rpx;\r\n border-width: 0rpx;\r\n border-radius: 12rpx;\r\n border-width: 2rpx;\r\n border-style: solid;\r\n box-sizing: border-box;\r\n text-align: left;\r\n font-size: 28rpx;\r\n color: #67c23a;\r\n border-color: rgba(227, 244, 218, 1);\r\n}\r\n\r\n.sw-toast--fail {\r\n background-color: #f5222d;\r\n border-width: 0rpx;\r\n background-color: rgba(254, 240, 240, 1);\r\n box-sizing: border-box;\r\n border-width: 2rpx;\r\n border-style: solid;\r\n border-color: rgba(253, 226, 226, 1);\r\n border-radius: 12rpx;\r\n height: 80rpx;\r\n display: flex;\r\n text-align: left;\r\n font-size: 28rpx;\r\n color: #f56c6c;\r\n}\r\n\r\n.sw-toast--warning {\r\n background-color: rgba(253, 246, 236, 1);\r\n height: 80rpx;\r\n border-width: 0rpx;\r\n border-radius: 12rpx;\r\n border-width: 2rpx;\r\n border-style: solid;\r\n box-sizing: border-box;\r\n text-align: left;\r\n font-size: 28rpx;\r\n color: #e6a23c;\r\n border-color: rgba(255, 236, 208, 1);\r\n}\r\n\r\n/* 大尺寸样式 */\r\n.sw-toast--large {\r\n padding: 32rpx 48rpx;\r\n font-size: 32rpx;\r\n line-height: 48rpx;\r\n}\r\n\r\n/* 加载图标 */\r\n.sw-toast__loading {\r\n margin-right: 16rpx;\r\n}\r\n\r\n/* 加载动画 */\r\n.sw-toast__spinner {\r\n display: inline-block;\r\n width: 40rpx;\r\n height: 40rpx;\r\n border: 4rpx solid rgba(255, 255, 255, 0.3);\r\n border-radius: 50%;\r\n border-top-color: #fff;\r\n animation: sw-toast-spin 0.8s linear infinite;\r\n}\r\n\r\n/* 自定义图标 */\r\n.sw-toast__custom-icon {\r\n font-size: 40rpx;\r\n}\r\n\r\n/* 状态图标 */\r\n.sw-toast__icon {\r\n margin-right: 16rpx;\r\n\r\n &--success,\r\n &--fail,\r\n &--warning {\r\n display: inline-block;\r\n width: 40rpx;\r\n height: 40rpx;\r\n font-size: 32rpx;\r\n font-weight: bold;\r\n text-align: center;\r\n line-height: 40rpx;\r\n }\r\n}\r\n\r\n/* 文本内容 */\r\n.sw-toast__text {\r\n margin: 0;\r\n white-space: nowrap;\r\n width: auto;\r\n}\r\n\r\n/* 长文本模式样式 */\r\n.sw-toast__text--long {\r\n word-wrap: break-word;\r\n overflow-wrap: break-word;\r\n white-space: pre-wrap;\r\n width: 100%;\r\n word-break: break-all;\r\n}\r\n\r\n/* 加载动画 */\r\n@keyframes sw-toast-spin {\r\n to {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n/* 响应式设计 */\r\n.sw-toast {\r\n max-width: 100%;\r\n padding: 16rpx 24rpx;\r\n font-size: 26rpx;\r\n line-height: 36rpx;\r\n}\r\n\r\n.sw-toast--large {\r\n padding: 24rpx 40rpx;\r\n font-size: 30rpx;\r\n line-height: 44rpx;\r\n}\r\n\r\n.sw-toast--top {\r\n top: 60rpx;\r\n}\r\n\r\n.sw-toast--bottom {\r\n bottom: 60rpx;\r\n}\r\n</style>\r\n","// 统一导出所有 MyComs 组件\r\n\r\n// 基础组件\r\nimport Button from './Button/index.vue'\r\n\r\nimport Dialog from './Dialog/index.vue'\r\nimport Divider from './Divider/index.vue'\r\n// 导出组合式函数\r\nimport { useDropdownSelect } from './DropdownSelect/dropdownSelect'\r\n// 表单组件\r\nimport DropdownSelect from './DropdownSelect/index.vue'\r\nimport { useDropdownWithBadge } from './DropdownWithBadge/dropdownWithBadge'\r\nimport DropdownWithBadge from './DropdownWithBadge/index.vue'\r\n\r\n// 筛选组件\r\nimport FilterDrawer from './FilterDrawer/index.vue'\r\nimport { useFilterDrawer } from './FilterDrawer/useFilterDrawer'\r\n\r\n// 交互组件\r\nimport InfiniteScroll from './InfiniteScroll/index.vue'\r\nimport Popup from './Popup/index.vue'\r\nimport PullRefresh from './PullRefresh/index.vue'\r\n\r\nimport Swiper from './Swiper/index.vue'\r\nimport Toast from './Toast/index.vue'\r\n// 导入字体图标样式\r\nimport '@/assets/font/iconfont.css'\r\n\r\nexport type { DropdownSelectEmits, DropdownSelectProps } from './DropdownSelect/type'\r\n\r\n// 导出所有类型\r\nexport type * from './DropdownSelect/type'\r\nexport type { DropdownWithBadgeEmits, DropdownWithBadgeProps } from './DropdownWithBadge/type'\r\nexport type * from './DropdownWithBadge/type'\r\n\r\nexport type { FilterDrawerEmits, FilterDrawerProps } from './FilterDrawer/type'\r\nexport type * from './FilterDrawer/type'\r\n\r\n// 命名导出\r\nexport {\r\n Button,\r\n Dialog,\r\n Divider,\r\n DropdownSelect,\r\n DropdownWithBadge,\r\n FilterDrawer,\r\n InfiniteScroll,\r\n Popup,\r\n PullRefresh,\r\n Swiper,\r\n Toast,\r\n useDropdownSelect,\r\n useDropdownWithBadge,\r\n useFilterDrawer,\r\n}\r\n\r\n// 默认导出,包含所有组件\r\nexport default {\r\n Button,\r\n Dialog,\r\n Divider,\r\n useDropdownSelect,\r\n DropdownSelect,\r\n useDropdownWithBadge,\r\n DropdownWithBadge,\r\n FilterDrawer,\r\n useFilterDrawer,\r\n InfiniteScroll,\r\n Popup,\r\n PullRefresh,\r\n Swiper,\r\n Toast,\r\n}\r\n"],"names":["_createElementBlock","_openBlock","_hoisted_2","_createElementVNode","_Fragment","$slots","_hoisted_3","_renderSlot","_normalizeClass","_hoisted_4","_hoisted_1","_toDisplayString","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","index","_normalizeProps","_guardReactiveProps","_unref","_createBlock","DropdownSelect","_mergeProps","modelValue","options","_toHandlers","_withCtx","iconName","_withDirectives","_createTextVNode","_normalizeStyle","_renderList","DropdownWithBadge","FilterDrawer"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "uniapp-dyckui",
3
3
  "type": "module",
4
- "version": "4.1.8",
4
+ "version": "4.1.10",
5
5
  "unibest-version": "4.1.1",
6
6
  "unibest-update-time": "2025-12-04",
7
7
  "packageManager": "pnpm@10.10.0",