feat(admin): standard pagination + synced profile columns on users list

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-22 18:10:53 +08:00
parent 2f6b6afc92
commit eebf3b3334
12 changed files with 495 additions and 59 deletions
@@ -54,12 +54,41 @@
<!-- 悬浮组件本体(几何随草稿即时变化) -->
<div
class="embed-preview-dock"
:class="`is-${placement.side}`"
:class="[`is-${placement.side}`, { 'is-v2': isV2 }]"
:style="widgetStyle"
data-test="embed-preview-widget"
>
<!-- UI-04 v2:真实组件预览(iframe 载 viewer /embed-preview——真实
EmbedStream/引擎/气泡层,样例数据注入;配置经 postMessage 实时
更新,§8.2「preview 用真实展示组件,数据源注入」)。 -->
<div v-if="isV2 && !isCollapsed" class="embed-preview-v2frame" data-test="embed-preview-v2">
<iframe
ref="previewFrame"
:src="previewSrc"
title="PhotoLive v2 preview"
data-test="embed-preview-v2-iframe"
></iframe>
</div>
<div
v-if="isCollapsed"
v-else-if="isCollapsed && isV2"
class="embed-handle embed-handle--v2"
data-test="embed-preview-handle"
role="button"
tabindex="0"
@click="expand"
@keydown.enter.prevent="expand"
@keydown.space.prevent="expand"
>
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false">
<path
fill="#1a73e8"
d="M9 2 7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
/>
</svg>
<span>{{ $t('embedSlotsManage.preview.handleText') }}</span>
</div>
<div
v-else-if="isCollapsed"
class="embed-handle"
data-test="embed-preview-handle"
role="button"
@@ -149,6 +178,19 @@
<span v-if="mode === 'mobile'" class="embed-preview-caption-hint">
{{ $t('embedSlotsManage.preview.collapsedHint') }}
</span>
<!-- 工具区(§8.2:仅管理预览,不在实际组件):模拟点赞事件(测试模拟)。 -->
<button
v-if="isV2 && mode === 'desktop'"
type="button"
class="embed-preview-sim"
data-test="embed-preview-sim-like"
@click="simulateLike"
>
{{ $t('embedSlotsManage.preview.simLike') }}
</button>
<span v-if="isV2" class="embed-preview-caption-hint">
{{ $t('embedSlotsManage.preview.sampleDataHint') }}
</span>
</div>
</div>
</template>
@@ -158,6 +200,8 @@
import {
buildPreviewPlacement,
buildPreviewWidgetStyle,
clampScrollSpeed,
embedOriginFromEnv,
type EmbedSettingsDraft
} from '@/utils/system/embedSlots'
@@ -180,6 +224,43 @@
const placement = computed(() => buildPreviewPlacement(props.draft, props.mode))
const widgetStyle = computed(() => buildPreviewWidgetStyle(placement.value))
// ── UI-04 v2 真实组件预览(§8.2)──
const isV2 = computed(() => props.draft.displayMode === 'vertical-stream')
const previewFrame = ref<HTMLIFrameElement | null>(null)
const embedOrigin = embedOriginFromEnv(import.meta.env)
/** 预览 iframe src:初始配置经查询(速度/气泡/语言);后续更新走
* postMessage(不整页重载,避免滚动位置闪烁)。 */
const previewSrc = computed(() => {
const params = new URLSearchParams({
speed: String(clampScrollSpeed(props.draft.scrollSpeed)),
lang: props.draft.lang === 'auto' ? 'zh-CN' : props.draft.lang
})
if (!props.draft.reactionsEnabled) params.set('reactions', 'off')
return `${embedOrigin}/embed-preview?${params.toString()}`
})
function postPreview(payload: Record<string, unknown>): void {
previewFrame.value?.contentWindow?.postMessage(
{ photolivePreview: true, ...payload },
embedOrigin
)
}
/** 草稿速度/气泡变化 → 实时推送(不发请求不写展示位,纯预览内生效)。 */
watch(
() => [props.draft.scrollSpeed, props.draft.reactionsEnabled] as const,
([speed, reactions]) => {
if (!isV2.value) return
postPreview({ type: 'config', speed: clampScrollSpeed(speed), reactions })
}
)
/** 模拟点赞(§8.2 工具区按钮;事件为测试模拟,注入真实校验管线)。 */
function simulateLike(): void {
postPreview({ type: 'burst', photoIndex: -1 })
}
/** 把手/关闭仅切换预览内展开态(用户选择优先于默认值;不持久化) */
const expanded = ref(false)
const isCollapsed = computed(() => placement.value.collapsed && !expanded.value)
@@ -353,6 +434,47 @@
box-shadow: 0 4px 16px rgb(0 0 0 / 0.18);
overflow: hidden;
}
/* v2 壳(对齐 loader v2 wrapper:1px #fffc 边、#10161e 底、高 280)。 */
.embed-preview-dock.is-v2 {
border: 1px solid #fffc;
background: #10161e;
box-shadow: 0 4px 16px #0f172a29;
height: 280px;
}
.embed-preview-v2frame {
width: 100%;
height: 100%;
}
.embed-preview-v2frame iframe {
width: 100%;
height: 100%;
border: 0;
display: block;
}
/* v2 收起把手(对齐 loader v2:白底 100×44、蓝图标)。 */
.embed-handle--v2 {
padding: 0;
width: 100px;
gap: 8px;
border: 1px solid #e3eaf2;
background: #ffffff;
box-shadow: 0 4px 16px #0f172a20;
color: #32618e;
font-size: 12px;
}
.embed-preview-sim {
padding: 2px 10px;
border: 1px solid #d5dfed;
border-radius: 6px;
background: #fff;
color: #285586;
font-size: 11px;
line-height: 20px;
cursor: pointer;
}
.embed-preview-sim:hover {
background: #f0f6ff;
}
.embed-root {
background: #fff;
font-family: