Compare commits

..

11 Commits

Author SHA1 Message Date
caiyiling bc79ebfe43 图片更改 2025-04-23 13:55:02 +08:00
caiyiling 98cea4336e 图片更改 2025-04-23 13:04:40 +08:00
wangxiaoshuang b59ea94010 字体样式修改 2025-04-21 15:22:11 +08:00
caiyiling 2c86f4f6bb 样式更改 2025-04-21 15:09:09 +08:00
caiyiling a4dab3d927 国际化更改 2025-04-21 15:06:05 +08:00
caiyiling 572a8d084c 中心影像服务页更改 2025-04-21 15:00:29 +08:00
wangxiaoshuang 9a32861b78 版本依赖冲突解决 2025-04-21 13:58:18 +08:00
wangxiaoshuang e6239f10d1 全局字体修改 2025-04-21 13:57:59 +08:00
wangxiaoshuang 35b232830a node版本升级 2025-04-18 17:33:21 +08:00
wangxiaoshuang 576f046f33 部分问题更改 2025-04-18 09:26:08 +08:00
caiyiling 194197323b 国际化更改 2025-04-17 17:57:29 +08:00
49 changed files with 17214 additions and 13176 deletions
-12
View File
@@ -1,12 +0,0 @@
{
"presets": [["@babel/preset-env", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
+1 -1
View File
@@ -8,7 +8,7 @@ module.exports = {
'@vue/standard'
],
parserOptions: {
parser: 'babel-eslint'
parser: '@babel/eslint-parser'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
-7
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 = {
plugins: plugins,
presets: [
'@vue/cli-plugin-babel/preset'
]
+16368 -12169
View File
File diff suppressed because it is too large Load Diff
+24 -33
View File
@@ -1,5 +1,5 @@
{
"name": "vue-template",
"name": "extensive-imaging",
"version": "0.1.0",
"private": true,
"scripts": {
@@ -8,41 +8,32 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"ali-oss": "^6.17.1",
"axios": "^0.19.2",
"core-js": "^3.6.4",
"dayjs": "^1.10.3",
"element-ui": "^2.13.0",
"js-cookie": "^2.2.1",
"jsencrypt": "^3.0.0-rc.1",
"core-js": "^3.8.3",
"element-ui": "^2.15.14",
"nprogress": "^0.2.0",
"v-viewer": "^1.6.4",
"vue": "^2.6.11",
"vue-cropper": "^0.5.5",
"vue-i18n": "^8.15.4",
"vue-router": "^3.1.5",
"vuex": "^3.1.2"
"v-viewer": "^1.7.4",
"vue": "^2.6.14",
"vue-i18n": "^8.28.2",
"vue-router": "^3.0.6",
"vuex": "^3.1.0"
},
"devDependencies": {
"@babel/preset-env": "^7.8.4",
"@vue/cli-plugin-babel": "^4.2.0",
"@vue/cli-plugin-eslint": "^4.2.0",
"@vue/cli-plugin-pwa": "^4.2.0",
"@vue/cli-service": "^4.2.0",
"@vue/eslint-config-standard": "^5.1.0",
"babel-eslint": "^10.0.3",
"babel-plugin-component": "^1.1.1",
"babel-plugin-transform-remove-console": "^6.9.4",
"compression-webpack-plugin": "^4.0.0",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^6.1.2",
"node-sass": "^4.12.0",
"sass-loader": "^8.0.2",
"svg-sprite-loader": "^4.2.1",
"vue-template-compiler": "^2.6.11"
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.27.0",
"@babel/preset-env": "^7.26.9",
"@vue/cli-plugin-babel": "^5.0.0",
"@vue/cli-plugin-eslint": "^5.0.0",
"@vue/cli-service": "^5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^7.2.1",
"eslint-plugin-standard": "^5.0.0",
"eslint-plugin-vue": "^8.0.3",
"sass": "~1.32.6",
"sass-loader": "^10.5.2",
"svg-sprite-loader": "^4.1.3",
"vue-template-compiler": "^2.6.14"
}
}
BIN
View File
Binary file not shown.
Binary file not shown.
+10 -3
View File
@@ -11,9 +11,11 @@
cursor: pointer;
margin-right: 20px;
}
.my_r {
color: #f66;
}
@font-face {
/* font-family:"montserratLight"; */
/* src: url('./static/montserrat-light-webfont.woff2'); */
@@ -22,11 +24,13 @@
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "Helvetica Neue For Number";
src: url("./static/SanFranciscoText-Light-15.otf");
unicode-range: U+30-39;
}
*,
h1,
h2,
@@ -34,15 +38,18 @@ h3,
h4,
h5,
h6 {
font-family: "Helvetica Neue For Number", "RalewayRegular";
/* font-family: "Helvetica Neue For Number", "RalewayRegular"; */
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 1.8;
font-variant-numeric: oldstyle-nums;
/* font-variant-numeric: oldstyle-nums; */
}
.viewer-toolbar > ul > li {
.viewer-toolbar>ul>li {
font-size: 0;
line-height: 0;
}
img {
-webkit-user-drag: none;
-moz-user-drag: none;
-28
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('请设置操作权限标签值')
}
}
}
-28
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('请设置角色权限标签值"')
}
}
}
-15
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
+41 -37
View File
@@ -4,54 +4,58 @@ const en = {
},
service: {
menuName: 'Services',
subMenu1: 'Centralized Imaging Service',
subMenu2: '3D Post-Processing Service',
subMenu1: 'Imaging CRO',
subMenu2: '3D 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.'
},
imagingService: {
tab1: 'Centralized Imaging Service',
tab2: 'Review Criteria',
tab1: 'Imaging CRO',
tab2: 'Criteria',
tab3: 'Indications',
tab4: 'Business Management Cloud Platform',
tab5: 'Team Expertise',
tab4: 'Workflow Platform',
tab5: 'Expertise',
tab6: 'Imaging System',
title1: 'Scope of Services',
title2: 'Service Offerings',
title1: 'Scope',
title2: 'Services',
title3: 'Business Workflow',
title4: 'Key Advantages',
title5: 'Distinguished Expert Network',
title6: 'Operations Team Expertise',
msg1: 'Whether you are a large pharmaceutical company, a biotech firm, a CRO, or a medical research institution, Extensive Imaging is fully committed to providing you with excellent medical imaging services tailored for clinical trials.',
msg2: 'Every clinical trial is unique, presenting distinct requirements and challenges. At Extensive Imaging, we offer a comprehensive suite of medical imaging services -- from early-stage R&D trials to large-scale international multi-site trials aimed at securing regulatory approval.',
title5: 'Global Network of Experts',
title6: 'Operations Team',
msg1: 'Whether you are pharmaceutical companies, biotech startups, contract research organizations, or biomedical research institutions, Extensive Imagings Research can provide you with clinical trial-related medical imaging services.',
msg2: 'Every clinical trial is unique, facing different requirements and challenges. Extensive Imagings Research offers you a series of medical imaging services, which covers from early stage R&D projects to large scale, international multicenter trials.',
msg3: 'Non-Oncology Imaging Review Criteria',
msg4: 'With the growing number of participants in multi-site clinical trials, the volume of imaging data has expended significantly, while regulatory requirements have also become increasing stringent. To address these challenges, Extensive Imaging has developed a cloud-based, end-to-end centralized imaging business management system. This platform enhances cross-site communication, reduces turnaround times, and ensures data security and traceability throughout the clinical trial workflow.',
msg5: 'Extensive Imaging Business Management Cloud Platform efficiently oversees every step of centralized imaging project workflows from image data acquisition at sites to regulatory submission of final results.',
msg6: 'All global trial stakeholders can maintain real-time communication across teams, and track project progress dynamically with full transparency.',
msg7: 'Design specifically for clinical trial imaging studies, Extensive Imaging Trial Management System minimizes reader subjectivity while improving measurement accuracy, quantitative analysis efficiency, and ensuring data security with full traceability. The system features DICOM & non-standard data viewers, advanced tumor lesion measurement tools, build-in eCRFs tailored to various diseases and assessment criteria. AI, radiomics, and 3D reconstruction for enhanced efficiency and broader indications.',
msg4: 'Nowadays there are more participants and stakeholders in multi-center clinical trials, and the volume of imaging data involved is increasing rapidly. At the same time, requirements of regulatory compliance have become even more strict. Because of this, Extensive Imagings Research developed a cloud-based clinical trial imaging workflow management system which can facilitate communications, reduce response time and guarantee the security and traceability of data.',
msg5: 'This system covers every step of IRC imaging assessment, from project setup to trial site surveys to image upload to the final data export. All stakeholders can work on a cloud-based, unified system, and keep connected and informed throughout the entire process on a global scale.',
msg6: '',
msg7: 'This cloud-based medical image reading software is designed specifically for image-based treatment response assessment in clinical trials.',
msg7_1: 'With the integrated rules and logic of the latest response criteria, this system can increase the accuracy of lesion measurements, reduce readers subjectivity and errors, ensure data quality and traceability, and improve the overall reading efficiency.',
msg7_2: 'In addition to major response criteria, such as RECIST1.1, PCWG3, Lugano 2014, iRECIST, etc., this system also supports customized or self-defined criteria through its highly flexible configuration tools.',
msg7_3: 'This system also provides readers with a set of advanced medical image reading tools, including Layout, Windowing, Inverse Color, Move, Rotate, Fit to Image, Screenshot, Diameter Measurement, Bidirectional Measurement, Arrow etc. DICOM images can be displayed based on subjects, visits, studies, or series, and non-DICOM images are also supported.',
msg8: 'Flexible Deployment: Cloud or on-premise, to secure, compliant, and user-friendly.',
msg9: 'Automated Reporting: Measurement results auto-populate into standardized eCRFs for accuracy and reliability.',
msg10: 'Versatile Tools: Multiple lesion measurement options for readers.',
msg11: 'Broad Compatibility: Support standard DICOM data, non-standard images and clinical data.',
msg12: 'Regulatory Compliance: Meets NMPA, FDA 510(k), CE, 21 CFR Part 11, Gxp, HIPPA.',
msg13: 'Extensive Imagings leadership in central imaging services stems from our highly experienced operations team and comprehensive support from global expert networks.',
msg14: 'Multi-disciplinary Coverage: Radiology, Oncology, Orthopedics, Hematology, ENT specialists.',
msg15: 'Global Expertise: Senior reviewers from Massachusetts General Hospital, Dana Barber, Sloan Kettering with 500+ trial experience (20000+ reads per reviewer annually).',
msg16: 'Domestic Specialists: Practicing radiologists from 3A hospital in Beijing/Shanghai with hands-on central imaging experience.',
msg17: 'Committee members from ACR, RSNA and the Chinese Society of Radiology.',
msg13: 'Extensive Imagings advantages in clinical trial imaging services are inseparable from our operations team with rich industry experience and rigorous work attitude, as well as the comprehensive professional knowledge support of our internal and external experts.',
msg14: 'Chinese and foreign expert team covers imaging, oncology, orthopedics, hematology, ENT and other specialties.',
msg15: 'Senior overseas experts work in prestigious institutions such as Massachusetts General Hospital, Dana-Farber, Sloan Kettering, etc., and many have participated in more than 500 trials.',
msg16: 'Chinese experts come from large tertiary hospitals in Beijing & Shanghai, and have rich hands-on experience in imaging trials.',
msg17: 'Advisory committee members include experts at the president and vice-president level from ACR, RSNA, and Chinese Society of Radiology.',
msg18: 'Harvard Medical School-certified program for independent central review (since 2017).',
msg19: 'Project Managers: 100+ cumulative successfully managed clinical trial imaging projects.',
msg20: 'Medical Team: Composed of seasoned physicians from China and the U.S. with radiology and clinical expertise, many of them previously served at top-10 healthcare institutions in both countries.',
msg21: 'Quality Control: All QC specialists possess clinical or medical imaging background.',
msg22: 'IT & Platform Development: Extensive healthcare IT experience in medical informatics & clinical trial systems.'
msg19: 'Project managers team has combined experience of more than 100 trials.',
msg20: 'Medical & scientific team is composed of senior physicians with imaging and clinical backgrounds from China and the United States, and many of them have worked in the top 10 hospitals in both countries.',
msg21: 'All quality control personnel have clinical or imaging background.',
msg22: 'R&D personnel have many years of experience in healthcare IT and imaging technology industry.'
},
processingService: {
tab1: '3-D Post-Processing Service',
tab1: '3D Post-Processing',
tab2: 'Services',
tab3: 'Workflow',
tab4: 'Cases',
tab5: '3D Reconstructions',
tab6: 'Image-Based Surgical Planning',
title1: '3-D Post-Processing Service',
title1: '3D Post-Processing',
title2: 'Benefits to Clients',
title3: 'Regulatory Compliance',
title4: 'Cardiovascular',
@@ -65,16 +69,16 @@ const en = {
title12: 'Kidney Mass',
title13: 'Pancreatic Tumor',
title14: 'Abdominal Aortic Aneurysm (AAA)',
msg1: `Advanced 3-D post-processing of CT and MRI images has become indispensable to clinical practice.
msg1: `Advanced 3D post-processing of CT and MRI images has become indispensable to clinical practice.
However,
hospitals face enormous challenges to implement high-quality, cost-effective 3-D post-processing
hospitals face enormous challenges to implement high-quality, cost-effective 3D post-processing
solutions
which can meet the ever-growing clinical demand. Everest Digital Medicine is an independent
post-processing center with specially trained radiologists and technologists, state-of-the-art
workstations, and validated clinical 3D protocols. We work with our clients to integrate advanced
medical
imaging technologies into their patient care.`,
msg2: 'Low cost, scalable turn-key 3-D solution',
msg2: 'Low cost, scalable turn-key 3D solution',
msg3: '24/7 availability & short turn-around time',
msg4: 'Specialized workforce & high post-processing quality',
msg5: 'Client\'s technologists can focus on CT/MRI scans',
@@ -82,7 +86,7 @@ const en = {
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
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 3D Post-Processings for CT and MRI studies.
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
clinical requirements.`,
@@ -125,12 +129,12 @@ const en = {
}
},
ImageBasedSurgicalPlanning: {
tab_LM: "Liver Mass",
tab_FSF: "Femoral Shaft Fracture",
tab_TAVI: "TAVI",
tab_KM: "Kidney Mass",
tab_PT: "Pancreatic Tumor",
tab_AAA: "AAA",
tab_LM: 'Liver Mass',
tab_FSF: 'Femoral Shaft Fracture',
tab_TAVI: 'TAVI',
tab_KM: 'Kidney Mass',
tab_PT: 'Pancreatic Tumor',
tab_AAA: 'AAA',
title1: 'Series',
title2: 'Clinical Implications',
title3: 'Images',
+3
View File
@@ -28,6 +28,9 @@ const zh = {
msg5: '展影医疗的业务管理云平台,能有效地管理中心影像项目工作流程的每一步,从研究机构采集影像数据,一直到向监管机构提交结果数据。',
msg6: '全球范围内的所有的项目参与方,都能保持实时沟通,并能随时了解项目的进展情况。',
msg7: '展影医疗基于云平台的影像评估系统是专门为临床试验中心影像项目而设计研发的,注重于消除阅片人的主观偏倚,提高测量和定量分析的效率和准确性,保证数据安全和结果的可追溯性。该系统包含面向Dicom和非标准数据的浏览工具,各种针对肿瘤靶病灶的测量工具,并且内置了针对不同疾病、不同评估标准的eCRF表格。人工智能、影像组学、三维重建技术的深度运用,极大地提高了影像评估的效率,拓展了评估标准和适应症的范围。',
msg7_1: '',
msg7_2: '',
msg7_3: '',
msg8: '支持云端或本地部署,保证影像评估工作安全、合规、方便地开展',
msg9: '测量结果自动输出到包含评估标准的eCRF表格,保证了数据和结果的准确性和可靠性',
msg10: '多种靶病灶测量工具可供阅片专家选择',
+25 -13
View File
@@ -1,27 +1,39 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store/index'
// import store from './store/index'
import ElementUI from 'element-ui'
import './static/styles/theme-blue/index.css' // 浅绿色主题
import i18n from '@/lang' // 国际化
import { btnPermission, includePermission } from './utils/permission'
// import { btnPermission, includePermission } from './utils/permission'
import Pagination from '@/components/Pagination'
import permission from './directive/permission'
// import permission from './directive/permission'
import { baseImgUrl } from './utils/config'
import './permission' // permission control
import 'nprogress/nprogress.css' // progress bar style
import './static/styles/element-variables.scss' // 自定义主题色
import './static/styles/index.scss'
import '@/static/icons'
import { OSSclient } from './utils/oss'
// import { OSSclient } from './utils/oss'
// use
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(ElementUI, {
@@ -29,18 +41,18 @@ Vue.use(ElementUI, {
})
Vue.config.productionTip = false
Vue.prototype.baseImgUrl = baseImgUrl
Vue.prototype.checkBtnPermission = btnPermission // 检查方法权限
Vue.prototype.includePermission = includePermission // 检查方法列表权限
Vue.prototype.OSSclient = OSSclient
// Vue.prototype.checkBtnPermission = btnPermission // 检查方法权限
// Vue.prototype.includePermission = includePermission // 检查方法列表权限
// Vue.prototype.OSSclient = OSSclient
// 全局组件挂载
Vue.component('Pagination', Pagination)
Vue.use(permission)
// Vue.use(permission)
new Vue({
router,
store,
// store,
i18n,
render: h => h(App)
}).$mount('#app')
-73
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: 131 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 797 KiB

-7
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";
@@ -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.
File diff suppressed because one or more lines are too long
+460
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;
}
}
-11
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
-19
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
-57
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
}
-33
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
}
-159
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
}
-15
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)
}
-29
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)
}
-8
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'
})
-40
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
}
-13
View File
@@ -1,7 +1,5 @@
import axios from 'axios'
import store from '@/store'
import { Message, MessageBox } from 'element-ui'
import { getToken } from '@/utils/auth'
axios.defaults.withCredentials = true // 跨域访问需要发送cookie时一定要加这句
@@ -50,9 +48,6 @@ function errorState (error) {
type: 'warning'
}
).then(() => {
store.dispatch('LogOut').then(() => {
location.reload() // 为了重新实例化vue-router对象 避免bug
})
})
break
case 403:
@@ -81,11 +76,6 @@ function errorState (error) {
// 封装axios
function HttpRequest (url, method = 'GET', params = {}, isLoading = true) {
method = method.toUpperCase()
if (isLoading && method === 'GET' && !store.state.app.loading) {
store.state.app.loading = true
}
// 设置token
const token = getToken()
const httpDefault = {
baseURL: process.env.VUE_APP_BASE_API,
method,
@@ -113,9 +103,6 @@ function HttpRequest (url, method = 'GET', params = {}, isLoading = true) {
// always executed
httpTime--
// console.log(httpTime, url)
if (!httpTime) {
store.state.app.loading = false
}
})
})
}
-50
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
}
-147
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()
}
}
}
-27
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对象
})
}
+25 -16
View File
@@ -50,10 +50,7 @@
style="margin-left:20px;">
<img src="@/static/images/ba.png" style="vertical-align: middle;">
<span style="margin: 0px 0px 0px 3px; vertical-align: middle;">沪公网安备 31011002005859</span></a>
</p><a target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=31011002005859"
style="margin-left:20px;">
</a>
<p></p>
</p>
<!-- <a href="javascript:;" class="backTop tre"><i class="fa fa-angle-up"></i></a> -->
<div class="shares cf">
<a href="http://weibo.com/u/5834449233?is_all=1" class="a1" target="_blank"><img
@@ -71,7 +68,7 @@
<script>
export default {
data() {
data () {
return {
menuList: [
'Home',
@@ -91,12 +88,12 @@ export default {
}
},
watch: {
$route(v) {
$route (v) {
this.changeTab()
}
},
methods: {
changeTab() {
changeTab () {
if (this.menuList.findIndex((v) => v === this.$route.name) === -1) {
if (this.$route.name === 'Center Imaging Services') {
this.active = '1'
@@ -108,10 +105,10 @@ export default {
this.active = this.menuList.findIndex((v) => v === this.$route.name)
}
},
selectTab(v, title) {
selectTab (v, title) {
console.log('selectTab')
if (v === '5') {
window.open('https://lili.elevateimaging.ai/')
window.open('https://irc.extimaging.com')
this.changeTab()
} else {
if (v.indexOf('-') > -1) {
@@ -132,7 +129,7 @@ export default {
}
}
},
mounted() {
mounted () {
if (this.menuList.findIndex((v) => v === this.$route.name) === -1) {
if (this.$route.name === 'Center Imaging Services') {
this.active = '1'
@@ -161,7 +158,7 @@ export default {
<style lang="scss" scoped>
.layout {
overflow: auto;
user-select: none;
// user-select: none;
.header {
background: #fff;
@@ -258,13 +255,21 @@ export default {
}
.bottom {
width: 1125px;
width: 1200px;
height: 74px;
background: #d4d4d4;
margin: 30px auto 30px auto;
position: relative;
font-size: 12px !important;
.copyright {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
p {
padding-left: 30px;
color: #3e3e3f;
@@ -272,7 +277,7 @@ export default {
}
.left {
float: left;
// float: left;
font-size: 12px !important;
a {
@@ -285,8 +290,13 @@ export default {
}
.shares {
float: right;
// float: right;
width: 100px;
height: 100%;
line-height: 1;
display: flex;
align-items: center;
justify-content: space-between;
// margin-top: 22px;
a {
@@ -296,7 +306,7 @@ export default {
border: 1px solid #898d98;
border-radius: 2px;
margin-right: 15px;
float: left;
// float: left;
display: flex;
align-items: center;
justify-content: center;
@@ -330,7 +340,6 @@ export default {
}
}
}
}
</style>
@@ -2,6 +2,14 @@
<div class="Image-Based-Surgical-Planning">
<div class="banner"></div>
<div class="content">
<el-breadcrumb separator-class="el-icon-arrow-right"
style="padding: 5px;background-color: #f1f1f1;margin-top: 10px;">
<el-breadcrumb-item :to="{ path: '/home' }">{{ $t('home.menuName') }}</el-breadcrumb-item>
<el-breadcrumb-item><a href="/services">{{ $t('service.menuName') }}</a></el-breadcrumb-item>
<el-breadcrumb-item><a href="/services/3DPostProcessingServices">{{ $t('service.subMenu2')
}}</a></el-breadcrumb-item>
<el-breadcrumb-item>{{ $t('processingService.tab6') }}</el-breadcrumb-item>
</el-breadcrumb>
<div class="solution">
<div class="soluDetail">
<div class="cont" style="display: block;margin-top: 0;">
@@ -452,7 +460,7 @@ export default {
// style="color: #1083b6;font-size: 22px;"
.content {
width: 926px;
width: 1200px;
margin: 0 auto;
.el-tabs__nav .el-tabs__item:first-child {
@@ -595,7 +603,7 @@ li {
}
.solution {
width: 926px;
width: 100%;
margin: 0 auto;
padding-top: 30px;
padding-bottom: 73px;
@@ -8,8 +8,7 @@
</template>
</div>
<div class="message">
<strong
v-html="$t(`ImageBasedSurgicalPlanning.${data.key}.series_${data.id}`)"></strong>
<strong v-html="$t(`ImageBasedSurgicalPlanning.${data.key}.series_${data.id}`)"></strong>
<p v-html="$t(`ImageBasedSurgicalPlanning.${data.key}.clinical_${data.id}`)"></p>
</div>
</div>
@@ -156,6 +155,8 @@ export default {
color: #aaa;
line-height: 20px;
padding: 17px 18px;
text-align: left;
word-break: keep-all;
}
}
}
@@ -465,7 +465,7 @@ export default {
min-height: 230px;
box-shadow: 0 0 8px rgba(0, 0, 0, .2);
padding: 40px;
width: 920px;
width: calc(100% - 80px);
.title {
font-size: 25px;
@@ -546,7 +546,7 @@ li {
}
.solution {
width: 926px;
width: 1200px;
margin: 0 auto;
padding-top: 30px;
padding-bottom: 73px;
@@ -580,7 +580,7 @@ li {
.tabsWW {
height: 65px;
width: 926px;
width: 100%;
margin: 0 auto;
}
@@ -622,7 +622,7 @@ li {
}
.soluDetail {
width: 926px;
width: 1200px;
margin: 0 auto;
padding-top: 55px;
padding-bottom: 30px;
@@ -631,6 +631,14 @@ li {
.itemX {
margin-bottom: 30px;
.sls1Process {
text-align: center;
img{
width:935px;
height:385px;
}
}
p {
font-size: 15px;
line-height: 28px;
@@ -686,12 +694,14 @@ li {
overflow: hidden;
margin-bottom: 10px !important;
display: flex;
justify-content: space-between;
padding:0 40px;
li {
width: 180px !important;
height: 180px !important;
width: 240px !important;
height: 240px !important;
float: left !important;
margin-left: 40px;
// margin-left: 40px;
margin-bottom: 22px;
padding: 0 !important;
@@ -31,7 +31,7 @@
v-if="$i18n.locale === 'en'"
src="@/static/images/image3.png"
alt=""
style="width: 400px;"
style="width: 500px;"
/>
</div>
<h2>{{ $t('imagingService.title2') }}</h2>
@@ -129,44 +129,174 @@
v-if="$i18n.locale === 'en'"
src="@/static/images/image4.png"
alt=""
style="display: block;margin: 0 auto;object-fit: cover;width: 900px;"
style="display: block;margin: 0 auto;object-fit: cover;width: 700px;"
/>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('imagingService.tab6')" name="5">
<div class="item">
<p style="margin-bottom: 20px;">{{ $t('imagingService.msg7') }}</p>
<h2>{{ $t('imagingService.title4') }}</h2>
<div style="display: flex;">
<ul>
<li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg8') }}
</li>
<li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg9') }}
</li>
<li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg10') }}
</li>
<li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg11') }}
</li>
<li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg12') }}
</li>
</ul>
<img
src="@/static/images/irc05.png"
alt=""
style="width:300px;position:relative;top:-60px;"
/>
</div>
<p>{{ $t('imagingService.msg7') }}</p>
<p>{{ $t('imagingService.msg7_1') }}</p>
<p>{{ $t('imagingService.msg7_2') }}</p>
<p style="margin-bottom: 20px;">{{ $t('imagingService.msg7_3') }}</p>
<template v-if="$i18n.locale === 'zh'">
<h2>{{ $t('imagingService.title4') }}</h2>
<div style="display: flex;">
<ul>
<li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg8') }}
</li>
<li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg9') }}
</li>
<li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg10') }}
</li>
<li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg11') }}
</li>
<li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg12') }}
</li>
</ul>
<img
src="@/static/images/irc05.png"
alt=""
style="width:300px;position:relative;top:-60px;"
/>
</div>
</template>
<template v-if="$i18n.locale === 'en'">
<h2>Major features of this system are as follows</h2>
<div style="display: flex;align-items: center;justify-content: space-between;">
<div style="width: 750px;">
<p style="font-size: 16px;margin-bottom: 20px;"> 1. An image management & reading tool for treatment response assessment </p>
<ul style="padding-left: 24px;margin: 0 0 5px 0;">
<li>
<i class="el-icon-caret-right"></i>
Image studies are managed and organized based on visits by default.
</li>
<li>
<i class="el-icon-caret-right"></i>
Multiple studies of different modalities can be clustered into one visit.
</li>
<li>
<i class="el-icon-caret-right"></i>
Image studies of different visits can be displayed in the same window side by side.
</li>
<li>
<i class="el-icon-caret-right"></i>
Image series containing lesions are clearly marked as key series.
</li>
<li>
<i class="el-icon-caret-right"></i>
Lesion marking tools can be customized.
</li>
</ul>
</div>
<div style="width: 400px;margin-right: 20px;text-align: center;">
<viewer :images="[`@/static/static/images/imaging_5_1.png`]">
<img style="height: 150px;cursor: pointer" src="@/static/images/imaging_5_1.png" alt="">
<div>
<span class="el-icon-view"></span>
<span style="margin-left: 4px">click image</span>
</div>
</viewer>
</div>
</div>
<div style="display: flex;align-items: center;justify-content: space-between;">
<div style="width: 750px;">
<p style="font-size: 16px;margin-bottom: 20px;">
2. An intelligent lesion marking tool
</p>
<ul style="padding-left: 24px;margin: 0 0 5px 0;">
<li>
<i class="el-icon-caret-right"></i>
Automatic generation of lesion entries in electronic image case report forms (eICRF).
</li>
<li>
<i class="el-icon-caret-right"></i>
Lesion information & measurement data are automatically recorded into eICRF.
</li>
<li>
<i class="el-icon-caret-right"></i>
Based on measurement data, automatically detect a lesions type, and update its state.
</li>
<li>
<i class="el-icon-caret-right"></i>
Intelligently remind total/per organ lesion number limits, and non-conformity issues.
</li>
<li>
<i class="el-icon-caret-right"></i>
Automatic verification of lesion informations accuracy, and intelligent error alerts.
</li>
<li>
<i class="el-icon-caret-right"></i>
Automatically display a slice which most likely contains the intended target lesion.
</li>
</ul>
</div>
<div style="width: 400px;margin-right: 20px;text-align: center;">
<viewer :images="[`@/static/static/images/imaging_5_2.png`]">
<img style="height: 150px;cursor: pointer" src="@/static/images/imaging_5_2.png" alt="">
<div>
<span class="el-icon-view"></span>
<span style="margin-left: 4px">click image</span>
</div>
</viewer>
</div>
</div>
<div style="display: flex;align-items: center;justify-content: space-between;">
<div style="width: 750px;">
<p style="font-size: 16px;margin-bottom: 20px;">
3. An automated treatment response computational tool
</p>
<ul style="padding-left: 24px;margin: 0 0 5px 0;">
<li>
<i class="el-icon-caret-right"></i>
Automatically identify minimal, maximal values, and compute sums, change rates etc., based on measurement data.
</li>
<li>
<i class="el-icon-caret-right"></i>
Automatically compute treatment responses of target, non-target, & new lesions.
</li>
<li>
<i class="el-icon-caret-right"></i>
Automatically compute the overall treatment response.
</li>
<li>
<i class="el-icon-caret-right"></i>
Reviewers can override automatically generated assessment results.
</li>
<li>
<i class="el-icon-caret-right"></i>
Automatic verification of lesion informations accuracy, and intelligent error alerts.
</li>
<li>
<i class="el-icon-caret-right"></i>
Automatically generate electronic imaging case report forms.
</li>
</ul>
</div>
<div style="width: 400px;margin-right: 20px;text-align: center;">
<viewer :images="[`@/static/static/images/imaging_5_3.png`]">
<img style="height: 150px;cursor: pointer" src="@/static/images/imaging_5_3.png" alt="">
<div>
<span class="el-icon-view"></span>
<span style="margin-left: 4px">click image</span>
</div>
</viewer>
</div>
</div>
</template>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('imagingService.tab5')" name="6">
@@ -191,10 +321,10 @@
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg17') }}
</li>
<li>
<!-- <li>
<i class="el-icon-caret-right"></i>
{{ $t('imagingService.msg18') }}
</li>
</li> -->
</ul>
<div style="width:340px;">
<img
@@ -203,12 +333,12 @@
alt=""
style="width: 340px;position:relative;top:-40px;"
/>
<img
<!-- <img
v-if="$i18n.locale === 'en'"
src="@/static/images/image7.png"
alt=""
style="width: 340px;position:relative;top:-40px;"
/>
/> -->
</div>
</div>
+29 -77
View File
@@ -1,31 +1,37 @@
const path = require('path')
const { resolve } = require('path')
const { defineConfig } = require('@vue/cli-service')
// 导入compression-webpack-plugin 开启gzip
const CompressionWebpackPlugin = require('compression-webpack-plugin')
// 定义压缩文件类型
const productionGzipExtensions = ['js', 'css']
function resolve (dir) {
return path.join(__dirname, dir)
}
module.exports = {
module.exports = defineConfig({
publicPath: '/',
lintOnSave: true,
lintOnSave: false,
// 打包时不生成.map文件
productionSourceMap: false,
chainWebpack: (config) => {
// 配置静态资源图片
config.module.rules.delete('images')
// 清除svg默认的处理方式
config.module.rules.delete('svg')
config.plugin('html').tap(args => {
args[0].title = 'Elevate Imaging'
return args
})
config.module.rule('svg').exclude.add(resolve('src/icons')).end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/icons'))
.end()
.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: {
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: {
extensions: ['.js', '.vue', '.json'], // 当引入文件时默认先找.js后缀的文件,没找到再从左往右继续
alias: {
@@ -34,68 +40,13 @@ module.exports = {
}
},
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: {
hints: false
},
module: {
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 = {
}
}
}
}
})