first commit

This commit is contained in:
userName 2024-08-23 11:07:11 +00:00
commit a9aa8da727
46 changed files with 14645 additions and 0 deletions

5
.editorconfig Normal file
View File

@ -0,0 +1,5 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

127
LICENSE Normal file
View File

@ -0,0 +1,127 @@
木兰宽松许可证, 第2版
木兰宽松许可证, 第2版
2020年1月 http://license.coscl.org.cn/MulanPSL2
您对“软件”的复制、使用、修改及分发受木兰宽松许可证第2版“本许可证”的如下条款的约束
0. 定义
“软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
“贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
“贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
“法人实体”是指提交贡献的机构及其“关联实体”。
“关联实体”是指对“本许可证”下的行为方而言控制、受控制或与其共同受控制的机构此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
1. 授予版权许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
2. 授予专利许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
3. 无商标许可
“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可但您为满足第4条规定的声明义务而必须使用除外。
4. 分发限制
您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
5. 免责声明与责任限制
“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
6. 语言
“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
条款结束
如何将木兰宽松许可证第2版应用到您的软件
如果您希望将木兰宽松许可证第2版应用到您的新软件为了方便接收者查阅建议您完成如下三步
1 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
2 请您在软件包的一级目录下创建以“LICENSE”为名的文件将整个许可证文本放入该文件中
3 请将如下声明文本放入每个源文件的头部注释中。
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
Mulan Permissive Software LicenseVersion 2
Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2)
January 2020 http://license.coscl.org.cn/MulanPSL2
Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
0. Definition
Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
Contribution means the copyrightable work licensed by a particular Contributor under this License.
Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
Legal Entity means the entity making a Contribution and all its Affiliates.
Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, control means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
1. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
2. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
3. No Trademark License
No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4.
4. Distribution Restriction
You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
5. Disclaimer of Warranty and Limitation of Liability
THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW ITS CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
6. Language
THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
END OF THE TERMS AND CONDITIONS
How to Apply the Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2) to Your Software
To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package;
iii Attach the statement to the appropriate annotated syntax at the beginning of each source file.
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.

24
README.md Normal file
View File

@ -0,0 +1,24 @@
# vue-erp-demo
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

318
doc/布局.html Normal file
View File

@ -0,0 +1,318 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模拟后台布局+路由使用</title>
<script src="https://unpkg.com/vue@2.6.13/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router@3.5.1/dist/vue-router.js"></script>
<style>
#root {
display: flex;
width: 100%;
}
#root .main {
width: 80%;
background: yellow;
font-size: 360px
}
#root div ul li {
padding: 0;
margin: 0;
list-style-type: none;
}
#root div a {
text-decoration: none;
}
</style>
</head>
<body>
<div id="root">
<!-- 菜单区域 -->
<menu-component :menus='menus'></menu-component>
<!-- 内容区域 -->
<main class="main">
<router-view></router-view>
</main>
</div>
<template id="menu-component">
<div>
<ul v-for="(item,index) in menus">
<router-link :to="item.path">
<li key="index">{{item.name}}</li>
<!-- <li key="index" v-if="!item.hidden">{{item.name}}</li> -->
</router-link>
<menu-component v-if="item.children" :menus="item.children"></menu-component>
</ul>
</div>
</template>
<script>
//创建router实例
let router = new VueRouter({
routes: [{
path: '/gansu',
component: {
template: '<div>甘肃省</div>'
}
}, {
path: '/lanzhou',
component: {
template: '<div>兰州市</div>'
}
}, {
path: '/chengguanqu',
component: {
template: '<div>城关区</div>'
}
}, {
path: '/anningqu',
component: {
template: '<div>安宁区</div>'
}
}, {
path: '/qilihequ',
component: {
template: '<div>七里河区</div>'
}
}, {
path: '/wuweishi',
component: {
template: '<div>武威市</div>'
}
}, {
path: '/liangzhouqu',
component: {
template: '<div>凉州区</div>'
}
}, {
path: '/minqinxian',
component: {
template: '<div>民勤县</div>'
}
}, {
path: '/gulangxian',
component: {
template: '<div>古浪县</div>'
}
}, {
path: '/tianshuishi',
component: {
template: '<div>天水市</div>'
}
}, {
path: '/qingyangshi',
component: {
template: '<div>庆阳市</div>'
}
}, {
path: '/shanxisheng',
component: {
template: '<div>陕西省</div>'
}
}, {
path: '/xianshi',
component: {
template: '<div>西安市</div>'
}
}, {
path: '/yulinshi',
component: {
template: '<div>榆林市</div>'
}
}, {
path: '/yananshi',
component: {
template: '<div>延安市</div>'
}
}, {
path: '/hubeisheng',
component: {
template: '<div>湖北省</div>'
}
}, {
path: '/wuhanshi',
component: {
template: '<div>武汉市</div>'
}
}, {
path: '/hongshanqu',
component: {
template: '<div>洪山区</div> '
}
}, {
path: '/hanyangqu',
component: {
template: '<div>汉阳区</div>'
}
}, {
path: '/wuchangqu',
component: {
template: '<div>武昌区</div>'
}
}, {
path: '/yichangshi',
component: {
template: '<div>宜昌市</div>'
}
}, {
path: '/xiangyangshi',
component: {
template: '<div>襄阳市</div>'
}
}, {
path: '/jingzhoushi ',
component: {
template: '<div>荆州市</div>'
}
}, {
path: '/hunansheng',
component: {
template: '<div>湖南省</div>'
}
}, {
path: '/changshashi',
component: {
template: '<div>长沙市</div>'
}
}, {
path: '/yongzhoushi',
component: {
template: '<div>永州市</div>'
}
}, {
path: '/huaihuashi',
component: {
template: '<div>怀化市</div>'
}
}, {
path: '/hengyangshi',
component: {
template: '<div>衡阳市</div>'
}
}]
})
//创建菜单组件
const MenuComponent = {
name: 'MenuComponent',
template: '#menu-component',
props: ['menus']
}
let vm = new Vue({
el: '#root',
router,
components: {
MenuComponent
},
data: {
menus: [{
name: '甘肃省',
path: 'gansu',
children: [{
name: '兰州市',
path: 'lanzhou',
children: [{
name: '城关区',
path: 'chengguanqu'
}, {
name: '安宁区',
path: 'anningqu',
}, {
name: '七里河区',
path: 'qilihequ'
}]
}, {
name: '武威市',
path: 'wuweishi',
children: [{
name: '凉州区',
path: 'liangzhouqu'
}, {
name: '民勤县',
path: 'minqinxian'
}, {
name: '古浪县',
path: 'gulangxian'
}]
}, {
name: '天水市',
path: 'tianshuishi'
}, {
name: '庆阳市',
path: 'qingyangshi'
}]
}, {
name: '陕西省',
path: 'shanxisheng',
children: [{
name: '西安市',
path: 'xianshi'
}, {
name: '榆林市',
path: 'yulinshi'
}, {
name: '延安市',
path: 'yananshi'
}, {
name: '咸阳市',
path: 'xianyangshi'
}]
}, {
name: '湖北省',
path: 'hubeisheng',
children: [{
name: '武汉市',
path: 'wuhanshi',
children: [{
name: '洪山区',
path: 'hongshanqu',
hidden: true
}, {
name: '汉阳区',
path: 'hanyangqu',
hidden: true
}, {
name: '武昌区',
path: 'wuchangqu',
hidden: true
}]
}, {
name: '宜昌市',
path: 'yichangshi'
}, {
name: '襄阳市',
path: 'xiangyangshi'
}, {
name: '荆州市',
path: 'jingzhoushi'
}]
}, {
name: '湖南省',
path: 'hunansheng',
children: [{
name: '长沙市',
path: 'changshashi'
}, {
name: '永州市',
path: 'yongzhoushi'
}, {
name: '怀化市',
path: 'huaihuashi'
}, {
name: '衡阳市',
path: 'hengyangshi'
}]
}]
}
})
</script>
</body>
</html>

239
doc/面试学习笔记1 Normal file
View File

@ -0,0 +1,239 @@
lession 1、插值表达式、指令、动态属性、v-html
{{name}}
<p :id="did"></p>
<div v-html="html">
<p>我是哈默</p>
</div>
v-html 会造成xss攻击
lession 2、计算属性
会做数据缓存
double2:{
get(){
return this.number*2 //原值改变,计算属性改变
},
set(value){
this.number=value/2 //计算属性改变,原值还原
}
}
lession 3、watch监听器-用来监听数值的变化
data(){
return{
name:'哈默',
info:{
hobby:'篮球'
}
}
}
watch:{
//字符串监听
name(newVal,oldVal){
console.log('name',newVal,oldVal);
},
//对象数据的监听
info:{
handler:function(newVal,oldVal){
console.log('info',newVal,oldVal)
},
deep:true //深度监听开关
}
}
lession 4、动态绑定 Class 和 Style
对象方式动态绑定样式
<li class="item" :class="{'active':isActive}">项目1</li>
数组方式绑定样式
<li class="item" :class="['active']">项目1</li>
行内样式动态绑定
<li class="item" :style="styleData">项目1</li>
data(){
return {
isActive:true,
styleData:{
background:'#efefef',
color:'#ffffff'
}
}
}
lession 5、v-if 和v-show 的区别
<p v-if="data==='a'"> a(v-if) </p>
<p v-show="data==='b'"> b(v-show) </p>
v-if 是直接决定页面元素是否存在
v-show是在样式级别控制元素是显示还是因此
lession 6、v-for 列表循环
数组遍历:
<li v-for="(item,index) in array" :key="item.id"></li>
对象遍历:
<li v-for="(value,key,index) in object" :key="key"></li>
object:{
name:'桃谷六仙'
hobby:'篮球'
}
v-if 和 v-for 联合使用
v-for 优先级比较高可以在v-for 的父元素写v-if判断
lession 7、事件
@click="logout(3,$event)"
logout(id,e){
alert(id)
console.log('e',e)
}
lession 8、父子组件通信方法prop $emit
通信方式总结:
父组件数据传递给子组件步骤:
1.父组件定义传递给儿子的数据
2.子组件用prop定义传递过来的数据的类型和变量的名称
3.父组件调用子组件,并进行属性值和变量名绑定
4.子组件调用父组件传递过来的数据
子组件给父组件传递数据的步骤:
1.子组件定义传递给父组件的数据对象和事件
2.父组件调用子组件,并且在调用标签绑定子组件传递过来的事件名称
3.在父组件的事件处理函数中接受子组件传递的数据对象
lession 9、兄弟组件通信方式
定义一个vue 实例
然后在兄弟组件中组件挂载的时候使用
eventBus.$on('addItem',this.handleAddTitle);
handleAddTitle(title){
console.log('title',title)
}
this.$emit('add',this.title)
eventBus.$emit('addItem',this.title)
lession 10、父子组件传值的5种方式
一、props+$emit()
二、回调函数方式(callback)
三、$parent+$children this.$children[0].data this.$parent.data
四、provide+inject 父组件 provide 子组件 inject
五、$attrs+$listeners 父、子、孙 三代组件 在子组件中用 v-bind="$attrs" 给孙组件 代传递父组件中的数据,在孙组件中之间用 {{$attrs.属性名}} 的方式使用 父组件中的数据
六、使用 ref 属性的方式 父组件 ref="childComp" this.$refs.childComp.属性名
lession 11、父子组件生命周期执行顺序问题
new vue() 生命周期
beforeCreate()
created()
beforeMount()
mounted()
beforeUpdate()
updated()
beforeDestory()
destroyed()
简化
created()
mounted()
updated()
destroyed()
存在父子组件的生命周期
parent:App created
child:List created
child:List mounted
parent:App mounted
结论:页面创建的时候 先父后子 挂载组件的顺序先子后父
parent:App before update
child:List before update
child:List updated
parent:App updated
结论:组件更新顺序 先父后子 更新完成的顺序 先子后父
lession 12、
1.nextTick 会在异步渲染完成后执行
2.异步渲染是批量进行渲染的
this.$nextTick(()=>{
const ulElem=this.$refs.ulRef;
const length=ulElem.childNodes.length;
console.log('length',length)
})
lession 13、slot 插槽
lession 14、vue3 和 vue2 的区别
Diff算法是如何实现的
配置项api options api
配置项型代码风格
很分散,逻辑不内聚,解决了耦合,没有解决
组合式api composition API
解决了高内聚问题
import {ref} from 'vue'
const useCmpute=(count)=>{
const plus=()=>{
count++;
}
const minus=()=>{
count--
}
return {
plus,minus
}
}
setup(){
const cout=ref(0);
{plus,minus}=useCompute(count);
return {
count,plus,minus
}
}
总结:
1. vue2和vue3双向数据绑定原理发生了改变
vue2 的双向数据绑定是利用ES5 的一个 API Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的
vue3 中使用了 es6 的 ProxyAPI 对数据代理。
相比于vue2.x使用proxy的优势如下
defineProperty只能监听某个属性不能对全对象监听
可以省去for in、闭包等内容来提升效率直接绑定整个对象即可
可以监听数组,不用再去单独的对数组做特异性操作 vue3.x可以检测到数组内部数据的变化
2. 默认进行懒观察lazy observation
在 2.x 版本里不管数据多大都会在一开始就为其创建观察者。当数据很大时这可能会在页面载入时造成明显的性能压力。3.x 版本,只会对「被用于渲染初始可见部分的数据」创建观察者,而且 3.x 的观察者更高效。
3. 更精准的变更通知。
比例来说2.x 版本中,使用 Vue.set 来给对象新增一个属性时,这个对象的所有 watcher 都会重新运行3.x 版本中,只有依赖那个属性的 watcher 才会重新运行。
4. 3.0 新加入了 TypeScript 以及 PWA 的支持
5.vue2和vue3组件发送改变
reactivity API
Vue2使用选项类型APIOptions API
Vue3合成型APIComposition API

129
doc/面试学习笔记2 Normal file
View File

@ -0,0 +1,129 @@
工程化&前端安全
webpack
基础配置
打包输出
插件使用
全局变量
源码分析
懒加载
模块热更新
懒加载
优化
PWA配置
打包优化
打包多页
noparse
happypack
抽离公共环境区分变量
构建npm组件库
自定义Loader(style-loader css-loader less-loader等)
自定义Plugin(html-webpack-plugin)
回溯历史
冲突解决
前端开发必会面试题和多种实战技巧
【问】$route和$router的区别
可以理解为,一个是用来获取路由信息的,一个是用来操作路由的
$route
route是路由信息对象里面主要包含路由的一些基本信息包括name、meta、path、hash、query、params、fullPath、matched、redirectedFrom
$router
router是VueRouter的实例包含了一些路由的跳转方法钩子函数等
【问】谈谈你对provide和inject的理解
通过provide/inject可以轻松实现跨级访问祖先组件的数据
在父组件中provide方法返回个对象在孙组件中用inject:['对象']访问对象
【问】什么是事件代理/事件委托
事件代理/事件委托是利用事件冒泡的特性,将本应该绑定在多个元素上的事件绑定在他们的祖先元素上,尤其在动态添加子元素的时候,可以非常方便的提高程序性能,减小内存空间
【问】call()和apply()的区别
实际上apply和call()的功能是一样的只是传入的参数列表形式不同apply传入的是数组
它们各自的定义:
apply调用一个对象的一个方法用另一个对象替换当前对象。例如B.apply(A, arguments);即A对象应用B对象的方法。
call调用一个对象的一个方法用另一个对象替换当前对象。例如B.call(A, args1,args2);即A对象调用B对象的方法。
它们的共同之处:
都“可以用来代替另一个对象调用一个方法将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象”。
它们的不同之处:
apply最多只能有两个参数——新this对象和一个数组argArray。如果给该方法传递多个参数则把参数都写进这个数组里面当然即使只有一个参数也要写进数组里。如果argArray不是一个有效的数组或arguments对象那么将导致一个TypeError。如果没有提供argArray和thisObj任何一个参数那么Global对象将被用作thisObj并且无法被传递任何参数。
call它可以接受多个参数第一个参数与apply一样后面则是一串参数列表。这个方法主要用在js对象各方法相互调用的时候使当前this实例指针保持一致或者在特殊情况下需要改变this指针。如果没有提供thisObj参数那么 Global 对象被用作thisObj。
实际上apply和call的功能是一样的只是传入的参数列表形式不同。
【问】什么是事件冒泡?什么是事件捕获
冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document)的顺序触发
捕获型事件:事件从最不精确的对象(document)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定)
支持w3c标准的浏览器在添加事件时用addEventListener(event,fn,useCapture)方法其中第三个参数useCapture是一个boolean值用来设置事件是在事件捕获时执行还是事件冒泡时执行而不兼容W3C的浏览器(IE)用attachEvent()方法次方法没有相关设置不过IE的事件模型默认是在事件冒泡时执行的也就是在useCapture等于false的时候执行所以把在处理事件时把useCapture设置为false是比较安全也是实现兼容浏览器的效果
【问】什么是"use strict";使用它的好处和坏处分别是什么?
【问】解释一下js的同源策略
同源策略,即拥有相同的协议(protocal),端口(如果指定),主机(域名)的两个页面是属于同一个源然而在IE中比较特殊IE中没有将端口号加入同源的条件中因此上图中端口不同那一项在IE中是算同源的<script><img><iframe>中的src,href都可以任意连接网络资源是不遵循同源策略的。
【问】请解释JSONP的工作原理
JSONP(JSON with Padding)是一个简单高效的跨域方式HTML中的script标签可以加载并执行其他域的javascript,于是我们可以通过script标记来动态加载其他域的资源例如我要在域A的页面pageA加载域B的数据那么在域B的页面pageB中我以JavaScript的形式声明pageA需要的数据然后在pageA中使用script标签把pageB加载进来那么pageB中的脚本就会得以执行。JSONP在此基础上加入了回调函数pageB加载完之后会执行pageA中定义的函数所需要的数据会以参数的形式传递给该函数。JSONP易于实现但是也会存在一些安全隐患如果第三方的脚本随意滴执行那么它就可以篡改页面内容截获敏感数据但是在受信任的双方传递数据JSONP是非常合适的选择。AJAX是不跨域的而JSONP是跨域的还有就是二者接受参数形式不一样
https://github.com/noxussj/Interview-Questions/issues/74
【问】vue父子组件传值有哪些方式
1.vm.$props
2.vm.$parent
3.provide/inject
4.vm.$emit
5.vm.$attrs
6.vm.$refs
【问】什么是组件化?模块化?工程化?
页面内容模块化
封装小的功能模块
模块化-》功能层
组件化-》视图层
路由的多级展示
导航的递归展示
路由出口
动态路由
var length=88;
function test(){
console.log(this.length)
}
var obj={
length:99,
action:function(test){
test();
arguments[0]();
}
}
obj.action(test,[1,2,3],4,5,6)
this指向规则
如果有调用者指向调用者无调用者默认为window
图片性能优化
雪碧图
精灵图
------减少网络请求的次数
字体图标
base64编码
后端返回的图片---图片懒加载
当全局变量和局部变量同名,全局变量不会作用于同盟局部变量作用域

12596
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

58
package.json Normal file
View File

@ -0,0 +1,58 @@
{
"name": "vue-erp-demo",
"version": "0.1.0",
"private": false,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.1",
"core-js": "^3.6.5",
"element-ui": "^2.15.2",
"express": "^4.17.1",
"lockr": "^0.8.5",
"socket.io": "^4.1.2",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"vuex": "^3.4.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/eslint-config-standard": "^5.1.2",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^6.2.2",
"less": "^4.1.1",
"less-loader": "^9.1.0",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"@vue/standard"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

30
public/index.html Normal file
View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
<style>
body,
html {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
</div>
<!-- built files will be auto injected -->
</body>
</html>

25
server.js Normal file
View File

@ -0,0 +1,25 @@
let app = require('express')()
let http = require('http').Server(app)
//let fs = require('fs')
let io = require('socket.io')(http)
var i = 0 //当前在线人
app.get('/', (req, res) => {
// res.send('这是后端服务')
//方法一、
// fs.readFile('./public/index.html', (err, data) => {
// if (err) throw err
// res.end(data)
// })
//方法二、
res.sendFile(__dirname + './public/index.html')
})
io.on('connection', () => {
i++
console.log('有' + i + '个用户连接了');
})
http.listen(3000, '127.0.0.1', () => {
console.log('app start port 3000')
})

7
src/App.vue Normal file
View File

@ -0,0 +1,7 @@
<template>
<div id="app">
<router-view/>
</div>
</template>

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,34 @@
<template>
<div class="hello">
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

30
src/components/Input.vue Normal file
View File

@ -0,0 +1,30 @@
<template>
<div>
<input type="text" v-model="title">
<button @click="add">添加</button>
</div>
</template>
<script>
import eventBus from '@/event/eventBus.js'
export default {
data(){
return{
title:''
}
},
methods:{
add(){
this.$emit('add',this.title)
eventBus.$emit('addItem',this.title)
}
},
mounted:{
},
created:{
}
}
</script>

61
src/components/Left.vue Normal file
View File

@ -0,0 +1,61 @@
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id"><a :id="item.id" href="">{{item.name}}</a></li>
</ul>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<el-input type="text" name="menu" v-model="menu"></el-input>
<el-button type="primary" @click="addMenu">添加菜单</el-button>
</div>
</template>
<style lang="less" scoped>
div{
ul{
margin: 0;
padding: 0;
list-style-type: none;
li{
margin: 0;
padding: 0;
list-style-type: none;
line-height: 30px;
a{
text-decoration: none;
color: #000000;
}
}
}
}
</style>
<script>
export default {
name:'Left',
props:{
list:Array
},
data(){
return{
menu:''
}
},
methods:{
addMenu(){
this.$emit('add',this.menu)
}
}
}
</script>

32
src/components/List.vue Normal file
View File

@ -0,0 +1,32 @@
<template>
<div class="list">
<ul>
<li v-for="item in list" :key="item.id">{{item.title}}</li>
</ul>
</div>
</template>
<script>
import eventBus from '@/event/eventBus.js'
export default {
props:{
list:Array
},
data(){
return{
}
},
methods:{
handleAddTitle(title){
console.log('title',title)
}
},
mounted(){
eventBus.$on('addItem',this.handleAddTitle)
},
beforeDestroy(){
eventBus.$off('addItem',this.handleAddTitle)
}
}
</script>

View File

@ -0,0 +1,35 @@
<template>
<div class="children">
儿子组件 children 中又引入了孙子组件 my-grandson
<ul v-for="item in fourClassic" :key="item.id">
<li>{{item.id}}----{{item.book}}-----{{item.writer}}</li>
</ul>
<p>这是个父组件的字符串类型的数据{{cuihua}}</p>
<p>这是父组件的计算属性{{computed}}</p>
<my-grandson></my-grandson>
</div>
</template>
<script>
import MyGrandson from '@/components/MyGrandson.vue'
export default {
components:{
MyGrandson
},
props:['cuihua','fourClassic','computed'],
}
</script>
<style lang="less" scoped>
.children{
ul{
li{
text-align: left;
}
}
}
</style>

View File

@ -0,0 +1,14 @@
<template>
<div class="grandson">
孙子组件grandson用 inject 读取爷爷组件的数据{{student}}
</div>
</template>
<script>
export default {
inject:['student'],
mounted(){
console.log(this.student)
}
}
</script>

6
src/event/eventBus.js Normal file
View File

@ -0,0 +1,6 @@
// 事件公交车用来解决兄弟组件之间的通信问题
import Vue from 'vue';
const eventBus = new Vue()
export default eventBus;

20
src/main.js Normal file
View File

@ -0,0 +1,20 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'
import axios from 'axios'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
Vue.config.productionTip = false
Vue.prototype.$http = axios
axios.defaults.baseUrl = "http://127.0.0.1:8080"
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')

88
src/router/index.js Normal file
View File

@ -0,0 +1,88 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [{
path: '/',
redirect: '/layout',
},
{
path: '/login',
name: 'login',
component: () =>
import ('../views/Login.vue')
},
{
path: '/layout',
name: 'layout',
component: () =>
import ('../views/layout/Index.vue'),
children: [{
path: '/index',
name: 'index',
component: require('../views/layout/home/Index.vue').default,
}, {
path: '/stats',
name: 'stats',
component: require('../views/layout/stats/Index.vue').default,
}, {
path: '/wms',
name: 'stats',
component: require('../views/layout/wms/Index.vue').default,
children: [{
path: '/wms/list',
name: 'lessionDemo1',
component: require('../views/layout/wms/List.vue').default,
}]
}, {
path: '/user',
name: 'user',
component: require('../views/layout/user/Index.vue').default,
children: [{
path: '/user/stats',
name: 'userStats',
component: require('../views/layout/user/Stats.vue').default,
}, {
path: '/user/role',
name: 'userRole',
component: require('../views/layout/user/Role.vue').default,
}]
}, {
path: '/lession',
name: 'lession',
component: require('../views/layout/lession/Index.vue').default,
children: [{
path: '/lession/demo1',
name: 'lessionDemo1',
component: require('../views/layout/lession/Demo1.vue').default,
}, {
path: '/lession/demo2',
name: 'lessionDemo2',
component: require('../views/layout/lession/Demo2.vue').default,
}]
}]
}
]
const router = new VueRouter({
routes
})
// router.beforeEach(function(to, from, next) {
// if (!sessionStorage.getItem('username')) {
// if (to.path !== '/login') {
// next('/login')
// }
// }
// next();
// })
// router.beforeResolve(function(to,from,next){
// })
export default router

8
src/store/getters.js Normal file
View File

@ -0,0 +1,8 @@
const getters = {
token: state => state.user.token,
refresh_token: state => state.user.refresh_token,
theme: state => state.user.theme,
userInfo: state => state.user.userInfo,
appToken: state => state.system.appToken,
userToken: state => state.system.userToken
}

20
src/store/index.js Normal file
View File

@ -0,0 +1,20 @@
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import user from './modules/user'
import system from './modules/system'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {
user,
system
},
getters
})
export default store

View File

@ -0,0 +1,53 @@
import Lockr from 'lockr/lockr.js'
const system = {
state: {
appToken: '',
userToken: '',
menu: {},
},
mutations: {
saveMenu(state, menu) {
state.menu = menu
Lockr.set('menu', state.menu)
},
getMenu(state) {
state.menu = Lockr.get('menu')
},
saveAppToken(state, appToken) {
state.appToken = appToken
Lockr.set('appToken', state.appToken)
},
getAppToken(state) {
state.appToken = Lockr.get('appToken')
},
saveUserToken(state, userToken) {
state.userToken = userToken
Lockr.set('userToken', state.userToken)
},
getUserToken(state) {
state.userToken = Lockr.get('userToken')
},
},
actions: {
saveAppToken({ commit }, appToken) {
commit('saveAppToken', appToken)
},
getAppToken({ commit }) {
commit('getAppToken')
},
saveUserToken({ commit }, userToken) {
commit('saveUserToken', userToken)
},
getUserToken({ commit }) {
commit('getUserToken')
},
saveMenu({ commit }, menu) {
commit('saveMenu', menu)
},
getMenu({ commit }) {
commit('getMenu')
}
}
}
export default system

99
src/store/modules/user.js Normal file
View File

@ -0,0 +1,99 @@
import Lockr from 'lockr/lockr.js'
const user = {
state: {
token: '',
refresh_token: '',
expires_in: '',
jti: '',
scope: '',
token_type: '',
userInfo: {},
theme: ''
},
mutations: {
saveTheme(state, theme) {
state.theme = theme
Lockr.set('theme', state.theme)
},
getTheme(state) {
state.theme = Lockr.get('theme')
},
SET_ACCESS_TOKEN(state, token) {
state.token = token
Lockr.set('token', state.token)
},
GET_ACCESS_TOKEN(state) {
state.token = Lockr.get('token')
},
SET_REFRESH_TOKEN(state, refresh_token) {
state.refresh_token = refresh_token
Lockr.set('refresh_token', state.refresh_token)
},
GET_REFRESH_TOKEN(state) {
state.refresh_token = Lockr.get('refresh_token')
},
SET_EXPIRES_IN(state, expires_in) {
state.expires_in = expires_in
Lockr.set('expires_in', state.expires_in)
},
SET_JTI(state, jti) {
state.jti = jti
Lockr.set('jti', state.jti)
},
SET_SCOPE(state, scope) {
state.scope = scope
Lockr.set('scope', state.scope)
},
SET_TOKEN_TYPE(state, token_type) {
state.token_type = token_type
Lockr.set('token_type', state.token_type)
},
saveUserInfo(state, userInfo) {
state.userInfo = userInfo
Lockr.set('userInfo', state.userInfo)
},
getUserInfo(state) {
state.userInfo = Lockr.get('userInfo')
}
},
actions: {
saveTheme({ commit }, theme) {
commit('saveTheme', theme)
},
getTheme({ commit }) {
commit('getTheme')
},
SET_ACCESS_TOKEN({ commit }, token) {
commit('SET_ACCESS_TOKEN', token)
},
GET_ACCESS_TOKEN({ commit }) {
commit('GET_ACCESS_TOKEN')
},
SET_REFRESH_TOKEN({ commit }, refresh_token) {
commit('SET_REFRESH_TOKEN', refresh_token)
},
GET_REFRESH_TOKEN({ commit }) {
commit('GET_REFRESH_TOKEN')
},
SET_EXPIRES_IN({ commit }, expires_in) {
commit('SET_EXPIRES_IN', expires_in)
},
SET_JTI({ commit }, jti) {
commit('SET_JTI', jti)
},
SET_SCOPE({ commit }, scope) {
commit('SET_SCOPE', scope)
},
SET_TOKEN_TYPE({ commit }, token_type) {
commit('SET_TOKEN_TYPE', token_type)
},
saveUserInfo({ commit }, userInfo) { //权限
commit('saveUserInfo', userInfo)
},
getUserInfo({ commit }) {
commit('getUserInfo')
}
}
}
export default user

