修改箭头颜色逻辑成本费用相关
This commit is contained in:
@@ -1,245 +0,0 @@
|
|||||||
# 驾驶舱:导航状态管理方案
|
|
||||||
|
|
||||||
## 需求概述
|
|
||||||
|
|
||||||
1. **页面通过菜单进入**:不清空 sessionStorage,使用新日期
|
|
||||||
2. **页面带参数跳转**:记录 sessionStorage
|
|
||||||
3. **sessionStorage 作为容器**:
|
|
||||||
- 点击跳转(非菜单点击)→ sessionStorage 多一条
|
|
||||||
- 点击返回 → sessionStorage 少一条
|
|
||||||
- 返回到最开始的页面 → sessionStorage 为空
|
|
||||||
- sessionStorage 为空 → 返回按钮消失
|
|
||||||
4. **多页面跳转支持**:A 可跳到 E,B 也可跳到 E,返回时能回到来源页面
|
|
||||||
|
|
||||||
## 方案设计:导航栈 + 页面状态分离
|
|
||||||
|
|
||||||
### sessionStorage 结构
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 导航栈:记录访问路径
|
|
||||||
key: 'cockpit_nav_stack'
|
|
||||||
value: [
|
|
||||||
{ path: '/operatingRevenue', timestamp: 123456 },
|
|
||||||
{ path: '/operatingRevenueBase', timestamp: 123457 }
|
|
||||||
]
|
|
||||||
|
|
||||||
// 各页面状态:记录业务数据
|
|
||||||
key: 'cockpit_nav_state_/operatingRevenue'
|
|
||||||
value: { dateData: {...}, factory: 'xxx', toPage: '/operatingRevenueBase' }
|
|
||||||
```
|
|
||||||
|
|
||||||
### 核心 API
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import {
|
|
||||||
pushNavigation, // 跳转前调用,将当前页面入栈
|
|
||||||
popNavigation, // 返回时调用,出栈并返回前一个页面
|
|
||||||
getNavStackLength, // 获取栈长度(用于返回按钮显示)
|
|
||||||
clearNavigation, // 菜单进入时清空所有(会触发 navigation-cleared 事件)
|
|
||||||
saveNavigationState, // 保存页面状态
|
|
||||||
consumeNavigationState // 消费页面状态
|
|
||||||
} from '@/utils/navigationReturnState';
|
|
||||||
```
|
|
||||||
|
|
||||||
### 核心流程
|
|
||||||
|
|
||||||
| 场景 | 操作 | 结果 |
|
|
||||||
|------|------|------|
|
|
||||||
| **菜单进入** | `clearNavigation()` | 清空栈和所有状态,触发 `navigation-cleared` 事件 |
|
|
||||||
| **A 跳转 B** | `pushNavigation('/A')` + `saveNavigationState('/A', {...})` | 栈长度 +1 |
|
|
||||||
| **B 返回 A** | `popNavigation()` → 跳转 | 栈长度 -1,状态自动恢复 |
|
|
||||||
| **栈为空** | 返回按钮隐藏 | 无历史记录 |
|
|
||||||
|
|
||||||
### 跳转时的调用示例
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// A 页面跳转 B
|
|
||||||
const currentPath = this.$route.path;
|
|
||||||
const state = {
|
|
||||||
dateData: { startTime: this.dateData.startTime, endTime: this.dateData.endTime },
|
|
||||||
factory: this.$route.query.factory,
|
|
||||||
toPage: '/B' // 要跳转的目标页面
|
|
||||||
};
|
|
||||||
pushNavigation(currentPath, state); // 入栈
|
|
||||||
saveNavigationState(currentPath, state); // 保存状态
|
|
||||||
this.$router.push({ path: '/B', query: {...} });
|
|
||||||
```
|
|
||||||
|
|
||||||
### 返回按钮逻辑(noRouterHeader.vue)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// data
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
navStackLength: 0, // 存储栈长度
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// computed:基于 navStackLength 显示按钮
|
|
||||||
computed: {
|
|
||||||
showReturnBtn() {
|
|
||||||
return this.navStackLength > 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// watch:监听菜单进入和路由变化
|
|
||||||
watch: {
|
|
||||||
'$route.query.menu': {
|
|
||||||
immediate: true,
|
|
||||||
handler(menuFlag) {
|
|
||||||
if (menuFlag === '1') {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.updateNavStackLength();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
$route() {
|
|
||||||
this.updateNavStackLength();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 生命周期:监听 navigation-cleared 事件
|
|
||||||
mounted() {
|
|
||||||
this.updateNavStackLength();
|
|
||||||
window.addEventListener('navigation-cleared', this.handleNavigationCleared);
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
window.removeEventListener('navigation-cleared', this.handleNavigationCleared);
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
updateNavStackLength() {
|
|
||||||
this.navStackLength = getNavStackLength();
|
|
||||||
},
|
|
||||||
handleNavigationCleared() {
|
|
||||||
this.updateNavStackLength(); // 收到清空事件后立即更新
|
|
||||||
},
|
|
||||||
handleReturn() {
|
|
||||||
this.$emit('before-return');
|
|
||||||
const prev = popNavigation(); // 出栈
|
|
||||||
if (prev && prev.path) {
|
|
||||||
this.$router.push(prev.path);
|
|
||||||
} else {
|
|
||||||
this.$router.go(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 列表页 mounted 时恢复状态
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
mounted() {
|
|
||||||
// 菜单进入时清空导航栈
|
|
||||||
if (this.$route.query.menu === '1') {
|
|
||||||
clearNavigation();
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentPath = this.$route.path;
|
|
||||||
const saved = consumeNavigationState(currentPath);
|
|
||||||
if (saved && saved.dateData) {
|
|
||||||
this.dateData = saved.dateData;
|
|
||||||
this.getData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 涉及文件
|
|
||||||
|
|
||||||
### 工具类
|
|
||||||
|
|
||||||
- `src/utils/navigationReturnState.js`:
|
|
||||||
- 提供导航栈管理 API
|
|
||||||
- `clearNavigation()` 执行后触发 `navigation-cleared` 事件
|
|
||||||
|
|
||||||
### 公共组件
|
|
||||||
|
|
||||||
- `src/views/home/components/noRouterHeader.vue`:
|
|
||||||
- `showReturnBtn` 根据 `navStackLength` 显示/隐藏返回按钮
|
|
||||||
- `handleNavigationCleared` 监听 `navigation-cleared` 事件
|
|
||||||
- `updateNavStackLength` 更新栈长度
|
|
||||||
- `handleReturn` 出栈并跳转到前一个页面
|
|
||||||
|
|
||||||
- `src/layout/components/AppMain.vue`:
|
|
||||||
- `key` 使用 `$route.fullPath`(包含 query 参数),确保组件能正确刷新
|
|
||||||
|
|
||||||
- `src/layout/components/Sidebar/Link.vue`:
|
|
||||||
- 点击菜单时自动添加 `menu=1` 和 `_t` 时间戳参数
|
|
||||||
|
|
||||||
### 跳转组件(点击跳转)
|
|
||||||
|
|
||||||
| 文件 | 改动 |
|
|
||||||
|------|------|
|
|
||||||
| `operatingLineBarSale.vue` | 跳转前调用 `pushNavigation` + `saveNavigationState` |
|
|
||||||
| `yearRelatedMetrics.vue` | 跳转前调用 `pushNavigation` + `saveNavigationState` |
|
|
||||||
| `monthlyRelatedMetrics.vue` | 跳转前调用 `pushNavigation` + `saveNavigationState` |
|
|
||||||
|
|
||||||
### 详情页
|
|
||||||
|
|
||||||
| 文件 | 改动 |
|
|
||||||
|------|------|
|
|
||||||
| `operatingRevenueBase.vue` | `@before-return` 时无需额外保存(已由跳转组件保存) |
|
|
||||||
| `salesVolumeAnalysisBase.vue` | 同上 |
|
|
||||||
| `unitPriceAnalysisBase.vue` | 同上 |
|
|
||||||
|
|
||||||
### 列表页(菜单入口)
|
|
||||||
|
|
||||||
| 文件 | 改动 |
|
|
||||||
|------|------|
|
|
||||||
| `operatingRevenue.vue` | `mounted` 时检测 `menu=1` 调用 `clearNavigation()`,并调用 `consumeNavigationState` 恢复状态 |
|
|
||||||
| `totalProfit.vue` | 同上 |
|
|
||||||
| `salesVolumeAnalysis.vue` | 同上 |
|
|
||||||
| `unitPriceAnalysis.vue` | 同上 |
|
|
||||||
| `expenseAnalysis.vue` | 同上 |
|
|
||||||
| `grossMargin.vue` | 同上 |
|
|
||||||
| `fullCostAnalysis.vue` | 同上 |
|
|
||||||
| `electricityCostAnalysis.vue` | 同上 |
|
|
||||||
| `inputOutputRatio.vue` | 同上 |
|
|
||||||
| `netPriceAnalysis.vue` | 同上 |
|
|
||||||
| `operatingProfit.vue` | 同上 |
|
|
||||||
| `procurementGainAnalysis.vue` | 同上 |
|
|
||||||
| `rawSheetYield.vue` | 同上 |
|
|
||||||
| `productionCostAnalysis.vue` | 同上 |
|
|
||||||
| `depreciationAnalysis.vue` | 同上 |
|
|
||||||
| `inventoryAnalysis.vue` | 同上 |
|
|
||||||
| `accountsReceivable.vue` | 同上 |
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **菜单进入清空**:需要在页面入口处调用 `clearNavigation()`
|
|
||||||
2. **状态键名**:使用页面路径作为键名,如 `/operatingRevenue`
|
|
||||||
3. **consume 即删除**:`consumeNavigationState` 读取后立即删除
|
|
||||||
4. **返回按钮同步**:通过 `navigation-cleared` 事件确保 sessionStorage 清空后返回按钮立即隐藏
|
|
||||||
5. **多页面跳转**:导航栈记录完整路径链,支持任意页面间的跳转和返回
|
|
||||||
6. **组件刷新**:AppMain 的 key 使用 fullPath,确保带参数的路由能正确刷新组件
|
|
||||||
7. **菜单参数**:Link.vue 自动添加 `menu=1` 和 `_t` 时间戳,确保每次 URL 不同
|
|
||||||
8. **额外字段配置**:`navigationExtraFields` 必须配置(重要!)
|
|
||||||
- 如果页面需要额外参数(如 `meterialName`)恢复,必须在组件 `data` 中配置 `navigationExtraFields`
|
|
||||||
- 格式:`navigationExtraFields: { 字段名: 默认值 }`
|
|
||||||
- 示例:`navigationExtraFields: { meterialName: '氢氧化铝' }`
|
|
||||||
- 缺少此配置会导致从详情页返回时额外参数无法恢复
|
|
||||||
9. **getData 调用条件**:mixin 回调中 `getData()` 的调用条件
|
|
||||||
- 原条件:`if (factory != null && dateData && dateData.startTime != null)`
|
|
||||||
- 问题:从浏览器直接打开页面时,`dateData.startTime` 为 null,导致 `getData()` 不执行
|
|
||||||
- 建议条件:`if (factory != null)` 只要 factory 有值就获取数据
|
|
||||||
10. **B→C→B 返回场景分析**:
|
|
||||||
| 进入 B 方式 | URL 参数 | sessionStorage | 状态恢复来源 | 备注 |
|
|
||||||
|-------------|----------|-----------------|--------------|------|
|
|
||||||
| 菜单直接进入 | 无 | 清空 | store 默认值 | 无返回按钮 |
|
|
||||||
| 从 A 页面跳转 | 有 | 有 | URL 参数 | 需配置 `navigationExtraFields` |
|
|
||||||
| 从 C 返回 B | 无 | 有 | sessionStorage | 需配置 `navigationExtraFields` |
|
|
||||||
- 从浏览器直接打开 B → 跳转 C → C 返回 B 时:`dateData.startTime` 可能为 null,需放宽 `getData()` 调用条件
|
|
||||||
11. **mixin 使用方式**:`mixins: [navigationStateMixin]` 后,需在组件 `created` 钩子中手动调用 `_initNavigationStateMixin(callback)`
|
|
||||||
- mixin **不自动执行** `created` 钩子,避免 `_navigationStateInitialized` 标记冲突
|
|
||||||
- callback 回调参数:`{ factory, dateData, ...extraFields }`
|
|
||||||
- 示例:
|
|
||||||
```javascript
|
|
||||||
created() {
|
|
||||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
|
||||||
if (factory != null && dateData && dateData.startTime != null) {
|
|
||||||
this.getData();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<!-- 本月数值:动态绑定颜色 -->
|
<!-- 本月数值:动态绑定颜色 -->
|
||||||
<div class="number" :style="{ color: getColor(item.currentValue, item.targetValue) }">
|
<div class="number" :style="{ color: getColor(item.thbe) }">
|
||||||
{{ item.currentValue }}
|
{{ item.currentValue }}
|
||||||
</div>
|
</div>
|
||||||
<div class="title">{{currentTap==='month'?'本月':'本年'}}</div>
|
<div class="title">{{currentTap==='month'?'本月':'本年'}}</div>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<!-- 环比额:计算差值并动态绑定颜色 -->
|
<!-- 环比额:计算差值并动态绑定颜色 -->
|
||||||
<div class="number" :style="{ color: getColor(item.currentValue, item.targetValue) }">
|
<div class="number" :style="{ color: getColor(item.thbe) }">
|
||||||
{{ item.thbe }}
|
{{ item.thbe }}
|
||||||
</div>
|
</div>
|
||||||
<div class="title">环比额</div>
|
<div class="title">环比额</div>
|
||||||
@@ -126,18 +126,11 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 颜色判断:本月 >= 上月 绿色,否则 橙色
|
// 颜色判断:本月 >= 上月 绿色,否则 橙色
|
||||||
getColor(current, target) {
|
getColor(thbe) {
|
||||||
return current >= target
|
return thbe < 0
|
||||||
? "rgba(98, 213, 180, 1)" // 绿色:增长或持平
|
? "rgba(98, 213, 180, 1)" // 绿色
|
||||||
: "rgba(249, 164, 74, 1)"; // 橙色:下降
|
: "rgba(249, 164, 74, 1)"; // 橙色
|
||||||
},
|
}
|
||||||
|
|
||||||
// 计算环比额(本月 - 上月),保留一位小数
|
|
||||||
// getData(current, target) {
|
|
||||||
// const diff = current - target;
|
|
||||||
// // 正数加"+"号,负数和零保持原样
|
|
||||||
// return diff > 0 ? `+${diff.toFixed(1)}` : diff.toFixed(1);
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mom">
|
<div class="mom">
|
||||||
环比{{ monthData?.momRate }}%
|
环比{{ monthData?.momRate }}%
|
||||||
<img v-if="monthData?.momRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
|
<img v-if="monthData?.momRate < 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
|
||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mom">
|
<div class="mom">
|
||||||
同比{{ ytdData?.yoyRate || 0 }}%
|
同比{{ ytdData?.yoyRate || 0 }}%
|
||||||
<img v-if="ytdData?.yoyRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
|
<img v-if="ytdData?.yoyRate < 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
|
||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style='font-size: 16px;position: absolute;right: 20px;top:10px'>
|
<div style='font-size: 16px;position: absolute;right: 20px;top:10px'>
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{detailData.rate}}%</span></span>
|
<span>完成率:<span style='color: #0B58FF;'>{{detailData.rate}}%</span></span>
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:detailData.diff>=0?'#30B590':'#FF9423'}" >{{detailData.diff}}</span></span>
|
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:flag>0?'#30B590':'#FF9423'}" >{{detailData.diff}}</span></span>
|
||||||
</div>
|
</div>
|
||||||
<div :ref="refName" id="coreLineChart" style="width: 100%; height: 210px;"></div>
|
<div :ref="refName" id="coreLineChart" style="width: 100%; height: 210px;"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,6 +18,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
myChart: null, // 存储图表实例,避免重复创建
|
myChart: null, // 存储图表实例,避免重复创建
|
||||||
|
flag: 0,
|
||||||
resizeHandler: null, // 窗口resize事件处理器
|
resizeHandler: null, // 窗口resize事件处理器
|
||||||
isMounted: false // 图表挂载标志,避免过早执行
|
isMounted: false // 图表挂载标志,避免过早执行
|
||||||
};
|
};
|
||||||
@@ -90,7 +91,8 @@ export default {
|
|||||||
this.myChart = echarts.init(chartDom);
|
this.myChart = echarts.init(chartDom);
|
||||||
const diff = this.detailData.diff || 0
|
const diff = this.detailData.diff || 0
|
||||||
const rate = this.detailData.rate || 0
|
const rate = this.detailData.rate || 0
|
||||||
const flagValue = (this.detailData.rate>=100?1:0) || 0
|
const flagValue = this.detailData.rate>=100?1:0
|
||||||
|
this.flag = flagValue
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mom">
|
<div class="mom">
|
||||||
环比{{ formatRate(factoryData?.thb) }}%
|
环比{{ formatRate(factoryData?.thb) }}%
|
||||||
<img v-if="factoryData?.thb >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="上升">
|
<img v-if="factoryData?.thb < 0" class="arrow" src="../../../assets/img/topArrow.png" alt="上升">
|
||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mom">
|
<div class="mom">
|
||||||
同比{{ formatRate(factoryData?.thb) }}%
|
同比{{ formatRate(factoryData?.thb) }}%
|
||||||
<img v-if="factoryData?.thb >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="上升">
|
<img v-if="factoryData?.thb < 0" class="arrow" src="../../../assets/img/topArrow.png" alt="上升">
|
||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user