黑马大事件项目开发

项目介绍

演示网站:

https://fe-bigevent-web.itheima.net/login

实现

1)创建项目

npm init vue@latest

2)安装项目需要的依赖

npm install element-plus --save
npm install axios
npm install sass -D

3)在main.js中加入Element的内容

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'//引入中文文件
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, component)
}
app.use(ElementPlus, {locale: zhCn,
})
app.mount('#app')

4)复制准备好的资料

  1. 删除asserts、components下面自动生成的内容
  2. 将资料中的静态资源拷贝到assets目录下

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用户功能

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

查询性质:(如果查询有条件, 页面绑定,收集条件)声明变量 --> 发送请求,获取数据—>绑定页面

增删改性质:页面绑定 --> 发送请求–> 处理返回结果

注册功能

将Login.vue复制到views目录下,然后从App.Vue中引入此组件

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实现注册功能

<script setup>
import { User, Lock } from '@element-plus/icons-vue'
import axios from 'axios';
import { ElMessage } from 'element-plus';
import { ref } from 'vue'
//控制注册与登录表单的显示, 默认显示注册
const isRegister = ref(false)//用户注册
const registerData = ref({username: '',password: '',password2: ''
})
const register = function () {axios.post('http://localhost:8080/user/register', registerData.value).then(resp => {if (resp.data.code == 0) {ElMessage.success('注册成功!')isRegister.value = false} else {ElMessage.error(resp.data.msg)}}).catch(err => {console.error(err)})
}
</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注册表单 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister"><el-form-item><h1>注册</h1></el-form-item><el-form-item><el-input :prefix-icon="User" v-model="registerData.username" placeholder="请输入用户名"></el-input></el-form-item><el-form-item><el-input :prefix-icon="Lock" type="password" v-model="registerData.password"placeholder="请输入密码"></el-input></el-form-item><el-form-item><el-input :prefix-icon="Lock" type="password" v-model="registerData.password2"placeholder="请输入再次密码"></el-input></el-form-item><!-- 注册按钮 --><el-form-item><el-button class="button" type="primary" @click="register()" auto-insert-space>注册</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false">← 返回</el-link></el-form-item></el-form><!-- 登录表单 --><el-form ref="form" size="large" autocomplete="off" v-else><el-form-item><h1>登录</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="请输入用户名"></el-input></el-form-item><el-form-item><el-input name="password" :prefix-icon="Lock" type="password" placeholder="请输入密码"></el-input></el-form-item><el-form-item class="flex"><div class="flex"><el-checkbox>记住我</el-checkbox><el-link type="primary" :underline="false">忘记密码?</el-link></div></el-form-item><!-- 登录按钮 --><el-form-item><el-button class="button" type="primary" auto-insert-space>登录</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true">注册 →</el-link></el-form-item></el-form></el-col></el-row>
</template>

跨域问题

当我们点击注册按钮,发送请求的时候,会出现下面的跨域问题

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

跨域问题是由于浏览器的同源策略引起的,当浏览器从一个域名的网页去请求另一个域名的资源时,出现域名、端口、协议任一不同,都属于跨域

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

跨域问题可以在前后任意一端进行解决,目前大部分企业项目会选择在前端代码中添加代理进行解决

vie.config.js中配置代理

import { fileURLToPath, URL } from 'node:url'import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'export default defineConfig({plugins: [vue(),],resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}},//配置代理server: {proxy: {'/api': {target: 'http://localhost:8080', // 后端服务器地址changeOrigin: true, // 是否改变请求域名rewrite: (path) => path.replace(/^\/api/, '')//将原有请求路径中的api替换为''}}}
})

修改发送请求的代码

//用户注册
const register = function(){// axios.post('http://localhost:8080/user/register',registerData.value).then(resp=>{//     console.log(resp.data);// })axios.post('/api/user/register',registerData.value).then(resp=>{console.log(resp.data);if(resp.data.code == 0){ElMessage.success('注册成功!')isRegister.value = false}else{ElMessage.error(resp.data.message)}}).catch(err => {console.log(err)})
}

