feat: 更新生产环境配置,修改API地址和增加新闻编辑功能
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# backend service base url, prod environment
|
# backend service base url, prod environment
|
||||||
VITE_SERVICE_BASE_URL=https://riwa-api.riwsan1.com
|
VITE_SERVICE_BASE_URL=http://192.168.1.2:9537
|
||||||
|
|
||||||
# other backend service base url, prod environment
|
# other backend service base url, prod environment
|
||||||
VITE_OTHER_SERVICE_BASE_URL= `{}`
|
VITE_OTHER_SERVICE_BASE_URL= `{}`
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
"@better-scroll/core": "2.5.1",
|
"@better-scroll/core": "2.5.1",
|
||||||
"@elysiajs/eden": "^1.4.5",
|
"@elysiajs/eden": "^1.4.5",
|
||||||
"@iconify/vue": "5.0.0",
|
"@iconify/vue": "5.0.0",
|
||||||
"@riwa/api-types": "http://192.168.1.2:9538/api/capp-eden-0.0.29.tgz",
|
"@riwa/api-types": "http://192.168.1.2:9538/api/capp-eden-0.0.30.tgz",
|
||||||
"@sa/axios": "workspace:*",
|
"@sa/axios": "workspace:*",
|
||||||
"@sa/color": "workspace:*",
|
"@sa/color": "workspace:*",
|
||||||
"@sa/hooks": "workspace:*",
|
"@sa/hooks": "workspace:*",
|
||||||
|
|||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -18,8 +18,8 @@ importers:
|
|||||||
specifier: 5.0.0
|
specifier: 5.0.0
|
||||||
version: 5.0.0(vue@3.5.25(typescript@5.9.3))
|
version: 5.0.0(vue@3.5.25(typescript@5.9.3))
|
||||||
'@riwa/api-types':
|
'@riwa/api-types':
|
||||||
specifier: http://192.168.1.2:9538/api/capp-eden-0.0.27.tgz
|
specifier: http://192.168.1.2:9538/api/capp-eden-0.0.30.tgz
|
||||||
version: '@capp/eden@http://192.168.1.2:9538/api/capp-eden-0.0.27.tgz(@elysiajs/eden@1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))'
|
version: '@capp/eden@http://192.168.1.2:9538/api/capp-eden-0.0.30.tgz(@elysiajs/eden@1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))'
|
||||||
'@sa/axios':
|
'@sa/axios':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:packages/axios
|
version: link:packages/axios
|
||||||
@@ -496,9 +496,9 @@ packages:
|
|||||||
'@borewit/text-codec@0.1.1':
|
'@borewit/text-codec@0.1.1':
|
||||||
resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==}
|
resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==}
|
||||||
|
|
||||||
'@capp/eden@http://192.168.1.2:9538/api/capp-eden-0.0.27.tgz':
|
'@capp/eden@http://192.168.1.2:9538/api/capp-eden-0.0.30.tgz':
|
||||||
resolution: {tarball: http://192.168.1.2:9538/api/capp-eden-0.0.27.tgz}
|
resolution: {tarball: http://192.168.1.2:9538/api/capp-eden-0.0.30.tgz}
|
||||||
version: 0.0.27
|
version: 0.0.30
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@elysiajs/eden': ^1.4.6
|
'@elysiajs/eden': ^1.4.6
|
||||||
|
|
||||||
@@ -4871,7 +4871,7 @@ snapshots:
|
|||||||
|
|
||||||
'@borewit/text-codec@0.1.1': {}
|
'@borewit/text-codec@0.1.1': {}
|
||||||
|
|
||||||
'@capp/eden@http://192.168.1.2:9538/api/capp-eden-0.0.27.tgz(@elysiajs/eden@1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))':
|
'@capp/eden@http://192.168.1.2:9538/api/capp-eden-0.0.30.tgz(@elysiajs/eden@1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@elysiajs/eden': 1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3))
|
'@elysiajs/eden': 1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3))
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,11 @@ function resetForm() {
|
|||||||
<MarkdownEditor v-model:value="formModel.content" placeholder="请输入新闻内容" :preview="false" />
|
<MarkdownEditor v-model:value="formModel.content" placeholder="请输入新闻内容" :preview="false" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
|
||||||
<NGrid cols="2" x-gap="16px">
|
<NGrid cols="3" x-gap="16px">
|
||||||
|
<NFormItemGi label="是否有视频" path="hasVideo">
|
||||||
|
<NSwitch v-model:value="formModel.hasVideo" />
|
||||||
|
</NFormItemGi>
|
||||||
|
|
||||||
<NFormItemGi label="是否热门" path="isHot">
|
<NFormItemGi label="是否热门" path="isHot">
|
||||||
<NSwitch v-model:value="formModel.isHot" />
|
<NSwitch v-model:value="formModel.isHot" />
|
||||||
</NFormItemGi>
|
</NFormItemGi>
|
||||||
|
|||||||
144
src/views/news/components/edit.vue
Normal file
144
src/views/news/components/edit.vue
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import type { FormInst, FormRules } from 'naive-ui';
|
||||||
|
import { client, safeClient } from '@/service/api';
|
||||||
|
import UploadS3 from '@/components/upload/index.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'NewsEdit'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'close'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
type NewsItem = Treaty.Body<typeof client.api.admin.news.post>;
|
||||||
|
|
||||||
|
const formRef = ref<FormInst | null>(null);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const formModel = ref<NewsItem>({
|
||||||
|
title: '',
|
||||||
|
summary: '',
|
||||||
|
content: '',
|
||||||
|
hasVideo: false,
|
||||||
|
isHot: false,
|
||||||
|
isPinned: false,
|
||||||
|
thumbnailId: null,
|
||||||
|
attachmentIds: [],
|
||||||
|
sortOrder: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules: FormRules = {
|
||||||
|
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
|
||||||
|
summary: [{ required: true, message: '请输入摘要', trigger: 'blur' }],
|
||||||
|
content: [{ required: true, message: '请输入内容', trigger: 'blur' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
await formRef.value?.validate();
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await safeClient(() =>
|
||||||
|
client.api.admin.news({ id: props.data.id }).patch({
|
||||||
|
...formModel.value
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.data) {
|
||||||
|
window.$message?.success('更新成功');
|
||||||
|
emit('close');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
formModel.value = {
|
||||||
|
title: props.data.title || '',
|
||||||
|
summary: props.data.summary || '',
|
||||||
|
content: props.data.content || '',
|
||||||
|
hasVideo: props.data.hasVideo ?? false,
|
||||||
|
isHot: props.data.isHot ?? false,
|
||||||
|
isPinned: props.data.isPinned ?? false,
|
||||||
|
thumbnailId: props.data.thumbnailId || null,
|
||||||
|
attachmentIds: props.data.attachmentIds || [],
|
||||||
|
sortOrder: props.data.sortOrder || 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<NForm ref="formRef" :model="formModel" :rules="rules" label-placement="left" :label-width="100">
|
||||||
|
<NFormItem label="缩略图" path="thumbnailId">
|
||||||
|
<UploadS3
|
||||||
|
:model-value="formModel.thumbnailId ? [formModel.thumbnailId] : undefined"
|
||||||
|
:max-size="20"
|
||||||
|
:max-files="1"
|
||||||
|
accept="image/*"
|
||||||
|
placeholder="上传图片"
|
||||||
|
:fetch-options="{ businessType: 'news_attachment' }"
|
||||||
|
@update:model-value="evt => (formModel.thumbnailId = evt.length > 0 ? evt[0] : undefined)"
|
||||||
|
/>
|
||||||
|
</NFormItem>
|
||||||
|
<NGrid cols="2" x-gap="16px">
|
||||||
|
<NFormItemGi label="标题" path="title">
|
||||||
|
<NInput v-model:value="formModel.title" placeholder="请输入标题" />
|
||||||
|
</NFormItemGi>
|
||||||
|
|
||||||
|
<NFormItemGi label="摘要" path="summary">
|
||||||
|
<NInput v-model:value="formModel.summary" placeholder="请输入摘要" />
|
||||||
|
</NFormItemGi>
|
||||||
|
</NGrid>
|
||||||
|
|
||||||
|
<NFormItem label="内容" path="content">
|
||||||
|
<MarkdownEditor v-model:value="formModel.content" placeholder="请输入新闻内容" :preview="false" />
|
||||||
|
</NFormItem>
|
||||||
|
|
||||||
|
<NGrid cols="3" x-gap="16px">
|
||||||
|
<NFormItemGi label="是否有视频" path="hasVideo">
|
||||||
|
<NSwitch v-model:value="formModel.hasVideo" />
|
||||||
|
</NFormItemGi>
|
||||||
|
|
||||||
|
<NFormItemGi label="是否热门" path="isHot">
|
||||||
|
<NSwitch v-model:value="formModel.isHot" />
|
||||||
|
</NFormItemGi>
|
||||||
|
|
||||||
|
<NFormItemGi label="是否置顶" path="isPinned">
|
||||||
|
<NSwitch v-model:value="formModel.isPinned" />
|
||||||
|
</NFormItemGi>
|
||||||
|
</NGrid>
|
||||||
|
|
||||||
|
<NFormItem label="排序值" path="sortOrder">
|
||||||
|
<NInputNumber v-model:value="formModel.sortOrder" placeholder="请输入排序值" class="w-full" />
|
||||||
|
</NFormItem>
|
||||||
|
|
||||||
|
<NFormItem label="附件文件" path="attachmentIds">
|
||||||
|
<UploadS3
|
||||||
|
:model-value="formModel.attachmentIds || []"
|
||||||
|
:max-size="20"
|
||||||
|
:max-files="3"
|
||||||
|
placeholder="上传附件"
|
||||||
|
:fetch-options="{ businessType: 'news_attachment' }"
|
||||||
|
@update:model-value="evt => (formModel.attachmentIds = evt.length > 0 ? evt : undefined)"
|
||||||
|
/>
|
||||||
|
</NFormItem>
|
||||||
|
</NForm>
|
||||||
|
|
||||||
|
<div class="mt-16px flex justify-end gap-12px">
|
||||||
|
<NButton @click="emit('close')">取消</NButton>
|
||||||
|
<NButton type="primary" :loading="loading" @click="handleSubmit">确定</NButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { h, useTemplateRef } from 'vue';
|
import { h, useTemplateRef } from 'vue';
|
||||||
import { useDialog } from 'naive-ui';
|
import { NTag, useDialog } from 'naive-ui';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { client, safeClient } from '@/service/api';
|
import { client, safeClient } from '@/service/api';
|
||||||
import type { TableBaseColumns, TableFetchData, TableInst } from '@/components/table';
|
import type { TableBaseColumns, TableFetchData, TableInst } from '@/components/table';
|
||||||
import Add from './components/add.vue';
|
import Add from './components/add.vue';
|
||||||
|
import Edit from './components/edit.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'NewsManagement'
|
name: 'NewsManagement'
|
||||||
@@ -20,67 +21,164 @@ const fetchData: TableFetchData = ({ pagination, filter }) => {
|
|||||||
const columns: TableBaseColumns = [
|
const columns: TableBaseColumns = [
|
||||||
{
|
{
|
||||||
key: 'title',
|
key: 'title',
|
||||||
title: '标题'
|
title: '标题',
|
||||||
|
fixed: 'left'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'summary',
|
key: 'summary',
|
||||||
title: '摘要'
|
title: '摘要'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'content',
|
||||||
|
title: '内容'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'hasVideo',
|
key: 'hasVideo',
|
||||||
title: '是否有视频'
|
title: '是否有视频',
|
||||||
|
width: 100,
|
||||||
|
render(row) {
|
||||||
|
return row.hasVideo ? h(NTag, { type: 'primary' }, '是') : h(NTag, { type: 'error' }, '否');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'isHot',
|
key: 'isHot',
|
||||||
title: '是否热门'
|
title: '是否热门',
|
||||||
|
width: 100,
|
||||||
|
render(row) {
|
||||||
|
return row.isHot ? h(NTag, { type: 'primary' }, '是') : h(NTag, { type: 'error' }, '否');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'isPinned',
|
key: 'isPinned',
|
||||||
title: '是否置顶'
|
title: '是否置顶',
|
||||||
|
width: 100,
|
||||||
|
render(row) {
|
||||||
|
return row.isPinned ? h(NTag, { type: 'primary' }, '是') : h(NTag, { type: 'error' }, '否');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'attachmentIds',
|
key: 'attachmentIds',
|
||||||
title: '附件文件个数',
|
title: '附件文件个数',
|
||||||
|
width: 120,
|
||||||
render: (row: any) => {
|
render: (row: any) => {
|
||||||
return row.attachmentIds ? row.attachmentIds.length : 0;
|
return row.attachmentIds ? row.attachmentIds.length : 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'sortOrder',
|
key: 'sortOrder',
|
||||||
title: '排序值'
|
title: '排序值',
|
||||||
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'publishedAt',
|
key: 'createdAt',
|
||||||
title: '发布时间',
|
title: '创建时间',
|
||||||
render: (row: any) => {
|
render: (row: any) => {
|
||||||
return dayjs(row.publishedAt).format('YYYY-MM-DD HH:mm');
|
return dayjs(row.createdAt).format('YYYY-MM-DD HH:mm');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'operations',
|
key: 'operations',
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 150,
|
width: 330,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
operations: (row: any) => [
|
operations: (row: any) => [
|
||||||
{
|
{
|
||||||
contentText: '编辑',
|
contentText: '编辑',
|
||||||
size: 'small',
|
size: 'small',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
// 编辑操作
|
handleEdit(row);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
contentText: '删除',
|
||||||
|
size: 'small',
|
||||||
|
type: 'error',
|
||||||
|
ghost: true,
|
||||||
|
onClick: () => {
|
||||||
|
dialog.warning({
|
||||||
|
title: '删除确认',
|
||||||
|
content: '确定要删除该新闻吗?此操作不可撤销。',
|
||||||
|
positiveText: '删除',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
await safeClient(() => client.api.admin.news({ id: row.id }).delete());
|
||||||
|
window.$message?.success('删除成功');
|
||||||
|
tableInst.value?.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
contentText: row.isPinned ? '取消置顶' : '置顶',
|
||||||
|
size: 'small',
|
||||||
|
onClick: () => {
|
||||||
|
dialog.warning({
|
||||||
|
title: '置顶确认',
|
||||||
|
content: '确定要置顶该新闻吗?',
|
||||||
|
positiveText: row.isPinned ? '取消置顶' : '置顶',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
if (row.isPinned) {
|
||||||
|
await safeClient(() => client.api.admin.news({ id: row.id }).pin.post({ isPinned: false }));
|
||||||
|
} else {
|
||||||
|
await safeClient(() => client.api.admin.news({ id: row.id }).pin.post({ isPinned: true }));
|
||||||
|
}
|
||||||
|
window.$message?.success(row.isPinned ? '取消置顶成功' : '置顶成功');
|
||||||
|
tableInst.value?.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
contentText: row.isHot ? '取消热门' : '设为热门',
|
||||||
|
size: 'small',
|
||||||
|
onClick: () => {
|
||||||
|
dialog.warning({
|
||||||
|
title: '热门确认',
|
||||||
|
content: '确定要设置该新闻为热门吗?',
|
||||||
|
positiveText: row.isHot ? '取消热门' : '设为热门',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
if (row.isHot) {
|
||||||
|
await safeClient(() => client.api.admin.news({ id: row.id }).hot.post({ isHot: false }));
|
||||||
|
} else {
|
||||||
|
await safeClient(() => client.api.admin.news({ id: row.id }).hot.post({ isHot: true }));
|
||||||
|
}
|
||||||
|
window.$message?.success(row.isHot ? '取消热门成功' : '设为热门成功');
|
||||||
|
tableInst.value?.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function handleEdit(row: any) {
|
||||||
|
const d = dialog.create({
|
||||||
|
title: '编辑新闻',
|
||||||
|
showIcon: false,
|
||||||
|
style: { width: '1000px' },
|
||||||
|
content: () =>
|
||||||
|
h(Edit, {
|
||||||
|
data: row,
|
||||||
|
onClose: () => {
|
||||||
|
d.destroy();
|
||||||
|
tableInst.value?.reload();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleAdd() {
|
function handleAdd() {
|
||||||
dialog.create({
|
const d = dialog.create({
|
||||||
title: '添加新闻',
|
title: '添加新闻',
|
||||||
showIcon: false,
|
showIcon: false,
|
||||||
style: { width: '1000px' },
|
style: { width: '1000px' },
|
||||||
content: () =>
|
content: () =>
|
||||||
h(Add, {
|
h(Add, {
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
|
d.destroy();
|
||||||
tableInst.value?.reload();
|
tableInst.value?.reload();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -89,7 +187,7 @@ function handleAdd() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<TableBase ref="tableInst" :fetch-data="fetchData" :columns="columns" @add="handleAdd" />
|
<TableBase ref="tableInst" :fetch-data="fetchData" :scroll-x="1600" :columns="columns" @add="handleAdd" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="css" scoped></style>
|
<style lang="css" scoped></style>
|
||||||
|
|||||||
Reference in New Issue
Block a user