Files
wehub-resource-sync 9b395f5cc3
E2E Headed Chrome / e2e-headed (macos-15) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (ubuntu-latest) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (windows-latest) (push) Has been cancelled
CI / build (macos-latest) (push) Has been cancelled
CI / build (ubuntu-latest) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / bun-test (push) Has been cancelled
CI / adapter-test (push) Has been cancelled
CI / smoke-test (macos-latest) (push) Has been cancelled
CI / smoke-test (ubuntu-latest) (push) Has been cancelled
Security Audit / audit (push) Has been cancelled
Build Chrome Extension / build (push) Has been cancelled
Trigger Website Rebuild (Docs Updated) / dispatch (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:48 +08:00

213 lines
7.0 KiB
JavaScript

/**
* ONES filters/peek 响应解析(tasks / my-tasks 共用)
*/
import { CliError } from '@jackwener/opencli/errors';
/** ONES task 里 field_values 常为 [{ field_uuid, value }, ...] */
function pickTitleFromFieldValuesArray(fv) {
if (!Array.isArray(fv))
return '';
for (const item of fv) {
if (!item || typeof item !== 'object')
continue;
const row = item;
const fu = String(row.field_uuid ?? '');
if (!fu.startsWith('field'))
continue;
const v = row.value;
if (typeof v === 'string' && v.trim())
return v.trim();
if (Array.isArray(v) && v.length && typeof v[0] === 'string' && v[0].trim())
return v[0].trim();
}
return '';
}
export function pickTaskTitle(e) {
for (const k of ['summary', 'name', 'title', 'subject']) {
const v = e[k];
if (typeof v === 'string' && v.trim())
return v.trim();
}
const fromArr = pickTitleFromFieldValuesArray(e.field_values);
if (fromArr)
return fromArr;
const fv = e.field_values;
if (fv && typeof fv === 'object' && !Array.isArray(fv)) {
const o = fv;
for (const k of ['field001', 'field002', 'field003']) {
const v = o[k];
if (typeof v === 'string' && v.trim())
return v.trim();
}
}
return '';
}
/** 表格里标题别撑爆终端 */
export function ellipsizeCell(s, max = 64) {
const t = s.trim();
if (t.length <= max)
return t;
return `${t.slice(0, max - 1)}…`;
}
/** 辅助列:长 uuid 缩略,完整值见 -f json */
export function briefUuid(id, head = 6, tail = 4) {
if (!id)
return '';
if (id.length <= head + tail + 1)
return id;
return `${id.slice(0, head)}${id.slice(-tail)}`;
}
export function formatStamp(v) {
if (v == null || v === '')
return '';
const n = Number(v);
if (Number.isNaN(n))
return String(v);
const ms = n > 1e14 ? Math.floor(n / 1000) : n > 1e12 ? n : n * 1000;
try {
return new Date(ms).toISOString().replace('T', ' ').slice(0, 19);
}
catch {
return String(v);
}
}
export function flattenPeekGroups(parsed, limit) {
if (!Array.isArray(parsed.groups)) {
throw new CliError('FETCH_ERROR', 'Unexpected filters/peek response (missing groups)', 'Try -f json; check team UUID and API version.');
}
const groups = parsed.groups;
const rows = [];
for (const g of groups) {
const entries = Array.isArray(g.entries) ? g.entries : [];
for (const e of entries) {
rows.push(e);
if (rows.length >= limit)
break;
}
if (rows.length >= limit)
break;
}
return rows.slice(0, limit);
}
function fieldArrayFirstString(fv, fieldUuid) {
if (!Array.isArray(fv))
return '';
for (const item of fv) {
if (!item || typeof item !== 'object')
continue;
const row = item;
if (String(row.field_uuid ?? '') !== fieldUuid)
continue;
const v = row.value;
if (typeof v === 'string')
return v;
if (Array.isArray(v) && v[0] != null)
return String(v[0]);
}
return '';
}
function fvRecord(e) {
const fv = e.field_values;
return fv && typeof fv === 'object' && !Array.isArray(fv) ? fv : null;
}
/** 工作项状态 uuid(用于查 task_statuses 得中文名) */
export function getTaskStatusRawId(e) {
const fv = e.field_values;
const fvObj = fvRecord(e);
if (typeof e.status_uuid === 'string')
return e.status_uuid;
return fieldArrayFirstString(fv, 'field016') || (fvObj ? String(fvObj.field016 ?? '') : '');
}
/** 项目 uuid */
export function getTaskProjectRawId(e) {
const fv = e.field_values;
const fvObj = fvRecord(e);
if (typeof e.project_uuid === 'string')
return e.project_uuid;
return fieldArrayFirstString(fv, 'field006') || (fvObj ? String(fvObj.field006 ?? '') : '');
}
/**
* Project API 里 assess/total/remaining_manhour 多为**定点整数**(与 Web 上「小时」不一致);
* 常见换算:raw / 1e5 ≈ 小时。若你方实例不同,可设 `ONES_MANHOUR_SCALE`(默认 100000)。
*/
export function onesManhourScale() {
const raw = Number(process.env.ONES_MANHOUR_SCALE?.trim());
if (Number.isFinite(raw) && raw > 0)
return raw;
return 1e5;
}
/** 界面/h 小数 → API 内 manhour 整数(与列表「工时」列同一刻度) */
export function hoursToOnesManhourRaw(hours) {
if (!Number.isFinite(hours) || hours <= 0)
return 0;
return Math.max(1, Math.round(hours * onesManhourScale()));
}
function formatHoursShort(hours) {
if (!Number.isFinite(hours))
return '';
const snapped = Math.round(hours * 1e6) / 1e6;
const near = Math.round(snapped);
if (Math.abs(snapped - near) < 1e-5)
return `${near}h`;
const t = Math.round(snapped * 10) / 10;
return Number.isInteger(t) ? `${t}h` : `${t.toFixed(1)}h`;
}
function formatManhourSegment(label, v) {
if (v == null || v === '')
return null;
const n = Number(v);
if (!Number.isFinite(n))
return null;
const hours = n / onesManhourScale();
return `${label}${formatHoursShort(hours)}`;
}
export function formatTaskManhourSummary(e) {
const parts = [];
const a = formatManhourSegment('估', e.assess_manhour);
const t = formatManhourSegment('登', e.total_manhour);
const r = formatManhourSegment('余', e.remaining_manhour);
if (a)
parts.push(a);
if (t)
parts.push(t);
if (r)
parts.push(r);
return parts.length ? parts.join(' ') : '—';
}
export function mapTaskEntry(e, labels) {
const statusId = getTaskStatusRawId(e);
const projectId = getTaskProjectRawId(e);
const fullUuid = String(e.uuid ?? '');
const title = ellipsizeCell(pickTaskTitle(e));
const briefIfLong = (s) => (s.length > 14 ? briefUuid(s) : s);
const statusLabel = labels?.statusByUuid?.get(statusId) ?? briefIfLong(statusId);
const projectLabel = labels?.projectByUuid?.get(projectId) ?? briefIfLong(projectId);
return {
title,
status: ellipsizeCell(statusLabel, 20),
project: ellipsizeCell(projectLabel, 40),
uuid: fullUuid,
updated: formatStamp(e.server_update_stamp),
工时: ellipsizeCell(formatTaskManhourSummary(e), 36),
};
}
export function defaultPeekBody(query) {
return {
with_boards: false,
boards: null,
query,
group_by: '',
sort: [{ create_time: { order: 'desc' } }],
include_subtasks: false,
include_status_uuid: true,
include_issue_type: false,
include_project_uuid: true,
is_show_derive: false,
};
}
export function parsePeekLimit(value, fallback) {
const parsed = Number.parseInt(String(value ?? ''), 10);
if (!Number.isFinite(parsed) || parsed <= 0)
return fallback;
return Math.max(1, Math.min(500, parsed));
}