Pure moves only — zero behavior change, template untouched:
- types.ts (447), utils.ts (51), data/{auth,navigation,practice,mentor,
home,supervisor,icons}.ts, styles/app.css (4690, still global non-scoped
via <style src>)
- App.vue 9006 → 3559 lines; 98 moved declarations verbatim except
export/import syntax
- Built CSS hash identical to pre-refactor (byte-equal styles); reactive
state, computed, watchers, lifecycle and templates all stay in App.vue
- Component-level split deferred to phase two after 2026-07-10 Alpha
52 lines
2.4 KiB
TypeScript
52 lines
2.4 KiB
TypeScript
import type { MobileRoute, PracticeRole, RoleKey, SupervisorPanelKey, UserPanelKey } from './types';
|
|
import { aliases, candidatePanelSegments, rolePaths, supervisorPanelSegments, userPanelSegments } from './data/navigation';
|
|
|
|
export const pathParts = () => window.location.pathname.split('/').filter(Boolean);
|
|
|
|
export const readRole = (): RoleKey => {
|
|
const parts = pathParts();
|
|
const part = parts[0] === 'h5' ? parts[1] : parts.find((item) => aliases[item]);
|
|
return aliases[part || 'user'] || 'user';
|
|
};
|
|
|
|
export const readRoute = (): MobileRoute => {
|
|
const parts = pathParts();
|
|
const offset = parts[0] === 'h5' ? 2 : 1;
|
|
return { role: readRole(), page: parts[offset] || '', detail: parts[offset + 1] || '' };
|
|
};
|
|
|
|
export const clampPercent = (value: number) => `${Math.max(0, Math.min(value, 100))}%`;
|
|
|
|
export const readStoredCredential = (key: string) => {
|
|
const value = localStorage.getItem(key)?.trim() || '';
|
|
return value === 'undefined' || value === 'null' ? '' : value;
|
|
};
|
|
|
|
export const practiceAvatarSrc = (role: PracticeRole) => `${import.meta.env.BASE_URL}practice/${role === 'customer' ? 'customer' : role === 'trainee' ? 'trainee' : 'coach'}-avatar.svg`;
|
|
|
|
export const practiceTurnDuration = (role: PracticeRole, index: number) => (role === 'coach' ? '00:15' : role === 'trainee' ? '已发送' : index > 0 ? '00:12' : '00:18');
|
|
|
|
export const formatPracticeScore = (score: number | string) => {
|
|
const value = Number(score);
|
|
return Number.isFinite(value) ? value.toFixed(1) : String(score || '4.0');
|
|
};
|
|
|
|
export const scoreStars = (score: number | string) => {
|
|
const value = Number(score);
|
|
const filled = Number.isFinite(value) ? Math.max(0, Math.min(5, Math.round(value))) : 4;
|
|
return `${'★ '.repeat(filled)}${'☆ '.repeat(5 - filled)}`.trim();
|
|
};
|
|
|
|
export const mapIconPath = (name: string) => `${import.meta.env.BASE_URL}map-icons/${name}.png`;
|
|
|
|
export const userPanelPath = (panel: UserPanelKey) =>
|
|
panel ? `${rolePaths.user}/${userPanelSegments[panel]}` : rolePaths.user;
|
|
|
|
export const candidatePanelPath = (panel: UserPanelKey) =>
|
|
panel ? `${rolePaths.candidate}/${candidatePanelSegments[panel]}` : rolePaths.candidate;
|
|
|
|
export const supervisorPanelPath = (panel: SupervisorPanelKey) =>
|
|
panel ? `${rolePaths.supervisor}/${supervisorPanelSegments[panel]}` : rolePaths.supervisor;
|
|
|
|
export const ringStyle = (value: number) => ({ '--value': value } as Record<string, string | number>);
|