这两天帮公司运维一个老的OA系统,说是老的但也不算很老,大概是23年前后的项目吧。
项目背景
整个项目技术栈还算比较主流,前端部分用的是老几样:
vue2 + vue-router + vuex + vue-cli + ant-design-vue@1.7.8
后端部分也是老几样:
SpringBoot + Radis + MyBatis + hangao
这个项目升级根本上不上来,之前领导还说过要不要升AntDesign的高版本,因为高版本有换肤功能。
我直截了当的告诉他:升不了!
AntDesign在Vue2中最后一个版本就是 @1.7.8,还问我工作量大不大。
我告诉他:比天还大!
有兄弟说可以用AI做升级,但是非常抱歉,内网离线环境开发,没有AI。
而且公司也没有资源部署本地AI,所以纯古法编程,难搞的很。
月份区间选择
现在需求这边提出来需要一个月份区间选择 和年份区间选择。
但是用过的人都知道,AngDesignVue的Vue2版本只有日期区间选择,也就是a-range-picker组件。

于是乎我自己封装了一下,下面是月份区间选择组件的源码:
html
<template>
<a-range-picker
ref="picker"
class="adv-month-range-picker"
:mode="panelMode"
:value="innerValue"
:open="sOpen"
:format="format"
:placeholder="placeholder"
:separator="separator"
:disabled="disabled"
:allow-clear="allowClear"
:size="size"
:disabled-date="disabledDate"
:get-calendar-container="getCalendarContainer"
:popup-style="popupStyle"
:show-today="showToday"
:input-read-only="inputReadOnly"
@openChange="handleOpenChange"
@panelChange="handlePanelChange"
@calendarChange="handleCalendarChange"
@change="handleChange"
/>
</template>
<script>
import moment from 'moment';
const PANEL_MODE = ['month', 'month']; // 必须传数组,RangeCalendar 内部按 mode[0] / mode[1] 取用
const UNIT = 'month';
const PANEL_CELL_SUFFIX = '-panel-cell';
function resolveClickedSide(node) {
let cur = node;
let depth = 0;
while (cur && depth++ < 24) {
const cls = typeof cur.className === 'string' ? cur.className : '';
if (cls) {
if (/(^|\s)[\w-]*-range-left(\s|$)/.test(cls)) return 'start';
if (/(^|\s)[\w-]*-range-right(\s|$)/.test(cls)) return 'end';
}
cur = cur.parentNode;
}
return null;
}
function findUp(node, matches, maxDepth) {
let cur = node;
let depth = 0;
const limit = maxDepth || 24;
while (cur && depth++ < limit) {
if (cur.nodeType === 1 && matches(cur)) return cur;
cur = cur.parentNode;
}
return null;
}
function panelCellBaseCls(el) {
const list = String(el.className || '').split(/\s+/);
for (let i = 0; i < list.length; i++) {
const c = list[i];
if (!c) continue;
if (/-panel-cell$/.test(c) || /-panel-cell-/.test(c)) {
return c.slice(0, c.indexOf(PANEL_CELL_SUFFIX) + PANEL_CELL_SUFFIX.length);
}
}
return null;
}
function closestPanelCell(node) {
return findUp(node, (el) => !!panelCellBaseCls(el), 12);
}
function panelCellDisabledCls(cell) {
const base = panelCellBaseCls(cell);
return base ? base + '-disabled' : null;
}
function panelCellMoment(cell) {
const base = panelCellBaseCls(cell);
if (!base) return null;
if (base.indexOf('-month-panel-cell') > -1) {
const panel = findUp(cell, (el) => !!el.querySelector('[class*="-month-panel-year-select-content"]'), 8);
if (!panel) return null;
const yearEl = panel.querySelector('[class*="-month-panel-year-select-content"]');
const year = yearEl ? parseInt(yearEl.textContent, 10) : NaN;
const table = findUp(cell, (el) => el.tagName === 'TABLE', 6);
const cells = table ? table.querySelectorAll('[class*="-month-panel-cell"]') : [];
const index = Array.prototype.indexOf.call(cells, cell);
if (isNaN(year) || index < 0) return null;
return moment([year, index, 1]);
}
if (base.indexOf('-year-panel-cell') > -1) {
const year = parseInt(cell.getAttribute('title') || cell.textContent, 10);
return isNaN(year) ? null : moment([year, 0, 1]);
}
return null;
}
export default {
name: 'MonthRangePicker',
model: { prop: 'value', event: 'change' },
props: {
/** 区间值,['2026-01','2026-12'];valueType=moment 时传 [moment, moment] */
value: { type: Array, default: () => [] },
/** 值类型:'string'(默认,便于直接塞进接口参数)| 'moment' */
valueType: { type: String, default: 'string' },
/** 展示与字符串格式 */
format: { type: String, default: 'YYYY-MM' },
placeholder: { type: Array, default: () => ['开始月份', '结束月份'] },
separator: { type: String, default: '~' },
disabled: { type: Boolean, default: false },
allowClear: { type: Boolean, default: true },
size: { type: String, default: 'default' },
showToday: { type: Boolean, default: false },
inputReadOnly: { type: Boolean, default: true },
disabledDate: { type: Function, default: null },
getCalendarContainer: { type: Function, default: null },
popupStyle: { type: Object, default: () => ({}) },
open: { type: Boolean, default: undefined },
defaultPickerValue: { type: Array, default: () => [] },
},
data() {
return {
sOpen: false,
pickedStart: null, // 已确认/待确认的开始
pickedEnd: null, // 已确认的结束;为 null 表示"只选了开始"
anchor: null, // 上一次面板显示的 [左, 右],用于兜底判断点击发生在哪一侧
clickedSide: null, // 'start' | 'end' | null(本次点击落在哪个面板)
};
},
computed: {
panelMode() {
return PANEL_MODE;
},
innerValue() {
if (!this.pickedStart) return [];
return this.pickedEnd ? [this.pickedStart, this.pickedEnd] : [this.pickedStart, undefined];
},
},
watch: {
value: {
immediate: true,
handler() {
this.syncFromProp();
},
},
open(val) {
if (val !== undefined && val !== this.sOpen) this.sOpen = !!val;
},
disabledDate() {
if (this.sOpen) this.decoratePanel();
},
},
mounted() {
this.__captureClick = (e) => {
this.clickedSide = resolveClickedSide(e.target);
};
document.addEventListener('click', this.__captureClick, true);
this.__blockDisabledClick = (e) => {
if (!this.disabledDate || !this.sOpen) return;
const cell = closestPanelCell(e.target);
if (!cell || !this.isDisabledCell(cell)) return;
e.preventDefault();
e.stopPropagation();
};
document.addEventListener('click', this.__blockDisabledClick, true);
this.__paintOnHover = (e) => {
if (!this.disabledDate || !this.sOpen) return;
this.paintCell(closestPanelCell(e.target));
};
document.addEventListener('mouseover', this.__paintOnHover, true);
if (typeof MutationObserver !== 'undefined') {
this.__observer = new MutationObserver(() => this.decoratePanel());
this.__observeTarget = null;
}
this.__decorating = false;
},
beforeDestroy() {
document.removeEventListener('click', this.__captureClick, true);
document.removeEventListener('click', this.__blockDisabledClick, true);
document.removeEventListener('mouseover', this.__paintOnHover, true);
this.stopObserve();
},
methods: {
fire(name, ...args) {
this.$emit(name, ...args);
const kebab = name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
if (kebab !== name) this.$emit(kebab, ...args);
},
isDisabledByUser(v) {
if (!this.disabledDate || !v) return false;
const m = moment.isMoment(v) ? v.clone() : moment(v);
if (!m.isValid()) return false;
try {
return !!this.disabledDate(m.startOf(UNIT));
} catch (e) {
return false;
}
},
isDisabledCell(cell) {
if (!this.disabledDate || !cell) return false;
return this.isDisabledByUser(panelCellMoment(cell));
},
paintCell(cell) {
if (!this.disabledDate || !cell) return;
const disabledCls = panelCellDisabledCls(cell);
if (!disabledCls) return;
const mine = cell.getAttribute('data-adv-disabled') === '1';
if (this.isDisabledCell(cell)) {
if (!mine) {
cell.setAttribute('data-adv-disabled', '1');
cell.classList.add(disabledCls);
}
} else if (mine) {
cell.removeAttribute('data-adv-disabled');
cell.classList.remove(disabledCls);
}
},
popupNode() {
const stack = [this.$refs.picker];
let guard = 0;
while (stack.length && guard++ < 200) {
const cur = stack.shift();
if (!cur) continue;
if (typeof cur.getPopupDomNode === 'function') {
let node = null;
try {
node = cur.getPopupDomNode();
} catch (e) {
node = null;
}
if (node && node.nodeType === 1) return node;
}
const kids = cur.$children;
if (kids && kids.length) for (let i = 0; i < kids.length; i++) stack.push(kids[i]);
}
return null;
},
visiblePopup() {
const nodes = document.querySelectorAll('[class*="-calendar-picker-container"]');
for (let i = nodes.length - 1; i >= 0; i--) {
const rect = nodes[i].getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) return nodes[i];
}
return null;
},
decoratePanel() {
if (!this.disabledDate) return;
this.$nextTick(() => this.paintPanel(0));
},
paintPanel(attempt) {
if (!this.disabledDate || !this.sOpen || this.__decorating) return;
const popup = this.popupNode() || this.visiblePopup();
if (!popup) {
if (attempt < 5) setTimeout(() => this.paintPanel(attempt + 1), 60);
return;
}
this.__decorating = true;
try {
if (this.__observer && this.__observeTarget !== popup) {
this.__observer.disconnect();
this.__observer.observe(popup, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ['class'],
});
this.__observeTarget = popup;
}
const cells = popup.querySelectorAll('[class*="-panel-cell"]');
Array.prototype.forEach.call(cells, (c) => this.paintCell(c));
} finally {
this.__decorating = false;
}
},
stopObserve() {
if (this.__observer) {
this.__observer.disconnect();
this.__observeTarget = null;
}
},
parseOne(v) {
if (!v) return null;
let m = moment.isMoment(v) ? v.clone() : moment(v, this.format, true);
if (!m.isValid()) m = moment(v);
return m.isValid() ? m.startOf(UNIT) : null;
},
syncFromProp() {
const arr = Array.isArray(this.value) ? this.value : [];
this.pickedStart = this.parseOne(arr[0]);
this.pickedEnd = this.parseOne(arr[1]);
this.anchor = this.currentDisplay();
},
currentDisplay() {
if (this.pickedStart) return [this.pickedStart, this.pickedEnd];
const base = this.parseOne((this.defaultPickerValue || [])[0]) || moment();
return [base, base.clone().add(1, UNIT)];
},
sameUnit(a, b) {
if (!a && !b) return true;
if (!a || !b) return false;
return a.isSame(b, UNIT);
},
toOut(m) {
if (!m) return null;
return this.valueType === 'moment' ? m.clone() : m.format(this.format);
},
emitValue(s, e) {
if (this.valueType === 'moment') return s || e ? [s, e] : [];
return s || e ? [this.toOut(s), this.toOut(e)] : [];
},
isCellPick(mode) {
const arr = Array.isArray(mode) ? mode : [mode];
return arr.some((m) => m !== 'month' && m !== 'year' && m !== 'decade');
},
detectClicked(v) {
const prev = this.anchor;
if (!prev || !v) return v && (v[0] || v[1]);
if (!this.sameUnit(v[0], prev[0])) return v[0];
if (!this.sameUnit(v[1], prev[1])) return v[1];
return v[0] || v[1];
},
handleOpenChange(open) {
this.sOpen = open;
if (!open && this.pickedStart && !this.pickedEnd) this.syncFromProp();
this.anchor = this.currentDisplay();
if (open) this.decoratePanel();
else this.stopObserve();
this.fire('openChange', open);
},
handlePanelChange(v, mode) {
this.fire('panelChange', v ? [v[0], v[1]] : v, mode);
this.decoratePanel(); // 切面板 / 翻页后重新上色
if (!this.isCellPick(mode)) return;
const side = this.clickedSide;
const clicked = side === 'start' ? v[0] : side === 'end' ? v[1] : this.detectClicked(v);
this.clickedSide = null;
if (!clicked) return;
if (this.isDisabledByUser(clicked)) return;
const start = this.pickedStart;
const end = this.pickedEnd;
if (!start || end) {
this.pickedStart = clicked;
this.pickedEnd = null;
this.anchor = v;
return;
}
const s = clicked.isAfter(start, UNIT) ? start : clicked;
const e = clicked.isAfter(start, UNIT) ? clicked : start;
this.pickedStart = s;
this.pickedEnd = e;
this.anchor = [s, e];
this.sOpen = false;
this.$emit('change', this.emitValue(s, e), [s.format(this.format), e.format(this.format)]);
},
handleCalendarChange(v) {
this.fire('calendarChange', v ? [v[0], v[1]] : v);
},
handleChange(v) {
const s = v && v[0];
const e = v && v[1];
if (s && e) {
if (this.sameUnit(s, this.pickedStart) && this.sameUnit(e, this.pickedEnd)) return; // 去重
this.pickedStart = s;
this.pickedEnd = e;
this.anchor = [s, e];
this.sOpen = false;
this.$emit('change', this.emitValue(s, e), [s.format(this.format), e.format(this.format)]);
return;
}
this.pickedStart = null;
this.pickedEnd = null;
this.anchor = this.currentDisplay();
this.$emit('change', [], []);
},
focus() {
const p = this.$refs.picker;
if (p && p.focus) p.focus();
},
blur() {
const p = this.$refs.picker;
if (p && p.blur) p.blur();
},
clear() {
this.pickedStart = null;
this.pickedEnd = null;
this.sOpen = false;
this.anchor = this.currentDisplay();
this.$emit('change', [], []);
},
},
};
</script>
年份区间选择

