管理端界面优化

This commit is contained in:
cxytw 2025-12-26 17:55:01 +08:00
parent f4c9a42974
commit 148297e8d2
42 changed files with 1671 additions and 859 deletions

View File

@ -2,8 +2,9 @@
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
"@/*": ["src/*"]
},
"typeRoots": []
},
"exclude": ["node_modules", "dist"]
}

View File

@ -4,28 +4,54 @@ import request from '@/utils/request'
* 评论管理 API
*/
// 动态评论列表
export function dynamicCommentListApi(params) {
// 评论列表
export function commentListApi(params) {
return request({
url: '/admin/comment/dynamic/list',
url: '/admin/comment/list',
method: 'get',
params
})
}
// 删除动态评论
export function dynamicCommentDeleteApi(id) {
// 评论详情
export function commentDetailApi(id) {
return request({
url: `/admin/comment/dynamic/delete/${id}`,
url: `/admin/comment/detail/${id}`,
method: 'get'
})
}
// 删除评论
export function commentDeleteApi(id) {
return request({
url: `/admin/comment/delete/${id}`,
method: 'post'
})
}
// 动态评论回复列表
export function commentReplyListApi(params) {
// 批量删除评论
export function commentBatchDeleteApi(ids) {
return request({
url: '/admin/comment/reply/list',
method: 'get',
params
url: '/admin/comment/batch-delete',
method: 'post',
data: { ids }
})
}
// 审核评论
export function commentAuditApi(id, data) {
return request({
url: `/admin/comment/audit/${id}`,
method: 'post',
data
})
}
// 回复评论
export function commentReplyApi(id, data) {
return request({
url: `/admin/comment/reply/${id}`,
method: 'post',
data
})
}

View File

@ -72,6 +72,7 @@ import fanGroupRouter from './modules/fanGroup';
import detailRouter from './modules/detail';
import sessionRouter from './modules/session';
import authRouter from './modules/auth';
import appealRouter from './modules/appeal';
/**
* Note: sub-menu only appear when route children.length >= 1
@ -210,6 +211,8 @@ export const constantRoutes = [
sessionRouter,
// 认证管理
authRouter,
// 申诉管理
appealRouter,
{
path: '/404',
component: () => import('@/views/error-page/404'),

View File

@ -5,7 +5,7 @@ const authRouter = {
component: Layout,
redirect: '/auth/car',
name: 'Auth',
alwaysShow: true,
alwaysShow: false,
meta: { title: '认证管理', icon: 'el-icon-s-check' },
children: [
{

View File

@ -1,11 +1,15 @@
import Layout from '@/layout';
/**
* 轮播图管理路由
* 优化单子菜单不展开
*/
const bannerRouter = {
path: '/banner',
component: Layout,
redirect: '/banner/list',
name: 'Banner',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '轮播图管理',
icon: 'el-icon-picture',
@ -15,7 +19,7 @@ const bannerRouter = {
path: '',
component: () => import('@/views/banner/index'),
name: 'BannerList',
meta: { title: '轮播图列表', icon: '' },
meta: { title: '轮播图管理', icon: 'el-icon-picture' },
},
],
};

View File

@ -8,7 +8,7 @@ const charmRouter = {
component: Layout,
redirect: '/charm/level',
name: 'Charm',
alwaysShow: true,
alwaysShow: false,
meta: { title: '魅力值管理', icon: 'el-icon-medal' },
children: [
{

View File

@ -1,11 +1,15 @@
import Layout from '@/layout'
/**
* 聊天付费配置路由
* 优化单子菜单不展开
*/
const chatPayConfigRouter = {
path: '/chatPayConfig',
component: Layout,
redirect: '/chatPayConfig/list',
name: 'ChatPayConfig',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '聊天付费配置',
icon: 'clipboard'
@ -16,8 +20,8 @@ const chatPayConfigRouter = {
name: 'ChatPayConfigList',
component: () => import('@/views/chatPayConfig/list/index'),
meta: {
title: '配置列表',
icon: ''
title: '聊天付费配置',
icon: 'clipboard'
}
}
]

View File

@ -1,11 +1,15 @@
import Layout from '@/layout'
/**
* 聊天常用语路由
* 优化单子菜单不展开
*/
const chatPhraseRouter = {
path: '/chatPhrase',
component: Layout,
redirect: '/chatPhrase/list',
name: 'ChatPhrase',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '聊天常用语',
icon: 'clipboard'
@ -16,8 +20,8 @@ const chatPhraseRouter = {
name: 'ChatPhraseList',
component: () => import('@/views/chatphrase/list/index'),
meta: {
title: '常用语列表',
icon: ''
title: '聊天常用语',
icon: 'clipboard'
}
}
]

View File

@ -8,7 +8,7 @@ const clientVersionRouter = {
component: Layout,
redirect: '/clientversion/list',
name: 'ClientVersion',
alwaysShow: true,
alwaysShow: false,
meta: { title: '客户端版本管理', icon: 'phone' },
children: [
{

View File

@ -8,7 +8,7 @@ const configRouter = {
component: Layout,
redirect: '/config/params',
name: 'Config',
alwaysShow: true,
alwaysShow: false,
meta: { title: '配置管理', icon: 'setting' },
children: [
{

View File

@ -5,7 +5,7 @@ const coupleRouter = {
component: Layout,
redirect: '/couple/matchText',
name: 'Couple',
alwaysShow: true,
alwaysShow: false,
meta: { title: '夫妻相管理', icon: 'peoples' },
children: [
{

View File

@ -5,7 +5,7 @@ const dynamicRouter = {
component: Layout,
redirect: '/dynamic/list',
name: 'Dynamic',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '动态管理',
icon: 'clipboard'

View File

@ -8,7 +8,7 @@ const exchangeRouter = {
component: Layout,
redirect: '/exchange/record',
name: 'Exchange',
alwaysShow: true,
alwaysShow: false,
meta: { title: '兑换管理', icon: 'money' },
children: [
{

View File

@ -1,18 +1,22 @@
import Layout from '@/layout';
/**
* 粉丝团管理路由
* 优化单子菜单不展开
*/
const fanGroupRouter = {
path: '/fanGroup',
component: Layout,
redirect: '/fanGroup/list',
name: 'FanGroup',
alwaysShow: true,
alwaysShow: false,
meta: { title: '粉丝团管理', icon: 'el-icon-star-on' },
children: [
{
path: 'list',
component: () => import('@/views/fanGroup/list/index'),
name: 'FanGroupList',
meta: { title: '粉丝团列表', icon: '' }
meta: { title: '粉丝团管理', icon: 'el-icon-star-on' }
}
]
};

View File

@ -5,7 +5,7 @@ const followRouter = {
component: Layout,
redirect: '/follow/record',
name: 'FollowManage',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '关注管理',
icon: 'el-icon-user-solid',

View File

@ -5,7 +5,7 @@ const giftRewardRouter = {
component: Layout,
redirect: '/giftreward/record',
name: 'GiftReward',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '礼物打赏管理',
icon: 'el-icon-present',

View File

@ -1,13 +1,17 @@
import Layout from '@/layout'
/**
* 金币兑换钻石配置路由
* 优化单子菜单不展开简化标题
*/
const goldDiamondConfigRouter = {
path: '/goldDiamondConfig',
component: Layout,
redirect: '/goldDiamondConfig/list',
name: 'GoldDiamondConfig',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '金币兑换钻石配置',
title: '金币钻石配置',
icon: 'clipboard'
},
children: [
@ -16,8 +20,8 @@ const goldDiamondConfigRouter = {
name: 'GoldDiamondConfigList',
component: () => import('@/views/goldDiamondConfig/list/index'),
meta: {
title: '配置列表',
icon: ''
title: '金币钻石配置',
icon: 'clipboard'
}
}
]

View File

@ -1,11 +1,15 @@
import Layout from '@/layout';
/**
* 头饰管理路由
* 优化单子菜单不展开
*/
const headwearRouter = {
path: '/headwear',
component: Layout,
redirect: '/headwear/shop',
name: 'Headwear',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '头饰管理',
icon: 'el-icon-magic-stick',
@ -15,7 +19,7 @@ const headwearRouter = {
path: 'shop',
component: () => import('@/views/headwear/index'),
name: 'HeadwearShop',
meta: { title: '礼物商城管理', icon: '' },
meta: { title: '头饰管理', icon: 'el-icon-magic-stick' },
},
],
};

View File

@ -5,7 +5,7 @@ const interactRouter = {
component: Layout,
redirect: '/interact/index',
name: 'Interact',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '互动管理',
icon: 'el-icon-chat-dot-round',

View File

@ -1,11 +1,15 @@
import Layout from '@/layout';
/**
* 邀请管理路由
* 优化单子菜单不展开
*/
const inviteRouter = {
path: '/invite',
component: Layout,
redirect: '/invite/list',
name: 'Invite',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '邀请管理',
icon: 'el-icon-share',
@ -15,7 +19,7 @@ const inviteRouter = {
path: 'list',
component: () => import('@/views/invite/list/index'),
name: 'InviteList',
meta: { title: '邀请列表', icon: '' },
meta: { title: '邀请管理', icon: 'el-icon-share' },
},
],
};

View File

@ -5,7 +5,7 @@ const levelRouter = {
component: Layout,
redirect: '/level/noble/list',
name: 'Level',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '贵族等级管理',
icon: 'el-icon-trophy',

View File

@ -8,7 +8,7 @@ const memberRouter = {
component: Layout,
redirect: '/member/package',
name: 'Member',
alwaysShow: true,
alwaysShow: false,
meta: { title: '会员管理', icon: 'el-icon-user' },
children: [
{

View File

@ -8,7 +8,7 @@ const mountPurchaseRouter = {
component: Layout,
redirect: '/mountpurchase/record',
name: 'MountPurchase',
alwaysShow: true,
alwaysShow: false,
meta: { title: '购买坐骑管理', icon: 'trophy' },
children: [
{

View File

@ -15,7 +15,7 @@ const orderRouter = {
component: Layout,
redirect: '/order/index',
name: 'Order',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '订单管理',
icon: 'clipboard',

View File

@ -5,7 +5,7 @@ const orderManageRouter = {
component: Layout,
redirect: '/orderManage/list',
name: 'OrderManage',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '订单管理',
icon: 'clipboard'

View File

@ -15,7 +15,7 @@ const platformActivityRouter = {
component: Layout,
redirect: '/activity/list',
name: 'PlatformActivityManage',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '平台活动管理',
icon: 'clipboard',

View File

@ -1,18 +1,22 @@
import Layout from '@/layout';
/**
* 敏感词管理路由
* 优化单子菜单不展开
*/
const sensitiveWordRouter = {
path: '/sensitiveWord',
component: Layout,
redirect: '/sensitiveWord/list',
name: 'SensitiveWord',
alwaysShow: true,
alwaysShow: false,
meta: { title: '敏感词管理', icon: 'el-icon-warning' },
children: [
{
path: 'list',
component: () => import('@/views/sensitiveWord/list/index'),
name: 'SensitiveWordList',
meta: { title: '敏感词列表', icon: '' }
meta: { title: '敏感词管理', icon: 'el-icon-warning' }
}
]
};

View File

@ -2,13 +2,14 @@ import Layout from '@/layout';
/**
* 会话管理路由
* 优化单子菜单不展开
*/
const sessionRouter = {
path: '/session',
component: Layout,
redirect: '/session/list',
name: 'Session',
alwaysShow: true,
alwaysShow: false,
meta: { title: '会话管理', icon: 'message' },
children: [
{
@ -16,8 +17,8 @@ const sessionRouter = {
component: () => import('@/views/session/list/index'),
name: 'SessionList',
meta: {
title: '会话列表',
icon: ''
title: '会话管理',
icon: 'message'
}
}
]

View File

@ -1,13 +1,17 @@
import Layout from '@/layout';
/**
* 社会管理路由
* 优化单子菜单不展开
*/
const socialRouter = {
path: '/social',
component: Layout,
redirect: '/social/dynamic',
name: 'Social',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '社会管理',
title: '社会动态',
icon: 'el-icon-user',
},
children: [
@ -15,7 +19,7 @@ const socialRouter = {
path: 'dynamic',
component: () => import('@/views/newtask/index'),
name: 'SocialDynamic',
meta: { title: '社会动态管理', icon: '' },
meta: { title: '社会动态', icon: 'el-icon-user' },
},
],
};

View File

@ -1,13 +1,17 @@
import Layout from '@/layout';
/**
* 系统消息管理路由
* 优化单子菜单不展开
*/
const systemMessageRouter = {
path: '/systemMessage',
component: Layout,
redirect: '/systemMessage/list',
name: 'SystemMessage',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '系统消息管理',
title: '系统消息',
icon: 'message',
},
children: [
@ -15,7 +19,7 @@ const systemMessageRouter = {
path: 'list',
component: () => import('@/views/systemMessage/list/index'),
name: 'SystemMessageList',
meta: { title: '系统消息列表', icon: '' },
meta: { title: '系统消息', icon: 'message' },
},
],
};

View File

@ -5,7 +5,7 @@ const verifycodeRouter = {
component: Layout,
redirect: '/verifycode/index',
name: 'VerifyCode',
alwaysShow: true,
alwaysShow: false,
meta: {
title: '验证码管理',
icon: 'el-icon-key',

View File

@ -2,21 +2,22 @@ import Layout from '@/layout';
/**
* 提现管理路由
* 已优化合并待审核/已审核为统一界面
*/
const withdrawRouter = {
path: '/withdraw',
component: Layout,
redirect: '/withdraw/pending',
redirect: '/withdraw/index',
name: 'Withdraw',
alwaysShow: true,
meta: { title: '提现管理', icon: 'bank-card' },
children: [
{
path: 'pending',
component: () => import('@/views/withdraw/pending/index'),
name: 'WithdrawPending',
path: 'index',
component: () => import('@/views/withdraw/index'),
name: 'WithdrawManage',
meta: {
title: '提现审核列表',
title: '提现审核',
icon: ''
}
},
@ -25,16 +26,7 @@ const withdrawRouter = {
component: () => import('@/views/withdraw/amount/index'),
name: 'WithdrawAmount',
meta: {
title: '提现金额管理',
icon: ''
}
},
{
path: 'approved',
component: () => import('@/views/withdraw/approved/index'),
name: 'WithdrawApproved',
meta: {
title: '提现已审核列表',
title: '提现金额配置',
icon: ''
}
}

View File

@ -105,7 +105,7 @@ body,
}
.layout-aside-width-default {
width: 206px !important;
width: 220px !important;
transition: width 0.2s ease;
box-shadow: 1px 1px 4px rgba(0, 21, 41, 0.08);
}

View File

@ -2,102 +2,183 @@
<div class="divBox">
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<el-page-header @back="goBack" content="申诉管理"></el-page-header>
<div class="mt20">
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="关键词:">
<el-input v-model="searchForm.keywords" placeholder="请输入关键词" clearable class="selWidth" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<div class="mb20">
<el-button type="primary" size="small" @click="handleAdd">新增</el-button>
</div>
<el-table :data="tableData" v-loading="loading" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="name" label="名称" min-width="120" />
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status ? 'success' : 'danger'">
{{ scope.row.status ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
<!-- 搜索区域 -->
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="申诉人">
<el-input v-model="searchForm.userName" placeholder="用户名/手机号" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="申诉类型">
<el-select v-model="searchForm.type" placeholder="全部" clearable style="width: 120px">
<el-option label="全部" value="" />
<el-option label="封号申诉" value="ban" />
<el-option label="禁言申诉" value="mute" />
<el-option label="处罚申诉" value="punish" />
<el-option label="其他" value="other" />
</el-select>
</el-form-item>
<el-form-item label="处理状态">
<el-select v-model="searchForm.status" placeholder="全部" clearable style="width: 120px">
<el-option label="全部" value="" />
<el-option label="待处理" :value="0" />
<el-option label="已通过" :value="1" />
<el-option label="已驳回" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="申诉时间">
<el-date-picker v-model="searchForm.startTime" type="date" placeholder="开始日期" value-format="yyyy-MM-dd" style="width: 140px" />
</el-form-item>
<el-form-item>
<el-date-picker v-model="searchForm.endTime" type="date" placeholder="结束日期" value-format="yyyy-MM-dd" style="width: 140px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table :data="tableData" v-loading="loading" border size="mini">
<el-table-column prop="id" label="ID" width="60" align="center" />
<el-table-column prop="user_id" label="用户ID" width="80" align="center" />
<el-table-column prop="user_name" label="申诉人" width="120" align="center" />
<el-table-column prop="phone" label="手机号" width="120" align="center" />
<el-table-column label="申诉类型" width="100" align="center">
<template slot-scope="scope">
<el-tag size="mini" :type="getTypeTag(scope.row.type)">{{ getTypeName(scope.row.type) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="reason" label="申诉原因" min-width="200" show-overflow-tooltip />
<el-table-column prop="create_time" label="申诉时间" width="160" align="center" />
<el-table-column label="处理状态" width="100" align="center">
<template slot-scope="scope">
<el-tag size="mini" :type="getStatusType(scope.row.status)">{{ getStatusName(scope.row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="process_time" label="处理时间" width="160" align="center" />
<el-table-column label="操作" width="180" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleDetail(scope.row)">详情</el-button>
<el-button v-if="scope.row.status === 0" type="success" size="mini" @click="handleProcess(scope.row)">处理</el-button>
<el-button v-if="scope.row.status !== 0" type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="acea-row row-right page mt20">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
/>
</div>
</div>
</el-card>
<!-- 详情弹窗 -->
<el-dialog title="申诉详情" :visible.sync="detailDialogVisible" width="650px" :close-on-click-modal="false">
<el-descriptions :column="2" border v-if="detailData">
<el-descriptions-item label="申诉ID">{{ detailData.id }}</el-descriptions-item>
<el-descriptions-item label="用户ID">{{ detailData.user_id }}</el-descriptions-item>
<el-descriptions-item label="申诉人">{{ detailData.user_name }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ detailData.phone }}</el-descriptions-item>
<el-descriptions-item label="申诉类型">
<el-tag size="small" :type="getTypeTag(detailData.type)">{{ getTypeName(detailData.type) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="处理状态">
<el-tag size="small" :type="getStatusType(detailData.status)">{{ getStatusName(detailData.status) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="申诉原因" :span="2">{{ detailData.reason }}</el-descriptions-item>
<el-descriptions-item label="申诉时间">{{ detailData.create_time }}</el-descriptions-item>
<el-descriptions-item label="处理时间">{{ detailData.process_time || '-' }}</el-descriptions-item>
<el-descriptions-item label="处理人">{{ detailData.processor || '-' }}</el-descriptions-item>
<el-descriptions-item label="处理结果">{{ detailData.process_result || '-' }}</el-descriptions-item>
<el-descriptions-item label="证据图片" :span="2" v-if="detailData.images && detailData.images.length">
<div class="image-list">
<el-image v-for="(img, idx) in detailData.images" :key="idx" :src="img" :preview-src-list="detailData.images" style="width: 80px; height: 80px; margin-right: 10px;" fit="cover" />
</div>
</el-descriptions-item>
</el-descriptions>
<div slot="footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
<!-- 处理弹窗 -->
<el-dialog title="处理申诉" :visible.sync="processDialogVisible" width="500px" :close-on-click-modal="false">
<el-form :model="processForm" label-width="80px">
<el-form-item label="处理结果" required>
<el-radio-group v-model="processForm.status">
<el-radio :label="1">通过申诉</el-radio>
<el-radio :label="2">驳回申诉</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="处理说明" required>
<el-input v-model="processForm.result" type="textarea" :rows="4" placeholder="请输入处理说明" />
</el-form-item>
</el-form>
<div slot="footer">
<el-button type="primary" @click="submitProcess">确认处理</el-button>
<el-button @click="processDialogVisible = false">取消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { appealListApi, appealDetailApi, appealProcessApi, appealDeleteApi } from '@/api/appeal';
export default {
name: 'AppealManage',
data() {
return {
searchForm: { keywords: '', page: 1, limit: 20 },
searchForm: {
userName: '',
type: '',
status: '',
startTime: '',
endTime: '',
page: 1,
limit: 10
},
tableData: [],
total: 0,
loading: false,
detailDialogVisible: false,
detailData: null,
processDialogVisible: false,
processForm: { id: null, status: 1, result: '' }
};
},
mounted() {
this.getList();
},
methods: {
getList() {
async getList() {
this.loading = true;
setTimeout(() => {
this.tableData = [];
this.total = 0;
try {
const res = await appealListApi(this.searchForm);
this.tableData = res.list || (res.data && res.data.list) || [];
this.total = res.total || (res.data && res.data.total) || 0;
} catch (error) {
this.$message.error(error.message || '获取列表失败');
} finally {
this.loading = false;
}, 500);
}
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
},
handleReset() {
this.searchForm = { keywords: '', page: 1, limit: 20 };
this.searchForm = { userName: '', type: '', status: '', startTime: '', endTime: '', page: 1, limit: 10 };
this.getList();
},
handleAdd() {
this.$message.info('新增功能待开发');
},
handleEdit(row) {
this.$message.info('编辑功能待开发');
},
handleDelete(row) {
this.$confirm('确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.$message.success('删除成功');
this.getList();
}).catch(() => {});
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.getList();
@ -106,15 +187,70 @@ export default {
this.searchForm.page = val;
this.getList();
},
goBack() {
this.$router.back();
getTypeName(type) {
const map = { ban: '封号申诉', mute: '禁言申诉', punish: '处罚申诉', other: '其他' };
return map[type] || type;
},
},
getTypeTag(type) {
const map = { ban: 'danger', mute: 'warning', punish: 'info', other: '' };
return map[type] || '';
},
getStatusName(status) {
const map = { 0: '待处理', 1: '已通过', 2: '已驳回' };
return map[status] || status;
},
getStatusType(status) {
const map = { 0: 'warning', 1: 'success', 2: 'danger' };
return map[status] || 'info';
},
async handleDetail(row) {
try {
const res = await appealDetailApi(row.id);
this.detailData = res.data || res;
} catch {
this.detailData = row;
}
this.detailDialogVisible = true;
},
handleProcess(row) {
this.processForm = { id: row.id, status: 1, result: '' };
this.processDialogVisible = true;
},
async submitProcess() {
if (!this.processForm.result) {
this.$message.warning('请输入处理说明');
return;
}
try {
await appealProcessApi(this.processForm.id, {
status: this.processForm.status,
result: this.processForm.result
});
this.$message.success('处理成功');
this.processDialogVisible = false;
this.getList();
} catch (error) {
this.$message.error(error.message || '处理失败');
}
},
handleDelete(row) {
this.$confirm('确定要删除该申诉记录吗?', '提示', { type: 'warning' })
.then(async () => {
try {
await appealDeleteApi(row.id);
this.$message.success('删除成功');
this.getList();
} catch (error) {
this.$message.error(error.message || '删除失败');
}
}).catch(() => {});
}
}
};
</script>
<style scoped lang="scss">
.selWidth {
width: 200px;
}
.mb20 { margin-bottom: 20px; }
.mt20 { margin-top: 20px; }
.image-list { display: flex; flex-wrap: wrap; }
</style>

View File

@ -2,102 +2,225 @@
<div class="divBox">
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<el-page-header @back="goBack" content="认证管理"></el-page-header>
<div class="mt20">
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="关键词:">
<el-input v-model="searchForm.keywords" placeholder="请输入关键词" clearable class="selWidth" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<div class="mb20">
<el-button type="primary" size="small" @click="handleAdd">新增</el-button>
</div>
<el-table :data="tableData" v-loading="loading" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="name" label="名称" min-width="120" />
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status ? 'success' : 'danger'">
{{ scope.row.status ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
<!-- 搜索区域 -->
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="用户信息">
<el-input v-model="searchForm.keyword" placeholder="用户名/手机号/ID" clearable style="width: 160px" />
</el-form-item>
<el-form-item label="认证类型">
<el-select v-model="searchForm.type" placeholder="全部" clearable style="width: 120px">
<el-option label="全部" value="" />
<el-option label="实名认证" value="realname" />
<el-option label="主播认证" value="anchor" />
<el-option label="公会认证" value="guild" />
</el-select>
</el-form-item>
<el-form-item label="审核状态">
<el-select v-model="searchForm.status" placeholder="全部" clearable style="width: 120px">
<el-option label="全部" value="" />
<el-option label="待审核" :value="0" />
<el-option label="已通过" :value="1" />
<el-option label="已拒绝" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="申请时间">
<el-date-picker v-model="searchForm.dateRange" type="daterange" range-separator="" start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd" style="width: 240px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 统计卡片 -->
<el-row :gutter="20" class="mb20">
<el-col :span="6">
<div class="stat-card stat-pending">
<div class="stat-num">{{ stats.pending }}</div>
<div class="stat-label">待审核</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-card stat-passed">
<div class="stat-num">{{ stats.passed }}</div>
<div class="stat-label">已通过</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-card stat-rejected">
<div class="stat-num">{{ stats.rejected }}</div>
<div class="stat-label">已拒绝</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-card stat-total">
<div class="stat-num">{{ stats.total }}</div>
<div class="stat-label">总申请</div>
</div>
</el-col>
</el-row>
<!-- 数据表格 -->
<el-table :data="tableData" v-loading="loading" border size="mini">
<el-table-column prop="id" label="ID" width="60" align="center" />
<el-table-column prop="user_id" label="用户ID" width="80" align="center" />
<el-table-column label="用户信息" width="180">
<template slot-scope="scope">
<div class="user-info">
<el-avatar :src="scope.row.avatar" :size="36" />
<div class="user-detail">
<div class="user-name">{{ scope.row.user_name }}</div>
<div class="user-phone">{{ scope.row.phone }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="认证类型" width="100" align="center">
<template slot-scope="scope">
<el-tag size="mini" :type="getTypeTag(scope.row.type)">{{ getTypeName(scope.row.type) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="real_name" label="真实姓名" width="100" align="center" />
<el-table-column prop="id_card" label="身份证号" width="160" align="center">
<template slot-scope="scope">
{{ maskIdCard(scope.row.id_card) }}
</template>
</el-table-column>
<el-table-column prop="create_time" label="申请时间" width="160" align="center" />
<el-table-column label="审核状态" width="100" align="center">
<template slot-scope="scope">
<el-tag size="mini" :type="getStatusType(scope.row.status)">{{ getStatusName(scope.row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="audit_time" label="审核时间" width="160" align="center" />
<el-table-column label="操作" width="180" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleDetail(scope.row)">详情</el-button>
<el-button v-if="scope.row.status === 0" type="success" size="mini" @click="handleAudit(scope.row, 1)">通过</el-button>
<el-button v-if="scope.row.status === 0" type="danger" size="mini" @click="handleAudit(scope.row, 2)">拒绝</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="acea-row row-right page mt20">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="searchForm.page" :page-sizes="[10, 20, 50, 100]" :page-size="searchForm.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
</div>
</div>
</el-card>
<!-- 详情弹窗 -->
<el-dialog title="认证详情" :visible.sync="detailDialogVisible" width="700px" :close-on-click-modal="false">
<el-descriptions :column="2" border v-if="detailData">
<el-descriptions-item label="用户ID">{{ detailData.user_id }}</el-descriptions-item>
<el-descriptions-item label="用户昵称">{{ detailData.user_name }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ detailData.phone }}</el-descriptions-item>
<el-descriptions-item label="认证类型">
<el-tag size="small" :type="getTypeTag(detailData.type)">{{ getTypeName(detailData.type) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="真实姓名">{{ detailData.real_name }}</el-descriptions-item>
<el-descriptions-item label="身份证号">{{ detailData.id_card }}</el-descriptions-item>
<el-descriptions-item label="审核状态">
<el-tag size="small" :type="getStatusType(detailData.status)">{{ getStatusName(detailData.status) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ detailData.create_time }}</el-descriptions-item>
<el-descriptions-item label="身份证正面" :span="2">
<el-image v-if="detailData.id_card_front" :src="detailData.id_card_front" :preview-src-list="[detailData.id_card_front]" style="width: 200px; height: 120px;" fit="cover" />
<span v-else>-</span>
</el-descriptions-item>
<el-descriptions-item label="身份证反面" :span="2">
<el-image v-if="detailData.id_card_back" :src="detailData.id_card_back" :preview-src-list="[detailData.id_card_back]" style="width: 200px; height: 120px;" fit="cover" />
<span v-else>-</span>
</el-descriptions-item>
<el-descriptions-item label="手持身份证" :span="2">
<el-image v-if="detailData.id_card_hand" :src="detailData.id_card_hand" :preview-src-list="[detailData.id_card_hand]" style="width: 200px; height: 120px;" fit="cover" />
<span v-else>-</span>
</el-descriptions-item>
<el-descriptions-item label="审核人" v-if="detailData.status !== 0">{{ detailData.auditor || '-' }}</el-descriptions-item>
<el-descriptions-item label="审核时间" v-if="detailData.status !== 0">{{ detailData.audit_time || '-' }}</el-descriptions-item>
<el-descriptions-item label="审核备注" :span="2" v-if="detailData.status !== 0">{{ detailData.audit_remark || '-' }}</el-descriptions-item>
</el-descriptions>
<div slot="footer">
<el-button v-if="detailData && detailData.status === 0" type="success" @click="handleAudit(detailData, 1)">通过</el-button>
<el-button v-if="detailData && detailData.status === 0" type="danger" @click="handleAudit(detailData, 2)">拒绝</el-button>
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
<!-- 审核弹窗 -->
<el-dialog :title="auditForm.status === 1 ? '通过认证' : '拒绝认证'" :visible.sync="auditDialogVisible" width="500px" :close-on-click-modal="false">
<el-form :model="auditForm" label-width="80px">
<el-form-item label="审核备注" :required="auditForm.status === 2">
<el-input v-model="auditForm.remark" type="textarea" :rows="4" :placeholder="auditForm.status === 1 ? '可选填写备注' : '请输入拒绝原因'" />
</el-form-item>
</el-form>
<div slot="footer">
<el-button :type="auditForm.status === 1 ? 'success' : 'danger'" @click="submitAudit">确认{{ auditForm.status === 1 ? '通过' : '拒绝' }}</el-button>
<el-button @click="auditDialogVisible = false">取消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { certificationListApi, certificationDetailApi, certificationAuditApi } from '@/api/certification';
export default {
name: 'CertificationManage',
data() {
return {
searchForm: { keywords: '', page: 1, limit: 20 },
searchForm: {
keyword: '',
type: '',
status: '',
dateRange: [],
page: 1,
limit: 10
},
tableData: [],
total: 0,
loading: false,
stats: { pending: 0, passed: 0, rejected: 0, total: 0 },
detailDialogVisible: false,
detailData: null,
auditDialogVisible: false,
auditForm: { id: null, status: 1, remark: '' }
};
},
mounted() {
this.getList();
},
methods: {
getList() {
async getList() {
this.loading = true;
setTimeout(() => {
this.tableData = [];
this.total = 0;
try {
const params = {
...this.searchForm,
startTime: this.searchForm.dateRange && this.searchForm.dateRange[0],
endTime: this.searchForm.dateRange && this.searchForm.dateRange[1]
};
delete params.dateRange;
const res = await certificationListApi(params);
this.tableData = res.list || (res.data && res.data.list) || [];
this.total = res.total || (res.data && res.data.total) || 0;
//
if (res.stats || (res.data && res.data.stats)) {
this.stats = res.stats || res.data.stats;
}
} catch (error) {
this.$message.error(error.message || '获取列表失败');
} finally {
this.loading = false;
}, 500);
}
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
},
handleReset() {
this.searchForm = { keywords: '', page: 1, limit: 20 };
this.searchForm = { keyword: '', type: '', status: '', dateRange: [], page: 1, limit: 10 };
this.getList();
},
handleAdd() {
this.$message.info('新增功能待开发');
},
handleEdit(row) {
this.$message.info('编辑功能待开发');
},
handleDelete(row) {
this.$confirm('确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.$message.success('删除成功');
this.getList();
}).catch(() => {});
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.getList();
@ -106,15 +229,80 @@ export default {
this.searchForm.page = val;
this.getList();
},
goBack() {
this.$router.back();
getTypeName(type) {
const map = { realname: '实名认证', anchor: '主播认证', guild: '公会认证' };
return map[type] || type;
},
},
getTypeTag(type) {
const map = { realname: '', anchor: 'success', guild: 'warning' };
return map[type] || 'info';
},
getStatusName(status) {
const map = { 0: '待审核', 1: '已通过', 2: '已拒绝' };
return map[status] || status;
},
getStatusType(status) {
const map = { 0: 'warning', 1: 'success', 2: 'danger' };
return map[status] || 'info';
},
maskIdCard(idCard) {
if (!idCard) return '-';
return idCard.replace(/^(.{6})(.*)(.{4})$/, '$1********$3');
},
async handleDetail(row) {
try {
const res = await certificationDetailApi(row.id);
this.detailData = res.data || res;
} catch {
this.detailData = row;
}
this.detailDialogVisible = true;
},
handleAudit(row, status) {
this.auditForm = { id: row.id, status, remark: '' };
this.auditDialogVisible = true;
},
async submitAudit() {
if (this.auditForm.status === 2 && !this.auditForm.remark) {
this.$message.warning('请输入拒绝原因');
return;
}
try {
await certificationAuditApi(this.auditForm.id, {
status: this.auditForm.status,
remark: this.auditForm.remark
});
this.$message.success(this.auditForm.status === 1 ? '已通过' : '已拒绝');
this.auditDialogVisible = false;
this.detailDialogVisible = false;
this.getList();
} catch (error) {
this.$message.error(error.message || '操作失败');
}
}
}
};
</script>
<style scoped lang="scss">
.selWidth {
width: 200px;
.mb20 { margin-bottom: 20px; }
.mt20 { margin-top: 20px; }
.stat-card {
padding: 20px;
border-radius: 4px;
text-align: center;
.stat-num { font-size: 28px; font-weight: bold; }
.stat-label { font-size: 14px; color: #666; margin-top: 5px; }
}
.stat-pending { background: #fff7e6; .stat-num { color: #fa8c16; } }
.stat-passed { background: #f6ffed; .stat-num { color: #52c41a; } }
.stat-rejected { background: #fff1f0; .stat-num { color: #f5222d; } }
.stat-total { background: #e6f7ff; .stat-num { color: #1890ff; } }
.user-info {
display: flex;
align-items: center;
.user-detail { margin-left: 10px; text-align: left; }
.user-name { font-weight: 500; }
.user-phone { font-size: 12px; color: #999; }
}
</style>

View File

@ -2,102 +2,210 @@
<div class="divBox">
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<el-page-header @back="goBack" content="评论管理"></el-page-header>
<div class="mt20">
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="关键词:">
<el-input v-model="searchForm.keywords" placeholder="请输入关键词" clearable class="selWidth" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<div class="mb20">
<el-button type="primary" size="small" @click="handleAdd">新增</el-button>
</div>
<el-table :data="tableData" v-loading="loading" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="name" label="名称" min-width="120" />
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status ? 'success' : 'danger'">
{{ scope.row.status ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
<!-- Tab切换 -->
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane label="动态评论" name="dynamic" />
<el-tab-pane label="直播评论" name="live" />
<el-tab-pane label="商品评论" name="product" />
</el-tabs>
<!-- 搜索区域 -->
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="评论人">
<el-input v-model="searchForm.userName" placeholder="用户名/ID" clearable style="width: 140px" />
</el-form-item>
<el-form-item label="评论内容">
<el-input v-model="searchForm.content" placeholder="关键词" clearable style="width: 140px" />
</el-form-item>
<el-form-item label="审核状态">
<el-select v-model="searchForm.status" placeholder="全部" clearable style="width: 100px">
<el-option label="全部" value="" />
<el-option label="待审核" :value="0" />
<el-option label="已通过" :value="1" />
<el-option label="已拒绝" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="评论时间">
<el-date-picker v-model="searchForm.dateRange" type="daterange" range-separator="-" start-placeholder="开始" end-placeholder="结束" value-format="yyyy-MM-dd" style="width: 220px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 批量操作 -->
<div class="mb20">
<el-button type="danger" size="small" :disabled="!selectedIds.length" @click="handleBatchDelete">
批量删除 <span v-if="selectedIds.length">({{ selectedIds.length }})</span>
</el-button>
<el-button type="success" size="small" :disabled="!selectedIds.length" @click="handleBatchAudit(1)">
批量通过
</el-button>
</div>
<!-- 数据表格 -->
<el-table :data="tableData" v-loading="loading" border size="mini" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column prop="id" label="ID" width="60" align="center" />
<el-table-column label="评论人" width="150">
<template slot-scope="scope">
<div class="user-info">
<el-avatar :src="scope.row.user_avatar" :size="32" />
<span class="user-name">{{ scope.row.user_name }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="content" label="评论内容" min-width="200" show-overflow-tooltip />
<el-table-column label="评论对象" width="150">
<template slot-scope="scope">
<div class="target-info">
<span class="target-type">{{ getTargetType(scope.row.target_type) }}</span>
<span class="target-name">{{ scope.row.target_name }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="like_count" label="点赞" width="70" align="center">
<template slot-scope="scope">
<span style="color: #f56c6c;">{{ scope.row.like_count || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="reply_count" label="回复" width="70" align="center">
<template slot-scope="scope">
<span style="color: #409eff;">{{ scope.row.reply_count || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="create_time" label="评论时间" width="160" align="center" />
<el-table-column label="状态" width="90" align="center">
<template slot-scope="scope">
<el-tag size="mini" :type="getStatusType(scope.row.status)">{{ getStatusName(scope.row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleDetail(scope.row)">详情</el-button>
<el-button v-if="scope.row.status === 0" type="success" size="mini" @click="handleAudit(scope.row, 1)">通过</el-button>
<el-button v-if="scope.row.status === 0" type="warning" size="mini" @click="handleAudit(scope.row, 2)">拒绝</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="acea-row row-right page mt20">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="searchForm.page" :page-sizes="[10, 20, 50, 100]" :page-size="searchForm.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
</div>
</div>
</el-card>
<!-- 详情弹窗 -->
<el-dialog title="评论详情" :visible.sync="detailDialogVisible" width="650px" :close-on-click-modal="false">
<el-descriptions :column="2" border v-if="detailData">
<el-descriptions-item label="评论ID">{{ detailData.id }}</el-descriptions-item>
<el-descriptions-item label="评论人">
<div class="user-info">
<el-avatar :src="detailData.user_avatar" :size="24" />
<span>{{ detailData.user_name }}</span>
</div>
</el-descriptions-item>
<el-descriptions-item label="评论类型">{{ getTargetType(detailData.target_type) }}</el-descriptions-item>
<el-descriptions-item label="评论对象">{{ detailData.target_name }}</el-descriptions-item>
<el-descriptions-item label="评论内容" :span="2">{{ detailData.content }}</el-descriptions-item>
<el-descriptions-item label="评论图片" :span="2" v-if="detailData.images && detailData.images.length">
<div class="image-list">
<el-image v-for="(img, idx) in detailData.images" :key="idx" :src="img" :preview-src-list="detailData.images" style="width: 80px; height: 80px; margin-right: 8px;" fit="cover" />
</div>
</el-descriptions-item>
<el-descriptions-item label="点赞数">{{ detailData.like_count || 0 }}</el-descriptions-item>
<el-descriptions-item label="回复数">{{ detailData.reply_count || 0 }}</el-descriptions-item>
<el-descriptions-item label="评论时间">{{ detailData.create_time }}</el-descriptions-item>
<el-descriptions-item label="审核状态">
<el-tag size="small" :type="getStatusType(detailData.status)">{{ getStatusName(detailData.status) }}</el-tag>
</el-descriptions-item>
</el-descriptions>
<!-- 回复列表 -->
<div v-if="detailData && detailData.replies && detailData.replies.length" class="reply-section">
<h4>回复列表</h4>
<div v-for="reply in detailData.replies" :key="reply.id" class="reply-item">
<el-avatar :src="reply.user_avatar" :size="28" />
<div class="reply-content">
<div class="reply-user">{{ reply.user_name }}</div>
<div class="reply-text">{{ reply.content }}</div>
<div class="reply-time">{{ reply.create_time }}</div>
</div>
</div>
</div>
<div slot="footer">
<el-button v-if="detailData && detailData.status === 0" type="success" @click="handleAudit(detailData, 1)">通过</el-button>
<el-button v-if="detailData && detailData.status === 0" type="warning" @click="handleAudit(detailData, 2)">拒绝</el-button>
<el-button type="danger" @click="handleDelete(detailData)">删除</el-button>
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { commentListApi, commentDetailApi, commentDeleteApi, commentBatchDeleteApi, commentAuditApi } from '@/api/comment';
export default {
name: 'CommentManage',
data() {
return {
searchForm: { keywords: '', page: 1, limit: 20 },
activeTab: 'dynamic',
searchForm: {
userName: '',
content: '',
status: '',
dateRange: [],
page: 1,
limit: 10
},
tableData: [],
total: 0,
loading: false,
selectedIds: [],
detailDialogVisible: false,
detailData: null
};
},
mounted() {
this.getList();
},
methods: {
getList() {
async getList() {
this.loading = true;
setTimeout(() => {
this.tableData = [];
this.total = 0;
try {
const params = {
...this.searchForm,
type: this.activeTab,
startTime: this.searchForm.dateRange && this.searchForm.dateRange[0],
endTime: this.searchForm.dateRange && this.searchForm.dateRange[1]
};
delete params.dateRange;
const res = await commentListApi(params);
this.tableData = res.list || (res.data && res.data.list) || [];
this.total = res.total || (res.data && res.data.total) || 0;
} catch (error) {
this.$message.error(error.message || '获取列表失败');
} finally {
this.loading = false;
}, 500);
}
},
handleTabClick() {
this.searchForm.page = 1;
this.getList();
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
},
handleReset() {
this.searchForm = { keywords: '', page: 1, limit: 20 };
this.searchForm = { userName: '', content: '', status: '', dateRange: [], page: 1, limit: 10 };
this.getList();
},
handleAdd() {
this.$message.info('新增功能待开发');
},
handleEdit(row) {
this.$message.info('编辑功能待开发');
},
handleDelete(row) {
this.$confirm('确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.$message.success('删除成功');
this.getList();
}).catch(() => {});
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.getList();
@ -106,15 +214,107 @@ export default {
this.searchForm.page = val;
this.getList();
},
goBack() {
this.$router.back();
handleSelectionChange(selection) {
this.selectedIds = selection.map(item => item.id);
},
},
getTargetType(type) {
const map = { dynamic: '动态', live: '直播', product: '商品' };
return map[type] || type;
},
getStatusName(status) {
const map = { 0: '待审核', 1: '已通过', 2: '已拒绝' };
return map[status] || status;
},
getStatusType(status) {
const map = { 0: 'warning', 1: 'success', 2: 'danger' };
return map[status] || 'info';
},
async handleDetail(row) {
try {
const res = await commentDetailApi(row.id);
this.detailData = res.data || res;
} catch {
this.detailData = row;
}
this.detailDialogVisible = true;
},
async handleAudit(row, status) {
const action = status === 1 ? '通过' : '拒绝';
try {
await commentAuditApi(row.id, { status });
this.$message.success(`${action}`);
this.detailDialogVisible = false;
this.getList();
} catch (error) {
this.$message.error(error.message || '操作失败');
}
},
async handleBatchAudit(status) {
if (!this.selectedIds.length) return;
try {
await Promise.all(this.selectedIds.map(id => commentAuditApi(id, { status })));
this.$message.success('批量审核成功');
this.getList();
} catch (error) {
this.$message.error(error.message || '操作失败');
}
},
handleDelete(row) {
this.$confirm('确定要删除该评论吗?', '提示', { type: 'warning' })
.then(async () => {
try {
await commentDeleteApi(row.id);
this.$message.success('删除成功');
this.detailDialogVisible = false;
this.getList();
} catch (error) {
this.$message.error(error.message || '删除失败');
}
}).catch(() => {});
},
handleBatchDelete() {
if (!this.selectedIds.length) return;
this.$confirm(`确定要删除选中的 ${this.selectedIds.length} 条评论吗?`, '提示', { type: 'warning' })
.then(async () => {
try {
await commentBatchDeleteApi(this.selectedIds);
this.$message.success('批量删除成功');
this.getList();
} catch (error) {
this.$message.error(error.message || '删除失败');
}
}).catch(() => {});
}
}
};
</script>
<style scoped lang="scss">
.selWidth {
width: 200px;
.mb20 { margin-bottom: 20px; }
.mt20 { margin-top: 20px; }
.user-info {
display: flex;
align-items: center;
.user-name { margin-left: 8px; }
}
.target-info {
.target-type { color: #409eff; font-size: 12px; }
.target-name { display: block; font-size: 12px; color: #666; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}
.image-list { display: flex; flex-wrap: wrap; }
.reply-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #eee;
h4 { margin-bottom: 15px; }
}
.reply-item {
display: flex;
padding: 10px 0;
border-bottom: 1px dashed #eee;
.reply-content { margin-left: 10px; flex: 1; }
.reply-user { font-weight: 500; font-size: 13px; }
.reply-text { margin: 5px 0; color: #333; }
.reply-time { font-size: 12px; color: #999; }
}
</style>

View File

@ -2,102 +2,163 @@
<div class="divBox">
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<el-page-header @back="goBack" content="粉丝团管理"></el-page-header>
<div class="mt20">
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="关键词:">
<el-input v-model="searchForm.keywords" placeholder="请输入关键词" clearable class="selWidth" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<div class="mb20">
<el-button type="primary" size="small" @click="handleAdd">新增</el-button>
</div>
<el-table :data="tableData" v-loading="loading" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="name" label="名称" min-width="120" />
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status ? 'success' : 'danger'">
{{ scope.row.status ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
<!-- 搜索区域 -->
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="主播信息">
<el-input v-model="searchForm.anchorKeyword" placeholder="主播名/ID" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="粉丝信息">
<el-input v-model="searchForm.fansKeyword" placeholder="粉丝名/ID" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="粉丝等级">
<el-select v-model="searchForm.level" placeholder="全部" clearable style="width: 100px">
<el-option label="全部" value="" />
<el-option v-for="i in 10" :key="i" :label="`Lv.${i}`" :value="i" />
</el-select>
</el-form-item>
<el-form-item label="关注时间">
<el-date-picker v-model="searchForm.dateRange" type="daterange" range-separator="" start-placeholder="开始" end-placeholder="结束" value-format="yyyy-MM-dd" style="width: 220px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table :data="tableData" v-loading="loading" border size="mini">
<el-table-column prop="id" label="ID" width="60" align="center" />
<el-table-column label="主播信息" width="180">
<template slot-scope="scope">
<div class="user-info">
<el-avatar :src="scope.row.anchor_avatar" :size="36" />
<div class="user-detail">
<div class="user-name">{{ scope.row.anchor_name }}</div>
<div class="user-id">ID: {{ scope.row.anchor_id }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="粉丝信息" width="180">
<template slot-scope="scope">
<div class="user-info">
<el-avatar :src="scope.row.fans_avatar" :size="36" />
<div class="user-detail">
<div class="user-name">{{ scope.row.fans_name }}</div>
<div class="user-id">ID: {{ scope.row.fans_id }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="粉丝等级" width="100" align="center">
<template slot-scope="scope">
<el-tag size="mini" :type="getLevelType(scope.row.level)">Lv.{{ scope.row.level }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="intimacy" label="亲密度" width="100" align="center">
<template slot-scope="scope">
<span style="color: #e6a23c;">{{ scope.row.intimacy || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="gift_value" label="送礼价值" width="100" align="center">
<template slot-scope="scope">
<span style="color: #f56c6c;">¥{{ scope.row.gift_value || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="follow_time" label="关注时间" width="160" align="center" />
<el-table-column prop="last_visit" label="最近访问" width="160" align="center" />
<el-table-column label="操作" width="120" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleDetail(scope.row)">详情</el-button>
<el-button type="danger" size="mini" @click="handleRemove(scope.row)">移除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="acea-row row-right page mt20">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="searchForm.page" :page-sizes="[10, 20, 50, 100]" :page-size="searchForm.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
</div>
</div>
</el-card>
<!-- 详情弹窗 -->
<el-dialog title="粉丝详情" :visible.sync="detailDialogVisible" width="600px" :close-on-click-modal="false">
<el-descriptions :column="2" border v-if="detailData">
<el-descriptions-item label="主播ID">{{ detailData.anchor_id }}</el-descriptions-item>
<el-descriptions-item label="主播昵称">{{ detailData.anchor_name }}</el-descriptions-item>
<el-descriptions-item label="粉丝ID">{{ detailData.fans_id }}</el-descriptions-item>
<el-descriptions-item label="粉丝昵称">{{ detailData.fans_name }}</el-descriptions-item>
<el-descriptions-item label="粉丝等级">
<el-tag size="small" :type="getLevelType(detailData.level)">Lv.{{ detailData.level }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="亲密度">{{ detailData.intimacy || 0 }}</el-descriptions-item>
<el-descriptions-item label="累计送礼">¥{{ detailData.gift_value || 0 }}</el-descriptions-item>
<el-descriptions-item label="送礼次数">{{ detailData.gift_count || 0 }}</el-descriptions-item>
<el-descriptions-item label="关注时间">{{ detailData.follow_time }}</el-descriptions-item>
<el-descriptions-item label="最近访问">{{ detailData.last_visit }}</el-descriptions-item>
<el-descriptions-item label="观看时长">{{ detailData.watch_duration || 0 }}分钟</el-descriptions-item>
<el-descriptions-item label="互动次数">{{ detailData.interact_count || 0 }}</el-descriptions-item>
</el-descriptions>
<div slot="footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { fansListApi, fansDetailApi, fansRemoveApi } from '@/api/fans';
export default {
name: 'FansManage',
data() {
return {
searchForm: { keywords: '', page: 1, limit: 20 },
searchForm: {
anchorKeyword: '',
fansKeyword: '',
level: '',
dateRange: [],
page: 1,
limit: 10
},
tableData: [],
total: 0,
loading: false,
detailDialogVisible: false,
detailData: null
};
},
mounted() {
this.getList();
},
methods: {
getList() {
async getList() {
this.loading = true;
setTimeout(() => {
this.tableData = [];
this.total = 0;
try {
const params = {
...this.searchForm,
startTime: this.searchForm.dateRange && this.searchForm.dateRange[0],
endTime: this.searchForm.dateRange && this.searchForm.dateRange[1]
};
delete params.dateRange;
const res = await fansListApi(params);
this.tableData = res.list || (res.data && res.data.list) || [];
this.total = res.total || (res.data && res.data.total) || 0;
} catch (error) {
this.$message.error(error.message || '获取列表失败');
} finally {
this.loading = false;
}, 500);
}
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
},
handleReset() {
this.searchForm = { keywords: '', page: 1, limit: 20 };
this.searchForm = { anchorKeyword: '', fansKeyword: '', level: '', dateRange: [], page: 1, limit: 10 };
this.getList();
},
handleAdd() {
this.$message.info('新增功能待开发');
},
handleEdit(row) {
this.$message.info('编辑功能待开发');
},
handleDelete(row) {
this.$confirm('确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.$message.success('删除成功');
this.getList();
}).catch(() => {});
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.getList();
@ -106,15 +167,45 @@ export default {
this.searchForm.page = val;
this.getList();
},
goBack() {
this.$router.back();
getLevelType(level) {
if (level >= 8) return 'danger';
if (level >= 5) return 'warning';
if (level >= 3) return 'success';
return 'info';
},
},
async handleDetail(row) {
try {
const res = await fansDetailApi(row.id);
this.detailData = res.data || res;
} catch {
this.detailData = row;
}
this.detailDialogVisible = true;
},
handleRemove(row) {
this.$confirm(`确定要移除 ${row.fans_name}${row.anchor_name} 的粉丝关系吗?`, '提示', { type: 'warning' })
.then(async () => {
try {
await fansRemoveApi(row.id);
this.$message.success('移除成功');
this.getList();
} catch (error) {
this.$message.error(error.message || '移除失败');
}
}).catch(() => {});
}
}
};
</script>
<style scoped lang="scss">
.selWidth {
width: 200px;
.mb20 { margin-bottom: 20px; }
.mt20 { margin-top: 20px; }
.user-info {
display: flex;
align-items: center;
.user-detail { margin-left: 10px; text-align: left; }
.user-name { font-weight: 500; font-size: 13px; }
.user-id { font-size: 12px; color: #999; }
}
</style>

View File

@ -2,142 +2,220 @@
<div class="divBox">
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<el-page-header @back="goBack" content="社会动态管理"></el-page-header>
<div class="mt20">
<!-- 搜索和新增 -->
<div class="mb20">
<el-form inline size="small" :model="searchForm">
<el-form-item>
<el-select v-model="searchForm.auditStatus" placeholder="审核状态(不限)" clearable style="width: 150px;">
<el-option label="已通过" value="1"></el-option>
<el-option label="审核中" value="0"></el-option>
<el-option label="已拒绝" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="searchForm.startTime" placeholder="开始时间" clearable style="width: 150px;" />
</el-form-item>
<el-form-item>
<el-input v-model="searchForm.endTime" placeholder="结束时间" clearable style="width: 150px;" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
</el-form-item>
</el-form>
</div>
<div class="mb20">
<el-button type="success" icon="el-icon-plus" @click="handleAdd">添加</el-button>
</div>
<!-- 搜索区域 -->
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="发布者">
<el-input v-model="searchForm.userName" placeholder="用户名/ID" clearable style="width: 140px" />
</el-form-item>
<el-form-item label="分区">
<el-select v-model="searchForm.zone" placeholder="全部分区" clearable style="width: 120px">
<el-option label="全部" value="" />
<el-option label="推荐" value="recommend" />
<el-option label="热门" value="hot" />
<el-option label="最新" value="new" />
<el-option label="关注" value="follow" />
</el-select>
</el-form-item>
<el-form-item label="审核状态">
<el-select v-model="searchForm.auditStatus" placeholder="全部" clearable style="width: 100px">
<el-option label="全部" value="" />
<el-option label="待审核" :value="0" />
<el-option label="已通过" :value="1" />
<el-option label="已拒绝" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="发布时间">
<el-date-picker v-model="searchForm.startTime" type="date" placeholder="开始日期" value-format="yyyy-MM-dd" style="width: 140px" />
</el-form-item>
<el-form-item>
<el-date-picker v-model="searchForm.endTime" type="date" placeholder="结束日期" value-format="yyyy-MM-dd" style="width: 140px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 表格 -->
<el-table :data="tableData" v-loading="loading" border style="width: 100%">
<el-table-column prop="id" label="编号" width="100" align="center" />
<el-table-column prop="zone" label="分区" width="150" align="center" />
<el-table-column label="审核状态" width="150" align="center">
<template slot-scope="scope">
<el-switch
v-model="scope.row.auditStatus"
@change="handleStatusChange(scope.row)"
:active-value="true"
:inactive-value="false"
></el-switch>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" align="center" />
<el-table-column label="操作" width="150" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
<!-- 操作按钮 -->
<div class="mb20">
<el-button type="primary" size="small" icon="el-icon-plus" @click="handleAdd">发布动态</el-button>
<el-button type="warning" size="small" icon="el-icon-refresh" @click="getList">刷新</el-button>
</div>
<!-- 数据表格 -->
<el-table :data="tableData" v-loading="loading" border size="mini">
<el-table-column prop="id" label="ID" width="60" align="center" />
<el-table-column label="发布者" width="150">
<template slot-scope="scope">
<div class="user-info">
<el-avatar :src="scope.row.user_avatar" :size="32" />
<span class="user-name">{{ scope.row.user_name }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="content" label="动态内容" min-width="200" show-overflow-tooltip />
<el-table-column label="图片/视频" width="120" align="center">
<template slot-scope="scope">
<el-image v-if="scope.row.images && scope.row.images.length" :src="scope.row.images[0]" :preview-src-list="scope.row.images" style="width: 50px; height: 50px;" fit="cover" />
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="分区" width="80" align="center">
<template slot-scope="scope">
<el-tag size="mini">{{ getZoneName(scope.row.zone) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="like_count" label="点赞" width="70" align="center">
<template slot-scope="scope">
<span style="color: #f56c6c;">{{ scope.row.like_count || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="comment_count" label="评论" width="70" align="center">
<template slot-scope="scope">
<span style="color: #409eff;">{{ scope.row.comment_count || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="share_count" label="分享" width="70" align="center">
<template slot-scope="scope">
<span style="color: #67c23a;">{{ scope.row.share_count || 0 }}</span>
</template>
</el-table-column>
<el-table-column prop="create_time" label="发布时间" width="160" align="center" />
<el-table-column label="审核状态" width="100" align="center">
<template slot-scope="scope">
<el-tag size="mini" :type="getAuditStatusType(scope.row.audit_status)">{{ getAuditStatusName(scope.row.audit_status) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="220" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleDetail(scope.row)">详情</el-button>
<el-button v-if="scope.row.audit_status === 0" type="success" size="mini" @click="handleAudit(scope.row, 1)">通过</el-button>
<el-button v-if="scope.row.audit_status === 0" type="warning" size="mini" @click="handleAudit(scope.row, 2)">拒绝</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="acea-row row-right page mt20">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="searchForm.page" :page-sizes="[10, 20, 50, 100]" :page-size="searchForm.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
</div>
</div>
</el-card>
<!-- 详情弹窗 -->
<el-dialog title="动态详情" :visible.sync="detailDialogVisible" width="700px" :close-on-click-modal="false">
<el-descriptions :column="2" border v-if="detailData">
<el-descriptions-item label="动态ID">{{ detailData.id }}</el-descriptions-item>
<el-descriptions-item label="发布者">
<div class="user-info">
<el-avatar :src="detailData.user_avatar" :size="24" />
<span>{{ detailData.user_name }}</span>
</div>
</el-descriptions-item>
<el-descriptions-item label="分区">{{ getZoneName(detailData.zone) }}</el-descriptions-item>
<el-descriptions-item label="审核状态">
<el-tag size="small" :type="getAuditStatusType(detailData.audit_status)">{{ getAuditStatusName(detailData.audit_status) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="动态内容" :span="2">{{ detailData.content }}</el-descriptions-item>
<el-descriptions-item label="图片/视频" :span="2" v-if="detailData.images && detailData.images.length">
<div class="image-list">
<el-image v-for="(img, idx) in detailData.images" :key="idx" :src="img" :preview-src-list="detailData.images" style="width: 100px; height: 100px; margin-right: 10px;" fit="cover" />
</div>
</el-descriptions-item>
<el-descriptions-item label="点赞数">{{ detailData.like_count || 0 }}</el-descriptions-item>
<el-descriptions-item label="评论数">{{ detailData.comment_count || 0 }}</el-descriptions-item>
<el-descriptions-item label="分享数">{{ detailData.share_count || 0 }}</el-descriptions-item>
<el-descriptions-item label="浏览数">{{ detailData.view_count || 0 }}</el-descriptions-item>
<el-descriptions-item label="发布时间">{{ detailData.create_time }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ detailData.update_time || '-' }}</el-descriptions-item>
</el-descriptions>
<div slot="footer">
<el-button v-if="detailData && detailData.audit_status === 0" type="success" @click="handleAudit(detailData, 1)">通过</el-button>
<el-button v-if="detailData && detailData.audit_status === 0" type="warning" @click="handleAudit(detailData, 2)">拒绝</el-button>
<el-button type="danger" @click="handleDelete(detailData)">删除</el-button>
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
<!-- 发布动态弹窗 -->
<el-dialog title="发布动态" :visible.sync="addDialogVisible" width="600px" :close-on-click-modal="false">
<el-form :model="addForm" :rules="addRules" ref="addForm" label-width="80px">
<el-form-item label="分区" prop="zone">
<el-select v-model="addForm.zone" placeholder="请选择分区" style="width: 100%;">
<el-option label="推荐" value="recommend" />
<el-option label="热门" value="hot" />
<el-option label="最新" value="new" />
</el-select>
</el-form-item>
<el-form-item label="内容" prop="content">
<el-input v-model="addForm.content" type="textarea" :rows="4" placeholder="请输入动态内容" />
</el-form-item>
<el-form-item label="图片">
<el-upload action="#" list-type="picture-card" :auto-upload="false" :file-list="addForm.fileList" :on-change="handleFileChange" :on-remove="handleFileRemove">
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer">
<el-button type="primary" @click="submitAdd">发布</el-button>
<el-button @click="addDialogVisible = false">取消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { socialDynamicListApi, socialDynamicDetailApi, socialDynamicAddApi, socialDynamicDeleteApi, socialDynamicAuditApi } from '@/api/socialDynamic';
export default {
name: 'SocialDynamicManage',
data() {
return {
searchForm: {
searchForm: {
userName: '',
zone: '',
auditStatus: '',
startTime: '',
endTime: '',
page: 1,
limit: 10
page: 1,
limit: 10
},
tableData: [],
total: 0,
loading: false,
dialogVisible: false,
dialogTitle: '添加',
formData: {
id: null,
zone: '',
auditStatus: true,
},
detailDialogVisible: false,
detailData: null,
addDialogVisible: false,
addForm: { zone: '', content: '', fileList: [] },
addRules: {
zone: [{ required: true, message: '请选择分区', trigger: 'change' }],
content: [{ required: true, message: '请输入内容', trigger: 'blur' }]
}
};
},
mounted() {
this.getList();
},
methods: {
getList() {
async getList() {
this.loading = true;
setTimeout(() => {
this.tableData = [];
this.total = 0;
try {
const res = await socialDynamicListApi(this.searchForm);
this.tableData = res.list || (res.data && res.data.list) || [];
this.total = res.total || (res.data && res.data.total) || 0;
} catch (error) {
this.$message.error(error.message || '获取列表失败');
} finally {
this.loading = false;
}, 500);
}
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
},
handleAdd() {
this.dialogTitle = '添加';
this.formData = {
id: null,
zone: '',
auditStatus: true,
};
this.dialogVisible = true;
},
handleEdit(row) {
this.dialogTitle = '编辑';
this.formData = { ...row };
this.dialogVisible = true;
},
handleStatusChange(row) {
this.$message.success('状态更新成功');
},
handleDelete(row) {
this.$confirm('确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.$message.success('删除成功');
this.getList();
}).catch(() => {});
},
handleSubmit() {
this.$message.success('保存成功');
this.dialogVisible = false;
handleReset() {
this.searchForm = { userName: '', zone: '', auditStatus: '', startTime: '', endTime: '', page: 1, limit: 10 };
this.getList();
},
handleSizeChange(val) {
@ -148,15 +226,91 @@ export default {
this.searchForm.page = val;
this.getList();
},
goBack() {
this.$router.back();
getZoneName(zone) {
const map = { recommend: '推荐', hot: '热门', new: '最新', follow: '关注' };
return map[zone] || zone;
},
},
getAuditStatusName(status) {
const map = { 0: '待审核', 1: '已通过', 2: '已拒绝' };
return map[status] || status;
},
getAuditStatusType(status) {
const map = { 0: 'warning', 1: 'success', 2: 'danger' };
return map[status] || 'info';
},
async handleDetail(row) {
try {
const res = await socialDynamicDetailApi(row.id);
this.detailData = res.data || res;
} catch {
this.detailData = row;
}
this.detailDialogVisible = true;
},
handleAdd() {
this.addForm = { zone: '', content: '', fileList: [] };
this.addDialogVisible = true;
},
handleFileChange(file, fileList) {
this.addForm.fileList = fileList;
},
handleFileRemove(file, fileList) {
this.addForm.fileList = fileList;
},
submitAdd() {
this.$refs.addForm.validate(async valid => {
if (!valid) return;
try {
await socialDynamicAddApi({
zone: this.addForm.zone,
content: this.addForm.content,
images: this.addForm.fileList.map(function(f) {
return f.url || (f.response && f.response.url);
})
});
this.$message.success('发布成功');
this.addDialogVisible = false;
this.getList();
} catch (error) {
this.$message.error(error.message || '发布失败');
}
});
},
async handleAudit(row, status) {
const action = status === 1 ? '通过' : '拒绝';
try {
await socialDynamicAuditApi(row.id, { status });
this.$message.success(`${action}`);
this.detailDialogVisible = false;
this.getList();
} catch (error) {
this.$message.error(error.message || '操作失败');
}
},
handleDelete(row) {
this.$confirm('确定要删除该动态吗?', '提示', { type: 'warning' })
.then(async () => {
try {
await socialDynamicDeleteApi(row.id);
this.$message.success('删除成功');
this.detailDialogVisible = false;
this.getList();
} catch (error) {
this.$message.error(error.message || '删除失败');
}
}).catch(() => {});
}
}
};
</script>
<style scoped lang="scss">
.selWidth {
width: 200px;
.mb20 { margin-bottom: 20px; }
.mt20 { margin-top: 20px; }
.user-info {
display: flex;
align-items: center;
.user-name { margin-left: 8px; }
}
.image-list { display: flex; flex-wrap: wrap; }
</style>

View File

@ -1,55 +1,12 @@
<template>
<div class="divBox">
<!-- 重定向提示此界面已废弃请使用 reportFeedback/reportList -->
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<el-page-header @back="goBack" content="举报反馈管理"></el-page-header>
<div class="mt20">
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="关键词:">
<el-input v-model="searchForm.keywords" placeholder="请输入关键词" clearable class="selWidth" />
</el-form-item>
<el-form-item label="状态:">
<el-select v-model="searchForm.status" placeholder="请选择" clearable class="selWidth">
<el-option label="待处理" value="0"></el-option>
<el-option label="已处理" value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<el-table :data="tableData" v-loading="loading" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="content" label="举报内容" min-width="200" />
<el-table-column prop="type" label="类型" width="100" align="center" />
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status ? 'success' : 'warning'">
{{ scope.row.status ? '已处理' : '待处理' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handleDetail(scope.row)">查看</el-button>
<el-button type="text" size="small" @click="handleProcess(scope.row)">处理</el-button>
</template>
</el-table-column>
</el-table>
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
</div>
<div class="padding-add" style="text-align: center; padding: 60px 20px;">
<i class="el-icon-warning-outline" style="font-size: 48px; color: #e6a23c;"></i>
<h3 style="margin: 20px 0;">此界面已合并</h3>
<p style="color: #666; margin-bottom: 20px;">举报管理功能已迁移至新界面请点击下方按钮跳转</p>
<el-button type="primary" @click="goToNewPage">前往举报管理</el-button>
</div>
</el-card>
</div>
@ -57,58 +14,15 @@
<script>
export default {
name: 'ReportManage',
data() {
return {
searchForm: { keywords: '', status: '', page: 1, limit: 20 },
tableData: [],
total: 0,
loading: false,
};
},
name: 'ReportRedirect',
mounted() {
this.getList();
//
this.$router.replace('/reportFeedback/reportList');
},
methods: {
getList() {
this.loading = true;
setTimeout(() => {
this.tableData = [];
this.total = 0;
this.loading = false;
}, 500);
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
},
handleReset() {
this.searchForm = { keywords: '', status: '', page: 1, limit: 20 };
this.getList();
},
handleDetail(row) {
this.$message.info('查看详情功能待开发');
},
handleProcess(row) {
this.$message.info('处理功能待开发');
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.getList();
},
handleCurrentChange(val) {
this.searchForm.page = val;
this.getList();
},
goBack() {
this.$router.back();
},
},
goToNewPage() {
this.$router.push('/reportFeedback/reportList');
}
}
};
</script>
<style scoped lang="scss">
.selWidth {
width: 200px;
}
</style>

View File

@ -1,51 +1,12 @@
<template>
<div class="divBox">
<!-- 重定向提示此界面已废弃请使用 sensitiveWord/list -->
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<el-page-header @back="goBack" content="敏感词管理"></el-page-header>
<div class="mt20">
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="关键词:">
<el-input v-model="searchForm.keywords" placeholder="请输入关键词" clearable class="selWidth" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<div class="mb20">
<el-button type="primary" size="small" @click="handleAdd">新增</el-button>
</div>
<el-table :data="tableData" v-loading="loading" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="name" label="名称" min-width="120" />
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status ? 'success' : 'danger'">
{{ scope.row.status ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
</div>
<div class="padding-add" style="text-align: center; padding: 60px 20px;">
<i class="el-icon-warning-outline" style="font-size: 48px; color: #e6a23c;"></i>
<h3 style="margin: 20px 0;">此界面已合并</h3>
<p style="color: #666; margin-bottom: 20px;">敏感词管理功能已迁移至新界面请点击下方按钮跳转</p>
<el-button type="primary" @click="goToNewPage">前往敏感词管理</el-button>
</div>
</el-card>
</div>
@ -53,68 +14,15 @@
<script>
export default {
name: 'SensitiveManage',
data() {
return {
searchForm: { keywords: '', page: 1, limit: 20 },
tableData: [],
total: 0,
loading: false,
};
},
name: 'SensitiveRedirect',
mounted() {
this.getList();
//
this.$router.replace('/sensitiveWord/list');
},
methods: {
getList() {
this.loading = true;
setTimeout(() => {
this.tableData = [];
this.total = 0;
this.loading = false;
}, 500);
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
},
handleReset() {
this.searchForm = { keywords: '', page: 1, limit: 20 };
this.getList();
},
handleAdd() {
this.$message.info('新增功能待开发');
},
handleEdit(row) {
this.$message.info('编辑功能待开发');
},
handleDelete(row) {
this.$confirm('确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.$message.success('删除成功');
this.getList();
}).catch(() => {});
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.getList();
},
handleCurrentChange(val) {
this.searchForm.page = val;
this.getList();
},
goBack() {
this.$router.back();
},
},
goToNewPage() {
this.$router.push('/sensitiveWord/list');
}
}
};
</script>
<style scoped lang="scss">
.selWidth {
width: 200px;
}
</style>

View File

@ -1,52 +1,12 @@
<template>
<div class="divBox">
<!-- 重定向提示此界面已废弃请使用 noviceTask/list -->
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<el-page-header @back="goBack" content="任务管理"></el-page-header>
<div class="mt20">
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="任务名称:">
<el-input v-model="searchForm.keywords" placeholder="请输入任务名称" clearable class="selWidth" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<div class="mb20">
<el-button type="primary" size="small" @click="handleAdd">新增任务</el-button>
</div>
<el-table :data="tableData" v-loading="loading" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="name" label="任务名称" min-width="120" />
<el-table-column prop="reward" label="奖励" width="100" align="center" />
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status ? 'success' : 'danger'">
{{ scope.row.status ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
</div>
<div class="padding-add" style="text-align: center; padding: 60px 20px;">
<i class="el-icon-warning-outline" style="font-size: 48px; color: #e6a23c;"></i>
<h3 style="margin: 20px 0;">此界面已合并</h3>
<p style="color: #666; margin-bottom: 20px;">任务管理功能已迁移至新界面请点击下方按钮跳转</p>
<el-button type="primary" @click="goToNewPage">前往任务管理</el-button>
</div>
</el-card>
</div>
@ -54,68 +14,15 @@
<script>
export default {
name: 'TaskManage',
data() {
return {
searchForm: { keywords: '', page: 1, limit: 20 },
tableData: [],
total: 0,
loading: false,
};
},
name: 'TaskRedirect',
mounted() {
this.getList();
//
this.$router.replace('/noviceTask/list');
},
methods: {
getList() {
this.loading = true;
setTimeout(() => {
this.tableData = [];
this.total = 0;
this.loading = false;
}, 500);
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
},
handleReset() {
this.searchForm = { keywords: '', page: 1, limit: 20 };
this.getList();
},
handleAdd() {
this.$message.info('新增功能待开发');
},
handleEdit(row) {
this.$message.info('编辑功能待开发');
},
handleDelete(row) {
this.$confirm('确定要删除该任务吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.$message.success('删除成功');
this.getList();
}).catch(() => {});
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.getList();
},
handleCurrentChange(val) {
this.searchForm.page = val;
this.getList();
},
goBack() {
this.$router.back();
},
},
goToNewPage() {
this.$router.push('/noviceTask/list');
}
}
};
</script>
<style scoped lang="scss">
.selWidth {
width: 200px;
}
</style>

View File

@ -2,119 +2,370 @@
<div class="divBox">
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<el-page-header @back="goBack" content="提现管理"></el-page-header>
<div class="mt20">
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="关键词:">
<el-input v-model="searchForm.keywords" placeholder="请输入关键词" clearable class="selWidth" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<div class="mb20">
<el-button type="primary" size="small" @click="handleAdd">新增</el-button>
</div>
<el-table :data="tableData" v-loading="loading" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="name" label="名称" min-width="120" />
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status ? 'success' : 'danger'">
{{ scope.row.status ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
<!-- Tab切换 -->
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane label="待审核提现" name="pending">
<!-- 搜索表单 -->
<el-form inline size="small" :model="searchForm" class="mb20">
<el-form-item label="提现人昵称">
<el-input v-model="searchForm.userNickname" placeholder="请输入提现人昵称" clearable style="width: 180px" />
</el-form-item>
<el-form-item label="申请时间">
<el-date-picker v-model="searchForm.startTime" type="datetime" placeholder="开始时间" value-format="yyyy-MM-dd HH:mm:ss" style="width: 180px" />
</el-form-item>
<el-form-item>
<el-date-picker v-model="searchForm.endTime" type="datetime" placeholder="结束时间" value-format="yyyy-MM-dd HH:mm:ss" style="width: 180px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 待审核表格 -->
<el-table :data="pendingData" v-loading="loading" border size="mini">
<el-table-column prop="id" label="ID" width="60" align="center" />
<el-table-column prop="user_id" label="用户ID" width="80" align="center" />
<el-table-column prop="user_nickname" label="用户昵称" width="120" align="center" />
<el-table-column prop="withdraw_amount" label="提现金额" width="100" align="center">
<template slot-scope="scope">
<span style="color: #f56c6c; font-weight: bold;">¥{{ scope.row.withdraw_amount }}</span>
</template>
</el-table-column>
<el-table-column prop="phone" label="手机号" width="120" align="center" />
<el-table-column prop="withdraw_type" label="提现类型" width="100" align="center" />
<el-table-column prop="withdraw_method" label="提现方式" width="100" align="center">
<template slot-scope="scope">
<el-tag size="mini" :type="getMethodType(scope.row.withdraw_method)">
{{ scope.row.withdraw_method }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="account_info" label="收款账号" min-width="150" align="center">
<template slot-scope="scope">
{{ scope.row.bank_card || scope.row.wechat_account || scope.row.alipay_account || '-' }}
</template>
</el-table-column>
<el-table-column prop="create_time" label="申请时间" width="160" align="center" />
<el-table-column label="操作" width="180" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="success" size="mini" @click="handleApprove(scope.row)">通过</el-button>
<el-button type="danger" size="mini" @click="handleReject(scope.row)">拒绝</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="已审核提现" name="approved">
<!-- 搜索表单 -->
<el-form inline size="small" :model="approvedSearchForm" class="mb20">
<el-form-item label="提现人昵称">
<el-input v-model="approvedSearchForm.userNickname" placeholder="请输入提现人昵称" clearable style="width: 180px" />
</el-form-item>
<el-form-item label="审核状态">
<el-select v-model="approvedSearchForm.status" placeholder="全部" clearable style="width: 120px">
<el-option label="全部" value="" />
<el-option label="已通过" value="1" />
<el-option label="已拒绝" value="2" />
</el-select>
</el-form-item>
<el-form-item label="审核时间">
<el-date-picker v-model="approvedSearchForm.startTime" type="datetime" placeholder="开始时间" value-format="yyyy-MM-dd HH:mm:ss" style="width: 180px" />
</el-form-item>
<el-form-item>
<el-date-picker v-model="approvedSearchForm.endTime" type="datetime" placeholder="结束时间" value-format="yyyy-MM-dd HH:mm:ss" style="width: 180px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleApprovedSearch">搜索</el-button>
<el-button icon="el-icon-refresh" @click="handleApprovedReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 已审核表格 -->
<el-table :data="approvedData" v-loading="approvedLoading" border size="mini">
<el-table-column prop="id" label="ID" width="60" align="center" />
<el-table-column prop="user_id" label="用户ID" width="80" align="center" />
<el-table-column prop="user_nickname" label="用户昵称" width="120" align="center" />
<el-table-column prop="withdraw_amount" label="提现金额" width="100" align="center">
<template slot-scope="scope">
<span style="color: #67c23a; font-weight: bold;">¥{{ scope.row.withdraw_amount }}</span>
</template>
</el-table-column>
<el-table-column prop="phone" label="手机号" width="120" align="center" />
<el-table-column prop="withdraw_method" label="提现方式" width="100" align="center">
<template slot-scope="scope">
<el-tag size="mini" :type="getMethodType(scope.row.withdraw_method)">
{{ scope.row.withdraw_method }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="account_info" label="收款账号" min-width="150" align="center">
<template slot-scope="scope">
{{ scope.row.bank_card || scope.row.wechat_account || scope.row.alipay_account || '-' }}
</template>
</el-table-column>
<el-table-column prop="create_time" label="申请时间" width="160" align="center" />
<el-table-column prop="audit_time" label="审核时间" width="160" align="center" />
<el-table-column label="审核状态" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status === 1 ? 'success' : 'danger'" size="mini">
{{ scope.row.status === 1 ? '已通过' : '已拒绝' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleViewDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 分页 -->
<div class="acea-row row-right page mt20">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="currentTotal"
/>
</div>
</div>
</el-card>
<!-- 详情弹窗 -->
<el-dialog title="提现详情" :visible.sync="detailDialogVisible" width="650px" :close-on-click-modal="false">
<el-descriptions :column="2" border v-if="detailData">
<el-descriptions-item label="用户ID">{{ detailData.user_id }}</el-descriptions-item>
<el-descriptions-item label="用户昵称">{{ detailData.user_nickname }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ detailData.phone }}</el-descriptions-item>
<el-descriptions-item label="提现类型">{{ detailData.withdraw_type }}</el-descriptions-item>
<el-descriptions-item label="提现方式">{{ detailData.withdraw_method }}</el-descriptions-item>
<el-descriptions-item label="提现金额">
<span style="color: #f56c6c; font-weight: bold;">¥{{ detailData.withdraw_amount }}</span>
</el-descriptions-item>
<el-descriptions-item label="银行名称">{{ detailData.bank_name || '-' }}</el-descriptions-item>
<el-descriptions-item label="银行卡号">{{ detailData.bank_card || '-' }}</el-descriptions-item>
<el-descriptions-item label="微信号">{{ detailData.wechat_account || '-' }}</el-descriptions-item>
<el-descriptions-item label="支付宝账号">{{ detailData.alipay_account || '-' }}</el-descriptions-item>
<el-descriptions-item label="审核状态">
<el-tag :type="detailData.status === 1 ? 'success' : 'danger'" size="small">
{{ detailData.status === 1 ? '已通过' : '已拒绝' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="审核人">{{ detailData.auditor_nickname || '-' }}</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ detailData.create_time }}</el-descriptions-item>
<el-descriptions-item label="审核时间">{{ detailData.audit_time }}</el-descriptions-item>
<el-descriptions-item label="审核备注" :span="2">{{ detailData.audit_remark || '-' }}</el-descriptions-item>
</el-descriptions>
<div slot="footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
<!-- 拒绝原因弹窗 -->
<el-dialog title="拒绝提现" :visible.sync="rejectDialogVisible" width="500px" :close-on-click-modal="false">
<el-form :model="rejectForm" label-width="80px">
<el-form-item label="拒绝原因" required>
<el-input v-model="rejectForm.reason" type="textarea" :rows="4" placeholder="请输入拒绝原因" />
</el-form-item>
</el-form>
<div slot="footer">
<el-button type="primary" @click="submitReject">确认拒绝</el-button>
<el-button @click="rejectDialogVisible = false">取消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { withdrawListApi, withdrawAuditApi } from '@/api/withdraw';
export default {
name: 'WithdrawManage',
data() {
return {
searchForm: { keywords: '', page: 1, limit: 20 },
tableData: [],
total: 0,
activeTab: 'pending',
//
searchForm: {
userNickname: '',
startTime: '',
endTime: ''
},
pendingData: [],
pendingTotal: 0,
pendingPage: 1,
pendingLimit: 10,
loading: false,
//
approvedSearchForm: {
userNickname: '',
status: '',
startTime: '',
endTime: ''
},
approvedData: [],
approvedTotal: 0,
approvedPage: 1,
approvedLimit: 10,
approvedLoading: false,
//
detailDialogVisible: false,
detailData: null,
rejectDialogVisible: false,
rejectForm: { id: null, reason: '' }
};
},
computed: {
currentPage() {
return this.activeTab === 'pending' ? this.pendingPage : this.approvedPage;
},
pageSize() {
return this.activeTab === 'pending' ? this.pendingLimit : this.approvedLimit;
},
currentTotal() {
return this.activeTab === 'pending' ? this.pendingTotal : this.approvedTotal;
}
},
mounted() {
this.getList();
this.getPendingList();
},
methods: {
getList() {
//
async getPendingList() {
this.loading = true;
setTimeout(() => {
this.tableData = [];
this.total = 0;
try {
const params = {
page: this.pendingPage,
limit: this.pendingLimit,
status: 0,
...this.searchForm
};
const res = await withdrawListApi(params);
this.pendingData = res.list || (res.data && res.data.list) || [];
this.pendingTotal = res.total || (res.data && res.data.total) || 0;
} catch (error) {
this.$message.error(error.message || '获取列表失败');
} finally {
this.loading = false;
}, 500);
}
},
//
async getApprovedList() {
this.approvedLoading = true;
try {
const params = {
page: this.approvedPage,
limit: this.approvedLimit,
status: this.approvedSearchForm.status || undefined,
...this.approvedSearchForm
};
//
if (!params.status) {
params.statusIn = '1,2';
}
const res = await withdrawListApi(params);
this.approvedData = res.list || (res.data && res.data.list) || [];
this.approvedTotal = res.total || (res.data && res.data.total) || 0;
} catch (error) {
this.$message.error(error.message || '获取列表失败');
} finally {
this.approvedLoading = false;
}
},
handleTabClick(tab) {
if (tab.name === 'pending') {
this.getPendingList();
} else {
this.getApprovedList();
}
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
this.pendingPage = 1;
this.getPendingList();
},
handleReset() {
this.searchForm = { keywords: '', page: 1, limit: 20 };
this.getList();
this.searchForm = { userNickname: '', startTime: '', endTime: '' };
this.pendingPage = 1;
this.getPendingList();
},
handleAdd() {
this.$message.info('新增功能待开发');
handleApprovedSearch() {
this.approvedPage = 1;
this.getApprovedList();
},
handleEdit(row) {
this.$message.info('编辑功能待开发');
},
handleDelete(row) {
this.$confirm('确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.$message.success('删除成功');
this.getList();
}).catch(() => {});
handleApprovedReset() {
this.approvedSearchForm = { userNickname: '', status: '', startTime: '', endTime: '' };
this.approvedPage = 1;
this.getApprovedList();
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.getList();
if (this.activeTab === 'pending') {
this.pendingLimit = val;
this.getPendingList();
} else {
this.approvedLimit = val;
this.getApprovedList();
}
},
handleCurrentChange(val) {
this.searchForm.page = val;
this.getList();
if (this.activeTab === 'pending') {
this.pendingPage = val;
this.getPendingList();
} else {
this.approvedPage = val;
this.getApprovedList();
}
},
goBack() {
this.$router.back();
getMethodType(method) {
const types = { '微信': 'success', '支付宝': 'primary', '银行卡': 'warning' };
return types[method] || 'info';
},
},
//
handleApprove(row) {
this.$confirm('确定要通过该提现申请吗?', '提示', { type: 'success' })
.then(async () => {
try {
await withdrawAuditApi(row.id, { status: 1, remark: '审核通过' });
this.$message.success('审核通过');
this.getPendingList();
} catch (error) {
this.$message.error(error.message || '操作失败');
}
}).catch(() => {});
},
//
handleReject(row) {
this.rejectForm = { id: row.id, reason: '' };
this.rejectDialogVisible = true;
},
async submitReject() {
if (!this.rejectForm.reason) {
this.$message.warning('请输入拒绝原因');
return;
}
try {
await withdrawAuditApi(this.rejectForm.id, { status: 2, remark: this.rejectForm.reason });
this.$message.success('已拒绝');
this.rejectDialogVisible = false;
this.getPendingList();
} catch (error) {
this.$message.error(error.message || '操作失败');
}
},
handleViewDetail(row) {
this.detailData = row;
this.detailDialogVisible = true;
}
}
};
</script>
<style scoped lang="scss">
.selWidth {
width: 200px;
}
.mb20 { margin-bottom: 20px; }
.mt20 { margin-top: 20px; }
</style>