This commit is contained in:
oneao committed 2025-09-25 22:19:00 +08:00
1 parent 3400068aa0
commit a2c6812ed0
6 files changed
+369 -246

No files matched your search

@@ -1,241 +0,0 @@
<script setup lang="ts">
import { useFormPopout, useIndeterminate } from 'sard-uniapp'
import { computed, ref, watch } from 'vue'
const props = defineProps({
type: {
type: String as PropType<'radio' | 'multiple'>,
default: 'radio',
},
label: {
value: String,
default: '',
},
value: {
type: null as unknown as PropType<any | any[]>,
default: undefined,
},
labelField: {
type: String,
default: 'label',
},
valueField: {
type: String,
default: 'value',
},
viewname: {
type: String,
},
})
const _visible = ref(false)
const searchValue = ref('')
// 内部状态
const _value = ref(
props.type === 'multiple' ? (Array.isArray(props.value) ? [...props.value] : []) : props.value,
)
const _options = ref([
{ label: '选项 1', value: 'option1', disabled: false },
{ label: '选项 2', value: 'option2', disabled: false },
{ label: '选项 3', value: 'option3', disabled: false },
{ label: '选项 4', value: 'option4', disabled: false },
{ label: '选项 5', value: 'option5', disabled: false },
{ label: '选项 1', value: 'option1', disabled: false },
{ label: '选项 2', value: 'option2', disabled: false },
{ label: '选项 3', value: 'option3', disabled: false },
{ label: '选项 4', value: 'option4', disabled: false },
{ label: '选项 5', value: 'option5', disabled: false },
{ label: '选项 1', value: 'option1', disabled: false },
{ label: '选项 2', value: 'option2', disabled: false },
{ label: '选项 3', value: 'option3', disabled: false },
{ label: '选项 4', value: 'option4', disabled: false },
{ label: '选项 5', value: 'option5', disabled: false },
])
const { allChecked, isIndeterminate, onAllChange } = useIndeterminate(
_value,
_options,
)
function select(option, toggle) {
if (!option.disabled) {
toggle(option.value)
}
}
// 显示数量
const count = computed(() => {
if (props.type === 'multiple') {
return `${(_value.value).length || 0} / ${_options.value.length}`
}
else {
return _value.value != null ? `1 / ${_options.value.length}` : `0 / ${_options.value.length}`
}
})
function handleAfterEnter() {
console.log('enter')
}
function showPopout() {
_visible.value = true
}
function onOverlayClick() {
_visible.value = false
}
</script>
<template>
<sar-popout-input
multiline
@click="showPopout"
>
<sar-popout
v-model:visible="_visible"
v-bind="$attrs"
title="请选择"
:show-footer="false"
@after-enter="handleAfterEnter"
@overlay-click="onOverlayClick"
>
<template v-if="props.type === 'multiple'" #title-prepend>
<view class="sar-checkbox-popout__check-all">
<sar-checkbox
v-model:checked="allChecked"
:indeterminate="isIndeterminate"
@change="onAllChange"
>
{{ count }}
</sar-checkbox>
</view>
</template>
<template #visible="{ already }">
<view v-if="already">
<sar-list-item>
<sar-input
v-model="searchValue"
placeholder="请输入搜索的值"
clearable
>
<template #prepend>
<sar-icon family="sari" name="search" />
</template>
</sar-input>
</sar-list-item>
<view class="sar-checkbox-popout__container">
<scroll-view
class="sar-checkbox-popout__scroll_searchable sar-checkbox-popout__scroll"
scroll-y
trap-scroll
:upper-threshold="0"
:lower-threshold="0"
:throttle="false"
>
<sar-checkbox-group
v-if="props.type === 'multiple'"
v-model="_value"
:validate-event="false"
>
<template #custom="{ toggle }">
<sar-list inlaid>
<sar-list-item
v-for="option in _options"
:key="option.value"
:title="option.label"
@click="select(option, toggle)"
>
<template #icon>
<sar-checkbox
readonly
:value="option.value"
:validate-event="false"
/>
</template>
</sar-list-item>
</sar-list>
</template>
</sar-checkbox-group>
<sar-radio-group
v-else
v-model="_value"
:validate-event="false"
>
<template #custom="{ toggle }">
<sar-list inlaid>
<sar-list-item
v-for="option in _options"
:key="option.value"
:title="option.label"
:hover="!option.disabled"
@click="select(option, toggle)"
>
<template #icon>
<sar-radio
readonly
:disabled="option.disabled"
:value="option.value"
/>
</template>
</sar-list-item>
</sar-list>
</template>
</sar-radio-group>
</scroll-view>
</view>
</view>
<view class="sar-popout__footer">
<sar-pagination
:total="43"
:page-size="10"
type="simple"
style="width: 100%;"
/>
</view>
</template>
</sar-popout>
</sar-popout-input>
</template>
<style lang="scss" scoped>
.sar-checkbox-popout__check-all {
position: relative;
box-sizing: border-box;
flex-direction: column;
position: absolute;
left: 1rem;
z-index: 2;
display: flex;
flex-direction: row;
align-items: center;
color: var(--sar-tertiary-color);
font-size: var(--sar-text-sm);
}
.sar-checkbox-popout__container {
position: relative;
}
.sar-checkbox-popout__scroll_searchable {
height: var(--sar-checkbox-popout-max-height);
}
.sar-checkbox-popout__scroll {
max-height: var(--sar-checkbox-popout-max-height);
}
.sar-popout__footer {
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
flex-direction: row;
gap: var(--sar-popout-button-gap);
align-items: center;
padding: var(--sar-popout-footer-gap-y) var(--sar-popout-footer-gap-x);
}
</style>
@@ -0,0 +1,39 @@
<script setup lang="ts">
import PickerSelect from '../picker-select/index.vue'
const props = defineProps({
type: {
type: String as PropType<'radio' | 'multiple'>,
default: 'radio',
},
label: {
value: String,
default: '',
},
value: {
type: null as unknown as PropType<any | any[]>,
default: undefined,
},
labelField: {
type: String,
default: 'label',
},
valueField: {
type: String,
default: 'value',
},
viewname: {
type: String,
},
})
const _visible = ref(false)
</script>
<template>
<sar-popout-input
@click="_visible = true"
>
<PickerSelect v-model:visible="_visible" />
</sar-popout-input>
</template>
@@ -0,0 +1,304 @@
<script setup lang="ts">
import { useIndeterminate } from 'sard-uniapp'
import { computed, ref } from 'vue'
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
type: {
type: String as PropType<'radio' | 'multiple'>,
default: 'radio',
},
label: {
value: String,
default: '',
},
value: {
type: null as unknown as PropType<any | any[]>,
default: undefined,
},
labelField: {
type: String,
default: 'label',
},
valueField: {
type: String,
default: 'value',
},
viewname: {
type: String,
},
})
const emit = defineEmits(['update:visible', 'update:value'])
const _visible = computed({
get: () => props.visible,
set: (val: boolean) => {
emit('update:visible', val)
},
})
const _loading = ref(false)
const searchValue = ref('')
const _pagation = ref({
current: 1,
pageSize: 10,
total: 20,
})
const isPrevDisabled = computed(() => _pagation.value.current <= 1)
const isNextDisabled = computed(() =>
_pagation.value.current >= Math.ceil(_pagation.value.total / _pagation.value.pageSize),
)
// 内部 value,双向绑定 props.value
const _value = computed({
get: () => {
// 如果是多选,保证返回数组
return props.type === 'multiple'
? (Array.isArray(props.value) ? [...props.value] : [])
: props.value
},
set: (val) => {
emit('update:value', val)
},
})
const _options = ref<any[]>([])
function setOptions(val: any[]) {
_options.value = val.map(item => ({
label: item[props.labelField] ?? '',
value: item[props.valueField] ?? undefined,
disabled: !!item.disabled,
...item,
}))
}
const { allChecked, isIndeterminate, onAllChange } = useIndeterminate(
_value,
_options,
)
function select(option, toggle) {
if (!option.disabled) {
toggle(option.value)
if (props.type === 'radio') {
_visible.value = false
}
}
}
// 显示数量
const count = computed(() => {
if (props.type === 'multiple') {
return `${(_value.value).length || 0} / ${_options.value.length}`
}
else {
return _value.value != null ? `1 / ${_options.value.length}` : `0 / ${_options.value.length}`
}
})
function handleAfterEnter() {
// _loading.value = true
}
function handlePagationChange(page: number) {
_pagation.value.current = page
}
</script>
<template>
<sar-popout
v-model:visible="_visible"
title="请选择"
:show-footer="false"
@after-enter="handleAfterEnter"
>
<template v-if="props.type === 'multiple'" #title-prepend>
<view class="sar-checkbox-popout__check-all">
<sar-checkbox
v-model:checked="allChecked"
:indeterminate="isIndeterminate"
@change="onAllChange"
>
{{ count }}
</sar-checkbox>
</view>
</template>
<template #visible="{ already }">
<view v-if="already">
<sar-list-item>
<sar-input
v-model="searchValue"
placeholder="请输入搜索的值"
clearable
>
<template #prepend>
<sar-icon family="sari" name="search" />
</template>
</sar-input>
</sar-list-item>
<view class="sar-checkbox-popout__container">
<scroll-view
class="sar-checkbox-popout__scroll_searchable sar-checkbox-popout__scroll"
scroll-y
trap-scroll
:upper-threshold="0"
:lower-threshold="0"
:throttle="false"
>
<sar-empty
v-if="_options.length === 0"
:root-style="{ height: '100%' }"
/>
<sar-checkbox-group
v-else-if="props.type === 'multiple'"
v-model="_value"
:validate-event="false"
>
<template #custom="{ toggle }">
<sar-list inlaid>
<sar-list-item
v-for="option in _options"
:key="option.value"
:title="option.label"
@click="select(option, toggle)"
>
<template #icon>
<sar-checkbox
readonly
:value="option.value"
:validate-event="false"
/>
</template>
</sar-list-item>
</sar-list>
</template>
</sar-checkbox-group>
<sar-radio-group
v-else
v-model="_value"
:validate-event="false"
>
<template #custom="{ toggle }">
<sar-list inlaid>
<sar-list-item
v-for="option in _options"
:key="option.value"
:title="option.label"
:hover="!option.disabled"
@click="select(option, toggle)"
>
<template #icon>
<sar-radio
readonly
:disabled="option.disabled"
:value="option.value"
/>
</template>
</sar-list-item>
</sar-list>
</template>
</sar-radio-group>
</scroll-view>
</view>
</view>
<view class="sar-popout__footer">
<sar-pagination
:current="_pagation.current"
:total="_pagation.total"
:page-size="_pagation.pageSize"
type="simple"
root-class="picker-select__pagination-root"
@change="handlePagationChange"
>
<template #prev>
<sar-button type="default" :disabled="isPrevDisabled" size="mini">
上一页
</sar-button>
</template>
<template #next>
<sar-button type="default" :disabled="isNextDisabled" size="mini">
下一页
</sar-button>
</template>
</sar-pagination>
<sar-button
v-if="props.type === 'multiple'"
:root-style="{ width: '100%' }"
>
确认
</sar-button>
</view>
<view v-if="_loading" class="sar-popout__mask">
<sar-loading
size="48rpx"
text-size="36rpx"
color="var(--sar-primary)"
text-color="var(--sar-primary)"
text="加载中"
/>
</view>
</template>
</sar-popout>
</template>
<style lang="scss" scoped>
.sar-popout__mask {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.7);
display: flex;
align-items: center;
justify-content: center;
border-top-left-radius: var(--sar-popout-border-top-radius);
border-top-right-radius: var(--sar-popout-border-top-radius);
z-index: 999;
}
.sar-checkbox-popout__check-all {
position: relative;
box-sizing: border-box;
flex-direction: column;
position: absolute;
left: 1rem;
z-index: 2;
display: flex;
flex-direction: row;
align-items: center;
color: var(--sar-tertiary-color);
font-size: var(--sar-text-sm);
}
.sar-checkbox-popout__container {
position: relative;
}
.sar-checkbox-popout__scroll_searchable {
height: var(--sar-checkbox-popout-max-height);
}
.sar-checkbox-popout__scroll {
max-height: var(--sar-checkbox-popout-max-height);
}
.sar-popout__footer {
width: 100%;
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 0.5rem;
align-items: center;
padding: var(--sar-popout-footer-gap-y) var(--sar-popout-footer-gap-x);
}
</style>
@@ -1,15 +1,27 @@
<script setup lang="ts">
import type { PopupProps } from 'sard-uniapp'
import { ref } from 'vue'
const visible = ref(false)
function show() {
visible.value = !visible.value
}
</script>
<template>
<custom-select
title="123"
<picker-select
v-model:visible="visible"
/>
<sar-list card>
<sar-list-item hover arrow title="底部划出" @click="show('slide-bottom')" />
<sar-list-item hover arrow title="底部划出" @click="show()" />
</sar-list>
<sar-form label-width="240rpx">
<sar-form-item label="Activity name">
<picker-select-input />
</sar-form-item>
<sar-form-item label="Activity name">
<picker-select-input />
</sar-form-item>
</sar-form>
</template>
<style lang="scss">
@@ -1 +1,9 @@
@import 'sard-uniapp/index.scss';
.picker-select__pagination-root {
width: 100%;
.sar-pagination__item,
.sar-pagination__ratio {
height: auto !important;
}
}
+2 -1
View File
@@ -7,6 +7,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
CustomSelect: typeof import('./../components/custom-select/index.vue')['default']
PickerSelect: typeof import('./../components/picker-select/index.vue')['default']
PickerSelectInput: typeof import('./../components/picker-select-input/index.vue')['default']
}
}