node版本升级

main
wangxiaoshuang 2025-04-18 17:33:21 +08:00
parent 576f046f33
commit 35b232830a
37 changed files with 583 additions and 15470 deletions

View File

@ -1,12 +0,0 @@
{
"presets": [["@babel/preset-env", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}

View File

@ -8,7 +8,7 @@ module.exports = {
'@vue/standard' '@vue/standard'
], ],
parserOptions: { parserOptions: {
parser: 'babel-eslint' parser: '@babel/eslint-parser'
}, },
rules: { rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',

View File

@ -1,11 +1,4 @@
const plugins = ['@vue/babel-plugin-transform-vue-jsx']
// 生产环境移除console
if (process.env.NODE_ENV === 'production') {
plugins.push('transform-remove-console')
}
module.exports = { module.exports = {
plugins: plugins,
presets: [ presets: [
'@vue/cli-plugin-babel/preset' '@vue/cli-plugin-babel/preset'
] ]

14561
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{ {
"name": "vue-template", "name": "extensive-imaging",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -8,41 +8,33 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"ali-oss": "^6.17.1",
"axios": "^0.19.2", "axios": "^0.19.2",
"core-js": "^3.6.4", "core-js": "^3.8.3",
"dayjs": "^1.10.3", "element-ui": "^2.15.14",
"element-ui": "^2.13.0",
"js-cookie": "^2.2.1",
"jsencrypt": "^3.0.0-rc.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"v-viewer": "^1.6.4", "v-viewer": "^1.7.4",
"vue": "^2.6.11", "vue": "^2.6.14",
"vue-cropper": "^0.5.5", "vue-i18n": "^8.28.2",
"vue-i18n": "^8.15.4", "vue-router": "^3.0.6",
"vue-router": "^3.1.5", "vuex": "^3.1.0"
"vuex": "^3.1.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-env": "^7.8.4", "@babel/core": "^7.12.16",
"@vue/cli-plugin-babel": "^4.2.0", "@babel/eslint-parser": "^7.27.0",
"@vue/cli-plugin-eslint": "^4.2.0", "@babel/preset-env": "^7.26.9",
"@vue/cli-plugin-pwa": "^4.2.0", "@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-service": "^4.2.0", "@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"@vue/eslint-config-standard": "^5.1.0", "@vue/eslint-config-standard": "^5.1.0",
"babel-eslint": "^10.0.3", "eslint": "^7.32.0",
"babel-plugin-component": "^1.1.1", "eslint-plugin-import": "^2.31.0",
"babel-plugin-transform-remove-console": "^6.9.4", "eslint-plugin-node": "^11.1.0",
"compression-webpack-plugin": "^4.0.0", "eslint-plugin-promise": "^7.2.1",
"eslint": "^6.7.2", "eslint-plugin-standard": "^5.0.0",
"eslint-plugin-import": "^2.20.1", "eslint-plugin-vue": "^8.0.3",
"eslint-plugin-node": "^11.0.0", "sass": "~1.32.6",
"eslint-plugin-promise": "^4.2.1", "sass-loader": "^10.5.2",
"eslint-plugin-standard": "^4.0.0", "svg-sprite-loader": "^4.1.3",
"eslint-plugin-vue": "^6.1.2", "vue-template-compiler": "^2.6.14"
"node-sass": "^4.12.0",
"sass-loader": "^8.0.2",
"svg-sprite-loader": "^4.2.1",
"vue-template-compiler": "^2.6.11"
} }
} }

Binary file not shown.

Binary file not shown.

View File

@ -1,28 +0,0 @@
/**
* 操作权限处理
* Copyright (c) 2019 ruoyi
*/
import store from '@/store'
export default {
inserted (el, binding, vnode) {
const { value } = binding
const allPermission = '*:*:*'
const permissions = store.getters && store.getters.userInfo.permissions
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
const hasPermissions = permissions.some(permission => {
return allPermission === permission || permissionFlag.includes(permission)
})
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error('请设置操作权限标签值')
}
}
}

View File

@ -1,28 +0,0 @@
/**
* 角色权限处理
* Copyright (c) 2019 ruoyi
*/
import store from '@/store'
export default {
inserted (el, binding, vnode) {
const { value } = binding
const superAdmin = 'admin'
const roles = store.getters && store.getters.userInfo.user.roles.map(item => item.roleName)
if (value && value instanceof Array && value.length > 0) {
const roleFlag = value
const hasRole = roles.some(role => {
return superAdmin === role || roleFlag.includes(role)
})
if (!hasRole) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error('请设置角色权限标签值"')
}
}
}

View File

@ -1,15 +0,0 @@
import hasRole from './hasRole'
import hasPermi from './hasPermi'
const install = function (Vue) {
Vue.directive('hasRole', hasRole)
Vue.directive('hasPermi', hasPermi)
}
if (window.Vue) {
window.hasRole = hasRole
window.hasPermi = hasPermi
Vue.use(install); // eslint-disable-line
}
export default install

View File

@ -5,7 +5,7 @@ const en = {
service: { service: {
menuName: 'Services', menuName: 'Services',
subMenu1: 'Imaging CRO', subMenu1: 'Imaging CRO',
subMenu2: '3-D Post-Processing Service', subMenu2: '3-D Post-Processing',
msg1: 'Extensive Imaging applies image processing and artificial intelligence technologies to imaging medicine, and provides imaging data analysis and diagnostic services closely related to clinical applications for physicians and patients, adhering to internationally recognized 3D imaging clinical diagnostic standards. Additionally, Extensive Imaging offers services such as medical imaging big data transmission, storage, sharing, statistics, and quality control for clients leveraging the advantages of cloud platforms.' msg1: 'Extensive Imaging applies image processing and artificial intelligence technologies to imaging medicine, and provides imaging data analysis and diagnostic services closely related to clinical applications for physicians and patients, adhering to internationally recognized 3D imaging clinical diagnostic standards. Additionally, Extensive Imaging offers services such as medical imaging big data transmission, storage, sharing, statistics, and quality control for clients leveraging the advantages of cloud platforms.'
}, },
imagingService: { imagingService: {
@ -46,13 +46,13 @@ const en = {
msg22: 'IT & Platform Development: Extensive healthcare IT experience in medical informatics & clinical trial systems.' msg22: 'IT & Platform Development: Extensive healthcare IT experience in medical informatics & clinical trial systems.'
}, },
processingService: { processingService: {
tab1: '3-D Post-Processing Service', tab1: '3-D Post-Processing',
tab2: 'Services', tab2: 'Services',
tab3: 'Workflow', tab3: 'Workflow',
tab4: 'Cases', tab4: 'Cases',
tab5: '3D Reconstructions', tab5: '3D Reconstructions',
tab6: 'Image-Based Surgical Planning', tab6: 'Image-Based Surgical Planning',
title1: '3-D Post-Processing Service', title1: '3-D Post-Processing',
title2: 'Benefits to Clients', title2: 'Benefits to Clients',
title3: 'Regulatory Compliance', title3: 'Regulatory Compliance',
title4: 'Cardiovascular', title4: 'Cardiovascular',
@ -83,7 +83,7 @@ const en = {
msg7: `Data transmission on our HIPPA-compliant network is encrypted with sophisticated algorithms. Image data msg7: `Data transmission on our HIPPA-compliant network is encrypted with sophisticated algorithms. Image data
is anonymized prior to transmission from the hospital, then stored at our data center with 24/7 security is anonymized prior to transmission from the hospital, then stored at our data center with 24/7 security
and back-up. Our radiologists process the images without knowledge of patients' identities.`, and back-up. Our radiologists process the images without knowledge of patients' identities.`,
msg8: `Everest Digital Medicine offers a wide range of 3-D post-processing services for CT and MRI studies. msg8: `Everest Digital Medicine offers a wide range of 3-D Post-Processings for CT and MRI studies.
These services encompass reconstructions, quantitative analysis, and image-based surgical planning.`, These services encompass reconstructions, quantitative analysis, and image-based surgical planning.`,
msg9: `Our radiologists and imaging specialists work with clients to design customized protocols based on their msg9: `Our radiologists and imaging specialists work with clients to design customized protocols based on their
clinical requirements.`, clinical requirements.`,

View File

@ -1,27 +1,39 @@
import Vue from 'vue' import Vue from 'vue'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import store from './store/index' // import store from './store/index'
import ElementUI from 'element-ui' import ElementUI from 'element-ui'
import './static/styles/theme-blue/index.css' // 浅绿色主题
import i18n from '@/lang' // 国际化 import i18n from '@/lang' // 国际化
import { btnPermission, includePermission } from './utils/permission' // import { btnPermission, includePermission } from './utils/permission'
import Pagination from '@/components/Pagination' import Pagination from '@/components/Pagination'
import permission from './directive/permission' // import permission from './directive/permission'
import { baseImgUrl } from './utils/config' import { baseImgUrl } from './utils/config'
import './permission' // permission control
import 'nprogress/nprogress.css' // progress bar style import 'nprogress/nprogress.css' // progress bar style
import './static/styles/element-variables.scss' // 自定义主题色
import './static/styles/index.scss' import './static/styles/index.scss'
import '@/static/icons' import '@/static/icons'
import { OSSclient } from './utils/oss' // import { OSSclient } from './utils/oss'
// use // use
import Viewer from 'v-viewer' // 引入js import Viewer from 'v-viewer' // 引入js
import 'viewerjs/dist/viewer.css' import './static/styles/viewer.css'
Viewer.setDefaults({
// navbar: true, //底部缩略图
toolbar: {
zoomIn: true,
zoomOut: true,
reset: true,
prev: true,
next: true,
rotateLeft: true,
rotateRight: true,
flipHorizontal: true,
flipVertical: true,
}
})
Vue.use(Viewer) // 默认配置写法 Vue.use(Viewer) // 默认配置写法
Vue.use(ElementUI, { Vue.use(ElementUI, {
@ -29,18 +41,18 @@ Vue.use(ElementUI, {
}) })
Vue.config.productionTip = false Vue.config.productionTip = false
Vue.prototype.baseImgUrl = baseImgUrl Vue.prototype.baseImgUrl = baseImgUrl
Vue.prototype.checkBtnPermission = btnPermission // 检查方法权限 // Vue.prototype.checkBtnPermission = btnPermission // 检查方法权限
Vue.prototype.includePermission = includePermission // 检查方法列表权限 // Vue.prototype.includePermission = includePermission // 检查方法列表权限
Vue.prototype.OSSclient = OSSclient // Vue.prototype.OSSclient = OSSclient
// 全局组件挂载 // 全局组件挂载
Vue.component('Pagination', Pagination) Vue.component('Pagination', Pagination)
Vue.use(permission) // Vue.use(permission)
new Vue({ new Vue({
router, router,
store, // store,
i18n, i18n,
render: h => h(App) render: h => h(App)
}).$mount('#app') }).$mount('#app')

View File

@ -1,73 +0,0 @@
import router from './router'
import store from './store'
// import { Message } from 'element-ui'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/register']
router.beforeEach((to, from, next) => {
NProgress.start()
// 前台
if (to.fullPath.indexOf('/front') !== -1) {
next()
} else { // 后台
if (getToken()) {
/* has token */
if (to.path === '/login') {
next({ name: store.state.permission.allRouterNames[0] })
NProgress.done()
} else {
if (!store.getters.userInfo) {
console.log('qqqqq')
// 判断当前用户是否已拉取完user_info信息
// store.dispatch('GetInfo').then(res => {
// store.dispatch('GenerateRoutes').then(accessRoutes => {
// router.addRoutes(accessRoutes) // 动态添加可访问路由表
// if (accessRoutes.length) { // 默认返回第一个
// // next({
// // name: accessRoutes[0].children[0].name,
// // query: to.query,
// // replace: true
// // })
// next({
// ...to,
// replace: true
// })
// } else {
// next({
// path: '/404',
// replace: true
// })
// }
// })
// }).catch(err => {
// store.dispatch('FedLogOut').then(() => {
// Message.error(err)
// next({ path: '/' })
// })
// })
} else {
next()
}
}
} else {
// 没有token
console.log(to.path)
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
} else {
next() // 否则全部重定向到登录页
NProgress.done()
}
}
}
})
router.afterEach(() => {
NProgress.done()
})

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 797 KiB

View File

@ -1,7 +0,0 @@
/* 改变主题色变量 */
$--color-primary: #302f6d;
/* 改变 icon 字体路径变量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";

View File

@ -0,0 +1,29 @@
.header{
background-color: #07c4a8;
}
.login-wrap{
background: rgba(56, 157, 170, 0.82);;
}
.plugins-tips{
background: #f2f2f2;
}
.plugins-tips a{
color: #428bca;
}
.el-upload--text em {
color: #428bca;
}
.pure-button{
background: #428bca;
}
.pagination > .active > a, .pagination > .active > a:hover, .pagination > .active > a:focus, .pagination > .active > span, .pagination > .active > span:hover, .pagination > .active > span:focus {
background-color: #428bca !important;
border-color: #428bca !important;
}
.tags-li.active {
border: 1px solid #428bca;
background-color: #428bca;
}
.collapse-btn:hover{
background: #428bca;
}

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,460 @@
/*!
* Viewer.js v1.11.7
* https://fengyuanchen.github.io/viewerjs
*
* Copyright 2015-present Chen Fengyuan
* Released under the MIT license
*
* Date: 2024-11-24T04:32:14.526Z
*/
.viewer-zoom-in::before,
.viewer-zoom-out::before,
.viewer-one-to-one::before,
.viewer-reset::before,
.viewer-prev::before,
.viewer-play::before,
.viewer-next::before,
.viewer-rotate-left::before,
.viewer-rotate-right::before,
.viewer-flip-horizontal::before,
.viewer-flip-vertical::before,
.viewer-fullscreen::before,
.viewer-fullscreen-exit::before,
.viewer-close::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 viewBox%3D%220 0 560 40%22%3E%3Cpath fill%3D%22%23fff%22 d%3D%22M49.6 17.9h20.2v3.9H49.6zm123.1 2 10.9-11 2.7 2.8-8.2 8.2 8.2 8.2-2.7 2.7-10.9-10.9zm94 0-10.8-11-2.7 2.8 8.1 8.2-8.1 8.2 2.7 2.7 10.8-10.9zM212 9.3l20.1 10.6L212 30.5V9.3zm161.5 4.6-7.2 6 7.2 5.9v-4h12.4v4l7.3-5.9-7.3-6v4h-12.4v-4zm40.2 12.3 5.9 7.2 5.9-7.2h-4V13.6h4l-5.9-7.3-5.9 7.3h4v12.6h-4zm35.9-16.5h6.3v2h-4.3V16h-2V9.7Zm14 0h6.2V16h-2v-4.3h-4.2v-2Zm6.2 14V30h-6.2v-2h4.2v-4.3h2Zm-14 6.3h-6.2v-6.3h2v4.4h4.3v2Zm-438 .1v-8.3H9.6v-3.9h8.2V9.7h3.9v8.2h8.1v3.9h-8.1v8.3h-3.9zM93.6 9.7h-5.8v3.9h2V30h3.8V9.7zm16.1 0h-5.8v3.9h1.9V30h3.9V9.7zm-11.9 4.1h3.9v3.9h-3.9zm0 8.2h3.9v3.9h-3.9zm244.6-11.7 7.2 5.9-7.2 6v-3.6c-5.4-.4-7.8.8-8.7 2.8-.8 1.7-1.8 4.9 2.8 8.2-6.3-2-7.5-6.9-6-11.3 1.6-4.4 8-5 11.9-4.9v-3.1Zm147.2 13.4h6.3V30h-2v-4.3h-4.3v-2zm14 6.3v-6.3h6.2v2h-4.3V30h-1.9zm6.2-14h-6.2V9.7h1.9V14h4.3v2zm-13.9 0h-6.3v-2h4.3V9.7h2V16zm33.3 12.5 8.6-8.6-8.6-8.7 1.9-1.9 8.6 8.7 8.6-8.7 1.9 1.9-8.6 8.7 8.6 8.6-1.9 2-8.6-8.7-8.6 8.7-1.9-2zM297 10.3l-7.1 5.9 7.2 6v-3.6c5.3-.4 7.7.8 8.7 2.8.8 1.7 1.7 4.9-2.9 8.2 6.3-2 7.5-6.9 6-11.3-1.6-4.4-7.9-5-11.8-4.9v-3.1Zm-157.3-.6c2.3 0 4.4.7 6 2l2.5-3 1.9 9.2h-9.3l2.6-3.1a6.2 6.2 0 0 0-9.9 5.1c0 3.4 2.8 6.3 6.2 6.3 2.8 0 5.1-1.9 6-4.4h4c-1 4.7-5 8.3-10 8.3a10 10 0 0 1-10-10.2 10 10 0 0 1 10-10.2Z%22%2F%3E%3C%2Fsvg%3E");
background-repeat: no-repeat;
background-size: 280px;
color: transparent;
display: block;
font-size: 0;
height: 20px;
line-height: 0;
width: 20px;
}
.viewer-zoom-in::before {
background-position: 0 0;
content: 'Zoom In';
}
.viewer-zoom-out::before {
background-position: -20px 0;
content: 'Zoom Out';
}
.viewer-one-to-one::before {
background-position: -40px 0;
content: 'One to One';
}
.viewer-reset::before {
background-position: -60px 0;
content: 'Reset';
}
.viewer-prev::before {
background-position: -80px 0;
content: 'Previous';
}
.viewer-play::before {
background-position: -100px 0;
content: 'Play';
}
.viewer-next::before {
background-position: -120px 0;
content: 'Next';
}
.viewer-rotate-left::before {
background-position: -140px 0;
content: 'Rotate Left';
}
.viewer-rotate-right::before {
background-position: -160px 0;
content: 'Rotate Right';
}
.viewer-flip-horizontal::before {
background-position: -180px 0;
content: 'Flip Horizontal';
}
.viewer-flip-vertical::before {
background-position: -200px 0;
content: 'Flip Vertical';
}
.viewer-fullscreen::before {
background-position: -220px 0;
content: 'Enter Full Screen';
}
.viewer-fullscreen-exit::before {
background-position: -240px 0;
content: 'Exit Full Screen';
}
.viewer-close::before {
background-position: -260px 0;
content: 'Close';
}
.viewer-container {
bottom: 0;
direction: ltr;
font-size: 0;
left: 0;
line-height: 0;
overflow: hidden;
position: absolute;
right: 0;
-webkit-tap-highlight-color: transparent;
top: 0;
-ms-touch-action: none;
touch-action: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.viewer-container::-moz-selection,
.viewer-container *::-moz-selection {
background-color: transparent;
}
.viewer-container::selection,
.viewer-container *::selection {
background-color: transparent;
}
.viewer-container:focus {
outline: 0;
}
.viewer-container img {
display: block;
height: auto;
max-height: none !important;
max-width: none !important;
min-height: 0 !important;
min-width: 0 !important;
width: 100%;
}
.viewer-canvas {
bottom: 0;
left: 0;
overflow: hidden;
position: absolute;
right: 0;
top: 0;
}
.viewer-canvas>img {
height: auto;
margin: 15px auto;
max-width: 90% !important;
width: auto;
}
.viewer-footer {
bottom: 0;
left: 0;
overflow: hidden;
position: absolute;
right: 0;
text-align: center;
}
.viewer-navbar {
background-color: rgba(0, 0, 0, 0.5);
overflow: hidden;
}
.viewer-list {
box-sizing: content-box;
height: 50px;
margin: 0;
overflow: hidden;
padding: 1px 0;
}
.viewer-list>li {
color: transparent;
cursor: pointer;
float: left;
font-size: 0;
height: 50px;
line-height: 0;
opacity: 0.5;
overflow: hidden;
transition: opacity 0.15s;
width: 30px;
}
.viewer-list>li:focus,
.viewer-list>li:hover {
opacity: 0.75;
}
.viewer-list>li:focus {
outline: 0;
}
.viewer-list>li+li {
margin-left: 1px;
}
.viewer-list>.viewer-loading {
position: relative;
}
.viewer-list>.viewer-loading::after {
border-width: 2px;
height: 20px;
margin-left: -10px;
margin-top: -10px;
width: 20px;
}
.viewer-list>.viewer-active,
.viewer-list>.viewer-active:focus,
.viewer-list>.viewer-active:hover {
opacity: 1;
}
.viewer-player {
background-color: #000;
bottom: 0;
cursor: none;
display: none;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}
.viewer-player>img {
left: 0;
position: absolute;
top: 0;
}
.viewer-toolbar>ul {
display: inline-block;
margin: 0 auto 5px;
overflow: hidden;
padding: 6px 3px;
}
.viewer-toolbar>ul>li {
background-color: rgba(0, 0, 0, 0.5);
border-radius: 50%;
cursor: pointer;
float: left;
height: 24px;
overflow: hidden;
transition: background-color 0.15s;
width: 24px;
}
.viewer-toolbar>ul>li:focus,
.viewer-toolbar>ul>li:hover {
background-color: rgba(0, 0, 0, 0.8);
}
.viewer-toolbar>ul>li:focus {
box-shadow: 0 0 3px #fff;
outline: 0;
position: relative;
z-index: 1;
}
.viewer-toolbar>ul>li::before {
margin: 2px;
}
.viewer-toolbar>ul>li+li {
margin-left: 1px;
}
.viewer-toolbar>ul>.viewer-small {
height: 18px;
margin-bottom: 3px;
margin-top: 3px;
width: 18px;
}
.viewer-toolbar>ul>.viewer-small::before {
margin: -1px;
}
.viewer-toolbar>ul>.viewer-large {
height: 30px;
margin-bottom: -3px;
margin-top: -3px;
width: 30px;
}
.viewer-toolbar>ul>.viewer-large::before {
margin: 5px;
}
.viewer-tooltip {
background-color: rgba(0, 0, 0, 0.8);
border-radius: 10px;
color: #fff;
display: none;
font-size: 12px;
height: 20px;
left: 50%;
line-height: 20px;
margin-left: -25px;
margin-top: -10px;
position: absolute;
text-align: center;
top: 50%;
width: 50px;
}
.viewer-title {
color: #ccc;
display: inline-block;
font-size: 12px;
line-height: 1.2;
margin: 5px 5%;
max-width: 90%;
min-height: 14px;
opacity: 0.8;
overflow: hidden;
text-overflow: ellipsis;
transition: opacity 0.15s;
white-space: nowrap;
}
.viewer-title:hover {
opacity: 1;
}
.viewer-button {
-webkit-app-region: no-drag;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 50%;
cursor: pointer;
height: 80px;
overflow: hidden;
position: absolute;
right: -40px;
top: -40px;
transition: background-color 0.15s;
width: 80px;
}
.viewer-button:focus,
.viewer-button:hover {
background-color: rgba(0, 0, 0, 0.8);
}
.viewer-button:focus {
box-shadow: 0 0 3px #fff;
outline: 0;
}
.viewer-button::before {
bottom: 15px;
left: 15px;
position: absolute;
}
.viewer-fixed {
position: fixed;
}
.viewer-open {
overflow: hidden;
}
.viewer-show {
display: block;
}
.viewer-hide {
display: none;
}
.viewer-backdrop {
background-color: rgba(0, 0, 0, 0.5);
}
.viewer-invisible {
visibility: hidden;
}
.viewer-move {
cursor: move;
cursor: grab;
}
.viewer-fade {
opacity: 0;
}
.viewer-in {
opacity: 1;
}
.viewer-transition {
transition: all 0.3s;
}
@keyframes viewer-spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.viewer-loading::after {
animation: viewer-spinner 1s linear infinite;
border: 4px solid rgba(255, 255, 255, 0.1);
border-left-color: rgba(255, 255, 255, 0.5);
border-radius: 50%;
content: '';
display: inline-block;
height: 40px;
left: 50%;
margin-left: -20px;
margin-top: -20px;
position: absolute;
top: 50%;
width: 40px;
z-index: 1;
}
@media (max-width: 767px) {
.viewer-hide-xs-down {
display: none;
}
}
@media (max-width: 991px) {
.viewer-hide-sm-down {
display: none;
}
}
@media (max-width: 1199px) {
.viewer-hide-md-down {
display: none;
}
}

View File

@ -1,11 +0,0 @@
const getters = {
sidebar: state => state.app.sidebar,
size: state => state.app.size,
device: state => state.app.device,
visitedViews: state => state.tagsView.visitedViews,
cachedViews: state => state.tagsView.cachedViews,
token: state => state.user.token,
userInfo: state => state.user.userInfo,
permission_routes: state => state.permission.menus
}
export default getters

View File

@ -1,19 +0,0 @@
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import tagsView from './modules/tagsView'
import settings from './modules/settings'
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app,
tagsView,
settings
},
getters
})
export default store

View File

@ -1,57 +0,0 @@
import Cookies from 'js-cookie'
const state = {
loading: false,
sidebar: {
opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
withoutAnimation: false
},
device: 'desktop',
size: 'medium'
}
const mutations = {
TOGGLE_SIDEBAR: state => {
state.sidebar.opened = !state.sidebar.opened
state.sidebar.withoutAnimation = false
if (state.sidebar.opened) {
Cookies.set('sidebarStatus', 1)
} else {
Cookies.set('sidebarStatus', 0)
}
},
CLOSE_SIDEBAR: (state, withoutAnimation) => {
Cookies.set('sidebarStatus', 0)
state.sidebar.opened = false
state.sidebar.withoutAnimation = withoutAnimation
},
TOGGLE_DEVICE: (state, device) => {
state.device = device
},
SET_SIZE: (state, size) => {
state.size = size
Cookies.set('size', size)
}
}
const actions = {
toggleSideBar ({ commit }) {
commit('TOGGLE_SIDEBAR')
},
closeSideBar ({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation)
},
toggleDevice ({ commit }, device) {
commit('TOGGLE_DEVICE', device)
},
setSize ({ commit }, size) {
commit('SET_SIZE', size)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@ -1,33 +0,0 @@
import variables from '@/static/styles/element-variables.scss'
import defaultSettings from '@/settings'
const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings
const state = {
theme: variables.theme,
showSettings: showSettings,
tagsView: tagsView,
fixedHeader: fixedHeader,
sidebarLogo: sidebarLogo
}
const mutations = {
CHANGE_SETTING: (state, { key, value }) => {
if (Object.prototype.hasOwnProperty.call(state, key)) {
state[key] = value
}
}
}
const actions = {
changeSetting ({ commit }, data) {
commit('CHANGE_SETTING', data)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@ -1,159 +0,0 @@
const state = {
visitedViews: [],
cachedViews: []
}
const mutations = {
ADD_VISITED_VIEW: (state, view) => {
if (state.visitedViews.some(v => v.path === view.path)) return
state.visitedViews.push(
Object.assign({}, view, {
title: view.meta.title || 'no-name'
})
)
},
ADD_CACHED_VIEW: (state, view) => {
if (state.cachedViews.includes(view.name)) return
if (!view.meta.noCache) {
state.cachedViews.push(view.name)
}
},
DEL_VISITED_VIEW: (state, view) => {
for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) {
state.visitedViews.splice(i, 1)
break
}
}
},
DEL_CACHED_VIEW: (state, view) => {
const index = state.cachedViews.indexOf(view.name)
index > -1 && state.cachedViews.splice(index, 1)
},
DEL_OTHERS_VISITED_VIEWS: (state, view) => {
state.visitedViews = state.visitedViews.filter(v => {
return v.meta.affix || v.path === view.path
})
},
DEL_OTHERS_CACHED_VIEWS: (state, view) => {
const index = state.cachedViews.indexOf(view.name)
if (index > -1) {
state.cachedViews = state.cachedViews.slice(index, index + 1)
} else {
state.cachedViews = []
}
},
DEL_ALL_VISITED_VIEWS: state => {
// keep affix tags
const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
state.visitedViews = affixTags
},
DEL_ALL_CACHED_VIEWS: state => {
state.cachedViews = []
},
UPDATE_VISITED_VIEW: (state, view) => {
for (let v of state.visitedViews) {
if (v.path === view.path) {
v = Object.assign(v, view)
break
}
}
}
}
const actions = {
addView ({ dispatch }, view) {
dispatch('addVisitedView', view)
dispatch('addCachedView', view)
},
addVisitedView ({ commit }, view) {
commit('ADD_VISITED_VIEW', view)
},
addCachedView ({ commit }, view) {
commit('ADD_CACHED_VIEW', view)
},
delView ({ dispatch, state }, view) {
return new Promise(resolve => {
dispatch('delVisitedView', view)
dispatch('delCachedView', view)
resolve({
visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews]
})
})
},
delVisitedView ({ commit, state }, view) {
return new Promise(resolve => {
commit('DEL_VISITED_VIEW', view)
resolve([...state.visitedViews])
})
},
delCachedView ({ commit, state }, view) {
return new Promise(resolve => {
commit('DEL_CACHED_VIEW', view)
resolve([...state.cachedViews])
})
},
delOthersViews ({ dispatch, state }, view) {
return new Promise(resolve => {
dispatch('delOthersVisitedViews', view)
dispatch('delOthersCachedViews', view)
resolve({
visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews]
})
})
},
delOthersVisitedViews ({ commit, state }, view) {
return new Promise(resolve => {
commit('DEL_OTHERS_VISITED_VIEWS', view)
resolve([...state.visitedViews])
})
},
delOthersCachedViews ({ commit, state }, view) {
return new Promise(resolve => {
commit('DEL_OTHERS_CACHED_VIEWS', view)
resolve([...state.cachedViews])
})
},
delAllViews ({ dispatch, state }, view) {
return new Promise(resolve => {
dispatch('delAllVisitedViews', view)
dispatch('delAllCachedViews', view)
resolve({
visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews]
})
})
},
delAllVisitedViews ({ commit, state }) {
return new Promise(resolve => {
commit('DEL_ALL_VISITED_VIEWS')
resolve([...state.visitedViews])
})
},
delAllCachedViews ({ commit, state }) {
return new Promise(resolve => {
commit('DEL_ALL_CACHED_VIEWS')
resolve([...state.cachedViews])
})
},
updateVisitedView ({ commit }, view) {
commit('UPDATE_VISITED_VIEW', view)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@ -1,15 +0,0 @@
import Cookies from 'js-cookie'
const TokenKey = 'CMS-Token'
export function getToken () {
return Cookies.get(TokenKey)
}
export function setToken (token) {
return Cookies.set(TokenKey, token)
}
export function removeToken () {
return Cookies.remove(TokenKey)
}

View File

@ -1,29 +0,0 @@
import JSEncrypt from 'jsencrypt/bin/jsencrypt'
// 密钥对生成 http://web.chacuo.net/netrsakeypair
const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD\n' +
'2iRe41HdTNF8RUhNnHit5NpMNtGL0NPTSSpPjjI1kJfVorRvaQerUgkCAwEAAQ=='
const privateKey = 'MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8\n' +
'mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9p\n' +
'B6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue\n' +
'/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZ\n' +
'UBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6\n' +
'vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha\n' +
'4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3\n' +
'tTbklZkD2A=='
// 加密
export function encrypt (txt) {
const encryptor = new JSEncrypt()
encryptor.setPublicKey(publicKey) // 设置公钥
return encryptor.encrypt(txt) // 对需要加密的数据进行加密
}
// 解密
export function decrypt (txt) {
const encryptor = new JSEncrypt()
encryptor.setPrivateKey(privateKey)
return encryptor.decrypt(txt)
}

View File

@ -1,8 +0,0 @@
const OSS = require('ali-oss')
export const OSSclient = new OSS({
region: 'oss-cn-hangzhou',
accessKeyId: 'LTAI5tGR1NwGgA9fBQGxbUVa',
accessKeySecret: 'Xp4kbhFoEywD9aHQVopnThiyZbBQBp',
bucket: 'hbbwgczx'
})

View File

@ -1,40 +0,0 @@
// @/utils/permission
import store from '@/store'
/**
* 判断是否拥有页面权限
*/
export function pagePermission (permission = '') {
// 这里要判断的权限没有设置的话,就等于不需要权限,直接返回 true
if (!permission) return false
const permissionList = store.state.permission.permissionList
return !!permissionList.includes(permission)
}
/**
* 判断是否拥有资源权限
*/
export function btnPermission (val = '') {
// 这里要判断的权限没有设置的话,就等于不需要权限,直接返回 true
if (!val) return false
return store.state.permission.btnsPermissionList.includes(val)
}
export function includePermission (list = []) {
// 这里要判断的权限没有设置的话,就等于不需要权限,直接返回 true
if (!list.length) return false
let flag = false
list.forEach(item => {
if (store.state.permission.btnsPermissionList.includes(item)) {
flag = true
}
})
return flag
}

View File

@ -1,7 +1,5 @@
import axios from 'axios' import axios from 'axios'
import store from '@/store'
import { Message, MessageBox } from 'element-ui' import { Message, MessageBox } from 'element-ui'
import { getToken } from '@/utils/auth'
axios.defaults.withCredentials = true // 跨域访问需要发送cookie时一定要加这句 axios.defaults.withCredentials = true // 跨域访问需要发送cookie时一定要加这句
@ -50,9 +48,6 @@ function errorState (error) {
type: 'warning' type: 'warning'
} }
).then(() => { ).then(() => {
store.dispatch('LogOut').then(() => {
location.reload() // 为了重新实例化vue-router对象 避免bug
})
}) })
break break
case 403: case 403:
@ -81,11 +76,6 @@ function errorState (error) {
// 封装axios // 封装axios
function HttpRequest (url, method = 'GET', params = {}, isLoading = true) { function HttpRequest (url, method = 'GET', params = {}, isLoading = true) {
method = method.toUpperCase() method = method.toUpperCase()
if (isLoading && method === 'GET' && !store.state.app.loading) {
store.state.app.loading = true
}
// 设置token
const token = getToken()
const httpDefault = { const httpDefault = {
baseURL: process.env.VUE_APP_BASE_API, baseURL: process.env.VUE_APP_BASE_API,
method, method,
@ -113,9 +103,6 @@ function HttpRequest (url, method = 'GET', params = {}, isLoading = true) {
// always executed // always executed
httpTime-- httpTime--
// console.log(httpTime, url) // console.log(httpTime, url)
if (!httpTime) {
store.state.app.loading = false
}
}) })
}) })
} }

View File

@ -1,50 +0,0 @@
// 递归返回菜单权限列表
export function menuRecursion (arr, recursion = []) {
if (arr.length) {
arr.forEach(item => {
if (item.url && (!item.list || !item.list.length)) {
recursion.push(item.url)
}
if (item.list && item.list.length) {
menuRecursion(item.list, recursion)
}
})
}
return recursion
}
export function menuLists (arr) {
if (arr.length) {
arr.forEach(item => {
item.label = item.alias
item.value = item.alias
if (item.children.length) {
menuLists(item.children)
}
})
}
}
export function formatMenus (originalArr) {
const arr1 = []
const arr2 = []
const obj = {}
originalArr.forEach(item => {
if (!arr1.includes(item.parentId)) {
arr1.push(item.parentId)
obj[item.parentId] = []
} else {
obj[item.parentId].push(item)
}
})
originalArr.forEach(item => {
for (const key in obj) {
if (item.menuId === parseInt(key)) {
item.children = obj[key]
arr2.push(item)
}
}
})
console.log(arr2)
return arr2
}

View File

@ -1,147 +0,0 @@
export const rules = {
validPwd: (rule, value, callback) => {
const re = /^[0-9a-zA-Z]*$/
if (!value.trim()) {
callback(new Error('密码不能为空'))
} else if (value.trim().length < 6) {
callback(new Error('密码至少为六位数'))
} else if (value.trim().length > 20) {
callback(new Error('密码至多为二十位数'))
} else if (!re.test(value.trim())) {
callback(new Error('密码只能是字母、数字的组合'))
} else {
callback()
}
},
email: (rule, value, callback) => {
const reg = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/
if (value && !reg.test(value)) {
callback(new Error('非法的邮箱格式'))
} else {
callback()
}
},
moblie: (rule, value, callback) => {
if (!(/^1[34578]\d{9}$/.test(value))) {
callback(new Error('不合格的手机号码'))
} else {
callback()
}
},
checkTel (rule, value, callback) {
if ((/^1[34578]\d{9}$/.test(value)) || /^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/.test(value)) {
callback()
} else {
callback(new Error('请填写合法的手机号码或者固话号码'))
}
},
number: (rule, value, callback) => {
const reg = /^[0-9]*$/
if (!reg.test(value)) {
callback(new Error('请填纯数字'))
} else {
callback()
}
},
// 正整数
positiveNumber: (rule, value, callback) => {
const reg = /^[1-9]\d*$/
if (!value.replace(/^(\s*)|(\s*)$/g, '')) {
callback(new Error('请输入内容'))
} else if (!reg.test(value)) {
callback(new Error('请填正整数'))
} else {
callback()
}
},
auth: (rule, value, callback) => {
if (JSON.stringify(value) === '{}' || (value.menuLists && value.menuLists.length === 0)) {
callback(new Error('请选择权限'))
} else {
callback()
}
},
str: (rule, value, callback) => {
if (!value.replace(/^(\s*)|(\s*)$/g, '')) {
callback(new Error('请填字符串'))
} else {
callback()
}
},
username: (rule, value, callback) => {
const reg = /^[\u4e00-\u9fa5_a-zA-Z0-9.·]+$/
if (!reg.test(value)) {
callback(new Error('登录名只能包含字母数字下划线'))
} else {
callback()
}
},
time: (rule, value, callback) => {
const nowTime = new Date().getTime()
const currentTime = new Date(value).getTime()
if (!value) {
callback(new Error('时间不能为空'))
}
if (currentTime < nowTime) {
callback(new Error('不能选择过去时间'))
} else {
callback()
}
},
timerange: (rule, value, callback) => {
const nowTime = new Date().getTime()
const startTime = new Date(value[0]).getTime()
const endTime = new Date(value[1]).getTime()
if (!value) {
callback(new Error('时间不能为空'))
}
if (startTime < nowTime || endTime < nowTime) {
callback(new Error('不能选择过去时间'))
} else {
callback()
}
},
year: (rule, value, callback) => {
const nowTime = new Date().getFullYear()
if (!value) {
callback(new Error('时间不能为空'))
}
if (value < nowTime) {
callback(new Error('不能选择过去年份'))
} else {
callback()
}
},
name: (rule, value, callback) => {
const reg = /^[\w\u4E00-\u9FA5\uF900-\uFA2D]*$/
if (!reg.test(value)) {
callback(new Error('含有非法字符'))
} else {
callback()
}
},
idCard (rule, value, callback) {
const reg = new RegExp(/^(\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$/)
if (!reg.test(value)) {
callback(new Error('请输入正确身份证'))
} else {
callback()
}
},
ip (rule, value, callback) {
const reg = new RegExp(/^(\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])$/)
if (!reg.test(value)) {
callback(new Error('请输入正确ip格式'))
} else {
callback()
}
},
port (rule, value, callback) {
const reg = new RegExp(/^(\d)+$/)
if (!reg.test(value)) {
callback(new Error('请输入正确端口号'))
} else {
callback()
}
}
}

View File

@ -1,27 +0,0 @@
import axios from 'axios'
import { getToken } from '@/utils/auth'
// const mimeMap = {
// xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
// zip: 'application/zip'
// }
const baseUrl = process.env.VUE_APP_BASE_API
export function downLoadZip (str, filename) {
axios({
method: 'get',
url: baseUrl + str,
responseType: 'blob',
headers: { Authorization: 'Bearer ' + getToken() }
}).then(res => {
const blob = new Blob([res.data], { type: 'application/zip' })
const downloadElement = document.createElement('a')
const href = window.URL.createObjectURL(blob) // 创建下载的链接
downloadElement.href = href
downloadElement.download = filename + '.zip' // 下载后文件名
document.body.appendChild(downloadElement)
downloadElement.click() // 点击下载
document.body.removeChild(downloadElement) // 下载完成移除元素
window.URL.revokeObjectURL(href) // 释放掉blob对象
})
}

View File

@ -633,6 +633,10 @@ li {
.sls1Process { .sls1Process {
text-align: center; text-align: center;
img{
width:935px;
height:385px;
}
} }
p { p {
@ -690,12 +694,14 @@ li {
overflow: hidden; overflow: hidden;
margin-bottom: 10px !important; margin-bottom: 10px !important;
display: flex; display: flex;
justify-content: space-between;
padding:0 40px;
li { li {
width: 180px !important; width: 240px !important;
height: 180px !important; height: 240px !important;
float: left !important; float: left !important;
margin-left: 40px; // margin-left: 40px;
margin-bottom: 22px; margin-bottom: 22px;
padding: 0 !important; padding: 0 !important;

View File

@ -1,31 +1,37 @@
const path = require('path') const { resolve } = require('path')
const { defineConfig } = require('@vue/cli-service')
// 导入compression-webpack-plugin 开启gzip module.exports = defineConfig({
const CompressionWebpackPlugin = require('compression-webpack-plugin')
// 定义压缩文件类型
const productionGzipExtensions = ['js', 'css']
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
publicPath: '/', publicPath: '/',
lintOnSave: true, lintOnSave: false,
// 打包时不生成.map文件 // 打包时不生成.map文件
productionSourceMap: false, productionSourceMap: false,
chainWebpack: (config) => { chainWebpack: (config) => {
// 配置静态资源图片 config.module.rule('svg').exclude.add(resolve('src/icons')).end()
config.module.rules.delete('images') config.module
// 清除svg默认的处理方式 .rule('icons')
config.module.rules.delete('svg') .test(/\.svg$/)
config.plugin('html').tap(args => { .include.add(resolve('src/icons'))
args[0].title = 'Extensive Imaging' .end()
return args .use('svg-sprite-loader')
}) .loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
config.plugin('html')
.tap(args => {
args[0].title = "Extensive Imaging";
return args;
})
}, },
configureWebpack: { configureWebpack: {
devtool: 'source-map', devtool: 'source-map',
output: {
filename: `static/js/[name].[contenthash:8].js`,
chunkFilename: `static/js/[name].[chunkhash:8].js`,
assetModuleFilename: 'static/wasm/[name].[contenthash:8][ext]'
},
resolve: { resolve: {
extensions: ['.js', '.vue', '.json'], // 当引入文件时默认先找.js后缀的文件没找到再从左往右继续 extensions: ['.js', '.vue', '.json'], // 当引入文件时默认先找.js后缀的文件没找到再从左往右继续
alias: { alias: {
@ -34,68 +40,13 @@ module.exports = {
} }
}, },
plugins: [ plugins: [
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'), // 匹配文件名
threshold: 10240, // 对10K以上的数据进行压缩
minRatio: 0.8,
deleteOriginalAssets: false // 是否删除源文件
})
// new WebpackAliyunOss({
// from: ['./dist/**'], // 上传那个文件或文件夹 可以是字符串或数组
// dist: "/tydist", // 需要上传到oss上的给定文件目录
// region: "oss-cn-hangzhou",
// accessKeyId: "LTAI5tGR1NwGgA9fBQGxbUVa",
// accessKeySecret: "Xp4kbhFoEywD9aHQVopnThiyZbBQBp",
// bucket: "hbbwgczx",
// overwrite: true, // 是否需要覆盖bucket上的同名文件
// // test: true,
// //上面一行,可以在进行测试看上传路径是否正确, 打开后只会显示上传路径并不会真正上传;
// // 因为文件标识符 "" 和 "/" 的区别 不进行 setOssPath配置,上传的文件夹就会拼到文件名上, 丢失了文件目录,所以需要对setOssPath 配置。
// setOssPath: filePath => {
// let index = filePath.lastIndexOf("dist");
// let Path = filePath.substring(index + 4, filePath.length);
// return Path.replace(/\\/g, "/");
// },
// setHeaders: filePath => {
// return {
// "Cache-Control": "max-age=31536000"
// };
// }
// })
], ],
performance: { performance: {
hints: false hints: false
}, },
module: { module: {
rules: [ rules: [
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [resolve('src/static/icons')],
options: {
symbolId: 'icon-[name]'
}
},
{
test: /\.(png|jpe?g|gif|webp|svg)(\?.*)?$/,
exclude: [resolve('src/static/icons')],
use: [
{
loader: 'url-loader',
options: {
limit: 4096,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
}
]
}
] ]
} }
}, },
@ -120,4 +71,5 @@ module.exports = {
} }
} }
} }
} })