This commit is contained in:
BianLzhaoMin 2025-09-23 14:25:17 +08:00
parent 6c2436be1d
commit ff07006574
5 changed files with 315 additions and 292 deletions

View File

@ -5,4 +5,4 @@ VUE_APP_TITLE = 水运工程设计施工AR应用平台
ENV = 'production' ENV = 'production'
# 水运工程设计施工AR应用平台/生产环境 # 水运工程设计施工AR应用平台/生产环境
VUE_APP_BASE_API = '/prod-api' VUE_APP_BASE_API = '/water-design/prod-api'

View File

@ -223,4 +223,4 @@
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,50 +1,54 @@
<template> <template>
<div class="navbar"> <div class="navbar">
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> <hamburger
id="hamburger-container"
:is-active="sidebar.opened"
class="hamburger-container"
@toggleClick="toggleSideBar"
/>
<breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" /> <breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" />
<top-nav v-if="topNav" id="topmenu-container" class="topmenu-container" /> <top-nav v-if="topNav" id="topmenu-container" class="topmenu-container" />
<div class="right-menu"> <div class="right-menu">
<template v-if="device!=='mobile'"> <template v-if="device !== 'mobile'">
<search id="header-search" class="right-menu-item" /> <search id="header-search" class="right-menu-item" />
<el-tooltip content="源码地址" effect="dark" placement="bottom"> <el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip> </el-tooltip>
<el-tooltip content="文档地址" effect="dark" placement="bottom"> <el-tooltip content="文档地址" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" /> <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip> </el-tooltip>
<screenfull id="screenfull" class="right-menu-item hover-effect" /> <screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="布局大小" effect="dark" placement="bottom"> <el-tooltip content="布局大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" /> <size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip> </el-tooltip>
</template>
</template> <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover">
<div class="avatar-wrapper">
<img :src="avatar" class="user-avatar" />
<span class="user-nickname"> {{ nickName }} </span>
</div>
<el-dropdown-menu slot="dropdown">
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item divided @click.native="logout">
<span>退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover"> <div class="right-menu-item hover-effect setting" @click="setLayout" v-if="setting">
<div class="avatar-wrapper"> <svg-icon icon-class="more-up" />
<img :src="avatar" class="user-avatar"> </div>
<span class="user-nickname"> {{ nickName }} </span>
</div> </div>
<el-dropdown-menu slot="dropdown">
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item divided @click.native="logout">
<span>退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class="right-menu-item hover-effect setting" @click="setLayout" v-if="setting">
<svg-icon icon-class="more-up" />
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@ -59,150 +63,147 @@ import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc' import RuoYiDoc from '@/components/RuoYi/Doc'
export default { export default {
emits: ['setLayout'], emits: ['setLayout'],
components: { components: {
Breadcrumb, Breadcrumb,
TopNav, TopNav,
Hamburger, Hamburger,
Screenfull, Screenfull,
SizeSelect, SizeSelect,
Search, Search,
RuoYiGit, RuoYiGit,
RuoYiDoc RuoYiDoc,
},
computed: {
...mapGetters([
'sidebar',
'avatar',
'device',
'nickName'
]),
setting: {
get() {
return this.$store.state.settings.showSettings
}
}, },
topNav: { computed: {
get() { ...mapGetters(['sidebar', 'avatar', 'device', 'nickName']),
return this.$store.state.settings.topNav setting: {
} get() {
} return this.$store.state.settings.showSettings
}, },
methods: { },
toggleSideBar() { topNav: {
this.$store.dispatch('app/toggleSideBar') get() {
return this.$store.state.settings.topNav
},
},
}, },
setLayout(event) { methods: {
this.$emit('setLayout') toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
},
setLayout(event) {
this.$emit('setLayout')
},
logout() {
this.$confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
this.$store.dispatch('LogOut').then(() => {
location.href = '/water-design/index'
})
})
.catch(() => {})
},
}, },
logout() {
this.$confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$store.dispatch('LogOut').then(() => {
location.href = '/index'
})
}).catch(() => {})
}
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.navbar { .navbar {
height: 50px; height: 50px;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
background: #fff; background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08); box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container { .hamburger-container {
line-height: 46px; line-height: 46px;
height: 100%; height: 100%;
float: left; float: left;
cursor: pointer;
transition: background .3s;
-webkit-tap-highlight-color:transparent;
&:hover {
background: rgba(0, 0, 0, .025)
}
}
.breadcrumb-container {
float: left;
}
.topmenu-container {
position: absolute;
left: 50px;
}
.errLog-container {
display: inline-block;
vertical-align: top;
}
.right-menu {
float: right;
height: 100%;
line-height: 50px;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
padding: 0 8px;
height: 100%;
font-size: 18px;
color: #5a5e66;
vertical-align: text-bottom;
&.hover-effect {
cursor: pointer; cursor: pointer;
transition: background .3s; transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
&:hover { &:hover {
background: rgba(0, 0, 0, .025) background: rgba(0, 0, 0, 0.025);
} }
}
} }
.avatar-container { .breadcrumb-container {
margin-right: 0px; float: left;
padding-right: 0px; }
.avatar-wrapper { .topmenu-container {
margin-top: 10px; position: absolute;
position: relative; left: 50px;
}
.user-avatar {
cursor: pointer; .errLog-container {
width: 30px; display: inline-block;
height: 30px; vertical-align: top;
border-radius: 50%; }
}
.right-menu {
.user-nickname{ float: right;
position: relative; height: 100%;
bottom: 10px; line-height: 50px;
font-size: 14px;
font-weight: bold; &:focus {
} outline: none;
}
.el-icon-caret-bottom {
cursor: pointer; .right-menu-item {
position: absolute; display: inline-block;
right: -20px; padding: 0 8px;
top: 25px; height: 100%;
font-size: 12px; font-size: 18px;
} color: #5a5e66;
} vertical-align: text-bottom;
&.hover-effect {
cursor: pointer;
transition: background 0.3s;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
}
.avatar-container {
margin-right: 0px;
padding-right: 0px;
.avatar-wrapper {
margin-top: 10px;
position: relative;
.user-avatar {
cursor: pointer;
width: 30px;
height: 30px;
border-radius: 50%;
}
.user-nickname {
position: relative;
bottom: 10px;
font-size: 14px;
font-weight: bold;
}
.el-icon-caret-bottom {
cursor: pointer;
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
} }
}
} }
</style> </style>

View File

@ -180,4 +180,5 @@ export default new Router({
mode: 'history', // 去掉url中的# mode: 'history', // 去掉url中的#
scrollBehavior: () => ({ y: 0 }), scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes, routes: constantRoutes,
base: '/water-design',
}) })

