前端Web案例-登录和退出

YangeIT大约 22 分钟Maven基础框架私服聚合继承依赖传递

前端Web案例-登录和退出

1. 修改员工

修改员工

image-20231221215653566

对于修改功能,分为两步实现:

  1. 点击 “编辑” 根据ID查询员工的信息,回显展示。
  2. 点击 “保存” 按钮,修改员工的信息 。

代码操作

回显展示

1). 为 "编辑" 按钮绑定事件

<el-button type="primary" size="small" @click="updateEmp(scope.row.id);>编辑</el-button>

2). 在 <script> </script> 中定义 updateEmp 函数

// 定义一个对话框标题的变量
const formTitle = ref<string>('')

//修改员工-回显
const updateEmp = async (id:number) => {
  clearEmp()
  dialogFormVisible.value = true
  formTitle.value = '修改员工'

  let result = await queryInfoApi(id)

  if(result.code){
    emp.value = result.data

    //处理工作经历中的时间范围
    let exprList = emp.value.exprList;
    if(exprList && exprList.length > 0){
      exprList.forEach(expr => {
        expr.exprDate = [expr.begin, expr.end]
      })
    }
  }
}

打开浏览器,点击 编辑 按钮,测试数据回显:

image-20231221220826769
image-20231221220826769

完整代码

到此呢,关于员工管理的基本的增删改查功能,我们已经完成了。 目前为止,src/views/emp/index.vue 的完整代码如下:

点击查看代码

src/views/emp/index.vue 的完整代码

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import axios from 'axios';
import type { EmpModelArray, EmpModel, SearchEmpModel, PaginationParam, EmpExprModel, DeptModel, DeptModelArray } from '@/api/model/model'
import { deleteApi, queryPageApi, addApi,queryInfoApi, updateApi } from '@/api/emp'
import { queryAllApi } from '@/api/dept'

let tableData = ref<EmpModelArray>([])

import { ElMessage, ElMessageBox,type UploadProps} from 'element-plus'

// 请求后台的数据--查询部分


//当页面挂载完毕,开始执行
onMounted(() => {
  // axios.get('https://mock.apifox.com/m1/3161925-0-default/depts').then(res=>{
  //   console.log(res)
  //   tableData.value=res.data.data
  // })

  queryEmp()
  queryAllDept()

})



const handleDelete = async (id: number) => {


  console.log("删除员工 id:", id)

  ElMessageBox.confirm(
    '您确认删除此数据吗?',
    '删除员工',
    {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      type: 'warning',
    }
  )
    .then(async () => {//点击确认按钮

      //发起删除请求,根据结果,显示Message
      const res = await deleteApi(id + '')
      // res 结果{code:1,message:‘’,data:null }
      if (res.code) {//判断code是否为0 如果并不是0,就是ture
        ElMessage({
          type: 'success',
          message: '操作成功',
        })
      } else {

        ElMessage.error('删除失败')

      }

      //无论成功和失败,都需要重新发起列表请求,筛选页面
      queryEmp()


    })
    .catch(() => {//点击取消按钮
      ElMessage({
        type: 'info',
        message: '取消删除',
      })
    })



}

// 搜索栏 start 
//定义一个对象,用来接受搜索表单参数

const searchEmp = ref<SearchEmpModel>({
  name: '',
  gender: '',
  begin: '',
  end: '',
  date: []
})

