fix(mobile): bottom tabbar switches whole views instead of inline panels
用户反馈「底部菜单不像正常菜单切换,点击后只在当前页面」修复: tab 视图互斥渲染(isHomeTab + activeUserPanel/activeSupervisorPanel 条件重组),三端首页/训练/知识/我的整页切换,不再页中叠加;切 tab 滚动回顶;训练 tab 不自动开局、进行中会话切走可切回;SOP 全屏层 返回进入前 tab;首页「我的成长」收敛为摘要卡(综合能力/今日完成/ 待复盘+查看全部),完整画像与历史仅在「我的」tab,消除同屏重复。
This commit is contained in:
+184
-60
@@ -52,7 +52,7 @@
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<Card v-if="roleKey !== 'user'" :title="roleKey === 'supervisor' ? '今日处理流程' : '今日使用流程'">
|
||||
<Card v-if="roleKey !== 'user' && isHomeTab" :title="roleKey === 'supervisor' ? '今日处理流程' : '今日使用流程'">
|
||||
<div class="flow-steps">
|
||||
<button v-for="item in roleFlowSteps" :key="`${item.phase}-${item.title}`" type="button" :class="{ active: isFlowActive(item) }" @click="openFlowStep(item)">
|
||||
<em>{{ item.phase }}</em>
|
||||
@@ -66,6 +66,7 @@
|
||||
</Card>
|
||||
|
||||
<template v-if="roleKey === 'supervisor'">
|
||||
<template v-if="isHomeTab">
|
||||
<Card title="团队今日概况">
|
||||
<div class="team-card">
|
||||
<div class="team-copy">
|
||||
@@ -169,8 +170,9 @@
|
||||
</span>
|
||||
<Icon name="chevron-right" size="26" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<Card v-if="activeSupervisorPanel" :title="supervisorPanelTitle">
|
||||
<Card v-if="activeSupervisorPanel && !isHomeTab" :title="supervisorPanelTitle">
|
||||
<div class="user-panel">
|
||||
<template v-if="activeSupervisorPanel === 'team' || activeSupervisorPanel === 'weakness'">
|
||||
<div class="progress-grid">
|
||||
@@ -201,6 +203,42 @@
|
||||
</button>
|
||||
</div>
|
||||
<em v-else class="panel-message">暂无待复盘记录</em>
|
||||
<div v-if="selectedReview" class="review-detail">
|
||||
<div class="review-detail-head">
|
||||
<span>
|
||||
<b>{{ selectedReview.trainee }} · {{ selectedReview.score }}分</b>
|
||||
<small>{{ selectedReview.time }} · {{ selectedReview.scene }}</small>
|
||||
</span>
|
||||
<em>{{ selectedReview.status }}</em>
|
||||
</div>
|
||||
|
||||
<div class="review-score-grid">
|
||||
<p v-for="item in selectedReview.scoreItems" :key="item.label">
|
||||
<span>{{ item.label }}</span>
|
||||
<b>{{ item.value }}</b>
|
||||
<small>{{ item.note }}</small>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="practice-dialogue review-dialogue">
|
||||
<p v-for="(turn, index) in selectedReview.dialogue" :key="`${turn.role}-${index}`" :class="turn.role">
|
||||
<span>{{ turn.label }}</span>
|
||||
{{ turn.text }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="review-summary">
|
||||
<b>导师改写</b>
|
||||
<p>{{ selectedReview.mentorRewrite }}</p>
|
||||
<b>AI点评</b>
|
||||
<p>{{ selectedReview.aiComment }}</p>
|
||||
</div>
|
||||
|
||||
<button type="button" :disabled="markingReviewed || selectedReview.status !== '待复盘'" @click="markReviewDone">
|
||||
{{ selectedReview.status === '待复盘' ? (markingReviewed ? '处理中' : '标记已复盘') : '已复盘' }}
|
||||
</button>
|
||||
<em v-if="reviewMessage">{{ reviewMessage }}</em>
|
||||
</div>
|
||||
<div class="result-list">
|
||||
<h3>专项建议</h3>
|
||||
<p>
|
||||
@@ -270,15 +308,12 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="panel-actions">
|
||||
<button type="button" @click="activeSupervisorPanel = ''">收起</button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<template v-if="roleKey === 'user'">
|
||||
<template v-if="roleKey === 'user' && isHomeTab">
|
||||
<section class="scenario-section">
|
||||
<div class="scenario-title">
|
||||
<h2>今天要学</h2>
|
||||
@@ -406,7 +441,7 @@
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<template v-else-if="isHomeTab">
|
||||
<Card title="今日任务" action="查看全部" @action="openUserAction('tasks')">
|
||||
<div class="task-card">
|
||||
<div class="task-copy">
|
||||
@@ -437,9 +472,75 @@
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<Card v-if="roleKey !== 'supervisor' && activeUserPanel" :title="userPanelTitle">
|
||||
<Card v-if="roleKey !== 'supervisor' && activeUserPanel && !isHomeTab" :title="userPanelTitle">
|
||||
<div class="user-panel">
|
||||
<template v-if="activeUserPanel === 'tasks'">
|
||||
<template v-if="roleKey === 'user'">
|
||||
<div class="task-card">
|
||||
<div class="task-copy">
|
||||
<strong>{{ userPrimaryTask.badge }}</strong>
|
||||
<h3>{{ userPrimaryTask.title }}</h3>
|
||||
<p>{{ userPrimaryTask.desc }}</p>
|
||||
<span>进度 {{ userPrimaryTask.progressText }}</span>
|
||||
<ProgressBar :value="userPrimaryTask.progress" />
|
||||
</div>
|
||||
<div class="headset-art">
|
||||
<Icon :name="userPrimaryTask.icon" size="104" stroke="1.7" />
|
||||
</div>
|
||||
<button type="button" @click="handlePrimaryAction">{{ userPrimaryTask.cta }}</button>
|
||||
</div>
|
||||
|
||||
<div v-if="practiceStatus !== 'idle'" class="practice-panel">
|
||||
<div class="practice-panel-head">
|
||||
<span>三角色对练</span>
|
||||
<b v-if="practiceResult">{{ practiceResult.totalScore }}分</b>
|
||||
<small v-else>{{ practiceStatus === 'starting' ? '准备中' : '进行中' }}</small>
|
||||
</div>
|
||||
|
||||
<div v-if="practiceResult" class="practice-result">
|
||||
<strong>{{ practiceResult.aiComment }}</strong>
|
||||
<p>{{ practiceResult.mentorRewrite }}</p>
|
||||
<div class="practice-result-actions">
|
||||
<button type="button" @click="startMobilePractice">再练一次</button>
|
||||
<button type="button" @click="goSupervisor">查看主管端</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="practice-live">
|
||||
<div class="practice-dialogue">
|
||||
<p v-for="(turn, index) in practiceTurns" :key="`${turn.role}-${index}`" :class="turn.role">
|
||||
<span>{{ turn.label }}</span>
|
||||
{{ turn.text }}
|
||||
</p>
|
||||
</div>
|
||||
<textarea v-model.trim="practiceDraft" rows="3" placeholder="输入你的回复" />
|
||||
<div class="practice-actions">
|
||||
<button type="button" :disabled="asrBusy" @click="toggleRecording">
|
||||
{{ recording ? '停止录音' : asrBusy ? '识别中' : useAudioFileFallback ? '选择语音' : '语音输入' }}
|
||||
</button>
|
||||
<input ref="practiceAudioInput" class="practice-audio-input" type="file" accept="audio/*,.mp3,.wav,.m4a,.webm,.ogg" capture @change="handlePracticeAudioFile" />
|
||||
<button type="button" @click="toggleVoice">{{ voiceEnabled ? '播报:开' : '播报:关' }}</button>
|
||||
<button type="button" @click="fillPracticeReply">填入建议回复</button>
|
||||
<button type="button" :disabled="practiceStatus === 'starting' || practiceStatus === 'submitting'" @click="submitMobilePractice">
|
||||
{{ practiceStatus === 'submitting' ? '提交中' : '提交本轮' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<em v-if="practiceMessage">{{ practiceMessage }}</em>
|
||||
</div>
|
||||
|
||||
<div v-if="practiceHistory.length" class="record-list">
|
||||
<h3>训练历史</h3>
|
||||
<button v-for="item in practiceHistory" :key="`${item.time}-${item.scene}`" type="button" @click="openUserHistory(item)">
|
||||
<span>
|
||||
<b>{{ item.scene }} · {{ item.score }}分</b>
|
||||
<small>{{ item.time }} · {{ item.summary }}</small>
|
||||
</span>
|
||||
<em>{{ item.status }}</em>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button class="panel-row" type="button" @click="handlePrimaryAction">
|
||||
<IconBubble :icon="workerRole.primary.icon" tone="teal" />
|
||||
<span>
|
||||
@@ -507,9 +608,14 @@
|
||||
</div>
|
||||
<em v-if="interviewMessage">{{ interviewMessage }}</em>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeUserPanel === 'sop' || activeUserPanel === 'cases'">
|
||||
<div class="knowledge-tabs">
|
||||
<button type="button" :class="{ active: activeUserPanel === 'sop' }" @click="switchKnowledgePanel('sop')">SOP</button>
|
||||
<button type="button" :class="{ active: activeUserPanel === 'cases' }" @click="switchKnowledgePanel('cases')">案例</button>
|
||||
</div>
|
||||
<form class="knowledge-search" @submit.prevent="searchKnowledge()">
|
||||
<input v-model.trim="knowledgeQuery" :placeholder="activeUserPanel === 'cases' ? '搜索案例关键词' : '搜索SOP关键词'" />
|
||||
<button type="submit" :disabled="knowledgeLoading">{{ knowledgeLoading ? '查询中' : '查询' }}</button>
|
||||
@@ -687,59 +793,34 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="panel-actions">
|
||||
<button type="button" @click="activeUserPanel = ''">收起</button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<section v-if="roleKey === 'user'" class="scenario-section growth-section">
|
||||
<section v-if="roleKey === 'user' && isHomeTab" class="scenario-section growth-section">
|
||||
<div class="scenario-title">
|
||||
<h2>我的成长</h2>
|
||||
<p>训练、复盘和能力画像集中在这里看</p>
|
||||
<p>这里只看摘要,完整记录去我的页</p>
|
||||
</div>
|
||||
|
||||
<Card title="我的进度" :action="workerRole.recommend" compact-action @action="openUserAction('recommend')">
|
||||
<div class="progress-grid">
|
||||
<ProgressTile v-for="item in workerRole.metrics" :key="item.title" :item="item" />
|
||||
</div>
|
||||
|
||||
<div v-if="competencyProfile" class="profile-card">
|
||||
<Card title="我的进度" action="查看全部" compact-action @action="openUserAction('profile')">
|
||||
<div class="profile-card growth-summary">
|
||||
<span>
|
||||
<small>综合能力</small>
|
||||
<b>{{ competencyProfile.score }}</b>
|
||||
<b>{{ competencyProfile?.score || workerRole.metrics[1]?.value || '-' }}</b>
|
||||
</span>
|
||||
<span>
|
||||
<small>训练历史</small>
|
||||
<b>{{ competencyProfile.completed }}</b>
|
||||
<small>今日完成</small>
|
||||
<b>{{ todayPracticeCompleted }}</b>
|
||||
</span>
|
||||
<span>
|
||||
<small>待复盘</small>
|
||||
<b>{{ competencyProfile.pendingReview }}</b>
|
||||
<b>{{ competencyProfile?.pendingReview ?? workerRole.metrics[2]?.value ?? '-' }}</b>
|
||||
</span>
|
||||
<div class="profile-dims">
|
||||
<p v-for="item in competencyProfile.dimensions" :key="item.label">
|
||||
<span>{{ item.label }}</span>
|
||||
<b>{{ item.value }}</b>
|
||||
<small>{{ item.note }}</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="practiceHistory.length" class="record-list">
|
||||
<h3>训练历史</h3>
|
||||
<button v-for="item in practiceHistory" :key="`${item.time}-${item.scene}`" type="button" @click="openUserHistory(item)">
|
||||
<span>
|
||||
<b>{{ item.scene }} · {{ item.score }}分</b>
|
||||
<small>{{ item.time }} · {{ item.summary }}</small>
|
||||
</span>
|
||||
<em>{{ item.status }}</em>
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
<Card v-else title="我的进度" :action="workerRole.recommend" compact-action @action="openUserAction('recommend')">
|
||||
<Card v-else-if="roleKey === 'candidate' && isHomeTab" title="我的进度" :action="workerRole.recommend" compact-action @action="openUserAction('recommend')">
|
||||
<div class="progress-grid">
|
||||
<ProgressTile v-for="item in workerRole.metrics" :key="item.title" :item="item" />
|
||||
</div>
|
||||
@@ -1168,6 +1249,7 @@ const phonePattern = /^1[3-9]\d{9}$/;
|
||||
|
||||
const roleKey = ref<RoleKey>(readRole());
|
||||
const activeTabLabel = ref('首页');
|
||||
const isHomeTab = computed(() => activeTabLabel.value === '首页');
|
||||
const tapped = ref('');
|
||||
const toastMessage = ref('');
|
||||
const phone = ref(localStorage.getItem(phoneKey) || '');
|
||||
@@ -1206,6 +1288,7 @@ const knowledgeResult = ref<KnowledgeSearchResponse | null>(null);
|
||||
const knowledgeLoading = ref(false);
|
||||
const knowledgeMessage = ref('');
|
||||
const sopPanelOpen = ref(false);
|
||||
const sopReturnTabLabel = ref('首页');
|
||||
const sopQuery = ref('');
|
||||
const sopResult = ref<KnowledgeSearchResponse | null>(null);
|
||||
const sopLoading = ref(false);
|
||||
@@ -1232,6 +1315,9 @@ let sopRecordedChunks: Blob[] = [];
|
||||
let customerAudio: HTMLAudioElement | null = null;
|
||||
|
||||
const syncPath = () => window.history.pushState({}, '', rolePaths[roleKey.value]);
|
||||
const scrollToTop = () => {
|
||||
window.requestAnimationFrame(() => window.scrollTo({ top: 0 }));
|
||||
};
|
||||
const tap = (name: string) => {
|
||||
tapped.value = name;
|
||||
toastMessage.value = `「${name}」本期演示未开放,正在建设中`;
|
||||
@@ -1434,9 +1520,7 @@ const transcribeRecording = async (blob: Blob, filename = 'practice.webm') => {
|
||||
};
|
||||
|
||||
const openSopPanel = () => {
|
||||
activeTabLabel.value = roleKey.value === 'supervisor' ? '案例' : '知识';
|
||||
activeUserPanel.value = '';
|
||||
activeSupervisorPanel.value = '';
|
||||
sopReturnTabLabel.value = activeTabLabel.value;
|
||||
sopPanelOpen.value = true;
|
||||
sopQuery.value = '';
|
||||
sopResult.value = null;
|
||||
@@ -1448,6 +1532,7 @@ const closeSopPanel = () => {
|
||||
sopMediaRecorder?.stop();
|
||||
}
|
||||
sopPanelOpen.value = false;
|
||||
activeTabLabel.value = sopReturnTabLabel.value;
|
||||
};
|
||||
|
||||
const toggleSopRecording = async () => {
|
||||
@@ -1578,6 +1663,14 @@ const searchKnowledge = async (query = knowledgeQuery.value, category = activeUs
|
||||
}
|
||||
};
|
||||
|
||||
const switchKnowledgePanel = (panel: Extract<UserPanelKey, 'sop' | 'cases'>) => {
|
||||
activeTabLabel.value = '知识';
|
||||
activeUserPanel.value = panel;
|
||||
knowledgeQuery.value =
|
||||
panel === 'cases' ? '投诉接待优秀案例' : roleKey.value === 'candidate' ? '客服接待基础流程' : '住宅投诉处理流程';
|
||||
void searchKnowledge(knowledgeQuery.value, panel === 'cases' ? 'case' : 'sop');
|
||||
};
|
||||
|
||||
const openFlowStep = (item: FlowStep) => {
|
||||
if (roleKey.value === 'supervisor') {
|
||||
openSupervisorAction(item.target as SupervisorPanelKey);
|
||||
@@ -1601,6 +1694,7 @@ const openCandidateAction = (panel: UserPanelKey) => {
|
||||
activeUserPanel.value = panel;
|
||||
userReviewDetail.value = null;
|
||||
userReviewMessage.value = '';
|
||||
scrollToTop();
|
||||
|
||||
if (panel === 'sop') {
|
||||
knowledgeQuery.value = '客服接待基础流程';
|
||||
@@ -1642,10 +1736,11 @@ const openUserAction = (panel: UserPanelKey) => {
|
||||
}
|
||||
|
||||
activeTabLabel.value =
|
||||
({ tasks: '首页', sop: '知识', cases: '知识', submit: '知识', profile: '我的', history: '我的', recommend: '知识' } as Partial<Record<UserPanelKey, string>>)[panel] || '首页';
|
||||
({ tasks: '训练', sop: '知识', cases: '知识', submit: '知识', profile: '我的', history: '我的', recommend: '知识' } as Partial<Record<UserPanelKey, string>>)[panel] || '首页';
|
||||
activeUserPanel.value = panel;
|
||||
userReviewDetail.value = null;
|
||||
userReviewMessage.value = '';
|
||||
scrollToTop();
|
||||
|
||||
if (panel === 'cases') {
|
||||
knowledgeQuery.value = '投诉接待优秀案例';
|
||||
@@ -1722,6 +1817,7 @@ const openSupervisorAction = (panel: SupervisorPanelKey) => {
|
||||
({ team: '团队', reviews: '团队', assign: '团队', weakness: '团队', case: '案例', sop: '案例', profile: '我的' } as Partial<Record<SupervisorPanelKey, string>>)[panel] || '首页';
|
||||
activeSupervisorPanel.value = panel;
|
||||
activeUserPanel.value = '';
|
||||
scrollToTop();
|
||||
if (panel === 'reviews' || panel === 'assign') {
|
||||
void fetchPracticeData();
|
||||
}
|
||||
@@ -1760,10 +1856,13 @@ const openSupervisorTool = (item: ToolItem) => {
|
||||
};
|
||||
|
||||
const openTab = (label: string) => {
|
||||
scrollToTop();
|
||||
activeTabLabel.value = label;
|
||||
if (roleKey.value === 'supervisor') {
|
||||
if (label === '首页') {
|
||||
activeSupervisorPanel.value = '';
|
||||
activeUserPanel.value = '';
|
||||
selectedReview.value = null;
|
||||
return;
|
||||
}
|
||||
if (label === '团队') {
|
||||
@@ -1783,6 +1882,8 @@ const openTab = (label: string) => {
|
||||
if (roleKey.value === 'candidate') {
|
||||
if (label === '首页') {
|
||||
activeUserPanel.value = '';
|
||||
userReviewDetail.value = null;
|
||||
userReviewMessage.value = '';
|
||||
return;
|
||||
}
|
||||
if (label === '面试') {
|
||||
@@ -1805,14 +1906,19 @@ const openTab = (label: string) => {
|
||||
|
||||
if (label === '首页') {
|
||||
activeUserPanel.value = '';
|
||||
userReviewDetail.value = null;
|
||||
userReviewMessage.value = '';
|
||||
return;
|
||||
}
|
||||
if (label === '训练') {
|
||||
void startMobilePractice();
|
||||
activeUserPanel.value = 'tasks';
|
||||
userReviewDetail.value = null;
|
||||
userReviewMessage.value = '';
|
||||
void fetchPracticeData();
|
||||
return;
|
||||
}
|
||||
if (label === '知识') {
|
||||
openUserAction('sop');
|
||||
switchKnowledgePanel('sop');
|
||||
return;
|
||||
}
|
||||
if (label === '我的') {
|
||||
@@ -1823,16 +1929,19 @@ const openTab = (label: string) => {
|
||||
};
|
||||
|
||||
const openUserHistory = async (item: PracticeRecord) => {
|
||||
activeTabLabel.value = '我的';
|
||||
if (!item.id) {
|
||||
activeUserPanel.value = 'history';
|
||||
userReviewMessage.value = '这条训练记录暂无复盘详情';
|
||||
userReviewDetail.value = null;
|
||||
scrollToTop();
|
||||
return;
|
||||
}
|
||||
|
||||
activeUserPanel.value = 'history';
|
||||
userReviewDetail.value = null;
|
||||
userReviewMessage.value = '';
|
||||
scrollToTop();
|
||||
try {
|
||||
userReviewDetail.value = await readApi<PracticeReviewDetail>(
|
||||
await fetch(`/dev-api/api/aihr/mobile/practice/reviews/${item.id}`, { headers: apiHeaders() })
|
||||
@@ -2031,6 +2140,7 @@ const submitMobilePractice = async () => {
|
||||
const startCandidateInterview = async () => {
|
||||
activeUserPanel.value = 'tasks';
|
||||
activeTabLabel.value = '面试';
|
||||
scrollToTop();
|
||||
interviewStatus.value = 'starting';
|
||||
interviewSessionId.value = '';
|
||||
interviewQuestions.value = [];
|
||||
@@ -2458,6 +2568,7 @@ const countTodayPracticeCompletions = () => {
|
||||
const prefix = todayPrefix();
|
||||
return practiceHistory.value.filter((item) => item.time.startsWith(prefix)).length;
|
||||
};
|
||||
const todayPracticeCompleted = computed(() => (roleKey.value === 'user' && practiceDataReady.value ? countTodayPracticeCompletions() : 0));
|
||||
const userPrimaryTask = computed<PrimaryTask>(() => {
|
||||
const base = workerRole.value.primary;
|
||||
if (roleKey.value !== 'user' || !practiceDataReady.value) return base;
|
||||
@@ -3686,6 +3797,28 @@ p {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.knowledge-tabs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.knowledge-tabs button {
|
||||
min-height: 38px;
|
||||
border-radius: 10px;
|
||||
color: var(--text-secondary);
|
||||
background: #ffffff;
|
||||
border: 1px solid rgba(213, 225, 233, 0.7);
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.knowledge-tabs button.active {
|
||||
color: var(--primary-teal-active);
|
||||
background: var(--primary-teal-light);
|
||||
border-color: rgba(15, 174, 166, 0.22);
|
||||
}
|
||||
|
||||
.knowledge-search input {
|
||||
min-width: 0;
|
||||
height: 42px;
|
||||
@@ -3707,8 +3840,7 @@ p {
|
||||
}
|
||||
|
||||
.knowledge-search button,
|
||||
.panel-primary,
|
||||
.panel-actions button {
|
||||
.panel-primary {
|
||||
min-height: 42px;
|
||||
border-radius: 10px;
|
||||
color: #ffffff;
|
||||
@@ -3869,16 +4001,8 @@ p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.panel-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.panel-actions button {
|
||||
min-width: 76px;
|
||||
color: var(--primary-teal-active);
|
||||
background: var(--primary-teal-light);
|
||||
box-shadow: none;
|
||||
.growth-summary {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.sop-overlay {
|
||||
|
||||
Reference in New Issue
Block a user