feat: 新增cron组件

This commit is contained in:
xlsea 2024-04-20 22:40:47 +08:00
parent 260058da45
commit 1f5d963557
16 changed files with 888 additions and 4 deletions

View File

@ -44,6 +44,7 @@
"@iconify/vue": "4.1.1",
"@sa/axios": "workspace:*",
"@sa/color-palette": "workspace:*",
"@sa/cron-input": "workspace:*",
"@sa/hooks": "workspace:*",
"@sa/materials": "workspace:*",
"@sa/utils": "workspace:*",

View File

@ -0,0 +1,15 @@
{
"name": "@sa/cron-input",
"version": "1.0.3",
"exports": {
".": "./src/index.ts"
},
"typesVersions": {
"*": {
"*": ["./src/*"]
}
},
"dependencies": {
"cron-parser": "^4.9.0"
}
}

View File

@ -0,0 +1,206 @@
<script setup lang="ts">
import cronParser from 'cron-parser';
import { computed, ref, watch } from 'vue';
import { DEFAULT_CRON_EXPRESSION, DEFAULT_LOCALE, FIELDS, LOCALE_CN, TYPE } from '../shared/constants';
import { weekNumberToLetter, zerofill } from '../shared/utils';
import Locales from '../i18n';
import CronBase from './internal/cron-base.vue';
defineOptions({ name: 'CronInput' });
interface Props {
modelValue?: string;
lang?: I18n.LocaleType;
}
const props = withDefaults(defineProps<Props>(), {
modelValue: DEFAULT_CRON_EXPRESSION,
lang: (JSON.parse(window.localStorage.getItem('lang')!) as I18n.LocaleType) || DEFAULT_LOCALE
});
interface Emits {
(e: 'update:modelValue', value: string): void;
}
const emit = defineEmits<Emits>();
const [second, minute, hour, date, month, week, year = ''] = props.modelValue.split(' ');
const cron = ref({ second, minute, hour, date, month, week, year });
const activeKey = ref(FIELDS[0].value);
const previewTime = ref(5);
const width = computed(() => {
return props.lang === LOCALE_CN ? '460px' : '520px';
});
const fields = computed(() => {
return FIELDS.map(field => {
const label = Locales[props.lang].field[field.value];
return { ...field, label };
});
});
const expressionLabel = computed(() => {
return Locales[props.lang].expression;
});
const previewLabel = computed(() => {
return Locales[props.lang].preview.join(previewTime.value.toString());
});
const expression = computed(() => {
return Object.values(cron.value).join(' ');
});
const previews = computed(() => {
let previewList = [];
try {
const interval = cronParser.parseExpression(expression.value);
for (let i = 0; i < previewTime.value; i += 1) {
const datetime = interval.next();
const previewYear = zerofill(datetime.getFullYear());
const previewMonth = zerofill(datetime.getMonth() + 1);
const previewDate = zerofill(datetime.getDate());
const previewHour = zerofill(datetime.getHours());
const previewMinute = zerofill(datetime.getMinutes());
const previewSecond = zerofill(datetime.getSeconds());
previewList.push(
`${previewYear}-${previewMonth}-${previewDate} ${previewHour}:${previewMinute}:${previewSecond}`
);
}
} catch (error) {
previewList = ['此表达式暂时无法解析!'];
}
return previewList;
});
watch(
() => cron.value,
val => {
val.week = weekNumberToLetter(val.week);
emit('update:modelValue', Object.values(val).join(' '));
},
{ deep: true }
);
watch(
() => cron.value.date,
val => {
if (val === TYPE.UNSPECIFIC) {
if (cron.value.week === TYPE.UNSPECIFIC) {
cron.value.week = TYPE.EVERY;
}
} else if (cron.value.week !== TYPE.UNSPECIFIC) {
cron.value.week = TYPE.UNSPECIFIC;
}
}
);
watch(
() => cron.value.week,
val => {
if (val === TYPE.UNSPECIFIC) {
if (cron.value.date === TYPE.UNSPECIFIC) {
cron.value.date = TYPE.EVERY;
}
} else if (cron.value.date !== TYPE.UNSPECIFIC) {
cron.value.date = TYPE.UNSPECIFIC;
}
}
);
</script>
<template>
<div class="cron-wrapper" :style="{ maxWidth: width }">
<NTabs v-model:value="activeKey" class="cron-tabs" type="segment">
<NTabPane v-for="field in fields" :key="field.value" :name="field.value" :tab="field.label">
<CronBase v-model="cron[field.value]" class="cron-base" :field="field" :locale="lang" />
</NTabPane>
</NTabs>
<div class="expression">
<div class="title">
<span class="label">{{ expressionLabel }}</span>
</div>
<div class="h-9px"></div>
<span class="content">{{ expression }}</span>
</div>
<div class="preview">
<div class="title">
<span class="label">{{ previewLabel }}</span>
</div>
<div class="h-9px"></div>
<ul class="list">
<li v-for="(preview, index) in previews" :key="preview">
<span class="index">{{ index + 1 }}</span>
<span>{{ preview }}</span>
</li>
</ul>
</div>
</div>
</template>
<style></style>
<style lang="scss" scoped>
.cron-wrapper {
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.06);
.cron-tabs {
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 3px;
.cron-base {
padding: 0 16px 16px 18px;
}
}
.expression,
.preview {
margin: 32px 0;
padding: 16px;
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 3px;
.title {
display: flex;
justify-content: center;
margin-top: -28px;
}
.label {
padding: 0 16px;
background-color: #ffffff;
}
.list {
margin-bottom: 0;
li:not(:first-child) {
margin-top: 3px;
}
}
.index {
display: inline-block;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
border-radius: 50%;
background-color: rgb(var(--primary-color));
color: #fff;
margin-right: 10px;
font-size: 13px;
}
}
.preview {
margin-bottom: 0;
}
}
</style>

View File

@ -0,0 +1,247 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { DATE, DEFAULT_LOCALE, LOCALE_EN, TYPE, WEEK, YEAR } from '../../shared/constants';
import { generateSpecifies, weekLetterToNumber } from '../../shared/utils';
import Locales from '../../i18n';
import InputNumber from './input-number.vue';
defineOptions({ name: 'CronBase' });
interface Props {
modelValue: string;
field: {
value: I18n.FieldType;
label: string;
min: number;
max: number;
};
locale: I18n.LocaleType;
}
const props = withDefaults(defineProps<Props>(), {
locale: DEFAULT_LOCALE
});
interface Emits {
(e: 'update:modelValue', value: string): void;
}
const emit = defineEmits<Emits>();
const labels = props.field.value === 'week' ? Object.values(Locales[props.locale].week) : null;
const { min, max, value: fieldValue } = props.field;
const type = ref(TYPE.EVERY);
const range = ref([min, min + 1]);
const step = ref([min, 1]);
const well = ref([min, 1]);
const specify = ref<number[]>([]);
const weekday = ref(1);
const lastDayOfWeek = ref(0);
const rangeStart = ref<[number, number]>([min, max - 1]);
const stepLeft = ref<[number, number]>([min, max]);
const stepRight = ref<[number, number]>([1, max]);
const wellLeft = ref<[number, number]>([0, 0]);
const wellRight = ref<[number, number]>([0, 0]);
const specifies = ref(generateSpecifies(min, max, labels));
if (fieldValue === WEEK) {
wellLeft.value = [1, 5];
wellRight.value = [min, max];
}
const label = computed(() => {
const i18n = Locales[props.locale];
const { type: typeLabel, fieldAlias: fieldAliasLabel } = i18n;
return {
empty: typeLabel.empty,
every: `${typeLabel.every}${fieldAliasLabel[props.field.value]}`,
unspecific: typeLabel.unspecific,
range: [
typeLabel.range[0],
(props.field.value === WEEK || props.locale === LOCALE_EN ? '' : props.field.label) + typeLabel.range[1],
props.field.value === WEEK || props.locale === LOCALE_EN ? '' : props.field.label
],
step: [
typeLabel.step[0],
props.field.label + typeLabel.step[1],
fieldAliasLabel[props.field.value] + typeLabel.step[2]
],
well: typeLabel.well,
weekday: typeLabel.weekday,
lastWeekday: typeLabel.lastWeekday,
lastDayOfDate: typeLabel.lastDayOfDate,
lastDayOfWeek: typeLabel.lastDayOfWeek,
specify: typeLabel.specify
};
});
const isEnWeek = computed(() => props.field.value === WEEK && props.locale === LOCALE_EN);
const rangeEnd = computed<[number, number]>(() => [range.value[0] + 1, props.field.max]);
const isEmpty = computed(() => props.field.value === YEAR);
const isUnspecific = computed(() => [DATE, WEEK].includes(props.field.value));
const isStep = computed(() => props.field.value !== WEEK);
const isWell = computed(() => props.field.value === WEEK);
const isLastDayOfDate = computed(() => props.field.value === DATE);
const isLastDayOfWeek = computed(() => props.field.value === WEEK);
const isWeekday = computed(() => props.field.value === DATE);
const isLastWeekday = computed(() => props.field.value === DATE);
const value = computed(() => {
switch (type.value) {
case TYPE.EMPTY:
case TYPE.UNSPECIFIC:
case TYPE.LAST_WEEKDAY:
case TYPE.EVERY:
return type.value;
case TYPE.RANGE:
return range.value.join(type.value);
case TYPE.STEP:
return step.value.join(type.value);
case TYPE.WELL:
return well.value.join(type.value);
case TYPE.WEEKDAY:
return `${weekday.value}${type.value}`;
case TYPE.LAST_DAY:
return props.field.value === DATE ? type.value : `${lastDayOfWeek.value}${type.value}`;
case TYPE.SPECIFY: {
const specifyValue = specify.value;
return specifyValue.length ? specifyValue.sort((a, b) => a - b).join(type.value) : `${specifyValue[0]}`;
}
default:
return '';
}
});
watch(
() => props.modelValue,
val => {
let data = val;
if (props.field.value === WEEK) {
data = weekLetterToNumber(val).replaceAll('7', '0');
}
if ([TYPE.EMPTY, TYPE.UNSPECIFIC, TYPE.LAST_DAY, TYPE.LAST_WEEKDAY, TYPE.EVERY].includes(data)) {
type.value = data;
} else if (data.includes(TYPE.RANGE)) {
type.value = TYPE.RANGE;
range.value = data.split(TYPE.RANGE).map(i => Number.parseInt(i, 10));
} else if (data.includes(TYPE.STEP)) {
type.value = TYPE.STEP;
step.value = data.split(TYPE.STEP).map(i => Number.parseInt(i, 10));
} else if (data.includes(TYPE.WELL)) {
type.value = TYPE.WELL;
well.value = data.split(TYPE.WELL).map(i => Number.parseInt(i, 10));
} else if (data.includes(TYPE.WEEKDAY)) {
type.value = TYPE.WEEKDAY;
weekday.value = Number.parseInt(data, 10);
} else if (data.includes(TYPE.LAST_DAY)) {
type.value = TYPE.LAST_DAY;
lastDayOfWeek.value = Number.parseInt(data, 10);
} else {
type.value = TYPE.SPECIFY;
specify.value = data.split(TYPE.SPECIFY).map(i => Number.parseInt(i, 10));
}
}
);
watch(
() => value.value,
val => {
emit('update:modelValue', val);
}
);
const onRangeStartChange = (val: number) => {
const [, ranEnd] = range.value;
if (val >= ranEnd) {
range.value[1] = val + 1;
}
};
const onCheckboxGroupChange = (val: string[]) => {
let checkType = TYPE.SPECIFY;
if (val.length === 0) {
checkType = props.field.value === YEAR ? TYPE.EMPTY : TYPE.EVERY;
}
type.value = checkType;
};
</script>
<template>
<NRadioGroup v-model:value="type" class="flex-col">
<NRadio v-if="isEmpty && field.value !== YEAR" class="cron-radio" :value="TYPE.EMPTY">{{ label.empty }}</NRadio>
<NRadio class="cron-radio" :value="TYPE.EVERY">{{ label.every }}</NRadio>
<NRadio v-if="isEmpty && field.value === YEAR" class="cron-radio" :value="TYPE.EMPTY">{{ label.empty }}</NRadio>
<NRadio v-if="isUnspecific" class="cron-radio" :value="TYPE.UNSPECIFIC">{{ label.unspecific }}</NRadio>
<div class="cron-radio flex items-center justify-start gap-5px">
<NRadio :value="TYPE.RANGE" />
{{ label.range[0] }}
<InputNumber
v-model="range[0]"
:range="rangeStart"
:field-value="field.value"
:locale="locale"
@update:value="onRangeStartChange"
/>
{{ label.range[1] }}
<InputNumber v-model="range[1]" :range="rangeEnd" :field-value="field.value" :locale="locale" />
{{ label.range[2] }}
</div>
<div v-if="isStep" class="cron-radio flex items-center justify-start gap-5px">
<NRadio :value="TYPE.STEP" />
<span>{{ label.step[0] }}</span>
<InputNumber v-model="step[0]" :range="stepLeft" />
<span>{{ label.step[1] }}</span>
<InputNumber v-model="step[1]" :range="stepRight" />
<span>{{ label.step[2] }}</span>
</div>
<div v-if="isWell" class="cron-radio flex items-center justify-start gap-5px">
<NRadio :value="TYPE.WELL" />
{{ label.well[0] }}
<InputNumber v-model="well[1]" :range="[...wellLeft]" />
{{ label.well[1] }}
<InputNumber v-model="well[0]" :range="[...wellRight]" :field-value="field.value" :locale="locale" />
</div>
<div v-if="isWeekday" class="cron-radio flex items-center justify-start gap-5px">
<NRadio :value="TYPE.WEEKDAY" />
{{ label.weekday[0] }}
<InputNumber v-model="weekday" :range="rangeStart" />
{{ label.weekday[1] }}
</div>
<NRadio v-if="isLastWeekday" class="cron-radio" :value="TYPE.LAST_WEEKDAY">{{ label.lastWeekday }}</NRadio>
<NRadio v-if="isLastDayOfDate" class="cron-radio" :value="TYPE.LAST_DAY">{{ label.lastDayOfDate }}</NRadio>
<div v-if="isLastDayOfWeek" class="cron-radio flex items-center justify-start gap-5px">
<NRadio v-if="isLastDayOfWeek" :value="TYPE.LAST_DAY" />
{{ label.lastDayOfWeek }}
<InputNumber v-model="lastDayOfWeek" :range="[0, 6]" :field-value="field.value" :locale="locale" />
</div>
<div class="cron-radio flex flex-wrap items-center justify-start gap-5px">
<NRadio class="cron-radio" :value="TYPE.SPECIFY">{{ label.specify }}</NRadio>
<NCheckboxGroup
v-model:checked="specify"
class="p-l-22px"
:class="{ 'checkbox-group-en-week': isEnWeek }"
@update:checked="onCheckboxGroupChange"
>
<NCheckbox
v-for="option in specifies"
:key="option.value"
:label="option.label"
:value="option.value"
class="min-w-50px"
/>
</NCheckboxGroup>
</div>
</NRadioGroup>
</template>
<style scoped lang="scss">
.cron-radio:not(:first-child) {
margin-top: 12px;
}
</style>

View File

@ -0,0 +1,59 @@
<script setup lang="ts">
import { computed } from 'vue';
import { formatterWeek, parserWeek } from '../../shared/utils';
import { WEEK } from '../../shared/constants';
defineOptions({ name: 'InputNumber' });
interface Props {
modelValue: number;
range: [number, number];
fieldValue?: string;
locale?: I18n.LocaleType;
}
const props = defineProps<Props>();
interface Emits {
(e: 'update:modelValue', value: number): void;
(e: 'change', value: number | null): void;
}
const emit = defineEmits<Emits>();
const value = computed({
get() {
return props.modelValue;
},
set(val) {
emit('update:modelValue', val);
}
});
const formatter = (val: number) => {
return props.fieldValue === WEEK ? formatterWeek(val.toString(), props.locale!) : null;
};
const parser = (val: string) => {
return props.fieldValue === WEEK ? parserWeek(val, props.locale!) : null;
};
const onChange = (val: number | null): void => {
emit('change', val);
};
</script>
<template>
<NInputNumber
v-model:value="value"
:min="range[0]"
:max="range[1]"
class="w-90px"
size="small"
:formatter="formatter"
:parser="parser"
@update:value="onChange"
/>
</template>
<style scoped></style>

53
packages/cron-input/src/i18n/i18n.d.ts vendored Normal file
View File

