更新产量驾驶舱 #10
							
								
								
									
										1
									
								
								.env.dev
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								.env.dev
									
									
									
									
									
								
							| @@ -5,6 +5,7 @@ ENV = 'development' | ||||
| VUE_APP_TITLE = 芋道管理系统 | ||||
|  | ||||
| # 芋道管理系统/开发环境 | ||||
| # VUE_APP_BASE_API = 'http://192.168.1.61:48080' | ||||
| VUE_APP_BASE_API = 'http://glass.kszny.picaiba.com' | ||||
|  | ||||
| # 路由懒加载 | ||||
|   | ||||
| @@ -54,6 +54,7 @@ | ||||
|     "highlight.js": "^11.9.0", | ||||
|     "js-beautify": "^1.15.1", | ||||
|     "jsencrypt": "3.3.1", | ||||
|     "lodash": "^4.17.21", | ||||
|     "mockjs": "^1.1.0", | ||||
|     "moment": "^2.30.1", | ||||
|     "nprogress": "0.2.0", | ||||
|   | ||||
| @@ -1,4 +1,180 @@ | ||||
| import axios from "@/utils/request"; | ||||
| import { deepClone } from "../../utils"; | ||||
|  | ||||
| /* 状态 */ | ||||
| const state = { | ||||
|   copilot: { | ||||
|     /* 产量驾驶舱 */ | ||||
|     yield: { | ||||
|       ftoInvest: null, | ||||
|       chipInvest: null, | ||||
|       chipOutput: null, | ||||
|       stdOutput: null, | ||||
|       bipvOutput: null, | ||||
|     }, | ||||
|     /* 能源驾驶舱 */ | ||||
|     energy: {}, | ||||
|     /* 效率驾驶舱 */ | ||||
|     efficiency: {}, | ||||
|   }, | ||||
|   home: { | ||||
|     ftoInvest: null, | ||||
|     chipInvest: null, | ||||
|     chipOutput: null, | ||||
|     stdOutput: null, | ||||
|     bipvOutput: null, | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| const mutations = { | ||||
|   SET_HOME_INFO: (state, payload) => { | ||||
|     state.home.ftoInvest = payload.ftoInvest; | ||||
|     state.home.chipInvest = payload.chipInvest; | ||||
|     state.home.chipOutput = payload.chipOutput; | ||||
|     state.home.stdOutput = payload.stdOutput; | ||||
|     state.home.bipvOutput = payload.bipvOutput; | ||||
|   }, | ||||
|   SET_COPILOT_INFO: (state, { type, payload }) => { | ||||
|     switch (type) { | ||||
|       case "yield": | ||||
|         state.copilot.yield.ftoInvest = payload.ftoInvest; | ||||
|         state.copilot.yield.chipInvest = payload.chipInvest; | ||||
|         state.copilot.yield.chipOutput = payload.chipOutput; | ||||
|         state.copilot.yield.stdOutput = payload.stdOutput; | ||||
|         state.copilot.yield.bipvOutput = payload.bipvOutput; | ||||
|         break; | ||||
|       case "energy": | ||||
|         state.copilot.energy = payload.data; | ||||
|         break; | ||||
|       case "efficiency": | ||||
|         state.copilot.efficiency = payload.data; | ||||
|         break; | ||||
|     } | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| const actions = { | ||||
|   /** 初始化首页数据 */ | ||||
|   async initHome({ commit }) { | ||||
|     const dataArr = await getHomeInfo(); | ||||
|     const targetArr = await getHomeTarget(); | ||||
|     const payload = splitCurrentAndPrevious(dataArr, targetArr); | ||||
|     commit("SET_HOME_INFO", payload); | ||||
|   }, | ||||
|   /** 初始化驾驶舱数据 */ | ||||
|   async initCopilot({ commit }, { period, source }) { | ||||
|     const fetcher = { | ||||
|       yield: getCopilotYield, | ||||
|       energy: null, | ||||
|       efficiency: null, | ||||
|     }[source]; | ||||
|     // 获取产量数据 | ||||
|     let { data: factoryList, type } = await fetcher(period); | ||||
|     let targetList = null; | ||||
|     if (source === "yield") { | ||||
|       // 获取目标数据 | ||||
|       let { data } = await fetcher(period, true); | ||||
|       targetList = data; | ||||
|     } | ||||
|     const payload = splitCurrentAndPrevious(factoryList, targetList); | ||||
|     commit("SET_COPILOT_INFO", { type, payload }); | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| export default { | ||||
|   namespaced: true, | ||||
|   state, | ||||
|   mutations, | ||||
|   actions, | ||||
| }; | ||||
|  | ||||
| // utils function | ||||
| function splitCurrentAndPrevious(factoryListResponse, targetListResponse) { | ||||
|   const { chipInvest, ftoInvest, chipOutput, stdOutput, bipvOutput } = init(); | ||||
|   if (factoryListResponse) { | ||||
|     for (const factory of factoryListResponse) { | ||||
|       const fId = getFactoryId(factory); | ||||
|       // 获取目标值 | ||||
|       if (targetListResponse) { | ||||
|         const { chipYield, componentYield, bipvProductOutput } = | ||||
|           getFactoryTargetValue(targetListResponse, fId); | ||||
|         chipOutput.target[fId] = chipYield; | ||||
|         stdOutput.target[fId] = componentYield; | ||||
|         bipvOutput.target[fId] = bipvProductOutput; | ||||
|       } | ||||
|       // 芯片投入 | ||||
|       chipInvest.current[fId] = factory.inputNumber; | ||||
|       chipInvest.previous[fId] = factory.previousYearInputNumber; | ||||
|       // FTO投入 | ||||
|       ftoInvest.current[fId] = factory.chipInput; | ||||
|       ftoInvest.previous[fId] = factory.previousYearChipInput; | ||||
|       // 产出数据, 0 - (玻璃)芯片产出, 1 - 标准组件产出, 2 - BIPV产出 | ||||
|       // 因为后端写的垃圾数据,所以这里要做一下判断 | ||||
|       if (![0, 1, 2].includes(factory.glassType)) continue; | ||||
|       const _t = [chipOutput, stdOutput, bipvOutput][factory.glassType]; | ||||
|       _t.current[fId] = factory.outputNumber; | ||||
|       _t.previous[fId] = factory.previousYearOutputNumber; | ||||
|       // debugger; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|       chipInvest, | ||||
|       ftoInvest, | ||||
|       chipOutput, | ||||
|       stdOutput, | ||||
|       bipvOutput, | ||||
|     }; | ||||
|   } | ||||
| } | ||||
|  | ||||
| function getFactoryId(factory) { | ||||
|   return factory.factory; | ||||
| } | ||||
|  | ||||
| function getFactoryTargetValue(targetList, factoryId) { | ||||
|   const target = targetList.find((item) => item.factory === factoryId); | ||||
|   if (target) { | ||||
|     return { | ||||
|       // 自带模拟数据了.... random_default | ||||
|       chipYield: target.chipYield ?? random_default(), | ||||
|       componentYield: target.componentYield ?? random_default(), | ||||
|       bipvProductOutput: target.bipvProductOutput ?? random_default(), | ||||
|     }; | ||||
|   } | ||||
|   return { | ||||
|     chipYield: random_default(), | ||||
|     componentYield: random_default(), | ||||
|     bipvProductOutput: random_default(), | ||||
|   }; | ||||
| } | ||||
|  | ||||
| function init() { | ||||
|   const t_ = { | ||||
|     current: Array(7).fill(0), | ||||
|     previous: Array(7).fill(0), | ||||
|   }; | ||||
|   // 芯片投入 | ||||
|   const chipInvest = deepClone(t_); | ||||
|   // FTO投入 | ||||
|   const ftoInvest = deepClone(t_); | ||||
|   // 芯片产出 | ||||
|   const chipOutput = { | ||||
|     ...deepClone(t_), | ||||
|     target: Array(7).fill(0), | ||||
|   }; | ||||
|   // 标准组件产出 | ||||
|   const stdOutput = deepClone(chipOutput); | ||||
|   // BIPV产出 | ||||
|   const bipvOutput = deepClone(chipOutput); | ||||
|  | ||||
|   return { | ||||
|     chipInvest, | ||||
|     ftoInvest, | ||||
|     chipOutput, | ||||
|     stdOutput, | ||||
|     bipvOutput, | ||||
|   }; | ||||
| } | ||||
|  | ||||
| function random_default() { | ||||
|   return 0; | ||||
| @@ -34,162 +210,57 @@ async function getHomeTarget() { | ||||
|   return null; | ||||
| } | ||||
|  | ||||
| /* 状态 */ | ||||
| const state = { | ||||
|   copilot: { | ||||
|     /* 产量驾驶舱 */ | ||||
|     yield: {}, | ||||
|     /* 能源驾驶舱 */ | ||||
|     energy: {}, | ||||
|     /* 效率驾驶舱 */ | ||||
|     efficiency: {}, | ||||
|   }, | ||||
|   home: { | ||||
|     ftoInvest: null, | ||||
|     chipInvest: null, | ||||
|     chipOutput: null, | ||||
|     stdOutput: null, | ||||
|     bipvOutput: null, | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| const mutations = { | ||||
|   SET_HOME_INFO: (state, payload) => { | ||||
|     state.home.ftoInvest = payload.ftoInvest; | ||||
|     state.home.chipInvest = payload.chipInvest; | ||||
|     state.home.chipOutput = payload.chipOutput; | ||||
|     state.home.stdOutput = payload.stdOutput; | ||||
|     state.home.bipvOutput = payload.bipvOutput; | ||||
|   }, | ||||
|   SET_COPILOT_INFO: (state) => {}, | ||||
| }; | ||||
|  | ||||
| const actions = { | ||||
|   /** 初始化首页数据 */ | ||||
|   async initHome({ commit }) { | ||||
|     const dataArr = await getHomeInfo(); | ||||
|     const targetArr = await getHomeTarget(); | ||||
|  | ||||
|     const chipInvest = { | ||||
|       current: Array(7).fill(0), | ||||
|       previous: Array(7).fill(0), | ||||
|     }; // 芯片投入 | ||||
|     const ftoInvest = { | ||||
|       current: Array(7).fill(0), | ||||
|       previous: Array(7).fill(0), | ||||
|     }; // FTO投入 | ||||
|     const chipOutput = { | ||||
|       current: Array(7).fill(0), | ||||
|       previous: Array(7).fill(0), | ||||
|       target: Array(7).fill(0), | ||||
|     }; // 芯片产出 | ||||
|     const stdOutput = { | ||||
|       current: Array(7).fill(0), | ||||
|       previous: Array(7).fill(0), | ||||
|       target: Array(7).fill(0), | ||||
|     }; // 标准组件产出 | ||||
|     const bipvOutput = { | ||||
|       current: Array(7).fill(0), | ||||
|       previous: Array(7).fill(0), | ||||
|       target: Array(7).fill(0), | ||||
|     }; // BIPV产出 | ||||
|  | ||||
|     if (dataArr) { | ||||
|       for (const factory of dataArr) { | ||||
|         /* 工厂索引 */ | ||||
|         const factoryId = factory.factory; | ||||
|         /* 收集目标数据 */ | ||||
|         if (targetArr) { | ||||
|           const target = targetArr.find((item) => item.factory === factoryId); | ||||
|           if (target) { | ||||
|             chipOutput.target.splice(factoryId, 1, target.chipYield ?? 0); | ||||
|             stdOutput.target.splice(factoryId, 1, target.componentYield ?? 0); | ||||
|             bipvOutput.target.splice(factoryId, 1, target.bipvProductOutput ?? 0); | ||||
|           } | ||||
|         } | ||||
|         /* 收集芯片投入数据 */ | ||||
|         chipInvest.current.splice( | ||||
|           factoryId, | ||||
|           1, | ||||
|           factory.inputNumber ?? random_default() | ||||
|         ); | ||||
|         chipInvest.previous.splice( | ||||
|           factoryId, | ||||
|           1, | ||||
|           factory.previousYearInputNumber ?? random_default() | ||||
|         ); | ||||
|         /* 收集FTO投入数据 */ | ||||
|         ftoInvest.current.splice( | ||||
|           factoryId, | ||||
|           1, | ||||
|           factory.chipInput ?? random_default() | ||||
|         ); | ||||
|         ftoInvest.previous.splice( | ||||
|           factoryId, | ||||
|           1, | ||||
|           factory.previousYearChipInput ?? random_default() | ||||
|         ); | ||||
|         /* 收集产出数据 */ | ||||
|         switch (factory.glassType) { | ||||
|           case 0: | ||||
|             // 玻璃芯片 产出 | ||||
|             chipOutput.current.splice( | ||||
|               factoryId, | ||||
|               1, | ||||
|               factory.outputNumber ?? random_default() | ||||
|             ); | ||||
|             chipOutput.previous.splice( | ||||
|               factoryId, | ||||
|               1, | ||||
|               factory.previousYearOutputNumber ?? random_default() | ||||
|             ); | ||||
|             break; | ||||
|           case 1: | ||||
|             // 标准组件 产出 | ||||
|             stdOutput.current.splice( | ||||
|               factoryId, | ||||
|               1, | ||||
|               factory.outputNumber ?? random_default() | ||||
|             ); | ||||
|             stdOutput.previous.splice( | ||||
|               factoryId, | ||||
|               1, | ||||
|               factory.previousYearOutputNumber ?? random_default() | ||||
|             ); | ||||
|             break; | ||||
|           case 2: | ||||
|             // BIPV 产出 | ||||
|             bipvOutput.current.splice( | ||||
|               factoryId, | ||||
|               1, | ||||
|               factory.outputNumber ?? random_default() | ||||
|             ); | ||||
|             bipvOutput.previous.splice( | ||||
|               factoryId, | ||||
|               1, | ||||
|               factory.previousYearOutputNumber ?? random_default() | ||||
|             ); | ||||
|             break; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       /* 更新 state */ | ||||
|       commit("SET_HOME_INFO", { | ||||
|         ftoInvest, | ||||
|         chipInvest, | ||||
|         chipOutput, | ||||
|         stdOutput, | ||||
|         bipvOutput, | ||||
|       }); | ||||
| async function fetcher(type = "yield", params) { | ||||
|   const { code, data } = await axios.post( | ||||
|     type == "yield" | ||||
|       ? // 产量 数据 | ||||
|         "/ip/prod-output/query-by-date" | ||||
|       : // 目标数据 | ||||
|         "/ip/prod-target/query-by-date", | ||||
|     { | ||||
|       ...params, | ||||
|     } | ||||
|   }, | ||||
|   /** 初始化驾驶舱数据 */ | ||||
|   async initCopilot({ commit }) {}, | ||||
| }; | ||||
|   ); | ||||
|   if (code == 0) { | ||||
|     return data; | ||||
|   } | ||||
|   console.warn("getCopilotYield failed, code: ", code); | ||||
|   return null; | ||||
| } | ||||
|  | ||||
| export default { | ||||
|   namespaced: true, | ||||
|   state, | ||||
|   mutations, | ||||
|   actions, | ||||
| }; | ||||
| /** | ||||
|  * | ||||
|  * @param {*} period 周期: 日周月年 | ||||
|  * @param {*} target 是否获取目标数据:默认 否 | ||||
|  * @returns | ||||
|  */ | ||||
| async function getCopilotYield(period, target = false) { | ||||
|   // 请求参数,直接一次性获取所有工厂 | ||||
|   let queryParams = { | ||||
|     factorys: [], | ||||
|     date: 4, | ||||
|   }; | ||||
|  | ||||
|   switch (period) { | ||||
|     case "日": | ||||
|       queryParams.date = 1; | ||||
|       break; | ||||
|     case "周": | ||||
|       queryParams.date = 2; | ||||
|       break; | ||||
|     case "月": | ||||
|       queryParams.date = 3; | ||||
|       break; | ||||
|     case "年": | ||||
|       queryParams.date = 4; | ||||
|       break; | ||||
|     default: | ||||
|       queryParams.date = 1; | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   return { | ||||
|     data: await fetcher(target ? "target" : "yield", queryParams), | ||||
|     type: "yield", | ||||
|   }; | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <DoubleRingWrapperVue /> | ||||
|   <DoubleRingWrapperVue data-source="BIPV产出" :period="period" /> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| @@ -15,7 +15,12 @@ import DoubleRingWrapperVue from "./base/DoubleRingWrapper.vue"; | ||||
| export default { | ||||
|   name: "BipvOutput", | ||||
|   components: { DoubleRingWrapperVue }, | ||||
|   props: {}, | ||||
|   props: { | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return {}; | ||||
|   }, | ||||
|   | ||||
							
								
								
									
										106
									
								
								src/views/copilot/components/charts/ChipInvest.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/views/copilot/components/charts/ChipInvest.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| <!--  | ||||
|     filename: FtoInvest.vue | ||||
|     author: liubin | ||||
|     date: 2024-04-10 08:59:28 | ||||
|     description:  | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <bar-chart-base | ||||
|     :legend="legend" | ||||
|     :series="series" | ||||
|     :xAxis="xAxis" | ||||
|     in="ChipInvest" | ||||
|     class="chip-invest-chart" | ||||
|   /> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import BarChartBase from "./base/BarChartBase.vue"; | ||||
|  | ||||
| export default { | ||||
|   name: "ChipInvest", | ||||
|   components: { BarChartBase }, | ||||
|   data() { | ||||
|     // 城市数组的顺序必须是固定的 | ||||
|     const cities = ["瑞昌", "邯郸", "株洲", "佳木斯", "成都", "凯盛", "蚌埠"]; | ||||
|     return { | ||||
|       xAxis: cities, | ||||
|     }; | ||||
|   }, | ||||
|   props: { | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     legend() { | ||||
|       switch (this.period) { | ||||
|         case "日": | ||||
|           return [{ label: "昨日", color: "#12f7f1" }]; | ||||
|         case "周": | ||||
|           return [{ label: "本周", color: "#12f7f1" }]; | ||||
|         case "月": { | ||||
|           const year = new Date().getFullYear(); | ||||
|           const month = new Date().getMonth() + 1; | ||||
|           return [ | ||||
|             { label: `${year - 1}年${month}月`, color: "#12f7f1" }, | ||||
|             { label: `${year}年${month}月`, color: "#58adfa" }, | ||||
|           ]; | ||||
|         } | ||||
|         case "年": { | ||||
|           const year = new Date().getFullYear(); | ||||
|           return [ | ||||
|             { label: `${year - 1}年`, color: "#12f7f1" }, | ||||
|             { label: `${year}年`, color: "#58adfa" }, | ||||
|           ]; | ||||
|         } | ||||
|         default: | ||||
|           return [ | ||||
|             { label: `${year - 1}年`, color: "#12f7f1" }, | ||||
|             { label: `${year}年`, color: "#58adfa" }, | ||||
|           ]; | ||||
|       } | ||||
|     }, | ||||
|     series() { | ||||
|       const { chipInvest } = this.$store.getters.copilot.yield; | ||||
|       let dataList = null; | ||||
|       switch (this.period) { | ||||
|         case "日": | ||||
|         case "周": | ||||
|           dataList = chipInvest?.current; | ||||
|           break; | ||||
|         default: | ||||
|           dataList = []; | ||||
|           dataList[0] = chipInvest?.pervious; | ||||
|           dataList[1] = chipInvest?.current; | ||||
|       } | ||||
|       return getTemplate(this.period, dataList); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| function getTemplate(period, dataList) { | ||||
|   const year = new Date().getFullYear(); | ||||
|   const month = new Date().getMonth() + 1; | ||||
|   return period == "日" || period == "周" | ||||
|     ? [ | ||||
|         { | ||||
|           name: period == "日" ? "昨日" : "本周", | ||||
|           data: dataList ?? [], | ||||
|         }, | ||||
|       ] | ||||
|     : [ | ||||
|         { | ||||
|           name: period == "年" ? `${year - 1}年` : `${year - 1}年${month}月`, | ||||
|           data: dataList ? dataList[0] : [], | ||||
|         }, | ||||
|         { | ||||
|           name: period == "年" ? `${year}年` : `${year}年${month}月`, | ||||
|           data: dataList ? dataList[1] : [], | ||||
|           // : Array.from({ length: 7 }, () => Math.floor(Math.random() * 1000)), | ||||
|         }, | ||||
|       ]; | ||||
| } | ||||
| </script> | ||||
| @@ -6,7 +6,7 @@ | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <DoubleRingWrapperVue /> | ||||
|   <DoubleRingWrapperVue data-source="芯片产出" :period="period" /> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| @@ -15,7 +15,12 @@ import DoubleRingWrapperVue from "./base/DoubleRingWrapper.vue"; | ||||
| export default { | ||||
|   name: "ChipOutput", | ||||
|   components: { DoubleRingWrapperVue }, | ||||
|   props: {}, | ||||
|   props: { | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return {}; | ||||
|   }, | ||||
|   | ||||
							
								
								
									
										108
									
								
								src/views/copilot/components/charts/FtoInvest.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/views/copilot/components/charts/FtoInvest.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| <!--  | ||||
|     filename: FtoInvest.vue | ||||
|     author: liubin | ||||
|     date: 2024-04-10 08:59:28 | ||||
|     description:  | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <bar-chart-base | ||||
|     :legend="legend" | ||||
|     :series="series" | ||||
|     :xAxis="xAxis" | ||||
|     in="ftoInvest" | ||||
|     class="fto-chart" | ||||
|   /> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import BarChartBase from "./base/BarChartBase.vue"; | ||||
|  | ||||
| export default { | ||||
|   name: "FtoInvest", | ||||
|   components: { BarChartBase }, | ||||
|   data() { | ||||
|     // 城市数组的顺序必须是固定的 | ||||
|     const cities = ["瑞昌", "邯郸", "株洲", "佳木斯", "成都", "凯盛", "蚌埠"]; | ||||
|     return { | ||||
|       xAxis: cities, | ||||
|     }; | ||||
|   }, | ||||
|   props: { | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     legend() { | ||||
|       switch (this.period) { | ||||
|         case "日": | ||||
|           return [{ label: "昨日", color: "#12f7f1" }]; | ||||
|         case "周": | ||||
|           return [{ label: "本周", color: "#12f7f1" }]; | ||||
|         case "月": { | ||||
|           const year = new Date().getFullYear(); | ||||
|           const month = new Date().getMonth() + 1; | ||||
|           return [ | ||||
|             { label: `${year - 1}年${month}月`, color: "#12f7f1" }, | ||||
|             { label: `${year}年${month}月`, color: "#58adfa" }, | ||||
|           ]; | ||||
|         } | ||||
|         case "年": { | ||||
|           const year = new Date().getFullYear(); | ||||
|           return [ | ||||
|             { label: `${year - 1}年`, color: "#12f7f1" }, | ||||
|             { label: `${year}年`, color: "#58adfa" }, | ||||
|           ]; | ||||
|         } | ||||
|         default: | ||||
|           return [ | ||||
|             { label: `${year - 1}年`, color: "#12f7f1" }, | ||||
|             { label: `${year}年`, color: "#58adfa" }, | ||||
|           ]; | ||||
|       } | ||||
|     }, | ||||
|     series() { | ||||
|       const { ftoInvest } = this.$store.getters.copilot.yield; | ||||
|       let dataList = null; | ||||
|  | ||||
|       switch (this.period) { | ||||
|         case "日": | ||||
|         case "周": | ||||
|           dataList = ftoInvest?.current; | ||||
|           break; | ||||
|         default: | ||||
|           dataList = []; | ||||
|           dataList[0] = ftoInvest?.pervious; | ||||
|           dataList[1] = ftoInvest?.current; | ||||
|       } | ||||
|  | ||||
|       return getTemplate(this.period, dataList); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| function getTemplate(period, dataList) { | ||||
|   const year = new Date().getFullYear(); | ||||
|   const month = new Date().getMonth() + 1; | ||||
|   return period == "日" || period == "周" | ||||
|     ? [ | ||||
|         { | ||||
|           name: period == "日" ? "昨日" : "本周", | ||||
|           data: dataList ?? [], | ||||
|         }, | ||||
|       ] | ||||
|     : [ | ||||
|         { | ||||
|           name: period == "年" ? `${year - 1}年` : `${year - 1}年${month}月`, | ||||
|           data: dataList ? dataList[0] : [], | ||||
|         }, | ||||
|         { | ||||
|           name: period == "年" ? `${year}年` : `${year}年${month}月`, | ||||
|           data: dataList ? dataList[1] : [], | ||||
|           // : Array.from({ length: 7 }, () => Math.floor(Math.random() * 1000)), | ||||
|         }, | ||||
|       ]; | ||||
| } | ||||
| </script> | ||||
| @@ -6,7 +6,7 @@ | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <DoubleRingWrapperVue /> | ||||
|   <DoubleRingWrapperVue data-source="标准组件产出" :period="period" /> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| @@ -15,7 +15,12 @@ import DoubleRingWrapperVue from "./base/DoubleRingWrapper.vue"; | ||||
| export default { | ||||
|   name: "StdOutput", | ||||
|   components: { DoubleRingWrapperVue }, | ||||
|   props: {}, | ||||
|   props: { | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return {}; | ||||
|   }, | ||||
|   | ||||
							
								
								
									
										257
									
								
								src/views/copilot/components/charts/base/BarChartBase.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								src/views/copilot/components/charts/base/BarChartBase.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,257 @@ | ||||
| <!--  | ||||
|     filename: BarChartBase.vue | ||||
|     author: liubin | ||||
|     date: 2024-04-10 08:59:28 | ||||
|     description:  | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <chart-container class="bar-chart-base"> | ||||
|     <div class="legend"> | ||||
|       <span | ||||
|         v-for="item in legend" | ||||
|         :key="item.label" | ||||
|         class="legend-item" | ||||
|         :style="{ fontSize: isFullscreen ? '0.58vw' : '0.54vw' }" | ||||
|         >{{ item.label }}</span | ||||
|       > | ||||
|     </div> | ||||
|     <div | ||||
|       ref="chart" | ||||
|       style="max-width: 50vw" | ||||
|       :style="{ height: vHeight + 'vh' }" | ||||
|     ></div> | ||||
|   </chart-container> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import screenfull from "screenfull"; | ||||
| import ChartContainerVue from "../../../../components/ChartContainer.vue"; | ||||
| import chartMixin from "@/mixins/chart.js"; | ||||
|  | ||||
| export default { | ||||
|   name: "BarChartBase", | ||||
|   components: { | ||||
|     ChartContainer: ChartContainerVue, | ||||
|   }, | ||||
|   mixins: [chartMixin], | ||||
|   props: { | ||||
|     vHeight: { | ||||
|       type: Number, | ||||
|       default: 34, | ||||
|     }, | ||||
|     legend: { | ||||
|       type: Array, | ||||
|       required: true, | ||||
|     }, | ||||
|     xAxis: { | ||||
|       type: Array, | ||||
|       required: true, | ||||
|     }, | ||||
|     series: { | ||||
|       type: Array, | ||||
|       required: true, | ||||
|     }, | ||||
|     in: { | ||||
|       type: String, | ||||
|       default: "", | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       isFullscreen: false, | ||||
|       actualOptions: null, | ||||
|       options: { | ||||
|         grid: { | ||||
|           left: "5%", | ||||
|           right: "4%", | ||||
|           bottom: "3%", | ||||
|           top: "15%", | ||||
|           containLabel: true, | ||||
|         }, | ||||
|         tooltip: {}, | ||||
|         xAxis: { | ||||
|           axisTick: { | ||||
|             show: false, | ||||
|           }, | ||||
|           axisLine: { | ||||
|             lineStyle: { | ||||
|               color: "#4561AE", | ||||
|             }, | ||||
|           }, | ||||
|           axisLabel: { | ||||
|             color: "#fff", | ||||
|             fontSize: 12, | ||||
|           }, | ||||
|           data: this.xAxis, | ||||
|         }, | ||||
|         yAxis: { | ||||
|           name: "单位/片", | ||||
|           nameTextStyle: { | ||||
|             color: "#fff", | ||||
|             fontSize: 12, | ||||
|           }, | ||||
|           axisTick: { | ||||
|             show: false, | ||||
|           }, | ||||
|           axisLabel: { | ||||
|             color: "#fff", | ||||
|             fontSize: 12, | ||||
|           }, | ||||
|           axisLine: { | ||||
|             show: true, | ||||
|             lineStyle: { | ||||
|               color: "#4561AE", | ||||
|             }, | ||||
|           }, | ||||
|           splitLine: { | ||||
|             lineStyle: { | ||||
|               color: "#4561AE", | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|         series: [ | ||||
|           { | ||||
|             name: "", // this.series[0].name, | ||||
|             type: "bar", | ||||
|             barWidth: 12, | ||||
|             itemStyle: { | ||||
|               borderRadius: [10, 10, 0, 0], | ||||
|               color: { | ||||
|                 type: "linear", | ||||
|                 x: 0, | ||||
|                 y: 0, | ||||
|                 x2: 0, | ||||
|                 y2: 1, | ||||
|                 colorStops: [ | ||||
|                   { | ||||
|                     offset: 0, | ||||
|                     color: "#12f7f1", // 0% 处的颜色 | ||||
|                   }, | ||||
|                   { | ||||
|                     offset: 0.35, | ||||
|                     color: "#12f7f177", // 100% 处的颜色 | ||||
|                   }, | ||||
|                   { | ||||
|                     offset: 0.75, | ||||
|                     color: "#12f7f133", // 100% 处的颜色 | ||||
|                   }, | ||||
|                   { | ||||
|                     offset: 1, | ||||
|                     color: "transparent", // 100% 处的颜色 | ||||
|                   }, | ||||
|                 ], | ||||
|                 global: false, // 缺省为 false | ||||
|               }, | ||||
|             }, | ||||
|             data: [], // this.series[0].data, | ||||
|           }, | ||||
|           { | ||||
|             name: "", // this.series[1].name, | ||||
|             type: "bar", | ||||
|             barWidth: 12, | ||||
|             // tooltip: { | ||||
|             //   valueFormatter: function (value) { | ||||
|             //     return value + " ml"; | ||||
|             //   }, | ||||
|             // }, | ||||
|             itemStyle: { | ||||
|               borderRadius: [10, 10, 0, 0], | ||||
|               color: { | ||||
|                 type: "linear", | ||||
|                 x: 0, | ||||
|                 y: 0, | ||||
|                 x2: 0, | ||||
|                 y2: 1, | ||||
|                 colorStops: [ | ||||
|                   { | ||||
|                     offset: 0, | ||||
|                     color: "#57abf8", // 0% 处的颜色 | ||||
|                   }, | ||||
|                   { | ||||
|                     offset: 1, | ||||
|                     color: "#364BFE66", // 100% 处的颜色 | ||||
|                   }, | ||||
|                 ], | ||||
|                 global: false, // 缺省为 false | ||||
|               }, | ||||
|             }, | ||||
|             data: [], // this.series[1].data, | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|     }; | ||||
|   }, | ||||
|   watch: { | ||||
|     /** 全屏状态切换时,对柱子粗细和字体大小进行相应调整 */ | ||||
|     isFullscreen(val) { | ||||
|       this.actualOptions.series.map((item) => { | ||||
|         item.barWidth = val ? 18 : 12; | ||||
|       }); | ||||
|       this.actualOptions.xAxis.axisLabel.fontSize = val ? 18 : 12; | ||||
|       this.actualOptions.yAxis.axisLabel.fontSize = val ? 18 : 12; | ||||
|       this.actualOptions.yAxis.nameTextStyle.fontSize = val ? 18 : 12; | ||||
|       this.initOptions(this.actualOptions); | ||||
|     }, | ||||
|     series(val) { | ||||
|       if (!val) { | ||||
|         this.initOptions(this.options); | ||||
|         return; | ||||
|       } | ||||
|       const actualOptions = JSON.parse(JSON.stringify(this.options)); | ||||
|       actualOptions.series[0].data = val[0].data; | ||||
|       actualOptions.series[0].name = val[0].name; | ||||
|       actualOptions.series[1].data = val?.[1]?.data || []; | ||||
|       actualOptions.series[1].name = val?.[1]?.name || ""; | ||||
|       this.actualOptions = actualOptions; | ||||
|       this.initOptions(actualOptions); | ||||
|     }, | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.actualOptions = this.options; | ||||
|     this.initOptions(this.options); | ||||
|  | ||||
|     if (screenfull.isEnabled) { | ||||
|       screenfull.on("change", () => { | ||||
|         this.isFullscreen = screenfull.isFullscreen; | ||||
|       }); | ||||
|     } | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| .bar-chart-base { | ||||
|   // position: relative; | ||||
|  | ||||
|   .legend { | ||||
|     position: absolute; | ||||
|     top: 5.2vh; | ||||
|     right: 1vw; | ||||
|   } | ||||
|  | ||||
|   .legend-item { | ||||
|     position: relative; | ||||
|     // font-size: 12px; | ||||
|     margin-right: 0.67vw; | ||||
|  | ||||
|     &::before { | ||||
|       content: ""; | ||||
|       display: inline-block; | ||||
|       width: 0.531vw; | ||||
|       height: 0.531vw; | ||||
|       background-color: #ccc; | ||||
|       border-radius: 2px; | ||||
|       margin-right: 0.22vw; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .legend-item:nth-child(1):before { | ||||
|     background-color: #12f7f1; | ||||
|   } | ||||
|  | ||||
|   .legend-item:nth-child(2):before { | ||||
|     background-color: #58adfa; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										148
									
								
								src/views/copilot/components/charts/base/CityData.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/views/copilot/components/charts/base/CityData.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| <!--  | ||||
|     filename: CityData.vue | ||||
|     author: liubin | ||||
|     date: 2024-04-17 09:55:12 | ||||
|     description:  | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <div class="city-data"> | ||||
|     <div class="headquarter"> | ||||
|       <div class="inner-shadow w-1"></div> | ||||
|       <div class="inner-shadow flex-1 flex"> | ||||
|         <CityName value="凯盛光伏" /> | ||||
|         <CityValue :value="headquarterValue" horizontal :period="period" /> | ||||
|       </div> | ||||
|       <div class="inner-shadow w-1"></div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="city-item-container"> | ||||
|       <CityItem | ||||
|         v-for="city in cities" | ||||
|         :key="city.name" | ||||
|         :location="city.name" | ||||
|         :value="city.value" | ||||
|         :period="period" | ||||
|       /> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import CityItemVue from "./CityItem.vue"; | ||||
| import CityNameVue from "./CityName.vue"; | ||||
| import CityValueVue from "./CityValue.vue"; | ||||
|  | ||||
| export default { | ||||
|   name: "CityData", | ||||
|   components: { | ||||
|     CityItem: CityItemVue, | ||||
|     CityName: CityNameVue, | ||||
|     CityValue: CityValueVue, | ||||
|   }, | ||||
|   props: { | ||||
|     dataSource: { | ||||
|       type: String, | ||||
|       default: null, | ||||
|     }, | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return {}; | ||||
|   }, | ||||
|   computed: { | ||||
|     headquarterValue() { | ||||
|       let getterName = ""; | ||||
|       switch (this.dataSource) { | ||||
|         case "标准组件产出": | ||||
|           getterName = "stdOutput"; | ||||
|           break; | ||||
|         case "芯片产出": | ||||
|           getterName = "chipOutput"; | ||||
|           break; | ||||
|         case "BIPV产出": | ||||
|           getterName = "bipvOutput"; | ||||
|           break; | ||||
|       } | ||||
|       return ( | ||||
|         "" + (this.$store.getters.copilot.yield[getterName]?.current?.[5] ?? 0) | ||||
|       ); | ||||
|     }, | ||||
|     cities() { | ||||
|       let getterName = ""; | ||||
|       switch (this.dataSource) { | ||||
|         case "标准组件产出": | ||||
|           getterName = "stdOutput"; | ||||
|           break; | ||||
|         case "芯片产出": | ||||
|           getterName = "chipOutput"; | ||||
|           break; | ||||
|         case "BIPV产出": | ||||
|           getterName = "bipvOutput"; | ||||
|           break; | ||||
|       } | ||||
|       const _cities = [ | ||||
|         { name: "瑞昌", value: 0 }, | ||||
|         { name: "邯郸", value: 0 }, | ||||
|         { name: "株洲", value: 0 }, | ||||
|         { name: "佳木斯", value: 0 }, | ||||
|         { name: "成都", value: 0 }, | ||||
|         { name: "凯盛光伏", value: 0 }, | ||||
|         { name: "蚌埠兴科", value: 0 }, | ||||
|       ]; | ||||
|       this.$store.getters.copilot.yield[getterName]?.current?.forEach( | ||||
|         (v, idx) => { | ||||
|           _cities[idx].value = v ?? 0; | ||||
|         } | ||||
|       ); | ||||
|       _cities.splice(4, 1); | ||||
|       return _cities; | ||||
|     }, | ||||
|   }, | ||||
|   mounted() {}, | ||||
|   methods: {}, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| .city-data { | ||||
|   width: 100%; | ||||
|   flex: 1; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   gap: 8px; | ||||
| } | ||||
|  | ||||
| .headquarter { | ||||
|   flex: 1; | ||||
|   display: flex; | ||||
|   gap: 8px; | ||||
| } | ||||
|  | ||||
| .w-1 { | ||||
|   width: 70px; | ||||
| } | ||||
|  | ||||
| .flex { | ||||
|   display: flex; | ||||
| } | ||||
|  | ||||
| .flex-1 { | ||||
|   flex: 1; | ||||
| } | ||||
|  | ||||
| .inner-shadow { | ||||
|   box-shadow: inset 0 0 12px 2px #fff3; | ||||
|   border-radius: 4px; | ||||
| } | ||||
|  | ||||
| .city-item-container { | ||||
|   flex: 3; | ||||
|   display: grid; | ||||
|   grid-template-columns: repeat(2, 1fr); | ||||
|   gap: 8px; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										58
									
								
								src/views/copilot/components/charts/base/CityItem.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/views/copilot/components/charts/base/CityItem.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| <!--  | ||||
|     filename: CityItem.vue | ||||
|     author: liubin | ||||
|     date: 2024-04-17 09:55:12 | ||||
|     description:  | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <div class="city-item inner-shadow"> | ||||
|     <CityName :value="location" /> | ||||
|     <CityValue :value="value+''" :period="period" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import CityNameVue from "./CityName.vue"; | ||||
| import CityValueVue from "./CityValue.vue"; | ||||
| import GradientTextVue from "./GradientText.vue"; | ||||
| export default { | ||||
|   name: "CityItem", | ||||
|   components: { | ||||
|     GradientTextVue, | ||||
|     CityName: CityNameVue, | ||||
|     CityValue: CityValueVue, | ||||
|   }, | ||||
|   props: { | ||||
|     location: { | ||||
|       type: String, | ||||
|       default: "", | ||||
|     }, | ||||
|     value: { | ||||
|       type: Number, | ||||
|       default: 0, | ||||
|     }, | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return {}; | ||||
|   }, | ||||
|   computed: {}, | ||||
|   mounted() {}, | ||||
|   methods: {}, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| .city-item { | ||||
|   display: flex; | ||||
| } | ||||
|  | ||||
| .inner-shadow { | ||||
|   box-shadow: inset 0 0 12px 2px #fff3; | ||||
|   border-radius: 4px; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										55
									
								
								src/views/copilot/components/charts/base/CityName.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/views/copilot/components/charts/base/CityName.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| <!--  | ||||
|     filename: CityName.vue | ||||
|     author: liubin | ||||
|     date: 2024-04-10 08:59:28 | ||||
|     description:  | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <div class="city-name"> | ||||
|     <img :src="Icon" alt="city icon" /> | ||||
|     <span>{{ value }}</span> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import Icon from "./icon.png"; | ||||
|  | ||||
| export default { | ||||
|   name: "CityName", | ||||
|   props: { | ||||
|     value: { | ||||
|       type: String, | ||||
|       default: "", | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { Icon }; | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .city-name { | ||||
|   min-width: 80px; | ||||
|   margin: auto; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   padding: 0 8px; | ||||
|   flex: 1; | ||||
| } | ||||
|  | ||||
| img { | ||||
|   /* width: 32px; */ | ||||
|   width: 1.543vw; | ||||
| } | ||||
|  | ||||
| span { | ||||
|   /* font-size: 12px;   */ | ||||
|   font-size: 0.77vw; | ||||
|   letter-spacing: 2px; | ||||
|   text-align: center; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										127
									
								
								src/views/copilot/components/charts/base/CityValue.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/views/copilot/components/charts/base/CityValue.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| <!--  | ||||
|     filename: CityValue.vue | ||||
|     author: liubin | ||||
|     date: 2024-04-10 08:59:28 | ||||
|     description:  | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <div class="city-value" :class="[horizontal ? 'horizontal' : '']"> | ||||
|     <span class="hint" :class="[horizontal ? 'horizontal' : '']">{{ | ||||
|       period == "周" ? "本周产出" : "今日产出" | ||||
|     }}</span> | ||||
|     <span class="value" :class="[horizontal ? 'horizontal' : '']">{{ | ||||
|       value | numberFilter | ||||
|     }}</span> | ||||
|     <!-- <GradientTextVue :text="value" :size="horizontal ? 32 : 26" /> --> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import GradientTextVue from "./GradientText.vue"; | ||||
|  | ||||
| export default { | ||||
|   name: "CityValue", | ||||
|   components: { GradientTextVue }, | ||||
|   props: { | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|     value: { | ||||
|       type: String, | ||||
|       default: "", | ||||
|     }, | ||||
|     horizontal: { | ||||
|       type: Boolean, | ||||
|       default: false, | ||||
|     }, | ||||
|   }, | ||||
|   filters: { | ||||
|     numberFilter(value) { | ||||
|       if (value != null && !isNaN(parseInt(value))) { | ||||
|         return parseInt(value).toLocaleString(); | ||||
|       } else { | ||||
|         return value; | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return {}; | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .city-value { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   padding: 0 8px; | ||||
|   flex: 2; | ||||
|   position: relative; | ||||
| } | ||||
|  | ||||
| .city-value.horizontal { | ||||
|   flex-direction: row; | ||||
| } | ||||
|  | ||||
| .city-value::after { | ||||
|   content: ""; | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   left: 0; | ||||
|   width: 1px; | ||||
|   height: 100%; | ||||
|   background: linear-gradient( | ||||
|     to bottom, | ||||
|     transparent 20%, | ||||
|     #fff 50%, | ||||
|     transparent 80% | ||||
|   ); | ||||
| } | ||||
|  | ||||
| span.hint { | ||||
|   margin: 0 0.77vw; | ||||
|   font-size: 0.77vw; | ||||
|   order: 2; | ||||
|   /* margin: 0 12px; | ||||
|   width: 32px; | ||||
|   font-size: 12px; */ | ||||
| } | ||||
|  | ||||
| span.hint.horizontal { | ||||
|   margin: 0 1.235vw; | ||||
|   width: 1.543vw; | ||||
|   order: 1; | ||||
|   font-size: 0.77vw; | ||||
|   /* margin: 0 12px; | ||||
|   width: 32px; | ||||
|   font-size: 12px; */ | ||||
| } | ||||
|  | ||||
| .value { | ||||
|   color: #4dd2fe; | ||||
|   text-align: center; | ||||
|   font-size: 1.132vw; | ||||
|   order: 1; | ||||
| } | ||||
| .value.horizontal { | ||||
|   text-align: left; | ||||
|   flex: 1; | ||||
|   font-size: 1.543vw; | ||||
|   order: 2; | ||||
| } | ||||
|  | ||||
| svg, | ||||
| .value { | ||||
|   width: 100px; | ||||
|   order: 1; | ||||
| } | ||||
|  | ||||
| .value.horizontal, | ||||
| svg.horizontal { | ||||
|   order: 2; | ||||
| } | ||||
| </style> | ||||
| @@ -12,9 +12,7 @@ | ||||
|     <div class="double-ring-chart__legend"> | ||||
|       <div v-for="item in legendItems" :key="item.label" class="legend-item"> | ||||
|         <span class="legend-item__label">{{ item.label }}</span> | ||||
|         <span class="legend-item__value">{{ | ||||
|           item.value.toLocaleString() | ||||
|         }}</span> | ||||
|         <span class="legend-item__value">{{ item.value | numberFilter }}</span> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| @@ -23,7 +21,7 @@ | ||||
| <script> | ||||
| import chartMixin from "@/mixins/chart.js"; | ||||
| import fullscreenMixin from "@/mixins/fullscreen.js"; | ||||
| import options from "./double-ring-chart-options"; | ||||
| import getOptions from "./double-ring-chart-options"; | ||||
|  | ||||
| export default { | ||||
|   name: "DoubleRingChart", | ||||
| @@ -33,21 +31,88 @@ export default { | ||||
|       type: Number, | ||||
|       default: 24, | ||||
|     }, | ||||
|     legendItems: { | ||||
|       type: Array, | ||||
|       default: () => [ | ||||
|         { label: "2023年累计", value: 88002 }, | ||||
|         { label: "2024年累计", value: 88002 }, | ||||
|         { label: "2025年累计", value: 88002 }, | ||||
|       ], | ||||
|     factoryId: { | ||||
|       type: Number, | ||||
|       required: true, | ||||
|     }, | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|     dataSource: { | ||||
|       type: String, | ||||
|       default: null, | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return {}; | ||||
|   }, | ||||
|   computed: {}, | ||||
|   filters: { | ||||
|     numberFilter(val) { | ||||
|       if (!isNaN(val)) { | ||||
|         return (+val).toLocaleString(); | ||||
|       } | ||||
|       return 0; | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     dataSourceField() { | ||||
|       switch (this.dataSource) { | ||||
|         case "标准组件产出": | ||||
|           return "stdOutput"; | ||||
|         case "芯片产出": | ||||
|           return "chipOutput"; | ||||
|         case "BIPV产出": | ||||
|           return "bipvOutput"; | ||||
|       } | ||||
|     }, | ||||
|     valueTuple() { | ||||
|       // [previousValue, currentValue, sumValue?] | ||||
|       const getter = this.$store.getters.copilot.yield[this.dataSourceField]; | ||||
|       if (this.period === "日" || this.period === "周") { | ||||
|         return [ | ||||
|           getter.previous[this.factoryId], | ||||
|           getter.current[this.factoryId], | ||||
|         ]; | ||||
|       } | ||||
|       return [ | ||||
|         getter.previous[this.factoryId], | ||||
|         getter.current[this.factoryId], | ||||
|         getter.target[this.factoryId], | ||||
|       ]; | ||||
|     }, | ||||
|  | ||||
|     options() { | ||||
|       const year = new Date().getFullYear(); | ||||
|       const month = new Date().getMonth() + 1; | ||||
|       const vt = this.valueTuple; | ||||
|       let titleValue = | ||||
|           vt[0] != null && vt[2] != null && vt[2] !== 0 | ||||
|             ? `${vt[1] / vt[2]}%` | ||||
|             : "0%", | ||||
|         subtitle = | ||||
|           this.period == "月" ? `${month}月累计产出` : `${year}年累计产出`; | ||||
|  | ||||
|       return getOptions({ | ||||
|         titleValue, | ||||
|         subtitle, | ||||
|         previousSum: this.valueTuple[0], | ||||
|         currentSum: this.valueTuple[1], | ||||
|         targetSum: this.valueTuple[2], | ||||
|       }); | ||||
|     }, | ||||
|  | ||||
|     legendItems() { | ||||
|       return calculateItems(this.period, this.valueTuple); | ||||
|     }, | ||||
|   }, | ||||
|   watch: { | ||||
|     legendItems() { | ||||
|       this.initOptions(this.options); | ||||
|     }, | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.initOptions(options); | ||||
|     this.initOptions(this.options); | ||||
|   }, | ||||
|   methods: { | ||||
|     // fullscreen mixin 需要的回调 | ||||
| @@ -56,6 +121,42 @@ export default { | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| function calculateItems(period, valueTuple) { | ||||
|   let items = []; | ||||
|   const today = new Date().getDate(); | ||||
|   const month = new Date().getMonth() + 1; | ||||
|   const year = new Date().getFullYear(); | ||||
|   switch (period) { | ||||
|     case "日": | ||||
|       items = [ | ||||
|         { label: `${month}月${today}日累计`, value: valueTuple[1] }, | ||||
|         { label: `去年${month}月${today}日累计`, value: valueTuple[0] }, | ||||
|       ]; | ||||
|       break; | ||||
|     case "周": | ||||
|       items = [ | ||||
|         { label: `本周累计`, value: valueTuple[1] }, | ||||
|         { label: `去年本周累计`, value: valueTuple[0] }, | ||||
|       ]; | ||||
|       break; | ||||
|     case "月": | ||||
|       items = [ | ||||
|         { label: `${month}月累计`, value: valueTuple[1] }, | ||||
|         { label: `去年${month}月累计`, value: valueTuple[0] }, | ||||
|         { label: `${month}月目标`, value: valueTuple[2] }, | ||||
|       ]; | ||||
|       break; | ||||
|     case "年": | ||||
|       items = [ | ||||
|         { label: `${year}年累计`, value: valueTuple[1] }, | ||||
|         { label: `${year - 1}年累计`, value: valueTuple[0] }, | ||||
|         { label: `${year}年目标`, value: valueTuple[2] }, | ||||
|       ]; | ||||
|       break; | ||||
|   } | ||||
|   return items; | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| @@ -100,17 +201,17 @@ export default { | ||||
| } | ||||
|  | ||||
| .legend-item:nth-child(1) .legend-item__label::before { | ||||
|   background: #0f65ff; | ||||
|   background: #12fff5; | ||||
| } | ||||
| .legend-item:nth-child(1) .legend-item__value { | ||||
|   color: #0f65ff; | ||||
|   color: #12fff5; | ||||
| } | ||||
|  | ||||
| .legend-item:nth-child(2) .legend-item__label::before { | ||||
|   background: #12fff5; | ||||
|   background: #0f65ff; | ||||
| } | ||||
| .legend-item:nth-child(2) .legend-item__value { | ||||
|   color: #12fff5; | ||||
|   color: #0f65ff; | ||||
| } | ||||
|  | ||||
| .legend-item:nth-child(3) .legend-item__label::before { | ||||
|   | ||||
| @@ -7,24 +7,46 @@ | ||||
|  | ||||
| <template> | ||||
|   <div class="double-ring-wrapper"> | ||||
|     <copilot-select :options="cityOptions" /> | ||||
|     <div class="flex-1 stretch"> | ||||
|       <DoubleRingChartVue /> | ||||
|     </div> | ||||
|     <template v-if="period == '月' || period == '年'"> | ||||
|       <copilot-select | ||||
|         @update:active="handleActiveUpdate" | ||||
|         :options="cityOptions" | ||||
|       /> | ||||
|       <div class="flex-1 stretch"> | ||||
|         <DoubleRingChartVue | ||||
|           :data-source="dataSource" | ||||
|           :period="period" | ||||
|           :factoryId="factoryId" | ||||
|         /> | ||||
|       </div> | ||||
|     </template> | ||||
|     <template v-else> | ||||
|       <CityData :data-source="dataSource" :period="period" /> | ||||
|     </template> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import CopilotSelect from "../../select.vue"; | ||||
| import fetcher from "./fetcherDoubleRing"; | ||||
| import DoubleRingChartVue from "./DoubleRingChart.vue"; | ||||
| import CityData from "./CityData.vue"; | ||||
|  | ||||
| export default { | ||||
|   name: "DoubleRingWrapper", | ||||
|   components: { CopilotSelect, DoubleRingChartVue }, | ||||
|   props: {}, | ||||
|   components: { CopilotSelect, DoubleRingChartVue, CityData }, | ||||
|   props: { | ||||
|     dataSource: { | ||||
|       type: String, | ||||
|       default: null, | ||||
|     }, | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       factoryId: 4, // 默认成都 | ||||
|       cityOptions: [ | ||||
|         "成都", | ||||
|         "邯郸", | ||||
| @@ -36,13 +58,11 @@ export default { | ||||
|       ], | ||||
|     }; | ||||
|   }, | ||||
|   computed: {}, | ||||
|   mounted() { | ||||
|     fetcher.getData().then((res) => { | ||||
|       console.log("getData--->", res); | ||||
|     }); | ||||
|   methods: { | ||||
|     handleActiveUpdate(index) { | ||||
|       this.factoryId = index; | ||||
|     }, | ||||
|   }, | ||||
|   methods: {}, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
|   | ||||
							
								
								
									
										64
									
								
								src/views/copilot/components/charts/base/GradientText.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/views/copilot/components/charts/base/GradientText.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| <!--  | ||||
|     filename: GradientText.vue | ||||
|     author: liubin | ||||
|     date: 2024-04-24 16:33:25 | ||||
|     description:  | ||||
| --> | ||||
|  | ||||
| <template> | ||||
|   <svg :height="size + 8" width="100%"> | ||||
|     <defs> | ||||
|       <linearGradient id="smoke-text" x1="0%" y1="0%" x2="0%" y2="100%"> | ||||
|         <stop offset="0%" style="stop-color: #00fff4; stop-opacity: 1" /> | ||||
|         <stop offset="100%" style="stop-color: #37bdfe; stop-opacity: 1" /> | ||||
|       </linearGradient> | ||||
|     </defs> | ||||
|     <text | ||||
|       x="0" | ||||
|       :y="size" | ||||
|       fill="url(#smoke-text)" | ||||
|       :style="{ | ||||
|         fontSize: `${size}px`, | ||||
|         letterSpacing: spacing || '2px', | ||||
|         fontFamily: 'Calibri, Verdana, sans-serif', | ||||
|       }" | ||||
|     > | ||||
|       {{ text | numberFilter }} | ||||
|     </text> | ||||
|   </svg> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "GradientText", | ||||
|   components: {}, | ||||
|   props: { | ||||
|     text: { | ||||
|       type: String, | ||||
|       default: "Test", | ||||
|     }, | ||||
|     spacing: { | ||||
|       type: String, | ||||
|       default: "1px", | ||||
|     }, | ||||
|     size: { | ||||
|       type: Number, | ||||
|       default: 24, | ||||
|     }, | ||||
|   }, | ||||
|   filters: { | ||||
|     numberFilter(value) { | ||||
|       if (value != null && !isNaN(parseInt(value))) { | ||||
|         return parseInt(value).toLocaleString(); | ||||
|       } else { | ||||
|         return value; | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return {}; | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"></style> | ||||
| @@ -1,4 +1,10 @@ | ||||
| export default { | ||||
| export default ({ | ||||
|   titleValue, | ||||
|   subtitle, | ||||
|   previousSum, | ||||
|   currentSum, | ||||
|   targetSum, | ||||
| }) => ({ | ||||
|   grid: { | ||||
|     left: 0, | ||||
|     right: 0, | ||||
| @@ -8,16 +14,16 @@ export default { | ||||
|   }, | ||||
|   tooltip: {}, | ||||
|   title: { | ||||
|     text: "78%", | ||||
|     left: "50%", | ||||
|     top: "40%", | ||||
|     text: titleValue, | ||||
|     left: "49%", | ||||
|     top: "39%", | ||||
|     textAlign: "center", | ||||
|     textStyle: { | ||||
|       fontWeight: 600, | ||||
|       fontSize: 32, | ||||
|       color: "#fffd", | ||||
|     }, | ||||
|     subtext: "\u200224年累计产出\u2002", | ||||
|     subtext: `\u2002${subtitle}\u2002`, | ||||
|     subtextStyle: { | ||||
|       fontSize: 14, | ||||
|       fontWeight: 100, | ||||
| @@ -26,17 +32,17 @@ export default { | ||||
|     }, | ||||
|   }, | ||||
|   series: [ | ||||
|     // 背景 series - 2024计划 | ||||
|     // 背景 series | ||||
|     { | ||||
|       type: "pie", | ||||
|       name: "2024目标", | ||||
|       name: "当前目标", | ||||
|       radius: ["70%", "85%"], | ||||
|       center: ["50%", "52%"], | ||||
|       emptyCircleStyle: { | ||||
|         color: "#042c5f33", | ||||
|       }, | ||||
|     }, | ||||
|     // 数据 series - 2024累计 | ||||
|     // 数据 series | ||||
|     { | ||||
|       type: "pie", | ||||
|       radius: ["70%", "85%"], | ||||
| @@ -44,15 +50,14 @@ export default { | ||||
|       avoidLabelOvervlap: false, | ||||
|       label: { | ||||
|         show: false, | ||||
|         // position: "center", | ||||
|       }, | ||||
|       labelLine: { | ||||
|         show: false, | ||||
|       }, | ||||
|       data: [ | ||||
|         { | ||||
|           value: 90, | ||||
|           name: "2024累计产出", | ||||
|           value: currentSum, | ||||
|           name: "当前累计产出", | ||||
|           selected: false, | ||||
|           itemStyle: { | ||||
|             borderJoin: "round", | ||||
| @@ -73,8 +78,16 @@ export default { | ||||
|           }, | ||||
|         }, | ||||
|         { | ||||
|           value: 20, | ||||
|           name: "-", | ||||
|           value: | ||||
|             targetSum > currentSum | ||||
|               ? targetSum - currentSum | ||||
|               : targetSum == 0 | ||||
|               ? currentSum == 0 | ||||
|                 ? 1 | ||||
|                 : 0 | ||||
|               : 0, | ||||
|  | ||||
|           name: "未达成累计", | ||||
|           itemStyle: { color: "transparent" }, | ||||
|           label: { show: false }, | ||||
|         }, | ||||
| @@ -94,8 +107,8 @@ export default { | ||||
|       }, | ||||
|       data: [ | ||||
|         { | ||||
|           value: 90, | ||||
|           name: "2023累计产出", | ||||
|           value: previousSum, | ||||
|           name: "上期累计产出", | ||||
|           selected: false, | ||||
|           itemStyle: { | ||||
|             borderJoin: "round", | ||||
| @@ -116,7 +129,12 @@ export default { | ||||
|           }, | ||||
|         }, | ||||
|         { | ||||
|           value: 20, | ||||
|           value: | ||||
|             targetSum > previousSum | ||||
|               ? targetSum - previousSum | ||||
|               : previousSum == 0 | ||||
|               ? 1 | ||||
|               : 0, | ||||
|           name: "-", | ||||
|           itemStyle: { color: "transparent" }, | ||||
|           label: { show: false }, | ||||
| @@ -124,4 +142,4 @@ export default { | ||||
|       ], | ||||
|     }, | ||||
|   ], | ||||
| }; | ||||
| }); | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								src/views/copilot/components/charts/base/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/views/copilot/components/charts/base/icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binair bestand niet weergegeven.
										
									
								
							| Na Breedte: | Hoogte: | Grootte: 5.0 KiB | 
| @@ -33,9 +33,28 @@ export default { | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       currentActive: '', | ||||
|       currentActive: this.options[0], | ||||
|     }; | ||||
|   }, | ||||
|   watch: { | ||||
|     currentActive: { | ||||
|       handler(val) { | ||||
|         this.$emit( | ||||
|           "update:active", | ||||
|           [ | ||||
|             "瑞昌", | ||||
|             "邯郸", | ||||
|             "株洲", | ||||
|             "佳木斯", | ||||
|             "成都", | ||||
|             "凯盛光伏", | ||||
|             "蚌埠兴科", | ||||
|           ].indexOf(val) | ||||
|         ); | ||||
|       }, | ||||
|       immediate: true, | ||||
|     }, | ||||
|   }, | ||||
|   computed: {}, | ||||
|   methods: {}, | ||||
| }; | ||||
| @@ -55,7 +74,7 @@ button { | ||||
|   padding: 8px 12px; | ||||
|   cursor: pointer; | ||||
|   position: relative; | ||||
|   transition: all .3s; | ||||
|   transition: all 0.3s; | ||||
| } | ||||
|  | ||||
| button.active, | ||||
|   | ||||
| @@ -42,6 +42,14 @@ export default { | ||||
|       period: "日", | ||||
|     }; | ||||
|   }, | ||||
|   // mounted() { | ||||
|   //   document.body.style.minHeight = "1024px"; | ||||
|   //   document.body.style.minWidth = "1550px"; | ||||
|   // }, | ||||
|   // destroyed() { | ||||
|   //   document.body.style.minHeight = "1024px"; | ||||
|   //   document.body.style.minWidth = "1550px"; | ||||
|   // }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
|   | ||||
| @@ -9,22 +9,22 @@ | ||||
|   <div class="yield-copilot"> | ||||
|     <section class="top flex"> | ||||
|       <db-container class="std-yield" title="标准组件产出" icon="std"> | ||||
|         <std-output /> | ||||
|         <std-output :period="period" /> | ||||
|       </db-container> | ||||
|       <db-container class="chip-yield" title="芯片产出" icon="chip2"> | ||||
|         <chip-output /> | ||||
|         <chip-output :period="period" /> | ||||
|       </db-container> | ||||
|       <db-container class="bipv-yield" title="BIPV产出" icon="bipv"> | ||||
|         <bipv-output /> | ||||
|         <bipv-output :period="period" /> | ||||
|       </db-container> | ||||
|     </section> | ||||
|     <section class="bottom flex"> | ||||
|       <db-container class="fto-involve" title="FTO投入"></db-container> | ||||
|       <db-container | ||||
|         class="chip-involve" | ||||
|         title="芯片投入" | ||||
|         icon="chip" | ||||
|       ></db-container> | ||||
|       <db-container class="fto-involve" title="FTO投入"> | ||||
|         <fto-invest :period="period" /> | ||||
|       </db-container> | ||||
|       <db-container class="chip-involve" title="芯片投入" icon="chip"> | ||||
|         <chip-invest :period="period" /> | ||||
|       </db-container> | ||||
|     </section> | ||||
|   </div> | ||||
| </template> | ||||
| @@ -33,8 +33,9 @@ | ||||
| import Container from "../components/Container.vue"; | ||||
| import StdOutputVue from "../components/charts/StdOutput.vue"; | ||||
| import ChipOutputVue from "../components/charts/ChipOutput.vue"; | ||||
|  | ||||
| import FtoInvestVue from "../components/charts/FtoInvest.vue"; | ||||
| import BipvOutputVue from "../components/charts/BipvOutput.vue"; | ||||
| import ChipInvestVue from "../components/charts/ChipInvest.vue"; | ||||
|  | ||||
| export default { | ||||
|   name: "YieldCopilot", | ||||
| @@ -43,13 +44,32 @@ export default { | ||||
|     StdOutput: StdOutputVue, | ||||
|     ChipOutput: ChipOutputVue, | ||||
|     BipvOutput: BipvOutputVue, | ||||
|     FtoInvest: FtoInvestVue, | ||||
|     ChipInvest: ChipInvestVue, | ||||
|   }, | ||||
|   props: { | ||||
|     period: { | ||||
|       type: String, | ||||
|       default: "日", | ||||
|     }, | ||||
|   }, | ||||
|   props: {}, | ||||
|   data() { | ||||
|     return {}; | ||||
|   }, | ||||
|   computed: {}, | ||||
|   methods: {}, | ||||
|   watch: { | ||||
|     period: { | ||||
|       handler(val) { | ||||
|         val && this.fetchData(val); | ||||
|       }, | ||||
|       immediate: true, | ||||
|     }, | ||||
|   }, | ||||
|   methods: { | ||||
|     fetchData(period = "日") { | ||||
|       console.log(`产量驾驶舱,获取${period}数据`); | ||||
|       this.$store.dispatch("copilot/initCopilot", { period, source: "yield" }); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
|   | ||||
		Verwijs in nieuw issue
	
	Block a user