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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref } from 'vue';
 
import { IconifyIcon } from '..\..\..\..\..\..\packages\icons\src';
import { isEmpty } from '..\..\..\..\..\..\packages\utils\src';
 
import { useClipboard, useVModel } from '@vueuse/core';
import { Button, Form, Input, message } from 'ant-design-vue';
 
import { IotDataSinkTypeEnum } from '#/api/iot/rule/data/sink';
 
const props = defineProps<{ modelValue: any }>();
 
const emit = defineEmits(['update:modelValue']);
 
const TABLE_SQL = `CREATE TABLE iot_device_message_sink (
    id VARCHAR(64) NOT NULL COMMENT '消息ID',
    device_id BIGINT NOT NULL COMMENT '设备编号',
    tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号',
    method VARCHAR(128) COMMENT '请求方法',
    report_time DATETIME COMMENT '上报时间',
    data TEXT COMMENT '完整消息JSON',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (id) USING BTREE,
    INDEX idx_create_time (create_time ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'IoT 设备消息流转目标表';`;
 
const config = useVModel(props, 'modelValue', emit);
 
const showSqlTip = ref(false);
const copied = ref(false);
const { copy } = useClipboard();
let copyResetTimer: null | ReturnType<typeof setTimeout> = null;
 
async function handleCopySql() {
  await copy(TABLE_SQL);
  copied.value = true;
  message.success('建表 SQL 已复制到剪贴板');
  if (copyResetTimer) {
    clearTimeout(copyResetTimer);
  }
  copyResetTimer = setTimeout(() => {
    copied.value = false;
    copyResetTimer = null;
  }, 2000);
}
 
onBeforeUnmount(() => {
  if (copyResetTimer) {
    clearTimeout(copyResetTimer);
    copyResetTimer = null;
  }
});
 
onMounted(() => {
  if (!isEmpty(config.value)) {
    return;
  }
  config.value = {
    type: `${IotDataSinkTypeEnum.DATABASE}`,
    jdbcUrl: '',
    username: '',
    password: '',
    tableName: 'iot_device_message_sink',
  };
});
</script>
 
<template>
  <Form.Item
    :name="['config', 'jdbcUrl']"
    :rules="[
      { required: true, message: 'JDBC 连接地址不能为空', trigger: 'blur' },
    ]"
    label="JDBC 地址"
  >
    <Input
      v-model:value="config.jdbcUrl"
      placeholder="请输入 JDBC 连接地址,如:jdbc:mysql://localhost:3306/iot_data"
    />
  </Form.Item>
  <Form.Item
    :name="['config', 'username']"
    :rules="[{ required: true, message: '用户名不能为空', trigger: 'blur' }]"
    label="用户名"
  >
    <Input v-model:value="config.username" placeholder="请输入数据库用户名" />
  </Form.Item>
  <Form.Item
    :name="['config', 'password']"
    :rules="[{ required: true, message: '密码不能为空', trigger: 'blur' }]"
    label="密码"
  >
    <Input.Password
      v-model:value="config.password"
      placeholder="请输入数据库密码"
    />
  </Form.Item>
  <Form.Item
    :name="['config', 'tableName']"
    :rules="[{ required: true, message: '目标表名不能为空', trigger: 'blur' }]"
    label="目标表名"
  >
    <div class="flex items-center gap-3">
      <Input
        v-model:value="config.tableName"
        placeholder="目标表名"
        class="w-[240px]"
      />
      <Button type="link" @click="showSqlTip = !showSqlTip">
        <IconifyIcon
          :icon="showSqlTip ? 'lucide:chevron-up' : 'lucide:file-text'"
          class="mr-1"
        />
        {{ showSqlTip ? '收起表结构提示' : '查看表结构提示' }}
      </Button>
    </div>
  </Form.Item>
  <div
    v-if="showSqlTip"
    class="mt-2 overflow-hidden rounded border border-gray-200 dark:border-gray-700"
  >
    <div
      class="flex items-center justify-between bg-gray-100 px-3 py-2 dark:bg-gray-800"
    >
      <span class="text-xs text-gray-600 dark:text-gray-300">
        目标数据库需包含以下结构的表,才能正常接收数据流转的消息
      </span>
      <Button size="small" @click="handleCopySql">
        <IconifyIcon
          :icon="copied ? 'lucide:check' : 'lucide:copy'"
          class="mr-1"
        />
        {{ copied ? '已复制' : '复制 SQL' }}
      </Button>
    </div>
    <pre
      class="m-0 overflow-x-auto bg-gray-50 p-3 font-mono text-[12px] leading-normal text-gray-800 dark:bg-gray-900 dark:text-gray-200"
    ><code>{{ TABLE_SQL }}</code></pre>
  </div>
</template>