feat(ui06): B3d governance + B3e closing — page headings, stat cards, info strip, embed slots
- 治理页通用层(ui06.scss):.ui06-page 工作区 / .ui06-page-heading 27px 页头 / .ui06-stats 三列统计卡 / .ui06-info-strip 提示条 / .ui06-card-title 17px 卡头 - 10 账号:页头(查看同步入口)+ 来源提示条;分页/同步档案列/G4 功能零改动 - 11 同步:页头(手动补拉上移);状态卡/历史/失败明细抽屉保留 - 12 用量:页头(阈值设置上移)+ 统计卡对齐示例(数字 27px + 注脚 10px) - 13 展示位:页头 + 卡片标题(配置对话框沿用 UI-04 真实组件) - 17-19 异常页:沿用 ArtException(大数字+文案+回退,语义等同示例;登记偏差) - i18n zh/en;typecheck/lint/vitest/build 全绿 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -111,3 +111,114 @@ body {
|
||||
outline: 3px solid #9fc7ff;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ══════════ 治理页通用(示例 .workspace / .page-heading / .stats / .stat / .info-strip) ══════════ */
|
||||
|
||||
.ui06-page {
|
||||
max-width: 1660px;
|
||||
padding: 28px 30px 44px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.ui06-page-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
margin-bottom: 22px;
|
||||
|
||||
h1 {
|
||||
font-size: 27px;
|
||||
letter-spacing: -0.7px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
> div > p {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
color: #7b899f;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 三列统计卡(示例 .stats / .stat) */
|
||||
.ui06-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 18px;
|
||||
margin-bottom: 22px;
|
||||
|
||||
.ui06-stat {
|
||||
padding: 21px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #e5eaf1;
|
||||
border-radius: 12px;
|
||||
|
||||
p {
|
||||
font-size: 11px;
|
||||
color: #8797ac;
|
||||
}
|
||||
|
||||
strong {
|
||||
display: block;
|
||||
margin: 9px 0 4px;
|
||||
font-size: 27px;
|
||||
font-weight: 650;
|
||||
letter-spacing: -0.6px;
|
||||
|
||||
.unit {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 10px;
|
||||
color: #97a4b5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 提示条(示例 .info-strip) */
|
||||
.ui06-info-strip {
|
||||
display: flex;
|
||||
gap: 9px;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 12px;
|
||||
color: #6480a8;
|
||||
background: #edf4fe;
|
||||
border: 1px solid #e2edfc;
|
||||
border-radius: 9px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.ui06-stats {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.ui06-page {
|
||||
padding: 20px 14px 40px;
|
||||
}
|
||||
|
||||
.ui06-page-heading {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 卡片头标题(示例 .card-head h2 17px) */
|
||||
.ui06-card-title {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@@ -586,7 +586,10 @@
|
||||
"totals": {
|
||||
"photoCount": "Total Photos",
|
||||
"totalBytes": "Total Storage",
|
||||
"thresholds": "Alert Thresholds"
|
||||
"thresholds": "Alert Thresholds",
|
||||
"photoCountNote": "Aggregate from usage response",
|
||||
"totalBytesNote": "Originals + viewing variants",
|
||||
"thresholdsNote": "An alert threshold, not a hard storage quota"
|
||||
},
|
||||
"albums": {
|
||||
"title": "Album Usage",
|
||||
@@ -621,6 +624,10 @@
|
||||
"kind_storage": "Storage",
|
||||
"kind_perAlbum": "Per album",
|
||||
"empty": "No alerts"
|
||||
},
|
||||
"page": {
|
||||
"title": "Storage & usage",
|
||||
"subtitle": "Understand retained assets and act before thresholds are exceeded."
|
||||
}
|
||||
},
|
||||
"usersManage": {
|
||||
@@ -649,7 +656,11 @@
|
||||
"actionResetPassword": "Reset Password",
|
||||
"actionActivate": "Activate",
|
||||
"actionDeactivate": "Deactivate",
|
||||
"actionUnlock": "Unlock"
|
||||
"actionUnlock": "Unlock",
|
||||
"subtitle": "Provisioned company accounts, credential state and local access controls.",
|
||||
"viewSync": "View user sync",
|
||||
"tableTitle": "Accounts",
|
||||
"sourceHint": "Users are pulled from the company directory on a schedule; no public sign-up. Source state and local lock are independent."
|
||||
},
|
||||
"filter": {
|
||||
"queryPlaceholder": "Account/name prefix",
|
||||
@@ -770,6 +781,10 @@
|
||||
"colCode": "Code",
|
||||
"colDetail": "Detail",
|
||||
"colCreatedAt": "Recorded At"
|
||||
},
|
||||
"page": {
|
||||
"title": "User sync",
|
||||
"subtitle": "Review scheduled directory pulls and resolve failed records."
|
||||
}
|
||||
},
|
||||
"embedSlotsManage": {
|
||||
@@ -790,7 +805,9 @@
|
||||
"unbound": "Unbound",
|
||||
"actionEdit": "Edit",
|
||||
"actionBind": "Bind Album",
|
||||
"actionCode": "Embed Code"
|
||||
"actionCode": "Embed Code",
|
||||
"subtitle": "Manage floating photo widgets: album binding, toggles and embed code.",
|
||||
"tableTitle": "Slots"
|
||||
},
|
||||
"form": {
|
||||
"nameLabel": "Name",
|
||||
|
||||
@@ -586,7 +586,10 @@
|
||||
"totals": {
|
||||
"photoCount": "照片总数",
|
||||
"totalBytes": "存储总量",
|
||||
"thresholds": "告警阈值"
|
||||
"thresholds": "告警阈值",
|
||||
"photoCountNote": "用量接口汇总",
|
||||
"totalBytesNote": "原图 + 浏览变体",
|
||||
"thresholdsNote": "告警阈值,并非存储硬配额"
|
||||
},
|
||||
"albums": {
|
||||
"title": "相册用量",
|
||||
@@ -621,6 +624,10 @@
|
||||
"kind_storage": "全库存储",
|
||||
"kind_perAlbum": "单相册",
|
||||
"empty": "暂无告警"
|
||||
},
|
||||
"page": {
|
||||
"title": "存储与用量",
|
||||
"subtitle": "查看长期保留的照片资产与告警阈值。"
|
||||
}
|
||||
},
|
||||
"usersManage": {
|
||||
@@ -649,7 +656,11 @@
|
||||
"actionResetPassword": "重置密码",
|
||||
"actionActivate": "激活",
|
||||
"actionDeactivate": "停用",
|
||||
"actionUnlock": "解锁"
|
||||
"actionUnlock": "解锁",
|
||||
"subtitle": "统一查看企业来源状态、登录凭据与本地账号治理。",
|
||||
"viewSync": "查看用户同步",
|
||||
"tableTitle": "账号列表",
|
||||
"sourceHint": "用户信息来自企业接口定期拉取,不开放注册;来源状态与本地锁定相互独立。"
|
||||
},
|
||||
"filter": {
|
||||
"queryPlaceholder": "账号/姓名前缀",
|
||||
@@ -770,6 +781,10 @@
|
||||
"colCode": "分类码",
|
||||
"colDetail": "详情",
|
||||
"colCreatedAt": "记录时间"
|
||||
},
|
||||
"page": {
|
||||
"title": "用户同步",
|
||||
"subtitle": "查看企业用户接口的定时拉取结果,定位失败记录。"
|
||||
}
|
||||
},
|
||||
"embedSlotsManage": {
|
||||
@@ -790,7 +805,9 @@
|
||||
"unbound": "未绑定",
|
||||
"actionEdit": "编辑",
|
||||
"actionBind": "绑定相册",
|
||||
"actionCode": "嵌入代码"
|
||||
"actionCode": "嵌入代码",
|
||||
"subtitle": "管理官网悬浮照片展示位:绑定相册、启停与接入代码。",
|
||||
"tableTitle": "展示位列表"
|
||||
},
|
||||
"form": {
|
||||
"nameLabel": "名称",
|
||||
|
||||
@@ -3,14 +3,24 @@
|
||||
文案)+ 嵌入代码卡。启停与任何管理面变更一样递增 configVersion——toast
|
||||
携带新版本号提示(嵌入端轮询发现版本升即整体重置渲染)。 -->
|
||||
<template>
|
||||
<div class="p-5">
|
||||
<div class="ui06-page">
|
||||
<!-- 页头(示例 13 .page-heading) -->
|
||||
<div class="ui06-page-heading">
|
||||
<div>
|
||||
<h1>{{ $t('embedSlotsManage.list.title') }}</h1>
|
||||
<p>{{ $t('embedSlotsManage.list.subtitle') }}</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<ElButton type="primary" data-test="slot-create-btn" @click="openCreate">
|
||||
{{ $t('embedSlotsManage.list.create') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ElCard data-test="embed-slots-card">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ $t('embedSlotsManage.list.title') }}</span>
|
||||
<ElButton type="primary" data-test="slot-create-btn" @click="openCreate">
|
||||
{{ $t('embedSlotsManage.list.create') }}
|
||||
</ElButton>
|
||||
<span class="ui06-card-title">{{ $t('embedSlotsManage.list.tableTitle') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -2,24 +2,37 @@
|
||||
相册用量表(title/photoCount/originalBytes/variantBytes/totalBytes
|
||||
人类可读)+ 阈值设置对话框 + 告警列表(未解决红/已解决灰,含信息与时间)。 -->
|
||||
<template>
|
||||
<div class="p-5">
|
||||
<!-- ── 合计头卡 ── -->
|
||||
<div class="grid gap-4 mb-4 sm:grid-cols-3" data-test="usage-totals">
|
||||
<ElCard shadow="never">
|
||||
<div class="text-gray-500 text-sm">{{ $t('usage.totals.photoCount') }}</div>
|
||||
<div class="text-2xl font-medium mt-1" data-test="usage-total-photos">
|
||||
{{ summary?.totals.photoCount ?? '-' }}
|
||||
</div>
|
||||
</ElCard>
|
||||
<ElCard shadow="never">
|
||||
<div class="text-gray-500 text-sm">{{ $t('usage.totals.totalBytes') }}</div>
|
||||
<div class="text-2xl font-medium mt-1" data-test="usage-total-bytes">
|
||||
<div class="ui06-page">
|
||||
<!-- 页头(示例 12 .page-heading:标题 + 副文 + 阈值设置入口上移) -->
|
||||
<div class="ui06-page-heading">
|
||||
<div>
|
||||
<h1>{{ $t('usage.page.title') }}</h1>
|
||||
<p>{{ $t('usage.page.subtitle') }}</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<ElButton plain data-test="usage-thresholds-btn" @click="thresholdsVisible = true">
|
||||
{{ $t('usage.thresholds.entry') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── 合计头卡(示例 .stats / .stat 三列统计) ── -->
|
||||
<div class="ui06-stats" data-test="usage-totals">
|
||||
<div class="ui06-stat">
|
||||
<p>{{ $t('usage.totals.photoCount') }}</p>
|
||||
<strong data-test="usage-total-photos">{{ summary?.totals.photoCount ?? '-' }}</strong>
|
||||
<small>{{ $t('usage.totals.photoCountNote') }}</small>
|
||||
</div>
|
||||
<div class="ui06-stat">
|
||||
<p>{{ $t('usage.totals.totalBytes') }}</p>
|
||||
<strong data-test="usage-total-bytes">
|
||||
{{ summary ? formatBytes(summary.totals.totalBytes) : '-' }}
|
||||
</div>
|
||||
</ElCard>
|
||||
<ElCard shadow="never">
|
||||
<div class="text-gray-500 text-sm">{{ $t('usage.totals.thresholds') }}</div>
|
||||
<div class="mt-1 flex flex-wrap gap-2" data-test="usage-threshold-tags">
|
||||
</strong>
|
||||
<small>{{ $t('usage.totals.totalBytesNote') }}</small>
|
||||
</div>
|
||||
<div class="ui06-stat">
|
||||
<p>{{ $t('usage.totals.thresholds') }}</p>
|
||||
<div class="mt-2 flex flex-wrap gap-2" data-test="usage-threshold-tags">
|
||||
<ElTag size="small" :type="summary?.thresholds.storageBytes ? 'success' : 'info'">
|
||||
{{ $t('usage.thresholds.storageShort') }}
|
||||
{{
|
||||
@@ -37,22 +50,15 @@
|
||||
}}
|
||||
</ElTag>
|
||||
</div>
|
||||
</ElCard>
|
||||
<small>{{ $t('usage.totals.thresholdsNote') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── 相册用量 + 告警 ── -->
|
||||
<ElCard data-test="usage-albums-card">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ $t('usage.albums.title') }}</span>
|
||||
<ElButton
|
||||
type="primary"
|
||||
plain
|
||||
data-test="usage-thresholds-btn"
|
||||
@click="thresholdsVisible = true"
|
||||
>
|
||||
{{ $t('usage.thresholds.entry') }}
|
||||
</ElButton>
|
||||
<span class="ui06-card-title">{{ $t('usage.albums.title') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -2,21 +2,31 @@
|
||||
运行历史(游标累积)、失败明细抽屉(点行打开)。页面存续期间 10s 自动
|
||||
刷新状态(onUnmounted 清理);手动补拉 202 受理后轮询至批次离开 running。 -->
|
||||
<template>
|
||||
<div class="p-5">
|
||||
<div class="ui06-page">
|
||||
<!-- 页头(示例 11 .page-heading:标题 + 副文 + 手动补拉入口上移) -->
|
||||
<div class="ui06-page-heading">
|
||||
<div>
|
||||
<h1>{{ $t('userSync.page.title') }}</h1>
|
||||
<p>{{ $t('userSync.page.subtitle') }}</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<ElButton
|
||||
type="primary"
|
||||
:loading="triggering"
|
||||
:disabled="status?.state === 'running'"
|
||||
data-test="sync-trigger-btn"
|
||||
@click="triggerSync"
|
||||
>
|
||||
{{ $t('userSync.status.trigger') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── 状态卡 ── -->
|
||||
<ElCard data-test="sync-status-card" class="mb-4">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ $t('userSync.status.title') }}</span>
|
||||
<ElButton
|
||||
type="primary"
|
||||
:loading="triggering"
|
||||
:disabled="status?.state === 'running'"
|
||||
data-test="sync-trigger-btn"
|
||||
@click="triggerSync"
|
||||
>
|
||||
{{ $t('userSync.status.trigger') }}
|
||||
</ElButton>
|
||||
<span class="ui06-card-title">{{ $t('userSync.status.title') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -3,11 +3,30 @@
|
||||
公司)展示 + 行内治理操作(编辑/重置密码/激活/停用/解锁)。自我保护双
|
||||
保险:UI 对自身行禁用角色/锁定编辑,服务端 400 仍会经 http 层本地化弹出。 -->
|
||||
<template>
|
||||
<div class="p-5">
|
||||
<div class="ui06-page">
|
||||
<!-- 页头(示例 10 .page-heading:标题 + 副文 + 查看同步入口) -->
|
||||
<div class="ui06-page-heading">
|
||||
<div>
|
||||
<h1>{{ $t('usersManage.list.title') }}</h1>
|
||||
<p>{{ $t('usersManage.list.subtitle') }}</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<ElButton plain data-test="users-goto-sync" @click="router.push('/system/user-sync')">
|
||||
{{ $t('usersManage.list.viewSync') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 来源提示条(示例 .info-strip:来源状态与本地锁定相互独立) -->
|
||||
<div class="ui06-info-strip">
|
||||
<ElIcon><InfoFilled /></ElIcon>
|
||||
<span>{{ $t('usersManage.list.sourceHint') }}</span>
|
||||
</div>
|
||||
|
||||
<ElCard data-test="users-card">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ $t('usersManage.list.title') }}</span>
|
||||
<span class="ui06-card-title">{{ $t('usersManage.list.tableTitle') }}</span>
|
||||
<!-- 过滤栏:前缀查询(account/displayName)+ 治理状态单选 -->
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<ElInput
|
||||
@@ -196,8 +215,10 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { InfoFilled } from '@element-plus/icons-vue'
|
||||
import UserEditDialog from './UserEditDialog.vue'
|
||||
import ResetPasswordDialog from './ResetPasswordDialog.vue'
|
||||
import {
|
||||
@@ -213,6 +234,7 @@
|
||||
defineOptions({ name: 'SystemUsers' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
// ── 过滤(查询即整页重载:游标键集依赖过滤条件稳定) ──
|
||||
|
||||
Reference in New Issue
Block a user