feat(desktop): drop parked review flow, add publish management
SaaS 侧人工审核后才创建 publish job,desktop 不再做二次审核:移除 manual/waiting_user/parked/from_parked 状态机与 LeaseFromParked 查询, desktop client 只执行发布并新增"发布管理"页(待发布队列 / 历史 / 再次 发送)。同时抽离 @geo/publisher-platforms 共享适配器包、新增 Redis-based desktop_presence 与 publish_record_support,刷新 admin-web 发布弹窗与 媒体库;plan A / spec 文档同步口径。
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { SearchOutlined, ReloadOutlined, ApiOutlined, ArrowRightOutlined, LinkOutlined, DeleteOutlined, EllipsisOutlined } from "@ant-design/icons-vue";
|
||||
import { SearchOutlined, ReloadOutlined, ApiOutlined, ArrowRightOutlined, DeleteOutlined } from "@ant-design/icons-vue";
|
||||
|
||||
import StatusBadge from "../components/StatusBadge.vue";
|
||||
import SurfaceCard from "../components/SurfaceCard.vue";
|
||||
@@ -10,22 +10,19 @@ import { formatDateTime, titleCaseToken } from "../lib/formatters";
|
||||
import { desktopPublishMediaCatalog } from "../lib/media-catalog";
|
||||
import type { RuntimeAccount } from "../types";
|
||||
|
||||
const { snapshot, refresh, loading } = useDesktopRuntime();
|
||||
const { snapshot, refresh, refreshAccounts, loading } = useDesktopRuntime();
|
||||
|
||||
const selectedPlatform = ref("all");
|
||||
const selectedStatus = ref("all");
|
||||
const searchQuery = ref("");
|
||||
const bindPendingPlatformId = ref<string | null>(null);
|
||||
const consolePendingAccountId = ref<string | null>(null);
|
||||
const unbindPendingAccountId = ref<string | null>(null);
|
||||
const actionSuccess = ref<string | null>(null);
|
||||
|
||||
type AccountAuthState = "authorized" | "expired" | "attention" | "risk";
|
||||
type AccountRow = Readonly<Omit<RuntimeAccount, "tags">> & { tags: readonly string[] };
|
||||
|
||||
const clientsById = computed(
|
||||
() => new Map((snapshot.value?.clients ?? []).map((item) => [item.id, item])),
|
||||
);
|
||||
|
||||
const accounts = computed(() => snapshot.value?.accounts ?? []);
|
||||
|
||||
const overview = computed(() => ({
|
||||
@@ -112,6 +109,17 @@ function accountInitial(account: AccountRow): string {
|
||||
return account.displayName.slice(0, 1).toUpperCase();
|
||||
}
|
||||
|
||||
function sessionStateLabel(account: AccountRow): string {
|
||||
switch (account.sessionState) {
|
||||
case "hot":
|
||||
return "活跃会话";
|
||||
case "warm":
|
||||
return "本地缓存";
|
||||
default:
|
||||
return "冷缓存";
|
||||
}
|
||||
}
|
||||
|
||||
const filteredAccounts = computed(() => {
|
||||
const keyword = searchQuery.value.trim().toLowerCase();
|
||||
|
||||
@@ -128,12 +136,11 @@ const filteredAccounts = computed(() => {
|
||||
return true;
|
||||
}
|
||||
|
||||
const clientName = clientsById.value.get(account.clientId)?.deviceName ?? account.clientId;
|
||||
const haystack = [
|
||||
account.displayName,
|
||||
account.platformUid,
|
||||
platformMeta(account.platform).label,
|
||||
clientName,
|
||||
account.partition,
|
||||
]
|
||||
.join(" ")
|
||||
.toLowerCase();
|
||||
@@ -144,6 +151,7 @@ const filteredAccounts = computed(() => {
|
||||
|
||||
function selectPlatform(platform: string) {
|
||||
selectedPlatform.value = platform;
|
||||
void refreshAccounts();
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
@@ -184,11 +192,26 @@ async function openConsole(account: AccountRow) {
|
||||
}
|
||||
}
|
||||
|
||||
async function unbindAccount(account: AccountRow) {
|
||||
unbindPendingAccountId.value = account.id;
|
||||
actionSuccess.value = null;
|
||||
|
||||
try {
|
||||
await window.desktopBridge.app.unbindPublishAccount(account.id, account.syncVersion);
|
||||
actionSuccess.value = `${account.displayName} 已解绑`;
|
||||
await refresh();
|
||||
} catch (error) {
|
||||
showClientActionError("unbind-account", error);
|
||||
} finally {
|
||||
unbindPendingAccountId.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
const tableColumns = [
|
||||
{ title: "昵称", key: "name", dataIndex: "name" },
|
||||
{ title: "平台", key: "platform", dataIndex: "platform" },
|
||||
{ title: "授权状态", key: "status", dataIndex: "status" },
|
||||
{ title: "归属客户端", key: "client", dataIndex: "client" },
|
||||
{ title: "本地会话", key: "session", dataIndex: "session" },
|
||||
{ title: "授权时间", key: "syncTime", dataIndex: "syncTime" },
|
||||
{ title: "操作", key: "actions", align: "right" as const }
|
||||
];
|
||||
@@ -203,7 +226,7 @@ const tableColumns = [
|
||||
<p>先把平台授权跑通。上面直接发起真实登录绑定,下面看已授权账号、状态和进入创作台入口。</p>
|
||||
</div>
|
||||
<div class="media-view__header-actions">
|
||||
<a-button :loading="loading" @click="refresh">
|
||||
<a-button :loading="loading" @click="refreshAccounts">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
刷新状态
|
||||
</a-button>
|
||||
@@ -292,7 +315,7 @@ const tableColumns = [
|
||||
<div class="panel list-panel">
|
||||
<div class="table-header">
|
||||
<h3 class="panel-title">授权账号列表</h3>
|
||||
<p class="panel-desc">当前共 {{ filteredAccounts.length }} 个媒体账号,支持重新授权和直接进入创作台</p>
|
||||
<p class="panel-desc">当前仅展示本机存在缓存分区或会话的媒体账号,并实时校验授权是否过期</p>
|
||||
|
||||
<div class="media-list-toolbar">
|
||||
<div class="toolbar-filters">
|
||||
@@ -317,7 +340,7 @@ const tableColumns = [
|
||||
|
||||
<a-input
|
||||
v-model:value="searchQuery"
|
||||
placeholder="搜索昵称、UID、平台或客户端"
|
||||
placeholder="搜索昵称、UID、平台或本地分区"
|
||||
style="width: 280px"
|
||||
allow-clear
|
||||
>
|
||||
@@ -372,11 +395,11 @@ const tableColumns = [
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'client'">
|
||||
<template v-else-if="column.key === 'session'">
|
||||
<div class="cell-primary-secondary">
|
||||
<div class="info-stack">
|
||||
<span class="title">{{ clientsById.get(record.clientId)?.deviceName ?? record.clientId }}</span>
|
||||
<span class="subtitle">{{ record.online ? "在线" : "离线" }}</span>
|
||||
<span class="title">{{ sessionStateLabel(record) }}</span>
|
||||
<span class="subtitle">{{ record.online ? "本机在线" : "本机离线" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -407,6 +430,22 @@ const tableColumns = [
|
||||
>
|
||||
重新授权
|
||||
</a-button>
|
||||
<a-popconfirm
|
||||
title="解绑这个账号?"
|
||||
description="会删除当前账号绑定,并清理本机的本地 session / partition 缓存。"
|
||||
ok-text="确认解绑"
|
||||
cancel-text="取消"
|
||||
@confirm="unbindAccount(record)"
|
||||
>
|
||||
<a-button
|
||||
danger
|
||||
size="small"
|
||||
:loading="unbindPendingAccountId === record.id"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
解绑
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user