1、添加教学场地历;

2、很多表继承了基础类,但是没有添加对应字段导致报错,把继承去掉
This commit is contained in:
2026-09-16 16:37:23 +08:00
parent 58499afe8e
commit 2e151ea228
178 changed files with 1471 additions and 531 deletions
@@ -17,11 +17,11 @@
<div class="sce-panel">
<div class="sce-panel-row">
<span class="sce-label">事件名称:</span>
<el-input v-model="toolbar.eventName" size="small" class="sce-event-input" :placeholder="mode === 'class' ? '请输入班历事件名称' : '请输入校历事件名称'" />
<el-checkbox v-model="toolbar.bold" class="sce-cb">加粗显示</el-checkbox>
<el-input v-model="toolbar.eventName" size="small" class="sce-event-input" :placeholder="mode === 'class' ? '请输入班历事件名称' : (mode === 'venue' ? '请输入场地事件名称' : '请输入校历事件名称')" />
<el-checkbox v-if="mode !== 'venue'" v-model="toolbar.bold" class="sce-cb">加粗显示</el-checkbox>
<el-checkbox v-model="toolbar.schedulable" class="sce-cb">可排课</el-checkbox>
<el-checkbox v-model="toolbar.mainCourse" class="sce-cb">正课</el-checkbox>
<el-checkbox v-model="toolbar.remarkShow" class="sce-cb">备注显示</el-checkbox>
<el-checkbox v-if="mode !== 'venue'" v-model="toolbar.mainCourse" class="sce-cb">正课</el-checkbox>
<el-checkbox v-if="mode !== 'venue'" v-model="toolbar.remarkShow" class="sce-cb">备注显示</el-checkbox>
<span class="sce-label">备注:</span>
<el-input
v-model="toolbar.remark"
@@ -37,6 +37,7 @@
<el-button type="primary" size="small" icon="el-icon-check" @click="applyEvent">设定</el-button>
<el-button type="danger" size="small" icon="el-icon-delete" @click="deleteEvent">删除</el-button>
<el-button size="small" icon="el-icon-brush" @click="absorbEvent">吸取</el-button>
<el-button v-if="mode === 'venue'" size="small" icon="el-icon-magic-stick" @click="openRuleDialog">规律生成</el-button>
<el-button size="small" icon="el-icon-refresh" @click="refreshGrid">刷新</el-button>
<span class="sce-divider" />
<el-checkbox v-model="show78" class="sce-cb">显示78节</el-checkbox>
@@ -45,6 +46,11 @@
</div>
</div>
<!-- 场地历:专用教室横幅 -->
<div v-if="mode === 'venue' && designatedTeams.length" class="sce-venue-banner">
指定本场地为专用教室的班次:{{ designatedTeams.join('、') }}
</div>
<!-- 时间编排区域 -->
<div ref="gridWrap" class="sce-grid-wrap" @mousedown="onGridMouseDown">
<table class="sce-grid" :class="{ 'is-selecting': selecting }">
@@ -72,9 +78,11 @@
class="sce-cell"
:class="cellClass(wIdx, col)"
:data-key="cellKey(wIdx, col.colIndex)"
:title="cellTitle(wIdx, col)"
@dblclick="onCellDblClick(wIdx, col)"
>
<span v-if="getEvent(wIdx, col.colIndex) && getEvent(wIdx, col.colIndex).name" class="sce-event-name" :class="{ 'is-bold': getEvent(wIdx, col.colIndex).bold }">{{ getEvent(wIdx, col.colIndex).name }}</span>
<span v-else-if="mode === 'venue' && getLessons(wIdx, col.colIndex)" class="sce-lesson-name">{{ lessonTextOf(wIdx, col.colIndex) }}</span>
<span v-else class="sce-date-num">{{ dateNumberOf(wIdx, col.dayIndex) }}</span>
</td>
</tr>
@@ -85,7 +93,47 @@
<div class="sce-status">
已选 {{ selectedKeys.length }} 个时间格
<span v-if="absorbPreview" class="sce-absorb-tip">已吸取:{{ absorbPreview }}</span>
<span v-if="mode === 'venue'" class="sce-legend">
<i class="lg lg-red" />不可用
<i class="lg lg-yellow" />场地备注
<i class="lg lg-blue" />已排课次
<i class="lg lg-gray" />校历不可排
</span>
</div>
<!-- 场地历:按星期规律生成 -->
<el-dialog title="按星期规律生成场地事件" :visible.sync="ruleDialog.visible" width="540px" append-to-body
:close-on-click-modal="false">
<el-form label-width="90px" size="small">
<el-form-item label="星期">
<el-checkbox-group v-model="ruleDialog.weekdays">
<el-checkbox v-for="(d, i) in weekDayOptions" :key="i" :label="i">{{ d }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="节次">
<el-checkbox-group v-model="ruleDialog.slots">
<el-checkbox v-for="s in slotOptions" :key="s.key" :label="s.key">{{ s.label }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="周次范围">
<el-input-number v-model="ruleDialog.weekStart" :min="1" :max="totalWeeks || 1" size="small" class="rule-week" />
<span class="rule-sep">至</span>
<el-input-number v-model="ruleDialog.weekEnd" :min="1" :max="totalWeeks || 1" size="small" class="rule-week" />
<span class="rule-unit">周</span>
</el-form-item>
<el-form-item label="事件内容">
<span class="rule-hint">使用上方工具栏填写的「事件名称 / 可排课 / 备注」</span>
</el-form-item>
<el-form-item label="预计生成">
<span>{{ rulePreviewCount }} 个时间格</span>
<span v-if="ruleSkippedCount" class="rule-hint">(跳过校历不可排/已排课 {{ ruleSkippedCount }} 格)</span>
</el-form-item>
</el-form>
<div slot="footer">
<el-button size="small" @click="ruleDialog.visible = false">取 消</el-button>
<el-button type="primary" size="small" :loading="ruleDialog.submitting" @click="applyRuleGenerate">生 成</el-button>
</div>
</el-dialog>
</div>
</template>
@@ -93,6 +141,13 @@
import { getSemester } from '@/api/teachBusiness/semester'
import { listXqxlb, addXqxlb, updateXqxlb } from '@/api/teachBusiness/xqxlb'
import { listClassCalendarEvent, updateClassCalendarEvent } from '@/api/teachOffice/classCalendarEvent'
import {
listVenueCalendarRange,
batchSaveVenueCalendar,
batchDeleteVenueCalendar,
listVenueLessons,
listVenueDesignatedTeams
} from '@/api/teachBusiness/venueCalendar'
// 节次定义:12/34/56 常显,78/晚上/夜间由开关控制可见
const SLOT_DEFS = [
@@ -116,6 +171,9 @@ const SLOT_COURSE_MAP = {
const WEEK_DAYS = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
// 场地历:单节次 -> 节次格 key(1-2 节合并在同一格)
const JC_SLOT_MAP = { 1: '12', 2: '12', 3: '34', 4: '34', 5: '56', 6: '56', 7: '78', 8: '78', 9: 'night', 10: 'night', 11: 'late', 12: 'late' }
function pad2(n) {
return n < 10 ? '0' + n : '' + n
}
@@ -142,6 +200,8 @@ export default {
mode: { type: String, default: 'school' },
xydxqbh: { type: String, default: '' },
xydbh: { type: String, default: '' },
// 场地历模式:教室编号
jsbh: { type: String, default: '' },
rangeStart: { type: String, default: '' },
rangeEnd: { type: String, default: '' }
},
@@ -181,7 +241,24 @@ export default {
moved: false, // 是否发生移动(区分点击与拖拽)
dragAddMode: false, // ctrl 拖拽为追加
// 窗口控制
absorbPreview: ''
absorbPreview: '',
// ---- 场地历模式 ----
// 校历不可排格:'yyyy-MM-dd#slotKey' -> 校历事件名(置灰只读,不落库)
schoolOff: {},
// 已排课次(蓝格只读):'yyyy-MM-dd#slotKey' -> [{ kcmc, jyxm, xydmc, jc }]
lessons: {},
// 指定本场地为专用教室的班次(顶部横幅)
designatedTeams: [],
weekDayOptions: WEEK_DAYS,
slotOptions: SLOT_DEFS,
ruleDialog: {
visible: false,
submitting: false,
weekdays: [],
slots: [],
weekStart: 1,
weekEnd: 1
}
}
},
computed: {
@@ -202,6 +279,36 @@ export default {
// 不再按日历起点/季度取年份,避免跨年度学期解析出错。
xqxlbNd() {
return Number(String(this.nd).slice(0, 6)) || Number(this.nd) || 0
},
// 规律生成:预计写入的格子数(排除学期范围外/校历不可排/已排课的格)
ruleTargets() {
const cells = []
let skipped = 0
const dlg = this.ruleDialog
if (!dlg.weekdays.length || !dlg.slots.length || !this.calendarStart) return { cells, skipped }
const w0 = Math.max(0, (dlg.weekStart || 1) - 1)
const w1 = Math.min(this.totalWeeks - 1, (dlg.weekEnd || this.totalWeeks) - 1)
for (let w = w0; w <= w1; w++) {
dlg.weekdays.forEach(dayIndex => {
if (this.isOutOfRange(w, dayIndex)) return
const d = this.cellDate(w, dayIndex)
const dateStr = fmtDate(d)
dlg.slots.forEach(slotKey => {
if (this.isSchoolOff(dateStr, slotKey) || this.hasLessonAt(dateStr, slotKey)) {
skipped++
return
}
cells.push({ dateStr, slotKey })
})
})
}
return { cells, skipped }
},
rulePreviewCount() {
return this.ruleTargets.cells.length
},
ruleSkippedCount() {
return this.ruleTargets.skipped
}
},
watch: {
@@ -217,6 +324,12 @@ export default {
if (this.mode === 'class' && val) this.loadClassCalendar()
}
},
jsbh: {
immediate: true,
handler(val) {
if (this.mode === 'venue' && val) this.loadVenueEvents()
}
},
visibleColumns(newCols, oldCols) {
// 节次列显隐会改变列索引,按「周期-星期-节次」重定位事件,避免事件随时间格列偏移
if (oldCols && oldCols.length) {
@@ -298,7 +411,8 @@ export default {
this.calendarStart = null
this.buildWeeks()
}
this.loadXqxlbEvents()
if (this.mode === 'venue') this.loadVenueEvents()
else this.loadXqxlbEvents()
})
.catch(() => {
this.$message.warning('学期详情获取失败,请确认学期数据')
@@ -360,6 +474,195 @@ export default {
})
this.events = loaded
},
/* ---------- 场地历(venue 模式) ---------- */
// 场地历为稀疏存储:只拉"事件"行,空格默认即"可排";
// 校历不可排格(节假日)整格置灰只读不落库;已排课次蓝格只读。
loadVenueEvents() {
if (!this.jsbh) return
const s = this.startDate ? fmtDate(this.startDate) : ''
const e = this.endDate ? fmtDate(this.endDate) : ''
Promise.all([
listXqxlb({ nd: this.xqxlbNd }).catch(() => ({ data: [] })),
listVenueCalendarRange(this.jsbh, s, e).catch(() => ({ data: [] })),
listVenueLessons(this.jsbh, s, e, this.xqxlbNd).catch(() => ({ data: [] })),
listVenueDesignatedTeams(this.jsbh).catch(() => ({ data: [] }))
]).then(([xlRes, evRes, lsRes, tmRes]) => {
const xlList = Array.isArray(xlRes.data) ? xlRes.data : (xlRes.data && xlRes.data.records) || []
const evList = Array.isArray(evRes.data) ? evRes.data : (evRes.data && evRes.data.records) || []
const lsList = Array.isArray(lsRes.data) ? lsRes.data : (lsRes.data && lsRes.data.records) || []
this.designatedTeams = Array.isArray(tmRes.data) ? tmRes.data : []
// 校历不可排格 -> 灰
const off = {}
xlList.forEach(item => {
if (item.kpk === true || item.kpk === 1) return
const dateStr = String(item.jqsj || '').slice(0, 10)
const slotKey = this.courseClassToSlotKey(item.courseClass)
if (dateStr && slotKey) off[dateStr + '#' + slotKey] = item.jqmc || '校历不可排课'
})
this.schoolOff = off
// 已排课次 -> 蓝
const lessons = {}
lsList.forEach(item => {
const dateStr = String(item.rq || '').slice(0, 10)
const slotKey = JC_SLOT_MAP[item.jc]
if (!dateStr || !slotKey) return
const key = dateStr + '#' + slotKey
if (!lessons[key]) lessons[key] = []
lessons[key].push(item)
})
this.lessons = lessons
this.renderVenueEvents(evList)
})
},
renderVenueEvents(list) {
// 同格(日期+节次段)两行单节次记录归并为一个事件
const merged = {}
list.forEach(row => {
const dateStr = String(row.rq || '').slice(0, 10)
const slotKey = JC_SLOT_MAP[row.jc]
if (!dateStr || !slotKey) return
const pos = this.locateDateSlot(dateStr, slotKey)
if (!pos) return
const colIndex = this.colIndexOf(pos.dayIndex, pos.slotKey)
const ck = colIndex >= 0 ? this.cellKey(pos.wIdx, colIndex) : null
const storeKey = ck || this.cellStableKey(pos.wIdx, pos.dayIndex, pos.slotKey)
const bucket = ck ? merged : this.hiddenEvents
const prev = bucket[storeKey]
if (prev) {
if (row.id) prev.ids.push(row.id)
if (!prev.name && row.mc) prev.name = row.mc
if (row.kpk === false || row.kpk === 0) prev.schedulable = false
if (!prev.remark && row.bz) prev.remark = row.bz
} else {
bucket[storeKey] = {
ids: row.id ? [row.id] : [],
name: row.mc || '',
schedulable: !(row.kpk === false || row.kpk === 0),
remark: row.bz || ''
}
}
})
this.events = merged
},
// 日期 + 节次格 -> 网格位置
locateDateSlot(dateStr, slotKey) {
if (!this.calendarStart || !dateStr) return null
const d = new Date(dateStr.replace(/-/g, '/'))
if (isNaN(d.getTime())) return null
const offset = Math.round((d - this.calendarStart) / (24 * 3600 * 1000))
if (offset < 0) return null
return { wIdx: Math.floor(offset / 7), dayIndex: offset % 7, slotKey }
},
cellDateStr(wIdx, dayIndex) {
const d = this.cellDate(wIdx, dayIndex)
return d ? fmtDate(d) : ''
},
isSchoolOff(dateStr, slotKey) {
return !!this.schoolOff[dateStr + '#' + slotKey]
},
hasLessonAt(dateStr, slotKey) {
return !!(this.lessons[dateStr + '#' + slotKey] || []).length
},
getLessons(wIdx, colIndex) {
const col = this.visibleColumns.find(c => c.colIndex === colIndex)
if (!col) return null
return this.lessons[this.cellDateStr(wIdx, col.dayIndex) + '#' + col.slotKey] || null
},
lessonTextOf(wIdx, colIndex) {
const list = this.getLessons(wIdx, colIndex) || []
const names = list.map(l => l.kcmc).filter(Boolean)
return names.length ? names.join(';') : '已排课'
},
cellTitle(wIdx, col) {
if (this.mode !== 'venue') return ''
const dateStr = this.cellDateStr(wIdx, col.dayIndex)
const parts = []
const offName = this.schoolOff[dateStr + '#' + col.slotKey]
if (offName) parts.push('校历:' + offName + '(不可排课)')
const ls = this.lessons[dateStr + '#' + col.slotKey] || []
ls.forEach(l => {
parts.push('课次:' + (l.kcmc || '-') +
(l.jyxm ? ' / 教员:' + l.jyxm : '') +
(l.xydmc ? ' / 班次:' + l.xydmc : ''))
})
const ev = this.getEvent(wIdx, col.colIndex)
if (ev && (ev.name || ev.remark)) {
parts.push('场地事件:' + (ev.name || '-') + (ev.remark ? '(' + ev.remark + ')' : ''))
}
return parts.join('\n')
},
// 场地历可编辑格过滤:剔除校历不可排、已排课次、学期范围外的格
filterVenueEditable(keys) {
const ok = []
let skipped = 0
keys.forEach(key => {
const pos = this.parseCellKey(key)
if (!pos) { skipped++; return }
const dateStr = this.cellDateStr(pos.wIdx, pos.dayIndex)
if (this.isOutOfRange(pos.wIdx, pos.dayIndex)
|| this.isSchoolOff(dateStr, pos.slotKey)
|| this.hasLessonAt(dateStr, pos.slotKey)) {
skipped++
return
}
ok.push({ key, pos, dateStr })
})
return { ok, skipped }
},
periodsOfSlot(slotKey) {
const seg = SLOT_COURSE_MAP[slotKey]
if (!seg) return []
return seg.split('-').map(Number).filter(n => !isNaN(n))
},
/* ---------- 规律生成 ---------- */
openRuleDialog() {
if (!this.toolbar.eventName.trim()) {
this.$message.warning('请先在上方工具栏填写事件名称')
return
}
this.ruleDialog.weekEnd = this.totalWeeks || 1
this.ruleDialog.visible = true
},
applyRuleGenerate() {
const name = this.toolbar.eventName.trim()
if (!name) {
this.$message.warning('请先填写事件名称')
return
}
const { cells } = this.ruleTargets
if (!cells.length) {
this.$message.warning('所选条件下没有可写入的时间格')
return
}
this.ruleDialog.submitting = true
this.saveVenueCells(cells, name).then(ok => {
if (ok) this.ruleDialog.visible = false
}).finally(() => {
this.ruleDialog.submitting = false
})
},
// 把 [{dateStr, slotKey}] 展开为单节次行并批量保存
saveVenueCells(cells, name) {
const rows = []
cells.forEach(c => {
this.periodsOfSlot(c.slotKey).forEach(jc => {
rows.push({
jsbh: this.jsbh,
rq: c.dateStr + ' 00:00:00',
jc: jc,
kpk: !!this.toolbar.schedulable,
mc: name,
bz: this.toolbar.remark || null
})
})
})
if (!rows.length) return Promise.resolve(false)
return batchSaveVenueCalendar(rows).then(res => {
this.$message.success((res && res.message) || '已保存 ' + rows.length + ' 条场地历')
this.reloadEvents()
return true
}).catch(() => false)
},
// 根据假期记录反推时间格位置(jqsj 日期 + courseClass 节次),与列是否可见无关
locateXqxlb(item) {
if (!this.calendarStart || !item.jqsj) return null
@@ -442,13 +745,20 @@ export default {
cellClass(wIdx, col) {
const classes = []
if (this.selectedKeys.includes(this.cellKey(wIdx, col.colIndex))) classes.push('is-selected')
// 班历:班次日期范围外的格子不可排,给出明确视觉提示
if (this.mode === 'class' && this.isOutOfRange(wIdx, col.dayIndex)) {
// 班历/场地历:学期日期范围外的格子不可排,给出明确视觉提示
if ((this.mode === 'class' || this.mode === 'venue') && this.isOutOfRange(wIdx, col.dayIndex)) {
classes.push('is-out-of-range')
}
const ev = this.getEvent(wIdx, col.colIndex)
// 可排课的全部白色;不可排课且事件名非空才标红
if (ev && ev.name && !ev.schedulable) {
if (this.mode === 'venue') {
const dateStr = this.cellDateStr(wIdx, col.dayIndex)
// 校历不可排(节假日)置灰只读;已排课次蓝色只读
if (this.isSchoolOff(dateStr, col.slotKey)) classes.push('is-school-off')
if (this.hasLessonAt(dateStr, col.slotKey)) classes.push('has-lesson')
// 场地事件:不可排标红,仍可排的纯备注标黄
if (ev) classes.push(ev.schedulable ? 'has-note' : 'no-schedule')
} else if (ev && ev.name && !ev.schedulable) {
// 可排课的全部白色;不可排课且事件名非空才标红
classes.push('no-schedule')
}
return classes
@@ -585,6 +895,16 @@ export default {
}
}
const keys = [...this.selectedKeys]
// 场地历:过滤不可编辑格(校历不可排/已排课次/范围外),按单节次展开批量保存
if (this.mode === 'venue') {
const { ok, skipped } = this.filterVenueEditable(keys)
if (skipped > 0) {
this.$message.warning(`已跳过 ${skipped} 个不可编辑格(校历不可排/已排课次/范围外)`)
}
if (!ok.length) return
this.saveVenueCells(ok.map(t => ({ dateStr: t.dateStr, slotKey: t.pos.slotKey })), name)
return
}
keys.forEach(key => {
const prev = this.events[key]
this.$set(this.events, key, {
@@ -613,6 +933,25 @@ export default {
type: 'warning'
}).then(() => {
const keys = [...this.selectedKeys]
// 场地历:收集事件行 ID 走批量删除
if (this.mode === 'venue') {
const ids = []
keys.forEach(key => {
const ev = this.events[key]
if (ev && ev.ids) ids.push(...ev.ids)
})
if (!ids.length) {
this.$message.warning('所选时间格没有场地事件')
return
}
batchDeleteVenueCalendar(ids).then(res => {
this.$message.success((res && res.message) || '已删除场地事件')
keys.forEach(key => this.$delete(this.events, key))
this.selectedKeys = []
this.reloadEvents()
}).catch(() => {})
return
}
this.persistDeleteEvents(keys).then(({ ok, fail }) => {
if (fail === 0) {
keys.forEach(key => {
@@ -643,6 +982,17 @@ export default {
},
onCellDblClick(wIdx, col) {
const ev = this.getEvent(wIdx, col.colIndex)
// 场地历:双击蓝格显示课次详情
if (this.mode === 'venue') {
const ls = this.getLessons(wIdx, col.colIndex)
if (ls && ls.length && !(ev && ev.name)) {
const lines = ls.map(l => '课程:' + (l.kcmc || '-') +
(l.jyxm ? ' 教员:' + l.jyxm : '') +
(l.xydmc ? ' 班次:' + l.xydmc : ''))
this.$alert(lines.join('<br/>'), '已排课次', { dangerouslyUseHTMLString: true })
return
}
}
if (ev && ev.name) {
this.toolbar.eventName = ev.name
this.toolbar.bold = !!ev.bold
@@ -660,6 +1010,7 @@ export default {
this.events = {}
this.hiddenEvents = {}
if (this.mode === 'class') this.loadClassEvents()
else if (this.mode === 'venue') this.loadVenueEvents()
else this.loadXqxlbEvents()
this.$message.info('已刷新')
},
@@ -667,6 +1018,7 @@ export default {
this.events = {}
this.hiddenEvents = {}
if (this.mode === 'class') this.loadClassEvents()
else if (this.mode === 'venue') this.loadVenueEvents()
else this.loadXqxlbEvents()
},
savePayload(payload) {
@@ -765,7 +1117,7 @@ export default {
/* ---------- 返回学期管理 ---------- */
handleClose() {
this.$emit('close')
if (this.mode !== 'class') {
if (this.mode === 'school') {
this.$router.replace({ path: '/teachBusiness/semester' })
}
}
@@ -827,6 +1179,15 @@ export default {
.sce-divider { width: 1px; height: 18px; background: #dcdfe6; margin: 0 14px; }
}
/* 场地历:专用教室横幅 */
.sce-venue-banner {
padding: 6px 14px;
font-size: 12px;
color: #e6a23c;
background: #fdf6ec;
border-bottom: 1px solid #ebeef5;
}
/* 时间编排区 */
.sce-grid-wrap {
flex: 1;
@@ -912,6 +1273,34 @@ export default {
// 可排课白色,不可排课且有事件标红
&.no-schedule { background: #fde2e2; }
// 场地历:仍可排的纯备注事件标黄
&.has-note { background: #fdf6ec; }
// 场地历:已排课次蓝格(只读)
&.has-lesson {
background: #e8f1fd;
cursor: default;
.sce-lesson-name {
display: block;
font-size: 10px;
color: #409eff;
line-height: 1;
padding: 2px 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
// 场地历:校历不可排格置灰只读
&.is-school-off {
background: #f0f0f0;
cursor: not-allowed;
.sce-date-num { color: #c0c4cc; }
}
// 班历:班次日期范围外的格子不可排
&.is-out-of-range {
background: #f4f4f5;
@@ -936,6 +1325,28 @@ export default {
background: #fafafa;
.sce-absorb-tip { margin-left: 12px; color: #00875a; }
.sce-legend {
float: right;
.lg {
display: inline-block;
width: 10px;
height: 10px;
margin: 0 4px 0 14px;
border: 1px solid #dcdfe6;
vertical-align: middle;
}
.lg-red { background: #fde2e2; }
.lg-yellow { background: #fdf6ec; }
.lg-blue { background: #e8f1fd; }
.lg-gray { background: #f0f0f0; }
}
}
}
.rule-week { width: 110px; }
.rule-sep { margin: 0 8px; color: #606266; }
.rule-unit { margin-left: 8px; color: #606266; }
.rule-hint { font-size: 12px; color: #909399; }
</style>