項目很點意思,感受很高超的樣子
先放下項目的github地址:https://github.com/tctangyanan/iReport
感謝各位偉大的程序員無私的分享本身的技術
老規矩,咱們會運行項目
頁面效果爲
先逐行分析代碼
先看main.js,引入了咱們須要的一些全局插件css
//main.js import Vue from 'vue' import 'normalize.css/normalize.css'// A modern alternative to CSS resets import App from './App' import router from './router' // import routes from './router/routes' import store from './vuex/store' import { sync } from 'vuex-router-sync' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import './styles/index.scss' // global css // 進度條 import NProgress from 'nprogress' // progress bar import 'nprogress/nprogress.css'// progress bar style import UtilsPlugin from './assets/utils' import HttpPlugin from './http/index' import VueParticles from 'vue-particles' // 粒子酷炫效果 import formatStyle from './filters/formatStyle.js' // import { Tag } from '../src/model/index' import './mock' // mock import VCharts from 'v-charts' Vue.config.productionTip = false Vue.use(ElementUI) // plugins Vue.use(UtilsPlugin) Vue.use(HttpPlugin) Vue.use(VCharts) Vue.use(VueParticles) Vue.filter('formatStyle', formatStyle) // const dispatch = store.dispatch router.beforeEach((to, from, next) => { NProgress.start() // start progress bar if (to.path === '/' || to.path === '/login' || to.path === '/404' || to.path === '/401') { setTimeout(next, 0) } else { setTimeout(next, 20) } }) router.afterEach((to) => { NProgress.done() // finish progress bar }) sync(store, router) /* eslint-disable no-new */ // global window.$globalHub = new Vue({ store, router, render: h => h(App) }).$mount('#app')
//App.vue <template> <div id="app" style="height:100%;"> <!--default slot--> <router-view :key="$route.path"></router-view> </div> </template> <script> import { mapState } from 'vuex' export default { components: {}, data () { return {} }, mounted () { // 禁用整個頁面的右擊事件 // document.oncontextmenu = () => { // return false // } }, computed: { ...mapState({}) }, methods: {} } </script> <style lang="less" rel="stylesheet/less"> #nprogress .spinner { display: none; } @import "styles/icon.css"; </style>
接下來看路由頁面html
//router.js /* eslint-disable no-undef */ import NotFind from '../pages/errors/404.vue' import VChars from '../pages/test/VChars.vue' // 生產/測試環境,使用路由懶加載 const _import = process.env.NODE_ENV === 'development' ? file => require(`@/pages/${file}.vue`).default : file => () => System.import(`@/pages/${file}.vue`).then(m => m.default) export default [ { path: '/', component: _import('login/Index') }, { path: '/login', component: _import('login/Index') }, { path: '/VChars', component: VChars }, { path: '/main', component: resolve => require(['../layout/Layout'], resolve), children: [ { path: '/dashboard', name: '首頁', component: _import('dashboard/Index') }, { path: '/table/list', name: '表格', component: _import('table/Index') }, { path: '/icons', name: '圖標', component: _import('icons/Index') }, { path: '/role/list', name: '角色管理', component: _import('roles/Index') }, { path: '/user/list', name: '用戶管理', component: _import('users/Index') }, { path: '/menus/list', name: '菜單設置', component: _import('menus/Index') }, { path: '/smsCode/list', name: '短信碼', component: _import('smsCode/Index') }, { path: '/errorCode/list', name: '錯誤碼', component: _import('errorCode/Index') }, { path: '/404', name: '404', component: NotFind }, { path: '/401', name: '401', component: _import('errors/401') } ] }, { path: '/editor', component: _import('editor/Index') } ]
//index.js /* eslint-disable no-undef */ import Vue from 'vue' import Router from 'vue-router' import config from '../../config/index' import routes from './routes' Vue.use(Router) export default new Router({ mode: 'history', // 後端支持可開 base: config.build.assetsPublicPath, routes })
接下來咱們根據頁面效果看代碼
vue
//src\pages\login\Index.vue <template> <!--region 粒子效果的登陸背景--> <div class="login-page"> <vue-particles color="#fff" :particleOpacity="0.7" :particlesNumber="60" shapeType="circle" :particleSize="4" linesColor="#fff" :linesWidth="1" :lineLinked="true" :lineOpacity="0.4" :linesDistance="150" :moveSpeed="2" :hoverEffect="true" hoverMode="grab" :clickEffect="true" clickMode="push" class="bg-lizi"> </vue-particles> <!--region 登陸表單--> <el-form :model="loginForm" class="login-form" label-position="left" size="large"> <el-form-item> <el-input type="text" v-model="loginForm.phone" auto-complete="off" placeholder="註冊時的手機號"> <span v-html="''" class="axon-icon" slot="prefix"></span> </el-input> </el-form-item> <el-form-item> <el-input :type="passwordType" v-model="loginForm.password" size="large" auto-complete="off" placeholder="登陸密碼"> <span v-html="''" class="axon-icon" slot="prefix"></span> <span v-html="passwordType === 'password' ? '' : ''" class="axon-icon" slot="suffix" @click="showPwd()"></span> </el-input> </el-form-item> <el-button type="primary" @click="submitLogin" @enter="submitLogin()" :loading="loading">登 錄</el-button> </el-form> <!--endregion--> </div> <!--endregion--> </template> <script> import BLL from './Index.js' import menusLisit from '../../../static/json/limit.json' export default { components: {}, data () { return { loginForm: { phone: '18951871658', password: '123456' }, loading: false, menus: menusLisit, passwordType: 'password' // 密碼控件的類型 } }, created () { this.BLL = new BLL(this) }, mounted () { }, methods: { // 登陸 submitLogin () { this.BLL.login() }, showPwd () { this.passwordType === 'password' ? this.passwordType = 'text' : this.passwordType = 'password' } } } </script> <style lang="scss"> @import "./Index.scss"; </style>
//src\pages\login\Index.js import Base from '../../base/index' export default class extends Base { /** * 用戶登陸 */ login () { if (!this.vm.loginForm.phone) { this.vm.$message.warning('請填寫登陸帳號!') return false } if (!this.vm.$utils.Validate.chkFormat(this.vm.loginForm.phone, 'phone')) { this.vm.$message.warning('登陸手機號的格式不正確!') return false } if (!this.vm.loginForm.password) { this.vm.$message.warning('請填寫登陸密碼!') return false } if (this.vm.loginForm.phone !== '18951871658' || this.vm.loginForm.password !== '123456') { this.vm.$message.warning('帳號或密碼不匹配!') return false } this.vm.loading = true // 取用戶的平臺權限信息,而且持久化 this.vm.$store.dispatch('init_sidebar_data', this.vm.menus) // 清空tab標籤 this.vm.$store.dispatch('del_all_tags') // 登陸成功 this.vm.$notify.success({ title: '登陸成功', message: '歡迎小主回來!' }) this.vm.$router.push({path: '/dashboard'}) } }
//src\pages\dashboard\Index.vue <template> <div class="dashboard-page"> <!--region 欄目--> <el-row class="panel-group" :gutter="40"> <template v-for="(panel, index) in panelList"> <el-col :key="index" :xs="12" :sm="12" :lg="6" class="card-panel-col"> <div class='card-panel' @click="handleSetLineChartData(panel.id)"> <div class="card-panel-icon-wrapper icon-people"> <div :class="`card-panel-icon panel-${panel.color}`"> <span class="axon-icon" v-html="panel.icon"></span> </div> </div> <div class="card-panel-description"> <div class="card-panel-text">{{ panel.label }}</div> <count-to class="card-panel-num" :startVal="0" :endVal="panel.value" :decimals=2 :duration="2600"></count-to> </div> </div> </el-col> </template> </el-row> <!--endregion--> <!--region 建立新場景--> <div class="add-new-scene"> <el-button type="primary" icon="el-icon-plus" plain round @click.native="creatNewScene">添加新場景</el-button> </div> <!--endregion--> </div> </template> <script> const CountTo = () => import('vue-count-to') const lineChartData = { newVisitis: { expectedData: [100, 120, 161, 134, 105, 160, 165], actualData: [120, 82, 91, 154, 162, 140, 145] }, messages: { expectedData: [200, 192, 120, 144, 160, 130, 140], actualData: [180, 160, 151, 106, 145, 150, 130] }, purchases: { expectedData: [80, 100, 121, 104, 105, 90, 100], actualData: [120, 90, 100, 138, 142, 130, 130] }, shoppings: { expectedData: [130, 140, 141, 142, 145, 150, 160], actualData: [120, 82, 91, 154, 162, 140, 130] } } export default { components: { CountTo }, data () { return { panelList: [ { id: 'newVisitis', label: 'Demo1', value: 102400, icon: '', color: 1// '#40c9c6' }, { id: 'messages', label: 'Demo2', value: 81212, icon: '', color: 2 // '#36a3f7' }, { id: 'purchases', label: 'Demo3', value: 9280.3, icon: '', color: 3 // '#f4516c' }, { id: 'shoppings', label: 'Demo4', value: 13600.6, icon: '', color: 4 // '#34bfa3' } ], lineChartData: lineChartData.newVisitis } }, mounted () { }, methods: { handleSetLineChartData (panel) { this.lineChartData = lineChartData[panel] }, /** * 建立新場景 */ creatNewScene () { this.$router.push({ path: '/editor' }) } } } </script> <style lang="scss" scoped> @import "../../styles/font.scss"; .dashboard-page { background-color: #f0f2f5; padding: 32px; overflow-y: auto; .panel-group { padding: 40px 0; .card-panel-col { margin-top: 10px; .card-panel { height: 108px; cursor: pointer; font-size: $font-size-s; position: relative; overflow: hidden; color: #666; background: #fff; -webkit-box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05); box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05); border-color: rgba(0, 0, 0, 0.05); display: flex; padding: 0 20px; &:hover { .card-panel-icon-wrapper { .card-panel-icon { border-radius: 4px; transition: all 0.38s ease-out; &.panel-1 { background-color: #40c9c6; .axon-icon { color: #ffffff; } } &.panel-2 { background-color: #36a3f7; .axon-icon { color: #ffffff; } } &.panel-3 { background-color: #f4516c; .axon-icon { color: #ffffff; } } &.panel-4 { background-color: #34bfa3; .axon-icon { color: #ffffff; } } } } } .card-panel-icon-wrapper { flex: 0 0 50%; .card-panel-icon { width: 60px; height: 60px; margin-top: 29px; text-align: center; line-height: 60px; &.panel-1 { .axon-icon { color: #40c9c6; } } &.panel-2 { .axon-icon { color: #36a3f7; } } &.panel-3 { .axon-icon { color: #f4516c; } } &.panel-4 { .axon-icon { color: #34bfa3; } } .axon-icon { font-size: $font-size-large; } } } .card-panel-description { flex: 0 0 50%; text-align: right; margin: 26px 0; .card-panel-text { font-size: $font-size-xxl; font-weight: 600; line-height: 32px; color: rgba(0, 0, 0, 0.45); } .card-panel-num { font-weight: 600; font-size: $font-size-xxxl; } } } } } .add-new-scene { text-align: center; } } </style>
這個裏面寫的css的方式處理的很是優雅,包括在css裏面的定義,很值得學習
接下來的頁面是,點擊添加新場景
咱們先看Index.vue中引入的組件git
//src\pages\editor\Index.vue <template> <div class="editor-index"> <!--頂部各類組件列表--> <the-tools></the-tools> <!--region 中間區域分爲三個區間--> <div class="index-center" ref="centerBox"> <!--左邊在線模板列表--> <the-template-list></the-template-list> <!--內容面板 編輯--> <div class="editor-box"> <the-editor-container ref="editorContainer"></the-editor-container> </div> <!--右側頁面列表--> <the-page-list></the-page-list> <!--組件屬性設置面板--> <the-comp-props-config></the-comp-props-config> <!--組件右擊圖層設置面板--> <the-comp-layer-manager></the-comp-layer-manager> </div> <!--endregion--> </div> </template> <script> // 右側頁面列表 import ThePageList from './components/ThePageList' // 左邊在線模板列表 import TheTemplateList from './components/TheTemplateList' // 頂部各類組件列表 import TheTools from './components/TheTools' // 面板 import TheEditorContainer from './components/TheEditorContainer' // 組件屬性設置面板 import TheCompPropsConfig from './components/TheCompPropsConfig' // 組件右擊圖層設置面板 import TheCompLayerManager from './components/TheCompLayerManager' export default { // 引入組件 components: { ThePageList, TheTemplateList, TheTools, TheEditorContainer, TheCompPropsConfig, TheCompLayerManager }, data () { return { toolJson: {}, panelWidthAndHeight: { width: 200, height: 400 } } }, created () { // 初始化 this.$store.dispatch('initPageEditor') }, mounted () { }, computed: {}, methods: {} } </script> <style lang="scss"> @import "../../styles/variables"; @import "../../styles/mixin"; .editor-index { background-color: #fff; width: 100%; height: 100%; display: flex; flex-direction: column; .index-center { box-sizing: border-box; flex: 0 0 calc(#{"100% - "+ $tool-header-height +""}); display: flex; flex-direction: row; position: relative; @include scrollBar; .editor-box { box-sizing: border-box; width: 100%; overflow-y: auto; padding: 40px 0; background-color: #eee; } } } </style>
接下來咱們一個一個分析
程序員
//src\components\EChars\reportComponent.js export default { // 園區客流檢測 數據 reportComponentList: [ { componentId: 87001, componentName: '柱狀圖', sourceRemark: '項王故里爲節日期間遊客量最多的,三臺山森林公園和洪澤湖溼地公園分列二三名。', tempHtml: '<i-LBarFour :chartsId="dataObj.chartsId" :title="dataObj.title" :xAxis="dataObj.xAxis" :yAxis="dataObj.yAxis" :unit="dataObj.unit"></i-LBarFour >', dataObj: { chartsId: `circularCharsID`, title: '接待省外訪客量TOP10景區排名', xAxis: ['項王故里', '三臺山森林公園', '洪澤湖溼地公園', '湖濱公園', '洋河酒廠', '雪楓公園', '龍王廟行宮', '中國楊樹博物館'], yAxis: [38056, 33824, 22672, 18616, 13112, 7016, 6864, 5792], unit: '人次' } }, { componentId: 87002, componentName: '折線圖', sourceRemark: '中秋期間,9月23日接待訪客量最多,9月24日接待訪客量最少', tempHtml: '<i-Line :chartsId="dataObj.chartsId" :title="dataObj.title" :xAxis="dataObj.xAxis" :yAxis="dataObj.yAxis" :unit="dataObj.unit"></i-Line >', dataObj: { chartsId: `lineCharsID`, title: '分日客流量變化', xAxis: ['9月22日', '9月23日', '9月23日'], yAxis: [59.28, 63.86, 33.86], unit: '萬人' } }, { componentId: 87003, componentName: '餅狀圖', sourceRemark: '節假日期間,年齡在22歲及如下、23-35歲和36-45歲的訪客佔訪客總量的60%', tempHtml: '<i-HollowPie :chartsId="dataObj.chartsId" :title="dataObj.title" :xAxis="dataObj.xAxis" :yAxis="dataObj.yAxis" :unit="dataObj.unit"></i-HollowPie>', dataObj: { chartsId: `hollowPieCharsID`, title: '訪客年齡段分佈', xAxis: ['22歲及如下', '23-35歲', '36-45歲', '46-55歲', '56歲及以上'], yAxis: [25145, 108532, 83761, 91837, 53111], unit: '%' } } ] }
//src\components\iDialogImg\Index.vue <!--region 封裝的圖片列表 卡片--> <template> <div class="custom-dialog-wrapper" v-if="dialogVisible"> <div class="custom-dialog-container" :style="{width: '60%',marginTop: '15vh'}"> <el-container> <el-aside :style="{height: '75vh',width:'200px'}"> <div class="custom-aside__wrapper"> <span class="custom-aside__title">圖片庫</span> <el-upload class="upload-demo" drag action="https://jsonplaceholder.typicode.com/posts/" multiple> <i class="el-icon-upload"></i> <div class="el-upload__text">將文件拖到此處,或<em>點擊上傳</em></div> <div class="el-upload__tip" slot="tip">只能上傳jpg/png文件,且不超過500kb</div> </el-upload> </div> </el-aside> <el-container> <el-header> <div class="custom-dialog__header"> <span class="custom-dialog__title">{{title}}</span> <a class="custom-dialog__headerbtn" @click="closeDialog"> <i class="el-dialog__close el-icon el-icon-close"></i> </a> </div> </el-header> <el-main> <ul v-if="operationFlag==='BackgroundImage'"> 背景圖片 <template v-for="item in backgroundImageList"> <li :key="item.id" @click="selectImage(item)" style="height: 30vh;"> <div class="item-bg-images" :style="{backgroundImage: 'url(' + item.url + ')'}"> <div class="item-active" v-if="imageSelectItem && imageSelectItem.id === item.id"> <span class="axon-icon" v-html="''"></span> </div> </div> </li> </template> </ul> <ul v-if="operationFlag==='Image' || operationFlag==='UpdateImages'"> <template v-for="item in iconList"> <li :key="item.id" @click="selectImage(item)" style="height: 24vh;border: 1px solid #c8c9ca"> <div class="item-bg-images" :style="{backgroundImage: 'url(' + item.url + ')'}"> <div class="item-active" v-if="imageSelectItem && imageSelectItem.id === item.id"> <span class="axon-icon" v-html="''"></span> </div> </div> </li> </template> </ul> </el-main> <el-footer> <el-button @click="cancelSelectImage" round> 取消 </el-button> <el-button type="success" @click="confirmSelectImage" round> 肯定 </el-button> </el-footer> </el-container> </el-container> </div> </div> </template> <!--endregion--> <script> export default { name: 'iDialogImg', data () { return { dialogVisible: '', title: '', operationFlag: '', backgroundImageList: [ { id: 1, url: require('../../assets/images/bgi/1521186133451071.jpg') }, { id: 2, url: require('../../assets/images/bgi/1522660019106280.png') }, { id: 3, url: require('../../assets/images/bgi/1521186133451071.jpg') }, { id: 4, url: require('../../assets/images/bgi/1522660019106280.png') }, { id: 5, url: require('../../assets/images/bgi/1521186133451071.jpg') }, { id: 6, url: require('../../assets/images/bgi/1522660019106280.png') }, { id: 7, url: require('../../assets/images/bgi/1521186133451071.jpg') }, { id: 8, url: require('../../assets/images/bgi/1522660019106280.png') }, { id: 9, url: require('../../assets/images/bgi/1521186133451071.jpg') }, { id: 10, url: require('../../assets/images/bgi/1522660019106280.png') }, { id: 11, url: require('../../assets/images/bgi/1521186133451071.jpg') }, { id: 12, url: require('../../assets/images/bgi/1522660019106280.png') }, { id: 13, url: require('../../assets/images/bgi/1521186133451071.jpg') }, { id: 14, url: require('../../assets/images/bgi/1522660019106280.png') } ], iconList: [ { id: 1, url: require('../../assets/icon/bridge1.png') }, { id: 2, url: require('../../assets/icon/person.png') }, { id: 3, url: require('../../assets/icon/ren1.png') }, { id: 4, url: require('../../assets/icon/bridge1.png') }, { id: 5, url: require('../../assets/icon/person.png') }, { id: 6, url: require('../../assets/icon/ren1.png') }, { id: 7, url: require('../../assets/icon/bridge1.png') }, { id: 8, url: require('../../assets/icon/person.png') }, { id: 9, url: require('../../assets/icon/bridge1.png') }, { id: 10, url: require('../../assets/icon/person.png') }, { id: 11, url: require('../../assets/icon/person.png') }, { id: 12, url: require('../../assets/icon/ren1.png') }, { id: 13, url: require('../../assets/icon/bridge1.png') }, { id: 14, url: require('../../assets/icon/person.png') } ], imageSelectItem: { id: null, url: '' } } }, created () { }, mounted () { }, computed: { // 取當前頁面 curPage () { return this.$store.getters.curPage }, // 取當前組件 currComp () { return this.$store.getters.curComp } }, methods: { // 關閉彈出框 closeDialog () { this.dialogVisible = false }, // 新增圖片/背景圖增彈出框 showAddDialog (flag) { this.operationFlag = flag switch (flag) { case 'Image': this.title = '新增圖標' break case 'BackgroundImage': this.title = '設置背景圖' break default: break } this.dialogVisible = true }, // 編輯圖片/背景圖增彈出框 showEditDialog () { this.title = '修改圖標' this.operationFlag = 'UpdateImages' this.dialogVisible = true }, // 取消圖片選中 cancelSelectImage () { this.imageSelectItem.id = null this.imageSelectItem.url = '' }, // 確認操做 confirmSelectImage () { this.closeDialog() switch (this.operationFlag) { case 'Image': this.handleAddImageComponent(this.imageSelectItem) break case 'BackgroundImage': this.updateCurPageBackgroundImage(this.imageSelectItem) break case 'UpdateImages': this.updateProps() break default: break } }, // 選中事件 selectImage (item) { console.log(item) this.imageSelectItem.id = item.id this.imageSelectItem.url = item.url }, // 添加圖片 handleAddImageComponent (item) { this.$store.dispatch('addNewCompByParams', { name: 'Image', url: item.url }) }, // 編輯圖片組件 updateProps () { this.$store.dispatch('editComp', { type: 'props', value: { 'url': this.imageSelectItem.url } }) }, // 設置當前頁面背景 updateCurPageBackgroundImage (item) { this.curPage.css.bgi = item.url this.syncCss('css', { 'bgi': this.curPage.css['bgi'] }, this.curPage.id) }, // 同步到持久化 syncCss (type, val, pageId) { this.$store.dispatch('editCurPage', { type: type, value: val, pageId: pageId }) } } } </script> <style lang="scss"> .custom-dialog-wrapper { z-index: 2025; position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto; margin: 0; .custom-dialog-container { position: relative; margin: 0 auto 50px; background: #fff; border-radius: 2px; box-shadow: 0 1px 3px rgba(0, 0, 0, .3); box-sizing: border-box; width: 50%; } } .el-aside { color: #333; text-align: center; .custom-aside__wrapper { width: 100%; position: relative; background-color: #1f2d3d; .custom-aside__title { position: absolute; top: 0; left: 0; width: 200px; height: 60px; line-height: 60px; font-size: 18px; color: #fff; background: #212121; font-weight: bolder; } .upload-demo { position: absolute; top: 80px; left: 0; .el-upload-dragger { width: 180px; } } } } .el-header { color: #333; box-shadow: 0 6px 6px 0 rgba(0, 0, 0, .16); .custom-dialog__header { padding: 20px 20px 10px 20px; .custom-dialog__title { line-height: 24px; font-size: 18px; color: #303133; } .custom-dialog__headerbtn { position: absolute; top: 20px; right: 20px; padding: 0; background: transparent; border: none; outline: none; cursor: pointer; font-size: 16px; } } } .el-main { background-color: #E9EEF3; color: #333; text-align: center; height: 60vh; overflow: scroll; ul { li { float: left; width: 24%; margin-top: 1.5vh; .item-bg-images { width: 100%; height: 100%; background-repeat: no-repeat; background-size: 100% 100%; .item-active { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; background-color: rgb(0, 0, 0); opacity: 0.7; span { color: #31b4a7; font-size: 52px; } } } } li:nth-child(4n + 1) { margin-left: 0px; } li:nth-child(4n + 2) { margin-left: 1%; } li:nth-child(4n + 3) { margin-left: 1%; } li:nth-child(4n + 4) { margin-left: 1%; } } } .el-footer { text-align: center; line-height: 60px; } </style>
//src\pages\editor\components\TheTemplateList.vue <template> <!--region 左邊在線模板列表--> <div class="the-template-list"> </div> <!--endregion--> </template> <script> export default { components: {}, data () { return {} }, created () { }, mounted () { }, computed: {}, methods: {} } </script> <style lang="scss"> @import "../../../styles/variables"; .the-template-list { background-color: #fff; box-sizing: border-box; border-right: 1px solid $border-color-1; flex: 0 0 $template-width; } </style>
//src\vuex\constTypes.js export default { // 頁面組件 ADD_COMPONENT: 'add_component', // 添加組件 TOGGLE_COMP: 'toggle_component', // 切換組件 COPY_COMP: 'copy_comp', // 拷貝組件 EDIT_COMP: 'edit_component', // 編輯組件 REMOVE_COMP: 'remove_component', // 刪除組件 // 頁面 ADD_PAGE: 'add_page', // 添加新頁面 INSERT_PAGE: 'insert_page', // 插入頁面 COPY_PAGE: 'copy_page', // 拷貝頁面 REMOVE_PAGE: 'remove_page', // 刪除頁面 EDIT_PAGE: 'edit_page', // 編輯頁面 TOGGLE_PAGE: 'toggle_page', // 切換頁面 ADD_COMP_TO_PAGES: 'add_component_to_page', // 往頁面中添加組件 REMOVE_COMP_TO_PAGES: 'remove_component_to_page', // 往頁面中刪除組件 // 屬性編輯調整 OPEN_PROPS_PANEL: 'open_props_panel', // 打開組件屬性面板 CLOSE_PROPS_PANEL: 'close_props_panel', // 關閉組件屬性面板 // 圖層編輯調整 OPEN_LAYER_PANEL: 'open_layer_panel', // 打開圖層屬性面板 CLOSE_LAYER_PANEL: 'close_layer_panel' // 關閉圖層屬性面板 }
//src\vuex\modules\pages.js import types from '../constTypes.js' import { getNewPageId, getNewPage } from '../function.js' import { cloneObj } from '../../assets/utils/util.js' const state = { list: [], curPageId: null } const getters = { // 取頁面列表 pages: (state) => state.list, // 取當前頁面ID curPageId: (state) => { if (state.curPageId) { return state.curPageId } if (state.list[0]) { return state.list[0]['id'] } }, // 取當前頁面 curPage: (state) => { return state.list .find( (_x) => _x.id === state.curPageId ) || state.list[0] }, getPageConfigByPageId: (state) => (pageId) => { return state.list.find(_x => _x.id === pageId) } } const mutations = { // 切換頁面 [types.TOGGLE_PAGE] (state, pageId) { state.curPageId = pageId }, // 添加頁面 [types.ADD_PAGE] (state, pageData) { state.list.push(pageData) }, // 插入頁面 [types.INSERT_PAGE] (state, { page, pageId }) { let index = state.list .findIndex( _x => _x.id === pageId ) if (index > -1) { state.list.splice(index + 1, 0, page) } }, // 拷貝頁面 [types.COPY_PAGE] (state, { prePageId, pageId }) { let list = state.list let index = list.findIndex(_x => _x.id === prePageId) if (index > -1) { // 複製一個新的頁面JSON對象 let pageData = cloneObj(list[index]) pageData.id = pageId // 克隆的頁面中組件處理 // if (pageData.comps && pageData.comps.length > 0) { // for (let i = 0; i < pageData.comps.length; i++) { // pageData.comps[i].id = pageData.comps[i].id + (i + 1) * pageData.comps[i].id // pageData.comps[i].parentId = pageId // } // } list.splice(index + 1, 0, pageData) state.curPageId = pageId } }, // 刪除頁面 [types.REMOVE_PAGE] (state, pageId) { let list = state.list let index = list.findIndex(_x => _x.id === pageId) if (index > -1) { list.splice(index, 1) let nextActivePage = list[index] || list[index - 1] state.curPageId = nextActivePage['id'] } }, // 編輯當前頁面 [types.EDIT_PAGE] (state, { type, value, pageId }) { let page = state.list .find( (_x) => _x.id === pageId || _x.id === state.curPageId ) if (page) { let pageProp = page[type] for (let key in value) { if (!pageProp[key]) { continue } if (typeof value[key] === 'object') { Object.assign(pageProp[key], value[key]) } else { pageProp[key] = value[key] } } } }, // 往頁面中添加組件 [types.ADD_COMP_TO_PAGES] (state, compData) { let curPage = state.list .find((_x) => _x.id === state.curPageId) if (curPage) { curPage.comps.push(compData) } }, // 往頁面中刪除組件 [types.REMOVE_COMP_TO_PAGES] (state, compId) { let curPage = state.list.find((_x) => _x.id === state.curPageId) if (curPage) { let comps = curPage.comps let index = curPage.comps.findIndex(_x => _x.id === compId) if (index > -1) { comps.splice(index, 1) if (comps && comps.length > 0) { curPage.comps = comps } else { curPage.comps = [] } } } } } const actions = { // 添加新的頁面 addNewPages ({ commit }) { const page = getNewPage() if (page) { commit(types.ADD_PAGE, page) } return page.id }, // 拷貝指定頁面 copyPage ({ commit }, pageId) { let id = getNewPageId() if (id) { commit(types.COPY_PAGE, { prePageId: pageId, pageId: id }) } return id }, // 刪除指定頁面 removePage ({ commit }, pageId) { commit(types.REMOVE_PAGE, pageId) return pageId }, // 從頁面中刪除組件 removeComponentToPage ({ commit }, compId) { commit(types.REMOVE_COMP_TO_PAGES, compId) return compId }, /** * 初始化Editor 編輯器 * 同時新建一個頁面 **/ initPageEditor ({ dispatch, commit }) { dispatch('addNewPages') .then((id) => { commit(types.TOGGLE_PAGE, id) }) }, /** * 切換頁面 * @param commit * @param pageId */ togglePage ({ commit }, pageId) { commit(types.TOGGLE_PAGE, pageId) }, /** * 修改當前頁面 * @param commit * @param type * @param value * @param pageId */ editCurPage ({ commit }, { type, value, pageId }) { commit([types.EDIT_PAGE], { type, value, pageId }) }, /** * 更新頁面中的某個組件中的css屬性 * @param commit * @param getters * @param state * @param type * @param key */ editCompOfCurPage ({ commit, getters, state }, { type, key }) { // 首先找到當前頁面 let _page = state.list.find(_x => _x.id === state.curPageId) if (_page) { // 找當前頁面中的組件 let _comp = _page.comps.find(_x => _x.id === window.$globalHub.$store.state.components.curCompId) let _obj = _comp[type] if (typeof _obj === 'object') { if (typeof _obj[key] === 'object') { Object.assign(_obj[key], window.$globalHub.$store.getters.curComp[type][key]) console.log(' state.list:', state.list) } else { _obj[key] = window.$globalHub.$store.getters.curComp[type][key] console.log(' state.list:', state.list) } } } } } export default { state, getters, actions, mutations }
這個裏面主要是數據的流向,真的簡化了不少dom的思想不須要dom思想github
//src\pages\editor\components\TheTemplateList.vue <template> <!--region 頁面列表--> <div class="the-page-list"> <div class="title">頁面管理</div> <div class="action-group"> <div class="item" @click="handleAddPage"> <span class="axon-icon" v-html="''"></span>添加 </div> <div class="item" @click="handleCopyPage"> <span class="axon-icon" v-html="''"></span>複製 </div> <div class="item" @click="handleRemovePage"> <span class="axon-icon" v-html="''"></span>刪除 </div> </div> <ul class="list"> <template v-for="(page, key) in pages"> <li :key="key" :id="page.id" :class="curPageId === page.id ? 'page active' : 'page'" @click="handleTogglePage(page.id)"> <span class="key"> <em>{{ key + 1 }}</em> </span> <p class="name">第 {{ key + 1 }} 頁</p> <span class="more-action axon-icon" v-html="''"> </span> </li> </template> </ul> </div> <!--endregion--> </template> <script> export default { components: {}, data () { return {} }, created () { }, mounted () { }, computed: { // 取頁面列表 pages () { try { return this.$store.getters.pages } catch (e) { this.$notify.error({ title: '錯誤', message: '初始化頁面失敗,請刷新後重試!' }) } }, // 當前頁面ID curPageId () { try { return this.$store.getters.curPageId } catch (e) { this.$notify.error({ title: '錯誤', message: '初始化頁面失敗,請刷新後重試!' }) } } }, methods: { /** * 建立新頁面 */ handleAddPage () { this.$store.dispatch('addNewPages', this.curPageId).then((id) => { this.handleTogglePage(id) }) }, /** * 拷貝當前頁面 */ handleCopyPage () { this.$store.dispatch('copyPage', this.curPageId).then((id) => { this.handleTogglePage(id) }) }, /** * 刪除當前頁面 */ handleRemovePage () { this.$store.dispatch('removePage', this.curPageId) }, /** * 切換頁面 * @param pageId */ handleTogglePage (pageId) { this.$store.dispatch('togglePage', pageId) } } } </script> <style lang="scss"> @import "../../../styles/variables"; .the-page-list { background-color: #fafafa; border-left: 1px solid $border-color-1; box-sizing: border-box; flex: 0 0 $setting-width; // 頁頭 .title { height: 40px; line-height: 40px; text-align: center; background-color: #e0e0e0; font-weight: 600; } // 列表 .list { .page { border-top: 1px solid #e6ebed; height: 70px; line-height: 70px; color: #666; cursor: pointer; font-size: 12px; position: relative; border-bottom: 1px solid #e6ebed; display: flex; .key, .more-action { flex: 0 0 44px; text-align: center; display: block; em { display: inline-block; width: 24px; height: 24px; line-height: 24px; text-align: center; border-radius: 12px; background-color: #ccc; color: #fff; font-weight: 400; font-style: normal; } } .more-action { font-size: 13px; } .name { font-size: 14px; flex: 100%; } &.active { background-color: #eee; .key { em { background-color: #1593ff; } } .name { color: #000; } } } } .action-group { display: flex; height: 36px; line-height: 36px; .item { flex: 0 0 33.33%; text-align: center; font-size: 12px; &:not(:last-child) { border-right: 1px solid #eee; } span { font-size: 14px; padding-right: 4px; } } } } </style>
//src\pages\editor\components\TheEditorContainer.vue <template> <div class="the-editor-container"> <div class="container-wrapper"> <!--region 頁面列表--> <div class="page-list"> <div class="page-wrapper"> <!--region 背景--> <div class="wrapper-background" :style="{ backgroundColor: curPage.css.bgc, backgroundImage: 'url(' + curPage.css.bgi + ')' }"></div> <!--endregion--> <!--region 組件--> <div class="component-wrapper"> <template v-for="comp in curPage.comps"> <vue-drr :ref="'comp_' + comp.id" :id="'comp_' + comp.id" :key="comp.id" :w="parseInt(comp.css.w)" :h="parseInt(comp.css.h)" :x="parseInt(comp.css.l)" :y="parseInt(comp.css.t)" :minw="20" :minh="30" :grid="grid" :parent="true" :angle="parseInt(comp.css.rotate)" @activated="toggleComp(comp.id, comp.name)" @resizestop="handleResizing" @dragstop="handleDragging" @rotatestop="handleRotating"> <comp-list @click="handleClick(comp)" @dblclick="handleDbClick" @contextmenu="handleContextmenu(comp)" :id="comp.id" class="comp" :style="comp.css | formatStyle('ft', 'lh')" :type="comp.name"></comp-list> </vue-drr> </template> </div> <!--endregion--> </div> </div> <!--endregion--> </div> </div> </template> <script> import vueDrr from 'vue-drr' import { throttle } from '../../../assets/utils/util.js' import BaseComps from '../../../components/iTemplate/index.js' const BASE_COMP_NAME = 'Base' const BASE_COMP_CONFIG = 'Config' export default { components: { vueDrr, compList: { props: { id: { type: [String, Number], required: true }, // 組件ID type: { type: String, required: true } // 組件類型名稱 }, render (h) { let _compFlagName = `${BASE_COMP_NAME}${this.type}` let _module = BaseComps[_compFlagName] return h(_module, { props: { id: this.id }, nativeOn: { click: throttle(this.handleClick, 1000), dblclick: this.handleDbClick, // contextmenu: throttle(this.handleContextmenu, 1000) contextmenu: evt => { // 阻止瀏覽器默認點擊行爲 evt.preventDefault() throttle(this.handleContextmenu(this.comp), 1000) } } }) }, methods: { // 單擊:左擊 handleClick (comp) { this.$emit('click', comp) }, // 雙擊 handleDbClick () { this.$emit('dblclick', this.type) }, // 單擊:右擊 handleContextmenu (comp) { this.$emit('contextmenu', comp) } } } }, data () { return { grid: [1, 1] // 組件拖動的網格 } }, created () { }, mounted () { }, computed: { // 取當前頁面 curPage () { try { return this.$store.getters.curPage } catch (e) { this.$notify.error({ title: '錯誤', message: '初始化頁面失敗,請刷新後重試!' }) } }, // 取當前正在編輯的組件 curCompId () { return this.$store.state.components.curCompId }, // 取當前組件 curComp () { return this.$store.getters.currComp } }, methods: { // 切換組件 toggleComp (id, name) { // 打開組件屬性設置面板 this.$store.dispatch('openPropsPanel', { id: id, name: name + BASE_COMP_CONFIG }) this.$store.dispatch('toggleComp', id) }, // 左擊:點擊組件 handleClick (comp) { // 關閉圖層面板 this.$store.dispatch('closeLayerPanel') // 打開組件屬性設置面板 this.$store.dispatch('openPropsPanel', { id: comp.id, name: comp.name + BASE_COMP_CONFIG }) }, /** * 雙擊組件 * @param type 組件類型 */ handleDbClick (type) { console.log('雙擊666') }, handleContextmenu (comp) { console.log('右擊666') this.$store.dispatch('closePropsPanel') // 打開圖層設置面板 this.$store.dispatch('openLayerPanel', { id: comp.id, name: comp.name + 'layer' + BASE_COMP_CONFIG }) }, // 組件拉伸時觸發 handleResizing (x, y, w, h) { this.updateCompStyle({ l: x, t: y, w, h }) }, // 組件拖動時觸發 handleDragging (x, y) { this.updateCompStyle({ l: x, t: y }) }, // 組件旋轉時觸發 handleRotating (angle) { this.updateCompStyle({ rotate: angle }) }, // 同步用戶修改 updateCompStyle (value) { this.$store.dispatch('editComp', { type: 'css', value: value }) } } } </script> <style lang="scss"> .the-editor-container { margin: 0 auto; border: 2px solid #ddd; border-radius: 2px; width: 52.7rem; min-height: 82.6rem; flex: 0 0 1; height: 100%; background-color: #fff; position: relative; .container-wrapper { width: 100%; height: 100%; .page-list { width: 100%; height: 100%; .page-wrapper { width: 100%; height: 100%; .wrapper-background { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-repeat: no-repeat; background-size: cover; background-origin: content-box; background-position: 50% 50%; } .component-wrapper { width: 100%; height: 100%; position: relative; } } } } } </style>
//src\pages\editor\components\TheCompPropsConfig.vue <!--region 組件屬性設置面板--> <template> <div ref="propsPanelConfig" class="the-comp-props-config-page" v-if="propsPanel && propsPanel.show" :style="{ height: panelHeight }"> <div :class="isMouseDown ? 'header pointer-events': 'header'"> <div class="title" ref="header" @mousedown="handleDragMouseDown">組件設置</div> <div class="close"> <span class="axon-icon" v-html="''" @click="closePropsPanel()"></span> </div> </div> <div class="props-panel-config"> <props-panel :name="propsPanel.name"></props-panel> </div> </div> </template> <!--endregion--> <script> import CompPropsConfig from '../../../components/iTemplate/config.js' import { throttle } from '../../../assets/utils/util.js' export default { components: { propsPanel: { props: { name: { type: String, required: true } }, render (h) { let _module = CompPropsConfig[this.name] return h(_module, { props: { name: { type: String } } }) } } }, data () { return { isMouseDown: false, // 鼠標是否按住可拖放區域 dragObj: { initX: 0, indexY: 0, width: 260, height: 48 } // 拖動對象位置 } }, created () { }, mounted () { document.addEventListener('mousemove', (e) => { this.throttleMove(e) }) document.addEventListener('mouseup', (e) => { this.handleDragMouseUp(e) }) }, computed: { // 根據父級組件高度設定屬性面板高度 panelHeight () { // 取父級組件中的父級標籤屬性 let _parentHeight = 0 try { _parentHeight = this.$parent.$refs.centerBox.offsetHeight return `${_parentHeight - 100}px` } catch (e) { return 0 } }, // 取面板信息 propsPanel () { return this.$store.getters.propPanel } }, watch: {}, methods: { // 關閉屬性面板 closePropsPanel () { console.log(' close:') this.$store.dispatch('closePropsPanel') }, // 拖動:當鼠標點下的時候,給要拖動的元素附上初始值 handleDragMouseDown (e) { this.isMouseDown = true this.dragObj.initX = e.offsetX this.dragObj.initY = e.offsetY this.dragObj.width = this.$refs.propsPanelConfig.offsetWidth this.dragObj.height = this.$refs.propsPanelConfig.offsetHeight - this.$refs.header.offsetHeight }, // 鼠標移動 --- 節流 throttleMove (e) { return throttle(this.handleDragMouseMove(e), 500) }, // 拖動過程當中,須要實時監聽位置變化 handleDragMouseMove (e) { if (this.isMouseDown) { // 移動外層須要移動的div let _cx = e.clientX - this.dragObj.initX let _cy = e.clientY - this.dragObj.indexY // 限制panel不能超出瀏覽器邊界 _cx = _cx >= 0 ? _cx : 0 _cy = _cy >= 0 ? _cy : 0 if (window.innerWidth - e.clientX + this.dragObj.initX < this.dragObj.width + 16 || window.innerWidth - this.dragObj.width < _cx) { _cx = window.innerWidth - this.dragObj.width } if (e.clientY > window.innerHeight - this.dragObj.height - this.$refs.header.offsetHeight + this.dragObj.initY) { _cy = window.innerHeight - this.$refs.header.offsetHeight - this.dragObj.height } this.$refs.propsPanelConfig.style.left = _cx + 'px' this.$refs.propsPanelConfig.style.top = _cy + 'px' } }, // 鼠標移除,取消監聽 handleDragMouseUp (e) { if (e.clientY > window.innerWidth || e.clientY < 0 || e.clientX < 0 || e.clientX > window.innerHeight) { this.isMouseDown = false } this.isMouseDown = false } } } </script> <style lang="scss"> .the-comp-props-config-page { background-color: #fafafa; position: fixed; left: 0; top: 0; width: 260px; overflow: hidden; box-shadow: 0 0 16px rgba(0, 0, 0, 0.16); z-index: 1000; height: 100%; user-select: none; .header { background-color: #fff; height: 48px; line-height: 48px; width: 100%; padding: 0 16px; user-select: none; display: flex; &.pointer-events { pointer-events: none; } .title { flex: 0 0 50%; font-size: 13px; cursor: move; } .close { flex: 0 0 50%; text-align: right; z-index: 9999; span { cursor: pointer; font-size: 14px; color: #a3afb7; } } } .props-panel-config { height: calc(#{"100% - 54px"}); .text-comp-config-page { height: 100%; .el-tabs { height: 100%; overflow: hidden; .el-tabs__content { height: calc(#{"100% - 50px"}); overflow-y: auto; .el-tab-pane { height: 100%; } } } } } } </style>
//src\pages\editor\components\TheCompLayerManager.vue <!--region 圖層設置面板--> <template> <div ref="layerManagerPanel" class="the-comp-layer-manager-page" v-if="layerPanel && layerPanel.show"> <div :class="isMouseDown ? 'header pointer-events': 'header'"> <div class="title" ref="header" @mousedown="handleDragMouseDown">圖層設置</div> <div class="close"> <span class="axon-icon" v-html="''" @click="closeLareyPanel()"></span> </div> </div> <div class="layer-panel-list"> <ul> <li @click="handRemove">刪除組件</li> <li @click="handleCopyComp">拷貝組件</li> <!---圖層顯示依據數組的降序排練--關於圖層數據的操做要反過來--> <li @click="handleSwapItem('down')">上移一層</li> <li @click="handleSwapItem('up')">下移一層</li> <li @click="handleSwapItem('bottom')">置頂</li> <li @click="handleSwapItem('top')">置底</li> </ul> </div> </div> </template> <!--endregion--> <script> import { throttle } from '../../../assets/utils/util.js' import SwapArrayItem from '../../../assets/utils/swapArrayItem.js' export default { components: {}, data () { return { isMouseDown: false, // 鼠標是否按住可拖放區域 dragObj: { initX: 0, indexY: 0, width: 160, height: 48 } // 拖動對象位置 } }, created () { }, mounted () { document.addEventListener('mousemove', (e) => { this.throttleMove(e) }) document.addEventListener('mouseup', (e) => { this.handleDragMouseUp(e) }) }, computed: { // 取圖層信息 layerPanel () { return this.$store.getters.layerPanel }, // 取當前頁面ID curPageId () { return this.$store.getters.curPage.id }, // 取當前頁面中全部組件 curPageComps () { return this.$store.getters.curPage.comps }, // 取當前正在編輯的組件 curCompId () { return this.$store.state.components.curCompId } }, watch: {}, methods: { // 關閉圖層面板 closeLareyPanel () { this.$store.dispatch('closeLayerPanel') }, // 刪除當前組件 handRemove () { let compId = this.curCompId // 刪除當前組件 this.$store.dispatch('removeComp', compId) // 將組件從當前頁面中移除 this.$store.dispatch('removeComponentToPage', compId) // 關閉圖層面板 this.$store.dispatch('closeLayerPanel') }, // 拷貝當前組件 handleCopyComp () { this.$store.dispatch('copyComp', this.curCompId) }, // 交換數組元素 handleSwapItem (operateName) { // 獲取當前數組下標 let index = this.accpetIndex(this.curPageComps, this.curCompId) SwapArrayItem.swapByOperate(this.curPageComps, index, operateName) }, /** * 獲取下標 * @param ary * @param id * @returns {number} */ accpetIndex (ary, id) { var index = -1 for (let i = 0; i < ary.length; i++) { if (ary[i] && ary[i].id === id) { index = i } } return index }, // 拖動:當鼠標點下的時候,給要拖動的元素附上初始值 handleDragMouseDown (e) { this.isMouseDown = true this.dragObj.initX = e.offsetX this.dragObj.initY = e.offsetY this.dragObj.width = this.$refs.layerManagerPanel.offsetWidth this.dragObj.height = this.$refs.layerManagerPanel.offsetHeight - this.$refs.header.offsetHeight }, // 鼠標移動 --- 節流 throttleMove (e) { return throttle(this.handleDragMouseMove(e), 500) }, // 拖動過程當中,須要實時監聽位置變化 handleDragMouseMove (e) { if (this.isMouseDown) { // 移動外層須要移動的div let _cx = e.clientX - this.dragObj.initX let _cy = e.clientY - this.dragObj.indexY // 限制panel不能超出瀏覽器邊界 _cx = _cx >= 0 ? _cx : 0 _cy = _cy >= 0 ? _cy : 0 if (window.innerWidth - e.clientX + this.dragObj.initX < this.dragObj.width + 16 || window.innerWidth - this.dragObj.width < _cx) { _cx = window.innerWidth - this.dragObj.width } if (e.clientY > window.innerHeight - this.dragObj.height - this.$refs.header.offsetHeight + this.dragObj.initY) { _cy = window.innerHeight - this.$refs.header.offsetHeight - this.dragObj.height } this.$refs.layerManagerPanel.style.left = _cx + 'px' this.$refs.layerManagerPanel.style.top = _cy + 'px' } }, // 鼠標移除,取消監聽 handleDragMouseUp (e) { if (e.clientY > window.innerWidth || e.clientY < 0 || e.clientX < 0 || e.clientX > window.innerHeight) { this.isMouseDown = false } this.isMouseDown = false } } } </script> <style lang="scss"> .the-comp-layer-manager-page { background-color: #fafafa; position: fixed; left: 0; top: 0; width: 160px; overflow: hidden; box-shadow: 0 0 16px rgba(0, 0, 0, 0.16); z-index: 1000; height: 286px; user-select: none; box-sizing: content-box; .header { background-color: #fff; height: 48px; line-height: 48px; width: 100%; padding: 0 16px; user-select: none; display: flex; &.pointer-events { pointer-events: none; } .title { flex: 0 0 50%; font-size: 13px; cursor: move; } .close { flex: 0 0 30%; text-align: right; z-index: 9999; span { cursor: pointer; font-size: 14px; color: #a3afb7; } } } .layer-panel-list { height: calc(#{"100% - 54px"}); font-size: 13px; li { height: 40px; line-height: 40px; padding: 0 16px; box-sizing: content-box; border-top: 1px solid #e6ebed; &:hover { color: #ffffff; background-color: #1593ff; } } li:nth-child(1) { &:hover { color: #ffffff; background-color: #ff2a6a; } } } } </style>
//src\pages\icons\Index.vue <template> <div class="icon-page"> <el-row :gutter="20"> <template v-for="(item, index) in icons"> <el-col :key="index" :span="6" class="item"> <span class="axon-icon" v-html="item.icon"></span> <p><span style="color: #606266;height: 1em;font-size: 12px;">{{item.icon}}</span></p> </el-col> </template> </el-row> </div> </template> <script> export default { components: {}, data () { return { icons: [{ name: '叉號', icon: '' }, { name: '原型叉號', icon: '' }, { name: '欄目', icon: '' }, { name: '箭頭', icon: '' }, { name: '隱藏密碼', icon: '' }, { name: '顯示密碼', icon: '' }, { name: '用戶', icon: '' }, { name: '用戶', icon: '' }, { name: '發貨', icon: '' }, { name: '訂單', icon: '' }, { name: '用戶', icon: '' }, { name: '金額', icon: '' }, { name: '主頁', icon: '' }, { name: '錯誤', icon: '' }, { name: '錯誤', icon: '' }, { name: '欄目', icon: '' }, { name: '欄目', icon: '' }, { name: '驗證碼', icon: '' }, { name: '手機', icon: '' } ] } }, mounted () {}, methods: {} } </script> <style lang="less" rel="stylesheet/less" scoped> .icon-page { background-color: #fff; padding: 20px 40px; .item { text-align: center; height: 120px; .axon-icon { font-size: 24px; color: #606266; margin-bottom: 15px; display: block; } } } </style>
//src\pages\table\Index.vue <template> <div class="table-page"> <!--region table 表格--> <i-table :list="list" :total="total" :loading="loading" :otherHeight="otherHeight" @handleSizeChange="handleSizeChange" @handleIndexChange="handleIndexChange" @handleSelectionChange="handleSelectionChange" :options="options" :pagination="pagination" :columns="columns" :operates="operates"> </i-table> <!--endregion--> </div> </template> <script type="text/jsx"> import iTable from '../../components/iTable/Index' import BLL from './Index' import { mapGetters } from 'vuex' export default { components: { iTable }, data () { return { total: 0, list: [], otherHeight: 208, filter: { date: '', phone: '' }, collapseTitle: '篩選條件:', columns: [ { prop: 'id', label: '編號', align: 'center', width: 60 }, { prop: 'title', label: '標題', align: 'center', width: 400, formatter: (row, column, cellValue) => { return `<span style="white-space: nowrap;color: dodgerblue;">${row.title}</span>` } }, { prop: 'state', label: '狀態', align: 'center', width: '160', render: (row, column) => { return row.state === 0 ? <el-tag type='success'> 上架 </el-tag> : row.state === 1 ? <el-tag type="info">下架</el-tag> : <el-tag type='danger'>審覈中</el-tag> } }, { prop: 'author', label: '做者', align: 'center', width: 120 }, { prop: 'phone', label: '聯繫方式', align: 'center', width: 160 }, { prop: 'email', label: '郵箱', align: 'center', width: 240 }, { prop: 'createDate', label: '發佈時間', align: 'center' } ], // 須要展現的列 operates: { width: 200, fixed: 'right', list: [ { label: '編輯', type: 'warning', show: true, icon: 'el-icon-edit', plain: false, disabled: false, method: (index, row) => { this.handleEdit(index, row) } }, { label: '刪除', type: 'danger', icon: 'el-icon-delete', show: true, plain: false, disabled: false, method: (index, row) => { this.handleDel(index, row) } } ] }, // 操做按鈕組 pagination: { pageIndex: 1, pageSize: 20 }, // 分頁參數 options: { stripe: true, // 是否爲斑馬紋 table highlightCurrentRow: true, // 是否支持當前行高亮顯示 mutiSelect: true // 是否支持列表項選中功能 } // table 的參數 } }, created () { this.BLL = new BLL(this) }, mounted () { this.BLL.getList() }, computed: { ...mapGetters(['button']), loading () { return this.$store.getters.btnLoading.str && this.$store.getters.btnLoading.id } }, methods: { // 切換每頁顯示的數量 handleSizeChange (pagination) { this.pagination = pagination this.BLL.getList() }, // 切換頁碼 handleIndexChange (pagination) { this.pagination = pagination this.BLL.getList() }, // 選中行 handleSelectionChange (val) { console.log('val:', val) }, // 編輯 handleEdit (index, row) { console.log(' index:', index) console.log(' row:', row) this.BLL.getList() }, // 刪除 handleDel (index, row) { console.log(' index:', index) console.log(' row:', row) }, // 刷新 reload (form) { console.log('into reload') this.$refs['filter'].resetFields() this.filter = { date: '', phone: '' } this.BLL.getList() }, // 篩選數據 filterData () { this.BLL.getList() } } } </script> <style lang="scss"> @import "./Index"; </style>
//src\pages\menus\Index.vue <!--菜單頁--> <template> <div class="menus-page"> <div slot="header" class="clearfix header"> </div> <i-table :list="menusList" :otherHeight="otherHeight" :columns="columns" :operates="operates"></i-table> </div> </template> <script type="text/jsx"> import iTable from '../../components/iTable/Index' import { mapState } from 'vuex' export default { components: { iTable }, data () { return { menusList: [], columns: [ { prop: 'name', label: '名稱', align: 'left', width: 200, className: 'first-level', formatter: function (row, column) { let marginLeft = parseInt(row.level) * 20 if (row.icon) { let icon = `<span class="axon-icon" style="margin-right:4px;margin-left:${marginLeft}px;font-size:16px;">${row.icon}</span>` return `${icon}<span>${row.name}</span>` } else { return `<span style="margin-left:${marginLeft}px;">${row.name}</span>` } } }, { prop: 'link', label: '地址', align: 'center' }, { prop: 'level', label: '排序', align: 'center', width: 100 }, { prop: 'state', label: '狀態', align: 'center', width: 100, render: function (row, column) { return row.state === 1 ? <el-tag>啓用</el-tag> : <el-tag type="info">禁用</el-tag> } } ], operates: { width: 200, fixed: 'right', list: [ { label: '編輯', type: 'warning', show: true, icon: 'el-icon-edit', plain: true, disabled: false, method: (index, row) => { this.handleEdit(index, row) } }, { label: '刪除', type: 'danger', show: true, icon: 'el-icon-delete', plain: true, disabled: false, method: (index, row) => { this.handelDel(index, row) } } ] }, otherHeight: 216 } }, mounted () { this.initMenus() }, computed: { ...mapState([ 'menus' ]), loading () { return this.$store.getters.btnLoading.str && this.$store.getters.btnLoading.id } }, methods: { initMenus () { if (this.menus && this.menus.sidebar.length > 0) { let level = 1 this.formatMenusList(this.menus.sidebar, level) } }, // 遞歸menus的各級菜單 formatMenusList (list, level) { for (let i = 0; i < list.length; i++) { this.menusList.push({ ...list[i], level }) if (list[i].children) { level++ this.formatMenusList(list[i].children, level) } } }, // 編輯菜單 handleEdit (index, row) { console.log(row) }, // 刪除菜單 handelDel (index, row) { console.log(row) }, add () { console.log('into add()') }, // 轉換 string 方法名爲 Function 對象 callback (fn) { fn = `this.${fn}()` return eval(fn) // eslint-disable-line // this.__callback(fn, this) } } } </script> <style lang="scss"> .menus-page { background-color: #fff; .header { padding: 10px 10px; } } </style>
<!--角色管理頁面--> <template> <div class="role-page"> <i-table></i-table> </div> </template> <script> import iTable from '../../components/iTable/Index' export default { components: { iTable }, data () { return {} }, mounted () { }, computed: {}, methods: {} } </script> <style lang="scss" scoped> .role-page { background-color: #fff; } </style>
<template> <div class="MyContain"> </div> </template> <script> export default { components: {}, data () { return {} }, mounted () { }, computed: {}, methods: {} } </script> <style lang="scss" scoped> .MyContain { background-color: #fff; } </style>
<template> <div class="MyContain"> </div> </template> <script> export default { components: {}, data () { return {} }, mounted () { }, computed: {}, methods: {} } </script> <style lang="scss" scoped> .MyContain { background-color: #fff; } </style>