页面路由跳转返回后数据回显,记住原先的状态
This commit is contained in:
4
.env.dev
4
.env.dev
@@ -10,11 +10,11 @@ VUE_APP_TITLE = 洛玻集团驾驶舱
|
||||
# VUE_APP_BASE_API = 'http://172.16.33.83:7070'
|
||||
|
||||
# 杨姗姗
|
||||
VUE_APP_BASE_API = 'http://172.16.20.218:8080'
|
||||
# VUE_APP_BASE_API = 'http://172.16.20.218:8080'
|
||||
# 小田
|
||||
# VUE_APP_BASE_API = 'http://172.16.19.232:7070'
|
||||
# 测试
|
||||
# VUE_APP_BASE_API = 'http://192.168.0.35:8080'
|
||||
VUE_APP_BASE_API = 'http://192.168.0.35:8080'
|
||||
# 闫阳
|
||||
# VUE_APP_BASE_API = 'http://172.16.19.131:7070'
|
||||
|
||||
|
||||
245
docs/驾驶舱-路由返回状态保持.md
Normal file
245
docs/驾驶舱-路由返回状态保持.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# 驾驶舱:导航状态管理方案
|
||||
|
||||
## 需求概述
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
@@ -20,7 +20,8 @@ export default {
|
||||
return this.$store.state.tagsView.cachedViews
|
||||
},
|
||||
key() {
|
||||
return this.$route.path
|
||||
// 包含 query 参数,确保带参数的路由能正确刷新组件
|
||||
return this.$route.fullPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,10 @@ export default {
|
||||
}
|
||||
}
|
||||
return {
|
||||
to: to
|
||||
to: {
|
||||
path: to,
|
||||
query: { menu: '1', _t: Date.now() } // 菜单入口标记,_t确保每次URL不同,触发组件更新
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,9 +304,11 @@ Router.prototype.push = function push(location) {
|
||||
return routerPush.call(this, location).catch(err => err)
|
||||
}
|
||||
|
||||
export default new Router({
|
||||
const router = new Router({
|
||||
base: process.env.VUE_APP_APP_NAME ? process.env.VUE_APP_APP_NAME : "/",
|
||||
mode: 'hash', // 去掉url中的#
|
||||
scrollBehavior: () => ({ y: 0 }),
|
||||
routes: constantRoutes
|
||||
})
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
219
src/utils/mixins/navigationStateMixin.js
Normal file
219
src/utils/mixins/navigationStateMixin.js
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* 驾驶舱页面导航状态恢复 Mixin
|
||||
* 用于:从 C 页面返回时恢复状态(dateData、factory)
|
||||
* 使用方式:在组件中 import 并添加到 mixins 数组
|
||||
*
|
||||
* 3 种进入方式及优先级:
|
||||
* 1. 菜单直接进入 → URL 无参数,sessionStorage 无状态 → 使用默认值
|
||||
* 2. A 页面跳转进入 → URL 有参数 ?factory=9&startTime=... → 使用 URL 参数
|
||||
* 3. C 页面返回进入 → URL 无参数,sessionStorage 有状态 → 使用 sessionStorage 恢复
|
||||
*
|
||||
* 额外字段配置(可选):
|
||||
* 有些页面需要额外字段(如 meterialName),需要在组件 data 中配置:
|
||||
* navigationExtraFields: {
|
||||
* meterialName: '氢氧化铝' // 字段名: 默认值
|
||||
* }
|
||||
* 组件中也需要定义对应的 data 字段:meterialName: '氢氧化铝'
|
||||
*/
|
||||
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 公共状态字段(可被子组件覆盖)
|
||||
dateData: {},
|
||||
factory: null,
|
||||
// 有效基地 ID 列表
|
||||
validFactoryIds: [5, 2, 7, 3, 8, 9, 10, 6],
|
||||
// 额外字段配置(可被子组件覆盖)
|
||||
// 格式:{ 字段名: 默认值 }
|
||||
// 例如:{ meterialName: '氢氧化铝' }
|
||||
navigationExtraFields: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 初始化导航状态恢复
|
||||
* @param {Function} onStateRestored - 状态恢复后的回调(用于调用 getData)
|
||||
*/
|
||||
_initNavigationStateMixin(onStateRestored) {
|
||||
const currentPath = this.$route.path
|
||||
const query = this.$route.query
|
||||
|
||||
// 防止重复初始化 - 使用实例属性作为标记
|
||||
if (this._navigationStateInitialized) {
|
||||
return
|
||||
}
|
||||
this._navigationStateInitialized = true
|
||||
|
||||
// 菜单入口时,先清空 sessionStorage,避免旧数据影响
|
||||
if (query.menu === '1') {
|
||||
clearNavigation()
|
||||
}
|
||||
|
||||
const saved = consumeNavigationState(currentPath)
|
||||
|
||||
// ========== 处理 factory ==========
|
||||
const urlFactoryNum = query.factory != null && query.factory !== '' ? Number(query.factory) : null
|
||||
const hasUrlFactory = urlFactoryNum != null && this.validFactoryIds.includes(urlFactoryNum)
|
||||
let restoredFactory = false
|
||||
|
||||
// 方式 2:URL 有有效参数
|
||||
if (hasUrlFactory) {
|
||||
this.factory = urlFactoryNum
|
||||
restoredFactory = true
|
||||
}
|
||||
|
||||
// 方式 3:从 sessionStorage 恢复
|
||||
if (saved && !restoredFactory) {
|
||||
const savedFactoryNum = Number(saved.factory)
|
||||
if (saved.factory != null && this.validFactoryIds.includes(savedFactoryNum)) {
|
||||
this.factory = savedFactoryNum
|
||||
restoredFactory = true
|
||||
}
|
||||
}
|
||||
|
||||
// 方式 1:使用 store 默认值
|
||||
if (!restoredFactory) {
|
||||
if (this.$store.getters.levelList && this.$store.getters.levelList.length > 0) {
|
||||
const validBases = this.$store.getters.levelList.filter(item => item.id !== 1)
|
||||
if (validBases.length > 0) {
|
||||
this.factory = validBases[0].id
|
||||
} else {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 处理 dateData ==========
|
||||
const hasUrlDateData = (query.startTime && query.endTime) ||
|
||||
(typeof query.dateData === 'string' && query.dateData && query.dateData !== '[object Object]')
|
||||
let restoredDateData = false
|
||||
|
||||
// 方式 2:URL 有参数
|
||||
if (hasUrlDateData) {
|
||||
if (query.startTime && query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(query.startTime),
|
||||
endTime: Number(query.endTime)
|
||||
}
|
||||
} else if (typeof query.dateData === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(query.dateData)
|
||||
if (parsed && parsed.startTime != null && parsed.endTime != null) {
|
||||
this.dateData = parsed
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
restoredDateData = true
|
||||
}
|
||||
|
||||
// 方式 3:从 sessionStorage 恢复
|
||||
if (saved && !restoredDateData) {
|
||||
if (saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
}
|
||||
restoredDateData = true
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 处理额外字段(如 meterialName)==========
|
||||
const extraFields = this.navigationExtraFields || {}
|
||||
const extraFieldKeys = Object.keys(extraFields)
|
||||
const extraFieldResults = {}
|
||||
|
||||
extraFieldKeys.forEach(fieldName => {
|
||||
const defaultValue = extraFields[fieldName]
|
||||
let restored = false
|
||||
|
||||
// 方式 2:URL 有参数
|
||||
if (query[fieldName] != null && query[fieldName] !== '') {
|
||||
this[fieldName] = query[fieldName]
|
||||
restored = true
|
||||
extraFieldResults[fieldName] = { source: 'url', value: query[fieldName] }
|
||||
}
|
||||
|
||||
// 方式 3:从 sessionStorage 恢复
|
||||
if (saved && !restored && saved[fieldName] != null) {
|
||||
this[fieldName] = saved[fieldName]
|
||||
restored = true
|
||||
extraFieldResults[fieldName] = { source: 'sessionStorage', value: saved[fieldName] }
|
||||
}
|
||||
|
||||
// 方式 1:使用默认值
|
||||
if (!restored) {
|
||||
this[fieldName] = defaultValue
|
||||
extraFieldResults[fieldName] = { source: 'default', value: defaultValue }
|
||||
}
|
||||
})
|
||||
|
||||
// 状态恢复完成后,调用回调
|
||||
this.$nextTick(() => {
|
||||
if (onStateRestored) {
|
||||
onStateRestored({
|
||||
factory: this.factory,
|
||||
dateData: this.dateData,
|
||||
...extraFieldResults
|
||||
})
|
||||
} else if (this._onNavigationStateRestored) {
|
||||
this._onNavigationStateRestored({
|
||||
factory: this.factory,
|
||||
dateData: this.dateData,
|
||||
...extraFieldResults
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存当前页面状态到 sessionStorage
|
||||
* @param {string} targetPath - 目标页面路径
|
||||
* @param {object} extraState - 额外要保存的状态(可选,用于动态添加字段)
|
||||
*/
|
||||
_saveNavigationState(targetPath, extraState = {}) {
|
||||
const { pushNavigation, saveNavigationState } = require('@/utils/navigationReturnState')
|
||||
const currentPath = this.$route.path
|
||||
|
||||
// 收集额外字段
|
||||
const extraFields = this.navigationExtraFields || {}
|
||||
const extraFieldData = {}
|
||||
Object.keys(extraFields).forEach(fieldName => {
|
||||
if (this[fieldName] != null) {
|
||||
extraFieldData[fieldName] = this[fieldName]
|
||||
}
|
||||
})
|
||||
|
||||
const state = {
|
||||
dateData: this.dateData && this.dateData.startTime != null ? {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
} : undefined,
|
||||
factory: this.factory,
|
||||
...extraFieldData,
|
||||
...extraState
|
||||
}
|
||||
pushNavigation(currentPath, state)
|
||||
saveNavigationState(currentPath, state)
|
||||
|
||||
// 构建 URL query
|
||||
const query = {
|
||||
factory: this.factory != null ? String(this.factory) : '',
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
|
||||
// 添加额外字段到 query
|
||||
Object.keys(extraFieldData).forEach(fieldName => {
|
||||
query[fieldName] = extraFieldData[fieldName]
|
||||
})
|
||||
|
||||
return {
|
||||
path: targetPath,
|
||||
query
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
169
src/utils/navigationReturnState.js
Normal file
169
src/utils/navigationReturnState.js
Normal file
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* 驾驶舱导航状态管理
|
||||
* 核心逻辑:
|
||||
* - 菜单进入:清空导航栈
|
||||
* - 页面跳转 push:当前页面状态入栈
|
||||
* - 返回 go(-1):出栈,恢复前一个页面状态
|
||||
* - 返回按钮:栈长度 > 0 时显示
|
||||
*/
|
||||
|
||||
const STACK_KEY = 'cockpit_nav_stack';
|
||||
const STATE_PREFIX = 'cockpit_nav_state_';
|
||||
|
||||
/**
|
||||
* 获取导航栈
|
||||
* @returns {Array<{ path: string, state: object }>}
|
||||
*/
|
||||
export function getNavStack() {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(STACK_KEY);
|
||||
if (!raw) return [];
|
||||
return JSON.parse(raw);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取导航栈长度
|
||||
* @returns {number}
|
||||
*/
|
||||
export function getNavStackLength() {
|
||||
return getNavStack().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面跳转时调用:将当前页面状态入栈
|
||||
* @param {string} fromPath - 来源页面路径
|
||||
* @param {object} state - 来源页面的业务状态(如 dateData)
|
||||
*/
|
||||
export function pushNavigation(fromPath, state = {}) {
|
||||
if (!fromPath) return;
|
||||
try {
|
||||
const stack = getNavStack();
|
||||
stack.push({
|
||||
path: fromPath,
|
||||
state,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
sessionStorage.setItem(STACK_KEY, JSON.stringify(stack));
|
||||
console.log('[navigation] push', fromPath, 'stack length:', stack.length);
|
||||
} catch (e) {
|
||||
console.warn('[navigation] push failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回时调用:出栈,返回前一个页面信息
|
||||
* @returns {{ path: string, state: object } | null}
|
||||
*/
|
||||
export function popNavigation() {
|
||||
try {
|
||||
const stack = getNavStack();
|
||||
if (stack.length === 0) return null;
|
||||
const prev = stack.pop();
|
||||
sessionStorage.setItem(STACK_KEY, JSON.stringify(stack));
|
||||
console.log('[navigation] pop', prev.path, 'stack length:', stack.length);
|
||||
return prev;
|
||||
} catch (e) {
|
||||
console.warn('[navigation] pop failed', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空导航栈和所有页面状态
|
||||
*/
|
||||
export function clearNavigation() {
|
||||
try {
|
||||
// 清空栈
|
||||
sessionStorage.removeItem(STACK_KEY);
|
||||
// 清空所有页面状态
|
||||
Object.keys(sessionStorage)
|
||||
.filter(k => k.startsWith(STATE_PREFIX))
|
||||
.forEach(k => sessionStorage.removeItem(k));
|
||||
console.log('[navigation] cleared');
|
||||
// 通知所有监听器
|
||||
window.dispatchEvent(new Event('navigation-cleared'));
|
||||
} catch (e) {
|
||||
console.warn('[navigation] clear failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定页面的状态
|
||||
* @param {string} path - 页面路径
|
||||
* @returns {object | null}
|
||||
*/
|
||||
export function getNavigationState(path) {
|
||||
if (!path) return null;
|
||||
try {
|
||||
const key = STATE_PREFIX + path;
|
||||
const raw = sessionStorage.getItem(key);
|
||||
if (!raw) return null;
|
||||
return JSON.parse(raw);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消费(读取并删除)指定页面的状态
|
||||
* @param {string} path - 页面路径
|
||||
* @returns {object | null}
|
||||
*/
|
||||
export function consumeNavigationState(path) {
|
||||
if (!path) return null;
|
||||
try {
|
||||
const key = STATE_PREFIX + path;
|
||||
const raw = sessionStorage.getItem(key);
|
||||
if (!raw) return null;
|
||||
sessionStorage.removeItem(key);
|
||||
return JSON.parse(raw);
|
||||
} catch (e) {
|
||||
try {
|
||||
sessionStorage.removeItem(STATE_PREFIX + path);
|
||||
} catch (e2) { /* ignore */ }
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存指定页面的状态
|
||||
* @param {string} path - 页面路径
|
||||
* @param {object} state - 业务状态
|
||||
*/
|
||||
export function saveNavigationState(path, state) {
|
||||
if (!path) return;
|
||||
try {
|
||||
const key = STATE_PREFIX + path;
|
||||
sessionStorage.setItem(key, JSON.stringify(state));
|
||||
} catch (e) {
|
||||
console.warn('[navigation] save state failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 兼容旧 API(后续可逐步移除)===========
|
||||
|
||||
/**
|
||||
* @deprecated 使用 pushNavigation 代替
|
||||
*/
|
||||
export function saveNavigationReturnState(scope, state) {
|
||||
console.warn('[navigation] saveNavigationReturnState 已废弃,请使用 pushNavigation + saveNavigationState');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 使用 consumeNavigationState 代替
|
||||
*/
|
||||
export function consumeNavigationReturnState(scope) {
|
||||
console.warn('[navigation] consumeNavigationReturnState 已废弃,请使用 popNavigation + consumeNavigationState');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export function getNavScope(moduleName) {
|
||||
console.warn('[navigation] getNavScope 已废弃,请直接使用路径作为 key');
|
||||
return `nav:${moduleName}`;
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import operatingLineChart from "../accountsReceivableComponents/operatingLineCha
|
||||
import operatingLineChartCumulative from "../accountsReceivableComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getAccountsReceivableData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "AccountsReceivable",
|
||||
components: {
|
||||
@@ -117,6 +118,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -125,7 +130,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
@@ -68,36 +68,6 @@ export default {
|
||||
// 只创建一次图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 绑定点击事件(只绑定一次,永久生效)
|
||||
this.myChart.getZr().on('click', (params) => {
|
||||
console.log('params', params);
|
||||
|
||||
// 提取点击的基地名称
|
||||
// const itemName = params.name;
|
||||
let itemName = undefined
|
||||
// 根据映射表获取对应的序号(未匹配到则返回0或其他默认值)
|
||||
let pointInPixel = [params.offsetX, params.offsetY];
|
||||
if (this.myChart.containPixel('grid', pointInPixel)) {
|
||||
let pointInGrid = this.myChart.convertFromPixel({
|
||||
seriesIndex: 0
|
||||
}, pointInPixel);
|
||||
let xIndex = pointInGrid[0]; //索引
|
||||
let handleIndex = Number(xIndex);
|
||||
let seriesObj = this.myChart.getOption(); //图表object对象
|
||||
var op = this.myChart.getOption();
|
||||
//获得图表中点击的列
|
||||
itemName = op.xAxis[0].data[handleIndex]; //获取点击的列名
|
||||
console.log(itemName, 'monthmonthmonth');
|
||||
console.log(handleIndex, seriesObj);
|
||||
};
|
||||
const baseIndex = this.baseNameToIndexMap[itemName] || 0;
|
||||
|
||||
console.log(`你点击了【${itemName}】(序号:${baseIndex})`);
|
||||
if (itemName === undefined) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
<el-dropdown-item divided @click.native='handleToggle'>切换账号</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<el-button type="text" class="return-btn" :title="'返回'" @click="handleReturn">
|
||||
<!-- <el-button type="text" class="return-btn" :title="'返回'" @click="handleReturn">
|
||||
<svg-icon style="color: #0B58FF;" icon-class="returnIcon" />
|
||||
</el-button>
|
||||
</el-button> -->
|
||||
<el-button type="text" class="screen-btn" :title="isFullScreen ? '退出全屏' : '全屏'" @click="changeFullScreen">
|
||||
<svg-icon style="color: #0B58FF;" v-if="isFullScreen" icon-class="unFullScreenView" />
|
||||
<svg-icon style="color: #0B58FF;" v-else icon-class="fullScreenView" />
|
||||
|
||||
@@ -26,7 +26,7 @@ import bgBaseTongcheng from '@/assets/images/bgBase/桐城.png';
|
||||
import bgBaseLuoyang from '@/assets/images/bgBase/洛阳.png';
|
||||
import bgBaseHefei from '@/assets/images/bgBase/合肥.png';
|
||||
import bgBaseSuqian from '@/assets/images/bgBase/宿迁.png';
|
||||
import bgBaseQinhuangdao from '@/assets/images/bgBase/秦皇岛.png';// 补充:江苏
|
||||
import bgBaseQinhuangdao from '@/assets/images/bgBase/秦皇岛.png';
|
||||
|
||||
export default {
|
||||
name: "BaseSelector",
|
||||
@@ -34,13 +34,11 @@ export default {
|
||||
factory: {
|
||||
type: [String, Number],
|
||||
default: undefined,
|
||||
validator: (val) => [5, 2, 7, 3, 8, 9, 10, 6].includes(val) // 校验序号范围(匹配新的baseNameToIndexMap)
|
||||
validator: (val) => [5, 2, 7, 3, 8, 9, 10, 6].includes(val)
|
||||
}
|
||||
},
|
||||
// 计算属性(响应式,levelList变化时会自动更新)
|
||||
computed: {
|
||||
buttonLevelList() {
|
||||
// 核心:通过$store.getters获取定义的getter
|
||||
let arr = []
|
||||
this.$store.getters.levelList.forEach(item => {
|
||||
this.buttonList.forEach(item2 => {
|
||||
@@ -49,7 +47,12 @@ export default {
|
||||
}
|
||||
})
|
||||
})
|
||||
this.activeButton = arr[0].id
|
||||
const validIds = [5, 2, 7, 3, 8, 9, 10, 6]
|
||||
if (this.factory != null && validIds.includes(Number(this.factory))) {
|
||||
this.activeButton = Number(this.factory)
|
||||
} else if (arr.length > 0) {
|
||||
this.activeButton = arr[0].id
|
||||
}
|
||||
return arr
|
||||
}
|
||||
},
|
||||
@@ -69,22 +72,13 @@ export default {
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 监听父组件传递的factory变化,同步本地选中索引
|
||||
factory: {
|
||||
handler(newVal) {
|
||||
console.log('watch factory=======================:', newVal);
|
||||
if (newVal) {
|
||||
this.activeButton = newVal;
|
||||
if (newVal != null) {
|
||||
this.activeButton = Number(newVal);
|
||||
}
|
||||
// // 根据新的baseNameToIndexMap,找到对应的基地名称
|
||||
// const targetName = Object.keys(this.baseNameToIndexMap).find(name => this.baseNameToIndexMap[name] === newVal);
|
||||
// // 根据名称找到buttonList中的索引
|
||||
// const targetIndex = this.buttonList.indexOf(targetName);
|
||||
// // 合法索引则更新,否则默认选中宜兴
|
||||
// this.activeButton = targetIndex > -1 ? targetIndex : 2;
|
||||
// console.log('当前选中基地:', this.buttonList[this.activeButton], '序号:', newVal);
|
||||
},
|
||||
// immediate: true // 初始化立即执行
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -92,10 +86,6 @@ export default {
|
||||
this.activeButton = id;
|
||||
this.$emit('baseChange', id);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化时触发事件,传递默认选中的宜兴序号(7)
|
||||
// this.$emit('baseChange', 5);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -115,7 +105,7 @@ export default {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
margin-right: -35px; // 重叠效果,可根据需求调整
|
||||
margin-right: -35px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="coreItem">
|
||||
<div @click="handleRoute(item.path)" class="item" v-for="(item, index) in itemList" :key="index">
|
||||
<div @click="handleRouter(item.path)" class="item" v-for="(item, index) in itemList" :key="index">
|
||||
<div class="unit">{{ item.unit }}</div>
|
||||
<div class="item-content">
|
||||
<div class="content-wrapper">
|
||||
@@ -39,6 +39,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
@@ -60,11 +61,23 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleRoute(path) {
|
||||
handleRouter(path) {
|
||||
const toPage = path.split('/').pop();
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
dateData: this.dateData
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="coreItem">
|
||||
<!-- 动态生成每个 item -->
|
||||
<div @click="handleDashboardClick(item.path)" class="item" v-for="(item, index) in itemList" :key="index">
|
||||
<div @click="handleRouter(item.path)" class="item" v-for="(item, index) in itemList" :key="index">
|
||||
<div class="unit">{{ item.name }}</div>
|
||||
<div class="item-content">
|
||||
<!-- 左右内容容器 -->
|
||||
@@ -34,6 +34,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
@@ -69,12 +70,23 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleDashboardClick(path) {
|
||||
handleRouter(path) {
|
||||
const toPage = path.split('/').pop();
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : 5,
|
||||
dateData: this.dateData
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<el-dropdown-item divided @click.native='handleToggle'>切换账号</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<el-button type="text" class="return-btn" :title="'返回'" @click="handleReturn">
|
||||
<el-button type="text" class="return-btn" :title="'返回'" v-if="showReturnBtn" @click="handleReturn">
|
||||
<svg-icon style="color: #0B58FF;" icon-class="returnIcon" />
|
||||
</el-button>
|
||||
<el-button type="text" class="screen-btn" :title="isFullScreen ? '退出全屏' : '全屏'" @click="changeFullScreen">
|
||||
@@ -46,6 +46,7 @@
|
||||
import { mapGetters } from 'vuex'
|
||||
import moment from 'moment'; // 引入moment
|
||||
import {getPath} from "@/utils/ruoyi";
|
||||
import { getNavStackLength, popNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: 'Header',
|
||||
props: {
|
||||
@@ -67,12 +68,30 @@ export default {
|
||||
timeTimer: null,
|
||||
date: Date.now(), // 使用当前时间戳作为初始值
|
||||
activeTime: 1, // 默认月维度(0=日,1=月,2=年)
|
||||
navStackLength: 0,
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
...mapGetters(['nickname']),
|
||||
showReturnBtn() {
|
||||
return this.navStackLength > 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听菜单参数变化,菜单进入时立即更新
|
||||
'$route.query.menu': {
|
||||
immediate: true,
|
||||
handler(menuFlag) {
|
||||
if (menuFlag === '1') {
|
||||
this.$nextTick(() => {
|
||||
this.updateNavStackLength();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
$route() {
|
||||
this.updateNavStackLength();
|
||||
},
|
||||
activeTime(newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
this.date = Date.now();
|
||||
@@ -95,10 +114,18 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化默认日期为当前月份
|
||||
console.log('初始化日期选择器,当前时间戳:', this.date);
|
||||
// 初始化导航栈长度
|
||||
this.updateNavStackLength();
|
||||
// 监听导航清空事件
|
||||
window.addEventListener('navigation-cleared', this.handleNavigationCleared);
|
||||
this.$nextTick(() => {
|
||||
this.emitTimeRange();
|
||||
const hasRange =
|
||||
this.dateData &&
|
||||
((this.dateData.startTime != null && this.dateData.startTime !== 0) ||
|
||||
(this.dateData.endTime != null && this.dateData.endTime !== 0));
|
||||
if (!hasRange) {
|
||||
this.emitTimeRange();
|
||||
}
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
@@ -106,15 +133,32 @@ export default {
|
||||
if (this.timeTimer) {
|
||||
clearInterval(this.timeTimer);
|
||||
}
|
||||
// 移除导航清空事件监听
|
||||
window.removeEventListener('navigation-cleared', this.handleNavigationCleared);
|
||||
},
|
||||
methods: {
|
||||
// 处理导航清空事件
|
||||
handleNavigationCleared() {
|
||||
this.updateNavStackLength();
|
||||
},
|
||||
// 更新导航栈长度
|
||||
updateNavStackLength() {
|
||||
this.navStackLength = getNavStackLength();
|
||||
},
|
||||
changeFullScreen() {
|
||||
this.$emit('screenfullChange');
|
||||
},
|
||||
handleReturn() {
|
||||
console.log('返回上一页');
|
||||
if (this.$router) {
|
||||
this.$router.go(-1);
|
||||
// 出栈获取前一个页面信息
|
||||
const prev = popNavigation();
|
||||
if (prev && prev.path) {
|
||||
// 有前一个页面,跳转到该页面(showReturnBtn 会自动从 sessionStorage 读取最新值)
|
||||
this.$router.push(prev.path);
|
||||
} else {
|
||||
// 没有前一个页面,使用默认返回
|
||||
this.$router.go(-1);
|
||||
}
|
||||
}
|
||||
},
|
||||
async logout() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="coreItem">
|
||||
<div class="item" @click="handleRoute(item.route)" v-for="(item, index) in itemList" :key="index" v-if='index<4'>
|
||||
<div class="item" @click="handleRouter(item.route)" v-for="(item, index) in itemList" :key="index" v-if='index<4'>
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="item-content">
|
||||
<div class="content-wrapper">
|
||||
@@ -50,7 +50,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="itemBottom">
|
||||
<div class="item" v-for="(item, index) in itemList" :key="index" @click="handleRoute(item.route)" v-if='index>=4'>
|
||||
<div class="item" v-for="(item, index) in itemList" :key="index" @click="handleRouter(item.route)" v-if='index>=4'>
|
||||
<div class="unit">{{ item.name }}</div>
|
||||
<div class="item-content">
|
||||
<div class="content-wrapper">
|
||||
@@ -95,6 +95,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
@@ -244,17 +245,26 @@ export default {
|
||||
};
|
||||
});
|
||||
},
|
||||
handleRoute(route) {
|
||||
if (route) {
|
||||
this.$router.push({
|
||||
path: route,
|
||||
query: {
|
||||
// 关键修复4:dateData是对象,需序列化后传递(否则路由query无法正常接收对象)
|
||||
dateData: this.dateData
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
handleRouter(path) {
|
||||
const toPage = path.split('/').pop();
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="coreItem">
|
||||
<div class="item" v-for="(item, index) in itemList" :key="index" @click="handleRouter(item)">
|
||||
<div class="item" v-for="(item, index) in itemList" :key="index" @click="handleRouter(item.path)">
|
||||
<div class="unit">{{ item.unit }}</div>
|
||||
<div class="item-content">
|
||||
<div class="content-wrapper">
|
||||
@@ -44,6 +44,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
@@ -96,16 +97,26 @@ export default {
|
||||
},
|
||||
|
||||
// 处理路由跳转
|
||||
handleRouter(obj) {
|
||||
if (obj.path) {
|
||||
this.$router.push({
|
||||
path: obj.path,
|
||||
query: {
|
||||
dateData:this.dateData
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
handleRouter(path) {
|
||||
const toPage = path.split('/').pop();
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="coreItem">
|
||||
<div class="item" @click="handleItemClick(index)" v-for="(item, index) in itemList"
|
||||
<div class="item" @click="handleRouter(item.route)" v-for="(item, index) in itemList"
|
||||
:key="index">
|
||||
<div class="unit">{{ item.unit }}</div>
|
||||
<div class="item-content">
|
||||
@@ -37,6 +37,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "Container",
|
||||
components: {},
|
||||
@@ -118,19 +119,25 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
handleItemClick(index) {
|
||||
const currentItem = this.itemList[index];
|
||||
console.log(`点击了第${index + 1}个item:`, currentItem.unit);
|
||||
this.$emit('item-click', { index, ...currentItem });
|
||||
this.activeIndex = index;
|
||||
if (currentItem.route) {
|
||||
this.$router.push({
|
||||
path: currentItem.route,
|
||||
query: {
|
||||
dateData: this.dateData
|
||||
}
|
||||
});
|
||||
}
|
||||
handleRouter(path) {
|
||||
const toPage = path.split('/').pop();
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 判断颜色的方法
|
||||
|
||||
@@ -40,6 +40,7 @@ import operatingLineChart from "../depreciationAnalysisComponents/operatingLineC
|
||||
import operatingLineChartCumulative from "../depreciationAnalysisComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getDepreciationAnalysisData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DepreciationAnalysis",
|
||||
components: {
|
||||
@@ -117,6 +118,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -125,7 +130,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
@@ -69,34 +69,34 @@ export default {
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 绑定点击事件(只绑定一次,永久生效)
|
||||
this.myChart.getZr().on('click', (params) => {
|
||||
console.log('params', params);
|
||||
// this.myChart.getZr().on('click', (params) => {
|
||||
// console.log('params', params);
|
||||
|
||||
// 提取点击的基地名称
|
||||
// const itemName = params.name;
|
||||
let itemName = undefined
|
||||
// 根据映射表获取对应的序号(未匹配到则返回0或其他默认值)
|
||||
let pointInPixel = [params.offsetX, params.offsetY];
|
||||
if (this.myChart.containPixel('grid', pointInPixel)) {
|
||||
let pointInGrid = this.myChart.convertFromPixel({
|
||||
seriesIndex: 0
|
||||
}, pointInPixel);
|
||||
let xIndex = pointInGrid[0]; //索引
|
||||
let handleIndex = Number(xIndex);
|
||||
let seriesObj = this.myChart.getOption(); //图表object对象
|
||||
var op = this.myChart.getOption();
|
||||
//获得图表中点击的列
|
||||
itemName = op.xAxis[0].data[handleIndex]; //获取点击的列名
|
||||
console.log(itemName, 'monthmonthmonth');
|
||||
console.log(handleIndex, seriesObj);
|
||||
};
|
||||
const baseIndex = this.baseNameToIndexMap[itemName] || 0;
|
||||
// // 提取点击的基地名称
|
||||
// // const itemName = params.name;
|
||||
// let itemName = undefined
|
||||
// // 根据映射表获取对应的序号(未匹配到则返回0或其他默认值)
|
||||
// let pointInPixel = [params.offsetX, params.offsetY];
|
||||
// if (this.myChart.containPixel('grid', pointInPixel)) {
|
||||
// let pointInGrid = this.myChart.convertFromPixel({
|
||||
// seriesIndex: 0
|
||||
// }, pointInPixel);
|
||||
// let xIndex = pointInGrid[0]; //索引
|
||||
// let handleIndex = Number(xIndex);
|
||||
// let seriesObj = this.myChart.getOption(); //图表object对象
|
||||
// var op = this.myChart.getOption();
|
||||
// //获得图表中点击的列
|
||||
// itemName = op.xAxis[0].data[handleIndex]; //获取点击的列名
|
||||
// console.log(itemName, 'monthmonthmonth');
|
||||
// console.log(handleIndex, seriesObj);
|
||||
// };
|
||||
// const baseIndex = this.baseNameToIndexMap[itemName] || 0;
|
||||
|
||||
console.log(`你点击了【${itemName}】(序号:${baseIndex})`);
|
||||
if (itemName === undefined) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
// console.log(`你点击了【${itemName}】(序号:${baseIndex})`);
|
||||
// if (itemName === undefined) {
|
||||
// return;
|
||||
// }
|
||||
// });
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
|
||||
@@ -40,6 +40,7 @@ import operatingLineChart from "../electricityCostAnalysisComponents/operatingLi
|
||||
import operatingLineChartCumulative from "../electricityCostAnalysisComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getElectricityCostAnalysisData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "electricityCostAnalysis",
|
||||
components: {
|
||||
@@ -117,6 +118,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -125,7 +130,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
@@ -66,8 +66,10 @@ import dataTrend from "../electricityCostAnalysisComponents/dataTrend.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getElectricityCostAnalysisFData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "electricityCostAnalysisBase",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -85,8 +87,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: 5,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -98,6 +99,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -157,14 +164,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import operatingLineBar from './operatingLineBarSale.vue';
|
||||
import operatingLineBar from './operatingLineBarSaleBase.vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
||||
<div class="dashboard left">
|
||||
<div class="title">
|
||||
销量·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard right">
|
||||
<div class="title">
|
||||
单价·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import verticalBarChart from './verticalBarChart.vue'
|
||||
|
||||
// import * as echarts from 'echarts'
|
||||
// import rawItem from './raw-Item.vue'
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, operatingSingleBar, verticalBarChart },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
itemData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
title: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
month: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
itemData: {
|
||||
handler(newValue, oldValue) {
|
||||
// this.updateChart()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
// computed: {
|
||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
||||
// sortedItemData() {
|
||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
||||
// const totalCostItems = this.itemData.filter(item =>
|
||||
// item.name && item.name.includes('总成本')
|
||||
// );
|
||||
// // 过滤出不包含“总成本”的项
|
||||
// const otherItems = this.itemData.filter(item =>
|
||||
// !item.name || !item.name.includes('总成本')
|
||||
// );
|
||||
// // 合并:总成本项在前,其他项在后
|
||||
// return [...totalCostItems, ...otherItems];
|
||||
// }
|
||||
// },
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
|
||||
.scroll-container {
|
||||
/* 1. 固定容器高度:根据页面布局调整(示例300px,超出则滚动) */
|
||||
max-height: 210px;
|
||||
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
|
||||
overflow-y: auto;
|
||||
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
|
||||
overflow-x: hidden;
|
||||
/* 4. 内边距:与标题栏和容器边缘对齐 */
|
||||
padding: 10px 0;
|
||||
|
||||
/* 5. 隐藏滚动条(兼容主流浏览器) */
|
||||
/* Chrome/Safari */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
scrollbar-width: none;
|
||||
/* IE/Edge */
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
width: 382px;
|
||||
height: 205px;
|
||||
background: #F9FCFF;
|
||||
padding: 16px 0 0 10px;
|
||||
|
||||
.title {
|
||||
// width: 190px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
// width: 190px;
|
||||
height: 32px;
|
||||
font-family: YouSheBiaoTiHei;
|
||||
font-size: 32px;
|
||||
color: #0B58FF;
|
||||
line-height: 32px;
|
||||
letter-spacing: 2px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mom {
|
||||
width: 120px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
// .line {
|
||||
// width: 500px;
|
||||
// height: 205px;
|
||||
// background: #F9FCFF;
|
||||
// }
|
||||
|
||||
// .leftTitle {
|
||||
// .item {
|
||||
// width: 67px;
|
||||
// height: 180px;
|
||||
// padding: 37px 23px;
|
||||
// background: #F9FCFF;
|
||||
// font-family: PingFangSC, PingFang SC;
|
||||
// font-weight: 400;
|
||||
// font-size: 18px;
|
||||
// color: #000000;
|
||||
// line-height: 25px;
|
||||
// letter-spacing: 1px;
|
||||
// // text-align: left;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
||||
<!-- <style>
|
||||
/* 全局 tooltip 样式(不使用 scoped,确保生效) */
|
||||
.production-status-chart-tooltip {
|
||||
background: #0a2b4f77 !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.production-status-chart-tooltip * {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style> -->
|
||||
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
myChart: null, // 存储图表实例
|
||||
resizeHandler: null, // 存储resize事件处理函数
|
||||
isMounted: false,
|
||||
// 核心:基地名称与序号的映射表(固定顺序)
|
||||
baseNameToIndexMap: {
|
||||
'宜兴': 7,
|
||||
'漳州': 8,
|
||||
'自贡': 3,
|
||||
'桐城': 2,
|
||||
'洛阳': 9,
|
||||
'合肥': 5,
|
||||
'宿迁': 6,
|
||||
'秦皇岛': 10
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isMounted = true;
|
||||
this.$nextTick(() => {
|
||||
this.initChart(); // 初始化图表(只执行一次)
|
||||
this.updateChart(); // 更新图表数据
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
handler() {
|
||||
if (!this.isMounted) return;
|
||||
console.log(this.chartData, 'chartData');
|
||||
this.updateChart(); // 仅更新数据,不重新创建实例
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理资源
|
||||
this.destroyChart();
|
||||
},
|
||||
methods: {
|
||||
// 初始化图表(只在mounted中执行一次)
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
if (process.env.NODE_ENV === 'development') console.warn('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
};
|
||||
// 绑定resize事件(只绑定一次)
|
||||
window.addEventListener('resize', this.resizeHandler);
|
||||
},
|
||||
|
||||
// 更新图表数据(数据变化时执行)
|
||||
updateChart() {
|
||||
if (!this.myChart) {
|
||||
return; // 实例未初始化则返回
|
||||
}
|
||||
|
||||
const { allPlaceNames,unit, series } = this.chartData || {};
|
||||
const xData = allPlaceNames || [];
|
||||
const chartSeries = series || [];
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 5,
|
||||
right: 20,
|
||||
left: 25,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0],
|
||||
// 可选:X轴标签显示“序号+名称”(如“1 宜兴”)
|
||||
// formatter: (value) => {
|
||||
// const index = this.baseNameToIndexMap[value] || '';
|
||||
// return index ? `${index} ${value}` : value;
|
||||
// }
|
||||
},
|
||||
data: xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: unit,
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } }
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
}
|
||||
],
|
||||
series: chartSeries
|
||||
};
|
||||
|
||||
// 只更新配置,不重新创建实例
|
||||
this.myChart.setOption(option, true); // 第二个参数true表示清空原有配置,避免数据残留
|
||||
},
|
||||
|
||||
// 销毁图表资源
|
||||
destroyChart() {
|
||||
// 移除resize事件
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler);
|
||||
}
|
||||
// 销毁图表实例
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -124,11 +124,27 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleDashboardClick(path) {
|
||||
// 从路径提取详情页名称,如 '/salesVolumeAnalysis/salesVolumeAnalysisBase' -> 'salesVolumeAnalysisBase'
|
||||
const toPage = path.split('/').pop();
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串,时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
dateData: this.dateData
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
||||
<div class="dashboard left">
|
||||
<div class="title">
|
||||
销量·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard right">
|
||||
<div class="title">
|
||||
单价·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import verticalBarChart from './verticalBarChart.vue'
|
||||
|
||||
// import * as echarts from 'echarts'
|
||||
// import rawItem from './raw-Item.vue'
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, operatingSingleBar, verticalBarChart },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
itemData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
title: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
month: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
itemData: {
|
||||
handler(newValue, oldValue) {
|
||||
// this.updateChart()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
// computed: {
|
||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
||||
// sortedItemData() {
|
||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
||||
// const totalCostItems = this.itemData.filter(item =>
|
||||
// item.name && item.name.includes('总成本')
|
||||
// );
|
||||
// // 过滤出不包含“总成本”的项
|
||||
// const otherItems = this.itemData.filter(item =>
|
||||
// !item.name || !item.name.includes('总成本')
|
||||
// );
|
||||
// // 合并:总成本项在前,其他项在后
|
||||
// return [...totalCostItems, ...otherItems];
|
||||
// }
|
||||
// },
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
|
||||
.scroll-container {
|
||||
/* 1. 固定容器高度:根据页面布局调整(示例300px,超出则滚动) */
|
||||
max-height: 210px;
|
||||
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
|
||||
overflow-y: auto;
|
||||
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
|
||||
overflow-x: hidden;
|
||||
/* 4. 内边距:与标题栏和容器边缘对齐 */
|
||||
padding: 10px 0;
|
||||
|
||||
/* 5. 隐藏滚动条(兼容主流浏览器) */
|
||||
/* Chrome/Safari */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
scrollbar-width: none;
|
||||
/* IE/Edge */
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
width: 382px;
|
||||
height: 205px;
|
||||
background: #F9FCFF;
|
||||
padding: 16px 0 0 10px;
|
||||
|
||||
.title {
|
||||
// width: 190px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
// width: 190px;
|
||||
height: 32px;
|
||||
font-family: YouSheBiaoTiHei;
|
||||
font-size: 32px;
|
||||
color: #0B58FF;
|
||||
line-height: 32px;
|
||||
letter-spacing: 2px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mom {
|
||||
width: 120px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
// .line {
|
||||
// width: 500px;
|
||||
// height: 205px;
|
||||
// background: #F9FCFF;
|
||||
// }
|
||||
|
||||
// .leftTitle {
|
||||
// .item {
|
||||
// width: 67px;
|
||||
// height: 180px;
|
||||
// padding: 37px 23px;
|
||||
// background: #F9FCFF;
|
||||
// font-family: PingFangSC, PingFang SC;
|
||||
// font-weight: 400;
|
||||
// font-size: 18px;
|
||||
// color: #000000;
|
||||
// line-height: 25px;
|
||||
// letter-spacing: 1px;
|
||||
// // text-align: left;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
||||
<!-- <style>
|
||||
/* 全局 tooltip 样式(不使用 scoped,确保生效) */
|
||||
.production-status-chart-tooltip {
|
||||
background: #0a2b4f77 !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.production-status-chart-tooltip * {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style> -->
|
||||
@@ -40,6 +40,7 @@ import operatingLineChart from "../expenseAnalysisComponents/operatingLineChart"
|
||||
import operatingLineChartCumulative from "../expenseAnalysisComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getExpenseAnalysisGroupData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -116,6 +117,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -124,7 +129,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
@@ -134,7 +157,6 @@ export default {
|
||||
sort: this.sort,
|
||||
index: undefined,
|
||||
factory: undefined
|
||||
// timeDim: obj.mode
|
||||
}).then((res) => {
|
||||
console.log(res);
|
||||
this.monthData = res.data.month
|
||||
|
||||
@@ -69,8 +69,10 @@ import profitLineChart from "../costComponents/profitLineChart.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getExpenseAnalysisFactoryData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -90,20 +92,25 @@ export default {
|
||||
beilv: 1,
|
||||
month:'',
|
||||
value: 100,
|
||||
dateData: {},
|
||||
index: '总费用',
|
||||
monthData: undefined,
|
||||
ytdData: undefined,
|
||||
monthAnalysis: [],
|
||||
ytdAnalysis: [],
|
||||
trend: [],
|
||||
factory:null
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -169,14 +176,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
@@ -198,8 +197,6 @@ export default {
|
||||
this.monthAnalysis = res.data.monthAnalysis
|
||||
this.ytdAnalysis = res.data.ytdAnalysis
|
||||
this.trend = res.data.trend
|
||||
|
||||
// this.monthData = res.data.month
|
||||
});
|
||||
},
|
||||
|
||||
@@ -208,7 +205,6 @@ export default {
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
// mode: obj.mode,
|
||||
}
|
||||
|
||||
this.getData()
|
||||
@@ -265,8 +261,6 @@ export default {
|
||||
},
|
||||
changeDate(val) {
|
||||
this.date = val;
|
||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
||||
// this.getData()
|
||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||
this.loopTime();
|
||||
} else {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import operatingLineBar from './operatingLineBarSale.vue';
|
||||
import operatingLineBar from './operatingLineBarSaleBase.vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
@@ -93,16 +94,20 @@ export default {
|
||||
if (itemName === undefined) {
|
||||
return;
|
||||
}
|
||||
const dd = this.dateData && typeof this.dateData === 'object' ? this.dateData : {};
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = { dateData: { startTime: dd.startTime, endTime: dd.endTime } };
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串;时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: 'expenseAnalysisBase',
|
||||
query: { // 使用query传递参数(推荐),也可使用params
|
||||
// baseName: itemName,
|
||||
factory: baseIndex,
|
||||
dateData: this.dateData
|
||||
query: {
|
||||
factory: String(baseIndex),
|
||||
startTime: dd.startTime != null ? String(dd.startTime) : '',
|
||||
endTime: dd.endTime != null ? String(dd.endTime) : ''
|
||||
}
|
||||
// 若仍需用base作为参数:
|
||||
// base: itemName,
|
||||
// params: { baseIndex: baseIndex }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
myChart: null, // 存储图表实例
|
||||
resizeHandler: null, // 存储resize事件处理函数
|
||||
isMounted: false, // 图表挂载标志,避免过早执行
|
||||
baseNameToIndexMap: {
|
||||
'宜兴': 7,
|
||||
'漳州': 8,
|
||||
'自贡': 3,
|
||||
'桐城': 2,
|
||||
'洛阳': 9,
|
||||
'合肥': 5,
|
||||
'宿迁': 6,
|
||||
'秦皇岛': 10
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isMounted = true;
|
||||
this.$nextTick(() => {
|
||||
this.initChart(); // 初始化图表(只执行一次)
|
||||
this.updateChart(); // 更新图表数据
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
handler() {
|
||||
if (!this.isMounted) return; // 挂载前保护
|
||||
console.log(this.chartData, 'chartData');
|
||||
this.updateChart(); // 仅更新数据,不重新创建实例
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理资源
|
||||
this.destroyChart();
|
||||
},
|
||||
methods: {
|
||||
// 初始化图表(只在mounted中执行一次)
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
if (process.env.NODE_ENV === 'development') console.warn('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 只创建一次图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
};
|
||||
// 绑定resize事件(只绑定一次)
|
||||
window.addEventListener('resize', this.resizeHandler);
|
||||
},
|
||||
|
||||
// 更新图表数据(数据变化时执行)
|
||||
updateChart() {
|
||||
if (!this.myChart) {
|
||||
return; // 实例未初始化则返回
|
||||
}
|
||||
|
||||
const { allPlaceNames, series } = this.chartData || {};
|
||||
const xData = allPlaceNames || [];
|
||||
const chartSeries = series || [];
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 5,
|
||||
right: 20,
|
||||
left: 25,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0],
|
||||
// 可选:X轴标签显示“序号+名称”(如“1 宜兴”)
|
||||
// formatter: (value) => {
|
||||
// const index = this.baseNameToIndexMap[value] || '';
|
||||
// return index ? `${index} ${value}` : value;
|
||||
// }
|
||||
},
|
||||
data: xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '万元',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: {show:true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } }
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
}
|
||||
],
|
||||
series: chartSeries
|
||||
};
|
||||
|
||||
// 只更新配置,不重新创建实例
|
||||
this.myChart.setOption(option, true); // 第二个参数true表示清空原有配置,避免数据残留
|
||||
},
|
||||
|
||||
// 销毁图表资源
|
||||
destroyChart() {
|
||||
// 移除resize事件
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler);
|
||||
}
|
||||
// 销毁图表实例
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -40,6 +40,7 @@ import operatingLineChartCumulative from "../fullCostAnalysisComponents/operatin
|
||||
|
||||
import { getUnitPriceAnalysisGroupData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -113,6 +114,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -121,7 +126,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData(obj) {
|
||||
|
||||
@@ -66,8 +66,10 @@ import dataTrend from "../fullCostAnalysisComponents/dataTrend.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getUnitPriceAnalysisBaseData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -84,8 +86,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: {},
|
||||
@@ -96,6 +97,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -155,14 +162,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import operatingLineBar from './operatingLineBarSale.vue';
|
||||
import operatingLineBar from './operatingLineBarSaleBase.vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
||||
<div class="dashboard left">
|
||||
<div class="title">
|
||||
销量·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard right">
|
||||
<div class="title">
|
||||
单价·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import verticalBarChart from './verticalBarChart.vue'
|
||||
|
||||
// import * as echarts from 'echarts'
|
||||
// import rawItem from './raw-Item.vue'
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, operatingSingleBar, verticalBarChart },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
itemData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
title: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
month: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
itemData: {
|
||||
handler(newValue, oldValue) {
|
||||
// this.updateChart()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
// computed: {
|
||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
||||
// sortedItemData() {
|
||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
||||
// const totalCostItems = this.itemData.filter(item =>
|
||||
// item.name && item.name.includes('总成本')
|
||||
// );
|
||||
// // 过滤出不包含“总成本”的项
|
||||
// const otherItems = this.itemData.filter(item =>
|
||||
// !item.name || !item.name.includes('总成本')
|
||||
// );
|
||||
// // 合并:总成本项在前,其他项在后
|
||||
// return [...totalCostItems, ...otherItems];
|
||||
// }
|
||||
// },
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
|
||||
.scroll-container {
|
||||
/* 1. 固定容器高度:根据页面布局调整(示例300px,超出则滚动) */
|
||||
max-height: 210px;
|
||||
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
|
||||
overflow-y: auto;
|
||||
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
|
||||
overflow-x: hidden;
|
||||
/* 4. 内边距:与标题栏和容器边缘对齐 */
|
||||
padding: 10px 0;
|
||||
|
||||
/* 5. 隐藏滚动条(兼容主流浏览器) */
|
||||
/* Chrome/Safari */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
scrollbar-width: none;
|
||||
/* IE/Edge */
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
width: 382px;
|
||||
height: 205px;
|
||||
background: #F9FCFF;
|
||||
padding: 16px 0 0 10px;
|
||||
|
||||
.title {
|
||||
// width: 190px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
// width: 190px;
|
||||
height: 32px;
|
||||
font-family: YouSheBiaoTiHei;
|
||||
font-size: 32px;
|
||||
color: #0B58FF;
|
||||
line-height: 32px;
|
||||
letter-spacing: 2px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mom {
|
||||
width: 120px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
// .line {
|
||||
// width: 500px;
|
||||
// height: 205px;
|
||||
// background: #F9FCFF;
|
||||
// }
|
||||
|
||||
// .leftTitle {
|
||||
// .item {
|
||||
// width: 67px;
|
||||
// height: 180px;
|
||||
// padding: 37px 23px;
|
||||
// background: #F9FCFF;
|
||||
// font-family: PingFangSC, PingFang SC;
|
||||
// font-weight: 400;
|
||||
// font-size: 18px;
|
||||
// color: #000000;
|
||||
// line-height: 25px;
|
||||
// letter-spacing: 1px;
|
||||
// // text-align: left;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
||||
<!-- <style>
|
||||
/* 全局 tooltip 样式(不使用 scoped,确保生效) */
|
||||
.production-status-chart-tooltip {
|
||||
background: #0a2b4f77 !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.production-status-chart-tooltip * {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style> -->
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
@@ -96,17 +97,20 @@ export default {
|
||||
if (itemName === undefined) {
|
||||
return;
|
||||
}
|
||||
// 路由跳转时携带序号(或名称+序号)
|
||||
const dd = this.dateData && typeof this.dateData === 'object' ? this.dateData : {};
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = { dateData: { startTime: dd.startTime, endTime: dd.endTime } };
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串;时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: 'fullCostAnalysisBase',
|
||||
query: { // 使用query传递参数(推荐),也可使用params
|
||||
// baseName: itemName,
|
||||
factory: baseIndex,
|
||||
dateData: this.dateData
|
||||
query: {
|
||||
factory: String(baseIndex),
|
||||
startTime: dd.startTime != null ? String(dd.startTime) : '',
|
||||
endTime: dd.endTime != null ? String(dd.endTime) : ''
|
||||
}
|
||||
// 若仍需用base作为参数:
|
||||
// base: itemName,
|
||||
// params: { baseIndex: baseIndex }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
myChart: null, // 存储图表实例
|
||||
resizeHandler: null, // 存储resize事件处理函数
|
||||
isMounted: false, // 图表挂载标志,避免过早执行
|
||||
// 核心:基地名称与序号的映射表(固定顺序)
|
||||
baseNameToIndexMap: {
|
||||
'宜兴': 7,
|
||||
'漳州': 8,
|
||||
'自贡': 3,
|
||||
'桐城': 2,
|
||||
'洛阳': 9,
|
||||
'合肥': 5,
|
||||
'宿迁': 6,
|
||||
'秦皇岛': 10
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isMounted = true;
|
||||
this.$nextTick(() => {
|
||||
this.initChart(); // 初始化图表(只执行一次)
|
||||
this.updateChart(); // 更新图表数据
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
handler() {
|
||||
if (!this.isMounted) return; // 挂载前保护
|
||||
console.log(this.chartData, 'chartData');
|
||||
this.updateChart(); // 仅更新数据,不重新创建实例
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理资源
|
||||
this.destroyChart();
|
||||
},
|
||||
methods: {
|
||||
// 初始化图表(只在mounted中执行一次)
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
if (process.env.NODE_ENV === 'development') console.warn('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 只创建一次图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
};
|
||||
// 绑定resize事件(只绑定一次)
|
||||
window.addEventListener('resize', this.resizeHandler);
|
||||
},
|
||||
|
||||
// 更新图表数据(数据变化时执行)
|
||||
updateChart() {
|
||||
if (!this.myChart) {
|
||||
return; // 实例未初始化则返回
|
||||
}
|
||||
|
||||
const { allPlaceNames,unit, series } = this.chartData || {};
|
||||
const xData = allPlaceNames || [];
|
||||
const chartSeries = series || [];
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 5,
|
||||
right: 20,
|
||||
left: 25,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0],
|
||||
// 可选:X轴标签显示“序号+名称”(如“1 宜兴”)
|
||||
// formatter: (value) => {
|
||||
// const index = this.baseNameToIndexMap[value] || '';
|
||||
// return index ? `${index} ${value}` : value;
|
||||
// }
|
||||
},
|
||||
data: xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: unit,
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { show: true, show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } }
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { show: true, show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
}
|
||||
],
|
||||
series: chartSeries
|
||||
};
|
||||
|
||||
// 只更新配置,不重新创建实例
|
||||
this.myChart.setOption(option, true); // 第二个参数true表示清空原有配置,避免数据残留
|
||||
},
|
||||
|
||||
// 销毁图表资源
|
||||
destroyChart() {
|
||||
// 移除resize事件
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler);
|
||||
}
|
||||
// 销毁图表实例
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -27,6 +27,7 @@
|
||||
<script>
|
||||
import Container from '../components/container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
@@ -147,11 +148,27 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleDashboardClick(path) {
|
||||
// 从路径提取详情页名称,如 '/salesVolumeAnalysis/salesVolumeAnalysisBase' -> 'salesVolumeAnalysisBase'
|
||||
const toPage = path.split('/').pop();
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串,时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
dateData: this.dateData
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
||||
<div class="dashboard left">
|
||||
<div class="title">
|
||||
销量·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard right">
|
||||
<div class="title">
|
||||
单价·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import verticalBarChart from './verticalBarChart.vue'
|
||||
|
||||
// import * as echarts from 'echarts'
|
||||
// import rawItem from './raw-Item.vue'
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, operatingSingleBar, verticalBarChart },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
itemData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
title: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
month: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
itemData: {
|
||||
handler(newValue, oldValue) {
|
||||
// this.updateChart()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
// computed: {
|
||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
||||
// sortedItemData() {
|
||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
||||
// const totalCostItems = this.itemData.filter(item =>
|
||||
// item.name && item.name.includes('总成本')
|
||||
// );
|
||||
// // 过滤出不包含“总成本”的项
|
||||
// const otherItems = this.itemData.filter(item =>
|
||||
// !item.name || !item.name.includes('总成本')
|
||||
// );
|
||||
// // 合并:总成本项在前,其他项在后
|
||||
// return [...totalCostItems, ...otherItems];
|
||||
// }
|
||||
// },
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
|
||||
.scroll-container {
|
||||
/* 1. 固定容器高度:根据页面布局调整(示例300px,超出则滚动) */
|
||||
max-height: 210px;
|
||||
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
|
||||
overflow-y: auto;
|
||||
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
|
||||
overflow-x: hidden;
|
||||
/* 4. 内边距:与标题栏和容器边缘对齐 */
|
||||
padding: 10px 0;
|
||||
|
||||
/* 5. 隐藏滚动条(兼容主流浏览器) */
|
||||
/* Chrome/Safari */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
scrollbar-width: none;
|
||||
/* IE/Edge */
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
width: 382px;
|
||||
height: 205px;
|
||||
background: #F9FCFF;
|
||||
padding: 16px 0 0 10px;
|
||||
.title {
|
||||
// width: 190px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
// width: 190px;
|
||||
height: 32px;
|
||||
font-family: YouSheBiaoTiHei;
|
||||
font-size: 32px;
|
||||
color: #0B58FF;
|
||||
line-height: 32px;
|
||||
letter-spacing: 2px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mom {
|
||||
width: 120px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
// .line {
|
||||
// width: 500px;
|
||||
// height: 205px;
|
||||
// background: #F9FCFF;
|
||||
// }
|
||||
|
||||
// .leftTitle {
|
||||
// .item {
|
||||
// width: 67px;
|
||||
// height: 180px;
|
||||
// padding: 37px 23px;
|
||||
// background: #F9FCFF;
|
||||
// font-family: PingFangSC, PingFang SC;
|
||||
// font-weight: 400;
|
||||
// font-size: 18px;
|
||||
// color: #000000;
|
||||
// line-height: 25px;
|
||||
// letter-spacing: 1px;
|
||||
// // text-align: left;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
||||
<!-- <style>
|
||||
/* 全局 tooltip 样式(不使用 scoped,确保生效) */
|
||||
.production-status-chart-tooltip {
|
||||
background: #0a2b4f77 !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.production-status-chart-tooltip * {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style> -->
|
||||
@@ -40,6 +40,7 @@ import operatingLineChart from "../grossMarginComponents/operatingLineChart";
|
||||
import operatingLineChartCumulative from "../grossMarginComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getGrossMarginGroupData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -97,7 +98,6 @@ export default {
|
||||
return {
|
||||
transform: `scale(${v})`,
|
||||
transformOrigin: "left top",
|
||||
// overflow: hidden;
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -121,6 +121,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -129,7 +133,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
@@ -70,8 +70,10 @@ import profitLineChart from "../costComponents/profitLineChart.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getGrossMarginFactoryData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -82,7 +84,6 @@ export default {
|
||||
monthlyRelatedMetrics,
|
||||
yearRelatedMetrics,
|
||||
dataTrend
|
||||
// psiLineChart
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -91,10 +92,9 @@ export default {
|
||||
beilv: 1,
|
||||
month:'',
|
||||
value: 100,
|
||||
dateData:{},
|
||||
levelId:undefined,
|
||||
index: '毛利率',
|
||||
factory: null,
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monthData: {},
|
||||
ytdData: {},
|
||||
monthAnalysis: [],
|
||||
@@ -106,6 +106,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -171,14 +177,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
@@ -200,8 +198,6 @@ export default {
|
||||
this.monthAnalysis = res.data.monthAnalysis
|
||||
this.ytdAnalysis = res.data.ytdAnalysis
|
||||
this.trend = res.data.trend
|
||||
|
||||
// this.monthData = res.data.month
|
||||
});
|
||||
},
|
||||
|
||||
@@ -210,7 +206,6 @@ export default {
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
// mode: obj.mode,
|
||||
}
|
||||
|
||||
this.getData()
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import operatingLineBar from './operatingLineBarSale.vue';
|
||||
import operatingLineBar from './operatingLineBarSaleBase.vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
name: 'yearRelatedMetrics',
|
||||
@@ -88,11 +89,26 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleDashboardClick(path) {
|
||||
const toPage = path.split('/').pop();
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
factory: this.$route.query.factory || this.factory,
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串,时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
dateData:this.dateData
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
@@ -68,19 +69,6 @@ export default {
|
||||
|
||||
// 只创建一次图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 绑定点击事件(只绑定一次,永久生效)
|
||||
// this.myChart.on('click', (params) => {
|
||||
// // 提取点击的基地名称
|
||||
// const itemName = params.name;
|
||||
// // 根据映射表获取对应的序号(未匹配到则返回0或其他默认值)
|
||||
// const baseIndex = this.baseNameToIndexMap[itemName] || 0;
|
||||
|
||||
// // 兼容不同图表类型的value:柱状图value是数值,折线图是[横坐标, 纵坐标]
|
||||
// // const itemValue = Array.isArray(params.value) ? params.value[1] : params.value;
|
||||
// // const seriesName = params.seriesName;
|
||||
|
||||
// console.log(`你点击了【${itemName}】(序号:${baseIndex})`);
|
||||
this.myChart.getZr().on('click', (params) => {
|
||||
console.log('params', params);
|
||||
|
||||
@@ -108,17 +96,20 @@ export default {
|
||||
if (itemName === undefined) {
|
||||
return;
|
||||
}
|
||||
// 路由跳转时携带序号(或名称+序号)
|
||||
const dd = this.dateData && typeof this.dateData === 'object' ? this.dateData : {};
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = { dateData: { startTime: dd.startTime, endTime: dd.endTime } };
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串;时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: 'grossMarginBase',
|
||||
query: { // 使用query传递参数(推荐),也可使用params
|
||||
// baseName: itemName,
|
||||
factory: baseIndex,
|
||||
dateData:this.dateData
|
||||
query: {
|
||||
factory: String(baseIndex),
|
||||
startTime: dd.startTime != null ? String(dd.startTime) : '',
|
||||
endTime: dd.endTime != null ? String(dd.endTime) : ''
|
||||
}
|
||||
// 若仍需用base作为参数:
|
||||
// base: itemName,
|
||||
// params: { baseIndex: baseIndex }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
myChart: null, // 存储图表实例
|
||||
resizeHandler: null, // 存储resize事件处理函数
|
||||
isMounted: false, // 图表挂载标志,避免过早执行
|
||||
// 核心:基地名称与序号的映射表(固定顺序)
|
||||
baseNameToIndexMap: {
|
||||
'宜兴': 7,
|
||||
'漳州': 8,
|
||||
'自贡': 3,
|
||||
'桐城': 2,
|
||||
'洛阳': 9,
|
||||
'合肥': 5,
|
||||
'宿迁': 6,
|
||||
'秦皇岛': 10
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isMounted = true;
|
||||
this.$nextTick(() => {
|
||||
this.initChart(); // 初始化图表(只执行一次)
|
||||
this.updateChart(); // 更新图表数据
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
handler() {
|
||||
if (!this.isMounted) return;
|
||||
console.log(this.chartData, 'chartData');
|
||||
this.updateChart(); // 仅更新数据,不重新创建实例
|
||||
},
|
||||
deep: true
|
||||
// 移除 immediate: true,由 mounted 中的 updateChart() 处理初始化
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理资源
|
||||
this.destroyChart();
|
||||
},
|
||||
methods: {
|
||||
// 初始化图表(只在mounted中执行一次)
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
if (process.env.NODE_ENV === 'development') console.warn('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 只创建一次图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
};
|
||||
// 绑定resize事件(只绑定一次)
|
||||
window.addEventListener('resize', this.resizeHandler);
|
||||
},
|
||||
|
||||
// 更新图表数据(数据变化时执行)
|
||||
updateChart() {
|
||||
if (!this.myChart) {
|
||||
return; // 实例未初始化则返回
|
||||
}
|
||||
|
||||
const { allPlaceNames, unit, series } = this.chartData || {};
|
||||
const xData = allPlaceNames || [];
|
||||
const chartSeries = series || [];
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 5,
|
||||
right: 20,
|
||||
left: 25,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0],
|
||||
// 可选:X轴标签显示“序号+名称”(如“1 宜兴”)
|
||||
// formatter: (value) => {
|
||||
// const index = this.baseNameToIndexMap[value] || '';
|
||||
// return index ? `${index} ${value}` : value;
|
||||
// }
|
||||
},
|
||||
data: xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: unit,
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } }
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
}
|
||||
],
|
||||
series: chartSeries
|
||||
};
|
||||
|
||||
// 只更新配置,不重新创建实例
|
||||
this.myChart.setOption(option, true); // 第二个参数true表示清空原有配置,避免数据残留
|
||||
},
|
||||
|
||||
// 销毁图表资源
|
||||
destroyChart() {
|
||||
// 移除resize事件
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler);
|
||||
}
|
||||
// 销毁图表实例
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -42,6 +42,7 @@
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
@@ -64,6 +65,10 @@ export default {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
factory: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
month: {
|
||||
type: String,
|
||||
default: ''
|
||||
@@ -93,11 +98,26 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleDashboardClick(path) {
|
||||
const toPage = path.split('/').pop();
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
factory: this.$route.query.factory || this.factory,
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串,时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
dateData: this.dateData
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div id="dayReport" class="dayReport" :style="styles">
|
||||
<sidebar style="opacity: .9;" v-if="!sidebar.hide && openSider" class="sidebar-container" />
|
||||
<ReportHeader top-title="洛玻集团运营驾驶舱" :openSider="openSider" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange"
|
||||
<ReportHeader top-title="洛玻集团运营驾驶舱" :dateData="dateData" :openSider="openSider" :is-full-screen="isFullScreen" @screenfullChange="screenfullChange"
|
||||
@siderOpenChange="siderOpenChange" @timeRangeChange="handleTimeChange" />
|
||||
<div class="main-body"
|
||||
style="margin-top: -20px; flex: 1; display: flex;padding: 0px 16px 0;flex-direction: column;">
|
||||
@@ -42,8 +42,10 @@ import keyWork from './components/keyWork.vue'
|
||||
import { getOperateCockpit, getOrderDetail } from '@/api/cockpit'
|
||||
import { Sidebar } from "../../layout/components";
|
||||
import { mapState } from "vuex";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: 'JtHome',
|
||||
mixins: [navigationStateMixin],
|
||||
components: { ReportHeader, coreSalesKPIs, keyProductionIndicators, coreBottomLeft, keyWork, orderProgress, financeCosts, Sidebar },
|
||||
data() {
|
||||
return {
|
||||
@@ -56,7 +58,7 @@ export default {
|
||||
productData: {},
|
||||
heat:{},
|
||||
purchase: {},
|
||||
dateData:{},
|
||||
//dateData 由 mixin 提供
|
||||
inventory: {},
|
||||
importantWork: [],
|
||||
finance: {},
|
||||
@@ -70,6 +72,12 @@ export default {
|
||||
created() {
|
||||
this.init()
|
||||
this.windowWidth(document.documentElement.clientWidth)
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ dateData }) => {
|
||||
if (dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
|
||||
@@ -40,6 +40,7 @@ import operatingLineChart from "../inputOutputRatioComponents/operatingLineChart
|
||||
import operatingLineChartCumulative from "../inputOutputRatioComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getInputOutputRateGroupData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -116,6 +117,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -124,7 +129,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
@@ -67,8 +67,10 @@ import dataTrend from "../inputOutputRatioComponents/dataTrend.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getInputOutputRateFactoryData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -87,8 +89,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
index: '投入产出率',
|
||||
monthData: undefined,
|
||||
ytdData: undefined,
|
||||
@@ -101,7 +102,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -153,7 +159,6 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
console.log('this.$router.query', this.$router.query);
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -162,14 +167,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
@@ -191,8 +188,6 @@ export default {
|
||||
this.monthAnalysis = res.data.monthAnalysis
|
||||
this.ytdAnalysis = res.data.ytdAnalysis
|
||||
this.trend = res.data.trend
|
||||
|
||||
// this.monthData = res.data.month
|
||||
});
|
||||
},
|
||||
|
||||
@@ -200,8 +195,7 @@ export default {
|
||||
this.month = obj.targetMonth
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
// mode: obj.mode,
|
||||
endTime: obj.endTime
|
||||
}
|
||||
|
||||
this.getData()
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import operatingLineBar from './operatingLineBarSale.vue';
|
||||
import operatingLineBar from './operatingLineBarSaleBase.vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
@@ -96,17 +96,20 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// 路由跳转时携带序号(或名称+序号)
|
||||
const dd = this.dateData && typeof this.dateData === 'object' ? this.dateData : {};
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = { dateData: { startTime: dd.startTime, endTime: dd.endTime } };
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串;时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: 'inputOutputRatioBase',
|
||||
query: { // 使用query传递参数(推荐),也可使用params
|
||||
// baseName: itemName,
|
||||
factory: baseIndex,
|
||||
dateData: this.dateData
|
||||
query: {
|
||||
factory: String(baseIndex),
|
||||
startTime: dd.startTime != null ? String(dd.startTime) : '',
|
||||
endTime: dd.endTime != null ? String(dd.endTime) : ''
|
||||
}
|
||||
// 若仍需用base作为参数:
|
||||
// base: itemName,
|
||||
// params: { baseIndex: baseIndex }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
myChart: null, // 存储图表实例
|
||||
resizeHandler: null, // 存储resize事件处理函数
|
||||
isMounted: false, // 图表挂载标志,避免过早执行
|
||||
// 核心:基地名称与序号的映射表(固定顺序)
|
||||
baseNameToIndexMap: {
|
||||
'宜兴': 7,
|
||||
'漳州': 8,
|
||||
'自贡': 3,
|
||||
'桐城': 2,
|
||||
'洛阳': 9,
|
||||
'合肥': 5,
|
||||
'宿迁': 6,
|
||||
'秦皇岛': 10
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isMounted = true;
|
||||
this.$nextTick(() => {
|
||||
this.initChart(); // 初始化图表(只执行一次)
|
||||
this.updateChart(); // 更新图表数据
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
handler() {
|
||||
if (!this.isMounted) return; // 挂载前保护
|
||||
console.log(this.chartData, 'chartData');
|
||||
this.updateChart(); // 仅更新数据,不重新创建实例
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理资源
|
||||
this.destroyChart();
|
||||
},
|
||||
methods: {
|
||||
// 初始化图表(只在mounted中执行一次)
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
if (process.env.NODE_ENV === 'development') console.warn('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 只创建一次图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
};
|
||||
// 绑定resize事件(只绑定一次)
|
||||
window.addEventListener('resize', this.resizeHandler);
|
||||
},
|
||||
|
||||
// 更新图表数据(数据变化时执行)
|
||||
updateChart() {
|
||||
if (!this.myChart) {
|
||||
return; // 实例未初始化则返回
|
||||
}
|
||||
|
||||
const { allPlaceNames, unit, series } = this.chartData || {};
|
||||
const xData = allPlaceNames || [];
|
||||
const chartSeries = series || [];
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 5,
|
||||
right: 20,
|
||||
left: 25,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0],
|
||||
// 可选:X轴标签显示“序号+名称”(如“1 宜兴”)
|
||||
// formatter: (value) => {
|
||||
// const index = this.baseNameToIndexMap[value] || '';
|
||||
// return index ? `${index} ${value}` : value;
|
||||
// }
|
||||
},
|
||||
data: xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: unit,
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } }
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
}
|
||||
],
|
||||
series: chartSeries
|
||||
};
|
||||
|
||||
// 只更新配置,不重新创建实例
|
||||
this.myChart.setOption(option, true); // 第二个参数true表示清空原有配置,避免数据残留
|
||||
},
|
||||
|
||||
// 销毁图表资源
|
||||
destroyChart() {
|
||||
// 移除resize事件
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler);
|
||||
}
|
||||
// 销毁图表实例
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -40,6 +40,7 @@ import operatingLineChart from "../inventoryAnalysisComponents/operatingLineChar
|
||||
import operatingLineChartCumulative from "../inventoryAnalysisComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getInventoryData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "InventoryAnalysis",
|
||||
components: {
|
||||
@@ -117,6 +118,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -125,7 +130,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
@@ -40,6 +40,7 @@ import operatingLineChart from "../netPriceAnalysisComponents/operatingLineChart
|
||||
import operatingLineChartCumulative from "../netPriceAnalysisComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getUnitPriceAnalysisGroupData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -115,6 +116,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -123,7 +128,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData(obj) {
|
||||
|
||||
@@ -67,8 +67,10 @@ import dataTrend from "../netPriceAnalysisComponents/dataTrend.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getUnitPriceAnalysisBaseData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -86,8 +88,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
relatedMon: {},
|
||||
relatedTotal: {},
|
||||
@@ -100,6 +101,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -159,14 +166,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
@@ -177,12 +176,9 @@ export default {
|
||||
const requestParams = {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
// index: this.index,
|
||||
// sort: 1,
|
||||
paramName: '净价',
|
||||
paramList: this.paramList,
|
||||
baseId: this.factory,
|
||||
// baseId: Number(this.factory),
|
||||
};
|
||||
// 调用接口
|
||||
getUnitPriceAnalysisBaseData(requestParams).then((res) => {
|
||||
@@ -200,7 +196,6 @@ export default {
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
// mode: obj.mode,
|
||||
}
|
||||
|
||||
this.getData()
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import operatingLineBar from './operatingLineBarSale.vue';
|
||||
import operatingLineBar from './operatingLineBarSaleBase.vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
@@ -129,11 +130,27 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleDashboardClick(path) {
|
||||
// 从路径提取详情页名称,如 '/salesVolumeAnalysis/salesVolumeAnalysisBase' -> 'salesVolumeAnalysisBase'
|
||||
const toPage = path.split('/').pop();
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串,时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
dateData:this.dateData
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
@@ -95,17 +96,20 @@ export default {
|
||||
if (itemName === undefined) {
|
||||
return;
|
||||
}
|
||||
// 路由跳转时携带序号(或名称+序号)
|
||||
const dd = this.dateData && typeof this.dateData === 'object' ? this.dateData : {};
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = { dateData: { startTime: dd.startTime, endTime: dd.endTime } };
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串;时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: 'netPriceAnalysisBase',
|
||||
query: { // 使用query传递参数(推荐),也可使用params
|
||||
// baseName: itemName,
|
||||
factory: baseIndex,
|
||||
dateData: this.dateData
|
||||
query: {
|
||||
factory: String(baseIndex),
|
||||
startTime: dd.startTime != null ? String(dd.startTime) : '',
|
||||
endTime: dd.endTime != null ? String(dd.endTime) : ''
|
||||
}
|
||||
// 若仍需用base作为参数:
|
||||
// base: itemName,
|
||||
// params: { baseIndex: baseIndex }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
myChart: null, // 存储图表实例
|
||||
resizeHandler: null, // 存储resize事件处理函数
|
||||
isMounted: false, // 图表挂载标志,避免过早执行
|
||||
// 核心:基地名称与序号的映射表(固定顺序)
|
||||
baseNameToIndexMap: {
|
||||
'宜兴': 7,
|
||||
'漳州': 8,
|
||||
'自贡': 3,
|
||||
'桐城': 2,
|
||||
'洛阳': 9,
|
||||
'合肥': 5,
|
||||
'宿迁': 6,
|
||||
'秦皇岛': 10
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isMounted = true;
|
||||
this.$nextTick(() => {
|
||||
this.initChart(); // 初始化图表(只执行一次)
|
||||
this.updateChart(); // 更新图表数据
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
handler() {
|
||||
if (!this.isMounted) return; // 挂载前保护
|
||||
console.log(this.chartData, 'chartData');
|
||||
this.updateChart(); // 仅更新数据,不重新创建实例
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理资源
|
||||
this.destroyChart();
|
||||
},
|
||||
methods: {
|
||||
// 初始化图表(只在mounted中执行一次)
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
if (process.env.NODE_ENV === 'development') console.warn('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 只创建一次图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
};
|
||||
// 绑定resize事件(只绑定一次)
|
||||
window.addEventListener('resize', this.resizeHandler);
|
||||
},
|
||||
|
||||
// 更新图表数据(数据变化时执行)
|
||||
updateChart() {
|
||||
if (!this.myChart) {
|
||||
return; // 实例未初始化则返回
|
||||
}
|
||||
|
||||
const { allPlaceNames, series } = this.chartData || {};
|
||||
const xData = allPlaceNames || [];
|
||||
const chartSeries = series || [];
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 5,
|
||||
right: 20,
|
||||
left: 25,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0],
|
||||
// 可选:X轴标签显示“序号+名称”(如“1 宜兴”)
|
||||
// formatter: (value) => {
|
||||
// const index = this.baseNameToIndexMap[value] || '';
|
||||
// return index ? `${index} ${value}` : value;
|
||||
// }
|
||||
},
|
||||
data: xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '元/㎡',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } }
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
}
|
||||
],
|
||||
series: chartSeries
|
||||
};
|
||||
|
||||
// 只更新配置,不重新创建实例
|
||||
this.myChart.setOption(option, true); // 第二个参数true表示清空原有配置,避免数据残留
|
||||
},
|
||||
|
||||
// 销毁图表资源
|
||||
destroyChart() {
|
||||
// 移除resize事件
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler);
|
||||
}
|
||||
// 销毁图表实例
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -28,6 +28,7 @@
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
@@ -129,11 +130,27 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleDashboardClick(path) {
|
||||
// 从路径提取详情页名称,如 '/salesVolumeAnalysis/salesVolumeAnalysisBase' -> 'salesVolumeAnalysisBase'
|
||||
const toPage = path.split('/').pop();
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串,时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
dateData: this.dateData
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import operatingLineBar from './operatingLineBarSale.vue';
|
||||
import operatingLineBar from './operatingLineBarSaleBase.vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
@@ -74,7 +75,6 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
// 初始化数据包含flag字段
|
||||
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
|
||||
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
|
||||
}
|
||||
@@ -93,34 +93,42 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleDashboardClick(path) {
|
||||
const toPage = path.split('/').pop();
|
||||
const currentPath = this.$route.path;
|
||||
const factoryValue = this.$route.query.factory ? this.$route.query.factory : this.factory;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
factory: factoryValue,
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
dateData: this.dateData
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
updateChart(data) {
|
||||
// 数据兜底
|
||||
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
|
||||
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
|
||||
|
||||
// 核心修改:将flag整合到数据对象中,无需单独定义salesFlag/unitPriceFlag
|
||||
this.salesData = {
|
||||
...salesItem, // 合并原有字段
|
||||
...salesItem,
|
||||
flag: salesItem.rate>=100?1:0
|
||||
};
|
||||
|
||||
this.unitPriceData = {
|
||||
...unitPriceItem, // 合并原有字段
|
||||
...unitPriceItem,
|
||||
flag: unitPriceItem.rate>=100?1:0
|
||||
};
|
||||
|
||||
// 调试:确认整合后的数据
|
||||
console.log('整合flag后的销量数据:', this.salesData);
|
||||
console.log('整合flag后的单价数据:', this.unitPriceData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,7 +422,7 @@ export default {
|
||||
height: 24px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
fontSize: 12px;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
font-style: normal;
|
||||
letter-spacing: 2px;
|
||||
@@ -494,7 +494,7 @@ export default {
|
||||
|
||||
.dropdown-option {
|
||||
padding: 6px 12px;
|
||||
fontSize: 12px;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
@@ -97,13 +98,19 @@ export default {
|
||||
if (itemName === undefined) {
|
||||
return;
|
||||
}
|
||||
// 路由跳转时携带序号(或名称+序号)
|
||||
const dd = this.dateData && typeof this.dateData === 'object' ? this.dateData : {};
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = { dateData: { startTime: dd.startTime, endTime: dd.endTime } };
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串;时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: 'operatingRevenueBase',
|
||||
query: { // 使用query传递参数(推荐),也可使用params
|
||||
// baseName: itemName,
|
||||
factory: baseIndex,
|
||||
dateData: this.dateData
|
||||
query: {
|
||||
factory: String(baseIndex),
|
||||
startTime: dd.startTime != null ? String(dd.startTime) : '',
|
||||
endTime: dd.endTime != null ? String(dd.endTime) : ''
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
189
src/views/home/operatingComponents/operatingLineBarSaleBase.vue
Normal file
189
src/views/home/operatingComponents/operatingLineBarSaleBase.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
myChart: null, // 存储图表实例
|
||||
resizeHandler: null, // 存储resize事件处理函数
|
||||
isMounted: false, // 图表挂载标志,避免过早执行
|
||||
// 核心:基地名称与序号的映射表(固定顺序)
|
||||
baseNameToIndexMap: {
|
||||
'宜兴': 7,
|
||||
'漳州': 8,
|
||||
'自贡': 3,
|
||||
'桐城': 2,
|
||||
'洛阳': 9,
|
||||
'合肥': 5,
|
||||
'宿迁': 6,
|
||||
'秦皇岛': 10
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isMounted = true;
|
||||
this.$nextTick(() => {
|
||||
this.initChart(); // 初始化图表(只执行一次)
|
||||
this.updateChart(); // 更新图表数据
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
handler() {
|
||||
if (!this.isMounted) return;
|
||||
console.log(this.chartData, 'chartData');
|
||||
this.updateChart(); // 仅更新数据,不重新创建实例
|
||||
},
|
||||
deep: true
|
||||
// 移除 immediate: true,由 mounted 中的 updateChart() 处理初始化
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理资源
|
||||
this.destroyChart();
|
||||
},
|
||||
methods: {
|
||||
// 初始化图表(只在mounted中执行一次)
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
if (process.env.NODE_ENV === 'development') console.warn('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 只创建一次图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
};
|
||||
// 绑定resize事件(只绑定一次)
|
||||
window.addEventListener('resize', this.resizeHandler);
|
||||
},
|
||||
|
||||
// 更新图表数据(数据变化时执行)
|
||||
updateChart() {
|
||||
if (!this.myChart) {
|
||||
return; // 实例未初始化则返回
|
||||
}
|
||||
|
||||
const { allPlaceNames, unit, series } = this.chartData || {};
|
||||
const xData = allPlaceNames || [];
|
||||
const chartSeries = series || [];
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 5,
|
||||
right: 20,
|
||||
left: 25,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0],
|
||||
// 可选:X轴标签显示“序号+名称”(如“1 宜兴”)
|
||||
// formatter: (value) => {
|
||||
// const index = this.baseNameToIndexMap[value] || '';
|
||||
// return index ? `${index} ${value}` : value;
|
||||
// }
|
||||
},
|
||||
data: xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: unit,
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { show: true, show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } }
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { show: true, show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
}
|
||||
],
|
||||
series: chartSeries
|
||||
};
|
||||
|
||||
// 只更新配置,不重新创建实例
|
||||
this.myChart.setOption(option, true); // 第二个参数true表示清空原有配置,避免数据残留
|
||||
},
|
||||
|
||||
// 销毁图表资源
|
||||
destroyChart() {
|
||||
// 移除resize事件
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler);
|
||||
}
|
||||
// 销毁图表实例
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -42,6 +42,7 @@
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
@@ -89,11 +90,27 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleDashboardClick(path) {
|
||||
// 从路径提取详情页名称,如 '/salesVolumeAnalysis/salesVolumeAnalysisBase' -> 'salesVolumeAnalysisBase'
|
||||
const toPage = path.split('/').pop();
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串,时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : 5,
|
||||
dateData: this.dateData
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -40,6 +40,7 @@ import operatingLineChart from "../operatingProfitComponents/operatingLineChart"
|
||||
import operatingLineChartCumulative from "../operatingProfitComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getProfitAnalysisManageList } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -115,6 +116,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -123,7 +128,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
@@ -65,8 +65,10 @@ import dataTrend from "../operatingProfitComponents/dataTrend.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getProfitAnalysisManageList } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -75,7 +77,6 @@ export default {
|
||||
totalOverview,
|
||||
dataTrend,
|
||||
relatedIndicatorsAnalysis
|
||||
// psiLineChart
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -84,20 +85,24 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
relatedData: {},
|
||||
trendName: '经营性利润',
|
||||
// cusProData: {},
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -157,14 +162,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
@@ -210,7 +207,6 @@ export default {
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
// mode: obj.mode,
|
||||
}
|
||||
|
||||
this.getData()
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import operatingLineBar from './operatingLineBarSale.vue';
|
||||
import operatingLineBar from './operatingLineBarSaleBase.vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
@@ -99,16 +100,20 @@ export default {
|
||||
}
|
||||
|
||||
// 路由跳转时携带序号(或名称+序号)
|
||||
const dd = this.dateData && typeof this.dateData === 'object' ? this.dateData : {};
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = { dateData: { startTime: dd.startTime, endTime: dd.endTime } };
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串;时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: 'operatingProfitBase',
|
||||
query: { // 使用query传递参数(推荐),也可使用params
|
||||
// baseName: itemName,
|
||||
factory: baseIndex,
|
||||
dateData: this.dateData
|
||||
query: {
|
||||
factory: String(baseIndex),
|
||||
startTime: dd.startTime != null ? String(dd.startTime) : '',
|
||||
endTime: dd.endTime != null ? String(dd.endTime) : ''
|
||||
}
|
||||
// 若仍需用base作为参数:
|
||||
// base: itemName,
|
||||
// params: { baseIndex: baseIndex }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
myChart: null, // 存储图表实例
|
||||
resizeHandler: null, // 存储resize事件处理函数
|
||||
isMounted: false, // 图表挂载标志,避免过早执行
|
||||
// 核心:基地名称与序号的映射表(固定顺序)
|
||||
baseNameToIndexMap: {
|
||||
'宜兴': 7,
|
||||
'漳州': 8,
|
||||
'自贡': 3,
|
||||
'桐城': 2,
|
||||
'洛阳': 9,
|
||||
'合肥': 5,
|
||||
'宿迁': 6,
|
||||
'秦皇岛': 10
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isMounted = true;
|
||||
this.$nextTick(() => {
|
||||
this.initChart(); // 初始化图表(只执行一次)
|
||||
this.updateChart(); // 更新图表数据
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
handler() {
|
||||
if (!this.isMounted) return;
|
||||
console.log(this.chartData, 'chartData');
|
||||
this.updateChart(); // 仅更新数据,不重新创建实例
|
||||
},
|
||||
deep: true
|
||||
// 移除 immediate: true,由 mounted 中的 updateChart() 处理初始化
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理资源
|
||||
this.destroyChart();
|
||||
},
|
||||
methods: {
|
||||
// 初始化图表(只在mounted中执行一次)
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
if (process.env.NODE_ENV === 'development') console.warn('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 只创建一次图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
};
|
||||
// 绑定resize事件(只绑定一次)
|
||||
window.addEventListener('resize', this.resizeHandler);
|
||||
},
|
||||
|
||||
// 更新图表数据(数据变化时执行)
|
||||
updateChart() {
|
||||
if (!this.myChart) {
|
||||
return; // 实例未初始化则返回
|
||||
}
|
||||
|
||||
const { allPlaceNames, unit, series } = this.chartData || {};
|
||||
const xData = allPlaceNames || [];
|
||||
const chartSeries = series || [];
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 5,
|
||||
right: 20,
|
||||
left: 25,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0],
|
||||
// 可选:X轴标签显示“序号+名称”(如“1 宜兴”)
|
||||
// formatter: (value) => {
|
||||
// const index = this.baseNameToIndexMap[value] || '';
|
||||
// return index ? `${index} ${value}` : value;
|
||||
// }
|
||||
},
|
||||
data: xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: unit,
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
|
||||
splitNumber: 4,
|
||||
axisTick: { show: true },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } }
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
axisTick: { show: true },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
}
|
||||
],
|
||||
series: chartSeries
|
||||
};
|
||||
|
||||
// 只更新配置,不重新创建实例
|
||||
this.myChart.setOption(option, true); // 第二个参数true表示清空原有配置,避免数据残留
|
||||
},
|
||||
|
||||
// 销毁图表资源
|
||||
destroyChart() {
|
||||
// 移除resize事件
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler);
|
||||
}
|
||||
// 销毁图表实例
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -24,6 +24,7 @@
|
||||
<script>
|
||||
import Container from '../components/container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
@@ -119,11 +120,27 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleDashboardClick(path) {
|
||||
// 从路径提取详情页名称,如 '/salesVolumeAnalysis/salesVolumeAnalysisBase' -> 'salesVolumeAnalysisBase'
|
||||
const toPage = path.split('/').pop();
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
factory: this.$route.query.factory || this.factory,
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串,时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
dateData: this.dateData
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -41,6 +41,7 @@ import operatingLineChartCumulative from "../operatingComponents/operatingLineCh
|
||||
|
||||
import { getSalesRevenueGroupData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -117,6 +118,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -125,7 +130,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<div id="dayReport" class="dayReport" :style="styles">
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
||||
<ReportHeader :dateData="dateData" size="psi" @timeRangeChange="handleTimeChange" top-title="基地营业收入"
|
||||
<ReportHeader :dateData="dateData" size="psi"
|
||||
@timeRangeChange="handleTimeChange" top-title="基地营业收入"
|
||||
:is-full-screen="isFullScreen" @screenfullChange="screenfullChange" />
|
||||
<div class="main-body" style="
|
||||
margin-top: -20px;
|
||||
@@ -70,8 +71,10 @@ import profitLineChart from "../costComponents/profitLineChart.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getSalesRevenueFactoryData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -82,7 +85,6 @@ export default {
|
||||
monthlyRelatedMetrics,
|
||||
yearRelatedMetrics,
|
||||
dataTrend
|
||||
// psiLineChart
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -91,8 +93,7 @@ export default {
|
||||
beilv: 1,
|
||||
month:'',
|
||||
value: 100,
|
||||
factory:null,
|
||||
dateData:{},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
index: '营业收入',
|
||||
monthData: undefined,
|
||||
ytdData: undefined,
|
||||
@@ -105,7 +106,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -157,7 +163,6 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
console.log('this.$router.query', this.$route.query);
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -166,20 +171,10 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
console.log('this.$route.query.dateData', this.$route.query.dateData);
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
this.index =value
|
||||
console.log('+++++++++++++++++++++++++++++++++111111')
|
||||
this.getData()
|
||||
},
|
||||
getData() {
|
||||
@@ -197,27 +192,20 @@ export default {
|
||||
this.monthAnalysis = res.data.monthAnalysis
|
||||
this.ytdAnalysis = res.data.ytdAnalysis
|
||||
this.trend = res.data.trend
|
||||
|
||||
// this.monthData = res.data.month
|
||||
});
|
||||
},
|
||||
|
||||
handleTimeChange(obj) {
|
||||
console.log('=====================================222222222222',obj);
|
||||
this.month = obj.targetMonth
|
||||
this.dateData = {
|
||||
startTime: obj.startTime,
|
||||
endTime: obj.endTime,
|
||||
// mode: obj.mode,
|
||||
}
|
||||
console.log('+++++++++++++++++++++++++++++++++22222')
|
||||
this.getData()
|
||||
},
|
||||
selectChange(data) {
|
||||
console.log('选中的数据:', data);
|
||||
this.factory = data
|
||||
if (this.dateData.startTime && this.dateData.endTime) {
|
||||
console.log('+++++++++++++++++++++++++++++++++33333')
|
||||
this.getData();
|
||||
}
|
||||
},
|
||||
@@ -253,8 +241,6 @@ export default {
|
||||
},
|
||||
// 全屏
|
||||
screenfullChange() {
|
||||
console.log("screenfull.enabled", screenfull.isEnabled);
|
||||
|
||||
if (!screenfull.isEnabled) {
|
||||
this.$message({
|
||||
message: "you browser can not work",
|
||||
|
||||
@@ -41,6 +41,7 @@ import operatingLineChartCumulative from "../procurementGainAnalysisComponents/o
|
||||
|
||||
import { getUnitPriceAnalysisGroupData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -116,6 +117,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -124,7 +129,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData(obj) {
|
||||
|
||||
@@ -65,8 +65,10 @@ import dataTrend from "../procurementGainAnalysisComponents/dataTrend.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getUnitPriceAnalysisBaseData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "procurementGainAnalysisBase",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -83,8 +85,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: {},
|
||||
@@ -95,6 +96,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -154,14 +161,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
@@ -175,7 +174,6 @@ export default {
|
||||
paramName: '总增效额',
|
||||
paramList: ['大宗增效额', '石料增效额', '材料增效额', '动力增效额', '技术服务增效额'],
|
||||
baseId: this.factory,
|
||||
// baseId: Number(this.factory),
|
||||
};
|
||||
// 调用接口
|
||||
getUnitPriceAnalysisBaseData(requestParams).then((res) => {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import operatingLineBar from './operatingLineBarSale.vue';
|
||||
import operatingLineBar from './operatingLineBarSaleBase.vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
||||
<div class="dashboard left">
|
||||
<div class="title">
|
||||
销量·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard right">
|
||||
<div class="title">
|
||||
单价·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import verticalBarChart from './verticalBarChart.vue'
|
||||
|
||||
// import * as echarts from 'echarts'
|
||||
// import rawItem from './raw-Item.vue'
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, operatingSingleBar, verticalBarChart },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
itemData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
title: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
month: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
itemData: {
|
||||
handler(newValue, oldValue) {
|
||||
// this.updateChart()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
// computed: {
|
||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
||||
// sortedItemData() {
|
||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
||||
// const totalCostItems = this.itemData.filter(item =>
|
||||
// item.name && item.name.includes('总成本')
|
||||
// );
|
||||
// // 过滤出不包含“总成本”的项
|
||||
// const otherItems = this.itemData.filter(item =>
|
||||
// !item.name || !item.name.includes('总成本')
|
||||
// );
|
||||
// // 合并:总成本项在前,其他项在后
|
||||
// return [...totalCostItems, ...otherItems];
|
||||
// }
|
||||
// },
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
|
||||
.scroll-container {
|
||||
/* 1. 固定容器高度:根据页面布局调整(示例300px,超出则滚动) */
|
||||
max-height: 210px;
|
||||
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
|
||||
overflow-y: auto;
|
||||
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
|
||||
overflow-x: hidden;
|
||||
/* 4. 内边距:与标题栏和容器边缘对齐 */
|
||||
padding: 10px 0;
|
||||
|
||||
/* 5. 隐藏滚动条(兼容主流浏览器) */
|
||||
/* Chrome/Safari */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
scrollbar-width: none;
|
||||
/* IE/Edge */
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
width: 382px;
|
||||
height: 205px;
|
||||
background: #F9FCFF;
|
||||
padding: 16px 0 0 10px;
|
||||
|
||||
.title {
|
||||
// width: 190px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
// width: 190px;
|
||||
height: 32px;
|
||||
font-family: YouSheBiaoTiHei;
|
||||
font-size: 32px;
|
||||
color: #0B58FF;
|
||||
line-height: 32px;
|
||||
letter-spacing: 2px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mom {
|
||||
width: 120px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
// .line {
|
||||
// width: 500px;
|
||||
// height: 205px;
|
||||
// background: #F9FCFF;
|
||||
// }
|
||||
|
||||
// .leftTitle {
|
||||
// .item {
|
||||
// width: 67px;
|
||||
// height: 180px;
|
||||
// padding: 37px 23px;
|
||||
// background: #F9FCFF;
|
||||
// font-family: PingFangSC, PingFang SC;
|
||||
// font-weight: 400;
|
||||
// font-size: 18px;
|
||||
// color: #000000;
|
||||
// line-height: 25px;
|
||||
// letter-spacing: 1px;
|
||||
// // text-align: left;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
||||
<!-- <style>
|
||||
/* 全局 tooltip 样式(不使用 scoped,确保生效) */
|
||||
.production-status-chart-tooltip {
|
||||
background: #0a2b4f77 !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.production-status-chart-tooltip * {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style> -->
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
@@ -98,17 +98,20 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// 路由跳转时携带序号(或名称+序号)
|
||||
const dd = this.dateData && typeof this.dateData === 'object' ? this.dateData : {};
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = { dateData: { startTime: dd.startTime, endTime: dd.endTime } };
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串;时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: 'procurementGainAnalysisBase',
|
||||
query: { // 使用query传递参数(推荐),也可使用params
|
||||
// baseName: itemName,
|
||||
factory: baseIndex,
|
||||
dateData: this.dateData
|
||||
query: {
|
||||
factory: String(baseIndex),
|
||||
startTime: dd.startTime != null ? String(dd.startTime) : '',
|
||||
endTime: dd.endTime != null ? String(dd.endTime) : ''
|
||||
}
|
||||
// 若仍需用base作为参数:
|
||||
// base: itemName,
|
||||
// params: { baseIndex: baseIndex }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
myChart: null, // 存储图表实例
|
||||
resizeHandler: null, // 存储resize事件处理函数
|
||||
isMounted: false, // 图表挂载标志,避免过早执行
|
||||
// 核心:基地名称与序号的映射表(固定顺序)
|
||||
baseNameToIndexMap: {
|
||||
'宜兴': 7,
|
||||
'漳州': 8,
|
||||
'自贡': 3,
|
||||
'桐城': 2,
|
||||
'洛阳': 9,
|
||||
'合肥': 5,
|
||||
'宿迁': 6,
|
||||
'秦皇岛': 10
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isMounted = true;
|
||||
this.$nextTick(() => {
|
||||
this.initChart(); // 初始化图表(只执行一次)
|
||||
this.updateChart(); // 更新图表数据
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
handler() {
|
||||
if (!this.isMounted) return; // 挂载前保护
|
||||
console.log(this.chartData, 'chartData');
|
||||
this.updateChart(); // 仅更新数据,不重新创建实例
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁时清理资源
|
||||
this.destroyChart();
|
||||
},
|
||||
methods: {
|
||||
// 初始化图表(只在mounted中执行一次)
|
||||
initChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
if (process.env.NODE_ENV === 'development') console.warn('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 只创建一次图表实例
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
// 定义resize处理函数(命名函数,方便移除)
|
||||
this.resizeHandler = () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
};
|
||||
// 绑定resize事件(只绑定一次)
|
||||
window.addEventListener('resize', this.resizeHandler);
|
||||
},
|
||||
|
||||
// 更新图表数据(数据变化时执行)
|
||||
updateChart() {
|
||||
if (!this.myChart) {
|
||||
return; // 实例未初始化则返回
|
||||
}
|
||||
|
||||
const { allPlaceNames, series } = this.chartData || {};
|
||||
const xData = allPlaceNames || [];
|
||||
const chartSeries = series || [];
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 5,
|
||||
right: 20,
|
||||
left: 25,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0],
|
||||
// 可选:X轴标签显示“序号+名称”(如“1 宜兴”)
|
||||
// formatter: (value) => {
|
||||
// const index = this.baseNameToIndexMap[value] || '';
|
||||
// return index ? `${index} ${value}` : value;
|
||||
// }
|
||||
},
|
||||
data: xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '万元',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } }
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'left'
|
||||
},
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
splitLine: { show: false },
|
||||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
}
|
||||
],
|
||||
series: chartSeries
|
||||
};
|
||||
|
||||
// 只更新配置,不重新创建实例
|
||||
this.myChart.setOption(option, true); // 第二个参数true表示清空原有配置,避免数据残留
|
||||
},
|
||||
|
||||
// 销毁图表资源
|
||||
destroyChart() {
|
||||
// 移除resize事件
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener('resize', this.resizeHandler);
|
||||
}
|
||||
// 销毁图表实例
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -27,6 +27,7 @@
|
||||
<script>
|
||||
import Container from '../components/container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import { pushNavigation, saveNavigationState } from '@/utils/navigationReturnState';
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
@@ -74,7 +75,7 @@ export default {
|
||||
{ key: 'stoneProfit', name: '石料类', unit: '万元',route:null},
|
||||
{ key: 'materialProfit', name: '材料类', unit: '万元',route:null},
|
||||
{ key: 'powerProfit', name: '动力类', unit: '万元',route:null},
|
||||
{ key: 'technicalServiceProfit', name: '技术服务类', unit: '万元',route:'/salesVolumeAnalysis/doublePlatedBase' },
|
||||
{ key: 'technicalServiceProfit', name: '技术服务类', unit: '万元',route:null },
|
||||
]
|
||||
},
|
||||
indicators() {
|
||||
@@ -142,9 +143,29 @@ export default {
|
||||
// 无需额外操作,初始化数据已赋值
|
||||
},
|
||||
methods: {
|
||||
handleRoute(path) {
|
||||
handleDashboardClick(path) {
|
||||
// 从路径提取详情页名称,如 '/salesVolumeAnalysis/salesVolumeAnalysisBase' -> 'salesVolumeAnalysisBase'
|
||||
const toPage = path.split('/').pop();
|
||||
// 保存当前页面状态并入栈
|
||||
const currentPath = this.$route.path;
|
||||
const state = {
|
||||
dateData: {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime
|
||||
},
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
toPage
|
||||
};
|
||||
pushNavigation(currentPath, state);
|
||||
saveNavigationState(currentPath, state);
|
||||
// query 仅支持字符串,时间范围用 startTime/endTime 传递
|
||||
this.$router.push({
|
||||
path: path
|
||||
path: path,
|
||||
query: {
|
||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
||||
startTime: this.dateData && this.dateData.startTime != null ? String(this.dateData.startTime) : '',
|
||||
endTime: this.dateData && this.dateData.endTime != null ? String(this.dateData.endTime) : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
handleChange(value) {
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
||||
<div class="dashboard left">
|
||||
<div class="title">
|
||||
销量·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard right">
|
||||
<div class="title">
|
||||
单价·万元
|
||||
</div>
|
||||
<div class="line">
|
||||
<operatingSingleBar></operatingSingleBar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import operatingSingleBar from './operatingSingleBar.vue'
|
||||
import verticalBarChart from './verticalBarChart.vue'
|
||||
|
||||
// import * as echarts from 'echarts'
|
||||
// import rawItem from './raw-Item.vue'
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, operatingSingleBar, verticalBarChart },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
itemData: { // 接收父组件传递的设备数据数组
|
||||
type: Array,
|
||||
default: () => [] // 默认空数组,避免报错
|
||||
},
|
||||
title: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
month: { // 接收父组件传递的设备数据数组
|
||||
type: String,
|
||||
default: () => '' // 默认空数组,避免报错
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
itemData: {
|
||||
handler(newValue, oldValue) {
|
||||
// this.updateChart()
|
||||
},
|
||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||
}
|
||||
},
|
||||
// computed: {
|
||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
||||
// sortedItemData() {
|
||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
||||
// const totalCostItems = this.itemData.filter(item =>
|
||||
// item.name && item.name.includes('总成本')
|
||||
// );
|
||||
// // 过滤出不包含“总成本”的项
|
||||
// const otherItems = this.itemData.filter(item =>
|
||||
// !item.name || !item.name.includes('总成本')
|
||||
// );
|
||||
// // 合并:总成本项在前,其他项在后
|
||||
// return [...totalCostItems, ...otherItems];
|
||||
// }
|
||||
// },
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
|
||||
.scroll-container {
|
||||
/* 1. 固定容器高度:根据页面布局调整(示例300px,超出则滚动) */
|
||||
max-height: 210px;
|
||||
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
|
||||
overflow-y: auto;
|
||||
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
|
||||
overflow-x: hidden;
|
||||
/* 4. 内边距:与标题栏和容器边缘对齐 */
|
||||
padding: 10px 0;
|
||||
|
||||
/* 5. 隐藏滚动条(兼容主流浏览器) */
|
||||
/* Chrome/Safari */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
scrollbar-width: none;
|
||||
/* IE/Edge */
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
width: 382px;
|
||||
height: 205px;
|
||||
background: #F9FCFF;
|
||||
padding: 16px 0 0 10px;
|
||||
|
||||
.title {
|
||||
// width: 190px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
// width: 190px;
|
||||
height: 32px;
|
||||
font-family: YouSheBiaoTiHei;
|
||||
font-size: 32px;
|
||||
color: #0B58FF;
|
||||
line-height: 32px;
|
||||
letter-spacing: 2px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mom {
|
||||
width: 120px;
|
||||
height: 18px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
line-height: 18px;
|
||||
letter-spacing: 1px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
// .line {
|
||||
// width: 500px;
|
||||
// height: 205px;
|
||||
// background: #F9FCFF;
|
||||
// }
|
||||
|
||||
// .leftTitle {
|
||||
// .item {
|
||||
// width: 67px;
|
||||
// height: 180px;
|
||||
// padding: 37px 23px;
|
||||
// background: #F9FCFF;
|
||||
// font-family: PingFangSC, PingFang SC;
|
||||
// font-weight: 400;
|
||||
// font-size: 18px;
|
||||
// color: #000000;
|
||||
// line-height: 25px;
|
||||
// letter-spacing: 1px;
|
||||
// // text-align: left;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
||||
<!-- <style>
|
||||
/* 全局 tooltip 样式(不使用 scoped,确保生效) */
|
||||
.production-status-chart-tooltip {
|
||||
background: #0a2b4f77 !important;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.production-status-chart-tooltip * {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style> -->
|
||||
@@ -66,8 +66,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuelYL
|
||||
import { mapState } from "vuex";
|
||||
import { getSingleMaterialAnalysis } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -76,7 +78,6 @@ export default {
|
||||
totalOverview,
|
||||
relateSingleFuelCostAnalysis,
|
||||
dataTrend
|
||||
// psiLineChart
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -85,14 +86,16 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
navigationExtraFields: {
|
||||
meterialName: '氢氧化铝' // 字段名: 默认值
|
||||
},
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
relatedData: {},
|
||||
trendName: '成本',
|
||||
meterialName:'',
|
||||
meterialName:'氢氧化铝',
|
||||
materialOptions: [
|
||||
{value:'氢氧化铝',label:'氢氧化铝'},
|
||||
{value:'碎玻璃(外购)',label:'碎玻璃'},
|
||||
@@ -111,6 +114,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -174,15 +183,7 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.meterialName = this.$route.query.name ? this.$route.query.name : '氢氧化铝'
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// this.meterialName = this.$route.query.name ? this.$route.query.name : '氢氧化铝'
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
|
||||
@@ -65,8 +65,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendCombustible.
|
||||
import { mapState } from "vuex";
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -83,8 +85,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -96,6 +97,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -161,14 +168,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
@@ -186,9 +185,7 @@ export default {
|
||||
analysisObject: [
|
||||
"原片燃料成本",
|
||||
],
|
||||
// paramList: ['制造成本', '财务费用', '销售费用', '管理费用', '运费'],
|
||||
levelId: this.factory,
|
||||
// baseId: Number(this.factory),
|
||||
};
|
||||
// 调用接口
|
||||
getCostAnalysisData(requestParams).then((res) => {
|
||||
|
||||
@@ -66,8 +66,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleCCA.vu
|
||||
import { mapState } from "vuex";
|
||||
import { getSingleMaterialAnalysis } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -85,8 +87,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -100,6 +101,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -163,14 +170,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
|
||||
@@ -64,8 +64,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendFuel.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -83,8 +85,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -96,6 +97,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -161,14 +168,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
@@ -186,7 +185,6 @@ export default {
|
||||
analysisObject: [
|
||||
"原片原料成本",
|
||||
],
|
||||
// paramList: ['制造成本', '财务费用', '销售费用', '管理费用', '运费'],
|
||||
levelId: this.factory,
|
||||
};
|
||||
// 调用接口
|
||||
|
||||
@@ -65,8 +65,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendFactoryBurde
|
||||
import { mapState } from "vuex";
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -84,8 +86,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -97,6 +98,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -162,14 +169,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
||||
<ReportHeader :dateData="dateData" size="psi" @timeRangeChange="handleTimeChange"
|
||||
@selectChange='handleOverheadChange' :selectName='overheadName' :selectOptions='overheadOptions'
|
||||
@selectChange='handleOverheadChange' :selectName='meterialName' :selectOptions='materialOptions'
|
||||
top-title="单项制造费用成本分析" :is-full-screen="isFullScreen"
|
||||
@screenfullChange="screenfullChange" :leftMargin="'280px'" />
|
||||
<div class="main-body" style="
|
||||
@@ -56,8 +56,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendProcessingLa
|
||||
import { mapState } from "vuex";
|
||||
import { getSingleMaterialCostAnalysis } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -73,15 +75,17 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
navigationExtraFields: {
|
||||
meterialName: '包材' // 字段名: 默认值
|
||||
},
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
relatedData: {},
|
||||
trendName: '制造费用',
|
||||
overheadName:'',
|
||||
overheadOptions:[
|
||||
meterialName:'',
|
||||
materialOptions:[
|
||||
{value:'包材',label:'包材'},
|
||||
{value:'备件、机物料',label:'备件、机物料'},
|
||||
{value:'折旧',label:'折旧'},
|
||||
@@ -93,6 +97,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -158,15 +168,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.overheadName = this.$route.query.name ? this.$route.query.name : '包材'
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
@@ -179,27 +180,27 @@ export default {
|
||||
const requestParams = {
|
||||
startTime: this.dateData.startTime,
|
||||
endTime: this.dateData.endTime,
|
||||
trendName: '原片'+this.overheadName+'成本',
|
||||
analysisObject: ['原片'+this.overheadName],
|
||||
trendName: '原片'+this.meterialName+'成本',
|
||||
analysisObject: ['原片'+this.meterialName],
|
||||
levelId: this.factory,
|
||||
isOriginal:0,//0:原片 1:加工
|
||||
};
|
||||
// 调用接口
|
||||
getSingleMaterialCostAnalysis(requestParams).then((res) => {
|
||||
this.monData = res.data.currentMonthData.find(item => {
|
||||
return item.name === '原片'+this.overheadName+'成本';
|
||||
return item.name === '原片'+this.meterialName+'成本';
|
||||
});
|
||||
|
||||
this.totalData = res.data.totalMonthData.find(item => {
|
||||
return item.name === '原片'+this.overheadName+'成本';
|
||||
return item.name === '原片'+this.meterialName+'成本';
|
||||
});
|
||||
// this.relatedMon = res.data.relatedMon
|
||||
this.relatedData = {
|
||||
relatedMon: res.data.currentMonthData.filter(item => {
|
||||
return item.name !== '原片'+this.overheadName+'成本';
|
||||
return item.name !== '原片'+this.meterialName+'成本';
|
||||
}), // 兜底月度数据
|
||||
relatedTotal: res.data.totalMonthData.filter(item => {
|
||||
return item.name !== '原片'+this.overheadName+'成本';
|
||||
return item.name !== '原片'+this.meterialName+'成本';
|
||||
}) // 兜底累计数据
|
||||
}
|
||||
this.trend = res.data.dataTrend
|
||||
@@ -217,7 +218,7 @@ export default {
|
||||
this.getData()
|
||||
},
|
||||
handleOverheadChange(val) {
|
||||
this.overheadName = val
|
||||
this.meterialName = val
|
||||
this.getData()
|
||||
},
|
||||
selectChange(data) {
|
||||
|
||||
@@ -41,6 +41,7 @@ import operatingLineChart from "../productionCostAnalysisComponents/operatingLin
|
||||
import operatingLineChartCumulative from "../productionCostAnalysisComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -117,6 +118,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -125,7 +130,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
@@ -65,8 +65,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrend.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -84,8 +86,7 @@ export default {
|
||||
beilv: 1,
|
||||
month:'',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -97,6 +98,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -162,15 +169,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
console.log('this.$route.query.factory', this.$route.query.factory);
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
@@ -188,9 +186,7 @@ export default {
|
||||
analysisObject: [
|
||||
"原片成本",
|
||||
],
|
||||
// paramList: ['制造成本', '财务费用', '销售费用', '管理费用', '运费'],
|
||||
levelId: this.factory,
|
||||
// baseId: Number(this.factory),
|
||||
};
|
||||
// 调用接口
|
||||
getCostAnalysisData(requestParams).then((res) => {
|
||||
|
||||
@@ -54,8 +54,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendProcessingLa
|
||||
import { mapState } from "vuex";
|
||||
import { getSingleMaterialCostAnalysis } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -71,8 +73,8 @@ export default {
|
||||
beilv: 1,
|
||||
month:'',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -84,6 +86,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -149,14 +157,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
|
||||
@@ -64,8 +64,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuelDi
|
||||
import { mapState } from "vuex";
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -82,8 +84,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -95,6 +96,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -160,14 +167,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
|
||||
@@ -63,8 +63,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendProcAuxMat.v
|
||||
import { mapState } from "vuex";
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -80,9 +82,8 @@ export default {
|
||||
timer: null,
|
||||
beilv: 1,
|
||||
month: '',
|
||||
factory: null,
|
||||
value: 100,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
trendName: '加工辅料',
|
||||
monData: {},
|
||||
totalData: {},
|
||||
@@ -94,6 +95,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -158,14 +165,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
|
||||
@@ -66,8 +66,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendPro.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -85,8 +87,7 @@ export default {
|
||||
beilv: 1,
|
||||
month:'',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
trendName: '加工制造费用成本',
|
||||
monData: {},
|
||||
totalData: {},
|
||||
@@ -99,6 +100,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -157,14 +164,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
|
||||
@@ -54,8 +54,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendProcessingLa
|
||||
import { mapState } from "vuex";
|
||||
import { getSingleMaterialCostAnalysis } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -71,8 +73,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -87,6 +88,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -150,14 +157,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
|
||||
@@ -41,6 +41,7 @@ import operatingLineChart from "../productionCostAnalysisComponents/operatingLin
|
||||
import operatingLineChartCumulative from "../productionCostAnalysisComponents/operatingLineChartCumulative.vue";
|
||||
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -117,6 +118,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -125,7 +130,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
@@ -65,8 +65,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendProcess.vue"
|
||||
import { mapState } from "vuex";
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -83,8 +85,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -96,6 +97,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -161,16 +168,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
console.log('this.$route.query.factory', this.$route.query.factory);
|
||||
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
|
||||
@@ -66,8 +66,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendProcessingFu
|
||||
import { mapState } from "vuex";
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -86,8 +88,7 @@ export default {
|
||||
beilv: 1,
|
||||
month:'',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
trendName: '加工燃料成本',
|
||||
monData: {},
|
||||
totalData: {},
|
||||
@@ -100,6 +101,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -157,14 +164,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
|
||||
@@ -54,8 +54,10 @@ import dataTrend from "../productionCostAnalysisComponents/dataTrendProcessingLa
|
||||
import { mapState } from "vuex";
|
||||
import { getSingleMaterialCostAnalysis } from '@/api/cockpit'
|
||||
import moment from "moment";
|
||||
import navigationStateMixin from "@/utils/mixins/navigationStateMixin";
|
||||
export default {
|
||||
name: "DayReport",
|
||||
mixins: [navigationStateMixin],
|
||||
components: {
|
||||
ReportHeader,
|
||||
changeBase,
|
||||
@@ -71,8 +73,7 @@ export default {
|
||||
beilv: 1,
|
||||
month: '',
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
// factory 和 dateData 由 mixin 提供
|
||||
monData: {},
|
||||
totalData: {},
|
||||
trend: [],
|
||||
@@ -84,6 +85,12 @@ export default {
|
||||
created() {
|
||||
this.init();
|
||||
this.windowWidth(document.documentElement.clientWidth);
|
||||
// 使用 mixin 初始化导航状态恢复
|
||||
this._initNavigationStateMixin(({ factory, dateData }) => {
|
||||
if (factory != null && dateData && dateData.startTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@@ -149,14 +156,6 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
if(this.$route.query.factory){
|
||||
this.factory =Number(this.$route.query.factory)
|
||||
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {
|
||||
this.factory = this.$store.getters.levelList[0].id
|
||||
}else{
|
||||
this.factory = this.$store.getters.levelList[1].id
|
||||
}
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
},
|
||||
methods: {
|
||||
changeItem(item) {
|
||||
|
||||
@@ -39,6 +39,7 @@ import { mapState } from "vuex";
|
||||
import operatingLineChart from "../productionCostAnalysisComponents/operatingLineChart";
|
||||
import operatingLineChartCumulative from "../productionCostAnalysisComponents/operatingLineChartCumulative.vue";
|
||||
import { getCostAnalysisData } from '@/api/cockpit'
|
||||
import { consumeNavigationState, clearNavigation } from '@/utils/navigationReturnState';
|
||||
export default {
|
||||
name: "DayReport",
|
||||
components: {
|
||||
@@ -116,6 +117,10 @@ export default {
|
||||
this.destroy();
|
||||
},
|
||||
mounted() {
|
||||
// 菜单入口时清空导航栈
|
||||
if (this.$route.query.menu === '1') {
|
||||
clearNavigation();
|
||||
}
|
||||
const _this = this;
|
||||
_this.beilv = document.documentElement.clientWidth / 1920;
|
||||
window.onresize = () => {
|
||||
@@ -124,7 +129,25 @@ export default {
|
||||
this.beilv = _this.clientWidth / 1920;
|
||||
})();
|
||||
};
|
||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||
// 从 sessionStorage 获取本页面保存的状态
|
||||
const currentPath = this.$route.path;
|
||||
const saved = consumeNavigationState(currentPath);
|
||||
if (saved && saved.dateData && saved.dateData.startTime != null && saved.dateData.endTime != null) {
|
||||
this.dateData = {
|
||||
startTime: saved.dateData.startTime,
|
||||
endTime: saved.dateData.endTime
|
||||
};
|
||||
} else if (this.$route.query.startTime && this.$route.query.endTime) {
|
||||
this.dateData = {
|
||||
startTime: Number(this.$route.query.startTime),
|
||||
endTime: Number(this.$route.query.endTime)
|
||||
};
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.dateData && this.dateData.startTime != null && this.dateData.endTime != null) {
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user