主界面+系统管理

This commit is contained in:
Lxq
2025-12-18 15:15:45 +08:00
parent 26a6152ecc
commit b60a50dca5
6 changed files with 383 additions and 200 deletions

View File

@@ -1 +1 @@
VITE_API_URL=http://192.168.110.204:9090 VITE_API_URL=http://223.71.122.54:9090

View File

@@ -1 +1 @@
VITE_API_URL=http://192.168.110.204:9090 VITE_API_URL=http://223.71.122.54:9090

View File

@@ -25,7 +25,7 @@ export function positionupd(data: any) {
export function positionlist(data: any) { export function positionlist(data: any) {
return request({ return request({
url: '/position/listPpage', url: '/position/listPage',
method: 'get', method: 'get',
params: data, params: data,
}) })

View File

@@ -5,6 +5,10 @@ const router = createRouter({
routes: [ routes: [
{ {
path: '/', path: '/',
component: () => import('../views/Layout.vue'),
children: [
{
path: '/user',
name: 'user', name: 'user',
component: () => import('../views/user/index.vue'), component: () => import('../views/user/index.vue'),
}, },
@@ -29,6 +33,8 @@ const router = createRouter({
component: () => import('../views/manage-log/index.vue'), component: () => import('../views/manage-log/index.vue'),
}, },
], ],
},
],
}) })
// 添加全局路由守卫 // 添加全局路由守卫

View File

@@ -0,0 +1,191 @@
<template>
<div class="layout-container">
<!-- 顶部导航栏 -->
<el-header class="layout-header">
<div class="header-left">
<h1 class="project-title">北京融创智能仪器管理系统</h1>
</div>
<div class="header-right">
<el-dropdown @command="handleCommand">
<span class="user-info">
<el-icon><User /></el-icon>
<span class="username">{{ userInfo.username || '管理员' }}</span>
<el-icon class="el-icon--right"><CaretBottom /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<!-- 主体区域 -->
<el-container class="layout-body">
<!-- 左侧菜单 -->
<el-aside width="200px" class="layout-aside">
<el-menu
:default-active="activeMenu"
class="sidebar-menu"
router
:collapse="false"
>
<el-sub-menu index="system">
<template #title>
<el-icon><Setting /></el-icon>
<span>系统管理</span>
</template>
<el-menu-item index="/user">
<el-icon><User /></el-icon>
<span>用户管理</span>
</el-menu-item>
<el-menu-item index="/role">
<el-icon><Avatar /></el-icon>
<span>角色管理</span>
</el-menu-item>
<el-menu-item index="/department">
<el-icon><OfficeBuilding /></el-icon>
<span>部门管理</span>
</el-menu-item>
<el-menu-item index="/position">
<el-icon><Briefcase /></el-icon>
<span>职位管理</span>
</el-menu-item>
<el-menu-item index="/manage-log">
<el-icon><Document /></el-icon>
<span>操作日志管理</span>
</el-menu-item>
</el-sub-menu>
</el-menu>
</el-aside>
<!-- 右侧内容区 -->
<el-main class="layout-main">
<RouterView />
</el-main>
</el-container>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { User, Setting, Avatar, OfficeBuilding, Briefcase, Document, CaretBottom } from '@element-plus/icons-vue'
import { useAuthStore } from '@/stores/auth'
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
// 用户信息可以从store或localStorage获取
const userInfo = ref({
username: localStorage.getItem('username') || '管理员',
})
// 当前激活的菜单
const activeMenu = computed(() => {
return route.path
})
// 处理下拉菜单命令
const handleCommand = (command: string) => {
if (command === 'logout') {
ElMessageBox.confirm('确认退出登录吗?', '提示', {
type: 'warning',
})
.then(() => {
// 清除token和用户信息
authStore.removeToken()
localStorage.removeItem('username')
ElMessage.success('退出成功')
// 跳转到登录页(如果有登录页)
// router.push('/login')
})
.catch(() => {})
}
}
</script>
<style scoped>
.layout-container {
height: 100vh;
display: flex;
flex-direction: column;
}
.layout-header {
height: 60px;
background-color: #409eff;
color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
}
.header-left {
display: flex;
align-items: center;
}
.project-title {
margin: 0;
font-size: 20px;
font-weight: 500;
color: #fff;
}
.header-right {
display: flex;
align-items: center;
}
.user-info {
display: flex;
align-items: center;
cursor: pointer;
color: #fff;
padding: 5px 10px;
border-radius: 4px;
transition: background-color 0.3s;
}
.user-info:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.user-info .el-icon {
margin-right: 5px;
}
.username {
margin: 0 8px;
}
.layout-body {
flex: 1;
display: flex;
overflow: hidden;
}
.layout-aside {
background-color: #fff;
border-right: 1px solid #e6e6e6;
overflow-y: auto;
}
.sidebar-menu {
border-right: none;
height: 100%;
}
.layout-main {
padding: 20px;
background-color: #f5f5f5;
overflow-y: auto;
}
</style>

View File

@@ -1,20 +1,28 @@
<template> <template>
<div class="log-page"> <div class="manage-log-page">
<el-card class="search-card" shadow="never"> <el-card class="search-card" shadow="never">
<div class="search-bar"> <div class="search-bar">
<el-form :inline="true" :model="queryForm" label-width="80px"> <el-form :inline="true" :model="queryForm" label-width="90px">
<el-form-item label="操作人"> <el-form-item label="日志名称">
<el-input <el-input
v-model="queryForm.operator" v-model="queryForm.logName"
placeholder="输入操作人" placeholder="输入日志名称"
clearable clearable
style="width: 200px" style="width: 200px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="操作类型"> <el-form-item label="日志类型">
<el-input <el-input
v-model="queryForm.operationType" v-model="queryForm.logType"
placeholder="输入操作类型" placeholder="输入日志类型"
clearable
style="width: 200px"
/>
</el-form-item>
<el-form-item label="创建人ID">
<el-input
v-model="queryForm.createId"
placeholder="输入创建人ID"
clearable clearable
style="width: 200px" style="width: 200px"
/> />
@@ -38,51 +46,16 @@
style="width: 100%" style="width: 100%"
v-loading="loading" v-loading="loading"
> >
<el-table-column type="index" label="序号" width="70" /> <el-table-column type="index" label="序号" width="80" />
<el-table-column <el-table-column prop="logName" label="日志名称" min-width="150" />
prop="operator" <el-table-column prop="logType" label="日志类型" min-width="120" />
label="操作人" <el-table-column prop="logContent" label="日志内容" min-width="200" show-overflow-tooltip />
min-width="120" <el-table-column prop="logWritetime" label="日志记录时间" min-width="180">
:formatter="formatCell" <template #default="scope">
/> {{ formatDateTime(scope.row.logWritetime) }}
<el-table-column </template>
prop="operationType" </el-table-column>
label="操作类型" <el-table-column prop="remark" label="备注" min-width="200" show-overflow-tooltip />
min-width="120"
:formatter="formatCell"
/>
<el-table-column
prop="operationContent"
label="操作内容"
min-width="200"
:formatter="formatCell"
show-overflow-tooltip
/>
<el-table-column
prop="module"
label="操作模块"
min-width="120"
:formatter="formatCell"
/>
<el-table-column
prop="ipAddress"
label="IP地址"
min-width="140"
:formatter="formatCell"
/>
<el-table-column
prop="operationTime"
label="操作时间"
width="180"
:formatter="formatCell"
/>
<el-table-column
prop="remark"
label="备注"
min-width="150"
:formatter="formatCell"
show-overflow-tooltip
/>
<el-table-column label="操作" width="180" fixed="right"> <el-table-column label="操作" width="180" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button type="primary" link @click="openDrawer('edit', scope.row)">编辑</el-button> <el-button type="primary" link @click="openDrawer('edit', scope.row)">编辑</el-button>
@@ -108,48 +81,42 @@
v-model="drawerVisible" v-model="drawerVisible"
:title="drawerTitle" :title="drawerTitle"
direction="rtl" direction="rtl"
:size="520" :size="500"
> >
<el-form <el-form
ref="formRef" ref="formRef"
:model="form" :model="form"
:rules="rules" :rules="rules"
label-width="100px" label-width="120px"
class="drawer-form" class="drawer-form"
> >
<el-form-item v-if="isEdit" label="日志ID"> <el-form-item v-if="isEdit" label="日志ID">
<el-input v-model="form.id" disabled /> <el-input v-model="form.id" disabled />
</el-form-item> </el-form-item>
<el-form-item label="操作人" prop="operator"> <el-form-item label="日志名称" prop="logName">
<el-input v-model="form.operator" placeholder="请输入操作人" /> <el-input v-model="form.logName" placeholder="请输入日志名称" />
</el-form-item> </el-form-item>
<el-form-item label="操作类型" prop="operationType"> <el-form-item label="日志类型" prop="logType">
<el-input v-model="form.operationType" placeholder="请输入操作类型" /> <el-input v-model="form.logType" placeholder="请输入日志类型" />
</el-form-item> </el-form-item>
<el-form-item label="操作内容" prop="operationContent"> <el-form-item label="日志内容" prop="logContent">
<el-input <el-input
v-model="form.operationContent" v-model="form.logContent"
type="textarea" type="textarea"
placeholder="请输入操作内容" placeholder="请输入日志内容"
:rows="3" :rows="4"
maxlength="500" maxlength="500"
show-word-limit show-word-limit
/> />
</el-form-item> </el-form-item>
<el-form-item label="操作模块" prop="module"> <el-form-item label="日志记录时间" prop="logWritetime">
<el-input v-model="form.module" placeholder="请输入操作模块" />
</el-form-item>
<el-form-item label="IP地址" prop="ipAddress">
<el-input v-model="form.ipAddress" placeholder="请输入IP地址" />
</el-form-item>
<el-form-item label="操作时间" prop="operationTime">
<el-date-picker <el-date-picker
v-model="form.operationTime" v-model="form.logWritetime"
type="datetime" type="datetime"
placeholder="选择操作时间" placeholder="选择日志记录时间"
style="width: 100%"
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DDTHH:mm:ss.SSSZ"
style="width: 100%"
/> />
</el-form-item> </el-form-item>
<el-form-item label="备注"> <el-form-item label="备注">
@@ -188,17 +155,16 @@ import {
} from '@/api/manage-log' } from '@/api/manage-log'
interface LogItem { interface LogItem {
id?: number | string id?: string | number
operator?: string logName: string
operationType?: string logType: string
operationContent?: string logContent: string
module?: string logWritetime?: string
ipAddress?: string
operationTime?: string
remark?: string remark?: string
remarks?: string createId?: string
createTime?: string createTime?: string
updateTime?: string updateTime?: string
updateId?: string
} }
const loading = ref(false) const loading = ref(false)
@@ -207,8 +173,9 @@ const tableData = ref<LogItem[]>([])
const total = ref(0) const total = ref(0)
const queryForm = reactive({ const queryForm = reactive({
operator: '', logName: '',
operationType: '', logType: '',
createId: '',
}) })
const pagination = reactive({ const pagination = reactive({
@@ -223,102 +190,84 @@ const isEdit = ref(false)
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
const form = reactive<LogItem>({ const form = reactive<LogItem>({
id: undefined, id: undefined,
operator: '', logName: '',
operationType: '', logType: '',
operationContent: '', logContent: '',
module: '', logWritetime: '',
ipAddress: '',
operationTime: '',
remark: '', remark: '',
}) })
const rules: FormRules = { const rules: FormRules = {
operator: [{ required: true, message: '请输入操作人', trigger: 'blur' }], logName: [{ required: true, message: '请输入日志名称', trigger: 'blur' }],
operationType: [{ required: true, message: '请输入操作类型', trigger: 'blur' }], logType: [{ required: true, message: '请输入日志类型', trigger: 'blur' }],
operationContent: [{ required: true, message: '请输入操作内容', trigger: 'blur' }], logContent: [{ required: true, message: '请输入日志内容', trigger: 'blur' }],
logWritetime: [{ required: true, message: '请选择日志记录时间', trigger: 'change' }],
}
const formatDateTime = (dateTime?: string) => {
if (!dateTime) return '-'
try {
const date = new Date(dateTime)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
} catch {
return dateTime
}
} }
const resetForm = () => { const resetForm = () => {
form.id = undefined form.id = undefined
form.operator = '' form.logName = ''
form.operationType = '' form.logType = ''
form.operationContent = '' form.logContent = ''
form.module = '' form.logWritetime = ''
form.ipAddress = ''
form.operationTime = ''
form.remark = '' form.remark = ''
formRef.value?.clearValidate() formRef.value?.clearValidate()
} }
const formatCell = (_row: any, _column: any, value: any) => {
if (value === 0) return 0
return value === undefined || value === null || value === '' ? '暂无' : value
}
const loadList = async () => { const loadList = async () => {
loading.value = true loading.value = true
try { try {
// 构建查询参数,过滤空字符串
const params: any = { const params: any = {
pageNum: pagination.pageNum, pageNum: pagination.pageNum,
pageSize: pagination.pageSize, pageSize: pagination.pageSize,
} }
if (queryForm.operator && queryForm.operator.trim()) {
params.operator = queryForm.operator.trim() // 只添加有值的查询参数
} if (queryForm.logName) params.logName = queryForm.logName
if (queryForm.operationType && queryForm.operationType.trim()) { if (queryForm.logType) params.logType = queryForm.logType
params.operationType = queryForm.operationType.trim() if (queryForm.createId) params.createId = queryForm.createId
}
const res: any = await manageloglist(params) const res: any = await manageloglist(params)
const data = res?.data ?? res ?? {}
console.log('操作日志列表API返回数据:', res) // 兼容多种返回格式:{data:{records,total}} | {records,total} | {data:[]} | []
// 根据返回格式处理数据
let pageData = res?.data
// 如果res.data不存在可能是直接返回了分页数据
if (!pageData && (res?.records || res?.total !== undefined)) {
pageData = res
}
// 兼容多种返回格式
const recordsFromData = const recordsFromData =
pageData?.records || data.records ||
pageData?.list || data.list ||
pageData?.rows || data.rows ||
pageData?.items || data.items ||
(Array.isArray(pageData?.data) ? pageData.data : undefined) (Array.isArray(data.data) ? data.data : undefined)
const records = Array.isArray(recordsFromData) const records = Array.isArray(recordsFromData)
? recordsFromData ? recordsFromData
: Array.isArray(pageData) : Array.isArray(data)
? pageData ? data
: Array.isArray(res?.data)
? res.data
: [] : []
const totalValue = const totalValue =
pageData?.total ?? data.total ?? data.count ?? data.totalCount ?? records.length ?? 0
pageData?.count ??
pageData?.totalCount ??
res?.total ??
(Array.isArray(records) ? records.length : 0)
tableData.value = records tableData.value = records
total.value = Number(totalValue) || 0 total.value = Number(totalValue) || 0
} catch (error) {
console.log('解析后的数据:', {
records: records.length,
total: total.value,
firstRecord: records[0]
})
} catch (error: any) {
console.error('load log list error', error) console.error('load log list error', error)
const errorMsg = error?.message || error?.msg || '加载操作日志列表失败' ElMessage.error('获取日志列表失败')
ElMessage.error(errorMsg)
tableData.value = []
total.value = 0
} finally { } finally {
loading.value = false loading.value = false
} }
@@ -330,8 +279,9 @@ const handleSearch = () => {
} }
const resetSearch = () => { const resetSearch = () => {
queryForm.operator = '' queryForm.logName = ''
queryForm.operationType = '' queryForm.logType = ''
queryForm.createId = ''
handleSearch() handleSearch()
} }
@@ -356,18 +306,11 @@ const openDrawer = async (mode: 'create' | 'edit', row?: LogItem) => {
const res: any = await managelogbyid(row.id) const res: any = await managelogbyid(row.id)
const data = res?.data || res || {} const data = res?.data || res || {}
form.id = data.id ?? row.id form.id = data.id ?? row.id
form.operator = data.operator ?? row.operator ?? '' form.logName = data.logName ?? row.logName ?? ''
form.operationType = data.operationType ?? row.operationType ?? '' form.logType = data.logType ?? row.logType ?? ''
form.operationContent = data.operationContent ?? row.operationContent ?? '' form.logContent = data.logContent ?? row.logContent ?? ''
form.module = data.module ?? row.module ?? '' form.logWritetime = data.logWritetime ?? row.logWritetime ?? ''
form.ipAddress = data.ipAddress ?? row.ipAddress ?? '' form.remark = data.remark ?? row.remark ?? ''
form.operationTime = data.operationTime ?? row.operationTime ?? ''
form.remark =
data.remark ??
data.remarks ??
row.remark ??
(row as any)?.remarks ??
''
} catch (error) { } catch (error) {
ElMessage.error('获取日志详情失败') ElMessage.error('获取日志详情失败')
drawerVisible.value = false drawerVisible.value = false
@@ -375,28 +318,67 @@ const openDrawer = async (mode: 'create' | 'edit', row?: LogItem) => {
} }
} }
// 将时间格式转换为ISO 8601格式
const formatToISO = (dateTime?: string) => {
if (!dateTime) return new Date().toISOString()
try {
// 如果已经是ISO格式直接返回
if (dateTime.includes('T') && dateTime.includes('Z')) {
return dateTime
}
// 如果是其他格式转换为ISO格式
const date = new Date(dateTime)
return date.toISOString()
} catch {
return new Date().toISOString()
}
}
const submitForm = () => { const submitForm = () => {
formRef.value?.validate(async (valid) => { formRef.value?.validate(async (valid) => {
if (!valid) return if (!valid) return
submitLoading.value = true submitLoading.value = true
try { try {
const payload: any = { // 获取当前时间ISO 8601格式
...form, const now = new Date().toISOString()
remarks: form.remark,
// 获取用户ID可以从localStorage或其他地方获取这里先设为0
const userId = 0 // 可以根据实际情况从用户store或localStorage获取
// 准备提交数据按照后端Swagger要求补充所有字段
const submitData: any = {
logName: form.logName,
logType: form.logType,
logContent: form.logContent || '',
remark: form.remark || '',
logWritetime: formatToISO(form.logWritetime),
createId: userId,
updateId: userId,
createTime: now,
updateTime: now,
delSign: false, // 新增时delSign为false
}
// 如果是编辑需要包含id
if (isEdit.value && form.id) {
submitData.id = Number(form.id)
} else {
// 新增时id设为0
submitData.id = 0
} }
if (isEdit.value) { if (isEdit.value) {
await managelogupd(payload) await managelogupd(submitData)
ElMessage.success('更新成功') ElMessage.success('更新成功')
} else { } else {
await managelogadd(payload) await managelogadd(submitData)
ElMessage.success('新增成功') ElMessage.success('新增成功')
} }
drawerVisible.value = false drawerVisible.value = false
loadList() loadList()
} catch (error: any) { } catch (error: any) {
console.error('submit log error', error) console.error('submit log error', error)
const errorMsg = error?.message || error?.msg || (isEdit.value ? '更新失败' : '新增失败') const errorMsg = error?.response?.data?.message || error?.message || (isEdit.value ? '更新失败' : '新增失败')
ElMessage.error(errorMsg) ElMessage.error(errorMsg)
} finally { } finally {
submitLoading.value = false submitLoading.value = false
@@ -409,7 +391,7 @@ const handleDelete = (row: LogItem) => {
ElMessage.warning('缺少日志ID') ElMessage.warning('缺少日志ID')
return return
} }
ElMessageBox.confirm(`确认删除操作日志吗?`, '提示', { ElMessageBox.confirm(`确认删除日志${row.logName}吗?`, '提示', {
type: 'warning', type: 'warning',
}) })
.then(async () => { .then(async () => {
@@ -421,10 +403,9 @@ const handleDelete = (row: LogItem) => {
await managelogdel(row.id) await managelogdel(row.id)
ElMessage.success('删除成功') ElMessage.success('删除成功')
loadList() loadList()
} catch (error: any) { } catch (error) {
console.error('delete log error', error) console.error('delete log error', error)
const errorMsg = error?.message || error?.msg || '删除失败' ElMessage.error('删除失败')
ElMessage.error(errorMsg)
} }
}) })
.catch(() => {}) .catch(() => {})
@@ -436,7 +417,7 @@ onMounted(() => {
</script> </script>
<style scoped> <style scoped>
.log-page { .manage-log-page {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
@@ -449,7 +430,7 @@ onMounted(() => {
.search-bar { .search-bar {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: flex-start;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 8px;
} }
@@ -458,6 +439,7 @@ onMounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
margin-top: 4px;
} }
.pagination { .pagination {
@@ -471,5 +453,9 @@ onMounted(() => {
justify-content: flex-end; justify-content: flex-end;
gap: 10px; gap: 10px;
} }
.drawer-form {
padding-right: 10px;
}
</style> </style>