diff --git a/src/api/device-manage/device/cloud-command.ts b/src/api/device-manage/device/cloud-command.ts
deleted file mode 100644
index 47d2f2b4..00000000
--- a/src/api/device-manage/device/cloud-command.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { CommandToDeviceData, GetCloudCommandLogsParams, MessageToDeviceData } from './types'
-import { defHttp } from '@/utils/http/axios'
-
-export function getCloudCommandLogs(params: GetCloudCommandLogsParams) {
-  return defHttp.get({
-    url: '/cloud/logPage',
-    params,
-  })
-}
-
-export function getMessageContent(id: string) {
-  return defHttp.get({
-    url: '/cloud/message',
-    params: {
-      id,
-    },
-  })
-}
-
-export function sendMessageToDevice(data: MessageToDeviceData) {
-  return defHttp.post({
-    url: '/cloud/sendMessage',
-    data,
-  })
-}
-
-export function sendCommandToDevice(data: CommandToDeviceData) {
-  return defHttp.post({
-    url: '/cloud/sendCommand',
-    data,
-  })
-}
diff --git a/src/api/device-manage/device/index.ts b/src/api/device-manage/device/index.ts
deleted file mode 100644
index 5682289e..00000000
--- a/src/api/device-manage/device/index.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import type { Device, DevicePropertie, GetDeviceListParams } from './types'
-import type { Topic } from '@/api/product/types'
-import { defHttp } from '@/utils/http/axios'
-
-export function getDeviceList(params: GetDeviceListParams) {
-  return defHttp.get<PageResult<Device>>({
-    url: '/device/page',
-    params,
-  })
-}
-
-export function createDevice(data: Partial<Device>) {
-  return defHttp.post({
-    url: '/device/save',
-    data,
-  })
-}
-
-export function updateDevice(data: Partial<Device>) {
-  return defHttp.post({
-    url: '/device/update',
-    data,
-  })
-}
-
-export function deleteDevice(id: string) {
-  return defHttp.post({
-    url: `/device/remove?id=${id}`,
-  })
-}
-
-export function getDeviceDetail(id: string) {
-  return defHttp.get<Device>({
-    url: '/device/detail',
-    params: {
-      id,
-    },
-  })
-}
-
-export function getDeviceProperties(modelId: string, deviceSn: string) {
-  return defHttp.get<{
-    properties?: DevicePropertie[]
-    updateTime?: string
-  }>({
-    url: '/device/properties',
-    params: {
-      deviceSn,
-      modelId,
-    },
-  })
-}
-
-export function getDeviceTopicList(params: PageParam & { deviceId: string }) {
-  return defHttp.get<PageResult<Topic>>({
-    url: '/device/topic/page',
-    params,
-  })
-}
-
-export function getMqttConnectParams(deviceId: string) {
-  return defHttp.get({
-    url: '/device/mqttLinkInfo',
-    params: {
-      deviceId,
-    },
-  })
-}
-
-export function getReportExample(productId: string, deviceSn: string) {
-  return defHttp.get({
-    url: '/device/messageExample',
-    params: {
-      productId,
-      deviceSn,
-    },
-  })
-}
diff --git a/src/api/device-manage/device/types.ts b/src/api/device-manage/device/types.ts
deleted file mode 100644
index 91667704..00000000
--- a/src/api/device-manage/device/types.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-export interface GetDeviceListParams extends PageParam {
-  productId?: string
-  deviceSn?: string
-  deviceName?: string
-  isOnline?: BooleanFlag
-}
-
-export interface Device {
-  id: string
-  productId: string
-  deviceSn: string
-  deviceName: string
-  deviceDesc: string
-  isOnline: BooleanFlag
-}
-
-export interface DevicePropertie {
-  identifier: string
-  name: string
-  unit: string
-  value: string
-  sort: number
-}
-
-export interface GetCloudCommandLogsParams extends PageParam {
-  messageType?: number
-}
-
-export enum CloudCommandType {
-  Attribute = 1,
-  Command = 2,
-  Message = 3,
-}
-
-export interface MessageToDeviceData {
-  deviceId: string
-  topic: string
-  message: string
-}
-
-export interface CommandToDeviceData {
-  deviceId: string
-  modelId: string
-  itemId: string
-  value: string | number
-}
diff --git a/src/api/device-manage/group/index.ts b/src/api/device-manage/group/index.ts
deleted file mode 100644
index a13f6c55..00000000
--- a/src/api/device-manage/group/index.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import type { DeviceGroup, GetdeviceListByGroupParams } from './types'
-import { defHttp } from '@/utils/http/axios'
-
-export function getDeviceGroupTree() {
-  return defHttp.get({
-    url: '/deviceGroup/tree',
-  })
-}
-
-export function getDevicegroupDetail(id: string) {
-  return defHttp.get({
-    url: '/deviceGroup/detail',
-    params: {
-      id,
-    },
-  })
-}
-
-export function getDeviceListByGroup(params: GetdeviceListByGroupParams) {
-  return defHttp.get({
-    url: '/device/pageByGroup',
-    params,
-  })
-}
-
-export function createDeviceGroup(data: Partial<DeviceGroup>) {
-  return defHttp.post({
-    url: '/deviceGroup/save',
-    data,
-  })
-}
-
-export function updateDeviceGroup(data: Partial<DeviceGroup>) {
-  return defHttp.post({
-    url: '/deviceGroup/update',
-    data,
-  })
-}
-
-export function deleteDevicegroup(id: string) {
-  return defHttp.post({
-    url: `/deviceGroup/remove?id=${id}`,
-  })
-}
-
-export function bindingDeviceToGroup(deviceGroupId: string, deviceIds: string) {
-  return defHttp.post({
-    url: '/deviceGroup/bindDevice',
-    data: {
-      deviceGroupId,
-      deviceIds,
-    },
-  })
-}
-
-export function unbindingDeviceFromGroup(deviceGroupId: string, deviceIds: string) {
-  return defHttp.post({
-    url: '/deviceGroup/unbindDevice',
-    data: {
-      deviceGroupId,
-      deviceIds,
-    },
-  })
-}
diff --git a/src/api/device-manage/group/types.ts b/src/api/device-manage/group/types.ts
deleted file mode 100644
index efec1276..00000000
--- a/src/api/device-manage/group/types.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-export interface DeviceGroup {
-  id: string
-  parentId: string
-  groupName: string
-  remark: string
-}
-
-export interface GetdeviceListByGroupParams extends PageParam {
-  groupId?: string
-  productId?: string
-  deviceSn?: string
-}
diff --git a/src/api/monitor-ops/log/index.ts b/src/api/monitor-ops/log/index.ts
deleted file mode 100644
index 3feadc3b..00000000
--- a/src/api/monitor-ops/log/index.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { GetLogListParams, Log, MessageContent } from './types'
-import { defHttp } from '@/utils/http/axios'
-
-export function getLogList(params: GetLogListParams) {
-  return defHttp.get<PageResult<Log>>({
-    url: '/device/log/page',
-    params,
-  })
-}
-
-export function getMessageContent(id: string) {
-  return defHttp.get<MessageContent>({
-    url: '/device/log/message',
-    params: {
-      id,
-    },
-  })
-}
diff --git a/src/api/monitor-ops/log/types.ts b/src/api/monitor-ops/log/types.ts
deleted file mode 100644
index cbf18dda..00000000
--- a/src/api/monitor-ops/log/types.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-export interface GetLogListParams extends PageParam {
-  productId: string
-  deviceSn: string
-  traceId: string
-  bizType: string
-  queryStartTime: string
-  queryEndTime: string
-}
-
-export interface Log {
-  productId: string
-  deviceSn: string
-  traceId: string
-  messageId?: string
-  bizType: number
-  operation: string
-  createTime: string
-  code: number
-}
-
-export interface MessageContent {
-  message: string
-  topic: string
-  createTime: string
-}
diff --git a/src/api/product/index.ts b/src/api/product/index.ts
deleted file mode 100644
index d5de8d4b..00000000
--- a/src/api/product/index.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import type { SubScription } from '../subscription/list/types'
-import type { GetProductListParmas, Product } from './types'
-import { defHttp } from '@/utils/http/axios'
-
-export function getProductList(params: GetProductListParmas) {
-  return defHttp.get<PageResult<Product>>({
-    url: '/product/page',
-    params,
-  })
-}
-
-export function getProductDetail(id: string) {
-  return defHttp.get<Product & { subscribe: SubScription }>({
-    url: `/product/detail?id=${id}`,
-  })
-}
-
-export function createProduct(data: Partial<Product>) {
-  return defHttp.post({
-    url: '/product/save',
-    data,
-  })
-}
-
-export function updateProduct(data: Partial<Product>) {
-  return defHttp.post({
-    url: '/product/update',
-    data,
-  })
-}
-
-export function deleteProduct(id: string) {
-  return defHttp.post({
-    url: `/product/remove?id=${id}`,
-  })
-}
-
-export function getAllProducts() {
-  return defHttp.get<Pick<Product, 'id' | 'productName'>[]>({
-    url: '/product/select',
-  })
-}
diff --git a/src/api/product/model.ts b/src/api/product/model.ts
deleted file mode 100644
index 63642e36..00000000
--- a/src/api/product/model.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import type { GetModelAttributeListParams, ModelAttribute, ModelAttributeWithForm, ModelService, SimpleAttribute } from './types'
-import { defHttp } from '@/utils/http/axios'
-
-export function getAllModelServices(productId: string) {
-  return defHttp.get<ModelService[]>({
-    url: '/thingModel/select',
-    params: {
-      productId,
-    },
-  })
-}
-
-export function createModelService(data: Partial<ModelService>) {
-  return defHttp.post({
-    url: '/thingModel/save',
-    data,
-  })
-}
-
-export function updateModelService(data: Partial<ModelService>) {
-  return defHttp.post({
-    url: '/thingModel/update',
-    data,
-  })
-}
-
-export function deleteModelService(id: string) {
-  return defHttp.post({
-    url: `/thingModel/remove?id=${id}`,
-  })
-}
-
-export function getModelAttributeList(params: GetModelAttributeListParams) {
-  return defHttp.get<PageResult<ModelAttribute>>({
-    url: '/thingModel/item/page',
-    params,
-  })
-}
-
-export function createModelAttribute(data: ModelAttributeWithForm) {
-  return defHttp.post({
-    url: '/thingModel/item/save',
-    data,
-  })
-}
-
-export function updateModelAttribute(data: ModelAttributeWithForm) {
-  return defHttp.post({
-    url: '/thingModel/item/update',
-    data,
-  })
-}
-
-export function deleteModelAttribute(id: string) {
-  return defHttp.post({
-    url: `/thingModel/item/remove?id=${id}`,
-  })
-}
-
-export function getAllModelAttributes(productId: string, itemType: number) {
-  return defHttp.get<SimpleAttribute[]>({
-    url: '/thingModel/item/queryList',
-    params: {
-      productId,
-      itemType,
-    },
-  })
-}
diff --git a/src/api/product/topic.ts b/src/api/product/topic.ts
deleted file mode 100644
index 893fc562..00000000
--- a/src/api/product/topic.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import type { GetTopicListPrams, Topic } from './types'
-import { defHttp } from '@/utils/http/axios'
-
-export function getTopicList(params: GetTopicListPrams) {
-  return defHttp.get<PageResult<Topic>>({
-    url: '/product/topic/page',
-    params,
-  })
-}
-
-export function createTopic(data: Partial<Topic>) {
-  return defHttp.post({
-    url: '/product/topic/save',
-    data,
-  })
-}
-
-export function updateTopic(data: Partial<Topic>) {
-  return defHttp.post({
-    url: '/product/topic/update',
-    data,
-  })
-}
-
-export function deleteTopic(id: string) {
-  return defHttp.post({
-    url: `/product/topic/remove?id=${id}`,
-  })
-}
diff --git a/src/api/product/types.ts b/src/api/product/types.ts
deleted file mode 100644
index 48cd6cd9..00000000
--- a/src/api/product/types.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-export interface GetProductListParmas extends PageParam {
-  productName?: string
-  networkType?: number
-  networkProtocol?: number
-  nodeType?: number
-  securityType?: number
-  dataType?: number
-}
-
-export interface Product {
-  id: string
-  tenantId: string
-  uuid: string
-  productName: string
-  productDesc: string
-  productKey: string
-  productSecret: string
-  nodeType: number
-  networkType: number
-  networkProtocol: number
-  authType: number
-  securityType: number
-  dataType: number
-  tsl: string
-  isRelease: number
-}
-
-export enum TopicType {
-  System = 1,
-  Custom = 2,
-}
-
-export interface GetTopicListPrams extends PageParam {
-  productId?: string
-  topicCategory?: TopicType
-}
-
-export interface Topic {
-  id: string
-  topic: string
-  topicType: number
-  enableScript: BooleanFlag
-  topicDesc: string
-  productId: string
-}
-
-export interface ModelService {
-  id: string
-  productId: string
-  serviceId: string
-  tenantId: string
-  description: string
-}
-
-export interface GetModelAttributeListParams extends PageParam {
-  productId?: string
-  modelId?: string
-}
-
-export enum ModelAttributeDataTypesEnum {
-  Int32 = 'int32',
-  Float = 'float',
-  Bool = 'bool',
-  Text = 'text',
-}
-
-export interface ModelAttribute {
-  id: string
-  name: string
-  itemType: number
-  identifier: string
-  modelId: string
-  modelName?: string
-  tenantId: string
-  method: string
-  sort: number
-  dataType: ModelAttributeDataTypesEnum
-  dataSpecs?: {
-    min?: number
-    max?: number
-    maxLength?: number
-    trueDesc?: string
-    falseDesc?: string
-    scale?: string
-  }
-}
-
-export interface ModelAttributeWithForm {
-  id: string
-  name: string
-  itemType: number
-  identifier: string
-  dataType: ModelAttributeDataTypesEnum
-  min: number
-  max: number
-  maxLength: number
-  scale: number
-  unit: string
-  trueDesc: string
-  falseDesc: string
-  method: string
-  sort: number
-  modelId: string
-}
-
-export interface SimpleAttribute {
-  dataType: ModelAttributeDataTypesEnum
-  itemType: number
-  modelId: string
-  modelName: string
-  name: string
-  dataSpecs: {
-    min: number
-    max: number
-    maxLength: number
-    trueDesc: string
-    falseDesc: string
-    scale: string
-  }
-}
diff --git a/src/api/subscription/consumer/index.ts b/src/api/subscription/consumer/index.ts
deleted file mode 100644
index f10df17a..00000000
--- a/src/api/subscription/consumer/index.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import type { Consumer, GetConsumerListParams, Product } from './types'
-import { defHttp } from '@/utils/http/axios'
-
-export function getConsumerList(params: GetConsumerListParams) {
-  return defHttp.get<PageResult<Consumer>>({
-    url: '/server/consumer/page',
-    params,
-  })
-}
-
-export function createConsumer(data: Partial<Consumer>) {
-  return defHttp.post({
-    url: '/server/consumer/save',
-    data,
-  })
-}
-
-export function updateConsumer(data: Partial<Consumer>) {
-  return defHttp.post({
-    url: '/server/consumer/update',
-    data,
-  })
-}
-
-export function deleteConsumer(id: string) {
-  return defHttp.post({
-    url: `/server/consumer/remove?id=${id}`,
-  })
-}
-
-export function getConsumerDetail(id: string) {
-  return defHttp.get<Consumer>({
-    url: '/server/consumer/detail',
-    params: {
-      id,
-    },
-  })
-}
-
-export function getOnlineClientList(consumerToken: string) {
-  return defHttp.get({
-    url: '/server/client/list',
-    params: {
-      consumerToken,
-    },
-  })
-}
-
-export function getSubscribeList(consumerId: string) {
-  return defHttp.get<Product[]>({
-    url: '/server/consumer/subscribeList',
-    params: {
-      consumerId,
-    },
-  })
-}
-
-export function disSubscription(consumerId: string, serverSubscribeId: string) {
-  return defHttp.post({
-    url: `/server/consumer/unsubscription?consumerId=${consumerId}&serverSubscribeId=${serverSubscribeId}`,
-  })
-}
diff --git a/src/api/subscription/consumer/types.ts b/src/api/subscription/consumer/types.ts
deleted file mode 100644
index 35742dc8..00000000
--- a/src/api/subscription/consumer/types.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-export interface GetConsumerListParams extends PageParam {
-  consumerName?: string
-}
-
-export interface Consumer {
-  id: string
-  consumerName: string
-  consumerToken: string
-  createTime: string
-  subscribes: string
-}
-
-export interface Product {
-  id: string
-  messageType: string
-  productId: string
-  tenantId: string
-}
diff --git a/src/api/subscription/list/index.ts b/src/api/subscription/list/index.ts
deleted file mode 100644
index f8ee4511..00000000
--- a/src/api/subscription/list/index.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import type { GetSubscriptionListParams, SubScription } from './types'
-import { defHttp } from '@/utils/http/axios'
-
-export function getSubscriptionList(params: GetSubscriptionListParams) {
-  return defHttp.get<PageResult<SubScription>>({
-    url: '/server/subscribe/page',
-    params,
-  })
-}
-
-export function createSubscription(data: Pick<SubScription, 'productId' | 'messageType'>) {
-  return defHttp.post({
-    url: '/server/subscribe/save',
-    data,
-  })
-}
-
-export function updateSubscription(data: Pick<SubScription, 'productId' | 'messageType' | 'id'>) {
-  return defHttp.post({
-    url: '/server/subscribe/update',
-    data,
-  })
-}
-
-export function deleteSubscription(id: string) {
-  return defHttp.post({
-    url: `/server/subscribe/remove?id=${id}`,
-  })
-}
-
-export function getAllSubscription() {
-  return defHttp.get<SubScription[]>({
-    url: '/server/subscribe/select',
-  })
-}
-
-export function getSubscriptionDetail(id: string) {
-  return defHttp.get({
-    url: '/server/consumer/detail',
-    params: {
-      id,
-    },
-  })
-}
diff --git a/src/api/subscription/list/types.ts b/src/api/subscription/list/types.ts
deleted file mode 100644
index c1a66711..00000000
--- a/src/api/subscription/list/types.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export interface GetSubscriptionListParams extends PageParam {
-  productId?: string
-}
-
-export interface SubScription {
-  id: string
-  messageType: string
-  productId: string
-  tenantId: string
-  createTime: string
-}
diff --git a/src/views/device-manage/device/DeviceFormModal.vue b/src/views/device-manage/device/DeviceFormModal.vue
deleted file mode 100644
index 7afc2b8f..00000000
--- a/src/views/device-manage/device/DeviceFormModal.vue
+++ /dev/null
@@ -1,55 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue'
-import { getFormSchema } from './data'
-import { useMessage } from '@/hooks/web/useMessage'
-import { BasicForm, useForm } from '@/components/Form'
-import { BasicModal, useModalInner } from '@/components/Modal'
-import { createDevice, updateDevice } from '@/api/device-manage/device'
-import type { Device } from '@/api/device-manage/device/types'
-
-defineOptions({ name: 'DeviceFormModal' })
-
-const emit = defineEmits(['success', 'register'])
-
-const isUpdate = ref(false)
-const [registerForm, { setFieldsValue, validate }] = useForm({
-  labelWidth: 100,
-  baseColProps: { span: 24 },
-  schemas: getFormSchema(isUpdate),
-  showActionButtonGroup: false,
-  actionColOptions: { span: 23 },
-})
-
-const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: Device) => {
-  isUpdate.value = true
-  setFieldsValue({ ...data })
-})
-
-async function handleSubmit() {
-  try {
-    const values = await validate<Device>()
-    setModalProps({ confirmLoading: true })
-    await (isUpdate.value ? updateDevice(values) : createDevice(values))
-    closeModal()
-    emit('success')
-    useMessage().createMessage.success('保存成功')
-  }
-  catch {}
-  finally {
-    setModalProps({ confirmLoading: false })
-  }
-}
-</script>
-
-<template>
-  <BasicModal
-    width="30%"
-    :min-height="100"
-    :title="isUpdate ? '编辑' : '新增'"
-    :after-close="() => isUpdate = false"
-    @register="registerModal"
-    @ok="handleSubmit"
-  >
-    <BasicForm @register="registerForm" />
-  </BasicModal>
-</template>
diff --git a/src/views/device-manage/device/components/CloudCommand.vue b/src/views/device-manage/device/components/CloudCommand.vue
deleted file mode 100644
index f901c310..00000000
--- a/src/views/device-manage/device/components/CloudCommand.vue
+++ /dev/null
@@ -1,104 +0,0 @@
-<script lang="ts" setup>
-import { computed, h, ref, unref } from 'vue'
-import { CloudSyncOutlined } from '@ant-design/icons-vue'
-import { Alert, Segmented } from 'ant-design-vue'
-import MessageModal from './MessageModal.vue'
-import SendCommandModal from './SendCommandModal.vue'
-import { BasicTable, useTable } from '@/components/Table'
-import { useModal } from '@/components/Modal'
-import { getCloudCommandLogs, getMessageContent } from '@/api/device-manage/device/cloud-command'
-import type { Device } from '@/api/device-manage/device/types'
-import { CloudCommandType } from '@/api/device-manage/device/types'
-import { usePermission } from '@/hooks/web/usePermission'
-
-defineProps<{ device?: Device }>()
-
-const [registerModal, { openModal }] = useModal<{ type: CloudCommandType, productId: string, deviceId: string }>()
-
-const commandTypes = [
-  {
-    label: '命令下发',
-    value: CloudCommandType.Command,
-    desc: '如果设备所属产品定义了命令功能,则您可以通过应用调用平台接口或者操作 “下发” 按钮下发命令,当前MQTT设备仅支持同步命令下发,NB设备仅支持异步命令下发。',
-  },
-  {
-    label: '属性下发',
-    value: CloudCommandType.Attribute,
-    desc: '属性下发依赖产品模型,平台会以异步方式(属性下发后无需等待设备侧回复相应)下发消息给设备,当前仅MQTT设备支持属性下发。',
-  },
-  {
-    label: '消息下发',
-    value: CloudCommandType.Message,
-    desc: '消息下发不依赖产品模型,平台会以异步方式(消息下发后无需等待设备侧回复相应)下发消息给设备,当前仅MQTT设备支持消息下发。',
-  },
-]
-const selectedCommonType = ref(CloudCommandType.Command)
-const selectedCommon = computed(() => commandTypes.find(item => item.value === selectedCommonType.value))
-
-const [register, { reload }] = useTable({
-  api: params => getCloudCommandLogs({ ...params, messageType: unref(selectedCommon)?.value }),
-  columns: [
-    {
-      title: '追踪 ID',
-      dataIndex: 'traceId',
-    },
-    {
-      title: '内容',
-      width: 200,
-      dataIndex: 'messageId',
-      customRender({ value }) {
-        return value && h(MessageModal, {
-          buttonText: '查看',
-          request: () => getMessageContent(value),
-        })
-      },
-    },
-    {
-      title: '主题',
-      dataIndex: 'operation',
-    },
-    {
-      title: '创建时间',
-      dataIndex: 'createTime',
-    },
-  ],
-  bordered: true,
-  inset: true,
-  canResize: false,
-})
-
-const { hasPermission } = usePermission()
-</script>
-
-<template>
-  <div>
-    <div flex="~ items-center gap-12px" mb="12px">
-      <Segmented v-model:value="selectedCommonType" :options="commandTypes" @change="() => reload()" />
-      <a-button
-        v-if="hasPermission('device_cloud_command_action')"
-        type="primary"
-        @click="openModal(true, {
-          deviceId: device!.id,
-          type: selectedCommon!.value,
-          productId: device!.productId,
-        })"
-      >
-        <CloudSyncOutlined />下发
-      </a-button>
-      <Alert
-        type="info"
-        show-icon
-        class="py-4px text-13px"
-        :message="selectedCommon?.desc"
-      />
-    </div>
-
-    <BasicTable @register="register" />
-
-    <SendCommandModal
-      :title="selectedCommon?.label"
-      @register="registerModal"
-      @success="reload"
-    />
-  </div>
-</template>
diff --git a/src/views/device-manage/device/components/DeviceInfo.vue b/src/views/device-manage/device/components/DeviceInfo.vue
deleted file mode 100644
index a9600e4b..00000000
--- a/src/views/device-manage/device/components/DeviceInfo.vue
+++ /dev/null
@@ -1,114 +0,0 @@
-<script lang="ts" setup>
-import { toRef } from 'vue'
-import { Card, Empty } from 'ant-design-vue'
-import { FieldTimeOutlined, SyncOutlined } from '@ant-design/icons-vue'
-import { useDeviceInfo } from './composables/useDeviceInfo'
-import { useDeviceProperties } from './composables/useDeviceProperties'
-import { Description } from '@/components/Description'
-import { useModelService } from '@/views/product/components/composables/useModelService'
-import { usePermission } from '@/hooks/web/usePermission'
-import { ProductTabEnums } from '@/views/product/data'
-import type { Device } from '@/api/device-manage/device/types'
-
-const props = defineProps<{ data: Device }>()
-
-const { scheam } = useDeviceInfo(toRef(props, 'data'))
-
-const {
-  modelServiceList,
-  selectedModelId,
-  setSelectedModelId,
-} = useModelService(props.data.productId)
-
-const {
-  isLoading,
-  deviceProperties,
-  reloadReviceProperties,
-} = useDeviceProperties(() => selectedModelId.value, () => props.data?.deviceSn)
-
-const { hasPermission } = usePermission()
-</script>
-
-<template>
-  <div>
-    <Description :schema="scheam" :data="data" :column="2" :label-style="{ width: '130px' }" />
-
-    <Card v-if="hasPermission('device_report_view')" mt="15px">
-      <div flex="~ items-center justify-between">
-        <div font-bold>
-          最新上报数据
-        </div>
-        <div flex="~ items-center">
-          <span v-if="deviceProperties?.updateTime" text="gray-400 center" mr="12px">
-            更新时间: {{ deviceProperties.updateTime }}
-          </span>
-          <a-button size="small" @click="reloadReviceProperties">
-            <SyncOutlined />
-            刷新
-          </a-button>
-        </div>
-      </div>
-
-      <div v-loading="isLoading" flex="~ gap-12px" mt="12px">
-        <div w="20%" border="0 r-1 solid gray-50 dark:white dark:opacity-5">
-          <div
-            v-for="item in modelServiceList" :key="item.id"
-            flex="~ items-center justify-between"
-            class="box-border h-60px cursor-pointer pl-10px hover:bg-gray-100 hover:dark:bg-white hover:dark:bg-opacity-5"
-            border="0 b-1 solid gray-50 dark:white dark:opacity-5"
-            :class="selectedModelId === item.id ? 'bg-gray-100 dark:bg-white dark:bg-opacity-5' : ''"
-            @click="setSelectedModelId(item.id)"
-          >
-            <div>
-              <div truncate>
-                {{ item.serviceId }}
-              </div>
-              <div mt="5px" text="12px gray-500" truncate :title="item.description">
-                {{ item.description }}
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <div
-          v-if="deviceProperties?.properties"
-          w-0 flex-1
-          grid="~ cols-4 rows-[160px] auto-rows-[160px] gap-12px"
-        >
-          <div
-            v-for="item in deviceProperties.properties"
-            :key="item.identifier"
-            p="15px" box-border rounded
-            shadow="hover:lg" transition="shadow"
-            flex="~ col justify-between"
-            style="background: linear-gradient(to right, rgba(243, 244, 246, 1), rgba(209, 216, 224, 1));"
-          >
-            <div>
-              <div font-500 text="16px">
-                {{ item.name }}
-              </div>
-              <div text="gray-500">
-                {{ item.identifier }}
-              </div>
-            </div>
-            <div text="22px center">
-              "{{ item.unit || 'null' }}"
-            </div>
-            <div text="right gray-500 hover:gray-800">
-              <span v-if="hasPermission('device_report_history')" class="cursor-pointer">
-                <FieldTimeOutlined />
-                历史
-              </span>
-            </div>
-          </div>
-        </div>
-        <div v-else text="center" flex-1>
-          <Empty :image="Empty.PRESENTED_IMAGE_SIMPLE" />
-          <a-button size="small" @click="$router.push(`/product/detail/${data?.productId}/${ProductTabEnums.Model}/${selectedModelId}`)">
-            点击添加属性
-          </a-button>
-        </div>
-      </div>
-    </Card>
-  </div>
-</template>
diff --git a/src/views/device-manage/device/components/MessageModal.vue b/src/views/device-manage/device/components/MessageModal.vue
deleted file mode 100644
index 80e595a3..00000000
--- a/src/views/device-manage/device/components/MessageModal.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-<script lang="ts" setup>
-import { h, ref } from 'vue'
-import { Modal } from 'ant-design-vue'
-import { EyeOutlined } from '@ant-design/icons-vue'
-import { useAsyncState } from '@vueuse/core'
-import type { DescItem } from '@/components/Description'
-import { Description } from '@/components/Description'
-import { JsonPreview } from '@/components/CodeEditor'
-import { noop } from '@/utils'
-
-const props = defineProps<{
-  buttonText?: string
-  request: (...args: any[]) => Promise<{ topic: string, message: string }>
-}>()
-
-const { state, execute, isLoading } = useAsyncState(props.request, undefined, { immediate: false })
-
-const open = ref(false)
-function handleOpen() {
-  if (state.value)
-    return open.value = true
-
-  execute()
-    .then(() => open.value = true)
-    .catch(noop)
-}
-
-const schema: DescItem[] = [
-  {
-    label: 'Topic',
-    field: 'topic',
-  },
-  {
-    label: '内容',
-    field: 'message',
-    render(value) {
-      let content = value
-      try {
-        content = JSON.parse(value)
-      }
-      catch {}
-      return h(JsonPreview, {
-        data: content,
-      })
-    },
-  },
-]
-</script>
-
-<template>
-  <a-button size="small" :loading="isLoading" @click="handleOpen">
-    <EyeOutlined />
-    {{ buttonText || '查看参数' }}
-  </a-button>
-
-  <Modal v-model:open="open" title="上报示例" :footer="null" width="50%">
-    <Description :data="state" :schema="schema" :column="1" />
-  </Modal>
-</template>
diff --git a/src/views/device-manage/device/components/MqttParamsModal.vue b/src/views/device-manage/device/components/MqttParamsModal.vue
deleted file mode 100644
index d6aa0f43..00000000
--- a/src/views/device-manage/device/components/MqttParamsModal.vue
+++ /dev/null
@@ -1,77 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue'
-import { Modal } from 'ant-design-vue'
-import { EyeOutlined } from '@ant-design/icons-vue'
-import { useAsyncState } from '@vueuse/core'
-import { getMqttConnectParams } from '@/api/device-manage/device'
-import type { DescItem } from '@/components/Description'
-import { Description } from '@/components/Description'
-import { noop } from '@/utils'
-import { copyText } from '@/utils/copyTextToClipboard'
-
-const props = defineProps<{ deviceId: string }>()
-
-const { state, execute, isLoading } = useAsyncState(() => getMqttConnectParams(props.deviceId), undefined, { immediate: false })
-
-const open = ref(false)
-function handleOpen() {
-  if (state.value)
-    return open.value = true
-
-  execute()
-    .then(() => {
-      open.value = true
-    })
-    .catch(noop)
-}
-
-const schema: DescItem[] = [
-  {
-    label: '服务器地址 (hostUrl)',
-    field: 'hostUrl',
-  },
-  {
-    label: '端口 (port)',
-    field: 'port',
-  },
-  {
-    label: 'ClientID (clientId)',
-    field: 'clientId',
-  },
-  {
-    label: '用户名 (username)',
-    field: 'username',
-  },
-  {
-    label: '密码 (password)',
-    field: 'password',
-  },
-]
-
-function handleCopy() {
-  const data = schema.map((item) => {
-    return {
-      key: item.field,
-      keyName: item.label,
-      value: state.value[item.field],
-    }
-  })
-  copyText(JSON.stringify(data))
-}
-</script>
-
-<template>
-  <a-button size="small" :loading="isLoading" @click="handleOpen">
-    <EyeOutlined />
-    查看参数
-  </a-button>
-
-  <Modal v-model:open="open" title="MQTT 连接参数" :footer="null">
-    <Description :data="state" :schema="schema" :column="1" />
-    <div text="center" mt="10px">
-      <a-button size="small" @click="handleCopy">
-        一键复制
-      </a-button>
-    </div>
-  </Modal>
-</template>
diff --git a/src/views/device-manage/device/components/SendCommandModal.vue b/src/views/device-manage/device/components/SendCommandModal.vue
deleted file mode 100644
index 7c0dc6eb..00000000
--- a/src/views/device-manage/device/components/SendCommandModal.vue
+++ /dev/null
@@ -1,185 +0,0 @@
-<script lang="ts" setup>
-import { BasicModal, useModalInner } from '@/components/Modal'
-import type { FormSchema, FormSchemaInner, Rule } from '@/components/Form'
-import { BasicForm, useForm } from '@/components/Form'
-import { CloudCommandType } from '@/api/device-manage/device/types'
-import { sendCommandToDevice, sendMessageToDevice } from '@/api/device-manage/device/cloud-command'
-import CodeEditor from '@/components/CodeEditor/src/CodeEditor.vue'
-import { getAllModelAttributes } from '@/api/product/model'
-import { getDeviceTopicList } from '@/api/device-manage/device'
-import { ModelAttributeDataTypesEnum, type SimpleAttribute } from '@/api/product/types'
-import { useMessage } from '@/hooks/web/useMessage'
-
-const emit = defineEmits(['register', 'success'])
-
-const [registerForm, { updateSchema, resetSchema, validate, setFieldsValue, clearValidate }] = useForm({
-  schemas: [],
-  labelWidth: 100,
-  baseColProps: { span: 24 },
-  showActionButtonGroup: false,
-  actionColOptions: { span: 23 },
-})
-
-let productId: string
-let deviceId: string
-let commandType: CloudCommandType
-const CommandAndAttributeSchemas: FormSchema[] = [
-  {
-    field: 'itemId',
-    label: '选择属性',
-    required: true,
-    component: 'ApiSelect',
-    componentProps: {
-      api: () => getAllModelAttributes(productId!, commandType),
-      valueField: 'id',
-      labelField: 'name',
-      allowClear: false,
-      onChange(_, option: SimpleAttribute & { label: string }) { // label is SimpleAttribute.name
-        if (!option)
-          return
-
-        const { dataType, label, dataSpecs, modelId } = option
-        setFieldsValue({ modelId, value: undefined })
-        // avoid non-null check
-        clearValidate()
-
-        let schema: Partial<FormSchemaInner> = {}
-        if (dataType === ModelAttributeDataTypesEnum.Bool) {
-          schema = {
-            rules: [], // remove rules cache
-            component: 'RadioGroup',
-            componentProps: {
-              options: [
-                { label: dataSpecs.falseDesc, value: 0 },
-                { label: dataSpecs.trueDesc, value: 1 },
-              ],
-            },
-          }
-        }
-        else {
-          let rules: Rule[] = []
-          switch (dataType) {
-            case ModelAttributeDataTypesEnum.Text:
-              rules = [{ max: dataSpecs.maxLength, message: `限制最大长度为 ${dataSpecs.maxLength}` }]
-              break
-            case ModelAttributeDataTypesEnum.Float:
-            case ModelAttributeDataTypesEnum.Int32:
-              rules = [
-                { type: 'number', min: +dataSpecs.min, max: +dataSpecs.max, message: `数值范围为: ${dataSpecs.min}-${dataSpecs.max}` },
-              ]
-              break
-            default: {
-              const _exhaustiveCheck: never = dataType
-              return _exhaustiveCheck
-            }
-          }
-
-          schema = {
-            rules,
-            // if it isn't text, then it's a float or int32
-            component: dataType === ModelAttributeDataTypesEnum.Text ? 'InputTextArea' : 'InputNumber',
-            componentProps: dataType !== ModelAttributeDataTypesEnum.Text
-              ? {
-                  precision: dataType === ModelAttributeDataTypesEnum.Float ? dataSpecs.scale : 0,
-                }
-              : { rows: 3 },
-          }
-        }
-        updateSchema({
-          label,
-          field: 'value',
-          ifShow: true,
-          ...schema,
-        })
-      },
-    },
-  },
-  {
-    field: 'value',
-    fields: ['modelId'],
-    required: true,
-    component: 'Input',
-    ifShow: false,
-  },
-]
-const MessageSchemas: FormSchema[] = [
-  {
-    field: 'topic',
-    label: 'Topic',
-    required: true,
-    component: 'ApiSelect',
-    componentProps: {
-      api: async () => (await getDeviceTopicList({ deviceId, current: 1, size: 500 })).records,
-      valueField: 'topic',
-      labelField: 'topic',
-    },
-  },
-  {
-    field: 'message',
-    label: '指令内容',
-    rules: [
-      {
-        async validator(_, value) {
-          try {
-            const code = JSON.stringify(JSON.parse(value))
-            if (code === '{}')
-            // eslint-disable-next-line unicorn/error-message
-              throw new Error()
-          }
-          catch {
-            // eslint-disable-next-line prefer-promise-reject-errors
-            return Promise.reject('指令内容必须是 JSON 格式, 且不能为空')
-          }
-        },
-        required: true,
-      },
-    ],
-    slot: 'Message',
-    helpMessage: '指令内容必须是 JSON 格式',
-    defaultValue: {},
-    componentProps: {
-      rows: 10,
-    },
-  },
-]
-const [register, { closeModal, setModalProps }] = useModalInner((params: { type: CloudCommandType, productId: string, deviceId: string }) => {
-  productId = params.productId
-  deviceId = params.deviceId
-  commandType = params.type
-  resetSchema(commandType === CloudCommandType.Message ? MessageSchemas : CommandAndAttributeSchemas)
-})
-
-async function handleSubmit() {
-  try {
-    const values = await validate<any>()
-    setModalProps({ confirmLoading: true })
-    values.deviceId = deviceId
-    const isSendMessage = commandType === CloudCommandType.Message
-    await (isSendMessage ? sendMessageToDevice(values) : sendCommandToDevice(values))
-    useMessage().createMessage.success('下发成功')
-    emit('success')
-    closeModal()
-  }
-  catch {}
-  finally {
-    setModalProps({ confirmLoading: false })
-  }
-}
-</script>
-
-<template>
-  <BasicModal
-    v-bind="$attrs"
-    :min-height="100"
-    @register="register"
-    @ok="handleSubmit"
-  >
-    <BasicForm @register="registerForm">
-      <template #Message="{ model, field }">
-        <div h="200px" border="1px solid gray-200 dark:#424242" w-full overflow-hidden rounded>
-          <CodeEditor v-model:value="model[field]" />
-        </div>
-      </template>
-    </BasicForm>
-  </BasicModal>
-</template>
diff --git a/src/views/device-manage/device/components/TopicList.vue b/src/views/device-manage/device/components/TopicList.vue
deleted file mode 100644
index 745fed33..00000000
--- a/src/views/device-manage/device/components/TopicList.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<script lang="ts" setup>
-import { h } from 'vue'
-import { Alert, Tag } from 'ant-design-vue'
-import { SyncOutlined } from '@ant-design/icons-vue'
-import { useRoute } from 'vue-router'
-import { BasicTable, useTable } from '@/components/Table'
-import { getDeviceTopicList } from '@/api/device-manage/device'
-import { useSystemEnumStore } from '@/store/modules/systemEnum'
-
-const route = useRoute()
-const { getSystemEnumLabel } = useSystemEnumStore()
-const [register, { reload }] = useTable({
-  api(params) {
-    return getDeviceTopicList({ ...params, deviceId: route.params.id as string })
-  },
-  columns: [
-    {
-      title: 'Topic',
-      dataIndex: 'topic',
-    },
-    {
-      title: '操作权限',
-      dataIndex: 'topicType',
-      customRender({ value }) {
-        return h(Tag, () => getSystemEnumLabel('eProductTopicType', value))
-      },
-    },
-    {
-      title: '描述',
-      dataIndex: 'topicDesc',
-    },
-  ],
-  bordered: true,
-  inset: true,
-  canResize: false,
-})
-</script>
-
-<template>
-  <div flex="~ items-center justify-between">
-    <Alert
-      type="info"
-      show-icon
-      class="py-4px text-13px"
-      message="Topic用以将设备数据分类上报,进而分别进行处理。除了系统预置的Topic,您也可以自定义Topic,然后在设备侧开发时选择数据上报的Topic。"
-    />
-    <a-button size="small" @click="reload">
-      <SyncOutlined />
-      刷新
-    </a-button>
-  </div>
-  <BasicTable mt="12px" @register="register" />
-</template>
diff --git a/src/views/device-manage/device/components/composables/useDeviceInfo.ts b/src/views/device-manage/device/components/composables/useDeviceInfo.ts
deleted file mode 100644
index 0f021d51..00000000
--- a/src/views/device-manage/device/components/composables/useDeviceInfo.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import type { Ref } from 'vue'
-import { h } from 'vue'
-import { Tag } from 'ant-design-vue'
-import MqttParamsModal from '../MqttParamsModal.vue'
-import MessageModal from '../MessageModal.vue'
-import type { DescItem } from '@/components/Description'
-import { getReportExample } from '@/api/device-manage/device'
-import { usePermission } from '@/hooks/web/usePermission'
-import type { Device } from '@/api/device-manage/device/types'
-
-export function useDeviceInfo(data: Ref<Device | undefined>) {
-  const { hasPermission } = usePermission()
-
-  const scheam: DescItem[] = [
-    {
-      field: 'productName',
-      label: '所属产品',
-    },
-    {
-      field: 'deviceName',
-      label: '设备名称',
-    },
-    {
-      field: 'deviceSn',
-      label: '设备序列号',
-    },
-    {
-      field: 'deviceSecret',
-      label: '设备密钥',
-    },
-    {
-      field: 'isActive',
-      label: '设备状态',
-      render(value) {
-        return h(Tag, { color: value ? 'green' : 'default' }, () => value ? '已激活' : '未激活')
-      },
-    },
-    {
-      field: 'isOnline',
-      label: '在线状态',
-      render(value) {
-        return h(Tag, { color: value ? 'green' : 'default' }, () => value ? '在线' : '离线')
-      },
-    },
-    {
-      field: 'createTime',
-      label: '创建时间',
-    },
-    {
-      field: 'activeTime',
-      label: '激活时间',
-    },
-    {
-      field: 'lastOnlineTime',
-      label: '最后上线时间',
-    },
-    {
-      field: 'lastOfflineTime',
-      label: '最后离线时间',
-    },
-    {
-      field: 'mqtt',
-      label: 'MQTT连接参数',
-      show: () => hasPermission('device_mqtt_params'),
-      render: () => h(MqttParamsModal, { deviceId: data.value!.id }),
-    },
-    {
-      field: 'report',
-      label: '上报示例',
-      show: () => hasPermission('device_report_example'),
-      render: () => h(MessageModal, {
-        request: () => getReportExample(data.value!.productId, data.value!.deviceSn),
-      }),
-    },
-    {
-      field: 'deviceDesc',
-      label: '设备描述',
-    },
-  ]
-
-  return {
-    scheam,
-  }
-}
diff --git a/src/views/device-manage/device/components/composables/useDeviceProperties.ts b/src/views/device-manage/device/components/composables/useDeviceProperties.ts
deleted file mode 100644
index e19c55b9..00000000
--- a/src/views/device-manage/device/components/composables/useDeviceProperties.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { useAsyncState } from '@vueuse/core'
-import { type MaybeRefOrGetter, toValue, watchEffect } from 'vue'
-import { getDeviceProperties } from '@/api/device-manage/device'
-
-export function useDeviceProperties(modelId: MaybeRefOrGetter<string | undefined>, deviceSn: MaybeRefOrGetter<string | undefined>) {
-  const { state, execute, isLoading } = useAsyncState(
-    () => getDeviceProperties(toValue(modelId)!, toValue(deviceSn)!),
-    undefined,
-    {
-      immediate: false,
-      resetOnExecute: false,
-    },
-  )
-
-  watchEffect(() => {
-    if (!toValue(modelId) || !toValue(deviceSn))
-      return
-
-    execute()
-  })
-
-  return {
-    isLoading,
-    deviceProperties: state,
-    reloadReviceProperties: execute,
-  }
-}
diff --git a/src/views/device-manage/device/components/index.ts b/src/views/device-manage/device/components/index.ts
deleted file mode 100644
index 66066a05..00000000
--- a/src/views/device-manage/device/components/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export { default as DeviceInfo } from './DeviceInfo.vue'
-export { default as TopicList } from './TopicList.vue'
-export { default as CloudCommand } from './CloudCommand.vue'
-export { default as SendCommandModal } from './SendCommandModal.vue'
diff --git a/src/views/device-manage/device/data.ts b/src/views/device-manage/device/data.ts
deleted file mode 100644
index bd49e40e..00000000
--- a/src/views/device-manage/device/data.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import { Tag } from 'ant-design-vue'
-import type { Ref } from 'vue'
-import { h } from 'vue'
-import type { BasicColumn, FormSchema } from '@/components/Table'
-import { getAllProducts } from '@/api/product'
-import { getAllTenants } from '@/api/system/tenant'
-
-export const columns: BasicColumn[] = [
-  {
-    title: '所属产品',
-    dataIndex: 'productName',
-  },
-  {
-    title: '设备名称',
-    dataIndex: 'deviceName',
-  },
-  {
-    title: '设备序列号',
-    dataIndex: 'deviceSn',
-  },
-  {
-    title: '设备描述',
-    dataIndex: 'deviceDesc',
-  },
-  {
-    title: '是否在线',
-    dataIndex: 'isOnline',
-    customRender({ value }) {
-      return h(Tag, {
-        color: value ? 'green' : 'default',
-      }, () => value ? '在线' : '离线')
-    },
-  },
-]
-
-export const searchFormSchemas: FormSchema[] = [
-  {
-    label: '所属租户',
-    field: 'tenantId',
-    component: 'ApiSelect',
-    componentProps: {
-      api: getAllTenants,
-      valueField: 'tenantId',
-      labelField: 'tenantName',
-    },
-    colProps: { span: 6 },
-  },
-  {
-    label: '所属产品',
-    field: 'productId',
-    component: 'ApiSelect',
-    componentProps: {
-      api: getAllProducts,
-      valueField: 'id',
-      labelField: 'productName',
-    },
-    colProps: { span: 6 },
-  },
-  {
-    label: '设备序列号',
-    field: 'deviceSn',
-    component: 'Input',
-    colProps: { span: 6 },
-  },
-  {
-    label: '设备名称',
-    field: 'deviceName',
-    component: 'Input',
-    colProps: { span: 6 },
-  },
-  {
-    label: '是否在线',
-    field: 'isOnline',
-    component: 'Select',
-    componentProps: {
-      options: [
-        { label: '在线', value: 1 },
-        { label: '离线', value: 0 },
-      ],
-    },
-    colProps: { span: 6 },
-  },
-]
-
-export function getFormSchema(isUpdate: Ref<boolean>): FormSchema[] {
-  return [
-    {
-      field: 'id',
-      show: false,
-      component: 'Input',
-    },
-    {
-      label: '设备名称',
-      field: 'deviceName',
-      component: 'Input',
-      required: true,
-    },
-    {
-      label: '设备序列号',
-      field: 'deviceSn',
-      component: 'Input',
-      required: true,
-      ifShow: () => !isUpdate.value,
-    },
-    {
-      label: '所属产品',
-      field: 'productId',
-      component: 'ApiSelect',
-      required: true,
-      ifShow: () => !isUpdate.value,
-      componentProps: {
-        api: getAllProducts,
-        valueField: 'id',
-        labelField: 'productName',
-      },
-    },
-    {
-      label: '设备描述',
-      field: 'deviceDesc',
-      component: 'InputTextArea',
-    },
-  ]
-}
diff --git a/src/views/device-manage/device/detail.vue b/src/views/device-manage/device/detail.vue
deleted file mode 100644
index 1c674b9e..00000000
--- a/src/views/device-manage/device/detail.vue
+++ /dev/null
@@ -1,39 +0,0 @@
-<script lang='ts' setup>
-import { Card, Tabs } from 'ant-design-vue'
-import { useAsyncState } from '@vueuse/core'
-import { useRoute } from 'vue-router'
-import { CloudCommand, DeviceInfo, TopicList } from './components'
-import { usePermission } from '@/hooks/web/usePermission'
-import { getDeviceDetail } from '@/api/device-manage/device'
-
-defineOptions({ name: 'DeviceDetail' })
-
-const route = useRoute()
-const { state: data } = useAsyncState(() => getDeviceDetail(route.params.id as string), undefined)
-
-const { hasPermission } = usePermission()
-</script>
-
-<template>
-  <div p="12px">
-    <Card title="设备详情">
-      <Tabs>
-        <Tabs.TabPane key="1" tab="设备信息">
-          <DeviceInfo v-if="data" :data="data" />
-        </Tabs.TabPane>
-        <Tabs.TabPane v-if="hasPermission('device_topic_view')" key="2" tab="已订阅 Topic">
-          <TopicList />
-        </Tabs.TabPane>
-        <Tabs.TabPane v-if="hasPermission('device_cloud_command_view')" key="3" tab="云端下发">
-          <CloudCommand :device="data" />
-        </Tabs.TabPane>
-      </Tabs>
-    </Card>
-  </div>
-</template>
-
-<style scoped lang="less">
-:deep(.ant-card-body) {
-  padding-top: 10px;
-}
-</style>
diff --git a/src/views/device-manage/device/index.vue b/src/views/device-manage/device/index.vue
deleted file mode 100644
index 9da1c6c3..00000000
--- a/src/views/device-manage/device/index.vue
+++ /dev/null
@@ -1,109 +0,0 @@
-<script lang="ts" setup>
-import { PlusOutlined } from '@ant-design/icons-vue'
-import { columns, searchFormSchemas } from './data'
-import DeviceFormModal from './DeviceFormModal.vue'
-import { SendCommandModal } from './components'
-import { useModal } from '@/components/Modal'
-import { BasicTable, TableAction, useTable } from '@/components/Table'
-import { deleteDevice, getDeviceList } from '@/api/device-manage/device'
-import { CloudCommandType } from '@/api/device-manage/device/types'
-import type { Device } from '@/api/device-manage/device/types'
-import { useMessage } from '@/hooks/web/useMessage'
-import { usePermission } from '@/hooks/web/usePermission'
-
-defineOptions({ name: 'Device' })
-
-const [registerModal, { openModal }] = useModal<Device>()
-const [
-  registerSendCommandModal,
-  { openModal: openSendCommandModal },
-] = useModal<{ type: CloudCommandType, productId: string, deviceId: string }>()
-
-const { hasPermission } = usePermission()
-
-const [register, { reload }] = useTable({
-  api: getDeviceList,
-  columns,
-  formConfig: {
-    schemas: searchFormSchemas,
-    labelWidth: 80,
-  },
-  bordered: true,
-  canResize: false,
-  useSearchForm: true,
-  actionColumn: {
-    width: 320,
-    title: '操作',
-    dataIndex: 'action',
-    fixed: 'right',
-    auth: ['view', 'edit', 'delete', 'cmd_send'].map(code => `device_${code}`),
-  },
-})
-
-async function handleDelete(id: string) {
-  try {
-    await deleteDevice(id)
-    useMessage().createMessage.success('删除成功')
-    reload()
-  }
-  catch {}
-}
-</script>
-
-<template>
-  <div>
-    <BasicTable :api="async () => ([] as Device[])" @register="register">
-      <template v-if="hasPermission('device_add')" #tableTitle>
-        <a-button type="primary" @click="openModal(true)">
-          <PlusOutlined />
-          新建
-        </a-button>
-      </template>
-
-      <template #bodyCell="{ column, record }">
-        <template v-if="column.key === 'action'">
-          <TableAction
-            :actions="[
-              {
-                icon: 'i-ant-design:file-search-outlined',
-                label: '详情',
-                auth: 'device_view',
-                onClick: () => $router.push(`/device-manage/device/detail/${record.id}`),
-              },
-              {
-                icon: 'i-ant-design:edit-outlined',
-                label: '编辑',
-                auth: 'device_edit',
-                onClick: () => openModal(true, record),
-              },
-              {
-                icon: 'i-ant-design:cloud-sync-outlined',
-                label: '指令下发',
-                auth: 'device_cmd_send',
-                onClick: () => openSendCommandModal(true, {
-                  deviceId: record.id,
-                  productId: record.productId,
-                  type: CloudCommandType.Message,
-                }),
-              },
-              {
-                icon: 'i-ant-design:delete-outlined',
-                danger: true,
-                label: '删除',
-                auth: 'device_delete',
-                popConfirm: {
-                  title: '是否要删除数据?',
-                  placement: 'left',
-                  confirm: () => handleDelete(record.id),
-                },
-              },
-            ]"
-          />
-        </template>
-      </template>
-    </BasicTable>
-
-    <DeviceFormModal @register="registerModal" @success="reload" />
-    <SendCommandModal title="指令下发" @register="registerSendCommandModal" />
-  </div>
-</template>
diff --git a/src/views/device-manage/group/components/BindingDeviceDrawer.vue b/src/views/device-manage/group/components/BindingDeviceDrawer.vue
deleted file mode 100644
index fecdfddf..00000000
--- a/src/views/device-manage/group/components/BindingDeviceDrawer.vue
+++ /dev/null
@@ -1,101 +0,0 @@
-<script lang="ts" setup>
-import { BasicDrawer, useDrawerInner } from '@/components/Drawer'
-import { BasicTable, useTable } from '@/components/Table'
-import { getDeviceList } from '@/api/device-manage/device'
-import { useMessage } from '@/hooks/web/useMessage'
-import { bindingDeviceToGroup } from '@/api/device-manage/group'
-import { useSharedProducts } from '@/views/subscription/list/data'
-import { noop } from '@/utils'
-
-const emit = defineEmits(['register', 'success'])
-
-let deviceGroupId: string
-const [register, { closeDrawer, setDrawerProps }] = useDrawerInner((id: string) => {
-  deviceGroupId = id
-})
-
-const { products } = useSharedProducts()
-
-const [registerTable, { getSelectRowKeys, clearSelectedRowKeys }] = useTable({
-  api: getDeviceList,
-  rowKey: 'id',
-  columns: [
-    {
-      title: '所属产品',
-      dataIndex: 'productId',
-      customRender({ value }) {
-        return products.value.find(item => item.id === value)?.productName
-      },
-    },
-    {
-      title: '产品名称',
-      dataIndex: 'deviceName',
-    },
-    {
-      title: '设备序列号',
-      dataIndex: 'deviceSn',
-    },
-  ],
-  formConfig: {
-    schemas: [
-      {
-        field: 'productId',
-        label: '所属产品',
-        component: 'Select',
-        componentProps: {
-          options: products as any,
-          showSearch: true,
-          fieldNames: {
-            label: 'productName',
-            value: 'id',
-          },
-        },
-        colProps: { span: 8 },
-      },
-      {
-        field: 'deviceSn',
-        label: '设备序列号',
-        component: 'Input',
-        colProps: { span: 8 },
-      },
-    ],
-    labelWidth: 90,
-  },
-  bordered: true,
-  canResize: false,
-  useSearchForm: true,
-  inset: true,
-  rowSelection: {},
-})
-
-function handleSubmit() {
-  const keys = getSelectRowKeys()
-  if (!keys.length)
-    return useMessage().createMessage.warn('请先选择设备')
-
-  setDrawerProps({ confirmLoading: true })
-  bindingDeviceToGroup(deviceGroupId, keys.join(','))
-    .then(() => {
-      useMessage().createMessage.success('绑定成功')
-      clearSelectedRowKeys()
-      closeDrawer()
-      emit('success')
-    })
-    .catch(noop)
-    .finally(() => {
-      setDrawerProps({ confirmLoading: false })
-    })
-}
-</script>
-
-<template>
-  <BasicDrawer
-    title="绑定设备"
-    width="900px"
-    show-footer
-    @register="register"
-    @ok="handleSubmit"
-  >
-    <BasicTable @register="registerTable" />
-  </BasicDrawer>
-</template>
diff --git a/src/views/device-manage/group/components/GroupFormModal.vue b/src/views/device-manage/group/components/GroupFormModal.vue
deleted file mode 100644
index 996a0daa..00000000
--- a/src/views/device-manage/group/components/GroupFormModal.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue'
-import { BasicModal, useModalInner } from '@/components/Modal'
-import { BasicForm, useForm } from '@/components/Form'
-import { createDeviceGroup, getDevicegroupDetail, updateDeviceGroup } from '@/api/device-manage/group'
-import type { DeviceGroup } from '@/api/device-manage/group/types'
-import { useMessage } from '@/hooks/web/useMessage'
-import { noop } from '@/utils'
-
-const emit = defineEmits(['register', 'success'])
-
-const [registerForm, { validate, setFieldsValue }] = useForm({
-  schemas: [
-    {
-      field: 'groupName',
-      fields: ['parentId', 'id'],
-      label: '群组名称',
-      required: true,
-      component: 'Input',
-      componentProps: {
-        maxlength: 30,
-        showCount: true,
-      },
-    },
-    {
-      field: 'remark',
-      label: '群组描述',
-      component: 'InputTextArea',
-      componentProps: {
-        rows: 5,
-      },
-    },
-  ],
-  labelWidth: 80,
-  baseColProps: { span: 24 },
-  showActionButtonGroup: false,
-  actionColOptions: { span: 23 },
-})
-
-const modalTitle = ref('新增根群组')
-
-const [register, { setModalProps, closeModal }] = useModalInner((data: { parentId: string } | { id: string }) => {
-  if ('id' in data) {
-    modalTitle.value = '编辑群组'
-    setModalProps({ loading: true, confirmLoading: true })
-    getDevicegroupDetail(data.id)
-      .then((res) => {
-        setFieldsValue(res)
-      })
-      .catch(noop)
-      .finally(() => {
-        setModalProps({ loading: false, confirmLoading: false })
-      })
-  }
-  else {
-    modalTitle.value = '新增子群组'
-    setFieldsValue(data)
-  }
-})
-
-async function handleSubmit() {
-  try {
-    const values = await validate<DeviceGroup>()
-    setModalProps({ confirmLoading: true })
-    await (values.id ? updateDeviceGroup(values) : createDeviceGroup(values))
-    closeModal()
-    emit('success')
-    useMessage().createMessage.success('保存成功')
-  }
-  catch {}
-  finally {
-    setModalProps({ confirmLoading: false })
-  }
-}
-</script>
-
-<template>
-  <BasicModal
-    :title="modalTitle"
-    :after-close="() => modalTitle = '新增根群组'"
-    @register="register"
-    @ok="handleSubmit"
-  >
-    <BasicForm @register="registerForm" />
-  </BasicModal>
-</template>
diff --git a/src/views/device-manage/group/components/GroupList.vue b/src/views/device-manage/group/components/GroupList.vue
deleted file mode 100644
index d0c375fd..00000000
--- a/src/views/device-manage/group/components/GroupList.vue
+++ /dev/null
@@ -1,112 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue'
-import { PlusOutlined, SyncOutlined } from '@ant-design/icons-vue'
-import { useAsyncState } from '@vueuse/core'
-import { Empty, Popconfirm, Space, Tree } from 'ant-design-vue'
-import type { EventDataNode } from 'ant-design-vue/es/tree'
-import GroupFormModal from './GroupFormModal.vue'
-import { useModal } from '@/components/Modal'
-import { deleteDevicegroup, getDeviceGroupTree } from '@/api/device-manage/group'
-import { useMessage } from '@/hooks/web/useMessage'
-import { usePermission } from '@/hooks/web/usePermission'
-
-defineProps<{ selectedGroupId: string | undefined }>()
-const emit = defineEmits(['update:selectedGroupId', 'change'])
-
-const [registerModal, { openModal }] = useModal<{ id: string } | { parentId: string }>()
-
-const { state, execute } = useAsyncState(getDeviceGroupTree, [], { resetOnExecute: false })
-
-async function handleDelete(id: string) {
-  try {
-    await deleteDevicegroup(id)
-    useMessage().createMessage.success('删除成功')
-    execute()
-  }
-  catch {}
-}
-
-const selectedKeys = ref<string[]>([])
-
-function onSelectNode(_, { selected, node }: { selected: boolean, node: EventDataNode }) {
-  // can't unselect
-  if (!selected)
-    return selectedKeys.value = [node.key as string]
-
-  emit('update:selectedGroupId', node.key)
-  emit('change')
-}
-
-const { hasPermission } = usePermission()
-</script>
-
-<template>
-  <div rounded="6px" px="10px" pt="12px" pb="6px" border-box bg="white dark:[var(--component-background)]" min-w="360px">
-    <div>
-      <div flex="~ items-center justify-between" h="35px">
-        <a-button v-if="hasPermission('device_group_add')" size="small" @click="openModal()">
-          <PlusOutlined />
-          添加根分组
-        </a-button>
-        <span v-else /> <!-- ghost -->
-
-        <a-button size="small" @click="execute">
-          <SyncOutlined />
-          刷新
-        </a-button>
-      </div>
-
-      <div mt="20px">
-        <Tree v-model:selected-keys="selectedKeys" :tree-data="state" :field-names="{ key: 'id' }" @select="onSelectNode">
-          <template #title="{ title, data }">
-            <div flex="~ items-center justify-between" py="8px" px="5px" box-border>
-              <div flex="1" width="0" truncate :title="title">
-                {{ title }}
-              </div>
-
-              <Space>
-                <span
-                  v-if="hasPermission('device_group_add')"
-                  class="i-ant-design:plus-outlined"
-                  title="添加子分组"
-                  @click="openModal(true, { parentId: data.id })"
-                />
-                <span
-                  v-if="hasPermission('device_group_edit')"
-                  class="i-ant-design:edit-outlined"
-                  title="编辑分组"
-                  @click="openModal(true, { id: data.id })"
-                />
-                <Popconfirm
-                  v-if="hasPermission('device_group_delete')"
-                  title="是否要删除数据?"
-                  :class="[data.hasChildren ? 'text-gray-300 cursor-not-allowed' : '']"
-                  :disabled="data.hasChildren"
-                  @confirm="handleDelete(data.id)"
-                >
-                  <span class="i-ant-design:delete-outlined" :title="data.hasChildren ? '存在子分组, 无法删除' : '删除分组'" />
-                </Popconfirm>
-              </Space>
-            </div>
-          </template>
-        </Tree>
-        <Empty v-if="!state.length" :image="Empty.PRESENTED_IMAGE_SIMPLE" />
-      </div>
-    </div>
-
-    <GroupFormModal @register="registerModal" @success="execute" />
-  </div>
-</template>
-
-<style scoped lang="less">
-:deep(.ant-tree-treenode) {
-  width: 100%;
-}
-:deep(.ant-tree-node-content-wrapper) {
-  flex: 1;
-  width: 0;
-}
-:deep(.ant-tree-switcher-icon) {
-  margin-top: 15px;
-}
-</style>
diff --git a/src/views/device-manage/group/components/index.ts b/src/views/device-manage/group/components/index.ts
deleted file mode 100644
index 39614a1d..00000000
--- a/src/views/device-manage/group/components/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { default as GroupList } from './GroupList.vue'
-export { default as BindingDeviceDrawer } from './BindingDeviceDrawer.vue'
diff --git a/src/views/device-manage/group/data.ts b/src/views/device-manage/group/data.ts
deleted file mode 100644
index 9f255d17..00000000
--- a/src/views/device-manage/group/data.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import { h } from 'vue'
-import { Tag } from 'ant-design-vue'
-import type { BasicColumn, FormSchema } from '@/components/Table'
-import { useSharedProducts } from '@/views/subscription/list/data'
-
-export function useColumns(): BasicColumn[] {
-  const { products } = useSharedProducts()
-  return [
-    {
-      title: '所属产品',
-      dataIndex: 'productId',
-      customRender({ value }) {
-        return products.value.find(item => item.id === value)?.productName
-      },
-    },
-    {
-      title: '设备序列号',
-      dataIndex: 'deviceSn',
-    },
-    {
-      title: '是否在线',
-      dataIndex: 'isOnline',
-      customRender({ value }) {
-        return h(Tag, {
-          color: value ? 'green' : 'default',
-        }, () => value ? '在线' : '离线')
-      },
-    },
-  ]
-}
-
-export function useSearchFormSchemas(): FormSchema[] {
-  const { products } = useSharedProducts()
-  return [
-    {
-      label: '所属产品',
-      field: 'productId',
-      component: 'Select',
-      componentProps: {
-        options: products as any,
-        showSearch: true,
-        fieldNames: {
-          label: 'productName',
-          value: 'id',
-        },
-      },
-      colProps: {
-        span: 8,
-      },
-    },
-    {
-      label: '设备序列号',
-      field: 'deviceSn',
-      component: 'Input',
-      colProps: {
-        span: 8,
-      },
-    },
-  ]
-}
diff --git a/src/views/device-manage/group/index.vue b/src/views/device-manage/group/index.vue
deleted file mode 100644
index 6e4097d6..00000000
--- a/src/views/device-manage/group/index.vue
+++ /dev/null
@@ -1,126 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue'
-import { Space } from 'ant-design-vue'
-import { DisconnectOutlined, LinkOutlined } from '@ant-design/icons-vue'
-import { BindingDeviceDrawer, GroupList } from './components'
-import { useColumns, useSearchFormSchemas } from './data'
-import { BasicTable, TableAction, useTable } from '@/components/Table'
-import { useMessage } from '@/hooks/web/useMessage'
-import { getDeviceListByGroup, unbindingDeviceFromGroup } from '@/api/device-manage/group'
-import { useDrawer } from '@/components/Drawer'
-import { noop } from '@/utils'
-import { usePermission } from '@/hooks/web/usePermission'
-
-const { hasPermission } = usePermission()
-
-const [register, { getSelectRowKeys, reload }] = useTable({
-  async api(params) {
-    if (!selectedGroupId.value)
-      return []
-
-    return getDeviceListByGroup({
-      ...params,
-      groupId: selectedGroupId.value,
-    })
-  },
-  rowKey: 'id',
-  columns: useColumns(),
-  formConfig: {
-    schemas: useSearchFormSchemas(),
-    labelWidth: 90,
-  },
-  rowSelection: {},
-  bordered: true,
-  canResize: false,
-  useSearchForm: true,
-  actionColumn: {
-    width: 210,
-    title: '操作',
-    dataIndex: 'action',
-    auth: ['device_group_unbinding', 'device_group_device_view'],
-  },
-})
-
-const [registerBindingDeviceDrawer, { openDrawer }] = useDrawer()
-
-const selectedGroupId = ref<string | undefined>()
-
-const message = useMessage()
-function handleBindingDivice() {
-  if (!selectedGroupId.value)
-    return message.createMessage.warn('请先选择分组')
-
-  openDrawer(true, selectedGroupId.value)
-}
-function handleUnbindingDivice(id?: string) {
-  const selectionKeys = id ? [id] : getSelectRowKeys()
-  if (!selectionKeys.length)
-    return message.createMessage.warn('请先选择设备')
-
-  function execute() {
-    unbindingDeviceFromGroup(selectedGroupId.value!, selectionKeys.join(','))
-      .then(() => {
-        message.createMessage.success('解绑成功')
-        reload()
-      })
-      .catch(noop)
-  }
-
-  if (id)
-    return execute()
-
-  // 批量绑定设备二次确认
-  message.createConfirm({
-    iconType: 'warning',
-    title: '是否要解绑选择的设备?',
-    onOk: execute,
-  })
-}
-</script>
-
-<template>
-  <div flex="~">
-    <GroupList v-model:selectedGroupId="selectedGroupId" my="12px" ml="12px" @change="reload" />
-    <BasicTable flex="1" @register="register">
-      <template v-if="hasPermission(['device_group_binding', 'device_group_unbinding'])" #tableTitle>
-        <Space>
-          <a-button v-if="hasPermission('device_group_binding')" @click="handleBindingDivice">
-            <LinkOutlined />
-            批量绑定设备
-          </a-button>
-          <a-button v-if="hasPermission('device_group_unbinding')" @click="handleUnbindingDivice()">
-            <DisconnectOutlined />
-            批量解绑设备
-          </a-button>
-        </Space>
-      </template>
-
-      <template #bodyCell="{ column, record }">
-        <template v-if="column.key === 'action'">
-          <TableAction
-            :actions="[
-              {
-                icon: 'i-ant-design:file-search-outlined',
-                label: '设备详情',
-                auth: 'device_group_device_view',
-                onClick: () => $router.push(`/device-manage/device/detail/${record.id}`),
-              },
-              {
-                icon: 'i-ant-design:edit-outlined',
-                label: '解绑设备',
-                auth: 'device_group_unbinding',
-                popConfirm: {
-                  title: '是否要解绑该设备?',
-                  placement: 'left',
-                  confirm: () => handleUnbindingDivice(record.id),
-                },
-              },
-            ]"
-          />
-        </template>
-      </template>
-    </BasicTable>
-
-    <BindingDeviceDrawer @register="registerBindingDeviceDrawer" @success="reload" />
-  </div>
-</template>
diff --git a/src/views/monitor-ops/log/MessageContentModal.vue b/src/views/monitor-ops/log/MessageContentModal.vue
deleted file mode 100644
index ffb1e466..00000000
--- a/src/views/monitor-ops/log/MessageContentModal.vue
+++ /dev/null
@@ -1,62 +0,0 @@
-<script lang="ts" setup>
-import { h, ref } from 'vue'
-import { Button } from 'ant-design-vue'
-import { CopyOutlined } from '@ant-design/icons-vue'
-import { getMessageContent } from '@/api/monitor-ops/log'
-import { BasicModal, useModalInner } from '@/components/Modal'
-import { noop } from '@/utils'
-import type { DescItem } from '@/components/Description'
-import { Description } from '@/components/Description'
-import { copyText } from '@/utils/copyTextToClipboard'
-import type { MessageContent } from '@/api/monitor-ops/log/types'
-
-defineEmits(['register'])
-
-const data = ref<MessageContent>()
-const [register] = useModalInner((id: string) => {
-  getMessageContent(id)
-    .then((res) => {
-      data.value = res
-    })
-    .catch(noop)
-})
-
-const schema: DescItem[] = [
-  {
-    field: 'topic',
-    label: 'Topic',
-  },
-  {
-    field: 'createTime',
-    label: '时间',
-  },
-  {
-    field: 'message',
-    label: '内容',
-    render(val) {
-      let content = val
-      try {
-        content = JSON.stringify(JSON.parse(val), null, 2)
-      }
-      catch {}
-      return h('div', [
-        h(Button, {
-          size: 'small',
-          onClick: () => copyText(content),
-        }, () => [h(CopyOutlined), '复制']),
-        h('pre', {
-          style: {
-            marginTop: '10px',
-          },
-        }, content),
-      ])
-    },
-  },
-]
-</script>
-
-<template>
-  <BasicModal title="消息内容" :footer="null" @register="register">
-    <Description :data="data" :schema="schema" :column="1" />
-  </BasicModal>
-</template>
diff --git a/src/views/monitor-ops/log/data.ts b/src/views/monitor-ops/log/data.ts
deleted file mode 100644
index 14e6f2f6..00000000
--- a/src/views/monitor-ops/log/data.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-import { h } from 'vue'
-import { Tag } from 'ant-design-vue'
-import dayjs from 'dayjs'
-import type { BasicColumn, FormSchema } from '@/components/Table'
-import { useSystemEnumStore } from '@/store/modules/systemEnum'
-import { useSharedProducts } from '@/views/subscription/list/data'
-
-export function useColumns(): BasicColumn[] {
-  const { products } = useSharedProducts()
-  const { getSystemEnumLabel } = useSystemEnumStore()
-
-  return [
-    {
-      title: '所属产品',
-      dataIndex: 'productId',
-      customRender({ value }) {
-        return products.value.find(item => item.id === value)?.productName
-      },
-    },
-    {
-      title: '设备序列号',
-      dataIndex: 'deviceSn',
-    },
-    {
-      title: 'TraceID',
-      dataIndex: 'traceId',
-    },
-    {
-      title: '消息内容',
-      dataIndex: 'messageId',
-      width: 120,
-    },
-    {
-      title: '业务类型',
-      dataIndex: 'bizType',
-      width: 150,
-      customRender({ value }) {
-        return h(Tag, () => getSystemEnumLabel('eDeviceLogBizType', value))
-      },
-    },
-    {
-      title: '操作',
-      dataIndex: 'operation',
-    },
-    {
-      title: '时间',
-      dataIndex: 'createTime',
-    },
-    {
-      title: '状态',
-      dataIndex: 'code',
-      width: 120,
-      customRender({ value }) {
-        return h('b', {
-          style: {
-            color: value === 200 ? '#16a34a' : '#dc2626',
-          },
-        }, value)
-      },
-    },
-  ]
-}
-
-export function useSearchFormSchema(): FormSchema[] {
-  const { products } = useSharedProducts()
-  const { getSystemEnums } = useSystemEnumStore()
-
-  return [
-    {
-      field: 'productId',
-      label: '所属产品',
-      component: 'Select',
-      componentProps: {
-        options: products as any,
-        showSearch: true,
-        fieldNames: {
-          label: 'productName',
-          value: 'id',
-        },
-      },
-      colProps: {
-        span: 6,
-      },
-    },
-    {
-      field: 'deviceSn',
-      label: '设备序列号',
-      component: 'Input',
-      colProps: {
-        span: 6,
-      },
-    },
-    {
-      field: 'traceId',
-      label: 'TraceId',
-      component: 'Input',
-      colProps: {
-        span: 6,
-      },
-    },
-    {
-      field: 'bizType',
-      label: '业务类型',
-      component: 'Select',
-      componentProps: {
-        options: getSystemEnums('eDeviceLogBizType'),
-      },
-      colProps: {
-        span: 6,
-      },
-    },
-    {
-      field: 'time',
-      label: '日志时间',
-      component: 'RangePicker',
-      componentProps: {
-        showTime: true,
-        disabledDate(current) {
-          return current && current > dayjs().endOf('day')
-        },
-      },
-      colProps: {
-        span: 6,
-      },
-    },
-  ]
-}
diff --git a/src/views/monitor-ops/log/index.vue b/src/views/monitor-ops/log/index.vue
deleted file mode 100644
index a759a86c..00000000
--- a/src/views/monitor-ops/log/index.vue
+++ /dev/null
@@ -1,44 +0,0 @@
-<script lang="ts" setup>
-import { useColumns, useSearchFormSchema } from './data'
-import MessageContentModal from './MessageContentModal.vue'
-import { getLogList } from '@/api/monitor-ops/log'
-import { BasicTable, useTable } from '@/components/Table'
-import { useModal } from '@/components/Modal'
-import type { GetLogListParams, Log } from '@/api/monitor-ops/log/types'
-
-const [registerModal, { openModal }] = useModal<string>()
-
-const [register] = useTable({
-  api(params: GetLogListParams & { time: [string, string] }) {
-    return getLogList({
-      ...params,
-      queryStartTime: params.time && params.time[0],
-      queryEndTime: params.time && params.time[1],
-    })
-  },
-  columns: useColumns(),
-  formConfig: {
-    labelWidth: 90,
-    schemas: useSearchFormSchema(),
-  },
-  bordered: true,
-  canResize: false,
-  useSearchForm: true,
-})
-</script>
-
-<template>
-  <div>
-    <BasicTable :api="async () => ([] as Log[])" @register="register">
-      <template #bodyCell="{ column, record }">
-        <template v-if="column.key === 'messageId'">
-          <a-button v-if="record.messageId" type="link" size="small" @click="openModal(true, record.messageId)">
-            查看
-          </a-button>
-        </template>
-      </template>
-    </BasicTable>
-
-    <MessageContentModal @register="registerModal" />
-  </div>
-</template>
diff --git a/src/views/product/ProductFormModal.vue b/src/views/product/ProductFormModal.vue
deleted file mode 100644
index 2469206c..00000000
--- a/src/views/product/ProductFormModal.vue
+++ /dev/null
@@ -1,55 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue'
-import { getFormSchema } from './data'
-import { useMessage } from '@/hooks/web/useMessage'
-import { BasicForm, useForm } from '@/components/Form'
-import { BasicModal, useModalInner } from '@/components/Modal'
-import { createProduct, updateProduct } from '@/api/product'
-import type { Product } from '@/api/product/types'
-
-defineOptions({ name: 'ProductFormModal' })
-
-const emit = defineEmits(['success', 'register'])
-
-const isUpdate = ref(false)
-const [registerForm, { setFieldsValue, validate }] = useForm({
-  labelWidth: 100,
-  baseColProps: { span: 24 },
-  schemas: getFormSchema(isUpdate),
-  showActionButtonGroup: false,
-  actionColOptions: { span: 23 },
-})
-
-const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: Product) => {
-  isUpdate.value = true
-  setFieldsValue({ ...data })
-})
-
-async function handleSubmit() {
-  try {
-    const values = await validate<Product>()
-    setModalProps({ confirmLoading: true })
-    await (isUpdate.value ? updateProduct(values) : createProduct(values))
-    closeModal()
-    emit('success')
-    useMessage().createMessage.success('保存成功')
-  }
-  catch {}
-  finally {
-    setModalProps({ confirmLoading: false })
-  }
-}
-</script>
-
-<template>
-  <BasicModal
-    width="30%"
-    :min-height="100"
-    :title="isUpdate ? '编辑' : '新增'"
-    :after-close="() => isUpdate = false"
-    @register="registerModal"
-    @ok="handleSubmit"
-  >
-    <BasicForm @register="registerForm" />
-  </BasicModal>
-</template>
diff --git a/src/views/product/components/Model.vue b/src/views/product/components/Model.vue
deleted file mode 100644
index 19e2799a..00000000
--- a/src/views/product/components/Model.vue
+++ /dev/null
@@ -1,138 +0,0 @@
-<script setup lang='ts'>
-import { Alert, Popconfirm } from 'ant-design-vue'
-import { useRoute } from 'vue-router'
-import { EyeOutlined, PlusOutlined, SyncOutlined } from '@ant-design/icons-vue'
-import { useModelService } from './composables/useModelService'
-import { useModelAttribute } from './composables/useModelAttribute'
-import ModelServiceFormModal from './ModelServiceFormModal.vue'
-import ModelAttributeFormModal from './ModelAttributeFormModal.vue'
-import { BasicTable, TableAction } from '@/components/Table'
-import type { ModelAttribute } from '@/api/product/types'
-import JsonPreviewModal from '@/components/JsonPreviewModal'
-import { usePermission } from '@/hooks/web/usePermission'
-
-defineProps<{ tsl?: string }>()
-
-const route = useRoute()
-const {
-  selectedModelId,
-  setSelectedModelId,
-  modelServiceList,
-  handleDeleteModelService,
-  registerModelServiceModal,
-  openModelServiceModal,
-  reloadModelService,
-} = useModelService(route.params.id as string, route.params.modelId as string)
-
-const {
-  registerModelAttributeTable,
-  registerModelAttributeModal,
-  openModelAttributeModal,
-  reloadModalAttribute,
-  handleDeleteModelAttribute,
-} = useModelAttribute(route.params.id as string, selectedModelId)
-
-const { hasPermission } = usePermission()
-</script>
-
-<template>
-  <div>
-    <div mb="12px">
-      <JsonPreviewModal title="物模型 TSL" :data="tsl || ''">
-        <a-button type="primary">
-          <EyeOutlined />
-          物模型 TSL
-        </a-button>
-      </JsonPreviewModal>
-    </div>
-    <Alert type="info" show-icon class="py-4px text-13px" message="产品模型用于描述设备具备的能力和特性。通过定义产品模型,在平台构建一款设备的抽象模型,使平台理解该款设备支持的服务、属性、命令等信息,如颜色、开关等。当定义完一款产品模型后,在进行注册设备时,就可以使用在控制台上定义的产品模型。" />
-
-    <div flex="~ gap-12px" mt="12px">
-      <div w="20%" border="0 r-1 solid gray-50 dark:white dark:opacity-5">
-        <div flex="~ items-center justify-between" border="0 b-1 solid gray-200 dark:white dark:opacity-5" class="mb-12px box-border h-40px px-10px">
-          <div font-bold>
-            服务列表
-          </div>
-          <div v-if="hasPermission('product_model_service_add')" class="i-ant-design:plus-outlined cursor-pointer text-20px" @click="openModelServiceModal()" />
-        </div>
-
-        <div
-          v-for="item in modelServiceList" :key="item.id"
-          flex="~ items-center justify-between"
-          class="box-border h-60px cursor-pointer pl-10px hover:bg-gray-100 hover:dark:bg-white hover:dark:bg-opacity-5"
-          border="0 b-1 solid gray-50 dark:white dark:opacity-5"
-          :class="selectedModelId === item.id ? 'bg-gray-100 dark:bg-white dark:bg-opacity-5' : ''"
-          @click="setSelectedModelId(item.id); reloadModalAttribute(true)"
-        >
-          <div w-0 flex-1>
-            <div truncate>
-              {{ item.serviceId }}
-            </div>
-            <div mt="5px" text="12px gray-500" truncate :title="item.description">
-              {{ item.description }}
-            </div>
-          </div>
-          <div v-if="item.serviceId !== 'DEFAULT'">
-            <a-button v-if="hasPermission('product_model_service_edit')" type="link" size="small" @click="openModelServiceModal(true, item)">
-              <span class="i-ant-design:edit-outlined" />
-            </a-button>
-            <Popconfirm v-if="hasPermission('product_model_service_delete')" title="是否要删除数据?" @confirm="handleDeleteModelService(item.id)">
-              <a-button type="link" size="small" danger>
-                <span class="i-ant-design:delete-outlined" />
-              </a-button>
-            </Popconfirm>
-          </div>
-        </div>
-      </div>
-
-      <div w-0 flex-1>
-        <BasicTable :api="async () => ([] as ModelAttribute[])" @register="registerModelAttributeTable">
-          <template v-if="hasPermission('product_model_attr_add')" #tableTitle>
-            <a-button type="primary" @click="openModelAttributeModal">
-              <PlusOutlined />
-              新增属性
-            </a-button>
-          </template>
-
-          <template #toolbar>
-            <a-button size="small" @click="reloadModalAttribute()">
-              <template #icon>
-                <SyncOutlined />
-              </template>
-              刷新
-            </a-button>
-          </template>
-
-          <template #bodyCell="{ column, record }">
-            <template v-if="column.key === 'action'">
-              <TableAction
-                :actions="[
-                  {
-                    icon: 'i-ant-design:edit-outlined',
-                    label: '编辑',
-                    auth: 'product_model_attr_edit',
-                    onClick: () => openModelAttributeModal(true, record),
-                  },
-                  {
-                    icon: 'i-ant-design:delete-outlined',
-                    danger: true,
-                    label: '删除',
-                    auth: 'product_model_attr_delete',
-                    popConfirm: {
-                      title: '是否要删除数据?',
-                      placement: 'left',
-                      confirm: () => handleDeleteModelAttribute(record.id),
-                    },
-                  },
-                ]"
-              />
-            </template>
-          </template>
-        </BasicTable>
-      </div>
-    </div>
-
-    <ModelServiceFormModal @register="registerModelServiceModal" @success="reloadModelService" />
-    <ModelAttributeFormModal :model-id="selectedModelId || ''" @register="registerModelAttributeModal" @success="reloadModalAttribute()" />
-  </div>
-</template>
diff --git a/src/views/product/components/ModelAttributeFormModal.vue b/src/views/product/components/ModelAttributeFormModal.vue
deleted file mode 100644
index 768a27b5..00000000
--- a/src/views/product/components/ModelAttributeFormModal.vue
+++ /dev/null
@@ -1,234 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue'
-import { FormItem, Input, InputNumber } from 'ant-design-vue'
-import { useMessage } from '@/hooks/web/useMessage'
-import { BasicForm, useForm } from '@/components/Form'
-import { BasicModal, useModalInner } from '@/components/Modal'
-import { createModelAttribute, updateModelAttribute } from '@/api/product/model'
-import { ModelAttributeDataTypesEnum } from '@/api/product/types'
-import type { ModelAttribute, ModelAttributeWithForm } from '@/api/product/types'
-
-defineOptions({ name: 'ModelAttributeFormModal' })
-const props = defineProps<{ modelId: string }>()
-
-const emit = defineEmits(['success', 'register'])
-
-const isUpdate = ref(false)
-const [registerForm, { setFieldsValue, validate }] = useForm({
-  labelWidth: 80,
-  baseColProps: { span: 24 },
-  schemas: [
-    {
-      field: 'id',
-      show: false,
-      component: 'Input',
-    },
-    {
-      field: 'itemType',
-      label: '功能类型',
-      required: true,
-      component: 'Select',
-      defaultValue: 1,
-      componentProps: {
-        options: [
-          { label: '属性', value: 1 },
-          { label: '命令', value: 2 },
-        ],
-      },
-      dynamicDisabled: () => isUpdate.value,
-    },
-    {
-      field: 'name',
-      label: '功能名称',
-      required: true,
-      component: 'Input',
-    },
-    {
-      field: 'identifier',
-      label: '标识符',
-      required: true,
-      component: 'Input',
-    },
-    {
-      field: 'dataType',
-      label: '数据类型',
-      required: true,
-      component: 'Select',
-      componentProps: {
-        options: Object.values(ModelAttributeDataTypesEnum).map(value => ({ label: value, value })),
-      },
-      dynamicDisabled: () => isUpdate.value,
-    },
-
-    // int32 / float schemas
-    {
-      field: 'valueScope',
-      fields: ['min', 'max'],
-      label: '取值范围',
-      slot: 'ValueScope',
-      required: true,
-      itemProps: {
-        autoLink: false,
-      },
-      defaultValue: '_', // skip the required check
-      ifShow: ({ values }) => [ModelAttributeDataTypesEnum.Int32, ModelAttributeDataTypesEnum.Float].includes(values.dataType),
-    },
-    {
-      field: 'scale',
-      label: '精度',
-      required: true,
-      helpMessage: '小数位',
-      component: 'InputNumber',
-      componentProps: {
-        precision: 0,
-      },
-      ifShow: ({ values }) => values.dataType === ModelAttributeDataTypesEnum.Float,
-    },
-    {
-      field: 'unit',
-      label: '单位',
-      component: 'Input',
-      ifShow: ({ values }) => [ModelAttributeDataTypesEnum.Int32, ModelAttributeDataTypesEnum.Float].includes(values.dataType),
-    },
-
-    // bool schemas
-    {
-      field: 'bool',
-      fields: ['trueDesc', 'falseDesc'],
-      label: '布尔值',
-      slot: 'Bool',
-      required: true,
-      itemProps: {
-        autoLink: false,
-      },
-      defaultValue: '_', // skip the required check
-      ifShow: ({ values }) => values.dataType === ModelAttributeDataTypesEnum.Bool,
-    },
-
-    // text schemas
-    {
-      field: 'maxLength',
-      label: '数据长度',
-      required: true,
-      component: 'InputNumber',
-      componentProps: {
-        precision: 0,
-      },
-      ifShow: ({ values }) => values.dataType === ModelAttributeDataTypesEnum.Text,
-    },
-
-    {
-      field: 'method',
-      label: '读写',
-      component: 'Select',
-      defaultValue: 'r',
-      componentProps: {
-        options: [
-          { label: '只读', value: 'r' },
-          { label: '读写', value: 'rw' },
-        ],
-      },
-    },
-    {
-      field: 'sort',
-      label: '排序',
-      component: 'InputNumber',
-      componentProps: {
-        precision: 0,
-      },
-    },
-  ],
-  showActionButtonGroup: false,
-  actionColOptions: { span: 23 },
-})
-
-const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: ModelAttribute) => {
-  isUpdate.value = true
-  setFieldsValue({ ...data, ...(data.dataSpecs || {}) })
-})
-
-async function handleSubmit() {
-  try {
-    const values = await validate<ModelAttributeWithForm>()
-    setModalProps({ confirmLoading: true })
-    values.modelId = props.modelId
-    await (isUpdate.value ? updateModelAttribute(values) : createModelAttribute(values))
-    closeModal()
-    emit('success')
-    useMessage().createMessage.success('保存成功')
-  }
-  catch {}
-  finally {
-    setModalProps({ confirmLoading: false })
-  }
-}
-
-function validatorValueScope(value1: number, value2: number) {
-  // eslint-disable-next-line prefer-promise-reject-errors
-  return value1 > value2 ? Promise.reject() : Promise.resolve()
-}
-</script>
-
-<template>
-  <BasicModal
-    width="40%"
-    :min-height="100"
-    :title="isUpdate ? '编辑' : '新增'"
-    :after-close="() => isUpdate = false"
-    @register="registerModal"
-    @ok="handleSubmit"
-  >
-    <BasicForm @register="registerForm">
-      <template #ValueScope="{ model }">
-        <div flex="~ justify-between gap-12px" class="custom-form-slot">
-          <FormItem
-            name="min"
-            :rules="[
-              { required: true, message: '请填写最小值' },
-              { validator: (_, min) => validatorValueScope(min, model.max), message: '最小值不能大于最大值' },
-            ]"
-          >
-            <InputNumber v-model:value="model.min" placeholder="最小值" />
-          </FormItem>
-
-          <span mt="5px">-</span>
-
-          <FormItem
-            name="max"
-            :rules="[
-              { required: true, message: '请填写最大值' },
-              { validator: (_, max) => validatorValueScope(model.min, max), message: '最大值不能小于最小值' },
-            ]"
-          >
-            <InputNumber v-model:value="model.max" placeholder="最大值" />
-          </FormItem>
-        </div>
-      </template>
-
-      <template #Bool="{ model }">
-        <div flex="~ justify-between gap-12px" class="custom-form-slot">
-          <FormItem name="trueDesc" :rules="[{ required: true, message: '请填写布尔值' }]">
-            <Input v-model:value="model.trueDesc" addon-before="0" placeholder="如:是" />
-          </FormItem>
-
-          <span mt="5px">-</span>
-
-          <FormItem name="falseDesc" :rules="[{ required: true, message: '请填写布尔值' }]">
-            <Input v-model:value="model.falseDesc" addon-before="1" placeholder="如:否" />
-          </FormItem>
-        </div>
-      </template>
-    </BasicForm>
-  </BasicModal>
-</template>
-
-<style scoped lang="less">
-.custom-form-slot .ant-form-item {
-  margin-bottom: 0;
-  flex: 1;
-
-  :deep(.ant-form-item-explain-error) {
-    position: absolute;
-  }
-}
-</style>
diff --git a/src/views/product/components/ModelServiceFormModal.vue b/src/views/product/components/ModelServiceFormModal.vue
deleted file mode 100644
index 0eb833b9..00000000
--- a/src/views/product/components/ModelServiceFormModal.vue
+++ /dev/null
@@ -1,79 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue'
-import { useRoute } from 'vue-router'
-import { useMessage } from '@/hooks/web/useMessage'
-import { BasicForm, useForm } from '@/components/Form'
-import { BasicModal, useModalInner } from '@/components/Modal'
-import { createModelService, updateModelService } from '@/api/product/model'
-import type { ModelService } from '@/api/product/types'
-
-defineOptions({ name: 'ModelServiceFormModal' })
-
-const emit = defineEmits(['success', 'register'])
-
-const isUpdate = ref(false)
-const [registerForm, { setFieldsValue, validate }] = useForm({
-  labelWidth: 80,
-  baseColProps: { span: 24 },
-  schemas: [
-    {
-      field: 'id',
-      show: false,
-      component: 'Input',
-    },
-    {
-      field: 'serviceId',
-      label: '服务 ID',
-      required: true,
-      component: 'Input',
-      componentProps: {
-        maxlength: 30,
-        showCount: true,
-      },
-    },
-    {
-      field: 'description',
-      label: '服务描述',
-      required: true,
-      component: 'InputTextArea',
-    },
-  ],
-  showActionButtonGroup: false,
-  actionColOptions: { span: 23 },
-})
-
-const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: ModelService) => {
-  isUpdate.value = true
-  setFieldsValue({ ...data })
-})
-
-const route = useRoute()
-async function handleSubmit() {
-  try {
-    const values = await validate<ModelService>()
-    setModalProps({ confirmLoading: true })
-    values.productId = route.params.id as string
-    await (isUpdate.value ? updateModelService(values) : createModelService(values))
-    closeModal()
-    emit('success')
-    useMessage().createMessage.success('保存成功')
-  }
-  catch {}
-  finally {
-    setModalProps({ confirmLoading: false })
-  }
-}
-</script>
-
-<template>
-  <BasicModal
-    width="30%"
-    :min-height="100"
-    :title="isUpdate ? '编辑' : '新增'"
-    :after-close="() => isUpdate = false"
-    @register="registerModal"
-    @ok="handleSubmit"
-  >
-    <BasicForm @register="registerForm" />
-  </BasicModal>
-</template>
diff --git a/src/views/product/components/Subscription.vue b/src/views/product/components/Subscription.vue
deleted file mode 100644
index 6de0e2b4..00000000
--- a/src/views/product/components/Subscription.vue
+++ /dev/null
@@ -1,56 +0,0 @@
-<script setup lang="ts">
-import { h } from 'vue'
-import { Alert, Tag } from 'ant-design-vue'
-import { LinkOutlined } from '@ant-design/icons-vue'
-import type { DescItem } from '@/components/Description'
-import { Description } from '@/components/Description'
-import { useSystemEnumStore } from '@/store/modules/systemEnum'
-import type { SubScription } from '@/api/subscription/list/types'
-
-defineProps<{
-  data?: SubScription
-}>()
-
-const { getSystemEnums } = useSystemEnumStore()
-
-const schema: DescItem[] = [
-  {
-    label: '订阅类型',
-    field: 'type',
-    render: () => 'mqtt',
-  },
-  {
-    label: '订阅消息',
-    field: 'messageType',
-    render: (val: string) => {
-      const values = val.split(',')
-      const types = getSystemEnums('eSubscribeMessageType')
-      return h(
-        'div',
-        types
-          .map(item => values.includes(item.value.toString()) ? item.label : null)
-          .filter(Boolean)
-          .map(name => h(Tag, () => name)),
-      )
-    },
-  },
-  {
-    label: '创建时间',
-    field: 'createTime',
-  },
-]
-</script>
-
-<template>
-  <div>
-    <Alert
-      type="info" show-icon class="py-4px"
-      message="服务端订阅:服务端可以直接订阅产品下多种类型的消息,例如设备上报属性、设备上报消息、设备状态变化通知、设备生命周期变更等。配置服务端订阅后配合使用【消费组】,平台会将产品下所有设备中已订阅类型的消息进行转发。"
-    />
-    <a-button type="link" size="small" my="12px" @click="$router.push({ name: 'SubscriptionList', state: { productId: data?.productId } })">
-      <LinkOutlined />
-      管理订阅
-    </a-button>
-    <Description :data="data" :schema="schema" :column="1" :label-style="{ width: '100px' }" />
-  </div>
-</template>
diff --git a/src/views/product/components/TopicFormModal.vue b/src/views/product/components/TopicFormModal.vue
deleted file mode 100644
index a15fc823..00000000
--- a/src/views/product/components/TopicFormModal.vue
+++ /dev/null
@@ -1,93 +0,0 @@
-<script lang="ts" setup>
-import { h, ref } from 'vue'
-import { useRoute } from 'vue-router'
-import { useMessage } from '@/hooks/web/useMessage'
-import { BasicForm, useForm } from '@/components/Form'
-import { BasicModal, useModalInner } from '@/components/Modal'
-import { createTopic, updateTopic } from '@/api/product/topic'
-import type { Topic } from '@/api/product/types'
-import { useSystemEnumStore } from '@/store/modules/systemEnum'
-
-defineOptions({ name: 'CustomTopicFormModal' })
-
-const emit = defineEmits(['success', 'register'])
-
-const isUpdate = ref(false)
-const { getSystemEnums } = useSystemEnumStore()
-const [registerForm, { setFieldsValue, validate }] = useForm({
-  labelWidth: 100,
-  baseColProps: { span: 24 },
-  schemas: [
-    {
-      field: 'id',
-      show: false,
-      component: 'Input',
-    },
-    {
-      field: 'topic',
-      label: 'Topic',
-      required: true,
-      // eslint-disable-next-line no-template-curly-in-string
-      helpMessage: 'Topic必须包含/${deviceSn}/占位符',
-      component: 'Input',
-      rules: [
-        // eslint-disable-next-line no-template-curly-in-string
-        { pattern: /\/\$\{deviceSn}\//, message: h('span', 'Topic必须包含/${deviceSn}/占位符') },
-      ],
-      dynamicDisabled: () => isUpdate.value,
-    },
-    {
-      field: 'topicType',
-      label: '操作权限',
-      required: true,
-      component: 'Select',
-      componentProps: {
-        options: getSystemEnums('eProductTopicType'),
-      },
-      dynamicDisabled: () => isUpdate.value,
-    },
-    {
-      field: 'topicDesc',
-      label: '描述',
-      component: 'InputTextArea',
-    },
-  ],
-  showActionButtonGroup: false,
-  actionColOptions: { span: 23 },
-})
-
-const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: Topic) => {
-  isUpdate.value = true
-  setFieldsValue({ ...data })
-})
-
-const route = useRoute()
-async function handleSubmit() {
-  try {
-    const values = await validate<Topic>()
-    setModalProps({ confirmLoading: true })
-    values.productId = route.params.id as string
-    await (isUpdate.value ? updateTopic(values) : createTopic(values))
-    closeModal()
-    emit('success')
-    useMessage().createMessage.success('保存成功')
-  }
-  catch {}
-  finally {
-    setModalProps({ confirmLoading: false })
-  }
-}
-</script>
-
-<template>
-  <BasicModal
-    width="30%"
-    :min-height="100"
-    :title="isUpdate ? '编辑' : '新增'"
-    :after-close="() => isUpdate = false"
-    @register="registerModal"
-    @ok="handleSubmit"
-  >
-    <BasicForm @register="registerForm" />
-  </BasicModal>
-</template>
diff --git a/src/views/product/components/TopicManage.vue b/src/views/product/components/TopicManage.vue
deleted file mode 100644
index e95376a3..00000000
--- a/src/views/product/components/TopicManage.vue
+++ /dev/null
@@ -1,145 +0,0 @@
-<script setup lang="ts">
-import { Alert, Segmented } from 'ant-design-vue'
-import { computed, ref } from 'vue'
-import { useRoute } from 'vue-router'
-import { PlusOutlined, SyncOutlined } from '@ant-design/icons-vue'
-import TopicFormModal from './TopicFormModal.vue'
-import type { Topic } from '@/api/product/types'
-import { TopicType } from '@/api/product/types'
-import { BasicTable, TableAction, useTable } from '@/components/Table'
-import { deleteTopic, getTopicList } from '@/api/product/topic'
-import { useSystemEnumStore } from '@/store/modules/systemEnum'
-import { useModal } from '@/components/Modal'
-import { useMessage } from '@/hooks/web/useMessage'
-import { usePermission } from '@/hooks/web/usePermission'
-
-const route = useRoute()
-const { getSystemEnumLabel } = useSystemEnumStore()
-
-const [registerModal, { openModal }] = useModal<Topic>()
-
-const topicType = ref<TopicType>(TopicType.System)
-const isCustomTopic = computed(() => topicType.value === TopicType.Custom)
-const alertMessage = computed(
-  () => isCustomTopic.value
-    ? 'Topic用以将设备数据分类上报,进而分别进行处理。除了系统预置的Topic,您也可以自定义Topic,然后在设备侧开发时选择数据上报的Topic。'
-    : '以下是系统Topic,设备侧通过系统预设的topic接收数据时,无需提前订阅。',
-)
-
-const { hasPermission } = usePermission()
-
-const [register, { reload, setTableData }] = useTable({
-  api(params) {
-    return getTopicList({
-      ...params,
-      productId: route.params.id,
-      topicCategory: topicType.value,
-    })
-  },
-  columns: [
-    {
-      title: 'Topic',
-      dataIndex: 'topic',
-    },
-    {
-      title: '操作权限',
-      dataIndex: 'topicType',
-      customRender: ({ value }) => getSystemEnumLabel('eProductTopicType', value),
-    },
-    {
-      title: '开启解析脚本',
-      dataIndex: 'enableScript',
-      ifShow: () => isCustomTopic.value,
-    },
-    {
-      title: '描述',
-      dataIndex: 'topicDesc',
-    },
-  ],
-  bordered: true,
-  inset: true,
-  canResize: false,
-  actionColumn: {
-    width: 150,
-    title: '操作',
-    dataIndex: 'action',
-    fixed: 'right',
-    ifShow: () => isCustomTopic.value,
-    auth: ['product_topic_edit', 'product_topic_delete'],
-  },
-})
-
-function onChangeTopicType() {
-  setTableData([])
-  reload()
-}
-
-async function handleDelete(id: string) {
-  try {
-    await deleteTopic(id)
-    useMessage().createMessage.success('删除成功')
-    reload()
-  }
-  catch {}
-}
-</script>
-
-<template>
-  <div>
-    <div flex="~ items-center justify-between gap-12px" mb="12px">
-      <div flex="~ wrap gap-12px">
-        <Segmented
-          v-model:value="topicType"
-          :options="[{ label: '系统 Topic', value: TopicType.System }, { label: '自定义 Topic', value: TopicType.Custom }]"
-          @change="onChangeTopicType"
-        />
-
-        <a-button v-if="isCustomTopic && hasPermission('product_topic_add')" type="primary" @click="openModal">
-          <PlusOutlined />
-          新增 Topic
-        </a-button>
-
-        <Alert :message="alertMessage" type="info" show-icon class="py-4px" />
-      </div>
-
-      <div class="mr-10px flex cursor-pointer items-center">
-        <a-button size="small" @click="reload">
-          <template #icon>
-            <SyncOutlined />
-          </template>
-          刷新
-        </a-button>
-      </div>
-    </div>
-
-    <BasicTable :api="async () => ([] as Topic[])" @register="register">
-      <template #bodyCell="{ column, record }">
-        <template v-if="column.key === 'action'">
-          <TableAction
-            :actions="[
-              {
-                icon: 'i-ant-design:edit-outlined',
-                label: '编辑',
-                auth: 'product_topic_edit',
-                onClick: () => openModal(true, record),
-              },
-              {
-                icon: 'i-ant-design:delete-outlined',
-                danger: true,
-                label: '删除',
-                auth: 'product_topic_delete',
-                popConfirm: {
-                  title: '是否要删除数据?',
-                  placement: 'left',
-                  confirm: () => handleDelete(record.id),
-                },
-              },
-            ]"
-          />
-        </template>
-      </template>
-    </BasicTable>
-
-    <TopicFormModal @register="registerModal" @success="reload" />
-  </div>
-</template>
diff --git a/src/views/product/components/composables/useModelAttribute.ts b/src/views/product/components/composables/useModelAttribute.ts
deleted file mode 100644
index a5db29be..00000000
--- a/src/views/product/components/composables/useModelAttribute.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { unref } from 'vue'
-import type { MaybeRef, Ref } from 'vue'
-import { useTable } from '@/components/Table'
-import { deleteModelAttribute, getModelAttributeList } from '@/api/product/model'
-import { useModal } from '@/components/Modal'
-import type { ModelAttribute } from '@/api/product/types'
-import { useMessage } from '@/hooks/web/useMessage'
-
-export function useModelAttribute(productId: MaybeRef<string>, modelId: Ref<string | undefined>) {
-  const [registerModelAttributeTable, { reload, setPagination }] = useTable({
-    async api(params) {
-      if (!unref(modelId))
-        return []
-
-      return getModelAttributeList({
-        ...params,
-        productId: unref(productId),
-        modelId: unref(modelId),
-      })
-    },
-    columns: [
-      {
-        title: '功能类型',
-        dataIndex: 'itemType',
-        customRender: ({ value }) => ({ 1: '属性', 2: '命令' }[value]),
-      },
-      {
-        title: '功能名称',
-        dataIndex: 'name',
-      },
-      {
-        title: '标识符',
-        dataIndex: 'identifier',
-      },
-      {
-        title: '数据类型',
-        dataIndex: 'dataType',
-      },
-    ],
-    bordered: true,
-    inset: true,
-    canResize: false,
-    actionColumn: {
-      width: 150,
-      title: '操作',
-      dataIndex: 'action',
-      fixed: 'right',
-      auth: ['product_model_attr_delete', 'product_model_attr_edit'],
-    },
-  })
-
-  const [registerModelAttributeModal, { openModal: openModelAttributeModal }] = useModal<ModelAttribute>()
-
-  async function handleDeleteModelAttribute(id: string) {
-    try {
-      await deleteModelAttribute(id)
-      useMessage().createMessage.success('删除成功')
-      reload()
-    }
-    catch {}
-  }
-
-  return {
-    registerModelAttributeTable,
-    registerModelAttributeModal,
-    openModelAttributeModal,
-    /**
-     * @param resetPageCurrent 是否重置分页
-     */
-    reloadModalAttribute(resetPageCurrent = false) {
-      if (resetPageCurrent)
-        setPagination({ current: 1 })
-
-      reload()
-    },
-    handleDeleteModelAttribute,
-  }
-}
diff --git a/src/views/product/components/composables/useModelService.ts b/src/views/product/components/composables/useModelService.ts
deleted file mode 100644
index 11f2cc72..00000000
--- a/src/views/product/components/composables/useModelService.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { ref } from 'vue'
-import { useAsyncState, watchOnce } from '@vueuse/core'
-import { deleteModelService, getAllModelServices } from '@/api/product/model'
-import { useMessage } from '@/hooks/web/useMessage'
-import { useModal } from '@/components/Modal'
-import type { ModelService } from '@/api/product/types'
-
-export function useModelService(productId: string, defaultModelId?: string) {
-  const selectedModelId = ref<string>()
-
-  const { state, execute } = useAsyncState(() => getAllModelServices(productId), [], {
-    resetOnExecute: false,
-  })
-
-  // 默认选择的 ModelId, 如果没有 defaultModelId 则是第一个元素的 Id
-  watchOnce(state, () => {
-    if (state.value.length > 0)
-      selectedModelId.value = defaultModelId || state.value[0].id
-  })
-
-  const [registerModelServiceModal, { openModal }] = useModal<ModelService>()
-
-  async function handleDeleteModelService(id: string) {
-    try {
-      await deleteModelService(id)
-      useMessage().createMessage.success('删除成功')
-      execute()
-    }
-    catch {}
-  }
-
-  return {
-    selectedModelId,
-    setSelectedModelId: (id: string) => selectedModelId.value = id,
-    reloadModelService: execute,
-    modelServiceList: state,
-    handleDeleteModelService,
-    registerModelServiceModal,
-    openModelServiceModal: openModal,
-  }
-}
diff --git a/src/views/product/components/index.ts b/src/views/product/components/index.ts
deleted file mode 100644
index cf20d6d4..00000000
--- a/src/views/product/components/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { default as TopicManage } from './TopicManage.vue'
-export { default as Model } from './Model.vue'
-export { default as Subscription } from './Subscription.vue'
diff --git a/src/views/product/data.ts b/src/views/product/data.ts
deleted file mode 100644
index 327de5e5..00000000
--- a/src/views/product/data.ts
+++ /dev/null
@@ -1,188 +0,0 @@
-import type { Ref } from 'vue'
-import type { BasicColumn, FormSchema } from '@/components/Table'
-import { useSystemEnumStoreWithOut } from '@/store/modules/systemEnum'
-
-const { getSystemEnumLabel, getSystemEnums } = useSystemEnumStoreWithOut()
-
-export const columns: BasicColumn[] = [
-  {
-    title: '产品名称',
-    dataIndex: 'productName',
-  },
-  {
-    title: '产品标识',
-    dataIndex: 'productKey',
-  },
-  {
-    title: '节点类型',
-    dataIndex: 'nodeType',
-    customRender: ({ value }) => getSystemEnumLabel('eProductNodeType', value),
-  },
-  {
-    title: '联网方式',
-    dataIndex: 'networkType',
-    customRender: ({ value }) => getSystemEnumLabel('eNetworkType', value),
-  },
-  {
-    title: '鉴权方式',
-    dataIndex: 'authType',
-    customRender: ({ value }) => getSystemEnumLabel('eAuthType', value),
-  },
-  {
-    title: '安全类型',
-    dataIndex: 'securityType',
-    customRender: ({ value }) => getSystemEnumLabel('eProductSecurityType', value),
-  },
-  {
-    title: '通信协议',
-    dataIndex: 'networkProtocol',
-    customRender: ({ value }) => getSystemEnumLabel('eNetworkProtocol', value),
-  },
-  {
-    title: '数据格式',
-    dataIndex: 'dataType',
-    customRender: ({ value }) => getSystemEnumLabel('eDataType', value),
-  },
-  {
-    title: '创建时间',
-    dataIndex: 'createTime',
-    width: 200,
-  },
-]
-
-export const searchFormSchemas: FormSchema[] = [
-  {
-    label: '产品名称',
-    field: 'productName',
-    component: 'Input',
-    colProps: { span: 6 },
-  },
-  {
-    label: '联网方式',
-    field: 'networkType',
-    component: 'Select',
-    componentProps: {
-      options: getSystemEnums('eNetworkType'),
-    },
-    colProps: { span: 6 },
-  },
-  {
-    label: '通信协议',
-    field: 'networkProtocol',
-    component: 'Select',
-    componentProps: {
-      options: getSystemEnums('eNetworkProtocol'),
-    },
-    colProps: { span: 6 },
-  },
-  {
-    label: '节点类型',
-    field: 'nodeType',
-    component: 'Select',
-    componentProps: {
-      options: getSystemEnums('eProductNodeType'),
-    },
-    colProps: { span: 6 },
-  },
-  {
-    label: '安全类型',
-    field: 'securityType',
-    component: 'Select',
-    componentProps: {
-      options: getSystemEnums('eProductSecurityType'),
-    },
-    colProps: { span: 6 },
-  },
-  {
-    label: '数据格式',
-    field: 'dataType',
-    component: 'Select',
-    componentProps: {
-      options: getSystemEnums('eDataType'),
-    },
-    colProps: { span: 6 },
-  },
-]
-
-export function getFormSchema(isUpdate: Ref<boolean>): FormSchema[] {
-  return [
-    {
-      field: 'id',
-      show: false,
-      component: 'Input',
-    },
-    {
-      label: '产品名称',
-      field: 'productName',
-      required: true,
-      component: 'Input',
-    },
-    {
-      label: '联网方式',
-      field: 'networkType',
-      component: 'Select',
-      componentProps: {
-        options: getSystemEnums('eNetworkType'),
-      },
-    },
-    {
-      label: '通信协议',
-      field: 'networkProtocol',
-      component: 'Select',
-      componentProps: {
-        options: getSystemEnums('eNetworkProtocol'),
-      },
-      ifShow: () => !isUpdate.value,
-    },
-    {
-      label: '节点类型',
-      field: 'nodeType',
-      required: true,
-      component: 'Select',
-      componentProps: {
-        options: getSystemEnums('eProductNodeType'),
-      },
-      ifShow: () => !isUpdate.value,
-    },
-    {
-      label: '安全类型',
-      field: 'securityType',
-      component: 'Select',
-      componentProps: {
-        options: getSystemEnums('eProductSecurityType'),
-      },
-      ifShow: () => !isUpdate.value,
-    },
-    {
-      label: '鉴权方式',
-      field: 'authType',
-      required: true,
-      component: 'Select',
-      componentProps: {
-        options: getSystemEnums('eAuthType'),
-      },
-      ifShow: () => !isUpdate.value,
-    },
-    {
-      label: '数据格式',
-      field: 'dataType',
-      required: true,
-      component: 'Select',
-      componentProps: {
-        options: getSystemEnums('eDataType'),
-      },
-      ifShow: () => !isUpdate.value,
-    },
-    {
-      label: '产品描述',
-      field: 'productDesc',
-      component: 'InputTextArea',
-    },
-  ]
-}
-
-export enum ProductTabEnums {
-  TopicManage = '1',
-  Model = '2',
-  Subscription = '3',
-}
diff --git a/src/views/product/detail.vue b/src/views/product/detail.vue
deleted file mode 100644
index ead6b75c..00000000
--- a/src/views/product/detail.vue
+++ /dev/null
@@ -1,96 +0,0 @@
-<script setup lang="ts">
-import { Card, Tabs } from 'ant-design-vue'
-import { useAsyncState } from '@vueuse/core'
-import { useRoute } from 'vue-router'
-import { ref } from 'vue'
-import { Model, Subscription, TopicManage } from './components'
-import { ProductTabEnums } from './data'
-import { Description } from '@/components/Description'
-import { getProductDetail } from '@/api/product'
-import type { DescItem } from '@/components/Description'
-import { useSystemEnumStore } from '@/store/modules/systemEnum'
-import { usePermission } from '@/hooks/web/usePermission'
-
-const route = useRoute()
-const { getSystemEnumLabel } = useSystemEnumStore()
-
-const { state } = useAsyncState(() => getProductDetail(route.params.id as string), undefined)
-const schema: DescItem[] = [
-  {
-    label: '产品名称',
-    field: 'productName',
-  },
-  {
-    label: '产品标识(ProductKey)',
-    field: 'productKey',
-  },
-  {
-    label: 'ProductSecret',
-    field: 'productSecret',
-  },
-  {
-    label: '联网方式',
-    field: 'networkType',
-    render: value => getSystemEnumLabel('eNetworkType', value),
-  },
-  {
-    label: '通信协议',
-    field: 'networkProtocol',
-    render: value => getSystemEnumLabel('eNetworkProtocol', value),
-  },
-  {
-    label: '节点类型',
-    field: 'nodeType',
-    render: value => getSystemEnumLabel('eProductNodeType', value),
-  },
-  {
-    label: '安全类型',
-    field: 'securityType',
-    render: value => getSystemEnumLabel('eProductSecurityType', value),
-  },
-  {
-    label: '鉴权方式',
-    field: 'authType',
-    render: value => getSystemEnumLabel('eAuthType', value),
-  },
-  {
-    label: '数据格式',
-    field: 'dataType',
-    render: value => getSystemEnumLabel('eDataType', value),
-  },
-  {
-    label: '创建时间',
-    field: 'createTime',
-  },
-  {
-    label: '产品描述',
-    field: 'productDesc',
-  },
-]
-
-const activeTab = ref<ProductTabEnums>(route.params.activeTab as unknown as ProductTabEnums || ProductTabEnums.TopicManage)
-
-const { hasPermission } = usePermission()
-</script>
-
-<template>
-  <div p="12px">
-    <Card title="基础信息">
-      <Description :data="state" :schema="schema" :column="2" />
-    </Card>
-
-    <Card mt="12px">
-      <Tabs v-model:active-key="activeTab">
-        <Tabs.TabPane v-if="hasPermission('product_topic_view')" :key="ProductTabEnums.TopicManage" tab="Topic 管理">
-          <TopicManage />
-        </Tabs.TabPane>
-        <Tabs.TabPane v-if="hasPermission('product_model_view')" :key="ProductTabEnums.Model" tab="物模型">
-          <Model :tsl="state?.tsl" />
-        </Tabs.TabPane>
-        <Tabs.TabPane v-if="hasPermission('product_subscription_view')" :key="ProductTabEnums.Subscription" tab="服务端订阅">
-          <Subscription :data="state?.subscribe" />
-        </Tabs.TabPane>
-      </Tabs>
-    </Card>
-  </div>
-</template>
diff --git a/src/views/product/index.vue b/src/views/product/index.vue
deleted file mode 100644
index 4cc56301..00000000
--- a/src/views/product/index.vue
+++ /dev/null
@@ -1,92 +0,0 @@
-<script setup lang="ts">
-import { PlusOutlined } from '@ant-design/icons-vue'
-import { columns, searchFormSchemas } from './data'
-import ProductFormModal from './ProductFormModal.vue'
-import { BasicTable, TableAction, useTable } from '@/components/Table'
-import { deleteProduct, getProductList } from '@/api/product'
-import { useModal } from '@/components/Modal'
-import type { Product } from '@/api/product/types'
-import { useMessage } from '@/hooks/web/useMessage'
-import { usePermission } from '@/hooks/web/usePermission'
-
-defineOptions({ name: 'Product' })
-
-const [registerModal, { openModal }] = useModal<Product>()
-
-const { hasPermission } = usePermission()
-
-const [register, { reload }] = useTable({
-  api: getProductList,
-  columns,
-  formConfig: {
-    schemas: searchFormSchemas,
-    labelWidth: 80,
-  },
-  bordered: true,
-  canResize: false,
-  useSearchForm: true,
-  actionColumn: {
-    width: 220,
-    title: '操作',
-    dataIndex: 'action',
-    fixed: 'right',
-    auth: ['view', 'edit', 'delete'].map(code => `product_${code}`),
-  },
-})
-
-async function handleDelete(id: string) {
-  try {
-    await deleteProduct(id)
-    useMessage().createMessage.success('删除成功')
-    reload()
-  }
-  catch {}
-}
-</script>
-
-<template>
-  <div>
-    <BasicTable :api="async () => ([] as Product[])" @register="register">
-      <template v-if="hasPermission('product_add')" #tableTitle>
-        <a-button type="primary" @click="openModal">
-          <PlusOutlined />
-          新建
-        </a-button>
-      </template>
-
-      <template #bodyCell="{ column, record }">
-        <template v-if="column.key === 'action'">
-          <TableAction
-            :actions="[
-              {
-                icon: 'i-ant-design:file-search-outlined',
-                label: '详情',
-                auth: 'product_view',
-                onClick: () => $router.push(`/product/detail/${record.id}`),
-              },
-              {
-                icon: 'i-ant-design:edit-outlined',
-                label: '编辑',
-                auth: 'product_edit',
-                onClick: () => openModal(true, record),
-              },
-              {
-                icon: 'i-ant-design:delete-outlined',
-                danger: true,
-                label: '删除',
-                auth: 'product_delete',
-                popConfirm: {
-                  title: '是否要删除数据?',
-                  placement: 'left',
-                  confirm: () => handleDelete(record.id),
-                },
-              },
-            ]"
-          />
-        </template>
-      </template>
-    </BasicTable>
-
-    <ProductFormModal @register="registerModal" @success="reload" />
-  </div>
-</template>
diff --git a/src/views/subscription/consumer/ConsumerFormModal.vue b/src/views/subscription/consumer/ConsumerFormModal.vue
deleted file mode 100644
index c7374e1b..00000000
--- a/src/views/subscription/consumer/ConsumerFormModal.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue'
-import { formSchema } from './data'
-import { useMessage } from '@/hooks/web/useMessage'
-import { BasicForm, useForm } from '@/components/Form'
-import { BasicModal, useModalInner } from '@/components/Modal'
-import { createConsumer, updateConsumer } from '@/api/subscription/consumer'
-import type { Consumer } from '@/api/subscription/consumer/types'
-import { getSubscriptionDetail } from '@/api/subscription/list'
-import { noop } from '@/utils'
-
-defineOptions({ name: 'ConsumerFormModal' })
-
-const emit = defineEmits(['success', 'register'])
-
-const isUpdate = ref(false)
-const [registerForm, { setFieldsValue, validate }] = useForm({
-  labelWidth: 100,
-  baseColProps: { span: 24 },
-  schemas: formSchema,
-  showActionButtonGroup: false,
-  actionColOptions: { span: 23 },
-})
-
-const [registerModal, { setModalProps, closeModal }] = useModalInner(async (id: string) => {
-  isUpdate.value = true
-  setModalProps({ confirmLoading: true, loading: true })
-  getSubscriptionDetail(id)
-    .then((data) => {
-      setFieldsValue({ ...data, subscribeIds: data.subscribes ? data.subscribes.split(',') : [] })
-    })
-    .catch(noop)
-    .finally(() => {
-      setModalProps({ confirmLoading: false, loading: false })
-    })
-})
-
-async function handleSubmit() {
-  try {
-    const values = await validate<Consumer & { subscribeIds: string[] | string }>()
-    setModalProps({ confirmLoading: true })
-    await (isUpdate.value ? updateConsumer(values) : createConsumer(values))
-    closeModal()
-    emit('success')
-    useMessage().createMessage.success('保存成功')
-  }
-  catch {}
-  finally {
-    setModalProps({ confirmLoading: false })
-  }
-}
-</script>
-
-<template>
-  <BasicModal
-    width="30%"
-    :min-height="100"
-    :title="isUpdate ? '编辑' : '新增'"
-    :after-close="() => isUpdate = false"
-    @register="registerModal"
-    @ok="handleSubmit"
-  >
-    <BasicForm @register="registerForm" />
-  </BasicModal>
-</template>
diff --git a/src/views/subscription/consumer/components/OnlineClient.vue b/src/views/subscription/consumer/components/OnlineClient.vue
deleted file mode 100644
index 4578b3c6..00000000
--- a/src/views/subscription/consumer/components/OnlineClient.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<script lang="ts" setup>
-import { getOnlineClientList } from '@/api/subscription/consumer'
-import { BasicTable, useTable } from '@/components/Table'
-
-const props = defineProps<{
-  consumerToken: string
-}>()
-
-const [register] = useTable({
-  api: () => getOnlineClientList(props.consumerToken),
-  columns: [
-    {
-      title: '客户端 ID',
-      dataIndex: 'uuid',
-    },
-    {
-      title: '客户端 IP',
-      dataIndex: 'address',
-    },
-    {
-      title: '最后上线时间',
-      dataIndex: 'onlineDate',
-    },
-  ],
-  bordered: true,
-  inset: true,
-  canResize: false,
-  pagination: false,
-})
-</script>
-
-<template>
-  <BasicTable @register="register" />
-</template>
diff --git a/src/views/subscription/consumer/components/Product.vue b/src/views/subscription/consumer/components/Product.vue
deleted file mode 100644
index f2c98696..00000000
--- a/src/views/subscription/consumer/components/Product.vue
+++ /dev/null
@@ -1,92 +0,0 @@
-<script lang="ts" setup>
-import { h, ref } from 'vue'
-import { Popconfirm, Space, Tag } from 'ant-design-vue'
-import { DisconnectOutlined } from '@ant-design/icons-vue'
-import { useAsyncState } from '@vueuse/core'
-import { disSubscription, getSubscribeList } from '@/api/subscription/consumer'
-import { BasicTable, useTable } from '@/components/Table'
-import { useSystemEnumStore } from '@/store/modules/systemEnum'
-import { getAllProducts } from '@/api/product'
-import type { Product } from '@/api/subscription/consumer/types'
-import { noop } from '@/utils'
-
-const props = defineProps<{ consumerId: string }>()
-
-const { getSystemEnums } = useSystemEnumStore()
-
-const { state: products } = useAsyncState(getAllProducts, [])
-
-const [register, { reload }] = useTable({
-  api: () => getSubscribeList(props.consumerId),
-  columns: [
-    {
-      title: '产品名称',
-      dataIndex: 'productId',
-      customRender({ value }) {
-        const product = products.value.find(item => item.id === value)
-        return product && product.productName
-      },
-    },
-    {
-      title: '推送消息类型',
-      dataIndex: 'messageType',
-      customRender({ value }) {
-        const values = value.split(',')
-        const types = getSystemEnums('eSubscribeMessageType')
-        return h(
-          Space,
-          () => types
-            .map(item => values.includes(item.value.toString()) ? item.label : null)
-            .filter(Boolean)
-            .map(name => h(Tag, () => name)),
-        )
-      },
-    },
-  ],
-  bordered: true,
-  inset: true,
-  canResize: false,
-  pagination: false,
-  actionColumn: {
-    width: 200,
-    title: '操作',
-    dataIndex: 'action',
-    fixed: 'right',
-    auth: ['consumer_disconnect'],
-  },
-})
-
-const loading = ref(false)
-function handleDisconnect(serverSubscribeId: string) {
-  loading.value = true
-  disSubscription(props.consumerId, serverSubscribeId)
-    .then(() => {
-      reload()
-    })
-    .catch(noop)
-    .finally(() => {
-      loading.value = false
-    })
-}
-</script>
-
-<template>
-  <BasicTable :api="async () => ([] as Product[])" @register="register">
-    <template #bodyCell="{ column, record }">
-      <template v-if="column.key === 'action'">
-        <Popconfirm
-          title="删除消费组后,该组内的消费者都将立即停止接收消息,并会清除所有相关资源,您确定要删除吗?"
-          ok-text="确定"
-          cancel-text="取消"
-          placement="left"
-          @confirm="() => handleDisconnect(record.id)"
-        >
-          <a-button type="link" size="small" danger :loading="loading">
-            <DisconnectOutlined />
-            解除订阅
-          </a-button>
-        </Popconfirm>
-      </template>
-    </template>
-  </BasicTable>
-</template>
diff --git a/src/views/subscription/consumer/components/index.ts b/src/views/subscription/consumer/components/index.ts
deleted file mode 100644
index eda7e295..00000000
--- a/src/views/subscription/consumer/components/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { default as OnlineClient } from './OnlineClient.vue'
-export { default as Product } from './Product.vue'
diff --git a/src/views/subscription/consumer/data.ts b/src/views/subscription/consumer/data.ts
deleted file mode 100644
index 0d9020c3..00000000
--- a/src/views/subscription/consumer/data.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import type { BasicColumn, FormSchema } from '@/components/Table'
-import { getAllSubscription } from '@/api/subscription/list'
-
-export const columns: BasicColumn[] = [
-  {
-    title: '消费组名称',
-    dataIndex: 'consumerName',
-  },
-  {
-    title: '消费组 Token',
-    dataIndex: 'consumerToken',
-  },
-  {
-    title: '创建时间',
-    dataIndex: 'createTime',
-  },
-]
-
-export const searchFormSchema: FormSchema[] = [
-  {
-    field: 'consumerName',
-    label: '消费组名称',
-    component: 'Input',
-    colProps: {
-      span: 6,
-    },
-  },
-]
-
-export const formSchema: FormSchema[] = [
-  {
-    field: 'consumerName',
-    fields: ['id'],
-    label: '消费组名称',
-    required: true,
-    component: 'Input',
-  },
-  {
-    field: 'subscribeIds',
-    label: '订阅',
-    required: true,
-    component: 'ApiSelect',
-    componentProps: {
-      api: getAllSubscription,
-      mode: 'multiple',
-      valueField: 'id',
-      labelField: 'productName',
-    },
-  },
-]
diff --git a/src/views/subscription/consumer/detail.vue b/src/views/subscription/consumer/detail.vue
deleted file mode 100644
index 17c6f469..00000000
--- a/src/views/subscription/consumer/detail.vue
+++ /dev/null
@@ -1,99 +0,0 @@
-<script lang="ts" setup>
-import { useAsyncState } from '@vueuse/core'
-import { Button, Card, Tabs } from 'ant-design-vue'
-import { CopyOutlined } from '@ant-design/icons-vue'
-import { useRoute } from 'vue-router'
-import { h } from 'vue'
-import { OnlineClient, Product } from './components'
-import { getConsumerDetail } from '@/api/subscription/consumer'
-import type { DescItem } from '@/components/Description'
-import { Description } from '@/components/Description'
-import { copyText } from '@/utils/copyTextToClipboard'
-import { usePermission } from '@/hooks/web/usePermission'
-
-const route = useRoute()
-const { state } = useAsyncState(() => getConsumerDetail(route.params.id as string), undefined)
-const schema: DescItem[] = [
-  {
-    field: 'consumerToken',
-    label: '消费组 Token',
-    render(value) {
-      return h('div', [
-        value,
-        h(Button, {
-          size: 'small',
-          type: 'link',
-          style: {
-            marginLeft: '5px',
-          },
-          onClick: () => copyText(value),
-        }, () => [h(CopyOutlined), '复制']),
-      ])
-    },
-  },
-  {
-    field: 'consumerName',
-    label: '消费组名称',
-  },
-  {
-    field: 'tenantId',
-    label: '企业编号',
-    render(value) {
-      return h('div', [
-        value,
-        h(Button, {
-          size: 'small',
-          type: 'link',
-          style: {
-            marginLeft: '5px',
-          },
-          onClick: () => copyText(value),
-        }, () => [h(CopyOutlined), '复制']),
-      ])
-    },
-  },
-  {
-    field: 'Topic',
-    label: 'Topic',
-    render(_, data) {
-      const value = `$SYS/${data.tenantId}/${data.consumerToken}/status `
-      return h('div', [
-        value,
-        h(Button, {
-          size: 'small',
-          type: 'link',
-          style: {
-            marginLeft: '5px',
-          },
-          onClick: () => copyText(value),
-        }, () => [h(CopyOutlined), '复制']),
-      ])
-    },
-  },
-  {
-    field: 'createTime',
-    label: '创建时间',
-  },
-]
-
-const { hasPermission } = usePermission()
-</script>
-
-<template>
-  <div p="12px">
-    <Card title="基础信息">
-      <Description :data="state" :schema="schema" :column="1" :label-style="{ width: '140px' }" />
-    </Card>
-
-    <Card v-if="hasPermission(['consumer_client_view', 'consumer_product_view'])" mt="12px">
-      <Tabs>
-        <Tabs.TabPane v-if="hasPermission('consumer_client_view')" key="1" tab="在线客户端">
-          <OnlineClient v-if="state" :consumer-token="state.consumerToken" />
-        </Tabs.TabPane>
-        <Tabs.TabPane v-if="hasPermission('consumer_product_view')" key="2" tab="订阅产品">
-          <Product v-if="state" :consumer-id="state.id" />
-        </Tabs.TabPane>
-      </Tabs>
-    </Card>
-  </div>
-</template>
diff --git a/src/views/subscription/consumer/index.vue b/src/views/subscription/consumer/index.vue
deleted file mode 100644
index 32112427..00000000
--- a/src/views/subscription/consumer/index.vue
+++ /dev/null
@@ -1,91 +0,0 @@
-<script lang="ts" setup>
-import { PlusOutlined } from '@ant-design/icons-vue'
-import { columns, searchFormSchema } from './data'
-import ConsumerFormModal from './ConsumerFormModal.vue'
-import { BasicTable, TableAction, useTable } from '@/components/Table'
-import { deleteConsumer, getConsumerList } from '@/api/subscription/consumer'
-import { useModal } from '@/components/Modal'
-import type { Consumer } from '@/api/subscription/consumer/types'
-import { useMessage } from '@/hooks/web/useMessage'
-import { usePermission } from '@/hooks/web/usePermission'
-
-defineOptions({ name: 'Consumer' })
-
-const [registerModal, { openModal }] = useModal<string>()
-
-const { hasPermission } = usePermission()
-
-const [register, { reload }] = useTable({
-  api: getConsumerList,
-  columns,
-  formConfig: {
-    labelWidth: 80,
-    schemas: searchFormSchema,
-  },
-  bordered: true,
-  canResize: false,
-  useSearchForm: true,
-  actionColumn: {
-    width: 220,
-    title: '操作',
-    dataIndex: 'action',
-    fixed: 'right',
-  },
-})
-
-async function handleDelete(id: string) {
-  try {
-    await deleteConsumer(id)
-    useMessage().createMessage.success('删除成功')
-    reload()
-  }
-  catch {}
-}
-</script>
-
-<template>
-  <div>
-    <BasicTable :api="async () => ([] as Consumer[])" @register="register">
-      <template v-if="hasPermission('consumer_add')" #tableTitle>
-        <a-button type="primary" @click="openModal()">
-          <PlusOutlined />
-          创建
-        </a-button>
-      </template>
-
-      <template #bodyCell="{ column, record }">
-        <template v-if="column.key === 'action'">
-          <TableAction
-            :actions="[
-              {
-                icon: 'i-ant-design:file-search-outlined',
-                label: '详情',
-                auth: 'consumer_view',
-                onClick: () => $router.push(`/subscription/consumer/detail/${record.id}`),
-              },
-              {
-                icon: 'i-ant-design:edit-outlined',
-                label: '修改',
-                auth: 'consumer_edit',
-                onClick: () => openModal(true, record.id),
-              },
-              {
-                icon: 'i-ant-design:delete-outlined',
-                danger: true,
-                label: '删除',
-                auth: 'consumer_delete',
-                popConfirm: {
-                  title: '确定要删除数据吗?',
-                  placement: 'left',
-                  confirm: () => handleDelete(record.id),
-                },
-              },
-            ]"
-          />
-        </template>
-      </template>
-    </BasicTable>
-
-    <ConsumerFormModal @register="registerModal" @success="reload" />
-  </div>
-</template>
diff --git a/src/views/subscription/list/SubscriptionFormModal.vue b/src/views/subscription/list/SubscriptionFormModal.vue
deleted file mode 100644
index abee291a..00000000
--- a/src/views/subscription/list/SubscriptionFormModal.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue'
-import { useFormSchema } from './data'
-import { useMessage } from '@/hooks/web/useMessage'
-import { BasicForm, useForm } from '@/components/Form'
-import { BasicModal, useModalInner } from '@/components/Modal'
-import { createSubscription, updateSubscription } from '@/api/subscription/list'
-import type { SubScription } from '@/api/subscription/list/types'
-
-defineOptions({ name: 'ProductFormModal' })
-
-const emit = defineEmits(['success', 'register'])
-
-const isUpdate = ref(false)
-const [registerForm, { setFieldsValue, validate }] = useForm({
-  labelWidth: 100,
-  baseColProps: { span: 24 },
-  schemas: useFormSchema(isUpdate),
-  showActionButtonGroup: false,
-  actionColOptions: { span: 23 },
-})
-
-const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: SubScription) => {
-  isUpdate.value = true
-  setFieldsValue({
-    ...data,
-    messageType: data.messageType.split(',').map(Number),
-  })
-})
-
-async function handleSubmit() {
-  try {
-    const values = await validate<Pick<SubScription, 'productId' | 'id' | 'messageType'>>()
-    setModalProps({ confirmLoading: true })
-    values.messageType = (values.messageType as unknown as string[]).join(',')
-    await (isUpdate.value ? updateSubscription(values) : createSubscription(values))
-    closeModal()
-    emit('success')
-    useMessage().createMessage.success('保存成功')
-  }
-  catch {}
-  finally {
-    setModalProps({ confirmLoading: false })
-  }
-}
-</script>
-
-<template>
-  <BasicModal
-    width="30%"
-    :min-height="100"
-    :title="isUpdate ? '编辑' : '新增'"
-    :after-close="() => isUpdate = false"
-    @register="registerModal"
-    @ok="handleSubmit"
-  >
-    <BasicForm @register="registerForm" />
-  </BasicModal>
-</template>
diff --git a/src/views/subscription/list/data.ts b/src/views/subscription/list/data.ts
deleted file mode 100644
index 21b14d14..00000000
--- a/src/views/subscription/list/data.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-import { h } from 'vue'
-import type { Ref } from 'vue'
-import { Space, Tag } from 'ant-design-vue'
-import { createSharedComposable, useAsyncState } from '@vueuse/core'
-import type { BasicColumn, FormSchema } from '@/components/Table'
-import { useSystemEnumStore } from '@/store/modules/systemEnum'
-import { getAllProducts } from '@/api/product'
-
-export const useSharedProducts = createSharedComposable(() => {
-  const { state } = useAsyncState(getAllProducts, [])
-  return { products: state }
-})
-
-export function useColumns(): BasicColumn[] {
-  const { getSystemEnums } = useSystemEnumStore()
-  const { products } = useSharedProducts()
-
-  return [
-    {
-      title: '产品名称',
-      dataIndex: 'productId',
-      customRender({ value }) {
-        return products.value.find(item => item.id === value)?.productName
-      },
-    },
-    {
-      title: '推送消息类型',
-      dataIndex: 'messageType',
-      customRender({ value }) {
-        const values = value.split(',')
-        const types = getSystemEnums('eSubscribeMessageType')
-        return h(
-          Space,
-          () => types
-            .map(item => values.includes(item.value.toString()) ? item.label : null)
-            .filter(Boolean)
-            .map(name => h(Tag, () => name)),
-        )
-      },
-    },
-    {
-      title: '创建时间',
-      dataIndex: 'createTime',
-    },
-  ]
-}
-
-export function useSearchFormSchema(productId?: string): FormSchema[] {
-  const { products } = useSharedProducts()
-
-  return [
-    {
-      field: 'productId',
-      label: '产品名称',
-      component: 'Select',
-      componentProps: {
-        options: products as any,
-        showSearch: true,
-        fieldNames: {
-          label: 'productName',
-          value: 'id',
-        },
-      },
-      defaultValue: productId || undefined,
-      colProps: {
-        span: 6,
-      },
-    },
-  ]
-}
-
-export function useFormSchema(isUpload: Ref<boolean>): FormSchema[] {
-  const { products } = useSharedProducts()
-  const { getSystemEnums } = useSystemEnumStore()
-
-  return [
-    {
-      field: 'productId',
-      fields: ['id'],
-      label: '产品名称',
-      required: true,
-      component: 'ApiSelect',
-      componentProps: {
-        options: products as any,
-        showSearch: true,
-        fieldNames: {
-          label: 'productName',
-          value: 'id',
-        },
-      },
-      dynamicDisabled: () => isUpload.value,
-    },
-    {
-      field: 'messageType',
-      label: '推送消息类型',
-      required: true,
-      component: 'Select',
-      componentProps: {
-        mode: 'multiple',
-        options: getSystemEnums('eSubscribeMessageType'),
-      },
-    },
-  ]
-}
diff --git a/src/views/subscription/list/index.vue b/src/views/subscription/list/index.vue
deleted file mode 100644
index 38f7c23f..00000000
--- a/src/views/subscription/list/index.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-<script lang="ts" setup>
-import { PlusOutlined } from '@ant-design/icons-vue'
-import { useColumns, useSearchFormSchema } from './data'
-import SubscriptionFormModal from './SubscriptionFormModal.vue'
-import { BasicTable, TableAction, useTable } from '@/components/Table'
-import { deleteSubscription, getSubscriptionList } from '@/api/subscription/list'
-import { useModal } from '@/components/Modal'
-import type { SubScription } from '@/api/subscription/list/types'
-import { useMessage } from '@/hooks/web/useMessage'
-import { usePermission } from '@/hooks/web/usePermission'
-
-defineOptions({ name: 'Subscription' })
-
-const [registerModal, { openModal }] = useModal<SubScription>()
-
-const { hasPermission } = usePermission()
-
-const [register, { reload }] = useTable({
-  api: getSubscriptionList,
-  columns: useColumns(),
-  formConfig: {
-    labelWidth: 80,
-    schemas: useSearchFormSchema(history.state.productId),
-  },
-  bordered: true,
-  canResize: false,
-  useSearchForm: true,
-  actionColumn: {
-    width: 160,
-    title: '操作',
-    dataIndex: 'action',
-    fixed: 'right',
-    auth: ['subscribe_edit', 'subscribe_delete'],
-  },
-})
-
-async function handleDelete(id: string) {
-  try {
-    await deleteSubscription(id)
-    useMessage().createMessage.success('删除成功')
-    reload()
-  }
-  catch {}
-}
-</script>
-
-<template>
-  <div>
-    <BasicTable :api="async () => ([] as SubScription[])" @register="register">
-      <template v-if="hasPermission('subscribe_add')" #tableTitle>
-        <a-button type="primary" @click="openModal()">
-          <PlusOutlined />
-          创建
-        </a-button>
-      </template>
-
-      <template #bodyCell="{ column, record }">
-        <template v-if="column.key === 'action'">
-          <TableAction
-            :actions="[
-              {
-                icon: 'i-ant-design:edit-outlined',
-                label: '修改',
-                auth: 'subscribe_edit',
-                onClick: () => openModal(true, record),
-              },
-              {
-                icon: 'i-ant-design:delete-outlined',
-                danger: true,
-                label: '删除',
-                auth: 'subscribe_delete',
-                popConfirm: {
-                  title: '确定要删除数据吗?',
-                  placement: 'left',
-                  confirm: () => handleDelete(record.id),
-                },
-              },
-            ]"
-          />
-        </template>
-      </template>
-    </BasicTable>
-
-    <SubscriptionFormModal @register="registerModal" @success="reload" />
-  </div>
-</template>