Files
Wormhole/web/src/components/ProxyHeadersFields.vue
T
rose_cat707 9a5de83fee
CI / docker (push) Successful in 1m36s
feat: Wormhole 内网穿透与反向代理网关
Go + Vue 管理台:Agent 隧道、域名反代、IP 安全、访客验证与监控大盘。
Gitea Actions CI 多架构镜像;纯 Go SQLite、无 CGO Docker 构建。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 17:44:54 +08:00

105 lines
2.8 KiB
Vue

<template>
<div class="proxy-headers-fields">
<el-form-item :label="t('ipForward.ipForward')">
<el-radio-group v-model="ipMode">
<el-radio label="replace">{{ ipForwardModeLabel('replace', t) }}</el-radio>
<el-radio label="append">{{ ipForwardModeLabel('append', t) }}</el-radio>
</el-radio-group>
<div class="form-hint">{{ modeHint }}</div>
</el-form-item>
<el-form-item :label="t('ipForward.customHeaders')">
<div class="header-list">
<div v-for="(row, idx) in headerRows" :key="idx" class="header-row">
<el-input v-model="row.key" :placeholder="t('ipForward.headerName')" @input="emitHeaders" />
<el-input v-model="row.value" :placeholder="t('ipForward.headerValue')" @input="emitHeaders" />
<el-button link type="danger" @click="removeRow(idx)">{{ t('common.delete') }}</el-button>
</div>
<el-button size="small" @click="addRow">{{ t('ipForward.addHeader') }}</el-button>
</div>
</el-form-item>
</div>
</template>
<script setup>
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { ipForwardModeHint, ipForwardModeLabel, normalizeIPForwardMode } from '@/utils/ipForward'
const { t } = useI18n()
const props = defineProps({
ipForwardMode: { type: String, default: 'replace' },
customHeaders: { type: String, default: '' },
})
const emit = defineEmits(['update:ipForwardMode', 'update:customHeaders'])
const headerRows = ref([])
function parseHeaders(raw) {
if (!raw || raw === '{}') return []
try {
const obj = JSON.parse(raw)
return Object.entries(obj).map(([key, value]) => ({ key, value: String(value) }))
} catch {
return []
}
}
function serializeHeaders(rows) {
const obj = {}
for (const row of rows) {
const key = row.key?.trim()
if (!key) continue
obj[key] = row.value ?? ''
}
return Object.keys(obj).length ? JSON.stringify(obj) : ''
}
function syncFromProps() {
headerRows.value = parseHeaders(props.customHeaders)
}
watch(() => props.customHeaders, syncFromProps, { immediate: true })
const ipMode = computed({
get() {
return normalizeIPForwardMode(props.ipForwardMode)
},
set(v) {
emit('update:ipForwardMode', v)
},
})
const modeHint = computed(() => ipForwardModeHint(ipMode.value, t))
function emitHeaders() {
emit('update:customHeaders', serializeHeaders(headerRows.value))
}
function addRow() {
headerRows.value.push({ key: '', value: '' })
emitHeaders()
}
function removeRow(idx) {
headerRows.value.splice(idx, 1)
emitHeaders()
}
</script>
<style scoped>
.header-list {
width: 100%;
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.header-row {
display: grid;
grid-template-columns: 1fr 1fr auto;
gap: var(--space-2);
align-items: center;
}
</style>