使用SpringBoot+Vue3开发项目(2)---- 设计文章分类的相关接口及页面

目录

 一.所用技术栈:

二.后端开发:

1.文章分类列表渲染:

 2.新增文章分类:

 3.编辑文章分类:

4.删除文章分类 :

5.完整三层架构后端代码:

(1)Controller层:

 (2)Service层:

(3)Mapper层:

三.前端开发:

1.路由的使用:

(1)Vue Router的使用:

(2)使用vue-router来实现登录页面与主页面展示效果:

 ①创建 index.js :

②在 main.js 导入创建的路由器:

(3)子路由的使用:

①添加五个组件

②配置子路由:

③在主页面的展示区声明router-view标签:

④给菜单项设置index属性,设置点击后的路由地址:

2.对于文章分类的前端开发:

(1)导入ArticleCategory.vue文件:

(2)文章分类的前端相关请求开发:

①Pinia状态管理库:

 1.在main.js文件导入pinia:

2.定义store状态:

②文章分类列表渲染:

③添加请求拦截器:

④使用拦截器来对未登录统一处理:

(3)新增文章分类:

(4)编辑文章分类与删除:


引言:上篇文章实现了用户的注册以及登录的接口及页面的开发,我们本篇博客讲述设计文章分类的相关接口及页面 =>

 一.所用技术栈:

  1. Vue3
  2. pinia
  3. axios
  4. element-plus
  5. router(路由)
  6. SpringBoot
  7. JWT

二.后端开发:

我们整个布局是下面的样子:

需要我们开发四个接口:

  • 文章分类列表渲染
  • 新增文章分类
  • 编辑文章分类
  • 删除文章分类 

因为这四个接口是基本的增删改查操作,所以这里不过多强调代码如何写,这里介绍一下参数校验的注解:

①在pojo类内属性上加的注解:

  1.  @NotNull:值不能为空
  2. @NotEmpty:值不能为空并且不能为空串
  3. @Email:格式是email邮箱格式
  4. @Patten(regexp="正则表达式"):判断格式

上面这四个需要在Controller层内参数前加上@Validated注解才能使用。

  1. @JsonIgnore:返回响应数据会忽略该属性(eg:可以在密码上加来加密)
  2. @JsonFormat(patten="yyyy-MM-dd HH:mm:ss"):时间格式

上面这两个注解使用时可不用加@Validated注解 。

②在Controller层内参数前加的注解:

  1. @URL:参数需要是地址格式

 而基于上面还是有些不够便捷,我们提出了分组校验的概念,在@NotNull@NotEmpty这两个注解前可加上(groups="接口的字节码")来限定哪些方法会使用,随后我们就再Controller层的参数前注解@Validated(接口的字节码),这样就可以实现分组校验。

如果我们同一个校验项属于多个分组,这样会很麻烦,这个时候我们就可以借助默认分组来处理   =>

若某个校验项没有指定分组,默认属于Default分组,分组之间可以继承,A extends B,那么A中可以拥有B中所有的校验项,所以在@Validated()括号内写继承Default的接口字节码就可以使用默认的校验项

@Data
public class Category {
//    @NotEmpty(groups = {Add.class,Update.class})@NotEmptyprivate String categoryName;//分类名称
//    @NotEmpty(groups = {Add.class,Update.class})@NotEmptyprivate String categoryAlias;//分类别名//若某个校验项没有指定分组,默认属于Default分组//分组之间可以继承,A extends B,那么A中可以拥有B中所有的校验项public interface Add extends Default {}public interface Update extends Default {}
}

基于上面的注解功能,我们在pojo包下的Category类属性前加上完整注解代码:

@Data
public class Category {@NotNull(groups = Update.class)private Integer id;//主键ID//    @NotEmpty(groups = {Add.class,Update.class})@NotEmptyprivate String categoryName;//分类名称
//    @NotEmpty(groups = {Add.class,Update.class})@NotEmptyprivate String categoryAlias;//分类别名private Integer createUser;//创建人ID@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime createTime;//创建时间@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime updateTime;//更新时间//若某个校验项没有指定分组,默认属于Default分组//分组之间可以继承,A extends B,那么A中可以拥有B中所有的校验项public interface Add extends Default {}public interface Update extends Default {}
}

