| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- <template>
- <view>
- <u-popup closeOnClickOverlay @close="handleClose" v-model="show" :show="show" :round="10" mode="bottom"
- border-radius="20">
- <view class="currency-mask">
- <view class="search" v-if="showSearch && props.options.length > 10">
- <up-input class="form-input" v-model="inputValueDoc" type="text" :placeholder="t('common.input')"
- :clearable="true" :border="'surround'">
- <template #prefix>
- <up-icon name="search" size="23"></up-icon>
- </template>
- </up-input>
- </view>
- <scroll-view class="currency-select" :style="actionSheetStyle1" scroll-y="true">
- <template v-if="filteredOptions.length > 0">
- <view v-for="(item, index) in filteredOptions" class="currency-item"
- :class="{ selected: item.text === inputValue }" @click="select(item)"
- :key="item.value + index" :ref="item.text === inputValue ? 'selectedItem' : null">
- {{ item.text }}
- </view>
- </template>
- </scroll-view>
- <view currency-select>
- <view v-if="filteredOptions.length == 0" class="currency-item">
- <view class="empty">
- <text>{{ t('Documentary.tradingCenter.item131') }}</text>
- </view>
- </view>
- </view>
- </view>
- </u-popup>
- </view>
- </template>
- <script setup lang="ts">
- import { ref, computed, watch, nextTick } from 'vue'
- import { useI18n } from 'vue-i18n'
- const { t } = useI18n()
- const props = defineProps<{
- modelValue: boolean
- showSearch: boolean
- inputValue: string
- options: Array<{ text: string; value: string }>
- }>()
- const emit = defineEmits(['update:modelValue', 'select'])
- const inputValueDoc = ref('')
- const show = ref(props.modelValue)
- const selectedItem = ref()
- const filteredOptions = computed(() => {
- if (!inputValueDoc.value) return props.options
- const keyword = inputValueDoc.value.toLowerCase()
- return props.options.filter((item) => item.text.toLowerCase().includes(keyword))
- })
- watch(
- () => props.modelValue,
- (val) => (show.value = val),
- )
- watch(show, async (val) => {
- emit('update:modelValue', val)
- inputValueDoc.value = ''
- if (val) {
- await nextTick()
- setTimeout(() => {
- const el = Array.isArray(selectedItem.value) ? selectedItem.value[0] : selectedItem.value
- }, 300)
- }
- })
- function handleClose() {
- emit('update:modelValue', false)
- }
- const ITEM_HEIGHT = 60
- const MAX_VISIBLE_COUNT = 10
- const SAFE_OFFSET = 98
- const MAX_RATIO = 0.8
- const actionSheetStyle1 = computed(() => {
- const options = props.options || []
- const count = options.length
- // ✅ 跨端安全获取高度
- const systemInfo = uni.getSystemInfoSync()
- const windowHeight = systemInfo.windowHeight || systemInfo.screenHeight
- // 超过阈值,固定最大高度
- if (count > MAX_VISIBLE_COUNT) {
- return {
- maxHeight: `${windowHeight * MAX_RATIO - SAFE_OFFSET}px`
- }
- }
- // 根据内容高度计算
- const contentHeight = count * ITEM_HEIGHT
- const maxHeight = Math.min(
- contentHeight,
- windowHeight * MAX_RATIO
- )
- return {
- maxHeight: `${maxHeight}px`
- }
- })
- function select(item: { text: string; value: string }) {
- emit('select', item)
- show.value = false
- }
- </script>
- <style scoped lang="scss">
- @import "@/uni.scss";
- .currency-mask {
- padding: px2rpx(24) px2rpx(16) 0 px2rpx(16);
- }
- .search {
- color: #8e8a8a;
- font-family: Roboto;
- font-size: px2rpx(16);
- font-style: normal;
- font-weight: 400;
- line-height: px2rpx(24);
- letter-spacing: px2rpx(0.08);
- display: flex;
- justify-content: space-between;
- align-items: center;
- display: flex;
- align-items: center;
- gap: px2rpx(82);
- border-radius: 30px;
- margin-bottom: px2rpx(10);
- }
- .form-input {
- padding: 0;
- }
- .form-input :deep(.up-input) {
- border: none !important;
- padding: 0 !important;
- }
- .form-input :deep(.up-input__content__field-wrapper__field) {
- font-size: px2rpx(16) !important;
- }
- .currency-select {
- height: calc(80vh - px2rpx(98));
- overflow-y: auto;
- padding: 0 0 px2rpx(22) 0;
- display: flex;
- flex-direction: column;
- align-items: center;
- z-index: 2;
- }
- .currency-item {
- display: flex;
- padding: px2rpx(12) px2rpx(16);
- align-items: center;
- gap: px2rpx(8);
- align-self: stretch;
- border-radius: 10px;
- color: #0e0f0c;
- font-size: px2rpx(16);
- font-style: normal;
- font-weight: 400;
- line-height: px2rpx(24);
- letter-spacing: -px2rpx(0.08);
- width: 100%;
- box-sizing: border-box;
- }
- .currency-item span {
- color: #454745;
- font-family: Inter;
- font-size: px2rpx(14);
- font-style: normal;
- font-weight: 400;
- line-height: px2rpx(22);
- letter-spacing: px2rpx(0.14);
- }
- .currency-item img {
- display: flex;
- width: px2rpx(48);
- height: px2rpx(48);
- justify-content: center;
- align-items: center;
- aspect-ratio: 1/1;
- border-radius: 100px;
- border: 1px solid rgba(14, 15, 12, 0.12);
- box-shadow: 0px 0px 40px 0px rgba(69, 71, 69, 0.2);
- }
- .selected {
- border-radius: 10px;
- background: rgba(255, 209, 216, 0.85);
- }
- .cancel-btn {
- width: 90vw;
- padding: px2rpx(20) 0;
- text-align: center;
- color: #111;
- border-radius: 20px;
- font-weight: bold;
- font-size: px2rpx(22);
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
- margin: px2rpx(10) 0;
- }
- // uView 样式覆盖
- :deep(.up-action-sheet__container) {
- max-height: 80vh;
- }
- :deep(.up-input) {
- padding: px2rpx(12) px2rpx(16);
- }
- </style>
|