// 查询函数
const queryEmp = async () => {

  // console.log('点击我,就可以查询数据,数据处理前:searchEmp:', searchEmp.value)
  // name: '萨达萨达', gender: '1', begin: '', end: '', date: Array(2)
  if (searchEmp.value.date.length > 0) {
    searchEmp.value.begin = searchEmp.value.date[0]
    searchEmp.value.end = searchEmp.value.date[1]
  } else {
    //没有选择时间范围
    searchEmp.value.begin = ''
    searchEmp.value.end = ''
  }
  // console.log('点击我,就可以查询数据,数据处理后:searchEmp:', searchEmp.value)
  //上面的是查询的条件,还需要补充2个参数,当前页,每页条数
  const res = await queryPageApi(
    searchEmp.value.begin,
    searchEmp.value.end,
    searchEmp.value.gender,
    searchEmp.value.name,
    fenyeParam.value.currentPage,
    fenyeParam.value.pageSize
  )

  // res是返回结果

  if (res.code) {
    tableData.value = res.data.rows
    fenyeParam.value.total = res.data.total
  }



}

const resetQueryEmp = () => {
  console.log("清空搜索栏")

  searchEmp.value = { name: '', gender: '', begin: '', end: '', date: [] }

  // 当前页需要回到1
  fenyeParam.value.currentPage = 1

  //清空后,重新查询,因为没有参数,所以查询的是所有!
  queryEmp();

}

// 分页栏 start
//定义一个变量 封装 每页条数和总条目数据
const fenyeParam = ref<PaginationParam>({
  currentPage: 1,
  pageSize: 5,
  total: 0
})




const small = ref(false)
const background = ref(false)
const disabled = ref(false)

const handleSizeChange = (val: number) => {
  //每页条数发生变化
  console.log(`每页条数 :${val}`)

  fenyeParam.value.pageSize = val


  queryEmp();
}
const handleCurrentChange = (val: number) => {
  //当前页发生变化
  console.log(`当前页: ${val}`)
  fenyeParam.value.currentPage = val
  queryEmp();
}

// 分页栏 end


// 批量删除  start
//选中对象的容器
const multipleSelection = ref<EmpModel[]>([])

//选中对象的id容器
const selectionIds = ref<(number | undefined)[]>([])


//如果多选框选择了,就会回调次方法 其中val就是最新的值
const handleSelectionChange = (val: EmpModel[]) => {
  multipleSelection.value = val


  console.log("批量删除,对象容器:", multipleSelection)
  //观察控制台,发i发现输出的是一个一个的员工对象
  //思考:批量删除的接口,需要传递的ids=1,2,3
  //解决方案:对象,对象2,对象3===》id1,id2,id3

  multipleSelection.value.map((item) => {
    //获取到每个对象item 将其中的id,添加到selectionIds数组中
    selectionIds.value.push(item.id)
  })

  // 发现每次变化,都会添加,导致selectionIds会出现重复id

  selectionIds.value = Array.from(new Set(selectionIds.value))

  // 此时发现id已经去重了,格式:[2,3,4,5,6]可以向后台提交了,



  console.log("批量删除,id容器:", selectionIds.value)
}




const deleteByIds = async () => {

  //判断ids是否为空

  if (selectionIds.value.length == 0) {

    ElMessage.error('请勾选要删除的数据!!')
    return
  }





  ElMessageBox.confirm(
    '您确认删除此数据吗?',
    '删除员工',
    {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      type: 'warning',
    }
  )
    .then(async () => {//点击确认按钮

      //发起删除请求,根据结果,显示Message

      //由于批量删除的数组,格式:[1,3,4,5],传给后台的数据是这样的1,3,4

      let ids = selectionIds.value.join(',')//1,3,4



      const res = await deleteApi(ids)
      // res 结果{code:1,message:‘’,data:null }
      if (res.code) {//判断code是否为0 如果并不是0,就是ture
        ElMessage({
          type: 'success',
          message: '操作成功',
        })
      } else {

        ElMessage.error('删除失败')

      }

      selectionIds.value = [];//无论删除成功和失败,都将ids 清空

      //无论成功和失败,都需要重新发起列表请求,筛选页面
      queryEmp()



    })
    .catch(() => {//点击取消按钮
      ElMessage({
        type: 'info',
        message: '取消删除',
      })
    })



}

// 批量删除 end

// 新增员工 start

