20 changed files with 1489 additions and 289 deletions
@ -0,0 +1,74 @@ |
|||
import { MapApi } from "@/api/shorepower/map"; |
|||
|
|||
export const getOperationTypeLabel = (value: string | number | undefined | null, options: { label: string, value: string | number }[]) => { |
|||
const item = options.find(opt => opt.value === value); |
|||
return item ? item.label : ''; |
|||
}; |
|||
|
|||
// 装货/卸货
|
|||
export const OPERATION_TYPE = [ |
|||
{ label: '装货', value: 1 }, |
|||
{ label: '卸货', value: 2 }, |
|||
{ label: '卸货并装货', value: 2 }, |
|||
] |
|||
|
|||
// 获取类型
|
|||
export const CARGO_CATEGORY = [ |
|||
{ label: '空船', value: 0 }, |
|||
{ label: '散货', value: 1 }, |
|||
{ label: '集装箱', value: 2 }, |
|||
{ label: '液体货物', value: 3 }, |
|||
{ label: '件杂货', value: 4 }, |
|||
{ label: '危险品', value: 5 }, |
|||
{ label: '冷藏货物', value: 6 }, |
|||
] |
|||
|
|||
// 未使用岸电原因
|
|||
export const UNUSED_SHORE_POWER_REASON = [ |
|||
{ label: '岸电用电接口不匹配', value: 1 }, |
|||
{ label: '岸电设施电压/频率不匹配', value: 2 }, |
|||
{ label: '电缆长度不匹配', value: 3 }, |
|||
{ label: '气象因素禁止作业', value: 4 }, |
|||
{ label: '船电设施损坏', value: 5 }, |
|||
{ label: '岸电设施维护中', value: 6 }, |
|||
{ label: '无受电设备', value: 7 }, |
|||
{ label: '拒绝使用岸电', value: 8 }, |
|||
{ label: '其他', value: 9 }, |
|||
] |
|||
|
|||
// 港区map
|
|||
export const HARBOR_DISTRICT = async () => { |
|||
const res = await MapApi.getHarborDistrictIdAndNameList() |
|||
return res.map(item => ({ |
|||
...item, |
|||
label: item.name, |
|||
value: item.id |
|||
})) |
|||
} |
|||
|
|||
// doc 码头map
|
|||
export const DOCK_DISTRICT = async () => { |
|||
const res = await MapApi.getDockIdAndNameList() |
|||
return res.map(item => ({ |
|||
...item, |
|||
label: item.name, |
|||
value: item.id |
|||
})) |
|||
} |
|||
|
|||
// 岸电状态
|
|||
export const SHORE_POWER_STATUS = [ |
|||
{ label: '待靠泊', value: 1 }, |
|||
{ label: '靠泊中', value: 2 }, |
|||
{ label: '岸电接入中', value: 3 }, |
|||
{ label: '用电中', value: 4 }, |
|||
{ label: '岸电卸载中', value: 5 }, |
|||
{ label: '岸电卸载完成', value: 6 }, |
|||
{ label: '离泊', value: 7 }, |
|||
{ label: '未使用岸电', value: 9 } |
|||
] |
|||
|
|||
export const BERTH_TYPE = [ |
|||
{ label: '左舷停舶', value: 'left' }, |
|||
{ label: '右舷停舶', value: 'right' }, |
|||
] |
|||
@ -0,0 +1,194 @@ |
|||
import { RealtimeDeviceData, ShipRespVo } from "@/types/shorepower"; |
|||
import dayjs from "dayjs"; |
|||
import { MapApi } from "@/api/shorepower/map"; |
|||
/** |
|||
* 将毫秒时间戳格式化为 'YYYY/MM/DD HH:mm:ss' 格式 |
|||
* @param timestamp 毫秒时间戳 |
|||
* @returns 格式化后的时间字符串 |
|||
*/ |
|||
export const formatTimestamp = ( |
|||
timestamp: number | null | undefined, |
|||
format = 'YYYY/MM/DD HH:mm:ss' |
|||
): string => { |
|||
if (!timestamp) return ''; |
|||
// 处理秒级时间戳(10位数字)
|
|||
if (timestamp < 1e12) { |
|||
timestamp *= 1000; |
|||
} |
|||
return dayjs(timestamp).format(format); |
|||
}; |
|||
|
|||
/** |
|||
* 将两个时间戳之间的差值格式化为 "X小时Y分钟Z秒" |
|||
* @param {number} startTime - 开始时间戳(毫秒) |
|||
* @param {number} endTime - 结束时间戳(毫秒) |
|||
* @returns {string} 格式化后的时间字符串,如 "2小时30分钟15秒" |
|||
*/ |
|||
export function formatDuration(startTime?: number, endTime?: number): string { |
|||
if (!startTime || !endTime) return '--'; |
|||
const diffMs = Math.max(0, endTime - startTime); |
|||
const totalSeconds = Math.floor(diffMs / 1000); |
|||
|
|||
const hours = Math.floor(totalSeconds / 3600); |
|||
const minutes = Math.floor((totalSeconds % 3600) / 60); |
|||
const seconds = totalSeconds % 60; |
|||
|
|||
let result = ''; |
|||
if (hours > 0) result += `${hours}小时`; |
|||
if (minutes > 0) result += `${minutes}分钟`; |
|||
if (seconds > 0 || result === '') result += `${seconds}秒`; |
|||
|
|||
return result || '0秒'; |
|||
} |
|||
|
|||
// 岸电使用文本构建
|
|||
export function showStatus(ship: ShipRespVo, realtimeDeviceData?: RealtimeDeviceData[]): { statusClass: string, status: string } | null { |
|||
const { usageRecordInfo, applyInfo } = ship; |
|||
if (!applyInfo || !usageRecordInfo || !usageRecordInfo.beginTime) { |
|||
return null; |
|||
} |
|||
if (applyInfo.reason == 0 && usageRecordInfo && usageRecordInfo.beginTime) { |
|||
|
|||
const start = new Date(usageRecordInfo.beginTime); |
|||
const end = usageRecordInfo.endTime ? new Date(usageRecordInfo.endTime) : new Date(); |
|||
|
|||
// 校验日期有效性
|
|||
if (isNaN(start.getTime()) || isNaN(end.getTime())) { |
|||
return null; |
|||
} |
|||
|
|||
// 校验时间顺序
|
|||
if (end < start) { |
|||
return null; // 或抛出错误、记录日志等
|
|||
} |
|||
|
|||
// 计算总毫秒差
|
|||
const diffMs = end.getTime() - start.getTime(); |
|||
|
|||
// 转换为秒
|
|||
const totalSeconds = Math.floor(diffMs / 1000); |
|||
|
|||
const hours = Math.floor(totalSeconds / 3600); |
|||
const minutes = Math.floor((totalSeconds % 3600) / 60); |
|||
const seconds = totalSeconds % 60; |
|||
|
|||
let useValue = 0; |
|||
|
|||
// 检查是否有结束读数
|
|||
const hasEndReading = usageRecordInfo.powerEndManualReading || usageRecordInfo.powerEndSystemReading; |
|||
|
|||
if (hasEndReading) { |
|||
// 有结束读数,使用常规计算方式
|
|||
// 优先计算人工差值
|
|||
const manualDiff = usageRecordInfo.powerStartManualReading !== undefined && usageRecordInfo.powerEndManualReading !== undefined |
|||
? usageRecordInfo.powerEndManualReading - usageRecordInfo.powerStartManualReading |
|||
: null; |
|||
// 然后计算系统差值
|
|||
const systemDiff = usageRecordInfo.powerStartSystemReading !== undefined && usageRecordInfo.powerEndSystemReading !== undefined |
|||
? usageRecordInfo.powerEndSystemReading - usageRecordInfo.powerStartSystemReading |
|||
: null; |
|||
// 使用人工差值优先,然后系统差值,最后默认0
|
|||
useValue = manualDiff ?? systemDiff ?? 0; |
|||
} else { |
|||
// 没有结束读数,使用实时数据计算
|
|||
const deviceId = ship.shorePower?.totalPowerDeviceId; |
|||
if (deviceId) { |
|||
try { |
|||
// 调用API获取实时数据
|
|||
const measureValueStr = getValueById(realtimeDeviceData || [], deviceId, |
|||
'measureValue') |
|||
if (measureValueStr !== undefined) { |
|||
|
|||
// 优先使用人工开始读数,然后系统开始读数
|
|||
const startReading = usageRecordInfo.powerStartManualReading ?? usageRecordInfo.powerStartSystemReading ?? 0; |
|||
const measureValue = typeof measureValueStr === 'string' ? parseFloat(measureValueStr) : measureValueStr; |
|||
useValue = measureValue - startReading; |
|||
} |
|||
} catch (error) { |
|||
console.error('获取实时数据失败:', error); |
|||
useValue = 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return { |
|||
statusClass: 'status-using', |
|||
status: `使用岸电${hours}小时${minutes}分钟${seconds}秒,${useValue.toFixed(2)}kWH` |
|||
} |
|||
} else if (applyInfo.reason != 0) { |
|||
// 状态码映射表 - 包含状态文本和颜色类型
|
|||
const statusMap: Record<string, { text: string; colorType: string }> = { |
|||
'-1': { text: '未知错误', colorType: 'default' }, |
|||
'1': { text: '岸电用电接口不匹配', colorType: 'default' }, |
|||
'2': { text: '岸电设施电压/频率不匹配', colorType: 'success' }, |
|||
'3': { text: '电缆长度不匹配', colorType: 'success' }, |
|||
'4': { text: '气象因素禁止作业', colorType: 'success' }, |
|||
'5': { text: '船电设施损坏', colorType: 'warning' }, |
|||
'6': { text: '岸电设施维护中', colorType: 'warning' }, |
|||
'7': { text: '无受电设备', colorType: 'danger' }, |
|||
'8': { text: '拒绝使用岸电', colorType: 'danger' }, |
|||
'9': { text: '其他', colorType: 'danger' } |
|||
} |
|||
|
|||
const reasonKey = applyInfo.reason?.toString() || '-1' |
|||
const statusInfo = statusMap[reasonKey] || { text: applyInfo.reason, colorType: 'default' } |
|||
|
|||
// 根据颜色类型设置对应的状态类
|
|||
let statusClass = 'status-cable' |
|||
switch (statusInfo.colorType) { |
|||
case 'danger': |
|||
statusClass = 'status-danger' |
|||
break |
|||
case 'warning': |
|||
statusClass = 'status-warning' |
|||
break |
|||
case 'success': |
|||
statusClass = 'status-success' |
|||
break |
|||
default: |
|||
statusClass = 'status-cable' |
|||
} |
|||
|
|||
return { |
|||
statusClass: statusClass, |
|||
status: statusInfo.text |
|||
} |
|||
} else { |
|||
return { |
|||
statusClass: '', |
|||
status: 'unknown' |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 根据 id 查找对象数组中的指定字段值,并保留两位小数 |
|||
* @param list - 对象数组,每个对象必须包含 id 字段 |
|||
* @param id - 要查找的 id 值 |
|||
* @param valueField - 要返回的字段名(必须是对象中除 id 外的 key) |
|||
* @returns 字段值,未找到则返回 undefined |
|||
*/ |
|||
export function getValueById<T extends { id: number | string }, K extends keyof Omit<T, 'id'>>( |
|||
list: T[], |
|||
id: T['id'], |
|||
valueField: K |
|||
): T[K] extends number | string ? string | undefined : T[K] | undefined { |
|||
const item = list.find(item => item.id === id); |
|||
|
|||
if (!item) return undefined; |
|||
|
|||
const value = item[valueField]; |
|||
|
|||
// 如果值是数字或可以转换为数字的字符串,则保留两位小数
|
|||
if (typeof value === 'number') { |
|||
return Number(value).toFixed(2) as any; |
|||
} |
|||
|
|||
if (typeof value === 'string' && !isNaN(Number(value))) { |
|||
return Number(value).toFixed(2) as any; |
|||
} |
|||
|
|||
// 否则返回原始值
|
|||
return value as any; |
|||
} |
|||
@ -0,0 +1,474 @@ |
|||
interface ShorePowerBerth { |
|||
id: number |
|||
berthId: number |
|||
equipmentId: number |
|||
name: string |
|||
totalPowerDeviceId: number |
|||
frequencyDeviceId: number |
|||
voltageDeviceId: number |
|||
currentDeviceId: number |
|||
status: number |
|||
createTime: number // Unix timestamp in milliseconds
|
|||
shorePowerEquipmentInfo: ShorePowerEquipmentInfo |
|||
} |
|||
|
|||
interface ShorePowerEquipmentInfo { |
|||
id: number |
|||
dockId: number |
|||
name: string |
|||
capacity: string // 可考虑转为 number,但原始数据为字符串 "2000"
|
|||
voltage: string // 如 "0.4/0.44"
|
|||
frequency: string // 如 "50/60"
|
|||
createTime: number // Unix timestamp in milliseconds
|
|||
} |
|||
|
|||
interface shipHistoryPageRequest { |
|||
/** |
|||
* 查询编号 |
|||
*/ |
|||
ids?: number[] |
|||
/** |
|||
* 页码,从 1 开始 |
|||
*/ |
|||
pageNo: number |
|||
/** |
|||
* 每页条数,最大值为 100 |
|||
*/ |
|||
pageSize: number |
|||
/** |
|||
* 船舶编号 |
|||
*/ |
|||
shipId?: number |
|||
/** |
|||
* 查询类型 |
|||
*/ |
|||
type?: number |
|||
[property: string]: any |
|||
} |
|||
|
|||
/** |
|||
* CommonResultPageResultShipRespVo |
|||
*/ |
|||
interface apiResponse<T> { |
|||
code?: number |
|||
data?: T |
|||
msg?: string |
|||
[property: string]: any |
|||
} |
|||
|
|||
/** |
|||
* PageResultShipRespVo,分页结果 |
|||
*/ |
|||
interface PageResultShipRespVo<T> { |
|||
/** |
|||
* 数据 |
|||
*/ |
|||
list: T[] |
|||
/** |
|||
* 总量 |
|||
*/ |
|||
total: number |
|||
[property: string]: any |
|||
} |
|||
|
|||
/** |
|||
* ShipRespVo,管理后台 - 船舶岸电使用情况 Response VO |
|||
*/ |
|||
export interface ShipRespVo { |
|||
/** |
|||
* 用电申请信息 |
|||
*/ |
|||
applyInfo: ApplyRespVO |
|||
/** |
|||
* 船舶基础信息 |
|||
*/ |
|||
shipBasicInfo: ShipBasicInfoRespVO |
|||
/** |
|||
* 用电接口信息 |
|||
*/ |
|||
shorePower: ShorePowerRespVO |
|||
/** |
|||
* 岸电接口与船舶关系 |
|||
*/ |
|||
shorePowerAndShip: ShorePowerAndShipRespVO |
|||
/** |
|||
* 岸电设施信息 |
|||
*/ |
|||
shorePowerEquipment: ShorePowerEquipmentRespVO |
|||
/** |
|||
* 船舶用电信息 |
|||
*/ |
|||
usageRecordInfo: UsageRecordRespVO |
|||
[property: string]: any |
|||
} |
|||
|
|||
/** |
|||
* 用电申请信息 |
|||
* |
|||
* ApplyRespVO,管理后台 - 用电申请 Response VO |
|||
*/ |
|||
export interface ApplyRespVO { |
|||
/** |
|||
* 船方代理单位 |
|||
*/ |
|||
agentCompany: string |
|||
/** |
|||
* 代理人联系方式 |
|||
*/ |
|||
agentContact: string |
|||
/** |
|||
* 是否申请使用岸电 |
|||
*/ |
|||
applyShorePower: boolean |
|||
/** |
|||
* 到达泊位 |
|||
*/ |
|||
arrivalBerth: number |
|||
/** |
|||
* 到达码头 |
|||
*/ |
|||
arrivalDock: number |
|||
/** |
|||
* 到达港 |
|||
*/ |
|||
arrivalHarborDistrict: number |
|||
/** |
|||
* 创建时间 |
|||
*/ |
|||
createTime: Date |
|||
/** |
|||
* 起运港 |
|||
*/ |
|||
departureHarborDistrict: string |
|||
/** |
|||
* 编号 |
|||
*/ |
|||
id: number |
|||
/** |
|||
* 货物类型(装货) |
|||
*/ |
|||
loadingCargoCategory: number |
|||
/** |
|||
* 货物名称(装货) |
|||
*/ |
|||
loadingCargoName: string |
|||
/** |
|||
* 货物吨数(装货) |
|||
*/ |
|||
loadingCargoTonnage: number |
|||
/** |
|||
* 装货/卸货 |
|||
*/ |
|||
operationType: number |
|||
/** |
|||
* 计划靠泊时间 |
|||
*/ |
|||
plannedBerthTime: Date |
|||
/** |
|||
* 计划离泊时间 |
|||
*/ |
|||
plannedDepartureTime: Date |
|||
/** |
|||
* 未使用岸电原因 |
|||
*/ |
|||
reason?: number |
|||
/** |
|||
* 备注 |
|||
*/ |
|||
remark?: string |
|||
/** |
|||
* 船舶编号 |
|||
*/ |
|||
shipId: number |
|||
/** |
|||
* 申请状态 |
|||
*/ |
|||
status: number |
|||
/** |
|||
* 内贸/外贸 |
|||
*/ |
|||
tradeType: number |
|||
/** |
|||
* 货物类型(卸货) |
|||
*/ |
|||
unloadingCargoCategory: number |
|||
/** |
|||
* 货物名称(卸货) |
|||
*/ |
|||
unloadingCargoName: string |
|||
/** |
|||
* 货物吨数(卸货) |
|||
*/ |
|||
unloadingCargoTonnage: number |
|||
/** |
|||
* 航次 |
|||
*/ |
|||
voyage: string |
|||
[property: string]: any |
|||
} |
|||
|
|||
/** |
|||
* 船舶基础信息 |
|||
* |
|||
* ShipBasicInfoRespVO,管理后台 - 船舶基础信息 Response VO |
|||
*/ |
|||
export interface ShipBasicInfoRespVO { |
|||
/** |
|||
* 船舶呼号 |
|||
*/ |
|||
callSign: string |
|||
/** |
|||
* 创建时间 |
|||
*/ |
|||
createTime: Date |
|||
/** |
|||
* 满载吃水深度 |
|||
*/ |
|||
fullLoadDraft: number |
|||
/** |
|||
* 编号 |
|||
*/ |
|||
id: number |
|||
/** |
|||
* 船检登记号 |
|||
*/ |
|||
inspectionNo: string |
|||
/** |
|||
* 船舶长度 |
|||
*/ |
|||
length: number |
|||
/** |
|||
* 船名 |
|||
*/ |
|||
name: string |
|||
/** |
|||
* 英文船名 |
|||
*/ |
|||
nameEn: string |
|||
/** |
|||
* 航运单位名称 |
|||
*/ |
|||
shippingCompany: string |
|||
/** |
|||
* 岸电负责人 |
|||
*/ |
|||
shorePowerContact: string |
|||
/** |
|||
* 联系方式 |
|||
*/ |
|||
shorePowerContactPhone: string |
|||
/** |
|||
* 船舶吨位 |
|||
*/ |
|||
tonnage: number |
|||
/** |
|||
* 船舶宽度 |
|||
*/ |
|||
width: number |
|||
[property: string]: any |
|||
} |
|||
|
|||
/** |
|||
* 用电接口信息 |
|||
* |
|||
* ShorePowerRespVO,管理后台 - 岸电 Response VO |
|||
*/ |
|||
export interface ShorePowerRespVO { |
|||
/** |
|||
* 泊位编号 |
|||
*/ |
|||
berthId: number |
|||
/** |
|||
* 创建时间 |
|||
*/ |
|||
createTime: Date |
|||
/** |
|||
* 岸电设备编号 |
|||
*/ |
|||
equipmentId: number |
|||
/** |
|||
* 频率设备编号 |
|||
*/ |
|||
frequencyDeviceId: number |
|||
/** |
|||
* 编号 |
|||
*/ |
|||
id: number |
|||
/** |
|||
* 名称 |
|||
*/ |
|||
name: string |
|||
/** |
|||
* 总电量设备编号 |
|||
*/ |
|||
totalPowerDeviceId: number |
|||
/** |
|||
* 电压设备编号 |
|||
*/ |
|||
voltageDeviceId: number |
|||
[property: string]: any |
|||
} |
|||
|
|||
/** |
|||
* 岸电接口与船舶关系 |
|||
* |
|||
* ShorePowerAndShipRespVO,管理后台 - 岸电接口与船舶关系 Response VO |
|||
*/ |
|||
export interface ShorePowerAndShipRespVO { |
|||
/** |
|||
* 申请单编号 |
|||
*/ |
|||
applyId: number |
|||
/** |
|||
* 创建时间 |
|||
*/ |
|||
createTime: Date |
|||
/** |
|||
* 编号 |
|||
*/ |
|||
id: number |
|||
/** |
|||
* 船舶编号 |
|||
*/ |
|||
shipId: number |
|||
/** |
|||
* 岸电接口编号 |
|||
*/ |
|||
shorePowerId: number |
|||
/** |
|||
* 靠泊状态 |
|||
*/ |
|||
status: number |
|||
/** |
|||
* 靠泊类型 |
|||
*/ |
|||
type: string |
|||
[property: string]: any |
|||
} |
|||
|
|||
/** |
|||
* 岸电设施信息 |
|||
* |
|||
* ShorePowerEquipmentRespVO,管理后台 - 岸电设施 Response VO |
|||
*/ |
|||
export interface ShorePowerEquipmentRespVO { |
|||
/** |
|||
* 装机容量 |
|||
*/ |
|||
capacity: string |
|||
/** |
|||
* 创建时间 |
|||
*/ |
|||
createTime: Date |
|||
/** |
|||
* 码头编号 |
|||
*/ |
|||
dockId: number |
|||
/** |
|||
* 频率 |
|||
*/ |
|||
frequency: string |
|||
/** |
|||
* 编号 |
|||
*/ |
|||
id: number |
|||
/** |
|||
* 名称 |
|||
*/ |
|||
name: string |
|||
/** |
|||
* 电压 |
|||
*/ |
|||
voltage: string |
|||
[property: string]: any |
|||
} |
|||
|
|||
/** |
|||
* 船舶用电信息 |
|||
* |
|||
* UsageRecordRespVO,管理后台 - 用电记录 Response VO |
|||
*/ |
|||
export interface UsageRecordRespVO { |
|||
/** |
|||
* 实际靠泊时间 |
|||
*/ |
|||
actualBerthTime?: Date |
|||
/** |
|||
* 实际离泊时间 |
|||
*/ |
|||
actualDepartureTime?: number | null | undefined |
|||
/** |
|||
* 用电申请编号 |
|||
*/ |
|||
applyId: number |
|||
/** |
|||
* 用电开始时供电方操作人 |
|||
*/ |
|||
beginPowerSupplyOperator?: string |
|||
/** |
|||
* 用电开始时用电方操作人 |
|||
*/ |
|||
beginPowerUsageOperator?: string |
|||
/** |
|||
* 用电开始时间 |
|||
*/ |
|||
beginTime?: number |
|||
/** |
|||
* 创建时间 |
|||
*/ |
|||
createTime: Date |
|||
/** |
|||
* 用电结束时间 |
|||
*/ |
|||
endTime?: number |
|||
/** |
|||
* 编号 |
|||
*/ |
|||
id: number |
|||
/** |
|||
* 用电结束时供电方操作人 |
|||
*/ |
|||
overPowerSupplyOperator?: string |
|||
/** |
|||
* 用电结束时用电方操作人 |
|||
*/ |
|||
overPowerUsageOperator?: string |
|||
/** |
|||
* 用电结束时间人工读数 |
|||
*/ |
|||
powerEndManualReading?: number |
|||
/** |
|||
* 用电结束时间系统读数 |
|||
*/ |
|||
powerEndSystemReading?: number |
|||
/** |
|||
* 用电开始时间人工读数 |
|||
*/ |
|||
powerStartManualReading?: number |
|||
/** |
|||
* 用电开始时间系统读数 |
|||
*/ |
|||
powerStartSystemReading?: number |
|||
/** |
|||
* 船舶编号 |
|||
*/ |
|||
shipId: number |
|||
/** |
|||
* 作业单状态 |
|||
*/ |
|||
status: number |
|||
[property: string]: any |
|||
} |
|||
|
|||
interface RealtimeDeviceData { |
|||
createTime: number // 时间戳(毫秒)
|
|||
deviceCode: string |
|||
deviceId: number |
|||
deviceName: string |
|||
deviceStatus: number |
|||
id: number |
|||
incrementCost: number |
|||
incrementValue: number |
|||
measureTime: number // 时间戳(毫秒)
|
|||
measureValue: number |
|||
} |
|||
Loading…
Reference in new issue