feat: add content report governance
This commit is contained in:
@@ -190,5 +190,80 @@ export const ContentService = {
|
||||
method: 'POST',
|
||||
body: { reason }
|
||||
});
|
||||
},
|
||||
async listContentReports({
|
||||
page,
|
||||
limit,
|
||||
id,
|
||||
tenant_id,
|
||||
tenant_code,
|
||||
tenant_name,
|
||||
content_id,
|
||||
content_title,
|
||||
reporter_id,
|
||||
reporter_name,
|
||||
handled_by,
|
||||
handled_by_name,
|
||||
reason,
|
||||
keyword,
|
||||
status,
|
||||
created_at_from,
|
||||
created_at_to,
|
||||
handled_at_from,
|
||||
handled_at_to,
|
||||
sortField,
|
||||
sortOrder
|
||||
} = {}) {
|
||||
const iso = (d) => {
|
||||
if (!d) return undefined;
|
||||
const date = d instanceof Date ? d : new Date(d);
|
||||
if (Number.isNaN(date.getTime())) return undefined;
|
||||
return date.toISOString();
|
||||
};
|
||||
|
||||
const query = {
|
||||
page,
|
||||
limit,
|
||||
id,
|
||||
tenant_id,
|
||||
tenant_code,
|
||||
tenant_name,
|
||||
content_id,
|
||||
content_title,
|
||||
reporter_id,
|
||||
reporter_name,
|
||||
handled_by,
|
||||
handled_by_name,
|
||||
reason,
|
||||
keyword,
|
||||
status,
|
||||
created_at_from: iso(created_at_from),
|
||||
created_at_to: iso(created_at_to),
|
||||
handled_at_from: iso(handled_at_from),
|
||||
handled_at_to: iso(handled_at_to)
|
||||
};
|
||||
if (sortField && sortOrder) {
|
||||
if (sortOrder === 1) query.asc = sortField;
|
||||
if (sortOrder === -1) query.desc = sortField;
|
||||
}
|
||||
|
||||
const data = await requestJson('/super/v1/content-reports', { query });
|
||||
return {
|
||||
page: data?.page ?? page ?? 1,
|
||||
limit: data?.limit ?? limit ?? 10,
|
||||
total: data?.total ?? 0,
|
||||
items: normalizeItems(data?.items)
|
||||
};
|
||||
},
|
||||
async processContentReport(id, { action, content_action, reason } = {}) {
|
||||
if (!id) throw new Error('id is required');
|
||||
return requestJson(`/super/v1/content-reports/${id}/process`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
action,
|
||||
content_action,
|
||||
reason
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -55,11 +55,43 @@ const commentCreatedAtTo = ref(null);
|
||||
const commentSortField = ref('created_at');
|
||||
const commentSortOrder = ref(-1);
|
||||
|
||||
const reportLoading = ref(false);
|
||||
const reports = ref([]);
|
||||
const reportTotalRecords = ref(0);
|
||||
const reportPage = ref(1);
|
||||
const reportRows = ref(10);
|
||||
const reportID = ref(null);
|
||||
const reportTenantID = ref(null);
|
||||
const reportTenantCode = ref('');
|
||||
const reportTenantName = ref('');
|
||||
const reportContentID = ref(null);
|
||||
const reportContentTitle = ref('');
|
||||
const reportReporterID = ref(null);
|
||||
const reportReporterName = ref('');
|
||||
const reportHandledBy = ref(null);
|
||||
const reportHandledByName = ref('');
|
||||
const reportReason = ref('');
|
||||
const reportKeyword = ref('');
|
||||
const reportStatus = ref('pending');
|
||||
const reportCreatedAtFrom = ref(null);
|
||||
const reportCreatedAtTo = ref(null);
|
||||
const reportHandledAtFrom = ref(null);
|
||||
const reportHandledAtTo = ref(null);
|
||||
const reportSortField = ref('created_at');
|
||||
const reportSortOrder = ref(-1);
|
||||
|
||||
const commentDeleteDialogVisible = ref(false);
|
||||
const commentDeleteLoading = ref(false);
|
||||
const commentDeleteReason = ref('');
|
||||
const commentDeleteTarget = ref(null);
|
||||
|
||||
const reportProcessDialogVisible = ref(false);
|
||||
const reportProcessSubmitting = ref(false);
|
||||
const reportProcessAction = ref('approve');
|
||||
const reportProcessContentAction = ref('unpublish');
|
||||
const reportProcessReason = ref('');
|
||||
const reportProcessTarget = ref(null);
|
||||
|
||||
const reviewDialogVisible = ref(false);
|
||||
const reviewSubmitting = ref(false);
|
||||
const reviewAction = ref('approve');
|
||||
@@ -92,6 +124,24 @@ const commentStatusOptions = [
|
||||
{ label: '已删除', value: 'deleted' }
|
||||
];
|
||||
|
||||
const reportStatusOptions = [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '待处理', value: 'pending' },
|
||||
{ label: '已成立', value: 'approved' },
|
||||
{ label: '已驳回', value: 'rejected' }
|
||||
];
|
||||
|
||||
const reportActionOptions = [
|
||||
{ label: '通过举报', value: 'approve' },
|
||||
{ label: '驳回举报', value: 'reject' }
|
||||
];
|
||||
|
||||
const reportContentActionOptions = [
|
||||
{ label: '不处理内容', value: 'ignore' },
|
||||
{ label: '下架内容', value: 'unpublish' },
|
||||
{ label: '封禁内容', value: 'block' }
|
||||
];
|
||||
|
||||
function getQueryValue(value) {
|
||||
if (Array.isArray(value)) return value[0];
|
||||
return value ?? null;
|
||||
@@ -162,6 +212,30 @@ function resetCommentFilters() {
|
||||
commentRows.value = 10;
|
||||
}
|
||||
|
||||
function resetReportFilters() {
|
||||
reportID.value = null;
|
||||
reportTenantID.value = null;
|
||||
reportTenantCode.value = '';
|
||||
reportTenantName.value = '';
|
||||
reportContentID.value = null;
|
||||
reportContentTitle.value = '';
|
||||
reportReporterID.value = null;
|
||||
reportReporterName.value = '';
|
||||
reportHandledBy.value = null;
|
||||
reportHandledByName.value = '';
|
||||
reportReason.value = '';
|
||||
reportKeyword.value = '';
|
||||
reportStatus.value = 'pending';
|
||||
reportCreatedAtFrom.value = null;
|
||||
reportCreatedAtTo.value = null;
|
||||
reportHandledAtFrom.value = null;
|
||||
reportHandledAtTo.value = null;
|
||||
reportSortField.value = 'created_at';
|
||||
reportSortOrder.value = -1;
|
||||
reportPage.value = 1;
|
||||
reportRows.value = 10;
|
||||
}
|
||||
|
||||
function applyRouteQuery(query) {
|
||||
resetFilters();
|
||||
|
||||
@@ -241,8 +315,51 @@ function formatCommentContent(value) {
|
||||
return `${text.slice(0, 60)}...`;
|
||||
}
|
||||
|
||||
function getReportStatusSeverity(value) {
|
||||
switch (value) {
|
||||
case 'approved':
|
||||
return 'success';
|
||||
case 'rejected':
|
||||
return 'secondary';
|
||||
case 'pending':
|
||||
default:
|
||||
return 'warn';
|
||||
}
|
||||
}
|
||||
|
||||
function getReportStatusLabel(value) {
|
||||
switch (value) {
|
||||
case 'approved':
|
||||
return '已成立';
|
||||
case 'rejected':
|
||||
return '已驳回';
|
||||
case 'pending':
|
||||
default:
|
||||
return '待处理';
|
||||
}
|
||||
}
|
||||
|
||||
function formatReportDetail(value) {
|
||||
const text = String(value || '');
|
||||
if (text.length <= 80) return text || '-';
|
||||
return `${text.slice(0, 80)}...`;
|
||||
}
|
||||
|
||||
function getReportActionLabel(value) {
|
||||
switch (value) {
|
||||
case 'block':
|
||||
return '封禁内容';
|
||||
case 'unpublish':
|
||||
return '下架内容';
|
||||
case 'ignore':
|
||||
default:
|
||||
return '不处理内容';
|
||||
}
|
||||
}
|
||||
|
||||
const selectedCount = computed(() => selectedContents.value.length);
|
||||
const reviewTargetCount = computed(() => reviewTargetIDs.value.length);
|
||||
const reportProcessNeedsContentAction = computed(() => reportProcessAction.value === 'approve');
|
||||
|
||||
async function loadContents() {
|
||||
loading.value = true;
|
||||
@@ -430,6 +547,100 @@ async function confirmCommentDelete() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadReports() {
|
||||
reportLoading.value = true;
|
||||
try {
|
||||
const result = await ContentService.listContentReports({
|
||||
page: reportPage.value,
|
||||
limit: reportRows.value,
|
||||
id: reportID.value || undefined,
|
||||
tenant_id: reportTenantID.value || undefined,
|
||||
tenant_code: reportTenantCode.value,
|
||||
tenant_name: reportTenantName.value,
|
||||
content_id: reportContentID.value || undefined,
|
||||
content_title: reportContentTitle.value,
|
||||
reporter_id: reportReporterID.value || undefined,
|
||||
reporter_name: reportReporterName.value,
|
||||
handled_by: reportHandledBy.value || undefined,
|
||||
handled_by_name: reportHandledByName.value,
|
||||
reason: reportReason.value,
|
||||
keyword: reportKeyword.value,
|
||||
status: reportStatus.value || undefined,
|
||||
created_at_from: reportCreatedAtFrom.value || undefined,
|
||||
created_at_to: reportCreatedAtTo.value || undefined,
|
||||
handled_at_from: reportHandledAtFrom.value || undefined,
|
||||
handled_at_to: reportHandledAtTo.value || undefined,
|
||||
sortField: reportSortField.value,
|
||||
sortOrder: reportSortOrder.value
|
||||
});
|
||||
reports.value = result.items;
|
||||
reportTotalRecords.value = result.total;
|
||||
} catch (error) {
|
||||
toast.add({ severity: 'error', summary: '加载失败', detail: error?.message || '无法加载举报列表', life: 4000 });
|
||||
} finally {
|
||||
reportLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onReportSearch() {
|
||||
reportPage.value = 1;
|
||||
loadReports();
|
||||
}
|
||||
|
||||
function onReportReset() {
|
||||
resetReportFilters();
|
||||
loadReports();
|
||||
}
|
||||
|
||||
function onReportPage(event) {
|
||||
reportPage.value = (event.page ?? 0) + 1;
|
||||
reportRows.value = event.rows ?? reportRows.value;
|
||||
loadReports();
|
||||
}
|
||||
|
||||
function onReportSort(event) {
|
||||
reportSortField.value = event.sortField ?? reportSortField.value;
|
||||
reportSortOrder.value = event.sortOrder ?? reportSortOrder.value;
|
||||
loadReports();
|
||||
}
|
||||
|
||||
function openReportProcessDialog(report) {
|
||||
reportProcessTarget.value = report;
|
||||
reportProcessAction.value = 'approve';
|
||||
reportProcessContentAction.value = 'unpublish';
|
||||
reportProcessReason.value = '';
|
||||
reportProcessDialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function confirmReportProcess() {
|
||||
const id = Number(reportProcessTarget.value?.id ?? 0);
|
||||
if (!id) return;
|
||||
|
||||
const action = reportProcessAction.value;
|
||||
const contentAction = action === 'approve' ? reportProcessContentAction.value : 'ignore';
|
||||
const reason = reportProcessReason.value.trim();
|
||||
if (action === 'reject' && !reason) {
|
||||
toast.add({ severity: 'warn', summary: '请输入原因', detail: '驳回举报时需填写原因', life: 3000 });
|
||||
return;
|
||||
}
|
||||
|
||||
reportProcessSubmitting.value = true;
|
||||
try {
|
||||
await ContentService.processContentReport(id, {
|
||||
action,
|
||||
content_action: contentAction,
|
||||
reason: reason || undefined
|
||||
});
|
||||
toast.add({ severity: 'success', summary: '已处理', detail: `举报ID: ${id}`, life: 3000 });
|
||||
reportProcessDialogVisible.value = false;
|
||||
await loadReports();
|
||||
} catch (error) {
|
||||
toast.add({ severity: 'error', summary: '处理失败', detail: error?.message || '无法处理举报', life: 4000 });
|
||||
} finally {
|
||||
reportProcessSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const unpublishDialogVisible = ref(false);
|
||||
const unpublishLoading = ref(false);
|
||||
const unpublishItem = ref(null);
|
||||
@@ -465,6 +676,8 @@ watch(
|
||||
(value) => {
|
||||
if (value === 'comments') {
|
||||
loadComments();
|
||||
} else if (value === 'reports') {
|
||||
loadReports();
|
||||
} else if (value === 'contents') {
|
||||
loadContents();
|
||||
}
|
||||
@@ -497,6 +710,7 @@ watch(
|
||||
<TabList>
|
||||
<Tab value="contents">内容列表</Tab>
|
||||
<Tab value="comments">评论治理</Tab>
|
||||
<Tab value="reports">举报治理</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel value="contents">
|
||||
@@ -792,6 +1006,167 @@ watch(
|
||||
</DataTable>
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel value="reports">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex flex-col">
|
||||
<span class="font-medium">举报列表</span>
|
||||
<span class="text-muted-color">跨租户内容举报处理</span>
|
||||
</div>
|
||||
<Button label="刷新" icon="pi pi-refresh" severity="secondary" @click="loadReports" :disabled="reportLoading" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<SearchPanel :loading="reportLoading" @search="onReportSearch" @reset="onReportReset">
|
||||
<SearchField label="ReportID">
|
||||
<InputNumber v-model="reportID" :min="1" placeholder="精确匹配" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="TenantID">
|
||||
<InputNumber v-model="reportTenantID" :min="1" placeholder="精确匹配" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="TenantCode">
|
||||
<InputText v-model="reportTenantCode" placeholder="模糊匹配" class="w-full" @keyup.enter="onReportSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="TenantName">
|
||||
<InputText v-model="reportTenantName" placeholder="模糊匹配" class="w-full" @keyup.enter="onReportSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="ContentID">
|
||||
<InputNumber v-model="reportContentID" :min="1" placeholder="精确匹配" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="ContentTitle">
|
||||
<InputText v-model="reportContentTitle" placeholder="内容标题" class="w-full" @keyup.enter="onReportSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="ReporterID">
|
||||
<InputNumber v-model="reportReporterID" :min="1" placeholder="精确匹配" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="ReporterName">
|
||||
<InputText v-model="reportReporterName" placeholder="模糊匹配" class="w-full" @keyup.enter="onReportSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="处理人ID">
|
||||
<InputNumber v-model="reportHandledBy" :min="1" placeholder="精确匹配" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="处理人">
|
||||
<InputText v-model="reportHandledByName" placeholder="模糊匹配" class="w-full" @keyup.enter="onReportSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="原因">
|
||||
<InputText v-model="reportReason" placeholder="举报原因" class="w-full" @keyup.enter="onReportSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="关键字">
|
||||
<InputText v-model="reportKeyword" placeholder="举报描述关键字" class="w-full" @keyup.enter="onReportSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="状态">
|
||||
<Select v-model="reportStatus" :options="reportStatusOptions" optionLabel="label" optionValue="value" placeholder="请选择" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="举报时间 From">
|
||||
<DatePicker v-model="reportCreatedAtFrom" showIcon showButtonBar placeholder="开始时间" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="举报时间 To">
|
||||
<DatePicker v-model="reportCreatedAtTo" showIcon showButtonBar placeholder="结束时间" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="处理时间 From">
|
||||
<DatePicker v-model="reportHandledAtFrom" showIcon showButtonBar placeholder="开始时间" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="处理时间 To">
|
||||
<DatePicker v-model="reportHandledAtTo" showIcon showButtonBar placeholder="结束时间" class="w-full" />
|
||||
</SearchField>
|
||||
</SearchPanel>
|
||||
|
||||
<DataTable
|
||||
:value="reports"
|
||||
dataKey="id"
|
||||
:loading="reportLoading"
|
||||
lazy
|
||||
:paginator="true"
|
||||
:rows="reportRows"
|
||||
:totalRecords="reportTotalRecords"
|
||||
:first="(reportPage - 1) * reportRows"
|
||||
:rowsPerPageOptions="[10, 20, 50, 100]"
|
||||
sortMode="single"
|
||||
:sortField="reportSortField"
|
||||
:sortOrder="reportSortOrder"
|
||||
@page="onReportPage"
|
||||
@sort="onReportSort"
|
||||
currentPageReportTemplate="显示第 {first} - {last} 条,共 {totalRecords} 条"
|
||||
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
|
||||
scrollable
|
||||
scrollHeight="640px"
|
||||
responsiveLayout="scroll"
|
||||
>
|
||||
<Column field="id" header="举报ID" sortable style="min-width: 8rem" />
|
||||
<Column header="内容" style="min-width: 20rem">
|
||||
<template #body="{ data }">
|
||||
<div class="flex flex-col">
|
||||
<span class="font-medium truncate max-w-[260px]">{{ data?.content_title || '-' }}</span>
|
||||
<span class="text-xs text-muted-color">ContentID: {{ data?.content_id ?? '-' }}</span>
|
||||
<Tag v-if="data?.content_status" :value="data.content_status" :severity="getContentStatusSeverity(data?.content_status)" class="mt-1 w-fit" />
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="租户" style="min-width: 16rem">
|
||||
<template #body="{ data }">
|
||||
<div class="flex flex-col">
|
||||
<span class="font-medium">{{ data?.tenant_name || '-' }}</span>
|
||||
<span class="text-xs text-muted-color">Code: {{ data?.tenant_code || '-' }} / ID: {{ data?.tenant_id ?? '-' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="作者" style="min-width: 14rem">
|
||||
<template #body="{ data }">
|
||||
<router-link v-if="(data?.content_owner_id ?? 0) > 0" class="inline-flex items-center gap-1 font-medium text-primary hover:underline" :to="`/superadmin/users/${data.content_owner_id}`">
|
||||
<span class="truncate max-w-[200px]">{{ data?.content_owner_name || `ID:${data?.content_owner_id ?? '-'}` }}</span>
|
||||
<i class="pi pi-external-link text-xs" />
|
||||
</router-link>
|
||||
<span v-else class="font-medium text-muted-color">-</span>
|
||||
<div class="text-xs text-muted-color">ID: {{ data?.content_owner_id ?? '-' }}</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="举报人" style="min-width: 14rem">
|
||||
<template #body="{ data }">
|
||||
<router-link v-if="(data?.reporter_id ?? 0) > 0" class="inline-flex items-center gap-1 font-medium text-primary hover:underline" :to="`/superadmin/users/${data.reporter_id}`">
|
||||
<span class="truncate max-w-[200px]">{{ data?.reporter_name || `ID:${data?.reporter_id ?? '-'}` }}</span>
|
||||
<i class="pi pi-external-link text-xs" />
|
||||
</router-link>
|
||||
<span v-else class="font-medium text-muted-color">-</span>
|
||||
<div class="text-xs text-muted-color">ID: {{ data?.reporter_id ?? '-' }}</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="原因" style="min-width: 12rem">
|
||||
<template #body="{ data }">
|
||||
<span class="text-sm">{{ data?.reason || '-' }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="描述" style="min-width: 22rem">
|
||||
<template #body="{ data }">
|
||||
<span class="text-sm">{{ formatReportDetail(data?.detail) }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="status" header="状态" sortable style="min-width: 10rem">
|
||||
<template #body="{ data }">
|
||||
<Tag :value="getReportStatusLabel(data?.status)" :severity="getReportStatusSeverity(data?.status)" />
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="created_at" header="举报时间" sortable style="min-width: 14rem">
|
||||
<template #body="{ data }">
|
||||
{{ formatDate(data?.created_at) }}
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="处理结果" style="min-width: 20rem">
|
||||
<template #body="{ data }">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-sm">{{ data?.handled_action ? getReportActionLabel(data?.handled_action) : '-' }}</span>
|
||||
<span class="text-xs text-muted-color">处理人: {{ data?.handled_by_name || '-' }}</span>
|
||||
<span class="text-xs text-muted-color">处理时间: {{ formatDate(data?.handled_at) }}</span>
|
||||
<span v-if="data?.handled_reason" class="text-xs text-muted-color truncate max-w-[260px]">说明: {{ data?.handled_reason }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="操作" style="min-width: 10rem">
|
||||
<template #body="{ data }">
|
||||
<Button label="处理" icon="pi pi-shield" text size="small" class="p-0" :disabled="data?.status !== 'pending'" @click="openReportProcessDialog(data)" />
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</div>
|
||||
@@ -838,6 +1213,35 @@ watch(
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<Dialog v-model:visible="reportProcessDialogVisible" :modal="true" :style="{ width: '560px' }">
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-medium">处理举报</span>
|
||||
<span class="text-muted-color truncate max-w-[280px]">{{ reportProcessTarget?.content_title ?? '-' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="text-sm text-muted-color">举报人:{{ reportProcessTarget?.reporter_name || '-' }} / 原因:{{ reportProcessTarget?.reason || '-' }}</div>
|
||||
<div>
|
||||
<label class="block font-medium mb-2">处理动作</label>
|
||||
<Select v-model="reportProcessAction" :options="reportActionOptions" optionLabel="label" optionValue="value" class="w-full" />
|
||||
</div>
|
||||
<div v-if="reportProcessNeedsContentAction">
|
||||
<label class="block font-medium mb-2">内容处置</label>
|
||||
<Select v-model="reportProcessContentAction" :options="reportContentActionOptions" optionLabel="label" optionValue="value" class="w-full" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block font-medium mb-2">处理说明</label>
|
||||
<InputText v-model="reportProcessReason" placeholder="可选,便于审计与通知" class="w-full" />
|
||||
</div>
|
||||
<div class="text-sm text-muted-color">处理后会记录审计,并视情况通知作者。</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button label="取消" icon="pi pi-times" text @click="reportProcessDialogVisible = false" :disabled="reportProcessSubmitting" />
|
||||
<Button label="确认处理" icon="pi pi-check" severity="success" @click="confirmReportProcess" :loading="reportProcessSubmitting" :disabled="reportProcessSubmitting || (reportProcessAction === 'reject' && !reportProcessReason.trim())" />
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<Dialog v-model:visible="commentDeleteDialogVisible" :modal="true" :style="{ width: '520px' }">
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
|
||||
Reference in New Issue
Block a user