优化-任务列表和详情页

This commit is contained in:
yml
2026-05-27 14:00:30 +08:00
parent 52b39c161a
commit 5583c60e72
5 changed files with 196 additions and 136 deletions
@@ -4,7 +4,6 @@ import { onMounted, ref } from "vue";
import { useAdminAction } from "@/composables/useAdminAction";
import { useAdminListPage } from "@/composables/useAdminListPage";
import AdminPageHeader from "@/components/admin/AdminPageHeader.vue";
import AdminStatusTag from "@/components/admin/AdminStatusTag.vue";
import {
closeAdminTask,
fetchAdminTasks,
@@ -127,6 +126,56 @@ function resetFilters() {
void loadTasks(1);
}
function resolveResourceStageLabel(status: string) {
const normalized = String(status || "").trim();
const labelMap: Record<string, string> = {
system_bound: "资源已准备",
pending_binding: "待准备资源",
pending_binding_prepare: "待准备资源",
retry_pending: "待重试",
manual_review: "人工处理",
binding_exception: "资源异常",
completed: "已完成",
not_started: "未开始",
};
return labelMap[normalized] || normalized || "-";
}
function resolveCustomerStageLabel(status: string) {
const normalized = String(status || "").trim();
const labelMap: Record<string, string> = {
not_started: "待打开链接",
waiting_user_claim: "待打开链接",
waiting_binding: "待客户绑定",
link_opened: "已打开链接",
user_binding: "客户绑定中",
binding_confirmed: "已提交",
binding_in_progress: "处理中",
binding_completed: "客户已完成",
binding_exception: "客户步骤异常",
completed: "已完成",
};
return labelMap[normalized] || normalized || "-";
}
function resolveTaskMainText(item: AdminTaskListItem) {
if (item.lastError) return "需要关注";
if (["failed", "manual_review", "retry_pending"].includes(item.status)) return "待处理";
if (["redeemed", "completed", "closed"].includes(item.status)) return "已完成";
if (item.userBindingStatus === "waiting_binding") return "等待客户绑定";
if (item.systemBindingStatus === "system_bound") return "资源已准备";
if (item.status === "pending_binding_prepare") return "待准备资源";
return item.status || "处理中";
}
function resolveTaskTone(item: AdminTaskListItem) {
if (item.lastError || ["failed", "manual_review", "retry_pending"].includes(item.status)) return "danger";
if (["redeemed", "completed", "closed"].includes(item.status)) return "success";
return "warning";
}
onMounted(() => {
void loadTasks(1);
});
@@ -136,7 +185,7 @@ onMounted(() => {
<div class="tasks-page list-page">
<AdminPageHeader
title="交付任务"
description="更紧凑地查看任务状态、角色绑定和异常信息。"
description="查看自动履约任务、客户步骤和异常处理。"
>
<template #extra>
<span class="total-badge"> {{ pagination.total }} 条任务</span>
@@ -148,7 +197,7 @@ onMounted(() => {
<div class="card-header">
<span class="card-title">查询任务</span>
<span class="card-desc"
>按状态任务号订单号角色标识SKU 和日期筛选</span
>按状态任务号订单号商品和日期筛选</span
>
</div>
</template>
@@ -175,13 +224,13 @@ onMounted(() => {
/>
<el-input
v-model="roleId"
placeholder="角色唯一标识"
placeholder="角色 ID"
clearable
@keyup.enter="loadTasks(1)"
/>
<el-input
v-model="skuCode"
placeholder="商品 SKU"
placeholder="商品名 / SKU"
clearable
@keyup.enter="loadTasks(1)"
/>
@@ -245,7 +294,7 @@ onMounted(() => {
size="small"
class="tasks-table data-table"
>
<el-table-column label="任务 / 商品" min-width="220">
<el-table-column label="任务 / 订单" min-width="230">
<template #default="{ row }">
<div class="cell-stack">
<RouterLink
@@ -263,6 +312,12 @@ onMounted(() => {
>重试 {{ row.retryCount }} </span
>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="商品" min-width="210">
<template #default="{ row }">
<div class="cell-stack">
<span class="cell-title" :title="row.skuName || row.skuCode">{{
row.skuName || row.skuCode
}}</span>
@@ -270,38 +325,19 @@ onMounted(() => {
</div>
</template>
</el-table-column>
<el-table-column label="进度" min-width="160">
<el-table-column label="状态" min-width="250">
<template #default="{ row }">
<div class="cell-stack">
<div class="progress-row">
<span class="progress-label">任务</span>
<AdminStatusTag :status="row.status" />
</div>
<div class="progress-row">
<span class="progress-label">系统</span>
<AdminStatusTag :status="row.systemBindingStatus" />
</div>
<div class="progress-row">
<span class="progress-label">完整</span>
<AdminStatusTag :status="row.userBindingStatus" />
</div>
</div>
</template>
</el-table-column>
<el-table-column label="角色信息" min-width="180">
<template #default="{ row }">
<div class="cell-stack">
<span
class="cell-title"
:title="row.roleName || row.roleId || '-'"
>{{ row.roleName || "-" }}</span
>
<span class="cell-subline" :title="row.roleId || '-'"
>角色ID {{ row.roleId || "-" }}</span
>
<span class="cell-subline"
>登录 {{ row.loginType || "-" }}</span
>
<div class="task-status-summary">
<span class="task-main-chip" :data-tone="resolveTaskTone(row)">
{{ resolveTaskMainText(row) }}
</span>
<p class="task-status-note">
客户 {{ resolveCustomerStageLabel(row.userBindingStatus) }}
· 资源 {{ resolveResourceStageLabel(row.systemBindingStatus) }}
</p>
<p v-if="row.roleName || row.roleId" class="task-status-note">
角色 {{ row.roleName || "-" }} / {{ row.roleId || "-" }}
</p>
</div>
</template>
</el-table-column>
@@ -343,21 +379,7 @@ onMounted(() => {
size="small"
type="danger"
plain
@click="
runAction({
id: row.taskId,
action: async () => {
const response = await retryAdminTask(row.taskId);
if (response.data.claimUrl)
lastClaimUrl = response.data.claimUrl;
},
successMessage: '任务已重试',
confirmText: `确认重试任务 ${row.taskNo} 吗?`,
onAfterAction: async () => {
await loadTasks();
},
})
"
@click="handleActionCommand('retry', row)"
>
重试
</el-button>
@@ -421,37 +443,54 @@ onMounted(() => {
width: 100%;
}
.progress-row {
display: flex;
align-items: center;
gap: var(--space-2);
.task-status-summary {
display: grid;
gap: 6px;
min-width: 0;
}
.progress-row :deep(.el-tag) {
min-width: 0;
max-width: calc(100% - 42px);
}
.progress-row :deep(.el-tag__content) {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.progress-label {
.task-main-chip {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 34px;
min-height: 22px;
padding: 0 7px;
width: fit-content;
min-height: 26px;
padding: 0 10px;
border-radius: var(--radius-full);
background: #f7f9fc;
border: 1px solid var(--border-default);
border: 1px solid transparent;
font-size: var(--text-xs);
font-weight: 800;
white-space: nowrap;
}
.task-main-chip[data-tone="success"] {
color: #0f766e;
background: var(--color-success-light);
border-color: #a6f4c5;
}
.task-main-chip[data-tone="warning"] {
color: #b54708;
background: var(--color-warning-light);
border-color: #fedf89;
}
.task-main-chip[data-tone="danger"] {
color: var(--color-danger-dark);
background: var(--color-danger-light);
border-color: #fecaca;
}
.task-status-note {
margin: 0;
color: var(--text-secondary);
font-size: 11px;
font-size: var(--text-xs);
line-height: 1.5;
}
.task-status-note + .task-status-note {
color: var(--text-primary);
font-weight: 700;
flex: 0 0 auto;
}
.cell-error {