first commit

This commit is contained in:
2025-10-16 21:32:16 +08:00
commit c446df73b5
229 changed files with 499497 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
Component({
options: {
// 关键修改:设置样式隔离选项
styleIsolation: 'shared' // 完全共享页面和组件样式
},
properties: {
visible: {
type: Boolean,
value: false,
observer: function (newVal) {
if (newVal) {
this.showModal();
} else {
this.hideModal();
}
}
},
modalContentClass: {
type: String,
value: ''
}
},
data: {
animationClass: 'modal-transition', // 保持过渡效果类
containerHeight: '25vh', // 设置为1/4屏幕高度
initialTranslate: '100%', // 初始在屏幕外
startY: 0, // 触摸起始位置
currentState: 'hidden', // 'hidden', 'bottom', 'half', 'full'
states: {
bottom: { height: '25vh', translate: '0' }, // 1/4屏幕高度设置translate为0使其正确显示在底部
full: { height: '100vh', translate: '0' } // 全屏高度
}
},
methods: {
showModal() {
this.setData({
visible: true,
currentState: 'bottom', // 默认显示1/4屏幕高度
containerHeight: this.data.states['bottom'].height,
initialTranslate: this.data.states['bottom'].translate
});
},
hideModal() {
this.setData({
initialTranslate: '100%', // 设置为底部位置
currentState: 'hidden'
});
setTimeout(() => {
if (this.data.currentState === 'hidden') {
this.setData({ visible: false });
}
}, 300); // 与动画时间一致
},
onHideModal() {
this._triggerCloseEvent();
},
// 遮罩层点击事件(如果有遮罩层的话)
onOverlayTap() {
this._triggerCloseEvent();
},
// 共用的关闭事件触发方法
_triggerCloseEvent() {
this.triggerEvent('close');
},
preventTouchMove() {},
onTouchStart(e) {
this.setData({ startY: e.touches[0].clientY });
this.setData({ animationClass: '' }); // 开始拖动时移除过渡效果,使拖动更跟手
},
onTouchMove(e) {
if (!this.data.visible) return;
const currentY = e.touches[0].clientY;
const deltaY = currentY - this.data.startY;
let newTranslate = '';
// 根据当前状态和拖动方向计算新的位移
if (this.data.currentState === 'full') {
newTranslate = `calc(0px + ${deltaY}px)`; // 从全屏位置开始拖
} else if (this.data.currentState === 'bottom') {
newTranslate = `calc(75vh + ${deltaY}px)`; // 从1/4屏幕高度位置开始拖
}
// 限制拖动范围,不能拖出屏幕顶部或底部之外
if (this.data.currentState === 'full' && parseInt(newTranslate) > 0) {
newTranslate = '0';
} else if (this.data.currentState === 'bottom' && deltaY < -300) {
// 向下拖动不超过屏幕
newTranslate = 'calc(75vh - 300px)';
}
this.setData({ initialTranslate: newTranslate });
},
onTouchEnd(e) {
const endY = e.changedTouches[0].clientY;
const deltaY = endY - this.data.startY;
const threshold = 50; // 判断状态切换的阈值
// 根据拖动距离和方向,以及当前状态,决定下一个状态
let nextState = this.data.currentState;
if (Math.abs(deltaY) > threshold) {
if (deltaY > 0) { // 向下拖
if (this.data.currentState === 'full') {
nextState = 'bottom';
}
} else { // 向上拖
if (this.data.currentState === 'bottom') {
nextState = 'full';
}
}
} else {
// 拖动距离小于阈值,则回到当前状态
nextState = this.data.currentState;
}
// 在切换状态前,添加过渡动画类
this.setData({
animationClass: 'modal-transition'
}, () => {
// 在回调中执行状态切换,确保动画类已经设置
this.switchToState(nextState);
});
},
switchToState(state) {
if (state === 'hidden') {
this.hideModal();
return;
}
const targetState = this.data.states[state];
if (targetState) {
this.setData({
currentState: state,
containerHeight: targetState.height,
initialTranslate: targetState.translate
});
// 通知父组件状态变化
this.triggerEvent('stateChange', { state: state });
}
}
}
});

View File