登录功能

定义数据和方法

//用户登录
const loginData = ref({username: '',password: ''
})
const login = function () {axios.post('/api/user/login', loginData.value).then(resp => {if (resp.data.code == 0) {ElMessage.success('登录成功!')//跳转首页} else {ElMessage.error(resp.data.message);}}).catch(err => {console.error(err)})
}

页面绑定

			<!-- 登录表单 --><el-form ref="form" size="large" autocomplete="off" :model="loginData" v-else><el-form-item><h1>登录</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="请输入用户名" v-model="loginData.username"></el-input></el-form-item><el-form-item><el-input name="password" :prefix-icon="Lock" type="password" v-model="loginData.password" placeholder="请输入密码"></el-input></el-form-item><el-form-item class="flex"><div class="flex"><el-checkbox>记住我</el-checkbox><el-link type="primary" :underline="false">忘记密码?</el-link></div></el-form-item><!-- 登录按钮 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="login()">登录</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true">注册 →</el-link></el-form-item></el-form>

Api封装

在src目录下创建util/request.js

//导入axios
import axios from 'axios';
import { ElMessage } from 'element-plus';//复制axios实例
const request = axios.create({baseURL: "/api"
})//为request实例添加响应拦截器
request.interceptors.response.use(result => {if (result.data.code === 0) {return result.data;}//报错ElMessage.error(result.data.message ? result.data.message : "服务器异常");return Promise.reject(result.data);},err => {console.log(err);return Promise.reject(err);}
)//导出request对象
export default request;

在src目录下创建/api/user.js

//导入request.js
import request from "@/util/request";//用户注册
export function registerAPI(params) {return request.post('/user/register', params.value);
}//用户登录
export function loginAPI(params) {return request.post('/user/login', params.value);
}

修改Login.vue中的方法

import {registerApi,loginApi} from  '@/api/user.js'//用户注册
const register = async function () {let data = await registerApi(registerData);ElMessage.success('注册成功!');
}//用户登录
const login = async function () {let result = await loginAPI(loginData);ElMessage.success('登录成功!');
}

路由

路由介绍

在App.vue中,不能同时展示Login.vue和Layout.vue的原因是vue属于单页面应用程序

也就是说一个网站中显示一个页面,所有的功能与交互都在这唯一的一个页面内完成

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实现上面内容就要用到路由,它就是根据浏览器的访问路径不同,将不同的组件渲染到唯一的一个页面上

而路由的核心就是配置页面路径和访问组件之间的映射关系

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用路由

添加页面

复制资料中的Home.vue到views目录中

安装路由

  1. 添加依赖
npm install vue-router@4
  1. 在src/router目录下,定义一个index.js文件,内容如下
//导入vue-router
import { createRouter, createWebHistory } from 'vue-router'//导入组件
import Login from '@/views/Login.vue'
import Home from '@/views/Home.vue'//定义路由关系
const routes = [{ path: '/login', component: Login },{ path: '/', component: Home }
]//创建路由器
const router = createRouter({history: createWebHistory(),routes: routes
});export default router
  1. 在main.js中导入创建应用实例的js文件,并调用实例的use方法使用路由器
// 导入
import router from '@/router'// 使用
app.use(router)
  1. 在App.vue文件的template标签中,定义router-view标签

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 在浏览器地址栏分别访问:http://localhost:5173/ 和 http://localhost:5173/login

登录跳转

在登录成功后,需要通过代码的方式将页面切换到首页,此时就需要调用路由器相关的API

//用户登录
const login = async function () {let result = await loginAPI(loginData);ElMessage.success('登录成功!');router.push("/")
}

子路由

在咱们的主页面中,当用户点击左侧的菜单时,右侧主区域的内容需要发生变化,

将来每切换一个菜单,右侧需要加载对应组件的内容进行展示,像这样的场景咱们也需要使用路由来完成

由于这些组件都需要在Layout.vue中展示, 而Layout.vue本身已经参与了路由,因此我们需要在Layout.vue中通过子路由的方式来实现

提供组件

将提供好的页面复制到项目中

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

配置子路由

在src/router/index.js中配置子路由

//导入vue-router
import { createRouter, createWebHistory } from 'vue-router'//导入组件
import Login from '@/views/Login.vue'
import Layout from '@/views/Layout.vue'
import ArticleCategory from '@/views/article/ArticleCategory.vue'
import ArticleManage from '@/views/article/ArticleManage.vue'
import UserInfo from '@/views/user/UserInfo.vue'
import UserAvatar from '@/views/user/UserAvatar.vue'
import UserResetPassword from '@/views/user/UserResetPassword.vue'//定义路由关系
const routes = [{ path: '/login', component: Login },{path: '/',component: Layout,//重定向redirect: '/article/manage',//子路由children: [{ path: '/article/category', component: ArticleCategory },{ path: '/article/manage', component: ArticleManage },{ path: '/user/info', component: UserInfo },{ path: '/user/avatar', component: UserAvatar },{ path: '/user/password', component: UserResetPassword },]}
]//创建路由器
const router = createRouter({history: createWebHistory(),routes: routes
});export default router

添加子路由锚点

在Layout.vue组件的右侧中间区域,添加router-view标签

<!-- 中间区域 -->
<el-main><div style="width: 1290px; height: 570px;border: 1px solid red;"><router-view></router-view></div>
</el-main>

添加跳转链接

为左侧按钮添加路由链接

        <!-- 左侧菜单 --><el-aside width="200px"><div class="el-aside__logo"></div><el-menu active-text-color="#ffd04b" background-color="#232323"  text-color="#fff" router><el-menu-item index="/article/category"><el-icon><Management /></el-icon><span>文章分类</span></el-menu-item><el-menu-item index="/article/manage"><el-icon><Promotion /></el-icon><span>文章管理</span></el-menu-item><el-sub-menu ><template #title><el-icon><UserFilled /></el-icon><span>个人中心</span></template><el-menu-item index="/user/info"><el-icon><User /></el-icon><span>基本资料</span></el-menu-item><el-menu-item index="/user/avatar"><el-icon><Crop /></el-icon><span>更换头像</span></el-menu-item><el-menu-item index="/user/password"><el-icon><EditPen /></el-icon><span>重置密码</span></el-menu-item></el-sub-menu></el-menu></el-aside>

文章分类功能

查询列表

创建src/api/category.js文件编写查询分类的功能

//导入请求工具类
import request from '@/util/request.js'//文章分类列表查询
export function findArticleCategoryListAPI(){return request.get('/category')
}

在ArticleCategory.vue中定义查询方法

import { findArticleCategoryListAPI } from '@/api/category';//获取所有文章分类数据
const categorys = ref([])
const findAllCategory = async function () {let result = await findArticleCategoryListAPI();categorys.value = result.data;
}
findAllCategory();

但是上述的代码并不能真正的获取到所有文章分类数据,服务器响应状态码为401,因为目前请求头中并没有携带token

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们后面所发送的请求都需要在请求头中携带token,这可以借助请求拦截器实现

而这个token是在登录成功返回来的,所有我们还要修改登录成功的代码,将返回的token存储在LocalStorage中

① 修改登录代码

//用户登录
const login = async function () {let result = await loginAPI(loginData);//将token报错到localStorage中localStorage.setItem("BIG_EVENT_TOKEN", result.data);ElMessage.success('登录成功!');router.push("/")
}

② 修改request工具类

//导入axios
import router from '@/router';
import axios from 'axios';
import { ElMessage } from 'element-plus';//复制axios实例
const request = axios.create({baseURL:"/api"
})//为request添加请求拦截器
request.interceptors.request.use((config)=>{//在发送请求之前做什么let token = localStorage.getItem("BIG_EVENT_TOKEN");//如果token中有值,在携带if(token){config.headers.Authorization=token}return config},(err)=>{//如果请求错误做什么Promise.reject(err)}
)//为request实例添加拦截器
request.interceptors.response.use(result => {if(result.data.code ===0){return result.data;}//报错ElMessage.error(result.data.msg ? result.data.msg : "服务器异常");return Promise.reject(result.data);},err=>{console.log(err);//如果响应状态码时401,代表未登录,给出对应的提示,并跳转到登录页if(err.response.status===401){ElMessage.error('请先登录!')router.push('/login')}else{ElMessage.error('服务异常');}return Promise.reject(err);}
)//导出request对象
export default request;

添加分类

category.js

//新增文章分类
export function saveCategoryAPI(params){return request.post('/category',params.value)
}

在ArticleCategory.vue中js部分

const dialogVisible = ref(false)//控制添加弹窗显示
const categoryForAdd = ref({})//添加分类对象
const saveCategory = async function () {let result = await saveCategoryAPI(categoryForAdd);//清空表单categoryForAdd.value = {}//关闭弹框dialogVisible.value = false//重新查询findAllCategory()
}

在ArticleCategory.vue中html部分

    <!-- 添加分类弹窗 --><el-dialog v-model="dialogVisible" title="添加弹层" width="30%"><el-form label-width="100px" style="padding-right: 30px"><el-form-item label="分类名称"><el-input v-model="categoryForAdd.categoryName" minlength="1" maxlength="10"></el-input></el-form-item><el-form-item label="分类别名"><el-input v-model="categoryForAdd.categoryAlias" minlength="1" maxlength="15"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="saveCategory()"> 确认 </el-button></span></template></el-dialog>

回显分类

在actegory.js新增主键查询方法

//根据id查询
export function findCategoryByIdAPI(id){return request.get('/category/'+id)
}

点击修改,触发查询回显方法

<el-button :icon="Edit" circle plain type="primary" @click="openUpdateDialog(row.id)"></el-button>

发送请求,获取分类

//打开修改框
const dialogVisible2 = ref(false)//控制修改弹窗显示
const categoryForUpdate = ref({})//添加修改分类对象
const openUpdateDialog = async function(id){//弹层dialogVisible2.value = true//查询let result = await findCategoryByIdAPI(id);//回显categoryForUpdate.value = result.data;
}

回显到页面上

    <!-- 修改分类弹窗 --><el-dialog v-model="dialogVisible2" title="修改弹层" width="30%"><el-form label-width="100px" style="padding-right: 30px"><el-form-item label="分类名称"><el-input v-model="categoryForUpdate.categoryName" minlength="1" maxlength="10"></el-input></el-form-item><el-form-item label="分类别名"><el-input v-model="categoryForUpdate.categoryAlias" minlength="1" maxlength="15"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogVisible2 = false">取消</el-button><el-button type="primary" @click="updateCategory()"> 确认 </el-button></span></template></el-dialog>

category.js

//修改文章分类
export function updateCategoryAPI(params){return request.put('/category',params.value)
}

修改分类

//修改分类
const updateCategory = async function () {let result = await updateCategoryAPI(categoryForUpdate);//关闭弹框dialogVisible2.value = false//重新查询findAllCategory()
}

删除分类

触发删除方法

<el-button :icon="Delete" circle plain type="danger" @click="deleteCategory(row.id)"></el-button>

category.js

//根据id删除
export function deleteCategoryByIdAPI(id){return request.delete('/category/'+id)
}

删除方法

//删除
const deleteCategory = async function (id) {ElMessageBox.confirm('你确认删除该分类信息吗?', '温馨提示',{confirmButtonText: '确认',cancelButtonText: '取消',type: 'warning',}).then(async () => {//用户点击了确认let result = await deleteCategoryByIdAPI(id);findAllCategory()ElMessage({type: 'success',message: '删除成功',})}).catch(() => {//用户点击了取消ElMessage({type: 'info',message: '取消删除',})})
}

文章管理功能

文章列表

文章分类选项

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

//文章分类数据模型
const categorys = ref([])
const findAllCategory = async function () {let result = await findArticleCategoryListAPI();categorys.value = result.data;
}
findAllCategory();

分页查询

article.js

//导入请求工具类
import request from '@/util/request.js'//分页列表查询
export function findArticleListByPageAPI(categoryId, state, pageNum, pageSize) {return request.get('/article', {params: {"categoryId": categoryId.value,"state": state.value,"pageNum": pageNum.value,"pageSize": pageSize.value,}});
}

页面渲染

<script setup>
import { findArticleListByPageAPI } from '@/api/article';
import { findArticleCategoryListAPI } from '@/api/category';
import {Edit,Delete, Plus
} from '@element-plus/icons-vue'
import { ref } from 'vue'
//import func from '../../../vue-temp/vue-editor-bridge';
//控制抽屉是否显示
const visibleDrawer = ref(false)
//添加表单数据模型
const articleModel = ref({title: '',categoryId: '',coverImg: '',content: '',state: ''
})//文章分类数据模型
const categorys = ref([])
const categoryMap = new Map()
const findArticleCategoryList = async function(){let result = await findArticleCategoryListAPI()categorys.value = result.datafor(let category of categorys.value){categoryMap.set(category.id,category.categoryName);}
}
findArticleCategoryList()//用户搜索时选中的分类id
const categoryId = ref('')
//用户搜索时选中的发布状态
const state = ref('')//分页条数据模型
const pageNum = ref(1)//当前页
const total = ref(20)//总条数
const pageSize = ref(3)//每页条数
const onSizeChange = (size) => {//当每页条数发生了变化,调用此函数//pageSize.value = sizefindArticleListByPage()
}
const onCurrentChange = (num) => {//当前页码发生变化,调用此函数//pageNum.value = numfindArticleListByPage()
}//文章列表数据模型
const articles = ref([])
const findArticleListByPage = async function(){let result =await findArticleListByPageAPI(categoryId,state,pageNum,pageSize)total.value = result.data.totalfor(let item of result.data.items){item.categoryName = categoryMap.get(item.categoryId)}articles.value = result.data.items
}
findArticleListByPage();</script>
<template><el-card class="page-container"><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary" @click="visibleDrawer = true">添加文章</el-button></div></div></template><!-- 搜索表单 --><el-form inline><el-form-item label="文章分类:" style="width: 250px;"><el-select placeholder="请选择" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id"></el-option></el-select></el-form-item><el-form-item label="发布状态:" style="width: 250px;"><el-select placeholder="请选择" v-model="state"><el-option label="已发布" value="1"></el-option><el-option label="草稿" value="0"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="findArticleListByPage">搜索</el-button><el-button>重置</el-button></el-form-item></el-form><!-- 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章标题" width="400" prop="title"></el-table-column><el-table-column label="分类" prop="categoryName"></el-table-column><el-table-column label="发表时间" prop="createTime"> </el-table-column><el-table-column label="状态" prop="state"><template #default="{ row }">{{row.state == 1 ? '已发布' : '草稿'}}</template></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary"></el-button><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="没有数据" /></template></el-table><!-- 分页条 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5, 10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /></el-card><!-- 抽屉 --><el-drawer v-model="visibleDrawer" title="添加文章" direction="rtl" size="50%"><!-- 添加文章表单 --><el-form :model="articleModel" label-width="100px"><el-form-item label="文章标题"><el-input v-model="articleModel.title" placeholder="请输入标题"></el-input></el-form-item><el-form-item label="文章分类"><el-select placeholder="请选择" v-model="articleModel.categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id"></el-option></el-select></el-form-item><el-form-item label="文章封面"><el-upload class="avatar-uploader" :auto-upload="false" :show-file-list="false"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="文章内容"><div class="editor">富文本编辑器</div></el-form-item><el-form-item><el-button type="primary">发布</el-button><el-button type="info">草稿</el-button></el-form-item></el-form></el-drawer>
</template>
<style lang="scss" scoped>
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}/* 抽屉样式 */
.avatar-uploader {:deep() {.avatar {width: 178px;height: 178px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}}
}.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
</style>

添加文章

富文本编辑器

文章内容需要使用到富文本编辑器,这里咱们使用一个开源的富文本编辑器 Quill

官网地址: https://vueup.github.io/vue-quill/

安装:

npm install @vueup/vue-quill@latest --save

导入组件和样式:

import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'

页面长使用quill组件:

<quill-editor theme="snow" v-model:content="articleModel.content" contentType="html"></quill-editor>

图片上传

将来当点击+图标,选择本地图片后,el-upload这个组件会自动发送请求,把图片上传到指定的服务器上,而不需要我们自己使用axios发送异步请求,所以需要给el-upload标签添加一些属性,控制请求的发送

auto-upload:是否自动上传

action: 服务器接口路径

name: 上传的文件字段名

headers: 设置上传的请求头

on-success: 上传成功的回调函数

//获取token
const getToken = function(){return localStorage.getItem("BIG_EVENT_TOKEN")
}//上传图片成功回调
const uploadSuccess = (img) => {//img就是后台响应的数据,格式为:{code:状态码,message:提示信息,data: 图片的存储地址}articleModel.value.coverImg=img.data
}
<el-form-item label="文章封面"><el-upload class="avatar-uploader" :show-file-list="false":auto-upload="true"  action="/api/upload":name="file" :headers="{'Authorization':getToken()}":on-success = "uploadSuccess"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload>
</el-form-item>

保存文章实现

article.js中提供添加文章函数

//添加文章
export function saveArticleAPI(params){return request.post('/article',params.value)
}

为已发布和草稿按钮绑定事件

<el-form-item><el-button type="primary" @click="addArticle('1')">发布</el-button><el-button type="info" @click="addArticle('0')">草稿</el-button>
</el-form-item>

ArticleManage.vue中提供addArticle函数完成添加文章接口的调用

//添加文章
const addArticle = async function (state) {articleModel.value.state = statelet result = await saveArticleAPI(articleModel);ElMessage.success(result.message ? result.message : '添加成功')//再次调用getArticles,获取文章findByPage()//隐藏抽屉visibleDrawer.value = false
}
<el-upload class="avatar-uploader" :show-file-list="false":auto-upload="true"  action="/api/upload":name="file" :headers="{'Authorization':getToken()}":on-success = "uploadSuccess"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
```

保存文章实现

article.js中提供添加文章函数

//添加文章
export function saveArticleAPI(params){return request.post('/article',params.value)
}

为已发布和草稿按钮绑定事件

<el-form-item><el-button type="primary" @click="addArticle('1')">发布</el-button><el-button type="info" @click="addArticle('0')">草稿</el-button>
</el-form-item>

ArticleManage.vue中提供addArticle函数完成添加文章接口的调用

//添加文章
const addArticle = async function (state) {articleModel.value.state = statelet result = await saveArticleAPI(articleModel);ElMessage.success(result.message ? result.message : '添加成功')//再次调用getArticles,获取文章findByPage()//隐藏抽屉visibleDrawer.value = false
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/419750.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

金融行业项目管理软件分享!华福证券上线奥博思 PowerProject 项目管理系统

如何正确的评估一款项目管理软件是否适合金融行业&#xff1f;金融机构例如银行、券商等企业在选型项目管理软件时可以从以下几方面考虑&#xff1a; 1&#xff0c;管理驾驶舱 金融机构需要对项目的各项数据进行深入分析&#xff0c;以便做出科学的决策。软件应能够生成详细的…

Cesium 展示——静态水添加动态波纹,模拟真实水面效果

文章目录 需求分析材料准备根据几何实例创建贴地面图元将图元添加到集合补充需求 分析 材料准备 首先我们需要准备水波的图片,放在最后大家自行

AI prompt(提示词)

# 好用的用于学习的AI提示词 ## 费曼学习法 请使用费曼学习法&#xff0c;用简单的语言解释&#xff08;量子力学&#xff09;是什么&#xff0c;并提供一个简单的例子来说明它如何应用 ## 帕累托法则&#xff08;80/20原则&#xff09; 将&#xff08;量子力学&#xff09;最…

Android 12 SystemUI下拉状态栏禁止QuickQSPanel展开

1.概述 遇到需求&#xff0c;QuickQSPanel首次下拉后展示快捷功能模块以后就是显示QuickQSPanel&#xff0c;而不展开QSPanel&#xff0c;接下来要从下滑手势下拉出状态栏分析功能实现。也就是直接是展开状态。 2、涉及核心类 frameworks\base\packages\SystemUI\src\com\and…

数据资产:新时代的财富密码

嘿&#xff0c;朋友们&#xff01;今天要和大家聊聊一个在数字化时代重要的话题 —— 数据资产&#xff0c;它可是新时代的财富密码哦 数据资产的庐山真面目 数据资产呢&#xff0c;就是企业或者个人拥有或控制的&#xff0c;能带来经济效益的数据资源。它的特点可不少呢。首先…

网络设备安全

网络设备安全概况 交换机安全威胁&#xff1a;交换机是网络基础设备&#xff0c;负责网络通信数据包的交换传输 交换机面临的网络安全威胁&#xff1a; 路由器安全威胁 网络设备安全机制与实现技术 认证机制&#xff1a;为防止网络设备滥用&#xff0c;网络设备读用户身份进行…

服务器断电,Centos7启动报错

服务器上安装了VMWare&#xff0c;在之上安装了Centos7&#xff0c;突发断电&#xff0c;再次启动报错 mount /dev/mapper/centos-root /sysroot执行后提示&#xff1a;xfs(dm-0):internal error xfs_want_corrupted_goto at line 993 of file fs/xfs/xfs_trans.c. Caaler xfs_…

E31.【C语言】练习:指针运算习题集(上)

Exercise 1 求下列代码的运行结果 #include <stdio.h> int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d",*(ptr - 1));return 0; } 答案速查: 分析&#xff1a; Exercise 2 求下列代码的运行结果 //在x86环境下 //假设结…

中秋之美——html5+css+js制作中秋网页

中秋之美——html5cssjs制作中秋网页 一、前言二、功能展示三、系统实现四、其它五、源码下载 一、前言 八月十五&#xff0c;秋已过半&#xff0c;是为中秋。 “但愿人长久&#xff0c;千里共婵娟”&#xff0c;中秋时节&#xff0c;气温已凉未寒&#xff0c;天高气爽&#x…

以太网--TCP/IP协议(一)

概述 以太网是局域网的一种&#xff0c;其他的比如还有令牌环、FDDI。和局域网对应的就是广域网&#xff0c;如Internet&#xff0c;城域网等。 从网络层次看&#xff0c;局域网协议主要偏重于低层&#xff08;业内一般把物理层、数据链路层归为低层&#xff09;。以太网协议…

2024Mysql And Redis基础与进阶操作系列(1)作者——LJS[含MySQL的下载、安装、配置详解步骤及报错对应解决方法]

目录 1.数据库与数据库管理系统 1.1 数据库的相关概念 1.2 数据库与数据库管理系统的关系 1.3 常见的数据库简介 Oracle 1. 核心功能 2. 架构和组件 3. 数据存储和管理 4. 高可用性和性能优化 5. 安全性 6. 版本和产品 7. 工具和接口 SQL Server 1. 核心功能 2. 架构和组…

Windows系统引入全新 Android 体验?快来尝鲜!

听说微软 Windows 11 操作系统引入全新体验 &#xff1a;实时访问 Android 设备图片。 意思就是在Android 设备上捕获了新照片或屏幕截图时&#xff0c;Windows 上立刻收到通知&#xff0c;且可以不用插数据线就能访问。 用Windows连接手机的功能其实早在Windows10就已经有的了…

GIS大事件!Bentley收购Cesium

9月6日&#xff0c;Cesium官方宣布加入Bentley。 Bentley我们并不陌生。最初Acute3D被Bentley公司收购&#xff0c;旗下软件由Smart3DCapture转型到ContextCapture&#xff0c;现又改名 iTwin Capture。 如今又收购了Cesium。 Cesium官方表示&#xff0c;Cesium开发平台与iTwi…

全能型AI vs专业型AI:谁主沉浮?

你是否听说过即将到来的AI革命&#xff1f;OpenAI的"草莓"模型即将在今年秋季问世&#xff0c;它不仅能解决复杂的数学问题&#xff0c;还能为你制定营销策略。这是否意味着AI正在向"全能型"发展&#xff1f;专业型AI是否即将成为历史&#xff1f;让我们一…

ROPS 自动化快速构造缓冲区溢出ROP链工具

项目地址:https://github.com/MartinxMax/ROPS ROPS 快速自动化构造ROP&#xff08;Return-Oriented Programming&#xff09;链的脚本&#xff0c;用于生成ROP攻击的有效载荷。 Usage $ ./rops.sh $ ./rops.sh /home/ayush/.binary/rop 该脚本将根据提供的二进制文件自动生…

抢鲜体验 PolarDB PG 15 开源版

unsetunsetPolarDB 商业版unsetunset 8 月&#xff0c;PolarDB PostgreSQL 版兼容 PostgreSQL 15 版本&#xff08;商业版&#xff09;正式发布上线。 当前版本主要增强优化了以下方面&#xff1a; 改进排序功能&#xff1a;改进内存和磁盘排序算法。 增强SQL功能&#xff1a;支…

全能型 AI 的崛起:未来的市场宠儿还是昙花一现?

近日&#xff0c;OpenAI 宣布将在秋季推出代号为“草莓”的新一代 AI 模型。这款 AI 被描述为全能型&#xff0c;从处理复杂的数学问题到应对主观性强的营销策略&#xff0c;它的能力可以覆盖多个领域。听起来像是科技界的“万能钥匙”&#xff0c;无论面对什么问题&#xff0c…

等保2.0测评之Nginx 中间件

前期调研 nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器&#xff0c;一般主要功能会有两种&#xff0c;一种作为一个HTTP服务器进行网站的发布处理&#xff0c;另外一种nginx可以作为反向代理进行负载均衡的实现。所以这里填主要功能的时候就要分清。 查看N…

深入了解以太坊

1. 以太坊编程语言和操作码 以太坊中智能合约的代码以高级语言编写&#xff0c;如 Serpent、LLL、Solidity 或 Viper,并可转换为 EVM 可以理解的字节码&#xff0c;以便执行。 Solidity 是为以太坊开发的高级语言之一&#xff0c;它具有类似 JavaScript 的语法&#xff0c;可以…

骨传导耳机哪个牌子最好?精选五款热门不踩雷品牌分享

每个人对耳机要求不同、使用场景不同&#xff0c;适合的耳机自然也不同&#xff0c;但是骨传导耳机不用入耳的佩戴方式更加安全、舒适和卫生&#xff0c;还能听到周围的环境声&#xff0c;因此被更多的运动爱好者所喜爱&#xff0c;今天我将我这么多年运动使用体验感较好的几款…