feat: 完善养成管理后台功能
This commit is contained in:
@@ -1,7 +1,29 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
export function ContentList(params) { return http.request({ url: '/cultivate/content/list', method: 'get', params }); }
|
||||
export function ContentReview(params) { return http.request({ url: '/cultivate/content/review', method: 'POST', params }); }
|
||||
export function ReportList(params) { return http.request({ url: '/cultivate/reports/list', method: 'get', params }); }
|
||||
export function ReportHandle(params) { return http.request({ url: '/cultivate/reports/handle', method: 'POST', params }); }
|
||||
export function OperationLogList(params) { return http.request({ url: '/cultivate/logs/list', method: 'get', params }); }
|
||||
export function ContentList(params) {
|
||||
return http.request({ url: '/cultivate/content/list', method: 'get', params });
|
||||
}
|
||||
export function ContentView(params) {
|
||||
return http.request({ url: '/cultivate/content/view', method: 'get', params });
|
||||
}
|
||||
export function ContentReview(params) {
|
||||
return http.request({ url: '/cultivate/content/review', method: 'POST', params });
|
||||
}
|
||||
export function ContentBatchReview(params) {
|
||||
return http.request({ url: '/cultivate/content/batch-review', method: 'POST', params });
|
||||
}
|
||||
export function ReportList(params) {
|
||||
return http.request({ url: '/cultivate/reports/list', method: 'get', params });
|
||||
}
|
||||
export function ReportView(params) {
|
||||
return http.request({ url: '/cultivate/reports/view', method: 'get', params });
|
||||
}
|
||||
export function ReportHandle(params) {
|
||||
return http.request({ url: '/cultivate/reports/handle', method: 'POST', params });
|
||||
}
|
||||
export function OperationLogList(params) {
|
||||
return http.request({ url: '/cultivate/logs/list', method: 'get', params });
|
||||
}
|
||||
export function CultivateDashboard() {
|
||||
return http.request({ url: '/cultivate/dashboard', method: 'get' });
|
||||
}
|
||||
|
||||
@@ -1,37 +1,236 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header"><n-card :bordered="false" title="内容审核">审核物友动态与评论,支持拒绝、下架和恢复。</n-card></div>
|
||||
<div class="n-layout-page-header"
|
||||
><n-card :bordered="false" title="内容审核"
|
||||
>审核物友动态与评论,支持拒绝、下架和恢复。</n-card
|
||||
></div
|
||||
>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicForm ref="searchFormRef" @register="register" @submit="reloadTable" @reset="reloadTable" @keyup.enter="reloadTable" />
|
||||
<BasicTable ref="actionRef" :columns="columns" :request="loadDataTable" :row-key="(row) => row.id" :actionColumn="actionColumn" :scroll-x="1450" />
|
||||
<BasicForm
|
||||
ref="searchFormRef"
|
||||
@register="register"
|
||||
@submit="reloadTable"
|
||||
@reset="reloadTable"
|
||||
@keyup.enter="reloadTable"
|
||||
/>
|
||||
<BasicTable
|
||||
ref="actionRef"
|
||||
:openChecked="true"
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
:actionColumn="actionColumn"
|
||||
:scroll-x="1450"
|
||||
:checked-row-keys="checkedIds"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button
|
||||
type="primary"
|
||||
:disabled="checkedIds.length === 0"
|
||||
v-if="hasPermission(['/cultivate/content/batch-review'])"
|
||||
@click="openBatchReview"
|
||||
>批量审核({{ checkedIds.length }})</n-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</n-card>
|
||||
<n-modal v-model:show="showReview" preset="dialog" title="内容审核" positive-text="确认" negative-text="取消" @positive-click="submitReview">
|
||||
<n-modal
|
||||
v-model:show="showReview"
|
||||
preset="dialog"
|
||||
:title="selected ? '内容审核' : '批量内容审核'"
|
||||
positive-text="确认"
|
||||
negative-text="取消"
|
||||
@positive-click="submitReview"
|
||||
>
|
||||
<n-space vertical>
|
||||
<n-select v-model:value="reviewForm.action" :options="reviewOptions" placeholder="请选择审核动作" />
|
||||
<n-input v-model:value="reviewForm.reason" type="textarea" placeholder="拒绝或下架时必须填写原因" />
|
||||
<n-select
|
||||
v-model:value="reviewForm.action"
|
||||
:options="reviewOptions"
|
||||
placeholder="请选择审核动作"
|
||||
/>
|
||||
<n-input
|
||||
v-model:value="reviewForm.reason"
|
||||
type="textarea"
|
||||
placeholder="拒绝或下架时必须填写原因"
|
||||
/>
|
||||
</n-space>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="showDetail" preset="card" title="内容详情" class="detail-modal">
|
||||
<n-spin :show="detailLoading">
|
||||
<n-descriptions v-if="detail" bordered :column="2" label-placement="left">
|
||||
<n-descriptions-item label="内容ID">{{ detail.id }}</n-descriptions-item>
|
||||
<n-descriptions-item label="作者"
|
||||
>{{ detail.authorNickname || '-' }}({{ detail.authorId }})</n-descriptions-item
|
||||
>
|
||||
<n-descriptions-item label="状态">{{ detail.status }}</n-descriptions-item>
|
||||
<n-descriptions-item label="审核状态">{{
|
||||
detail.auditStatus || '-'
|
||||
}}</n-descriptions-item>
|
||||
<n-descriptions-item label="正文" :span="2">{{
|
||||
detail.contentText || '-'
|
||||
}}</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
<n-divider title-placement="left">媒体素材</n-divider>
|
||||
<n-data-table :columns="mediaColumns" :data="detail?.media || []" :pagination="false" />
|
||||
<n-divider title-placement="left">审核记录</n-divider>
|
||||
<n-data-table :columns="auditColumns" :data="detail?.audits || []" :pagination="false" />
|
||||
<n-divider title-placement="left">关联举报</n-divider>
|
||||
<n-data-table
|
||||
:columns="reportColumns"
|
||||
:data="detail?.relatedReports || []"
|
||||
:pagination="false"
|
||||
/>
|
||||
</n-spin>
|
||||
</n-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, onMounted, reactive, ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
import { ContentList, ContentReview } from '@/api/cultivate/content';
|
||||
import { columns, ContentState, loadOptions, schemas } from './model';
|
||||
import { h, onMounted, reactive, ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
import {
|
||||
ContentBatchReview,
|
||||
ContentList,
|
||||
ContentReview,
|
||||
ContentView,
|
||||
} from '@/api/cultivate/content';
|
||||
import { columns, ContentState, loadOptions, schemas } from './model';
|
||||
|
||||
const actionRef = ref(); const searchFormRef = ref<any>({}); const showReview = ref(false); const selected = ref<ContentState>();
|
||||
const message = useMessage(); const { hasPermission } = usePermission();
|
||||
const reviewForm = reactive({ action: 'approve', reason: '' });
|
||||
const reviewOptions = [{ label: '审核通过', value: 'approve' }, { label: '审核拒绝', value: 'reject' }, { label: '下架', value: 'take_down' }, { label: '恢复发布', value: 'restore' }];
|
||||
const [register] = useForm({ gridProps: { cols: '1 s:1 m:2 l:4 xl:4 2xl:4' }, labelWidth: 90, schemas });
|
||||
const loadDataTable = (res) => ContentList({ ...searchFormRef.value?.formModel, ...res });
|
||||
const reloadTable = () => actionRef.value?.reload();
|
||||
function openReview(row: ContentState) { selected.value = row; reviewForm.action = row.status === 'reviewing' ? 'approve' : row.status === 'taken_down' ? 'restore' : 'take_down'; reviewForm.reason = ''; showReview.value = true; }
|
||||
async function submitReview() { if (!selected.value) return false; if ((reviewForm.action === 'reject' || reviewForm.action === 'take_down') && !reviewForm.reason.trim()) { message.warning('请填写原因'); return false; } await ContentReview({ objectType: selected.value.objectType, id: selected.value.id, ...reviewForm }); message.success('处理成功'); reloadTable(); return true; }
|
||||
const actionColumn = reactive({ width: 120, title: '操作', key: 'action', fixed: 'right', render(row: ContentState) { return h(TableAction as any, { style: 'button', actions: [{ label: '审核处理', onClick: () => openReview(row), auth: ['/cultivate/content/review'], disabled: !hasPermission(['/cultivate/content/review']) }] }); } });
|
||||
onMounted(loadOptions);
|
||||
const actionRef = ref();
|
||||
const searchFormRef = ref<any>({});
|
||||
const showReview = ref(false);
|
||||
const selected = ref<ContentState>();
|
||||
const checkedIds = ref<Array<string | number>>([]);
|
||||
const message = useMessage();
|
||||
const { hasPermission } = usePermission();
|
||||
const reviewForm = reactive({ action: 'approve', reason: '' });
|
||||
const showDetail = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
const detail = ref<any>();
|
||||
const mediaColumns = [
|
||||
{ title: '类型', key: 'mediaType' },
|
||||
{ title: '文件ID', key: 'fileId' },
|
||||
{ title: '缩略图ID', key: 'thumbnailFileId' },
|
||||
{ title: '审核状态', key: 'auditStatus' },
|
||||
];
|
||||
const auditColumns = [
|
||||
{ title: '来源', key: 'auditSource' },
|
||||
{
|
||||
title: '状态变化',
|
||||
key: 'statusAfter',
|
||||
render: (row) => `${row.statusBefore || '-'} → ${row.statusAfter}`,
|
||||
},
|
||||
{ title: '结果', key: 'resultCode' },
|
||||
{ title: '操作人', key: 'operatorId' },
|
||||
{ title: '时间', key: 'occurredAt' },
|
||||
];
|
||||
const reportColumns = [
|
||||
{ title: '举报ID', key: 'id' },
|
||||
{ title: '原因', key: 'reasonCode' },
|
||||
{ title: '状态', key: 'status' },
|
||||
{ title: '时间', key: 'createdAt' },
|
||||
];
|
||||
const reviewOptions = [
|
||||
{ label: '审核通过', value: 'approve' },
|
||||
{ label: '审核拒绝', value: 'reject' },
|
||||
{ label: '下架', value: 'take_down' },
|
||||
{ label: '恢复发布', value: 'restore' },
|
||||
];
|
||||
const [register] = useForm({
|
||||
gridProps: { cols: '1 s:1 m:2 l:4 xl:4 2xl:4' },
|
||||
labelWidth: 90,
|
||||
schemas,
|
||||
});
|
||||
const loadDataTable = (res) => ContentList({ ...searchFormRef.value?.formModel, ...res });
|
||||
const reloadTable = () => actionRef.value?.reload();
|
||||
function openReview(row: ContentState) {
|
||||
selected.value = row;
|
||||
reviewForm.action =
|
||||
row.status === 'reviewing'
|
||||
? 'approve'
|
||||
: row.status === 'taken_down'
|
||||
? 'restore'
|
||||
: 'take_down';
|
||||
reviewForm.reason = '';
|
||||
showReview.value = true;
|
||||
}
|
||||
function onCheckedRow(rowKeys: Array<string | number>) {
|
||||
checkedIds.value = rowKeys;
|
||||
}
|
||||
function openBatchReview() {
|
||||
selected.value = undefined;
|
||||
reviewForm.action = 'approve';
|
||||
reviewForm.reason = '';
|
||||
showReview.value = true;
|
||||
}
|
||||
async function openDetail(row: ContentState) {
|
||||
showDetail.value = true;
|
||||
detailLoading.value = true;
|
||||
try {
|
||||
detail.value = await ContentView({ objectType: row.objectType, id: row.id });
|
||||
} finally {
|
||||
detailLoading.value = false;
|
||||
}
|
||||
}
|
||||
async function submitReview() {
|
||||
if (
|
||||
(reviewForm.action === 'reject' || reviewForm.action === 'take_down') &&
|
||||
!reviewForm.reason.trim()
|
||||
) {
|
||||
message.warning('请填写原因');
|
||||
return false;
|
||||
}
|
||||
if (selected.value) {
|
||||
await ContentReview({
|
||||
objectType: selected.value.objectType,
|
||||
id: selected.value.id,
|
||||
...reviewForm,
|
||||
});
|
||||
message.success('处理成功');
|
||||
} else {
|
||||
const result: any = await ContentBatchReview({
|
||||
objectType: searchFormRef.value?.formModel?.objectType || 'feed_post',
|
||||
ids: checkedIds.value.map(String),
|
||||
...reviewForm,
|
||||
});
|
||||
if (result.failedCount) {
|
||||
message.warning(`成功 ${result.successCount} 条,失败 ${result.failedCount} 条`);
|
||||
} else {
|
||||
message.success(`已处理 ${result.successCount} 条内容`);
|
||||
}
|
||||
checkedIds.value = [];
|
||||
}
|
||||
reloadTable();
|
||||
return true;
|
||||
}
|
||||
const actionColumn = reactive({
|
||||
width: 170,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
render(row: ContentState) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{ label: '详情', onClick: () => openDetail(row), auth: ['/cultivate/content/view'] },
|
||||
{
|
||||
label: '审核处理',
|
||||
onClick: () => openReview(row),
|
||||
auth: ['/cultivate/content/review'],
|
||||
disabled: !hasPermission(['/cultivate/content/review']),
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
onMounted(loadOptions);
|
||||
</script>
|
||||
<style scoped>
|
||||
.detail-modal {
|
||||
width: min(1100px, 92vw);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="养成概览"
|
||||
>汇总后院养成、物友动态、内容审核与举报治理数据。</n-card
|
||||
>
|
||||
</div>
|
||||
<n-spin :show="loading">
|
||||
<n-grid cols="1 s:2 m:3 l:4" responsive="screen" :x-gap="16" :y-gap="16">
|
||||
<n-grid-item v-for="item in cards" :key="item.key">
|
||||
<n-card :title="item.label" size="small">
|
||||
<n-statistic :value="statistics[item.key] || 0" />
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { CultivateDashboard } from '@/api/cultivate/content';
|
||||
|
||||
const loading = ref(false);
|
||||
const statistics = reactive<Record<string, number>>({});
|
||||
const cards = [
|
||||
{ key: 'plantCount', label: '累计种植' },
|
||||
{ key: 'growingCount', label: '生长中' },
|
||||
{ key: 'bloomCount', label: '已开花' },
|
||||
{ key: 'harvestedCount', label: '已收获' },
|
||||
{ key: 'active7d', label: '近 7 日活跃用户' },
|
||||
{ key: 'active30d', label: '近 30 日活跃用户' },
|
||||
{ key: 'postCount', label: '已发布动态' },
|
||||
{ key: 'commentCount', label: '已发布评论' },
|
||||
{ key: 'likeCount', label: '累计点赞' },
|
||||
{ key: 'pendingAuditCount', label: '待审核内容' },
|
||||
{ key: 'pendingReportCount', label: '待处理举报' },
|
||||
{ key: 'processingReportCount', label: '处理中举报' },
|
||||
];
|
||||
|
||||
async function loadDashboard() {
|
||||
loading.value = true;
|
||||
try {
|
||||
Object.assign(statistics, await CultivateDashboard());
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadDashboard);
|
||||
</script>
|
||||
@@ -1,12 +1,44 @@
|
||||
import { ref } from 'vue';
|
||||
import { FormSchema } from '@/components/Form';
|
||||
export const schemas = ref<FormSchema[]>([
|
||||
{ field: 'operatorId', component: 'NInput', label: '操作人ID', componentProps: { placeholder: '请输入操作人ID' } },
|
||||
{ field: 'operationType', component: 'NInput', label: '操作类型', componentProps: { placeholder: '如 content、report、flower' } },
|
||||
{ field: 'targetType', component: 'NInput', label: '对象类型', componentProps: { placeholder: '请输入对象类型' } },
|
||||
{
|
||||
field: 'operatorId',
|
||||
component: 'NInput',
|
||||
label: '操作人ID',
|
||||
componentProps: { placeholder: '请输入操作人ID' },
|
||||
},
|
||||
{
|
||||
field: 'operationType',
|
||||
component: 'NInput',
|
||||
label: '操作类型',
|
||||
componentProps: { placeholder: '如 content、report、flower' },
|
||||
},
|
||||
{
|
||||
field: 'targetType',
|
||||
component: 'NInput',
|
||||
label: '对象类型',
|
||||
componentProps: { placeholder: '请输入对象类型' },
|
||||
},
|
||||
{
|
||||
field: 'targetId',
|
||||
component: 'NInput',
|
||||
label: '对象ID',
|
||||
componentProps: { placeholder: '请输入对象ID' },
|
||||
},
|
||||
{
|
||||
field: 'requestId',
|
||||
component: 'NInput',
|
||||
label: '请求ID',
|
||||
componentProps: { placeholder: '请输入请求ID' },
|
||||
},
|
||||
]);
|
||||
export const columns = [
|
||||
{ title: '日志ID', key: 'id', width: 190 }, { title: '操作人', key: 'operatorId', width: 150 }, { title: '操作类型', key: 'operationType', width: 160 },
|
||||
{ title: '对象类型', key: 'targetType', width: 130 }, { title: '对象ID', key: 'targetId', width: 190 }, { title: '原因/结果', key: 'reason', width: 260, ellipsis: { tooltip: true } },
|
||||
{ title: '请求ID', key: 'requestId', width: 220 }, { title: '操作时间', key: 'createdAt', width: 180 },
|
||||
{ title: '日志ID', key: 'id', width: 190 },
|
||||
{ title: '操作人', key: 'operatorId', width: 150 },
|
||||
{ title: '操作类型', key: 'operationType', width: 160 },
|
||||
{ title: '对象类型', key: 'targetType', width: 130 },
|
||||
{ title: '对象ID', key: 'targetId', width: 190 },
|
||||
{ title: '原因/结果', key: 'reason', width: 260, ellipsis: { tooltip: true } },
|
||||
{ title: '请求ID', key: 'requestId', width: 220 },
|
||||
{ title: '操作时间', key: 'createdAt', width: 180 },
|
||||
];
|
||||
|
||||
@@ -1,25 +1,214 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header"><n-card :bordered="false" title="举报处理">查看物友动态、评论及用户举报并记录处理结果。</n-card></div>
|
||||
<div class="n-layout-page-header"
|
||||
><n-card :bordered="false" title="举报处理"
|
||||
>查看物友动态、评论及用户举报并记录处理结果。</n-card
|
||||
></div
|
||||
>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicForm ref="searchFormRef" @register="register" @submit="reloadTable" @reset="reloadTable" />
|
||||
<BasicTable ref="actionRef" :columns="columns" :request="loadDataTable" :row-key="(row) => row.id" :actionColumn="actionColumn" :scroll-x="1650" />
|
||||
<BasicForm
|
||||
ref="searchFormRef"
|
||||
@register="register"
|
||||
@submit="reloadTable"
|
||||
@reset="reloadTable"
|
||||
/>
|
||||
<BasicTable
|
||||
ref="actionRef"
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
:actionColumn="actionColumn"
|
||||
:scroll-x="1650"
|
||||
/>
|
||||
</n-card>
|
||||
<n-modal v-model:show="showHandle" preset="dialog" title="处理举报" positive-text="确认" negative-text="取消" @positive-click="submitHandle">
|
||||
<n-space vertical><n-select v-model:value="handleForm.action" :options="handleOptions" /><n-input v-model:value="handleForm.handleResult" type="textarea" placeholder="办结或驳回时必须填写处理结果" /></n-space>
|
||||
<n-modal
|
||||
v-model:show="showHandle"
|
||||
preset="dialog"
|
||||
title="处理举报"
|
||||
positive-text="确认"
|
||||
negative-text="取消"
|
||||
@positive-click="submitHandle"
|
||||
>
|
||||
<n-space vertical>
|
||||
<n-select v-model:value="handleForm.action" :options="handleOptions" />
|
||||
<n-select
|
||||
v-model:value="handleForm.contentAction"
|
||||
:options="contentActionOptions"
|
||||
:disabled="handleForm.action !== 'resolve'"
|
||||
placeholder="内容联动处置"
|
||||
/>
|
||||
<n-select
|
||||
v-model:value="handleForm.userAction"
|
||||
:options="userActionOptions"
|
||||
:disabled="handleForm.action !== 'resolve'"
|
||||
placeholder="用户联动处置"
|
||||
/>
|
||||
<n-input
|
||||
v-model:value="handleForm.reason"
|
||||
type="textarea"
|
||||
placeholder="联动处置时必须填写原因"
|
||||
/>
|
||||
<n-input
|
||||
v-model:value="handleForm.handleResult"
|
||||
type="textarea"
|
||||
placeholder="办结或驳回时必须填写处理结果"
|
||||
/>
|
||||
</n-space>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="showDetail" preset="card" title="举报详情" class="detail-modal">
|
||||
<n-spin :show="detailLoading">
|
||||
<n-descriptions v-if="detail" bordered :column="2">
|
||||
<n-descriptions-item label="举报人"
|
||||
>{{ detail.reporterNickname || '-' }}({{ detail.reporterId }})</n-descriptions-item
|
||||
>
|
||||
<n-descriptions-item label="被举报人"
|
||||
>{{ detail.reportedUserNickname || '-' }}({{
|
||||
detail.reportedUserId
|
||||
}})</n-descriptions-item
|
||||
>
|
||||
<n-descriptions-item label="对象"
|
||||
>{{ detail.objectType }} / {{ detail.objectId }}</n-descriptions-item
|
||||
>
|
||||
<n-descriptions-item label="原因">{{ detail.reasonCode }}</n-descriptions-item>
|
||||
<n-descriptions-item label="说明" :span="2">{{
|
||||
detail.description || '-'
|
||||
}}</n-descriptions-item>
|
||||
<n-descriptions-item label="证据文件" :span="2">{{
|
||||
formatEvidence(detail.evidenceFileIds)
|
||||
}}</n-descriptions-item>
|
||||
<n-descriptions-item v-if="detail.targetContent" label="目标内容" :span="2">{{
|
||||
detail.targetContent.contentText || '-'
|
||||
}}</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
<n-divider title-placement="left">该用户近期举报记录</n-divider>
|
||||
<n-data-table :columns="historyColumns" :data="detail?.history || []" :pagination="false" />
|
||||
</n-spin>
|
||||
</n-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { h, onMounted, reactive, ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table'; import { BasicForm, useForm } from '@/components/Form';
|
||||
import { ReportHandle, ReportList } from '@/api/cultivate/content'; import { columns, loadOptions, ReportState, schemas } from './model';
|
||||
const actionRef = ref(); const searchFormRef = ref<any>({}); const showHandle = ref(false); const selected = ref<ReportState>(); const message = useMessage();
|
||||
const handleForm = reactive({ action: 'processing', handleResult: '' }); const handleOptions = [{ label: '标记处理中', value: 'processing' }, { label: '确认处理', value: 'resolve' }, { label: '驳回举报', value: 'reject' }];
|
||||
const [register] = useForm({ gridProps: { cols: '1 s:1 m:2 l:4' }, labelWidth: 90, schemas }); const loadDataTable = (res) => ReportList({ ...searchFormRef.value?.formModel, ...res }); const reloadTable = () => actionRef.value?.reload();
|
||||
function openHandle(row: ReportState) { selected.value = row; handleForm.action = row.status === 'submitted' ? 'processing' : 'resolve'; handleForm.handleResult = ''; showHandle.value = true; }
|
||||
async function submitHandle() { if (!selected.value) return false; if (handleForm.action !== 'processing' && !handleForm.handleResult.trim()) { message.warning('请填写处理结果'); return false; } await ReportHandle({ id: selected.value.id, ...handleForm }); message.success('处理成功'); reloadTable(); return true; }
|
||||
const actionColumn = reactive({ width: 110, title: '操作', key: 'action', fixed: 'right', render(row: ReportState) { return h(TableAction as any, { style: 'button', actions: [{ label: '处理', onClick: () => openHandle(row), auth: ['/cultivate/reports/handle'], disabled: row.status === 'resolved' || row.status === 'rejected' }] }); } });
|
||||
onMounted(loadOptions);
|
||||
import { h, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import { ReportHandle, ReportList, ReportView } from '@/api/cultivate/content';
|
||||
import { columns, loadOptions, ReportState, schemas } from './model';
|
||||
const actionRef = ref();
|
||||
const searchFormRef = ref<any>({});
|
||||
const showHandle = ref(false);
|
||||
const showDetail = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
const detail = ref<any>();
|
||||
const selected = ref<ReportState>();
|
||||
const message = useMessage();
|
||||
const handleForm = reactive({
|
||||
action: 'processing',
|
||||
handleResult: '',
|
||||
contentAction: 'none',
|
||||
userAction: 'none',
|
||||
reason: '',
|
||||
});
|
||||
const handleOptions = [
|
||||
{ label: '标记处理中', value: 'processing' },
|
||||
{ label: '确认处理', value: 'resolve' },
|
||||
{ label: '驳回举报', value: 'reject' },
|
||||
];
|
||||
const contentActionOptions = [
|
||||
{ label: '不处置内容', value: 'none' },
|
||||
{ label: '下架被举报内容', value: 'take_down' },
|
||||
];
|
||||
const userActionOptions = [
|
||||
{ label: '不处置用户', value: 'none' },
|
||||
{ label: '冻结用户', value: 'freeze' },
|
||||
{ label: '封禁用户', value: 'ban' },
|
||||
];
|
||||
const historyColumns = [
|
||||
{ title: '举报ID', key: 'id' },
|
||||
{ title: '对象类型', key: 'objectType' },
|
||||
{ title: '原因', key: 'reasonCode' },
|
||||
{ title: '状态', key: 'status' },
|
||||
{ title: '提交时间', key: 'createdAt' },
|
||||
];
|
||||
watch(
|
||||
() => handleForm.action,
|
||||
(action) => {
|
||||
if (action !== 'resolve') {
|
||||
handleForm.contentAction = 'none';
|
||||
handleForm.userAction = 'none';
|
||||
handleForm.reason = '';
|
||||
}
|
||||
}
|
||||
);
|
||||
const [register] = useForm({ gridProps: { cols: '1 s:1 m:2 l:4' }, labelWidth: 90, schemas });
|
||||
const loadDataTable = (res) => ReportList({ ...searchFormRef.value?.formModel, ...res });
|
||||
const reloadTable = () => actionRef.value?.reload();
|
||||
function openHandle(row: ReportState) {
|
||||
selected.value = row;
|
||||
Object.assign(handleForm, {
|
||||
action: row.status === 'submitted' ? 'processing' : 'resolve',
|
||||
handleResult: '',
|
||||
contentAction: 'none',
|
||||
userAction: 'none',
|
||||
reason: '',
|
||||
});
|
||||
showHandle.value = true;
|
||||
}
|
||||
async function openDetail(row: ReportState) {
|
||||
showDetail.value = true;
|
||||
detailLoading.value = true;
|
||||
try {
|
||||
detail.value = await ReportView({ id: row.id });
|
||||
} finally {
|
||||
detailLoading.value = false;
|
||||
}
|
||||
}
|
||||
function formatEvidence(value: any) {
|
||||
if (!value) return '-';
|
||||
if (Array.isArray(value)) return value.join('、');
|
||||
return typeof value === 'string' ? value : JSON.stringify(value);
|
||||
}
|
||||
async function submitHandle() {
|
||||
if (!selected.value) return false;
|
||||
if (handleForm.action !== 'processing' && !handleForm.handleResult.trim()) {
|
||||
message.warning('请填写处理结果');
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
(handleForm.contentAction !== 'none' || handleForm.userAction !== 'none') &&
|
||||
!handleForm.reason.trim()
|
||||
) {
|
||||
message.warning('请填写联动处置原因');
|
||||
return false;
|
||||
}
|
||||
await ReportHandle({ id: selected.value.id, ...handleForm });
|
||||
message.success('处理成功');
|
||||
reloadTable();
|
||||
return true;
|
||||
}
|
||||
const actionColumn = reactive({
|
||||
width: 160,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
render(row: ReportState) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{ label: '详情', onClick: () => openDetail(row), auth: ['/cultivate/reports/view'] },
|
||||
{
|
||||
label: '处理',
|
||||
onClick: () => openHandle(row),
|
||||
auth: ['/cultivate/reports/handle'],
|
||||
disabled: row.status === 'resolved' || row.status === 'rejected',
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
onMounted(loadOptions);
|
||||
</script>
|
||||
<style scoped>
|
||||
.detail-modal {
|
||||
width: min(1000px, 92vw);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user