This commit is contained in:
dzc committed 2026-07-02 18:18:39 +08:00
1 parent 8cc6972f1c
commit 5668acf964
5 files changed
+155 -52

No files matched your search

@@ -0,0 +1,98 @@
<template>
<el-select v-model="state.selectedLabels" multiple filterable collapse-tags collapse-tags-tooltip
:max-collapse-tags="props.maxCollapseTags" :placeholder="props.placeholder" :clearable="props.clearable"
:disabled="props.disabled" style="width: 100%" @change="handleChange">
<el-option v-for="item in capabilityOptions" :key="item.value" :label="item.label" :value="item.value">
<div class="flex items-center justify-between gap-3">
<span class="truncate">{{ item.label }}</span>
<el-tag size="small" effect="plain">第{{ item.bit }}位</el-tag>
</div>
</el-option>
</el-select>
</template>
<script setup lang="ts" name="select-device-capability">
import {
DEVICE_CAPABILITY_LABELS,
formatDeviceCapability,
parseDeviceCapability,
} from '/@/utils/dispose'
const emit = defineEmits(['change'])
const selectValue = defineModel<any>()
const props = withDefaults(
defineProps<{
placeholder?: string
disabled?: boolean
clearable?: boolean
outputFormat?: 'comma' | 'binary' | 'decimal' | 'labels'
maxCollapseTags?: number
}>(),
{
placeholder: '请选择通道能力',
disabled: false,
clearable: true,
outputFormat: 'comma',
maxCollapseTags: 3,
},
)
const capabilityOptions = DEVICE_CAPABILITY_LABELS.map((label, index) => ({
label,
value: label,
bit: index + 1,
}))
const state = reactive({
selectedLabels: [] as string[],
})
const normalizeToLabels = (value: unknown) => {
if (Array.isArray(value)) {
return value.filter((item): item is string => typeof item === 'string' && DEVICE_CAPABILITY_LABELS.includes(item))
}
if (typeof value === 'number') {
return parseDeviceCapability(`${value}`)
}
if (typeof value === 'string') {
const trimmed = value.trim()
if (!trimmed) return []
if (/^[01,\s]+$/.test(trimmed) || /^\d+$/.test(trimmed)) {
return parseDeviceCapability(trimmed)
}
return trimmed
.split(/[、,,]/)
.map((item) => item.trim())
.filter((item) => DEVICE_CAPABILITY_LABELS.includes(item))
}
return []
}
const formatOutputValue = (labels: string[]) => {
if (props.outputFormat === 'labels') {
return labels
}
return formatDeviceCapability(labels, props.outputFormat)
}
const syncSelectedLabels = (value: unknown) => {
state.selectedLabels = normalizeToLabels(value)
}
const handleChange = (labels: string[]) => {
const formattedValue = formatOutputValue(labels)
selectValue.value = formattedValue
emit('change', formattedValue)
}
watch(
selectValue,
(newVal, oldVal) => {
if (newVal === oldVal && state.selectedLabels.length) return
syncSelectedLabels(newVal)
},
{ immediate: true, deep: true },
)
</script>
+2
View File
@@ -28,6 +28,7 @@ import TagUserStatus from "/@/components/Tag/TagUserStatus.vue";
import SelectArea from "/@/components/Select/SelectArea.vue";
import SelectDevice from "/@/components/Select/SelectDevice.vue";
import SelectDeviceCapability from "/@/components/Select/SelectDeviceCapability.vue";
import SelectDeviceGroup from "/@/components/Select/SelectDeviceGroup.vue";
import SelectDuty from "/@/components/Select/SelectDuty.vue";
import SelectDutyType from "/@/components/Select/SelectDutyType.vue";
@@ -99,6 +100,7 @@ export default {
app.component("SelectArea", SelectArea);
app.component("SelectDevice", SelectDevice);
app.component("SelectDeviceCapability", SelectDeviceCapability);
app.component("SelectDeviceGroup", SelectDeviceGroup);
app.component("SelectDuty", SelectDuty);
app.component("SelectDutyType", SelectDutyType);
+54 -36
View File
@@ -61,42 +61,43 @@ export function timeDiffBySeconds(seconds: number | string) {
* 3) 十进制字符串,例如 "12345";将自动转换为二进制位图
* - 输出:返回启用能力对应的中文名称数组(仅包含位为 1 的能力)
*/
export const DEVICE_CAPABILITY_LABELS: string[] = [
"智能报警",
"鱼眼",
"电动聚焦",
"红外测温",
"热度图统计",
"过线统计",
"区域人数统计",
"人脸检测",
"人员识别",
"目标抓拍",
"主从跟踪",
"出入口抓拍",
"AR",
"多区域人数统计",
"雷球联动",
"行为分析",
"云台控制",
"保留位",
"扬声器",
"智能算法升级",
"MAC采集",
"自定义定时抓图",
"人脸开门",
"工装检测",
"人脸质量检测",
"人脸相似度比对",
"客流定时订阅",
"人脸分析",
"AR后端打标",
"AR广角180°",
"AR广角360°",
"废弃位",
];
export function parseDeviceCapability(capability: string): string[] {
if (!capability) return [];
const labels: string[] = [
"智能报警",
"鱼眼",
"电动聚焦",
"红外测温",
"热度图统计",
"过线统计",
"区域人数统计",
"人脸检测",
"人员识别",
"目标抓拍",
"主从跟踪",
"出入口抓拍",
"AR",
"多区域人数统计",
"雷球联动",
"行为分析",
"云台控制",
"保留位",
"扬声器",
"智能算法升级",
"MAC采集",
"自定义定时抓图",
"人脸开门",
"工装检测",
"人脸质量检测",
"人脸相似度比对",
"客流定时订阅",
"人脸分析",
"AR后端打标",
"AR广角180°",
"AR广角360°",
"废弃位",
];
const trimmed = capability.trim();
let getBitFromRight: (posFromRight: number) => number;
if (trimmed.includes(",")) {
@@ -123,8 +124,25 @@ export function parseDeviceCapability(capability: string): string[] {
return [];
}
const result: string[] = [];
for (let i = 1; i <= labels.length; i++) {
if (getBitFromRight(i) === 1) result.push(labels[i - 1]);
for (let i = 1; i <= DEVICE_CAPABILITY_LABELS.length; i++) {
if (getBitFromRight(i) === 1) result.push(DEVICE_CAPABILITY_LABELS[i - 1]);
}
return result;
}
export function formatDeviceCapability(
labels: string[],
format: "comma" | "binary" | "decimal" = "comma"
) {
const bitList = DEVICE_CAPABILITY_LABELS.map((label) =>
labels.includes(label) ? "1" : "0"
);
if (format === "binary") {
return [...bitList].reverse().join("");
}
if (format === "decimal") {
const binary = [...bitList].reverse().join("").replace(/^0+/, "");
return binary ? BigInt(`0b${binary}`).toString(10) : "0";
}
return bitList.join(",");
}
@@ -36,7 +36,7 @@
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="通道能力" prop="capability">
<el-input v-model="form.capability" type="textarea" :rows="3" placeholder="请输入通道能力集" />
<select-device-capability v-model="form.capability" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
-15
View File
@@ -17,26 +17,11 @@
<el-input v-model="form.orgName" placeholder="请输入组织名称" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="唯一标识码" prop="orgSn">
<el-input v-model="form.orgSn" placeholder="请输入唯一标识码" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="组织类型" prop="orgType">
<dict-select disabled v-model="form.orgType" :options="dh_org_type" placeholder="请选择组织类型" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="状态" prop="stat">
<el-input v-model="form.stat" placeholder="请输入状态" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="排序码" prop="sort">
<el-input v-model="form.sort" placeholder="请输入排序码" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>