feat: 提交教师专业发展前台(profession-view)工程源码(Vue3 + Vite + view-ui-plus)
build / build (push) Successful in 4m13s

This commit is contained in:
lijing
2026-09-30 16:15:36 +08:00
parent cfd0a18ba5
commit e6decedae4
344 changed files with 29911 additions and 2 deletions
+167
View File
@@ -0,0 +1,167 @@
<script setup lang="ts">
/**
* 个人空间侧栏(方案D原型 page-space 左列):
* panel 1 = space-head 蓝渐变头(字章头像/姓名/积分)+ space-statrow(累计记录/获赞/被访问)
* + points-wrap 积分进度条 + space-menu 栏目菜单(sm-item + 篇数徽章)
* panel 2 = 本月概览(ov-grid 四色,仅 full 模式)
* panel 3 = 最近访客(visitors 圆字章:默认一排,超过一排显示「查看全部访客」就地展开全部)
* mode:full=栏目列表·个人空间/我的首页完整版;mini=发布编辑、记录详情精简版(无统计行/进度条/本月概览)。
*/
const props = withDefaults(
defineProps<{
/** 我的 / 他的 */
himWord?: string;
teacherName?: string;
points?: number | string;
/** 头像 URL(仅本人登录态可能有;他人显示姓名首字圆形) */
avatar?: string;
/** 该教师各栏目篇数:{ id, name, recordCount } */
modules?: any[];
/** 最近访客:{ visitorId, visitorName } */
visitors?: any[];
/** 侧栏访客一排展示个数(面板内宽 262px / 章 26px + 间距 8px = 7 个),超出折叠 */
visitorRowSize?: number;
/** 当前高亮栏目(个人首页无高亮传 null) */
activeMouid?: number | null;
/** 当前已处于「我的首页」时,首页链接呈当前态 */
homeCurrent?: boolean;
/** 空间聚合(SpaceOverview:recordTotal/likeTotal/visitTotal/month)——传入则显示统计行与本月概览 */
overview?: Record<string, any> | null;
/** full=完整版侧栏;mini=精简版(发布/详情页) */
mode?: "full" | "mini";
}>(),
{ himWord: "我的", teacherName: "", points: 0, avatar: "", modules: () => [], visitors: () => [], visitorRowSize: 7, activeMouid: null, homeCurrent: false, overview: null, mode: "full" }
);
import { ref, watch } from "vue";
const emit = defineEmits<{
(e: "home"): void;
(e: "module", m: any): void;
(e: "visitor", v: any): void;
}>();
/** 访客展开态:折叠=只显示一排;展开=全部 */
const visitorsOpen = ref(false);
watch(
() => props.visitors,
() => {
visitorsOpen.value = false;
}
);
/** 积分进度刻度(满刻度 1000,超出按 100%) */
function pointsWidth(): number {
const p = Number(props.points || 0);
return Math.max(4, Math.min(100, Math.round((p / 1000) * 100)));
}
function firstChar(name?: string): string {
return (name || "?").replace(/\s/g, "").slice(0, 1) || "?";
}
</script>
<template>
<aside class="col-side space-side">
<!-- ============ 个人空间卡 ============ -->
<section class="panel">
<div class="space-head">
<img v-if="avatar" class="space-avatar" :src="avatar" alt="" />
<div v-else class="space-avatar">{{ firstChar(teacherName) }}</div>
<div class="space-name">{{ teacherName || "—" }}</div>
<div class="space-points">积分 <b>{{ points || 0 }}</b> 分</div>
<a class="space-home-link" :class="{ current: homeCurrent }" href="javascript:void(0)" @click.prevent="emit('home')">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10.5L12 3l9 7.5"/><path d="M5 9.5V21h14V9.5"/><path d="M10 21v-6h4v6"/></svg>
{{ himWord }}首页
</a>
</div>
<div v-if="mode === 'full' && overview" class="space-statrow">
<div class="ss"><b>{{ overview.recordTotal ?? 0 }}</b><span>累计记录</span></div>
<div class="ss"><b>{{ overview.likeTotal ?? 0 }}</b><span>获赞</span></div>
<div class="ss"><b>{{ overview.visitTotal ?? 0 }}</b><span>被访问</span></div>
</div>
<div v-if="mode === 'full'" class="points-wrap">
<div class="points-lab"><span>成长积分</span><b>{{ points || 0 }} / 1000</b></div>
<div class="points-bar"><i :style="{ width: pointsWidth() + '%' }"></i></div>
<div class="points-hint">积分随发布与互动累积</div>
</div>
<div class="space-menu">
<div
v-for="m in modules"
:key="m.id"
class="sm-item"
:class="{ on: m.id === activeMouid }"
@click="emit('module', m)"
>
{{ m.name }}<span class="ct">{{ m.recordCount ?? 0 }}</span>
</div>
<div v-if="!modules.length" class="sm-empty">暂无栏目数据</div>
</div>
</section>
<!-- ============ 本月概览(仅完整版) ============ -->
<section v-if="mode === 'full' && overview && overview.month" class="panel">
<div class="panel-hd"><h3>本月概览</h3><span class="ext">{{ new Date().getMonth() + 1 }}月</span></div>
<div class="ov-grid">
<div class="ov-item"><b style="color:#2b82ec"><i style="background:#2b82ec"></i>{{ overview.month.publish ?? 0 }}</b><span>本月发布</span></div>
<div class="ov-item"><b style="color:#21b8a6"><i style="background:#21b8a6"></i>{{ overview.month.comments ?? 0 }}</b><span>新获评论</span></div>
<div class="ov-item"><b style="color:#8b6ff0"><i style="background:#8b6ff0"></i>{{ overview.month.reads ?? 0 }}</b><span>新增阅读</span></div>
<div class="ov-item"><b style="color:#f0a13a"><i style="background:#f0a13a"></i>{{ overview.month.visitors ?? 0 }}</b><span>新访客</span></div>
</div>
</section>
<!-- ============ 最近访客(默认一排;超过一排显示「查看全部访客」就地展开) ============ -->
<section class="panel">
<div class="panel-hd"><h3>最近访客</h3></div>
<div class="visitors" :class="{ open: visitorsOpen }">
<a
v-for="(v, i) in visitorsOpen ? visitors : visitors.slice(0, visitorRowSize)"
:key="i"
class="v"
href="javascript:void(0)"
:title="v.visitorName"
@click.prevent="v.visitorId && emit('visitor', v)"
>{{ firstChar(v.visitorName) }}</a>
<span v-if="!visitors.length" class="sm-empty" style="padding:4px 0 12px">暂无访客</span>
</div>
<a
v-if="visitors.length > visitorRowSize"
class="v-more"
href="javascript:void(0)"
@click.prevent="visitorsOpen = !visitorsOpen"
>{{ visitorsOpen ? "收起访客 ▴" : `查看全部访客(${visitors.length})▾` }}</a>
</section>
</aside>
</template>
<style scoped>
/* col-side / panel / space-head / sm-item / visitors 等主样式由全局 profession-ui.css 提供 */
.space-side {
width: 296px;
flex: none;
}
.space-head img.space-avatar {
object-fit: cover;
}
/* 空间头内的首页入口(挂在积分下方):全页面统一玻璃半透明胶囊(用户指定第二种样式),不再区分 current 态 */
.space-home-link {
display: inline-flex;
align-items: center;
gap: 5px;
margin-top: 9px;
font-size: 12px;
color: #fff;
background: rgba(255, 255, 255, 0.26);
border: 1px solid rgba(255, 255, 255, 0.45);
border-radius: 999px;
padding: 3px 12px;
transition: all 0.2s;
}
.space-home-link:hover {
background: rgba(255, 255, 255, 0.38);
}
.sm-empty {
padding: 10px 12px 12px;
font-size: 12px;
color: var(--muted, #8fa9c4);
}
</style>