后台管理项目笔记

1)什么是后台管理项目

可以让用户通过一个可视化工具,实现对于数据库的增删改查的操作,根据不同的角色,操作的内容也是不同的

2)vue.config.js(全局配置文件)


3)element-ui框架的两种引入方式

第一种(直接在main.js文件注册)

第二种

# 在src下的plugin文件夹中创建 .js文件,将vue,elementui,elementuicss,引入并注册,最后main.js文件中进行 import 导入


4)全局样式设置

#在App.vue文件中设置全局样式

5)element-ui 表单验证

#prop属性代表需要验证的字段名

6)加载进度条===nprogress


7)vue.config.js(全局文件配置)


8) md5加密

第一步

yarn add js-md5

第二步(在utils文件夹下建一个md5.js文件)

第三步

在需要使用的地方进行导入,使用即可

9)axios中添加token令牌和vue添加全局方法

第一步:在创建axios的实例中,写一个方法将token信息保存到请求头中

第二步:在plugin插件文件夹中创建一个myplugin.js文件,添加vue全局方法必须在main.js引入并vue.use

import myplugin from './plugin/myplugin ' vue.use(myplugin )

第三步:判断是否登录成功,成功情况下,存储token到本地,同时调用将token存入headers的this.$setToken方法


10)竖直方向导航折叠功能

**第一步:给折叠导航添加 :collapse="isCollapse"  是否折叠属性

**第二步:isCollapse 进行状态设置

##注意事项:折叠时盒子的宽度设为auto,不折叠时设置为 固定px


11)不同环境公共接口改变

第一步

在config文件夹下创建base_url.js 和index.js 
在base_url.js中写入以下代码

第二步

在index.js中进行导入base_url.js 并进行导出

第三步

在定义公共请求的js文件中 导入BASE_URL 进行使用


12) 封装axios

第一步

在util文件夹中创建一个request.js文件 定义公共的请求头和地址等
import axios from 'axios';
// 创建一个 Axios 实例
const service = axios.create({
  baseURL: 'https://autumnfish.cn', // 接口的基础路径
  timeout: 5000, // 请求超时时间
});
// 请求拦截器
service.interceptors.request.use(
  (config) =>{
      return config;
    } ,
  (error) => {
    // 处理请求错误

    console.error(error);
    return Promise.reject(error);
  });
// 响应拦截器
service.interceptors.response.use(
  (response) =>{
    
    return response
  } ,
  (error) => {
    // 处理响应错误

    console.error(error);
    return Promise.reject(error);
  });
export const request = service;

第二步

创建数据访问层
# 1.创建一个api 文件夹
# 2.创建一个具体功能的文件夹和js文件(举例:登录页面的所有请求都在此文件中)
# 3.导出每一个方法
import {request} from '@/utilis/request.js'
// 获取笑话
const getxiaohua = ()=>{
    return request({
        url:'/api/joke'
    })
}
// 用户注册
const register = (data)=>{
    return request({
        url:'/api/user/register',
        method:'post',
        data:data
    })
}
export{
    getxiaohua,
    register
}

第三步

# 需要用到的页面导入接口层api的方法
import {getxiaohua,register} from '../api/login/index.js'

  methods:{
	 async handle(){
		let res =  await getxiaohua()
    console.log(res.data);
	  },

   async zhuche(){
    let res = await register({
      username:'娃娃鱼-2023'
    })
    
    console.log(res);
    }
  }

13)element-ui image组件使用

图片预览功能(层级问题,组件的属性中有一个覆盖Body的层级属性)

初级版

#1. 注意图片地址必须有路径才能显示,可能需要手动拼接地址
#2. fit='cover' 表示图片自动裁剪,不会变形


高级版

# 考虑到服务器可能会更改地址,需要进行变量定义
#  没有头像图片时,默认显示默认头像

第一步: 在自定义的config文件夹下,创建index.js文件,导出头像地址

第二步:在需要的页面,进行引入使用


14)屏幕缩小时,顶部导航栏变形处理

设置最小宽度,宽度小于设置最小宽度时,出现滚动条(多试几个最小宽度值)


15)axios请求拦截与响应拦截(加载Loading配置)

import axios from 'axios'
//引入加载组件
import { Loading } from 'element-ui';
//创建axios实例
const instance = axios.create({
    baseURL: 'https://api.apiopen.top/api',
    timeout: 3000,
  });
  //定义变量
  let loading;
  //定义开始加载函数
function startLoading() {
  //创建loading实例
    loading = Loading.service({
        lock: true,
        text: "加载中...",
        background: 'rgba(0,0,0,0.8)'
    })
}
//定义关闭加载函数
function endLoading() {
    loading.close()
}
//添加请求拦截
  instance.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    //请求前开启加载函数
    startLoading()
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });
  // 添加响应拦截器
  instance.interceptors.response.use(function (response) {
  //对响应数据做点什么
  //响应之后延迟3秒触发关闭加载函数
  setTimeout(() => {
    endLoading()
  }, 3000);
  return response;
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error);
});

