mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
fix: 修复 web/admin-spa 合并冲突和构建错误
- 恢复 web/admin-spa 目录到 dev 分支状态 - 修复 ApiKeysView.vue 中的重复变量声明问题 - 重新构建前端项目确保正常运行 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1 +0,0 @@
|
||||
.accounts-container[data-v-981f21d9]{min-height:calc(100vh - 300px)}.table-container[data-v-981f21d9]{overflow-x:auto;border-radius:12px;border:1px solid rgba(0,0,0,.05)}.table-row[data-v-981f21d9]{transition:all .2s ease}.table-row[data-v-981f21d9]:hover{background-color:#00000005}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
pre[data-v-2c02f1f7]{white-space:pre-wrap;word-wrap:break-word}.tab-content[data-v-277a81a9]{min-height:calc(100vh - 300px)}.table-container[data-v-277a81a9]{overflow-x:auto;border-radius:12px;border:1px solid rgba(0,0,0,.05)}.table-row[data-v-277a81a9]{transition:all .2s ease}.table-row[data-v-277a81a9]:hover{background-color:#00000005}.loading-spinner[data-v-277a81a9]{width:24px;height:24px;border:2px solid #e5e7eb;border-top:2px solid #3b82f6;border-radius:50%;animation:spin-277a81a9 1s linear infinite}@keyframes spin-277a81a9{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.api-key-date-picker[data-v-277a81a9] .el-input__inner{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.api-key-date-picker[data-v-277a81a9] .el-input__inner:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.api-key-date-picker[data-v-277a81a9] .el-range-separator{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.custom-date-picker[data-v-e2cbd0e3] .el-input__inner{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.custom-date-picker[data-v-e2cbd0e3] .el-input__inner:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1));--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.custom-date-picker[data-v-e2cbd0e3] .el-input__inner{font-size:13px;padding:0 10px}.custom-date-picker[data-v-e2cbd0e3] .el-range-separator{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1));padding:0 2px}.custom-date-picker[data-v-e2cbd0e3] .el-range-input{font-size:13px}@keyframes spin-e2cbd0e3{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.animate-spin[data-v-e2cbd0e3]{animation:spin-e2cbd0e3 1s linear infinite}
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{c as b,r as x,q as f,x as a,z as s,L as i,Q as y,u as o,P as m,Y as _,K as u,aq as c,O as g,y as n}from"./vue-vendor-CKToUHZx.js";import{_ as v,u as w}from"./index-9AMT1Op2.js";/* empty css */import"./element-plus-B8Fs_0jW.js";import"./vendor-BDiMbLwQ.js";const h={class:"flex items-center justify-center min-h-screen p-6"},k={class:"glass-strong rounded-3xl p-10 w-full max-w-md shadow-2xl"},L={class:"text-center mb-8"},S={class:"w-20 h-20 mx-auto mb-6 bg-gradient-to-br from-blue-500/20 to-purple-500/20 border border-gray-300/30 rounded-2xl flex items-center justify-center backdrop-blur-sm overflow-hidden"},V=["src"],I={key:1,class:"fas fa-cloud text-3xl text-gray-700"},N={key:1,class:"w-12 h-12 bg-gray-300/50 rounded animate-pulse"},q={key:0,class:"text-3xl font-bold text-white mb-2 header-title"},D={key:1,class:"h-9 w-64 bg-gray-300/50 rounded animate-pulse mx-auto mb-2"},E=["disabled"],j={key:0,class:"fas fa-sign-in-alt mr-2"},B={key:1,class:"loading-spinner mr-2"},M={key:0,class:"mt-6 p-4 bg-red-500/20 border border-red-500/30 rounded-xl text-red-800 text-sm text-center backdrop-blur-sm"},F={__name:"LoginView",setup(O){const e=w(),d=b(()=>e.oemLoading),l=x({username:"",password:""});f(()=>{e.loadOemSettings()});const p=async()=>{await e.login(l.value)};return(T,t)=>(n(),a("div",h,[s("div",k,[s("div",L,[s("div",S,[d.value?(n(),a("div",N)):(n(),a(y,{key:0},[o(e).oemSettings.siteIconData||o(e).oemSettings.siteIcon?(n(),a("img",{key:0,src:o(e).oemSettings.siteIconData||o(e).oemSettings.siteIcon,alt:"Logo",class:"w-12 h-12 object-contain",onError:t[0]||(t[0]=r=>r.target.style.display="none")},null,40,V)):(n(),a("i",I))],64))]),!d.value&&o(e).oemSettings.siteName?(n(),a("h1",q,m(o(e).oemSettings.siteName),1)):d.value?(n(),a("div",D)):i("",!0),t[3]||(t[3]=s("p",{class:"text-gray-600 text-lg"}," 管理后台 ",-1))]),s("form",{class:"space-y-6",onSubmit:_(p,["prevent"])},[s("div",null,[t[4]||(t[4]=s("label",{class:"block text-sm font-semibold text-gray-900 mb-3"},"用户名",-1)),u(s("input",{"onUpdate:modelValue":t[1]||(t[1]=r=>l.value.username=r),type:"text",required:"",class:"form-input w-full",placeholder:"请输入用户名"},null,512),[[c,l.value.username]])]),s("div",null,[t[5]||(t[5]=s("label",{class:"block text-sm font-semibold text-gray-900 mb-3"},"密码",-1)),u(s("input",{"onUpdate:modelValue":t[2]||(t[2]=r=>l.value.password=r),type:"password",required:"",class:"form-input w-full",placeholder:"请输入密码"},null,512),[[c,l.value.password]])]),s("button",{type:"submit",disabled:o(e).loginLoading,class:"btn btn-primary w-full py-4 px-6 text-lg font-semibold"},[o(e).loginLoading?i("",!0):(n(),a("i",j)),o(e).loginLoading?(n(),a("div",B)):i("",!0),g(" "+m(o(e).loginLoading?"登录中...":"登录"),1)],8,E)],32),o(e).loginError?(n(),a("div",M,[t[6]||(t[6]=s("i",{class:"fas fa-exclamation-triangle mr-2"},null,-1)),g(m(o(e).loginError),1)])):i("",!0)])]))}},P=v(F,[["__scopeId","data-v-82195a01"]]);export{P as default};
|
||||
@@ -1 +0,0 @@
|
||||
/* empty css */import{_ as r}from"./index-9AMT1Op2.js";import{x as t,y as s,z as o,Q as d,L as a,A as c,C as g,P as i}from"./vue-vendor-CKToUHZx.js";const u={class:"flex items-center gap-4"},f={class:"w-12 h-12 bg-gradient-to-br from-blue-500/20 to-purple-500/20 border border-gray-300/30 rounded-xl flex items-center justify-center backdrop-blur-sm flex-shrink-0 overflow-hidden"},y=["src"],m={key:1,class:"fas fa-cloud text-xl text-gray-700"},h={key:1,class:"w-8 h-8 bg-gray-300/50 rounded animate-pulse"},x={class:"flex flex-col justify-center min-h-[48px]"},b={class:"flex items-center gap-3"},k={key:1,class:"h-8 w-64 bg-gray-300/50 rounded animate-pulse"},_={key:0,class:"text-gray-600 text-sm leading-tight mt-0.5"},S={__name:"LogoTitle",props:{loading:{type:Boolean,default:!1},title:{type:String,default:""},subtitle:{type:String,default:""},logoSrc:{type:String,default:""},titleClass:{type:String,default:"text-gray-900"}},setup(e){const n=l=>{l.target.style.display="none"};return(l,p)=>(s(),t("div",u,[o("div",f,[e.loading?(s(),t("div",h)):(s(),t(d,{key:0},[e.logoSrc?(s(),t("img",{key:0,src:e.logoSrc,alt:"Logo",class:"w-8 h-8 object-contain",onError:n},null,40,y)):(s(),t("i",m))],64))]),o("div",x,[o("div",b,[!e.loading&&e.title?(s(),t("h1",{key:0,class:g(["text-2xl font-bold header-title leading-tight",e.titleClass])},i(e.title),3)):e.loading?(s(),t("div",k)):a("",!0),c(l.$slots,"after-title",{},void 0,!0)]),e.subtitle?(s(),t("p",_,i(e.subtitle),1)):a("",!0)])]))}},C=r(S,[["__scopeId","data-v-718feedc"]]);export{C as L};
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.user-menu-dropdown[data-v-590374fc]{margin-top:8px}.fade-enter-active[data-v-590374fc],.fade-leave-active[data-v-590374fc]{transition:opacity .3s}.fade-enter-from[data-v-590374fc],.fade-leave-to[data-v-590374fc]{opacity:0}
|
||||
@@ -1 +0,0 @@
|
||||
.settings-container[data-v-d29d5f49]{min-height:calc(100vh - 300px)}.card[data-v-d29d5f49]{background:#fff;border-radius:12px;box-shadow:0 2px 12px #0000001a;border:1px solid #e5e7eb}.table-container[data-v-d29d5f49]{overflow:hidden;border-radius:8px;border:1px solid #f3f4f6}.table-row[data-v-d29d5f49]{transition:background-color .2s ease}.table-row[data-v-d29d5f49]:hover{background-color:#f9fafb}.form-input[data-v-d29d5f49]{width:100%;border-radius:.5rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1));padding:.5rem 1rem;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.form-input[data-v-d29d5f49]:focus{border-color:transparent;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.btn[data-v-d29d5f49]{display:inline-flex;align-items:center;justify-content:center;border-radius:.5rem;padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:600;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.btn[data-v-d29d5f49]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-offset-width: 2px}.btn-primary[data-v-d29d5f49]{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-primary[data-v-d29d5f49]:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.btn-primary[data-v-d29d5f49]:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.btn-success[data-v-d29d5f49]{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-success[data-v-d29d5f49]:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.btn-success[data-v-d29d5f49]:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity, 1))}.loading-spinner[data-v-d29d5f49]{height:1.25rem;width:1.25rem}@keyframes spin-d29d5f49{to{transform:rotate(360deg)}}.loading-spinner[data-v-d29d5f49]{animation:spin-d29d5f49 1s linear infinite;border-radius:9999px;border-width:2px;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1));--tw-border-opacity: 1;border-top-color:rgb(37 99 235 / var(--tw-border-opacity, 1))}
|
||||
@@ -1,3 +0,0 @@
|
||||
import{aR as k,r as x,aW as C,q as O,x as m,z as e,u as i,K as T,aq as N,L as _,O as v,C as j,P as S,y as g}from"./vue-vendor-CKToUHZx.js";import{s as c}from"./toast-BvwA7Mwb.js";import{a as D,_ as F}from"./index-9AMT1Op2.js";import"./element-plus-B8Fs_0jW.js";import"./vendor-BDiMbLwQ.js";const E=k("settings",()=>{const l=x({siteName:"Claude Relay Service",siteIcon:"",siteIconData:"",updatedAt:null}),r=x(!1),p=x(!1),d=async()=>{r.value=!0;try{const s=await D.get("/admin/oem-settings");return s&&s.success&&(l.value={...l.value,...s.data},f()),s}catch(s){throw console.error("Failed to load OEM settings:",s),s}finally{r.value=!1}},a=async s=>{p.value=!0;try{const o=await D.put("/admin/oem-settings",s);return o&&o.success&&(l.value={...l.value,...o.data},f()),o}catch(o){throw console.error("Failed to save OEM settings:",o),o}finally{p.value=!1}},w=async()=>{const s={siteName:"Claude Relay Service",siteIcon:"",siteIconData:"",updatedAt:null};return l.value={...s},await a(s)},f=()=>{if(l.value.siteName&&(document.title=`${l.value.siteName} - 管理后台`),l.value.siteIconData||l.value.siteIcon){const s=document.querySelector('link[rel="icon"]')||document.createElement("link");s.rel="icon",s.href=l.value.siteIconData||l.value.siteIcon,document.querySelector('link[rel="icon"]')||document.head.appendChild(s)}};return{oemSettings:l,loading:r,saving:p,loadOemSettings:d,saveOemSettings:a,resetOemSettings:w,applyOemSettings:f,formatDateTime:s=>s?new Date(s).toLocaleString("zh-CN",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}):"",validateIconFile:s=>{const o=[];return s.size>350*1024&&o.push("图标文件大小不能超过 350KB"),["image/x-icon","image/png","image/jpeg","image/jpg","image/svg+xml"].includes(s.type)||o.push("不支持的文件类型,请选择 .ico, .png, .jpg 或 .svg 文件"),{isValid:o.length===0,errors:o}},fileToBase64:s=>new Promise((o,n)=>{const t=new FileReader;t.onload=u=>o(u.target.result),t.onerror=n,t.readAsDataURL(s)})}}),B={class:"settings-container"},V={class:"card p-6"},R={key:0,class:"text-center py-12"},M={key:1,class:"table-container"},A={class:"min-w-full"},q={class:"divide-y divide-gray-200/50"},z={class:"table-row"},K={class:"px-6 py-4"},L={class:"table-row"},U={class:"px-6 py-4"},$={class:"space-y-3"},P={key:0,class:"inline-flex items-center gap-3 p-3 bg-gray-50 rounded-lg"},W=["src"],G={class:"px-6 py-6",colspan:"2"},H={class:"flex items-center justify-between"},J={class:"flex gap-3"},Q=["disabled"],X={key:0,class:"loading-spinner mr-2"},Y={key:1,class:"fas fa-save mr-2"},Z=["disabled"],ee={key:0,class:"text-sm text-gray-500"},te={__name:"SettingsView",setup(l){const r=E(),{loading:p,saving:d,oemSettings:a}=C(r),w=x();O(async()=>{try{await r.loadOemSettings()}catch{c("加载设置失败","error")}});const f=async()=>{try{const n={siteName:a.value.siteName,siteIcon:a.value.siteIcon,siteIconData:a.value.siteIconData},t=await r.saveOemSettings(n);t&&t.success?c("OEM设置保存成功","success"):c((t==null?void 0:t.message)||"保存失败","error")}catch{c("保存OEM设置失败","error")}},b=async()=>{if(confirm(`确定要重置为默认设置吗?
|
||||
|
||||
这将清除所有自定义的网站名称和图标设置。`))try{const n=await r.resetOemSettings();n&&n.success?c("已重置为默认设置","success"):c("重置失败","error")}catch{c("重置失败","error")}},h=async n=>{const t=n.target.files[0];if(!t)return;const u=r.validateIconFile(t);if(!u.isValid){u.errors.forEach(y=>c(y,"error"));return}try{const y=await r.fileToBase64(t);a.value.siteIconData=y}catch{c("文件读取失败","error")}n.target.value=""},I=()=>{a.value.siteIcon="",a.value.siteIconData=""},s=()=>{console.warn("Icon failed to load")},o=r.formatDateTime;return(n,t)=>(g(),m("div",B,[e("div",V,[t[12]||(t[12]=e("div",{class:"flex flex-col md:flex-row justify-between items-center gap-4 mb-6"},[e("div",null,[e("h3",{class:"text-xl font-bold text-gray-900 mb-2"}," 其他设置 "),e("p",{class:"text-gray-600"}," 自定义网站名称和图标 ")])],-1)),i(p)?(g(),m("div",R,t[2]||(t[2]=[e("div",{class:"loading-spinner mx-auto mb-4"},null,-1),e("p",{class:"text-gray-500"}," 正在加载设置... ",-1)]))):(g(),m("div",M,[e("table",A,[e("tbody",q,[e("tr",z,[t[4]||(t[4]=e("td",{class:"px-6 py-4 whitespace-nowrap w-48"},[e("div",{class:"flex items-center"},[e("div",{class:"w-8 h-8 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center mr-3"},[e("i",{class:"fas fa-font text-white text-xs"})]),e("div",null,[e("div",{class:"text-sm font-semibold text-gray-900"}," 网站名称 "),e("div",{class:"text-xs text-gray-500"}," 品牌标识 ")])])],-1)),e("td",K,[T(e("input",{"onUpdate:modelValue":t[0]||(t[0]=u=>i(a).siteName=u),type:"text",class:"form-input w-full max-w-md",placeholder:"Claude Relay Service",maxlength:"100"},null,512),[[N,i(a).siteName]]),t[3]||(t[3]=e("p",{class:"text-xs text-gray-500 mt-1"}," 将显示在浏览器标题和页面头部 ",-1))])]),e("tr",L,[t[9]||(t[9]=e("td",{class:"px-6 py-4 whitespace-nowrap w-48"},[e("div",{class:"flex items-center"},[e("div",{class:"w-8 h-8 bg-gradient-to-br from-purple-500 to-purple-600 rounded-lg flex items-center justify-center mr-3"},[e("i",{class:"fas fa-image text-white text-xs"})]),e("div",null,[e("div",{class:"text-sm font-semibold text-gray-900"}," 网站图标 "),e("div",{class:"text-xs text-gray-500"}," Favicon ")])])],-1)),e("td",U,[e("div",$,[i(a).siteIconData||i(a).siteIcon?(g(),m("div",P,[e("img",{src:i(a).siteIconData||i(a).siteIcon,alt:"图标预览",class:"w-8 h-8",onError:s},null,40,W),t[6]||(t[6]=e("span",{class:"text-sm text-gray-600"},"当前图标",-1)),e("button",{class:"text-red-600 hover:text-red-900 font-medium hover:bg-red-50 px-3 py-1 rounded-lg transition-colors",onClick:I},t[5]||(t[5]=[e("i",{class:"fas fa-trash mr-1"},null,-1),v("删除 ",-1)]))])):_("",!0),e("div",null,[e("input",{ref_key:"iconFileInput",ref:w,type:"file",accept:".ico,.png,.jpg,.jpeg,.svg",class:"hidden",onChange:h},null,544),e("button",{class:"btn btn-success px-4 py-2",onClick:t[1]||(t[1]=u=>n.$refs.iconFileInput.click())},t[7]||(t[7]=[e("i",{class:"fas fa-upload mr-2"},null,-1),v(" 上传图标 ",-1)])),t[8]||(t[8]=e("span",{class:"text-xs text-gray-500 ml-3"},"支持 .ico, .png, .jpg, .svg 格式,最大 350KB",-1))])])])]),e("tr",null,[e("td",G,[e("div",H,[e("div",J,[e("button",{disabled:i(d),class:j(["btn btn-primary px-6 py-3",{"opacity-50 cursor-not-allowed":i(d)}]),onClick:f},[i(d)?(g(),m("div",X)):(g(),m("i",Y)),v(" "+S(i(d)?"保存中...":"保存设置"),1)],10,Q),e("button",{class:"btn bg-gray-100 text-gray-700 hover:bg-gray-200 px-6 py-3",disabled:i(d),onClick:b},t[10]||(t[10]=[e("i",{class:"fas fa-undo mr-2"},null,-1),v(" 重置为默认 ",-1)]),8,Z)]),i(a).updatedAt?(g(),m("div",ee,[t[11]||(t[11]=e("i",{class:"fas fa-clock mr-1"},null,-1)),v(" 最后更新:"+S(i(o)(i(a).updatedAt)),1)])):_("",!0)])])])])])]))])]))}},le=F(te,[["__scopeId","data-v-d29d5f49"]]);export{le as default};
|
||||
@@ -1 +0,0 @@
|
||||
.tutorial-container[data-v-508c8654]{min-height:calc(100vh - 300px)}.tutorial-content[data-v-508c8654]{animation:fadeIn-508c8654 .3s ease-in-out}@keyframes fadeIn-508c8654{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}code[data-v-508c8654]{font-family:Fira Code,Monaco,Menlo,Ubuntu Mono,monospace}.tutorial-content h4[data-v-508c8654]{scroll-margin-top:100px}.tutorial-content .bg-gradient-to-r[data-v-508c8654]{transition:all .2s ease}.tutorial-content .bg-gradient-to-r[data-v-508c8654]:hover{transform:translateY(-1px);box-shadow:0 4px 12px #0000001a}
|
||||
File diff suppressed because one or more lines are too long
2
web/admin-spa/dist/assets/index-9AMT1Op2.js
vendored
2
web/admin-spa/dist/assets/index-9AMT1Op2.js
vendored
File diff suppressed because one or more lines are too long
5
web/admin-spa/dist/assets/index-V6aqLFfH.css
vendored
5
web/admin-spa/dist/assets/index-V6aqLFfH.css
vendored
File diff suppressed because one or more lines are too long
4
web/admin-spa/dist/index.html
vendored
4
web/admin-spa/dist/index.html
vendored
@@ -18,12 +18,12 @@
|
||||
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
|
||||
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
|
||||
<script type="module" crossorigin src="/admin-next/assets/index-9AMT1Op2.js"></script>
|
||||
<script type="module" crossorigin src="/admin-next/assets/index-D3bIJLrk.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/admin-next/assets/vue-vendor-CKToUHZx.js">
|
||||
<link rel="modulepreload" crossorigin href="/admin-next/assets/vendor-BDiMbLwQ.js">
|
||||
<link rel="modulepreload" crossorigin href="/admin-next/assets/element-plus-B8Fs_0jW.js">
|
||||
<link rel="stylesheet" crossorigin href="/admin-next/assets/element-plus-CPnoEkWW.css">
|
||||
<link rel="stylesheet" crossorigin href="/admin-next/assets/index-V6aqLFfH.css">
|
||||
<link rel="stylesheet" crossorigin href="/admin-next/assets/index-Db9Zyv8s.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -288,92 +288,36 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-3">模型映射表 (可选)</label>
|
||||
<div class="bg-blue-50 p-3 rounded-lg mb-3">
|
||||
<p class="text-xs text-blue-700">
|
||||
<i class="fas fa-info-circle mr-1" />
|
||||
留空表示支持所有模型且不修改请求。配置映射后,左侧模型会被识别为支持的模型,右侧是实际发送的模型。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 模型映射表 -->
|
||||
<div class="space-y-2 mb-3">
|
||||
<div
|
||||
v-for="(mapping, index) in modelMappings"
|
||||
:key="index"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<input
|
||||
v-model="mapping.from"
|
||||
type="text"
|
||||
class="form-input flex-1"
|
||||
placeholder="原始模型名称"
|
||||
>
|
||||
<i class="fas fa-arrow-right text-gray-400" />
|
||||
<input
|
||||
v-model="mapping.to"
|
||||
type="text"
|
||||
class="form-input flex-1"
|
||||
placeholder="映射后的模型名称"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="p-2 text-red-500 hover:bg-red-50 rounded-lg transition-colors"
|
||||
@click="removeModelMapping(index)"
|
||||
>
|
||||
<i class="fas fa-trash" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加映射按钮 -->
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-4 py-2 border-2 border-dashed border-gray-300 text-gray-600 rounded-lg hover:border-gray-400 hover:text-gray-700 transition-colors"
|
||||
@click="addModelMapping"
|
||||
>
|
||||
<i class="fas fa-plus mr-2" />
|
||||
添加模型映射
|
||||
</button>
|
||||
|
||||
<!-- 快捷添加按钮 -->
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-3">支持的模型 (可选)--注意,ClaudeCode必须加上hiku模型!</label>
|
||||
<div class="mb-2 flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-blue-100 text-blue-700 rounded-lg hover:bg-blue-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-5-sonnet-20241022', 'claude-3-5-sonnet-20241022')"
|
||||
@click="addPresetModel('claude-sonnet-4-20250514')"
|
||||
>
|
||||
+ Sonnet 3.5
|
||||
+ claude-sonnet-4-20250514
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-purple-100 text-purple-700 rounded-lg hover:bg-purple-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-opus-20240229', 'claude-3-opus-20240229')"
|
||||
@click="addPresetModel('claude-opus-4-20250514')"
|
||||
>
|
||||
+ Opus 3
|
||||
+ claude-opus-4-20250514
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-5-haiku-20241022', 'claude-3-5-haiku-20241022')"
|
||||
class="px-3 py-1 text-xs bg-green-100 text-green-700 rounded-lg hover:bg-purple-200 transition-colors"
|
||||
@click="addPresetModel('claude-3-5-haiku-20241022')"
|
||||
>
|
||||
+ Haiku 3.5
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-orange-100 text-orange-700 rounded-lg hover:bg-orange-200 transition-colors"
|
||||
@click="addPresetMapping('claude-sonnet-4-20250514', 'claude-3-5-sonnet-20241022')"
|
||||
>
|
||||
+ Sonnet 4 → 3.5
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-red-100 text-red-700 rounded-lg hover:bg-red-200 transition-colors"
|
||||
@click="addPresetMapping('claude-opus-4-20250514', 'claude-3-opus-20240229')"
|
||||
>
|
||||
+ Opus 4 → 3
|
||||
+ claude-3-5-haiku-20241022
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="form.supportedModels"
|
||||
rows="3"
|
||||
class="form-input w-full resize-none"
|
||||
placeholder="每行一个模型,留空表示支持所有模型。特别注意,ClaudeCode必须加上hiku模型!"
|
||||
/>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
留空表示支持所有模型。如果指定模型,请求中的模型不在列表内将不会调度到此账号
|
||||
</p>
|
||||
@@ -697,92 +641,36 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-3">模型映射表 (可选)</label>
|
||||
<div class="bg-blue-50 p-3 rounded-lg mb-3">
|
||||
<p class="text-xs text-blue-700">
|
||||
<i class="fas fa-info-circle mr-1" />
|
||||
留空表示支持所有模型且不修改请求。配置映射后,左侧模型会被识别为支持的模型,右侧是实际发送的模型。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 模型映射表 -->
|
||||
<div class="space-y-2 mb-3">
|
||||
<div
|
||||
v-for="(mapping, index) in modelMappings"
|
||||
:key="index"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<input
|
||||
v-model="mapping.from"
|
||||
type="text"
|
||||
class="form-input flex-1"
|
||||
placeholder="原始模型名称"
|
||||
>
|
||||
<i class="fas fa-arrow-right text-gray-400" />
|
||||
<input
|
||||
v-model="mapping.to"
|
||||
type="text"
|
||||
class="form-input flex-1"
|
||||
placeholder="映射后的模型名称"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="p-2 text-red-500 hover:bg-red-50 rounded-lg transition-colors"
|
||||
@click="removeModelMapping(index)"
|
||||
>
|
||||
<i class="fas fa-trash" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加映射按钮 -->
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-4 py-2 border-2 border-dashed border-gray-300 text-gray-600 rounded-lg hover:border-gray-400 hover:text-gray-700 transition-colors"
|
||||
@click="addModelMapping"
|
||||
>
|
||||
<i class="fas fa-plus mr-2" />
|
||||
添加模型映射
|
||||
</button>
|
||||
|
||||
<!-- 快捷添加按钮 -->
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-3">支持的模型 (可选)</label>
|
||||
<div class="mb-2 flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-blue-100 text-blue-700 rounded-lg hover:bg-blue-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-5-sonnet-20241022', 'claude-3-5-sonnet-20241022')"
|
||||
@click="addPresetModel('claude-sonnet-4-20250514')"
|
||||
>
|
||||
+ Sonnet 3.5
|
||||
+ claude-sonnet-4-20250514
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-purple-100 text-purple-700 rounded-lg hover:bg-purple-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-opus-20240229', 'claude-3-opus-20240229')"
|
||||
@click="addPresetModel('claude-opus-4-20250514')"
|
||||
>
|
||||
+ Opus 3
|
||||
+ claude-opus-4-20250514
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-5-haiku-20241022', 'claude-3-5-haiku-20241022')"
|
||||
class="px-3 py-1 text-xs bg-green-100 text-green-700 rounded-lg hover:bg-purple-200 transition-colors"
|
||||
@click="addPresetModel('claude-3-5-haiku-20241022')"
|
||||
>
|
||||
+ Haiku 3.5
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-orange-100 text-orange-700 rounded-lg hover:bg-orange-200 transition-colors"
|
||||
@click="addPresetMapping('claude-sonnet-4-20250514', 'claude-3-5-sonnet-20241022')"
|
||||
>
|
||||
+ Sonnet 4 → 3.5
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-red-100 text-red-700 rounded-lg hover:bg-red-200 transition-colors"
|
||||
@click="addPresetMapping('claude-opus-4-20250514', 'claude-3-opus-20240229')"
|
||||
>
|
||||
+ Opus 4 → 3
|
||||
+ claude-3-5-haiku-20241022
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="form.supportedModels"
|
||||
rows="3"
|
||||
class="form-input w-full resize-none"
|
||||
placeholder="每行一个模型,留空表示支持所有模型。特别注意,ClaudeCode必须加上hiku模型!"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -968,32 +856,11 @@ const form = ref({
|
||||
apiUrl: props.account?.apiUrl || '',
|
||||
apiKey: props.account?.apiKey || '',
|
||||
priority: props.account?.priority || 50,
|
||||
supportedModels: props.account?.supportedModels?.join('\n') || '',
|
||||
userAgent: props.account?.userAgent || '',
|
||||
rateLimitDuration: props.account?.rateLimitDuration || 60
|
||||
})
|
||||
|
||||
// 模型映射表数据
|
||||
const modelMappings = ref([])
|
||||
|
||||
// 初始化模型映射表
|
||||
const initModelMappings = () => {
|
||||
if (props.account?.supportedModels) {
|
||||
// 如果是对象格式(新的映射表)
|
||||
if (typeof props.account.supportedModels === 'object' && !Array.isArray(props.account.supportedModels)) {
|
||||
modelMappings.value = Object.entries(props.account.supportedModels).map(([from, to]) => ({
|
||||
from,
|
||||
to
|
||||
}))
|
||||
} else if (Array.isArray(props.account.supportedModels)) {
|
||||
// 如果是数组格式(旧格式),转换为映射表
|
||||
modelMappings.value = props.account.supportedModels.map(model => ({
|
||||
from: model,
|
||||
to: model
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 表单验证错误
|
||||
const errors = ref({
|
||||
name: '',
|
||||
@@ -1188,7 +1055,9 @@ const createAccount = async () => {
|
||||
data.apiUrl = form.value.apiUrl
|
||||
data.apiKey = form.value.apiKey
|
||||
data.priority = form.value.priority || 50
|
||||
data.supportedModels = convertMappingsToObject() || {}
|
||||
data.supportedModels = form.value.supportedModels
|
||||
? form.value.supportedModels.split('\n').filter(m => m.trim())
|
||||
: []
|
||||
data.userAgent = form.value.userAgent || null
|
||||
data.rateLimitDuration = form.value.rateLimitDuration || 60
|
||||
}
|
||||
@@ -1305,7 +1174,9 @@ const updateAccount = async () => {
|
||||
data.apiKey = form.value.apiKey
|
||||
}
|
||||
data.priority = form.value.priority || 50
|
||||
data.supportedModels = convertMappingsToObject() || {}
|
||||
data.supportedModels = form.value.supportedModels
|
||||
? form.value.supportedModels.split('\n').filter(m => m.trim())
|
||||
: []
|
||||
data.userAgent = form.value.userAgent || null
|
||||
data.rateLimitDuration = form.value.rateLimitDuration || 60
|
||||
}
|
||||
@@ -1421,44 +1292,28 @@ watch(() => form.value.groupId, (newGroupId) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 添加模型映射
|
||||
const addModelMapping = () => {
|
||||
modelMappings.value.push({ from: '', to: '' })
|
||||
}
|
||||
|
||||
// 移除模型映射
|
||||
const removeModelMapping = (index) => {
|
||||
modelMappings.value.splice(index, 1)
|
||||
}
|
||||
|
||||
// 添加预设映射
|
||||
const addPresetMapping = (from, to) => {
|
||||
// 检查是否已存在相同的映射
|
||||
const exists = modelMappings.value.some(mapping => mapping.from === from)
|
||||
if (exists) {
|
||||
showToast(`模型 ${from} 的映射已存在`, 'info')
|
||||
// 添加预设模型
|
||||
const addPresetModel = (modelName) => {
|
||||
// 获取当前模型列表
|
||||
const currentModels = form.value.supportedModels
|
||||
? form.value.supportedModels.split('\n').filter(m => m.trim())
|
||||
: []
|
||||
|
||||
// 检查是否已存在
|
||||
if (currentModels.includes(modelName)) {
|
||||
showToast(`模型 ${modelName} 已存在`, 'info')
|
||||
return
|
||||
}
|
||||
|
||||
modelMappings.value.push({ from, to })
|
||||
showToast(`已添加映射: ${from} → ${to}`, 'success')
|
||||
}
|
||||
|
||||
// 将模型映射表转换为对象格式
|
||||
const convertMappingsToObject = () => {
|
||||
const mapping = {}
|
||||
modelMappings.value.forEach(item => {
|
||||
if (item.from && item.to) {
|
||||
mapping[item.from] = item.to
|
||||
}
|
||||
})
|
||||
return Object.keys(mapping).length > 0 ? mapping : null
|
||||
// 添加到列表
|
||||
currentModels.push(modelName)
|
||||
form.value.supportedModels = currentModels.join('\n')
|
||||
showToast(`已添加模型 ${modelName}`, 'success')
|
||||
}
|
||||
|
||||
// 监听账户变化,更新表单
|
||||
watch(() => props.account, (newAccount) => {
|
||||
if (newAccount) {
|
||||
initModelMappings()
|
||||
// 重新初始化代理配置
|
||||
const proxyConfig = newAccount.proxy && newAccount.proxy.host && newAccount.proxy.port
|
||||
? {
|
||||
@@ -1493,6 +1348,7 @@ watch(() => props.account, (newAccount) => {
|
||||
apiUrl: newAccount.apiUrl || '',
|
||||
apiKey: '', // 编辑模式不显示现有的 API Key
|
||||
priority: newAccount.priority || 50,
|
||||
supportedModels: newAccount.supportedModels?.join('\n') || '',
|
||||
userAgent: newAccount.userAgent || '',
|
||||
rateLimitDuration: newAccount.rateLimitDuration || 60
|
||||
}
|
||||
@@ -1514,7 +1370,4 @@ watch(() => props.account, (newAccount) => {
|
||||
}
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 初始化时调用
|
||||
initModelMappings()
|
||||
</script>
|
||||
@@ -534,6 +534,7 @@
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { showToast } from '@/utils/toast'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useClientsStore } from '@/stores/clients'
|
||||
import { useApiKeysStore } from '@/stores/apiKeys'
|
||||
import { apiClient } from '@/config/api'
|
||||
@@ -551,6 +552,7 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['close', 'success'])
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const clientsStore = useClientsStore()
|
||||
const apiKeysStore = useApiKeysStore()
|
||||
const loading = ref(false)
|
||||
@@ -636,6 +638,8 @@ const updateApiKey = async () => {
|
||||
concurrencyLimit: form.concurrencyLimit !== '' && form.concurrencyLimit !== null ? parseInt(form.concurrencyLimit) : 0,
|
||||
dailyCostLimit: form.dailyCostLimit !== '' && form.dailyCostLimit !== null ? parseFloat(form.dailyCostLimit) : 0,
|
||||
permissions: form.permissions,
|
||||
claudeAccountId: form.claudeAccountId || null,
|
||||
geminiAccountId: form.geminiAccountId || null,
|
||||
tags: form.tags
|
||||
}
|
||||
|
||||
@@ -647,25 +651,6 @@ const updateApiKey = async () => {
|
||||
data.enableClientRestriction = form.enableClientRestriction
|
||||
data.allowedClients = form.allowedClients
|
||||
|
||||
// 处理Claude账户绑定(区分OAuth和Console)
|
||||
if (form.claudeAccountId) {
|
||||
if (form.claudeAccountId.startsWith('console:')) {
|
||||
// Claude Console账户
|
||||
data.claudeConsoleAccountId = form.claudeAccountId.substring(8);
|
||||
data.claudeAccountId = null; // 清空OAuth绑定
|
||||
} else {
|
||||
// Claude OAuth账户
|
||||
data.claudeAccountId = form.claudeAccountId;
|
||||
data.claudeConsoleAccountId = null; // 清空Console绑定
|
||||
}
|
||||
} else {
|
||||
data.claudeAccountId = null;
|
||||
data.claudeConsoleAccountId = null;
|
||||
}
|
||||
|
||||
// Gemini账户绑定
|
||||
data.geminiAccountId = form.geminiAccountId || null;
|
||||
|
||||
const result = await apiClient.put(`/admin/api-keys/${props.apiKey.id}`, data)
|
||||
|
||||
if (result.success) {
|
||||
@@ -762,15 +747,7 @@ onMounted(async () => {
|
||||
form.concurrencyLimit = props.apiKey.concurrencyLimit || ''
|
||||
form.dailyCostLimit = props.apiKey.dailyCostLimit || ''
|
||||
form.permissions = props.apiKey.permissions || 'all'
|
||||
// 处理Claude账户绑定初始化
|
||||
if (props.apiKey.claudeAccountId) {
|
||||
form.claudeAccountId = props.apiKey.claudeAccountId;
|
||||
} else if (props.apiKey.claudeConsoleAccountId) {
|
||||
form.claudeAccountId = `console:${props.apiKey.claudeConsoleAccountId}`;
|
||||
} else {
|
||||
form.claudeAccountId = '';
|
||||
}
|
||||
|
||||
form.claudeAccountId = props.apiKey.claudeAccountId || ''
|
||||
form.geminiAccountId = props.apiKey.geminiAccountId || ''
|
||||
form.restrictedModels = props.apiKey.restrictedModels || []
|
||||
form.allowedClients = props.apiKey.allowedClients || []
|
||||
|
||||
@@ -472,10 +472,10 @@
|
||||
:default-time="defaultTime"
|
||||
size="small"
|
||||
style="width: 280px;"
|
||||
@update:model-value="(value) => onApiKeyCustomDateRangeChange(key.id, value)"
|
||||
class="api-key-date-picker"
|
||||
:clearable="true"
|
||||
:unlink-panels="false"
|
||||
@update:model-value="(value) => onApiKeyCustomDateRangeChange(key.id, value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -989,11 +989,6 @@ const accounts = ref({ claude: [], gemini: [], claudeGroups: [], geminiGroups: [
|
||||
const editingExpiryKey = ref(null)
|
||||
const expiryEditModalRef = ref(null)
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const pageSizeOptions = [5, 10, 20, 50, 100]
|
||||
|
||||
// 标签相关
|
||||
const selectedTagFilter = ref('')
|
||||
const availableTags = ref([])
|
||||
@@ -1017,8 +1012,8 @@ const renewingApiKey = ref(null)
|
||||
const newApiKeyData = ref(null)
|
||||
const batchApiKeyData = ref([])
|
||||
|
||||
// 计算筛选和排序后的API Keys(未分页)
|
||||
const filteredAndSortedApiKeys = computed(() => {
|
||||
// 计算排序后的API Keys
|
||||
const sortedApiKeys = computed(() => {
|
||||
// 先进行标签筛选
|
||||
let filteredKeys = apiKeys.value
|
||||
if (selectedTagFilter.value) {
|
||||
@@ -1109,17 +1104,8 @@ const loadAccounts = async () => {
|
||||
apiClient.get('/admin/account-groups')
|
||||
])
|
||||
|
||||
// 合并Claude OAuth账户和Claude Console账户
|
||||
const claudeAccounts = []
|
||||
|
||||
if (claudeData.success) {
|
||||
claudeData.data?.forEach(account => {
|
||||
claudeAccounts.push({
|
||||
...account,
|
||||
platform: 'claude-oauth',
|
||||
isDedicated: account.accountType === 'dedicated'
|
||||
})
|
||||
})
|
||||
accounts.value.claude = claudeData.data || []
|
||||
}
|
||||
|
||||
if (claudeConsoleData.success) {
|
||||
@@ -1132,10 +1118,7 @@ const loadAccounts = async () => {
|
||||
}
|
||||
|
||||
if (geminiData.success) {
|
||||
accounts.value.gemini = (geminiData.data || []).map(account => ({
|
||||
...account,
|
||||
isDedicated: account.accountType === 'dedicated'
|
||||
}))
|
||||
accounts.value.gemini = geminiData.data || []
|
||||
}
|
||||
|
||||
if (groupsData.success) {
|
||||
@@ -1165,9 +1148,6 @@ const loadApiKeys = async () => {
|
||||
}
|
||||
})
|
||||
availableTags.value = Array.from(tagsSet).sort()
|
||||
|
||||
// 重置到第一页
|
||||
currentPage.value = 1
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('加载 API Keys 失败', 'error')
|
||||
@@ -1184,8 +1164,6 @@ const sortApiKeys = (field) => {
|
||||
apiKeysSortBy.value = field
|
||||
apiKeysSortOrder.value = 'asc'
|
||||
}
|
||||
// 排序时重置到第一页
|
||||
currentPage.value = 1
|
||||
}
|
||||
|
||||
// 格式化数字
|
||||
@@ -1202,28 +1180,23 @@ const calculateApiKeyCost = (usage) => {
|
||||
}
|
||||
|
||||
// 获取绑定账户名称
|
||||
const getBoundAccountName = (claudeAccountId, claudeConsoleAccountId) => {
|
||||
// 优先显示Claude OAuth账户
|
||||
if (claudeAccountId) {
|
||||
const claudeAccount = accounts.value.claude.find(acc => acc.id === claudeAccountId)
|
||||
if (claudeAccount) {
|
||||
return claudeAccount.name
|
||||
}
|
||||
// 如果找不到,返回账户ID的前8位
|
||||
return `账户-${claudeAccountId.substring(0, 8)}`
|
||||
const getBoundAccountName = (accountId) => {
|
||||
if (!accountId) return '未知账户'
|
||||
|
||||
// 从Claude账户列表中查找
|
||||
const claudeAccount = accounts.value.claude.find(acc => acc.id === accountId)
|
||||
if (claudeAccount) {
|
||||
return claudeAccount.name
|
||||
}
|
||||
|
||||
// 其次显示Claude Console账户
|
||||
if (claudeConsoleAccountId) {
|
||||
const consoleAccount = accounts.value.claude.find(acc => acc.id === claudeConsoleAccountId)
|
||||
if (consoleAccount) {
|
||||
return `${consoleAccount.name} (Console)`
|
||||
}
|
||||
// 如果找不到,返回账户ID的前8位
|
||||
return `Console-${claudeConsoleAccountId.substring(0, 8)}`
|
||||
// 从Gemini账户列表中查找
|
||||
const geminiAccount = accounts.value.gemini.find(acc => acc.id === accountId)
|
||||
if (geminiAccount) {
|
||||
return geminiAccount.name
|
||||
}
|
||||
|
||||
return '未知账户'
|
||||
// 如果找不到,返回账户ID的前8位
|
||||
return `账户-${accountId.substring(0, 8)}`
|
||||
}
|
||||
|
||||
// 检查API Key是否过期
|
||||
@@ -1504,7 +1477,7 @@ const deleteApiKey = async (keyId) => {
|
||||
const copyApiStatsLink = (apiKey) => {
|
||||
// 构建统计页面的完整URL
|
||||
const baseUrl = window.location.origin
|
||||
const statsUrl = `${baseUrl}/admin-next/api-stats?apiId=${apiKey.id}`
|
||||
const statsUrl = `${baseUrl}/admin/api-stats?apiId=${apiKey.id}`
|
||||
|
||||
// 使用传统的textarea方法复制到剪贴板
|
||||
const textarea = document.createElement('textarea')
|
||||
@@ -1673,5 +1646,4 @@ onMounted(async () => {
|
||||
.api-key-date-picker :deep(.el-range-separator) {
|
||||
@apply text-gray-500;
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user