提交 acef7ce5 编写于 作者: 小木偶MO

init

上级 9111d8a1
> 1%
last 2 versions
not dead
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
src/assets
src/icons
public
dist
node_modules
module.exports = {
root: true,
env: {
node: true,
},
extends: ["plugin:vue/recommended", "eslint:recommended", "@vue/prettier"],
parser: "vue-eslint-parser",
parserOptions: {
ecmaVersion: 2020,
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
rules: {
//'no-undef': 2,
"no-console": "off",
"no-debugger": "off",
"vue/no-v-html": "off",
"vue/html-self-closing": [
"error",
{
html: {
void: "any",
normal: "any",
component: "always",
},
svg: "always",
math: "always",
},
],
// Vue.js风格指南(https://cn.vuejs.org/v2/style-guide/)
// Vue组件排序
"vue/order-in-components": [
"warn",
{
order: [
"el",
"name",
"key",
"parent",
"functional",
["delimiters", "comments"],
["components", "directives", "filters"],
"extends",
"mixins",
["provide", "inject"],
"ROUTER_GUARDS",
"layout",
"middleware",
"validate",
"scrollToTop",
"transition",
"loading",
"inheritAttrs",
"model",
["props", "propsData"],
"emits",
"setup",
"fetch",
"asyncData",
"data",
"head",
"computed",
"watch",
"watchQuery",
"LIFECYCLE_HOOKS",
"methods",
["template", "render"],
"renderError",
],
},
],
// Vue属性排序
"vue/attributes-order": [
"warn",
{
order: [
"DEFINITION",
"LIST_RENDERING",
"CONDITIONALS",
"RENDER_MODIFIERS",
"GLOBAL",
"UNIQUE",
"TWO_WAY_BINDING",
"OTHER_DIRECTIVES",
"OTHER_ATTR",
"EVENTS",
"CONTENT",
],
alphabetical: true, //字母顺序
},
],
},
overrides: [
{
files: [
"**/__tests__/*.{j,t}s?(x)",
"**/tests/unit/**/*.spec.{j,t}s?(x)",
],
env: {
jest: true,
},
},
],
};
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Lock files
yarn.lock
pnpm-lock.yaml
package-lock.json
# Yarn v2
.pnp.*
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
# Vab
public/video
*.zip
*.7z
*.rar
src/vab/styles/themes/red.scss
module.exports = {
extends: ['stylelint-config-recess-order', 'stylelint-config-prettier'],
}
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
env: {
development: {
plugins: ['dynamic-import-node'],
},
},
}
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules"]
}
const chokidarNext = require('chokidar')
const bodyParser = require('body-parser')
const chalkNext = require('chalk')
const path = require('path')
const { mock } = require('mockjs')
const { baseURL } = require('../src/config')
const mockDir = path.join(process.cwd(), 'mock')
const { handleMockArray } = require('./utils')
/**
*
* @param url
* @param type
* @param respond
* @returns {{response(*=, *=): void, type: (*|string), url: RegExp}}
*/
const responseFake = (url, type, respond) => {
return {
url: new RegExp(`${baseURL}${url}`),
type: type || 'get',
response(req, res) {
res.status(200)
console.log(chalkNext.green(`\n> 请求地址:${req.path}`))
if (JSON.stringify(req.body) !== '{}')
console.log(
chalkNext.green(`> 请求参数(body):${JSON.stringify(req.body)}`)
)
if (JSON.stringify(req.query) !== '{}')
console.log(
chalkNext.green(`> 请求参数(query):${JSON.stringify(req.query)}`)
)
res.json(mock(respond instanceof Function ? respond(req, res) : respond))
},
}
}
/**
*
* @param app
* @returns {{mockStartIndex: number, mockRoutesLength: number}}
*/
const registerRoutes = (app) => {
let mockLastIndex
const mocks = []
const mockArray = handleMockArray()
mockArray.forEach((item) => {
const obj = require(item)
mocks.push(...obj)
})
const mocksForServer = mocks.map((route) =>
responseFake(route.url, route.type, route.response)
)
const mockRoutesLength = Object.keys(mocksForServer).length
for (const item of mocksForServer) {
app[item.type](item.url, item.response)
mockLastIndex = app._router.stack.length
}
return {
mockRoutesLength,
mockStartIndex: mockLastIndex - mockRoutesLength,
}
}
/**
*
* @param app
*/
module.exports = (app) => {
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
extended: true,
})
)
const mockRoutes = registerRoutes(app)
let mockRoutesLength = mockRoutes.mockRoutesLength
let mockStartIndex = mockRoutes.mockStartIndex
chokidarNext
.watch(mockDir, {
ignored: /xmo-mock-server/,
ignoreInitial: true,
})
.on('all', (event) => {
if (event === 'change' || event === 'add') {
try {
app._router.stack.splice(mockStartIndex, mockRoutesLength)
Object.keys(require.cache).forEach((item) => {
if (item.includes(mockDir))
delete require.cache[require.resolve(item)]
})
const mockRoutes = registerRoutes(app)
mockRoutesLength = mockRoutes.mockRoutesLength
mockStartIndex = mockRoutes.mockStartIndex
} catch (error) {
console.log(chalkNext.red(error))
}
}
})
}
const fs = require('fs')
const { Random } = require('mockjs')
/**
* @description 随机生成图片url。
* @returns {string}
*/
function handleRandomImage(width = 50, height = 50) {
return `https://picsum.photos/${width}/${height}?random=${Random.guid()}`
}
/**
* @description 处理所有 controller 模块,npm run serve时在node环境中自动输出controller文件夹下Mock接口,请勿修改。
* @returns {[]}
*/
function handleMockArray() {
const getFiles = (path, baseUrl = './controller') => {
const files = fs.readdirSync(path)
return files.flatMap((file) => {
const fPath = `${path}/${file}`
const stat = fs.statSync(fPath)
return stat.isDirectory()
? getFiles(fPath, `${baseUrl}/${file}`)
: `${baseUrl}/${file}`
})
}
return getFiles('mock/controller')
}
module.exports = {
handleRandomImage,
handleMockArray,
}
{
"name": "exam-ui",
"version": "0.0.1",
"author": "xmo",
"license": "Mozilla Public License Version 2.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"build:report": "vue-cli-service build --report",
"lint": "eslint --ext .js mock --fix&&vue-cli-service lint",
"lint:style": "stylelint **/*.{vue,scss} --fix",
"inspect": "vue-cli-service inspect",
"clear": "npm cache clean -f&&rimraf node_modules&&npm install --registry=https://registry.npm.taobao.org&&cnpm i image-webpack-loader -D",
"use:npm": "nrm use npm",
"use:taobao": "nrm use taobao",
"update": "ncu -u --reject compression-webpack-plugin,filemanager-webpack-plugin,sass,sass-loader --registry https://registry.npm.taobao.org&&npm install --registry=https://registry.npm.taobao.org&&cnpm i image-webpack-loader -D",
"update:globle": "ncu -g",
"image-webpack-loader": "cnpm i image-webpack-loader -D"
},
"repository": {
"type": "git",
"url": "https://codechina.csdn.net/xmo/exam-ui.git"
},
"dependencies": {
"axios": "^0.21.1",
"clipboard": "^2.0.8",
"core-js": "^3.15.1",
"dayjs": "^1.10.5",
"echarts": "^5.1.2",
"element-ui": "^2.15.3",
"js-cookie": "^2.2.1",
"file-saver": "^2.0.5",
"jsencrypt": "^3.2.0",
"lodash": "^4.17.21",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"qs": "^6.10.1",
"resize-detector": "^0.3.0",
"screenfull": "^5.1.0",
"vue": "^2.6.14",
"vue-i18n": "^8.24.5",
"vue-router": "^3.5.2",
"vuedraggable": "^2.24.3",
"vuex": "^3.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.13",
"@vue/cli-plugin-eslint": "^4.5.13",
"@vue/cli-plugin-router": "^4.5.13",
"@vue/cli-plugin-vuex": "^4.5.13",
"@vue/cli-service": "^4.5.13",
"@vue/eslint-config-prettier": "^6.0.0",
"body-parser": "^1.19.0",
"chalk": "^4.1.1",
"chokidar": "^3.5.2",
"compression-webpack-plugin": "^6.1.1",
"eslint": "^7.29.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.11.1",
"filemanager-webpack-plugin": "^3.1.1",
"image-webpack-loader": "^7.0.1",
"lint-staged": "^11.0.0",
"plop": "^2.7.4",
"prettier": "^2.3.1",
"raw-loader": "^4.0.2",
"sass": "~1.32.13",
"sass-loader": "^10.2.0",
"stylelint": "^13.13.1",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-recess-order": "^2.4.0",
"svg-sprite-loader": "^6.0.8",
"vab-templates": "^0.0.5",
"vue-eslint-parser": "^7.6.0",
"vue-template-compiler": "^2.6.14",
"webpackbar": "^5.0.0-3"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.(js,jsx,vue)": [
"vue-cli-service lint",
"git add"
]
},
"keywords": [],
"engines": {
"node": ">= 12.0.0",
"npm": ">=3.0.0"
}
}
module.exports = {
printWidth: 80,
tabWidth: 2,
useTabs: false,
semi: false,
singleQuote: true,
quoteProps: "as-needed",
jsxSingleQuote: false,
trailingComma: "es5",
bracketSpacing: true,
jsxBracketSameLine: false,
arrowParens: "always",
htmlWhitespaceSensitivity: "ignore",
vueIndentScriptAndStyle: true,
endOfLine: "lf",
};
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8" />
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
<meta content="webkit" name="renderer" />
<meta
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
name="viewport"
/>
<link href="<%= BASE_URL %>favicon.ico" rel="icon" />
<title><%= VUE_APP_TITLE %></title>
<meta content="<%= VUE_APP_AUTHOR %>" name="author" />
<link href="<%= BASE_URL %>static/css/loading.css" rel="stylesheet" />
</head>
<body>
<noscript></noscript>
<div id="app">
<div class="first-loading-wrp">
<div class="loading-wrp">
<span class="dot dot-spin">
<i></i>
<i></i>
<i></i>
<i></i>
</span>
</div>
<h1><%= VUE_APP_TITLE %></h1>
</div>
</div>
</body>
</html>
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<template>
<span v-if="theme.showTheme"></span>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'XmoTheme',
computed: {
...mapGetters({
theme: 'settings/theme',
}),
},
}
</script>
<style scoped></style>
/**
* @description 导出vue/cli配置,以下所有配置修改需要重启项目
*/
module.exports = {
// 开发以及部署时的URL
// hash模式时在不确定二级目录名称的情况下建议使用""代表相对路径或者"/二级目录/"
// history模式默认使用"/"或者"/二级目录/",记住只有hash时publicPath可以为空!!!
publicPath: '',
// 生产环境构建文件的目录名
outputDir: 'dist',
// 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录。
assetsDir: 'static',
// 开发环境每次保存时是否输出为eslint编译警告
lintOnSave: true,
// 进行编译的依赖
transpileDependencies: ['resize-detector'],
// 开发环境端口号
devPort: 10000,
// 需要自动注入并加载的模块
providePlugin: {},
// npm run build时是否自动生成7z压缩包
build7z: false,
// npm run build时是否生成gzip
buildGzip: false,
// npm run build时是否开启图片压缩,由于国内网路原因image-webpack-loader必须使用cnpm安装,如无法使用cnpm,请配置false
imageCompression: true,
}
/**
* @description 4个子配置,vue/cli配置|通用配置|主题配置|网络配置导出
* config中的部分配置由vue.config.js读取,本质是node,故不可使用window等浏览器对象
*/
const cli = require('./cli.config')
const setting = require('./setting.config')
const theme = require('./theme.config')
const network = require('./net.config')
module.exports = {
...cli,
...setting,
...theme,
...network,
}
/**
* @description 导出网络配置
**/
module.exports = {
// 默认的接口地址,开发环境和生产环境都会走/vab-mock-server
// 正式项目可以选择自己配置成需要的接口地址,如"https://api.xxx.com"
// 问号后边代表开发环境,冒号后边代表生产环境
baseURL:
process.env.NODE_ENV === 'development'
? '/xmo-mock-server'
: '/xmo-mock-server',
// 配后端数据的接收方式application/json;charset=UTF-8 或 application/x-www-form-urlencoded;charset=UTF-8
contentType: 'application/json;charset=UTF-8',
// 最长请求时间
requestTimeout: 10000,
// 操作正常code,支持String、Array、int多种类型
successCode: [200, 0, '200', '0'],
// 数据状态的字段名称
statusName: 'code',
// 状态信息的字段名称
messageName: 'msg',
}
/**
* @description 导出通用配置
*/
module.exports = {
// 标题,此项修改后需要重启项目!!! (包括初次加载雪花屏的标题 页面的标题 浏览器的标题)
title: 'Exam UI',
// 标题分隔符
titleSeparator: ' - ',
// 标题是否反转
// 如果为false: "page - title"
// 如果为ture : "title - page"
titleReverse: false,
// 简写
abbreviation: 'exam-ui',
// pro版本copyright可随意修改
copyright: 'xmo<hsq_bobble@126.com>',
// 缓存路由的最大数量
keepAliveMaxNum: 20,
// 路由模式,可选值为 history 或 hash
routerMode: 'hash',
// 不经过token校验的路由,白名单路由建议配置到与login页面同级,如果需要放行带传参的页面,请使用query传参,配置时只配置path即可
routesWhiteList: ['/login', '/register', '/callback', '/404', '/403'],
// 加载时显示文字
loadingText: '正在加载中...',
// token名称
tokenName: 'token',
// token在localStorage、sessionStorage、cookie存储的key的名称
tokenTableName: 'exam-token',
// token存储位置localStorage sessionStorage cookie
storage: 'localStorage',
// token失效回退到登录页时是否记录本次的路由(是否记录当前tab页)
recordRoute: true,
// 是否开启logo,不显示时设置false,请填写src/icon路径下的图标名称
// 如需使用内置RemixIcon图标,请自行去logo组件切换注释代码(内置svg雪碧图较大,对性能有一定影响)
logo: 'vuejs-fill',
// 语言类型zh、en
i18n: 'zh',
// 消息框消失时间
messageDuration: 3000,
// 在哪些环境下显示高亮错误 ['development', 'production']
errorLog: 'development',
// 是否开启登录拦截
loginInterception: true,
// 是否开启登录RSA加密
loginRSA: false,
// intelligence(前端导出路由)和all(后端导出路由)两种方式
authentication: 'intelligence',
// 是否支持游客模式,支持情况下,访问白名单,可查看所有asyncRoutes
supportVisit: false,
// 是否开启roles字段进行角色权限控制(如果是all模式后端完全处理角色并进行json组装,可设置false不处理路由中的roles字段)
rolesControl: true,
// vertical column comprehensive common布局时是否只保持一个子菜单的展开
uniqueOpened: false,
// vertical column comprehensive common布局时默认展开的菜单path,使用逗号隔开建议只展开一个
defaultOpeneds: [],
// 需要加loading层的请求,防止重复提交
debounce: ['doEdit'],
// 分栏布局和综合布局时,是否点击一级菜单默认开启二级菜单(默认第一个,可通过redirect自定义)
openFirstMenu: true,
}
/**
* @description 导出主题配置,注意事项:此配置下的项修改后需清理浏览器缓存!!!
*/
module.exports = {
// 布局种类:横向布局horizontal、纵向布局vertical、分栏布局column、综合布局comprehensive、常规布局common
layout: 'column',
// 主题名称:默认default、海洋之心ocean、绿荫草场green、碰触纯白white
themeName: 'default',
// 分栏风格(仅针对分栏布局column时生效):横向风格horizontal、纵向风格vertical、卡片风格card、箭头风格arrow
columnStyle: 'vertical',
// 是否固定头部固定
fixedHeader: true,
// 是否开启顶部进度条
showProgressBar: true,
// 是否开启标签页
showTabs: true,
// 显示标签页时标签页样式:卡片风格card、灵动风格smart、圆滑风格smooth
tabsBarStyle: 'card',
// 是否标签页图标
showTabsBarIcon: true,
// 是否开启语言选择组件
showLanguage: true,
// 是否开启刷新组件
showRefresh: true,
// 是否开启搜索组件
showSearch: true,
// 是否开启主题组件
showTheme: true,
// 是否开启通知组件
showNotice: true,
// 是否开启全屏组件
showFullScreen: true,
// 是否开启右侧悬浮窗
showThemeSetting: true,
//纵向布局、常规布局、综合布局时是否默认收起左侧菜单(不支持分栏布局、横向布局)
collapse: false,
}
import Vue from 'vue'
import App from './App'
import store from './store'
import router from './router'
import '@/icon'
// 全局样式
import './styles/main.scss'
// 加载主题
const Themes = require.context('./styles/themes', false, /\.scss$/)
Themes.keys().map(Themes)
// 加载插件
const Plugins = require.context('./plugins', true, /\.js$/)
Plugins.keys().map(Plugins)
// 加载组件
const Components = require.context('./components', true, /\.vue$/)
Components.keys()
.map(Components)
.forEach((item) => {
if (item.default.name) {
Vue.component(item.default.name, item.default)
}
})
// 生产环境注释掉下方代码
import { baseURL } from './config'
import { isExternal } from '@/utils/validate'
if (process.env.NODE_ENV === 'production' && !isExternal(baseURL)) {
const { mockXHR } = require('@/utils/mock')
mockXHR()
}
// 生产环境注释掉上方代码
Vue.config.productionTip = false
new Vue({
el: '#app',
store,
router,
render: (h) => h(App),
})
/**
* @description 导入所有 vuex 模块,自动加入namespaced:true,用于解决vuex命名冲突,请勿修改。
*/
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const modules = {}
const files = require.context('./modules', false, /\.js$/)
files.keys().forEach((key) => {
modules[key.replace(/(modules|\/|\.|js)/g, '')] = {
...files(key).default,
namespaced: true,
}
})
const store = new Vuex.Store({
modules,
})
export default store
/**
* @description 异常捕获的状态拦截,请勿修改
*/
const state = () => ({
errorLogs: [],
})
const getters = {
errorLogs: (state) => state.errorLogs,
}
const mutations = {
addErrorLog(state, errorLog) {
state.errorLogs.push(errorLog)
},
clearErrorLog: (state) => {
state.errorLogs.splice(0)
},
}
const actions = {
addErrorLog({ commit }, errorLog) {
commit('addErrorLog', errorLog)
},
clearErrorLog({ commit }) {
commit('clearErrorLog')
},
}
export default { state, getters, mutations, actions }
/**
* @description 所有全局配置的状态管理,如无必要请勿修改
*/
import { isJson } from '@/utils/validate'
import {
collapse as _collapse,
columnStyle,
fixedHeader,
i18n,
layout,
logo,
showFullScreen,
showLanguage,
showNotice,
showProgressBar,
showRefresh,
showSearch,
showTabs,
showTabsBarIcon,
showTheme,
showThemeSetting,
tabsBarStyle,
themeName,
title,
} from '@/config'
const defaultTheme = {
layout,
themeName,
columnStyle,
fixedHeader,
showProgressBar,
showTabs,
tabsBarStyle,
showTabsBarIcon,
showLanguage,
showRefresh,
showSearch,
showTheme,
showNotice,
showFullScreen,
showThemeSetting,
}
const getLocalStorage = (key) => {
const value = localStorage.getItem(key)
if (isJson(value)) {
return JSON.parse(value)
} else {
return false
}
}
const { collapse } = getLocalStorage('collapse')
const { language } = getLocalStorage('language')
const state = () => ({
logo,
title,
device: 'desktop',
collapse: collapse || _collapse,
language: language || i18n,
theme: getLocalStorage('theme') || { ...defaultTheme },
extra: { first: '' },
})
const getters = {
logo: (state) => state.logo,
title: (state) => state.title,
device: (state) => state.device,
collapse: (state) => state.collapse,
language: (state) => state.language,
theme: (state) => state.theme,
extra: (state) => state.extra,
}
const mutations = {
openSideBar(state) {
state.collapse = false
},
foldSideBar(state) {
state.collapse = true
},
toggleDevice(state, device) {
state.device = device
},
toggleCollapse(state) {
state.collapse = !state.collapse
localStorage.setItem('collapse', `{"collapse":${state.collapse}}`)
},
changeLanguage(state, language) {
state.language = language
localStorage.setItem('language', `{"language":"${language}"}`)
},
saveTheme(state) {
localStorage.setItem('theme', JSON.stringify(state.theme))
},
resetTheme(state) {
state.theme = { ...defaultTheme }
localStorage.removeItem('theme')
document.getElementsByTagName(
'body'
)[0].className = `xmo-theme-${state.theme.themeName}`
},
}
const actions = {
openSideBar({ commit }) {
commit('openSideBar')
},
foldSideBar({ commit }) {
commit('foldSideBar')
},
toggleDevice({ commit }, device) {
commit('toggleDevice', device)
},
toggleCollapse({ commit }) {
commit('toggleCollapse')
},
changeLanguage: ({ commit }, language) => {
commit('changeLanguage', language)
},
saveTheme({ commit }) {
commit('saveTheme')
},
resetTheme({ commit }) {
commit('resetTheme')
},
}
export default { state, getters, mutations, actions }
/**
* @description 导入所有 controller 模块,浏览器环境中自动输出controller文件夹下Mock接口,请勿修改。
*/
import Mock from 'mockjs'
import { paramObj } from '@/utils/index'
const files = require.context('../../mock/controller', true, /\.js$/)
const mocks = files.keys().flatMap(files)
export function mockXHR() {
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
Mock.XHR.prototype.send = function () {
if (this.custom.xhr) {
this.custom.xhr.withCredentials = this.withCredentials || false
if (this.responseType) {
this.custom.xhr.responseType = this.responseType
}
}
if (this.custom.requestHeaders)
this.custom.options.headers = this.custom.requestHeaders
this.proxy_send(...arguments)
}
function XHRHttpRequest(respond) {
return function (options) {
let result
if (respond instanceof Function) {
const { body, type, url, headers } = options
result = respond({
method: type,
body: JSON.parse(body),
query: paramObj(url),
headers: headers,
})
} else {
result = respond
}
return Mock.mock(result)
}
}
mocks.forEach((item) => {
Mock.mock(
new RegExp(item.url),
item.type || 'get',
XHRHttpRequest(item.response)
)
})
}
/**
* @description 判读是否为外链
* @param path
* @returns {boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:|\/\/)/.test(path)
}
/**
* @description 校验密码是否小于6位
* @param value
* @returns {boolean}
*/
export function isPassword(value) {
return value.length >= 6
}
/**
* @description 判断是否为数字
* @param value
* @returns {boolean}
*/
export function isNumber(value) {
const reg = /^[0-9]*$/
return reg.test(value)
}
/**
* @description 判断是否是名称
* @param value
* @returns {boolean}
*/
export function isName(value) {
const reg = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/
return reg.test(value)
}
/**
* @description 判断是否为IP
* @param ip
* @returns {boolean}
*/
export function isIP(ip) {
const reg =
/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
return reg.test(ip)
}
/**
* @description 判断是否是传统网站
* @param url
* @returns {boolean}
*/
export function isUrl(url) {
const reg =
/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return reg.test(url)
}
/**
* @description 判断是否是小写字母
* @param value
* @returns {boolean}
*/
export function isLowerCase(value) {
const reg = /^[a-z]+$/
return reg.test(value)
}
/**
* @description 判断是否是大写字母
* @param value
* @returns {boolean}
*/
export function isUpperCase(value) {
const reg = /^[A-Z]+$/
return reg.test(value)
}
/**
* @description 判断是否是大写字母开头
* @param value
* @returns {boolean}
*/
export function isAlphabets(value) {
const reg = /^[A-Za-z]+$/
return reg.test(value)
}
/**
* @description 判断是否是字符串
* @param value
* @returns {boolean}
*/
export function isString(value) {
return typeof value === 'string' || value instanceof String
}
/**
* @description 判断是否是数组
* @param arg
*/
export function isArray(arg) {
if (typeof Array.isArray === 'undefined') {
return Object.prototype.toString.call(arg) === '[object Array]'
}
return Array.isArray(arg)
}
/**
* @description 判断是否是端口号
* @param value
* @returns {boolean}
*/
export function isPort(value) {
const reg =
/^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/
return reg.test(value)
}
/**
* @description 判断是否是手机号
* @param value
* @returns {boolean}
*/
export function isPhone(value) {
const reg = /^1\d{10}$/
return reg.test(value)
}
/**
* @description 判断是否是身份证号(第二代)
* @param value
* @returns {boolean}
*/
export function isIdCard(value) {
const reg =
/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
return reg.test(value)
}
/**
* @description 判断是否是邮箱
* @param value
* @returns {boolean}
*/
export function isEmail(value) {
const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/
return reg.test(value)
}
/**
* @description 判断是否中文
* @param value
* @returns {boolean}
*/
export function isChina(value) {
const reg = /^[\u4E00-\u9FA5]{2,4}$/
return reg.test(value)
}
/**
* @description 判断是否为空
* @param value
* @returns {boolean}
*/
export function isBlank(value) {
return (
value === null ||
false ||
value === '' ||
value.trim() === '' ||
value.toLocaleLowerCase().trim() === 'null'
)
}
/**
* @description 判断是否为固话
* @param value
* @returns {boolean}
*/
export function isTel(value) {
const reg =
/^(400|800)([0-9\\-]{7,10})|(([0-9]{4}|[0-9]{3})([- ])?)?([0-9]{7,8})(([- 转])*([0-9]{1,4}))?$/
return reg.test(value)
}
/**
* @description 判断是否为数字且最多两位小数
* @param value
* @returns {boolean}
*/
export function isNum(value) {
const reg = /^\d+(\.\d{1,2})?$/
return reg.test(value)
}
/**
* @description 判断是否为json
* @param value
* @returns {boolean}
*/
export function isJson(value) {
if (typeof value === 'string') {
const obj = JSON.parse(value)
return !!(typeof obj === 'object' && obj)
}
return false
}
/**
* config配置
*/
const path = require('path')
const {
publicPath,
assetsDir,
outputDir,
lintOnSave,
transpileDependencies,
title,
abbreviation,
devPort,
providePlugin,
build7z,
buildGzip,
imageCompression,
} = require('./src/config')
const webpackBanner = ' build : EXAM-UI'
const webpackBarName = 'Exam UI'
const { version, author } = require('./package.json')
const Webpack = require('webpack')
const WebpackBar = require('webpackbar')
const FileManagerPlugin = require('filemanager-webpack-plugin')
const dayjs = require('dayjs')
const dateTime = dayjs().format('YYYY-M-D HH:mm:ss')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ['html', 'js', 'css', 'svg']
process.env.VUE_APP_TITLE = title
process.env.VUE_APP_AUTHOR = author
process.env.VUE_APP_UPDATE_TIME = dateTime
process.env.VUE_APP_VERSION = version
const resolve = (dir) => {
return path.join(__dirname, dir)
}
module.exports = {
publicPath,
assetsDir,
outputDir,
lintOnSave,
transpileDependencies,
devServer: {
hot: true,
port: devPort,
open: true,
noInfo: false,
overlay: {
warnings: true,
errors: true,
},
// 注释掉的地方是前端配置代理访问后端的示例
// baseURL必须为/xxx,而不是后端服务器,请先了解代理逻辑,再设置前端代理
// !!!一定要注意!!!
// 1.这里配置了跨域及代理只针对开发环境生效
// 2.不建议你在前端配置跨域,建议你后端配置Allow-Origin,Method,Headers,放行token字段,一步到位
// 3.后端配置了跨域,就不需要前端再配置,会发生Origin冲突
// proxy: {
// [baseURL]: {
// target: `http://你的后端接口地址`,
// ws: true,
// changeOrigin: true,
// pathRewrite: {
// ['^' + baseURL]: '',
// },
// },
// },
after: require('./mock'),
},
configureWebpack() {
return {
resolve: {
alias: {
'~': resolve('.'),
'@': resolve('src'),
},
},
plugins: [
new Webpack.ProvidePlugin(providePlugin),
new WebpackBar({
name: webpackBarName,
}),
],
}
},
chainWebpack(config) {
config.resolve.symlinks(true)
config.module.rule('svg').exclude.add(resolve('src/icon'))
config.module
.rule('vabIcon')
.test(/\.svg$/)
.include.add(resolve('src/icon'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({ symbolId: 'xmo-icon-[name]' })
config.when(process.env.NODE_ENV === 'development', (config) => {
config.devtool('source-map')
})
config.when(process.env.NODE_ENV === 'production', (config) => {
config.performance.set('hints', false)
config.devtool('none')
config.optimization.splitChunks({
automaticNameDelimiter: '-',
chunks: 'all',
cacheGroups: {
chunk: {
name: 'xmo-chunk',
test: /[\\/]node_modules[\\/]/,
minSize: 131072,
maxSize: 524288,
chunks: 'async',
minChunks: 2,
priority: 10,
},
vue: {
name: 'vue',
test: /[\\/]node_modules[\\/](vue(.*)|core-js)[\\/]/,
chunks: 'initial',
priority: 20,
},
elementUI: {
name: 'element-ui',
test: /[\\/]node_modules[\\/]element-ui(.*)[\\/]/,
priority: 30,
},
extra: {
name: 'vab-extra',
test: resolve('src/extra'),
priority: 40,
},
// 根据使用模块抽取公共代码
// echarts: {
// name: 'echarts',
// test: /[\\/]node_modules[\\/](echarts|zrender|tslib)[\\/]/,
// priority: 50,
// },
},
})
config
.plugin('banner')
.use(Webpack.BannerPlugin, [`${webpackBanner}${dateTime}`])
if (imageCompression)
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
bypassOnDebug: true,
})
.end()
if (buildGzip)
config.plugin('compression').use(CompressionWebpackPlugin, [
{
filename: '[path][base].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' + productionGzipExtensions.join('|') + ')$'
),
threshold: 8192,
minRatio: 0.8,
},
])
if (build7z)
config.plugin('fileManager').use(FileManagerPlugin, [
{
events: {
onEnd: {
archive: [
{
source: `./${outputDir}`,
destination: `./${outputDir}/${abbreviation}_${dayjs().unix()}.zip`,
},
],
},
},
},
])
})
},
runtimeCompiler: true,
productionSourceMap: false,
css: {
requireModuleExtension: true,
sourceMap: true,
loaderOptions: {
scss: {
additionalData(content, loaderContext) {
const { resourcePath, rootContext } = loaderContext
const relativePath = path.relative(rootContext, resourcePath)
if (
relativePath.replace(/\\/g, '/') !==
'src/styles/variables/variables.scss'
)
return '@import "~@/styles/variables/variables.scss";' + content
return content
},
},
},
},
}
const webpackConfig = require("@vue/cli-service/webpack.config.js");
module.exports = webpackConfig;
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册