CI / backend (push) Canceled after 0s
问题:侧栏贴左边,内容区却用 mx-auto max-w-6xl 居中,窗口一宽就出现 "侧栏到内容之间一大片空白、内容右侧又一大片空白"的割裂感。2K 屏上尤其明显。 做法(用户给的两个方向里选"侧栏贴边 + 内容自适应"): - AppShell 内容区去掉居中与固定宽度,改为随窗口铺开,断点加宽内边距 (lg 32 / xl 48 / 2xl 64),侧栏在 xl 从 240 加宽到 264 - 宽了之后不能只是把元素拉长,所以同步改成"用宽度"而不是"被拉长": - 任务行:md 以上标题左、状态/优先级/截止/标签右对齐成一行,窄屏仍折两行 - 知识、工单列表:md 两列 / 2xl 三列,卡片不再横跨整屏 - 看板本来就是四等分列,自适应后自然铺满 - PageHeader 的描述文字限 max-w-3xl,宽屏下不出现过长的行宽 e2e/shots.mjs 支持 VIEWPORT=2560x1400 SUFFIX=-wide 覆盖视口,用来核对宽屏。 三个 e2e 复跑全绿;tsc 0 错,构建 242.02 kB / gzip 76.78 kB。
93 lines
3.9 KiB
JavaScript
93 lines
3.9 KiB
JavaScript
import { chromium } from "playwright-core";
|
|
|
|
const API = "http://127.0.0.1:8100/api/v1";
|
|
const WEB = "http://127.0.0.1:5180";
|
|
const phone = "1370000" + String(Date.now()).slice(-4);
|
|
|
|
async function call(path, body, token, method = "POST") {
|
|
const res = await fetch(API + path, {
|
|
method,
|
|
headers: {
|
|
"content-type": "application/json",
|
|
...(token ? { authorization: "Bearer " + token } : {}),
|
|
},
|
|
body: body ? JSON.stringify(body) : undefined,
|
|
});
|
|
const json = await res.json();
|
|
if (json.code !== 0) throw new Error(path + " -> " + JSON.stringify(json).slice(0, 200));
|
|
return json.data;
|
|
}
|
|
|
|
const sent = await call("/auth/sms/send", { phone });
|
|
const login = await call("/auth/login", { phone, code: sent.dev_code ?? "123456" });
|
|
const token = login.tokens.access_token;
|
|
const ws = await call("/workspaces", { name: "海图工作室", slug: "haitu-" + Date.now().toString(36) }, token);
|
|
const wsId = ws.id;
|
|
|
|
const tasks = [
|
|
["整理发票原件并寄给财务", "todo", 2, "#报销 财务"],
|
|
["给客户出第三季度复盘报告", "doing", 3, "#客户 周报"],
|
|
["把采集端的日志级别改回 info", "doing", 1, ""],
|
|
["和供应商确认二次报价", "blocked", 2, "#采购"],
|
|
["入职物料清单更新", "done", 0, ""],
|
|
["评审新版落地页文案", "inbox", 1, "#设计"],
|
|
];
|
|
for (const [title, status, priority, tags] of tasks) {
|
|
const item = await call(`/workspaces/${wsId}/items`, { title, status, priority }, token);
|
|
if (tags) {
|
|
const names = tags.split(" ").filter((t) => t.startsWith("#")).map((t) => t.slice(1));
|
|
if (names.length) await call(`/workspaces/${wsId}/items/${item.id}`, { version: item.version, tag_names: names }, token, "PATCH");
|
|
}
|
|
}
|
|
|
|
const shop = await call(`/workspaces/${wsId}/items`, { title: "官网改版:首页轮播要换成新版素材", status: "todo", priority: 2 }, token);
|
|
await call(`/workspaces/${wsId}/items/${shop.id}/knowledge?doc_type=howto`, {}, token);
|
|
await call(`/workspaces/${wsId}/knowledge`, { title: "报销单必须贴发票原件,打印件不算", doc_type: "note" }, token);
|
|
|
|
const me = await call("/workspaces/" + wsId + "/members", null, token, "GET").catch(() => null);
|
|
|
|
// 视口可覆盖,用来核对宽屏布局:VIEWPORT=2560x1400 SUFFIX=-wide node e2e/shots.mjs
|
|
const [VW, VH] = (process.env.VIEWPORT ?? "1440x960").split("x").map(Number);
|
|
const SUFFIX = process.env.SUFFIX ?? "";
|
|
|
|
const browser = await chromium.launch({
|
|
executablePath: "C:\\Users\\Steven\\AppData\\Local\\ms-playwright\\chromium-1223\\chrome-win64\\chrome.exe",
|
|
});
|
|
const page = await browser.newPage({ viewport: { width: VW, height: VH }, deviceScaleFactor: 1 });
|
|
await page.goto(WEB + "/login");
|
|
await page.evaluate(([t, r]) => {
|
|
localStorage.setItem("dadao.access", t);
|
|
localStorage.setItem("dadao.refresh", r);
|
|
}, [login.tokens.access_token, login.tokens.refresh_token]);
|
|
|
|
const shots = [];
|
|
async function shot(url, name, prep) {
|
|
await page.goto(WEB + url, { waitUntil: "networkidle" });
|
|
if (prep) await prep();
|
|
await page.waitForTimeout(600);
|
|
const file = `e2e/artifacts/ui-${name}${SUFFIX}.png`;
|
|
await page.screenshot({ path: file, fullPage: false });
|
|
shots.push(file);
|
|
}
|
|
|
|
await shot("/", "home");
|
|
await shot(`/w/${wsId}`, "tasks");
|
|
await shot(`/w/${wsId}`, "tasks-all", async () => {
|
|
await page.getByRole("button", { name: "全部", exact: true }).click();
|
|
await page.waitForTimeout(500);
|
|
});
|
|
await shot(`/w/${wsId}/board`, "board");
|
|
await shot(`/w/${wsId}/tickets`, "tickets");
|
|
await shot(`/w/${wsId}/knowledge`, "knowledge");
|
|
await shot(`/w/${wsId}/members`, "members");
|
|
await shot(`/w/${wsId}`, "detail", async () => {
|
|
await page.getByRole("button", { name: "全部", exact: true }).click();
|
|
await page.waitForTimeout(500);
|
|
await page.getByRole("button", { name: "把采集端的日志级别改回 info" }).first().click();
|
|
await page.waitForTimeout(800);
|
|
});
|
|
await shot("/login", "login");
|
|
|
|
console.log(shots.join("\n"));
|
|
await browser.close();
|