16) element-ui upload 上传文件(注意样式的坑 删除掉.el-upload)

# dom 结构
 <el-upload
  class="avatar-uploader"
  //真实的上传服务器地址
  action="https://lianghj.top:8888/api/private/v1/upload"
  :show-file-list="false"
  //上传成功时的回调函数
   :on-success="handleAvatarSuccess"
  //上传中的回调函数
   :on-progress="onProgress"
  //上传前的回调函数
  :before-upload="beforeAvatarUpload">
  <img v-if="imageUrl" :src="imageUrl" class="avatar">
  <i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>


# js内容

 data() {
    return {
      imageUrl: "",
    };
  },
  methods: {
    //上传成功触发的函数
    handleAvatarSuccess(res, file) {
      this.imageUrl = URL.createObjectURL(file.raw);
      setTimeout(() => {
        //myplgin给VUE添加的成功消息提示信息
        this.$scuessMessage();
         //myplgin给VUE添加的结束Loading加载函数
        this.$endLoading();
      }, 3000);
    },
    //上传前触发的函数
    beforeAvatarUpload(file) {
      const isJPG = file.type === "image/jpeg";
      const isLt2M = file.size / 1024 / 1024 < 2;

      if (!isJPG) {
        this.$message.error("上传头像图片只能是 JPG 格式!");
      }
      if (!isLt2M) {
        this.$message.error("上传头像图片大小不能超过 2MB!");
      }
      return isJPG && isLt2M;
    },
    //上传中的触发函数
    onProgress() {
      //myplgin给VUE添加的Loading开始加载函数
      this.$startLoading();
    },
  },

17)elment-ui Table表格组件基本使用

第一种

 	// 属性说明
	1. :data 关联定义的数组数据
    2.  prop 直接展示数组数据中的字段
    3.  lable 表格的头部标题
    4.  :sortable 是否开启排序
    <el-table
    :data="tableData"   
     class="table"  border style="width: 51%">
      <el-table-column type="selection" width="50"></el-table-column>
      <el-table-column prop="num" label="序号" width="80"></el-table-column>
      <el-table-column
        prop="createDate"
        :sort-method="sortmethod"
        label="日期"
        width="125"
        :sortable="true"
      ></el-table-column>
      <el-table-column
        prop="optUserName"
        label="姓名"
        width="120"
        :sortable="true"
      ></el-table-column>
    </el-table>
# scope.row 整行的数据
# scope.$index 当前行的索引

# 表格开头序号生成的两种方式
1.  <el-table>
type="index"
	</el-table>
2. <el-table>
	 <template slot-scope="scope">
       {{scope.$index}}
      </template>
	</el-table>
# 表格插入按钮
	<el-table>
	 <template slot-scope="scope">
       <el-button type="primary" icon="el-icon-edit" circle></el-button>
       <el-button type="danger" icon="el-icon-delete" circle></el-button>
      </template>
	</el-table>

# scope报错解决
    <template slot-scope="{}"> </template>

# dom 结构
  <el-table
    :data="tableData"
    size="mini"
    style="width: 100%">
    <el-table-column
      #//表头
      label="角色编号"
      width="180">
      <template slot-scope="scope">      
                                          #//相当于循环tableData中每一项的roleId 
        <span style="margin-left: 10px">{{ scope.row.roleId }}</span>
      </template>
    </el-table-column>
      <el-table-column
      label="角色名称"
      width="180">
      <template slot-scope="scope">
                                          #//相当于循环tableData中每一项的roleName 
        <span style="margin-left: 10px">{{ scope.row.roleName }}</span>
      </template>
    </el-table-column>

    <el-table-column label="操作">
      <template slot-scope="scope">
        <el-button
          size="mini"
          @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
        <el-button
          size="mini"
          type="danger"
          @click="handleDelete(scope.$index, scope.row)">删除</el-button>
      </template>
    </el-table-column>
  </el-table>

# js 代码
	
data() {
      return {
        #//表格数据
        tableData: [{
          roleId:1,
          roleName: '王小虎',
        }, 
        {
          roleId:2,
          roleName: '张三',
        }, 
        {
          roleId:3,
          roleName: '李四',
        }
        ],
      }
    },
    methods: {
      #//编辑函数
      handleEdit(index, row) {
        console.log(index, row);
      },
      #//删除函数
      handleDelete(index, row) {
        console.log(index, row);
      },
    }

18)element-ui from表单组件基本使用