View File

@ -3,7 +3,7 @@ import { Notification, MessageBox, Message, Loading } from 'element-ui'
import store from '@/store' import store from '@/store'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode' import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from "@/utils/ruoyi" import { tansParams, blobValidate } from '@/utils/ruoyi'
import cache from '@/plugins/cache' import cache from '@/plugins/cache'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
@ -14,139 +14,160 @@ export let isRelogin = { show: false }
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例 // 创建axios实例
const service = axios.create({ const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分 // axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API, baseURL: process.env.VUE_APP_BASE_API,
// 超时 // 超时
timeout: 10000 timeout: 10000,
}) })
// request拦截器 // request拦截器
service.interceptors.request.use(config => { service.interceptors.request.use(
// 是否需要设置 token (config) => {
const isToken = (config.headers || {}).isToken === false // 是否需要设置 token
// 是否需要防止数据重复提交 const isToken = (config.headers || {}).isToken === false
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false // 是否需要防止数据重复提交
if (getToken() && !isToken) { const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 if (getToken() && !isToken) {
} config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
// get请求映射params参数 }
if (config.method === 'get' && config.params) { // get请求映射params参数
let url = config.url + '?' + tansParams(config.params) if (config.method === 'get' && config.params) {
url = url.slice(0, -1) let url = config.url + '?' + tansParams(config.params)
config.params = {} url = url.slice(0, -1)
config.url = url config.params = {}
} config.url = url
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { }
const requestObj = { if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
url: config.url, const requestObj = {
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, url: config.url,
time: new Date().getTime() data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
} time: new Date().getTime(),
const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小 }
const limitSize = 5 * 1024 * 1024 // 限制存放数据5M const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小
if (requestSize >= limitSize) { const limitSize = 5 * 1024 * 1024 // 限制存放数据5M
console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制无法进行防重复提交验证。') if (requestSize >= limitSize) {
return config console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制无法进行防重复提交验证。')
} return config
const sessionObj = cache.session.getJSON('sessionObj') }
if (sessionObj === undefined || sessionObj === null || sessionObj === '') { const sessionObj = cache.session.getJSON('sessionObj')
cache.session.setJSON('sessionObj', requestObj) if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
} else { cache.session.setJSON('sessionObj', requestObj)
const s_url = sessionObj.url // 请求地址 } else {
const s_data = sessionObj.data // 请求数据 const s_url = sessionObj.url // 请求地址
const s_time = sessionObj.time // 请求时间 const s_data = sessionObj.data // 请求数据
const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交 const s_time = sessionObj.time // 请求时间
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交
const message = '数据正在处理,请勿重复提交' if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
console.warn(`[${s_url}]: ` + message) const message = '数据正在处理,请勿重复提交'
return Promise.reject(new Error(message)) console.warn(`[${s_url}]: ` + message)
} else { return Promise.reject(new Error(message))
cache.session.setJSON('sessionObj', requestObj) } else {
} cache.session.setJSON('sessionObj', requestObj)
} }
} }
return config }
}, error => { return config
console.log(error) },
Promise.reject(error) (error) => {
}) console.log(error)
Promise.reject(error)
},
)
// 响应拦截器 // 响应拦截器
service.interceptors.response.use(res => { service.interceptors.response.use(
// 未设置状态码则默认成功状态 (res) => {
const code = res.data.code || 200 // 未设置状态码则默认成功状态
// 获取错误信息 const code = res.data.code || 200
const msg = errorCode[code] || res.data.msg || errorCode['default'] // 获取错误信息
// 二进制数据则直接返回 const msg = errorCode[code] || res.data.msg || errorCode['default']
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { // 二进制数据则直接返回
return res.data if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
} return res.data
if (code === 401) { }
if (!isRelogin.show) { if (code === 401) {
isRelogin.show = true if (!isRelogin.show) {
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { isRelogin.show = true
isRelogin.show = false MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
store.dispatch('LogOut').then(() => { confirmButtonText: '重新登录',
location.href = '/index' cancelButtonText: '取消',
}) type: 'warning',
}).catch(() => { })
isRelogin.show = false .then(() => {
}) isRelogin.show = false
} store.dispatch('LogOut').then(() => {
return Promise.reject('无效的会话,或者会话已过期,请重新登录。') location.href = '/water-design/index'
} else if (code === 500) { })
Message({ message: msg, type: 'error' }) })
return Promise.reject(new Error(msg)) .catch(() => {
} else if (code === 601) { isRelogin.show = false
Message({ message: msg, type: 'warning' }) })
return Promise.reject('error') }
} else if (code !== 200) { return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
Notification.error({ title: msg }) } else if (code === 500) {
return Promise.reject('error') Message({ message: msg, type: 'error' })
} else { return Promise.reject(new Error(msg))
return res.data } else if (code === 601) {
} Message({ message: msg, type: 'warning' })
}, return Promise.reject('error')
error => { } else if (code !== 200) {
console.log('err' + error) Notification.error({ title: msg })
let { message } = error return Promise.reject('error')
if (message == "Network Error") { } else {
message = "后端接口连接异常" return res.data
} else if (message.includes("timeout")) { }
message = "系统接口请求超时" },
} else if (message.includes("Request failed with status code")) { (error) => {
message = "系统接口" + message.substr(message.length - 3) + "异常" console.log('err' + error)
} let { message } = error
Message({ message: message, type: 'error', duration: 5 * 1000 }) if (message == 'Network Error') {
return Promise.reject(error) message = '后端接口连接异常'
} } else if (message.includes('timeout')) {
message = '系统接口请求超时'
} else if (message.includes('Request failed with status code')) {
message = '系统接口' + message.substr(message.length - 3) + '异常'
}
Message({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
},
) )
// 通用下载方法 // 通用下载方法
export function download(url, params, filename, config) { export function download(url, params, filename, config) {
downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) downloadLoadingInstance = Loading.service({
return service.post(url, params, { text: '正在下载数据,请稍候',
transformRequest: [(params) => { return tansParams(params) }], spinner: 'el-icon-loading',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, background: 'rgba(0, 0, 0, 0.7)',
responseType: 'blob', })
...config return service
}).then(async (data) => { .post(url, params, {
const isBlob = blobValidate(data) transformRequest: [
if (isBlob) { (params) => {
const blob = new Blob([data]) return tansParams(params)
saveAs(blob, filename) },
} else { ],
const resText = await data.text() headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
const rspObj = JSON.parse(resText) responseType: 'blob',
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] ...config,
Message.error(errMsg) })
} .then(async (data) => {
downloadLoadingInstance.close() const isBlob = blobValidate(data)
}).catch((r) => { if (isBlob) {
console.error(r) const blob = new Blob([data])
Message.error('下载文件出现错误,请联系管理员!') saveAs(blob, filename)
downloadLoadingInstance.close() } else {
}) const resText = await data.text()
const rspObj = JSON.parse(resText)
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
Message.error(errMsg)
}
downloadLoadingInstance.close()
})
.catch((r) => {
console.error(r)
Message.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close()
})
} }
export default service export default service