系统管理后台实现商户页面和用户页面

This commit is contained in:
2025-06-10 13:01:35 +08:00
parent 2aba4022f2
commit db5d707e54
10 changed files with 1131 additions and 0 deletions

10
src/api/feedback.js Normal file
View File

@ -0,0 +1,10 @@
import request from '@/utils/request'
// 获取用户列表
export function feedbackList(params) {
return request({
url: '/x/feedback',
method: 'get',
params
})
}

18
src/api/merchant.js Normal file
View File

@ -0,0 +1,18 @@
import request from '@/utils/request'
export function merchantList(params){
return request({
url: '/x/merchant',
method: 'get',
params
})
}
export function merchantAudit(data) {
return request({
url: '/x/merchant/audit',
method: 'post',
data
})
}

36
src/api/user.js Normal file
View File

@ -0,0 +1,36 @@
import request from '@/utils/request'
// 获取用户列表
export function userList(params) {
return request({
url: '/x/user/list',
method: 'get',
params
})
}
// 删除用户
export function deleteUser(id) {
return request({
url: `/api/users/${id}`,
method: 'delete'
})
}
// 批量删除用户
export function batchDeleteUsers(ids) {
return request({
url: '/api/users/batch',
method: 'delete',
data: { ids }
})
}
// 更新用户状态
export function updateUserStatus(id, status) {
return request({
url: `/api/users/${id}/status`,
method: 'put',
data: { status }
})
}

View File

