🎨 新增vip用户管理以及讲师包月服务管理功能
This commit is contained in:
39
src/api/goods/teacherVip.js
Normal file
39
src/api/goods/teacherVip.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import service from '@/utils/request'
|
||||
|
||||
|
||||
export const list = (params) => {
|
||||
return service({
|
||||
url: '/app_user/teacher_vips',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const add = (data) => {
|
||||
return service({
|
||||
url: '/app_user/teacher_vip',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const edit = (data) => {
|
||||
return service({
|
||||
url: '/app_user/teacher_vip',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const del = (data) => {
|
||||
return service({
|
||||
url: '/app_user/teacher_vip',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const detail = (params) => {
|
||||
return service({
|
||||
url: '/app_user/teacher_vip',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
@@ -84,4 +84,23 @@ export const getLoginInfo = (params) => {
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveUserVip
|
||||
export const removeUserVip = (data) => {
|
||||
return service({
|
||||
url: '/app_user/vip',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// getVipUserList
|
||||
export const getVipUserList = (params) => {
|
||||
return service({
|
||||
url: '/app_user/vip/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
@@ -292,6 +292,90 @@ export const ORDER_TABLE_CONFIG = {
|
||||
]
|
||||
}
|
||||
|
||||
// VIP用户搜索配置
|
||||
export const VIP_USER_SEARCH_CONFIG = [
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'user_id',
|
||||
label: '用户ID',
|
||||
placeholder: '请输入用户ID',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'name',
|
||||
label: '用户名称',
|
||||
placeholder: '请输入用户名称',
|
||||
}
|
||||
]
|
||||
|
||||
// VIP用户表格配置
|
||||
export const VIP_USER_TABLE_CONFIG = {
|
||||
index: true,
|
||||
schemes: [
|
||||
{
|
||||
attrs: {
|
||||
label: '头像',
|
||||
prop: 'avatar',
|
||||
align: 'center'
|
||||
},
|
||||
slot: 'avatar'
|
||||
},
|
||||
{
|
||||
attrs: {
|
||||
label: '用户ID',
|
||||
prop: 'id',
|
||||
align: 'center'
|
||||
}
|
||||
},
|
||||
{
|
||||
attrs: {
|
||||
label: '用户名称',
|
||||
prop: 'nick_name',
|
||||
align: 'center'
|
||||
}
|
||||
},
|
||||
{
|
||||
attrs: {
|
||||
label: '手机号',
|
||||
prop: 'phone',
|
||||
align: 'center'
|
||||
}
|
||||
},
|
||||
{
|
||||
attrs: {
|
||||
label: 'VIP等级',
|
||||
prop: 'user_label',
|
||||
align: 'center'
|
||||
},
|
||||
slot: 'user_label'
|
||||
},
|
||||
{
|
||||
attrs: {
|
||||
label: 'VIP到期时间',
|
||||
prop: 'vip_expire_time',
|
||||
align: 'center'
|
||||
},
|
||||
slot: 'vip_expire_time'
|
||||
},
|
||||
{
|
||||
attrs: {
|
||||
label: '账户余额',
|
||||
prop: 'balance',
|
||||
align: 'center'
|
||||
},
|
||||
slot: 'balance'
|
||||
},
|
||||
{
|
||||
attrs: {
|
||||
label: '操作',
|
||||
prop: 'operate',
|
||||
align: 'center'
|
||||
},
|
||||
slot: 'operate'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 用户登录日志搜索配置
|
||||
export const LOGIN_LOG_SEARCH_CONFIG = [
|
||||
{
|
||||
@@ -371,17 +455,11 @@ export const LOGIN_LOG_TABLE_CONFIG = {
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 用户列表
|
||||
export const USER_TABLE_CONFIG = {
|
||||
index: true,
|
||||
schemes: [
|
||||
{
|
||||
attrs: {
|
||||
label: 'ID',
|
||||
prop: 'ID',
|
||||
align: 'center'
|
||||
}
|
||||
},
|
||||
{
|
||||
attrs: {
|
||||
label: '头像',
|
||||
@@ -505,4 +583,4 @@ export const ARTICLE_TABLE_CONFIG = {
|
||||
slot: 'operate'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@@ -90,7 +90,9 @@
|
||||
"/src/view/systemTools/version/version.vue": "SysVersion",
|
||||
"/src/view/user/index.vue": "UserManage",
|
||||
"/src/view/user/user/index.vue": "Index",
|
||||
"/src/view/user/user/loginLog.vue": "LoginLog",
|
||||
"/src/view/user/user/teacherApply.vue": "TeacherApply",
|
||||
"/src/view/user/user/vipUser.vue": "VipUser",
|
||||
"/src/plugin/announcement/form/info.vue": "InfoForm",
|
||||
"/src/plugin/announcement/view/info.vue": "Info",
|
||||
"/src/plugin/customerservice/view/chat/index.vue": "ServiceMain",
|
||||
|
279
src/view/goods/teacher_vip/index.vue
Normal file
279
src/view/goods/teacher_vip/index.vue
Normal file
@@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="gva-search-box">
|
||||
<el-form :inline="true" :model="searchInfo" class="demo-form-inline" @keyup.enter="onSubmit">
|
||||
<el-form-item label="讲师">
|
||||
<div style="display:flex;gap:8px;align-items:center">
|
||||
<el-input v-model="searchInfo.teacher_name" placeholder="请选择讲师" clearable style="width: 220px" readonly />
|
||||
<el-button type="primary" plain @click="openTeacherChoose('search')">选择讲师</el-button>
|
||||
<el-button @click="clearTeacher('search')">清空</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="search" @click="onSubmit">查询</el-button>
|
||||
<el-button icon="refresh" @click="onReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="gva-table-box">
|
||||
<div class="gva-btn-list">
|
||||
<el-button type="primary" icon="plus" @click="openEdit('create')">新增</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
ref="multipleTable"
|
||||
style="width: 100%"
|
||||
tooltip-effect="dark"
|
||||
:data="tableData"
|
||||
row-key="ID"
|
||||
>
|
||||
<el-table-column type="index" label="#" width="60" />
|
||||
<el-table-column align="left" label="ID" prop="ID" width="100" />
|
||||
<el-table-column align="left" label="标题" prop="title" min-width="160" />
|
||||
<el-table-column align="left" label="讲师ID" prop="teacher_id" width="110" />
|
||||
<el-table-column align="left" label="讲师" prop="teacher_name" min-width="140" />
|
||||
<el-table-column align="left" label="头像" width="120">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
v-if="scope.row.avatar"
|
||||
style="width: 60px; height: 60px"
|
||||
:src="scope.row.avatar"
|
||||
:preview-src-list="[scope.row.avatar]"
|
||||
fit="cover"
|
||||
/>
|
||||
<span v-else>无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" label="价格" prop="price" width="120">
|
||||
<template #default="{ row }">
|
||||
{{ formatPrice(row.price) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" label="更新时间" prop="UpdatedAt" width="180">
|
||||
<template #default="{ row }">{{ formatDate(row.UpdatedAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" label="描述" prop="desc" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column align="left" label="操作" fixed="right" width="160">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="openEdit('update', row)">编辑</el-button>
|
||||
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="gva-pagination">
|
||||
<el-pagination
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:current-page="page"
|
||||
:page-size="pageSize"
|
||||
:page-sizes="[10, 30, 50, 100]"
|
||||
:total="total"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 选择讲师弹窗 -->
|
||||
<userChoose ref="userChooseRef" title="选择讲师人员" @getRecipientInfo="onChooseTeacher" />
|
||||
|
||||
<!-- 新增/编辑 抽屉 -->
|
||||
<el-drawer destroy-on-close :size="'520px'" v-model="dialogVisible" :show-close="false" :before-close="closeDialog">
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-lg">{{ editType==='create'?'新增讲师包月':'编辑讲师包月' }}</span>
|
||||
<div>
|
||||
<el-button :loading="btnLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="closeDialog">取 消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="formData" ref="formRef" label-position="top" :rules="rules">
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="formData.title" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="讲师" prop="teacher_id">
|
||||
<div style="display:flex;gap:8px;align-items:center;width:100%">
|
||||
<el-input v-model="formData.teacher_name" placeholder="请选择讲师" readonly />
|
||||
<el-button type="primary" plain @click="openTeacherChoose('form')">选择讲师</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="价格(元)" prop="price">
|
||||
<el-input v-model.number="formData.price" type="number" min="0" placeholder="请输入价格" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" prop="desc">
|
||||
<el-input v-model="formData.desc" type="textarea" :rows="4" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { list as getTeacherVipList, add as createTeacherVip, edit as updateTeacherVip, del as deleteTeacherVip, detail as getDetail } from '@/api/goods/teacherVip'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import { formatDate } from '@/utils/format'
|
||||
import userChoose from '@/components/userChoose/index.vue'
|
||||
|
||||
const page = ref(1)
|
||||
const total = ref(0)
|
||||
const pageSize = ref(10)
|
||||
const tableData = ref([])
|
||||
const searchInfo = ref({ teacher_id: undefined, teacher_name: '' })
|
||||
|
||||
// 讲师选择
|
||||
const userChooseRef = ref()
|
||||
const chooseScene = ref('search')
|
||||
const openTeacherChoose = (scene) => {
|
||||
chooseScene.value = scene
|
||||
userChooseRef.value && userChooseRef.value.open()
|
||||
}
|
||||
const clearTeacher = (scene) => {
|
||||
if (scene === 'search') {
|
||||
searchInfo.value.teacher_id = undefined
|
||||
searchInfo.value.teacher_name = ''
|
||||
} else {
|
||||
formData.value.teacher_id = undefined
|
||||
formData.value.teacher_name = ''
|
||||
}
|
||||
}
|
||||
const onChooseTeacher = (data) => {
|
||||
const id = data.id || data.ID
|
||||
const name = data.nick_name || data.username || data.name
|
||||
if (chooseScene.value === 'search') {
|
||||
searchInfo.value.teacher_id = id
|
||||
searchInfo.value.teacher_name = name
|
||||
} else {
|
||||
formData.value.teacher_id = id
|
||||
formData.value.teacher_name = name
|
||||
}
|
||||
}
|
||||
|
||||
const formatPrice = (val) => {
|
||||
if (val === null || val === undefined) return '-'
|
||||
const yuan = Number(val) / 100
|
||||
return `${yuan.toFixed(2)} 元`
|
||||
}
|
||||
|
||||
const onReset = () => {
|
||||
searchInfo.value = { teacher_id: undefined }
|
||||
page.value = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
page.value = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
page.value = val
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const getTableData = async () => {
|
||||
const params = { page: page.value, pageSize: pageSize.value }
|
||||
if (searchInfo.value.teacher_id !== undefined && searchInfo.value.teacher_id !== '' && searchInfo.value.teacher_id !== null) {
|
||||
params.teacher_id = searchInfo.value.teacher_id
|
||||
}
|
||||
const res = await getTeacherVipList(params)
|
||||
if (res.code === 0) {
|
||||
tableData.value = res.data.list || []
|
||||
total.value = res.data.total || 0
|
||||
page.value = res.data.page || page.value
|
||||
pageSize.value = res.data.pageSize || pageSize.value
|
||||
}
|
||||
}
|
||||
|
||||
getTableData()
|
||||
|
||||
// ============== 新增/编辑/删除 ==============
|
||||
const dialogVisible = ref(false)
|
||||
const formRef = ref()
|
||||
const btnLoading = ref(false)
|
||||
const editType = ref('create')
|
||||
const formData = ref({
|
||||
ID: undefined,
|
||||
title: '',
|
||||
teacher_id: undefined,
|
||||
teacher_name: '',
|
||||
price: 0,
|
||||
desc: ''
|
||||
})
|
||||
const rules = {
|
||||
title: [{ required: true, message: '请输入标题', trigger: ['blur','input'] }],
|
||||
teacher_id: [{ required: true, message: '请选择讲师', trigger: ['change','blur'] }],
|
||||
price: [{ required: true, message: '请输入价格', trigger: ['blur','input'] }]
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
formData.value = { ID: undefined, title: '', teacher_id: undefined, teacher_name: '', price: 0, desc: '' }
|
||||
}
|
||||
|
||||
const openEdit = async(type, row) => {
|
||||
editType.value = type
|
||||
if (type === 'update' && row?.ID) {
|
||||
const res = await getDetail({ id: row.ID })
|
||||
if (res.code === 0) {
|
||||
const data = { ...res.data }
|
||||
if (data.price !== null && data.price !== undefined) {
|
||||
data.price = Number(data.price) / 100
|
||||
}
|
||||
formData.value = { ...formData.value, ...data }
|
||||
}
|
||||
} else {
|
||||
resetForm()
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const closeDialog = () => {
|
||||
dialogVisible.value = false
|
||||
resetForm()
|
||||
}
|
||||
|
||||
const submitForm = () => {
|
||||
formRef.value?.validate(async(valid)=>{
|
||||
if (!valid) return
|
||||
btnLoading.value = true
|
||||
const payload = { ...formData.value }
|
||||
// 元 -> 分
|
||||
if (payload.price !== null && payload.price !== undefined) {
|
||||
payload.price = Math.round(Number(payload.price) * 100)
|
||||
}
|
||||
let res
|
||||
if (editType.value === 'create') {
|
||||
res = await createTeacherVip(payload)
|
||||
} else {
|
||||
res = await updateTeacherVip(payload)
|
||||
}
|
||||
btnLoading.value = false
|
||||
if (res && res.code === 0) {
|
||||
ElMessage.success('保存成功')
|
||||
closeDialog()
|
||||
getTableData()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleDelete = async(row) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除该记录吗?','提示',{ type:'warning' })
|
||||
const res = await deleteTeacherVip({ ID: row.ID })
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('删除成功')
|
||||
getTableData()
|
||||
}
|
||||
} catch { /* 用户取消 */ }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
106
src/view/user/user/loginLog.vue
Normal file
106
src/view/user/user/loginLog.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="searchForm">
|
||||
<searchForm
|
||||
:search="LOGIN_LOG_SEARCH_CONFIG"
|
||||
@searchData="searchData"
|
||||
@resetData="resetData"
|
||||
class="search-box searchForm"
|
||||
/>
|
||||
</div>
|
||||
<div class="gva-table-box">
|
||||
<Content
|
||||
@changePage="changePage"
|
||||
:total="total"
|
||||
v-model:tabloading="tableLoading"
|
||||
v-model:currentPage="queryParams.page"
|
||||
v-model:pageSize="queryParams.pageSize"
|
||||
:data="tableData"
|
||||
:config="LOGIN_LOG_TABLE_CONFIG"
|
||||
>
|
||||
<template #login_time="{ row }">
|
||||
{{ row.login_time }}
|
||||
</template>
|
||||
</Content>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ElMessage } from 'element-plus'
|
||||
import searchForm from '@/components/searchForm/index.vue'
|
||||
import Content from '@/components/content/index.vue'
|
||||
import { getLoginInfo } from '@/api/user/index.js'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { LOGIN_LOG_SEARCH_CONFIG, LOGIN_LOG_TABLE_CONFIG } from '@/config'
|
||||
|
||||
const tableLoading = ref(true)
|
||||
const tableData = ref([])
|
||||
const queryParams = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
user_id: '',
|
||||
user_name: ''
|
||||
})
|
||||
const total = ref(0)
|
||||
|
||||
// 搜索数据
|
||||
const searchData = (data) => {
|
||||
queryParams.value.page = 1
|
||||
queryParams.value = { ...queryParams.value, ...data }
|
||||
getList()
|
||||
}
|
||||
|
||||
// 重置数据
|
||||
const resetData = () => {
|
||||
queryParams.value = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
user_id: '',
|
||||
user_name: ''
|
||||
}
|
||||
getList()
|
||||
}
|
||||
|
||||
// 获取登录日志列表
|
||||
async function getList() {
|
||||
try {
|
||||
tableLoading.value = true
|
||||
const res = await getLoginInfo(queryParams.value)
|
||||
if (res.code === 0) {
|
||||
tableData.value = res.data.list || []
|
||||
total.value = res.data.total || 0
|
||||
} else {
|
||||
ElMessage.error(res.msg || '获取登录日志失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取登录日志失败:', error)
|
||||
ElMessage.error('获取登录日志失败')
|
||||
} finally {
|
||||
tableLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
function changePage(data) {
|
||||
queryParams.value.page = data
|
||||
getList()
|
||||
}
|
||||
|
||||
// 页面加载时获取数据
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.searchForm {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.gva-table-box {
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
186
src/view/user/user/vipUser.vue
Normal file
186
src/view/user/user/vipUser.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="searchForm">
|
||||
<searchForm
|
||||
:search="VIP_USER_SEARCH_CONFIG"
|
||||
@searchData="searchData"
|
||||
@resetData="resetData"
|
||||
class="search-box searchForm"
|
||||
/>
|
||||
</div>
|
||||
<div class="gva-table-box">
|
||||
<Content
|
||||
@changePage="changePage"
|
||||
:total="total"
|
||||
v-model:tabloading="tableLoading"
|
||||
v-model:currentPage="queryParams.page"
|
||||
v-model:pageSize="queryParams.pageSize"
|
||||
:data="tableData"
|
||||
:config="VIP_USER_TABLE_CONFIG"
|
||||
>
|
||||
<template #avatar="{ row }">
|
||||
<el-image
|
||||
style="width: 60px; height: 60px"
|
||||
:src="row.avatar"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="[row.avatar]"
|
||||
show-progress
|
||||
:initial-index="4"
|
||||
fit="cover"
|
||||
/>
|
||||
</template>
|
||||
<template #user_label="{ row }">
|
||||
<el-tag :type="getVipLabelType(row.user_label)">
|
||||
{{ getVipLabelText(row.user_label) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #vip_expire_time="{ row }">
|
||||
{{ row.vip_expire_time || '-' }}
|
||||
</template>
|
||||
<template #balance="{ row }">
|
||||
{{ formatBalance(row.balance) }}
|
||||
</template>
|
||||
<template #operate="{ row }">
|
||||
<el-button type="danger" link @click="handleRemoveVip(row)">
|
||||
移除VIP
|
||||
</el-button>
|
||||
</template>
|
||||
</Content>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import searchForm from '@/components/searchForm/index.vue'
|
||||
import Content from '@/components/content/index.vue'
|
||||
import { getVipUserList, removeUserVip } from '@/api/user/index.js'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { VIP_USER_SEARCH_CONFIG, VIP_USER_TABLE_CONFIG } from '@/config'
|
||||
|
||||
const tableLoading = ref(true)
|
||||
const tableData = ref([])
|
||||
const queryParams = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
user_id: '',
|
||||
name: ''
|
||||
})
|
||||
const total = ref(0)
|
||||
|
||||
// 搜索数据
|
||||
const searchData = (data) => {
|
||||
queryParams.value.page = 1
|
||||
queryParams.value = { ...queryParams.value, ...data }
|
||||
getList()
|
||||
}
|
||||
|
||||
// 重置数据
|
||||
const resetData = () => {
|
||||
queryParams.value = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
user_id: '',
|
||||
name: ''
|
||||
}
|
||||
getList()
|
||||
}
|
||||
|
||||
// 获取VIP用户列表
|
||||
async function getList() {
|
||||
try {
|
||||
tableLoading.value = true
|
||||
const res = await getVipUserList(queryParams.value)
|
||||
if (res.code === 0) {
|
||||
tableData.value = res.data.list || []
|
||||
total.value = res.data.total || 0
|
||||
} else {
|
||||
ElMessage.error(res.msg || '获取VIP用户列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取VIP用户列表失败:', error)
|
||||
ElMessage.error('获取VIP用户列表失败')
|
||||
} finally {
|
||||
tableLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
function changePage(data) {
|
||||
queryParams.value.page = data
|
||||
getList()
|
||||
}
|
||||
|
||||
// 格式化余额显示
|
||||
function formatBalance(balance) {
|
||||
if (!balance || balance === 0) return '0.00元'
|
||||
return (balance / 100).toFixed(2) + '元'
|
||||
}
|
||||
|
||||
// 获取VIP等级标签类型
|
||||
function getVipLabelType(userLabel) {
|
||||
const typeMap = {
|
||||
1: 'info', // 注册会员
|
||||
2: 'warning', // VIP
|
||||
3: 'success' // SVIP
|
||||
}
|
||||
return typeMap[userLabel] || 'info'
|
||||
}
|
||||
|
||||
// 获取VIP等级文本
|
||||
function getVipLabelText(userLabel) {
|
||||
const textMap = {
|
||||
1: '注册会员',
|
||||
2: 'VIP',
|
||||
3: 'SVIP'
|
||||
}
|
||||
return textMap[userLabel] || '未知'
|
||||
}
|
||||
|
||||
// 移除用户VIP
|
||||
async function handleRemoveVip(row) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要移除用户 "${row.nick_name}" 的VIP身份吗?此操作不可撤销。`,
|
||||
'确认移除VIP',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
|
||||
const res = await removeUserVip({ id: row.id })
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('移除VIP成功')
|
||||
getList() // 刷新列表
|
||||
} else {
|
||||
ElMessage.error(res.msg || '移除VIP失败')
|
||||
}
|
||||
} catch (error) {
|
||||
// 取消操作时不需要提示
|
||||
if (error !== 'cancel') {
|
||||
ElMessage.error('移除VIP失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载时获取数据
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.searchForm {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.gva-table-box {
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user