@zhang_libo/resource-hub 1.0.11 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhang_libo/resource-hub",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "资源导航系统 - 集中整理与维护站点、工具、知识库与链接,支持用户登录、收藏、访问历史与后台管理",
5
5
  "keywords": [
6
6
  "resource",
@@ -256,7 +256,13 @@ function AdminCategories() {
256
256
  )}
257
257
 
258
258
  {/* Create/Edit Modal */}
259
- <window.Modal isOpen={showModal} onClose={() => setShowModal(false)} title={editTarget ? '编辑类别' : '新增类别'} width="480px">
259
+ <window.Modal
260
+ isOpen={showModal}
261
+ onClose={() => setShowModal(false)}
262
+ title={editTarget ? '编辑类别' : '新增类别'}
263
+ width="480px"
264
+ closeOnBackdrop={false}
265
+ >
260
266
  <div>
261
267
  <div style={{ marginBottom: '16px' }}>
262
268
  <label style={modalLabelStyle}>类别名称</label>
@@ -331,7 +331,13 @@ function AdminUsers() {
331
331
  )}
332
332
 
333
333
  {/* Create/Edit Modal */}
334
- <window.Modal isOpen={showModal} onClose={() => setShowModal(false)} title={editTarget ? '编辑用户' : '新增用户'} width="480px">
334
+ <window.Modal
335
+ isOpen={showModal}
336
+ onClose={() => setShowModal(false)}
337
+ title={editTarget ? '编辑用户' : '新增用户'}
338
+ width="480px"
339
+ closeOnBackdrop={false}
340
+ >
335
341
  <form onSubmit={(e) => { e.preventDefault(); handleSave(); }}>
