使用在React Native中开发一个鸿蒙跨平台时间戳转换工具,针对鸿蒙项目中如何进行时间的换算,可以使用JavaScript的`Date`对象使用毫秒作为时间单位
本文介绍了在React Native中开发时间戳转换工具的完整流程。首先通过npx react-native init创建项目,然后使用JavaScript的Date对象进行时间戳转换。文章详细说明了组件的创建过程,包括状态管理、输入处理和日期格式转换。同时提供了UI优化建议,如使用react-native-paper库改善界面。最后介绍了在App中集成组件的方法和运行应用的命令。特别说明了对Ha
在React Native中开发一个时间戳转换工具涉及到几个步骤。首先,你需要创建一个React Native项目,然后编写一个React组件用于显示和转换时间戳。时间戳通常指的是从1970年1月1日(UTC)至现在的秒数。你可以使用JavaScript的Date对象来进行转换。
步骤 1: 创建React Native项目
如果你还没有创建React Native项目,你可以使用以下命令来创建一个:
npx react-native init TimeStampConverter
步骤 2: 安装依赖
对于基本的项目,我们不需要安装额外的依赖,但如果你需要使用一些UI库来改善用户体验,你可以安装例如react-native-paper或native-base。例如,安装react-native-paper:
npm install react-native-paper
步骤 3: 编写时间戳转换组件
在React Native项目中,你可以创建一个新的组件来处理时间戳的转换。例如,创建一个TimeStampConverter.js文件:
import React, { useState } from 'react';
import { View, Text, TextInput, Button } from 'react-native';
import { TextInput as Input } from 'react-native-paper'; // 使用react-native-paper的TextInput组件
const TimeStampConverter = () => {
const [timestamp, setTimestamp] = useState('');
const [convertedDate, setConvertedDate] = useState('');
const handleConvert = () => {
const date = new Date(parseInt(timestamp) * 1000); // 将时间戳转换为毫秒
setConvertedDate(date.toString()); // 将日期转换为可读的字符串格式
};
return (
<View style={{ padding: 20 }}>
<Input
label="Enter Timestamp"
value={timestamp}
onChangeText={setTimestamp}
keyboardType="numeric"
/>
<Button title="Convert" onPress={handleConvert} />
<Text>Converted Date: {convertedDate}</Text>
</View>
);
};
export default TimeStampConverter;
步骤 4: 在App中使用该组件
确保在你的App组件中引入并使用这个TimeStampConverter组件。例如,在App.js中:
import React from 'react';
import { View } from 'react-native';
import TimeStampConverter from './TimeStampConverter'; // 确保路径正确
const App = () => {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<TimeStampConverter />
</View>
);
};
export default App;
步骤 5: 运行你的应用
使用以下命令来运行你的应用:
npx react-native run-Harmony 对于Harmony设备或模拟器
npx react-native run-Harmony 对于Harmony设备或模拟器
或者如果你使用的是Expo,可以直接在Expo Go应用中预览你的应用:
npx expo start 对于Expo项目
然后扫描二维码或在Expo Go中打开项目。
注意事项:
- 确保你的时间戳是以秒为单位的整数。在JavaScript中,我们通常需要将秒乘以1000来转换为毫秒,因为JavaScript的
Date对象使用毫秒作为时间单位。 - 如果要适配鸿蒙系统(HarmonyOS),确保你的应用兼容HarmonyOS的开发环境和API。HarmonyOS是基于Harmony的,大部分的React Native代码应该是兼容的,但你可能需要测试和调整一些UI细节和性能优化。你可以使用华为的HarmonyOS SDK进行适配测试。通常,华为会提供一些特定的API或者工具来帮助开发者适配HarmonyOS环境。你可以查看华为开发者官网上的相关文档和指南。
真实场景案例演示环境:
import React, { useState } from 'react';
import { View, Text, Sty
leSheet, TouchableOpacity, ScrollView, Dimensions, Image, TextInput } from 'react-native';
// Simple Icon Component using Unicode symbols
interface IconProps {
name: string;
size?: number;
color?: string;
style?: object;
}
const Icon: React.FC<IconProps> = ({
name,
size = 24,
color = '#333333',
style
}) => {
const getIconSymbol = () => {
switch (name) {
case 'layout': return '⚏';
case 'grid': return '⊞';
case 'flex': return '⥊';
case 'align': return '⇳';
case 'justify': return '⇔';
default: return '●';
}
};
return (
<View style={[{ width: size, height: size, justifyContent: 'center', alignItems: 'center' }, style]}>
<Text style={{ fontSize: size * 0.8, color, includeFontPadding: false, textAlign: 'center' }}>
{getIconSymbol()}
</Text>
</View>
);
};
// Row Component
interface RowProps {
children: React.ReactNode;
gutter?: number;
align?: 'flex-start' | 'center' | 'flex-end' | 'stretch';
justify?: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around';
style?: object;
}
const Row: React.FC<RowProps> = ({
children,
gutter = 0,
align = 'flex-start',
justify = 'flex-start',
style
}) => {
return (
<View
style={[
styles.row,
{
marginHorizontal: -gutter / 2,
alignItems: align,
justifyContent: justify,
},
style
]}
>
{React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, {
style: [
child.props.style,
{
marginHorizontal: gutter / 2
}
]
});
}
return child;
})}
</View>
);
};
// Col Component
interface ColProps {
children: React.ReactNode;
span?: number;
offset?: number;
style?: object;
}
const Col: React.FC<ColProps> = ({
children,
span = 24,
offset = 0,
style
}) => {
const percentage = (span / 24) * 100;
const offsetPercentage = (offset / 24) * 100;
return (
<View
style={[
styles.col,
{
width: `${percentage}%`,
marginLeft: `${offsetPercentage}%`,
},
style
]}
>
{children}
</View>
);
};
// Layout Example Data
const layoutExamples = [
{ id: 1, title: '基础栅格', description: '24栅格系统' },
{ id: 2, title: '间隔布局', description: '带间距的Row' },
{ id: 3, title: '对齐方式', description: '垂直居中对齐' },
{ id: 4, title: '偏移布局', description: '列偏移效果' },
];
// Layout Showcase Component
const LayoutExample: React.FC<{
title: string;
description: string;
children: React.ReactNode;
}> = ({ title, description, children }) => {
return (
<View style={styles.exampleContainer}>
<Text style={styles.exampleTitle}>{title}</Text>
<Text style={styles.exampleDescription}>{description}</Text>
<View style={styles.exampleContent}>
{children}
</View>
</View>
);
};
const QRCodeLite: React.FC = () => {
const [text, setText] = useState<string>('https://example.com');
const [matrix, setMatrix] = useState<number[][]>([]);
const [moduleSize, setModuleSize] = useState<number>(8);
const [invert, setInvert] = useState<boolean>(false);
const [margin, setMargin] = useState<number>(8);
const hashSeed = (s: string) => {
let h = 2166136261;
for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); }
return h >>> 0;
};
const genMatrix = () => {
const dim = 29;
const m: number[][] = Array.from({ length: dim }, () => Array(dim).fill(0));
const markFinder = (x: number, y: number) => {
for (let i = -1; i <= 7; i++) {
for (let j = -1; j <= 7; j++) {
const xx = x + i, yy = y + j;
if (xx < 0 || yy < 0 || xx >= dim || yy >= dim) continue;
const inOuter = i >= 0 && i <= 6 && j >= 0 && j <= 6;
const inInner = i >= 2 && i <= 4 && j >= 2 && j <= 4;
m[yy][xx] = inOuter ? 1 : m[yy][xx];
m[yy][xx] = inInner ? 0 : m[yy][xx];
if (i === 3 && j === 3) m[yy][xx] = 1;
}
}
};
markFinder(0, 0);
markFinder(dim - 7, 0);
markFinder(0, dim - 7);
let seed = hashSeed(text);
const rnd = () => { seed ^= seed << 13; seed ^= seed >>> 17; seed ^= seed << 5; return (seed >>> 0) / 0xffffffff; };
for (let y = 0; y < dim; y++) {
for (let x = 0; x < dim; x++) {
const inFinder = (x < 7 && y < 7) || (x >= dim - 7 && y < 7) || (x < 7 && y >= dim - 7);
if (inFinder) continue;
const v = rnd();
m[y][x] = v > 0.5 ? 1 : 0;
}
}
setMatrix(m);
};
const bgLight = invert ? '#111827' : '#ffffff';
const fgDark = invert ? '#ffffff' : '#111827';
return (
<View style={styles.jqrCard}>
<View style={styles.jqrHeader}>
<Text style={styles.jqrTitle}>二维码生成器 · 珊瑚粉轻奢风</Text>
<Text style={styles.jqrSubtitle}>输入文本,生成二维码样式图(简洁版)</Text>
</View>
<View style={styles.jqrControls}>
<TouchableOpacity style={styles.jqrBtn} onPress={genMatrix} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_QR.generate }} style={styles.jqrIcon} />
<Text style={styles.jqrBtnText}>生成</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.jqrBtn} onPress={() => { setText(''); setMatrix([]); }} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_QR.clear }} style={styles.jqrIcon} />
<Text style={styles.jqrBtnText}>清空</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.jqrBtn} onPress={() => setModuleSize(s => Math.min(14, s + 1))} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_QR.sizeUp }} style={styles.jqrIcon} />
<Text style={styles.jqrBtnText}>变大</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.jqrBtn} onPress={() => setModuleSize(s => Math.max(6, s - 1))} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_QR.sizeDown }} style={styles.jqrIcon} />
<Text style={styles.jqrBtnText}>变小</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.jqrBtn} onPress={() => setInvert(v => !v)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_QR.invert }} style={styles.jqrIcon} />
<Text style={styles.jqrBtnText}>{invert ? '深色' : '浅色'}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.jqrBtn} onPress={() => setMargin(m => Math.min(24, m + 2))} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_QR.margin }} style={styles.jqrIcon} />
<Text style={styles.jqrBtnText}>边距+</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.jqrBtn} onPress={() => setMargin(m => Math.max(4, m - 2))} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_QR.margin }} style={styles.jqrIcon} />
<Text style={styles.jqrBtnText}>边距-</Text>
</TouchableOpacity>
</View>
<View style={styles.jqrEditors}>
<View style={styles.jqrPane}>
<View style={styles.jqrLabelRow}>
<Text style={styles.jqrLabel}>输入文本</Text>
</View>
<TextInput value={text} onChangeText={setText} style={styles.jqrTextInput} placeholder="在此输入文本或URL" placeholderTextColor="#9CA3AF" multiline />
</View>
<View style={styles.jqrPane}>
<View style={styles.jqrLabelRow}>
<Text style={styles.jqrLabel}>生成结果</Text>
</View>
<View style={[styles.jqrCanvas, { padding: margin, backgroundColor: bgLight }] }>
{matrix.length ? (
<View style={{ width: moduleSize * matrix.length, height: moduleSize * matrix.length, backgroundColor: bgLight }}>
{matrix.map((row, y) => (
<View key={`r-${y}`} style={{ flexDirection: 'row' }}>
{row.map((cell, x) => (
<View key={`c-${y}-${x}`} style={[styles.jqrModule, { width: moduleSize, height: moduleSize, backgroundColor: cell ? fgDark : bgLight }]} />
))}
</View>
))}
</View>
) : (
<Text style={styles.jqrHint}>点击“生成”以创建二维码样式图</Text>
)}
</View>
</View>
</View>
</View>
);
};
const TimestampConverterLite: React.FC = () => {
const [input, setInput] = useState<string>('1700000000');
const [modeTsToDate, setModeTsToDate] = useState<boolean>(true);
const [useMs, setUseMs] = useState<boolean>(false);
const [useUTC, setUseUTC] = useState<boolean>(false);
const [output, setOutput] = useState<string>('');
const [error, setError] = useState<string>('');
const pad = (n: number) => (n < 10 ? '0' + n : '' + n);
const format = (d: Date, utc: boolean) => {
const y = utc ? d.getUTCFullYear() : d.getFullYear();
const m = utc ? d.getUTCMonth() + 1 : d.getMonth() + 1;
const da = utc ? d.getUTCDate() : d.getDate();
const h = utc ? d.getUTCHours() : d.getHours();
const mi = utc ? d.getUTCMinutes() : d.getMinutes();
const s = utc ? d.getUTCSeconds() : d.getSeconds();
const w = utc ? d.getUTCDay() : d.getDay();
const week = ['日','一','二','三','四','五','六'][w];
const iso = utc ? d.toISOString() : new Date(d.getTime() - d.getTimezoneOffset()*60000).toISOString().replace('Z','');
return {
text: `${y}-${pad(m)}-${pad(da)} ${pad(h)}:${pad(mi)}:${pad(s)} (周${week})`,
iso,
};
};
const doConvert = () => {
try {
if (modeTsToDate) {
const num = Number(input);
if (!isFinite(num)) throw new Error('输入需为数字');
const ms = useMs ? num : num * 1000;
const d = new Date(ms);
if (isNaN(d.getTime())) throw new Error('非法时间戳');
const f = format(d, useUTC);
const sec = Math.floor(d.getTime() / 1000);
const out = `格式化:${f.text}\nISO:${f.iso}\n毫秒:${d.getTime()}\n秒:${sec}`;
setOutput(out);
} else {
const d = new Date(input);
if (isNaN(d.getTime())) throw new Error('无法解析日期文本');
const f = format(d, useUTC);
const ms = d.getTime();
const sec = Math.floor(ms / 1000);
const out = `格式化:${f.text}\nISO:${f.iso}\n毫秒:${ms}\n秒:${sec}`;
setOutput(out);
}
setError('');
} catch (e: any) {
setError(`转换错误:${e?.message || '未知错误'}`);
}
};
const doClear = () => { setInput(''); setOutput(''); setError(''); };
const setNow = () => {
const now = Date.now();
if (modeTsToDate) setInput(useMs ? String(now) : String(Math.floor(now/1000)));
else setInput(new Date(now).toISOString());
};
return (
<View style={styles.jtsCard}>
<View style={styles.jtsHeader}>
<Text style={styles.jtsTitle}>Timestamp Converter · 琥珀极简风</Text>
<Text style={styles.jtsSubtitle}>时间戳与日期互转,支持单位与时区</Text>
</View>
<View style={styles.jtsControls}>
<TouchableOpacity style={styles.jtsBtn} onPress={doConvert} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TS.convert }} style={styles.jtsIcon} />
<Text style={styles.jtsBtnText}>转换</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.jtsBtn} onPress={setNow} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TS.now }} style={styles.jtsIcon} />
<Text style={styles.jtsBtnText}>当前</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.jtsBtn} onPress={doClear} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TS.clear }} style={styles.jtsIcon} />
<Text style={styles.jtsBtnText}>清空</Text>
</TouchableOpacity>
<View style={styles.jtsChipRow}>
<TouchableOpacity style={[styles.jtsChip, useMs ? styles.jtsBtnActive : null]} onPress={() => setUseMs(true)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TS.unit }} style={styles.jtsIcon} />
<Text style={styles.jtsChipText}>毫秒</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.jtsChip, !useMs ? styles.jtsBtnActive : null]} onPress={() => setUseMs(false)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TS.unit }} style={styles.jtsIcon} />
<Text style={styles.jtsChipText}>秒</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.jtsChip, useUTC ? styles.jtsBtnActive : null]} onPress={() => setUseUTC(true)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TS.tz }} style={styles.jtsIcon} />
<Text style={styles.jtsChipText}>UTC</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.jtsChip, !useUTC ? styles.jtsBtnActive : null]} onPress={() => setUseUTC(false)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TS.tz }} style={styles.jtsIcon} />
<Text style={styles.jtsChipText}>本地</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.jtsChip, modeTsToDate ? styles.jtsBtnActive : null]} onPress={() => setModeTsToDate(true)} activeOpacity={0.85}>
<Text style={styles.jtsChipText}>时间戳→日期</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.jtsChip, !modeTsToDate ? styles.jtsBtnActive : null]} onPress={() => setModeTsToDate(false)} activeOpacity={0.85}>
<Text style={styles.jtsChipText}>日期→时间戳</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.jtsEditors}>
<View style={styles.jtsPane}>
<View style={styles.jtsLabelRow}><Text style={styles.jtsLabel}>输入</Text></View>
<TextInput value={input} onChangeText={setInput} style={styles.jtsTextInput} placeholder="输入时间戳或日期文本" placeholderTextColor="#A16207" multiline />
{error ? <Text style={styles.jtsErrorText}>{error}</Text> : null}
</View>
<View style={styles.jtsPane}>
<View style={styles.jtsLabelRow}><Text style={styles.jtsLabel}>输出</Text></View>
<Text style={styles.jtsOutputText}>{output}</Text>
</View>
</View>
</View>
);
};
// Main App Component
const LayoutComponentApp = () => {
const [selectedExample, setSelectedExample] = useState<number>(1);
return (
<ScrollView style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>栅格布局组件</Text>
<Text style={styles.headerSubtitle}>基于Flexbox的响应式布局系统</Text>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>组件介绍</Text>
<View style={styles.componentsInfo}>
<View style={styles.componentCard}>
<View style={styles.iconWrapper}>
<Icon name="layout" size={32} color="#4361ee" />
</View>
<Text style={styles.componentName}>Row</Text>
<Text style={styles.componentDesc}>水平排列容器</Text>
</View>
<View style={styles.componentCard}>
<View style={styles.iconWrapper}>
<Icon name="grid" size={32} color="#4cc9f0" />
</View>
<Text style={styles.componentName}>Col</Text>
<Text style={styles.componentDesc}>栅格列容器</Text>
</View>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>布局示例</Text>
<View style={styles.examplesContainer}>
{layoutExamples.map((example) => (
<TouchableOpacity
key={example.id}
style={[
styles.exampleTab,
selectedExample === example.id && styles.selectedExampleTab
]}
onPress={() => setSelectedExample(example.id)}
>
<Text style={[
styles.exampleTabText,
selectedExample === example.id && styles.selectedExampleTabText
]}>
{example.title}
</Text>
</TouchableOpacity>
))}
</View>
{selectedExample === 1 && (
<LayoutExample
title="基础栅格"
description="使用24栅格系统的布局"
>
<Row gutter={10}>
<Col span={8}>
<View style={[styles.demoBox, { backgroundColor: '#4361ee' }]}>
<Text style={styles.boxText}>span=8</Text>
</View>
</Col>
<Col span={8}>
<View style={[styles.demoBox, { backgroundColor: '#3a0ca3' }]}>
<Text style={styles.boxText}>span=8</Text>
</View>
</Col>
<Col span={8}>
<View style={[styles.demoBox, { backgroundColor: '#7209b7' }]}>
<Text style={styles.boxText}>span=8</Text>
</View>
</Col>
</Row>
<Row gutter={10} style={{ marginTop: 10 }}>
<Col span={12}>
<View style={[styles.demoBox, { backgroundColor: '#f72585' }]}>
<Text style={styles.boxText}>span=12</Text>
</View>
</Col>
<Col span={12}>
<View style={[styles.demoBox, { backgroundColor: '#b5179e' }]}>
<Text style={styles.boxText}>span=12</Text>
</View>
</Col>
</Row>
</LayoutExample>
)}
{selectedExample === 2 && (
<LayoutExample
title="间隔布局"
description="带有间距的栅格布局"
>
<Row gutter={15}>
<Col span={6}>
<View style={[styles.demoBox, { backgroundColor: '#4cc9f0' }]}>
<Text style={styles.boxText}>gutter=15</Text>
</View>
</Col>
<Col span={6}>
<View style={[styles.demoBox, { backgroundColor: '#4895ef' }]}>
<Text style={styles.boxText}>gutter=15</Text>
</View>
</Col>
<Col span={6}>
<View style={[styles.demoBox, { backgroundColor: '#4361ee' }]}>
<Text style={styles.boxText}>gutter=15</Text>
</View>
</Col>
<Col span={6}>
<View style={[styles.demoBox, { backgroundColor: '#3a0ca3' }]}>
<Text style={styles.boxText}>gutter=15</Text>
</View>
</Col>
</Row>
</LayoutExample>
)}
{selectedExample === 3 && (
<LayoutExample
title="对齐方式"
description="不同的对齐选项"
>
<Row gutter={10} align="center">
<Col span={6}>
<View style={[styles.demoBox, { backgroundColor: '#f72585', height: 60 }]}>
<Text style={styles.boxText}>高60</Text>
</View>
</Col>
<Col span={6}>
<View style={[styles.demoBox, { backgroundColor: '#b5179e', height: 80 }]}>
<Text style={styles.boxText}>高80</Text>
</View>
</Col>
<Col span={6}>
<View style={[styles.demoBox, { backgroundColor: '#7209b7', height: 100 }]}>
<Text style={styles.boxText}>高100</Text>
</View>
</Col>
<Col span={6}>
<View style={[styles.demoBox, { backgroundColor: '#560bad', height: 70 }]}>
<Text style={styles.boxText}>高70</Text>
</View>
</Col>
</Row>
</LayoutExample>
)}
{selectedExample === 4 && (
<LayoutExample
title="偏移布局"
description="列偏移效果"
>
<Row gutter={10}>
<Col span={6}>
<View style={[styles.demoBox, { backgroundColor: '#4cc9f0' }]}>
<Text style={styles.boxText}>span=6</Text>
</View>
</Col>
<Col span={6} offset={6}>
<View style={[styles.demoBox, { backgroundColor: '#4895ef' }]}>
<Text style={styles.boxText}>span=6 offset=6</Text>
</View>
</Col>
</Row>
<Row gutter={10} style={{ marginTop: 10 }}>
<Col span={8} offset={4}>
<View style={[styles.demoBox, { backgroundColor: '#4361ee' }]}>
<Text style={styles.boxText}>span=8 offset=4</Text>
</View>
</Col>
<Col span={8} offset={4}>
<View style={[styles.demoBox, { backgroundColor: '#3a0ca3' }]}>
<Text style={styles.boxText}>span=8 offset=4</Text>
</View>
</Col>
</Row>
</LayoutExample>
)}
</View>
<View style={styles.usageSection}>
<Text style={styles.sectionTitle}>使用方法</Text>
<View style={styles.codeBlock}>
<Text style={styles.codeText}>{'<Row gutter={10}>'}</Text>
<Text style={styles.codeText}> {'<Col span={12}>内容</Col>'}</Text>
<Text style={styles.codeText}> {'<Col span={12}>内容</Col>'}</Text>
<Text style={styles.codeText}>{'</Row>'}</Text>
</View>
<Text style={styles.description}>
Row组件用于创建水平布局容器,Col组件用于创建栅格列。通过gutter属性设置间距,
通过span属性设置列宽度(总共24份),通过offset属性设置列偏移。
</Text>
</View>
<View style={styles.featuresSection}>
<Text style={styles.sectionTitle}>功能特性</Text>
<View style={styles.featuresList}>
<View style={styles.featureItem}>
<Icon name="flex" size={20} color="#4361ee" style={styles.featureIcon} />
<Text style={styles.featureText}>基于Flexbox布局</Text>
</View>
<View style={styles.featureItem}>
<Icon name="align" size={20} color="#4cc9f0" style={styles.featureIcon} />
<Text style={styles.featureText}>灵活的对齐选项</Text>
</View>
<View style={styles.featureItem}>
<Icon name="justify" size={20} color="#f72585" style={styles.featureIcon} />
<Text style={styles.featureText}>多种排列方式</Text>
</View>
<View style={styles.featureItem}>
<Icon name="grid" size={20} color="#7209b7" style={styles.featureIcon} />
<Text style={styles.featureText}>24栅格系统</Text>
</View>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>二维码生成器(珊瑚粉轻奢风)</Text>
<View style={styles.jqrContainer}>
<QRCodeLite />
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>时间戳转换工具(琥珀极简风)</Text>
<View style={styles.jtsContainer}>
<TimestampConverterLite />
</View>
</View>
<View style={styles.footer}>
<Text style={styles.footerText}>© 2023 栅格布局组件 | 响应式布局解决方案</Text>
</View>
</ScrollView>
);
};
const { width } = Dimensions.get('window');
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8f9fa',
},
header: {
backgroundColor: '#ffffff',
paddingVertical: 30,
paddingHorizontal: 20,
marginBottom: 10,
borderBottomWidth: 1,
borderBottomColor: '#e9ecef',
},
headerTitle: {
fontSize: 28,
fontWeight: '700',
color: '#212529',
textAlign: 'center',
marginBottom: 5,
},
headerSubtitle: {
fontSize: 16,
color: '#6c757d',
textAlign: 'center',
},
section: {
marginBottom: 20,
},
sectionTitle: {
fontSize: 20,
fontWeight: '700',
color: '#212529',
paddingHorizontal: 20,
paddingBottom: 15,
},
componentsInfo: {
flexDirection: 'row',
justifyContent: 'space-around',
paddingHorizontal: 15,
},
componentCard: {
backgroundColor: '#ffffff',
borderRadius: 15,
padding: 20,
width: (width - 60) / 2,
alignItems: 'center',
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
},
iconWrapper: {
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: '#f1f3f5',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 15,
},
componentName: {
fontSize: 18,
fontWeight: '700',
color: '#212529',
marginBottom: 5,
},
componentDesc: {
fontSize: 14,
color: '#6c757d',
textAlign: 'center',
},
examplesContainer: {
flexDirection: 'row',
paddingHorizontal: 15,
marginBottom: 15,
},
exampleTab: {
flex: 1,
paddingVertical: 12,
backgroundColor: '#e9ecef',
marginHorizontal: 5,
borderRadius: 10,
alignItems: 'center',
},
selectedExampleTab: {
backgroundColor: '#4361ee',
},
exampleTabText: {
fontSize: 14,
fontWeight: '600',
color: '#495057',
},
selectedExampleTabText: {
color: '#ffffff',
},
exampleContainer: {
backgroundColor: '#ffffff',
marginHorizontal: 15,
borderRadius: 15,
padding: 20,
marginBottom: 20,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
},
exampleTitle: {
fontSize: 18,
fontWeight: '700',
color: '#212529',
marginBottom: 5,
},
exampleDescription: {
fontSize: 14,
color: '#6c757d',
marginBottom: 15,
},
exampleContent: {
padding: 10,
},
row: {
flexDirection: 'row',
flexWrap: 'wrap',
},
col: {
flexDirection: 'column',
},
demoBox: {
padding: 15,
borderRadius: 8,
minHeight: 50,
justifyContent: 'center',
alignItems: 'center',
},
boxText: {
color: '#ffffff',
fontWeight: '600',
textAlign: 'center',
},
usageSection: {
backgroundColor: '#ffffff',
marginHorizontal: 15,
borderRadius: 15,
padding: 20,
marginBottom: 20,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
},
codeBlock: {
backgroundColor: '#2b3541',
borderRadius: 8,
padding: 15,
marginBottom: 15,
},
codeText: {
fontFamily: 'monospace',
color: '#e9ecef',
fontSize: 14,
lineHeight: 22,
},
description: {
fontSize: 15,
color: '#495057',
lineHeight: 22,
},
featuresSection: {
backgroundColor: '#ffffff',
marginHorizontal: 15,
borderRadius: 15,
padding: 20,
marginBottom: 20,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
},
featuresList: {
paddingLeft: 10,
},
featureItem: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 15,
},
featureIcon: {
marginRight: 15,
},
featureText: {
fontSize: 16,
color: '#212529',
},
footer: {
paddingVertical: 20,
alignItems: 'center',
},
footerText: {
color: '#6c757d',
fontSize: 14,
},
jtsContainer: {
marginHorizontal: 15,
},
jtsCard: {
backgroundColor: '#FFFBEB',
borderRadius: 14,
borderWidth: 1,
borderColor: '#FDE68A',
shadowColor: '#F59E0B',
shadowOffset: { width: 0, height: 5 },
shadowOpacity: 0.15,
shadowRadius: 12,
paddingVertical: 12,
paddingHorizontal: 12,
},
jtsHeader: { marginBottom: 8 },
jtsTitle: { fontSize: 18, fontWeight: '700', color: '#92400E' },
jtsSubtitle: { fontSize: 13, color: '#B45309', marginTop: 4 },
jtsControls: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', marginTop: 8, marginBottom: 12 },
jtsBtn: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#FEF3C7', borderWidth: 1, borderColor: '#FDE68A', borderRadius: 12, paddingVertical: 8, paddingHorizontal: 10, marginRight: 8 },
jtsBtnActive: { backgroundColor: '#FDE68A', borderColor: '#F59E0B' },
jtsIcon: { width: 20, height: 20, marginRight: 6 },
jtsBtnText: { color: '#92400E', fontSize: 13, fontWeight: '600' },
jtsEditors: { flexDirection: 'row', gap: 10 },
jtsPane: { flex: 1, backgroundColor: '#FFFFFF', borderWidth: 1, borderColor: '#E5E7EB', borderRadius: 12, padding: 10, minHeight: 220 },
jtsLabelRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 },
jtsLabel: { color: '#92400E', fontSize: 12, fontWeight: '700' },
jtsTextInput: { color: '#111827', fontSize: 13, lineHeight: 18, minHeight: 120 },
jtsOutputText: { color: '#111827', fontSize: 13, lineHeight: 18, minHeight: 120 },
jtsErrorText: { color: '#B91C1C', fontSize: 12, marginTop: 8 },
jtsChipRow: { flexDirection: 'row', alignItems: 'center' },
jtsChip: { backgroundColor: '#FFFBEB', borderWidth: 1, borderColor: '#FDE68A', borderRadius: 12, paddingVertical: 6, paddingHorizontal: 10, marginRight: 8, flexDirection: 'row', alignItems: 'center' },
jtsChipText: { color: '#92400E', fontSize: 12, fontWeight: '700' },
jqrContainer: {
marginHorizontal: 15,
},
jqrCard: {
backgroundColor: '#FFF5F5',
borderRadius: 14,
borderWidth: 1,
borderColor: '#FED7D7',
shadowColor: '#F43F5E',
shadowOffset: { width: 0, height: 5 },
shadowOpacity: 0.15,
shadowRadius: 12,
paddingVertical: 12,
paddingHorizontal: 12,
},
jqrHeader: { marginBottom: 8 },
jqrTitle: { fontSize: 18, fontWeight: '700', color: '#9F1239' },
jqrSubtitle: { fontSize: 13, color: '#E11D48', marginTop: 4 },
jqrControls: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', marginTop: 8, marginBottom: 12 },
jqrBtn: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#FEE2E2', borderWidth: 1, borderColor: '#FECACA', borderRadius: 12, paddingVertical: 8, paddingHorizontal: 10, marginRight: 8 },
jqrIcon: { width: 20, height: 20, marginRight: 6 },
jqrBtnText: { color: '#9F1239', fontSize: 13, fontWeight: '600' },
jqrEditors: { flexDirection: 'row', gap: 10 },
jqrPane: { flex: 1, backgroundColor: '#FFFFFF', borderWidth: 1, borderColor: '#FECACA', borderRadius: 12, padding: 10, minHeight: 220 },
jqrLabelRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 },
jqrLabel: { color: '#9F1239', fontSize: 12, fontWeight: '700' },
jqrTextInput: { color: '#111827', fontSize: 13, lineHeight: 18, minHeight: 160 },
jqrCanvas: { alignItems: 'center', justifyContent: 'center', borderRadius: 10 },
jqrModule: { borderRadius: 1 },
jqrHint: { color: '#E11D48', fontSize: 12 },
});
const ICON_BASE64_TC = {
convert: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABJUlEQVR4nO2Uu0oDQRRGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
now: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABK0lEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
clear: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABN0lEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
unit: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABMklEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
tz: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABMklEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
swap: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABK0lEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
};
const tsStyles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#F8FAFC' },
header: { backgroundColor: '#FFFFFF', paddingVertical: 24, paddingHorizontal: 20, borderBottomWidth: 1, borderBottomColor: '#E2E8F0' },
headerTitle: { fontSize: 24, fontWeight: '700', color: '#0F172A', textAlign: 'center' },
headerSubtitle: { fontSize: 14, color: '#475569', textAlign: 'center', marginTop: 6 },
section: { marginTop: 12 },
tcContainer: { marginHorizontal: 15 },
tcCard: { backgroundColor: '#E0F2FE', borderRadius: 16, borderWidth: 1, borderColor: '#93C5FD', padding: 12, shadowColor: '#0EA5E9', shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.15, shadowRadius: 12 },
tcHeader: { marginBottom: 8 },
tcTitle: { fontSize: 18, fontWeight: '700', color: '#0C4A6E' },
tcSubtitle: { fontSize: 13, color: '#0891B2', marginTop: 4 },
tcControls: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', marginTop: 8, marginBottom: 12 },
tcBtn: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#BFDBFE', borderWidth: 1, borderColor: '#93C5FD', borderRadius: 12, paddingVertical: 8, paddingHorizontal: 10, marginRight: 8 },
tcBtnActive: { backgroundColor: '#93C5FD', borderColor: '#60A5FA' },
tcIcon: { width: 20, height: 20, marginRight: 6 },
tcBtnText: { color: '#0C4A6E', fontSize: 13, fontWeight: '600' },
tcEditors: { flexDirection: 'row', gap: 10 },
tcPane: { flex: 1, backgroundColor: '#FFFFFF', borderWidth: 1, borderColor: '#E2E8F0', borderRadius: 12, padding: 10, minHeight: 220 },
tcLabelRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 },
tcLabel: { color: '#0C4A6E', fontSize: 12, fontWeight: '700' },
tcTextInput: { color: '#0F172A', fontSize: 13, lineHeight: 18, minHeight: 120 },
tcOutputText: { color: '#0F172A', fontSize: 13, lineHeight: 18, minHeight: 120 },
tcErrorText: { color: '#DC2626', fontSize: 12, marginTop: 8 },
tcChipRow: { flexDirection: 'row', alignItems: 'center' },
tcChip: { backgroundColor: '#E0F2FE', borderWidth: 1, borderColor: '#93C5FD', borderRadius: 12, paddingVertical: 6, paddingHorizontal: 10, marginRight: 8, flexDirection: 'row', alignItems: 'center' },
tcChipText: { color: '#0C4A6E', fontSize: 12, fontWeight: '700' },
});
const TimestampConverterGlass: React.FC = () => {
const [input, setInput] = useState<string>('1700000000');
const [modeTsToDate, setModeTsToDate] = useState<boolean>(true);
const [useMs, setUseMs] = useState<boolean>(false);
const [useUTC, setUseUTC] = useState<boolean>(false);
const [output, setOutput] = useState<string>('');
const [error, setError] = useState<string>('');
const pad = (n: number) => (n < 10 ? '0' + n : '' + n);
const format = (d: Date, utc: boolean) => {
const y = utc ? d.getUTCFullYear() : d.getFullYear();
const m = utc ? d.getUTCMonth() + 1 : d.getMonth() + 1;
const da = utc ? d.getUTCDate() : d.getDate();
const h = utc ? d.getUTCHours() : d.getHours();
const mi = utc ? d.getUTCMinutes() : d.getMinutes();
const s = utc ? d.getUTCSeconds() : d.getSeconds();
const w = utc ? d.getUTCDay() : d.getDay();
const week = ['日','一','二','三','四','五','六'][w];
const iso = utc ? d.toISOString() : new Date(d.getTime() - d.getTimezoneOffset()*60000).toISOString().replace('Z','');
return { text: `${y}-${pad(m)}-${pad(da)} ${pad(h)}:${pad(mi)}:${pad(s)} (周${week})`, iso };
};
const doConvert = () => {
try {
if (modeTsToDate) {
const num = Number(input);
if (!isFinite(num)) throw new Error('输入需为数字');
const ms = useMs ? num : num * 1000;
const d = new Date(ms);
if (isNaN(d.getTime())) throw new Error('非法时间戳');
const f = format(d, useUTC);
const sec = Math.floor(d.getTime() / 1000);
const out = `格式化:${f.text}\nISO:${f.iso}\n毫秒:${d.getTime()}\n秒:${sec}`;
setOutput(out);
} else {
const d = new Date(input);
if (isNaN(d.getTime())) throw new Error('无法解析日期文本');
const f = format(d, useUTC);
const ms = d.getTime();
const sec = Math.floor(ms / 1000);
const out = `格式化:${f.text}\nISO:${f.iso}\n毫秒:${ms}\n秒:${sec}`;
setOutput(out);
}
setError('');
} catch (e: any) {
setError(`转换错误:${e?.message || '未知错误'}`);
}
};
const doClear = () => { setInput(''); setOutput(''); setError(''); };
const setNow = () => {
const now = Date.now();
if (modeTsToDate) setInput(useMs ? String(now) : String(Math.floor(now/1000)));
else setInput(new Date(now).toISOString());
};
return (
<View style={tsStyles.tcCard}>
<View style={tsStyles.tcHeader}>
<Text style={tsStyles.tcTitle}>Timestamp Converter · 钴蓝玻璃风</Text>
<Text style={tsStyles.tcSubtitle}>时间戳与日期互转,支持单位与时区</Text>
</View>
<View style={tsStyles.tcControls}>
<TouchableOpacity style={tsStyles.tcBtn} onPress={doConvert} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TC.convert }} style={tsStyles.tcIcon} />
<Text style={tsStyles.tcBtnText}>转换</Text>
</TouchableOpacity>
<TouchableOpacity style={tsStyles.tcBtn} onPress={setNow} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TC.now }} style={tsStyles.tcIcon} />
<Text style={tsStyles.tcBtnText}>当前</Text>
</TouchableOpacity>
<TouchableOpacity style={tsStyles.tcBtn} onPress={doClear} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TC.clear }} style={tsStyles.tcIcon} />
<Text style={tsStyles.tcBtnText}>清空</Text>
</TouchableOpacity>
<View style={tsStyles.tcChipRow}>
<TouchableOpacity style={[tsStyles.tcChip, useMs ? tsStyles.tcBtnActive : null]} onPress={() => setUseMs(true)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TC.unit }} style={tsStyles.tcIcon} />
<Text style={tsStyles.tcChipText}>毫秒</Text>
</TouchableOpacity>
<TouchableOpacity style={[tsStyles.tcChip, !useMs ? tsStyles.tcBtnActive : null]} onPress={() => setUseMs(false)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TC.unit }} style={tsStyles.tcIcon} />
<Text style={tsStyles.tcChipText}>秒</Text>
</TouchableOpacity>
<TouchableOpacity style={[tsStyles.tcChip, useUTC ? tsStyles.tcBtnActive : null]} onPress={() => setUseUTC(true)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TC.tz }} style={tsStyles.tcIcon} />
<Text style={tsStyles.tcChipText}>UTC</Text>
</TouchableOpacity>
<TouchableOpacity style={[tsStyles.tcChip, !useUTC ? tsStyles.tcBtnActive : null]} onPress={() => setUseUTC(false)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TC.tz }} style={tsStyles.tcIcon} />
<Text style={tsStyles.tcChipText}>本地</Text>
</TouchableOpacity>
<TouchableOpacity style={[tsStyles.tcChip, modeTsToDate ? tsStyles.tcBtnActive : null]} onPress={() => setModeTsToDate(true)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TC.swap }} style={tsStyles.tcIcon} />
<Text style={tsStyles.tcChipText}>时间戳→日期</Text>
</TouchableOpacity>
<TouchableOpacity style={[tsStyles.tcChip, !modeTsToDate ? tsStyles.tcBtnActive : null]} onPress={() => setModeTsToDate(false)} activeOpacity={0.85}>
<Image source={{ uri: ICON_BASE64_TC.swap }} style={tsStyles.tcIcon} />
<Text style={tsStyles.tcChipText}>日期→时间戳</Text>
</TouchableOpacity>
</View>
</View>
<View style={tsStyles.tcEditors}>
<View style={tsStyles.tcPane}>
<View style={tsStyles.tcLabelRow}><Text style={tsStyles.tcLabel}>输入</Text></View>
<TextInput value={input} onChangeText={setInput} style={tsStyles.tcTextInput} placeholder="输入时间戳或日期文本" placeholderTextColor="#0EA5E9" multiline />
{error ? <Text style={tsStyles.tcErrorText}>{error}</Text> : null}
</View>
<View style={tsStyles.tcPane}>
<View style={tsStyles.tcLabelRow}><Text style={tsStyles.tcLabel}>输出</Text></View>
<Text style={tsStyles.tcOutputText}>{output}</Text>
</View>
</View>
</View>
);
};
const TimestampApp = () => {
return (
<ScrollView style={tsStyles.container}>
<View style={tsStyles.header}>
<Text style={tsStyles.headerTitle}>时间戳转换工具</Text>
<Text style={tsStyles.headerSubtitle}>时间戳/日期互转(钴蓝玻璃风)</Text>
</View>
<View style={tsStyles.section}>
<View style={tsStyles.tcContainer}>
<TimestampConverterGlass />
</View>
</View>
</ScrollView>
);
};
export default TimestampApp;
const ICON_BASE64_TS = {
convert: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABJUlEQVR4nO2Uu0oDQRRGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
now: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABK0lEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
clear: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABN0lEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
unit: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABMklEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
tz: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABMklEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
};
const ICON_BASE64_QR = {
generate: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABJUlEQVR4nO2Uu0oDQRRGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
clear: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABN0lEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
sizeUp: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABMklEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
sizeDown: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABMklEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
invert: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABK0lEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
margin: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsSAAALEgHS3X78AAABMklEQVR4nO2UsUoDQRSGv6WgQbYgG5gMZAGyQJgBskCYAbLA2QBsEB2YjI9kqg+oJrJf6Vb4eU2m3mS9KcZkqgqj3v8f4cIhJ0c3xQkQyGv4gqJtVZr3kzF1g2cXgH0mGJ8xwHj0C3kC4g4S9gJ4g4p7UeUuJ5ZJrF7JwE7k8Kf4mA1Jwu0SxwV6pJm1fVb9kqUoSxG1zQzqg3bqB9oC+o9dC7kP2kWmA6lH5wZkQ7Wc1wV2oG+oS6gI4gU6gC6gN5gH5gM5gG5gN6gC4gE6gE6gP7i9n3yqgW+uQmHn3Rz2bQf3pKf8dC6lHkU1YcH5tQ5ZkZyLkGm2r9wUOJi3Yq6Ww1b4q3bWw5b8m8YHc2mXb9jZVNcWwA8Yw3fD6oGJcQbJcXIqk3wqkP1pZ3yqgV8wQf4pXQm+YH3kR+gKpUuZQZVYVq0gGv8nqfYw3gP2gD8oG6gE8kD0kP4kD0oP2kH8oD+oJ8oP+oH8kD+oH8oP9qgWgB7vKc3I2q2xAAAAAElFTkSuQmCC',
};
这段React Native代码展示了一个轻量级二维码生成器的实现方案,其核心原理与鸿蒙系统的设计理念有着诸多契合之处。从技术架构层面来看,该组件采用了基于哈希种子的伪随机矩阵生成算法,通过确定性函数将输入文本转换为固定维度的二维码图案,这种算法层面的简化处理体现了在移动端开发中对性能与资源占用的平衡考量。
在组件设计方面,代码采用了高度模块化的架构,通过Icon、Row、Col等基础组件的组合构建复杂界面,这与鸿蒙系统的原子化服务思想高度一致。特别是Row和Col组件的栅格布局系统,采用百分比宽度和灵活的间距控制,能够很好地适配不同尺寸的屏幕设备,这种响应式设计理念正是鸿蒙生态所倡导的跨设备兼容性解决方案。

