<div class="homepage_menu">
<el-menu
active-text-color="#ffd04b"
background-color="#545c64"
class="el-menu-vertical-demo"
default-active="2"
text-color="#fff"
:unique-opened="true"
:router="true"
>
<!-- index 绑定的是单独展开的哪一列 element期望的是string类型 转换一下-->
<el-sub-menu :index="menu.id + ''" v-for="menu in menus" :key="menu.id">
<template #title>
<span>{{ menu.title }}</span>
</template>
<template v-for="menuChildren in menu.children">
<el-menu-item
:index="'/' + menu.name + '/' + menuChildren.name"
v-if="!menuChildren.hidden"
:key="menuChildren.id"
>{{ menuChildren.title }}</el-menu-item
>
</template>
</el-sub-menu>
</el-menu>
</div>
开发日记
//根据鼠标移入 移出事件 来判断鼠标选择的是哪一个一级菜单 然后让菜单展示数据
//根据后端给的id来判断
<li
v-for="item in menu"
:key="item.id"
//传递过去 对应鼠标移动到的菜单对应的id
@mouseenter="mourseHover(item.id)"
@mouseleave="mourseOut"
/>
//函数
import {lagsList ,searchCourse} from "../api/api";
//查询标签的数据
let taglist = ref([]);
let searchCourselist = ref([]);
//获取课程标签数据的参数
let tagsParams = {
pageNum: 1, //页码 默认1
pageSize: 10, //分页大小 默认10
entity: {
firstCategory: "", //一级分类id
},
};
//鼠标移入 触发
const isFirst = ref(false);
//id辨别移入的是哪一个一级菜单
const mourseHover = (id) => {
isFirst.value = true;
//根据一级id来获取数据
tagsParams.entity.firstCategory = id;
//课程标签接口
lagsList(tagsParams).then((res) => {
taglist.value = res.data.pageInfo.list;
});
//课程数据接口
searchCourse(tagsParams).then(res =>{
searchCourselist.value = res.data.pageInfo.list;
})
};
//鼠标移出 触发
const mourseOut = () => {
isFirst.value = false;
};
分页查询数据
1 利用element分页
//total 是数据的总条数
<el-pagination background layout="prev, pager, next"
:total="total"
//切换到最新页数据
@current-change='handleCurrentChange' />
//参数
let page = reactive({
pageNum: 1,
pageSize: 4,
});
//请求商品数据接口
const product = () => {
searchCourse(page).then((res) => {
total.value =res.data.pageInfo.total
productList.value = res.data.pageInfo.list;
});
};
//分页数据
let total =ref(0);
//val是点击的当前页码
const handleCurrentChange = (val) =>{
//页码赋值
page.pageNum =val;
//请求展示商品数据的接口 再次请求----查询对应页数据
product()
}
一级 二级 难度查询对应数据
//一级
<el-tag
class="category-poniter-item"
effect="plain"
type="info"
v-for="item in firstCategorys"
:key="item.id"
@click="buildingCondition('fcategory', item.id)"
>
</el-tag>
// 二级
<el-tag
class="category-poniter-item"
effect="plain"
type="info"
v-for="item in secondCategorys"
:key="item.id"
@click="buildingCondition('scategory', item.id)"
>
</el-tag>
//难度
<el-tag
class="category-poniter-item"
effect="plain"
type="info"
v-for="item in difficulty"
:key="item.id"
@click="buildingCondition('clevel', item.code)"
>
</el-tag>
//二级分类
let category =reactive({
categoryId: -1
})
let secondCategorys = ref([]);
const getSecondCate = () =>{
getSecondCategorys(category).then((res) => {
secondCategorys.value = res.data.list;
});
}
getSecondCate()
//展示商品接口
let page = reactive({
pageNum: 1,
pageSize: 4,
entity: {
firstCategory: "", //一级分类Id
secondCategory: "", //二级分类Id
courseLevel: "", // 课程等级(1初级 2 中级 3高级)
isFree: "", //免费课程
isMember: "", //会员课程
sortBy: "", //筛选排序 最新时间 最多购买 价格 time-desc
},
});
let productList = ref([]);
//根据一级 二级分类的选择来显示商品的内容 应该在一二级接口里面都调用mostNew接口
const product = () => {
searchCourse(page).then((res) => {
total.value = res.data.pageInfo.total;
productList.value = res.data.pageInfo.list;
});
};
product();
//通过点击事件的type不同 来区分一级 二级 难度
const buildingCondition = () =>{
if(type === 'fcategory'){
//一级
category.categoryId = id;
getSecondCate ();
page.entity.firstCategory = id;
page.entity.secondCategory = '';
product ();
return
}
if(type === 'scategory'){
//二级
page.entity.secondCategory = id;
product ()
}
//难度
if (type === "clevel") {
currentType[type].id = id;
page.entity.courseLevel = id;
product(page);
return;
}
切换页面 ---------页面都写了 只是让一个显示 一个不显示
<div class="info-nav">
<div class="nav-container">
//真---------标题
<div class="chapter-item " @click="chapter = true" >
<div :class="chapter? 'active1' : ''">章节</div>
<div :class="chapter ? 'line active2' : ''"></div>
</div>
//点击false-------标题
<div class="chapter-item" @click="chapter = false">
<div :class=" !chapter ? 'active1' : ''">下载资料</div>
<div :class="!chapter ? 'line active2' : ''"></div>
</div>
</div>
</div>
//真时 显示------内容
<div class="course" v-if="chapter">
<div class="main">
<div class="introduction">
<div class="desc">
该课程暂无介绍
</div>
</div>
</div>
</div>
</div>
//false 显示-------内容
<div v-else>
<div>
<div class="down" v-if="shuju.length > 0">
<div class="source">
<span class="downloadCourse">哈哈哈哈哈</span>
<button class="downloadbtn">下载资料</button>
</div>
</div>
</div>
<div>
<el-empty
description="该课程暂无资料"
></el-empty>
</div>
</div>
<script setup>
import {ref} from 'vue';
//一上来就是真
let chapter = ref(true);
let shuju = ref([])
</script>
<li
v-for="item in loginTxt"
:key="item.id"
class="nav-items"
:class="current == item.id ? 'actives' : ''"
@click="loginChange(item.id)"
>
<a :class="current == item.id ? 'activess' : ''">{{
item.text
}}</a>
</li>
//一个大盒子
<div class="tab-content">
//账号
<div class="tab-pane fade show active" v-if="current == 1">
<!-- 账号登录 -->
<div class="tab-main" >
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules">
<el-form-item class="login-user" prop="username">
<el-icon><avatar /></el-icon>
<el-input
v-model="ruleForm.username"
placeholder="请输入您的用户名"
/>
</el-form-item>
<el-form-item class="login-password" prop="userpwd">
<el-icon><lock /></el-icon>
<el-input
type="password"
v-model="ruleForm.userpwd"
placeholder="请输入您的密码"
/>
</el-form-item>
<el-form-item class="login-submit">
<el-button type="primary" @click="userBtn(ruleFormRef)"
>登录</el-button
>
</el-form-item>
<a class="forgetpwd">忘记密码?</a>
<div class="login-text">
登录即同意相关服务条款和隐私政策
<a>《小鹿线用户服务协议》</a><a>《小鹿线隐私政策》</a>
若您没有账号,系统将为您自动创建账号并登录。
</div>
</el-form>
</div>
</div>
//否则
<div class="tab-pane fade" v-else>
<!-- 短信登录 -->
<div class="tab-main">
<el-form>
<el-form-item class="login-user" prop="phone">
<el-icon><avatar /></el-icon>
<el-input placeholder="请输入您的手机号" />
</el-form-item>
<el-form-item class="login-Verification" prop="captcha">
<el-input placeholder="请输入您的手机号" />
<el-button
class="btn btn-primary sendcaptcha"
type="primary"
>发送验证码</el-button
>
</el-form-item>
<div class="login-submit">
<el-button
class="btn btn-primary sendcaptcha"
type="primary"
>登录</el-button
>
</div>
</el-form>
</div>
</div>
<script setup>
//上来就是账号登录
let current = ref(1);
//数据驱动视图
let loginTxt = reactive(
{id :1 ,text : 账号登录},
{id:2 ,text : 手机号登录}
)
// 接受ID 点击的ID给 current传去
//当current ==1 时账号
const loginChange = (id) =>{
current.value = id
}
<script>
.actives {
color: #388eff;
border-bottom: 4px solid #388eff;
}
.activess {
color: #388eff !important;
}
单选框
<el-radio-group v-model="radio" @change="change">
<el-radio :label="1">免费课程</el-radio>
<el-radio :label="2">会员课程</el-radio>
</el-radio-group>
//切换免费 会员课程
let radio = ref("0");
const change = (val) => {
if (val == "1") {
page.entity.isFree = "1";
page.entity.isMember = "";
}
if (val == "2") {
page.entity.isMember = "1";
page.entity.isFree = "";
}
product();
};
pinia持久化存储数据
//安装pinia
npm i pinia
//pinia持久化存储数据
npm i pinia-plugin-persist
import { defineStore } from 'pinia'
export const useUserStore = defineStore({
id: 'user',
state: () => {
return {
token: ''
}
},
actions:{
//保存token
setToken( token ){
this.token = token;
} ,
//清除token
clearToken(){
this.token = ''
}
},
// 开启数据缓存
persist: {
enabled: true,
strategies: [{
key: 'xiaoluxian_user',
storage: localStorage,
//paths: ['token']
}]
}
})
pinia使用
//登录接口
export const loginByJson = (data) => requests.post(`/api/u/loginByJson`, data);
//引入对应模块
import {useUserStore} from '../store/use'
let store = useUserStore()
const userBtn = () => {
ruleFormRef.value.validate((valid, fields) => {
if (valid) {
loginByJson({
//加密
username: Encrypt(ruleForm.username),
password: Encrypt(ruleForm.userpwd),
}).then((res) => {
if (res.meta.code != 10006) {
ElMessage({
message: res.meta.msg,
type: "success",
});
return;
}
ElMessage({
message: "成功",
type: "success",
});
//存储token--------------------------------------------------------------------
store.setToken(res.data.accessToken);
router.go(-1);
});
console.log("用户名和密码验证成功");
} else {
ElMessage({
message: "格式错误",
type: "success",
});
}
});
};
//请求头带去token
import {useUserStore} from '../store/use'
//2. 请求拦截器
service.interceptors.request.use(
(config) => {
let store = useUserStore();
let token = store.token;
if (token) {
config.headers["Authorization"] = token;
}
return config;
},
(error) => {
Promise.reject(error);
}
);
//退出登录
const goLogout = () => {
ElMessageBox.confirm('确定退出登录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
logout().then(res=>{
if( res.meta.code=='200'){
let userStore = useUserStore();
//把pinia里面的清除函数拿过来---------------------------------------------
userStore.clearToken();
router.go(0);
ElMessage.success({
message: '退出成功!'
});
}
})
}).catch(() => {
ElMessage.info({
message: '已取消'
});
});
视频播放的插件
表单校验
<template>
<el-form :model='from'
:rules= 'rules'
ref='fromb'>
<el-form-item prop="name">
<el-input v-model='from.name' />
<el-button @click ='onSubmit(fromb)'></el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import {ref,reactive} from 'vue'
const from = reactive({
name:''
})
const fromb = ref ()
const rules = reactive({
name: [
{ required: true, message: 'Please input Activity name', trigger: 'blur' },
{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
],})
const onSubmit =(f) =>{
f.validate((valid, fields) => {
if (valid) {
console.log('submit!')
} else {
console.log('error submit!', fields)
}
})
}
</script>
flex布局
//flex布局
display :flex;
//justify-content
justify-content : center ------ 水平居中
justify-content : space-between -------- 先两边贴边 在平分剩余空间(重要)
justify-content : space-around---------- 平分剩余空间
//flex-wrap
flex-wrap : wrap ------- 换行
//align-items
align-items : center --------- 垂直居中
照片 动画效果
.courseItem {
width: 285px;
height: 280px;
margin: 0 20px 20px 0;
//动画效果
transition: margin-top 0.2s;
}
.courseItem:hover {
//向上移动10px
margin-top: -10px;
cursor: pointer;
}