diff --git a/src/api/system/dept.ts b/src/api/system/dept.ts
index 54ac5e5..6cfa90e 100644
--- a/src/api/system/dept.ts
+++ b/src/api/system/dept.ts
@@ -47,13 +47,4 @@ export function deptDelete(deptId) {
     url: '/dept/delete/'+deptId,
     method: 'DELETE',
   });
-}
-/**
- * @description: 批量删除部门
- */
-export function deptBatchDelete(deptId) {
-  return http.request({
-    url: '/dept/batchDelete/'+deptId,
-    method: 'DELETE',
-  });
 }
\ No newline at end of file
diff --git a/src/api/system/operLog.ts b/src/api/system/operLog.ts
new file mode 100644
index 0000000..280ad8a
--- /dev/null
+++ b/src/api/system/operLog.ts
@@ -0,0 +1,40 @@
+import { http } from '@/utils/http/axios';
+
+/**
+ * @description: 操作日志列表
+ */
+export function getOperLogList(params?) {
+  return http.request({
+    url: '/oper/log/page',
+    method: 'GET',
+    params,
+  });
+}
+/**
+ * @description: 根据ID获取详情
+ */
+export function getOperLogDetail(id) {
+  return http.request({
+    url: '/oper/log/detail/'+id,
+    method: 'get',
+  });
+}
+/**
+ * @description: 删除操作日志
+ */
+export function operLogDelete(id) {
+  return http.request({
+    url: '/oper/log/delete/'+id,
+    method: 'DELETE',
+  });
+}
+/**
+ * @description: 批量删除操作日志
+ */
+export function operLogBatchDelete(data:any) {
+  return http.request({
+    url: '/oper/log/batchDelete',
+    method: 'DELETE',
+    data
+  });
+}
\ No newline at end of file
diff --git a/src/views/system/operLog/columns.ts b/src/views/system/operLog/columns.ts
new file mode 100644
index 0000000..15a3e05
--- /dev/null
+++ b/src/views/system/operLog/columns.ts
@@ -0,0 +1,71 @@
+import { h } from 'vue';
+import { ElTag } from 'element-plus';
+
+export const columns = [
+  {
+    type: 'selection',
+  },
+  {
+    label: '日志标题',
+    prop: 'title',
+  },
+  {
+    label: '操作用户',
+    prop: 'createUser',
+  },
+  {
+    label: '请求ip',
+    prop: 'ip',
+  },
+  {
+    label: '浏览器',
+    prop: 'browser',
+  },
+  {
+    label: '操作系统',
+    prop: 'os',
+  },
+  {
+    label: '操作类型',
+    prop: 'typeText',
+  },
+  {
+    label: '操作来源',
+    prop: 'sourceText',
+  },
+  {
+    label: '请求方式',
+    prop: 'requestMethod',
+  },
+  {
+    label: '请求URL',
+    prop: 'url',
+  },
+  {
+    label: '请求地区',
+    prop: 'location',
+  },
+  {
+    label: '请求耗时',
+    prop: 'consumeTime',
+  },
+  {
+    label: '状态',
+    prop: 'status',
+    render(record) {
+      return h(
+        ElTag,
+        {
+          type: record.row.status ==0 ? 'success' : 'danger',
+        },
+        {
+          default: () => (record.row.status ==0 ? '正常' : '异常'),
+        },
+      );
+    },
+  },
+  {
+    label: '创建时间',
+    prop: 'createTime',
+  },
+];
diff --git a/src/views/system/operLog/edit.vue b/src/views/system/operLog/edit.vue
new file mode 100644
index 0000000..051e949
--- /dev/null
+++ b/src/views/system/operLog/edit.vue
@@ -0,0 +1,112 @@
+<template>
+  <el-dialog
+      v-model="props.visible"
+      :title="props.positionId?'编辑':'新增'"
+      :append-to-body="true"
+      width="500"
+      :close-on-click-modal="false"
+      :before-close="dialogClose"
+  >
+    <el-form
+        class="ls-form"
+        ref="formRef"
+        :model="formData"
+        label-width="80px"
+    >
+      <el-form-item
+          label="名称"
+          prop="name"
+          :rules="{ required: true, message: '请输入名称', trigger: 'blur' }"
+      >
+        <el-input
+            class="ls-input"
+            v-model="formData.name"
+            placeholder="请输入名称"
+            clearable
+        />
+      </el-form-item>
+      <el-form-item label="岗位状态" prop="status">
+          <el-radio-group v-model="formData.status" name="status">
+            <el-radio :value="1">正常</el-radio>
+            <el-radio :value="2">禁用</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      <el-form-item label="排序" prop="sort">
+        <el-input-number v-model="formData.sort"/>
+      </el-form-item>
+      <el-form-item label="备注" prop="note">
+          <el-input v-model="formData.note" type="textarea" placeholder="请输入备注" clearable />
+        </el-form-item>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="dialogClose">取消</el-button>
+        <el-button :loading="subLoading" type="primary" @click="submit">
+          确定
+        </el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+<script lang="ts" setup>
+import type {FormInstance} from "element-plus";
+import {getPositionDetail,positionAdd,positionUpdate} from "@/api/system/position";
+import {onMounted, reactive, shallowRef} from "vue";
+import {message} from "@/utils/auth";
+import {useLockFn} from "@/utils/useLockFn";
+
+const emit = defineEmits(["success", "update:visible"]);
+const formRef = shallowRef<FormInstance>();
+const formData = reactive({
+  id: "",
+  name: "",
+  status: 1,
+  sort: 0,
+  note:'',
+});
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false
+  },
+  positionId: {
+    type: Number,
+    required: true,
+    default: 0
+  }
+});
+
+const handleSubmit = async () => {
+    await formRef.value?.validate();
+    props.positionId ? await positionUpdate(formData) : await positionAdd(formData);
+    message("操作成功");
+    emit("update:visible", false);
+    emit("success");
+};
+
+const dialogClose = () => {
+  emit("update:visible", false);
+};
+
+const { isLock:subLoading,lockFn: submit } = useLockFn(handleSubmit);
+
+
+const setFormData = async () => {
+  const data = await getPositionDetail(props.positionId);
+  for (const key in formData) {
+    if (data[key] != null && data[key] != undefined) {
+      //@ts-ignore
+      formData[key] = data[key];
+    }
+  }
+};
+
+onMounted(() => {
+  if (props.positionId) {
+    setFormData();
+  }
+});
+
+</script>
diff --git a/src/views/system/operLog/index.vue b/src/views/system/operLog/index.vue
new file mode 100644
index 0000000..9c25428
--- /dev/null
+++ b/src/views/system/operLog/index.vue
@@ -0,0 +1,147 @@
+<template>
+  <PageWrapper>
+    <el-card :bordered="false" class="pt-3 mb-3 proCard">
+      <BasicForm @register="register" @submit="handleSubmit" @reset="handleReset"></BasicForm>
+    </el-card>
+    <el-card :bordered="false" class="proCard">
+      <BasicTable
+        :columns="columns"
+        :request="loadDataTable"
+        :row-key="(row) => row.id"
+        ref="tableRef"
+        :actionColumn="actionColumn"
+         @selection-change="onSelectionChange"
+      >
+        <template #tableTitle>
+          <el-button type="danger" @click="handleDelete()" :disabled="!selectionData.length">
+              <template #icon>
+                <el-icon class="el-input__icon">
+                  <Delete />
+                </el-icon>
+              </template>
+              删除
+            </el-button>
+        </template>
+
+        <template #action>
+          <TableAction />
+        </template>
+      </BasicTable>
+    </el-card>
+
+    <editDialog
+        v-if="editVisible"
+        :positionId="positionId"
+        v-model:visible="editVisible"
+        @success="reloadTable"
+    >
+    </editDialog>
+  </PageWrapper>
+</template>
+
+<script lang="ts" setup>
+  import { reactive, ref, h,nextTick,defineAsyncComponent } from 'vue';
+  import { ColProps } from 'element-plus';
+  import { schemas } from './querySchemas';
+  import { BasicForm, useForm } from '@/components/Form/index';
+  import { BasicTable, TableAction } from '@/components/Table';
+  import { getOperLogList,operLogDelete,operLogBatchDelete } from '@/api/system/operLog';
+  import { columns } from './columns';
+  import { PlusOutlined } from '@vicons/antd';
+  import {message,confirm} from "@/utils/auth";
+  const editDialog = defineAsyncComponent(() =>
+    import('./edit.vue')
+)
+const positionId =ref(0)
+const editVisible=ref(false)
+  const selectionData = ref([])
+  const tableRef = ref();
+  const formParams = reactive({
+    type:'',
+    status:''
+  });
+  const actionColumn = reactive({
+    width: 250,
+    label: '操作',
+    prop: 'action',
+    fixed: 'right',
+    render(record) {
+      return h(TableAction, {
+        style: 'button',
+        actions: [
+          // {
+          //   label: '编辑',
+          //   type: 'warning',
+          //   onClick: handleEdit.bind(null, record),
+          //   ifShow: () => {
+          //     return true;
+          //   },
+          //   auth: ['basic_list'],
+          // },
+          {
+            label: '删除',
+            type: 'danger',
+            onClick: handleDelete.bind(null, record),
+            // 根据业务控制是否显示 isShow 和 auth 是并且关系
+            ifShow: () => {
+              return true;
+            },
+            // 根据权限控制是否显示: 有权限,会显示,支持多个
+            // auth: ['basic_list'],
+          },
+        ],
+      });
+    },
+  });
+
+  const loadDataTable = async (res: any) => {
+    const result = await getOperLogList({ ...formParams, ...res });
+    return result;
+  };
+
+  function reloadTable() {
+    tableRef.value.reload({pageNo:1});
+  }
+  const [register, {}] = useForm({
+    labelWidth: 80,
+    layout: 'horizontal',
+    colProps: { span: 6 } as ColProps,
+    submitOnReset:true,
+    schemas
+  });
+  function handleSubmit(values: Recordable) {
+    for (const key in formParams) {
+      if (values[key] != null && values[key] != undefined) {
+        formParams[key] = values[key];
+      }
+    }
+    reloadTable();
+  }
+
+  function handleReset() {
+    for (const key in formParams) {
+        formParams[key] ='';
+    }
+  }
+const handleEdit = async (record: Recordable) => {
+  positionId.value=record.row.id
+  await nextTick();
+  editVisible.value=true
+};
+
+async function handleDelete(record: Recordable) {
+    let ids = []
+    if(!record){
+      ids = selectionData.value.map(({id}) => id);
+    }
+    await confirm('确定要删除?');
+    record? await operLogDelete(record.row.id):await operLogBatchDelete(ids);
+    message("删除成功");
+    reloadTable()
+  }
+function onSelectionChange(value){
+    selectionData.value = value
+  }
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/system/operLog/querySchemas.ts b/src/views/system/operLog/querySchemas.ts
new file mode 100644
index 0000000..d362940
--- /dev/null
+++ b/src/views/system/operLog/querySchemas.ts
@@ -0,0 +1,39 @@
+import { FormSchema } from '@/components/Form/index';
+export const schemas: FormSchema[] = [
+  {
+    field: 'type',
+    component: 'Select',
+    label: '请求类型',
+    componentProps: {
+      placeholder: '请选择请求类型',
+      options: [
+        {
+          label: '登录',
+          value: '0',
+        },
+        {
+          label: '退出',
+          value: '1',
+        },
+      ],
+    },
+  },
+  {
+    field: 'status',
+    component: 'Select',
+    label: '请求状态',
+    componentProps: {
+      placeholder: '请选择状态',
+      options: [
+        {
+          label: '正常',
+          value: '0',
+        },
+        {
+          label: '异常',
+          value: '1',
+        },
+      ],
+    },
+  },
+];
diff --git a/src/views/system/position/index.vue b/src/views/system/position/index.vue
index 9e6ab83..e018db0 100644
--- a/src/views/system/position/index.vue
+++ b/src/views/system/position/index.vue
@@ -62,7 +62,7 @@
 )
 const positionId =ref(0)
 const editVisible=ref(false)
-  const selectionData = ref([])
+const selectionData = ref([])
   const tableRef = ref();
   const formParams = reactive({
     name:'',