fix feat 一大堆关羽扫雷的
This commit is contained in:
parent
237d785a4f
commit
5691d0601d
@ -442,6 +442,27 @@
|
||||
margin-bottom: $spacing-xs;
|
||||
display: flex;
|
||||
gap: $spacing-sm;
|
||||
|
||||
// 最新日志高亮动画(第一个元素是最新的)
|
||||
&:first-child {
|
||||
animation: logHighlight 1s ease-out;
|
||||
background: rgba($brand-primary, 0.1);
|
||||
padding: 4rpx 8rpx;
|
||||
border-radius: 4rpx;
|
||||
margin-left: -8rpx;
|
||||
margin-right: -8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes logHighlight {
|
||||
0% {
|
||||
background: rgba($brand-primary, 0.3);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
100% {
|
||||
background: rgba($brand-primary, 0.1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.log-time {
|
||||
@ -969,6 +990,16 @@
|
||||
border: 1px solid $border-dark;
|
||||
// 确保格子始终保持正方形
|
||||
aspect-ratio: 1 / 1;
|
||||
// 阻止长按缩放
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
// 额外的防护
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
outline: none;
|
||||
// 禁用任何手势
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
.grid-cell {
|
||||
@ -983,6 +1014,16 @@
|
||||
aspect-ratio: 1 / 1;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
// 阻止长按弹出菜单和缩放
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
// 额外的防护
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
outline: none;
|
||||
// 禁用任何手势
|
||||
overscroll-behavior: none;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
@ -1000,6 +1041,12 @@
|
||||
border: 2rpx dashed rgba($accent-cyan, 0.5);
|
||||
}
|
||||
|
||||
// 观察者旗帜标记样式
|
||||
&.has-flag {
|
||||
border: 2rpx solid rgba($color-warning, 0.6);
|
||||
background: rgba($color-warning, 0.1);
|
||||
}
|
||||
|
||||
// --- 新增特效样式 ---
|
||||
&.explosion {
|
||||
animation: explode 0.5s ease-out;
|
||||
@ -1184,6 +1231,33 @@
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
// 观察者旗帜图标
|
||||
.spectator-flag {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
animation: flagWave 2s ease-in-out infinite;
|
||||
|
||||
.flag-icon {
|
||||
font-size: 36rpx;
|
||||
filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.3));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes flagWave {
|
||||
0%, 100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
25% {
|
||||
transform: rotate(5deg);
|
||||
}
|
||||
75% {
|
||||
transform: rotate(-5deg);
|
||||
}
|
||||
}
|
||||
|
||||
.magnifier-mark {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@ -1281,6 +1355,15 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
// 最新日志高亮动画(第一个元素是最新的)
|
||||
&:first-child {
|
||||
animation: gameLogHighlight 0.8s ease-out;
|
||||
background: rgba($brand-primary, 0.15);
|
||||
padding: 2rpx 6rpx;
|
||||
border-radius: 4rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&.log-system {
|
||||
color: $accent-cyan;
|
||||
}
|
||||
@ -1290,6 +1373,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes gameLogHighlight {
|
||||
0% {
|
||||
background: rgba($brand-primary, 0.4);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
100% {
|
||||
background: rgba($brand-primary, 0.15);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =====================================
|
||||
// 弹窗 (全屏遮罩)
|
||||
|
||||
@ -165,10 +165,14 @@
|
||||
cell.revealed ? (cell.type === 'bomb' ? 'type-bomb explosion' : (cell.type === 'item' ? 'type-item item-' + (cell.itemId || 'generic') : 'type-empty')) : 'bg-slate-800',
|
||||
{
|
||||
'revealed': cell.revealed,
|
||||
'has-magnifier': myPlayer && myPlayer.revealedCells && myPlayer.revealedCells[i]
|
||||
'has-magnifier': myPlayer && myPlayer.revealedCells && myPlayer.revealedCells[i],
|
||||
'has-flag': isSpectator && spectatorFlags[i] // 观察者旗帜标记
|
||||
}
|
||||
]"
|
||||
@tap="handleCellClick(i)"
|
||||
@touchstart="handleCellTouchStart(i, $event)"
|
||||
@touchend="handleCellTouchEnd($event)"
|
||||
@touchmove="handleCellTouchMove($event)"
|
||||
>
|
||||
<view v-if="cell.revealed">
|
||||
<text v-if="cell.type === 'bomb'" class="cell-icon">💣</text>
|
||||
@ -188,6 +192,11 @@
|
||||
<text class="magnifier-badge">🔍</text>
|
||||
</view>
|
||||
|
||||
<!-- 观察者旗帜标记 -->
|
||||
<view v-else-if="isSpectator && spectatorFlags[i]" class="spectator-flag">
|
||||
<text class="flag-icon">🚩</text>
|
||||
</view>
|
||||
|
||||
<!-- 格子上的飘字 -->
|
||||
<view v-for="l in getCellLabels(i)" :key="l.id" class="float-label" :class="'text-' + l.type">
|
||||
{{ l.text }}
|
||||
@ -335,6 +344,8 @@ export default {
|
||||
showResultModal: false,
|
||||
onlineCount: 0,
|
||||
onlineCountInterval: null,
|
||||
spectatorFlags: {}, // 观察者模式的旗帜标记 { cellIndex: true }
|
||||
longPressTimer: null, // 长按定时器
|
||||
// Timers
|
||||
matchInterval: null,
|
||||
turnInterval: null,
|
||||
@ -407,11 +418,6 @@ export default {
|
||||
this.showResultModal = true;
|
||||
}
|
||||
},
|
||||
logs() {
|
||||
this.$nextTick(() => {
|
||||
this.logsScrollTop = 99999 + Math.random();
|
||||
});
|
||||
},
|
||||
'gameState.currentTurnIndex'() {
|
||||
this.resetTurnTimer();
|
||||
},
|
||||
@ -422,7 +428,11 @@ export default {
|
||||
onLoad(options) {
|
||||
this.fetchGameConfig();
|
||||
const { game_token, nakama_server, nakama_key, match_id, is_spectator, uid } = options;
|
||||
if (is_spectator) this.isSpectator = true;
|
||||
if (is_spectator) {
|
||||
this.isSpectator = true;
|
||||
// 观察者模式:禁用页面缩放
|
||||
this.disablePageZoom();
|
||||
}
|
||||
if (match_id) this.matchId = match_id;
|
||||
if (uid) this.stableUserId = uid;
|
||||
|
||||
@ -436,6 +446,33 @@ export default {
|
||||
this.cleanup();
|
||||
},
|
||||
methods: {
|
||||
disablePageZoom() {
|
||||
// 观察者模式:禁用页面缩放和长按菜单
|
||||
// 注意:这些操作在 H5 环境下有效
|
||||
// #ifdef H5
|
||||
document.addEventListener('contextmenu', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
|
||||
// 禁用双指缩放
|
||||
document.addEventListener('gesturestart', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener('gesturechange', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener('gestureend', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
// #endif
|
||||
|
||||
// 微信小程序环境下,通过页面配置禁用缩放
|
||||
// 这些需要在 pages.json 中配置
|
||||
},
|
||||
async fetchGameConfig() {
|
||||
try {
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
@ -464,10 +501,19 @@ export default {
|
||||
},
|
||||
addLog(type, content) {
|
||||
const now = new Date();
|
||||
const timeStr = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0');
|
||||
const timeStr = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0');
|
||||
const id = Date.now() + Math.random().toString();
|
||||
this.logs.push({ id, type, content, time: timeStr });
|
||||
if (this.logs.length > 50) this.logs.shift();
|
||||
|
||||
// 插入到数组头部,最新的在最上面
|
||||
this.logs.unshift({ id, type, content, time: timeStr });
|
||||
|
||||
// 增加日志存储限制到 100 条,让用户能看到更多历史
|
||||
if (this.logs.length > 100) this.logs.pop();
|
||||
|
||||
// 确保滚动到顶部显示最新日志
|
||||
this.$nextTick(() => {
|
||||
this.logsScrollTop = 0;
|
||||
});
|
||||
},
|
||||
spawnLabel(x, y, text, type, cellIndex, targetUserId) {
|
||||
const id = Date.now() + Math.random().toString();
|
||||
@ -839,7 +885,10 @@ export default {
|
||||
},
|
||||
handleCellClick(idx) {
|
||||
// 前置条件检查
|
||||
if (this.isSpectator) return;
|
||||
if (this.isSpectator) {
|
||||
// 观察者模式:不处理普通点击,只处理长按
|
||||
return;
|
||||
}
|
||||
if (!this.gameState?.gameStarted) {
|
||||
uni.showToast({ title: '游戏尚未开始', icon: 'none' });
|
||||
return;
|
||||
@ -854,6 +903,69 @@ export default {
|
||||
// 发送移动指令
|
||||
nakamaManager.sendMatchState(this.matchId, 3, JSON.stringify({ index: idx }));
|
||||
},
|
||||
// 观察者模式:长按插旗
|
||||
handleCellLongPress(idx) {
|
||||
// 只有观察者才能长按插旗
|
||||
if (!this.isSpectator) return;
|
||||
if (!this.gameState?.grid) return;
|
||||
if (this.gameState.grid[idx].revealed) return;
|
||||
|
||||
// 切换旗帜状态
|
||||
if (this.spectatorFlags[idx]) {
|
||||
this.$delete(this.spectatorFlags, idx);
|
||||
this.addLog('system', `🚩 移除了位置 ${idx} 的旗帜`);
|
||||
} else {
|
||||
this.$set(this.spectatorFlags, idx, true);
|
||||
this.addLog('system', `🚩 在位置 ${idx} 插上了旗帜`);
|
||||
}
|
||||
},
|
||||
// 触摸开始 - 用于检测长按
|
||||
handleCellTouchStart(idx, event) {
|
||||
// 只有观察者才处理长按插旗
|
||||
if (!this.isSpectator) return;
|
||||
|
||||
// 阻止默认行为,防止长按弹出菜单(仅观察者)
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
// 清除之前的定时器
|
||||
if (this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
}
|
||||
|
||||
// 设置长按定时器(800ms)
|
||||
this.longPressTimer = setTimeout(() => {
|
||||
// 添加震动反馈
|
||||
// #ifndef MP-ALIPAY
|
||||
uni.vibrateShort();
|
||||
// #endif
|
||||
this.handleCellLongPress(idx);
|
||||
this.longPressTimer = null;
|
||||
}, 800);
|
||||
},
|
||||
// 触摸结束 - 取消长按
|
||||
handleCellTouchEnd(event) {
|
||||
// 只有观察者需要处理
|
||||
if (!this.isSpectator) return;
|
||||
|
||||
if (this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
this.longPressTimer = null;
|
||||
}
|
||||
},
|
||||
// 触摸移动 - 取消长按
|
||||
handleCellTouchMove(event) {
|
||||
// 只有观察者需要处理
|
||||
if (!this.isSpectator) return;
|
||||
|
||||
// 如果手指移动了,取消长按
|
||||
if (this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
this.longPressTimer = null;
|
||||
}
|
||||
},
|
||||
refreshAndPlayAgain() {
|
||||
uni.removeStorageSync('minesweeper_last_match_id');
|
||||
uni.navigateBack();
|
||||
@ -873,6 +985,13 @@ export default {
|
||||
clearInterval(this.matchInterval);
|
||||
clearInterval(this.turnInterval);
|
||||
clearInterval(this.onlineCountInterval);
|
||||
|
||||
// 清理长按定时器
|
||||
if (this.longPressTimer) {
|
||||
clearTimeout(this.longPressTimer);
|
||||
this.longPressTimer = null;
|
||||
}
|
||||
|
||||
nakamaManager.disconnect();
|
||||
},
|
||||
async fetchOnlineCount() {
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<text class="coupon-rules">{{ item.rules || '全场通用' }}</text>
|
||||
<text class="coupon-rules">{{ formatRules(item.rules) }}</text>
|
||||
|
||||
<!-- 使用进度条 -->
|
||||
<view class="coupon-progress" v-if="item.amount && item.remaining !== undefined && item.remaining < item.amount">
|
||||
@ -164,6 +164,18 @@ function formatValue(val) {
|
||||
return (Number(val) / 100).toFixed(0)
|
||||
}
|
||||
|
||||
// 格式化优惠券规则描述中的分转为元
|
||||
function formatRules(rules) {
|
||||
if (!rules) return '全场通用'
|
||||
// 将"XXX分"替换为"¥X.XX元"格式
|
||||
return rules.replace(/(\d+)分/g, (match, p1) => {
|
||||
const yuan = (Number(p1) / 100).toFixed(2)
|
||||
// 去掉末尾的.00
|
||||
const formatted = yuan.endsWith('.00') ? yuan.slice(0, -3) : yuan
|
||||
return `¥${formatted}元`
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化有效期
|
||||
function formatExpiry(item) {
|
||||
// 后端返回的字段是 valid_end
|
||||
|
||||
@ -184,6 +184,12 @@
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "扫雷对战",
|
||||
"disableScroll": true,
|
||||
"mp-weixin": {
|
||||
"disableSwipeBack": true
|
||||
},
|
||||
"h5": {
|
||||
"titleNView": false
|
||||
},
|
||||
"app-plus": {
|
||||
"bounce": "none"
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<text class="coupon-rules">{{ item.rules || '全场通用' }}</text>
|
||||
<text class="coupon-rules">{{ formatCouponRules(item.rules) }}</text>
|
||||
|
||||
<!-- 使用进度条 (仅当有使用记录时显示) -->
|
||||
<view class="coupon-progress-wrap" v-if="item.amount && item.remaining !== undefined && item.remaining < item.amount">
|
||||
@ -1135,6 +1135,16 @@ export default {
|
||||
formatCouponValue(val) {
|
||||
return (Number(val) / 100).toFixed(0)
|
||||
},
|
||||
formatCouponRules(rules) {
|
||||
if (!rules) return '全场通用'
|
||||
// 将"XXX分"替换为"¥X.XX元"格式
|
||||
return rules.replace(/(\d+)分/g, (match, p1) => {
|
||||
const yuan = (Number(p1) / 100).toFixed(2)
|
||||
// 去掉末尾的.00
|
||||
const formatted = yuan.endsWith('.00') ? yuan.slice(0, -3) : yuan
|
||||
return `¥${formatted}元`
|
||||
})
|
||||
},
|
||||
formatCouponExpiry(item) {
|
||||
if (!item.end_time) return '长期有效'
|
||||
return `有效期至 ${this.formatDate(item.end_time)}`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user