# dom 结构

 <el-form
      :model="ruleForm"
      status-icon
	  #//验证规则
      :rules="rules"
      ref="ruleForm"
      label-width="100px"
      class="demo-ruleForm"
    > 
                                 #//验证字段admin  不写prop无法进行验证
      <el-form-item label="账号" prop="admin">
        <el-input
          type="text"
          v-model="ruleForm.admin"
          autocomplete="off"
        ></el-input>
      </el-form-item>
									 #//验证字段checkPass 不写prop无法进行验证
      <el-form-item label="确认密码" prop="checkPass">
        <el-input
          type="password"
          v-model="ruleForm.checkPass"
          autocomplete="off"
        ></el-input>
      </el-form-item>

      <el-form-item>
        <el-button type="primary" @click="submitForm('ruleForm')"
          >提交</el-button
        >
        <el-button @click="resetForm('ruleForm')">重置</el-button>
      </el-form-item>
    </el-form>

# js 代码
	
	 data() {
         //验证账号规则
    var validateAdmin = (rule, value, callback) => {
      if (!value) {
        return callback(new Error("账号不能为空"));
      } else {
        callback();
      }
    }; 
         //验证密码规则
    var validatePass = (rule, value, callback) => {
      if (value === "") {
        callback(new Error("请输入密码"));
      } else {
        callback();
      }
    };

    return {
        #//表单数据
      ruleForm: {
        admin: "",
        checkPass: "",
      },
        #//验证表单规则
      rules: {
 #//验证字段admin   验证方法为validateAdmin    验证方式为失焦验证    
        admin: [{ validator: validateAdmin, trigger: "blur" }],
 #//验证字段checkPass   验证方法为validatePass    验证方式为失焦验证           
        checkPass: [{ validator: validatePass, trigger: "blur" }],
      },
    };
  },
  methods: {
      #//提交表单方法(会自动执行设置的验证规则方法,全部通过才走if)
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          alert("submit!");
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
     #//表单重置方法(清空表单内容)   
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
  },

19)element-ui drawer抽屉组件基本使用

  # dom 结构
  <el-drawer
  title="我是标题"
	#//是否展示抽屉
  :visible.sync="drawer"
	#//抽屉打开方向
  :direction="direction"
	#//关闭抽屉弹框
  :before-close="handleClose">
  <span>我来啦!</span>
</el-drawer>

 # js 代码
 
 data() {
    return {
        #//显示抽屉
      drawer: true,
        #//打开抽屉方向为==>从右至左
      direction: "rtl",
    };
  },
  methods: {
      #//关闭抽屉弹框(必须通过done()才可以关闭弹框)
    handleClose(done) {
      this.$confirm("确认关闭?")
        .then((_) => {
          done();
        })
        .catch((_) => {});
    },
  },

20)element-ui select 下拉框组件基本使用

# dom 结构
		#// clearable属性表示可清空选项
   <el-select clearable v-model="value" placeholder="请选择">
    <el-option
      v-for="item in options"
      :key="item.value"
      :label="item.label"
      :value="item.value">
    </el-option>
  </el-select>

# js 代码
 	
	data() {
      return {
        options: [{
          value: '选项1',
          label: '黄金糕'
        }, {
          value: '选项2',
          label: '双皮奶'
        }],
        value: ''
      }
    }

21)element-ui Pagination 分页功能)(表格与分页联动)

#   清楚了解每个字段的含义   

# 表格dom结构
	<el-table
	#//计算选定页数时,截取当前页的数据
    :data="tableData.slice((currentPage-1)*pagesize,currentPage*pagesize)"
    size="mini"
    style="width: 100%">
    <el-table-column
      label="角色编号"
      width="180">
      <template slot-scope="scope">                                    
        <span style="margin-left: 10px">{{ scope.row.roleId }}</span>
      </template>
    </el-table-column>
      <el-table-column
      label="角色名称"
      width="180">
      <template slot-scope="scope">
        <span style="margin-left: 10px">{{ scope.row.roleName }}</span>
      </template>
    </el-table-column>

    <el-table-column label="操作">
      <template slot-scope="scope">
        <el-button
          size="mini"
          @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
        <el-button
          size="mini"
          type="danger"
          @click="handleDelete(scope.$index, scope.row)">删除</el-button>
      </template>
    </el-table-column>
  </el-table>

# 分页dom结构
  <div class="block">
    <el-pagination
	    #//开启选中背景颜色
        background
        #//点击选中每页多少条事件
      @size-change="handleSizeChange"
		#//点击选中当前多少页事件
      @current-change="handleCurrentChange"
		#//默认选中第几页
      :current-page="currentPage"
		#// 多少条/页
      :page-sizes="[5,10, 15, 20]"
		#//每页展示多少条
      :page-size="pagesize"
		#// 自定义展示需要的样式(例如总条数,上一页图标,不要哪个就去掉)
      layout="total, sizes, prev, pager, next, jumper"
		#// 总条数等于表格数据的总长度
      :total="tableData.length">
    </el-pagination>
  </div>

# js 代码
	data() {
      return {
          #//默认选中页数为第一页
          currentPage: 1,
          #//每页展示5条数据
          pagesize:5,
          #//表格数据
        tableData: [{
          roleId:1,
          roleName: '王小虎',
        }, 
        {
          roleId:2,
          roleName: '张三',
        }, 
        {
          roleId:3,
          roleName: '李四',
        },
          {
          roleId:4,
          roleName: '博裕',
        },
          {
          roleId:5,
          roleName: '博闻',
        },
          {
          roleId:6,
          roleName: '博容',
        },
             {
          roleId:7,
          roleName: '博文',
        },             
        {
          roleId:8,
          roleName: '博艺',
        }, 
         {
          roleId:9,
          roleName: '博雅',
        }, 
         {
          roleId:10,
          roleName: '承德',
        }, 
         {
          roleId:11,
          roleName: '朝宗',
        }, 
         {
          roleId:12,
          roleName: '存志',
        }, 
         {
          roleId:13,
          roleName: '澹雅',
        }, 
         {
          roleId:14,
          roleName: '得韬',
        }, 
         {
          roleId:15,
          roleName: '德辉',
        }, 
         {
          roleId:16,
          roleName: '方旭',
        }, 
         {
          roleId:17,
          roleName: '飞轩',
        }, 
         {
          roleId:18,
          roleName: '芳华',
        }, 
         {
          roleId:19,
          roleName: '芳蕤',
        }, 
         {
          roleId:20,
          roleName: '芳苓',
        }, 
         {
          roleId:21,
          roleName: '芳蔼',
        }, 
         {
          roleId:22,
          roleName: '芳菲',
        }, 
         {
          roleId:23,
          roleName: '管彤',
        }, 
         {
          roleId:24,
          roleName: '和畅',
        }, 
         {
          roleId:25,
          roleName: '涵畅',
        }, 
         {
          roleId:26,
          roleName: '涵涵',
        },       
    
        ],
      }
    },
    methods: {
   
      handleEdit(index, row) {
        console.log(index, row);
      },

      handleDelete(index, row) {
        console.log(index, row);
      },
       #// 每页/条 事件
       handleSizeChange(val) {
        console.log(`每页 ${val} 条`);
        this.pagesize=val
      },
       #// 当前选中第几页
      handleCurrentChange(val) {
        console.log(`当前页: ${val}`);
        this.currentPage=val
      }
    }

22)动态修改VUE网页标题

const routes = [
  {
    path: '/home',
    //路由元信息
    meta:{title:'首页'},
    component: () => import( '../views/Home.vue'),
  },
  {
    path: '/detail',
    //路由元信息
    meta:{title:'详情页'},
    component: () => import( '../views/Detail.vue'),
  },
]

 router.beforeEach((to,from,next)=>{
      #//进行判断是否有meta字段和title
      if(to.meta && to.meta.title){
      #//设置网站标题
        document.title=to.meta.title
      }
      next()
  })

23)element-ui 富文本框基本使用(富文本框可以上传图片

# 首先先在plugin文件夹下的myplugin.js文件下引入vue-quill-editor和样式
#在main.js文件中进行 导入整个myplugin.js文件,然后vue进行注册

# myplugin.js文件
import Vue from 'vue'
import VueQuillEditor from 'vue-quill-editor'
Vue.use(VueQuillEditor)
// require styles
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'

# main.js文件
import  './plugin/myplugin';
# dom结构
<quill-editor v-model="content" ref="myQuillEditor" style="height: 200px"> 
</quill-editor>

# js 代码
data(){
    return{
        content:''
    }
}

24) 组件内修改elementui样式(必须有一个根节点,否则不生效)

 #使用deep会报错 
::v-deep .el-dialog__title{
    margin-left: 600px;
  }
# 或者
  ::v-deep {
    .el-dialog__title {
      margin-left: 600px;
       color: #fff;
    }
  .el-dialog__header{
      background-color: #d9001b;
    }
  }

###### vue3中 必须   :deep(.el-table th.el-table__cell) {
      background-color: #f2f2f2;
    }

  

25)vue2导出excel表格功能(获取使用xlsx插件)

1. 安装依赖

npm install vue-json-excel  或  yarn add vue-json-excel

2. 引入组件

1.全局引入
 import Vue from 'vue'
import JsonExcel from 'vue-json-excel'

Vue.component('downloadExcel', JsonExcel)

2.局部引入
import JsonExcel from "vue-json-excel";

components: {
    DownloadExcel: JsonExcel,
  },

3. 在模版中使用

<download-excel
          class="export-btn"
          :data="tableData"
          :fields="jsonFields"
          type="xls"
          header="患者列表"
          name="患者列表.xls"
        >
          导出
        </download-excel>

#参数说明
name=“患者列表”. ------------------导出Excel文件的文件名
header="患者列表" ------------------ 这是个excel的头部
:fields = “jsonFields” ------------------Excel中表头的名称(里面的属性是excel表每一列的title,用多个词组组成的属性名(中间有空格的)要加双引号; 指定接口的json内某些数据下载,若不指定,默认导出全部数据中心全部字段)
:data = “tableData”. -------------------导出的数据
type="xls" -------------------导出Excel的文件类型,默认为xls

