后台管理系统的模块化开发:vue2+vueRouter+Element-ui+axios
前端前辈大大的要求:
一、项目准备
vue create 文件名
安装axios: npm install axios
安装element-ui:
element-ui按需导入:
安装 babel-plugin-component:npm install babel-plugin-component -D
按需引入在main.js中导入需要的组件即可:
babel报错:
将es2015修改为@babel/preset-env重启即可
最后删除不必要的代码和文件,再配置一下main.js
二、前端模拟后端服务:json-sevre
在任意文件夹下创建一个json类型的数据文件:
打开终端,输入命令json-server --watch db.json
启动服务
没有安装的用npm安装一下:npm install -g json-server
查看版本号: json-server -v
三、axios的封装和api的统一管理
1.request.js
import axios from 'axios';
const service = axios.create({
baseURL: 'http://120.79.56.27/rongtong/api/web',
timeout: 300 * 1000,
headers: {
'Content-Type': 'application/json' // 设置正确的 Content-Type
}
});
// 请求拦截
service.interceptors.request.use((config) => {
config.headers = config.headers || {}
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = token
}
return config
})
// 响应拦截
service.interceptors.response.use(
(res) => {
// 在请求成功后对响应数据做处理
// console.log(res.data);
if (res.status >= 200 && res.status < 300) {
return res;
}
},
(err) => {
// 对响应错误做些什么
if(err.response.status){
switch (err.response.status) {
case 400:
// 处理错误信息,例如抛出错误信息提示,或者跳转页面等处理方式。
err.message = "错误请求";
break;
case 401:
err.message = "未授权,请重新登录";
break;
case 403:
err.message = "拒绝访问";
break;
case 404:
err.message = "请求错误,未找到该资源!!!!";
alert(err.message)
break;
case 405:
err.message = "请求方法未允许";
break;
case 408:
err.message = "请求超时";
break;
case 500:
err.message = "服务器端出错";
break;
case 501:
err.message = "网络未实现";
break;
case 502:
err.message = "网络错误";
break;
case 503:
err.message = "服务不可用";
break;
case 504:
err.message = "网络超时";
break;
case 505:
err.message = "http版本不支持该请求";
break;
default:
err.message = `连接错误${err.response.status}`;
}
}
return Promise.reject(err); // 在响应错误的时候的逻辑处理
}
);
export default {
get(url, params) {
return service.get(url, {
params: params,
});
},
downLoad(url, params) {
return service.get(url, {
params: params,
responseType:'blob'
});
},
post(url, data,config) {
return service.post(url, data,config);
},
delete(url, data) {
return service.delete(url, data);
},
put(url, data) {
return service.put(url, data);
},
};
export default {
get(url, params) {
return service.get(url, {
params: params,
});
},
post(url, data,config) {
return service.post(url, data,config);
},
delete(url, data) {
return service.delete(url, data);
},
put(url, data) {
return service.put(url, data);
},
};
响应拦截器成功返回的状态一定不能===200,2开头返回的都是成功的
2.api统一管理
使用
四、后台框架:element-ui + <router-view>
匹配路由
使用element-ui的布局容器布局:
<router-view>
放在main的位置
home.vue:
二级路由配置:
五、封装侧边栏(结合el-menu进行二次封装)
在页面home.vue引入了组件slideBar,由于slideBar侧边栏里的菜单项的条目和层级数是不确定的,我们进行了二次封装使菜单项根据传入的数据递归渲染
1.slideBar.vue:
slideBar是父组件,slideBarItem是子组件,父亲传入具体的barList值给儿子进行渲染
<template>
<div>
<el-menu
:default-active="activeIndex"
class="el-menu-vertical-demo"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b" router>
<slide-bar-item :barList="barList"></slide-bar-item>
</el-menu>
</div>
</template>
<script>
import slideBarItem from './slideBarItem.vue';
export default {
components:{
slideBarItem
},
data(){
return{
activeIndex: '',
barList:[
{
id:1,
label:'老大',
link:'/menu1',
children:[
{
id:2,
label:'老大的大儿子',
link:'/submenu1',
address:"2-1",
children:[]
},
{
id:3,
label:'老大的二儿子',
link:'/submenu2',
address:"2-2"
}
]
},
{
id:4,
label:'老二',
link:'/menu2'
},
{
id:5,
label:'老三',
link:"/menu3"
}
],
}
},
props:{
},
methods: {
}
}
</script>
<style>
</style>
2.slideBarItem.vue
在子组件中,不光要接收父组件传过来的barList值,同时在模板中调用自身(也需要引入注册和使用),通过v-if和v-else判断是否具有子项进行条件渲染,使用
?.
语法,如果对象的属性值不存在,这个表达式将返回undefined,不会抛出错误
vue中用template标签包裹循环渲染列表
在 Vue 中使用 标签而不是
<div>
标签来包裹和循环渲染列表有几个好处。主要原因是<template>
标签作为模板点位符,可帮助我们包裹元素,但在循环过程中 template 标签不会被渲染到页面,使用template而不是div标签,不会在最终的渲染中生成额外的 DOM 元素,因此可以保持 DOM 结构的简洁和干净。
<template>
<div>
<template v-for="(item) in barList" >
<el-submenu :index="item.link" v-if="item?.children?.length > 0" :key="item.id" >
<template slot="title">
<i class="el-icon-location"></i>
<span>{{ item.label }}</span>
</template>
<slide-bar-item :barList="item.children"></slide-bar-item>
</el-submenu>
<div v-else :key="item.id">
<el-menu-item :index="item.link" :key="item.id">
<i class="el-icon-menu"></i>
<span slot="title">{{ item.label }}</span>
</el-menu-item>
</div>
</template>
</div>
</template>
<script>
import slideBarItem from './slideBarItem'
export default {
name:'slide-bar-item',
props: {
barList: {
type: Array,
required: true,
default: () => {
return []
}
}
},
components:{
slideBarItem
}
}
</script>
<style></style>
六、封装el-dialogue
新增和编辑快报dialogue框需要出现两次,所以想到了封装复用,当时还在思考怎么传数据不会冲突呢,佬儿二十分钟支招搞定了
1.子组件初始数据
直接在子组件中定义好控制表单显示和隐藏的变量以及表单数据
2.定义open()方法
区分是编辑还是新增,以及修改dialogue可见的布尔值,这样就能通过父组件传过来的值呈现不同内容的表单;在编辑情况下,表单会有默认的数据呈现,所以需要获取到用户点击父组件编辑的那一行代码,需要传值,而新增默认为空的数据,取默认form里面的数据为初始数据,不需要传值,所以我们可用根据父组件是否传值的特点进行区分是编辑还是新增快报;
父组件点击新增:
父组件点击编辑:
3.ref和$refs
父组件通过$refs调用子组件的open方法并传值
4.效果
七、组件传值
1.Dom更新机制问题
点击编辑弹框内出现富文本的的默认内容时出了问题,发现不管使用了props、watch、本地存储还是store,父组件传值后子组件都不能动态接收,修改Open方法后得到了解决
修改前:
修改后:
原因是因为this.dialogFormVisible(控制表格的显示和隐藏,以及自组件的渲染)
放在if语句的外面,可能导致表单的数据还没有合并子组件就已经渲染出来了,富文本子组件在form数据没有更新之前就被渲染,没有接收到最新的数据
2.this.$nextTick()
在这个问题解决过程中,使用nextTick方法将对话框的可见性延迟到下一次DOM更新循环之后,确保对话框显示时,form.content传给子组件的数据已经被正确更新
3.子组件动态接收父组件props并实时渲染
用vulue接收
wach深度监听(这里其实普通监听一下就可以了):
4.this.$options.data()
重置data里面的数据,新增时打开弹窗需要清理
八、登录后请求头带token
登录成功后将token存入本地
在请求拦截器中添加token
其他
1.event-bus事件
event-bus只对组件和组件之间有效,我敲得时候想让一个非父子的页面给一个组件传值失败了,问了佬儿后才知道原来不能传,传值可以用路由传参,我用的是vuex,也可以,复习一下路由传参……应该还有其他方法
2.store模块化取值
store模块化过后通过$store.state.模块化名.属性名进行访问
3.定义提交方法
1.新增:判断对象的属性值中是否包含空值:
Object.values(tableItem).includes('')
;2.编辑修改:通知父组件修改,map方法可以修改原数组中的元素
this.tableData.map(e => { … }):map 方法会遍历数组中的每一个元素,并返回一个新数组。
if (e.id === form.id) { return { …e, …form }; }:如果找到匹配的元素,通过对象展开运算符
将原对象和新对象合并,生成一个新的对象。
return e;:如果没有匹配的元素,返回原对象。
4.获取当前时间
格式:如2024-8-11 15:34:30
5.vue2使用富文本编辑器vue-quill-editor
参考:http://t.csdnimg.cn/dOB6v
6.md5加密密码
安装 js-md5插件:npm install js -md5 -D
在页面中使用:import md5 from "js-md5"
7.下载文件
通过 url 下载文件,window.URL 对象可以为 Blob 对象生成一个网络地址 ,结合 a 标签的 download 属性,可以实现点击 url 下载文件;参考http://t.csdnimg.cn/PzZtg
const blob = new Blob([res.data]);
// 创建一个新的 Blob 对象,将来自服务器的响应数据(res.data)放入数组中传递给 Blob 构造函数。
// Blob 对象代表了一个不可变的、原始数据的类文件对象。
const url = window.URL.createObjectURL(blob);
// 使用 window.URL.createObjectURL() 方法创建一个临时的 URL,该 URL 指向之前创建的 Blob 对象。
// 这个 URL 可以用来在浏览器中访问 Blob 数据,例如,通过设置 <a> 标签的 href 属性来下载文件。
let a = document.createElement('a');
// 创建一个新的 <a>(锚)元素,该元素将用于触发下载。
a.href = url;
// 将 <a> 元素的 href 属性设置为之前创建的 Blob URL。
// 这意味着当用户点击这个 <a> 元素时,浏览器将尝试下载位于该 URL 的文件。
a.download = 'filename.xls';
// 设置 <a> 元素的 download 属性,定义下载文件的默认名称。
// 当用户点击链接时,浏览器会使用这个属性作为下载文件的名称。
document.body.appendChild(a);
// 将 <a> 元素添加到文档的 body 中。
// 这一步是必要的,因为一些浏览器要求 <a> 元素必须被添加到文档中才能触发点击事件。
a.click(); // 调用 click 方法
// 通过编程方式模拟点击 <a> 元素,从而触发下载。
// 这一步会使得浏览器开始下载位于 a.href 指定的 Blob 数据。
document.body.removeChild(a); // 立即移除 <a> 标签
// 从文档的 body 中移除 <a> 元素。
// 这一步是清理操作,因为下载一旦开始,<a> 元素就不再需要了。
window.URL.revokeObjectURL(url); // 清理创建的URL对象
// 调用 window.URL.revokeObjectURL() 方法来释放之前创建的 Blob URL。
// 这可以防止内存泄漏,因为不再需要的 URL 应该被清理掉。
需要在request.js中声明一下响应的文件类型
8.Object.assign()
row是一个对象,去除里面的content和available属性 详解http://t.csdnimg.cn/QACO5
这是我本来搜到的方法……
结果老大一句代码就搞定了啊哈哈:
不说了,去复习es6的方法了……
9.去除富文本包裹的p标签(正则表达式)
delTag(str) {
return str.replace(/<[^>]+>/g, "");
}