Initial commit: first version of project

This commit is contained in:
duyufeng
2025-09-16 17:27:55 +08:00
commit b9024cd644
45 changed files with 9230 additions and 0 deletions

11
src/views/NotFound.vue Normal file
View File

@@ -0,0 +1,11 @@
<template>
<div>
<div class="not-found">
<div class="not-found-title">404</div>
<div class="not-found-text">您访问的页面不存在</div>
</div>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped lang="less"></style>

View File

@@ -0,0 +1,19 @@
<!-- 面板主页 -->
<template>
<div class="dashboard">
<div class="dashboard-container">
<div class="dashboard-text">欢迎来到面板首页</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
</script>
<style lang="less" scoped>
.dashboard {
height: 100%;
}
</style>

116
src/views/admin/index.vue Normal file
View File

@@ -0,0 +1,116 @@
<!-- 主页 -->
<template>
<div class="container">
<!-- 左侧 导航栏 -->
<div class="container-left">
<h4>东方低碳</h4>
<div class="left-nav">
<AdminMenu />
</div>
</div>
<!-- 右侧 主体 -->
<div class="container-right">
<div class="top">
<MenuFoldOutlined />
<div class="account">
<img src="../../assets/images/avatar.png" alt="头像" />
<span>admin</span>
</div>
</div>
<div class="breadcrumb">
<!-- 面包屑 -->
<router-link to="/admin">首页</router-link>/{{ currentRouteName }}
</div>
<div class="main">
<!-- 主内容区 -->
<router-view />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import AdminMenu from '@/components/admin/Menu.vue'
const route = useRoute()
// 根据当前路由获取面包屑名称
const currentRouteName = computed(() => {
return route.meta?.breadcrumb || ''
})
</script>
<style lang="less" scoped>
.container {
width: 100vw;
height: 100vh;
display: flex;
gap: 0px;
.container-left {
width: 260px;
background-color: #013249;
height: 100%;
display: flex;
flex-direction: column;
h4 {
color: #ffffff;
padding: 20px 16px 10px;
margin: 0;
}
.left-nav {
flex: 1;
overflow-y: auto;
}
}
.container-right {
flex: 1;
display: flex;
flex-direction: column;
.top {
padding: 16px 20px;
border-bottom: 1px solid #f0f0f0;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #ffffff;
.anticon {
font-size: 20px;
cursor: pointer;
}
.account {
cursor: pointer;
img {
margin-right: 8px;
width: 35px;
}
}
}
//面包屑
.breadcrumb {
padding: 16px 20px;
border-bottom: 1px solid #f0f0f0;
a {
color: #666666;
margin-right: 2px;
}
}
.main {
flex: 1;
overflow-y: auto;
padding: 0 20px 20px;
}
}
}
</style>

112
src/views/admin/login.vue Normal file
View File

