|
| 1 | +--- |
| 2 | +id: download-page |
| 3 | +title: 下载 |
| 4 | +--- |
| 5 | + |
| 6 | +import React, { useState } from 'react'; |
| 7 | + |
| 8 | +export const DownloadPage = () => { |
| 9 | + const [copiedKey, setCopiedKey] = useState(null); |
| 10 | + |
| 11 | + const copyToClipboard = (text, key) => { |
| 12 | + navigator.clipboard.writeText(text).then(() => { |
| 13 | + setCopiedKey(key); |
| 14 | + setTimeout(() => setCopiedKey(null), 2000); |
| 15 | + }); |
| 16 | + }; |
| 17 | + |
| 18 | + const card = { background: '#fff', borderRadius: '12px', border: '1px solid #e5e9f2', boxShadow: '0 2px 12px rgba(15,35,100,0.07)' }; |
| 19 | + |
| 20 | + /* ── 数据 ── */ |
| 21 | + const driverItems = [ |
| 22 | + { |
| 23 | + key: 'jdbc', |
| 24 | + name: 'JDBC 驱动', |
| 25 | + version: '9.0', |
| 26 | + platform: '全平台', |
| 27 | + size: '—', |
| 28 | + checksumAlgo: 'MD5', |
| 29 | + sha256: 'e3dd1552586cf392cfd8135a7c1fde9e', |
| 30 | + url: '/download/hgdb-jdbc-v9.0.jar', |
| 31 | + }, |
| 32 | + ]; |
| 33 | + |
| 34 | + const toolItems = [ |
| 35 | + { |
| 36 | + key: 'assess-x86', |
| 37 | + name: 'Assess 迁移工具', |
| 38 | + version: '1.0.0', |
| 39 | + platform: 'Linux x86_64', |
| 40 | + size: '—', |
| 41 | + checksumAlgo: 'MD5', |
| 42 | + sha256: '8a75568de634f5f6ace18797d0d217d8', |
| 43 | + url: 'https://yum.highgo.com/dists/IvorySQL/download/assess-1.0.0-linux.gtk.x86_64.tar.gz', |
| 44 | + }, |
| 45 | + ]; |
| 46 | + |
| 47 | + const sectionMeta = { |
| 48 | + database: { title: 'IvorySQL 数据库', subtitle: '服务端软件包与发布资产', accentColor: '#2f74ff', |
| 49 | + icon: <svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M4 7c0 1.657 3.582 3 8 3s8-1.343 8-3M4 7c0-1.657 3.582-3 8-3s8 1.343 8 3M4 7v5m16-5v5M4 12c0 1.657 3.582 3 8 3s8-1.343 8-3M4 12v5m16-5v5M4 17c0 1.657 3.582 3 8 3s8-1.343 8-3" /></svg> }, |
| 50 | + drivers: { title: 'IvorySQL 驱动', subtitle: '用于应用程序连接的客户端驱动', accentColor: '#7D52F4', |
| 51 | + icon: <svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg> }, |
| 52 | + tools: { title: '相关工具', subtitle: '迁移与管理实用工具', accentColor: '#0891b2', |
| 53 | + icon: <svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /><path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg> }, |
| 54 | + }; |
| 55 | + |
| 56 | + const SectionHeader = ({ id }) => { |
| 57 | + const m = sectionMeta[id]; |
| 58 | + return ( |
| 59 | + <div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '16px' }}> |
| 60 | + <div style={{ width: '36px', height: '36px', borderRadius: '9px', flexShrink: 0, background: `${m.accentColor}15`, color: m.accentColor, display: 'flex', alignItems: 'center', justifyContent: 'center' }}> |
| 61 | + {m.icon} |
| 62 | + </div> |
| 63 | + <div> |
| 64 | + <div style={{ fontSize: '1.1rem', fontWeight: 700, color: '#1e293b', lineHeight: 1.2 }}>{m.title}</div> |
| 65 | + <div style={{ fontSize: '0.82rem', color: '#94a3b8', marginTop: '2px' }}>{m.subtitle}</div> |
| 66 | + </div> |
| 67 | + </div> |
| 68 | + ); |
| 69 | + }; |
| 70 | + |
| 71 | + const DatabaseCard = () => { |
| 72 | + const ac = sectionMeta.database.accentColor; |
| 73 | + return ( |
| 74 | + <a href="https://github.com/IvorySQL/IvorySQL/releases" target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none', display: 'block' }}> |
| 75 | + <div style={{ ...card, padding: '22px 28px', display: 'flex', alignItems: 'center', gap: '20px', borderLeft: `4px solid ${ac}`, transition: 'box-shadow 0.2s, transform 0.2s', marginBottom: '12px' }} |
| 76 | + onMouseEnter={e => { e.currentTarget.style.boxShadow = `0 6px 24px ${ac}26`; e.currentTarget.style.transform = 'translateY(-2px)'; }} |
| 77 | + onMouseLeave={e => { e.currentTarget.style.boxShadow = '0 2px 12px rgba(15,35,100,0.07)'; e.currentTarget.style.transform = 'translateY(0)'; }}> |
| 78 | + <div style={{ minWidth: '44px', height: '44px', borderRadius: '10px', flexShrink: 0, background: `linear-gradient(135deg, ${ac} 0%, ${ac}99 100%)`, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: `0 3px 10px ${ac}4d` }}> |
| 79 | + <svg width="22" height="22" fill="none" viewBox="0 0 24 24" stroke="#fff" strokeWidth="1.8"><path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg> |
| 80 | + </div> |
| 81 | + <div style={{ flex: 1 }}> |
| 82 | + <div style={{ display: 'flex', alignItems: 'center', gap: '10px', flexWrap: 'wrap' }}> |
| 83 | + <span style={{ fontSize: '1.05rem', fontWeight: 700, color: '#1e293b' }}>IvorySQL 软件包</span> |
| 84 | + <span style={{ background: `${ac}18`, color: ac, fontSize: '0.72rem', fontWeight: 700, padding: '2px 10px', borderRadius: '20px' }}>最新稳定版</span> |
| 85 | + </div> |
| 86 | + <div style={{ marginTop: '4px' }}> |
| 87 | + <span style={{ fontSize: '0.875rem', color: '#64748b' }}>从 GitHub 下载最新的服务端安装包、源码压缩包及发布资产。</span> |
| 88 | + </div> |
| 89 | + </div> |
| 90 | + <svg width="18" height="18" fill="none" viewBox="0 0 24 24" stroke={ac} strokeWidth="2" style={{ flexShrink: 0, opacity: 0.7 }}><path strokeLinecap="round" strokeLinejoin="round" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /></svg> |
| 91 | + </div> |
| 92 | + </a> |
| 93 | + ); |
| 94 | + }; |
| 95 | + |
| 96 | + const DownloadTable = ({ items, accentColor }) => { |
| 97 | + const thStyle = { |
| 98 | + padding: '10px 16px', |
| 99 | + fontSize: '0.78rem', fontWeight: 700, color: '#64748b', |
| 100 | + textTransform: 'uppercase', letterSpacing: '0.05em', |
| 101 | + textAlign: 'left', whiteSpace: 'nowrap', |
| 102 | + }; |
| 103 | + |
| 104 | + return ( |
| 105 | + <div style={{ ...card, overflow: 'hidden' }}> |
| 106 | + <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 2.5fr 1fr', background: `${accentColor}0d`, borderBottom: '1px solid #e5e9f2' }}> |
| 107 | + <div style={thStyle}>软件包类型</div> |
| 108 | + <div style={thStyle}>软件包大小</div> |
| 109 | + <div style={thStyle}>完整性校验</div> |
| 110 | + <div style={{ ...thStyle, textAlign: 'center' }}>软件包下载</div> |
| 111 | + </div> |
| 112 | + |
| 113 | + {items.map((item, idx) => ( |
| 114 | + <div key={item.key} style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 2.5fr 1fr', alignItems: 'center', borderBottom: idx < items.length - 1 ? '1px solid #f1f5f9' : 'none', background: idx % 2 === 0 ? '#fff' : '#fafbff' }}> |
| 115 | + <div style={{ padding: '14px 16px' }}> |
| 116 | + <div style={{ fontWeight: 600, fontSize: '0.9rem', color: '#1e293b' }}>{item.name}</div> |
| 117 | + <div style={{ display: 'flex', alignItems: 'center', gap: '6px', marginTop: '4px', flexWrap: 'wrap' }}> |
| 118 | + <span style={{ fontSize: '0.75rem', color: '#fff', background: accentColor, padding: '1px 8px', borderRadius: '4px', fontWeight: 600 }}>{item.platform}</span> |
| 119 | + <span style={{ fontSize: '0.75rem', color: '#94a3b8' }}>v{item.version}</span> |
| 120 | + </div> |
| 121 | + </div> |
| 122 | + |
| 123 | + <div style={{ padding: '14px 16px', fontSize: '0.875rem', color: '#475569', fontVariantNumeric: 'tabular-nums' }}> |
| 124 | + {item.size} |
| 125 | + </div> |
| 126 | + |
| 127 | + <div style={{ padding: '14px 16px' }}> |
| 128 | + {item.sha256 ? ( |
| 129 | + <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}> |
| 130 | + <span style={{ fontSize: '0.72rem', fontWeight: 700, color: accentColor, background: `${accentColor}12`, padding: '1px 6px', borderRadius: '3px', flexShrink: 0 }}>{item.checksumAlgo || 'SHA256'}</span> |
| 131 | + <span style={{ fontFamily: 'monospace', fontSize: '0.78rem', color: '#475569', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '100px' }}> |
| 132 | + {item.sha256.slice(0, 12)}… |
| 133 | + </span> |
| 134 | + <button onClick={() => copyToClipboard(item.sha256, item.key)} title="复制 SHA256" |
| 135 | + style={{ flexShrink: 0, background: 'none', border: 'none', cursor: 'pointer', padding: '2px', color: copiedKey === item.key ? '#22c55e' : '#94a3b8', display: 'flex', alignItems: 'center' }}> |
| 136 | + {copiedKey === item.key |
| 137 | + ? <svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.5"><path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" /></svg> |
| 138 | + : <svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg> |
| 139 | + } |
| 140 | + </button> |
| 141 | + </div> |
| 142 | + ) : ( |
| 143 | + <span style={{ fontSize: '0.82rem', color: '#cbd5e1', fontStyle: 'italic' }}>待填写</span> |
| 144 | + )} |
| 145 | + </div> |
| 146 | + |
| 147 | + <div style={{ padding: '14px 16px', textAlign: 'center' }}> |
| 148 | + <a href={item.url} |
| 149 | + style={{ display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '0.82rem', fontWeight: 600, color: accentColor, textDecoration: 'none', padding: '6px 12px', borderRadius: '6px', border: `1px solid ${accentColor}40`, background: `${accentColor}08`, transition: 'background 0.15s', whiteSpace: 'nowrap' }} |
| 150 | + onMouseEnter={e => { e.currentTarget.style.background = `${accentColor}18`; }} |
| 151 | + onMouseLeave={e => { e.currentTarget.style.background = `${accentColor}08`; }}> |
| 152 | + <svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2"><path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg> |
| 153 | + 下载 |
| 154 | + </a> |
| 155 | + </div> |
| 156 | + </div> |
| 157 | + ))} |
| 158 | + </div> |
| 159 | + ); |
| 160 | + }; |
| 161 | + |
| 162 | + return ( |
| 163 | + <div style={{ maxWidth: '860px', margin: '0 auto', paddingBottom: '48px' }}> |
| 164 | + |
| 165 | + {/* ── Hero banner ── */} |
| 166 | + <div style={{ background: 'linear-gradient(135deg, #7D52F4 0%, #5a35c4 60%, #4a28b0 100%)', borderRadius: '16px', padding: '40px 40px 36px', marginBottom: '40px', position: 'relative', overflow: 'hidden' }}> |
| 167 | + <div style={{ position: 'absolute', top: '-30px', right: '-30px', width: '200px', height: '200px', borderRadius: '50%', background: 'radial-gradient(circle, rgba(125,82,244,0.35) 0%, transparent 70%)', pointerEvents: 'none' }} /> |
| 168 | + <div style={{ position: 'relative', zIndex: 1 }}> |
| 169 | + <h1 style={{ color: '#fff', margin: '0 0 10px', fontSize: '2rem', fontWeight: 800, lineHeight: 1.25 }}>IvorySQL 下载</h1> |
| 170 | + <p style={{ color: 'rgba(220,210,255,0.8)', margin: 0, fontSize: '1rem', lineHeight: 1.6 }}> |
| 171 | + 下载 IvorySQL 数据库、驱动程序及支持工具,快速开启兼容 Oracle 的 PostgreSQL 之旅。 |
| 172 | + </p> |
| 173 | + </div> |
| 174 | + </div> |
| 175 | + |
| 176 | + {/* ── Database ── */} |
| 177 | + <div style={{ marginBottom: '36px' }}> |
| 178 | + <SectionHeader id="database" /> |
| 179 | + <DatabaseCard /> |
| 180 | + </div> |
| 181 | + |
| 182 | + {/* ── Drivers ── */} |
| 183 | + <div style={{ marginBottom: '36px' }}> |
| 184 | + <SectionHeader id="drivers" /> |
| 185 | + <DownloadTable items={driverItems} accentColor={sectionMeta.drivers.accentColor} /> |
| 186 | + </div> |
| 187 | + |
| 188 | + {/* ── Tools ── */} |
| 189 | + <div> |
| 190 | + <SectionHeader id="tools" /> |
| 191 | + <DownloadTable items={toolItems} accentColor={sectionMeta.tools.accentColor} /> |
| 192 | + </div> |
| 193 | + |
| 194 | + {/* ── Contact banner ── */} |
| 195 | + <div style={{ marginTop: '36px', background: 'linear-gradient(90deg, rgba(125,82,244,0.06) 0%, rgba(125,82,244,0.03) 100%)', border: '1px solid rgba(125,82,244,0.18)', borderRadius: '10px', padding: '16px 24px', display: 'flex', alignItems: 'center', gap: '12px', flexWrap: 'wrap' }}> |
| 196 | + <svg width="18" height="18" fill="none" viewBox="0 0 24 24" stroke="#7D52F4" strokeWidth="2" style={{ flexShrink: 0 }}><path strokeLinecap="round" strokeLinejoin="round" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /></svg> |
| 197 | + <span style={{ fontSize: '0.9rem', color: '#334155' }}> |
| 198 | + 需要帮助或有疑问?请联系{' '} |
| 199 | + <a href="mailto:support@ivorysql.org" style={{ color: '#7D52F4', fontWeight: 600 }}>support@ivorysql.org</a> |
| 200 | + </span> |
| 201 | + </div> |
| 202 | + </div> |
| 203 | + ); |
| 204 | +}; |
| 205 | + |
| 206 | +<DownloadPage /> |
0 commit comments