From a9640ff059cc862557a1fd4425a2a4b2362c422c Mon Sep 17 00:00:00 2001 From: chiyi Date: Tue, 27 Oct 2026 17:59:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(users):=20=E5=BD=A9=E8=89=B2=E5=A4=B4?= =?UTF-8?q?=E5=83=8F=20+=20=E6=93=8D=E4=BD=9C=E5=88=97=E5=90=B8=E5=BA=95?= =?UTF-8?q?=20+=20=E6=BB=9A=E8=BD=AE=E6=A8=AA=E6=BB=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 头像按用户名哈希出淡彩底色(9 色轮换),不再全灰 - 操作列 position:sticky 吸在右缘,横向滚动时按钮始终可见 - 鼠标纵向滚轮映射为表格横向滚动(滚到尽头放行给页面) - 最小宽收窄 1280→1150,1440 视口基本放下 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- frontend/src/views/UsersView.vue | 56 ++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/frontend/src/views/UsersView.vue b/frontend/src/views/UsersView.vue index e4de5ed..5deea5b 100644 --- a/frontend/src/views/UsersView.vue +++ b/frontend/src/views/UsersView.vue @@ -38,6 +38,33 @@ const ROLE_OPTIONS = [ ] const roleLabel = (r) => ({ user: '用户', agent: '代理', admin: '管理员' }[r] || '用户') +// 头像淡彩:按用户名哈希出固定的一对浅底深字色 +const AVATAR_TONES = [ + ['#dbeafe', '#1d4ed8'], ['#dcfce7', '#15803d'], ['#fef3c7', '#b45309'], + ['#fce7f3', '#be185d'], ['#ede9fe', '#6d28d9'], ['#cffafe', '#0e7490'], + ['#fee2e2', '#b91c1c'], ['#ffedd5', '#c2410c'], ['#ecfccb', '#4d7c0f'], +] +const avatarStyle = (u) => { + const s = u.name || u.email || '?' + let h = 0 + for (const c of s) h = (h * 31 + c.charCodeAt(0)) >>> 0 + const [background, color] = AVATAR_TONES[h % AVATAR_TONES.length] + return { background, color } +} + +// 窄窗口下表格横向溢出:把鼠标纵向滚轮映射成横向滚动; +// 滚到左/右尽头就放行,页面正常纵向滚动。 +const onTableWheel = (e) => { + const el = e.currentTarget + if (el.scrollWidth <= el.clientWidth) return + if (Math.abs(e.deltaY) <= Math.abs(e.deltaX)) return + const max = el.scrollWidth - el.clientWidth + const next = el.scrollLeft + e.deltaY + if ((next <= 0 && el.scrollLeft <= 0) || (next >= max && el.scrollLeft >= max)) return + el.scrollLeft = next + e.preventDefault() +} + // Concurrency groups — resolve a user's group id → name/limit for the table, // and offer them in the edit form. const cgroups = ref([]) @@ -267,21 +294,21 @@ async function doRecharge() { -
- +
+
- - - + + + - + @@ -312,7 +339,7 @@ async function doRecharge() {
- {{ (u.name || u.email || '?').slice(0, 1).toUpperCase() }} + {{ (u.name || u.email || '?').slice(0, 1).toUpperCase() }}
{{ u.name || '—' }}
{{ u.email || '—' }}
@@ -578,14 +605,23 @@ async function doRecharge() { width: 34px; height: 34px; border-radius: 10px; - background: var(--surface-2); - box-shadow: inset 0 0 0 1px var(--hairline); - color: var(--fg-2); font-size: 13px; font-weight: 800; flex-shrink: 0; } +/* --- 操作列吸底右侧:横向滚动时按钮始终可见 */ +.utable thead th:last-child, +.utable tbody td:last-child { + position: sticky; + right: 0; + box-shadow: -10px 0 14px -10px rgb(15 23 42 / 0.16); +} +.utable thead th:last-child { background: #fafafa; } +.utable tbody td:last-child { background: var(--surface-solid); } +.utable tbody tr:hover td:last-child { background: #f7f7f8; } +.utable tbody tr.row-on td:last-child { background: #f1f1f3; } + /* --- icon-only action buttons */ .act { display: inline-flex; align-items: center; justify-content: center;