| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- <!--
- * @Author: liuJie
- * @Date: 2026-01-25 22:08:04
- * @LastEditors: liuJie
- * @LastEditTime: 2026-01-25 22:24:34
- * @Describe: http设置器
- -->
- <script lang="ts" setup>
- import { watch, ref } from 'vue'
- import { Input, IconButton } from '@repo/ui'
- import { clone, isEqual } from 'lodash-es'
- const props = withDefaults(
- defineProps<{
- data: any
- }>(),
- {
- data: {}
- }
- )
- const emit = defineEmits<{
- update: [data: unknown]
- }>()
- const DEFAULT_DATA = {
- method: 'GET',
- url: '',
- headers: [],
- params: [],
- bodyType: 'json',
- body: '',
- verifySSL: true,
- timeoutConfig: {
- connect: 8,
- read: 6,
- write: 1
- },
- output: {
- body: '',
- status_code: 200,
- headers: [],
- files: []
- },
- errorConfig: {
- retry: true,
- max_retry: 3,
- retry_delay: 100
- },
- exception: 'none',
- exceptionDefaultValue: {
- body: '',
- status_code: 0,
- headers: '{}'
- }
- }
- const formData = ref(clone(DEFAULT_DATA))
- watch(
- () => props.data,
- (newVal) => {
- if (!isEqual(newVal, formData.value)) {
- formData.value = {
- ...clone(DEFAULT_DATA),
- ...(newVal || {})
- }
- }
- },
- {
- deep: true,
- immediate: true
- }
- )
- const methodOptions = [
- { label: 'GET', value: 'GET' },
- { label: 'POST', value: 'POST' },
- { label: 'PUT', value: 'PUT' },
- { label: 'DELETE', value: 'DELETE' },
- { label: 'PATCH', value: 'PATCH' }
- ]
- const bodyTypeOptions = [
- { label: 'none', value: 'none' },
- { label: 'form-data', value: 'form-data' },
- { label: 'x-www-form-urlencoded', value: 'x-www-form-urlencoded' },
- { label: 'raw', value: 'raw' },
- { label: 'json', value: 'json' },
- { label: 'binary', value: 'binary' }
- ]
- const exceptionOptions = [
- { label: '无', value: 'none' },
- { label: '默认值', value: 'default_value' },
- { label: '异常分支', value: 'exception_branch' }
- ]
- const headers = ref([{ key: '', value: '' }])
- const params = ref([{ key: '', value: '' }])
- const body = ref<string | Record<string, any>[]>('')
- watch(
- () => formData.value,
- (value) => {
- emit('update', value)
- },
- { deep: true }
- )
- watch(
- () => headers.value,
- (val) => {
- formData.value.headers = val.filter((item) => item.key && item.value)
- }
- )
- watch(
- () => params.value,
- (val) => {
- formData.value.params = val.filter((item) => item.key && item.value)
- }
- )
- watch(
- () => body.value,
- (val) => {
- if (typeof val === 'object') {
- val = val.filter((item) => item.key)
- }
- formData.value.body = val
- }
- )
- const handleAddHeader = (index: number) => {
- if (index === headers.value.length - 1) {
- headers.value.push({ key: '', value: '' })
- }
- }
- const handleAddParam = (index: number) => {
- if (index === params.value.length - 1) {
- params.value.push({ key: '', value: '' })
- }
- }
- const handleDeleteHeader = (index: number) => {
- headers.value.splice(index, 1)
- if (headers.value.length === 0) {
- headers.value.push({ key: '', value: '' })
- }
- }
- const handleDeleteParam = (index: number) => {
- params.value.splice(index, 1)
- if (params.value.length === 0) {
- params.value.push({ key: '', value: '' })
- }
- }
- const formDataItem = { key: '', value: '', type: 'text' }
- const defaultItem = { key: '', value: '' }
- const handleChangeBodyType = (type: string) => {
- if (['form-data', 'x-www-form-urlencoded'].includes(type)) {
- const item = type === 'form-data' ? formDataItem : defaultItem
- body.value = [{ ...item }]
- formData.value.body = [{ ...item }]
- } else {
- body.value = ''
- formData.value.body = ''
- }
- }
- const handleAddBody = (index: number) => {
- if (index !== body.value.length - 1) return
- const item = formData.value.bodyType === 'form-data' ? formDataItem : defaultItem
- if (typeof body.value === 'string') return
- body.value = [...body.value, { ...item }]
- }
- const handleDeleteBody = (index: number) => {
- if (typeof body.value === 'string') return
- body.value.splice(index, 1)
- if (body.value.length === 0) {
- const item = formData.value.bodyType === 'form-data' ? formDataItem : defaultItem
- body.value = [{ ...item }]
- }
- }
- </script>
- <template>
- <el-scrollbar class="w-full box-border p-12px">
- <el-form label-width="50px">
- <el-form-item label="API" label-position="top">
- <div class="w-full flex gap-8px">
- <el-select
- style="width: 100px"
- :options="methodOptions"
- v-model="formData.method"
- placeholder="请选择"
- >
- </el-select>
- <el-input class="flex-1" v-model="formData.url" placeholder="URL..."></el-input>
- </div>
- </el-form-item>
- <el-form-item label="HEADERS" label-position="top">
- <el-table :data="headers" border>
- <el-table-column align="center" prop="key" label="键">
- <template #default="{ row }">
- <Input v-model="row.key" variant="borderless" placeholder="请输入" />
- </template>
- </el-table-column>
- <el-table-column align="center" prop="value" label="值">
- <template #default="{ row, $index }">
- <div class="relative">
- <Input
- v-model="row.value"
- variant="borderless"
- placeholder="请输入"
- @focus="handleAddHeader($index)"
- />
- <IconButton
- class="absolute right-0 top-5px"
- icon="ep:delete"
- link
- @click="handleDeleteHeader($index)"
- />
- </div>
- </template>
- </el-table-column>
- </el-table>
- </el-form-item>
- <el-form-item label="PARAMS" label-position="top">
- <el-table :data="params" border>
- <el-table-column align="center" prop="key" label="键">
- <template #default="{ row }">
- <Input v-model="row.key" variant="borderless" placeholder="请输入" />
- </template>
- </el-table-column>
- <el-table-column align="center" prop="value" label="值">
- <template #default="{ row, $index }">
- <div class="relative">
- <Input
- v-model="row.value"
- variant="borderless"
- placeholder="请输入"
- @focus="handleAddParam($index)"
- />
- <IconButton
- class="absolute right-0 top-5px"
- icon="ep:delete"
- link
- @click="handleDeleteParam($index)"
- />
- </div>
- </template>
- </el-table-column>
- </el-table>
- </el-form-item>
- <el-form-item label="BODY" label-position="top">
- <el-radio-group v-model="formData.bodyType" @change="handleChangeBodyType">
- <el-radio v-for="type in bodyTypeOptions" :key="type.value" :value="type.value">{{
- type.label
- }}</el-radio>
- </el-radio-group>
- </el-form-item>
- <div
- v-if="formData.bodyType === 'form-data' || formData.bodyType === 'x-www-form-urlencoded'"
- class="mb-12px"
- >
- <el-table :data="body" border>
- <el-table-column align="center" prop="key" label="键">
- <template #default="{ row }">
- <Input v-model="row.key" variant="borderless" placeholder="请输入" />
- </template>
- </el-table-column>
- <el-table-column
- v-if="formData.bodyType === 'form-data'"
- align="center"
- prop="type"
- label="类型"
- >
- <template #default="{ row }">
- <el-select
- v-model="row.type"
- placeholder="请选择"
- :options="[
- { label: 'text', value: 'text' },
- { label: 'file', value: 'file' }
- ]"
- />
- </template>
- </el-table-column>
- <el-table-column align="center" prop="value" label="值">
- <template #default="{ row, $index }">
- <div class="relative">
- <Input
- v-model="row.value"
- variant="borderless"
- placeholder="请输入"
- @focus="handleAddBody($index)"
- />
- <IconButton
- class="absolute right-0 top-5px"
- icon="ep:delete"
- link
- @click="handleDeleteBody($index)"
- />
- </div>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <div v-if="['json', 'raw', 'binary'].includes(formData.bodyType)" class="mb-12px">
- <el-input
- v-model="formData.body"
- :type="formData.bodyType != 'binary' ? 'textarea' : ''"
- :placeholder="formData.bodyType != 'binary' ? '请输入' : '请输入变量'"
- :autosize="{ minRows: 5, maxRows: 10 }"
- />
- </div>
- <el-form-item label="验证SSL证书" label-width="120px" label-position="left">
- <div class="w-full text-right">
- <el-switch v-model="formData.verifySSL"></el-switch>
- </div>
- </el-form-item>
- <el-collapse>
- <el-collapse-item title="超时设置" name="1">
- <el-form-item label="连接超时" label-width="120px" label-position="top">
- <el-input-number
- v-model="formData.timeoutConfig.connect"
- :min="1"
- :max="10"
- controls-position="right"
- style="width: 100%"
- suffix="s"
- placeholder="请输入连接超时"
- ></el-input-number>
- </el-form-item>
- <el-form-item label="读取超时" label-width="120px" label-position="top">
- <el-input-number
- v-model="formData.timeoutConfig.read"
- :min="1"
- :max="10"
- controls-position="right"
- style="width: 100%"
- suffix="s"
- placeholder="请输入连接超时"
- ></el-input-number>
- </el-form-item>
- <el-form-item label="写入超时" label-width="120px" label-position="top">
- <el-input-number
- v-model="formData.timeoutConfig.write"
- :min="1"
- :max="10"
- controls-position="right"
- style="width: 100%"
- suffix="s"
- placeholder="请输入连接超时"
- ></el-input-number>
- </el-form-item>
- </el-collapse-item>
- <el-collapse-item title="输出变量" name="2">
- <ul>
- <li>
- <div>
- <span class="text-#333">body</span>
- <span class="text-#999 ml-8px">string</span>
- </div>
- <div class="text-#666">响应内容</div>
- </li>
- <li>
- <div>
- <span class="text-#333">status_code</span>
- <span class="text-#999 ml-8px">number</span>
- </div>
- <div class="text-#666">响应状态码</div>
- </li>
- <li>
- <div>
- <span class="text-#333">headers</span>
- <span class="text-#999 ml-8px">object</span>
- </div>
- <div class="text-#666">响应头列表JSON</div>
- </li>
- <li>
- <div>
- <span class="text-#333">files</span>
- <span class="text-#999 ml-8px">Array[File]</span>
- </div>
- <div class="text-#666">文件列表</div>
- </li>
- </ul>
- </el-collapse-item>
- </el-collapse>
- <el-form-item label="失败时重试" label-width="120px" label-position="left">
- <div class="w-full text-right">
- <el-switch v-model="formData.errorConfig.retry"></el-switch>
- </div>
- </el-form-item>
- <div v-if="formData.errorConfig.retry" class="flex items-center mb-12px">
- <div class="w-150px text-12px text-gray-500">最大重试次数</div>
- <div class="flex-1 flex items-center gap-8px">
- <el-slider
- v-model="formData.errorConfig.max_retry"
- :max="10"
- :min="1"
- :step="1"
- style="flex: 1"
- ></el-slider>
- <el-input-number
- v-model="formData.errorConfig.max_retry"
- :min="1"
- :max="10"
- controls-position="right"
- style="flex: 1"
- ></el-input-number>
- </div>
- </div>
- <div v-if="formData.errorConfig.retry" class="flex items-center mb-12px">
- <div class="w-150px text-12px text-gray-500">重试次数间隔时间(ms)</div>
- <div class="flex-1 flex items-center gap-8px">
- <el-slider
- v-model="formData.errorConfig.retry_delay"
- :max="5000"
- :min="100"
- :step="1"
- style="flex: 1"
- ></el-slider>
- <el-input-number
- v-model="formData.errorConfig.retry_delay"
- :max="5000"
- :min="100"
- controls-position="right"
- style="flex: 1"
- ></el-input-number>
- </div>
- </div>
- <el-form-item label="异常处理" label-width="90px" label-position="left">
- <div class="w-full text-right">
- <el-select
- v-model="formData.exception"
- :options="exceptionOptions"
- style="width: 120px"
- />
- </div>
- </el-form-item>
- <div v-if="formData.exception === 'default_value'">
- <div class="text-12px text-gray-500">当发生异常时,指定默认数据输出</div>
- <el-form-item label="body" label-position="top">
- <el-input v-model="formData.exceptionDefaultValue.body" type="textarea" rows="3" />
- </el-form-item>
- <el-form-item label="status_code" label-position="top">
- <el-input-number
- controls-position="right"
- v-model="formData.exceptionDefaultValue.status_code"
- />
- </el-form-item>
- <el-form-item label="headers" label-position="top">
- <el-input v-model="formData.exceptionDefaultValue.headers" type="textarea" rows="5" />
- </el-form-item>
- </div>
- <div v-if="formData.exception === 'exception_branch'">
- <div class="text-12px text-gray-500">请在画布定义异常处理逻辑!</div>
- </div>
- </el-form>
- </el-scrollbar>
- </template>
|