Commit b4d53406 authored by 水落(YangLei)'s avatar 水落(YangLei)

feat: css框架引入,列表组件开发

parent ed077355
...@@ -27,3 +27,6 @@ $ npm run serve ...@@ -27,3 +27,6 @@ $ npm run serve
### 说明 ### 说明
用户名/密码:admin/666666 用户名/密码:admin/666666
css 工具函数 地址
https://www.tailwindcss.cn/docs/installation#post-css-7
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
}, },
"dependencies": { "dependencies": {
"@antv/data-set": "^0.11.4", "@antv/data-set": "^0.11.4",
"@tailwindcss/postcss7-compat": "^2.2.2",
"animate.css": "^4.1.0", "animate.css": "^4.1.0",
"ant-design-vue": "1.7.2", "ant-design-vue": "1.7.2",
"axios": "^0.19.2", "axios": "^0.19.2",
...@@ -26,6 +27,7 @@ ...@@ -26,6 +27,7 @@
"js-cookie": "^2.2.1", "js-cookie": "^2.2.1",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.2",
"viser-vue": "^2.4.8", "viser-vue": "^2.4.8",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-i18n": "^8.18.2", "vue-i18n": "^8.18.2",
......
import Table from './table/index.vue';
export { Table };
<template> <template>
<div :class="['page-header', layout, pageWidth]"> <div :class="['page-header', layout, pageWidth]">
<div class="page-header-wide"> <a-breadcrumb>
<div class="breadcrumb"> <a-breadcrumb-item :key="index" v-for="(item, index) in breadcrumb">
<a-breadcrumb> <span>{{ item }}</span>
<a-breadcrumb-item :key="index" v-for="(item, index) in breadcrumb"> </a-breadcrumb-item>
<span>{{item}}</span> </a-breadcrumb>
</a-breadcrumb-item>
</a-breadcrumb>
</div>
<div class="detail">
<div class="main">
<div class="row">
<h1 v-if="showPageTitle && title" class="title">{{title}}</h1>
<div class="action">
<slot name="action"></slot>
</div>
</div>
<div class="row">
<div v-if="this.$slots.content" class="content">
<div v-if="avatar" class="avatar">
<a-avatar :src="avatar" :size="72" />
</div>
<slot name="content"></slot>
</div>
<div v-if="this.$slots.extra" class="extra">
<slot name="extra"></slot>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</template> </template>
...@@ -63,43 +38,11 @@ export default { ...@@ -63,43 +38,11 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.page-header { .page-header {
background: @base-bg-color;
padding: 16px 24px; padding: 16px 24px;
&.head.fixed { &.head.fixed {
margin: auto; margin: auto;
max-width: 1400px; max-width: 1400px;
} }
.page-header-wide {
.breadcrumb {
margin-bottom: 20px;
}
.detail {
display: flex;
.row {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.avatar {
margin: 0 24px 0 0;
}
.main {
width: 100%;
.title {
font-size: 20px;
color: @title-color;
margin-bottom: 16px;
}
.content {
display: flex;
flex-wrap: wrap;
color: @text-color-second;
}
.extra {
display: flex;
}
}
}
}
} }
</style> </style>
<template>
<div>
<div :class="$style.card" v-if="$scopedSlots.search">
<a-form-model layout="inline" :model="queryForm">
<slot name="search" :query="queryForm" />
</a-form-model>
<div class="tw-text-right">
<a-space>
<a-button @click="queryForm = {}">重置</a-button>
<a-button type="primary" @click="getData">查询</a-button>
</a-space>
</div>
</div>
<div class="tw-mt-3" :class="$style.card">
<a-space class="tw-mb-2.5">
<a-button v-if="addBtn" type="primary" key="add" v-bind="addBtn.options" @click="add">
{{ addBtn.text || '新建' }}
</a-button>
<template v-for="btn of buttons">
<a-button :type="btn.primary" :key="btn.text">{{ btn.text }}</a-button>
</template>
</a-space>
<a-table :data-source="data" :loading="loading" v-bind="$attrs">
<slot />
</a-table>
</div>
<!-- 新增 -->
<a-drawer
:title="addBtn.title || '新建'"
placement="right"
:visible="addVisible"
@close="addDrawerClose"
v-if="addBtn"
:maskClosable="addBtn.maskClosable"
>
<slot name="add" />
</a-drawer>
</div>
</template>
<script>
import { request, METHOD } from '@/utils/requestUtil';
import BASE_URL from '@/utils/baseUrlUtil';
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
export default {
props: {
url: String,
buttons: {
type: Array,
default: () => [],
},
addBtn: {
type: Object,
},
},
data() {
return {
data: [],
queryForm: {},
loading: false,
addVisible: false,
};
},
mounted() {
this.getData();
},
methods: {
async getData() {
console.log('查询');
this.loading = true;
setTimeout(() => {
this.data = data;
this.loading = false;
}, 1000);
// const res = await request(`${BASE_URL}/${this.url}`, METHOD.GET);
// console.log(res);
},
add() {
this.addVisible = true;
},
addDrawerClose() {
this.addVisible = false;
},
},
};
</script>
<style module>
.card {
@apply tw-bg-white tw-rounded-md tw-p-2.5;
}
</style>
...@@ -7,6 +7,7 @@ import { initRouter } from './router'; ...@@ -7,6 +7,7 @@ import { initRouter } from './router';
import VueI18n from 'vue-i18n'; import VueI18n from 'vue-i18n';
import {accountModule, settingModule} from './pages/frame/store'; import {accountModule, settingModule} from './pages/frame/store';
import App from './App.vue'; import App from './App.vue';
import "tailwindcss/tailwind.css"
import 'animate.css/source/animate.css'; import 'animate.css/source/animate.css';
import './theme/index.less'; import './theme/index.less';
......
import Mock from 'mockjs';
Mock.mock(`${process.env.VUE_APP_API_BASE_URL}/table`, 'post', () => ({
data: {
pageNum: 1,
pageSize: 10,
records: [
{
regionId: 1403455275008032,
regionName: '陈浩玮',
organizationId: 146,
organizationName: '一级系统:巴基斯坦公司',
remark: '123',
editorId: 1,
editorName: '管理员',
editTime: '2021-03-16 22:20:25',
},
{
regionId: 1403455136596000,
regionName: '地点测试',
organizationId: 212,
organizationName: '吴国',
remark: '123',
editorId: 1,
editorName: '管理员',
editTime: '2021-03-16 22:19:19',
},
{
regionId: 1400871246102560,
regionName: '陈浩玮测试',
organizationId: 212,
organizationName: '吴国',
remark: '321',
editorId: 1,
editorName: '管理员',
editTime: '2021-03-02 16:04:25',
},
{
regionId: 1396338094768160,
regionName: '文件地区',
organizationId: 208,
organizationName: '扩普发展',
remark: '123213123123213213223123123',
editorId: 1,
editorName: '管理员',
editTime: '2021-03-02 15:37:11',
},
],
total: 4,
size: 10,
current: 1,
pages: 1,
},
code: 'sys.success',
message: null,
traceId: '1805b654e82a4f2a8357d2f8922d9b19',
meta: null,
}));
<template> <template>
<div class="page-layout"> <div class="page-layout">
<page-header ref="pageHeader" :style="`margin-top: ${multiPage ? 0 : -24}px`" :breadcrumb="breadcrumb" :title="pageTitle" :logo="logo" :avatar="avatar"> <page-header
ref="pageHeader"
:style="`margin-top: ${multiPage ? 0 : -24}px`"
:breadcrumb="breadcrumb"
:title="pageTitle"
:logo="logo"
:avatar="avatar"
>
<slot name="action" slot="action"></slot> <slot name="action" slot="action"></slot>
<slot slot="content" name="headerContent"></slot> <slot slot="content" name="headerContent"></slot>
<div slot="content" v-if="!this.$slots.headerContent && desc"> <div slot="content" v-if="!this.$slots.headerContent && desc">
<p>{{ desc }}</p> <p>{{ desc }}</p>
<div v-if="this.linkList" class="link"> <div v-if="this.linkList" class="link">
<template v-for="(link, index) in linkList"> <template v-for="(link, index) in linkList">
<a :key="index" :href="link.href"> <a :key="index" :href="link.href"> <a-icon :type="link.icon" />{{ link.title }} </a>
<a-icon :type="link.icon" />{{ link.title }}
</a>
</template> </template>
</div> </div>
</div> </div>
...@@ -76,7 +81,7 @@ export default { ...@@ -76,7 +81,7 @@ export default {
let breadcrumb = page && page.breadcrumb; let breadcrumb = page && page.breadcrumb;
if (breadcrumb) { if (breadcrumb) {
let i18nBreadcrumb = []; let i18nBreadcrumb = [];
breadcrumb.forEach((item) => { breadcrumb.forEach(item => {
i18nBreadcrumb.push(this.$t(item)); i18nBreadcrumb.push(this.$t(item));
}); });
return i18nBreadcrumb; return i18nBreadcrumb;
...@@ -95,8 +100,8 @@ export default { ...@@ -95,8 +100,8 @@ export default {
const path = this.$route.path; const path = this.$route.path;
let breadcrumb = []; let breadcrumb = [];
routes routes
.filter((item) => path.includes(item.path)) .filter(item => path.includes(item.path))
.forEach((route) => { .forEach(route => {
const path = route.path.length === 0 ? '/home' : route.path; const path = route.path.length === 0 ? '/home' : route.path;
breadcrumb.push(this.$t(getI18nKey(path))); breadcrumb.push(this.$t(getI18nKey(path)));
}); });
...@@ -119,11 +124,7 @@ export default { ...@@ -119,11 +124,7 @@ export default {
</script> </script>
<style lang="less"> <style lang="less">
.page-header {
margin: 0 -24px 0;
}
.link { .link {
/*margin-top: 16px;*/
line-height: 24px; line-height: 24px;
a { a {
font-size: 14px; font-size: 14px;
...@@ -136,9 +137,7 @@ export default { ...@@ -136,9 +137,7 @@ export default {
} }
.page-content { .page-content {
position: relative; position: relative;
padding: 24px 0 0;
&.side {
}
&.head.fixed { &.head.fixed {
margin: 0 auto; margin: 0 auto;
max-width: 1400px; max-width: 1400px;
......
<template> <template>
<h1>Menu Management</h1> <my-table url="table" addBtn="addBtn">
<template #search="{query}">
<a-form-model-item label="所属组织">
<a-input v-model="query.name" />
</a-form-model-item>
<a-form-model-item label="所属组织">
<a-input v-model="query.name" />
</a-form-model-item>
<a-form-model-item label="所属组织">
<a-input v-model="query.name" />
</a-form-model-item>
<a-form-model-item label="所属组织">
<a-input v-model="query.name" />
</a-form-model-item>
<a-form-model-item label="所属组织">
<a-input v-model="query.name" />
</a-form-model-item>
<a-form-model-item label="所属组织">
<a-input v-model="query.name" />
</a-form-model-item>
</template>
<template #add>
<a-input v-model="add.name" />
</template>
<a-table-column title="姓名" data-index="name" />
<a-table-column title="操作">
<template #default="row">
<span>
<a> {{ row }}</a>
<a-divider type="vertical" />
<a>Delete</a>
</span>
</template>
</a-table-column>
</my-table>
</template> </template>
\ No newline at end of file <script>
export default {
data: () => ({
add: {},
addBtn: {},
}),
};
</script>
<template>
<my-table url="table" :search="search" />
</template>
<script>
export default {
data: () => ({
search: [
{
type: 'input',
key: 'name',
label: '所属组织',
options: {
placeholder: '请选择所属组织',
},
},
{
type: 'input',
key: 'address',
label: '地区名称',
},
],
}),
};
</script>
import VueI18nPlugin from './vueI18nPlugin' import VueI18nPlugin from './vueI18nPlugin';
import AuthorityPlugin from './authorityPlugin' import AuthorityPlugin from './authorityPlugin';
import TabsPagePlugin from './tabsPagePlugin' import TabsPagePlugin from './tabsPagePlugin';
import { Table } from '@/components';
const Plugins = { const Plugins = {
install: function (Vue) { install: function(Vue) {
Vue.use(VueI18nPlugin) Vue.use(VueI18nPlugin);
Vue.use(AuthorityPlugin) Vue.use(AuthorityPlugin);
Vue.use(TabsPagePlugin) Vue.use(TabsPagePlugin);
} Vue.component('my-table', Table);
} },
};
export default Plugins; export default Plugins;
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
//const API_PROXY_PREFIX='/api' //const API_PROXY_PREFIX='/api'
//const BASE_URL = process.env.NODE_ENV === 'production' ? process.env.VUE_APP_API_BASE_URL : API_PROXY_PREFIX //const BASE_URL = process.env.NODE_ENV === 'production' ? process.env.VUE_APP_API_BASE_URL : API_PROXY_PREFIX
const BASE_URL = process.env.VUE_APP_API_BASE_URL; const BASE_URL = process.env.VUE_APP_API_BASE_URL;
export default BASE_URL; export default BASE_URL;
\ No newline at end of file
module.exports = {
purge: ['./src/*.vue'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
prefix: 'tw-',
corePlugins: {
preflight: false,
fixed: false,
},
};
let path = require("path"); let path = require('path');
const webpack = require("webpack"); const webpack = require('webpack');
const ThemeColorReplacer = require("webpack-theme-color-replacer"); const ThemeColorReplacer = require('webpack-theme-color-replacer');
const globalConfig = require("./src/config/global.config"); const globalConfig = require('./src/config/global.config');
const settingConfig = require("./src/config/setting.config"); const settingConfig = require('./src/config/setting.config');
const cssResolverConfig = require("./src/config/cssResolver.config"); const cssResolverConfig = require('./src/config/cssResolver.config');
const { getMenuColors, getAntdColors, getThemeToggleColors, getFunctionalColors } = require('./src/utils/colorUtil'); const {
getMenuColors,
const CompressionWebpackPlugin = require("compression-webpack-plugin"); getAntdColors,
getThemeToggleColors,
getFunctionalColors,
} = require('./src/utils/colorUtil');
const productionGzipExtensions = ["js", "css"]; const CompressionWebpackPlugin = require('compression-webpack-plugin');
const isProd = process.env.NODE_ENV === "production";
const productionGzipExtensions = ['js', 'css'];
const isProd = process.env.NODE_ENV === 'production';
const theme = settingConfig.theme; const theme = settingConfig.theme;
function getThemeColors(color, $theme) { function getThemeColors(color, $theme) {
const _color = color || theme.color const _color = color || theme.color;
const mode = $theme || theme.mode const mode = $theme || theme.mode;
const replaceColors = getThemeToggleColors(_color, mode) const replaceColors = getThemeToggleColors(_color, mode);
const themeColors = [ const themeColors = [
...replaceColors.mainColors, ...replaceColors.mainColors,
...replaceColors.subColors, ...replaceColors.subColors,
...replaceColors.menuColors, ...replaceColors.menuColors,
...replaceColors.contentColors, ...replaceColors.contentColors,
...replaceColors.rgbColors, ...replaceColors.rgbColors,
...replaceColors.functionalColors.success, ...replaceColors.functionalColors.success,
...replaceColors.functionalColors.warning, ...replaceColors.functionalColors.warning,
...replaceColors.functionalColors.error, ...replaceColors.functionalColors.error,
] ];
return themeColors return themeColors;
} }
function modifyVars(color) { function modifyVars(color) {
let _color = color || theme.color let _color = color || theme.color;
const palettes = getAntdColors(_color, theme.mode) const palettes = getAntdColors(_color, theme.mode);
const menuColors = getMenuColors(_color, theme.mode) const menuColors = getMenuColors(_color, theme.mode);
const { success, warning, error } = getFunctionalColors(theme.mode) const { success, warning, error } = getFunctionalColors(theme.mode);
const primary = palettes[5] const primary = palettes[5];
return { return {
'primary-color': primary, 'primary-color': primary,
'primary-1': palettes[0], 'primary-1': palettes[0],
'primary-2': palettes[1], 'primary-2': palettes[1],
'primary-3': palettes[2], 'primary-3': palettes[2],
'primary-4': palettes[3], 'primary-4': palettes[3],
'primary-5': palettes[4], 'primary-5': palettes[4],
'primary-6': palettes[5], 'primary-6': palettes[5],
'primary-7': palettes[6], 'primary-7': palettes[6],
'primary-8': palettes[7], 'primary-8': palettes[7],
'primary-9': palettes[8], 'primary-9': palettes[8],
'primary-10': palettes[9], 'primary-10': palettes[9],
'info-color': primary, 'info-color': primary,
'success-color': success[5], 'success-color': success[5],
'warning-color': warning[5], 'warning-color': warning[5],
'error-color': error[5], 'error-color': error[5],
'alert-info-bg-color': palettes[0], 'alert-info-bg-color': palettes[0],
'alert-info-border-color': palettes[2], 'alert-info-border-color': palettes[2],
'alert-success-bg-color': success[0], 'alert-success-bg-color': success[0],
'alert-success-border-color': success[2], 'alert-success-border-color': success[2],
'alert-warning-bg-color': warning[0], 'alert-warning-bg-color': warning[0],
'alert-warning-border-color': warning[2], 'alert-warning-border-color': warning[2],
'alert-error-bg-color': error[0], 'alert-error-bg-color': error[0],
'alert-error-border-color': error[2], 'alert-error-border-color': error[2],
'processing-color': primary, 'processing-color': primary,
'menu-dark-submenu-bg': menuColors[0], 'menu-dark-submenu-bg': menuColors[0],
'layout-header-background': menuColors[1], 'layout-header-background': menuColors[1],
'layout-trigger-background': menuColors[2], 'layout-trigger-background': menuColors[2],
'btn-danger-bg': error[4], 'btn-danger-bg': error[4],
'btn-danger-border': error[4], 'btn-danger-border': error[4],
...globalConfig.theme[theme.mode] ...globalConfig.theme[theme.mode],
} };
} }
// 修正 webpack-theme-color-replacer 插件提取的 css 结果 // 修正 webpack-theme-color-replacer 插件提取的 css 结果
function resolveCss(output, srcArr) { function resolveCss(output, srcArr) {
let regExps = [] let regExps = [];
// 提取 resolve 配置中所有的正则配置 // 提取 resolve 配置中所有的正则配置
Object.keys(cssResolverConfig).forEach(key => { Object.keys(cssResolverConfig).forEach(key => {
let isRegExp = false let isRegExp = false;
let reg = {} let reg = {};
try { try {
reg = eval(key) reg = eval(key);
isRegExp = reg instanceof RegExp isRegExp = reg instanceof RegExp;
} catch (e) { } catch (e) {
isRegExp = false isRegExp = false;
} }
if (isRegExp) { if (isRegExp) {
regExps.push([reg, cssResolverConfig[key]]) regExps.push([reg, cssResolverConfig[key]]);
} }
}) });
// 去重 // 去重
srcArr = dropDuplicate(srcArr) srcArr = dropDuplicate(srcArr);
// 处理 css // 处理 css
let outArr = [] let outArr = [];
srcArr.forEach(text => { srcArr.forEach(text => {
// 转换为 css 对象 // 转换为 css 对象
let cssObj = parseCssObj(text) let cssObj = parseCssObj(text);
// 根据selector匹配配置,匹配成功,则按配置处理 css // 根据selector匹配配置,匹配成功,则按配置处理 css
if (cssResolverConfig[cssObj.selector] != undefined) { if (cssResolverConfig[cssObj.selector] != undefined) {
let cfg = cssResolverConfig[cssObj.selector] let cfg = cssResolverConfig[cssObj.selector];
if (cfg) { if (cfg) {
outArr.push(cfg.resolve(text, cssObj)) outArr.push(cfg.resolve(text, cssObj));
} }
} else { } else {
let cssText = '' let cssText = '';
// 匹配不成功,则测试是否有匹配的正则配置,有则按正则对应的配置处理 // 匹配不成功,则测试是否有匹配的正则配置,有则按正则对应的配置处理
for (let regExp of regExps) { for (let regExp of regExps) {
if (regExp[0].test(cssObj.selector)) { if (regExp[0].test(cssObj.selector)) {
let cssCfg = regExp[1] let cssCfg = regExp[1];
cssText = cssCfg ? cssCfg.resolve(text, cssObj) : '' cssText = cssCfg ? cssCfg.resolve(text, cssObj) : '';
break break;
}
// 未匹配到正则,则设置 cssText 为默认的 css(即不处理)
cssText = text;
}
if (cssText != '') {
outArr.push(cssText);
}
} }
// 未匹配到正则,则设置 cssText 为默认的 css(即不处理) });
cssText = text output = outArr.join('\n');
} return output;
if (cssText != '') {
outArr.push(cssText)
}
}
})
output = outArr.join('\n')
return output
} }
// 数组去重 // 数组去重
function dropDuplicate(arr) { function dropDuplicate(arr) {
let map = {} let map = {};
let r = [] let r = [];
for (let s of arr) { for (let s of arr) {
if (!map[s]) { if (!map[s]) {
r.push(s) r.push(s);
map[s] = 1 map[s] = 1;
}
} }
} return r;
return r
} }
/** /**
...@@ -146,137 +151,142 @@ function dropDuplicate(arr) { ...@@ -146,137 +151,142 @@ function dropDuplicate(arr) {
* }} * }}
*/ */
function parseCssObj(cssText) { function parseCssObj(cssText) {
let css = {} let css = {};
const ruleIndex = cssText.indexOf('{') const ruleIndex = cssText.indexOf('{');
css.selector = cssText.substring(0, ruleIndex) css.selector = cssText.substring(0, ruleIndex);
const ruleBody = cssText.substring(ruleIndex + 1, cssText.length - 1) const ruleBody = cssText.substring(ruleIndex + 1, cssText.length - 1);
const rules = ruleBody.split(';') const rules = ruleBody.split(';');
css.rules = rules css.rules = rules;
css.toText = function () { css.toText = function() {
let body = '' let body = '';
this.rules.forEach(item => { body += item + ';' }) this.rules.forEach(item => {
return `${this.selector}{${body}}` body += item + ';';
} });
return css return `${this.selector}{${body}}`;
};
return css;
} }
const assetsCDN = { const assetsCDN = {
// webpack build externals cdn预加载使用 // webpack build externals cdn预加载使用
externals: { externals: {
vue: "Vue", vue: 'Vue',
"vue-router": "VueRouter", 'vue-router': 'VueRouter',
vuex: "Vuex", vuex: 'Vuex',
axios: "axios", axios: 'axios',
nprogress: "NProgress", nprogress: 'NProgress',
clipboard: "ClipboardJS", clipboard: 'ClipboardJS',
"@antv/data-set": "DataSet", '@antv/data-set': 'DataSet',
"js-cookie": "Cookies", 'js-cookie': 'Cookies',
}, },
css: [], css: [],
js: [ js: [
"//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js", '//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
"//cdn.jsdelivr.net/npm/vue-router@3.3.4/dist/vue-router.min.js", '//cdn.jsdelivr.net/npm/vue-router@3.3.4/dist/vue-router.min.js',
"//cdn.jsdelivr.net/npm/vuex@3.4.0/dist/vuex.min.js", '//cdn.jsdelivr.net/npm/vuex@3.4.0/dist/vuex.min.js',
"//cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js", '//cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
"//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js", '//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js',
"//cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js", '//cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js',
"//cdn.jsdelivr.net/npm/@antv/data-set@0.11.4/build/data-set.min.js", '//cdn.jsdelivr.net/npm/@antv/data-set@0.11.4/build/data-set.min.js',
"//cdn.jsdelivr.net/npm/js-cookie@2.2.1/src/js.cookie.min.js", '//cdn.jsdelivr.net/npm/js-cookie@2.2.1/src/js.cookie.min.js',
], ],
}; };
module.exports = { module.exports = {
devServer: { devServer: {
// proxy: { // proxy: {
// '/api': { //此处要与 /services/api.js 中的 API_PROXY_PREFIX 值保持一致 // '/api': { //此处要与 /services/api.js 中的 API_PROXY_PREFIX 值保持一致
// target: process.env.VUE_APP_API_BASE_URL, // target: process.env.VUE_APP_API_BASE_URL,
// changeOrigin: true, // changeOrigin: true,
// pathRewrite: { // pathRewrite: {
// '^/api': '' // '^/api': ''
// } // }
// } // }
// } // }
},
pluginOptions: {
/**
* 导入css 预处理器的一些公共的样式文件变量,比如:variables , mixins , functions,
* 避免在每个样式文件中手动的@import导入,然后在各个css 文件中直接使用 变量。
*
* theme.less 定义的变量预先导入 直接使用
*/
"style-resources-loader": {
preProcessor: "less",
//这个是绝对路径,不能使用 alias中配置的别名路径,如@表示的src
patterns: [path.resolve(__dirname, "./src/theme/default.less")],
}, },
}, pluginOptions: {
configureWebpack: (config) => { /**
config.entry.app = ["babel-polyfill", "whatwg-fetch", "./src/main.js"]; * 导入css 预处理器的一些公共的样式文件变量,比如:variables , mixins , functions,
config.performance = { * 避免在每个样式文件中手动的@import导入,然后在各个css 文件中直接使用 变量。
hints: false, *
}; * theme.less 定义的变量预先导入 直接使用
config.plugins.push( */
new ThemeColorReplacer({ 'style-resources-loader': {
//Optional. output css file name, suport [contenthash] and [hash]. preProcessor: 'less',
fileName: "css/theme-colors-[contenthash:8].css", //这个是绝对路径,不能使用 alias中配置的别名路径,如@表示的src
//matchColors: Array<string> Colors array for extracting css file. patterns: [path.resolve(__dirname, './src/theme/default.less')],
//Css rules which have any one of these colors will be extracted out. },
//抽取所有相关颜色做替换 },
matchColors: getThemeColors(), configureWebpack: config => {
//injectCss: boolean Optional. Inject css text into js file, no need to download theme-colors-xxx.css any more. config.entry.app = ['babel-polyfill', 'whatwg-fetch', './src/main.js'];
injectCss: true, config.performance = {
//resolveCss: Function(resultCss : string) : string Optional. Resolve result css code as you wish. hints: false,
//转换原有CSS中的特定样式为自定义样式 };
resolveCss, config.plugins.push(
}) new ThemeColorReplacer({
); //Optional. output css file name, suport [contenthash] and [hash].
// Ignore all locale files of moment.js fileName: 'css/theme-colors-[contenthash:8].css',
config.plugins.push(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)); //matchColors: Array<string> Colors array for extracting css file.
// 生产环境下将资源压缩成gzip格式 //Css rules which have any one of these colors will be extracted out.
if (isProd) { //抽取所有相关颜色做替换
// add `CompressionWebpack` plugin to webpack plugins matchColors: getThemeColors(),
config.plugins.push( //injectCss: boolean Optional. Inject css text into js file, no need to download theme-colors-xxx.css any more.
new CompressionWebpackPlugin({ injectCss: true,
algorithm: "gzip", //resolveCss: Function(resultCss : string) : string Optional. Resolve result css code as you wish.
test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"), //转换原有CSS中的特定样式为自定义样式
threshold: 10240, resolveCss,
minRatio: 0.8, }),
}) );
); // Ignore all locale files of moment.js
} config.plugins.push(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/));
// if prod, add externals // 生产环境下将资源压缩成gzip格式
if (isProd) { if (isProd) {
config.externals = assetsCDN.externals; // add `CompressionWebpack` plugin to webpack plugins
} config.plugins.push(
}, new CompressionWebpackPlugin({
chainWebpack: (config) => { algorithm: 'gzip',
// 生产环境下关闭css压缩的 colormin 项,因为此项优化与主题色替换功能冲突 test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
if (isProd) { threshold: 10240,
config.plugin("optimize-css").tap((args) => { minRatio: 0.8,
args[0].cssnanoOptions.preset[1].colormin = false; }),
return args; );
}); }
} // if prod, add externals
// 生产环境下使用CDN if (isProd) {
if (isProd) { config.externals = assetsCDN.externals;
config.plugin("html").tap((args) => { }
args[0].cdn = assetsCDN; },
return args; chainWebpack: config => {
}); // 生产环境下关闭css压缩的 colormin 项,因为此项优化与主题色替换功能冲突
} if (isProd) {
}, config.plugin('optimize-css').tap(args => {
css: { args[0].cssnanoOptions.preset[1].colormin = false;
loaderOptions: { return args;
less: { });
lessOptions: { }
modifyVars: modifyVars(), // 生产环境下使用CDN
javascriptEnabled: true, if (isProd) {
config.plugin('html').tap(args => {
args[0].cdn = assetsCDN;
return args;
});
}
},
css: {
loaderOptions: {
less: {
lessOptions: {
modifyVars: modifyVars(),
javascriptEnabled: true,
},
},
postcss: {
plugins: [require('tailwindcss')],
},
}, },
},
}, },
}, publicPath: process.env.VUE_APP_PUBLIC_PATH,
publicPath: process.env.VUE_APP_PUBLIC_PATH, outputDir: 'dist',
outputDir: "dist", assetsDir: 'static',
assetsDir: "static", productionSourceMap: false,
productionSourceMap: false,
}; };
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