fix: supervisor role check for subsidiary data + color passthrough in matrix view

Subsidiary courses now only appear in SUPERVISOR mode, not TEACHER mode.
Matrix view shows each subsidiary teacher's courses with a distinct color.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-02 10:22:22 +08:00
parent ba05fc1b3a
commit bb19834725
3 changed files with 38 additions and 11 deletions
+25 -5
View File
@@ -29,6 +29,7 @@
v-for="slot in timeSlots"
:key="slot.key"
class="header-cell sr-cell"
:style="{ width: columnWidths[slot.key] }"
>
<div class="header-label">{{ slot.label }}</div>
<div class="header-time">{{ slot.time }}</div>
@@ -46,6 +47,7 @@
v-for="slot in timeSlots"
:key="`${day.dateKey}_${slot.key}`"
class="event-cell sr-cell"
:style="{ width: columnWidths[slot.key] }"
>
<template v-if="eventMap[day.dateKey]?.[slot.key]?.length">
<div
@@ -154,6 +156,21 @@ export default defineComponent({
return map;
});
/** 每列宽度 = 按该列最多课块数量计算,课块横向排列时宽度自适应 */
const columnWidths = computed(() => {
const widths: Record<string, string> = {};
for (const slot of timeSlots.value) {
let maxN = 1;
for (const day of weekDays.value) {
const evts = eventMap.value[day.dateKey]?.[slot.key] || [];
if (evts.length > maxN) maxN = evts.length;
}
// 每个课块至少 1.4rem,列宽 = 数量 × 1.4rem + 间距
widths[slot.key] = `${Math.max(1.8, maxN * 1.4 + (maxN - 1) * 0.03)}rem`;
}
return widths;
});
function eventStyle(ev: NormalizedClazzEvent) {
return {
backgroundColor: ev.color || 'rgba(55, 136, 216, 0.12)',
@@ -169,7 +186,7 @@ export default defineComponent({
emit('event-click', ev);
}
return { timeSlots, weekDays, eventMap, fmtTime, eventStyle, onCellClick, onEventClick };
return { timeSlots, weekDays, eventMap, columnWidths, fmtTime, eventStyle, onCellClick, onEventClick };
},
});
</script>
@@ -306,8 +323,12 @@ export default defineComponent({
}
}
/* ─── 课块容器 ─── */
/* ─── 课块容器(横向排列) ─── */
.event-cell {
display: flex;
flex-direction: row;
gap: 0.03rem;
align-items: stretch;
padding: 0.03rem;
border-bottom: 1px solid #f5f5f5;
border-left: 1px solid #f5f5f5;
@@ -315,13 +336,12 @@ export default defineComponent({
}
.event-block {
flex: 1;
min-width: 0;
padding: 0.04rem 0.06rem;
border-radius: 0.04rem;
cursor: pointer;
overflow: hidden;
margin-bottom: 0.02rem;
&:last-child { margin-bottom: 0; }
.ev-title {
font-weight: 600;
+6 -2
View File
@@ -307,6 +307,10 @@ export default defineComponent({
repeatList: computed(() => store.repeatList),
subsidiaryClazzByHtyId: computed(() => store.subsidiaryClazzByHtyId),
getViewingSubsidiaries: () => viewingSubsidiaries.value,
getSubsidiaryColor: (htyId) => {
const idx = subsidiaries.value.findIndex(x => x.to_user_id === htyId);
return idx >= 0 ? Colors[idx] : undefined;
},
});
// 切换视图时缓存
@@ -493,8 +497,8 @@ export default defineComponent({
const comments_count = computed(() => count_comments(store.current.id))
const searchForSubsidiaries = async () => {
// query clazz rows of subsidiary teachers under current user if user is supervisor
if (usingUser.store.currentRole === HtyBaseRoles.TEACHER && usingUser.store.current.roles.some(r => r.role_key === HtySuperRoles.SUPERVISOR)) {
// 主管老师角色下才加载下属老师的排课
if (usingUser.store.currentRole !== HtyBaseRoles.TEACHER && usingUser.store.current.roles.some(r => r.role_key === HtySuperRoles.SUPERVISOR)) {
subsidiaries.value = usingSupervisor.store.subsidiaries;
if (!subsidiaries.value.length) {
await usingSupervisor.getSubsidiaries();
+7 -4
View File
@@ -98,8 +98,10 @@ export function useClazzViewModel(params: {
subsidiaryClazzByHtyId: ComputedRef<Record<string, { list: Clazz[]; repeatList: ClazzRepeatRow[] }>>;
/** Getter 避免 TDZ hoisting 问题 */
getViewingSubsidiaries: () => string[];
/** 下属老师颜色映射,htyId → 颜色 */
getSubsidiaryColor?: (htyId: string) => string | undefined;
}) {
const { list, repeatList, subsidiaryClazzByHtyId, getViewingSubsidiaries } = params;
const { list, repeatList, subsidiaryClazzByHtyId, getViewingSubsidiaries, getSubsidiaryColor } = params;
/** 扁平化的标准化事件列表 */
const normalizedEvents = computed<NormalizedClazzEvent[]>(() => {
@@ -125,15 +127,16 @@ export function useClazzViewModel(params: {
// 下属老师的排课
for (const [htyId, data] of Object.entries(subsidiaryClazzByHtyId.value)) {
const hidden = !getViewingSubsidiaries().includes(htyId);
const color = getSubsidiaryColor?.(htyId);
for (const item of data.list) {
if (item.is_delete) continue;
out.push(makeEvent(item, false, true, { subsidiaryTeacherId: htyId, visible: !hidden }));
out.push(makeEvent(item, false, true, { subsidiaryTeacherId: htyId, visible: !hidden, color }));
}
for (const row of data.repeatList) {
if (row.is_delete) continue;
out.push(makeEvent(row, true, true, { subsidiaryTeacherId: htyId, visible: !hidden }));
out.push(makeEvent(row, true, true, { subsidiaryTeacherId: htyId, visible: !hidden, color }));
if (row.instance && row.instance.id) {
out.push(makeEvent(row.instance, false, true, { subsidiaryTeacherId: htyId, visible: !hidden }));
out.push(makeEvent(row.instance, false, true, { subsidiaryTeacherId: htyId, visible: !hidden, color }));
}
}
}