JAVAWeb实战(前端篇)

项目实战一

0.项目结构

1.创建vue3项目,并导入所需的依赖

npm install vue-router
npm install axios
npm install pinia
npm install vue

2.定义路由,axios,pinia相关的对象 文件(.js)

2.1路由(.js)

import {createRouter,createWebHashHistory} from 'vue-router'import Login from '../components/Login.vue'
import Regist from '../components/Regist.vue'
import ShowSchedule from '../components/ShowSchedule.vue'import pinia from '../pinia.js'
import {defineUser} from '../store/userStore.js'let sysUser = defineUser(pinia)
const router= createRouter({history:createWebHashHistory(),routes:[{path:"/",redirect:"/showSchedule"},{path:"/showSchedule",component:ShowSchedule},{path:"/login",component:Login},{path:"/regist",component:Regist}]
})// 路由 的全局前置守卫 判断是否可以访问showSchedule
router.beforeEach((to,from,next)=>{if(to.path == '/showSchedule'){// 登陆过放行// 没登录 回到登录页if(sysUser.username == ''){next("/login")}else{next()}}else{next()}})export default router 

2.2axios(.js)

import axios from 'axios'//  创建instance实例
const instance = axios.create({baseURL:'http://localhost:8080/'})//  添加请求拦截
instance.interceptors.request.use(// 设置请求头配置信息config=>{//处理指定的请求头return config},// 设置请求错误处理函数error=>{return Promise.reject(error)}
)
// 添加响应拦截器
instance.interceptors.response.use(// 设置响应正确时的处理函数response=>{return response},// 设置响应异常时的处理函数error=>{return Promise.reject(error)}
)
// 默认导出
export default instance

2.3pinia

2.3.1开启pinia(.js)
// 开启pinia
import {createPinia} from 'pinia'
let pinia = createPinia()export default pinia
2.3.2根据数据库定义接收的pinia文件(.js)

数据库

schedule表

对应的pinia文件

这里用集合来接收所有数据

/* 
专门用于存储日程状态信息的pinia*/
import {defineStore} from 'pinia'export const defindSchedule= defineStore("scheduleList",{state:()=>{return {itemList:[]}},getters:{},actions:{}}

user表

对应的pinia文件

/* 
专门用于存储用户状态信息的pinia*/
import {defineStore} from 'pinia'export const defineUser= defineStore("loginUser",{state:()=>{return {uid:0,username:'' }},getters:{},actions:{}}
)

将他们挂载在main.js文件中

import { createApp } from 'vue'import App from './App.vue'import router from './router/router'import pinia from './pinia.js'const app =createApp(App)
app.use(router)
app.use(pinia)
app.mount('#app')

3.定义组件

3.1定义头组件

Header.vue

<script setup>/* 导入pinia数据 */import {defineUser} from "../store/userStore.js"import {defindSchedule} from "../store/scheduleStore.js"let sysUser = defineUser()let schedule = defindSchedule()import {useRouter} from 'vue-router'let router = useRouter()function logout(){// 清除所有pinia数据sysUser.$reset()schedule.$reset()// 跳转到登录页router.push("/login")}</script><template><div><h1 class="ht">欢迎使用日程管理系统</h1><div><div  class="optionDiv" v-if="sysUser.username == ''"><router-link to="/login"><button class="b1s">登录</button></router-link>   <router-link to="/regist"><button class="b1s">注册</button></router-link></div><div   class="optionDiv" v-else>欢迎 {{ sysUser.username }}   <button class="b1b" @click="logout()">退出登录</button> <router-link to="/showSchedule"><button class="b1b">查看我的日程</button></router-link></div><br></div></div>
</template><style scoped>.ht{text-align: center;color: cadetblue;font-family: 幼圆;}.b1s{border: 2px solid powderblue;border-radius: 4px;width:60px;background-color: antiquewhite;}.b1b{border: 2px solid powderblue;border-radius: 4px;width:100px;background-color: antiquewhite;}.optionDiv{width: 400px;float: right;}
</style>

3.2定义登入组件  Login.vue

<script setup>import {ref,reactive} from 'vue'import {useRouter} from 'vue-router'const router = useRouter()import{ defineUser}  from '../store/userStore.js'let sysUser = defineUser()import request  from '../utils/request'let loginUser =reactive({username:"",userPwd:""})let usernameMsg =ref("")let userPwdMsg =ref("")function checkUsername(){let usernameReg= /^[a-zA-Z0-9]{5,10}$/if(!usernameReg.test(loginUser.username)){usernameMsg.value="格式有误"return false}usernameMsg.value="OK"return true}function checkUserPwd(){let userPwdReg = /^[0-9]{6}$/if(!userPwdReg.test(loginUser.userPwd)){userPwdMsg.value="格式有误"return false}userPwdMsg.value="OK"return true}async function login(){// 表单数据格式都正确再提交let flag1 =checkUsername()let flag2 =checkUserPwd()if(!(flag1 && flag2)){return }let {data} = await request.post("user/login",loginUser) //发送请求if(data.code == 200 ){alert("登录成功")console.log(data)// 获得登录的用户信息,更新到pinia中sysUser.uid =data.data.loginUser.uid    //pinia获取登入信息sysUser.username = data.data.loginUser.username// 跳转到showSchedulerouter.push("/showSchedule")} else if( data.code == 503){alert("密码有误")}else if (data.code == 501 ){alert("用户名有误")}else {alert("未知错误")}}</script><template><div><h3 class="ht">请登录</h3><table class="tab" cellspacing="0px"><tr class="ltr"><td>请输入账号</td><td><input class="ipt" type="text" v-model="loginUser.username"@blur="checkUsername()"><span id="usernameMsg" v-text="usernameMsg"></span></td></tr><tr class="ltr"><td>请输入密码</td><td><input class="ipt" type="password" v-model="loginUser.userPwd"@blur="checkUserPwd()"><span id="userPwdMsg" v-text="userPwdMsg"></span></td></tr><tr class="ltr"><td colspan="2" class="buttonContainer"><input class="btn1" type="button" @click="login()" value="登录"><input class="btn1" type="button" value="重置"><router-link to="/regist"><button class="btn1">去注册</button></router-link></td></tr></table></div>
</template><style scoped>.ht{text-align: center;color: cadetblue;font-family: 幼圆;}.tab{width: 500px;border: 5px solid cadetblue;margin: 0px auto;border-radius: 5px;font-family: 幼圆;}.ltr td{border: 1px solid  powderblue;}.ipt{border: 0px;width: 50%;}.btn1{border: 2px solid powderblue;border-radius: 4px;width:60px;background-color: antiquewhite;}#usernameMsg , #userPwdMsg {color: gold;}.buttonContainer{text-align: center;}
</style>

3.3定义注册组件 Regist.vue

<script setup>import {ref,reactive} from 'vue'/* 导入发送请求的axios对象 */import request from'../utils/request'import {useRouter} from 'vue-router'const router = useRouter()let registUser = reactive({username:"",userPwd:""})let usernameMsg=ref('')let userPwdMsg=ref('')let reUserPwdMsg=ref('')let reUserPwd=ref('')async function checkUsername(){let usernameReg= /^[a-zA-Z0-9]{5,10}$/if(!usernameReg.test(registUser.username)){usernameMsg.value="格式有误"return false}// 发送异步请求   继续校验用户名是否被占用let {data} = await request.post(`user/checkUsernameUsed?username=${registUser.username}`)if(data.code != 200){usernameMsg.value="用户名占用"return false}usernameMsg.value="可用"return true}function checkUserPwd(){let userPwdReg = /^[0-9]{6}$/if(!userPwdReg.test(registUser.userPwd)){userPwdMsg.value="格式有误"return false}userPwdMsg.value="OK"return true}function checkReUserPwd(){let userPwdReg = /^[0-9]{6}$/if(!userPwdReg.test(reUserPwd.value)){reUserPwdMsg.value="格式有误"return false}if(registUser.userPwd != reUserPwd.value){reUserPwdMsg.value="两次密码不一致"return false}reUserPwdMsg.value="OK"return true}// 注册的方法async function regist(){// 校验所有的输入框是否合法let flag1 =await checkUsername()let flag2 =await checkUserPwd()let flag3 =await checkReUserPwd()if(flag1 && flag2 && flag3){let  {data}= await request.post("user/regist",registUser)//  let  {data}=  request.post("user/regist",registUser)if(data.code == 200){// 注册成功跳转 登录页alert("注册成功,快去登录吧")router.push("/login")}else{alert("抱歉,用户名被抢注了")}}else{alert("校验不通过,请求再次检查数据")}}function clearForm(){registUser.username=""registUser.userPwd=""usernameMsg.value=""userPwdMsg.value=""reUserPwd.value=""reUserPwdMsg.value=""}</script><template><div><h3 class="ht">请注册</h3><table class="tab" cellspacing="0px"><tr class="ltr"><td>请输入账号</td><td><input class="ipt" id="usernameInput" type="text" name="username" v-model="registUser.username"@blur="checkUsername()"><span id="usernameMsg" class="msg" v-text="usernameMsg"></span></td></tr><tr class="ltr"><td>请输入密码</td><td><input class="ipt" id="userPwdInput" type="password" name="userPwd" v-model="registUser.userPwd"@blur="checkUserPwd()"><span id="userPwdMsg" class="msg" v-text="userPwdMsg"></span></td></tr><tr class="ltr"><td>确认密码</td><td><input class="ipt" id="reUserPwdInput" type="password" v-model="reUserPwd"@blur="checkReUserPwd()"><span id="reUserPwdMsg" class="msg" v-text="reUserPwdMsg"></span></td></tr><tr class="ltr"><td colspan="2" class="buttonContainer"><input class="btn1" type="button" @click="regist()" value="注册"><input class="btn1" type="button" @click="clearForm()" value="重置"><router-link to="/login"><button class="btn1">去登录</button></router-link></td></tr></table></div>
</template>
<style scoped>.ht{text-align: center;color: cadetblue;font-family: 幼圆;}.tab{width: 500px;border: 5px solid cadetblue;margin: 0px auto;border-radius: 5px;font-family: 幼圆;}.ltr td{border: 1px solid  powderblue;}.ipt{border: 0px;width: 50%;}.btn1{border: 2px solid powderblue;border-radius: 4px;width:60px;background-color: antiquewhite;}.msg {color: gold;}.buttonContainer{text-align: center;}
</style>

3.4定义主体显示内容组件ShowSchedule.vue

<script setup>/* 导入pinia数据 */import {defineUser} from '../store/userStore.js'import {defindSchedule} from '../store/scheduleStore.js'let sysUser = defineUser()let schedule = defindSchedule()import {ref,reactive,onUpdated,onMounted} from 'vue'import request from '../utils/request'//挂载完毕后,立刻查询当前用户的所有日程信息,赋值给piniaonMounted(async ()=>{showSchedule()})// 查询当前用户所有日程信息 并展示到视图的方法async function showSchedule(){// 发送异步请求,获得当前用户的所有日程记录let {data} = await request.get("schedule/findAllSchedule",{params:{"uid":sysUser.uid}})schedule.itemList = data.data.itemList}// 为当前用户增加一个空的日程记录async function addItem(){let{data} = await request.get('schedule/addDefaultSchedule',{params:{"uid":sysUser.uid}})  if(data.code == 200){// 增加成功,刷新页面数据showSchedule()}else{alert("增加失败")}}async function updateItem(index){// 找到要修改的数据 发送给服务端,更新进入数据库即可let {data} =  await request.post("schedule/updateSchedule",schedule.itemList[index])   if(data.code == 200){showSchedule()alert("更新成功")}else{alert("更新失败")}}async function removeItem(index){let sid =schedule.itemList[index].sidlet {data} = await request.get(`schedule/removeSchedule`,{params:{"sid":sid}})if(data.code == 200){showSchedule()alert("删除成功")}else{alert("删除失败")}}</script><template><div><h3 class="ht">您的日程如下</h3>
<table class="tab" cellspacing="0px"><tr class="ltr"><th>编号</th><th>内容</th><th>进度</th><th>操作</th></tr><tr class="ltr" v-for="item,index in schedule.itemList" :key="index"><td v-text = "index+1"></td><td ><input type="text" v-model="item.title"></td><td ><input type="radio"  value="1" v-model="item.completed"> 已完成<input type="radio"  value="0" v-model="item.completed"> 未完成</td><td class="buttonContainer"><button class="btn1" @click="removeItem(index)">删除</button><button class="btn1" @click="updateItem(index)">保存修改</button></td></tr><tr class="ltr buttonContainer" ><td colspan="4"><button class="btn1" @click="addItem()">新增日程</button></td></tr>
</table></div>
</template><style scoped>.ht{text-align: center;color: cadetblue;font-family: 幼圆;}.tab{width: 80%;border: 5px solid cadetblue;margin: 0px auto;border-radius: 5px;font-family: 幼圆;}.ltr td{border: 1px solid  powderblue;}.ipt{border: 0px;width: 50%;}.btn1{border: 2px solid powderblue;border-radius: 4px;width:100px;background-color: antiquewhite;}#usernameMsg , #userPwdMsg {color: gold;}.buttonContainer{text-align: center;}</style>

3.5放入主组件 APP.vue

<script setup>import Header from './components/Header.vue'
</script><template><div><Header></Header><hr><router-view></router-view></div></template><style scoped>
</style>

项目实战一结束

项目实战二

项目结构

1.创建项目,导入所需依赖

这里照着dependencies里面的依赖下载就行

{"name": "my-vue3-vite-project","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite","build": "vite build","preview": "vite preview"},"dependencies": {"axios": "^1.4.0","element-plus": "^2.2.5","less": "^4.1.3","less-loader": "^7.3.0","mitt": "^3.0.0","nprogress": "^0.2.0","pinia": "^2.0.14","vue": "^3.2.47","vue-router": "^4.1.6"},"devDependencies": {"@vitejs/plugin-vue": "^4.1.0","typescript": "^4.7.2","vite": "^4.3.2"}
}

2.定义依赖对象相关文件(.js)

2.1路由

index.js

import { createRouter, createWebHistory } from "vue-router";
import { staticRoutes } from "./routes";
import { useUserInfoStore } from '../stores/userInfo';
import pinia from '../stores';
import { getToken, removeToken } from '../utils/token-utils';
import { ElMessage } from 'element-plus';const router = createRouter({history: createWebHistory(),routes: staticRoutes,
});const userInfoStore = useUserInfoStore(pinia)//全局前置守卫
router.beforeEach(async (to, from, next) => {const token = getToken()const userInfo = !!userInfoStore.nickNameif (token) {if (to.path == "/login") {next({ path: "/" })} else {if (userInfo) {next()} else {try {await userInfoStore.getInfo()next()} catch (error) {removeToken()}}}} else {next()}
});// //使用全局后置钩子配置关闭进度条
// router.afterEach(() => {
//   NProgress.done();
// });// 导出路由
export default router;

router.js


export const staticRoutes = [{path: "/",redirect: "/headlinenews",},{// 头条path: "/headlinenews",component: () => import("../pages/HeadlineNews/index.vue"),name: "HeadlineNews",},{//头条详情path: "/detail",component: () => import("../pages/Detail/index.vue"),name: "Detail",},{// 登录path: "/login",component: () => import("../pages/Login/index.vue"),name: "Login",},{//注册path: "/register",component: () => import("../pages/Register/index.vue"),name: "Register",},{//发布新闻的页面path: "/addormodifynews",component: () => import("../pages/addOrModifyNews/index.vue"),name: "addOrModifyNews",},
];

2.2axios

import axios from "axios";
import { ElMessage } from 'element-plus';
import pinia from '../stores/index';
import { useUserInfoStore } from '../stores/userInfo';
import NProgress from "nprogress";
import "nprogress/nprogress.css";
// 配置新建一个 axios 实例
const service = axios.create({baseURL: "/app-dev/",timeout: 50000,
});// 添加请求拦截器
service.interceptors.request.use((config) => {NProgress.start()//开启进度条// 如果有token, 通过请求头携带给后台const userInfoStore = useUserInfoStore(pinia) // 如果不是在组件中调用,必须传入piniaconst token = userInfoStore.tokenif (token) {// config.headers['token'] = token  // 报错: headers对象并没有声明有token, 不能随便添加(config.headers)['token'] = token}return config;
});// 添加响应拦截器
service.interceptors.response.use((response) => {NProgress.done()//关闭进度条if(response.data.code !== 200){// 判断响应状态码if (response.data.code == 501)  return  Promise.reject(ElMessage.error("用户名有误"))else if (response.data.code == 503) return  Promise.reject(ElMessage.error("密码有误"))else if (response.data.code == 504) return  Promise.reject(ElMessage.error("登录已过期"))else if (response.data.code == 505) return  Promise.reject(ElMessage.error("用户名占用"))} else {return response.data.data; /* 返回成功响应数据中的data属性数据 */}},(error) => {NProgress.done()//关闭进度条return Promise.reject(error.message);}
);export default service;

2.3token

const TokenKey = 'vue_admin_template_token'export function getToken() {return localStorage.getItem(TokenKey)
}export function setToken(token: string) {localStorage.setItem(TokenKey, token)
}export function removeToken() {localStorage.removeItem(TokenKey)
}

2.4pinia

import { createPinia } from 'pinia';const pinia = createPinia();export default pinia;
2.4.1pinia存储

import { defineStore } from 'pinia';
import { getToken, removeToken, setToken } from '../utils/token-utils';
import { getLogin,getUserInfo } from '../api/index';/*** 用户信息* @methods setUserInfos 设置用户信息*/
export const useUserInfoStore = defineStore('userInfo', {state: () => ({token: getToken(),nickName: '',uid: '',}),actions: {// 登陆的异步actionasync login (loginForm) {// 发送登陆的请求const result = await getLogin(loginForm)// 请求成功后, 取出token保存  pinia和local中const token = result.tokenthis.token = tokensetToken(token)},async getInfo () {const result = await getUserInfo()this.nickName = result.loginUser.nickNamethis.uid = result.loginUser.uid},initUserInfo(){removeToken()this.nickName = ""this.uid = ""console.log('1111111111');}},});

2.5api

import request from "../utils/request/"// portal/findAllTypes
//获取分类列表
export const getfindAllTypes = () => {return request.get("portal/findAllTypes");
};
// 分页带条件查询所有头条
export const getfindNewsPageInfo = (info) => {return request.post("portal/findNewsPage",info);
};
// 查看头条详情
export const getshowHeadlineDetail = (id) => {return request({method: "post",url: "portal/showHeadlineDetail",headers: {"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",},data:`hid=${id}`});
};//删除的回调
// headline/removeByHid
export const removeByHid = (id) => {return request({method: "post",url: "headline/removeByHid",headers: {"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",},data:`hid=${id}`})
};//登录的接口
export const getLogin = (info) => {return request.post("user/login",info);
};
//获取用户信息的接口
export const getUserInfo = (info) => {return request.get("user/getUserInfo");
};//注册校验的接口  user/checkUserName
export const registerValidateApi = (username) => {return request({method: "post",url: "user/checkUserName",headers: {"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",},data:`username=${username}`})
};// 注册的接口
export const registerApi = (userInfo) => {return request.post("user/regist",userInfo)
}
//判断用户登录过期的接口
export const isUserOverdue = () => {return request.get("user/checkLogin")
}// 修改头条回显的接口
export const getFindHeadlineByHid = (id) => {return request({method: "post",url: "headline/findHeadlineByHid",headers: {"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",},data:`hid=${id}`});
};//点击保存修改的回调
// headline/update
export const saveOrAddNews = (news) => {return request.post("headline/update",news)
}// headline/publish
export const issueNews = (news) => {return request.post("headline/publish",news)
}

3组件

3.1头组件Header.vue

<template><div class="headerContainer"><!-- 头部左侧区域 --><div class="left"><ul><li @click="HighlightHandler(index,)"  v-for="(item,index) in findAllTypeList" :key="item.tid"><a :class="{ active: item.isHighlight }" href="javascript:;">{{item.tname}}</a></li></ul></div><!-- 头部右侧区域 --><div class="right"><div class="rightInput" style="margin-right: 50px;"><el-input v-model="keywords" placeholder="搜索最新头条"></el-input><!-- <el-button   type="primary">搜索</el-button> --></div><!-- 用户登录以后的展示 --><div class="btn-dropdown"><!-- 用户没有登录的时候的展示 --><div v-if="nickName" style="display: flex; justify-content: center; align-items: center;"><el-dropdown><el-button type="primary">您好:{{ nickName }}<el-icon class="el-icon--right"><arrow-down /></el-icon></el-button><template #dropdown><el-dropdown-menu><el-dropdown-item @click="handlerNews">发布新闻</el-dropdown-item><el-dropdown-item>个人中心</el-dropdown-item><el-dropdown-item>浏览记录</el-dropdown-item><el-dropdown-item @click="Logout">退出登录</el-dropdown-item></el-dropdown-menu></template></el-dropdown></div><div v-else class="containerButton"><el-button size="small" style="background: #212529; color: #aea7a2" @click="toLogin">登录</el-button><el-button size="small" style="background: #ffc107; color: #684802" @click="toRegister">注册</el-button></div></div></div></div>
</template><script>
import { defineComponent } from 'vue'
export default defineComponent({name: 'Header'
})
</script><script setup>
import { getfindAllTypes, isUserOverdue } from '../api/index'
import { ref, onMounted , getCurrentInstance ,watch, onUpdated} from "vue"
import { useRouter } from 'vue-router'
import { ArrowDown } from '@element-plus/icons-vue'
import { removeToken } from '../utils/token-utils' 
import pinia from '../stores/index';
import { useUserInfoStore } from '../stores/userInfo'
const userInfoStore = useUserInfoStore(pinia)
const nickName = ref("")
// 获取到 全局事件总线
const { Bus } = getCurrentInstance().appContext.config.globalProperties
const router = useRouter()
const keywords = ref("") // 收集搜索最新头条参数
//监视搜索参数的变化 ,当搜索参数变化的时候给HeadlineNews组件传递数据
watch(keywords, (newVal) => {Bus.emit('keyword', newVal)
})
const findAllTypeList = ref([])//所有头条分类
const toLogin = () => {
router.push({ name: "Login" });
}
//点击去注册页面
const toRegister = () => {router.push({ name: "Register" });
}
const getList = async () => {let result = await getfindAllTypes()// 遍历数据添加高亮标识result.forEach((item) => {item.tid = item.tiditem.tname = item.tnameitem.isHighlight = false})// 添加微头条数据result.unshift({isHighlight: true,tid: 0,tname: "微头条"})findAllTypeList.value = result
}
// 页面挂载的生命周期回调
onUpdated(() => {nickName.value = userInfoStore.nickName
})
onMounted(() => {getList()
})//点击切换高亮的回调(排他思想)
const HighlightHandler = (index) => {findAllTypeList.value.forEach((item) => {item.isHighlight = false})// 切换高亮的时候把tid传给HeadlineNews组件findAllTypeList.value[index].isHighlight = trueBus.emit('tid', findAllTypeList.value[index].tid)
}// 点击退出登录的回调
const Logout = () => {removeToken()userInfoStore.initUserInfo()nickName.value = ""router.go({ name: "HeadlineNews" });
}//点击发布新闻的回调
const handlerNews = async () => {//发送请求判断用户是否token过期await isUserOverdue()router.push({ name: "addOrModifyNews" });
}
</script><style>
.el-dropdown {vertical-align: top;width: 100px;
}.el-dropdown+.el-dropdown {margin-left: 15px;
}.el-icon-arrow-down {font-size: 12px;
}
</style><style lang="less" scoped>
.headerContainer {width: 100%;height: 60px;background: #212529;display: flex;justify-content: space-around;.left {ul {display: flex;li {list-style: none;margin-left: 20px;a:-webkit-any-link {text-decoration: none;color: #59646b;&.active {color: #c0adab;}}}}}.right {.containerButton {display: flex;align-items: center;}line-height: 60px;display: flex;flex-wrap: nowrap;.rightInput {display: flex;align-items: center;:deep(.el-input__inner) {height: 30px;width: 150px;}}.btn-dropdown{display: flex;align-items: center;}:deep(.el-button) {margin: 0 0 0 10px;display: flex;justify-content: center;align-items: center;}}
}.example-showcase .el-dropdown + .el-dropdown {margin-left: 15px;
}
.example-showcase .el-dropdown-link {cursor: pointer;color: var(--el-color-primary);display: flex;align-items: center;
}
</style>

3.2安装上面顺序第一个

<template><el-card class="box-card AddNewsContainer"><el-form :rules="newsRules" :model="formData" ref="formRef" size="default"><el-form-item label="文章标题" prop="title"><el-input v-model="formData.title" placeholder="请输入标题"></el-input></el-form-item><el-form-item style="margin: 50px 0;" label="文章内容" prop="article"><el-input v-model="formData.article" type="textarea" rows="8"></el-input></el-form-item><el-form-item label="文章内容"  prop="type"><el-select v-model="formData.type" placeholder="请选择文章类别"><el-option v-for="item in article" :label="item.name" :value="item.type"></el-option></el-select></el-form-item></el-form><el-form-item><el-button   @click="handlerCancel">取消</el-button><el-button type="primary"  @click="handlerSave">保存</el-button></el-form-item></el-card>
</template><script>
import { defineComponent } from 'vue'
import { isUserOverdue } from '../../api/index'
export default defineComponent({name: 'AddNews'
})
</script>
<script  setup>
import { getFindHeadlineByHid , saveOrAddNews, issueNews } from "../../api/index"
import { ref, onMounted } from "vue"
import { useRoute } from 'vue-router'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
const router = useRouter() 
const route = useRoute() const formRef = ref()
// 校验规则
const validateType = (rule, value, callback) => {if (value.length) {callback()} else {callback(new Error('文章标题是必须的'))}
}
// 校验规则
const validateArticle = (rule, value, callback) => {if (value.length) {callback()} else {callback(new Error('文章内容是必须的'))}
}
// 校验规则
const validateTitle = (rule, value, callback) => {if (value.length) {callback()} else {callback(new Error('文章类别是必须的'))
}
}
// 校验规则
const newsRules = {title: [{ required: true, trigger: 'blur', validator: validateTitle }],article: [{ required: true, trigger: 'blur', validator: validateArticle }],type: [{ required: true, validator: validateType }],
}const formData = ref({hid:null,title: "",   // 文章标题article: "", // 文章内容type: ""     // 文章类别
})
//初始化文章类别数据
const article = [{type: "1",name: "新闻"},{type: "2",name: "体育"},{type: "3",name: "娱乐"},{type: "4",name: "科技"},{type: "5",name: "其他"}
]
// 如果是点击修改的话 路由就会携带hid参数  就要发送请求 获取数据回显
const clickModifyEcho = async () => {if (!route.query.hid)  returnlet result = await getFindHeadlineByHid(route.query.hid)formData.value.title = result.headline.titleformData.value.article = result.headline.articleformData.value.type = result.headline.type === 1 ? "新闻" : result.headline.type === 2 ? "体育" : result.headline.type === 3 ? "娱乐" : result.headline.type === 4 ? "科技" : "其他" 
}
//页面挂载生命周期
onMounted(() => {clickModifyEcho()
})
//点击取消的回调
const handlerCancel = () => {router.back()
}
//点击保存的回调
const handlerSave = async () => {await formRef.value?.validate()//发送请求判断用户是否token过期await isUserOverdue()
const Obj = {...formData.value}//整理请求参数
//  Obj.hid = userInfoStore.uid.toString()  //添加用户id 让后端知道谁添加的Obj.hid = route.query.hid  //添加用户id 让后端知道谁添加的
// 判断type类型if(Obj.type == "新闻" ) Obj.type = "1"if(Obj.type == "体育" ) Obj.type = "2"if(Obj.type == "娱乐" ) Obj.type = "3"if(Obj.type == "科技" ) Obj.type = "4"if (Obj.type == "其他") Obj.type = "5"//发送请求if (route.query.hid) {await saveOrAddNews(Obj)ElMessage.success("修改成功")}else {await issueNews(formData.value)ElMessage.success("添加成功")}router.push({ name: "HeadlineNews" });
}</script><style lang="less" scoped>
.AddNewsContainer {width: 600px;margin: 150px auto;
}
</style>

3.3第二个

<template><div class="seeDetails"><div><h4>{{ detailList.title }}</h4></div><div style="margin-right: 250px"><span>{{ detailList.typeName }}</span><span>{{ detailList.pageViews }}浏览</span><span>{{ detailList.pastHours }}小时前</span></div><div style="width: 500px; margin: 20px 0px 0px 70px"><p>{{ detailList.article }}</p></div></div>
</template><script >import { defineComponent } from 'vue'export default  defineComponent({name:'Detail'})
</script>
<script  setup>
import { getshowHeadlineDetail } from "../../api/index"
import { ref , onMounted } from "vue"
import { useRoute } from 'vue-router'
const route = useRoute() // 路由信息对象const detailList = ref({}) //详情数据
//获取详情初始化数据
const getDetailList = async () => {let result = await getshowHeadlineDetail(route.query.hid)detailList.value = result.headline
}
// 页面初始化钩子
onMounted(() => {getDetailList()
})</script><style lang="less" scoped>
.seeDetails {width: 1200px;margin: 0 auto;display: flex;flex-direction: column;align-items: center;div {span {padding-right: 15px;font-size: 14px;color: #8d91aa;}p {font-size: 14px;color: #2b2e30;}}
}
</style>

3.4第三个

<template><div class="container"><div class="listItem"><!-- 每一项头条列表 --><div class="containerItem" v-for="item in pageData" :key="item.hid"><div><span class="text">{{ item.title }}</span></div><div class="detail"><span>{{ item.type == 1 ? "新闻":item.type == 2 ? "体育": item.type == 3 ? "娱乐": item.type == 4 ? "科技" : "其他" }}</span><span>{{item.pageViews}}浏览</span><span>{{item.pastHours}}小时前</span></div><div><el-button @click="toDetail(item.hid)" size="small"style="background: #198754; margin-left: 15px; color: #bbd3dc">查看全文</el-button><el-popconfirm v-if="item.publisher == type" @confirm="handlerDelete(item.hid)" :title="`您确定要删除${item.title}吗?`"><template #reference><el-button    size="small" style="background: #dc3545; color: #bbd3dc">删除</el-button></template></el-popconfirm><el-button @click="Modify(item.hid)" v-if="item.publisher == type"  size="small" style="background: #212529; color: #bbd3dc">修改</el-button></div></div><!-- 分页器 --><div style="margin-top: 20px"><el-pagination v-model:current-page="findNewsPageInfo.pageNum"v-model:page-size="findNewsPageInfo.pageSize" @size-change="getPageList"@current-change="getPageList":page-sizes="[5,7,10]" backgroundlayout="prev, pager, next , ->, sizes, total" :total="totalSize" /></div></div></div>
</template><script >
import { getfindNewsPageInfo , removeByHid } from "../../api/index"import { defineComponent } from 'vue'export default  defineComponent({name:'HeadlineNews'})
</script>
<script  setup>
import { ref, onMounted, getCurrentInstance, watch } from "vue"
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import pinia from '../../stores/index';
import { useUserInfoStore } from '../../stores/userInfo'
const  { Bus } = getCurrentInstance().appContext.config.globalProperties
const userInfoStore = useUserInfoStore(pinia)
const router = useRouter()
const type = userInfoStore.uid
const findNewsPageInfo = ref({keyWords: "", // 搜索标题关键字type: 0,           // 新闻类型pageNum: 1,        // 页码数pageSize: 5,     // 页大小}
)
const totalSize = ref(0) //分页总数量
// 初始化列表数据
const pageData = ref([{hid: null,pageViews: null,pastHours: null,publisher: null,title: "",type: null
}])//接收header组件用户搜索的数据
Bus.on('keyword', (keywords) => {findNewsPageInfo.value.keyWords = keywords
})
// header点击切换高亮的时候传递过来的tid
Bus.on('tid', (type) => {findNewsPageInfo.value.type = type
})
// 监视初始化参数type的变化,当type发生改变的时候重新发送请求获取列表数据
watch(() => findNewsPageInfo.value, () => {getPageList()
}, {deep: true,
})
// 初始化请求分页列表数据
const getPageList = async () => {let result = await getfindNewsPageInfo(findNewsPageInfo.value)pageData.value = result.pageInfo.pageDatafindNewsPageInfo.value.pageNum = result.pageInfo.pageNumfindNewsPageInfo.value.pageSize = result.pageInfo.pageSizetotalSize.value = +result.pageInfo.totalSize
}
// 组件挂载的生命周期钩子
onMounted(() => {getPageList()
})
// 点击查看全文的回调
const toDetail = (hid) => {router.push({ name: "Detail" ,query:{ hid }});
}// 点击删除的回调
const handlerDelete = async (id) => {await removeByHid(id)ElMessage.success('删除成功!')//重新获取列表请求getPageList()
}
//点击修改的回调
const Modify = (hid) => {router.push({ name: "addOrModifyNews", query: { hid } });
}
</script><style lang="less" scoped>
.container {width: 1200px;margin: 0 auto;display: flex;flex-direction: column;align-items: center;// 列表样式.listItem {.containerItem {margin-top: 20px;border-radius: 10px;border: 2px solid #ebebeb;width: 600px;height: 120px;div {margin-top: 10px;}.text {margin-left: 15px;color: #353a3f;}.detail {span {margin-left: 15px;color: #8b778a;font-size: 14px;}}}}
}
</style>

3.5第四个

<template><div class="login-container"><el-form:model="loginForm"ref="formRef"label-width="80px"class="login-form":rules="loginRules" ><h2>用户登录</h2><el-form-item label="用户名" prop="username"><el-inputv-model="loginForm.username"ref="username"name="username"autocomplete="off"placeholder="请输入用户名"></el-input></el-form-item><el-form-item label="密码" prop="userPwd"><el-inputtype="password"v-model="loginForm.userPwd"autocomplete="off"placeholder="请输入密码"></el-input></el-form-item><el-form-item><el-button type="success" @click.native.prevent="login">登录</el-button><el-button type="primary" @click="toRegister">注册</el-button></el-form-item></el-form></div>
</template><script lang="ts">import { defineComponent } from 'vue'export default  defineComponent({name:'Login'})
</script>
<script lang="ts" setup>
import { ref } from "vue"
import { useUserInfoStore } from '../../stores/userInfo';import type { FormInstance } from 'element-plus';
import { useRouter } from 'vue-router'
const userInfoStore = useUserInfoStore()
const router = useRouter()
const formRef = ref<FormInstance>()
const loading = ref(false)
//账号密码参数
const loginForm = ref({username: "zhangsan",userPwd: "123456",
})
// 校验规则
const validateUsername = (rule: any, value: any, callback: any) => {if (value.length < 4) {callback(new Error('用户名长度不能小于4位'))} else {callback()}
}
// 校验规则
const validatePassword = (rule: any, value: any, callback: any) => {if (value.length < 6) {callback(new Error('密码长度不能小于6位'))} else {callback()}
}
// 校验规则
const loginRules = {username: [{ required: true, validator: validateUsername }],userPwd: [{ required: true, trigger: 'blur', validator: validatePassword }]
}
//点击登录的回调
const login = async () => {// console.log('点击登录');await formRef.value?.validate()loading.value = truetry {// await getUserInfo(loginForm.value)await userInfoStore.login(loginForm.value)router.push({ name: "HeadlineNews" });} finally {loading.value = false}// loading.value = true// const { username, userPwd } = loginForm.value// try {//   await userInfoStore.login(username, userPwd)//   router.push({ path: redirect.value || '/' })// } finally {//   loading.value = false// }
}const toRegister = ()=> {router.push({ name: "Register" });
}
</script><style scoped>
.login-container {display: flex;justify-content: center;align-items: center;height: 100vh;
}
.login-form {width: 400px;text-align: center;
}
</style>

3.6第五个

<template><div class="register-container"><el-form:model="registerForm"ref="formRef"label-width="80px"class="register-form":rules="registerRules"><h2>用户注册</h2><el-form-item label="姓名" prop="nickName"><el-inputv-model="registerForm.nickName"autocomplete="off"ref="nickName"name="nickName"placeholder="请输入姓名"></el-input></el-form-item><el-form-item label="用户名" prop="username"><el-inputv-model="registerForm.username"autocomplete="off"ref="username"name="username"placeholder="请输入用户名"></el-input></el-form-item><el-form-item label="密码" prop="userPwd"><el-inputtype="password"v-model="registerForm.userPwd"ref="userPwd"name="userPwd"autocomplete="off"placeholder="请输入密码"></el-input></el-form-item><!-- prop="confirmPassword" --><el-form-item label="确认密码"  prop="confirmPassword"><el-inputtype="password"v-model="registerForm.confirmPassword"autocomplete="off"ref="confirmPassword"name="confirmPassword"placeholder="请确认密码"></el-input></el-form-item><el-form-item><el-button type="primary" @click="register">注册</el-button><el-button type="danger" @click="resetForm">重置</el-button><el-button type="success" @click="goLogin">去登录</el-button></el-form-item></el-form></div>
</template><script lang="ts">import { defineComponent } from 'vue'export default  defineComponent({name:'Register'})
</script>
<script lang="ts" setup>
import { ref } from "vue"
import { useRouter } from 'vue-router'
import { ElMessage, FormInstance } from 'element-plus';
import { registerValidateApi, registerApi } from "../../api/index"
const router = useRouter()
// 初始化注册参数
const registerForm = ref({username: "",userPwd: "",confirmPassword: "",nickName:''
})
const formRef = ref<FormInstance>()// 校验规则
const validateUsername = (rule: any, value: any, callback: any) => {if (value.length < 4) {callback(new Error('用户名长度不能小于4位'))} else {callback()}
}
// 校验规则
const validatePassword = (rule: any, value: any, callback: any) => {if (value.length < 6) {callback(new Error('密码长度不能小于6位'))} else {callback()}
}
// 校验规则
const validateConfirmPassword = (rule: any, value: any, callback: any) => {if (value.length < 6) {callback(new Error('密码长度不能小于6位'))} else {callback()}
}
// 校验规则
const validateNickName = (rule: any, value: any, callback: any) => {if (value.length >= 2  && value.length  <= 6  ) {callback()} else {callback(new Error('姓名必须在2-6位'))
}
}
// 校验规则
const registerRules = {nickName: [{ required: true, trigger: 'blur', validator: validateNickName }],username: [{ required: true, validator: validateUsername }],userPwd: [{ required: true, trigger: 'blur', validator: validatePassword }],confirmPassword: [{ required: true, trigger: 'blur', validator: validateConfirmPassword }]
}//点击注册的回调
const register = async () => {await formRef.value?.validate()if (registerForm.value.userPwd == registerForm.value.confirmPassword) {// 调用用户名校验接口await registerValidateApi(registerForm.value.username)//  整理参数const obj = {username: "",userPwd: "",nickName: ''}obj.username = registerForm.value.usernameobj.userPwd = registerForm.value.userPwdobj.nickName = registerForm.value.nickName//  调用注册接口await registerApi(obj)formRef.value?.resetFields()ElMessage.success("注册成功")} else {return ElMessage.error("密码和确定密码必须一致")}}
//点击去登录的回调
const goLogin = () => {router.push({path:"/login"})
}//点击重置的回调
const resetForm = () => {//重置表单formRef.value?.resetFields()
}</script><style scoped>
.register-container {display: flex;justify-content: center;align-items: center;height: 100vh;
}
.register-form {width: 400px;text-align: center;
}
</style>

3.7一起加入主题组件App.vue

<template><div ><Header v-show="isHeader"></Header><router-view></router-view></div>
</template><script >
import Header from './components/Header.vue'import { defineComponent } from 'vue'export default  defineComponent({name:'App'})
</script>
<script  setup>
import { computed } from "vue"
import { useRoute } from 'vue-router'
const route = useRoute() // 路由信息对象
// 判断是否显示header组件const isHeader =  computed(() => {return route.name !== "Login" && route.name !== "Register" && route.name !== "addNews";
})</script><style lang="less" scoped></style>

项目实战二结束

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

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

相关文章

当全球银行系统“崩溃”时会发生什么?

有句名言&#xff1a;“当美国打喷嚏时&#xff0c;世界就会感冒……”换句话说&#xff0c;当人们对美国及其经济稳定性的信心下降时&#xff0c;其他经济体&#xff08;以及黄金、白银和股票等资产&#xff09;的价值往往会下降。 与任何其他资产类别一样&#xff0c;加密货…

超详细-数据结构-二叉树概念及结构,堆的概念及结构以及堆的代码的c语言实现

本篇博客将详细讲述二叉树的概念&#xff0c;堆的概念及结构以及堆的代码实现&#xff0c;以及二叉树&#xff0c;堆的相关应用。Top K 问题&#xff0c;堆排序的实现以及二叉树链式结构的实现将在之后的博客更新。你可在目录中找到你想重点阅读的内容。堆的完整代码实现在文章…

如何撤销/回滚远程修改

1. git revert 通过git revert commit_id&#xff0c;撤销指定commit&#xff0c;然后push到远程分支&#xff0c;即可撤销指定commit的修改&#xff0c;并新增一个revert的提交记录。 2. 撤销HEAD的修改并删除提交记录 git reset --hard HEAD^ # 撤销最近一次的修改 git pu…

【秋招笔试题】方程

解析&#xff1a;暴力枚举。建议用Python的eval函数,C手写略麻烦。 #include <iostream> #include <string> #include <vector> #include <sstream>using namespace std;long long stringResult(const string &expr) {vector<string> plusP…

文字改视频技术——Rerender A Video

Rerender A Video 的实现技术结合了深度学习、计算机视觉、图像处理、GPU 加速和云计算等多种先进技术&#xff0c;旨在提供高效、优质的视频渲染和增强功能。以下是详细说明&#xff0c;特别突出风格迁移技术的解释。 一、Rerender A Video 介绍 Rerender A Video 利用深度学…

visual studio性能探测器使用案列

visual studio性能探测器使用案列 在visual studio中&#xff0c;我们可以使用自带的工具对项目进行性能探测&#xff0c;具体如下 1.选择性能探查器 Vs2022/Vs2019中打开方式&#xff1a; Vs2017打开方式&#xff1a; 注意最好将解决方案配置为&#xff1a;Release Debu…

昇思25天学习打卡营第22天|CycleGAN图像风格迁移互换

相关知识 CycleGAN 循环生成网络&#xff0c;实现了在没有配对示例的情况下将图像从源域X转换到目标域Y的方法&#xff0c;应用于域迁移&#xff0c;也就是图像风格迁移。上章介绍了可以完成图像翻译任务的Pix2Pix&#xff0c;但是Pix2Pix的数据必须是成对的。CycleGAN中只需…

如何获得某个Window画面所属包名packageName和用户userId

在安卓上获得某个Window画面所属包名packageName和用户userId的方法 1&#xff0c;用到的工具如下&#xff1a; adb androidSDK里的monitor工具 adb shell dumpsys window animator adb shell dumpsys window命令 jdk 1.8已在安卓14模拟器上测试通过。 以AOSP的launcher中的m…

【.NET 6 实战--孢子记账--从单体到微服务】--开发环境设置

在这一小节&#xff0c;我们将设置开发环境。 一、安装SDK 咱们的项目使用的是 .NET6&#xff0c;开发前我们需要从官网上下载.NET6 SDK&#xff08;点击下载&#xff09;&#xff0c;这里要注意的是我们需要下载.NET6 SDK&#xff0c;而不是 .NET6 Runtiem 。SDK 包含 Runti…

C++静态成员变量和静态成员函数

演示代码如下&#xff1a; #include<iostream> using namespace std;class Person { public://静态成员函数 所有对象共享一个函数&#xff0c;且只能调用静态成员变量 ******static void func(){m_A 300;cout << "静态成员函数调用" << endl;}/…

【MySQL进阶之路 | 高级篇】简述Bin Log日志

1. 日志类型 MySQL有不同类型的日志文件&#xff0c;用来存储不同类型的日志&#xff0c;分为二进制日志、错误日志、通用查询日志和慢查询日志&#xff0c;这也是常用的4种。MySQL 8又新增两种支持的日志:中继日志和数据定义语句日志。使用这些日志文件&#xff0c;可以查看M…

openFeign实现服务间调用

以两个模块&#xff08;batch&#xff0c;business&#xff09;为例子&#xff0c;期望实现batch调用business中的hello接口 在主程序batch中引入pom文件 <!--远程调用openfeign--><dependency><groupId>org.springframework.cloud</groupId><arti…

STK 12.9 feature highlights

STK 12.9 feature highlights The workflow for viewing, adding, deleting, and modifying an object’s active Access Constraints has been completely revamped. Using the “Active Constraints” panel in an object’s Properties Browser, you can view all active A…

Linux网络工具“瑞士军刀“集合

一、背景 平常我们在进行Linux服务器相关运维的时候&#xff0c;总会遇到一些网络相关的问题。我们可以借助这些小巧、功能强悍的工具帮助我们排查问题、解决问题。 下面结合之前的一些使用经验为大家介绍一下一些经典应用场景下&#xff0c;这个网络命令工具如何使用的。例如怎…

游泳馆押金原路退回源码解析

<dl class"list "><dd class"address-wrapper dd-padding"><div class"address-container"><cyberdiv style"color:#f0efed;font-size:14px;float:right;position:absolute;right:10px;top: 2px;">●●●<…

java的插桩

可以参考这个&#xff0c;利用Gradle Transform可以实现精准插桩&#xff1a;https://www.51cto.com/article/713694.html

后端面试题日常练-day09 【Java基础】

题目 希望这些选择题能够帮助您进行后端面试的准备&#xff0c;答案在文末 Java中的静态方法和实例方法有何区别&#xff1f; a) 静态方法可以直接通过类名调用&#xff0c;实例方法需要通过对象实例调用 b) 静态方法可以访问实例变量&#xff0c;实例方法可以访问静态变量 c)…

[Python][文件]详细讲解

目录 1.文件操作1.打开文件2.关闭文件3.写文件4.读文件 2.上下文管理器 1.文件操作 1.打开文件 使用内建函数open()打开一个文件f open(D:/test.txt, r)参数&#xff1a; 第一个参数是一个字符串&#xff0c;表示要打开的文件路径第二个参数是一个字符串&#xff0c;表示打开…

MYSQL 第三次作业

1、第三次作业 01、SELECT * FROM student; SELECT * FROM score; 02、SELECT * FROM student LIMIT 1, 3; 03、SELECT * FROM student WHERE department IN (计算机系, 英语系); 04、SELECT * FROM student WHERE birth_year > 1998; 05、SELECT department, COUNT(*) as c…

react的State生命周期

React的State和生命周期是React框架中非常重要的概念&#xff0c;它们共同协作以实现组件的动态更新和高效渲染。以下是对React的State和生命周期的详细解析&#xff1a; React的State 定义与作用&#xff1a; 状态&#xff08;State&#xff09;是React组件内部的数据源&am…