瀏覽代碼

fix:前端项目,接口信息管理的页面没有添加到git中,将没有添加到git的文件添加到git

yang yi 11 月之前
父節點
當前提交
f43a922259

+ 4 - 0
.gitignore

@@ -21,6 +21,10 @@
 /*
 
 !/**/**.java
+!/**/**.vue
+!/**/**.yaml
+!/**/**.yml
+!/**/**.properties
 
 !/src
 !/sql

+ 730 - 0
ui/yangyi-open-api-platform-ui/src/views/interfaceInfo/main.vue

@@ -0,0 +1,730 @@
+<template>
+    <div class="interface-info-container">
+        <!--<div class="page-header">-->
+        <!--    <h1 class="page-title">接口信息管理</h1>-->
+        <!--    <p class="page-description">管理系统所有接口的基本信息和配置</p>-->
+        <!--</div>-->
+
+        <!-- 搜索和操作区域 -->
+        <el-card class="search-card">
+            <el-form :model="searchForm" :inline="true" class="search-form">
+            <el-form-item label="接口名称">
+                <el-input
+                    v-model="searchForm.name"
+                    placeholder="请输入接口名称"
+                    clearable
+                    style="width: 200px"
+                />
+            </el-form-item>
+
+            <el-form-item label="接口状态">
+                <el-select
+                    v-model="searchForm.status"
+                    placeholder="请选择状态"
+                    clearable
+                    style="width: 120px"
+                >
+                    <el-option label="开启" value="1" />
+                    <el-option label="关闭" value="0" />
+                </el-select>
+            </el-form-item>
+
+            <el-form-item label="请求类型">
+                <el-select
+                    v-model="searchForm.method"
+                    placeholder="请选择请求类型"
+                    clearable
+                    style="width: 120px"
+                >
+                    <el-option label="GET" value="GET" />
+                    <el-option label="POST" value="POST" />
+                    <el-option label="PUT" value="PUT" />
+                    <el-option label="DELETE" value="DELETE" />
+                </el-select>
+            </el-form-item>
+
+            <el-form-item>
+                <el-button type="primary" @click="handleSearch" :icon="Search">
+                搜索
+            </el-button>
+            <el-button @click="handleReset" :icon="Refresh">
+            重置
+        </el-button>
+    </el-form-item>
+</el-form>
+</el-card>
+
+<!-- 操作按钮区域 -->
+<div class="operation-area">
+    <el-button type="primary" @click="handleAdd" :icon="Plus">
+    新增接口
+</el-button>
+<el-button
+    type="danger"
+        :disabled="multipleSelection.length === 0"
+@click="handleBatchDelete"
+:icon="Delete"
+    >
+    批量删除
+    </el-button>
+</div>
+
+<!-- 数据表格 -->
+<el-card class="table-card">
+    <el-table
+        v-loading="loading"
+    :data="tableData"
+    style="width: 100%"
+    @selection-change="handleSelectionChange"
+    :default-sort="{ prop: 'createTime', order: 'descending' }"
+    >
+    <el-table-column type="selection" width="55" />
+
+    <!--<el-table-column prop="id" label="ID" width="80" sortable />-->
+
+    <el-table-column prop="name" label="接口名称" min-width="150" show-overflow-tooltip />
+
+    <el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip />
+
+    <el-table-column prop="url" label="接口地址" min-width="200" show-overflow-tooltip>
+        <template #default="{ row }">
+        <el-tag type="info" size="small">{{ row.method }}</el-tag>
+        <span style="margin-left: 8px">{{ row.url }}</span>
+    </template>
+</el-table-column>
+
+<el-table-column prop="status" label="状态" width="100">
+    <template #default="{ row }">
+    <el-tag :type="row.status === 1 ? 'success' : 'danger'">
+    {{ row.status === 1 ? '开启' : '关闭' }}
+</el-tag>
+</template>
+</el-table-column>
+
+<el-table-column prop="method" label="请求类型" width="100">
+    <template #default="{ row }">
+    <el-tag
+    :type="getMethodTagType(row.method)"
+    effect="light"
+    >
+    {{ row.method }}
+</el-tag>
+</template>
+</el-table-column>
+
+<el-table-column prop="createTime" label="创建时间" width="180" sortable>
+    <template #default="{ row }">
+    {{ formatDate(row.createTime) }}
+</template>
+</el-table-column>
+
+<el-table-column prop="updateTime" label="更新时间" width="180" sortable>
+    <template #default="{ row }">
+    {{ formatDate(row.updateTime) }}
+</template>
+</el-table-column>
+
+<el-table-column label="操作" width="200" fixed="right">
+    <template #default="{ row }">
+    <el-button
+        size="small"
+        type="primary"
+        link
+    @click="handleEdit(row)"
+    :icon="Edit"
+    >
+    编辑
+</el-button>
+<el-button
+    size="small"
+    type="success"
+    link
+              @click="handleView(row)"
+:icon="View"
+    >
+    详情
+    </el-button>
+<el-button
+    size="small"
+    type="danger"
+    link
+              @click="handleDelete(row)"
+:icon="Delete"
+    >
+    删除
+    </el-button>
+</template>
+</el-table-column>
+</el-table>
+
+<!-- 分页 -->
+<div class="pagination-container">
+    <el-pagination
+        v-model:current-page="pagination.current"
+        v-model:page-size="pagination.size"
+    :page-sizes="[10, 20, 50, 100]"
+    :total="pagination.total"
+    layout="total, sizes, prev, pager, next, jumper"
+    @size-change="handleSizeChange"
+    @current-change="handleCurrentChange"
+    />
+</div>
+</el-card>
+
+<!-- 新增/编辑对话框 -->
+<el-dialog
+      :title="dialogTitle"
+v-model="dialogVisible"
+width="600px"
+:before-close="handleDialogClose"
+    >
+    <el-form
+ref="formRef"
+:model="form"
+:rules="rules"
+label-width="100px"
+class="form-container"
+    >
+    <el-form-item label="接口名称" prop="name">
+    <el-input
+v-model="form.name"
+placeholder="请输入接口名称"
+maxlength="50"
+show-word-limit
+/>
+</el-form-item>
+
+<el-form-item label="接口描述" prop="description">
+    <el-input
+        v-model="form.description"
+        type="textarea"
+    :rows="3"
+    placeholder="请输入接口描述"
+    maxlength="200"
+    show-word-limit
+    />
+</el-form-item>
+
+<el-form-item label="接口地址" prop="url">
+    <el-input
+        v-model="form.url"
+        placeholder="请输入接口地址,如:/api/user/list"
+    />
+</el-form-item>
+
+<el-form-item label="请求类型" prop="method">
+    <el-select v-model="form.method" placeholder="请选择请求类型" style="width: 100%">
+        <el-option label="GET" value="GET" />
+        <el-option label="POST" value="POST" />
+        <el-option label="PUT" value="PUT" />
+        <el-option label="DELETE" value="DELETE" />
+    </el-select>
+</el-form-item>
+
+<el-form-item label="请求头" prop="requestHeader">
+    <el-input
+        v-model="form.requestHeader"
+        type="textarea"
+    :rows="3"
+    placeholder='请输入请求头,JSON格式,如:{"Content-Type": "application/json"}'
+    />
+</el-form-item>
+
+<el-form-item label="响应头" prop="responseHeader">
+    <el-input
+        v-model="form.responseHeader"
+        type="textarea"
+    :rows="3"
+    placeholder='请输入响应头,JSON格式,如:{"Content-Type": "application/json"}'
+    />
+</el-form-item>
+
+<el-form-item label="接口状态" prop="status">
+    <el-radio-group v-model="form.status">
+        <el-radio :label="1">开启</el-radio>
+    <el-radio :label="0">关闭</el-radio>
+</el-radio-group>
+</el-form-item>
+</el-form>
+
+<template #footer>
+<span class="dialog-footer">
+    <el-button @click="handleDialogClose">取消</el-button>
+<el-button type="primary" @click="handleSubmit" :loading="submitLoading">
+    确定
+    </el-button>
+</span>
+</template>
+</el-dialog>
+
+<!-- 详情对话框 -->
+<el-dialog
+    title="接口详情"
+    v-model="detailDialogVisible"
+    width="700px"
+>
+    <el-descriptions :column="2" border>
+    <el-descriptions-item label="接口ID">{{ currentRow?.id }}</el-descriptions-item>
+    <el-descriptions-item label="接口名称">{{ currentRow?.name }}</el-descriptions-item>
+    <el-descriptions-item label="接口描述" :span="2">
+    {{ currentRow?.description }}
+</el-descriptions-item>
+<el-descriptions-item label="接口地址" :span="2">
+    <el-tag :type="getMethodTagType(currentRow?.method)" style="margin-right: 8px">
+    {{ currentRow?.method }}
+</el-tag>
+{{ currentRow?.url }}
+</el-descriptions-item>
+<el-descriptions-item label="接口状态">
+    <el-tag :type="currentRow?.status === 1 ? 'success' : 'danger'">
+    {{ currentRow?.status === 1 ? '开启' : '关闭' }}
+</el-tag>
+</el-descriptions-item>
+<el-descriptions-item label="创建人">{{ currentRow?.userId }}</el-descriptions-item>
+<el-descriptions-item label="请求头" :span="2">
+    <pre class="json-content">{{ formatJson(currentRow?.requestHeader) }}</pre>
+</el-descriptions-item>
+<el-descriptions-item label="响应头" :span="2">
+    <pre class="json-content">{{ formatJson(currentRow?.responseHeader) }}</pre>
+</el-descriptions-item>
+<el-descriptions-item label="创建时间">
+    {{ formatDate(currentRow?.createTime) }}
+</el-descriptions-item>
+<el-descriptions-item label="更新时间">
+    {{ formatDate(currentRow?.updateTime) }}
+</el-descriptions-item>
+</el-descriptions>
+
+<template #footer>
+<span class="dialog-footer">
+    <el-button type="primary" @click="detailDialogVisible = false">关闭</el-button>
+</span>
+</template>
+</el-dialog>
+</div>
+</template>
+
+<script setup>
+    import { ref, reactive, onMounted, computed } from 'vue'
+    import { ElMessage, ElMessageBox } from 'element-plus'
+    import {
+    Search,
+    Refresh,
+    Plus,
+    Edit,
+    Delete,
+    View
+} from '@element-plus/icons-vue'
+    import {addInterface, deleteInterface, queryInterface, updateInterface} from "../../commom/api/interfaceInfo.js";
+
+    // 响应式数据
+    const loading = ref(false)
+    const dialogVisible = ref(false)
+    const detailDialogVisible = ref(false)
+    const submitLoading = ref(false)
+    const multipleSelection = ref([])
+    const currentRow = ref(null)
+
+    // 搜索表单
+    const searchForm = reactive({
+    name: '',
+    status: '',
+    method: ''
+})
+
+    // 分页配置
+    const pagination = reactive({
+    current: 1,
+    size: 10,
+    total: 0
+})
+
+    // 表格数据
+    const tableData = ref([
+    {
+        id: 1,
+        name: '用户列表接口',
+        description: '获取系统所有用户列表信息',
+        url: '/api/user/list',
+        requestHeader: '{"Content-Type": "application/json"}',
+        responseHeader: '{"Content-Type": "application/json"}',
+        status: 1,
+        method: 'GET',
+        userId: 1,
+        createTime: new Date('2024-01-15 10:00:00'),
+        updateTime: new Date('2024-01-15 10:00:00'),
+        deleteFlag: 0
+    },
+    {
+        id: 2,
+        name: '新增用户接口',
+        description: '创建新的系统用户',
+        url: '/api/user/add',
+        requestHeader: '{"Content-Type": "application/json"}',
+        responseHeader: '{"Content-Type": "application/json"}',
+        status: 1,
+        method: 'POST',
+        userId: 1,
+        createTime: new Date('2024-01-16 14:30:00'),
+        updateTime: new Date('2024-01-16 14:30:00'),
+        deleteFlag: 0
+    }
+    ])
+
+    // 表单数据
+    const form = reactive({
+    id: null,
+    name: '',
+    description: '',
+    url: '',
+    requestHeader: '',
+    responseHeader: '',
+    status: 1,
+    method: ''
+})
+
+    // 表单引用和验证规则
+    const formRef = ref()
+    const rules = reactive({
+    name: [
+{ required: true, message: '请输入接口名称', trigger: 'blur' },
+{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+    ],
+    description: [
+{ required: true, message: '请输入接口描述', trigger: 'blur' },
+{ min: 5, max: 200, message: '长度在 5 到 200 个字符', trigger: 'blur' }
+    ],
+    url: [
+{ required: true, message: '请输入接口地址', trigger: 'blur' }
+    ],
+    method: [
+{ required: true, message: '请选择请求类型', trigger: 'change' }
+    ],
+    status: [
+{ required: true, message: '请选择接口状态', trigger: 'change' }
+    ]
+})
+
+    // 计算属性
+    const dialogTitle = computed(() => {
+    return form.id ? '编辑接口' : '新增接口'
+})
+
+    // 方法
+    const handleSearch = () => {
+    pagination.current = 1
+    fetchTableData()
+}
+
+    const handleReset = () => {
+    Object.assign(searchForm, {
+        name: '',
+        status: '',
+        method: ''
+    })
+    pagination.current = 1
+    fetchTableData()
+}
+
+    const handleAdd = () => {
+    resetForm()
+    dialogVisible.value = true
+}
+
+    const handleEdit = (row) => {
+    resetForm()
+    Object.assign(form, { ...row })
+    dialogVisible.value = true
+}
+
+    const handleView = (row) => {
+    currentRow.value = row
+    detailDialogVisible.value = true
+}
+
+    const handleDelete = async (row) => {
+    try {
+    await ElMessageBox.confirm(
+    `确定要删除接口 "${row.name}" 吗?`,
+    '删除确认',
+{
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+}
+    )
+
+    // 调用删除API
+    // await deleteInterfaceInfo(row.id)
+      deleteInterface(row.id).then(res=>{
+        if(res.code===200){
+          ElMessage.success('删除成功')
+          fetchTableData()
+        }else{
+          ElMessage.error(res.msg)
+        }
+      })
+} catch (error) {
+    // 用户取消删除
+      ElMessage.info('取消删除')
+}
+}
+
+    const handleBatchDelete = async () => {
+    if (multipleSelection.value.length === 0) {
+    ElMessage.warning('请选择要删除的接口')
+    return
+}
+
+    try {
+    await ElMessageBox.confirm(
+    `确定要删除选中的 ${multipleSelection.value.length} 个接口吗?`,
+    '批量删除确认',
+{
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+}
+    )
+
+    const ids = multipleSelection.value.map(item => item.id)
+    // 调用批量删除API
+    // await batchDeleteInterfaceInfo(ids)
+      deleteInterface(ids).then(res=>{
+        if(res.code===200){
+          ElMessage.success('批量删除成功')
+          multipleSelection.value = []
+          fetchTableData()
+        }else{
+          ElMessage.error(res.msg)
+        }
+      })
+} catch (error) {
+    // 用户取消删除
+}
+}
+
+    const handleSelectionChange = (selection) => {
+    multipleSelection.value = selection
+}
+
+    const handleSizeChange = (size) => {
+    pagination.size = size
+    pagination.current = 1
+    fetchTableData()
+}
+
+    const handleCurrentChange = (current) => {
+    pagination.current = current
+    fetchTableData()
+}
+
+    const handleDialogClose = () => {
+    dialogVisible.value = false
+    resetForm()
+}
+
+    const handleSubmit = async () => {
+    if (!formRef.value) return
+
+    try {
+    const valid = await formRef.value.validate()
+    if (valid) {
+    submitLoading.value = true
+
+    if (form.id) {
+    // 调用更新API
+      updateInterface(form).then(res=>{
+      if(res.code===200){
+        ElMessage.success('更新成功')
+        fetchTableData()
+      }else{
+        ElMessage.error(res.msg)
+      }
+      })
+} else {
+    // 调用新增API
+      addInterface(form).then(res=>{
+        if(res.code===200){
+          ElMessage.success('新增成功')
+          fetchTableData()
+        }else{
+          ElMessage.error(res.msg)
+        }
+      })
+}
+
+    dialogVisible.value = false
+    // fetchTableData()
+}
+} catch (error) {
+    console.error('提交失败:', error)
+    ElMessage.error('操作失败,请稍后重试')
+} finally {
+    submitLoading.value = false
+}
+}
+
+    const resetForm = () => {
+    if (formRef.value) {
+    formRef.value.resetFields()
+}
+    Object.assign(form, {
+    id: null,
+    name: '',
+    description: '',
+    url: '',
+    requestHeader: '',
+    responseHeader: '',
+    status: 1,
+    method: ''
+})
+}
+
+    const fetchTableData = async () => {
+    loading.value = true
+    try {
+    // 模拟API调用
+      queryInterface({...searchForm, ...pagination }).then((res) => {
+          tableData.value = res.data.records
+          pagination.total = res.data.total
+      })
+} catch (error) {
+    console.error('获取数据失败:', error)
+    ElMessage.error('获取数据失败')
+} finally {
+    loading.value = false
+}
+}
+
+    const formatDate = (date) => {
+    if (!date) return '-'
+    return new Date(date).toLocaleString('zh-CN')
+}
+
+    const formatJson = (jsonStr) => {
+    if (!jsonStr) return '-'
+    try {
+    const obj = JSON.parse(jsonStr)
+    return JSON.stringify(obj, null, 2)
+} catch {
+    return jsonStr
+}
+}
+
+    const getMethodTagType = (method) => {
+    const types = {
+    'GET': 'success',
+    'POST': 'primary',
+    'PUT': 'warning',
+    'DELETE': 'danger'
+}
+    return types[method] || 'info'
+}
+
+    // 生命周期
+    onMounted(() => {
+    fetchTableData()
+})
+</script>
+
+<style scoped>
+    .interface-info-container {
+    padding: 20px;
+}
+
+    .page-header {
+    margin-bottom: 24px;
+}
+
+    .page-title {
+    font-size: 24px;
+    font-weight: 600;
+    color: #333;
+    margin: 0 0 8px 0;
+}
+
+    .page-description {
+    color: #666;
+    margin: 0;
+}
+
+    .search-card {
+    margin-bottom: 16px;
+}
+
+    .search-form {
+    margin-bottom: -18px;
+}
+
+    .operation-area {
+    margin-bottom: 16px;
+}
+
+    .table-card {
+    margin-bottom: 20px;
+}
+
+    .pagination-container {
+    margin-top: 20px;
+    display: flex;
+    justify-content: flex-end;
+}
+
+    .form-container {
+    padding-right: 20px;
+}
+
+    .json-content {
+    background: #f5f7fa;
+    padding: 8px;
+    border-radius: 4px;
+    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
+    font-size: 12px;
+    margin: 0;
+    white-space: pre-wrap;
+    word-break: break-all;
+}
+
+    /* 响应式设计 */
+    @media (max-width: 768px) {
+    .interface-info-container {
+    padding: 16px;
+}
+
+    .search-form {
+    display: flex;
+    flex-direction: column;
+}
+
+    .search-form .el-form-item {
+    margin-right: 0;
+    margin-bottom: 16px;
+    width: 100%;
+}
+
+    .search-form .el-form-item:last-child {
+    margin-bottom: 0;
+}
+}
+
+    @media (max-width: 480px) {
+    .interface-info-container {
+    padding: 12px;
+}
+
+    .page-title {
+    font-size: 20px;
+}
+
+    .operation-area {
+    display: flex;
+    flex-direction: column;
+    gap: 8px;
+}
+
+    .operation-area .el-button {
+    width: 100%;
+}
+}
+</style>

+ 649 - 0
ui/yangyi-open-api-platform-ui/src/views/main.vue

@@ -0,0 +1,649 @@
+<template>
+  <el-container class="main-layout">
+    <!-- 头部导航 -->
+    <el-header class="layout-header">
+      <div class="header-left">
+        <div class="logo">
+          <i class="el-icon-monitor logo-icon"></i>
+          <span class="system-name">管理系统</span>
+        </div>
+
+        <!-- 移动端菜单切换按钮 -->
+        <el-button
+            class="mobile-menu-btn"
+            icon="el-icon-menu"
+            @click="toggleMobileMenu"
+            v-show="isMobile"
+        />
+      </div>
+
+      <div class="header-right">
+        <el-dropdown @command="handleUserCommand">
+          <div class="user-info">
+            <el-avatar :size="32" :src="userInfo.avatar" />
+            <span class="username">{{ userInfo.name }}</span>
+            <i class="el-icon-arrow-down"></i>
+          </div>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <!--<el-dropdown-item command="profile">-->
+              <!--  <i class="el-icon-user"></i>个人信息-->
+              <!--</el-dropdown-item>-->
+              <!--<el-dropdown-item command="settings">-->
+              <!--  <i class="el-icon-setting"></i>系统设置-->
+              <!--</el-dropdown-item>-->
+              <el-dropdown-item divided command="logout">
+                <i class="el-icon-switch-button"></i>退出登录
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+
+        <el-tooltip content="全屏切换" placement="bottom">
+          <el-button
+              class="header-btn"
+              :icon="isFullscreen ? 'el-icon-close' : 'el-icon-full-screen'"
+              @click="toggleFullscreen"
+          />
+        </el-tooltip>
+
+        <!--<el-tooltip content="消息通知" placement="bottom">-->
+        <!--  <el-badge :value="5" class="header-btn">-->
+        <!--    <el-button icon="el-icon-bell" @click="showNotifications" />-->
+        <!--  </el-badge>-->
+        <!--</el-tooltip>-->
+      </div>
+    </el-header>
+
+    <el-container class="layout-body">
+      <!-- 侧边栏菜单 -->
+      <el-aside
+          :width="isCollapse ? '64px' : '240px'"
+          :class="['layout-aside', { 'mobile-open': mobileMenuOpen }]"
+      >
+        <div class="menu-toggle" @click="toggleCollapse" v-if="!isMobile">
+          <i :class="isCollapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'"></i>
+        </div>
+
+        <el-menu
+            :default-active="activeMenu"
+            :collapse="isCollapse && !isMobile"
+            :collapse-transition="false"
+            router
+            class="sidebar-menu"
+            background-color="#001529"
+            text-color="#bfbfbf"
+            active-text-color="#1890ff"
+            @select="handleMenuSelect"
+        >
+          <template v-for="menu in menuList" :key="menu.path">
+            <!-- 有子菜单的项 -->
+            <el-sub-menu v-if="menu.children && menu.children.length" :index="menu.path">
+              <template #title>
+                <i :class="menu.icon"></i>
+                <span>{{ menu.title }}</span>
+              </template>
+              <el-menu-item
+                  v-for="child in menu.children"
+                  :key="child.path"
+                  :index="child.path"
+              >
+                <i :class="child.icon"></i>
+                <span>{{ child.title }}</span>
+              </el-menu-item>
+            </el-sub-menu>
+
+            <!-- 没有子菜单的项 -->
+            <el-menu-item v-else :index="menu.path">
+              <i :class="menu.icon"></i>
+              <span>{{ menu.title }}</span>
+            </el-menu-item>
+          </template>
+        </el-menu>
+      </el-aside>
+
+      <!-- 主内容区域 -->
+      <el-main class="layout-main">
+        <div class="main-content">
+          <router-view v-slot="{ Component }">
+            <transition name="fade-transform" mode="out-in">
+              <component :is="Component" />
+            </transition>
+          </router-view>
+        </div>
+      </el-main>
+    </el-container>
+
+    <!-- 移动端遮罩层 -->
+    <div
+        v-show="isMobile && mobileMenuOpen"
+        class="mobile-mask"
+        @click="closeMobileMenu"
+    ></div>
+  </el-container>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, onUnmounted } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import {getUserName, getUserAvatar, clearTokenAndUserInfo} from "/src/commom/utils/auth.js";
+
+const router = useRouter()
+const route = useRoute()
+
+// 响应式数据
+const isCollapse = ref(false)
+const isFullscreen = ref(false)
+const mobileMenuOpen = ref(false)
+const isMobile = ref(false)
+
+const name = computed(()=>getUserName())
+const avatar = 'https://www.loliapi.com/acg/pp/'
+// 用户信息
+const userInfo = ref({
+  name: name?name:'管理员',
+  avatar: avatar &&avatar !=='' ?avatar:'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'
+})
+
+// 菜单数据
+const menuList = ref([
+  {
+    path: '/dashboard',
+    title: '仪表盘',
+    icon: 'el-icon-data-analysis',
+    children: [
+      {
+        path: '/dashboard/overview',
+        title: '数据概览',
+        icon: 'el-icon-trend-charts'
+      },
+      {
+        path: '/dashboard/analytics',
+        title: '数据分析',
+        icon: 'el-icon-data-line'
+      }
+    ]
+  },
+  {
+    path: '/user',
+    title: '用户管理',
+    icon: 'el-icon-user',
+    children: [
+      {
+        path: '/user/list',
+        title: '用户列表',
+        icon: 'el-icon-user-solid'
+      },
+      {
+        path: '/user/role',
+        title: '角色管理',
+        icon: 'el-icon-s-custom'
+      },
+      {
+        path: '/user/permission',
+        title: '权限管理',
+        icon: 'el-icon-key'
+      }
+    ]
+  },
+  {
+    path: '/interfaceInfo',
+    title: '接口管理',
+    icon: 'el-icon-document',
+    children: [
+      {
+        path: '/manage/interfaceInfo',
+        title: '接口管理',
+        icon: 'el-icon-notebook-2'
+      }
+    ]
+  },
+  // {
+  //   path: '/system',
+  //   title: '系统设置',
+  //   icon: 'el-icon-setting',
+  //   children: [
+  //     {
+  //       path: '/system/basic',
+  //       title: '基础设置',
+  //       icon: 'el-icon-tools'
+  //     },
+  //     {
+  //       path: '/system/logs',
+  //       title: '系统日志',
+  //       icon: 'el-icon-document-checked'
+  //     }
+  //   ]
+  // },
+  // {
+  //   path: '/monitor',
+  //   title: '系统监控',
+  //   icon: 'el-icon-monitor',
+  //   children: [
+  //     {
+  //       path: '/monitor/server',
+  //       title: '服务器监控',
+  //       icon: 'el-icon-cpu'
+  //     },
+  //     {
+  //       path: '/monitor/performance',
+  //       title: '性能监控',
+  //       icon: 'el-icon-odometer'
+  //     }
+  //   ]
+  // }
+])
+
+// 计算属性
+const activeMenu = computed(() => route.path)
+
+// 方法
+const toggleCollapse = () => {
+  isCollapse.value = !isCollapse.value
+}
+
+const toggleMobileMenu = () => {
+  mobileMenuOpen.value = !mobileMenuOpen.value
+}
+
+const closeMobileMenu = () => {
+  mobileMenuOpen.value = false
+}
+
+const handleMenuSelect = () => {
+  if (isMobile.value) {
+    closeMobileMenu()
+  }
+}
+
+const toggleFullscreen = () => {
+  if (!document.fullscreenElement) {
+    document.documentElement.requestFullscreen()
+    isFullscreen.value = true
+  } else {
+    if (document.exitFullscreen) {
+      document.exitFullscreen()
+      isFullscreen.value = false
+    }
+  }
+}
+
+const handleUserCommand = (command) => {
+  switch (command) {
+    // case 'profile':
+    //   router.push('/profile')
+    //   break
+    // case 'settings':
+    //   router.push('/system/basic')
+    //   break
+    case 'logout':
+      handleLogout()
+      break
+  }
+}
+
+const handleLogout = async () => {
+  try {
+    await ElMessageBox.confirm('确定要退出登录吗?', '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning'
+    })
+
+    // 清除用户信息
+    clearTokenAndUserInfo()
+    ElMessage.success('退出成功')
+    router.push('/login')
+  } catch (error) {
+    // 用户取消退出
+  }
+}
+
+const showNotifications = () => {
+  ElMessage.info('这是消息通知')
+}
+
+// 检测屏幕尺寸
+const checkMobile = () => {
+  isMobile.value = window.innerWidth <= 768
+  if (!isMobile.value) {
+    mobileMenuOpen.value = false
+  }
+}
+
+// 监听全屏状态变化
+const handleFullscreenChange = () => {
+  isFullscreen.value = !!document.fullscreenElement
+}
+
+// 监听窗口大小变化
+const handleResize = () => {
+  checkMobile()
+}
+
+// 生命周期
+onMounted(() => {
+  document.addEventListener('fullscreenchange', handleFullscreenChange)
+  window.addEventListener('resize', handleResize)
+  checkMobile() // 初始化时检测一次
+})
+
+onUnmounted(() => {
+  document.removeEventListener('fullscreenchange', handleFullscreenChange)
+  window.removeEventListener('resize', handleResize)
+})
+</script>
+
+<style scoped>
+.main-layout {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  height: 100vh !important;
+  width: 100vw !important;
+  margin: 0;
+  padding: 0;
+  overflow: hidden;
+}
+
+/* 头部样式 */
+.layout-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background: #fff;
+  border-bottom: 1px solid #e6e6e6;
+  padding: 0 20px;
+  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
+  z-index: 1001;
+  height: 60px !important;
+  position: relative;
+}
+
+.header-left {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+}
+
+.logo {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  font-weight: 600;
+  font-size: 18px;
+  color: #1890ff;
+}
+
+.logo-icon {
+  font-size: 24px;
+}
+
+.system-name {
+  font-size: 18px;
+  font-weight: 600;
+  color: #333;
+}
+
+.mobile-menu-btn {
+  display: none;
+  border: none;
+  background: transparent;
+  font-size: 18px;
+  color: #666;
+}
+
+.header-right {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+}
+
+.user-info {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 8px 12px;
+  border-radius: 6px;
+  cursor: pointer;
+  transition: background-color 0.3s;
+}
+
+.user-info:hover {
+  background-color: #f5f5f5;
+}
+
+.username {
+  font-size: 14px;
+  color: #333;
+}
+
+.header-btn {
+  border: none;
+  background: transparent;
+  font-size: 16px;
+  color: #666;
+  cursor: pointer;
+  padding: 8px;
+  border-radius: 6px;
+  transition: all 0.3s;
+}
+
+.header-btn:hover {
+  background-color: #f5f5f5;
+  color: #1890ff;
+}
+
+/* 主体样式 */
+.layout-body {
+  height: calc(100vh - 60px) !important;
+  width: 100%;
+  position: relative;
+}
+
+/* 侧边栏样式 */
+.layout-aside {
+  background: #001529;
+  transition: all 0.3s;
+  position: relative;
+  overflow: hidden;
+  height: 100%;
+}
+
+.menu-toggle {
+  height: 48px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #fff;
+  cursor: pointer;
+  border-bottom: 1px solid #002140;
+  transition: all 0.3s;
+}
+
+.menu-toggle:hover {
+  background: #002140;
+}
+
+.menu-toggle i {
+  font-size: 18px;
+}
+
+.sidebar-menu {
+  border: none;
+  height: calc(100% - 48px);
+  overflow-y: auto;
+}
+
+.sidebar-menu:not(.el-menu--collapse) {
+  width: 240px;
+}
+
+/* 主内容区域样式 */
+.layout-main {
+  padding: 0;
+  background: #f0f2f5;
+  overflow: hidden;
+  height: 100%;
+  width: 100%;
+}
+
+.main-content {
+  height: 100%;
+  padding: 20px;
+  overflow-y: auto;
+}
+
+/* 移动端遮罩层 */
+.mobile-mask {
+  position: fixed;
+  top: 60px;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.5);
+  z-index: 999;
+}
+
+/* 过渡动画 */
+.fade-transform-leave-active,
+.fade-transform-enter-active {
+  transition: all 0.3s;
+}
+
+.fade-transform-enter-from {
+  opacity: 0;
+  transform: translateX(-30px);
+}
+
+.fade-transform-leave-to {
+  opacity: 0;
+  transform: translateX(30px);
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .layout-header {
+    padding: 0 16px;
+  }
+
+  .system-name {
+    font-size: 16px;
+  }
+
+  .username {
+    display: none;
+  }
+
+  .mobile-menu-btn {
+    display: block;
+  }
+
+  .layout-aside {
+    position: fixed;
+    top: 60px;
+    left: 0;
+    bottom: 0;
+    z-index: 1000;
+    transform: translateX(-100%);
+    transition: transform 0.3s;
+    width: 240px !important;
+  }
+
+  .layout-aside.mobile-open {
+    transform: translateX(0);
+  }
+
+  .menu-toggle {
+    display: none;
+  }
+
+  .sidebar-menu {
+    height: 100%;
+  }
+
+  .main-content {
+    padding: 16px;
+  }
+}
+
+@media (max-width: 480px) {
+  .layout-header {
+    padding: 0 12px;
+  }
+
+  .header-right {
+    gap: 8px;
+  }
+
+  .main-content {
+    padding: 12px;
+  }
+
+  .logo {
+    gap: 8px;
+  }
+
+  .logo-icon {
+    font-size: 20px;
+  }
+
+  .system-name {
+    font-size: 16px;
+  }
+}
+
+/* 滚动条样式 */
+.main-content::-webkit-scrollbar {
+  width: 6px;
+}
+
+.main-content::-webkit-scrollbar-track {
+  background: #f1f1f1;
+  border-radius: 3px;
+}
+
+.main-content::-webkit-scrollbar-thumb {
+  background: #c1c1c1;
+  border-radius: 3px;
+}
+
+.main-content::-webkit-scrollbar-thumb:hover {
+  background: #a8a8a8;
+}
+
+.sidebar-menu::-webkit-scrollbar {
+  width: 4px;
+}
+
+.sidebar-menu::-webkit-scrollbar-track {
+  background: #001529;
+}
+
+.sidebar-menu::-webkit-scrollbar-thumb {
+  background: #003366;
+  border-radius: 2px;
+}
+
+.sidebar-menu::-webkit-scrollbar-thumb:hover {
+  background: #004488;
+}
+
+/* 确保全局样式 */
+:global(html), :global(body) {
+  margin: 0;
+  padding: 0;
+  height: 100%;
+  width: 100%;
+  overflow: hidden;
+}
+
+:global(#app) {
+  height: 100vh;
+  width: 100vw;
+  overflow: hidden;
+}
+</style>