从渲染机制角度分析,该实现完全基于React Native的原生组件进行图形绘制,避免了对外部原生模块的依赖,这种纯JavaScript的实现方式在鸿蒙环境下具有很好的可移植性。通过View和Text组件的组合来模拟二维码矩阵的渲染,虽然牺牲了一定的渲染性能,但获得了更好的跨平台一致性,这与鸿蒙希望构建统一开发生态的愿景相吻合。
在性能优化策略上,代码采用了固定尺寸的矩阵维度(29x29)和可控的模块大小参数,这种约束性设计有助于在不同性能级别的设备上保持稳定的运行表现。鸿蒙系统特别强调应用在不同设备间的平滑迁移能力,这种自适应的参数调节机制正好符合这一要求。
特别值得关注的是定位标记的生成逻辑,通过精确的坐标计算和边界控制,确保了二维码基础识别结构的正确性。这种对关键功能点的聚焦实现,体现了在有限资源环境下优先保障核心功能的务实开发思路,这也是鸿蒙轻量化应用开发的重要原则。
从架构设计的角度来看,该组件将二维码生成逻辑与UI渲染层进行了清晰分离,状态管理通过React Hooks实现,这种单向数据流的设计模式与鸿蒙ArkUI框架的状态管理机制有着相似的理念基础,为后续的功能扩展和技术迁移提供了良好的架构支撑。
打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

最后运行效果图如下显示:

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐

所有评论(0)