Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fdbd2505b | ||
|
|
1049719e3f | ||
|
|
6b5c69ee53 | ||
|
|
8a62e7383d |
66
app/page.jsx
66
app/page.jsx
@@ -472,7 +472,7 @@ function Stat({ label, value, delta }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function FeedbackModal({ onClose }) {
|
function FeedbackModal({ onClose, user }) {
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [succeeded, setSucceeded] = useState(false);
|
const [succeeded, setSucceeded] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
@@ -563,7 +563,7 @@ function FeedbackModal({ onClose }) {
|
|||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<input type="hidden" name="email" value={user?.email || ''} />
|
||||||
<div className="form-group" style={{ marginBottom: 20 }}>
|
<div className="form-group" style={{ marginBottom: 20 }}>
|
||||||
<label htmlFor="message" className="muted" style={{ display: 'block', marginBottom: 8, fontSize: '14px' }}>
|
<label htmlFor="message" className="muted" style={{ display: 'block', marginBottom: 8, fontSize: '14px' }}>
|
||||||
反馈内容
|
反馈内容
|
||||||
@@ -1232,7 +1232,7 @@ function CloudConfigModal({ onConfirm, onCancel, type = 'empty' }) {
|
|||||||
</div>
|
</div>
|
||||||
<p className="muted" style={{ marginBottom: 20, fontSize: '14px', lineHeight: '1.6' }}>
|
<p className="muted" style={{ marginBottom: 20, fontSize: '14px', lineHeight: '1.6' }}>
|
||||||
{isConflict
|
{isConflict
|
||||||
? '检测到本地配置比云端更新,请选择操作:'
|
? '检测到本地配置与云端不一致,请选择操作:'
|
||||||
: '是否将本地配置同步到云端?'}
|
: '是否将本地配置同步到云端?'}
|
||||||
</p>
|
</p>
|
||||||
<div className="row" style={{ flexDirection: 'column', gap: 12 }}>
|
<div className="row" style={{ flexDirection: 'column', gap: 12 }}>
|
||||||
@@ -1856,12 +1856,12 @@ export default function HomePage() {
|
|||||||
// 检查更新
|
// 检查更新
|
||||||
const [hasUpdate, setHasUpdate] = useState(false);
|
const [hasUpdate, setHasUpdate] = useState(false);
|
||||||
const [latestVersion, setLatestVersion] = useState('');
|
const [latestVersion, setLatestVersion] = useState('');
|
||||||
|
const [updateContent, setUpdateContent] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkUpdate = async () => {
|
const checkUpdate = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('https://api.github.com/repos/hzm0321/real-time-fund/releases/latest');
|
const res = await fetch('https://api.github.com/repos/hzm0321/real-time-fund/releases/latest');
|
||||||
console.log(packageJson.version)
|
|
||||||
if (!res.ok) return;
|
if (!res.ok) return;
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.tag_name) {
|
if (data.tag_name) {
|
||||||
@@ -1869,6 +1869,7 @@ export default function HomePage() {
|
|||||||
if (remoteVersion !== packageJson.version) {
|
if (remoteVersion !== packageJson.version) {
|
||||||
setHasUpdate(true);
|
setHasUpdate(true);
|
||||||
setLatestVersion(remoteVersion);
|
setLatestVersion(remoteVersion);
|
||||||
|
setUpdateContent(data.body || '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -3224,7 +3225,6 @@ export default function HomePage() {
|
|||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
return {
|
return {
|
||||||
version: 1,
|
|
||||||
funds,
|
funds,
|
||||||
favorites: cleanedFavorites,
|
favorites: cleanedFavorites,
|
||||||
groups: cleanedGroups,
|
groups: cleanedGroups,
|
||||||
@@ -3235,7 +3235,6 @@ export default function HomePage() {
|
|||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return {
|
return {
|
||||||
version: 1,
|
|
||||||
funds: [],
|
funds: [],
|
||||||
favorites: [],
|
favorites: [],
|
||||||
groups: [],
|
groups: [],
|
||||||
@@ -3313,6 +3312,11 @@ export default function HomePage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data?.data && typeof data.data === 'object' && Object.keys(data.data).length > 0) {
|
if (data?.data && typeof data.data === 'object' && Object.keys(data.data).length > 0) {
|
||||||
|
const localPayload = collectLocalPayload();
|
||||||
|
const localComparable = getComparablePayload(localPayload);
|
||||||
|
const cloudComparable = getComparablePayload(data.data);
|
||||||
|
|
||||||
|
if (localComparable !== cloudComparable) {
|
||||||
const cloudTime = new Date(data.updated_at || 0).getTime();
|
const cloudTime = new Date(data.updated_at || 0).getTime();
|
||||||
const localTime = new Date(localStorage.getItem('localUpdatedAt') || 0).getTime();
|
const localTime = new Date(localStorage.getItem('localUpdatedAt') || 0).getTime();
|
||||||
|
|
||||||
@@ -3320,6 +3324,7 @@ export default function HomePage() {
|
|||||||
setCloudConfigModal({ open: true, userId, type: 'conflict', cloudData: data.data });
|
setCloudConfigModal({ open: true, userId, type: 'conflict', cloudData: data.data });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await applyCloudConfig(data.data, data.updated_at);
|
await applyCloudConfig(data.data, data.updated_at);
|
||||||
return;
|
return;
|
||||||
@@ -3366,13 +3371,11 @@ export default function HomePage() {
|
|||||||
const exportLocalData = async () => {
|
const exportLocalData = async () => {
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
version: 1,
|
|
||||||
funds: JSON.parse(localStorage.getItem('funds') || '[]'),
|
funds: JSON.parse(localStorage.getItem('funds') || '[]'),
|
||||||
favorites: JSON.parse(localStorage.getItem('favorites') || '[]'),
|
favorites: JSON.parse(localStorage.getItem('favorites') || '[]'),
|
||||||
groups: JSON.parse(localStorage.getItem('groups') || '[]'),
|
groups: JSON.parse(localStorage.getItem('groups') || '[]'),
|
||||||
collapsedCodes: JSON.parse(localStorage.getItem('collapsedCodes') || '[]'),
|
collapsedCodes: JSON.parse(localStorage.getItem('collapsedCodes') || '[]'),
|
||||||
refreshMs: parseInt(localStorage.getItem('refreshMs') || '30000', 10),
|
refreshMs: parseInt(localStorage.getItem('refreshMs') || '30000', 10),
|
||||||
viewMode,
|
|
||||||
holdings: JSON.parse(localStorage.getItem('holdings') || '{}'),
|
holdings: JSON.parse(localStorage.getItem('holdings') || '{}'),
|
||||||
exportedAt: new Date().toISOString()
|
exportedAt: new Date().toISOString()
|
||||||
};
|
};
|
||||||
@@ -3579,7 +3582,6 @@ export default function HomePage() {
|
|||||||
<span>基估宝</span>
|
<span>基估宝</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<img alt="项目Github地址" src={githubImg.src} style={{ width: '30px', height: '30px', cursor: 'pointer' }} onClick={() => window.open("https://github.com/hzm0321/real-time-fund")} />
|
|
||||||
{hasUpdate && (
|
{hasUpdate && (
|
||||||
<div
|
<div
|
||||||
className="badge"
|
className="badge"
|
||||||
@@ -3590,6 +3592,7 @@ export default function HomePage() {
|
|||||||
<UpdateIcon width="14" height="14" />
|
<UpdateIcon width="14" height="14" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<img alt="项目Github地址" src={githubImg.src} style={{ width: '30px', height: '30px', cursor: 'pointer' }} onClick={() => window.open("https://github.com/hzm0321/real-time-fund")} />
|
||||||
<div className="badge" title="当前刷新频率">
|
<div className="badge" title="当前刷新频率">
|
||||||
<span>刷新</span>
|
<span>刷新</span>
|
||||||
<strong>{Math.round(refreshMs / 1000)}秒</strong>
|
<strong>{Math.round(refreshMs / 1000)}秒</strong>
|
||||||
@@ -3604,6 +3607,15 @@ export default function HomePage() {
|
|||||||
>
|
>
|
||||||
<RefreshIcon className={refreshing ? 'spin' : ''} width="18" height="18" />
|
<RefreshIcon className={refreshing ? 'spin' : ''} width="18" height="18" />
|
||||||
</button>
|
</button>
|
||||||
|
{/*<button*/}
|
||||||
|
{/* className="icon-button"*/}
|
||||||
|
{/* aria-label="打开设置"*/}
|
||||||
|
{/* onClick={() => setSettingsOpen(true)}*/}
|
||||||
|
{/* title="设置"*/}
|
||||||
|
{/* hidden*/}
|
||||||
|
{/*>*/}
|
||||||
|
{/* <SettingsIcon width="18" height="18" />*/}
|
||||||
|
{/*</button>*/}
|
||||||
{/* 用户菜单 */}
|
{/* 用户菜单 */}
|
||||||
<div className="user-menu-container" ref={userMenuRef}>
|
<div className="user-menu-container" ref={userMenuRef}>
|
||||||
<button
|
<button
|
||||||
@@ -4599,6 +4611,7 @@ export default function HomePage() {
|
|||||||
<FeedbackModal
|
<FeedbackModal
|
||||||
key={feedbackNonce}
|
key={feedbackNonce}
|
||||||
onClose={() => setFeedbackOpen(false)}
|
onClose={() => setFeedbackOpen(false)}
|
||||||
|
user={user}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
@@ -4837,9 +4850,28 @@ export default function HomePage() {
|
|||||||
<UpdateIcon width="20" height="20" style={{color: 'var(--success)'}} />
|
<UpdateIcon width="20" height="20" style={{color: 'var(--success)'}} />
|
||||||
<span>更新提示</span>
|
<span>更新提示</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="muted" style={{ marginBottom: 24, fontSize: '14px', lineHeight: '1.6' }}>
|
<div style={{ marginBottom: 24 }}>
|
||||||
检测到新版本,是否刷新浏览器以更新
|
<p className="muted" style={{ fontSize: '14px', lineHeight: '1.6', marginBottom: 12 }}>
|
||||||
|
检测到新版本,是否刷新浏览器以更新?
|
||||||
|
<br/>
|
||||||
|
更新内容如下:
|
||||||
</p>
|
</p>
|
||||||
|
{updateContent && (
|
||||||
|
<div style={{
|
||||||
|
background: 'rgba(0,0,0,0.2)',
|
||||||
|
padding: '12px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
fontSize: '13px',
|
||||||
|
lineHeight: '1.5',
|
||||||
|
maxHeight: '200px',
|
||||||
|
overflowY: 'auto',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
border: '1px solid rgba(255,255,255,0.1)'
|
||||||
|
}}>
|
||||||
|
{updateContent}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="row" style={{ gap: 12 }}>
|
<div className="row" style={{ gap: 12 }}>
|
||||||
<button
|
<button
|
||||||
className="button secondary"
|
className="button secondary"
|
||||||
@@ -4884,6 +4916,18 @@ export default function HomePage() {
|
|||||||
|
|
||||||
<form onSubmit={handleSendOtp}>
|
<form onSubmit={handleSendOtp}>
|
||||||
<div className="form-group" style={{ marginBottom: 16 }}>
|
<div className="form-group" style={{ marginBottom: 16 }}>
|
||||||
|
<div style={{
|
||||||
|
marginBottom: 12,
|
||||||
|
padding: '8px 12px',
|
||||||
|
background: 'rgba(230, 162, 60, 0.1)',
|
||||||
|
border: '1px solid rgba(230, 162, 60, 0.2)',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '0.8rem',
|
||||||
|
color: '#e6a23c',
|
||||||
|
lineHeight: '1.4'
|
||||||
|
}}>
|
||||||
|
⚠️ 登录功能目前正在测试,使用过程中如遇到问题欢迎大家在 <a href="https://github.com/hzm0321/real-time-fund/issues" target="_blank" style={{ textDecoration: 'underline', color: 'inherit' }}>Github</a> 上反馈
|
||||||
|
</div>
|
||||||
<div className="muted" style={{ marginBottom: 8, fontSize: '0.8rem' }}>
|
<div className="muted" style={{ marginBottom: 8, fontSize: '0.8rem' }}>
|
||||||
请输入邮箱,我们将发送验证码到您的邮箱
|
请输入邮箱,我们将发送验证码到您的邮箱
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "real-time-fund",
|
"name": "real-time-fund",
|
||||||
"version": "0.1.0",
|
"version": "0.1.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
|||||||
Reference in New Issue
Block a user