diff --git a/packages/design/aurora/src/select-wrapper/index.ts b/packages/design/aurora/src/select-wrapper/index.ts new file mode 100644 index 0000000000..67b5a05801 --- /dev/null +++ b/packages/design/aurora/src/select-wrapper/index.ts @@ -0,0 +1,109 @@ +import { iconChevronDown } from '@opentiny/vue-icon' + +export default { + // 虚拟滚动的默认options不一致 + baseOpts: { optionHeight: 34, limit: 20 }, + icons: { + dropdownIcon: iconChevronDown() + }, + state: { + sizeMap: { + default: 30, + mini: 24, + small: 36, + medium: 42 + }, + spacingHeight: 2, + initialInputHeight: 30, + // 显示清除等图标时,不隐藏下拉箭头时 + autoHideDownIcon: false, + delayBlur: true + }, + props: { + tagType: 'info', + stopPropagation: true + }, + renderless: (props, hooks, { emit }, api) => { + const state = api.state + + return { + // 兼容不同主题输入框尺寸对应标签尺寸不一致 + computedCollapseTagSize: () => { + let size = 'small' + + if (~['small', 'mini'].indexOf(state.selectSize)) { + size = state.selectSize + } else if (~['medium', 'default'].indexOf(state.selectSize)) { + size = 'small' + } + + return size + }, + // 兼容显示清除图标时,是否同时显示下拉图标 + computedShowDropdownIcon: () => { + return !(props.remote && props.filterable && !props.remoteConfig.showIcon) + }, + + // aui 的勾选未处理disabled的选项,故此放这里。 + toggleCheckAll: (filtered) => { + const getEnabledValues = (options) => { + let values = [] + + for (let i = 0; i < options.length; i++) { + if (!options[i].state.disabled && !options[i].state.groupDisabled && options[i].state.visible) { + values.push(options[i].value) + } + } + + return values + } + + let value + const enabledValues = getEnabledValues(state.options) + + if (filtered) { + if (state.filteredSelectCls === 'check' || state.filteredSelectCls === 'halfselect') { + value = Array.from(new Set([...state.modelValue, ...enabledValues])) + } else { + value = state.modelValue.filter((val) => !enabledValues.includes(val)) + } + } else { + if (state.selectCls === 'check') { + value = enabledValues + } else if (state.selectCls === 'halfselect') { + const unchecked = state.options.filter((item) => !item.state.disabled && item.state.selectCls === 'check') + + unchecked.length ? (value = enabledValues) : (value = []) + } else if (state.selectCls === 'checked-sur') { + value = [] + } + } + + const requiredValue = [] + if (props.multiple) { + state.options.forEach((opt) => { + if (opt.required) requiredValue.push(opt.value) + }) + } + + if (Array.isArray(value)) { + value = requiredValue.concat(value.filter((val) => !requiredValue.find((requireVal) => requireVal === val))) + } + + api.setSoftFocus() + + state.isSilentBlur = true + api.updateModelValue(value) + api.directEmitChange(value) + }, + // aurora 禁用和只展示的时候都是tagText,默认主题是 isDisplayOnly 才显示tagText + computedShowTagText: () => { + return state.isDisabled || state.isDisplayOnly + }, + // aurora 禁用已选项无效果,必选不显示关闭图标 + isTagClosable: (item) => { + return !item.required + } + } + } +} diff --git a/packages/design/saas/src/select-wrapper/icon-loading.svg b/packages/design/saas/src/select-wrapper/icon-loading.svg new file mode 100644 index 0000000000..94160f70a1 --- /dev/null +++ b/packages/design/saas/src/select-wrapper/icon-loading.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/design/saas/src/select-wrapper/index.ts b/packages/design/saas/src/select-wrapper/index.ts new file mode 100644 index 0000000000..adae4352ed --- /dev/null +++ b/packages/design/saas/src/select-wrapper/index.ts @@ -0,0 +1,106 @@ +import { iconChevronDown, iconPlus } from '@opentiny/vue-icon' +import loadingIcon from './icon-loading.svg' + +export default { + // 虚拟滚动的默认options不一致 + baseOpts: { optionHeight: 34, limit: 20 }, + icons: { + dropdownIcon: iconChevronDown(), + addIcon: iconPlus(), + loadingIcon + }, + state: { + sizeMap: { + default: 28, + mini: 24, + small: 28, + medium: 32 + }, + spacingHeight: 4, + initialInputHeight: 28, + // 显示清除等图标时,不隐藏下拉箭头时 + autoHideDownIcon: false, + delayBlur: true + }, + props: { + tagType: 'info', + stopPropagation: true + }, + renderless: (props, hooks, { emit }, api) => { + const state = api.state + + return { + computedCollapseTagSize: () => { + let size = 'small' + + if (~['small', 'mini'].indexOf(state.selectSize)) { + size = state.selectSize + } else if (~['medium', 'default'].indexOf(state.selectSize)) { + size = 'small' + } + + return size + }, + // aui 的勾选未处理disabled的选项,故此放这里。 + toggleCheckAll: (filtered) => { + const getEnabledValues = (options) => { + let values = [] + + for (let i = 0; i < options.length; i++) { + if (!options[i].state.disabled && !options[i].state.groupDisabled && options[i].state.visible) { + values.push(options[i].value) + } + } + + return values + } + + let value + const enabledValues = getEnabledValues(state.options) + + if (filtered) { + if (state.filteredSelectCls === 'check' || state.filteredSelectCls === 'halfselect') { + value = Array.from(new Set([...state.modelValue, ...enabledValues])) + } else { + value = state.modelValue.filter((val) => !enabledValues.includes(val)) + } + } else { + if (state.selectCls === 'check') { + value = enabledValues + } else if (state.selectCls === 'halfselect') { + const unchecked = state.options.filter((item) => !item.state.disabled && item.state.selectCls === 'check') + + unchecked.length ? (value = enabledValues) : (value = []) + } else if (state.selectCls === 'checked-sur') { + value = [] + } + } + + const requiredValue = [] + if (props.multiple) { + state.options.forEach((opt) => { + if (opt.required) requiredValue.push(opt.value) + }) + } + + if (Array.isArray(value)) { + value = requiredValue.concat(value.filter((val) => !requiredValue.find((requireVal) => requireVal === val))) + } + + api.setSoftFocus() + + state.isSilentBlur = true + api.updateModelValue(value) + api.directEmitChange(value) + }, + // aurora 禁用和只展示的时候都是tagText,默认主题是 isDisplayOnly 才显示tagText + computedShowTagText: () => { + return state.isDisabled || state.isDisplayOnly + }, + // aurora 禁用已选项无效果,必选不显示关闭图标 + isTagClosable: (item) => { + return !item.required + } + } + } +} diff --git a/packages/renderless/src/base-select/index.ts b/packages/renderless/src/base-select/index.ts index 445bbbe4e7..cc8dc91fdf 100644 --- a/packages/renderless/src/base-select/index.ts +++ b/packages/renderless/src/base-select/index.ts @@ -1249,6 +1249,9 @@ const postOperOfToVisible = ({ props, state, constants, vm }) => { if (props.modelValue && props.initLabel && !state.selectedLabel) { state.selectedLabel = props.initLabel } + } else if (props.modelValue && props.initLabel) { + // 如果 state.selected 不存在,但有 modelValue 和 initLabel,则使用 initLabel + state.selectedLabel = props.initLabel } } diff --git a/packages/renderless/src/select-wrapper/vue.ts b/packages/renderless/src/select-wrapper/vue.ts index 87134f2e4c..8420c214ac 100644 --- a/packages/renderless/src/select-wrapper/vue.ts +++ b/packages/renderless/src/select-wrapper/vue.ts @@ -25,11 +25,11 @@ export const renderless = (props, { reactive, computed, useAttrs }, { constants, : ['tiny-select'] const { class: _omitClass, ...restAttrs } = attrs - return { ...props, ...restAttrs, - class: classArray + class: Array.from(new Set(classArray)), + dataTag: 'tiny-select' } }) diff --git a/packages/vue/src/base-select/src/mobile-first.vue b/packages/vue/src/base-select/src/mobile-first.vue index 336ef84f70..90ec1e6f17 100644 --- a/packages/vue/src/base-select/src/mobile-first.vue +++ b/packages/vue/src/base-select/src/mobile-first.vue @@ -302,6 +302,8 @@ :display-only-content="state.displayOnlyContent" :unselectable="state.readonly ? 'on' : 'off'" :validate-event="false" + :show-empty-value="showEmptyValue" + :input-box-type="inputBoxType" :tabindex="multiple && filterable ? '-1' : tabindex" @focus="handleFocus" @blur="handleBlur" @@ -758,7 +760,10 @@ export default defineComponent({ 'showAllTextTag', 'hoverExpand', 'clickExpand', - 'maxVisibleRows' + 'maxVisibleRows', + 'initLabel', + 'inputBoxType', + 'showEmptyValue' ], setup(props, context) { return setup({ props, context, renderless, api, classes }) diff --git a/packages/vue/src/grid-select/src/mobile-first.vue b/packages/vue/src/grid-select/src/mobile-first.vue index 443d8c26cc..227a42e496 100644 --- a/packages/vue/src/grid-select/src/mobile-first.vue +++ b/packages/vue/src/grid-select/src/mobile-first.vue @@ -37,8 +37,8 @@ ref="gridRef" auto-resize :row-id="valueField" - :select-config="state.selectConfig" - :radio-config="state.radioConfig" + :select-config="buildSelectConfig()" + :radio-config="buildRadioConfig()" :highlight-current-row="true" :columns="gridOp?.columns || []" :data="Array.isArray(state.gridData) ? state.gridData : state.gridData?.data || state.gridData || []" diff --git a/packages/vue/src/input/src/mobile-first.vue b/packages/vue/src/input/src/mobile-first.vue index 2042a69f6f..78ac882ff9 100644 --- a/packages/vue/src/input/src/mobile-first.vue +++ b/packages/vue/src/input/src/mobile-first.vue @@ -94,13 +94,15 @@ ? 'h-6 leading-6 text-xs placeholder:text-xs' : 'h-7 leading-7', slots.prepend || slots.append ? 'align-middle table-cell' : 'inline-block', - slots.prepend && slots.append - ? 'rounded-none' - : slots.prepend - ? 'rounded-tl-none rounded-bl-none rounded-tr rounded-br' - : slots.append - ? 'rounded-tl rounded-bl rounded-tr-none rounded-br-none' - : 'rounded', + inputBoxType === 'underline' + ? 'rounded-none border-t-0 border-l-0 border-r-0 border-b sm:border-b' + : slots.prepend && slots.append + ? 'rounded-none' + : slots.prepend + ? 'rounded-tl-none rounded-bl-none rounded-tr rounded-br' + : slots.append + ? 'rounded-tl rounded-bl rounded-tr-none rounded-br-none' + : 'rounded', readonly ? ' text-ellipsis overflow-hidden whitespace-nowrap' : 'sm:border', (slots.prefix || prefixIcon) && (slots.suffix || suffixIcon || clearable || showPassword) ? 'px-6 sm:px-6' @@ -427,7 +429,8 @@ export default defineComponent({ 'popupMore', 'showTooltip', 'frontClearIcon', - 'hoverExpand' + 'hoverExpand', + 'inputBoxType' ], setup(props, context): any { return setup({ props, context, renderless, api }) diff --git a/packages/vue/src/select-wrapper/src/index.ts b/packages/vue/src/select-wrapper/src/index.ts index 292eeb8457..ecd331351f 100644 --- a/packages/vue/src/select-wrapper/src/index.ts +++ b/packages/vue/src/select-wrapper/src/index.ts @@ -12,7 +12,7 @@ import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common' import { t } from '@opentiny/vue-locale' -import template from 'virtual-template?pc' +import template from 'virtual-template?pc|mobile-first' const $constants = { CLASS: { @@ -326,9 +326,9 @@ export default defineComponent({ type: Boolean, default: false }, - InputBoxType: { + inputBoxType: { type: String, - default: 'input', + default: 'normal', validator: (value: string) => ['input', 'underline'].includes(value) }, tagType: { diff --git a/packages/vue/src/select-wrapper/src/mobile-first.vue b/packages/vue/src/select-wrapper/src/mobile-first.vue new file mode 100644 index 0000000000..e6773c2b75 --- /dev/null +++ b/packages/vue/src/select-wrapper/src/mobile-first.vue @@ -0,0 +1,316 @@ + + + diff --git a/packages/vue/src/select-wrapper/src/pc.vue b/packages/vue/src/select-wrapper/src/pc.vue index babf871bc2..2d3e3fc975 100644 --- a/packages/vue/src/select-wrapper/src/pc.vue +++ b/packages/vue/src/select-wrapper/src/pc.vue @@ -263,7 +263,7 @@ export default defineComponent({ type: Boolean, default: false }, - InputBoxType: { + inputBoxType: { type: String, default: 'input', validator: (value: string) => ['input', 'underline'].includes(value)