gaoluyang
2026-06-24 712aa51536236d43e87273e4ce45ac5691dffad8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<script lang="ts" setup>
import { ref, watch } from 'vue';
 
import { IconifyIcon } from '..\..\..\..\..\..\..\packages\icons\src';
import { isEmpty } from '..\..\..\..\..\..\..\packages\utils\src';
 
import { Button, Input } from 'ant-design-vue';
 
defineOptions({ name: 'KeyValueEditor' });
 
const props = defineProps<{
  addButtonText: string;
  modelValue: Record<string, string>;
}>();
 
const emit = defineEmits(['update:modelValue']);
 
interface KeyValueItem {
  _uid: number;
  key: string;
  value: string;
}
 
let uidCounter = 0;
const items = ref<KeyValueItem[]>([]); // 内部 key-value 项列表
 
/** 添加项目 */
function addItem() {
  uidCounter += 1;
  items.value.push({ _uid: uidCounter, key: '', value: '' });
  updateModelValue();
}
 
/** 移除项目 */
function removeItem(index: number) {
  items.value.splice(index, 1);
  updateModelValue();
}
 
/** 更新 modelValue */
function updateModelValue() {
  const result: Record<string, string> = {};
  items.value.forEach((item) => {
    if (item.key) {
      result[item.key] = item.value;
    }
  });
  emit('update:modelValue', result);
}
 
/** 监听项目变化 */
watch(items, updateModelValue, { deep: true });
watch(
  () => props.modelValue,
  (val) => {
    // 列表有值后以列表中的值为准
    if (isEmpty(val) || !isEmpty(items.value)) {
      return;
    }
    items.value = Object.entries(props.modelValue).map(([key, value]) => {
      uidCounter += 1;
      return { _uid: uidCounter, key, value };
    });
  },
);
</script>
 
<template>
  <div v-for="(item, index) in items" :key="item._uid" class="mb-2 flex w-full">
    <Input v-model:value="item.key" class="mr-2" placeholder="键" />
    <Input v-model:value="item.value" placeholder="值" />
    <Button class="ml-2" type="link" danger @click="removeItem(index)">
      <IconifyIcon icon="ant-design:delete-outlined" />
      删除
    </Button>
  </div>
  <Button type="link" @click="addItem">
    <IconifyIcon icon="ant-design:plus-outlined" />
    {{ addButtonText }}
  </Button>
</template>