首次提交:初始化项目代码

This commit is contained in:
sunct
2026-07-31 14:42:05 +08:00
commit ecca407485
84 changed files with 24158 additions and 0 deletions
+262
View File
@@ -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>