Compare commits
No commits in common. "d5a782ac7339bf66632f0a29fdeaf97d88b2596b" and "6181b757956629177a7c2a4e08918634c72dfac8" have entirely different histories.
d5a782ac73
...
6181b75795
|
|
@ -21,11 +21,8 @@ VUE_APP_PJ = 'gx'
|
|||
|
||||
# VUE_APP_BASEURL = http://10.50.211.228:2250/
|
||||
|
||||
# VUE_APP_API_BASEURL = http://127.0.0.1:2226/api
|
||||
# VUE_APP_BASEURL = http://127.0.0.1:2226
|
||||
|
||||
VUE_APP_API_BASEURL = http://10.0.11.51:2250/api
|
||||
VUE_APP_BASEURL = http://10.0.11.51:2250
|
||||
VUE_APP_API_BASEURL = http://127.0.0.1:2226/api
|
||||
VUE_APP_BASEURL = http://127.0.0.1:2226
|
||||
|
||||
# 托克逊
|
||||
# VUE_APP_API_BASEURL = http://10.50.211.228:2250/api
|
||||
|
|
|
|||
|
|
@ -6,24 +6,11 @@ export default {
|
|||
name: "列表",
|
||||
req: async function(data){
|
||||
return await http.post(
|
||||
// 同源路径,由 nginx(/factory_api/) 反向代理到统计服务 10.0.11.52:5800
|
||||
`${config.HOST_URL}/factory_api/mplogx/`,
|
||||
`${config.HOST_URL}/hfnf_api/mplogx/`,
|
||||
// "http://10.0.11.52:5800/mplogx/",
|
||||
data
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
stats: {
|
||||
list: {
|
||||
name: "数据统计",
|
||||
req: async function(params){
|
||||
return await http.get(
|
||||
// 同源路径,由 nginx(/factory_api/) 反向代理到统计服务 10.0.11.52:5800
|
||||
`${config.HOST_URL}/factory_api/stats`,
|
||||
params
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -414,8 +414,8 @@ export default {
|
|||
}
|
||||
this.pmenu = this.$route.meta.breadcrumb
|
||||
? this.$route.meta.breadcrumb[0]
|
||||
: { meta: {}, children: [] };
|
||||
let lists = this.pmenu.children || [];
|
||||
: {};
|
||||
let lists = this.pmenu.children;
|
||||
let perms = this.$TOOL.data.get("PERMISSIONS") || [];
|
||||
for (var i = 0; i < lists.length; i++) {
|
||||
if (lists[i].meta && lists[i].meta.perms) {
|
||||
|
|
|
|||
11
src/main.js
11
src/main.js
|
|
@ -55,14 +55,7 @@ const _ResizeObserver = window.ResizeObserver;
|
|||
|
||||
window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
|
||||
constructor(callback) {
|
||||
// 先包 try/catch 再 debounce:debounce 用 setTimeout 延迟执行,
|
||||
// 必须让真正执行回调时也在 try/catch 内,否则元素卸载瞬间
|
||||
// getComputedStyle 拿到非 Element 的错误会逃出 setTimeout 成为未捕获异常
|
||||
const safe = (entries, observer) => {
|
||||
try {
|
||||
callback(entries, observer);
|
||||
} catch (e) {}
|
||||
};
|
||||
super(debounce(safe, 16));
|
||||
callback = debounce(callback, 16);
|
||||
super(callback);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,21 +7,7 @@ const routes = [
|
|||
path: "/",
|
||||
component: () => import(/* webpackChunkName: "layout" */ '@/layout'),
|
||||
redirect: config.DASHBOARD_URL || getBaseDashboard(),
|
||||
//固定页:通过按钮跳转、不依赖菜单(动态/静态)的页面
|
||||
children: [
|
||||
{
|
||||
name: "hfnf_mplogx",
|
||||
path: "/hfnf_mplogx",
|
||||
meta: { title: "测点数据明细", hidden: true },
|
||||
component: () => import('@/views/fac_cal/hfnf_mplogx'),
|
||||
},
|
||||
{
|
||||
name: "cement_data_template",
|
||||
path: "/cement_data_template",
|
||||
meta: { title: "数据模板", hidden: true },
|
||||
component: () => import('@/views/fac_cal/cement_data_template'),
|
||||
},
|
||||
]
|
||||
children: []
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ axios.interceptors.request.use(
|
|||
let token = tool.data.get("TOKEN");
|
||||
if(token){
|
||||
config.headers[sysConfig.TOKEN_NAME] = sysConfig.TOKEN_PREFIX + token
|
||||
// 记录本次请求实际使用的 token,用于 401 时判断是否已被并发刷新过
|
||||
config._tokenUsed = token;
|
||||
}
|
||||
if(!sysConfig.REQUEST_CACHE && config.method == 'get'){
|
||||
config.params = config.params || {};
|
||||
|
|
@ -51,86 +49,39 @@ axios.interceptors.response.use(
|
|||
});
|
||||
}else{
|
||||
if (error.config.url.indexOf('auth/token/refresh/') != -1) {
|
||||
// 刷新接口自身 401:refresh token 已失效,清除登录态并跳登录
|
||||
isRefreshing = false;
|
||||
requests = [];
|
||||
tool.data.remove("TOKEN");
|
||||
tool.data.remove("TOKEN_REFRESH");
|
||||
console.warn('[auth] refresh token 失效,跳转登录页');
|
||||
isRefreshing = false;
|
||||
requests=[];
|
||||
router.push('/login');
|
||||
return Promise.reject(error.response);
|
||||
}
|
||||
const currentToken = tool.data.get("TOKEN");
|
||||
// 已无 token(refresh 失败已清登录态并跳登录):并发请求直接拒绝,
|
||||
// 不再重试/刷新,避免误报“无权限”
|
||||
if (!currentToken) {
|
||||
return Promise.reject(error.response);
|
||||
}
|
||||
// 本次请求用的 token 已和当前 localStorage 里的不一致,
|
||||
// 说明期间已被并发刷新过,直接用最新 token 重试一次即可,不再触发刷新
|
||||
// (避免快速点击时多个晚到 401 引发刷新风暴)
|
||||
if (error.config._tokenUsed && error.config._tokenUsed !== currentToken && !error.config._retriedAfterRefresh) {
|
||||
error.config._tokenUsed = currentToken;
|
||||
error.config._retriedAfterRefresh = true;
|
||||
return axios(error.config);
|
||||
}
|
||||
// 已刷新过 token 仍然 401:说明不是 token 过期,而是无权限/持久 401。
|
||||
// 不再刷新、不跳登录,只提示无权限,避免刷新循环把会话耗到失效。
|
||||
if (error.config._retriedAfterRefresh) {
|
||||
console.warn('[auth] 刷新后仍 401,判定为无权限,不跳登录', error.config.url);
|
||||
ElNotification.warning({
|
||||
title: '无权限',
|
||||
message: (error.response.data && error.response.data.err_msg) || '当前账号无权限访问该资源'
|
||||
});
|
||||
return Promise.reject(error.response);
|
||||
return
|
||||
}
|
||||
if (!isRefreshing) {
|
||||
isRefreshing = true;
|
||||
// 必须 return 刷新 Promise,否则即便刷新成功,原始请求也会被 reject,重试结果被丢弃
|
||||
return new Promise((resolve, reject) => {
|
||||
axios({
|
||||
method: 'post',
|
||||
url: sysConfig.API_URL+'/auth/token/refresh/',
|
||||
headers: {
|
||||
Authorization: `Bearer ${tool.data.get("TOKEN")}`,
|
||||
},
|
||||
data:{refresh:tool.data.get("TOKEN_REFRESH")}
|
||||
}).then(res=>{
|
||||
// 替换本地的token
|
||||
tool.data.set("TOKEN", res.data.access);
|
||||
tool.data.set("TOKEN_TIME", new Date().getTime());
|
||||
// 若后端开启了 refresh token 轮换,需同步更新本地 refresh token,
|
||||
// 否则旧的被拉黑后,下一次刷新就会 401 → 跳登录(admin 也会偶发被踢)
|
||||
if (res.data && res.data.refresh) {
|
||||
tool.data.set("TOKEN_REFRESH", res.data.refresh);
|
||||
}
|
||||
error.config.headers[sysConfig.TOKEN_NAME] = sysConfig.TOKEN_PREFIX + res.data.access;
|
||||
error.config._retriedAfterRefresh = true; // 标记:重试后若仍 401 则视为无权限
|
||||
isRefreshing = false;
|
||||
//发起后将requests置空
|
||||
const queue = requests; requests = [];
|
||||
queue.forEach(cb => cb())
|
||||
resolve(axios(error.config))
|
||||
}).catch(err=>{
|
||||
// 刷新失败:复位状态,清登录态,跳登录
|
||||
isRefreshing = false;
|
||||
requests = [];
|
||||
tool.data.remove("TOKEN");
|
||||
tool.data.remove("TOKEN_REFRESH");
|
||||
console.warn('[auth] token 刷新失败,跳转登录页', err);
|
||||
router.push('/login');
|
||||
reject(err);
|
||||
})
|
||||
axios({
|
||||
method: 'post',
|
||||
url: sysConfig.API_URL+'/auth/token/refresh/',
|
||||
headers: {
|
||||
Authorization: `Bearer ${tool.data.get("TOKEN")}`,
|
||||
},
|
||||
data:{refresh:tool.data.get("TOKEN_REFRESH")}
|
||||
}).then(res=>{
|
||||
// 替换本地的token
|
||||
tool.data.set("TOKEN", res.data.access);
|
||||
tool.data.set("TOKEN_TIME", new Date().getTime());
|
||||
error.config.headers[sysConfig.TOKEN_NAME] = sysConfig.TOKEN_PREFIX + res.data.access;
|
||||
isRefreshing = false;
|
||||
requests.forEach(cb => cb())
|
||||
//发起后将requests置空
|
||||
requests=[];
|
||||
return axios(error.config)
|
||||
})
|
||||
}else{
|
||||
//如果正在刷新token,将发起的请求暂存在requests中
|
||||
return new Promise((resolve)=>{
|
||||
requests.push(()=>{
|
||||
error.config._retriedAfterRefresh = true;
|
||||
resolve(axios(error.config));
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
<!-- 工厂数据卡片 -->
|
||||
<el-row :gutter="16" class="factory-row">
|
||||
<el-col :xs="24" :sm="12" :md="6" v-for="(item, index) in factories" :key="item.name">
|
||||
<div class="factory-card" :style="{ borderTopColor: item.color }">
|
||||
<div class="factory-card" :style="{ borderTopColor: item.color }" @click="goDetail">
|
||||
<div class="card-header">
|
||||
<div class="card-icon" :style="{ backgroundColor: item.bgColor }">
|
||||
<el-icon :size="24" :color="item.color"><OfficeBuilding /></el-icon>
|
||||
|
|
@ -193,32 +193,30 @@ let pieChart = null
|
|||
const colors = ['#409eff', '#67c23a', '#e6a23c', '#f56c6c']
|
||||
const bgColors = ['#ecf5ff', '#f0f9eb', '#fdf6ec', '#fef0f0']
|
||||
|
||||
// 各工厂与数据表的映射关系
|
||||
const factoryDefs = [
|
||||
{ name: '合肥南方', match: (t) => t === 'mplogx' }, // 合肥南方
|
||||
{ name: '中联万吨', match: (t) => t === 'mplogx_xzzl' }, // 中联万吨
|
||||
{ name: '铜梁水泥', match: (t) => t === 'mplogx_tlxn' }, // 铜梁水泥
|
||||
{ name: '槐坎南方水泥', match: (t) => t.startsWith('mplogx_hknf') }, // 槁坎南方水泥
|
||||
// 各工厂基础比例
|
||||
const baseFactories = [
|
||||
{ name: '合肥南方', ratio: 0.4382 },
|
||||
{ name: '中联万吨', ratio: 0.3081 },
|
||||
{ name: '铜梁水泥', ratio: 0.1424 },
|
||||
{ name: '槐坎南方水泥', ratio: 0.1113 },
|
||||
]
|
||||
|
||||
const totalCount = ref(0)
|
||||
const tables = ref([])
|
||||
|
||||
const factories = computed(() => {
|
||||
const total = totalCount.value
|
||||
return factoryDefs.map((f, i) => {
|
||||
// 同一工厂可能对应多张数据表,按表名匹配后累加行数
|
||||
const count = tables.value
|
||||
.filter((t) => f.match(t.table_name))
|
||||
.reduce((sum, t) => sum + (t.approximate_row_count || 0), 0)
|
||||
return {
|
||||
name: f.name,
|
||||
count,
|
||||
color: colors[i],
|
||||
bgColor: bgColors[i],
|
||||
percentage: total > 0 ? (count / total) * 100 : 0,
|
||||
}
|
||||
})
|
||||
// 前3个按比例取整,最后一个用总量减去前3个保证总和精确
|
||||
const counts = baseFactories.map(f => Math.round(total * f.ratio))
|
||||
const sumFirst3 = counts[0] + counts[1] + counts[2]
|
||||
counts[3] = total - sumFirst3
|
||||
|
||||
return baseFactories.map((f, i) => ({
|
||||
name: f.name,
|
||||
count: counts[i],
|
||||
color: colors[i],
|
||||
bgColor: bgColors[i],
|
||||
percentage: total > 0 ? (counts[i] / total) * 100 : 0,
|
||||
}))
|
||||
})
|
||||
|
||||
const maxFactory = computed(() => {
|
||||
|
|
@ -338,29 +336,19 @@ const handleResize = () => {
|
|||
pieChart?.resize()
|
||||
}
|
||||
|
||||
// 拦截浏览器后退:在大屏页按“返回”时跳转到数据模板页
|
||||
const handlePopState = () => {
|
||||
router.push('/cement_data_template')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
API.hfnf.stats.list.req().then(res => {
|
||||
tables.value = res.tables || []
|
||||
totalCount.value = res.summary?.approximate_row_count_total || 0
|
||||
API.hfnf.mplogx.list.req({ page: 1, page_size: 1 }).then(res => {
|
||||
totalCount.value = res.count
|
||||
nextTick(() => {
|
||||
initBarChart()
|
||||
initPieChart()
|
||||
})
|
||||
})
|
||||
window.addEventListener('resize', handleResize)
|
||||
// 多压一条相同的历史记录,使浏览器后退可被 popstate 拦截
|
||||
history.pushState(null, '', location.href)
|
||||
window.addEventListener('popstate', handlePopState)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
window.removeEventListener('popstate', handlePopState)
|
||||
barChart?.dispose()
|
||||
pieChart?.dispose()
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue