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:
@@ -29,6 +29,7 @@
|
|||||||
v-for="slot in timeSlots"
|
v-for="slot in timeSlots"
|
||||||
:key="slot.key"
|
:key="slot.key"
|
||||||
class="header-cell sr-cell"
|
class="header-cell sr-cell"
|
||||||
|
:style="{ width: columnWidths[slot.key] }"
|
||||||
>
|
>
|
||||||
<div class="header-label">{{ slot.label }}</div>
|
<div class="header-label">{{ slot.label }}</div>
|
||||||
<div class="header-time">{{ slot.time }}</div>
|
<div class="header-time">{{ slot.time }}</div>
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
v-for="slot in timeSlots"
|
v-for="slot in timeSlots"
|
||||||
:key="`${day.dateKey}_${slot.key}`"
|
:key="`${day.dateKey}_${slot.key}`"
|
||||||
class="event-cell sr-cell"
|
class="event-cell sr-cell"
|
||||||
|
:style="{ width: columnWidths[slot.key] }"
|
||||||
>
|
>
|
||||||
<template v-if="eventMap[day.dateKey]?.[slot.key]?.length">
|
<template v-if="eventMap[day.dateKey]?.[slot.key]?.length">
|
||||||
<div
|
<div
|
||||||
@@ -154,6 +156,21 @@ export default defineComponent({
|
|||||||
return map;
|
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) {
|
function eventStyle(ev: NormalizedClazzEvent) {
|
||||||
return {
|
return {
|
||||||
backgroundColor: ev.color || 'rgba(55, 136, 216, 0.12)',
|
backgroundColor: ev.color || 'rgba(55, 136, 216, 0.12)',
|
||||||
@@ -169,7 +186,7 @@ export default defineComponent({
|
|||||||
emit('event-click', ev);
|
emit('event-click', ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { timeSlots, weekDays, eventMap, fmtTime, eventStyle, onCellClick, onEventClick };
|
return { timeSlots, weekDays, eventMap, columnWidths, fmtTime, eventStyle, onCellClick, onEventClick };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -306,8 +323,12 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── 课块容器 ─── */
|
/* ─── 课块容器(横向排列) ─── */
|
||||||
.event-cell {
|
.event-cell {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 0.03rem;
|
||||||
|
align-items: stretch;
|
||||||
padding: 0.03rem;
|
padding: 0.03rem;
|
||||||
border-bottom: 1px solid #f5f5f5;
|
border-bottom: 1px solid #f5f5f5;
|
||||||
border-left: 1px solid #f5f5f5;
|
border-left: 1px solid #f5f5f5;
|
||||||
@@ -315,13 +336,12 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.event-block {
|
.event-block {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
padding: 0.04rem 0.06rem;
|
padding: 0.04rem 0.06rem;
|
||||||
border-radius: 0.04rem;
|
border-radius: 0.04rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 0.02rem;
|
|
||||||
|
|
||||||
&:last-child { margin-bottom: 0; }
|
|
||||||
|
|
||||||
.ev-title {
|
.ev-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -307,6 +307,10 @@ export default defineComponent({
|
|||||||
repeatList: computed(() => store.repeatList),
|
repeatList: computed(() => store.repeatList),
|
||||||
subsidiaryClazzByHtyId: computed(() => store.subsidiaryClazzByHtyId),
|
subsidiaryClazzByHtyId: computed(() => store.subsidiaryClazzByHtyId),
|
||||||
getViewingSubsidiaries: () => viewingSubsidiaries.value,
|
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 comments_count = computed(() => count_comments(store.current.id))
|
||||||
|
|
||||||
const searchForSubsidiaries = async () => {
|
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;
|
subsidiaries.value = usingSupervisor.store.subsidiaries;
|
||||||
if (!subsidiaries.value.length) {
|
if (!subsidiaries.value.length) {
|
||||||
await usingSupervisor.getSubsidiaries();
|
await usingSupervisor.getSubsidiaries();
|
||||||
|
|||||||
@@ -98,8 +98,10 @@ export function useClazzViewModel(params: {
|
|||||||
subsidiaryClazzByHtyId: ComputedRef<Record<string, { list: Clazz[]; repeatList: ClazzRepeatRow[] }>>;
|
subsidiaryClazzByHtyId: ComputedRef<Record<string, { list: Clazz[]; repeatList: ClazzRepeatRow[] }>>;
|
||||||
/** Getter 避免 TDZ hoisting 问题 */
|
/** Getter 避免 TDZ hoisting 问题 */
|
||||||
getViewingSubsidiaries: () => string[];
|
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[]>(() => {
|
const normalizedEvents = computed<NormalizedClazzEvent[]>(() => {
|
||||||
@@ -125,15 +127,16 @@ export function useClazzViewModel(params: {
|
|||||||
// 下属老师的排课
|
// 下属老师的排课
|
||||||
for (const [htyId, data] of Object.entries(subsidiaryClazzByHtyId.value)) {
|
for (const [htyId, data] of Object.entries(subsidiaryClazzByHtyId.value)) {
|
||||||
const hidden = !getViewingSubsidiaries().includes(htyId);
|
const hidden = !getViewingSubsidiaries().includes(htyId);
|
||||||
|
const color = getSubsidiaryColor?.(htyId);
|
||||||
for (const item of data.list) {
|
for (const item of data.list) {
|
||||||
if (item.is_delete) continue;
|
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) {
|
for (const row of data.repeatList) {
|
||||||
if (row.is_delete) continue;
|
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) {
|
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 }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user