chore: 优化密码库前端界面与样式
This commit is contained in:
@@ -43,6 +43,10 @@
|
||||
<meta property="og:type" content="website" />
|
||||
|
||||
<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>
|
||||
<body>
|
||||
<noscript>您需要启用JavaScript才能运行此应用程序。</noscript>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "萌芽密码管理器",
|
||||
"short_name": "萌芽密码",
|
||||
"description": "安全、便捷的个人密码管理工具",
|
||||
"short_name": "萌芽密码管理器",
|
||||
"description": "萌芽密码管理器",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "any",
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#4caf50" />
|
||||
<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>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
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%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
@@ -29,6 +31,7 @@
|
||||
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; }
|
||||
button {
|
||||
font-family: inherit;
|
||||
background: linear-gradient(135deg, #66bb6a, #4caf50);
|
||||
color: #fff;
|
||||
border: none;
|
||||
|
||||
@@ -39,13 +39,13 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 25px 30px;
|
||||
border-bottom: 2px solid #e8f5e9;
|
||||
padding: 18px 22px;
|
||||
border-bottom: 1px solid #e8f5e9;
|
||||
}
|
||||
|
||||
.form-header h2 {
|
||||
color: #2e7d32;
|
||||
font-size: 24px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -72,35 +72,47 @@
|
||||
}
|
||||
|
||||
.password-form {
|
||||
padding: 30px;
|
||||
padding: 20px 22px 22px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
column-gap: 20px;
|
||||
row-gap: 0;
|
||||
margin-bottom: 14px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
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 {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
padding: 12px;
|
||||
padding: 10px 12px;
|
||||
border: 2px solid #c8e6c9;
|
||||
border-radius: 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s;
|
||||
background: white;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
@@ -149,12 +161,16 @@
|
||||
|
||||
.password-input-group {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.password-input-group input {
|
||||
flex: 1;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.generate-password-btn,
|
||||
@@ -162,13 +178,17 @@
|
||||
background: linear-gradient(135deg, #66bb6a 0%, #4caf50 100%);
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
padding: 10px 12px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
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,
|
||||
@@ -183,11 +203,11 @@
|
||||
}
|
||||
|
||||
.password-generator-options {
|
||||
margin-top: 12px;
|
||||
padding: 15px;
|
||||
margin-top: 8px;
|
||||
padding: 12px;
|
||||
background: rgba(200, 230, 201, 0.2);
|
||||
border-radius: 10px;
|
||||
border: 2px solid #c8e6c9;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #c8e6c9;
|
||||
}
|
||||
|
||||
.option-row {
|
||||
@@ -221,9 +241,9 @@
|
||||
|
||||
.option-checkboxes {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 15px;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
@@ -291,10 +311,10 @@
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 15px;
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 2px solid #e8f5e9;
|
||||
gap: 12px;
|
||||
margin-top: 18px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #e8f5e9;
|
||||
}
|
||||
|
||||
.cancel-button,
|
||||
@@ -338,21 +358,26 @@
|
||||
}
|
||||
|
||||
.form-header {
|
||||
padding: 20px;
|
||||
padding: 16px 18px;
|
||||
}
|
||||
|
||||
.form-header h2 {
|
||||
font-size: 20px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.password-form {
|
||||
padding: 20px;
|
||||
padding: 16px 18px 18px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
column-gap: 0;
|
||||
row-gap: 0;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.form-group--full {
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
|
||||
@@ -11,7 +11,6 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
|
||||
website: '',
|
||||
officialName: '',
|
||||
tags: '',
|
||||
logo: '',
|
||||
});
|
||||
|
||||
const [passwordOptions, setPasswordOptions] = useState({
|
||||
@@ -35,7 +34,6 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
|
||||
website: entry.website || '',
|
||||
officialName: entry.officialName || entry.software || '',
|
||||
tags: entry.tags || '',
|
||||
logo: entry.logo || '',
|
||||
});
|
||||
setShowPasswordGenerator(false);
|
||||
} else {
|
||||
@@ -54,7 +52,10 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
onSave(formData);
|
||||
onSave({
|
||||
...formData,
|
||||
logo: entry?.logo ?? '',
|
||||
});
|
||||
};
|
||||
|
||||
const generatePassword = () => {
|
||||
@@ -133,6 +134,16 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>标签</label>
|
||||
<input
|
||||
type="text"
|
||||
name="tags"
|
||||
value={formData.tags}
|
||||
onChange={handleChange}
|
||||
placeholder="标签(用空格或逗号分隔)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-row">
|
||||
@@ -143,7 +154,7 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
|
||||
name="account"
|
||||
value={formData.account}
|
||||
onChange={handleChange}
|
||||
placeholder="账号"
|
||||
placeholder="登录账号"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
@@ -270,7 +281,7 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
|
||||
name="username"
|
||||
value={formData.username}
|
||||
onChange={handleChange}
|
||||
placeholder="用户名"
|
||||
placeholder="显示名 / 昵称"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
@@ -308,29 +319,6 @@ const PasswordForm = ({ entry, onSave, onCancel }) => {
|
||||
</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">
|
||||
<button type="button" className="cancel-button" onClick={onCancel}>
|
||||
取消
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.password-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
gap: 14px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* 大屏幕(1600px以上)- 5列 */
|
||||
@@ -44,8 +44,8 @@
|
||||
|
||||
.password-card {
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 255, 248, 0.95) 100%);
|
||||
border-radius: 20px;
|
||||
padding: 18px;
|
||||
border-radius: 14px;
|
||||
padding: 12px 12px 10px;
|
||||
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);
|
||||
backdrop-filter: blur(10px);
|
||||
@@ -54,6 +54,8 @@
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.password-card::before {
|
||||
@@ -81,22 +83,22 @@
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 2px solid rgba(232, 245, 233, 0.6);
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid rgba(232, 245, 233, 0.6);
|
||||
}
|
||||
|
||||
.card-logo-wrapper {
|
||||
flex-shrink: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 14px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
padding: 5px;
|
||||
box-shadow: 0 2px 8px rgba(76, 175, 80, 0.15);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
@@ -139,10 +141,10 @@
|
||||
}
|
||||
|
||||
.card-type {
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #1b5e20;
|
||||
line-height: 1.3;
|
||||
line-height: 1.25;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -196,17 +198,17 @@
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.edit-button,
|
||||
.delete-button {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
padding: 5px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -236,24 +238,26 @@
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 4px;
|
||||
padding-top: 0;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
gap: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
position: relative;
|
||||
padding: 6px 10px;
|
||||
padding: 3px 7px;
|
||||
background: rgba(248, 255, 248, 0.5);
|
||||
border-radius: 8px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s;
|
||||
border: 1px solid transparent;
|
||||
min-height: 32px;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -271,13 +275,13 @@
|
||||
background: rgba(76, 175, 80, 0.1);
|
||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
||||
cursor: pointer;
|
||||
padding: 6px 8px;
|
||||
border-radius: 6px;
|
||||
padding: 3px 6px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.2s;
|
||||
flex-shrink: 0;
|
||||
color: #4caf50;
|
||||
min-width: 32px;
|
||||
height: 28px;
|
||||
min-width: 28px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -301,10 +305,10 @@
|
||||
|
||||
.info-label {
|
||||
color: #555;
|
||||
font-weight: 600;
|
||||
min-width: 52px;
|
||||
font-weight: 700;
|
||||
min-width: 46px;
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
@@ -314,11 +318,11 @@
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.password-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: 600;
|
||||
font-weight: 700;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
@@ -330,6 +334,7 @@
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.info-link:hover {
|
||||
@@ -344,17 +349,18 @@
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 卡片底部标签 */
|
||||
/* 卡片底部标签:贴在卡片底部,内容少时留白在中间区域 */
|
||||
.card-tags {
|
||||
margin-top: 10px;
|
||||
padding: 6px 10px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 6px;
|
||||
padding: 4px 7px;
|
||||
border-top: 1px solid rgba(232, 245, 233, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
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 {
|
||||
@@ -362,10 +368,10 @@
|
||||
background: rgba(76, 175, 80, 0.12);
|
||||
border: 1px solid rgba(76, 175, 80, 0.25);
|
||||
color: #4caf50;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
padding: 1px 6px;
|
||||
border-radius: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -482,23 +488,23 @@
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.password-card {
|
||||
padding: 14px;
|
||||
border-radius: 16px;
|
||||
padding: 10px 10px 8px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
gap: 7px;
|
||||
margin-bottom: 7px;
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
|
||||
.card-logo-wrapper {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.card-type {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.card-account-type {
|
||||
@@ -507,25 +513,24 @@
|
||||
}
|
||||
|
||||
.card-content {
|
||||
gap: 6px;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
font-size: 12px;
|
||||
padding: 5px 8px;
|
||||
gap: 6px;
|
||||
min-height: 28px;
|
||||
font-size: 11px;
|
||||
padding: 3px 6px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
min-width: 48px;
|
||||
font-size: 11px;
|
||||
min-width: 42px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
padding: 5px 7px;
|
||||
min-width: 28px;
|
||||
height: 26px;
|
||||
padding: 2px 5px;
|
||||
min-width: 26px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.copy-button svg {
|
||||
@@ -533,48 +538,59 @@
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.card-title-row {
|
||||
gap: 4px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.edit-button,
|
||||
.delete-button {
|
||||
padding: 6px;
|
||||
padding: 2px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.edit-button svg,
|
||||
.delete-button svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
gap: 6px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-tags {
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
padding: 5px 8px;
|
||||
font-size: 10px;
|
||||
margin-top: 5px;
|
||||
padding: 3px 6px;
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.password-card {
|
||||
padding: 12px;
|
||||
border-radius: 14px;
|
||||
padding: 9px 9px 7px;
|
||||
border-radius: 11px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding-bottom: 8px;
|
||||
gap: 6px;
|
||||
margin-bottom: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.card-logo-wrapper {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.card-type {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.card-account-type {
|
||||
@@ -583,19 +599,18 @@
|
||||
}
|
||||
|
||||
.card-content {
|
||||
gap: 5px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
font-size: 11px;
|
||||
padding: 4px 6px;
|
||||
gap: 5px;
|
||||
min-height: 26px;
|
||||
font-size: 10px;
|
||||
padding: 2px 5px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
min-width: 44px;
|
||||
font-size: 10px;
|
||||
min-width: 40px;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
@@ -615,10 +630,32 @@
|
||||
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 {
|
||||
margin-top: 6px;
|
||||
padding-top: 6px;
|
||||
padding: 4px 6px;
|
||||
font-size: 9px;
|
||||
margin-top: 4px;
|
||||
padding: 2px 5px;
|
||||
font-size: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,25 +45,32 @@ const EmptyIcon = () => (
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 判断是否为手机端
|
||||
const useIsMobile = () => {
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
|
||||
// 与 PasswordList.css 网格列数保持一致,便于按「行数×列数」计算每页条数
|
||||
const getGridColumnCount = (width) => {
|
||||
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(() => {
|
||||
const handler = () => setIsMobile(window.innerWidth <= 768);
|
||||
const handler = () => setColumns(getGridColumnCount(window.innerWidth));
|
||||
window.addEventListener('resize', handler);
|
||||
return () => window.removeEventListener('resize', handler);
|
||||
}, []);
|
||||
return isMobile;
|
||||
return columns;
|
||||
};
|
||||
|
||||
const PasswordList = ({ entries, onEdit, onDelete }) => {
|
||||
const [copiedId, setCopiedId] = useState(null);
|
||||
const [logoCache, setLogoCache] = useState({});
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const isMobile = useIsMobile();
|
||||
const gridColumns = useGridColumnCount();
|
||||
|
||||
// 每页条数:手机端 3行×2列=6,电脑端 2行×5列=10
|
||||
const pageSize = isMobile ? 6 : 10;
|
||||
const ROWS_PER_PAGE = 4;
|
||||
const pageSize = ROWS_PER_PAGE * gridColumns;
|
||||
const totalPages = Math.ceil(entries.length / pageSize);
|
||||
|
||||
// entries 变化时重置到第一页
|
||||
@@ -71,6 +78,14 @@ const PasswordList = ({ entries, onEdit, onDelete }) => {
|
||||
setCurrentPage(1);
|
||||
}, [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);
|
||||
|
||||
// 获取网站favicon
|
||||
@@ -79,7 +94,7 @@ const PasswordList = ({ entries, onEdit, onDelete }) => {
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
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 {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -4,18 +4,27 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 全站:霞鹜文楷等宽 */
|
||||
html {
|
||||
font-family: 'LXGW WenKai Mono', ui-monospace, monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-family: inherit;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background: linear-gradient(135deg, #a8e6cf 0%, #dcedc1 50%, #ffd3a5 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
button,
|
||||
input,
|
||||
textarea,
|
||||
select {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user