// 定义一个emp员工对象,接收对话框提交的数组

let emp = ref<EmpModel>({

  username: '',
  password: '',
  name: '',
  gender: '',
  phone: '',
  job: '',
  salary: '',
  image: '',
  entryDate: '',
  deptId: '',
  exprList: [] //工作经历
})


let labelWidth = ref(80)

//定义一个boolea类型变量,用来控制对话框的显示和隐藏
const dialogFormVisible = ref<boolean>(false)//默认false 隐藏

//准备性别和职位和部门的的下拉框数据start
//职位列表数据
const jobs = ref([{ name: '班主任', value: 1 }, { name: '讲师', value: 2 }, { name: '学工主管', value: 3 }, { name: '教研主管', value: 4 }, { name: '咨询师', value: 5 }, { name: '其他', value: 6 }])
//性别列表数据
const genders = ref([{ name: '男', value: 1 }, { name: '女', value: 2 }])
const depts = ref<DeptModelArray>([])

//发起网络请求,获取到部门的列表

const queryAllDept = async () => {
  const res = await queryAllApi()
  //code data  message

  if (res.code) {
    depts.value = res.data
  }
}
//准备性别和职位和部门的的下拉框数据end

//弹出新建员工对话框
const add = () => {
  dialogFormVisible.value = true;
  formTitle.value = '增加员工'
}

//点击新建员工对话框中的 保存

const save = async () => {

  let result = null;
  //给员工经历中的起止时间 赋值
  if (emp.value.exprList.length > 0) {
    emp.value.exprList.forEach(expr => {
      expr.begin = expr.exprDate[0]
      expr.end = expr.exprDate[1]
    })
  }


  if (emp.value.id) { // 存在id,修改
    console.log('修改员工数据:', emp.value)
    //发起网络请求,保存数据
    result = await updateApi(emp.value)
  } else {// 不存在id,修改
    console.log('新增员工数据:', emp.value)
    //发起网络请求,保存数据
    result = await addApi(emp.value)
  }


  if (result.code) {//如果0 等同fasle 如果是非0等同于true
      ElMessage({
        type: 'success',
        message: '操作成功',
      })
      dialogFormVisible.value = false;
      queryEmp()

    } else {


      ElMessage.error(result.msg)
    }

  // 无论成功和失败,清空emp数据
  clearEmp();



}

// 清除提交的参数数据
const clearEmp = () => {
  emp.value = {
    username: '',
    password: '',
    name: '',
    gender: '',
    phone: '',
    job: '',
    salary: '',
    image: '',
    entryDate: '',
    deptId: '',
    exprList: []
  }
}

// 员工图片上传 start

//上传给后台,成功后,回调
const handleAvatarSuccess: UploadProps['onSuccess'] = (
  response,
  uploadFile
) => {
 emp.value.image = response.data
// console.log(response)

}

//上传前,进行校验 ,如果返回true就上传,如果返回false 就不上传,弹出警告提示!!
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
  if (rawFile.type !== 'image/jpeg') {
    ElMessage.error('必须上传JpEg格式的图片')
    return false
  } else if (rawFile.size / 1024 / 1024 > 2) {
    ElMessage.error('上传的图片不能超过2M!!!')
    return false
  }
  return true
}

// 员工图片上传 end

const addWorkItem=()=>{

  emp.value.exprList.push({
    exprDate:[],
    begin:'',
    end:'',
    company:'',
    job:'',
  })
}

const delWorkItem=(expr:EmpExprModel)=>{

console.log("要删除工作经历:",expr)

if(emp.value.exprList){//不为空 ,就可以删除

  const index=emp.value.exprList.indexOf(expr);

  if(index!=-1){
    emp.value.exprList.splice(index,1)
  }

}



}



// 定义一个对话框标题的变量
const formTitle = ref<string>('')

