246 lines
7.1 KiB
Vue
246 lines
7.1 KiB
Vue
<template>
|
||
<el-calendar v-model="value" v-if="isComponentActive" v-loading="isComponentLoading"
|
||
element-loading-text="主人,别着急我在努力加载中^_^">
|
||
<template #date-cell="{ data }">
|
||
<div class="date-cell-holiday"
|
||
v-if="Object.keys(holidays).includes(data.day)"
|
||
@click="onclick(data.date)"
|
||
style="cursor: pointer;">
|
||
<span style="color: red;">{{ data.day.split('-')[2] }}</span><br>
|
||
<span>{{ holidays[data.day] }}</span>
|
||
</div>
|
||
<div class="date-cell" v-else @click="onclick(data.date)" style="cursor: pointer;">
|
||
<span style="color: red;" v-if="data.date.getDay() == 0 || data.date.getDay() == 6">
|
||
{{ data.day.split('-')[2] }}
|
||
</span>
|
||
<span v-else> {{ data.day.split('-')[2] }}</span>
|
||
</div>
|
||
<div class="date-cell-event" v-if="Object.keys(eventDataObject).includes(data.day)">
|
||
<span>{{ eventDataObject[data.day] }}</span>
|
||
</div>
|
||
</template>
|
||
</el-calendar>
|
||
|
||
<div class="toolbar">
|
||
<span class="toolbar-label">待办事项</span>
|
||
<el-select v-model="pageSize" size="small" style="width: 110px">
|
||
<el-option label="10 条/页" :value="10" />
|
||
<el-option label="20 条/页" :value="20" />
|
||
<el-option label="50 条/页" :value="50" />
|
||
<el-option label="100 条/页" :value="100" />
|
||
</el-select>
|
||
</div>
|
||
|
||
<el-table :data="eventDataList" v-loading="isComponentLoading">
|
||
<el-table-column property="eventDate" label="Date" width="150" />
|
||
<el-table-column property="title" label="title" width="200" />
|
||
<el-table-column property="eventInfo" label="eventInfo" />
|
||
<el-table-column label="操作" width="120">
|
||
<template #default="{ row }">
|
||
<el-button size="small" type="danger" @click="onDelete(row)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<el-pagination
|
||
class="pagination"
|
||
background
|
||
layout="prev, pager, next, total"
|
||
:total="pagination.total"
|
||
:page-size="pageSize"
|
||
:current-page="pagination.page"
|
||
@current-change="onPageChange"
|
||
/>
|
||
|
||
<el-dialog v-model="dialogFormVisible" title="待办事项">
|
||
<el-form :model="form">
|
||
<el-form-item label="日期" :label-width="formLabelWidth">
|
||
<span>{{ form.eventDate }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="title" :label-width="formLabelWidth">
|
||
<el-input v-model="form.title" autocomplete="off" />
|
||
</el-form-item>
|
||
<el-form-item label="eventInfo" :label-width="formLabelWidth">
|
||
<el-input v-model="form.eventInfo" autocomplete="off" type="textarea" />
|
||
</el-form-item>
|
||
<el-form-item label="alert" :label-width="formLabelWidth">
|
||
<el-checkbox-group v-model="checkedalerts" :min="0" :max="2">
|
||
<el-checkbox v-for="city in alerts" :key="city" :value="city">{{
|
||
city
|
||
}}</el-checkbox>
|
||
</el-checkbox-group>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<span class="dialog-footer">
|
||
<el-button @click="dialogFormVisible = false">Cancel</el-button>
|
||
<el-button type="primary" @click="confirm">
|
||
Confirm
|
||
</el-button>
|
||
</span>
|
||
</template>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { get_holiday } from '@/service/calendar'
|
||
import { toLocalDate } from '@/utils/date'
|
||
import { get_event, post_event, delete_event } from '@/api/calendar'
|
||
import { Event } from '@/entity/index'
|
||
import { settingsStore } from '@/store/modules/settings'
|
||
|
||
// -- IMPORT --
|
||
const useSettingsStore = settingsStore()
|
||
|
||
// -- REACTIVE OBJECT --
|
||
const holidays = reactive<Record<string, string>>({})
|
||
|
||
const eventDataList = reactive<Event[]>([])
|
||
const eventDataObject = reactive<Record<string, string>>({})
|
||
const form = reactive({
|
||
// 与后端 CalendarEventRequest 对齐(camelCase)
|
||
eventDate: '',
|
||
title: '',
|
||
eventInfo: '',
|
||
})
|
||
// -- REF OBJECT --
|
||
const value = ref(new Date())
|
||
const dialogFormVisible = ref(false)
|
||
const formLabelWidth = '140px'
|
||
const isComponentActive = ref(false)
|
||
const isComponentLoading = ref(true)
|
||
|
||
const checkedalerts = ref([])
|
||
const alerts = ['email', 'messageBox', '手机短信', '微信通知']
|
||
|
||
// 分页
|
||
const pageSize = ref(10)
|
||
const pagination = reactive({ page: 1, total: 0 })
|
||
|
||
// 当前选中的月份(yyyy-MM)
|
||
const monthStr = computed(() => {
|
||
const d = value.value
|
||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`
|
||
})
|
||
|
||
// -- EVENT DEFINITION
|
||
const onclick = (date: Date) => {
|
||
form.eventDate = toLocalDate(date)
|
||
dialogFormVisible.value = true
|
||
}
|
||
|
||
const confirm = async () => {
|
||
await post_event({
|
||
eventDate: form.eventDate,
|
||
title: form.title,
|
||
eventInfo: form.eventInfo,
|
||
})
|
||
dialogFormVisible.value = false
|
||
handleCommand('refresh')
|
||
}
|
||
|
||
const handleCommand = (command: string) => {
|
||
if (command === 'refresh') {
|
||
useSettingsStore.settings.isLoading = true
|
||
useSettingsStore.settings.isRouterAlive = false
|
||
nextTick(() => {
|
||
useSettingsStore.settings.isRouterAlive = true
|
||
useSettingsStore.settings.isLoading = false
|
||
})
|
||
}
|
||
}
|
||
|
||
const onPageChange = (p: number) => {
|
||
pagination.page = p
|
||
reloadEvents()
|
||
}
|
||
|
||
const reloadEvents = async () => {
|
||
isComponentLoading.value = true
|
||
try {
|
||
const res = await get_event({
|
||
month: monthStr.value,
|
||
page: pagination.page,
|
||
pageSize: pageSize.value
|
||
})
|
||
// 后端 ApiResponse 结构:{code, message, data: {items, total, page, pageSize}}
|
||
const pageResp = res.data?.data ?? res.data
|
||
const items = Array.isArray(pageResp) ? pageResp : (pageResp?.items ?? [])
|
||
pagination.total = pageResp?.total ?? items.length
|
||
pagination.page = pageResp?.page ?? pagination.page
|
||
|
||
eventDataList.length = 0
|
||
eventDataObject.value = {}
|
||
items.forEach((record: Event) => {
|
||
eventDataList.push(record)
|
||
eventDataObject[record.eventDate] = record.title
|
||
})
|
||
} catch {
|
||
// 错误已由 axios 拦截器处理
|
||
} finally {
|
||
isComponentLoading.value = false
|
||
}
|
||
}
|
||
|
||
const onDelete = async (row: Event) => {
|
||
if (!row.id) return
|
||
try {
|
||
await ElMessageBox.confirm(`确认删除 "${row.title}"?`, '提示', { type: 'warning' })
|
||
} catch {
|
||
return // 用户取消
|
||
}
|
||
await delete_event(row.id)
|
||
ElMessage.success('删除成功')
|
||
reloadEvents()
|
||
}
|
||
|
||
// 切换月份 / 改变 pageSize → 重新加载
|
||
watch([monthStr, pageSize], () => {
|
||
pagination.page = 1
|
||
reloadEvents()
|
||
}, { deep: true })
|
||
|
||
/**
|
||
* AUTO INVOKE FUNCTION
|
||
*/
|
||
onMounted(async () => {
|
||
isComponentActive.value = true
|
||
|
||
await get_holiday().then((resp) => {
|
||
Object.keys(resp.data).forEach(element => {
|
||
holidays[element] = resp.data[element]
|
||
})
|
||
}).catch(() => { /* 节假日失败不影响 */ })
|
||
|
||
await reloadEvents()
|
||
})
|
||
|
||
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.date-cell-holiday {
|
||
background: #dfd;
|
||
}
|
||
.date-cell-event {
|
||
background: yellow;
|
||
}
|
||
|
||
.toolbar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin: 16px 0 8px;
|
||
}
|
||
.toolbar-label {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
}
|
||
.pagination {
|
||
margin: 16px 0;
|
||
justify-content: flex-end;
|
||
}
|
||
</style>
|