PickupTimeCascader.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. <template>
  2. <div class="pickup-time-cascader">
  3. <el-cascader
  4. ref="cascaderRef"
  5. v-model="selectedValue"
  6. :options="cascaderOptions"
  7. :props="cascaderProps"
  8. :show-all-levels="true"
  9. :placeholder="placeholder"
  10. :clearable="clearable"
  11. :filterable="filterable"
  12. :disabled="disabled"
  13. :size="size"
  14. @change="handleChange"
  15. @visible-change="handleVisibleChange"
  16. @expand-change="handleExpandChange"
  17. >
  18. <template #default="{ node, data }">
  19. <div class="cascader-item" :class="{'is-date': !data.timeLabel, 'is-time': data.timeLabel}">
  20. <span class="item-label">{{ data.label }}</span>
  21. <!-- <span v-if="data.tag" class="item-tag" :class="data.tagClass">-->
  22. <!-- {{ data.tag }}-->
  23. <!-- </span>-->
  24. <!-- <span v-if="data.recommend" class="item-recommend">推荐</span>-->
  25. <!-- <span v-if="data.night" class="item-night">夜间</span>-->
  26. </div>
  27. </template>
  28. <template #empty>
  29. <div class="empty-content">
  30. <span class="empty-text">无可用时间段</span>
  31. </div>
  32. </template>
  33. </el-cascader>
  34. </div>
  35. </template>
  36. <script setup>
  37. import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
  38. const props = defineProps({
  39. modelValue: {
  40. type: Array,
  41. default: () => []
  42. },
  43. placeholder: {
  44. type: String,
  45. default: '请选择上门时间'
  46. },
  47. clearable: {
  48. type: Boolean,
  49. default: true
  50. },
  51. filterable: {
  52. type: Boolean,
  53. default: false
  54. },
  55. disabled: {
  56. type: Boolean,
  57. default: false
  58. },
  59. size: {
  60. type: String,
  61. default: 'large',
  62. validator: (value) => ['large', 'default', 'small'].includes(value)
  63. },
  64. // 显示天数
  65. days: {
  66. type: Number,
  67. default: 3
  68. },
  69. // 时间间隔(分钟)
  70. timeInterval: {
  71. type: Number,
  72. default: 60
  73. },
  74. // 开始时间
  75. startTime: {
  76. type: Number,
  77. default: 9
  78. },
  79. // 结束时间
  80. endTime: {
  81. type: Number,
  82. default: 20
  83. },
  84. // 当前时间(用于测试)
  85. currentTime: {
  86. type: Date,
  87. default: () => new Date()
  88. },
  89. // 是否自动选择推荐时间
  90. autoSelect: {
  91. type: Boolean,
  92. default: true
  93. }
  94. })
  95. const emit = defineEmits(['update:modelValue', 'change', 'select', 'clear'])
  96. const selectedValue = ref([])
  97. const cascaderRef = ref(null)
  98. const currentTime = ref(new Date())
  99. const timer = ref(null)
  100. // 级联选择器配置
  101. const cascaderProps = {
  102. expandTrigger: 'click',
  103. multiple: false,
  104. emitPath: true,
  105. value: 'value',
  106. label: 'label',
  107. children: 'children',
  108. disabled: 'disabled',
  109. checkStrictly: false,
  110. lazy: false
  111. }
  112. // 基础时间段配置
  113. const baseTimeSlots = computed(() => {
  114. const slots = []
  115. const startHour = props.startTime
  116. const endHour = props.endTime
  117. const interval = props.timeInterval / 60 // 转换为小时
  118. for (let hour = startHour; hour < endHour; hour += interval) {
  119. const start = hour
  120. const end = hour + interval
  121. const startStr = start.toString().padStart(2, '0')
  122. const endStr = end.toString().padStart(2, '0')
  123. slots.push({
  124. start: start,
  125. end: end,
  126. label: `${startStr}:00-${endStr}:00`,
  127. value: `${startStr}:00-${endStr}:00`,
  128. startHour: start,
  129. endHour: end
  130. })
  131. }
  132. return slots
  133. })
  134. // 获取今天日期字符串
  135. const todayDate = computed(() => {
  136. const today = new Date()
  137. return formatDate(today)
  138. })
  139. // 生成日期列表
  140. const dateList = computed(() => {
  141. const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
  142. const list = []
  143. const today = new Date()
  144. for (let i = 0; i < props.days; i++) {
  145. const date = new Date(today)
  146. date.setDate(date.getDate() + i)
  147. const year = date.getFullYear()
  148. const month = date.getMonth() + 1
  149. const day = date.getDate()
  150. const week = days[date.getDay()]
  151. const dateStr = formatDate(date)
  152. let label = ''
  153. if (i === 0) {
  154. label = `今天 ${month}月${day}日 ${week}`
  155. } else if (i === 1) {
  156. label = `明天 ${month}月${day}日 ${week}`
  157. } else if (i === 2) {
  158. label = `后天 ${month}月${day}日 ${week}`
  159. } else {
  160. label = `${month}月${day}日 ${week}`
  161. }
  162. list.push({
  163. value: dateStr,
  164. label: label,
  165. year: year,
  166. month: month,
  167. day: day,
  168. week: week,
  169. isToday: i === 0,
  170. isTomorrow: i === 1,
  171. isDayAfterTomorrow: i === 2,
  172. fullLabel: `${month}月${day}日 ${week}`,
  173. displayDate: `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`
  174. })
  175. }
  176. return list
  177. })
  178. // 计算当前时间20分钟后的时间
  179. const twentyMinutesLater = computed(() => {
  180. const now = new Date()
  181. now.setMinutes(now.getMinutes() + 3)
  182. return now
  183. })
  184. // 计算一小时后的小时(向上取整)
  185. const oneHourLaterHour = computed(() => {
  186. const now = new Date()
  187. now.setHours(now.getHours() + 1)
  188. return Math.ceil(now.getHours())
  189. })
  190. // 计算一小时内时间段
  191. const withinOneHourSlot = computed(() => {
  192. if (!dateList.value.length || !dateList.value[0].isToday) return null
  193. const now = currentTime.value
  194. const later20min = twentyMinutesLater.value
  195. // 计算开始时间:当前时间 + 20分钟,分钟向上取整到10的倍数
  196. const startMinutes = later20min.getMinutes()
  197. const roundedMinutes = Math.ceil(startMinutes / 10) * 10
  198. let startHour = later20min.getHours()
  199. let startMinute = roundedMinutes
  200. // 如果分钟超过60,小时加1,分钟归零
  201. if (startMinute >= 60) {
  202. startHour += 1
  203. startMinute = 0
  204. }
  205. // 计算结束时间:开始时间 + 1小时
  206. let endHour = startHour + 1
  207. let endMinute = startMinute
  208. // 如果开始时间已经超过结束时间限制,则不显示
  209. if (startHour >= props.endTime) return null
  210. // 如果结束时间超过结束时间限制,则调整
  211. if (endHour > props.endTime) {
  212. endHour = props.endTime
  213. endMinute = 0
  214. }
  215. // 如果开始时间小于开始时间限制,则调整
  216. if (startHour < props.startTime) {
  217. startHour = props.startTime
  218. startMinute = 0
  219. }
  220. // 检查时间段是否有效(至少30分钟)
  221. const slotDuration = (endHour - startHour) * 60 + (endMinute - startMinute)
  222. if (slotDuration < 30) return null
  223. const startStr = formatTime(startHour, startMinute)
  224. const endStr = formatTime(endHour, endMinute)
  225. return {
  226. start: startHour + startMinute / 60,
  227. end: endHour + endMinute / 60,
  228. label: `一小时内`, // label: `一小时内 (${startStr}-${endStr})`
  229. value: `${startStr}-${endStr}`,
  230. isWithinOneHour: true,
  231. startHour: startHour,
  232. startMinute: startMinute,
  233. endHour: endHour,
  234. endMinute: endMinute
  235. }
  236. })
  237. // 推荐时间段(一小时内)
  238. const recommendTime = computed(() => {
  239. if (!dateList.value.length || !dateList.value[0].isToday) return null
  240. // 如果有一小时内选项,则推荐一小时内
  241. if (withinOneHourSlot.value) {
  242. return withinOneHourSlot.value.value
  243. }
  244. return null
  245. })
  246. // 检查时间段是否可用
  247. const isTimeSlotAvailable = (timeLabel, dateInfo) => {
  248. if (!dateInfo) return false
  249. // 如果是"一小时内"的特殊格式
  250. if (timeLabel.includes('一小时内')) {
  251. return withinOneHourSlot.value !== null
  252. }
  253. // 普通时间段格式
  254. const timeMatch = timeLabel.match(/(\d{2}:\d{2})-(\d{2}:\d{2})/)
  255. if (!timeMatch) return false
  256. const startTime = timeMatch[1]
  257. const startHour = parseInt(startTime.split(':')[0])
  258. // 检查是否在允许的时间范围内
  259. if (startHour < props.startTime || startHour >= props.endTime) {
  260. return false
  261. }
  262. // 如果是今天,需要检查时间是否在当前时间+1小时之后
  263. if (dateInfo.isToday) {
  264. const oneHourLater = oneHourLaterHour.value
  265. return startHour >= oneHourLater
  266. }
  267. return true
  268. }
  269. // 生成级联选择器选项
  270. const cascaderOptions = computed(() => {
  271. const options = []
  272. dateList.value.forEach(date => {
  273. const dateOption = {
  274. value: date.value,
  275. label: date.label,
  276. date: date.value,
  277. dateLabel: date.label,
  278. isToday: date.isToday,
  279. children: [],
  280. disabled: false
  281. }
  282. // 生成时间选项
  283. const isToday = date.isToday
  284. const now = currentTime.value
  285. const oneHourLater = oneHourLaterHour.value
  286. // 今天的时间段需要根据当前时间+1小时过滤
  287. if (isToday) {
  288. // 首先添加"一小时内"选项(如果有)
  289. if (withinOneHourSlot.value) {
  290. dateOption.children.push({
  291. value: withinOneHourSlot.value.value,
  292. label: withinOneHourSlot.value.label,
  293. timeLabel: withinOneHourSlot.value.value,
  294. recommend: true,
  295. tag: '推荐',
  296. tagClass: 'tag-recommend',
  297. disabled: false,
  298. soon: false,
  299. night: isNightTime(withinOneHourSlot.value.startHour),
  300. isWithinOneHour: true,
  301. startHour: withinOneHourSlot.value.startHour,
  302. startMinute: withinOneHourSlot.value.startMinute,
  303. endHour: withinOneHourSlot.value.endHour,
  304. endMinute: withinOneHourSlot.value.endMinute
  305. })
  306. }
  307. // 添加其他时间段(开始时间从一小时后开始)
  308. baseTimeSlots.value.forEach(slot => {
  309. // 时间段开始时间必须在当前时间+1小时之后
  310. const isDisabled = slot.start < oneHourLater
  311. const isSoon = false
  312. const isRecommend = false // 一小时内才是推荐
  313. if (!isDisabled) {
  314. dateOption.children.push({
  315. value: slot.value,
  316. label: slot.label,
  317. timeLabel: slot.label,
  318. recommend: isRecommend,
  319. tag: isRecommend ? '推荐' : '',
  320. tagClass: isRecommend ? 'tag-recommend' : '',
  321. disabled: isDisabled && !isRecommend,
  322. soon: isSoon,
  323. night: isNightTime(slot.start),
  324. startHour: slot.start,
  325. endHour: slot.end
  326. })
  327. }
  328. })
  329. } else {
  330. // 明天及以后显示所有时间段
  331. baseTimeSlots.value.forEach(slot => {
  332. dateOption.children.push({
  333. value: slot.value,
  334. label: slot.label,
  335. timeLabel: slot.label,
  336. recommend: false,
  337. tag: '',
  338. tagClass: '',
  339. disabled: false,
  340. soon: false,
  341. night: isNightTime(slot.start),
  342. startHour: slot.start,
  343. endHour: slot.end
  344. })
  345. })
  346. }
  347. // 如果没有可用的时间段,则禁用该日期
  348. if (dateOption.children.length === 0) {
  349. dateOption.disabled = true
  350. dateOption.label = `${dateOption.label} (无可选时间)`
  351. }
  352. options.push(dateOption)
  353. })
  354. return options
  355. })
  356. // 是否是夜间时间段
  357. const isNightTime = (hour) => {
  358. return hour >= 18
  359. }
  360. // 获取开始小时
  361. const getStartHour = (timeLabel, selectedTimeOption) => {
  362. if (selectedTimeOption && selectedTimeOption.isWithinOneHour) {
  363. return selectedTimeOption.startHour + selectedTimeOption.startMinute / 60
  364. }
  365. const match = timeLabel.match(/(\d{2}):/)
  366. return match ? parseInt(match[1]) : 0
  367. }
  368. // 获取结束小时
  369. const getEndHour = (timeLabel, selectedTimeOption) => {
  370. if (selectedTimeOption && selectedTimeOption.isWithinOneHour) {
  371. return selectedTimeOption.endHour + selectedTimeOption.endMinute / 60
  372. }
  373. const match = timeLabel.match(/-(\d{2}):/)
  374. return match ? parseInt(match[1]) : 0
  375. }
  376. // 处理选择变化
  377. const handleChange = (value) => {
  378. if (!value || value.length === 0) {
  379. emit('update:modelValue', [])
  380. emit('change', null)
  381. emit('clear')
  382. return
  383. }
  384. const [date, time] = value
  385. const selectedDate = dateList.value.find(d => d.value === date)
  386. const selectedTime = findTimeOption(date, time)
  387. if (!selectedDate || !selectedTime) {
  388. return
  389. }
  390. // 计算开始和结束时间
  391. const startHour = getStartHour(time, selectedTime)
  392. const endHour = getEndHour(time, selectedTime)
  393. // 计算具体的开始和结束时间字符串
  394. const startTimeStr = formatDateTime(date, startHour)
  395. const endTimeStr = formatDateTime(date, endHour)
  396. // 生成显示文本
  397. let displayText = ''
  398. if (selectedTime.isWithinOneHour) {
  399. displayText = `${selectedDate.label} 一小时内`
  400. } else {
  401. displayText = `${selectedDate.label} ${time}`
  402. }
  403. const timeData = {
  404. date: date,
  405. time: time,
  406. dateLabel: selectedDate.label,
  407. timeLabel: time,
  408. fullLabel: displayText,
  409. displayText: displayText,
  410. startTime: startTimeStr,
  411. endTime: endTimeStr,
  412. isToday: selectedDate.isToday,
  413. isImmediate: selectedTime.isWithinOneHour || false,
  414. isNight: selectedTime.night || false,
  415. isSoon: selectedTime.soon || false,
  416. startHour: startHour,
  417. endHour: endHour
  418. }
  419. emit('update:modelValue', value)
  420. emit('change', timeData)
  421. emit('select', timeData)
  422. }
  423. // 格式化日期时间
  424. const formatDateTime = (dateStr, hour) => {
  425. const date = new Date(dateStr)
  426. const wholeHour = Math.floor(hour)
  427. const minutes = Math.round((hour - wholeHour) * 60)
  428. date.setHours(wholeHour, minutes, 0, 0)
  429. const year = date.getFullYear()
  430. const month = String(date.getMonth() + 1).padStart(2, '0')
  431. const day = String(date.getDate()).padStart(2, '0')
  432. const hours = String(date.getHours()).padStart(2, '0')
  433. const mins = String(date.getMinutes()).padStart(2, '0')
  434. const seconds = String(date.getSeconds()).padStart(2, '0')
  435. return `${year}-${month}-${day} ${hours}:${mins}:${seconds}`
  436. }
  437. // 查找时间选项
  438. const findTimeOption = (date, time) => {
  439. const dateOption = cascaderOptions.value.find(opt => opt.value === date)
  440. if (!dateOption || !dateOption.children) return null
  441. return dateOption.children.find(child => child.value === time)
  442. }
  443. // 处理弹窗显示/隐藏
  444. const handleVisibleChange = (visible) => {
  445. if (visible) {
  446. // 更新当前时间
  447. currentTime.value = new Date()
  448. // 如果没有选中值,自动选择推荐或第一个可用选项
  449. if (selectedValue.value.length === 0 && props.autoSelect) {
  450. nextTick(() => {
  451. const defaultOption = getDefaultOption()
  452. if (defaultOption) {
  453. selectedValue.value = defaultOption
  454. handleChange(defaultOption)
  455. }
  456. })
  457. }
  458. }
  459. }
  460. // 处理菜单展开
  461. const handleExpandChange = (activeLabels) => {
  462. // 可以在这里添加菜单展开时的逻辑
  463. }
  464. // 获取默认选项
  465. const getDefaultOption = () => {
  466. if (!cascaderOptions.value.length) return null
  467. // 首先尝试今天的推荐时间段
  468. const todayOption = cascaderOptions.value[0]
  469. if (todayOption && todayOption.children && todayOption.children.length > 0) {
  470. // 优先选择推荐时间段(一小时内)
  471. const recommendOption = todayOption.children.find(child => child.recommend && !child.disabled)
  472. if (recommendOption) {
  473. return [todayOption.value, recommendOption.value]
  474. }
  475. // 否则选择第一个可用时间段
  476. const firstAvailable = todayOption.children.find(child => !child.disabled)
  477. if (firstAvailable) {
  478. return [todayOption.value, firstAvailable.value]
  479. }
  480. }
  481. // 尝试其他日期的第一个可用时间段
  482. for (let i = 1; i < cascaderOptions.value.length; i++) {
  483. const option = cascaderOptions.value[i]
  484. if (option.children && option.children.length > 0) {
  485. const firstAvailable = option.children.find(child => !child.disabled)
  486. if (firstAvailable) {
  487. return [option.value, firstAvailable.value]
  488. }
  489. }
  490. }
  491. return null
  492. }
  493. // 工具函数
  494. const formatDate = (date) => {
  495. const year = date.getFullYear()
  496. const month = String(date.getMonth() + 1).padStart(2, '0')
  497. const day = String(date.getDate()).padStart(2, '0')
  498. return `${year}-${month}-${day}`
  499. }
  500. const formatTime = (hour, minute) => {
  501. const hourStr = hour.toString().padStart(2, '0')
  502. const minuteStr = minute.toString().padStart(2, '0')
  503. return `${hourStr}:${minuteStr}`
  504. }
  505. // 监听外部值变化
  506. watch(() => props.modelValue, (newVal) => {
  507. console.log("props.modelValue====", props.modelValue)
  508. if (JSON.stringify(newVal) !== JSON.stringify(selectedValue.value)) {
  509. selectedValue.value = newVal || []
  510. }
  511. }, { immediate: true })
  512. // 监听当前时间变化(每分钟更新一次)
  513. watch(currentTime, () => {
  514. // 如果今天的时间选项已经过期,需要重新计算
  515. const todayOption = cascaderOptions.value[0]
  516. if (todayOption && todayOption.isToday) {
  517. // 这里可以添加逻辑来处理时间过期的选项
  518. }
  519. })
  520. // 初始化
  521. onMounted(() => {
  522. // 定时更新当前时间(每分钟更新一次)
  523. timer.value = setInterval(() => {
  524. if (cascaderRef.value && cascaderRef.value.dropDownVisible) {
  525. currentTime.value = new Date()
  526. }
  527. }, 60000) // 每分钟更新一次
  528. })
  529. // 组件卸载时清除定时器
  530. onUnmounted(() => {
  531. if (timer.value) {
  532. clearInterval(timer.value)
  533. }
  534. })
  535. // 暴露方法
  536. defineExpose({
  537. getSelectedTimeData: () => {
  538. if (selectedValue.value.length !== 2) return null
  539. const [date, time] = selectedValue.value
  540. const selectedDate = dateList.value.find(d => d.value === date)
  541. const selectedTime = findTimeOption(date, time)
  542. if (!selectedDate || !selectedTime) return null
  543. const startHour = getStartHour(time, selectedTime)
  544. const endHour = getEndHour(time, selectedTime)
  545. const startTimeStr = formatDateTime(date, startHour)
  546. const endTimeStr = formatDateTime(date, endHour)
  547. let displayText = ''
  548. if (selectedTime.isWithinOneHour) {
  549. displayText = `${selectedDate.label} 一小时内`
  550. } else {
  551. displayText = `${selectedDate.label} ${time}`
  552. }
  553. return {
  554. date: date,
  555. time: time,
  556. dateLabel: selectedDate.label,
  557. timeLabel: time,
  558. fullLabel: displayText,
  559. startTime: startTimeStr,
  560. endTime: endTimeStr,
  561. isToday: selectedDate.isToday,
  562. isImmediate: selectedTime.isWithinOneHour || false,
  563. isNight: selectedTime.night || false,
  564. isSoon: selectedTime.soon || false,
  565. startHour: startHour,
  566. endHour: endHour
  567. }
  568. },
  569. clearSelection: () => {
  570. selectedValue.value = []
  571. emit('update:modelValue', [])
  572. emit('change', null)
  573. emit('clear')
  574. },
  575. refreshTime: () => {
  576. currentTime.value = new Date()
  577. },
  578. setSelectedTime: (date, time) => {
  579. const dateExists = dateList.value.some(d => d.value === date)
  580. if (dateExists) {
  581. selectedValue.value = [date, time]
  582. handleChange([date, time])
  583. return true
  584. }
  585. return false
  586. }
  587. })
  588. </script>
  589. <style scoped lang="scss">
  590. .pickup-time-cascader {
  591. width: 100%;
  592. :deep(.el-cascader) {
  593. width: 100%;
  594. .el-input {
  595. width: 100%;
  596. .el-input__wrapper {
  597. width: 100%;
  598. box-sizing: border-box;
  599. &.is-focus {
  600. box-shadow: 0 0 0 1px var(--el-color-primary) inset;
  601. }
  602. .el-input__inner {
  603. color: #333;
  604. font-weight: 500;
  605. }
  606. }
  607. }
  608. }
  609. :deep(.el-cascader__dropdown) {
  610. max-height: 400px;
  611. overflow-y: auto;
  612. border-radius: 8px;
  613. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  614. .el-cascader-panel {
  615. border: none;
  616. .el-cascader-menu {
  617. min-width: 200px;
  618. max-height: 400px;
  619. overflow-y: auto;
  620. &:first-child {
  621. min-width: 220px;
  622. }
  623. .el-cascader-node {
  624. height: auto;
  625. min-height: 40px;
  626. padding: 8px 12px;
  627. &:hover:not(.is-disabled) {
  628. background-color: #f5f7fa;
  629. }
  630. &.is-selectable {
  631. &.in-active-path,
  632. &.is-active {
  633. background-color: #f0f7ff;
  634. .cascader-item {
  635. .item-label {
  636. color: var(--el-color-primary);
  637. font-weight: 600;
  638. }
  639. }
  640. }
  641. }
  642. &.is-disabled {
  643. .cascader-item {
  644. .item-label {
  645. color: #c0c4cc;
  646. }
  647. }
  648. }
  649. .cascader-item {
  650. display: flex;
  651. align-items: center;
  652. justify-content: space-between;
  653. width: 100%;
  654. min-height: 32px;
  655. box-sizing: border-box;
  656. font-size: 14px;
  657. &.is-date {
  658. .item-label {
  659. font-weight: 500;
  660. color: #333;
  661. }
  662. &.is-today {
  663. .item-label {
  664. color: #f2270c;
  665. }
  666. }
  667. }
  668. &.is-time {
  669. .item-label {
  670. color: #666;
  671. }
  672. }
  673. .item-label {
  674. flex: 1;
  675. text-align: left;
  676. overflow: hidden;
  677. text-overflow: ellipsis;
  678. white-space: nowrap;
  679. }
  680. .item-tag {
  681. font-size: 12px;
  682. padding: 2px 8px;
  683. border-radius: 10px;
  684. margin-left: 8px;
  685. font-weight: 500;
  686. &.tag-recommend {
  687. background-color: #fef2f0;
  688. color: #f2270c;
  689. border: 1px solid #f2270c;
  690. }
  691. &.tag-soon {
  692. background-color: #fff7e6;
  693. color: #ff9900;
  694. border: 1px solid #ff9900;
  695. }
  696. &.tag-night {
  697. background-color: #f0f5ff;
  698. color: #4a5cff;
  699. border: 1px solid #4a5cff;
  700. }
  701. }
  702. .item-recommend {
  703. font-size: 12px;
  704. color: #f2270c;
  705. font-weight: 500;
  706. margin-left: 8px;
  707. padding: 2px 6px;
  708. background-color: #fef2f0;
  709. border-radius: 4px;
  710. }
  711. .item-night {
  712. font-size: 12px;
  713. color: #4a5cff;
  714. font-weight: 500;
  715. margin-left: 8px;
  716. padding: 2px 6px;
  717. background-color: #f0f5ff;
  718. border-radius: 4px;
  719. }
  720. }
  721. }
  722. }
  723. }
  724. }
  725. .empty-content {
  726. padding: 20px;
  727. text-align: center;
  728. .empty-text {
  729. font-size: 14px;
  730. color: #999;
  731. }
  732. }
  733. }
  734. // 夜间时间段特殊样式
  735. .night-time-option {
  736. :deep(.el-cascader-node) {
  737. .cascader-item {
  738. .item-label {
  739. color: #4a5cff !important;
  740. }
  741. }
  742. &.is-active {
  743. .cascader-item {
  744. .item-label {
  745. color: #4a5cff !important;
  746. }
  747. }
  748. }
  749. }
  750. }
  751. // 推荐时间段特殊样式
  752. .recommend-time-option {
  753. :deep(.el-cascader-node) {
  754. .cascader-item {
  755. .item-label {
  756. color: #f2270c !important;
  757. }
  758. }
  759. &.is-active {
  760. .cascader-item {
  761. .item-label {
  762. color: #f2270c !important;
  763. }
  764. }
  765. }
  766. }
  767. }
  768. </style>