Files
ballet-server/miniprogram/src/components/common.tsx
T
yuxuanhui b0c62d461e feat: add project icon picker component and integrate with project management
- Implemented ProjectIconPicker component for selecting project icons.
- Created ProjectIcon component to display selected icons.
- Updated calendar page to remove unused PageHeading component.
- Modified growth page to enhance UI and improve text clarity.
- Refactored index page to include project icons and improve layout.
- Added mine page for user profile management with avatar selection.
- Introduced projects-sort page for managing project order with drag-and-drop functionality.
- Enhanced projects page to allow icon selection when adding or editing projects.
- Updated record page to streamline user experience and improve form actions.
- Adjusted practice service to support project icon management.
- Added tests for project saving functionality to ensure correct handling of icons.
- Updated global type definitions to support SVG and PNG imports.
2026-09-29 18:40:25 +08:00

44 lines
2.4 KiB
TypeScript

import Taro from '@tarojs/taro'
import { Text, View } from '@tarojs/components'
import Button from '@taroify/core/button'
import '@taroify/core/button/style'
import type { PracticeRecord } from '../services/practice'
import { duration } from '../utils/dates'
export { Button }
export function FormActions({ busy, cancelDisabled, saveDisabled, saveLabel = '保存', onCancel, onSave }: {
busy: boolean
cancelDisabled?: boolean
saveDisabled?: boolean
saveLabel?: string
onCancel: () => unknown
onSave: () => unknown
}) {
return <View className='form-actions'>
<View className='form-action'><Button block variant='outlined' disabled={busy || cancelDisabled} onClick={() => { void onCancel() }}>取消</Button></View>
<View className='form-action'><Button block color='primary' loading={busy} disabled={busy || saveDisabled} onClick={() => { void onSave() }}>{saveLabel}</Button></View>
</View>
}
export function LoadState({ loading, error, retry }: { loading: boolean; error: string; retry: () => unknown }) {
if (!loading && !error) return null
return <View className='panel status-panel'><Text className={error ? 'error-text' : 'muted'}>{loading ? '正在读取你的练习…' : error}</Text>{error && <Button size='small' variant='outlined' onClick={() => { void retry() }}>重新加载</Button>}</View>
}
export function Empty({ title }: { title: string }) {
return <View className='empty'><Text className='empty-title'>{title}</Text></View>
}
export function RecordCards({ records }: { records: PracticeRecord[] }) {
return <View>{records.map(record => <View className='record-card' key={record.id}>
<View className='row'><Text className='record-name'>{record.projectName}</Text><Text className='record-duration'>{duration(record.minutes)}</Text></View>
<View className='row'><Text className='muted small'>{record.date}{record.archived ? ' · 已归档项目' : ''}</Text><Button size='small' variant='text' onClick={() => { void Taro.navigateTo({ url: `/pages/record/index?id=${record.id}` }) }}>更正</Button></View>
{record.note && <Text className='record-note'>{record.note}</Text>}
</View>)}</View>
}
/** Return to the actual originating page; deep-entry forms fall back to records. */
export async function returnToSource(message?: string) {
if (Taro.getCurrentPages().length > 1) await Taro.navigateBack()
else await Taro.switchTab({ url: '/pages/index/index' })
if (message) void Taro.showToast({ title: message, icon: 'none' })
}