介绍完这个注解开发,我们就可以开发后端接口。 

1.文章分类列表渲染:

 根据文档要求,我们需要返回一个列表,每个元素是一个Category类封装的数据。 

 2.新增文章分类:

这里新增数据就可以使用@Validated注解来判断数据

根据文档要求,我们只需根据用户 id 保存两个数据值,为了在我们JWT令牌中拿到用户id,我们就会使用ThreadLocal来开辟线程空间存储JWT令牌,如果想使用就可以在线程空间获取JWT令牌中的用户id。

 如果不了解ThreadLocal请查看下面博客:使用ThreadLocal来存取单线程内的数据-CSDN博客

Map<String,Object> map = ThreadLocalUtil.get();
Integer userId = (Integer) map.get("id");
category.setCreateUser(userId);

 3.编辑文章分类:

 这里更新数据就可以使用@Validated注解来判断数据。

4.删除文章分类 :

删除就没什么好说的了,这里就不过多强调。

5.完整三层架构后端代码:

(1)Controller层:

@RestController
@RequestMapping("/category")
public class CateController {@Autowiredprivate CategoryService categoryService;@PostMappingpublic Result add(@RequestBody @Validated(Category.Add.class) Category category){categoryService.add(category);return Result.success();}@GetMappingpublic Result<List<Category>> list(){List<Category> list = categoryService.list();return Result.success(list);}@PutMappingpublic Result update(@RequestBody @Validated(Category.Update.class) Category category){categoryService.update(category);return Result.success();}@DeleteMappingpublic Result delete(Integer id){categoryService.deleteById(id);return Result.success();}
}

 (2)Service层:

@Service
public class CategoryServiceImpl implements CategoryService {@Autowiredprivate CategoryMapper categoryMapper;@Overridepublic void add(Category category) {category.setCreateTime(LocalDateTime.now());category.setUpdateTime(LocalDateTime.now());Map<String,Object> map = ThreadLocalUtil.get();Integer userId = (Integer) map.get("id");category.setCreateUser(userId);categoryMapper.add(category);}@Overridepublic List<Category> list() {Map<String,Object> map = ThreadLocalUtil.get();Integer userId = (Integer) map.get("id");return categoryMapper.list(userId);}@Overridepublic Category findById(Integer id) {return categoryMapper.findById(id);}@Overridepublic void update(Category category) {category.setUpdateTime(LocalDateTime.now());categoryMapper.update(category);}@Overridepublic void deleteById(Integer id) {categoryMapper.deleteById(id);}
}

(3)Mapper层:

@Mapper
public interface CategoryMapper {@Insert("insert into category(category_name,category_alias,create_user,create_time,update_time)" +" values (#{categoryName},#{categoryAlias},#{createUser},#{createTime},#{updateTime})")void add(Category category);@Select("select * from category where create_user = #{userId}")List<Category> list(Integer userId);@Select("select * from category where id = #{id}")Category findById(Integer id);@Update("update category set category_name = #{categoryName} , category_alias = #{categoryAlias} , update_time = #{updateTime} where id = #{id}")void update(Category category);@Delete("delete from category where id = #{id}")void deleteById(Integer id);
}

三.前端开发:

1.路由的使用:

路由指的是根据不同的访问路径,展示不同组件的内容。

Vue Router 是 Vue.js 的官方路由。

(1)Vue Router的使用:

  1. 安装vue-router: cnpm install vue-router@4
  2. 在src/router/index.js中创建路由器,并导出
  3. 在vue应用实例中使用vue-router
  4. 声明router-view标签,展示组件内容

(2)使用vue-router来实现登录页面与主页面展示效果:

 ①创建 index.js :