4.Excel表格表头的设置

export default{
   data(){
       return{
          jsonFields: {  //导出Excel表格的表头设置
              '序号': 'type',
              '姓名': 'userName',
              '年龄': 'age',
              '手机号': 'phone',
              '注册时间': 'createTime',
          },
       }
    }
 }

5.Excel表格中的数据

export default{
   data(){
       return{
          tableData:[
            {"userName":"张三","age":18,"gender":"phone":15612345612,"createTime":"2019-10-22"},
            {"userName":"李四","age":17,"gender":"phone":15612345613,"createTime":"2019-10-23"},
            {"userName":"王五","age":19,"gender":"phone":15612345615,"createTime":"2019-10-25"},
            {"userName":"赵六","age":18,"gender":"phone":15612345618,"createTime":"2019-10-15"},     
          ]
       }
   }
}

26)DatePicker日期控件

处理时间格式

    <el-date-picker
      format="yyyy 年 MM 月 dd 日"  #日期控件显示的格式
      value-format="yyyy-MM-dd">   #data数据显示的格式	
    </el-date-picker>

27)确认删除弹框提示

调用element-ui 对话框方法,将要操作的行为,放在.then中


28)对话框Dialog 嵌套表单


29)封装消息提示

//消息提示封装
import { Message } from 'element-ui';
function message_e(message){
   Message.warning({
    message: message,
  })
}
function message_c(message){
      Message.success({
        message: message,
      })
}
export{message_e,message_c}

30)日期控件基本使用

给日期控件添加  value-format="yyyy-MM-dd" 属性   在change事件中可获取到 2022-10-31 格式的值


31)vue自带加载提示

    <div id="boxs"
  v-loading="sendLoading"   #true 打开加载, false关闭加载
    element-loading-text="网络加载中"
    element-loading-spinner="el-icon-loading"
   >

32) 去除menu菜单栏白边

    .el-menu {
      border-right: 0;
    }

33) menu菜单激活背景颜色修改

    .el-menu-item.is-active {
  background-color: #2674c5 !important;
     }

34)element中的方法是组件自带的(不可以使用错误的@+方法名)

// 正确使用组件的方法
# 举例 清除全选的方法
1.给table组件 挂载dom   <el-table ref="table"></el-table>
2.获取dom  const table = ref();
3.使用相应的方法  table.value.clearSelection();

35) vue3 导出excel表格功能

下载插件(0.17.0版本,过高控制台报错)

yarn add xlsx 

项目引入

import XLSX from "xlsx";

封装函数

/*
    * @description:
    * @param {Object} json 服务端发过来的数据
    * @param {String} name 导出Excel文件名字
   
    * @param {String} titleArr 导出Excel表头
   
    * @param {String} sheetName 导出sheetName名字
    * @return:
    */
import XLSX from "xlsx";
let exportExcel = function(json, name, titleArr, sheetName) {
    /* convert state to workbook */
    var data = new Array();
    var keyArray = new Array();
    const getLength = function (obj) {
      var count = 0;
      for (var i in obj) {
        if (obj.hasOwnProperty(i)) {
        count++;
        }
      }
    return count;
    };
    for (const key1 in json) {
    if (json.hasOwnProperty(key1)) {
      const element = json[key1];
      var rowDataArray = new Array();
      for (const key2 in element) {
      if (element.hasOwnProperty(key2)) {
        const element2 = element[key2];
        rowDataArray.push(element2);
        if (keyArray.length < getLength(element)) {
        keyArray.push(key2);
        }
      }
      }
      data.push(rowDataArray);
    }
    }
    // keyArray为英文字段表头
    data.splice(0, 0, keyArray, titleArr);
    console.log('data', data);
    const ws = XLSX.utils.aoa_to_sheet(data);
    const wb = XLSX.utils.book_new();
    // 此处隐藏英文字段表头
    var wsrows = [{ hidden: true }];
    ws['!rows'] = wsrows; // ws - worksheet
    XLSX.utils.book_append_sheet(wb, ws, sheetName);
    /* generate file and send to client */
    XLSX.writeFile(wb, name + '.xlsx');
  }
  export {exportExcel}

导入使用

import {exportExcel} from '../utils/excel.js'
import { ref } from "vue";
const tableData = ref([
  {
    userName: "张三",
    age: 18,
    gender: "男",
    phone: 15612345612,
    createTime: "2019-10-22",
  },
  {
    userName: "李四",
    age: 17,
    gender: "男",
    phone: 15612345613,
    createTime: "2019-10-23",
  },
  {
    userName: "王五",
    age: 19,
    gender: "男",
    phone: 15612345615,
    createTime: "2019-10-25",
  },
  {
    userName: "赵六",
    age: 18,
    gender: "男",
    phone: 15612345618,
    createTime: "2019-10-15",
  },
]);
const titleArr =ref(['姓名','年龄','性别','电话','创建时间']) //表头中文名
exportExcel(tableData.value, '表格数据', titleArr.value, 'sheetName');

