chore: 优化密码库前端界面与样式

This commit is contained in:
2026-06-24 21:58:49 +08:00
parent 485f5e0fc1
commit 21e1225a23
8 changed files with 248 additions and 167 deletions

View File

@@ -43,6 +43,10 @@
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<title>萌芽密码管理器</title> <title>萌芽密码管理器</title>
<!-- 霞鹜文楷等宽 (LXGW WenKai Mono) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lxgw-wenkai-webfont@1.7.0/lxgwwenkaimono-regular.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lxgw-wenkai-webfont@1.7.0/lxgwwenkaimono-bold.css" />
</head> </head>
<body> <body>
<noscript>您需要启用JavaScript才能运行此应用程序。</noscript> <noscript>您需要启用JavaScript才能运行此应用程序。</noscript>

View File

@@ -1,7 +1,7 @@
{ {
"name": "萌芽密码管理器", "name": "萌芽密码管理器",
"short_name": "萌芽密码", "short_name": "萌芽密码管理器",
"description": "安全、便捷的个人密码管理工具", "description": "萌芽密码管理",
"start_url": "/", "start_url": "/",
"display": "standalone", "display": "standalone",
"orientation": "any", "orientation": "any",

View File

@@ -5,10 +5,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#4caf50" /> <meta name="theme-color" content="#4caf50" />
<title>离线 - 萌芽密码管理器</title> <title>离线 - 萌芽密码管理器</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lxgw-wenkai-webfont@1.7.0/lxgwwenkaimono-regular.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lxgw-wenkai-webfont@1.7.0/lxgwwenkaimono-bold.css" />
<style> <style>
* { box-sizing: border-box; margin: 0; padding: 0; } * { box-sizing: border-box; margin: 0; padding: 0; }
body { body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-family: 'LXGW WenKai Mono', ui-monospace, monospace;
background: linear-gradient(135deg, #f0fdf0 0%, #e8f5e9 100%); background: linear-gradient(135deg, #f0fdf0 0%, #e8f5e9 100%);
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
@@ -29,6 +31,7 @@
h1 { font-size: 22px; color: #1b5e20; margin-bottom: 12px; font-weight: 700; } h1 { font-size: 22px; color: #1b5e20; margin-bottom: 12px; font-weight: 700; }
p { font-size: 15px; color: #666; line-height: 1.6; margin-bottom: 24px; } p { font-size: 15px; color: #666; line-height: 1.6; margin-bottom: 24px; }
button { button {
font-family: inherit;
background: linear-gradient(135deg, #66bb6a, #4caf50); background: linear-gradient(135deg, #66bb6a, #4caf50);
color: #fff; color: #fff;
border: none; border: none;

View File

@@ -39,13 +39,13 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 25px 30px; padding: 18px 22px;
border-bottom: 2px solid #e8f5e9; border-bottom: 1px solid #e8f5e9;
} }
.form-header h2 { .form-header h2 {
color: #2e7d32; color: #2e7d32;
font-size: 24px; font-size: 20px;
font-weight: 600; font-weight: 600;
margin: 0; margin: 0;
} }
@@ -72,35 +72,47 @@
} }
.password-form { .password-form {
padding: 30px; padding: 20px 22px 22px;
} }
.form-row { .form-row {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 20px; column-gap: 20px;
margin-bottom: 20px; row-gap: 0;
margin-bottom: 14px;
align-items: start;
} }
.form-group { .form-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 6px;
min-width: 0;
width: 100%;
max-width: 100%;
}
.form-group--full {
grid-column: 1 / -1;
} }
.form-group label { .form-group label {
color: #666; color: #666;
font-size: 14px; font-size: 13px;
font-weight: 500; font-weight: 500;
} }
.form-group input { .form-group input {
padding: 12px; padding: 10px 12px;
border: 2px solid #c8e6c9; border: 2px solid #c8e6c9;
border-radius: 10px; border-radius: 8px;
font-size: 14px; font-size: 14px;
transition: all 0.3s; transition: all 0.3s;
background: white; background: white;
width: 100%;
min-width: 0;
box-sizing: border-box;
} }
.form-group input:focus { .form-group input:focus {
@@ -149,12 +161,16 @@
.password-input-group { .password-input-group {
display: flex; display: flex;
gap: 8px; gap: 6px;
align-items: center; align-items: stretch;
width: 100%;
min-width: 0;
} }
.password-input-group input { .password-input-group input {
flex: 1; flex: 1 1 0;
min-width: 0;
width: auto;
} }
.generate-password-btn, .generate-password-btn,
@@ -162,13 +178,17 @@
background: linear-gradient(135deg, #66bb6a 0%, #4caf50 100%); background: linear-gradient(135deg, #66bb6a 0%, #4caf50 100%);
border: none; border: none;
color: white; color: white;
font-size: 18px; font-size: 16px;
cursor: pointer; cursor: pointer;
padding: 10px 12px; padding: 8px 10px;
border-radius: 8px; border-radius: 8px;
transition: all 0.2s; transition: all 0.2s;
flex-shrink: 0; flex-shrink: 0;
box-shadow: 0 2px 5px rgba(76, 175, 80, 0.3); box-shadow: 0 2px 5px rgba(76, 175, 80, 0.3);
align-self: stretch;
display: inline-flex;
align-items: center;
justify-content: center;
} }
.generate-password-btn:hover, .generate-password-btn:hover,
@@ -183,11 +203,11 @@
} }
.password-generator-options { .password-generator-options {
margin-top: 12px; margin-top: 8px;
padding: 15px; padding: 12px;
background: rgba(200, 230, 201, 0.2); background: rgba(200, 230, 201, 0.2);
border-radius: 10px; border-radius: 8px;
border: 2px solid #c8e6c9; border: 1px solid #c8e6c9;
} }
.option-row { .option-row {
@@ -221,9 +241,9 @@
.option-checkboxes { .option-checkboxes {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px; gap: 8px 10px;
margin-bottom: 15px; margin-bottom: 12px;
} }
.checkbox-label { .checkbox-label {
@@ -291,10 +311,10 @@
.form-actions { .form-actions {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
gap: 15px; gap: 12px;
margin-top: 30px; margin-top: 18px;
padding-top: 20px; padding-top: 16px;
border-top: 2px solid #e8f5e9; border-top: 1px solid #e8f5e9;
} }
.cancel-button, .cancel-button,
@@ -338,21 +358,26 @@
} }
.form-header { .form-header {
padding: 20px; padding: 16px 18px;
} }
.form-header h2 { .form-header h2 {
font-size: 20px; font-size: 18px;
} }
.password-form { .password-form {
padding: 20px; padding: 16px 18px 18px;
} }
.form-row { .form-row {
grid-template-columns: 1fr; grid-template-columns: minmax(0, 1fr);
gap: 15px; column-gap: 0;
margin-bottom: 15px; row-gap: 0;
margin-bottom: 12px;
}
.form-group--full {
grid-column: 1;
} }
.form-actions { .form-actions {

View File

@@ -11,7 +11,6 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
website: '', website: '',
officialName: '', officialName: '',
tags: '', tags: '',
logo: '',
}); });
const [passwordOptions, setPasswordOptions] = useState({ const [passwordOptions, setPasswordOptions] = useState({
@@ -35,7 +34,6 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
website: entry.website || '', website: entry.website || '',
officialName: entry.officialName || entry.software || '', officialName: entry.officialName || entry.software || '',
tags: entry.tags || '', tags: entry.tags || '',
logo: entry.logo || '',
}); });
setShowPasswordGenerator(false); setShowPasswordGenerator(false);
} else { } else {
@@ -54,7 +52,10 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
onSave(formData); onSave({
...formData,
logo: entry?.logo ?? '',
});
}; };
const generatePassword = () => { const generatePassword = () => {
@@ -133,6 +134,16 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
required required
/> />
</div> </div>
<div className="form-group">
<label>标签</label>
<input
type="text"
name="tags"
value={formData.tags}
onChange={handleChange}
placeholder="标签(用空格或逗号分隔)"
/>
</div>
</div> </div>
<div className="form-row"> <div className="form-row">
@@ -143,7 +154,7 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
name="account" name="account"
value={formData.account} value={formData.account}
onChange={handleChange} onChange={handleChange}
placeholder="账号" placeholder="登录账号"
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
@@ -270,7 +281,7 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
name="username" name="username"
value={formData.username} value={formData.username}
onChange={handleChange} onChange={handleChange}
placeholder="用户名" placeholder="显示名 / 昵称"
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
@@ -308,29 +319,6 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
</div> </div>
</div> </div>
<div className="form-row">
<div className="form-group">
<label>标签</label>
<input
type="text"
name="tags"
value={formData.tags}
onChange={handleChange}
placeholder="标签(用空格分隔)"
/>
</div>
<div className="form-group">
<label>Logo图标URL可选</label>
<input
type="url"
name="logo"
value={formData.logo}
onChange={handleChange}
placeholder="https://example.com/logo.png留空则自动获取"
/>
</div>
</div>
<div className="form-actions"> <div className="form-actions">
<button type="button" className="cancel-button" onClick={onCancel}> <button type="button" className="cancel-button" onClick={onCancel}>
取消 取消

View File

@@ -1,8 +1,8 @@
.password-list { .password-list {
display: grid; display: grid;
grid-template-columns: repeat(5, 1fr); grid-template-columns: repeat(5, 1fr);
gap: 20px; gap: 14px;
margin-bottom: 30px; margin-bottom: 24px;
} }
/* 大屏幕1600px以上- 5列 */ /* 大屏幕1600px以上- 5列 */
@@ -44,8 +44,8 @@
.password-card { .password-card {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 255, 248, 0.95) 100%); background: linear-gradient(135deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 255, 248, 0.95) 100%);
border-radius: 20px; border-radius: 14px;
padding: 18px; padding: 12px 12px 10px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(76, 175, 80, 0.1); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(76, 175, 80, 0.1);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
@@ -54,6 +54,8 @@
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%;
min-height: 0;
} }
.password-card::before { .password-card::before {
@@ -81,22 +83,22 @@
.card-header { .card-header {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
gap: 12px; gap: 8px;
margin-bottom: 12px; margin-bottom: 8px;
padding-bottom: 12px; padding-bottom: 8px;
border-bottom: 2px solid rgba(232, 245, 233, 0.6); border-bottom: 1px solid rgba(232, 245, 233, 0.6);
} }
.card-logo-wrapper { .card-logo-wrapper {
flex-shrink: 0; flex-shrink: 0;
width: 50px; width: 40px;
height: 50px; height: 40px;
border-radius: 14px; border-radius: 10px;
background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 8px; padding: 5px;
box-shadow: 0 2px 8px rgba(76, 175, 80, 0.15); box-shadow: 0 2px 8px rgba(76, 175, 80, 0.15);
transition: all 0.3s; transition: all 0.3s;
} }
@@ -139,10 +141,10 @@
} }
.card-type { .card-type {
font-size: 16px; font-size: 14px;
font-weight: 700; font-weight: 700;
color: #1b5e20; color: #1b5e20;
line-height: 1.3; line-height: 1.25;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@@ -196,17 +198,17 @@
.card-actions { .card-actions {
display: flex; display: flex;
gap: 10px; gap: 6px;
} }
.edit-button, .edit-button,
.delete-button { .delete-button {
background: rgba(255, 255, 255, 0.8); background: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(76, 175, 80, 0.2); border: 1px solid rgba(76, 175, 80, 0.2);
font-size: 18px; font-size: 16px;
cursor: pointer; cursor: pointer;
padding: 8px; padding: 5px;
border-radius: 8px; border-radius: 6px;
transition: all 0.2s; transition: all 0.2s;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -236,24 +238,26 @@
.card-content { .card-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 4px;
padding-top: 0; padding-top: 0;
flex: 1; flex: 1;
min-height: 0;
} }
.info-row { .info-row {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 6px;
font-size: 13px; font-size: 12px;
line-height: 1.4; font-weight: 600;
line-height: 1.3;
position: relative; position: relative;
padding: 6px 10px; padding: 3px 7px;
background: rgba(248, 255, 248, 0.5); background: rgba(248, 255, 248, 0.5);
border-radius: 8px; border-radius: 6px;
transition: all 0.2s; transition: all 0.2s;
border: 1px solid transparent; border: 1px solid transparent;
min-height: 32px; min-height: 0;
overflow: hidden; overflow: hidden;
} }
@@ -271,13 +275,13 @@
background: rgba(76, 175, 80, 0.1); background: rgba(76, 175, 80, 0.1);
border: 1px solid rgba(76, 175, 80, 0.2); border: 1px solid rgba(76, 175, 80, 0.2);
cursor: pointer; cursor: pointer;
padding: 6px 8px; padding: 3px 6px;
border-radius: 6px; border-radius: 5px;
transition: all 0.2s; transition: all 0.2s;
flex-shrink: 0; flex-shrink: 0;
color: #4caf50; color: #4caf50;
min-width: 32px; min-width: 28px;
height: 28px; height: 24px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -301,10 +305,10 @@
.info-label { .info-label {
color: #555; color: #555;
font-weight: 600; font-weight: 700;
min-width: 52px; min-width: 46px;
flex-shrink: 0; flex-shrink: 0;
font-size: 12px; font-size: 11px;
} }
.info-value { .info-value {
@@ -314,11 +318,11 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
min-width: 0; min-width: 0;
font-weight: 600;
} }
.password-value { .password-value {
font-family: 'Courier New', monospace; font-weight: 700;
font-weight: 600;
color: #2e7d32; color: #2e7d32;
} }
@@ -330,6 +334,7 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
min-width: 0; min-width: 0;
font-weight: 600;
} }
.info-link:hover { .info-link:hover {
@@ -344,17 +349,18 @@
font-size: 12px; font-size: 12px;
} }
/* 卡片底部标签 */ /* 卡片底部标签:贴在卡片底部,内容少时留白在中间区域 */
.card-tags { .card-tags {
margin-top: 10px; flex-shrink: 0;
padding: 6px 10px; margin-top: 6px;
padding: 4px 7px;
border-top: 1px solid rgba(232, 245, 233, 0.6); border-top: 1px solid rgba(232, 245, 233, 0.6);
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 4px;
flex-wrap: wrap; flex-wrap: wrap;
background: linear-gradient(135deg, rgba(200, 230, 201, 0.3) 0%, rgba(165, 214, 167, 0.2) 100%); background: linear-gradient(135deg, rgba(200, 230, 201, 0.3) 0%, rgba(165, 214, 167, 0.2) 100%);
border-radius: 8px; border-radius: 6px;
} }
.card-tag { .card-tag {
@@ -362,10 +368,10 @@
background: rgba(76, 175, 80, 0.12); background: rgba(76, 175, 80, 0.12);
border: 1px solid rgba(76, 175, 80, 0.25); border: 1px solid rgba(76, 175, 80, 0.25);
color: #4caf50; color: #4caf50;
font-size: 11px; font-size: 10px;
font-weight: 600; font-weight: 700;
padding: 2px 8px; padding: 1px 6px;
border-radius: 10px; border-radius: 8px;
white-space: nowrap; white-space: nowrap;
} }
@@ -482,23 +488,23 @@
@media (max-width: 768px) { @media (max-width: 768px) {
.password-card { .password-card {
padding: 14px; padding: 10px 10px 8px;
border-radius: 16px; border-radius: 12px;
} }
.card-header { .card-header {
gap: 10px; gap: 7px;
margin-bottom: 10px; margin-bottom: 7px;
padding-bottom: 10px; padding-bottom: 7px;
} }
.card-logo-wrapper { .card-logo-wrapper {
width: 44px; width: 36px;
height: 44px; height: 36px;
} }
.card-type { .card-type {
font-size: 14px; font-size: 13px;
} }
.card-account-type { .card-account-type {
@@ -507,25 +513,24 @@
} }
.card-content { .card-content {
gap: 6px; gap: 3px;
} }
.info-row { .info-row {
font-size: 12px; font-size: 11px;
padding: 5px 8px; padding: 3px 6px;
gap: 6px; gap: 5px;
min-height: 28px;
} }
.info-label { .info-label {
min-width: 48px; min-width: 42px;
font-size: 11px; font-size: 10px;
} }
.copy-button { .copy-button {
padding: 5px 7px; padding: 2px 5px;
min-width: 28px; min-width: 26px;
height: 26px; height: 22px;
} }
.copy-button svg { .copy-button svg {
@@ -533,48 +538,59 @@
height: 14px; height: 14px;
} }
.card-title-row {
gap: 4px;
align-items: flex-start;
}
.edit-button, .edit-button,
.delete-button { .delete-button {
padding: 6px; padding: 2px;
width: 24px;
height: 24px;
box-sizing: border-box;
border-radius: 5px;
} }
.edit-button svg, .edit-button svg,
.delete-button svg { .delete-button svg {
width: 16px; width: 13px;
height: 16px; height: 13px;
} }
.card-actions { .card-actions {
gap: 6px; flex-direction: column;
align-items: center;
gap: 3px;
flex-shrink: 0;
} }
.card-tags { .card-tags {
margin-top: 8px; margin-top: 5px;
padding-top: 8px; padding: 3px 6px;
padding: 5px 8px; font-size: 9px;
font-size: 10px;
} }
} }
@media (max-width: 480px) { @media (max-width: 480px) {
.password-card { .password-card {
padding: 12px; padding: 9px 9px 7px;
border-radius: 14px; border-radius: 11px;
} }
.card-header { .card-header {
gap: 8px; gap: 6px;
margin-bottom: 8px; margin-bottom: 6px;
padding-bottom: 8px; padding-bottom: 6px;
} }
.card-logo-wrapper { .card-logo-wrapper {
width: 40px; width: 34px;
height: 40px; height: 34px;
} }
.card-type { .card-type {
font-size: 13px; font-size: 12px;
} }
.card-account-type { .card-account-type {
@@ -583,19 +599,18 @@
} }
.card-content { .card-content {
gap: 5px; gap: 2px;
} }
.info-row { .info-row {
font-size: 11px; font-size: 10px;
padding: 4px 6px; padding: 2px 5px;
gap: 5px; gap: 4px;
min-height: 26px;
} }
.info-label { .info-label {
min-width: 44px; min-width: 40px;
font-size: 10px; font-size: 9px;
} }
.empty-state { .empty-state {
@@ -615,10 +630,32 @@
font-size: 13px; font-size: 13px;
} }
.card-title-row {
gap: 3px;
align-items: flex-start;
}
.edit-button,
.delete-button {
width: 22px;
height: 22px;
padding: 1px;
border-radius: 4px;
}
.edit-button svg,
.delete-button svg {
width: 12px;
height: 12px;
}
.card-actions {
gap: 2px;
}
.card-tags { .card-tags {
margin-top: 6px; margin-top: 4px;
padding-top: 6px; padding: 2px 5px;
padding: 4px 6px; font-size: 8px;
font-size: 9px;
} }
} }

View File

@@ -45,25 +45,32 @@ const EmptyIcon = () => (
</svg> </svg>
); );
// 判断是否为手机端 // 与 PasswordList.css 网格列数保持一致,便于按「行数×列数」计算每页条数
const useIsMobile = () => { const getGridColumnCount = (width) => {
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); if (width <= 768) return 2;
if (width <= 1199) return 3;
if (width <= 1599) return 4;
return 5;
};
const useGridColumnCount = () => {
const [columns, setColumns] = useState(() => getGridColumnCount(window.innerWidth));
useEffect(() => { useEffect(() => {
const handler = () => setIsMobile(window.innerWidth <= 768); const handler = () => setColumns(getGridColumnCount(window.innerWidth));
window.addEventListener('resize', handler); window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler); return () => window.removeEventListener('resize', handler);
}, []); }, []);
return isMobile; return columns;
}; };
const PasswordList = ({ entries, onEdit, onDelete }) => { const PasswordList = ({ entries, onEdit, onDelete }) => {
const [copiedId, setCopiedId] = useState(null); const [copiedId, setCopiedId] = useState(null);
const [logoCache, setLogoCache] = useState({}); const [logoCache, setLogoCache] = useState({});
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const isMobile = useIsMobile(); const gridColumns = useGridColumnCount();
// 每页条数:手机端 3行×2列=6电脑端 2行×5列=10 const ROWS_PER_PAGE = 4;
const pageSize = isMobile ? 6 : 10; const pageSize = ROWS_PER_PAGE * gridColumns;
const totalPages = Math.ceil(entries.length / pageSize); const totalPages = Math.ceil(entries.length / pageSize);
// entries 变化时重置到第一页 // entries 变化时重置到第一页
@@ -71,6 +78,14 @@ const PasswordList = ({ entries, onEdit, onDelete }) => {
setCurrentPage(1); setCurrentPage(1);
}, [entries]); }, [entries]);
// 列数变化(窗口缩放)时避免当前页超出总页数
useEffect(() => {
setCurrentPage((p) => {
const tp = Math.max(1, Math.ceil(entries.length / pageSize) || 1);
return Math.min(p, tp);
});
}, [pageSize, entries.length]);
const pagedEntries = entries.slice((currentPage - 1) * pageSize, currentPage * pageSize); const pagedEntries = entries.slice((currentPage - 1) * pageSize, currentPage * pageSize);
// 获取网站favicon // 获取网站favicon
@@ -79,7 +94,7 @@ const PasswordList = ({ entries, onEdit, onDelete }) => {
try { try {
const urlObj = new URL(url); const urlObj = new URL(url);
const domain = urlObj.host; const domain = urlObj.host;
return `https://cf-favicon.pages.dev/api/favicon?url=${domain}`; return `https://favicon.smyhub.com/api/favicon?url=${encodeURIComponent(domain)}`;
} catch { } catch {
return null; return null;
} }

View File

@@ -4,18 +4,27 @@
box-sizing: border-box; box-sizing: border-box;
} }
/* 全站:霞鹜文楷等宽 */
html {
font-family: 'LXGW WenKai Mono', ui-monospace, monospace;
}
body { body {
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family: inherit;
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
background: linear-gradient(135deg, #a8e6cf 0%, #dcedc1 50%, #ffd3a5 100%); background: linear-gradient(135deg, #a8e6cf 0%, #dcedc1 50%, #ffd3a5 100%);
min-height: 100vh; min-height: 100vh;
} }
code { button,
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', input,
monospace; textarea,
select {
font-family: inherit;
}
code {
font-family: inherit;
} }