修改日期问题弹出问题
This commit is contained in:
181
src/components/CustomPopup/CustomPopup.tsx
Normal file
181
src/components/CustomPopup/CustomPopup.tsx
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
import React, { useRef, useState } from 'react'
|
||||||
|
import type { CSSProperties, ReactNode } from 'react'
|
||||||
|
import { View, Text } from '@tarojs/components'
|
||||||
|
import { Button } from '@nutui/nutui-react-taro'
|
||||||
|
import styles from './index.module.scss'
|
||||||
|
|
||||||
|
export interface CustomPopupProps {
|
||||||
|
visible: boolean
|
||||||
|
onClose: () => void
|
||||||
|
title?: ReactNode
|
||||||
|
showHeader?: boolean
|
||||||
|
hideFooter?: boolean
|
||||||
|
cancelText?: string
|
||||||
|
confirmText?: string
|
||||||
|
onCancel?: () => void
|
||||||
|
onConfirm?: () => void
|
||||||
|
children?: ReactNode
|
||||||
|
className?: string
|
||||||
|
style?: CSSProperties
|
||||||
|
// 与 CommonPopup 保持入参一致
|
||||||
|
position?: 'center' | 'bottom' | 'top' | 'left' | 'right'
|
||||||
|
round?: boolean
|
||||||
|
zIndex?: number
|
||||||
|
enableDragToClose?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomPopup: React.FC<CustomPopupProps> = ({
|
||||||
|
visible,
|
||||||
|
onClose,
|
||||||
|
title,
|
||||||
|
showHeader = false,
|
||||||
|
hideFooter = false,
|
||||||
|
cancelText = '返回',
|
||||||
|
confirmText = '完成',
|
||||||
|
onCancel,
|
||||||
|
onConfirm,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
position = 'bottom',
|
||||||
|
round = true,
|
||||||
|
zIndex,
|
||||||
|
enableDragToClose = true,
|
||||||
|
}) => {
|
||||||
|
const [dragOffset, setDragOffset] = useState(0)
|
||||||
|
const [isDragging, setIsDragging] = useState(false)
|
||||||
|
const touchStartY = useRef(0)
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
if (onCancel) {
|
||||||
|
onCancel()
|
||||||
|
} else {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTouchStart = (e: any) => {
|
||||||
|
if (!enableDragToClose) return
|
||||||
|
|
||||||
|
touchStartY.current = e.touches[0].clientY
|
||||||
|
setIsDragging(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTouchMove = (e: any) => {
|
||||||
|
if (!enableDragToClose || !isDragging) return
|
||||||
|
|
||||||
|
const currentY = e.touches[0].clientY
|
||||||
|
const deltaY = currentY - touchStartY.current
|
||||||
|
|
||||||
|
if (deltaY > 0) {
|
||||||
|
setDragOffset(Math.min(deltaY, 100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTouchEnd = () => {
|
||||||
|
if (!enableDragToClose || !isDragging) return
|
||||||
|
|
||||||
|
setIsDragging(false)
|
||||||
|
|
||||||
|
if (dragOffset > 50) {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
setDragOffset(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlayAlignItems =
|
||||||
|
position === 'center'
|
||||||
|
? 'center'
|
||||||
|
: position === 'top'
|
||||||
|
? 'flex-start'
|
||||||
|
: 'flex-end'
|
||||||
|
|
||||||
|
const handleOverlayClick = () => {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
className={styles['custom-popup-overlay']}
|
||||||
|
style={{ zIndex: zIndex ?? undefined, alignItems: overlayAlignItems }}
|
||||||
|
onClick={handleOverlayClick}
|
||||||
|
>
|
||||||
|
<View className={styles['custom-popup-move']} catchMove></View>
|
||||||
|
<View
|
||||||
|
className={`${styles['custom-popup']} ${className ? className : ''}`}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{enableDragToClose && (
|
||||||
|
<View
|
||||||
|
className={styles['custom-popup__drag-handle-container']}
|
||||||
|
onTouchStart={handleTouchStart}
|
||||||
|
onTouchMove={handleTouchMove}
|
||||||
|
onTouchEnd={handleTouchEnd}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
className={styles['custom-popup__drag-handle']}
|
||||||
|
style={{
|
||||||
|
transform: `translateX(-50%) translateY(${dragOffset * 0.3}px)`,
|
||||||
|
opacity: isDragging ? 0.8 : 1,
|
||||||
|
transition: isDragging ? 'none' : 'all 0.3s ease-out',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showHeader && (
|
||||||
|
<View className={styles['custom-popup__header']}>
|
||||||
|
{typeof title === 'string' ? (
|
||||||
|
<Text className={styles['custom-popup__title']}>{title}</Text>
|
||||||
|
) : (
|
||||||
|
title
|
||||||
|
)}
|
||||||
|
<View className={styles['close_button']} onClick={onClose}>
|
||||||
|
<View className={styles['close_icon']}>
|
||||||
|
<View className={styles['close_line']} />
|
||||||
|
<View className={styles['close_line']} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View className={styles['custom-popup__body']}>{children}</View>
|
||||||
|
|
||||||
|
{!hideFooter && (
|
||||||
|
<View className={styles['custom-popup__footer']}>
|
||||||
|
<Button
|
||||||
|
className={`${styles['custom-popup__btn']} ${styles['custom-popup__btn-cancel']}`}
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
|
onClick={handleCancel}
|
||||||
|
>
|
||||||
|
{cancelText}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={`${styles['custom-popup__btn']} ${styles['custom-popup__btn-confirm']}`}
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
onClick={onConfirm}
|
||||||
|
>
|
||||||
|
{confirmText}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomPopup
|
||||||
|
|
||||||
153
src/components/CustomPopup/index.module.scss
Normal file
153
src/components/CustomPopup/index.module.scss
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
@use "~@/scss/themeColor.scss" as theme;
|
||||||
|
|
||||||
|
.custom-popup-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.custom-popup-move{
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 9998;
|
||||||
|
}
|
||||||
|
.custom-popup {
|
||||||
|
position: relative;
|
||||||
|
z-index: 9999;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: theme.$page-background-color;
|
||||||
|
border-radius: 20px 20px 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
.custom-popup__drag-handle-container {
|
||||||
|
position: relative;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__drag-handle {
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
left: 50%;
|
||||||
|
width: 90px;
|
||||||
|
height: 30px;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
width: 32px;
|
||||||
|
height: 4px;
|
||||||
|
background-color: rgba(22, 24, 35, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px 20px;
|
||||||
|
|
||||||
|
.custom-popup__title {
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 1.27em;
|
||||||
|
color: #000000;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close_button {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
.close_icon {
|
||||||
|
position: relative;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
|
||||||
|
.close_line {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 17px;
|
||||||
|
height: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #000000;
|
||||||
|
transform: translate(-50%, -50%) rotate(45deg);
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
transform: translate(-50%, -50%) rotate(-45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__body {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__footer {
|
||||||
|
padding: 8px 10px 0 10px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
background: #fafafa;
|
||||||
|
padding-bottom: max(10px, env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__btn {
|
||||||
|
flex: 1;
|
||||||
|
font-feature-settings: "liga" off, "clig" off;
|
||||||
|
font-family: "PingFang SC";
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__btn-cancel {
|
||||||
|
background: #f5f6f7;
|
||||||
|
color: #1f2329;
|
||||||
|
border: none;
|
||||||
|
width: 154px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: #fff;
|
||||||
|
padding: 4px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-popup__btn-confirm {
|
||||||
|
width: 154px;
|
||||||
|
height: 44px;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: #000;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
padding: 4px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
4
src/components/CustomPopup/index.ts
Normal file
4
src/components/CustomPopup/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import CustomPopup from './CustomPopup'
|
||||||
|
export default CustomPopup
|
||||||
|
export * from './CustomPopup'
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import CommonPopup from "@/components/CommonPopup";
|
import CommonPopup from "@/components/CommonPopup";
|
||||||
import { View } from "@tarojs/components";
|
import { View } from "@tarojs/components";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
import CalendarUI, {
|
import CalendarUI, {
|
||||||
CalendarUIRef,
|
CalendarUIRef,
|
||||||
} from "@/components/Picker/CalendarUI/CalendarUI";
|
} from "@/components/Picker/CalendarUI/CalendarUI";
|
||||||
@@ -47,6 +48,13 @@ const DialogCalendarCard: React.FC<DialogCalendarCardProps> = ({
|
|||||||
onClose();
|
onClose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!selected) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请选择日期',
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 年份选择完成后,进入月份选择
|
// 年份选择完成后,进入月份选择
|
||||||
setType("time");
|
setType("time");
|
||||||
} else if (type === "month") {
|
} else if (type === "month") {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import CommonPopup from "@/components/CommonPopup";
|
import CommonPopup from "@/components/CommonPopup";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
import { View } from "@tarojs/components";
|
import { View } from "@tarojs/components";
|
||||||
import CalendarUI, {
|
import CalendarUI, {
|
||||||
CalendarUIRef,
|
CalendarUIRef,
|
||||||
@@ -32,6 +33,13 @@ const DayDialog: React.FC<DayDialogProps> = ({
|
|||||||
} | null>(null);
|
} | null>(null);
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
console.log(selected, 'selectedselected');
|
console.log(selected, 'selectedselected');
|
||||||
|
if (!selected) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请选择日期',
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
const finalDate = dayjs(selected as Date).format("YYYY-MM-DD");
|
const finalDate = dayjs(selected as Date).format("YYYY-MM-DD");
|
||||||
if (onChange){
|
if (onChange){
|
||||||
onChange(finalDate)
|
onChange(finalDate)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import NumberInterval from "./NumberInterval";
|
|||||||
import TimeSelector from "./TimeSelector";
|
import TimeSelector from "./TimeSelector";
|
||||||
import TitleTextarea from "./TitleTextarea";
|
import TitleTextarea from "./TitleTextarea";
|
||||||
import CommonPopup from "./CommonPopup";
|
import CommonPopup from "./CommonPopup";
|
||||||
|
import CustomPopup from "./CustomPopup";
|
||||||
import { CalendarUI, DialogCalendarCard } from "./Picker";
|
import { CalendarUI, DialogCalendarCard } from "./Picker";
|
||||||
import CommonDialog from "./CommonDialog";
|
import CommonDialog from "./CommonDialog";
|
||||||
import PublishMenu from "./PublishMenu/PublishMenu";
|
import PublishMenu from "./PublishMenu/PublishMenu";
|
||||||
@@ -37,6 +38,7 @@ export {
|
|||||||
TimeSelector,
|
TimeSelector,
|
||||||
TitleTextarea,
|
TitleTextarea,
|
||||||
CommonPopup,
|
CommonPopup,
|
||||||
|
CustomPopup,
|
||||||
DialogCalendarCard,
|
DialogCalendarCard,
|
||||||
CalendarUI,
|
CalendarUI,
|
||||||
CommonDialog,
|
CommonDialog,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import { View, Text, Textarea, Image } from '@tarojs/components'
|
import { View, Text, Textarea, Image } from '@tarojs/components'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { ConfigProvider, Loading, Popup, Toast } from '@nutui/nutui-react-taro'
|
import { ConfigProvider, Loading, Toast } from '@nutui/nutui-react-taro'
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
import uploadFiles from '@/services/uploadFiles'
|
import uploadFiles from '@/services/uploadFiles'
|
||||||
import publishService from '@/services/publishService'
|
import publishService from '@/services/publishService'
|
||||||
@@ -109,7 +109,10 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleTextChange = (e: any) => {
|
const handleTextChange = (e: any) => {
|
||||||
setText(e.detail.value)
|
const text = e.detail.value;
|
||||||
|
const maxAllowedLength = 120;
|
||||||
|
const truncatedVal = text.length > maxAllowedLength ? text.slice(0, maxAllowedLength) : text
|
||||||
|
setText(truncatedVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用全局键盘状态监听
|
// 使用全局键盘状态监听
|
||||||
@@ -191,73 +194,90 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const showManualButton = uploadFailCount >= maxFailCount
|
const showManualButton = uploadFailCount >= maxFailCount
|
||||||
|
if (!visible) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阻止弹窗内的触摸事件冒泡
|
||||||
|
const handleTouchMoveInPopup = (e) => {
|
||||||
|
if (!isKeyboardVisible) {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popup
|
<View
|
||||||
visible={visible}
|
className={styles.aiImportPopupOverlay}
|
||||||
position="bottom"
|
>
|
||||||
round={true}
|
<View className={styles.aiImportPopupWrapper} onTouchMove={handleTouchMoveInPopup} catchMove></View>
|
||||||
closeable={false}
|
<View
|
||||||
onClose={closePopupBefore}
|
className={styles.aiImportPopup}
|
||||||
className={styles.aiImportPopup}
|
style={{ paddingBottom: isKeyboardVisible ? `${keyboardHeight}px` : undefined }}
|
||||||
style={{ paddingBottom: isKeyboardVisible ? `${keyboardHeight}px` : undefined }}
|
>
|
||||||
>
|
<View className={styles.popupContent}>
|
||||||
<View className={styles.popupContent}>
|
{/* 头部 */}
|
||||||
{/* 头部 */}
|
<View className={styles.header}>
|
||||||
<View className={styles.header}>
|
<View className={styles.titleContainer}>
|
||||||
<View className={styles.titleContainer}>
|
<Image src={images.ICON_IMPORTANT_BLACK} className={styles.lightningIcon} />
|
||||||
<Image src={images.ICON_IMPORTANT_BLACK} className={styles.lightningIcon} />
|
<Text className={styles.title}>智能导入球局信息</Text>
|
||||||
<Text className={styles.title}>智能导入球局信息</Text>
|
|
||||||
</View>
|
|
||||||
<View className={styles.closeButton} onClick={closePopupBefore}>
|
|
||||||
<Image src={images.ICON_CLOSE} className={styles.lightningIcon} />
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 文本域 */}
|
|
||||||
<View className={styles.textAreaContainer}>
|
|
||||||
<Textarea
|
|
||||||
className={styles.textArea}
|
|
||||||
value={text}
|
|
||||||
onInput={handleTextChange}
|
|
||||||
onFocus={() => {}}
|
|
||||||
onBlur={() => {}}
|
|
||||||
placeholder="在此「粘贴识别」或输入文本,智能拆分球局时间、费用、地点和其他信息,并帮你智能生成球局标题"
|
|
||||||
maxlength={-1}
|
|
||||||
showConfirmBar={false}
|
|
||||||
placeholderClass={styles.textArea_placeholder}
|
|
||||||
autoHeight
|
|
||||||
// 关闭系统自动上推,改为手动根据键盘高度加内边距
|
|
||||||
adjustPosition={false}
|
|
||||||
/>
|
|
||||||
<View className={styles.charCount}>
|
|
||||||
<Text className={`${styles.charCountText} ${isCharCountExceeded ? styles.charCountTextExceeded : ''}`}>
|
|
||||||
{text.length}/100
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 图片识别按钮 */}
|
|
||||||
<View className={styles.imageRecognitionContainer}>
|
|
||||||
<View className={`${styles.imageRecognitionButton} ${uploadLoading ? styles.uploadLoadingContainer : ''}`} onClick={handleImageRecognition}>
|
|
||||||
{
|
|
||||||
uploadLoading ? (<Image src={images.ICON_UPLOAD_SUCCESS} className={styles.cameraIcon} />) : (<Image src={images.ICON_UPLOAD_IMG} className={styles.cameraIcon} />)
|
|
||||||
}
|
|
||||||
<Text className={styles.imageRecognitionText}>图片识别</Text>
|
|
||||||
<Text className={styles.imageRecognitionDesc}>{uploadLoading ? '已上传 1 张图片' : '支持订场截图/小红书笔记截图等图片'}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 底部按钮 */}
|
|
||||||
<View className={styles.bottomButtons}>
|
|
||||||
{showManualButton && (
|
|
||||||
<View className={styles.manualButton} onClick={handleManualPublish}>
|
|
||||||
<Text className={styles.manualButtonText}>手动发布球局</Text>
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
<View className={styles.closeButton} onClick={closePopupBefore}>
|
||||||
<View className={styles.pasteButton} onClick={handlePasteAndRecognize}>
|
<Image src={images.ICON_CLOSE} className={styles.lightningIcon} />
|
||||||
{
|
</View>
|
||||||
loading ? (
|
</View>
|
||||||
|
|
||||||
|
{/* 文本域 */}
|
||||||
|
<View className={styles.textAreaContainer}>
|
||||||
|
<Textarea
|
||||||
|
className={styles.textArea}
|
||||||
|
value={text}
|
||||||
|
onInput={handleTextChange}
|
||||||
|
onFocus={() => {}}
|
||||||
|
onBlur={() => {}}
|
||||||
|
placeholder="在此「粘贴识别」或输入文本,智能拆分球局时间、费用、地点和其他信息,并帮你智能生成球局标题"
|
||||||
|
maxlength={-1}
|
||||||
|
showConfirmBar={false}
|
||||||
|
placeholderClass={styles.textArea_placeholder}
|
||||||
|
autoHeight
|
||||||
|
// 关闭系统自动上推,改为手动根据键盘高度加内边距
|
||||||
|
adjustPosition={false}
|
||||||
|
/>
|
||||||
|
<View className={styles.charCount}>
|
||||||
|
<Text className={`${styles.charCountText} ${isCharCountExceeded ? styles.charCountTextExceeded : ''}`}>
|
||||||
|
{text.length}/100
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 图片识别按钮 */}
|
||||||
|
<View className={styles.imageRecognitionContainer}>
|
||||||
|
<View
|
||||||
|
className={`${styles.imageRecognitionButton} ${
|
||||||
|
uploadLoading ? styles.uploadLoadingContainer : ''
|
||||||
|
}`}
|
||||||
|
onClick={handleImageRecognition}
|
||||||
|
>
|
||||||
|
{uploadLoading ? (
|
||||||
|
<Image src={images.ICON_UPLOAD_SUCCESS} className={styles.cameraIcon} />
|
||||||
|
) : (
|
||||||
|
<Image src={images.ICON_UPLOAD_IMG} className={styles.cameraIcon} />
|
||||||
|
)}
|
||||||
|
<Text className={styles.imageRecognitionText}>图片识别</Text>
|
||||||
|
<Text className={styles.imageRecognitionDesc}>
|
||||||
|
{uploadLoading ? '已上传 1 张图片' : '支持订场截图/小红书笔记截图等图片'}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 底部按钮 */}
|
||||||
|
<View className={styles.bottomButtons}>
|
||||||
|
{showManualButton && (
|
||||||
|
<View className={styles.manualButton} onClick={handleManualPublish}>
|
||||||
|
<Text className={styles.manualButtonText}>手动发布球局</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<View className={styles.pasteButton} onClick={handlePasteAndRecognize}>
|
||||||
|
{loading ? (
|
||||||
<View className={styles.loadingContainer}>
|
<View className={styles.loadingContainer}>
|
||||||
<ConfigProvider theme={{ nutuiLoadingIconColor: '#fff', nutuiLoadingIconSize: '20px' }}>
|
<ConfigProvider theme={{ nutuiLoadingIconColor: '#fff', nutuiLoadingIconSize: '20px' }}>
|
||||||
<Loading type="circular" />
|
<Loading type="circular" />
|
||||||
@@ -269,13 +289,13 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
|
|||||||
<Image src={images.ICON_COPY} className={styles.clipboardIcon} />
|
<Image src={images.ICON_COPY} className={styles.clipboardIcon} />
|
||||||
<Text className={styles.pasteButtonText}>粘贴并识别</Text>
|
<Text className={styles.pasteButtonText}>粘贴并识别</Text>
|
||||||
</>
|
</>
|
||||||
)
|
)}
|
||||||
}
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
<Toast id="toast" />
|
||||||
</View>
|
</View>
|
||||||
<Toast id="toast" />
|
</View>
|
||||||
</Popup>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,34 @@
|
|||||||
@use '~@/scss/themeColor.scss' as theme;
|
@use '~@/scss/themeColor.scss' as theme;
|
||||||
|
|
||||||
|
.aiImportPopupOverlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 9998;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aiImportPopupWrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 9998;
|
||||||
|
}
|
||||||
.aiImportPopup {
|
.aiImportPopup {
|
||||||
background-color: #fff;
|
width: 100%;
|
||||||
&:global(.nut-popup-bottom.nut-popup-round) {
|
background-color:#fafafa;
|
||||||
border-radius: 20px 20px 0 0!important;
|
border-radius: 16px 16px 0 0;
|
||||||
}
|
position: relative;
|
||||||
|
z-index: 9999;
|
||||||
.popupContent {
|
.popupContent {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: #fff;
|
|
||||||
border-radius: 16px 16px 0 0;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { View, Text, Input, ScrollView, Image } from '@tarojs/components'
|
|||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { Loading } from '@nutui/nutui-react-taro'
|
import { Loading } from '@nutui/nutui-react-taro'
|
||||||
import StadiumDetail, { StadiumDetailRef } from './StadiumDetail'
|
import StadiumDetail, { StadiumDetailRef } from './StadiumDetail'
|
||||||
import { CommonPopup } from '../../../../components'
|
import { CommonPopup, CustomPopup } from '../../../../components'
|
||||||
import { getLocation } from '@/utils/locationUtils'
|
import { getLocation } from '@/utils/locationUtils'
|
||||||
import PublishService from '@/services/publishService'
|
import PublishService from '@/services/publishService'
|
||||||
import images from '@/config/images'
|
import images from '@/config/images'
|
||||||
@@ -188,24 +188,20 @@ const SelectStadium: React.FC<SelectStadiumProps> = ({
|
|||||||
// 如果显示详情页面
|
// 如果显示详情页面
|
||||||
if (showDetail && selectedStadium) {
|
if (showDetail && selectedStadium) {
|
||||||
return (
|
return (
|
||||||
<CommonPopup
|
<CustomPopup
|
||||||
visible={visible}
|
visible={visible}
|
||||||
onClose={handleCancel}
|
onClose={handleCancel}
|
||||||
cancelText="返回"
|
cancelText="返回"
|
||||||
confirmText="确认"
|
confirmText="确认"
|
||||||
className="select-stadium-popup"
|
|
||||||
onCancel={handleDetailCancel}
|
onCancel={handleDetailCancel}
|
||||||
onConfirm={handleConfirm}
|
onConfirm={handleConfirm}
|
||||||
position="bottom"
|
|
||||||
//style={{ paddingBottom: keyboardVisible ? `20px` : undefined }}
|
|
||||||
round
|
|
||||||
>
|
>
|
||||||
<StadiumDetail
|
{/* 内容区域 */}
|
||||||
ref={stadiumDetailRef}
|
<StadiumDetail
|
||||||
stadium={selectedStadium}
|
ref={stadiumDetailRef}
|
||||||
//onAnyInput={handleAnyInput}
|
stadium={selectedStadium}
|
||||||
/>
|
/>
|
||||||
</CommonPopup>
|
</CustomPopup>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
.stadium-detail-scroll{
|
.stadium-detail-scroll{
|
||||||
height:60vh;
|
max-height:60vh;
|
||||||
}
|
}
|
||||||
// 已选球场
|
// 已选球场
|
||||||
// 场馆列表
|
// 场馆列表
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import React, { useState, useCallback, forwardRef, useImperativeHandle } from 'react'
|
import React, { useState, useCallback, forwardRef, useImperativeHandle, useEffect } from 'react'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
||||||
import images from '@/config/images'
|
import images from '@/config/images'
|
||||||
import TextareaTag from '@/components/TextareaTag'
|
import TextareaTag from '@/components/TextareaTag'
|
||||||
// import CoverImageUpload, { type CoverImage } from '@/components/ImageUpload'
|
// import CoverImageUpload, { type CoverImage } from '@/components/ImageUpload'
|
||||||
import UploadCover, { type CoverImageValue } from '@/components/UploadCover'
|
import UploadCover, { type CoverImageValue } from '@/components/UploadCover'
|
||||||
|
import { useKeyboardHeight } from '@/store/keyboardStore'
|
||||||
import { useDictionaryActions } from '@/store/dictionaryStore'
|
import { useDictionaryActions } from '@/store/dictionaryStore'
|
||||||
|
|
||||||
import './StadiumDetail.scss'
|
import './StadiumDetail.scss'
|
||||||
@@ -75,6 +76,10 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
|
|||||||
const court_type = getDictionaryValue('court_type') || []
|
const court_type = getDictionaryValue('court_type') || []
|
||||||
const court_surface = getDictionaryValue('court_surface') || []
|
const court_surface = getDictionaryValue('court_surface') || []
|
||||||
const supplementary_information = getDictionaryValue('supplementary_information') || []
|
const supplementary_information = getDictionaryValue('supplementary_information') || []
|
||||||
|
|
||||||
|
// 使用全局键盘状态
|
||||||
|
const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener } = useKeyboardHeight()
|
||||||
|
|
||||||
const stadiumInfo = [
|
const stadiumInfo = [
|
||||||
{
|
{
|
||||||
label: '场地类型',
|
label: '场地类型',
|
||||||
@@ -171,14 +176,36 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const changeTextarea = (value) => {
|
// 使用全局键盘状态监听
|
||||||
|
useEffect(() => {
|
||||||
|
// 初始化全局键盘监听器
|
||||||
|
initializeKeyboardListener()
|
||||||
|
|
||||||
|
// 添加本地监听器
|
||||||
|
const removeListener = addListener((height, visible) => {
|
||||||
|
console.log('AiImportPopup 收到键盘变化:', height, visible)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
removeListener()
|
||||||
|
}
|
||||||
|
}, [initializeKeyboardListener, addListener])
|
||||||
|
|
||||||
|
const changeTextarea = (value: boolean) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
// 先滚动到底部
|
// 先滚动到底部
|
||||||
setScrollTop(scrollTop ? scrollTop + 1 : 9999);
|
setScrollTop(scrollTop ? scrollTop + 15 : 9999);
|
||||||
// 使用 setTimeout 确保滚动后再更新 openPicker
|
// 使用 setTimeout 确保滚动后再更新 openPicker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 当键盘显示时触发 changeTextarea
|
||||||
|
useEffect(() => {
|
||||||
|
if (isKeyboardVisible) {
|
||||||
|
changeTextarea(true)
|
||||||
|
}
|
||||||
|
}, [isKeyboardVisible])
|
||||||
|
|
||||||
const changePicker = (value) => {
|
const changePicker = (value) => {
|
||||||
setOpenPicker(value);
|
setOpenPicker(value);
|
||||||
}
|
}
|
||||||
@@ -189,7 +216,7 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
|
|||||||
<ScrollView
|
<ScrollView
|
||||||
className='stadium-detail-scroll'
|
className='stadium-detail-scroll'
|
||||||
refresherBackground="#FAFAFA"
|
refresherBackground="#FAFAFA"
|
||||||
scrollY={!openPicker}
|
scrollY
|
||||||
scrollTop={scrollTop}
|
scrollTop={scrollTop}
|
||||||
>
|
>
|
||||||
{/* 已选球场 */}
|
{/* 已选球场 */}
|
||||||
@@ -239,7 +266,7 @@ const StadiumDetail = forwardRef<StadiumDetailRef, StadiumDetailProps>(({
|
|||||||
updateFormData(item.prop, value)
|
updateFormData(item.prop, value)
|
||||||
}}
|
}}
|
||||||
// onBlur={() => changeTextarea(false)}
|
// onBlur={() => changeTextarea(false)}
|
||||||
onFocus={() => changeTextarea(true)}
|
//onFocus={() => changeTextarea(true)}
|
||||||
placeholder='有其他场地信息可备注'
|
placeholder='有其他场地信息可备注'
|
||||||
options={(item.options || []).map((o) => ({ label: o, value: o }))}
|
options={(item.options || []).map((o) => ({ label: o, value: o }))}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -783,6 +783,7 @@ const PublishBall: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<GeneralNavbar
|
<GeneralNavbar
|
||||||
title={titleBar}
|
title={titleBar}
|
||||||
|
backgroundColor={'#FAFAFA'}
|
||||||
className={styles["publish-ball-navbar"]}
|
className={styles["publish-ball-navbar"]}
|
||||||
/>
|
/>
|
||||||
<View
|
<View
|
||||||
|
|||||||
Reference in New Issue
Block a user