123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- <template>
- <view class="fs-modal">
- <view class="fs-modal-box" :class="{ show: modelValue }" :style="{ width }">
- <view class="fs-modal-title title" v-if="showTitle">{{ title }}</view>
- <view class="fs-modal-content">
- <slot>{{ content }}</slot>
- </view>
- <view class="fs-modal-ft">
- <view class="fs-modal-ft-btn fs-modal-ft-cancel" v-if="showCancel" @click="handleCancel">{{ cancelText }}</view>
- <view
- v-if="showConfirm"
- class="fs-modal-ft-btn fs-modal-ft-confirm"
- :class="[confirmTextColorType]"
- :style="{ color: confirmTextColor }"
- @click="handleConfirm"
- >
- <view class="fs-loader" v-if="loading"></view>
- <template v-else>
- {{ confirmText }}
- </template>
- </view>
- </view>
- <view class="fs-modal-close" v-if="showClose" @click="handleClose">
- <fs-icon type="icon-close-circle" color="#fff" size="50rpx"></fs-icon>
- </view>
- </view>
- <fs-mask
- :modelValue="modelValue"
- @close="handleClose"
- :maskClickable="maskClickable"
- :blurable="blurable"
- ></fs-mask>
- </view>
- </template>
- <script>
- /**
- * 模态框组件
- * @description 模态框组件
- * @property {String} width 模态框宽度
- * @property {String} title 标题
- * @property {Boolean} showTitle 是否显示标题
- * @property {Boolean} maskClickable 点击遮罩是否可关闭
- * @property {String} content 内容
- * @property {Boolean} blurable 毛玻璃效果
- * @property {Boolean} showClose 是否显示关闭
- * @property {Function} beforeClose 异步关闭
- * @property {String} cancelText 取消按钮文字
- * @property {Boolean} showCancel 是否显示取消按钮
- * @property {String} confirmText 确定按钮文字
- * @property {Boolean} showConfirm 是否显示确定按钮
- * @property {String} confirmTextColor 确定按钮文字颜色
- * @property {Boolean} confirmTextColorType = [primary | danger | warning | info | success] 确定按钮文字颜色类型
- * @event {Function} confirm 确定事件
- * @event {Function} cancel 取消事件
- */
- export default {
- name: 'fs-model'
- }
- </script>
- <script setup>
- import { ref } from 'vue'
- const props = defineProps({
- modelValue: Boolean,
- width: {
- type: String,
- default: '80vw'
- },
- maskClickable: {
- type: Boolean,
- default: true
- },
- blurable: Boolean,
- title: {
- type: String,
- default: '提示'
- },
- showTitle: {
- type: Boolean,
- default: true
- },
- content: String,
- showCancel: {
- type: Boolean,
- default: true
- },
- cancelText: {
- type: String,
- default: '取消'
- },
- showConfirm: {
- type: Boolean,
- default: true
- },
- confirmText: {
- type: String,
- default: '确定'
- },
- confirmTextColor: {
- type: String
- },
- confirmTextColorType: {
- type: String,
- default: 'primary',
- validator(value) {
- return ['primary', 'success', 'info', 'warning', 'danger'].includes(value)
- }
- },
- showClose: {
- type: Boolean,
- default: false
- },
- beforeClose: Function
- })
- const emits = defineEmits(['update:modelValue', 'confirm', 'cancel'])
- const loading = ref(false)
- const handleClose = () => {
- emits('update:modelValue', false)
- }
- const handleMask = () => {
- if (props.maskClickable) {
- handleClose()
- }
- }
- const handleCancel = () => {
- handleClose()
- emits('cancel')
- }
- const handleConfirm = () => {
- if (props.beforeClose) {
- loading.value = true
- props.beforeClose('confirm', (flag = true) => {
- loading.value = false
- flag && handleClose()
- })
- } else {
- handleClose()
- emits('confirm', false)
- }
- }
- </script>
- <style lang="scss" scoped>
- .fs-modal {
- &-box {
- position: fixed;
- background-color: #fff;
- z-index: 900;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%) scale(0);
- border-radius: var(--radius);
- &.show {
- transform: translate(-50%, -50%) scale(1);
- }
- }
- &-title {
- padding: 20rpx;
- text-align: center;
- }
- &-content {
- padding: 20rpx 20rpx 40rpx;
- text-align: center;
- }
- &-ft {
- display: flex;
- &-btn {
- flex: 1;
- height: 80rpx;
- line-height: 80rpx;
- text-align: center;
- border-top: 2rpx solid var(--border-color);
- cursor: pointer;
- & + & {
- border-left: 2rpx solid var(--border-color);
- }
- }
- }
- &-close {
- position: absolute;
- top: -110rpx;
- left: 50%;
- transform: translateX(-50%);
- &::before {
- position: absolute;
- bottom: 0;
- left: 50%;
- transform: translate(-50%, 100%);
- height: 60rpx;
- width: 2rpx;
- background-color: #fff;
- content: '';
- }
- }
- }
- .fs-loader {
- display: inline-block;
- width: 40rpx;
- height: 40rpx;
- color: inherit;
- vertical-align: middle;
- border: 4rpx solid currentcolor;
- border-bottom-color: transparent;
- border-radius: 50%;
- animation: 1s loader linear infinite;
- position: relative;
- }
- @keyframes loader {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
- }
- </style>
|