diff --git a/project.config.json b/project.config.json index 5634447..4a3111e 100644 --- a/project.config.json +++ b/project.config.json @@ -71,5 +71,5 @@ } ] }, - "appid": "wx71ac9c27c3c3e3f4" + "appid": "wxf9ce8010f1ad24aa" } \ No newline at end of file diff --git a/src/app.json b/src/app.json index d1cc702..7a4fe4f 100644 --- a/src/app.json +++ b/src/app.json @@ -71,6 +71,31 @@ ] }, { + "root": "gift", + "pages": [ + "pages/giftList/index", + "pages/giftDetail/index", + "pages/conformOrder/index", + "pages/siteList/index", + "pages/siteEdit/index", + "pages/orderEnd/index", + "pages/orderDetail/index", + "pages/myGift/index", + "pages/priceDetail/index", + "pages/scoreRule/index", + "pages/dtpDurg/index", + "pages/vipCert/index", + "pages/vipReject/index", + "pages/vipPending/index", + "pages/vipStartPending/index", + "pages/myHealthRecord/index", + "pages/myHealthRecordChart/index", + "pages/cutaneous/index", + "pages/cutaneousDetail/index", + "pages/cutaneousVideo/index" + ] + }, + { "root": "doc", "pages": ["pages/doc1/index"] } diff --git a/src/app.ts b/src/app.ts index 04e8bd6..37b6a7b 100644 --- a/src/app.ts +++ b/src/app.ts @@ -16,25 +16,72 @@ App({ // 测试号 wx2b0bb13edf717c1d // dev // appid:wxf9ce8010f1ad24aa - // url: 'https://m.xd.hbraas.com', - // upFileUrl: 'https://m.xd.hbraas.com/', - // imageUrl: 'https://m.xd.hbraas.com/xd/', + url: 'https://m.xd.hbraas.com', + upFileUrl: 'https://m.xd.hbraas.com/', + imageUrl: 'https://m.xd.hbraas.com/xd/', // pro // appid:wx71ac9c27c3c3e3f4 - url: 'https://m.xd.hbsaas.com', - upFileUrl: 'https://m.xd.hbsaas.com/', - imageUrl: 'https://m.xd.hbsaas.com/api/xd/', + // url: 'https://m.xd.hbsaas.com', + // upFileUrl: 'https://m.xd.hbsaas.com/', + // imageUrl: 'https://m.xd.hbsaas.com/api/xd/', loginState: '', isLogin: 0, + isNewReg: 0, loginType: 0, - scene: null, + scene: {}, backPath: '', zdUserInfo: {}, + DiagnoseType: [ + { + id: 1, + name: 'Ⅰ型 眼肌无力,可伴闭眼无力,其它肌群肌力正常', + }, + { + id: 2, + name: 'Ⅱa 型 主要累及四肢肌或(和)躯干肌,可有较轻的咽喉肌受累', + }, + { + id: 3, + name: 'Ⅱb 型 主要累及咽喉肌或(和)呼吸肌,可有轻度或相同的四肢肌或(和)躯干肌受累', + }, + { + id: 4, + name: 'Ⅲa 型 主要累及四肢肌或(和)躯干肌,可有较轻的咽喉肌受累', + }, + { + id: 5, + name: 'Ⅲb 型 主要累及咽喉肌或(和)呼吸肌,可有轻度或相同的四肢肌或(和)躯干肌受累', + }, + { + id: 6, + name: 'Ⅳa 型 主要累及四肢肌或(和)躯干肌受累,可有较轻的咽喉肌受累', + }, + { + id: 7, + name: 'Ⅳb 型 主要累及咽喉肌或(和)呼吸肌,可有轻度或相同的四肢肌或(和)躯干肌受累', + }, + { + id: 8, + name: 'Ⅴ型 气管插管,伴或不伴机械通气(除外术后常规使用);仅鼻饲而不进行气管插管的病例为Ⅳb 型', + }, + { + id: 9, + name: '其它', + }, + ], + storyStatus: { + 1: '已提交待审核', + 2: '审核未通过', + 3: '审核通过待校正', + 4: '校正完成', + 5: '已发布', + 100: '审核未通过', + }, }, onLaunch() { Page = page as WechatMiniprogram.Page.Constructor; @@ -53,6 +100,7 @@ App({ }).then((res: any) => { this.globalData.loginState = res.loginState; this.globalData.isLogin = res.isLogin; + this.globalData.isNewReg = res.isNewReg; this.globalData.loginType = res.loginType; }); }, @@ -68,7 +116,21 @@ App({ this.globalData.scene = parseScene(options.query.scene); } }, - waitLogin({ type = 0 } = { type: 'any' }) { + updateLoginInfo(callback?: (any) => void) { + wx.ajax({ + method: 'GET', + url: '?r=xd/user/get-account-info', + data: {}, + }).then((res) => { + this.globalData.isLogin = res.isLogin; + this.globalData.loginType = res.loginType; + this.globalData.isNewReg = res.isNewReg; + if (callback) { + callback(res); + } + }); + }, + waitLogin({ type = [0] } = { type: 'any' }) { return new Promise((resolve) => { const checkLogin = () => { if (this.globalData.loginState) { @@ -84,25 +146,41 @@ App({ checkLogin(); }); }, - checkLoginType(type: 0 | 1 | 2 | 'any') { - const { loginType, isLogin } = this.globalData; + checkLoginType(type) { + const { loginType, isLogin, isNewReg } = this.globalData; if (type === 'any') { return true; } if (isLogin !== 1) { - if (type === 0) { + if (type.includes(0) && loginType === 0) { return true; } wx.reLaunch({ - url: '/pages/login/index', + url: '/patient/pages/login/index', }); return false; } - if (loginType !== 2) { + + if (isNewReg !== 1) { + const typePageUrl = { + 1: '/patient/pages/entryInfo/index', + 2: '/pages/login/index', + }[loginType as 1 | 2]; + wx.reLaunch({ + url: typePageUrl, + }); + return false; + } + + if (!type.includes(loginType)) { + const typePageUrl = { + 1: '/patient/pages/index/index', + 2: '/pages/index/index', + }[loginType as 1 | 2]; wx.reLaunch({ - url: '/pages/login/index', + url: typePageUrl, }); return false; } @@ -136,7 +214,9 @@ App({ }, // zd相关函数 zdMpBehavior(data: { PageName: string; doctor?: boolean }) { - let url = '?r=xd/mp-behavior/add'; + const { loginType } = this.globalData; + if (loginType === 0) return; + let url = '?r=zd/mp-behavior/add'; if (data.doctor) { url = '?r=zd/doctor/mp-behavior/add'; } diff --git a/src/components/calendar/core.js b/src/components/calendar/core.js new file mode 100644 index 0000000..f79b6a2 --- /dev/null +++ b/src/components/calendar/core.js @@ -0,0 +1,144 @@ +import { dateUtil, getCalendarConfig } from './utils/index' + +/** + * 计算当前月份前后两月应占的格子 + * @param {number} year 年份 + * @param {number} month 月份 + */ +function calculateEmptyGrids(year, month, config) { + const prevMonthGrids = calculatePrevMonthGrids(year, month, config) + const nextMonthGrids = calculateNextMonthGrids(year, month, config) + return { + prevMonthGrids, + nextMonthGrids + } +} + +/** + * 计算上月应占的格子 + * @param {number} year 年份 + * @param {number} month 月份 + */ +function calculatePrevMonthGrids(year, month, config) { + let emptyGrids = [] + const prevMonthDays = dateUtil.getDatesCountOfMonth(year, month - 1) + let firstDayOfWeek = dateUtil.firstDayOfWeek(year, month) + if (config.firstDayOfWeek === 'Mon') { + if (firstDayOfWeek === 0) { + firstDayOfWeek = 6 + } else { + firstDayOfWeek -= 1 + } + } + if (firstDayOfWeek > 0) { + const len = prevMonthDays - firstDayOfWeek + const { onlyShowCurrentMonth } = config + const YMInfo = dateUtil.getPrevMonthInfo({ year, month }) + for (let i = prevMonthDays; i > len; i--) { + if (onlyShowCurrentMonth) { + emptyGrids.push('') + } else { + const week = dateUtil.getDayOfWeek(+year, +month, i) + emptyGrids.push({ + ...YMInfo, + date: i, + week + }) + } + } + emptyGrids.reverse() + } + return emptyGrids +} +/** + * 计算下一月日期是否需要多展示的日期 + * 某些月份日期为5排,某些月份6排,统一为6排 + * @param {number} year + * @param {number} month + * @param {object} config + */ +function calculateExtraEmptyDate(year, month, config) { + let extDate = 0 + if (+month === 2) { + extDate += 7 + let firstDayofMonth = dateUtil.getDayOfWeek(year, month, 1) + if (config.firstDayOfWeek === 'Mon') { + if (+firstDayofMonth === 1) extDate += 7 + } else { + if (+firstDayofMonth === 0) extDate += 7 + } + } else { + let firstDayofMonth = dateUtil.getDayOfWeek(year, month, 1) + if (config.firstDayOfWeek === 'Mon') { + if (firstDayofMonth !== 0 && firstDayofMonth < 6) { + extDate += 7 + } + } else { + if (firstDayofMonth <= 5) { + extDate += 7 + } + } + } + return extDate +} +/** + * 计算下月应占的格子 + * @param {number} year 年份 + * @param {number} month 月份 + */ +function calculateNextMonthGrids(year, month, config) { + let emptyGrids = [] + const datesCount = dateUtil.getDatesCountOfMonth(year, month) + let lastDayWeek = dateUtil.getDayOfWeek(year, month, datesCount) + if (config.firstDayOfWeek === 'Mon') { + if (lastDayWeek === 0) { + lastDayWeek = 6 + } else { + lastDayWeek -= 1 + } + } + let len = 7 - (lastDayWeek + 1) + const { onlyShowCurrentMonth } = config + if (!onlyShowCurrentMonth) { + len = len + calculateExtraEmptyDate(year, month, config) + } + const YMInfo = dateUtil.getNextMonthInfo({ year, month }) + for (let i = 1; i <= len; i++) { + const week = dateUtil.getDayOfWeek(+year, +month, i) + if (onlyShowCurrentMonth) { + emptyGrids.push('') + } else { + emptyGrids.push({ + id: i - 1, + ...YMInfo, + date: i, + week: week || 7 + }) + } + } + return emptyGrids +} +/** + * 设置日历面板数据 + * @param {number} year 年份 + * @param {number} month 月份 + * @param {number} curDate 日期 + */ +function calculateCurrentMonthDates(year, month) { + return dateUtil.calcDates(year, month) +} + +export function calcJumpData({ dateInfo, config, component }) { + dateInfo = dateInfo || dateUtil.todayFMD() + const { year, month, date } = dateInfo + const calendarConfig = config || getCalendarConfig(component) + const emptyGrids = calculateEmptyGrids(year, month, calendarConfig) + const calendar = { + curYear: year, + curMonth: month, + curDate: date, + dates: calculateCurrentMonthDates(year, month), + ...emptyGrids + } + return calendar +} diff --git a/src/components/calendar/helper.js b/src/components/calendar/helper.js new file mode 100644 index 0000000..ada887b --- /dev/null +++ b/src/components/calendar/helper.js @@ -0,0 +1,12 @@ +import { dateUtil } from './utils/index' + +export function calcTargetYMInfo() { + return { + right: dateUtil.getPrevMonthInfo, + left: dateUtil.getNextMonthInfo, + prev_month: dateUtil.getPrevMonthInfo, + next_month: dateUtil.getNextMonthInfo, + prev_year: dateUtil.getPrevYearInfo, + next_year: dateUtil.getNextYearInfo + } +} diff --git a/src/components/calendar/index.d.ts b/src/components/calendar/index.d.ts new file mode 100644 index 0000000..509db18 --- /dev/null +++ b/src/components/calendar/index.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/components/calendar/index.js b/src/components/calendar/index.js new file mode 100644 index 0000000..7781ce1 --- /dev/null +++ b/src/components/calendar/index.js @@ -0,0 +1,258 @@ +import plugins from './plugins/index' +import { calcJumpData } from './core' +import { renderCalendar } from './render' +import { calcTargetYMInfo } from './helper' +import { dateUtil, calendarGesture, logger } from './utils/index' + +Component({ + options: { + styleIsolation: 'apply-shared', + multipleSlots: true // 在组件定义时的选项中启用多slot支持 + }, + properties: { + config: { + type: Object, + value: {} + } + }, + lifetimes: { + attached: function() { + this.initComp() + } + }, + methods: { + initComp() { + const calendarConfig = this.setDefaultDisableDate() + this.setConfig(calendarConfig) + }, + // 禁用某天日期配置默认为今天 + setDefaultDisableDate() { + const calendarConfig = this.properties.config || {} + if (calendarConfig.disableMode && !calendarConfig.disableMode.date) { + calendarConfig.disableMode.date = dateUtil.toTimeStr( + dateUtil.todayFMD() + ) + } + return calendarConfig + }, + initCalendar(config) { + const { defaultDate } = config + let date = dateUtil.todayFMD() + if (defaultDate && typeof defaultDate === 'string') { + const dateInfo = defaultDate.split('-') + if (dateInfo.length < 3) { + return logger.warn('defaultDate配置格式应为: 2018-4-2 或 2018-04-02') + } else { + date = { + year: +dateInfo[0], + month: +dateInfo[1], + date: +dateInfo[2] + } + } + } + const waitRenderData = calcJumpData({ + dateInfo: date, + config + }) + const timestamp = dateUtil.todayTimestamp() + if (config.autoChoosedWhenJump) { + const target = waitRenderData.dates.filter( + item => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(date) + ) + if (target && target.length) { + if (!waitRenderData.selectedDates) { + waitRenderData.selectedDates = target + } else { + waitRenderData.selectedDates.push(target[0]) + } + } + } + return { + ...waitRenderData, + todayTimestamp: timestamp, + weeksCh: dateUtil.getWeekHeader(config.firstDayOfWeek) + } + }, + setConfig(config) { + if (config.markToday && typeof config.markToday === 'string') { + config.highlightToday = true + } + config.theme = config.theme || 'default' + this.setData( + { + config + }, + () => { + for (let plugin of plugins.installed) { + const [, p] = plugin + if (typeof p.install === 'function') { + p.install(this) + } + if (typeof p.methods === 'function') { + const methods = p.methods(this) + for (let fnName in methods) { + if (fnName.startsWith('__')) continue + const fn = methods[fnName] + if (typeof fn === 'function') { + if (!this.calendar) this.calendar = {} + this.calendar[fnName] = fn + } + } + } + } + const initData = this.initCalendar(config) + renderCalendar.call(this, initData, config) + } + ) + }, + tapDate(e) { + const { info } = e.currentTarget.dataset + const { date, disable } = info || {} + if (disable || !date) return + const { calendar, config } = this.data + let calendarData = calendar + let calendarConfig = config + if (config.takeoverTap) { + return this.triggerEvent('takeoverTap', info) + } + for (let plugin of plugins.installed) { + const [, p] = plugin + if (typeof p.onTapDate === 'function') { + const { + calendarData: __calendarData, + calendarConfig: __calendarConfig + } = p.onTapDate(info, calendarData, calendarConfig) + calendarData = __calendarData + calendarConfig = __calendarConfig + } + } + renderCalendar.call(this, calendarData, calendarConfig).then(() => { + this.triggerEvent('afterTapDate', info) + }) + }, + /** + * 日历滑动开始 + * @param {object} e + */ + calendarTouchstart(e) { + const t = e.touches[0] + const startX = t.clientX + const startY = t.clientY + this.swipeLock = true + this.setData({ + 'gesture.startX': startX, + 'gesture.startY': startY + }) + }, + /** + * 日历滑动中 + * @param {object} e + */ + calendarTouchmove(e) { + const { gesture } = this.data + const { preventSwipe } = this.properties.config + if (!this.swipeLock || preventSwipe) return + if (calendarGesture.isLeft(gesture, e.touches[0])) { + this.handleSwipe('left') + this.swipeLock = false + } + if (calendarGesture.isRight(gesture, e.touches[0])) { + this.handleSwipe('right') + this.swipeLock = false + } + }, + calendarTouchend(e) { + this.setData({ + 'calendar.leftSwipe': 0, + 'calendar.rightSwipe': 0 + }) + }, + handleSwipe(direction) { + let swipeKey = 'calendar.leftSwipe' + if (direction === 'right') { + swipeKey = 'calendar.rightSwipe' + } + this.setData({ + [swipeKey]: 1 + }) + const { calendar } = this.data + let calendarData = calendar + const { curYear, curMonth } = calendarData + const getMonthInfo = calcTargetYMInfo()[direction] + const target = getMonthInfo({ + year: +curYear, + month: +curMonth + }) + target.direction = direction + this.renderCalendar(target) + }, + changeDate(e) { + const { type } = e.currentTarget.dataset + const { calendar: calendarData } = this.data + const { curYear, curMonth } = calendarData + const getMonthInfo = calcTargetYMInfo()[type] + const target = getMonthInfo({ + year: +curYear, + month: +curMonth + }) + target.direction = type + this.renderCalendar(target) + }, + renderCalendar(target) { + let { calendar: calendarData, config } = this.data + const { curYear, curMonth } = calendarData || {} + for (let plugin of plugins.installed) { + const [, p] = plugin + if (typeof p.onSwitchCalendar === 'function') { + calendarData = p.onSwitchCalendar(target, calendarData, this) + } + } + return renderCalendar.call(this, calendarData, config).then(() => { + let triggerEventName = 'whenChangeMonth' + if (config.weekMode) { + triggerEventName = 'whenChangeWeek' + } + this.triggerEvent(triggerEventName, { + current: { + year: +curYear, + month: +curMonth + }, + next: target + }) + this.triggerEvent('onSwipe', { + current: { + year: +curYear, + month: +curMonth + }, + next: target, + type: triggerEventName + }) + }) + }, + doubleClickJumpToToday() { + const { multi, weekMode } = this.calendar.getCalendarConfig() || {} + if (multi || weekMode) return + if (this.count === undefined) { + this.count = 1 + } else { + this.count += 1 + } + if (this.lastClick) { + const difference = new Date().getTime() - this.lastClick + if ( + difference < 500 && + this.count >= 2 && + typeof this.calendar.jump === 'function' + ) { + const today = dateUtil.todayFMD() + this.calendar.jump(today) + } + this.count = undefined + this.lastClick = undefined + } else { + this.lastClick = new Date().getTime() + } + this.triggerEvent('jumpToToday') + } + } +}) diff --git a/src/components/calendar/index.json b/src/components/calendar/index.json new file mode 100644 index 0000000..1edff93 --- /dev/null +++ b/src/components/calendar/index.json @@ -0,0 +1,3 @@ +{ + "component": true +} \ No newline at end of file diff --git a/src/components/calendar/index.wxml b/src/components/calendar/index.wxml new file mode 100644 index 0000000..7bbabf9 --- /dev/null +++ b/src/components/calendar/index.wxml @@ -0,0 +1,116 @@ + + + + + + + + + {{calendar.curYear || "--"}} 年 {{calendar.curMonth || "--"}} 月 + + + + + + + + + {{item}} + + + + + + + + + {{item.date}} + + + + + + + + {{config.markToday && item.isToday ? config.markToday : item.date}} + + {{item.label || item.lunar.Term || item.lunar.IDayCn}} + + + + {{item.todoText}} + + + + + + + + + + {{item.date}} + + + + + + diff --git a/src/components/calendar/index.wxss b/src/components/calendar/index.wxss new file mode 100644 index 0000000..f23814c --- /dev/null +++ b/src/components/calendar/index.wxss @@ -0,0 +1,247 @@ +@import "./theme/iconfont.wxss"; +@import "./theme/theme-default.wxss"; +@import "./theme/theme-elegant.wxss"; +@import "./theme/theme-nuohe.wxss"; + +.blod { + font-weight: bold; +} + +.b { + display: flex; +} + +.lr { + flex-direction: row; +} + +.tb { + flex-direction: column; +} + +.pc { + justify-content: center; +} + +.ac { + align-items: center; +} + +.cc { + align-items: center; + justify-content: center; +} + +.wrap { + flex-wrap: wrap; +} + +.flex { + flex-grow: 1; +} + +.bg { + background-image: linear-gradient(to bottom, #faefe7, #ffcbd7); + overflow: hidden; +} + +.white-color { + color: #fff; +} + +.fs24 { + font-size: 24rpx; +} + +.fs28 { + font-size: 28rpx; +} + +.fs32 { + font-size: 32rpx; +} + +.fs36 { + font-size: 36rpx; +} + +.calendar { + width: 100%; + box-sizing: border-box; +} + +/* 日历操作栏 */ + +.handle { + height: 80rpx; +} + +.prev-handle, +.next-handle { + padding: 20rpx; +} + +.date-in-handle { + height: 80rpx; +} + +/* 星期栏 */ + +.weeks { + height: 50rpx; + line-height: 50rpx; + opacity: 0.5; +} + +.week { + text-align: center; +} + +.grid, +.week { + width: 14.286014285714286%; +} + +.date-wrap { + width: 100%; + height: 80rpx; + position: relative; + left: 0; + top: 0; +} + +.date { + position: relative; + left: 0; + top: 0; + width: 55rpx; + height: 55rpx; + text-align: center; + line-height: 55rpx; + font-size: 28rpx; + font-weight: 200; + border-radius: 50%; + transition: all 0.3s; + font-weight: bold; + + animation-name: choosed; + + animation-duration: 0.5s; + + animation-timing-function: linear; + + animation-iteration-count: 1; +} + +.date-area-mode { + width: 100%; + + border-radius: 0; +} + +.date-desc { + width: 150%; + + height: 32rpx; + + font-size: 20rpx; + + line-height: 32rpx; + + position: absolute; + + left: 50%; + + transform: translateX(-50%); + + overflow: hidden; + + word-break: break-all; + + text-overflow: ellipsis; + + white-space: nowrap; + + -webkit-line-clamp: 1; + + text-align: center; +} + +@keyframes choosed { + from { + transform: scale(1); + } + + 50% { + transform: scale(0.9); + } + + to { + transform: scale(1); + } +} + +/* 日期圆圈标记 */ + +.todo-circle { + border-width: 1rpx; + + border-style: solid; + + box-sizing: border-box; +} + +/* 待办点标记相关样式 */ + +.todo-dot { + width: 16rpx; + + height: 16rpx; + + border-radius: 50%; + + position: absolute; + + left: 50%; + bottom: -10rpx !important; + + transform: translateX(-50%); +} + +.todo-dot-top { + top: 3rpx; +} + +.todo-dot.todo-dot-top-always { + top: -8rpx; +} + +.todo-dot.todo-dot-bottom { + bottom: 0; +} + +.todo-dot.todo-dot-bottom-always { + bottom: -10rpx; +} + +/* 日期描述文字(待办文字/农历)相关样式 */ + +.date-desc.date-desc-top { + top: -6rpx; +} + +.date-desc.date-desc-top-always { + top: -20rpx; +} + +.date-desc.date-desc-bottom { + bottom: -14rpx; +} + +.todo-circle .date-desc.date-desc-bottom { + bottom: -30rpx; +} + +.date-desc.date-desc-bottom-always { + bottom: -28rpx; +} diff --git a/src/components/calendar/plugins/holidays/holidays-map.js b/src/components/calendar/plugins/holidays/holidays-map.js new file mode 100644 index 0000000..1472f87 --- /dev/null +++ b/src/components/calendar/plugins/holidays/holidays-map.js @@ -0,0 +1,212 @@ +/* * + @Author: drfu* + @Description: 数据来源于国务院办公厅关于2020年部分节假日安排的通知(国办发明电〔2019〕16号)_政府信息公开专栏,http://www.gov.cn/zhengce/content/2019-11/21/content_5454164.htm + @Date: 2020-10-12 14:29:45* + * @Last Modified by: drfu + * @Last Modified time: 2020-10-16 17:38:08 +*/ + +// 节日列表 +export const festival = { + // 农历固定日期节日 + lunar: { + 1: { + 1: { + type: 'festival', + name: '春节', + label: '春节' + }, + 8: { + type: 'festival', + name: '腊八节', + label: '腊八' + }, + 15: { + type: 'festival', + name: '元宵节', + label: '元宵' + } + }, + 7: { + 7: { + type: 'festival', + name: '七夕节', + label: '七夕' + }, + 15: { + type: 'festival', + name: '中元节', + label: '中元节' + } + }, + 9: { + 9: { + type: 'festival', + name: '重阳节', + label: '重阳节' + } + } + }, + // 阳历固定日期节日 + solar: { + 2: { + 14: { + type: 'festival', + name: '情人节', + label: '情人节' + } + }, + 3: { + 12: { + type: 'festival', + name: '植树节', + label: '植树节' + } + }, + 4: { + 1: { + type: 'festival', + name: '愚人节', + label: '愚人节' + }, + 5: { + type: 'festival', + name: '清明节', + label: '清明节' + } + }, + 5: { + 1: { + type: 'festival', + name: '劳动节', + label: '劳动节' + } + }, + 6: { + 1: { + type: 'festival', + name: '儿童节', + label: '儿童节' + } + }, + 7: { + 1: { + type: 'festival', + name: '建党节', + label: '建党节' + } + }, + 8: { + 1: { + type: 'festival', + name: '建军节', + label: '建军节' + } + }, + 9: { + 10: { + type: 'festival', + name: '教师节', + label: '教师节' + } + }, + 10: { + 1: { + type: 'festival', + name: '国庆节', + label: '国庆节' + } + }, + 12: { + 25: { + type: 'festival', + name: '圣诞节', + label: '圣诞节' + } + } + } +} + +export const holidays = { + 2020: { + 1: { + 1: { + type: 'holiday', + name: '元旦', + label: '休' + }, + 19: { + type: 'work', + name: '调班', + label: '班' + }, + '24-30': { + type: 'holiday', + name: '春节', + label: '休' + } + }, + 2: { + 1: { + type: 'work', + name: '调班', + label: '班' + } + }, + 4: { + '4-6': { + type: 'holiday', + name: '清明节', + label: '休' + }, + 26: { + type: 'work', + name: '调班', + label: '班' + } + }, + 5: { + '1-5': { + type: 'holiday', + name: '劳动节', + label: '休' + }, + 9: { + type: 'work', + name: '调班', + label: '班' + } + }, + 6: { + '25-27': { + type: 'holiday', + name: '端午节', + label: '休' + }, + 28: { + type: 'work', + name: '调班', + label: '班' + } + }, + 9: { + 27: { + type: 'work', + name: '调班', + label: '班' + } + }, + 10: { + '1-8': { + type: 'holiday', + name: '国庆节/中秋节', + label: '休' + }, + 10: { + type: 'work', + name: '调班', + label: '班' + } + } + } +} diff --git a/src/components/calendar/plugins/holidays/index.d.ts b/src/components/calendar/plugins/holidays/index.d.ts new file mode 100644 index 0000000..509db18 --- /dev/null +++ b/src/components/calendar/plugins/holidays/index.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/components/calendar/plugins/holidays/index.js b/src/components/calendar/plugins/holidays/index.js new file mode 100644 index 0000000..36b8eeb --- /dev/null +++ b/src/components/calendar/plugins/holidays/index.js @@ -0,0 +1,201 @@ +/* * + @Author: drfu* + @Description: 显示法定节假日班/休情况 + @Date: 2020-10-12 14:29:45* + * @Last Modified by: drfu + * @Last Modified time: 2020-10-16 17:34:13 +*/ + +import { holidays, festival } from './holidays-map' +import { dateUtil, getCalendarData, logger } from '../../utils/index' + +/** + * 当前是否在休假期内 + * @param {object} { year, month } + * @param {object} { start, end, current } + * @returns + */ +function inHolidays({ year, month }, { start, end, current }) { + const getTimeStamp = dateUtil.getTimeStamp + const startTimestamp = getTimeStamp({ + year, + month, + date: start + }) + const endTimestamp = getTimeStamp({ + year, + month, + date: end + }) + const currentDateTimestamp = getTimeStamp({ + year, + month, + date: current + }) + if ( + currentDateTimestamp >= startTimestamp && + currentDateTimestamp <= endTimestamp + ) { + return true + } + return false +} + +function addSpecialFestival(date, component) { + const { convertlLunar2Solar, convertSolarLunar } = component.calendar || {} + const lunarDateInfo = convertSolarLunar(date) + const { lYear, lMonth } = lunarDateInfo || {} + // 春节 + const info = { + type: 'festival', + name: '除夕', + label: '除夕' + } + if (lMonth === 12) { + if (!festival.lunar['12']) festival.lunar['12'] = {} + if (convertlLunar2Solar(`${lYear}-12-30`) === -1) { + festival.lunar['12']['29'] = info + } else { + festival.lunar['12']['30'] = info + } + } +} + +/** + * 是否匹配到节日 + * @param {object} [dateInfo={}] + * @param {object} [component={}] + * @returns {object|boolean} 匹配到的节日数据或者false + */ +function hasFestivalDate(dateInfo = {}, component = {}) { + const { month, date } = dateInfo + let festivalDate = festival.solar[month] && festival.solar[month][date] + if (!festivalDate) { + const { convertSolarLunar } = component.calendar || {} + const lunarDateInfo = convertSolarLunar(dateInfo) + const { lMonth, lDay } = lunarDateInfo + festivalDate = festival.lunar[lMonth] && festival.lunar[lMonth][lDay] + if (!festivalDate) { + const festivalOfMonth = festival.lunar[lMonth] || {} + const festivalDateKey = Object.keys(festivalOfMonth).find(item => + item.match(new RegExp(`\\b${lDay}\\b`)) + ) + if (!festivalDateKey) { + festivalDate = false + } else { + const festivalInfo = festival.lunar[lMonth][festivalDateKey] + if (!festivalInfo) { + festivalDate = false + } else { + const { condition } = festivalInfo + if (typeof condition === 'function') { + festivalDate = condition(lunarDateInfo) + } else { + festivalDate = false + } + } + } + } + } + return festivalDate +} + +export default () => { + return { + name: 'holidays', + beforeRender(calendarData = {}, calendarConfig = {}, component) { + let { dates = [] } = calendarData + if (calendarConfig.showHolidays || calendarConfig.showFestival) { + dates = dates.map(d => { + let item = { ...d } + const { year, month, date } = item + const holidaysOfMonth = + (holidays[year] && holidays[year][month]) || {} + const holidayDate = holidaysOfMonth[date] + if (holidayDate) { + item = { + ...item, + ...holidayDate + } + } else { + const holidayKeys = Object.keys(holidaysOfMonth).filter(item => + item.includes('-') + ) + let target = '' + for (let v of holidayKeys) { + const [start, end] = v.split('-') + if (+d.date >= +start && +d.date <= +end) { + target = v + break + } + } + const [start, end] = target.split('-') + const isInHolidays = inHolidays( + { + year, + month + }, + { + start, + end, + current: date + } + ) + if (isInHolidays) { + item = { + ...item, + ...holidaysOfMonth[target] + } + } else if (calendarConfig.showFestival) { + const { convertSolarLunar, convertlLunar2Solar } = + component.calendar || {} + if ( + typeof convertSolarLunar !== 'function' || + typeof convertlLunar2Solar !== 'function' + ) { + return logger.warn( + '农历节日显示需要引入农历插件(/component/v2/plugins/solarLunar)' + ) + } + addSpecialFestival(item, component) + const festivalDate = hasFestivalDate(item, component) + if (festivalDate) { + item = { + ...item, + ...festivalDate + } + } + } + } + return item + }) + } + return { + calendarData: { + ...calendarData, + dates: dates + }, + calendarConfig + } + }, + methods(component) { + return { + getHolidaysOfCurrentYear() { + const calendar = getCalendarData('calendar', component) + const { curYear } = calendar + return this.methods(component).getHolidaysOfYear(curYear) + }, + getHolidaysOfYear(year) { + if (!year) return logger.warn('getHolidaysOfCurrentYear() 入参错误') + if (!holidays[year]) { + logger.warn('未匹配到当前年份节假日信息,请自行补充') + return { + err: 'not match' + } + } + return holidays[year] + } + } + } + } +} diff --git a/src/components/calendar/plugins/index.d.ts b/src/components/calendar/plugins/index.d.ts new file mode 100644 index 0000000..509db18 --- /dev/null +++ b/src/components/calendar/plugins/index.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/components/calendar/plugins/index.js b/src/components/calendar/plugins/index.js new file mode 100644 index 0000000..baa75d7 --- /dev/null +++ b/src/components/calendar/plugins/index.js @@ -0,0 +1,18 @@ +import preset from './preset/index' + +export default { + installed: [...preset], + use(plugin) { + if (typeof plugin !== 'function') return + const info = plugin() || {} + const { name } = info + if ( + name && + name !== 'methods' && + !this.installed.some(p => p[0] === name) + ) { + this.installed.unshift([name, info]) + } + return this + } +} diff --git a/src/components/calendar/plugins/preset/base.js b/src/components/calendar/plugins/preset/base.js new file mode 100644 index 0000000..20d61fe --- /dev/null +++ b/src/components/calendar/plugins/preset/base.js @@ -0,0 +1,277 @@ +/** + * @Author: drfu* + * @Description: 基础功能 + * @Date: 2020-10-08 21:22:09* + * @Last Modified by: drfu + * @Last Modified time: 2020-10-11 13:28:52 + * */ + +import { calcJumpData } from '../../core' +import { renderCalendar } from '../../render' +import { + dateUtil, + getCalendarData, + setCalendarData, + getCalendarConfig +} from '../../utils/index' + +export default () => { + return { + name: 'base', + beforeRender(calendarData = {}, calendarConfig) { + const calendar = calendarData + const { selectedDates = [], dates } = calendar + let _dates = [...dates] + if (selectedDates.length) { + const selectedDatesStr = selectedDates.map(date => + dateUtil.toTimeStr(date) + ) + _dates.forEach(date => { + const dateStr = dateUtil.toTimeStr(date) + if (selectedDatesStr.includes(dateStr)) { + date.choosed = true + } + }) + } + return { + calendarData: { + ...calendarData, + dates: _dates + }, + calendarConfig + } + }, + onTapDate(tapedDate, calendarData = {}, calendarConfig = {}) { + const calendar = { + ...calendarData + } + const dateIndex = dateUtil.findDateIndexInArray( + tapedDate, + calendarData.dates + ) + const { multi, inverse } = calendarConfig + let dates = [...calendar.dates] + const { selectedDates = [] } = calendar + if (!multi) { + let preSelectedDate = {} + if (selectedDates.length) { + preSelectedDate = [...selectedDates].pop() || {} + } + const timeStr = dateUtil.toTimeStr + if (!inverse && timeStr(preSelectedDate) === timeStr(tapedDate)) { + return calendar + } + let _tapedDate = { ...tapedDate, choosed: !tapedDate.choosed } + + dates[dateIndex] = _tapedDate + if (preSelectedDate.date) { + const idx = dateUtil.findDateIndexInArray(preSelectedDate, dates) + const date = dates[idx] + if (date) { + date.choosed = false + } + } + if (dates[dateIndex].choosed) { + calendar.selectedDates = [dates[dateIndex]] + } else { + calendar.selectedDates = [] + } + } else { + dates[dateIndex] = { + ...dates[dateIndex], + choosed: !dates[dateIndex].choosed + } + if (!calendar.selectedDates) { + calendar.selectedDates = [] + } + if (dates[dateIndex].choosed) { + calendar.selectedDates.push(dates[dateIndex]) + } else { + calendar.selectedDates = calendar.selectedDates.filter( + date => + dateUtil.toTimeStr(date) !== dateUtil.toTimeStr(dates[dateIndex]) + ) + } + } + return { + calendarData: { + ...calendar, + dates + }, + calendarConfig + } + }, + onSwitchCalendar(date, calendarData = {}, component) { + const calendarConfig = getCalendarConfig(component) + if (calendarConfig.weekMode) { + return calendarData + } + const updatedRenderData = calcJumpData({ + dateInfo: date, + config: calendarConfig + }) + return { + ...calendarData, + ...updatedRenderData + } + }, + methods(component) { + return { + jump: dateInfo => { + if (Object.prototype.toString.call(dateInfo) !== '[object Object]') + return + const updatedRenderData = calcJumpData({ + dateInfo, + component + }) + const existCalendarData = getCalendarData('calendar', component) + const config = getCalendarConfig(component) + if (config.autoChoosedWhenJump) { + const target = updatedRenderData.dates[dateInfo.date - 1] + if (!updatedRenderData.selectedDates) { + updatedRenderData.selectedDates = [target] + } else { + updatedRenderData.selectedDates.push(target) + } + } + return renderCalendar.call(component, { + ...existCalendarData, + ...updatedRenderData + }) + }, + getCalendarConfig() { + return getCalendarConfig(component) + }, + setCalendarConfig(config) { + return new Promise((resolve, reject) => { + if (!component || !component.data.config) { + reject('异常:未找到组件配置信息') + return + } + let conf = { ...component.config, ...config } + component.config = conf + setCalendarData({ config: conf }, component) + .then(resolve) + .catch(reject) + }) + }, + cancelSelectedDates(cancelDates = []) { + const existCalendarData = getCalendarData('calendar', component) || {} + const { dates = [], selectedDates = [] } = existCalendarData + let updatedRenderData = {} + const config = getCalendarConfig(component) + let chooseAreaData = {} + if (config.chooseAreaMode) { + chooseAreaData = { + chooseAreaTimestamp: [], + tempChooseAreaTimestamp: [] + } + } + if (!cancelDates.length) { + dates.forEach(item => { + item.choosed = false + }) + updatedRenderData = { + dates, + selectedDates: [] + } + } else { + const cancelDatesStr = cancelDates.map(date => + dateUtil.toTimeStr(date) + ) + const filterSelectedDates = selectedDates.filter( + date => !cancelDatesStr.includes(dateUtil.toTimeStr(date)) + ) + dates.forEach(date => { + if (cancelDatesStr.includes(dateUtil.toTimeStr(date))) { + date.choosed = false + } + }) + updatedRenderData = { + dates, + selectedDates: filterSelectedDates + } + } + + return renderCalendar.call(component, { + ...existCalendarData, + ...updatedRenderData, + ...chooseAreaData + }) + }, + setSelectedDates: targetDates => { + const existCalendarData = getCalendarData('calendar', component) + let { dates, selectedDates = [] } = existCalendarData || {} + let __selectedDates = [] + let __dates = dates + if (!targetDates) { + __dates = dates.map(item => { + const date = { ...item } + date.choosed = true + if (existCalendarData.showLabelAlways && date.showTodoLabel) { + date.showTodoLabel = true + } else { + date.showTodoLabel = false + } + return date + }) + __selectedDates = dates + } else if (targetDates && targetDates.length) { + const allSelected = dateUtil.uniqueArrayByDate( + selectedDates.concat(targetDates) + ) + const allSelectedDateStr = allSelected.map(d => + dateUtil.toTimeStr(d) + ) + __dates = dates.map(item => { + const date = { ...item } + if (allSelectedDateStr.includes(dateUtil.toTimeStr(date))) { + date.choosed = true + __selectedDates.push(date) + } + if (existCalendarData.showLabelAlways && date.showTodoLabel) { + date.showTodoLabel = true + } else { + date.showTodoLabel = false + } + return date + }) + } + return renderCalendar.call(component, { + ...existCalendarData, + dates: __dates, + selectedDates: __selectedDates + }) + }, + setDateStyle: toSetDates => { + if (!Array.isArray(toSetDates)) return Promise.reject() + const existCalendarData = getCalendarData('calendar', component) + const { dates = [], specialStyleDates } = existCalendarData || {} + if (Array.isArray(specialStyleDates)) { + toSetDates = dateUtil.uniqueArrayByDate([ + ...specialStyleDates, + ...toSetDates + ]) + } + const toSetDatesStr = toSetDates.map(item => dateUtil.toTimeStr(item)) + const _dates = dates.map(item => { + const idx = toSetDatesStr.indexOf(dateUtil.toTimeStr(item)) + if (idx > -1) { + return { + ...item, + class: toSetDates[idx].class + } + } else { + return item + } + }) + return renderCalendar.call(component, { + ...existCalendarData, + dates: _dates, + specialStyleDates: toSetDates + }) + } + } + } + } +} diff --git a/src/components/calendar/plugins/preset/get-calendar-data.js b/src/components/calendar/plugins/preset/get-calendar-data.js new file mode 100644 index 0000000..ec5ab64 --- /dev/null +++ b/src/components/calendar/plugins/preset/get-calendar-data.js @@ -0,0 +1,69 @@ +/** + * @Author: drfu* + * @Description: 获取日历数据 + * @Date: 2020-10-08 21:22:09* + * @Last Modified by: drfu + * @Last Modified time: 2020-10-11 13:42:37 + * */ + +import { getCalendarData, logger, getCalendarConfig } from '../../utils/index' + +function wrapDateWithLunar(dates = [], convertFn) { + const datesWithLunar = JSON.parse(JSON.stringify(dates)).map(date => ({ + ...date, + lunar: convertFn(date) + })) + return datesWithLunar +} + +export default () => { + return { + name: 'getData', + methods(component) { + return { + getCurrentYM: () => { + const { curYear, curMonth } = getCalendarData('calendar', component) + return { + year: curYear, + month: curMonth + } + }, + getSelectedDates: (options = {}) => { + const dates = + getCalendarData('calendar.selectedDates', component) || [] + const config = getCalendarConfig(component) || {} + if (options.lunar && !config.showLunar) { + const injectedFns = component.calendar || {} + if (typeof injectedFns.convertSolarLunar === 'function') { + return wrapDateWithLunar(dates, injectedFns.convertSolarLunar) + } else { + logger.warn('获取农历信息需引入农历插件') + } + } else { + return dates + } + }, + getCalendarDates: (options = {}) => { + const config = getCalendarConfig(component) || {} + const dates = getCalendarData('calendar.dates', component) + if (options.lunar && !config.showLunar) { + const injectedFns = component.calendar || {} + if (typeof injectedFns.convertSolarLunar === 'function') { + return wrapDateWithLunar(dates, injectedFns.convertSolarLunar) + } else { + logger.warn('获取农历信息需引入农历插件') + } + } else { + return dates + } + }, + getCalendarAllData: () => { + return { + data: getCalendarData('calendar', component) || {}, + config: getCalendarConfig(component) || {} + } + } + } + } + } +} diff --git a/src/components/calendar/plugins/preset/index.d.ts b/src/components/calendar/plugins/preset/index.d.ts new file mode 100644 index 0000000..509db18 --- /dev/null +++ b/src/components/calendar/plugins/preset/index.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/components/calendar/plugins/preset/index.js b/src/components/calendar/plugins/preset/index.js new file mode 100644 index 0000000..f448cfc --- /dev/null +++ b/src/components/calendar/plugins/preset/index.js @@ -0,0 +1,9 @@ +import base from './base' +import getCalendarData from './get-calendar-data' + +const preset = [ + ['base', base()], + ['get-calendar-data', getCalendarData()] +] + +export default preset diff --git a/src/components/calendar/plugins/selectable.d.ts b/src/components/calendar/plugins/selectable.d.ts new file mode 100644 index 0000000..509db18 --- /dev/null +++ b/src/components/calendar/plugins/selectable.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/components/calendar/plugins/selectable.js b/src/components/calendar/plugins/selectable.js new file mode 100644 index 0000000..d306837 --- /dev/null +++ b/src/components/calendar/plugins/selectable.js @@ -0,0 +1,221 @@ +/** + * @Author: drfu* + * @Description: 禁用、启用日期选择 + * @Date: 2020-10-08 21:22:09* + * @Last Modified by: drfu + * @Last Modified time: 2020-10-08 21:25:00 + * */ + +import { getCalendarData, dateUtil, logger } from '../utils/index' +import { renderCalendar } from '../render' + +function convertEnableAreaToTimestamp(timearea = []) { + const start = timearea[0].split('-') + const end = timearea[1].split('-') + if (start.length !== 3 || end.length !== 3) { + logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]') + return {} + } + const startTimestamp = dateUtil + .newDate(start[0], start[1], start[2]) + .getTime() + const endTimestamp = dateUtil.newDate(end[0], end[1], end[2]).getTime() + return { + start, + end, + startTimestamp, + endTimestamp + } +} + +function isValiditeOfDateArea(dateArea) { + const { + start, + end, + startTimestamp, + endTimestamp + } = convertEnableAreaToTimestamp(dateArea) + if (!start || !end) return + const datesCountOfStart = dateUtil.getDatesCountOfMonth(start[0], start[1]) + const datesCountOfEnd = dateUtil.getDatesCountOfMonth(end[0], end[1]) + if (start[2] > datesCountOfStart || start[2] < 1) { + logger.warn('enableArea() 开始日期错误,指定日期不在指定月份天数范围内') + return false + } else if (start[1] > 12 || start[1] < 1) { + logger.warn('enableArea() 开始日期错误,月份超出1-12月份') + return false + } else if (end[2] > datesCountOfEnd || end[2] < 1) { + logger.warn('enableArea() 截止日期错误,指定日期不在指定月份天数范围内') + return false + } else if (end[1] > 12 || end[1] < 1) { + logger.warn('enableArea() 截止日期错误,月份超出1-12月份') + return false + } else if (startTimestamp > endTimestamp) { + logger.warn('enableArea()参数最小日期大于了最大日期') + return false + } else { + return true + } +} + +function handleDisableMode(calendarConfig) { + const { disableMode } = calendarConfig + if (!disableMode) return {} + const disableBound = + dateUtil.getTimeStamp(disableMode.date) || dateUtil.todayTimestamp() + return { + disableBound, + disableType: disableMode.type + } +} + +function disabledByConfig(dateInfo, currentDate, calendarConfig) { + const date = { ...dateInfo } + const { disableType, disableBound } = handleDisableMode(calendarConfig) + if ( + (disableType === 'before' && disableBound && currentDate < disableBound) || + (disableType === 'after' && disableBound && currentDate > disableBound) + ) { + date.disable = true + } else { + date.disable = false + } + return date +} + +export default () => { + return { + name: 'enable', + beforeRender(calendarData = {}, calendarConfig = {}) { + const { + dates, + enableArea, + enableDates, + disableDates, + renderCausedBy + } = calendarData + const _dates = [...dates].map(date => { + let item = { ...date } + const timeStr = dateUtil.toTimeStr(date) + const timestamp = +dateUtil.getTimeStamp(item) + if (renderCausedBy === 'enableDates') { + if (enableDates && enableDates.length) { + if (enableDates.includes(timeStr)) { + item.disable = false + } else { + item.disable = true + } + return item + } + } else if (renderCausedBy === 'enableArea') { + if (enableArea && enableArea.length) { + const [startTimestamp, endTimestamp] = enableArea || [] + const ifOutofArea = + +startTimestamp > timestamp || timestamp > +endTimestamp + item.disable = ifOutofArea + return item + } + } else if (renderCausedBy === 'disableDates') { + if (disableDates && disableDates.length) { + if (disableDates && disableDates.includes(timeStr)) { + item.disable = true + } else { + item.disable = false + } + return item + } + } + return disabledByConfig(item, timestamp, calendarConfig) + }) + + return { + calendarData: { + ...calendarData, + dates: _dates + }, + calendarConfig + } + }, + methods(component) { + return { + enableArea: (dateArea = []) => { + if (dateArea.length === 2) { + const validate = isValiditeOfDateArea(dateArea) + if (validate) { + const existCalendarData = getCalendarData('calendar', component) + const { + startTimestamp, + endTimestamp + } = convertEnableAreaToTimestamp(dateArea) + + return renderCalendar.call(component, { + ...existCalendarData, + renderCausedBy: 'enableArea', + enableArea: [startTimestamp, endTimestamp] + }) + } + } else { + return Promise.inject( + 'enableArea()参数需为时间范围数组,形如:["2018-8-4" , "2018-8-24"]' + ) + } + }, + enableDates: (toSet = []) => { + if (!toSet.length) return + const existCalendarData = getCalendarData('calendar', component) + const { enableDates = [] } = existCalendarData || {} + let toSetDates = toSet.map(item => { + if (typeof item === 'string') { + return dateUtil.transformDateRow2Dict(item) + } + return item + }) + if (enableDates.length) { + toSetDates = dateUtil.uniqueArrayByDate([ + ...toSetDates, + ...enableDates.map(d => dateUtil.transformDateRow2Dict(d)) + ]) + } + return renderCalendar.call(component, { + ...existCalendarData, + renderCausedBy: 'enableDates', + enableDates: toSetDates.map(date => { + if (typeof date !== 'string') { + return dateUtil.toTimeStr(date) + } + return date + }) + }) + }, + disableDates: toSet => { + const existCalendarData = getCalendarData('calendar', component) + const { disableDates = [], dates = [] } = existCalendarData || {} + let toSetDates = toSet.map(item => { + let date = { ...item } + if (typeof date === 'string') { + return dateUtil.transformDateRow2Dict(item) + } + return item + }) + if (disableDates && disableDates.length) { + toSetDates = dateUtil.uniqueArrayByDate([ + ...toSetDates, + ...disableDates.map(d => dateUtil.transformDateRow2Dict(d)) + ]) + } + return renderCalendar.call(component, { + ...existCalendarData, + renderCausedBy: 'disableDates', + dates, + disableDates: toSetDates.map(date => { + if (typeof date !== 'string') { + return dateUtil.toTimeStr(date) + } + return date + }) + }) + } + } + } + } +} diff --git a/src/components/calendar/plugins/solarLunar/convertSolarLunar.js b/src/components/calendar/plugins/solarLunar/convertSolarLunar.js new file mode 100644 index 0000000..e34bd1e --- /dev/null +++ b/src/components/calendar/plugins/solarLunar/convertSolarLunar.js @@ -0,0 +1,1036 @@ +/** + * @1900-2100区间内的公历、农历互转 + * @Version 1.0.3 + * @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0] + * @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0] + */ +/* 公历年月日转农历数据 返回json */ +// calendar.solar2lunar(1987,11,01); +/** 农历年月日转公历年月日 **/ +// calendar.lunar2solar(1987,9,10); +// 调用以上方法后返回类似如下object(json)具体以上就不需要解释了吧! +// c开头的是公历各属性值 l开头的自然就是农历咯 gz开头的就是天干地支纪年的数据啦~ +// { +// Animal: "兔", +// IDayCn: "初十", +// IMonthCn: "九月", +// Term: null, +// astro: "天蝎座", +// cDay: 1, +// cMonth: 11, +// cYear: 1987, +// gzDay: "甲寅", +// gzMonth: "庚戌", +// gzYear: "丁卯", +// isLeap: false, +// isTerm: false, +// isToday: false, +// lDay: 10, +// lMonth: 9, +// lYear: 1987, +// nWeek: 7, +// ncWeek: "星期日" +// } +// 该代码还有其它可以调用的方法,请自己查看代码中的详细注释 +const calendar = { + /** + * 农历1900-2100的润大小信息表 + * @Array Of Property + * @return Hex + */ + lunarInfo: [ + 0x04bd8, + 0x04ae0, + 0x0a570, + 0x054d5, + 0x0d260, + 0x0d950, + 0x16554, + 0x056a0, + 0x09ad0, + 0x055d2, // 1900-1909 + 0x04ae0, + 0x0a5b6, + 0x0a4d0, + 0x0d250, + 0x1d255, + 0x0b540, + 0x0d6a0, + 0x0ada2, + 0x095b0, + 0x14977, // 1910-1919 + 0x04970, + 0x0a4b0, + 0x0b4b5, + 0x06a50, + 0x06d40, + 0x1ab54, + 0x02b60, + 0x09570, + 0x052f2, + 0x04970, // 1920-1929 + 0x06566, + 0x0d4a0, + 0x0ea50, + 0x06e95, + 0x05ad0, + 0x02b60, + 0x186e3, + 0x092e0, + 0x1c8d7, + 0x0c950, // 1930-1939 + 0x0d4a0, + 0x1d8a6, + 0x0b550, + 0x056a0, + 0x1a5b4, + 0x025d0, + 0x092d0, + 0x0d2b2, + 0x0a950, + 0x0b557, // 1940-1949 + 0x06ca0, + 0x0b550, + 0x15355, + 0x04da0, + 0x0a5b0, + 0x14573, + 0x052b0, + 0x0a9a8, + 0x0e950, + 0x06aa0, // 1950-1959 + 0x0aea6, + 0x0ab50, + 0x04b60, + 0x0aae4, + 0x0a570, + 0x05260, + 0x0f263, + 0x0d950, + 0x05b57, + 0x056a0, // 1960-1969 + 0x096d0, + 0x04dd5, + 0x04ad0, + 0x0a4d0, + 0x0d4d4, + 0x0d250, + 0x0d558, + 0x0b540, + 0x0b6a0, + 0x195a6, // 1970-1979 + 0x095b0, + 0x049b0, + 0x0a974, + 0x0a4b0, + 0x0b27a, + 0x06a50, + 0x06d40, + 0x0af46, + 0x0ab60, + 0x09570, // 1980-1989 + 0x04af5, + 0x04970, + 0x064b0, + 0x074a3, + 0x0ea50, + 0x06b58, + 0x055c0, + 0x0ab60, + 0x096d5, + 0x092e0, // 1990-1999 + 0x0c960, + 0x0d954, + 0x0d4a0, + 0x0da50, + 0x07552, + 0x056a0, + 0x0abb7, + 0x025d0, + 0x092d0, + 0x0cab5, // 2000-2009 + 0x0a950, + 0x0b4a0, + 0x0baa4, + 0x0ad50, + 0x055d9, + 0x04ba0, + 0x0a5b0, + 0x15176, + 0x052b0, + 0x0a930, // 2010-2019 + 0x07954, + 0x06aa0, + 0x0ad50, + 0x05b52, + 0x04b60, + 0x0a6e6, + 0x0a4e0, + 0x0d260, + 0x0ea65, + 0x0d530, // 2020-2029 + 0x05aa0, + 0x076a3, + 0x096d0, + 0x04afb, + 0x04ad0, + 0x0a4d0, + 0x1d0b6, + 0x0d250, + 0x0d520, + 0x0dd45, // 2030-2039 + 0x0b5a0, + 0x056d0, + 0x055b2, + 0x049b0, + 0x0a577, + 0x0a4b0, + 0x0aa50, + 0x1b255, + 0x06d20, + 0x0ada0, // 2040-2049 + /** Add By JJonline@JJonline.Cn **/ + 0x14b63, + 0x09370, + 0x049f8, + 0x04970, + 0x064b0, + 0x168a6, + 0x0ea50, + 0x06b20, + 0x1a6c4, + 0x0aae0, // 2050-2059 + 0x0a2e0, + 0x0d2e3, + 0x0c960, + 0x0d557, + 0x0d4a0, + 0x0da50, + 0x05d55, + 0x056a0, + 0x0a6d0, + 0x055d4, // 2060-2069 + 0x052d0, + 0x0a9b8, + 0x0a950, + 0x0b4a0, + 0x0b6a6, + 0x0ad50, + 0x055a0, + 0x0aba4, + 0x0a5b0, + 0x052b0, // 2070-2079 + 0x0b273, + 0x06930, + 0x07337, + 0x06aa0, + 0x0ad50, + 0x14b55, + 0x04b60, + 0x0a570, + 0x054e4, + 0x0d160, // 2080-2089 + 0x0e968, + 0x0d520, + 0x0daa0, + 0x16aa6, + 0x056d0, + 0x04ae0, + 0x0a9d4, + 0x0a2d0, + 0x0d150, + 0x0f252, // 2090-2099 + 0x0d520 + ], // 2100 + + /** + * 公历每个月份的天数普通表 + * @Array Of Property + * @return Number + */ + solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + + /** + * 天干地支之天干速查表 + * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"] + * @return Cn string + */ + Gan: [ + '\u7532', + '\u4e59', + '\u4e19', + '\u4e01', + '\u620a', + '\u5df1', + '\u5e9a', + '\u8f9b', + '\u58ec', + '\u7678' + ], + + /** + * 天干地支之地支速查表 + * @Array Of Property + * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"] + * @return Cn string + */ + Zhi: [ + '\u5b50', + '\u4e11', + '\u5bc5', + '\u536f', + '\u8fb0', + '\u5df3', + '\u5348', + '\u672a', + '\u7533', + '\u9149', + '\u620c', + '\u4ea5' + ], + + /** + * 天干地支之地支速查表<=>生肖 + * @Array Of Property + * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"] + * @return Cn string + */ + Animals: [ + '\u9f20', + '\u725b', + '\u864e', + '\u5154', + '\u9f99', + '\u86c7', + '\u9a6c', + '\u7f8a', + '\u7334', + '\u9e21', + '\u72d7', + '\u732a' + ], + + /** + * 24节气速查表 + * @Array Of Property + * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"] + * @return Cn string + */ + solarTerm: [ + '\u5c0f\u5bd2', + '\u5927\u5bd2', + '\u7acb\u6625', + '\u96e8\u6c34', + '\u60ca\u86f0', + '\u6625\u5206', + '\u6e05\u660e', + '\u8c37\u96e8', + '\u7acb\u590f', + '\u5c0f\u6ee1', + '\u8292\u79cd', + '\u590f\u81f3', + '\u5c0f\u6691', + '\u5927\u6691', + '\u7acb\u79cb', + '\u5904\u6691', + '\u767d\u9732', + '\u79cb\u5206', + '\u5bd2\u9732', + '\u971c\u964d', + '\u7acb\u51ac', + '\u5c0f\u96ea', + '\u5927\u96ea', + '\u51ac\u81f3' + ], + + /** + * 1900-2100各年的24节气日期速查表 + * @Array Of Property + * @return 0x string For splice + */ + sTermInfo: [ + '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf97c3598082c95f8c965cc920f', + '97bd0b06bdb0722c965ce1cfcc920f', + 'b027097bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', + '97bd0b06bdb0722c965ce1cfcc920f', + 'b027097bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', + '97bd0b06bdb0722c965ce1cfcc920f', + 'b027097bd097c36b0b6fc9274c91aa', + '9778397bd19801ec9210c965cc920e', + '97b6b97bd19801ec95f8c965cc920f', + '97bd09801d98082c95f8e1cfcc920f', + '97bd097bd097c36b0b6fc9210c8dc2', + '9778397bd197c36c9210c9274c91aa', + '97b6b97bd19801ec95f8c965cc920e', + '97bd09801d98082c95f8e1cfcc920f', + '97bd097bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c91aa', + '97b6b97bd19801ec95f8c965cc920e', + '97bcf97c3598082c95f8e1cfcc920f', + '97bd097bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf97c3598082c95f8c965cc920f', + '97bd097bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf97c3598082c95f8c965cc920f', + '97bd097bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', + '97bd097bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', + '97bd097bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf97c359801ec95f8c965cc920f', + '97bd097bd07f595b0b6fc920fb0722', + '9778397bd097c36b0b6fc9210c8dc2', + '9778397bd19801ec9210c9274c920e', + '97b6b97bd19801ec95f8c965cc920f', + '97bd07f5307f595b0b0bc920fb0722', + '7f0e397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c920e', + '97b6b97bd19801ec95f8c965cc920f', + '97bd07f5307f595b0b0bc920fb0722', + '7f0e397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bd07f1487f595b0b0bc920fb0722', + '7f0e397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf7f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf7f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf7f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c965cc920e', + '97bcf7f1487f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b97bd19801ec9210c9274c920e', + '97bcf7f0e47f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9210c91aa', + '97b6b97bd197c36c9210c9274c920e', + '97bcf7f0e47f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36c9210c9274c920e', + '97b6b7f0e47f531b0723b0b6fb0722', + '7f0e37f5307f595b0b0bc920fb0722', + '7f0e397bd097c36b0b6fc9210c8dc2', + '9778397bd097c36b0b70c9274c91aa', + '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e37f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc9210c8dc2', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9274c91aa', + '97b6b7f0e47f531b0723b0787b0721', + '7f0e27f0e47f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9210c91aa', + '97b6b7f0e47f149b0723b0787b0721', + '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', + '9778397bd097c36b0b6fc9210c8dc2', + '977837f0e37f149b0723b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', + '7f0e37f5307f595b0b0bc920fb0722', + '7f0e397bd097c35b0b6fc9210c8dc2', + '977837f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e37f1487f595b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc9210c8dc2', + '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', + '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd097c35b0b6fc920fb0722', + '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', + '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', + '977837f0e37f14998082b0787b06bd', + '7f07e7f0e47f149b0723b0787b0721', + '7f0e27f0e47f531b0b0bb0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', + '977837f0e37f14998082b0723b06bd', + '7f07e7f0e37f149b0723b0787b0721', + '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e397bd07f595b0b0bc920fb0722', + '977837f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', + '7f0e37f1487f595b0b0bb0b6fb0722', + '7f0e37f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', + '7f0e37f1487f531b0b0bb0b6fb0722', + '7f0e37f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e37f1487f531b0b0bb0b6fb0722', + '7f0e37f0e37f14898082b072297c35', + '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e37f0e37f14898082b072297c35', + '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e37f0e366aa89801eb072297c35', + '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f149b0723b0787b0721', + '7f0e27f1487f531b0b0bb0b6fb0722', + '7f0e37f0e366aa89801eb072297c35', + '7ec967f0e37f14998082b0723b06bd', + '7f07e7f0e47f149b0723b0787b0721', + '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e37f0e366aa89801eb072297c35', + '7ec967f0e37f14998082b0723b06bd', + '7f07e7f0e37f14998083b0787b0721', + '7f0e27f0e47f531b0723b0b6fb0722', + '7f0e37f0e366aa89801eb072297c35', + '7ec967f0e37f14898082b0723b02d5', + '7f07e7f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', + '7f0e36665b66aa89801e9808297c35', + '665f67f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b0721', + '7f07e7f0e47f531b0723b0b6fb0722', + '7f0e36665b66a449801e9808297c35', + '665f67f0e37f14898082b0723b02d5', + '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e36665b66a449801e9808297c35', + '665f67f0e37f14898082b072297c35', + '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e26665b66a449801e9808297c35', + '665f67f0e37f1489801eb072297c35', + '7ec967f0e37f14998082b0787b06bd', + '7f07e7f0e47f531b0723b0b6fb0721', + '7f0e27f1487f531b0b0bb0b6fb0722' + ], + + /** + * 数字转中文速查表 + * @Array Of Property + * @trans ['日','一','二','三','四','五','六','七','八','九','十'] + * @return Cn string + */ + nStr1: [ + '\u65e5', + '\u4e00', + '\u4e8c', + '\u4e09', + '\u56db', + '\u4e94', + '\u516d', + '\u4e03', + '\u516b', + '\u4e5d', + '\u5341' + ], + + /** + * 日期转农历称呼速查表 + * @Array Of Property + * @trans ['初','十','廿','卅'] + * @return Cn string + */ + nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'], + + /** + * 月份转农历称呼速查表 + * @Array Of Property + * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊'] + * @return Cn string + */ + nStr3: [ + '\u6b63', + '\u4e8c', + '\u4e09', + '\u56db', + '\u4e94', + '\u516d', + '\u4e03', + '\u516b', + '\u4e5d', + '\u5341', + '\u51ac', + '\u814a' + ], + + /** + * 返回农历y年一整年的总天数 + * @param lunar Year + * @return Number + * @eg:var count = calendar.lYearDays(1987) ;//count=387 + */ + lYearDays: function(y) { + let i + let sum = 348 + for (i = 0x8000; i > 0x8; i >>= 1) { + sum += calendar.lunarInfo[y - 1900] & i ? 1 : 0 + } + return sum + calendar.leapDays(y) + }, + + /** + * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0 + * @param lunar Year + * @return Number (0-12) + * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6 + */ + leapMonth: function(y) { + // 闰字编码 \u95f0 + return calendar.lunarInfo[y - 1900] & 0xf + }, + + /** + * 返回农历y年闰月的天数 若该年没有闰月则返回0 + * @param lunar Year + * @return Number (0、29、30) + * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29 + */ + leapDays: function(y) { + if (calendar.leapMonth(y)) { + return calendar.lunarInfo[y - 1900] & 0x10000 ? 30 : 29 + } + return 0 + }, + + /** + * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法 + * @param lunar Year + * @return Number (-1、29、30) + * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29 + */ + monthDays: function(y, m) { + if (m > 12 || m < 1) return -1 // 月份参数从1至12,参数错误返回-1 + return calendar.lunarInfo[y - 1900] & (0x10000 >> m) ? 30 : 29 + }, + + /** + * 返回公历(!)y年m月的天数 + * @param solar Year + * @return Number (-1、28、29、30、31) + * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30 + */ + solarDays: function(y, m) { + if (m > 12 || m < 1) return -1 // 若参数错误 返回-1 + const ms = m - 1 + if (+ms === 1) { + // 2月份的闰平规律测算后确认返回28或29 + return (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0 ? 29 : 28 + } else { + return calendar.solarMonth[ms] + } + }, + + /** + * 农历年份转换为干支纪年 + * @param lYear 农历年的年份数 + * @return Cn string + */ + toGanZhiYear: function(lYear) { + let ganKey = (lYear - 3) % 10 + let zhiKey = (lYear - 3) % 12 + if (+ganKey === 0) ganKey = 10 // 如果余数为0则为最后一个天干 + if (+zhiKey === 0) zhiKey = 12 // 如果余数为0则为最后一个地支 + return calendar.Gan[ganKey - 1] + calendar.Zhi[zhiKey - 1] + }, + + /** + * 公历月、日判断所属星座 + * @param cMonth [description] + * @param cDay [description] + * @return Cn string + */ + toAstro: function(cMonth, cDay) { + const s = + '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf' + const arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22] + return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7' // 座 + }, + + /** + * 传入offset偏移量返回干支 + * @param offset 相对甲子的偏移量 + * @return Cn string + */ + toGanZhi: function(offset) { + return calendar.Gan[offset % 10] + calendar.Zhi[offset % 12] + }, + + /** + * 传入公历(!)y年获得该年第n个节气的公历日期 + * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 + * @return day Number + * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春 + */ + getTerm: function(y, n) { + if (y < 1900 || y > 2100) return -1 + if (n < 1 || n > 24) return -1 + const _table = calendar.sTermInfo[y - 1900] + const _info = [ + parseInt('0x' + _table.substr(0, 5)).toString(), + parseInt('0x' + _table.substr(5, 5)).toString(), + parseInt('0x' + _table.substr(10, 5)).toString(), + parseInt('0x' + _table.substr(15, 5)).toString(), + parseInt('0x' + _table.substr(20, 5)).toString(), + parseInt('0x' + _table.substr(25, 5)).toString() + ] + const _calday = [ + _info[0].substr(0, 1), + _info[0].substr(1, 2), + _info[0].substr(3, 1), + _info[0].substr(4, 2), + + _info[1].substr(0, 1), + _info[1].substr(1, 2), + _info[1].substr(3, 1), + _info[1].substr(4, 2), + + _info[2].substr(0, 1), + _info[2].substr(1, 2), + _info[2].substr(3, 1), + _info[2].substr(4, 2), + + _info[3].substr(0, 1), + _info[3].substr(1, 2), + _info[3].substr(3, 1), + _info[3].substr(4, 2), + + _info[4].substr(0, 1), + _info[4].substr(1, 2), + _info[4].substr(3, 1), + _info[4].substr(4, 2), + + _info[5].substr(0, 1), + _info[5].substr(1, 2), + _info[5].substr(3, 1), + _info[5].substr(4, 2) + ] + return parseInt(_calday[n - 1]) + }, + + /** + * 传入农历数字月份返回汉语通俗表示法 + * @param lunar month + * @return Cn string + * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月' + */ + toChinaMonth: function(m) { + // 月 => \u6708 + if (m > 12 || m < 1) return -1 // 若参数错误 返回-1 + let s = calendar.nStr3[m - 1] + s += '\u6708' // 加上月字 + return s + }, + + /** + * 传入农历日期数字返回汉字表示法 + * @param lunar day + * @return Cn string + * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一' + */ + toChinaDay: function(d) { + // 日 => \u65e5 + let s + switch (d) { + case 10: + s = '\u521d\u5341' + break + case 20: + s = '\u4e8c\u5341' + break + case 30: + s = '\u4e09\u5341' + break + default: + s = calendar.nStr2[Math.floor(d / 10)] + s += calendar.nStr1[d % 10] + } + return s + }, + + /** + * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春” + * @param y year + * @return Cn string + * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔' + */ + getAnimal: function(y) { + return calendar.Animals[(y - 4) % 12] + }, + + /** + * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON + * @param y solar year + * @param m solar month + * @param d solar day + * @return JSON object + * @eg:console.log(calendar.solar2lunar(1987,11,01)); + */ + solar2lunar: function(y, m, d) { + // 参数区间1900.1.31~2100.12.31 + // 年份限定、上限 + if (y < 1900 || y > 2100) { + return -1 // undefined转换为数字变为NaN + } + // 公历传参最下限 + if (+y === 1900 && +m === 1 && +d < 31) { + return -1 + } + // 未传参 获得当天 + let objDate + if (!y) { + objDate = new Date() + } else { + objDate = new Date(y, parseInt(m) - 1, d) + } + let i + let leap = 0 + let temp = 0 + // 修正ymd参数 + y = objDate.getFullYear() + m = objDate.getMonth() + 1 + d = objDate.getDate() + let offset = + (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - + Date.UTC(1900, 0, 31)) / + 86400000 + for (i = 1900; i < 2101 && offset > 0; i++) { + temp = calendar.lYearDays(i) + offset -= temp + } + if (offset < 0) { + offset += temp + i-- + } + + // 是否今天 + const isTodayObj = new Date() + let isToday = false + if ( + isTodayObj.getFullYear() === +y && + isTodayObj.getMonth() + 1 === +m && + isTodayObj.getDate() === +d + ) { + isToday = true + } + // 星期几 + let nWeek = objDate.getDay() + const cWeek = calendar.nStr1[nWeek] + // 数字表示周几顺应天朝周一开始的惯例 + if (+nWeek === 0) { + nWeek = 7 + } + // 农历年 + const year = i + leap = calendar.leapMonth(i) // 闰哪个月 + let isLeap = false + + // 效验闰月 + for (i = 1; i < 13 && offset > 0; i++) { + // 闰月 + if (leap > 0 && i === leap + 1 && isLeap === false) { + --i + isLeap = true + temp = calendar.leapDays(year) // 计算农历闰月天数 + } else { + temp = calendar.monthDays(year, i) // 计算农历普通月天数 + } + // 解除闰月 + if (isLeap === true && i === leap + 1) isLeap = false + offset -= temp + } + // 闰月导致数组下标重叠取反 + if (offset === 0 && leap > 0 && i === leap + 1) { + if (isLeap) { + isLeap = false + } else { + isLeap = true + --i + } + } + if (offset < 0) { + offset += temp + --i + } + // 农历月 + const month = i + // 农历日 + const day = offset + 1 + // 天干地支处理 + const sm = m - 1 + const gzY = calendar.toGanZhiYear(year) + + // 当月的两个节气 + // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year` + const firstNode = calendar.getTerm(y, m * 2 - 1) // 返回当月「节」为几日开始 + const secondNode = calendar.getTerm(y, m * 2) // 返回当月「节」为几日开始 + + // 依据12节气修正干支月 + let gzM = calendar.toGanZhi((y - 1900) * 12 + m + 11) + if (d >= firstNode) { + gzM = calendar.toGanZhi((y - 1900) * 12 + m + 12) + } + + // 传入的日期的节气与否 + let isTerm = false + let Term = null + if (+firstNode === d) { + isTerm = true + Term = calendar.solarTerm[m * 2 - 2] + } + if (+secondNode === d) { + isTerm = true + Term = calendar.solarTerm[m * 2 - 1] + } + // 日柱 当月一日与 1900/1/1 相差天数 + const dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10 + const gzD = calendar.toGanZhi(dayCyclical + d - 1) + // 该日期所属的星座 + const astro = calendar.toAstro(m, d) + + return { + lYear: year, + lMonth: month, + lDay: day, + Animal: calendar.getAnimal(year), + IMonthCn: (isLeap ? '\u95f0' : '') + calendar.toChinaMonth(month), + IDayCn: calendar.toChinaDay(day), + cYear: y, + cMonth: m, + cDay: d, + gzYear: gzY, + gzMonth: gzM, + gzDay: gzD, + isToday: isToday, + isLeap: isLeap, + nWeek: nWeek, + ncWeek: '\u661f\u671f' + cWeek, + isTerm: isTerm, + Term: Term, + astro: astro + } + }, + + /** + * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON + * @param y lunar year + * @param m lunar month + * @param d lunar day + * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] + * @return JSON object + * @eg:console.log(calendar.lunar2solar(1987,9,10)); + */ + lunar2solar: function(y, m, d, isLeapMonth) { + // 参数区间1900.1.31~2100.12.1 + isLeapMonth = !!isLeapMonth + // let leapOffset = 0; + const leapMonth = calendar.leapMonth(y) + // let leapDay = calendar.leapDays(y); + if (isLeapMonth && leapMonth !== m) return -1 // 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同 + if ( + (+y === 2100 && +m === 12 && +d > 1) || + (+y === 1900 && +m === 1 && +d < 31) + ) + return -1 // 超出了最大极限值 + const day = calendar.monthDays(y, m) + let _day = day + // bugFix 2016-9-25 + // if month is leap, _day use leapDays method + if (isLeapMonth) { + _day = calendar.leapDays(y, m) + } + if (y < 1900 || y > 2100 || d > _day) return -1 // 参数合法性效验 + + // 计算农历的时间差 + let offset = 0 + for (let i = 1900; i < y; i++) { + offset += calendar.lYearDays(i) + } + let leap = 0 + let isAdd = false + for (let i = 1; i < m; i++) { + leap = calendar.leapMonth(y) + if (!isAdd) { + // 处理闰月 + if (leap <= i && leap > 0) { + offset += calendar.leapDays(y) + isAdd = true + } + } + offset += calendar.monthDays(y, i) + } + // 转换闰月农历 需补充该年闰月的前一个月的时差 + if (isLeapMonth) offset += day + // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点) + const stmap = Date.UTC(1900, 1, 30, 0, 0, 0) + const calObj = new Date((offset + d - 31) * 86400000 + stmap) + const cY = calObj.getUTCFullYear() + const cM = calObj.getUTCMonth() + 1 + const cD = calObj.getUTCDate() + + return calendar.solar2lunar(cY, cM, cD) + } +} + +const { + Gan, + Zhi, + nStr1, + nStr2, + nStr3, + Animals, + solarTerm, + lunarInfo, + sTermInfo, + solarMonth, + ...rest +} = calendar + +export default rest diff --git a/src/components/calendar/plugins/solarLunar/index.d.ts b/src/components/calendar/plugins/solarLunar/index.d.ts new file mode 100644 index 0000000..509db18 --- /dev/null +++ b/src/components/calendar/plugins/solarLunar/index.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/components/calendar/plugins/solarLunar/index.js b/src/components/calendar/plugins/solarLunar/index.js new file mode 100644 index 0000000..6d6a336 --- /dev/null +++ b/src/components/calendar/plugins/solarLunar/index.js @@ -0,0 +1,59 @@ +import { dateUtil } from '../../utils/index' +import convertSolarLunar from './convertSolarLunar' + +function getDateRow2Dict(dateInfo) { + if (!dateInfo) return dateInfo + if (typeof dateInfo === 'string' && dateInfo.includes('-')) { + dateInfo = dateUtil.transformDateRow2Dict(dateInfo) + } + return dateInfo +} + +export default () => { + return { + name: 'convertSolarLunar', + beforeRender(calendarData = {}, calendarConfig = {}) { + let { dates = [], selectedDates = [] } = calendarData + if (calendarConfig.showLunar) { + dates = dates.map(dataInfo => { + const { year, month, date } = dataInfo + return { + ...dataInfo, + lunar: convertSolarLunar.solar2lunar(year, month, date) + } + }) + selectedDates = selectedDates.map(dataInfo => { + const { year, month, date } = dataInfo + return { + ...dataInfo, + lunar: convertSolarLunar.solar2lunar(year, month, date) + } + }) + } + return { + calendarData: { + ...calendarData, + dates: dates, + selectedDates: selectedDates + }, + calendarConfig + } + }, + methods() { + return { + convertSolarLunar: dateInfo => { + dateInfo = getDateRow2Dict(dateInfo) + if (!dateInfo) return dateInfo + const { year, month, date } = dateInfo + return convertSolarLunar.solar2lunar(year, month, date) + }, + convertlLunar2Solar: (dateInfo, isLeapMonth) => { + dateInfo = getDateRow2Dict(dateInfo) + if (!dateInfo) return dateInfo + const { year, month, date } = dateInfo + return convertSolarLunar.lunar2solar(year, month, date, isLeapMonth) + } + } + } + } +} diff --git a/src/components/calendar/plugins/time-range.js b/src/components/calendar/plugins/time-range.js new file mode 100644 index 0000000..9788861 --- /dev/null +++ b/src/components/calendar/plugins/time-range.js @@ -0,0 +1,305 @@ +/** + * @Author: drfu* + * @Description: 时间区域选择 + * @Date: 2020-10-08 21:22:09* + * @Last Modified by: drfu + * @Last Modified time: 2020-10-11 13:56:32 + * */ + +import { renderCalendar } from '../render' +import { + logger, + dateUtil, + getCalendarConfig, + getCalendarData +} from '../utils/index' + +function pusheNextMonthDateArea( + dateInfo = {}, + startTimestamp, + endTimestamp, + selectedDates = [] +) { + let tempOfSelectedDate = [...selectedDates] + const dates = dateUtil.calcDates(dateInfo.year, dateInfo.month) + let datesLen = dates.length + for (let i = 0; i < datesLen; i++) { + const date = dates[i] + const timeStamp = dateUtil.getTimeStamp(date) + if (timeStamp <= endTimestamp && timeStamp >= startTimestamp) { + tempOfSelectedDate.push({ + ...date, + choosed: true + }) + } + if (i === datesLen - 1 && timeStamp < endTimestamp) { + pusheNextMonthDateArea( + dateUtil.getNextMonthInfo(date), + startTimestamp, + endTimestamp, + tempOfSelectedDate + ) + } + } + return tempOfSelectedDate +} +function pushPrevMonthDateArea( + dateInfo = {}, + startTimestamp, + endTimestamp, + selectedDates = [] +) { + let tempOfSelectedDate = [...selectedDates] + const dates = dateUtil.sortDatesByTime( + dateUtil.calcDates(dateInfo.year, dateInfo.month), + 'desc' + ) + let datesLen = dates.length + let firstDate = dateUtil.getTimeStamp(dates[0]) + for (let i = 0; i < datesLen; i++) { + const date = dates[i] + const timeStamp = dateUtil.getTimeStamp(date) + if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) { + tempOfSelectedDate.push({ + ...date, + choosed: true + }) + } + if (i === datesLen - 1 && firstDate > startTimestamp) { + pushPrevMonthDateArea( + dateUtil.getPrevMonthInfo(date), + startTimestamp, + endTimestamp, + tempOfSelectedDate + ) + } + } + return tempOfSelectedDate +} +/** + * 当设置日期区域非当前时保存其它月份的日期至已选日期数组 + * @param {object} info + */ +function calcDateWhenNotInOneMonth(info) { + const { firstDate, lastDate, startTimestamp, endTimestamp } = info + let { selectedDate } = info + if (dateUtil.getTimeStamp(firstDate) > startTimestamp) { + selectedDate = pushPrevMonthDateArea( + dateUtil.getPrevMonthInfo(firstDate), + startTimestamp, + endTimestamp, + selectedDate + ) + } + if (dateUtil.getTimeStamp(lastDate) < endTimestamp) { + selectedDate = pusheNextMonthDateArea( + dateUtil.getNextMonthInfo(lastDate), + startTimestamp, + endTimestamp, + selectedDate + ) + } + return [...selectedDate] +} + +/** + * 指定日期区域转时间戳 + * @param {array} timearea 时间区域 + */ +export function convertTimeRangeToTimestamp(timearea = []) { + const start = timearea[0].split('-') + const end = timearea[1].split('-') + if (start.length !== 3 || end.length !== 3) { + logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]') + return {} + } + const startTimestamp = dateUtil + .newDate(start[0], start[1], start[2]) + .getTime() + const endTimestamp = dateUtil.newDate(end[0], end[1], end[2]).getTime() + return { + start, + end, + startTimestamp, + endTimestamp + } +} + +/** + * 校验时间区域是否合法 + * @param {array} dateArea 时间区域 + */ +function validateTimeRange(dateArea) { + const { + start, + end, + startTimestamp, + endTimestamp + } = convertTimeRangeToTimestamp(dateArea) + if (!start || !end) return + const startMonthDays = dateUtil.getDatesCountOfMonth(start[0], start[1]) + const endMonthDays = dateUtil.getDatesCountOfMonth(end[0], end[1]) + if (start[2] > startMonthDays || start[2] < 1) { + logger.warn('enableArea() 开始日期错误,指定日期不在当前月份天数范围内') + return false + } else if (start[1] > 12 || start[1] < 1) { + logger.warn('enableArea() 开始日期错误,月份超出1-12月份') + return false + } else if (end[2] > endMonthDays || end[2] < 1) { + logger.warn('enableArea() 截止日期错误,指定日期不在当前月份天数范围内') + return false + } else if (end[1] > 12 || end[1] < 1) { + logger.warn('enableArea() 截止日期错误,月份超出1-12月份') + return false + } else if (startTimestamp > endTimestamp) { + logger.warn('enableArea()参数最小日期大于了最大日期') + return false + } else { + return true + } +} + +export default () => { + return { + name: 'timeRange', + beforeRender(calendarData = {}, calendarConfig = {}) { + const { + chooseAreaTimestamp = [], + dates = [], + selectedDates = [] + } = calendarData + let __dates = dates + let __selectedDates = selectedDates + const [startDateTimestamp, endDateTimestamp] = chooseAreaTimestamp + if (chooseAreaTimestamp.length === 2) { + __selectedDates = [] + __dates = dates.map(d => { + const date = { ...d } + const dateTimeStamp = dateUtil.getTimeStamp(date) + if ( + dateTimeStamp >= startDateTimestamp && + endDateTimestamp >= dateTimeStamp + ) { + date.choosed = true + __selectedDates.push(date) + } else { + date.choosed = false + __selectedDates = __selectedDates.filter( + item => dateUtil.getTimeStamp(item) !== dateTimeStamp + ) + } + return date + }) + const monthOfStartDate = new Date(startDateTimestamp).getMonth() + const monthOfEndDate = new Date(endDateTimestamp).getMonth() + if (monthOfStartDate !== monthOfEndDate) { + __selectedDates = calcDateWhenNotInOneMonth({ + firstDate: __dates[0], + lastDate: __dates[__dates.length - 1], + startTimestamp: startDateTimestamp, + endTimestamp: endDateTimestamp, + selectedDate: __selectedDates + }) + } + } + return { + calendarData: { + ...calendarData, + dates: __dates, + selectedDates: dateUtil.sortDatesByTime( + dateUtil.uniqueArrayByDate(__selectedDates) + ) + }, + calendarConfig + } + }, + onTapDate(tapedDate, calendarData = {}, calendarConfig = {}) { + if (!calendarConfig.chooseAreaMode) { + return { + calendarData, + calendarConfig + } + } + let { + tempChooseAreaTimestamp = [], + chooseAreaTimestamp: existChooseAreaTimestamp = [], + selectedDates = [], + dates = [] + } = calendarData + const timestamp = dateUtil.getTimeStamp(tapedDate) + let __dates = [...dates] + let __selectedDates = [...selectedDates] + if ( + tempChooseAreaTimestamp.length === 2 || + existChooseAreaTimestamp.length === 2 + ) { + tempChooseAreaTimestamp = [tapedDate] + __selectedDates = [] + __dates.forEach(d => (d.choosed = false)) + } else if (tempChooseAreaTimestamp.length === 1) { + const preChoosedDate = tempChooseAreaTimestamp[0] + const preTimestamp = dateUtil.getTimeStamp(preChoosedDate) + if (preTimestamp <= timestamp) { + tempChooseAreaTimestamp.push(tapedDate) + } else if (preTimestamp > timestamp) { + tempChooseAreaTimestamp.unshift(tapedDate) + } + } else { + tempChooseAreaTimestamp = [tapedDate] + } + let chooseAreaTimestamp = [] + if (tempChooseAreaTimestamp.length === 2) { + const [startDate, endDate] = tempChooseAreaTimestamp + const startDateTimestamp = dateUtil.getTimeStamp(startDate) + const endDateTimestamp = dateUtil.getTimeStamp(endDate) + chooseAreaTimestamp = [startDateTimestamp, endDateTimestamp] + } + return { + calendarData: { + ...calendarData, + chooseAreaTimestamp, + tempChooseAreaTimestamp, + dates: __dates, + selectedDates: __selectedDates + }, + calendarConfig: { + ...calendarConfig, + multi: true + } + } + }, + methods(component) { + return { + /** + * 设置连续日期选择区域 + * @param {array} dateArea 区域开始结束日期数组 + */ + chooseDateArea: (dateArea = []) => { + if (dateArea.length === 1) { + dateArea = dateArea.concat(dateArea) + } + if (dateArea.length !== 2) return + const isRight = validateTimeRange(dateArea) + if (!isRight) return + const config = getCalendarConfig(component) || {} + const { startTimestamp, endTimestamp } = convertTimeRangeToTimestamp( + dateArea + ) + const existCalendarData = getCalendarData('calendar', component) + return renderCalendar.call( + component, + { + ...existCalendarData, + chooseAreaTimestamp: [startTimestamp, endTimestamp] + }, + { + ...config, + multi: true, + chooseAreaMode: true + } + ) + } + } + } + } +} diff --git a/src/components/calendar/plugins/todo.d.ts b/src/components/calendar/plugins/todo.d.ts new file mode 100644 index 0000000..509db18 --- /dev/null +++ b/src/components/calendar/plugins/todo.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/components/calendar/plugins/todo.js b/src/components/calendar/plugins/todo.js new file mode 100644 index 0000000..396a7f4 --- /dev/null +++ b/src/components/calendar/plugins/todo.js @@ -0,0 +1,135 @@ +/** + * @Author: drfu* + * @Description: 代办事项 + * @Date: 2020-10-08 21:22:09* + * @Last Modified by: drfu + * @Last Modified time: 2020-10-11 14:23:02 + * */ + +import { getCalendarData, dateUtil } from '../utils/index' +import { renderCalendar } from '../render' + +function updateDatePropertyOfTodoLabel(todos, dates, showLabelAlways) { + const datesInfo = [...dates] + for (let todo of todos) { + let targetIdx = datesInfo.findIndex( + item => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(todo) + ) + let target = datesInfo[targetIdx] + if (!target) continue + if (showLabelAlways) { + target.showTodoLabel = true + } else { + target.showTodoLabel = !target.choosed + } + if (target.showTodoLabel) { + target.todoText = todo.todoText + } + target.color = todo.color + } + return datesInfo +} + +export default () => { + return { + name: 'todo', + beforeRender(calendarData = {}, calendarConfig = {}, component) { + const { todos = [], dates = [], showLabelAlways } = calendarData + const dateWithTodoInfo = updateDatePropertyOfTodoLabel( + todos, + dates, + showLabelAlways + ) + return { + calendarData: { + ...calendarData, + dates: dateWithTodoInfo + }, + calendarConfig + } + }, + methods(component) { + return { + setTodos: (options = {}) => { + const calendar = getCalendarData('calendar', component) + if (!calendar || !calendar.dates) { + return Promise.reject('请等待日历初始化完成后再调用该方法') + } + const { + circle, + dotColor = '', + pos = 'bottom', + showLabelAlways, + dates: todoDates = [] + } = options + const { todos = [] } = calendar + const tranformStr2NumOfTodo = todoDates.map(date => + dateUtil.tranformStr2NumOfDate(date) + ) + const calendarData = { + dates: calendar.dates, + todos: dateUtil.uniqueArrayByDate( + todos.concat(tranformStr2NumOfTodo) + ) + } + if (!circle) { + calendarData.todoLabelPos = pos + calendarData.todoLabelColor = dotColor + } + calendarData.todoLabelCircle = circle || false + calendarData.showLabelAlways = showLabelAlways || false + const existCalendarData = getCalendarData('calendar', component) + return renderCalendar.call(component, { + ...existCalendarData, + ...calendarData + }) + }, + deleteTodos(todos = []) { + if (!(todos instanceof Array) || !todos.length) + return Promise.reject('deleteTodos()应为入参为非空数组') + const existCalendarData = getCalendarData('calendar', component) + const allTodos = existCalendarData.todos || [] + const toDeleteTodos = todos.map(item => dateUtil.toTimeStr(item)) + const remainTodos = allTodos.filter( + item => !toDeleteTodos.includes(dateUtil.toTimeStr(item)) + ) + const { dates, curYear, curMonth } = existCalendarData + const _dates = [...dates] + const currentMonthTodos = dateUtil.filterDatesByYM( + { + year: curYear, + month: curMonth + }, + remainTodos + ) + _dates.forEach(item => { + item.showTodoLabel = false + }) + currentMonthTodos.forEach(item => { + _dates[item.date - 1].showTodoLabel = !_dates[item.date - 1].choosed + }) + return renderCalendar.call(component, { + ...existCalendarData, + dates: _dates, + todos: remainTodos + }) + }, + clearTodos() { + const existCalendarData = getCalendarData('calendar', component) + const _dates = [...existCalendarData.dates] + _dates.forEach(item => { + item.showTodoLabel = false + }) + return renderCalendar.call(component, { + ...existCalendarData, + dates: _dates, + todos: [] + }) + }, + getTodos() { + return getCalendarData('calendar.todos', component) || [] + } + } + } + } +} diff --git a/src/components/calendar/plugins/week.js b/src/components/calendar/plugins/week.js new file mode 100644 index 0000000..71032bc --- /dev/null +++ b/src/components/calendar/plugins/week.js @@ -0,0 +1,432 @@ +/** + * @Author: drfu* + * @Description: 周视图 + * @Date: 2020-10-08 21:22:09* + * @Last Modified by: drfu + * @Last Modified time: 2020-10-12 14:39:45 + * */ + +import { renderCalendar } from '../render' +import { + getCalendarConfig, + getCalendarData, + logger, + dateUtil +} from '../utils/index' +import { calcJumpData } from '../core' + +/** + * 当月第一周所有日期 + */ +function firstWeekInMonth( + target = {}, + calendarDates = [], + calendarConfig = {} +) { + const { firstDayOfWeek } = calendarConfig + const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon' + const { year, month } = target + let firstDay = dateUtil.getDayOfWeek(year, month, 1) + if (firstDayOfWeekIsMon && firstDay === 0) { + firstDay = 7 + } + const [, end] = [0, 7 - firstDay] + return calendarDates.slice(0, firstDayOfWeekIsMon ? end + 1 : end) +} + +/** + * 当月最后一周所有日期 + */ +function lastWeekInMonth(target = {}, calendarDates = [], calendarConfig = {}) { + const { firstDayOfWeek } = calendarConfig + const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon' + const { year, month } = target + const lastDay = dateUtil.getDatesCountOfMonth(year, month) + let lastDayWeek = dateUtil.getDayOfWeek(year, month, lastDay) + if (firstDayOfWeekIsMon && lastDayWeek === 0) { + lastDayWeek = 7 + } + const [start, end] = [lastDay - lastDayWeek, lastDay] + return calendarDates.slice(firstDayOfWeekIsMon ? start : start - 1, end) +} + +/** + * 判断目标日期是否在某些指定日历内 + */ +function dateIsInDatesRange(target, dates) { + if (!target || !dates || !dates.length) return false + const targetDateStr = dateUtil.toTimeStr(target) + let rst = false + for (let date of dates) { + const dateStr = dateUtil.toTimeStr(date) + if (dateStr === targetDateStr) { + rst = true + return rst + } + rst = false + } + return rst +} + +function getDatesWhenTargetInFirstWeek(target, firstWeekDates) { + const { year, month } = target + const prevMonthInfo = dateUtil.getPrevMonthInfo({ year, month }) + let lastMonthDatesCount = dateUtil.getDatesCountOfMonth( + prevMonthInfo.year, + prevMonthInfo.month + ) + let dates = firstWeekDates + let firstWeekCount = firstWeekDates.length + for (let i = 0; i < 7 - firstWeekCount; i++) { + const week = dateUtil.getDayOfWeek(+year, +month, lastMonthDatesCount) + dates.unshift({ + year: prevMonthInfo.year, + month: prevMonthInfo.month, + date: lastMonthDatesCount, + week + }) + lastMonthDatesCount -= 1 + } + return dates +} + +function getDatesWhenTargetInLastWeek(target, lastWeekDates) { + const { year, month } = target + const prevMonthInfo = dateUtil.getNextMonthInfo({ year, month }) + let dates = lastWeekDates + let lastWeekCount = lastWeekDates.length + for (let i = 0; i < 7 - lastWeekCount; i++) { + const week = dateUtil.getDayOfWeek(+year, +month, i + 1) + dates.push({ + year: prevMonthInfo.year, + month: prevMonthInfo.month, + date: i + 1, + week + }) + } + return dates +} + +function getDates(target, calendarDates = [], calendarConfig = {}) { + const { year, month, date } = target + const targetDay = dateUtil.getDayOfWeek(year, month, date) + const { firstDayOfWeek } = calendarConfig + const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon' + if (firstDayOfWeekIsMon) { + const startIdx = date - (targetDay || 7) + return calendarDates.splice(startIdx, 7) + } else { + const startIdx = date - targetDay - 1 + return calendarDates.splice(startIdx, 7) + } +} + +function getTargetWeekDates(target, calendarConfig) { + if (!target) return + const { year, month } = target + const calendarDates = dateUtil.calcDates(year, month) + const firstWeekDates = firstWeekInMonth(target, calendarDates, calendarConfig) + const lastWeekDates = lastWeekInMonth(target, calendarDates, calendarConfig) + if (dateIsInDatesRange(target, firstWeekDates)) { + return getDatesWhenTargetInFirstWeek(target, firstWeekDates) + } else if (dateIsInDatesRange(target, lastWeekDates)) { + return getDatesWhenTargetInLastWeek(target, lastWeekDates) + } else { + return getDates(target, calendarDates, calendarConfig) + } +} + +/** + * 计算周视图下当前这一周最后一天 + */ +function calculateLastDateOfCurrentWeek(calendarData = {}) { + const { dates = [] } = calendarData + return dates[dates.length - 1] +} +/** + * 计算周视图下当前这一周第一天 + */ +function calculateFirstDateOfCurrentWeek(calendarData = {}) { + const { dates } = calendarData + return dates[0] +} + +/** + * 计算下一周的日期 + */ +function calculateNextWeekDates(calendarData = {}) { + let { curYear, curMonth } = calendarData + let calendarDates = [] + let lastDateInThisWeek = calculateLastDateOfCurrentWeek(calendarData) + const { year: LYear, month: LMonth } = lastDateInThisWeek + if (curYear !== LYear || curMonth !== LMonth) { + calendarDates = dateUtil.calcDates(LYear, LMonth) + curYear = LYear + curMonth = LMonth + } else { + calendarDates = dateUtil.calcDates(curYear, curMonth) + } + const lastDateInThisMonth = dateUtil.getDatesCountOfMonth(curYear, curMonth) + const count = lastDateInThisMonth - lastDateInThisWeek.date + const lastDateIdx = calendarDates.findIndex( + date => dateUtil.toTimeStr(date) === dateUtil.toTimeStr(lastDateInThisWeek) + ) + const startIdx = lastDateIdx + 1 + if (count >= 7) { + return { + dates: calendarDates.splice(startIdx, 7), + year: curYear, + month: curMonth + } + } else { + const nextMonth = dateUtil.getNextMonthInfo({ + year: curYear, + month: curMonth + }) + const { year, month } = nextMonth || {} + const calendarDatesOfNextMonth = dateUtil.calcDates(year, month) + const remainDatesOfThisMonth = calendarDates.splice(startIdx) + const patchDatesOfNextMonth = calendarDatesOfNextMonth.splice( + 0, + 7 - remainDatesOfThisMonth.length + ) + return { + dates: [...remainDatesOfThisMonth, ...patchDatesOfNextMonth], + ...nextMonth + } + } +} + +/** + * 计算上一周的日期 + */ +function calculatePrevWeekDates(calendarData = {}) { + let { curYear, curMonth } = calendarData + let firstDateInThisWeek = calculateFirstDateOfCurrentWeek(calendarData) + let calendarDates = [] + const { year: FYear, month: FMonth } = firstDateInThisWeek + if (curYear !== FYear || curMonth !== FMonth) { + calendarDates = dateUtil.calcDates(FYear, FMonth) + curYear = FYear + curMonth = FMonth + } else { + calendarDates = dateUtil.calcDates(curYear, curMonth) + } + const firstDateIdx = calendarDates.findIndex( + date => dateUtil.toTimeStr(date) === dateUtil.toTimeStr(firstDateInThisWeek) + ) + if (firstDateIdx - 7 >= 0) { + const startIdx = firstDateIdx - 7 + return { + dates: calendarDates.splice(startIdx, 7), + year: curYear, + month: curMonth + } + } else { + const prevMonth = dateUtil.getPrevMonthInfo({ + year: curYear, + month: curMonth + }) + const { year, month } = prevMonth || {} + const calendarDatesOfPrevMonth = dateUtil.calcDates(year, month) + const remainDatesOfThisMonth = calendarDates.splice( + 0, + firstDateInThisWeek.date - 1 + ) + const patchDatesOfPrevMonth = calendarDatesOfPrevMonth.splice( + -(7 - remainDatesOfThisMonth.length) + ) + return { + dates: [...patchDatesOfPrevMonth, ...remainDatesOfThisMonth], + ...prevMonth + } + } +} + +export default () => { + return { + name: 'week', + beforeRender(calendarData = {}, calendarConfig = {}, component) { + const { initializedWeekMode, selectedDates } = calendarData + if (calendarConfig.weekMode && !initializedWeekMode) { + const { defaultDate } = calendarConfig + const target = + (selectedDates && selectedDates[0]) || + (defaultDate && dateUtil.transformDateRow2Dict(defaultDate)) || + dateUtil.todayFMD() + const waitRenderData = this.methods( + component + ).__calcDatesWhenSwitchView('week', target) + const { data, config } = waitRenderData || {} + const setSelectDates = this.methods( + component + ).__selectTargetDateWhenJump(target, data.dates, config) + return { + calendarData: { + ...data, + ...setSelectDates, + weeksCh: dateUtil.getWeekHeader(calendarConfig.firstDayOfWeek), + initializedWeekMode: true + }, + calendarConfig + } + } + return { + calendarData, + calendarConfig + } + }, + onSwitchCalendar(target = {}, calendarData = {}, component) { + const { direction } = target + const { curYear, curMonth } = calendarData + const calendarConfig = getCalendarConfig(component) + let waitRenderData = {} + if (calendarConfig.weekMode) { + if (direction === 'left') { + waitRenderData = calculateNextWeekDates(calendarData) + } else { + waitRenderData = calculatePrevWeekDates(calendarData) + } + const { dates, year, month } = waitRenderData + return { + ...calendarData, + dates, + curYear: year || curYear, + curMonth: month || curMonth + } + } + return calendarData + }, + methods(component) { + return { + __selectTargetDateWhenJump: (target = {}, dates = [], config = {}) => { + let selectedDate = target + const weekDates = dates.map((date, idx) => { + const tmp = { ...date } + tmp.id = idx + const isTarget = + dateUtil.toTimeStr(target) === dateUtil.toTimeStr(tmp) + if (isTarget && !target.choosed && config.autoChoosedWhenJump) { + tmp.choosed = true + selectedDate = tmp + } + return tmp + }) + return { + dates: weekDates, + selectedDates: [selectedDate] + } + }, + __calcDatesForWeekMode(target, config = {}, calendarData = {}) { + const { year, month } = target || {} + const weekDates = getTargetWeekDates(target, config) + weekDates.forEach((date, idx) => (date.id = idx)) + return { + data: { + ...calendarData, + prevMonthGrids: null, + nextMonthGrids: null, + dates: weekDates, + curYear: year, + curMonth: month + }, + config: { + ...config, + weekMode: true + } + } + }, + __calcDatesForMonthMode(target, config = {}, calendarData = {}) { + const { year, month } = target || {} + const waitRenderData = calcJumpData({ + dateInfo: target, + config + }) + return { + data: { + ...calendarData, + ...waitRenderData, + curYear: year, + curMonth: month + }, + config: { + ...config, + weekMode: false + } + } + }, + /** + * 周、月视图切换 + * @param {string} view 视图 [week, month] + * @param {object} target + */ + __calcDatesWhenSwitchView: (view, target) => { + const calendarConfig = getCalendarConfig(component) + if (calendarConfig.multi) + return logger.warn('多选模式不能切换周月视图') + const existCalendarData = getCalendarData('calendar', component) || {} + const { + selectedDates = [], + dates = [], + curYear, + curMonth + } = existCalendarData + const currentMonthSelected = selectedDates.filter( + item => curYear === +item.year || curMonth === +item.month + ) + let jumpTarget = {} + if (target) { + jumpTarget = target + } else { + if (currentMonthSelected.length) { + jumpTarget = currentMonthSelected.pop() + } else { + jumpTarget = dates[0] + } + } + if (view === 'week') { + return this.methods(component).__calcDatesForWeekMode( + jumpTarget, + calendarConfig, + existCalendarData + ) + } else { + return this.methods(component).__calcDatesForMonthMode( + jumpTarget, + calendarConfig, + existCalendarData + ) + } + }, + weekModeJump: dateInfo => { + const target = dateInfo || dateUtil.todayFMD() + const existCalendarData = getCalendarData('calendar', component) || {} + const waitRenderData = this.methods( + component + ).__calcDatesWhenSwitchView('week', target) + const { data, config } = waitRenderData || {} + const setSelectDates = this.methods( + component + ).__selectTargetDateWhenJump(target, data.dates, config) + return renderCalendar.call( + component, + { + ...existCalendarData, + ...data, + ...setSelectDates + }, + config + ) + }, + switchView: (view, target) => { + const waitRenderData = this.methods( + component + ).__calcDatesWhenSwitchView(view, target) + const { data, config } = waitRenderData || {} + if (!data) return logger.warn('当前状态不能切换为周视图') + return renderCalendar.call(component, data, config) + } + } + } + } +} diff --git a/src/components/calendar/render.js b/src/components/calendar/render.js new file mode 100644 index 0000000..47ea457 --- /dev/null +++ b/src/components/calendar/render.js @@ -0,0 +1,49 @@ +import plugins from "./plugins/index"; +import { getCalendarConfig } from "./utils/index"; + +/** + * 渲染日历 + */ +export function renderCalendar(calendarData, calendarConfig) { + return new Promise((resolve) => { + const Component = this; + if (Component.firstRender === void 0) { + Component.firstRender = true; + } else { + Component.firstRender = false; + } + const exitData = Component.data.calendar || {}; + for (let plugin of plugins.installed) { + const [, p] = plugin; + if (typeof p.beforeRender === "function") { + const { calendarData: newData, calendarConfig: newConfig } = + p.beforeRender( + { ...exitData, ...calendarData }, + calendarConfig || getCalendarConfig(Component), + Component + ); + calendarData = newData; + calendarConfig = newConfig; + } + } + + Component.setData( + { + config: calendarConfig, + calendar: calendarData, + }, + () => { + const rst = { + calendar: calendarData, + config: calendarConfig, + firstRender: Component.firstRender, + }; + resolve(rst); + if (Component.firstRender) { + Component.triggerEvent("afterCalendarRender", rst); + Component.firstRender = false; + } + } + ); + }); +} diff --git a/src/components/calendar/theme/iconfont.wxss b/src/components/calendar/theme/iconfont.wxss new file mode 100644 index 0000000..062b3c6 --- /dev/null +++ b/src/components/calendar/theme/iconfont.wxss @@ -0,0 +1,29 @@ +@font-face { + font-family: 'iconfont'; + src: url(data:font/truetype;charset=utf-8;base64,AAEAAAANAIAAAwBQRkZUTYda3jUAAAfEAAAAHEdERUYAKQANAAAHpAAAAB5PUy8yPllJ4AAAAVgAAABWY21hcAAP65kAAAHIAAABQmdhc3D//wADAAAHnAAAAAhnbHlmLotR3AAAAxwAAAGkaGVhZBTU+ykAAADcAAAANmhoZWEHKwOFAAABFAAAACRobXR4DasB4gAAAbAAAAAWbG9jYQC0AR4AAAMMAAAAEG1heHABEwAyAAABOAAAACBuYW1lKeYRVQAABMAAAAKIcG9zdEoLnOYAAAdIAAAAUgABAAAAAQAAiPM8al8PPPUACwQAAAAAANjbW5YAAAAA2NtblgCzAAQDTQL8AAAACAACAAAAAAAAAAEAAAOA/4AAXAQAAAAAAANNAAEAAAAAAAAAAAAAAAAAAAAEAAEAAAAHACYAAgAAAAAAAgAAAAoACgAAAP8AAAAAAAAAAQQAAZAABQAAAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA5+vn7gOA/4AAXAOAAIAAAAABAAAAAAAABAAAAAAAAAAEAAAABAABLgD4ALQAswAAAAAAAwAAAAMAAAAcAAEAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAA5+7//wAA5+v//xgYAAEAAAAAAAABBgAAAQAAAAAAAAABAgAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJgBMAI4A0gABAS4ABAMKAvwAEgAACQEmBh0BFBcJAQYdARQWNwE2NAL+/j0ECQYBaP6YBgkEAcMMAZkBYAMEBU0IBf7n/ucFCE0FBAMBYAoeAAAAAQD4AAQC1AL8ABIAAAE1NCYHAQYUFwEWNj0BNCcJATYC1AkE/j0MDAHDBAkG/pgBaAYCpk0FBAP+oAoeCv6gAwQFTQgFARkBGQUAAAIAtAAgA00C4AASACUAAAkBNiYrASIHAwYUFwEWOwEyNicTATYmKwEiBwMGFBcBFjsBMjYnAREBCQMEBU0IBf8HBwD/BQhNBQQDJwEJAwQFTQgF/wcHAP8FCE0FBAMBgAFTBAkG/roJFgn+ugYJBAFTAVMECQb+ugkWCf66BgkEAAAAAAIAswAgA0wC4AASACUAAAEDJisBIgYXCQEGFjsBMjcBNjQlAyYrASIGFwkBBhY7ATI3ATY0AhX/BQhNBQQDAQn+9wMEBU0IBQD/BwEp/wUITQUEAwEJ/vcDBAVNCAUA/wcBlAFGBgkE/q3+rQQJBgFGCRYJAUYGCQT+rf6tBAkGAUYJFgAAAAAAABIA3gABAAAAAAAAABUALAABAAAAAAABAAgAVAABAAAAAAACAAcAbQABAAAAAAADAAgAhwABAAAAAAAEAAgAogABAAAAAAAFAAsAwwABAAAAAAAGAAgA4QABAAAAAAAKACsBQgABAAAAAAALABMBlgADAAEECQAAACoAAAADAAEECQABABAAQgADAAEECQACAA4AXQADAAEECQADABAAdQADAAEECQAEABAAkAADAAEECQAFABYAqwADAAEECQAGABAAzwADAAEECQAKAFYA6gADAAEECQALACYBbgAKAEMAcgBlAGEAdABlAGQAIABiAHkAIABpAGMAbwBuAGYAbwBuAHQACgAACkNyZWF0ZWQgYnkgaWNvbmZvbnQKAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABSAGUAZwB1AGwAYQByAABSZWd1bGFyAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABWAGUAcgBzAGkAbwBuACAAMQAuADAAAFZlcnNpb24gMS4wAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAABHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuAABoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAABodHRwOi8vZm9udGVsbG8uY29tAAACAAAAAAAAAAoAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAcAAAABAAIBAgEDAQQBBQVyaWdodARsZWZ0CmRvdWJsZWxlZnQLZG91YmxlcmlnaHQAAAAAAAH//wACAAEAAAAMAAAAFgAAAAIAAQADAAYAAQAEAAAAAgAAAAAAAAABAAAAANWkJwgAAAAA2NtblgAAAADY21uW) format('truetype'); + font-weight: normal; + font-style: normal; +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; +} + +.icon-right::before { + content: "\e7eb"; +} + +.icon-left::before { + content: "\e7ec"; +} + +.icon-doubleleft::before { + content: "\e7ed"; +} + +.icon-doubleright::before { + content: "\e7ee"; +} diff --git a/src/components/calendar/theme/theme-default.wxss b/src/components/calendar/theme/theme-default.wxss new file mode 100644 index 0000000..03887df --- /dev/null +++ b/src/components/calendar/theme/theme-default.wxss @@ -0,0 +1,61 @@ + +/* 日历主要颜色相关样式 */ + +.default_color, +.default_weekend-color, +.default_handle-color, +.default_week-color { + color: #ff629a; +} + +.default_today { + color: #fff; + background-color: #874fb4; +} + +.default_choosed { + color: #fff; + background-color: #ff629a; +} + +.default_date-disable { + color: #c7c7c7; +} + +.default_choosed.default_date-disable { + color: #e2e2e2; + background-color: #c2afb6; +} + +.default_prev-month-date, +.default_next-month-date { + color: #e2e2e2; +} + +.default_normal-date { + color: #88d2ac; +} + +.default_todo-circle { + border-color: #88d2ac; +} + +.default_todo-dot { + background-color: #e54d42; +} + +.default_date-desc { + color: #c2c2c2; +} + +.default_date-desc-lunar { + color: #e54d42; +} + +.default_date-desc-disable { + color: #e2e2e2; +} + +.default_festival { + color: #c2c2c2; +} diff --git a/src/components/calendar/theme/theme-elegant.wxss b/src/components/calendar/theme/theme-elegant.wxss new file mode 100644 index 0000000..0f3a7b2 --- /dev/null +++ b/src/components/calendar/theme/theme-elegant.wxss @@ -0,0 +1,58 @@ +.elegant_color, +.elegant_weekend-color, +.elegant_handle-color, +.elegant_week-color { + color: #333; +} + +.elegant_today { + color: #000; + background-color: #e1e7f5; +} + +.elegant_choosed { + color: #000; + background-color: #e2e2e2; +} + +.elegant_date-disable { + color: #c7c7c7; +} + +.elegant_choosed.elegant_date-disable { + color: #999; + background-color: #ebebeb; +} + +.elegant_prev-month-date, +.elegant_next-month-date { + color: #e2e2e2; +} + +.elegant_normal-date { + color: #777; +} + +.elegant_todo-circle { + border-color: #161035; +} + +.elegant_todo-dot { + background-color: #161035; +} + +.elegant_date-desc { + color: #c2c2c2; +} + +.elegant_date-desc-lunar { + color: #161035; +} + +.elegant_date-desc-disable { + color: #e2e2e2; +} + +.elegant_festival { + color: #c2c2c2; +} diff --git a/src/components/calendar/theme/theme-nuohe.wxss b/src/components/calendar/theme/theme-nuohe.wxss new file mode 100644 index 0000000..030f31d --- /dev/null +++ b/src/components/calendar/theme/theme-nuohe.wxss @@ -0,0 +1,90 @@ +/* 日历主要颜色相关样式 */ + +.nuohe_color, +.nuohe_weekend-color, +.nuohe_handle-color, +.nuohe_week-color { + color: rgba(51, 51, 51, 1); +} + +.nuohe_today { + color: #fff; + background-color: #874fb4; +} + +.nuohe_choosed { + color: rgba(51, 51, 51, 1); + background-color: rgba(189, 189, 189, 0.3); +} + +.nuohe_date-disable { + color: #c7c7c7; +} + +.nuohe_choosed.nuohe_date-disable { + color: #e2e2e2; + background-color: #c2afb6; +} + +.nuohe_prev-month-date, +.nuohe_next-month-date { + color: #e2e2e2; +} + +.nuohe_normal-date { + color: rgba(51, 51, 51, 1); +} + +.nuohe_todo-circle { + border-color: #88d2ac; +} + +.nuohe_todo-dot { + background-color: #e54d42; +} + +.nuohe_date-desc { + color: #c2c2c2; +} + +.nuohe_date-desc-lunar { + color: #e54d42; +} + +.nuohe_date-desc-disable { + color: #e2e2e2; +} + +.nuohe_festival { + color: #c2c2c2; +} + +.date.doc { + position: relative; + background-color: #e5e5e5; +} + +.date.doc::after { + position: absolute; + z-index: 1; + top: -5rpx; + right: -5rpx; + content: ""; + width: 17rpx; + height: 24rpx; + background-repeat: no-repeat; + background-size: cover; + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAABACAYAAABhspUgAAAAAXNSR0IArs4c6QAACAVJREFUaEPtmltsHFcZx/9nZu92bMdJSMitzoWkoXVKUrVV8wKk4gHxgJTwgKA8lhfgCZBoUJEqUYGA0AhRQUSpFJQIHlyqogZFIipUoKiAKJAYQkp9qR1ir9fe6+zuXM4FfWf2MrOJY+/aS0zlkRxZzsw5v/nP/3znO+c7DItcl4vFTWVl/7wg5EctKSK2knCVBFcKcrGH7vL3MvfwVnYeGbsaustgAAODCSDCDLU1Hp/Ym+o7fnrvB9+9U3NssT5ezWe+k5X8qwvCQ0FwVKSEswJg6sfmHH+cm2l0SZ3Tj8F84ChjiDID+1I9ly4ceuTjbQFfyM2MpDk/meEu8oKjJIUG9khh1YHEtUeupgPAWl3AAENEwzLEmIGd8dToq8OPDbcF/OLCrZFZ7p5Mcxc5wVEUAmQLT8mOLFHvfCwzF7ZEC3CcGdiRSIxeHD7WHvDZhZsjM56ngbMELDlsWVO4c4ExOd8E9i1R969vhzhj2BFPjv76cJvApPCtkMK8YYkVOALLUXh7PNE+8M/Iw553MiN8S1ii6eGVAI+mZ0PfhzHoAWfWBlyMMWyPJ0dfG368PUu8nJ8bWRD8ZPYOUWIlwH+YmQ4Dg8FoAPu22J5Ijb7ywKPtAV8uZZ/LC36qIH11q7UIIZRCp8CW5+Ly7M3bgVkzUlC0ONC74VcvHTj6ybaiBN38RmVhV9YxTBs2nrl08VTRrjy1gvEGZRh+HGu5epOp808ceehMFFEkDYN/f++Df1+sn0UnjtYHjB9+63tQ6ssrAV70WcZOyy8+/ZXltL0o8Hfzk3u4jc94Bh7nSm0uOZWNruvFozBMkzEG+iF3KGm5XJTAZFVIxRXCjnGkNB0pIwXPTWYcu6/MeYLAgh1HozHeE48VE6Y5vyFiXknFoufO73/4nWVZ4kwmc9BG5QVLiONcKSYYEFU0z/vd1P3rQaEiBYiwnSvr2Jgo5mFxTz9Gcbj+AjTjmczPKbbF41fej9jnzz5w9J/B9kMKP58e/1xO4ayjZNKlwaUUkqYJJZsDjRKfBcH1dN3pRa3dKpUwX61oqRs5RS1amLWpOmWYzv5k7xfO3X/0p/W+GsBnMtNPLQjvx1UlDUcKQDEkDKanYRJR6W+tMM0dzPPOYYMvmauWkStXfOBA1kYxWecWMJAwDHV/T9+Xzh088kLDSmfmpx9b8Lw3qpDxqv7MCilmaFBJ/9aMmZYcY05VK78aFw2DrGXBdp2AymSLQDIEAynD9B4c3PKRnwwduqIV/ubc5N+Kgj9EnqQEhxIQ+iwyEHPJIm+WSxBMj7TV4NX+lVIiXyzURGCg/NjP3ijdNEAzH/FsjcX/dWn42CH2g+zMJ9Ju5bWyFDrnFQB6GMGSur4N6Bpzq5hwnVUBbW2kWq3CcXyV6QoMvgZwghk43Dtwgn17burlrHBPaGAlEQFDnN68Bku41NDvrQLsVVK2FZhzAcsqNYCpP/rCZA2tsGGAgIeSvRfZs3Pj0wUhdpZr028vo/cj4OaHJ+WvVIpdUbfeaKHg26KpMoU3P7cgaALeHIun2TPpcaskeE9ZSp0v9DPTjwoB4Cytx+xyV4FLxSKE9FeL9TDnZ3D+KoSA+yIRm52aGVOWEiBLVKXEoBHRdtDRoXbNcQ9XuwxMliBrhIBr8VgDGwxJZoI9TcCSo6wkHCmxUQOHV8Zz3MVVu9JVha2SBR6YjCjlrK/1tCUMwwf+WgDYrQGLlsC1VoDJFiHgoMLBSLsWgClSJOvAJdncd6hbYi0B08DzLVFTeB24g+F5t0G3rnAHgt72yLrC93LieO952ARHVDWXQQVuYbqaWQ2rLtrGVJmhEljR6pyRFqS1fIKS+LjB/JmuNQ7vlrMYUs1dxp3udRwrvdJV4GedJ3BDbl6yj1UFdgsJVNMbQp0mt5UQ67OXBLknwOO/OILi2KYQXN++Bez99F/XJvA75x+G9e7GEFzvfTnsf/Iv68CNKhFla50OujWtsD3fA2tyMPSpM3/eBSebCv0tPljBlkfCm9i9Q1kkNofXiF0fdJO/HEb++tYlvXmnGwYOpTF04lrov7oOPDFyGIUb7+sIuP9gBns+Fd6zXgdunen+7xT+z28OIPOn3R1ZYsujU9jxsbf/tx6m3pQMVxzGLhyFNdUycezOYd9n3wrBMeP23c+ue/hO0q7pOLwOfC/Sy1uvfwDZa9tC4g8Oz2L78X8vOTjviYeXpLrLDSsC3qqy2KZyjeYH3EnsKf92JTxLPvti9UOYkv233eefWKEqE52rWGSJFNx9pxbu9Wbge2/VvOYVru/Ar+UN7cb+8FosGTRrz35RpulhE+zrs1TjoKKMf5BuIzN1cTFYRcpwD6NOd2scxRIVZZqbN/q0la7sN6tIumTwjdlxXUWqCAIW6KeiTMtxxXnh4boTPoK4ZJxq84ZssQgvAEwl3HpVXyusK0kG2HNzk/mSEP0VJfS5NCosKjq7USuAU15lKYkbXa4i3czlwOkUQe0KAtfrzf2RqMVOZ6Z+VxD8w1RUJEtElV9JDx4Ipd8nXFtX+btxOULg7ay/d9eohDL/tFX9PCZB70qk3mQ/ys0M5T3nWlXKXqrYcyaRpFtrRw7qiPQyRa3Aso8JLevdqAg/mptHwW1uZ9XPYxIwiUc/GyLRyuH+LcO6d4KuCv5SRYojrpQDFIcjMLTp60dcdMJel6D1YM+y0MI3uUIg57n4RyGLoueGE/xar2SLHsO0NsXi13b1DDz5/H0Hxv8Lo3UyF0Y2DrcAAAAASUVORK5CYII="); +} + +.date.error { + background-color: rgba(207, 83, 117, 1); + border-radius: 50%; + color: #fff; +} + +.date.primary { + background-color: rgba(37, 217, 200, 1); + color: #fff; + border-radius: 50%; +} diff --git a/src/components/calendar/utils/index.js b/src/components/calendar/utils/index.js new file mode 100644 index 0000000..2f8eb1d --- /dev/null +++ b/src/components/calendar/utils/index.js @@ -0,0 +1,275 @@ +import Logger from "./logger"; +import WxData from "./wxData"; + +let systemInfo; +export function getSystemInfo() { + if (systemInfo) return systemInfo; + systemInfo = wx.getSystemInfoSync(); + return systemInfo; +} + +export function isIos() { + const sys = getSystemInfo(); + return /iphone|ios/i.test(sys.platform); +} + +class Gesture { + /** + * 左滑 + * @param {object} e 事件对象 + * @returns {boolean} 布尔值 + */ + isLeft(gesture = {}, touche = {}) { + const { startX, startY } = gesture; + const deltaX = touche.clientX - startX; + const deltaY = touche.clientY - startY; + if (deltaX < -60 && deltaY < 20 && deltaY > -20) { + return true; + } else { + return false; + } + } + /** + * 右滑 + * @param {object} e 事件对象 + * @returns {boolean} 布尔值 + */ + isRight(gesture = {}, touche = {}) { + const { startX, startY } = gesture; + const deltaX = touche.clientX - startX; + const deltaY = touche.clientY - startY; + + if (deltaX > 60 && deltaY < 20 && deltaY > -20) { + return true; + } else { + return false; + } + } +} + +class DateUtil { + newDate(year, month, date) { + let cur = `${+year}-${+month}-${+date}`; + if (isIos()) { + cur = `${+year}/${+month}/${+date}`; + } + return new Date(cur); + } + /** + * 计算指定日期时间戳 + * @param {object} date + */ + getTimeStamp(dateInfo) { + if (typeof dateInfo === "string") { + dateInfo = this.transformDateRow2Dict(dateInfo); + } + if (Object.prototype.toString.call(dateInfo) !== "[object Object]") return; + const dateUtil = new DateUtil(); + return dateUtil.newDate(dateInfo.year, dateInfo.month, dateInfo.date).getTime(); + } + /** + * 计算指定月份共多少天 + * @param {number} year 年份 + * @param {number} month 月份 + */ + getDatesCountOfMonth(year, month) { + return new Date(Date.UTC(year, month, 0)).getUTCDate(); + } + /** + * 计算指定月份第一天星期几 + * @param {number} year 年份 + * @param {number} month 月份 + */ + firstDayOfWeek(year, month) { + return new Date(Date.UTC(year, month - 1, 1)).getUTCDay(); + } + /** + * 计算指定日期星期几 + * @param {number} year 年份 + * @param {number} month 月份 + * @param {number} date 日期 + */ + getDayOfWeek(year, month, date) { + return new Date(Date.UTC(year, month - 1, date)).getUTCDay(); + } + todayFMD() { + const _date = new Date(); + const year = _date.getFullYear(); + const month = _date.getMonth() + 1; + const date = _date.getDate(); + return { + year: +year, + month: +month, + date: +date, + }; + } + todayTimestamp() { + const { year, month, date } = this.todayFMD(); + const timestamp = this.newDate(year, month, date).getTime(); + return timestamp; + } + toTimeStr(dateInfo = {}) { + return `${+dateInfo.year}-${+dateInfo.month}-${+dateInfo.date}`; + } + transformDateRow2Dict(dateStr) { + if (typeof dateStr === "string" && dateStr.includes("-")) { + const [year, month, date] = dateStr.split("-"); + return this.tranformStr2NumOfDate({ + year, + month, + date, + }); + } + return {}; + } + tranformStr2NumOfDate(date = {}) { + const target = { ...date }; + // 可能传入字符串 + target.year = +target.year; + target.month = +target.month; + target.date = +target.date; + return target; + } + sortDatesByTime(dates = [], sortType) { + return dates.sort((a, b) => { + const at = this.getTimeStamp(a); + const bt = this.getTimeStamp(b); + if (at < bt && sortType !== "desc") { + return -1; + } else { + return 1; + } + }); + } + getPrevMonthInfo(date = {}) { + const prevMonthInfo = + Number(date.month) > 1 + ? { + year: +date.year, + month: Number(date.month) - 1, + } + : { + year: Number(date.year) - 1, + month: 12, + }; + return prevMonthInfo; + } + getNextMonthInfo(date = {}) { + const nextMonthInfo = + Number(date.month) < 12 + ? { + year: +date.year, + month: Number(date.month) + 1, + } + : { + year: Number(date.year) + 1, + month: 1, + }; + return nextMonthInfo; + } + getPrevYearInfo(date = {}) { + return { + year: Number(date.year) - 1, + month: +date.month, + }; + } + getNextYearInfo(date = {}) { + return { + year: Number(date.year) + 1, + month: +date.month, + }; + } + findDateIndexInArray(target, dates) { + return dates.findIndex((item) => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(target)); + } + calcDates(year, month) { + const datesCount = this.getDatesCountOfMonth(year, month); + const dates = []; + const today = dateUtil.todayFMD(); + for (let i = 1; i <= datesCount; i++) { + const week = dateUtil.getDayOfWeek(+year, +month, i); + const date = { + year: +year, + id: i - 1, + month: +month, + date: i, + week, + isToday: +today.year === +year && +today.month === +month && i === +today.date, + }; + dates.push(date); + } + return dates; + } + /** + * 日期数组根据日期去重 + * @param {array} array 数组 + */ + uniqueArrayByDate(array = []) { + let uniqueObject = {}; + let uniqueArray = []; + array.forEach((item) => { + uniqueObject[dateUtil.toTimeStr(item)] = item; + }); + for (let i in uniqueObject) { + uniqueArray.push(uniqueObject[i]); + } + return uniqueArray; + } + /** + * 筛选指定年月日期 + * @param {object} target 指定年月 + * @param {array} dates 待筛选日期 + */ + filterDatesByYM(target, dates) { + if (target) { + const { year, month } = target; + const _dates = dates.filter((item) => +item.year === +year && +item.month === +month); + return _dates; + } + return dates; + } + getWeekHeader(firstDayOfWeek) { + let weeksCh = ["日", "一", "二", "三", "四", "五", "六"]; + if (firstDayOfWeek === "Mon") { + weeksCh = ["一", "二", "三", "四", "五", "六", "日"]; + } + return weeksCh; + } +} + +/** + * 获取当前页面实例 + */ +export function getCurrentPage() { + const pages = getCurrentPages() || []; + const last = pages.length - 1; + return pages[last] || {}; +} + +export function getComponentById(componentId) { + const logger = new Logger(); + let page = getCurrentPage() || {}; + if (page.selectComponent && typeof page.selectComponent === "function") { + if (componentId) { + return page.selectComponent(componentId); + } else { + logger.warn("请传入组件ID"); + } + } else { + logger.warn("该基础库暂不支持多个小程序日历组件"); + } +} + +export const logger = new Logger(); +export const calendarGesture = new Gesture(); +export const dateUtil = new DateUtil(); +export const getCalendarData = (key, component) => new WxData(component).getData(key); +export const setCalendarData = (data, component) => new WxData(component).setData(data); +export const getCalendarConfig = (component) => getCalendarData("config", component); +export const setCalendarConfig = (config, component) => + setCalendarData( + { + config, + }, + component, + ); diff --git a/src/components/calendar/utils/logger.js b/src/components/calendar/utils/logger.js new file mode 100644 index 0000000..5100ec5 --- /dev/null +++ b/src/components/calendar/utils/logger.js @@ -0,0 +1,23 @@ +export default class Logger { + info(msg) { + console.log( + '%cInfo: %c' + msg, + 'color:#FF0080;font-weight:bold', + 'color: #FF509B' + ) + } + warn(msg) { + console.log( + '%cWarn: %c' + msg, + 'color:#FF6600;font-weight:bold', + 'color: #FF9933' + ) + } + tips(msg) { + console.log( + '%cTips: %c' + msg, + 'color:#00B200;font-weight:bold', + 'color: #00CC33' + ) + } +} diff --git a/src/components/calendar/utils/wxData.js b/src/components/calendar/utils/wxData.js new file mode 100644 index 0000000..463ef3e --- /dev/null +++ b/src/components/calendar/utils/wxData.js @@ -0,0 +1,30 @@ +class WxData { + constructor(component) { + this.Component = component + } + getData(key) { + const data = this.Component.data + if (!key) return data + if (key.includes('.')) { + let keys = key.split('.') + const tmp = keys.reduce((prev, next) => { + return prev[next] + }, data) + return tmp + } else { + return this.Component.data[key] + } + } + setData(data) { + return new Promise((resolve, reject) => { + if (!data) return reject('no data to set') + if (typeof data === 'object') { + this.Component.setData(data, () => { + resolve(data) + }) + } + }) + } +} + +export default WxData diff --git a/src/components/ec-canvas/ec-canvas.js b/src/components/ec-canvas/ec-canvas.js new file mode 100644 index 0000000..1b4c9aa --- /dev/null +++ b/src/components/ec-canvas/ec-canvas.js @@ -0,0 +1,271 @@ +import WxCanvas from './wx-canvas'; + +let ctx; +let echarts; + +function compareVersion(v1, v2) { + v1 = v1.split('.'); + v2 = v2.split('.'); + const len = Math.max(v1.length, v2.length); + + while (v1.length < len) { + v1.push('0'); + } + while (v2.length < len) { + v2.push('0'); + } + + for (let i = 0; i < len; i++) { + const num1 = parseInt(v1[i]); + const num2 = parseInt(v2[i]); + + if (num1 > num2) { + return 1; + } else if (num1 < num2) { + return -1; + } + } + return 0; +} + +Component({ + properties: { + canvasId: { + type: String, + value: 'ec-canvas', + }, + + ec: { + type: Object, + }, + + forceUseOldCanvas: { + type: Boolean, + value: false, + }, + }, + + data: { + isUseNewCanvas: false, + }, + + ready: async function () { + echarts = await require.async('../../gift/compontnts/echart/echarts.js'); + // Disable prograssive because drawImage doesn't support DOM as parameter + // See https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html + echarts.registerPreprocessor((option) => { + if (option && option.series) { + if (option.series.length > 0) { + option.series.forEach((series) => { + series.progressive = 0; + }); + } else if (typeof option.series === 'object') { + option.series.progressive = 0; + } + } + }); + + if (!this.data.ec) { + console.warn( + '组件需绑定 ec 变量,例:', + ); + return; + } + + if (!this.data.ec.lazyLoad) { + this.init(); + } + }, + + methods: { + init: async function (callback) { + echarts = await require.async('../../gift/compontnts/echart/echarts.js'); + const version = wx.getSystemInfoSync().SDKVersion; + + const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0; + const forceUseOldCanvas = this.data.forceUseOldCanvas; + const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas; + this.setData({ isUseNewCanvas }); + + if (forceUseOldCanvas && canUseNewCanvas) { + console.warn('开发者强制使用旧canvas,建议关闭'); + } + + if (isUseNewCanvas) { + // console.log('微信基础库版本大于2.9.0,开始使用'); + // 2.9.0 可以使用 + this.initByNewWay(callback); + } else { + const isValid = compareVersion(version, '1.9.91') >= 0; + if (!isValid) { + console.error( + '微信基础库版本过低,需大于等于 1.9.91。' + + '参见:https://github.com/ecomfe/echarts-for-weixin' + + '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82', + ); + return; + } else { + console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能'); + this.initByOldWay(callback); + } + } + }, + + initByOldWay(callback) { + // 1.9.91 <= version < 2.9.0:原来的方式初始化 + ctx = wx.createCanvasContext(this.data.canvasId, this); + const canvas = new WxCanvas(ctx, this.data.canvasId, false); + + echarts.setCanvasCreator(() => { + return canvas; + }); + // const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr + const canvasDpr = 1; + var query = wx.createSelectorQuery().in(this); + query + .select('.ec-canvas') + .boundingClientRect((res) => { + if (typeof callback === 'function') { + this.chart = callback(canvas, res.width, res.height, canvasDpr); + } else if (this.data.ec && typeof this.data.ec.onInit === 'function') { + this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr); + } else { + this.triggerEvent('init', { + canvas: canvas, + width: res.width, + height: res.height, + canvasDpr: canvasDpr, // 增加了dpr,可方便外面echarts.init + }); + } + }) + .exec(); + }, + + initByNewWay(callback) { + // version >= 2.9.0:使用新的方式初始化 + const query = wx.createSelectorQuery().in(this); + query + .select('.ec-canvas') + .fields({ node: true, size: true }) + .exec((res) => { + const canvasNode = res[0].node; + this.canvasNode = canvasNode; + + const canvasDpr = wx.getSystemInfoSync().pixelRatio; + const canvasWidth = res[0].width; + const canvasHeight = res[0].height; + + const ctx = canvasNode.getContext('2d'); + + const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode); + echarts.setCanvasCreator(() => { + return canvas; + }); + + if (typeof callback === 'function') { + this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr); + } else if (this.data.ec && typeof this.data.ec.onInit === 'function') { + this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr); + } else { + this.triggerEvent('init', { + canvas: canvas, + width: canvasWidth, + height: canvasHeight, + dpr: canvasDpr, + }); + } + }); + }, + canvasToTempFilePath(opt) { + if (this.data.isUseNewCanvas) { + // 新版 + const query = wx.createSelectorQuery().in(this); + query + .select('.ec-canvas') + .fields({ node: true, size: true }) + .exec((res) => { + const canvasNode = res[0].node; + opt.canvas = canvasNode; + wx.canvasToTempFilePath(opt); + }); + } else { + // 旧的 + if (!opt.canvasId) { + opt.canvasId = this.data.canvasId; + } + ctx.draw(true, () => { + wx.canvasToTempFilePath(opt, this); + }); + } + }, + + touchStart(e) { + if (this.chart && e.touches.length > 0) { + var touch = e.touches[0]; + var handler = this.chart.getZr().handler; + handler.dispatch('mousedown', { + zrX: touch.x, + zrY: touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {}, + }); + handler.dispatch('mousemove', { + zrX: touch.x, + zrY: touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {}, + }); + handler.processGesture(wrapTouch(e), 'start'); + } + }, + + touchMove(e) { + if (this.chart && e.touches.length > 0) { + var touch = e.touches[0]; + var handler = this.chart.getZr().handler; + handler.dispatch('mousemove', { + zrX: touch.x, + zrY: touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {}, + }); + handler.processGesture(wrapTouch(e), 'change'); + } + }, + + touchEnd(e) { + if (this.chart) { + const touch = e.changedTouches ? e.changedTouches[0] : {}; + var handler = this.chart.getZr().handler; + handler.dispatch('mouseup', { + zrX: touch.x, + zrY: touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {}, + }); + handler.dispatch('click', { + zrX: touch.x, + zrY: touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {}, + }); + handler.processGesture(wrapTouch(e), 'end'); + } + }, + }, +}); + +function wrapTouch(event) { + for (let i = 0; i < event.touches.length; ++i) { + const touch = event.touches[i]; + touch.offsetX = touch.x; + touch.offsetY = touch.y; + } + return event; +} diff --git a/src/components/ec-canvas/ec-canvas.json b/src/components/ec-canvas/ec-canvas.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/src/components/ec-canvas/ec-canvas.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/src/components/ec-canvas/ec-canvas.wxml b/src/components/ec-canvas/ec-canvas.wxml new file mode 100644 index 0000000..88826d9 --- /dev/null +++ b/src/components/ec-canvas/ec-canvas.wxml @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/ec-canvas/ec-canvas.wxss b/src/components/ec-canvas/ec-canvas.wxss new file mode 100644 index 0000000..5307021 --- /dev/null +++ b/src/components/ec-canvas/ec-canvas.wxss @@ -0,0 +1,5 @@ +.ec-canvas { + width: 100%; + height: 100%; + z-index: 1; +} diff --git a/src/components/ec-canvas/wx-canvas.js b/src/components/ec-canvas/wx-canvas.js new file mode 100644 index 0000000..6c7c90b --- /dev/null +++ b/src/components/ec-canvas/wx-canvas.js @@ -0,0 +1,111 @@ +export default class WxCanvas { + constructor(ctx, canvasId, isNew, canvasNode) { + this.ctx = ctx; + this.canvasId = canvasId; + this.chart = null; + this.isNew = isNew + if (isNew) { + this.canvasNode = canvasNode; + } + else { + this._initStyle(ctx); + } + + // this._initCanvas(zrender, ctx); + + this._initEvent(); + } + + getContext(contextType) { + if (contextType === '2d') { + return this.ctx; + } + } + + // canvasToTempFilePath(opt) { + // if (!opt.canvasId) { + // opt.canvasId = this.canvasId; + // } + // return wx.canvasToTempFilePath(opt, this); + // } + + setChart(chart) { + this.chart = chart; + } + + addEventListener() { + // noop + } + + attachEvent() { + // noop + } + + detachEvent() { + // noop + } + + _initCanvas(zrender, ctx) { + zrender.util.getContext = function () { + return ctx; + }; + + zrender.util.$override('measureText', function (text, font) { + ctx.font = font || '12px sans-serif'; + return ctx.measureText(text); + }); + } + + _initStyle(ctx) { + ctx.createRadialGradient = () => { + return ctx.createCircularGradient(arguments); + }; + } + + _initEvent() { + this.event = {}; + const eventNames = [{ + wxName: 'touchStart', + ecName: 'mousedown' + }, { + wxName: 'touchMove', + ecName: 'mousemove' + }, { + wxName: 'touchEnd', + ecName: 'mouseup' + }, { + wxName: 'touchEnd', + ecName: 'click' + }]; + eventNames.forEach(name => { + this.event[name.wxName] = e => { + const touch = e.touches[0]; + this.chart.getZr().handler.dispatch(name.ecName, { + zrX: name.wxName === 'tap' ? touch.clientX : touch.x, + zrY: name.wxName === 'tap' ? touch.clientY : touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {} + }); + }; + }); + } + + set width(w) { + if (this.canvasNode) this.canvasNode.width = w + } + set height(h) { + if (this.canvasNode) this.canvasNode.height = h + } + + get width() { + if (this.canvasNode) + return this.canvasNode.width + return 0 + } + get height() { + if (this.canvasNode) + return this.canvasNode.height + return 0 + } +} diff --git a/src/components/freeAudio/index.js b/src/components/freeAudio/index.js index 8b916f4..042cfd5 100644 --- a/src/components/freeAudio/index.js +++ b/src/components/freeAudio/index.js @@ -1,176 +1,176 @@ -const app = getApp(); -import dayjs from "dayjs"; -Component({ - behaviors: [], - properties: { - audio: { - type: Object, - observer(val) { - if (this.audioContext) { - const { play } = this.data; - if (play) { - this.audioContext.pause(); - this.setData({ - play: false, - progress: 0, - time: "00", - }); - } - this.audioContext.destroy(); - } - if (val) { - this.audioAddEventListener(val); - } - }, - }, - }, - data: { - Timestamp: app.globalData.Timestamp, - progress: 0, - time: "00", - duration: "00", - play: false, - loading: true, - - imageUrl: app.globalData.imageUrl, - progressimg: true, - }, - lifetimes: { - created() {}, - async attached() {}, - moved() {}, - detached() { - if (this.audioContext) { - const { play } = this.data; - if (play) { - this.audioContext.pause(); - } - this.audioContext.destroy(); - } - this.audioAddEventListener = null; - this.setData({ - play: false, - progress: 0, - time: "00", - }); - }, - }, - pageLifetimes: { - // 组件所在页面的生命周期函数 - show: function () {}, - hide: function () { - const { play } = this.data; - if (play) { - this.audioContext.pause(); - } - }, - resize: function () {}, - }, - methods: { - togglePlay() { - const { play, loading } = this.data; - if (loading) { - wx.showToast({ - title: "音频加载中", - icon: "none", - }); - return; - } - if (play) { - this.audioContext.pause(); - } else { - this.audioContext.play(); - } - }, - formatTime(time) { - let m = parseInt(time / 60); - let s = parseInt(time % 60); - return this.towNum(m) + ":" + this.towNum(s); - }, - towNum(num) { - if (num >= 10) { - return num; - } else { - return "0" + num; - } - }, - audioAddEventListener(val) { - const that = this; - this.setData({ - duration: this.formatTime(val.size), - }); - that.audioContext = wx.createInnerAudioContext(); - that.audioContext.src = val.url; - that.setData({ - loading: false, - }); - that.audioContext.onError(({ errCode, ...reset }) => { - console.log("reset: ", reset); - console.log("errCode: ", errCode); - if (errCode === 10004 || errCode == 10001 || errCode == -1) { - that.audioContext.destroy(); - that.setData({ - loading: true, - }); - setTimeout(() => { - that.audioAddEventListener(val); - }, 300); - } - }); - that.audioContext.onPlay(() => { - that.setData({ - play: true, - }); - }); - that.audioContext.onPause(() => { - console.log(1111111); - that.setData({ - play: false, - }); - }); - that.audioContext.onEnded(() => { - that.audioContext.seek(0); - that.setData({ - play: false, - progress: 0, - time: "00", - }); - }); - that.audioContext.onTimeUpdate(() => { - const duration = that.audioContext.duration || 0; - const currentTime = that.audioContext.currentTime || 0; - const progress = (currentTime / duration) * 100; - if (duration == Infinity) { - return; - } - that.setData({ - play: true, - duration: that.formatTime(duration), - time: that.formatTime(currentTime), - }); - if (that.data.progressimg) { - this.setData({ - progress: progress, - }); - } - }); - }, - handleAuthChangeimg() { - console.log(11111); - this.setData({ - progressimg: false, - }); - }, - handleAuthChange(e) { - console.log(22222222222); - let { duration } = this.data; - const secods = this.audioContext.duration || duration.split(":")[0] * 60 + duration.split(":")[1] * 1; - const progress = e.detail.value; - let seek = ((secods / 100) * progress).toFixed(3) * 1; - this.audioContext.seek(seek); - this.setData({ - progressimg: true, - }); - }, - }, -}); +const app = getApp(); +import dayjs from "dayjs"; +Component({ + behaviors: [], + properties: { + audio: { + type: Object, + observer(val) { + if (this.audioContext) { + const { play } = this.data; + if (play) { + this.audioContext.pause(); + this.setData({ + play: false, + progress: 0, + time: "00", + }); + } + this.audioContext.destroy(); + } + if (val) { + this.audioAddEventListener(val); + } + }, + }, + }, + data: { + Timestamp: app.globalData.Timestamp, + progress: 0, + time: "00", + duration: "00", + play: false, + loading: true, + + imageUrl: app.globalData.imageUrl, + progressimg: true, + }, + lifetimes: { + created() {}, + async attached() {}, + moved() {}, + detached() { + if (this.audioContext) { + const { play } = this.data; + if (play) { + this.audioContext.pause(); + } + this.audioContext.destroy(); + } + this.audioAddEventListener = null; + this.setData({ + play: false, + progress: 0, + time: "00", + }); + }, + }, + pageLifetimes: { + // 组件所在页面的生命周期函数 + show: function () {}, + hide: function () { + const { play } = this.data; + if (play) { + this.audioContext.pause(); + } + }, + resize: function () {}, + }, + methods: { + togglePlay() { + const { play, loading } = this.data; + if (loading) { + wx.showToast({ + title: "音频加载中", + icon: "none", + }); + return; + } + if (play) { + this.audioContext.pause(); + } else { + this.audioContext.play(); + } + }, + formatTime(time) { + let m = parseInt(time / 60); + let s = parseInt(time % 60); + return this.towNum(m) + ":" + this.towNum(s); + }, + towNum(num) { + if (num >= 10) { + return num; + } else { + return "0" + num; + } + }, + audioAddEventListener(val) { + const that = this; + this.setData({ + duration: this.formatTime(val.size), + }); + that.audioContext = wx.createInnerAudioContext(); + that.audioContext.src = val.url; + that.setData({ + loading: false, + }); + that.audioContext.onError(({ errCode, ...reset }) => { + console.log("reset: ", reset); + console.log("errCode: ", errCode); + if (errCode === 10004 || errCode == 10001 || errCode == -1) { + that.audioContext.destroy(); + that.setData({ + loading: true, + }); + setTimeout(() => { + that.audioAddEventListener(val); + }, 300); + } + }); + that.audioContext.onPlay(() => { + that.setData({ + play: true, + }); + }); + that.audioContext.onPause(() => { + console.log(1111111); + that.setData({ + play: false, + }); + }); + that.audioContext.onEnded(() => { + that.audioContext.seek(0); + that.setData({ + play: false, + progress: 0, + time: "00", + }); + }); + that.audioContext.onTimeUpdate(() => { + const duration = that.audioContext.duration || 0; + const currentTime = that.audioContext.currentTime || 0; + const progress = (currentTime / duration) * 100; + if (duration == Infinity) { + return; + } + that.setData({ + play: true, + duration: that.formatTime(duration), + time: that.formatTime(currentTime), + }); + if (that.data.progressimg) { + this.setData({ + progress: progress, + }); + } + }); + }, + handleAuthChangeimg() { + console.log(11111); + this.setData({ + progressimg: false, + }); + }, + handleAuthChange(e) { + console.log(22222222222); + let { duration } = this.data; + const secods = this.audioContext.duration || duration.split(":")[0] * 60 + duration.split(":")[1] * 1; + const progress = e.detail.value; + let seek = ((secods / 100) * progress).toFixed(3) * 1; + this.audioContext.seek(seek); + this.setData({ + progressimg: true, + }); + }, + }, +}); diff --git a/src/components/freeAudio/index.wxml b/src/components/freeAudio/index.wxml index 706fc0a..d5bdc83 100644 --- a/src/components/freeAudio/index.wxml +++ b/src/components/freeAudio/index.wxml @@ -1,26 +1,28 @@ - - - - - - - - {{duration}} - - + + + + + + + + {{duration}} + + diff --git a/src/components/pageNavbar/index.wxml b/src/components/pageNavbar/index.wxml index 2c17406..1c782f4 100644 --- a/src/components/pageNavbar/index.wxml +++ b/src/components/pageNavbar/index.wxml @@ -1,7 +1,7 @@ - - - - - - - + + + + + + + diff --git a/src/components/patient-tab-bar/index.json b/src/components/patient-tab-bar/index.json new file mode 100644 index 0000000..03fc072 --- /dev/null +++ b/src/components/patient-tab-bar/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "van-tabbar": "@vant/weapp/tabbar/index", + "van-tabbar-item": "@vant/weapp/tabbar-item/index" + } +} diff --git a/src/components/patient-tab-bar/index.scss b/src/components/patient-tab-bar/index.scss new file mode 100644 index 0000000..a2aa18b --- /dev/null +++ b/src/components/patient-tab-bar/index.scss @@ -0,0 +1,19 @@ +/* custom-tab-bar/index.wxss */ + + +.tab-item { + .icon { + width: 50rpx; + height: 50rpx; + } + .name { + font-size: 24rpx; + color: #CCCCCC; + &.active{ + color: #CF5375; + } + &.drug-active{ + color: #25D9C8; + } + } +} diff --git a/src/components/patient-tab-bar/index.ts b/src/components/patient-tab-bar/index.ts new file mode 100644 index 0000000..f237027 --- /dev/null +++ b/src/components/patient-tab-bar/index.ts @@ -0,0 +1,113 @@ +import { getCurrentPageUrl } from '@/utils/util'; +const app = getApp(); + +Component({ + properties: {}, + data: { + imageUrl: app.globalData.imageUrl, + isChild: 0, + active: 0, + list: [ + { + pagePath: '/patient/pages/index/index', + text: '首页', + icon: 'home', + iconActive: 'home-active', + }, + { + pagePath: '/patient/pages/repository/index', + text: 'MG全知道', + icon: 'story', + iconActive: 'story-active', + }, + { + pagePath: '/patient/pages/live/index', + text: '周三大咖说', + icon: 'gift', + iconActive: 'gift-active', + }, + { + pagePath: '/patient/pages/story/index', + text: '向往的生活', + icon: 'class', + iconActive: 'class-active', + }, + { + pagePath: '/patient/pages/my/index', + text: '我的', + icon: 'my', + iconActive: 'my-active', + }, + ], + userInfo: {}, + showRed: false, + anyWhere: false, + + config: {} as any, + }, + observers: {}, + lifetimes: { + ready() { + app.zdGetTheme().then((res) => { + this.setData({ + theme: res, + }); + }); + + const pagePath = getCurrentPageUrl(); + const active = this.data.list.findIndex((item) => item.pagePath === pagePath); + this.setData({ + active, + anyWhere: app.globalData.anyWhere, + }); + this.setData({ + showRed: app.globalData.showRed, + }); + // getApp().registerListener(() => { + // this.setData({ + // anyWhere: app.globalData.anyWhere, + // }); + // wx.ajax({ + // method: 'GET', + // url: '?r=zd/common/get-config', + // }).then((res) => { + // this.setData({ + // config: res, + // }); + // }); + // }); + }, + }, + methods: { + onChange() {}, + handleNav(e) { + const { index } = e.currentTarget.dataset; + const { list, config } = this.data; + const pagePath = list[index].pagePath; + app.globalData.BeginnerCardId = ''; + this.setData({ + anyWhere: app.globalData.anyWhere, + }); + if (pagePath == '/patient/pages/my/index') { + app.zdPermissionVerification(2, 0, `/patient/pages/index/index`).then(() => { + wx.reLaunch({ + url: pagePath, + }); + }); + } else if (pagePath == '/patient/pages/repository/index' && config.picTextEbookStatus == 1) { + app.zdPermissionVerification(2, 0, `/pages/repository/index`).then(() => { + const webviewUrl = encodeURIComponent( + `${app.globalData.url}/zdcare/#/cover?loginState=${app.globalData.loginState}`, + ); + wx.navigateTo({ + url: `/patient/pages/webview/index?url=${webviewUrl}`, + }); + }); + } else { + wx.reLaunch({ + url: pagePath, + }); + } + }, + }, +}); diff --git a/src/components/patient-tab-bar/index.wxml b/src/components/patient-tab-bar/index.wxml new file mode 100644 index 0000000..0bb9f3e --- /dev/null +++ b/src/components/patient-tab-bar/index.wxml @@ -0,0 +1,16 @@ + + + + + + {{item.text}} + + + + diff --git a/src/components/star/index.ts b/src/components/star/index.ts index af9a4a6..942db9a 100644 --- a/src/components/star/index.ts +++ b/src/components/star/index.ts @@ -1,204 +1,204 @@ -const app = getApp(); -/** - * >=min && <=max - * @param min - * @param max - */ -function getRandom(min, max) { - return min + Math.floor(Math.random() * (max - min + 1)); -} -Component({ - options: {}, - lifetimes: { - attached() { - const query = wx.createSelectorQuery().in(this); - query - .select("#thumsCanvas") - .fields({ node: true, size: true }) - .exec((res) => { - const canvas = res[0].node; - const context = canvas.getContext("2d"); - this.setData({ - context: context, - }); - - const dpr = wx.getSystemInfoSync().pixelRatio; - canvas.width = res[0].width * dpr; - canvas.height = res[0].height * dpr; - this.data.width = res[0].width * dpr; - this.data.height = res[0].height * dpr; - // context.fillStyle = "rgba(255, 255, 255, 0)"; - const images = [ - "star/icon1.png", - "star/icon2.png", - "star/icon3.png", - "star/icon4.png", - "star/icon5.png", - "star/icon6.png", - "star/icon7.png", - "star/icon8.png", - ]; - const promiseAll = [] as Array>; - images.forEach((src) => { - const p = new Promise(function (resolve) { - const img = canvas.createImage(); - img.onerror = img.onload = resolve.bind(null, img); - img.src = app.globalData.imageUrl + src; - }); - promiseAll.push(p); - }); - Promise.all(promiseAll).then((imgsList) => { - const imgsLists = imgsList.filter((d) => { - if (d && d.width > 0) return true; - return false; - }); - this.setData({ - imgsList: imgsLists, - }); - if (this.data.imgsList.length == 0) { - // logger.error("imgsList load all error"); - wx.showToast({ - icon: "none", - title: "imgsList load all error", - }); - return; - } - }); - }); - }, - }, - properties: {}, - data: { - imgsList: [] as WechatMiniprogram.ImageData[], - width: 0, - height: 0, - context: null as any, - scanning: false, - renderList: [], - scaleTime: 0.1, // 百分比 - }, - methods: { - handleTap() { - this.start(); - }, - createRender() { - if (this.data.imgsList.length == 0) return null; - const basicScale = [0.6, 0.9, 1.2][getRandom(0, 2)]; - - const getScale = (diffTime) => { - if (diffTime < this.data.scaleTime) { - return +(diffTime / this.data.scaleTime).toFixed(2) * basicScale; - } else { - return basicScale; - } - }; - const context = this.data.context; - // 随机读取一个图片来渲染 - const image: any = this.data.imgsList[getRandom(0, this.data.imgsList.length - 1)]; - const offset = 20; - const basicX = this.data.width / 2 + getRandom(-offset, offset); - const angle = getRandom(2, 10); - let ratio = getRandom(10, 30) * (getRandom(0, 1) ? 1 : -1); - const getTranslateX = (diffTime) => { - if (diffTime < this.data.scaleTime) { - // 放大期间,不进行摇摆位移 - return basicX; - } else { - return basicX + ratio * Math.sin(angle * (diffTime - this.data.scaleTime)); - } - }; - - const getTranslateY = (diffTime) => { - return image.height / 2 + (this.data.height - image.height / 2) * (1 - diffTime); - }; - - const fadeOutStage = getRandom(14, 18) / 100; - const getAlpha = (diffTime) => { - let left = 1 - +diffTime; - if (left > fadeOutStage) { - return 1; - } else { - return 1 - +((fadeOutStage - left) / fadeOutStage).toFixed(2); - } - }; - - return (diffTime) => { - // 差值满了,即结束了 0 ---》 1 - if (diffTime >= 1) return true; - context.save(); - const scale = getScale(diffTime); - // const rotate = getRotate(); - const translateX = getTranslateX(diffTime); - const translateY = getTranslateY(diffTime); - context.translate(translateX, translateY); - context.scale(scale, scale); - // context.rotate(rotate * Math.PI / 180); - context.globalAlpha = getAlpha(diffTime); - context.drawImage(image, -image.width / 2, -image.height / 2, image.width, image.height); - context.restore(); - }; - }, - scan() { - this.data.context.clearRect(0, 0, this.data.width, this.data.height); - this.data.context.fillStyle = "rgba(255, 255, 255, 0)"; - this.data.context.fillRect(0, 0, 200, 400); - let index = 0; - let length = this.data.renderList.length; - if (length > 0) { - this.requestFrame(this.scan.bind(this)); - this.setData({ - scanning: true, - }); - } else { - this.setData({ - scanning: false, - }); - } - while (index < length) { - const child = this.data.renderList[index]; - if (!child || !child.render || child.render.call(null, (Date.now() - child.timestamp) / child.duration)) { - // 结束了,删除该动画 - this.setData({ - renderList: [...this.data.renderList].filter((_item, fIndex) => fIndex != index), - }); - length--; - } else { - // continue - index++; - } - } - }, - start() { - const render = this.createRender(); - const duration = getRandom(1500, 3000); - this.setData({ - renderList: [ - ...this.data.renderList, - { - render, - duration, - timestamp: Date.now(), - }, - ], - }); - - if (!this.data.scanning) { - this.setData({ - scanning: true, - }); - - this.requestFrame(this.scan.bind(this)); - // this.scan.bind(this)(); - } - return this; - }, - requestFrame(cb) { - return ( - this.data.context.requestAnimationFrame || - (function (callback) { - setTimeout(callback, 1000 / 60); - })(cb) - ); - }, - }, -}); +const app = getApp(); +/** + * >=min && <=max + * @param min + * @param max + */ +function getRandom(min, max) { + return min + Math.floor(Math.random() * (max - min + 1)); +} +Component({ + options: {}, + lifetimes: { + attached() { + const query = wx.createSelectorQuery().in(this); + query + .select("#thumsCanvas") + .fields({ node: true, size: true }) + .exec((res) => { + const canvas = res[0].node; + const context = canvas.getContext("2d"); + this.setData({ + context: context, + }); + + const dpr = wx.getSystemInfoSync().pixelRatio; + canvas.width = res[0].width * dpr; + canvas.height = res[0].height * dpr; + this.data.width = res[0].width * dpr; + this.data.height = res[0].height * dpr; + // context.fillStyle = "rgba(255, 255, 255, 0)"; + const images = [ + "za-images/star/icon1.png", + "za-images/star/icon2.png", + "za-images/star/icon3.png", + "za-images/star/icon4.png", + "za-images/star/icon5.png", + "za-images/star/icon6.png", + "za-images/star/icon7.png", + "za-images/star/icon8.png", + ]; + const promiseAll = [] as Array>; + images.forEach((src) => { + const p = new Promise(function (resolve) { + const img = canvas.createImage(); + img.onerror = img.onload = resolve.bind(null, img); + img.src = app.globalData.imageUrl + src; + }); + promiseAll.push(p); + }); + Promise.all(promiseAll).then((imgsList) => { + const imgsLists = imgsList.filter((d) => { + if (d && d.width > 0) return true; + return false; + }); + this.setData({ + imgsList: imgsLists, + }); + if (this.data.imgsList.length == 0) { + // logger.error("imgsList load all error"); + wx.showToast({ + icon: "none", + title: "imgsList load all error", + }); + return; + } + }); + }); + }, + }, + properties: {}, + data: { + imgsList: [] as WechatMiniprogram.ImageData[], + width: 0, + height: 0, + context: null as any, + scanning: false, + renderList: [], + scaleTime: 0.1, // 百分比 + }, + methods: { + handleTap() { + this.start(); + }, + createRender() { + if (this.data.imgsList.length == 0) return null; + const basicScale = [0.6, 0.9, 1.2][getRandom(0, 2)]; + + const getScale = (diffTime) => { + if (diffTime < this.data.scaleTime) { + return +(diffTime / this.data.scaleTime).toFixed(2) * basicScale; + } else { + return basicScale; + } + }; + const context = this.data.context; + // 随机读取一个图片来渲染 + const image: any = this.data.imgsList[getRandom(0, this.data.imgsList.length - 1)]; + const offset = 20; + const basicX = this.data.width / 2 + getRandom(-offset, offset); + const angle = getRandom(2, 10); + let ratio = getRandom(10, 30) * (getRandom(0, 1) ? 1 : -1); + const getTranslateX = (diffTime) => { + if (diffTime < this.data.scaleTime) { + // 放大期间,不进行摇摆位移 + return basicX; + } else { + return basicX + ratio * Math.sin(angle * (diffTime - this.data.scaleTime)); + } + }; + + const getTranslateY = (diffTime) => { + return image.height / 2 + (this.data.height - image.height / 2) * (1 - diffTime); + }; + + const fadeOutStage = getRandom(14, 18) / 100; + const getAlpha = (diffTime) => { + let left = 1 - +diffTime; + if (left > fadeOutStage) { + return 1; + } else { + return 1 - +((fadeOutStage - left) / fadeOutStage).toFixed(2); + } + }; + + return (diffTime) => { + // 差值满了,即结束了 0 ---》 1 + if (diffTime >= 1) return true; + context.save(); + const scale = getScale(diffTime); + // const rotate = getRotate(); + const translateX = getTranslateX(diffTime); + const translateY = getTranslateY(diffTime); + context.translate(translateX, translateY); + context.scale(scale, scale); + // context.rotate(rotate * Math.PI / 180); + context.globalAlpha = getAlpha(diffTime); + context.drawImage(image, -image.width / 2, -image.height / 2, image.width, image.height); + context.restore(); + }; + }, + scan() { + this.data.context.clearRect(0, 0, this.data.width, this.data.height); + this.data.context.fillStyle = "rgba(255, 255, 255, 0)"; + this.data.context.fillRect(0, 0, 200, 400); + let index = 0; + let length = this.data.renderList.length; + if (length > 0) { + this.requestFrame(this.scan.bind(this)); + this.setData({ + scanning: true, + }); + } else { + this.setData({ + scanning: false, + }); + } + while (index < length) { + const child = this.data.renderList[index]; + if (!child || !child.render || child.render.call(null, (Date.now() - child.timestamp) / child.duration)) { + // 结束了,删除该动画 + this.setData({ + renderList: [...this.data.renderList].filter((_item, fIndex) => fIndex != index), + }); + length--; + } else { + // continue + index++; + } + } + }, + start() { + const render = this.createRender(); + const duration = getRandom(1500, 3000); + this.setData({ + renderList: [ + ...this.data.renderList, + { + render, + duration, + timestamp: Date.now(), + }, + ], + }); + + if (!this.data.scanning) { + this.setData({ + scanning: true, + }); + + this.requestFrame(this.scan.bind(this)); + // this.scan.bind(this)(); + } + return this; + }, + requestFrame(cb) { + return ( + this.data.context.requestAnimationFrame || + (function (callback) { + setTimeout(callback, 1000 / 60); + })(cb) + ); + }, + }, +}); diff --git a/src/components/toast/index.ts b/src/components/toast/index.ts index c21e978..4e2f947 100644 --- a/src/components/toast/index.ts +++ b/src/components/toast/index.ts @@ -1,139 +1,139 @@ -const app = getApp() -Component({ - properties: { - show: { - type: Boolean, - value: false, - }, - type: String, - params: { - type: Object, - value() { - return {} - }, - }, - }, - observers: { - show(val) { - if (val) { - app.getTheme().then((res) => { - this.setData({ - theme: res, - }) - }) - if (this.data.params.timeOut) { - let time = this.data.params.timeOut - const timerFunc = () => { - if (time <= 0) { - this.setData({ - timeOut: '', - }) - clearInterval(this.timer) - return - } - this.setData({ - timeOut: `${time}s`, - }) - --time - } - timerFunc() - this.timer = setInterval(timerFunc, 1000) - } - if (['storyLead', 'storyStar', 'storyShare'].includes(this.data.type)) { - this.getSettingInfo() - } - } - this.setData({ - userInfo: app.globalData.userInfo, - }) - }, - type(val) { - if (val === 'healthCare') { - this.getOpenPatientList() - } - }, - }, - data: { - imageUrl: '', - protocol: true, - timeOut: '', - settingsInfo: {}, - userInfo: {}, - openPatientList: {}, - }, - lifetimes: { - attached() { - this.setData({ - imageUrl: app.globalData.imageUrl, - Timestamp: app.globalData.Timestamp, - }) - }, - }, - methods: { - timer: null as any, - handleOk() { - if (this.data.timeOut) { - return - } - this.triggerEvent('ok', { protocol: this.data.protocol }) - }, - handleCancel(e = { currentTarget: { dataset: { key: '' } } }) { - const { key } = e.currentTarget.dataset - if (this.timer) { - clearInterval(this.timer) - this.setData({ - timeOut: '', - }) - } - this.triggerEvent('cancel', { key }) - }, - handleTaskCancel() { - this.triggerEvent('taskCancel') - }, - handleJump() { - this.triggerEvent('jump') - }, - handleDel() { - this.triggerEvent('del') - }, - routerTo(e) { - const { path } = e.currentTarget.dataset - wx.navigateTo({ - url: path, - }) - this.handleCancel() - }, - getSettingInfo() { - wx.ajax({ - method: 'GET', - url: '?r=xd/drugs/setting-info', - data: {}, - }).then((res) => { - this.setData({ - settingsInfo: res, - }) - }) - }, - getOpenPatientList() { - wx.ajax({ - method: 'GET', - url: '?r=xd/nrdl/open-patient-list', - data: {}, - }).then((res) => { - this.setData({ - openPatientList: res, - }) - }) - }, - handleProtocolChange() { - this.setData({ - protocol: !this.data.protocol, - }) - }, - handleAdlQuestion() { - wx.navigateTo({ - url: '/pages/repositoryDetail/index?id=9', - }) - }, - }, -}) +const app = getApp(); +Component({ + properties: { + show: { + type: Boolean, + value: false, + }, + type: String, + params: { + type: Object, + value() { + return {}; + }, + }, + }, + observers: { + show(val) { + if (val) { + app.zdGetTheme().then((res) => { + this.setData({ + theme: res, + }); + }); + if (this.data.params.timeOut) { + let time = this.data.params.timeOut; + const timerFunc = () => { + if (time <= 0) { + this.setData({ + timeOut: '', + }); + clearInterval(this.timer); + return; + } + this.setData({ + timeOut: `${time}s`, + }); + --time; + }; + timerFunc(); + this.timer = setInterval(timerFunc, 1000); + } + if (['storyLead', 'storyStar', 'storyShare'].includes(this.data.type)) { + this.getSettingInfo(); + } + } + this.setData({ + zdUserInfo: app.globalData.zdUserInfo, + }); + }, + type(val) { + if (val === 'healthCare') { + this.getOpenPatientList(); + } + }, + }, + data: { + imageUrl: '', + protocol: true, + timeOut: '', + settingsInfo: {}, + zdUserInfo: {}, + openPatientList: {}, + }, + lifetimes: { + attached() { + this.setData({ + imageUrl: app.globalData.imageUrl, + Timestamp: app.globalData.Timestamp, + }); + }, + }, + methods: { + timer: null as any, + handleOk() { + if (this.data.timeOut) { + return; + } + this.triggerEvent('ok', { protocol: this.data.protocol }); + }, + handleCancel(e = { currentTarget: { dataset: { key: '' } } }) { + const { key } = e.currentTarget.dataset; + if (this.timer) { + clearInterval(this.timer); + this.setData({ + timeOut: '', + }); + } + this.triggerEvent('cancel', { key }); + }, + handleTaskCancel() { + this.triggerEvent('taskCancel'); + }, + handleJump() { + this.triggerEvent('jump'); + }, + handleDel() { + this.triggerEvent('del'); + }, + routerTo(e) { + const { path } = e.currentTarget.dataset; + wx.navigateTo({ + url: path, + }); + this.handleCancel(); + }, + getSettingInfo() { + wx.ajax({ + method: 'GET', + url: '?r=xd/drugs/setting-info', + data: {}, + }).then((res) => { + this.setData({ + settingsInfo: res, + }); + }); + }, + getOpenPatientList() { + wx.ajax({ + method: 'GET', + url: '?r=xd/nrdl/open-patient-list', + data: {}, + }).then((res) => { + this.setData({ + openPatientList: res, + }); + }); + }, + handleProtocolChange() { + this.setData({ + protocol: !this.data.protocol, + }); + }, + handleAdlQuestion() { + wx.navigateTo({ + url: '/pages/repositoryDetail/index?id=9', + }); + }, + }, +}); diff --git a/src/components/toast/index.wxml b/src/components/toast/index.wxml index f98a49e..ea03240 100644 --- a/src/components/toast/index.wxml +++ b/src/components/toast/index.wxml @@ -6,9 +6,9 @@ show="{{ show }}" > - + - + 训练完成 @@ -34,7 +34,7 @@ - + 该亲友号解绑后 @@ -50,7 +50,7 @@ - + 删除代表您将放弃发布此条内容 @@ -64,7 +64,7 @@ - + 您是希望继续投稿吗? @@ -74,7 +74,7 @@ - + 删除代表您将放弃发布此条内容 @@ -97,7 +97,7 @@ - + 抑制物检测提醒 @@ -117,7 +117,7 @@ - + {{params.date}} {{params.week}} @@ -164,8 +164,11 @@ - - + + (1)您的故事中可能会涉及您、您的孩子或其它人的个人信息甚至敏感个人信息,如个人影像、个人健康信息及生活经历。建议您不要在故事中提及真实身份信息。请您谨慎考虑是否在此公开发布您的故事。您确认并承诺您已经向有关个人进行充分的告知并征得同意。如果有关个人未满14周岁,您确认并承诺您为该儿童的监护人并且同意公开发表该儿童的个人信息。 @@ -201,10 +204,10 @@ {{settingsInfo.PublishStoryScore}} 能量激励 - + - + 你知道吗? @@ -225,9 +228,9 @@ - + - + 感谢加油! @@ -244,13 +247,13 @@ - - + + - + - - + + 分享您的经验或故事 审核发布后 @@ -271,7 +274,11 @@ - + Hi,{{params.patientName}} @@ -292,14 +299,14 @@ - + 自在生活“双达标”测评提醒 建议您每次用药7天后做1次“双达标”测评,更好了解身体变化 - + @@ -314,7 +321,7 @@ wx:if="{{type == 'aldAlertTest'}}" > - + @@ -336,7 +343,7 @@ - + 一步认证成为 @@ -345,7 +352,7 @@ 专属服务用户登记 - + 立即认证 @@ -355,7 +362,7 @@ - + 给力加油站电话随访服务启动 @@ -365,7 +372,7 @@ 提醒您健康管理 - + 《电话随访知情同意书》 @@ -377,7 +384,7 @@ - + 艾加莫德进入医保 @@ -396,7 +403,7 @@ wx:if="{{type == 'ndrlAldAlert'}}" > - + @@ -405,7 +412,7 @@ - + 什么是MG-ADL? @@ -416,7 +423,11 @@