前端代码优化

This commit is contained in:
2026-06-01 10:09:39 +08:00
parent 3f488d2e72
commit 795cd3affa
6 changed files with 77 additions and 2647 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -8,13 +8,13 @@
<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="90px"> <el-form :inline="true" :model="queryForm" label-width="72px" class="search-form">
<el-form-item label="日志类型"> <el-form-item label="日志类型" class="search-item search-item--log-type">
<el-select <el-select
v-model="queryForm.logType" v-model="queryForm.logType"
placeholder="请选择日志类型" placeholder="请选择日志类型"
clearable clearable
style="width: 280px" class="search-select search-select--log-type"
> >
<el-option <el-option
v-for="item in logTypeOptions" v-for="item in logTypeOptions"
@@ -25,13 +25,13 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="操作人"> <el-form-item label="操作人" class="search-item search-item--operator">
<el-select <el-select
v-model="queryForm.createId" v-model="queryForm.createId"
placeholder="请选择操作人" placeholder="请选择操作人"
clearable clearable
filterable filterable
style="width: 200px" class="search-select search-select--operator"
> >
<el-option <el-option
v-for="item in userOptions" v-for="item in userOptions"
@@ -42,7 +42,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="录入时间"> <el-form-item label="录入时间" class="search-item search-item--time">
<el-date-picker <el-date-picker
v-model="queryForm.logWritetimeRange" v-model="queryForm.logWritetimeRange"
type="datetimerange" type="datetimerange"
@@ -51,16 +51,16 @@
end-placeholder="结束时间" end-placeholder="结束时间"
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-DD HH:mm:ss"
style="width: 360px" class="search-range"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item class="search-actions">
<el-button type="primary" @click="handleSearch">查询</el-button> <el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button> <el-button @click="resetSearch">重置</el-button>
</el-form-item> </el-form-item>
<el-form-item style="margin-left: 84px"> <el-form-item class="search-export">
<el-button v-permission="'sys:managelog:excel'" type="success" :loading="exportLoading" @click="handleExport">导出日志</el-button> <el-button v-permission="'sys:managelog:excel'" type="success" :loading="exportLoading" @click="handleExport">导出日志</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@@ -419,11 +419,53 @@ onMounted(async () => {
} }
.search-bar { .search-bar {
overflow-x: hidden;
}
.search-form {
display: flex; display: flex;
justify-content: space-between; align-items: center;
align-items: flex-start; flex-wrap: nowrap;
flex-wrap: wrap; width: 100%;
gap: 8px; gap: 8px;
white-space: nowrap;
}
.search-item {
margin-bottom: 0;
flex: 0 0 auto;
}
.search-item--log-type,
.search-item--operator {
flex-shrink: 1;
}
.search-item--time {
flex: 1 1 420px;
min-width: 280px;
}
.search-select--log-type {
width: 220px;
}
.search-select--operator {
width: 180px;
}
.search-range {
width: 100%;
max-width: 420px;
}
.search-actions,
.search-export {
margin-left: 0;
}
.search-export {
margin-right: 0;
} }
.pagination { .pagination {

File diff suppressed because it is too large Load Diff

View File

@@ -1,412 +0,0 @@
<template>
<div class="position-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>系统管理</el-breadcrumb-item>
<el-breadcrumb-item>职位管理</el-breadcrumb-item>
</el-breadcrumb>
<el-card class="search-card" shadow="never">
<div class="search-bar">
<el-form :inline="true" :model="queryForm" label-width="80px">
<el-form-item label="职位名称">
<el-input
v-model="queryForm.posiName"
placeholder="输入名称"
clearable
style="width: 200px"
/>
</el-form-item>
<el-form-item label="职位编码">
<el-input
v-model="queryForm.posiCode"
placeholder="输入职位编码"
clearable
style="width: 200px"
/>
</el-form-item>
<el-form-item>
<el-button v-permission="'sys:positionselect'" type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
<div class="toolbar">
<el-button v-permission="'sys:positionadd'" type="primary" @click="openDrawer('create')">新增职位</el-button>
</div>
</div>
</el-card>
<el-card shadow="never">
<el-table
:data="tableData"
stripe
border
style="width: 100%"
v-loading="loading"
>
<el-table-column type="index" label="序号" width="70" />
<el-table-column
prop="posiName"
label="职位名称"
min-width="180"
:formatter="formatCell"
/>
<el-table-column
prop="posiCode"
label="职位编码"
min-width="180"
:formatter="formatCell"
/>
<el-table-column
prop="remark"
label="备注"
min-width="200"
:formatter="formatCell"
/>
<el-table-column label="操作" width="180" fixed="right">
<template #default="scope">
<el-button v-permission="'sys:positionupd'" type="primary" link @click="openDrawer('edit', scope.row)">编辑</el-button>
<el-button v-permission="'sys:positiondel'" type="danger" link @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
background
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</el-card>
<el-drawer
v-model="drawerVisible"
:title="drawerTitle"
direction="rtl"
:size="420"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="90px"
class="drawer-form"
>
<el-form-item v-if="isEdit" label="职位ID">
<el-input v-model="form.id" disabled />
</el-form-item>
<el-form-item label="职位名称" prop="posiName">
<el-input v-model="form.posiName" placeholder="请输入职位名称" />
</el-form-item>
<el-form-item label="职位编码" prop="posiCode">
<el-input v-model="form.posiCode" placeholder="请输入职位编码" />
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="form.remark"
type="textarea"
placeholder="请输入备注"
:rows="3"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
<div class="drawer-footer">
<el-button @click="drawerVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitForm">
{{ isEdit ? '更新' : '提交' }}
</el-button>
</div>
</template>
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { positionadd, positionbyid, positiondel, positionlist, positionupd } from '@/api/system/position'
interface PositionItem {
id?: number | string
posiName: string
posiCode: string
remark?: string
remarks?: string
createTime?: string
updateTime?: string
creator?: string
updater?: string
delFlag?: boolean
}
const loading = ref(false)
const submitLoading = ref(false)
const tableData = ref<PositionItem[]>([])
const total = ref(0)
const queryForm = reactive({
posiName: '',
posiCode: '',
})
const pagination = reactive({
pageNum: 1,
pageSize: 10,
})
const drawerVisible = ref(false)
const drawerTitle = ref('新增职位')
const isEdit = ref(false)
const formRef = ref<FormInstance>()
const form = reactive<PositionItem>({
id: undefined,
posiName: '',
posiCode: '',
remark: '',
})
const rules: FormRules = {
posiName: [{ required: true, message: '请输入职位名称', trigger: 'blur' }],
posiCode: [{ required: true, message: '请输入职位编码', trigger: 'blur' }],
}
const resetForm = () => {
form.id = undefined
form.posiName = ''
form.posiCode = ''
form.remark = ''
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 () => {
loading.value = true
try {
// 构建查询参数,过滤空字符串
const params: any = {
pageNum: pagination.pageNum,
pageSize: pagination.pageSize,
}
if (queryForm.posiName && queryForm.posiName.trim()) {
params.posiName = queryForm.posiName.trim()
}
if (queryForm.posiCode && queryForm.posiCode.trim()) {
params.posiCode = queryForm.posiCode.trim()
}
const res: any = await positionlist(params)
console.log('职位列表API返回数据:', res)
// 根据Swagger文档返回格式为: { code: 0, message: "string", data: { records: [], total: 0 } }
// request拦截器返回的是整个响应体所以res就是 { code: 0, message: "string", data: {...} }
// 需要从res.data中获取分页数据
let pageData = res?.data
// 如果res.data不存在可能是直接返回了分页数据
if (!pageData && (res?.records || res?.total !== undefined)) {
pageData = res
}
// 兼容多种返回格式
const recordsFromData =
pageData?.records ||
pageData?.list ||
pageData?.rows ||
pageData?.items ||
(Array.isArray(pageData?.data) ? pageData.data : undefined)
const records = Array.isArray(recordsFromData)
? recordsFromData
: Array.isArray(pageData)
? pageData
: Array.isArray(res?.data)
? res.data
: []
const totalValue =
pageData?.total ??
pageData?.count ??
pageData?.totalCount ??
res?.total ??
(Array.isArray(records) ? records.length : 0)
tableData.value = records
total.value = Number(totalValue) || 0
console.log('解析后的数据:', {
records: records.length,
total: total.value,
pageData: pageData,
firstRecord: records[0]
})
} catch (error: any) {
console.error('load position list error', error)
const errorMsg = error?.message || error?.msg || '加载职位列表失败'
ElMessage.error(errorMsg)
tableData.value = []
total.value = 0
} finally {
loading.value = false
}
}
const handleSearch = () => {
pagination.pageNum = 1
loadList()
}
const resetSearch = () => {
queryForm.posiName = ''
queryForm.posiCode = ''
handleSearch()
}
const handleSizeChange = (size: number) => {
pagination.pageSize = size
loadList()
}
const handleCurrentChange = (page: number) => {
pagination.pageNum = page
loadList()
}
const openDrawer = async (mode: 'create' | 'edit', row?: PositionItem) => {
resetForm()
isEdit.value = mode === 'edit'
drawerTitle.value = isEdit.value ? '编辑职位' : '新增职位'
drawerVisible.value = true
if (isEdit.value && row?.id != null) {
try {
const res: any = await positionbyid(row.id)
const data = res?.data || res || {}
form.id = data.id ?? row.id
form.posiName = data.posiName ?? row.posiName
form.posiCode = data.posiCode ?? row.posiCode
form.remark =
data.remark ??
data.remarks ??
row.remark ??
(row as any)?.remarks ??
''
} catch (error) {
ElMessage.error('获取职位详情失败')
drawerVisible.value = false
}
}
}
const submitForm = () => {
formRef.value?.validate(async (valid) => {
if (!valid) return
submitLoading.value = true
try {
if (isEdit.value) {
// 同步备注的两种字段名,兼容后端不同写法
await positionupd({ ...form, remarks: form.remark })
ElMessage.success('更新成功')
} else {
await positionadd({ ...form, remarks: form.remark })
ElMessage.success('新增成功')
}
drawerVisible.value = false
loadList()
} catch (error) {
console.error('submit position error', error)
ElMessage.error(isEdit.value ? '更新失败' : '新增失败')
} finally {
submitLoading.value = false
}
})
}
const handleDelete = (row: PositionItem) => {
if (!row.id) {
ElMessage.warning('缺少职位ID')
return
}
ElMessageBox.confirm(`确认删除职位「${row.posiName}」吗?`, '提示', {
type: 'warning',
})
.then(async () => {
if (row.id == null) {
ElMessage.warning('缺少职位ID无法删除')
return
}
try {
await positiondel(row.id)
ElMessage.success('删除成功')
loadList()
} catch (error) {
console.error('delete position error', error)
ElMessage.error('删除失败')
}
})
.catch(() => {})
}
onMounted(() => {
loadList()
})
</script>
<style scoped>
.position-page {
display: flex;
flex-direction: column;
gap: 12px;
}
.search-card {
padding-bottom: 4px;
}
.search-bar {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.toolbar {
display: flex;
align-items: center;
gap: 8px;
}
.pagination {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.drawer-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
</style>

View File

@@ -29,7 +29,7 @@
<el-button type="primary" @click="handleSearch">查询</el-button> <el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button> <el-button @click="resetSearch">重置</el-button>
</el-form-item> </el-form-item>
<el-form-item style="margin-left: 276px" > <el-form-item style="margin-right: auto" >
<el-button v-permission="'tb:exceptionrecord:excel'" type="success" :loading="exportLoading" @click="handleExport">导出记录</el-button> <el-button v-permission="'tb:exceptionrecord:excel'" type="success" :loading="exportLoading" @click="handleExport">导出记录</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>

View File

@@ -46,9 +46,14 @@
> >
<span class="action-badge-label">绑定动作</span> <span class="action-badge-label">绑定动作</span>
<span class="action-badge-name">{{ getActionName(action) }}</span> <span class="action-badge-name">{{ getActionName(action) }}</span>
<el-tag :type="getStatusTag(getActionStatus(action))" size="small" effect="light" round> <div class="action-status-wrap">
{{ getStatusText(getActionStatus(action)) }} <el-tag :type="getStatusTag(getActionStatus(action))" size="small" effect="light" round>
</el-tag> {{ getStatusText(getActionStatus(action)) }}
</el-tag>
<span v-if="getActionRecordCount(action) > 0" class="record-dot action-badge-record-dot">
{{ getActionRecordCount(action) }}
</span>
</div>
</div> </div>
<span v-if="getIslandActionList(item).length > 2" class="action-badge-more"> <span v-if="getIslandActionList(item).length > 2" class="action-badge-more">
+{{ getIslandActionList(item).length - 2 }} +{{ getIslandActionList(item).length - 2 }}
@@ -619,6 +624,7 @@ onMounted(loadData)
} }
.action-badge { .action-badge {
position: relative;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
@@ -644,6 +650,19 @@ onMounted(loadData)
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.action-status-wrap {
position: relative;
flex: 0 0 auto;
display: inline-flex;
align-items: center;
gap: 4px;
}
.action-badge-record-dot {
position: static;
margin-left: 4px;
}
.action-badge-more { .action-badge-more {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;