@@ -0,0 +1,112 @@
<!-- src/views/Login.vue -->
<template>
<div class="login-container">
<div class="login-form">
<h2>用户登录</h2>
<a-form :model="formState" :rules="rules" @finish="handleLogin">
<a-form-item name="username">
<a-input v-model:value="formState.username" placeholder="请输入用户名" size="large">
<template #prefix>
<UserOutlined />
</template>
</a-input>
</a-form-item>
<a-form-item name="password">
<a-input-password v-model:value="formState.password" placeholder="请输入密码" size="large">
<template #prefix>
<LockOutlined />
</template>
</a-input-password>
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit" size="large" block :loading="loading">
登录
</a-button>
</a-form-item>
</a-form>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
import { useUserStore } from '@/stores/user'
import { http } from '@/utils/axios';
interface FormState {
username: string
password: string
}
const router = useRouter()
const userStore = useUserStore()
const formState = ref<FormState>({
username: '',
password: ''
})
const loading = ref(false)
const rules = {
username: [{ required: true, message: '请输入用户名' }],
password: [{ required: true, message: '请输入密码' }]
}
const handleLogin = async (values: FormState) => {
loading.value = true
const params = { data: values.username, pwd: values.password };
http.post('/perm/login', params).then(res => {
console.log(res.data);
if (res.data.code === '0') {
message.success('登录成功')
window.sessionStorage.setItem('token', res.data.data.token);
console.log(res.data.data.token, 'hahah', window.sessionStorage.getItem('token'));
router.push('/admin')//跳转到管理端首页
} else {
message.error(res.data.message)
}
})
// try {
// await userStore.login({
// username: values.username,
// password: values.password
// })
// message.success('登录成功')
// router.push('/admin')//跳转到管理端首页
// } catch (error) {
// message.error('登录失败,请检查用户名和密码')
// } finally {
// loading.value = false
// }
}
</script>
<style scoped>
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: #f0f2f5;
}
.login-form {
width: 360px;
padding: 36px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.login-form h2 {
text-align: center;
margin-bottom: 24px;
color: #1890ff;
}
</style>

View File

@@ -0,0 +1,397 @@
<template>
<div class="addOredit">
<div class="header">
<div class="left">
<span class="" @click="save">保存</span>
<span class="" @click="publish" v-if="typeValue == '2'">发布</span>
</div>
<div class="right">
<span class="" @click="cancel">取消</span>
</div>
</div>
<div class="content">
<div class="left">
<div class="top">
<a-form-item name="newTitle" label="新闻标题">
<a-textarea v-model:value="titleName" />
<div class="num">{{ titleName.length }}/150</div>
</a-form-item>
</div>
<div class="bottom">
<!-- 富文本编辑器 -->
<Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" style="border-bottom: 1px solid #f2f2f2" />
<Editor v-model="valueHtml" :defaultConfig="editorConfig"
style="height: 800px; overflow-y: hidden; border: 1px solid #f2f2f2;" @onCreated="handleCreated"
@onChange="handleChange" />
</div>
</div>
<div class="right">
<h4>基本属性</h4>
<a-form ref="formRef" name="custom-validation" :model="formState" class="basicattribute">
<a-form-item has-feedback label="作者" name="author">
<a-input v-model:value="formState.author" type="text" autocomplete="off" />
</a-form-item>
<a-form-item has-feedback label="编辑" name="editor">
<a-input v-model:value="formState.editor" type="text" autocomplete="off" />
</a-form-item>
<a-form-item ref="name" label="封面图">
<!-- <a-image :width="80" :height="80" class="imgItem" :src="imageUrl" /> -->
<a-upload v-model:file-list="fileList" name="coverImage" list-type="picture-card" class="cover-uploader"
:show-upload-list="true" :action="uploadUrl" :max-count="1" :customRequest="setRequest">
<div v-if="fileList.length < 1">
<plus-outlined />
<div style="margin-top: 8px">上传</div>
</div>
</a-upload>
<!-- 图片预览 -->
<a-modal :visible="previewVisible" :title="previewTitle" :footer="null" @cancel="handleCancelPreview">
<img alt="预览图片" :src="previewImage" style="width: 100%" />
</a-modal>
</a-form-item>
</a-form>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onBeforeUnmount, shallowRef, onMounted, onUnmounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { PlusOutlined } from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
import type { UploadProps } from 'ant-design-vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { IDomEditor } from '@wangeditor/editor'
import '@wangeditor/editor/dist/css/style.css'
import { http } from '@/utils/axios';
const router = useRouter()
const route = useRoute()
// 编辑器实例
const editorRef = shallowRef()
const valueHtml = ref('')
//当前编辑传来的data数据
const currentData = ref();
const typeValue = ref();
// 上传相关
const fileList = ref<UploadProps['fileList']>([]);
const uploadUrl = ref('/api/system/minio/upload') // 替换为实际上传接口
const previewVisible = ref(false)
const previewImage = ref('')
const previewTitle = ref('')
const imageUrl = ref('');
const titleName = ref('');//新闻标题
const formState = ref({
author: '',
editor: '',
coverImageUrl: '',
attachId: '',//图片id
});
//接收跳转路由过来的参数
onMounted(() => {
const { data, type } = route.query;
if (data && typeof data === 'string' && type == '2') {//编辑
console.log(JSON.parse(data), '我是新增页面新增新增', type);
const parsedData = JSON.parse(data);
console.log(parsedData, type);
typeValue.value = type;
currentData.value = parsedData;
getByEditData()
}
});
//获取编辑数据
const getByEditData = async () => {
await http.post("/api/journalism/getDetailById", currentData.value.id).then(res => {
console.log(res, '获取编辑数据');
if (res.data.code == '0') {
const dataData = res.data.data;
if (dataData) {
//回填表单数据
titleName.value = dataData.title;
formState.value.author = dataData.author;
formState.value.editor = dataData.editor;
formState.value.attachId = dataData.attachId;
//富文本编辑器回填
if (editorRef.value) {
valueHtml.value = dataData.content;
} else {
// 延迟一点时间再设置
setTimeout(() => {
valueHtml.value = dataData.content;
}, 100);
}
//获取图片
// 请求图片文件
const attachId = dataData.attachId;
const config: any = {
responseType: 'blob',
};
if (attachId) {
// 下载图片用以显示
http.post("/api/minio/download" + "?attachId=" + attachId, config).then((res: any) => {
const blob = new Blob([res]);
const url = URL.createObjectURL(blob);
console.log(url, '图片地址222222');
imageUrl.value = url;
fileList.value = [{
uid: '-1',
name: dataData.title ? `${dataData.title}.jpg` : 'image.jpg',
status: 'done',
url: url,
thumbUrl: url,
response: { url: url }
}];
});
}
}
}
}).catch(error => {
});
}
//获取上传图片的id
/************************富文本编辑器*********************/
// 工具栏配置
const toolbarConfig = {}
// 编辑器配置
const editorConfig = {
placeholder: '请输入内容...',
MENU_CONF: {
uploadImage: {
// 图片上传配置
}
}
}
// 编辑器创建回调
const handleCreated = (editor: IDomEditor) => {
editorRef.value = editor
}
// 内容变化回调
const handleChange = (editor: IDomEditor) => {
valueHtml.value = editor.getHtml()
}
// 组件销毁时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
/*********************上传图片***************************/
// 预览图片
const handlePreview = async (file: any) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj)
}
previewImage.value = file.url || file.preview
previewVisible.value = true
previewTitle.value = file.name || file.url.substring(file.url.lastIndexOf('/') + 1)
}
// 取消预览
const handleCancelPreview = () => {
previewVisible.value = false
previewTitle.value = ''
}
// 转换为base64
const getBase64 = (file: File): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result as string)
reader.onerror = error => reject(error)
})
}
const limit = 10;
// 上传图片文件
const setRequest = (f: any) => {
const file1 = f.file;
const isJpgOrPng = file1.type === 'image/jpeg' || file1.type === 'image/png';
if (!isJpgOrPng) {
return message.warn('只能上传图片!');
}
const isLt2M = file1.size / 1024 / 1024 < limit;
if (!isLt2M) {
return message.warn(`文件最大不能超过${limit}M!`);
}
const formData = new FormData();
formData.append('file', file1);
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
};
http.post('/api/minio/upload', formData, config).then((res: any) => {
const data = res.data;
fileList.value = [
{
uid: file1.uid,
name: file1.name,
status: 'done',
url: data.data.link,
thumbUrl: data.data.link//缩略图显示
},
];
formState.value.attachId = data.data.id;
formState.value.coverImageUrl = data.data.link;
console.log(res);
})
};
// 保存
const save = async () => {
console.log('save')
const params = {
title: titleName.value,
content: valueHtml.value,
author: formState.value.author,
editor: formState.value.editor,
publishStatus: "3",
attachId: formState.value?.attachId,//图片的id
}
if (currentData.value) {//编辑时
params.id = currentData.value.id;
}
await http.post('/api/journalism/save', params).then(res => {
if (res.data.code === '0') {
message.success('保存成功')
router.push('/admin/news')
} else {
message.error(res.data.message)
}
})
}
// 发布(编辑时发布)
const publish = () => {
console.log('publish')
const params: any[] = [];
params.push(currentData?.value.code);
http.post('/api/journalism/publishByCodeList', params).then(res => {
if (res.data.code == '0') {
message.success('重新发布成功');
router.push('/admin/news')
}
}).catch(error => {
console.log(error);
});
}
//取消
const cancel = () => {
console.log('cancel')
router.push('/admin/news')
}
//页面关闭时销毁当前页数据
onUnmounted(() => {
// 重置所有响应式数据
titleName.value = '';
valueHtml.value = '';
formState.value = {
author: '',
editor: '',
coverImageUrl: '',
attachId: '',
};
// 清空文件列表
fileList.value = [];
// 重置当前数据
currentData.value = null;
typeValue.value = null;
// 重置预览相关数据
previewVisible.value = false;
previewImage.value = '';
previewTitle.value = '';
// 销毁编辑器实例
if (editorRef.value) {
editorRef.value.destroy();
editorRef.value = null;
}
console.log('页面数据已清理');
});
</script>
<style lang="less" scoped>
.addOredit {
width: 1200px;
margin: auto;
.header {
height: 52px;
background: #fff;
display: flex;
justify-content: space-between;
margin-bottom: 10px;
align-items: center;
padding: 0 20px;
.left {
span {
margin-right: 20px;
cursor: pointer;
}
}
.right {
span {
cursor: pointer;
}
}
}
.content {
display: flex;
height: calc(100vh - 212px);
gap: 10px;
.left {
flex: 2;
.top {
background: #fff;
padding: 20px 20px 10px;
margin-bottom: 10px;
.ant-form-item {
margin-bottom: 0;
}
.num {
text-align: right;
padding-top: 4px;
}
}
.bottom {
background: #fff;
padding: 20px;
height: calc(100% - 120px);
}
}
.right {
flex: 1;
background: #fff;
h4 {
padding: 15px;
border-bottom: 1px solid #eee;
text-align: center;
}
.basicattribute {
padding: 20px;
}
}
}
}
</style>

