vue-api-request-builder 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.umd.js CHANGED
@@ -1,6 +1,6 @@
1
- (function(h,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(h=typeof globalThis<"u"?globalThis:h||self,e(h.ApiRequestBuilder={},h.Vue))})(this,function(h,e){"use strict";const q={class:"key-value-input flex flex-col gap-1"},P={class:"flex gap-2"},z={key:0,class:"flex flex-col gap-1"},D={class:"flex w-full gap-1 items-center"},I=e.defineComponent({__name:"KeyValueInput",props:{modelValue:{},addButtonText:{default:"添加参数"},showPreview:{type:Boolean,default:!1},previewText:{default:""}},emits:["update:modelValue"],setup(o,{emit:y}){const d=o,n=y,r=e.ref(d.modelValue);e.watch(()=>d.modelValue,i=>{r.value=i}),e.watch(r,i=>{n("update:modelValue",i)},{deep:!0});const m=()=>{r.value.push({key:"",value:""})},a=i=>{r.value.splice(i,1)},u=()=>{r.value=[]};return(i,s)=>{const c=e.resolveComponent("a-button"),f=e.resolveComponent("a-popconfirm"),C=e.resolveComponent("a-input"),_=e.resolveComponent("a-typography-paragraph"),w=e.resolveComponent("a-typography");return e.openBlock(),e.createElementBlock("div",q,[e.createElementVNode("div",P,[e.createVNode(c,{type:"primary",onClick:m,class:"w-40 max-w-full",size:"small"},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(i.addButtonText),1)]),_:1}),e.createVNode(f,{title:"确认清空",description:"确定要清空所有参数吗?","ok-text":"确认","cancel-text":"取消",onConfirm:u},{default:e.withCtx(()=>[e.createVNode(c,{type:"primary",danger:"",class:"w-40 max-w-full",size:"small",disabled:r.value.length===0},{default:e.withCtx(()=>s[0]||(s[0]=[e.createTextVNode(" 清空 ")])),_:1},8,["disabled"])]),_:1})]),r.value.length>0?(e.openBlock(),e.createElementBlock("div",z,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(r.value,(x,N)=>(e.openBlock(),e.createElementBlock("div",{key:N,class:"flex"},[e.createElementVNode("div",D,[e.createVNode(c,{type:"primary",danger:"",onClick:k=>a(N),class:"w-16",size:"small"},{default:e.withCtx(()=>s[1]||(s[1]=[e.createTextVNode(" 删除 ")])),_:2},1032,["onClick"]),e.createVNode(C,{value:x.key,"onUpdate:value":k=>x.key=k,placeholder:"键",style:{width:"60%"},size:"small"},null,8,["value","onUpdate:value"]),e.createVNode(C,{value:x.value,"onUpdate:value":k=>x.value=k,placeholder:"值",style:{width:"100%"},size:"small"},null,8,["value","onUpdate:value"])])]))),128))])):e.createCommentVNode("",!0),i.showPreview?(e.openBlock(),e.createBlock(w,{key:1,class:"m-0"},{default:e.withCtx(()=>[e.createVNode(_,{class:"!m-0"},{default:e.withCtx(()=>[e.createElementVNode("pre",null,e.toDisplayString(i.previewText),1)]),_:1})]),_:1})):e.createCommentVNode("",!0)])}}}),S=(o,y)=>{const d=o.__vccOpts||o;for(const[n,r]of y)d[n]=r;return d},g=S(I,[["__scopeId","data-v-1e5ef051"]]),j={method:"GET",url:"https://yesno.wtf",path:"/api",auth:{type:"none"},params:[],headers:[],body:{type:"application/json"}},J={class:"request-form"},L={class:"flex flex-col gap-1"},F={class:"flex flex-row gap-1"},H={class:"flex flex-row gap-1"},A={class:"mt-1"},K={key:0,class:"flex flex-col gap-1 mt-2"},M={class:"flex flex-row gap-1 h-6"},G={class:"flex flex-row gap-1"},W={key:1,class:"flex flex-col gap-1 mt-1"},X={class:"flex flex-row gap-1 items-center"},R=S(e.defineComponent({__name:"RequestForm",props:{modelValue:{default:()=>j}},emits:["update:modelValue"],setup(o,{emit:y}){const d=o,n=y,r=e.ref(d.modelValue.method),m=e.ref(d.modelValue.url),a=e.ref(d.modelValue.auth.type),u=e.ref(d.modelValue.path),i=e.ref(d.modelValue.auth.username||""),s=e.ref(d.modelValue.auth.password||""),c=e.ref(d.modelValue.auth.token||""),f=e.ref(d.modelValue.params),C=e.ref(d.modelValue.headers),_=e.ref(d.modelValue.body.formData||[]),w=e.ref(d.modelValue.body.type),x=e.ref(d.modelValue.body.json||""),N=e.ref(d.modelValue.body.raw||""),k=e.ref("");function te(l,t){let V;return function(...T){clearTimeout(V),V=setTimeout(()=>l.apply(this,T),t)}}const $=()=>({method:r.value,url:m.value,path:u.value,auth:{type:a.value,...a.value==="Basic"?{username:i.value,password:s.value}:a.value==="Bearer"?{token:c.value}:{}},params:f.value,headers:C.value,body:{type:w.value,...w.value==="application/json"?{json:x.value}:w.value==="multipart/form-data"?{formData:_.value}:{raw:N.value}}}),oe=te(()=>{n("update:modelValue",$())},100);e.watch([r,m,u,a,i,s,c,f,C,w,_,x,N],()=>{oe()},{deep:!0}),e.watch(()=>d.modelValue,l=>{const t=$();JSON.stringify(l)!==JSON.stringify(t)&&(r.value=l.method,m.value=l.url,u.value=l.path,a.value=l.auth.type,i.value=l.auth.username||"",s.value=l.auth.password||"",c.value=l.auth.token||"",f.value=l.params,C.value=l.headers,w.value=l.body.type,_.value=l.body.formData||[],x.value=l.body.json||"",N.value=l.body.raw||"")},{deep:!0});const ae=e.computed(()=>{const l=f.value.filter(t=>!!t.key).map(t=>t.key+"="+encodeURIComponent(t.value)).join("&");return l===""?"":"?"+l}),le=e.computed(()=>{switch(w.value){case"application/json":try{return x.value?JSON.stringify(JSON.parse(x.value),null,2):"-空-"}catch{return"Invalid JSON"}case"multipart/form-data":const l="----WebKitFormBoundaryPreview";return _.value.filter(V=>!!V.key).map(V=>`--${l}\r
2
- Content-Disposition: form-data; name="${V.key}"\r
1
+ (function(_,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(_=typeof globalThis<"u"?globalThis:_||self,e(_.ApiRequestBuilder={},_.Vue))})(this,function(_,e){"use strict";const P={class:"key-value-input flex flex-col gap-1"},v={class:"flex gap-2"},I={key:0,class:"flex flex-col gap-1"},L={class:"flex w-full gap-1 items-center"},J=e.defineComponent({__name:"KeyValueInput",props:{modelValue:{},addButtonText:{default:"添加参数"},showPreview:{type:Boolean,default:!1},previewText:{default:""}},emits:["update:modelValue"],setup(a,{emit:y}){const d=a,l=y,r=e.ref(d.modelValue);e.watch(()=>d.modelValue,s=>{r.value=s}),e.watch(r,s=>{l("update:modelValue",s)},{deep:!0});const u=()=>{r.value.push({key:"",value:""})},n=s=>{r.value.splice(s,1)},m=()=>{r.value=[]};return(s,o)=>{const p=e.resolveComponent("a-button"),f=e.resolveComponent("a-popconfirm"),h=e.resolveComponent("a-input"),C=e.resolveComponent("a-typography-paragraph"),N=e.resolveComponent("a-typography");return e.openBlock(),e.createElementBlock("div",P,[e.createElementVNode("div",v,[e.createVNode(p,{type:"primary",onClick:u,class:"w-40 max-w-full",size:"small"},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(s.addButtonText),1)]),_:1}),e.createVNode(f,{title:"确认清空",description:"确定要清空所有参数吗?","ok-text":"确认","cancel-text":"取消",onConfirm:m},{default:e.withCtx(()=>[e.createVNode(p,{type:"primary",danger:"",class:"w-40 max-w-full",size:"small",disabled:r.value.length===0},{default:e.withCtx(()=>o[0]||(o[0]=[e.createTextVNode(" 清空 ")])),_:1},8,["disabled"])]),_:1})]),r.value.length>0?(e.openBlock(),e.createElementBlock("div",I,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(r.value,(V,k)=>(e.openBlock(),e.createElementBlock("div",{key:k,class:"flex"},[e.createElementVNode("div",L,[e.createVNode(p,{type:"primary",danger:"",onClick:x=>n(k),class:"w-16",size:"small"},{default:e.withCtx(()=>o[1]||(o[1]=[e.createTextVNode(" 删除 ")])),_:2},1032,["onClick"]),e.createVNode(h,{value:V.key,"onUpdate:value":x=>V.key=x,placeholder:"键",style:{width:"60%"},size:"small"},null,8,["value","onUpdate:value"]),e.createVNode(h,{value:V.value,"onUpdate:value":x=>V.value=x,placeholder:"值",style:{width:"100%"},size:"small"},null,8,["value","onUpdate:value"])])]))),128))])):e.createCommentVNode("",!0),s.showPreview?(e.openBlock(),e.createBlock(N,{key:1,class:"m-0"},{default:e.withCtx(()=>[e.createVNode(C,{class:"!m-0"},{default:e.withCtx(()=>[e.createElementVNode("pre",null,e.toDisplayString(s.previewText),1)]),_:1})]),_:1})):e.createCommentVNode("",!0)])}}}),S=(a,y)=>{const d=a.__vccOpts||a;for(const[l,r]of y)d[l]=r;return d},E=S(J,[["__scopeId","data-v-1e5ef051"]]),j={method:"GET",url:"https://yesno.wtf",path:"/api",auth:{type:"none"},params:[],headers:[],body:{type:"application/json"}},F={class:"request-form"},H={class:"flex flex-col gap-1"},M={class:"flex flex-row gap-1"},A={class:"flex flex-row gap-1"},K={class:"mt-1"},G={key:0,class:"flex flex-col gap-1 mt-2"},W={class:"flex flex-row gap-1 h-6"},X={class:"flex flex-row gap-1"},Q={key:1,class:"flex flex-col gap-1 mt-1"},Y={class:"flex flex-row gap-1 items-center"},q=S(e.defineComponent({__name:"RequestForm",props:{modelValue:{default:()=>j}},emits:["update:modelValue"],setup(a,{emit:y}){const d=a,l=y,r=e.ref(d.modelValue.method),u=e.ref(d.modelValue.url),n=e.ref(d.modelValue.auth.type),m=e.ref(d.modelValue.path),s=e.ref(d.modelValue.auth.username||""),o=e.ref(d.modelValue.auth.password||""),p=e.ref(d.modelValue.auth.token||""),f=e.ref(d.modelValue.params),h=e.ref(d.modelValue.headers),C=e.ref(d.modelValue.body.formData||[]),N=e.ref(d.modelValue.body.type),V=e.ref(d.modelValue.body.json||""),k=e.ref(d.modelValue.body.raw||""),x=e.ref("");function U(i,t){let w;return function(...g){clearTimeout(w),w=setTimeout(()=>i.apply(this,g),t)}}const z=()=>({method:r.value,url:u.value,path:m.value,auth:{type:n.value,...n.value==="Basic"?{username:s.value,password:o.value}:n.value==="Bearer"?{token:p.value}:{}},params:f.value,headers:h.value,body:{type:N.value,...N.value==="application/json"?{json:V.value}:N.value==="multipart/form-data"?{formData:C.value}:{raw:k.value}}}),pe=U(()=>{l("update:modelValue",z())},100);e.watch([r,u,m,n,s,o,p,f,h,N,C,V,k],()=>{pe()},{deep:!0}),e.watch(()=>d.modelValue,i=>{const t=z();JSON.stringify(i)!==JSON.stringify(t)&&(r.value=i.method,u.value=i.url,m.value=i.path,n.value=i.auth.type,s.value=i.auth.username||"",o.value=i.auth.password||"",p.value=i.auth.token||"",f.value=i.params,h.value=i.headers,N.value=i.body.type,C.value=i.body.formData||[],V.value=i.body.json||"",k.value=i.body.raw||"")},{deep:!0});const ce=e.computed(()=>{const i=f.value.filter(t=>!!t.key).map(t=>t.key+"="+encodeURIComponent(t.value)).join("&");return i===""?"":"?"+i}),me=e.computed(()=>{switch(N.value){case"application/json":try{return V.value?JSON.stringify(JSON.parse(V.value),null,2):"-空-"}catch{return"Invalid JSON"}case"multipart/form-data":const i="----WebKitFormBoundaryPreview";return C.value.filter(w=>!!w.key).map(w=>`--${i}\r
2
+ Content-Disposition: form-data; name="${w.key}"\r
3
3
  \r
4
- ${V.value}\r
5
- `).join("")+`--${l}--\r
6
- `;case"text/plain":return N.value||"-空-";default:return"-空-"}}),ne=()=>{try{x.value&&(JSON.parse(x.value),k.value="")}catch{k.value="Invalid JSON format"}},re=()=>{try{const l=m.value,t=new URL(l);m.value=`${t.protocol}//${t.host}`,u.value=t.pathname;const V=new URLSearchParams(t.search),T=[];V.forEach((b,v)=>{T.push({key:v,value:b})}),f.value=T}catch(l){console.error("URL解析失败:",l)}},se=e.computed(()=>{if(!m.value)return!1;try{const l=m.value,t=new URL(l);return t.search!==""||t.pathname!=="/"}catch{return!1}});return(l,t)=>{const V=e.resolveComponent("a-input"),T=e.resolveComponent("a-button"),b=e.resolveComponent("a-select-option"),v=e.resolveComponent("a-select"),E=e.resolveComponent("a-card"),B=e.resolveComponent("a-radio-button"),O=e.resolveComponent("a-radio-group"),de=e.resolveComponent("a-input-password"),U=e.resolveComponent("a-textarea"),pe=e.resolveComponent("a-alert"),ie=e.resolveComponent("a-form");return e.openBlock(),e.createElementBlock("div",J,[e.createVNode(ie,{layout:"vertical"},{default:e.withCtx(()=>[e.createVNode(E,{title:"URL配置",class:"form-section",size:"small"},{default:e.withCtx(()=>[e.createElementVNode("div",L,[e.createElementVNode("div",F,[e.createVNode(V,{value:m.value,"onUpdate:value":t[0]||(t[0]=p=>m.value=p),placeholder:"基础URL",size:"small"},null,8,["value"]),se.value?(e.openBlock(),e.createBlock(T,{key:0,type:"primary",size:"small",onClick:re},{default:e.withCtx(()=>t[13]||(t[13]=[e.createTextVNode("拆解")])),_:1})):e.createCommentVNode("",!0)]),e.createElementVNode("div",H,[e.createVNode(v,{value:r.value,"onUpdate:value":t[1]||(t[1]=p=>r.value=p),class:"w-40",size:"small"},{default:e.withCtx(()=>[e.createVNode(b,{value:"GET"},{default:e.withCtx(()=>t[14]||(t[14]=[e.createTextVNode("GET")])),_:1}),e.createVNode(b,{value:"POST"},{default:e.withCtx(()=>t[15]||(t[15]=[e.createTextVNode("POST")])),_:1}),e.createVNode(b,{value:"PUT"},{default:e.withCtx(()=>t[16]||(t[16]=[e.createTextVNode("PUT")])),_:1}),e.createVNode(b,{value:"DELETE"},{default:e.withCtx(()=>t[17]||(t[17]=[e.createTextVNode("DELETE")])),_:1}),e.createVNode(b,{value:"OPTIONS"},{default:e.withCtx(()=>t[18]||(t[18]=[e.createTextVNode("OPTIONS")])),_:1})]),_:1},8,["value"]),e.createVNode(V,{value:u.value,"onUpdate:value":t[2]||(t[2]=p=>u.value=p),placeholder:"路径",size:"small"},null,8,["value"])])]),e.createElementVNode("div",A,[e.createVNode(g,{modelValue:f.value,"onUpdate:modelValue":t[3]||(t[3]=p=>f.value=p),"add-button-text":"添加参数","show-preview":!0,"preview-text":m.value+u.value+ae.value},null,8,["modelValue","preview-text"])])]),_:1}),e.createVNode(E,{title:"认证方案",class:"form-section",size:"small"},{default:e.withCtx(()=>[e.createVNode(O,{value:a.value,"onUpdate:value":t[4]||(t[4]=p=>a.value=p),"button-style":"solid",size:"small"},{default:e.withCtx(()=>[e.createVNode(B,{value:"none"},{default:e.withCtx(()=>t[19]||(t[19]=[e.createTextVNode("无认证")])),_:1}),e.createVNode(B,{value:"Basic"},{default:e.withCtx(()=>t[20]||(t[20]=[e.createTextVNode("Basic认证")])),_:1}),e.createVNode(B,{value:"Bearer"},{default:e.withCtx(()=>t[21]||(t[21]=[e.createTextVNode("Bearer认证")])),_:1})]),_:1},8,["value"]),a.value==="Basic"?(e.openBlock(),e.createElementBlock("div",K,[e.createElementVNode("div",M,[t[22]||(t[22]=e.createElementVNode("p",{class:"m-0 w-16"},"用户名",-1)),e.createVNode(V,{value:i.value,"onUpdate:value":t[5]||(t[5]=p=>i.value=p),size:"small"},null,8,["value"])]),e.createElementVNode("div",G,[t[23]||(t[23]=e.createElementVNode("p",{class:"m-0 w-16"},"密码",-1)),e.createVNode(de,{value:s.value,"onUpdate:value":t[6]||(t[6]=p=>s.value=p),size:"small"},null,8,["value"])])])):e.createCommentVNode("",!0),a.value==="Bearer"?(e.openBlock(),e.createElementBlock("div",W,[e.createElementVNode("div",X,[t[24]||(t[24]=e.createElementVNode("p",{class:"m-0 w-16 h-full"},"Token",-1)),e.createVNode(U,{value:c.value,"onUpdate:value":t[7]||(t[7]=p=>c.value=p),size:"small",class:"mt-1",rows:"4",placeholder:"请输入Token"},null,8,["value"])])])):e.createCommentVNode("",!0)]),_:1}),e.createVNode(E,{title:"请求头",class:"form-section",size:"small"},{default:e.withCtx(()=>[e.createVNode(g,{modelValue:C.value,"onUpdate:modelValue":t[8]||(t[8]=p=>C.value=p),"add-button-text":"添加请求头","show-preview":!1},null,8,["modelValue"])]),_:1}),r.value==="POST"||r.value==="PUT"?(e.openBlock(),e.createBlock(E,{key:0,title:"请求体",class:"form-section",size:"small"},{default:e.withCtx(()=>[e.createVNode(O,{value:w.value,"onUpdate:value":t[9]||(t[9]=p=>w.value=p),"button-style":"solid",size:"small",class:"mb-1"},{default:e.withCtx(()=>[e.createVNode(B,{value:"application/json"},{default:e.withCtx(()=>t[25]||(t[25]=[e.createTextVNode("JSON")])),_:1}),e.createVNode(B,{value:"multipart/form-data"},{default:e.withCtx(()=>t[26]||(t[26]=[e.createTextVNode("Form Data")])),_:1}),e.createVNode(B,{value:"text/plain"},{default:e.withCtx(()=>t[27]||(t[27]=[e.createTextVNode("Raw")])),_:1})]),_:1},8,["value"]),w.value==="application/json"?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createVNode(U,{value:x.value,"onUpdate:value":t[10]||(t[10]=p=>x.value=p),rows:6,placeholder:"请输入 JSON 数据",onInput:ne},null,8,["value"]),k.value?(e.openBlock(),e.createBlock(pe,{key:0,type:"error",message:k.value,banner:"",style:{"margin-bottom":"8px"}},null,8,["message"])):e.createCommentVNode("",!0)],64)):e.createCommentVNode("",!0),w.value==="multipart/form-data"?(e.openBlock(),e.createBlock(g,{key:1,modelValue:_.value,"onUpdate:modelValue":t[11]||(t[11]=p=>_.value=p),"add-button-text":"添加表单字段","show-preview":!1},null,8,["modelValue"])):e.createCommentVNode("",!0),w.value==="text/plain"?(e.openBlock(),e.createBlock(U,{key:2,value:N.value,"onUpdate:value":t[12]||(t[12]=p=>N.value=p),rows:6,placeholder:"请输入原始数据"},null,8,["value"])):e.createCommentVNode("",!0),e.createElementVNode("pre",null,e.toDisplayString(le.value),1)]),_:1})):e.createCommentVNode("",!0)]),_:1})])}}}),[["__scopeId","data-v-c9f65487"]]);async function Q(o,y="xhr"){return y==="fetch"?Y(o):Z(o)}async function Y(o){var m;const y=o.params.filter(a=>!!a.key).map(a=>`${a.key}=${encodeURIComponent(a.value)}`).join("&"),d=`${o.url}${o.path}${y?"?"+y:""}`,n=new Headers;o.headers.forEach(a=>{a.key&&n.append(a.key,a.value)});const r={method:o.method,headers:n,credentials:"omit"};if(o.auth.type==="Basic"&&o.auth.username&&o.auth.password){const a=btoa(`${o.auth.username}:${o.auth.password}`);n.append("Authorization",`Basic ${a}`)}else o.auth.type==="Bearer"&&o.auth.token&&n.append("Authorization",`Bearer ${o.auth.token}`);if(o.method==="POST"||o.method==="PUT")switch(o.body.type){case"application/json":r.body=o.body.json||"",n.set("Content-Type","application/json; charset=utf-8");break;case"multipart/form-data":const a=new FormData;(m=o.body.formData)==null||m.forEach(u=>{u.key&&a.append(u.key,u.value)}),r.body=a;break;case"text/plain":r.body=o.body.raw||"",n.set("Content-Type","text/plain; charset=utf-8");break}try{const a=await fetch(d,r),u={};a.headers.forEach((c,f)=>{u[f.toLowerCase()]=c});let i="";if((u["content-type"]||"").startsWith("application/json"))try{const c=await a.json();i=JSON.stringify(c,null,2)}catch{i=await a.text()}else i=await a.text();return{status:a.status.toString(),headers:u,body:i}}catch{throw new Error("Request failed")}}async function Z(o){return new Promise((y,d)=>{var i;const n=new XMLHttpRequest,r=o.auth.type==="Basic"?o.auth.username:null,m=o.auth.type==="Basic"?o.auth.password:null,a=o.params.filter(s=>!!s.key).map(s=>`${s.key}=${encodeURIComponent(s.value)}`).join("&"),u=`${o.url}${o.path}${a?"?"+a:""}`;if(n.open(o.method,u,!0,r,m),o.headers.forEach(s=>{s.key&&n.setRequestHeader(s.key,s.value)}),o.auth.type==="Bearer"&&o.auth.token&&n.setRequestHeader("Authorization",`Bearer ${o.auth.token}`),o.method==="POST"||o.method==="PUT"){let s="";switch(o.body.type){case"application/json":s=o.body.json||"",n.setRequestHeader("Content-Type","application/json; charset=utf-8");break;case"multipart/form-data":const c=new FormData;(i=o.body.formData)==null||i.forEach(f=>{f.key&&c.append(f.key,f.value)}),s=c;break;case"text/plain":s=o.body.raw||"",n.setRequestHeader("Content-Type","text/plain; charset=utf-8");break}n.send(s)}else n.send();n.onload=()=>{const s=ee(n),c={status:n.status.toString(),headers:s,body:""};if((s["content-type"]||"").startsWith("application/json"))try{c.body=JSON.stringify(JSON.parse(n.responseText),null,2)}catch{c.body=n.responseText}else c.body=n.responseText;y(c)},n.onerror=()=>{d(new Error("Request failed"))}})}function ee(o){const y=o.getAllResponseHeaders().trim().split(/[\r\n]+/),d={};return y.forEach(function(n){var u;const r=n.split(": "),m=((u=r.shift())==null?void 0:u.toLowerCase())||"",a=r.join(": ");d[m]=a}),d}h.KeyValueInput=g,h.RequestForm=R,h.default=R,h.defaultRequestSchema=j,h.executeRequest=Q,Object.defineProperties(h,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
4
+ ${w.value}\r
5
+ `).join("")+`--${i}--\r
6
+ `;case"text/plain":return k.value||"-空-";default:return"-空-"}}),ue=()=>{try{V.value&&(JSON.parse(V.value),x.value="")}catch{x.value="Invalid JSON format"}},fe=()=>{try{const i=u.value,t=new URL(i);u.value=`${t.protocol}//${t.host}`,m.value=t.pathname;const w=new URLSearchParams(t.search),g=[];w.forEach((b,$)=>{g.push({key:$,value:b})}),f.value=g}catch(i){console.error("URL解析失败:",i)}},ye=e.computed(()=>{if(!u.value)return!1;try{const i=u.value,t=new URL(i);return t.search!==""||t.pathname!=="/"}catch{return!1}});return(i,t)=>{const w=e.resolveComponent("a-input"),g=e.resolveComponent("a-button"),b=e.resolveComponent("a-select-option"),$=e.resolveComponent("a-select"),T=e.resolveComponent("a-card"),B=e.resolveComponent("a-radio-button"),D=e.resolveComponent("a-radio-group"),Ve=e.resolveComponent("a-input-password"),R=e.resolveComponent("a-textarea"),xe=e.resolveComponent("a-alert"),Ne=e.resolveComponent("a-form");return e.openBlock(),e.createElementBlock("div",F,[e.createVNode(Ne,{layout:"vertical"},{default:e.withCtx(()=>[e.createVNode(T,{title:"URL配置",class:"form-section",size:"small"},{default:e.withCtx(()=>[e.createElementVNode("div",H,[e.createElementVNode("div",M,[e.createVNode(w,{value:u.value,"onUpdate:value":t[0]||(t[0]=c=>u.value=c),placeholder:"基础URL",size:"small"},null,8,["value"]),ye.value?(e.openBlock(),e.createBlock(g,{key:0,type:"primary",size:"small",onClick:fe},{default:e.withCtx(()=>t[13]||(t[13]=[e.createTextVNode("拆解")])),_:1})):e.createCommentVNode("",!0)]),e.createElementVNode("div",A,[e.createVNode($,{value:r.value,"onUpdate:value":t[1]||(t[1]=c=>r.value=c),class:"w-40",size:"small"},{default:e.withCtx(()=>[e.createVNode(b,{value:"GET"},{default:e.withCtx(()=>t[14]||(t[14]=[e.createTextVNode("GET")])),_:1}),e.createVNode(b,{value:"POST"},{default:e.withCtx(()=>t[15]||(t[15]=[e.createTextVNode("POST")])),_:1}),e.createVNode(b,{value:"PUT"},{default:e.withCtx(()=>t[16]||(t[16]=[e.createTextVNode("PUT")])),_:1}),e.createVNode(b,{value:"DELETE"},{default:e.withCtx(()=>t[17]||(t[17]=[e.createTextVNode("DELETE")])),_:1}),e.createVNode(b,{value:"OPTIONS"},{default:e.withCtx(()=>t[18]||(t[18]=[e.createTextVNode("OPTIONS")])),_:1})]),_:1},8,["value"]),e.createVNode(w,{value:m.value,"onUpdate:value":t[2]||(t[2]=c=>m.value=c),placeholder:"路径",size:"small"},null,8,["value"])])]),e.createElementVNode("div",K,[e.createVNode(E,{modelValue:f.value,"onUpdate:modelValue":t[3]||(t[3]=c=>f.value=c),"add-button-text":"添加参数","show-preview":!0,"preview-text":u.value+m.value+ce.value},null,8,["modelValue","preview-text"])])]),_:1}),e.createVNode(T,{title:"认证方案",class:"form-section",size:"small"},{default:e.withCtx(()=>[e.createVNode(D,{value:n.value,"onUpdate:value":t[4]||(t[4]=c=>n.value=c),"button-style":"solid",size:"small"},{default:e.withCtx(()=>[e.createVNode(B,{value:"none"},{default:e.withCtx(()=>t[19]||(t[19]=[e.createTextVNode("无认证")])),_:1}),e.createVNode(B,{value:"Basic"},{default:e.withCtx(()=>t[20]||(t[20]=[e.createTextVNode("Basic认证")])),_:1}),e.createVNode(B,{value:"Bearer"},{default:e.withCtx(()=>t[21]||(t[21]=[e.createTextVNode("Bearer认证")])),_:1})]),_:1},8,["value"]),n.value==="Basic"?(e.openBlock(),e.createElementBlock("div",G,[e.createElementVNode("div",W,[t[22]||(t[22]=e.createElementVNode("p",{class:"m-0 w-16"},"用户名",-1)),e.createVNode(w,{value:s.value,"onUpdate:value":t[5]||(t[5]=c=>s.value=c),size:"small"},null,8,["value"])]),e.createElementVNode("div",X,[t[23]||(t[23]=e.createElementVNode("p",{class:"m-0 w-16"},"密码",-1)),e.createVNode(Ve,{value:o.value,"onUpdate:value":t[6]||(t[6]=c=>o.value=c),size:"small"},null,8,["value"])])])):e.createCommentVNode("",!0),n.value==="Bearer"?(e.openBlock(),e.createElementBlock("div",Q,[e.createElementVNode("div",Y,[t[24]||(t[24]=e.createElementVNode("p",{class:"m-0 w-16 h-full"},"Token",-1)),e.createVNode(R,{value:p.value,"onUpdate:value":t[7]||(t[7]=c=>p.value=c),size:"small",class:"mt-1",rows:"4",placeholder:"请输入Token"},null,8,["value"])])])):e.createCommentVNode("",!0)]),_:1}),e.createVNode(T,{title:"请求头",class:"form-section",size:"small"},{default:e.withCtx(()=>[e.createVNode(E,{modelValue:h.value,"onUpdate:modelValue":t[8]||(t[8]=c=>h.value=c),"add-button-text":"添加请求头","show-preview":!1},null,8,["modelValue"])]),_:1}),r.value==="POST"||r.value==="PUT"?(e.openBlock(),e.createBlock(T,{key:0,title:"请求体",class:"form-section",size:"small"},{default:e.withCtx(()=>[e.createVNode(D,{value:N.value,"onUpdate:value":t[9]||(t[9]=c=>N.value=c),"button-style":"solid",size:"small",class:"mb-1"},{default:e.withCtx(()=>[e.createVNode(B,{value:"application/json"},{default:e.withCtx(()=>t[25]||(t[25]=[e.createTextVNode("JSON")])),_:1}),e.createVNode(B,{value:"multipart/form-data"},{default:e.withCtx(()=>t[26]||(t[26]=[e.createTextVNode("Form Data")])),_:1}),e.createVNode(B,{value:"text/plain"},{default:e.withCtx(()=>t[27]||(t[27]=[e.createTextVNode("Raw")])),_:1})]),_:1},8,["value"]),N.value==="application/json"?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createVNode(R,{value:V.value,"onUpdate:value":t[10]||(t[10]=c=>V.value=c),rows:6,placeholder:"请输入 JSON 数据",onInput:ue},null,8,["value"]),x.value?(e.openBlock(),e.createBlock(xe,{key:0,type:"error",message:x.value,banner:"",style:{"margin-bottom":"8px"}},null,8,["message"])):e.createCommentVNode("",!0)],64)):e.createCommentVNode("",!0),N.value==="multipart/form-data"?(e.openBlock(),e.createBlock(E,{key:1,modelValue:C.value,"onUpdate:modelValue":t[11]||(t[11]=c=>C.value=c),"add-button-text":"添加表单字段","show-preview":!1},null,8,["modelValue"])):e.createCommentVNode("",!0),N.value==="text/plain"?(e.openBlock(),e.createBlock(R,{key:2,value:k.value,"onUpdate:value":t[12]||(t[12]=c=>k.value=c),rows:6,placeholder:"请输入原始数据"},null,8,["value"])):e.createCommentVNode("",!0),e.createElementVNode("pre",null,e.toDisplayString(me.value),1)]),_:1})):e.createCommentVNode("",!0)]),_:1})])}}}),[["__scopeId","data-v-c9f65487"]]);async function O(a,y="xhr"){return y==="fetch"?Z(a):ee(a)}async function Z(a){var u;const y=a.params.filter(n=>!!n.key).map(n=>`${n.key}=${encodeURIComponent(n.value)}`).join("&"),d=`${a.url}${a.path}${y?"?"+y:""}`,l=new Headers;a.headers.forEach(n=>{n.key&&l.append(n.key,n.value)});const r={method:a.method,headers:l,credentials:"omit"};if(a.auth.type==="Basic"&&a.auth.username&&a.auth.password){const n=btoa(`${a.auth.username}:${a.auth.password}`);l.append("Authorization",`Basic ${n}`)}else a.auth.type==="Bearer"&&a.auth.token&&l.append("Authorization",`Bearer ${a.auth.token}`);if(a.method==="POST"||a.method==="PUT")switch(a.body.type){case"application/json":r.body=a.body.json||"",l.set("Content-Type","application/json; charset=utf-8");break;case"multipart/form-data":const n=new FormData;(u=a.body.formData)==null||u.forEach(m=>{m.key&&n.append(m.key,m.value)}),r.body=n;break;case"text/plain":r.body=a.body.raw||"",l.set("Content-Type","text/plain; charset=utf-8");break}try{const n=await fetch(d,r),m={};n.headers.forEach((p,f)=>{m[f.toLowerCase()]=p});let s="";if((m["content-type"]||"").startsWith("application/json"))try{const p=await n.json();s=JSON.stringify(p,null,2)}catch{s=await n.text()}else s=await n.text();return{status:n.status.toString(),headers:m,body:s}}catch{throw new Error("Request failed")}}async function ee(a){return new Promise((y,d)=>{var s;const l=new XMLHttpRequest,r=a.auth.type==="Basic"?a.auth.username:null,u=a.auth.type==="Basic"?a.auth.password:null,n=a.params.filter(o=>!!o.key).map(o=>`${o.key}=${encodeURIComponent(o.value)}`).join("&"),m=`${a.url}${a.path}${n?"?"+n:""}`;if(l.open(a.method,m,!0,r,u),a.headers.forEach(o=>{o.key&&l.setRequestHeader(o.key,o.value)}),a.auth.type==="Bearer"&&a.auth.token&&l.setRequestHeader("Authorization",`Bearer ${a.auth.token}`),a.method==="POST"||a.method==="PUT"){let o="";switch(a.body.type){case"application/json":o=a.body.json||"",l.setRequestHeader("Content-Type","application/json; charset=utf-8");break;case"multipart/form-data":const p=new FormData;(s=a.body.formData)==null||s.forEach(f=>{f.key&&p.append(f.key,f.value)}),o=p;break;case"text/plain":o=a.body.raw||"",l.setRequestHeader("Content-Type","text/plain; charset=utf-8");break}l.send(o)}else l.send();l.onload=()=>{const o=te(l),p={status:l.status.toString(),headers:o,body:""};if((o["content-type"]||"").startsWith("application/json"))try{p.body=JSON.stringify(JSON.parse(l.responseText),null,2)}catch{p.body=l.responseText}else p.body=l.responseText;y(p)},l.onerror=()=>{d(new Error("Request failed"))}})}function te(a){const y=a.getAllResponseHeaders().trim().split(/[\r\n]+/),d={};return y.forEach(function(l){var m;const r=l.split(": "),u=((m=r.shift())==null?void 0:m.toLowerCase())||"",n=r.join(": ");d[u]=n}),d}const oe={style:{display:"flex","align-items":"center",gap:"8px","margin-bottom":"8px"}},ae={class:"flex flex-col gap-2"},le={class:"flex flex-col gap-1"},ne={key:0,class:"border border-solid border-gray-300 w-full"},re={class:"border border-gray-300"},se={class:"border border-gray-300"},de={key:1},ie=S(e.defineComponent({__name:"ResponseSection",props:{modelValue:{}},setup(a){const y=a,d=e.ref("xhr"),l=e.ref({status:"",headers:{},body:"",timing:0}),r=e.ref(""),u=s=>{const o=Number(s);return o>=200&&o<300?"success":o>=300&&o<400?"warning":o>=400&&o<500||o>=500?"error":"default"},n=s=>s instanceof Error?s.message:"请求失败",m=async()=>{r.value="";const s=Date.now();try{l.value=await O(y.modelValue,d.value),l.value.timing=Date.now()-s}catch(o){r.value=n(o),l.value={status:"Error",headers:{},body:o instanceof Error?o.message:"Request failed",timing:Date.now()-s}}};return(s,o)=>{const p=e.resolveComponent("a-radio-button"),f=e.resolveComponent("a-radio-group"),h=e.resolveComponent("a-button"),C=e.resolveComponent("a-alert"),N=e.resolveComponent("a-tag"),V=e.resolveComponent("a-textarea"),k=e.resolveComponent("a-card");return e.openBlock(),e.createBlock(k,{title:"响应",class:"form-section",size:"small"},{default:e.withCtx(()=>[e.createElementVNode("div",oe,[e.createVNode(f,{value:d.value,"onUpdate:value":o[0]||(o[0]=x=>d.value=x),"button-style":"solid",size:"small"},{default:e.withCtx(()=>[e.createVNode(p,{value:"xhr"},{default:e.withCtx(()=>o[2]||(o[2]=[e.createTextVNode("XMLHttpRequest")])),_:1}),e.createVNode(p,{value:"fetch"},{default:e.withCtx(()=>o[3]||(o[3]=[e.createTextVNode("Fetch")])),_:1})]),_:1},8,["value"]),e.createVNode(h,{type:"primary",onClick:m,size:"small"},{default:e.withCtx(()=>o[4]||(o[4]=[e.createTextVNode("发送")])),_:1})]),r.value?(e.openBlock(),e.createBlock(C,{key:0,message:r.value,type:"error","show-icon":"",style:{"margin-bottom":"8px"}},null,8,["message"])):e.createCommentVNode("",!0),e.createElementVNode("div",ae,[o[7]||(o[7]=e.createElementVNode("div",{class:"text-sm font-bold"},"基本信息",-1)),e.createElementVNode("div",le,[e.createElementVNode("div",null,[o[5]||(o[5]=e.createElementVNode("span",null,"状态码:",-1)),e.createVNode(N,{color:u(l.value.status)},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(l.value.status),1)]),_:1},8,["color"])]),e.createElementVNode("div",null,[o[6]||(o[6]=e.createElementVNode("span",null,"耗时:",-1)),e.createElementVNode("span",null,e.toDisplayString(l.value.timing?`${l.value.timing}ms`:"-"),1)])]),o[8]||(o[8]=e.createElementVNode("div",{class:"text-sm font-bold"},"响应头",-1)),Object.keys(l.value.headers).length>0?(e.openBlock(),e.createElementBlock("table",ne,[e.createElementVNode("tbody",null,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(Object.entries(l.value.headers),([x,U])=>(e.openBlock(),e.createElementBlock("tr",{key:x},[e.createElementVNode("td",re,e.toDisplayString(x),1),e.createElementVNode("td",se,e.toDisplayString(U),1)]))),128))])])):(e.openBlock(),e.createElementBlock("p",de,"无响应头")),o[9]||(o[9]=e.createElementVNode("div",{class:"text-sm font-bold"},"响应体",-1)),e.createVNode(V,{value:l.value.body,"onUpdate:value":o[1]||(o[1]=x=>l.value.body=x),rows:5,readonly:"",style:{width:"100%"},size:"small"},null,8,["value"])])]),_:1})}}}),[["__scopeId","data-v-1dd2b288"]]);_.KeyValueInput=E,_.RequestForm=q,_.ResponseSection=ie,_.default=q,_.defaultRequestSchema=j,_.executeRequest=O,Object.defineProperties(_,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "vue-api-request-builder",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.umd.js",
6
6
  "module": "dist/index.es.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
- "dist",
10
- "lib"
9
+ "dist"
11
10
  ],
12
11
  "scripts": {
13
12
  "dev": "vite",
@@ -21,7 +20,8 @@
21
20
  },
22
21
  "dependencies": {
23
22
  "ant-design-vue": "4.x",
24
- "vue": "^3.5.13"
23
+ "vue": "^3.5.13",
24
+ "vue-api-request-builder": "^0.2.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^20.0.0",
@@ -1 +0,0 @@
1
- .key-value-input[data-v-1e5ef051]{width:100%}.empty-state[data-v-1e5ef051]{color:#999;text-align:center;padding:8px 0}.key-value-input[data-v-1e5ef051] .ant-typography pre{background-color:#f5f5f5;padding:4px 8px;border-radius:4px;margin:0;width:100%;overflow-x:auto}.key-value-input[data-v-1e5ef051] .ant-space-item{flex:1}.key-value-input[data-v-1e5ef051] .ant-space-item:first-child{flex:0 0 100px}.key-value-input[data-v-1e5ef051] .ant-space-item:nth-child(2){flex:0 0 200px}.key-value-input[data-v-1e5ef051] .ant-list-item{padding:4px 0}[data-v-1e5ef051] .ant-typography pre{background-color:#f5f5f5;padding:8px;border-radius:4px;margin:0;width:100%;overflow-x:auto;max-height:60px;font-size:12px;line-height:1.5}.request-form[data-v-c9f65487]{margin:0;padding:0}.request-form[data-v-c9f65487] *{font-size:.9rem}.form-section[data-v-c9f65487]{margin-bottom:8px;width:100%}[data-v-c9f65487] .ant-card-head{background-color:#fafafa}[data-v-c9f65487] .ant-card-head-title{font-weight:500;padding:4px 0}[data-v-c9f65487] .ant-card-body{padding:8px 12px}[data-v-c9f65487] .ant-space{width:100%;display:flex;gap:8px;align-items:center}pre[data-v-c9f65487]{background-color:#f5f5f5;padding:8px;border-radius:4px;margin:0;width:100%;overflow-x:auto}
@@ -1,144 +0,0 @@
1
- <template>
2
- <div class="key-value-input flex flex-col gap-1">
3
- <div class="flex gap-2">
4
- <a-button type="primary" @click="addItem" class="w-40 max-w-full" size="small">
5
- {{ addButtonText }}
6
- </a-button>
7
- <a-popconfirm
8
- title="确认清空"
9
- description="确定要清空所有参数吗?"
10
- ok-text="确认"
11
- cancel-text="取消"
12
- @confirm="clearItems"
13
- >
14
- <a-button type="primary" danger class="w-40 max-w-full" size="small" :disabled="items.length === 0">
15
- 清空
16
- </a-button>
17
- </a-popconfirm>
18
- </div>
19
-
20
- <template v-if="items.length > 0">
21
- <div class="flex flex-col gap-1">
22
- <div v-for="(item, index) in items" :key="index" class="flex">
23
- <div class="flex w-full gap-1 items-center">
24
- <a-button type="primary" danger @click="removeItem(index)" class="w-16" size="small">
25
- 删除
26
- </a-button>
27
- <a-input v-model:value="item.key" placeholder="键" style="width: 60%" size="small" />
28
- <a-input v-model:value="item.value" placeholder="值" style="width: 100%" size="small" />
29
- </div>
30
- </div>
31
- </div>
32
- </template>
33
-
34
- <a-typography v-if="showPreview" class="m-0">
35
- <a-typography-paragraph class="!m-0">
36
- <pre>{{ previewText }}</pre>
37
- </a-typography-paragraph>
38
- </a-typography>
39
- </div>
40
- </template>
41
-
42
- <script setup lang="ts">
43
- import { ref, watch } from "vue";
44
-
45
- interface KeyValueItem {
46
- key: string;
47
- value: string;
48
- }
49
-
50
- interface Props {
51
- modelValue: KeyValueItem[];
52
- addButtonText?: string;
53
- showPreview?: boolean;
54
- previewText?: string;
55
- }
56
-
57
- const props = withDefaults(defineProps<Props>(), {
58
- addButtonText: "添加参数",
59
- showPreview: false,
60
- previewText: "",
61
- });
62
-
63
- const emit = defineEmits<{
64
- (e: "update:modelValue", value: KeyValueItem[]): void;
65
- }>();
66
-
67
- const items = ref<KeyValueItem[]>(props.modelValue);
68
-
69
- watch(
70
- () => props.modelValue,
71
- (newValue) => {
72
- items.value = newValue;
73
- }
74
- );
75
-
76
- watch(
77
- items,
78
- (newValue) => {
79
- emit("update:modelValue", newValue);
80
- },
81
- { deep: true }
82
- );
83
-
84
- const addItem = () => {
85
- items.value.push({ key: "", value: "" });
86
- };
87
-
88
- const removeItem = (index: number) => {
89
- items.value.splice(index, 1);
90
- };
91
-
92
- const clearItems = () => {
93
- items.value = [];
94
- };
95
- </script>
96
-
97
- <style scoped>
98
- .key-value-input {
99
- width: 100%;
100
- }
101
-
102
- .empty-state {
103
- color: #999;
104
- text-align: center;
105
- padding: 8px 0;
106
- }
107
-
108
- .key-value-input :deep(.ant-typography pre) {
109
- background-color: #f5f5f5;
110
- padding: 4px 8px;
111
- border-radius: 4px;
112
- margin: 0;
113
- width: 100%;
114
- overflow-x: auto;
115
- }
116
-
117
- .key-value-input :deep(.ant-space-item) {
118
- flex: 1;
119
- }
120
-
121
- .key-value-input :deep(.ant-space-item:first-child) {
122
- flex: 0 0 100px;
123
- }
124
-
125
- .key-value-input :deep(.ant-space-item:nth-child(2)) {
126
- flex: 0 0 200px;
127
- }
128
-
129
- .key-value-input :deep(.ant-list-item) {
130
- padding: 4px 0;
131
- }
132
-
133
- :deep(.ant-typography pre) {
134
- background-color: #f5f5f5;
135
- padding: 8px;
136
- border-radius: 4px;
137
- margin: 0;
138
- width: 100%;
139
- overflow-x: auto;
140
- max-height: 60px;
141
- font-size: 12px;
142
- line-height: 1.5;
143
- }
144
- </style>
@@ -1,388 +0,0 @@
1
- <template>
2
- <div class="request-form">
3
- <a-form layout="vertical">
4
- <!-- 请求部分 -->
5
- <a-card title="URL配置" class="form-section" size="small">
6
- <div class="flex flex-col gap-1">
7
- <div class="flex flex-row gap-1">
8
- <a-input v-model:value="url" placeholder="基础URL" size="small" />
9
- <a-button v-if="canParseUrl" type="primary" size="small" @click="parseUrl"
10
- >拆解</a-button
11
- >
12
- </div>
13
- <div class="flex flex-row gap-1">
14
- <a-select v-model:value="method" class="w-40" size="small">
15
- <a-select-option value="GET">GET</a-select-option>
16
- <a-select-option value="POST">POST</a-select-option>
17
- <a-select-option value="PUT">PUT</a-select-option>
18
- <a-select-option value="DELETE">DELETE</a-select-option>
19
- <a-select-option value="OPTIONS">OPTIONS</a-select-option>
20
- </a-select>
21
- <a-input v-model:value="path" placeholder="路径" size="small" />
22
- </div>
23
- </div>
24
-
25
- <!-- URL参数拼接部分 -->
26
- <div class="mt-1">
27
- <KeyValueInput
28
- v-model="params"
29
- add-button-text="添加参数"
30
- :show-preview="true"
31
- :preview-text="url + path + queryString"
32
- />
33
- </div>
34
- </a-card>
35
-
36
- <!-- 认证部分 -->
37
- <a-card title="认证方案" class="form-section" size="small">
38
- <a-radio-group v-model:value="auth" button-style="solid" size="small">
39
- <a-radio-button value="none">无认证</a-radio-button>
40
- <a-radio-button value="Basic">Basic认证</a-radio-button>
41
- <a-radio-button value="Bearer">Bearer认证</a-radio-button>
42
- </a-radio-group>
43
-
44
- <template v-if="auth === 'Basic'">
45
- <div class="flex flex-col gap-1 mt-2">
46
- <div class="flex flex-row gap-1 h-6">
47
- <p class="m-0 w-16">用户名</p>
48
- <a-input v-model:value="httpUser" size="small" />
49
- </div>
50
- <div class="flex flex-row gap-1">
51
- <p class="m-0 w-16">密码</p>
52
- <a-input-password v-model:value="httpPassword" size="small" />
53
- </div>
54
- </div>
55
- </template>
56
- <template v-if="auth === 'Bearer'">
57
- <div class="flex flex-col gap-1 mt-1">
58
- <div class="flex flex-row gap-1 items-center">
59
- <p class="m-0 w-16 h-full">Token</p>
60
- <a-textarea
61
- v-model:value="httpToken"
62
- size="small"
63
- class="mt-1"
64
- rows="4"
65
- placeholder="请输入Token"
66
- />
67
- </div>
68
- </div>
69
- </template>
70
- </a-card>
71
-
72
- <!-- 请求头部分 -->
73
- <a-card title="请求头" class="form-section" size="small">
74
- <KeyValueInput v-model="headers" add-button-text="添加请求头" :show-preview="false" />
75
- </a-card>
76
-
77
- <!-- 请求体部分 -->
78
- <a-card
79
- v-if="method === 'POST' || method === 'PUT'"
80
- title="请求体"
81
- class="form-section"
82
- size="small"
83
- >
84
- <a-radio-group v-model:value="contentType" button-style="solid" size="small" class="mb-1">
85
- <a-radio-button value="application/json">JSON</a-radio-button>
86
- <a-radio-button value="multipart/form-data">Form Data</a-radio-button>
87
- <a-radio-button value="text/plain">Raw</a-radio-button>
88
- </a-radio-group>
89
-
90
- <!-- JSON 输入 -->
91
- <template v-if="contentType === 'application/json'">
92
- <a-textarea
93
- v-model:value="jsonBody"
94
- :rows="6"
95
- placeholder="请输入 JSON 数据"
96
- @input="handleJsonInput"
97
- />
98
- <a-alert
99
- v-if="jsonError"
100
- type="error"
101
- :message="jsonError"
102
- banner
103
- style="margin-bottom: 8px"
104
- />
105
- </template>
106
-
107
- <!-- Form Data 输入 -->
108
- <template v-if="contentType === 'multipart/form-data'">
109
- <KeyValueInput
110
- v-model="bodyParams"
111
- add-button-text="添加表单字段"
112
- :show-preview="false"
113
- />
114
- </template>
115
-
116
- <!-- Raw 输入 -->
117
- <template v-if="contentType === 'text/plain'">
118
- <a-textarea v-model:value="rawBody" :rows="6" placeholder="请输入原始数据" />
119
- </template>
120
-
121
- <!-- 预览部分 -->
122
- <pre>{{ requestBodyPreview }}</pre>
123
- </a-card>
124
- </a-form>
125
- </div>
126
- </template>
127
-
128
- <script setup lang="ts">
129
- import { ref, computed, watch } from "vue";
130
- import KeyValueInput from "./KeyValueInput.vue";
131
- import type { RequestSchema, KeyValuePair } from "../types/request";
132
- import { defaultRequestSchema } from "../types/request";
133
-
134
- interface Props {
135
- modelValue?: RequestSchema;
136
- }
137
-
138
- const props = withDefaults(defineProps<Props>(), {
139
- modelValue: () => defaultRequestSchema,
140
- });
141
-
142
- const emit = defineEmits<{
143
- (e: "update:modelValue", value: RequestSchema): void;
144
- }>();
145
-
146
- // 响应式状态
147
- const method = ref(props.modelValue.method);
148
- const url = ref(props.modelValue.url);
149
- const auth = ref(props.modelValue.auth.type);
150
- const path = ref(props.modelValue.path);
151
- const httpUser = ref(props.modelValue.auth.username || "");
152
- const httpPassword = ref(props.modelValue.auth.password || "");
153
- const httpToken = ref(props.modelValue.auth.token || "");
154
- const params = ref<KeyValuePair[]>(props.modelValue.params);
155
- const headers = ref<KeyValuePair[]>(props.modelValue.headers);
156
- const bodyParams = ref<KeyValuePair[]>(props.modelValue.body.formData || []);
157
- const contentType = ref(props.modelValue.body.type);
158
- const jsonBody = ref(props.modelValue.body.json || "");
159
- const rawBody = ref(props.modelValue.body.raw || "");
160
- const jsonError = ref("");
161
-
162
- // 创建一个防抖函数
163
- function debounce<T extends (...args: any[]) => any>(fn: T, delay: number) {
164
- let timeoutId: ReturnType<typeof setTimeout>;
165
- return function (this: any, ...args: Parameters<T>) {
166
- clearTimeout(timeoutId);
167
- timeoutId = setTimeout(() => fn.apply(this, args), delay);
168
- };
169
- }
170
-
171
- // 创建一个生成schema的函数
172
- const generateSchema = () => {
173
- return {
174
- method: method.value,
175
- url: url.value,
176
- path: path.value,
177
- auth: {
178
- type: auth.value,
179
- ...(auth.value === "Basic"
180
- ? {
181
- username: httpUser.value,
182
- password: httpPassword.value,
183
- }
184
- : auth.value === "Bearer"
185
- ? {
186
- token: httpToken.value,
187
- }
188
- : {}),
189
- },
190
- params: params.value,
191
- headers: headers.value,
192
- body: {
193
- type: contentType.value,
194
- ...(contentType.value === "application/json"
195
- ? { json: jsonBody.value }
196
- : contentType.value === "multipart/form-data"
197
- ? { formData: bodyParams.value }
198
- : { raw: rawBody.value }),
199
- },
200
- };
201
- };
202
-
203
- // 创建一个更新schema的函数
204
- const updateSchema = debounce(() => {
205
- emit("update:modelValue", generateSchema());
206
- }, 100);
207
-
208
- // 监听内部状态变化
209
- watch(
210
- [
211
- method,
212
- url,
213
- path,
214
- auth,
215
- httpUser,
216
- httpPassword,
217
- httpToken,
218
- params,
219
- headers,
220
- contentType,
221
- bodyParams,
222
- jsonBody,
223
- rawBody,
224
- ],
225
- () => {
226
- updateSchema();
227
- },
228
- { deep: true }
229
- );
230
-
231
- // 监听外部变化
232
- watch(
233
- () => props.modelValue,
234
- (newValue) => {
235
- const currentSchema = generateSchema();
236
- // 防止循环更新
237
- if (JSON.stringify(newValue) === JSON.stringify(currentSchema)) {
238
- return;
239
- }
240
-
241
- method.value = newValue.method;
242
- url.value = newValue.url;
243
- path.value = newValue.path;
244
- auth.value = newValue.auth.type;
245
- httpUser.value = newValue.auth.username || "";
246
- httpPassword.value = newValue.auth.password || "";
247
- httpToken.value = newValue.auth.token || "";
248
- params.value = newValue.params;
249
- headers.value = newValue.headers;
250
- contentType.value = newValue.body.type;
251
- bodyParams.value = newValue.body.formData || [];
252
- jsonBody.value = newValue.body.json || "";
253
- rawBody.value = newValue.body.raw || "";
254
- },
255
- { deep: true }
256
- );
257
-
258
- // 计算属性
259
- const queryString = computed(() => {
260
- const result = params.value
261
- .filter((p) => !!p.key)
262
- .map((p) => p.key + "=" + encodeURIComponent(p.value))
263
- .join("&");
264
- return result === "" ? "" : "?" + result;
265
- });
266
-
267
- const requestBodyPreview = computed(() => {
268
- switch (contentType.value) {
269
- case "application/json":
270
- try {
271
- if (!jsonBody.value) {
272
- return "-空-";
273
- }
274
- return JSON.stringify(JSON.parse(jsonBody.value), null, 2);
275
- } catch (e) {
276
- return "Invalid JSON";
277
- }
278
- case "multipart/form-data":
279
- const boundary = "----WebKitFormBoundaryPreview";
280
- const formData = bodyParams.value
281
- .filter((p) => !!p.key)
282
- .map(
283
- (p) =>
284
- `--${boundary}\r\nContent-Disposition: form-data; name="${p.key}"\r\n\r\n${p.value}\r\n`
285
- )
286
- .join("");
287
- return formData + `--${boundary}--\r\n`;
288
- case "text/plain":
289
- return rawBody.value || "-空-";
290
- default:
291
- return "-空-";
292
- }
293
- });
294
-
295
- const handleJsonInput = () => {
296
- try {
297
- if (jsonBody.value) {
298
- JSON.parse(jsonBody.value);
299
- jsonError.value = "";
300
- }
301
- } catch (e) {
302
- jsonError.value = "Invalid JSON format";
303
- }
304
- };
305
-
306
- const parseUrl = () => {
307
- try {
308
- const fullUrl = url.value;
309
- const urlObj = new URL(fullUrl);
310
-
311
- // 设置基础URL
312
- url.value = `${urlObj.protocol}//${urlObj.host}`;
313
-
314
- // 设置路径
315
- path.value = urlObj.pathname;
316
-
317
- // 解析查询参数
318
- const searchParams = new URLSearchParams(urlObj.search);
319
- const newParams: KeyValuePair[] = [];
320
- searchParams.forEach((value, key) => {
321
- newParams.push({ key, value });
322
- });
323
- params.value = newParams;
324
- } catch (error) {
325
- // 如果URL解析失败,显示错误提示
326
- console.error("URL解析失败:", error);
327
- }
328
- };
329
-
330
- const canParseUrl = computed(() => {
331
- if (!url.value) return false;
332
-
333
- try {
334
- const fullUrl = url.value;
335
- const urlObj = new URL(fullUrl);
336
-
337
- // 检查是否有查询参数或路径
338
- return urlObj.search !== "" || urlObj.pathname !== "/";
339
- } catch {
340
- return false;
341
- }
342
- });
343
- </script>
344
-
345
- <style scoped>
346
- .request-form {
347
- margin: 0;
348
- padding: 0px;
349
- }
350
-
351
- .request-form * {
352
- font-size: 0.9rem;
353
- }
354
-
355
- .form-section {
356
- margin-bottom: 8px;
357
- width: 100%;
358
- }
359
-
360
- :deep(.ant-card-head) {
361
- background-color: #fafafa;
362
- }
363
-
364
- :deep(.ant-card-head-title) {
365
- font-weight: 500;
366
- padding: 4px 0;
367
- }
368
-
369
- :deep(.ant-card-body) {
370
- padding: 8px 12px;
371
- }
372
-
373
- :deep(.ant-space) {
374
- width: 100%;
375
- display: flex;
376
- gap: 8px;
377
- align-items: center;
378
- }
379
-
380
- pre {
381
- background-color: #f5f5f5;
382
- padding: 8px;
383
- border-radius: 4px;
384
- margin: 0;
385
- width: 100%;
386
- overflow-x: auto;
387
- }
388
- </style>