336
342
  {formErrors.general && (
337
343
  <div style={{ background: 'color-mix(in srgb, var(--danger) 8%, var(--surface-elevated))', border: '1px solid color-mix(in srgb, var(--danger) 22%, var(--control-border))', borderRadius: '10px', padding: '10px 14px', marginBottom: '14px', fontSize: '13px', color: 'var(--danger)' }}>
@@ -419,8 +425,13 @@ function AdminUsers() {
419
425
  </window.Modal>
420
426
 
421
427
  {/* Reset Password Modal */}
422
- <window.Modal isOpen={!!resetTarget} onClose={() => { setResetTarget(null); setResetError(''); }}
423
- title={`重置密码 — ${resetTarget?.displayName || ''}`} width="400px">
428
+ <window.Modal
429
+ isOpen={!!resetTarget}
430
+ onClose={() => { setResetTarget(null); setResetError(''); }}
431
+ title={`重置密码 — ${resetTarget?.displayName || ''}`}
432
+ width="400px"
433
+ closeOnBackdrop={false}
434
+ >
424
435
  <div>
425
436
  <p style={{ fontSize: '14px', color: 'var(--text-secondary)', marginBottom: '14px' }}>
426
437
  系统将自动生成临时密码并发送至用户邮箱,用户可凭临时密码登录后自行修改。
@@ -1,3 +1,52 @@
1
+ function PasswordField({ field, label, showKey, placeholder, autoComplete, value, showPassword, error, onChange, onToggleShow, onKeyDown, disabled }) {
2
+ return (
3
+ <div style={{ marginBottom: '14px' }}>
4
+ <label style={{
5
+ fontSize: '13px', fontWeight: 500, color: 'var(--text-primary)',
6
+ display: 'block', marginBottom: '6px',
7
+ }}>
8
+ {label}
9
+ </label>
10
+ <div style={{ position: 'relative' }}>
11
+ <input
12
+ type={showPassword ? 'text' : 'password'}
13
+ name={field}
14
+ autoComplete={autoComplete}
15
+ value={value}
16
+ placeholder={placeholder}
17
+ onChange={onChange}
18
+ onKeyDown={onKeyDown}
19
+ disabled={disabled}
20
+ style={{
21
+ width: '100%', padding: '8px 40px 8px 12px',
22
+ border: `1px solid ${error ? 'var(--danger)' : 'var(--border)'}`,
23
+ borderRadius: '8px', background: 'var(--bg-secondary)',
24
+ color: 'var(--text-primary)', fontSize: '14px',
25
+ outline: 'none', boxSizing: 'border-box',
26
+ }}
27
+ />
28
+ <button
29
+ type="button"
30
+ onClick={onToggleShow}
31
+ style={{
32
+ position: 'absolute', right: '10px', top: '50%', transform: 'translateY(-50%)',
33
+ background: 'none', border: 'none', cursor: 'pointer',
34
+ color: 'var(--text-secondary)', display: 'flex', alignItems: 'center',
35
+ }}
36
+ >
37
+ {showPassword ? <lucide.EyeOff size={16} /> : <lucide.Eye size={16} />}
38
+ </button>
39
+ </div>
40
+ {field === 'newPassword' && (
41
+ <window.PasswordStrength password={value} />
42
+ )}
43
+ {error && (
44
+ <div style={{ fontSize: '12px', color: 'var(--danger)', marginTop: '4px' }}>{error}</div>
45
+ )}
46
+ </div>
47
+ );
48
+ }
49
+
1
50
  function ChangePasswordModal({ isOpen, onClose }) {
2
51
  const state = window.useAppState();
3
52
  const dispatch = window.useAppDispatch();
@@ -72,52 +121,9 @@ function ChangePasswordModal({ isOpen, onClose }) {
72
121
  }
73
122
  };
74
123
 
75
- const PasswordField = ({ field, label, showKey, placeholder, autoComplete }) => (
76
- <div style={{ marginBottom: '14px' }}>
77
- <label style={{
78
- fontSize: '13px', fontWeight: 500, color: 'var(--text-primary)',
79
- display: 'block', marginBottom: '6px',
80
- }}>
81
- {label}
82
- </label>
83
- <div style={{ position: 'relative' }}>
84
- <input
85
- type={showFields[showKey] ? 'text' : 'password'}
86
- name={field}
87
- autoComplete={autoComplete}
88
- value={form[field]}
89
- placeholder={placeholder}
90
- onChange={e => setForm(f => ({ ...f, [field]: e.target.value }))}
91
- onKeyDown={e => { if (e.key === 'Enter') handleSubmit(); }}
92
- disabled={loading}
93
- style={{
94
- width: '100%', padding: '8px 40px 8px 12px',
95
- border: `1px solid ${errors[field] ? 'var(--danger)' : 'var(--border)'}`,
96
- borderRadius: '8px', background: 'var(--bg-secondary)',
97
- color: 'var(--text-primary)', fontSize: '14px',
98
- outline: 'none', boxSizing: 'border-box',
99
- }}
100
- />
101
- <button
102
- type="button"
103
- onClick={() => setShowFields(s => ({ ...s, [showKey]: !s[showKey] }))}
104
- style={{
105
- position: 'absolute', right: '10px', top: '50%', transform: 'translateY(-50%)',
106
- background: 'none', border: 'none', cursor: 'pointer',
107
- color: 'var(--text-secondary)', display: 'flex', alignItems: 'center',
108
- }}
109
- >
110
- {showFields[showKey] ? <lucide.EyeOff size={16} /> : <lucide.Eye size={16} />}
111
- </button>
112
- </div>
113
- {field === 'newPassword' && (
114
- <window.PasswordStrength password={form.newPassword} />
115
- )}
116
- {errors[field] && (
117
- <div style={{ fontSize: '12px', color: 'var(--danger)', marginTop: '4px' }}>{errors[field]}</div>
118
- )}
119
- </div>
120
- );
124
+ const handleFieldChange = (field) => (e) => setForm(f => ({ ...f, [field]: e.target.value }));
125
+ const handleToggleShow = (showKey) => () => setShowFields(s => ({ ...s, [showKey]: !s[showKey] }));
126
+ const handleKeyDown = (e) => { if (e.key === 'Enter') handleSubmit(); };
121
127
 
122
128
  return (
123
129
  <window.Modal isOpen={isOpen} onClose={onClose} title="修改密码" width="440px" closeOnBackdrop={false} closeOnEscape>
@@ -143,6 +149,13 @@ function ChangePasswordModal({ isOpen, onClose }) {
143
149
  showKey="current"
144
150
  placeholder="请输入当前密码"
145
151
  autoComplete="current-password"
152
+ value={form.currentPassword}
153
+ showPassword={showFields.current}
154
+ error={errors.currentPassword}
155
+ onChange={handleFieldChange('currentPassword')}
156
+ onToggleShow={handleToggleShow('current')}
157
+ onKeyDown={handleKeyDown}
158
+ disabled={loading}
146
159
  />
147
160
  <PasswordField
148
161
  field="newPassword"
@@ -150,6 +163,13 @@ function ChangePasswordModal({ isOpen, onClose }) {
150
163
  showKey="new"
151
164
  placeholder="至少8位,含字母和数字"
152
165
  autoComplete="new-password"
166
+ value={form.newPassword}
167
+ showPassword={showFields.new}
168
+ error={errors.newPassword}
169
+ onChange={handleFieldChange('newPassword')}
170
+ onToggleShow={handleToggleShow('new')}
171
+ onKeyDown={handleKeyDown}
172
+ disabled={loading}
153
173
  />
154
174
  <PasswordField
155
175
  field="confirmPassword"
@@ -157,6 +177,13 @@ function ChangePasswordModal({ isOpen, onClose }) {
157
177
  showKey="confirm"
158
178
  placeholder="再次输入新密码"
159
179
  autoComplete="new-password"
180
+ value={form.confirmPassword}
181
+ showPassword={showFields.confirm}
182
+ error={errors.confirmPassword}
183
+ onChange={handleFieldChange('confirmPassword')}
184
+ onToggleShow={handleToggleShow('confirm')}
185
+ onKeyDown={handleKeyDown}
186
+ disabled={loading}
160
187
  />
161
188
 
162
189
  <div style={{