HttpHeaderEditor.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <template>
  2. <el-dialog
  3. v-model="dialogVisible"
  4. title="编辑请求头"
  5. width="600px"
  6. :close-on-click-modal="false"
  7. @close="handleClose"
  8. >
  9. <div class="header-editor">
  10. <div class="header-list">
  11. <div v-for="(item, index) in headerList" :key="index" class="header-item">
  12. <el-input v-model="item.key" placeholder="请输入参数名" class="header-key" clearable />
  13. <span class="separator">:</span>
  14. <el-input
  15. v-model="item.value"
  16. placeholder="请输入参数值 (支持表达式 ${变量名})"
  17. class="header-value"
  18. clearable
  19. />
  20. <el-button
  21. type="danger"
  22. :icon="Delete"
  23. circle
  24. size="small"
  25. @click="removeHeader(index)"
  26. />
  27. </div>
  28. </div>
  29. <el-button type="primary" :icon="Plus" class="add-btn" @click="addHeader">
  30. 添加请求头
  31. </el-button>
  32. </div>
  33. <template #footer>
  34. <span class="dialog-footer">
  35. <el-button @click="handleClose">取消</el-button>
  36. <el-button type="primary" @click="handleSave">保存</el-button>
  37. </span>
  38. </template>
  39. </el-dialog>
  40. </template>
  41. <script lang="ts" setup>
  42. import { Delete, Plus } from '@element-plus/icons-vue'
  43. defineOptions({ name: 'HttpHeaderEditor' })
  44. const props = defineProps({
  45. modelValue: {
  46. type: Boolean,
  47. default: false
  48. },
  49. headers: {
  50. type: String,
  51. default: ''
  52. }
  53. })
  54. const emit = defineEmits(['update:modelValue', 'save'])
  55. interface HeaderItem {
  56. key: string
  57. value: string
  58. }
  59. const dialogVisible = computed({
  60. get: () => props.modelValue,
  61. set: (val) => emit('update:modelValue', val)
  62. })
  63. const headerList = ref<HeaderItem[]>([])
  64. // 解析请求头字符串为列表
  65. const parseHeaders = (headersStr: string): HeaderItem[] => {
  66. if (!headersStr || !headersStr.trim()) {
  67. return [{ key: '', value: '' }]
  68. }
  69. const lines = headersStr.split('\n').filter((line) => line.trim())
  70. const parsed = lines.map((line) => {
  71. const colonIndex = line.indexOf(':')
  72. if (colonIndex > 0) {
  73. return {
  74. key: line.substring(0, colonIndex).trim(),
  75. value: line.substring(colonIndex + 1).trim()
  76. }
  77. }
  78. return { key: line.trim(), value: '' }
  79. })
  80. return parsed.length > 0 ? parsed : [{ key: '', value: '' }]
  81. }
  82. // 将列表转换为请求头字符串
  83. const stringifyHeaders = (headers: HeaderItem[]): string => {
  84. return headers
  85. .filter((item) => item.key.trim())
  86. .map((item) => `${item.key}: ${item.value}`)
  87. .join('\n')
  88. }
  89. // 添加请求头
  90. const addHeader = () => {
  91. headerList.value.push({ key: '', value: '' })
  92. }
  93. // 移除请求头
  94. const removeHeader = (index: number) => {
  95. if (headerList.value.length === 1) {
  96. // 至少保留一行
  97. headerList.value = [{ key: '', value: '' }]
  98. } else {
  99. headerList.value.splice(index, 1)
  100. }
  101. }
  102. // 保存
  103. const handleSave = () => {
  104. const headersStr = stringifyHeaders(headerList.value)
  105. emit('save', headersStr)
  106. dialogVisible.value = false
  107. }
  108. // 关闭
  109. const handleClose = () => {
  110. dialogVisible.value = false
  111. }
  112. // 监听对话框打开,初始化数据
  113. watch(
  114. () => props.modelValue,
  115. (val) => {
  116. if (val) {
  117. headerList.value = parseHeaders(props.headers)
  118. }
  119. },
  120. { immediate: true }
  121. )
  122. </script>
  123. <style lang="scss" scoped>
  124. .header-editor {
  125. .header-list {
  126. max-height: 400px;
  127. overflow-y: auto;
  128. margin-bottom: 16px;
  129. }
  130. .header-item {
  131. display: flex;
  132. align-items: center;
  133. gap: 8px;
  134. margin-bottom: 12px;
  135. .header-key {
  136. flex: 0 0 180px;
  137. }
  138. .separator {
  139. color: #606266;
  140. font-weight: 500;
  141. }
  142. .header-value {
  143. flex: 1;
  144. }
  145. }
  146. .add-btn {
  147. width: 100%;
  148. }
  149. }
  150. .dialog-footer {
  151. display: flex;
  152. justify-content: flex-end;
  153. gap: 10px;
  154. }
  155. </style>