u
This commit is contained in:
1 parent
68f08d921a
commit
3656d05f62
97 files changed
+17495
No files matched your search
@@ -0,0 +1,32 @@
|
||||
<script lang="ts" setup>
|
||||
defineOptions({
|
||||
name: 'OverlayLoading',
|
||||
})
|
||||
defineProps<{
|
||||
visible: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<sar-overlay :visible="visible">
|
||||
<view class="overlay">
|
||||
<sar-loading
|
||||
vertical
|
||||
color="#fff"
|
||||
text-color="#fff"
|
||||
>
|
||||
加载中...
|
||||
</sar-loading>
|
||||
</view>
|
||||
</sar-overlay>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.overlay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
Whitespace-only changes.
@@ -0,0 +1,104 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { dateSearchProps } from './types'
|
||||
|
||||
// 定义 props 和 emits
|
||||
const props = defineProps(dateSearchProps)
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string | number): void
|
||||
}>()
|
||||
|
||||
// 初始化 dateValue,确保它从 props.modelValue 获取初始值
|
||||
const dateValue = ref<string | number>(props.modelValue || 0) // 如果没有 modelValue 默认 ''
|
||||
|
||||
// 监听 props.modelValue 的变化,并更新 dateValue
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue: string | number) => {
|
||||
if (props.type === 'date') {
|
||||
// 如果是字符串类型
|
||||
if (typeof newValue === 'string') {
|
||||
// 尝试将字符串转为时间戳
|
||||
const timestamp = Date.parse(newValue)
|
||||
// 如果是有效的时间戳,则使用它;否则使用 0
|
||||
dateValue.value = Number.isNaN(timestamp) ? 0 : timestamp
|
||||
}
|
||||
else {
|
||||
// 如果是数字类型,直接赋值
|
||||
dateValue.value = newValue
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const showValue = computed(() => {
|
||||
// 如果 dateValue 有值且不为 0
|
||||
if (props.type === 'date') {
|
||||
if (dateValue.value && dateValue.value !== 0) {
|
||||
// 转换时间戳为 YYYY-MM-DD 格式
|
||||
const date = new Date(dateValue.value)
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0') // 补零
|
||||
const day = date.getDate().toString().padStart(2, '0') // 补零
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
else {
|
||||
// 如果 dateValue 为 0 或空,返回 placeholder
|
||||
return props.placeholder || '请输入'
|
||||
}
|
||||
}
|
||||
else {
|
||||
return dateValue.value || props.placeholder
|
||||
}
|
||||
})
|
||||
|
||||
const showValueClass = computed(() => {
|
||||
return dateValue.value ? 'text_black' : 'text_gray'
|
||||
})
|
||||
|
||||
// 监听 dateValue 变化并通知父组件
|
||||
watch(dateValue, (newValue) => {
|
||||
emit('update:modelValue', newValue) // 将变化同步回父组件
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<wd-datetime-picker v-bind="props" v-model="dateValue" use-default-slot>
|
||||
<view class="datetime-picker-container">
|
||||
<view class="datetime-display" :class="showValueClass">
|
||||
{{ showValue }}
|
||||
</view>
|
||||
</view>
|
||||
</wd-datetime-picker>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 包裹容器,确保内容水平居中 */
|
||||
.datetime-picker-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* 输入框样式 */
|
||||
.datetime-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 4px;
|
||||
font-size: 14px;
|
||||
color: #333; /* 默认文本颜色 */
|
||||
text-align: center;
|
||||
background-color: #f5f5f5; /* 背景色 */
|
||||
border-radius: 4px; /* 圆角 */
|
||||
}
|
||||
/* 黑色文本样式 */
|
||||
.text_black {
|
||||
color: black;
|
||||
}
|
||||
/* 灰色文本样式 */
|
||||
.text_gray {
|
||||
color: gray;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,10 @@
|
||||
import { datetimePickerProps } from 'wot-design-uni/components/wd-datetime-picker/types'
|
||||
|
||||
export const dateSearchProps = {
|
||||
...datetimePickerProps,
|
||||
modelValue: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<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 },
|
||||
labelField2: { type: String, required: false },
|
||||
valueField: { type: String, required: true },
|
||||
viewname: { type: String, required: true },
|
||||
field1: { type: String, required: true },
|
||||
field2: { type: String, required: true },
|
||||
placeholder: { type: String, default: '请选择' },
|
||||
searchcondition: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
searchType: {
|
||||
type: String as PropType<'default' | 'ex' | 'single'>,
|
||||
default: 'default',
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:label', val: string): void
|
||||
(e: 'update:value', val: any): void
|
||||
(e: 'change', value: 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)
|
||||
|
||||
emits('change', selectValue)
|
||||
}
|
||||
</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"
|
||||
:label-field2="props.labelField2"
|
||||
:value-field="props.valueField"
|
||||
:searchcondition="searchcondition"
|
||||
:search-type="searchType"
|
||||
@update:value="emits('update:value', $event)"
|
||||
@change="handleSelectChange"
|
||||
/>
|
||||
</sar-popout-input>
|
||||
</template>
|
||||
@@ -0,0 +1,403 @@
|
||||
<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,
|
||||
},
|
||||
labelField2: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
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) => {
|
||||
const label1 = item[props.labelField] ?? ''
|
||||
const label2 = props.labelField2
|
||||
? item[props.labelField2] ?? ''
|
||||
: ''
|
||||
|
||||
return {
|
||||
label: label2 ? `${label1} | ${label2}` : label1,
|
||||
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) => {
|
||||
const label1 = item[props.labelField] ?? ''
|
||||
const label2 = props.labelField2
|
||||
? item[props.labelField2] ?? ''
|
||||
: ''
|
||||
|
||||
return {
|
||||
label: label2 ? `${label1} | ${label2}` : label1,
|
||||
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="请选择"
|
||||
@after-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>
|
||||
Reference in new issue
Block a user