mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-07 21:46:12 +08:00
完成axios可视化
This commit is contained in:
parent
7c1ca78ec0
commit
d135f5b7a8
@ -93,7 +93,10 @@ module.exports = configure(function (ctx) {
|
||||
],
|
||||
},
|
||||
]);
|
||||
chain.resolve.alias.set("plugins", path.join(__dirname, "./src/plugins"));
|
||||
chain.resolve.alias.set(
|
||||
"plugins",
|
||||
path.join(__dirname, "./src/plugins")
|
||||
);
|
||||
chain.resolve.alias.set("js", path.join(__dirname, "./src/js"));
|
||||
},
|
||||
extendWebpack(cfg) {
|
||||
@ -194,7 +197,6 @@ module.exports = configure(function (ctx) {
|
||||
orientation: "portrait",
|
||||
background_color: "#ffffff",
|
||||
theme_color: "#027be3",
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
|
60
src/components/editor/composer/BorderLabel.vue
Normal file
60
src/components/editor/composer/BorderLabel.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="border-label" :data-label="label">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "BorderLabel",
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.border-label {
|
||||
width: 100%;
|
||||
border: 1px solid var(--border-color, rgba(0, 0, 0, 0.1));
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.border-label::before {
|
||||
content: attr(data-label);
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
left: 16px;
|
||||
background: #fff;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
padding: 0 8px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.border-label::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.body--dark .border-label {
|
||||
--border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.body--dark .border-label::before {
|
||||
background: #303133;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
</style>
|
@ -202,7 +202,7 @@ export default defineComponent({
|
||||
.command-composer :deep(.q-field--filled .q-field__control),
|
||||
.command-composer :deep(.q-field--filled .q-field__control > *),
|
||||
.command-composer
|
||||
:deep(.q-field--filled.q-select--with-input .q-field__native) {
|
||||
:deep(.q-field--filled:not(.q-field--labeled) .q-field__native) {
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
max-height: 36px !important;
|
||||
@ -236,9 +236,10 @@ export default defineComponent({
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* 输入框背景颜色 */
|
||||
/* 输入框背景颜色及内边距 */
|
||||
.command-composer :deep(.q-field--filled .q-field__control) {
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
/* 输入框聚焦时的背景颜色 */
|
||||
|
@ -88,12 +88,8 @@
|
||||
<template v-else-if="command.hasAxiosEditor">
|
||||
<AxiosConfigEditor v-model="argvLocal" class="col" />
|
||||
</template>
|
||||
<!-- Fetch编辑器 -->
|
||||
<template v-else-if="command.hasFetchEditor">
|
||||
<FetchConfigEditor v-model="argvLocal" class="col" />
|
||||
</template>
|
||||
<!-- 普通参数输入 -->
|
||||
<template v-else>
|
||||
<template v-else-if="!command.hasNoArgs">
|
||||
<VariableInput
|
||||
v-model="argvLocal"
|
||||
:label="placeholder"
|
||||
@ -115,7 +111,6 @@ import KeyEditor from "./KeyEditor.vue";
|
||||
import UBrowserEditor from "./ubrowser/UBrowserEditor.vue";
|
||||
import VariableInput from "./VariableInput.vue";
|
||||
import AxiosConfigEditor from "./http/AxiosConfigEditor.vue";
|
||||
import FetchConfigEditor from "./http/FetchConfigEditor.vue";
|
||||
import { validateVariableName } from "js/common/variableValidator";
|
||||
|
||||
export default defineComponent({
|
||||
@ -125,7 +120,6 @@ export default defineComponent({
|
||||
UBrowserEditor,
|
||||
VariableInput,
|
||||
AxiosConfigEditor,
|
||||
FetchConfigEditor,
|
||||
},
|
||||
props: {
|
||||
command: {
|
||||
@ -162,7 +156,7 @@ export default defineComponent({
|
||||
},
|
||||
argvLocal: {
|
||||
get() {
|
||||
if (this.command.hasAxiosEditor || this.command.hasFetchEditor) {
|
||||
if (this.command.hasAxiosEditor) {
|
||||
// 如果是编辑现有配置
|
||||
if (
|
||||
this.command.argv &&
|
||||
@ -183,12 +177,11 @@ export default defineComponent({
|
||||
set(value) {
|
||||
const updatedCommand = {
|
||||
...this.command,
|
||||
argv:
|
||||
this.command.hasAxiosEditor || this.command.hasFetchEditor
|
||||
? typeof value === "string"
|
||||
? value
|
||||
: JSON.stringify(value)
|
||||
: value,
|
||||
argv: this.command.hasAxiosEditor
|
||||
? typeof value === "string"
|
||||
? value
|
||||
: JSON.stringify(value)
|
||||
: value,
|
||||
};
|
||||
this.$emit("update:command", updatedCommand);
|
||||
},
|
||||
|
261
src/components/editor/composer/DictEditor.vue
Normal file
261
src/components/editor/composer/DictEditor.vue
Normal file
@ -0,0 +1,261 @@
|
||||
<template>
|
||||
<div class="dict-editor">
|
||||
<div
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
class="row q-col-gutter-sm items-center"
|
||||
>
|
||||
<div class="col-4">
|
||||
<q-select
|
||||
v-if="options?.items"
|
||||
:model-value="item.key"
|
||||
:options="options.items"
|
||||
label="名称"
|
||||
dense
|
||||
filled
|
||||
use-input
|
||||
input-debounce="0"
|
||||
:hide-selected="!!inputValue"
|
||||
@filter="filterFn"
|
||||
@update:model-value="(val) => handleSelect(val, index)"
|
||||
@input-value="(val) => handleInput(val, index)"
|
||||
@blur="handleBlur"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="code" />
|
||||
</template>
|
||||
</q-select>
|
||||
<q-input
|
||||
v-else
|
||||
:model-value="item.key"
|
||||
label="名称"
|
||||
dense
|
||||
filled
|
||||
@update:model-value="(val) => updateItemKey(val, index)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="code" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VariableInput
|
||||
:model-value="item.value"
|
||||
:label="item.key || '值'"
|
||||
:command="{ icon: 'code' }"
|
||||
@update:model-value="(val) => updateItemValue(val, index)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-container">
|
||||
<template v-if="items.length === 1">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="sm"
|
||||
icon="add"
|
||||
class="center-btn"
|
||||
@click="addItem"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="index === items.length - 1">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="sm"
|
||||
icon="remove"
|
||||
class="top-btn"
|
||||
@click="removeItem(index)"
|
||||
/>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="sm"
|
||||
icon="add"
|
||||
class="bottom-btn"
|
||||
@click="addItem"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="sm"
|
||||
icon="remove"
|
||||
class="center-btn"
|
||||
@click="removeItem(index)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "./VariableInput.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "DictEditor",
|
||||
components: {
|
||||
VariableInput,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
const modelEntries = Object.entries(this.modelValue || {});
|
||||
return {
|
||||
localItems: modelEntries.length
|
||||
? modelEntries.map(([key, value]) => ({
|
||||
key,
|
||||
value: typeof value === "string" ? value : JSON.stringify(value),
|
||||
}))
|
||||
: [{ key: "", value: "" }],
|
||||
filterOptions: this.options?.items || [],
|
||||
inputValue: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
items: {
|
||||
get() {
|
||||
return this.localItems;
|
||||
},
|
||||
set(newItems) {
|
||||
this.localItems = newItems;
|
||||
const dict = {};
|
||||
newItems.forEach((item) => {
|
||||
if (item.key && item.value) {
|
||||
dict[item.key] = item.value;
|
||||
}
|
||||
});
|
||||
this.$emit("update:modelValue", dict);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addItem() {
|
||||
this.items = [...this.items, { key: "", value: "" }];
|
||||
},
|
||||
removeItem(index) {
|
||||
const newItems = [...this.items];
|
||||
newItems.splice(index, 1);
|
||||
if (newItems.length === 0) {
|
||||
newItems.push({ key: "", value: "" });
|
||||
}
|
||||
this.items = newItems;
|
||||
const dict = {};
|
||||
newItems.forEach((item) => {
|
||||
if (item.key && item.value) {
|
||||
dict[item.key] = item.value;
|
||||
}
|
||||
});
|
||||
this.$emit("update:modelValue", dict);
|
||||
},
|
||||
updateItemKey(val, index) {
|
||||
const newItems = [...this.items];
|
||||
newItems[index].key = val;
|
||||
this.items = newItems;
|
||||
},
|
||||
updateItemValue(val, index) {
|
||||
const newItems = [...this.items];
|
||||
newItems[index].value = val;
|
||||
this.items = newItems;
|
||||
},
|
||||
handleInput(val, index) {
|
||||
this.inputValue = val;
|
||||
if (val && !this.filterOptions.includes(val)) {
|
||||
const newItems = [...this.items];
|
||||
newItems[index].key = val;
|
||||
this.items = newItems;
|
||||
}
|
||||
},
|
||||
handleSelect(val, index) {
|
||||
this.inputValue = "";
|
||||
const newItems = [...this.items];
|
||||
newItems[index].key = val;
|
||||
this.items = newItems;
|
||||
},
|
||||
handleBlur() {
|
||||
this.inputValue = "";
|
||||
},
|
||||
filterFn(val, update) {
|
||||
if (!this.options?.items) return;
|
||||
|
||||
update(() => {
|
||||
if (val === "") {
|
||||
this.filterOptions = this.options.items;
|
||||
} else {
|
||||
const needle = val.toLowerCase();
|
||||
this.filterOptions = this.options.items.filter(
|
||||
(v) => v.toLowerCase().indexOf(needle) > -1
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dict-editor {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 防止输入框换行 */
|
||||
:deep(.q-field__native) {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
position: relative;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-container .q-btn {
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
min-height: 16px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.btn-container .center-btn {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn-container .top-btn {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.btn-container .bottom-btn {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.q-btn .q-icon) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.q-btn.q-btn--dense) {
|
||||
padding: 0;
|
||||
min-height: 16px;
|
||||
}
|
||||
</style>
|
@ -101,7 +101,6 @@
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
round
|
||||
icon="keyboard_arrow_up"
|
||||
size="xs"
|
||||
class="number-btn"
|
||||
@ -110,7 +109,6 @@
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
round
|
||||
icon="keyboard_arrow_down"
|
||||
size="xs"
|
||||
class="number-btn"
|
||||
@ -262,7 +260,7 @@ export default defineComponent({
|
||||
.string-toggle {
|
||||
min-width: 24px;
|
||||
padding: 4px;
|
||||
opacity: 0.8;
|
||||
opacity: 0.6;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
@ -348,6 +346,7 @@ export default defineComponent({
|
||||
.number-controls {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
width: 32px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
@ -37,86 +37,185 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Headers -->
|
||||
<div class="col-12">
|
||||
<HeaderEditor
|
||||
:headers="localConfig.headers"
|
||||
@input="
|
||||
(val) => {
|
||||
localConfig.headers = val;
|
||||
updateConfig();
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 请求参数 -->
|
||||
<div class="col-12">
|
||||
<VariableInput
|
||||
v-model="localConfig.params"
|
||||
label="URL参数"
|
||||
:command="{ icon: 'link' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 请求体 -->
|
||||
<div class="col-12">
|
||||
<VariableInput
|
||||
v-model="localConfig.data"
|
||||
label="请求体"
|
||||
:command="{ icon: 'data_object' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 超时设置 -->
|
||||
<div class="col-12">
|
||||
<VariableInput
|
||||
v-model="localConfig.timeout"
|
||||
label="超时时间(ms)"
|
||||
:command="{ icon: 'timer', inputType: 'number' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 其他选项 -->
|
||||
<!-- 响应设置 -->
|
||||
<div class="col-12">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-6">
|
||||
<q-checkbox
|
||||
v-model="localConfig.withCredentials"
|
||||
label="发送凭证"
|
||||
<div class="col-3">
|
||||
<q-select
|
||||
v-model="localConfig.responseType"
|
||||
filled
|
||||
dense
|
||||
emit-value
|
||||
map-options
|
||||
:options="['json', 'text', 'blob', 'arraybuffer']"
|
||||
label="响应类型"
|
||||
@update:model-value="updateConfig"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="data_object" />
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<VariableInput
|
||||
v-model="localConfig.maxRedirects"
|
||||
label="最大重定向次数"
|
||||
:command="{ icon: 'repeat', inputType: 'number' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<q-checkbox
|
||||
v-model="localConfig.decompress"
|
||||
label="自动解压"
|
||||
<div class="col">
|
||||
<VariableInput
|
||||
v-model="localConfig.timeout"
|
||||
label="超时时间(ms)"
|
||||
:command="{ icon: 'timer', inputType: 'number' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 响应类型 -->
|
||||
<div class="col-12">
|
||||
<q-select
|
||||
v-model="localConfig.responseType"
|
||||
:options="['json', 'text', 'blob', 'arraybuffer']"
|
||||
label="响应类型"
|
||||
dense
|
||||
filled
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="updateConfig"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="data_object" />
|
||||
</template>
|
||||
</q-select>
|
||||
<!-- Headers -->
|
||||
<!-- Content-Type -->
|
||||
<q-select
|
||||
v-model="localConfig.headers['Content-Type']"
|
||||
label="Content-Type"
|
||||
dense
|
||||
filled
|
||||
emit-value
|
||||
map-options
|
||||
:options="contentTypes"
|
||||
class="col-12"
|
||||
@update:model-value="updateConfig"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="data_object" />
|
||||
</template>
|
||||
</q-select>
|
||||
<!-- User-Agent -->
|
||||
<div class="col-12 row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
<VariableInput
|
||||
v-model="localConfig.headers['User-Agent']"
|
||||
label="User Agent"
|
||||
:command="{ icon: 'devices' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto flex items-center">
|
||||
<q-btn-dropdown flat dense dropdown-icon="menu">
|
||||
<q-list>
|
||||
<q-item
|
||||
v-for="ua in userAgentOptions"
|
||||
:key="ua.value"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="setUserAgent(ua.value)"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ ua.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Other Headers -->
|
||||
<div class="col-12" style="margin-top: -8px">
|
||||
<BorderLabel label="Headers">
|
||||
<DictEditor
|
||||
v-model="otherHeaders"
|
||||
:options="{
|
||||
items: commonHeaderOptions,
|
||||
}"
|
||||
@update:model-value="updateHeaders"
|
||||
/>
|
||||
</BorderLabel>
|
||||
</div>
|
||||
|
||||
<!-- 请求体 -->
|
||||
<div v-if="hasRequestData" class="col-12" style="margin-top: -8px">
|
||||
<BorderLabel label="请求体">
|
||||
<DictEditor
|
||||
v-model="localConfig.data"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</BorderLabel>
|
||||
</div>
|
||||
|
||||
<!-- 请求参数 -->
|
||||
<div class="col-12" style="margin-top: -8px">
|
||||
<BorderLabel label="URL参数">
|
||||
<DictEditor
|
||||
v-model="localConfig.params"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</BorderLabel>
|
||||
</div>
|
||||
|
||||
<!-- 认证信息 -->
|
||||
<div class="col-12" style="margin-top: -8px">
|
||||
<BorderLabel label="HTTP认证">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-6">
|
||||
<VariableInput
|
||||
v-model="localConfig.auth.username"
|
||||
label="用户名"
|
||||
:command="{ icon: 'person' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<VariableInput
|
||||
v-model="localConfig.auth.password"
|
||||
label="密码"
|
||||
:command="{ icon: 'password' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BorderLabel>
|
||||
</div>
|
||||
|
||||
<!-- 代理设置 -->
|
||||
<div class="col-12" style="margin-top: -8px">
|
||||
<BorderLabel label="代理设置">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-3">
|
||||
<VariableInput
|
||||
v-model="localConfig.proxy.host"
|
||||
label="主机"
|
||||
:command="{ icon: 'dns' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<VariableInput
|
||||
v-model="localConfig.proxy.port"
|
||||
label="端口"
|
||||
:command="{ icon: 'router', inputType: 'number' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<VariableInput
|
||||
v-model="localConfig.proxy.auth.username"
|
||||
label="用户名"
|
||||
:command="{ icon: 'person' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<VariableInput
|
||||
v-model="localConfig.proxy.auth.password"
|
||||
label="密码"
|
||||
:command="{ icon: 'password' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BorderLabel>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -124,14 +223,17 @@
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "../VariableInput.vue";
|
||||
import HeaderEditor from "./HeaderEditor.vue";
|
||||
import DictEditor from "../DictEditor.vue";
|
||||
import { formatJsonVariables } from "js/composer/formatString";
|
||||
import { userAgent, commonHeaders, contentTypes } from "js/options/httpHeaders";
|
||||
import BorderLabel from "../BorderLabel.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AxiosConfigEditor",
|
||||
components: {
|
||||
VariableInput,
|
||||
HeaderEditor,
|
||||
DictEditor,
|
||||
BorderLabel,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
@ -144,7 +246,6 @@ export default defineComponent({
|
||||
let initialConfig = {};
|
||||
if (typeof this.modelValue === "string") {
|
||||
try {
|
||||
// 尝试从代码字符串中提取配置对象
|
||||
const match = this.modelValue.match(
|
||||
/axios\.\w+\([^{]*({\s*[^]*})\s*\)/
|
||||
);
|
||||
@ -162,15 +263,34 @@ export default defineComponent({
|
||||
localConfig: {
|
||||
url: "",
|
||||
method: "GET",
|
||||
headers: {},
|
||||
params: "",
|
||||
data: "",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
params: {},
|
||||
data: {},
|
||||
timeout: 0,
|
||||
withCredentials: false,
|
||||
maxRedirects: 5,
|
||||
responseType: "json",
|
||||
decompress: true,
|
||||
auth: {
|
||||
username: "",
|
||||
password: "",
|
||||
},
|
||||
proxy: {
|
||||
host: "",
|
||||
port: "",
|
||||
auth: {
|
||||
username: "",
|
||||
password: "",
|
||||
},
|
||||
},
|
||||
...initialConfig,
|
||||
},
|
||||
userAgentOptions: userAgent,
|
||||
contentTypes,
|
||||
commonHeaderOptions: commonHeaders
|
||||
.filter((h) => !["User-Agent", "Content-Type"].includes(h.value))
|
||||
.map((h) => h.value),
|
||||
otherHeaders: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@ -179,72 +299,43 @@ export default defineComponent({
|
||||
([name, value]) => ({ name, value })
|
||||
);
|
||||
},
|
||||
computed: {
|
||||
hasRequestData() {
|
||||
return ["PUT", "POST", "PATCH"].includes(this.localConfig.method);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateConfig() {
|
||||
// 移除空值
|
||||
const config = { ...this.localConfig };
|
||||
Object.keys(config).forEach((key) => {
|
||||
if (
|
||||
config[key] === "" ||
|
||||
config[key] === null ||
|
||||
config[key] === undefined
|
||||
) {
|
||||
delete config[key];
|
||||
}
|
||||
});
|
||||
|
||||
// 生成代码
|
||||
const { method = "GET", url, data, ...restConfig } = config;
|
||||
const { method = "GET", url, data, ...restConfig } = this.localConfig;
|
||||
if (!url) return;
|
||||
let code = "";
|
||||
const variableFields = ["headers", "timeout", "params", "data"];
|
||||
if (method.toUpperCase() === "GET") {
|
||||
code = `axios.get(${url}${
|
||||
Object.keys(restConfig).length
|
||||
? `, ${formatJsonVariables(restConfig, variableFields)}`
|
||||
: ""
|
||||
})`;
|
||||
} else {
|
||||
const { data: reqData, ...configWithoutData } = restConfig;
|
||||
code = `axios.${method.toLowerCase()}(${url}${
|
||||
reqData ? `, ${reqData}` : ", undefined"
|
||||
}${
|
||||
Object.keys(configWithoutData).length
|
||||
? `, ${formatJsonVariables(restConfig, variableFields)}`
|
||||
: ""
|
||||
})`;
|
||||
}
|
||||
|
||||
// 这两个字段非VariableInput获取,不进行处理
|
||||
const excludeFields = ["headers.Content-Type", "responseType"];
|
||||
const configStr = Object.keys(restConfig).length
|
||||
? `, ${formatJsonVariables(restConfig, null, excludeFields)}`
|
||||
: "";
|
||||
|
||||
const code = `axios.${method.toLowerCase()}(${url}${
|
||||
this.hasRequestData ? `, ${formatJsonVariables(data)}` : ""
|
||||
}${configStr})?.data`;
|
||||
|
||||
this.$emit("update:modelValue", code);
|
||||
},
|
||||
addHeader() {
|
||||
this.headers.push({ name: "", value: "" });
|
||||
updateHeaders(headers) {
|
||||
// 保留 Content-Type 和 User-Agent
|
||||
const { "Content-Type": contentType, "User-Agent": userAgent } =
|
||||
this.localConfig.headers;
|
||||
// 重置 headers,只保留特殊字段
|
||||
this.localConfig.headers = {
|
||||
"Content-Type": contentType,
|
||||
...(userAgent ? { "User-Agent": userAgent } : {}),
|
||||
...headers,
|
||||
};
|
||||
this.updateConfig();
|
||||
},
|
||||
removeHeader(index) {
|
||||
this.headers.splice(index, 1);
|
||||
this.updateHeaders();
|
||||
},
|
||||
updateHeaders() {
|
||||
this.localConfig.headers = this.headers.reduce((acc, header) => {
|
||||
if (header.name) {
|
||||
// 如果值是变量引用(不带引号),直接使用
|
||||
if (
|
||||
header.value &&
|
||||
!header.value.startsWith('"') &&
|
||||
!header.value.endsWith('"')
|
||||
) {
|
||||
acc[header.name] = header.value;
|
||||
} else {
|
||||
// <EFBFBD><EFBFBD>则尝试解析JSON,如果失败则使用原始值
|
||||
try {
|
||||
acc[header.name] = JSON.parse(header.value);
|
||||
} catch (e) {
|
||||
acc[header.name] = header.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
setUserAgent(value) {
|
||||
this.localConfig.headers["User-Agent"] = value;
|
||||
this.updateConfig();
|
||||
},
|
||||
},
|
||||
|
@ -1,276 +0,0 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<!-- 基础配置 -->
|
||||
<div class="col-12">
|
||||
<VariableInput
|
||||
v-model="localConfig.url"
|
||||
label="请求地址"
|
||||
:command="{ icon: 'link' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<q-select
|
||||
v-model="localConfig.method"
|
||||
:options="['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']"
|
||||
label="请求方法"
|
||||
dense
|
||||
filled
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="updateConfig"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="send" />
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<!-- Headers -->
|
||||
<div class="col-12">
|
||||
<HeaderEditor
|
||||
:headers="localConfig.headers"
|
||||
@input="
|
||||
(val) => {
|
||||
localConfig.headers = val;
|
||||
updateConfig();
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 请求体 -->
|
||||
<div class="col-12">
|
||||
<VariableInput
|
||||
v-model="localConfig.body"
|
||||
label="请求体"
|
||||
:command="{ icon: 'data_object' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 重定向策略 -->
|
||||
<div class="col-12">
|
||||
<q-select
|
||||
v-model="localConfig.redirect"
|
||||
:options="['follow', 'error', 'manual']"
|
||||
label="重定向策略"
|
||||
dense
|
||||
filled
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="updateConfig"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="alt_route" />
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<!-- 其他选项 -->
|
||||
<div class="col-12">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-6">
|
||||
<VariableInput
|
||||
v-model="localConfig.follow"
|
||||
label="最大重定向次数"
|
||||
:command="{ icon: 'repeat', inputType: 'number' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<VariableInput
|
||||
v-model="localConfig.timeout"
|
||||
label="超时时间(ms)"
|
||||
:command="{ icon: 'timer', inputType: 'number' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-6">
|
||||
<VariableInput
|
||||
v-model="localConfig.size"
|
||||
label="最大响应大小(bytes)"
|
||||
:command="{ icon: 'data_usage', inputType: 'number' }"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<q-checkbox
|
||||
v-model="localConfig.compress"
|
||||
label="启用压缩"
|
||||
@update:model-value="updateConfig"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "../VariableInput.vue";
|
||||
import HeaderEditor from "./HeaderEditor.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "FetchConfigEditor",
|
||||
components: {
|
||||
VariableInput,
|
||||
HeaderEditor,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [Object, String],
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
let initialConfig = {};
|
||||
if (typeof this.modelValue === "string") {
|
||||
try {
|
||||
// 尝试从代码字符串中提取配置对象
|
||||
const match = this.modelValue.match(/fetch\([^{]*({\s*[^]*})\s*\)/);
|
||||
if (match) {
|
||||
initialConfig = JSON.parse(match[1]);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse config from code string");
|
||||
}
|
||||
} else {
|
||||
initialConfig = this.modelValue;
|
||||
}
|
||||
|
||||
return {
|
||||
localConfig: {
|
||||
url: "",
|
||||
method: "GET",
|
||||
headers: {},
|
||||
body: "",
|
||||
redirect: "follow",
|
||||
follow: 20,
|
||||
timeout: 0,
|
||||
size: 0,
|
||||
compress: true,
|
||||
...initialConfig,
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 将headers对象转换为数组形式
|
||||
this.headers = Object.entries(this.localConfig.headers || {}).map(
|
||||
([name, value]) => ({ name, value })
|
||||
);
|
||||
},
|
||||
methods: {
|
||||
updateConfig() {
|
||||
// 移除空值
|
||||
const config = { ...this.localConfig };
|
||||
Object.keys(config).forEach((key) => {
|
||||
if (
|
||||
config[key] === "" ||
|
||||
config[key] === null ||
|
||||
config[key] === undefined
|
||||
) {
|
||||
delete config[key];
|
||||
}
|
||||
});
|
||||
|
||||
// 生成代码
|
||||
const { url, body, headers, ...init } = config;
|
||||
if (!url) return;
|
||||
|
||||
// 处理请求体
|
||||
if (body) {
|
||||
init.body = body;
|
||||
}
|
||||
|
||||
// 处理headers
|
||||
if (headers && Object.keys(headers).length) {
|
||||
init.headers = headers;
|
||||
}
|
||||
|
||||
const variableFields = [
|
||||
"headers",
|
||||
"body",
|
||||
"redirect",
|
||||
"follow",
|
||||
"timeout",
|
||||
"size",
|
||||
];
|
||||
const code = `fetch(${url}${
|
||||
Object.keys(init).length
|
||||
? `, ${formatJsonVariables(init, variableFields)}`
|
||||
: ""
|
||||
})`;
|
||||
|
||||
this.$emit("update:modelValue", code);
|
||||
},
|
||||
addHeader() {
|
||||
this.headers.push({ name: "", value: "" });
|
||||
},
|
||||
removeHeader(index) {
|
||||
this.headers.splice(index, 1);
|
||||
this.updateHeaders();
|
||||
},
|
||||
updateHeaders() {
|
||||
this.localConfig.headers = this.headers.reduce((acc, header) => {
|
||||
if (header.name) {
|
||||
// 如果值是变量引用(不带引号),直接使用
|
||||
if (
|
||||
header.value &&
|
||||
!header.value.startsWith('"') &&
|
||||
!header.value.endsWith('"')
|
||||
) {
|
||||
acc[header.name] = header.value;
|
||||
} else {
|
||||
// 否则尝试解析JSON,如果失败则使用原始值
|
||||
try {
|
||||
acc[header.name] = JSON.parse(header.value);
|
||||
} catch (e) {
|
||||
acc[header.name] = header.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
this.updateConfig();
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
deep: true,
|
||||
handler(newValue) {
|
||||
if (typeof newValue === "string") {
|
||||
// 如果是字符串,说明是编辑现有的配置
|
||||
try {
|
||||
const config = JSON.parse(newValue);
|
||||
this.localConfig = {
|
||||
...this.localConfig,
|
||||
...config,
|
||||
};
|
||||
this.headers = Object.entries(config.headers || {}).map(
|
||||
([name, value]) => ({ name, value })
|
||||
);
|
||||
} catch (e) {
|
||||
// 如果解析失败,保持当前状态
|
||||
}
|
||||
} else {
|
||||
this.localConfig = {
|
||||
...this.localConfig,
|
||||
...newValue,
|
||||
};
|
||||
this.headers = Object.entries(this.localConfig.headers || {}).map(
|
||||
([name, value]) => ({ name, value })
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,198 +0,0 @@
|
||||
<template>
|
||||
<div class="q-pa-sm">
|
||||
<!-- 添加新header按钮 -->
|
||||
<div class="row items-center q-gutter-sm">
|
||||
<q-select
|
||||
v-model="newHeaderField"
|
||||
:options="commonHeaders"
|
||||
label="添加常用Header"
|
||||
dense
|
||||
filled
|
||||
emit-value
|
||||
map-options
|
||||
style="width: 200px"
|
||||
@update:model-value="addCommonHeader"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="add" />
|
||||
</template>
|
||||
</q-select>
|
||||
<q-btn flat round dense icon="add" @click="addCustomHeader" />
|
||||
</div>
|
||||
|
||||
<!-- header列表 -->
|
||||
<div
|
||||
v-for="(header, index) in headersList"
|
||||
:key="index"
|
||||
class="row q-col-gutter-sm q-mt-sm items-center"
|
||||
>
|
||||
<!-- header名称 -->
|
||||
<div class="col-4">
|
||||
<q-input
|
||||
v-if="!header.isCommon"
|
||||
v-model="header.name"
|
||||
label="Header名称"
|
||||
dense
|
||||
filled
|
||||
@update:model-value="emitUpdate"
|
||||
/>
|
||||
<q-input
|
||||
v-else
|
||||
:model-value="header.name"
|
||||
label="Header名称"
|
||||
dense
|
||||
filled
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- header值 -->
|
||||
<div class="col">
|
||||
<div class="row items-center q-col-gutter-sm">
|
||||
<!-- User-Agent特殊处理 -->
|
||||
<template v-if="header.name === 'User-Agent'">
|
||||
<div class="col">
|
||||
<VariableInput
|
||||
v-model="header.value"
|
||||
label="User Agent"
|
||||
:command="{ icon: 'devices' }"
|
||||
@update:model-value="emitUpdate"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn-dropdown flat dense icon="list">
|
||||
<q-list>
|
||||
<q-item
|
||||
v-for="ua in userAgentOptions"
|
||||
:key="ua.value"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="setUserAgent(header, ua.value)"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ ua.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 其他header -->
|
||||
<template v-else>
|
||||
<div class="col">
|
||||
<VariableInput
|
||||
v-model="header.value"
|
||||
:label="header.name"
|
||||
:command="{ icon: 'code' }"
|
||||
@update:model-value="emitUpdate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 删除按钮 -->
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
icon="delete"
|
||||
@click="removeHeader(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "../VariableInput.vue";
|
||||
import { userAgent, commonHeaders } from "js/options/httpHeaders";
|
||||
|
||||
export default defineComponent({
|
||||
name: "HeaderEditor",
|
||||
components: {
|
||||
VariableInput,
|
||||
},
|
||||
props: {
|
||||
headers: {
|
||||
type: [Object, String],
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
emits: ["input"],
|
||||
data() {
|
||||
let headersObj = {};
|
||||
if (typeof this.headers === "string") {
|
||||
try {
|
||||
const match = this.headers.match(/headers":\s*({[^}]+})/);
|
||||
if (match) {
|
||||
headersObj = JSON.parse(match[1]);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse headers from code string");
|
||||
}
|
||||
} else if (typeof this.headers === "object") {
|
||||
headersObj = this.headers;
|
||||
}
|
||||
|
||||
return {
|
||||
headersList: Object.entries(headersObj).map(([name, value]) => ({
|
||||
name,
|
||||
value: typeof value === "string" ? value : JSON.stringify(value),
|
||||
isCommon: commonHeaders.some((h) => h.value === name),
|
||||
})),
|
||||
newHeaderField: null,
|
||||
commonHeaders,
|
||||
userAgentOptions: userAgent,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
addCommonHeader(headerName) {
|
||||
if (!headerName) return;
|
||||
if (this.headersList.some((h) => h.name === headerName)) {
|
||||
this.newHeaderField = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.headersList.push({
|
||||
name: headerName,
|
||||
value: "",
|
||||
isCommon: true,
|
||||
});
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.newHeaderField = null;
|
||||
});
|
||||
|
||||
this.emitUpdate();
|
||||
},
|
||||
addCustomHeader() {
|
||||
this.headersList.push({
|
||||
name: "",
|
||||
value: "",
|
||||
isCommon: false,
|
||||
});
|
||||
},
|
||||
removeHeader(index) {
|
||||
this.headersList.splice(index, 1);
|
||||
this.emitUpdate();
|
||||
},
|
||||
setUserAgent(header, value) {
|
||||
header.value = value;
|
||||
this.emitUpdate();
|
||||
},
|
||||
emitUpdate() {
|
||||
const headers = {};
|
||||
this.headersList.forEach((header) => {
|
||||
if (header.name && header.value) {
|
||||
headers[header.name] = header.value;
|
||||
}
|
||||
});
|
||||
|
||||
this.$emit("input", headers);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="row q-col-gutter-sm ubrowser-function-input">
|
||||
<div class="col-12">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-3">
|
||||
@ -35,6 +35,7 @@
|
||||
type="textarea"
|
||||
dense
|
||||
borderless
|
||||
style="font-family: monospace, monoca, consola"
|
||||
autogrow
|
||||
@update:model-value="updateFunction"
|
||||
>
|
||||
@ -198,7 +199,39 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.q-field__control) .text-primary.func-symbol {
|
||||
font-size: 18px !important;
|
||||
.ubrowser-function-input :deep(.q-field__control) .text-primary.func-symbol {
|
||||
font-size: 24px !important;
|
||||
}
|
||||
|
||||
.ubrowser-function-input :deep(.q-select__input) {
|
||||
display: flex !important;
|
||||
flex-wrap: nowrap !important;
|
||||
overflow-x: auto !important;
|
||||
scrollbar-width: none !important;
|
||||
-ms-overflow-style: none !important;
|
||||
}
|
||||
|
||||
.ubrowser-function-input :deep(.q-select .q-field__native) {
|
||||
display: flex !important;
|
||||
flex-wrap: nowrap !important;
|
||||
overflow-x: auto !important;
|
||||
scrollbar-width: none !important;
|
||||
-ms-overflow-style: none !important;
|
||||
}
|
||||
|
||||
.ubrowser-function-input :deep(.q-select .q-field__native > div) {
|
||||
display: flex !important;
|
||||
flex-wrap: nowrap !important;
|
||||
flex: 0 0 auto !important;
|
||||
}
|
||||
|
||||
.ubrowser-function-input :deep(.q-select .q-chip) {
|
||||
flex: 0 0 auto !important;
|
||||
margin-right: 4px !important;
|
||||
}
|
||||
|
||||
.ubrowser-function-input :deep(.q-select__input::-webkit-scrollbar),
|
||||
.ubrowser-function-input :deep(.q-select .q-field__native::-webkit-scrollbar) {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
@ -42,28 +42,20 @@ export const commandCategories = [
|
||||
},
|
||||
{
|
||||
value: "ubrowser",
|
||||
label: "UBrowser浏览器操作",
|
||||
desc: "配置UBrowser浏览器操作",
|
||||
label: "ubrowser浏览器操作",
|
||||
desc: "配置ubrowser浏览器操作",
|
||||
hasUBrowserEditor: true,
|
||||
isAsync: true,
|
||||
icon: "public",
|
||||
},
|
||||
{
|
||||
value: "axios",
|
||||
label: "发送HTTP请求(Axios)",
|
||||
label: "HTTP请求(Axios)",
|
||||
desc: "使用Axios发送HTTP请求",
|
||||
hasAxiosEditor: true,
|
||||
isAsync: true,
|
||||
icon: "http",
|
||||
},
|
||||
{
|
||||
value: "fetch",
|
||||
label: "发送HTTP请求(Fetch)",
|
||||
desc: "使用Fetch API发送HTTP请求",
|
||||
hasFetchEditor: true,
|
||||
isAsync: true,
|
||||
icon: "http",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -82,24 +74,31 @@ export const commandCategories = [
|
||||
desc: "要写入剪切板的内容",
|
||||
icon: "content_copy",
|
||||
},
|
||||
{
|
||||
value: "electron.clipboard.readText",
|
||||
label: "获取剪贴板内容",
|
||||
desc: "获取剪贴板内容",
|
||||
icon: "content_copy",
|
||||
hasNoArgs: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "消息通知",
|
||||
icon: "notifications",
|
||||
commands: [
|
||||
{
|
||||
value: "console.log",
|
||||
label: "打印消息",
|
||||
desc: "要打印的消息文本",
|
||||
icon: "info",
|
||||
},
|
||||
{
|
||||
value: "message",
|
||||
label: "发送系统消息",
|
||||
desc: "要发送的系统消息文本",
|
||||
icon: "message",
|
||||
},
|
||||
{
|
||||
value: "quickcommand.showMessageBox",
|
||||
label: "弹窗显示消息",
|
||||
desc: "要弹窗显示的消息文本",
|
||||
icon: "warning",
|
||||
},
|
||||
{
|
||||
value: "send",
|
||||
label: "发送文本到活动窗口",
|
||||
|
@ -12,14 +12,46 @@ const processVariableValue = (value) => {
|
||||
return value.slice(1, -1);
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查路径是否匹配或是目标路径的父路径
|
||||
* @param {string} currentPath 当前路径
|
||||
* @param {string[]} targetPaths 目标路径列表
|
||||
* @returns {boolean} 是否匹配
|
||||
*/
|
||||
const isPathMatched = (currentPath, targetPaths) => {
|
||||
if (!targetPaths) return false;
|
||||
return targetPaths.some(
|
||||
(path) =>
|
||||
path === currentPath || // 精确匹配
|
||||
path.startsWith(currentPath + ".") // 是父路径
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 递归移除对象中的空值
|
||||
* @param {Object} obj 要处理的对象
|
||||
* @returns {Object} 处理后的对象
|
||||
*/
|
||||
const removeEmptyValues = (obj) => {
|
||||
return _.omitBy(obj, (value) => {
|
||||
if (_.isNil(value) || value === "") return true;
|
||||
if (typeof value === "object")
|
||||
return _.isEmpty(removeEmptyValues(value));
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 递归处理对象的值
|
||||
* @param {Object} obj 要处理的对象
|
||||
* @param {string} parentPath 父路径
|
||||
* @param {string[]|null} variableFields 需要处理的字段列表,null表示处理所有字段
|
||||
* @param {string[]|null} excludeFields 需要排除的字段列表,即使匹配了处理条件也不处理
|
||||
* @returns {string} 处理后的字符串
|
||||
*/
|
||||
const processObject = (obj, parentPath = "", variableFields) => {
|
||||
const processObject = (obj, parentPath = "", variableFields, excludeFields) => {
|
||||
// 移除空值
|
||||
obj = removeEmptyValues(obj);
|
||||
let result = "{\n";
|
||||
const entries = Object.entries(obj);
|
||||
|
||||
@ -28,16 +60,20 @@ const processObject = (obj, parentPath = "", variableFields) => {
|
||||
let valueStr = "";
|
||||
|
||||
// 检查是否需要处理当前字段
|
||||
const shouldProcess =
|
||||
!variableFields || // 不传递variableFields则处理所有字段
|
||||
variableFields.includes(parentPath) || // 父字段是完整处理字段
|
||||
variableFields.includes(key) || // 当前字段是完整处理字段
|
||||
variableFields.includes(currentPath) || // 当前路径精确匹配
|
||||
variableFields.some((field) => field.startsWith(currentPath + ".")); // 当前路径是指定路径的父路径
|
||||
const isIncluded =
|
||||
!variableFields || isPathMatched(currentPath, variableFields);
|
||||
const isExcluded =
|
||||
excludeFields && isPathMatched(currentPath, excludeFields);
|
||||
const shouldProcess = isIncluded && !isExcluded;
|
||||
|
||||
// 处理对象类型
|
||||
if (typeof value === "object" && value !== null) {
|
||||
valueStr = processObject(value, currentPath, variableFields);
|
||||
valueStr = processObject(
|
||||
value,
|
||||
currentPath,
|
||||
variableFields,
|
||||
excludeFields
|
||||
);
|
||||
}
|
||||
// 处理字符串类型
|
||||
else if (typeof value === "string") {
|
||||
@ -71,13 +107,19 @@ const processObject = (obj, parentPath = "", variableFields) => {
|
||||
* 1. 完整字段处理:如 headers - 处理整个对象及其所有子字段
|
||||
* 2. 指定路径处理:如 data.headers.Referer - 只处理特定路径
|
||||
* 3. 不传递 variableFields 则处理所有字段
|
||||
* 4. 可以通过 excludeFields 排除特定字段,即使匹配了处理条件也不处理
|
||||
* @param {string} jsonStr JSON字符串
|
||||
* @param {string[]|null} [variableFields] 需要处理的字段列表,包括完整字段和指定路径。不传则处理所有字段
|
||||
* @param {string[]|null} [excludeFields] 需要排除的字段列表,即使匹配了处理条件也不处理
|
||||
* @returns {string} 处理后的字符串
|
||||
*/
|
||||
export const formatJsonVariables = (jsonObj, variableFields = null) => {
|
||||
export const formatJsonVariables = (
|
||||
jsonObj,
|
||||
variableFields = null,
|
||||
excludeFields = null
|
||||
) => {
|
||||
try {
|
||||
return processObject(jsonObj, "", variableFields);
|
||||
return processObject(jsonObj, "", variableFields, excludeFields);
|
||||
} catch (e) {
|
||||
console.warn("Failed to process JSON variables:", e);
|
||||
return JSON.stringify(jsonObj, null, 2);
|
||||
|
@ -55,12 +55,15 @@ export const commonHeaders = [
|
||||
{ label: "Content-Type", value: "Content-Type" },
|
||||
{ label: "Authorization", value: "Authorization" },
|
||||
{ label: "User-Agent", value: "User-Agent" },
|
||||
{ label: "Cookie", value: "Cookie" },
|
||||
{ label: "Accept", value: "Accept" },
|
||||
{ label: "Accept-Language", value: "Accept-Language" },
|
||||
{ label: "Accept-Encoding", value: "Accept-Encoding" },
|
||||
{ label: "Cookie", value: "Cookie" },
|
||||
{ label: "Origin", value: "Origin" },
|
||||
{ label: "Referer", value: "Referer" },
|
||||
{ label: "X-Requested-With", value: "X-Requested-With" },
|
||||
{ label: "X-Forwarded-For", value: "X-Forwarded-For" },
|
||||
{ label: "X-Real-IP", value: "X-Real-IP" },
|
||||
];
|
||||
|
||||
export const deviceName = [
|
||||
@ -75,3 +78,38 @@ export const deviceName = [
|
||||
{ label: "HUAWEI Mate30", value: "HUAWEI Mate30" },
|
||||
{ label: "HUAWEI Mate30 Pro", value: "HUAWEI Mate30 Pro" },
|
||||
];
|
||||
|
||||
export const contentTypes = [
|
||||
{
|
||||
label: "application/json",
|
||||
value: "application/json",
|
||||
},
|
||||
{
|
||||
label: "application/x-www-form-urlencoded",
|
||||
value: "application/x-www-form-urlencoded",
|
||||
},
|
||||
{
|
||||
label: "multipart/form-data",
|
||||
value: "multipart/form-data",
|
||||
},
|
||||
{
|
||||
label: "text/plain",
|
||||
value: "text/plain",
|
||||
},
|
||||
{
|
||||
label: "text/html",
|
||||
value: "text/html",
|
||||
},
|
||||
{
|
||||
label: "text/xml",
|
||||
value: "text/xml",
|
||||
},
|
||||
{
|
||||
label: "application/xml",
|
||||
value: "application/xml",
|
||||
},
|
||||
{
|
||||
label: "application/octet-stream",
|
||||
value: "application/octet-stream",
|
||||
},
|
||||
];
|
||||
|
Loading…
x
Reference in New Issue
Block a user