36)vue3图片下载功能

const downloadIamge = (imgsrc,name)=>{
var image = new Image();
				// 解决跨域 Canvas 污染问题
				image.setAttribute("crossOrigin", "anonymous");
				image.onload = function() {
					var canvas = document.createElement("canvas");
					canvas.width = image.width;
					canvas.height = image.height;
					var context = canvas.getContext("2d");
					context.drawImage(image, 0, 0, image.width, image.height);
					var url = canvas.toDataURL("image/png"); //得到图片的base64编码数据
					var a = document.createElement("a"); // 生成一个a元素
					var event = new MouseEvent("click"); // 创建一个单击事件
					a.download = name || "photo"; // 设置图片名称
					a.href = url; // 将生成的URL设置为a.href属性
					a.dispatchEvent(event); // 触发a的单击事件
				};
				image.src = imgsrc;
}
const downs = (url,name)=>{
imgUrl.value = url;
    // 调用下载函数  传入图片的服务器地址和图片名字
downloadIamge(imgUrl.value,name)
}

37 element 表格全选与非全选(批量删除,新增,修改等功能)

### 究极大坑
// 不要使用其他事件  使用@selection-change = "selectChange" 即可

38) 登录流程和权限划分

1.将登录成功后的token存储在sotrge中,用户跳转页面时,作为是否登录的标志,如果storge中存在token,表示已登录了

2.将系统所有页面,分为两类(1.需要登录才可以访问,2.不需要登录直接访问)

3.前端在每次跳转时,需要做出如下判断

    1. 前往登录页面时

      1.如果已经登陆过,禁止访问登录页

      2.如果没有登录,直接跳转登录页

      ​ 1.验证用户输入的账号密码,如果成功则往storge中存储token

    2. 前往非登录页时

      1.用户无需登录就能访问的页面

      ​ 1.直接跳转

      2.用户需要登录才能访问的页面

      ​ 1.如果用户还没有登录

      ​ 1.进入登录页

      ​ 2.如果用户已经登录(进行权限区分)

      ​ 1.如果用户拥有此权限则正常访问

      ​ 2.如果用户没有权限,则提示用户无权限


相关问题

Q1:在vue-router的beforEach方法进行逻辑处理时,页面跳转死循环永远空白?
A1:beforEach方法内的next并不会阻止代码继续执行,如果逻辑出现偏差,会导致next无限循环,从而出现白屏报错,对每一个next进行指定的if判断同时next后使用return终止代码执行,防止next无限循环

Q2:既然你是通过storage存储的信息,那么用户手动修改storage,不就可以访问了吗?
A2:
	1.在beforEach内每次跳转时,请求一个判断用户是否登录的简易接口,如果当前后台返回用户正常信息,则代表用户已经登录,走流程。
	弊端:每次跳转都要请求接口,会导致跳转卡顿,影响用户体验。

	2.接口进行统一封装,所有接口都会携带用户token,针对必须登录才可以访问的接口,后台会去判断用户token是否失效,如果失效,此几口会返回用户未登录(token过去)。在封装内会对信息先进行一层处理,如果发现返回信息是用户未登录,则统一提示用户,并且返回登录页

main.js相关代码

1.和APP.vue同级别目录创建permisson.js文件

// 用于处理权限
import Store from "@/store";
import router from "@/router";
const routes = [
  {
    path: "aaa",
    name: "aaa",
    component: () => import("./views/AsdView.vue"),
  },
  {
    path: "ddd",
    name: "ddd",
    component: () => import("./views/BbbView.vue"),
  },
];
router.beforeEach((to, from, next) => {
  const token = JSON.parse(localStorage.getItem("token"));
  if (token && to.name == "login") next({ name: "home" });
  if (!token && to.name !== "login") next({ name: "login" });
  // 动态路由加载
  if (token && Store.state.router.length === 0) {
    Store.dispatch("fncgetrouters", ["aaa"]).then(() => {
      // 权限集合 根据后端返回的信息 筛选出全部符合的页面(此处是模拟传递的['aaa'],实际应传递token 获取路由name字段表)
      console.log(Store.state.router);
      const routerRule = routes.filter((item) =>
        Store.state.router.includes(item.name)
      );
      routerRule.forEach((item) => {
        /**
         * addRoute(参数1.参数2)
         * 参数1:父路由的name,如果不写代表当前路由是1级路由
         * 参数2:路由信息
         */
        router.addRoute("home", item);
      });
      // 无权限页面
      router.addRoute("home", {
        path: "*",
        name: "403",
        component: () => import("./views/403View.vue"),
      });
      next();
      return;
    });
  }
  next();
});
export default router;

2. main.js文件

import router from './permisson'  // 引入权限路由js文件

3.rouer/index.js文件

