Unverified Commit d46ded81 authored by Sendya's avatar Sendya

feat: add pro-layout (ant-design-vue)

parent 555c1bc9
> 1%
last 2 versions
not ie <= 10
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
NODE_ENV=production
IS_ANALYZ=true
\ No newline at end of file
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/strongly-recommended',
'@vue/standard'
],
rules: {
// 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
// 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-console': 'off',
'no-debugger': 'off',
'generator-star-spacing': 'off',
'no-mixed-operators': 0,
'vue/max-attributes-per-line': [
2,
{
'singleline': 5,
'multiline': {
'max': 1,
'allowFirstLine': false
}
}
],
'vue/attribute-hyphenation': 0,
'vue/html-self-closing': 0,
'vue/component-name-in-template-casing': 0,
'vue/html-closing-bracket-spacing': 0,
'vue/singleline-html-element-content-newline': 0,
'vue/no-unused-components': 0,
'vue/multiline-html-element-content-newline': 0,
'vue/no-use-v-if-with-v-for': 0,
'vue/html-closing-bracket-newline': 0,
'vue/no-parsing-error': 0,
'no-tabs': 0,
'quotes': [
2,
'single',
{
'avoidEscape': true,
'allowTemplateLiterals': true
}
],
'semi': [
2,
'never',
{
'beforeStatementContinuationChars': 'never'
}
],
'no-delete-var': 2,
'prefer-const': [
2,
{
'ignoreReadBeforeAssign': false
}
]
},
parserOptions: {
parser: 'babel-eslint'
},
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
jest: true
}
}
]
}
const plugins = []
// lazy load ant-design-vue
// if your use import on Demand, Use this code
plugins.push(['import', {
'libraryName': 'ant-design-vue',
'libraryDirectory': 'es',
'style': true // `style: true` 会加载 less 文件
}])
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
[
'@babel/preset-env',
{
'useBuiltIns': 'entry',
'corejs': 3
}
]
],
plugins
}
// DO NOT CHANGE `storageOptions` CONFIG. UNLESS YOU KNOW WHAT YOU ARE DOING!
export const storageOptions = {
namespace: 'pro__', // key prefix
name: 'ls', // name variable Vue.[ls] or this.[$ls],
storage: 'local' // storage name session, local, memory
}
export default {
navTheme: 'dark',
primaryColor: '#1890FF',
layout: 'sidemenu',
contentWidth: 'Fluid',
fixedHeader: false,
autoHideHeader: false,
fixSiderbar: false,
colorWeak: false,
menu: {
locale: true
},
title: 'Ant Design Pro',
pwa: false,
iconfontUrl: ''
}
const ThemeColorReplacer = require('webpack-theme-color-replacer')
const generate = require('@ant-design/colors/lib/generate').default
const getAntdSerials = (color) => {
// 淡化(即less的tint)
const lightens = new Array(9).fill().map((t, i) => {
return ThemeColorReplacer.varyColor.lighten(color, i / 10)
})
const colorPalettes = generate(color)
const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace('#', '')).join(',')
return lightens.concat(colorPalettes).concat(rgb)
}
const themePluginOption = {
fileName: 'css/theme-colors-[contenthash:8].css',
matchColors: getAntdSerials('#1890ff'), // 主色系列
// 改变样式选择器,解决样式覆盖问题
changeSelector (selector) {
switch (selector) {
case '.ant-calendar-today .ant-calendar-date':
return ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)' + selector
case '.ant-btn:focus,.ant-btn:hover':
return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger):not(.ant-btn-link),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger):not(.ant-btn-link)'
case '.ant-btn.active,.ant-btn:active':
return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger):not(.ant-btn-link),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger):not(.ant-btn-link)'
case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
return ':not(.ant-steps-item-process)' + selector
case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover':
case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover':
return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover'
case '.ant-menu-horizontal > .ant-menu-item-selected > a':
case '.ant-menu-horizontal>.ant-menu-item-selected>a':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a'
case '.ant-menu-horizontal > .ant-menu-item > a:hover':
case '.ant-menu-horizontal>.ant-menu-item>a:hover':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover'
default :
return selector
}
}
}
const dynamicThemePlugin = () => new ThemeColorReplacer(themePluginOption)
module.exports = dynamicThemePlugin
<template>
<div id="app">
<router-view />
</div>
</template>
<style lang="less" scoped>
.nav-container {
width: 400px;
padding-top: 100px;
margin: 0 auto;
ul {
li {
display: inline-block;
margin: 0 10px;
}
}
a {
user-select: none;
text-decoration: none;
&.router-link-active {
color: #FF0000;
}
}
}
</style>
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 128 128" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.6 (67491) - http://www.bohemiancoding.com/sketch -->
<title>Vue</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="69.644116%" y1="0%" x2="69.644116%" y2="100%" id="linearGradient-1">
<stop stop-color="#29CDFF" offset="0%"></stop>
<stop stop-color="#148EFF" offset="37.8600687%"></stop>
<stop stop-color="#0A60FF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="-19.8191553%" y1="-36.7931464%" x2="138.57919%" y2="157.637507%" id="linearGradient-2">
<stop stop-color="#29CDFF" offset="0%"></stop>
<stop stop-color="#0F78FF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="68.1279872%" y1="-35.6905737%" x2="30.4400914%" y2="114.942679%" id="linearGradient-3">
<stop stop-color="#FA8E7D" offset="0%"></stop>
<stop stop-color="#F74A5C" offset="51.2635191%"></stop>
<stop stop-color="#F51D2C" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Vue" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group" transform="translate(19.000000, 9.000000)">
<path d="M89.96,90.48 C78.58,93.48 68.33,83.36 67.62,82.48 L46.6604487,62.2292258 C45.5023849,61.1103236 44.8426845,59.5728835 44.8296987,57.9626396 L44.5035564,17.5209948 C44.4948861,16.4458744 44.0537714,15.4195095 43.2796864,14.6733517 L29.6459999,1.53153737 C28.055475,-0.00160504005 25.5232423,0.0449126588 23.9900999,1.63543756 C23.2715121,2.38092066 22.87,3.37600834 22.87,4.41143746 L22.87,64.3864751 C22.87,67.0807891 23.9572233,69.6611067 25.885409,71.5429748 L63.6004615,108.352061 C65.9466323,110.641873 69.6963584,110.624605 72.0213403,108.313281" id="Path-Copy" fill="url(#linearGradient-1)" fill-rule="nonzero" transform="translate(56.415000, 54.831157) scale(-1, 1) translate(-56.415000, -54.831157) "></path>
<path d="M68,90.1163122 C56.62,93.1163122 45.46,83.36 44.75,82.48 L23.7904487,62.2292258 C22.6323849,61.1103236 21.9726845,59.5728835 21.9596987,57.9626396 L21.6335564,17.5209948 C21.6248861,16.4458744 21.1837714,15.4195095 20.4096864,14.6733517 L6.7759999,1.53153737 C5.185475,-0.00160504005 2.65324232,0.0449126588 1.12009991,1.63543756 C0.401512125,2.38092066 3.90211878e-13,3.37600834 3.90798505e-13,4.41143746 L3.94351218e-13,64.3864751 C3.94681177e-13,67.0807891 1.08722326,69.6611067 3.01540903,71.5429748 L40.7807092,108.401101 C43.1069304,110.671444 46.8180151,110.676525 49.1504445,108.412561" id="Path" fill="url(#linearGradient-2)" fill-rule="nonzero"></path>
<path d="M43.2983488,19.0991931 L27.5566079,3.88246244 C26.7624281,3.11476967 26.7409561,1.84862177 27.5086488,1.05444194 C27.8854826,0.664606611 28.4044438,0.444472651 28.9466386,0.444472651 L60.3925021,0.444472651 C61.4970716,0.444472651 62.3925021,1.33990315 62.3925021,2.44447265 C62.3925021,2.9858375 62.1730396,3.50407742 61.7842512,3.88079942 L46.0801285,19.0975301 C45.3051579,19.8484488 44.0742167,19.8491847 43.2983488,19.0991931 Z" id="Path" fill="url(#linearGradient-3)"></path>
</g>
</g>
</svg>
\ No newline at end of file
import { Cascader } from 'ant-design-vue'
import provinces from 'china-division/dist/provinces.json'
import cities from 'china-division/dist/cities.json'
import areas from 'china-division/dist/areas.json'
areas.forEach(area => {
const matchCity = cities.filter(city => city.code === area.cityCode)[0]
if (matchCity) {
matchCity.children = matchCity.children || []
matchCity.children.push({
label: area.name,
value: area.code
})
}
})
cities.forEach(city => {
const matchProvince = provinces.filter(
province => province.code === city.provinceCode
)[0]
if (matchProvince) {
matchProvince.children = matchProvince.children || []
matchProvince.children.push({
label: city.name,
value: city.code,
children: city.children
})
}
})
const options = provinces.map(province => ({
label: province.name,
value: province.code,
children: province.children
}))
console.log(options)
export default {
name: 'CitySelect2',
render () {
return (
<Cascader options={options} />
)
}
}
import { Button, Radio, Select, Popover } from 'ant-design-vue'
import './index.less'
const filterProvinceList = [{ n: 'A', v: 'A' }, { n: 'F', v: 'F' }, { n: 'G', v: 'G' }, { n: 'H', v: 'H' }, { n: 'J', v: 'J' }, { n: 'L', v: 'L' }, { n: 'N', v: 'N' }, { n: 'Q', v: 'Q' }, { n: 'S', v: 'S' }, { n: 'T', v: 'T' }, { n: 'X', v: 'X' }, { n: 'Y', v: 'Y' }, { n: 'Z', v: 'Z' }, { n: '直辖市', v: '1' }, { n: '港澳', v: '2' }]
const filterCityList = [{ n: 'A', v: 'A' }, { n: 'B', v: 'B' }, { n: 'C', v: 'C' }, { n: 'D', v: 'D' }, { n: 'E', v: 'E' }, { n: 'F', v: 'F' }, { n: 'G', v: 'G' }, { n: 'H', v: 'H' }, { n: 'I', v: 'I' }, { n: 'J', v: 'J' }, { n: 'K', v: 'K' }, { n: 'L', v: 'L' }, { n: 'N', v: 'N' }, { n: 'M', v: 'M' }, { n: 'O', v: 'O' }, { n: 'Q', v: 'Q' }, { n: 'P', v: 'P' }, { n: 'R', v: 'R' }, { n: 'S', v: 'S' }, { n: 'T', v: 'T' }, { n: 'U', v: 'U' }, { n: 'V', v: 'V' }, { n: 'W', v: 'W' }, { n: 'X', v: 'X' }, { n: 'Y', v: 'Y' }, { n: 'Z', v: 'Z' }]
export default {
name: 'CitySelect',
props: {
prefixCls: {
type: String,
default: 'ant-pro-city-select'
},
defaultValue: {
type: String,
default: ''
}
},
data () {
return {
byType: 'city'
}
},
methods: {
renderHeader () {
const handleChange = (e) => {
this.byType = e.target.value
}
const selectFilters = this.byType === 'city' ? filterCityList.map(item => <Button size={'small'}>{item.n}</Button>) : filterProvinceList.map(item => <Button size={'small'}>{item.n}</Button>)
return (
<div class={'city-select-header'}>
<Radio.Group size={'small'} value={this.byType} onChange={handleChange} style={{ marginRight: '16px' }}>
<Radio.Button value={'province'}>按省份</Radio.Button>
<Radio.Button value={'city'}>按城市</Radio.Button>
</Radio.Group>
<Select size={'small'} showSearch={true} placeholder={'输入城市名称搜索'} style={ { width: '200px' } }>
<Select.Option value={'xiamen'}>厦门</Select.Option>
<Select.Option value={'fuzhou'}>福州</Select.Option>
</Select>
<div class={'select-filters'}>
{selectFilters}
</div>
</div>
)
},
renderBody () {
return (
<div class={'city-select-body'}>
</div>
)
}
},
render () {
const classes = {
[`${this.prefixCls}`]: true
}
return (
<div class={classes}>
<Popover overlayStyle={{ width: '400px' }} placement={'topLeft'}>
<div slot="content">
{this.renderHeader()}
{this.renderBody()}
</div>
<Button type={'link'}>北京市</Button>
</Popover>
</div>
)
}
}
@import "../index";
@ant-pro-city-select-prefix : @{ant-pro-prefix}-city-select;
.city-select-header .select-filters {
margin-top: 8px;
button {
min-width: 26px;
min-height: 26px;
line-height: 26px;
margin-right: 4px;
margin-bottom: 4px;
}
}
.city-select-body {
margin-top: 16px;
}
\ No newline at end of file
[
{
"code": null,
"name": "新疆维吾尔自治区",
"cities": [
{
"code": "650100000000",
"name": "乌鲁木齐市"
},
{
"code": "650200000000",
"name": "克拉玛依市"
},
{
"code": "650400000000",
"name": "吐鲁番市"
},
{
"code": "650500000000",
"name": "哈密市"
},
{
"code": "652300000000",
"name": "昌吉回族自治州"
},
{
"code": "652700000000",
"name": "博尔塔拉蒙古自治州"
},
{
"code": "652800000000",
"name": "巴音郭楞蒙古自治州"
},
{
"code": "652900000000",
"name": "阿克苏地区"
},
{
"code": "653000000000",
"name": "克孜勒苏柯尔克孜自治州"
},
{
"code": "653100000000",
"name": "喀什地区"
},
{
"code": "653200000000",
"name": "和田地区"
},
{
"code": "654000000000",
"name": "伊犁哈萨克自治州"
},
{
"code": "654200000000",
"name": "塔城地区"
},
{
"code": "654300000000",
"name": "阿勒泰地区"
},
{
"code": "659000000000",
"name": "自治区直辖县级行政区划"
}
]
},
{
"code": null,
"name": "甘肃省",
"cities": [
{
"code": "620100000000",
"name": "兰州市"
},
{
"code": "620200000000",
"name": "嘉峪关市"
},
{
"code": "620300000000",
"name": "金昌市"
},
{
"code": "620400000000",
"name": "白银市"
},
{
"code": "620500000000",
"name": "天水市"
},
{
"code": "620600000000",
"name": "武威市"
},
{
"code": "620700000000",
"name": "张掖市"
},
{
"code": "620800000000",
"name": "平凉市"
},
{
"code": "620900000000",
"name": "酒泉市"
},
{
"code": "621000000000",
"name": "庆阳市"
},
{
"code": "621100000000",
"name": "定西市"
},
{
"code": "621200000000",
"name": "陇南市"
},
{
"code": "622900000000",
"name": "临夏回族自治州"
},
{
"code": "623000000000",
"name": "甘南藏族自治州"
}
]
},
{
"code": "120100000000",
"name": "天津市",
"cities": []
},
{
"code": null,
"name": "山东省",
"cities": [
{
"code": "370100000000",
"name": "济南市"
},
{
"code": "370200000000",
"name": "青岛市"
},
{
"code": "370300000000",
"name": "淄博市"
},
{
"code": "370400000000",
"name": "枣庄市"
},
{
"code": "370500000000",
"name": "东营市"
},
{
"code": "370600000000",
"name": "烟台市"
},
{
"code": "370700000000",
"name": "潍坊市"
},
{
"code": "370800000000",
"name": "济宁市"
},
{
"code": "370900000000",
"name": "泰安市"
},
{
"code": "371000000000",
"name": "威海市"
},
{
"code": "371100000000",
"name": "日照市"
},
{
"code": "371200000000",
"name": "莱芜市"
},
{
"code": "371300000000",
"name": "临沂市"
},
{
"code": "371400000000",
"name": "德州市"
},
{
"code": "371500000000",
"name": "聊城市"
},
{
"code": "371600000000",
"name": "滨州市"
},
{
"code": "371700000000",
"name": "菏泽市"
}
]
},
{
"code": null,
"name": "西藏自治区",
"cities": [
{
"code": "540100000000",
"name": "拉萨市"
},
{
"code": "540200000000",
"name": "日喀则市"
},
{
"code": "540300000000",
"name": "昌都市"
},
{
"code": "540400000000",
"name": "林芝市"
},
{
"code": "540500000000",
"name": "山南市"
},
{
"code": "540600000000",
"name": "那曲市"
},
{
"code": "542500000000",
"name": "阿里地区"
}
]
},
{
"code": null,
"name": "安徽省",
"cities": [
{
"code": "340100000000",
"name": "合肥市"
},
{
"code": "340200000000",
"name": "芜湖市"
},
{
"code": "340300000000",
"name": "蚌埠市"
},
{
"code": "340400000000",
"name": "淮南市"
},
{
"code": "340500000000",
"name": "马鞍山市"
},
{
"code": "340600000000",
"name": "淮北市"
},
{
"code": "340700000000",
"name": "铜陵市"
},
{
"code": "340800000000",
"name": "安庆市"
},
{
"code": "341000000000",
"name": "黄山市"
},
{
"code": "341100000000",
"name": "滁州市"
},
{
"code": "341200000000",
"name": "阜阳市"
},
{
"code": "341300000000",
"name": "宿州市"
},
{
"code": "341500000000",
"name": "六安市"
},
{
"code": "341600000000",
"name": "亳州市"
},
{
"code": "341700000000",
"name": "池州市"
},
{
"code": "341800000000",
"name": "宣城市"
}
]
},
{
"code": null,
"name": "内蒙古自治区",
"cities": [
{
"code": "150100000000",
"name": "呼和浩特市"
},
{
"code": "150200000000",
"name": "包头市"
},
{
"code": "150300000000",
"name": "乌海市"
},
{
"code": "150400000000",
"name": "赤峰市"
},
{
"code": "150500000000",
"name": "通辽市"
},
{
"code": "150600000000",
"name": "鄂尔多斯市"
},
{
"code": "150700000000",
"name": "呼伦贝尔市"
},
{
"code": "150800000000",
"name": "巴彦淖尔市"
},
{
"code": "150900000000",
"name": "乌兰察布市"
},
{
"code": "152200000000",
"name": "兴安盟"
},
{
"code": "152500000000",
"name": "锡林郭勒盟"
},
{
"code": "152900000000",
"name": "阿拉善盟"
}
]
},
{
"code": null,
"name": "黑龙江省",
"cities": [
{
"code": "230100000000",
"name": "哈尔滨市"
},
{
"code": "230200000000",
"name": "齐齐哈尔市"
},
{
"code": "230300000000",
"name": "鸡西市"
},
{
"code": "230400000000",
"name": "鹤岗市"
},
{
"code": "230500000000",
"name": "双鸭山市"
},
{
"code": "230600000000",
"name": "大庆市"
},
{
"code": "230700000000",
"name": "伊春市"
},
{
"code": "230800000000",
"name": "佳木斯市"
},
{
"code": "230900000000",
"name": "七台河市"
},
{
"code": "231000000000",
"name": "牡丹江市"
},
{
"code": "231100000000",
"name": "黑河市"
},
{
"code": "231200000000",
"name": "绥化市"
},
{
"code": "232700000000",
"name": "大兴安岭地区"
}
]
},
{
"code": "310100000000",
"name": "上海市",
"cities": []
},
{
"code": null,
"name": "山西省",
"cities": [
{
"code": "140100000000",
"name": "太原市"
},
{
"code": "140200000000",
"name": "大同市"
},
{
"code": "140300000000",
"name": "阳泉市"
},
{
"code": "140400000000",
"name": "长治市"
},
{
"code": "140500000000",
"name": "晋城市"
},
{
"code": "140600000000",
"name": "朔州市"
},
{
"code": "140700000000",
"name": "晋中市"
},
{
"code": "140800000000",
"name": "运城市"
},
{
"code": "140900000000",
"name": "忻州市"
},
{
"code": "141000000000",
"name": "临汾市"
},
{
"code": "141100000000",
"name": "吕梁市"
}
]
},
{
"code": "110100000000",
"name": "北京市",
"cities": []
},
{
"code": null,
"name": "云南省",
"cities": [
{
"code": "530100000000",
"name": "昆明市"
},
{
"code": "530300000000",
"name": "曲靖市"
},
{
"code": "530400000000",
"name": "玉溪市"
},
{
"code": "530500000000",
"name": "保山市"
},
{
"code": "530600000000",
"name": "昭通市"
},
{
"code": "530700000000",
"name": "丽江市"
},
{
"code": "530800000000",
"name": "普洱市"
},
{
"code": "530900000000",
"name": "临沧市"
},
{
"code": "532300000000",
"name": "楚雄彝族自治州"
},
{
"code": "532500000000",
"name": "红河哈尼族彝族自治州"
},
{
"code": "532600000000",
"name": "文山壮族苗族自治州"
},
{
"code": "532800000000",
"name": "西双版纳傣族自治州"
},
{
"code": "532900000000",
"name": "大理白族自治州"
},
{
"code": "533100000000",
"name": "德宏傣族景颇族自治州"
},
{
"code": "533300000000",
"name": "怒江傈僳族自治州"
},
{
"code": "533400000000",
"name": "迪庆藏族自治州"
}
]
},
{
"code": "500100000000",
"name": "重庆市",
"cities": []
},
{
"code": null,
"name": "湖北省",
"cities": [
{
"code": "420100000000",
"name": "武汉市"
},
{
"code": "420200000000",
"name": "黄石市"
},
{
"code": "420300000000",
"name": "十堰市"
},
{
"code": "420500000000",
"name": "宜昌市"
},
{
"code": "420600000000",
"name": "襄阳市"
},
{
"code": "420700000000",
"name": "鄂州市"
},
{
"code": "420800000000",
"name": "荆门市"
},
{
"code": "420900000000",
"name": "孝感市"
},
{
"code": "421000000000",
"name": "荆州市"
},
{
"code": "421100000000",
"name": "黄冈市"
},
{
"code": "421200000000",
"name": "咸宁市"
},
{
"code": "421300000000",
"name": "随州市"
},
{
"code": "422800000000",
"name": "恩施土家族苗族自治州"
}
]
},
{
"code": null,
"name": "广西壮族自治区",
"cities": [
{
"code": "450100000000",
"name": "南宁市"
},
{
"code": "450200000000",
"name": "柳州市"
},
{
"code": "450300000000",
"name": "桂林市"
},
{
"code": "450400000000",
"name": "梧州市"
},
{
"code": "450500000000",
"name": "北海市"
},
{
"code": "450600000000",
"name": "防城港市"
},
{
"code": "450700000000",
"name": "钦州市"
},
{
"code": "450800000000",
"name": "贵港市"
},
{
"code": "450900000000",
"name": "玉林市"
},
{
"code": "451000000000",
"name": "百色市"
},
{
"code": "451100000000",
"name": "贺州市"
},
{
"code": "451200000000",
"name": "河池市"
},
{
"code": "451300000000",
"name": "来宾市"
},
{
"code": "451400000000",
"name": "崇左市"
}
]
},
{
"code": null,
"name": "陕西省",
"cities": [
{
"code": "610100000000",
"name": "西安市"
},
{
"code": "610200000000",
"name": "铜川市"
},
{
"code": "610300000000",
"name": "宝鸡市"
},
{
"code": "610400000000",
"name": "咸阳市"
},
{
"code": "610500000000",
"name": "渭南市"
},
{
"code": "610600000000",
"name": "延安市"
},
{
"code": "610700000000",
"name": "汉中市"
},
{
"code": "610800000000",
"name": "榆林市"
},
{
"code": "610900000000",
"name": "安康市"
},
{
"code": "611000000000",
"name": "商洛市"
}
]
},
{
"code": null,
"name": "吉林省",
"cities": [
{
"code": "220100000000",
"name": "长春市"
},
{
"code": "220200000000",
"name": "吉林市"
},
{
"code": "220300000000",
"name": "四平市"
},
{
"code": "220400000000",
"name": "辽源市"
},
{
"code": "220500000000",
"name": "通化市"
},
{
"code": "220600000000",
"name": "白山市"
},
{
"code": "220700000000",
"name": "松原市"
},
{
"code": "220800000000",
"name": "白城市"
},
{
"code": "222400000000",
"name": "延边朝鲜族自治州"
}
]
},
{
"code": null,
"name": "宁夏回族自治区",
"cities": [
{
"code": "640100000000",
"name": "银川市"
},
{
"code": "640200000000",
"name": "石嘴山市"
},
{
"code": "640300000000",
"name": "吴忠市"
},
{
"code": "640400000000",
"name": "固原市"
},
{
"code": "640500000000",
"name": "中卫市"
}
]
},
{
"code": null,
"name": "广东省",
"cities": [
{
"code": "440100000000",
"name": "广州市"
},
{
"code": "440200000000",
"name": "韶关市"
},
{
"code": "440300000000",
"name": "深圳市"
},
{
"code": "440400000000",
"name": "珠海市"
},
{
"code": "440500000000",
"name": "汕头市"
},
{
"code": "440600000000",
"name": "佛山市"
},
{
"code": "440700000000",
"name": "江门市"
},
{
"code": "440800000000",
"name": "湛江市"
},
{
"code": "440900000000",
"name": "茂名市"
},
{
"code": "441200000000",
"name": "肇庆市"
},
{
"code": "441300000000",
"name": "惠州市"
},
{
"code": "441400000000",
"name": "梅州市"
},
{
"code": "441500000000",
"name": "汕尾市"
},
{
"code": "441600000000",
"name": "河源市"
},
{
"code": "441700000000",
"name": "阳江市"
},
{
"code": "441800000000",
"name": "清远市"
},
{
"code": "441900000000",
"name": "东莞市"
},
{
"code": "442000000000",
"name": "中山市"
},
{
"code": "445100000000",
"name": "潮州市"
},
{
"code": "445200000000",
"name": "揭阳市"
},
{
"code": "445300000000",
"name": "云浮市"
}
]
},
{
"code": null,
"name": "河北省",
"cities": [
{
"code": "130100000000",
"name": "石家庄市"
},
{
"code": "130200000000",
"name": "唐山市"
},
{
"code": "130300000000",
"name": "秦皇岛市"
},
{
"code": "130400000000",
"name": "邯郸市"
},
{
"code": "130500000000",
"name": "邢台市"
},
{
"code": "130600000000",
"name": "保定市"
},
{
"code": "130700000000",
"name": "张家口市"
},
{
"code": "130800000000",
"name": "承德市"
},
{
"code": "130900000000",
"name": "沧州市"
},
{
"code": "131000000000",
"name": "廊坊市"
},
{
"code": "131100000000",
"name": "衡水市"
}
]
},
{
"code": null,
"name": "河南省",
"cities": [
{
"code": "410100000000",
"name": "郑州市"
},
{
"code": "410200000000",
"name": "开封市"
},
{
"code": "410300000000",
"name": "洛阳市"
},
{
"code": "410400000000",
"name": "平顶山市"
},
{
"code": "410500000000",
"name": "安阳市"
},
{
"code": "410600000000",
"name": "鹤壁市"
},
{
"code": "410700000000",
"name": "新乡市"
},
{
"code": "410800000000",
"name": "焦作市"
},
{
"code": "410900000000",
"name": "濮阳市"
},
{
"code": "411000000000",
"name": "许昌市"
},
{
"code": "411100000000",
"name": "漯河市"
},
{
"code": "411200000000",
"name": "三门峡市"
},
{
"code": "411300000000",
"name": "南阳市"
},
{
"code": "411400000000",
"name": "商丘市"
},
{
"code": "411500000000",
"name": "信阳市"
},
{
"code": "411600000000",
"name": "周口市"
},
{
"code": "411700000000",
"name": "驻马店市"
}
]
},
{
"code": null,
"name": "浙江省",
"cities": [
{
"code": "330100000000",
"name": "杭州市"
},
{
"code": "330200000000",
"name": "宁波市"
},
{
"code": "330300000000",
"name": "温州市"
},
{
"code": "330400000000",
"name": "嘉兴市"
},
{
"code": "330500000000",
"name": "湖州市"
},
{
"code": "330600000000",
"name": "绍兴市"
},
{
"code": "330700000000",
"name": "金华市"
},
{
"code": "330800000000",
"name": "衢州市"
},
{
"code": "330900000000",
"name": "舟山市"
},
{
"code": "331000000000",
"name": "台州市"
},
{
"code": "331100000000",
"name": "丽水市"
}
]
},
{
"code": null,
"name": "贵州省",
"cities": [
{
"code": "520100000000",
"name": "贵阳市"
},
{
"code": "520200000000",
"name": "六盘水市"
},
{
"code": "520300000000",
"name": "遵义市"
},
{
"code": "520400000000",
"name": "安顺市"
},
{
"code": "520500000000",
"name": "毕节市"
},
{
"code": "520600000000",
"name": "铜仁市"
},
{
"code": "522300000000",
"name": "黔西南布依族苗族自治州"
},
{
"code": "522600000000",
"name": "黔东南苗族侗族自治州"
},
{
"code": "522700000000",
"name": "黔南布依族苗族自治州"
}
]
},
{
"code": null,
"name": "江西省",
"cities": [
{
"code": "360100000000",
"name": "南昌市"
},
{
"code": "360200000000",
"name": "景德镇市"
},
{
"code": "360300000000",
"name": "萍乡市"
},
{
"code": "360400000000",
"name": "九江市"
},
{
"code": "360500000000",
"name": "新余市"
},
{
"code": "360600000000",
"name": "鹰潭市"
},
{
"code": "360700000000",
"name": "赣州市"
},
{
"code": "360800000000",
"name": "吉安市"
},
{
"code": "360900000000",
"name": "宜春市"
},
{
"code": "361000000000",
"name": "抚州市"
},
{
"code": "361100000000",
"name": "上饶市"
}
]
},
{
"code": null,
"name": "青海省",
"cities": [
{
"code": "630100000000",
"name": "西宁市"
},
{
"code": "630200000000",
"name": "海东市"
},
{
"code": "632200000000",
"name": "海北藏族自治州"
},
{
"code": "632300000000",
"name": "黄南藏族自治州"
},
{
"code": "632500000000",
"name": "海南藏族自治州"
},
{
"code": "632600000000",
"name": "果洛藏族自治州"
},
{
"code": "632700000000",
"name": "玉树藏族自治州"
},
{
"code": "632800000000",
"name": "海西蒙古族藏族自治州"
}
]
},
{
"code": null,
"name": "福建省",
"cities": [
{
"code": "350100000000",
"name": "福州市"
},
{
"code": "350200000000",
"name": "厦门市"
},
{
"code": "350300000000",
"name": "莆田市"
},
{
"code": "350400000000",
"name": "三明市"
},
{
"code": "350500000000",
"name": "泉州市"
},
{
"code": "350600000000",
"name": "漳州市"
},
{
"code": "350700000000",
"name": "南平市"
},
{
"code": "350800000000",
"name": "龙岩市"
},
{
"code": "350900000000",
"name": "宁德市"
}
]
},
{
"code": null,
"name": "江苏省",
"cities": [
{
"code": "320100000000",
"name": "南京市"
},
{
"code": "320200000000",
"name": "无锡市"
},
{
"code": "320300000000",
"name": "徐州市"
},
{
"code": "320400000000",
"name": "常州市"
},
{
"code": "320500000000",
"name": "苏州市"
},
{
"code": "320600000000",
"name": "南通市"
},
{
"code": "320700000000",
"name": "连云港市"
},
{
"code": "320800000000",
"name": "淮安市"
},
{
"code": "320900000000",
"name": "盐城市"
},
{
"code": "321000000000",
"name": "扬州市"
},
{
"code": "321100000000",
"name": "镇江市"
},
{
"code": "321200000000",
"name": "泰州市"
},
{
"code": "321300000000",
"name": "宿迁市"
}
]
},
{
"code": null,
"name": "辽宁省",
"cities": [
{
"code": "210100000000",
"name": "沈阳市"
},
{
"code": "210200000000",
"name": "大连市"
},
{
"code": "210300000000",
"name": "鞍山市"
},
{
"code": "210400000000",
"name": "抚顺市"
},
{
"code": "210500000000",
"name": "本溪市"
},
{
"code": "210600000000",
"name": "丹东市"
},
{
"code": "210700000000",
"name": "锦州市"
},
{
"code": "210800000000",
"name": "营口市"
},
{
"code": "210900000000",
"name": "阜新市"
},
{
"code": "211000000000",
"name": "辽阳市"
},
{
"code": "211100000000",
"name": "盘锦市"
},
{
"code": "211200000000",
"name": "铁岭市"
},
{
"code": "211300000000",
"name": "朝阳市"
},
{
"code": "211400000000",
"name": "葫芦岛市"
}
]
},
{
"code": null,
"name": "海南省",
"cities": [
{
"code": "460100000000",
"name": "海口市"
},
{
"code": "460200000000",
"name": "三亚市"
},
{
"code": "460300000000",
"name": "三沙市"
},
{
"code": "460400000000",
"name": "儋州市"
}
]
},
{
"code": null,
"name": "四川省",
"cities": [
{
"code": "510100000000",
"name": "成都市"
},
{
"code": "510300000000",
"name": "自贡市"
},
{
"code": "510400000000",
"name": "攀枝花市"
},
{
"code": "510500000000",
"name": "泸州市"
},
{
"code": "510600000000",
"name": "德阳市"
},
{
"code": "510700000000",
"name": "绵阳市"
},
{
"code": "510800000000",
"name": "广元市"
},
{
"code": "510900000000",
"name": "遂宁市"
},
{
"code": "511000000000",
"name": "内江市"
},
{
"code": "511100000000",
"name": "乐山市"
},
{
"code": "511300000000",
"name": "南充市"
},
{
"code": "511400000000",
"name": "眉山市"
},
{
"code": "511500000000",
"name": "宜宾市"
},
{
"code": "511600000000",
"name": "广安市"
},
{
"code": "511700000000",
"name": "达州市"
},
{
"code": "511800000000",
"name": "雅安市"
},
{
"code": "511900000000",
"name": "巴中市"
},
{
"code": "512000000000",
"name": "资阳市"
},
{
"code": "513200000000",
"name": "阿坝藏族羌族自治州"
},
{
"code": "513300000000",
"name": "甘孜藏族自治州"
},
{
"code": "513400000000",
"name": "凉山彝族自治州"
}
]
},
{
"code": null,
"name": "湖南省",
"cities": [
{
"code": "430100000000",
"name": "长沙市"
},
{
"code": "430200000000",
"name": "株洲市"
},
{
"code": "430300000000",
"name": "湘潭市"
},
{
"code": "430400000000",
"name": "衡阳市"
},
{
"code": "430500000000",
"name": "邵阳市"
},
{
"code": "430600000000",
"name": "岳阳市"
},
{
"code": "430700000000",
"name": "常德市"
},
{
"code": "430800000000",
"name": "张家界市"
},
{
"code": "430900000000",
"name": "益阳市"
},
{
"code": "431000000000",
"name": "郴州市"
},
{
"code": "431100000000",
"name": "永州市"
},
{
"code": "431200000000",
"name": "怀化市"
},
{
"code": "431300000000",
"name": "娄底市"
},
{
"code": "433100000000",
"name": "湘西土家族苗族自治州"
}
]
}
]
\ No newline at end of file
import { Icon, Menu, Dropdown } from 'ant-design-vue'
import './index.less'
import i18nMixin from '@example/store/i18n-mixin'
const locales = ['zh-CN', 'zh-TW', 'en-US', 'pt-BR']
const languageLabels = {
'zh-CN': '简体中文',
'zh-TW': '繁体中文',
'en-US': 'English',
'pt-BR': 'Português'
}
// eslint-disable-next-line
const languageIcons = {
'zh-CN': '🇨🇳',
'zh-TW': '🇭🇰',
'en-US': '🇺🇸',
'pt-BR': '🇧🇷'
}
const SelectLang = {
name: 'SelectLang',
mixins: [i18nMixin],
render () {
const changeLang = ({ key }) => {
this.setLang(key)
}
const langMenu = (
<Menu class={'menu'} selectedKeys={[this.currentLang]} onClick={changeLang}>
{locales.map(locale => (
<Menu.Item key={locale}>
<span role="img" aria-label={languageLabels[locale]}>
{languageIcons[locale]}
</span>{' '}
{languageLabels[locale]}
</Menu.Item>
))}
</Menu>
)
return (
<Dropdown overlay={langMenu} placement="bottomRight">
<span class={'drop-down'}>
<Icon type="global" title={this.$t('navBar.lang')} />
</span>
</Dropdown>
)
}
}
export default SelectLang
@import "~ant-design-vue/es/style/themes/default";
@header-menu-prefix-cls: ~'@{ant-prefix}-pro-header-menu';
.drop-down {
line-height: @layout-header-height;
vertical-align: top;
cursor: pointer;
> i {
font-size: 16px !important;
transform: none !important;
svg {
position: relative;
top: -1px;
}
}
.anticon {
margin-right: 8px;
}
.ant-dropdown-menu-item {
min-width: 160px;
}
}
// eslint-disable-next-line
import BasicLayout from '../layouts/BasicLayout'
const asyncRouterMap = [
{
path: '/',
name: 'index',
component: BasicLayout,
meta: { title: 'menu.home' },
redirect: '/dashboard/analysis',
children: [
{
path: '/dashboard',
name: 'dashboard',
meta: { keepAlive: true, title: 'menu.dashboard.default', icon: 'dashboard' },
redirect: '/dashboard/analysis',
component: { render: (h) => h('router-view') },
children: [
{
path: '/dashboard/analysis',
name: 'analysis',
meta: {
keepAlive: true,
title: 'menu.dashboard.analysis'
},
component: () => import(/* webpackChunkName: "about" */ '../views/BlockPage')
},
{
path: '/dashboard/workplace/:id?',
name: 'workplace',
meta: {
global: true,
keepAlive: true,
title: 'menu.dashboard.workplace'
},
component: () => import(/* webpackChunkName: "about" */ '../views/TestPage')
}
]
},
{
path: '/page2',
name: 'page2',
meta: {
keepAlive: true,
title: 'menu.nav2',
icon: 'video-camera'
},
component: () => import(/* webpackChunkName: "about" */ '../views/TestPage2')
}
]
}
]
export { asyncRouterMap }
export {
default as SettingOutline
} from '@ant-design/icons/lib/outline/SettingOutline'
export {
default as GithubOutline
} from '@ant-design/icons/lib/outline/GithubOutline'
export {
default as CopyrightOutline
} from '@ant-design/icons/lib/outline/CopyrightOutline'
/* MultiTab begin */
export {
default as CloseOutline
} from '@ant-design/icons/lib/outline/CloseOutline'
export {
default as ReloadOutline
} from '@ant-design/icons/lib/outline/ReloadOutline'
export {
default as DownOutline
} from '@ant-design/icons/lib/outline/DownOutline'
export {
default as AlignLeftOutline
} from '@ant-design/icons/lib/outline/AlignLeftOutline'
/* MultiTab end */
/* Layout begin */
export {
default as LeftOutline
} from '@ant-design/icons/lib/outline/LeftOutline'
export {
default as RightOutline
} from '@ant-design/icons/lib/outline/RightOutline'
export {
default as MenuFoldOutline
} from '@ant-design/icons/lib/outline/MenuFoldOutline'
export {
default as MenuUnfoldOutline
} from '@ant-design/icons/lib/outline/MenuUnfoldOutline'
export {
default as DashboardOutline
} from '@ant-design/icons/lib/outline/DashboardOutline'
export {
default as VideoCameraOutline
} from '@ant-design/icons/lib/outline/VideoCameraOutline'
export {
default as LoadingOutline
} from '@ant-design/icons/lib/outline/LoadingOutline'
export {
default as GlobalOutline
} from '@ant-design/icons/lib/outline/GlobalOutline'
/* Layout end */
/* eslint-disable */
/**
* 该文件是为了按需加载,剔除掉了一些不需要的框架组件。
* 减少了编译支持库包大小
*
* 当需要更多组件依赖时,在该文件加入即可
*
* 注意:需要同时修改 babel.config.js 文件下的配置项
*/
import Vue from 'vue'
import {
LocaleProvider,
Layout,
Input,
InputNumber,
Button,
Switch,
Radio,
Checkbox,
Select,
Card,
Form,
Row,
Col,
Modal,
Table,
Tabs,
Icon,
Badge,
Popover,
Dropdown,
List,
Avatar,
Breadcrumb,
Steps,
Spin,
Menu,
Drawer,
Tooltip,
Alert,
Tag,
Divider,
DatePicker,
TimePicker,
Upload,
Progress,
Skeleton,
Popconfirm,
message,
notification
} from 'ant-design-vue'
// import VueCropper from 'vue-cropper'
Vue.use(LocaleProvider)
Vue.use(Layout)
Vue.use(Input)
Vue.use(InputNumber)
Vue.use(Button)
Vue.use(Switch)
Vue.use(Radio)
Vue.use(Checkbox)
Vue.use(Select)
Vue.use(Card)
Vue.use(Form)
Vue.use(Row)
Vue.use(Col)
Vue.use(Modal)
Vue.use(Table)
Vue.use(Tabs)
Vue.use(Icon)
Vue.use(Badge)
Vue.use(Popover)
Vue.use(Dropdown)
Vue.use(List)
Vue.use(Avatar)
Vue.use(Breadcrumb)
Vue.use(Steps)
Vue.use(Spin)
Vue.use(Menu)
Vue.use(Drawer)
Vue.use(Tooltip)
Vue.use(Alert)
Vue.use(Tag)
Vue.use(Divider)
Vue.use(DatePicker)
Vue.use(TimePicker)
Vue.use(Upload)
Vue.use(Progress)
Vue.use(Skeleton)
Vue.use(Popconfirm)
// Add to this.$xxx
Vue.prototype.$confirm = Modal.confirm
Vue.prototype.$message = message
Vue.prototype.$notification = notification
Vue.prototype.$info = Modal.info
Vue.prototype.$success = Modal.success
Vue.prototype.$error = Modal.error
Vue.prototype.$warning = Modal.warning
/* eslint-disable */
/**
* 该文件用于直接全量引入 antd
* 如果想减少依赖体积,可以使用同目录下的 lazyload.js 按需加载
* 注意:需要同时修改 babel.config.js 文件下的配置项
*/
import Vue from 'vue'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.less'
Vue.use(Antd)
import Vue from 'vue'
import store from '../store'
import defaultSettings from '@config/defaultSettings'
import {
TOGGLE_CONTENT_WIDTH,
TOGGLE_FIXED_HEADER,
TOGGLE_FIXED_SIDEBAR, TOGGLE_HIDE_HEADER,
TOGGLE_LAYOUT, TOGGLE_NAV_THEME, TOGGLE_WEAK
} from '../store/mutation-types'
export default function initializer () {
console.log('API_URL:', process.env.BASE_URL)
// store.commit(ACCESS_TOKEN, Vue.ls.get(ACCESS_TOKEN))
store.commit(TOGGLE_LAYOUT, Vue.ls.get(TOGGLE_LAYOUT, defaultSettings.layout))
store.commit(TOGGLE_FIXED_HEADER, Vue.ls.get(TOGGLE_FIXED_HEADER, defaultSettings.fixedHeader))
store.commit(TOGGLE_FIXED_SIDEBAR, Vue.ls.get(TOGGLE_FIXED_SIDEBAR, defaultSettings.fixSiderbar))
store.commit(TOGGLE_CONTENT_WIDTH, Vue.ls.get(TOGGLE_CONTENT_WIDTH, defaultSettings.contentWidth))
store.commit(TOGGLE_HIDE_HEADER, Vue.ls.get(TOGGLE_HIDE_HEADER, defaultSettings.autoHideHeader))
store.commit(TOGGLE_NAV_THEME, Vue.ls.get(TOGGLE_NAV_THEME, defaultSettings.navTheme))
store.commit(TOGGLE_WEAK, Vue.ls.get(TOGGLE_WEAK, defaultSettings.colorWeak))
}
import Vue from 'vue'
import Storage from 'vue-ls'
// use antd
// import './antd/normal'
// lazyload antd
import './antd/lazyload'
import { storageOptions } from '@config/defaultSettings'
import { DocumentTitle } from '@/'
Vue.use(DocumentTitle)
Vue.use(Storage, storageOptions)
import 'core-js/features/array/from' // <- at the top of your entry point
import 'core-js/features/array/flat' // <- at the top of your entry point
import 'core-js/features/set' // <- at the top of your entry point
import 'core-js/features/promise' // <- at the top of your entry point
import 'core-js/features/object/entries'
import 'regenerator-runtime/runtime'
@import '~ant-design-vue/es/style/themes/default.less';
html,
body,
#app, #root {
height: 100%;
}
.colorWeak {
filter: invert(80%);
}
.ant-layout.layout-basic {
height: 100vh;
min-height: 100vh;
}
canvas {
display: block;
}
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
ul,
ol {
list-style: none;
}
@media (max-width: @screen-xs) {
.ant-table {
width: 100%;
overflow-x: auto;
&-thead > tr,
&-tbody > tr {
> th,
> td {
white-space: pre;
> span {
display: block;
}
}
}
}
}
import './BasicLayout.less'
import { Avatar, Dropdown, Menu } from 'ant-design-vue'
import { asyncRouterMap } from '../config/router.config.js'
import { i18nRender } from '@example/locales'
import ProLayout from '@/'
import SelectLang from '@example/components/SelectLang'
import LogoSvg from '../assets/logo.svg?inline'
import defaultSettings from '@config/defaultSettings'
const Account = {
name: 'Account',
render () {
const accountMenu = (
<Menu class="menu">
<Menu.Item key="info">
个人信息
</Menu.Item>
<Menu.Item key="settings">
个人设置
</Menu.Item>
<Menu.Item key="logout">
退出登录
</Menu.Item>
</Menu>
)
return (
<Dropdown overlay={accountMenu} placement="bottomRight">
<span class={'account'}>
<Avatar size="small" src="https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png" class="antd-pro-global-header-index-avatar" />
<span>Serati Ma</span>
</span>
</Dropdown>
)
}
}
const rightContentRender = (h, props) => {
return (
<div class="ant-pro-global-header-index-right">
<Account class={'ant-pro-global-header-index-action'} />
<SelectLang class={'ant-pro-global-header-index-action'} />
</div>
)
}
export default {
name: 'BasicLayout',
data () {
return {
// 侧栏收起状态
collapsed: false,
// 自动隐藏头部栏
autoHideHeader: false,
// 媒体查询
query: {},
// 布局类型
layout: 'sidemenu', // 'sidemenu', 'topmenu'
// 定宽: true / 流式: false
contentWidth: true
}
},
render (h) {
const {
collapsed,
contentWidth,
autoHideHeader,
layout
} = this
const handleMediaQuery = (val) => {
this.query = val
}
const handleCollapse = (val) => {
this.collapsed = val
}
const menus = asyncRouterMap.find(item => item.path === '/').children
const cdProps = {
props: {
menus,
collapsed,
autoHideHeader,
mediaQuery: this.query,
handleMediaQuery,
handleCollapse,
layout,
contentWidth,
rightContentRender,
i18nRender,
logo: LogoSvg,
title: defaultSettings.title
}
}
return (
<ProLayout {...cdProps}>
<router-view />
</ProLayout>
)
}
}
@import "~ant-design-vue/es/style/themes/default";
.ant-pro-global-header-index-right {
.account {
.antd-pro-global-header-index-avatar {
margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
margin-right: 8px;
color: @primary-color;
vertical-align: top;
background: rgba(255, 255, 255, 0.85);
}
}
.menu {
.anticon {
margin-right: 8px;
}
.ant-dropdown-menu-item {
min-width: 160px;
}
}
}
import Vue from 'vue'
import VueI18n from 'vue-i18n'
// default lang
import enUS from './lang/en-US'
// eslint-disable-next-line
import { request } from '@/utils/request'
Vue.use(VueI18n)
export const defaultLang = 'en-US'
const messages = {
'en-US': {
...enUS
}
}
const i18n = new VueI18n({
locale: defaultLang,
fallbackLocale: defaultLang,
messages
})
const loadedLanguages = [defaultLang]
// eslint-disable-next-line
function setI18nLanguage (lang) {
i18n.locale = lang
// request.headers['Accept-Language'] = lang
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguageAsync (lang = defaultLang) {
return new Promise(resolve => {
// 缓存语言设置
Vue.ls.set('lang', lang)
if (i18n.locale !== lang) {
if (!loadedLanguages.includes(lang)) {
return import(/* webpackChunkName: "lang-[request]" */ `./lang/${lang}`).then(msg => {
i18n.setLocaleMessage(lang, msg.default)
loadedLanguages.push(lang)
return setI18nLanguage(lang)
})
}
return resolve(setI18nLanguage(lang))
}
return resolve(lang)
})
}
export function i18nRender (key) {
return i18n.t(`${key}`)
}
export default i18n
export default {
navBar: {
lang: 'Language'
},
layouts: {
},
menu: {
home: 'Home',
dashboard: {
default: 'Dashboard',
analysis: 'Analysis',
workplace: 'Workplace'
},
nav2: 'Nav 2*'
}
}
export default {
navBar: {
lang: '语言'
},
layouts: {
},
menu: {
home: '首页',
dashboard: {
default: '仪表盘',
analysis: '分析页',
workplace: '工作台'
},
nav2: '导航2*'
}
}
import './core/polyfills' // with polyfills
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import i18n from './locales'
import './router/router-guards'
import './core/library'
import initializer from './core/bootstrap'
import './global.less'
Vue.config.productionTip = false
window._vue = new Vue({
router,
store,
i18n,
beforeCreate: initializer,
render: h => h(App)
}).$mount('#app')
import Vue from 'vue'
import VueRouter from 'vue-router'
import { asyncRouterMap } from '../config/router.config'
// hack router push/replace callback
['push', 'replace'].map(key => {
return {
k: key,
prop: VueRouter.prototype[key]
}
}).forEach(item => {
VueRouter.prototype[item.k] = function newCall (location, onResolve, onReject) {
if (onResolve || onReject) return item.prop.call(this, location, onResolve, onReject)
return item.prop.call(this, location).catch(err => err)
}
})
Vue.use(VueRouter)
const routes = asyncRouterMap
const router = new VueRouter({
routes
})
export default router
import router from './index'
router.beforeEach((to, from, next) => {
next()
})
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable no-restricted-globals */
/* eslint-disable no-underscore-dangle */
/* globals workbox */
workbox.core.setCacheNameDetails({
prefix: 'antd-pro',
suffix: 'v1'
})
// Control all opened tabs ASAP
workbox.clientsClaim()
/**
* Use precaching list generated by workbox in build process.
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching
*/
workbox.precaching.precacheAndRoute(self.__precacheManifest || [])
/**
* Register a navigation route.
* https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route
*/
workbox.routing.registerNavigationRoute('/index.html')
/**
* Use runtime cache:
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing#.registerRoute
*
* Workbox provides all common caching strategies including CacheFirst, NetworkFirst etc.
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies
*/
/**
* Handle API requests
*/
workbox.routing.registerRoute(/\/api\//, workbox.strategies.networkFirst())
/**
* Handle third party requests
*/
workbox.routing.registerRoute(
/^https:\/\/gw.alipayobjects.com\//,
workbox.strategies.networkFirst()
)
workbox.routing.registerRoute(
/^https:\/\/cdnjs.cloudflare.com\//,
workbox.strategies.networkFirst()
)
workbox.routing.registerRoute(/\/color.less/, workbox.strategies.networkFirst())
/**
* Response to client after skipping waiting with MessageChannel
*/
addEventListener('message', event => {
const replyPort = event.ports[0]
const message = event.data
if (replyPort && message && message.type === 'skip-waiting') {
event.waitUntil(
self
.skipWaiting()
.then(
() => replyPort.postMessage({ error: null }),
error => replyPort.postMessage({ error })
)
)
}
})
const getters = {
device: state => state.app.device,
lang: state => state.app.lang
}
export default getters
import { mapState } from 'vuex'
const i18nMixin = {
computed: {
...mapState({
currentLang: state => state.app.lang
})
},
methods: {
setLang (lang) {
this.$store.dispatch('setLang', lang)
}
}
}
export default i18nMixin
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import getters from './getters'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
app
},
state: {
},
mutations: {
},
actions: {
},
getters
})
import Vue from 'vue'
import {
SIDEBAR_TYPE,
TOGGLE_DEVICE,
TOGGLE_NAV_THEME,
TOGGLE_LAYOUT,
TOGGLE_FIXED_HEADER,
TOGGLE_FIXED_SIDEBAR,
TOGGLE_CONTENT_WIDTH,
TOGGLE_HIDE_HEADER,
TOGGLE_COLOR,
TOGGLE_WEAK,
// i18n
APP_LANGUAGE
} from '../mutation-types'
import { loadLanguageAsync } from '../../locales'
const App = {
state: {
sidebar: true,
device: 'desktop',
theme: 'dark',
layout: '',
contentWidth: '',
fixedHeader: false,
fixedSidebar: false,
autoHideHeader: false,
color: '',
weak: false,
lang: 'en-US'
},
mutations: {
[SIDEBAR_TYPE]: (state, type) => {
state.sidebar = type
Vue.ls.set(SIDEBAR_TYPE, type)
},
[TOGGLE_DEVICE]: (state, device) => {
state.device = device
},
[TOGGLE_NAV_THEME]: (state, theme) => {
state.theme = theme
Vue.ls.set(TOGGLE_NAV_THEME, theme)
},
[TOGGLE_LAYOUT]: (state, mode) => {
state.layout = mode
Vue.ls.set(TOGGLE_LAYOUT, mode)
},
[TOGGLE_FIXED_HEADER]: (state, mode) => {
state.fixedHeader = mode
Vue.ls.set(TOGGLE_FIXED_HEADER, mode)
},
[TOGGLE_FIXED_SIDEBAR]: (state, mode) => {
state.fixedSidebar = mode
Vue.ls.set(TOGGLE_FIXED_SIDEBAR, mode)
},
[TOGGLE_CONTENT_WIDTH]: (state, type) => {
state.contentWidth = type
Vue.ls.set(TOGGLE_CONTENT_WIDTH, type)
},
[TOGGLE_HIDE_HEADER]: (state, type) => {
state.autoHideHeader = type
Vue.ls.set(TOGGLE_HIDE_HEADER, type)
},
[TOGGLE_COLOR]: (state, color) => {
state.color = color
Vue.ls.set(TOGGLE_COLOR, color)
},
[TOGGLE_WEAK]: (state, mode) => {
state.weak = mode
Vue.ls.set(TOGGLE_WEAK, mode)
},
[APP_LANGUAGE]: (state, lang) => {
console.log('lang', lang)
state.lang = lang
Vue.ls.set(APP_LANGUAGE, lang)
}
},
actions: {
setLang ({ commit }, lang) {
return new Promise((resolve, reject) => {
commit(APP_LANGUAGE, lang)
loadLanguageAsync(lang).then(() => {
resolve()
}).catch((e) => {
reject(e)
})
})
}
}
}
export default App
export const ACCESS_TOKEN = 'Access-Token'
export const SIDEBAR_TYPE = 'sidebar_type'
export const TOGGLE_DEVICE = 'device'
export const TOGGLE_NAV_THEME = 'nav_theme'
export const TOGGLE_LAYOUT = 'layout'
export const TOGGLE_FIXED_HEADER = 'fixed_header'
export const TOGGLE_FIXED_SIDEBAR = 'fixed_sidebar'
export const TOGGLE_CONTENT_WIDTH = 'content_width'
export const TOGGLE_HIDE_HEADER = 'auto_hide_header'
export const TOGGLE_COLOR = 'color'
export const TOGGLE_WEAK = 'weak'
export const APP_LANGUAGE = 'app_language'
export const CONTENT_WIDTH_TYPE = {
Fluid: 'Fluid',
Fixed: 'Fixed'
}
<template>
<document-title title="BlockPage">
<div>
<strong>Block Page</strong>
</div>
</document-title>
</template>
<script>
export default {
name: 'BlockPage'
}
</script>
<style scoped>
</style>
<template>
<document-title title="我是PAGE1">
<div class="page-test-wrapper">
<h1>Test Page keepAlive: {{ $route.meta.keepAlive }}</h1>
<div class="box">
<div style="margin: 1em 0">
<a-input placeholder="home"/>
</div>
<a-button @click="handleClick">Click Me!</a-button><a-divider type="vertical" />
<a-button @click="handleRefresh">Refresh Current</a-button><a-divider type="vertical" />
<a-button @click="handleGetAllCache">Get All Cache</a-button><a-divider type="vertical" />
<a-button @click="handleChangeLang('zh-CN')">Lang zh-CN</a-button><a-divider type="vertical" />
<a-button @click="handleChangeLang('en-US')">Lang en-US</a-button>
<a-divider />
</div>
</div>
</document-title>
</template>
<script>
import { loadLanguageAsync } from '@example/locales'
export default {
methods: {
handleClick (e) {
this.$router.push({ path: '/page2' })
},
handleClearPage1 () {
},
handleGetAllCache () {
this.$tab.caches((data) => {
})
},
handleRefresh () {
// this.$tab.refresh('/page2')
this.$tab.refresh()
},
handleChangeLang (lang) {
loadLanguageAsync(lang)
}
}
}
</script>
<style lang="less" scoped>
.page-test-wrapper {
width: 400px;
margin: 0 auto;
}
</style>
<template>
<document-title :title="$t($route.meta.title)">
<div class="page-test-wrapper">
<h1>Test Page2 keepAlive: {{ $route.meta.keepAlive }}</h1>
<div class="box">
<div style="margin: 1em 0">
<a-input v-model="newVal" placeholder="..."/>
</div>
<a-button @click="handleClick">Click Me!</a-button>
<a-divider />
<p>{{ newVal }}</p>
</div>
</div>
</document-title>
</template>
<script>
export default {
data () {
return {
newVal: ''
}
},
methods: {
handleClick (e) {
this.$router.push({ path: '/dashboard/analysis' })
}
}
}
</script>
<style lang="less" scoped>
.page-test-wrapper {
width: 400px;
margin: 0 auto;
}
</style>
module.exports = {
preset: '@vue/cli-plugin-unit-jest'
}
{
"name": "@vue-component/pro-layout",
"version": "1.0.0",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint",
"analyz": "vue-cli-service build --mode analyz"
},
"dependencies": {
"ant-design-vue": "^1.4.4",
"core-js": "^3.1.2",
"umi-request": "^1.2.8",
"vue": "^2.6.10",
"vue-container-query": "^0.1.0",
"vue-i18n": "^8.15.0",
"vue-ls": "^3.2.1",
"vue-router": "^3.0.6",
"vue-svg-component-runtime": "^1.0.1",
"vuex": "^3.0.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.0.0",
"@vue/cli-plugin-eslint": "^4.0.0",
"@vue/cli-plugin-router": "^4.0.0",
"@vue/cli-plugin-unit-jest": "^4.0.0",
"@vue/cli-plugin-vuex": "^4.0.0",
"@vue/cli-service": "^4.0.0",
"@vue/eslint-config-standard": "^4.0.0",
"@vue/test-utils": "1.0.0-beta.29",
"babel-eslint": "^10.0.1",
"babel-plugin-import": "^1.12.2",
"eslint": "^5.16.0",
"eslint-plugin-html": "^6.0.0",
"eslint-plugin-vue": "^5.0.0",
"less": "^3.0.4",
"less-loader": "^5.0.0",
"vue-svg-icon-loader": "^2.1.1",
"vue-template-compiler": "^2.6.10",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-theme-color-replacer": "^1.3.2"
}
}
module.exports = {
plugins: {
autoprefixer: {}
}
}
<!DOCTYPE html>
<html lang="en">
<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>Ant Design Pro</title>
</head>
<body>
<noscript>
<strong>We're sorry but <b>Ant Design Pro</b> 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>
import { Layout } from 'ant-design-vue'
import { ContainerQuery } from 'vue-container-query'
import GridContent from '@/components/GridContent'
import { SiderMenuWrapper, GlobalFooter } from '@/components'
import './BasicLayout.less'
import { SiderMenuProps } from './components/SiderMenu/SiderMenu'
import HeaderView, { HeaderViewProps } from './Header.jsx'
export const BasicLayoutProps = {
...SiderMenuProps,
...HeaderViewProps,
locale: {
type: String,
default: 'en-US'
},
breadcrumbRender: {
type: Function,
default: () => undefined
},
disableMobile: {
type: Boolean,
default: false
},
mediaQuery: {
type: Object,
default: () => {}
},
handleMediaQuery: {
type: Function,
default: () => undefined
}
}
// eslint-disable-next-line
const MediaQueryEnum = {
'screen-xs': {
maxWidth: 575
},
'screen-sm': {
minWidth: 576,
maxWidth: 767
},
'screen-md': {
minWidth: 768,
maxWidth: 991
},
'screen-lg': {
minWidth: 992,
maxWidth: 1199
},
'screen-xl': {
minWidth: 1200,
maxWidth: 1599
},
'screen-xxl': {
minWidth: 1600
}
}
const headerRender = (h, props) => {
if (props.headerRender === false) {
return null
}
return <HeaderView { ...{ props } } />
}
const BasicLayout = {
name: 'BasicLayout',
functional: true,
props: BasicLayoutProps,
render (h, {
props,
data,
children
}) {
const {
menus,
layout,
logo,
contentWidth,
collapsed,
// eslint-disable-next-line
collapsedButtonRender, autoHideHeader,
mediaQuery,
handleMediaQuery,
handleCollapse
} = props
return (
<div>
<ContainerQuery query={MediaQueryEnum} onChange={handleMediaQuery}>
<Layout class={{ 'basicLayout': true, ...mediaQuery }}>
{layout !== 'topmenu' && (
<SiderMenuWrapper
{ ...{ props: props } }
menus={menus}
theme={'dark'}
mode={'inline'}
logo={logo}
isMobile={false}
collapsed={collapsed}
onCollapse={handleCollapse}
handleCollapse={handleCollapse}
title='Ant Design Pro'
/>
)}
<Layout class={[layout]} style={{ paddingLeft: '0', minHeight: '100vh' }}>
{/* <HeaderView
menus={menus}
contentWidth={contentWidth}
layout={layout}
theme={'dark'}
mode={'horizontal'}
logo={LogoSvg}
collapsed={collapsed}
handleCollapse={handleCollapse}
collapsedButtonRender={collapsedButtonRender}
autoHideHeader={autoHideHeader}
headerRender={headerRender}
title='Ant Design Pro' /> */}
{headerRender(h, {
...props,
theme: 'dark',
mode: 'horizontal',
title: 'Ant Design Pro'
})}
<Layout.Content style={{ margin: '24px 16px', padding: '24px', minHeight: '280px' }}>
<GridContent contentWidth={contentWidth}>
{children}
</GridContent>
</Layout.Content>
<Layout.Footer>
<GlobalFooter>
<template slot="links">
<a href="https://www.github.com/vueComponent/" target="_self">Github</a>
<a href="https://www.github.com/sendya/" target="_self">@Sendya</a>
</template>
<template slot="copyright">
<a href="https://github.com/vueComponent">vueComponent</a>
</template>
</GlobalFooter>
</Layout.Footer>
</Layout>
</Layout>
</ContainerQuery>
</div>
)
}
}
export default BasicLayout
@import "~ant-design-vue/es/style/themes/default";
@sider-menu-prefix-cls: ~'@{ant-prefix}-pro-sider-menu';
@nav-header-height: @layout-header-height;
.basicLayout {
.ant-layout-sider-children {
height: 100%;
}
.trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: #1890ff;
}
}
.color-picker {
margin: 10px 0;
}
}
const BlockLayout = {
name: 'BlockLayout',
functional: true,
render (createElement, content) {
return content.children
}
}
export default BlockLayout
/* eslint-disable */
import './Header.less'
import { Layout } from 'ant-design-vue'
import RouteMenu from './components/RouteMenu/Menu'
import { defaultRenderLogoAntTitle, SiderMenuProps } from './components/SiderMenu/SiderMenu'
import GlobalHeader, { GlobalHeaderProps } from './components/GlobalHeader'
const { Header } = Layout
export const HeaderViewProps = {
...GlobalHeaderProps,
...SiderMenuProps,
isMobile: {
type: Boolean,
default: () => false
},
collapsed: {
type: Boolean,
required: true
},
logo: {
type: null,
default: () => null
},
autoHideHeader: {
type: Boolean,
required: true
},
menuRender: {
type: null
},
headerRender: {
type: null
},
rightContentRender: {
type: null
},
siderWidth: {
type: Number
},
visible: {
type: Boolean,
default: true
}
}
const renderContent = (h, props) => {
const isTop = props.layout === 'topmenu'
const isMobile = false
const maxWidth = 1200 - 280 - 120
const contentWidth = props.contentWidth
const baseCls = 'ant-pro-top-nav-header'
const { logo, title, theme, headerRender } = props
let defaultDom = <GlobalHeader {...{ props: props }} />
if (isTop && !isMobile) {
defaultDom = (
<div class={[baseCls, theme]}>
<div class={[`${baseCls}-main`, contentWidth ? 'wide' : '']}>
<div class={`${baseCls}-left`}>
<div class={`${baseCls}-logo`} key="logo" id="logo">
{defaultRenderLogoAntTitle(h, logo, title, null)}
</div>
</div>
<div class={`${baseCls}-menu`} style={{ maxWidth: `${maxWidth}px`, flex: 1 }}>
<RouteMenu {...{ props: props }} />
</div>
</div>
</div>
)
}
if (headerRender) {
return headerRender(h, props)
}
return defaultDom
}
const HeaderView = {
name: 'HeaderView',
props: HeaderViewProps,
render (h) {
const {
visible,
siderWidth: width,
autoHideHeader,
} = this.$props
const props = this.$props
return (
visible ? (
<Header
style={{ padding: 0, width, zIndex: 2 }}
class={autoHideHeader ? 'ant-pro-fixed-header' : ''}
>
{renderContent(h, props)}
</Header>
) : null
)
}
}
export default HeaderView
@import "~ant-design-vue/es/style/themes/default";
@global-header-prefix-cls: ~'@{ant-prefix}-pro-global-header';
@top-nav-header-prefix-cls: ~'@{ant-prefix}-pro-top-nav-header';
@pro-header-bg: @component-background;
@pro-header-hover-bg: @component-background;
@pro-header-box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.@{top-nav-header-prefix-cls} {
position: relative;
width: 100%;
height: @layout-header-height;
box-shadow: @box-shadow-base;
transition: background 0.3s, width 0.2s;
.ant-menu-submenu.ant-menu-submenu-horizontal {
height: 100%;
line-height: @layout-header-height;
.ant-menu-submenu-title {
height: 100%;
}
}
&.light {
background-color: @component-background;
h1 {
color: #002140;
}
}
&-main {
display: flex;
height: @layout-header-height;
padding-left: 24px;
&.wide {
max-width: 1200px;
margin: auto;
padding-left: 0;
}
.left {
display: flex;
flex: 1;
}
.right {
width: 324px;
}
}
&-logo {
position: relative;
width: 165px;
height: @layout-header-height;
overflow: hidden;
line-height: @layout-header-height;
transition: all 0.3s;
img, svg {
display: inline-block;
height: 32px;
width: 32px;
vertical-align: middle;
}
h1 {
display: inline-block;
margin: 0 0 0 12px;
color: @btn-primary-color;
font-weight: 400;
font-size: 16px;
vertical-align: top;
}
}
&-menu {
.ant-menu.ant-menu-horizontal {
height: @layout-header-height;
line-height: @layout-header-height;
border: none;
}
}
}
.@{global-header-prefix-cls} {
position: relative;
height: @layout-header-height;
padding: 0;
background: @pro-header-bg;
box-shadow: @pro-header-box-shadow;
&-index-right {
float: right;
height: 100%;
margin-left: auto;
overflow: hidden;
.@{global-header-prefix-cls}-index-action {
display: inline-block;
height: 100%;
padding: 0 12px;
cursor: pointer;
transition: all .3s;
&:hover {
background: rgba(0,0,0,.025);
}
}
}
&-logo {
display: inline-block;
height: @layout-header-height;
padding: 0 0 0 24px;
font-size: 20px;
line-height: @layout-header-height;
vertical-align: top;
cursor: pointer;
img {
display: inline-block;
width: 32px;
vertical-align: middle;
}
}
&-menu {
.anticon {
margin-right: 8px;
}
.ant-dropdown-menu-item {
min-width: 160px;
}
}
&-trigger {
height: @layout-header-height;
padding: ~'calc((@{layout-header-height} - 26px) / 2)' 24px;
font-size: 20px;
cursor: pointer;
transition: all 0.3s, padding 0s;
&:hover {
background: @pro-header-hover-bg;
}
}
.dark {
height: @layout-header-height;
.action {
color: rgba(255, 255, 255, 0.85);
> i {
color: rgba(255, 255, 255, 0.85);
}
&:hover,
&.opened {
background: @primary-color;
}
.ant-badge {
color: rgba(255, 255, 255, 0.85);
}
}
}
}
import { PageHeaderWrapper } from '@/components'
const PageView = {
name: 'PageView',
render () {
return (
<PageHeaderWrapper>
<route-view />
</PageHeaderWrapper>
)
}
}
export default PageView
/* eslint-disable */
class SideEffect {
constructor ({ propsToState, handleStateChange }) {
if (typeof propsToState !== 'function') {
throw new Error('Expected propsToState to be a function.')
}
if (typeof handleStateChange !== 'function') {
throw new Error('Expected handleStateChange to be a function.')
}
this.options = {
propsToState,
handleStateChange
}
}
}
export default SideEffect
// import SideEffect from './SideEffect'
import { setDocumentTitle } from '@/utils/util'
// const sideEffect = new SideEffect({
// propsToState (propsList) {
// var innermostProps = propsList[propsList.length - 1]
// if (innermostProps) {
// return innermostProps.title
// }
// },
// handleStateChange (title, prefix) {
// console.log('title', title, prefix)
// const nextTitle = `${(title || '')} - ${prefix}`
// if (nextTitle !== document.title) {
// setDocumentTitle(nextTitle)
// }
// }
// })
const handleStateChange = (title, prefix) => {
const nextTitle = `${(title || '')} - ${prefix}`
if (nextTitle !== document.title) {
setDocumentTitle(nextTitle)
}
}
const DocumentTitle = {
name: 'DocumentTitle',
functional: true,
props: {
prefix: {
type: String,
required: false,
default: 'Ant Design Pro'
},
title: {
type: String,
required: true
}
},
// eslint-disable-next-line // { props, data, children }
render (createElement, { props, data, children }) {
handleStateChange(props.title, props.prefix)
return children
}
}
DocumentTitle.install = function (Vue) {
// const mountedInstances = []
// let state
// function __emit (sideEffect) {
// const options = sideEffect.options
// state = options.propsToState(mountedInstances.map(function (instance) {
// return instance
// }))
// options.handleStateChange(state)
// }
// Vue.mixin({
// beforeMount () {
// const sideEffect = this.$options.sideEffect
// if (sideEffect) {
// mountedInstances.push(this)
// __emit(sideEffect)
// }
// },
// updated () {
// const sideEffect = this.$options.sideEffect
// if (sideEffect) {
// __emit(sideEffect)
// }
// },
// beforeDestroy () {
// const sideEffect = this.$options.sideEffect
// if (sideEffect) {
// const index = mountedInstances.indexOf(this)
// mountedInstances.splice(index, 1)
// __emit(sideEffect)
// }
// }
// })
Vue.component(DocumentTitle.name, DocumentTitle)
}
export default DocumentTitle
import { getComponentFromProp, hasProp } from 'ant-design-vue/lib/_util/props-util'
import './index.less'
const GlobalFooterProps = {
links: {
type: Array,
required: false
},
copyright: {
type: null,
required: false
}
}
const GlobalFooter = {
name: 'GlobalFooter',
props: GlobalFooterProps,
render () {
const copyright = getComponentFromProp(this, 'copyright')
const links = getComponentFromProp(this, 'links')
const linksType = hasProp(links)
return (
<footer class="ant-pro-global-footer">
<div class="ant-pro-global-footer-links">
{linksType && (
links.map(link => (
<a
key={link.key}
title={link.key}
target={link.blankTarget ? '_blank' : '_self'}
href={link.href}
>
{link.title}
</a>
))
) || links}
</div>
{copyright && (
<div class="ant-pro-global-footer-copyright">{copyright}</div>
)}
</footer>
)
}
}
export default GlobalFooter
@import "~ant-design-vue/es/style/themes/default";
@global-footer-prefix-cls: ~'@{ant-prefix}-pro-global-footer';
.@{global-footer-prefix-cls} {
margin: 48px 0 24px 0;
padding: 0 16px;
text-align: center;
&-links {
margin-bottom: 8px;
a {
color: @text-color-secondary;
transition: all 0.3s;
&:not(:last-child) {
margin-right: 40px;
}
&:hover {
color: @text-color;
}
}
}
&-copyright {
color: @text-color-secondary;
font-size: @font-size-base;
}
}
/* eslint-disable */
import './index.less'
import debounce from 'lodash/debounce'
import { triggerEvent, inBrowser } from '../../utils/util'
import { Icon } from 'ant-design-vue'
import { defaultRenderLogo } from '../SiderMenu/SiderMenu'
export const GlobalHeaderProps = {
collapsed: {
type: Boolean,
required: true
},
handleCollapse: {
type: Function,
default: () => undefined
},
isMobile: {
type: Boolean,
default: () => false
},
logo: {
type: null,
default: () => null
},
menuRender: {
type: Function,
required: false
},
collapsedButtonRender: {
type: Function,
defualt: null,
required: false
},
rightContentRender: {
type: Function,
required: false
}
}
const defaultRenderCollapsedButton = (h, collapsed) => (
<Icon type={collapsed ? 'menu-unfold' : 'menu-fold'}/>
)
const GlobalHeader = {
name: 'GlobalHeader',
props: GlobalHeaderProps,
render (h) {
const toggle = () => {
const { collapsed, handleCollapse } = this.$props
if (handleCollapse) handleCollapse(!collapsed)
this.triggerResizeEvent()
}
const renderCollapsedButton = () => {
const {
collapsed,
collapsedButtonRender = defaultRenderCollapsedButton,
menuRender
} = this.$props
if (collapsedButtonRender !== false && menuRender !== false) {
return (
<span class="ant-pro-global-header-trigger" onClick={toggle}>
{collapsedButtonRender(h, collapsed)}
</span>
)
}
return null
}
const { isMobile, logo, rightContentRender } = this.$props
const headerCls = 'ant-pro-global-header'
return (
<div class={headerCls}>
{isMobile && (
<a class={`${headerCls}-logo`} key="logo">
{defaultRenderLogo(h, logo)}
</a>
)}
{renderCollapsedButton()}
{rightContentRender && rightContentRender(h, this.$props)}
</div>
)
},
methods: {
triggerResizeEvent: debounce(() => {
inBrowser && triggerEvent(window, 'resize')
})
},
beforeDestroy () {
this.triggerResizeEvent.cancel()
}
}
export default GlobalHeader
.ant-pro-global-header {
.ant-pro-global-header-index-action {
i {
color: rgba(0,0,0,.65);
vertical-align: middle;
}
}
}
import './index.less'
const GridContent = {
name: 'GridContent',
functional: true,
props: {
children: {
type: null,
default: null
},
contentWidth: {
type: Boolean,
default: false
}
},
render (h, content) {
const { contentWidth: propsContentWidth } = content.props
const children = content.children
let className = 'ant-pro-grid-content'
if (propsContentWidth) {
className = className + ' wide'
}
return <div class={className}>{children}</div>
}
}
export default GridContent
@import "~ant-design-vue/es/style/themes/default";
@grid-content-prefix-cls: ~'@{ant-prefix}-pro-grid-content';
.@{grid-content-prefix-cls} {
width: 100%;
height: 100%;
min-height: 100%;
transition: 0.3s;
&.wide {
max-width: 1200px;
margin: 0 auto;
}
}
/* eslint-disable */
import GridContent from '../GridContent'
import { getComponentFromProp } from 'ant-design-vue/lib/_util/props-util'
const prefixedClassName = 'ant-pro-page-header-wrap'
const renderPageHeader = (h, content, extraContent) => {
if (!content && !extraContent) {
return null
}
return (
<div class={`${prefixedClassName}-detail`}>
<div class={`${prefixedClassName}-main`}>
<div class={`${prefixedClassName}-row`}>
{ content && (
<div class={`${prefixedClassName}-content`}>{content}</div>
)}
{ extraContent && (
<div class={`${prefixedClassName}-extraContent`}>
{extraContent}
</div>
)}
</div>
</div>
</div>
)
}
const defaultPageHeaderRender = (h, props) => {
const {
title,
content,
pageHeaderRender,
extraContent,
...restProps
} = props
return renderPageHeader(h, content, extraContent)
}
const PageHeaderWrapper = {
name: 'PageHeaderWrapper',
render (h) {
const children = getComponentFromProp(this, 'children')
return (
<div style={{ margin: '-24px -24px 0' }}>
<div class={`${prefixedClassName}-page-header-warp`}>
<GridContent>{defaultPageHeaderRender(h, this.$props)}</GridContent>
</div>
</div>
)
}
}
export default PageHeaderWrapper
import { Menu, Icon } from 'ant-design-vue'
export const RouteMenuProps = {
menus: {
type: Array,
required: true
},
theme: {
type: String,
required: false,
default: 'dark'
},
mode: {
type: String,
required: false,
default: 'inline'
},
collapsed: {
type: Boolean,
required: false,
default: false
},
i18nRender: {
type: Function,
default: () => undefined
}
}
const defaultI18nRender = (context) => context
const renderMenu = (h, item, i18nRender) => {
if (item && !item.hidden) {
const bool = item.children && !item.hideChildrenInMenu
return bool ? renderSubMenu(h, item, i18nRender) : renderMenuItem(h, item, i18nRender)
}
return null
}
const renderSubMenu = (h, item, i18nRender) => {
return (
<Menu.SubMenu key={item.path}>
<span slot={'title'}>
{renderIcon(h, item.meta.icon)}
<span>{renderTitle(h, item.meta.title, i18nRender)}</span>
</span>
{!item.hideChildrenInMenu && item.children.map(cd => renderMenu(h, cd, i18nRender))}
</Menu.SubMenu>
)
}
const renderMenuItem = (h, item, i18nRender) => {
const meta = Object.assign({}, item.meta)
const target = meta.target || null
const CustomTag = target && 'a' || 'router-link'
const props = { to: { name: item.name } }
const attrs = { href: item.path, target: target }
if (item.children && item.hideChildrenInMenu) {
// 把有子菜单的 并且 父菜单是要隐藏子菜单的
// 都给子菜单增加一个 hidden 属性
// 用来给刷新页面时, selectedKeys 做控制用
item.children.forEach(cd => {
cd.meta = Object.assign(cd.meta, { hidden: true })
})
}
return (
<Menu.Item key={item.path}>
<CustomTag {...{ props, attrs }}>
{renderIcon(h, meta.icon)}
<span>{renderTitle(h, meta.title, i18nRender)}</span>
</CustomTag>
</Menu.Item>
)
}
const renderIcon = (h, icon) => {
if (icon === undefined || icon === 'none' || icon === null) {
return null
}
const props = {}
typeof (icon) === 'object' ? (props.component = icon) : (props.type = icon)
return <Icon {...{ props }} />
}
const renderTitle = (h, title, i18nRender) => {
return <span>{ i18nRender(title) }</span>
}
const RouteMenu = {
name: 'RouteMenu',
props: RouteMenuProps,
data () {
return {
openKeys: [],
selectedKeys: [],
cachedOpenKeys: []
}
},
render (h) {
const { mode, theme, menus, i18nRender = defaultI18nRender } = this
const handleOpenChange = (openKeys) => {
// 在水平模式下时,不再执行后续
if (mode === 'horizontal') {
this.openKeys = openKeys
return
}
const latestOpenKey = openKeys.find(key => !this.openKeys.includes(key))
if (!this.rootSubmenuKeys.includes(latestOpenKey)) {
this.openKeys = openKeys
} else {
this.openKeys = latestOpenKey ? [latestOpenKey] : []
}
}
const dynamicProps = {
props: {
mode,
theme,
openKeys: this.openKeys
},
on: {
select: menu => {
this.selectedKeys = menu.selectedKeys
this.$emit('select', menu)
},
openChange: handleOpenChange
}
}
const menuItems = menus.map(item => {
if (item.hidden) {
return null
}
return renderMenu(h, item, i18nRender)
})
return <Menu vModel={this.selectedKeys} {...dynamicProps}>{menuItems}</Menu>
},
methods: {
updateMenu () {
const routes = this.$route.matched.concat()
const { hidden } = this.$route.meta
if (routes.length >= 3 && hidden) {
routes.pop()
this.selectedKeys = [routes[routes.length - 1].path]
} else {
this.selectedKeys = [routes.pop().path]
}
const openKeys = []
if (this.mode === 'inline') {
routes.forEach(item => {
item.path && openKeys.push(item.path)
})
}
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
}
},
computed: {
rootSubmenuKeys: vm => {
const keys = []
vm.menus.forEach(item => keys.push(item.path))
return keys
}
},
created () {
this.$watch('$route', () => {
this.updateMenu()
})
this.$watch('collapsed', val => {
if (val) {
this.cachedOpenKeys = this.openKeys.concat()
this.openKeys = []
} else {
this.openKeys = this.cachedOpenKeys
}
})
},
mounted () {
this.updateMenu()
}
}
export default RouteMenu
import RouteMenu from './Menu'
export default RouteMenu
import { Layout } from 'ant-design-vue'
import RouteMenu from '../RouteMenu'
import './index.less'
const { Sider } = Layout
export const SiderMenuProps = {
i18nRender: {
type: Function,
default: () => undefined
},
mode: {
type: String,
required: false,
default: 'inline'
},
theme: {
type: String,
required: false,
default: 'dark'
},
contentWidth: {
type: Boolean,
default: false
},
collapsible: {
type: Boolean,
required: false,
default: false
},
collapsed: {
type: Boolean,
required: false,
default: false
},
handleCollapse: {
type: Function,
default: () => undefined
},
menus: {
type: Array,
required: true
},
siderWidth: {
type: Number,
default: 256
},
isMobile: {
type: Boolean,
default: false
},
layout: {
type: String,
default: 'inline'
},
logo: {
type: null,
default: ''
},
title: {
type: String,
default: ''
}
}
export const defaultRenderLogo = (h, logo) => {
if (typeof logo === 'string') {
return <img src={logo} alt="logo" />
}
if (typeof logo === 'function') {
return logo()
}
return h(logo)
}
export const defaultRenderLogoAntTitle = (h, logo, title, menuHeaderRender) => {
if (menuHeaderRender === false) {
return null
}
const logoDom = defaultRenderLogo(h, logo)
const titleDom = <h1>{title}</h1>
if (menuHeaderRender) {
return menuHeaderRender(h, logoDom, titleDom)
}
return (
<span>
{logoDom}
{titleDom}
</span>
)
}
const SiderMenu = {
name: 'SiderMenu',
model: {
prop: 'collapsed',
event: 'collapse'
},
props: SiderMenuProps,
render (h) {
const {
collapsible,
collapsed,
siderWidth,
fixSiderbar,
mode,
theme,
menus,
logo,
title,
handleCollapse,
i18nRender
} = this
const siderCls = ['ant-pro-sider-menu-sider']
if (fixSiderbar) siderCls.push('fix-sider-bar')
if (theme === 'light') siderCls.push('light')
//
// const handleCollapse = (collapsed, type) => {
// this.$emit('collapse', collapsed)
// }
return (<Sider
class={siderCls}
breakpoint={'lg'}
trigger={null}
width={siderWidth}
theme={theme}
collapsible={collapsible}
collapsed={collapsed}
onCollapse={handleCollapse}
>
<div class='ant-pro-sider-menu-logo' id='logo'>
<router-link to={{ path: '/' }}>
{defaultRenderLogoAntTitle(h, logo, title, null)}
</router-link>
</div>
<RouteMenu collapsed={collapsed} menus={menus} mode={mode} theme={theme} i18nRender={i18nRender} />
</Sider>)
}
}
export default SiderMenu
import { Drawer } from 'ant-design-vue'
import SiderMenu, { SiderMenuProps } from './SiderMenu'
const SiderMenuWrapper = {
name: 'SiderMenuWrapper',
model: {
prop: 'collapsed',
event: 'collapse'
},
props: SiderMenuProps,
render () {
const {
isMobile,
collapsed,
handleCollapse: onCollapse
} = this
return isMobile ? (
<Drawer
class="ant-pro-sider-menu"
visible={!collapsed}
placement="left"
maskClosable
onClose={() => onCollapse && onCollapse(true)}
bodyStyle={{
padding: 0,
height: '100vh'
}}
>
<SiderMenu {...{ props: this.$props }} collapsed={isMobile ? false : collapsed} />
</Drawer>
) : (
<SiderMenu class="ant-pro-sider-menu" {...{ props: this.$props }} />
)
}
}
SiderMenuWrapper.install = function (Vue) {
Vue.component(SiderMenuWrapper.name, SiderMenuWrapper)
}
export default SiderMenuWrapper
import SiderMenu from './SiderMenu'
import SiderMenuWrapper from './SiderMenuWrapper'
export {
SiderMenu,
SiderMenuWrapper
}
@import "~ant-design-vue/es/style/themes/default";
@sider-menu-prefix-cls: ~'@{ant-prefix}-pro-sider-menu';
@nav-header-height: @layout-header-height;
.@{sider-menu-prefix-cls} {
&-logo {
position: relative;
height: 64px;
padding-left: 24px;
overflow: hidden;
transition: all .3s;
line-height: @nav-header-height;
background: @layout-sider-background;
svg, h1 {
display: inline-block;
}
svg {
height: 32px;
width: 32px;
vertical-align: middle;
}
h1 {
color: @white;
font-size: 20px;
margin: 0 0 0 12px;
font-family: Avenir,Helvetica Neue,Arial,Helvetica,sans-serif;
font-weight: 600;
vertical-align: middle;
}
}
&-sider {
position: relative;
z-index: 10;
min-height: 100vh;
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
&.fix-sider-bar {
position: fixed;
top: 0;
left: 0;
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
.ant-menu-root {
height: ~'calc(100vh - @{nav-header-height})';
overflow-y: auto;
}
.ant-menu-inline {
border-right: 0;
.ant-menu-item,
.ant-menu-submenu-title {
width: 100%;
}
}
}
&.light {
background-color: white;
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
.@{sider-menu-prefix-cls}-logo {
background: white;
box-shadow: 1px 1px 0 0 @border-color-split;
h1 {
color: @primary-color;
}
}
.ant-menu-light {
border-right-color: transparent;
}
}
}
&-icon {
width: 14px;
vertical-align: baseline;
}
.top-nav-menu li.ant-menu-item {
height: @nav-header-height;
line-height: @nav-header-height;
}
.drawer .drawer-content {
background: @layout-sider-background;
}
.ant-menu-inline-collapsed {
& > .ant-menu-item .sider-menu-item-img + span,
&
> .ant-menu-item-group
> .ant-menu-item-group-list
> .ant-menu-item
.sider-menu-item-img
+ span,
&
> .ant-menu-submenu
> .ant-menu-submenu-title
.sider-menu-item-img
+ span {
display: inline-block;
max-width: 0;
opacity: 0;
}
}
.ant-menu-item .sider-menu-item-img + span,
.ant-menu-submenu-title .sider-menu-item-img + span {
opacity: 1;
transition: opacity 0.3s @ease-in-out, width 0.3s @ease-in-out;
}
.ant-drawer-body {
padding: 0;
}
}
import RouteMenu from './RouteMenu'
import { SiderMenu, SiderMenuWrapper } from './SiderMenu'
import PageHeaderWrapper from './PageHeaderWrapper'
import GlobalFooter from './GlobalFooter'
export {
RouteMenu,
SiderMenu,
SiderMenuWrapper,
PageHeaderWrapper,
GlobalFooter
}
import BasicLayout, { BasicLayoutProps } from './BasicLayout'
import BlockLayout from './BlockLayout'
import PageView from './PageView'
import DocumentTitle from './components/DocumentTitle'
export {
PageView,
BlockLayout,
DocumentTitle,
BasicLayoutProps
}
export default BasicLayout
import client from 'webpack-theme-color-replacer/client'
import generate from '@ant-design/colors/lib/generate'
import { message } from 'ant-design-vue'
export const themeColor = {
getAntdSerials (color) {
// 淡化(即less的tint)
const lightens = new Array(9).fill().map((t, i) => {
return client.varyColor.lighten(color, i / 10)
})
// colorPalette 变换得到颜色值
const colorPalettes = generate(color)
const rgb = client.varyColor.toNum3(color.replace('#', '')).join(',')
return lightens.concat(colorPalettes).concat(rgb)
},
changeColor (newColor) {
var options = {
newColors: this.getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors`
changeUrl (cssUrl) {
return `/${cssUrl}` // while router is not `hash` mode, it needs absolute path
}
}
return client.changer.changeColor(options, Promise)
}
}
export const updateTheme = newPrimaryColor => {
const hideMessage = message.loading('正在切换主题', 0)
themeColor.changeColor(newPrimaryColor).finally(t => {
setTimeout(() => {
hideMessage()
}, 1500)
})
}
import request, { extend } from 'umi-request'
import { notification } from 'ant-design-vue'
const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。'
}
const errorHandler = (error) => {
const { response = {} } = error
const errortext = codeMessage[response.status] || response.statusText
const { status, url } = response
notification.error({
message: `请求错误 ${status}: ${url}`,
description: errortext
})
}
export const BASE_URL = process.env.VUE_APP_API_URL || '/api/v1'
const customRequest = extend({
prefix: BASE_URL,
timeout: 1000,
errorHandler
})
// request 拦截器
customRequest.interceptors.request.use((url, options) => {
return (
{
url: `${url}&interceptors=yes`,
options: { ...options, interceptors: true }
}
)
})
// response 拦截器
customRequest.interceptors.response.use((response, options) => {
response.headers.append('interceptors', 'yes yo')
return response
})
export {
request,
extend
}
export default customRequest
import triggerEvent from 'ant-design-vue/es/_util/triggerEvent'
import { inBrowser } from 'ant-design-vue/es/_util/env'
/**
* 触发 window.resize
*/
export {
triggerEvent,
inBrowser
}
export const setDocumentTitle = function (title) {
document.title = title
const ua = navigator.userAgent
// eslint-disable-next-line
const regex = /\bMicroMessenger\/([\d\.]+)/
if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) {
const i = document.createElement('iframe')
i.src = '/favicon.ico'
i.style.display = 'none'
i.onload = function () {
setTimeout(function () {
i.remove()
}, 9)
}
document.body.appendChild(i)
}
}
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'
describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
const msg = 'new message'
const wrapper = shallowMount(HelloWorld, {
propsData: { msg }
})
expect(wrapper.text()).toMatch(msg)
})
})
const path = require('path')
const { IgnorePlugin } = require('webpack')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const dynamicThemePlugin = require('./config/dynamicTheme.js')
const isProd = process.env.NODE_ENV === 'production'
const isAnalyz = process.env.IS_ANALYZ === 'true'
function resolve (dir) {
return path.join(__dirname, dir)
}
const assetsCDN = {
externals: {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios'
},
assets: {
css: [],
// https://unpkg.com/browse/vue@2.6.10/
js: [
'//cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js',
'//cdn.jsdelivr.net/npm/vue-router@3.1.3/dist/vue-router.min.js',
'//cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js',
'//cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js'
]
}
}
// vue.config
const defaultConfig = {
configureWebpack: {
plugins: [
// Ignore all locale files of moment.js
new IgnorePlugin(/^\.\/locale$/, /moment$/)
],
resolve: {
alias: {
'@ant-design/icons/lib/dist$': resolve('./example/src/core/antd/icons.js')
}
},
externals: isProd ? assetsCDN.externals : {}
},
chainWebpack: (config) => {
config.entry('app').clear().add('./example/src/main.js')
config.resolve.alias
.set('@config', resolve('./config'))
.set('@example', resolve('./example/src'))
// if `production` env require on cdn assets
isProd && config.plugin('html').tap(args => {
args[0].cdn = assetsCDN.assets
return args
})
// if `IS_ANALYZ` env is TRUE on report bundle info
isAnalyz && config.plugin('webpack-report').use(BundleAnalyzerPlugin, [
{
analyzerMode: 'static'
}
])
const svgRule = config.module.rule('svg')
svgRule.uses.clear()
svgRule.oneOf('inline')
.resourceQuery(/inline/)
.use('vue-svg-icon-loader')
.loader('vue-svg-icon-loader')
.end()
.end()
.oneOf('external')
.use('file-loader')
.loader('file-loader')
.options({
name: 'assets/[name].[hash:8].[ext]'
})
},
css: {
loaderOptions: {
less: {
modifyVars: {
// less vars,customize ant design theme
// 'primary-color': '#F5222D',
// 'link-color': '#F5222D',
// 'border-radius-base': '4px'
},
// DO NOT REMOVE THIS LINE
javascriptEnabled: true
}
}
},
devServer: {
// development server port 8000
port: 8000
// If you want to turn on the proxy, please remove the mockjs /src/main.jsL11
// proxy: {
// '/api': {
// target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro',
// ws: false,
// changeOrigin: true
// }
// }
},
// disable source map in production
productionSourceMap: false,
lintOnSave: undefined,
// babel-loader no-ignore node_modules/*
transpileDependencies: []
}
if (!isProd) {
defaultConfig.configureWebpack.plugins.push(dynamicThemePlugin())
}
module.exports = defaultConfig
This source diff could not be displayed because it is too large. You can view the blob instead.
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