|
|
@@ -0,0 +1,668 @@
|
|
|
+<template>
|
|
|
+ <NList>
|
|
|
+ <template #header>
|
|
|
+ <div class="flex gap-2">
|
|
|
+ <NButton disabled :focusable="false" type="success" @click="downloadXlsx">
|
|
|
+ <template #icon><span class="i-ph:download" /></template>
|
|
|
+ 下载xlsx
|
|
|
+ </NButton>
|
|
|
+ <NButton :focusable="false" type="success" @click="downloadJson">
|
|
|
+ <template #icon><span class="i-ph:download" /></template>
|
|
|
+ 下载json
|
|
|
+ </NButton>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <NListItem
|
|
|
+ v-for="(strategy, name) in transformStrategyMap(props.data.strategyList)"
|
|
|
+ :key="name"
|
|
|
+ >
|
|
|
+ <NThing :title="name">
|
|
|
+ <NDataTable
|
|
|
+ :columns="createColumns()"
|
|
|
+ :data="createData(strategy.dayStrategy)"
|
|
|
+ :bordered="false"
|
|
|
+ :single-line="false"
|
|
|
+ />
|
|
|
+ </NThing>
|
|
|
+ </NListItem>
|
|
|
+ </NList>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { NList, NListItem, NDataTable, NThing, NTag, NButton, NTooltip } from 'naive-ui'
|
|
|
+import type { DataTableColumns } from 'naive-ui'
|
|
|
+import type { TemplateDetailInfo } from '@/service/despatchConfig/interface'
|
|
|
+import { saveAs } from 'file-saver'
|
|
|
+
|
|
|
+const props = defineProps<{
|
|
|
+ data: TemplateDetailInfo
|
|
|
+}>()
|
|
|
+
|
|
|
+type PeakType = 'super-off-peak' | 'off-peak' | 'mid-peak' | 'on-peak' | 'super-on-peak' | 'unknown'
|
|
|
+
|
|
|
+type DespatchType =
|
|
|
+ | 'A - Excess solar export to grid(只放不充)'
|
|
|
+ | 'B - aPower standby(待机)'
|
|
|
+ | 'C - Excess solar charge aPower(只充不放)'
|
|
|
+ | 'D - Self-consumption(自发自用)'
|
|
|
+ | 'E - aPower charge from solar(优先充电)'
|
|
|
+ | 'F - aPower supply home(优先供载)'
|
|
|
+ | 'G - aPower charge from grid(电网备电)'
|
|
|
+ | 'H - aPower export to the grid(满额馈⽹)'
|
|
|
+ | 'unknown'
|
|
|
+
|
|
|
+const DespatchTypeMap: Record<string, DespatchType> = {
|
|
|
+ A: 'A - Excess solar export to grid(只放不充)',
|
|
|
+ B: 'B - aPower standby(待机)',
|
|
|
+ C: 'C - Excess solar charge aPower(只充不放)',
|
|
|
+ D: 'D - Self-consumption(自发自用)',
|
|
|
+ E: 'E - aPower charge from solar(优先充电)',
|
|
|
+ F: 'F - aPower supply home(优先供载)',
|
|
|
+ G: 'G - aPower charge from grid(电网备电)',
|
|
|
+ H: 'H - aPower export to the grid(满额馈⽹)',
|
|
|
+}
|
|
|
+
|
|
|
+type TemplateStrategyList = TemplateDetailInfo['strategyList']
|
|
|
+type TemplateSubStrategy = TemplateStrategyList[number]['detailVoList'][number]
|
|
|
+
|
|
|
+interface StrategyMap {
|
|
|
+ [name: string]: StrategyData
|
|
|
+}
|
|
|
+
|
|
|
+interface StrategyData {
|
|
|
+ month: string // 生效月份
|
|
|
+ dayStrategy:
|
|
|
+ | {
|
|
|
+ type: 'everyday'
|
|
|
+ everyday: SubStrategyData[]
|
|
|
+ }
|
|
|
+ | {
|
|
|
+ type: 'workdays&holidays'
|
|
|
+ workdays: SubStrategyData[]
|
|
|
+ holidays: SubStrategyData[]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type DayStrategyType = 'everyday' | 'workdays&holidays'
|
|
|
+type DayStrategy = StrategyData['dayStrategy']
|
|
|
+
|
|
|
+interface SubStrategyData {
|
|
|
+ dayType: string
|
|
|
+ timeRange: string
|
|
|
+ peakType: PeakType
|
|
|
+ despatch: string[]
|
|
|
+ remark: string
|
|
|
+ data: TemplateSubStrategy
|
|
|
+ dayTypeRowSpan?: number // dayType的列合并数
|
|
|
+}
|
|
|
+
|
|
|
+const makeSubStrategyData = (data: TemplateSubStrategy, type: DayStrategyType): SubStrategyData => {
|
|
|
+ return {
|
|
|
+ dayType: type,
|
|
|
+ timeRange: `${data.startHourTime} - ${data.endHourTime}`,
|
|
|
+ peakType: ((): PeakType => {
|
|
|
+ switch (data.waveType) {
|
|
|
+ case 0:
|
|
|
+ return 'off-peak'
|
|
|
+ case 1:
|
|
|
+ return 'mid-peak'
|
|
|
+ case 2:
|
|
|
+ return 'on-peak'
|
|
|
+ case 3:
|
|
|
+ return 'super-on-peak'
|
|
|
+ case 4:
|
|
|
+ return 'super-off-peak'
|
|
|
+ default:
|
|
|
+ return 'unknown'
|
|
|
+ }
|
|
|
+ })(),
|
|
|
+ despatch: ((): string[] => {
|
|
|
+ if (data.solarPriority === '1,2,0' && data.loadPriority === '1,3,2') {
|
|
|
+ // A
|
|
|
+ return [DespatchTypeMap.A]
|
|
|
+ } else if (data.solarPriority === '1,2,0' && data.loadPriority === '1,2,0') {
|
|
|
+ // B
|
|
|
+ return [DespatchTypeMap.B]
|
|
|
+ } else if (data.solarPriority === '1,3,2' && data.loadPriority === '1,2,0') {
|
|
|
+ // C
|
|
|
+ return [DespatchTypeMap.C]
|
|
|
+ } else if (data.solarPriority === '1,3,2' && data.loadPriority === '1,3,2') {
|
|
|
+ // D
|
|
|
+ return [DespatchTypeMap.D]
|
|
|
+ } else if (data.solarPriority === '1,2,3' && data.loadPriority === '1,2,0') {
|
|
|
+ // E/G
|
|
|
+ return [DespatchTypeMap.E, DespatchTypeMap.G]
|
|
|
+ } else if (data.solarPriority === '1,2,0' && data.loadPriority === '1,2,3') {
|
|
|
+ // F/H
|
|
|
+ return [DespatchTypeMap.F, DespatchTypeMap.H]
|
|
|
+ } else if (data.dischargePower != null) {
|
|
|
+ return [`aPower discharge ${data.dischargePower}kw`]
|
|
|
+ } else if (data.chargePower != null) {
|
|
|
+ return [`aPower charge ${data.chargePower}kw`]
|
|
|
+ } else {
|
|
|
+ return ['unknown']
|
|
|
+ }
|
|
|
+ })(),
|
|
|
+ remark: '',
|
|
|
+ data,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const transformStrategyMap = (dataList: TemplateStrategyList): StrategyMap => {
|
|
|
+ const strategyMap: StrategyMap = {}
|
|
|
+ dataList.forEach((d) => {
|
|
|
+ if (strategyMap[d.seasonName] === undefined) {
|
|
|
+ strategyMap[d.seasonName] = {
|
|
|
+ month: d.month,
|
|
|
+ dayStrategy: (() => {
|
|
|
+ if (d.dayType === 1) {
|
|
|
+ // 工作日
|
|
|
+ return {
|
|
|
+ type: 'workdays&holidays',
|
|
|
+ workdays: d.detailVoList.map((item) =>
|
|
|
+ makeSubStrategyData(item, 'workdays&holidays'),
|
|
|
+ ),
|
|
|
+ holidays: [],
|
|
|
+ }
|
|
|
+ } else if (d.dayType === 2) {
|
|
|
+ // 节假日
|
|
|
+ return {
|
|
|
+ type: 'workdays&holidays',
|
|
|
+ workdays: [],
|
|
|
+ holidays: d.detailVoList.map((item) =>
|
|
|
+ makeSubStrategyData(item, 'workdays&holidays'),
|
|
|
+ ),
|
|
|
+ }
|
|
|
+ } else if (d.dayType === 3) {
|
|
|
+ // 每天
|
|
|
+ return {
|
|
|
+ type: 'everyday',
|
|
|
+ everyday: d.detailVoList.map((item) => makeSubStrategyData(item, 'everyday')),
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new Error(`未知dayType值(${d.dayType})`)
|
|
|
+ }
|
|
|
+ })(),
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (strategyMap[d.seasonName].month !== d.month) {
|
|
|
+ throw new Error(
|
|
|
+ `${d.seasonName}的month配置错误(${strategyMap[d.seasonName].month} != ${d.month})`,
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ const dayStrategy = strategyMap[d.seasonName].dayStrategy
|
|
|
+ if (d.dayType === 1) {
|
|
|
+ if (dayStrategy.type === 'workdays&holidays') {
|
|
|
+ dayStrategy.workdays = d.detailVoList.map((item) =>
|
|
|
+ makeSubStrategyData(item, 'workdays&holidays'),
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ throw new Error(
|
|
|
+ `${d.seasonName}的dayStrategy类型不匹配(dayType: ${d.dayType}, dayStrategyType: ${strategyMap[d.seasonName].dayStrategy.type})`,
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } else if (d.dayType === 2) {
|
|
|
+ if (dayStrategy.type === 'workdays&holidays') {
|
|
|
+ dayStrategy.holidays = d.detailVoList.map((item) =>
|
|
|
+ makeSubStrategyData(item, 'workdays&holidays'),
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ throw new Error(
|
|
|
+ `${d.seasonName}的dayStrategy类型不匹配(dayType: ${d.dayType}, dayStrategyType: ${strategyMap[d.seasonName].dayStrategy.type})`,
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } else if (d.dayType === 3) {
|
|
|
+ // 不应该出现两个everyday配置
|
|
|
+ throw new Error(`${d.seasonName}的dayStrategy-everyday类型数据冲突`)
|
|
|
+ } else {
|
|
|
+ throw new Error(`未知dayType值(${d.dayType})`)
|
|
|
+ }
|
|
|
+ strategyMap[d.seasonName].dayStrategy = dayStrategy
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return strategyMap
|
|
|
+}
|
|
|
+
|
|
|
+const createColumns = (): DataTableColumns<SubStrategyData> => {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ title: 'DayType',
|
|
|
+ key: 'dayType',
|
|
|
+ width: 100,
|
|
|
+ fixed: 'left',
|
|
|
+ rowSpan: (rowData, rowIndex) => rowData.dayTypeRowSpan ?? 1,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: 'TimeRang',
|
|
|
+ key: 'timeRange',
|
|
|
+ width: 120,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: 'PeakType',
|
|
|
+ key: 'peakType',
|
|
|
+ width: 150,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: 'DespatchType',
|
|
|
+ key: 'despatch',
|
|
|
+ width: 320,
|
|
|
+ render: (rowData, rowIndex) => {
|
|
|
+ return h(
|
|
|
+ 'div',
|
|
|
+ {
|
|
|
+ class: 'flex flex-col gap-2 items-start',
|
|
|
+ },
|
|
|
+ rowData.despatch.map((despatchTag) => {
|
|
|
+ return h(
|
|
|
+ NTag,
|
|
|
+ {
|
|
|
+ type: 'info',
|
|
|
+ bordered: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ default: () => despatchTag,
|
|
|
+ },
|
|
|
+ )
|
|
|
+ }),
|
|
|
+ )
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: 'Remark',
|
|
|
+ key: 'remark',
|
|
|
+ // width: 250,
|
|
|
+ ellipsis: {
|
|
|
+ tooltip: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: 'Data',
|
|
|
+ key: 'data',
|
|
|
+ fixed: 'right',
|
|
|
+ width: 60,
|
|
|
+ render(rowData, rowIndex) {
|
|
|
+ return h(
|
|
|
+ NTooltip,
|
|
|
+ { placement: 'left' },
|
|
|
+ {
|
|
|
+ trigger: () =>
|
|
|
+ h(
|
|
|
+ NButton,
|
|
|
+ {
|
|
|
+ quaternary: true,
|
|
|
+ circle: true,
|
|
|
+ size: 'small',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ icon: () =>
|
|
|
+ h('span', {
|
|
|
+ class: 'i-ph:info-duotone',
|
|
|
+ }),
|
|
|
+ },
|
|
|
+ ),
|
|
|
+
|
|
|
+ default: () =>
|
|
|
+ h(
|
|
|
+ 'pre',
|
|
|
+ {
|
|
|
+ class: 'text-xs whitespace-pre-wrap',
|
|
|
+ style: {
|
|
|
+ maxWidth: '400px',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ JSON.stringify(rowData.data, null, 2),
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ )
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ]
|
|
|
+}
|
|
|
+
|
|
|
+const createData = (data: DayStrategy): SubStrategyData[] => {
|
|
|
+ if (data.type === 'everyday') {
|
|
|
+ if (data.everyday.length > 0) {
|
|
|
+ data.everyday[0].dayTypeRowSpan = data.everyday.length
|
|
|
+ }
|
|
|
+ return data.everyday
|
|
|
+ } else if (data.type === 'workdays&holidays') {
|
|
|
+ if (data.workdays.length > 0) {
|
|
|
+ data.workdays[0].dayTypeRowSpan = data.workdays.length
|
|
|
+ }
|
|
|
+ if (data.holidays.length > 0) {
|
|
|
+ data.holidays[0].dayTypeRowSpan = data.holidays.length
|
|
|
+ }
|
|
|
+ return [...data.workdays, ...data.holidays]
|
|
|
+ } else {
|
|
|
+ throw new Error(`未知DayStrategy(${data})`)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const downloadXlsx = () => {}
|
|
|
+
|
|
|
+const downloadJson = () => {
|
|
|
+ const obj = createTemplateJson(props.data)
|
|
|
+ const jsonStr = JSON.stringify(
|
|
|
+ obj,
|
|
|
+ (_, value) => {
|
|
|
+ if (value === undefined || value === null) {
|
|
|
+ return undefined
|
|
|
+ }
|
|
|
+
|
|
|
+ return value
|
|
|
+ },
|
|
|
+ 2,
|
|
|
+ )
|
|
|
+
|
|
|
+ // 创建一个 Blob 对象,指定 MIME 类型为 'application/json'
|
|
|
+ const blob = new Blob([jsonStr], {
|
|
|
+ type: 'application/json',
|
|
|
+ })
|
|
|
+
|
|
|
+ let fileName = obj.base.utilityAbbreviation
|
|
|
+ if (obj.base.derSchedule !== '') {
|
|
|
+ fileName = `${fileName}_${obj.base.derSchedule}`
|
|
|
+ }
|
|
|
+ if (obj.base.scheduleName !== '') {
|
|
|
+ fileName = `${fileName}_${obj.base.scheduleName}`
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前年份、月份、日期、小时和分钟
|
|
|
+ const now = new Date()
|
|
|
+ const year = now.getFullYear()
|
|
|
+ const month = String(now.getMonth() + 1).padStart(2, '0') // 月份从0开始,需要加1并确保两位数
|
|
|
+ const day = String(now.getDate()).padStart(2, '0') // 确保日期为两位数
|
|
|
+ const hours = String(now.getHours()).padStart(2, '0') // 确保小时为两位数
|
|
|
+ const minutes = String(now.getMinutes()).padStart(2, '0') // 确保分钟为两位数
|
|
|
+ // 日期时间后缀
|
|
|
+ const timeSuffix = `${year}${month}${day}_${hours}${minutes}`
|
|
|
+
|
|
|
+ fileName = `${fileName}_${timeSuffix}.json`
|
|
|
+
|
|
|
+ saveAs(blob, fileName)
|
|
|
+}
|
|
|
+
|
|
|
+interface ScheduleConfig {
|
|
|
+ base: ScheduleBaseConfig
|
|
|
+ table: ScheduleTableConfig
|
|
|
+}
|
|
|
+
|
|
|
+interface ScheduleBaseConfig {
|
|
|
+ templateId?: number
|
|
|
+ country: number
|
|
|
+ area: number
|
|
|
+ derSchedule: string
|
|
|
+ utilityCompany: string
|
|
|
+ utilityAbbreviation: string
|
|
|
+ tariffSchedule: string
|
|
|
+ tariffType: number
|
|
|
+ scheduleName: string
|
|
|
+}
|
|
|
+
|
|
|
+interface ScheduleTableConfig {
|
|
|
+ tables: ScheduleTable[]
|
|
|
+}
|
|
|
+
|
|
|
+interface ScheduleTable {
|
|
|
+ key: number
|
|
|
+ subScheduleName: string
|
|
|
+ checkedMonth: string[]
|
|
|
+ dayType: string
|
|
|
+ timePeriodTable: TimePeriodData[] // 用于日期类型:每天、工作日
|
|
|
+ timePeriodTableHolidays: TimePeriodData[] // 用于日期类型:周末、节假日
|
|
|
+ tariffTable: TariffTable
|
|
|
+ tariffTableHolidays: TariffTable
|
|
|
+}
|
|
|
+
|
|
|
+interface TimePeriodData {
|
|
|
+ id: number
|
|
|
+ timeRange: {
|
|
|
+ startTime: string
|
|
|
+ endTime: string
|
|
|
+ }
|
|
|
+ rateType: number
|
|
|
+ rateName?: string
|
|
|
+ schedulingPolicy: PolicyName
|
|
|
+ customSchedule?: CustomSchedule
|
|
|
+ dischargeMinSOC?: number
|
|
|
+}
|
|
|
+
|
|
|
+interface CustomSchedule {
|
|
|
+ solarPriority?: string // 光伏优先级(数字大的优先级高,0为不执行) [0]:市电 [1]:负载 [2]:电池
|
|
|
+ loadPriority?: string // 负载优先级(数字大的优先级高,0为不执行) [0]:市电 [1]:光伏 [2]:电池
|
|
|
+
|
|
|
+ chargePower?: number // 单台电池充电功率,单位kw,精度0.1kw
|
|
|
+ dischargePower?: number // 单台电池放电功率,单位kw,精度0.1kw
|
|
|
+ chargeMax?: number // 单台电池最大充电功率,单位kw,精度0.1kw
|
|
|
+ dischargeMax?: number // 单台电池最大放电功率,单位kw,精度0.1kw
|
|
|
+
|
|
|
+ gridDischargeMax?: number //
|
|
|
+ gridChargeMax?: number //
|
|
|
+
|
|
|
+ gridFeedMax?: number //
|
|
|
+
|
|
|
+ solarCutoff?: number //
|
|
|
+ gridMax?: number //
|
|
|
+
|
|
|
+ maxChargeSoc?: number //
|
|
|
+ minDischargeSoc?: number //
|
|
|
+ heatEnable?: number //
|
|
|
+ powerOffApower?: string //
|
|
|
+ offGrid?: number // 主动离网,0:不控制(默认),1:断开,2:闭合
|
|
|
+ gcaoMax?: number //(预留SOC补电状态)电网充电使能,0:不允许,1:允许电网充电
|
|
|
+ rampTime?: number // 爬坡时间,0~300s
|
|
|
+}
|
|
|
+
|
|
|
+export interface TariffTable {
|
|
|
+ rateOffPeak?: number
|
|
|
+ rateMidPeak?: number
|
|
|
+ rateOnPeak?: number
|
|
|
+ rateSuperOnPeak?: number
|
|
|
+ rateSuperOffPeak?: number
|
|
|
+ sellOffPeak?: number
|
|
|
+ sellMidPeak?: number
|
|
|
+ sellOnPeak?: number
|
|
|
+ sellSuperOnPeak?: number
|
|
|
+ sellSuperOffPeak?: number
|
|
|
+}
|
|
|
+
|
|
|
+type MonthType =
|
|
|
+ | 'Jan'
|
|
|
+ | 'Feb'
|
|
|
+ | 'Mar'
|
|
|
+ | 'Apr'
|
|
|
+ | 'May'
|
|
|
+ | 'Jun'
|
|
|
+ | 'Jul'
|
|
|
+ | 'Aug'
|
|
|
+ | 'Sep'
|
|
|
+ | 'Oct'
|
|
|
+ | 'Nov'
|
|
|
+ | 'Dec'
|
|
|
+
|
|
|
+// 必须同配置编辑器的schedulingPolicyConfig定义相同
|
|
|
+type PolicyName =
|
|
|
+ | 'Self-Consumption' // 自发自用 = D
|
|
|
+ | 'PV surplus charging' // 光伏剩余充电 = C
|
|
|
+ | 'PV priority charging' // 光伏优先充电 = E
|
|
|
+ | 'Battery priority load' // 电池优先供载 = F
|
|
|
+ | 'Battery not dispatched' // 电池不参与调度 = B
|
|
|
+ | 'Battery forced discharge' // 电池强制放电
|
|
|
+ | 'Backup Only' // 仅备电
|
|
|
+
|
|
|
+const createTemplateJson = (info: TemplateDetailInfo): ScheduleConfig => {
|
|
|
+ return {
|
|
|
+ base: {
|
|
|
+ templateId: info.template.id,
|
|
|
+ country: info.template.countryId,
|
|
|
+ area: info.template.provinceId,
|
|
|
+ derSchedule: info.template.derSchdule ?? '',
|
|
|
+ utilityCompany: info.template.eleCompanyFullName,
|
|
|
+ utilityAbbreviation: info.template.electricCompany,
|
|
|
+ tariffSchedule: info.template.gridType,
|
|
|
+ tariffType: info.template.electricityType,
|
|
|
+ scheduleName: info.template.name,
|
|
|
+ },
|
|
|
+ table: {
|
|
|
+ tables: ((): ScheduleTable[] => {
|
|
|
+ const strategyMap = new Map<string, ScheduleTable>()
|
|
|
+
|
|
|
+ info.strategyList.forEach((strategy) => {
|
|
|
+ const key = `${strategy.seasonName} - ${strategy.month}`
|
|
|
+ const old = strategyMap.get(key)
|
|
|
+
|
|
|
+ const timePeriodList: TimePeriodData[] = strategy.detailVoList.map<TimePeriodData>(
|
|
|
+ (detail) => {
|
|
|
+ return {
|
|
|
+ id: Date.now(),
|
|
|
+ timeRange: {
|
|
|
+ startTime: detail.startHourTime,
|
|
|
+ endTime: detail.endHourTime,
|
|
|
+ },
|
|
|
+ rateType: detail.waveType,
|
|
|
+ rateName: detail.name,
|
|
|
+ schedulingPolicy: ((): PolicyName => {
|
|
|
+ if (detail.solarPriority === '1,2,0' && detail.loadPriority === '1,3,2') {
|
|
|
+ // A -
|
|
|
+ throw new Error(`调度A(${DespatchTypeMap.A})未实现`)
|
|
|
+ } else if (detail.solarPriority === '1,2,0' && detail.loadPriority === '1,2,0') {
|
|
|
+ // B - 电池不参与调度
|
|
|
+ return 'Battery not dispatched'
|
|
|
+ } else if (detail.solarPriority === '1,3,2' && detail.loadPriority === '1,2,0') {
|
|
|
+ // C - 光伏剩余充电
|
|
|
+ return 'PV surplus charging'
|
|
|
+ } else if (detail.solarPriority === '1,3,2' && detail.loadPriority === '1,3,2') {
|
|
|
+ // D - 自发自用
|
|
|
+ return 'Self-Consumption'
|
|
|
+ } else if (detail.solarPriority === '1,2,3' && detail.loadPriority === '1,2,0') {
|
|
|
+ // E/G - 光伏优先充电
|
|
|
+ return 'PV priority charging'
|
|
|
+ } else if (detail.solarPriority === '1,2,0' && detail.loadPriority === '1,2,3') {
|
|
|
+ // F/H - 电池优先供载
|
|
|
+ return 'Battery priority load'
|
|
|
+ } else if (detail.dischargePower != null) {
|
|
|
+ // 电池强制放电
|
|
|
+ return 'Battery forced discharge'
|
|
|
+ } else if (detail.chargePower != null) {
|
|
|
+ // 仅备电
|
|
|
+ return 'Backup Only'
|
|
|
+ } else {
|
|
|
+ throw new Error(`未知的调度逻辑\n${JSON.stringify(detail, null, 2)}`)
|
|
|
+ }
|
|
|
+ })(),
|
|
|
+ // customSchedule?: CustomSchedule, // 自定义功能暂不完善,未实现
|
|
|
+ dischargeMinSOC: detail.minDischargeSoc ?? undefined,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ )
|
|
|
+
|
|
|
+ const tariffTable: TariffTable = {
|
|
|
+ rateOffPeak: strategy.eleticRateValley ?? undefined, // 谷期电价
|
|
|
+ rateMidPeak: strategy.eleticRateShoulder ?? undefined, // 平期电价
|
|
|
+ rateOnPeak: strategy.eleticRatePeak ?? undefined, // 峰期电价
|
|
|
+ rateSuperOnPeak: strategy.eleticRateSharp ?? undefined, // 尖峰期电价
|
|
|
+ rateSuperOffPeak: strategy.eleticRateSuperOffPeak ?? undefined, // 超低谷期电价
|
|
|
+ sellOffPeak: strategy.eleticSellValley ?? undefined, // 谷期馈网电价
|
|
|
+ sellMidPeak: strategy.eleticSellShoulder ?? undefined, // 平期馈网电价
|
|
|
+ sellOnPeak: strategy.eleticSellPeak ?? undefined, // 峰期馈网电价
|
|
|
+ sellSuperOnPeak: strategy.eleticSellSharp ?? undefined, // 尖峰期馈网电价
|
|
|
+ sellSuperOffPeak: strategy.eleticSellSuperOffPeak ?? undefined, // 超低谷期馈网电价
|
|
|
+ }
|
|
|
+
|
|
|
+ if (old === undefined) {
|
|
|
+ strategyMap.set(key, {
|
|
|
+ key: Date.now(),
|
|
|
+ subScheduleName: strategy.seasonName,
|
|
|
+ checkedMonth: strategy.month.split(',').map<MonthType>((m) => {
|
|
|
+ switch (m.trim()) {
|
|
|
+ case '1':
|
|
|
+ return 'Jan'
|
|
|
+ case '2':
|
|
|
+ return 'Feb'
|
|
|
+ case '3':
|
|
|
+ return 'Mar'
|
|
|
+ case '4':
|
|
|
+ return 'Apr'
|
|
|
+ case '5':
|
|
|
+ return 'May'
|
|
|
+ case '6':
|
|
|
+ return 'Jun'
|
|
|
+ case '7':
|
|
|
+ return 'Jul'
|
|
|
+ case '8':
|
|
|
+ return 'Aug'
|
|
|
+ case '9':
|
|
|
+ return 'Sep'
|
|
|
+ case '10':
|
|
|
+ return 'Oct'
|
|
|
+ case '11':
|
|
|
+ return 'Nov'
|
|
|
+ case '12':
|
|
|
+ return 'Dec'
|
|
|
+ default:
|
|
|
+ throw new Error(`错误的month(${strategy.month})`)
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ dayType: (() => {
|
|
|
+ switch (strategy.dayType) {
|
|
|
+ case 1:
|
|
|
+ return 'WeekdaysAndWeekends' // Weekdays
|
|
|
+ case 2:
|
|
|
+ return 'WeekdaysAndWeekends' // Holidays
|
|
|
+ case 3:
|
|
|
+ return 'Everyday'
|
|
|
+ default:
|
|
|
+ throw new Error(`错误的dayType(${strategy.dayType})`)
|
|
|
+ }
|
|
|
+ })(),
|
|
|
+ timePeriodTable: ((): TimePeriodData[] => {
|
|
|
+ if (strategy.dayType === 1 || strategy.dayType === 3) {
|
|
|
+ return timePeriodList
|
|
|
+ }
|
|
|
+ return []
|
|
|
+ })(), // 用于日期类型:每天、工作日
|
|
|
+ timePeriodTableHolidays: ((): TimePeriodData[] => {
|
|
|
+ if (strategy.dayType === 2) {
|
|
|
+ return timePeriodList
|
|
|
+ }
|
|
|
+ return []
|
|
|
+ })(), // 用于日期类型:周末、节假日
|
|
|
+ tariffTable: ((): TariffTable => {
|
|
|
+ if (strategy.dayType === 1 || strategy.dayType === 3) {
|
|
|
+ return tariffTable
|
|
|
+ }
|
|
|
+ return {}
|
|
|
+ })(), // 用于日期类型:每天、工作日
|
|
|
+ tariffTableHolidays: ((): TariffTable => {
|
|
|
+ if (strategy.dayType === 2) {
|
|
|
+ return tariffTable
|
|
|
+ }
|
|
|
+ return {}
|
|
|
+ })(), // 用于日期类型:周末、节假日
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ if (strategy.dayType === 1) {
|
|
|
+ // 工作日
|
|
|
+ strategyMap.set(key, {
|
|
|
+ ...old,
|
|
|
+ timePeriodTable: timePeriodList,
|
|
|
+ tariffTable,
|
|
|
+ })
|
|
|
+ } else if (strategy.dayType === 2) {
|
|
|
+ // 节假日
|
|
|
+ strategyMap.set(key, {
|
|
|
+ ...old,
|
|
|
+ timePeriodTableHolidays: timePeriodList,
|
|
|
+ tariffTableHolidays: tariffTable,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ return [...strategyMap.values()]
|
|
|
+ })(),
|
|
|
+ },
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|