vue组件、自定义指令、路由

vue

1.vue组件" title="vue组件">vue组件 

   组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树。

注册一个全局组件语法格式如下:

Vue.component(tagName, options)

tagName 为组件名,options 为配置选项。注册后,我们可以使用以下方式来调用组件:

<tagName></tagName>

1. 组件名大小写:

 定义组件名的方式有两种:

(1)使用 kebab-case

Vue.component('my-component-name', { /* ... */ })

  当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如 <my-component-name>。

(2)使用 PascalCase

Vue.component('MyComponentName', { /* ... */ })

  当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 <my-component-name> 和 <MyComponentName> 都是可接受的。注意,尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。

2.全局组件

  所有实例都能用全局组件。

        <div id="app">

<vue></vue>

</div>

<script>

// 注册

Vue.component('vue', {

template: '<h1>自定义组件!</h1>'

})

// 创建根实例

new Vue({

el: '#app'

})

</script>

结果:

3.局部组件

  我们也可以在实例选项中注册局部组件,这样组件只能在这个实例中使用。

        <div id="app">

<vue></vue>

</div>

<script>

var Child = {

template: '<h1>自定义组件!</h1>'

}

// 创建根实例

new Vue({

el: '#app',

components: {

// <vue> 将只在父模板可用

'vue': Child

}

})

</script>

  对于 components 对象中的每个属性来说,其属性名就是自定义元素的名字,其属性值就是这个组件的选项对象。

  注意:组件的定义data必须为Function。

4.Prop

  prop 是父组件用来传递数据的一个自定义属性。父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 "prop"。

        <div id="app">

<child message="hello!"></child>

</div>

<script>

// 注册

Vue.component('child', {

// 声明 props

props: ['message'],

// 同样也可以在 vm 实例中像 "this.message" 这样使用

template: '<span>{{ message }}</span>'

})

// 创建根实例

new Vue({

el: '#app'

})

</script>

结果:

5.动态 prop

  类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件。

        <div id="app">

<div>

<input v-model="parentMsg">

<br>

<child v-bind:message="parentMsg"></child>

</div>

</div>

<script>

// 注册

Vue.component('child', {

// 声明 props

props: ['message'],

// 同样也可以在 vm 实例中像 "this.message" 这样使用

template: '<span>{{ message }}</span>'

})

// 创建根实例

new Vue({

el: '#app',

data: {

parentMsg: '父组件内容'

}

})

</script>

结果:

以下实例中将 v-bind 指令将 todo 传到每一个重复的组件中:

        <div id="app">

<ol>

<todo-item v-for="item in sites" v-bind:todo="item"></todo-item>

</ol>

</div>

<script>

Vue.component('todo-item', {

props: ['todo'],

template: '<li>{{ todo.text }}</li>'

})

new Vue({

el: '#app',

data: {

sites: [{

text: 'Baidu'

},

{

text: 'Google'

},

{

text: 'Taobao'

}

]

}

})

</script>

结果:

注意: prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。

6.Prop 验证

  组件可以为 props 指定验证要求。为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<title></title>

<script src="js/vue.min.js"></script>

</head>

<body>

<div id="app">

<!--v-bind:可以缩写为:-->

<example :propA="age" v-bind:propB="name" :propc="'C的值'"></example>

</div>

<script>

Vue.component('example', {

template: '<span>{{ propa }} - {{ propb }} - {{ propc }} - {{ propd }} - {{ prope }} - {{ propf }}</span>',

props: {

// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)

propa: Number,

// 多个可能的类型

propb: [String, Number],

// 必填的字符串

propc: {

type: String,

required: true

},

// 带有默认值的数字

propd: {

type: Number,

default: 100

},

// 带有默认值的对象

prope: {

type: Object,

// 对象或数组默认值必须从一个工厂函数获取

default: function() {

return {

message: 'hello'

}

}

},

// 自定义验证函数

propf: {

validator: function(value) {

// 这个值必须匹配下列字符串中的一个

return ['success', 'warning', 'danger'].indexOf(value) !== -1

}

}

}

})

new Vue({

el: '#app',

data: {

age: 28,

name: '25'

}

})

</script>

</body>

</html>

注意:

(1)当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。type 可以是下面原生构造器:

String、Number、Boolean、Array、Object、Date、Function、Symbol

(2)type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认。

例如:

        <div >

<!--v-bind:可以缩写为:-->

<example :author="new Person('三丰', '张')"></example>

</div>

<script>

function Person(firstName, lastName) {

this.firstName = firstName

this.lastName = lastName

}

Vue.component('example', {

template: '<span>{{ author.firstName }}</span>',

props: {

// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)

author: Person

}

})

new Vue({

el: '#app'

})

</script>

结果:

7.自定义事件

  组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!

  我们可以使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:

    使用 $on(eventName) 监听事件

    使用 $emit(eventName) 触发事件

  另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。

以下实例中子组件已经和它外部完全解耦了。它所做的只是触发一个父组件关心的内部事件。

        <div >

<div >

<p>{{ total }}</p>

<button-counter v-on:increment="incrementTotal"></button-counter>

<button-counter v-on:increment="incrementTotal"></button-counter>

</div>

</div>

<script>

Vue.component('button-counter', {

template: '<button v-on:click="incrementHandler">{{ counter }}</button>',

data: function() {

return {

counter: 0

}

},

methods: {

incrementHandler: function() {

this.counter += 1

this.$emit('increment')

}

},

})

new Vue({

el: '#counter-event-example',

data: {

total: 0

},

methods: {

incrementTotal: function() {

this.total += 1

}

}

})

</script>

结果:

如果你想在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on 。例如:

<my-component v-on:click.native="doTheThing"></my-component>

8.data 必须是一个函数

  上面例子中,可以看到 button-counter 组件中的 data 不是一个对象,而是一个函数。

  这样的好处就是每个实例可以维护一份被返回对象的独立的拷贝,如果 data 是一个对象则会影响到其他实例。

            var buttonCounter2Data = {

count: 0

}

Vue.component('button-counter2', {

/*

data: function () {

// data 选项是一个函数,组件不相互影响

return {

count: 0

}

},

*/

data: function() {

// data 选项是一个对象,会影响到其他实例

return buttonCounter2Data

},

template: '<button v-on:click="count++">点击了 {{ count }} 次。</button>'

})

new Vue({

el: '#components-demo3'

})

</script>

结果:

三个按钮会同步变化

2.自定义指令

   除了默认设置的核心指令( v-model 和 v-show ), Vue 也允许注册自定义指令。

1.注册全局指令

  该指令的功能是在页面加载时,元素获得焦点。

        <div >

<p>页面载入时,input 元素自动获取焦点:</p>

<input v-focus>

</div>

<script>

// 注册一个全局自定义指令 v-focus

Vue.directive('focus', {

// 当绑定元素插入到 DOM 中。

inserted: function(el) {

// 聚焦元素

el.focus()

}

})

// 创建根实例

new Vue({

el: '#app'

})

</script>

2.注册局部指令

  在实例使用 directives 选项来注册局部指令,这样指令只能在这个实例中使用

        <div >

<p>页面载入时,input 元素自动获取焦点:</p>

<input v-focus>

</div>

<script>

// 创建根实例

new Vue({

el: '#app',

directives: {

// 注册一个局部的自定义指令 v-focus

focus: {

// 指令的定义

inserted: function(el) {

// 聚焦元素

el.focus()

}

}

}

})

</script>

3.钩子

1.钩子函数

指令定义函数提供了几个钩子函数(可选):

bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。

inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。

update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。

componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。

unbind: 只调用一次, 指令与元素解绑时调用。

2.钩子函数参数

