用户列表

This commit is contained in:
qiuyuan 2026-01-29 15:30:29 +08:00
parent ff9383d79f
commit a5471e319d
6 changed files with 619 additions and 32 deletions

View File

@ -9,10 +9,18 @@ export const getBlackCustomersList = (params) => request.basic.get('/api/v1/blac
// 移除黑名单
export const deleteBlackCustomers = (id) => request.basic.delete(`/api/v1/blackCustomers/${id}`)
// 拉黑用户
export const updateCustomers = (id, data) => request.basic.put(`/api/v1/customers/${id}`, data)
// 用户流水
export const getTopUpRecordsList = (params) => request.basic.get('/api/v1/top-up-records/customer', params)
// 用户实例订单
export const getCustomerOrderList = (params) => request.basic.get('/api/v1/orders/customer', params)
// 用户实例列表
export const getCustomerHostList = (params) => request.basic.get('/api/v1/host-cases/customer', params)
//获取单个banner
export const getBanner = (id) => request.basic.get(`/api/v1/banners/${id}`)

View File

@ -34,12 +34,11 @@ class PayStatusDict extends BaseDict {
['PaymentProcessing', '支付中'],
['PaymentSuccessful', '支付成功'],
['PaymentFailed', '支付失败'],
['PaymentCancelled', '已取消']
['PaymentCancelled', '已取消'],
])
}
}
// 认证状态
class AuthenticationDict extends BaseDict {
constructor() {
@ -72,35 +71,59 @@ class DisabledDict extends BaseDict {
}
}
//镜像类型
class ImgType extends BaseDict{
constructor(){
class ImgType extends BaseDict {
constructor() {
super([
['USER','用户镜像'],
['SYSTEM','系统镜像']
['USER', '用户镜像'],
['SYSTEM', '系统镜像']
])
}
}
//计费方式
class PayType extends BaseDict{
constructor(){
class PayType extends BaseDict {
constructor() {
super([
['PayOnTime','按时付费'],
['PayOnDay','按日付费'],
['PayOnWeek','按周付费'],
['PayOnMonth','按月付费'],
['PayOnYear','按年付费'],
['PayOnTime', '按时付费'],
['PayOnDay', '按日付费'],
['PayOnWeek', '按周付费'],
['PayOnMonth', '按月付费'],
['PayOnYear', '按年付费'],
])
}
}
//算力券状态
class TicketStatus extends BaseDict{
constructor(){
class TicketStatus extends BaseDict {
constructor() {
super([
['ENABLED','启用'],
['DISABLED','禁用'],
['ENABLED', '启用'],
['DISABLED', '禁用'],
])
}
}
// 健康状态
class HealthStatus extends BaseDict {
constructor() {
super([
['Normal', '正常'],
['Abnormal', '异常'],
])
}
}
// 运行状况
class RunningStatus extends BaseDict {
constructor() {
super([
['RUNNING', '运行中'],
['STOPPED', '已停止'],
['RELEASED', '已释放'],
['CREATING', '创建中'],
['RESTARTING', '重启中'],
])
}
}
//如何使用:import {payTypeDict} from '@/enums/dict
//获取下拉框列表payTypeDict.options
//获取label:payTypeDict.getLabel(1)
@ -109,6 +132,8 @@ export const payStatusDict = new PayStatusDict()
export const authenticationDict = new AuthenticationDict()
export const authenticationTypeDict = new AuthenticationTypeDict()
export const disabledDict = new DisabledDict()
export const imgType =new ImgType()
export const payType=new PayType()
export const ticketStatus=new TicketStatus()
export const imgType = new ImgType()
export const payType = new PayType()
export const ticketStatus = new TicketStatus()
export const healthStatus = new HealthStatus()
export const runningStatus = new RunningStatus()

View File

@ -0,0 +1,181 @@
<template>
<a-modal :open="modal.open" :title="modal.title" :confirm-loading="modal.confirmLoading" :after-close="onAfterClose"
@ok="handleOk" @cancel="handleCancel" width="80%">
<a-table :columns="columns" :data-source="listData" :loading="loading" :pagination="paginationState"
:scroll="{ x: 1000 }" @change="onTableChange">
<template #bodyCell="{ column, record }">
<template v-if="'banner_type' === column.key">
<!--状态-->
<a-tag v-if="record.banner_type == 1" color="processing">
首页轮播图
</a-tag>
<!--状态-->
<a-tag v-else color="processing">
营销活动图
</a-tag>
</template>
</template>
</a-table>
</a-modal>
</template>
<script setup>
import { cloneDeep } from 'lodash-es'
import apis from '@/apis'
import { useForm, useModal, usePagination } from '@/hooks'
import { config } from '@/config'
import { disabledDict, authenticationTypeDict, payStatusDict, payTypeDict, payType, healthStatus, runningStatus } from '@/enums/dict'
const emit = defineEmits(['ok'])
const { listData, loading, showLoading, hideLoading, paginationState, searchFormData, resetPagination } =
usePagination()
const { modal, showModal, hideModal } = useModal()
const { formRef, formRules, formRecord, formData, resetForm } = useForm()
const columns = [
{ title: '订单编号', dataIndex: 'caseName', key: 'caseName', fixed: 'left', width: 200 },
{ title: '镜像标识', dataIndex: 'image', key: 'image', width: 200 },
{
title: '计费方式',
dataIndex: 'billingMethod',
key: 'billingMethod',
width: 150,
customRender: ({ text }) => payType.getLabel(text) || text,
},
// {
// title: '',
// key: 'accountType',
// dataIndex: 'accountType',
// width: 120,
// },
{ title: 'GPU类型', key: 'gpuType', dataIndex: 'gpuType', width: 120 },
{ title: 'GPU数量', key: 'gpuCount', dataIndex: 'gpuCount', width: 120 },
{ title: '价格(单价)', key: 'unitPrice', dataIndex: 'unitPrice', width: 120 },
{ title: '计费时长', key: 'billingTime', dataIndex: 'billingTime', width: 120 },
{
title: '健康状态',
key: 'healthStatus',
dataIndex: 'healthStatus',
width: 120,
customRender: ({ text }) => healthStatus.getLabel(text) || text,
},
{
title: '运行状态',
key: 'runningStatus',
dataIndex: 'runningStatus',
width: 120,
customRender: ({ text }) => runningStatus.getLabel(text) || text,
},
{ title: '到期时间', key: 'expirationTime', dataIndex: 'paexpirationTimeyStatus', width: 120 },
{ title: '最后一次扣费时间', key: 'lastDeductionTime', dataIndex: 'lastDeductionTime', width: 120 },
]
/**
* 流水查询
*/
async function handleHost(userId) {
formRecord.value = userId
showModal({
type: 'edit',
title: '查看实例列表',
})
try {
showLoading()
const { pageSize, current } = paginationState
const params = {
pageSize,
current: current,
userId: userId,
}
const res = await apis.userControl.getCustomerHostList(params)
if (config('http.code.success') === res.success) {
const { data, total } = res
if (data && data.length > 0) {
listData.value = data
paginationState.total = total || 0
} else {
listData.value = []
paginationState.total = 0
}
} else {
listData.value = []
paginationState.total = 0
}
} catch (error) {
console.error('获取流水记录失败:', error)
listData.value = []
paginationState.total = 0
} finally {
hideLoading()
}
}
/**
* 确定
*/
function handleOk() {
formRef.value
.validateFields()
.then(async (values) => {
try {
showLoading()
const { pageSize, current } = paginationState
const params = {
pageSize,
current: current,
userId: formRecord.value,
}
//
//
hideLoading()
hideModal()
emit('ok')
} catch (error) {
hideLoading()
}
})
.catch(() => {
hideLoading()
})
}
/**
* 分页
*/
function onTableChange({ current, pageSize }) {
paginationState.current = current
paginationState.pageSize = pageSize
//
if (formRecord.value) {
handleOrder(formRecord.value)
}
}
/**
* 取消
*/
function handleCancel() {
hideModal()
}
/**
* 关闭后
*/
function onAfterClose() {
resetForm()
listData.value = []
paginationState.total = 0
}
defineExpose({
handleHost,
})
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,174 @@
<template>
<a-modal :open="modal.open" :title="modal.title" :confirm-loading="modal.confirmLoading" :after-close="onAfterClose"
@ok="handleOk" @cancel="handleCancel" width="80%">
<a-table :columns="columns" :data-source="listData" :loading="loading" :pagination="paginationState"
:scroll="{ x: 1000 }" @change="onTableChange">
<template #bodyCell="{ column, record }">
<template v-if="'banner_type' === column.key">
<!--状态-->
<a-tag v-if="record.banner_type == 1" color="processing">
首页轮播图
</a-tag>
<!--状态-->
<a-tag v-else color="processing">
营销活动图
</a-tag>
</template>
</template>
</a-table>
</a-modal>
</template>
<script setup>
import { cloneDeep } from 'lodash-es'
import apis from '@/apis'
import { useForm, useModal, usePagination } from '@/hooks'
import { config } from '@/config'
import { disabledDict, authenticationTypeDict,payStatusDict , payTypeDict, payType } from '@/enums/dict'
const emit = defineEmits(['ok'])
const { listData, loading, showLoading, hideLoading, paginationState, searchFormData, resetPagination } =
usePagination()
const { modal, showModal, hideModal } = useModal()
const { formRef, formRules, formRecord, formData, resetForm } = useForm()
const columns = [
{ title: '订单编号', dataIndex: 'orderNumber', key: 'orderNumber', fixed: 'left', width: 200 },
{ title: '实例名称', dataIndex: 'caseName', key: 'caseName', fixed: 'left', width: 200 },
{
title: '计费方式',
dataIndex: 'billingMethod',
key: 'billingMethod',
width: 150,
customRender: ({ text }) => payType.getLabel(text) || text,
},
{
title: '支付状态',
dataIndex: 'status',
key: 'status',
width: 150,
customRender: ({ text }) => payStatusDict.getLabel(text) || text,
},
// {
// title: '',
// key: 'accountType',
// dataIndex: 'accountType',
// width: 120,
// },
{ title: '计费开始时间', key: 'billingStart', dataIndex: 'billingStart', width: 120 },
{ title: '计费结束时间', key: 'billingEnd', dataIndex: 'billingEnd', width: 120 },
{ title: '计费时长', key: 'billingTime', dataIndex: 'billingTime', width: 120 },
{ title: '单价', key: 'price', dataIndex: 'price', width: 120 },
{ title: '总费用', key: 'totalAmount', dataIndex: 'totalAmount', width: 120 },
{ title: '算力券名称', key: 'voucherName', dataIndex: 'voucherName', width: 120 },
{ title: '支付状态', key: 'payStatus', dataIndex: 'payStatus', width: 120 },
]
/**
* 流水查询
*/
async function handleOrder(userId) {
formRecord.value = userId
showModal({
type: 'edit',
title: '查看实例订单列表',
})
try {
showLoading()
const { pageSize, current } = paginationState
const params = {
pageSize,
current: current,
userId: userId,
}
const res = await apis.userControl.getCustomerOrderList(params)
if (config('http.code.success') === res.success) {
const { data, total } = res
if (data && data.length > 0) {
listData.value = data
paginationState.total = total || 0
} else {
listData.value = []
paginationState.total = 0
}
} else {
listData.value = []
paginationState.total = 0
}
} catch (error) {
console.error('获取流水记录失败:', error)
listData.value = []
paginationState.total = 0
} finally {
hideLoading()
}
}
/**
* 确定
*/
function handleOk() {
formRef.value
.validateFields()
.then(async (values) => {
try {
showLoading()
const { pageSize, current } = paginationState
const params = {
pageSize,
current: current,
userId: formRecord.value,
}
//
//
hideLoading()
hideModal()
emit('ok')
} catch (error) {
hideLoading()
}
})
.catch(() => {
hideLoading()
})
}
/**
* 分页
*/
function onTableChange({ current, pageSize }) {
paginationState.current = current
paginationState.pageSize = pageSize
//
if (formRecord.value) {
handleOrder(formRecord.value)
}
}
/**
* 取消
*/
function handleCancel() {
hideModal()
}
/**
* 关闭后
*/
function onAfterClose() {
resetForm()
listData.value = []
paginationState.total = 0
}
defineExpose({
handleOrder,
})
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,168 @@
<template>
<a-modal :open="modal.open" :title="modal.title" :confirm-loading="modal.confirmLoading" :after-close="onAfterClose"
@ok="handleOk" @cancel="handleCancel" width="80%">
<a-table :columns="columns" :data-source="listData" :loading="loading" :pagination="paginationState"
:scroll="{ x: 1000 }" @change="onTableChange">
<template #bodyCell="{ column, record }">
<template v-if="'banner_type' === column.key">
<!--状态-->
<a-tag v-if="record.banner_type == 1" color="processing">
首页轮播图
</a-tag>
<!--状态-->
<a-tag v-else color="processing">
营销活动图
</a-tag>
</template>
</template>
</a-table>
</a-modal>
</template>
<script setup>
import { cloneDeep } from 'lodash-es'
import apis from '@/apis'
import { useForm, useModal, usePagination } from '@/hooks'
import { config } from '@/config'
import { disabledDict, authenticationTypeDict,payStatusDict , payTypeDict } from '@/enums/dict'
const emit = defineEmits(['ok'])
const { listData, loading, showLoading, hideLoading, paginationState, searchFormData, resetPagination } =
usePagination()
const { modal, showModal, hideModal } = useModal()
const { formRef, formRules, formRecord, formData, resetForm } = useForm()
const columns = [
{ title: '充值金额', dataIndex: 'topUpAmount', key: 'topUpAmount', fixed: 'left', width: 200 },
{
title: '支付方式',
dataIndex: 'paymentMethod',
key: 'paymentMethod',
width: 150,
customRender: ({ text }) => payTypeDict.getLabel(text) || text,
},
{
title: '支付状态',
dataIndex: 'status',
key: 'status',
width: 150,
customRender: ({ text }) => payStatusDict.getLabel(text) || text,
},
// {
// title: '',
// key: 'accountType',
// dataIndex: 'accountType',
// width: 120,
// },
{ title: '支付第三方编号', key: 'thirdPartyId', dataIndex: 'thirdPartyId', width: 120 },
{ title: '支付时间', key: 'paymentTime', dataIndex: 'paymentTime', width: 120 },
]
/**
* 流水查询
*/
async function handleWater(userId) {
formRecord.value = userId
showModal({
type: 'edit',
title: '查看流水列表',
})
try {
showLoading()
const { pageSize, current } = paginationState
const params = {
pageSize,
current: current,
userId: userId,
}
const res = await apis.userControl.getTopUpRecordsList(params)
if (config('http.code.success') === res.success) {
const { data, total } = res
if (data && data.length > 0) {
listData.value = data
paginationState.total = total || 0
} else {
listData.value = []
paginationState.total = 0
}
} else {
listData.value = []
paginationState.total = 0
}
} catch (error) {
console.error('获取流水记录失败:', error)
listData.value = []
paginationState.total = 0
} finally {
hideLoading()
}
}
/**
* 确定
*/
function handleOk() {
formRef.value
.validateFields()
.then(async (values) => {
try {
showLoading()
const { pageSize, current } = paginationState
const params = {
pageSize,
current: current,
userId: formRecord.value,
}
//
//
hideLoading()
hideModal()
emit('ok')
} catch (error) {
hideLoading()
}
})
.catch(() => {
hideLoading()
})
}
/**
* 分页
*/
function onTableChange({ current, pageSize }) {
paginationState.current = current
paginationState.pageSize = pageSize
//
if (formRecord.value) {
handleWater(formRecord.value)
}
}
/**
* 取消
*/
function handleCancel() {
hideModal()
}
/**
* 关闭后
*/
function onAfterClose() {
resetForm()
listData.value = []
paginationState.total = 0
}
defineExpose({
handleWater,
})
</script>
<style lang="less" scoped></style>

View File

@ -70,17 +70,23 @@
</template>
<template v-if="'action' === column.key">
<x-action-button @click="$refs.editDialogRef.handleEdit(record)">
<!-- <x-action-button @click="$refs.editDialogRef.handleEdit(record)">
<a-tooltip>
<template #title> {{ $t('pages.system.role.edit') }}</template>
<edit-outlined />
</a-tooltip>
</x-action-button>
</x-action-button> -->
<x-action-button @click="handleRemove(record)">
<a-tooltip>
<template #title> 拉黑用户</template>
<delete-outlined style="color: #ff4d4f" />
</a-tooltip>
封禁
</x-action-button>
<x-action-button @click="handleWater(record)">
流水
</x-action-button>
<x-action-button @click="handleHost(record)">
实例
</x-action-button>
<x-action-button @click="handleOrder(record)">
实例订单
</x-action-button>
</template>
</template>
@ -90,6 +96,9 @@
</a-row>
<edit-dialog ref="editDialogRef" @ok="onOk"></edit-dialog>
<water-dialog ref="waterDialogRef" @ok="onOk"></water-dialog>
<order-dialog ref="orderDialogRef" @ok="onOk"></order-dialog>
<host-case-dialog ref="hostCaseDialogRef" @ok="onOk"></host-case-dialog>
</template>
<script setup>
@ -101,6 +110,9 @@ import { config } from '@/config'
import { statusTypeEnum } from '@/enums/system'
import { usePagination, useForm } from '@/hooks'
import EditDialog from './components/EditDialog.vue'
import WaterDialog from './components/WaterDialog.vue'
import OrderDialog from './components/OrderDialog.vue'
import HostCaseDialog from './components/HostCaseDialog.vue'
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue'
import { useI18n } from 'vue-i18n'
import { disabledDict, authenticationTypeDict } from '@/enums/dict'
@ -110,7 +122,7 @@ defineOptions({
})
const { t } = useI18n() // t
const columns = [
{ title: '用户姓名', dataIndex: 'userName', width: 200 },
{ title: '用户姓名', dataIndex: 'userName', fixed: 'left', width: 200 },
{ title: '用户联系方式', dataIndex: 'phone', width: 150 },
{
title: '状态',
@ -128,13 +140,16 @@ const columns = [
},
{ title: '余额', key: 'balance', dataIndex: 'balance', width: 120 },
{ title: '算力点', key: 'point', dataIndex: 'point', width: 120 },
{ title: t('button.action'), key: 'action', fixed: 'right', width: 120 },
{ title: t('button.action'), key: 'action', fixed: 'right', width: 250 },
]
const { listData, loading, showLoading, hideLoading, paginationState, searchFormData, resetPagination } =
usePagination()
const { resetForm } = useForm()
const editDialogRef = ref()
const waterDialogRef = ref()
const orderDialogRef = ref()
const hostCaseDialogRef = ref()
getPageList()
@ -184,6 +199,22 @@ function handleRemove({ id, status }) {
})
}
//
function handleWater({ id }) {
waterDialogRef.value?.handleWater(id)
}
//
function handleHost({ id }) {
console.log(id)
hostCaseDialogRef.value?.handleHost(id)
}
//
function handleOrder({ id }) {
console.log(id)
orderDialogRef.value?.handleOrder(id)
}
/**
* 分页
*/