暫無描述

index.vue 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. <script lang="ts" setup>
  2. import { computed, ref, watch } from 'vue';
  3. import { Page, useVbenDrawer } from '@vben/common-ui';
  4. import dayjs from 'dayjs';
  5. import { ElCheckbox, ElRadioButton, ElRadioGroup } from 'element-plus';
  6. import {
  7. Tickets,
  8. Plus,
  9. User,
  10. CaretLeft,
  11. CaretRight,
  12. } from '@element-plus/icons-vue';
  13. import CreateTasK from './components/create/index.vue';
  14. import Day from './components/day/index.vue';
  15. import Month from './components/month/index.vue';
  16. import Week from './components/week/index.vue';
  17. // 根据timeType计算默认日期
  18. const getDefaultDate = (type: string) => {
  19. switch (type) {
  20. case 'month': {
  21. return dayjs().format('YYYY-MM-DD');
  22. }
  23. case 'week': {
  24. return dayjs().format('YYYY-MM-DD');
  25. }
  26. default: {
  27. return dayjs().format('YYYY-MM-DD');
  28. }
  29. }
  30. };
  31. const searchParams = ref({
  32. timeType: 'day',
  33. station: '',
  34. date: getDefaultDate('day'),
  35. });
  36. // 当前显示的日期
  37. const currentDate = ref(dayjs());
  38. const currentDate2 = ref(dayjs()); //单独给月份的时间
  39. // 任务授权状态
  40. const taskStatuses = ref({
  41. canAuthorize: true,
  42. authorized: true,
  43. beAuthorized: true,
  44. });
  45. // 监听timeType变化,更新默认日期和当前显示日期
  46. watch(
  47. () => searchParams.value.timeType,
  48. (newType) => {
  49. searchParams.value.date = getDefaultDate(newType);
  50. currentDate.value = dayjs(searchParams.value.date);
  51. currentDate2.value = dayjs(searchParams.value.date); //单独给月份的时间
  52. },
  53. );
  54. // 确保currentDate和currentDate2始终保持同步
  55. watch(
  56. () => currentDate.value,
  57. (newDate) => {
  58. currentDate2.value = newDate;
  59. },
  60. );
  61. //单独给月份的监听器
  62. watch(
  63. () => currentDate2.value,
  64. (newDate) => {
  65. currentDate.value = newDate;
  66. },
  67. );
  68. // 计算年份和周数
  69. const year = computed(() => {
  70. return dayjs(searchParams.value.date).year();
  71. });
  72. const weekNumber = computed(() => {
  73. return dayjs(searchParams.value.date).week();
  74. });
  75. // 计算当前选中日期所在周的开始和结束日期
  76. const currentWeekRange = computed(() => {
  77. const date = dayjs(searchParams.value.date);
  78. // 计算周开始(周一)
  79. const startOfWeek = date.startOf('week');
  80. // 调整为周一(dayjs默认周日为一周开始)
  81. const monday =
  82. startOfWeek.day() === 0 ? startOfWeek.subtract(1, 'day') : startOfWeek;
  83. // 计算周日
  84. const sunday = monday.add(6, 'day');
  85. return {
  86. start: monday,
  87. end: sunday,
  88. };
  89. });
  90. // 生成日历天数
  91. const calendarDays = computed(() => {
  92. const days = [];
  93. const year = currentDate.value.year();
  94. const month = currentDate.value.month();
  95. // 获取当月第一天
  96. const firstDay = dayjs(new Date(year, month, 1));
  97. // 获取当月第一天是星期几(0-6,0是周日)
  98. const firstDayOfWeek = firstDay.day();
  99. // 计算需要显示的上个月天数
  100. const prevMonthDays = firstDayOfWeek === 0 ? 6 : firstDayOfWeek - 1;
  101. // 获取当月最后一天
  102. const lastDay = dayjs(new Date(year, month + 1, 0));
  103. const lastDate = lastDay.date();
  104. // 添加上个月的日期
  105. for (let i = prevMonthDays; i > 0; i--) {
  106. const date = firstDay.subtract(i, 'day');
  107. const isInCurrentWeek =
  108. searchParams.value.timeType === 'week' &&
  109. date.isAfter(currentWeekRange.value.start.subtract(1, 'day')) &&
  110. date.isBefore(currentWeekRange.value.end.add(1, 'day'));
  111. days.push({
  112. date: date.format('YYYY-MM-DD'),
  113. day: date.date(),
  114. isToday: date.isSame(dayjs(), 'day'),
  115. isOtherMonth: true,
  116. isInCurrentWeek,
  117. });
  118. }
  119. // 添加当月的日期
  120. for (let i = 1; i <= lastDate; i++) {
  121. const date = dayjs(new Date(year, month, i));
  122. const isInCurrentWeek =
  123. searchParams.value.timeType === 'week' &&
  124. date.isAfter(currentWeekRange.value.start.subtract(1, 'day')) &&
  125. date.isBefore(currentWeekRange.value.end.add(1, 'day'));
  126. days.push({
  127. date: date.format('YYYY-MM-DD'),
  128. day: i,
  129. isToday: date.isSame(dayjs(), 'day'),
  130. isOtherMonth: false,
  131. isInCurrentWeek,
  132. });
  133. }
  134. // 计算需要显示的下个月天数
  135. const totalDays = days.length;
  136. const nextMonthDays = 42 - totalDays; // 6行7列=42个格子
  137. // 添加下个月的日期
  138. for (let i = 1; i <= nextMonthDays; i++) {
  139. const date = lastDay.add(i, 'day');
  140. const isInCurrentWeek =
  141. searchParams.value.timeType === 'week' &&
  142. date.isAfter(currentWeekRange.value.start.subtract(1, 'day')) &&
  143. date.isBefore(currentWeekRange.value.end.add(1, 'day'));
  144. days.push({
  145. date: date.format('YYYY-MM-DD'),
  146. day: date.date(),
  147. isToday: date.isSame(dayjs(), 'day'),
  148. isOtherMonth: true,
  149. isInCurrentWeek,
  150. });
  151. }
  152. return days;
  153. });
  154. // 切换到上个月
  155. const prevMonth = () => {
  156. currentDate.value = currentDate.value.subtract(1, 'month');
  157. currentDate2.value = currentDate2.value.subtract(1, 'month');
  158. };
  159. // 切换到下个月
  160. const nextMonth = () => {
  161. currentDate.value = currentDate.value.add(1, 'month');
  162. currentDate2.value = currentDate2.value.add(1, 'month');
  163. };
  164. // 选择日期
  165. const selectDate = (date: string) => {
  166. searchParams.value.date = date;
  167. };
  168. const [CreateTaskDrawer, createTaskDrawerApi] = useVbenDrawer({
  169. // 连接抽离的组件
  170. connectedComponent: CreateTasK,
  171. // placement: 'left',
  172. });
  173. const createTaskDrawerEvent = () => {
  174. createTaskDrawerApi.setData({ isUpdate: false }).open();
  175. };
  176. console.log(currentDate.value);
  177. </script>
  178. <template>
  179. <Page
  180. style="height: 100%"
  181. title=""
  182. :auto-content-height="true"
  183. class="h-screen"
  184. >
  185. <div class="flex h-full min-h-0 flex-row space-x-4">
  186. <!--左侧筛选区域-->
  187. <div
  188. class="relative h-full w-80 flex-shrink-0 overflow-y-auto rounded-lg bg-white p-4 pb-20 shadow"
  189. >
  190. <!--油站选择-->
  191. <div class="mb-4">
  192. <ElSelect
  193. v-model="searchParams.station"
  194. placeholder="请选择油站"
  195. class="w-full"
  196. >
  197. <ElOption label="张三站-龙飞加油站" value="1" />
  198. </ElSelect>
  199. </div>
  200. <!--日期导航和视图切换-->
  201. <div class="mb-4">
  202. <div class="flex items-center justify-between">
  203. <!--时间筛选靠左-->
  204. <div class="flex items-center space-x-2">
  205. <el-icon><CaretLeft @click="prevMonth" /></el-icon>
  206. <span class="font-medium">{{
  207. currentDate.format('YYYY年 M月')
  208. }}</span>
  209. <el-icon><CaretRight @click="nextMonth" /></el-icon>
  210. </div>
  211. <!--视图切换靠右-->
  212. <ElRadioGroup
  213. v-model="searchParams.timeType"
  214. size="small"
  215. class="custom-radio-group"
  216. >
  217. <ElRadioButton label="day" class="custom-radio-btn"
  218. >日</ElRadioButton
  219. >
  220. <ElRadioButton label="week" class="custom-radio-btn"
  221. >周</ElRadioButton
  222. >
  223. <ElRadioButton label="month" class="custom-radio-btn"
  224. >月</ElRadioButton
  225. >
  226. </ElRadioGroup>
  227. </div>
  228. <!--日历显示-->
  229. <div class="calendar-container mt-1">
  230. <!--星期标题-->
  231. <div class="mb-1 flex justify-between text-xs text-gray-500">
  232. <div class="w-7 text-center">一</div>
  233. <div class="w-7 text-center">二</div>
  234. <div class="w-7 text-center">三</div>
  235. <div class="w-7 text-center">四</div>
  236. <div class="w-7 text-center">五</div>
  237. <div class="w-7 text-center">六</div>
  238. <div class="w-7 text-center">日</div>
  239. </div>
  240. <!--日期网格-->
  241. <div class="grid grid-cols-7 gap-1">
  242. <div
  243. v-for="day in calendarDays"
  244. :key="day.date"
  245. :style="
  246. day.date === '2025-12-22' || day.date === '2025-12-23'
  247. ? 'background-color: #FFE6E0'
  248. : day.isInCurrentWeek && !day.isToday
  249. ? 'background-color: #E6E8EB'
  250. : ''
  251. "
  252. class="flex h-7 w-7 cursor-pointer items-center justify-center rounded text-xs"
  253. :class="{
  254. 'bg-blue-600 text-white': day.isToday,
  255. 'bg-gray-100 text-gray-400': day.isOtherMonth,
  256. 'hover:bg-gray-100': !day.isOtherMonth,
  257. }"
  258. @click="selectDate(day.date)"
  259. >
  260. {{ day.day }}
  261. </div>
  262. </div>
  263. </div>
  264. </div>
  265. <!--任务授权状态-->
  266. <div class="mb-4">
  267. <div class="mb-2 font-medium">任务授权状态:</div>
  268. <div class="space-y-1" style="display: flex; flex-direction: column">
  269. <ElCheckbox v-model="taskStatuses.canAuthorize">
  270. 任务可授权
  271. </ElCheckbox>
  272. <ElCheckbox v-model="taskStatuses.authorized">
  273. 任务已授权
  274. </ElCheckbox>
  275. <ElCheckbox v-model="taskStatuses.beAuthorized">
  276. 任务被授权
  277. </ElCheckbox>
  278. </div>
  279. </div>
  280. <!--新增任务按钮固定在底部-->
  281. <div class="absolute bottom-0 left-0 w-full bg-white p-4">
  282. <div class="space-y-2">
  283. <ElButton
  284. type="primary"
  285. class="w-full"
  286. @click="createTaskDrawerEvent"
  287. >
  288. <template #icon>
  289. <el-icon><Plus /></el-icon> </template
  290. >油站新增任务
  291. </ElButton>
  292. </div>
  293. <div class="mt-1 space-y-2">
  294. <ElButton type="default" class="w-full"
  295. ><template #icon>
  296. <el-icon><User /></el-icon> </template
  297. >访客新增任务
  298. </ElButton>
  299. </div>
  300. <div class="mt-1 space-y-2">
  301. <ElButton type="default" class="w-full">
  302. <template #icon>
  303. <el-icon><Tickets /></el-icon>
  304. </template>
  305. 工单提报
  306. </ElButton>
  307. </div>
  308. </div>
  309. </div>
  310. <!--右侧内容区域-->
  311. <div class="flex h-full min-h-0 flex-1 flex-col">
  312. <ElCard class="flex h-full w-full flex-col">
  313. <div
  314. class="min-h-0 flex-1 overflow-y-auto"
  315. style="max-height: calc(100vh - 120px)"
  316. >
  317. <Day v-if="searchParams.timeType === 'day'" />
  318. <Week
  319. v-if="searchParams.timeType === 'week'"
  320. :year="year"
  321. :week-number="weekNumber"
  322. />
  323. <Month
  324. v-if="searchParams.timeType === 'month'"
  325. :month="currentDate"
  326. />
  327. </div>
  328. </ElCard>
  329. </div>
  330. </div>
  331. <CreateTaskDrawer />
  332. </Page>
  333. </template>
  334. <style scoped lang="scss">
  335. // :deep(.el-card) {
  336. // border: none !important;
  337. // box-shadow: none !important;
  338. // background: transparent !important;
  339. // }
  340. :deep(.custom-radio-group .el-radio-button) {
  341. padding: 4px;
  342. // background: transparent !important;
  343. background-color: #fafafa !important;
  344. border: none !important;
  345. }
  346. :deep(.custom-radio-group .el-radio-button__inner) {
  347. padding: 0 8px;
  348. background: transparent !important;
  349. border: none !important;
  350. box-shadow: none !important;
  351. }
  352. :deep(.custom-radio-group .el-radio-button__inner:hover) {
  353. color: #1890ff !important;
  354. background: transparent !important;
  355. }
  356. :deep(.custom-radio-group .el-radio-button.is-active .el-radio-button__inner) {
  357. color: #1890ff !important;
  358. background: transparent !important;
  359. border: none !important;
  360. box-shadow: none !important;
  361. }
  362. </style>