feat: 新增新闻管理功能,包括新闻的创建、编辑和列表展示,更新相关路由和类型定义

This commit is contained in:
2026-01-08 23:25:17 +07:00
parent 7546000679
commit afb52e1a73
9 changed files with 614 additions and 7 deletions

View File

@@ -0,0 +1,200 @@
<script lang="ts" setup>
import { computed, h, ref, useTemplateRef } from 'vue';
import type { FormInst, FormRules } from 'naive-ui';
import { NInput, useDialog } from 'naive-ui';
import { client, safeClient } from '@/service/api';
import UploadS3 from '@/components/upload/index.vue';
defineOptions({ name: 'AddNews' });
type Body = CommonType.TreatyBody<typeof client.api.admin.news.post>;
const emit = defineEmits<{
(e: 'close'): void;
}>();
const formRef = useTemplateRef<FormInst | null>('formRef');
const dialog = useDialog();
const form = ref<Body>({
title: '',
content: '',
categoryId: '',
attachmentIds: [],
isPinned: false
});
// 获取分类列表
const { data: categories, execute: refreshCategories } = safeClient(() =>
client.api.admin.news_categories.get({
query: {
pageIndex: 1,
pageSize: 100
}
})
);
const categoryOptions = computed(
() =>
categories.value?.data.map(cat => ({
label: cat.name,
value: cat.id
})) || []
);
// 新增分类
function handleAddCategory() {
const categoryName = ref('');
dialog.create({
title: '新增分类',
content: () =>
h('div', { class: 'py-4' }, [
h('div', { class: 'mb-2 text-14px' }, '分类名称'),
h(NInput, {
value: categoryName.value,
placeholder: '请输入分类名称',
'onUpdate:value': (val: string) => {
categoryName.value = val;
}
})
]),
positiveText: '创建',
negativeText: '取消',
onPositiveClick: async () => {
if (!categoryName.value.trim()) {
window.$message?.error('请输入分类名称');
return false;
}
const { data } = await safeClient(() =>
client.api.admin.news_categories.post({
name: categoryName.value
})
);
if (data) {
window.$message?.success('分类创建成功');
await refreshCategories();
form.value.categoryId = data.value?.id;
}
}
});
}
const rules: FormRules = {
title: [
{
required: true,
message: '请输入新闻标题',
trigger: ['blur', 'input']
},
{
min: 2,
max: 200,
message: '标题长度应在2-200个字符之间',
trigger: ['blur', 'input']
}
],
content: [
{
required: true,
message: '请输入新闻内容',
trigger: ['blur', 'input']
},
{
min: 10,
message: '内容至少10个字符',
trigger: ['blur', 'input']
}
],
categoryId: [
{
required: true,
message: '请选择新闻分类',
trigger: 'change'
}
]
};
async function handleSubmit() {
formRef.value?.validate(async errors => {
if (!errors) {
const { data } = await safeClient(() =>
client.api.admin.news.post({
...form.value
})
);
if (data) {
window.$message?.success('新闻创建成功');
emit('close');
}
}
});
}
</script>
<template>
<NForm
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
label-placement="left"
require-mark-placement="left"
>
<NFormItem label="新闻标题" path="title">
<NInput v-model:value="form.title" placeholder="请输入新闻标题" maxlength="200" show-count />
</NFormItem>
<NFormItem label="新闻分类" path="categoryId">
<NSelect v-model:value="form.categoryId" :options="categoryOptions" placeholder="请选择新闻分类" class="flex-1">
<template #action>
<div class="px-3 py-2">
<NButton size="small" type="primary" block @click.stop="handleAddCategory">
<template #icon>
<icon-ic-round-plus class="text-icon" />
</template>
新增分类
</NButton>
</div>
</template>
</NSelect>
</NFormItem>
<NFormItem label="新闻内容" path="content">
<NInput
v-model:value="form.content"
type="textarea"
placeholder="请输入新闻内容"
:rows="10"
maxlength="10000"
show-count
/>
</NFormItem>
<NFormItem label="附件上传" path="attachmentIds">
<UploadS3
:model-value="form.attachmentIds || []"
:max-size="20"
:max-files="10"
accept="*/*"
placeholder="上传附件"
:fetch-options="{ businessType: 'other' }"
@update:model-value="evt => (form.attachmentIds = evt)"
/>
</NFormItem>
<NFormItem label="置顶显示" path="isPinned">
<NSwitch v-model:value="form.isPinned">
<template #checked>是</template>
<template #unchecked>否</template>
</NSwitch>
</NFormItem>
<NSpace justify="end" class="mt-4">
<NButton @click="$emit('close')">取消</NButton>
<NButton type="primary" @click="handleSubmit">创建新闻</NButton>
</NSpace>
</NForm>
</template>
<style lang="css" scoped></style>