소스 검색

Signed-off-by: zhaobao <528046418@qq.com>

zhaobao 3 년 전
부모
커밋
95cfea77c8

+ 4 - 4
src/api/paas/apiApi.ts

@@ -31,9 +31,9 @@ export function getApiByList(data: any) {
  * @param apiId
  * @returns
  */
-export function getApiById(apiId: number) {
+export function getApiById(modId: number, apiId: number) {
   return apiRequest({
-    url: `/paas/api/${apiId}`,
+    url: `/paas/api/${modId}/${apiId}`,
     method: 'GET',
   })
 }
@@ -70,9 +70,9 @@ export function updateApi(data: any) {
  * @param status
  * @returns
  */
-export function updateApiStatus(apiId: number, status: number) {
+export function updateApiStatus(modId: number, apiId: number, status: number) {
   return apiRequest({
-    url: `/paas/api/updateStatus/${apiId}/${status}`,
+    url: `/paas/api/updateStatus/${modId}/${apiId}/${status}`,
     method: 'PUT',
   })
 }

+ 17 - 13
src/router/modules/daas.ts

@@ -27,41 +27,45 @@ const routes: RouteRecordRaw[] = [
     ],
   },
   {
-    path: '/daas/data/access',
+    path: '/daas/data/database-access',
     component: Layout,
-    redirect: '/daas/data/type',
-    name: 'dataTypeList',
+    redirect: '/daas/data/database-access/index',
+    name: 'databaseAccess',
     meta: {
-      title: '数据库接入配置',
+      title: '数据库接入',
       icon: 'ep:coin',
     },
     children: [
       {
-        path: 'database-type',
-        name: 'database_type',
-        component: () => import('@/views/daas/database_type/index.vue'),
+        path: 'index',
+        name: 'database_access',
+        component: () => import('@/views/daas/database_access/index.vue'),
         meta: {
-          title: '数据库类别',
+          title: '数据库接入',
+          sidebar: false,
+          activeMenu: '/daas/data/database-access',
         },
       },
     ],
   },
   {
-    path: '/daas/data',
+    path: '/daas/data/database-type',
     component: Layout,
-    redirect: '/daas/data/type',
+    redirect: '/daas/data/database-type/index',
     name: 'dataTypeList',
     meta: {
-      title: '数据库接入配置',
+      title: '数据库类型',
       icon: 'ep:coin',
     },
     children: [
       {
-        path: 'database-type',
+        path: 'index',
         name: 'database_type',
         component: () => import('@/views/daas/database_type/index.vue'),
         meta: {
-          title: '数据库类别',
+          title: '数据库类型',
+          sidebar: false,
+          activeMenu: '/daas/data/database-type',
         },
       },
     ],

+ 4 - 3
src/router/modules/model.ts

@@ -9,7 +9,7 @@ const routes: RouteRecordRaw[] = [
     redirect: '/daas/model/object/index',
     name: 'daasModel',
     meta: {
-      title: '数据模型',
+      title: '对象定义',
       icon: 'ep:coin',
     },
     children: [
@@ -41,8 +41,8 @@ const routes: RouteRecordRaw[] = [
   {
     path: '/daas/model/manipulation',
     component: Layout,
-    redirect: '/daas/model/manipulation/index',
-    name: 'daasModel',
+    redirect: '/daas/model/manipulation/behavior',
+    name: 'daasManipulation',
     meta: {
       title: '对象操作',
       icon: 'ic:twotone-double-arrow',
@@ -54,6 +54,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('@/views/daas/manipulation/index.vue'),
         meta: {
           title: '对象操作',
+          sidebar: false,
         },
       },
     ],

+ 0 - 1
src/views/app_center/publish/index.vue

@@ -93,7 +93,6 @@ function onGoOn() {
   <div>
     <page-header title="应用发布" content="上线发布应用软件或应用服务" />
     <page-main>
-    {{ state.status }}
       <el-row v-if="state.status === 0" type="flex" justify="center">
         <el-col :md="12" :sm="18">
           <el-form ref="formRef" :model="state.formData" :rules="formRules" label-width="120px" label-suffix=":">

+ 119 - 0
src/views/daas/database_access/components/DBType.vue

@@ -0,0 +1,119 @@
+<route lang="yaml">
+meta:
+  enabled: false
+</route>
+
+<script lang="ts" setup>
+import { ElMessage } from 'element-plus'
+import type { FormInstance, FormRules } from 'element-plus'
+import { createDBType, getDBTypeById, updateDBType } from '@/api/daas/dbTypeApi'
+const emit = defineEmits(['success'])
+
+const formRef = ref<FormInstance>()
+const formRules = ref<FormRules>({
+  dbTypeTitle: [
+    { required: true, message: '请输入标题', trigger: 'blur' },
+  ],
+  dbTypeDesc: [
+    { required: false, message: '请输入描述', trigger: 'blur' },
+  ],
+})
+
+const state = reactive({
+  title: '',
+  dialogVisible: false,
+  loading: false,
+  formData: {
+    dbTypeId: 0,
+    dbTypeTitle: '',
+    dbTypeDesc: '',
+  },
+  actionType: 1,
+})
+
+onMounted(() => {})
+
+const getData = async (dbTypeId: number) => {
+  state.loading = true
+  const { data } = await getDBTypeById(dbTypeId)
+  state.loading = false
+  state.formData = data
+}
+
+function onSubmit() {
+  formRef.value && formRef.value.validate(async (valid) => {
+    if (valid) {
+      if (state.actionType === 1) {
+        await createDBType(state.formData)
+        state.dialogVisible = false
+        emit('success')
+      }
+      else {
+        await updateDBType(state.formData)
+        state.dialogVisible = false
+        emit('success')
+      }
+      ElMessage.success({
+        message: '保存成功',
+        center: true,
+      })
+    }
+  })
+}
+
+function onCancel() {
+  state.dialogVisible = false
+}
+
+function resetFormData() {
+  state.formData = {
+    dbTypeId: 0,
+    dbTypeTitle: '',
+    dbTypeDesc: '',
+  }
+}
+
+defineExpose({
+  showAddModel() {
+    resetFormData()
+    state.title = '新增'
+    state.actionType = 1
+    state.dialogVisible = true
+  },
+  showEditModel(dbTypeId: number) {
+    resetFormData()
+    state.title = '编辑'
+    state.actionType = 2
+    state.dialogVisible = true
+    getData(dbTypeId)
+  },
+
+})
+</script>
+
+<template>
+  <div>
+    <el-dialog v-model="state.dialogVisible" :title="state.title" width="600px" :close-on-click-modal="false" append-to-body destroy-on-close>
+      <div v-loading="state.loading">
+        <el-form ref="formRef" :model="state.formData" :rules="formRules" label-width="120px" label-suffix=":">
+          <el-form-item label="分类名称" prop="dbTypeTitle">
+            <el-input v-model="state.formData.dbTypeTitle" placeholder="请输入标题" />
+          </el-form-item>
+
+          <el-form-item label="简要说明" prop="dbTypeDesc">
+            <el-input v-model="state.formData.dbTypeDesc" type="textarea" rows="10" placeholder="请输入简要说明" />
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <template #footer>
+        <el-button size="large" @click="onCancel">
+          取消
+        </el-button>
+        <el-button type="primary" size="large" @click="onSubmit">
+          保存
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>

+ 123 - 0
src/views/daas/database_access/index.vue

@@ -0,0 +1,123 @@
+<script lang="ts" setup name="DBTypeIndex">
+import { ElMessage, ElMessageBox } from 'element-plus'
+import DBType from './components/DBType.vue'
+import { deleteDBTypeById, getDBTypeByList } from '@/api/daas/dbTypeApi'
+
+const state = reactive({
+  loading: false,
+  // 表格是否自适应高度
+  tableAutoHeight: false,
+  // 搜索
+  search: {
+    keywords: '',
+  },
+  // 列表数据
+  dataList: [],
+})
+
+const appCatRef = ref()
+
+const getDataList = async () => {
+  state.loading = true
+  const { data } = await getDBTypeByList({})
+  state.dataList = data
+  state.loading = false
+}
+
+onMounted(() => {
+  getDataList()
+})
+
+function onCreate() {}
+
+function onDBTypeAdd() {
+  appCatRef.value.showAddModel()
+}
+
+function onDBTypeEdit(row: any) {
+  const appCatId = row.appCatId
+  appCatRef.value.showEditModel(appCatId)
+}
+
+function onDBTypeDel(row: any) {
+  ElMessageBox.confirm(`确认删除「${row.dbTypeTitle}」吗?`, '确认信息').then(async () => {
+    await deleteDBTypeById(row.appCatId)
+    await getDataList()
+    ElMessage.success({
+      message: '删除成功',
+      center: true,
+    })
+  }).catch(() => {})
+}
+</script>
+
+<template>
+  <div :class="{ 'absolute-container': state.tableAutoHeight }">
+    <page-header title="数据库接入设定">
+      <template #content>
+        <div>
+          系统支持多种数据库接入,用于设定接入参数
+        </div>
+      </template>
+
+      <el-button-group>
+        <el-button type="primary" size="large" plain @click="onDBTypeAdd">
+          <template #icon>
+            <el-icon>
+              <svg-icon name="ep:plus" />
+            </el-icon>
+          </template>
+          新增
+        </el-button>
+      </el-button-group>
+    </page-header>
+
+    <page-main>
+      <el-table v-loading="state.loading" class="list-table" :data="state.dataList" border stripe highlight-current-row height="100%">
+        <el-table-column type="index" label="序号" width="60" header-align="center" align="center" />
+
+        <el-table-column prop="appCatTitle" label="名称" header-align="center" align="center" width="200" />
+
+        <el-table-column prop="appCatDesc" label="说明" />
+
+        <el-table-column label="操作" width="250" align="center" fixed="right">
+          <template #default="scope">
+            <el-button type="primary" size="small" plain @click="onDBTypeEdit(scope.row)">
+              编辑
+            </el-button>
+            <el-button v-if="!scope.row.isFixed" type="danger" size="small" plain @click="onDBTypeDel(scope.row)">
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </page-main>
+    <DBType ref="appCatRef" @success="getDataList" />
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.absolute-container {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+
+  .page-header {
+    margin-bottom: 0;
+  }
+
+  .page-main {
+    // 让 page-main 的高度自适应
+    flex: 1;
+    overflow: auto;
+    display: flex;
+    flex-direction: column;
+
+    .search-container {
+      margin-bottom: 0;
+    }
+  }
+}
+</style>

+ 2 - 2
src/views/daas/database_type/index.vue

@@ -53,10 +53,10 @@ function onDBTypeDel(row: any) {
 
 <template>
   <div :class="{ 'absolute-container': state.tableAutoHeight }">
-    <page-header title="数据库接入设定">
+    <page-header title="数据库类型设定">
       <template #content>
         <div>
-          系统支持多种数据库接入,用于设定接入参数
+          系统支持多种数据库类型,用于设定接入参数
         </div>
       </template>
 

+ 137 - 0
src/views/paas/api/components/Api/index.vue

@@ -0,0 +1,137 @@
+<route lang="yaml">
+meta:
+  enabled: false
+    </route>
+
+<script lang="ts" setup>
+import type { FormInstance, FormRules } from 'element-plus'
+import { createApi, getApiById, updateApi } from '@/api/paas/apiApi'
+const emit = defineEmits(['success'])
+
+const formRef = ref<FormInstance>()
+const formRules = ref<FormRules>({
+  apiTitle: [
+    { required: true, message: '请输入标题', trigger: 'blur' },
+  ],
+  apiDesc: [
+    { required: false, message: '请输入描述', trigger: 'blur' },
+  ],
+})
+
+const state = reactive({
+  title: '',
+  dialogVisible: false,
+  loading: false,
+  formData: {
+    modId: 0,
+    apiId: -1,
+    apiTitle: '',
+    apiCode: '',
+    status: 0,
+    apiDesc: '',
+    apiParams: '',
+  },
+  actionType: 1,
+})
+
+const getData = async (modId: number, apiId: number) => {
+  state.loading = true
+  const { data } = await getApiById(modId, apiId)
+  state.loading = false
+  state.formData = data
+}
+
+function onSubmit() {
+  formRef.value && formRef.value.validate(async (valid) => {
+    if (valid) {
+      if (state.actionType === 1) {
+        await createApi(state.formData)
+        state.dialogVisible = false
+        emit('success')
+      }
+      else {
+        await updateApi(state.formData)
+        state.dialogVisible = false
+        emit('success')
+      }
+    }
+  })
+}
+
+function onCancel() {
+  state.dialogVisible = false
+}
+
+function resetFormData() {
+  state.formData = {
+    modId: 0,
+    apiId: -1,
+    apiTitle: '',
+    apiCode: '',
+    status: 0,
+    apiDesc: '',
+    apiParams: '',
+  }
+}
+
+defineExpose({
+  showAddApi(modId: number) {
+    resetFormData()
+    state.title = '新增API'
+    state.actionType = 1
+    state.formData.modId = modId
+    state.dialogVisible = true
+  },
+  showEditApi(modId: number, apiId: any) {
+    resetFormData()
+    state.title = '编辑API'
+    state.formData.modId = modId
+    state.actionType = 2
+    state.dialogVisible = true
+    getData(modId, apiId)
+  },
+
+})
+</script>
+
+<template>
+  <div>
+    <el-dialog v-model="state.dialogVisible" :title="state.title" width="600px" :close-on-click-modal="false" append-to-body destroy-on-close>
+      <div v-loading="state.loading">
+        <el-form ref="formRef" :model="state.formData" :rules="formRules" label-width="120px" label-suffix=":">
+          <el-form-item label="标题" prop="apiTitle">
+            <el-input v-model="state.formData.apiTitle" placeholder="请输入标题" />
+          </el-form-item>
+          <el-form-item label="编码" prop="apiCode">
+            <el-input v-model="state.formData.apiCode" placeholder="请输入编码" />
+          </el-form-item>
+          <el-form-item label="参数">
+            <el-input v-model="state.formData.apiParams" placeholder="请输入参数" />
+          </el-form-item>
+          <el-form-item label="状态" prop="apiTitle">
+            <el-switch
+              v-model="state.formData.status"
+              :active-value="0"
+              :inactive-value="1"
+              inline-prompt
+              active-text="启用"
+              inactive-text="禁用"
+            />
+          </el-form-item>
+          <el-form-item label="简要说明" prop="apiDesc">
+            <el-input v-model="state.formData.apiDesc" type="textarea" rows="10" placeholder="请输入标题" />
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <template #footer>
+        <el-button size="large" @click="onCancel">
+          取消
+        </el-button>
+        <el-button type="primary" size="large" @click="onSubmit">
+          保存
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>

+ 116 - 0
src/views/paas/api/components/Module/index.vue

@@ -0,0 +1,116 @@
+<route lang="yaml">
+meta:
+  enabled: false
+  </route>
+
+<script lang="ts" setup>
+import type { FormInstance, FormRules } from 'element-plus'
+import { createModule, getModuleById, updateModule } from '@/api/paas/moduleApi'
+const emit = defineEmits(['success'])
+
+const formRef = ref<FormInstance>()
+const formRules = ref<FormRules>({
+  modTitle: [
+    { required: true, message: '请输入标题', trigger: 'blur' },
+  ],
+  modDesc: [
+    { required: false, message: '请输入描述', trigger: 'blur' },
+  ],
+})
+
+const state = reactive({
+  title: '',
+  dialogVisible: false,
+  loading: false,
+  formData: {
+    modId: 1,
+    modTitle: '',
+    modDesc: '',
+    isFixed: 0,
+  },
+  actionType: 1,
+})
+
+onMounted(() => {})
+
+const getData = async (modId: number) => {
+  state.loading = true
+  const { data } = await getModuleById(modId)
+  state.loading = false
+  state.formData = data
+}
+
+function onSubmit() {
+  formRef.value && formRef.value.validate(async (valid) => {
+    if (valid) {
+      if (state.actionType === 1) {
+        await createModule(state.formData)
+        state.dialogVisible = false
+        emit('success')
+      }
+      else {
+        await updateModule(state.formData)
+        state.dialogVisible = false
+        emit('success')
+      }
+    }
+  })
+}
+
+function onCancel() {
+  state.dialogVisible = false
+}
+
+function resetFormData() {
+  state.formData = {
+    modId: -1,
+    modTitle: '',
+    modDesc: '',
+    isFixed: 0,
+  }
+}
+
+defineExpose({
+  showAddModule() {
+    resetFormData()
+    state.title = '新增API模块'
+    state.actionType = 1
+    state.dialogVisible = true
+  },
+  showEditModule(modId: any) {
+    resetFormData()
+    state.title = '编辑API模块'
+    state.actionType = 2
+    state.dialogVisible = true
+    getData(modId)
+  },
+
+})
+</script>
+
+<template>
+  <div>
+    <el-dialog v-model="state.dialogVisible" :title="state.title" width="600px" :close-on-click-modal="false" append-to-body destroy-on-close>
+      <div v-loading="state.loading">
+        <el-form ref="formRef" :model="state.formData" :rules="formRules" label-width="120px" label-suffix=":">
+          <el-form-item label="API模块名称" prop="modTitle">
+            <el-input v-model="state.formData.modTitle" placeholder="请输入标题" />
+          </el-form-item>
+
+          <el-form-item label="简要说明" prop="modDesc">
+            <el-input v-model="state.formData.modDesc" type="textarea" rows="10" placeholder="请输入标题" />
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <template #footer>
+        <el-button size="large" @click="onCancel">
+          取消
+        </el-button>
+        <el-button type="primary" size="large" @click="onSubmit">
+          保存
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>

+ 0 - 122
src/views/paas/api/components/dictionaryDialog/index.vue

@@ -1,122 +0,0 @@
-<script lang="ts" setup>
-import type { FormInstance, FormRules } from 'element-plus'
-// import api from '@/api'
-
-const props = withDefaults(
-  defineProps<{
-    modelValue: boolean
-    parentId?: string | number
-    id?: string | number
-    tree: any[]
-  }>(),
-  {
-    modelValue: false,
-    parentId: '',
-    id: '',
-  },
-)
-
-const emit = defineEmits(['update:modelValue', 'addNode', 'editNode'])
-const myVisible = ref(props.modelValue)
-const title = computed(() => props.id === '' ? '新增字典' : '编辑字典')
-function formatTree(tree: any[], id: string | number, childrenDisabled = false) {
-  const data: any[] = []
-  tree.forEach((item) => {
-    const temp = { ...item }
-    if (temp.id === id || childrenDisabled) {
-      temp.disabled = true
-      if (temp.children) {
-        temp.children = formatTree(temp.children, id, true)
-      }
-    }
-    else {
-      temp.disabled = false
-      if (temp.children) {
-        temp.children = formatTree(temp.children, id, childrenDisabled)
-      }
-    }
-    data.push(temp)
-  })
-  return data
-}
-const myTree = computed(() => formatTree(props.tree, props.id))
-
-const formRef = ref<FormInstance>()
-const form = ref({
-  parentId: props.parentId,
-  id: props.id,
-  name: '',
-  code: '',
-})
-const formRules = ref<FormRules>({
-  name: [
-    { required: true, message: '请输入字典名称' },
-  ],
-  code: [
-    { required: true, message: '请输入字典编码' },
-  ],
-})
-
-onMounted(() => {
-  if (props.id !== '') {
-    // api.get('pages_example/dictionary/detail', {
-    //   params: {
-    //     id: props.id,
-    //   },
-    //   baseURL: '/mock/',
-    // }).then((res) => {
-    //   // form.value.parentId = res.data.parentId
-    //   form.value.id = res.data.id
-    //   form.value.name = res.data.name
-    //   form.value.code = res.data.code
-    // })
-  }
-})
-
-function onSubmit() {
-  if (form.value.id === '') {
-    formRef.value && formRef.value.validate((valid) => {
-      if (valid) { /* empty */ }
-    })
-  }
-  else {
-    formRef.value && formRef.value.validate((valid) => {
-      if (valid) { /* empty */ }
-    })
-  }
-}
-
-function onCancel() {
-  myVisible.value = false
-}
-</script>
-
-<template>
-  <el-dialog v-model="myVisible" :title="title" width="400px" :close-on-click-modal="false" append-to-body destroy-on-close @closed="emit('update:modelValue', false)">
-    <el-form ref="formRef" :model="form" :rules="formRules" label-width="80px">
-      <el-form-item label="父级字典" prop="parentId">
-        <el-cascader v-model="form.parentId" :options="myTree" :props="{ value: 'id', emitPath: false, checkStrictly: true }" :show-all-levels="false" placeholder="请选择父级字典,默认为根字典" clearable />
-      </el-form-item>
-      <el-form-item label="字典名称" prop="name">
-        <el-input v-model="form.name" placeholder="请输入字典名称" clearable />
-      </el-form-item>
-      <el-form-item label="字典编码" prop="code">
-        <el-input v-model="form.code" placeholder="请输入字典编码" clearable />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <el-button size="large" @click="onCancel">
-        取消
-      </el-button>
-      <el-button type="primary" size="large" @click="onSubmit">
-        确定
-      </el-button>
-    </template>
-  </el-dialog>
-</template>
-
-<style lang="scss" scoped>
-:deep(.el-cascader) {
-  width: 100%;
-}
-</style>

+ 0 - 109
src/views/paas/api/components/dictionaryItemDialog/index.vue

@@ -1,109 +0,0 @@
-<script lang="ts" setup>
-import type { FormInstance, FormRules } from 'element-plus'
-// import api from '@/api'
-
-const props = withDefaults(
-  defineProps<{
-    modelValue: boolean
-    dictionaryId: string | number
-    id?: string | number
-    tree: any[]
-  }>(),
-  {
-    modelValue: false,
-    dictionaryId: '',
-    id: '',
-  },
-)
-
-const emit = defineEmits(['update:modelValue', 'success'])
-const myVisible = ref(props.modelValue)
-const title = computed(() => props.id === '' ? '新增字典项' : '编辑字典项')
-
-const formRef = ref<FormInstance>()
-const form = ref({
-  dictionaryId: props.dictionaryId,
-  id: props.id,
-  name: '',
-  value: '',
-  enable: true,
-})
-const formRules = ref<FormRules>({
-  dictionaryId: [
-    { required: true, message: '请选择所属字典' },
-  ],
-  name: [
-    { required: true, message: '请输入字典项名称' },
-  ],
-  value: [
-    { required: true, message: '请输入字典项键值' },
-  ],
-})
-
-onMounted(() => {
-  if (props.id !== '') {
-    // api.get('pages_example/dictionary/item/detail', {
-    //   params: {
-    //     id: props.id,
-    //   },
-    //   baseURL: '/mock/',
-    // }).then((res) => {
-    //   // form.value.dictionaryId = res.data.dictionaryId
-    //   form.value.id = res.data.id
-    //   form.value.name = res.data.name
-    //   form.value.value = res.data.value
-    //   form.value.enable = res.data.enable
-    // })
-  }
-})
-
-function onSubmit() {
-  if (form.value.id === '') {
-    formRef.value && formRef.value.validate((valid) => {
-      if (valid) { /* empty */ }
-    })
-  }
-  else {
-    formRef.value && formRef.value.validate((valid) => {
-      if (valid) { /* empty */ }
-    })
-  }
-}
-
-function onCancel() {
-  myVisible.value = false
-}
-</script>
-
-<template>
-  <el-dialog v-model="myVisible" :title="title" width="400px" :close-on-click-modal="false" append-to-body destroy-on-close @closed="emit('update:modelValue', false)">
-    <el-form ref="formRef" :model="form" :rules="formRules" label-width="100px">
-      <el-form-item label="所属字典" prop="dictionaryId">
-        <el-cascader v-model="form.dictionaryId" :options="tree" :props="{ value: 'id', emitPath: false, checkStrictly: true }" :show-all-levels="false" placeholder="请选择所属字典" />
-      </el-form-item>
-      <el-form-item label="字典项名称" prop="name">
-        <el-input v-model="form.name" placeholder="请输入字典项名称" clearable />
-      </el-form-item>
-      <el-form-item label="字典项键值" prop="value">
-        <el-input v-model="form.value" placeholder="请输入字典项键值" clearable />
-      </el-form-item>
-      <el-form-item label="状态" prop="enable">
-        <el-switch v-model="form.enable" inline-prompt active-text="启用" inactive-text="禁用" />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <el-button size="large" @click="onCancel">
-        取消
-      </el-button>
-      <el-button type="primary" size="large" @click="onSubmit">
-        确定
-      </el-button>
-    </template>
-  </el-dialog>
-</template>
-
-<style lang="scss" scoped>
-:deep(.el-cascader) {
-  width: 100%;
-}
-</style>

+ 247 - 360
src/views/paas/api/index.vue

@@ -1,258 +1,192 @@
 <route lang="yaml">
-meta: ''
-enabled: false
-</route>
-
-<script lang="ts" setup name="PagesExampleDictionary">
-import { ElMessageBox } from 'element-plus'
-import type Node from 'element-plus/es/components/tree/src/model/node'
-import dictionaryDialog from './components/dictionaryDialog/index.vue'
-import dictionaryItemDialog from './components/dictionaryItemDialog/index.vue'
-
-interface Dict {
-  id: string | number
+meta:
+  enabled: false
+    </route>
+
+<script lang="ts" setup>
+import { ElMessage, ElMessageBox } from 'element-plus'
+import PSModule from './components/Module/index.vue'
+import PSApi from './components/Api/index.vue'
+import { deleteApiById, getApiByList, updateApiStatus } from '@/api/paas/apiApi'
+import { deleteModuleById, getModuleByList } from '@/api/paas/moduleApi'
+
+interface TreeNode {
   label: string
-  code: string
-  children?: Dict[]
+  isLeaf: boolean
+  modId: number
+  children?: TreeNode[]
+}
+
+interface Object {
+  modId: number
+  objectId: number
+  objectTitle: string
+  objectCode: string
+  comment: string
 }
-const dictionaryRef = ref()
-const dictionary = ref({
-  search: '',
-  tree: [] as Dict[],
-  currentNode: undefined as Node | undefined,
-  currentData: undefined as Dict | undefined,
-  dialog: {
-    visible: false,
-    parentId: '' as Dict['id'],
-    id: '' as Dict['id'],
-  },
-})
 
-const { pagination, getParams, onSizeChange, onCurrentChange, onSortChange } = usePagination()
-pagination.value.size = 20
-pagination.value.sizes = [20, 50, 100]
-const dictionaryItemRef = ref()
-const dictionaryItem = ref({
+const state = reactive({
   loading: false,
-  // 搜索
+  keywords: '',
+  Modules: [] as TreeNode[],
+  curModuleId: 0,
+  objectList: [] as Object[],
+  dialogState: 1,
   search: {
-    dictionaryId: '' as Dict['id'],
-    title: '',
-  },
-  // 列表数据
-  dataList: [],
-  selectionDataList: [],
-  dialog: {
-    visible: false,
-    id: '' as string | number,
+    keywords: '',
   },
 })
 
-const getDictionaryList = () => {
-  dictionaryItem.value.search.dictionaryId = ''
-}
-onMounted(() => {
-  getDictionaryList()
-})
-watch(() => dictionary.value.search, (val) => {
-  dictionaryRef.value!.filter(val)
-})
-const dictionaryFilter = (value: string, data: Dict) => {
-  if (!value) {
-    return true
+const leftTreeRef = ref()
+const ModuleRef = ref()
+const getApiData = async () => {
+  const curModuleId = state.curModuleId
+  if (curModuleId > 0) {
+    state.loading = true
+    const { data } = await getApiByList({ modId: curModuleId })
+    state.objectList = data
+    state.loading = false
   }
-  return data.label.includes(value)
 }
-const dictionaryAdd = (data?: Dict) => {
-  dictionary.value.currentData = data
-  dictionary.value.dialog.parentId = data?.id ?? ''
-  dictionary.value.dialog.id = ''
-  dictionary.value.dialog.visible = true
-}
-const dictionaryEdit = (node: Node, data: Dict) => {
-  dictionary.value.currentNode = node
-  dictionary.value.currentData = data
-  dictionary.value.dialog.parentId = node.parent.data.id ?? ''
-  dictionary.value.dialog.id = data.id
-  dictionary.value.dialog.visible = true
-}
-const dictionaryDelete = (node: Node, data: Dict) => {
-  ElMessageBox.confirm(`确认删除「${data.label}」吗?`, '确认信息').then(() => {
 
-  })
-}
-// 新增成功后更新树
-const dictionaryAddNode = (data: Dict) => {
-  if (dictionary.value.currentData) {
-    if (!dictionary.value.currentData.children) {
-      dictionary.value.currentData.children = []
-    }
-    dictionary.value.currentData.children.push({
-      id: data.id,
-      label: data.label,
-      code: data.code,
+const getModuleData = async () => {
+  const { data } = await getModuleByList('')
+  const children: TreeNode[] = []
+  data.forEach((item: any) => {
+    return children.push({
+      label: item.modTitle,
+      modId: item.modId,
+      isLeaf: true,
     })
-  }
-  else {
-    dictionary.value.tree.push({
-      id: data.id,
-      label: data.label,
-      code: data.code,
+  })
+  state.Modules = [
+    {
+      label: 'API模块列表',
+      isLeaf: false,
+      modId: -1,
+      children,
+    },
+  ]
+
+  if (data && data.length > 0) {
+    state.curModuleId = data[0].modId
+    nextTick(() => {
+      leftTreeRef.value.setCurrentKey(data[0].modId)
     })
+    await getApiData()
   }
 }
-// 编辑成功后更新树
-const dictionaryEditNode = (data: Dict, parentId: string | number) => {
-  if (dictionary.value.currentNode && dictionary.value.currentData) {
-    if ((dictionary.value.currentNode.parent.data.id ?? '') === parentId) {
-      // 如果 parentId 一致说明节点位置没有变化,直接更新
-      dictionary.value.currentData.label = data.label
-      dictionary.value.currentData.code = data.code
-    }
-    else {
-      // 先更新原有节点信息
-      const parent = dictionary.value.currentNode.parent
-      const children: Dict[] = parent.data.children || parent.data
-      const index = children.findIndex(item => item.id === data.id)
-      children[index].label = data.label
-      children[index].code = data.code
-      // 然后找到需要移动到的父节点位置,并将原有节点移动过去
-      if (parentId) {
-        const findDictionary: any = (list: Dict[], parentId: number) => {
-          for (const i in list) {
-            if (list[i].id === parentId) {
-              return list[i]
-            }
-            else if (list[i].children) {
-              const temp = findDictionary(list[i].children, parentId)
-              if (temp) {
-                return temp
-              }
-            }
-          }
-        }
-        const targetNode = findDictionary(dictionary.value.tree, parentId)
-        if (!targetNode.children) {
-          targetNode.children = []
-        }
-        targetNode.children.push(children[index])
-      }
-      else {
-        dictionary.value.tree.push(children[index])
-      }
-      // 最后删除原节点
-      children.splice(index, 1)
-    }
-  }
-}
-const dictionaryClick = (data: Dict) => {
-  pagination.value.page = 1
-  dictionaryItem.value.search.dictionaryId = data.id
+// 模型新增
+const onModuleAdd = () => {
+  ModuleRef.value.showAddModule()
 }
 
-watch(() => dictionaryItem.value.search.dictionaryId, () => {
-  getDictionaryItemList()
-})
-
-function getDictionaryItemList() {
-  dictionaryItem.value.loading = true
-  const params = getParams()
-  dictionaryItem.value.search.dictionaryId && (params.dictionaryId = dictionaryItem.value.search.dictionaryId)
-  dictionaryItem.value.search.title && (params.title = dictionaryItem.value.search.title)
+// 模型编辑
+const onModuleEdit = (data: { ModuleId: number }) => {
+  const ModuleId = data.ModuleId
+  ModuleRef.value.showEditModule(ModuleId)
 }
 
-function onChangeEnable(row: any) {
-  return new Promise<boolean>((resolve) => {
-    ElMessageBox.confirm(`确认${!row.enable ? '启用' : '禁用'}「${row.name}」吗?`, '确认信息').then(() => {
-      row.enableLoading = true
-    }).catch(() => {
-      return resolve(false)
+// 模型删除
+const onModuleDelete = (data: any) => {
+  ElMessageBox.confirm(`确认删除「${data.modTitle}」吗?`, '确认信息').then(async () => {
+    await deleteModuleById(data.modId)
+    await getModuleData()
+    ElMessage.success({
+      message: '删除成功',
+      center: true,
     })
   })
 }
 
-// 每页数量切换
-function sizeChange(size: number) {
-  onSizeChange(size).then(() => getDictionaryItemList())
+// 模型选择
+const onTreeNodeClick = (node: TreeNode) => {
+  if (node.isLeaf) {
+    state.curModuleId = node.modId
+    getApiData()
+  }
 }
 
-// 当前页码切换(翻页)
-function currentChange(page = 1) {
-  onCurrentChange(page).then(() => getDictionaryItemList())
-}
+const objectTableRef = ref()
+const apiRef = ref()
 
-// 字段排序
-function sortChange({ prop, order }: { prop: string; order: string }) {
-  onSortChange(prop, order).then(() => getDictionaryItemList())
+// 检索对象
+const onSearch = () => {
+  getApiData()
 }
 
-function onCreate() {
-  dictionaryItem.value.dialog.id = ''
-  dictionaryItem.value.dialog.visible = true
+// 新增API
+const onApiAdd = () => {
+  apiRef.value.showAddApi(state.curModuleId)
 }
 
-function onEdit(row: any) {
-  dictionaryItem.value.dialog.id = row.id
-  dictionaryItem.value.dialog.visible = true
+// 编辑API
+const onApiEdit = (data: { apiId: number; modId: number }) => {
+  const apiId = data.apiId
+  const modId = data.modId
+  apiRef.value.showEditApi(modId, apiId)
 }
 
-function onDelete(row: any) {
-  ElMessageBox.confirm(`确认删除「${row.name}」吗?`, '确认信息').then(() => {
-  }).catch(() => {})
+// 删除API
+const onApiDelete = (row: any) => {
+  const { apiId, apiTitle } = row
+  ElMessageBox.confirm(`确认删除「${apiTitle}」吗?`, '确认信息').then(async () => {
+    await deleteApiById(apiId)
+    await getApiData()
+    ElMessage.success({
+      message: '删除成功',
+      center: true,
+    })
+  })
 }
-
-function onDeleteMulti(rows: any[]) {
-  ElMessageBox.confirm(`确认删除选中的 ${rows.length} 条数据吗?`, '确认信息').then(() => {
-  }).catch(() => {})
+async function onChangeStatus(row: { modId: number;apiId: number; status: number }) {
+  await updateApiStatus(row.modId, row.apiId, row.status)
 }
+onMounted(() => {
+  getModuleData()
+})
 </script>
 
 <template>
   <div class="absolute-container">
-    <page-header title="字典管理" content="页面数据为 Mock 示例数据,非真实数据。" />
+    <tool-header title="API接口">
+      <template #right>
+        <el-button plain @click="onModuleAdd">
+          <template #icon>
+            <el-icon>
+              <svg-icon name="i-ep:plus" />
+            </el-icon>
+          </template>
+          新增API模块
+        </el-button>
+      </template>
+    </tool-header>
+
     <div class="page-main">
-      <LayoutContainer hide-left-side-toggle>
+      <LayoutContainer left-side-width="300px" hide-left-side-toggle>
         <template #leftSide>
-          <el-button-group class="btns">
-            <el-button type="primary" class="add" @click="dictionaryAdd()">
-              新增字典
-            </el-button>
-            <el-button @click="getDictionaryList">
-              <template #icon>
-                <el-icon>
-                  <svg-icon name="i-ep:refresh" />
-                </el-icon>
-              </template>
-            </el-button>
-          </el-button-group>
-          <el-input v-model="dictionary.search" placeholder="请输入关键词筛选字典" clearable class="search" />
           <el-scrollbar class="tree">
-            <ElTree ref="dictionaryRef" :data="dictionary.tree" :filter-node-method="dictionaryFilter as any" default-expand-all @node-click="dictionaryClick">
+            <ElTree ref="leftTreeRef" :data="state.Modules" default-expand-all node-key="modId" @node-click="onTreeNodeClick">
               <template #default="{ node, data }">
                 <div class="custom-tree-node">
                   <div class="label" :title="node.label">
-                    {{ node.label }}
-                  </div>
-                  <div class="code">
-                    {{ data.code }}
+                    <el-icon v-if="data.isLeaf" size="16px" style="margin-right: 5px;">
+                      <svg-icon name="uim:box" />
+                    </el-icon>
+                    <div class="text">
+                      {{ data.label }}
+                    </div>
                   </div>
-                  <div class="actions">
+                  <div v-if="data.isLeaf" class="actions">
                     <el-button-group>
-                      <el-button type="primary" plain size="default" @click.stop="dictionaryAdd(data)">
-                        <template #icon>
-                          <el-icon>
-                            <svg-icon name="i-ep:plus" />
-                          </el-icon>
-                        </template>
-                      </el-button>
-                      <el-button type="info" plain size="default" @click.stop="dictionaryEdit(node, data)">
+                      <el-button type="info" plain size="small" @click.stop="onModuleEdit(data)">
                         <template #icon>
                           <el-icon>
                             <svg-icon name="i-ep:edit" />
                           </el-icon>
                         </template>
                       </el-button>
-                      <el-button type="danger" plain size="default" @click.stop="dictionaryDelete(node, data)">
+                      <el-button type="danger" plain size="small" @click.stop="onModuleDelete(data)">
                         <template #icon>
                           <el-icon>
                             <svg-icon name="i-ep:delete" />
@@ -266,194 +200,147 @@ function onDeleteMulti(rows: any[]) {
             </ElTree>
           </el-scrollbar>
         </template>
-        <div v-show="dictionaryItem.search.dictionaryId" class="container">
-          <el-space wrap>
-            <el-button type="primary" @click="onCreate">
-              <template #icon>
-                <el-icon>
-                  <svg-icon name="i-ep:plus" />
-                </el-icon>
-              </template>
-            </el-button>
-            <el-button type="danger" :disabled="!dictionaryItem.selectionDataList.length" @click="onDeleteMulti(dictionaryItem.selectionDataList)">
-              <template #icon>
-                <el-icon>
-                  <svg-icon name="i-ep:delete" />
-                </el-icon>
-              </template>
-            </el-button>
-            <el-input v-model="dictionaryItem.search.title" placeholder="请输入关键词筛选字典项" clearable style="width: 200px;" />
-            <el-button @click="getDictionaryItemList">
-              <template #icon>
-                <el-icon>
-                  <svg-icon name="i-ep:search" />
-                </el-icon>
-              </template>
-            </el-button>
-          </el-space>
-          <el-table ref="dictionaryItemRef" v-loading="dictionaryItem.loading" :data="dictionaryItem.dataList" border stripe highlight-current-row height="100%" @sort-change="sortChange" @selection-change="dictionaryItem.selectionDataList = $event">
-            <el-table-column type="selection" align="center" fixed />
-            <el-table-column prop="name" label="名称" />
-            <el-table-column label="键值" align="center" width="150">
-              <template #default="scope">
-                <el-tag type="info">
-                  {{ scope.row.value }}
-                </el-tag>
-              </template>
-            </el-table-column>
-            <el-table-column label="状态" width="100" align="center">
-              <template #default="scope">
-                <el-switch v-model="scope.row.enable" :loading="scope.row.enableLoading" inline-prompt active-text="启用" inactive-text="禁用" :before-change="() => onChangeEnable(scope.row)" />
+
+        <div class="container">
+          <tool-bar>
+            <template #right>
+              <el-input v-model="state.search.keywords" placeholder="请输入名称,支持模糊查询" style="padding-right: 10px;" clearable />
+              <el-button type="primary" @click="onSearch">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:search" />
+                  </el-icon>
+                </template>
+              </el-button>
+
+              <el-button plain size="default" @click.stop="onApiAdd">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:plus" />
+                  </el-icon>
+                </template>
+                新增API
+              </el-button>
+            </template>
+          </tool-bar>
+
+          <el-table ref="objectTableRef" v-loading="state.loading" class="list-table" :data="state.objectList" border stripe highlight-current-row height="100%">
+            <el-table-column type="index" label="序号" header-align="center" align="center" width="70" />
+
+            <el-table-column prop="apiTitle" label="名称" header-align="center" align="center" width="200" />
+
+            <el-table-column prop="apiCode" label="代码" header-align="center" align="center" width="150" />
+            <el-table-column prop="status" label="status" header-align="center" align="center" width="150">
+              <template #default="{ row }">
+                <el-switch
+                  v-model="row.status"
+                  :active-value="0"
+                  :inactive-value="1"
+                  inline-prompt
+                  active-text="启用"
+                  inactive-text="禁用"
+                  @change="(status) => onChangeStatus(row)"
+                />
               </template>
             </el-table-column>
-            <el-table-column label="操作" width="200" align="center">
+
+            <el-table-column prop="apiDesc" label="说明" header-align="center" align="left" />
+
+            <el-table-column label="操作" header-align="center" align="center" width="200">
               <template #default="scope">
-                <el-button type="primary" size="small" plain @click="onEdit(scope.row)">
+                <el-button type="primary" size="small" plain @click="onApiEdit(scope.row)">
                   编辑
                 </el-button>
-                <el-button type="danger" size="small" plain @click="onDelete(scope.row)">
+                <el-button type="danger" size="small" plain @click="onApiDelete(scope.row)">
                   删除
                 </el-button>
               </template>
             </el-table-column>
           </el-table>
-          <el-pagination :current-page="pagination.page" :total="pagination.total" :page-size="pagination.size" :page-sizes="pagination.sizes" :layout="pagination.layout" :hide-on-single-page="false" class="pagination" background @size-change="sizeChange" @current-change="currentChange" />
-        </div>
-        <div v-show="!dictionaryItem.search.dictionaryId" class="container">
-          <div class="empty">
-            请在左侧新增或选择一个字典
-          </div>
         </div>
       </LayoutContainer>
-      <dictionaryDialog v-if="dictionary.dialog.visible" :id="dictionary.dialog.id" v-model="dictionary.dialog.visible" :parent-id="dictionary.dialog.parentId" :tree="dictionary.tree" @add-node="dictionaryAddNode" @edit-node="dictionaryEditNode" />
-      <dictionaryItemDialog v-if="dictionaryItem.dialog.visible" :id="dictionaryItem.dialog.id" v-model="dictionaryItem.dialog.visible" :dictionary-id="dictionaryItem.search.dictionaryId" :tree="dictionary.tree" @success="getDictionaryItemList" />
     </div>
+    <PSModule ref="ModuleRef" @success="getModuleData" />
+    <PSApi ref="apiRef" @success="getApiData" />
   </div>
 </template>
 
-<style lang="scss" scoped>
-.absolute-container {
-  position: absolute;
-  width: 100%;
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-
-  .page-header {
-    margin-bottom: 0;
-  }
-
-  .page-main {
-    // 让 page-main 的高度自适应
-    flex: 1;
-    overflow: auto;
-    display: flex;
-    flex-direction: column;
-
-    .flex-container {
-      position: static;
+    <style lang="scss" scoped>
+    .container {
+      height: 100%;
+      display: flex;
+      flex-direction: column;
     }
-  }
-}
-
-.flex-container {
-  :deep(.left-side) {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
 
-    .btns {
-      display: inline-flex;
-      width: 100%;
-
-      .add {
-        width: 100%;
-      }
-    }
-
-    .search {
-      margin: 15px 0;
-    }
+    .absolute-container {
+      :deep(.left-side) {
+        display: flex;
+        flex-direction: column;
+        height: 100%;
 
-    .tree {
-      flex: 1;
-      overflow-y: auto;
+        .btns {
+          display: inline-flex;
+          width: 100%;
+          margin-bottom: var(--container-padding);
 
-      .el-tree {
-        .el-tree-node__content {
-          height: 60px;
-        }
-
-        .is-current > .el-tree-node__content {
-          background-color: var(--el-color-primary-light-9);
+          .add {
+            width: 100%;
+          }
         }
 
-        .custom-tree-node {
+        .tree {
           flex: 1;
-          position: relative;
-          width: 0;
-          height: 100%;
-          display: flex;
-          flex-direction: column;
-          justify-content: center;
-
-          .label {
-            width: calc(100% - 10px);
-            color: var(--el-text-color-primary);
-
-            @include text-overflow;
-          }
-
-          .code {
-            width: calc(100% - 10px);
-            color: var(--el-text-color-placeholder);
+          overflow-y: auto;
 
-            @include text-overflow;
-          }
+          .el-tree {
+            .el-tree-node__content {
+              height: 40px;
+            }
 
-          &:hover {
-            .actions {
-              display: block;
+            .is-current > .el-tree-node__content {
+              background-color: var(--el-color-primary-light-9);
             }
-          }
 
-          .actions {
-            display: none;
-            position: absolute;
-            right: 10px;
-            top: 50%;
-            transform: translateY(-50%);
+            .custom-tree-node {
+              flex: 1;
+              position: relative;
+              width: 0;
+              height: 100%;
+              display: flex;
+              flex-direction: column;
+              justify-content: center;
+
+              .label {
+                display: inline-flex;
+                align-items: center;
+                width: calc(100% - 10px);
+                color: var(--el-text-color-primary);
+
+                .text {
+                  @include text-overflow;
+                }
+              }
+
+              &:hover {
+                .actions {
+                  display: block;
+                }
+              }
+
+              .actions {
+                display: none;
+                position: absolute;
+                right: 10px;
+                top: 50%;
+                transform: translateY(-50%);
 
-            .el-button {
-              padding: 5px 8px;
+                .el-button {
+                  padding: 5px 8px;
+                }
+              }
             }
           }
         }
       }
     }
-  }
-
-  :deep(.main) {
-    display: flex;
-    justify-content: center;
-  }
-
-  .container {
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-    width: 100%;
-    height: 100%;
-
-    .empty {
-      text-align: center;
-      font-size: 32px;
-      color: var(--el-text-color-placeholder);
-    }
-
-    .el-table {
-      margin: 15px 0;
-    }
-  }
-}
-</style>
+    </style>

+ 82 - 215
src/views/paas/log/index.vue

@@ -1,93 +1,73 @@
 <script lang="ts" setup>
-import { ElMessageBox } from 'element-plus'
-import type Node from 'element-plus/es/components/tree/src/model/node'
-import DetailForm from './components/DetailForm/index.vue'
-
-// import api from '@/api'
-
-const menuRef = ref()
-const menu = ref([])
-const form = ref({
-  visible: false,
-  id: '',
-  parentId: '',
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { deleteApiRequestById, getApiRequestByPage } from '@/api/paas/apiReqApi'
+const { pagination, getParams, onSizeChange, onCurrentChange } = usePagination()
+
+interface TreeNode {
+  label: string
+  groupId: number
+  children?: TreeNode[]
+}
+const state = reactive({
+  loading: false,
+  curGroupId: 0,
+  menu: [] as TreeNode[],
+  userList: [],
+  search: {
+    keywords: '',
+  },
 })
 
-const getMenuList = () => {
-  // api.get('pages_example/menu/list', {
-  //   baseURL: '/mock/',
-  // }).then((res: { data: never[] }) => {
-  //   menu.value = res.data
-  // })
+// 查询日志列表
+const getLogData = async () => {
+  const params = getParams()
+  params.keywords = state.search.keywords
+  state.loading = true
+  const { data } = await getApiRequestByPage(params)
+  state.loading = false
+  state.userList = data
+  state.loading = false
+}
+// 每页数量切换
+function sizeChange(size: number) {
+  onSizeChange(size).then(() => getLogData())
 }
-onMounted(() => {
-  getMenuList()
-})
 
-const menuAdd = (data: any) => {
-  form.value = {
-    visible: false,
-    id: '',
-    parentId: '',
-  }
-  nextTick(() => {
-    form.value = {
-      visible: true,
-      id: '',
-      parentId: data.id,
-    }
-  })
+// 当前页码切换(翻页)
+function currentChange(page = 1) {
+  onCurrentChange(page).then(() => getLogData())
 }
-const menuEdit = (data?: any) => {
-  form.value = {
-    visible: false,
-    id: '',
-    parentId: '',
-  }
-  nextTick(() => {
-    form.value = {
-      visible: true,
-      id: data?.id ?? '',
-      parentId: data?.parentId ?? '',
-    }
-  })
+// 查询
+const onSearch = () => {
+  getLogData()
 }
-const menuDelete = (node: Node, data: any) => {
-  ElMessageBox.confirm(`确认删除「${data.label}」吗?`, '确认信息').then(() => {
 
+// 删除日志
+const onLogDelete = async (data: { logTitle: any; logId: number }) => {
+  ElMessageBox.confirm(`确认删除「${data.logTitle}」吗?`, '确认信息').then(async () => {
+    await deleteApiRequestById(data.logId)
+    await getLogData()
+    ElMessage.success({
+      message: '删除成功',
+      center: true,
+    })
   })
 }
-const menuSubmit = () => {
-  form.value = {
-    visible: false,
-    id: '',
-    parentId: '',
-  }
-  getMenuList()
-}
+
+onMounted(() => {
+  getLogData()
+})
 </script>
 
 <template>
   <div class="absolute-container">
-    <page-header title="导航管理" content="页面数据为 Mock 示例数据,非真实数据。" />
+    <page-header title="日志管理" content="日志调用管理" />
     <div class="page-main">
       <LayoutContainer left-side-width="300px" hide-left-side-toggle>
         <template #leftSide>
-          <el-button-group class="btns">
-            <el-button type="primary" class="add" @click="menuEdit()">
-              新增主导航
-            </el-button>
-            <el-button @click="getMenuList">
-              <template #icon>
-                <el-icon>
-                  <svg-icon name="i-ep:refresh" />
-                </el-icon>
-              </template>
-            </el-button>
-          </el-button-group>
           <el-scrollbar class="tree">
-            <ElTree ref="menuRef" :data="menu" default-expand-all>
-              <template #default="{ node, data }">
+            <ElTree ref="menuRef" :data="state.menu" default-expand-all>
+              <template #default="{ data }">
                 <div class="custom-tree-node">
                   <div class="label" :title="data.meta.title">
                     <el-icon v-if="data.meta.icon" size="16px" style="margin-right: 5px;">
@@ -97,158 +77,45 @@ const menuSubmit = () => {
                       {{ data.meta.title }}
                     </div>
                   </div>
-                  <div class="actions">
-                    <el-button-group>
-                      <el-button type="primary" plain size="small" @click.stop="menuAdd(data)">
-                        <template #icon>
-                          <el-icon>
-                            <svg-icon name="i-ep:plus" />
-                          </el-icon>
-                        </template>
-                      </el-button>
-                      <el-button type="info" plain size="small" @click.stop="menuEdit(data)">
-                        <template #icon>
-                          <el-icon>
-                            <svg-icon name="i-ep:edit" />
-                          </el-icon>
-                        </template>
-                      </el-button>
-                      <el-button type="danger" plain size="small" @click.stop="menuDelete(node, data)">
-                        <template #icon>
-                          <el-icon>
-                            <svg-icon name="i-ep:delete" />
-                          </el-icon>
-                        </template>
-                      </el-button>
-                    </el-button-group>
-                  </div>
                 </div>
               </template>
             </ElTree>
           </el-scrollbar>
         </template>
-        <!-- <DetailForm v-if="form.visible" :id="form.id" :parent-id="form.parentId" @on-submit="menuSubmit" />
-        <div v-else class="empty">
-          请在左侧新增或编辑一个导航
-        </div> -->
+        <div class="container">
+          <tool-bar>
+            <template #right>
+              <el-input v-model="state.search.keywords" placeholder="请输入名称,支持模糊查询" style="padding-right: 10px;" clearable />
+              <el-button type="primary" @click="onSearch">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:search" />
+                  </el-icon>
+                </template>
+              </el-button>
+            </template>
+          </tool-bar>
+
+          <el-table ref="accountTableRef" v-loading="state.loading" class="list-table" :data="state.userList" border stripe highlight-current-row>
+            <el-table-column type="index" align="center" label="序号" width="60" />
+            <el-table-column align="center" width="100" label="接口名称" prop="ip" />
+            <el-table-column align="center" label="调用者IP" prop="ip" />
+            <el-table-column align="center" label="创建时间" prop="createTime" />
+            <el-table-column align="center" label="调用次数" prop="time" />
+            <el-table-column label="操作" header-align="center" align="center" width="220">
+              <template #default="scope">
+                <el-button v-if="scope.row.isFixed !== 1" size="small" type="danger" @click="onLogDelete(scope.row)">
+                  删除
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <el-pagination :current-page="pagination.page" :total="pagination.total" :page-size="pagination.size" :page-sizes="pagination.sizes" :layout="pagination.layout" :hide-on-single-page="false" class="pagination" background @current-change="currentChange" @size-change="sizeChange" />
+        </div>
       </LayoutContainer>
     </div>
   </div>
 </template>
 
 <style lang="scss" scoped>
-.absolute-container {
-  position: absolute;
-  width: 100%;
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-
-  .page-header {
-    margin-bottom: 0;
-  }
-
-  .page-main {
-    flex: 1;
-    overflow: auto;
-    display: flex;
-    flex-direction: column;
-
-    .flex-container {
-      position: static;
-    }
-  }
-}
-
-.flex-container {
-  :deep(.left-side) {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-
-    .btns {
-      display: inline-flex;
-      width: 100%;
-      margin-bottom: var(--container-padding);
-
-      .add {
-        width: 100%;
-      }
-    }
-
-    .tree {
-      flex: 1;
-      overflow-y: auto;
-
-      .el-tree {
-        .el-tree-node__content {
-          height: 40px;
-        }
-
-        .is-current > .el-tree-node__content {
-          background-color: var(--el-color-primary-light-9);
-        }
-
-        .custom-tree-node {
-          flex: 1;
-          position: relative;
-          width: 0;
-          height: 100%;
-          display: flex;
-          flex-direction: column;
-          justify-content: center;
-
-          .label {
-            display: inline-flex;
-            align-items: center;
-            width: calc(100% - 10px);
-            color: var(--el-text-color-primary);
-
-            .text {
-              @include text-overflow;
-            }
-          }
-
-          &:hover {
-            .actions {
-              display: block;
-            }
-          }
-
-          .actions {
-            display: none;
-            position: absolute;
-            right: 10px;
-            top: 50%;
-            transform: translateY(-50%);
-
-            .el-button {
-              padding: 5px 8px;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  :deep(.main) {
-    .main-container {
-      display: flex;
-      flex-direction: column;
-      justify-content: center;
-      width: 100%;
-      height: 100%;
-      padding: 0;
-    }
-  }
-
-  .empty {
-    flex: 1;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    font-size: 32px;
-    color: var(--el-text-color-placeholder);
-  }
-}
 </style>