//修改员工-回显
const updateEmp = async (id:number) => {
  clearEmp()
  dialogFormVisible.value = true
  formTitle.value = '修改员工'

  let result = await queryInfoApi(id)

  if(result.code){
    emp.value = result.data

    //处理工作经历中的时间范围
    let exprList = emp.value.exprList;
    if(exprList && exprList.length > 0){
      exprList.forEach(expr => {
        expr.exprDate = [expr.begin, expr.end]
      })
    }
  }
}





</script>

<template>
  <h1>员工管理</h1>
  <!-- 搜索栏 start -->
  <!-- 搜索栏 -->
  <el-form :inline="true" :model="searchEmp" class="demo-form-inline">
    <el-form-item label="姓名">
      <el-input v-model="searchEmp.name" placeholder="请输入姓名" />
    </el-form-item>

    <el-form-item label="性别">
      <el-select v-model="searchEmp.gender" placeholder="请选择">
        <el-option label="" value="1" />
        <el-option label="" value="2" />
      </el-select>
    </el-form-item>

    <el-form-item label="入职时间">
      <el-date-picker v-model="searchEmp.date" type="daterange" range-separator="" start-placeholder="开始时间"
        end-placeholder="结束时间" value-format="YYYY-MM-DD" />
    </el-form-item>

    <el-form-item>
      <el-button type="primary" @click="queryEmp">查询</el-button>
      <el-button @click="resetQueryEmp">清空</el-button>
    </el-form-item>
  </el-form>

  <!-- 功能按钮 -->
  <el-button type="success" @click="add">+ 新增员工</el-button>
  <el-button type="danger" @click="deleteByIds">- 批量删除</el-button>
  <br><br>


  <!-- 搜索栏 end  -->



  <br>

  <el-table :data="tableData" border style="width: 100%" @selection-change="handleSelectionChange" fit>
    <el-table-column type="selection" width="55" />

    <el-table-column prop="name" label="姓名" align="center" width="130px" />
    <el-table-column prop="gender" label="性别" align="center" width="100px">
      <template #default="scope">
        <span v-if="scope.row.gender == 1"></span>
        <span v-else-if="scope.row.gender == 2"></span>
      </template>
    </el-table-column>

    <el-table-column prop="image" label="头像" align="center" >
   
      <template #default="scope">
        <el-image style="width: 100px; height: 100px" :src="scope.row.image" />
      </template>
    </el-table-column>



    <el-table-column prop="deptName" label="所属部门" align="center" />
    <el-table-column prop="job" label="职位" align="center" width="100px">
      <template #default="scope">
        <span v-if="scope.row.job == 1">班主任</span>
        <span v-else-if="scope.row.job == 2">讲师</span>
        <span v-else-if="scope.row.job == 3">学工主管</span>
        <span v-else-if="scope.row.job == 4">教研主管</span>
        <span v-else-if="scope.row.job == 5">咨询师</span>
        <span v-else>其他</span>
      </template>
    </el-table-column>

    <el-table-column prop="entryDate" label="入职时间" align="center" width="130px" />
    <el-table-column prop="updateTime" label="最后修改时间" align="center" />
    <el-table-column label="操作" align="center">
      <template #default="scope">
        <el-button type="primary" size="small" @click="updateEmp(scope.row.id)">编辑</el-button>
        <el-button type="danger" size="small" @click="handleDelete(scope.row.id)">删除</el-button>
      </template>
    </el-table-column>
  </el-table>
  <br>

  <!-- 分页条 -->
  <el-pagination v-model:current-page="fenyeParam.currentPage" v-model:page-size="fenyeParam.pageSize"
    :page-sizes="[5, 10, 15, 20]" :small="small" :disabled="disabled" :background="background"
    layout="total, sizes, prev, pager, next, jumper" :total="fenyeParam.total" @size-change="handleSizeChange"
    @current-change="handleCurrentChange" />

  <!-- 新增和修改员工对话框 -->

  <!-- 新增员工 / 修改员工的 DiaLog对话框 -->
  <el-dialog v-model="dialogFormVisible" :title="formTitle">
    <el-form :model="emp">
      <!-- 第一行 -->
      <el-row>
        <el-col :span="12">
          <el-form-item label="用户名" :label-width="labelWidth">
            <el-input v-model="emp.username" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="姓名" :label-width="labelWidth">
            <el-input v-model="emp.name" />
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第二行 -->
      <el-row>
        <el-col :span="12">
          <el-form-item label="性别" :label-width="labelWidth">
            <el-select v-model="emp.gender" placeholder="请选择" style="width: 100%;">
              <el-option v-for=" gender in genders" :label="gender.name" :value="gender.value" />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="手机号" :label-width="labelWidth">
            <el-input v-model="emp.phone" />
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第三行 -->
      <el-row>
        <el-col :span="12">
          <el-form-item label="薪资" :label-width="labelWidth">
            <el-input v-model="emp.salary" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="入职日期" :label-width="labelWidth">
            <el-date-picker v-model="emp.entryDate" type="date" placeholder="请选择入职日期" value-format="YYYY-MM-DD"
              style="width: 100%;" />
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第四行 -->
      <el-row>
        <el-col :span="12">
          <el-form-item label="所属部门" :label-width="labelWidth">
            <el-select v-model="emp.deptId" placeholder="请选择" style="width: 100%;">
              <el-option v-for=" dept in depts" :label="dept.name" :value="dept.id" />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="职位" :label-width="labelWidth">
            <el-select v-model="emp.job" placeholder="请选择" style="width: 100%;">
              <el-option v-for=" job in jobs" :label="job.name" :value="job.value" />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第五行 -->
      <el-row>
        <el-col :span="12">
          <el-form-item label="头像" :label-width="labelWidth">
            <el-upload
              class="avatar-uploader"
              action="api/upload"
              :show-file-list="false"
              :on-success="handleAvatarSuccess"
              :before-upload="beforeAvatarUpload"
            >
              <img v-if="emp.image" :src="emp.image" class="avatar" />
              <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
            </el-upload>


          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第六行 -->
      <el-row>
        <el-col :span="24">
          <el-form-item label="工作经历" :label-width="labelWidth">
            <el-button type="success" size="small" @click="addWorkItem">+ 添加工作经历</el-button>
          </el-form-item>
        </el-col>
      </el-row>

      <!-- 第七行  使用v-for -->
      <el-row :gutter="5" v-for="expr in emp.exprList">
        <el-col :span="10">
          <el-form-item label="时间" size="small" :label-width="labelWidth">
            <el-date-picker type="daterange" v-model="expr.exprDate" range-separator="" start-placeholder="开始时间" end-placeholder="结束时间"
              value-format="YYYY-MM-DD" />
          </el-form-item>
        </el-col>

        <el-col :span="6">
          <el-form-item label="公司" size="small">
            <el-input placeholder="公司名称" v-model="expr.company"/>
          </el-form-item>
        </el-col>

        <el-col :span="6">
          <el-form-item label="职位" size="small">
            <el-input placeholder="职位名称" v-model="expr.job"/>
          </el-form-item>
        </el-col>

        <el-col :span="2">
          <el-form-item size="small">
            <el-button type="danger" @click="delWorkItem(expr)">- 删除</el-button>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>

    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取消</el-button>
        <el-button type="primary" @click="save">确定</el-button>
      </span>
    </template>
  </el-dialog>

</template>

<style scoped>
.demo-pagination-block+.demo-pagination-block {
  margin-top: 10px;
}

.demo-pagination-block .demonstration {
  margin-bottom: 16px;
}

.avatar-uploader .avatar {
  width: 178px;
  height: 178px;
  display: block;
}
</style>

<style>
.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
}

.avatar-uploader .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;
}
</style>

总结

课堂作业

  1. 根据上述提示完成员工修改 🎤

2. 登录

2.1 基本功能实现

基本功能实现

image-20231222214427884

代码操作

  1. 导入资料中提供的登录页面的背景图片。 【04. 登录认证-基础文件/bg1.jpg ---------> 将该文件复制到项目的 src/assets 目录中】

  2. 导入资料中提供的登录页面的组件。【04. 登录认证-基础文件/login ---------> 将该目录复制到项目的 src/views 目录中】

  3. 导入资料中提供的登录的api文件。【04. 登录认证-基础文件/login.ts ---------> 将该文件复制到项目的 src/api 目录中】

图示:image

  1. login/index.vue 中编写登录操作交互的逻辑,最终整个文件的代码如下:
<script setup lang="ts">
  import { ref } from 'vue'
  import type { LoginEmp } from '@/api/model/model'
  import { loginApi } from '@/api/login';
  import { ElMessage } from 'element-plus';
  import { useRouter } from 'vue-router'

  
  let loginForm = ref<LoginEmp>({username:'', password:''})


  //获取到路由实例对象
  const router = useRouter()

  //执行登录函数
  const login = async () => {
    //发送请求, 执行登录
    const result = await loginApi(loginForm.value)
    if(result.code){//如果登录成功, 提示成功信息
      ElMessage.success('登录成功') 
      //跳转页面 ---> Vue-Router
      router.push('/index')
    }else {//如果登录失败, 提示错误信息
      ElMessage.error(result.msg)
    }
  }

  //重置操作
  const reset = () => {
    loginForm.value = {username:'', password:''}
  }
