169 lines
4.6 KiB
Vue
169 lines
4.6 KiB
Vue
|
|
<script setup lang="ts">
|
|||
|
|
import type { DataPushRule } from '#/api-client';
|
|||
|
|
|
|||
|
|
import { ref, toRaw, watch } from 'vue';
|
|||
|
|
|
|||
|
|
import { Button, Input, Select, Switch } from 'ant-design-vue';
|
|||
|
|
|
|||
|
|
defineOptions({
|
|||
|
|
name: 'DataPushRuleEditor',
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const props = defineProps<{
|
|||
|
|
// 是否禁用编辑(如设备未开启"覆盖产品配置"时)
|
|||
|
|
disabled?: boolean;
|
|||
|
|
rules?: DataPushRule[] | null;
|
|||
|
|
}>();
|
|||
|
|
|
|||
|
|
const emit = defineEmits<{
|
|||
|
|
(e: 'update:rules', value: DataPushRule[]): void;
|
|||
|
|
}>();
|
|||
|
|
|
|||
|
|
// 数据类型 / 推送方式:与后端字符串枚举一一对应(生成的契约为字符串字面量)
|
|||
|
|
const dataTypeOptions = [
|
|||
|
|
{ label: '属性', value: 'Property' },
|
|||
|
|
{ label: '事件', value: 'Event' },
|
|||
|
|
{ label: '状态', value: 'Status' },
|
|||
|
|
];
|
|||
|
|
const pushTypeOptions = [
|
|||
|
|
{ label: 'HTTP POST', value: 'HttpPost' },
|
|||
|
|
{ label: 'Redis 普通订阅', value: 'NormalReisSubscription' },
|
|||
|
|
{ label: 'Redis 可靠订阅', value: 'ReliableReisSubscription' },
|
|||
|
|
{ label: 'Redis List', value: 'RedisListPush' },
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
// 内部维护一份可编辑副本,变更后回抛给父级
|
|||
|
|
let seed = 0;
|
|||
|
|
// 记录最近一次抛出的数组引用:用于识别 v-model 回环,避免重建 list 导致输入框失焦
|
|||
|
|
let lastEmitted: DataPushRule[] | null = null;
|
|||
|
|
const list = ref<(DataPushRule & { _key: number })[]>([]);
|
|||
|
|
|
|||
|
|
watch(
|
|||
|
|
() => props.rules,
|
|||
|
|
(val) => {
|
|||
|
|
// 跳过自身 emit 造成的回环,仅在外部真正替换数据时重建。
|
|||
|
|
// 注意:父级用 ref 存储时会把数组包成 reactive 代理,故须用 toRaw 比较原始引用。
|
|||
|
|
if (toRaw(val) === lastEmitted) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
list.value = Array.isArray(val)
|
|||
|
|
? val.map((r) => ({ ...r, _key: seed++ }))
|
|||
|
|
: [];
|
|||
|
|
},
|
|||
|
|
{ immediate: true },
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
function emitChange() {
|
|||
|
|
// 抛出时剥离内部 _key,仅回传契约字段
|
|||
|
|
const out = list.value.map((r) => ({
|
|||
|
|
ruleName: r.ruleName,
|
|||
|
|
enabled: r.enabled,
|
|||
|
|
dataType: r.dataType,
|
|||
|
|
includeNames: r.includeNames ?? null,
|
|||
|
|
pushType: r.pushType,
|
|||
|
|
pushServer: r.pushServer,
|
|||
|
|
pushInfo: r.pushInfo,
|
|||
|
|
}));
|
|||
|
|
lastEmitted = out;
|
|||
|
|
emit('update:rules', out);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function addRule() {
|
|||
|
|
list.value.push({
|
|||
|
|
_key: seed++,
|
|||
|
|
ruleName: '',
|
|||
|
|
enabled: true,
|
|||
|
|
dataType: 'Property',
|
|||
|
|
includeNames: null,
|
|||
|
|
pushType: 'HttpPost',
|
|||
|
|
pushServer: '',
|
|||
|
|
pushInfo: '',
|
|||
|
|
});
|
|||
|
|
emitChange();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function removeRule(index: number) {
|
|||
|
|
list.value.splice(index, 1);
|
|||
|
|
emitChange();
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<div>
|
|||
|
|
<!-- 表头 -->
|
|||
|
|
<div class="mb-1 flex items-center gap-2 text-xs text-gray-500">
|
|||
|
|
<span class="w-[44px] flex-shrink-0">启用</span>
|
|||
|
|
<span class="w-[96px] flex-shrink-0">数据类型</span>
|
|||
|
|
<span class="w-[150px] flex-shrink-0">推送方式</span>
|
|||
|
|
<span class="flex-1">服务地址</span>
|
|||
|
|
<span class="flex-1">路径 / 主题 / KEY</span>
|
|||
|
|
<span class="w-[120px] flex-shrink-0">备注</span>
|
|||
|
|
<span class="w-[48px] flex-shrink-0">操作</span>
|
|||
|
|
</div>
|
|||
|
|
<!-- 规则行:用稳定 key 的 v-for,避免输入时被重建导致失焦 -->
|
|||
|
|
<div
|
|||
|
|
v-for="(item, index) in list"
|
|||
|
|
:key="item._key"
|
|||
|
|
class="mb-2 flex items-center gap-2"
|
|||
|
|
>
|
|||
|
|
<span class="w-[44px] flex-shrink-0">
|
|||
|
|
<Switch
|
|||
|
|
v-model:checked="item.enabled"
|
|||
|
|
size="small"
|
|||
|
|
:disabled="disabled"
|
|||
|
|
@change="emitChange"
|
|||
|
|
/>
|
|||
|
|
</span>
|
|||
|
|
<Select
|
|||
|
|
v-model:value="item.dataType"
|
|||
|
|
:options="dataTypeOptions"
|
|||
|
|
:disabled="disabled"
|
|||
|
|
class="w-[96px] flex-shrink-0"
|
|||
|
|
@change="emitChange"
|
|||
|
|
/>
|
|||
|
|
<Select
|
|||
|
|
v-model:value="item.pushType"
|
|||
|
|
:options="pushTypeOptions"
|
|||
|
|
:disabled="disabled"
|
|||
|
|
class="w-[150px] flex-shrink-0"
|
|||
|
|
@change="emitChange"
|
|||
|
|
/>
|
|||
|
|
<Input
|
|||
|
|
v-model:value="item.pushServer"
|
|||
|
|
:disabled="disabled"
|
|||
|
|
class="flex-1"
|
|||
|
|
placeholder="HTTP:http://ip:port,Redis:ip:port"
|
|||
|
|
@change="emitChange"
|
|||
|
|
/>
|
|||
|
|
<Input
|
|||
|
|
v-model:value="item.pushInfo"
|
|||
|
|
:disabled="disabled"
|
|||
|
|
class="flex-1"
|
|||
|
|
placeholder="HTTP 为路径,Redis 为主题或 key"
|
|||
|
|
@change="emitChange"
|
|||
|
|
/>
|
|||
|
|
<Input
|
|||
|
|
v-model:value="item.ruleName"
|
|||
|
|
:disabled="disabled"
|
|||
|
|
class="w-[120px] flex-shrink-0"
|
|||
|
|
placeholder="可空"
|
|||
|
|
@change="emitChange"
|
|||
|
|
/>
|
|||
|
|
<span class="w-[48px] flex-shrink-0">
|
|||
|
|
<Button
|
|||
|
|
type="link"
|
|||
|
|
danger
|
|||
|
|
size="small"
|
|||
|
|
:disabled="disabled"
|
|||
|
|
@click="removeRule(index)"
|
|||
|
|
>
|
|||
|
|
删除
|
|||
|
|
</Button>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<Button type="dashed" block :disabled="disabled" @click="addRule">
|
|||
|
|
+ 添加推送规则
|
|||
|
|
</Button>
|
|||
|
|
</div>
|
|||
|
|
</template>
|