| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- <template>
- <el-container class="admin-user-container">
- <el-header height="auto" class="search-header">
- <el-form :inline="true" :model="queryParams" size="small" class="search-form" @submit.native.prevent>
- <el-form-item label="用户筛选">
- <el-input
- v-model="queryParams.screenName"
- clearable
- placeholder="输入显示名搜索"
- prefix-icon="el-icon-search"
- class="search-input"
- @keyup.enter.native="handleSearch"
- />
- </el-form-item>
- <el-form-item label="状态">
- <el-select
- v-model="queryParams.status"
- clearable
- placeholder="全部状态"
- class="status-select"
- @change="handleSearch"
- >
- <el-option label="正常" :value="1" />
- <el-option label="封禁中" :value="2" />
- <el-option label="已注销" :value="3" />
- </el-select>
- </el-form-item>
- <el-form-item class="search-actions">
- <el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button>
- <el-button icon="el-icon-refresh" class="btn-refresh" @click="onRefresh">重置</el-button>
- </el-form-item>
- </el-form>
- </el-header>
- <el-main class="table-main">
- <el-table
- v-loading="loading"
- :data="dataList"
- border
- stripe
- height="520"
- style="width: 100%"
- class="custom-table"
- >
- <el-table-column
- fixed="left"
- label="No"
- type="index"
- width="60"
- align="center"
- />
- <el-table-column
- label="头像"
- width="80"
- align="center"
- >
- <template slot-scope="scope">
- <el-avatar
- :size="36"
- :src="scope.row.avatarUrl"
- icon="el-icon-user-solid"
- class="table-avatar"
- />
- </template>
- </el-table-column>
- <el-table-column
- prop="userId"
- label="用户 ID"
- width="120"
- align="center"
- />
- <el-table-column
- prop="username"
- label="用户名"
- min-width="120"
- />
- <el-table-column
- prop="screenName"
- label="显示名"
- min-width="120"
- >
- <template slot-scope="scope">
- <router-link :to="`/user/${scope.row.userId}`" class="user-link">
- <span>{{ scope.row.screenName || '未设置' }}</span>
- <i class="el-icon-link link-icon" />
- </router-link>
- </template>
- </el-table-column>
- <el-table-column
- prop="signature"
- label="个性签名"
- min-width="180"
- >
- <template slot-scope="scope">
- <el-tooltip
- v-if="scope.row.signature"
- :content="scope.row.signature"
- placement="top"
- :open-delay="400"
- >
- <span class="ellipsis-text">{{ scope.row.signature }}</span>
- </el-tooltip>
- <span v-else class="text-muted">-</span>
- </template>
- </el-table-column>
- <el-table-column
- prop="pubDate"
- label="注册时间"
- width="160"
- align="center"
- />
- <el-table-column
- label="帐号状态"
- width="100"
- align="center"
- >
- <template slot-scope="scope">
- <el-tag v-if="scope.row.status === 1" type="success" size="mini" effect="light">
- 正常
- </el-tag>
- <el-tag v-else-if="scope.row.status === 2" type="danger" size="mini" effect="light">
- 封禁中
- </el-tag>
- <el-tag v-else type="info" size="mini" effect="light">
- 已注销
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column
- label="拥有的角色"
- min-width="140"
- >
- <template slot-scope="scope">
- <div v-if="scope.row.roles && scope.row.roles.length" class="role-tags-wrapper">
- <el-tag
- v-for="role in (Array.isArray(scope.row.roles) ? scope.row.roles : scope.row.roles.split(','))"
- :key="role"
- size="mini"
- type="warning"
- class="role-tag"
- >
- {{ role }}
- </el-tag>
- </div>
- <span v-else class="text-muted">普通用户</span>
- </template>
- </el-table-column>
- <el-table-column
- fixed="right"
- label="操作"
- width="100"
- align="center"
- >
- <template slot-scope="scope">
- <el-button
- size="mini"
- type="text"
- class="btn-action-delete"
- icon="el-icon-lock"
- :disabled="scope.row.status !== 1"
- @click="handleDisable(scope.$index, scope.row)"
- >
- 禁用
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- <div class="pagination-container">
- <el-pagination
- background
- layout="total, prev, pager, next, sizes"
- :page-sizes="[10, 12, 20, 50]"
- :page-size="pageSize"
- :current-page="currentPage"
- :total="totalSize"
- @current-change="handleCurrentChange"
- @size-change="handleSizeChange"
- />
- </div>
- </el-main>
- </el-container>
- </template>
- <script>
- import { getUserList } from '@/api/admin'
- export default {
- name: 'AdminUserList',
- data() {
- return {
- queryParams: {
- pn: 1,
- status: '', // 初始化为空字符串,以便触发 el-select 的 clearable
- screenName: ''
- },
- currentPage: 1,
- pageSize: 12,
- totalSize: 0,
- dataList: [],
- loading: false
- }
- },
- created() {
- this.getData()
- },
- methods: {
- getData() {
- this.loading = true
- this.queryParams.pn = this.currentPage
- // 过滤掉空字符串参数
- const params = { ...this.queryParams }
- if (!params.screenName.trim()) delete params.screenName
- if (params.status === '') delete params.status
- getUserList(params).then(resp => {
- if (resp.code === 0) {
- const respData = resp.data
- this.dataList = respData.list || []
- this.totalSize = respData.totalSize || 0
- } else {
- this.$message.error(resp.msg)
- }
- }).catch(err => {
- this.$message.error(err.message || '获取列表失败')
- }).finally(() => {
- this.loading = false
- })
- },
- // 点击查询或下拉切换时调用
- handleSearch() {
- this.currentPage = 1 // 搜索时重置回第一页
- this.getData()
- },
- // 重置/刷新
- onRefresh() {
- this.queryParams.screenName = ''
- this.queryParams.status = ''
- this.currentPage = 1
- this.getData()
- this.$message.success('数据已重置并刷新')
- },
- handleCurrentChange(pageNumber) {
- this.currentPage = pageNumber
- this.getData()
- // 丝滑滚动回顶部
- const tableBody = this.$el.querySelector('.el-table__body-wrapper')
- if (tableBody) tableBody.scrollTop = 0
- },
- handleSizeChange(newSize) {
- this.pageSize = newSize
- this.currentPage = 1
- this.getData()
- },
- handleDisable(index, row) {
- this.$confirm(`确定要封禁用户 [${row.screenName || row.username}] 吗?封禁后其所有正在进行的会话将被强制中断。`, '安全警告', {
- confirmButtonText: '确定封禁',
- cancelButtonText: '取消',
- confirmButtonClass: 'el-button--danger',
- type: 'warning'
- }).then(() => {
- // 调用封禁 API 成功后应执行刷新:this.getData()
- this.$message.success(`用户 ${row.userId} 封禁成功`)
- }).catch(() => {})
- }
- }
- }
- </script>
- <style scoped>
- /* 容器精细布局 */
- .admin-user-container {
- background: #fff;
- border-radius: 8px;
- display: flex;
- flex-direction: column;
- }
- /* 顶部搜索栏现代化微调 */
- .search-header {
- padding: 16px 4px 4px 4px;
- border-bottom: 1px solid #f1f5f9;
- }
- .search-form {
- display: flex;
- flex-wrap: wrap;
- gap: 4px 16px;
- }
- ::v-deep .el-form-item {
- margin-bottom: 12px;
- }
- ::v-deep .el-form-item__label {
- color: #64748b;
- font-weight: 500;
- padding-right: 8px;
- }
- .search-input {
- width: 200px;
- }
- .status-select {
- width: 130px;
- }
- .btn-refresh {
- background: #f8fafc;
- border-color: #cbd5e1;
- color: #64748b;
- }
- .btn-refresh:hover {
- color: #1890ff;
- border-color: #1890ff;
- background: #fff;
- }
- /* 表格主体美化 */
- .table-main {
- padding: 16px 0 0 0;
- }
- .custom-table {
- border-radius: 6px;
- overflow: hidden;
- }
- /* 只保留需要的行内溢出裁剪,优化自带 tooltip 样式 */
- .ellipsis-text {
- display: block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- color: #334155;
- }
- .text-muted {
- color: #94a3b8;
- }
- /* 头像与连接项美化 */
- .table-avatar {
- border: 1px solid #e2e8f0;
- background-color: #f8fafc;
- box-shadow: 0 2px 6px rgba(0,0,0,0.02);
- }
- .user-link {
- color: #1890ff;
- text-decoration: none;
- display: inline-flex;
- align-items: center;
- gap: 4px;
- font-weight: 500;
- }
- .user-link:hover {
- color: #40a9ff;
- text-decoration: underline;
- }
- .link-icon {
- font-size: 12px;
- opacity: 0.7;
- }
- /* 角色标签容器包裹弹性盒模型 */
- .role-tags-wrapper {
- display: flex;
- flex-wrap: wrap;
- gap: 4px;
- }
- .role-tag {
- border-radius: 4px;
- border: 1px solid #fee2e2;
- background: #fff7ed;
- color: #ea580c;
- }
- /* 操作项去纽扣化改文字 */
- .btn-action-delete {
- color: #ef4444;
- font-weight: 500;
- padding: 0;
- }
- .btn-action-delete:hover {
- color: #dc2626;
- }
- .btn-action-delete.is-disabled {
- color: #cbd5e1;
- }
- /* 底部分页精细对齐 */
- .pagination-container {
- margin-top: 20px;
- display: flex;
- justify-content: flex-end;
- }
- ::v-deep .el-pagination.is-background .el-pager li:not(.disabled).active {
- background-gradient: linear-gradient(135deg, #1890ff 0%, #0076e4 100%);
- background-color: #1890ff;
- }
- </style>
|