@@ -187,6 +187,27 @@ function sparkSvg(pts) {
187187}
188188
189189function stationRow ( s ) {
190+ // 固定成本渠道:不访问接口,只展示摊销信息
191+ if ( s . type === "fixed" ) {
192+ const daily = s . fixedCostCny > 0 && s . fixedDays > 0 ? s . fixedCostCny / s . fixedDays : 0 ;
193+ return `
194+ <div class="st-row" data-id="${ s . id } ">
195+ <div class="st-plate">¥</div>
196+ <div class="st-main" style="cursor:default">
197+ <div class="st-name">${ esc ( s . name ) } <span class="demo-tag">固定成本</span></div>
198+ <div class="st-meta">${ s . baseUrl ? esc ( s . baseUrl ) + " · " : "" } 不访问接口 · 仅计入利润成本</div>
199+ <div class="st-predict"><span>日均摊销 ${ cny ( daily ) } </span><span>·</span><span>每次 ${ cny ( s . fixedCostCny || 0 ) } 管 ${ s . fixedDays || 0 } 天</span></div>
200+ </div>
201+ <div class="st-balance">
202+ <div class="amt">${ cny ( daily ) } </div>
203+ <div class="sub">每天</div>
204+ </div>
205+ <div class="st-actions">
206+ <button class="icon-btn" data-act="edit" title="编辑"><svg viewBox="0 0 24 24"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z"/></svg></button>
207+ <button class="icon-btn" data-act="delete" title="删除"><svg viewBox="0 0 24 24"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
208+ </div>
209+ </div>` ;
210+ }
190211 const st = statusOf ( s ) ;
191212 const b = s . balance ;
192213 const rate = rateOf ( s ) ;
@@ -596,7 +617,7 @@ function renderUsage() {
596617 `<button data-range="${ v } " class="${ state . usageRange === v ? "active" : "" } ">${ l } </button>` ) . join ( "" ) } </div>
597618 <select class="select usage-station" id="usageStation">
598619 <option value="all">全部上游站点</option>
599- ${ state . stations . map ( ( s ) => `<option value="${ s . id } "${ state . usageStation === s . id ? " selected" : "" } >${ esc ( s . name ) } ${ s . isOwn ? "(我的站)" : "" } </option>` ) . join ( "" ) }
620+ ${ state . stations . filter ( ( s ) => s . type !== "fixed" ) . map ( ( s ) => `<option value="${ s . id } "${ state . usageStation === s . id ? " selected" : "" } >${ esc ( s . name ) } ${ s . isOwn ? "(我的站)" : "" } </option>` ) . join ( "" ) }
600621 </select>
601622 </div>
602623 <div id="usageBody"><div class="chart-empty">加载中…</div></div>` ;
@@ -845,7 +866,6 @@ function renderOwn() {
845866 <div class="usage-filters">
846867 <div class="seg" id="ownRange">${ OWN_RANGES . map ( ( [ v , l ] ) =>
847868 `<button data-range="${ v } " class="${ state . ownRange === v ? "active" : "" } ">${ l } </button>` ) . join ( "" ) } </div>
848- <span class="muted" id="ownStationName"></span>
849869 </div>
850870 <div id="ownBody"><div class="chart-empty">加载中…</div></div>` ;
851871 loadOwn ( ) ;
@@ -875,9 +895,6 @@ function renderOwnBody() {
875895 if ( ! el || ! state . ownData ) return ;
876896 const d = state . ownData ;
877897 const rate = rateOf ( d . station ) ;
878- const nameEl = $ ( "#ownStationName" ) ;
879- if ( nameEl ) nameEl . textContent = `数据来源:${ d . station . name } ` ;
880-
881898 const totCost = d . byModel . reduce ( ( a , m ) => a + m . cost , 0 ) * rate ;
882899 const totTokens = d . byModel . reduce ( ( a , m ) => a + m . tokens , 0 ) ;
883900 const totReqs = d . byModel . reduce ( ( a , m ) => a + m . requests , 0 ) ;
@@ -1211,7 +1228,8 @@ function openModal(station) {
12111228 $ ( "#f-password" ) . value = "" ;
12121229 $ ( "#f-lowBalance" ) . value = station ?. lowBalanceUsd ?? "" ;
12131230 $ ( "#f-cnyRate" ) . value = station ?. cnyPerUsd ?? "" ;
1214- $ ( "#f-fixedCny" ) . value = station ?. fixedMonthlyCny ?? "" ;
1231+ $ ( "#f-fixedCost" ) . value = station ?. fixedCostCny ?? "" ;
1232+ $ ( "#f-fixedDays" ) . value = station ?. fixedDays ?? "" ;
12151233 $ ( "#f-own" ) . checked = ! ! station ?. isOwn ;
12161234 if ( station ) {
12171235 $ ( "#f-accessToken" ) . placeholder = station . hasAccessToken ? "已配置,留空保持不变" : "令牌 / JWT" ;
@@ -1239,10 +1257,16 @@ function syncCredFields() {
12391257 "newapi-key" : "任意可用的 sk- 密钥;通过 OpenAI 兼容计费接口查询额度。" ,
12401258 sub2api : "Sub2API 登录后的访问令牌(JWT);过期需手动更换,推荐用账号密码模式。" ,
12411259 "sub2api-password" : "填 Sub2API 的登录邮箱和密码,面板会自动登录并在令牌过期时自动续期。开启 2FA 的账号不支持。" ,
1260+ fixed : "包月 / 包年等定期投入的上游:不访问任何接口,只按天摊销计入利润成本。" ,
12421261 } ;
12431262 $ ( "#f-type-hint" ) . textContent = hints [ type ] || "" ;
12441263 $ ( "#l-accessToken" ) . textContent = type . startsWith ( "sub2api" ) ? "登录令牌(JWT)" : "访问令牌" ;
12451264 $ ( "#f-own-wrap" ) . style . display = type === "newapi" ? "" : "none" ; // 下游分析只支持 new-api
1265+ // 固定成本类型:只留名称/地址/固定成本,隐藏阈值与汇率;其他类型隐藏固定成本
1266+ const isFixed = type === "fixed" ;
1267+ $ ( "#f-fixedWrap" ) . style . display = isFixed ? "" : "none" ;
1268+ $ ( "#f-lowBalance" ) . closest ( ".form-field" ) . style . display = isFixed ? "none" : "" ;
1269+ $ ( "#f-cnyRate" ) . closest ( ".form-field" ) . style . display = isFixed ? "none" : "" ;
12461270}
12471271$ ( "#f-type" ) . onchange = syncCredFields ;
12481272
@@ -1255,7 +1279,8 @@ $("#modalSave").onclick = async () => {
12551279 email : $ ( "#f-email" ) . value . trim ( ) ,
12561280 lowBalanceUsd : $ ( "#f-lowBalance" ) . value . trim ( ) ,
12571281 cnyPerUsd : $ ( "#f-cnyRate" ) . value . trim ( ) ,
1258- fixedMonthlyCny : $ ( "#f-fixedCny" ) . value . trim ( ) ,
1282+ fixedCostCny : $ ( "#f-fixedCost" ) . value . trim ( ) ,
1283+ fixedDays : $ ( "#f-fixedDays" ) . value . trim ( ) ,
12591284 isOwn : $ ( "#f-type" ) . value === "newapi" && $ ( "#f-own" ) . checked ,
12601285 } ;
12611286 const at = $ ( "#f-accessToken" ) . value . trim ( ) ;
@@ -1264,7 +1289,12 @@ $("#modalSave").onclick = async () => {
12641289 if ( at ) payload . accessToken = at ;
12651290 if ( ak ) payload . apiKey = ak ;
12661291 if ( pw ) payload . password = pw ;
1267- if ( ! payload . baseUrl ) return toast ( "请填写站点地址" , "err" ) ;
1292+ if ( payload . type === "fixed" ) {
1293+ if ( ! ( Number ( payload . fixedCostCny ) > 0 ) ) return toast ( "请填写每次付费金额" , "err" ) ;
1294+ if ( ! ( Number ( payload . fixedDays ) > 0 ) ) return toast ( "请填写覆盖天数" , "err" ) ;
1295+ } else if ( ! payload . baseUrl ) {
1296+ return toast ( "请填写站点地址" , "err" ) ;
1297+ }
12681298 try {
12691299 if ( editingId ) { await api . update ( editingId , payload ) ; toast ( "已更新" ) ; }
12701300 else { await api . create ( payload ) ; toast ( "已添加,正在查询余额…" ) ; }
0 commit comments