@ -0,0 +1,53 @@
declare namespace I18n {
type LocaleType = 'en-US' | 'zh-CN';
type FieldType = 'second' | 'minute' | 'hour' | 'date' | 'month' | 'week' | 'year';
interface LocaleFields {
second: string;
minute: string;
hour: string;
date: string;
month: string;
week: string;
year: string;
}
interface LocaleFieldAlias {
second: string;
minute: string;
hour: string;
date: string;
month: string;
week: string;
year: string;
}
interface LocaleTypes {
empty: string;
every: string;
unspecific: string;
range: string[];
step: string[];
well: string[];
weekday: string[];
lastWeekday: string;
lastDayOfDate: string;
lastDayOfWeek: string;
specify: string;
}
interface LocaleWeek {
[key: string]: string;
}
interface Translations {
field: LocaleFields;
fieldAlias: LocaleFieldAlias;
type: LocaleTypes;
week: LocaleWeek;
expression: string;
preview: string[];
previewError: string;
}
}

View File

@ -0,0 +1,10 @@
import { LOCALE_CN, LOCALE_EN } from '../shared/constants';
import zhCN from './langs/zh-cn';
import enUS from './langs/en-us';
const locales: Record<string, I18n.Translations> = {
[LOCALE_CN]: zhCN,
[LOCALE_EN]: enUS
};
export default locales;

View File

@ -0,0 +1,47 @@
const englishTranslations: I18n.Translations = {
field: {
second: 'Second',
minute: 'Minute',
hour: 'Hour',
date: 'Date',
month: 'Month',
week: 'Week',
year: 'Year'
},
fieldAlias: {
second: 'second',
minute: 'minute',
hour: 'hour',
date: 'date',
month: 'month',
week: 'week',
year: 'year'
},
type: {
empty: 'Empty',
every: 'Every ',
unspecific: 'Unspecific',
range: ['From ', ' to ', ''],
step: ['Start with ', ', execute every', ''],
well: ['The ', ''],
weekday: ['Nearest weekday to the ', ' of current month'],
lastWeekday: 'Last weekday of current month',
lastDayOfDate: 'Last day of current month',
lastDayOfWeek: 'Last ',
specify: 'Specify'
},
week: {
Sunday: 'Sunday',
Monday: 'Monday',
Tuesday: 'Tuesday',
Wednesday: 'Wednesday',
Thursday: 'Thursday',
Friday: 'Friday',
Saturday: 'Saturday'
},
expression: 'The complete expression',
preview: ['Last ', ' runtimes'],
previewError: 'This expression is temporarily unparsed!'
};
export default englishTranslations;

View File

@ -0,0 +1,47 @@
const chineseTranslations: I18n.Translations = {
field: {
second: '秒',
minute: '分',
hour: '时',
date: '日',
month: '月',
week: '周',
year: '年'
},
fieldAlias: {
second: '秒钟',
minute: '分钟',
hour: '小时',
date: '天',
month: '个月',
week: '星期',
year: '年'
},
type: {
empty: '不指定',
every: '每',
unspecific: '不指定',
range: ['从', '到', ''],
step: ['从', '开始,每', '执行一次'],
well: ['当月第', '个'],
weekday: ['离当月', '号最近的那个工作日'],
lastWeekday: '当月最后一个工作日',
lastDayOfDate: '当月最后一天',
lastDayOfWeek: '当月最后一个',
specify: '指定'
},
week: {
Sunday: '星期日',
Monday: '星期一',
Tuesday: '星期二',
Wednesday: '星期三',
Thursday: '星期四',
Friday: '星期五',
Saturday: '星期六'
},
expression: '完整表达式',
preview: ['最近', '次运行时间'],
previewError: '此表达式暂时无法解析!'
};
export default chineseTranslations;

View File

@ -0,0 +1,3 @@
import Cron from './components/cron.vue';
export default Cron;

View File

@ -0,0 +1,77 @@
export const MIN_SECOND = 0;
export const MAX_SECOND = 59;
export const MIN_MINUTE = 0;
export const MAX_MINUTE = 59;
export const MIN_HOUR = 0;
export const MAX_HOUR = 23;
export const MIN_DATE = 1;
export const MAX_DATE = 31;
export const MIN_MONTH = 1;
export const MAX_MONTH = 12;
export const MIN_WEEK = 0;
export const MAX_WEEK = 6;
export const MIN_YEAR = new Date().getFullYear();
export const MAX_YEAR = 2099;
export const SUNDAY = 'sunday';
export const MONDAY = 'monday';
export const TUESDAY = 'tuesday';
export const WEDNESDAY = 'wednesday';
export const THURSDAY = 'thursday';
export const FRIDAY = 'friday';
export const SATURDAY = 'saturday';
export const WEEKS = [
{ value: SUNDAY, abbr: 'SUN', index: '0' },
{ value: MONDAY, abbr: 'MON', index: '1' },
{ value: TUESDAY, abbr: 'TUE', index: '2' },
{ value: WEDNESDAY, abbr: 'WED', index: '3' },
{ value: THURSDAY, abbr: 'THU', index: '4' },
{ value: FRIDAY, abbr: 'FRI', index: '5' },
{ value: SATURDAY, abbr: 'SAT', index: '6' }
];
export const WEEK_INDEX_REGEXP = new RegExp(WEEKS.map(({ index }) => `(?<!#)${index}`).join('|'), 'g');
export const WEEK_LETTER_REGEXP = new RegExp(WEEKS.map(({ abbr }) => abbr).join('|'), 'g');
export const TYPE = {
EVERY: '*',
RANGE: '-',
STEP: '/',
SPECIFY: ',',
UNSPECIFIC: '?',
EMPTY: '',
LAST_DAY: 'L',
LAST_WEEKDAY: 'LW',
WELL: '#',
WEEKDAY: 'W'
};
export const SECOND = 'second';
export const MINUTE = 'minute';
export const HOUR = 'hour';
export const DATE = 'date';
export const MONTH = 'month';
export const WEEK = 'week';
export const YEAR = 'year';
export const FIELDS: { value: I18n.FieldType; min: number; max: number }[] = [
{ value: SECOND, min: MIN_SECOND, max: MAX_SECOND },
{ value: MINUTE, min: MIN_MINUTE, max: MAX_MINUTE },
{ value: HOUR, min: MIN_HOUR, max: MAX_HOUR },
{ value: DATE, min: MIN_DATE, max: MAX_DATE },
{ value: MONTH, min: MIN_MONTH, max: MAX_MONTH },
{ value: WEEK, min: MIN_WEEK, max: MAX_WEEK },
{ value: YEAR, min: MIN_YEAR, max: MAX_YEAR }
];
export const LOCALE_EN: I18n.LocaleType = 'en-US';
export const LOCALE_CN: I18n.LocaleType = 'zh-CN';
export const DEFAULT_CRON_EXPRESSION = '* * * * * ?';
export const DEFAULT_LOCALE: I18n.LocaleType = LOCALE_CN;

View File

