feat: vip page

This commit is contained in:
zzc
2026-02-21 00:38:19 +08:00
parent ccadda41d8
commit 85c588dc4f
5 changed files with 304 additions and 8 deletions

View File

@@ -8,6 +8,14 @@ export function getUserList(data) {
})
}
export function getVipUserList(data) {
return request({
url: '/management/api/spring/user/vip/list',
method: 'get',
params: data,
})
}
export function getUserFeedbackList(data) {
return request({
url: '/management/api/common/feedback/list',

View File

@@ -227,6 +227,12 @@ export const asyncRoutes = [
component: () => import('@/views/spring/user/user/index'),
meta: { title: '用户' },
},
{
path: 'vipUser',
name: 'VipUser',
component: () => import('@/views/spring/user/vipUser/index'),
meta: { title: '会员用户' },
},
{
path: 'userChat',
name: 'UserChat',

View File

@@ -70,14 +70,27 @@ export function formatTime(time, option) {
const now = new Date()
const diff = (now - time) / 1000
if (diff < 30) {
return '刚刚'
} else if (diff < 3600) {
return `${Math.ceil(diff / 60)}分钟前`
} else if (diff < 3600 * 24) {
return `${Math.ceil(diff / 3600)}小时前`
} else if (diff < 3600 * 24 * 2) {
return '1天前'
if (diff < 0) {
const absDiff = Math.abs(diff)
if (absDiff < 30) {
return '刚刚'
} else if (absDiff < 3600) {
return `${Math.ceil(absDiff / 60)}分钟后`
} else if (absDiff < 3600 * 24) {
return `${Math.ceil(absDiff / 3600)}小时后`
} else if (absDiff < 3600 * 24 * 2) {
return '1天后'
}
} else {
if (diff < 30) {
return '刚刚'
} else if (diff < 3600) {
return `${Math.ceil(diff / 60)}分钟前`
} else if (diff < 3600 * 24) {
return `${Math.ceil(diff / 3600)}小时前`
} else if (diff < 3600 * 24 * 2) {
return '1天前'
}
}
if (option) {

View File

@@ -35,4 +35,5 @@ export const userMap = {
'6985a7c7f76d8026019f4e28': '妈妈',
'697a34996c4283e7b5904dbd': '哎萌',
'6973765449d17414b66970a2': '我',
'697da7f1af55ae2451863526': '姐姐',
}

View File

@@ -0,0 +1,268 @@
<template>
<div class="cat-user-container">
<vab-query-form>
<!-- <vab-query-form-left-panel :span="12"> -->
<!-- <el-button icon="el-icon-plus" type="primary" @click="handleEdit">添加</el-button> -->
<!-- <el-button icon="el-icon-delete" type="danger" @click="handleDelete">批量删除</el-button> -->
<!-- </vab-query-form-left-panel> -->
<vab-query-form-left-panel :span="12">
<el-form :inline="true" :model="queryForm" @submit.native.prevent>
<el-form-item>
<el-input v-model.trim="queryForm.userId" clearable placeholder="请输入查询条件" />
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
</el-form-item>
</el-form>
</vab-query-form-left-panel>
</vab-query-form>
<el-table v-loading="listLoading" :data="list" :element-loading-text="elementLoadingText" @selection-change="setSelectRows">
<el-table-column align="center" label="头像" show-overflow-tooltip>
<template #default="{ row }">
<img alt="image" :src="row.detail.avatar" style="width: 50px; height: 50px; object-fit: cover; border-radius: 50%" />
</template>
</el-table-column>
<el-table-column align="left" label="信息" show-overflow-tooltip>
<template #default="{ row }">
<div>
<div>
<strong>名称</strong>
{{ row.detail.nickname }}
</div>
<div>
<strong>平台</strong>
{{ row.source }}
</div>
<div>
<strong>设备 id</strong>
<template v-if="row.deviceId">
<el-link type="primary" :underline="false" @click="goToDevice(row.deviceId)">{{ row.deviceId }}</el-link>
</template>
<template v-else>--</template>
</div>
</div>
</template>
</el-table-column>
<el-table-column align="left" label="使用信息" show-overflow-tooltip>
<template #default="{ row }">
<div>
<div>
<strong>分享数</strong>
<el-link type="primary" :underline="false" @click="goToShareRecord(row.detail.id)">{{ row.shareCount }}</el-link>
</div>
<div>
<strong>查看数</strong>
<el-link type="primary" :underline="false" @click="goToViewRecord(row.detail.id)">{{ row.viewCount }}</el-link>
</div>
<div>
<strong>保存数</strong>
<el-link type="primary" :underline="false" @click="goToSaveRecord(row.detail.id)">{{ row.saveCount }}</el-link>
</div>
</div>
</template>
</el-table-column>
<el-table-column align="left" label="会员时间" show-overflow-tooltip>
<template #default="{ row }">
<div>
<div>
<strong>开始时间</strong>
{{ formatTime(row.startAt) }}
</div>
<div>
<strong>结束时间</strong>
{{ formatTime(row.endAt) }}
</div>
</div>
</template>
</el-table-column>
<el-table-column align="center" label="状态" show-overflow-tooltip>
<template #default="{ row }">
<el-tag v-if="isExpired(row.endAt)" type="danger">已过期</el-tag>
<el-tag v-else type="success">生效中</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="剩余天数" show-overflow-tooltip>
<template #default="{ row }">
{{ getRemainingDays(row.endAt) }}
</template>
</el-table-column>
<el-table-column align="center" label="创建时间" show-overflow-tooltip>
<template #default="{ row }">
{{ formatTime(row.createdAt) }}
</template>
</el-table-column>
<!-- <el-table-column label="操作" show-overflow-tooltip width="200">
<template #default="{ row }">
<el-button type="text" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column> -->
</el-table>
<el-pagination
background
:current-page="queryForm.pageNo"
:layout="layout"
:page-size="queryForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</template>
<script>
import { getVipUserList } from '@/api/spring/user'
import { formatTime } from '@/utils'
export default {
name: 'VipUser',
data() {
return {
host: 'https://file.lihailezzc.com/',
list: null,
listLoading: true,
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
selectRows: '',
elementLoadingText: '正在加载...',
queryForm: {
pageNo: 1,
pageSize: 10,
userId: '',
},
timeOutID: null,
}
},
created() {
if (this.$route.query.userId) {
this.queryForm.userId = this.$route.query.userId
}
this.fetchData()
},
beforeDestroy() {
clearTimeout(this.timeOutID)
},
methods: {
formatTime,
parseDate(time) {
if (typeof time === 'string' && !isNaN(Date.parse(time))) {
return new Date(time)
} else if (`${time}`.length === 10) {
return new Date(parseInt(time) * 1000)
} else {
return new Date(+time)
}
},
isExpired(time) {
if (!time) return true
const date = this.parseDate(time)
if (isNaN(date.getTime())) return true
return date.getTime() < Date.now()
},
getRemainingDays(time) {
if (!time) return '--'
const date = this.parseDate(time)
if (isNaN(date.getTime())) return '--'
const diff = date.getTime() - Date.now()
if (diff < 0) return '0天'
const days = Math.ceil(diff / (1000 * 60 * 60 * 24))
return `${days}`
},
setSelectRows(val) {
this.selectRows = val
},
handleEdit(row) {
if (row.id) {
this.$refs['edit'].showEdit(row)
} else {
this.$refs['edit'].showEdit()
}
},
handleDelete(row) {
if (row.id) {
this.$baseConfirm('你确定要删除当前项吗', null, async () => {
const { msg } = await doDelete({ ids: [row.id] })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
if (this.selectRows.length > 0) {
const ids = this.selectRows.map((item) => item.id).join()
this.$baseConfirm('你确定要删除选中项吗', null, async () => {
const { msg } = await doDelete({ ids: ids.split(',') })
this.$baseMessage(msg, 'success')
this.fetchData()
})
} else {
this.$baseMessage('未选中任何行', 'error')
return false
}
}
},
handleSizeChange(val) {
this.queryForm.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.queryForm.pageNo = val
this.fetchData()
},
queryData() {
this.queryForm.pageNo = 1
this.fetchData()
},
goToDevice(deviceId) {
this.$router.push({ path: '/spring/user/userChat', query: { deviceId } })
},
goToShareRecord(userId) {
this.$router.push({ path: '/spring/user/shareRecord', query: { userId } })
},
goToViewRecord(userId) {
this.$router.push({ path: '/spring/user/viewRecord', query: { userId } })
},
goToSaveRecord(userId) {
this.$router.push({ path: '/spring/user/saveRecord', query: { userId } })
},
async fetchData() {
this.listLoading = true
const { data } = await getVipUserList(this.queryForm)
this.list = data.list
this.total = data.totalCount
this.timeOutID = setTimeout(() => {
this.listLoading = false
}, 300)
},
},
}
</script>
<style scoped>
.author-option {
display: flex;
align-items: center;
}
.author-avatar {
width: 24px;
height: 24px;
border-radius: 50%;
object-fit: cover;
margin-right: 8px;
}
.author-name {
font-size: 14px;
}
.image-grid {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.grid-image {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 8px;
}
</style>