fastbee/history/referencePage/KeyValueEditor.vue
2026-05-31 08:42:02 +08:00

308 lines
7.1 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="key-value-editor">
<div v-if="bulkEdit" class="kv-toolbar">
<el-button type="text" size="mini" class="kv-mode-btn" @click="toggleBulkMode">
{{ bulkMode ? '键值编辑' : '批量编辑' }}
</el-button>
</div>
<div v-if="!bulkMode" class="kv-table">
<div class="kv-header" :class="rowLayoutClass">
<span v-if="showEnabled" class="col-check" />
<span class="col-key">Key</span>
<span class="col-value">Value</span>
<span v-if="showDescription" class="col-desc">Description</span>
<span class="col-action" />
</div>
<div
v-for="(item, index) in rows"
:key="index"
class="kv-row"
:class="[rowLayoutClass, { 'is-disabled': showEnabled && item.enabled === false }]"
>
<div v-if="showEnabled" class="col-check">
<el-checkbox v-model="item.enabled" />
</div>
<div class="col-key">
<el-input v-model="item.key" size="small" :placeholder="keyPlaceholder" />
</div>
<div class="col-value">
<el-input v-model="item.value" size="small" :placeholder="valuePlaceholder" />
</div>
<div v-if="showDescription" class="col-desc">
<el-input v-model="item.description" size="small" placeholder="说明" />
</div>
<div class="col-action">
<el-button
type="text"
size="small"
class="kv-delete-btn"
icon="el-icon-delete"
@click="removeRow(index)"
/>
</div>
</div>
<div v-if="!rows.length" class="kv-empty">暂无参数,点击下方按钮添加或使用批量编辑</div>
</div>
<div v-else class="kv-bulk">
<el-input
v-model="bulkText"
type="textarea"
:rows="bulkRows"
:placeholder="bulkPlaceholder"
@blur="syncBulkToRows"
/>
<p v-if="bulkError" class="kv-bulk-error">{{ bulkError }}</p>
<p class="kv-bulk-hint">{{ bulkHint }}</p>
</div>
<div v-if="!bulkMode" class="kv-footer">
<el-button type="text" size="mini" icon="el-icon-plus" class="kv-add-btn" @click="addRow">
{{ addText }}
</el-button>
</div>
</div>
</template>
<script>
import { bulkTextToRows, rowsToBulkText } from '../utils/requestTemplate';
export default {
name: 'PlatformKeyValueEditor',
props: {
value: { type: Array, default: () => [] },
addText: { type: String, default: '添加参数' },
keyPlaceholder: { type: String, default: '参数名' },
valuePlaceholder: { type: String, default: '参数值' },
showEnabled: { type: Boolean, default: true },
showDescription: { type: Boolean, default: true },
bulkEdit: { type: Boolean, default: true },
bulkRows: { type: Number, default: 10 },
bulkPlaceholder: {
type: String,
default: '每行一个参数,格式 key:value\n以 # 开头的行表示禁用,例如:\n#disabledKey:value\nactiveKey:${token}',
},
bulkHint: {
type: String,
default: '每行格式为 key:value,仅按第一个冒号分割;以 # 开头的行在保存时视为禁用项。',
},
},
data() {
return {
bulkMode: false,
bulkText: '',
bulkError: '',
};
},
computed: {
rows: {
get() {
return this.value;
},
set(value) {
this.$emit('input', value);
},
},
rowLayoutClass() {
return {
'no-desc': !this.showDescription,
'no-check': !this.showEnabled,
};
},
},
beforeDestroy() {
if (this._bulkTimer) {
clearTimeout(this._bulkTimer);
}
},
methods: {
/** 提交表单前调用,将批量编辑内容写回 v-model */
flushBulk() {
if (!this.bulkMode) {
return;
}
this.applyBulkText(true);
},
toggleBulkMode() {
if (this.bulkMode) {
if (!this.applyBulkText()) {
return;
}
this.bulkMode = false;
this.bulkError = '';
return;
}
this.bulkText = rowsToBulkText(this.rows);
this.bulkError = '';
this.bulkMode = true;
},
syncBulkToRows() {
if (!this.bulkMode) {
return;
}
this.applyBulkText(true);
},
applyBulkText(silent) {
const parsed = bulkTextToRows(this.bulkText);
if (!silent && this.bulkText.trim() && !parsed.length) {
this.bulkError = '未能解析出有效参数,请检查每行是否为 key:value 格式';
return false;
}
const descMap = {};
(this.rows || []).forEach(row => {
if (row && row.key) {
descMap[row.key] = row.description || '';
}
});
parsed.forEach(row => {
row.description = descMap[row.key] || '';
});
this.rows = parsed.length ? parsed : [];
this.bulkError = '';
return true;
},
addRow() {
const rows = this.rows.slice();
rows.push({ enabled: true, key: '', value: '', description: '' });
this.rows = rows;
},
removeRow(index) {
const rows = this.rows.slice();
rows.splice(index, 1);
this.rows = rows;
},
},
};
</script>
<style scoped>
.key-value-editor {
width: 100%;
}
.kv-toolbar {
display: flex;
justify-content: flex-end;
margin-bottom: 4px;
}
.kv-mode-btn {
padding: 0 4px;
font-size: 12px;
color: #409eff;
}
.kv-table {
border: 1px solid #e4e7ed;
border-radius: 4px;
overflow: hidden;
background: #fff;
}
.kv-header,
.kv-row {
display: grid;
grid-template-columns: 36px minmax(0, 1fr) minmax(0, 1fr) 120px 36px;
align-items: center;
column-gap: 8px;
padding: 0 8px;
}
.kv-header.no-desc,
.kv-row.no-desc {
grid-template-columns: 36px minmax(0, 1fr) minmax(0, 1fr) 36px;
}
.kv-header.no-check,
.kv-row.no-check {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 120px 36px;
}
.kv-header.no-check.no-desc,
.kv-row.no-check.no-desc {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 36px;
}
.kv-header {
height: 36px;
background: #f5f7fa;
border-bottom: 1px solid #e4e7ed;
font-size: 12px;
font-weight: 600;
color: #909399;
}
.kv-row {
min-height: 40px;
padding-top: 4px;
padding-bottom: 4px;
border-bottom: 1px solid #f0f2f5;
}
.kv-row:last-child {
border-bottom: none;
}
.kv-row.is-disabled .col-key ::v-deep .el-input__inner,
.kv-row.is-disabled .col-value ::v-deep .el-input__inner {
color: #c0c4cc;
}
.col-check {
display: flex;
align-items: center;
justify-content: center;
}
.col-action {
display: flex;
align-items: center;
justify-content: center;
}
.kv-delete-btn {
padding: 4px;
color: #f56c6c;
font-size: 16px;
}
.kv-delete-btn:hover {
color: #f78989;
}
.kv-empty {
padding: 16px;
text-align: center;
font-size: 12px;
color: #c0c4cc;
}
.kv-footer {
margin-top: 6px;
}
.kv-add-btn {
padding-left: 0;
color: #409eff;
}
.kv-bulk ::v-deep .el-textarea__inner {
font-family: Consolas, Monaco, 'Courier New', monospace;
font-size: 13px;
line-height: 1.5;
}
.kv-bulk-hint {
margin: 6px 0 0;
font-size: 12px;
color: #909399;
line-height: 1.5;
}
.kv-bulk-error {
margin: 6px 0 0;
font-size: 12px;
color: #f56c6c;
}
</style>