Commit a0c51663 by fanx

init

0 parents
{
"presets": [
["@babel/preset-env", {
"modules": false
}]
],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
\ No newline at end of file \ No newline at end of file
> 1%
last 2 versions
not dead
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
VUE_APP_NODE_ENV="development"
VUE_APP_VERSION="0.0.1"
VUE_APP_API="http://api.k780.com/"
VUE_APP_NODE_ENV="development"
VUE_APP_VERSION="0.0.1"
VUE_APP_API="http://api.k780.com/"
VUE_APP_NODE_ENV="production"
VUE_APP_VERSION="0.0.1"
VUE_APP_API="http://api.k780.com/"
VUE_APP_NODE_ENV="test"
VUE_APP_VERSION="0.0.1"
VUE_APP_API="http://api.k780.com/"
module.exports = {
root: true,
env: {
node: true
},
extends: [
'plugin:vue/essential',
'@vue/standard'
],
parserOptions: {
parser: 'babel-eslint'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'arrow-parens': 0, // allow paren-less arrow functions
'generator-star-spacing': 0, // allow async-await
"no-unused-vars": [1, {
"vars": "local", //允许声明未使用变量
"args": "none" //参数不检查
}],
"semi": [0], //关闭语句强制分号结尾
"no-multiple-empty-lines": [0, { "max": 100 }], //空行最多不能超过100行
"no-mixed-spaces-and-tabs": [0], //关闭禁止混用tab和空格
"comma-dangle": [2, 'never'], //解决最后一行 有一个逗号问题
}
}
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# nafm-web-element
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
This diff could not be displayed because it is too large.
{
"name": "nafm-web-element",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vue-cli-service serve",
"test": "vue-cli-service serve --mode test",
"pro": "vue-cli-service serve --mode pro",
"build:dev": "vue-cli-service build --mode dev",
"build:test": "vue-cli-service build --mode test",
"build:pro": "vue-cli-service build --mode pro --modern",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.1",
"core-js": "^3.6.5",
"element-ui": "^2.15.1",
"js-cookie": "^2.2.1",
"nprogress": "^0.2.0",
"vue": "^2.6.11",
"vue-pdf": "^4.2.0",
"vue-router": "^3.2.0",
"vuex": "^3.4.0",
"vuex-persist": "^3.1.3"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/eslint-config-standard": "^5.1.2",
"babel-eslint": "^10.1.0",
"babel-plugin-component": "^1.1.1",
"compression-webpack-plugin": "^3.1.0",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^6.2.2",
"node-sass": "^4.12.0",
"sass-loader": "^8.0.2",
"vue-template-compiler": "^2.6.11",
"webpack-spritesmith": "^1.1.0"
}
}
No preview for this file type
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
No preview for this file type
No preview for this file type
<template>
<div id="app">
<router-view/>
</div>
</template>
\ No newline at end of file \ No newline at end of file
html {
padding: 0;
margin: 0;
-webkit-tap-highlight-color: transparent;
}
body{
padding: 0;
margin: 0;
height: 100vh;
}
#app{
height: 100vh;
}
.icon {
width: 1.2em;
height: 1.2em;
fill: currentColor;
overflow: hidden;
}
.right {
text-align: right;
}
.left {
text-align: left;
}
.center {
text-align: center;
}
.clearfix {
*zoom: 1;
}
.clearfix:before,
.clearfix:after {
display: block;
content: "";
line-height: 0;
}
.clearfix:after {
clear: both;
}
.mr5 {
margin-right: 5px;
}
.mr10 {
margin-right: 10px;
}
.mt10 {
margin-top: 10px;
}
.ml20 {
margin-left: 20px;
}
.mr20 {
margin-right: 20px;
}
.mt20 {
margin-top: 20px;
}
.pad10 {
padding: 10px;
}
.pad20 {
padding: 20px;
}
.pl5 {
padding-left: 5px !important;
}
.pc50 {
width: 50% !important;
}
.pl20 {
padding-left: 20px;
}
.pr20 {
padding-right: 20px;
}
.pl15 {
padding-left: 15px;
}
.pr15 {
padding-right: 15px;
}
.buttonLeft5 {
margin-left: 5px;
}
.w100 {
width: 100%;
}
.w140 {
width: 140px !important;
}
.w224 {
width: 224px !important;
}
\ No newline at end of file \ No newline at end of file
/**
* I think element-ui's default theme color is too light for long-term use.
* So I modified the default color and you can modify it to your liking.
**/
/* theme color */
$--color-primary: #1890ff;
$--color-success: #13ce66;
$--color-warning: #ffba00;
$--color-danger: #ff4949;
// $--color-info: #1E1E1E;
$--button-font-weight: 400;
// $--color-text-regular: #1f2d3d;
$--border-color-light: #dfe4ed;
$--border-color-lighter: #e6ebf5;
$--table-border: 1px solid #dfe6ec;
/* icon font path, required */
$--font-path: "~element-ui/lib/theme-chalk/fonts";
@import "~element-ui/packages/theme-chalk/src/index";
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
theme: $--color-primary;
}
@import 'element-variables.scss';
@import 'mixins.scss';
@import 'variable.scss';
@import 'sidebar.scss';
@import 'common.scss';
\ No newline at end of file \ No newline at end of file
@mixin round-borders ($radius) {
border-radius: $radius;
-moz-border-radius: $radius;
-webkit-border-radius: $radius;
}
@mixin justify-align-center() {
display: flex;
justify-content: center;
align-items: center;
}
@mixin justify-align-between() {
display: flex;
align-items: center;
justify-content: space-between;
}
@mixin justify-between {
display: flex;
justify-content: space-between;
}
@mixin justify-center() {
display: flex;
justify-content: center;
}
@mixin align-center() {
display: flex;
align-items: center;
}
@mixin ellipsis() {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 固定显示多上行,size:固定显示的行数
@mixin line-clamp($size) {
overflow: hidden;
text-overflow: ellipsis;
line-clamp: $size;
-webkit-line-clamp: $size;
display: -webkit-box;
-webkit-box-orient: vertical;
}
// 旋转角度
@mixin transform-rotate($deg) {
transform: rotate($deg);
-ms-transform: rotate($deg);
-moz-transform: rotate($deg);
-webkit-transform: rotate($deg);
-o-transform: rotate($deg);
}
\ No newline at end of file \ No newline at end of file
#app {
.main-container {
min-height: 100%;
transition: margin-left .28s;
margin-left: $sideBarWidth;
position: relative;
}
.sidebar-container {
transition: width 0.28s;
width: $sideBarWidth !important;
background-color: $menuBg;
height: 100%;
position: fixed;
font-size: 0px;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
// reset element-ui css
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
}
.scrollbar-wrapper {
overflow-x: hidden !important;
}
.el-scrollbar__bar.is-vertical {
right: 0px;
}
.el-scrollbar {
height: 100%;
}
&.has-logo {
.el-scrollbar {
height: calc(100% - 50px);
}
}
.is-horizontal {
display: none;
}
a {
display: inline-block;
width: 100%;
overflow: hidden;
}
.svg-icon {
margin-right: 16px;
}
.sub-el-icon {
margin-right: 12px;
margin-left: -2px;
}
.el-menu {
border: none;
height: 100%;
width: 100% !important;
}
// menu hover
.submenu-title-noDropdown,
.el-submenu__title {
&:hover {
background-color: $menuHover !important;
}
}
.is-active>.el-submenu__title {
color: $subMenuActiveText !important;
}
& .nest-menu .el-submenu>.el-submenu__title,
& .el-submenu .el-menu-item {
min-width: $sideBarWidth !important;
background-color: $subMenuBg !important;
&:hover {
background-color: $subMenuHover !important;
}
}
}
.hideSidebar {
.sidebar-container {
width: 54px !important;
}
.main-container {
margin-left: 54px;
}
.submenu-title-noDropdown {
padding: 0 !important;
position: relative;
.el-tooltip {
padding: 0 !important;
.svg-icon {
margin-left: 20px;
}
.sub-el-icon {
margin-left: 19px;
}
}
}
.el-submenu {
overflow: hidden;
&>.el-submenu__title {
padding: 0 !important;
.svg-icon {
margin-left: 20px;
}
.sub-el-icon {
margin-left: 19px;
}
.el-submenu__icon-arrow {
display: none;
}
}
}
.el-menu--collapse {
.el-submenu {
&>.el-submenu__title {
&>span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
}
}
}
}
.el-menu--collapse .el-menu .el-submenu {
min-width: $sideBarWidth !important;
}
// mobile responsive
.mobile {
.main-container {
margin-left: 0px;
}
.sidebar-container {
transition: transform .28s;
width: $sideBarWidth !important;
}
&.hideSidebar {
.sidebar-container {
pointer-events: none;
transition-duration: 0.3s;
transform: translate3d(-$sideBarWidth, 0, 0);
}
}
}
.withoutAnimation {
.main-container,
.sidebar-container {
transition: none;
}
}
}
// when menu collapsed
.el-menu--vertical {
&>.el-menu {
.svg-icon {
margin-right: 16px;
}
.sub-el-icon {
margin-right: 12px;
margin-left: -2px;
}
}
.nest-menu .el-submenu>.el-submenu__title,
.el-menu-item {
&:hover {
// you can use $subMenuHover
background-color: $menuHover !important;
}
}
// the scroll bar appears when the subMenu is too long
>.el-menu--popup {
max-height: 100vh;
overflow-y: auto;
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
border-radius: 20px;
}
}
}
\ No newline at end of file \ No newline at end of file
.icon-sprite { display: inline-block; background-image: url(../images/sprite.png); background-size: 200px 200px; }
.icon-logo { width: 200px; height: 200px; background-position: 0px 0px; }
\ No newline at end of file \ No newline at end of file
// base color
$blue:#324157;
$light-blue:#3A71A8;
$red:#C03639;
$pink: #E65D6E;
$green: #30B08F;
$tiffany: #4AB7BD;
$yellow:#FEC171;
$panGreen: #30B08F;
$headerBg: #177EC6;
// sidebar
$menuText:#bfcbd9;
$menuActiveText:#409EFF;
$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951
$menuBg:#304156;
$menuHover:#263445;
$subMenuBg:#1f2d3d;
$subMenuHover:#001528;
$sideBarWidth: 210px;
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
menuText: $menuText;
menuActiveText: $menuActiveText;
subMenuActiveText: $subMenuActiveText;
menuBg: $menuBg;
menuHover: $menuHover;
subMenuBg: $subMenuBg;
subMenuHover: $subMenuHover;
sideBarWidth: $sideBarWidth;
headerBg: $headerBg;
}
import Vue from 'vue';
import {
Pagination,
Dialog,
Autocomplete,
Dropdown,
DropdownMenu,
DropdownItem,
Menu,
Submenu,
MenuItem,
MenuItemGroup,
Input,
InputNumber,
Radio,
RadioGroup,
RadioButton,
Checkbox,
CheckboxButton,
CheckboxGroup,
Switch,
Select,
Option,
OptionGroup,
Button,
ButtonGroup,
Table,
TableColumn,
DatePicker,
TimeSelect,
TimePicker,
Popover,
Tooltip,
Breadcrumb,
BreadcrumbItem,
Form,
FormItem,
Tabs,
TabPane,
Tag,
Tree,
Alert,
Slider,
Icon,
Row,
Col,
Upload,
Progress,
Spinner,
Badge,
Card,
Rate,
Steps,
Step,
Carousel,
CarouselItem,
Collapse,
CollapseItem,
Cascader,
ColorPicker,
Transfer,
Container,
Header,
Aside,
Main,
Footer,
Timeline,
TimelineItem,
Link,
Divider,
Image,
Calendar,
Backtop,
PageHeader,
CascaderPanel,
Loading,
MessageBox,
Message,
Notification
} from 'element-ui';
const component = [
Pagination,
Dialog,
Autocomplete,
Dropdown,
DropdownMenu,
DropdownItem,
Menu,
Submenu,
MenuItem,
MenuItemGroup,
Input,
InputNumber,
Radio,
RadioGroup,
RadioButton,
Checkbox,
CheckboxButton,
CheckboxGroup,
Switch,
Select,
Option,
OptionGroup,
Button,
ButtonGroup,
Table,
TableColumn,
DatePicker,
TimeSelect,
TimePicker,
Popover,
Tooltip,
Breadcrumb,
BreadcrumbItem,
Form,
FormItem,
Tabs,
TabPane,
Tag,
Tree,
Alert,
Slider,
Icon,
Row,
Col,
Upload,
Progress,
Spinner,
Badge,
Card,
Rate,
Steps,
Step,
Carousel,
CarouselItem,
Collapse,
CollapseItem,
Cascader,
ColorPicker,
Transfer,
Container,
Header,
Aside,
Main,
Footer,
Timeline,
TimelineItem,
Link,
Divider,
Image,
Calendar,
Backtop,
PageHeader,
CascaderPanel,
];
component.forEach(item => Vue.use(item));
Object.assign(Vue.prototype, {
$message: Message,
$notify: Notification,
$prompt: MessageBox.prompt,
$confirm: MessageBox.confirm,
$alert: MessageBox.alert,
$msgbox: MessageBox,
$loading: Loading.service,
})
//公共组件
// import Header from '@/components/Header';
// [
// Header
// ].forEach(item => Vue.component(item.name, item))
\ No newline at end of file \ No newline at end of file
<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">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
isExternal() {
return isExternal(this.iconClass)
},
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
display: inline-block;
}
</style>
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg component
// register globally
Vue.component('svg-icon', SvgIcon)
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg>
\ No newline at end of file \ No newline at end of file
# replace default config
# multipass: true
# full: true
plugins:
# - name
#
# or:
# - name: false
# - name: true
#
# or:
# - name:
# param1: 1
# param2: 2
- removeAttrs:
attrs:
- 'fill'
- 'fill-rule'
<template>
<div class="app-main-wrapper">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'AppMain'
}
</script>
<style lang="scss" scoped>
.app-main-wrapper {
display: flex;
flex: 1;
padding: 15px;
}
</style>
\ No newline at end of file \ No newline at end of file
<template>
<div class="header-wrapper">
<div class="header-left">
logo
</div>
<div class="header-right">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>查看</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<span>王小虎</span>
</div>
</div>
</template>
<script>
export default {
name: 'HeaderNav'
}
</script>
<style lang="scss" scoped>
@import '~@/assets/css/variable.scss';
.header-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 15px;
height: 60px;
background: $menuBg;
color: #fff;
border-bottom: 1px solid $menuHover;
}
</style>
\ No newline at end of file \ No newline at end of file
<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) {
if (icon.includes('el-icon')) {
vnodes.push(<i class={[icon, 'sub-el-icon']} />)
} else {
vnodes.push(<svg-icon icon-class={icon}/>)
}
}
if (title) {
vnodes.push(<span slot='title'>{(title)}</span>)
}
return vnodes
}
}
</script>
<style scoped>
.sub-el-icon {
color: currentColor;
width: 1em;
height: 1em;
}
</style>
<template>
<component :is="type" v-bind="linkProps(to)">
<slot />
</component>
</template>
<script>
import { isExternal } from '@/utils/validate'
export default {
props: {
to: {
type: String,
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 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)">
<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'
export default {
name: 'SidebarItem',
components: { Item, AppLink },
props: {
// route object
item: {
type: Object,
required: true
},
isNest: {
type: Boolean,
default: false
},
basePath: {
type: String,
default: ''
}
},
data() {
// To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
// TODO: refactor with render function
this.onlyOneChild = null
return {}
},
methods: {
hasOneShowingChild(children = [], parent) {
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) {
if (isExternal(routePath)) {
return routePath
}
if (isExternal(this.basePath)) {
return this.basePath
}
return path.resolve(this.basePath, routePath)
}
}
}
</script>
<template>
<el-aside class="slide-wrapper">
<el-menu :default-active="activeMenu" :background-color="variables.menuBg" :text-color="variables.menuText" :unique-opened="false" :active-text-color="variables.menuActiveText"
:collapse-transition="false" mode="vertical">
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
</el-aside>
</template>
<script>
// import { mapGetters } from 'vuex';
import SidebarItem from './SidebarItem'
import variables from '@/assets/css/variable.scss'
import {asyncRoutes} from '@/router/index'
export default {
name: 'SideBar',
components: {
SidebarItem
},
data() {
return {
permission_routes: asyncRoutes
}
},
computed: {
// ...mapGetters(['permission_routes']),
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
},
variables() {
return variables
},
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/css/variable";
.slide-wrapper {
height: 100%;
}
.el-menu {
height: 100%;
}
</style>
\ No newline at end of file \ No newline at end of file
<template>
<div class="app-wrapper">
<HeaderNav />
<div class="app-content">
<SideBar />
<AppMain />
</div>
</div>
</template>
<script>
import HeaderNav from './components/HeaderNav'
import SideBar from './components/Sidebar'
import AppMain from './components/AppMain'
export default {
name: 'Layout',
components: {
HeaderNav,
SideBar,
AppMain
}
}
</script>
<style lang="scss" scoped>
.app-wrapper {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.app-content {
display: flex;
flex-direction: row;
justify-content: space-between;
}
</style>
\ No newline at end of file \ No newline at end of file
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/components/Element-Ui'
import '@/assets/css/index.scss'
import './icons' // icon
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/layout/';
import portalRouter from './modules/portal'
Vue.use(VueRouter)
export const constantRoutes = [
{
path: '/',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path(.*)',
component: () => import(/* webpackChunkName: "redirect" */ '@/views/redirect/index'),
}
]
},
{
path: '/login',
component: () => import(/* webpackChunkName: "login" */ '@/views/login/index'),
hidden: true
},
]
export const asyncRoutes = [
portalRouter
]
const routes = [].concat(constantRoutes, asyncRoutes)
const router = new VueRouter({
routes
})
export default router
import Layout from '@/layout/'
const portalRouter = {
path: '/portal',
component: Layout,
name: 'portal',
meta: {
icon: 'profile',
title: '门户管理'
},
children: [
{
path: '/portal/carouselManage',
name: 'carouselManage',
exact: true,
meta: {
title: '轮播图管理'
},
component: () => import(/* webpackChunkName: "carouselManage" */ '@/views/portalManage/carouselManage/index.vue'), // 轮播图管理
},
{
path: '/portal/scrollBarManage',
name: 'scrollBarManage',
exact: true,
meta: {
title: '滚动条管理'
},
component: () => import(/* webpackChunkName: "scrollBarManage" */ '@/views/portalManage/scrollBarManage/index.vue'), // 滚动条管理
},
{
path: '/portal/pdf',
name: 'pdf',
exact: true,
meta: {
title: '滚动条管理'
},
component: () => import(/* webpackChunkName: "pdf" */ '@/views/portalManage/pdf/index.vue'), // 滚动条管理
},
]
}
export default portalRouter;
\ No newline at end of file \ No newline at end of file
const getter = {
permission_routes: state => state.permission.routes,
}
export default getter
\ No newline at end of file \ No newline at end of file
import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from 'vuex-persist'
import createLogger from 'vuex/dist/logger'
import portal from './modules/portal'
import permission from './modules/permission'
import getters from './getters'
// vuex 数据存储 进行本地存储操作
const vuexLocal = new VuexPersistence({
storage: window.localStorage,
modules: ["portal"]
})
Vue.use(Vuex)
const store = new Vuex.Store({
strict: process.env.NODE_ENV !== 'production',
modules: {
portal,
permission
},
getters,
plugins: [vuexLocal.plugin, createLogger()]
})
export default store
import { asyncRoutes, constantRoutes } from '@/router'
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
export default {
namespaced: true,
state: {
routes: [],
addRoutes: []
},
mutations: {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = asyncRoutes //constantRoutes.concat(routes)
}
},
actions: {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
}
export default {
namespaced: true,
state: {
},
mutations: {
},
actions: {
},
modules: {
}
}
import Cookies from 'js-cookie'
const TokenKey = 'NAFM_TOKEN'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
\ No newline at end of file \ No newline at end of file
import Vue from 'vue';
import Request from './Request'
const Axios = new Request()
Plugin.install = (Vue) => {
Vue.prototype.$http = Axios
}
Object.assign(window, {
$http: Axios
})
Vue.use(Plugin)
export default Axios
\ No newline at end of file \ No newline at end of file
import axios from 'axios'
const APP_ENV = process.env.VUE_APP_NODE_ENV
const APP_API = process.env.VUE_APP_API
const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求是不存在的,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。'
}
const instance = axios.create({
baseURL: APP_ENV === 'development' ? '/api' : APP_API
})
class Request {
constructor(baseURL) {
this.baseURL = baseURL
}
// 检查返回状态
checkStatus(response) {
console.log(2)
const responseData = response.data
const errorText = response && (codeMessage[response.status] || response.statusText)
if (response && (response.stutus === 200 || response.stutus === 304 || response.status === 400)) {
if (responseData.stutus == 0) {
return responseData
}
if (responseData.status === 401 || responseData.status === 402) {
return Promise.reject(errorText)
}
return Promise.reject(responseData)
}
Promise.reject(response)
}
// 拦截器
interceptors(instance, scope) {
// 请求拦截器
instance.interceptors.request.use(config => {
config.url = baseURL
config.scope = scope
return config
}, error => {
return Promise.reject(error)
})
// 相应拦截器
instance.interceptors.response.use(res => {
return res
}, error => {
const { response, message } = error || {}
if (message && message.includes('timeout')) {
return Promise.reject({ statusText: '网络超时,请重新尝试!' })
}
let errorInfo = response
if (!errorInfo) {
try {
const { request: { status, statusText }, config } = JSON.parse(JSON.stringify(error))
errorInfo = {
status,
statusText,
request: { responseURL: config.url }
}
} catch (e) {
errorInfo = error
}
}
return Promise.reject(errorInfo)
})
}
// 设置请求
setRequest(method, url, data, scope, file = false) {
this.interceptors(instance, scope)
const options = { method, url }
let contentType = ''
if (file) {
contentType = 'multipart/form-data'
} else if (method == 'post') {
contentType = 'application/json'
} else {
contentType = 'application/x-www-form-urlencoded; charset=UTF-8'
}
const headers = {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': contentType
}
Object.assign(options, {
headers,
[method == 'post' ? 'data' : 'params']: data
})
return instance(options).then(this.checkStatus).catch(this.error)
}
// get
get(url, data, scope) {
return this.setRequest('get', url, data, scope)
}
// post
post(url, data, scope) {
console.log(1,url)
return this.setRequest('post', url, data, scope)
}
// GET
GET(url, data, scope) {
return this.setRequest('get', url, data, scope).then(this.success)
}
// POST
POST(url, data, scope) {
return this.setRequest('post', url, data, scope).them(this.success)
}
// 错误
error(e) {
return Promise.reject(e)
}
// 成功
success(da) {
return da.data
}
// 文件成功
fileSuccess(data) {
return data
}
}
export default Request;
\ No newline at end of file \ No newline at end of file
const breadTextConfig = {
// dashboard
'/dashboard/home': ['仪表盘', '首页'],
// 门户管理
'/portal/carouselManage': ['门户管理', '轮播图管理'],
'/portal/scrollBarManage': ['门户管理', '滚动条管理'],
// 表单
'/form/basicForm': ['表单', '基础表单'],
'/form/stepForm': ['表单', '步骤表单']
}
export default breadTextConfig
\ No newline at end of file \ No newline at end of file
const tableScroll = (columns, y) => {
const status = typeof y == "string" || typeof y == 'number'
const status_two = typeof columns == "string" || typeof columns == 'number'
let _y = status ? y : document.body.clientHeight - y.getBoundingClientRect().top - 150;
let opt = {
x: status_two ? columns : columns.reduce((c, n) => {
let cn = typeof c == "number" ? c : parseInt(c['width'] || 300);
let nn = parseInt(n['width'] || 300);
return cn + nn;
}),
y: Math.max(_y, 300)
}
return opt
}
// 封装接口返回数据
const handlePromise = (promise) => {
return promise.then(res => {
return Promise.resolve(res)
}).catch(e => {
return Promise.resolve(res)
})
}
export {
tableScroll,
handlePromise
}
\ No newline at end of file \ No newline at end of file
/**
* Created by PanJiaChen on 16/11/18.
*/
/**
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUsername(str) {
const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0
}
/**
* @param {string} url
* @returns {Boolean}
*/
export function validURL(url) {
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return reg.test(url)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validLowerCase(str) {
const reg = /^[a-z]+$/
return reg.test(str)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUpperCase(str) {
const reg = /^[A-Z]+$/
return reg.test(str)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validAlphabets(str) {
const reg = /^[A-Za-z]+$/
return reg.test(str)
}
/**
* @param {string} email
* @returns {Boolean}
*/
export function validEmail(email) {
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return reg.test(email)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function isString(str) {
if (typeof str === 'string' || str instanceof String) {
return true
}
return false
}
/**
* @param {Array} arg
* @returns {Boolean}
*/
export function isArray(arg) {
if (typeof Array.isArray === 'undefined') {
return Object.prototype.toString.call(arg) === '[object Array]'
}
return Array.isArray(arg)
}
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<template>
<div>
login
</div>
</template>
<script>
export default {
name: 'Login'
}
</script>
\ No newline at end of file \ No newline at end of file
<template>
<div>11</div>
</template>
<script>
export default {
name: 'CarouseManage',
}
</script>
\ No newline at end of file \ No newline at end of file
<template>
<div style="padding:30px;">
<Pdf v-for="i in numPages" :key="i" :src="src" :page="i" />
</div>
</template>
<script>
import Pdf from 'vue-pdf'
export default {
components: {
Pdf
},
data() {
return {
fileUrl: `${window.location.origin}/test1.pdf`,
numPages: undefined,
src: '',
dialogVisible2: false
}
},
mounted() {
this.src = Pdf.createLoadingTask(this.fileUrl)
this.src.promise.then(pdf => {
this.numPages = pdf.numPages
})
},
methods: {
}
}
</script>
<template>
<div>
滚动条管理
</div>
</template>
\ No newline at end of file \ No newline at end of file
<script>
export default {
created() {
const { params, query } = this.$route
const { path } = params
this.$router.replace({ path: '/' + path, query })
},
render: function(h) {
return h() // avoid warning message
}
}
</script>
const path = require('path')
const CompressionPlugin = require('compression-webpack-plugin')
const SpritesmithPlugin = require('webpack-spritesmith') // 雪碧图
const environment = process.env.VUE_APP_NODE_ENV // 定义变量 环境
const app_version = process.env.VUE_APP_VERSION // 定义变量 版本号
// cdn
const cdn = {
// 开发环境
dev: {
css: [],
js: []
},
// 生产环境
build: {
css: [],
js: [
// `https://lib.baomitu.com/vue/2.6.11/vue.min.js`,
// `https://lib.baomitu.com/vue-router/3.1.3/vue-router.min.js`,
// `https://lib.baomitu.com/vuex/3.1.2/vuex.min.js`
]
}
}
// 雪碧图的自定义模板
const templateFunction = function (data) {
var shared = '.icon-sprite { display: inline-block; background-image: url(I); background-size: Dpx Hpx; }'
.replace('I', data.sprites[0].image)
.replace('D', data.sprites[0].total_width)
.replace('H', data.sprites[0].total_height)
var perSprite = data.sprites.map(function (sprite) {
return '.icon-N { width: Wpx; height: Hpx; background-position: Xpx Ypx; }'
.replace('N', sprite.name.replace(/_/g, '-'))
.replace('W', sprite.width)
.replace('H', sprite.height)
.replace('X', sprite.offset_x)
.replace('Y', sprite.offset_y)
}).join('\n')
return shared + '\n' + perSprite
}
module.exports = {
outputDir: "dist",
assetsDir: "assets",
publicPath: "./",
devServer: {
host: '0.0.0.0',
port: "8080",
https: false,
hotOnly: false,
overlay: {
warning: false,
errors: false
},
proxy: {
'/api': {
// 测试环境
target: process.env.VUE_APP_API, // http://192.168.0.114:8000/
changeOrigin: true, // 是否跨域
pathRewrite: {
'^/api': '' // 需要rewrite重写的, //
}
}
},
},
pages: {
index: {
entry: "./src/main.js",
template: path.join(__dirname, "public/index.html"),
filename: 'index.html',
title: 'NAFMII-WEB',
chunks: ['chunk-vendors', 'chunk-common', 'index'],
// cdn: environment === 'development' ? cdn.dev : cdn.build
}
},
productionSourceMap: false,
css: {
loaderOptions: {
less: {
javascriptEnabled: true
},
}
},
configureWebpack: config => {
if (environment === "devproduction" || environment === 'test' || environment === "uat" || environment === "pro") {
//开发环境开启压缩
config.plugins.push(new CompressionPlugin({ // Gzip压缩配置
test: /\.js$|\.html$|\.scss/, // 匹配文件名
threshold: 0,
deleteOriginalAssets: false // 是否删除原文件
}))
}
return {
externals: {
//moment: "moment",
//axios: "axios"
},
resolve: {
modules: ["node_modules", "spritesmith-generated"],
alias: {
// 别名
'@': path.resolve('src'),
}
},
plugins: [
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, './src/assets/icon'),
glob: '*.png'
},
target: { // 输出雪碧图文件及样式文件,这个是打包后,自动生成的雪碧图和样式
image: path.resolve(__dirname, './src/assets/images/sprite.png'),
css: [
[path.resolve(__dirname, './src/assets/css/sprite.scss'), {
// 引用自己的模板
format: 'function_based_template'
}]
]
},
customTemplates: { // 自定义模板入口
function_based_template: templateFunction
},
apiOptions: { // 样式文件中调用雪碧图地址写法
cssImageRef: '../images/sprite.png'
},
spritesmithOptions: { // 让合成的每个图片有一定的距离
padding: 20
}
})
]
}
},
chainWebpack: config => {
const hashName = `assets/js/[name].[hash:8].${environment}.${app_version}.js`
config.output.filename(hashName).end()
config.output.chunkFilename(hashName).end()
// const filename = config.output.store.get('filename')
// const chunkFilename = config.output.store.get('chunkFilename')
// const newfilename = filename.replace('[name]', '[name].[hash:8]')
// const newchunkFilename = chunkFilename.replace('[name]', '[name].[hash:8]')
// config.output.filename((newfilename)).end()
// config.output.chunkFilename(newchunkFilename).end()
},
lintOnSave: false
}
\ No newline at end of file \ No newline at end of file
This diff could not be displayed because it is too large.
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!