diff --git a/package.json b/package.json index 31113036..ddb10d2a 100644 --- a/package.json +++ b/package.json @@ -28,18 +28,18 @@ }, "license": "MIT", "devDependencies": { - "@crowdin/crowdin-api-client": "^1.51.1", + "@crowdin/crowdin-api-client": "^1.52.0", "@emotion/babel-plugin": "^11.13.5", "@emotion/css": "^11.13.5", "@rsdoctor/rspack-plugin": "^1.5.0", - "@rspack/cli": "^1.7.2", - "@rspack/core": "^1.7.2", - "@swc/core": "^1.15.8", + "@rspack/cli": "^1.7.3", + "@rspack/core": "^1.7.3", + "@swc/core": "^1.15.10", "@swc/jest": "^0.2.39", - "@types/chrome": "0.1.33", + "@types/chrome": "0.1.35", "@types/decompress": "^4.2.7", "@types/jest": "^30.0.0", - "@types/node": "^25.0.8", + "@types/node": "^25.0.9", "@types/punycode": "^2.1.4", "@vue/babel-plugin-jsx": "^2.0.1", "babel-loader": "^10.0.0", @@ -65,7 +65,7 @@ "echarts": "^6.0.0", "element-plus": "2.13.1", "punycode": "^2.3.1", - "vue": "^3.5.26", + "vue": "^3.5.27", "vue-router": "^4.6.4" }, "engines": { diff --git a/src/pages/app/components/Dashboard/components/TopKVisit/Title/TitleSelect.tsx b/src/pages/app/components/Dashboard/components/TopKVisit/Title/TitleSelect.tsx index 24f1840c..b2e3d89f 100644 --- a/src/pages/app/components/Dashboard/components/TopKVisit/Title/TitleSelect.tsx +++ b/src/pages/app/components/Dashboard/components/TopKVisit/Title/TitleSelect.tsx @@ -53,9 +53,8 @@ const TitleSelect = defineComponent(({ values, field }) => { onChange={val => filter[field] = val as number} popperOptions={{ placement: 'bottom' }} popperClass={popoverCls} - > - {values.map(k => )} - + options={values.map(k => ({ label: k, value: k }))} + /> ) }, { props: ['field', "values"] }) diff --git a/src/pages/app/components/Option/components/AppearanceOption/DarkModeInput.tsx b/src/pages/app/components/Option/components/AppearanceOption/DarkModeInput.tsx index 3b2a3bdf..9622d844 100644 --- a/src/pages/app/components/Option/components/AppearanceOption/DarkModeInput.tsx +++ b/src/pages/app/components/Option/components/AppearanceOption/DarkModeInput.tsx @@ -5,10 +5,13 @@ * https://opensource.org/licenses/MIT */ import { t } from "@app/locale" -import { ElOption, ElSelect, ElTimePicker } from "element-plus" +import { ElSelect, ElTimePicker } from "element-plus" import { computed, defineComponent, StyleValue } from "vue" const ALL_MODES: timer.option.DarkMode[] = ["default", "on", "off", "timed"] +const labelOfMode = (mode: timer.option.DarkMode) => mode === 'default' + ? t(msg => msg.option.followBrowser) + : t(msg => msg.option.appearance.darkMode.options[mode]) function computeSecondToDate(secondOfDate: number): Date { const now = new Date() @@ -46,14 +49,8 @@ const _default = defineComponent(props => { size="small" style={{ width: "120px" }} onChange={val => props.onChange?.(val as timer.option.DarkMode, [props.startSecond, props.endSecond])} - > - { - ALL_MODES.map(value => value === 'default' ? msg.option.followBrowser : msg.option.appearance.darkMode.options[value])} - />) - } - + options={ALL_MODES.map(value => ({ value, label: labelOfMode(value) }))} + /> {props.modelValue === "timed" && <> msg.option.followBrowser + const SORTED_LOCALES: timer.Locale[] = ALL_LOCALES // Keep the locale same as this browser first position .sort((a, _b) => a === localeSameAsBrowser ? -1 : 0) -const allLocaleOptions: timer.option.LocaleOption[] = ["default", ...SORTED_LOCALES] + +const allLocaleOptions = (["default", ...SORTED_LOCALES] satisfies timer.option.LocaleOption[]).map(locale => ({ + value: locale, + label: locale === "default" ? t(FOLLOW_BROWSER) : localeMessages[locale].name +})) function copy(target: timer.option.AppearanceOption, source: timer.option.AppearanceOption) { target.displayWhitelistMenu = source.displayWhitelistMenu @@ -42,7 +48,7 @@ function copy(target: timer.option.AppearanceOption, source: timer.option.Appear const DEFAULT_ANIMA_DURATION = defaultAppearance().chartAnimationDuration const DEFAULT_SIDE_PANEL_ENABLED = true -const FOLLOW_BROWSER: I18nKey = msg => msg.option.followBrowser + const _default = defineComponent((_props, ctx) => { const { option } = useOption({ @@ -107,12 +113,8 @@ const _default = defineComponent((_props, ctx) => { style={{ width: "120px" }} onChange={(newVal: timer.option.LocaleOption) => handleLocaleChange(newVal)} filterable - > - {allLocaleOptions.map(locale => )} - + options={allLocaleOptions} + /> {!IS_ANDROID && <> { modelValue={backupType.value} size="small" onChange={(val: timer.backup.Type) => backupType.value = val} - > - {ALL_TYPES.map(type => )} - + options={ALL_TYPES.map(value => ({ value, label: TYPE_NAMES[value] }))} + /> { class={levelSelectStyle.cls} style={{ width: `${levelSelectStyle.width}px` }} onChange={handleLevelChange} - > - {ALL_LEVEL.map(item => msg.option.limit.level[item])} />)} - + options={ALL_LEVEL.map(value => ({ value, label: t(msg => msg.option.limit.level[value]) }))} + /> { .then(() => option.limitVerifyDifficulty = val) .catch(console.warn) } - > - {ALL_DIFF.map(item => msg.option.limit.level.verificationDifficulty[item])} />)} - + options={ALL_DIFF.map(value => ({ value, label: t(msg => msg.option.limit.level.verificationDifficulty[value]) }))} + /> { size="small" style={{ width: '120px' }} onChange={(val: timer.option.WeekStartOption) => option.weekStart = val} - > - {weekStartOptionPairs.map(([val, label]) => )} - + options={weekStartOptionPairs.map(([value, label]) => ({ value, label }))} + /> }) diff --git a/src/pages/app/components/common/category/CategorySelect/index.tsx b/src/pages/app/components/common/category/CategorySelect/index.tsx index b0ce94d2..bc74edab 100644 --- a/src/pages/app/components/common/category/CategorySelect/index.tsx +++ b/src/pages/app/components/common/category/CategorySelect/index.tsx @@ -48,4 +48,4 @@ const CategorySelect = defineComponent((props, ctx) => { ) }, { props: ['clearable', 'modelValue', 'size', 'width', 'onVisibleChange', 'onChange'] }) -export default CategorySelect \ No newline at end of file +export default CategorySelect diff --git a/src/pages/app/components/common/filter/CategoryFilter.tsx b/src/pages/app/components/common/filter/CategoryFilter.tsx index a8f494ed..2631e308 100644 --- a/src/pages/app/components/common/filter/CategoryFilter.tsx +++ b/src/pages/app/components/common/filter/CategoryFilter.tsx @@ -1,7 +1,7 @@ import { useCategory } from "@app/context" import { t } from "@app/locale" import { CATE_NOT_SET_ID } from "@util/site" -import { ElOption, ElSelect } from "element-plus" +import { ElSelect } from "element-plus" import { computed, defineComponent, type StyleValue } from "vue" type Props = ModelValue & { @@ -12,9 +12,9 @@ type Props = ModelValue & { const CategoryFilter = defineComponent(props => { const cate = useCategory() - const displayCategories = computed(() => [ - { id: CATE_NOT_SET_ID, name: t(msg => msg.shared.cate.notSet) } satisfies timer.site.Cate, - ...cate.all, + const options = computed(() => [ + { value: CATE_NOT_SET_ID, label: t(msg => msg.shared.cate.notSet) }, + ...cate.all.map(c => ({ value: c.id, label: c.name })) ]) return () => cate.enabled ? ( @@ -29,9 +29,8 @@ const CategoryFilter = defineComponent(props => { onClear={() => props.onChange?.(undefined)} placeholder={t(msg => msg.siteManage.column.cate)} style={{ width: '200px' } satisfies StyleValue} - > - {displayCategories.value?.map(cate => )} - + options={options.value} + /> ) : null }, { props: ['modelValue', 'onChange', 'disabled', 'useCache'] }) diff --git a/src/pages/app/components/common/filter/MultiSelectFilterItem.tsx b/src/pages/app/components/common/filter/MultiSelectFilterItem.tsx index 138404ae..f943eaa6 100644 --- a/src/pages/app/components/common/filter/MultiSelectFilterItem.tsx +++ b/src/pages/app/components/common/filter/MultiSelectFilterItem.tsx @@ -1,44 +1,42 @@ import { useCached } from "@hooks" -import { ElOption, ElSelect } from "element-plus" -import { defineComponent, type PropType, watch } from "vue" +import { ElSelect } from "element-plus" +import { defineComponent, watch } from "vue" import { useRoute } from "vue-router" import { SELECT_WRAPPER_STYLE } from "./common" -const MultiSelectFilterItem = defineComponent({ - props: { - defaultValue: Array as PropType<(string | number)[]>, - /** - * Whether to save the value in the localStorage with {@param historyName} - */ - historyName: String, - placeholder: String, - disabled: Boolean, - options: Array as PropType<{ value: string | number, label?: string }[]>, - }, - emits: { - change: (_val: (string | number)[]) => true, - }, - setup(props, ctx) { - const cacheKey = props.historyName && `__filter_item_multi_select_${useRoute().path}_${props.historyName}` - const { data, setter } = useCached(cacheKey, props.defaultValue) - watch(data, () => ctx.emit('change', data.value ?? [])) +type Data = string | number - return () => ( - setter([])} - placeholder={props.placeholder} - style={SELECT_WRAPPER_STYLE} - > - {props.options?.map(({ value, label }) => )} - - ) - } -}) +type Props = { + defaultValue?: Data[] + /** + * Whether to save the value in the localStorage with {@param historyName} + */ + historyName?: string + placeholder?: string + disabled?: boolean + options?: { value: Data, label?: string }[] + onChange?: (val: Data[]) => void +} + +const MultiSelectFilterItem = defineComponent(props => { + const cacheKey = props.historyName && `__filter_item_multi_select_${useRoute().path}_${props.historyName}` + const { data, setter } = useCached(cacheKey, props.defaultValue) + watch(data, val => props.onChange?.(val ?? [])) + + return () => ( + setter([])} + placeholder={props.placeholder} + style={SELECT_WRAPPER_STYLE} + options={props.options} + /> + ) +}, { props: ['defaultValue', 'historyName', 'placeholder', 'disabled', 'options'] }) export default MultiSelectFilterItem \ No newline at end of file diff --git a/src/pages/app/components/common/filter/SelectFilterItem.tsx b/src/pages/app/components/common/filter/SelectFilterItem.tsx index edf39cde..25ee0e99 100644 --- a/src/pages/app/components/common/filter/SelectFilterItem.tsx +++ b/src/pages/app/components/common/filter/SelectFilterItem.tsx @@ -6,37 +6,33 @@ */ import { useCached } from "@hooks" -import { ElOption, ElSelect } from "element-plus" -import { defineComponent, watch, type PropType } from "vue" +import { ElSelect } from "element-plus" +import { defineComponent, watch } from "vue" import { useRoute } from "vue-router" import { SELECT_WRAPPER_STYLE } from "./common" -const _default = defineComponent({ - props: { - defaultValue: String, - /** - * Whether to save the value in the localStorage with {@param historyName} - */ - historyName: String, - options: Object as PropType> - }, - emits: { - select: (_val: string | undefined) => true - }, - setup(props, ctx) { - const cacheKey = props.historyName && `__filter_item_select_${useRoute().path}_${props.historyName}` - const { data, setter } = useCached(cacheKey, props.defaultValue) - watch(data, () => ctx.emit('select', data.value)) - return () => ( - - {Object.entries(props.options || {}).map(([value, label]) => )} - - ) - } -}) +type Props = { + defaultValue?: string + /** + * Whether to save the value in the localStorage with {@param historyName} + */ + historyName?: string + options: Record + onSelect?: (val: string | undefined) => void +} -export default _default \ No newline at end of file +const SelectFilterItem = defineComponent(props => { + const cacheKey = props.historyName && `__filter_item_select_${useRoute().path}_${props.historyName}` + const { data, setter } = useCached(cacheKey, props.defaultValue) + watch(data, val => props.onSelect?.(val)) + return () => ( + ({ label, value }))} + /> + ) +}, { props: ['defaultValue', 'historyName', 'options', 'onSelect'] }) + +export default SelectFilterItem \ No newline at end of file