mirror of
https://github.com/element-plus/element-plus.git
synced 2025-01-18 10:59:10 +08:00
refactor(component): improve code (#7959)
This commit is contained in:
parent
ff5ea7e2ed
commit
3adb0f2077
@ -1,5 +1,4 @@
|
|||||||
[
|
[
|
||||||
"packages/components/autocomplete/",
|
|
||||||
"packages/components/cascader-panel/",
|
"packages/components/cascader-panel/",
|
||||||
"packages/components/cascader/",
|
"packages/components/cascader/",
|
||||||
"packages/components/checkbox/",
|
"packages/components/checkbox/",
|
||||||
|
@ -5,12 +5,10 @@
|
|||||||
:class="[ns.b(), ns.m(type), ns.is('center', center), ns.is(effect)]"
|
:class="[ns.b(), ns.m(type), ns.is('center', center), ns.is(effect)]"
|
||||||
role="alert"
|
role="alert"
|
||||||
>
|
>
|
||||||
<el-icon
|
<el-icon v-if="showIcon && iconComponent" :class="iconClass">
|
||||||
v-if="showIcon && iconComponent"
|
|
||||||
:class="[ns.e('icon'), isBigIcon]"
|
|
||||||
>
|
|
||||||
<component :is="iconComponent" />
|
<component :is="iconComponent" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
|
|
||||||
<div :class="ns.e('content')">
|
<div :class="ns.e('content')">
|
||||||
<span
|
<span
|
||||||
v-if="title || $slots.title"
|
v-if="title || $slots.title"
|
||||||
@ -58,21 +56,21 @@ const slots = useSlots()
|
|||||||
|
|
||||||
const ns = useNamespace('alert')
|
const ns = useNamespace('alert')
|
||||||
|
|
||||||
// state
|
|
||||||
const visible = ref(true)
|
const visible = ref(true)
|
||||||
|
|
||||||
// computed
|
|
||||||
const iconComponent = computed(
|
const iconComponent = computed(
|
||||||
() => TypeComponentsMap[props.type] || TypeComponentsMap['info']
|
() => TypeComponentsMap[props.type] || TypeComponentsMap['info']
|
||||||
)
|
)
|
||||||
const isBigIcon = computed(
|
|
||||||
() => props.description || { [ns.is('big')]: slots.default }
|
const iconClass = computed(() => [
|
||||||
)
|
ns.e('icon'),
|
||||||
|
{ [ns.is('big')]: !!props.description || !!slots.default },
|
||||||
|
])
|
||||||
|
|
||||||
const isBoldTitle = computed(
|
const isBoldTitle = computed(
|
||||||
() => props.description || { [ns.is('bold')]: slots.default }
|
() => props.description || { [ns.is('bold')]: slots.default }
|
||||||
)
|
)
|
||||||
|
|
||||||
// methods
|
|
||||||
const close = (evt: MouseEvent) => {
|
const close = (evt: MouseEvent) => {
|
||||||
visible.value = false
|
visible.value = false
|
||||||
emit('close', evt)
|
emit('close', evt)
|
||||||
|
@ -6,13 +6,18 @@ import {
|
|||||||
isString,
|
isString,
|
||||||
} from '@element-plus/utils'
|
} from '@element-plus/utils'
|
||||||
import { useTooltipContentProps } from '@element-plus/components/tooltip'
|
import { useTooltipContentProps } from '@element-plus/components/tooltip'
|
||||||
import { UPDATE_MODEL_EVENT } from '@element-plus/constants'
|
import {
|
||||||
|
CHANGE_EVENT,
|
||||||
|
INPUT_EVENT,
|
||||||
|
UPDATE_MODEL_EVENT,
|
||||||
|
} from '@element-plus/constants'
|
||||||
|
|
||||||
import type { ExtractPropTypes } from 'vue'
|
import type { ExtractPropTypes } from 'vue'
|
||||||
import type Autocomplete from './autocomplete.vue'
|
import type Autocomplete from './autocomplete.vue'
|
||||||
import type { Placement } from '@element-plus/components/popper'
|
import type { Placement } from '@element-plus/components/popper'
|
||||||
import type { Awaitable } from '@element-plus/utils'
|
import type { Awaitable } from '@element-plus/utils'
|
||||||
|
|
||||||
export type AutocompleteData = { value: string }[]
|
export type AutocompleteData = Record<string, any>[]
|
||||||
export type AutocompleteFetchSuggestionsCallback = (
|
export type AutocompleteFetchSuggestionsCallback = (
|
||||||
data: AutocompleteData
|
data: AutocompleteData
|
||||||
) => void
|
) => void
|
||||||
@ -81,8 +86,8 @@ export type AutocompleteProps = ExtractPropTypes<typeof autocompleteProps>
|
|||||||
|
|
||||||
export const autocompleteEmits = {
|
export const autocompleteEmits = {
|
||||||
[UPDATE_MODEL_EVENT]: (value: string) => isString(value),
|
[UPDATE_MODEL_EVENT]: (value: string) => isString(value),
|
||||||
input: (value: string) => isString(value),
|
[INPUT_EVENT]: (value: string) => isString(value),
|
||||||
change: (value: string) => isString(value),
|
[CHANGE_EVENT]: (value: string) => isString(value),
|
||||||
focus: (evt: FocusEvent) => evt instanceof FocusEvent,
|
focus: (evt: FocusEvent) => evt instanceof FocusEvent,
|
||||||
blur: (evt: FocusEvent) => evt instanceof FocusEvent,
|
blur: (evt: FocusEvent) => evt instanceof FocusEvent,
|
||||||
clear: () => true,
|
clear: () => true,
|
||||||
|
@ -95,19 +95,25 @@ import {
|
|||||||
nextTick,
|
nextTick,
|
||||||
onMounted,
|
onMounted,
|
||||||
ref,
|
ref,
|
||||||
useAttrs as useCompAttrs,
|
useAttrs as useRawAttrs,
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { debounce } from 'lodash-unified'
|
import { debounce } from 'lodash-unified'
|
||||||
import { onClickOutside } from '@vueuse/core'
|
import { onClickOutside } from '@vueuse/core'
|
||||||
|
import { Loading } from '@element-plus/icons-vue'
|
||||||
import { useAttrs, useNamespace } from '@element-plus/hooks'
|
import { useAttrs, useNamespace } from '@element-plus/hooks'
|
||||||
import { generateId, isArray, throwError } from '@element-plus/utils'
|
import { generateId, isArray, throwError } from '@element-plus/utils'
|
||||||
import { UPDATE_MODEL_EVENT } from '@element-plus/constants'
|
import {
|
||||||
|
CHANGE_EVENT,
|
||||||
|
INPUT_EVENT,
|
||||||
|
UPDATE_MODEL_EVENT,
|
||||||
|
} from '@element-plus/constants'
|
||||||
import ElInput from '@element-plus/components/input'
|
import ElInput from '@element-plus/components/input'
|
||||||
import ElScrollbar from '@element-plus/components/scrollbar'
|
import ElScrollbar from '@element-plus/components/scrollbar'
|
||||||
import ElTooltip from '@element-plus/components/tooltip'
|
import ElTooltip from '@element-plus/components/tooltip'
|
||||||
import ElIcon from '@element-plus/components/icon'
|
import ElIcon from '@element-plus/components/icon'
|
||||||
import { Loading } from '@element-plus/icons-vue'
|
|
||||||
import { autocompleteEmits, autocompleteProps } from './autocomplete'
|
import { autocompleteEmits, autocompleteProps } from './autocomplete'
|
||||||
|
import type { AutocompleteData } from './autocomplete'
|
||||||
|
|
||||||
import type { StyleValue } from 'vue'
|
import type { StyleValue } from 'vue'
|
||||||
import type { TooltipInstance } from '@element-plus/components/tooltip'
|
import type { TooltipInstance } from '@element-plus/components/tooltip'
|
||||||
import type { InputInstance } from '@element-plus/components/input'
|
import type { InputInstance } from '@element-plus/components/input'
|
||||||
@ -122,73 +128,71 @@ const COMPONENT_NAME = 'ElAutocomplete'
|
|||||||
const props = defineProps(autocompleteProps)
|
const props = defineProps(autocompleteProps)
|
||||||
const emit = defineEmits(autocompleteEmits)
|
const emit = defineEmits(autocompleteEmits)
|
||||||
|
|
||||||
const ns = useNamespace('autocomplete')
|
|
||||||
let isClear = false
|
|
||||||
const attrs = useAttrs()
|
const attrs = useAttrs()
|
||||||
const compAttrs = useCompAttrs()
|
const rawAttrs = useRawAttrs()
|
||||||
const suggestions = ref<any[]>([])
|
const ns = useNamespace('autocomplete')
|
||||||
const highlightedIndex = ref(-1)
|
|
||||||
const dropdownWidth = ref('')
|
|
||||||
const activated = ref(false)
|
|
||||||
const suggestionDisabled = ref(false)
|
|
||||||
const loading = ref(false)
|
|
||||||
const inputRef = ref<InputInstance>()
|
const inputRef = ref<InputInstance>()
|
||||||
const regionRef = ref<HTMLElement>()
|
const regionRef = ref<HTMLElement>()
|
||||||
const popperRef = ref<TooltipInstance>()
|
const popperRef = ref<TooltipInstance>()
|
||||||
const listboxRef = ref<HTMLElement>()
|
const listboxRef = ref<HTMLElement>()
|
||||||
|
|
||||||
const listboxId = computed(() => {
|
let isClear = false
|
||||||
return ns.b(String(generateId()))
|
const suggestions = ref<AutocompleteData>([])
|
||||||
})
|
const highlightedIndex = ref(-1)
|
||||||
const styles = computed(() => compAttrs.style as StyleValue)
|
const dropdownWidth = ref('')
|
||||||
|
const activated = ref(false)
|
||||||
|
const suggestionDisabled = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const listboxId = computed(() => ns.b(String(generateId())))
|
||||||
|
const styles = computed(() => rawAttrs.style as StyleValue)
|
||||||
|
|
||||||
const suggestionVisible = computed(() => {
|
const suggestionVisible = computed(() => {
|
||||||
const isValidData = isArray(suggestions.value) && suggestions.value.length > 0
|
const isValidData = suggestions.value.length > 0
|
||||||
return (isValidData || loading.value) && activated.value
|
return (isValidData || loading.value) && activated.value
|
||||||
})
|
})
|
||||||
const suggestionLoading = computed(() => {
|
|
||||||
return !props.hideLoading && loading.value
|
|
||||||
})
|
|
||||||
|
|
||||||
const onSuggestionShow = () => {
|
const suggestionLoading = computed(() => !props.hideLoading && loading.value)
|
||||||
nextTick(() => {
|
|
||||||
if (suggestionVisible.value) {
|
const onSuggestionShow = async () => {
|
||||||
dropdownWidth.value = `${inputRef.value!.$el.offsetWidth}px`
|
await nextTick()
|
||||||
}
|
if (suggestionVisible.value) {
|
||||||
})
|
dropdownWidth.value = `${inputRef.value!.$el.offsetWidth}px`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getData = async (queryString: string) => {
|
const getData = async (queryString: string) => {
|
||||||
if (suggestionDisabled.value) {
|
if (suggestionDisabled.value) return
|
||||||
return
|
|
||||||
}
|
const cb = (suggestionList: AutocompleteData) => {
|
||||||
loading.value = true
|
|
||||||
const cb = (suggestionsArg: any[]) => {
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
if (suggestionDisabled.value) {
|
if (suggestionDisabled.value) return
|
||||||
return
|
|
||||||
}
|
if (isArray(suggestionList)) {
|
||||||
if (isArray(suggestionsArg)) {
|
suggestions.value = suggestionList
|
||||||
suggestions.value = suggestionsArg
|
|
||||||
highlightedIndex.value = props.highlightFirstItem ? 0 : -1
|
highlightedIndex.value = props.highlightFirstItem ? 0 : -1
|
||||||
} else {
|
} else {
|
||||||
throwError(COMPONENT_NAME, 'autocomplete suggestions must be an array')
|
throwError(COMPONENT_NAME, 'autocomplete suggestions must be an array')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
if (isArray(props.fetchSuggestions)) {
|
if (isArray(props.fetchSuggestions)) {
|
||||||
cb(props.fetchSuggestions)
|
cb(props.fetchSuggestions)
|
||||||
} else {
|
} else {
|
||||||
const result = await props.fetchSuggestions(queryString, cb)
|
const result = await props.fetchSuggestions(queryString, cb)
|
||||||
if (isArray(result)) {
|
if (isArray(result)) cb(result)
|
||||||
cb(result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const debouncedGetData = debounce(getData, props.debounce)
|
const debouncedGetData = debounce(getData, props.debounce)
|
||||||
const handleInput = (value: string) => {
|
|
||||||
const valuePresented = Boolean(value)
|
|
||||||
|
|
||||||
emit('input', value)
|
const handleInput = (value: string) => {
|
||||||
|
const valuePresented = !!value
|
||||||
|
|
||||||
|
emit(INPUT_EVENT, value)
|
||||||
emit(UPDATE_MODEL_EVENT, value)
|
emit(UPDATE_MODEL_EVENT, value)
|
||||||
|
|
||||||
suggestionDisabled.value = false
|
suggestionDisabled.value = false
|
||||||
activated.value ||= isClear && valuePresented
|
activated.value ||= isClear && valuePresented
|
||||||
|
|
||||||
@ -202,9 +206,11 @@ const handleInput = (value: string) => {
|
|||||||
}
|
}
|
||||||
debouncedGetData(value)
|
debouncedGetData(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = (value: string) => {
|
const handleChange = (value: string) => {
|
||||||
emit('change', value)
|
emit(CHANGE_EVENT, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleFocus = (evt: FocusEvent) => {
|
const handleFocus = (evt: FocusEvent) => {
|
||||||
activated.value = true
|
activated.value = true
|
||||||
emit('focus', evt)
|
emit('focus', evt)
|
||||||
@ -212,16 +218,19 @@ const handleFocus = (evt: FocusEvent) => {
|
|||||||
debouncedGetData(String(props.modelValue))
|
debouncedGetData(String(props.modelValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBlur = (evt: FocusEvent) => {
|
const handleBlur = (evt: FocusEvent) => {
|
||||||
emit('blur', evt)
|
emit('blur', evt)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = () => {
|
||||||
activated.value = false
|
activated.value = false
|
||||||
isClear = true
|
isClear = true
|
||||||
emit(UPDATE_MODEL_EVENT, '')
|
emit(UPDATE_MODEL_EVENT, '')
|
||||||
emit('clear')
|
emit('clear')
|
||||||
}
|
}
|
||||||
const handleKeyEnter = () => {
|
|
||||||
|
const handleKeyEnter = async () => {
|
||||||
if (
|
if (
|
||||||
suggestionVisible.value &&
|
suggestionVisible.value &&
|
||||||
highlightedIndex.value >= 0 &&
|
highlightedIndex.value >= 0 &&
|
||||||
@ -230,19 +239,20 @@ const handleKeyEnter = () => {
|
|||||||
handleSelect(suggestions.value[highlightedIndex.value])
|
handleSelect(suggestions.value[highlightedIndex.value])
|
||||||
} else if (props.selectWhenUnmatched) {
|
} else if (props.selectWhenUnmatched) {
|
||||||
emit('select', { value: props.modelValue })
|
emit('select', { value: props.modelValue })
|
||||||
nextTick(() => {
|
await nextTick()
|
||||||
suggestions.value = []
|
suggestions.value = []
|
||||||
highlightedIndex.value = -1
|
highlightedIndex.value = -1
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleKeyEscape = (e) => {
|
|
||||||
|
const handleKeyEscape = (evt: Event) => {
|
||||||
if (suggestionVisible.value) {
|
if (suggestionVisible.value) {
|
||||||
e.preventDefault()
|
evt.preventDefault()
|
||||||
e.stopPropagation()
|
evt.stopPropagation()
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
activated.value = false
|
activated.value = false
|
||||||
}
|
}
|
||||||
@ -251,23 +261,23 @@ const focus = () => {
|
|||||||
inputRef.value?.focus()
|
inputRef.value?.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSelect = (item: any) => {
|
const handleSelect = async (item: any) => {
|
||||||
emit('input', item[props.valueKey])
|
emit(INPUT_EVENT, item[props.valueKey])
|
||||||
emit(UPDATE_MODEL_EVENT, item[props.valueKey])
|
emit(UPDATE_MODEL_EVENT, item[props.valueKey])
|
||||||
emit('select', item)
|
emit('select', item)
|
||||||
nextTick(() => {
|
await nextTick()
|
||||||
suggestions.value = []
|
suggestions.value = []
|
||||||
highlightedIndex.value = -1
|
highlightedIndex.value = -1
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const highlight = (index: number) => {
|
const highlight = (index: number) => {
|
||||||
if (!suggestionVisible.value || loading.value) {
|
if (!suggestionVisible.value || loading.value) return
|
||||||
return
|
|
||||||
}
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
highlightedIndex.value = -1
|
highlightedIndex.value = -1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index >= suggestions.value.length) {
|
if (index >= suggestions.value.length) {
|
||||||
index = suggestions.value.length - 1
|
index = suggestions.value.length - 1
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { buildProps, definePropType, iconPropType } from '@element-plus/utils'
|
import {
|
||||||
|
buildProps,
|
||||||
|
definePropType,
|
||||||
|
iconPropType,
|
||||||
|
isNumber,
|
||||||
|
} from '@element-plus/utils'
|
||||||
import { componentSizes } from '@element-plus/constants'
|
import { componentSizes } from '@element-plus/constants'
|
||||||
import type { ExtractPropTypes } from 'vue'
|
import type { ExtractPropTypes } from 'vue'
|
||||||
import type { ObjectFitProperty } from 'csstype'
|
import type { ObjectFitProperty } from 'csstype'
|
||||||
@ -9,7 +14,7 @@ export const avatarProps = buildProps({
|
|||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
values: componentSizes,
|
values: componentSizes,
|
||||||
default: '',
|
default: '',
|
||||||
validator: (val: unknown): val is number => typeof val === 'number',
|
validator: (val: unknown): val is number => isNumber(val),
|
||||||
},
|
},
|
||||||
shape: {
|
shape: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import type { ExtractPropTypes } from 'vue'
|
import type { ExtractPropTypes } from 'vue'
|
||||||
|
import type Backtop from './backtop.vue'
|
||||||
|
|
||||||
export const backtopProps = {
|
export const backtopProps = {
|
||||||
visibilityHeight: {
|
visibilityHeight: {
|
||||||
@ -24,3 +25,5 @@ export const backtopEmits = {
|
|||||||
click: (evt: MouseEvent) => evt instanceof MouseEvent,
|
click: (evt: MouseEvent) => evt instanceof MouseEvent,
|
||||||
}
|
}
|
||||||
export type BacktopEmits = typeof backtopEmits
|
export type BacktopEmits = typeof backtopEmits
|
||||||
|
|
||||||
|
export type BacktopInstance = InstanceType<typeof Backtop>
|
||||||
|
@ -20,7 +20,6 @@ import { ElIcon } from '@element-plus/components/icon'
|
|||||||
import { easeInOutCubic, throwError } from '@element-plus/utils'
|
import { easeInOutCubic, throwError } from '@element-plus/utils'
|
||||||
import { CaretTop } from '@element-plus/icons-vue'
|
import { CaretTop } from '@element-plus/icons-vue'
|
||||||
import { useNamespace } from '@element-plus/hooks'
|
import { useNamespace } from '@element-plus/hooks'
|
||||||
|
|
||||||
import { backtopEmits, backtopProps } from './backtop'
|
import { backtopEmits, backtopProps } from './backtop'
|
||||||
|
|
||||||
const COMPONENT_NAME = 'ElBacktop'
|
const COMPONENT_NAME = 'ElBacktop'
|
||||||
@ -43,6 +42,8 @@ const backTopStyle = computed(() => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const scrollToTop = () => {
|
const scrollToTop = () => {
|
||||||
|
// TODO: use https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo, with behavior: 'smooth'
|
||||||
|
|
||||||
if (!el.value) return
|
if (!el.value) return
|
||||||
const beginTime = Date.now()
|
const beginTime = Date.now()
|
||||||
const beginValue = el.value.scrollTop
|
const beginValue = el.value.scrollTop
|
||||||
@ -68,6 +69,7 @@ const handleClick = (event: MouseEvent) => {
|
|||||||
|
|
||||||
const handleScrollThrottled = useThrottleFn(handleScroll, 300)
|
const handleScrollThrottled = useThrottleFn(handleScroll, 300)
|
||||||
|
|
||||||
|
useEventListener(container, 'scroll', handleScrollThrottled)
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
container.value = document
|
container.value = document
|
||||||
el.value = document.documentElement
|
el.value = document.documentElement
|
||||||
@ -79,7 +81,5 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
container.value = el.value
|
container.value = el.value
|
||||||
}
|
}
|
||||||
|
|
||||||
useEventListener(container, 'scroll', handleScrollThrottled)
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -18,14 +18,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getCurrentInstance, inject, ref } from 'vue'
|
import { getCurrentInstance, inject, ref, toRefs } from 'vue'
|
||||||
import ElIcon from '@element-plus/components/icon'
|
import ElIcon from '@element-plus/components/icon'
|
||||||
import { breadcrumbKey } from '@element-plus/tokens'
|
import { breadcrumbKey } from '@element-plus/tokens'
|
||||||
import { useNamespace } from '@element-plus/hooks'
|
import { useNamespace } from '@element-plus/hooks'
|
||||||
import { breadcrumbItemProps } from './breadcrumb-item'
|
import { breadcrumbItemProps } from './breadcrumb-item'
|
||||||
|
|
||||||
import type { Router } from 'vue-router'
|
import type { Router } from 'vue-router'
|
||||||
import type { BreadcrumbProps } from './breadcrumb'
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'ElBreadcrumbItem',
|
name: 'ElBreadcrumbItem',
|
||||||
@ -34,12 +33,11 @@ defineOptions({
|
|||||||
const props = defineProps(breadcrumbItemProps)
|
const props = defineProps(breadcrumbItemProps)
|
||||||
|
|
||||||
const instance = getCurrentInstance()!
|
const instance = getCurrentInstance()!
|
||||||
const router = instance.appContext.config.globalProperties.$router as Router
|
const breadcrumbContext = inject(breadcrumbKey, undefined)!
|
||||||
const breadcrumbInjection = inject(breadcrumbKey, {} as BreadcrumbProps)!
|
|
||||||
|
|
||||||
const ns = useNamespace('breadcrumb')
|
const ns = useNamespace('breadcrumb')
|
||||||
|
|
||||||
const { separator, separatorIcon } = breadcrumbInjection
|
const { separator, separatorIcon } = toRefs(breadcrumbContext)
|
||||||
|
const router = instance.appContext.config.globalProperties.$router as Router
|
||||||
|
|
||||||
const link = ref<HTMLSpanElement>()
|
const link = ref<HTMLSpanElement>()
|
||||||
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import { buildProps, definePropType } from '@element-plus/utils'
|
import {
|
||||||
import { UPDATE_MODEL_EVENT } from '@element-plus/constants'
|
buildProps,
|
||||||
|
definePropType,
|
||||||
|
isArray,
|
||||||
|
isDate,
|
||||||
|
} from '@element-plus/utils'
|
||||||
|
import { INPUT_EVENT, UPDATE_MODEL_EVENT } from '@element-plus/constants'
|
||||||
import type { ExtractPropTypes } from 'vue'
|
import type { ExtractPropTypes } from 'vue'
|
||||||
import type Calendar from './calendar.vue'
|
import type Calendar from './calendar.vue'
|
||||||
|
|
||||||
@ -10,23 +15,23 @@ export type CalendarDateType =
|
|||||||
| 'next-year'
|
| 'next-year'
|
||||||
| 'today'
|
| 'today'
|
||||||
|
|
||||||
|
const isValidRange = (range: unknown): range is [Date, Date] =>
|
||||||
|
isArray(range) && range.length === 2 && range.every((item) => isDate(item))
|
||||||
|
|
||||||
export const calendarProps = buildProps({
|
export const calendarProps = buildProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Date,
|
type: Date,
|
||||||
},
|
},
|
||||||
range: {
|
range: {
|
||||||
type: definePropType<[Date, Date]>(Array),
|
type: definePropType<[Date, Date]>(Array),
|
||||||
validator: (range: unknown): range is [Date, Date] =>
|
validator: isValidRange,
|
||||||
Array.isArray(range) &&
|
|
||||||
range.length === 2 &&
|
|
||||||
range.every((item) => item instanceof Date),
|
|
||||||
},
|
},
|
||||||
} as const)
|
} as const)
|
||||||
export type CalendarProps = ExtractPropTypes<typeof calendarProps>
|
export type CalendarProps = ExtractPropTypes<typeof calendarProps>
|
||||||
|
|
||||||
export const calendarEmits = {
|
export const calendarEmits = {
|
||||||
[UPDATE_MODEL_EVENT]: (value: Date) => value instanceof Date,
|
[UPDATE_MODEL_EVENT]: (value: Date) => isDate(value),
|
||||||
input: (value: Date) => value instanceof Date,
|
[INPUT_EVENT]: (value: Date) => isDate(value),
|
||||||
}
|
}
|
||||||
export type CalendarEmits = typeof calendarEmits
|
export type CalendarEmits = typeof calendarEmits
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ import dayjs from 'dayjs'
|
|||||||
import { ElButton, ElButtonGroup } from '@element-plus/components/button'
|
import { ElButton, ElButtonGroup } from '@element-plus/components/button'
|
||||||
import { useLocale, useNamespace } from '@element-plus/hooks'
|
import { useLocale, useNamespace } from '@element-plus/hooks'
|
||||||
import { debugWarn } from '@element-plus/utils'
|
import { debugWarn } from '@element-plus/utils'
|
||||||
|
import { INPUT_EVENT, UPDATE_MODEL_EVENT } from '@element-plus/constants'
|
||||||
import DateTable from './date-table.vue'
|
import DateTable from './date-table.vue'
|
||||||
import { calendarEmits, calendarProps } from './calendar'
|
import { calendarEmits, calendarProps } from './calendar'
|
||||||
|
|
||||||
@ -66,32 +67,11 @@ const props = defineProps(calendarProps)
|
|||||||
const emit = defineEmits(calendarEmits)
|
const emit = defineEmits(calendarEmits)
|
||||||
|
|
||||||
const ns = useNamespace('calendar')
|
const ns = useNamespace('calendar')
|
||||||
|
|
||||||
const { t, lang } = useLocale()
|
const { t, lang } = useLocale()
|
||||||
|
|
||||||
const selectedDay = ref<Dayjs>()
|
const selectedDay = ref<Dayjs>()
|
||||||
const now = dayjs().locale(lang.value)
|
const now = dayjs().locale(lang.value)
|
||||||
|
|
||||||
const prevMonthDayjs = computed(() => {
|
|
||||||
return date.value.subtract(1, 'month').date(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
const nextMonthDayjs = computed(() => {
|
|
||||||
return date.value.add(1, 'month').date(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
const prevYearDayjs = computed(() => {
|
|
||||||
return date.value.subtract(1, 'year').date(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
const nextYearDayjs = computed(() => {
|
|
||||||
return date.value.add(1, 'year').date(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
const i18nDate = computed(() => {
|
|
||||||
const pickedMonth = `el.datepicker.month${date.value.format('M')}`
|
|
||||||
return `${date.value.year()} ${t('el.datepicker.year')} ${t(pickedMonth)}`
|
|
||||||
})
|
|
||||||
|
|
||||||
const realSelectedDay = computed<Dayjs | undefined>({
|
const realSelectedDay = computed<Dayjs | undefined>({
|
||||||
get() {
|
get() {
|
||||||
if (!props.modelValue) return selectedDay.value
|
if (!props.modelValue) return selectedDay.value
|
||||||
@ -102,11 +82,36 @@ const realSelectedDay = computed<Dayjs | undefined>({
|
|||||||
selectedDay.value = val
|
selectedDay.value = val
|
||||||
const result = val.toDate()
|
const result = val.toDate()
|
||||||
|
|
||||||
emit('input', result)
|
emit(INPUT_EVENT, result)
|
||||||
emit('update:modelValue', result)
|
emit(UPDATE_MODEL_EVENT, result)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// if range is valid, we get a two-digit array
|
||||||
|
const validatedRange = computed(() => {
|
||||||
|
if (!props.range) return []
|
||||||
|
const rangeArrDayjs = props.range.map((_) => dayjs(_).locale(lang.value))
|
||||||
|
const [startDayjs, endDayjs] = rangeArrDayjs
|
||||||
|
if (startDayjs.isAfter(endDayjs)) {
|
||||||
|
debugWarn(COMPONENT_NAME, 'end time should be greater than start time')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (startDayjs.isSame(endDayjs, 'month')) {
|
||||||
|
// same month
|
||||||
|
return calculateValidatedDateRange(startDayjs, endDayjs)
|
||||||
|
} else {
|
||||||
|
// two months
|
||||||
|
if (startDayjs.add(1, 'month').month() !== endDayjs.month()) {
|
||||||
|
debugWarn(
|
||||||
|
COMPONENT_NAME,
|
||||||
|
'start time and end time interval must not exceed two months'
|
||||||
|
)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return calculateValidatedDateRange(startDayjs, endDayjs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const date: ComputedRef<Dayjs> = computed(() => {
|
const date: ComputedRef<Dayjs> = computed(() => {
|
||||||
if (!props.modelValue) {
|
if (!props.modelValue) {
|
||||||
if (realSelectedDay.value) {
|
if (realSelectedDay.value) {
|
||||||
@ -119,6 +124,15 @@ const date: ComputedRef<Dayjs> = computed(() => {
|
|||||||
return dayjs(props.modelValue).locale(lang.value)
|
return dayjs(props.modelValue).locale(lang.value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const prevMonthDayjs = computed(() => date.value.subtract(1, 'month').date(1))
|
||||||
|
const nextMonthDayjs = computed(() => date.value.add(1, 'month').date(1))
|
||||||
|
const prevYearDayjs = computed(() => date.value.subtract(1, 'year').date(1))
|
||||||
|
const nextYearDayjs = computed(() => date.value.add(1, 'year').date(1))
|
||||||
|
|
||||||
|
const i18nDate = computed(() => {
|
||||||
|
const pickedMonth = `el.datepicker.month${date.value.format('M')}`
|
||||||
|
return `${date.value.year()} ${t('el.datepicker.year')} ${t(pickedMonth)}`
|
||||||
|
})
|
||||||
|
|
||||||
// https://github.com/element-plus/element-plus/issues/3155
|
// https://github.com/element-plus/element-plus/issues/3155
|
||||||
// Calculate the validate date range according to the start and end dates
|
// Calculate the validate date range according to the start and end dates
|
||||||
@ -194,31 +208,6 @@ const calculateValidatedDateRange = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if range is valid, we get a two-digit array
|
|
||||||
const validatedRange = computed(() => {
|
|
||||||
if (!props.range) return []
|
|
||||||
const rangeArrDayjs = props.range.map((_) => dayjs(_).locale(lang.value))
|
|
||||||
const [startDayjs, endDayjs] = rangeArrDayjs
|
|
||||||
if (startDayjs.isAfter(endDayjs)) {
|
|
||||||
debugWarn(COMPONENT_NAME, 'end time should be greater than start time')
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
if (startDayjs.isSame(endDayjs, 'month')) {
|
|
||||||
// same month
|
|
||||||
return calculateValidatedDateRange(startDayjs, endDayjs)
|
|
||||||
} else {
|
|
||||||
// two months
|
|
||||||
if (startDayjs.add(1, 'month').month() !== endDayjs.month()) {
|
|
||||||
debugWarn(
|
|
||||||
COMPONENT_NAME,
|
|
||||||
'start time and end time interval must not exceed two months'
|
|
||||||
)
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
return calculateValidatedDateRange(startDayjs, endDayjs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const pickDay = (day: Dayjs) => {
|
const pickDay = (day: Dayjs) => {
|
||||||
realSelectedDay.value = day
|
realSelectedDay.value = day
|
||||||
}
|
}
|
||||||
|
@ -48,11 +48,10 @@ import {
|
|||||||
getPrevMonthLastDays,
|
getPrevMonthLastDays,
|
||||||
toNestedArr,
|
toNestedArr,
|
||||||
} from './date-table'
|
} from './date-table'
|
||||||
|
|
||||||
import type { CalendarDateCell, CalendarDateCellType } from './date-table'
|
import type { CalendarDateCell, CalendarDateCellType } from './date-table'
|
||||||
import type { Dayjs } from 'dayjs'
|
import type { Dayjs } from 'dayjs'
|
||||||
|
|
||||||
dayjs.extend(localeData)
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'DateTable',
|
name: 'DateTable',
|
||||||
})
|
})
|
||||||
@ -60,6 +59,8 @@ defineOptions({
|
|||||||
const props = defineProps(dateTableProps)
|
const props = defineProps(dateTableProps)
|
||||||
const emit = defineEmits(dateTableEmits)
|
const emit = defineEmits(dateTableEmits)
|
||||||
|
|
||||||
|
dayjs.extend(localeData)
|
||||||
|
|
||||||
const { t, lang } = useLocale()
|
const { t, lang } = useLocale()
|
||||||
const nsTable = useNamespace('calendar-table')
|
const nsTable = useNamespace('calendar-table')
|
||||||
const nsDay = useNamespace('calendar-day')
|
const nsDay = useNamespace('calendar-day')
|
||||||
|
@ -13,6 +13,7 @@ export const cardProps = buildProps({
|
|||||||
},
|
},
|
||||||
shadow: {
|
shadow: {
|
||||||
type: String,
|
type: String,
|
||||||
|
values: ['always', 'hover', 'never'],
|
||||||
default: 'always',
|
default: 'always',
|
||||||
},
|
},
|
||||||
} as const)
|
} as const)
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { buildProps, isBoolean } from '@element-plus/utils'
|
import { buildProps, isBoolean } from '@element-plus/utils'
|
||||||
|
import { CHANGE_EVENT } from '@element-plus/constants'
|
||||||
|
|
||||||
import type CheckTag from './check-tag.vue'
|
import type CheckTag from './check-tag.vue'
|
||||||
import type { ExtractPropTypes } from 'vue'
|
import type { ExtractPropTypes } from 'vue'
|
||||||
|
|
||||||
@ -12,7 +14,7 @@ export type CheckTagProps = ExtractPropTypes<typeof checkTagProps>
|
|||||||
|
|
||||||
export const checkTagEmits = {
|
export const checkTagEmits = {
|
||||||
'update:checked': (value: boolean) => isBoolean(value),
|
'update:checked': (value: boolean) => isBoolean(value),
|
||||||
change: (value: boolean) => isBoolean(value),
|
[CHANGE_EVENT]: (value: boolean) => isBoolean(value),
|
||||||
}
|
}
|
||||||
export type CheckTagEmits = typeof checkTagEmits
|
export type CheckTagEmits = typeof checkTagEmits
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { CHANGE_EVENT } from '@element-plus/constants'
|
||||||
import { useNamespace } from '@element-plus/hooks'
|
import { useNamespace } from '@element-plus/hooks'
|
||||||
import { checkTagEmits, checkTagProps } from './check-tag'
|
import { checkTagEmits, checkTagProps } from './check-tag'
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ const ns = useNamespace('check-tag')
|
|||||||
|
|
||||||
const handleChange = () => {
|
const handleChange = () => {
|
||||||
const checked = !props.checked
|
const checked = !props.checked
|
||||||
emit('change', checked)
|
emit(CHANGE_EVENT, checked)
|
||||||
emit('update:checked', checked)
|
emit('update:checked', checked)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user