View File

@@ -0,0 +1,405 @@
<!-- src/views/admin/users.vue -->
<template>
<div class="newsCenter">
<div class="form">
<a-form :model="formState" :layout="'inline'" @finish="onFinish" class="ant-form-inline-custom">
<a-form-item name="inputValue" label="标题">
<a-input v-model:value="formState.inputValue" placeholder="请输入标题" />
</a-form-item>
<a-form-item name="selectValue" label="状态">
<a-select v-model:value="formState.selectValue" style="width: 150px" placeholder="请选择状态" :option="newStatus">
<a-select-option v-for="item in newStatus" :key="item.value" :value="item.value">
{{ item.label }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="发布时间">
<a-range-picker v-model:value="dateRange" format="YYYY-MM-DD" :placeholder="['开始日期', '结束日期']" />
</a-form-item>
<a-form-item label="创建时间">
<a-range-picker v-model:value="createDate" format="YYYY-MM-DD" :placeholder="['开始日期', '结束日期']" />
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit" @click="getTableData()">查询</a-button>
<a-button style="margin-left: 8px" @click="resetForm">
重置
</a-button>
</a-form-item>
</a-form>
</div>
<div class="button">
<a-button type="primary" @click="addEditNews('', '1')">新增</a-button>
<a-button type="primary" @click="batchPublish">批量发布</a-button>
<a-button type="primary" danger @click=deleteNews>批量删除</a-button>
</div>
<a-table :dataSource="dataSource" :columns="columns" bordered :pagination="pagination" @change="handleTableChange"
:row-selection="rowSelection" row-key="id">
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'status'">
{{ record.publishStatus.label }}
</template>
<template v-if="column.dataIndex === 'operation'">
<div class="operEdit">
<!-- 草稿时为发布 -->
<a-button type="text" @click="publish(record)" v-if="record.publishStatus.value === 0" class="blue">
发布
</a-button>
<!-- 已撤回的话是重新发布 -->
<a-button type="text" @click="rePublish(record)"
v-if="record.publishStatus.value === 2 || record.publishStatus.value === 3" class="blue">
重新发布
</a-button>
<a-button type="text" v-if="record.publishStatus.value === 0 || record.publishStatus.value === 1"
class="blue" @click="addEditNews(record, '3')">
撤销
</a-button>
<a-button type="text" class="blue" @click="addEditNews(record, '2')">
编辑
</a-button>
<a-button type="text" danger @click="deleteNews(record, '2')">删除</a-button>
</div>
</template>
</template>
</a-table>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { ref, reactive, onMounted, computed } from 'vue'
import { http } from '@/utils/axios';
import { message, Modal } from 'ant-design-vue';
import dayjs, { Dayjs } from 'dayjs'
const router = useRouter()
const newStatus = ref([]);//状态枚举
// 表单部分
interface FormState {
inputValue: string;
selectValue?: string;
}
const formState = reactive<FormState>({
inputValue: '',
selectValue: undefined,
});
// 修改日期范围的类型定义
const dateRange = ref<[Dayjs | null, Dayjs | null]>([null, null]);
const createDate = ref<[Dayjs | null, Dayjs | null]>([null, null]);
// 表单-获取枚举数据
const getEnum = async () => {
// 请求枚举数据
await http.postText('/system/common/getEnum', 'PublishStatus')
try {
const res = await http.postText('/system/common/getEnum', 'PublishStatus');
if (res?.data?.code === "0" && res.data.data?.length > 0) {
// 确保数据格式正确
newStatus.value = res.data.data.map((item: any) => ({
label: item.label,
value: item.value
}));
} else {
newStatus.value = [];
}
console.log(newStatus.value, '枚举数据');
} catch (error) {
console.error(error);
}
}
const onFinish = (values: FormState) => {
console.log('Received values:', values);
console.log('Date range:', dateRange.value);
};
const resetForm = () => {
formState.inputValue = '';
formState.selectValue = undefined;
dateRange.value = [null, null];
createDate.value = [null, null];
};
/***********************新增新闻******************************/
const addEditNews = (data: any, type: string) => {
console.log('新增新闻');
if (type === '1') {//新增
router.push('/admin/news/add')
} else if (type === '2') {//编辑
console.log(data, '编辑');
const dataParams = { id: data.id, code: data.code }
router.push({
name: 'AdminNewsAdd',
query: {
data: JSON.stringify(dataParams),
type: '2'
}
})
} else if (type === '3') {//撤销,掉接口提交即可
console.log(data, '撤销');
const params = {
// title: data.title,
// content: "标题内容",
// author: "sjs",
// editor: "sjs",
publishStatus: "2",
id: data.id
}
Modal.confirm({
title: '警告',
content: '确定要撤销吗?此操作不可恢复。',
okText: '确认',
cancelText: '取消',
onOk: () => {
http.post('/api/journalism/save', params).then(res => {
if (res.data.code === '0') {
message.success('撤销成功')
getTableData();
} else {
message.error(res.data.message)
}
})
},
onCancel() {
console.log('取消删除');
},
})
}
}
//删除
const deleteNews = (data: any, type: string) => {
console.log(data, '删除');
if (!type && selectedRows.value.length == 0) {//批量删除
message.warning('请选择要删除的新闻');
return;
}
Modal.confirm({
title: '警告',
content: '确定要删除选中的新闻吗?此操作不可恢复。',
okText: '确认',
cancelText: '取消',
onOk: () => {
const getCodeArr: any[] = [];
if (!type) {//批量删除
for (let i = 0; i < selectedRows.value.length; i++) {
getCodeArr.push(selectedRows.value[i].code)
}
}
if (type == '2') {//单个删除
getCodeArr.push(data.code)
}
return http.post('/api/journalism/deleteByCodeList', getCodeArr).then(res => {
if (res.data.code === '0') {
message.success('删除成功')
getTableData();
} else {
message.error(res.data.message)
}
})
},
onCancel() {
console.log('取消删除');
},
});
}
//批量发布
const batchPublish = () => {
if (selectedRows.value.length == 0) {
message.warning('请选择要发布的新闻');
return
} else {
const getCode: any[] = [];
for (let i = 0; i < selectedRows.value.length; i++) {
getCode.push(selectedRows.value[i].code)
}
console.log(getCode, '批量发布');
releaseFunc(getCode, '1')
}
}
//发布
const publish = (data: any) => {
const getCode: any[] = [];
getCode.push(data.code)
releaseFunc(getCode, '2')
}
//重新发布
const rePublish = (data: any) => {
const getCode: any[] = [];
getCode.push(data.code)
releaseFunc(getCode, '3')
}
/***********************表格部分******************************/
// 表格选中项
const selectedRowKeys = ref<string[]>([]);
const selectedRows = ref<any[]>([]);
// 表格行选择配置
const rowSelection = computed(() => ({
selectedRowKeys: selectedRowKeys.value,
onChange: (selectedKeys: any[], selectedRowsData: any[]) => {
selectedRowKeys.value = selectedKeys;
selectedRows.value = selectedRowsData;
},
columnWidth: '60px',
}));
// 分页配置
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
showTotal: (total: number) => `${total} 条数据`,
});
const dataSource = ref()//表格数据源
const columns = ref([
{
title: '序号',
dataIndex: 'address',
customRender: (text: any) => {
return text.index + 1;
},
width: 80,
},
{
title: '标题',
dataIndex: 'title',
key: 'title'
},
{
title: '状态',
dataIndex: 'status',
key: 'status'
},
{
title: '发布时间',
dataIndex: 'publishTimeStr',
key: 'publishTimeStr'
},
{
title: '创建时间',
dataIndex: 'createTimeStr',
key: 'createTimeStr'
},
{
title: '操作',
dataIndex: 'operation',
key: 'operation'
}
])
// 表格分页变化处理
const handleTableChange = (pag: any) => {
pagination.current = pag.current;
pagination.pageSize = pag.pageSize;
// 重新获取数据
getTableData();
};
// 获取表格数据
const getTableData = async () => {
console.log('获取表格数据');
const params = {
"page": {
"pageNum": pagination.current,
"pageSize": pagination.pageSize
},
"selectParam": {
"title": formState.inputValue ? formState.inputValue : undefined,
"publishStatus": formState?.selectValue ?? undefined,
}
}
// 发布时间范围
if (dateRange.value && dateRange.value[0] && dateRange.value[1]) {
params.selectParam.publishStartTime = dayjs(dateRange.value[0]).format('YYYY-MM-DD');
params.selectParam.publishEndTime = dayjs(dateRange.value[1]).format('YYYY-MM-DD');
}
// 创建时间范围
if (createDate.value && createDate.value[0] && createDate.value[1]) {
params.selectParam.createStartTime = dayjs(createDate.value[0]).format('YYYY-MM-DD');
params.selectParam.createEndTime = dayjs(createDate.value[1]).format('YYYY-MM-DD');
}
await http.post('/api/journalism/getPageList', params).then(res => {
console.log("hahahres", res)
if (res.data?.data?.records.length > 0) {
dataSource.value = res.data.data.records;
}
}).catch(error => {
console.log(error);
});
}
//表格批量发布:1、发布:2、重新发布:3
const releaseFunc = async (params: any, type: string) => {
console.log('表格批量发布、发布、重新发布');
await http.post('/api/journalism/publishByCodeList', params).then(res => {
if (res.data.code == '0') {
if (type == '1') {//批量发布
message.success('批量发布成功');
getTableData();
} else if (type == '2') {//重新发布
message.success('重新发布成功');
getTableData();
} else {//发布
message.success('发布成功');
getTableData();
}
}
}).catch(error => {
console.log(error);
});
}
onMounted(() => {
getEnum();
getTableData();
})
</script>
<style lang="less" scoped>
.newsCenter {
background-color: #ffffff;
height: 100%;
padding: 20px;
.form {
display: flex;
align-items: center;
margin-bottom: 25px;
.ant-form-item {
margin-bottom: 15px;
}
}
.button {
display: flex;
align-items: center;
margin: 15px 0;
gap: 10px;
}
.operEdit {
display: flex;
align-items: center;
gap: 10px;
.blue {
color: #2778FF;
}
.ant-btn {
padding: 4px 2px;
}
.ant-btn:hover {
background-color: transparent;
}
}
}
</style>

