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.css +1 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.es.js +427 -302
- package/dist/index.umd.js +5 -5
- package/package.json +4 -4
- package/dist/vue-api-request-builder.css +0 -1
- package/lib/components/KeyValueInput.vue +0 -144
- package/lib/components/RequestForm.vue +0 -388
- package/lib/components/ResponseSection.vue +0 -144
- package/lib/index.ts +0 -17
- package/lib/types/request.ts +0 -55
- package/lib/utils/request.ts +0 -194
package/dist/index.umd.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
(function(
|
|
2
|
-
Content-Disposition: form-data; name="${
|
|
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
|
-
${
|
|
5
|
-
`).join("")+`--${
|
|
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
|
|
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>
|