前端代码优化
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -8,13 +8,13 @@
|
||||
|
||||
<el-card class="search-card" shadow="never">
|
||||
<div class="search-bar">
|
||||
<el-form :inline="true" :model="queryForm" label-width="90px">
|
||||
<el-form-item label="日志类型">
|
||||
<el-form :inline="true" :model="queryForm" label-width="72px" class="search-form">
|
||||
<el-form-item label="日志类型" class="search-item search-item--log-type">
|
||||
<el-select
|
||||
v-model="queryForm.logType"
|
||||
placeholder="请选择日志类型"
|
||||
clearable
|
||||
style="width: 280px"
|
||||
class="search-select search-select--log-type"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in logTypeOptions"
|
||||
@@ -25,13 +25,13 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="操作人">
|
||||
<el-form-item label="操作人" class="search-item search-item--operator">
|
||||
<el-select
|
||||
v-model="queryForm.createId"
|
||||
placeholder="请选择操作人"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 200px"
|
||||
class="search-select search-select--operator"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
@@ -42,7 +42,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="录入时间">
|
||||
<el-form-item label="录入时间" class="search-item search-item--time">
|
||||
<el-date-picker
|
||||
v-model="queryForm.logWritetimeRange"
|
||||
type="datetimerange"
|
||||
@@ -51,16 +51,16 @@
|
||||
end-placeholder="结束时间"
|
||||
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 class="search-actions">
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
</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-form-item>
|
||||
</el-form>
|
||||
@@ -419,11 +419,53 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
width: 100%;
|
||||
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 {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
</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-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -46,9 +46,14 @@
|
||||
>
|
||||
<span class="action-badge-label">绑定动作</span>
|
||||
<span class="action-badge-name">{{ getActionName(action) }}</span>
|
||||
<div class="action-status-wrap">
|
||||
<el-tag :type="getStatusTag(getActionStatus(action))" size="small" effect="light" round>
|
||||
{{ getStatusText(getActionStatus(action)) }}
|
||||
</el-tag>
|
||||
<span v-if="getActionRecordCount(action) > 0" class="record-dot action-badge-record-dot">
|
||||
{{ getActionRecordCount(action) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="getIslandActionList(item).length > 2" class="action-badge-more">
|
||||
+{{ getIslandActionList(item).length - 2 }}
|
||||
@@ -619,6 +624,7 @@ onMounted(loadData)
|
||||
}
|
||||
|
||||
.action-badge {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -644,6 +650,19 @@ onMounted(loadData)
|
||||
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 {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user