51
src/views/admin/users.vue Normal file
View File

@@ -0,0 +1,51 @@
<!-- src/views/admin/users.vue -->
<template>
<div class="users">
<h2>用户管理</h2>
<p>这里是用户管理页面</p>
<a-table :dataSource="dataSource" :columns="columns" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const dataSource = ref([
{
key: '1',
name: '张三',
age: 32,
address: '上海市浦东新区'
},
{
key: '2',
name: '李四',
age: 42,
address: '北京市朝阳区'
}
])
const columns = ref([
{
title: '姓名',
dataIndex: 'name',
key: 'name'
},
{
title: '年龄',
dataIndex: 'age',
key: 'age'
},
{
title: '住址',
dataIndex: 'address',
key: 'address'
}
])
</script>
<style scoped>
.users {
padding: 20px;
}
</style>

24
src/views/pc/about.vue Normal file
View File

@@ -0,0 +1,24 @@
<template>
<div class="about">
<a-layout>
<a-layout-sider v-model:collapsed="collapsed" :trigger="null" collapsible width="256">
<Navigation />
</a-layout-sider>
<a-layout>
<a-layout-content class="content">
<div class="content-inner">
<h1>欢迎来到about页2222222222222</h1>
<p>这里是网站的主要内容区域</p>
</div>
</a-layout-content>
</a-layout>
</a-layout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Navigation from '@/components/navigation.vue'
const collapsed = ref(false)
</script>
<style lang="less" scoped></style>

