diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..a1311e5b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,47 @@ +# 统一换行符,保证跨平台构建产物字节一致。 +# 背景:Windows 上 core.autocrlf=true 会把文本文件 checkout 成 CRLF, +# 前端构建(Vite)按内容哈希命名产物,换行符差异会导致同一文件哈希漂移 +# (例如 workbuddy-mark SVG 在 Windows 构建后文件名变化)。 +# 这里显式声明 eol=lf,优先级高于 core.autocrlf,任何平台 checkout 都是 LF。 + +# 默认:自动检测文本,仓库内与工作区统一 LF +* text=auto eol=lf + +# 明确二进制类型,禁止任何换行符转换 +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.webp binary +*.woff binary +*.woff2 binary +*.ttf binary +*.eot binary +*.otf binary +*.gz binary +*.tgz binary +*.zip binary +*.pdf binary +*.wasm binary + +# SVG 是文本,保持 LF(与上面 * 规则一致,显式写出更直观) +*.svg text eol=lf + +# Go / 脚本 / 配置类常见文本 +*.go text eol=lf +*.mjs text eol=lf +*.js text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +*.css text eol=lf +*.html text eol=lf +*.json text eol=lf +*.md text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.sh text eol=lf + +# Windows 批处理必须保持 CRLF +*.bat text eol=crlf +*.cmd text eol=crlf diff --git a/.opencode/.gitignore b/.opencode/.gitignore new file mode 100644 index 00000000..47241b6e --- /dev/null +++ b/.opencode/.gitignore @@ -0,0 +1 @@ +images/ diff --git a/changelog/unreleased/traffic-chart-error-series.md b/changelog/unreleased/traffic-chart-error-series.md new file mode 100644 index 00000000..45a8647e --- /dev/null +++ b/changelog/unreleased/traffic-chart-error-series.md @@ -0,0 +1,7 @@ +### English + +- Fix the Overview traffic chart so the success and failure lines each plot their own counts instead of both tracking total requests, and make the failure line cover every non-success outcome (error, incomplete, canceled) so no traffic disappears from the curve. + +### 中文 + +- 修复概览页流量折线图:成功与失败各自显示自己的计数,不再都跟着总请求数;失败折线覆盖全部未成功请求(失败、未完成、取消),曲线不再丢数据。 diff --git a/frontend/src/components/overview/TrafficChart.tsx b/frontend/src/components/overview/TrafficChart.tsx index 406d83fb..96774d25 100644 --- a/frontend/src/components/overview/TrafficChart.tsx +++ b/frontend/src/components/overview/TrafficChart.tsx @@ -31,7 +31,7 @@ function niceMax(peak: number) { return nice * base } -type ChartRow = RequestStatsPoint & { okCount: number } +type ChartRow = RequestStatsPoint & { okCount: number; errorCount: number } function TrafficTooltip({ active, @@ -60,7 +60,8 @@ function TrafficTooltip({
{var i=typeof l==`function`?l(e.value,r):e.value;return p===`width`?ok(lT(i,{fontSize:t,letterSpacing:n}),m,d):lT(i,{fontSize:t,letterSpacing:n})[p]},g=i[0],_=i[1],v=i.length>=2&&g!=null&&_!=null?le(_.coordinate-g.coordinate):1,y=sk(a,v,p);return c===`equidistantPreserveStart`?uk(v,y,h,i,o):c===`equidistantPreserveEnd`?dk(v,y,h,i,o):(f=c===`preserveStart`||c===`preserveStartEnd`?vk(v,y,h,i,o,c===`preserveStartEnd`):_k(v,y,h,i,o),f.filter(e=>e.isShow))}var bk=e=>{var t=e.ticks,n=e.label,r=e.labelGapWithTick,i=r===void 0?5:r,a=e.tickSize,o=a===void 0?0:a,s=e.tickMargin,c=s===void 0?0:s,l=0;if(t){Array.from(t).forEach(e=>{if(e){var t=e.getBoundingClientRect();t.width>l&&(l=t.width)}});var u=n?n.getBoundingClientRect().width:0,d=o+c,f=l+d+u+(n?i:0);return Math.round(f)}return 0},xk=e=>{var t=e.ticks,n=e.label,r=e.labelGapWithTick,i=r===void 0?5:r,a=e.tickSize,o=a===void 0?0:a,s=e.tickMargin,c=s===void 0?0:s,l=0;if(t){Array.from(t).forEach(e=>{if(e){var t=e.getBoundingClientRect();t.height>l&&(l=t.height)}});var u=n?n.getBoundingClientRect().height:0,d=o+c,f=l+d+u+(n?i:0);return Math.round(f)}return 0},Sk=Ua({name:`renderedTicks`,initialState:{xAxis:{},yAxis:{}},reducers:{setRenderedTicks:(e,t)=>{var n=t.payload,r=n.axisType,i=n.axisId,a=n.ticks;e[r][i]=V(a)},removeRenderedTicks:(e,t)=>{var n=t.payload,r=n.axisType,i=n.axisId;delete e[r][i]}}}),Ck=Sk.actions,wk=Ck.setRenderedTicks,Tk=Ck.removeRenderedTicks,Ek=Sk.reducer,Dk=[`axisLine`,`width`,`height`,`className`,`hide`,`ticks`,`axisType`,`axisId`];function Ok(e,t){return Nk(e)||Mk(e,t)||Ak(e,t)||kk()}function kk(){throw TypeError(`Invalid attempt to destructure non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function Ak(e,t){if(e){if(typeof e==`string`)return jk(e,t);var n={}.toString.call(e).slice(8,-1);return n===`Object`&&e.constructor&&(n=e.constructor.name),n===`Map`||n===`Set`?Array.from(e):n===`Arguments`||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?jk(e,t):void 0}}function jk(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n