vue3 Fullcalendar 实现动态创建、编辑(拖拽)计划-----详细版
vue
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
项目地址:https://gitcode.com/gh_mirrors/vu/vue
免费下载资源
·
前情介绍:vue3 fullcalendar实现日历视图创建事项
Demo简省版
考虑到很多人找我要demo代码,但我又不一定能及时回复,所以我决定再详细的列一下我的代码:
<template>
<div v-loading="loading" class="plan_management">
<!-- 这里是自定义头部,切换视图类型和切换日期 -->
<div class="calendarHeader">
<div class="header_left">
<h1>{{ type ==='3' ? '计划列表' : calendarTitle }}</h1>
</div>
<div class="header_right">
<span v-if="type!=='3'&&isShowBack" class="blue-color backToday" @click="getToday()">{{ type==='1'?'返回本月':'返回本周' }}</span>
<el-select v-model="type" placeholder="视图类型" style="width: 80px" size="small" class="header_select" @change="handleChangeType">
<el-option label="月" value="1" />
<el-option label="周" value="2" />
<el-option label="列" value="3" />
</el-select>
<!-- 选择月份的日期框 -->
<el-date-picker v-if="type === '1'" v-model="showMonth" type="month" size="small" :clearable="false" placeholder="请选择日期" style="margin-left: 10px; vertical-align: middle;" @change="changeDate" />
<el-button-group v-if="type === '2'" style="margin-left: 10px;">
<el-button size="small" class="el-icon-arrow-left" @click="getPrev()">上一周</el-button>
<el-button size="small" @click="getNext()">下一周<i class="el-icon-arrow-right" /></el-button>
</el-button-group>
<el-select v-model="planCategoryId" placeholder="计划分类" style="width: 120px" class="header_select" size="small" @change="handleChangePlanId">
<el-option label="全部" value="" />
<el-option v-for="item in categoryList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<div v-if="categoryList.length" class="separator" />
<el-tooltip content="类目维护" placement="top" effect="light">
<el-button size="small" class="el-icon-setting setting_btn" @click="handleSetting()" />
</el-tooltip>
<el-button v-if="categoryList.length" size="small" type="primary" class="el-icon-plus" @click="handleAddPlan()"> 新增计划</el-button>
</div>
</div>
<!-- 月视图和周视图显示,列视图显示表格形式 -->
<div v-show="type !== '3'" ref="fullcalendar" class="card" />
<el-table
v-show="type === '3'"
ref="tableRef"
v-loading="loading"
:data="infoList"
fit
border
height="auto"
size="medium"
class="dark-table base-table format-height-table"
>
<el-table-column label="标题" prop="title " :min-width="110" show-overflow-tooltip>
<template #default="{ row }">
<span class="nowrap blue-color" @click="handleClickList(row)">{{ row.title || '--' }}</span>
</template>
</el-table-column>
<el-table-column label="类型" prop="planCategoryName" :minwidth="110" show-overflow-tooltip>
<template #default="{ row }">
<span>{{ row.planCategoryName || '--' }}</span>
</template>
</el-table-column>
<el-table-column label="负责人/协作者" prop="managerId " :min-width="110" show-overflow-tooltip>
<template #default="{ row }">
{{ row.managerId }}
</template>
</el-table-column>
<el-table-column label="开始时间" prop="startDate " :minwidth="110" show-overflow-tooltip>
<template #default="{ row }">
<span>{{ row.startDate }} {{ row.startDateMinute }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" prop="endDate " :minwidth="110" show-overflow-tooltip>
<template #default="{ row }">
<span>{{ row.endDate }} {{ row.endDateMinute }}</span>
</template>
</el-table-column>
<el-table-column label="操作" :width="110" fixed="right" class-name="fixed-right">
<template #default="{ row }">
<span class="blue-color" @click="handleEdit(row)">编辑</span>
<span class="blue-color" @click="handleDelete(row)">删除</span>
</template>
</el-table-column>
</el-table>
<!-- 新建编辑日程 -->
<DrawerAddPlan :drawer="drawerVisiable" :drawer-type="drawerType" :category-list="categoryAllList" :detail-data="detailData" @closeDrawer="closeDrawer" />
<!-- 类目维护,我这边有维护类型的概念,这里可以删除 -->
<DialogCategory :dialog-show="dialogCategory" :detail-list="categoryAllList" @closeDialog="closeDialogCategory" />
<!-- 查看计划 -->
<DialogCalendar :dialog-show="dialogCalendar" :detail-info="detailInfo" :category-json="categoryJSON" @closeDialog="closeDialogCalendar" />
</div>
</template>
<script>
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from 'vue'
import { formatDateFilter, formatDateDay, formatDate, formatCalendar, formatYM, getWeekNumber } from '@/utils/formatTime'
import DrawerAddPlan from './components/drawer-add-plan.vue'
import DialogCategory from './components/dialog-category.vue'
import DialogCalendar from './components/dialog-calendar.vue'
import { Calendar } from '@fullcalendar/core'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import listPlugin from '@fullcalendar/list'
import interactionPlugin from '@fullcalendar/interaction'
import { documentPlanCategoryList, plandetailList, plandetailId, deletePlanDetail, savePlanDetail } from '@/api/planManagement'
export default {
name: 'PlanManagement',
components: { DrawerAddPlan, DialogCategory, DialogCalendar},
setup() {
const { proxy } = getCurrentInstance()
const state = reactive({
calendarTitle: new Date().getFullYear() + '年' + Number(new Date().getMonth() + 1) + '月', // 日历头部显示文字
dialogVisiable: false,
showMonth: formatYM(new Date()), // 显示月份
loading: false,
isShowBack: false, // 是否显示回到当月或当周
planCategoryId: '', // 计划分类Id
type: '1',
dialogType: '',
detailInfo: {},
Tcalendar: null,
drawerVisiable: false,
drawerType: '',
colorJSON: { // 我这里有类别的概念,不同的类别又有不同的颜色
'green': { title: '#00B578', class: 'green' },
'red': { title: '#FA5151', class: 'red' },
'orange': { title: '#FF8F1F', class: 'orange' },
'yellow': { title: '#FFC300', class: 'yellow' },
'cyan': { title: '#07B9B9', class: 'cyan' },
'blue': { title: '#3662EC', class: 'blue' },
'purple': { title: '#8A38F5', class: 'purple' },
'magenta': { title: '#EB2F96', class: 'magenta' }
},
fullcalendar: ref(),
detailData: {},
calendarList: [],
calendarViewType: {
1: 'dayGridMonth',
2: 'timeGridWeek',
3: 'listMonth'
},
nowDate: new Date(),
dialogCategory: false, // 计划分类弹出窗
dialogCalendar: false, // 计划详情弹出窗
infoList: [], // 日历显示的列信息
categoryJSON: {}, // 计划分类json
categoryAllList: [], // 全部计划分类
categoryList: [] // 已启用计划分类
})
onMounted(() => {
initCalendar()
getPlanCategoryList()
})
const initCalendar = () => {
state.Tcalendar = new Calendar(state.fullcalendar, {
plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
initialView: 'dayGridMonth',
aspectRatio: 2.2,
locale: 'zh-cn',
handleWindowResize: true,
editable: true, // 允许编辑表格
droppable: true,
eventDurationEditable: true,
eventResizableFromStart: true,
selectable: true, // 允许用户通过单击和拖动来突出显示多个日期或时间段
firstDay: 1, // 设置一周中显示的第一天是哪天,周日是0,周一是1,类推。
unselectAuto: true, // 当点击页面日历以外的位置时,是否自动取消当前的选中状态
unselectCancel: '.el-drawer',
dayMaxEvents: true,
// eventLimit: true,
headerToolbar: false,
buttonText: {
today: '回到今天',
month: '月',
week: '周',
list: '列',
day: '日'
},
allDayText: '全天',
events: state.infoList,
eventClassNames: function(arg) { // 添加自定义class
return [arg.event.extendedProps.class]
},
eventContent: function(arg) {
const italicEl = document.createElement('div')
if (arg.event.extendedProps.startDateMinute && state.type === '1') {
const childEl = document.createElement('span')
childEl.innerHTML = arg.event.extendedProps.startDateMinute
italicEl.append(childEl)
}
italicEl.append(arg.event.title)
italicEl.setAttribute('class', `plan_title ${arg.event.extendedProps.class}`)
return { domNodes: [italicEl] }
},
eventDrop: function(info) {
// 拖拽停止时触发
handleDrap(info)
},
eventClick: function(info) {
// 点击查看时触发
handleClick(info)
},
select: function(info) {
// 视图选择日期触发
handleSelectDate(info)
},
eventResize: function(info) {
handleEventResize(info)
}
})
state.Tcalendar.render()
}
// 上一月、周、日
const getPrev = () => {
state.Tcalendar.prev()
state.calendarTitle = state.Tcalendar.view.title
const nowDate = formatDateFilter(state.calendarTitle)
// 判断已经是当前周隐藏返回当前周按钮
if ((getWeekNumber(nowDate) === getWeekNumber(new Date())) && (new Date(nowDate).getFullYear() === new Date().getFullYear())) {
state.isShowBack = false
} else {
state.isShowBack = true
}
getCalendarList()
}
// 下一月、周、日
const getNext = () => {
state.Tcalendar.next()
state.calendarTitle = state.Tcalendar.view.title
const nowDate = formatDateFilter(state.calendarTitle)
// 判断已经是当前周隐藏返回当前周按钮
if ((getWeekNumber(nowDate) === getWeekNumber(new Date())) && (new Date(nowDate).getFullYear() === new Date().getFullYear())) {
state.isShowBack = false
} else {
state.isShowBack = true
}
getCalendarList()
}
// 回到今天
const getToday = () => {
state.Tcalendar.today()
state.calendarTitle = state.Tcalendar.view.title
state.isShowBack = false
state.showMonth = formatYM(new Date())
getCalendarList()
}
// 计划分类列表
const getPlanCategoryList = () => {
state.loading = true
state.categoryList = []
state.categoryAllList = []
state.categoryJSON = {}
documentPlanCategoryList({ page: '1', limit: '-1' }).then(res => {
state.loading = false
if (res) {
res.data.data.forEach(item => {
state.categoryJSON[item.id] = { lable: item.name, color: item.color, status: item.status }
state.categoryAllList.push(item)
if (item.status) {
state.categoryList.push(item)
}
})
getCalendarList()
}
})
}
const handleSetting = () => {
state.dialogCategory = true
}
const getCalendarList = () => {
const params = {
planCategoryId: state.planCategoryId,
startDate: state.type === '3' ? '' : formatDateFilter(state.calendarTitle),
type: state.type
}
state.loading = true
state.Tcalendar.getEventSources().forEach(item => {
item.remove()
})
plandetailList(params).then(res => {
state.loading = false
if (res) {
state.infoList = res.data.data
state.infoList.forEach(item => {
item.class = state.colorJSON[state.categoryJSON[item.planCategoryId]?.color]?.class
item.start = item.startDateMinute ? item.startDate + ' ' + item.startDateMinute : item.startDate
if ((item.startDate !== item.endDate) || item.isAllDay) {
item.end = item.endDateMinute ? formatDate(new Date(item.endDate).getTime()) + ' ' + item.endDateMinute : formatDate(new Date(item.endDate).getTime() + 1000 * 60 * 60 * 24)
} else {
item.end = item.endDateMinute ? item.endDate + ' ' + item.endDateMinute : item.endDate
}
})
state.Tcalendar.addEventSource(state.infoList)
}
})
}
// 点击计划查看
const handleClick = (info) => {
const detail = info.event._def
plandetailId(detail.publicId).then(res => {
if (res) {
state.detailInfo = res.data.data
state.dialogCalendar = true
}
})
}
// 列视图点击查看
const handleClickList = (row) => {
plandetailId(row.id).then(res => {
if (res) {
state.detailInfo = res.data.data
state.dialogCalendar = true
}
})
}
// 删除
const handleDelete = (row) => {
proxy.$confirm('是否确认删除', '删除确认', {
confirmButtonText: '确认删除',
cancelButtonText: '取消',
showCancelButton: true,
closeOnClickModal: false,
type: 'warning'
}).then(() => {
state.loading = true
deletePlanDetail(row.id).then(function(res) {
state.loading = false
if (res) {
proxy.$message.success('删除成功!')
getCalendarList()
}
})
}).catch(() => {})
}
// 编辑
const handleEdit = (row) => {
state.loading = true
plandetailId(row.id).then(res => {
state.loading = false
if (res) {
state.detailData = res.data.data
state.drawerType = 'edit'
state.drawerVisiable = true
}
})
}
const closeDialog = () => {
state.dialogVisiable = false
}
const closeDialogCategory = (val) => {
state.dialogCategory = false
if (val) {
getPlanCategoryList()
}
}
const closeDialogCalendar = (val) => {
state.dialogCalendar = false
if (val?.isRefresh) {
getCalendarList()
}
if (val?.isEdit) {
// 编辑计量计划
state.detailData = val.info
state.drawerType = 'edit'
state.drawerVisiable = true
}
}
const closeDrawer = (val) => {
state.drawerVisiable = false
if (val) {
getCalendarList()
}
}
// 切换视图类型
const handleChangeType = (val) => {
if (val === '1') {
state.Tcalendar.changeView('dayGridMonth')
state.showMonth = formatYM(new Date())
} else if (val === '2') {
state.Tcalendar.changeView('timeGridWeek')
} else {
state.Tcalendar.changeView('listMonth')
}
state.isShowBack = false
state.calendarTitle = state.Tcalendar.view.title
getToday()
}
// 切换类型
const handleChangePlanId = () => {
getCalendarList()
}
// 新增计划
const handleAddPlan = () => {
state.drawerVisiable = true
state.drawerType = 'add'
state.detailData = {
managerIds: [],
fileList: [],
isAllDay: 0,
startDate: formatDate(new Date()),
endDate: formatDate(new Date()),
startDateMinute: new Date().getHours() < 23 ? new Date().getHours() + 1 + ':00' : '23:00',
endDateMinute: new Date().getHours() < 22 ? new Date().getHours() + 2 + ':00' : '23:00'
}
}
// 拖拽计划时触发
const handleDrap = (info) => {
const params = { ...info.event.extendedProps, id: info.event.id }
params.startDate = formatCalendar(info.event.start)
if (info.event.allDay) {
// 全天
params.startDateMinute = ''
params.endDateMinute = ''
params.isAllDay = 1
params.endDate = info.event.end ? formatCalendar(new Date(info.event.end).getTime() - 24 * 3600 * 1000) : formatCalendar(info.event.start)
} else {
// 非全天
params.startDateMinute = formatCalendar(info.event.start, 'hour')
params.endDateMinute = formatCalendar(new Date(info.event.end), 'hour')
params.endDate = info.event.end ? formatCalendar(new Date(info.event.end)) : formatCalendar(info.event.start)
params.isAllDay = 0
}
state.loading = true
savePlanDetail(params).then(res => {
state.loading = false
if (res) {
proxy.$message.success('修改成功!')
getCalendarList()
}
})
}
// 调整大小时触发
const handleEventResize = (info) => {
const params = { ...info.event.extendedProps, id: info.event.id }
params.startDate = formatCalendar(info.event.start)
if (info.event.allDay) {
// 全天
params.startDateMinute = ''
params.endDateMinute = ''
params.isAllDay = 1
params.endDate = info.event.end ? formatCalendar(new Date(info.event.end).getTime() - 24 * 3600 * 1000) : formatCalendar(info.event.start)
} else {
// 非全天
params.startDateMinute = formatCalendar(info.event.start, 'hour')
params.endDateMinute = formatCalendar(new Date(info.event.end), 'hour')
params.endDate = info.event.end ? formatCalendar(new Date(info.event.end)) : formatCalendar(info.event.start)
params.isAllDay = 0
}
state.loading = true
savePlanDetail(params).then(res => {
state.loading = false
if (res) {
proxy.$message.success('修改成功!')
getCalendarList()
}
})
}
// 拖拽触发
const handleSelectDate = (info) => {
if (info.view.type === 'timeGridWeek') {
// 周视图
if (info.allDay) {
state.detailData = {
startDate: formatCalendar(info.startStr),
endDate: formatCalendar(new Date(info.endStr).getTime() - 24 * 3600 * 1000),
managerIds: [],
fileList: [],
startDateMinute: '',
endDateMinute: '',
isAllDay: 1
}
} else {
state.detailData = {
startDate: formatCalendar(info.startStr),
endDate: formatCalendar(info.endStr),
managerIds: [],
fileList: [],
startDateMinute: formatCalendar(info.startStr, 'hour'),
endDateMinute: formatCalendar(info.endStr, 'hour'),
isAllDay: 0
}
}
} else {
// 月视图
if (info.startStr === formatDate(new Date(info.endStr).getTime() - 24 * 3600 * 1000)) {
// 只选择一天,默认非全天
state.detailData = {
startDate: info.startStr,
endDate: formatDate(new Date(info.endStr).getTime() - 24 * 3600 * 1000),
managerIds: [],
fileList: [],
startDateMinute: new Date().getHours() < 23 ? new Date().getHours() + 1 + ':00' : '23:00',
endDateMinute: new Date().getHours() < 22 ? new Date().getHours() + 2 + ':00' : '23:00',
isAllDay: 0
}
} else {
// 跨天,默认全天
state.detailData = {
startDate: info.startStr,
endDate: formatDate(new Date(info.endStr).getTime() - 24 * 3600 * 1000),
managerIds: [],
fileList: [],
startDateMinute: '',
endDateMinute: '',
isAllDay: 1
}
}
}
state.drawerVisiable = true
state.drawerType = 'add'
}
// 切换月份和日期
const changeDate = (date) => {
state.Tcalendar.gotoDate(formatDate(date))
// 判断不是当前月份,显示返回当前月
if (date.getMonth() !== new Date().getMonth() || (new Date().getFullYear() !== new Date(date).getFullYear())) {
state.isShowBack = true
} else {
state.isShowBack = false
}
state.calendarTitle = state.Tcalendar.view.title
getCalendarList()
}
return {
...toRefs(state),
formatDateFilter,
changeDate,
getPrev,
handleDrap,
handleAddPlan,
handleClickList,
handleDelete,
handleEdit,
handleEventResize,
handleChangePlanId,
getNext,
getToday,
closeDrawer,
getCalendarList,
handleSetting,
handleChangeType,
closeDialog,
handleClick,
closeDialogCategory,
closeDialogCalendar,
handleSelectDate,
formatDateDay,
formatDate
}
}
}
</script>
<style lang="scss" scoped>
.calendarHeader {
margin: 0 0 20px 0;
display: flex;
flex-direction: row;
justify-content: space-between;
.header_select {
margin: 0 0 0 10px;
display: inline-block;
vertical-align: middle;
}
.separator {
display: inline-block;
position: relative;
margin: 0 12px;
&:after {
content: '';
position: absolute;
top: -16px;
left: 0;
height: 24px;
width: 1px;
background: #DCDFE6;
}
}
}
h1 {
font-size: 20px;
font-weight: 500;
line-height: 32px;
margin: 0 0 0 0;
text-align: left;
vertical-align: middle;
display: inline-block;
color: #303133;
}
// .el-button-group {
// vertical-align: top;
// }
</style>
<style lang="scss" scoped>
@import 'fullcalendar.scss';
</style>
以上是主要页面的代码部分,其中样式在fullcalendar.scss这里,内容是:
.plan_management {
.fc-col-header {
height: 32px;
line-height: 32px;
background-color: #f5f7fa;
}
.fc-theme-standard .fc-scrollgrid {
border: 0 !important;
}
.fc-daygrid-day-top {
line-height: 18px;
flex-direction: row !important;
}
.fc .fc-button-primary {
background-color: $background-color;
color: #606266;
border-color: #DCDFE6;
}
.fc .fc-button-primary:not(:disabled).fc-button-active, .fc .fc-button-primary:not(:disabled):active {
color: #fff;
background-color: $tes-primary;
border-color: $tes-primary;
}
.fc .fc-button-primary:not(:disabled).fc-button-active:focus, .fc .fc-button-primary:not(:disabled):active:focus, .fc .fc-button-primary:focus {
box-shadow: none;
}
.fc-theme-standard td, .fc-theme-standard .fc-scrollgrid {
border: 1px solid #ebeef5;
}
.fc-theme-standard td:first-child {
border-left: 0;
border-right: 0;
}
.fc-theme-standard th {
border-bottom: 1px solid #ebeef5;
border-right: 0;
border-left: 0;
}
.fc .fc-button-primary:hover {
color: $tes-primary;
background-color: #fff;
border-color: #DCDFE6;
}
.fc .fc-button-primary:disabled {
color: #fff;
background-color: $tes-primary;
border-color: $tes-primary;
}
.fc-event-title {
line-height: 16px;
}
.fc .fc-highlight {
background: $tes-primary2;
}
// 周视图
.fc .fc-timegrid-body {
.fc-timegrid-slots {
table tbody tr:nth-of-type(odd) td {
border-bottom-color: transparent;
}
}
}
// 列视图
.fc-theme-standard .fc-list-day-cushion {
line-height: 20px;
background-color: #f5f7fa;
}
.fc .fc-list-table td {
line-height: 18px;
}
.fc-theme-standard .fc-list {
border: 1px solid #ebeef5;
}
.fc .fc-daygrid-day-number {
margin: 8px 0 0 8px;
color: #303133;
font-weight: 500;
}
.fc-scrollgrid-sync-inner {
color: #303133;
}
.fc .fc-daygrid-day.fc-day-today {
background-color: $tes-primary2;
.fc-daygrid-day-number {
background-color: $tes-primary;
color: #fff;
border-radius: 4px;
}
}
.fc .fc-col-header-cell-cushion {
padding: 0;
user-select: none;
}
.fc-timegrid-slot-label-cushion {
user-select: none;
}
.fc-event-title {
line-height: 20px;
}
.fc .fc-daygrid-event {
margin: 0 0 4px 0;
}
.fc-theme-standard .fc-popover-header {
background-color: #fff;
}
.fc .fc-popover {
z-index: 20;
}
.fc .fc-popover-header {
padding: 20px 20px 14px 20px;
border-radius: 10px 10px 0 0;
}
.fc .fc-popover-title {
color: $tes-primary;
font-size: 20px;
}
.fc .fc-more-popover .fc-popover-body {
padding: 0 20px 16px 20px;
border-radius: 0 0 10px 10px;
}
.fc-theme-standard th {
font-weight: 400;
}
.fc-theme-standard .fc-popover {
border-radius: 4px;
}
.fc-daygrid-day-bottom {
color: $tes-primary;
}
.fc .fc-timegrid-col.fc-day-today {
background-color: $tes-primary2;
}
.fc .fc-timegrid-slot {
height: 48px;
}
.fc .fc-timegrid-divider {
padding: 0;
}
.fc .fc-daygrid-body-natural .fc-daygrid-day-events {
margin-bottom: 3px;
}
.fc-event {
position: relative;
line-height: 20px;
padding: 0 0 0 8px;
&:after {
content: '';
width: 4px;
height: 100%;
border-radius: 2px 0 0 2px;
position: absolute;
top: 0;
left: 0;
}
.plan_title {
color: #303133;
max-width: 98%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
span {
margin-right: 6px;
}
}
.green.fc-event {
&:after {
background-color: #00B578;
}
span {
color: #00B578;
}
}
.red.fc-event {
&:after {
background-color: #FA5151;
}
span {
color: #FA5151;
}
}
.orange.fc-event {
&:after {
background-color: #FF8F1F;
}
span {
color: #FF8F1F;
}
}
.yellow.fc-event {
&:after {
background-color: #FFC300;
}
span {
color: #FFC300;
}
}
.cyan.fc-event {
&:after {
background-color: #07B9B9;
}
span {
color: #07B9B9;
}
}
.blue.fc-event {
&:after {
background-color: #3662EC;
}
span {
color: #3662EC;
}
}
.purple.fc-event {
&:after {
background-color: #8A38F5;
}
span {
color: #8A38F5;
}
}
.magenta.fc-event {
&:after {
background-color: #EB2F96;
}
span {
color: #EB2F96;
}
}
.fc-h-event {
border-color: transparent;
}
.fc-direction-ltr .fc-daygrid-event.fc-event-end, .fc-direction-rtl .fc-daygrid-event.fc-event-start {
margin-left: 4px;
margin-right: 4px;
}
.fc-event.fc-event-draggable {
overflow: hidden;
}
.fc-event.fc-daygrid-event {
transition: all 0.1s;
}
.fc-event.fc-daygrid-event.green {
background-color: rgba(0, 181, 120, 0.1);
&:hover {
background-color: rgba(0, 181, 120, 0.2);
}
}
.fc-event.fc-daygrid-event.red {
background-color: rgba(250, 81, 81, 0.1);
&:hover {
background-color: rgba(250, 81, 81, 0.2);
}
}
.fc-event.fc-daygrid-event.orange {
background-color: rgba(255, 143, 31, 0.1);
&:hover {
background-color: rgba(255, 143, 31, 0.2);
}
}
.fc-event.fc-daygrid-event.yellow {
background-color: rgba(255, 195, 0, 0.1);
&:hover {
background-color: rgba(255, 195, 0, 0.2);
}
}
.fc-event.fc-daygrid-event.cyan {
background-color: rgba(7, 185, 185, 0.1);
&:hover {
background-color: rgba(7, 185, 185, 0.2);
}
}
.fc-event.fc-daygrid-event.blue {
background-color: rgba(54, 98, 236, 0.1);
&:hover {
background-color: rgba(54, 98, 236, 0.2);
}
}
.fc-event.fc-daygrid-event.purple {
background-color: rgba(138, 56, 245, 0.1);
&:hover {
background-color: rgba(138, 56, 245, 0.2);
}
}
.fc-event.fc-event-draggable.magenta {
background-color: rgba(235, 47, 150, 0.1);
&:hover {
background-color: rgba(235, 47, 150, 0.2);
}
}
.fc-timegrid-event {
font-size: 14px;
}
.fc-event.fc-timegrid-event {
background-color: #fff;
border-radius: 0px 6px 6px 0px;
box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.2);
padding: 7px 7px 7px 12px;
height: 100%;
}
.fc-v-event {
border: 0;
}
}
以上为这块儿的主要部分,其中新增,删除,编辑的组件不算复杂,都是系统里面普通的组件,没什么特别的地方!
DrawerAddPlan 、DialogCategory(维护类目用的) 、DialogCalendar 为自定义封装的组件
formatDateFilter, formatDateDay, formatDate, formatCalendar, formatYM, getWeekNumber是为了方便显示和格式化时间写的一些方法。
考虑到很多人想直接拿来用,这里把这些东西都放上来
组件部分:
目录地址:
drawer-add-plan.vue(新增、编辑日程)
<template>
<el-drawer
v-model="showDrawer"
:title="titleJSON[type]"
direction="rtl"
:before-close="handleClose"
:size="744"
destroy-on-close
:close-on-click-modal="false"
custom-class="page-drawer"
@opened="handleOpened"
>
<div v-loading="drawerLoading">
<el-form ref="formRef" :model="formData" :rules="formRules" label-position="top" class="form-height-auto">
<el-row :gutter="5">
<el-col :span="18">
<el-form-item label="标题:" prop="title">
<el-input
ref="inputRef"
v-model="formData.title"
v-trim
maxlength="100"
placeholder="请输入标题"
@change="handleModify"
/>
</el-form-item>
</el-col>
<el-col :span="6" class="padding_form">
<el-form-item prop="planCategoryId">
<el-select
v-model="formData.planCategoryId"
placeholder="请选择计划类型"
style="width: 100%"
@change="handleModify"
>
<el-option-group v-for="item in categorySelectList" :key="item.label" :label="item.label">
<el-option
v-for="val in item.group"
:key="val.id"
:label="val.name"
:value="val.id"
:disabled="val.status !== 1"
>
<span style="float: left">{{ val.name }}</span>
<span v-if="val.status !== 1" class="fr" style="color: red; font-size: 10px">已停用</span>
</el-option>
</el-option-group>
</el-select>
</el-form-item>
</el-col>
<el-col v-if="formData.isAllDay === 1" :span="21">
<el-form-item label="计划时间:">
<el-date-picker
v-model="formData.dateArray"
type="daterange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
:shortcuts="shortcuts"
size="small"
style="width: 100%"
@change="handleChangeDate"
/>
</el-form-item>
</el-col>
<!-- 不勾选全天的显示 -->
<el-col v-else :span="21">
<el-row :gutter="4">
<el-col :span="6">
<el-form-item label="计划时间:" prop="startDate">
<el-date-picker
v-model="formData.startDate"
type="date"
placeholder="开始日期"
size="small"
style="width: 100%"
@change="
val => {
return handleFormat(val, 'startDate');
}
"
/>
</el-form-item>
</el-col>
<el-col :span="5" class="padding_form">
<el-form-item label="" prop="startDateMinute">
<el-time-select
v-model="formData.startDateMinute"
placeholder="开始"
start="00:00"
step="00:30"
end="23:30"
:max-time="formData.startDate === formData.endDate ? formData.endDateMinute : ''"
class="select-time"
style="width: 100%"
@change="handleModify"
/>
</el-form-item>
</el-col>
<el-col :span="2" class="padding_form text-center">
<el-form-item> 至 </el-form-item>
</el-col>
<el-col :span="6" class="padding_form">
<el-form-item prop="endDate">
<el-date-picker
v-model="formData.endDate"
type="date"
placeholder="结束日期"
size="small"
style="width: 100%"
@change="
val => {
return handleFormat(val, 'endDate');
}
"
/>
</el-form-item>
</el-col>
<el-col :span="5" class="padding_form">
<el-form-item prop="endDateMinute">
<el-time-select
v-model="formData.endDateMinute"
placeholder="结束"
start="00:00"
step="00:30"
end="23:30"
:min-time="formData.startDate === formData.endDate ? formData.startDateMinute : ''"
class="select-time"
style="width: 100%"
@change="handleModify"
/>
</el-form-item>
</el-col>
</el-row>
</el-col>
<el-col :span="3" class="padding_form">
<el-form-item prop="isAllDay">
<el-checkbox
v-model="formData.isAllDay"
label="全天"
size="small"
:true-label="1"
:false-label="0"
style="margin-left: 20px"
@change="handleChangeCheckbox"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="负责人/协作者:" props="managerId">
<el-select
v-model="formData.managerIds"
multiple
placeholder="请选择负责人/协作者"
style="width: 100%"
size="small"
@change="handleModify"
>
<el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-upload
class="upload-demo"
:file-list="formData.attachmentVoList"
:action="uploadAction"
:headers="headerconfig"
:on-remove="handleRemove"
:on-success="uploadSuccess"
:before-upload="beforeUpload"
:on-preview="handleClickFile"
>
<div class="title-group">
<el-button size="small" type="primary" @keyup.prevent @keydown.enter.prevent>上传附件</el-button>
<span class="tip">支持扩展名: .doc .docx .pdf .jpg ...</span>
</div>
</el-upload>
</el-col>
</el-row>
</el-form>
<div class="drawer-footer">
<el-button type="primary" :loading="drawerLoading" @click="onSubmit">保存</el-button>
<el-button :loading="drawerLoading" @click="handleClose">取消</el-button>
</div>
</div>
</el-drawer>
</template>
<script>
import { ref, watch, reactive, getCurrentInstance, toRefs } from 'vue';
import { formatDate, formatDateTime } from '@/utils/formatTime';
import { savePlanDetail, deleteFile } from '@/api/planManagement';
export default {
name: 'DrawerUnit',
components: {},
props: {
drawer: {
type: Boolean,
default: false
},
drawerType: {
type: String,
required: true
},
detailData: {
type: Object,
default: function () {
return {};
}
},
categoryList: {
type: Array,
default: function () {
return [];
}
}
},
emits: ['closeDrawer'],
setup(props, context) {
const { proxy } = getCurrentInstance();
// 抽屉事件
const showDrawer = ref(props.drawer);
const state = reactive({
isModified: false,
drawerLoading: false,
inputRef: ref(),
categorySelectList: [
{
label: '可选择',
group: []
},
{
label: '已停用',
group: []
}
],
type: '',
headerconfig: {},
titleJSON: {
add: '新增计划',
edit: '编辑计划'
},
userList: [],
options: {
1: '年',
2: '月'
},
shortcuts: [
{
text: '近三天',
value: (() => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 3);
return [start, end];
})()
},
{
text: '最近一周',
value: (() => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
return [start, end];
})()
},
{
text: '最近一个月',
value: (() => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
return [start, end];
})()
}
],
detailData: {}, // 详情数据
formRef: ref(),
uploadAction: '/api-document/document/attachment/upload', // 上传附件的接口
formData: {
dateArray: []
},
categoryProps: {
expandTrigger: 'hover',
checkStrictly: true,
children: 'children',
label: 'name',
value: 'id'
}
});
// 开始日期不能大于结束日期
function durationDate(rule, value, callback) {
let ruleValue = rule.value;
if (typeof ruleValue === 'function') {
ruleValue = rule.value();
} else if (typeof ruleValue === 'object') {
ruleValue = rule.value;
}
if (ruleValue.startDate && ruleValue.endDate) {
if (new Date(ruleValue.endDate).getTime() < new Date(ruleValue.startDate).getTime()) {
callback(new Error(rule.message));
} else {
callback();
}
} else {
callback();
}
}
const formRules = {
title: [{ required: true, message: '请输入使用标题', trigger: 'change' }],
planCategoryId: [{ required: true, message: '请选择计划类型', trigger: 'change' }],
startDate: [
{ required: true, message: '请选择开始日期', trigger: 'change' },
{
validator: durationDate,
value: () => ({
startDate: state.formData.startDate,
endDate: state.formData.endDate
}),
message: '不能大于结束时间',
trigger: 'change'
}
],
startDateMinute: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
endDate: [
{ required: true, message: '请选择结束日期', trigger: 'change' },
{
validator: durationDate,
value: () => ({
startDate: formatDate(state.formData.startDate),
endDate: formatDate(state.formData.endDate)
}),
message: '不能小于开始日期',
trigger: 'change'
}
],
endDateMinute: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
};
// 关闭抽屉
const handleClose = () => {
if (state.isModified) {
proxy
.$confirm('确认离开当前页面吗?离开后数据不可恢复', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
showClose: false,
closeOnClickModal: false,
closeOnPressEscape: false
})
.then(() => {
showDrawer.value = false;
state.isModified = false;
context.emit('closeDrawer', false);
})
.catch(() => {});
} else {
showDrawer.value = false;
context.emit('closeDrawer', false);
}
};
// 格式化日期
const handleChangeDate = val => {
if (val?.length) {
state.formData.startDate = formatDate(val[0]);
state.formData.endDate = formatDate(val[1]);
} else {
state.formData.startDate = '';
state.formData.endDate = '';
}
};
watch(props, newValue => {
showDrawer.value = newValue.drawer;
if (showDrawer.value) {
state.categorySelectList[0].group = [];
state.categorySelectList[1].group = [];
props.categoryList.forEach(item => {
if (item.status) {
state.categorySelectList[0].group.push(item);
} else {
state.categorySelectList[1].group.push(item);
}
});
state.isModified = false;
state.type = props.drawerType;
if (props.drawerType === 'add') {
initDetail(props.detailData);
} else {
getDetail(props.detailData);
}
}
});
const handleOpened = () => {
if (state.inputRef) {
state.inputRef.focus();
}
};
const handleModify = () => {
state.isModified = true;
};
const initDetail = newData => {
state.formData = {
...newData,
dateArray: [newData.startDate, newData.endDate],
planCategoryId: state.categorySelectList[0].group[0]?.id,
attachmentVoList: []
};
};
const getDetail = newData => {
const detailInfo = JSON.parse(JSON.stringify(newData));
detailInfo.attachmentVoList.forEach(item => {
item.name = item.fileName;
item.id = item.fileId;
});
detailInfo.fileIdList = detailInfo.attachmentVoList.filter(item => {
return item.fileId;
});
state.formData = {
...detailInfo,
dateArray: [detailInfo.startDate, detailInfo.endDate],
managerIds: detailInfo.managerId.split(',')
};
};
// 新增、编辑
const onSubmit = () => {
proxy.$refs['formRef'].validate(valid => {
if (valid) {
state.drawerLoading = true;
const params = {
...state.formData,
fileIdList: state.formData.attachmentVoList.map(item => {
return item.id;
}),
managerId: state.formData.managerIds.toString()
};
delete params.managerIds;
delete params.attachmentVoList;
delete params.dateArray;
savePlanDetail(params).then(res => {
state.drawerLoading = false;
if (res) {
state.isModified = false;
context.emit('closeDrawer', true);
proxy.$message.success('保存成功');
}
});
} else {
return false;
}
});
};
const uploadSuccess = (res, file, fileList) => {
if (res.code === 200) {
state.formData.attachmentVoList.push({ name: file.name, id: res.data[0] });
} else {
proxy.$message.error(res.message);
}
};
const beforeUpload = file => {
const fileSize = file.size / 1024 / 1024 < 20;
if (!fileSize) {
proxy.$message.error('上传附件大小不能超过20M');
return false;
} else if (file.size === 0) {
proxy.$message.error('上传附件大小不能为空');
return false;
} else {
return true;
}
};
const handleRemove = (file, fileList) => {
const fileId = file.id || file?.response?.data[0];
if (fileId) {
state.drawerLoading = true;
deleteFile(fileId).then(res => {
state.drawerLoading = false;
if (res) {
state.formData.attachmentVoList = state.formData.attachmentVoList.filter(item => {
return item.id !== fileId;
});
proxy.$message.success('附件删除成功!');
return true;
} else {
return false;
}
});
}
};
const getContent = value => {
state.formData.description = value;
};
// 切换全天和半天
const handleChangeCheckbox = val => {
if (val) {
if (state.formData.startDate) {
state.formData.dateArray = [state.formData.startDate, state.formData.endDate];
} else {
state.formData.dateArray = [];
}
state.formData.startDateMinute = '';
state.formData.endDateMinute = '';
} else {
state.formData.dateArray = [];
}
handleModify();
};
// 格式化日期
const handleFormat = (val, fieldName) => {
if (val) {
state.formData[fieldName] = formatDate(val);
}
handleModify();
};
// 点击附件
const handleClickFile = file => {
// console.log(file)
};
return {
...toRefs(state),
getDetail,
formRules,
handleOpened,
handleClickFile,
handleFormat,
handleChangeCheckbox,
handleRemove,
beforeUpload,
uploadSuccess,
getContent,
handleModify,
handleChangeDate,
onSubmit,
handleClose,
formatDate,
formatDateTime,
showDrawer
};
}
};
</script>
<style lang="scss" scoped>
:deep(.el-input--medium) {
line-height: 1;
}
.title-group {
display: flex;
align-items: center;
gap: 10px;
.tip {
line-height: 20px;
font-size: 12px;
color: $tes-font2;
}
}
.padding_form {
padding: 32px 0 0 0;
}
.select-time {
:deep(.el-icon-time) {
line-height: 31px;
}
}
:deep(.el-form .el-form-item .el-form-item__label) {
color: $tes-font;
}
.drawer-footer {
width: calc(100% - 80px);
background: #fff;
}
</style>
dialog-calendar.vue内容:
<template>
<el-dialog
v-model="dialogVisiable"
title="查看计划"
:modal="false"
:close-on-click-modal="true"
:width="500"
custom-class="calendar_plan_dialog"
@close="handleClose"
>
<div v-if="dialogVisiable" v-loading="dialogLoading">
<div :class="`fc-event dialog_header ${categoryJson[formData.planCategoryId]?.color}`">
<div class="dialog_title">
{{ formData.title || '--' }}
<div class="dialog_icon">
<i v-if="isCanEdit" class="el-icon-edit" @click="handleEdit()" />
<i class="el-icon-delete" @click="handleDelete()" />
</div>
</div>
</div>
<el-form
ref="formRef"
:model="formData"
:rules="ruleInfo"
label-position="left"
size="small"
label-width="110px"
class="my-form"
>
<el-row>
<el-col :span="24">
<el-form-item prop="managerId" label="负责人/协作者:">
<span>{{ formData.managerId}}</span>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item prop="planCategoryName" label="类型:">
<span>{{ formData.planCategoryName }}</span>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item prop="title" label="计划时间:">
<span
>{{ formData.startDate }} {{ formData.startDateMinute }} ~ {{ formData.endDate }}
{{ formData.endDateMinute }}</span
>
</el-form-item>
</el-col>
<el-divider />
<el-col :span="24">
<div class="form_item_header">
<div class="label">描述:</div>
<span v-if="formData.description" class="font-sm" @click="isShowAll = !isShowAll">{{
isShowAll ? '收起详情' : '展开详情'
}}</span>
</div>
<div
:class="`form_item_content ${isShowAll ? 'packDown' : 'packUp'} ${formData.description ? '' : 'empty'} ${
formData.attachmentVoList.length ? 'margin_Bot' : ''
}`"
v-html="formData.description ? formData.description : '暂无描述'"
/>
<div v-for="item in formData.attachmentVoList" :key="item.fileId" class="fileItem" @click="downLoad(item)">
{{ item.fileName }}
</div>
</el-col>
<el-divider />
<el-col :span="24">
<div class="form-item_flex">
<el-form-item prop="createTime" label="创建时间:" label-width="70px">
<span>{{ formatDateTime(formData.createTime) }}</span>
</el-form-item>
<el-form-item prop="createBy" label="创建人:" label-width="60px">
{{ formData.createBy }}
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
</div>
</el-dialog>
</template>
<script>
import { reactive, ref, toRefs, watch, getCurrentInstance } from 'vue';
import { formatDate, formatDateTime } from '@/utils/formatTime';
import { deletePlanDetail } from '@/api/planManagement';
export default {
name: 'DialogCalendar',
components: {},
props: {
dialogShow: {
type: Boolean,
default: false
},
detailInfo: {
type: Object,
default: () => {}
},
categoryJson: {
type: Object,
default: () => {}
}
},
emits: ['closeDialog'],
setup(props, context) {
const { proxy } = getCurrentInstance();
const state = reactive({
formData: {
attachmentVoList: []
},
formRef: ref(),
dialogTitle: '新增日程',
isShowAll: false,
type: '',
isAllDay: false, // 是否是全天
colorJSON: {
绿色: 'green',
红色: 'red',
橙色: 'orange',
黄色: 'yellow',
青色: 'cyan',
蓝色: 'blue',
紫色: 'purple',
品红色: 'magenta'
},
categoryJson: {},
nameList: [],
dialogLoading: false,
ruleInfo: {},
isCanEdit: false, // 是否可以编辑
differences: 0, // 开始时间结束时间相差天数
isEdit: false,
dialogVisiable: false,
ruleForm: ref(),
inputRef: ref(),
loading: false
});
watch(props, newValue => {
state.dialogVisiable = newValue.dialogShow;
if (state.dialogVisiable) {
state.categoryJson = props.categoryJson;
state.isCanEdit = Object.values(state.categoryJson).some(item => {
return item.status === 1;
});
state.formData = props.detailInfo;
}
});
// 下载附件
const downLoad = fileItem => {
window.open(fileItem.fileUrl);
};
// 删除计划
const handleDelete = () => {
proxy
.$confirm('是否确认删除', '删除确认', {
confirmButtonText: '确认删除',
cancelButtonText: '取消',
showCancelButton: true,
closeOnClickModal: false,
type: 'warning'
})
.then(() => {
state.dialogLoading = true;
deletePlanDetail(state.formData.id).then(function (res) {
state.dialogLoading = false;
if (res) {
proxy.$message.success('删除成功!');
state.dialogVisiable = false;
context.emit('closeDialog', { isRefresh: true });
}
});
})
.catch(() => {});
};
// 编辑计划
const handleEdit = () => {
state.dialogVisiable = false;
context.emit('closeDialog', { isRefresh: false, isEdit: true, info: state.formData });
};
// 关闭弹出窗
const handleClose = () => {
context.emit('closeDialog');
};
return {
...toRefs(state),
downLoad,
handleDelete,
handleEdit,
handleClose,
formatDate,
formatDateTime
};
}
};
</script>
<style lang="scss" scoped>
.fileItem {
display: inline-block;
cursor: pointer;
}
.fc-event {
background: #fff;
line-height: 22px;
font-size: 18px;
box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.2);
border-radius: 6px;
margin: 0 0 20px 0;
}
.form_item_content.packUp {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.form_item_content.packDown {
max-height: 15vh;
overflow-y: auto;
}
.form_item_content.margin_Bot {
margin-bottom: 16px;
}
.form_item_content.empty {
text-align: center;
color: $tes-font2;
}
.dialog_title {
display: flex;
justify-content: space-between;
padding: 12px 12px 12px 18px;
color: #606266;
align-items: center;
}
.dialog_icon {
display: flex;
gap: 6px;
cursor: pointer;
user-select: none;
color: $tes-font1;
i {
width: 24px;
height: 24px;
font-size: 14px;
padding: 5px;
border-radius: 3px;
&:hover {
background: $tes-border2;
}
}
}
.form_item_header {
line-height: 32px;
color: #909399;
.label {
display: inline-block;
}
span {
float: right;
cursor: pointer;
user-select: none;
&:hover {
color: $tes-primary;
}
}
}
.form-item_flex {
display: flex;
align-items: center;
gap: 20px;
}
.el-form .el-form-item {
margin: 0;
}
.el-divider--horizontal {
margin: 14px 0;
}
.el-dialog .el-dialog__body .el-form-item__label {
color: #909399;
}
</style>
<style lang="scss">
@import '../fullcalendar.scss';
.calendar_plan_dialog {
border-radius: 10px;
border: 1px solid #dcdfe6;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.15);
.el-dialog__header {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
background: #f0f2f5;
}
.el-dialog__body {
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
background: linear-gradient(180deg, #f0f2f5 34px, #fff 35px);
padding: 10px 20px 20px 20px !important;
.el-form-item__label {
color: #909399;
}
}
.form_item_content {
line-height: 20px;
p,
ul,
li {
margin: 0;
padding: 0;
}
}
}
</style>
一些格式化时间日期的函数
// 过滤年月日
export function formatDateFilter(time) {
let timeResult;
if (time.indexOf('日') > -1) {
timeResult = time.replace(/年/g, '-').replace(/月/g, '-').split('日')[0];
timeResult = formatDate(timeResult);
} else {
timeResult = time.replace(/年/g, '-').replace(/月/g, '');
timeResult = formatYM(timeResult);
}
return timeResult;
}
// 过滤只显示月日, date:只显示日期,month:只显示月份,不传全部显示
export function formatDateDay(time, type) {
var date = new Date();
if (time) {
date = new Date(time);
}
const month = date.getMonth() + 1;
const day = date.getDate();
// if ((date.getMonth() + 1) < 10) {
// month = '0' + month
// }
if (type === 'date') {
return day;
} else if (type === 'month') {
return month;
} else {
return month + '月' + day + '日';
}
}
// 过滤年月日,包括星期几
export function formatDate(time, num, text) {
var date = new Date();
var time_str = '';
var show_day = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
if (time) {
date = new Date(time);
} else {
return time;
}
let month = date.getMonth() + 1;
let day = date.getDate();
if (date.getMonth() + 1 < 10) {
month = '0' + month;
}
if (date.getDate() < 10) {
day = '0' + day;
}
if (num === 1) {
time_str = date.getFullYear() + '年' + month + '月' + day + '日 ' + show_day[date.getDay()];
} else {
if (text) {
time_str = date.getFullYear() + '年' + month + '月' + day + '日';
} else {
time_str = date.getFullYear() + '-' + month + '-' + day;
}
}
if (time_str === 'NaN-NaN-NaN') {
return '';
} else {
return time_str;
}
}
// 过滤显示几点几分
export function formatCalendar(time, type) {
let timeStr;
var date = formatDateTime(time);
if (type === 'hour') {
timeStr = date.split(' ')[1].split(':')[0] + ':' + date.split(' ')[1].split(':')[1];
} else {
timeStr = date.split(' ')[0];
}
return timeStr;
}
// 过滤年月
export function formatYM(time) {
var date = new Date();
if (time) {
date = new Date(time);
}
let month = date.getMonth() + 1;
if (date.getMonth() + 1 < 10) {
month = '0' + month;
}
return date.getFullYear() + '-' + month;
}
// 获取当前日期是一年的第几周
export function getWeekNumber(date) {
const nowDate = new Date(date);
const startOfYear = new Date(nowDate.getFullYear(), 0, 1);
const startOfWeek = new Date(startOfYear);
// 计算当前时间与一年的开始周的时间差,单位为毫秒
const diff = nowDate.getTime() - startOfWeek.getTime();
// 计算时间差对应的周数
const currentWeek = Math.ceil(diff / (7 * 24 * 60 * 60 * 1000));
return currentWeek;
}
GitHub 加速计划 / vu / vue
207.54 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
73486cb5
* chore: fix link broken
Signed-off-by: snoppy <michaleli@foxmail.com>
* Update packages/template-compiler/README.md [skip ci]
---------
Signed-off-by: snoppy <michaleli@foxmail.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 4 个月前
e428d891
Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
更多推荐
已为社区贡献3条内容
所有评论(0)