html
<template>
<a-range-picker
ref="picker"
class="adv-year-range-picker"
:mode="panelMode"
:value="innerValue"
:open="sOpen"
:format="format"
:placeholder="placeholder"
:separator="separator"
:disabled="disabled"
:allow-clear="allowClear"
:size="size"
:disabled-date="disabledDate"
:get-calendar-container="getCalendarContainer"
:popup-style="popupStyle"
:show-today="showToday"
:input-read-only="inputReadOnly"
@openChange="handleOpenChange"
@panelChange="handlePanelChange"
@calendarChange="handleCalendarChange"
@change="handleChange"
/>
</template>
<script>
import moment from 'moment';
const PANEL_MODE = ['year', 'year']; // 必须传数组
const UNIT = 'year';
const PANEL_CELL_SUFFIX = '-panel-cell';
function resolveClickedSide(node) {
let cur = node;
let depth = 0;
while (cur && depth++ < 24) {
const cls = typeof cur.className === 'string' ? cur.className : '';
if (cls) {
if (/(^|\s)[\w-]*-range-left(\s|$)/.test(cls)) return 'start';
if (/(^|\s)[\w-]*-range-right(\s|$)/.test(cls)) return 'end';
}
cur = cur.parentNode;
}
return null;
}
function findUp(node, matches, maxDepth) {
let cur = node;
let depth = 0;
const limit = maxDepth || 24;
while (cur && depth++ < limit) {
if (cur.nodeType === 1 && matches(cur)) return cur;
cur = cur.parentNode;
}
return null;
}
function panelCellBaseCls(el) {
const list = String(el.className || '').split(/\s+/);
for (let i = 0; i < list.length; i++) {
const c = list[i];
if (!c) continue;
if (/-panel-cell$/.test(c) || /-panel-cell-/.test(c)) {
return c.slice(0, c.indexOf(PANEL_CELL_SUFFIX) + PANEL_CELL_SUFFIX.length);
}
}
return null;
}
function closestPanelCell(node) {
return findUp(node, (el) => !!panelCellBaseCls(el), 12);
}
function panelCellDisabledCls(cell) {
const base = panelCellBaseCls(cell);
return base ? base + '-disabled' : null;
}
function panelCellMoment(cell) {
const base = panelCellBaseCls(cell);
if (!base) return null;
if (base.indexOf('-year-panel-cell') > -1) {
const year = parseInt(cell.getAttribute('title') || cell.textContent, 10);
return isNaN(year) ? null : moment([year, 0, 1]);
}
if (base.indexOf('-month-panel-cell') > -1) {
const panel = findUp(cell, (el) => !!el.querySelector('[class*="-month-panel-year-select-content"]'), 8);
if (!panel) return null;
const yearEl = panel.querySelector('[class*="-month-panel-year-select-content"]');
const year = yearEl ? parseInt(yearEl.textContent, 10) : NaN;
const table = findUp(cell, (el) => el.tagName === 'TABLE', 6);
const cells = table ? table.querySelectorAll('[class*="-month-panel-cell"]') : [];
const index = Array.prototype.indexOf.call(cells, cell);
if (isNaN(year) || index < 0) return null;
return moment([year, index, 1]);
}
return null;
}
export default {
name: 'YearRangePicker',
model: { prop: 'value', event: 'change' },
props: {
value: { type: Array, default: () => [] },
valueType: { type: String, default: 'string' },
format: { type: String, default: 'YYYY' },
placeholder: { type: Array, default: () => ['开始年份', '结束年份'] },
separator: { type: String, default: '~' },
disabled: { type: Boolean, default: false },
allowClear: { type: Boolean, default: true },
size: { type: String, default: 'default' },
showToday: { type: Boolean, default: false },
inputReadOnly: { type: Boolean, default: true },
disabledDate: { type: Function, default: null },
getCalendarContainer: { type: Function, default: null },
popupStyle: { type: Object, default: () => ({}) },
open: { type: Boolean, default: undefined },
defaultPickerValue: { type: Array, default: () => [] },
},
data() {
return {
sOpen: false,
pickedStart: null,
pickedEnd: null,
anchor: null,
clickedSide: null,
};
},
computed: {
panelMode() {
return PANEL_MODE;
},
innerValue() {
if (!this.pickedStart) return [];
return this.pickedEnd ? [this.pickedStart, this.pickedEnd] : [this.pickedStart, undefined];
},
},
watch: {
value: {
immediate: true,
handler() {
this.syncFromProp();
},
},
open(val) {
if (val !== undefined && val !== this.sOpen) this.sOpen = !!val;
},
disabledDate() {
if (this.sOpen) this.decoratePanel();
},
},
mounted() {
this.__captureClick = (e) => {
this.clickedSide = resolveClickedSide(e.target);
};
document.addEventListener('click', this.__captureClick, true);
this.__blockDisabledClick = (e) => {
if (!this.disabledDate || !this.sOpen) return;
const cell = closestPanelCell(e.target);
if (!cell || !this.isDisabledCell(cell)) return;
e.preventDefault();
e.stopPropagation();
};
document.addEventListener('click', this.__blockDisabledClick, true);
this.__paintOnHover = (e) => {
if (!this.disabledDate || !this.sOpen) return;
this.paintCell(closestPanelCell(e.target));
};
document.addEventListener('mouseover', this.__paintOnHover, true);
if (typeof MutationObserver !== 'undefined') {
this.__observer = new MutationObserver(() => this.decoratePanel());
this.__observeTarget = null;
}
this.__decorating = false;
},
beforeDestroy() {
document.removeEventListener('click', this.__captureClick, true);
document.removeEventListener('click', this.__blockDisabledClick, true);
document.removeEventListener('mouseover', this.__paintOnHover, true);
this.stopObserve();
},
methods: {
fire(name, ...args) {
this.$emit(name, ...args);
const kebab = name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
if (kebab !== name) this.$emit(kebab, ...args);
},
isDisabledByUser(v) {
if (!this.disabledDate || !v) return false;
const m = moment.isMoment(v) ? v.clone() : moment(v);
if (!m.isValid()) return false;
try {
return !!this.disabledDate(m.startOf(UNIT));
} catch (e) {
return false;
}
},
isDisabledCell(cell) {
if (!this.disabledDate || !cell) return false;
return this.isDisabledByUser(panelCellMoment(cell));
},
paintCell(cell) {
if (!this.disabledDate || !cell) return;
const disabledCls = panelCellDisabledCls(cell);
if (!disabledCls) return;
const mine = cell.getAttribute('data-adv-disabled') === '1';
if (this.isDisabledCell(cell)) {
if (!mine) {
cell.setAttribute('data-adv-disabled', '1');
cell.classList.add(disabledCls);
}
} else if (mine) {
cell.removeAttribute('data-adv-disabled');
cell.classList.remove(disabledCls);
}
},
popupNode() {
const stack = [this.$refs.picker];
let guard = 0;
while (stack.length && guard++ < 200) {
const cur = stack.shift();
if (!cur) continue;
if (typeof cur.getPopupDomNode === 'function') {
let node = null;
try {
node = cur.getPopupDomNode();
} catch (e) {
node = null;
}
if (node && node.nodeType === 1) return node;
}
const kids = cur.$children;
if (kids && kids.length) for (let i = 0; i < kids.length; i++) stack.push(kids[i]);
}
return null;
},
visiblePopup() {
const nodes = document.querySelectorAll('[class*="-calendar-picker-container"]');
for (let i = nodes.length - 1; i >= 0; i--) {
const rect = nodes[i].getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) return nodes[i];
}
return null;
},
decoratePanel() {
if (!this.disabledDate) return;
this.$nextTick(() => this.paintPanel(0));
},
paintPanel(attempt) {
if (!this.disabledDate || !this.sOpen || this.__decorating) return;
const popup = this.popupNode() || this.visiblePopup();
if (!popup) {
if (attempt < 5) setTimeout(() => this.paintPanel(attempt + 1), 60);
return;
}
this.__decorating = true;
try {
if (this.__observer && this.__observeTarget !== popup) {
this.__observer.disconnect();
this.__observer.observe(popup, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ['class'],
});
this.__observeTarget = popup;
}
const cells = popup.querySelectorAll('[class*="-panel-cell"]');
Array.prototype.forEach.call(cells, (c) => this.paintCell(c));
} finally {
this.__decorating = false;
}
},
stopObserve() {
if (this.__observer) {
this.__observer.disconnect();
this.__observeTarget = null;
}
},
parseOne(v) {
if (!v) return null;
let m = moment.isMoment(v) ? v.clone() : moment(v, this.format, true);
if (!m.isValid()) m = moment(v);
return m.isValid() ? m.startOf(UNIT) : null;
},
syncFromProp() {
const arr = Array.isArray(this.value) ? this.value : [];
this.pickedStart = this.parseOne(arr[0]);
this.pickedEnd = this.parseOne(arr[1]);
this.anchor = this.currentDisplay();
},
currentDisplay() {
if (this.pickedStart) return [this.pickedStart, this.pickedEnd];
const base = this.parseOne((this.defaultPickerValue || [])[0]) || moment();
return [base, base.clone().add(1, UNIT)];
},
sameUnit(a, b) {
if (!a && !b) return true;
if (!a || !b) return false;
return a.isSame(b, UNIT);
},
toOut(m) {
if (!m) return null;
return this.valueType === 'moment' ? m.clone() : m.format(this.format);
},
emitValue(s, e) {
if (this.valueType === 'moment') return s || e ? [s, e] : [];
return s || e ? [this.toOut(s), this.toOut(e)] : [];
},
isCellPick(mode) {
const arr = Array.isArray(mode) ? mode : [mode];
return arr.some((m) => m !== 'month' && m !== 'year' && m !== 'decade');
},
detectClicked(v) {
const prev = this.anchor;
if (!prev || !v) return v && (v[0] || v[1]);
if (!this.sameUnit(v[0], prev[0])) return v[0];
if (!this.sameUnit(v[1], prev[1])) return v[1];
return v[0] || v[1];
},
handleOpenChange(open) {
this.sOpen = open;
if (!open && this.pickedStart && !this.pickedEnd) this.syncFromProp();
this.anchor = this.currentDisplay();
if (open) this.decoratePanel();
else this.stopObserve();
this.fire('openChange', open);
},
handlePanelChange(v, mode) {
this.fire('panelChange', v ? [v[0], v[1]] : v, mode);
this.decoratePanel(); // 切面板 / 翻页后重新上色
if (!this.isCellPick(mode)) return;
const side = this.clickedSide;
const clicked = side === 'start' ? v[0] : side === 'end' ? v[1] : this.detectClicked(v);
this.clickedSide = null;
if (!clicked) return;
// 双保险:捕获阶段万一没拦住,也不允许写进禁用值
if (this.isDisabledByUser(clicked)) return;
const start = this.pickedStart;
const end = this.pickedEnd;
if (!start || end) {
this.pickedStart = clicked;
this.pickedEnd = null;
this.anchor = v;
return;
}
const s = clicked.isAfter(start, UNIT) ? start : clicked;
const e = clicked.isAfter(start, UNIT) ? clicked : start;
this.pickedStart = s;
this.pickedEnd = e;
this.anchor = [s, e];
this.sOpen = false;
this.$emit('change', this.emitValue(s, e), [s.format(this.format), e.format(this.format)]);
},
handleCalendarChange(v) {
this.fire('calendarChange', v ? [v[0], v[1]] : v);
},
handleChange(v) {
const s = v && v[0];
const e = v && v[1];
if (s && e) {
if (this.sameUnit(s, this.pickedStart) && this.sameUnit(e, this.pickedEnd)) return; // 去重
this.pickedStart = s;
this.pickedEnd = e;
this.anchor = [s, e];
this.sOpen = false;
this.$emit('change', this.emitValue(s, e), [s.format(this.format), e.format(this.format)]);
return;
}
this.pickedStart = null;
this.pickedEnd = null;
this.anchor = this.currentDisplay();
this.$emit('change', [], []);
},
focus() {
const p = this.$refs.picker;
if (p && p.focus) p.focus();
},
blur() {
const p = this.$refs.picker;
if (p && p.blur) p.blur();
},
clear() {
this.pickedStart = null;
this.pickedEnd = null;
this.sOpen = false;
this.anchor = this.currentDisplay();
this.$emit('change', [], []);
},
},
};
</script>