107
src/utils/network.js Normal file
View File

@ -0,0 +1,107 @@
// 封装 axios 请求响应拦截器封装
import axios from "axios";
import store from '@/store'
import { Message } from "_element-ui@2.15.2@element-ui";
var instance = axios.create({
baseURL: 'http://localhost:8080',
timeout: 300000,
});
instance.interceptors.request.use(config => {
store.dispatch('GET_ACCESS_TOKEN')
if (store.getters.token) {
config.headers['Authorization'] = 'bearer ' + store.getters.token
}
store.dispatch('getAppToken')
store.dispatch('getUserToken')
if (store.getters.appToken) {
config.headers['userToken'] = store.getters.userToken
config.headers['appToken'] = store.getters.appToken
}
if (config.method === 'post' || config.method.method === 'put') {
if (config.url == '/RAC/oauth/token') {
config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
config.data = qs.stringify(config.data)
}
} else if (config.method === 'get' || config.method === 'delete') {
if (config.method === 'delete') {
if (config.url.indexOf('/tags') != -1) {
config.headers['Context-Type'] = 'application/json'
} else {
config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
config.data = qs.stringify(config.data)
}
} else {
config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
config.data = qs.stringify(config.data)
}
}
if (config.method === 'put') {
if (config.url.indexOf('/properties') != -1) {
config.headers['Content-Type'] = 'application/json'
}
}
if (config.method === "patch") {
if (config.url.indexOf('/tags') != -1) {
config.headers['Content-Type'] = 'application/json'
}
}
return config
}, error => {
return Promise.reject(error)
})
instance.interceptors.response.use(function(response) {
if (response.status != 200) {
Message({
message: response.data.msg,
type: 'warning'
});
}
return response;
}, async function(error) {
if (error.response) {
switch (error.response.status) {
case 401:
let fullurl = window.location.href
let index = fullurl.indexOf('/#')
let length = fullurl.substring(index).length
if (length == 3) {
Message({
showClose: true,
message: '用户名或密码错误,请刷新浏览器后重新填写正确的用户名和密码',
type: 'error'
});
}
let res = await doRequest(error)
if (res) {
return res;
}
break;
case 400:
Message({
showClose: true,
message: error.response.data.responseStatus.message,
type: 'warning'
});
break;
case 500:
break;
default:
Message({
showClose: true,
message: '连接服务器失败',
type: 'warning'
})
}
}
return Promise.reject(error)
})