@@ -0,0 +1,14 @@
{
"component": true,
"usingComponents": {},
"componentGenerics": {
"selectable": true
},
"styleIsolation": "shared",
"externalClasses": [
"modal-content-class",
"modal-header-class",
"modal-basic-info-class",
"modal-detail-info-class"
]
}

View File

@@ -0,0 +1,19 @@
<view wx:if="{{visible}}" class="modal-overlay bottom-modal" bindtap="onHideModal">
<view
class="modal-container {{animationClass}} {{modalContentClass}}"
style="height: {{containerHeight}}; transform: translateY({{initialTranslate}});"
bindtouchstart="onTouchStart"
bindtouchmove="onTouchMove"
bindtouchend="onTouchEnd"
catchtap="preventTouchMove"
>
<!-- 拖动指示器 -->
<view class="modal-handle"></view>
<!-- 内容区域使用slot让父组件可以自定义内容 -->
<slot></slot>
<!-- 关闭按钮 -->
<view class="modal-close" bindtap="onHideModal">×</view>
</view>
</view>

View File

@@ -0,0 +1,169 @@
/* 添加组件样式隔离设置 */
:host {
isolation: isolate;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: transparent;
z-index: 9998;
display: flex;
justify-content: flex-end;
align-items: flex-end;
}
.modal-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
border-radius: 20rpx 20rpx 0 0;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
z-index: 9999;
overflow: hidden;
transition: transform 0.3s ease-out;
}
.modal-handle {
width: 80rpx;
height: 8rpx;
background-color: #dcdcdc;
border-radius: 4rpx;
margin: 24rpx auto;
}
.modal-close {
position: absolute;
top: 20rpx;
right: 20rpx;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background-color: rgba(0, 0, 0, 0.1);
display: flex;
justify-content: center;
align-items: center;
font-size: 40rpx;
color: #666666;
}
/* 确保modal-container样式优先级 */
.modal-container {
padding: 30rpx !important;
background-color: #fff !important;
border-radius: 30rpx !important;
overflow: hidden !important;
position: relative !important;
z-index: 100 !important;
width: 100% !important;
box-sizing: border-box !important;
}
/* 内部元素样式 - 提高选择器优先级 */
.modal-header {
margin-bottom: 20rpx !important;
}
.modal-title {
font-size: 36rpx !important;
font-weight: bold !important;
margin-bottom: 10rpx !important;
color: #333 !important;
}
.modal-subtitle {
font-size: 28rpx !important;
color: #666 !important;
margin-bottom: 20rpx !important;
}
.modal-basic-info {
margin-bottom: 20rpx !important;
}
.modal-detail-info {
transition: all 0.3s ease-in-out !important;
}
/* 货运人员头部样式 */
.delivery-person-header {
display: flex !important;
align-items: center !important;
gap: 20rpx !important;
margin-bottom: 20rpx !important;
}
.delivery-person-avatar {
width: 120rpx !important;
height: 120rpx !important;
border-radius: 50% !important;
overflow: hidden !important;
background-color: #f5f5f5 !important;
}
.delivery-person-avatar image {
width: 100% !important;
height: 100% !important;
display: block !important;
}
.person-info-header {
flex: 1 !important;
}
/* 信息项样式 */
.detail-item {
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
padding: 16rpx 0 !important;
border-bottom: 1rpx solid #f0f0f0 !important;
}
.detail-item:last-child {
border-bottom: none !important;
}
.detail-label {
font-size: 28rpx !important;
color: #666 !important;
}
.detail-value {
font-size: 28rpx !important;
color: #333 !important;
text-align: right !important;
}
/* 状态徽章样式 */
.status-badge {
padding: 6rpx 20rpx !important;
border-radius: 20rpx !important;
font-size: 24rpx !important;
font-weight: 500 !important;
}
.status-idle {
background-color: #e6f7ff !important;
color: #1890ff !important;
}
.status-busy {
background-color: #fff2e8 !important;
color: #fa8c16 !important;
}
.status-delivering {
background-color: #f6ffed !important;
color: #52c41a !important;
}
.status-offline {
background-color: #f5f5f5 !important;
color: #8c8c8c !important;
}