修改导航

This commit is contained in:
筱野
2025-09-21 22:00:30 +08:00
parent b42a5d610c
commit 64aa4ab2e3
10 changed files with 263 additions and 40 deletions

View File

@@ -0,0 +1,49 @@
.customNavbar {
position: fixed;
top: 0;
left: 0;
z-index: 999;
width: 100%;
background-color: #FAFAFA;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}
.navbarContent {
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
padding: 0 16px;
}
.leftSection {
display: flex;
align-items: center;
min-width: 60px;
}
.centerSection {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.rightSection {
display: flex;
align-items: center;
min-width: 60px;
}
.title {
font-size: 18px;
font-weight: 600;
color: #333;
text-align: center;
}
.backIcon {
width: 24px;
height: 24px;
cursor: pointer;
}

View File

@@ -0,0 +1,105 @@
import React from 'react'
import { View, Text, Image } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { useGlobalState } from '@/store/global'
import styles from './index.module.scss'
import img from '@/config/images'
interface GeneralNavbarProps {
title?: string
titleStyle?: React.CSSProperties
titleClassName?: string
leftContent?: React.ReactNode
backgroundColor?: string
showBack?: boolean
showLeft?: boolean
onBack?: () => void
className?: string
}
const GeneralNavbar: React.FC<GeneralNavbarProps> = ({
title = '',
titleStyle,
titleClassName = '',
leftContent,
backgroundColor = '#FAFAFA',
showBack = true,
showLeft = true,
onBack,
className = ''
}) => {
const { statusNavbarHeightInfo } = useGlobalState()
const { statusBarHeight, navBarHeight } = statusNavbarHeightInfo
const handleBack = () => {
if (onBack) {
onBack()
} else {
Taro.navigateBack()
}
}
const renderLeftContent = () => {
if (!showLeft) {
return null
}
if (leftContent) {
return leftContent
}
if (showBack) {
return (
<Image
src={img.ICON_LIST_SEARCH_BACK}
className={styles.backIcon}
onClick={handleBack}
/>
)
}
return null
}
const renderTitle = () => {
if (!title) {
return null
}
return (
<Text
className={`${styles.title} ${titleClassName}`}
style={titleStyle}
>
{title}
</Text>
)
}
return (
<View
className={`${styles.customNavbar} ${className}`}
style={{
height: `${navBarHeight}px`,
paddingTop: `${statusBarHeight}px`,
backgroundColor
}}
>
<View className={styles.navbarContent}>
<View className={styles.leftSection}>
{renderLeftContent()}
</View>
<View className={styles.centerSection}>
{renderTitle()}
</View>
<View className={styles.rightSection}>
{/* 右侧占位,保持标题居中 */}
</View>
</View>
</View>
)
}
export default GeneralNavbar

View File

@@ -44,14 +44,7 @@ const PublishMenu: React.FC<PublishMenuProps> = () => {
})
}
const handlePasteAndRecognize = (text: string) => {
console.log('识别的文本:', text)
// TODO: 实现文本识别逻辑
Taro.showToast({
title: '文本识别功能开发中',
icon: 'none'
})
}
return (
<View className={styles.publishMenu}>
@@ -129,7 +122,6 @@ const PublishMenu: React.FC<PublishMenuProps> = () => {
visible={aiImportVisible}
onClose={handleAiImportClose}
onManualPublish={handleManualPublish}
onPasteAndRecognize={handlePasteAndRecognize}
/>
</View>
)

View File

@@ -66,8 +66,11 @@ const TimeSelector: React.FC<TimeSelectorProps> = ({
<View className='time-content' onClick={() => openPicker('start')}>
<Text className='time-label'></Text>
<view className='time-text-wrapper'>
{value.start_time && (<>
<Text className='time-text'>{getDate(value.start_time)}</Text>
<Text className='time-text time-am'>{getTime(value.start_time)}</Text>
</>)}
{!value.start_time && (<Text className='time-text'></Text>)}
</view>
</View>
</View>
@@ -80,8 +83,9 @@ const TimeSelector: React.FC<TimeSelectorProps> = ({
<View className='time-content' onClick={() => openPicker('end')}>
<Text className='time-label'></Text>
<view className='time-text-wrapper'>
{showEndTime && (<Text className='time-text'>{getDate(value.end_time)}</Text>)}
<Text className='time-text time-am'>{getTime(value.end_time)}</Text>
{value.end_time && (<>{showEndTime && (<Text className='time-text'>{getDate(value.end_time)}</Text>)}
<Text className='time-text time-am'>{getTime(value.end_time)}</Text></>)}
{!value.end_time && (<Text className='time-text'></Text>)}
</view>
</View>
</View>

View File

@@ -20,6 +20,7 @@ import RefundPopup from "./refundPopup";
import GameManagePopup from './GameManagePopup';
import FollowUserCard from './FollowUserCard/index';
import Comments from "./Comments";
import GeneralNavbar from "./GeneralNavbar";
export {
ActivityTypeSwitch,
@@ -45,4 +46,5 @@ export {
GameManagePopup,
FollowUserCard,
Comments,
GeneralNavbar,
};

View File

@@ -12,19 +12,19 @@ export interface AiImportPopupProps {
visible: boolean
onClose: () => void
onManualPublish?: () => void
onPasteAndRecognize?: (text: string) => void
}
const AiImportPopup: React.FC<AiImportPopupProps> = ({
visible,
onClose,
onManualPublish,
onPasteAndRecognize
}) => {
const [text, setText] = useState('')
const [uploadFailCount, setUploadFailCount] = useState(0)
const [loading, setLoading] = useState(false)
const [uploadLoading, setUploadLoading] = useState(false)
const [keyboardHeight, setKeyboardHeight] = useState(0)
const [isKeyboardVisible, setIsKeyboardVisible] = useState(false)
const maxFailCount = 3
// 获取 actions在组件顶层调用 Hook
@@ -33,12 +33,34 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
const textIdentification = async (text: string) => {
setLoading(true)
const res = await publishService.extract_tennis_activity({text})
console.log(res)
const { data } = res
if (data && data?.length > 0) {
navigateToPublishBall(data)
} else {
Taro.showToast({
title: '未识别到球局信息',
icon: 'error'
})
setUploadFailCount(prev => prev + 1)
}
setLoading(false)
}
const initAiPopup = () => {
setText('')
setUploadFailCount(0)
setLoading(false)
setUploadLoading(false)
setKeyboardHeight(0)
setIsKeyboardVisible(false)
}
const handlePasteAndRecognize = async () => {
if (text) {
textIdentification(text)
} else {
getClipboardData()
}
}
const getClipboardData = async () => {
try {
const res = await Taro.getClipboardData()
@@ -75,6 +97,8 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
const navigateToPublishBall = (data: any) => {
if (Array.isArray(data) && data.length > 0) {
setPublishData(data)
initAiPopup()
onClose()
Taro.navigateTo({
url: '/publish_pages/publishBall/index?type=ai'
})
@@ -85,6 +109,32 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
setText(e.detail.value)
}
// 监听键盘高度变化,保持弹窗贴合底部
useEffect(() => {
Taro.onKeyboardHeightChange?.((res: any) => {
const height = Number(res?.height || 0)
if (height > 0) {
setIsKeyboardVisible(true)
setKeyboardHeight(height)
} else {
setIsKeyboardVisible(false)
setKeyboardHeight(0)
}
})
return () => {
// Taro 里 onKeyboardHeightChange 返回的不是取消函数,这里通过置零兜底
setIsKeyboardVisible(false)
setKeyboardHeight(0)
// 微信小程序环境可调用 offKeyboardHeightChange如存在则尝试注销
// @ts-ignore
if (typeof Taro.offKeyboardHeightChange === 'function') {
// @ts-ignore
Taro.offKeyboardHeightChange()
}
}
}, [])
const handleImageRecognition = async () => {
try {
const res = await Taro.chooseMedia({
@@ -104,7 +154,16 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
setUploadLoading(true)
const publishData = await publishService.extract_tennis_activity_from_image({image_url: ossPath})
const { data } = publishData
if (data && data?.length > 0) {
navigateToPublishBall(data)
} else {
Taro.showToast({
title: '未识别到球局信息',
icon: 'error'
})
setUploadFailCount(prev => prev + 1)
setUploadLoading(false)
}
setLoading(false)
}
}
@@ -138,6 +197,7 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
closeable={false}
onClose={onClose}
className={styles.aiImportPopup}
style={{ paddingBottom: isKeyboardVisible ? `${keyboardHeight}px` : undefined }}
>
<View className={styles.popupContent}>
{/* 头部 */}
@@ -157,11 +217,15 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
className={styles.textArea}
value={text}
onInput={handleTextChange}
onFocus={() => setIsKeyboardVisible(true)}
onBlur={() => setIsKeyboardVisible(false)}
placeholder="在此「粘贴识别」或输入文本,智能拆分球局时间、费用、地点和其他信息,并帮你智能生成球局标题"
maxlength={100}
showConfirmBar={false}
placeholderClass={styles.textArea_placeholder}
autoHeight
// 关闭系统自动上推,改为手动根据键盘高度加内边距
adjustPosition={false}
/>
<View className={styles.charCount}>
<Text className={styles.charCountText}>{text.length}/100</Text>
@@ -186,7 +250,7 @@ const AiImportPopup: React.FC<AiImportPopupProps> = ({
<Text className={styles.manualButtonText}></Text>
</View>
)}
<View className={styles.pasteButton} onClick={getClipboardData}>
<View className={styles.pasteButton} onClick={handlePasteAndRecognize}>
{
loading ? (
<View className={styles.loadingContainer}>

View File

@@ -1,4 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '发布',
navigationBarBackgroundColor: '#FAFAFA'
navigationStyle: 'custom'
})

View File

@@ -1,14 +1,15 @@
@use '~@/scss/themeColor.scss' as theme;
.publish-ball {
padding-top: 0;
min-height: 100vh;
background: theme.$page-background-color;
box-sizing: border-box;
position: relative;
&__scroll {
height: calc(100vh - 120px);
overflow: auto;
padding: 4px 16px 72px 16px;
padding: 4px 16px 20px 16px;
box-sizing: border-box;
}
@@ -255,3 +256,7 @@
transform: rotate(360deg);
}
}
.publish-ball-navbar{
box-shadow: none!important;
}

View File

@@ -10,6 +10,8 @@ import { FormFieldConfig, publishBallFormSchema } from '../../config/formSchema/
import { PublishBallFormData } from '../../../types/publishBall';
import PublishService from '@/services/publishService';
import { getNextHourTime, getEndTime, delay } from '@/utils';
import { useGlobalState } from "@/store/global"
import GeneralNavbar from "@/components/GeneralNavbar"
import images from '@/config/images'
import { useUserInfo } from '@/store/userStore'
import styles from './index.module.scss'
@@ -57,12 +59,13 @@ const PublishBall: React.FC = () => {
const [isSubmitDisabled, setIsSubmitDisabled] = useState(false)
const userInfo = useUserInfo();
const publishAiData = usePublishBallData()
const { statusNavbarHeightInfo } = useGlobalState();
// 获取页面参数并设置导航标题
const [optionsConfig, setOptionsConfig] = useState<FormFieldConfig[]>(publishBallFormSchema)
console.log(userInfo, 'userInfo');
const [formData, setFormData] = useState<PublishBallFormData[]>([defaultFormData])
const [checked, setChecked] = useState(true)
const [titleBar, setTitleBar] = useState('发布')
// 删除确认弹窗状态
const [deleteConfirm, setDeleteConfirm] = useState<{
visible: boolean;
@@ -412,13 +415,9 @@ const PublishBall: React.FC = () => {
}, [] as FormFieldConfig[])
setOptionsConfig(newFormSchema)
setFormData([defaultFormData])
Taro.setNavigationBarTitle({
title: '发布畅打活动'
})
setTitleBar('发布畅打活动')
} else {
Taro.setNavigationBarTitle({
title: '发布'
})
setTitleBar('发布')
setFormData([{...defaultFormData, wechat: { ...defaultFormData.wechat, default_wechat_contact: userPhone } }])
}
} else if (type === 'ai') {
@@ -435,9 +434,7 @@ const PublishBall: React.FC = () => {
} else {
setFormData([defaultFormData])
}
Taro.setNavigationBarTitle({
title: '发布畅打活动'
})
setTitleBar('发布畅打活动')
}
}
}
@@ -459,7 +456,9 @@ const PublishBall: React.FC = () => {
}, [])
return (
<View className={styles['publish-ball']}>
<View>
<GeneralNavbar title={titleBar} backgroundColor="#FAFAFA" className={styles['publish-ball-navbar']} />
<View className={styles['publish-ball']} style={{ paddingTop: `${statusNavbarHeightInfo.totalHeight}px` }}>
{/* 活动类型切换 */}
<View className={styles['activity-type-switch']}>
{/* <ActivityTypeSwitch
@@ -468,7 +467,7 @@ const PublishBall: React.FC = () => {
/> */}
</View>
<View className={styles['publish-ball__scroll']}>
<View className={styles['publish-ball__scroll']} style={{ height: `calc(100vh - ${statusNavbarHeightInfo.totalHeight+120}px)` }}>
{
formData.map((item, index) => (
<View key={index}>
@@ -553,6 +552,7 @@ const PublishBall: React.FC = () => {
}
</View>
</View>
</View>
)
}

View File

@@ -27,6 +27,9 @@ export interface PublishBallFormData {
description_tag: string[] // 备注标签
}
is_substitute_supported: boolean // 是否支持替补
wechat:{
is_wechat_contact: boolean // 是否需要微信联系
default_wechat_contact: string // 默认微信联系
wechat_contact: string // 微信联系
}
}