feat: 新增养成管理后台
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
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 }); }
|
||||
@@ -0,0 +1,6 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
export function List(params) { return http.request({ url: '/cultivate/flowers/list', method: 'get', params }); }
|
||||
export function View(params) { return http.request({ url: '/cultivate/flowers/view', method: 'get', params }); }
|
||||
export function Edit(params) { return http.request({ url: '/cultivate/flowers/edit', method: 'POST', params }); }
|
||||
export function Status(params) { return http.request({ url: '/cultivate/flowers/status', method: 'POST', params }); }
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<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" />
|
||||
</n-card>
|
||||
<n-modal v-model:show="showReview" preset="dialog" title="内容审核" 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-space>
|
||||
</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';
|
||||
|
||||
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);
|
||||
</script>
|
||||
@@ -0,0 +1,43 @@
|
||||
import { computed, h, ref } from 'vue';
|
||||
import { NTag } from 'naive-ui';
|
||||
import { FormSchema } from '@/components/Form';
|
||||
import { useDictStore } from '@/store/modules/dict';
|
||||
|
||||
const dict = useDictStore();
|
||||
|
||||
export interface ContentState {
|
||||
id: string;
|
||||
objectType: string;
|
||||
postId: string;
|
||||
authorId: string;
|
||||
contentText: string;
|
||||
flowerCode: string;
|
||||
visibility: string;
|
||||
status: string;
|
||||
auditStatus: string;
|
||||
rejectReason: string;
|
||||
likeCount: number;
|
||||
commentCount: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export const schemas = ref<FormSchema[]>([
|
||||
{ field: 'objectType', component: 'NSelect', label: '内容类型', defaultValue: 'feed_post', componentProps: { options: computed(() => dict.getOptions('CultivateContentTypeOptions')) } },
|
||||
{ field: 'status', component: 'NSelect', label: '状态', componentProps: { options: computed(() => dict.getOptions('CultivateContentStatusOptions')), clearable: true } },
|
||||
{ field: 'authorId', component: 'NInput', label: '作者ID', componentProps: { placeholder: '请输入作者ID' } },
|
||||
{ field: 'keyword', component: 'NInput', label: '内容关键词', componentProps: { placeholder: '请输入正文关键词' } },
|
||||
]);
|
||||
|
||||
export const columns = [
|
||||
{ title: '内容ID', key: 'id', width: 190 },
|
||||
{ title: '类型', key: 'objectType', width: 90, render: (row: ContentState) => dict.getLabel('CultivateContentTypeOptions', row.objectType) },
|
||||
{ title: '作者ID', key: 'authorId', width: 150 },
|
||||
{ title: '正文', key: 'contentText', width: 300, ellipsis: { tooltip: true } },
|
||||
{ title: '可见范围', key: 'visibility', width: 100 },
|
||||
{ title: '状态', key: 'status', width: 110, render(row: ContentState) { return h(NTag, { type: row.status === 'published' ? 'success' : row.status === 'reviewing' ? 'warning' : row.status === 'rejected' ? 'error' : 'default' }, { default: () => dict.getLabel('CultivateContentStatusOptions', row.status) }); } },
|
||||
{ title: '点赞', key: 'likeCount', width: 80 },
|
||||
{ title: '评论', key: 'commentCount', width: 80 },
|
||||
{ title: '创建时间', key: 'createdAt', width: 180 },
|
||||
];
|
||||
|
||||
export function loadOptions() { dict.loadOptions(['CultivateContentTypeOptions', 'CultivateContentStatusOptions']); }
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<n-modal v-model:show="showModal" :mask-closable="false" preset="dialog" :title="formValue.id ? '编辑花种配置' : '新建花种配置'" class="flower-modal">
|
||||
<n-scrollbar class="flower-scroll"><n-spin :show="loading"><n-form ref="formRef" :model="formValue" :rules="rules" label-placement="left" :label-width="110">
|
||||
<n-grid cols="1 s:1 m:2" responsive="screen" :x-gap="16">
|
||||
<n-gi><n-form-item label="花种编码" path="flowerCode"><n-input v-model:value="formValue.flowerCode" :disabled="formValue.id > 0" placeholder="如 rose" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="花种名称" path="flowerName"><n-input v-model:value="formValue.flowerName" /></n-form-item></n-gi>
|
||||
<n-gi span="2"><n-form-item label="花语/简介"><n-input v-model:value="formValue.flowerDesc" type="textarea" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="图标文件ID"><n-input v-model:value="formValue.iconFileId" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="状态" path="status"><n-select v-model:value="formValue.status" :options="statusOptions" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="每日浇水上限"><n-input-number v-model:value="formValue.dailyWaterLimit" :min="0" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="每日施肥上限"><n-input-number v-model:value="formValue.dailyFertilizeLimit" :min="0" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="排序"><n-input-number v-model:value="formValue.sortNo" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="默认分享标签"><n-input v-model:value="formValue.shareConfig.default_tag" /></n-form-item></n-gi>
|
||||
</n-grid>
|
||||
<n-divider title-placement="left">成长阶段</n-divider>
|
||||
<n-card v-for="rule in formValue.stageRules" :key="rule.stage" size="small" class="stage-card" :title="rule.stage">
|
||||
<n-grid cols="1 s:1 m:3" responsive="screen" :x-gap="12">
|
||||
<n-gi><n-form-item label="下一阶段"><n-input v-model:value="rule.nextStage" disabled /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="升级成长值"><n-input-number v-model:value="rule.requiredPoints" :min="0" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="允许动作"><n-select v-model:value="rule.allowedActions" multiple :options="actionOptions" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="浇水成长值"><n-input-number v-model:value="rule.waterPoints" :min="0" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="施肥成长值"><n-input-number v-model:value="rule.fertilizePoints" :min="0" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="排序"><n-input-number v-model:value="rule.sortNo" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="静态图文件ID"><n-input v-model:value="rule.imageFileId" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="动效文件ID"><n-input v-model:value="rule.animationFileId" /></n-form-item></n-gi>
|
||||
<n-gi><n-form-item label="进度文案"><n-input v-model:value="rule.progressTextTemplate" /></n-form-item></n-gi>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
</n-form></n-spin></n-scrollbar>
|
||||
<template #action><n-space><n-button @click="showModal=false">取消</n-button><n-button type="primary" :loading="submitting" @click="submit">保存并发布配置</n-button></n-space></template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { Edit, View } from '@/api/cultivate/flowers';
|
||||
import { actionOptions, newState, rules, State, statusOptions } from './model';
|
||||
const emit=defineEmits(['reloadTable']);const message=useMessage();const showModal=ref(false);const loading=ref(false);const submitting=ref(false);const formRef=ref();const formValue=ref<State>(newState(null));
|
||||
function openModal(state:State|null){showModal.value=true;formValue.value=newState(null);if(!state?.id)return;loading.value=true;View({id:state.id}).then((res)=>formValue.value=newState(res)).finally(()=>loading.value=false)}
|
||||
function submit(){formRef.value?.validate((errors)=>{if(errors){message.error('请检查配置内容');return}submitting.value=true;Edit(formValue.value).then(()=>{message.success('配置已保存');showModal.value=false;emit('reloadTable')}).finally(()=>submitting.value=false)})}
|
||||
defineExpose({openModal});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.flower-modal { width: min(1080px, 92vw); }
|
||||
.flower-scroll { max-height: 78vh; padding-right: 16px; }
|
||||
.stage-card { margin-bottom: 12px; }
|
||||
</style>
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<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">
|
||||
<template #tableTitle><n-button v-if="hasPermission(['/cultivate/flowers/edit'])" type="primary" @click="addTable"><template #icon><n-icon><PlusOutlined /></n-icon></template>新建花种</n-button></template>
|
||||
</BasicTable>
|
||||
</n-card>
|
||||
<Edit ref="editRef" @reloadTable="reloadTable" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, onMounted, reactive, ref } from 'vue';
|
||||
import { useDialog, useMessage } from 'naive-ui';
|
||||
import { PlusOutlined } from '@vicons/antd';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
import { List, Status } from '@/api/cultivate/flowers';
|
||||
import { columns, loadOptions, schemas, State } from './model';
|
||||
import Edit from './edit.vue';
|
||||
|
||||
const actionRef = ref(); const searchFormRef = ref<any>({}); const editRef = ref();
|
||||
const dialog = useDialog(); const message = useMessage(); const { hasPermission } = usePermission();
|
||||
const [register] = useForm({ gridProps: { cols: '1 s:1 m:2 l:4 xl:4 2xl:4' }, labelWidth: 90, schemas });
|
||||
const loadDataTable = async (res) => List({ ...searchFormRef.value?.formModel, ...res });
|
||||
const reloadTable = () => actionRef.value?.reload(); const addTable = () => editRef.value.openModal(null);
|
||||
const actionColumn = reactive({ width: 220, title: '操作', key: 'action', fixed: 'right', render(record: State) { return h(TableAction as any, { style: 'button', actions: [
|
||||
{ label: '编辑配置', onClick: () => editRef.value.openModal(record), auth: ['/cultivate/flowers/edit'] },
|
||||
{ label: record.status === 'online' ? '下架' : '上架', onClick: () => changeStatus(record), auth: ['/cultivate/flowers/status'] },
|
||||
] }); } });
|
||||
function changeStatus(record: State) { const status = record.status === 'online' ? 'offline' : 'online'; dialog.warning({ title: '确认操作', content: `确定${status === 'online' ? '上架' : '下架'}“${record.flowerName}”吗?`, positiveText: '确定', negativeText: '取消', onPositiveClick: () => Status({ id: record.id, status }).then(() => { message.success('操作成功'); reloadTable(); }) }); }
|
||||
onMounted(loadOptions);
|
||||
</script>
|
||||
@@ -0,0 +1,83 @@
|
||||
import { computed, h, ref } from 'vue';
|
||||
import { NTag } from 'naive-ui';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { FormSchema } from '@/components/Form';
|
||||
import { useDictStore } from '@/store/modules/dict';
|
||||
|
||||
const dict = useDictStore();
|
||||
|
||||
export interface StageRule {
|
||||
id?: number;
|
||||
stage: string;
|
||||
nextStage: string;
|
||||
requiredPoints: number;
|
||||
waterPoints: number;
|
||||
fertilizePoints: number;
|
||||
allowedActions: string[];
|
||||
imageFileId: string;
|
||||
animationFileId: string;
|
||||
progressTextTemplate: string;
|
||||
completionText: string;
|
||||
sortNo: number;
|
||||
}
|
||||
|
||||
export class State {
|
||||
id = 0;
|
||||
flowerCode = '';
|
||||
flowerName = '';
|
||||
flowerDesc = '';
|
||||
iconFileId = '';
|
||||
status = 'offline';
|
||||
configVersion = '';
|
||||
dailyWaterLimit = 3;
|
||||
dailyFertilizeLimit = 1;
|
||||
shareConfig: Record<string, any> = { can_share_snapshot: true, default_tag: '' };
|
||||
sortNo = 0;
|
||||
stageRules: StageRule[] = defaultStageRules();
|
||||
createdAt = '';
|
||||
updatedAt = '';
|
||||
constructor(state?: Partial<State>) { if (state) Object.assign(this, state); }
|
||||
}
|
||||
|
||||
export function defaultStageRules(): StageRule[] {
|
||||
return [
|
||||
stageRule('seed', 'sprout', 12, 1, 0, ['water'], 10),
|
||||
stageRule('sprout', 'bud', 60, 1, 0, ['water'], 20),
|
||||
stageRule('bud', 'bloom', 90, 1, 5, ['water', 'fertilize'], 30),
|
||||
stageRule('bloom', '', 0, 0, 0, [], 40),
|
||||
];
|
||||
}
|
||||
function stageRule(stage: string, nextStage: string, requiredPoints: number, waterPoints: number, fertilizePoints: number, allowedActions: string[], sortNo: number): StageRule {
|
||||
return { stage, nextStage, requiredPoints, waterPoints, fertilizePoints, allowedActions, imageFileId: '', animationFileId: '', progressTextTemplate: '', completionText: '', sortNo };
|
||||
}
|
||||
export function newState(state: State | Record<string, any> | null): State { return state ? new State(cloneDeep(state)) : new State(); }
|
||||
|
||||
export const rules = {
|
||||
flowerCode: { required: true, trigger: ['blur', 'input'], message: '请输入花种编码' },
|
||||
flowerName: { required: true, trigger: ['blur', 'input'], message: '请输入花种名称' },
|
||||
status: { required: true, trigger: ['change'], message: '请选择状态' },
|
||||
};
|
||||
|
||||
export const schemas = ref<FormSchema[]>([
|
||||
{ field: 'flowerCode', component: 'NInput', label: '花种编码', componentProps: { placeholder: '请输入花种编码' } },
|
||||
{ field: 'flowerName', component: 'NInput', label: '花种名称', componentProps: { placeholder: '请输入花种名称' } },
|
||||
{ field: 'status', component: 'NSelect', label: '状态', componentProps: { options: computed(() => dict.getOptions('CultivateFlowerStatusOptions')), clearable: true } },
|
||||
{ field: 'configVersion', component: 'NInput', label: '配置版本', componentProps: { placeholder: '请输入配置版本' } },
|
||||
]);
|
||||
|
||||
export const columns = [
|
||||
{ title: '花种编码', key: 'flowerCode', width: 140 },
|
||||
{ title: '花种名称', key: 'flowerName', width: 140 },
|
||||
{ title: '花语/简介', key: 'flowerDesc', width: 240, ellipsis: { tooltip: true } },
|
||||
{ title: '成长阶段', key: 'stageCount', width: 100 },
|
||||
{ title: '浇水上限', key: 'dailyWaterLimit', width: 100 },
|
||||
{ title: '施肥上限', key: 'dailyFertilizeLimit', width: 100 },
|
||||
{ title: '配置版本', key: 'configVersion', width: 180 },
|
||||
{ title: '状态', key: 'status', width: 100, render(row: State) { return h(NTag, { type: row.status === 'online' ? 'success' : 'default' }, { default: () => dict.getLabel('CultivateFlowerStatusOptions', row.status) }); } },
|
||||
{ title: '排序', key: 'sortNo', width: 80 },
|
||||
{ title: '更新时间', key: 'updatedAt', width: 180 },
|
||||
];
|
||||
|
||||
export function loadOptions() { dict.loadOptions(['CultivateFlowerStatusOptions']); }
|
||||
export const statusOptions = computed(() => dict.getOptions('CultivateFlowerStatusOptions'));
|
||||
export const actionOptions = [{ label: '浇水', value: 'water' }, { label: '施肥', value: 'fertilize' }];
|
||||
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<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" :scroll-x="1500" />
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'; import { BasicTable } from '@/components/Table'; import { BasicForm, useForm } from '@/components/Form'; import { OperationLogList } from '@/api/cultivate/content'; import { columns, schemas } from './model';
|
||||
const actionRef = ref(); const searchFormRef = ref<any>({}); const [register] = useForm({ gridProps: { cols: '1 s:1 m:2 l:4' }, labelWidth: 90, schemas }); const loadDataTable = (res) => OperationLogList({ ...searchFormRef.value?.formModel, ...res }); const reloadTable = () => actionRef.value?.reload();
|
||||
</script>
|
||||
@@ -0,0 +1,12 @@
|
||||
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: '请输入对象类型' } },
|
||||
]);
|
||||
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 },
|
||||
];
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<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" />
|
||||
</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>
|
||||
</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);
|
||||
</script>
|
||||
@@ -0,0 +1,20 @@
|
||||
import { computed, h, ref } from 'vue';
|
||||
import { NTag } from 'naive-ui';
|
||||
import { FormSchema } from '@/components/Form';
|
||||
import { useDictStore } from '@/store/modules/dict';
|
||||
|
||||
const dict = useDictStore();
|
||||
export interface ReportState { id: string; reporterId: string; reportedUserId: string; objectType: string; objectId: string; reasonCode: string; description: string; status: string; handlerId: string; handleResult: string; handledAt: string; createdAt: string; }
|
||||
export const schemas = ref<FormSchema[]>([
|
||||
{ field: 'objectType', component: 'NSelect', label: '对象类型', componentProps: { options: computed(() => dict.getOptions('CultivateReportObjectTypeOptions')), clearable: true } },
|
||||
{ field: 'status', component: 'NSelect', label: '处理状态', componentProps: { options: computed(() => dict.getOptions('CultivateReportStatusOptions')), clearable: true } },
|
||||
{ field: 'reporterId', component: 'NInput', label: '举报人ID', componentProps: { placeholder: '请输入举报人ID' } },
|
||||
]);
|
||||
export const columns = [
|
||||
{ title: '举报ID', key: 'id', width: 190 }, { title: '举报人', key: 'reporterId', width: 150 }, { title: '被举报人', key: 'reportedUserId', width: 150 },
|
||||
{ title: '对象', key: 'objectType', width: 90, render: (row: ReportState) => dict.getLabel('CultivateReportObjectTypeOptions', row.objectType) || row.objectType },
|
||||
{ title: '对象ID', key: 'objectId', width: 190 }, { title: '原因', key: 'reasonCode', width: 120 }, { title: '补充说明', key: 'description', width: 220, ellipsis: { tooltip: true } },
|
||||
{ title: '状态', key: 'status', width: 100, render(row: ReportState) { return h(NTag, { type: row.status === 'resolved' ? 'success' : row.status === 'submitted' ? 'warning' : 'default' }, { default: () => dict.getLabel('CultivateReportStatusOptions', row.status) }); } },
|
||||
{ title: '处理结果', key: 'handleResult', width: 220, ellipsis: { tooltip: true } }, { title: '提交时间', key: 'createdAt', width: 180 },
|
||||
];
|
||||
export function loadOptions() { dict.loadOptions(['CultivateReportObjectTypeOptions', 'CultivateReportStatusOptions']); }
|
||||
Reference in New Issue
Block a user