33
src/views/pc/index.vue Normal file
View File

@@ -0,0 +1,33 @@
<!-- src/views/News.vue -->
<template>
<div class="index-home">
啊哈哈哈哈我来了
<div class="content">
我是首页内容
<!-- 将轮播图放在内容区域或页脚区域 -->
<div class="carousel-wrapper">
<a-carousel autoplay :autoplay-speed="5000">
<div>
<video src="https://ec-resource20.oss-cn-shanghai.aliyuncs.com/ec_official_website/3_1.mp4" autoplay loop
muted playsinline style="width: 100%; height: auto;"></video>
</div>
<div>
<video src="https://ec-resource20.oss-cn-shanghai.aliyuncs.com/ec_official_website/3_2.mp4" autoplay loop
muted playsinline style="width: 100%; height: auto;"></video>
</div>
<div>
<video src="https://ec-resource20.oss-cn-shanghai.aliyuncs.com/ec_official_website/3_3.mp4" autoplay loop
muted playsinline style="width: 100%; height: auto;"></video>
</div>
</a-carousel>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
</script>
<style scoped></style>

104
src/views/pc/news.vue Normal file
View File

@@ -0,0 +1,104 @@
<!-- src/views/News.vue -->
<template>
<div class="news">
<a-layout>
<a-layout-sider v-model:collapsed="collapsed" :trigger="null" collapsible width="256">
<Navigation />
</a-layout-sider>
<a-layout>
<a-layout-content class="content">
<div class="content-inner">
<h1>新闻消息22222</h1>
<a-list class="news-list" item-layout="vertical" size="large" :pagination="pagination"
:data-source="newsList">
<template #renderItem="{ item }">
<a-list-item key="item.title">
<a-list-item-meta :description="item.date">
<template #title>
<a :href="item.href">{{ item.title }}</a>
</template>
</a-list-item-meta>
{{ item.content }}
</a-list-item>
</template>
</a-list>
</div>
</a-layout-content>
</a-layout>
</a-layout>
<!-- 将轮播图放在内容区域或页脚区域 -->
<div class="carousel-wrapper">
<a-carousel autoplay :autoplay-speed="5000">
<div>
<video src="https://ec-resource20.oss-cn-shanghai.aliyuncs.com/ec_official_website/3_1.mp4" autoplay loop
muted playsinline style="width: 100%; height: auto;"></video>
</div>
<div>
<video src="https://ec-resource20.oss-cn-shanghai.aliyuncs.com/ec_official_website/3_2.mp4" autoplay loop
muted playsinline style="width: 100%; height: auto;"></video>
</div>
<div>
<video src="https://ec-resource20.oss-cn-shanghai.aliyuncs.com/ec_official_website/3_3.mp4" autoplay loop
muted playsinline style="width: 100%; height: auto;"></video>
</div>
</a-carousel>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Navigation from '@/components/navigation.vue'
const collapsed = ref(false)
const newsList = ref([
{
title: '网站正式上线',
href: '#',
date: '2025-01-01',
content: '我们的网站今天正式上线了,欢迎大家访问!'
},
{
title: '新功能发布',
href: '#',
date: '2025-01-05',
content: '我们发布了全新的个人中心功能,提供更多个性化服务。'
},
{
title: '系统维护通知',
href: '#',
date: '2025-01-10',
content: '为了提供更好的服务,系统将于本周末进行维护升级。'
}
])
const pagination = {
onChange: (page: number) => {
console.log(page)
},
pageSize: 5,
}
</script>
<style scoped>
.news {
height: 100%;
}
.content {
margin: 24px 16px;
padding: 24px;
background: #fff;
min-height: 280px;
}
.content-inner {
padding: 24px;
}
.news-list {
margin-top: 24px;
}
</style>