@ -0,0 +1,186 @@
<template>
<div class="approved-list">
<el-table
v-loading="loading"
:data="tableData"
border={false}
class="custom-table"
header-row-class-name="custom-header"
row-class-name="custom-row"
>
<el-table-column prop="name" label="商户名称" header-align="center" align="center" />
<el-table-column prop="contactName" label="联系人" header-align="center" align="center" />
<el-table-column prop="contactPhone" label="联系电话" header-align="center" align="center" />
<el-table-column prop="contactEmail" label="联系邮箱" header-align="center" align="center" />
<el-table-column prop="address" label="地址" header-align="center" align="center" />
<el-table-column prop="auditAt" label="通过时间" header-align="center" align="center" />
<el-table-column prop="expireAt" label="服务到期时间" header-align="center" align="center" />
<el-table-column prop="status" label="状态" header-align="center" align="center">
<template slot-scope="scope">
<span :class="['status-tag', scope.row.status === 1 ? 'status-normal' : 'status-block']">
{{ scope.row.status === 1 ? '正常' : '禁用' }}
</span>
</template>
</el-table-column>
<el-table-column label="操作" width="280" header-align="center" align="center">
<template slot-scope="scope">
<el-button type="text" size="mini" class="edit-btn" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" size="mini" class="view-btn" @click="handleViewStores(scope.row)">查看门店</el-button>
<el-button
type="text"
size="mini"
:class="scope.row.status === 1 ? 'block-btn' : 'unblock-btn'"
@click="handleToggleStatus(scope.row)"
>
{{ scope.row.status === 1 ? '禁用' : '启用' }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 编辑弹窗 -->
<merchant-edit
:visible.sync="editDialogVisible"
:merchant-data="currentMerchant"
@success="handleEditSuccess"
/>
</div>
</template>
<script>
import { merchantList } from '@/api/merchant'
import MerchantEdit from './merchant-edit.vue'
export default {
name: 'ApprovedList',
components: {
MerchantEdit
},
data() {
return {
loading: false,
tableData: [],
editDialogVisible: false,
currentMerchant: {}
}
},
created() {
this.getList()
},
methods: {
async getList() {
try {
this.loading = true
const res = await merchantList({
auditStatus: 2
})
if (res.code === 0) {
this.tableData = res.data.list || []
} else {
this.$message.error(res.message || '获取已通过列表失败')
}
} catch (error) {
this.$message.error('获取已通过列表失败')
} finally {
this.loading = false
}
},
handleEdit(row) {
this.currentMerchant = { ...row }
this.editDialogVisible = true
},
handleViewStores(row) {
// TODO: 实现查看门店功能
this.$message.info('查看门店功能开发中')
},
async handleToggleStatus(row) {
try {
const action = row.status === 1 ? '禁用' : '启用'
await this.$confirm(`确认${action}该商户吗?`, '提示', {
type: 'warning'
})
// TODO: 调用禁用/启用接口
this.$message.success(`${action}成功`)
this.getList()
} catch (error) {
if (error !== 'cancel') {
this.$message.error('操作失败')
}
}
},
handleEditSuccess() {
this.getList()
}
}
}
</script>
<style lang="scss" scoped>
.approved-list {
margin-top: 20px;
}
.custom-table {
background: #fff;
border: none;
font-size: 15px;
::v-deep .el-table__body-wrapper td,
::v-deep .el-table__header-wrapper th {
border-right: none !important;
border-left: none !important;
}
}
.custom-header th {
background: #f7f8fa !important;
color: #888;
font-weight: 500;
border-bottom: 1px solid #f0f0f0 !important;
border-top: 1px solid #f0f0f0 !important;
padding-top: 16px !important;
padding-bottom: 16px !important;
height: 56px !important;
line-height: 24px !important;
}
.custom-row td {
border-bottom: 1px solid #f0f0f0 !important;
border-top: none !important;
padding-top: 14px !important;
padding-bottom: 14px !important;
height: 52px !important;
line-height: 22px !important;
vertical-align: middle !important;
}
.status-tag {
display: inline-block;
padding: 2px 16px;
border-radius: 16px;
font-size: 14px;
font-weight: 500;
}
.status-normal {
background: #e8f7ea;
color: #3fc06d;
}
.status-block {
background: #fbeaea;
color: #f56c6c;
}
.edit-btn {
color: #556ff6;
font-weight: 500;
}
.view-btn {
color: #67c23a;
font-weight: 500;
margin-left: 8px;
}
.block-btn {
color: #f56c6c;
font-weight: 500;
margin-left: 8px;
}
.unblock-btn {
color: #67c23a;
font-weight: 500;
margin-left: 8px;
}
</style>

View File

@ -0,0 +1,179 @@
<template>
<el-dialog
:title="isEdit ? '编辑商户' : '新增商户'"
:visible.sync="dialogVisible"
width="600px"
:close-on-click-modal="false"
@close="handleClose"
>
<el-form
ref="form"
:model="form"
:rules="rules"
label-width="100px"
class="merchant-form"
>
<el-form-item label="商户名称" prop="name">
<el-input v-model="form.name" placeholder="请输入商户名称" />
</el-form-item>
<el-form-item label="联系人" prop="contactName">
<el-input v-model="form.contactName" placeholder="请输入联系人" />
</el-form-item>
<el-form-item label="联系电话" prop="contactPhone">
<el-input v-model="form.contactPhone" placeholder="请输入联系电话" />
</el-form-item>
<el-form-item label="联系邮箱" prop="contactEmail">
<el-input v-model="form.contactEmail" placeholder="请输入联系邮箱" />
</el-form-item>
<el-form-item label="地址" prop="address">
<el-input v-model="form.address" placeholder="请输入地址" />
</el-form-item>
<el-form-item label="服务到期时间" prop="expireAt" v-if="isEdit">
<el-date-picker
v-model="form.expireAt"
type="datetime"
placeholder="请选择服务到期时间"
value-format="yyyy-MM-dd HH:mm:ss"
/>
</el-form-item>
<el-form-item label="状态" prop="status" v-if="isEdit">
<el-radio-group v-model="form.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="2">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose"> </el-button>
<el-button type="primary" :loading="loading" @click="handleSubmit"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { merchantUpdate, merchantCreate } from '@/api/merchant'
export default {
name: 'MerchantEdit',
props: {
visible: {
type: Boolean,
default: false
},
merchantData: {
type: Object,
default: () => ({})
}
},
data() {
return {
dialogVisible: false,
loading: false,
isEdit: false,
form: {
name: '',
contactName: '',
contactPhone: '',
contactEmail: '',
address: '',
expireAt: '',
status: 1
},
rules: {
name: [
{ required: true, message: '请输入商户名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
contactName: [
{ required: true, message: '请输入联系人', trigger: 'blur' }
],
contactPhone: [
{ required: true, message: '请输入联系电话', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
],
contactEmail: [
{ required: true, message: '请输入联系邮箱', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
address: [
{ required: true, message: '请输入地址', trigger: 'blur' }
],
expireAt: [
{ required: true, message: '请选择服务到期时间', trigger: 'change' }
]
}
}
},
watch: {
visible(val) {
this.dialogVisible = val
if (val) {
this.initForm()
}
}
},
methods: {
initForm() {
this.isEdit = !!this.merchantData.id
if (this.isEdit) {
this.form = { ...this.merchantData }
} else {
this.form = {
name: '',
contactName: '',
contactPhone: '',
contactEmail: '',
address: '',
expireAt: '',
status: 1
}
}
this.$nextTick(() => {
this.$refs.form && this.$refs.form.clearValidate()
})
},
handleClose() {
this.$emit('update:visible', false)
},
async handleSubmit() {
try {
await this.$refs.form.validate()
this.loading = true
const api = this.isEdit ? merchantUpdate : merchantCreate
const res = await api(this.form)
if (res.code === 0) {
this.$message.success(this.isEdit ? '编辑成功' : '新增成功')
this.$emit('success')
this.handleClose()
} else {
this.$message.error(res.message || '操作失败')
}
} catch (error) {
console.error(error)
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.merchant-form {
padding: 20px 0;
::v-deep .el-form-item__label {
font-weight: 500;
}
::v-deep .el-input__inner {
height: 40px;
line-height: 40px;
}
::v-deep .el-date-editor.el-input {
width: 100%;
}
}
.dialog-footer {
text-align: right;
padding-top: 20px;
}
</style>

View File

@ -0,0 +1,177 @@
<template>
<div class="pending-list">
<el-table
v-loading="loading"
:data="tableData"
border={false}
class="custom-table"
header-row-class-name="custom-header"
row-class-name="custom-row"
>
<el-table-column prop="name" label="商户名称" header-align="center" align="center" />
<el-table-column prop="contactName" label="联系人" header-align="center" align="center" />
<el-table-column prop="contactPhone" label="联系电话" header-align="center" align="center" />
<el-table-column prop="contactEmail" label="联系邮箱" header-align="center" align="center" />
<el-table-column prop="address" label="地址" header-align="center" align="center" />
<el-table-column prop="createdAt" label="申请时间" header-align="center" align="center" />
<el-table-column prop="status" label="状态" header-align="center" align="center">
<template slot-scope="scope">
<span :class="['status-tag', scope.row.status === 1 ? 'status-normal' : 'status-block']">
{{ scope.row.status === 1 ? '正常' : '禁用' }}
</span>
</template>
</el-table-column>
<el-table-column label="操作" width="200" header-align="center" align="center">
<template slot-scope="scope">
<el-button type="text" size="mini" class="approve-btn" @click="handleApprove(scope.row)">通过</el-button>
<el-button type="text" size="mini" class="reject-btn" @click="handleReject(scope.row)">拒绝</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { merchantList, merchantAudit } from '@/api/merchant'
export default {
name: 'PendingList',
data() {
return {
loading: false,
tableData: []
}
},
created() {
this.getList()
},
methods: {
async getList() {
try {
this.loading = true
const res = await merchantList({
auditStatus: 1
})
if (res.code === 0) {
this.tableData = res.data.list || []
} else {
this.$message.error(res.message || '获取待审核列表失败')
}
} catch (error) {
this.$message.error('获取待审核列表失败')
} finally {
this.loading = false
}
},
async handleApprove(row) {
try {
await this.$confirm('确认通过该商户的审核吗?', '提示', {
type: 'warning'
})
const res = await merchantAudit({
id: row.id,
auditStatus: 2,
auditRemark: '审核通过'
})
if (res.code === 0) {
this.$message.success('审核通过成功')
this.getList()
} else {
this.$message.error(res.message || '审核失败')
}
} catch (error) {
if (error !== 'cancel') {
this.$message.error('操作失败')
}
}
},
async handleReject(row) {
try {
const { value: rejectReason } = await this.$prompt('请输入拒绝原因', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /\S+/,
inputErrorMessage: '拒绝原因不能为空'
})
const res = await merchantAudit({
id: row.id,
auditStatus: 3,
rejectReason: rejectReason
})
if (res.code === 0) {
this.$message.success('已拒绝该商户')
this.getList()
} else {
this.$message.error(res.message || '操作失败')
}
} catch (error) {
if (error !== 'cancel') {
this.$message.error('操作失败')
}
}
}
}
}
</script>
<style lang="scss" scoped>
.pending-list {
margin-top: 20px;
}
.custom-table {
background: #fff;
border: none;
font-size: 15px;
::v-deep .el-table__body-wrapper td,
::v-deep .el-table__header-wrapper th {
border-right: none !important;
border-left: none !important;
}
}
.custom-header th {
background: #f7f8fa !important;
color: #888;
font-weight: 500;
border-bottom: 1px solid #f0f0f0 !important;
border-top: 1px solid #f0f0f0 !important;
padding-top: 16px !important;
padding-bottom: 16px !important;
height: 56px !important;
line-height: 24px !important;
}
.custom-row td {
border-bottom: 1px solid #f0f0f0 !important;
border-top: none !important;
padding-top: 14px !important;
padding-bottom: 14px !important;
height: 52px !important;
line-height: 22px !important;
vertical-align: middle !important;
}
.status-tag {
display: inline-block;
padding: 2px 16px;
border-radius: 16px;
font-size: 14px;
font-weight: 500;
}
.status-normal {
background: #e8f7ea;
color: #3fc06d;
}
.status-block {
background: #fbeaea;
color: #f56c6c;
}
.approve-btn {
color: #556ff6;
font-weight: 500;
}
.reject-btn {
color: #f56c6c;
font-weight: 500;
margin-left: 8px;
}
</style>

View File

@ -0,0 +1,114 @@
<template>
<div class="rejected-list">
<el-table
v-loading="loading"
:data="tableData"
border={false}
class="custom-table"
header-row-class-name="custom-header"
row-class-name="custom-row"
>
<el-table-column prop="name" label="商户名称" header-align="center" align="center" />
<el-table-column prop="contactName" label="联系人" header-align="center" align="center" />
<el-table-column prop="contactPhone" label="联系电话" header-align="center" align="center" />
<el-table-column prop="contactEmail" label="联系邮箱" header-align="center" align="center" />
<el-table-column prop="address" label="地址" header-align="center" align="center" />
<el-table-column prop="auditAt" label="拒绝时间" header-align="center" align="center" />
<el-table-column prop="rejectReason" label="拒绝原因" header-align="center" align="center" />
<el-table-column prop="status" label="状态" header-align="center" align="center">
<template slot-scope="scope">
<span :class="['status-tag', scope.row.status === 1 ? 'status-normal' : 'status-block']">
{{ scope.row.status === 1 ? '正常' : '禁用' }}
</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { merchantList } from '@/api/merchant'
export default {
name: 'RejectedList',
data() {
return {
loading: false,
tableData: []
}
},
created() {
this.getList()
},
methods: {
async getList() {
try {
this.loading = true
const res = await merchantList({
auditStatus: 3
})
if (res.code === 0) {
this.tableData = res.data.list || []
} else {
this.$message.error(res.message || '获取已拒绝列表失败')
}
} catch (error) {
this.$message.error('获取已拒绝列表失败')
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.rejected-list {
margin-top: 20px;
}
.custom-table {
background: #fff;
border: none;
font-size: 15px;
::v-deep .el-table__body-wrapper td,
::v-deep .el-table__header-wrapper th {
border-right: none !important;
border-left: none !important;
}
}
.custom-header th {
background: #f7f8fa !important;
color: #888;
font-weight: 500;
border-bottom: 1px solid #f0f0f0 !important;
border-top: 1px solid #f0f0f0 !important;
padding-top: 16px !important;
padding-bottom: 16px !important;
height: 56px !important;
line-height: 24px !important;
}
.custom-row td {
border-bottom: 1px solid #f0f0f0 !important;
border-top: none !important;
padding-top: 14px !important;
padding-bottom: 14px !important;
height: 52px !important;
line-height: 22px !important;
vertical-align: middle !important;
}
.status-tag {
display: inline-block;
padding: 2px 16px;
border-radius: 16px;
font-size: 14px;
font-weight: 500;
}
.status-normal {
background: #e8f7ea;
color: #3fc06d;
}
.status-block {
background: #fbeaea;
color: #f56c6c;
}
</style>

View File

@ -0,0 +1,123 @@
<template>
<div class="merchant-list">
<div class="search-area">
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item label="商户名称">
<el-input v-model="searchForm.name" placeholder="请输入商户名称" clearable />
</el-form-item>
<el-form-item label="联系人">
<el-input v-model="searchForm.contactName" placeholder="请输入联系人" clearable />
</el-form-item>
<el-form-item label="联系电话">
<el-input v-model="searchForm.contactPhone" placeholder="请输入联系电话" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
<el-button type="text" class="refresh-btn" @click="handleRefresh">
<i class="el-icon-refresh"></i>
刷新
</el-button>
</el-form-item>
</el-form>
</div>
<div class="status-tabs">
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane label="待审核" name="pending">
<pending-list ref="pendingList" />
</el-tab-pane>
<el-tab-pane label="已通过" name="approved">
<approved-list ref="approvedList" />
</el-tab-pane>
<el-tab-pane label="已拒绝" name="rejected">
<rejected-list ref="rejectedList" />
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import PendingList from './components/pending.vue'
import ApprovedList from './components/approved.vue'
import RejectedList from './components/rejected.vue'
export default {
name: 'MerchantList',
components: {
PendingList,
ApprovedList,
RejectedList
},
data() {
return {
activeTab: 'pending',
searchForm: {
name: '',
contactName: '',
contactPhone: ''
}
}
},
created() {
// 确保默认选中待审核标签页
this.activeTab = 'pending'
},
methods: {
handleSearch() {
this.$refs[`${this.activeTab}List`].getList()
},
handleReset() {
this.searchForm = {
name: '',
contactName: '',
contactPhone: ''
}
this.$refs[`${this.activeTab}List`].getList()
},
handleTabClick(tab) {
this.activeTab = tab.name
this.$refs[`${this.activeTab}List`].getList()
},
handleRefresh() {
this.$refs[`${this.activeTab}List`].getList()
}
}
}
</script>
<style lang="scss" scoped>
.merchant-list {
padding: 20px;
background: #fff;
border-radius: 4px;
}
.search-area {
margin-bottom: 20px;
padding: 20px;
background: #f7f8fa;
border-radius: 4px;
}
.search-form {
display: flex;
flex-wrap: wrap;
gap: 10px;
::v-deep .el-form-item {
margin-bottom: 0;
margin-right: 0;
}
}
.refresh-btn {
margin-left: 8px;
color: #556ff6;
font-weight: 500;
i {
margin-right: 4px;
}
}
.status-tabs {
background: #fff;
padding: 20px;
border-radius: 4px;
}
</style>

View File

View File

@ -0,0 +1,288 @@
<template>
<div class="user-list-page">
<div class="search-row">
<el-input
v-model="searchForm.nickname"
placeholder="搜索用户..."
prefix-icon="el-icon-search"
class="search-input"
@keyup.enter.native="handleSearch"
clearable
/>
<div class="user-action">
<el-button type="primary" class="add-btn" @click="onAdd">添加用户</el-button>
</div>
</div>
<div class="table-card">
<el-table
:data="tableData"
v-loading="tableLoading"
border={false}
class="custom-table"
header-row-class-name="custom-header"
row-class-name="custom-row"
>
<el-table-column label="用户信息" min-width="300">
<template slot-scope="scope">
<div class="user-info-cell">
<el-avatar :size="48" :src="scope.row.avatar">
<span class="avatar-text">{{ scope.row.nickname ? scope.row.nickname.substr(0,1) : '?' }}</span>
</el-avatar>
<div class="user-info-text">
<div class="nickname">{{ scope.row.nickname || '-' }}</div>
<div class="username">{{ scope.row.username || '-' }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="phone" label="手机号" width="300" align="center">
<template slot-scope="scope">
{{ scope.row.phone || '-' }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template slot-scope="scope">
<span :class="['status-tag', scope.row.status === 0 ? 'status-normal' : 'status-block']">
{{ scope.row.status === 0 ? '正常' : '禁用' }}
</span>
</template>
</el-table-column>
<el-table-column prop="birthday" label="注册时间" width="200" align="center">
<template slot-scope="scope">
{{ scope.row.birthday ? scope.row.birthday : '-' }}
</template>
</el-table-column>
<el-table-column prop="lastLoginStoreName" label="最近登录门店" width="200" align="center" />
<el-table-column label="操作" width="120" align="center">
<template slot-scope="scope">
<el-button type="text" size="mini" class="edit-btn" @click="onEdit(scope.row)">编辑</el-button>
<el-button type="text" size="mini" class="delete-btn" @click="onDeleteSingle(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="table-footer">
<div class="table-summary">
显示第 {{ (searchForm.page - 1) * searchForm.pageSize + 1 }}
{{ Math.min(searchForm.page * searchForm.pageSize, total) }} {{ total }} 条记录
</div>
<el-pagination
background
layout="prev, pager, next"
:current-page="searchForm.page"
:page-size="searchForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
class="custom-pagination"
/>
</div>
</div>
</div>
</template>
<script>
import { userList, deleteUser, batchDeleteUsers } from '@/api/user'
export default {
name: 'UserList',
data() {
return {
tableLoading: false,
tableData: [],
total: 0,
searchForm: {
page: 1,
pageSize: 10,
nickname: ''
}
}
},
created() {
this.getList()
},
methods: {
async getList() {
this.tableLoading = true
try {
const res = await userList(this.searchForm)
if (res.code === 0) {
this.tableData = res.data.list
this.total = res.data.total
} else {
this.$message.error(res.message || '获取数据失败')
}
} catch (error) {
this.$message.error('获取数据失败')
console.error(error)
} finally {
this.tableLoading = false
}
},
handleSearch() {
this.searchForm.page = 1
this.getList()
},
handleCurrentChange(val) {
this.searchForm.page = val
this.getList()
},
onAdd() {
// TODO: 实现添加用户弹窗
this.$message.info('添加用户功能待实现')
},
onEdit(row) {
// TODO: 实现编辑功能
this.$message.info('编辑功能待实现')
},
async onDeleteSingle(row) {
try {
await this.$confirm('确认删除该用户吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
const res = await deleteUser(row.id)
if (res.code === 0) {
this.$message.success('删除成功')
this.getList()
} else {
this.$message.error(res.message || '删除失败')
}
} catch (error) {
if (error !== 'cancel') {
this.$message.error('删除失败')
console.error(error)
}
}
}
}
}
</script>
<style lang="scss" scoped>
.user-list-page {
background: #fff;
min-height: 100vh;
padding: 0 32px 32px 32px;
}
.search-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20px;
margin-bottom: 18px;
}
.search-input {
width: 320px;
background: #fff;
border-radius: 6px;
}
.user-action .add-btn {
background: #556ff6;
border: none;
border-radius: 6px;
font-weight: 500;
}
.table-card {
background: #fff;
border-radius: 10px;
box-shadow: none;
padding: 0 0 12px 0;
}
.custom-table {
background: #fff;
border: none;
font-size: 15px;
::v-deep .el-table__body-wrapper td,
::v-deep .el-table__header-wrapper th {
border-right: none !important;
border-left: none !important;
}
}
.custom-header th {
background: #f7f8fa !important;
color: #888;
font-weight: 500;
border-bottom: 1px solid #f0f0f0 !important;
border-top: 1px solid #f0f0f0 !important;
padding-top: 16px !important;
padding-bottom: 16px !important;
height: 56px !important;
line-height: 24px !important;
}
.custom-row td {
border-bottom: 1px solid #f0f0f0 !important;
border-top: none !important;
padding-top: 14px !important;
padding-bottom: 14px !important;
height: 52px !important;
line-height: 22px !important;
vertical-align: middle !important;
}
.user-info-cell {
display: flex;
align-items: center;
}
.user-info-cell .el-avatar {
margin-right: 16px;
background: #e3e6f0;
color: #556ff6;
font-weight: bold;
}
.user-info-text .nickname {
font-weight: 600;
color: #222;
font-size: 16px;
}
.user-info-text .username {
color: #888;
font-size: 13px;
margin-top: 2px;
}
.status-tag {
display: inline-block;
padding: 2px 16px;
border-radius: 16px;
font-size: 14px;
font-weight: 500;
background: #e8f7ea;
color: #3fc06d;
}
.status-block {
background: #fbeaea;
color: #f56c6c;
}
.table-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 8px;
padding: 0 12px;
}
.table-summary {
color: #888;
font-size: 13px;
}
.custom-pagination {
.el-pager li {
border-radius: 6px;
min-width: 32px;
height: 32px;
line-height: 32px;
font-size: 15px;
}
}
.edit-btn {
color: #556ff6;
font-weight: 500;
}
.delete-btn {
color: #f56c6c;
font-weight: 500;
margin-left: 8px;
}
::v-deep .el-breadcrumb__inner,
::v-deep .el-breadcrumb__inner a {
font-size: 18px !important;
font-weight: 500;
}
</style>