diff --git a/packages/uni-app-plus/src/service/api/page.ts b/packages/uni-app-plus/src/helpers/page.ts similarity index 96% rename from packages/uni-app-plus/src/service/api/page.ts rename to packages/uni-app-plus/src/helpers/page.ts index 20294c1c033a69678361eb94d54e94021e6ea02c..21b8e20fc2497401d8db11ceca290847e3bfef37 100644 --- a/packages/uni-app-plus/src/service/api/page.ts +++ b/packages/uni-app-plus/src/helpers/page.ts @@ -64,7 +64,7 @@ function addEventListener(pageId: string, callback: CallBack) { callbacks[pageId] = callback } -class Page { +export class Page { webview: PlusWebviewWebviewObject constructor(webview: PlusWebviewWebviewObject) { @@ -72,11 +72,13 @@ class Page { } sendMessage(data: any) { - const message = { - __message: { - data, - }, - } + const message = JSON.parse( + JSON.stringify({ + __message: { + data, + }, + }) + ) const id = this.webview.id if (BroadcastChannel_) { const channel = new BroadcastChannel_(id) diff --git a/packages/uni-app-plus/src/service/api/device/scanCode.ts b/packages/uni-app-plus/src/service/api/device/scanCode.ts index 103fefd34790860a13a4e1288a0385c44f663dd9..6b19b2ff7d7ac787677974237e37202bc7128d90 100644 --- a/packages/uni-app-plus/src/service/api/device/scanCode.ts +++ b/packages/uni-app-plus/src/service/api/device/scanCode.ts @@ -1,4 +1,4 @@ -import { showPage } from '../page.js' +import { showPage } from '../../../helpers/page.js' import { defineAsyncApi, API_SCAN_CODE, diff --git a/packages/uni-app-plus/src/service/api/location/chooseLocation.ts b/packages/uni-app-plus/src/service/api/location/chooseLocation.ts index 0f078725c9e5b243bd1c12c41cee9fe7df3ae0d2..f782445f0cd51831f60a270a0e5aa6422d4c614f 100644 --- a/packages/uni-app-plus/src/service/api/location/chooseLocation.ts +++ b/packages/uni-app-plus/src/service/api/location/chooseLocation.ts @@ -4,7 +4,7 @@ import { defineAsyncApi, ChooseLocationProtocol, } from '@dcloudio/uni-api' -import { showPage } from '../page' +import { showPage } from '../../../helpers/page' import { getStatusBarStyle } from '../../../helpers/statusBar' export const chooseLocation = defineAsyncApi( diff --git a/packages/uni-app-plus/src/service/api/location/openLocation.ts b/packages/uni-app-plus/src/service/api/location/openLocation.ts index 5274ae2358aa2157cf716a1739defa8aed8928d2..e0145418327ac0c4ae4ed078e1b073839e45dc6a 100644 --- a/packages/uni-app-plus/src/service/api/location/openLocation.ts +++ b/packages/uni-app-plus/src/service/api/location/openLocation.ts @@ -5,7 +5,7 @@ import { OpenLocationProtocol, OpenLocationOptions, } from '@dcloudio/uni-api' -import { showPage } from '../page.js' +import { showPage } from '../../../helpers/page.js' export const openLocation = defineAsyncApi( API_OPEN_LOCATION, diff --git a/packages/uni-app-plus/src/view/components/picker/index.tsx b/packages/uni-app-plus/src/view/components/picker/index.tsx index 62f2430b379288e9b18b4b76c0a5172f76127805..dc2981336cc0a5bf0cc0f2eccd39f385301afd47 100644 --- a/packages/uni-app-plus/src/view/components/picker/index.tsx +++ b/packages/uni-app-plus/src/view/components/picker/index.tsx @@ -1,5 +1,341 @@ -import { defineBuiltInComponent } from '@dcloudio/uni-components' +import { Ref, ref, watch, onBeforeUnmount, ExtractPropTypes, inject } from 'vue' +import { + defineBuiltInComponent, + useCustomEvent, + EmitEvent, +} from '@dcloudio/uni-components' +import { useI18n, initI18nPickerMsgsOnce } from '@dcloudio/uni-core' +import { UniFormCtx, uniFormKey } from '@dcloudio/uni-components' +import { showPage, Page } from '../../../helpers/page' +import { getNavigationBarHeight } from '../../../helpers/navigationBar' + +type Mode = 'selector' | 'multiSelector' | 'time' | 'date' +type Field = 'year' | 'month' | 'day' +const mode: Record = { + SELECTOR: 'selector', + MULTISELECTOR: 'multiSelector', + TIME: 'time', + DATE: 'date', + // 暂不支持城市选择 + // REGION: 'region' +} +const fields: Record = { + YEAR: 'year', + MONTH: 'month', + DAY: 'day', +} + +function padLeft(num: number) { + return num > 9 ? num : `0${num}` +} +function getDate(str: Props['value'], _mode: Mode) { + str = String(str || '') + const date = new Date() + if (_mode === mode.TIME) { + const strs = str.split(':') + if (strs.length === 2) { + date.setHours(parseInt(strs[0]), parseInt(strs[1])) + } + } else { + const strs = str.split('-') + if (strs.length === 3) { + date.setFullYear( + parseInt(strs[0]), + parseInt(String(parseFloat(strs[1]) - 1)), + parseInt(strs[2]) + ) + } + } + return date +} + +function getDefaultStartValue(props: any) { + if ((props as Props).mode === mode.TIME) { + return '00:00' + } + if ((props as Props).mode === mode.DATE) { + const year = new Date().getFullYear() - 100 + switch ((props as Props).fields) { + case fields.YEAR: + return year + case fields.MONTH: + return year + '-01' + default: + return year + '-01-01' + } + } + return '' +} +function getDefaultEndValue(props: any) { + if ((props as Props).mode === mode.TIME) { + return '23:59' + } + if ((props as Props).mode === mode.DATE) { + const year = new Date().getFullYear() + 100 + switch ((props as Props).fields) { + case fields.YEAR: + return year + case fields.MONTH: + return year + '-12' + default: + return year + '-12-31' + } + } + return '' +} + +const props = { + name: { + type: String, + default: '', + }, + range: { + type: Array, + default() { + return [] + }, + }, + rangeKey: { + type: String, + default: '', + }, + value: { + type: [Number, String, Array], + default: 0, + }, + mode: { + type: String, + default: mode.SELECTOR, + validator(val: string) { + return Object.values(mode).indexOf(val as Mode) >= 0 + }, + }, + fields: { + type: String, + default: '', + }, + start: { + type: String, + default: getDefaultStartValue, + }, + end: { + type: String, + default: getDefaultEndValue, + }, + disabled: { + type: [Boolean, String], + default: false, + }, +} +type Props = ExtractPropTypes export default /*#__PURE__*/ defineBuiltInComponent({ name: 'Picker', + props, + emits: ['change', 'cancel', 'columnchange'], + setup(props, { emit }) { + initI18nPickerMsgsOnce() + const { t, getLocale } = useI18n() + const rootRef: Ref = ref(null) + const trigger = useCustomEvent>(rootRef, emit) + + const valueSync: Ref | number | string | null> = ref(null) + const page: Ref = ref(null) + + type ShowPickerData = Props & { + value: typeof valueSync.value + locale: ReturnType + messages: { + done: string + cancel: string + } + } + + const _setValueSync = () => { + let val = props.value + switch (props.mode) { + case mode.MULTISELECTOR: + { + if (!Array.isArray(val)) { + val = [] + } + if (!Array.isArray(valueSync.value)) { + valueSync.value = [] + } + const length = (valueSync.value.length = Math.max( + val.length, + props.range.length + )) + for (let index = 0; index < length; index++) { + const val0 = Number(val[index]) + const val1 = Number(valueSync.value[index]) + const val2 = isNaN(val0) ? (isNaN(val1) ? 0 : val1) : val0 + valueSync.value.splice(index, 1, val2 < 0 ? 0 : val2) + } + } + break + case mode.TIME: + case mode.DATE: + valueSync.value = String(val) + break + default: { + const _valueSync = Number(val) + valueSync.value = _valueSync < 0 ? 0 : _valueSync + break + } + } + } + const _updatePicker = (data: ShowPickerData) => { + page.value && page.value.sendMessage(data) + } + const _showWeexPicker = (data: ShowPickerData) => { + let res: { event?: Parameters[0] } = { event: 'cancel' } + page.value = showPage({ + url: '__uniapppicker', + data, + style: { + titleNView: undefined, + animationType: 'none', + animationDuration: 0, + background: 'rgba(0,0,0,0)', + popGesture: 'none', + }, + onMessage: (message) => { + const event = message.event + if (event === 'created') { + _updatePicker(data) + return + } + if (event === 'columnchange') { + delete message.event + trigger(event, {} as Event, message) + return + } + res = message + }, + onClose: () => { + page.value = null + const event = res.event + delete res.event + event && trigger(event, {} as Event, res) + }, + }) + } + const _showNativePicker = (data: ShowPickerData, popover: any) => { + plus.nativeUI[props.mode === mode.TIME ? 'pickTime' : 'pickDate']( + (res) => { + const date = res.date + trigger('change', {} as Event, { + value: + props.mode === mode.TIME + ? `${padLeft(date.getHours())}:${padLeft(date.getMinutes())}` + : `${date.getFullYear()}-${padLeft( + date.getMonth() + 1 + )}-${padLeft(date.getDate())}`, + }) + }, + () => { + trigger('cancel', {} as Event, {}) + }, + props.mode === mode.TIME + ? { + time: getDate(props.value, mode.TIME), + popover, + } + : { + date: getDate(props.value, mode.DATE), + minDate: getDate(props.start, mode.DATE), + maxDate: getDate(props.end, mode.DATE), + popover, + } + ) + } + const _showPicker = (data: ShowPickerData, popover: any) => { + if ( + (data.mode === mode.TIME || data.mode === mode.DATE) && + !data.fields + ) { + _showNativePicker(data, popover) + } else { + data.fields = Object.values(fields).includes(data.fields as Field) + ? data.fields + : fields.DAY + _showWeexPicker(data) + } + } + const _show = (event: MouseEvent) => { + if (props.disabled) { + return + } + const eventTarget = event.currentTarget as HTMLElement + const rect = eventTarget.getBoundingClientRect() + _showPicker( + Object.assign({}, props, { + value: valueSync.value, + locale: getLocale(), + messages: { + done: t('uni.picker.done'), + cancel: t('uni.picker.cancel'), + }, + }), + { + top: rect.top + getNavigationBarHeight(), + left: rect.left, + width: rect.width, + height: rect.height, + } + ) + } + + const uniForm = inject( + uniFormKey, + false as unknown as UniFormCtx + ) + const formField = { + submit: (): [string, any] => [props.name, valueSync.value], + reset: () => { + switch (props.mode) { + case mode.SELECTOR: + valueSync.value = 0 + break + case mode.MULTISELECTOR: + Array.isArray(props.value) && + (valueSync.value = props.value.map((val) => 0)) + break + case mode.DATE: + case mode.TIME: + valueSync.value = '' + break + default: + break + } + }, + } + if (uniForm) { + uniForm.addField(formField) + onBeforeUnmount(() => uniForm.removeField(formField)) + } + + Object.keys(props).forEach((key) => { + if (key !== 'name') { + watch( + () => (props as any)[key], + (val) => { + const data = {} + ;(data as any)[key] = val + _updatePicker(data as any) + }, + { deep: true } + ) + } + }) + watch(() => props.value, _setValueSync, { deep: true }) + _setValueSync() + + return () => ( + + + + ) + }, }) diff --git a/packages/uni-components/src/components/button/index.tsx b/packages/uni-components/src/components/button/index.tsx index feb00be4cdcf8920e3edcf64e899a25fa5115da4..c253f229f9cf4c5cb87a9d0d01a13a09cd895cbb 100644 --- a/packages/uni-components/src/components/button/index.tsx +++ b/packages/uni-components/src/components/button/index.tsx @@ -1,4 +1,4 @@ -import { inject, onBeforeUnmount, ref } from 'vue' +import { inject, onBeforeUnmount, ref, getCurrentInstance } from 'vue' import { useI18n, initI18nButtonMsgsOnce } from '@dcloudio/uni-core' import { defineBuiltInComponent } from '../../helpers/component' import { useHover } from '../../helpers/useHover' diff --git a/packages/uni-components/src/components/radio-group/index.tsx b/packages/uni-components/src/components/radio-group/index.tsx index 5bccab5775b22533140cc37f4860cce0a3906129..09a5a5cf843e469aab04a48b9e622ae6151dac3f 100644 --- a/packages/uni-components/src/components/radio-group/index.tsx +++ b/packages/uni-components/src/components/radio-group/index.tsx @@ -1,4 +1,4 @@ -import { inject, provide, ref, onMounted } from 'vue' +import { inject, provide, ref, onMounted, onBeforeUnmount } from 'vue' import type { Ref, ExtractPropTypes, WritableComputedRef } from 'vue' import { PolySymbol } from '@dcloudio/uni-core' import { UniFormCtx, uniFormKey } from '../form' @@ -77,16 +77,20 @@ function useProvideRadioGroup( }) const uniForm = inject(uniFormKey, false as unknown as UniFormCtx) + const formField = { + submit: () => { + let data: [string, any] = ['', null] + if (props.name !== '') { + data[0] = props.name + data[1] = getFieldsValue() + } + return data + }, + } if (uniForm) { - uniForm.addField({ - submit: () => { - let data: [string, any] = ['', null] - if (props.name !== '') { - data[0] = props.name - data[1] = getFieldsValue() - } - return data - }, + uniForm.addField(formField) + onBeforeUnmount(() => { + uniForm.removeField(formField) }) }