//导入vue-router
import { createRouter , createWebHistory } from "vue-router";//导入vue组件
import LoginVue from '@/views/Login.vue';
import LayoutVue from "@/views/Layout.vue";//定义路由关系
const routes = [{path:'/login',component: LoginVue},{path:'/',  //设置访问路径component: LayoutVue,  //设置访问路径对应的访问组件}
]//创建路由器
const router = createRouter({history: createWebHistory(), //路由模式routes: routes  //路由关系
})//导出路由器
export default router;
②在 main.js 导入创建的路由器:
import './assets/main.scss'import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
//导入创建的路由器
//index.js可以省略不写,会默认导入该文件
import router from '@/router'const app = createApp(App);
//将router传递至App.vue
app.use(router)
app.use(ElementPlus);
app.mount('#app')

③在App.vue声明标签:

<script setup></script><template><router-view></router-view>
</template><style></style>

这样就可以实现在同一页面显示不同组件。但是这样如果我们登录成功后,不会直接跳转主页面,那么这个时候我们需要通过路由来完成跳转主页面。

//导入路由器
import { useRouter } from 'vue-router';
const router = useRouter();
//通过路由跳转首页
router.push('跳转路径');

所以在login.vue文件内进行操作:

//导入路由器
import { useRouter } from 'vue-router';
const router = useRouter();
//表单数据校验
const login = async()=>{let result = await userLoginService(registerData.value);ElMessage.success(result.msg ? result.msg : '登录成功');//通过路由跳转首页router.push('/');
}

(3)子路由的使用:

为了在我们主页面下展示区点击按钮展示不同的子组件,我们就引入了子路由的知识:

上图是我们的每一级路由关系,我们想将五个vue文件在主页面Layout.vue中的展示区展示,就需要配置子路由 =>

①添加五个组件
②配置子路由:

在内部先定义路由关系,然后创建路由器,最后导出(暴露)路由器。

在路由关系内主页面内部设置 children 属性来声明五个子路由,并且为了不让主页面'/'为空,我们使用 redirect 来将 '/' 地址重定向为 '/article/manage' =>

//导入vue-router
import { createRouter , createWebHistory } from "vue-router";//导入vue组件
import LoginVue from '@/views/Login.vue';
import LayoutVue from "@/views/Layout.vue";
import ArticleCategoryVue from '@/views/article/ArticleCategory.vue'
import ArticleManageVue from '@/views/article/ArticleManage.vue'
import UserAvatarVue from '@/views/user/UserAvatar.vue'
import UserInfoVue from '@/views/user/UserInfo.vue'
import UserResetPasswordVue from '@/views/user/UserResetPassword.vue'//定义路由关系
const routes = [{path:'/login',component: LoginVue},{path:'/',component: LayoutVue,redirect: '/article/manage',  //重定向//子路由children:[{path:'/article/category',component: ArticleCategoryVue},{path:'/article/manage',component: ArticleManageVue},{path:'/user/avatar',component: UserAvatarVue},{path:'/user/info',component: UserInfoVue},{path:'/user/resetPassword',component: UserResetPasswordVue},]}
]//创建路由器
const router = createRouter({history: createWebHistory(), //路由模式routes: routes  //路由关系
})//导出路由器
export default router;
③在主页面的展示区声明router-view标签:
            <!-- 中间区域 --><el-main><!-- <div style="width: 1290px; height: 570px;border: 1px solid red;">内容展示区</div> --><!-- 路由 --><router-view></router-view></el-main>
④给菜单项设置index属性,设置点击后的路由地址:

这样设置后,当我们点击文字时候就可以自动在我们设置的标签router-view展示区来展示index='地址'中的地址。

<!-- 左侧菜单 -->
<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/resetPassword"><el-icon><EditPen /></el-icon><span>重置密码</span></el-menu-item></el-sub-menu></el-menu>
</el-aside>

所以这样我们就分别对五个vue组件开发就可以了。

2.对于文章分类的前端开发:

(1)导入ArticleCategory.vue文件:

<script setup>
import {Edit,Delete
} from '@element-plus/icons-vue'
import { ref } from 'vue'
const categorys = ref([{"id": 3,"categoryName": "美食","categoryAlias": "my","createTime": "2023-09-02 12:06:59","updateTime": "2023-09-02 12:06:59"},{"id": 4,"categoryName": "娱乐","categoryAlias": "yl","createTime": "2023-09-02 12:08:16","updateTime": "2023-09-02 12:08:16"},{"id": 5,"categoryName": "军事","categoryAlias": "js","createTime": "2023-09-02 12:08:33","updateTime": "2023-09-02 12:08:33"}
])
</script>
<template><el-card class="page-container"><template #header><div class="header"><span>文章分类</span><div class="extra"><el-button type="primary">添加分类</el-button></div></div></template><el-table :data="categorys" style="width: 100%"><el-table-column label="序号" width="100" type="index"> </el-table-column><el-table-column label="分类名称" prop="categoryName"></el-table-column><el-table-column label="分类别名" prop="categoryAlias"></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-card>
</template><style lang="scss" scoped>
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
</style>

我们通过定义响应式数据categorys来动态响应文章分类数据。

设置 :data="categorys" 将categorys数据绑定 table 表格,并 prop 属性来分别把属性绑定到  column 上。

(2)文章分类的前端相关请求开发:

  • 文章分类列表渲染
  • 新增文章分类
  • 编辑文章分类
  • 删除文章分类 

我们根据上面的四个内容开发 =>

一般我们都会再 js 文件内定义函数然后再vue组件进行使用请求函数。 

①Pinia状态管理库:

在article.js文件定义请求函数:

 为了传递JWT令牌,我们就会利用Pinia状态管理库,它允许跨组件或页面共享状态。

使用Pinia步骤:

  1. 安装pinia:cnpm install pinia
  2. 在vue应用实例中使用pinia
  3. 在src/stores/token.js中定义store
  4. 在组件中使用store 
 1.在main.js文件导入pinia:

这里因为Pinia是默认内存存储,刷新浏览器会丢失数据,我们使用pinia内的Persist插件就可以将Pinia中的数据持久化存储。

为了使用persist,我们需要安装persist:cnpm install pinia-persistedstate-plugin,然后再pinia中使用persist,并且需要再main.js导入一下。

import './assets/main.scss'import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from '@/router'
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-persistedstate-plugin'const app = createApp(App);
//创建createPinia()函数实例
const pinia = createPinia();
const persist = createPersistedState();
pinia.use(persist)
app.use(pinia)
app.use(router)
app.use(ElementPlus);
app.mount('#app')
2.定义store状态:

其实无外乎就是使用defineStore()方法,在方法内部分为两个参数来写 :

第一个参数:名字,确保唯一性。
第二个参数:函数,其内部可以定义状态的所有内容,其内部先创建一个响应式数据,然后设置获取数据以及删除数据的方法,最后返回数据以及方法。

加上Persist插件就可以将Pinia中的数据持久化存储。

//定义store
import { defineStore } from "pinia";
import {ref} from 'vue'
/*第一个参数:名字,确保唯一性第二个参数:函数,其内部可以定义状态的所有内容返回值:函数
*/
export const useTokenStore = defineStore('token',()=>{//定义状态内容//1.定义响应式变量const token = ref('');//2.定义函数来修改token值const setToken = (newToken)=>{token.value = newToken;}//3.定义函数来移除token值const removeToken = ()=>{token.value = '';}return {token,setToken,removeToken}
},{persist: true  //因为Pinia是默认内存存储,刷新浏览器会丢失数据,使用Persist插件就可以将Pinia中的数据持久化存储
}
);

这样我们就可以调用定义的useTokenStore来使用pinia了。

②文章分类列表渲染:

我们首先再Login.vue文件中把得到的token存储到pinia中:

//导入store状态
import { useTokenStore } from '@/stores/token.js';
//导入路由器
import { useRouter } from 'vue-router';
const tokenstore = useTokenStore();
const router = useRouter();
//表单数据校验
const login = async()=>{let result = await userLoginService(registerData.value);// alert(result.msg ? result.msg : '登录成功'); ElMessage.success(result.msg ? result.msg : '登录成功');//将得到的token存储到pinia中tokenstore.setToken(result.data);//通过路由跳转首页router.push('/');
}

然后再article.js中定义请求函数:

import request from '@/utils/request.js'
import { useTokenStore } from '@/stores/token.js';export const ArticleCategoryListService = ()=>{const tokenStore = useTokenStore();//在pinia中定义的响应式数据不需要加.value才能使用数据return request.get('/category',{headers:{'Authorization':tokenStore.token}});
} 

但是这样我们需要将剩下的请求函数都要传递JWT令牌,代码会很繁琐,这个时候我们就可以添加请求拦截器来使用回调函数来发送。

③添加请求拦截器:

在request.js文件中添加请求拦截器:

import { useTokenStore } from '@/stores/token.js';
//添加请求拦截器
instance.interceptors.request.use((config)=>{//请求前的回调const tokenStore = useTokenStore();if(tokenStore.token){//通过config调用headers获取请求头,在调用Authorization将JWT令牌存放到内部以此来添加统一的请求头config.headers.Authorization = tokenStore.token;}return config;},(err)=>{//请求错误的回调Promise.reject(err);//异步的状态转化成失败的状态}
)
export default instance;

修改article.js文件的请求函数:

import request from '@/utils/request.js'export const ArticleCategoryListService = ()=>{return request.get('/category');
} 
④使用拦截器来对未登录统一处理:

为了将没有登录的用户直接打开主界面,随后浏览器直接能跳转到登录页面,我们就可以添加响应拦截器来对未登录统一处理:

在request.js文件中必须通过下面的方式才能导入router进行使用 =>
import router from '@/router';

// import { useRouter } from 'vue-router';
// const router = useRouter();
//在request.js文件中必须通过下面的方式才能导入router进行使用
import router from '@/router'
//添加响应拦截器
instance.interceptors.response.use(result=>{//判断业务状态码if(result.data.code === 0){return result.data;}//操作失败// alert(result.data.message ? result.data.message : '服务异常')ElMessage.error(result.data.message ? result.data.message : '服务异常')//异步操作的状态转换为失败return Promise.reject(result.data);},err=>{//判断响应状态码401if(err.response.status === 401){ElMessage.error('请先登录');//使用路由跳转登录页面router.push('/login');}else{ElMessage.error('服务异常');}return Promise.reject(err);//异步的状态转化成失败的状态}
)
export default instance;

(3)新增文章分类:

我们为了实现上面操作,在Article.vue添加组件:

<!-- 添加分类弹窗 -->
<el-dialog v-model="dialogVisible" title="添加弹层" width="30%"><el-form :model="categoryModel" :rules="rules" label-width="100px" style="padding-right: 30px"><el-form-item label="分类名称" prop="categoryName"><el-input v-model="categoryModel.categoryName" minlength="1" maxlength="10"></el-input></el-form-item><el-form-item label="分类别名" prop="categoryAlias"><el-input v-model="categoryModel.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"> 确认 </el-button></span></template>
</el-dialog>

随后我们通过设置响应式数据来控制添加分类弹窗 =>

//控制添加分类弹窗
const dialogVisible = ref(false)

随后调用点击方法:

//添加分类数据模型
const categoryModel = ref({categoryName: '',categoryAlias: ''
})
//添加分类表单校验
const rules = {categoryName: [{ required: true, message: '请输入分类名称', trigger: 'blur' },],categoryAlias: [{ required: true, message: '请输入分类别名', trigger: 'blur' },]
}
//添加表单
import { ElMessage } from 'element-plus';
const addCategory = async ()=>{//调用接口let result = await ArticleCategoryAddService(categoryModel.value);ElMessage.success(result.message ? result.message : '添加成功');//获取文章分类的函数articleCategoryList();//让添加分类弹窗消失dialogVisible = false;//添加后值消失categoryModel = ref({categoryName: '',categoryAlias: ''})
}

 在article.js中:

import request from '@/utils/request.js'export const ArticleCategoryAddService = (categoryData)=>{return request.post('/category',categoryData);
}

(4)编辑文章分类与删除:

在组件内:

我们复用添加的弹框给编辑弹框,在下面点击事件使用三目运算符进行处理。 

<script setup>
import {Edit,Delete
} from '@element-plus/icons-vue'
import { ref } from 'vue'
const categorys = ref([]);
//声明异步函数
import { ArticleCategoryListService,ArticleCategoryAddService,ArticleCategoryUpdateService,ArticleCategoryDeleteService } from '@/api/article.js';
const articleCategoryList = async ()=>{let result = await ArticleCategoryListService();categorys.value = result.data;
}
articleCategoryList();
//控制添加分类弹窗
const dialogVisible = ref(false)//添加分类数据模型
const categoryModel = ref({categoryName: '',categoryAlias: ''
})
//添加分类表单校验
const rules = {categoryName: [{ required: true, message: '请输入分类名称', trigger: 'blur' },],categoryAlias: [{ required: true, message: '请输入分类别名', trigger: 'blur' },]
}
//添加表单
import { ElMessage } from 'element-plus';
const addCategory = async ()=>{//调用接口let result = await ArticleCategoryAddService(categoryModel.value);ElMessage.success(result.message ? result.message : '添加成功');//获取文章分类的函数articleCategoryList();//让添加分类弹窗消失dialogVisible = false;//添加后值消失categoryModel = ref({categoryName: '',categoryAlias: ''})
}
//定义变量来控制标题
const title = ref('');
//展示编辑弹窗
const showDialog = (row)=>{dialogVisible.value = true;title.value ='编辑分类';//数据拷贝categoryModel.value.categoryName = row.categoryName;categoryModel.value.categoryAlias = row.categoryAlias;//扩展id属性,将来需要传递给后台完成分类的修改categoryModel.value.id = row.id;
}
//编辑分类
const updateCategory = ()=>{let result  = ArticleCategoryUpdateService(categoryModel.value);ElMessage.success(result.message ? result.message : '修改成功');articleCategoryList();dialogVisible.value = false;
}
//清空
const clearData = ()=>{categoryModel.value.categoryName = '';categoryModel.value.categoryAlias = '';
}
//删除分类
import { ElMessageBox } from 'element-plus'
const deleteCategory = (row)=>{//提示用户ElMessageBox.confirm('确认要删除该分类信息吗?','温馨提示',{confirmButtonText: '确认',cancelButtonText: '取消',type: 'warning',center: true,}).then(async () => {let result = await ArticleCategoryDeleteService(row.id); ElMessage({type: 'success',message: '删除成功',})articleCategoryList();}).catch(() => {ElMessage({type: 'info',message: '取消删除',})})
}
</script>
<template><el-card class="page-container"><template #header><div class="header"><span>文章分类</span><div class="extra"><el-button type="primary" @click="dialogVisible = true;title = '添加分类';clearData()">添加分类</el-button></div></div></template><el-table :data="categorys" style="width: 100%"><el-table-column label="序号" width="100" type="index"> </el-table-column><el-table-column label="分类名称" prop="categoryName"></el-table-column><el-table-column label="分类别名" prop="categoryAlias"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" @click="showDialog(row)" ></el-button><el-button :icon="Delete" circle plain type="danger" @click="deleteCategory(row)"></el-button></template></el-table-column><template #empty><el-empty description="没有数据" /></template></el-table><!-- 添加分类弹窗 --><el-dialog v-model="dialogVisible" :title="title" width="30%"><el-form :model="categoryModel" :rules="rules" label-width="100px" style="padding-right: 30px"><el-form-item label="分类名称" prop="categoryName"><el-input v-model="categoryModel.categoryName" minlength="1" maxlength="10"></el-input></el-form-item><el-form-item label="分类别名" prop="categoryAlias"><el-input v-model="categoryModel.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="title ==='添加分类' ? addCategory() : updateCategory()"> 确认 </el-button></span></template></el-dialog></el-card>
</template><style lang="scss" scoped>
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
</style>

在article.js完整代码:

import request from '@/utils/request.js'
// import { useTokenStore } from '@/stores/token.js';export const ArticleCategoryListService = ()=>{//在请求拦截器添加了JWT令牌到请求头中return request.get('/category');
} export const ArticleCategoryAddService = (categoryData)=>{return request.post('/category',categoryData);
}//文章分类编辑
export const ArticleCategoryUpdateService = (categoryData)=>{return request.put('/category',categoryData);
}//文章分类删除
export const ArticleCategoryDeleteService = (id)=>{return request.delete('/category?id='+id)
}

好了,今天的内容就到这里了,下期继续更新大事件项目前后端开发,感谢收看!!!

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

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

相关文章

优化if-else的11种方案

优雅永不过时&#xff01; 1. 使用早返回&#xff08;Early Return&#xff09;&#xff1a;尽可能早地返回&#xff0c;避免嵌套的if-else。 优化前&#xff1a; public class NoEarlyReturnExample {public boolean hasPositiveNumber(int[] numbers) {boolean foundPositi…

学习大数据DAY31 Python基础语法4和基于Python中的MySQL 编程

目录 Python 库 模块 time&datetime 库 连接 MySQL 操作 结构操作 数据增删改操作 数据查询操作 上机练习 7 面向对象 OOP 封装 继承 三层架构---面向对象思想模型层 数据层 业务逻辑显示层 上机练习 8 三层架构开发豆瓣网 关于我对 AI 写代码的看法&#xf…

客户数据分析模型:RFM模型的深度解析与应用探索

RFM模型&#xff0c;作为客户数据分析中的经典工具&#xff0c;凭借其简单而强大的分析能力&#xff0c;被广泛应用于各行各业。本文旨在深入探讨RFM模型的核心原理、应用价值&#xff0c;并详细阐述其在2C&#xff08;面向消费者&#xff09;和2B&#xff08;面向企业&#xf…

使用ThreadLocal来存取单线程内的数据

一.什么是ThreadLocal&#xff1f; ThreadLocal&#xff0c;即线程本地变量。如果你创建了一个 ThreadLocal变量&#xff0c;那么访问这个变量的每个线程都会有这个变量的一个本地拷贝&#xff0c;多个线程操作这个变量的时候&#xff0c;实际是在操作自己本地内存里面的变量&…

自注意力和位置编码

一、自注意力 1、给定一个由词元组成的输入序列x1,…,xn&#xff0c; 其中任意xi∈R^d&#xff08;1≤i≤n&#xff09;。 该序列的自注意力输出为一个长度相同的序列 y1,…,yn&#xff0c;其中&#xff1a; 2、自注意力池化层将xi当作key&#xff0c;value&#xff0c;query来…

Ubuntu配置carla docker环境

前言: 本文只在以下设备成功运行, 其他设备不保证能成功, 可以参考在自己设备进行配置 环境 ubuntu 20.04carla 0.9.15gpu 3060(notebook) 安装显卡驱动&nvidia-container-toolkit 显卡驱动 安装完成系统后直接在’软件和更新->附加驱动’直接选择470(proprietary…

Leetcode3227. 字符串元音游戏

Every day a Leetcode 题目来源&#xff1a;3227. 字符串元音游戏 解法1&#xff1a;博弈论 分类讨论&#xff1a; 如果 s 不包含任何元音&#xff0c;小红输。如果 s 包含奇数个元音&#xff0c;小红可以直接把整个 s 移除&#xff0c;小红赢。如果 s 包含正偶数个元音&am…

R 语言学习教程,从入门到精通,R 基础运算(5)

1、R 基础运算 本章介绍 R 语言的简单运算。 1.1、赋值 一般语言的赋值是 号&#xff0c;但是 R 语言是数学语言&#xff0c;所以赋值符号与我们数学书上的伪代码很相似&#xff0c;是一个左箭头 <- &#xff1a; a <- 123 b <- 456 print(a b)以上代码执行结果…

最好用的掏耳勺是哪种?年度五款可视挖耳勺高分机型

在我们的日常生活中&#xff0c;掏耳朵似乎是一件再平常不过的小事&#xff0c;不过&#xff0c;传统挖耳勺在使用时完全依赖我们的感觉和经验&#xff0c;我们无法直接看到耳道内部的情况。这就如同在黑暗中摸索&#xff0c;极易造成意外伤害。稍有不慎&#xff0c;就可能刮伤…

使用gitea私有仓库作为依赖

实际问题 由于公司团队使用gitea搭建了git私有仓库&#xff0c;在开发Go程序的时候会有一些公共代码&#xff0c;比如插件和主程序之间要共享接口和数据结构&#xff0c;所以就需要在gitea私有仓库中创建依赖仓库&#xff0c;然后其他仓库引用这个私有仓库作为依赖。 解决方案…

如何实现pxe安装部署

此实验环境&#xff1a;rhel7主机 一、kickstart自动化安装脚本 1、安装可视化图形 [rootlocalhost ~]# yum group install "Server with GUI" 2、关闭vmware dhcp功能&#xff08;编辑-虚拟网络编辑器&#xff09; 3、httpd 1、安装httpd服务 [rootlocalhost …

网鼎杯比赛二次注入技巧

文章目录 前端的网页展示分析题目暴力破解寻找代码找到注入点进行注入查询想要的文件 前端的网页展示 分析题目 1.目前我们能看到的只有三个页面&#xff0c;但是我们可以看到三个*号。 2.考虑三个*的密码是什么&#xff0c;这里可以采用暴力破解&#xff08;我们先猜这是三个…

会声会影下载免费吗?会声会影2023中文旗舰版下载及配置最低要求

**会声会影2024&#xff1a;引领视频创作新时代的创新之旅** 在数字时代的浪潮中&#xff0c;视频创作已成为连接世界、表达创意的重要方式。随着技术的不断进步&#xff0c;一款名为“会声会影2024”的视频编辑软件横空出世&#xff0c;它不仅继承了前代产品的优秀传统&#…

C#MQTT协议应用

1 &#xff0c;MQTT介绍&#xff1a;MQTT详解以及实际操作_mqtt使用-CSDN博客 2&#xff0c;MQTT应用&#xff1a; C#MQTT编程06--MQTT服务器和客户端(winform版)_c# mqtt服务器-CSDN博客 3&#xff0c;MQTT实例&#xff1a; 效果 代码&#xff1a; 服务端 public parti…

通过Stack Overflow线程栈溢出的问题实例,详解C++程序线程栈溢出的诸多细节

目录 1、问题说明 2、从Visual Studio输出窗口中找到了线索,发生了Stack Overflow线程栈溢出的异常 3、发生Stack Overflow线程栈溢出的原因分析 4、线程占用的栈空间大小说明 5、引发线程栈溢出的常见原因和场景总结 6、在问题函数入口处添加return语句,在Debug下运行…

TI音频功放TAS6511(二)

3.数字音频处理 芯片支持高级数字音频处理能力&#xff0c;包括&#xff1a; 高通滤波器/直流阻断 数字音量控制 PVDD的Foldback/AGL 热Foldback 双象限增益补偿 混合调制 实时负载诊断 低延迟路径 喇叭功率限制 1&#xff09;PVDD Foldback 本功能主要为了防止音频…

【Material-UI】Autocomplete中的禁用选项:Disabled options

文章目录 一、简介二、基本用法三、进阶用法1. 动态禁用2. 提示禁用原因3. 复杂的禁用条件 四、最佳实践1. 一致性2. 提供反馈3. 优化性能 五、总结 Material-UI的Autocomplete组件提供了丰富的功能&#xff0c;包括禁用特定选项的能力。这一特性对于限制用户选择、提供更好的用…

【你也能从零基础学会网站开发】SQL Server 2000中的数据类型之String字符串类型

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;程序猿、设计师、技术分享 &#x1f40b; 希望大家多多支持, 我们一起学习和进步&#xff01; &#x1f3c5; 欢迎评论 ❤️点赞&#x1f4ac;评论 &#x1f4c2;收藏 &#x1f4c2;加关注 SQL Server 中…

不用PS也能抠图?点哪抠哪,简直是职场人的最强助手

抠图你还在用 PS 一点点抠吗&#xff1f; 不仅费时费力&#xff0c;还常常达不到理想效果&#xff0c;真的太让人崩溃了 但别担心&#xff0c;我找到了一个超棒的工具——千鹿设计助手的AI智能抠图插件。它就像你的私人设计小助手&#xff0c;能快速帮你把想要的元素抠出来&…

《花100块做个摸鱼小网站! · 序》灵感来源

序 大家好呀&#xff0c;我是summo&#xff0c;这次来写写我在上班空闲(摸鱼)的时候做的一个小网站的事。去年阿里云不是推出了个活动嘛&#xff0c;2核2G的云服务器一年只要99块钱&#xff0c;懂行的人应该知道这个价格在业界已经是非常良心了&#xff0c;虽然优惠只有一年&a…