</script>

<template>
  <div id="container">
    <div class="login-form">
      <el-form label-width="80px">
        <p class="title">Tlias智能学习辅助系统</p>
        <el-form-item label="用户名" prop="username">
          <el-input v-model="loginForm.username" placeholder="请输入用户名"></el-input>
        </el-form-item>

        <el-form-item label="密码" prop="password">
          <el-input type="password" v-model="loginForm.password" placeholder="请输入密码"></el-input>
        </el-form-item>

        <el-form-item>
          <el-button class="button" type="primary" @click="login">登 录</el-button>
          <el-button class="button" type="info" @click="reset">重 置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<style scoped>
#container {
  padding: 10%;
  height: 410px;
  background-image: url('../../assets/bg1.jpg');
  background-repeat: no-repeat;
  background-size: cover;
}

.login-form {
  max-width: 400px;
  padding: 30px;
  margin: 0 auto;
  border: 1px solid #e0e0e0;
  border-radius: 10px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
  background-color: white;
}

.title {
  font-size: 30px;
  font-family: '楷体';
  text-align: center;
  margin-bottom: 30px;
  font-weight: bold;
}

.button {
  margin-top: 30px;
  width: 120px;
}
</style>



 
 
 






 
 
 
 
 
 
 
 
 
 
 
 
 
 





















 
 






































  1. 在router/index.ts中增加登录页的路由配置
{
  path: '/login',
  name: 'login',
  component: () => import('../views/login/index.vue') //登录页
}
image
image

6.打开浏览器,访问 http://127.0.0.1:5173/loginopen in new window 进行测试

image
image
image
image

通过测试,我们看到,已经登录成功了。

但是呢,登录成功后,我们并没有对服务器端返回的token进行存储。

那么也就意味着,再后续的请求中,我们是没有办法直接获取到token,并在后续的每一次请求中,把这个token传递给服务端的。

所以,接下来,我们就需要考虑的是token的存储。

我们需要考虑将token存储起来,并且还得保证,存储之后各个vue组件都可以获取到这个token。 那这里我们就可以借助于 Vue3 中提供的 Pinia状态管理工具 来解决这个问题。

总结

课堂作业

  1. 根据上述提示,完成登录界面的书写🎤

2.2 Pinia存储令牌

前言