1
src/utils/websock.js Normal file
View File

@ -0,0 +1 @@
// websock工具封装

76
src/views/Login.vue Normal file
View File

@ -0,0 +1,76 @@
<template>
<div class="login">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="用户名" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" v-model="ruleForm.password"></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>
</div>
</template>
<script>
export default {
data() {
return {
ruleForm: {
name: 'admin',
password: '123456'
},
rules: {
name: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
password:[
{required:true,message:'请输入密码',trigger:'blur'}
]
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let {name,password}=this.ruleForm;
this.$http({
method:'post',
url:'get_login',
data:{user:name,password:password}
}).then(res=>{
if(res.status==200){
sessionStorage.setItem('username',name)
sessionStorage.setItem('password',password)
this.$router.push('/home')
this.$message.success('登录成功')
}else{
sessionStorage.setItem('username',name)
sessionStorage.setItem('password',password)
this.$router.push('/home')
this.$message.success('登录成功')
}
}).catch(res=>{
if(res.status==404){
this.$message.error('后端服务错误')
}
})
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>

View File

@ -0,0 +1,20 @@
<template>
<div class="breadcrumb">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>活动管理</el-breadcrumb-item>
<el-breadcrumb-item>活动列表</el-breadcrumb-item>
<el-breadcrumb-item>活动详情</el-breadcrumb-item>
</el-breadcrumb>
</div>
</template>
<script>
export default {
}
</script>
<style lang="less" scoped>
.el-breadcrumb {
line-height: 60px;
}
</style>

View File

@ -0,0 +1,84 @@
<template>
<el-container>
<!-- 左侧导航 -->
<Nav/>
<el-container>
<el-header>
<el-button class="logout" type="primary" @click="logout(3,$event)" round size="mini">退出</el-button>
<!-- 面包屑 -->
<Breadcrumb/>
</el-header>
<el-main>
<router-view></router-view>
</el-main>
<el-footer>
&copy; copy-right 桃谷六仙 联系QQ:1575955004 出品-基于VUE+ELEMENT框架的后台管理页面
</el-footer>
</el-container>
</el-container>
</template>
<script>
import Left from '@/components/Left.vue'
import Nav from './Nav.vue'
import Breadcrumb from './Breadcrumb.vue'
export default {
name: 'Home',
components:{
Left,Nav,
Breadcrumb
},
data(){
return{
}
},
methods:{
logout(id,e){
alert(id)
console.log('e',e)
},
add(item){
this.menus.push({
id:new Date().getTime().toString().slice(10),
name:item
})
}
}
}
</script>
<style lang="less" scoped>
body,html{margin: 0;padding: 0;}
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}
.el-header {
.el-button{
margin-top: 17px;
}
}
.logout{
float:right;
}
.el-breadcrumb {
line-height: 60px;
}
.el-main {
background-color: #E9EEF3;
color: #333;
text-align: center;
height: calc(100vh - 120px);
}
body > .el-container {
margin-bottom: 40px;
}
</style>

103
src/views/layout/Nav.vue Normal file
View File

@ -0,0 +1,103 @@
<template>
<el-aside width="200px">
<h2 class="logo">VUE-后台管理</h2>
<el-menu default-active="2" class="el-menu-vertical-demo" router>
<NavItem v-for="v in menus" :key="v.url" :item='v' :path="v.url"/>
</el-menu>
</el-aside>
</template>
<style lang="less" scoped>
.el-aside {
background-color: #D3DCE6;
color: #333;
text-align: center;
.el-menu{
background: none;
li{
.el-menu{
background: none;
}
}
}
}
</style>
<script>
import NavItem from './NavItem.vue'
export default {
name:'Nav',
components:{
NavItem
},
data(){
return{
menus:[{
name: '首页',
url: '/index'
},
{
name: '数据统计',
url: '/stats'
},
{
name: '信息管理',
url: '/wms',
child: [{
name: '列表展示',
url: '/wms/list'
}]
},
{
name: '用户管理',
url: '/user',
child: [{
name: '用户统计',
url: '/user/stats'
}, {
name: '角色统计',
url: '/user/role'
}]
},
{
name: '学习实例',
url: '/lession',
child: [{
name: '示例1',
url: '/lession/demo1'
}, {
name: '示例2',
url: '/lession/demo2'
}, {
name: '示例3',
url: '/lession/demo3'
}, {
name: '示例4',
url: '/lession/demo4'
}, {
name: '示例5',
url: '/lession/demo5'
}]
}
]
}
},
mounted(){
//this.getNav()
},
methods:{
//
getNav(){
this.$http.get('get_nav').then(res=>{
console.log(res)
let {code,result}=res.data
if(code=='200'){
this.menus=result
}
})
}
}
}
</script>

View File

@ -0,0 +1,45 @@
<template>
<div>
<!-- 无子级 -->
<el-menu-item :index="item.url" v-if="!item.child">
<i class="el-icon-menu"></i>
<span slot="title">{{item.name}}</span>
</el-menu-item>
<!-- 有子级 -->
<el-submenu :index="path" v-else>
<template slot="title">
<i class="el-icon-location"></i>
<span>{{item.name}}</span>
</template>
<NavItem v-for="child in item.child" :key="child.url" :item='child' :path="child.url"/>
</el-submenu>
</div>
</template>
<style lang="less" scoped>
.el-aside {
.el-menu{
background: none;
li{
.el-menu{
background: none;
}
}
}
}
</style>
<script>
export default {
name:'NavItem',
data(){
return{
items:[]
}
},
props:['item','path']
}
</script>

View File

@ -0,0 +1,3 @@
<template>
<div>后台首页</div>
</template>

View File

@ -0,0 +1,47 @@
<template>
<div class="container">
<!-- https://github.com/noxussj/Interview-Questions/issues/74 -->
父亲组件用 provider 方法定义了一个数据对象 student 但是不能直接在爷爷组件中使用
<my-children :cuihua="msg" :fourClassic="list" :computed='val'></my-children>
<p>父子组件传值关键点
1.儿子组件获取父亲组件的数据
在儿子组件定义props数组相当于给儿子组件定义了标签属性在父亲页面对这个属性上绑定传入父亲组件的数据在儿子页面渲染父亲组件的数据
</p>
</div>
</template>
<script>
import MyChildren from '@/components/MyChildren.vue';
export default {
components:{
MyChildren
},
provide(){
return{
student:{
name:'谢晓峰'
}
}
},
data(){
return {
msg:'像风一样自由',
list:[
{id:1,book:'西游记',writer:'吴承恩'},
{id:2,book:'三国演义',writer:'罗贯中'},
{id:3,book:'水浒传',writer:'施耐庵'},
{id:4,book:'红楼梦',writer:'高鹗'},
]
}
},
computed:{
val(){
return this.msg+'????啊'
}
},
mounted(){
console.log(this.student)
}
}
</script>

View File

@ -0,0 +1,30 @@
<template>
<div>
<MyInput @add="add"/>
<MyList :list="list"/>
</div>
</template>
<script>
import MyInput from '@/components/Input.vue'
import MyList from '@/components/List.vue'
export default {
components:{
MyInput,MyList
},
data(){
return{
}
},
methods:{
},
mounted:{
},
created:{
}
}
</script>

View File

@ -0,0 +1,7 @@
<template>
<div>
示例展示列表
<router-view></router-view>
</div>
</template>

View File

@ -0,0 +1,11 @@
<template>
<div class="container">
图表统计页面
</div>
</template>
<script>
export default {
}
</script>

View File

@ -0,0 +1,5 @@
<template>
<div>用户管理页面
<router-view></router-view>
</div>
</template>

View File

@ -0,0 +1,3 @@
<template>
<div>用户角色页面</div>
</template>

View File

@ -0,0 +1,3 @@
<template>
<div>用户统计页面</div>
</template>

View File

@ -0,0 +1,3 @@
<template>
<div>信息列表</div>
</template>

View File

@ -0,0 +1,3 @@
<template>
<div>信息列表</div>
</template>

8
vue.config.js Normal file
View File

@ -0,0 +1,8 @@
module.exports = {
devServer: {
port: 8080,
host: 'localhost',
open: true
},
lintOnSave: false
}