@ -0,0 +1,80 @@
/* eslint-disable @typescript-eslint/no-shadow */
import I18n from '../i18n';
import { WEEK, WEEKS, WEEK_INDEX_REGEXP, WEEK_LETTER_REGEXP } from './constants';
/**
* .
*
* @param min - .
* @param max - .
* @param labels - .
*/
export function generateSpecifies(
min: number,
max: number,
labels: string[] | null
): { value: number; label: string }[] {
const specifies = [];
let index = 0;
for (let specify = min; specify <= max; specify += 1) {
specifies.push({ value: specify, label: labels ? labels[index] : specify.toString() });
index += 1;
}
return specifies;
}
/**
* .
*
* @param value - .
*/
export function zerofill(value: number): string {
const prefix = value < 10 ? '0' : '';
return `${prefix}${value}`;
}
/**
* .
*
* @param value - .
*/
export function weekNumberToLetter(value: string): string {
return value.replace(WEEK_INDEX_REGEXP, value => WEEKS.find(({ index }) => [index].includes(value))?.abbr || value);
}
/**
* .
*
* @param value - .
*/
export function weekLetterToNumber(value: string): string {
return value.replace(WEEK_LETTER_REGEXP, value => WEEKS.find(({ abbr }) => abbr === value)?.index || value);
}
/**
* <a-input-number> formatter .
*
* @param value - .
* @param locale - .
*/
export function formatterWeek(value: string, locale: I18n.LocaleType): string {
const day = WEEKS.find(({ index }) => index === value)?.value;
return I18n[locale].week[day!];
}
/**
* <a-input-number> parser .
*
* @param value - .
* @param locale - .
*/
export function parserWeek(value: string, locale: I18n.LocaleType): number {
const [key] = Object.entries(I18n[locale][WEEK]).find(([, val]) => val === value) as [string, string];
return WEEKS.findIndex(({ value }) => value === key);
}

View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"types": ["node", "naive-ui/volar"],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@ -20,6 +20,9 @@ importers:
'@sa/color-palette':
specifier: workspace:*
version: link:packages/color-palette
'@sa/cron-input':
specifier: workspace:*
version: link:packages/cron-input
'@sa/hooks':
specifier: workspace:*
version: link:packages/hooks
@ -188,6 +191,12 @@ importers:
specifier: 2.9.3
version: 2.9.3
packages/cron-input:
dependencies:
cron-parser:
specifier: ^4.9.0
version: 4.9.0
packages/hooks:
dependencies:
'@sa/axios':
@ -3239,6 +3248,13 @@ packages:
vary: 1.1.2
dev: true
/cron-parser@4.9.0:
resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==}
engines: {node: '>=12.0.0'}
dependencies:
luxon: 3.4.4
dev: false
/cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@ -5644,6 +5660,11 @@ packages:
engines: {node: '>=12'}
dev: true
/luxon@3.4.4:
resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==}
engines: {node: '>=12'}
dev: false
/magic-string@0.30.6:
resolution: {integrity: sha512-n62qCLbPjNjyo+owKtveQxZFZTBm+Ms6YoGD23Wew6Vw337PElFNifQpknPruVRQV57kVShPnLGo9vWxVhpPvA==}
engines: {node: '>=12'}

View File

@ -45,10 +45,7 @@ export const generatedRoutes: GeneratedRoute[] = [
component: 'layout.base$view.about',
meta: {
title: 'about',
i18nKey: 'route.about',
icon: 'fluent:book-information-24-regular',
order: 10,
hideInMenu: true
i18nKey: 'route.about'
}
},
{

View File

@ -41,6 +41,7 @@ declare module 'vue' {
NButton: typeof import('naive-ui')['NButton']
NCard: typeof import('naive-ui')['NCard']
NCheckbox: typeof import('naive-ui')['NCheckbox']
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
NColorPicker: typeof import('naive-ui')['NColorPicker']
NDataTable: typeof import('naive-ui')['NDataTable']
NDatePicker: typeof import('naive-ui')['NDatePicker']