问题: 目前执行登录操作,登录成功之后,并没有将令牌信息起来,在后续的每次操作中,也就拿不到登录时的令牌信息了。

方案: 需要在登录成功后,将令牌等信息存储起来。 在后续的请求中,再将令牌取出来,携带到服务端。

image-20240111170912474

如果在项目的多个组件中,要共享数据,可以使用Vue3中提供的状态管理库 Pinia。

代码操作

定义Store

参照官方文档:https://pinia.vuejs.org/zh/core-concepts/open in new window

1). 将stores/counter.ts 文件重命名为 stores/loginEmp.ts,并定义如下内容:

import { ref } from 'vue'
import { defineStore } from 'pinia'
import type {LoginInfo} from '@/api/model/model'

export const useLoginEmpStore = defineStore('loginEmp', () => {
  const loginEmp = ref<LoginInfo>({})
  const setLoginEmp = (emp: LoginInfo) => { //存入
    loginEmp.value = emp;
  }
  const getLoginEmp = () => { //获取
    return loginEmp.value;
  }
  const clearLoginEmp = () => { //清除
    loginEmp.value = {}
  }
  return { loginEmp, setLoginEmp, getLoginEmp, clearLoginEmp }
})
image
image

总结

课堂作业

  1. pinia有什么作用?解决了什么问题?🎤
  2. 参考上述步骤,完成代码?

2.3 功能完善

功能完善

需求1: 目前,即使用户未登录的情况下访问服务器,服务器会响应401状态码,但是前端并不会跳转到登录页面。

需求2: 页面刷新之后,pinia中存储的令牌数据,就消失了,再次请求就获取不到令牌数据了。

代码操作

目前,即使用户未登录的情况下访问服务器,服务器会响应401状态码,但是前端并不会跳转到登录页面。

image-20240111174329980

具体代码实现如下:

//axios的响应 response 拦截器
request.interceptors.response.use(
  (response) => { //成功回调
    return response.data
  },
  (error) => { //失败回调
    if(error.response.status == 401){ //跳转登录页面
      ElMessage.error('登录失效, 请重新登录')
      router.push('/login')
    }else {
      ElMessage.error('接口访问异常')
    }
    return Promise.reject(error)
  }
)

注意: :记得将后端代码的 aop,拦截器或者过滤器打开哦!!!

总结

课堂作业

  1. 为什么要将登录信息持久化??🎤

2.4 退出登录

退出登录

代码操作

1). 为 layout/index.vue 组件中的 "退出登录" 按钮,绑定事件

image-20231222223718121

2). 在 <script> </script> 中定义退出的逻辑

<script lang="ts" setup>
  import { ref } from 'vue'
  import { ElMessage, ElMessageBox } from 'element-plus' 
  import router from '@/router'
  import { useLoginEmpStore } from '@/stores/loginEmp'
  
  const loginName = ref()
  	
  const loginStore = useLoginEmpStore();
  loginName.value = loginStore.getLoginEmp().name;
  
  //退出 
  const logout = () => {
    ElMessageBox.confirm('您确认退出登录吗?' , '退出登录', {confirmButtonText:'确认', cancelButtonText:'取消', type:'warning'})
    .then(async () => {
      loginStore.clearLoginEmp();
      router.push('/login')
      ElMessage.success('退出成功')
    }).catch(() => {
      ElMessage.info('取消退出')
    })
  }
</script>
image
image

打开浏览器测试效果:

image-20231222223817392
image-20231222223817392

4. 打包部署

打包部署

到此呢,部门管理、员工管理、登录认证的功能,我们都已经完成了。 那接下来,我们就来说一下前端工程的打包部署。 前端项目最终开发完毕之后,是需要打包,然后部署在nginx服务器上运行的 。

image-20231222224433835

代码操作

执行npm run build即可将项目打包,打包后的文件会出现在 dist 目录中。

image
image
image
image