mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
feat: 完成众筹后台管理功能
This commit is contained in:
parent
5444ed77ad
commit
2efc669ab2
57
api/handler/admin/reward_handler.go
Normal file
57
api/handler/admin/reward_handler.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"chatplus/core"
|
||||||
|
"chatplus/handler"
|
||||||
|
"chatplus/store/model"
|
||||||
|
"chatplus/store/vo"
|
||||||
|
"chatplus/utils"
|
||||||
|
"chatplus/utils/resp"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RewardHandler struct {
|
||||||
|
handler.BaseHandler
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRewardHandler(app *core.AppServer, db *gorm.DB) *RewardHandler {
|
||||||
|
h := RewardHandler{db: db}
|
||||||
|
h.App = app
|
||||||
|
return &h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RewardHandler) List(c *gin.Context) {
|
||||||
|
var items []model.Reward
|
||||||
|
res := h.db.Find(&items)
|
||||||
|
var rewards = make([]vo.Reward, 0)
|
||||||
|
if res.Error == nil {
|
||||||
|
userIds := make([]uint, 0)
|
||||||
|
for _, v := range items {
|
||||||
|
userIds = append(userIds, v.UserId)
|
||||||
|
}
|
||||||
|
var users []model.User
|
||||||
|
h.db.Where("id IN ?", userIds).Find(&users)
|
||||||
|
var userMap = make(map[uint]model.User)
|
||||||
|
for _, u := range users {
|
||||||
|
userMap[u.Id] = u
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range items {
|
||||||
|
var r vo.Reward
|
||||||
|
err := utils.CopyObject(v, &r)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Id = v.Id
|
||||||
|
r.Username = userMap[v.UserId].Username
|
||||||
|
r.CreatedAt = v.CreatedAt.Unix()
|
||||||
|
r.UpdatedAt = v.UpdatedAt.Unix()
|
||||||
|
rewards = append(rewards, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.SUCCESS(c, rewards)
|
||||||
|
}
|
@ -136,6 +136,7 @@ func main() {
|
|||||||
fx.Provide(admin.NewApiKeyHandler),
|
fx.Provide(admin.NewApiKeyHandler),
|
||||||
fx.Provide(admin.NewUserHandler),
|
fx.Provide(admin.NewUserHandler),
|
||||||
fx.Provide(admin.NewChatRoleHandler),
|
fx.Provide(admin.NewChatRoleHandler),
|
||||||
|
fx.Provide(admin.NewRewardHandler),
|
||||||
|
|
||||||
// 创建服务
|
// 创建服务
|
||||||
fx.Provide(service.NewAliYunSmsService),
|
fx.Provide(service.NewAliYunSmsService),
|
||||||
@ -213,6 +214,10 @@ func main() {
|
|||||||
group.POST("sort", h.SetSort)
|
group.POST("sort", h.SetSort)
|
||||||
group.GET("remove", h.Remove)
|
group.GET("remove", h.Remove)
|
||||||
}),
|
}),
|
||||||
|
fx.Invoke(func(s *core.AppServer, h *admin.RewardHandler) {
|
||||||
|
group := s.Engine.Group("/api/admin/reward/")
|
||||||
|
group.GET("list", h.List)
|
||||||
|
}),
|
||||||
|
|
||||||
fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
|
fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
|
||||||
err := s.Run(db)
|
err := s.Run(db)
|
||||||
|
11
api/store/vo/reward.go
Normal file
11
api/store/vo/reward.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package vo
|
||||||
|
|
||||||
|
type Reward struct {
|
||||||
|
BaseVo
|
||||||
|
UserId uint `json:"user_id"` // 用户 ID
|
||||||
|
Username string `json:"username"`
|
||||||
|
TxId string `json:"tx_id"` // 交易ID
|
||||||
|
Amount float64 `json:"amount"` // 打赏金额
|
||||||
|
Remark string `json:"remark"` // 打赏备注
|
||||||
|
Status bool `json:"status"` // 核销状态
|
||||||
|
}
|
@ -79,6 +79,11 @@ const items = [
|
|||||||
index: '/admin/apikey',
|
index: '/admin/apikey',
|
||||||
title: 'API-KEY 管理',
|
title: 'API-KEY 管理',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: 'reward',
|
||||||
|
index: '/admin/reward',
|
||||||
|
title: '众筹管理',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: 'log',
|
icon: 'log',
|
||||||
index: '/admin/loginLog',
|
index: '/admin/loginLog',
|
||||||
|
@ -69,6 +69,12 @@ const routes = [
|
|||||||
meta: {title: 'API-KEY 管理'},
|
meta: {title: 'API-KEY 管理'},
|
||||||
component: () => import('@/views/admin/ApiKey.vue'),
|
component: () => import('@/views/admin/ApiKey.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/admin/reward',
|
||||||
|
name: 'admin-reward',
|
||||||
|
meta: {title: '众筹管理'},
|
||||||
|
component: () => import('@/views/admin/RewardList.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/loginLog',
|
path: '/admin/loginLog',
|
||||||
name: 'admin-loginLog',
|
name: 'admin-loginLog',
|
||||||
|
@ -1,373 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="admin-body common-layout">
|
|
||||||
<el-container>
|
|
||||||
<el-container>
|
|
||||||
<el-aside
|
|
||||||
:style="{
|
|
||||||
height: winHeight + 'px',
|
|
||||||
width: sideWidth + 'px',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div :class="showSidebar?'title':'title hide'">
|
|
||||||
<el-image :src="logo" class="logo"/>
|
|
||||||
<span class="text">{{ title }}</span>
|
|
||||||
<span
|
|
||||||
class="fold"
|
|
||||||
@click="showSidebar = !showSidebar"
|
|
||||||
:style="{ right: foldIconRight + 'px' }"
|
|
||||||
>
|
|
||||||
<el-icon>
|
|
||||||
<Fold/>
|
|
||||||
</el-icon>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<ul class="nav-list">
|
|
||||||
<li
|
|
||||||
v-for="nav in navs"
|
|
||||||
:key="nav.id"
|
|
||||||
:style="{ paddingLeft: nodeListPaddingLeft + 'px' }"
|
|
||||||
:class="nav.active?'active':''"
|
|
||||||
@click="addTab(nav)"
|
|
||||||
>
|
|
||||||
<el-tooltip
|
|
||||||
class="box-item"
|
|
||||||
effect="light"
|
|
||||||
:content="nav.title"
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<el-icon>
|
|
||||||
<Menu/>
|
|
||||||
</el-icon>
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
<span v-if="showSidebar">{{ nav.title }}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<el-row class="nav-footer">
|
|
||||||
<div class="source">
|
|
||||||
<i class="iconfont icon-github"></i>
|
|
||||||
<el-link href="https://github.com/yangjian102621/chatgpt-plus" target="_blank">ChatGPT-Plus-V3</el-link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="logout" @click="logout">
|
|
||||||
<i class="iconfont icon-logout"></i>
|
|
||||||
<span>退出登录</span>
|
|
||||||
</div>
|
|
||||||
</el-row>
|
|
||||||
</el-aside>
|
|
||||||
|
|
||||||
<el-main>
|
|
||||||
<div
|
|
||||||
class="main-container"
|
|
||||||
:style="{ height: winHeight + 'px' }"
|
|
||||||
>
|
|
||||||
<x-welcome v-if="curTab==='welcome'"/>
|
|
||||||
|
|
||||||
<div v-else>
|
|
||||||
<el-tabs
|
|
||||||
v-model="curTab"
|
|
||||||
class="content-tabs"
|
|
||||||
type="card"
|
|
||||||
closable
|
|
||||||
@tab-remove="removeTab"
|
|
||||||
@tab-change="changeTab"
|
|
||||||
>
|
|
||||||
<el-tab-pane label="系统配置" name="config" v-if="arrayContains(tabs, 'config')">
|
|
||||||
<sys-config v-if="curTab==='config'"/>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="用户管理" name="user" v-if="arrayContains(tabs, 'user')">
|
|
||||||
<user-list v-if="curTab==='user'"/>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="角色管理" name="role" v-if="arrayContains(tabs, 'role')">
|
|
||||||
<role-list v-if="curTab==='role'"/>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="API KEY" name="apikey" v-if="arrayContains(tabs, 'apikey')">
|
|
||||||
<api-key v-if="curTab==='apikey'"/>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="登录日志" name="loginLog" v-if="arrayContains(tabs, 'loginLog')">
|
|
||||||
<login-log v-if="curTab==='loginLog'"/>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-main>
|
|
||||||
</el-container>
|
|
||||||
</el-container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import {computed, onMounted, ref} from 'vue'
|
|
||||||
import {Fold, Menu} from "@element-plus/icons-vue"
|
|
||||||
import XWelcome from "@/views/admin/Welcome.vue";
|
|
||||||
import SysConfig from "@/views/admin/SysConfig.vue";
|
|
||||||
import {arrayContains, removeArrayItem} from "@/utils/libs";
|
|
||||||
import UserList from "@/views/admin/UserList.vue";
|
|
||||||
import RoleList from "@/views/admin/RoleList.vue";
|
|
||||||
import {httpGet} from "@/utils/http";
|
|
||||||
import {ElMessage} from "element-plus";
|
|
||||||
import {useRouter} from "vue-router";
|
|
||||||
import ApiKey from "@/views/admin/ApiKey.vue";
|
|
||||||
import LoginLog from "@/views/admin/LoginLog.vue";
|
|
||||||
|
|
||||||
const title = ref('Chat-Plus 控制台')
|
|
||||||
const logo = ref('images/logo.png')
|
|
||||||
const navs = ref([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: '系统配置',
|
|
||||||
tab: 'config',
|
|
||||||
active: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: '用户管理',
|
|
||||||
tab: 'user',
|
|
||||||
active: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
title: '角色管理',
|
|
||||||
tab: 'role',
|
|
||||||
active: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
title: 'API KEY',
|
|
||||||
tab: 'apikey',
|
|
||||||
active: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
title: '登录日志',
|
|
||||||
tab: 'loginLog',
|
|
||||||
active: false,
|
|
||||||
}
|
|
||||||
])
|
|
||||||
const tabs = ref([])
|
|
||||||
const curNav = ref(null)
|
|
||||||
const curTab = ref('welcome')
|
|
||||||
const winHeight = ref(window.innerHeight)
|
|
||||||
const showSidebar = ref(true)
|
|
||||||
|
|
||||||
const sideWidth = computed(() => {
|
|
||||||
return showSidebar.value ? 250 : 30
|
|
||||||
})
|
|
||||||
const foldIconRight = computed(() => {
|
|
||||||
return showSidebar.value ? 3 : 0
|
|
||||||
})
|
|
||||||
const nodeListPaddingLeft = computed(() => {
|
|
||||||
return showSidebar.value ? 20 : 5
|
|
||||||
})
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
// 获取会话信息
|
|
||||||
httpGet("/api/admin/session").then(() => {
|
|
||||||
// 加载系统配置
|
|
||||||
httpGet('/api/admin/config/get?key=system').then(res => {
|
|
||||||
title.value = res.data['admin_title'];
|
|
||||||
}).catch(e => {
|
|
||||||
ElMessage.error("加载系统配置失败: " + e.message)
|
|
||||||
})
|
|
||||||
}).catch(() => {
|
|
||||||
router.push('/admin/login')
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener("resize", function () {
|
|
||||||
winHeight.value = window.innerHeight
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const logout = function () {
|
|
||||||
httpGet("/api/admin/logout").then(() => {
|
|
||||||
router.push('/admin/login')
|
|
||||||
}).catch((e) => {
|
|
||||||
ElMessage.error("注销失败: " + e.message);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加 tab 窗口
|
|
||||||
const addTab = function (nav) {
|
|
||||||
if (curNav.value) {
|
|
||||||
curNav.value.active = false
|
|
||||||
}
|
|
||||||
nav.active = true
|
|
||||||
curNav.value = nav;
|
|
||||||
curTab.value = nav.tab;
|
|
||||||
if (!arrayContains(tabs.value, nav.tab)) {
|
|
||||||
this.tabs.push(nav.tab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换 tab 窗口
|
|
||||||
const changeTab = function (name) {
|
|
||||||
for (let i = 0; i < navs.value.length; i++) {
|
|
||||||
let _nav = navs.value[i]
|
|
||||||
if (_nav.tab === name) {
|
|
||||||
curNav.value.active = false // 取消上一个 active 窗口的激活状态
|
|
||||||
_nav.active = true
|
|
||||||
curNav.value = _nav;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除 tab 窗口
|
|
||||||
const removeTab = function (name) {
|
|
||||||
tabs.value = removeArrayItem(tabs.value, name);
|
|
||||||
if (tabs.value.length === 0) {
|
|
||||||
curTab.value = 'welcome';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < navs.value.length; i++) {
|
|
||||||
if (navs.value[i].tab === tabs.value[tabs.value.length - 1]) {
|
|
||||||
addTab(navs.value[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="stylus">
|
|
||||||
$sideBgColor = #252526;
|
|
||||||
$borderColor = #4676d0;
|
|
||||||
.admin-body {
|
|
||||||
.el-aside {
|
|
||||||
background-color: $sideBgColor;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
text-align: center;
|
|
||||||
line-height: 60px;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 20px;
|
|
||||||
border-bottom: 2px solid #333841;
|
|
||||||
display flex
|
|
||||||
flex-direction row
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
background-color #ffffff
|
|
||||||
border-radius 50%;
|
|
||||||
width 32px;
|
|
||||||
height 32px;
|
|
||||||
margin: 12px 5px 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fold {
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
top: 2px;
|
|
||||||
margin-left 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.title.hide {
|
|
||||||
.text {
|
|
||||||
display none
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
display none
|
|
||||||
}
|
|
||||||
|
|
||||||
.fold {
|
|
||||||
margin-left 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-list {
|
|
||||||
list-style: none;
|
|
||||||
position: relative;
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
li {
|
|
||||||
line-height: 40px;
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0 10px 0 10px;
|
|
||||||
border-bottom: 1px dashed #333841;
|
|
||||||
|
|
||||||
i {
|
|
||||||
margin-right: 6px;
|
|
||||||
position: relative;
|
|
||||||
top: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
li.active {
|
|
||||||
background-color: #363535
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-footer {
|
|
||||||
flex-direction column
|
|
||||||
|
|
||||||
div {
|
|
||||||
padding 10px 20px;
|
|
||||||
font-size 14px;
|
|
||||||
color #aaaaaa
|
|
||||||
|
|
||||||
.el-link {
|
|
||||||
color #aaaaaa
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconfont {
|
|
||||||
margin-right 5px
|
|
||||||
position relative
|
|
||||||
top 1px
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logout {
|
|
||||||
cursor pointer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-main {
|
|
||||||
--el-main-padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
.main-container {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
|
|
||||||
.el-tabs {
|
|
||||||
background: #ffffff;
|
|
||||||
padding 10px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="stylus">
|
|
||||||
.pagination {
|
|
||||||
padding-top 20px;
|
|
||||||
display flex
|
|
||||||
justify-content center
|
|
||||||
width 100%
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-tabs__item {
|
|
||||||
height 35px
|
|
||||||
line-height 35px
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-tabs__content {
|
|
||||||
padding-bottom 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
75
web/src/views/admin/RewardList.vue
Normal file
75
web/src/views/admin/RewardList.vue
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container list" v-loading="loading">
|
||||||
|
|
||||||
|
<el-row>
|
||||||
|
<el-table :data="items" :row-key="row => row.id">
|
||||||
|
<el-table-column prop="username" label="用户名"/>
|
||||||
|
<el-table-column prop="tx_id" label="转账单号"/>
|
||||||
|
<el-table-column prop="amount" label="转账金额"/>
|
||||||
|
|
||||||
|
<el-table-column label="转账时间">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ dateFormat(scope.row['created_at']) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="核销时间">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row['status']">{{ dateFormat(scope.row['updated_at']) }}</span>
|
||||||
|
<el-tag v-else>未核销</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
</el-table>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {reactive, ref} from "vue";
|
||||||
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
import {dateFormat, disabledDate, removeArrayItem} from "@/utils/libs";
|
||||||
|
import {Plus} from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
// 变量定义
|
||||||
|
const items = ref([])
|
||||||
|
const loading = ref(true)
|
||||||
|
|
||||||
|
// 获取数据
|
||||||
|
httpGet('/api/admin/reward/list').then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
// 初始化数据
|
||||||
|
const arr = res.data;
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
arr[i].last_used_at = dateFormat(arr[i].last_used_at)
|
||||||
|
}
|
||||||
|
items.value = arr
|
||||||
|
}
|
||||||
|
loading.value = false
|
||||||
|
}).catch(() => {
|
||||||
|
ElMessage.error("获取数据失败");
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.list {
|
||||||
|
|
||||||
|
.opt-box {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
display flex;
|
||||||
|
justify-content flex-end
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
@ -5,6 +5,7 @@
|
|||||||
@selection-change="handleSelectionChange">
|
@selection-change="handleSelectionChange">
|
||||||
<el-table-column type="selection" width="38"/>
|
<el-table-column type="selection" width="38"/>
|
||||||
<el-table-column prop="username" label="用户名"/>
|
<el-table-column prop="username" label="用户名"/>
|
||||||
|
<el-table-column prop="mobile" label="手机号"/>
|
||||||
<el-table-column prop="nickname" label="昵称"/>
|
<el-table-column prop="nickname" label="昵称"/>
|
||||||
<el-table-column prop="calls" label="提问次数" width="100"/>
|
<el-table-column prop="calls" label="提问次数" width="100"/>
|
||||||
<el-table-column label="状态" width="80">
|
<el-table-column label="状态" width="80">
|
||||||
|
Loading…
Reference in New Issue
Block a user