Procházet zdrojové kódy

feat(gateway): 重构网关 IP 管理功能

- 新增 IP 列表模态框组件,用于查看网关 IP 详情
- 优化网关列表页面,增加批量删除功能- 调整路由配置,移除冗余的 IP 列表页面
- 更新 API 接口,统一网关和 IP相关请求
- 重构数据库操作,提高数据处理效率
fusu před 3 týdny
rodič
revize
69ef0daade

+ 1 - 1
internal/repository/admin/gatewayipadmin.go

@@ -132,5 +132,5 @@ func (r *gatewayIpAdminRepository) DeleteGatewayIp(ctx context.Context,id int64)
 }
 
 func (r *gatewayIpAdminRepository) DeleteGatewayIps(ctx context.Context, ids []int64) error {
-	return r.DB(ctx).Model(&model.Gatewayip{}).Where("id in ?", ids).Delete(ids).Error
+	return r.DB(ctx).Model(&model.Gatewayip{}).Where("id in ?", ids).Delete(&model.Gatewayip{}).Error
 }

+ 13 - 4
web/src/api/list/gateway-list.js

@@ -1,17 +1,26 @@
 import { useDelete, useGet, usePost, usePut } from '~@/utils/request'
 
 export async function getListApi(params) {
-  return useGet('/v1/gateway/list', params)
+  return useGet('/v1/gatewayIp/getList', params)
 }
 
+export async function getList(params) {
+  return useGet('/v1/gatewayIp/get', params)
+}
+
+
 export async function addApi(data) {
-  return usePost('/v1/gateway/add', data)
+  return usePost('/v1/gatewayIp/add', data)
 }
 
 export async function updateApi(data) {
-  return usePut('/v1/gateway/edit', data)
+  return usePut('/v1/gatewayIp/edit', data)
 }
 
 export async function deleteApi(id) {
-  return useDelete(`/v1/gateway/del`, { id })
+  return useDelete(`/v1/gatewayIp/del`, { id })
+}
+
+export async function deleteApiList(data) {
+  return useDelete(`/v1/gatewayIp/delList`, data)
 }

+ 6 - 5
web/src/locales/lang/pages/zh-CN.js

