feat: redesign live recorder control console ui
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
|
||||
type BadgeContext =
|
||||
| "generic"
|
||||
| "availability"
|
||||
| "recording"
|
||||
| "task"
|
||||
| "session"
|
||||
| "cleanup"
|
||||
| "upload"
|
||||
| "boolean";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
label?: string;
|
||||
status?: string | number | boolean | null;
|
||||
context?: BadgeContext;
|
||||
size?: "sm" | "md";
|
||||
}>(),
|
||||
{
|
||||
label: "",
|
||||
status: null,
|
||||
context: "generic",
|
||||
size: "md"
|
||||
}
|
||||
);
|
||||
|
||||
type Tone = "gray" | "green" | "blue" | "yellow" | "red" | "orange" | "indigo";
|
||||
|
||||
function normalizeValue(value: string | number | boolean | null) {
|
||||
if (typeof value === "string") {
|
||||
return value.trim().toLowerCase();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function resolveToneByNumber(value: number, context: BadgeContext): Tone {
|
||||
if (context === "availability") {
|
||||
if (value === 2) {
|
||||
return "green";
|
||||
}
|
||||
|
||||
return "gray";
|
||||
}
|
||||
|
||||
if (context === "recording") {
|
||||
if (value === 2) {
|
||||
return "blue";
|
||||
}
|
||||
|
||||
if (value === 1) {
|
||||
return "green";
|
||||
}
|
||||
|
||||
return "gray";
|
||||
}
|
||||
|
||||
if (context === "task" || context === "session") {
|
||||
if (value === 2) {
|
||||
return "blue";
|
||||
}
|
||||
|
||||
if (value === 1 || value === 7) {
|
||||
return "indigo";
|
||||
}
|
||||
|
||||
if (value === 0) {
|
||||
return "yellow";
|
||||
}
|
||||
|
||||
if (value === 3) {
|
||||
return "orange";
|
||||
}
|
||||
|
||||
if (value === 4) {
|
||||
return "green";
|
||||
}
|
||||
|
||||
if (value === 5) {
|
||||
return "red";
|
||||
}
|
||||
|
||||
return "gray";
|
||||
}
|
||||
|
||||
if (context === "upload") {
|
||||
if (value === 1) {
|
||||
return "green";
|
||||
}
|
||||
|
||||
if (value === 2) {
|
||||
return "red";
|
||||
}
|
||||
|
||||
return "gray";
|
||||
}
|
||||
|
||||
if (context === "boolean") {
|
||||
return value ? "green" : "gray";
|
||||
}
|
||||
|
||||
return "gray";
|
||||
}
|
||||
|
||||
function resolveToneByKeyword(value: string): Tone {
|
||||
if (
|
||||
value.includes("live") ||
|
||||
value.includes("online") ||
|
||||
value.includes("living") ||
|
||||
value.includes("开播") ||
|
||||
value.includes("直播中") ||
|
||||
value === "started" ||
|
||||
value === "completed" ||
|
||||
value.includes("归档") ||
|
||||
value.includes("healthy")
|
||||
) {
|
||||
return "green";
|
||||
}
|
||||
|
||||
if (value.includes("recording") || value.includes("录制中") || value.includes("uploading")) {
|
||||
return "blue";
|
||||
}
|
||||
|
||||
if (
|
||||
value.includes("pending") ||
|
||||
value.includes("queued") ||
|
||||
value.includes("待") ||
|
||||
value.includes("waiting") ||
|
||||
value.includes("storage")
|
||||
) {
|
||||
return "yellow";
|
||||
}
|
||||
|
||||
if (value.includes("retry") || value.includes("stopping") || value.includes("停止中")) {
|
||||
return "orange";
|
||||
}
|
||||
|
||||
if (value.includes("process") || value.includes("transcod")) {
|
||||
return "indigo";
|
||||
}
|
||||
|
||||
if (
|
||||
value.includes("error") ||
|
||||
value.includes("fail") ||
|
||||
value.includes("异常") ||
|
||||
value.includes("错误") ||
|
||||
value.includes("离线") ||
|
||||
value === "poll_failed"
|
||||
) {
|
||||
return "red";
|
||||
}
|
||||
|
||||
return "gray";
|
||||
}
|
||||
|
||||
const tone = computed<Tone>(() => {
|
||||
const normalized = normalizeValue(props.status);
|
||||
|
||||
if (typeof normalized === "number") {
|
||||
return resolveToneByNumber(normalized, props.context);
|
||||
}
|
||||
|
||||
if (typeof normalized === "boolean") {
|
||||
return normalized ? "green" : "gray";
|
||||
}
|
||||
|
||||
if (typeof normalized === "string" && normalized.length > 0) {
|
||||
return resolveToneByKeyword(normalized);
|
||||
}
|
||||
|
||||
return "gray";
|
||||
});
|
||||
|
||||
const displayLabel = computed(() => {
|
||||
if (props.label) {
|
||||
return props.label;
|
||||
}
|
||||
|
||||
if (props.status === null || props.status === undefined || props.status === "") {
|
||||
return "--";
|
||||
}
|
||||
|
||||
return String(props.status);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span class="status-badge" :class="[`status-badge--${tone}`, `status-badge--${size}`]">
|
||||
<span class="status-badge__dot"></span>
|
||||
<span>{{ displayLabel }}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
max-width: 100%;
|
||||
border-radius: 999px;
|
||||
border: 1px solid transparent;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status-badge--sm {
|
||||
min-height: 28px;
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-badge--md {
|
||||
min-height: 30px;
|
||||
padding: 0 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-badge__dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
flex-shrink: 0;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.status-badge--gray {
|
||||
background: #f8fafc;
|
||||
border-color: #e2e8f0;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.status-badge--green {
|
||||
background: rgba(34, 197, 94, 0.12);
|
||||
border-color: rgba(34, 197, 94, 0.18);
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
.status-badge--blue {
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
border-color: rgba(37, 99, 235, 0.16);
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.status-badge--yellow {
|
||||
background: rgba(245, 158, 11, 0.12);
|
||||
border-color: rgba(245, 158, 11, 0.18);
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.status-badge--red {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border-color: rgba(239, 68, 68, 0.16);
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.status-badge--orange {
|
||||
background: rgba(249, 115, 22, 0.12);
|
||||
border-color: rgba(249, 115, 22, 0.18);
|
||||
color: #c2410c;
|
||||
}
|
||||
|
||||
.status-badge--indigo {
|
||||
background: rgba(99, 102, 241, 0.12);
|
||||
border-color: rgba(99, 102, 241, 0.18);
|
||||
color: #4f46e5;
|
||||
}
|
||||
|
||||
:global(html[data-theme="dark"]) .status-badge--gray {
|
||||
background: rgba(148, 163, 184, 0.12);
|
||||
border-color: rgba(148, 163, 184, 0.2);
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
:global(html[data-theme="dark"]) .status-badge--green {
|
||||
color: #86efac;
|
||||
}
|
||||
|
||||
:global(html[data-theme="dark"]) .status-badge--blue {
|
||||
color: #93c5fd;
|
||||
}
|
||||
|
||||
:global(html[data-theme="dark"]) .status-badge--yellow {
|
||||
color: #fcd34d;
|
||||
}
|
||||
|
||||
:global(html[data-theme="dark"]) .status-badge--red {
|
||||
color: #fca5a5;
|
||||
}
|
||||
|
||||
:global(html[data-theme="dark"]) .status-badge--orange {
|
||||
color: #fdba74;
|
||||
}
|
||||
|
||||
:global(html[data-theme="dark"]) .status-badge--indigo {
|
||||
color: #a5b4fc;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user