import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
const routes = [
  {
    path: "/",
    name: "home",
    component: () => import("../views/HomeView.vue"),
    children: [],
  },
  {
    path: "/login",
    name: "login",
    component: () => import("../views/LoginView.vue"),
  },
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});

export default router;

4.Store/index.js文件

import Vue from 'vue'
import Vuex from 'vuex'
// 获取路由实例
import router from '@/router'
console.log(router);
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    router:[]
  },
  getters: {
  },
  mutations: {
    changerouter(state,data){
      state.router = data
    }
  },
  actions: {
    // 请求数据获取到路由name字段表
   fncgetrouters({commit},token){
      commit('changerouter',token)
    }
    //伪代码
    /**  if(data.code == 200){
      commit('changerouter',token)
      return;
    }
     用户手动修改数据信息 登录过期
    localStorage.clear()
    router.push({name:'login})
    
    */
  },
  modules: {
  }
})

39) 富文本上传图片

      <!-- 富文本 -->
      <div class="quill-editor">
        <!-- 图片上传组件辅助-->
        <el-upload
          class="avatar-uploader"
          :action="uploadUrl"
          name="editorfm"
          :show-file-list="false"
          :on-success="uploadSuccess"
          :before-upload="beforeUpload"
        >
        </el-upload>
        <!--富文本编辑器组件-->
        <quill-editor
          v-model="content"
          :content="content"
          :options="editorOption"
          @blur="onEditorBlur($event)"
          @focus="onEditorFocus($event)"
          @ready="onEditorReady($event)"
          ref="QuillEditor"
          style="
            width: 10.6125rem;
            height: 3.1125rem;
            margin-left: 3.7175rem;
            margin-top: 0.25rem;
          "
        >
        </quill-editor>
      </div>

	  <script>
              const toolbarOptions = [
  ["bold", "italic", "underline", "strike"], // 加粗,斜体,下划线,删除线
  ["blockquote", "code-block"], //引用,代码块
  [{ header: 1 }, { header: 2 }], // 几级标题
  [{ list: "ordered" }, { list: "bullet" }], // 有序列表,无序列表
  [{ script: "sub" }, { script: "super" }], // 下角标,上角标
  [{ indent: "-1" }, { indent: "+1" }], // 缩进
  [{ direction: "rtl" }], // 文字输入方向
  [{ size: ["small", false, "large", "huge"] }], // 字体大小
  [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
  [{ color: [] }, { background: [] }], // 颜色选择
  [
    {
      font: [
        "SimSun",
        "SimHei",
        "Microsoft-YaHei",
        "KaiTi",
        "FangSong",
        "Arial",
      ],
    },
  ], // 字体
  [{ align: [] }], // 居中
  ["clean"], // 清除样式,
  ["link", "image"], // 上传图片、上传视频
];
           data(){
              return{
                  uploadUrl: `/api/editor_upload.php`, // 服务器上传地址
                  editorOption: {
        placeholder: "请在这里输入",
        theme: "snow", //主题 snow/bubble
        modules: {
          history: {
            delay: 1000,
            maxStack: 50,
            userOnly: false,
          },
          toolbar: {
            container: toolbarOptions,
            handlers: {
              image: function (value) {
                if (value) {
                  // 调用element的图片上传组件
                  document.querySelector(".avatar-uploader input").click();
                } else {
                  this.quill.format("image", false);
                }
              },
            },
          },
        },
      },
                  methods:{
                          uploadSuccess(res) {
      // 获取富文本组件实例
      let quill = this.$refs.QuillEditor.quill;
      // 如果上传成功
      if (res) {
        this.loading = false
        console.log(res);
        // 获取光标所在位置
        let length = quill.getSelection().index;
        // 插入图片,res为服务器返回的图片链接地址
        quill.insertEmbed(length, "image", res.data.filepath);
        // 调整光标到最后
        quill.setSelection(length + 1);
      } else {
        // 提示信息,需引入Message
        this.$message.error("图片插入失败!");
      }
    },
                  }
              }
          }   
        
      </script>

40)按需引入element-ui组件

1. 下载element-ui 和 babel-plugin-component

yarn add element-ui     yarn add babel-plugin-component

2.在babel.config.js文件进行如下配置

module.exports = {
  
  presets: [
    '@vue/cli-plugin-babel/preset',
    ["@babel/preset-env", { "modules": false }]
  ],
  plugins: [
    [
      "component",
      {
        libraryName: "element-ui",
        styleLibraryName: "theme-chalk"
      }
    ]
  ]
  
}

3.建立 /src/components/element/index.js js示例代码如下

import {
    Button,
    Input,
    Radio,
    Table,
    Form
  } from 'element-ui'
  
  const coms = [
    Button, 
    Input, 
    Radio, 
    Table,
    Form
  ]
  
  export default {
    install(Vue){
      coms.map(c => {
        Vue.component(c.name, c)
      })
    }
  }

4. main.js中引入局部插件并使用

