370 行
9.5 KiB
Plaintext
370 行
9.5 KiB
Plaintext
<template>
|
|
<!-- #ifdef APP -->
|
|
<scroll-view style="flex:1">
|
|
<!-- #endif -->
|
|
<view class="root">
|
|
<view class="date">
|
|
<text class="date-text">{{ current_month }}</text>
|
|
</view>
|
|
<view ref="drawHeaderRef" class="calendar-header"></view>
|
|
<view ref="drawWeeksRef" class="calendar-week" @click="select"></view>
|
|
<view class="btn-group">
|
|
<button size="mini" @click="preDate">上个月</button>
|
|
<button size="mini" @click="gotoToday">回到今天</button>
|
|
<button size="mini" @click="nextDate">下个月</button>
|
|
</view>
|
|
<view>{{ timeData.fullDate }} {{ current_day }}</view>
|
|
</view>
|
|
<!-- #ifdef APP -->
|
|
</scroll-view>
|
|
<!-- #endif -->
|
|
</template>
|
|
<script setup lang="uts">
|
|
import { Calendar, DateType } from './index.uts'
|
|
|
|
type CoordsType = {
|
|
x : number;
|
|
y : number;
|
|
width : number;
|
|
height : number;
|
|
data : DateType
|
|
}
|
|
|
|
// Template refs
|
|
const drawHeaderRef = ref<UniElement | null>(null)
|
|
const drawWeeksRef = ref<UniElement | null>(null)
|
|
|
|
const weeks = ref<Array<Array<DateType>>>([])
|
|
const $coords = ref<Array<CoordsType>>([])
|
|
const $calendar = ref(new Calendar() as Calendar)
|
|
const timeData = ref<DateType>({
|
|
fullDate: '',
|
|
year: 0,
|
|
month: 0,
|
|
date: 0,
|
|
day: 0,
|
|
lunar: '',
|
|
disabled: false,
|
|
is_today: false
|
|
} as DateType)
|
|
type DataType = {
|
|
testWidth: number,
|
|
}
|
|
|
|
const data = reactive({
|
|
testWidth: 0,
|
|
} as DataType)
|
|
|
|
// 获取月份
|
|
const current_month = computed((): string => {
|
|
const nowDate = timeData.value
|
|
const month = nowDate.month
|
|
return month < 10 ? '0' + month : month.toString()
|
|
})
|
|
|
|
const current_day = computed((): string => {
|
|
const time = timeData.value.data
|
|
if (time == null) {
|
|
return ''
|
|
}
|
|
return time.IMonthCn + time.IDayCn
|
|
})
|
|
|
|
// 绘制日历顶部信息
|
|
function drawHeader() {
|
|
const refs = drawHeaderRef.value
|
|
if (refs == null) return
|
|
let ctx = refs.getDrawableContext()
|
|
if (ctx == null) return
|
|
// 渲染之前如果有内容,需要清空
|
|
ctx.reset()
|
|
const date_header_map = ['一', '二', '三', '四', '五', '六', '日']
|
|
|
|
const width = refs.getBoundingClientRect().width
|
|
const num = date_header_map.length
|
|
const one_width = width / num
|
|
|
|
ctx.font = '12'
|
|
ctx.textAlign = 'center'
|
|
|
|
for (let i = 0; i < num; i++) {
|
|
let box_left = i * one_width + 2
|
|
let box_width = one_width - 4
|
|
let box_height = 26
|
|
|
|
// 文本赋值
|
|
const text = date_header_map[i]
|
|
let text_left = box_width / 2 + box_left
|
|
let text_top = box_height / 2 + 6
|
|
|
|
ctx.fillText(text, text_left, text_top)
|
|
}
|
|
ctx.update()
|
|
}
|
|
|
|
// 绘制日历主体
|
|
function drawWeek(weeks : Array<Array<DateType>>, time : string) {
|
|
const start_time = Date.now()
|
|
const refs = drawWeeksRef.value
|
|
if (refs == null) return
|
|
let ctx = refs.getDrawableContext()
|
|
if (ctx == null) return
|
|
const dom = refs.getBoundingClientRect()
|
|
const width = dom.width
|
|
const height = dom.height
|
|
let week_len = weeks.length
|
|
const one_width = width / weeks[0].length
|
|
const one_height = height / week_len
|
|
|
|
if (time != '') {
|
|
$coords.value = [] as Array<CoordsType>
|
|
ctx.reset()
|
|
}
|
|
|
|
ctx.textAlign = 'center'
|
|
|
|
for (let week = 0; week < week_len; week++) {
|
|
|
|
const week_item = weeks[week]
|
|
for (let day = 0; day < week_item.length; day++) {
|
|
const day_item = week_item[day]
|
|
let day_left = day * one_width + 2
|
|
let day_top = one_height * week + 2
|
|
let day_width = one_width - 4
|
|
let day_height = one_height - 4
|
|
|
|
// 文本赋值
|
|
let text = day_item.date.toString()
|
|
let text_left = day * one_width + (one_width / 2)
|
|
let text_top = one_height * week + 25
|
|
|
|
ctx.font = '16'
|
|
|
|
// 日期是否禁用
|
|
if (day_item.disabled) {
|
|
ctx.fillStyle = '#ccc'
|
|
} else {
|
|
// 是否为今天
|
|
if (day_item.is_today) {
|
|
ctx.fillStyle = 'red'
|
|
} // 是否为选中日期
|
|
else if (time == day_item.fullDate) {
|
|
ctx.fillStyle = 'blue';
|
|
}
|
|
// 默认颜色
|
|
else {
|
|
ctx.fillStyle = '#666';
|
|
}
|
|
|
|
// 第一次渲染获取数据
|
|
// if (time == '') {
|
|
// 存储坐标组,用于点击事件
|
|
const coords : CoordsType = {
|
|
x: day_left,
|
|
y: day_top,
|
|
width: day_width,
|
|
height: day_height,
|
|
data: day_item
|
|
}
|
|
|
|
// TODO 兼容安卓data内$开头的属性的赋值问题
|
|
let gridArr = $coords.value
|
|
gridArr.push(coords)
|
|
// }
|
|
}
|
|
|
|
ctx.fillText(text, text_left, text_top)
|
|
|
|
text = day_item.lunar
|
|
let lunar_left = day * one_width + (one_width / 2)
|
|
let lunar_top = one_height * week + 42
|
|
ctx.font = '10'
|
|
ctx.fillText(text, lunar_left, lunar_top)
|
|
}
|
|
}
|
|
|
|
ctx.update()
|
|
console.log('diff time', Date.now() - start_time);
|
|
}
|
|
|
|
function init() {
|
|
const calendar = $calendar.value
|
|
const refs = drawWeeksRef.value
|
|
if (refs != null) {
|
|
let ctx = refs.getDrawableContext()
|
|
if (ctx != null) {
|
|
ctx.reset()
|
|
}
|
|
}
|
|
|
|
// timeData.value = calendar.getDateInfo()
|
|
// 检查是否有fulltime ,来确定是否初次渲染
|
|
let fulltime = timeData.value.fullDate
|
|
if (fulltime == '') {
|
|
timeData.value = calendar.getDateInfo()
|
|
fulltime = timeData.value.fullDate
|
|
}
|
|
|
|
weeks.value = calendar.getWeeks(fulltime)
|
|
// 绘制日历头部
|
|
drawHeader()
|
|
drawWeek(weeks.value, fulltime)
|
|
}
|
|
|
|
// 点击具体的日历格子
|
|
function clickGrid(x : number, y : number) {
|
|
// 小格子数组
|
|
const calendar = $calendar.value
|
|
const gridArray = $coords.value
|
|
|
|
// 遍历小格子数组,找到最接近点击坐标的小格子
|
|
for (let i = 0; i < gridArray.length; i++) {
|
|
const grid = gridArray[i]
|
|
// 计算小格子理论上的最大值
|
|
const max_x = grid.x + grid.width
|
|
const max_y = grid.y + grid.height
|
|
|
|
const is_x_limit = grid.x < x && x < max_x
|
|
const is_y_limit = grid.y < y && y < max_y
|
|
|
|
const is_select = is_x_limit && is_y_limit
|
|
|
|
if (is_select) {
|
|
const data = grid.data
|
|
timeData.value = calendar.getDateInfo(data.fullDate)
|
|
drawWeek(weeks.value, grid.data.fullDate)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 触发整个日历的点击事件,需要计算点击位置
|
|
function select(event : UniPointerEvent) {
|
|
const refs = drawWeeksRef.value
|
|
if (refs == null) return
|
|
const rect = refs.getBoundingClientRect();
|
|
const dom_x = rect.left; // 元素左上角相对于视口的 X 坐标
|
|
const dom_y = rect.top; // 元素左上角相对于视口的 Y 坐标
|
|
// const touch = event.touches[0];
|
|
const clientX = event.clientX; // X 坐标
|
|
const clientY = event.clientY; // Y 坐标
|
|
// 计算点击的相对位置
|
|
const x = clientX - dom_x
|
|
const y = clientY - dom_y
|
|
|
|
clickGrid(x, y)
|
|
}
|
|
|
|
// 回到今天
|
|
function gotoToday() {
|
|
const calendar = $calendar.value
|
|
const time = calendar.getDate()
|
|
timeData.value = calendar.getDateInfo(time.fullDate)
|
|
weeks.value = calendar.getWeeks(time.fullDate)
|
|
|
|
// 重新绘制日历
|
|
drawWeek(weeks.value, time.fullDate)
|
|
}
|
|
|
|
// 判断是否为当前月份
|
|
function isCurrentMonth(year : number, month : number) : boolean {
|
|
const today = new Date();
|
|
return year === today.getFullYear() && month === today.getMonth() + 1;
|
|
}
|
|
|
|
// 切换上个月
|
|
function preDate() {
|
|
const fulldate = timeData.value.fullDate
|
|
const calendar = $calendar.value
|
|
let time = calendar.getDate(fulldate, -1, 'month')
|
|
const newDate = time.year + '-' + time.month + '-1';
|
|
time = calendar.getDate(newDate)
|
|
|
|
timeData.value = calendar.getDateInfo(time.fullDate)
|
|
weeks.value = calendar.getWeeks(time.fullDate)
|
|
|
|
// 判断是否回到当前月份
|
|
if (isCurrentMonth(time.year, time.month)) {
|
|
gotoToday();
|
|
} else {
|
|
// 否则正常绘制日历
|
|
drawWeek(weeks.value, time.fullDate)
|
|
}
|
|
}
|
|
|
|
// 切换下个他
|
|
function nextDate() {
|
|
const fulldate = timeData.value.fullDate
|
|
const calendar = $calendar.value
|
|
let time = calendar.getDate(fulldate, 1, 'month')
|
|
|
|
const newDate = time.year + '-' + time.month + '-1';
|
|
time = calendar.getDate(newDate)
|
|
|
|
timeData.value = calendar.getDateInfo(time.fullDate)
|
|
weeks.value = calendar.getWeeks(time.fullDate)
|
|
|
|
// 判断是否回到当前月份
|
|
if (isCurrentMonth(time.year, time.month)) {
|
|
gotoToday();
|
|
} else {
|
|
// 否则正常绘制日历
|
|
drawWeek(weeks.value, time.fullDate)
|
|
}
|
|
}
|
|
|
|
onResize(() => {
|
|
// 屏幕发生变化,初始化日历
|
|
init()
|
|
})
|
|
|
|
onReady(() => {
|
|
init()
|
|
|
|
// 仅自动化测试
|
|
const header = drawHeaderRef.value
|
|
if (header != null) {
|
|
data.testWidth = header.getBoundingClientRect().width;
|
|
}
|
|
})
|
|
|
|
defineExpose({
|
|
data
|
|
})
|
|
</script>
|
|
|
|
<style>
|
|
.root {
|
|
/* flex: 1; */
|
|
position: relative;
|
|
padding: 15px;
|
|
background-color: #fff;
|
|
}
|
|
|
|
.calendar-header {
|
|
height: 30px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.date {
|
|
margin-bottom: 10px;
|
|
margin-left: 10px;
|
|
}
|
|
|
|
.date-text {
|
|
font-size: 34px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.calendar-week {
|
|
height: 350px;
|
|
margin: 2px 0;
|
|
}
|
|
|
|
.btn-group {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
margin: 20px 0;
|
|
}
|
|
</style>
|