目錄javascript
官方演示:https://d2admin.fairyever.com/#/index
官方文檔:https://doc.d2admin.fairyever.com/zh/css
npm install -g @d2-admin/d2-admin-cli
d2 create 項目名稱
或者html
d2 c 項目名稱
以後選擇 簡化版前端
進入項目文件夾vue
npm i
npm run serve
d2-container
是 D2Admin 構建頁面最重要的組件,在模板頁面中記得要用該標籤包裹,該標籤針對不樣式的頁面內置不一樣的類型,詳見官方文檔// .env.development # 開發環境 # 頁面 title 前綴 VUE_APP_TITLE=ZAdmin
修改完成後重啓項目即生效java
首頁LOGL:
替換 .\public\image\theme\d2\logo\all.pngios
網頁 ico 小圖標:
替換 .\public\icon.icogit
v-charts 官方文檔:https://v-charts.js.org/#/github
npm i v-charts echarts -S
// main.js import Vue from 'vue' import VCharts from 'v-charts' import App from './App.vue' Vue.use(VCharts) new Vue({ el: '#app', render: h => h(App) })
以折線圖爲例,其餘類型詳見官方文檔。vue-router
爲了美觀,將其包含在elementUI的Card組件中。
<template> <el-card> <ve-line :data="chartData"></ve-line> </el-card> </template> <script> export default { data: function () { return { chartData: { columns: ['日期', '訪問用戶', '下單用戶', '下單率'], rows: [ { '日期': '1/1', '訪問用戶': 1393, '下單用戶': 1093, '下單率': 0.32 }, { '日期': '1/2', '訪問用戶': 3530, '下單用戶': 3230, '下單率': 0.26 }, { '日期': '1/3', '訪問用戶': 2923, '下單用戶': 2623, '下單率': 0.76 }, { '日期': '1/4', '訪問用戶': 1723, '下單用戶': 1423, '下單率': 0.49 }, { '日期': '1/5', '訪問用戶': 3792, '下單用戶': 3492, '下單率': 0.323 }, { '日期': '1/6', '訪問用戶': 4593, '下單用戶': 4293, '下單率': 0.78 } ] } } } } </script>
lazy
屬性的影響(初始化時有個渲染的過程),若是沒有開啓延遲渲染,會只渲染第一個標籤頁內容,所以須要將 lazy
設置爲 true
從第二 tbgs 標籤頁起,將lazy
屬性設置爲 true
<el-tabs v-model="activeName" @tab-click="handleClick"> <el-tab-pane label="用戶管理" name="first"> <ve-line :data="chartData"></ve-line> </el-tab-pane> <el-tab-pane :lazy="true" label="配置管理" name="second"> <ve-histogram :data="chartDataHis"></ve-histogram> </el-tab-pane> <el-tab-pane :lazy="true" label="角色管理" name="third">角色管理</el-tab-pane> <el-tab-pane :lazy="true" label="定時任務補償" name="fourth">定時任務補償</el-tab-pane> </el-tabs>
D2 CURD是一個基於Vue.js 和 Element UI的表格組件,封裝了經常使用的表格操做,使用該組件能夠快速在頁面上處理表格數據。
詳見官方文檔
npm i element-ui @d2-projects/d2-crud -S
import Vue from 'vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import D2Crud from '@d2-projects/d2-crud' Vue.use(ElementUI) Vue.use(D2Crud) new Vue({ el: '#app', render: h => h(App) })
此處爲帶有新增和分頁功能的表格,但CURD2.x中分頁功能的數據須要從後臺獲取,所以這裏只簡單演示了表格的樣式。
columns
: 表格的列屬性
data
: 表格的數據
add-title
: 彈出新增模態框的標題
pagination
: 開啓分頁功能
loading
: 加載數據時會有加載中的樣式
slot="header"
: 表格的頭部信息,自定義樣式(如:標題,按鈕,輸入框等)能夠顯示在表格的頂部(2.x新增,更好的替代了1.x中的 title
屬性)
BusinessTable1List
: 後臺數據API,獲取後臺數據,這裏只是頁面展現,採用固定的數據,未使用API接口。
<template> <d2-container> <el-card> <d2-crud ref="d2Crud" :columns="columns" :data="data" add-title="添加數據" :add-template="addTemplate" :form-options="formOptions" :pagination="pagination" :loading="loading" @pagination-current-change="paginationCurrentChange" @dialog-open="handleDialogOpen" @row-add="handleRowAdd" @dialog-cancel="handleDialogCancel"> <el-button slot="header" style="margin-bottom: 5px" @click="addRow"><i class="fa fa-plus" aria-hidden="true"></i> 新增</el-button> <el-button slot="header" style="margin-bottom: 5px" @click="addRowWithNewTemplate">使用自定義模板新增</el-button> <el-input slot="header" style="margin-bottom: 5px" placeholder="商品名稱" suffix-icon="el-icon-search"> </el-input> <el-input slot="header" style="margin-bottom: 5px" placeholder="最低價格" suffix-icon="el-icon-caret-bottom"> </el-input> <el-input slot="header" style="margin-bottom: 5px" placeholder="最高價格" suffix-icon="el-icon-caret-top"> </el-input> <el-button slot="header" style="margin-bottom: 5px"><i class="el-icon-search"></i> 搜索</el-button> </d2-crud> </el-card> </d2-container> </template> <script> // import { BusinessTable1List } from '@api/demo.business.table.1' export default { data () { return { columns: [ { title: '日期', key: 'date' }, { title: '姓名', key: 'name' }, { title: '地址', key: 'address' } ], data: [ { date: '2016-05-02', name: '王小虎', address: '上海市普陀區金沙江路 1518 弄' }, { date: '2016-05-04', name: '王小虎', address: '上海市普陀區金沙江路 1517 弄' }, { date: '2016-05-01', name: '王小虎', address: '上海市普陀區金沙江路 1519 弄' }, { date: '2016-05-03', name: '王小虎', address: '上海市普陀區金沙江路 1516 弄' } ], addTemplate: { date: { title: '日期', value: '2016-05-05' }, name: { title: '姓名', value: '王小虎' }, address: { title: '地址', value: '上海市普陀區金沙江路 1520 弄' } }, formOptions: { labelWidth: '80px', labelPosition: 'left', saveLoading: false }, loading: false, pagination: { currentPage: 1, pageSize: 5, total: 100 } } }, mounted () { this.fetchData() }, methods: { handleDialogOpen ({ mode }) { this.$message({ message: '打開模態框,模式爲:' + mode, type: 'success' }) }, // 普通的新增 addRow () { this.$refs.d2Crud.showDialog({ mode: 'add' }) }, // 傳入自定義模板的新增 addRowWithNewTemplate () { this.$refs.d2Crud.showDialog({ mode: 'add', template: { name: { title: '姓名', value: '' }, value1: { title: '新屬性1', value: '' }, value2: { title: '新屬性2', value: '' } } }) }, handleRowAdd (row, done) { this.formOptions.saveLoading = true setTimeout(() => { console.log(row) this.$message({ message: '保存成功', type: 'success' }); // done能夠傳入一個對象來修改提交的某個字段 done({ address: '我是經過done事件傳入的數據!' }) this.formOptions.saveLoading = false }, 300) }, handleDialogCancel (done) { this.$message({ message: '取消保存', type: 'warning' }); done() }, paginationCurrentChange (currentPage) { this.pagination.currentPage = currentPage this.fetchData() }, fetchData () { this.loading = true BusinessTable1List({ ...this.pagination }).then(res => { this.data = res.list this.pagination.total = res.page.total this.loading = false }).catch(err => { console.log('err', err) this.loading = false }) } } } </script> <style scoped> .el-input { width: 200px; margin-right: 10px; } </style>
兩個概念:
注意:建立調用API的URL時,需帶上前綴
/api/xxx
// /mock/api/product.js const Mock = require('mockjs') const Qs = require('qs') const Random = Mock.Random const titleList = ['男士上衣', 'T恤', '襯衫', '牛仔褲', '皮衣', '短裙', '女士襯衫', '長裙', '羽絨服', '秋褲', '軍大衣']; const getProductList = function (params) { // 獲取所有商品列表或者分頁商品列表 let products = [] for (let i = 0; i < 100; i++) { let product = { id: i + 1, title: titleList[Math.floor(Math.random()*titleList.length)], price: Random.float(10, 100).toFixed(2), stock: Random.integer(10, 100), saleCount: Random.integer(0, 90), isSale: Random.integer(0, 1), createTime: Random.datetime(), imgUrl: Random.dataImage('60x60', 'ZAdmin-' + (i + 1)), showEditButton: true, showRemoveButton: true } products.push(product) } let total = products.length; if (params.body && products.length) { let pageInfo = JSON.parse(Qs.parse(params).body); let start = (pageInfo.currentPage - 1) * pageInfo.pageSize; let end = pageInfo.currentPage * pageInfo.pageSize; products = products.slice(start, end); console.log(`start: ${start} end: ${end} products: ${products} total: ${total}`); } return { products, total } } // 經過post請求,使用參數的token判斷是否登陸,並經過參數判斷獲取所有評論列表或者獲取分頁評論列表 const getProductComments = function (params) { let data = JSON.parse(Qs.parse(params).body); console.log('api-comments-params: ', data); if (!data.token) { return { status: 401, msg: 'token錯誤,須要登陸', data: {} } } let comments = [] for (let i = 0; i < 120; i++) { let comment = { id: i + 1, name: Random.cname(), itemScore: Random.integer(1, 5), serviceScore: Random.integer(1, 5), content: Random.ctitle(10, 50), createTime: Random.datetime(), showEditButton: true, showRemoveButton: true } comments.push(comment) } let total = comments.length; if (data.page) { let pageInfo = data.page; let start = (pageInfo.currentPage - 1) * pageInfo.pageSize; let end = pageInfo.currentPage * pageInfo.pageSize; comments = comments.slice(start, end); console.log(`currentPage: ${pageInfo.currentPage} start: ${start} end: ${end} comments: ${comments} total: ${total}`); } return { status: 0, msg: '成功', data: comments, total: total } } // 建立API的URL,讓vue經過URL獲取數據 Mock.mock('/api/products', 'get', getProductList); // 獲取全部商品 Mock.mock('/api/products', 'post', getProductList); // 獲取分頁商品數據 Mock.mock('/api/productComments', 'post', getProductComments)
注意:
引用 plugin/axios 中的request
方法發起請求
發起請求的URL,不須要帶/api/
前綴
// /src/api/pruduct.js import request from '@/plugin/axios' export function getProducts (data) { return request({ url: '/products', method: 'get', data }) } export function getPageProducts (data) { return request({ url: '/products', method: 'post', data }) } export function getProductComments (data) { return request({ url: '/productComments', method: 'post', data }) }
注意:
namespaced: true
,加了命名空間操做,將該文件直接加在了product文件夾下,被調用時,中間省去了一層 modules,爲 product/product// \src\store\modules\product\modules\product.js import { getProducts, getPageProducts, getProductComments } from '@/api/product' export default { namespaced: true, actions: { getProducts ({ commit }, payload) { commit('getProducts', payload) }, getPageProducts ({ commit }, payload) { commit('getPageProducts', payload) }, getComments ({ commit }, payload) { commit('getComments', payload) } }, mutations: { getProducts (state, payload) { state.loading = true; getProducts(payload).then(res => { console.log('getProducts: ', res); state.loading = false; state.products = res.products; }).catch( err => { console.log('err', err) state.loading = false }) }, getComments (state, payload) { // 獲取所有評價信息或者分頁評價信息 state.loading = true; console.log(payload); getProductComments(payload).then(res => { console.log('getComments: ', res); if (res.status == 0) { state.loading = false; state.total = res.total; if (payload.page) { state.pageComments = res.data; } else { state.comments = res.data; } } }).catch( err => { console.log('err', err) state.loading = false }) }, getPageProducts (state, payload) { console.log('page_params: ', payload); state.loading = true; getPageProducts(payload).then(res => { console.log('res: ', res); state.loading = false; state.total = res.total; state.pageProducts = res.products; }).catch( err => { console.log('err', err) state.loading = false }) } }, state: { products: [], pageProducts: [], comments: [], pageComments: [], loading: false, total: 0 }, getters: { products: (state) => { return state.products }, pageProducts: (state) => { return state.pageProducts }, comments: (state) => { return state.comments }, pageComments: (state) => { return state.pageComments }, loading: (state) => { return state.loading }, total: (state) => { return state.total } } }
// \src\store\index.js import Vue from 'vue' import Vuex from 'vuex' import d2admin from './modules/d2admin' import product from './modules/product' Vue.use(Vuex) export default new Vuex.Store({ modules: { d2admin, product } })
注意:
product/product
cookies.get('token')
能夠獲取當前用戶的token自定義組件,用於顯示圖片:
// \src\views\product\all\MyImage.vue <template> <div> <img :src="value" alt="商品圖片"> </div> </template> <script> export default { props: { value: '' // 必須叫value } } </script>
基於 CURD2.x 的本地增刪改查 以及 分頁功能實現
<template> <d2-container> <template slot="header"> <div class="flex-header"> <div class="header-title"> 商品列表 </div> <div> <el-input v-model="searchWords" placeholder="商品名稱" suffix-icon="el-icon-search"></el-input> <el-input v-model="searchMinPrice" placeholder="最低價格" suffix-icon="el-icon-caret-bottom"> </el-input> <el-input v-model="searchMaxPrice" placeholder="最高價格" suffix-icon="el-icon-caret-top"> </el-input> <el-button @click="onSearch"><i class="el-icon-search"></i> 搜索</el-button> <el-button type="primary" @click="addRow"><i class="fa fa-plus" aria-hidden="true"></i> 添加商品</el-button> </div> </div> </template> <el-card> <d2-crud ref="d2Crud" :columns="columns" :data="filterProducts.length ? filterProducts : products" add-title="添加商品" :add-template="addTemplate" :rowHandle="rowHandle" edit-title="編輯商品信息" :edit-template="editTemplate" :form-options="formOptions" :loading="loading" :pagination="pagination" @pagination-current-change="paginationCurrentChange" @row-add="handleRowAdd" @row-edit="handleRowEdit" @row-remove="handleRowRemove" @dialog-cancel="handleDialogCancel"> </d2-crud> </el-card> <template slot="footer">ZAdmin Created By <a href="">@ZBW</a> for D2-admin</template> </d2-container> </template> <script> // 導入自定義用於顯示圖片的組件 import MyImage from "./MyImage" export default { data () { return { searchWords: '', searchMinPrice: '', searchMaxPrice: '', filterProducts: [], columns: [ { title: 'ID', key: 'id', width: '40' }, { title: '名稱', key: 'title' }, { title: '價格', key: 'price', width: '80' }, { title: '庫存', key: 'stock', width: '80' }, { title: '銷量', key: 'saleCount', width: '80' }, { title: '是否上架', key: 'isSale', component: { name: 'el-select', options: [ { value: 0, label: '否' }, { value: 1, label: '是' } ] } }, { title: '圖片', key: 'imgUrl', width: '120', component: { name: MyImage } }, { title: '建立時間', key: 'createTime' } ], addTemplate: { createTime: { title: '建立日期', value: '2019-06-01', component: { name: 'el-date-picker', span: 12 } }, isSale: { title: '是否上架', value: 0, component: { name: 'el-select', options: [ { value: 0, label: '否' }, { value: 1, label: '是' } ], span: 12 } }, title: { title: '名稱', value: '', span: 24 }, price: { title: '價格', value: '', span: 24 } }, rowHandle: { columnHeader: '操做', edit: { icon: 'el-icon-edit', text: '編輯', size: 'mini', show (index, row) { if (row.showEditButton) { return true } return false }, disabled (index, row) { if (row.forbidEdit) { return true } return false } }, remove: { icon: 'el-icon-delete', size: 'mini', text: '刪除', fixed: 'right', confirm: true, show (index, row) { if (row.showRemoveButton) { return true } return false } } }, editTemplate: { createTime: { title: '建立日期', component: { name: 'el-date-picker', span: 12 } }, isSale: { title: '是否上架', component: { name: 'el-select', options: [ { value: 0, label: '否' }, { value: 1, label: '是' } ], span: 12 } }, title: { title: '名稱', span: 24 }, price: { title: '價格', span: 24 }, forbidEdit: { title: '禁用按鈕', value: false, component: { show: false } }, showEditButton: { title: '顯示按鈕', value: true, component: { show: false } } }, formOptions: { labelWidth: '80px', labelPosition: 'left', saveLoading: false }, } }, created() { // 調用vuex中方法時,須要加上命名空間 product/product this.fetchData(); this.$store.commit('product/product/getProducts'); // 請求所有商品列表 }, computed: { all_products() { // 所有商品列表,用於搜索 return this.$store.getters['product/product/products']; }, products() { // 當前分頁的商品列表 // 取 vuex 中數據時,須要加上命名空間 product/product return this.$store.getters['product/product/pageProducts'] }, loading() { return this.$store.getters['product/product/loading'] }, pagination() { return { currentPage: 1, pageSize: 5, background: true, total: this.$store.getters['product/product/total'] } } }, methods: { onSearch() { this.filterProducts = this.all_products.filter(p => { if (this.searchWords){ return p.price >= parseFloat(this.searchMinPrice) && p.price <= parseFloat(this.searchMaxPrice) && p.title.includes(this.searchWords); } else { return p.price >= parseFloat(this.searchMinPrice) && p.price <= parseFloat(this.searchMaxPrice); } }) console.log('filterProducts: ', this.filterProducts); }, addRow () { // 點擊新增後,以「添加」模式打開模態框 this.$refs.d2Crud.showDialog({ mode: 'add' }) }, handleRowAdd (row, done) { // 點擊確認添加後觸發的事件,能夠將數據傳遞到後臺,保存至數據庫中 this.formOptions.saveLoading = true setTimeout(() => { // row 是表單提交的內容 console.log(row) this.$message({ message: '保存成功', type: 'success' }); // done能夠傳入一個對象來修改提交的某個字段 done({ price: '你雖然提交了 可是我能在這修改你顯示在頁面的內容!' }) this.formOptions.saveLoading = false }, 300) }, handleRowEdit ({ index, row }, done) { // 點擊確認修改後觸發的事件,能夠將數據傳遞到後臺,保存至數據庫中 // index 是當前編輯行的索引, row 是當前編輯行的數據, done 用於控制編輯成功,能夠在 done() 以前加入本身的邏輯代碼 this.formOptions.saveLoading = true setTimeout(() => { console.log(index) console.log(row) this.$message({ message: '編輯成功', type: 'success' }) // done能夠傳入一個對象來修改提交的某個字段 // done()能夠傳入包含表單字段的對象來覆蓋提交的數據,done(false) 能夠取消編輯 done({ price: '你雖然在後臺修改了價格,可是我能在這控制你在前臺顯示的內容' }) this.formOptions.saveLoading = false }, 300) }, handleRowRemove ({ index, row }, done) { // 與編輯相似 setTimeout(() => { console.log(index) console.log(row) this.$message({ message: '刪除成功', type: 'success' }) done() }, 300) }, handleDialogCancel (done) { // 關閉模態框執行的事件,並能夠自定義執行done函數 this.$message({ message: '取消保存', type: 'warning' }); done() }, paginationCurrentChange (currentPage) { // 分頁頁碼發生改變觸發的事件 this.pagination.currentPage = currentPage this.fetchData() }, fetchData () { // 點擊分頁按鈕後,動態請求該頁所需的數據 this.$store.commit('product/product/getPageProducts', {pageSize: this.pagination.pageSize, currentPage: this.pagination.currentPage}) } } } </script> <style scoped> .flex-header { display: flex; justify-content: space-between; align-items:center } .header-title { min-width: 4rem; } .flex-header .el-input { width: 200px; margin-right: 10px; } </style>
經測試,各功能所有正常。
用戶權限:
控制權限的幾種方式:
// src\mock\api\sys.login.js import Mock from 'mockjs' const userDB = [ { username: 'admin', password: 'admin', uuid: 'admin-uuid', name: '管理員', role: 'admin' }, { username: 'editor', password: 'editor', uuid: 'editor-uuid', name: '編輯', role: 'editor' }, { username: 'user1', password: 'user1', uuid: 'user1-uuid', name: '用戶1', role: 'user' } ] // 爲不一樣的用戶角色(role)劃分不一樣的權限 const permissions = { admin: [ { path: '/index', operations: ['modify', 'delete', 'add'] }, { path: '/product', operations: ['modify', 'delete', 'add'] }, { path: '/order', operations: ['modify', 'delete', 'add'] }, { path: '/permission', operations: ['modify', 'delete', 'add'] }, { path: '/users', operations: ['modify', 'delete', 'add'] }, { path: '/menu', operations: ['modify', 'delete', 'add'] }, { path: '/stuff', operations: ['modify', 'delete', 'add'] }, { path: '/data-charts', operations: ['modify', 'delete', 'add'] }, { path: '/log' } ], editor: [{ path: '/index', operations: ['modify', 'add'] }, { path: '/product', operations: ['modify', 'add'] }, { path: '/permission', operations: ['modify'] }, { path: '/order', operations: ['modify', 'add'] } ], user: [{ path: '/index', operations: ['add'] }, { path: '/product', operations: ['add'] }, { path: '/order', operations: ['add'] } ] } Mock.mock('/api/userInfo', 'post', ({ body }) => { const bodyObj = JSON.parse(body) if (!bodyObj.token) { return { status: 401, msg: 'token錯誤,須要登陸', data: {} } } const user = userDB.find(e => e.uuid === bodyObj.uuid) if (user) { return { status: 0, msg: '成功', data: { username: user.username, name: user.name, uuid: user.uuid, role: user.role, permissions: permissions[user.role] } } } else { return { status: 401, msg: '用戶名或密碼錯誤', data: {} } } }) // 判斷是否登陸 export default [ { path: '/api/login', method: 'post', handle ({ body }) { const user = userDB.find(e => e.username === body.username && e.password === body.password) if (user) { return { code: 0, msg: '登陸成功', data: { ...user, token: '8dfhassad0asdjwoeiruty' } } } else { return { code: 401, msg: '用戶名或密碼錯誤', data: {} } } } } ]
// src\api\sys.login.js import request from '@/plugin/axios' export function AccountLogin (data) { return request({ url: '/login', method: 'post', data }) } export function getUserInfo (data) { return request({ url: '/userinfo', method: 'post', data }) }
// \src\router\index.js import Vue from 'vue' import VueRouter from 'vue-router' // 進度條 import NProgress from 'nprogress' import 'nprogress/nprogress.css' import store from '@/store/index' import util from '@/libs/util.js' // 路由數據 import routes from './routes' Vue.use(VueRouter) // 導出路由 在 main.js 裏使用 const router = new VueRouter({ routes }) /** * 路由攔截 * 權限驗證 */ router.beforeEach((to, from, next) => { // 進度條 NProgress.start() // 關閉搜索面板 store.commit('d2admin/search/set', false) // 獲取路由的meta信息,判斷當前頁面是否須要驗證(登陸驗證,權限驗證等等) // 驗證當前路由全部的匹配中是否須要有登陸驗證的 if (to.matched.some(r => r.meta.auth)) { // 這裏暫時將cookie裏是否存有token做爲驗證是否登陸的條件 // 請根據自身業務須要修改 const token = util.cookies.get('token') // 獲取cookie中的token if (token && token !== 'undefined') { // 用戶已登陸,從sessionStorage中獲取用戶權限信息 let userInfo = JSON.parse(sessionStorage.getItem('userInfo')); console.log(token, userInfo); if (userInfo) { let permissions = userInfo.permissions; console.log('router permissions: ', permissions); let allow = false; permissions.forEach(p => { let item = router.match(p.path) // 找到權限列表中匹配改條路由對應的權限信息 if (item) { // 匹配到路由後,將權限信息添加在路由的meta中 item.meta.operations = p.operations } // 徹底匹配或者前綴匹配 console.log('path: ', to.path, p.path); if (p.path === to.path || to.path.startsWith(p.path)) { allow = true; } }) // 根據allow判斷是否能夠跳轉到下一個頁面 if (allow) { next() } else { next({ name: '401' }) } } else { next() } } else { // 沒有登陸的時候跳轉到登陸界面 // 攜帶上登錄成功以後須要跳轉的頁面完整路徑 next({ name: 'login', query: { redirect: to.fullPath } }) // https://github.com/d2-projects/d2-admin/issues/138 NProgress.done() } } else { // 不須要身份校驗 直接經過 next() } }) router.afterEach(to => { // 進度條 NProgress.done() // 多頁控制 打開新的頁面 store.dispatch('d2admin/page/open', to) // 更改標題 util.title(to.meta.title) }) export default router
this.$route.meta.[operations]
)的 meta 中的權限信息(如:增刪改查),得出具體某個頁面的操做權限信息,針對不一樣用戶權限作出不一樣的操做。注意:
// src\store\modules\d2admin\modules\account.js import { Message, MessageBox } from 'element-ui' import util from '@/libs/util.js' import router from '@/router' import { AccountLogin, getUserInfo } from '@api/sys.login' export default { namespaced: true, actions: { /** * @description 登陸 * @param {Object} param context * @param {Object} param username {String} 用戶帳號 * @param {Object} param password {String} 密碼 * @param {Object} param route {Object} 登陸成功後定向的路由對象 任何 vue-router 支持的格式 */ login ({ dispatch }, { username = '', password = '' } = {}) { return new Promise((resolve, reject) => { // 開始請求登陸接口 AccountLogin({ username, password }) .then(async res => { // 設置 cookie 必定要存 uuid 和 token 兩個 cookie // 整個系統依賴這兩個數據進行校驗和存儲 // uuid 是用戶身份惟一標識 用戶註冊的時候肯定 而且不可改變 不可重複 // token 表明用戶當前登陸狀態 建議在網絡請求中攜帶 token // 若有必要 token 須要定時更新,默認保存一天 util.cookies.set('uuid', res.uuid) util.cookies.set('token', res.token) // 登陸成功後,加載用戶信息和權限信息 getUserInfo({ uuid: res.uuid, token: res.token }).then(res => { console.log('get user info: ', res); if (res.status == 401) { return; } else { let userInfo = res.data; sessionStorage.setItem('userInfo', JSON.stringify(userInfo)); } }) // 設置 vuex 用戶信息 await dispatch('d2admin/user/set', { name: res.name }, { root: true }) // 用戶登陸後從持久化數據加載一系列的設置 await dispatch('load') // 結束 resolve() }) .catch(err => { console.log('err: ', err) reject(err) }) }) }, /** * @description 註銷用戶並返回登陸頁面 * @param {Object} param context * @param {Object} param confirm {Boolean} 是否須要確認 */ logout ({ commit, dispatch }, { confirm = false } = {}) { /** * @description 註銷 */ async function logout () { // 刪除cookie util.cookies.remove('token') util.cookies.remove('uuid') // 退出後將sessionStorage中的用戶信息刪除 sessionStorage.removeItem('userInfo') // 清空 vuex 用戶信息 await dispatch('d2admin/user/set', {}, { root: true }) // 跳轉路由 router.push({ name: 'login' }) } // 判斷是否須要確認 if (confirm) { commit('d2admin/gray/set', true, { root: true }) MessageBox.confirm('註銷當前帳戶嗎? 打開的標籤頁和用戶設置將會被保存。', '確認操做', { confirmButtonText: '肯定註銷', cancelButtonText: '放棄', type: 'warning' }) .then(() => { commit('d2admin/gray/set', false, { root: true }) logout() }) .catch(() => { commit('d2admin/gray/set', false, { root: true }) Message({ message: '放棄註銷用戶' }) }) } else { logout() } }, /** * @description 用戶登陸後從持久化數據加載一系列的設置 * @param {Object} state vuex state */ load ({ dispatch }) { return new Promise(async resolve => { // DB -> store 加載用戶名 await dispatch('d2admin/user/load', null, { root: true }) // DB -> store 加載主題 await dispatch('d2admin/theme/load', null, { root: true }) // DB -> store 加載頁面過渡效果設置 await dispatch('d2admin/transition/load', null, { root: true }) // DB -> store 持久化數據加載上次退出時的多頁列表 await dispatch('d2admin/page/openedLoad', null, { root: true }) // DB -> store 持久化數據加載側邊欄摺疊狀態 await dispatch('d2admin/menu/asideCollapseLoad', null, { root: true }) // DB -> store 持久化數據加載全局尺寸 await dispatch('d2admin/size/load', null, { root: true }) // end resolve() }) } } }
// src\views\permission\index.vue <template> <d2-container> <template slot="header">頁面權限驗證</template> <el-card> <d2-crud ref="d2Crud" :columns="columns" :data="data" add-title="個人新增" edit-title="個人修改" :add-template="addTemplate" :edit-template="editTemplate" :rowHandle="rowHandle" :form-options="formOptions" @row-add="handleRowAdd" @row-edit="handleRowEdit" @row-remove="handleRowRemove" @dialog-cancel="handleDialogCancel"> <el-button slot="header" type="primary" style="margin-bottom: 5px;" @click="addRow">新增</el-button> </d2-crud> </el-card> <template slot="footer">ZAdmin Created By <a href="">@ZBW</a> for D2-admin</template> </d2-container> </template> <script> export default { data () { return { columns: [ { title: 'ID', key: 'id', width: 50 }, { title: '日期', key: 'date', width: 150 }, { title: '姓名', key: 'name' }, { title: '地址', key: 'address' }, { title: '會員', key: 'role', width: 100 } ], data: [ { id: 1, date: '2016-05-02', name: '王小虎', address: '上海市普陀區金沙江路 1518 弄', role: '普通會員', showEditButton: this.$route.meta.operations.includes('modify'), showRemoveButton: this.$route.meta.operations.includes('delete') }, { id: 2, date: '2016-05-04', name: '王小虎', address: '上海市普陀區金沙江路 1517 弄', role: '普通會員', showEditButton: this.$route.meta.operations.includes('modify'), showRemoveButton: this.$route.meta.operations.includes('delete') }, { id: 3, date: '2016-05-01', name: '王小虎', address: '上海市普陀區金沙江路 1519 弄', role: '普通會員', showEditButton: this.$route.meta.operations.includes('modify'), showRemoveButton: this.$route.meta.operations.includes('delete') }, { id: 4, date: '2016-05-03', name: '王小虎', address: '上海市普陀區金沙江路 1516 弄', role: '普通會員', showEditButton: this.$route.meta.operations.includes('modify'), showRemoveButton: this.$route.meta.operations.includes('delete') }, { id: 5, date: '2016-05-02', name: '王小虎', address: '上海市普陀區金沙江路 1518 弄', role: '普通會員', showEditButton: this.$route.meta.operations.includes('modify'), showRemoveButton: this.$route.meta.operations.includes('delete') } ], rowHandle: { columnHeader: '編輯表格', edit: { icon: 'el-icon-edit', text: '編輯', size: 'small', show (index, row) { if (row.showEditButton) { return true } return false }, disabled (index, row) { if (row.forbidEdit) { return true } return false } }, remove: { icon: 'el-icon-delete', size: 'small', fixed: 'right', confirm: true, show (index, row) { if (row.showRemoveButton) { return true } return false }, disabled (index, row) { if (row.forbidRemove) { return true } return false } } }, addTemplate: { date: { title: '日期', value: '2018-11-11', component: { name: 'el-date-picker', span: 12 } }, role: { title: '會員', value: '普通會員', component: { name: 'el-select', options: [ { value: '普通會員', label: '普通會員' }, { value: '金卡會員', label: '金卡會員' } ], span: 12 } }, name: { title: '姓名', value: '' }, address: { title: '地址', value: '' } }, editTemplate: { date: { title: '日期', value: '2018-11-11', component: { name: 'el-date-picker', span: 12 } }, role: { title: '會員', value: '普通會員', component: { name: 'el-select', options: [ { value: '普通會員', label: '普通會員' }, { value: '金卡會員', label: '金卡會員' } ], span: 12 } }, name: { title: '姓名', value: '' }, address: { title: '地址', value: '' }, forbidEdit: { title: '禁用按鈕', value: false, component: { show: false } }, showEditButton: { title: '顯示按鈕', value: true, component: { show: false } } }, formOptions: { labelWidth: '80px', labelPosition: 'left', saveLoading: false } } }, methods: { addRow () { this.$refs.d2Crud.showDialog({ mode: 'add' }) }, handleRowAdd (row, done) { this.formOptions.saveLoading = true setTimeout(() => { console.log(row) this.$message({ message: '添加成功', type: 'success' }); done({ showEditButton: this.$route.meta.operations.includes('modify'), showRemoveButton: this.$route.meta.operations.includes('delete') }) this.formOptions.saveLoading = false }, 300); }, handleRowEdit ({index, row}, done) { this.formOptions.saveLoading = true setTimeout(() => { console.log(index) console.log(row) this.$message({ message: '編輯成功', type: 'success' }) done() this.formOptions.saveLoading = false }, 300) }, handleDialogCancel (done) { this.$message({ message: '操做已取消', type: 'warning' }); done() }, handleRowRemove ({index, row}, done) { setTimeout(() => { console.log(index) console.log(row) this.$message({ message: '刪除成功', type: 'success' }) done() }, 300) } } } </script> <style scoped> </style>
admin:
editer:
user: