This commit is contained in:
oneao committed 2025-09-30 17:26:30 +08:00
1 parent 92fbbf7c6d
commit b03e2c1e69
149 files changed
+925 -14628

No files matched your search

@@ -0,0 +1,55 @@
<script setup lang="ts">
import PickerSelect from '../picker-select/index.vue'
const props = defineProps({
label: { type: String, default: '' },
value: { type: null as unknown as PropType<any>, default: undefined },
labelField: { type: String, required: true },
valueField: { type: String, required: true },
viewname: { type: String, required: true },
field1: { type: String, required: true },
field2: { type: String, required: true },
placeholder: { type: String, default: '请选择' },
})
const emits = defineEmits<{
(e: 'update:label', val: string): void
(e: 'update:value', val: any): void
}>()
const _visible = ref(false)
const _label = ref(props.label)
// 同步 props.label
watch(() => props.label, (val) => {
_label.value = val
})
// 选中时只处理 label
function handleSelectChange(selectValue: any) {
if (!selectValue)
return
_label.value = selectValue[props.labelField]
emits('update:label', _label.value)
}
</script>
<template>
<sar-popout-input
v-model="_label"
:placeholder="props.placeholder"
@click="_visible = true"
>
<PickerSelect
v-model:visible="_visible"
:value="props.value"
:viewname="props.viewname"
:field1="props.field1"
:field2="props.field2"
:label-field="props.labelField"
:value-field="props.valueField"
@update:value="emits('update:value', $event)"
@change="handleSelectChange"
/>
</sar-popout-input>
</template>
@@ -0,0 +1,385 @@
<script setup lang="ts">
import type { PropType } from 'vue'
import { useIndeterminate } from 'sard-uniapp'
import { computed, ref } from 'vue'
import { getAutocompleteDataApi, getAutocompleteDataExApi } from '@/request/api'
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
type: {
type: String as PropType<'radio' | 'multiple'>,
default: 'radio',
},
value: {
type: null as unknown as PropType<any | any[]>,
default: undefined,
},
labelField: {
type: String,
required: true,
},
valueField: {
type: String,
required: true,
},
viewname: {
type: String,
},
searchcondition: {
type: String,
required: false,
},
userid: {
type: String,
required: false,
},
field1: {
type: String,
required: true,
},
field2: {
type: String,
required: true,
},
searchType: {
type: String as PropType<'default' | 'ex' | 'single'>,
default: 'default',
required: false,
},
})
const emit = defineEmits(['update:visible', 'update:value', 'change'])
const _visible = ref(props.visible)
watch(() => props.visible, val => _visible.value = val)
const loading = ref(true)
const searchValue = ref('')
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
})
const isPrevDisabled = computed(() => pagination.value.current <= 1)
const isNextDisabled = computed(() =>
pagination.value.current >= Math.ceil(pagination.value.total / pagination.value.pageSize),
)
// 内部 value,双向绑定 props.value
const _value = computed({
get: () => {
if (props.type === 'multiple') {
return Array.isArray(props.value) ? props.value : []
}
return props.value ?? null
},
set: (val) => {
emit('update:value', val)
},
})
const options = ref<any[]>([])
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 selectedOption = options.value.find(
o => o.value === option.value,
)
_visible.value = false
emit('update:visible', false)
emit('change', selectedOption)
}
}
}
// 显示数量
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() {
getData()
}
function handlePagationChange(page: number) {
pagination.value.current = page
getData()
}
function handleSearch() {
pagination.value.current = 1
pagination.value.total = 0
pagination.value.pageSize = 10
getData()
}
function getData() {
if (props.searchType === 'ex') {
getAutocompleteDataEx()
}
else if (props.searchType === 'single') {
getAutocompleteDataSingle()
}
else {
getAutocompleteData()
}
}
function getAutocompleteData() {
loading.value = true
getAutocompleteDataApi({
q: searchValue.value,
page: pagination.value.current,
limit: pagination.value.pageSize,
viewname: props.viewname,
field1: props.field1,
field2: props.field2,
}).then((res) => {
pagination.value.total = res.data.total
options.value = res.data.rows.map(item => ({
label: item[props.labelField] ?? '',
value: item[props.valueField] ?? undefined,
disabled: !!item.disabled,
...item,
}))
console.log(res.data.rows)
}).finally(() => {
loading.value = false
})
}
function getAutocompleteDataEx() {
loading.value = true
getAutocompleteDataExApi({
q: searchValue.value,
page: pagination.value.current,
limit: pagination.value.pageSize,
viewname: props.viewname,
field1: props.field1,
field2: props.field2,
userid: props.userid,
searchcondition: props.searchcondition,
}).then((res) => {
pagination.value.total = res.data.total
options.value = res.data.rows.map(item => ({
label: item[props.labelField] ?? '',
value: item[props.valueField] ?? undefined,
disabled: !!item.disabled,
...item,
}))
}).finally(() => {
loading.value = false
})
}
function getAutocompleteDataSingle() {
}
function handleCancel() {
_visible.value = false
emit('update:visible', false)
}
</script>
<template>
<sar-popout
:visible="_visible"
:show-footer="false"
title="请选择"
@enter="handleAfterEnter"
@close="handleCancel"
>
<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
@confirm="handleSearch"
>
<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
:key="option.value"
readonly
:disabled="option.disabled"
:value="option.value"
/>
</template>
</sar-list-item>
</sar-list>
</template>
</sar-radio-group>
</scroll-view>
</view>
<view class="sar-popout__footer">
<sar-pagination
:current="pagination.current"
:total="pagination.total"
:page-size="pagination.pageSize"
type="simple"
root-class="picker-select__pagination-root"
@change="handlePagationChange"
>
<template #prev>
<sar-button type="default" :disabled="isPrevDisabled" size="mini" :loading="loading">
上一页
</sar-button>
</template>
<template #next>
<sar-button type="default" :disabled="isNextDisabled" size="mini" :loading="loading">
下一页
</sar-button>
</template>
</sar-pagination>
<sar-button
v-if="props.type === 'multiple'"
:root-style="{ width: '100%' }"
>
确认
</sar-button>
</view>
</view>
</template>
</sar-popout>
</template>
<style lang="scss" scoped>
.sar-checkbox-popout__check-all {
position: relative;
box-sizing: border-box;
flex-direction: column;
position: absolute;
left: 32rpx;
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: 16rpx;
align-items: center;
padding: var(--sar-popout-footer-gap-y) var(--sar-popout-footer-gap-x);
}
</style>