首次提交:初始化项目代码
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
<template>
|
||||
<view class="center-modal" v-if="visible">
|
||||
<!-- 遮罩层:点击关闭。mask 与 dialog-wrap 是兄弟节点,点击不会相互干扰 -->
|
||||
<view class="cm-mask" @click="handleMaskClick" hover-stop-propagation></view>
|
||||
<!-- 弹窗容器,绝对定位居中 -->
|
||||
<view class="cm-dialog-wrap">
|
||||
<!-- 弹窗主体 -->
|
||||
<view class="cm-dialog" :style="{ backgroundColor: themeConfig.cardBgColor }">
|
||||
<view class="cm-header" v-if="title || $slots.header">
|
||||
<slot name="header">
|
||||
<text class="cm-title" :style="{ color: themeConfig.textPrimary }">{{ title }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
<view class="cm-body" :style="{ maxHeight: maxBodyHeight }">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view class="cm-footer" v-if="showCancel || showConfirm || $slots.footer">
|
||||
<slot name="footer">
|
||||
<view
|
||||
v-if="showCancel"
|
||||
class="cm-btn cm-btn--cancel"
|
||||
:style="{ background: cancelBg, color: themeConfig.textPrimary }"
|
||||
hover-class="cm-btn--cancel--active"
|
||||
:hover-stay-time="100"
|
||||
@click="handleCancel"
|
||||
>
|
||||
<text class="cm-btn__text">{{ cancelText }}</text>
|
||||
</view>
|
||||
<view
|
||||
v-if="showConfirm"
|
||||
class="cm-btn cm-btn--confirm"
|
||||
:class="{ 'cm-btn--disabled': confirmDisabled }"
|
||||
:style="confirmBtnStyle"
|
||||
hover-class="cm-btn--confirm--active"
|
||||
:hover-stay-time="100"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
<text class="cm-btn__text">{{ confirmText }}</text>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 关闭按钮:放在弹窗容器内,与 dialog 平级,避免被 dialog 拦截事件 -->
|
||||
<view
|
||||
v-if="showClose"
|
||||
class="cm-close"
|
||||
:style="{ background: themeConfig.cardBgColor, color: themeConfig.textTertiary, boxShadow: `0 6rpx 20rpx ${themeConfig.shadowColor || 'rgba(0,0,0,0.15)'}` }"
|
||||
hover-class="cm-close--active"
|
||||
:hover-stay-time="100"
|
||||
@click="handleClose"
|
||||
>
|
||||
<FaIcon icon="xmark" :size="22" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FaIcon from './FaIcon.vue'
|
||||
import themeMixin from '@/mixins/themeMixin.js'
|
||||
|
||||
export default {
|
||||
name: 'CenterModal',
|
||||
components: { FaIcon },
|
||||
mixins: [themeMixin],
|
||||
props: {
|
||||
visible: { type: Boolean, default: false },
|
||||
title: { type: String, default: '' },
|
||||
showClose: { type: Boolean, default: true },
|
||||
showCancel: { type: Boolean, default: true },
|
||||
showConfirm: { type: Boolean, default: true },
|
||||
maskClosable: { type: Boolean, default: true },
|
||||
cancelText: { type: String, default: '取消' },
|
||||
confirmText: { type: String, default: '确定' },
|
||||
confirmColor: { type: String, default: '' },
|
||||
confirmDisabled: { type: Boolean, default: false },
|
||||
maxBodyHeight: { type: String, default: '70vh' }
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* 确认按钮样式:直接应用主题渐变色(不依赖 CSS 变量),确保主题切换立即生效
|
||||
*/
|
||||
confirmBtnStyle() {
|
||||
const bg = this.confirmColor || this.themeConfig.gradientSoft
|
||||
return {
|
||||
background: bg,
|
||||
color: '#ffffff',
|
||||
boxShadow: `0 6rpx 18rpx ${this.themeConfig.shadowColor || 'rgba(198, 142, 63, 0.35)'}`
|
||||
}
|
||||
},
|
||||
cancelBg() {
|
||||
// 跟随主题色:使用 inputBgColor 创造与主题协调的次按钮背景
|
||||
return this.themeConfig.inputBgColor || 'rgba(0, 0, 0, 0.06)'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleMaskClick() {
|
||||
if (!this.maskClosable) return
|
||||
this.handleClose()
|
||||
},
|
||||
handleClose() {
|
||||
if (!this.visible) return
|
||||
this.$emit('update:visible', false)
|
||||
this.$emit('close')
|
||||
},
|
||||
handleCancel() {
|
||||
if (!this.visible) return
|
||||
this.$emit('update:visible', false)
|
||||
this.$emit('close')
|
||||
this.$emit('cancel')
|
||||
},
|
||||
handleConfirm() {
|
||||
if (!this.visible) return
|
||||
if (this.confirmDisabled) return
|
||||
this.$emit('confirm')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.center-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.cm-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
animation: cm-fadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes cm-fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
/* 弹窗容器:用绝对定位 + transform 居中,简单可靠 */
|
||||
.cm-dialog-wrap {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: cm-zoomIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
@keyframes cm-zoomIn {
|
||||
from { transform: translate(-50%, -50%) scale(0.92); opacity: 0; }
|
||||
to { transform: translate(-50%, -50%) scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
.cm-dialog {
|
||||
position: relative;
|
||||
width: 80vw;
|
||||
max-width: 600rpx;
|
||||
max-height: 85vh;
|
||||
border-radius: 24rpx;
|
||||
padding: 56rpx 40rpx 40rpx;
|
||||
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.18);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cm-header {
|
||||
text-align: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.cm-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.cm-body {
|
||||
font-size: 28rpx;
|
||||
line-height: 1.6;
|
||||
min-height: 60rpx;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.cm-footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 24rpx;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
/* 关闭按钮:紧贴弹窗右上角外侧 */
|
||||
.cm-close {
|
||||
position: absolute;
|
||||
top: -28rpx;
|
||||
right: -28rpx;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 50%;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2rpx solid rgba(0, 0, 0, 0.06);
|
||||
transition: transform 0.2s ease, opacity 0.2s ease;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.cm-close--active {
|
||||
transform: scale(0.9);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 按钮:默认中等大小,不依赖 .btn 统一样式,避免被外部样式干扰 */
|
||||
.cm-btn {
|
||||
flex: 1;
|
||||
min-height: 80rpx;
|
||||
padding: 0 40rpx;
|
||||
border-radius: 14rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
outline: none;
|
||||
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.cm-btn__text {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.cm-btn--cancel--active {
|
||||
opacity: 0.75;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.cm-btn--confirm--active {
|
||||
opacity: 0.92;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.cm-btn--disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cm-btn:only-child {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user