钩子函数的参数有:除了 el 之外,其它参数都应该是只读的,切勿进行修改。

  • el: 指令所绑定的元素,可以用来直接操作 DOM 。

  • binding: 一个对象,包含以下属性:

    • name: 指令名,不包括 v- 前缀。

    • value: 指令的绑定值, 例如: v-my-directive="1 + 1", value 的值是 2

    • oldValue: 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。

    • expression: 绑定值的表达式或变量名。 例如 v-my-directive="1 + 1" , expression 的值是 "1 + 1"

    • arg: 传给指令的参数。例如 v-my-directive:foo, arg 的值是 "foo"

    • modifiers: 一个包含修饰符的对象。 例如: v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }

  • vnode: Vue 编译生成的虚拟节点。

  • oldVnode: 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

例如:

        <div >

</div>

<script>

Vue.directive('vue', {

bind: function(el, binding, vnode) {

var s = JSON.stringify

el.innerHTML =

'name: ' + s(binding.name) + '<br>' +

'value: ' + s(binding.value) + '<br>' +

'expression: ' + s(binding.expression) + '<br>' +

'argument: ' + s(binding.arg) + '<br>' +

'modifiers: ' + s(binding.modifiers) + '<br>' +

'vnode keys: ' + Object.keys(vnode).join(', ')

}

})

new Vue({

el: '#app',

data: {

message: 'vue 6A!'

}

})

</script>

结果:

3.简写

  在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写:

        <div >

123

</div>

<script>

Vue.directive('color-swatch', function(el, binding) {

el.style.backgroundColor = binding.value

})

new Vue({

el: '#app',

data: {

color: 'red'

}

})

</script>

结果:

4.对象字面量

  如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。

        <div >

<div v-vue="{ color: 'green', text: 'vue 6A!' }"></div>

</div>

<script>

Vue.directive('vue', function(el, binding) {

// 简写方式设置文本及背景颜色

el.innerHTML = binding.value.text

el.style.backgroundColor = binding.value.color

})

new Vue({

el: '#app'

})

</script>

结果:

补充:vue自定义路由实现权限管理。例如,下面只是根据角色来进行权限验证,角色在登录的时候会存到localStorage 

(1) 定义判断的方法: 需要绑 inserted 钩子函数。否则会导致el.parentNode 为null。

import { getRoles } from './auth.js';

/**

*

* @param {*} el 元素

* @param {*} binding 包含值相关的对象

*/

export function hasRole(el, binding) {

if (!el || !binding) {

return;

}

const roles = binding.value;

if (!roles) {

return;

}

const validteRoles = roles.split(",");

const userRoles = getRoles();

// 求交集

let intersection = userRoles.filter(function (val) { return validteRoles.indexOf(val) > -1 })

if (!!intersection && intersection.length > 0) {

return;

}

// 无权限删除该元素

el.parentNode && el.parentNode.removeChild(el);

}

(2)注册全局指令(vue项目在main.js中注册)

// 自定义的指令

import { hasRole } from '@/utils/directive.js'

Vue.directive('hasRole', { inserted: hasRole });

(3) vue页面中使用

<i class="el-icon-edit" @click="update(scope.row)" title="编辑" v-hasRole="'系统管理员'"></i>

3.路由

  Vue.js 路由允许我们通过不同的 URL 访问不同的内容。通过 Vue.js 可以实现多视图的单页Web应用(single page web application,SPA)。Vue.js 路由需要载入 vue-router 库。

1.简单实例

  Vue.js + vue-router 可以很简单的实现单页应用。<router-link> 是一个组件,该组件用于设置一个导航链接,切换不同 HTML 内容。 to 属性为目标地址, 即要显示的内容。

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<title></title>

<script src="js/vue.min.js" type="text/javascript" charset="utf-8"></script>

<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

</head>

<body>

<div id="app">

<h1>Hello App!</h1>

<p>

<!-- 使用 router-link 组件来导航. -->

<!-- 通过传入 `to` 属性指定链接. -->

<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->

<router-link to="/foo">Go to Foo</router-link>

<router-link to="/bar">Go to Bar</router-link>

</p>

<!-- 路由出口 -->

<!-- 路由匹配到的组件将渲染在这里 -->

<router-view></router-view>

</div>

<script>

// 0. 如果使用模块化机制编程,导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)

// 1. 定义(路由)组件。

// 可以从其他文件 import 进来

const Foo = {

template: '<div>foo</div>'

}

const Bar = {

template: '<div>bar</div>'

}

// 2. 定义路由

// 每个路由应该映射一个组件。 其中"component" 可以是

// 通过 Vue.extend() 创建的组件构造器,

// 或者,只是一个组件配置对象。

// 我们晚点再讨论嵌套路由。

const routes = [{

path: '/foo',

component: Foo

}, {

path: '/bar',

component: Bar

}]

// 3. 创建 router 实例,然后传 `routes` 配置

// 你还可以传别的配置参数, 不过先这么简单着吧。

const router = new VueRouter({

routes // (缩写)相当于 routes: routes

})

// 4. 创建和挂载根实例。

// 记得要通过 router 配置参数注入路由,

// 从而让整个应用都有路由功能

const app = new Vue({

router

}).$mount('#app')

// 现在,应用已经启动了!

</script>

</body>

</html>

结果:点击过的导航链接都会加上样式 class ="router-link-exact-active router-link-active"。$mount('#app')相当于绑定元素,也就是el为#app。

2.<router-link> 相关属性

(1) to  表示目标路由的链接。 当被点击后,内部会立刻把 to 的值传到 router.push(),所以这个值可以是一个字符串或者是描述目标位置的对象。

<!-- 字符串 -->

<router-link to="home">Home</router-link>

<!-- 渲染结果 -->

<a href="home">Home</a>

<!-- 使用 v-bind 的 JS 表达式 -->

<router-link v-bind:to="'home'">Home</router-link>

<!-- 不写 v-bind 也可以,就像绑定别的属性一样 -->

<router-link :to="'home'">Home</router-link>

<!-- 同上 -->

<router-link :to="{ path: 'home' }">Home</router-link>

<!-- 命名的路由 -->

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

<!-- 带查询参数,下面的结果为 /register?plan=private -->

<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>

(2)replace  设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),导航后不会留下 history 记录。

<router-link :to="{ path: '/abc'}" replace></router-link>

(3)append  设置 append 属性后,则在当前 (相对) 路径前添加基路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b

<router-link :to="{ path: 'relative/path'}" append></router-link>

(4) tag 有时候想要 <router-link> 渲染成某种标签,例如 <li>。 于是我们使用 tag prop 类指定何种标签,同样它还是会监听点击,触发导航。

<router-link to="/foo" tag="li">foo</router-link>

<!-- 渲染结果 -->

<li>foo</li>

(5)active-class 设置 链接激活时使用的 CSS 类名。可以通过以下代码来替代。

<style>

._active{

background-color : red;

}

</style>

<p>

<router-link v-bind:to = "{ path: '/route1'}" active-class = "_active">Router Link 1</router-link>

<router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>

</p>

  注意这里 class 使用 active_class="_active"。

(6)exact-active-class  配置当链接被精确匹配的时候应该激活的 class。可以通过以下代码来替代。

<p>

<router-link v-bind:to = "{ path: '/route1'}" exact-active-class = "_active">Router Link 1</router-link>

<router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>

</p>

(7)event  声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。

<router-link v-bind:to = "{ path: '/route1'}" event = "mouseover">Router Link 1</router-link>

  以上代码设置了 event 为 mouseover ,及在鼠标移动到 Router Link 1 上时导航的 HTML 内容会发生改变。

3.路由总结

1.路由的to属性默认是根据path进行跳转。也可以通过指定name根据name进行跳转

                <!--指定跳到name为newsListName-->

<router-link :to="{name: 'newsListName'}">News</router-link>

2.一级路由  path:'/'    默认为显示。也可以通过一级路由重定向到二级路由。例如:

(1)默认显示:

            const routes = [{

path: '/',

name: 'index',

component: Home

}, {

path: '/home',

name: 'home',

component: Home

}, {

path: '/list',

name: 'newsListName',

component: NewsList

}]

(2)有了redirect属性会覆盖掉component对应的组件:

            const routes = [{

path: '/',

name: 'index',

component: Home,

redirect: '/list'

}, {

path: '/home',

name: 'home',

component: Home

}, {

path: '/list',

name: 'newsListName',

component: NewsList

}]

3.也可以通过点击事件切换路由

<button type="button" @click="goToLink">到首页</button>

methods : {

goToLink() {

// 可以做一些前置处理

this.$router.replace('/home')

}

}

4.关于子路由的使用如下:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<title></title>

<script src="js/vue.min.js" type="text/javascript" charset="utf-8"></script>

<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

</head>

<body>

<div id="app">

<h1>Hello App!</h1>

<p>

<!-- 使用 router-link 组件来导航. -->

<!-- 通过传入 `to` 属性指定链接. -->

<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->

<!--默认根据path属性进行跳转-->

<router-link to="/home">首页</router-link>

<!--指定跳到name为newsListName-->

<router-link :to="{name: 'newsListName'}">News</router-link>

<button type="button" @click="goToLink">到首页</button>

</p>

<!-- 路由出口 -->

<!-- 路由匹配到的组件将渲染在这里 -->

<router-view></router-view>

</div>

<script>

// 0. 如果使用模块化机制编程,导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)

// 1. 定义(路由)组件。

// 可以从其他文件 import 进来

const Home = {

template: "<div>欢迎您!</div>"

}

const NewsList = {

template: "<div>NewsList<br/><router-link to='/list/add'>add</router-link><br/><router-link to='/list/detail'>detail</router-link><router-view></router-view></div>"

}

// 子路由

const NewsAdd = {

template: "<div>添加新闻</div>"

}

const NewsDetail = {

template: "<div>新闻详情</div>"

}

// 2. 定义路由

// 每个路由应该映射一个组件。 其中"component" 可以是通过 Vue.extend() 创建的组件构造器,或者,只是一个组件配置对象。

const routes = [{

path: '/',

name: 'index',

component: Home,

redirect: '/list'

}, {

path: '/home',

name: 'home',

component: Home

}, {

path: '/list',

name: 'newsListName',

component: NewsList,

children: [{

path: 'add',

name: 'newsListAdd',

component: NewsAdd

}, {

path: 'detail',

name: 'newsListDetail',

component: NewsDetail

}]

}]

// 3. 创建 router 实例,然后传 `routes` 配置

// 你还可以传别的配置参数, 不过先这么简单着吧。

const router = new VueRouter({

routes // (缩写)相当于 routes: routes

})

// 4. 创建和挂载根实例。

// 记得要通过 router 配置参数注入路由,

// 从而让整个应用都有路由功能

const app = new Vue({

router,

methods : {

goToLink() {

// 可以做一些前置处理

this.$router.replace('/home')

}

}

}).$mount('#app')

// 现在,应用已经启动了!

</script>

</body>

</html>

补充:vue路由的两种模式:history和hash模式 (现在基于ajax的路由插件也有。ajax异步请求的URL不会改变,现在也有jquery插件解决这种问题。)

  默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。是用 url#/xxx/xxx这种形式。例如:http://localhost:8080/#/test

const router = new VueRouter({

mode: 'hash',

routes: [...]

})

  原理:url 中常会出现 #,一可以表示锚点(如回到顶部按钮的原理),二是路由里的锚点(hash)。Web 服务并不会解析 hash,也就是说 # 后的内容 Web 服务都会自动忽略,但是 JavaScript 是可以通过 window.location.hash 读取到的,读取到路径加以解析之后就可以响应不同路径的逻辑处理。 

  如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

例如:

const router = new VueRouter({

mode: 'history',

routes: [...]

})

  当使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!

  不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

原理:history.pushState 和 history.replaceState 。两个 API 都会操作浏览器的历史记录,而不会引起页面的刷新,pushState会增加一条新的历史记录,而replaceState则会替换当前的历史记录。

补充:如下是使用vuetify自定义组件父组件向子组件传值,子组件用事件的方法提示父组件更新数据

父组件:

<template v-slot:top>

<v-card>

<v-sheet class="px-3 pt-3 pb-3" v-if="!loading">

<v-skeleton-loader class="mx-auto" max-width="300" type="card"></v-skeleton-loader>

</v-sheet>

<v-card-text class="nopadding" v-else>

<v-row justify="space-between">

<v-col cols="12" sm="4">

<v-card style="min-height: 220px;" outlined>

<v-treeview

:items="permissionTreeData"

activatable

color="warning"

return-object

hoverable

transition

@update:active="clickTree"

></v-treeview>

</v-card>

</v-col>

<v-divider vertical></v-divider>

<v-col class="d-flex text-center">

<v-scroll-y-transition mode="out-in">

<v-card style="width: 100%; border: none;" outlined>

<!-- <p style="background-color: #000; width: 100%;">123</p> -->

<v-data-table

:options.sync="options"

:headers="headers"

:items="records"

:server-items-length="total"

disable-sort

class="elevation-1"

>

<template v-slot:top>

<v-toolbar flat color="white">

<v-toolbar-title>权限列表</v-toolbar-title>

<v-divider class="mx-6" inset vertical></v-divider>

<!-- 自定义组件。parentPermission传递数据。reload监听事件,子组件用$emit('reload')提交 -->

<CreatePermission :parentPermission="selectedTree" @reload="reload"></CreatePermission>

<v-spacer></v-spacer>

</v-toolbar>

</template>

<template v-slot:item.enabled="{item}">

<Value-Mapper :map="{true: '是', false: '否'}" :mapKey="item.enabled"></Value-Mapper>

</template>

<template v-slot:item.type="{item}">

<Value-Mapper :map="{'button': '按钮', 'menu': '菜单'}" :mapKey="item.type"></Value-Mapper>

</template>

<template v-slot:item.actions="{ item }">

<!-- 自定义组件。updateParam传递要修改的数据。reload监听事件,子组件用$emit('reload')提交 -->

<UpdatePermission :submitParam="item" @reload="reload"></UpdatePermission>

<delete-hint icon="mdi-delete" :func="() => { return deleteOne(item.id) }"></delete-hint>

</template>

</v-data-table>

</v-card>

</v-scroll-y-transition>

</v-col>

</v-row>

</v-card-text>

</v-card>

</template>

<script>

import { convert_tree } from "@/utils/tree";

export default {

data: () => ({

loading: false,

permissionTreeData: [],

selectedTree: {

id: 0,

name: "",

},

total: 0,

options: {},

records: [],

queryParam: {

pageNum: 0,

pageSize: 0,

parentId: 1,

},

headers: [

{

text: "权限名称",

align: "start",

sortable: false,

value: "name",

},

{ text: "权限码", value: "code" },

{ text: "类型", value: "type" },

{ text: "启用", value: "enabled", sortable: false },

{ text: "操作", value: "actions", sortable: false },

],

}),

watch: {

options: {

handler(newval, oldVal) {

this.queryParam.pageNum = this.options.page;

this.queryParam.pageSize = this.options.itemsPerPage;

this.page();

},

deep: true,

},

},

created() {

this.reload();

},

methods: {

reload() {

this.getAllPermission();

this.page();

},

clickTree(array) {

if (array && array.length > 0) {

const clickedTree = array[0];

this.selectedTree.id = clickedTree.id;

this.selectedTree.name = clickedTree.name;

this.queryParam.parentId = clickedTree.id;

} else {

this.selectedTree.id = 0;

this.queryParam.parentId = "";

this.selectedTree.name = "";

}

this.page();

},

// 获取所有的权限

getAllPermission() {

this.$http

.get("/user/api/v1/permission/listAll")

.then((res) => {

this.permissionTreeData = convert_tree(res.data, null);

})

.catch((error) => {});

},

page() {

this.$http

.post("/user/api/v1/permission/page", this.queryParam)

.then((res) => {

this.loading = true;

this.total = res.data.total;

this.records = res.data.records;

})

.catch((error) => {});

},

deleteOne(id) {

const url = "/user/api/v1/permission/delete/" + id;

this.$http

.post(url)

.then(async (res) => {

if (res.success) {

res.msg = "删除成功!";

this.page();

this.getAllPermission();

}

this.$store.commit("SET_SNACK", res);

return true;

})

.catch((err) => {

this.$store.commit("SET_SNACK", err);

});

},

},

components: {

CreatePermission: () => import("../component/permissions/CreatePermission"),

UpdatePermission: () => import("../component/permissions/UpdatePermission"),

DeleteHint: () => import("../component/DeleteHint"),

ValueMapper: () => import("../component/ValueMapper"),

},

};

</script>

子组件:CreatePermission.vue

<template>

<div id="createRule">

<v-icon title="创建权限" class="ml-4" v-text="icon" @click.stop="openDialog" />

<v-dialog v-model="dialog" fullscreen hide-overlay transition="dialog-bottom-transition">

<v-card>

<v-card-title>

<span class="headline">创建权限</span>

</v-card-title>

<v-card-text>

<v-form v-model="valid" :lazy-validation="lazy">

<v-container>

<v-row>

<v-col cols="12" sm="6" md="6">

<v-text-field

v-model="submitParam.name"

:rules="nameRules"

autofocus

label="*权限名称"

></v-text-field>

</v-col>

<v-col cols="12" sm="6" md="6">

<v-text-field v-model="submitParam.code" :rules="nameRules" label="*权限码"></v-text-field>

</v-col>

<v-col cols="12" sm="6" md="6">

<v-select v-model="submitParam.type" :items="typeOptions" label="*类型"></v-select>

</v-col>

<v-col cols="12" sm="6" md="6">

<v-select v-model="submitParam.enabled" :items="enabledOptions" label="*启用"></v-select>

</v-col>

<v-col cols="12" sm="12" md="6">

<v-text-field v-model="parentPermission.name" readonly="true" label="父权限"></v-text-field>

</v-col>

<v-col cols="12" sm="12" md="6">

<v-textarea v-model="submitParam.description" outlined label="描述" rows="2"></v-textarea>

</v-col>

</v-row>

</v-container>

</v-form>

</v-card-text>

<v-card-actions>

<v-spacer></v-spacer>

<v-btn color="blue darken-1" text @click="closeDialog">取消</v-btn>

<v-btn color="blue darken-1" text :disabled="!valid" @click="doSubmit">保存</v-btn>

</v-card-actions>

</v-card>

</v-dialog>

</div>

</template>

<script>

import { required } from "@/utils/validate";

