From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/views/iot/rule/scene/form/inputs/JsonParamsInput.vue |  519 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 519 insertions(+), 0 deletions(-)

diff --git a/src/views/iot/rule/scene/form/inputs/JsonParamsInput.vue b/src/views/iot/rule/scene/form/inputs/JsonParamsInput.vue
new file mode 100644
index 0000000..5bfa970
--- /dev/null
+++ b/src/views/iot/rule/scene/form/inputs/JsonParamsInput.vue
@@ -0,0 +1,519 @@
+<!-- JSON鍙傛暟杈撳叆缁勪欢 - 閫氱敤鐗堟湰 -->
+<template>
+  <!-- 鍙傛暟閰嶇疆 -->
+  <div class="w-full space-y-12px">
+    <!-- JSON 杈撳叆妗� -->
+    <div class="relative">
+      <el-input
+        v-model="paramsJson"
+        type="textarea"
+        :rows="4"
+        :placeholder="placeholder"
+        @input="handleParamsChange"
+        :class="{ 'is-error': jsonError }"
+      />
+      <!-- 鏌ョ湅璇︾粏绀轰緥寮瑰嚭灞� -->
+      <div class="absolute top-8px right-8px">
+        <el-popover
+          placement="left-start"
+          :width="450"
+          trigger="click"
+          :show-arrow="true"
+          :offset="8"
+          popper-class="json-params-detail-popover"
+        >
+          <template #reference>
+            <el-button
+              type="info"
+              :icon="InfoFilled"
+              circle
+              size="small"
+              :title="JSON_PARAMS_INPUT_CONSTANTS.VIEW_EXAMPLE_TITLE"
+            />
+          </template>
+
+          <!-- 寮瑰嚭灞傚唴瀹� -->
+          <div class="json-params-detail-content">
+            <div class="flex items-center gap-8px mb-16px">
+              <Icon :icon="titleIcon" class="text-[var(--el-color-primary)] text-18px" />
+              <span class="text-16px font-600 text-[var(--el-text-color-primary)]">
+                {{ title }}
+              </span>
+            </div>
+
+            <div class="space-y-16px">
+              <!-- 鍙傛暟鍒楄〃 -->
+              <div v-if="paramsList.length > 0">
+                <div class="flex items-center gap-8px mb-8px">
+                  <Icon :icon="paramsIcon" class="text-[var(--el-color-primary)] text-14px" />
+                  <span class="text-14px font-500 text-[var(--el-text-color-primary)]">
+                    {{ paramsLabel }}
+                  </span>
+                </div>
+                <div class="ml-22px space-y-8px">
+                  <div
+                    v-for="param in paramsList"
+                    :key="param.identifier"
+                    class="flex items-center justify-between p-8px bg-[var(--el-fill-color-lighter)] rounded-4px"
+                  >
+                    <div class="flex-1">
+                      <div class="text-12px font-500 text-[var(--el-text-color-primary)]">
+                        {{ param.name }}
+                        <el-tag v-if="param.required" size="small" type="danger" class="ml-4px">
+                          {{ JSON_PARAMS_INPUT_CONSTANTS.REQUIRED_TAG }}
+                        </el-tag>
+                      </div>
+                      <div class="text-11px text-[var(--el-text-color-secondary)]">
+                        {{ param.identifier }}
+                      </div>
+                    </div>
+                    <div class="flex items-center gap-8px">
+                      <el-tag :type="getParamTypeTag(param.dataType)" size="small">
+                        {{ getParamTypeName(param.dataType) }}
+                      </el-tag>
+                      <span class="text-11px text-[var(--el-text-color-secondary)]">
+                        {{ getExampleValue(param) }}
+                      </span>
+                    </div>
+                  </div>
+                </div>
+
+                <div class="mt-12px ml-22px">
+                  <div class="text-12px text-[var(--el-text-color-secondary)] mb-6px">
+                    {{ JSON_PARAMS_INPUT_CONSTANTS.COMPLETE_JSON_FORMAT }}
+                  </div>
+                  <pre
+                    class="p-12px bg-[var(--el-fill-color-light)] rounded-4px text-11px text-[var(--el-text-color-primary)] overflow-x-auto border-l-3px border-[var(--el-color-primary)]"
+                  >
+                      <code>{{ generateExampleJson() }}</code>
+                    </pre>
+                </div>
+              </div>
+
+              <!-- 鏃犲弬鏁版彁绀� -->
+              <div v-else>
+                <div class="text-center py-16px">
+                  <p class="text-14px text-[var(--el-text-color-secondary)]">{{ emptyMessage }}</p>
+                </div>
+              </div>
+            </div>
+          </div>
+        </el-popover>
+      </div>
+    </div>
+
+    <!-- 楠岃瘉鐘舵�佸拰閿欒鎻愮ず -->
+    <div class="flex items-center justify-between">
+      <div class="flex items-center gap-8px">
+        <Icon
+          :icon="
+            jsonError
+              ? JSON_PARAMS_INPUT_ICONS.STATUS_ICONS.ERROR
+              : JSON_PARAMS_INPUT_ICONS.STATUS_ICONS.SUCCESS
+          "
+          :class="jsonError ? 'text-[var(--el-color-danger)]' : 'text-[var(--el-color-success)]'"
+          class="text-14px"
+        />
+        <span
+          :class="jsonError ? 'text-[var(--el-color-danger)]' : 'text-[var(--el-color-success)]'"
+          class="text-12px"
+        >
+          {{ jsonError || JSON_PARAMS_INPUT_CONSTANTS.JSON_FORMAT_CORRECT }}
+        </span>
+      </div>
+
+      <!-- 蹇�熷~鍏呮寜閽� -->
+      <div v-if="paramsList.length > 0" class="flex items-center gap-8px">
+        <span class="text-12px text-[var(--el-text-color-secondary)]">{{
+          JSON_PARAMS_INPUT_CONSTANTS.QUICK_FILL_LABEL
+        }}</span>
+        <el-button size="small" type="primary" plain @click="fillExampleJson">
+          {{ JSON_PARAMS_INPUT_CONSTANTS.EXAMPLE_DATA_BUTTON }}
+        </el-button>
+        <el-button size="small" type="danger" plain @click="clearParams">{{
+          JSON_PARAMS_INPUT_CONSTANTS.CLEAR_BUTTON
+        }}</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { useVModel } from '@vueuse/core'
+import { InfoFilled } from '@element-plus/icons-vue'
+import {
+  IoTDataSpecsDataTypeEnum,
+  JSON_PARAMS_INPUT_CONSTANTS,
+  JSON_PARAMS_INPUT_ICONS,
+  JSON_PARAMS_EXAMPLE_VALUES,
+  JsonParamsInputTypeEnum,
+  type JsonParamsInputType
+} from '@/views/iot/utils/constants'
+
+/** JSON鍙傛暟杈撳叆缁勪欢 - 閫氱敤鐗堟湰 */
+defineOptions({ name: 'JsonParamsInput' })
+
+interface JsonParamsConfig {
+  // 鏈嶅姟閰嶇疆
+  service?: {
+    name: string
+    inputParams?: any[]
+  }
+  // 浜嬩欢閰嶇疆
+  event?: {
+    name: string
+    outputParams?: any[]
+  }
+  // 灞炴�ч厤缃�
+  properties?: any[]
+  // 鑷畾涔夐厤缃�
+  custom?: {
+    name: string
+    params: any[]
+  }
+}
+
+interface Props {
+  modelValue?: string
+  config?: JsonParamsConfig
+  type?: JsonParamsInputType
+  placeholder?: string
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: string): void
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  type: JsonParamsInputTypeEnum.SERVICE,
+  placeholder: JSON_PARAMS_INPUT_CONSTANTS.PLACEHOLDER
+})
+
+const emit = defineEmits<Emits>()
+
+const localValue = useVModel(props, 'modelValue', emit, {
+  defaultValue: ''
+})
+
+const paramsJson = ref('') // JSON鍙傛暟瀛楃涓�
+const jsonError = ref('') // JSON楠岃瘉閿欒淇℃伅
+
+// 璁$畻灞炴�э細鍙傛暟鍒楄〃
+const paramsList = computed(() => {
+  switch (props.type) {
+    case JsonParamsInputTypeEnum.SERVICE:
+      return props.config?.service?.inputParams || []
+    case JsonParamsInputTypeEnum.EVENT:
+      return props.config?.event?.outputParams || []
+    case JsonParamsInputTypeEnum.PROPERTY:
+      return props.config?.properties || []
+    case JsonParamsInputTypeEnum.CUSTOM:
+      return props.config?.custom?.params || []
+    default:
+      return []
+  }
+})
+
+// 璁$畻灞炴�э細鏍囬
+const title = computed(() => {
+  switch (props.type) {
+    case JsonParamsInputTypeEnum.SERVICE:
+      return JSON_PARAMS_INPUT_CONSTANTS.TITLES.SERVICE(props.config?.service?.name)
+    case JsonParamsInputTypeEnum.EVENT:
+      return JSON_PARAMS_INPUT_CONSTANTS.TITLES.EVENT(props.config?.event?.name)
+    case JsonParamsInputTypeEnum.PROPERTY:
+      return JSON_PARAMS_INPUT_CONSTANTS.TITLES.PROPERTY
+    case JsonParamsInputTypeEnum.CUSTOM:
+      return JSON_PARAMS_INPUT_CONSTANTS.TITLES.CUSTOM(props.config?.custom?.name)
+    default:
+      return JSON_PARAMS_INPUT_CONSTANTS.TITLES.DEFAULT
+  }
+})
+
+// 璁$畻灞炴�э細鏍囬鍥炬爣
+const titleIcon = computed(() => {
+  switch (props.type) {
+    case JsonParamsInputTypeEnum.SERVICE:
+      return JSON_PARAMS_INPUT_ICONS.TITLE_ICONS.SERVICE
+    case JsonParamsInputTypeEnum.EVENT:
+      return JSON_PARAMS_INPUT_ICONS.TITLE_ICONS.EVENT
+    case JsonParamsInputTypeEnum.PROPERTY:
+      return JSON_PARAMS_INPUT_ICONS.TITLE_ICONS.PROPERTY
+    case JsonParamsInputTypeEnum.CUSTOM:
+      return JSON_PARAMS_INPUT_ICONS.TITLE_ICONS.CUSTOM
+    default:
+      return JSON_PARAMS_INPUT_ICONS.TITLE_ICONS.DEFAULT
+  }
+})
+
+// 璁$畻灞炴�э細鍙傛暟鍥炬爣
+const paramsIcon = computed(() => {
+  switch (props.type) {
+    case JsonParamsInputTypeEnum.SERVICE:
+      return JSON_PARAMS_INPUT_ICONS.PARAMS_ICONS.SERVICE
+    case JsonParamsInputTypeEnum.EVENT:
+      return JSON_PARAMS_INPUT_ICONS.PARAMS_ICONS.EVENT
+    case JsonParamsInputTypeEnum.PROPERTY:
+      return JSON_PARAMS_INPUT_ICONS.PARAMS_ICONS.PROPERTY
+    case JsonParamsInputTypeEnum.CUSTOM:
+      return JSON_PARAMS_INPUT_ICONS.PARAMS_ICONS.CUSTOM
+    default:
+      return JSON_PARAMS_INPUT_ICONS.PARAMS_ICONS.DEFAULT
+  }
+})
+
+// 璁$畻灞炴�э細鍙傛暟鏍囩
+const paramsLabel = computed(() => {
+  switch (props.type) {
+    case JsonParamsInputTypeEnum.SERVICE:
+      return JSON_PARAMS_INPUT_CONSTANTS.PARAMS_LABELS.SERVICE
+    case JsonParamsInputTypeEnum.EVENT:
+      return JSON_PARAMS_INPUT_CONSTANTS.PARAMS_LABELS.EVENT
+    case JsonParamsInputTypeEnum.PROPERTY:
+      return JSON_PARAMS_INPUT_CONSTANTS.PARAMS_LABELS.PROPERTY
+    case JsonParamsInputTypeEnum.CUSTOM:
+      return JSON_PARAMS_INPUT_CONSTANTS.PARAMS_LABELS.CUSTOM
+    default:
+      return JSON_PARAMS_INPUT_CONSTANTS.PARAMS_LABELS.DEFAULT
+  }
+})
+
+// 璁$畻灞炴�э細绌虹姸鎬佹秷鎭�
+const emptyMessage = computed(() => {
+  switch (props.type) {
+    case JsonParamsInputTypeEnum.SERVICE:
+      return JSON_PARAMS_INPUT_CONSTANTS.EMPTY_MESSAGES.SERVICE
+    case JsonParamsInputTypeEnum.EVENT:
+      return JSON_PARAMS_INPUT_CONSTANTS.EMPTY_MESSAGES.EVENT
+    case JsonParamsInputTypeEnum.PROPERTY:
+      return JSON_PARAMS_INPUT_CONSTANTS.EMPTY_MESSAGES.PROPERTY
+    case JsonParamsInputTypeEnum.CUSTOM:
+      return JSON_PARAMS_INPUT_CONSTANTS.EMPTY_MESSAGES.CUSTOM
+    default:
+      return JSON_PARAMS_INPUT_CONSTANTS.EMPTY_MESSAGES.DEFAULT
+  }
+})
+
+// 璁$畻灞炴�э細鏃犻厤缃秷鎭�
+const noConfigMessage = computed(() => {
+  switch (props.type) {
+    case JsonParamsInputTypeEnum.SERVICE:
+      return JSON_PARAMS_INPUT_CONSTANTS.NO_CONFIG_MESSAGES.SERVICE
+    case JsonParamsInputTypeEnum.EVENT:
+      return JSON_PARAMS_INPUT_CONSTANTS.NO_CONFIG_MESSAGES.EVENT
+    case JsonParamsInputTypeEnum.PROPERTY:
+      return JSON_PARAMS_INPUT_CONSTANTS.NO_CONFIG_MESSAGES.PROPERTY
+    case JsonParamsInputTypeEnum.CUSTOM:
+      return JSON_PARAMS_INPUT_CONSTANTS.NO_CONFIG_MESSAGES.CUSTOM
+    default:
+      return JSON_PARAMS_INPUT_CONSTANTS.NO_CONFIG_MESSAGES.DEFAULT
+  }
+})
+
+/**
+ * 澶勭悊鍙傛暟鍙樺寲浜嬩欢
+ */
+const handleParamsChange = () => {
+  try {
+    jsonError.value = '' // 娓呴櫎涔嬪墠鐨勯敊璇�
+
+    if (paramsJson.value.trim()) {
+      const parsed = JSON.parse(paramsJson.value)
+      localValue.value = paramsJson.value
+
+      // 棰濆鐨勫弬鏁伴獙璇�
+      if (typeof parsed !== 'object' || parsed === null) {
+        jsonError.value = JSON_PARAMS_INPUT_CONSTANTS.PARAMS_MUST_BE_OBJECT
+        return
+      }
+
+      // 楠岃瘉蹇呭~鍙傛暟
+      for (const param of paramsList.value) {
+        if (param.required && (!parsed[param.identifier] || parsed[param.identifier] === '')) {
+          jsonError.value = JSON_PARAMS_INPUT_CONSTANTS.PARAM_REQUIRED_ERROR(param.name)
+          return
+        }
+      }
+    } else {
+      localValue.value = ''
+    }
+
+    // 楠岃瘉閫氳繃
+    jsonError.value = ''
+  } catch (error) {
+    jsonError.value = JSON_PARAMS_INPUT_CONSTANTS.JSON_FORMAT_ERROR(
+      error instanceof Error ? error.message : JSON_PARAMS_INPUT_CONSTANTS.UNKNOWN_ERROR
+    )
+  }
+}
+
+/**
+ * 蹇�熷~鍏呯ず渚嬫暟鎹�
+ */
+const fillExampleJson = () => {
+  paramsJson.value = generateExampleJson()
+  handleParamsChange()
+}
+
+/**
+ * 娓呯┖鍙傛暟
+ */
+const clearParams = () => {
+  paramsJson.value = ''
+  localValue.value = ''
+  jsonError.value = ''
+}
+
+/**
+ * 鑾峰彇鍙傛暟绫诲瀷鍚嶇О
+ * @param dataType 鏁版嵁绫诲瀷
+ * @returns 绫诲瀷鍚嶇О
+ */
+const getParamTypeName = (dataType: string) => {
+  // 浣跨敤 constants.ts 涓凡鏈夌殑 getDataTypeName 鍑芥暟閫昏緫
+  const typeMap = {
+    [IoTDataSpecsDataTypeEnum.INT]: '鏁存暟',
+    [IoTDataSpecsDataTypeEnum.FLOAT]: '娴偣鏁�',
+    [IoTDataSpecsDataTypeEnum.DOUBLE]: '鍙岀簿搴�',
+    [IoTDataSpecsDataTypeEnum.TEXT]: '瀛楃涓�',
+    [IoTDataSpecsDataTypeEnum.BOOL]: '甯冨皵鍊�',
+    [IoTDataSpecsDataTypeEnum.ENUM]: '鏋氫妇',
+    [IoTDataSpecsDataTypeEnum.DATE]: '鏃ユ湡',
+    [IoTDataSpecsDataTypeEnum.STRUCT]: '缁撴瀯浣�',
+    [IoTDataSpecsDataTypeEnum.ARRAY]: '鏁扮粍'
+  }
+  return typeMap[dataType] || dataType
+}
+
+/**
+ * 鑾峰彇鍙傛暟绫诲瀷鏍囩鏍峰紡
+ * @param dataType 鏁版嵁绫诲瀷
+ * @returns 鏍囩鏍峰紡
+ */
+const getParamTypeTag = (dataType: string) => {
+  const tagMap = {
+    [IoTDataSpecsDataTypeEnum.INT]: 'primary',
+    [IoTDataSpecsDataTypeEnum.FLOAT]: 'success',
+    [IoTDataSpecsDataTypeEnum.DOUBLE]: 'success',
+    [IoTDataSpecsDataTypeEnum.TEXT]: 'info',
+    [IoTDataSpecsDataTypeEnum.BOOL]: 'warning',
+    [IoTDataSpecsDataTypeEnum.ENUM]: 'danger',
+    [IoTDataSpecsDataTypeEnum.DATE]: 'primary',
+    [IoTDataSpecsDataTypeEnum.STRUCT]: 'info',
+    [IoTDataSpecsDataTypeEnum.ARRAY]: 'warning'
+  }
+  return tagMap[dataType] || 'info'
+}
+
+/**
+ * 鑾峰彇绀轰緥鍊�
+ * @param param 鍙傛暟瀵硅薄
+ * @returns 绀轰緥鍊�
+ */
+const getExampleValue = (param: any) => {
+  const exampleConfig =
+    JSON_PARAMS_EXAMPLE_VALUES[param.dataType] || JSON_PARAMS_EXAMPLE_VALUES.DEFAULT
+  return exampleConfig.display
+}
+
+/**
+ * 鐢熸垚绀轰緥JSON
+ * @returns JSON瀛楃涓�
+ */
+const generateExampleJson = () => {
+  if (paramsList.value.length === 0) {
+    return '{}'
+  }
+
+  const example = {}
+  paramsList.value.forEach((param) => {
+    const exampleConfig =
+      JSON_PARAMS_EXAMPLE_VALUES[param.dataType] || JSON_PARAMS_EXAMPLE_VALUES.DEFAULT
+    example[param.identifier] = exampleConfig.value
+  })
+
+  return JSON.stringify(example, null, 2)
+}
+
+/**
+ * 澶勭悊鏁版嵁鍥炴樉
+ * @param value 鍊煎瓧绗︿覆
+ */
+const handleDataDisplay = (value: string) => {
+  if (!value || !value.trim()) {
+    paramsJson.value = ''
+    jsonError.value = ''
+    return
+  }
+
+  try {
+    // 灏濊瘯瑙f瀽JSON锛屽鏋滄垚鍔熷垯鏍煎紡鍖�
+    const parsed = JSON.parse(value)
+    paramsJson.value = JSON.stringify(parsed, null, 2)
+    jsonError.value = ''
+  } catch {
+    // 濡傛灉涓嶆槸鏈夋晥鐨凧SON锛岀洿鎺ヤ娇鐢ㄥ師瀛楃涓�
+    paramsJson.value = value
+    jsonError.value = ''
+  }
+}
+
+// 鐩戝惉澶栭儴鍊煎彉鍖栵紙缂栬緫妯″紡鏁版嵁鍥炴樉锛�
+watch(
+  () => localValue.value,
+  async (newValue, oldValue) => {
+    // 閬垮厤寰幆鏇存柊
+    if (newValue === oldValue) return
+
+    // 浣跨敤 nextTick 纭繚鍦ㄤ笅涓�涓� tick 涓鐞嗘暟鎹�
+    await nextTick()
+    handleDataDisplay(newValue || '')
+  },
+  { immediate: true }
+)
+
+// 缁勪欢鎸傝浇鍚庝篃灏濊瘯澶勭悊涓�娆℃暟鎹洖鏄�
+onMounted(async () => {
+  await nextTick()
+  if (localValue.value) {
+    handleDataDisplay(localValue.value)
+  }
+})
+
+// 鐩戝惉閰嶇疆鍙樺寲
+watch(
+  () => props.config,
+  (newConfig, oldConfig) => {
+    // 鍙湁鍦ㄩ厤缃湡姝e彉鍖栨椂鎵嶆竻绌烘暟鎹�
+    if (JSON.stringify(newConfig) !== JSON.stringify(oldConfig)) {
+      // 濡傛灉娌℃湁澶栭儴浼犲叆鐨勫�硷紝鎵嶆竻绌烘暟鎹�
+      if (!localValue.value) {
+        paramsJson.value = ''
+        jsonError.value = ''
+      }
+    }
+  }
+)
+</script>
+
+<style scoped>
+/* 寮瑰嚭灞傚唴瀹规牱寮� */
+.json-params-detail-content {
+  padding: 4px 0;
+}
+
+/* 寮瑰嚭灞傝嚜瀹氫箟鏍峰紡 */
+:global(.json-params-detail-popover) {
+  max-width: 500px !important;
+}
+
+:global(.json-params-detail-popover .el-popover__content) {
+  padding: 16px !important;
+}
+
+/* JSON 浠g爜鍧楁牱寮� */
+.json-params-detail-content pre {
+  max-height: 200px;
+  overflow-y: auto;
+}
+</style>

--
Gitblit v1.8.0