feat: 品牌统一 LokiStack + 全站中英 i18n + 用户批量删除 + 主题定时切换与浅色侧栏

- 品牌: Go 模块/数据文件/二进制/构建部署资产全部 opsx→lokistack;前端 localStorage 命名空间迁移(读旧写新,含按用户 id 前缀迁移);修复 nginx upstream 与 Makefile 引用已改名服务的断裂
- i18n: react-i18next 落地,common + 11 个页面域命名空间全站中英(菜单/顶栏/登录/列表页/表单/确认弹窗/状态徽章);设置页语言即时切换;修复词典自注册导致的循环导入白屏
- 用户管理: 批量删除(POST /users/batch-delete,不删自己/最后管理员,部分失败不阻断)+ 行复选/全选/批量工具条
- 主题: 浅色侧栏(中性灰激活态)、四态模式(浅色/深色/跟随系统/定时切换,按时区判定)、顶栏太阳月亮二态、设置页紧凑分段选择器
- UI: 登录记住用户名、CardActionButton 统一五页卡片操作钮、shadcn Badge 替换手写徽章、语义色修正
- 清理: 移除主机 Web 终端(前后端)、k8s-helm/Helm 遗留代码与字段、README 重写精简
- 修复: 侧栏与日志告警 order 重复、路由标题 i18n key 未翻译、词典双逗号等
This commit is contained in:
opsx committed 2026-10-27 17:11:38 +08:00
1 parent 02d1bd87a7
commit f3733f0bfc
201 files changed
+5438 -2388

No files matched your search

