forked from liweijie/education
330 lines
8.6 KiB
Vue
330 lines
8.6 KiB
Vue
<template>
|
||
<div class="semester-bar">
|
||
<!-- 左:当前学期信息 -->
|
||
<div class="semester-bar__item semester-bar__current">
|
||
<i class="el-icon-date" />
|
||
<span class="label">当前学期</span>
|
||
<span class="value">{{ currentSemesterName }}</span>
|
||
<span v-if="currentSemesterDate" class="date">{{ currentSemesterDate }}</span>
|
||
</div>
|
||
|
||
<span class="semester-bar__divider" />
|
||
|
||
<!-- 中:今日日期 -->
|
||
<div class="semester-bar__item semester-bar__date">
|
||
<i class="el-icon-time" />
|
||
<span class="value">{{ todayText }}</span>
|
||
<span class="week">{{ weekText }}</span>
|
||
</div>
|
||
|
||
<span class="semester-bar__divider" />
|
||
|
||
<!-- 右:学期切换下拉框 -->
|
||
<div class="semester-bar__item semester-bar__switch">
|
||
<span class="label">切换学期</span>
|
||
<div class="semester-select-wrap">
|
||
<el-select
|
||
:value="activeSemester ? activeSemester.nd : ''"
|
||
placeholder="请选择学期"
|
||
size="small"
|
||
filterable
|
||
class="semester-select"
|
||
popper-class="semester-select-popper"
|
||
@change="handleSwitch"
|
||
>
|
||
<el-option
|
||
v-for="item in semesterList"
|
||
:key="item.nd"
|
||
:label="getSemesterName(item.nd)"
|
||
:value="item.nd"
|
||
/>
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { listSemester, updateSemester } from '@/api/teachBusiness/semester'
|
||
|
||
export default {
|
||
name: 'SemesterBar',
|
||
data() {
|
||
return {
|
||
// 学期列表(接口数据)
|
||
semesterList: [],
|
||
// 当前选中的学期对象
|
||
activeSemester: null,
|
||
today: new Date(),
|
||
loading: false
|
||
}
|
||
},
|
||
computed: {
|
||
currentSemesterName() {
|
||
return this.getSemesterName(this.activeSemester ? this.activeSemester.nd : '')
|
||
},
|
||
currentSemesterDate() {
|
||
const item = this.activeSemester
|
||
if (!item) return ''
|
||
const start = this.formatDate(item.kxrq)
|
||
const end = this.formatDate(item.jsrq)
|
||
if (!start && !end) return ''
|
||
return `${start || '?'} ~ ${end || '?'}`
|
||
},
|
||
todayText() {
|
||
const d = this.today
|
||
const pad = n => (n < 10 ? '0' + n : '' + n)
|
||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`
|
||
},
|
||
weekText() {
|
||
const weeks = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
||
return weeks[this.today.getDay()]
|
||
}
|
||
},
|
||
created() {
|
||
this.getSemesterList()
|
||
},
|
||
methods: {
|
||
/** 加载学期列表,默认选中当前学期(dqxq=true),无当前学期则选第一条 */
|
||
getSemesterList() {
|
||
this.loading = true
|
||
listSemester({ pageNum: 1, pageSize: 100 }).then(response => {
|
||
const data = response.data || {}
|
||
const list = data.records || []
|
||
this.semesterList = list
|
||
const current = list.find(i => i.dqxq)
|
||
this.activeSemester = current || (list[0] ? list[0] : null)
|
||
this.loading = false
|
||
}).catch(() => {
|
||
this.semesterList = []
|
||
this.loading = false
|
||
})
|
||
},
|
||
/** 格式化后端 LocalDateTime(2026-02-23T00:00:00 -> 2026-02-23) */
|
||
formatDate(value) {
|
||
if (!value) return ''
|
||
return String(value).slice(0, 10)
|
||
},
|
||
/** 学期代号 -> 学期名称(如 202601 -> 2026年春季学期) */
|
||
getSemesterName(nd) {
|
||
if (!nd) return ''
|
||
const s = String(nd)
|
||
const xn = s.slice(0, 4)
|
||
const xq = s.slice(4)
|
||
const map = { '01': '春季学期', '02': '夏季学期', '03': '秋季学期' }
|
||
return xn + '年' + (map[xq] || '第' + xq + '学期')
|
||
},
|
||
/** 切换学期:将目标学期设为当前学期,原当前学期取消 */
|
||
handleSwitch(nd) {
|
||
const current = this.activeSemester
|
||
if (!nd || (current && nd === current.nd)) return
|
||
const target = this.semesterList.find(i => i.nd === nd)
|
||
if (!target) return
|
||
// 当前已是"当前学期"的其他记录(需取消)
|
||
const others = this.semesterList.filter(i => i.dqxq && i.nd !== nd)
|
||
|
||
updateSemester({ ...target, dqxq: true }).then(() => {
|
||
// 若存在原当前学期,一并取消
|
||
if (others.length) {
|
||
return Promise.all(others.map(o => updateSemester({ ...o, dqxq: false })))
|
||
}
|
||
return Promise.resolve()
|
||
}).then(() => {
|
||
this.activeSemester = target
|
||
this.$message.success(`已切换至:${this.getSemesterName(nd)}`)
|
||
// 重新拉取,同步最新 dqxq 状态
|
||
this.getSemesterList()
|
||
// 通知学期管理页面自动刷新
|
||
this.$root.$emit('semester-current-changed')
|
||
}).catch(() => {
|
||
this.$message.error('切换学期失败,请稍后重试')
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.semester-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 10px;
|
||
height: 36px;
|
||
font-size: 13px;
|
||
color: rgba(255, 255, 255, 0.85);
|
||
user-select: none;
|
||
|
||
&__item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 36px;
|
||
padding: 0 16px;
|
||
border-radius: 7px;
|
||
background: rgba(255, 255, 255, 0.09);
|
||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||
box-sizing: border-box;
|
||
|
||
i {
|
||
color: #fff;
|
||
margin-right: 7px;
|
||
font-size: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
height: 100%;
|
||
}
|
||
|
||
.label,
|
||
.value,
|
||
.week,
|
||
.date {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 100%;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.label {
|
||
color: rgba(255, 255, 255, 0.75);
|
||
margin-right: 6px;
|
||
}
|
||
|
||
.value {
|
||
font-weight: 600;
|
||
color: #fff;
|
||
}
|
||
|
||
.date {
|
||
margin-left: 10px;
|
||
font-size: 12px;
|
||
color: rgba(255, 255, 255, 0.75);
|
||
letter-spacing: 0.5px;
|
||
padding-left: 10px;
|
||
border-left: 1px solid rgba(255, 255, 255, 0.25);
|
||
}
|
||
|
||
.week {
|
||
margin-left: 8px;
|
||
color: rgba(255, 255, 255, 0.75);
|
||
}
|
||
}
|
||
|
||
&__current {
|
||
margin-right: 0;
|
||
}
|
||
|
||
&__date {
|
||
margin-right: 0;
|
||
}
|
||
|
||
/* 胶囊式布局后不再需要分隔线 */
|
||
&__divider {
|
||
display: none;
|
||
}
|
||
|
||
&__switch {
|
||
background: rgba(255, 255, 255, 0.09);
|
||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||
padding: 0 16px;
|
||
border-radius: 7px;
|
||
height: 36px;
|
||
|
||
/* 用一层定高 flex 容器包裹 select,彻底隔离其默认行高外溢 */
|
||
.semester-select-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 36px;
|
||
}
|
||
|
||
.semester-select {
|
||
width: 200px;
|
||
}
|
||
|
||
/* 关闭 select/input 外层默认行高,交给 flex 居中 */
|
||
::v-deep .el-select,
|
||
::v-deep .el-input {
|
||
height: 36px;
|
||
line-height: normal;
|
||
}
|
||
|
||
::v-deep .el-input__inner {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
color: #fff !important;
|
||
border-radius: 7px;
|
||
height: 36px;
|
||
padding: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
&::placeholder {
|
||
color: rgba(255, 255, 255, 0.7) !important;
|
||
}
|
||
}
|
||
|
||
::v-deep .el-input__inner:hover {
|
||
background: transparent !important;
|
||
}
|
||
|
||
/* 去掉 focus 时的蓝色外光晕(Element 默认 box-shadow) */
|
||
::v-deep .el-input__inner:focus,
|
||
::v-deep .el-select .el-input__inner:focus,
|
||
::v-deep .el-select .el-input.is-focus .el-input__inner {
|
||
border-color: transparent !important;
|
||
box-shadow: none !important;
|
||
outline: none !important;
|
||
}
|
||
|
||
::v-deep .el-input__icon {
|
||
color: rgba(255, 255, 255, 0.85);
|
||
}
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<style lang="scss">
|
||
/* 学期下拉浮层渲染在 body 下,scoped 无法命中,故用非 scoped 样式通过 popper-class 单独定制,
|
||
使其与顶部深色栏视觉统一(仅作用于 SemesterBar 的下拉,不影响页面其他 select)。 */
|
||
.semester-select-popper {
|
||
background: #084c3f !important;
|
||
border: 1px solid rgba(255, 255, 255, 0.14) !important;
|
||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35) !important;
|
||
border-radius: 7px;
|
||
|
||
.el-select-dropdown__list {
|
||
padding: 4px;
|
||
}
|
||
|
||
.el-select-dropdown__item {
|
||
color: rgba(255, 255, 255, 0.85);
|
||
border-radius: 5px;
|
||
height: 32px;
|
||
line-height: 32px;
|
||
|
||
&.hover,
|
||
&:hover {
|
||
background: rgba(255, 255, 255, 0.12);
|
||
color: #fff;
|
||
}
|
||
|
||
&.selected {
|
||
color: #fff;
|
||
font-weight: 600;
|
||
background: rgba(255, 255, 255, 0.16);
|
||
}
|
||
|
||
/* 关键选项的选中图标保持可见 */
|
||
.el-select-dropdown__item span {
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
.el-scrollbar__bar.is-vertical {
|
||
> div {
|
||
background-color: rgba(255, 255, 255, 0.25);
|
||
}
|
||
}
|
||
}
|
||
</style>
|