Commit 00ae65cd authored by YangAo's avatar YangAo 🇨🇳

第二版初步完成

parent 8a67f824
......@@ -9,12 +9,14 @@
"dependencies": {
"axios": "^0.18.1",
"babel-polyfill": "^6.26.0",
"clone": "^2.1.2",
"echarts": "^4.9.0",
"element-ui": "^2.13.2",
"js-cookie": "^3.0.1",
"mavon-editor": "^2.6.17",
"node-sass": "^5.0.0",
"sass-loader": "^10.1.0",
"svg-sprite-loader": "5.1.1",
"view-design": "^4.3.2",
"vue": "^2.6.10",
"vue-cropperjs": "^3.0.0",
......@@ -23,8 +25,7 @@
"vue-router": "^3.0.3",
"vue-schart": "^2.0.0",
"vuedraggable": "^2.17.0",
"vuex": "3.6.2",
"svg-sprite-loader": "5.1.1"
"vuex": "3.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.9.0",
......
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><defs><style>.a{fill:#fff;}</style></defs><rect class="a" width="8" height="8"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18"><defs><style>.a{fill:none;}.b{isolation:isolate;clip-path:url(#a);}.c{fill:#fff;stroke:#fff;stroke-width:0.5px;}</style><clipPath id="a"><rect class="a" x="1" y="1" width="18" height="18"/></clipPath></defs><g class="b" transform="translate(-1 -1)"><path class="c" d="M14.222,20.062a2.031,2.031,0,1,0-1.833-1.156.255.255,0,0,1-.051.289l-1.149,1.149a.254.254,0,0,1-.289.049,3.556,3.556,0,0,0-4.987,2.513.254.254,0,0,1-.249.2H4.17a.251.251,0,0,1-.237-.162A2.033,2.033,0,1,0,3.962,24.3a.255.255,0,0,1,.241-.176h1.46a.254.254,0,0,1,.249.2,3.56,3.56,0,0,0,4.363,2.737.251.251,0,0,1,.281.119l.73,1.263a2.032,2.032,0,1,0,3.379,1.548,2.05,2.05,0,0,0-2.017-2.059,2.2,2.2,0,0,0-.29.019.255.255,0,0,1-.256-.124l-.611-1.059a.254.254,0,0,1,.067-.329,3.556,3.556,0,0,0,.494-5.187.253.253,0,0,1,.011-.348l.992-.992a.255.255,0,0,1,.289-.051,2.056,2.056,0,0,0,.878.2Zm0-3.048a1.016,1.016,0,1,1-1.016,1.016A1.017,1.017,0,0,1,14.222,17.015ZM2.032,24.682a1.016,1.016,0,1,1,1.016-1.016A1.017,1.017,0,0,1,2.032,24.682Zm10.6,4.27a1.016,1.016,0,1,1-1.016,1.016A1.017,1.017,0,0,1,12.635,28.951Zm-.7-5.333a2.542,2.542,0,1,1-.744-1.8,2.518,2.518,0,0,1,.744,1.8Z" transform="translate(1.854 -13.999)"/></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18"><defs><style>.a{fill:none;}.b{isolation:isolate;clip-path:url(#a);}.c{stroke-width:0.3px;}</style><clipPath id="a"><rect class="a" x="1" y="1" width="18" height="18"/></clipPath></defs><g class="b" transform="translate(-1 -1)"><path class="c" d="M14.222,20.062a2.031,2.031,0,1,0-1.833-1.156.255.255,0,0,1-.051.289l-1.149,1.149a.254.254,0,0,1-.289.049,3.556,3.556,0,0,0-4.987,2.513.254.254,0,0,1-.249.2H4.17a.251.251,0,0,1-.237-.162A2.033,2.033,0,1,0,3.962,24.3a.255.255,0,0,1,.241-.176h1.46a.254.254,0,0,1,.249.2,3.56,3.56,0,0,0,4.363,2.737.251.251,0,0,1,.281.119l.73,1.263a2.032,2.032,0,1,0,3.379,1.548,2.05,2.05,0,0,0-2.017-2.059,2.2,2.2,0,0,0-.29.019.255.255,0,0,1-.256-.124l-.611-1.059a.254.254,0,0,1,.067-.329,3.556,3.556,0,0,0,.494-5.187.253.253,0,0,1,.011-.348l.992-.992a.255.255,0,0,1,.289-.051,2.056,2.056,0,0,0,.878.2Zm0-3.048a1.016,1.016,0,1,1-1.016,1.016A1.017,1.017,0,0,1,14.222,17.015ZM2.032,24.682a1.016,1.016,0,1,1,1.016-1.016A1.017,1.017,0,0,1,2.032,24.682Zm10.6,4.27a1.016,1.016,0,1,1-1.016,1.016A1.017,1.017,0,0,1,12.635,28.951Zm-.7-5.333a2.542,2.542,0,1,1-.744-1.8,2.518,2.518,0,0,1,.744,1.8Z" transform="translate(1.854 -13.999)"/></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18"><defs><style>.a{fill:none;}.b{isolation:isolate;clip-path:url(#a);}.c{fill:#fff;}</style><clipPath id="a"><rect class="a" x="1" y="1" width="18" height="18"/></clipPath></defs><g class="b" transform="translate(-1 -1)"><g transform="translate(-34.19 -85.244)"><path class="c" d="M52.563,93.569a1.075,1.075,0,0,0,0-1.953l-7.4-4.143a2.184,2.184,0,0,0-1.944,0l-7.4,4.143a1.074,1.074,0,0,0,0,1.953l2.069,1.158c0,.018-.005.035-.005.053v5.5a.712.712,0,0,0,.01.119c.139,1.865,3.244,2.84,6.3,2.84,3.092,0,6.233-1,6.306-2.9q0-.028,0-.057v-5.5c0-.018,0-.035-.005-.053l2.069-1.158Zm-8.635-4.834a.588.588,0,0,1,.261-.05.6.6,0,0,1,.262.051l6.891,3.858L44.451,96.45a.59.59,0,0,1-.261.05.6.6,0,0,1-.262-.05l-6.891-3.858,6.891-3.858Zm5.12,11.512c0,.01,0,.02,0,.029-.006.536-1.854,1.52-4.856,1.52s-4.851-.984-4.856-1.52a.746.746,0,0,0-.005-.079V95.536l3.888,2.177a2.121,2.121,0,0,0,1.943,0l3.887-2.176v4.711Z" transform="translate(0 0)"/><path class="c" d="M907.13,454.823a.724.724,0,0,0-.724.724v5.747a.724.724,0,1,0,1.448,0v-5.747A.724.724,0,0,0,907.13,454.823Z" transform="translate(-854.768 -360.657)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18"><defs><style>.a{fill:none;}.b{isolation:isolate;clip-path:url(#a);}.c{stroke-width:0.3px;}</style><clipPath id="a"><rect class="a" x="1" y="1" width="18" height="18"/></clipPath></defs><g class="b" transform="translate(-1 -1)"><g transform="translate(-34.19 -85.244)"><path class="c" d="M52.563,93.569a1.075,1.075,0,0,0,0-1.953l-7.4-4.143a2.184,2.184,0,0,0-1.944,0l-7.4,4.143a1.074,1.074,0,0,0,0,1.953l2.069,1.158c0,.018-.005.035-.005.053v5.5a.712.712,0,0,0,.01.119c.139,1.865,3.244,2.84,6.3,2.84,3.092,0,6.233-1,6.306-2.9q0-.028,0-.057v-5.5c0-.018,0-.035-.005-.053l2.069-1.158Zm-8.635-4.834a.588.588,0,0,1,.261-.05.6.6,0,0,1,.262.051l6.891,3.858L44.451,96.45a.59.59,0,0,1-.261.05.6.6,0,0,1-.262-.05l-6.891-3.858,6.891-3.858Zm5.12,11.512c0,.01,0,.02,0,.029-.006.536-1.854,1.52-4.856,1.52s-4.851-.984-4.856-1.52a.746.746,0,0,0-.005-.079V95.536l3.888,2.177a2.121,2.121,0,0,0,1.943,0l3.887-2.176v4.711Z" transform="translate(0 0)"/><path class="c" d="M907.13,454.823a.724.724,0,0,0-.724.724v5.747a.724.724,0,1,0,1.448,0v-5.747A.724.724,0,0,0,907.13,454.823Z" transform="translate(-854.768 -360.657)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18"><defs><style>.a{fill:none;}.b{isolation:isolate;clip-path:url(#a);}.c{fill:#fff;stroke:#fff;stroke-width:0.3px;}</style><clipPath id="a"><rect class="a" x="1" y="1" width="18" height="18"/></clipPath></defs><g class="b" transform="translate(-1 -1)"><g transform="translate(-59.35 -236.767)"><g transform="translate(66.857 254.545)"><path class="c" d="M342.2,125.9h-3.69a.517.517,0,1,0,0,1.024h3.69a.517.517,0,0,0,0-1.024Z" transform="translate(-337.915 -125.893)"/></g><g transform="translate(66.112 252.545)"><path class="c" d="M342.93,123.425h-5.163a.506.506,0,1,0,0,1.012h5.163a.506.506,0,1,0,0-1.012Z" transform="translate(-337.259 -123.424)"/></g><path class="c" d="M344.369,116.473a.484.484,0,0,0,.461-.517,5.175,5.175,0,0,0-1.327-3.262,3.892,3.892,0,0,0-2.891-1.339.495.495,0,0,0-.517.506.484.484,0,0,0,.517.506,3.026,3.026,0,0,1,2.25,1.024,4.32,4.32,0,0,1,1.125,2.61A.461.461,0,0,0,344.369,116.473Z" transform="translate(-271.237 128.604)"/><path class="c" d="M159.383,1.905A6.862,6.862,0,0,0,154.544,0a6.466,6.466,0,0,0-6.682,6.173,5.949,5.949,0,0,0,2.349,4.661,1.1,1.1,0,0,1,.4.851V12.2A1.388,1.388,0,0,0,152,13.546h5.12A1.365,1.365,0,0,0,158.5,12.2v-.515a1.1,1.1,0,0,1,.411-.852,6,6,0,0,0,2.349-4.661,5.8,5.8,0,0,0-1.879-4.269Zm-1.914,9.747v.515a.355.355,0,0,1-.329.336h-5.156a.331.331,0,0,1-.317-.336v-.515a2.049,2.049,0,0,0-.787-1.569,4.912,4.912,0,0,1-1.949-3.91,5.646,5.646,0,0,1,11.251,0,4.922,4.922,0,0,1-1.938,3.91,2.058,2.058,0,0,0-.775,1.569Z" transform="translate(-85.362 238)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18"><defs><style>.a{fill:none;}.b{isolation:isolate;clip-path:url(#a);}.c{stroke-width:0.3px;}</style><clipPath id="a"><rect class="a" x="1" y="1" width="18" height="18"/></clipPath></defs><g class="b" transform="translate(-1 -1)"><g transform="translate(-59.35 -236.767)"><g transform="translate(66.857 254.545)"><path class="c" d="M342.2,125.9h-3.69a.517.517,0,1,0,0,1.024h3.69a.517.517,0,0,0,0-1.024Z" transform="translate(-337.915 -125.893)"/></g><g transform="translate(66.112 252.545)"><path class="c" d="M342.93,123.425h-5.163a.506.506,0,1,0,0,1.012h5.163a.506.506,0,1,0,0-1.012Z" transform="translate(-337.259 -123.424)"/></g><path class="c" d="M344.369,116.473a.484.484,0,0,0,.461-.517,5.175,5.175,0,0,0-1.327-3.262,3.892,3.892,0,0,0-2.891-1.339.495.495,0,0,0-.517.506.484.484,0,0,0,.517.506,3.026,3.026,0,0,1,2.25,1.024,4.32,4.32,0,0,1,1.125,2.61A.461.461,0,0,0,344.369,116.473Z" transform="translate(-271.237 128.604)"/><path class="c" d="M159.383,1.905A6.862,6.862,0,0,0,154.544,0a6.466,6.466,0,0,0-6.682,6.173,5.949,5.949,0,0,0,2.349,4.661,1.1,1.1,0,0,1,.4.851V12.2A1.388,1.388,0,0,0,152,13.546h5.12A1.365,1.365,0,0,0,158.5,12.2v-.515a1.1,1.1,0,0,1,.411-.852,6,6,0,0,0,2.349-4.661,5.8,5.8,0,0,0-1.879-4.269Zm-1.914,9.747v.515a.355.355,0,0,1-.329.336h-5.156a.331.331,0,0,1-.317-.336v-.515a2.049,2.049,0,0,0-.787-1.569,4.912,4.912,0,0,1-1.949-3.91,5.646,5.646,0,0,1,11.251,0,4.922,4.922,0,0,1-1.938,3.91,2.058,2.058,0,0,0-.775,1.569Z" transform="translate(-85.362 238)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18"><defs><style>.a{fill:none;}.b{isolation:isolate;clip-path:url(#a);}.c{fill:#fff;}</style><clipPath id="a"><rect class="a" x="1" y="1" width="18" height="18"/></clipPath></defs><g class="b" transform="translate(-1 -1)"><g transform="translate(-103.3 -60.2)"><path class="c" d="M118.1,62.2h-9.161a2.571,2.571,0,0,0-2.636,2.494V75.706a2.571,2.571,0,0,0,2.636,2.494h9.157a2.576,2.576,0,0,0,2.638-2.494V64.7A2.571,2.571,0,0,0,118.1,62.2Zm-10.528,2.5a1.331,1.331,0,0,1,1.368-1.286h9.159a1.33,1.33,0,0,1,1.368,1.286V75.71A1.331,1.331,0,0,1,118.095,77h-9.159a1.33,1.33,0,0,1-1.368-1.286Z"/><path class="c" d="M295.533,183.408h6.482a.631.631,0,1,0,0-1.263h-6.482a.631.631,0,1,0,0,1.263Zm0-2.218h6.482a.631.631,0,0,0,0-1.263h-6.482a.631.631,0,1,0,0,1.263Zm1.009-4.917-.308,1.793a.632.632,0,0,0,.918.667l1.61-.845,1.61.847a.632.632,0,0,0,.918-.667l-.308-1.791,1.3-1.27a.633.633,0,0,0-.352-1.078l-1.8-.267-.806-1.631a.65.65,0,0,0-1.133,0l-.808,1.631-1.8.261a.634.634,0,0,0-.35,1.08Zm3.134-.116.148.856-.768-.4a.634.634,0,0,0-.591,0l-.768.4.148-.856a.629.629,0,0,0-.181-.56l-.623-.607.854-.125a.638.638,0,0,0,.479-.347l.386-.779.386.781a.639.639,0,0,0,.477.345l.861.125-.623.607A.635.635,0,0,0,299.676,176.158Z" transform="translate(-185.245 -107.552)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18"><defs><style>.a{fill:none;}.b{isolation:isolate;clip-path:url(#a);}.c{stroke-width:0.3px;}</style><clipPath id="a"><rect class="a" x="1" y="1" width="18" height="18"/></clipPath></defs><g class="b" transform="translate(-1 -1)"><g transform="translate(-103.3 -60.2)"><path class="c" d="M118.1,62.2h-9.161a2.571,2.571,0,0,0-2.636,2.494V75.706a2.571,2.571,0,0,0,2.636,2.494h9.157a2.576,2.576,0,0,0,2.638-2.494V64.7A2.571,2.571,0,0,0,118.1,62.2Zm-10.528,2.5a1.331,1.331,0,0,1,1.368-1.286h9.159a1.33,1.33,0,0,1,1.368,1.286V75.71A1.331,1.331,0,0,1,118.095,77h-9.159a1.33,1.33,0,0,1-1.368-1.286Z"/><path class="c" d="M295.533,183.408h6.482a.631.631,0,1,0,0-1.263h-6.482a.631.631,0,1,0,0,1.263Zm0-2.218h6.482a.631.631,0,0,0,0-1.263h-6.482a.631.631,0,1,0,0,1.263Zm1.009-4.917-.308,1.793a.632.632,0,0,0,.918.667l1.61-.845,1.61.847a.632.632,0,0,0,.918-.667l-.308-1.791,1.3-1.27a.633.633,0,0,0-.352-1.078l-1.8-.267-.806-1.631a.65.65,0,0,0-1.133,0l-.808,1.631-1.8.261a.634.634,0,0,0-.35,1.08Zm3.134-.116.148.856-.768-.4a.634.634,0,0,0-.591,0l-.768.4.148-.856a.629.629,0,0,0-.181-.56l-.623-.607.854-.125a.638.638,0,0,0,.479-.347l.386-.779.386.781a.639.639,0,0,0,.477.345l.861.125-.623.607A.635.635,0,0,0,299.676,176.158Z" transform="translate(-185.245 -107.552)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18"><defs><style>.a{fill:none;}.b{isolation:isolate;clip-path:url(#a);}.c{fill:#fff;stroke:#fff;stroke-width:0.2px;}</style><clipPath id="a"><rect class="a" x="1" y="1" width="18" height="18"/></clipPath></defs><g class="b" transform="translate(-1 -1)"><g transform="translate(-70.4 -172.933)"><path class="c" d="M168.653,183.612a4.6,4.6,0,1,1,4.644.032,8.838,8.838,0,0,1,5.411,4.635h-1.262a7.914,7.914,0,0,0-6.526-3.858,8.075,8.075,0,0,0-6.6,3.858h-1.19a8.832,8.832,0,0,1,5.521-4.667ZM171,183.1a3.5,3.5,0,1,0-3.451-3.5A3.476,3.476,0,0,0,171,183.1Z" transform="translate(-90.619 0)"/><path class="c" d="M88.824,727.467a.583.583,0,0,1,0,1.167c-2.291,0-4.233.705-7.792,2.123a1.709,1.709,0,0,1-1.262,0c-3.589-1.419-5.533-2.125-7.793-2.125a.583.583,0,0,1,0-1.167c2.426,0,4.5.737,8.213,2.2a.57.57,0,0,0,.421,0C84.291,728.2,86.367,727.467,88.824,727.467Z" transform="translate(0 -539.945)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18"><defs><style>.a{fill:none;}.b{isolation:isolate;clip-path:url(#a);}.c{stroke-width:0.3px;}</style><clipPath id="a"><rect class="a" x="1" y="1" width="18" height="18"/></clipPath></defs><g class="b" transform="translate(-1 -1)"><g transform="translate(-70.4 -172.933)"><path class="c" d="M168.653,183.612a4.6,4.6,0,1,1,4.644.032,8.838,8.838,0,0,1,5.411,4.635h-1.262a7.914,7.914,0,0,0-6.526-3.858,8.075,8.075,0,0,0-6.6,3.858h-1.19a8.832,8.832,0,0,1,5.521-4.667ZM171,183.1a3.5,3.5,0,1,0-3.451-3.5A3.476,3.476,0,0,0,171,183.1Z" transform="translate(-90.619 0)"/><path class="c" d="M88.824,727.467a.583.583,0,0,1,0,1.167c-2.291,0-4.233.705-7.792,2.123a1.709,1.709,0,0,1-1.262,0c-3.589-1.419-5.533-2.125-7.793-2.125a.583.583,0,0,1,0-1.167c2.426,0,4.5.737,8.213,2.2a.57.57,0,0,0,.421,0C84.291,728.2,86.367,727.467,88.824,727.467Z" transform="translate(0 -539.945)"/></g></g></svg>
\ No newline at end of file
export default {
computed: {
device() {
return this.$store.state.app.device
}
},
mounted() {
// In order to fix the click on menu on the ios device will trigger the mouseleave bug
this.fixBugIniOS()
},
methods: {
fixBugIniOS() {
const $subMenu = this.$refs.subMenu
if ($subMenu) {
const handleMouseleave = $subMenu.handleMouseleave
$subMenu.handleMouseleave = (e) => {
if (this.device === 'mobile') {
return
}
handleMouseleave(e)
}
}
}
}
}
<script>
export default {
name: 'MenuItem',
functional: true,
props: {
icon: {
type: String,
default: ''
},
title: {
type: String,
default: ''
}
},
render(h, context) {
const { icon, title } = context.props
const vnodes = []
if (icon) {
vnodes.push(<svg-icon icon-class={icon}/>)
}
if (title) {
if (title.length > 5) {
vnodes.push(<span slot='title' title={(title)}>{(title)}</span>)
} else {
vnodes.push(<span slot='title'>{(title)}</span>)
}
}
return vnodes
}
}
</script>
<template>
<component :is="type" v-bind="linkProps(to)">
<slot />
</component>
</template>
<script>
import { isExternal } from '@/utils/validate'
export default {
props: {
to: {
type: [String, Object],
required: true
}
},
computed: {
isExternal() {
return isExternal(this.to)
},
type() {
if (this.isExternal) {
return 'a'
}
return 'router-link'
}
},
methods: {
linkProps(to) {
if (this.isExternal) {
return {
href: to,
target: '_blank',
rel: 'noopener'
}
}
return {
to: to
}
}
}
}
</script>
<template>
<div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
<transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo" />
<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
</router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo" />
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
</router-link>
</transition>
</div>
</template>
<script>
import logoImg from '@/assets/logo/logo.png'
import variables from '@/assets/styles/variables.scss'
export default {
name: 'SidebarLogo',
props: {
collapse: {
type: Boolean,
required: true
}
},
computed: {
variables() {
return variables;
},
sideTheme() {
return this.$store.state.settings.sideTheme
}
},
data() {
return {
title: '企业服务平台',
logo: logoImg
}
}
}
</script>
<style lang="scss" scoped>
.sidebarLogoFade-enter-active {
transition: opacity 1.5s;
}
.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
opacity: 0;
}
.sidebar-logo-container {
position: relative;
width: 100%;
height: 50px;
line-height: 50px;
background: #2b2f3a;
text-align: center;
overflow: hidden;
& .sidebar-logo-link {
height: 100%;
width: 100%;
& .sidebar-logo {
width: 32px;
height: 32px;
vertical-align: middle;
margin-right: 12px;
}
& .sidebar-title {
display: inline-block;
margin: 0;
color: #fff;
font-weight: 600;
line-height: 50px;
font-size: 14px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
vertical-align: middle;
}
}
&.collapse {
.sidebar-logo {
margin-right: 0px;
}
}
}
</style>
<template>
<div v-if="!item.hidden">
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<template slot="title">
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
</template>
<sidebar-item
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/>
</el-submenu>
</div>
</template>
<script>
import path from 'path'
import { isExternal } from '@/utils/validate'
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'
export default {
name: 'SidebarItem',
components: { Item, AppLink },
mixins: [FixiOSBug],
props: {
// route object
item: {
type: Object,
required: true
},
isNest: {
type: Boolean,
default: false
},
basePath: {
type: String,
default: ''
}
},
data() {
this.onlyOneChild = null
return {}
},
methods: {
hasOneShowingChild(children = [], parent) {
if (!children) {
children = [];
}
const showingChildren = children.filter(item => {
if (item.hidden) {
return false
} else {
// Temp set(will be used if only has one showing child)
this.onlyOneChild = item
return true
}
})
// When there is only one child router, the child router is displayed by default
if (showingChildren.length === 1) {
return true
}
// Show parent if there are no child router to display
if (showingChildren.length === 0) {
this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
return true
}
return false
},
resolvePath(routePath, routeQuery) {
if (isExternal(routePath)) {
return routePath
}
if (isExternal(this.basePath)) {
return this.basePath
}
if (routeQuery) {
let query = JSON.parse(routeQuery);
return { path: path.resolve(this.basePath, routePath), query: query }
}
return path.resolve(this.basePath, routePath)
}
}
}
</script>
<template>
<div :class="{'has-logo':showLogo}" :style="{ backgroundColor: settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
:text-color="settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
:unique-opened="true"
:active-text-color="settings.theme"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item
v-for="(route, index) in sidebarRouters"
:key="route.path + index"
:item="route"
:base-path="route.path"
/>
</el-menu>
</el-scrollbar>
</div>
</template>
<script>
import { mapGetters, mapState } from "vuex";
import Logo from "./Logo";
import SidebarItem from "./SidebarItem";
import variables from "@/assets/styles/variables.scss";
export default {
components: { SidebarItem, Logo },
computed: {
...mapState(["settings"]),
...mapGetters(["sidebarRouters", "sidebar"]),
activeMenu() {
const route = this.$route;
const { meta, path } = route;
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu;
}
return path;
},
showLogo() {
return this.$store.state.settings.sidebarLogo;
},
variables() {
return variables;
},
isCollapse() {
return !this.sidebar.opened;
}
}
};
</script>
<!--矢量图组件
前置设置: 引入依赖`svg-sprite-loader`, 复制`assets/icons`整个的目录到项目对应路径中, 在`main.js`中引入`assets/icons`.
使用方法: 将要显示的svg存放在`assets/icons/svg`中, 使用标签`<svg-icon icon-class="对应svg的文件名(不包含后缀)" />` -->
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
......
......@@ -22,25 +22,6 @@
</el-menu-item>
</el-menu>
</div>
<div class="header-right">
<div class="header-user-con">
<!-- 切换主题 -->
<!--<div @click="handleChangeStyle()">
<el-tooltip content="切换主题" placement="bottom">
<i :class="globalTheme ? 'el-icon-moon' : 'el-icon-sunny'"></i>
</el-tooltip>
</div>-->
<!-- 用户头像 -->
<div class="user-avatar">
<img alt="头像" src="@img/img.jpg"/>
</div>
<!-- 用户名下拉菜单 -->
<div class="username">{{ username }}</div>
<div class="separator"></div>
<div class="logout" @click="handleCommand('logout')">退出</div>
</div>
</div>
</div>
</div>
......
<!--带有一级菜单的页面 父页面-->
<template>
<el-container class="app-body">
<el-header ref="head" class="app-body-head">
<!-- 页面头部部分 -->
<div class="header">
<div class="logo">
<img class="logo-image" src="@img/logo2.png" alt="图标" />
</div>
<div class="right">
<!-- 水平一级菜单 -->
<div class="first-menu">
<el-menu
mode="horizontal"
text-color="rgba(255, 255, 255, 0.75)"
active-text-color="#FFFFFF"
:default-active="currentRoutePath"
router>
<el-menu-item
v-for="({path, title}, index) in menuItemList"
:index="path"
:route="{path}"
:key="index">
<span slot="title">{{ title }}</span>
</el-menu-item>
</el-menu>
</div>
<div class="header-right">
<div class="header-user-con">
<!-- 切换主题 -->
<!--<div @click="handleChangeStyle()">
<el-tooltip content="切换主题" placement="bottom">
<i :class="globalTheme ? 'el-icon-moon' : 'el-icon-sunny'"></i>
</el-tooltip>
</div>-->
<!-- 用户头像 -->
<div class="user-avatar">
<img alt="头像" src="@img/img.jpg" />
</div>
<!-- 用户名下拉菜单 -->
<div class="username">{{ username }}</div>
<div class="separator"></div>
<div class="logout" @click="handleCommand('logout')">退出</div>
</div>
</div>
</div>
</div>
</el-header>
<!--根据头部高度进行主体大小的动态修改-->
<el-main class="app-body-main" :style="{height: `calc(100% - ${headerHeight}px`}">
<router-view v-if="route" />
<slot v-else></slot>
</el-main>
</el-container>
</template>
<script>
export default {
name: "First",
props: {
/**
* 头部导航菜单信息列表
*/
menuItemList: {
type: Array,
default: _ => []
},
/**
* 是否以路由方式进行运行(路由模式: 可以作为拥有二级菜单的页面的父页面)
*/
route: {
type: Boolean,
default: true
}
},
data() {
return {
// 头部高度
headerHeight: 0
};
},
computed: {
// 从存储中获取用户名
username() {
return localStorage.getItem("ms_username") || "";
},
// 当前路由信息, 用于处理头部菜单默认激活状态
currentRoutePath() {
return this.$route.path;
}
},
mounted() {
// 页面构建完成赋值头部高度
this.headerHeight = this.$refs.head.$el.clientHeight;
},
methods: {
/**
* 切换菜单栏
* @param path 目标路径
*/
handleSelect(path) {
this.$router.push({
path: path
});
},
/**
* 用户名下拉菜单选择事件 根据命令执行对应操作
* @param command 命令 (logout——退出)
*/
handleCommand(command) {
if (command === "logout") {
localStorage.removeItem("ms_username");
this.$router.push({
path: "/Login"
});
}
}
}
};
</script>
<style scoped lang="scss">
.app-body {
height: 100%;
&-head {
padding: 0;
height: auto !important;
.header {
background: #2B2E31;
position: relative;
box-sizing: border-box;
display: flex;
width: 100%;
height: 64px;
font-size: 16px;
.logo {
flex-shrink: 0;
flex-grow: 0;
width: 240px;
height: auto;
display: flex;
flex-direction: column;
justify-content: center;
padding-left: 20px;
&-image {
width: 161px;
height: 40px;
}
}
.right {
flex: 1;
display: flex;
justify-content: space-between;
// 头部菜单
.first-menu {
.el-menu {
height: 100%;
margin-left: -15px;
&--horizontal {
border-bottom: none !important;
background: transparent;
}
&-item {
height: 100%;
border: none;
&:hover, &:focus {
background: transparent;
}
font-size: 16px;
padding: 0 45px;
}
.is-active {
border-bottom-color: transparent !important;
}
}
}
// 用户头像区域的样式
.header-right {
display: flex;
align-items: center;
padding-right: 20px;
color: white;
font: {
size: 15px;
};
.header-user-con {
display: flex;
align-items: center;
justify-content: center;
height: auto;
}
.user-avatar {
margin-left: 20px;
img {
display: block;
width: 16px;
height: 16px;
border-radius: 50%;
}
}
.username {
margin-left: 10px;
}
.separator {
margin: 0 20px;
background: white;
width: 1px;
height: 16px;
}
}
}
}
}
&-main {
padding: 0;
overflow: auto;
}
}
</style>
\ No newline at end of file
<template>
<el-submenu v-if="menuItem.children" :index="menuItem.path">
<template #title>
<svg-icon v-if="menuItem.meta.icon !== false" :icon-class="menuItem.meta.icon || 'content_dian'" />
<span>{{ menuItem.meta.title }}</span>
</template>
<item v-for="(menuItem, index) in menuItem.children" :menu-item="menuItem" :key="index" />
</el-submenu>
<el-menu-item v-else :index="menuItem.path">
<svg-icon v-if="menuItem.meta.icon !== false" :icon-class="menuItem.meta.icon || 'content_dian'" />
<template #title>
<span>{{ menuItem.meta.title }}</span>
</template>
</el-menu-item>
</template>
<script>
export default {
name: "Item",
props: {
menuItem: {
type: Object,
default: () => ({
path: "",
meta: {
title: ""
},
children: [{
path: "",
meta: {
title: ""
}
}]
})
}
}
};
</script>
<style scoped lang="scss">
.el-submenu {
::v-deep &__title {
padding: 0 20px;
font-size: 14px;
height: 44px;
line-height: 44px;
margin: 0 20px;
&:hover {
background: transparent !important;
}
& > .svg-icon {
margin-right: 20px;
stroke: #CADCFB;
}
.el-submenu__icon-arrow {
font-weight: bold;
font-size: 14px;
color: #CADCFB;
}
}
}
.el-menu-item {
padding: 0 20px !important;
font-size: 14px;
height: 44px;
line-height: 44px;
margin: 0 20px;
border-radius: 22px;
&:hover {
background: transparent !important;
}
.svg-icon {
display: none;
width: 8px;
height: 8px;
stroke: #CADCFB;
margin: {
left: 5px;
right: 25px;
};
}
span {
margin-left: 38px;
}
&.is-active {
background: #387DF7 !important;
.svg-icon {
display: inline;
}
span {
margin-left: 0;
}
}
}
.is-opened {
::v-deep .el-submenu__title {
color: white !important;
.el-submenu__icon-arrow {
color: white;
}
}
.el-menu-item {
color: white !important
}
.svg-icon {
stroke: white;
}
}
</style>
\ No newline at end of file
<template>
<div class="side-bar">
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
background-color="#2B2E31"
text-color="#CADCFB"
unique-opened
active-text-color="#FFFFFF"
:collapse-transition="false"
mode="vertical"
router
>
<item v-for="(menuItem, index) in alreadyMenuItemList" :menu-item="menuItem" :key="index" />
</el-menu>
</el-scrollbar>
</div>
</template>
<script>
import { join } from "path";
import Item from "@/layout/Second/Sidebar/Item";
import clone from 'clone';
export default {
name: "Sidebar",
components: {
Item
},
props: {
// 菜单列表
menuItemList: {
type: Array,
required: true
},
// 前缀基础路径
basePath: {
type: String,
default: "/"
}
},
computed: {
activeMenu() {
const route = this.$route;
const { meta, path } = route;
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu;
}
return path;
},
/**
* 初始化好的菜单列表对象
*/
alreadyMenuItemList() {
return this.initMenuItemList(this.menuItemList, this.basePath);
}
},
methods: {
/**
* 对菜单列表进行初始化, 对路径进行处理, 非斜杠开头的路径将会与父路径进行拼接, 第一层的父路径为斜杠.
* @param menuItemList 原始菜单列表
* @param basePath 前缀路径
*/
initMenuItemList(menuItemList, basePath = '/') {
return menuItemList.map(item => {
item = clone(item)
// 判断是否以basePath开头
if (!new RegExp(`^(${basePath}|/)`).test(item.path)) {
// 非法开头则对基于basePath进行拼接
item.path = join(basePath, item.path);
}
// 如果有子菜单递归进行处理
if (Array.isArray(item.children) && item.children.length > 0) {
item.children = this.initMenuItemList(item.children, item.path);
}
// 返回处理结果
return item;
});
}
}
};
</script>
<style scoped lang="scss">
.side-bar {
height: 100%;
::v-deep .scrollbar-wrapper {
overflow-x: hidden;
}
::v-deep .el-scrollbar {
height: 100%;
.el-scrollbar__view {
height: 100%;
.el-menu {
height: 100%;
}
}
}
}
</style>
<template>
<first :route="false">
<!--container 有全局样式 其下面的 el-aside width:240px, el-main padding: 0 @/assets/css/main.css -->
<el-container class="container">
<el-aside>
<Sidebar :menu-item-list="menuItemList" :base-path="basePath" />
</el-aside>
<el-main>
<router-view v-if="route" />
<slot v-else></slot>
</el-main>
</el-container>
</first>
</template>
<script>
import First from "@/layout/First";
import Sidebar from "@/layout/Second/Sidebar";
import { join } from "path";
export default {
name: "Second",
props: {
// 是否以路由方式进行运行(路由模式: 可以作为拥有二级菜单的页面的父页面)
route: {
type: Boolean,
default: true
},
// 菜单列表
menuItemList: {
type: Array,
default: () => [{
path: "",
meta: {
title: ""
},
children: [{
path: "",
meta: {
title: ""
}
}]
}]
},
// 前缀基础路径
basePath: {
type: String,
default: "/"
}
},
components: {
First,
Sidebar
},
data() {
return {};
},
computed: {
},
methods: {
}
};
</script>
<style scoped lang="scss">
.second-body {
height: 100%;
}
</style>
\ No newline at end of file
......@@ -10,58 +10,29 @@ VueRouter.prototype.push = function push(location) {
export default new VueRouter({
routes: [
{
path: "/product-introduction/:pathInfo(.*)",
component: _ => import("@/views/ProductIntroduction"),
},
{
path: "/", // 程序启动默认路由
component: () => import("@/components/common/Whole.vue"),
meta: { title: "整体页面布局" },
redirect: "/product-introduction", // 重定向到首页
redirect: "/product-introduction/teaching-and-academic-affairs/smart-teaching-and-training-platform", // 重定向
children: [
{
path: "/Home",
component: () => import("@/page/Home.vue"),
component: () => import("@/views/Home.vue"),
meta: { title: "首页" }
},
{
path: "/product-introduction",
component: () => import("@/page/ProductIntroduction/index.vue"),
meta: { title: "产品介绍" },
redirect: "/product-introduction/teaching-and-academic-affairs", // 该配置是若点击选择一级菜单时,默认选中并跳转到该一级菜单下的第一个二级菜单
children: [
{
path: "teaching-and-academic-affairs",
component: () => import("@/page/ProductIntroduction/TeachingAndAcademicAffairs.vue"),
meta: { title: "教学与教务" }
},
{
path: "student-work",
component: () => import("@/page/ProductIntroduction/StudentWork.vue"),
meta: { title: "学生工作" }
},
{
path: "research-and-logistics",
component: () => import("@/page/ProductIntroduction/ResearchAndLogistics.vue"),
meta: { title: "科研与后勤" }
},
{
path: "administration-personnel",
component: () => import("@/page/ProductIntroduction/AdministrationPersonnel.vue"),
meta: { title: "行政与人事" }
},
{
path: "big-data-analytics",
component: () => import("@/page/ProductIntroduction/BigDataAnalytics.vue"),
meta: { title: "大数据分析" }
}
]
},
{
path: "/product-cases",
component: () => import("@/page/ProductCases/index.vue"),
component: () => import("@/views/ProductCases/index.vue"),
meta: { title: "产品案例" }
},
{
path: "/company-introduction",
component: () => import("@/page/CompanyIntroduction/index.vue"),
component: () => import("@/views/CompanyIntroduction/index.vue"),
meta: { title: "公司简介" }
},
{
......@@ -71,7 +42,7 @@ export default new VueRouter({
},
{
path: "/permission", // 权限页面
component: () => import("@/page/Permission.vue"),
component: () => import("@/views/Permission.vue"),
meta: {
title: "权限测试",
permission: true
......@@ -79,19 +50,19 @@ export default new VueRouter({
},
{
path: "/404",
component: () => import("@/page/404.vue"),
component: () => import("@/views/404.vue"),
meta: { title: "404" }
},
{
path: "/403",
component: () => import("@/page/403.vue"),
component: () => import("@/views/403.vue"),
meta: { title: "403" }
}
]
},
{
path: "/Login", // 登录页面
component: () => import("@/page/Login.vue"),
component: () => import("@/views/Login.vue"),
meta: { title: "登录" }
},
{
......@@ -100,3 +71,39 @@ export default new VueRouter({
}
]
});
/* path: "/product-introduction",
component: () => import("@/page/ProductIntroduction/index.vue"),
meta: { title: "产品介绍" },
redirect: "/product-introduction/teaching-and-academic-affairs", // 该配置是若点击选择一级菜单时,默认选中并跳转到该一级菜单下的第一个二级菜单
children: [
{
path: "teaching-and-academic-affairs",
component: () => import("@/page/ProductIntroduction/TeachingAndAcademicAffairs.vue"),
meta: { title: "教学与教务" }
},
{
path: "student-work",
component: () => import("@/page/ProductIntroduction/StudentWork.vue"),
meta: { title: "学生工作" }
},
{
path: "research-and-logistics",
component: () => import("@/page/ProductIntroduction/ResearchAndLogistics.vue"),
meta: { title: "科研与后勤" }
},
{
path: "administration-personnel",
component: () => import("@/page/ProductIntroduction/AdministrationPersonnel.vue"),
meta: { title: "行政与人事" }
},
{
path: "big-data-analytics",
component: () => import("@/page/ProductIntroduction/BigDataAnalytics.vue"),
meta: { title: "大数据分析" }
}
]
},
{
*/
\ No newline at end of file
......@@ -81,3 +81,29 @@ export function isArray(arg) {
}
return Array.isArray(arg)
}
/**
* 判断是否为空
*/
export function empty(value) {
switch (typeof value) {
case 'undefined':
return true
case 'string':
if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length === 0) { return true }
break
case 'boolean':
if (!value) { return true }
break
case 'number':
if (value === 0 || isNaN(value)) { return true }
break
case 'object':
if (value === null || value.length === 0) { return true }
for (var i in value) {
return false
}
return true
}
return false
}
<template>
<div class="single-info">
<div class="head">
<div class="head-text">{{ title }}</div>
<div class="head-separator"></div>
</div>
<div class="content">
<div class="content-text" v-html="content" />
</div>
<div class="image">
<div class="image-body">
<div class="image-body-modal">
<el-button round @click="gotoNewTab(url)">产品预览 →</el-button>
</div>
<el-image :src="image" :preview-src-list="imageList" />
</div>
</div>
</div>
</template>
<script>
export default {
name: "Single",
props: {
// 显示标题
title: {
type: String,
required: true
},
// 描述文本
content: {
type: String,
required: true
},
// 跳转地址
url: {
type: String,
default: ""
},
// 封面图片
image: {
type: String,
required: true
},
// 预览图片列表
imageList: {
type: Array,
default: _ => []
}
},
methods: {
/**
* 跳转新页面
* @param url 跳转地址
*/
gotoNewTab(url) {
if (url)
window.open(url, "_blank");
else
this.$Message.error('缺少地址信息')
}
}
};
</script>
<style scoped lang="scss">
.single-info {
padding: 30px 0 25px;
.head {
margin-bottom: 28px;
display: flex;
flex-direction: column;
align-items: center;
&-text {
margin-bottom: 14px;
font-size: 20px;
font-weight: 400;
color: #222222;
line-height: 22px;
}
&-separator {
width: 32px;
height: 4px;
background: #387DF7;
}
}
.content {
display: flex;
justify-content: center;
margin-bottom: 28px;
&-text {
flex: 7738;
}
&::before {
display: block;
content: '';
flex: 1131;
}
&::after {
display: block;
content: '';
flex: 1131;
}
}
.image {
display: flex;
justify-content: center;
&-body {
position: relative;
width: 1097px;
height: 636px;
&-modal {
display: none;
z-index: 2;
position: absolute;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, .4);
.el-button {
width: 125px;
height: 38px;
padding: 6px 17px;
color: white;
background: #387DF7;
border: 1px solid #387DF7;
}
}
&:hover {
.image-body-modal {
display: flex;
}
}
.el-image {
width: 1097px;
height: 636px;
}
}
}
}
</style>
\ No newline at end of file
This diff is collapsed.
......@@ -42,7 +42,7 @@ export default {
require("@img/ProductIntroduction/StudentWork/zonghepince4.png")
]
}, {
title: "招生系统(小程序)",
title: "招生系统",
url: "http://192.168.10.4:10280/project56/index.html",
content: "主要用于职业技术类院校或技工院校等中职、高职院校的招生过程管理;\n包含常见的招生团队管理、生源管理、费用管理、报销管理、招生渠道管理、工具管理以及数据分析等模块。可精细化管理,降低招生成本,提高销售转化。",
image: require("@img/ProductIntroduction/StudentWork/zhaoshengxitong1.png"),
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment