index.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. <template>
  2. <a-card title="设备统计" >
  3. <a-row justify="center">
  4. <a-col
  5. v-for="item in state.deviceCount"
  6. :key="item.key"
  7. :xs="12" :sm="12" :md="6" :lg="8" :xl="4"
  8. >
  9. <div class="statistic" >
  10. <span class="count" :style="{color: item.color}" >{{item.value}}</span>
  11. <span>台</span>
  12. </div>
  13. <div class="label" >
  14. {{item.label}}
  15. </div>
  16. </a-col>
  17. </a-row>
  18. </a-card>
  19. <a-card style="margin-top: 20px;">
  20. <a-row justify="space-between" >
  21. <a-col :span="20" >
  22. <a-form layout="inline" >
  23. <a-form-item >
  24. <a-select allowClear style="width: 176px;" v-model:value="searchState.deviceStatus" placeholder="请输入设备状态">
  25. <a-select-option
  26. v-for="item in DeviceContriller.deviceStatus"
  27. :key="item.key"
  28. :value="item.key"
  29. >{{item.name}}</a-select-option>
  30. </a-select>
  31. </a-form-item>
  32. <a-form-item style="width: 400px;">
  33. <a-input-group compact >
  34. <a-select allowClear v-model:value="searchState.searchKey" style="width: 150px;" >
  35. <a-select-option v-for="item in searchList" :key="item.key" :value="item.key">{{item.name}}</a-select-option>
  36. </a-select>
  37. <a-input allowClear v-model:value="searchState.searchValue" style="width: 176px" placeholder="请输入查询值"/>
  38. </a-input-group>
  39. </a-form-item>
  40. <a-form-item><a-button @click="resetFields([])">重置</a-button></a-form-item>
  41. <a-form-item><a-button type="primary" @click="getDevicePage">搜索</a-button> </a-form-item>
  42. </a-form>
  43. </a-col>
  44. <a-col :span="2" >
  45. <a-button type="primary" @click="openModal('add', {})"> + 新增设备</a-button>
  46. </a-col>
  47. </a-row>
  48. <a-table
  49. style="margin-top: 20px;width: 100%;"
  50. :columns="columns"
  51. :data-source="state.dataSource"
  52. :loading="state.loading"
  53. :pagination="searchState"
  54. @change="changePage"
  55. >
  56. <template #bodyCell="{column, record}">
  57. <template v-if="column.key === 'id'" >
  58. <a @click="goDetailPage(record.id)">{{record.id}}</a>
  59. </template>
  60. <template v-if="column.key === 'deviceStatus'" >
  61. <a-tag :color="record.deviceStatus.color" >{{record.deviceStatus.name}}</a-tag>
  62. </template>
  63. <template v-if="column.key === 'deviceNodeType'" >
  64. <a-tag>{{record.deviceNodeType == 'GATEWAY'?'直连类型':'非直连类型'}}</a-tag>
  65. </template>
  66. <template v-if="column.key === 'rtsUrl'" >
  67. {{record.rtsUrl ? '支持': '否'}}
  68. </template>
  69. <template v-if="column.key === 'action'">
  70. <a-space>
  71. <a @click="goDetailPage(record.id)">查看</a>
  72. <a @click="goDebugPage(record.id)" >调试</a>
  73. <a @click="goAnalysisPage(record.id)">分析</a>
  74. <a-popconfirm
  75. title="确实要删除吗?"
  76. ok-text="确定"
  77. cancel-text="取消"
  78. @confirm="delDevice(record.id)"
  79. >
  80. <a href="#">删除</a>
  81. </a-popconfirm>
  82. </a-space>
  83. </template>
  84. </template>
  85. </a-table>
  86. </a-card>
  87. <a-modal
  88. title="单设备注册"
  89. :open="state.visible"
  90. @cancel="state.visible = false"
  91. @ok="ok"
  92. >
  93. <a-form :labelCol="{span: 6}" :wrapperCol="{span: 14}" >
  94. <a-form-item label="所属产品" v-bind="validateInfosDeviceState.modelId" >
  95. <a-select allowClear v-model:value="deviceState.modelId" >
  96. <a-select-option
  97. v-for="item in state.modelList"
  98. :key="item.id"
  99. :value="item.id"
  100. >
  101. {{item.modelLabel}}
  102. </a-select-option>
  103. </a-select>
  104. </a-form-item>
  105. <a-form-item label="设备标识码" v-bind="validateInfosDeviceState.deviceCode" >
  106. <a-input allowClear v-model:value="deviceState.deviceCode" />
  107. </a-form-item>
  108. <a-form-item label="设备名称" >
  109. <a-input allowClear v-model:value="deviceState.deviceLabel" />
  110. </a-form-item>
  111. <a-form-item label="设备认证类型" >
  112. <a-radio-group v-model:value="deviceState.deviceAuthType">
  113. <a-radio value="SECRET">密钥认证</a-radio>
  114. <a-radio value="X509CERT">x509证书</a-radio>
  115. </a-radio-group>
  116. </a-form-item>
  117. <a-form-item label="密匙" v-if="deviceState.deviceAuthType === DeviceAuthTypeEnum.SECRET " >
  118. <a-input-password allowClear v-model:value="deviceState.deviceSecret" />
  119. </a-form-item>
  120. <a-form-item label="确认密匙" v-if="deviceState.deviceAuthType === DeviceAuthTypeEnum.SECRET ">
  121. <a-input-password allowClear v-model:value="deviceState.confirmSecret" />
  122. </a-form-item>
  123. </a-form>
  124. </a-modal>
  125. </template>
  126. <script lang="ts" setup >
  127. import { DeviceContriller, ModelController } from '@/controller/index'
  128. import { onMounted, reactive, ref, toRefs, nextTick, computed, watch } from 'vue'
  129. import { Form, message } from 'ant-design-vue'
  130. import { DeviceAuthTypeEnum } from '@/enum/common'
  131. import { useRoute, useRouter } from 'vue-router'
  132. import { DownOutlined } from '@ant-design/icons-vue'
  133. const useForm = Form.useForm
  134. const router = useRouter()
  135. const modelId = (useRoute().query.modelId || '') as string
  136. console.log('modelId:', modelId)
  137. const columns = [
  138. {
  139. title: '设备ID',
  140. dataIndex: 'id',
  141. key: 'id',
  142. ellipsis: true
  143. },
  144. {
  145. title: '设备名称',
  146. dataIndex: 'deviceLabel',
  147. ellipsis: true
  148. },
  149. {
  150. title: '设备标识码',
  151. dataIndex: 'deviceCode',
  152. ellipsis: true
  153. },
  154. {
  155. title: '设备描述',
  156. dataIndex: 'deviceDescription',
  157. ellipsis: true
  158. },
  159. {
  160. title: '节点类型',
  161. dataIndex: 'deviceNodeType',
  162. key: 'deviceNodeType',
  163. width: 100
  164. },
  165. {
  166. title: '所属产品',
  167. dataIndex: 'modelLabel',
  168. ellipsis: true
  169. },
  170. {
  171. title: '支持live',
  172. dataIndex: 'rtsUrl',
  173. key: 'rtsUrl',
  174. width: 100
  175. },
  176. {
  177. title: '状态',
  178. dataIndex: 'deviceStatus',
  179. key: 'deviceStatus',
  180. ellipsis: true,
  181. width: 80
  182. },
  183. {
  184. title: '操作',
  185. key: 'action'
  186. }
  187. ]
  188. const searchList = [
  189. { name: '设备id', key: 'deviceId' },
  190. { name: '设备名称', key: 'deviceLabel' },
  191. { name: '设备标识码', key: 'deviceCode' }
  192. ]
  193. const analysisList = [
  194. { label: '上下线分析', key: 'session' },
  195. { label: '属性分析', key: 'attr' }
  196. ]
  197. const state = reactive<{
  198. modelId: string,
  199. deviceCount: any,
  200. dataSource: IOT.API.DEVICE.Device[],
  201. loading: boolean,
  202. visible: boolean,
  203. opraState: 'add' | 'update',
  204. modelList: IOT.API.MODEL.ModelDot[]
  205. analysisVisible: boolean
  206. analysisType: 'session' | 'attr' | ''
  207. }>({
  208. modelId: modelId,
  209. deviceCount: [],
  210. dataSource: [],
  211. modelList: [],
  212. loading: false,
  213. visible: false,
  214. analysisVisible: false,
  215. analysisType: '',
  216. opraState: 'add'
  217. })
  218. const searchState = reactive({
  219. page: 1,
  220. pageSize: 10,
  221. total: 0,
  222. deviceStatus: null,
  223. modelId: modelId,
  224. searchKey: 'deviceId',
  225. searchValue: '',
  226. showSizeChanger: false
  227. })
  228. const deviceState = reactive({
  229. id: '',
  230. modelId: '',
  231. deviceLabel: '',
  232. deviceCode: '',
  233. deviceDescription: '',
  234. deviceAuthType: DeviceAuthTypeEnum.SECRET,
  235. modelLabel: '',
  236. deviceSecret: '',
  237. confirmSecret: ''
  238. })
  239. const { resetFields, validate, validateInfos } = useForm(searchState, {})
  240. const { resetFields: resetFieldsDevice, validate: validateDevice, validateInfos: validateInfosDeviceState } = useForm(deviceState, reactive({
  241. modelId: [{ required: true, message: '请选择所属产品' }],
  242. deviceCode: [{ required: true, message: '请填写设备码' }]
  243. }))
  244. const changePage = ({ current }) => {
  245. searchState.page = current
  246. getDevicePage()
  247. }
  248. const goDebugPage = (id: string) => {
  249. router.push({ path: '/devOps/onlineTest', query: { id } })
  250. }
  251. const goDetailPage = (id: string) => {
  252. router.push({ path: '/device/detail', query: { id } })
  253. }
  254. const delDevice = async (id: string) => {
  255. await DeviceContriller.del(id)
  256. getDevicePage()
  257. }
  258. const goAnalysisPage = (id: string) => {
  259. router.push({ path: '/device/analysis', query: { id } })
  260. }
  261. const ok = () => {
  262. if (deviceState.deviceAuthType === DeviceAuthTypeEnum.SECRET && deviceState.deviceSecret !== deviceState.confirmSecret) {
  263. message.warn('两次密匙输入不同')
  264. return
  265. }
  266. validateDevice().then(async (r) => {
  267. await DeviceContriller.post(deviceState)
  268. state.visible = false
  269. getDevicePage()
  270. })
  271. }
  272. const getModelList = async () => {
  273. const { data } = await ModelController.list()
  274. state.modelList = data
  275. }
  276. const openModal = (opraState: 'add' | 'update', record:Partial<IOT.API.DEVICE.Device> = {}) => {
  277. state.visible = true
  278. state.opraState = opraState
  279. getModelList()
  280. resetFieldsDevice({})
  281. }
  282. const getDevicePage = async () => {
  283. state.loading = true
  284. const { data, sum } = await DeviceContriller.page(searchState)
  285. state.loading = false
  286. state.dataSource = data
  287. searchState.total = sum
  288. }
  289. const getDeviceStatistics = async () => {
  290. const data = await DeviceContriller.statistics({ modelId: state.modelId })
  291. state.deviceCount = data as any
  292. }
  293. onMounted(() => {
  294. getDeviceStatistics()
  295. getDevicePage()
  296. })
  297. </script>
  298. <style lang="less" scoped >
  299. @import '~@/styles/theme.less';
  300. .statistic {
  301. color: @sublabel-color;
  302. .count {
  303. font-size: 48px;
  304. margin-right: 10px;
  305. }
  306. }
  307. .label {
  308. font-size: 18px;
  309. color: @sublabel-color;
  310. }
  311. </style>