import 'element-ui/lib/theme-chalk/index.css'
//引入 element 插件
import element from './components/element'
Vue.use(element)

按需引入使用 message消息提示 和 messageBox 弹框

import { Message,MessageBox  } from 'element-ui';

// 使用
Message.success('成功提示');
Message.warning('警告提示');
Message.error('错误提示');
====================
// 使用
MessageBox.confirm('确定要删除吗?', '提示', {
  confirmButtonText: '确定',
  cancelButtonText: '取消',
  type: 'warning'
}).then(() => {
  // 确认操作
}).catch(() => {
  // 取消操作
})    

41) ...省略 + 展开功能

<template>
  <div id="box">
    <div>
      <div class="dd" v-if="!isExpanded">
        {{ content.slice(0, maxLength) }}
        {{ content.length > maxLength ? "..." : "" }}
        <button @click="isExpanded = true">展开</button>
      </div>
      <div class="dd" v-else>
        {{ content }}
        <button @click="isExpanded = false">收起</button>
      </div>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isExpanded: false,
      content: "231654654654asd撒大苏打",
      maxLength: 6,  // 超过6个字符长度  ...
    };
  },
  methods: {},
};
</script>

<style lang="scss" scoped>
.ebox {
  width: 600px;
  height: 600px;
}
button {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 14px;
  color: blue;
  text-decoration: underline;
}
</style>


42 el-input 修改高度和图标位置

::v-deep .el-input__inner {
  height: 0.4rem;
}
::v-deep .el-input__inner {
  padding: 0 0.375rem;
}
::v-deep .el-input__icon {
  line-height: 100%;
}

43) el-button 修改样式

      ### padding 设为0
.btn{
            height: 0.4rem;
      width: .825rem;
      padding: 0;
      }

44) menu菜单栏与路由事实联动

利用计算属性 实时监听vuex的改变

<el-menu
          :default-active.sync="currentActive"
</el-menu>

computed:{
  currentActive(){
return this.$store.state.path;
  }
}

45)前端权限控制的意义

1. 降低非法操作的可能性
2. 尽可能排除不必要请求,减轻服务器压力
3. 提高用户体验,只展示自己相关的界面

# 前端权限的控制本质上来说,就是控制前端的 (视图层的展示) 和 前端所发送的(请求) 

46)前端权限控制思路

1.菜单的控制

在登录请求中,会得到权限数据,根据权限数据,展示对应的菜单,点击菜单,才能查看相关的界面。
# 登录成功将菜单栏数据 存储在vuex  然后 在菜单栏页面获取,进行渲染
  • 返回的数据结构

  • 刷新菜单栏数据丢失问题(将sessionStorage和vuex同步)

  • 退出登录时,清空sessionStorage 并跳转到登录页 同时进行刷新操作清空vuex数据

2.界面的控制

如果用户没有登录,手动在地址栏敲入管理界面的地址,则需要跳转到登录界面。
如果用户已经登录,手动敲入非权限内的地址,则需要跳转404界面。
  • 登录成功后需要进行 路由守卫(未登录情况下,返回登录页) 和 动态路由(登录情况下,不具备权限页面的路由要删除掉,动态的进行路由配置)

    动态路由配置

    1. router.js

    两个地方调用 动态添加路由方法

3.按钮的控制

在某个菜单的界面中,还得根据权限数据,展示出可进行操作的按钮,比如删除,修改,增加。
  • 在动态路由中添加元信息(将页面的具体按钮相关权限,赋值给元信息)

  • 在utils创建permission.js(进行自定义指令注册)必须在main.js中导入

  • 在具体的页面,添加自定义指令

4.请求和响应的控制

如果用户通过非常规操作,比如通过浏览器调试工具将某些禁用的按钮变成启用状态,此时发送的请求,应该被拦截。

47) pc端屏幕适配自适应(使用建议width:auto,其他正常px单位)

1.安装适配插件

yarn add postcss-px-to-viewport 

2.项目根目录添加配置文件

postcss.config.js

3.添加配置

module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      unitToConvert: 'px', // 需要转换的单位,默认为"px"
      viewportWidth: 1920, // 设计稿的视口宽度
      unitPrecision: 5, // 单位转换后保留的精度
      propList: ['*'], // 能转化为vw的属性列表
      viewportUnit: 'vw', // 希望使用的视口单位
      fontViewportUnit: 'vw', // 字体使用的视口单位
      selectorBlackList: [], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
      minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
      mediaQuery: false, // 媒体查询里的单位是否需要转换单位
      replace: true, //  是否直接更换属性值,而不添加备用属性
      exclude: undefined, // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
      include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换
      landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
      landscapeUnit: 'vw', // 横屏时使用的单位
      landscapeWidth: 1920 // 横屏时使用的视口宽度
    }
  }
}
posted @ 2023-08-08 15:44  我亦无他_唯手熟尔  阅读(1)  评论(0)    收藏  举报