feat(aihr): align figma pages for release
This commit is contained in:
@@ -92,17 +92,38 @@
|
||||
</view>
|
||||
<view v-else-if="!historyLoading" class="empty compact-empty">暂无专项指派记录</view>
|
||||
</view>
|
||||
|
||||
<view class="panel progress-panel">
|
||||
<view class="section-head">
|
||||
<text class="panel-title">派发进度与预警</text>
|
||||
<text class="muted">{{ progressSummary }}</text>
|
||||
</view>
|
||||
<view class="progress-metrics">
|
||||
<view><text>{{ progressCounts.pending }}</text><label>待训练</label></view>
|
||||
<view><text>{{ progressCounts.completed }}</text><label>已完成</label></view>
|
||||
<view><text>{{ progressCounts.retry }}</text><label>低分再练</label></view>
|
||||
</view>
|
||||
<view v-if="alerts.length" class="assignment-alert-list">
|
||||
<view v-for="alert in alerts" :key="alert.type" class="assignment-alert" :class="alert.severity">
|
||||
<text>{{ alert.title }}</text>
|
||||
<text>{{ alert.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="detail-empty">当前暂无团队预警</view>
|
||||
</view>
|
||||
</template>
|
||||
<SupervisorTabBar v-if="loggedIn && !accessDenied" active="team" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, ref } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import type { PracticeAssignment } from '@/types/api';
|
||||
import type { PracticeAlert, PracticeAssignment } from '@/types/api';
|
||||
import { clearAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { createPracticeAssignment, getPracticeTeamSnapshot } from '@/services/practice';
|
||||
import { createPracticeAssignment, getPracticeAlerts, getPracticeTeamSnapshot } from '@/services/practice';
|
||||
import SupervisorTabBar from '@/components/SupervisorTabBar.vue';
|
||||
import {
|
||||
createAsyncGenerationGuard,
|
||||
inferSupervisorScenarioId,
|
||||
@@ -118,6 +139,7 @@ const targetName = ref('');
|
||||
const assignmentScenarioId = ref<SupervisorScenarioId>('fee-parking');
|
||||
const assignmentReason = ref('');
|
||||
const assignments = ref<PracticeAssignment[]>([]);
|
||||
const alerts = ref<PracticeAlert[]>([]);
|
||||
const loggedIn = ref(false);
|
||||
const accessDenied = ref(false);
|
||||
const assigning = ref(false);
|
||||
@@ -132,6 +154,15 @@ const selectedTargetLabel = computed(() => {
|
||||
return targetName.value.trim() ? `${targetName.value.trim()} · ${target}` : target;
|
||||
});
|
||||
const recentAssignments = computed(() => manualPracticeAssignments(assignments.value).slice(0, 10));
|
||||
const progressCounts = computed(() => {
|
||||
const items = recentAssignments.value;
|
||||
return {
|
||||
pending: items.filter((item) => item.status !== '已完成').length,
|
||||
completed: items.filter((item) => item.status === '已完成').length,
|
||||
retry: items.filter((item) => item.source === 'retry').length
|
||||
};
|
||||
});
|
||||
const progressSummary = computed(() => `${progressCounts.value.pending} 项未完成`);
|
||||
|
||||
const queryText = (value: unknown) => {
|
||||
if (typeof value !== 'string') return '';
|
||||
@@ -159,6 +190,7 @@ const resetDraft = () => {
|
||||
const clearPrivateState = () => {
|
||||
resetDraft();
|
||||
assignments.value = [];
|
||||
alerts.value = [];
|
||||
historyLoading.value = false;
|
||||
historyRequests.invalidate();
|
||||
};
|
||||
@@ -191,10 +223,11 @@ const loadAssignments = async (force = false) => {
|
||||
const generation = historyRequests.begin();
|
||||
historyLoading.value = true;
|
||||
try {
|
||||
const snapshot = await getPracticeTeamSnapshot();
|
||||
const [snapshot, nextAlerts] = await Promise.all([getPracticeTeamSnapshot(), getPracticeAlerts()]);
|
||||
if (historyRequests.isCurrent(generation)) {
|
||||
accessDenied.value = false;
|
||||
assignments.value = snapshot.assignments;
|
||||
alerts.value = nextAlerts;
|
||||
}
|
||||
} catch (error) {
|
||||
if (historyRequests.isCurrent(generation)) {
|
||||
@@ -306,8 +339,8 @@ onBeforeUnmount(() => {
|
||||
|
||||
<style scoped>
|
||||
.assign-page {
|
||||
padding-bottom: 42px;
|
||||
background: #f4f7fb;
|
||||
padding-bottom: 112px;
|
||||
background: linear-gradient(180deg, var(--employee-bg-soft) 0%, var(--employee-bg) 100%);
|
||||
}
|
||||
|
||||
.login-panel,
|
||||
@@ -368,8 +401,8 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
.scenario-chip.active {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
background: var(--employee-primary-soft);
|
||||
color: var(--employee-primary);
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
@@ -429,4 +462,80 @@ onBeforeUnmount(() => {
|
||||
.compact-empty {
|
||||
padding: 18px 8px 4px;
|
||||
}
|
||||
|
||||
.progress-panel {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.progress-metrics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
padding: 12px 8px;
|
||||
border-radius: 14px;
|
||||
background: var(--employee-primary-soft);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress-metrics view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.progress-metrics text {
|
||||
color: var(--employee-primary);
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.progress-metrics label {
|
||||
color: var(--employee-muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.assignment-alert-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.assignment-alert {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 10px 12px;
|
||||
border-left: 3px solid #f59e0b;
|
||||
border-radius: 10px;
|
||||
background: #fff7e6;
|
||||
}
|
||||
|
||||
.assignment-alert.high {
|
||||
border-left-color: #ef4444;
|
||||
background: #fff1f2;
|
||||
}
|
||||
|
||||
.assignment-alert text:first-child {
|
||||
color: #a15c00;
|
||||
font-size: 13px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.assignment-alert.high text:first-child {
|
||||
color: #c2410c;
|
||||
}
|
||||
|
||||
.assignment-alert text:last-child {
|
||||
color: var(--employee-muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.detail-empty {
|
||||
margin-top: 12px;
|
||||
color: var(--employee-muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<SupervisorTabBar v-if="loggedIn && !accessDenied" active="today" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -138,6 +139,7 @@ import type {
|
||||
import { clearAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { getPracticeAlerts, getPracticeReviews, getPracticeTeamSnapshot } from '@/services/practice';
|
||||
import SupervisorTabBar from '@/components/SupervisorTabBar.vue';
|
||||
import {
|
||||
actionablePracticeAlerts,
|
||||
buildSupervisorMetrics,
|
||||
@@ -259,8 +261,8 @@ onShow(() => {
|
||||
|
||||
<style scoped>
|
||||
.supervisor-page {
|
||||
padding-bottom: 42px;
|
||||
background: #f4f7fb;
|
||||
padding-bottom: 112px;
|
||||
background: linear-gradient(180deg, var(--employee-bg-soft) 0%, var(--employee-bg) 100%);
|
||||
}
|
||||
|
||||
.page-head,
|
||||
@@ -295,27 +297,27 @@ onShow(() => {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e2e8f0;
|
||||
border: 1px solid #dbe7ff;
|
||||
border-radius: 8px;
|
||||
background: #e2e8f0;
|
||||
background: #dbe7ff;
|
||||
}
|
||||
|
||||
.metric-item {
|
||||
min-height: 104px;
|
||||
padding: 14px;
|
||||
background: #f8fafc;
|
||||
background: #f8fbff;
|
||||
}
|
||||
|
||||
.metric-item.primary {
|
||||
background: #fff5f5;
|
||||
background: #eef4ff;
|
||||
}
|
||||
|
||||
.metric-item.primary .metric-value {
|
||||
color: #e70014;
|
||||
color: var(--employee-primary);
|
||||
}
|
||||
|
||||
.metric-item.primary .metric-label {
|
||||
color: #bd0010;
|
||||
color: var(--employee-primary);
|
||||
}
|
||||
|
||||
.metric-value,
|
||||
@@ -361,15 +363,16 @@ onShow(() => {
|
||||
gap: 12px;
|
||||
padding: 12px 0;
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
background: transparent;
|
||||
border: 1px solid #e5edf7;
|
||||
border-radius: 14px;
|
||||
background: #ffffff;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.task-row:last-child,
|
||||
.alert-row:last-child,
|
||||
.preview-row:last-child {
|
||||
border-bottom: 0;
|
||||
border-bottom: 1px solid #e5edf7;
|
||||
}
|
||||
|
||||
.task-copy,
|
||||
@@ -432,7 +435,7 @@ onShow(() => {
|
||||
.preview-score {
|
||||
min-width: 42px;
|
||||
flex: 0 0 auto;
|
||||
color: #dc2626;
|
||||
color: var(--employee-primary);
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
text-align: right;
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<view class="page supervisor-profile-page">
|
||||
<view class="page-head">
|
||||
<view>
|
||||
<text class="page-title">我的主管工作台</text>
|
||||
<text class="page-subtitle">账号与主管身份</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel account-card">
|
||||
<text class="panel-title">当前账号</text>
|
||||
<text class="account-phone">{{ phone || '未登录' }}</text>
|
||||
<text class="muted account-note">主管端团队数据仅对当前项目范围可见。</text>
|
||||
</view>
|
||||
|
||||
<view class="panel identity-card">
|
||||
<text class="panel-title">主管身份</text>
|
||||
<view class="identity-row">
|
||||
<text class="identity-label">角色</text>
|
||||
<text class="identity-value">项目主管</text>
|
||||
</view>
|
||||
<view class="identity-row">
|
||||
<text class="identity-label">可操作范围</text>
|
||||
<text class="identity-value">团队画像、复盘、专项派发</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="secondary-button logout-button" @click="logout">退出登录</button>
|
||||
<SupervisorTabBar active="profile" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
import SupervisorTabBar from '@/components/SupervisorTabBar.vue';
|
||||
import { clearAuth, getAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
|
||||
const phone = ref('');
|
||||
|
||||
const refreshAuth = () => {
|
||||
phone.value = getAuth().phone;
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
clearAuth();
|
||||
rememberLoginRedirect('/pages/supervisor/profile/index');
|
||||
uni.redirectTo({ url: '/pages/auth/login/index' });
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
resetPageScroll();
|
||||
if (!isLoggedIn()) {
|
||||
rememberLoginRedirect('/pages/supervisor/profile/index');
|
||||
uni.redirectTo({ url: '/pages/auth/login/index' });
|
||||
return;
|
||||
}
|
||||
refreshAuth();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.supervisor-profile-page {
|
||||
background: linear-gradient(180deg, var(--employee-bg-soft) 0%, var(--employee-bg) 100%);
|
||||
}
|
||||
|
||||
.account-card,
|
||||
.identity-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.account-phone {
|
||||
color: var(--employee-primary);
|
||||
font-size: 24px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.account-note {
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.identity-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--employee-line);
|
||||
}
|
||||
|
||||
.identity-label {
|
||||
color: var(--employee-muted);
|
||||
}
|
||||
|
||||
.identity-value {
|
||||
color: var(--employee-text);
|
||||
font-weight: 700;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.logout-button {
|
||||
width: 100%;
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -154,6 +154,7 @@
|
||||
<view class="empty">{{ reviewId ? '复盘详情暂不可用' : '缺少有效的复盘ID' }}</view>
|
||||
<button class="secondary-button back-list-button" @click="goToReviews">返回待复盘</button>
|
||||
</view>
|
||||
<SupervisorTabBar v-if="loggedIn && !accessDenied" active="review" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -165,6 +166,7 @@ import { authenticatedFileUrl, releaseAuthenticatedFileUrl } from '@/services/ap
|
||||
import { clearAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { getPracticeReviewDetail, markPracticeReviewed } from '@/services/practice';
|
||||
import SupervisorTabBar from '@/components/SupervisorTabBar.vue';
|
||||
import {
|
||||
createAsyncGenerationGuard,
|
||||
isReviewEditable,
|
||||
@@ -411,8 +413,8 @@ onBeforeUnmount(() => {
|
||||
|
||||
<style scoped>
|
||||
.review-page {
|
||||
padding-bottom: 42px;
|
||||
background: #f4f7fb;
|
||||
padding-bottom: 112px;
|
||||
background: linear-gradient(180deg, var(--employee-bg-soft) 0%, var(--employee-bg) 100%);
|
||||
}
|
||||
|
||||
.subpage-header {
|
||||
@@ -514,8 +516,8 @@ onBeforeUnmount(() => {
|
||||
flex: 0 0 auto;
|
||||
padding: 7px 10px;
|
||||
border-radius: 8px;
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
background: var(--employee-primary-soft);
|
||||
color: var(--employee-primary);
|
||||
font-size: 15px;
|
||||
font-weight: 900;
|
||||
}
|
||||
@@ -618,8 +620,8 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
.scenario-chip.active {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
background: var(--employee-primary-soft);
|
||||
color: var(--employee-primary);
|
||||
}
|
||||
|
||||
.next-training-section {
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
</view>
|
||||
<view v-else-if="!loading" class="empty">暂无待复盘记录</view>
|
||||
</view>
|
||||
<SupervisorTabBar v-if="loggedIn && !accessDenied" active="review" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -65,6 +66,7 @@ import type { PracticeAlert, PracticeRecord } from '@/types/api';
|
||||
import { clearAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { getPracticeAlerts, getPracticeReviews } from '@/services/practice';
|
||||
import SupervisorTabBar from '@/components/SupervisorTabBar.vue';
|
||||
import { buildSupervisorMetrics, isSupervisorAccessDenied } from '@/services/supervisor-view';
|
||||
|
||||
const currentPath = '/pages/supervisor/reviews/index';
|
||||
@@ -176,8 +178,8 @@ onShow(() => {
|
||||
|
||||
<style scoped>
|
||||
.reviews-page {
|
||||
padding-bottom: 42px;
|
||||
background: #f4f7fb;
|
||||
padding-bottom: 112px;
|
||||
background: linear-gradient(180deg, var(--employee-bg-soft) 0%, var(--employee-bg) 100%);
|
||||
}
|
||||
|
||||
.subpage-header {
|
||||
@@ -221,14 +223,14 @@ onShow(() => {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 13px 0;
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
background: transparent;
|
||||
border: 1px solid #e5edf7;
|
||||
border-radius: 14px;
|
||||
background: #ffffff;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.review-row:last-child {
|
||||
border-bottom: 0;
|
||||
border-bottom: 1px solid #e5edf7;
|
||||
}
|
||||
|
||||
.review-copy {
|
||||
@@ -277,7 +279,7 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.review-score text:first-child {
|
||||
color: #dc2626;
|
||||
color: var(--employee-primary);
|
||||
font-size: 24px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,13 @@
|
||||
<text class="muted">按风险排序</text>
|
||||
</view>
|
||||
<view v-if="teamProfiles.length" class="team-list">
|
||||
<view v-for="member in teamProfiles" :key="member.key" class="team-row" :class="member.risk">
|
||||
<view
|
||||
v-for="member in teamProfiles"
|
||||
:key="member.key"
|
||||
class="team-row"
|
||||
:class="member.risk"
|
||||
@click="selectMember(member)"
|
||||
>
|
||||
<view class="team-main">
|
||||
<view class="team-title-row">
|
||||
<text class="team-name">{{ member.displayName }}</text>
|
||||
@@ -65,12 +71,49 @@
|
||||
<text v-if="member.latestScene" class="team-tag">{{ member.latestScene }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<button class="secondary-button team-action" @click="assignMember(member)">派专项</button>
|
||||
<button class="secondary-button team-action" @click.stop="assignMember(member)">派专项</button>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="!loading" class="empty">暂无团队画像数据</view>
|
||||
</view>
|
||||
|
||||
<view v-if="selectedMember" class="panel member-detail-panel">
|
||||
<view class="section-head">
|
||||
<text class="panel-title">{{ selectedMember.displayName }} · 能力详情</text>
|
||||
<button class="text-button" @click="selectedMember = null">收起</button>
|
||||
</view>
|
||||
<view class="member-summary">
|
||||
<view>
|
||||
<text class="member-score">{{ selectedMember.latestScoreText }}</text>
|
||||
<text class="member-summary-label">最近训练评分</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="member-score">{{ memberRecords.length }}</text>
|
||||
<text class="member-summary-label">训练次数</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="member-score">{{ selectedMember.pendingReview }}</text>
|
||||
<text class="member-summary-label">待复盘</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="subsection-title">训练趋势</text>
|
||||
<view v-if="memberRecords.length" class="trend-list">
|
||||
<view v-for="item in memberRecords" :key="item.id || item.time" class="trend-row">
|
||||
<text class="trend-scene">{{ item.scene }}</text>
|
||||
<view class="trend-track"><view class="trend-fill" :style="{ width: `${Math.max(0, Math.min(100, item.score))}%` }" /></view>
|
||||
<text class="trend-score">{{ item.score }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="detail-empty">暂无训练评分,先派发一次专项训练</view>
|
||||
<text class="subsection-title">成长证据</text>
|
||||
<view class="evidence-list">
|
||||
<text>已完成 {{ memberRecords.filter((item) => item.status === '已完成').length }} 次训练</text>
|
||||
<text>当前 {{ selectedMember.pendingAssignment }} 项专项待训练</text>
|
||||
<text>{{ selectedMember.reason }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<SupervisorTabBar v-if="loggedIn && !accessDenied" active="team" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -81,6 +124,7 @@ import type { PracticeAssignment, PracticeRecord, PracticeTeamMember } from '@/t
|
||||
import { clearAuth, isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { getPracticeTeamSnapshot } from '@/services/practice';
|
||||
import SupervisorTabBar from '@/components/SupervisorTabBar.vue';
|
||||
import {
|
||||
buildTeamProfiles,
|
||||
inferSupervisorScenarioId,
|
||||
@@ -96,6 +140,7 @@ const loggedIn = ref(false);
|
||||
const accessDenied = ref(false);
|
||||
const loading = ref(false);
|
||||
const message = ref('');
|
||||
const selectedMember = ref<TeamProfile | null>(null);
|
||||
|
||||
const teamProfiles = computed(() => buildTeamProfiles(records.value, assignments.value, members.value));
|
||||
const riskCounts = computed(() => teamProfiles.value.reduce(
|
||||
@@ -105,11 +150,18 @@ const riskCounts = computed(() => teamProfiles.value.reduce(
|
||||
},
|
||||
{ high: 0, medium: 0, low: 0 }
|
||||
));
|
||||
const memberRecords = computed(() => {
|
||||
if (!selectedMember.value) return [];
|
||||
return records.value
|
||||
.filter((item) => item.extPartyId === selectedMember.value?.extPartyId)
|
||||
.slice(0, 6);
|
||||
});
|
||||
|
||||
const clearData = () => {
|
||||
records.value = [];
|
||||
assignments.value = [];
|
||||
members.value = [];
|
||||
selectedMember.value = null;
|
||||
};
|
||||
|
||||
const refreshAuthState = () => {
|
||||
@@ -167,6 +219,10 @@ const assignMember = (member: TeamProfile) => {
|
||||
uni.navigateTo({ url: `/pages/supervisor/assign/index?${query}` });
|
||||
};
|
||||
|
||||
const selectMember = (member: TeamProfile) => {
|
||||
selectedMember.value = selectedMember.value?.key === member.key ? null : member;
|
||||
};
|
||||
|
||||
const goLogin = () => {
|
||||
rememberLoginRedirect(currentPath);
|
||||
uni.redirectTo({ url: '/pages/auth/login/index' });
|
||||
@@ -201,8 +257,8 @@ onShow(() => {
|
||||
|
||||
<style scoped>
|
||||
.team-page {
|
||||
padding-bottom: 42px;
|
||||
background: #f4f7fb;
|
||||
padding-bottom: 112px;
|
||||
background: linear-gradient(180deg, var(--employee-bg-soft) 0%, var(--employee-bg) 100%);
|
||||
}
|
||||
|
||||
.subpage-header {
|
||||
@@ -239,7 +295,7 @@ onShow(() => {
|
||||
display: grid;
|
||||
margin-top: 16px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e2e8f0;
|
||||
border: 1px solid #dbe7ff;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
@@ -247,7 +303,7 @@ onShow(() => {
|
||||
|
||||
.risk-item {
|
||||
padding: 13px 8px;
|
||||
border-right: 1px solid #e2e8f0;
|
||||
border-right: 1px solid #dbe7ff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -281,7 +337,7 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.risk-item.low .risk-value {
|
||||
color: #15803d;
|
||||
color: var(--employee-primary);
|
||||
}
|
||||
|
||||
.team-list {
|
||||
@@ -294,8 +350,10 @@ onShow(() => {
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 14px 0 14px 12px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
border-left: 4px solid #22c55e;
|
||||
border: 1px solid #e5edf7;
|
||||
border-left: 4px solid var(--employee-primary);
|
||||
border-radius: 14px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.team-row:last-child {
|
||||
@@ -350,8 +408,8 @@ onShow(() => {
|
||||
.team-tag {
|
||||
padding: 4px 7px;
|
||||
border-radius: 8px;
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
background: var(--employee-primary-soft);
|
||||
color: var(--employee-primary);
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
}
|
||||
@@ -363,4 +421,110 @@ onShow(() => {
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.member-detail-panel {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.text-button {
|
||||
min-height: 32px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: var(--employee-primary);
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.member-summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
padding: 12px 8px;
|
||||
border-radius: 14px;
|
||||
background: var(--employee-primary-soft);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.member-score,
|
||||
.member-summary-label,
|
||||
.trend-scene,
|
||||
.trend-score,
|
||||
.subsection-title,
|
||||
.evidence-list text {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.member-score {
|
||||
color: var(--employee-primary);
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.member-summary-label {
|
||||
margin-top: 4px;
|
||||
color: var(--employee-muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.subsection-title {
|
||||
margin-top: 16px;
|
||||
color: var(--employee-text);
|
||||
font-size: 14px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.trend-list {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.trend-row {
|
||||
display: grid;
|
||||
grid-template-columns: 92px 1fr 30px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.trend-scene,
|
||||
.trend-score {
|
||||
overflow: hidden;
|
||||
color: var(--employee-muted);
|
||||
font-size: 12px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.trend-score {
|
||||
color: var(--employee-primary);
|
||||
font-weight: 900;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.trend-track {
|
||||
height: 8px;
|
||||
overflow: hidden;
|
||||
border-radius: 999px;
|
||||
background: #e7eefc;
|
||||
}
|
||||
|
||||
.trend-fill {
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(90deg, #6f9cff, #2f6bff);
|
||||
}
|
||||
|
||||
.evidence-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.evidence-list text,
|
||||
.detail-empty {
|
||||
color: var(--employee-muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user