新增风险项撤回操作并优化队列布局
This commit is contained in:
parent
e172d82eb5
commit
9359f11cd0
@ -28,6 +28,10 @@ export function updateRiskItem(itemId: number, payload: RiskItemUpdate) {
|
||||
return http.patch<ApiResponse<RiskItem>>(`/v1/risk-items/${itemId}`, payload);
|
||||
}
|
||||
|
||||
export function withdrawRiskItem(itemId: number) {
|
||||
return http.post<ApiResponse<null>>(`/v1/risk-items/${itemId}/withdraw`);
|
||||
}
|
||||
|
||||
export function reviewRiskItem(itemId: number, payload: ReviewAction) {
|
||||
return http.post<ApiResponse<ReviewRecord>>(
|
||||
`/v1/risk-items/${itemId}/review`,
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
RefreshCw,
|
||||
Save,
|
||||
SlidersHorizontal,
|
||||
Undo2,
|
||||
Upload,
|
||||
Users,
|
||||
X,
|
||||
@ -40,6 +41,7 @@ const statusFilter = ref("待审核");
|
||||
const expandedId = ref<number | null>(null);
|
||||
const editingId = ref<number | null>(null);
|
||||
const reviewingId = ref<number | null>(null);
|
||||
const withdrawingId = ref<number | null>(null);
|
||||
const ocrOpen = ref(false);
|
||||
const attachmentError = ref("");
|
||||
const modelConfig = ref<ModelWeightConfig | null>(null);
|
||||
@ -245,6 +247,20 @@ async function review(item: RiskItem, action: "通过" | "驳回") {
|
||||
reviewingId.value = null;
|
||||
}
|
||||
}
|
||||
async function withdraw(item: RiskItem) {
|
||||
if (withdrawingId.value !== null) return;
|
||||
if (!window.confirm("确认撤回该风险项吗?撤回后将不再显示在审核队列中。"))
|
||||
return;
|
||||
withdrawingId.value = item.id;
|
||||
try {
|
||||
await store.withdraw(item.id);
|
||||
if (expandedId.value === item.id) expandedId.value = null;
|
||||
} catch {
|
||||
// The exact API error is shown above the queue.
|
||||
} finally {
|
||||
withdrawingId.value = null;
|
||||
}
|
||||
}
|
||||
function formatDate(value: string | null) {
|
||||
if (!value) return "未记录时间";
|
||||
return new Intl.DateTimeFormat("zh-CN", {
|
||||
@ -374,22 +390,37 @@ onMounted(() => {
|
||||
class="review-item"
|
||||
:class="{ expanded: expandedId === item.id }"
|
||||
>
|
||||
<button
|
||||
class="review-summary"
|
||||
type="button"
|
||||
@click="toggleItem(item.id)"
|
||||
>
|
||||
<span class="source-badge" :class="sourceClass(item.sourceType)"
|
||||
><component :is="sourceIcon(item.sourceType)" :size="13" />{{
|
||||
item.sourceType
|
||||
}}</span
|
||||
><span class="review-title"
|
||||
><strong>{{ item.productName || "未填写产品名称" }}</strong
|
||||
><small
|
||||
>{{ item.productCategory || "未分类" }} ·
|
||||
{{ formatDate(item.submittedAt || item.createdAt) }}</small
|
||||
></span
|
||||
><span class="review-meta"
|
||||
<div class="review-summary">
|
||||
<button
|
||||
class="review-summary__content"
|
||||
type="button"
|
||||
@click="toggleItem(item.id)"
|
||||
>
|
||||
<span class="source-badge" :class="sourceClass(item.sourceType)"
|
||||
><component :is="sourceIcon(item.sourceType)" :size="13" />{{
|
||||
item.sourceType
|
||||
}}</span
|
||||
><span class="review-title"
|
||||
><strong>{{ item.productName || "未填写产品名称" }}</strong
|
||||
><small
|
||||
>{{ item.productCategory || "未分类" }} ·
|
||||
{{ formatDate(item.submittedAt || item.createdAt) }}</small
|
||||
></span
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
v-if="item.status === '待审核' || item.status === '待预审'"
|
||||
class="review-withdraw"
|
||||
type="button"
|
||||
:disabled="withdrawingId !== null"
|
||||
title="撤回风险项"
|
||||
@click="withdraw(item)"
|
||||
>
|
||||
<Undo2 :size="12" />{{
|
||||
withdrawingId === item.id ? "撤回中" : "撤回"
|
||||
}}
|
||||
</button>
|
||||
<span class="review-meta"
|
||||
><span
|
||||
class="level-tag"
|
||||
:class="levelClass(item.defectLevel)"
|
||||
@ -401,11 +432,17 @@ onMounted(() => {
|
||||
:class="item.status === '待审核' ? 'is-pending' : 'is-pre'"
|
||||
>{{ item.status }}</span
|
||||
></span
|
||||
><ChevronUp
|
||||
v-if="expandedId === item.id"
|
||||
:size="17"
|
||||
/><ChevronDown v-else :size="17" />
|
||||
</button>
|
||||
>
|
||||
<button
|
||||
class="review-toggle"
|
||||
type="button"
|
||||
:aria-label="expandedId === item.id ? '收起风险项' : '展开风险项'"
|
||||
@click="toggleItem(item.id)"
|
||||
>
|
||||
<ChevronUp v-if="expandedId === item.id" :size="17" />
|
||||
<ChevronDown v-else :size="17" />
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="expandedId === item.id" class="review-detail">
|
||||
<template v-if="editingId === item.id"
|
||||
><div class="edit-grid">
|
||||
@ -1068,16 +1105,68 @@ onMounted(() => {
|
||||
.review-summary {
|
||||
width: 100%;
|
||||
gap: 11px;
|
||||
border: 0;
|
||||
padding: 14px 5px;
|
||||
padding: 8px 5px;
|
||||
background: transparent;
|
||||
color: var(--color-text);
|
||||
text-align: left;
|
||||
}
|
||||
.review-summary:hover,
|
||||
.review-item.expanded .review-summary {
|
||||
background: var(--color-bg-warm);
|
||||
}
|
||||
.review-summary__content {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
border: 0;
|
||||
padding: 6px 0;
|
||||
background: transparent;
|
||||
color: var(--color-text);
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
.review-withdraw {
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
min-height: 26px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 5px;
|
||||
padding: 0 8px;
|
||||
background: var(--color-surface);
|
||||
color: var(--color-text-muted);
|
||||
font: inherit;
|
||||
font-size: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.review-withdraw:hover:not(:disabled) {
|
||||
border-color: #fecaca;
|
||||
background: #fef2f2;
|
||||
color: var(--color-danger);
|
||||
}
|
||||
.review-withdraw:disabled {
|
||||
cursor: wait;
|
||||
opacity: 0.55;
|
||||
}
|
||||
.review-toggle {
|
||||
display: grid;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
flex: 0 0 28px;
|
||||
place-items: center;
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
}
|
||||
.review-toggle:hover {
|
||||
background: var(--color-surface);
|
||||
}
|
||||
.source-badge {
|
||||
display: inline-flex;
|
||||
min-width: 52px;
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
fetchPendingRiskItems,
|
||||
reviewRiskItem,
|
||||
updateRiskItem,
|
||||
withdrawRiskItem,
|
||||
} from "@/api/riskItems";
|
||||
import type {
|
||||
RiskItem,
|
||||
@ -76,6 +77,21 @@ export const useReviewStore = defineStore("review", () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function withdraw(itemId: number) {
|
||||
error.value = null;
|
||||
try {
|
||||
await withdrawRiskItem(itemId);
|
||||
items.value = items.value.filter((item) => item.id !== itemId);
|
||||
} catch (reason) {
|
||||
error.value = axios.isAxiosError(reason)
|
||||
? String(reason.response?.data?.message || "撤回失败")
|
||||
: reason instanceof Error
|
||||
? reason.message
|
||||
: "撤回失败";
|
||||
throw reason;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
items,
|
||||
loading,
|
||||
@ -85,5 +101,6 @@ export const useReviewStore = defineStore("review", () => {
|
||||
submit,
|
||||
update,
|
||||
review,
|
||||
withdraw,
|
||||
};
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user