@@ -192,13 +192,14 @@ export default {
   'account.settings.message.desc3': '待办任务将以站内信的形式通知',
 
   //网关组
-  'gateway.group.title': '网关',
-  'gateway.group.name': '网关组名称',
+  'gateway.group.title': '网关IP',
+  'gateway.group.name': '名称',
   'gateway.group.ruleId': '规则ID',
   'gateway.group.hostId': '订单ID',
-  'gateway.group.id': '网关组ID',
-  'gateway.group.comment': '网关组备注',
-  'gateway.group.operator': '网关组运营商',
+  'gateway.group.id': 'ID',
+  'gateway.group.comment': '备注',
+  'gateway.group.nodeArea' : '区域',
+  'gateway.group.operator': '网关运营商',
   'gateway.group.banUdp': '是否封UDP',
   'gateway.group.telecommunications': '电信',
   'gateway.group.BGP': 'BGP',

+ 97 - 0
web/src/pages/menu/components/IpListModal.vue

@@ -0,0 +1,97 @@
+<script setup>
+import { ref, shallowRef } from 'vue'
+import { Drawer, Descriptions, DescriptionsItem, Spin, Empty } from 'ant-design-vue'
+import { getListApi } from '~@/api/list/gateway-group-ip-list.js'
+
+const visible = ref(false)
+const title = ref('IP详情')
+const loading = ref(false)
+const dataSource = ref([])
+
+const columns = shallowRef([
+  { title: 'ID', dataIndex: 'id', key: 'id' },
+  { title: '实例ID', dataIndex: 'hostId', key: 'hostId' },
+  { title: '名称', dataIndex: 'name', key: 'name' },
+  { title: 'IP', dataIndex: 'ip', key: 'ip' },
+  { title: '区域', dataIndex: 'nodeArea', key: 'nodeArea' },
+  { title: '运营商', dataIndex: 'operator', key: 'operator' },
+  { title: '是否封UDP', dataIndex: 'banUdp', key: 'banUdp' },
+  { title: '是否封海外', dataIndex: 'banOverseas', key: 'banOverseas' },
+  { title: '备注', dataIndex: 'comment', key: 'comment' },
+  { title: '创建时间', dataIndex: 'createdAt', key: 'createdAt' },
+  { title: '更新时间', dataIndex: 'updatedAt', key: 'updatedAt' },
+])
+
+async function open(record) {
+  visible.value = true
+  dataSource.value = []
+  if (record && record.id) {
+    try {
+      loading.value = true
+      const res = await getListApi({ id: record.id })
+      if (res.code === 0 && res.data) {
+        // The API returns a single object, wrap it in an array for the table
+        dataSource.value = Array.isArray(res.data) ? res.data : [res.data]
+      } else {
+        dataSource.value = []
+      }
+    } catch (e) {
+      console.error(e)
+      dataSource.value = []
+    } finally {
+      loading.value = false
+    }
+  }
+}
+
+function close() {
+  visible.value = false
+}
+
+defineExpose({ open })
+</script>
+
+<template>
+  <a-drawer
+    :visible="visible"
+    :title="title"
+    placement="right"
+    :width="'60%'"
+    @close="close"
+  >
+    <a-spin :spinning="loading">
+      <a-descriptions v-if="dataSource.length > 0" bordered :column="{ xxl: 2, xl: 2, lg: 2, md: 2, sm: 1, xs: 1 }">
+        <template v-for="col in columns" :key="col.key">
+          <a-descriptions-item :label="col.title">
+            <template v-if="col.dataIndex === 'operator'">
+              <span v-if="dataSource[0][col.dataIndex] === 1">电信</span>
+              <span v-else-if="dataSource[0][col.dataIndex] === 2">BGP</span>
+              <span v-else>未知</span>
+            </template>
+            <template v-else-if="col.dataIndex === 'banUdp'">
+              <span v-if="dataSource[0][col.dataIndex] === 0">否</span>
+              <span v-else-if="dataSource[0][col.dataIndex] === 1">是</span>
+              <span v-else>未知</span>
+            </template>
+            <template v-else-if="col.dataIndex === 'banOverseas'">
+              <span v-if="dataSource[0][col.dataIndex] === 0">否</span>
+              <span v-else-if="dataSource[0][col.dataIndex] === 1">是</span>
+              <span v-else>未知</span>
+            </template>
+            <template v-else>
+              {{ dataSource[0][col.dataIndex] }}
+            </template>
+          </a-descriptions-item>
+        </template>
+      </a-descriptions>
+      <a-empty v-else />
+    </a-spin>
+  </a-drawer>
+</template>
+
+<style scoped>
+:deep(.ant-descriptions-item-label),
+:deep(.ant-descriptions-item-content) {
+  font-size: 16px;
+}
+</style>

+ 25 - 14
web/src/pages/menu/components/crud-table-modal.vue

@@ -20,9 +20,10 @@ const defaultFormData = {
   id: 0,
   name: '',
   hostId: 0,
-  ruleId: 0,
+  ip: '',
   comment: '',
   operator: 1,
+  nodeArea: '宁波',
   banUdp: 0,
   banOverseas: 0,
 }
@@ -91,28 +92,28 @@ defineExpose({
 <template>
   <a-modal v-model:open="visible" :title="title" :confirm-loading="loading" @cancel="handleCancel">
     <a-form ref="formRef" :model="formData" class="w-full" :label-col="labelCol" :wrapper-col="wrapperCol">
-      <a-form-item :label="t('gateway.group.name')" name="name" :rules="[{ required: true, message: '请输入网关组名称' }]">
+      <a-form-item :label="t('gateway.group.name')" name="name">
         <a-input v-model:value="formData.name" :maxlength="50" placeholder="请输入网关组名称" />
       </a-form-item>
       <a-form-item :label="t('gateway.group.hostId')" name="hostId" :rules="[{ required: true, message: '请输入订单ID' }]">
         <a-input v-model:value="formData.hostId" :maxlength="50" placeholder="请输入订单ID" />
       </a-form-item>
-      <a-form-item :label="t('gateway.group.ruleId')" name="ruleId" :rules="[{ required: true, message: '请输入规则ID' }]">
-        <a-input v-model:value="formData.ruleId" :maxlength="50" placeholder="请输入规则ID" />
+      <a-form-item label="IP" name="ip" :rules="[{ required: true, message: '请输入IP' }]">
+        <a-input v-model:value="formData.ip" :maxlength="50" placeholder="请输入IP" />
+      </a-form-item>
+      <a-form-item :label="t('gateway.group.nodeArea')" name="nodeArea">
+        <a-select v-model:value="formData.nodeArea" placeholder="请选择区域">
+          <a-select-option value="宁波">
+            宁波
+          </a-select-option>
+          <a-select-option value="香港">
+            香港
+          </a-select-option>
+        </a-select>
       </a-form-item>
       <a-form-item :label="t('gateway.group.comment')" name="comment">
         <a-textarea v-model:value="formData.comment" show-count :maxlength="200" placeholder="请输入备注" />
       </a-form-item>
-      <a-form-item :label="t('gateway.group.banUdp')" name="banUdp">
-        <a-radio-group v-model:value="formData.banUdp">
-          <a-radio :value="1">
-            是
-          </a-radio>
-          <a-radio :value="0">
-            否
-          </a-radio>
-        </a-radio-group>
-      </a-form-item>
       <a-form-item :label="t('gateway.group.operator')" name="operator">
         <a-radio-group v-model:value="formData.operator">
           <a-radio :value="1">
@@ -123,6 +124,16 @@ defineExpose({
           </a-radio>
         </a-radio-group>
       </a-form-item>
+      <a-form-item :label="t('gateway.group.banUdp')" name="banUdp">
+        <a-radio-group v-model:value="formData.banUdp">
+          <a-radio :value="1">
+            是
+          </a-radio>
+          <a-radio :value="0">
+            否
+          </a-radio>
+        </a-radio-group>
+      </a-form-item>
       <a-form-item :label="t('gateway.group.banOverseas')" name="banOverseas">
         <a-radio-group v-model:value="formData.banOverseas">
           <a-radio :value="1">

+ 1 - 131
web/src/pages/menu/ip-list.vue

@@ -1,131 +1 @@
-<script setup>
-import { ref, shallowRef, computed } from 'vue'
-import { useRoute } from 'vue-router'
-import { PlusOutlined } from '@ant-design/icons-vue'
-import CrudTableModal from './components/ip-list-modal.vue'
-import { getListApi, deleteApi } from '~@/api/list/gateway-group-ip-list.js'
-import { useTableQuery } from '~@/composables/table-query'
-
-const route = useRoute()
-const message = useMessage()
-const gatewayGroupId = computed(() => Number(route.query.id || 0))
-
-const columns = shallowRef([
-  { title: 'ID', dataIndex: 'id', key: 'id' },
-  { title: '网关组ID', dataIndex: 'gatewayGroupId', key: 'gatewayGroupId' },
-  { title: 'IP地址', dataIndex: 'ip', key: 'ip' },
-  { title: '标签', dataIndex: 'tag', key: 'tag' },
-  { title: '归属地', dataIndex: 'originPlace', key: 'originPlace' },
-  { title: '备注', dataIndex: 'comment', key: 'comment' },
-  { title: '创建时间', dataIndex: 'createdAt', key: 'createdAt' },
-  { title: '更新时间', dataIndex: 'updatedAt', key: 'updatedAt' },
-  { title: '操作', dataIndex: 'action', key: 'action' },
-])
-
-const { state, initQuery, resetQuery, query } = useTableQuery({
-  queryApi: getListApi,
-  queryParams: {
-    gatewayGroupId: gatewayGroupId.value,
-    ip: undefined,
-  },
-  afterQuery: (res) => {
-    console.log('afterQuery', res)
-    if (!res || !Array.isArray(res.records)) {
-      // 如果数据格式不正确,返回一个空状态,防止程序崩溃
-      return { list: [], total: 0 }
-    }
-    return res
-  },
-})
-
-const crudTableModal = ref()
-
-async function handleDelete(record) {
-  if (!record.id) return message.error('id 不能为空')
-  try {
-    const res = await deleteApi(record.id)
-    if (res.code === 0) {
-      await query()
-      message.success('删除成功')
-    }
-  } catch (e) {
-    console.log(e)
-  }
-}
-
-function handleAdd() {
-  crudTableModal.value?.open({ gatewayGroupId: gatewayGroupId.value })
-}
-
-function handleEdit(record) {
-  crudTableModal.value?.open(record)
-}
-</script>
-
-<template>
-  <page-container>
-    <a-card mb-4>
-      <a-form class="system-crud-wrapper" :label-col="{ span: 7 }" :model="state.queryParams">
-        <a-row :gutter="[15, 0]">
-          <a-col :span="6">
-            <a-form-item name="ip" label="IP地址">
-              <a-input v-model:value="state.queryParams.ip" placeholder="请输入IP地址" />
-            </a-form-item>
-          </a-col>
-          <a-col :span="6">
-            <a-space flex justify-end w-full>
-              <a-button :loading="state.loading" type="primary" @click="initQuery">
-                查询
-              </a-button>
-              <a-button :loading="state.loading" @click="resetQuery">
-                重置
-              </a-button>
-            </a-space>
-          </a-col>
-        </a-row>
-      </a-form>
-    </a-card>
-
-    <a-card title="IP列表">
-      <template #extra>
-        <a-space size="middle">
-          <a-button type="primary" @click="handleAdd">
-            <template #icon>
-              <PlusOutlined />
-            </template>
-            新增
-          </a-button>
-        </a-space>
-      </template>
-      <a-table
-        row-key="id"
-        :loading="state.loading"
-        :columns="columns"
-        :data-source="state.dataSource"
-        :pagination="state.pagination"
-      >
-        <template #bodyCell="scope">
-          <template v-if="scope?.column?.dataIndex === 'action'">
-            <div flex gap-2>
-              <a-button type="link" @click="handleEdit(scope?.record)">
-                编辑
-              </a-button>
-              <a-popconfirm
-                title="确定删除该条数据?"
-                ok-text="确定"
-                cancel-text="取消"
-                @confirm="handleDelete(scope?.record)"
-              >
-                <a-button type="link">
-                  删除
-                </a-button>
-              </a-popconfirm>
-            </div>
-          </template>
-        </template>
-      </a-table>
-    </a-card>
-
-    <CrudTableModal ref="crudTableModal" @ok="query" />
-  </page-container>
-</template>
+ 

+ 139 - 63
web/src/pages/menu/menu1.vue

@@ -1,98 +1,150 @@
 <script setup>
+import { computed, ref, shallowRef } from 'vue'
 import { PlusOutlined } from '@ant-design/icons-vue'
+import { Modal } from 'ant-design-vue'
+import dayjs from 'dayjs'
 import CrudTableModal from './components/crud-table-modal.vue'
-import { deleteApi, getListApi } from '~@/api/list/gateway-list.js'
+import IpListModal from './components/IpListModal.vue'
+import { deleteApi, deleteApiList, getListApi } from '~@/api/list/gateway-list.js'
 import { useTableQuery } from '~@/composables/table-query'
-import { useRouter } from 'vue-router'
 
 const message = useMessage()
 const columns = shallowRef([
   {
     title: 'ID',
-      dataIndex: 'id', // JSON 属性名是 'id'
-    key: 'id',       // 加上 key 是好习惯
+    dataIndex: 'id',
+    key: 'id',
+    width: 80,
   },
   {
     title: '订单ID',
-    dataIndex: 'hostId', // Go 的 host_id -> JSON 的 hostId
+    dataIndex: 'hostId',
     key: 'hostId',
-  },
-  {
-    title: '规则ID',
-    dataIndex: 'ruleId',
-    key: 'ruleId',
+    width: 100,
   },
   {
     title: '名称',
     dataIndex: 'name',
     key: 'name',
+    width: 150,
+    ellipsis: true,
+  },
+  {
+    title: 'IP',
+    dataIndex: 'ip',
+    key: 'ip',
+    width: 150,
   },
   {
     title: '备注',
-    dataIndex: 'comment', // Go 的 comment -> JSON 的 comment
+    dataIndex: 'comment',
     key: 'comment',
+    width: 200,
+    ellipsis: true,
   },
   {
     title: '运营商',
     dataIndex: 'operator',
     key: 'operator',
+    width: 100,
+  },
+  {
+    title: '区域',
+    dataIndex: 'nodeArea',
+    key: 'nodeArea',
+    width: 100,
   },
   {
     title: '是否封禁UDP',
-    dataIndex: 'banUdp', // Go 的 ban_udp -> JSON 的 banUdp
+    dataIndex: 'banUdp',
     key: 'banUdp',
+    width: 120,
   },
   {
     title: '是否封海外',
-    dataIndex: 'banOverseas', // Go 的 ban_udp -> JSON 的 banUdp
+    dataIndex: 'banOverseas',
     key: 'banOverseas',
+    width: 120,
   },
-
   {
     title: '创建时间',
-    dataIndex: 'createdAt', // Go 的 created_at -> JSON 的 createdAt
+    dataIndex: 'createdAt',
     key: 'createdAt',
+    width: 180,
+    customRender: ({ text }) => text ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '',
   },
   {
     title: '更新时间',
     dataIndex: 'updatedAt',
     key: 'updatedAt',
+    width: 180,
+    customRender: ({ text }) => text ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '',
   },
   {
     title: '操作',
     dataIndex: 'action',
     key: 'action',
+    width: 240,
+    fixed: 'right',
   },
 ])
 const { state, initQuery, resetQuery, query } = useTableQuery({
   queryApi: getListApi,
   queryParams: {
-    id:undefined,
     name: undefined,
+    comment: undefined,
   },
   afterQuery: (res) => {
     if (!res || !Array.isArray(res.records)) {
-      // 如果数据格式不正确,返回一个空状态,防止程序崩溃
       return { list: [], total: 0 }
     }
     return res
   },
 })
 const crudTableModal = ref()
+const selectedRowKeys = ref([])
+
+const rowSelection = computed(() => {
+  return {
+    selectedRowKeys: selectedRowKeys.value,
+    onChange: (keys) => {
+      selectedRowKeys.value = keys
+    },
+  }
+})
+
+async function handleBatchDelete() {
+  Modal.confirm({
+    title: '确定删除选中的数据吗?',
+    content: `即将删除 ${selectedRowKeys.value.length} 条数据。`,
+    okText: '确定',
+    cancelText: '取消',
+    onOk: async () => {
+      try {
+        const res = await deleteApiList({ ids: selectedRowKeys.value })
+        if (res.code === 0) {
+          await query()
+          selectedRowKeys.value = []
+          message.success('删除成功')
+        }
+      } catch (e) {
+        console.log(e)
+      }
+    },
+  })
+}
+
 async function handleDelete(record) {
-  if (!record.id)
-    return message.error('id 不能为空')
+  if (!record.id) return message.error('id 不能为空')
   try {
     const res = await deleteApi(record.id)
-    if (res.code === 0)
+    if (res.code === 0) {
       await query()
       message.success('删除成功')
-
-  }
-  catch (e) {
+    }
+  } catch (e) {
     console.log(e)
   }
-
 }
 function handleAdd() {
   crudTableModal.value?.open()
@@ -101,34 +153,28 @@ function handleEdit(record) {
   crudTableModal.value?.open(record)
 }
 
-const router = useRouter()
+const ipListModal = ref()
 function handleViewIpList(record) {
-  router.push(`/menu/ip-list?id=${record.id}`)
+  ipListModal.value?.open(record)
 }
-
 </script>
 
 <template>
   <page-container>
-    <a-card mb-4>
+    <a-card class="mb-4">
       <a-form class="system-crud-wrapper" :label-col="{ span: 7 }" :model="state.queryParams">
         <a-row :gutter="[15, 0]">
-          <a-col :span="6">
-            <a-form-item name="name" label="名">
-              <a-input v-model:value="state.queryParams.name" placeholder="请输入名" />
+          <a-col :xs="24" :sm="12" :md="8" :lg="6">
+            <a-form-item name="name" label="名">
+              <a-input v-model:value="state.queryParams.name" placeholder="请输入名" />
             </a-form-item>
           </a-col>
-          <a-col :span="6">
-            <a-form-item name="value" label="值">
-              <a-input v-model:value="state.queryParams.value" placeholder="请输入值" />
+          <a-col :xs="24" :sm="12" :md="8" :lg="6">
+            <a-form-item name="comment" label="备注">
+              <a-input v-model:value="state.queryParams.comment" placeholder="请输入备注" />
             </a-form-item>
           </a-col>
-          <a-col :span="6">
-            <a-form-item name="remark" label="备注">
-              <a-input v-model:value="state.queryParams.remark" placeholder="请输入备注" />
-            </a-form-item>
-          </a-col>
-          <a-col :span="6">
+          <a-col :xs="24" :sm="12" :md="8" :lg="6">
             <a-space flex justify-end w-full>
               <a-button :loading="state.loading" type="primary" @click="initQuery">
                 查询
@@ -151,25 +197,35 @@ function handleViewIpList(record) {
             </template>
             新增
           </a-button>
+          <a-button type="primary" danger :disabled="!selectedRowKeys || selectedRowKeys.length === 0" @click="handleBatchDelete">
+            批量删除
+          </a-button>
         </a-space>
       </template>
       <a-table
-          row-key="id" :row-selection="state.rowSelections" :loading="state.loading" :columns="columns"
-          :data-source="state.dataSource" :pagination="state.pagination"
+        row-key="id"
+        :row-selection="rowSelection"
+        :loading="state.loading"
+        :columns="columns"
+        :data-source="state.dataSource"
+        :pagination="state.pagination"
+        bordered
+        striped
+        :scroll="{ x: 1820 }"
       >
         <template #bodyCell="scope">
           <template v-if="scope?.column?.dataIndex === 'action'">
-            <div flex gap-2>
-              <a-button type="link" @click="handleEdit(scope?.record)">
+            <div class="flex items-center justify-start gap-x-1">
+              <a-button type="link" class="!pl-0" @click="handleEdit(scope?.record)">
                 编辑
               </a-button>
               <a-popconfirm
-                  title="确定删除该条数据?" ok-text="确定" cancel-text="取消"
-                  @confirm="handleDelete(scope?.record)"
+                title="确定删除该条数据吗?"
+                ok-text="确定"
+                cancel-text="取消"
+                @confirm="handleDelete(scope?.record)"
               >
-                <a-button type="link">
-                  删除
-                </a-button>
+                <a-button type="link"> 删除 </a-button>
               </a-popconfirm>
               <a-button type="link" @click="handleViewIpList(scope?.record)">
                 查看IP列表
@@ -178,37 +234,57 @@ function handleViewIpList(record) {
           </template>
 
           <template v-else-if="scope?.column?.dataIndex === 'operator'">
-            <!-- 直接在这里用 v-if 判断 -->
-            <span v-if="scope?.text === 1">电信</span>
-            <span v-else-if="scope?.text === 2">BGP</span>
-            <span v-else>未知</span>
+            <a-tag v-if="scope?.text === 1" color="blue">
+              电信
+            </a-tag>
+            <a-tag v-else-if="scope?.text === 2" color="purple">
+              BGP
+            </a-tag>
+            <a-tag v-else>
+              未知
+            </a-tag>
           </template>
 
           <template v-else-if="scope?.column?.dataIndex === 'banUdp'">
-            <!-- 直接在这里用 v-if 判断 -->
-            <span v-if="scope?.text === 0">否</span>
-            <span v-else-if="scope?.text === 1">是</span>
-            <span v-else>未知</span>
+            <a-tag v-if="scope?.text === 1" color="red">
+              是
+            </a-tag>
+            <a-tag v-else-if="scope?.text === 0" color="green">
+              否
+            </a-tag>
+            <a-tag v-else>
+              未知
+            </a-tag>
           </template>
 
           <template v-else-if="scope?.column?.dataIndex === 'banOverseas'">
-            <!-- 直接在这里用 v-if 判断 -->
-            <span v-if="scope?.text === 0">否</span>
-            <span v-else-if="scope?.text === 1">是</span>
-            <span v-else>未知</span>
+            <a-tag v-if="scope?.text === 1" color="red">
+              是
+            </a-tag>
+            <a-tag v-else-if="scope?.text === 0" color="green">
+              否
+            </a-tag>
+            <a-tag v-else>
+              未知
+            </a-tag>
           </template>
         </template>
       </a-table>
     </a-card>
 
     <CrudTableModal ref="crudTableModal" @ok="query" />
+    <IpListModal ref="ipListModal" />
   </page-container>
 </template>
 
 <style lang="less" scoped>
-.system-crud-wrapper{
-  .ant-form-item{
+.system-crud-wrapper {
+  .ant-form-item {
     margin: 0;
   }
 }
+
+:deep(.ant-table-tbody > tr > td) {
+  font-size: 16px;
+}
 </style>

+ 1 - 10
web/src/router/dynamic-routes.js

@@ -130,16 +130,7 @@ export default [
         name: 'MenuMenu11',
         component: () => import('~/pages/menu/menu1.vue'),
         meta: {
-          title: '网关组管理',
-        },
-      },
-      {
-        path: '/menu/ip-list',
-        name: 'IpList',
-        component: () => import('~/pages/menu/ip-list.vue'),
-        meta: {
-          title: 'IP列表',
-          hidden: true,
+          title: '网关IP管理',
         },
       },
       {

+ 1 - 0
web/types/components.d.ts

@@ -28,6 +28,7 @@ declare module 'vue' {
     ADivider: typeof import('ant-design-vue/es')['Divider']
     ADrawer: typeof import('ant-design-vue/es')['Drawer']
     ADropdown: typeof import('ant-design-vue/es')['Dropdown']
+    AEmpty: typeof import('ant-design-vue/es')['Empty']
     AForm: typeof import('ant-design-vue/es')['Form']
     AFormItem: typeof import('ant-design-vue/es')['FormItem']
     AFormItemRest: typeof import('ant-design-vue/es')['FormItemRest']