export default {

data: () => ({

valid: true,

lazy: false,

icon: "mdi-plus-thick",

dialog: false,

submitParam: {

name: "",

code: "",

type: "menu",

enabled: true,

parentId: 0,

description: ""

},

enabledOptions: [

{ text: "是", value: true },

{ text: "否", value: false }

],

typeOptions: [

{ text: "按钮", value: "button" },

{ text: "菜单", value: "menu" }

],

nameRules: [required]

}),

props: {

parentPermission: {

id: 0,

name: ""

}

},

created() {

},

methods: {

// 打开对话框

openDialog() {

if (!this.parentPermission.name) {

this.$store.commit("SET_SNACK", {

msg: "请选择父权限!",

success: false

});

return;

}

this.submitParam.parentId = this.parentPermission.id;

this.dialog = true;

// 参数

this.submitParam.name = "";

this.submitParam.code = "";

this.submitParam.enabled = true;

this.submitParam.description = "";

this.submitParam.type = "menu";

},

// 关闭对话框

closeDialog() {

this.dialog = false;

},

// 创建

doSubmit() {

this.$http

.post("/user/api/v1/permission/save", this.submitParam)

.then(res => {

if (res.success) {

res.msg = "权限创建成功";

}

this.$store.commit("SET_SNACK", res);

// 刷新权限列表(向父组件提交事件)

this.$emit("reload");

})

.catch(error => {});

this.closeDialog();

}

}

};

</script>

 子组件UpdatePermission.vue (接收父组件传递的对象声明是Object)

<template>

<div id="updatePermission" style="display: inline-block">

<v-icon title="更新权限" class="ml-4" @click.stop="openDialog" v-text="icon"/>

<v-dialog v-model="dialog" fullscreen hide-overlay transition="dialog-bottom-transition">

<v-card>

<v-card-title>

<span class="headline">更新权限</span>

</v-card-title>

<v-card-text>

<v-form v-model="valid" :lazy-validation="lazy">

<v-container>

<v-row>

<v-col cols="12" sm="6" md="6">

<v-text-field

v-model="submitParam.name"

:rules="nameRules"

autofocus

label="*权限名称"

></v-text-field>

</v-col>

<v-col cols="12" sm="6" md="6">

<v-text-field v-model="submitParam.code" :rules="nameRules" label="*权限码"></v-text-field>

</v-col>

<v-col cols="12" sm="6" md="6">

<v-select v-model="submitParam.type" :items="typeOptions" label="*类型"></v-select>

</v-col>

<v-col cols="12" sm="6" md="6">

<v-select v-model="submitParam.enabled" :items="enabledOptions" label="*启用"></v-select>

</v-col>

<v-col cols="12" sm="12" md="6">

<v-textarea v-model="submitParam.description" outlined label="描述" rows="2"></v-textarea>

</v-col>

</v-row>

</v-container>

</v-form>

</v-card-text>

<v-card-actions>

<v-spacer></v-spacer>

<v-btn color="blue darken-1" text @click="closeDialog">取消</v-btn>

<v-btn color="blue darken-1" text :disabled="!valid" @click="doSubmit">保存</v-btn>

</v-card-actions>

</v-card>

</v-dialog>

</div>

</template>

<script>

import { required } from "@/utils/validate";

export default {

data: () => ({

icon: 'mdi-pencil',

valid: true,

lazy: false,

dialog: false,

enabledOptions: [

{ text: "是", value: true },

{ text: "否", value: false }

],

typeOptions: [

{ text: "按钮", value: "button" },

{ text: "菜单", value: "menu" }

],

nameRules: [required]

}),

props: {

submitParam: Object

},

created() {},

methods: {

// 打开对话框

openDialog() {

// 每次进入更新页面时,将roleProp进行深拷贝,防止在子组件修改引起父组件内容变化

Object.assign({}, this.submitParam);

this.dialog = true;

},

// 关闭对话框

closeDialog() {

this.dialog = false;

},

// 创建

doSubmit() {

this.$http

.post("/user/api/v1/permission/update", this.submitParam)

.then(res => {

if (res.success) {

res.msg = "权限修改成功";

}

this.$store.commit("SET_SNACK", res);

// 刷新权限列表(向父组件提交事件)

this.$emit("reload");

})

.catch(error => {});

this.closeDialog();

}

}

};

</script>

以上是 vue组件、自定义指令、路由 的全部内容, 来源链接: utcz.com/z/377341.html

回到顶部