This commit is contained in:
oneao committed 2026-02-27 17:29:28 +08:00
1 parent 1903861354
commit 1b1a54f7dd
35 files changed
+1185 -535

No files matched your search

+62 -202
View File
@@ -1,207 +1,67 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>记账列表</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
import { useCallback } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Sortable from 'react-native-sortables';
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
const DATA = Array.from({ length: 4 }, (_, index) => `Item ${index + 1}`);
export default function CustomHandleExample() {
const renderItem = useCallback(
({ item }: { item: string }) => (
<View style={styles.card}>
<Text style={styles.text}>{item}</Text>
{/* Wraps the handle component */}
{/* highlight-next-line */}
<Sortable.Handle>
<View style={styles.handle}>
<Text style={styles.handleText}>::</Text>
</View>
{/* highlight-next-line */}
</Sortable.Handle>
</View>
),
[]
);
return (
<View style={styles.container}>
<Sortable.Grid
activeItemScale={1.05}
columns={1}
data={DATA}
overDrag='vertical'
renderItem={renderItem}
rowGap={10}
// highlight-next-line
customHandle // must be set to use a custom handle
/>
</View>
);
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto;
background: #f5f6f8;
color: #333;
}
.app {
max-width: 480px;
margin: 0 auto;
background: #f5f6f8;
min-height: 100vh;
}
/* 顶部统计 */
.summary {
background: #ffffff;
padding: 20px 16px;
border-bottom: 1px solid #eee;
}
.summary-title {
font-size: 14px;
color: #888;
margin-bottom: 10px;
}
.summary-row {
display: flex;
justify-content: space-between;
}
.summary-item {
text-align: center;
flex: 1;
}
.summary-item span {
display: block;
font-size: 12px;
color: #999;
}
.summary-item strong {
font-size: 18px;
font-weight: 600;
}
/* 日期分组 */
.date-group {
margin-top: 12px;
}
.date-header {
padding: 8px 16px;
font-size: 13px;
color: #666;
}
/* 账单项 */
.record {
background: #fff;
padding: 14px 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #f0f0f0;
}
.record-left {
display: flex;
flex-direction: column;
}
.record-type {
font-size: 15px;
font-weight: 500;
}
.record-desc {
font-size: 12px;
color: #999;
margin-top: 4px;
}
.record-amount {
font-size: 16px;
font-weight: 600;
}
.income {
color: #2bb673;
}
.expense {
color: #e74c3c;
}
</style>
</head>
<body>
<div id="app" class="app">
<!-- 顶部统计 -->
<div class="summary">
<div class="summary-title">本月统计</div>
<div class="summary-row">
<div class="summary-item">
<span>收入</span>
<strong class="income">{{ totalIncome }}</strong>
</div>
<div class="summary-item">
<span>支出</span>
<strong class="expense">{{ totalExpense }}</strong>
</div>
<div class="summary-item">
<span>结余</span>
<strong>{{ balance }}</strong>
</div>
</div>
</div>
<!-- 列表 -->
<div v-for="group in groupedRecords" :key="group.date" class="date-group">
<div class="date-header">{{ group.date }}</div>
<div
v-for="item in group.list"
:key="item.id"
class="record"
>
<div class="record-left">
<div class="record-type">{{ item.category }}</div>
<div class="record-desc">{{ item.remark }}</div>
</div>
<div
class="record-amount"
:class="item.type === 'income' ? 'income' : 'expense'"
>
{{ item.type === 'income' ? '+' : '-' }}{{ item.amount }}
</div>
</div>
</div>
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
records: [
{ id: 1, date: '2026-02-11', category: '餐饮', remark: '午餐', amount: 35, type: 'expense' },
{ id: 2, date: '2026-02-11', category: '工资', remark: '2月工资', amount: 8000, type: 'income' },
{ id: 3, date: '2026-02-10', category: '交通', remark: '地铁', amount: 6, type: 'expense' },
{ id: 4, date: '2026-02-10', category: '购物', remark: '日用品', amount: 120, type: 'expense' }
]
}
const styles = StyleSheet.create({
card: {
alignItems: 'center',
backgroundColor: '#2e8555',
borderRadius: 12,
flexDirection: 'row',
justifyContent: 'center',
padding: 24
},
computed: {
totalIncome() {
return this.records
.filter(r => r.type === 'income')
.reduce((sum, r) => sum + r.amount, 0)
},
totalExpense() {
return this.records
.filter(r => r.type === 'expense')
.reduce((sum, r) => sum + r.amount, 0)
},
balance() {
return this.totalIncome - this.totalExpense
},
groupedRecords() {
const groups = {}
this.records.forEach(item => {
if (!groups[item.date]) {
groups[item.date] = []
}
groups[item.date].push(item)
})
return Object.keys(groups).map(date => ({
date,
list: groups[date]
}))
}
container: {
padding: 16
},
handle: {
backgroundColor: 'rgba(0,0,0,0.2)',
borderRadius: 5,
padding: 10
},
handleText: {
color: 'white',
fontWeight: 'bold'
},
text: {
color: 'white',
flex: 1,
fontWeight: 'bold'
}
}).mount('#app')
</script>
</body>
</html>
});