- 修改 common.less: - 新增 .page-wrapper 类,使用 flex 纵向布局占满父容器 - 修改 .table-container 为 flex 布局,让表格自适应高度 - .el-table 使用 flex:1 实现内容区域滚动、表头固定 - 7个列表页面添加 page-wrapper 类: - SystemUser.vue, SystemRole.vue - Hitokoto.vue, PhotoWall.vue, Music.vue, SourceImage.vue - Article.vue(特殊处理:左右分栏布局适配) 现在设置 100 条数据时,表格内容独立滚动,分页始终可见,不会触发整页滚动。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
197 lines
6.4 KiB
Vue
197 lines
6.4 KiB
Vue
<template>
|
|
<div class="page-wrapper">
|
|
<el-form inline :model="search" @submit.prevent>
|
|
<el-form-item label="用户名/昵称">
|
|
<el-input v-model="search.username" />
|
|
</el-form-item>
|
|
</el-form>
|
|
<div class="btn-container">
|
|
<el-button type="primary" @click="add">添加</el-button>
|
|
<div class="search-btn">
|
|
<el-button type="primary" @click="loadDataBase(true)" icon="Search">搜索</el-button>
|
|
<el-button @click="reset" icon="RefreshLeft">重置</el-button>
|
|
</div>
|
|
</div>
|
|
<div class="table-container">
|
|
<el-table :data="systemUserData" v-loading="loading" stripe>
|
|
<el-table-column prop="username" label="用户名" />
|
|
<el-table-column prop="realname" label="昵称" />
|
|
<el-table-column prop="created_at" label="创建时间" >
|
|
<template #default="scope">
|
|
{{ datetimeFormat(scope.row.created_at) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="updated_at" label="更新时间" >
|
|
<template #default="scope">
|
|
{{ datetimeFormat(scope.row.updated_at) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" >
|
|
<template #default="scope">
|
|
<el-button link icon="Edit" @click="update(scope.row)" title="修改"></el-button>
|
|
<el-button link type="danger" icon="Delete" @click="remove(scope.row)" title="删除"></el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
<div class="page-container">
|
|
<el-pagination background
|
|
:page-sizes="store.state.pageSizeOpts"
|
|
:layout="store.state.pageLayout"
|
|
:current-page="search.pageNum"
|
|
:total="total"
|
|
@size-change="pageSizeChange"
|
|
@current-change="pageChange">
|
|
</el-pagination>
|
|
</div>
|
|
<el-dialog v-model="addModal" :title="modalTitle" >
|
|
<el-form ref="userForm" :model="formData" :rules="ruleValidate" :label-width="120">
|
|
<el-form-item label="用户名" prop="username">
|
|
<el-input v-model="formData.username" />
|
|
</el-form-item>
|
|
<el-form-item label="密码" prop="password">
|
|
<el-input v-model="formData.password" type="password" />
|
|
</el-form-item>
|
|
<el-form-item label="昵称" prop="realname">
|
|
<el-input v-model="formData.realname" />
|
|
</el-form-item>
|
|
<el-form-item label="角色" prop="realname">
|
|
<el-select v-model="formData.role_ids" multiple >
|
|
<el-option v-for="role in roles" :key="role._id" :value="role._id" :label="role.name"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<el-button @click="addModal = false">取消</el-button>
|
|
<el-button type="primary" @click="save" :loading="modalLoading">确定</el-button>
|
|
</span>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
<script setup lang="ts">
|
|
import { ref, reactive, computed, nextTick } from 'vue'
|
|
import { useStore } from 'vuex'
|
|
import { useBaseList } from '@/model/baselist'
|
|
import { Page } from '@/model/common.dto'
|
|
import http from '@/utils/http'
|
|
import { SystemUserModel } from '@/model/system/system-user'
|
|
import { SystemRoleModel } from '@/model/system/system-role'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import { VForm } from '@/types'
|
|
|
|
const store = useStore()
|
|
|
|
class SystemUserPage extends Page {
|
|
username?: string
|
|
reset() {
|
|
super.reset()
|
|
this.username = undefined
|
|
}
|
|
}
|
|
|
|
const { loading, modalLoading, total, search, setLoadData, loadDataBase, reset, pageChange, pageSizeChange, datetimeFormat } = useBaseList(new SystemUserPage())
|
|
|
|
const systemUserData = ref<SystemUserModel[]>([])
|
|
const roles = ref<SystemRoleModel[]>([])
|
|
const addModal = ref(false)
|
|
const modalTitle = ref<string | null>(null)
|
|
const formData = reactive<SystemUserModel>({
|
|
_id: null,
|
|
username: null,
|
|
password: null,
|
|
realname: null,
|
|
role_ids: []
|
|
})
|
|
const userForm = ref<VForm>()
|
|
|
|
const ruleValidate = computed(() => ({
|
|
username: [
|
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
|
{ validator: async (rule: object, value: string, callback: Function) => {
|
|
const data = await http.get<any, any>('/api/v1/system/user/exists', {params: {username: value, id: formData._id}})
|
|
if(data.data.exists) {
|
|
callback(new Error('用户名已存在'))
|
|
} else {
|
|
callback()
|
|
}
|
|
}, trigger: 'blur'
|
|
}
|
|
],
|
|
password: [
|
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
|
{ min: 8, max: 16, message: '密码长度8~16位', trigger: 'blur' },
|
|
{ pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![-=+_.,]+$)[\da-zA-Z-=+_.,]{8,16}$/, message: '密码由字母、数字、特殊字符中的任意两种组成', trigger: 'blur' }
|
|
],
|
|
}))
|
|
|
|
async function loadData() {
|
|
loading.value = true
|
|
const data = await http.get<{params: SystemUserPage}, any>('/api/v1/system/user/list', {params: search})
|
|
loading.value = false
|
|
total.value = data.total
|
|
systemUserData.value = data.data
|
|
}
|
|
setLoadData(loadData)
|
|
|
|
function add() {
|
|
Object.assign(formData, {
|
|
_id: null,
|
|
username: null,
|
|
password: null,
|
|
realname: null,
|
|
role_ids: []
|
|
})
|
|
modalTitle.value = '新增用户'
|
|
addModal.value = true
|
|
clearValidate()
|
|
}
|
|
|
|
function update(row: SystemUserModel) {
|
|
Object.assign(formData, {
|
|
_id: row._id,
|
|
username: row.username,
|
|
realname: row.realname,
|
|
role_ids: row.role_ids
|
|
})
|
|
modalTitle.value = '修改用户'
|
|
addModal.value = true
|
|
clearValidate()
|
|
}
|
|
|
|
async function save() {
|
|
userForm.value?.validate(async (valid: boolean) => {
|
|
if (!valid) return
|
|
modalLoading.value = true
|
|
const data = await http.post<SystemUserModel, any>('/api/v1/system/user/save', formData)
|
|
modalLoading.value = false
|
|
addModal.value = false
|
|
ElMessage.success(data.message)
|
|
loadData()
|
|
})
|
|
}
|
|
|
|
function remove(row: SystemUserModel) {
|
|
ElMessageBox.confirm(`是否确认删除 ${row.username} 用户?`, '确认删除', {type: 'warning'}).then(async () => {
|
|
const data = await http.delete<{params: {id: string}}, any>('/api/v1/system/user/delete', {params: {id: row._id}})
|
|
if(data.status) {
|
|
ElMessage.success(data.message)
|
|
loadData()
|
|
} else {
|
|
ElMessage.warning(data.message)
|
|
}
|
|
}).catch(() => {})
|
|
}
|
|
|
|
function clearValidate() {
|
|
nextTick(() => {
|
|
userForm.value?.clearValidate()
|
|
})
|
|
}
|
|
|
|
loadData()
|
|
http.get<never, any>('/api/v1/system/role/listAll').then(data => {
|
|
roles.value = data
|
|
})
|
|
</script> |