This commit is contained in:
oneao committed 2026-03-04 22:22:07 +08:00
1 parent 34240d2e16
commit bf350d8d35
21 files changed
+670 -304

No files matched your search

+166 -119
View File
@@ -1,130 +1,177 @@
<!DOCTYPE html>
<html lang="en">
import type { FinanceRecord } from '@/types/finance/record'
import Big from 'big.js'
import { useEffect, useState } from 'react'
import { Keyboard, Pressable, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'
import { ADatePicker, AInput, LucideIcon } from '@/components/common/ao/index'
import { useKeyboardHeight } from '@/hooks/useKeyboardHeight'
import { getCurrentDate } from '@/utils/time'
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>加入其它空间 Demo</title>
<style>
body {
font-family: sans-serif;
background: #f5f6f8;
margin: 0;
padding: 0;
interface KeydownItem {
key: string | number
content: React.ReactNode
}
interface AmountKeyboardProps {
show?: boolean
record: FinanceRecord
// 可选:点击完成按钮触发
onDone?: (record: FinanceRecord) => void
}
export default function AmountKeyboard({
show = false,
record,
onDone,
}: AmountKeyboardProps) {
// 自定义键盘计算过程
const [keydownInput, setKeydownInput] = useState('')
// 日期选择
const [showDatePicker, setShowDatePicker] = useState(false)
// 备注聚焦
const [remarkIsFocused, setRemarkIsFocused] = useState(false)
// 键盘高度
const keyboardHeight = useKeyboardHeight()
const [innerRecord, setInnerRecord] = useState<FinanceRecord>({
id: 0,
category_id: 0,
amount: '',
remark: '',
record_time: '',
member_id: 0,
member_name: '',
})
useEffect(() => {
setInnerRecord(record)
setKeydownInput(record.amount || '0.00')
}, [record])
useEffect(() => {
if (!keydownInput) {
setInnerRecord({
...innerRecord,
amount: '0',
})
return
}
/* 页面列表项 */
.space-option {
padding: 16px;
background: #fff;
margin: 16px;
border-radius: 12px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
try {
setInnerRecord({
...innerRecord,
amount: calculateWithBig(keydownInput),
})
}
.space-option:hover {
background: #f0f0f0;
catch {
setInnerRecord({
...innerRecord,
amount: '0',
})
}
}, [keydownInput])
/* modal 背景 */
.modal-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
display: none;
justify-content: center;
align-items: center;
}
return (
<View style={{
backgroundColor: '#fff',
elevation: 20,
}}
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View className="h-28 flex pl-4 pr-4 justify-center">
<View className="flex flex-row items-center justify-between">
<Pressable
className="flex flex-row items-center gap-1.5 px-2 py-1.5 rounded-md bg-gray-50 active:opacity-70"
onPress={() => setShowDatePicker(true)}
>
<LucideIcon icon="Calendar" size={16} color="#60a5fa" clickable={false} />
<Text className="text-gray-600 text-sm">{innerRecord.record_time}</Text>
<LucideIcon icon="ChevronDown" size={12} color="#aaa" clickable={false} />
</Pressable>
<Text className="text-3xl text-blue-700 text-right">{innerRecord.amount}</Text>
</View>
/* modal 内容 */
.modal {
background: #fff;
border-radius: 16px;
padding: 24px;
width: 90%;
max-width: 400px;
}
<Text className="text-gray-400 text-right">{keydownInput}</Text>
<AInput
value={innerRecord.remark}
placeholder="备注"
className="bg-[#f2f3f5] border-0"
size="sm"
onChangeText={(val) => {
setInnerRecord({
...innerRecord,
remark: val,
})
}}
onFocus={() => setRemarkIsFocused(true)}
onBlur={() => setRemarkIsFocused(false)}
/>
</View>
</TouchableWithoutFeedback>
<View className="flex flex-row flex-wrap" style={{ height: 220 }}>
{keys.map((item, idx) => {
const isFirstCol = idx % 4 === 0
const isFirstRow = idx < 4
const isLastCol = (idx + 1) % 4 === 0
const isLastRow = idx >= 12
.modal h2 {
margin: 0 0 16px 0;
font-size: 20px;
color: #333;
}
return (
<Pressable
key={item.key}
onPress={() => handlePress(item.key)}
style={{
width: '25%',
height: '25%',
justifyContent: 'center',
alignItems: 'center',
borderTopWidth: isFirstRow ? 0.3 : 0,
borderLeftWidth: isFirstCol ? 0.3 : 0,
borderRightWidth: !isLastCol ? 0.3 : 0,
borderBottomWidth: !isLastRow ? 0.3 : 0,
borderColor: '#ddd',
}}
>
{({ pressed }) => {
const defaultBg = item.key === '/' ? '#e6d7b8' : '#fff'
const pressedBg = item.key === '/' ? '#f5e9d4' : '#f2f2f2'
.modal input {
width: 100%;
padding: 12px;
margin-bottom: 16px;
border-radius: 8px;
border: 1px solid #ccc;
font-size: 16px;
}
return (
<>
<View
style={{
...StyleSheet.absoluteFillObject,
backgroundColor: pressed ? pressedBg : defaultBg,
}}
/>
{item.content}
</>
)
}}
</Pressable>
)
})}
</View>
{remarkIsFocused && keyboardHeight > 220 && (
<View style={{ height: keyboardHeight + 10 - 220 }} />
)}
.modal button {
width: 100%;
padding: 12px;
border-radius: 8px;
border: none;
background: #333;
color: #fff;
font-size: 16px;
cursor: pointer;
margin-bottom: 8px;
}
<ADatePicker
pickerTitle="记账时间"
mode="date"
date={innerRecord.record_time}
show={showDatePicker}
onClose={() => {
setShowDatePicker(false)
}}
onConfirm={(date) => {
setShowDatePicker(false)
.modal .scan-button {
background: #f0f0f0;
color: #333;
}
</style>
</head>
<body>
<!-- 加入其它空间选项 -->
<div class="space-option" id="joinSpaceBtn">
<span>加入其它空间</span>
<span>→</span>
</div>
<!-- Modal -->
<div class="modal-backdrop" id="modalBackdrop">
<div class="modal">
<h2>加入空间</h2>
<input type="text" placeholder="请输入邀请码" id="inviteInput" />
<button id="joinBtn">加入</button>
<button class="scan-button" id="scanBtn">扫码加入</button>
</div>
</div>
<script>
const joinBtnEl = document.getElementById('joinSpaceBtn');
const modalEl = document.getElementById('modalBackdrop');
joinBtnEl.addEventListener('click', () => {
modalEl.style.display = 'flex';
});
modalEl.addEventListener('click', (e) => {
if (e.target === modalEl) {
modalEl.style.display = 'none';
}
});
document.getElementById('joinBtn').addEventListener('click', () => {
const code = document.getElementById('inviteInput').value;
alert(`尝试加入空间:${code}`);
});
document.getElementById('scanBtn').addEventListener('click', () => {
alert('扫码功能触发(可接入扫码库)');
});
</script>
</body>
</html>
setInnerRecord({
...innerRecord,
record_time: date,
})
}}
/>
</View>
)
}