API: - 新增 player-messages 域:充值审核通过/拒绝、Banner/公告推广消息,支持多语言模板 - 新增 presence 域:Redis 心跳在线状态,管理端可查询在线玩家数 - User 表增加 visible_menus 字段;新增 player_messages 表及迁移 - 充值审核通过/拒绝时按系统配置自动写入玩家站内消息 - 管理端新增 GET /deposit-orders/pending-count、GET /presence/online-count - 玩家端新增消息 CRUD、presence/ping、home 返回 inbox 开关配置 - 员工管理支持 visibleMenus 配置与删除保护(不能删自己/最后超管) - SystemConfig 增加 inbox 功能开关及各类通知开关 Admin: - 员工管理:按角色默认菜单 + 可勾选可见菜单项 - ManageLayout:按 visibleMenus 过滤侧栏;充值待审数量角标轮询 - Contents:富文本编辑器、图片字段组件重构 - DashboardPlayers:展示在线玩家数;AdminPlayerStatusCell 在线状态列 - 多页面 i18n 与权限细节调整 Player: - 站内邮箱中心(InboxHub):消息列表/详情、未读角标、一键已读/删除 - 公告列表与详情页;走马灯可跳转详情 - 客服 Modal 改为 Panel,与邮箱 Hub 整合 - 充值状态轮询通知;presence 心跳;BetSlip 清空二次确认 - HomeView 今日赛事板块;FootballView 等体验优化 Shared: 新增 CANNOT_DELETE_SELF、STAFF_NOT_FOUND、MESSAGE_NOT_FOUND 等错误码 Docs: 玩家端缺失功能分析文档 Chore: 移除 .agents/skills 设计类 skill 文件 Co-authored-by: Cursor <cursoragent@cursor.com>
187 lines
3.9 KiB
Vue
187 lines
3.9 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
visible: boolean;
|
|
title?: string;
|
|
message: string;
|
|
confirmText?: string;
|
|
cancelText?: string;
|
|
danger?: boolean;
|
|
loading?: boolean;
|
|
}>(),
|
|
{
|
|
danger: false,
|
|
loading: false,
|
|
},
|
|
);
|
|
|
|
const emit = defineEmits<{
|
|
'update:visible': [value: boolean];
|
|
confirm: [];
|
|
cancel: [];
|
|
}>();
|
|
|
|
const { t } = useI18n();
|
|
|
|
const resolvedTitle = computed(() => props.title ?? t('common.confirm'));
|
|
const resolvedConfirmText = computed(() => props.confirmText ?? t('common.confirm'));
|
|
const resolvedCancelText = computed(() => props.cancelText ?? t('common.cancel'));
|
|
|
|
function close() {
|
|
if (props.loading) return;
|
|
emit('update:visible', false);
|
|
emit('cancel');
|
|
}
|
|
|
|
function onConfirm() {
|
|
if (props.loading) return;
|
|
emit('confirm');
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Teleport to="body">
|
|
<Transition name="confirm-fade">
|
|
<div
|
|
v-if="visible"
|
|
class="confirm-overlay"
|
|
@click.self="close"
|
|
>
|
|
<div
|
|
class="confirm-modal"
|
|
role="alertdialog"
|
|
aria-modal="true"
|
|
:aria-labelledby="title ? 'confirm-dialog-title' : undefined"
|
|
:aria-describedby="'confirm-dialog-message'"
|
|
>
|
|
<h2 v-if="title" id="confirm-dialog-title" class="confirm-title">{{ resolvedTitle }}</h2>
|
|
<p id="confirm-dialog-message" class="confirm-message">{{ message }}</p>
|
|
|
|
<div class="confirm-actions">
|
|
<button
|
|
type="button"
|
|
class="confirm-btn cancel"
|
|
:disabled="loading"
|
|
@click="close"
|
|
>
|
|
{{ resolvedCancelText }}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="confirm-btn confirm"
|
|
:class="{ danger }"
|
|
:disabled="loading"
|
|
@click="onConfirm"
|
|
>
|
|
{{ resolvedConfirmText }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</Teleport>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.confirm-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 1000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
|
|
background: rgba(0, 0, 0, 0.72);
|
|
backdrop-filter: blur(4px);
|
|
-webkit-backdrop-filter: blur(4px);
|
|
}
|
|
|
|
.confirm-modal {
|
|
width: 100%;
|
|
max-width: 340px;
|
|
background: linear-gradient(165deg, #1a1810 0%, #121212 45%, #0a0a0a 100%);
|
|
border: 1px solid var(--border-gold-soft, rgba(200, 168, 78, 0.25));
|
|
border-radius: 12px;
|
|
padding: 22px 18px 16px;
|
|
box-shadow: 0 0 24px rgba(212, 175, 55, 0.08);
|
|
}
|
|
|
|
.confirm-title {
|
|
margin: 0 0 10px;
|
|
font-size: 17px;
|
|
font-weight: 800;
|
|
color: var(--gold, #c8a84e);
|
|
text-align: center;
|
|
line-height: 1.35;
|
|
}
|
|
|
|
.confirm-message {
|
|
margin: 0 0 20px;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
color: #c8c8c8;
|
|
text-align: center;
|
|
}
|
|
|
|
.confirm-actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
.confirm-btn {
|
|
flex: 1;
|
|
min-height: 44px;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
cursor: pointer;
|
|
transition: opacity 0.15s;
|
|
}
|
|
|
|
.confirm-btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.confirm-btn.cancel {
|
|
border: 1px solid #333;
|
|
background: transparent;
|
|
color: #888;
|
|
}
|
|
|
|
.confirm-btn.confirm {
|
|
border: none;
|
|
background: linear-gradient(135deg, #d4a017, #e8c84a);
|
|
color: #1a1a1a;
|
|
}
|
|
|
|
.confirm-btn.confirm.danger {
|
|
background: linear-gradient(135deg, #c0392b, #e74c3c);
|
|
color: #fff;
|
|
}
|
|
|
|
.confirm-fade-enter-active,
|
|
.confirm-fade-leave-active {
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.confirm-fade-enter-active .confirm-modal,
|
|
.confirm-fade-leave-active .confirm-modal {
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.confirm-fade-enter-from,
|
|
.confirm-fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
.confirm-fade-enter-from .confirm-modal,
|
|
.confirm-fade-leave-to .confirm-modal {
|
|
transform: scale(0.96);
|
|
}
|
|
</style>
|