+316
View File
@@ -0,0 +1,316 @@
// logs 命名空间 — 日志查询页(/logs)中文文案
export default {
nav: {
aria: '日志中心导航',
},
tabs: {
query: 'Loki 查询',
access: '查询授权',
sources: '数据源管理',
descQuery: '按日志流和时间范围检索;拖动时间线缩小范围,点日志查看前后文。',
descAccess: '用策略组复用日志范围,并一次关联多个用户。',
descSources: '配置 Loki 连接与认证方式。',
},
toolbar: {
run: '查询',
running: '查询中…',
tail: 'Tail',
tailStop: '停止 Tail',
sourceAria: '数据源',
sourceEmptyOption: '暂无可用数据源',
limitLabel: '每页条数',
limitHint: '每页最多 1000 条;翻页只保留当前页。大范围先看时间线,再拖选高峰时段。',
rangeInvalid: '请选择有效的起止时间',
queryFailed: '查询失败',
},
source: {
loadFailed: '加载数据源失败',
missingTitle: '还没有可用的 Loki 数据源',
missingBody:
'请先到「数据源管理」关联 Loki:集群内可直接选「集群服务」(命名空间 / Service / 端口,无需 Ingress);跨集群或公网查询,请先给 Loki 配置 Ingress 域名,再用「URL 直连」填写该域名。未关联数据源无法查询。',
goConfigure: '去配置数据源',
sectionAria: '数据源管理',
heading: 'Loki 数据源',
hint: '提示:Loki 部署在集群内时,可直接添加「集群服务」数据源(无需 Ingress);跨集群 / 公网查询需先为 Loki 配置 Ingress 域名,再用「URL 直连」关联。未关联数据源时,查询页无法使用。',
managedCluster: '集群 {{name}}',
managedBadge: '平台部署 · 随部署管理',
add: '+ 手动添加',
nameLabel: '名称',
targetUrl: 'URL 直连',
targetClusterSvc: '集群服务',
addressLabel: 'Loki 地址',
clusterLabel: '集群',
clusterPlaceholder: '选择集群…',
namespaceLabel: '命名空间',
serviceLabel: 'Service',
portLabel: '端口',
authLabel: '认证方式',
authNone: '无认证',
authBasic: 'Basic Auth',
authBearer: 'Bearer Token',
authHeaders: '自定义 Headers',
usernameLabel: '用户名',
passwordLabel: '密码',
tokenLabel: '令牌',
headersLabel: '自定义 Headers(JSON 对象)',
headersInvalid: 'Headers 必须是字符串键值 JSON 对象',
skipTLS: '跳过 TLS 校验',
setDefault: '设为默认',
test: '测试连接',
save: '保存数据源',
saveFailed: '保存失败',
remove: '删除',
},
range: {
lastMinutes: '最近 {{count}} 分钟',
lastHours: '最近 {{count}} 小时',
custom: '自定义时间',
title: '选择时间范围',
start: '开始时间',
end: '结束时间',
apply: '应用时间',
recent: '最近使用',
},
editor: {
aria: 'LogQL 查询语句',
valuesOf: '{{label}} 的值',
quickTab: '快捷 LogQL',
historyTab: '查询历史',
historyTabCount: '查询历史 ({{count}})',
escClose: 'Esc 关闭',
historyEmpty: '运行查询后,常用 LogQL 会出现在这里',
},
templates: {
aria: '快捷查询模板',
title: '快捷生成 LogQL',
hint: '先选 namespace,再选 app,最后选副本 pod;app 候选只来自所选命名空间。',
namespace: '命名空间',
app: 'App',
appWithKey: 'App({{key}})',
pod: '副本 / Pod',
selectCount: '请选择(共 {{count}} 个)',
noApp: '该范围内无 app',
pickNsFirst: '先选命名空间可缩小范围',
noPod: '该 app 下无副本',
optional: '可选',
scopeLogs: '查询该范围日志',
errorLogs: '错误 error',
warnLogs: '告警 warn',
podLogs: '指定副本日志',
ingressLogs: 'Ingress 访问日志',
scopeHint: '先选 namespace,再选 app 或 pod,也可在 Label 浏览器选值',
},
labels: {
browser: 'Label 浏览器',
selectedCount: '已选 {{count}}',
loadingLabels: '加载标签中…',
noLabels: '暂无可用标签',
pickHint: '选择左侧标签,再搜索对应的值',
valuesOf: '{{label}} 的值',
searchAria: '搜索 {{label}} 的值',
searchPlaceholder: '搜索或输入标签值',
scoped: '按当前查询条件缩小候选',
loading: '加载中…',
noValues: '没有匹配值',
useAsValue: '使用“{{value}}”作为值',
hint: '先选 namespace 可查该命名空间全部副本;再选 app / service_name 可缩小到某个服务(无 app 标签的工作负载用 namespace 即可)。候选来自当前时间范围内已写入 Loki 的日志;授权范围仍由服务端限制。',
},
volume: {
statsLoading: '正在统计整个时间范围的日志量,日志页可继续浏览。',
statsError: '日志量统计超时或失败;日志页仍可使用。可缩小时间范围后重新查询。',
histogramLoading: '统计中…',
histogramEmpty: '暂无日志量数据',
timelineAria: '日志量时间线',
timelineTitle: '日志量时间线',
timelineHint: '悬停查看数量,按住任意高度拖选时间;选择后自动查询。',
timelineTotal: '时间桶合计 {{count}} 条',
timelineBucket: '{{count}} 条',
},
results: {
aria: '日志结果',
title: '日志结果',
summary: '第 {{page}} 页 · 本页 {{count}} 条',
summaryMore: '第 {{page}} 页 · 本页 {{count}} 条 · 还有更多',
viewAria: '日志展示方式',
viewRaw: '原始日志',
viewTable: '表格',
viewLine: '折线图',
viewStream: '流图',
viewPie: '饼图',
loadingPage: '正在加载日志页',
loadingQuery: '正在查询 Loki',
loadingWait: '已等待 {{seconds}} 秒。大范围查询可能需要较长时间。',
pagingHint: '日志按时间从新到旧翻页;图表展示当前页,顶部时间线展示整个查询范围。',
domainFilter: '域名筛选',
domainPlaceholder: '输入或选择域名',
domainQuery: '在 Loki 中查询',
domainHint: '仅匹配日志记录的请求 Host。当前 {{shown}} / {{total}} 条。',
domainNoMatch: '当前域名筛选没有匹配的日志',
filterLabel: '筛选此标签',
showContext: '查看前后各 5 条',
prevPage: '上一页',
nextPage: '下一页',
pageOf: '第 {{page}} 页',
noSourceTitle: '暂无可查询的数据源',
noSourceHint: '请联系管理员添加或授权数据源',
noMatchTitle: '未找到匹配的日志记录',
noMatchHint: '当前时间范围和筛选条件下没有匹配结果',
idleTitle: '输入 LogQL 并运行查询',
idleHint: '也可以使用 Label 浏览器快速构建查询',
retry: '重新查询',
},
views: {
noDomain: '未记录域名',
otherDomain: '其他域名',
unknown: '未知',
untagged: '未标记',
other: '其他',
tableLoaded: '当前页已加载 {{count}} 条日志。',
tableEmpty: '暂无可展示的日志',
thTime: '时间',
thNsPod: 'namespace / pod',
thLevel: '级别',
thLog: '日志',
lineByLoaded: '按当前已加载日志统计;更多结果请继续加载。',
lineByRange: '按查询时间范围统计的日志量,来自 Loki 时间聚合。',
lineGroupAria: '折线图分组字段',
lineByDomain: '按域名',
lineByTotal: '总量',
lineEmpty: '当前查询没有可用的时间统计',
lineSeriesTotal: '日志条数',
streamParsed: '展示 {{count}} 条已解析日志',
streamByLevel: '按级别展示当前已加载的 {{count}} 条日志',
streamSuffix: ',基于当前已加载结果。',
streamGroupAria: '流图分组字段',
streamByStatus: '按 HTTP 状态段',
streamEmpty: '暂无可展示的日志流',
pieByAccess: '按访问字段统计已解析的 {{count}} 条日志',
pieByLabel: '按标签统计当前已加载的 {{count}} 条日志',
pieGroupAria: '饼图分组字段',
pieDomain: '域名(请求 Host)',
pieStatus: 'HTTP 状态码',
pieMethod: '请求方法',
piePath: '请求路径',
pieUpstream: '上游服务',
pieLevel: '级别',
pieEmpty: '暂无可展示的标签分布',
},
ingress: {
modesAria: 'Ingress 统计方式',
modeTraffic: '接口 PV / UV',
modeErrors: '异常 Top 10',
modeSlow: '慢请求 Top 10',
modeDetail: '请求明细',
summary:
'仅统计当前页已加载的 {{loaded}} 条日志,其中 {{parsed}} 条为 ingress-nginx 访问日志。翻页后重新统计;UV 按非空客户端 IP 去重。',
statPv: 'PV',
statUv: 'UV(IP)',
statErrors: '4xx / 5xx',
statSlow: '慢请求 ≥ {{threshold}}s',
errorClassAria: '异常状态码范围',
errorAll: '全部 4xx / 5xx',
error5xx: '只看 5xx',
error4xx: '只看 4xx',
slowThreshold: '慢请求阈值',
seconds: '{{count}} 秒',
slowHint: '依据 access log 的 request_time 排序;408 / 499 / 504 仅作超时线索,不能单凭状态码断定后端超时。',
slowMissing: '{{count}} 条缺少有效耗时。',
trafficHint: '访问成功率按 2xx / 3xx 请求数 ÷ PV 计算;路径已去掉查询参数。',
thHost: '请求 Host',
thPath: '接口路径',
thSuccess: '成功率',
thErrorRate: '异常率',
thStatus: '状态码',
thSlow: '慢请求',
thMaxLatency: '最长耗时',
thAvgLatency: '平均耗时',
thTimeout: '408 / 499 / 504',
thClientIp: '客户端 IP',
thStatusShort: '状态',
thLatency: '耗时',
thTime: '时间',
empty: '当前页没有符合条件的请求',
noDomain: '未记录 Host',
},
context: {
title: '同一日志流的前后文',
sameFile: '同一文件',
sameStream: '同一日志流',
meta: '前后各最多 5 条 · 前后 5 分钟',
file: '文件: {{name}}',
current: '当前日志',
currentAria: '当前日志',
beforeAria: '之前的日志',
afterAria: '之后的日志',
beforeTitle: '之前 · {{count}} 条',
afterTitle: '之后 · {{count}} 条',
beforeEmpty: '此前 5 分钟内无日志',
afterEmpty: '此后 5 分钟内无日志',
loadFailed: '读取上下文失败',
},
access: {
loadFailed: '加载查询策略失败',
validate: '请填写策略组名称,选择数据源,并选择「所有人」或至少一位用户',
saveFailed: '保存策略组失败',
deleteFailed: '删除策略组失败',
sourceRef: '数据源 #{{id}}',
listTitle: '查询策略组',
create: '新建',
listHint: '一套日志范围可关联多个用户,后续修改会同时生效。',
loading: '加载中…',
empty: '还没有策略组',
anyone: '所有人',
userCount: '{{count}} 位用户',
editTitle: '编辑策略组',
createTitle: '新建策略组',
formHint: '选择数据源及允许的日志范围,再关联需要复用此策略的用户。',
remove: '删除',
nameLabel: '策略组名称',
namePlaceholder: '例如:生产环境 Agent 日志',
sourceLabel: '数据源',
sourceEmpty: '请先添加数据源',
scopeTitle: '允许查询的日志范围',
ownerFallback: '策略组',
scopeHint: '可按命名空间、App、副本 / Pod 精确限制。未选择的标签不作限制。',
usersTitle: '关联用户',
selectedAnyone: '已选 所有人(anyone)',
selectedCount: '已选 {{count}} 位',
anyoneToggle: '所有人(anyone)',
anyoneActive: '✓ 所有人(anyone)',
selectFiltered: '选择当前筛选',
clear: '清空',
usersHint: '同一用户在同一数据源只能属于一个策略组;已在其他组的用户会置灰。',
searchAria: '搜索用户',
searchPlaceholder: '搜索用户名或昵称',
grouped: '已分组',
noMatch: '没有匹配用户',
saving: '保存中…',
save: '保存策略组',
deleteTitle: '删除查询策略组',
deleteDesc: '删除「{{name}}」后,关联用户将失去由该组授予的数据源访问权限。',
ruleAria: '{{name}} 查询范围',
nsLabel: '命名空间',
appLabel: 'App',
podLabel: '副本 / Pod',
},
select: {
placeholder: '搜索并选择',
any: '不限',
loading: '加载中…',
noMatch: '无匹配值',
},
raw: {
searching: '查询中…',
count: '{{count}} 条',
dedupSuffix: '(去重后,原 {{count}} 条)',
dedup: '去重',
empty: '没有匹配的日志',
original: '日志原文',
labelsTitle: '标签(点击过滤)',
filterValue: '过滤此值',
excludeValue: '排除此值',
fields: '提取字段',
loadMore: '加载更多',
loadingMore: '加载中…',
},
}