fix(mobile): require employee position before user flow
This commit is contained in:
@@ -177,6 +177,7 @@ import { answerDailyDrill, finishPractice, getPracticeAssignments, getPracticePr
|
||||
import { isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { searchKnowledge } from '@/services/knowledge';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { ensureEmployeePosition } from '@/services/position';
|
||||
import { chooseSpeechAudio, transcribeSpeechBlob, transcribeSpeechFile } from '@/services/speech';
|
||||
|
||||
type PracticeStatus = 'idle' | 'starting' | 'active' | 'submitting' | 'finished';
|
||||
@@ -675,11 +676,18 @@ const finish = async (force = false) => {
|
||||
}
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
const handleShow = async () => {
|
||||
resetPageScroll();
|
||||
refreshAuthState();
|
||||
if (loggedIn.value && !(await ensureEmployeePosition('/pages/user/practice/index'))) {
|
||||
return;
|
||||
}
|
||||
void loadAssignments();
|
||||
void loadPrepCard();
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
void handleShow();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -14,6 +14,26 @@
|
||||
<button v-else class="secondary-button account-action" @click="logout">退出</button>
|
||||
</view>
|
||||
|
||||
<view v-if="loggedIn" class="panel position-panel">
|
||||
<view class="position-current">
|
||||
<view>
|
||||
<text class="panel-title">岗位身份</text>
|
||||
<text class="position-title">{{ positionProfile.value }} · {{ positionProfile.heroTitle }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="position-actions">
|
||||
<button
|
||||
v-for="item in positionCards"
|
||||
:key="item.value"
|
||||
class="position-chip"
|
||||
:class="{ active: item.value === positionProfile.value }"
|
||||
@click="changePosition(item.value)"
|
||||
>
|
||||
{{ item.value }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="profile" class="panel">
|
||||
<text class="panel-title">成长摘要</text>
|
||||
<view class="score-row">
|
||||
@@ -117,9 +137,20 @@
|
||||
import { ref } from 'vue';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
import type { CompetencyProfile, PracticeRecord, PromotionEvidence } from '@/types/api';
|
||||
import { clearAuth, getAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import {
|
||||
clearAuth,
|
||||
getAuth,
|
||||
getPositionProfile,
|
||||
getSelectedPositionProfile,
|
||||
isLoggedIn,
|
||||
rememberLoginRedirect,
|
||||
saveSelectedPosition,
|
||||
userPositionOptions,
|
||||
type UserPosition
|
||||
} from '@/services/auth';
|
||||
import { getCompetencyProfile, getPracticeHistory, getPromotionEvidence } from '@/services/practice';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { ensureEmployeePosition } from '@/services/position';
|
||||
|
||||
const loggedIn = ref(false);
|
||||
const phone = ref('');
|
||||
@@ -128,11 +159,14 @@ const message = ref('');
|
||||
const profile = ref<CompetencyProfile | null>(null);
|
||||
const history = ref<PracticeRecord[]>([]);
|
||||
const evidence = ref<PromotionEvidence | null>(null);
|
||||
const positionProfile = ref(getSelectedPositionProfile());
|
||||
const positionCards = userPositionOptions.map((item) => getPositionProfile(item));
|
||||
|
||||
const refreshAuth = () => {
|
||||
const auth = getAuth();
|
||||
loggedIn.value = isLoggedIn();
|
||||
phone.value = auth.phone;
|
||||
positionProfile.value = getSelectedPositionProfile();
|
||||
};
|
||||
|
||||
const clearProfileData = () => {
|
||||
@@ -147,6 +181,10 @@ const loadData = async () => {
|
||||
clearProfileData();
|
||||
return;
|
||||
}
|
||||
if (!(await ensureEmployeePosition('/pages/user/profile/index'))) {
|
||||
return;
|
||||
}
|
||||
refreshAuth();
|
||||
loading.value = true;
|
||||
message.value = '';
|
||||
try {
|
||||
@@ -183,6 +221,13 @@ const logout = () => {
|
||||
uni.showToast({ title: '已退出', icon: 'none' });
|
||||
};
|
||||
|
||||
const changePosition = (position: UserPosition) => {
|
||||
const saved = saveSelectedPosition(position);
|
||||
if (!saved) return;
|
||||
positionProfile.value = getSelectedPositionProfile();
|
||||
uni.showToast({ title: '岗位已更新', icon: 'none' });
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
resetPageScroll();
|
||||
void loadData();
|
||||
@@ -214,6 +259,48 @@ onShow(() => {
|
||||
min-width: 84px;
|
||||
}
|
||||
|
||||
.position-current {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.position-title {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
color: #2563eb;
|
||||
font-size: 16px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.position-actions {
|
||||
display: grid;
|
||||
margin-top: 14px;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.position-chip {
|
||||
display: flex;
|
||||
min-height: 38px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
background: #f8fafc;
|
||||
color: #475569;
|
||||
font-size: 13px;
|
||||
font-weight: 900;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.position-chip.active {
|
||||
border-color: #2563eb;
|
||||
background: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.score-row {
|
||||
display: grid;
|
||||
margin-top: 16px;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
v-model="query"
|
||||
class="textarea"
|
||||
maxlength="200"
|
||||
placeholder="例如: 业主投诉噪音,生活顾问应该怎么处理?"
|
||||
:placeholder="quickQuestionPlaceholder"
|
||||
/>
|
||||
<view class="chip-row">
|
||||
<button v-for="item in quickQuestions" :key="item" class="chip" :disabled="loading" @click="useQuickQuestion(item)">
|
||||
@@ -107,7 +107,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
import type {
|
||||
AnswerFeedbackVerdict,
|
||||
@@ -119,10 +119,13 @@ import {
|
||||
searchKnowledge,
|
||||
submitAnswerFeedback
|
||||
} from '@/services/knowledge';
|
||||
import { isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { getSelectedPositionProfile, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { ensureEmployeePosition } from '@/services/position';
|
||||
|
||||
const quickQuestions = ['业主投诉噪音怎么安抚', '停车费催缴怎么说', '生活顾问的一天做什么'];
|
||||
const positionProfile = ref(getSelectedPositionProfile());
|
||||
const quickQuestions = computed(() => positionProfile.value.quickQuestions);
|
||||
const quickQuestionPlaceholder = computed(() => `例如: ${quickQuestions.value[0] || '岗位SOP怎么处理?'}`);
|
||||
const query = ref('');
|
||||
const result = ref<KnowledgeSearchResponse | null>(null);
|
||||
const summaryCard = ref<SummaryCardResponse | null>(null);
|
||||
@@ -143,6 +146,7 @@ const resetResultState = () => {
|
||||
|
||||
const refreshAuthState = () => {
|
||||
loggedIn.value = isLoggedIn();
|
||||
positionProfile.value = getSelectedPositionProfile();
|
||||
};
|
||||
|
||||
const handleRequestError = (error: unknown, fallback: string, wasLoggedIn = loggedIn.value) => {
|
||||
@@ -169,6 +173,10 @@ const search = async () => {
|
||||
goLogin();
|
||||
return;
|
||||
}
|
||||
if (!(await ensureEmployeePosition('/pages/user/sop/index'))) {
|
||||
return;
|
||||
}
|
||||
positionProfile.value = getSelectedPositionProfile();
|
||||
loading.value = true;
|
||||
message.value = '';
|
||||
resetResultState();
|
||||
@@ -256,9 +264,13 @@ const goLogin = () => {
|
||||
uni.navigateTo({ url: '/pages/auth/login/index' });
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
const handleShow = async () => {
|
||||
resetPageScroll();
|
||||
refreshAuthState();
|
||||
if (loggedIn.value && !(await ensureEmployeePosition('/pages/user/sop/index'))) {
|
||||
return;
|
||||
}
|
||||
positionProfile.value = getSelectedPositionProfile();
|
||||
if (!loggedIn.value) {
|
||||
resetResultState();
|
||||
}
|
||||
@@ -276,6 +288,10 @@ onShow(() => {
|
||||
} else {
|
||||
message.value = '登录后继续查询该问题';
|
||||
}
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
void handleShow();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
<view class="mentor-hero">
|
||||
<view class="hero-copy">
|
||||
<text class="hero-role">生活顾问</text>
|
||||
<text class="hero-title">客服管家</text>
|
||||
<text class="hero-role">{{ positionProfile.value }}</text>
|
||||
<text class="hero-title">{{ positionProfile.heroTitle }}</text>
|
||||
<text class="hero-sub">有问题,随时问老师傅</text>
|
||||
</view>
|
||||
<image class="mentor-image" src="/static/mentor.png" mode="aspectFit" />
|
||||
@@ -95,9 +95,9 @@
|
||||
<view v-else class="empty">暂无工具入口</view>
|
||||
</view>
|
||||
|
||||
<button class="tip-card" @click="ask('催费话术')">
|
||||
<button class="tip-card" @click="ask(primaryTipQuestion)">
|
||||
<text class="tip-mark">贴</text>
|
||||
<text>今日小贴士:催费沟通先共情,再说明,再给方案。</text>
|
||||
<text>{{ positionProfile.tip }}</text>
|
||||
<text class="tip-arrow">›</text>
|
||||
</button>
|
||||
</view>
|
||||
@@ -109,14 +109,17 @@ import { onShow } from '@dcloudio/uni-app';
|
||||
import type { PracticeAssignment, RoleHome } from '@/types/api';
|
||||
import { getMobileHome } from '@/services/home';
|
||||
import { getPracticeAssignments } from '@/services/practice';
|
||||
import { getAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { getAuth, getSelectedPositionProfile, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { ensureEmployeePosition } from '@/services/position';
|
||||
|
||||
const home = ref<RoleHome | null>(null);
|
||||
const assignments = ref<PracticeAssignment[]>([]);
|
||||
const message = ref('');
|
||||
const loggedIn = ref(false);
|
||||
const quickQuestions = ['催费话术', '投诉应对', '收费标准'];
|
||||
const positionProfile = ref(getSelectedPositionProfile());
|
||||
const quickQuestions = computed(() => positionProfile.value.quickQuestions);
|
||||
const primaryTipQuestion = computed(() => quickQuestions.value[0] || '岗位SOP');
|
||||
const pendingSopQueryKey = 'aihr_sop_pending_query';
|
||||
const pendingAssignmentKey = 'aihr_pending_assignment_id';
|
||||
|
||||
@@ -176,12 +179,17 @@ const mentorBubbleText = computed(() =>
|
||||
|
||||
const loadHome = async () => {
|
||||
loggedIn.value = isLoggedIn();
|
||||
positionProfile.value = getSelectedPositionProfile();
|
||||
message.value = '';
|
||||
assignments.value = [];
|
||||
if (!loggedIn.value) {
|
||||
home.value = null;
|
||||
return;
|
||||
}
|
||||
if (!(await ensureEmployeePosition('/pages/user/today/index'))) {
|
||||
return;
|
||||
}
|
||||
positionProfile.value = getSelectedPositionProfile();
|
||||
const [homeResult, assignmentResult] = await Promise.allSettled([
|
||||
getMobileHome('user'),
|
||||
getPracticeAssignments(true)
|
||||
|
||||
Reference in New Issue
Block a user