111
src/views/pc/profile.vue Normal file
View File

@@ -0,0 +1,111 @@
<!-- src/views/Profile.vue -->
<template>
<div class="profile">
<a-layout>
<a-layout-sider v-model:collapsed="collapsed" :trigger="null" collapsible width="256">
<Navigation />
</a-layout-sider>
<a-layout>
<a-layout-content class="content">
<div class="content-inner">
<h1>个人中心</h1>
<a-card title="基本信息" style="margin-bottom: 24px;">
<a-descriptions :column="1">
<a-descriptions-item label="用户名">
{{ userInfo.username }}
</a-descriptions-item>
<a-descriptions-item label="邮箱">
{{ userInfo.email }}
</a-descriptions-item>
<a-descriptions-item label="注册时间">
{{ userInfo.registerDate }}
</a-descriptions-item>
</a-descriptions>
</a-card>
<a-card title="修改密码">
<a-form :model="passwordForm" :rules="passwordRules" @finish="handleChangePassword">
<a-form-item label="原密码" name="oldPassword">
<a-input-password v-model:value="passwordForm.oldPassword" />
</a-form-item>
<a-form-item label="新密码" name="newPassword">
<a-input-password v-model:value="passwordForm.newPassword" />
</a-form-item>
<a-form-item label="确认新密码" name="confirmPassword">
<a-input-password v-model:value="passwordForm.confirmPassword" />
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit">
修改密码
</a-button>
</a-form-item>
</a-form>
</a-card>
</div>
</a-layout-content>
</a-layout>
</a-layout>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Navigation from '@/components/navigation.vue'
import { useUserStore } from '@/stores/user'
const collapsed = ref(false)
const userStore = useUserStore()
const userInfo = ref({
username: '张三',
email: 'zhangsan@example.com',
registerDate: '2025-01-01'
})
interface PasswordForm {
oldPassword: string
newPassword: string
confirmPassword: string
}
const passwordForm = ref<PasswordForm>({
oldPassword: '',
newPassword: '',
confirmPassword: ''
})
const passwordRules = {
oldPassword: [{ required: true, message: '请输入原密码' }],
newPassword: [{ required: true, message: '请输入新密码' }],
confirmPassword: [{ required: true, message: '请确认新密码' }]
}
const handleChangePassword = (values: PasswordForm) => {
if (values.newPassword !== values.confirmPassword) {
console.log('两次输入的密码不一致')
return
}
console.log('修改密码:', values)
}
</script>
<style scoped>
.profile {
height: 100%;
}
.content {
margin: 24px 16px;
padding: 24px;
background: #fff;
min-height: 280px;
}
.content-inner {
padding: 24px;
}
</style>